Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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"
33 #include "asterisk/dns_resolver.h"
34 #include "asterisk/dns_query_set.h"
35 #include "asterisk/dns_internal.h"
36 
38  /*! Boolean indicator if query set has completed */
40  /*! Number of times resolve() method has been called */
41  int resolves;
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 
52 static 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  */
89 static 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  */
111 static 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  */
131 static 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  */
161 static 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  */
181 static 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 
190  if (ast_dns_resolver_register(&query_set_resolver)) {
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 
282 cleanup:
283  ast_dns_resolver_unregister(&query_set_resolver);
284  return res;
285 }
286 
287 AST_TEST_DEFINE(query_set)
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 
309 AST_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 
329 AST_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 
350 AST_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 
372 static 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 
382 static 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 
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
Asterisk main include file. File version handling, generic pbx functions.
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
#define DNS_ANSWER
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
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:563
void ast_dns_resolver_unregister(struct ast_dns_resolver *resolver)
Unregister a DNS resolver.
Definition: dns_core.c:680
void * user_data
User-specific data.
Definition: dns_internal.h:197
static void * resolution_thread(void *dns_query)
Thread that performs asynchronous resolution.
static void query_set_data_destructor(void *obj)
struct ast_dns_query_set * ast_dns_query_set_create(void)
Create a query set to hold queries.
Definition: dns_query_set.c:60
Test Framework API.
static struct ast_dns_resolver query_set_resolver
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_cond_init(cond, attr)
Definition: lock.h:199
#define ast_assert(a)
Definition: utils.h:695
#define ast_mutex_lock(a)
Definition: lock.h:187
#define NULL
Definition: resample.c:96
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.
#define ast_cond_signal(cond)
Definition: lock.h:201
const char * name
The name of the resolver implementation.
Definition: dns_resolver.h:34
pthread_cond_t ast_cond_t
Definition: lock.h:176
#define ao2_bump(obj)
Definition: astobj2.h:491
#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:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
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
static int load_module(void)
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.
int ast_dns_resolver_register(struct ast_dns_resolver *resolver)
Register a DNS resolver.
Definition: dns_core.c:630
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static int query_set_cancel(struct ast_dns_query *query)
Resolver&#39;s cancel() method.
static int query_set_resolve(struct ast_dns_query *query)
Resolver&#39;s resolve() method.
void ast_dns_resolver_completed(struct ast_dns_query *query)
Mark a DNS query as having been completed.
Definition: dns_core.c:599
DNS Query Set API.
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
static struct query_set_data * query_set_data_alloc(void)
#define ast_cond_destroy(cond)
Definition: lock.h:200
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
DNS resolver implementation.
Definition: dns_resolver.h:32
Internal DNS structure definitions.
A DNS query.
Definition: dns_internal.h:137
int ast_dns_query_set_resolve_cancel(struct ast_dns_query_set *query_set)
Cancel an asynchronous DNS query set resolution.
Vector container support.
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
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
Definition: test.c:65
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
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.
static int unload_module(void)
static int total
Definition: res_adsi.c:968
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:177
#define ast_mutex_init(pmutex)
Definition: lock.h:184
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.
#define ast_mutex_destroy(a)
Definition: lock.h:186
static void query_set_callback(const struct ast_dns_query_set *query_set)
Callback which is invoked upon query set completion.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
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
Asterisk module definitions.
A set of DNS queries.
Definition: dns_internal.h:185
#define DNS_ANSWER_SIZE
DNS Resolver API.
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:204
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.
AST_TEST_DEFINE(query_set)
Structure for mutex and tracking information.
Definition: lock.h:135
Core DNS API.
ast_test_result_state
Definition: test.h:200
void * ast_dns_query_set_get_data(const struct ast_dns_query_set *query_set)
Retrieve user specific data from a query set.
#define ast_mutex_unlock(a)
Definition: lock.h:188