summaryrefslogtreecommitdiff
path: root/pjlib-util
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2016-06-20 10:10:42 +0000
committerNanang Izzuddin <nanang@teluu.com>2016-06-20 10:10:42 +0000
commitb80242b94843137edd58e9075a892c7971b7bf55 (patch)
tree7162251a3b656d1af18c48b1ce02332a54de351c /pjlib-util
parentbddd40a4e4db55d1c6b046a4fe21b733e76f9fd5 (diff)
Close #1927: IPv6 support in DNS SRV:
- support DNS A and AAAA resolution for each target in DNS SRV record - support fallback to DNS A and DNS AAAA resolution when DNS SRV record is not available - support IPv6 nameservers. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5349 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib-util')
-rw-r--r--pjlib-util/include/pjlib-util/resolver.h56
-rw-r--r--pjlib-util/include/pjlib-util/srv_resolver.h27
-rw-r--r--pjlib-util/src/pjlib-util-test/resolver_test.c529
-rw-r--r--pjlib-util/src/pjlib-util/resolver.c328
-rw-r--r--pjlib-util/src/pjlib-util/srv_resolver.c257
5 files changed, 1062 insertions, 135 deletions
diff --git a/pjlib-util/include/pjlib-util/resolver.h b/pjlib-util/include/pjlib-util/resolver.h
index 0611747b..fa22d046 100644
--- a/pjlib-util/include/pjlib-util/resolver.h
+++ b/pjlib-util/include/pjlib-util/resolver.h
@@ -226,6 +226,47 @@ typedef struct pj_dns_a_record
/**
+ * This structure represents DNS address record, i.e: DNS A and DNS AAAA
+ * records, as the result of parsing DNS response packet using
+ * #pj_dns_parse_addr_response().
+ */
+typedef struct pj_dns_addr_record
+{
+ /** The target name being queried. */
+ pj_str_t name;
+
+ /** If target name corresponds to a CNAME entry, the alias contains
+ * the value of the CNAME entry, otherwise it will be empty.
+ */
+ pj_str_t alias;
+
+ /** Number of IP addresses. */
+ unsigned addr_count;
+
+ /** IP addresses of the host found in the response */
+ struct {
+
+ /** IP address family */
+ int af;
+
+ /** IP address */
+ union {
+ /** IPv4 address */
+ pj_in_addr v4;
+
+ /** IPv6 address */
+ pj_in6_addr v6;
+ } ip;
+
+ } addr[PJ_DNS_MAX_IP_IN_A_REC];
+
+ /** Internal buffer for hostname and alias. */
+ char buf_[128];
+
+} pj_dns_addr_record;
+
+
+/**
* Set default values to the DNS settings.
*
* @param s The DNS settings to be initialized.
@@ -408,6 +449,21 @@ PJ_DECL(pj_status_t) pj_dns_parse_a_response(const pj_dns_parsed_packet *pkt,
/**
+ * A utility function to parse a DNS response containing AAAA records into
+ * DNS AAAA record.
+ *
+ * @param pkt The DNS response packet.
+ * @param rec The structure to be initialized with the parsed
+ * DNS AAAA record from the packet.
+ *
+ * @return PJ_SUCCESS if response can be parsed successfully.
+ */
+PJ_DECL(pj_status_t) pj_dns_parse_addr_response(
+ const pj_dns_parsed_packet *pkt,
+ pj_dns_addr_record *rec);
+
+
+/**
* Put the specified DNS packet into DNS cache. This function is mainly used
* for testing the resolver, however it can also be used to inject entries
* into the resolver.
diff --git a/pjlib-util/include/pjlib-util/srv_resolver.h b/pjlib-util/include/pjlib-util/srv_resolver.h
index 770a2a72..bcaf9843 100644
--- a/pjlib-util/include/pjlib-util/srv_resolver.h
+++ b/pjlib-util/include/pjlib-util/srv_resolver.h
@@ -84,27 +84,34 @@ typedef enum pj_dns_srv_option
* Specify if the resolver should fallback with DNS A
* resolution when the SRV resolution fails. This option may
* be specified together with PJ_DNS_SRV_FALLBACK_AAAA to
- * make the resolver fallback to AAAA if SRV resolution fails,
- * and then to DNS A resolution if the AAAA resolution fails.
+ * make the resolver fallback to both DNS A and DNS AAAA
+ * resolutions if SRV resolution fails.
*/
PJ_DNS_SRV_FALLBACK_A = 1,
/**
* Specify if the resolver should fallback with DNS AAAA
* resolution when the SRV resolution fails. This option may
- * be specified together with PJ_DNS_SRV_FALLBACK_A to
- * make the resolver fallback to AAAA if SRV resolution fails,
- * and then to DNS A resolution if the AAAA resolution fails.
+ * be specified together with PJ_DNS_SRV_FALLBACK_AAAA to
+ * make the resolver fallback to both DNS A and DNS AAAA
+ * resolutions if SRV resolution fails.
*/
PJ_DNS_SRV_FALLBACK_AAAA = 2,
/**
* Specify if the resolver should try to resolve with DNS AAAA
- * resolution first of each targets in the DNS SRV record. If
- * this option is not specified, the SRV resolver will query
- * the DNS A record for the target instead.
+ * resolution of each targets in the DNS SRV record. If this
+ * option is not specified, the SRV resolver will query the
+ * DNS A record for the target instead.
+ */
+ PJ_DNS_SRV_RESOLVE_AAAA = 4,
+
+ /**
+ * Specify if the resolver should try to resolve with DNS AAAA
+ * resolution only (i.e: without DNS A resolution) for each targets
+ * in the DNS SRV record.
*/
- PJ_DNS_SRV_RESOLVE_AAAA = 4
+ PJ_DNS_SRV_RESOLVE_AAAA_ONLY = 8
} pj_dns_srv_option;
@@ -131,7 +138,7 @@ typedef struct pj_dns_srv_record
pj_uint16_t port;
/** The host address. */
- pj_dns_a_record server;
+ pj_dns_addr_record server;
} entry[PJ_DNS_SRV_MAX_ADDR];
diff --git a/pjlib-util/src/pjlib-util-test/resolver_test.c b/pjlib-util/src/pjlib-util-test/resolver_test.c
index e68efe4f..9feae8d0 100644
--- a/pjlib-util/src/pjlib-util-test/resolver_test.c
+++ b/pjlib-util/src/pjlib-util-test/resolver_test.c
@@ -191,6 +191,20 @@ static int print_rr(pj_uint8_t *pkt, int size, pj_uint8_t *pos,
p += 6;
size -= 6;
+ } else if (rr->type == PJ_DNS_TYPE_AAAA) {
+
+ if (size < 18)
+ return -1;
+
+ /* RDLEN is 16 */
+ write16(p, 16);
+
+ /* Address */
+ pj_memcpy(p+2, &rr->rdata.aaaa.ip_addr, 16);
+
+ p += 18;
+ size -= 18;
+
} else if (rr->type == PJ_DNS_TYPE_CNAME ||
rr->type == PJ_DNS_TYPE_NS ||
rr->type == PJ_DNS_TYPE_PTR) {
@@ -330,7 +344,7 @@ static int server_thread(void *p)
while (!thread_quit) {
pj_fd_set_t rset;
pj_time_val timeout = {0, 500};
- pj_sockaddr_in src_addr;
+ pj_sockaddr src_addr;
pj_dns_parsed_packet *req;
char pkt[1024];
pj_ssize_t pkt_len;
@@ -405,16 +419,21 @@ static int poll_worker_thread(void *p)
static void destroy(void);
-static int init(void)
+static int init(pj_bool_t use_ipv6)
{
pj_status_t status;
pj_str_t nameservers[2];
pj_uint16_t ports[2];
int i;
- nameservers[0] = pj_str("127.0.0.1");
+ if (use_ipv6) {
+ nameservers[0] = pj_str("::1");
+ nameservers[1] = pj_str("::1");
+ } else {
+ nameservers[0] = pj_str("127.0.0.1");
+ nameservers[1] = 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];
@@ -425,14 +444,18 @@ static int init(void)
status = pj_sem_create(pool, NULL, 0, 2, &sem);
pj_assert(status == PJ_SUCCESS);
+ thread_quit = PJ_FALSE;
+
for (i=0; i<2; ++i) {
- pj_sockaddr_in addr;
+ pj_sockaddr addr;
- status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &g_server[i].sock);
+ status = pj_sock_socket((use_ipv6? pj_AF_INET6() : 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);
+ pj_sockaddr_init((use_ipv6? pj_AF_INET6() : pj_AF_INET()),
+ &addr, NULL, (pj_uint16_t)g_server[i].port);
status = pj_sock_bind(g_server[i].sock, &addr, sizeof(addr));
if (status != PJ_SUCCESS)
@@ -693,6 +716,215 @@ static int a_parser_test(void)
////////////////////////////////////////////////////////////////////////////
+/* DNS A/AAAA parser tests */
+static int addr_parser_test(void)
+{
+ pj_dns_parsed_packet pkt;
+ pj_dns_addr_record rec;
+ pj_status_t rc;
+
+ PJ_LOG(3,(THIS_FILE, " DNS A/AAAA 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 = 4;
+
+ /* 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;
+
+ /* Additional RR corresponding to the query, DNS AAAA RR */
+ pkt.ans[3].name = pj_str("ahost");
+ pkt.ans[3].type = PJ_DNS_TYPE_AAAA;
+ pkt.ans[3].dnsclass = 1;
+ pkt.ans[3].ttl = 1;
+ pkt.ans[3].rdata.aaaa.ip_addr.u6_addr32[0] = 0x01020304;
+
+
+ rc = pj_dns_parse_addr_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 == 2);
+ pj_assert(rec.addr[0].af==pj_AF_INET() && rec.addr[0].ip.v4.s_addr == 0x01020304);
+ pj_assert(rec.addr[1].af==pj_AF_INET6() && rec.addr[1].ip.v6.u6_addr32[0] == 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_addr_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].ip.v4.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_addr_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_addr_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_addr_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_addr_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_addr_response(&pkt, &rec);
+ pj_assert(rc == PJLIB_UTIL_EDNSNOANSWERREC);
+
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
/* Simple DNS test */
#define IP_ADDR0 0x00010203
@@ -1005,6 +1237,27 @@ static void action1_1(const pj_dns_parsed_packet *pkt,
res->ans[1].ttl = 1;
res->ans[1].name = pj_str(alias);
res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR1;
+
+ } else if (pkt->q[0].type == PJ_DNS_TYPE_AAAA) {
+ 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_AAAA;
+ res->ans[1].dnsclass = 1;
+ res->ans[1].ttl = 1;
+ res->ans[1].name = pj_str(alias);
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[0] = IP_ADDR1;
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[1] = IP_ADDR1;
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[2] = IP_ADDR1;
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[3] = IP_ADDR1;
}
*p_res = res;
@@ -1026,12 +1279,16 @@ static void srv_cb_1(void *user_data,
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);
+
+ /* IPv4 only */
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].ip.v4.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)
@@ -1045,6 +1302,61 @@ static void srv_cb_1b(void *user_data,
PJ_ASSERT_ON_FAIL(rec->count == 0, return);
}
+
+static void srv_cb_1c(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].port == PORT1, return);
+
+ /* IPv4 and IPv6 */
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 2, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET() &&
+ rec->entry[0].server.addr[0].ip.v4.s_addr == IP_ADDR1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[1].af == pj_AF_INET6() &&
+ rec->entry[0].server.addr[1].ip.v6.u6_addr32[0] == IP_ADDR1, return);
+}
+
+
+static void srv_cb_1d(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].port == PORT1, return);
+
+ /* IPv6 only */
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET6() &&
+ rec->entry[0].server.addr[0].ip.v6.u6_addr32[0] == IP_ADDR1, return);
+}
+
+
static int srv_resolver_test(void)
{
pj_status_t status;
@@ -1078,6 +1390,48 @@ static int srv_resolver_test(void)
pj_thread_sleep(1000 +
((set.qretr_count + 2) * set.qretr_delay));
+
+ /* DNS SRV option PJ_DNS_SRV_RESOLVE_AAAA */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): option PJ_DNS_SRV_RESOLVE_AAAA"));
+
+ 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_DNS_SRV_RESOLVE_AAAA,
+ NULL, &srv_cb_1c, NULL);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_sem_wait(sem);
+
+ pj_thread_sleep(1000);
+
+ /* DNS SRV option PJ_DNS_SRV_RESOLVE_AAAA_ONLY */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): option PJ_DNS_SRV_RESOLVE_AAAA_ONLY"));
+
+ 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_DNS_SRV_RESOLVE_AAAA_ONLY,
+ NULL, &srv_cb_1d, NULL);
+ pj_assert(status == PJ_SUCCESS);
+
+ pj_sem_wait(sem);
+
+ pj_thread_sleep(1000);
+
+
/* Successful scenario */
PJ_LOG(3,(THIS_FILE, " srv_resolve(): parallel queries"));
g_server[0].pkt_count = 0;
@@ -1102,7 +1456,6 @@ static int srv_resolver_test(void)
/* 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;
@@ -1114,6 +1467,7 @@ static int srv_resolver_test(void)
pj_sem_wait(sem);
+
return 0;
}
@@ -1171,6 +1525,27 @@ static void action2_1(const pj_dns_parsed_packet *pkt,
res->ans[1].name = pj_str(alias);
res->ans[1].ttl = 1;
res->ans[1].rdata.a.ip_addr.s_addr = IP_ADDR2;
+
+ } else if (pkt->q[0].type == PJ_DNS_TYPE_AAAA) {
+ 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_AAAA;
+ res->ans[1].dnsclass = 1;
+ res->ans[1].ttl = 1;
+ res->ans[1].name = pj_str(alias);
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[0] = IP_ADDR2;
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[1] = IP_ADDR2;
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[2] = IP_ADDR2;
+ res->ans[1].rdata.aaaa.ip_addr.u6_addr32[3] = IP_ADDR2;
}
*p_res = res;
@@ -1192,8 +1567,62 @@ static void srv_cb_2(void *user_data,
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);
+
+ /* IPv4 only */
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET() &&
+ rec->entry[0].server.addr[0].ip.v4.s_addr == IP_ADDR2, return);
+}
+
+static void srv_cb_2a(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].port == PORT2, return);
+
+ /* IPv4 and IPv6 */
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 2, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET() &&
+ rec->entry[0].server.addr[0].ip.v4.s_addr == IP_ADDR2, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[1].af == pj_AF_INET6() &&
+ rec->entry[0].server.addr[1].ip.v6.u6_addr32[0] == IP_ADDR2, return);
+}
+
+static void srv_cb_2b(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].port == PORT2, return);
+
+ /* IPv6 only */
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr_count == 1, return);
+ PJ_ASSERT_ON_FAIL(rec->entry[0].server.addr[0].af == pj_AF_INET6() &&
+ rec->entry[0].server.addr[0].ip.v6.u6_addr32[0] == IP_ADDR2, return);
}
static int srv_resolver_fallback_test(void)
@@ -1202,6 +1631,7 @@ static int srv_resolver_fallback_test(void)
pj_str_t domain = pj_str(TARGET);
pj_str_t res_name = pj_str("_sip._udp.");
+ /* Fallback test */
PJ_LOG(3,(THIS_FILE, " srv_resolve(): fallback test"));
g_server[0].action = ACTION_CB;
@@ -1235,6 +1665,51 @@ static int srv_resolver_fallback_test(void)
pj_assert(g_server[0].pkt_count == 0);
pj_assert(g_server[1].pkt_count == 0);
+ /* Clear cache */
+ pj_thread_sleep(1000);
+
+ /* Fallback with PJ_DNS_SRV_FALLBACK_A and PJ_DNS_SRV_FALLBACK_AAAA */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): fallback to DNS A and AAAA"));
+
+ 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_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA,
+ NULL, &srv_cb_2a, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror(" srv_resolve error", status);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ pj_sem_wait(sem);
+
+ /* Clear cache */
+ pj_thread_sleep(1000);
+
+ /* Fallback with PJ_DNS_SRV_FALLBACK_AAAA only */
+ PJ_LOG(3,(THIS_FILE, " srv_resolve(): fallback to DNS AAAA only"));
+
+ 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_DNS_SRV_FALLBACK_AAAA,
+ NULL, &srv_cb_2b, NULL);
+ if (status != PJ_SUCCESS) {
+ app_perror(" srv_resolve error", status);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ pj_sem_wait(sem);
+
+ /* Clear cache */
+ pj_thread_sleep(1000);
+
return 0;
}
@@ -1333,7 +1808,7 @@ static void srv_cb_3(void *user_data,
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_assert(rec->entry[i].server.addr[j].ip.v4.s_addr == IP_ADDR3+j);
}
}
@@ -1374,7 +1849,7 @@ int resolver_test(void)
{
int rc;
- rc = init();
+ rc = init(PJ_FALSE);
if (rc != 0)
goto on_error;
@@ -1382,6 +1857,10 @@ int resolver_test(void)
if (rc != 0)
goto on_error;
+ rc = addr_parser_test();
+ if (rc != 0)
+ goto on_error;
+
rc = simple_test();
if (rc != 0)
goto on_error;
@@ -1395,6 +1874,31 @@ int resolver_test(void)
srv_resolver_many_test();
destroy();
+
+
+#if PJ_HAS_IPV6
+ /* Similar tests using IPv6 socket and without parser tests */
+ PJ_LOG(3,(THIS_FILE, " Re-run DNS resolution tests using IPv6 socket"));
+
+ rc = init(PJ_TRUE);
+ 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();
+#endif
+
return 0;
on_error:
@@ -1402,4 +1906,3 @@ on_error:
return rc;
}
-
diff --git a/pjlib-util/src/pjlib-util/resolver.c b/pjlib-util/src/pjlib-util/resolver.c
index 6d7a5903..202ad824 100644
--- a/pjlib-util/src/pjlib-util/resolver.c
+++ b/pjlib-util/src/pjlib-util/resolver.c
@@ -79,7 +79,7 @@ static const char *state_names[3] =
*/
struct nameserver
{
- pj_sockaddr_in addr; /**< Server address. */
+ pj_sockaddr addr; /**< Server address. */
enum ns_state state; /**< Nameserver state. */
pj_time_val state_expiry; /**< Time set next state. */
@@ -179,13 +179,24 @@ struct pj_dns_resolver
pj_sock_t udp_sock; /**< UDP socket. */
pj_ioqueue_key_t *udp_key; /**< UDP socket ioqueue key. */
unsigned char udp_rx_pkt[UDPSZ];/**< UDP receive buffer. */
- unsigned char udp_tx_pkt[UDPSZ];/**< UDP receive buffer. */
- pj_ssize_t udp_len; /**< Length of received packet. */
+ unsigned char udp_tx_pkt[UDPSZ];/**< UDP transmit buffer. */
pj_ioqueue_op_key_t udp_op_rx_key; /**< UDP read operation key. */
pj_ioqueue_op_key_t udp_op_tx_key; /**< UDP write operation key. */
- pj_sockaddr_in udp_src_addr; /**< Source address of packet */
+ pj_sockaddr udp_src_addr; /**< Source address of packet */
int udp_addr_len; /**< Source address length. */
+#if PJ_HAS_IPV6
+ /* IPv6 socket */
+ pj_sock_t udp6_sock; /**< UDP socket. */
+ pj_ioqueue_key_t *udp6_key; /**< UDP socket ioqueue key. */
+ unsigned char udp6_rx_pkt[UDPSZ];/**< UDP receive buffer. */
+ //unsigned char udp6_tx_pkt[UDPSZ];/**< UDP transmit buffer. */
+ pj_ioqueue_op_key_t udp6_op_rx_key;/**< UDP read operation key. */
+ pj_ioqueue_op_key_t udp6_op_tx_key;/**< UDP write operation key. */
+ pj_sockaddr udp6_src_addr; /**< Source address of packet */
+ int udp6_addr_len; /**< Source address length. */
+#endif
+
/* Settings */
pj_dns_settings settings; /**< Resolver settings. */
@@ -237,6 +248,17 @@ static void close_sock(pj_dns_resolver *resv)
pj_sock_close(resv->udp_sock);
resv->udp_sock = PJ_INVALID_SOCKET;
}
+
+#if PJ_HAS_IPV6
+ if (resv->udp6_key != NULL) {
+ pj_ioqueue_unregister(resv->udp6_key);
+ resv->udp6_key = NULL;
+ resv->udp6_sock = PJ_INVALID_SOCKET;
+ } else if (resv->udp6_sock != PJ_INVALID_SOCKET) {
+ pj_sock_close(resv->udp6_sock);
+ resv->udp6_sock = PJ_INVALID_SOCKET;
+ }
+#endif
}
@@ -244,6 +266,8 @@ static void close_sock(pj_dns_resolver *resv)
static pj_status_t init_sock(pj_dns_resolver *resv)
{
pj_ioqueue_callback socket_cb;
+ pj_sockaddr bound_addr;
+ pj_ssize_t rx_pkt_size;
pj_status_t status;
/* Create the UDP socket */
@@ -269,15 +293,59 @@ static pj_status_t init_sock(pj_dns_resolver *resv)
pj_ioqueue_op_key_init(&resv->udp_op_tx_key, sizeof(resv->udp_op_tx_key));
/* Start asynchronous read to the UDP socket */
- resv->udp_len = sizeof(resv->udp_rx_pkt);
+ rx_pkt_size = sizeof(resv->udp_rx_pkt);
resv->udp_addr_len = sizeof(resv->udp_src_addr);
status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_rx_key,
- resv->udp_rx_pkt, &resv->udp_len,
+ resv->udp_rx_pkt, &rx_pkt_size,
PJ_IOQUEUE_ALWAYS_ASYNC,
&resv->udp_src_addr, &resv->udp_addr_len);
if (status != PJ_EPENDING)
return status;
+
+#if PJ_HAS_IPV6
+ /* Also setup IPv6 socket */
+
+ /* Create the UDP socket */
+ status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0,
+ &resv->udp6_sock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Bind to any address/port */
+ pj_sockaddr_init(pj_AF_INET6(), &bound_addr, NULL, 0);
+ status = pj_sock_bind(resv->udp6_sock, &bound_addr,
+ pj_sockaddr_get_len(&bound_addr));
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Register to ioqueue */
+ pj_bzero(&socket_cb, sizeof(socket_cb));
+ socket_cb.on_read_complete = &on_read_complete;
+ status = pj_ioqueue_register_sock(resv->pool, resv->ioqueue,
+ resv->udp6_sock, resv, &socket_cb,
+ &resv->udp6_key);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_ioqueue_op_key_init(&resv->udp6_op_rx_key,
+ sizeof(resv->udp6_op_rx_key));
+ pj_ioqueue_op_key_init(&resv->udp6_op_tx_key,
+ sizeof(resv->udp6_op_tx_key));
+
+ /* Start asynchronous read to the UDP socket */
+ rx_pkt_size = sizeof(resv->udp6_rx_pkt);
+ resv->udp6_addr_len = sizeof(resv->udp6_src_addr);
+ status = pj_ioqueue_recvfrom(resv->udp6_key, &resv->udp6_op_rx_key,
+ resv->udp6_rx_pkt, &rx_pkt_size,
+ PJ_IOQUEUE_ALWAYS_ASYNC,
+ &resv->udp6_src_addr, &resv->udp6_addr_len);
+ if (status != PJ_EPENDING)
+ return status;
+#else
+ PJ_UNUSED_ARG(bound_addr);
+#endif
+
return PJ_SUCCESS;
}
@@ -474,8 +542,11 @@ PJ_DEF(pj_status_t) pj_dns_resolver_set_ns( pj_dns_resolver *resolver,
for (i=0; i<count; ++i) {
struct nameserver *ns = &resolver->ns[i];
- status = pj_sockaddr_in_init(&ns->addr, &servers[i],
- (pj_uint16_t)(ports ? ports[i] : PORT));
+ status = pj_sockaddr_init(pj_AF_INET(), &ns->addr, &servers[i],
+ (pj_uint16_t)(ports ? ports[i] : PORT));
+ if (status != PJ_SUCCESS)
+ status = pj_sockaddr_init(pj_AF_INET6(), &ns->addr, &servers[i],
+ (pj_uint16_t)(ports ? ports[i] : PORT));
if (status != PJ_SUCCESS) {
pj_mutex_unlock(resolver->mutex);
return PJLIB_UTIL_EDNSINNSADDR;
@@ -612,7 +683,13 @@ static pj_status_t transmit_query(pj_dns_resolver *resolver,
}
/* Check if the socket is available for sending */
- if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)) {
+ if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)
+#if PJ_HAS_IPV6
+ || pj_ioqueue_is_pending(resolver->udp6_key,
+ &resolver->udp6_op_tx_key)
+#endif
+ )
+ {
++q->transmit_cnt;
PJ_LOG(4,(resolver->name.ptr,
"Socket busy in transmitting DNS %s query for %s%s",
@@ -642,19 +719,29 @@ static pj_status_t transmit_query(pj_dns_resolver *resolver,
pj_ssize_t sent = (pj_ssize_t) pkt_size;
struct nameserver *ns = &resolver->ns[servers[i]];
- status = pj_ioqueue_sendto(resolver->udp_key,
- &resolver->udp_op_tx_key,
- resolver->udp_tx_pkt, &sent, 0,
- &resolver->ns[servers[i]].addr,
- sizeof(pj_sockaddr_in));
+ if (ns->addr.addr.sa_family == pj_AF_INET()) {
+ status = pj_ioqueue_sendto(resolver->udp_key,
+ &resolver->udp_op_tx_key,
+ resolver->udp_tx_pkt, &sent, 0,
+ &ns->addr,
+ pj_sockaddr_get_len(&ns->addr));
+ }
+#if PJ_HAS_IPV6
+ else {
+ status = pj_ioqueue_sendto(resolver->udp6_key,
+ &resolver->udp6_op_tx_key,
+ resolver->udp_tx_pkt, &sent, 0,
+ &ns->addr,
+ pj_sockaddr_get_len(&ns->addr));
+ }
+#endif
PJ_PERROR(4,(resolver->name.ptr, status,
"%s %d bytes to NS %d (%s:%d): DNS %s query for %s",
(q->transmit_cnt==0? "Transmitting":"Re-transmitting"),
(int)pkt_size, servers[i],
- pj_inet_ntop2(pj_AF_INET(), &ns->addr.sin_addr, addr,
- sizeof(addr)),
- (int)pj_ntohs(ns->addr.sin_port),
+ pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
+ pj_sockaddr_get_port(&ns->addr),
pj_dns_get_type_name(q->key.qtype),
q->key.name));
@@ -1028,6 +1115,132 @@ PJ_DEF(pj_status_t) pj_dns_parse_a_response(const pj_dns_parsed_packet *pkt,
}
+/*
+ * DNS response containing A and/or AAAA packet.
+ */
+PJ_DEF(pj_status_t) pj_dns_parse_addr_response(
+ const pj_dns_parsed_packet *pkt,
+ pj_dns_addr_record *rec)
+{
+ enum { MAX_SEARCH = 20 };
+ pj_str_t hostname, alias = {NULL, 0}, *resname;
+ pj_size_t bufstart = 0;
+ pj_size_t bufleft;
+ unsigned i, ansidx, cnt=0;
+
+ PJ_ASSERT_RETURN(pkt && rec, PJ_EINVAL);
+
+ /* Init the record */
+ pj_bzero(rec, sizeof(*rec));
+
+ bufleft = sizeof(rec->buf_);
+
+ /* Return error if there's error in the packet. */
+ if (PJ_DNS_GET_RCODE(pkt->hdr.flags))
+ return PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(pkt->hdr.flags));
+
+ /* Return error if there's no query section */
+ if (pkt->hdr.qdcount == 0)
+ return PJLIB_UTIL_EDNSINANSWER;
+
+ /* Return error if there's no answer */
+ if (pkt->hdr.anscount == 0)
+ return PJLIB_UTIL_EDNSNOANSWERREC;
+
+ /* Get the hostname from the query. */
+ hostname = pkt->q[0].name;
+
+ /* Copy hostname to the record */
+ if (hostname.slen > (int)bufleft) {
+ return PJ_ENAMETOOLONG;
+ }
+
+ pj_memcpy(&rec->buf_[bufstart], hostname.ptr, hostname.slen);
+ rec->name.ptr = &rec->buf_[bufstart];
+ rec->name.slen = hostname.slen;
+
+ bufstart += hostname.slen;
+ bufleft -= hostname.slen;
+
+ /* Find the first RR which name matches the hostname. */
+ for (ansidx=0; ansidx < pkt->hdr.anscount; ++ansidx) {
+ if (pj_stricmp(&pkt->ans[ansidx].name, &hostname)==0)
+ break;
+ }
+
+ if (ansidx == pkt->hdr.anscount)
+ return PJLIB_UTIL_EDNSNOANSWERREC;
+
+ resname = &hostname;
+
+ /* Keep following CNAME records. */
+ while (pkt->ans[ansidx].type == PJ_DNS_TYPE_CNAME &&
+ cnt++ < MAX_SEARCH)
+ {
+ resname = &pkt->ans[ansidx].rdata.cname.name;
+
+ if (!alias.slen)
+ alias = *resname;
+
+ for (i=0; i < pkt->hdr.anscount; ++i) {
+ if (pj_stricmp(resname, &pkt->ans[i].name)==0)
+ break;
+ }
+
+ if (i==pkt->hdr.anscount)
+ return PJLIB_UTIL_EDNSNOANSWERREC;
+
+ ansidx = i;
+ }
+
+ if (cnt >= MAX_SEARCH)
+ return PJLIB_UTIL_EDNSINANSWER;
+
+ if (pkt->ans[ansidx].type != PJ_DNS_TYPE_A &&
+ pkt->ans[ansidx].type != PJ_DNS_TYPE_AAAA)
+ {
+ return PJLIB_UTIL_EDNSINANSWER;
+ }
+
+ /* Copy alias to the record, if present. */
+ if (alias.slen) {
+ if (alias.slen > (int)bufleft)
+ return PJ_ENAMETOOLONG;
+
+ pj_memcpy(&rec->buf_[bufstart], alias.ptr, alias.slen);
+ rec->alias.ptr = &rec->buf_[bufstart];
+ rec->alias.slen = alias.slen;
+
+ bufstart += alias.slen;
+ bufleft -= alias.slen;
+ }
+
+ /* Get the IP addresses. */
+ cnt = 0;
+ for (i=0; i < pkt->hdr.anscount && cnt < PJ_DNS_MAX_IP_IN_A_REC ; ++i) {
+ if ((pkt->ans[i].type == PJ_DNS_TYPE_A ||
+ pkt->ans[i].type == PJ_DNS_TYPE_AAAA) &&
+ pj_stricmp(&pkt->ans[i].name, resname)==0)
+ {
+ if (pkt->ans[i].type == PJ_DNS_TYPE_A) {
+ rec->addr[cnt].af = pj_AF_INET();
+ rec->addr[cnt].ip.v4 = pkt->ans[i].rdata.a.ip_addr;
+ } else {
+ rec->addr[cnt].af = pj_AF_INET6();
+ rec->addr[cnt].ip.v6 = pkt->ans[i].rdata.aaaa.ip_addr;
+ }
+ ++cnt;
+ }
+ }
+ rec->addr_count = cnt;
+
+ if (cnt == 0)
+ return PJLIB_UTIL_EDNSNOANSWERREC;
+
+ return PJ_SUCCESS;
+}
+
+
/* Set nameserver state */
static void set_nameserver_state(pj_dns_resolver *resolver,
unsigned index,
@@ -1036,7 +1249,7 @@ static void set_nameserver_state(pj_dns_resolver *resolver,
{
struct nameserver *ns = &resolver->ns[index];
enum ns_state old_state = ns->state;
- char addr[PJ_INET_ADDRSTRLEN];
+ char addr[PJ_INET6_ADDRSTRLEN];
ns->state = state;
ns->state_expiry = *now;
@@ -1050,9 +1263,8 @@ static void set_nameserver_state(pj_dns_resolver *resolver,
ns->state_expiry.sec += resolver->settings.bad_ns_ttl;
PJ_LOG(5, (resolver->name.ptr, "Nameserver %s:%d state changed %s --> %s",
- pj_inet_ntop2(pj_AF_INET(), &ns->addr.sin_addr, addr,
- sizeof(addr)),
- (int)pj_ntohs(ns->addr.sin_port),
+ pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
+ pj_sockaddr_get_port(&ns->addr),
state_names[old_state], state_names[state]));
}
@@ -1131,7 +1343,7 @@ static pj_status_t select_nameservers(pj_dns_resolver *resolver,
/* Update name server status */
static void report_nameserver_status(pj_dns_resolver *resolver,
- const pj_sockaddr_in *ns_addr,
+ const pj_sockaddr *ns_addr,
const pj_dns_parsed_packet *pkt)
{
unsigned i;
@@ -1168,10 +1380,7 @@ static void report_nameserver_status(pj_dns_resolver *resolver,
for (i=0; i<resolver->ns_count; ++i) {
struct nameserver *ns = &resolver->ns[i];
- if (ns->addr.sin_addr.s_addr == ns_addr->sin_addr.s_addr &&
- ns->addr.sin_port == ns_addr->sin_port &&
- ns->addr.sin_family == ns_addr->sin_family)
- {
+ if (pj_sockaddr_cmp(&ns->addr, ns_addr) == 0) {
if (q_id == ns->q_id) {
/* Calculate response time */
pj_time_val rt = now;
@@ -1394,12 +1603,33 @@ static void on_read_complete(pj_ioqueue_key_t *key,
pj_pool_t *pool = NULL;
pj_dns_parsed_packet *dns_pkt;
pj_dns_async_query *q;
- char addr[PJ_INET_ADDRSTRLEN];
+ char addr[PJ_INET6_ADDRSTRLEN];
+ pj_sockaddr *src_addr;
+ int *src_addr_len;
+ unsigned char *rx_pkt;
+ pj_ssize_t rx_pkt_size;
pj_status_t status;
PJ_USE_EXCEPTION;
resolver = (pj_dns_resolver *) pj_ioqueue_get_user_data(key);
+ pj_assert(resolver);
+
+#if PJ_HAS_IPV6
+ if (key == resolver->udp6_key) {
+ src_addr = &resolver->udp6_src_addr;
+ src_addr_len = &resolver->udp6_addr_len;
+ rx_pkt = resolver->udp6_rx_pkt;
+ rx_pkt_size = sizeof(resolver->udp6_rx_pkt);
+ } else
+#endif
+ {
+ src_addr = &resolver->udp_src_addr;
+ src_addr_len = &resolver->udp_addr_len;
+ rx_pkt = resolver->udp_rx_pkt;
+ rx_pkt_size = sizeof(resolver->udp_rx_pkt);
+ }
+
pj_mutex_lock(resolver->mutex);
@@ -1411,9 +1641,8 @@ static void on_read_complete(pj_ioqueue_key_t *key,
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(resolver->name.ptr,
"DNS resolver read error from %s:%d: %s",
- pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
- addr, sizeof(addr)),
- pj_ntohs(resolver->udp_src_addr.sin_port),
+ pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+ pj_sockaddr_get_port(src_addr),
errmsg));
goto read_next_packet;
@@ -1422,9 +1651,8 @@ static void on_read_complete(pj_ioqueue_key_t *key,
PJ_LOG(5,(resolver->name.ptr,
"Received %d bytes DNS response from %s:%d",
(int)bytes_read,
- pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
- addr, sizeof(addr)),
- pj_ntohs(resolver->udp_src_addr.sin_port)));
+ pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+ pj_sockaddr_get_port(src_addr)));
/* Check for zero packet */
@@ -1439,7 +1667,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
status = -1;
dns_pkt = NULL;
PJ_TRY {
- status = pj_dns_parse_packet(pool, resolver->udp_rx_pkt,
+ status = pj_dns_parse_packet(pool, rx_pkt,
(unsigned)bytes_read, &dns_pkt);
}
PJ_CATCH_ANY {
@@ -1448,7 +1676,7 @@ static void on_read_complete(pj_ioqueue_key_t *key,
PJ_END;
/* Update nameserver status */
- report_nameserver_status(resolver, &resolver->udp_src_addr, dns_pkt);
+ report_nameserver_status(resolver, src_addr, dns_pkt);
/* Handle parse error */
if (status != PJ_SUCCESS) {
@@ -1457,9 +1685,8 @@ static void on_read_complete(pj_ioqueue_key_t *key,
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(3,(resolver->name.ptr,
"Error parsing DNS response from %s:%d: %s",
- pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
- addr, sizeof(addr)),
- pj_ntohs(resolver->udp_src_addr.sin_port),
+ pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+ pj_sockaddr_get_port(src_addr),
errmsg));
goto read_next_packet;
}
@@ -1471,9 +1698,8 @@ static void on_read_complete(pj_ioqueue_key_t *key,
if (!q) {
PJ_LOG(5,(resolver->name.ptr,
"DNS response from %s:%d id=%d discarded",
- pj_inet_ntop2(pj_AF_INET(), &resolver->udp_src_addr.sin_addr,
- addr, sizeof(addr)),
- pj_ntohs(resolver->udp_src_addr.sin_port),
+ pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
+ pj_sockaddr_get_port(src_addr),
(unsigned)dns_pkt->hdr.id));
goto read_next_packet;
}
@@ -1536,13 +1762,11 @@ read_next_packet:
/* needed just in case PJ_HAS_POOL_ALT_API is set */
pj_pool_release(pool);
}
- bytes_read = sizeof(resolver->udp_rx_pkt);
- resolver->udp_addr_len = sizeof(resolver->udp_src_addr);
- status = pj_ioqueue_recvfrom(resolver->udp_key, op_key,
- resolver->udp_rx_pkt,
- &bytes_read, PJ_IOQUEUE_ALWAYS_ASYNC,
- &resolver->udp_src_addr,
- &resolver->udp_addr_len);
+
+ status = pj_ioqueue_recvfrom(key, op_key, rx_pkt, &rx_pkt_size,
+ PJ_IOQUEUE_ALWAYS_ASYNC,
+ src_addr, src_addr_len);
+
if (status != PJ_EPENDING) {
char errmsg[PJ_ERR_MSG_SIZE];
@@ -1642,14 +1866,14 @@ PJ_DEF(void) pj_dns_resolver_dump(pj_dns_resolver *resolver,
PJ_LOG(3,(resolver->name.ptr, " Name servers:"));
for (i=0; i<resolver->ns_count; ++i) {
- char addr[PJ_INET_ADDRSTRLEN];
+ char addr[PJ_INET6_ADDRSTRLEN];
struct nameserver *ns = &resolver->ns[i];
PJ_LOG(3,(resolver->name.ptr,
" NS %d: %s:%d (state=%s until %ds, rtt=%d ms)",
- i, pj_inet_ntop2(pj_AF_INET(), &ns->addr.sin_addr, addr,
- sizeof(addr)),
- pj_ntohs(ns->addr.sin_port),
+ i,
+ pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
+ pj_sockaddr_get_port(&ns->addr),
state_names[ns->state],
ns->state_expiry.sec - now.sec,
PJ_TIME_VAL_MSEC(ns->rt_delay)));
diff --git a/pjlib-util/src/pjlib-util/srv_resolver.c b/pjlib-util/src/pjlib-util/srv_resolver.c
index 6e325b43..339e5de6 100644
--- a/pjlib-util/src/pjlib-util/srv_resolver.c
+++ b/pjlib-util/src/pjlib-util/srv_resolver.c
@@ -37,22 +37,26 @@ struct common
pj_dns_type type; /**< Type of this structure.*/
};
+#pragma pack(1)
struct srv_target
{
struct common common;
+ struct common common_aaaa;
pj_dns_srv_async_query *parent;
pj_str_t target_name;
pj_dns_async_query *q_a;
+ pj_dns_async_query *q_aaaa;
char target_buf[PJ_MAX_HOSTNAME];
pj_str_t cname;
char cname_buf[PJ_MAX_HOSTNAME];
- unsigned port;
+ pj_uint16_t port;
unsigned priority;
unsigned weight;
unsigned sum;
unsigned addr_cnt;
- pj_in_addr addr[ADDR_MAX_COUNT];
+ pj_sockaddr addr[ADDR_MAX_COUNT];/**< Address family and IP.*/
};
+#pragma pack()
struct pj_dns_srv_async_query
{
@@ -135,6 +139,10 @@ PJ_DEF(pj_status_t) pj_dns_srv_resolve( const pj_str_t *domain_name,
query_job->domain_part.slen = target_name.slen - len;
query_job->def_port = (pj_uint16_t)def_port;
+ /* Normalize query job option PJ_DNS_SRV_RESOLVE_AAAA_ONLY */
+ if (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)
+ query_job->option |= PJ_DNS_SRV_RESOLVE_AAAA;
+
/* Start the asynchronous query_job */
query_job->dns_state = PJ_DNS_TYPE_SRV;
@@ -178,6 +186,11 @@ PJ_DEF(pj_status_t) pj_dns_srv_cancel_query(pj_dns_srv_async_query *query,
srv->q_a = NULL;
has_pending = PJ_TRUE;
}
+ if (srv->q_aaaa) {
+ pj_dns_resolver_cancel_query(srv->q_aaaa, PJ_FALSE);
+ srv->q_aaaa = NULL;
+ has_pending = PJ_TRUE;
+ }
}
if (has_pending && notify && query->cb) {
@@ -314,29 +327,56 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
query_job->srv[i].target_name.ptr = query_job->srv[i].target_buf;
}
- /* Check for Additional Info section if A records are available, and
- * fill in the IP address (so that we won't need to resolve the A
+ /* Check for Additional Info section if A/AAAA records are available, and
+ * fill in the IP address (so that we won't need to resolve the A/AAAA
* record with another DNS query_job).
*/
for (i=0; i<response->hdr.arcount; ++i) {
pj_dns_parsed_rr *rr = &response->arr[i];
unsigned j;
- if (rr->type != PJ_DNS_TYPE_A)
+ /* Skip non-A/AAAA record */
+ if (rr->type != PJ_DNS_TYPE_A && rr->type != PJ_DNS_TYPE_AAAA)
+ continue;
+
+ /* Also skip if:
+ * - it is A record and app only want AAAA record, or
+ * - it is AAAA record and app does not want AAAA record
+ */
+ if ((rr->type == PJ_DNS_TYPE_A &&
+ (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)!=0) ||
+ (rr->type == PJ_DNS_TYPE_AAAA &&
+ (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)==0))
+ {
continue;
+ }
- /* Yippeaiyee!! There is an "A" record!
+ /* Yippeaiyee!! There is an "A/AAAA" record!
* Update the IP address of the corresponding SRV record.
*/
for (j=0; j<query_job->srv_cnt; ++j) {
- if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0 &&
- query_job->srv[j].addr_cnt < ADDR_MAX_COUNT)
- {
+ if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0
+ && query_job->srv[j].addr_cnt < ADDR_MAX_COUNT)
+ {
unsigned cnt = query_job->srv[j].addr_cnt;
- query_job->srv[j].addr[cnt].s_addr = rr->rdata.a.ip_addr.s_addr;
+ if (rr->type == PJ_DNS_TYPE_A) {
+ pj_sockaddr_init(pj_AF_INET(),
+ &query_job->srv[j].addr[cnt], NULL,
+ query_job->srv[j].port);
+ query_job->srv[j].addr[cnt].ipv4.sin_addr =
+ rr->rdata.a.ip_addr;
+ } else {
+ pj_sockaddr_init(pj_AF_INET6(),
+ &query_job->srv[j].addr[cnt], NULL,
+ query_job->srv[j].port);
+ query_job->srv[j].addr[cnt].ipv6.sin6_addr =
+ rr->rdata.aaaa.ip_addr;
+ }
+
/* Only increment host_resolved once per SRV record */
if (query_job->srv[j].addr_cnt == 0)
++query_job->host_resolved;
+
++query_job->srv[j].addr_cnt;
break;
}
@@ -354,6 +394,7 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
rr->name.ptr));
}
*/
+
}
/* Rescan again the name specified in the SRV record to see if IP
@@ -362,16 +403,32 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
*/
for (i=0; i<query_job->srv_cnt; ++i) {
pj_in_addr addr;
+ pj_in6_addr addr6;
if (query_job->srv[i].addr_cnt != 0) {
/* IP address already resolved */
continue;
}
- if (pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name,
+ if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)==0 &&
+ pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name,
&addr) == PJ_SUCCESS)
{
- query_job->srv[i].addr[query_job->srv[i].addr_cnt++] = addr;
+ unsigned cnt = query_job->srv[i].addr_cnt;
+ pj_sockaddr_init(pj_AF_INET(), &query_job->srv[i].addr[cnt],
+ NULL, query_job->srv[i].port);
+ query_job->srv[i].addr[cnt].ipv4.sin_addr = addr;
+ ++query_job->srv[i].addr_cnt;
+ ++query_job->host_resolved;
+ } else if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)!=0 &&
+ pj_inet_pton(pj_AF_INET6(), &query_job->srv[i].target_name,
+ &addr6) == PJ_SUCCESS)
+ {
+ unsigned cnt = query_job->srv[i].addr_cnt;
+ pj_sockaddr_init(pj_AF_INET6(), &query_job->srv[i].addr[cnt],
+ NULL, query_job->srv[i].port);
+ query_job->srv[i].addr[cnt].ipv6.sin6_addr = addr6;
+ ++query_job->srv[i].addr_cnt;
++query_job->host_resolved;
}
}
@@ -387,11 +444,11 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
(query_job->srv_cnt ? ':' : ' ')));
for (i=0; i<query_job->srv_cnt; ++i) {
- char addr[PJ_INET_ADDRSTRLEN];
+ char addr[PJ_INET6_ADDRSTRLEN];
if (query_job->srv[i].addr_cnt != 0) {
- pj_inet_ntop(pj_AF_INET(), &query_job->srv[i].addr[0],
- addr, sizeof(addr));
+ pj_sockaddr_print(&query_job->srv[i].addr[0],
+ addr, sizeof(addr), 2);
} else
pj_ansi_strcpy(addr, "-");
@@ -407,13 +464,16 @@ static void build_server_entries(pj_dns_srv_async_query *query_job,
}
-/* Start DNS A record queries for all SRV records in the query_job structure */
+/* Start DNS A and/or AAAA record queries for all SRV records in
+ * the query_job structure.
+ */
static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
{
unsigned i, err_cnt = 0;
pj_status_t err=PJ_SUCCESS, status;
query_job->dns_state = PJ_DNS_TYPE_A;
+
for (i=0; i<query_job->srv_cnt; ++i) {
struct srv_target *srv = &query_job->srv[i];
@@ -423,18 +483,37 @@ static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
srv->target_name.ptr));
srv->common.type = PJ_DNS_TYPE_A;
+ srv->common_aaaa.type = PJ_DNS_TYPE_AAAA;
srv->parent = query_job;
+ status = PJ_SUCCESS;
+
+ /* Start DNA A record query */
+ if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) == 0)
+ {
+ status = pj_dns_resolver_start_query(query_job->resolver,
+ &srv->target_name,
+ PJ_DNS_TYPE_A, 0,
+ &dns_callback,
+ &srv->common, &srv->q_a);
+ }
+
+ /* Start DNA AAAA record query */
+ if (status == PJ_SUCCESS &&
+ (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0)
+ {
+ status = pj_dns_resolver_start_query(query_job->resolver,
+ &srv->target_name,
+ PJ_DNS_TYPE_AAAA, 0,
+ &dns_callback,
+ &srv->common_aaaa, &srv->q_aaaa);
+ }
+
/* See also #1809: dns_callback() will be invoked synchronously when response
* is available in the cache, and var 'query_job->host_resolved' will get
* incremented within the dns_callback(), which will cause this function
* returning false error, so don't use that variable for counting errors.
*/
- status = pj_dns_resolver_start_query(query_job->resolver,
- &srv->target_name,
- PJ_DNS_TYPE_A, 0,
- &dns_callback,
- srv, &srv->q_a);
if (status != PJ_SUCCESS) {
query_job->host_resolved++;
err_cnt++;
@@ -464,6 +543,9 @@ static void dns_callback(void *user_data,
} else if (common->type == PJ_DNS_TYPE_A) {
srv = (struct srv_target*) common;
query_job = srv->parent;
+ } else if (common->type == PJ_DNS_TYPE_AAAA) {
+ srv = (struct srv_target*)((pj_int8_t*)common-sizeof(struct common));
+ query_job = srv->parent;
} else {
pj_assert(!"Unexpected user data!");
return;
@@ -474,6 +556,7 @@ static void dns_callback(void *user_data,
/* We are getting SRV response */
+ /* Clear the outstanding job */
query_job->q_srv = NULL;
if (status == PJ_SUCCESS && pkt->hdr.anscount != 0) {
@@ -507,13 +590,15 @@ static void dns_callback(void *user_data,
* an A record and resolve with DNS A resolution.
*/
if (query_job->srv_cnt == 0) {
+ unsigned new_option = 0;
+
/* Looks like we aren't getting any SRV responses.
* Resolve the original target as A record by creating a
* single "dummy" srv record and start the hostname resolution.
*/
PJ_LOG(4, (query_job->objname,
"DNS SRV resolution failed for %.*s, trying "
- "resolving A record for %.*s",
+ "resolving A/AAAA record for %.*s",
(int)query_job->full_name.slen,
query_job->full_name.ptr,
(int)query_job->domain_part.slen,
@@ -526,11 +611,20 @@ static void dns_callback(void *user_data,
query_job->srv[i].priority = 0;
query_job->srv[i].weight = 0;
query_job->srv[i].port = query_job->def_port;
- }
+
+ /* Update query_job resolution option based on fallback option */
+ if (query_job->option & PJ_DNS_SRV_FALLBACK_AAAA)
+ new_option |= (PJ_DNS_SRV_RESOLVE_AAAA |
+ PJ_DNS_SRV_RESOLVE_AAAA_ONLY);
+ if (query_job->option & PJ_DNS_SRV_FALLBACK_A)
+ new_option &= (~PJ_DNS_SRV_RESOLVE_AAAA_ONLY);
+
+ query_job->option = new_option;
+ }
- /* Resolve server hostnames (DNS A record) for hosts which don't have
- * A record yet.
+ /* Resolve server hostnames (DNS A/AAAA record) for hosts which
+ * don't have A/AAAA record yet.
*/
if (query_job->host_resolved != query_job->srv_cnt) {
status = resolve_hostnames(query_job);
@@ -544,54 +638,83 @@ static void dns_callback(void *user_data,
}
} else if (query_job->dns_state == PJ_DNS_TYPE_A) {
+ pj_bool_t is_type_a, srv_completed;
- /* Clear the outstanding job */
- srv->q_a = NULL;
+ /* Clear outstanding job */
+ if (common->type == PJ_DNS_TYPE_A) {
+ srv_completed = (srv->q_aaaa == NULL);
+ srv->q_a = NULL;
+ } else if (common->type == PJ_DNS_TYPE_AAAA) {
+ srv_completed = (srv->q_a == NULL);
+ srv->q_aaaa = NULL;
+ } else {
+ pj_assert(!"Unexpected job type");
+ query_job->last_error = status = PJ_EINVALIDOP;
+ goto on_error;
+ }
+
+ is_type_a = (common->type == PJ_DNS_TYPE_A);
/* Check that we really have answer */
if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
- char addr[PJ_INET_ADDRSTRLEN];
- pj_dns_a_record rec;
+ char addr[PJ_INET6_ADDRSTRLEN];
+ pj_dns_addr_record rec;
/* Parse response */
- status = pj_dns_parse_a_response(pkt, &rec);
+ status = pj_dns_parse_addr_response(pkt, &rec);
if (status != PJ_SUCCESS)
goto on_error;
pj_assert(rec.addr_count != 0);
/* Update CNAME alias, if present. */
- if (rec.alias.slen) {
+ if (srv->cname.slen==0 && rec.alias.slen) {
pj_assert(rec.alias.slen <= (int)sizeof(srv->cname_buf));
srv->cname.ptr = srv->cname_buf;
pj_strcpy(&srv->cname, &rec.alias);
- } else {
- srv->cname.slen = 0;
+ //} else {
+ //srv->cname.slen = 0;
}
/* Update IP address of the corresponding hostname or CNAME */
- if (srv->addr_cnt < ADDR_MAX_COUNT) {
- srv->addr[srv->addr_cnt++].s_addr = rec.addr[0].s_addr;
-
- PJ_LOG(5,(query_job->objname,
- "DNS A for %.*s: %s",
- (int)srv->target_name.slen,
- srv->target_name.ptr,
- pj_inet_ntop2(pj_AF_INET(), &rec.addr[0],
- addr, sizeof(addr))));
- }
-
- /* Check for multiple IP addresses */
- for (i=1; i<rec.addr_count && srv->addr_cnt < ADDR_MAX_COUNT; ++i)
+ for (i=0; i<rec.addr_count && srv->addr_cnt<ADDR_MAX_COUNT; ++i)
{
- srv->addr[srv->addr_cnt++].s_addr = rec.addr[i].s_addr;
-
- PJ_LOG(5,(query_job->objname,
- "Additional DNS A for %.*s: %s",
- (int)srv->target_name.slen,
- srv->target_name.ptr,
- pj_inet_ntop2(pj_AF_INET(), &rec.addr[i],
- addr, sizeof(addr))));
+ pj_bool_t added = PJ_FALSE;
+
+ if (is_type_a && rec.addr[i].af == pj_AF_INET()) {
+ pj_sockaddr_init(pj_AF_INET(), &srv->addr[srv->addr_cnt],
+ NULL, srv->port);
+ srv->addr[srv->addr_cnt].ipv4.sin_addr =
+ rec.addr[i].ip.v4;
+ added = PJ_TRUE;
+ } else if (!is_type_a && rec.addr[i].af == pj_AF_INET6()) {
+ pj_sockaddr_init(pj_AF_INET6(), &srv->addr[srv->addr_cnt],
+ NULL, srv->port);
+ srv->addr[srv->addr_cnt].ipv6.sin6_addr =
+ rec.addr[i].ip.v6;
+ added = PJ_TRUE;
+ } else {
+ /* Mismatched address family, e.g: getting IPv6 address in
+ * DNS A query resolution.
+ */
+ PJ_LOG(4,(query_job->objname,
+ "Bad address family in DNS %s query for %.*s",
+ (is_type_a? "A" : "AAAA"),
+ (int)srv->target_name.slen,
+ srv->target_name.ptr));
+ }
+
+ if (added) {
+ PJ_LOG(5,(query_job->objname,
+ "DNS %s for %.*s: %s",
+ (is_type_a? "A" : "AAAA"),
+ (int)srv->target_name.slen,
+ srv->target_name.ptr,
+ pj_sockaddr_print(&srv->addr[srv->addr_cnt],
+ addr, sizeof(addr), 2)));
+
+ ++srv->addr_cnt;
+ }
}
} else if (status != PJ_SUCCESS) {
@@ -602,11 +725,17 @@ static void dns_callback(void *user_data,
/* Log error */
pj_strerror(status, errmsg, sizeof(errmsg));
- PJ_LOG(4,(query_job->objname, "DNS A record resolution failed: %s",
+ PJ_LOG(4,(query_job->objname,
+ "DNS %s record resolution failed: %s",
+ (is_type_a? "A" : "AAAA"),
errmsg));
}
- ++query_job->host_resolved;
+ /* Increment host resolved count when both DNS A and AAAA record
+ * queries for this server are completed.
+ */
+ if (srv_completed)
+ ++query_job->host_resolved;
} else {
pj_assert(!"Unexpected state!");
@@ -623,6 +752,7 @@ static void dns_callback(void *user_data,
for (i=0; i<query_job->srv_cnt; ++i) {
unsigned j;
struct srv_target *srv2 = &query_job->srv[i];
+ pj_dns_addr_record *s = &srv_rec.entry[srv_rec.count].server;
srv_rec.entry[srv_rec.count].priority = srv2->priority;
srv_rec.entry[srv_rec.count].weight = srv2->weight;
@@ -634,10 +764,13 @@ static void dns_callback(void *user_data,
pj_assert(srv2->addr_cnt <= PJ_DNS_MAX_IP_IN_A_REC);
- for (j=0; j<srv2->addr_cnt; ++j) {
- srv_rec.entry[srv_rec.count].server.addr[j].s_addr =
- srv2->addr[j].s_addr;
- ++srv_rec.entry[srv_rec.count].server.addr_count;
+ for (j=0; j<srv2->addr_cnt; ++j) {
+ s->addr[j].af = srv2->addr[j].addr.sa_family;
+ if (s->addr[j].af == pj_AF_INET())
+ s->addr[j].ip.v4 = srv2->addr[j].ipv4.sin_addr;
+ else
+ s->addr[j].ip.v6 = srv2->addr[j].ipv6.sin6_addr;
+ ++s->addr_count;
}
if (srv2->addr_cnt > 0) {
@@ -680,6 +813,10 @@ on_error:
query_job->domain_part.ptr,
status,
pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
+
+ /* Cancel any pending query */
+ pj_dns_srv_cancel_query(query_job, PJ_FALSE);
+
(*query_job->cb)(query_job->token, status, NULL);
return;
}