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;
|
||
|
}
|