/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" #include #include #include /* For logging purpose. */ #define THIS_FILE "dns_test.c" struct result { pj_status_t status; pjsip_server_addresses servers; }; static void cb(pj_status_t status, void *token, const struct pjsip_server_addresses *addr) { struct result *result = (struct result*) token; result->status = status; if (status == PJ_SUCCESS) pj_memcpy(&result->servers, addr, sizeof(*addr)); } static void add_dns_entries(pj_dns_resolver *resv) { /* Inject DNS SRV entry */ pj_dns_parsed_packet pkt; pj_dns_parsed_query q; pj_dns_parsed_rr ans[4]; pj_dns_parsed_rr ar[5]; pj_str_t tmp; unsigned i; /* * This is answer to SRV query to "example.com" domain, and * the answer contains full reference to the A records of * the server. The full DNS records is : _sip._udp.example.com 3600 IN SRV 0 0 5060 sip01.example.com. _sip._udp.example.com 3600 IN SRV 0 20 5060 sip02.example.com. _sip._udp.example.com 3600 IN SRV 0 10 5060 sip03.example.com. _sip._udp.example.com 3600 IN SRV 1 0 5060 sip04.example.com. sip01.example.com. 3600 IN A 1.1.1.1 sip02.example.com. 3600 IN A 2.2.2.2 sip03.example.com. 3600 IN A 3.3.3.3 sip04.example.com. 3600 IN A 4.4.4.4 ; Additionally, add A record for "example.com" example.com. 3600 IN A 5.5.5.5 */ pj_bzero(&pkt, sizeof(pkt)); pj_bzero(ans, sizeof(ans)); pj_bzero(ar, sizeof(ar)); pkt.hdr.flags = PJ_DNS_SET_QR(1); pkt.hdr.anscount = PJ_ARRAY_SIZE(ans); pkt.hdr.arcount = 0; pkt.ans = ans; pkt.arr = ar; ans[0].name = pj_str("_sip._udp.example.com"); ans[0].type = PJ_DNS_TYPE_SRV; ans[0].dnsclass = PJ_DNS_CLASS_IN; ans[0].ttl = 3600; ans[0].rdata.srv.prio = 0; ans[0].rdata.srv.weight = 0; ans[0].rdata.srv.port = 5060; ans[0].rdata.srv.target = pj_str("sip01.example.com"); ans[1].name = pj_str("_sip._udp.example.com"); ans[1].type = PJ_DNS_TYPE_SRV; ans[1].dnsclass = PJ_DNS_CLASS_IN; ans[1].ttl = 3600; ans[1].rdata.srv.prio = 0; ans[1].rdata.srv.weight = 20; ans[1].rdata.srv.port = 5060; ans[1].rdata.srv.target = pj_str("sip02.example.com"); ans[2].name = pj_str("_sip._udp.example.com"); ans[2].type = PJ_DNS_TYPE_SRV; ans[2].dnsclass = PJ_DNS_CLASS_IN; ans[2].ttl = 3600; ans[2].rdata.srv.prio = 0; ans[2].rdata.srv.weight = 10; ans[2].rdata.srv.port = 5060; ans[2].rdata.srv.target = pj_str("sip03.example.com"); ans[3].name = pj_str("_sip._udp.example.com"); ans[3].type = PJ_DNS_TYPE_SRV; ans[3].dnsclass = PJ_DNS_CLASS_IN; ans[3].ttl = 3600; ans[3].rdata.srv.prio = 1; ans[3].rdata.srv.weight = 0; ans[3].rdata.srv.port = 5060; ans[3].rdata.srv.target = pj_str("sip04.example.com"); pj_dns_resolver_add_entry( resv, &pkt, PJ_FALSE); ar[0].name = pj_str("sip01.example.com"); ar[0].type = PJ_DNS_TYPE_A; ar[0].dnsclass = PJ_DNS_CLASS_IN; ar[0].ttl = 3600; ar[0].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "1.1.1.1")); ar[1].name = pj_str("sip02.example.com"); ar[1].type = PJ_DNS_TYPE_A; ar[1].dnsclass = PJ_DNS_CLASS_IN; ar[1].ttl = 3600; ar[1].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "2.2.2.2")); ar[2].name = pj_str("sip03.example.com"); ar[2].type = PJ_DNS_TYPE_A; ar[2].dnsclass = PJ_DNS_CLASS_IN; ar[2].ttl = 3600; ar[2].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "3.3.3.3")); ar[3].name = pj_str("sip04.example.com"); ar[3].type = PJ_DNS_TYPE_A; ar[3].dnsclass = PJ_DNS_CLASS_IN; ar[3].ttl = 3600; ar[3].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "4.4.4.4")); ar[4].name = pj_str("example.com"); ar[4].type = PJ_DNS_TYPE_A; ar[4].dnsclass = PJ_DNS_CLASS_IN; ar[4].ttl = 3600; ar[4].rdata.a.ip_addr = pj_inet_addr(pj_cstr(&tmp, "5.5.5.5")); /* * Create individual A records for all hosts in "example.com" domain. */ for (i=0; icount != result.servers.count) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 10: result count mismatch")); return 10; } for (i=0; icount; ++i) { pj_sockaddr_in *ra = (pj_sockaddr_in *)&ref->entry[i].addr; pj_sockaddr_in *rb = (pj_sockaddr_in *)&result.servers.entry[i].addr; if (ra->sin_addr.s_addr != rb->sin_addr.s_addr) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 20: IP address mismatch")); return 20; } if (ra->sin_port != rb->sin_port) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 30: port mismatch")); return 30; } if (ref->entry[i].addr_len != result.servers.entry[i].addr_len) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 40: addr_len mismatch")); return 40; } if (ref->entry[i].type != result.servers.entry[i].type) { PJ_LOG(3,(THIS_FILE, " test_resolve() error 50: transport type mismatch")); return 50; } } } return PJ_SUCCESS; } /* * Perform round-robin/load balance test. */ static int round_robin_test(pj_pool_t *pool) { enum { COUNT = 400, PCT_ALLOWANCE = 5 }; unsigned i; struct server_hit { char *ip_addr; unsigned percent; unsigned hits; } server_hit[] = { { "1.1.1.1", 3, 0 }, { "2.2.2.2", 65, 0 }, { "3.3.3.3", 32, 0 }, { "4.4.4.4", 0, 0 } }; PJ_LOG(3,(THIS_FILE, " Performing round-robin/load-balance test..")); /* Do multiple resolve request to "example.com". * The resolver should select the server based on the weight proportion * the the servers in the SRV entry. */ for (i=0; isin_addr.s_addr) { server_hit[j].hits++; break; } } if (j == PJ_ARRAY_SIZE(server_hit)) { PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 10: returned address mismatch")); return 10; } } /* Print the actual hit rate */ for (i=0; i (int)server_hit[i].percent) { PJ_LOG(1,(THIS_FILE, "..round_robin_test() error 20: " "hit rate difference for server %s (%d%%) is more than " "tolerable allowance (%d%%)", server_hit[i].ip_addr, actual_pct - server_hit[i].percent, PCT_ALLOWANCE)); return 20; } } PJ_LOG(3,(THIS_FILE, " Load balance test success, hit-rate is " "within %d%% allowance", PCT_ALLOWANCE)); return PJ_SUCCESS; } #define C(expr) status = expr; \ if (status != PJ_SUCCESS) app_perror(THIS_FILE, "Error", status); static void add_ref(pjsip_server_addresses *r, pjsip_transport_type_e type, char *addr, int port) { pj_sockaddr_in *a; pj_str_t tmp; r->entry[r->count].type = type; r->entry[r->count].priority = 0; r->entry[r->count].weight = 0; r->entry[r->count].addr_len = sizeof(pj_sockaddr_in); a = (pj_sockaddr_in *)&r->entry[r->count].addr; a->sin_family = pj_AF_INET(); tmp = pj_str(addr); a->sin_addr = pj_inet_addr(&tmp); a->sin_port = pj_htons((pj_uint16_t)port); r->count++; } static void create_ref(pjsip_server_addresses *r, pjsip_transport_type_e type, char *addr, int port) { r->count = 0; add_ref(r, type, addr, port); } /* * Main test entry. */ int resolve_test(void) { pj_pool_t *pool; pj_dns_resolver *resv; pj_str_t nameserver; pj_uint16_t port = 5353; pj_status_t status; pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000); status = pjsip_endpt_create_resolver(endpt, &resv); nameserver = pj_str("192.168.0.106"); pj_dns_resolver_set_ns(resv, 1, &nameserver, &port); pjsip_endpt_set_resolver(endpt, resv); add_dns_entries(resv); /* These all should be resolved as IP addresses (DNS A query) */ { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_UDP, "1.1.1.1", 5060); status = test_resolve("IP address without transport and port", pool, PJSIP_TRANSPORT_UNSPECIFIED, "1.1.1.1", 0, &ref); if (status != PJ_SUCCESS) return -100; } { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_UDP, "1.1.1.1", 5060); status = test_resolve("IP address with explicit port", pool, PJSIP_TRANSPORT_UNSPECIFIED, "1.1.1.1", 5060, &ref); if (status != PJ_SUCCESS) return -110; } { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_TCP, "1.1.1.1", 5060); status = test_resolve("IP address without port (TCP)", pool, PJSIP_TRANSPORT_TCP,"1.1.1.1", 0, &ref); if (status != PJ_SUCCESS) return -120; } { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_TLS, "1.1.1.1", 5061); status = test_resolve("IP address without port (TLS)", pool, PJSIP_TRANSPORT_TLS, "1.1.1.1", 0, &ref); if (status != PJ_SUCCESS) return -130; } /* This should be resolved as DNS A record (because port is present) */ { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_UDP, "5.5.5.5", 5060); status = test_resolve("domain name with port should resolve to A record", pool, PJSIP_TRANSPORT_UNSPECIFIED, "example.com", 5060, &ref); if (status != PJ_SUCCESS) return -140; } /* This will fail to be resolved as SRV, resolver should fallback to * resolving to A record. */ { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_UDP, "2.2.2.2", 5060); status = test_resolve("failure with SRV fallback to A record", pool, PJSIP_TRANSPORT_UNSPECIFIED, "sip02.example.com", 0, &ref); if (status != PJ_SUCCESS) return -150; } /* Same as above, but explicitly for TLS. */ { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_TLS, "2.2.2.2", 5061); status = test_resolve("failure with SRV fallback to A record (for TLS)", pool, PJSIP_TRANSPORT_TLS, "sip02.example.com", 0, &ref); if (status != PJ_SUCCESS) return -150; } /* Standard DNS SRV followed by A recolution */ { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_UDP, "6.6.6.6", 50060); status = test_resolve("standard SRV resolution", pool, PJSIP_TRANSPORT_UNSPECIFIED, "domain.com", 0, &ref); if (status != PJ_SUCCESS) return -155; } /* Standard DNS SRV followed by A recolution (explicit transport) */ { pjsip_server_addresses ref; create_ref(&ref, PJSIP_TRANSPORT_TCP, "6.6.6.6", 50060); add_ref(&ref, PJSIP_TRANSPORT_TCP, "7.7.7.7", 50060); status = test_resolve("standard SRV resolution with explicit transport (TCP)", pool, PJSIP_TRANSPORT_TCP, "domain.com", 0, &ref); if (status != PJ_SUCCESS) return -160; } /* Round robin/load balance test */ if (round_robin_test(pool) != 0) return -170; /* Timeout test */ { status = test_resolve("timeout test", pool, PJSIP_TRANSPORT_UNSPECIFIED, "an.invalid.address", 0, NULL); if (status == PJ_SUCCESS) return -150; } return 0; }