Merge "core: Tweak startup order."
[asterisk/asterisk.git] / tests / test_dns_srv.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  * Mark Michelson <mmichelson@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*** MODULEINFO
21         <depend>TEST_FRAMEWORK</depend>
22         <support_level>core</support_level>
23  ***/
24
25 #include "asterisk.h"
26
27 #include <arpa/nameser.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_srv.h"
34 #include "asterisk/dns_test.h"
35
36 struct srv_record {
37         uint16_t priority;
38         uint16_t weight;
39         uint16_t port;
40         const char *host;
41         unsigned int ignore_priority;
42         unsigned int ignore_weight;
43         unsigned int ignore_port;
44         unsigned int ignore_host;
45 };
46
47 static int generate_srv_record(void *dns_record, char *buf)
48 {
49         struct srv_record *record = dns_record;
50         uint16_t priority = htons(record->priority);
51         uint16_t weight = htons(record->weight);
52         uint16_t port = htons(record->port);
53         char *ptr = buf;
54
55         if (!record->ignore_priority) {
56                 memcpy(ptr, &priority, sizeof(priority));
57                 ptr += sizeof(priority);
58         }
59
60         if (!record->ignore_weight) {
61                 memcpy(ptr, &weight, sizeof(weight));
62                 ptr += sizeof(weight);
63         }
64
65         if (!record->ignore_port) {
66                 memcpy(ptr, &port, sizeof(port));
67                 ptr += sizeof(port);
68         }
69
70         if (!record->ignore_host) {
71                 ptr += ast_dns_test_write_domain(record->host, ptr);
72         }
73
74         return ptr - buf;
75 }
76
77 static struct srv_record *test_records;
78 static int num_test_records;
79 static char ans_buffer[1024];
80
81 static void *srv_thread(void *dns_query)
82 {
83         struct ast_dns_query *query = dns_query;
84         int i;
85         int ans_size;
86
87         ans_size = ast_dns_test_generate_result(query, test_records, num_test_records,
88                         sizeof(struct srv_record), generate_srv_record, ans_buffer);
89
90         ast_dns_resolver_set_result(query, 0, 0, NOERROR, "goose.feathers", ans_buffer, ans_size);
91
92         for (i = 0; i < num_test_records; ++i) {
93                 char record[128];
94                 int srv_size;
95
96                 srv_size = generate_srv_record(&test_records[i], record);
97                 ast_dns_resolver_add_record(query, T_SRV, C_IN, 12345, record, srv_size);
98         }
99
100         ast_dns_resolver_completed(query);
101
102         ao2_ref(query, -1);
103         return NULL;
104 }
105
106 static int srv_resolve(struct ast_dns_query *query)
107 {
108         pthread_t thread;
109
110         return ast_pthread_create_detached(&thread, NULL, srv_thread, ao2_bump(query));
111 }
112
113 static int srv_cancel(struct ast_dns_query *query)
114 {
115         return -1;
116 }
117
118 static struct ast_dns_resolver srv_resolver = {
119         .name = "srv_test",
120         .priority = 0,
121         .resolve = srv_resolve,
122         .cancel = srv_cancel,
123 };
124
125 static enum ast_test_result_state nominal_test(struct ast_test *test, struct srv_record *records,
126         int *srv_record_order, int num_records)
127 {
128         RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
129         const struct ast_dns_record *record;
130         enum ast_test_result_state res = AST_TEST_PASS;
131         int i;
132
133         test_records = records;
134         num_test_records = num_records;
135         memset(ans_buffer, 0, sizeof(ans_buffer));
136
137         ast_dns_resolver_register(&srv_resolver);
138
139         if (ast_dns_resolve("goose.feathers", T_SRV, C_IN, &result)) {
140                 ast_test_status_update(test, "DNS resolution failed\n");
141                 res = AST_TEST_FAIL;
142                 goto cleanup;
143         }
144
145         if (!result) {
146                 ast_test_status_update(test, "DNS resolution returned no result\n");
147                 res = AST_TEST_FAIL;
148                 goto cleanup;
149         }
150
151         i = 0;
152         for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
153                 if (ast_dns_srv_get_priority(record) != records[srv_record_order[i]].priority) {
154                         ast_test_status_update(test, "Unexpected priority in returned SRV record\n");
155                         res = AST_TEST_FAIL;
156                 }
157                 if (ast_dns_srv_get_weight(record) != records[srv_record_order[i]].weight) {
158                         ast_test_status_update(test, "Unexpected weight in returned SRV record\n");
159                         res = AST_TEST_FAIL;
160                 }
161                 if (ast_dns_srv_get_port(record) != records[srv_record_order[i]].port) {
162                         ast_test_status_update(test, "Unexpected port in returned SRV record\n");
163                         res = AST_TEST_FAIL;
164                 }
165                 if (strcmp(ast_dns_srv_get_host(record), records[srv_record_order[i]].host)) {
166                         ast_test_status_update(test, "Unexpected host in returned SRV record\n");
167                         res = AST_TEST_FAIL;
168                 }
169                 ++i;
170         }
171
172         if (i != num_records) {
173                 ast_test_status_update(test, "Unexpected number of records returned in SRV lookup\n");
174                 res = AST_TEST_FAIL;
175         }
176
177 cleanup:
178
179         ast_dns_resolver_unregister(&srv_resolver);
180
181         test_records = NULL;
182         num_test_records = 0;
183         memset(ans_buffer, 0, sizeof(ans_buffer));
184
185         return res;
186 }
187
188 AST_TEST_DEFINE(srv_resolve_single_record)
189 {
190         struct srv_record records[] = {
191                 { 10, 10, 5060, "goose.down" },
192         };
193
194         int srv_record_order[] = { 0, };
195
196         switch (cmd) {
197         case TEST_INIT:
198                 info->name = "srv_resolve_single_record";
199                 info->category = "/main/dns/srv/";
200                 info->summary = "Test an SRV lookup which returns a single record";
201                 info->description = "This test defines a single SRV record and performs a\n"
202                         "resolution of the domain to which they belong. The test ensures that all\n"
203                         "fields of the SRV record are parsed correctly";
204                 return AST_TEST_NOT_RUN;
205         case TEST_EXECUTE:
206                 break;
207         }
208
209         return nominal_test(test, records, srv_record_order, ARRAY_LEN(records));
210 }
211
212 AST_TEST_DEFINE(srv_resolve_sort_priority)
213 {
214         struct srv_record records[] = {
215                 { 20, 10, 5060, "tacos" },
216                 { 10, 10, 5060, "goose.down" },
217         };
218         int srv_record_order[] = { 1, 0};
219
220         switch (cmd) {
221         case TEST_INIT:
222                 info->name = "srv_resolve_sort_priority";
223                 info->category = "/main/dns/srv/";
224                 info->summary = "Test an SRV lookup which returns two records with differing priorities";
225                 info->description = "This test defines two SRV records with differing priorities and\n"
226                         "performs a resolution of the domain to which they belong. The test ensures that\n"
227                         "the two records are sorted according to priority and that all fields of the SRV\n"
228                         "records are parsed correctly";
229                 return AST_TEST_NOT_RUN;
230         case TEST_EXECUTE:
231                 break;
232         }
233
234         return nominal_test(test, records, srv_record_order, ARRAY_LEN(records));
235 }
236
237 AST_TEST_DEFINE(srv_resolve_same_priority_zero_weight)
238 {
239         struct srv_record records[] = {
240                 { 10, 0, 5060, "tacos" },
241                 { 10, 10, 5060, "goose.down" },
242         };
243         int srv_record_order[] = { 1, 0};
244
245         switch (cmd) {
246         case TEST_INIT:
247                 info->name = "srv_resolve_same_priority_zero_weight";
248                 info->category = "/main/dns/srv/";
249                 info->summary = "Test an SRV lookup which returns two records with same priority but different weights";
250                 info->description = "This test defines two SRV records with same priority but different weights and\n"
251                         "performs a resolution of the domain to which they belong. The test ensures that\n"
252                         "the record with zero weight comes last and that all fields of the SRV\n"
253                         "records are parsed correctly";
254                 return AST_TEST_NOT_RUN;
255         case TEST_EXECUTE:
256                 break;
257         }
258
259         return nominal_test(test, records, srv_record_order, ARRAY_LEN(records));
260 }
261
262 AST_TEST_DEFINE(srv_resolve_same_priority_different_weights)
263 {
264         struct srv_record records[] = {
265                 { 10, 10, 5060, "tacos" },
266                 { 10, 20, 5060, "goose.down" },
267         };
268
269         int srv_record_occurence[2] = { 0, };
270         enum ast_test_result_state res = AST_TEST_PASS;
271         int count = 0;
272
273         switch (cmd) {
274         case TEST_INIT:
275                 info->name = "srv_resolve_same_priority_different_weights";
276                 info->category = "/main/dns/srv/";
277                 info->summary = "Test an SRV lookup which returns two records with same priority but different weights";
278                 info->description = "This test defines two SRV records with same priority but different weights and\n"
279                         "performs a resolution of the domain to which they belong. The test ensures that\n"
280                         "the record with higher weight occurs more often than the one of lesser weight";
281                 return AST_TEST_NOT_RUN;
282         case TEST_EXECUTE:
283                 break;
284         }
285
286         test_records = records;
287         num_test_records = ARRAY_LEN(records);
288
289         ast_dns_resolver_register(&srv_resolver);
290
291         for (count = 0; count < 100; count++) {
292                 struct ast_dns_result *result;
293                 const struct ast_dns_record *record;
294                 int i;
295
296                 memset(ans_buffer, 0, sizeof(ans_buffer));
297
298                 if (ast_dns_resolve("goose.feathers", T_SRV, C_IN, &result)) {
299                         ast_test_status_update(test, "DNS resolution failed\n");
300                         res = AST_TEST_FAIL;
301                         goto cleanup;
302                 }
303
304                 if (!result) {
305                         ast_test_status_update(test, "DNS resolution returned no result\n");
306                         res = AST_TEST_FAIL;
307                         goto cleanup;
308                 }
309
310                 record = ast_dns_result_get_records(result);
311                 for (i = 0; i < ARRAY_LEN(records); i++) {
312                         if (ast_dns_srv_get_priority(record) != records[i].priority) {
313                                 continue;
314                         }
315                         if (ast_dns_srv_get_weight(record) != records[i].weight) {
316                                 continue;
317                         }
318                         if (ast_dns_srv_get_port(record) != records[i].port) {
319                                 continue;
320                         }
321                         if (strcmp(ast_dns_srv_get_host(record), records[i].host)) {
322                                 continue;
323                         }
324
325                         srv_record_occurence[i]++;
326                         break;
327                 }
328
329                 ast_dns_result_free(result);
330         }
331
332         if (srv_record_occurence[0] > srv_record_occurence[1]) {
333                 ast_test_status_update(test, "SRV sorting resulted in lesser weight being returned more often\n");
334                 res = AST_TEST_FAIL;
335         }
336
337 cleanup:
338
339         ast_dns_resolver_unregister(&srv_resolver);
340
341         test_records = NULL;
342         num_test_records = 0;
343         memset(ans_buffer, 0, sizeof(ans_buffer));
344
345         return res;
346 }
347
348 AST_TEST_DEFINE(srv_resolve_different_priorities_different_weights)
349 {
350         struct srv_record records[] = {
351                 { 10, 10, 5060, "tacos" },
352                 { 10, 20, 5060, "goose.down" },
353                 { 5, 80, 5060, "moo" },
354                 { 5, 10, 5060, "Canada" },
355         };
356
357         int srv_record_priority[4] = { 5, 5, 10, 10 };
358         int srv_record_occurence[4] = { 0, };
359         enum ast_test_result_state res = AST_TEST_PASS;
360         int count = 0;
361
362         switch (cmd) {
363         case TEST_INIT:
364                 info->name = "srv_resolve_different_priorities_different_weights";
365                 info->category = "/main/dns/srv/";
366                 info->summary = "Test an SRV lookup which returns four records with different priority and different weights";
367                 info->description = "This test defines four SRV records, two with one priority and two with another priority,\n"
368                         "and different weights and performs a resolution of the domain to which they belong.\n"
369                         "The test ensures that the priorities are sorted properly and that the records with higher weight\n"
370                         "occur more often than the ones of less weight.";
371                 return AST_TEST_NOT_RUN;
372         case TEST_EXECUTE:
373                 break;
374         }
375
376         test_records = records;
377         num_test_records = ARRAY_LEN(records);
378
379         ast_dns_resolver_register(&srv_resolver);
380
381         for (count = 0; count < 100; count++) {
382                 struct ast_dns_result *result;
383                 const struct ast_dns_record *record;
384                 int i;
385
386                 memset(ans_buffer, 0, sizeof(ans_buffer));
387
388                 if (ast_dns_resolve("goose.feathers", T_SRV, C_IN, &result)) {
389                         ast_test_status_update(test, "DNS resolution failed\n");
390                         res = AST_TEST_FAIL;
391                         goto cleanup;
392                 }
393
394                 if (!result) {
395                         ast_test_status_update(test, "DNS resolution returned no result\n");
396                         res = AST_TEST_FAIL;
397                         goto cleanup;
398                 }
399
400                 i = 0;
401                 for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
402                         if (ast_dns_srv_get_priority(record) != srv_record_priority[i]) {
403                                 ast_test_status_update(test, "Unexpected priority in returned SRV record\n");
404                                 res = AST_TEST_FAIL;
405                         }
406                         i++;
407                 }
408
409                 record = ast_dns_result_get_records(result);
410                 for (i = 0; i < ARRAY_LEN(records); i++) {
411                         if (ast_dns_srv_get_priority(record) != records[i].priority) {
412                                 continue;
413                         }
414                         if (ast_dns_srv_get_weight(record) != records[i].weight) {
415                                 continue;
416                         }
417                         if (ast_dns_srv_get_port(record) != records[i].port) {
418                                 continue;
419                         }
420                         if (strcmp(ast_dns_srv_get_host(record), records[i].host)) {
421                                 continue;
422                         }
423
424                         srv_record_occurence[i]++;
425                         break;
426                 }
427
428                 ast_dns_result_free(result);
429         }
430
431         if (srv_record_occurence[0] > srv_record_occurence[1]) {
432                 ast_test_status_update(test, "SRV sorting resulted in lesser weight being returned more often for priority 10\n");
433                 res = AST_TEST_FAIL;
434         }
435
436         if (srv_record_occurence[3] > srv_record_occurence[2]) {
437                 ast_test_status_update(test, "SRV sorting resulted in lesser weight being returned more often for priority 5\n");
438                 res = AST_TEST_FAIL;
439         }
440
441 cleanup:
442
443         ast_dns_resolver_unregister(&srv_resolver);
444
445         test_records = NULL;
446         num_test_records = 0;
447         memset(ans_buffer, 0, sizeof(ans_buffer));
448
449         return res;
450 }
451
452 static enum ast_test_result_state invalid_record_test(struct ast_test *test, struct srv_record *records,
453         int num_records)
454 {
455         RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
456         const struct ast_dns_record *record;
457         enum ast_test_result_state res = AST_TEST_PASS;
458
459         test_records = records;
460         num_test_records = num_records;
461         memset(ans_buffer, 0, sizeof(ans_buffer));
462
463         ast_dns_resolver_register(&srv_resolver);
464
465         if (ast_dns_resolve("goose.feathers", T_SRV, C_IN, &result)) {
466                 ast_test_status_update(test, "DNS resolution failed\n");
467                 res = AST_TEST_FAIL;
468                 goto cleanup;
469         }
470
471         if (!result) {
472                 ast_test_status_update(test, "DNS resolution returned no result\n");
473                 res = AST_TEST_FAIL;
474                 goto cleanup;
475         }
476
477         record = ast_dns_result_get_records(result);
478         if (record) {
479                 ast_test_status_update(test, "Unexpected record returned from SRV query\n");
480                 res = AST_TEST_FAIL;
481         }
482
483 cleanup:
484
485         ast_dns_resolver_unregister(&srv_resolver);
486
487         test_records = NULL;
488         num_test_records = 0;
489         memset(ans_buffer, 0, sizeof(ans_buffer));
490
491         return res;
492 }
493
494 AST_TEST_DEFINE(srv_resolve_record_missing_weight_port_host)
495 {
496         struct srv_record records[] = {
497                 { 10, 10, 5060, "tacos.com", 0, 1, 1, 1 },
498         };
499
500         switch (cmd) {
501         case TEST_INIT:
502                 info->name = "srv_resolve_record_missing_weight_port_host";
503                 info->category = "/main/dns/srv/";
504                 info->summary = "Test an SRV lookup which returns a single invalid record";
505                 info->description = "This test defines a single SRV record and performs a\n"
506                         "resolution of the domain to which they belong. The test ensures that the\n"
507                         "record is determined to be corrupt as it contains only a priority";
508                 return AST_TEST_NOT_RUN;
509         case TEST_EXECUTE:
510                 break;
511         }
512
513         return invalid_record_test(test, records, ARRAY_LEN(records));
514 }
515
516 AST_TEST_DEFINE(srv_resolve_record_missing_port_host)
517 {
518         struct srv_record records[] = {
519                 { 10, 10, 5060, "tacos.com", 0, 0, 1, 1 },
520         };
521
522         switch (cmd) {
523         case TEST_INIT:
524                 info->name = "srv_resolve_record_missing_port_host";
525                 info->category = "/main/dns/srv/";
526                 info->summary = "Test an SRV lookup which returns a single invalid record";
527                 info->description = "This test defines a single SRV record and performs a\n"
528                         "resolution of the domain to which they belong. The test ensures that the\n"
529                         "record is determined to be corrupt as it contains only a priority and weight";
530                 return AST_TEST_NOT_RUN;
531         case TEST_EXECUTE:
532                 break;
533         }
534
535         return invalid_record_test(test, records, ARRAY_LEN(records));
536 }
537
538 AST_TEST_DEFINE(srv_resolve_record_missing_host)
539 {
540         struct srv_record records[] = {
541                 { 10, 10, 5060, "tacos.com", 0, 0, 0, 1 },
542         };
543
544         switch (cmd) {
545         case TEST_INIT:
546                 info->name = "srv_resolve_record_missing_host";
547                 info->category = "/main/dns/srv/";
548                 info->summary = "Test an SRV lookup which returns a single invalid record";
549                 info->description = "This test defines a single SRV record and performs a\n"
550                         "resolution of the domain to which they belong. The test ensures that the\n"
551                         "record is determined to be corrupt as it contains only a priority, weight,\n"
552                         "and port";
553                 return AST_TEST_NOT_RUN;
554         case TEST_EXECUTE:
555                 break;
556         }
557
558         return invalid_record_test(test, records, ARRAY_LEN(records));
559 }
560
561 static int unload_module(void)
562 {
563         AST_TEST_UNREGISTER(srv_resolve_single_record);
564         AST_TEST_UNREGISTER(srv_resolve_sort_priority);
565         AST_TEST_UNREGISTER(srv_resolve_same_priority_zero_weight);
566         AST_TEST_UNREGISTER(srv_resolve_same_priority_different_weights);
567         AST_TEST_UNREGISTER(srv_resolve_different_priorities_different_weights);
568         AST_TEST_UNREGISTER(srv_resolve_record_missing_weight_port_host);
569         AST_TEST_UNREGISTER(srv_resolve_record_missing_port_host);
570         AST_TEST_UNREGISTER(srv_resolve_record_missing_host);
571
572         return 0;
573 }
574
575 static int load_module(void)
576 {
577         AST_TEST_REGISTER(srv_resolve_single_record);
578         AST_TEST_REGISTER(srv_resolve_sort_priority);
579         AST_TEST_REGISTER(srv_resolve_same_priority_zero_weight);
580         AST_TEST_REGISTER(srv_resolve_same_priority_different_weights);
581         AST_TEST_REGISTER(srv_resolve_different_priorities_different_weights);
582         AST_TEST_REGISTER(srv_resolve_record_missing_weight_port_host);
583         AST_TEST_REGISTER(srv_resolve_record_missing_port_host);
584         AST_TEST_REGISTER(srv_resolve_record_missing_host);
585
586         return AST_MODULE_LOAD_SUCCESS;
587 }
588
589 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DNS SRV Tests");