1
/* $Id: dns_test.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
#include <pjlib-util.h>
25
/* For logging purpose. */
26
#define THIS_FILE "dns_test.c"
31
pjsip_server_addresses servers;
35
static void cb(pj_status_t status,
37
const struct pjsip_server_addresses *addr)
39
struct result *result = (struct result*) token;
41
result->status = status;
42
if (status == PJ_SUCCESS)
43
pj_memcpy(&result->servers, addr, sizeof(*addr));
47
static void add_dns_entries(pj_dns_resolver *resv)
49
/* Inject DNS SRV entry */
50
pj_dns_parsed_packet pkt;
51
pj_dns_parsed_query q;
52
pj_dns_parsed_rr ans[4];
53
pj_dns_parsed_rr ar[5];
58
* This is answer to SRV query to "example.com" domain, and
59
* the answer contains full reference to the A records of
60
* the server. The full DNS records is :
62
_sip._udp.example.com 3600 IN SRV 0 0 5060 sip01.example.com.
63
_sip._udp.example.com 3600 IN SRV 0 20 5060 sip02.example.com.
64
_sip._udp.example.com 3600 IN SRV 0 10 5060 sip03.example.com.
65
_sip._udp.example.com 3600 IN SRV 1 0 5060 sip04.example.com.
67
sip01.example.com. 3600 IN A 1.1.1.1
68
sip02.example.com. 3600 IN A 2.2.2.2
69
sip03.example.com. 3600 IN A 3.3.3.3
70
sip04.example.com. 3600 IN A 4.4.4.4
72
; Additionally, add A record for "example.com"
73
example.com. 3600 IN A 5.5.5.5
76
pj_bzero(&pkt, sizeof(pkt));
77
pj_bzero(ans, sizeof(ans));
78
pj_bzero(ar, sizeof(ar));
80
pkt.hdr.flags = PJ_DNS_SET_QR(1);
81
pkt.hdr.anscount = PJ_ARRAY_SIZE(ans);
86
ans[0].name = pj_str("_sip._udp.example.com");
87
ans[0].type = PJ_DNS_TYPE_SRV;
88
ans[0].dnsclass = PJ_DNS_CLASS_IN;
90
ans[0].rdata.srv.prio = 0;
91
ans[0].rdata.srv.weight = 0;
92
ans[0].rdata.srv.port = 5060;
93
ans[0].rdata.srv.target = pj_str("sip01.example.com");
95
ans[1].name = pj_str("_sip._udp.example.com");
96
ans[1].type = PJ_DNS_TYPE_SRV;
97
ans[1].dnsclass = PJ_DNS_CLASS_IN;
99
ans[1].rdata.srv.prio = 0;
100
ans[1].rdata.srv.weight = 20;
101
ans[1].rdata.srv.port = 5060;
102
ans[1].rdata.srv.target = pj_str("sip02.example.com");
104
ans[2].name = pj_str("_sip._udp.example.com");
105
ans[2].type = PJ_DNS_TYPE_SRV;
106
ans[2].dnsclass = PJ_DNS_CLASS_IN;
108
ans[2].rdata.srv.prio = 0;
109
ans[2].rdata.srv.weight = 10;
110
ans[2].rdata.srv.port = 5060;
111
ans[2].rdata.srv.target = pj_str("sip03.example.com");
113
ans[3].name = pj_str("_sip._udp.example.com");
114
ans[3].type = PJ_DNS_TYPE_SRV;
115
ans[3].dnsclass = PJ_DNS_CLASS_IN;
117
ans[3].rdata.srv.prio = 1;
118
ans[3].rdata.srv.weight = 0;
119
ans[3].rdata.srv.port = 5060;
120
ans[3].rdata.srv.target = pj_str("sip04.example.com");
122
pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
124
ar[0].name = pj_str("sip01.example.com");
125
ar[0].type = PJ_DNS_TYPE_A;
126
ar[0].dnsclass = PJ_DNS_CLASS_IN;
128
ar[0].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "1.1.1.1"));
130
ar[1].name = pj_str("sip02.example.com");
131
ar[1].type = PJ_DNS_TYPE_A;
132
ar[1].dnsclass = PJ_DNS_CLASS_IN;
134
ar[1].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "2.2.2.2"));
136
ar[2].name = pj_str("sip03.example.com");
137
ar[2].type = PJ_DNS_TYPE_A;
138
ar[2].dnsclass = PJ_DNS_CLASS_IN;
140
ar[2].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "3.3.3.3"));
142
ar[3].name = pj_str("sip04.example.com");
143
ar[3].type = PJ_DNS_TYPE_A;
144
ar[3].dnsclass = PJ_DNS_CLASS_IN;
146
ar[3].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "4.4.4.4"));
148
ar[4].name = pj_str("example.com");
149
ar[4].type = PJ_DNS_TYPE_A;
150
ar[4].dnsclass = PJ_DNS_CLASS_IN;
152
ar[4].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "5.5.5.5"));
155
* Create individual A records for all hosts in "example.com" domain.
157
for (i=0; i<PJ_ARRAY_SIZE(ar); ++i) {
158
pj_bzero(&pkt, sizeof(pkt));
159
pkt.hdr.flags = PJ_DNS_SET_QR(1);
164
q.dnsclass = PJ_DNS_CLASS_IN;
165
pkt.hdr.anscount = 1;
168
pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
172
* Simulate DNS error response by creating these answers.
173
* Sample of invalid SRV records: _sip._udp.sip01.example.com.
175
for (i=0; i<PJ_ARRAY_SIZE(ans); ++i) {
176
pj_dns_parsed_query q;
178
char *services[] = { "_sip._udp.", "_sip._tcp.", "_sips._tcp."};
181
for (j=0; j<PJ_ARRAY_SIZE(services); ++j) {
182
q.dnsclass = PJ_DNS_CLASS_IN;
183
q.type = PJ_DNS_TYPE_SRV;
186
pj_bzero(buf, sizeof(buf));
187
pj_strcpy2(&q.name, services[j]);
188
pj_strcat(&q.name, &ans[i].rdata.srv.target);
190
pj_bzero(&pkt, sizeof(pkt));
192
pkt.hdr.flags = PJ_DNS_SET_QR(1) |
193
PJ_DNS_SET_RCODE(PJ_DNS_RCODE_NXDOMAIN);
196
pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
204
* This time we let SRV and A get answered in different DNS
208
/* The "domain.com" DNS records (note the different the port):
210
_sip._tcp.domain.com 3600 IN SRV 1 0 50060 sip06.domain.com.
211
_sip._tcp.domain.com 3600 IN SRV 2 0 50060 sip07.domain.com.
213
sip06.domain.com. 3600 IN A 6.6.6.6
214
sip07.domain.com. 3600 IN A 7.7.7.7
217
pj_bzero(&pkt, sizeof(pkt));
218
pj_bzero(&ans, sizeof(ans));
219
pkt.hdr.flags = PJ_DNS_SET_QR(1);
220
pkt.hdr.anscount = 2;
223
/* Add the SRV records, with reverse priority (to test that sorting
226
ans[0].name = pj_str("_sip._tcp.domain.com");
227
ans[0].type = PJ_DNS_TYPE_SRV;
228
ans[0].dnsclass = PJ_DNS_CLASS_IN;
230
ans[0].rdata.srv.prio = 2;
231
ans[0].rdata.srv.weight = 0;
232
ans[0].rdata.srv.port = 50060;
233
ans[0].rdata.srv.target = pj_str("SIP07.DOMAIN.COM");
235
ans[1].name = pj_str("_sip._tcp.domain.com");
236
ans[1].type = PJ_DNS_TYPE_SRV;
237
ans[1].dnsclass = PJ_DNS_CLASS_IN;
239
ans[1].rdata.srv.prio = 1;
240
ans[1].rdata.srv.weight = 0;
241
ans[1].rdata.srv.port = 50060;
242
ans[1].rdata.srv.target = pj_str("SIP06.DOMAIN.COM");
244
pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
246
/* From herein there is only one answer */
247
pkt.hdr.anscount = 1;
249
/* Add a single SRV for UDP */
250
ans[0].name = pj_str("_sip._udp.domain.com");
251
ans[0].type = PJ_DNS_TYPE_SRV;
252
ans[0].dnsclass = PJ_DNS_CLASS_IN;
254
ans[0].rdata.srv.prio = 0;
255
ans[0].rdata.srv.weight = 0;
256
ans[0].rdata.srv.port = 50060;
257
ans[0].rdata.srv.target = pj_str("SIP06.DOMAIN.COM");
259
pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
262
/* Add the A record for sip06.domain.com */
263
ans[0].name = pj_str("sip06.domain.com");
264
ans[0].type = PJ_DNS_TYPE_A;
265
ans[0].dnsclass = PJ_DNS_CLASS_IN;
267
ans[0].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "6.6.6.6"));
271
q.name = ans[0].name;
272
q.type = ans[0].type;
273
q.dnsclass = ans[0].dnsclass;
275
pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
277
/* Add the A record for sip07.domain.com */
278
ans[0].name = pj_str("sip07.domain.com");
279
ans[0].type = PJ_DNS_TYPE_A;
280
ans[0].dnsclass = PJ_DNS_CLASS_IN;
282
ans[0].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "7.7.7.7"));
286
q.name = ans[0].name;
287
q.type = ans[0].type;
288
q.dnsclass = ans[0].dnsclass;
290
pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE);
297
* Perform server resolution where the results are expected to
298
* come in strict order.
300
static int test_resolve(const char *title,
302
pjsip_transport_type_e type,
305
pjsip_server_addresses *ref)
307
pjsip_host_info dest;
308
struct result result;
310
PJ_LOG(3,(THIS_FILE, " test_resolve(): %s", title));
313
dest.flag = pjsip_transport_get_flag_from_type(type);
314
dest.addr.host = pj_str(name);
315
dest.addr.port = port;
317
result.status = 0x12345678;
319
pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb);
321
while (result.status == 0x12345678) {
323
pj_time_val timeout = { 1, 0 };
324
pjsip_endpt_handle_events(endpt, &timeout);
326
pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE);
329
if (result.status != PJ_SUCCESS) {
330
app_perror(" pjsip_endpt_resolve() error", result.status);
331
return result.status;
337
if (ref->count != result.servers.count) {
338
PJ_LOG(3,(THIS_FILE, " test_resolve() error 10: result count mismatch"));
342
for (i=0; i<ref->count; ++i) {
343
pj_sockaddr_in *ra = (pj_sockaddr_in *)&ref->entry[i].addr;
344
pj_sockaddr_in *rb = (pj_sockaddr_in *)&result.servers.entry[i].addr;
346
if (ra->sin_addr.s_addr != rb->sin_addr.s_addr) {
347
PJ_LOG(3,(THIS_FILE, " test_resolve() error 20: IP address mismatch"));
350
if (ra->sin_port != rb->sin_port) {
351
PJ_LOG(3,(THIS_FILE, " test_resolve() error 30: port mismatch"));
354
if (ref->entry[i].addr_len != result.servers.entry[i].addr_len) {
355
PJ_LOG(3,(THIS_FILE, " test_resolve() error 40: addr_len mismatch"));
358
if (ref->entry[i].type != result.servers.entry[i].type) {
359
PJ_LOG(3,(THIS_FILE, " test_resolve() error 50: transport type mismatch"));
369
* Perform round-robin/load balance test.
371
static int round_robin_test(pj_pool_t *pool)
373
enum { COUNT = 400, PCT_ALLOWANCE = 5 };
383
{ "2.2.2.2", 65, 0 },
384
{ "3.3.3.3", 32, 0 },
388
PJ_LOG(3,(THIS_FILE, " Performing round-robin/load-balance test.."));
390
/* Do multiple resolve request to "example.com".
391
* The resolver should select the server based on the weight proportion
392
* the the servers in the SRV entry.
394
for (i=0; i<COUNT; ++i) {
395
pjsip_host_info dest;
396
struct result result;
399
dest.type = PJSIP_TRANSPORT_UDP;
400
dest.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_UDP);
401
dest.addr.host = pj_str("example.com");
404
result.status = 0x12345678;
406
pjsip_endpt_resolve(endpt, pool, &dest, &result, &cb);
408
while (result.status == 0x12345678) {
410
pj_time_val timeout = { 1, 0 };
411
pjsip_endpt_handle_events(endpt, &timeout);
413
pj_dns_resolver_dump(pjsip_endpt_get_resolver(endpt), PJ_TRUE);
416
/* Find which server was "hit" */
417
for (j=0; j<PJ_ARRAY_SIZE(server_hit); ++j) {
422
tmp = pj_str(server_hit[j].ip_addr);
423
a1 = pj_inet_addr(&tmp);
424
a2 = (pj_sockaddr_in*) &result.servers.entry[0].addr;
426
if (a1.s_addr == a2->sin_addr.s_addr) {
427
server_hit[j].hits++;
432
if (j == PJ_ARRAY_SIZE(server_hit)) {
433
PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 10: returned address mismatch"));
438
/* Print the actual hit rate */
439
for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) {
440
PJ_LOG(3,(THIS_FILE, " ..Server %s: weight=%d%%, hit %d%% times",
441
server_hit[i].ip_addr, server_hit[i].percent,
442
(server_hit[i].hits * 100) / COUNT));
445
/* Compare the actual hit with the weight proportion */
446
for (i=0; i<PJ_ARRAY_SIZE(server_hit); ++i) {
447
int actual_pct = (server_hit[i].hits * 100) / COUNT;
449
if (actual_pct + PCT_ALLOWANCE < (int)server_hit[i].percent ||
450
actual_pct - PCT_ALLOWANCE > (int)server_hit[i].percent)
453
"..round_robin_test() error 20: "
454
"hit rate difference for server %s (%d%%) is more than "
455
"tolerable allowance (%d%%)",
456
server_hit[i].ip_addr,
457
actual_pct - server_hit[i].percent,
464
" Load balance test success, hit-rate is "
465
"within %d%% allowance", PCT_ALLOWANCE));
470
#define C(expr) status = expr; \
471
if (status != PJ_SUCCESS) app_perror(THIS_FILE, "Error", status);
473
static void add_ref(pjsip_server_addresses *r,
474
pjsip_transport_type_e type,
481
r->entry[r->count].type = type;
482
r->entry[r->count].priority = 0;
483
r->entry[r->count].weight = 0;
484
r->entry[r->count].addr_len = sizeof(pj_sockaddr_in);
486
a = (pj_sockaddr_in *)&r->entry[r->count].addr;
487
a->sin_family = pj_AF_INET();
489
a->sin_addr = pj_inet_addr(&tmp);
490
a->sin_port = pj_htons((pj_uint16_t)port);
495
static void create_ref(pjsip_server_addresses *r,
496
pjsip_transport_type_e type,
501
add_ref(r, type, addr, port);
508
int resolve_test(void)
511
pj_dns_resolver *resv;
513
pj_uint16_t port = 5353;
516
pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
518
status = pjsip_endpt_create_resolver(endpt, &resv);
520
nameserver = pj_str("192.168.0.106");
521
pj_dns_resolver_set_ns(resv, 1, &nameserver, &port);
522
pjsip_endpt_set_resolver(endpt, resv);
524
add_dns_entries(resv);
526
/* These all should be resolved as IP addresses (DNS A query) */
528
pjsip_server_addresses ref;
529
create_ref(&ref, PJSIP_TRANSPORT_UDP, "1.1.1.1", 5060);
530
status = test_resolve("IP address without transport and port", pool, PJSIP_TRANSPORT_UNSPECIFIED, "1.1.1.1", 0, &ref);
531
if (status != PJ_SUCCESS)
535
pjsip_server_addresses ref;
536
create_ref(&ref, PJSIP_TRANSPORT_UDP, "1.1.1.1", 5060);
537
status = test_resolve("IP address with explicit port", pool, PJSIP_TRANSPORT_UNSPECIFIED, "1.1.1.1", 5060, &ref);
538
if (status != PJ_SUCCESS)
542
pjsip_server_addresses ref;
543
create_ref(&ref, PJSIP_TRANSPORT_TCP, "1.1.1.1", 5060);
544
status = test_resolve("IP address without port (TCP)", pool, PJSIP_TRANSPORT_TCP,"1.1.1.1", 0, &ref);
545
if (status != PJ_SUCCESS)
549
pjsip_server_addresses ref;
550
create_ref(&ref, PJSIP_TRANSPORT_TLS, "1.1.1.1", 5061);
551
status = test_resolve("IP address without port (TLS)", pool, PJSIP_TRANSPORT_TLS, "1.1.1.1", 0, &ref);
552
if (status != PJ_SUCCESS)
556
/* This should be resolved as DNS A record (because port is present) */
558
pjsip_server_addresses ref;
559
create_ref(&ref, PJSIP_TRANSPORT_UDP, "5.5.5.5", 5060);
560
status = test_resolve("domain name with port should resolve to A record", pool, PJSIP_TRANSPORT_UNSPECIFIED, "example.com", 5060, &ref);
561
if (status != PJ_SUCCESS)
565
/* This will fail to be resolved as SRV, resolver should fallback to
566
* resolving to A record.
569
pjsip_server_addresses ref;
570
create_ref(&ref, PJSIP_TRANSPORT_UDP, "2.2.2.2", 5060);
571
status = test_resolve("failure with SRV fallback to A record", pool, PJSIP_TRANSPORT_UNSPECIFIED, "sip02.example.com", 0, &ref);
572
if (status != PJ_SUCCESS)
576
/* Same as above, but explicitly for TLS. */
578
pjsip_server_addresses ref;
579
create_ref(&ref, PJSIP_TRANSPORT_TLS, "2.2.2.2", 5061);
580
status = test_resolve("failure with SRV fallback to A record (for TLS)", pool, PJSIP_TRANSPORT_TLS, "sip02.example.com", 0, &ref);
581
if (status != PJ_SUCCESS)
585
/* Standard DNS SRV followed by A recolution */
587
pjsip_server_addresses ref;
588
create_ref(&ref, PJSIP_TRANSPORT_UDP, "6.6.6.6", 50060);
589
status = test_resolve("standard SRV resolution", pool, PJSIP_TRANSPORT_UNSPECIFIED, "domain.com", 0, &ref);
590
if (status != PJ_SUCCESS)
594
/* Standard DNS SRV followed by A recolution (explicit transport) */
596
pjsip_server_addresses ref;
597
create_ref(&ref, PJSIP_TRANSPORT_TCP, "6.6.6.6", 50060);
598
add_ref(&ref, PJSIP_TRANSPORT_TCP, "7.7.7.7", 50060);
599
status = test_resolve("standard SRV resolution with explicit transport (TCP)", pool, PJSIP_TRANSPORT_TCP, "domain.com", 0, &ref);
600
if (status != PJ_SUCCESS)
605
/* Round robin/load balance test */
606
if (round_robin_test(pool) != 0)
611
status = test_resolve("timeout test", pool, PJSIP_TRANSPORT_UNSPECIFIED, "an.invalid.address", 0, NULL);
612
if (status == PJ_SUCCESS)