mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			161 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Asterisk -- An open source telephony toolkit.
 | 
						|
 *
 | 
						|
 * Copyright (C) 2015, Digium, Inc.
 | 
						|
 *
 | 
						|
 * Joshua Colp <jcolp@digium.com>
 | 
						|
 *
 | 
						|
 * See http://www.asterisk.org for more information about
 | 
						|
 * the Asterisk project. Please do not directly contact
 | 
						|
 * any of the maintainers of this project for assistance;
 | 
						|
 * the project provides a web site, mailing lists and IRC
 | 
						|
 * channels for your use.
 | 
						|
 *
 | 
						|
 * This program is free software, distributed under the terms of
 | 
						|
 * the GNU General Public License Version 2. See the LICENSE file
 | 
						|
 * at the top of the source tree.
 | 
						|
 */
 | 
						|
 | 
						|
/*! \file
 | 
						|
 *
 | 
						|
 * \brief DNS Recurring Query Support
 | 
						|
 *
 | 
						|
 * \author Joshua Colp <jcolp@digium.com>
 | 
						|
 */
 | 
						|
 | 
						|
/*** MODULEINFO
 | 
						|
	<support_level>core</support_level>
 | 
						|
 ***/
 | 
						|
 | 
						|
#include "asterisk.h"
 | 
						|
 | 
						|
#include "asterisk/astobj2.h"
 | 
						|
#include "asterisk/linkedlists.h"
 | 
						|
#include "asterisk/sched.h"
 | 
						|
#include "asterisk/strings.h"
 | 
						|
#include "asterisk/dns_core.h"
 | 
						|
#include "asterisk/dns_recurring.h"
 | 
						|
#include "asterisk/dns_internal.h"
 | 
						|
 | 
						|
#include <arpa/nameser.h>
 | 
						|
 | 
						|
/*! \brief Delay between TTL expiration and the next DNS query, to make sure the
 | 
						|
resolver cache really expired. */
 | 
						|
#define EXTRA_TTL 2
 | 
						|
#define MAX_TTL ((INT_MAX - EXTRA_TTL) / 1000)
 | 
						|
 | 
						|
/*! \brief Destructor for a DNS query */
 | 
						|
static void dns_query_recurring_destroy(void *data)
 | 
						|
{
 | 
						|
	struct ast_dns_query_recurring *recurring = data;
 | 
						|
 | 
						|
	ao2_cleanup(recurring->user_data);
 | 
						|
}
 | 
						|
 | 
						|
static void dns_query_recurring_resolution_callback(const struct ast_dns_query *query);
 | 
						|
 | 
						|
/*! \brief Scheduled recurring query callback */
 | 
						|
static int dns_query_recurring_scheduled_callback(const void *data)
 | 
						|
{
 | 
						|
	struct ast_dns_query_recurring *recurring = (struct ast_dns_query_recurring *)data;
 | 
						|
 | 
						|
	ao2_lock(recurring);
 | 
						|
	recurring->timer = -1;
 | 
						|
	if (!recurring->cancelled) {
 | 
						|
		recurring->active = ast_dns_resolve_async(recurring->name, recurring->rr_type, recurring->rr_class, dns_query_recurring_resolution_callback,
 | 
						|
			recurring);
 | 
						|
	}
 | 
						|
	ao2_unlock(recurring);
 | 
						|
 | 
						|
	ao2_ref(recurring, -1);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*! \brief Query resolution callback */
 | 
						|
static void dns_query_recurring_resolution_callback(const struct ast_dns_query *query)
 | 
						|
{
 | 
						|
	struct ast_dns_query_recurring *recurring = ast_dns_query_get_data(query);
 | 
						|
	struct ast_dns_query *callback_query;
 | 
						|
 | 
						|
	/* Create a separate query to invoke the user specific callback on as the
 | 
						|
	 * recurring query user data may get used externally (by the unit test)
 | 
						|
	 * and thus changing it is problematic
 | 
						|
	 */
 | 
						|
	callback_query = dns_query_alloc(query->name, query->rr_type, query->rr_class,
 | 
						|
		recurring->callback, recurring->user_data);
 | 
						|
	if (callback_query) {
 | 
						|
		/* The result is immutable at this point and can be safely provided */
 | 
						|
		callback_query->result = query->result;
 | 
						|
		callback_query->callback(callback_query);
 | 
						|
		callback_query->result = NULL;
 | 
						|
		ao2_ref(callback_query, -1);
 | 
						|
	}
 | 
						|
 | 
						|
	ao2_lock(recurring);
 | 
						|
	/* So.. if something has not externally cancelled this we can reschedule based on the TTL */
 | 
						|
	if (!recurring->cancelled) {
 | 
						|
		const struct ast_dns_result *result = ast_dns_query_get_result(query);
 | 
						|
		int ttl = MIN(ast_dns_result_get_lowest_ttl(result), MAX_TTL);
 | 
						|
 | 
						|
		if (ttl) {
 | 
						|
			recurring->timer = ast_sched_add(ast_dns_get_sched(), (ttl + EXTRA_TTL) * 1000, dns_query_recurring_scheduled_callback, ao2_bump(recurring));
 | 
						|
			if (recurring->timer < 0) {
 | 
						|
				/* It is impossible for this to be the last reference as the query has a reference to it */
 | 
						|
				ao2_ref(recurring, -1);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	ao2_replace(recurring->active, NULL);
 | 
						|
	ao2_unlock(recurring);
 | 
						|
}
 | 
						|
 | 
						|
struct ast_dns_query_recurring *ast_dns_resolve_recurring(const char *name, int rr_type, int rr_class, ast_dns_resolve_callback callback, void *data)
 | 
						|
{
 | 
						|
	struct ast_dns_query_recurring *recurring;
 | 
						|
 | 
						|
	if (ast_strlen_zero(name) || !callback || !ast_dns_get_sched()) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	recurring = ao2_alloc(sizeof(*recurring) + strlen(name) + 1, dns_query_recurring_destroy);
 | 
						|
	if (!recurring) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	recurring->callback = callback;
 | 
						|
	recurring->user_data = ao2_bump(data);
 | 
						|
	recurring->timer = -1;
 | 
						|
	recurring->rr_type = rr_type;
 | 
						|
	recurring->rr_class = rr_class;
 | 
						|
	strcpy(recurring->name, name); /* SAFE */
 | 
						|
 | 
						|
	recurring->active = ast_dns_resolve_async(name, rr_type, rr_class, dns_query_recurring_resolution_callback, recurring);
 | 
						|
	if (!recurring->active) {
 | 
						|
		ao2_ref(recurring, -1);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	return recurring;
 | 
						|
}
 | 
						|
 | 
						|
int ast_dns_resolve_recurring_cancel(struct ast_dns_query_recurring *recurring)
 | 
						|
{
 | 
						|
	int res = 0;
 | 
						|
 | 
						|
	ao2_lock(recurring);
 | 
						|
 | 
						|
	recurring->cancelled = 1;
 | 
						|
	AST_SCHED_DEL_UNREF(ast_dns_get_sched(), recurring->timer, ao2_ref(recurring, -1));
 | 
						|
 | 
						|
	if (recurring->active) {
 | 
						|
		res = ast_dns_resolve_cancel(recurring->active);
 | 
						|
		ao2_replace(recurring->active, NULL);
 | 
						|
	}
 | 
						|
 | 
						|
	ao2_unlock(recurring);
 | 
						|
 | 
						|
	return res;
 | 
						|
}
 |