Asterisk - The Open Source Telephony Project GIT-master-2de1a68
test_dns_srv.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2015, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@digium.com>
7 * 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"
33#include "asterisk/dns_srv.h"
34#include "asterisk/dns_test.h"
35
36struct 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
47static 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
77static struct srv_record *test_records;
79static char ans_buffer[1024];
80
81static void *srv_thread(void *dns_query)
82{
83 struct ast_dns_query *query = dns_query;
84 int i;
85 int ans_size;
86
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
101
102 ao2_ref(query, -1);
103 return NULL;
104}
105
106static int srv_resolve(struct ast_dns_query *query)
107{
108 pthread_t thread;
109
111}
112
113static int srv_cancel(struct ast_dns_query *query)
114{
115 return -1;
116}
117
119 .name = "srv_test",
120 .priority = 0,
121 .resolve = srv_resolve,
122 .cancel = srv_cancel,
123};
124
125static enum ast_test_result_state nominal_test(struct ast_test *test, struct srv_record *records,
126 int *srv_record_order, int num_records)
127{
129 const struct ast_dns_record *record;
131 int i;
132
134 num_test_records = num_records;
135 memset(ans_buffer, 0, sizeof(ans_buffer));
136
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
177cleanup:
178
180
183 memset(ans_buffer, 0, sizeof(ans_buffer));
184
185 return res;
186}
187
188AST_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
212AST_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
237AST_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
262AST_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, };
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
288
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
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
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
337cleanup:
338
340
343 memset(ans_buffer, 0, sizeof(ans_buffer));
344
345 return res;
346}
347
348AST_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, };
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
378
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
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
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
441cleanup:
442
444
447 memset(ans_buffer, 0, sizeof(ans_buffer));
448
449 return res;
450}
451
453 int num_records)
454{
456 const struct ast_dns_record *record;
458
460 num_test_records = num_records;
461 memset(ans_buffer, 0, sizeof(ans_buffer));
462
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
478 if (record) {
479 ast_test_status_update(test, "Unexpected record returned from SRV query\n");
480 res = AST_TEST_FAIL;
481 }
482
483cleanup:
484
486
489 memset(ans_buffer, 0, sizeof(ans_buffer));
490
491 return res;
492}
493
494AST_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
514}
515
516AST_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
536}
537
538AST_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
559}
560
561static 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
575static 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
587}
588
char weight
pthread_t thread
Definition: app_sla.c:329
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
static int priority
static int records
Definition: cdr_pgsql.c:78
static PGresult * result
Definition: cel_pgsql.c:84
Core DNS API.
const struct ast_dns_record * ast_dns_record_get_next(const struct ast_dns_record *record)
Get the next DNS record.
Definition: dns_core.c:170
const struct ast_dns_record * ast_dns_result_get_records(const struct ast_dns_result *result)
Get the first record of a DNS Result.
Definition: dns_core.c:102
void ast_dns_result_free(struct ast_dns_result *result)
Free the DNS result information.
Definition: dns_core.c:130
int ast_dns_resolve(const char *name, int rr_type, int rr_class, struct ast_dns_result **result)
Synchronously resolve a DNS query.
Definition: dns_core.c:314
DNS Resolver API.
int ast_dns_resolver_set_result(struct ast_dns_query *query, unsigned int secure, unsigned int bogus, unsigned int rcode, const char *canonical, const char *answer, size_t answer_size)
Set result information for a DNS query.
Definition: dns_core.c:456
void ast_dns_resolver_completed(struct ast_dns_query *query)
Mark a DNS query as having been completed.
Definition: dns_core.c:599
int ast_dns_resolver_register(struct ast_dns_resolver *resolver)
Register a DNS resolver.
Definition: dns_core.c:630
int ast_dns_resolver_add_record(struct ast_dns_query *query, int rr_type, int rr_class, int ttl, const char *data, const size_t size)
Add a DNS record to the result of a DNS query.
Definition: dns_core.c:535
void ast_dns_resolver_unregister(struct ast_dns_resolver *resolver)
Unregister a DNS resolver.
Definition: dns_core.c:680
DNS SRV Record Parsing API.
const char * ast_dns_srv_get_host(const struct ast_dns_record *record)
Get the hostname from an SRV record.
Definition: dns_srv.c:188
unsigned short ast_dns_srv_get_priority(const struct ast_dns_record *record)
Get the priority from an SRV record.
Definition: dns_srv.c:196
unsigned short ast_dns_srv_get_weight(const struct ast_dns_record *record)
Get the weight from an SRV record.
Definition: dns_srv.c:204
unsigned short ast_dns_srv_get_port(const struct ast_dns_record *record)
Get the port from an SRV record.
Definition: dns_srv.c:212
int ast_dns_test_write_domain(const char *string, char *buf)
Write a DNS domain to a buffer.
Definition: dns_test.c:224
int ast_dns_test_generate_result(struct ast_dns_query *query, void *records, size_t num_records, size_t record_size, record_fn generate, char *buffer)
Generate a full DNS response for the given DNS records.
Definition: dns_test.c:229
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
def info(msg)
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
#define NULL
Definition: resample.c:96
A DNS query.
Definition: dns_internal.h:137
For AST_LIST.
Definition: dns_internal.h:39
DNS resolver implementation.
Definition: dns_resolver.h:32
const char * name
The name of the resolver implementation.
Definition: dns_resolver.h:34
The result of a DNS query.
Definition: dns_internal.h:117
uint16_t priority
Definition: test_dns_srv.c:37
unsigned int ignore_weight
Definition: test_dns_srv.c:42
uint16_t weight
Definition: test_dns_srv.c:38
uint16_t port
Definition: test_dns_srv.c:39
unsigned int ignore_port
Definition: test_dns_srv.c:43
unsigned int ignore_priority
Definition: test_dns_srv.c:41
const char * host
Definition: test_dns_srv.c:40
unsigned int ignore_host
Definition: test_dns_srv.c:44
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
static enum ast_test_result_state nominal_test(struct ast_test *test, struct srv_record *records, int *srv_record_order, int num_records)
Definition: test_dns_srv.c:125
static struct srv_record * test_records
Definition: test_dns_srv.c:77
static enum ast_test_result_state invalid_record_test(struct ast_test *test, struct srv_record *records, int num_records)
Definition: test_dns_srv.c:452
static int srv_resolve(struct ast_dns_query *query)
Definition: test_dns_srv.c:106
static void * srv_thread(void *dns_query)
Definition: test_dns_srv.c:81
static int num_test_records
Definition: test_dns_srv.c:78
AST_TEST_DEFINE(srv_resolve_single_record)
Definition: test_dns_srv.c:188
static struct ast_dns_resolver srv_resolver
Definition: test_dns_srv.c:118
static char ans_buffer[1024]
Definition: test_dns_srv.c:79
static int generate_srv_record(void *dns_record, char *buf)
Definition: test_dns_srv.c:47
static int load_module(void)
Definition: test_dns_srv.c:575
static int srv_cancel(struct ast_dns_query *query)
Definition: test_dns_srv.c:113
static int unload_module(void)
Definition: test_dns_srv.c:561
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:588
#define ARRAY_LEN(a)
Definition: utils.h:666