Asterisk - The Open Source Telephony Project GIT-master-d856a3e
test_dns_query_set.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2015, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <depend>TEST_FRAMEWORK</depend>
21 <support_level>core</support_level>
22 ***/
23
24#include "asterisk.h"
25
26#include <arpa/nameser.h>
27#include <arpa/inet.h>
28
29#include "asterisk/test.h"
30#include "asterisk/module.h"
31#include "asterisk/vector.h"
32#include "asterisk/dns_core.h"
36
38 /*! Boolean indicator if query set has completed */
40 /*! Number of times resolve() method has been called */
42 /*! Number of times resolve() method is allowed to be called */
44 /*! Number of times cancel() method has been called */
45 int cancel;
46 /*! Number of times cancel() method is allowed to be called */
50};
51
52static void query_set_data_destructor(void *obj)
53{
54 struct query_set_data *qsdata = obj;
55
56 ast_mutex_destroy(&qsdata->lock);
57 ast_cond_destroy(&qsdata->cond);
58}
59
61{
62 struct query_set_data *qsdata;
63
64 qsdata = ao2_alloc(sizeof(*qsdata), query_set_data_destructor);
65 if (!qsdata) {
66 return NULL;
67 }
68
69 ast_mutex_init(&qsdata->lock);
70 ast_cond_init(&qsdata->cond, NULL);
71
72 return qsdata;
73}
74
75#define DNS_ANSWER "Yes sirree"
76#define DNS_ANSWER_SIZE strlen(DNS_ANSWER)
77
78/*!
79 * \brief Thread that performs asynchronous resolution.
80 *
81 * This thread uses the query's user data to determine how to
82 * perform the resolution. If the allowed number of resolutions
83 * has not been reached then this will succeed, otherwise the
84 * query is expected to have been canceled.
85 *
86 * \param dns_query The ast_dns_query that is being performed
87 * \return NULL
88 */
89static void *resolution_thread(void *dns_query)
90{
91 struct ast_dns_query *query = dns_query;
92 struct ast_dns_query_set *query_set = ast_dns_query_get_data(query);
93 struct query_set_data *qsdata = query_set->user_data;
94
95 ast_assert(qsdata != NULL);
96
97 ast_dns_resolver_set_result(query, 0, 0, NOERROR, "asterisk.org", DNS_ANSWER, DNS_ANSWER_SIZE);
99
100 ao2_ref(query, -1);
101 return NULL;
102}
103
104/*!
105 * \brief Resolver's resolve() method
106 *
107 * \param query The query that is to be resolved
108 * \retval 0 Successfully created thread to perform the resolution
109 * \retval non-zero Failed to create resolution thread
110 */
111static int query_set_resolve(struct ast_dns_query *query)
112{
113 struct ast_dns_query_set *query_set = ast_dns_query_get_data(query);
114 struct query_set_data *qsdata = query_set->user_data;
115 pthread_t resolver_thread;
116
117 /* Only the queries which will not be canceled actually start a thread */
118 if (qsdata->resolves++ < qsdata->cancel_allowed) {
119 return 0;
120 }
121
122 return ast_pthread_create_detached(&resolver_thread, NULL, resolution_thread, ao2_bump(query));
123}
124
125/*!
126 * \brief Resolver's cancel() method
127 *
128 * \param query The query to cancel
129 * \return 0
130 */
131static int query_set_cancel(struct ast_dns_query *query)
132{
133 struct ast_dns_query_set *query_set = ast_dns_query_get_data(query);
134 struct query_set_data *qsdata;
135 int res = -1;
136
137 if (!query_set) {
138 return -1;
139 }
140 qsdata = query_set->user_data;
141
142 if (qsdata->cancel++ < qsdata->cancel_allowed) {
143 res = 0;
144 }
145
146 return res;
147}
148
150 .name = "query_set",
151 .priority = 0,
152 .resolve = query_set_resolve,
153 .cancel = query_set_cancel,
154};
155
156/*!
157 * \brief Callback which is invoked upon query set completion
158 *
159 * \param query_set The query set
160 */
161static void query_set_callback(const struct ast_dns_query_set *query_set)
162{
163 struct query_set_data *qsdata = ast_dns_query_set_get_data(query_set);
164
165 ast_mutex_lock(&qsdata->lock);
166 qsdata->query_set_complete = 1;
167 ast_cond_signal(&qsdata->cond);
168 ast_mutex_unlock(&qsdata->lock);
169}
170
171/*!
172 * \brief Framework for running a query set DNS test
173 *
174 * This function serves as a common way of testing various numbers of queries in a
175 * query set and optional canceling of them.
176 *
177 * \param test The test being run
178 * \param resolve The number of queries that should be allowed to complete resolution
179 * \param cancel The number of queries that should be allowed to be canceled
180 */
181static enum ast_test_result_state query_set_test(struct ast_test *test, int resolve, int cancel)
182{
183 int total = resolve + cancel;
184 RAII_VAR(struct ast_dns_query_set *, query_set, NULL, ao2_cleanup);
185 RAII_VAR(struct query_set_data *, qsdata, NULL, ao2_cleanup);
187 int idx;
188 struct timespec timeout;
189
191 ast_test_status_update(test, "Failed to register query set DNS resolver\n");
192 return AST_TEST_FAIL;
193 }
194
195 qsdata = query_set_data_alloc();
196 if (!qsdata) {
197 ast_test_status_update(test, "Failed to allocate data necessary for query set test\n");
198 res = AST_TEST_FAIL;
199 goto cleanup;
200 }
201
202 query_set = ast_dns_query_set_create();
203 if (!query_set) {
204 ast_test_status_update(test, "Failed to create DNS query set\n");
205 res = AST_TEST_FAIL;
206 goto cleanup;
207 }
208
209 qsdata->resolves_allowed = resolve;
210 qsdata->cancel_allowed = cancel;
211
212 for (idx = 0; idx < total; ++idx) {
213 if (ast_dns_query_set_add(query_set, "asterisk.org", T_A, C_IN)) {
214 ast_test_status_update(test, "Failed to add query to DNS query set\n");
215 res = AST_TEST_FAIL;
216 goto cleanup;
217 }
218 }
219
220 if (ast_dns_query_set_num_queries(query_set) != total) {
221 ast_test_status_update(test, "DNS query set does not contain the correct number of queries\n");
222 res = AST_TEST_FAIL;
223 goto cleanup;
224 }
225
227
228 if (cancel && (cancel == total)) {
229 if (ast_dns_query_set_resolve_cancel(query_set)) {
230 ast_test_status_update(test, "Failed to cancel DNS query set when it should be cancellable\n");
231 res = AST_TEST_FAIL;
232 }
233
234 if (qsdata->query_set_complete) {
235 ast_test_status_update(test, "Query set callback was invoked despite all queries being cancelled\n");
236 res = AST_TEST_FAIL;
237 }
238
239 goto cleanup;
240 } else if (cancel) {
241 if (!ast_dns_query_set_resolve_cancel(query_set)) {
242 ast_test_status_update(test, "Successfully cancelled DNS query set when it should not be possible\n");
243 res = AST_TEST_FAIL;
244 goto cleanup;
245 }
246 }
247
248 timeout = ast_tsnow();
249 timeout.tv_sec += 10;
250
251 ast_mutex_lock(&qsdata->lock);
252 while (!qsdata->query_set_complete) {
253 if (ast_cond_timedwait(&qsdata->cond, &qsdata->lock, &timeout) == ETIMEDOUT) {
254 break;
255 }
256 }
257 ast_mutex_unlock(&qsdata->lock);
258
259 if (!qsdata->query_set_complete) {
260 ast_test_status_update(test, "Query set did not complete when it should have\n");
261 res = AST_TEST_FAIL;
262 goto cleanup;
263 }
264
265 for (idx = 0; idx < ast_dns_query_set_num_queries(query_set); ++idx) {
266 const struct ast_dns_query *query = ast_dns_query_set_get(query_set, idx);
267
268 if (strcmp(ast_dns_query_get_name(query), "asterisk.org")) {
269 ast_test_status_update(test, "Query did not have expected name\n");
270 res = AST_TEST_FAIL;
271 }
272 if (ast_dns_query_get_rr_type(query) != T_A) {
273 ast_test_status_update(test, "Query did not have expected type\n");
274 res = AST_TEST_FAIL;
275 }
276 if (ast_dns_query_get_rr_class(query) != C_IN) {
277 ast_test_status_update(test, "Query did not have expected class\n");
278 res = AST_TEST_FAIL;
279 }
280 }
281
282cleanup:
284 return res;
285}
286
288{
289 switch (cmd) {
290 case TEST_INIT:
291 info->name = "query_set";
292 info->category = "/main/dns/query_set/";
293 info->summary = "Test nominal asynchronous DNS query set";
294 info->description =
295 "This tests nominal query set in the following ways:\n"
296 "\t* Multiple queries are added to a query set\n"
297 "\t* The mock resolver is configured to respond to all queries\n"
298 "\t* Asynchronous resolution of the query set is started\n"
299 "\t* The mock resolver responds to all queries\n"
300 "\t* We ensure that the query set callback is invoked upon completion";
301 return AST_TEST_NOT_RUN;
302 case TEST_EXECUTE:
303 break;
304 }
305
306 return query_set_test(test, 4, 0);
307}
308
309AST_TEST_DEFINE(query_set_empty)
310{
311 switch (cmd) {
312 case TEST_INIT:
313 info->name = "query_set_empty";
314 info->category = "/main/dns/query_set/";
315 info->summary = "Test nominal asynchronous empty DNS query set";
316 info->description =
317 "This tests nominal query set in the following ways:\n"
318 "\t* No queries are added to a query set\n"
319 "\t* Asynchronous resolution of the query set is started\n"
320 "\t* We ensure that the query set callback is invoked upon completion";
321 return AST_TEST_NOT_RUN;
322 case TEST_EXECUTE:
323 break;
324 }
325
326 return query_set_test(test, 0, 0);
327}
328
329AST_TEST_DEFINE(query_set_nominal_cancel)
330{
331 switch (cmd) {
332 case TEST_INIT:
333 info->name = "query_set_nominal_cancel";
334 info->category = "/main/dns/query_set/";
335 info->summary = "Test nominal asynchronous DNS query set cancellation";
336 info->description =
337 "This tests nominal query set cancellation in the following ways:\n"
338 "\t* Multiple queries are added to a query set\n"
339 "\t* The mock resolver is configured to NOT respond to any queries\n"
340 "\t* Asynchronous resolution of the query set is started\n"
341 "\t* The query set is canceled and is confirmed to return with success";
342 return AST_TEST_NOT_RUN;
343 case TEST_EXECUTE:
344 break;
345 }
346
347 return query_set_test(test, 0, 4);
348}
349
350AST_TEST_DEFINE(query_set_off_nominal_cancel)
351{
352 switch (cmd) {
353 case TEST_INIT:
354 info->name = "query_set_off_nominal_cancel";
355 info->category = "/main/dns/query_set/";
356 info->summary = "Test off-nominal asynchronous DNS query set cancellation";
357 info->description =
358 "This tests nominal query set cancellation in the following ways:\n"
359 "\t* Multiple queries are added to a query set\n"
360 "\t* The mock resolver is configured to respond to half the queries\n"
361 "\t* Asynchronous resolution of the query set is started\n"
362 "\t* The query set is canceled and is confirmed to return failure\n"
363 "\t* The query set callback is confirmed to run, since it could not be fully canceled";
364 return AST_TEST_NOT_RUN;
365 case TEST_EXECUTE:
366 break;
367 }
368
369 return query_set_test(test, 2, 2);
370}
371
372static int unload_module(void)
373{
374 AST_TEST_UNREGISTER(query_set);
375 AST_TEST_UNREGISTER(query_set_empty);
376 AST_TEST_UNREGISTER(query_set_nominal_cancel);
377 AST_TEST_UNREGISTER(query_set_off_nominal_cancel);
378
379 return 0;
380}
381
382static int load_module(void)
383{
384 AST_TEST_REGISTER(query_set);
385 AST_TEST_REGISTER(query_set_empty);
386 AST_TEST_REGISTER(query_set_nominal_cancel);
387 AST_TEST_REGISTER(query_set_off_nominal_cancel);
388
390}
391
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
Core DNS API.
int ast_dns_query_get_rr_type(const struct ast_dns_query *query)
Get the record resource type of a DNS query.
Definition: dns_core.c:62
int ast_dns_query_get_rr_class(const struct ast_dns_query *query)
Get the record resource class of a DNS query.
Definition: dns_core.c:67
void * ast_dns_query_get_data(const struct ast_dns_query *query)
Get the user specific data of a DNS query.
Definition: dns_core.c:72
const char * ast_dns_query_get_name(const struct ast_dns_query *query)
Get the name queried in a DNS query.
Definition: dns_core.c:57
Internal DNS structure definitions.
DNS Query Set API.
struct ast_dns_query * ast_dns_query_set_get(const struct ast_dns_query_set *query_set, unsigned int index)
Retrieve a query from a query set.
struct ast_dns_query_set * ast_dns_query_set_create(void)
Create a query set to hold queries.
Definition: dns_query_set.c:60
size_t ast_dns_query_set_num_queries(const struct ast_dns_query_set *query_set)
Retrieve the number of queries in a query set.
int ast_dns_query_set_add(struct ast_dns_query_set *query_set, const char *name, int rr_type, int rr_class)
Add a query to a query set.
int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set)
Cancel an asynchronous DNS query set resolution.
void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dns_query_set_callback callback, void *data)
Asynchronously resolve queries in a query set.
void * ast_dns_query_set_get_data(const struct ast_dns_query_set *query_set)
Retrieve user specific data from a query set.
DNS Resolver API.
int ast_dns_resolver_set_result(struct ast_dns_query *query, unsigned int secure, unsigned int bogus, unsigned int rcode, const char *canonical, const char *answer, size_t answer_size)
Set result information for a DNS query.
Definition: dns_core.c:456
void ast_dns_resolver_completed(struct ast_dns_query *query)
Mark a DNS query as having been completed.
Definition: dns_core.c:599
int ast_dns_resolver_register(struct ast_dns_resolver *resolver)
Register a DNS resolver.
Definition: dns_core.c:632
void ast_dns_resolver_unregister(struct ast_dns_resolver *resolver)
Unregister a DNS resolver.
Definition: dns_core.c:682
#define ast_cond_destroy(cond)
Definition: lock.h:202
#define ast_cond_init(cond, attr)
Definition: lock.h:201
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:206
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
pthread_cond_t ast_cond_t
Definition: lock.h:178
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
#define ast_cond_signal(cond)
Definition: lock.h:203
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
def info(msg)
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
static int total
Definition: res_adsi.c:970
#define NULL
Definition: resample.c:96
A set of DNS queries.
Definition: dns_internal.h:185
void * user_data
User-specific data.
Definition: dns_internal.h:197
A DNS query.
Definition: dns_internal.h:137
DNS resolver implementation.
Definition: dns_resolver.h:32
const char * name
The name of the resolver implementation.
Definition: dns_resolver.h:34
Structure for mutex and tracking information.
Definition: lock.h:135
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
static struct ast_dns_resolver query_set_resolver
static enum ast_test_result_state query_set_test(struct ast_test *test, int resolve, int cancel)
Framework for running a query set DNS test.
static int query_set_cancel(struct ast_dns_query *query)
Resolver's cancel() method.
#define DNS_ANSWER_SIZE
static struct query_set_data * query_set_data_alloc(void)
static void query_set_data_destructor(void *obj)
static void * resolution_thread(void *dns_query)
Thread that performs asynchronous resolution.
static void query_set_callback(const struct ast_dns_query_set *query_set)
Callback which is invoked upon query set completion.
static int load_module(void)
AST_TEST_DEFINE(query_set)
static int unload_module(void)
static int query_set_resolve(struct ast_dns_query *query)
Resolver's resolve() method.
#define DNS_ANSWER
struct timespec ast_tsnow(void)
Returns current timespec. Meant to avoid calling ast_tvnow() just to create a timespec from the timev...
Definition: time.h:186
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:588
Vector container support.