diff options
author | Nanang Izzuddin <nanang@teluu.com> | 2016-06-20 10:10:42 +0000 |
---|---|---|
committer | Nanang Izzuddin <nanang@teluu.com> | 2016-06-20 10:10:42 +0000 |
commit | b80242b94843137edd58e9075a892c7971b7bf55 (patch) | |
tree | 7162251a3b656d1af18c48b1ce02332a54de351c /pjsip | |
parent | bddd40a4e4db55d1c6b046a4fe21b733e76f9fd5 (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 'pjsip')
-rw-r--r-- | pjsip/src/pjsip/sip_resolve.c | 192 |
1 files changed, 151 insertions, 41 deletions
diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index d05a02f2..e97123d6 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -53,6 +53,7 @@ struct query void *token; pjsip_resolver_callback *cb; pj_dns_async_query *object; + pj_dns_async_query *object6; pj_status_t last_error; /* Original request: */ @@ -64,6 +65,9 @@ struct query /* NAPTR records: */ unsigned naptr_cnt; struct naptr_target naptr[8]; + + /* Query result */ + pjsip_server_addresses server; }; @@ -80,6 +84,9 @@ static void srv_resolver_cb(void *user_data, static void dns_a_callback(void *user_data, pj_status_t status, pj_dns_parsed_packet *response); +static void dns_aaaa_callback(void *user_data, + pj_status_t status, + pj_dns_parsed_packet *response); /* @@ -206,7 +213,10 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, /* Is it IP address or hostname? And if it's an IP, which version? */ ip_addr_ver = get_ip_addr_ver(&target->addr.host); - /* Initialize address family type */ + /* Initialize address family type. Unfortunately, target type doesn't + * really tell the address family type, except when IPv6 flag is + * explicitly set. + */ if ((ip_addr_ver == 6) || (type & PJSIP_TRANSPORT_IPV6)) af = pj_AF_INET6(); else if (ip_addr_ver == 4) @@ -401,19 +411,40 @@ PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, target->addr.port)); if (query->query_type == PJ_DNS_TYPE_SRV) { + int opt = 0; + + if (af == pj_AF_UNSPEC()) + opt = PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA | + PJ_DNS_SRV_RESOLVE_AAAA; + else if (af == pj_AF_INET6()) + opt = PJ_DNS_SRV_FALLBACK_AAAA | PJ_DNS_SRV_RESOLVE_AAAA_ONLY; + else /* af == pj_AF_INET() */ + opt = PJ_DNS_SRV_FALLBACK_A; status = pj_dns_srv_resolve(&query->naptr[0].name, &query->naptr[0].res_type, query->req.def_port, pool, resolver->res, - PJ_TRUE, query, &srv_resolver_cb, NULL); + opt, query, &srv_resolver_cb, NULL); } else if (query->query_type == PJ_DNS_TYPE_A) { - status = pj_dns_resolver_start_query(resolver->res, - &query->naptr[0].name, - PJ_DNS_TYPE_A, 0, - &dns_a_callback, - query, &query->object); + /* Resolve DNS A record if address family is not fixed to IPv6 */ + if (af != pj_AF_INET6()) { + status = pj_dns_resolver_start_query(resolver->res, + &query->naptr[0].name, + PJ_DNS_TYPE_A, 0, + &dns_a_callback, + query, &query->object); + } + + /* Resolve DNS AAAA record if address family is not fixed to IPv4 */ + if (af != pj_AF_INET()) { + status = pj_dns_resolver_start_query(resolver->res, + &query->naptr[0].name, + PJ_DNS_TYPE_AAAA, 0, + &dns_aaaa_callback, + query, &query->object6); + } } else { pj_assert(!"Unexpected"); @@ -454,18 +485,40 @@ static void dns_a_callback(void *user_data, pj_dns_parsed_packet *pkt) { struct query *query = (struct query*) user_data; - pjsip_server_addresses srv; - pj_dns_a_record rec; - unsigned i; + pjsip_server_addresses *srv = &query->server; - rec.addr_count = 0; + /* Reset outstanding job */ + query->object = NULL; - /* Parse the response */ if (status == PJ_SUCCESS) { - status = pj_dns_parse_a_response(pkt, &rec); - } + pj_dns_addr_record rec; + unsigned i; + + /* Parse the response */ + rec.addr_count = 0; + status = pj_dns_parse_addr_response(pkt, &rec); + + /* Build server addresses and call callback */ + for (i = 0; i < rec.addr_count && + srv->count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) + { + /* Should not happen, just in case */ + if (rec.addr[i].af != pj_AF_INET()) + continue; + + srv->entry[srv->count].type = query->naptr[0].type; + srv->entry[srv->count].priority = 0; + srv->entry[srv->count].weight = 0; + srv->entry[srv->count].addr_len = sizeof(pj_sockaddr_in); + pj_sockaddr_in_init(&srv->entry[srv->count].addr.ipv4, + 0, (pj_uint16_t)query->req.def_port); + srv->entry[srv->count].addr.ipv4.sin_addr = rec.addr[i].ip.v4; + + ++srv->count; + } + + } else { - if (status != PJ_SUCCESS) { char errmsg[PJ_ERR_MSG_SIZE]; /* Log error */ @@ -473,30 +526,79 @@ static void dns_a_callback(void *user_data, PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", errmsg)); - /* Call the callback */ - (*query->cb)(status, query->token, NULL); - return; + query->last_error = status; } - /* Build server addresses and call callback */ - srv.count = 0; - for (i = 0; i < rec.addr_count && - srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) - { - srv.entry[srv.count].type = query->naptr[0].type; - srv.entry[srv.count].priority = 0; - srv.entry[srv.count].weight = 0; - srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in); - pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4, - 0, (pj_uint16_t)query->req.def_port); - srv.entry[srv.count].addr.ipv4.sin_addr.s_addr = - rec.addr[i].s_addr; - - ++srv.count; + /* Call the callback if all DNS queries have been completed */ + if (query->object == NULL && query->object6 == NULL) { + if (srv->count > 0) + (*query->cb)(PJ_SUCCESS, query->token, &query->server); + else + (*query->cb)(query->last_error, query->token, NULL); } +} - /* Call the callback */ - (*query->cb)(PJ_SUCCESS, query->token, &srv); + +/* + * This callback is called when target is resolved with DNS AAAA query. + */ +static void dns_aaaa_callback(void *user_data, + pj_status_t status, + pj_dns_parsed_packet *pkt) +{ + struct query *query = (struct query*) user_data; + pjsip_server_addresses *srv = &query->server; + + /* Reset outstanding job */ + query->object6 = NULL; + + if (status == PJ_SUCCESS) { + pj_dns_addr_record rec; + unsigned i; + + /* Parse the response */ + rec.addr_count = 0; + status = pj_dns_parse_addr_response(pkt, &rec); + + /* Build server addresses and call callback */ + for (i = 0; i < rec.addr_count && + srv->count < PJSIP_MAX_RESOLVED_ADDRESSES; ++i) + { + /* Should not happen, just in case */ + if (rec.addr[i].af != pj_AF_INET6()) + continue; + + srv->entry[srv->count].type = query->naptr[0].type | + PJSIP_TRANSPORT_IPV6; + srv->entry[srv->count].priority = 0; + srv->entry[srv->count].weight = 0; + srv->entry[srv->count].addr_len = sizeof(pj_sockaddr_in); + pj_sockaddr_init(pj_AF_INET6(), &srv->entry[srv->count].addr, + 0, (pj_uint16_t)query->req.def_port); + srv->entry[srv->count].addr.ipv6.sin6_addr = rec.addr[i].ip.v6; + + ++srv->count; + } + + } else { + + char errmsg[PJ_ERR_MSG_SIZE]; + + /* Log error */ + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(4,(query->objname, "DNS AAAA record resolution failed: %s", + errmsg)); + + query->last_error = status; + } + + /* Call the callback if all DNS queries have been completed */ + if (query->object == NULL && query->object6 == NULL) { + if (srv->count > 0) + (*query->cb)(PJ_SUCCESS, query->token, &query->server); + else + (*query->cb)(query->last_error, query->token, NULL); + } } @@ -514,7 +616,7 @@ static void srv_resolver_cb(void *user_data, /* Log error */ pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", + PJ_LOG(4,(query->objname, "DNS A/AAAA record resolution failed: %s", errmsg)); /* Call the callback */ @@ -525,19 +627,27 @@ static void srv_resolver_cb(void *user_data, /* Build server addresses and call callback */ srv.count = 0; for (i=0; i<rec->count; ++i) { + const pj_dns_addr_record *s = &rec->entry[i].server; unsigned j; - for (j = 0; j < rec->entry[i].server.addr_count && + for (j = 0; j < s->addr_count && srv.count < PJSIP_MAX_RESOLVED_ADDRESSES; ++j) { srv.entry[srv.count].type = query->naptr[0].type; srv.entry[srv.count].priority = rec->entry[i].priority; srv.entry[srv.count].weight = rec->entry[i].weight; srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in); - pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4, - 0, (pj_uint16_t)rec->entry[i].port); - srv.entry[srv.count].addr.ipv4.sin_addr.s_addr = - rec->entry[i].server.addr[j].s_addr; + pj_sockaddr_init(s->addr[j].af, + &srv.entry[srv.count].addr, + 0, (pj_uint16_t)rec->entry[i].port); + if (s->addr[j].af == pj_AF_INET6()) + srv.entry[srv.count].addr.ipv6.sin6_addr = s->addr[j].ip.v6; + else + srv.entry[srv.count].addr.ipv4.sin_addr = s->addr[j].ip.v4; + + /* Update transport type if this is IPv6 */ + if (s->addr[j].af == pj_AF_INET6()) + srv.entry[srv.count].type |= PJSIP_TRANSPORT_IPV6; ++srv.count; } |