Merge "Revert "app_voicemail: Remove need to subscribe to stasis""
[asterisk/asterisk.git] / tests / test_dns.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, Mark Michelson
5  *
6  * Mark Michelson <mmichelson@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/dns_core.h"
32 #include "asterisk/dns_resolver.h"
33 #include "asterisk/dns_internal.h"
34
35 /* Used when a stub is needed for certain tests */
36 static int stub_resolve(struct ast_dns_query *query)
37 {
38         return 0;
39 }
40
41 /* Used when a stub is needed for certain tests */
42 static int stub_cancel(struct ast_dns_query *query)
43 {
44         return 0;
45 }
46
47 AST_TEST_DEFINE(resolver_register_unregister)
48 {
49         struct ast_dns_resolver cool_guy_resolver = {
50                 .name = "A snake that swallowed a deer",
51                 .priority = 19890504,
52                 .resolve = stub_resolve,
53                 .cancel = stub_cancel,
54         };
55
56         switch (cmd) {
57         case TEST_INIT:
58                 info->name = "resolver_register_unregister";
59                 info->category = "/main/dns/";
60                 info->summary = "Test nominal resolver registration and unregistration";
61                 info->description =
62                         "The test performs the following steps:\n"
63                         "\t* Register a valid resolver.\n"
64                         "\t* Unregister the resolver.\n"
65                         "If either step fails, the test fails";
66                 return AST_TEST_NOT_RUN;
67         case TEST_EXECUTE:
68                 break;
69         }
70
71         if (ast_dns_resolver_register(&cool_guy_resolver)) {
72                 ast_test_status_update(test, "Unable to register a perfectly good resolver\n");
73                 return AST_TEST_FAIL;
74         }
75
76         ast_dns_resolver_unregister(&cool_guy_resolver);
77
78         return AST_TEST_PASS;
79 }
80
81 AST_TEST_DEFINE(resolver_register_off_nominal)
82 {
83         struct ast_dns_resolver valid = {
84                 .name = "valid",
85                 .resolve = stub_resolve,
86                 .cancel = stub_cancel,
87         };
88
89         struct ast_dns_resolver incomplete1 = {
90                 .name = NULL,
91                 .resolve = stub_resolve,
92                 .cancel = stub_cancel,
93         };
94
95         struct ast_dns_resolver incomplete2 = {
96                 .name = "incomplete2",
97                 .resolve = NULL,
98                 .cancel = stub_cancel,
99         };
100
101         struct ast_dns_resolver incomplete3 = {
102                 .name = "incomplete3",
103                 .resolve = stub_resolve,
104                 .cancel = NULL,
105         };
106
107         switch (cmd) {
108         case TEST_INIT:
109                 info->name = "resolver_register_off_nominal";
110                 info->category = "/main/dns/";
111                 info->summary = "Test off-nominal resolver registration";
112                 info->description =
113                         "Test off-nominal resolver registration:\n"
114                         "\t* Register a duplicate resolver\n"
115                         "\t* Register a resolver without a name\n"
116                         "\t* Register a resolver without a resolve() method\n"
117                         "\t* Register a resolver without a cancel() method";
118                 return AST_TEST_NOT_RUN;
119         case TEST_EXECUTE:
120                 break;
121         }
122
123         if (ast_dns_resolver_register(&valid)) {
124                 ast_test_status_update(test, "Failed to register valid resolver\n");
125                 return AST_TEST_FAIL;
126         }
127
128         if (!ast_dns_resolver_register(&valid)) {
129                 ast_test_status_update(test, "Successfully registered the same resolver multiple times\n");
130                 return AST_TEST_FAIL;
131         }
132
133         ast_dns_resolver_unregister(&valid);
134
135         if (!ast_dns_resolver_register(NULL)) {
136                 ast_test_status_update(test, "Successfully registered a NULL resolver\n");
137                 return AST_TEST_FAIL;
138         }
139
140         if (!ast_dns_resolver_register(&incomplete1)) {
141                 ast_test_status_update(test, "Successfully registered a DNS resolver with no name\n");
142                 return AST_TEST_FAIL;
143         }
144
145         if (!ast_dns_resolver_register(&incomplete2)) {
146                 ast_test_status_update(test, "Successfully registered a DNS resolver with no resolve() method\n");
147                 return AST_TEST_FAIL;
148         }
149
150         if (!ast_dns_resolver_register(&incomplete3)) {
151                 ast_test_status_update(test, "Successfully registered a DNS resolver with no cancel() method\n");
152                 return AST_TEST_FAIL;
153         }
154
155         return AST_TEST_PASS;
156 }
157
158 AST_TEST_DEFINE(resolver_unregister_off_nominal)
159 {
160         struct ast_dns_resolver non_existent = {
161                 .name = "I do not exist",
162                 .priority = 20141004,
163                 .resolve = stub_resolve,
164                 .cancel = stub_cancel,
165         };
166
167         switch (cmd) {
168         case TEST_INIT:
169                 info->name = "resolver_unregister_off_nominal";
170                 info->category = "/main/dns/";
171                 info->summary = "Test off-nominal DNS resolver unregister";
172                 info->description =
173                         "The test attempts the following:\n"
174                         "\t* Unregister a resolver that is not registered.\n"
175                         "\t* Unregister a NULL pointer.\n"
176                         "Because unregistering a resolver does not return an indicator of success, the best\n"
177                         "this test can do is verify that nothing blows up when this is attempted.";
178                 return AST_TEST_NOT_RUN;
179         case TEST_EXECUTE:
180                 break;
181         }
182
183         ast_dns_resolver_unregister(&non_existent);
184         ast_dns_resolver_unregister(NULL);
185
186         return AST_TEST_PASS;
187 }
188
189 AST_TEST_DEFINE(resolver_data)
190 {
191         struct ast_dns_query some_query;
192
193         struct digits {
194                 int fingers;
195                 int toes;
196         };
197
198         RAII_VAR(struct digits *, average, NULL, ao2_cleanup);
199         RAII_VAR(struct digits *, polydactyl, NULL, ao2_cleanup);
200
201         struct digits *data_ptr;
202
203         switch (cmd) {
204         case TEST_INIT:
205                 info->name = "resolver_data";
206                 info->category = "/main/dns/";
207                 info->summary = "Test getting and setting data on a DNS resolver";
208                 info->description = "This test does the following:\n"
209                         "\t* Ensure that requesting resolver data results in a NULL return if no data has been set.\n"
210                         "\t* Ensure that setting resolver data does not result in an error.\n"
211                         "\t* Ensure that retrieving the set resolver data returns the data we expect\n"
212                         "\t* Ensure that setting new resolver data on the query does not result in an error\n"
213                         "\t* Ensure that retrieving the resolver data returns the new data that we set";
214                 return AST_TEST_NOT_RUN;
215         case TEST_EXECUTE:
216                 break;
217         }
218
219         memset(&some_query, 0, sizeof(some_query));
220
221         average = ao2_alloc(sizeof(*average), NULL);
222         polydactyl = ao2_alloc(sizeof(*average), NULL);
223
224         if (!average || !polydactyl) {
225                 ast_test_status_update(test, "Allocation failure during unit test\n");
226                 return AST_TEST_FAIL;
227         }
228
229         /* Ensure that NULL is retrieved if we haven't set anything on the query */
230         data_ptr = ast_dns_resolver_get_data(&some_query);
231         if (data_ptr) {
232                 ast_test_status_update(test, "Retrieved non-NULL resolver data from query unexpectedly\n");
233                 return AST_TEST_FAIL;
234         }
235
236         if (ast_dns_resolver_set_data(&some_query, average)) {
237                 ast_test_status_update(test, "Failed to set resolver data on query\n");
238                 return AST_TEST_FAIL;
239         }
240
241         /* Go ahead now and remove the query's reference to the resolver data to prevent memory leaks */
242         ao2_ref(average, -1);
243
244         /* Ensure that data can be set and retrieved */
245         data_ptr = ast_dns_resolver_get_data(&some_query);
246         if (!data_ptr) {
247                 ast_test_status_update(test, "Unable to retrieve resolver data from DNS query\n");
248                 return AST_TEST_FAIL;
249         }
250
251         if (data_ptr != average) {
252                 ast_test_status_update(test, "Unexpected resolver data retrieved from DNS query\n");
253                 return AST_TEST_FAIL;
254         }
255
256         /* Ensure that attempting to set new resolver data on the query fails */
257         if (!ast_dns_resolver_set_data(&some_query, polydactyl)) {
258                 ast_test_status_update(test, "Successfully overwrote resolver data on a query. We shouldn't be able to do that\n");
259                 return AST_TEST_FAIL;
260         }
261
262         return AST_TEST_PASS;
263 }
264
265 static int test_results(struct ast_test *test, const struct ast_dns_query *query,
266                 unsigned int expected_secure, unsigned int expected_bogus,
267                 unsigned int expected_rcode, const char *expected_canonical,
268                 const char *expected_answer, size_t answer_size)
269 {
270         struct ast_dns_result *result;
271
272         result = ast_dns_query_get_result(query);
273         if (!result) {
274                 ast_test_status_update(test, "Unable to retrieve result from query\n");
275                 return -1;
276         }
277
278         if (ast_dns_result_get_secure(result) != expected_secure ||
279                         ast_dns_result_get_bogus(result) != expected_bogus ||
280                         ast_dns_result_get_rcode(result) != expected_rcode ||
281                         strcmp(ast_dns_result_get_canonical(result), expected_canonical) ||
282                         memcmp(ast_dns_result_get_answer(result), expected_answer, answer_size)) {
283                 ast_test_status_update(test, "Unexpected values in result from query\n");
284                 return -1;
285         }
286
287         return 0;
288 }
289
290 /* When setting a DNS result, we have to provide the raw DNS answer. This
291  * is not happening. Sorry. Instead, we provide a dummy string and call it
292  * a day
293  */
294 #define DNS_ANSWER "Grumble Grumble"
295 #define DNS_ANSWER_SIZE strlen(DNS_ANSWER)
296
297 AST_TEST_DEFINE(resolver_set_result)
298 {
299         struct ast_dns_query some_query;
300         struct ast_dns_result *result;
301
302         struct dns_result {
303                 unsigned int secure;
304                 unsigned int bogus;
305                 unsigned int rcode;
306         } results[] = {
307                 { 0, 0, NOERROR, },
308                 { 0, 1, NOERROR, },
309                 { 1, 0, NOERROR, },
310                 { 0, 0, NXDOMAIN, },
311         };
312         int i;
313         enum ast_test_result_state res = AST_TEST_PASS;
314
315         switch (cmd) {
316         case TEST_INIT:
317                 info->name = "resolver_set_result";
318                 info->category = "/main/dns/";
319                 info->summary = "Test setting and getting results on DNS queries";
320                 info->description =
321                         "This test performs the following:\n"
322                         "\t* Sets a result that is not secure, bogus, and has rcode 0\n"
323                         "\t* Sets a result that is not secure, has rcode 0, but is secure\n"
324                         "\t* Sets a result that is not bogus, has rcode 0, but is secure\n"
325                         "\t* Sets a result that is not secure or bogus, but has rcode NXDOMAIN\n"
326                         "After each result is set, we ensure that parameters retrieved from\n"
327                         "the result have the expected values.";
328                 return AST_TEST_NOT_RUN;
329         case TEST_EXECUTE:
330                 break;
331         }
332
333         memset(&some_query, 0, sizeof(some_query));
334
335         for (i = 0; i < ARRAY_LEN(results); ++i) {
336                 if (ast_dns_resolver_set_result(&some_query, results[i].secure, results[i].bogus,
337                                 results[i].rcode, "asterisk.org", DNS_ANSWER, DNS_ANSWER_SIZE)) {
338                         ast_test_status_update(test, "Unable to add DNS result to query\n");
339                         res = AST_TEST_FAIL;
340                 }
341
342                 if (test_results(test, &some_query, results[i].secure, results[i].bogus,
343                                 results[i].rcode, "asterisk.org", DNS_ANSWER, DNS_ANSWER_SIZE)) {
344                         res = AST_TEST_FAIL;
345                 }
346         }
347
348         /* The final result we set needs to be freed */
349         result = ast_dns_query_get_result(&some_query);
350         ast_dns_result_free(result);
351
352         return res;
353 }
354
355 AST_TEST_DEFINE(resolver_set_result_off_nominal)
356 {
357         struct ast_dns_query some_query;
358         struct ast_dns_result *result;
359
360         switch (cmd) {
361         case TEST_INIT:
362                 info->name = "resolver_set_result_off_nominal";
363                 info->category = "/main/dns/";
364                 info->summary = "Test setting off-nominal DNS results";
365                 info->description =
366                         "This test performs the following:\n"
367                         "\t* Attempt to add a DNS result that is both bogus and secure\n"
368                         "\t* Attempt to add a DNS result that has no canonical name";
369                 return AST_TEST_NOT_RUN;
370         case TEST_EXECUTE:
371                 break;
372         }
373
374         memset(&some_query, 0, sizeof(some_query));
375
376         if (!ast_dns_resolver_set_result(&some_query, 1, 1, NOERROR, "asterisk.org",
377                                 DNS_ANSWER, DNS_ANSWER_SIZE)) {
378                 ast_test_status_update(test, "Successfully added a result that was both secure and bogus\n");
379                 result = ast_dns_query_get_result(&some_query);
380                 ast_dns_result_free(result);
381                 return AST_TEST_FAIL;
382         }
383
384         if (!ast_dns_resolver_set_result(&some_query, 0, 0, NOERROR, NULL,
385                                 DNS_ANSWER, DNS_ANSWER_SIZE)) {
386                 ast_test_status_update(test, "Successfully added result with no canonical name\n");
387                 result = ast_dns_query_get_result(&some_query);
388                 ast_dns_result_free(result);
389                 return AST_TEST_FAIL;
390         }
391
392         return AST_TEST_PASS;
393 }
394
395 static int test_record(struct ast_test *test, const struct ast_dns_record *record,
396                 int rr_type, int rr_class, int ttl, const char *data, const size_t size)
397 {
398         if (ast_dns_record_get_rr_type(record) != rr_type) {
399                 ast_test_status_update(test, "Unexpected rr_type from DNS record\n");
400                 return -1;
401         }
402
403         if (ast_dns_record_get_rr_class(record) != rr_class) {
404                 ast_test_status_update(test, "Unexpected rr_class from DNS record\n");
405                 return -1;
406         }
407
408         if (ast_dns_record_get_ttl(record) != ttl) {
409                 ast_test_status_update(test, "Unexpected ttl from DNS record\n");
410                 return -1;
411         }
412
413         if (memcmp(ast_dns_record_get_data(record), data, size)) {
414                 ast_test_status_update(test, "Unexpected data in DNS record\n");
415                 return -1;
416         }
417
418         return 0;
419 }
420
421 AST_TEST_DEFINE(resolver_add_record)
422 {
423         RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
424         struct ast_dns_query some_query;
425         const struct ast_dns_record *record;
426
427         static const char *V4 = "127.0.0.1";
428         static const size_t V4_BUFSIZE = sizeof(struct in_addr);
429         char v4_buf[V4_BUFSIZE];
430
431         static const char *V6 = "::1";
432         static const size_t V6_BUFSIZE = sizeof(struct in6_addr);
433         char v6_buf[V6_BUFSIZE];
434
435         struct dns_record_details {
436                 int type;
437                 int class;
438                 int ttl;
439                 const char *data;
440                 const size_t size;
441                 int visited;
442         } records[] = {
443                 { T_A, C_IN, 12345, v4_buf, V4_BUFSIZE, 0, },
444                 { T_AAAA, C_IN, 12345, v6_buf, V6_BUFSIZE, 0, },
445         };
446
447         int num_records_visited = 0;
448
449         switch (cmd) {
450         case TEST_INIT:
451                 info->name = "resolver_add_record";
452                 info->category = "/main/dns/";
453                 info->summary = "Test adding DNS records to a query";
454                 info->description =
455                         "This test performs the following:\n"
456                         "\t* Ensure a nominal A record can be added to a query result\n"
457                         "\t* Ensures that the record can be retrieved\n"
458                         "\t* Ensure that a second record can be added to the query result\n"
459                         "\t* Ensures that both records can be retrieved";
460                 return AST_TEST_NOT_RUN;
461         case TEST_EXECUTE:
462                 break;
463         }
464
465         memset(&some_query, 0, sizeof(some_query));
466
467         if (ast_dns_resolver_set_result(&some_query, 0, 0, NOERROR, "asterisk.org",
468                                 DNS_ANSWER, DNS_ANSWER_SIZE)) {
469                 ast_test_status_update(test, "Unable to set result for DNS query\n");
470                 return AST_TEST_FAIL;
471         }
472
473         result = ast_dns_query_get_result(&some_query);
474         if (!result) {
475                 ast_test_status_update(test, "Unable to retrieve result from query\n");
476                 return AST_TEST_FAIL;
477         }
478
479         inet_pton(AF_INET, V4, v4_buf);
480
481         /* Nominal Record */
482         if (ast_dns_resolver_add_record(&some_query, records[0].type, records[0].class,
483                                 records[0].ttl, records[0].data, records[0].size)) {
484                 ast_test_status_update(test, "Unable to add nominal record to query result\n");
485                 return AST_TEST_FAIL;
486         }
487
488         /* I should only be able to retrieve one record */
489         record = ast_dns_result_get_records(result);
490         if (!record) {
491                 ast_test_status_update(test, "Unable to retrieve record from result\n");
492                 return AST_TEST_FAIL;
493         }
494
495         if (test_record(test, record, records[0].type, records[0].class, records[0].ttl,
496                                 records[0].data, records[0].size)) {
497                 return AST_TEST_FAIL;
498         }
499
500         if (ast_dns_record_get_next(record)) {
501                 ast_test_status_update(test, "Multiple records returned when only one was expected\n");
502                 return AST_TEST_FAIL;
503         }
504
505         inet_pton(AF_INET6, V6, v6_buf);
506
507         if (ast_dns_resolver_add_record(&some_query, records[1].type, records[1].class,
508                                 records[1].ttl, records[1].data, records[1].size)) {
509                 ast_test_status_update(test, "Unable to add second record to query result\n");
510                 return AST_TEST_FAIL;
511         }
512
513         for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
514                 int res;
515
516                 /* The order of returned records is not specified by the API. We use the record type
517                  * as the discriminator to determine which record data to expect.
518                  */
519                 if (ast_dns_record_get_rr_type(record) == records[0].type) {
520                         res = test_record(test, record, records[0].type, records[0].class, records[0].ttl, records[0].data, records[0].size);
521                         records[0].visited = 1;
522                 } else if (ast_dns_record_get_rr_type(record) == records[1].type) {
523                         res = test_record(test, record, records[1].type, records[1].class, records[1].ttl, records[1].data, records[1].size);
524                         records[1].visited = 1;
525                 } else {
526                         ast_test_status_update(test, "Unknown record type found in DNS results\n");
527                         return AST_TEST_FAIL;
528                 }
529
530                 if (res) {
531                         return AST_TEST_FAIL;
532                 }
533
534                 ++num_records_visited;
535         }
536
537         if (!records[0].visited || !records[1].visited) {
538                 ast_test_status_update(test, "Did not visit all added DNS records\n");
539                 return AST_TEST_FAIL;
540         }
541
542         if (num_records_visited != ARRAY_LEN(records)) {
543                 ast_test_status_update(test, "Did not visit the expected number of DNS records\n");
544                 return AST_TEST_FAIL;
545         }
546
547         return AST_TEST_PASS;
548 }
549
550 AST_TEST_DEFINE(resolver_add_record_off_nominal)
551 {
552         RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
553         struct ast_dns_query some_query;
554         static const char *V4 = "127.0.0.1";
555         static const size_t V4_BUFSIZE = sizeof(struct in_addr);
556         char v4_buf[V4_BUFSIZE];
557
558         switch (cmd) {
559         case TEST_INIT:
560                 info->name = "resolver_add_record_off_nominal";
561                 info->category = "/main/dns/";
562                 info->summary = "Test adding off-nominal DNS records to a query";
563                 info->description =
564                         "This test performs the following:\n"
565                         "\t* Ensure a nominal A record cannot be added if no result has been set.\n"
566                         "\t* Ensure that an A record with invalid RR types cannot be added to a query\n"
567                         "\t* Ensure that an A record with invalid RR classes cannot be added to a query\n"
568                         "\t* Ensure that an A record with invalid TTL cannot be added to a query\n"
569                         "\t* Ensure that an A record with NULL data cannot be added to a query\n"
570                         "\t* Ensure that an A record with invalid length cannot be added to a query";
571                 return AST_TEST_NOT_RUN;
572         case TEST_EXECUTE:
573                 break;
574         }
575
576         memset(&some_query, 0, sizeof(some_query));
577
578         inet_ntop(AF_INET, V4, v4_buf, V4_BUFSIZE);
579
580         /* Add record before setting result */
581         if (!ast_dns_resolver_add_record(&some_query, T_A, C_IN, 12345, v4_buf, V4_BUFSIZE)) {
582                 ast_test_status_update(test, "Successfully added DNS record to query before setting a result\n");
583                 return AST_TEST_FAIL;
584         }
585
586         if (ast_dns_resolver_set_result(&some_query, 0, 0, NOERROR, "asterisk.org",
587                                 DNS_ANSWER, DNS_ANSWER_SIZE)) {
588                 ast_test_status_update(test, "Unable to set result for DNS query\n");
589                 return AST_TEST_FAIL;
590         }
591
592         /* We get the result so it will be cleaned up when the function exits */
593         result = ast_dns_query_get_result(&some_query);
594
595         /* Invalid RR types */
596         if (!ast_dns_resolver_add_record(&some_query, -1, C_IN, 12345, v4_buf, V4_BUFSIZE)) {
597                 ast_test_status_update(test, "Successfully added DNS record with negative RR type\n");
598                 return AST_TEST_FAIL;
599         }
600
601         if (!ast_dns_resolver_add_record(&some_query, 65536 + 1, C_IN, 12345, v4_buf, V4_BUFSIZE)) {
602                 ast_test_status_update(test, "Successfully added DNS record with too large RR type\n");
603                 return AST_TEST_FAIL;
604         }
605
606         /* Invalid RR classes */
607         if (!ast_dns_resolver_add_record(&some_query, T_A, -1, 12345, v4_buf, V4_BUFSIZE)) {
608                 ast_test_status_update(test, "Successfully added DNS record with negative RR class\n");
609                 return AST_TEST_FAIL;
610         }
611
612         if (!ast_dns_resolver_add_record(&some_query, T_A, 65536 + 1, 12345, v4_buf, V4_BUFSIZE)) {
613                 ast_test_status_update(test, "Successfully added DNS record with too large RR class\n");
614                 return AST_TEST_FAIL;
615         }
616
617         /* Invalid TTL */
618         if (!ast_dns_resolver_add_record(&some_query, T_A, C_IN, -1, v4_buf, V4_BUFSIZE)) {
619                 ast_test_status_update(test, "Successfully added DNS record with negative TTL\n");
620                 return AST_TEST_FAIL;
621         }
622
623         /* No data */
624         if (!ast_dns_resolver_add_record(&some_query, T_A, C_IN, 12345, NULL, 0)) {
625                 ast_test_status_update(test, "Successfully added a DNS record with no data\n");
626                 return AST_TEST_FAIL;
627         }
628
629         /* Lie about the length */
630         if (!ast_dns_resolver_add_record(&some_query, T_A, C_IN, 12345, v4_buf, 0)) {
631                 ast_test_status_update(test, "Successfully added a DNS record with length zero\n");
632                 return AST_TEST_FAIL;
633         }
634
635         return AST_TEST_PASS;
636 }
637
638 /*!
639  * \brief File-scoped data used during resolver tests
640  *
641  * This data has to live at file-scope since it needs to be
642  * accessible by multiple threads.
643  */
644 static struct resolver_data {
645         /*! True if the resolver's resolve() method has been called */
646         int resolve_called;
647         /*! True if the resolver's cancel() method has been called */
648         int canceled;
649         /*! True if resolution successfully completed. This is mutually exclusive with \ref canceled */
650         int resolution_complete;
651         /*! Lock used for protecting \ref cancel_cond */
652         ast_mutex_t lock;
653         /*! Condition variable used to coordinate canceling a query */
654         ast_cond_t cancel_cond;
655 } test_resolver_data;
656
657 /*!
658  * \brief Thread spawned by the mock resolver
659  *
660  * All DNS resolvers are required to be asynchronous. The mock resolver
661  * spawns this thread for every DNS query that is executed.
662  *
663  * This thread waits for 5 seconds and then returns the same A record
664  * every time. The 5 second wait is to allow for the query to be
665  * canceled if desired
666  *
667  * \param dns_query The ast_dns_query that is being resolved
668  * \return NULL
669  */
670 static void *resolution_thread(void *dns_query)
671 {
672         struct ast_dns_query *query = dns_query;
673         struct timespec timeout;
674
675         static const char *V4 = "127.0.0.1";
676         static const size_t V4_BUFSIZE = sizeof(struct in_addr);
677         char v4_buf[V4_BUFSIZE];
678
679         timeout = ast_tsnow();
680         timeout.tv_sec += 5;
681
682         ast_mutex_lock(&test_resolver_data.lock);
683         while (!test_resolver_data.canceled) {
684                 if (ast_cond_timedwait(&test_resolver_data.cancel_cond, &test_resolver_data.lock, &timeout) == ETIMEDOUT) {
685                         break;
686                 }
687         }
688         ast_mutex_unlock(&test_resolver_data.lock);
689
690         if (test_resolver_data.canceled) {
691                 ast_dns_resolver_completed(query);
692                 ao2_ref(query, -1);
693                 return NULL;
694         }
695
696         ast_dns_resolver_set_result(query, 0, 0, NOERROR, "asterisk.org", DNS_ANSWER, DNS_ANSWER_SIZE);
697
698         inet_pton(AF_INET, V4, v4_buf);
699         ast_dns_resolver_add_record(query, T_A, C_IN, 12345, v4_buf, V4_BUFSIZE);
700
701         test_resolver_data.resolution_complete = 1;
702         ast_dns_resolver_completed(query);
703
704         ao2_ref(query, -1);
705         return NULL;
706 }
707
708 /*!
709  * \brief Mock resolver's resolve method
710  *
711  * \param query The query to resolve
712  * \retval 0 Successfully spawned resolution thread
713  * \retval non-zero Failed to spawn the resolution thread
714  */
715 static int test_resolve(struct ast_dns_query *query)
716 {
717         pthread_t resolver_thread;
718
719         test_resolver_data.resolve_called = 1;
720         return ast_pthread_create_detached(&resolver_thread, NULL, resolution_thread, ao2_bump(query));
721 }
722
723 /*!
724  * \brief Mock resolver's cancel method
725  *
726  * This signals the resolution thread not to return any DNS results.
727  *
728  * \param query DNS query to cancel
729  * \return 0
730  */
731 static int test_cancel(struct ast_dns_query *query)
732 {
733         ast_mutex_lock(&test_resolver_data.lock);
734         test_resolver_data.canceled = 1;
735         ast_cond_signal(&test_resolver_data.cancel_cond);
736         ast_mutex_unlock(&test_resolver_data.lock);
737
738         return 0;
739 }
740
741 /*!
742  * \brief Initialize global mock resolver data.
743  *
744  * This must be called at the beginning of tests that use the mock resolver
745  */
746 static void resolver_data_init(void)
747 {
748         test_resolver_data.resolve_called = 0;
749         test_resolver_data.canceled = 0;
750         test_resolver_data.resolution_complete = 0;
751
752         ast_mutex_init(&test_resolver_data.lock);
753         ast_cond_init(&test_resolver_data.cancel_cond, NULL);
754 }
755
756 /*!
757  * \brief Cleanup global mock resolver data
758  *
759  * This must be called at the end of tests that use the mock resolver
760  */
761 static void resolver_data_cleanup(void)
762 {
763         ast_mutex_destroy(&test_resolver_data.lock);
764         ast_cond_destroy(&test_resolver_data.cancel_cond);
765 }
766
767 /*!
768  * \brief The mock resolver
769  *
770  * The mock resolver does not care about the DNS query that is
771  * actually being made on it. It simply regurgitates the same
772  * DNS record no matter what.
773  */
774 static struct ast_dns_resolver test_resolver = {
775         .name = "test",
776         .priority = 0,
777         .resolve = test_resolve,
778         .cancel = test_cancel,
779 };
780
781 AST_TEST_DEFINE(resolver_resolve_sync)
782 {
783         RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
784         enum ast_test_result_state res = AST_TEST_PASS;
785
786         switch (cmd) {
787         case TEST_INIT:
788                 info->name = "resolver_resolve_sync";
789                 info->category = "/main/dns/";
790                 info->summary = "Test a nominal synchronous DNS resolution";
791                 info->description =
792                         "This test performs a synchronous DNS resolution of a domain. The goal of this\n"
793                         "test is not to check the records for accuracy. Rather, the goal is to ensure that\n"
794                         "the resolver is called into as expected, that the query completes entirely before\n"
795                         "returning from the synchronous resolution, that nothing tried to cancel the resolution\n,"
796                         "and that some records were returned.";
797                 return AST_TEST_NOT_RUN;
798         case TEST_EXECUTE:
799                 break;
800         }
801
802         if (ast_dns_resolver_register(&test_resolver)) {
803                 ast_test_status_update(test, "Unable to register test resolver\n");
804                 return AST_TEST_FAIL;
805         }
806
807         resolver_data_init();
808
809         if (ast_dns_resolve("asterisk.org", T_A, C_IN, &result)) {
810                 ast_test_status_update(test, "Resolution of address failed\n");
811                 res = AST_TEST_FAIL;
812                 goto cleanup;
813         }
814
815         if (!result) {
816                 ast_test_status_update(test, "DNS resolution returned a NULL result\n");
817                 res = AST_TEST_FAIL;
818                 goto cleanup;
819         }
820
821         if (!test_resolver_data.resolve_called) {
822                 ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");
823                 res = AST_TEST_FAIL;
824                 goto cleanup;
825         }
826
827         if (test_resolver_data.canceled) {
828                 ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");
829                 res = AST_TEST_FAIL;
830                 goto cleanup;
831         }
832
833         if (!test_resolver_data.resolution_complete) {
834                 ast_test_status_update(test, "Synchronous resolution completed early?\n");
835                 res = AST_TEST_FAIL;
836                 goto cleanup;
837         }
838
839         if (!ast_dns_result_get_records(result)) {
840                 ast_test_status_update(test, "Synchronous resolution yielded no records.\n");
841                 res = AST_TEST_FAIL;
842                 goto cleanup;
843         }
844
845 cleanup:
846         ast_dns_resolver_unregister(&test_resolver);
847         resolver_data_cleanup();
848         return res;
849 }
850
851 /*!
852  * \brief A resolve() method that simply fails
853  *
854  * \param query The DNS query to resolve. This is ignored.
855  * \return -1
856  */
857 static int fail_resolve(struct ast_dns_query *query)
858 {
859         return -1;
860 }
861
862 AST_TEST_DEFINE(resolver_resolve_sync_off_nominal)
863 {
864         struct ast_dns_resolver terrible_resolver = {
865                 .name = "Uwe Boll's Filmography",
866                 .priority = 0,
867                 .resolve = fail_resolve,
868                 .cancel = stub_cancel,
869         };
870
871         struct ast_dns_result *result = NULL;
872
873         struct dns_resolve_data {
874                 const char *name;
875                 int rr_type;
876                 int rr_class;
877                 struct ast_dns_result **result;
878         } resolves [] = {
879                 { NULL,           T_A,       C_IN,      &result },
880                 { "asterisk.org", -1,        C_IN,      &result },
881                 { "asterisk.org", 65536 + 1, C_IN,      &result },
882                 { "asterisk.org", T_A,       -1,        &result },
883                 { "asterisk.org", T_A,       65536 + 1, &result },
884                 { "asterisk.org", T_A,       C_IN,      NULL },
885         };
886
887         int i;
888
889         enum ast_test_result_state res = AST_TEST_PASS;
890
891         switch (cmd) {
892         case TEST_INIT:
893                 info->name = "resolver_resolve_sync_off_nominal";
894                 info->category = "/main/dns/";
895                 info->summary = "Test off-nominal synchronous DNS resolution";
896                 info->description =
897                         "This test performs several off-nominal synchronous DNS resolutions:\n"
898                         "\t* Attempt resolution with NULL name\n"
899                         "\t* Attempt resolution with invalid RR type\n"
900                         "\t* Attempt resolution with invalid RR class\n"
901                         "\t* Attempt resolution with NULL result pointer\n"
902                         "\t* Attempt resolution with resolver that returns an error";
903                 return AST_TEST_NOT_RUN;
904         case TEST_EXECUTE:
905                 break;
906         }
907
908         if (ast_dns_resolver_register(&test_resolver)) {
909                 ast_test_status_update(test, "Failed to register test resolver\n");
910                 return AST_TEST_FAIL;
911         }
912
913         for (i = 0; i < ARRAY_LEN(resolves); ++i) {
914                 if (!ast_dns_resolve(resolves[i].name, resolves[i].rr_type, resolves[i].rr_class, resolves[i].result)) {
915                         ast_test_status_update(test, "Successfully resolved DNS query with invalid parameters\n");
916                         res = AST_TEST_FAIL;
917                 } else if (result) {
918                         ast_test_status_update(test, "Failed resolution set a non-NULL result\n");
919                         ast_dns_result_free(result);
920                         res = AST_TEST_FAIL;
921                 }
922         }
923
924         ast_dns_resolver_unregister(&test_resolver);
925
926         /* As a final test, try a legitimate query with a bad resolver */
927         if (ast_dns_resolver_register(&terrible_resolver)) {
928                 ast_test_status_update(test, "Failed to register the terrible resolver\n");
929                 return AST_TEST_FAIL;
930         }
931
932         if (!ast_dns_resolve("asterisk.org", T_A, C_IN, &result)) {
933                 ast_test_status_update(test, "DNS resolution succeeded when we expected it not to\n");
934                 ast_dns_resolver_unregister(&terrible_resolver);
935                 return AST_TEST_FAIL;
936         }
937
938         ast_dns_resolver_unregister(&terrible_resolver);
939
940         if (result) {
941                 ast_test_status_update(test, "Failed DNS resolution set the result to something non-NULL\n");
942                 ast_dns_result_free(result);
943                 return AST_TEST_FAIL;
944         }
945
946         return res;
947 }
948
949 /*!
950  * \brief Data used by async result callback
951  *
952  * This is the typical combination of boolean, lock, and condition
953  * used to synchronize the activities of two threads. In this case,
954  * the testing thread waits on the condition, and the async callback
955  * signals the condition when the asynchronous callback is complete.
956  */
957 struct async_resolution_data {
958         int complete;
959         ast_mutex_t lock;
960         ast_cond_t cond;
961 };
962
963 /*!
964  * \brief Destructor for async_resolution_data
965  */
966 static void async_data_destructor(void *obj)
967 {
968         struct async_resolution_data *async_data = obj;
969
970         ast_mutex_destroy(&async_data->lock);
971         ast_cond_destroy(&async_data->cond);
972 }
973
974 /*!
975  * \brief Allocation/initialization for async_resolution_data
976  *
977  * The DNS core mandates that a query's user data has to be ao2 allocated,
978  * so this is a helper method for doing that.
979  *
980  * \retval NULL Failed allocation
981  * \retval non-NULL Newly allocated async_resolution_data
982  */
983 static struct async_resolution_data *async_data_alloc(void)
984 {
985         struct async_resolution_data *async_data;
986
987         async_data = ao2_alloc(sizeof(*async_data), async_data_destructor);
988         if (!async_data) {
989                 return NULL;
990         }
991
992         async_data->complete = 0;
993         ast_mutex_init(&async_data->lock);
994         ast_cond_init(&async_data->cond, NULL);
995
996         return async_data;
997 }
998
999 /*!
1000  * \brief Async DNS callback
1001  *
1002  * This is called when an async query completes, either because it resolved or
1003  * because it was canceled. In our case, this callback is used to signal to the
1004  * test that it can continue
1005  *
1006  * \param query The DNS query that has completed
1007  */
1008 static void async_callback(const struct ast_dns_query *query)
1009 {
1010         struct async_resolution_data *async_data = ast_dns_query_get_data(query);
1011
1012         ast_mutex_lock(&async_data->lock);
1013         async_data->complete = 1;
1014         ast_cond_signal(&async_data->cond);
1015         ast_mutex_unlock(&async_data->lock);
1016 }
1017
1018 AST_TEST_DEFINE(resolver_resolve_async)
1019 {
1020         RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup);
1021         RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
1022         struct ast_dns_result *result;
1023         enum ast_test_result_state res = AST_TEST_PASS;
1024         struct timespec timeout;
1025
1026         switch (cmd) {
1027         case TEST_INIT:
1028                 info->name = "resolver_resolve_async";
1029                 info->category = "/main/dns/";
1030                 info->summary = "Test a nominal asynchronous DNS resolution";
1031                 info->description =
1032                         "This test performs an asynchronous DNS resolution of a domain. The goal of this\n"
1033                         "test is not to check the records for accuracy. Rather, the goal is to ensure that\n"
1034                         "the resolver is called into as expected, that we regain control before the query\n"
1035                         "is completed, and to ensure that nothing tried to cancel the resolution.";
1036                 return AST_TEST_NOT_RUN;
1037         case TEST_EXECUTE:
1038                 break;
1039         }
1040
1041         if (ast_dns_resolver_register(&test_resolver)) {
1042                 ast_test_status_update(test, "Unable to register test resolver\n");
1043                 return AST_TEST_FAIL;
1044         }
1045
1046         resolver_data_init();
1047
1048         async_data = async_data_alloc();
1049         if (!async_data) {
1050                 ast_test_status_update(test, "Failed to allocate asynchronous data\n");
1051                 res = AST_TEST_FAIL;
1052                 goto cleanup;
1053         }
1054
1055         active = ast_dns_resolve_async("asterisk.org", T_A, C_IN, async_callback, async_data);
1056         if (!active) {
1057                 ast_test_status_update(test, "Asynchronous resolution of address failed\n");
1058                 res = AST_TEST_FAIL;
1059                 goto cleanup;
1060         }
1061
1062         if (!test_resolver_data.resolve_called) {
1063                 ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");
1064                 res = AST_TEST_FAIL;
1065                 goto cleanup;
1066         }
1067
1068         if (test_resolver_data.canceled) {
1069                 ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");
1070                 res = AST_TEST_FAIL;
1071                 goto cleanup;
1072         }
1073
1074         timeout = ast_tsnow();
1075         timeout.tv_sec += 10;
1076         ast_mutex_lock(&async_data->lock);
1077         while (!async_data->complete) {
1078                 if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {
1079                         break;
1080                 }
1081         }
1082         ast_mutex_unlock(&async_data->lock);
1083
1084         if (!async_data->complete) {
1085                 ast_test_status_update(test, "Asynchronous resolution timed out\n");
1086                 res = AST_TEST_FAIL;
1087                 goto cleanup;
1088         }
1089
1090         if (!test_resolver_data.resolution_complete) {
1091                 ast_test_status_update(test, "Asynchronous resolution completed early?\n");
1092                 res = AST_TEST_FAIL;
1093                 goto cleanup;
1094         }
1095
1096         result = ast_dns_query_get_result(active->query);
1097         if (!result) {
1098                 ast_test_status_update(test, "Asynchronous resolution yielded no result\n");
1099                 res = AST_TEST_FAIL;
1100                 goto cleanup;
1101         }
1102
1103         if (!ast_dns_result_get_records(result)) {
1104                 ast_test_status_update(test, "Asynchronous result had no records\n");
1105                 res = AST_TEST_FAIL;
1106                 goto cleanup;
1107         }
1108
1109 cleanup:
1110         ast_dns_resolver_unregister(&test_resolver);
1111         resolver_data_cleanup();
1112         return res;
1113 }
1114
1115 /*! Stub async resolution callback */
1116 static void stub_callback(const struct ast_dns_query *query)
1117 {
1118         return;
1119 }
1120
1121 AST_TEST_DEFINE(resolver_resolve_async_off_nominal)
1122 {
1123         struct ast_dns_resolver terrible_resolver = {
1124                 .name = "Ed Wood's Filmography",
1125                 .priority = 0,
1126                 .resolve = fail_resolve,
1127                 .cancel = stub_cancel,
1128         };
1129
1130         struct dns_resolve_data {
1131                 const char *name;
1132                 int rr_type;
1133                 int rr_class;
1134                 ast_dns_resolve_callback callback;
1135         } resolves [] = {
1136                 { NULL,           T_A,       C_IN,      stub_callback },
1137                 { "asterisk.org", -1,        C_IN,      stub_callback },
1138                 { "asterisk.org", 65536 + 1, C_IN,      stub_callback },
1139                 { "asterisk.org", T_A,       -1,        stub_callback },
1140                 { "asterisk.org", T_A,       65536 + 1, stub_callback },
1141                 { "asterisk.org", T_A,       C_IN,      NULL },
1142         };
1143
1144         struct ast_dns_query_active *active;
1145         enum ast_test_result_state res = AST_TEST_PASS;
1146         int i;
1147
1148         switch (cmd) {
1149         case TEST_INIT:
1150                 info->name = "resolver_resolve_async_off_nominal";
1151                 info->category = "/main/dns/";
1152                 info->summary = "Test off-nominal asynchronous DNS resolution";
1153                 info->description =
1154                         "This test performs several off-nominal asynchronous DNS resolutions:\n"
1155                         "\t* Attempt resolution with NULL name\n"
1156                         "\t* Attempt resolution with invalid RR type\n"
1157                         "\t* Attempt resolution with invalid RR class\n"
1158                         "\t* Attempt resolution with NULL callback pointer\n"
1159                         "\t* Attempt resolution with resolver that returns an error";
1160                 return AST_TEST_NOT_RUN;
1161         case TEST_EXECUTE:
1162                 break;
1163         }
1164
1165         if (ast_dns_resolver_register(&test_resolver)) {
1166                 ast_test_status_update(test, "Failed to register test resolver\n");
1167                 return AST_TEST_FAIL;
1168         }
1169
1170         for (i = 0; i < ARRAY_LEN(resolves); ++i) {
1171                 active = ast_dns_resolve_async(resolves[i].name, resolves[i].rr_type, resolves[i].rr_class,
1172                                 resolves[i].callback, NULL);
1173                 if (active) {
1174                         ast_test_status_update(test, "Successfully performed asynchronous resolution with invalid data\n");
1175                         ao2_ref(active, -1);
1176                         res = AST_TEST_FAIL;
1177                 }
1178         }
1179
1180         ast_dns_resolver_unregister(&test_resolver);
1181
1182         if (ast_dns_resolver_register(&terrible_resolver)) {
1183                 ast_test_status_update(test, "Failed to register the DNS resolver\n");
1184                 return AST_TEST_FAIL;
1185         }
1186
1187         active = ast_dns_resolve_async("asterisk.org", T_A, C_IN, stub_callback, NULL);
1188
1189         ast_dns_resolver_unregister(&terrible_resolver);
1190
1191         if (active) {
1192                 ast_test_status_update(test, "Successfully performed asynchronous resolution with invalid data\n");
1193                 ao2_ref(active, -1);
1194                 return AST_TEST_FAIL;
1195         }
1196
1197         return res;
1198 }
1199
1200 AST_TEST_DEFINE(resolver_resolve_async_cancel)
1201 {
1202         RAII_VAR(struct async_resolution_data *, async_data, NULL, ao2_cleanup);
1203         RAII_VAR(struct ast_dns_query_active *, active, NULL, ao2_cleanup);
1204         struct ast_dns_result *result;
1205         enum ast_test_result_state res = AST_TEST_PASS;
1206         struct timespec timeout;
1207
1208         switch (cmd) {
1209         case TEST_INIT:
1210                 info->name = "resolver_resolve_async_cancel";
1211                 info->category = "/main/dns/";
1212                 info->summary = "Test canceling an asynchronous DNS resolution";
1213                 info->description =
1214                         "This test performs an asynchronous DNS resolution of a domain and then cancels\n"
1215                         "the resolution. The goal of this test is to ensure that the cancel() callback of\n"
1216                         "the resolver is called and that it properly interrupts the resolution such that no\n"
1217                         "records are returned.";
1218                 return AST_TEST_NOT_RUN;
1219         case TEST_EXECUTE:
1220                 break;
1221         }
1222
1223         if (ast_dns_resolver_register(&test_resolver)) {
1224                 ast_test_status_update(test, "Unable to register test resolver\n");
1225                 return AST_TEST_FAIL;
1226         }
1227
1228         resolver_data_init();
1229
1230         async_data = async_data_alloc();
1231         if (!async_data) {
1232                 ast_test_status_update(test, "Failed to allocate asynchronous data\n");
1233                 res = AST_TEST_FAIL;
1234                 goto cleanup;
1235         }
1236
1237         active = ast_dns_resolve_async("asterisk.org", T_A, C_IN, async_callback, async_data);
1238         if (!active) {
1239                 ast_test_status_update(test, "Asynchronous resolution of address failed\n");
1240                 res = AST_TEST_FAIL;
1241                 goto cleanup;
1242         }
1243
1244         if (!test_resolver_data.resolve_called) {
1245                 ast_test_status_update(test, "DNS resolution did not call resolver's resolve() method\n");
1246                 res = AST_TEST_FAIL;
1247                 goto cleanup;
1248         }
1249
1250         if (test_resolver_data.canceled) {
1251                 ast_test_status_update(test, "Resolver's cancel() method called for no reason\n");
1252                 res = AST_TEST_FAIL;
1253                 goto cleanup;
1254         }
1255
1256         ast_dns_resolve_cancel(active);
1257
1258         if (!test_resolver_data.canceled) {
1259                 ast_test_status_update(test, "Resolver's cancel() method was not called\n");
1260                 res = AST_TEST_FAIL;
1261                 goto cleanup;
1262         }
1263
1264         timeout = ast_tsnow();
1265         timeout.tv_sec += 10;
1266         ast_mutex_lock(&async_data->lock);
1267         while (!async_data->complete) {
1268                 if (ast_cond_timedwait(&async_data->cond, &async_data->lock, &timeout) == ETIMEDOUT) {
1269                         break;
1270                 }
1271         }
1272         ast_mutex_unlock(&async_data->lock);
1273
1274         if (!async_data->complete) {
1275                 ast_test_status_update(test, "Asynchronous resolution timed out\n");
1276                 res = AST_TEST_FAIL;
1277                 goto cleanup;
1278         }
1279
1280         if (test_resolver_data.resolution_complete) {
1281                 ast_test_status_update(test, "Resolution completed without cancelation\n");
1282                 res = AST_TEST_FAIL;
1283                 goto cleanup;
1284         }
1285
1286         result = ast_dns_query_get_result(active->query);
1287         if (result) {
1288                 ast_test_status_update(test, "Canceled resolution had a result\n");
1289                 res = AST_TEST_FAIL;
1290                 goto cleanup;
1291         }
1292
1293 cleanup:
1294         ast_dns_resolver_unregister(&test_resolver);
1295         resolver_data_cleanup();
1296         return res;
1297 }
1298
1299 static int unload_module(void)
1300 {
1301         AST_TEST_UNREGISTER(resolver_register_unregister);
1302         AST_TEST_UNREGISTER(resolver_register_off_nominal);
1303         AST_TEST_UNREGISTER(resolver_unregister_off_nominal);
1304         AST_TEST_UNREGISTER(resolver_data);
1305         AST_TEST_UNREGISTER(resolver_set_result);
1306         AST_TEST_UNREGISTER(resolver_set_result_off_nominal);
1307         AST_TEST_UNREGISTER(resolver_add_record);
1308         AST_TEST_UNREGISTER(resolver_add_record_off_nominal);
1309         AST_TEST_UNREGISTER(resolver_resolve_sync);
1310         AST_TEST_UNREGISTER(resolver_resolve_sync_off_nominal);
1311         AST_TEST_UNREGISTER(resolver_resolve_async);
1312         AST_TEST_UNREGISTER(resolver_resolve_async_off_nominal);
1313         AST_TEST_UNREGISTER(resolver_resolve_async_cancel);
1314
1315         return 0;
1316 }
1317
1318 static int load_module(void)
1319 {
1320         AST_TEST_REGISTER(resolver_register_unregister);
1321         AST_TEST_REGISTER(resolver_register_off_nominal);
1322         AST_TEST_REGISTER(resolver_unregister_off_nominal);
1323         AST_TEST_REGISTER(resolver_data);
1324         AST_TEST_REGISTER(resolver_set_result);
1325         AST_TEST_REGISTER(resolver_set_result_off_nominal);
1326         AST_TEST_REGISTER(resolver_add_record);
1327         AST_TEST_REGISTER(resolver_add_record_off_nominal);
1328         AST_TEST_REGISTER(resolver_resolve_sync);
1329         AST_TEST_REGISTER(resolver_resolve_sync_off_nominal);
1330         AST_TEST_REGISTER(resolver_resolve_async);
1331         AST_TEST_REGISTER(resolver_resolve_async_off_nominal);
1332         AST_TEST_REGISTER(resolver_resolve_async_cancel);
1333
1334         return AST_MODULE_LOAD_SUCCESS;
1335 }
1336
1337 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DNS API Tests");