summaryrefslogtreecommitdiff
path: root/main/dns_system_resolver.c
blob: 8cb92c0588c3d46198b06d88fb89e895c0dc4f2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
 * Asterisk -- An open source telephony toolkit.
 *
 * Copyright (C) 2015, Digium, Inc.
 *
 * Ashley Sanders <asanders@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 The default DNS resolver for Asterisk.
 *
 * \arg See also \ref res_resolver_unbound
 *
 * \author Ashley Sanders <asanders@digium.com>
 */

#include "asterisk.h"

#include "asterisk/_private.h"
#include "asterisk/astobj2.h"
#include "asterisk/dns.h"
#include "asterisk/dns_core.h"
#include "asterisk/dns_resolver.h"
#include "asterisk/linkedlists.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/utils.h"

/*! \brief The consideration priority for this resolver implementation. */
#define DNS_SYSTEM_RESOLVER_PRIORITY INT_MAX

/*! \brief Resolver return code upon success. */
#define DNS_SYSTEM_RESOLVER_SUCCESS 0

/*! \brief Resolver return code upon failure. */
#define DNS_SYSTEM_RESOLVER_FAILURE -1


static int dns_system_resolver_add_record(void *context, unsigned char *record, int record_len, int ttl);
static int dns_system_resolver_cancel(struct ast_dns_query *query);
static void dns_system_resolver_destroy(void);
static int dns_system_resolver_process_query(void *data);
static int dns_system_resolver_resolve(struct ast_dns_query *query);
static int dns_system_resolver_set_response(void *context, unsigned char *dns_response, int dns_response_len, int rcode);


/*! \brief The task processor to use for making DNS searches asynchronous. */
static struct ast_taskprocessor *dns_system_resolver_tp;

/*! \brief The base definition for the dns_system_resolver */
struct ast_dns_resolver dns_system_resolver_base = {
	.name = "system",
	.priority = DNS_SYSTEM_RESOLVER_PRIORITY,
	.resolve = dns_system_resolver_resolve,
	.cancel = dns_system_resolver_cancel,
};

/*!
 * \brief Callback to handle processing resource records.
 *
 * \details Adds an individual resource record discovered with ast_search_dns_ex to the
 *          ast_dns_query currently being resolved.
 *
 * \internal
 *
 * \param context     A void pointer to the ast_dns_query being processed.
 * \param record      An individual resource record discovered during the DNS search.
 * \param record_len  The length of the resource record.
 * \param ttl         The resource record's expiration time limit (time to live).
 *
 * \retval  0 on success
 * \retval -1 on failure
 */
static int dns_system_resolver_add_record(void *context, unsigned char *record, int record_len, int ttl)
{
	struct ast_dns_query *query = context;

	/* Add the record to the query.*/
	return ast_dns_resolver_add_record(query,
	                                   ast_dns_query_get_rr_type(query),
	                                   ast_dns_query_get_rr_class(query),
	                                   ttl,
	                                   (const char*) record,
	                                   record_len);
}

/*!
 * \brief Cancels processing resolution for a given query.
 *
 * \note The system API calls block so there is no way to cancel them. Therefore, this function always
 * returns failure when invoked.
 *
 * \internal
 *
 * \param query  The ast_dns_query to cancel.
 *
 * \retval  0 on success
 * \retval -1 on failure
 */
static int dns_system_resolver_cancel(struct ast_dns_query *query)
{
	return DNS_SYSTEM_RESOLVER_FAILURE;
}

/*!
 * \brief Destructor.
 *
 * \internal
 */
static void dns_system_resolver_destroy(void)
{
	/* Unreference the task processor */
	dns_system_resolver_tp = ast_taskprocessor_unreference(dns_system_resolver_tp);

	/* Unregister the base resolver */
	ast_dns_resolver_unregister(&dns_system_resolver_base);
}

/*!
 * \brief Callback to handle processing the query from the ast_taskprocessor instance.
 *
 * \internal
 *
 * \param data  A void pointer to the ast_dns_query being processed.
 *
 * \retval -1 on search failure
 * \retval  0 on no records found
 * \retval  1 on success
 */
static int dns_system_resolver_process_query(void *data)
{
	struct ast_dns_query *query = data;

	/* Perform the DNS search */
	enum ast_dns_search_result res = ast_search_dns_ex(query,
	                                                   ast_dns_query_get_name(query),
	                                                   ast_dns_query_get_rr_class(query),
	                                                   ast_dns_query_get_rr_type(query),
	                                                   dns_system_resolver_set_response,
	                                                   dns_system_resolver_add_record);

	/* Handle the possible return values from the DNS search */
	if (res == AST_DNS_SEARCH_FAILURE) {
		ast_debug(1, "DNS search failed for query: '%s'\n",
		        ast_dns_query_get_name(query));
	} else if (res == AST_DNS_SEARCH_NO_RECORDS) {
		ast_debug(1, "DNS search failed to yield any results for query: '%s'\n",
		        ast_dns_query_get_name(query));
	}

	/* Mark the query as complete */
	ast_dns_resolver_completed(query);

	/* Reduce the reference count on the query object */
	ao2_ref(query, -1);

	return res;
}

/*!
 * \brief Resolves a DNS query.
 *
 * \internal
 *
 * \param query  The ast_dns_query to resolve.
 *
 * \retval  0 on successful load of query handler to the ast_taskprocessor instance
 * \retval -1 on failure to load the query handler to the ast_taskprocessor instance
 */
static int dns_system_resolver_resolve(struct ast_dns_query *query)
{
	/* Add query processing handler to the task processor */
	int res = ast_taskprocessor_push(dns_system_resolver_tp,
	                                 dns_system_resolver_process_query,
	                                 ao2_bump(query));

	/* The query processing handler was not added to the task processor */
	if (res < 0) {
		ast_log(LOG_ERROR, "Failed to perform async DNS resolution of '%s'\n",
		        ast_dns_query_get_name(query));
		ao2_ref(query, -1);
	}

	/* Return the result of adding the query processing handler to the task processor */
	return res;
}

/*!
 * \brief Callback to handle initializing the results field.
 *
 * \internal
 *
 * \param dns_response  The full DNS response.
 * \param dns_response  The length of the full DNS response.
 * \param rcode         The DNS response code.
 *
 * \retval  0 on success
 * \retval -1 on failure
 */
static int dns_system_resolver_set_response(void *context, unsigned char *dns_response, int dns_response_len, int rcode)
{
	struct ast_dns_query *query = context;
	int res;

	/* Instantiate the query's result field (if necessary). */
	if (!ast_dns_query_get_result(query)) {
		res = ast_dns_resolver_set_result(query,
		                                  0,
		                                  0,
		                                  rcode,
		                                  ast_dns_query_get_name(query),
		                                  (const char*) dns_response,
		                                  dns_response_len);

		if (res) {
			/* There was a problem instantiating the results field. */
			ast_log(LOG_ERROR, "Could not instantiate the results field for query: '%s'\n",
			        ast_dns_query_get_name(query));
		}
	} else {
		res = DNS_SYSTEM_RESOLVER_SUCCESS;
	}

	return res;
}

/*!
 * \brief Initializes the resolver.
 *
 * \retval  0 on success
 * \retval -1 on failure
 */
int ast_dns_system_resolver_init(void)
{
	/* Register the base resolver */
	int res = ast_dns_resolver_register(&dns_system_resolver_base);

	if (res) {
		return DNS_SYSTEM_RESOLVER_FAILURE;
	}

	/* Instantiate the task processor */
	dns_system_resolver_tp = ast_taskprocessor_get("dns_system_resolver_tp",
	                                                TPS_REF_DEFAULT);

	/* Return error if the task processor failed to instantiate */
	if (!dns_system_resolver_tp) {
		return DNS_SYSTEM_RESOLVER_FAILURE;
	}

	/* Register the cleanup function */
	ast_register_cleanup(dns_system_resolver_destroy);

	return DNS_SYSTEM_RESOLVER_SUCCESS;
}