mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-27 06:31:54 +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;
 | |
| }
 |