summaryrefslogtreecommitdiff
path: root/pjsip
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 /pjsip
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 'pjsip')
-rw-r--r--pjsip/src/pjsip/sip_resolve.c192
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;
}