blob: a088e5d96a4b238f6c2db05544004d5acb8a417e [file] [log] [blame]
Benny Prijonofa9e5b12006-10-08 12:39:34 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "test.h"
20#include <pjsip.h>
21#include <pjlib.h>
22#include <pjlib-util.h>
23
24/* For logging purpose. */
25#define THIS_FILE "dns_test.c"
26
27struct result
28{
29 pj_status_t status;
30 pjsip_server_addresses servers;
31};
32
33
34static void cb(pj_status_t status,
35 void *token,
36 const struct pjsip_server_addresses *addr)
37{
38 struct result *result = token;
39
40 result->status = status;
41 if (status == PJ_SUCCESS)
42 pj_memcpy(&result->servers, addr, sizeof(*addr));
43}
44
45
46static void add_dns_entries(pj_dns_resolver *resv)
47{
48 /* Inject DNS SRV entry */
49 pj_dns_parsed_packet pkt;
50 pj_dns_parsed_rr ans[4];
51 pj_dns_parsed_rr ar[5];
52 unsigned i;
53
54 /*
55 * This is answer to SRV query to "example.com" domain, and
56 * the answer contains full reference to the A records of
57 * the server. The full DNS records is :
58
59 _sip._udp.example.com 3600 IN SRV 0 0 5060 sip01.example.com.
60 _sip._udp.example.com 3600 IN SRV 0 20 5060 sip02.example.com.
61 _sip._udp.example.com 3600 IN SRV 0 10 5060 sip03.example.com.
62 _sip._udp.example.com 3600 IN SRV 1 0 5060 sip04.example.com.
63
64 sip01.example.com. 3600 IN A 1.1.1.1
65 sip02.example.com. 3600 IN A 2.2.2.2
66 sip03.example.com. 3600 IN A 3.3.3.3
67 sip04.example.com. 3600 IN A 4.4.4.4
68
69 ; Additionally, add A record for "example.com"
70 example.com. 3600 IN A 5.5.5.5
71
72 */
73 pj_bzero(&pkt, sizeof(pkt));
74 pj_bzero(ans, sizeof(ans));
75 pj_bzero(ar, sizeof(ar));
76
77 pkt.hdr.flags = PJ_DNS_SET_QR(1);
78 pkt.hdr.anscount = PJ_ARRAY_SIZE(ans);
79 pkt.hdr.arcount = 0;
80 pkt.ans = ans;
81 pkt.arr = ar;
82
83 ans[0].name = pj_str("_sip._udp.example.com");
84 ans[0].type = PJ_DNS_TYPE_SRV;
85 ans[0].dnsclass = PJ_DNS_CLASS_IN;
86 ans[0].ttl = 3600;
87 ans[0].rdata.srv.prio = 0;
88 ans[0].rdata.srv.weight = 0;
89 ans[0].rdata.srv.port = 5060;
90 ans[0].rdata.srv.target = pj_str("sip01.example.com");
91
92 ans[1].name = pj_str("_sip._udp.example.com");
93 ans[1].type = PJ_DNS_TYPE_SRV;
94 ans[1].dnsclass = PJ_DNS_CLASS_IN;
95 ans[1].ttl = 3600;
96 ans[1].rdata.srv.prio = 0;
97 ans[1].rdata.srv.weight = 20;
98 ans[1].rdata.srv.port = 5060;
99 ans[1].rdata.srv.target = pj_str("sip02.example.com");
100
101 ans[2].name = pj_str("_sip._udp.example.com");
102 ans[2].type = PJ_DNS_TYPE_SRV;
103 ans[2].dnsclass = PJ_DNS_CLASS_IN;
104 ans[2].ttl = 3600;
105 ans[2].rdata.srv.prio = 0;
106 ans[2].rdata.srv.weight = 10;
107 ans[2].rdata.srv.port = 5060;
108 ans[2].rdata.srv.target = pj_str("sip03.example.com");
109
110 ans[3].name = pj_str("_sip._udp.example.com");
111 ans[3].type = PJ_DNS_TYPE_SRV;
112 ans[3].dnsclass = PJ_DNS_CLASS_IN;
113 ans[3].ttl = 3600;
114 ans[3].rdata.srv.prio = 1;
115 ans[3].rdata.srv.weight = 0;
116 ans[3].rdata.srv.port = 5060;
117 ans[3].rdata.srv.target = pj_str("sip04.example.com");
118
119 pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
120
121 ar[0].name = pj_str("sip01.example.com");
122 ar[0].type = PJ_DNS_TYPE_A;
123 ar[0].dnsclass = PJ_DNS_CLASS_IN;
124 ar[0].ttl = 3600;
125 ar[0].rdata.a.ip_addr = pj_str("1.1.1.1");
126
127 ar[1].name = pj_str("sip02.example.com");
128 ar[1].type = PJ_DNS_TYPE_A;
129 ar[1].dnsclass = PJ_DNS_CLASS_IN;
130 ar[1].ttl = 3600;
131 ar[1].rdata.a.ip_addr = pj_str("2.2.2.2");
132
133 ar[2].name = pj_str("sip03.example.com");
134 ar[2].type = PJ_DNS_TYPE_A;
135 ar[2].dnsclass = PJ_DNS_CLASS_IN;
136 ar[2].ttl = 3600;
137 ar[2].rdata.a.ip_addr = pj_str("3.3.3.3");
138
139 ar[3].name = pj_str("sip04.example.com");
140 ar[3].type = PJ_DNS_TYPE_A;
141 ar[3].dnsclass = PJ_DNS_CLASS_IN;
142 ar[3].ttl = 3600;
143 ar[3].rdata.a.ip_addr = pj_str("4.4.4.4");
144
145 ar[4].name = pj_str("example.com");
146 ar[4].type = PJ_DNS_TYPE_A;
147 ar[4].dnsclass = PJ_DNS_CLASS_IN;
148 ar[4].ttl = 3600;
149 ar[4].rdata.a.ip_addr = pj_str("5.5.5.5");
150
151 /*
152 * Create individual A records for all hosts in "example.com" domain.
153 */
154 for (i=0; i<PJ_ARRAY_SIZE(ar); ++i) {
155 pj_bzero(&pkt, sizeof(pkt));
156 pkt.hdr.anscount = 1;
157 pkt.hdr.flags = PJ_DNS_SET_QR(1);
158 pkt.ans = &ar[i];
159
160 pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
161 }
162
163 /*
164 * Simulate DNS error response by creating these answers.
165 * Sample of invalid SRV records: _sip._udp.sip01.example.com.
166 */
167 for (i=0; i<PJ_ARRAY_SIZE(ans); ++i) {
168 pj_dns_parsed_query q;
169 char buf[128];
170 char *services[] = { "_sip._udp.", "_sip._tcp.", "_sips._tcp."};
171 unsigned j;
172
173 for (j=0; j<PJ_ARRAY_SIZE(services); ++j) {
174 q.dnsclass = PJ_DNS_CLASS_IN;
175 q.type = PJ_DNS_TYPE_SRV;
176
177 q.name.ptr = buf;
178 pj_bzero(buf, sizeof(buf));
179 pj_strcpy2(&q.name, services[j]);
180 pj_strcat(&q.name, &ans[i].rdata.srv.target);
181
182 pj_bzero(&pkt, sizeof(pkt));
183 pkt.hdr.qdcount = 1;
184 pkt.hdr.flags = PJ_DNS_SET_QR(1) |
185 PJ_DNS_SET_RCODE(PJ_DNS_RCODE_NXDOMAIN);
186 pkt.q = &q;
187
188 pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
189 }
190 }
191
192
193 /*
194 * ANOTHER DOMAIN.
195 *
196 * This time we let SRV and A get answered in different DNS
197 * query.
198 */
199
200 /* The "domain.com" DNS records (note the different the port):
201
202 _sip._tcp.domain.com 3600 IN SRV 1 0 50060 sip06.domain.com.
203 _sip._tcp.domain.com 3600 IN SRV 2 0 50060 sip07.domain.com.
204
205 sip06.domain.com. 3600 IN A 6.6.6.6
206 sip07.domain.com. 3600 IN A 7.7.7.7
207 */
208
209 pj_bzero(&pkt, sizeof(pkt));
210 pj_bzero(&ans, sizeof(ans));
211 pkt.hdr.flags = PJ_DNS_SET_QR(1);
212 pkt.hdr.anscount = 2;
213 pkt.ans = ans;
214
215 /* Add the SRV records, with reverse priority (to test that sorting
216 * works.
217 */
218 ans[0].name = pj_str("_sip._tcp.domain.com");
219 ans[0].type = PJ_DNS_TYPE_SRV;
220 ans[0].dnsclass = PJ_DNS_CLASS_IN;
221 ans[0].ttl = 3600;
222 ans[0].rdata.srv.prio = 2;
223 ans[0].rdata.srv.weight = 0;
224 ans[0].rdata.srv.port = 50060;
225 ans[0].rdata.srv.target = pj_str("SIP07.DOMAIN.COM");
226
227 ans[1].name = pj_str("_sip._tcp.domain.com");
228 ans[1].type = PJ_DNS_TYPE_SRV;
229 ans[1].dnsclass = PJ_DNS_CLASS_IN;
230 ans[1].ttl = 3600;
231 ans[1].rdata.srv.prio = 1;
232 ans[1].rdata.srv.weight = 0;
233 ans[1].rdata.srv.port = 50060;
234 ans[1].rdata.srv.target = pj_str("SIP06.DOMAIN.COM");
235
236 pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
237
238 /* From herein there is only one answer */
239 pkt.hdr.anscount = 1;
240
241 /* Add a single SRV for UDP */
242 ans[0].name = pj_str("_sip._udp.domain.com");
243 ans[0].type = PJ_DNS_TYPE_SRV;
244 ans[0].dnsclass = PJ_DNS_CLASS_IN;
245 ans[0].ttl = 3600;
246 ans[0].rdata.srv.prio = 0;
247 ans[0].rdata.srv.weight = 0;
248 ans[0].rdata.srv.port = 50060;
249 ans[0].rdata.srv.target = pj_str("SIP06.DOMAIN.COM");
250
251 pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
252
253
254 /* Add the A record for sip06.domain.com */
255 ans[0].name = pj_str("sip06.domain.com");
256 ans[0].type = PJ_DNS_TYPE_A;
257 ans[0].dnsclass = PJ_DNS_CLASS_IN;
258 ans[0].ttl = 3600;
259 ans[0].rdata.a.ip_addr = pj_str("6.6.6.6");
260
261 pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
262
263 /* Add the A record for sip07.domain.com */
264 ans[0].name = pj_str("sip07.domain.com");
265 ans[0].type = PJ_DNS_TYPE_A;
266 ans[0].dnsclass = PJ_DNS_CLASS_IN;
267 ans[0].ttl = 3600;
268 ans[0].rdata.a.ip_addr = pj_str("7.7.7.7");
269
270 pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
271}
272
273
274/*
275 * Perform server resolution where the results are expected to
276 * come in strict order.
277 */
278static int test_resolve(const char *title,
279 pj_pool_t *pool,
280 pjsip_transport_type_e type,
281 char *name,
282 int port,
283 pjsip_server_addresses *ref)
284{
285 pjsip_host_info dest;
286 struct result result;
287
288 PJ_LOG(3,(THIS_FILE, " test_resolve(): %s", title));
289
290 dest.type = type;
291 dest.flag = pjsip_transport_get_flag_from_type(type);
292 dest.addr.host = pj_str(name);
293 dest.addr.port = port;
294
295 result.status = 0x12345678;
296
297 pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb);
298
299 while (result.status == 0x12345678) {
300 int i = 0;
301 pj_time_val timeout = { 1, 0 };
302 pjsip_endpt_handle_events(endpt, &timeout);
303 if (i == 1)
304 pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE);
305 }
306
307 if (result.status != PJ_SUCCESS) {
308 app_perror(" pjsip_endpt_resolve() error", result.status);
309 return result.status;
310 }
311
312 if (ref) {
313 unsigned i;
314
315 if (ref->count != result.servers.count) {
316 PJ_LOG(3,(THIS_FILE, " test_resolve() error 10: result count mismatch"));
317 return 10;
318 }
319
320 for (i=0; i<ref->count; ++i) {
321 pj_sockaddr_in *ra = (pj_sockaddr_in *)&ref->entry[i].addr;
322 pj_sockaddr_in *rb = (pj_sockaddr_in *)&result.servers.entry[i].addr;
323
324 if (ra->sin_addr.s_addr != rb->sin_addr.s_addr) {
325 PJ_LOG(3,(THIS_FILE, " test_resolve() error 20: IP address mismatch"));
326 return 20;
327 }
328 if (ra->sin_port != rb->sin_port) {
329 PJ_LOG(3,(THIS_FILE, " test_resolve() error 30: port mismatch"));
330 return 30;
331 }
332 if (ref->entry[i].addr_len != result.servers.entry[i].addr_len) {
333 PJ_LOG(3,(THIS_FILE, " test_resolve() error 40: addr_len mismatch"));
334 return 40;
335 }
336 if (ref->entry[i].type != result.servers.entry[i].type) {
337 PJ_LOG(3,(THIS_FILE, " test_resolve() error 50: transport type mismatch"));
338 return 50;
339 }
340 }
341 }
342
343 return PJ_SUCCESS;
344}
345
346/*
347 * Perform round-robin/load balance test.
348 */
349static int round_robin_test(pj_pool_t *pool)
350{
351 enum { COUNT = 400, PCT_ALLOWANCE = 5 };
352 unsigned i;
353 struct server_hit
354 {
355 char *ip_addr;
356 unsigned percent;
357 unsigned hits;
358 } server_hit[] =
359 {
360 { "1.1.1.1", 3, 0 },
361 { "2.2.2.2", 65, 0 },
362 { "3.3.3.3", 32, 0 },
363 { "4.4.4.4", 0, 0 }
364 };
365
366 PJ_LOG(3,(THIS_FILE, " Performing round-robin/load-balance test.."));
367
368 /* Do multiple resolve request to "example.com".
369 * The resolver should select the server based on the weight proportion
370 * the the servers in the SRV entry.
371 */
372 for (i=0; i<COUNT; ++i) {
373 pjsip_host_info dest;
374 struct result result;
375 unsigned j;
376
377 dest.type = PJSIP_TRANSPORT_UDP;
378 dest.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);
379 dest.addr.host = pj_str("example.com");
380 dest.addr.port = 0;
381
382 result.status = 0x12345678;
383
384 pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb);
385
386 while (result.status == 0x12345678) {
387 int i = 0;
388 pj_time_val timeout = { 1, 0 };
389 pjsip_endpt_handle_events(endpt, &timeout);
390 if (i == 1)
391 pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE);
392 }
393
394 /* Find which server was "hit" */
395 for (j=0; j<PJ_ARRAY_SIZE(server_hit); ++j) {
396 pj_str_t tmp;
397 pj_in_addr a1;
398 pj_sockaddr_in *a2;
399
400 tmp = pj_str(server_hit[j].ip_addr);
401 a1 = pj_inet_addr(&tmp);
402 a2 = (pj_sockaddr_in*) &result.servers.entry[0].addr;
403
404 if (a1.s_addr == a2->sin_addr.s_addr) {
405 server_hit[j].hits++;
406 break;
407 }
408 }
409
410 if (j == PJ_ARRAY_SIZE(server_hit)) {
411 PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 10: returned address mismatch"));
412 return 10;
413 }
414 }
415
416 /* Print the actual hit rate */
417 for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) {
418 PJ_LOG(3,(THIS_FILE, " ..Server %s: weight=%d%%, hit %d%% times",
419 server_hit[i].ip_addr, server_hit[i].percent,
420 (server_hit[i].hits * 100) / COUNT));
421 }
422
423 /* Compare the actual hit with the weight proportion */
424 for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) {
425 int actual_pct = (server_hit[i].hits * 100) / COUNT;
426
427 if (actual_pct + PCT_ALLOWANCE < (int)server_hit[i].percent ||
428 actual_pct - PCT_ALLOWANCE > (int)server_hit[i].percent)
429 {
430 PJ_LOG(1,(THIS_FILE,
431 "..round_robin_test() error 20: "
432 "hit rate difference for server %s (%d%%) is more than "
433 "tolerable allowance (%d%%)",
434 server_hit[i].ip_addr,
435 actual_pct - server_hit[i].percent,
436 PCT_ALLOWANCE));
437 return 20;
438 }
439 }
440
441 PJ_LOG(3,(THIS_FILE,
442 " Load balance test success, hit-rate is "
443 "within %d%% allowance", PCT_ALLOWANCE));
444 return PJ_SUCCESS;
445}
446
447
448#define C(expr) status = expr; \
449 if (status != PJ_SUCCESS) app_perror(THIS_FILE, "Error", status);
450
451static void add_ref(pjsip_server_addresses *r,
452 pjsip_transport_type_e type,
453 char *addr,
454 int port)
455{
456 pj_sockaddr_in *a;
457 pj_str_t tmp;
458
459 r->entry[r->count].type = type;
460 r->entry[r->count].priority = 0;
461 r->entry[r->count].weight = 0;
462 r->entry[r->count].addr_len = sizeof(pj_sockaddr_in);
463
464 a = (pj_sockaddr_in *)&r->entry[r->count].addr;
465 a->sin_family = PJ_AF_INET;
466 tmp = pj_str(addr);
467 a->sin_addr = pj_inet_addr(&tmp);
468 a->sin_port = pj_htons((pj_uint16_t)port);
469
470 r->count++;
471}
472
473static void create_ref(pjsip_server_addresses *r,
474 pjsip_transport_type_e type,
475 char *addr,
476 int port)
477{
478 r->count = 0;
479 add_ref(r, type, addr, port);
480}
481
482
483/*
484 * Main test entry.
485 */
486int resolve_test(void)
487{
488 pj_pool_t *pool;
489 pj_dns_resolver *resv;
490 pj_str_t nameserver;
491 pj_uint16_t port = 5353;
492 pj_status_t status;
493
494 pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
495
496 status = pjsip_endpt_create_resolver(endpt, &resv);
497
498 nameserver = pj_str("192.168.0.106");
499 pj_dns_resolver_set_ns(resv, 1, &nameserver, &port);
500 pjsip_endpt_set_resolver(endpt, resv);
501
502 add_dns_entries(resv);
503
504 /* These all should be resolved as IP addresses (DNS A query) */
505 {
506 pjsip_server_addresses ref;
507 create_ref(&ref, PJSIP_TRANSPORT_UDP, "1.1.1.1", 5060);
508 status = test_resolve("IP address without transport and port", pool, PJSIP_TRANSPORT_UNSPECIFIED, "1.1.1.1", 0, &ref);
509 if (status != PJ_SUCCESS)
510 return -100;
511 }
512 {
513 pjsip_server_addresses ref;
514 create_ref(&ref, PJSIP_TRANSPORT_UDP, "1.1.1.1", 5060);
515 status = test_resolve("IP address with explicit port", pool, PJSIP_TRANSPORT_UNSPECIFIED, "1.1.1.1", 5060, &ref);
516 if (status != PJ_SUCCESS)
517 return -110;
518 }
519 {
520 pjsip_server_addresses ref;
521 create_ref(&ref, PJSIP_TRANSPORT_TCP, "1.1.1.1", 5060);
522 status = test_resolve("IP address without port (TCP)", pool, PJSIP_TRANSPORT_TCP,"1.1.1.1", 0, &ref);
523 if (status != PJ_SUCCESS)
524 return -120;
525 }
526 {
527 pjsip_server_addresses ref;
528 create_ref(&ref, PJSIP_TRANSPORT_TLS, "1.1.1.1", 5061);
529 status = test_resolve("IP address without port (TLS)", pool, PJSIP_TRANSPORT_TLS, "1.1.1.1", 0, &ref);
530 if (status != PJ_SUCCESS)
531 return -130;
532 }
533
534 /* This should be resolved as DNS A record (because port is present) */
535 {
536 pjsip_server_addresses ref;
537 create_ref(&ref, PJSIP_TRANSPORT_UDP, "5.5.5.5", 5060);
538 status = test_resolve("domain name with port should resolve to A record", pool, PJSIP_TRANSPORT_UNSPECIFIED, "example.com", 5060, &ref);
539 if (status != PJ_SUCCESS)
540 return -140;
541 }
542
543 /* This will fail to be resolved as SRV, resolver should fallback to
544 * resolving to A record.
545 */
546 {
547 pjsip_server_addresses ref;
548 create_ref(&ref, PJSIP_TRANSPORT_UDP, "2.2.2.2", 5060);
549 status = test_resolve("failure with SRV fallback to A record", pool, PJSIP_TRANSPORT_UNSPECIFIED, "sip02.example.com", 0, &ref);
550 if (status != PJ_SUCCESS)
551 return -150;
552 }
553
554 /* Same as above, but explicitly for TLS. */
555 {
556 pjsip_server_addresses ref;
557 create_ref(&ref, PJSIP_TRANSPORT_TLS, "2.2.2.2", 5061);
558 status = test_resolve("failure with SRV fallback to A record (for TLS)", pool, PJSIP_TRANSPORT_TLS, "sip02.example.com", 0, &ref);
559 if (status != PJ_SUCCESS)
560 return -150;
561 }
562
563 /* Standard DNS SRV followed by A recolution */
564 {
565 pjsip_server_addresses ref;
566 create_ref(&ref, PJSIP_TRANSPORT_UDP, "6.6.6.6", 50060);
567 status = test_resolve("standard SRV resolution", pool, PJSIP_TRANSPORT_UNSPECIFIED, "domain.com", 0, &ref);
568 if (status != PJ_SUCCESS)
569 return -155;
570 }
571
572 /* Standard DNS SRV followed by A recolution (explicit transport) */
573 {
574 pjsip_server_addresses ref;
575 create_ref(&ref, PJSIP_TRANSPORT_TCP, "6.6.6.6", 50060);
576 add_ref(&ref, PJSIP_TRANSPORT_TCP, "7.7.7.7", 50060);
577 status = test_resolve("standard SRV resolution with explicit transport (TCP)", pool, PJSIP_TRANSPORT_TCP, "domain.com", 0, &ref);
578 if (status != PJ_SUCCESS)
579 return -160;
580 }
581
582
583 /* Round robin/load balance test */
584 if (round_robin_test(pool) != 0)
585 return -170;
586
587 /* Timeout test */
588 {
589 status = test_resolve("timeout test", pool, PJSIP_TRANSPORT_UNSPECIFIED, "an.invalid.address", 0, NULL);
590 if (status == PJ_SUCCESS)
591 return -150;
592 }
593
594 return 0;
595}
596