Merge "ari: Implement 'debug all' and request/response logging"
[asterisk/asterisk.git] / tests / test_dns_naptr.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
28 #include "asterisk/test.h"
29 #include "asterisk/module.h"
30 #include "asterisk/dns_core.h"
31 #include "asterisk/dns_resolver.h"
32 #include "asterisk/dns_naptr.h"
33 #include "asterisk/dns_test.h"
34
35 struct naptr_record {
36         uint16_t order;
37         uint16_t preference;
38         struct ast_dns_test_string flags;
39         struct ast_dns_test_string services;
40         struct ast_dns_test_string regexp;
41         const char *replacement;
42 };
43
44 /*!
45  * \brief Given a NAPTR record, generate a binary form, as would appear in DNS RDATA
46  *
47  * This is part of a DNS answer, specific to NAPTR. It consists of all parts of
48  * the NAPTR record, encoded as it should be in a DNS record.
49  *
50  * There is no buffer size passed to this function since we provide
51  * the data ourselves and have sized the buffer to be way larger
52  * than necessary for the tests.
53  *
54  * \param string The NAPTR record to encode
55  * \param buf The buffer to write the record into
56  * \return The number of bytes written to the buffer
57  */
58 static int generate_naptr_record(void *dns_record, char *buf)
59 {
60         struct naptr_record *record = dns_record;
61         uint16_t net_order = htons(record->order);
62         uint16_t net_preference = htons(record->preference);
63         char *ptr = buf;
64
65         memcpy(ptr, &net_order, sizeof(net_order));
66         ptr += sizeof(net_order);
67
68         memcpy(ptr, &net_preference, sizeof(net_preference));
69         ptr += sizeof(net_preference);
70
71         ptr += ast_dns_test_write_string(&record->flags, ptr);
72         ptr += ast_dns_test_write_string(&record->services, ptr);
73         ptr += ast_dns_test_write_string(&record->regexp, ptr);
74         ptr += ast_dns_test_write_domain(record->replacement, ptr);
75
76         return ptr - buf;
77 }
78
79 /*!
80  * \brief A pointer to an array of records for a test
81  *
82  * Each test is expected to set this pointer to its local
83  * array of records and then re-set tis pointer to NULL
84  * at the end of the test
85  */
86 static struct naptr_record *test_records;
87 /*!
88  * \brief The number of records in the test_records array.
89  *
90  * Each test must set this to the appropriate value at the
91  * beginning of the test and must set this back to zero at
92  * the end of the test.
93  */
94 static int num_test_records;
95 /*!
96  * \brief A buffer to place raw DNS records into.
97  *
98  * This buffer is way larger than any DNS records we actually
99  * wish to create during any of the tests, but that's fine.
100  */
101 static char ans_buffer[1024];
102
103 /*!
104  * \brief Asynchronous NAPTR resolution thread.
105  *
106  * This builds an appropriate DNS response based on the NAPTR
107  * records for a given test. Once the records have been created,
108  * the records are added to the DNS result
109  */
110 static void *naptr_thread(void *dns_query)
111 {
112         struct ast_dns_query *query = dns_query;
113         int i;
114         int ans_size;
115
116         ans_size = ast_dns_test_generate_result(query, test_records, num_test_records,
117                         sizeof(struct naptr_record), generate_naptr_record, ans_buffer);
118
119         ast_dns_resolver_set_result(query, 0, 0, NOERROR, "goose.feathers", ans_buffer, ans_size);
120
121         for (i = 0; i < num_test_records; ++i) {
122                 char record[128];
123                 int naptr_size;
124
125                 naptr_size = generate_naptr_record(&test_records[i], record);
126                 ast_dns_resolver_add_record(query, T_NAPTR, C_IN, 12345, record, naptr_size);
127         }
128
129         ast_dns_resolver_completed(query);
130
131         ao2_ref(query, -1);
132         return NULL;
133 }
134
135 /*!
136  * \brief Mock NAPTR resolution method.
137  *
138  * This spawns a thread to handle generation of the necessary NAPTR records
139  */
140 static int naptr_resolve(struct ast_dns_query *query)
141 {
142         pthread_t thread;
143
144         return ast_pthread_create_detached(&thread, NULL, naptr_thread, ao2_bump(query));
145 }
146
147 /*!
148  * \brief A STUB
149  */
150 static int naptr_cancel(struct ast_dns_query *query)
151 {
152         return 0;
153 }
154
155 /*!
156  * \brief Mock NAPTR resolver
157  */
158 static struct ast_dns_resolver naptr_resolver = {
159         .name = "naptr_test",
160         .priority = 0,
161         .resolve = naptr_resolve,
162         .cancel = naptr_cancel,
163 };
164
165 AST_TEST_DEFINE(naptr_resolve_nominal)
166 {
167         RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
168         const struct ast_dns_record *record;
169         struct naptr_record records[] = {
170                 /* Incredibly plain record */
171                 { 200, 100, {1, "A"}, {4, "BLAH"}, {0, ""}, "goose.down" },
172                 /* Records with valid but unusual flags */
173                 { 300,   8, {0, ""}, {4, "BLAH"}, {0, ""}, "goose.down" },
174                 { 300,   6, {1, "3"}, {4, "BLAH"}, {0, ""}, "goose.down" },
175                 { 100,   2, {2, "32"}, {4, "BLAH"}, {0, ""}, "goose.down" },
176                 { 400, 100, {3, "A32"}, {4, "BLAH"}, {0, ""}, "goose.down" },
177                 /* Records with valid but unusual services */
178                 { 100, 700, {0, ""}, {0, ""}, {0, ""}, "goose.down" },
179                 { 500, 102, {1, "A"}, {42, "A+B12+C+D+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"}, {0, ""}, "goose.down" },
180                 { 500, 100, {1, "A"}, {14, "A+B12+C+D+EEEE"}, {0, ""}, "goose.down" },
181                 /* Records with valid regexes (regexes are always unusual) */
182                 { 500, 101, {1, "A"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, "" },
183                 { 500,  99, {1, "A"}, {4, "BLAH"}, {15, "0.*0horse.mane0"}, "" },
184                 {  10, 100, {1, "A"}, {4, "BLAH"}, {11, "!.*!\\!\\!\\!!"}, "" },
185                 { 700, 999, {1, "A"}, {4, "BLAH"}, {30, "!(.)(.)(.)(.)!\\1.m.\\2.n\\3.o\\4!"}, "" },
186         };
187
188         int naptr_record_order[] = { 10, 3, 5, 0, 2, 1, 4, 9, 7, 8, 6, 11};
189         enum ast_test_result_state res = AST_TEST_PASS;
190         int i;
191
192         switch (cmd) {
193         case TEST_INIT:
194                 info->name = "naptr_resolve";
195                 info->category = "/main/dns/naptr/";
196                 info->summary = "Test nominal resolution of NAPTR records";
197                 info->description = "This test defines four valid NAPTR records and\n"
198                         "performs a resolution of the domain to which they belong. The test\n"
199                         "ensures that all fields of the NAPTR records are parsed correctly\n"
200                         "and that the records are returned in sorted order";
201                 return AST_TEST_NOT_RUN;
202         case TEST_EXECUTE:
203                 break;
204         }
205
206         test_records = records;
207         num_test_records = ARRAY_LEN(records);
208         memset(ans_buffer, 0, sizeof(ans_buffer));
209
210         ast_dns_resolver_register(&naptr_resolver);
211
212         if (ast_dns_resolve("goose.feathers", T_NAPTR, C_IN, &result)) {
213                 ast_test_status_update(test, "DNS resolution failed\n");
214                 res = AST_TEST_FAIL;
215                 goto cleanup;
216         }
217
218         if (!result) {
219                 ast_test_status_update(test, "DNS resolution returned no result\n");
220                 res = AST_TEST_FAIL;
221                 goto cleanup;
222         }
223
224         i = 0;
225         for (record = ast_dns_result_get_records(result); record; record = ast_dns_record_get_next(record)) {
226                 if (ast_dns_naptr_get_order(record) != records[naptr_record_order[i]].order) {
227                         ast_test_status_update(test, "Expected order %hu, got order %hu from NAPTR record\n",
228                                         records[naptr_record_order[i]].order, ast_dns_naptr_get_order(record));
229                         res = AST_TEST_FAIL;
230                 }
231                 if (ast_dns_naptr_get_preference(record) != records[naptr_record_order[i]].preference) {
232                         ast_test_status_update(test, "Expected preference %hu, got preference %hu from NAPTR record\n",
233                                         records[naptr_record_order[i]].preference, ast_dns_naptr_get_preference(record));
234                         res = AST_TEST_FAIL;
235                 }
236                 if (strcmp(ast_dns_naptr_get_flags(record), records[naptr_record_order[i]].flags.val)) {
237                         ast_test_status_update(test, "Expected flags %s, got flags %s from NAPTR record\n",
238                                         records[naptr_record_order[i]].flags.val, ast_dns_naptr_get_flags(record));
239                         res = AST_TEST_FAIL;
240                 }
241                 if (strcmp(ast_dns_naptr_get_service(record), records[naptr_record_order[i]].services.val)) {
242                         ast_test_status_update(test, "Expected services %s, got services %s from NAPTR record\n",
243                                         records[naptr_record_order[i]].services.val, ast_dns_naptr_get_service(record));
244                         res = AST_TEST_FAIL;
245                 }
246                 if (strcmp(ast_dns_naptr_get_regexp(record), records[naptr_record_order[i]].regexp.val)) {
247                         ast_test_status_update(test, "Expected regexp %s, got regexp %s from NAPTR record\n",
248                                         records[naptr_record_order[i]].regexp.val, ast_dns_naptr_get_regexp(record));
249                         res = AST_TEST_FAIL;
250                 }
251                 if (strcmp(ast_dns_naptr_get_replacement(record), records[naptr_record_order[i]].replacement)) {
252                         ast_test_status_update(test, "Expected replacement %s, got replacement %s from NAPTR record\n",
253                                         records[naptr_record_order[i]].replacement, ast_dns_naptr_get_replacement(record));
254                         res = AST_TEST_FAIL;
255                 }
256                 ++i;
257         }
258
259         if (i != ARRAY_LEN(records)) {
260                 ast_test_status_update(test, "Unexpected number of records returned in NAPTR lookup\n");
261                 res = AST_TEST_FAIL;
262         }
263
264 cleanup:
265
266         ast_dns_resolver_unregister(&naptr_resolver);
267
268         test_records = NULL;
269         num_test_records = 0;
270         memset(ans_buffer, 0, sizeof(ans_buffer));
271
272         return res;
273 }
274
275 static enum ast_test_result_state off_nominal_test(struct ast_test *test, struct naptr_record *records, int num_records)
276 {
277         RAII_VAR(struct ast_dns_result *, result, NULL, ast_dns_result_free);
278         enum ast_test_result_state res = AST_TEST_PASS;
279         const struct ast_dns_record *record;
280
281         test_records = records;
282         num_test_records = num_records;
283         memset(ans_buffer, 0, sizeof(ans_buffer));
284
285         ast_dns_resolver_register(&naptr_resolver);
286
287         if (ast_dns_resolve("goose.feathers", T_NAPTR, C_IN, &result)) {
288                 ast_test_status_update(test, "Failed to perform DNS resolution, despite using valid inputs\n");
289                 res = AST_TEST_FAIL;
290                 goto cleanup;
291         }
292
293         if (!result) {
294                 ast_test_status_update(test, "Synchronous DNS resolution failed to set a result\n");
295                 res = AST_TEST_FAIL;
296                 goto cleanup;
297         }
298
299         record = ast_dns_result_get_records(result);
300         if (record) {
301                 ast_test_status_update(test, "DNS resolution returned records when it was not expected to\n");
302                 res = AST_TEST_FAIL;
303                 goto cleanup;
304         }
305
306 cleanup:
307         ast_dns_resolver_unregister(&naptr_resolver);
308
309         test_records = NULL;
310         num_test_records = 0;
311         memset(ans_buffer, 0, sizeof(ans_buffer));
312
313         return res;
314 }
315
316 AST_TEST_DEFINE(naptr_resolve_off_nominal_length)
317 {
318         struct naptr_record records[] = {
319                 { 100, 100, {255, "A"}, {4, "BLAH"},   {15, "!.*!horse.mane!"}, "" },
320                 { 100, 100, {0, "A"},   {4, "BLAH"},   {15, "!.*!horse.mane!"}, "" },
321                 { 100, 100, {1, "A"},   {255, "BLAH"}, {15, "!.*!horse.mane!"}, "" },
322                 { 100, 100, {1, "A"},   {2, "BLAH"},   {15, "!.*!horse.mane!"}, "" },
323                 { 100, 100, {1, "A"},   {4, "BLAH"},   {255, "!.*!horse.mane!"}, "" },
324                 { 100, 100, {1, "A"},   {4, "BLAH"},   {3, "!.*!horse.mane!"}, "" },
325                 { 100, 100, {255, "A"}, {255, "BLAH"}, {255, "!.*!horse.mane!"}, "" },
326                 { 100, 100, {0, "A"},   {2, "BLAH"},   {3, "!.*!horse.mane!"}, "" },
327         };
328
329         switch (cmd) {
330         case TEST_INIT:
331                 info->name = "naptr_resolve_off_nominal_length";
332                 info->category = "/main/dns/naptr/";
333                 info->summary = "Test resolution of NAPTR records with off-nominal lengths";
334                 info->description = "This test defines a set of records where the strings provided\n"
335                         "within the record are valid, but the lengths of the strings in the record are\n"
336                         "invalid, either too large or too small. The goal of this test is to ensure that\n"
337                         "these invalid lengths result in resolution failures";
338                 return AST_TEST_NOT_RUN;
339         case TEST_EXECUTE:
340                 break;
341         }
342
343         return off_nominal_test(test, records, ARRAY_LEN(records));
344 }
345
346 AST_TEST_DEFINE(naptr_resolve_off_nominal_flags)
347 {
348         struct naptr_record records[] = {
349                 /* Non-alphanumeric flag */
350                 { 100, 100, {1, "!"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
351                 /* Mix of valid and non-alphanumeric */
352                 { 100, 100, {2, "A!"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
353                 { 100, 100, {2, "!A"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
354                 /* Invalid combinations of flags */
355                 { 100, 100, {2, "sa"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
356                 { 100, 100, {2, "su"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
357                 { 100, 100, {2, "sp"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
358                 { 100, 100, {2, "as"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
359                 { 100, 100, {2, "au"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
360                 { 100, 100, {2, "ap"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
361                 { 100, 100, {2, "ua"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
362                 { 100, 100, {2, "us"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
363                 { 100, 100, {2, "up"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
364                 { 100, 100, {2, "pa"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
365                 { 100, 100, {2, "ps"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
366                 { 100, 100, {2, "pu"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, ""},
367         };
368
369         switch (cmd) {
370         case TEST_INIT:
371                 info->name = "naptr_resolve_off_nominal_flags";
372                 info->category = "/main/dns/naptr/";
373                 info->summary = "Ensure that NAPTR records with invalid flags are not presented in results";
374                 info->description = "This test defines a set of records where the flags provided are\n"
375                         "invalid in some way. This may be due to providing non-alphanumeric characters or\n"
376                         "by providing clashing flags. The result should be that none of the defined records\n"
377                         "are returned by the resolver";
378                 return AST_TEST_NOT_RUN;
379         case TEST_EXECUTE:
380                 break;
381         }
382
383         return off_nominal_test(test, records, ARRAY_LEN(records));
384 }
385
386 AST_TEST_DEFINE(naptr_resolve_off_nominal_services)
387 {
388         struct naptr_record records[] = {
389                 { 100, 100, {1, "A"}, {5, "BLAH!"}, {15, "!.*!horse.mane!"}, ""},
390                 { 100, 100, {1, "A"}, {5, "BL!AH"}, {15, "!.*!horse.mane!"}, ""},
391                 { 100, 100, {1, "A"}, {8, "1SIP+D2U"}, {15, "!.*!horse.mane!"}, ""},
392                 { 100, 100, {1, "A"}, {8, "SIP+1D2U"}, {15, "!.*!horse.mane!"}, ""},
393                 { 100, 100, {1, "A"}, {4, "+D2U"}, {15, "!.*!horse.mane!"}, ""},
394                 { 100, 100, {1, "A"}, {4, "SIP+"}, {15, "!.*!horse.mane!"}, ""},
395                 { 100, 100, {1, "A"}, {8, "SIP++D2U"}, {15, "!.*!horse.mane!"}, ""},
396                 { 100, 100, {1, "A"}, {37, "SIPSIPSIPSIPSIPSIPSIPSIPSIPSIPSIP+D2U"}, {15, "!.*!horse.mane!"}, ""},
397                 { 100, 100, {1, "A"}, {37, "SIP+D2UD2UD2UD2UD2UD2UD2UD2UD2UD2UD2U"}, {15, "!.*!horse.mane!"}, ""},
398         };
399
400         switch (cmd) {
401         case TEST_INIT:
402                 info->name = "naptr_resolve_off_nominal_services";
403                 info->category = "/main/dns/naptr/";
404                 info->summary = "Ensure that NAPTR records with invalid services are not presented in results";
405                 info->description = "This test defines a set of records where the services provided are\n"
406                         "invalid in some way. This may be due to providing non-alphanumeric characters, providing\n"
407                         "protocols or resolution services that start with a non-alphabetic character, or\n"
408                         "providing fields that are too long.";
409                 return AST_TEST_NOT_RUN;
410         case TEST_EXECUTE:
411                 break;
412         }
413
414         return off_nominal_test(test, records, ARRAY_LEN(records));
415 }
416
417 AST_TEST_DEFINE(naptr_resolve_off_nominal_regexp)
418 {
419         struct naptr_record records[] = {
420                 /* Invalid delim-char */
421                 { 100, 100, {1, "A"}, {4, "BLAH"}, {15, "1.*1horse.mane1"}, ""},
422                 /* Not enough delim-chars */
423                 { 100, 100, {1, "A"}, {4, "BLAH"}, {14, "!.*!horse.mane"}, ""},
424                 /* Not enough delim-chars, part 2 */
425                 { 100, 100, {1, "A"}, {4, "BLAH"}, {16, "!.*!horse.mane\\!"}, ""},
426                 /* Too many delim-chars */
427                 { 100, 100, {1, "A"}, {4, "BLAH"}, {15, "!.*!horse!mane!"}, ""},
428                 /* Invalid regex flag */
429                 { 100, 100, {1, "A"}, {4, "BLAH"}, {16, "!.*!horse.mane!o"}, ""},
430                 /* Invalid backreference */
431                 { 100, 100, {1, "A"}, {4, "BLAH"}, {13, "!.*!horse.\\0!"}, ""},
432                 /* Invalid regex */
433                 { 100, 100, {1, "A"}, {4, "BLAH"}, {16, "!(.*!horse.mane!"}, ""},
434         };
435
436         switch (cmd) {
437         case TEST_INIT:
438                 info->name = "naptr_resolve_off_nominal_regexp";
439                 info->category = "/main/dns/naptr/";
440                 info->summary = "Ensure that NAPTR records with invalid regexps are not presented in results";
441                 info->description = "This test defines a set of records where the regexps provided are\n"
442                         "invalid in some way. The test ensures that none of the invalid records are returned\n"
443                         "when performing a NAPTR lookup";
444                 return AST_TEST_NOT_RUN;
445         case TEST_EXECUTE:
446                 break;
447         }
448
449         return off_nominal_test(test, records, ARRAY_LEN(records));
450 }
451
452 AST_TEST_DEFINE(naptr_resolve_off_nominal_interactions)
453 {
454         struct naptr_record records[] = {
455                 /* Both regexp and replacement are specified */
456                 { 100, 100, {1, "A"}, {4, "BLAH"}, {15, "!.*!horse.mane!"}, "goose.down"},
457                 /* XXX RFC 2915 says that a service MUST be present if terminal flags are
458                  * specified. However, RFCs 3401-3404 do not specify this behavior, so
459                  * I am not putting in a test for it
460                  */
461         };
462
463         switch (cmd) {
464         case TEST_INIT:
465                 info->name = "naptr_resolve_off_nominal_interactions";
466                 info->category = "/main/dns/naptr/";
467                 info->summary = "Ensure that NAPTR records with invalid interactions are not presented in results";
468                 info->description = "This test defines a set of records where all parts are individually valid,\n"
469                         "but when combined do not make sense and are thus invalid.";
470                 return AST_TEST_NOT_RUN;
471         case TEST_EXECUTE:
472                 break;
473         }
474
475         return off_nominal_test(test, records, ARRAY_LEN(records));
476 }
477
478 static int unload_module(void)
479 {
480         AST_TEST_UNREGISTER(naptr_resolve_nominal);
481         AST_TEST_UNREGISTER(naptr_resolve_off_nominal_length);
482         AST_TEST_UNREGISTER(naptr_resolve_off_nominal_flags);
483         AST_TEST_UNREGISTER(naptr_resolve_off_nominal_services);
484         AST_TEST_UNREGISTER(naptr_resolve_off_nominal_regexp);
485         AST_TEST_UNREGISTER(naptr_resolve_off_nominal_interactions);
486
487         return 0;
488 }
489
490 static int load_module(void)
491 {
492         AST_TEST_REGISTER(naptr_resolve_nominal);
493         AST_TEST_REGISTER(naptr_resolve_off_nominal_length);
494         AST_TEST_REGISTER(naptr_resolve_off_nominal_flags);
495         AST_TEST_REGISTER(naptr_resolve_off_nominal_services);
496         AST_TEST_REGISTER(naptr_resolve_off_nominal_regexp);
497         AST_TEST_REGISTER(naptr_resolve_off_nominal_interactions);
498
499         return AST_MODULE_LOAD_SUCCESS;
500 }
501
502 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DNS API Tests");