| /* $Id$ */ |
| /* |
| * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> |
| * |
| * 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 <pjlib-util.h> |
| #include <pjlib.h> |
| #include "test.h" |
| |
| |
| #define THIS_FILE "srv_resolver_test.c" |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /* |
| * TODO: create various invalid DNS packets. |
| */ |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| |
| #define ACTION_REPLY 0 |
| #define ACTION_IGNORE -1 |
| #define ACTION_CB -2 |
| |
| static struct server_t |
| { |
| pj_sock_t sock; |
| pj_uint16_t port; |
| pj_thread_t *thread; |
| |
| /* Action: |
| * 0: reply with the response in resp. |
| * -1: ignore query (to simulate timeout). |
| * other: reply with that error |
| */ |
| int action; |
| |
| pj_dns_parsed_packet resp; |
| void (*action_cb)(const pj_dns_parsed_packet *pkt, |
| pj_dns_parsed_packet **p_res); |
| |
| unsigned pkt_count; |
| |
| } g_server[2]; |
| |
| static pj_pool_t *pool; |
| static pj_dns_resolver *resolver; |
| static pj_bool_t thread_quit; |
| static pj_timer_heap_t *timer_heap; |
| static pj_ioqueue_t *ioqueue; |
| static pj_thread_t *poll_thread; |
| static pj_sem_t *sem; |
| static pj_dns_settings set; |
| |
| #define MAX_LABEL 32 |
| |
| struct label_tab |
| { |
| unsigned count; |
| |
| struct { |
| unsigned pos; |
| pj_str_t label; |
| } a[MAX_LABEL]; |
| }; |
| |
| static void write16(pj_uint8_t *p, pj_uint16_t val) |
| { |
| p[0] = (pj_uint8_t)(val >> 8); |
| p[1] = (pj_uint8_t)(val & 0xFF); |
| } |
| |
| static void write32(pj_uint8_t *p, pj_uint32_t val) |
| { |
| val = pj_htonl(val); |
| pj_memcpy(p, &val, 4); |
| } |
| |
| static int print_name(pj_uint8_t *pkt, int size, |
| pj_uint8_t *pos, const pj_str_t *name, |
| struct label_tab *tab) |
| { |
| pj_uint8_t *p = pos; |
| const char *endlabel, *endname; |
| unsigned i; |
| pj_str_t label; |
| |
| /* Check if name is in the table */ |
| for (i=0; i<tab->count; ++i) { |
| if (pj_strcmp(&tab->a[i].label, name)==0) |
| break; |
| } |
| |
| if (i != tab->count) { |
| write16(p, (pj_uint16_t)(tab->a[i].pos | (0xc0 << 8))); |
| return 2; |
| } else { |
| if (tab->count < MAX_LABEL) { |
| tab->a[tab->count].pos = (unsigned)(p-pkt); |
| tab->a[tab->count].label.ptr = (char*)(p+1); |
| tab->a[tab->count].label.slen = name->slen; |
| ++tab->count; |
| } |
| } |
| |
| endlabel = name->ptr; |
| endname = name->ptr + name->slen; |
| |
| label.ptr = (char*)name->ptr; |
| |
| while (endlabel != endname) { |
| |
| while (endlabel != endname && *endlabel != '.') |
| ++endlabel; |
| |
| label.slen = (endlabel - label.ptr); |
| |
| if (size < label.slen+1) |
| return -1; |
| |
| *p = (pj_uint8_t)label.slen; |
| pj_memcpy(p+1, label.ptr, label.slen); |
| |
| size -= (int)(label.slen+1); |
| p += (label.slen+1); |
| |
| if (endlabel != endname && *endlabel == '.') |
| ++endlabel; |
| label.ptr = (char*)endlabel; |
| } |
| |
| if (size == 0) |
| return -1; |
| |
| *p++ = '\0'; |
| |
| return (int)(p-pos); |
| } |
| |
| static int print_rr(pj_uint8_t *pkt, int size, pj_uint8_t *pos, |
| const pj_dns_parsed_rr *rr, struct label_tab *tab) |
| { |
| pj_uint8_t *p = pos; |
| int len; |
| |
| len = print_name(pkt, size, pos, &rr->name, tab); |
| if (len < 0) |
| return -1; |
| |
| p += len; |
| size -= len; |
| |
| if (size < 8) |
| return -1; |
| |
| pj_assert(rr->dnsclass == 1); |
| |
| write16(p+0, (pj_uint16_t)rr->type); /* type */ |
| write16(p+2, (pj_uint16_t)rr->dnsclass); /* class */ |
| write32(p+4, rr->ttl); /* TTL */ |
| |
| p += 8; |
| size -= 8; |
| |
| if (rr->type == PJ_DNS_TYPE_A) { |
| |
| if (size < 6) |
| return -1; |
| |
| /* RDLEN is 4 */ |
| write16(p, 4); |
| |
| /* Address */ |
| pj_memcpy(p+2, &rr->rdata.a.ip_addr, 4); |
| |
| p += 6; |
| size -= 6; |
| |
| } else if (rr->type == PJ_DNS_TYPE_CNAME || |
| rr->type == PJ_DNS_TYPE_NS || |
| rr->type == PJ_DNS_TYPE_PTR) { |
| |
| if (size < 4) |
| return -1; |
| |
| len = print_name(pkt, size-2, p+2, &rr->rdata.cname.name, tab); |
| if (len < 0) |
| return -1; |
| |
| write16(p, (pj_uint16_t)len); |
| |
| p += (len + 2); |
| size -= (len + 2); |
| |
| } else if (rr->type == PJ_DNS_TYPE_SRV) { |
| |
| if (size < 10) |
| return -1; |
| |
| write16(p+2, rr->rdata.srv.prio); /* Priority */ |
| write16(p+4, rr->rdata.srv.weight); /* Weight */ |
| write16(p+6, rr->rdata.srv.port); /* Port */ |
| |
| /* Target */ |
| len = print_name(pkt, size-8, p+8, &rr->rdata.srv.target, tab); |
| if (len < 0) |
| return -1; |
| |
| /* RDLEN */ |
| write16(p, (pj_uint16_t)(len + 6)); |
| |
| p += (len + 8); |
| size -= (len + 8); |
| |
| } else { |
| pj_assert(!"Not supported"); |
| return -1; |
| } |
| |
| return (int)(p-pos); |
| } |
| |
| static int print_packet(const pj_dns_parsed_packet *rec, pj_uint8_t *pkt, |
| int size) |
| { |
| pj_uint8_t *p = pkt; |
| struct label_tab tab; |
| int i, len; |
| |
| tab.count = 0; |
| |
| #if 0 |
| pj_enter_critical_section(); |
| PJ_LOG(3,(THIS_FILE, "Sending response:")); |
| pj_dns_dump_packet(rec); |
| pj_leave_critical_section(); |
| #endif |
| |
| pj_assert(sizeof(pj_dns_hdr)==12); |
| if (size < (int)sizeof(pj_dns_hdr)) |
| return -1; |
| |
| /* Initialize header */ |
| write16(p+0, rec->hdr.id); |
| write16(p+2, rec->hdr.flags); |
| write16(p+4, rec->hdr.qdcount); |
| write16(p+6, rec->hdr.anscount); |
| write16(p+8, rec->hdr.nscount); |
| write16(p+10, rec->hdr.arcount); |
| |
| p = pkt + sizeof(pj_dns_hdr); |
| size -= sizeof(pj_dns_hdr); |
| |
| /* Print queries */ |
| for (i=0; i<rec->hdr.qdcount; ++i) { |
| |
| len = print_name(pkt, size, p, &rec->q[i].name, &tab); |
| if (len < 0) |
| return -1; |
| |
| p += len; |
| size -= len; |
| |
| if (size < 4) |
| return -1; |
| |
| /* Set type */ |
| write16(p+0, (pj_uint16_t)rec->q[i].type); |
| |
| /* Set class (IN=1) */ |
| pj_assert(rec->q[i].dnsclass == 1); |
| write16(p+2, rec->q[i].dnsclass); |
| |
| p += 4; |
| } |
| |
| /* Print answers */ |
| for (i=0; i<rec->hdr.anscount; ++i) { |
| len = print_rr(pkt, size, p, &rec->ans[i], &tab); |
| if (len < 0) |
| return -1; |
| |
| p += len; |
| size -= len; |
| } |
| |
| /* Print NS records */ |
| for (i=0; i<rec->hdr.nscount; ++i) { |
| len = print_rr(pkt, size, p, &rec->ns[i], &tab); |
| if (len < 0) |
| return -1; |
| |
| p += len; |
| size -= len; |
| } |
| |
| /* Print additional records */ |
| for (i=0; i<rec->hdr.arcount; ++i) { |
| len = print_rr(pkt, size, p, &rec->arr[i], &tab); |
| if (len < 0) |
| return -1; |
| |
| p += len; |
| size -= len; |
| } |
| |
| return (int)(p - pkt); |
| } |
| |
| |
| static int server_thread(void *p) |
| { |
| struct server_t *srv = (struct server_t*)p; |
| |
| while (!thread_quit) { |
| pj_fd_set_t rset; |
| pj_time_val timeout = {0, 500}; |
| pj_sockaddr_in src_addr; |
| pj_dns_parsed_packet *req; |
| char pkt[1024]; |
| pj_ssize_t pkt_len; |
| int rc, src_len; |
| |
| PJ_FD_ZERO(&rset); |
| PJ_FD_SET(srv->sock, &rset); |
| |
| rc = pj_sock_select((int)(srv->sock+1), &rset, NULL, NULL, &timeout); |
| if (rc != 1) |
| continue; |
| |
| src_len = sizeof(src_addr); |
| pkt_len = sizeof(pkt); |
| rc = pj_sock_recvfrom(srv->sock, pkt, &pkt_len, 0, |
| &src_addr, &src_len); |
| if (rc != 0) { |
| app_perror("Server error receiving packet", rc); |
| continue; |
| } |
| |
| PJ_LOG(5,(THIS_FILE, "Server %d processing packet", srv - &g_server[0])); |
| srv->pkt_count++; |
| |
| rc = pj_dns_parse_packet(pool, pkt, (unsigned)pkt_len, &req); |
| if (rc != PJ_SUCCESS) { |
| app_perror("server error parsing packet", rc); |
| continue; |
| } |
| |
| /* Verify packet */ |
| pj_assert(req->hdr.qdcount == 1); |
| pj_assert(req->q[0].dnsclass == 1); |
| |
| /* Simulate network RTT */ |
| pj_thread_sleep(50); |
| |
| if (srv->action == ACTION_IGNORE) { |
| continue; |
| } else if (srv->action == ACTION_REPLY) { |
| srv->resp.hdr.id = req->hdr.id; |
| pkt_len = print_packet(&srv->resp, (pj_uint8_t*)pkt, sizeof(pkt)); |
| pj_sock_sendto(srv->sock, pkt, &pkt_len, 0, &src_addr, src_len); |
| } else if (srv->action == ACTION_CB) { |
| pj_dns_parsed_packet *resp; |
| (*srv->action_cb)(req, &resp); |
| resp->hdr.id = req->hdr.id; |
| pkt_len = print_packet(resp, (pj_uint8_t*)pkt, sizeof(pkt)); |
| pj_sock_sendto(srv->sock, pkt, &pkt_len, 0, &src_addr, src_len); |
| } else if (srv->action > 0) { |
| req->hdr.flags |= PJ_DNS_SET_RCODE(srv->action); |
| pkt_len = print_packet(req, (pj_uint8_t*)pkt, sizeof(pkt)); |
| pj_sock_sendto(srv->sock, pkt, &pkt_len, 0, &src_addr, src_len); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int poll_worker_thread(void *p) |
| { |
| PJ_UNUSED_ARG(p); |
| |
| while (!thread_quit) { |
| pj_time_val delay = {0, 100}; |
| pj_timer_heap_poll(timer_heap, NULL); |
| pj_ioqueue_poll(ioqueue, &delay); |
| } |
| |
| return 0; |
| } |
| |
| static void destroy(void); |
| |
| static int init(void) |
| { |
| pj_status_t status; |
| pj_str_t nameservers[2]; |
| pj_uint16_t ports[2]; |
| int i; |
| |
| nameservers[0] = pj_str("127.0.0.1"); |
| ports[0] = 5553; |
| nameservers[1] = pj_str("127.0.0.1"); |
| ports[1] = 5554; |
| |
| g_server[0].port = ports[0]; |
| g_server[1].port = ports[1]; |
| |
| pool = pj_pool_create(mem, NULL, 2000, 2000, NULL); |
| |
| status = pj_sem_create(pool, NULL, 0, 2, &sem); |
| pj_assert(status == PJ_SUCCESS); |
| |
| for (i=0; i<2; ++i) { |
| pj_sockaddr_in addr; |
| |
| status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &g_server[i].sock); |
| if (status != PJ_SUCCESS) |
| return -10; |
| |
| pj_sockaddr_in_init(&addr, NULL, (pj_uint16_t)g_server[i].port); |
| |
| status = pj_sock_bind(g_server[i].sock, &addr, sizeof(addr)); |
| if (status != PJ_SUCCESS) |
| return -20; |
| |
| status = pj_thread_create(pool, NULL, &server_thread, &g_server[i], |
| 0, 0, &g_server[i].thread); |
| if (status != PJ_SUCCESS) |
| return -30; |
| } |
| |
| status = pj_timer_heap_create(pool, 16, &timer_heap); |
| pj_assert(status == PJ_SUCCESS); |
| |
| status = pj_ioqueue_create(pool, 16, &ioqueue); |
| pj_assert(status == PJ_SUCCESS); |
| |
| status = pj_dns_resolver_create(mem, NULL, 0, timer_heap, ioqueue, &resolver); |
| if (status != PJ_SUCCESS) |
| return -40; |
| |
| pj_dns_resolver_get_settings(resolver, &set); |
| set.good_ns_ttl = 20; |
| set.bad_ns_ttl = 20; |
| pj_dns_resolver_set_settings(resolver, &set); |
| |
| status = pj_dns_resolver_set_ns(resolver, 2, nameservers, ports); |
| pj_assert(status == PJ_SUCCESS); |
| |
| status = pj_thread_create(pool, NULL, &poll_worker_thread, NULL, 0, 0, &poll_thread); |
| pj_assert(status == PJ_SUCCESS); |
| |
| return 0; |
| } |
| |
| |
| static void destroy(void) |
| { |
| int i; |
| |
| thread_quit = PJ_TRUE; |
| |
| for (i=0; i<2; ++i) { |
| pj_thread_join(g_server[i].thread); |
| pj_sock_close(g_server[i].sock); |
| } |
| |
| pj_thread_join(poll_thread); |
| |
| pj_dns_resolver_destroy(resolver, PJ_FALSE); |
| pj_ioqueue_destroy(ioqueue); |
| pj_timer_heap_destroy(timer_heap); |
| |
| pj_sem_destroy(sem); |
| pj_pool_release(pool); |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /* DNS A parser tests */ |
| static int a_parser_test(void) |
| { |
| pj_dns_parsed_packet pkt; |
| pj_dns_a_record rec; |
| pj_status_t rc; |
| |
| PJ_LOG(3,(THIS_FILE, " DNS A record parser tests")); |
| |
| pkt.q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); |
| pkt.ans = (pj_dns_parsed_rr*) |
| pj_pool_calloc(pool, 32, sizeof(pj_dns_parsed_rr)); |
| |
| /* Simple answer with direct A record, but with addition of |
| * a CNAME and another A to confuse the parser. |
| */ |
| PJ_LOG(3,(THIS_FILE, " A RR with duplicate CNAME/A")); |
| pkt.hdr.flags = 0; |
| pkt.hdr.qdcount = 1; |
| pkt.q[0].type = PJ_DNS_TYPE_A; |
| pkt.q[0].dnsclass = 1; |
| pkt.q[0].name = pj_str("ahost"); |
| pkt.hdr.anscount = 3; |
| |
| /* This is the RR corresponding to the query */ |
| pkt.ans[0].name = pj_str("ahost"); |
| pkt.ans[0].type = PJ_DNS_TYPE_A; |
| pkt.ans[0].dnsclass = 1; |
| pkt.ans[0].ttl = 1; |
| pkt.ans[0].rdata.a.ip_addr.s_addr = 0x01020304; |
| |
| /* CNAME to confuse the parser */ |
| pkt.ans[1].name = pj_str("ahost"); |
| pkt.ans[1].type = PJ_DNS_TYPE_CNAME; |
| pkt.ans[1].dnsclass = 1; |
| pkt.ans[1].ttl = 1; |
| pkt.ans[1].rdata.cname.name = pj_str("bhost"); |
| |
| /* DNS A RR to confuse the parser */ |
| pkt.ans[2].name = pj_str("bhost"); |
| pkt.ans[2].type = PJ_DNS_TYPE_A; |
| pkt.ans[2].dnsclass = 1; |
| pkt.ans[2].ttl = 1; |
| pkt.ans[2].rdata.a.ip_addr.s_addr = 0x0203; |
| |
| |
| rc = pj_dns_parse_a_response(&pkt, &rec); |
| pj_assert(rc == PJ_SUCCESS); |
| pj_assert(pj_strcmp2(&rec.name, "ahost")==0); |
| pj_assert(rec.alias.slen == 0); |
| pj_assert(rec.addr_count == 1); |
| pj_assert(rec.addr[0].s_addr == 0x01020304); |
| |
| /* Answer with the target corresponds to a CNAME entry, but not |
| * as the first record, and with additions of some CNAME and A |
| * entries to confuse the parser. |
| */ |
| PJ_LOG(3,(THIS_FILE, " CNAME RR with duplicate CNAME/A")); |
| pkt.hdr.flags = 0; |
| pkt.hdr.qdcount = 1; |
| pkt.q[0].type = PJ_DNS_TYPE_A; |
| pkt.q[0].dnsclass = 1; |
| pkt.q[0].name = pj_str("ahost"); |
| pkt.hdr.anscount = 4; |
| |
| /* This is the DNS A record for the alias */ |
| pkt.ans[0].name = pj_str("ahostalias"); |
| pkt.ans[0].type = PJ_DNS_TYPE_A; |
| pkt.ans[0].dnsclass = 1; |
| pkt.ans[0].ttl = 1; |
| pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202; |
| |
| /* CNAME entry corresponding to the query */ |
| pkt.ans[1].name = pj_str("ahost"); |
| pkt.ans[1].type = PJ_DNS_TYPE_CNAME; |
| pkt.ans[1].dnsclass = 1; |
| pkt.ans[1].ttl = 1; |
| pkt.ans[1].rdata.cname.name = pj_str("ahostalias"); |
| |
| /* Another CNAME to confuse the parser */ |
| pkt.ans[2].name = pj_str("ahost"); |
| pkt.ans[2].type = PJ_DNS_TYPE_CNAME; |
| pkt.ans[2].dnsclass = 1; |
| pkt.ans[2].ttl = 1; |
| pkt.ans[2].rdata.cname.name = pj_str("ahostalias2"); |
| |
| /* Another DNS A to confuse the parser */ |
| pkt.ans[3].name = pj_str("ahostalias2"); |
| pkt.ans[3].type = PJ_DNS_TYPE_A; |
| pkt.ans[3].dnsclass = 1; |
| pkt.ans[3].ttl = 1; |
| pkt.ans[3].rdata.a.ip_addr.s_addr = 0x03030303; |
| |
| rc = pj_dns_parse_a_response(&pkt, &rec); |
| pj_assert(rc == PJ_SUCCESS); |
| pj_assert(pj_strcmp2(&rec.name, "ahost")==0); |
| pj_assert(pj_strcmp2(&rec.alias, "ahostalias")==0); |
| pj_assert(rec.addr_count == 1); |
| pj_assert(rec.addr[0].s_addr == 0x02020202); |
| |
| /* |
| * No query section. |
| */ |
| PJ_LOG(3,(THIS_FILE, " No query section")); |
| pkt.hdr.qdcount = 0; |
| pkt.hdr.anscount = 0; |
| |
| rc = pj_dns_parse_a_response(&pkt, &rec); |
| pj_assert(rc == PJLIB_UTIL_EDNSINANSWER); |
| |
| /* |
| * No answer section. |
| */ |
| PJ_LOG(3,(THIS_FILE, " No answer section")); |
| pkt.hdr.flags = 0; |
| pkt.hdr.qdcount = 1; |
| pkt.q[0].type = PJ_DNS_TYPE_A; |
| pkt.q[0].dnsclass = 1; |
| pkt.q[0].name = pj_str("ahost"); |
| pkt.hdr.anscount = 0; |
| |
| rc = pj_dns_parse_a_response(&pkt, &rec); |
| pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); |
| |
| /* |
| * Answer doesn't match query. |
| */ |
| PJ_LOG(3,(THIS_FILE, " Answer doesn't match query")); |
| pkt.hdr.flags = 0; |
| pkt.hdr.qdcount = 1; |
| pkt.q[0].type = PJ_DNS_TYPE_A; |
| pkt.q[0].dnsclass = 1; |
| pkt.q[0].name = pj_str("ahost"); |
| pkt.hdr.anscount = 1; |
| |
| /* An answer that doesn't match the query */ |
| pkt.ans[0].name = pj_str("ahostalias"); |
| pkt.ans[0].type = PJ_DNS_TYPE_A; |
| pkt.ans[0].dnsclass = 1; |
| pkt.ans[0].ttl = 1; |
| pkt.ans[0].rdata.a.ip_addr.s_addr = 0x02020202; |
| |
| rc = pj_dns_parse_a_response(&pkt, &rec); |
| pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); |
| |
| |
| /* |
| * DNS CNAME that doesn't have corresponding DNS A. |
| */ |
| PJ_LOG(3,(THIS_FILE, " CNAME with no matching DNS A RR (1)")); |
| pkt.hdr.flags = 0; |
| pkt.hdr.qdcount = 1; |
| pkt.q[0].type = PJ_DNS_TYPE_A; |
| pkt.q[0].dnsclass = 1; |
| pkt.q[0].name = pj_str("ahost"); |
| pkt.hdr.anscount = 1; |
| |
| /* The CNAME */ |
| pkt.ans[0].name = pj_str("ahost"); |
| pkt.ans[0].type = PJ_DNS_TYPE_CNAME; |
| pkt.ans[0].dnsclass = 1; |
| pkt.ans[0].ttl = 1; |
| pkt.ans[0].rdata.cname.name = pj_str("ahostalias"); |
| |
| rc = pj_dns_parse_a_response(&pkt, &rec); |
| pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); |
| |
| |
| /* |
| * DNS CNAME that doesn't have corresponding DNS A. |
| */ |
| PJ_LOG(3,(THIS_FILE, " CNAME with no matching DNS A RR (2)")); |
| pkt.hdr.flags = 0; |
| pkt.hdr.qdcount = 1; |
| pkt.q[0].type = PJ_DNS_TYPE_A; |
| pkt.q[0].dnsclass = 1; |
| pkt.q[0].name = pj_str("ahost"); |
| pkt.hdr.anscount = 2; |
| |
| /* The CNAME */ |
| pkt.ans[0].name = pj_str("ahost"); |
| pkt.ans[0].type = PJ_DNS_TYPE_CNAME; |
| pkt.ans[0].dnsclass = 1; |
| pkt.ans[0].ttl = 1; |
| pkt.ans[0].rdata.cname.name = pj_str("ahostalias"); |
| |
| /* DNS A record, but the name doesn't match */ |
| pkt.ans[1].name = pj_str("ahost"); |
| pkt.ans[1].type = PJ_DNS_TYPE_A; |
| pkt.ans[1].dnsclass = 1; |
| pkt.ans[1].ttl = 1; |
| pkt.ans[1].rdata.a.ip_addr.s_addr = 0x01020304; |
| |
| rc = pj_dns_parse_a_response(&pkt, &rec); |
| pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC); |
| |
| return 0; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /* Simple DNS test */ |
| #define IP_ADDR0 0x00010203 |
| |
| static void dns_callback(void *user_data, |
| pj_status_t status, |
| pj_dns_parsed_packet *resp) |
| { |
| PJ_UNUSED_ARG(user_data); |
| |
| pj_sem_post(sem); |
| |
| PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return); |
| PJ_ASSERT_ON_FAIL(resp, return); |
| PJ_ASSERT_ON_FAIL(resp->hdr.anscount == 1, return); |
| PJ_ASSERT_ON_FAIL(resp->ans[0].type == PJ_DNS_TYPE_A, return); |
| PJ_ASSERT_ON_FAIL(resp->ans[0].rdata.a.ip_addr.s_addr == IP_ADDR0, return); |
| |
| } |
| |
| |
| static int simple_test(void) |
| { |
| pj_str_t name = pj_str("helloworld"); |
| pj_dns_parsed_packet *r; |
| pj_status_t status; |
| |
| PJ_LOG(3,(THIS_FILE, " simple successful test")); |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| g_server[0].action = ACTION_REPLY; |
| r = &g_server[0].resp; |
| r->hdr.qdcount = 1; |
| r->hdr.anscount = 1; |
| r->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); |
| r->q[0].type = PJ_DNS_TYPE_A; |
| r->q[0].dnsclass = 1; |
| r->q[0].name = name; |
| r->ans = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_rr); |
| r->ans[0].type = PJ_DNS_TYPE_A; |
| r->ans[0].dnsclass = 1; |
| r->ans[0].name = name; |
| r->ans[0].rdata.a.ip_addr.s_addr = IP_ADDR0; |
| |
| g_server[1].action = ACTION_REPLY; |
| r = &g_server[1].resp; |
| r->hdr.qdcount = 1; |
| r->hdr.anscount = 1; |
| r->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); |
| r->q[0].type = PJ_DNS_TYPE_A; |
| r->q[0].dnsclass = 1; |
| r->q[0].name = name; |
| r->ans = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_rr); |
| r->ans[0].type = PJ_DNS_TYPE_A; |
| r->ans[0].dnsclass = 1; |
| r->ans[0].name = name; |
| r->ans[0].rdata.a.ip_addr.s_addr = IP_ADDR0; |
| |
| status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, |
| &dns_callback, NULL, NULL); |
| if (status != PJ_SUCCESS) |
| return -1000; |
| |
| pj_sem_wait(sem); |
| pj_thread_sleep(1000); |
| |
| |
| /* Both servers must get packet */ |
| pj_assert(g_server[0].pkt_count == 1); |
| pj_assert(g_server[1].pkt_count == 1); |
| |
| return 0; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /* DNS nameserver fail-over test */ |
| |
| static void dns_callback_1b(void *user_data, |
| pj_status_t status, |
| pj_dns_parsed_packet *resp) |
| { |
| PJ_UNUSED_ARG(user_data); |
| PJ_UNUSED_ARG(resp); |
| |
| pj_sem_post(sem); |
| |
| PJ_ASSERT_ON_FAIL(status==PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN), |
| return); |
| } |
| |
| |
| |
| |
| /* DNS test */ |
| static int dns_test(void) |
| { |
| pj_str_t name = pj_str("name00"); |
| pj_status_t status; |
| |
| PJ_LOG(3,(THIS_FILE, " simple error response test")); |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; |
| g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; |
| |
| status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, |
| &dns_callback_1b, NULL, NULL); |
| if (status != PJ_SUCCESS) |
| return -1000; |
| |
| pj_sem_wait(sem); |
| pj_thread_sleep(1000); |
| |
| /* Now only server 0 should get packet, since both servers are |
| * in STATE_ACTIVE state |
| */ |
| pj_assert((g_server[0].pkt_count == 1 && g_server[1].pkt_count == 0) || |
| (g_server[1].pkt_count == 1 && g_server[0].pkt_count == 0)); |
| |
| /* Wait to allow probing period to complete */ |
| PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", |
| set.good_ns_ttl)); |
| pj_thread_sleep(set.good_ns_ttl * 1000); |
| |
| /* |
| * Fail-over test |
| */ |
| PJ_LOG(3,(THIS_FILE, " failing server0")); |
| g_server[0].action = ACTION_IGNORE; |
| g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| name = pj_str("name01"); |
| status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, |
| &dns_callback_1b, NULL, NULL); |
| if (status != PJ_SUCCESS) |
| return -1000; |
| |
| pj_sem_wait(sem); |
| |
| /* |
| * Check that both servers still receive requests, since they are |
| * in probing state. |
| */ |
| PJ_LOG(3,(THIS_FILE, " checking both NS during probing period")); |
| g_server[0].action = ACTION_IGNORE; |
| g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| name = pj_str("name02"); |
| status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, |
| &dns_callback_1b, NULL, NULL); |
| if (status != PJ_SUCCESS) |
| return -1000; |
| |
| pj_sem_wait(sem); |
| pj_thread_sleep(set.qretr_delay * set.qretr_count); |
| |
| /* Both servers must get requests */ |
| pj_assert(g_server[0].pkt_count >= 1); |
| pj_assert(g_server[1].pkt_count == 1); |
| |
| /* Wait to allow probing period to complete */ |
| PJ_LOG(3,(THIS_FILE, " waiting for probing state to end (%d sec)", |
| set.qretr_delay * |
| (set.qretr_count+2) / 1000)); |
| pj_thread_sleep(set.qretr_delay * (set.qretr_count + 2)); |
| |
| |
| /* |
| * Now only server 1 should get requests. |
| */ |
| PJ_LOG(3,(THIS_FILE, " verifying only good NS is used")); |
| g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; |
| g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| name = pj_str("name03"); |
| status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, |
| &dns_callback_1b, NULL, NULL); |
| if (status != PJ_SUCCESS) |
| return -1000; |
| |
| pj_sem_wait(sem); |
| pj_thread_sleep(1000); |
| |
| /* Both servers must get requests */ |
| pj_assert(g_server[0].pkt_count == 0); |
| pj_assert(g_server[1].pkt_count == 1); |
| |
| /* Wait to allow probing period to complete */ |
| PJ_LOG(3,(THIS_FILE, " waiting for active NS to expire (%d sec)", |
| set.good_ns_ttl)); |
| pj_thread_sleep(set.good_ns_ttl * 1000); |
| |
| /* |
| * Now fail server 1 to switch to server 0 |
| */ |
| g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; |
| g_server[1].action = ACTION_IGNORE; |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| name = pj_str("name04"); |
| status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, |
| &dns_callback_1b, NULL, NULL); |
| if (status != PJ_SUCCESS) |
| return -1000; |
| |
| pj_sem_wait(sem); |
| |
| /* Wait to allow probing period to complete */ |
| PJ_LOG(3,(THIS_FILE, " waiting for probing state (%d sec)", |
| set.qretr_delay * (set.qretr_count+2) / 1000)); |
| pj_thread_sleep(set.qretr_delay * (set.qretr_count + 2)); |
| |
| /* |
| * Now only server 0 should get requests. |
| */ |
| PJ_LOG(3,(THIS_FILE, " verifying good NS")); |
| g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; |
| g_server[1].action = ACTION_IGNORE; |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| name = pj_str("name05"); |
| status = pj_dns_resolver_start_query(resolver, &name, PJ_DNS_TYPE_A, 0, |
| &dns_callback_1b, NULL, NULL); |
| if (status != PJ_SUCCESS) |
| return -1000; |
| |
| pj_sem_wait(sem); |
| pj_thread_sleep(1000); |
| |
| /* Only good NS should get request */ |
| pj_assert(g_server[0].pkt_count == 1); |
| pj_assert(g_server[1].pkt_count == 0); |
| |
| |
| return 0; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /* Resolver test, normal, with CNAME */ |
| #define IP_ADDR1 0x02030405 |
| #define PORT1 50061 |
| |
| static void action1_1(const pj_dns_parsed_packet *pkt, |
| pj_dns_parsed_packet **p_res) |
| { |
| pj_dns_parsed_packet *res; |
| char *target = "sip.somedomain.com"; |
| |
| res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); |
| |
| if (res->q == NULL) { |
| res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); |
| } |
| if (res->ans == NULL) { |
| res->ans = (pj_dns_parsed_rr*) |
| pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr)); |
| } |
| |
| res->hdr.qdcount = 1; |
| res->q[0].type = pkt->q[0].type; |
| res->q[0].dnsclass = pkt->q[0].dnsclass; |
| res->q[0].name = pkt->q[0].name; |
| |
| if (pkt->q[0].type == PJ_DNS_TYPE_SRV) { |
| |
| pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp.somedomain.com")==0); |
| |
| res->hdr.anscount = 1; |
| res->ans[0].type = PJ_DNS_TYPE_SRV; |
| res->ans[0].dnsclass = 1; |
| res->ans[0].name = res->q[0].name; |
| res->ans[0].ttl = 1; |
| res->ans[0].rdata.srv.prio = 1; |
| res->ans[0].rdata.srv.weight = 2; |
| res->ans[0].rdata.srv.port = PORT1; |
| res->ans[0].rdata.srv.target = pj_str(target); |
| |
| } else if (pkt->q[0].type == PJ_DNS_TYPE_A) { |
| char *alias = "sipalias.somedomain.com"; |
| |
| pj_assert(pj_strcmp2(&res->q[0].name, target)==0); |
| |
| res->hdr.anscount = 2; |
| res->ans[0].type = PJ_DNS_TYPE_CNAME; |
| res->ans[0].dnsclass = 1; |
| res->ans[0].ttl = 1000; /* resolver should select minimum TTL */ |
| res->ans[0].name = res->q[0].name; |
| res->ans[0].rdata.cname.name = pj_str(alias); |
| |
| res->ans[1].type = PJ_DNS_TYPE_A; |
| res->ans[1].dnsclass = 1; |
| res->ans[1].ttl = 1; |
| res->ans[1].name = pj_str(alias); |
| res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR1; |
| } |
| |
| *p_res = res; |
| } |
| |
| static void srv_cb_1(void *user_data, |
| pj_status_t status, |
| const pj_dns_srv_record *rec) |
| { |
| PJ_UNUSED_ARG(user_data); |
| |
| pj_sem_post(sem); |
| |
| PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return); |
| PJ_ASSERT_ON_FAIL(rec->count == 1, return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 1, return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 2, return); |
| PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, "sip.somedomain.com")==0, |
| return); |
| PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias.somedomain.com")==0, |
| return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].s_addr == IP_ADDR1, return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT1, return); |
| |
| |
| } |
| |
| static void srv_cb_1b(void *user_data, |
| pj_status_t status, |
| const pj_dns_srv_record *rec) |
| { |
| PJ_UNUSED_ARG(user_data); |
| |
| pj_sem_post(sem); |
| |
| PJ_ASSERT_ON_FAIL(status==PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_RCODE_NXDOMAIN), |
| return); |
| PJ_ASSERT_ON_FAIL(rec->count == 0, return); |
| } |
| |
| static int srv_resolver_test(void) |
| { |
| pj_status_t status; |
| pj_str_t domain = pj_str("somedomain.com"); |
| pj_str_t res_name = pj_str("_sip._udp."); |
| |
| /* Successful scenario */ |
| PJ_LOG(3,(THIS_FILE, " srv_resolve(): success scenario")); |
| |
| g_server[0].action = ACTION_CB; |
| g_server[0].action_cb = &action1_1; |
| g_server[1].action = ACTION_CB; |
| g_server[1].action_cb = &action1_1; |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, |
| NULL, &srv_cb_1, NULL); |
| pj_assert(status == PJ_SUCCESS); |
| |
| pj_sem_wait(sem); |
| |
| /* Because of previous tests, only NS 1 should get the request */ |
| pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */ |
| pj_assert(g_server[1].pkt_count == 0); |
| |
| |
| /* Wait until cache expires and nameserver state moves out from STATE_PROBING */ |
| PJ_LOG(3,(THIS_FILE, " waiting for cache to expire (~15 secs)..")); |
| pj_thread_sleep(1000 + |
| ((set.qretr_count + 2) * set.qretr_delay)); |
| |
| /* Successful scenario */ |
| PJ_LOG(3,(THIS_FILE, " srv_resolve(): parallel queries")); |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, |
| NULL, &srv_cb_1, NULL); |
| pj_assert(status == PJ_SUCCESS); |
| |
| |
| status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, |
| NULL, &srv_cb_1, NULL); |
| pj_assert(status == PJ_SUCCESS); |
| |
| pj_sem_wait(sem); |
| pj_sem_wait(sem); |
| |
| /* Only server one should get a query */ |
| pj_assert(g_server[0].pkt_count == 2); /* 2 because of SRV and A resolution */ |
| pj_assert(g_server[1].pkt_count == 0); |
| |
| /* Since TTL is one, subsequent queries should fail */ |
| PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache expires scenario")); |
| |
| |
| pj_thread_sleep(1000); |
| |
| g_server[0].action = PJ_DNS_RCODE_NXDOMAIN; |
| g_server[1].action = PJ_DNS_RCODE_NXDOMAIN; |
| |
| status = pj_dns_srv_resolve(&domain, &res_name, 5061, pool, resolver, PJ_TRUE, |
| NULL, &srv_cb_1b, NULL); |
| pj_assert(status == PJ_SUCCESS); |
| |
| pj_sem_wait(sem); |
| |
| return 0; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /* Fallback because there's no SRV in answer */ |
| #define TARGET "domain2.com" |
| #define IP_ADDR2 0x02030405 |
| #define PORT2 50062 |
| |
| static void action2_1(const pj_dns_parsed_packet *pkt, |
| pj_dns_parsed_packet **p_res) |
| { |
| pj_dns_parsed_packet *res; |
| |
| res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); |
| |
| res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); |
| res->ans = (pj_dns_parsed_rr*) |
| pj_pool_calloc(pool, 4, sizeof(pj_dns_parsed_rr)); |
| |
| res->hdr.qdcount = 1; |
| res->q[0].type = pkt->q[0].type; |
| res->q[0].dnsclass = pkt->q[0].dnsclass; |
| res->q[0].name = pkt->q[0].name; |
| |
| if (pkt->q[0].type == PJ_DNS_TYPE_SRV) { |
| |
| pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp." TARGET)==0); |
| |
| res->hdr.anscount = 1; |
| res->ans[0].type = PJ_DNS_TYPE_A; // <-- this will cause the fallback |
| res->ans[0].dnsclass = 1; |
| res->ans[0].name = res->q[0].name; |
| res->ans[0].ttl = 1; |
| res->ans[0].rdata.srv.prio = 1; |
| res->ans[0].rdata.srv.weight = 2; |
| res->ans[0].rdata.srv.port = PORT2; |
| res->ans[0].rdata.srv.target = pj_str("sip01." TARGET); |
| |
| } else if (pkt->q[0].type == PJ_DNS_TYPE_A) { |
| char *alias = "sipalias01." TARGET; |
| |
| pj_assert(pj_strcmp2(&res->q[0].name, TARGET)==0); |
| |
| res->hdr.anscount = 2; |
| res->ans[0].type = PJ_DNS_TYPE_CNAME; |
| res->ans[0].dnsclass = 1; |
| res->ans[0].name = res->q[0].name; |
| res->ans[0].ttl = 1; |
| res->ans[0].rdata.cname.name = pj_str(alias); |
| |
| res->ans[1].type = PJ_DNS_TYPE_A; |
| res->ans[1].dnsclass = 1; |
| res->ans[1].name = pj_str(alias); |
| res->ans[1].ttl = 1; |
| res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR2; |
| } |
| |
| *p_res = res; |
| } |
| |
| static void srv_cb_2(void *user_data, |
| pj_status_t status, |
| const pj_dns_srv_record *rec) |
| { |
| PJ_UNUSED_ARG(user_data); |
| |
| pj_sem_post(sem); |
| |
| PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return); |
| PJ_ASSERT_ON_FAIL(rec->count == 1, return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].priority == 0, return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].weight == 0, return); |
| PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.name, TARGET)==0, |
| return); |
| PJ_ASSERT_ON_FAIL(pj_strcmp2(&rec->entry[0].server.alias, "sipalias01." TARGET)==0, |
| return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].s_addr == IP_ADDR2, return); |
| PJ_ASSERT_ON_FAIL(rec->entry[0].port == PORT2, return); |
| } |
| |
| static int srv_resolver_fallback_test(void) |
| { |
| pj_status_t status; |
| pj_str_t domain = pj_str(TARGET); |
| pj_str_t res_name = pj_str("_sip._udp."); |
| |
| PJ_LOG(3,(THIS_FILE, " srv_resolve(): fallback test")); |
| |
| g_server[0].action = ACTION_CB; |
| g_server[0].action_cb = &action2_1; |
| g_server[1].action = ACTION_CB; |
| g_server[1].action_cb = &action2_1; |
| |
| status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE, |
| NULL, &srv_cb_2, NULL); |
| if (status != PJ_SUCCESS) { |
| app_perror(" srv_resolve error", status); |
| pj_assert(status == PJ_SUCCESS); |
| } |
| |
| pj_sem_wait(sem); |
| |
| /* Subsequent query should just get the response from the cache */ |
| PJ_LOG(3,(THIS_FILE, " srv_resolve(): cache test")); |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| status = pj_dns_srv_resolve(&domain, &res_name, PORT2, pool, resolver, PJ_TRUE, |
| NULL, &srv_cb_2, NULL); |
| if (status != PJ_SUCCESS) { |
| app_perror(" srv_resolve error", status); |
| pj_assert(status == PJ_SUCCESS); |
| } |
| |
| pj_sem_wait(sem); |
| |
| pj_assert(g_server[0].pkt_count == 0); |
| pj_assert(g_server[1].pkt_count == 0); |
| |
| return 0; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| /* Too many SRV or A entries */ |
| #define DOMAIN3 "d3" |
| #define SRV_COUNT3 (PJ_DNS_SRV_MAX_ADDR+1) |
| #define A_COUNT3 (PJ_DNS_MAX_IP_IN_A_REC+1) |
| #define PORT3 50063 |
| #define IP_ADDR3 0x03030303 |
| |
| static void action3_1(const pj_dns_parsed_packet *pkt, |
| pj_dns_parsed_packet **p_res) |
| { |
| pj_dns_parsed_packet *res; |
| unsigned i; |
| |
| res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet); |
| |
| if (res->q == NULL) { |
| res->q = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_query); |
| } |
| |
| res->hdr.qdcount = 1; |
| res->q[0].type = pkt->q[0].type; |
| res->q[0].dnsclass = pkt->q[0].dnsclass; |
| res->q[0].name = pkt->q[0].name; |
| |
| if (pkt->q[0].type == PJ_DNS_TYPE_SRV) { |
| |
| pj_assert(pj_strcmp2(&pkt->q[0].name, "_sip._udp." DOMAIN3)==0); |
| |
| res->hdr.anscount = SRV_COUNT3; |
| res->ans = (pj_dns_parsed_rr*) |
| pj_pool_calloc(pool, SRV_COUNT3, sizeof(pj_dns_parsed_rr)); |
| |
| for (i=0; i<SRV_COUNT3; ++i) { |
| char *target; |
| |
| res->ans[i].type = PJ_DNS_TYPE_SRV; |
| res->ans[i].dnsclass = 1; |
| res->ans[i].name = res->q[0].name; |
| res->ans[i].ttl = 1; |
| res->ans[i].rdata.srv.prio = (pj_uint16_t)i; |
| res->ans[i].rdata.srv.weight = 2; |
| res->ans[i].rdata.srv.port = (pj_uint16_t)(PORT3+i); |
| |
| target = (char*)pj_pool_alloc(pool, 16); |
| sprintf(target, "sip%02d." DOMAIN3, i); |
| res->ans[i].rdata.srv.target = pj_str(target); |
| } |
| |
| } else if (pkt->q[0].type == PJ_DNS_TYPE_A) { |
| |
| //pj_assert(pj_strcmp2(&res->q[0].name, "sip." DOMAIN3)==0); |
| |
| res->hdr.anscount = A_COUNT3; |
| res->ans = (pj_dns_parsed_rr*) |
| pj_pool_calloc(pool, A_COUNT3, sizeof(pj_dns_parsed_rr)); |
| |
| for (i=0; i<A_COUNT3; ++i) { |
| res->ans[i].type = PJ_DNS_TYPE_A; |
| res->ans[i].dnsclass = 1; |
| res->ans[i].ttl = 1; |
| res->ans[i].name = res->q[0].name; |
| res->ans[i].rdata.a.ip_addr.s_addr = IP_ADDR3+i; |
| } |
| } |
| |
| *p_res = res; |
| } |
| |
| static void srv_cb_3(void *user_data, |
| pj_status_t status, |
| const pj_dns_srv_record *rec) |
| { |
| unsigned i; |
| |
| PJ_UNUSED_ARG(user_data); |
| PJ_UNUSED_ARG(status); |
| PJ_UNUSED_ARG(rec); |
| |
| pj_assert(status == PJ_SUCCESS); |
| pj_assert(rec->count == PJ_DNS_SRV_MAX_ADDR); |
| |
| for (i=0; i<PJ_DNS_SRV_MAX_ADDR; ++i) { |
| unsigned j; |
| |
| pj_assert(rec->entry[i].priority == i); |
| pj_assert(rec->entry[i].weight == 2); |
| //pj_assert(pj_strcmp2(&rec->entry[i].server.name, "sip." DOMAIN3)==0); |
| pj_assert(rec->entry[i].server.alias.slen == 0); |
| pj_assert(rec->entry[i].port == PORT3+i); |
| |
| pj_assert(rec->entry[i].server.addr_count == PJ_DNS_MAX_IP_IN_A_REC); |
| |
| for (j=0; j<PJ_DNS_MAX_IP_IN_A_REC; ++j) { |
| pj_assert(rec->entry[i].server.addr[j].s_addr == IP_ADDR3+j); |
| } |
| } |
| |
| pj_sem_post(sem); |
| } |
| |
| static int srv_resolver_many_test(void) |
| { |
| pj_status_t status; |
| pj_str_t domain = pj_str(DOMAIN3); |
| pj_str_t res_name = pj_str("_sip._udp."); |
| |
| /* Successful scenario */ |
| PJ_LOG(3,(THIS_FILE, " srv_resolve(): too many entries test")); |
| |
| g_server[0].action = ACTION_CB; |
| g_server[0].action_cb = &action3_1; |
| g_server[1].action = ACTION_CB; |
| g_server[1].action_cb = &action3_1; |
| |
| g_server[0].pkt_count = 0; |
| g_server[1].pkt_count = 0; |
| |
| status = pj_dns_srv_resolve(&domain, &res_name, 1, pool, resolver, PJ_TRUE, |
| NULL, &srv_cb_3, NULL); |
| pj_assert(status == PJ_SUCCESS); |
| |
| pj_sem_wait(sem); |
| |
| return 0; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| |
| |
| int resolver_test(void) |
| { |
| int rc; |
| |
| rc = init(); |
| if (rc != 0) |
| goto on_error; |
| |
| rc = a_parser_test(); |
| if (rc != 0) |
| goto on_error; |
| |
| rc = simple_test(); |
| if (rc != 0) |
| goto on_error; |
| |
| rc = dns_test(); |
| if (rc != 0) |
| goto on_error; |
| |
| srv_resolver_test(); |
| srv_resolver_fallback_test(); |
| srv_resolver_many_test(); |
| |
| destroy(); |
| return 0; |
| |
| on_error: |
| destroy(); |
| return rc; |
| } |
| |
| |