1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pjlib-util/srv_resolver.h>
21 #include <pjlib-util/errno.h>
22 #include <pj/array.h>
23 #include <pj/assert.h>
24 #include <pj/log.h>
25 #include <pj/os.h>
26 #include <pj/pool.h>
27 #include <pj/rand.h>
28 #include <pj/string.h>
29 
30 
31 #define THIS_FILE   "srv_resolver.c"
32 
33 #define ADDR_MAX_COUNT	    PJ_DNS_MAX_IP_IN_A_REC
34 
35 struct common
36 {
37     pj_dns_type		     type;	    /**< Type of this structure.*/
38 };
39 
40 #pragma pack(1)
41 struct srv_target
42 {
43     struct common	    common;
44     struct common	    common_aaaa;
45     pj_dns_srv_async_query *parent;
46     pj_str_t		    target_name;
47     pj_dns_async_query	   *q_a;
48     pj_dns_async_query	   *q_aaaa;
49     char		    target_buf[PJ_MAX_HOSTNAME];
50     pj_str_t		    cname;
51     char		    cname_buf[PJ_MAX_HOSTNAME];
52     pj_uint16_t		    port;
53     unsigned		    priority;
54     unsigned		    weight;
55     unsigned		    sum;
56     unsigned		    addr_cnt;
57     pj_sockaddr		    addr[ADDR_MAX_COUNT];/**< Address family and IP.*/
58 };
59 #pragma pack()
60 
61 struct pj_dns_srv_async_query
62 {
63     struct common	     common;
64     char		    *objname;
65 
66     pj_dns_type		     dns_state;	    /**< DNS type being resolved.   */
67     pj_dns_resolver	    *resolver;	    /**< Resolver SIP instance.	    */
68     void		    *token;
69     pj_dns_async_query	    *q_srv;
70     pj_dns_srv_resolver_cb  *cb;
71     pj_status_t		     last_error;
72 
73     /* Original request: */
74     unsigned		     option;
75     pj_str_t		     full_name;
76     pj_str_t		     domain_part;
77     pj_uint16_t		     def_port;
78 
79     /* SRV records and their resolved IP addresses: */
80     unsigned		     srv_cnt;
81     struct srv_target	     srv[PJ_DNS_SRV_MAX_ADDR];
82 
83     /* Number of hosts in SRV records that the IP address has been resolved */
84     unsigned		     host_resolved;
85 
86 };
87 
88 
89 /* Async resolver callback, forward decl. */
90 static void dns_callback(void *user_data,
91 			 pj_status_t status,
92 			 pj_dns_parsed_packet *pkt);
93 
94 
95 
96 /*
97  * The public API to invoke DNS SRV resolution.
98  */
pj_dns_srv_resolve(const pj_str_t * domain_name,const pj_str_t * res_name,unsigned def_port,pj_pool_t * pool,pj_dns_resolver * resolver,unsigned option,void * token,pj_dns_srv_resolver_cb * cb,pj_dns_srv_async_query ** p_query)99 PJ_DEF(pj_status_t) pj_dns_srv_resolve( const pj_str_t *domain_name,
100 				        const pj_str_t *res_name,
101 					unsigned def_port,
102 					pj_pool_t *pool,
103 					pj_dns_resolver *resolver,
104 					unsigned option,
105 					void *token,
106 					pj_dns_srv_resolver_cb *cb,
107 					pj_dns_srv_async_query **p_query)
108 {
109     pj_size_t len;
110     pj_str_t target_name;
111     pj_dns_srv_async_query *query_job;
112     pj_status_t status;
113 
114     PJ_ASSERT_RETURN(domain_name && domain_name->slen &&
115 		     res_name && res_name->slen &&
116 		     pool && resolver && cb, PJ_EINVAL);
117 
118     /* Build full name */
119     len = domain_name->slen + res_name->slen + 2;
120     target_name.ptr = (char*) pj_pool_alloc(pool, len);
121     pj_strcpy(&target_name, res_name);
122     if (res_name->ptr[res_name->slen-1] != '.')
123 	pj_strcat2(&target_name, ".");
124     len = target_name.slen;
125     pj_strcat(&target_name, domain_name);
126     target_name.ptr[target_name.slen] = '\0';
127 
128 
129     /* Build the query_job state */
130     query_job = PJ_POOL_ZALLOC_T(pool, pj_dns_srv_async_query);
131     query_job->common.type = PJ_DNS_TYPE_SRV;
132     query_job->objname = target_name.ptr;
133     query_job->resolver = resolver;
134     query_job->token = token;
135     query_job->cb = cb;
136     query_job->option = option;
137     query_job->full_name = target_name;
138     query_job->domain_part.ptr = target_name.ptr + len;
139     query_job->domain_part.slen = target_name.slen - len;
140     query_job->def_port = (pj_uint16_t)def_port;
141 
142     /* Normalize query job option PJ_DNS_SRV_RESOLVE_AAAA_ONLY */
143     if (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)
144 	query_job->option |= PJ_DNS_SRV_RESOLVE_AAAA;
145 
146     /* Start the asynchronous query_job */
147 
148     query_job->dns_state = PJ_DNS_TYPE_SRV;
149 
150     PJ_LOG(5, (query_job->objname,
151 	       "Starting async DNS %s query_job: target=%.*s:%d",
152 	       pj_dns_get_type_name(query_job->dns_state),
153 	       (int)target_name.slen, target_name.ptr,
154 	       def_port));
155 
156     status = pj_dns_resolver_start_query(resolver, &target_name,
157 				         query_job->dns_state, 0,
158 					 &dns_callback,
159     					 query_job, &query_job->q_srv);
160     if (status==PJ_SUCCESS && p_query)
161 	*p_query = query_job;
162 
163     return status;
164 }
165 
166 
167 /*
168  * Cancel pending query.
169  */
pj_dns_srv_cancel_query(pj_dns_srv_async_query * query,pj_bool_t notify)170 PJ_DEF(pj_status_t) pj_dns_srv_cancel_query(pj_dns_srv_async_query *query,
171 					    pj_bool_t notify)
172 {
173     pj_bool_t has_pending = PJ_FALSE;
174     unsigned i;
175 
176     if (query->q_srv) {
177 	pj_dns_resolver_cancel_query(query->q_srv, PJ_FALSE);
178 	query->q_srv = NULL;
179 	has_pending = PJ_TRUE;
180     }
181 
182     for (i=0; i<query->srv_cnt; ++i) {
183 	struct srv_target *srv = &query->srv[i];
184 	if (srv->q_a) {
185 	    pj_dns_resolver_cancel_query(srv->q_a, PJ_FALSE);
186 	    srv->q_a = NULL;
187 	    has_pending = PJ_TRUE;
188 	}
189 	if (srv->q_aaaa) {
190 	    /* Check if it is a dummy query. */
191 	    if (srv->q_aaaa != (pj_dns_async_query*)0x1) {
192 		pj_dns_resolver_cancel_query(srv->q_aaaa, PJ_FALSE);
193 		has_pending = PJ_TRUE;
194 	    }
195 	    srv->q_aaaa = NULL;
196 	}
197     }
198 
199     if (has_pending && notify && query->cb) {
200 	(*query->cb)(query->token, PJ_ECANCELLED, NULL);
201     }
202 
203     return has_pending? PJ_SUCCESS : PJ_EINVALIDOP;
204 }
205 
206 
207 #define SWAP(type,ptr1,ptr2) if (ptr1 != ptr2) { \
208 				type tmp; \
209 				pj_memcpy(&tmp, ptr1, sizeof(type)); \
210 				pj_memcpy(ptr1, ptr2, sizeof(type)); \
211 				(ptr1)->target_name.ptr = (ptr1)->target_buf;\
212 				pj_memcpy(ptr2, &tmp, sizeof(type)); \
213 				(ptr2)->target_name.ptr = (ptr2)->target_buf;\
214 			     } else {}
215 
216 
217 /* Build server entries in the query_job based on received SRV response */
build_server_entries(pj_dns_srv_async_query * query_job,pj_dns_parsed_packet * response)218 static void build_server_entries(pj_dns_srv_async_query *query_job,
219 				 pj_dns_parsed_packet *response)
220 {
221     unsigned i;
222 
223     /* Save the Resource Records in DNS answer into SRV targets. */
224     query_job->srv_cnt = 0;
225     for (i=0; i<response->hdr.anscount &&
226 	      query_job->srv_cnt < PJ_DNS_SRV_MAX_ADDR; ++i)
227     {
228 	pj_dns_parsed_rr *rr = &response->ans[i];
229 	struct srv_target *srv = &query_job->srv[query_job->srv_cnt];
230 
231 	if (rr->type != PJ_DNS_TYPE_SRV) {
232 	    PJ_LOG(4,(query_job->objname,
233 		      "Received non SRV answer for SRV query_job!"));
234 	    continue;
235 	}
236 
237 	if (rr->rdata.srv.target.slen > PJ_MAX_HOSTNAME) {
238 	    PJ_LOG(4,(query_job->objname, "Hostname is too long!"));
239 	    continue;
240 	}
241 
242 	if (rr->rdata.srv.target.slen == 0) {
243 	    PJ_LOG(4,(query_job->objname, "Hostname is empty!"));
244 	    continue;
245 	}
246 
247 	/* Build the SRV entry for RR */
248 	pj_bzero(srv, sizeof(*srv));
249 	srv->target_name.ptr = srv->target_buf;
250 	pj_strncpy(&srv->target_name, &rr->rdata.srv.target,
251 		   sizeof(srv->target_buf));
252 	srv->port = rr->rdata.srv.port;
253 	srv->priority = rr->rdata.srv.prio;
254 	srv->weight = rr->rdata.srv.weight;
255 
256 	++query_job->srv_cnt;
257     }
258 
259     if (query_job->srv_cnt == 0) {
260 	PJ_LOG(4,(query_job->objname,
261 		  "Could not find SRV record in DNS answer!"));
262 	return;
263     }
264 
265     /* First pass:
266      *	order the entries based on priority.
267      */
268     for (i=0; i<query_job->srv_cnt-1; ++i) {
269 	unsigned min = i, j;
270 	for (j=i+1; j<query_job->srv_cnt; ++j) {
271 	    if (query_job->srv[j].priority < query_job->srv[min].priority)
272 		min = j;
273 	}
274 	SWAP(struct srv_target, &query_job->srv[i], &query_job->srv[min]);
275     }
276 
277     /* Second pass:
278      *	Order the entry in a list.
279      *
280      *  The algorithm for selecting server among servers with the same
281      *  priority is described in RFC 2782.
282      */
283     for (i=0; i<query_job->srv_cnt; ++i) {
284 	unsigned j, count=1, sum;
285 
286 	/* Calculate running sum for servers with the same priority */
287 	sum = query_job->srv[i].sum = query_job->srv[i].weight;
288 	for (j=i+1; j<query_job->srv_cnt &&
289 		    query_job->srv[j].priority == query_job->srv[i].priority; ++j)
290 	{
291 	    sum += query_job->srv[j].weight;
292 	    query_job->srv[j].sum = sum;
293 	    ++count;
294 	}
295 
296 	if (count > 1) {
297 	    unsigned r;
298 
299 	    /* Elect one random number between zero and the total sum of
300 	     * weight (inclusive).
301 	     */
302 	    r = pj_rand() % (sum + 1);
303 
304 	    /* Select the first server which running sum is greater than or
305 	     * equal to the random number.
306 	     */
307 	    for (j=i; j<i+count; ++j) {
308 		if (query_job->srv[j].sum >= r)
309 		    break;
310 	    }
311 
312 	    /* Must have selected one! */
313 	    pj_assert(j != i+count);
314 
315 	    /* Put this entry in front (of entries with same priority) */
316 	    SWAP(struct srv_target, &query_job->srv[i], &query_job->srv[j]);
317 
318 	    /* Remove all other entries (of the same priority) */
319 	    /* Don't need to do this.
320 	     * See https://trac.pjsip.org/repos/ticket/1719
321 	    while (count > 1) {
322 		pj_array_erase(query_job->srv, sizeof(struct srv_target),
323 			       query_job->srv_cnt, i+1);
324 		--count;
325 		--query_job->srv_cnt;
326 	    }
327 	    */
328 	}
329     }
330 
331     /* Since we've been moving around SRV entries, update the pointers
332      * in target_name.
333      */
334     for (i=0; i<query_job->srv_cnt; ++i) {
335 	query_job->srv[i].target_name.ptr = query_job->srv[i].target_buf;
336     }
337 
338     /* Check for Additional Info section if A/AAAA records are available, and
339      * fill in the IP address (so that we won't need to resolve the A/AAAA
340      * record with another DNS query_job).
341      */
342     for (i=0; i<response->hdr.arcount; ++i) {
343 	pj_dns_parsed_rr *rr = &response->arr[i];
344 	unsigned j;
345 
346 	/* Skip non-A/AAAA record */
347 	if (rr->type != PJ_DNS_TYPE_A && rr->type != PJ_DNS_TYPE_AAAA)
348 	    continue;
349 
350 	/* Also skip if:
351 	 * - it is A record and app only want AAAA record, or
352 	 * - it is AAAA record and app does not want AAAA record
353 	 */
354 	if ((rr->type == PJ_DNS_TYPE_A &&
355 	    (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)!=0) ||
356 	    (rr->type == PJ_DNS_TYPE_AAAA &&
357 	    (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)==0))
358 	{
359 	    continue;
360 	}
361 
362 	/* Yippeaiyee!! There is an "A/AAAA" record!
363 	 * Update the IP address of the corresponding SRV record.
364 	 */
365 	for (j=0; j<query_job->srv_cnt; ++j) {
366 	    if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0
367 		&& query_job->srv[j].addr_cnt < ADDR_MAX_COUNT)
368 	    {
369 		unsigned cnt = query_job->srv[j].addr_cnt;
370 		if (rr->type == PJ_DNS_TYPE_A) {
371 		    pj_sockaddr_init(pj_AF_INET(),
372 					&query_job->srv[j].addr[cnt], NULL,
373 					query_job->srv[j].port);
374 		    query_job->srv[j].addr[cnt].ipv4.sin_addr =
375 						rr->rdata.a.ip_addr;
376 		} else {
377 		    pj_sockaddr_init(pj_AF_INET6(),
378 					&query_job->srv[j].addr[cnt], NULL,
379 					query_job->srv[j].port);
380 		    query_job->srv[j].addr[cnt].ipv6.sin6_addr =
381 						rr->rdata.aaaa.ip_addr;
382 		}
383 
384 		/* Only increment host_resolved once per SRV record */
385 		if (query_job->srv[j].addr_cnt == 0)
386 		    ++query_job->host_resolved;
387 
388 		++query_job->srv[j].addr_cnt;
389 		break;
390 	    }
391 	}
392 
393 	/* Not valid message; SRV entry might have been deleted in
394 	 * server selection process.
395 	 */
396 	/*
397 	if (j == query_job->srv_cnt) {
398 	    PJ_LOG(4,(query_job->objname,
399 		      "Received DNS SRV answer with A record, but "
400 		      "couldn't find matching name (name=%.*s)",
401 		      (int)rr->name.slen,
402 		      rr->name.ptr));
403 	}
404 	*/
405 
406     }
407 
408     /* Rescan again the name specified in the SRV record to see if IP
409      * address is specified as the target name (unlikely, but well, who
410      * knows..).
411      */
412     for (i=0; i<query_job->srv_cnt; ++i) {
413 	pj_in_addr addr;
414 	pj_in6_addr addr6;
415 	unsigned cnt = query_job->srv[i].addr_cnt;
416 
417 	if (cnt != 0) {
418 	    /* IP address already resolved */
419 	    continue;
420 	}
421 
422 	if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY)==0 &&
423 	    pj_inet_pton(pj_AF_INET(), &query_job->srv[i].target_name,
424 			 &addr) == PJ_SUCCESS)
425 	{
426 	    pj_sockaddr_init(pj_AF_INET(), &query_job->srv[i].addr[cnt],
427 			     NULL, query_job->srv[i].port);
428 	    query_job->srv[i].addr[cnt].ipv4.sin_addr = addr;
429 	    ++query_job->srv[i].addr_cnt;
430 	    ++query_job->host_resolved;
431 	} else if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA)!=0 &&
432 		   pj_inet_pton(pj_AF_INET6(), &query_job->srv[i].target_name,
433 				&addr6) == PJ_SUCCESS)
434 	{
435 	    pj_sockaddr_init(pj_AF_INET6(), &query_job->srv[i].addr[cnt],
436 			     NULL, query_job->srv[i].port);
437 	    query_job->srv[i].addr[cnt].ipv6.sin6_addr = addr6;
438 	    ++query_job->srv[i].addr_cnt;
439 	    ++query_job->host_resolved;
440 	}
441     }
442 
443     /* Print resolved entries to the log */
444     PJ_LOG(5,(query_job->objname,
445 	      "SRV query_job for %.*s completed, "
446 	      "%d of %d total entries selected%c",
447 	      (int)query_job->full_name.slen,
448 	      query_job->full_name.ptr,
449 	      query_job->srv_cnt,
450 	      response->hdr.anscount,
451 	      (query_job->srv_cnt ? ':' : ' ')));
452 
453     for (i=0; i<query_job->srv_cnt; ++i) {
454 	char addr[PJ_INET6_ADDRSTRLEN];
455 
456 	if (query_job->srv[i].addr_cnt != 0) {
457 	    pj_sockaddr_print(&query_job->srv[i].addr[0],
458 			 addr, sizeof(addr), 2);
459 	} else
460 	    pj_ansi_strcpy(addr, "-");
461 
462 	PJ_LOG(5,(query_job->objname,
463 		  " %d: SRV %d %d %d %.*s (%s)",
464 		  i, query_job->srv[i].priority,
465 		  query_job->srv[i].weight,
466 		  query_job->srv[i].port,
467 		  (int)query_job->srv[i].target_name.slen,
468 		  query_job->srv[i].target_name.ptr,
469 		  addr));
470     }
471 }
472 
473 
474 /* Start DNS A and/or AAAA record queries for all SRV records in
475  * the query_job structure.
476  */
resolve_hostnames(pj_dns_srv_async_query * query_job)477 static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
478 {
479     unsigned i, err_cnt = 0;
480     pj_status_t err=PJ_SUCCESS, status;
481 
482     query_job->dns_state = PJ_DNS_TYPE_A;
483 
484     for (i=0; i<query_job->srv_cnt; ++i) {
485 	struct srv_target *srv = &query_job->srv[i];
486 
487 	if (srv->addr_cnt != 0) {
488 	    /*
489 	     * This query is already counted as resolved because of the
490 	     * additional records in the SRV response or the target name
491 	     * is an IP address exception in build_server_entries().
492 	     */
493 	    continue;
494 	}
495 
496 	PJ_LOG(5, (query_job->objname,
497 		   "Starting async DNS A query_job for %.*s",
498 		   (int)srv->target_name.slen,
499 		   srv->target_name.ptr));
500 
501 	srv->common.type = PJ_DNS_TYPE_A;
502 	srv->common_aaaa.type = PJ_DNS_TYPE_AAAA;
503 	srv->parent = query_job;
504 	srv->q_a = NULL;
505 	srv->q_aaaa = NULL;
506 
507 	status = PJ_SUCCESS;
508 
509 	/* Start DNS A record query */
510 	if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA_ONLY) == 0)
511 	{
512 	    if ((query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0) {
513 		/* If there will be DNS AAAA query too, let's setup
514 		 * a dummy one here, otherwise app callback may be called
515 		 * immediately (before DNS AAAA query is sent) when
516 		 * DNS A record is available in the cache.
517 		 */
518 		srv->q_aaaa = (pj_dns_async_query*)0x1;
519 	    }
520 	    status = pj_dns_resolver_start_query(query_job->resolver,
521 						 &srv->target_name,
522 						 PJ_DNS_TYPE_A, 0,
523 						 &dns_callback,
524 						 &srv->common, &srv->q_a);
525 	}
526 
527 	/* Start DNS AAAA record query */
528 	if (status == PJ_SUCCESS &&
529 	    (query_job->option & PJ_DNS_SRV_RESOLVE_AAAA) != 0)
530 	{
531 	    status = pj_dns_resolver_start_query(query_job->resolver,
532 						 &srv->target_name,
533 						 PJ_DNS_TYPE_AAAA, 0,
534 						 &dns_callback,
535 						 &srv->common_aaaa, &srv->q_aaaa);
536 	}
537 
538 	/* See also #1809: dns_callback() will be invoked synchronously when response
539 	 * is available in the cache, and var 'query_job->host_resolved' will get
540 	 * incremented within the dns_callback(), which will cause this function
541 	 * returning false error, so don't use that variable for counting errors.
542 	 */
543 	if (status != PJ_SUCCESS) {
544 	    query_job->host_resolved++;
545 	    err_cnt++;
546 	    err = status;
547 	}
548     }
549 
550     return (err_cnt == query_job->srv_cnt) ? err : PJ_SUCCESS;
551 }
552 
553 /*
554  * This callback is called by PJLIB-UTIL DNS resolver when asynchronous
555  * query_job has completed (successfully or with error).
556  */
dns_callback(void * user_data,pj_status_t status,pj_dns_parsed_packet * pkt)557 static void dns_callback(void *user_data,
558 			 pj_status_t status,
559 			 pj_dns_parsed_packet *pkt)
560 {
561     struct common *common = (struct common*) user_data;
562     pj_dns_srv_async_query *query_job;
563     struct srv_target *srv = NULL;
564     unsigned i;
565 
566     if (common->type == PJ_DNS_TYPE_SRV) {
567 	query_job = (pj_dns_srv_async_query*) common;
568 	srv = NULL;
569     } else if (common->type == PJ_DNS_TYPE_A) {
570 	srv = (struct srv_target*) common;
571 	query_job = srv->parent;
572     } else if (common->type == PJ_DNS_TYPE_AAAA) {
573 	srv = (struct srv_target*)((pj_int8_t*)common-sizeof(struct common));
574 	query_job = srv->parent;
575     } else {
576 	pj_assert(!"Unexpected user data!");
577 	return;
578     }
579 
580     /* Proceed to next stage */
581     if (query_job->dns_state == PJ_DNS_TYPE_SRV) {
582 
583 	/* We are getting SRV response */
584 
585 	/* Clear the outstanding job */
586 	query_job->q_srv = NULL;
587 
588 	if (status == PJ_SUCCESS) {
589 	    if (PJ_DNS_GET_TC(pkt->hdr.flags)) {
590 		/* Got truncated answer, the standard recommends to follow up
591 		 * the query using TCP. Since we currently don't support it,
592 		 * just return error.
593 		 */
594 		PJ_LOG(4,(query_job->objname,
595 			  "Discard truncated DNS SRV response for %.*s",
596 			  (int)query_job->full_name.slen,
597 			  query_job->full_name.ptr));
598 
599 		status = PJ_EIGNORED;
600 		query_job->last_error = status;
601 		goto on_error;
602 	    } else if (pkt->hdr.anscount != 0) {
603 		/* Got SRV response, build server entry. If A records are
604 		 * available in additional records section of the DNS response,
605 		 * save them too.
606 		 */
607 		build_server_entries(query_job, pkt);
608 	    }
609 
610 	} else {
611 	    char errmsg[PJ_ERR_MSG_SIZE];
612 
613 	    /* Update query_job last error */
614 	    query_job->last_error = status;
615 
616 	    pj_strerror(status, errmsg, sizeof(errmsg));
617 	    PJ_LOG(4,(query_job->objname,
618 		      "DNS SRV resolution failed for %.*s: %s",
619 		      (int)query_job->full_name.slen,
620 		      query_job->full_name.ptr,
621 		      errmsg));
622 
623 	    /* Trigger error when fallback is disabled */
624 	    if ((query_job->option &
625 		 (PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA)) == 0)
626 	    {
627 		goto on_error;
628 	    }
629 	}
630 
631 	/* If we can't build SRV record, assume the original target is
632 	 * an A record and resolve with DNS A resolution.
633 	 */
634 	if (query_job->srv_cnt == 0 && query_job->domain_part.slen > 0) {
635 	    unsigned new_option = 0;
636 
637 	    /* Looks like we aren't getting any SRV responses.
638 	     * Resolve the original target as A record by creating a
639 	     * single "dummy" srv record and start the hostname resolution.
640 	     */
641 	    PJ_LOG(4, (query_job->objname,
642 		       "DNS SRV resolution failed for %.*s, trying "
643 		       "resolving A/AAAA record for %.*s",
644 		       (int)query_job->full_name.slen,
645 		       query_job->full_name.ptr,
646 		       (int)query_job->domain_part.slen,
647 		       query_job->domain_part.ptr));
648 
649 	    /* Create a "dummy" srv record using the original target */
650 	    i = query_job->srv_cnt++;
651 	    pj_bzero(&query_job->srv[i], sizeof(query_job->srv[i]));
652 	    query_job->srv[i].target_name = query_job->domain_part;
653 	    query_job->srv[i].priority = 0;
654 	    query_job->srv[i].weight = 0;
655 	    query_job->srv[i].port = query_job->def_port;
656 
657 	    /* Update query_job resolution option based on fallback option */
658 	    if (query_job->option & PJ_DNS_SRV_FALLBACK_AAAA)
659 		new_option |= (PJ_DNS_SRV_RESOLVE_AAAA |
660 			       PJ_DNS_SRV_RESOLVE_AAAA_ONLY);
661 	    if (query_job->option & PJ_DNS_SRV_FALLBACK_A)
662 		new_option &= (~PJ_DNS_SRV_RESOLVE_AAAA_ONLY);
663 
664 	    query_job->option = new_option;
665 	}
666 
667 
668 	/* Resolve server hostnames (DNS A/AAAA record) for hosts which
669 	 * don't have A/AAAA record yet.
670 	 */
671 	if (query_job->host_resolved != query_job->srv_cnt) {
672 	    status = resolve_hostnames(query_job);
673 	    if (status != PJ_SUCCESS)
674 		goto on_error;
675 
676 	    /* Must return now. Callback may have been called and query_job
677 	     * may have been destroyed.
678 	     */
679 	    return;
680 	}
681 
682     } else if (query_job->dns_state == PJ_DNS_TYPE_A) {
683 	pj_bool_t is_type_a, srv_completed;
684         pj_dns_addr_record rec;
685 
686 	/* Avoid warning: potentially uninitialized local variable 'rec' */
687 	rec.alias.slen = 0;
688 	rec.addr_count = 0;
689 
690 	/* Clear outstanding job */
691 	if (common->type == PJ_DNS_TYPE_A) {
692 	    srv_completed = (srv->q_aaaa == NULL);
693 	    srv->q_a = NULL;
694 	} else if (common->type == PJ_DNS_TYPE_AAAA) {
695 	    srv_completed = (srv->q_a == NULL);
696 	    srv->q_aaaa = NULL;
697 	} else {
698 	    pj_assert(!"Unexpected job type");
699 	    query_job->last_error = status = PJ_EINVALIDOP;
700 	    goto on_error;
701 	}
702 
703 	is_type_a = (common->type == PJ_DNS_TYPE_A);
704 
705         /* Parse response */
706         if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
707             status = pj_dns_parse_addr_response(pkt, &rec);
708             if (status!=PJ_SUCCESS) {
709                 PJ_PERROR(4,(query_job->objname, status,
710 			     "DNS %s record parse error for '%.*s'.",
711 			     (is_type_a ? "A" : "AAAA"),
712 			     (int)query_job->domain_part.slen,
713 			     query_job->domain_part.ptr));
714 	    }
715 	}
716 
717 	/* Check that we really have answer */
718 	if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
719 	    char addr[PJ_INET6_ADDRSTRLEN];
720 
721 	    pj_assert(rec.addr_count != 0);
722 
723 	    /* Update CNAME alias, if present. */
724 	    if (srv->cname.slen==0 && rec.alias.slen) {
725 		pj_assert(rec.alias.slen <= (int)sizeof(srv->cname_buf));
726 		srv->cname.ptr = srv->cname_buf;
727 		pj_strcpy(&srv->cname, &rec.alias);
728 	    //} else {
729 		//srv->cname.slen = 0;
730 	    }
731 
732 	    /* Update IP address of the corresponding hostname or CNAME */
733 	    for (i=0; i<rec.addr_count && srv->addr_cnt<ADDR_MAX_COUNT; ++i)
734 	    {
735 		pj_bool_t added = PJ_FALSE;
736 
737 		if (is_type_a && rec.addr[i].af == pj_AF_INET()) {
738 		    pj_sockaddr_init(pj_AF_INET(), &srv->addr[srv->addr_cnt],
739 				     NULL, srv->port);
740 		    srv->addr[srv->addr_cnt].ipv4.sin_addr =
741 				     rec.addr[i].ip.v4;
742 		    added = PJ_TRUE;
743 		} else if (!is_type_a && rec.addr[i].af == pj_AF_INET6()) {
744 		    pj_sockaddr_init(pj_AF_INET6(), &srv->addr[srv->addr_cnt],
745 				     NULL, srv->port);
746 		    srv->addr[srv->addr_cnt].ipv6.sin6_addr =
747 				     rec.addr[i].ip.v6;
748 		    added = PJ_TRUE;
749 		} else {
750 		    /* Mismatched address family, e.g: getting IPv6 address in
751 		     * DNS A query resolution.
752 		     */
753 		    PJ_LOG(4,(query_job->objname,
754 			      "Bad address family in DNS %s query for %.*s",
755 			      (is_type_a? "A" : "AAAA"),
756 			      (int)srv->target_name.slen,
757 			      srv->target_name.ptr));
758 		}
759 
760 		if (added) {
761 		    PJ_LOG(5,(query_job->objname,
762 			      "DNS %s for %.*s: %s",
763 			      (is_type_a? "A" : "AAAA"),
764 			      (int)srv->target_name.slen,
765 			      srv->target_name.ptr,
766 			      pj_sockaddr_print(&srv->addr[srv->addr_cnt],
767 						addr, sizeof(addr), 2)));
768 
769 		    ++srv->addr_cnt;
770 		}
771 	    }
772 
773 	} else if (status != PJ_SUCCESS) {
774 	    /* Update last error */
775 	    query_job->last_error = status;
776 
777 	    /* Log error */
778 	    PJ_PERROR(4,(query_job->objname, status,
779 			 "DNS %s record resolution failed",
780 			 (is_type_a? "A" : "AAAA")));
781 	}
782 
783 	/* Increment host resolved count when both DNS A and AAAA record
784 	 * queries for this server are completed.
785 	 */
786 	if (srv_completed)
787 	    ++query_job->host_resolved;
788 
789     } else {
790 	pj_assert(!"Unexpected state!");
791 	query_job->last_error = status = PJ_EINVALIDOP;
792 	goto on_error;
793     }
794 
795     /* Check if all hosts have been resolved */
796     if (query_job->host_resolved == query_job->srv_cnt) {
797 	/* Got all answers, build server addresses */
798 	pj_dns_srv_record srv_rec;
799 
800 	srv_rec.count = 0;
801 	for (i=0; i<query_job->srv_cnt; ++i) {
802 	    unsigned j;
803 	    struct srv_target *srv2 = &query_job->srv[i];
804 	    pj_dns_addr_record *s = &srv_rec.entry[srv_rec.count].server;
805 
806 	    srv_rec.entry[srv_rec.count].priority = srv2->priority;
807 	    srv_rec.entry[srv_rec.count].weight = srv2->weight;
808 	    srv_rec.entry[srv_rec.count].port = (pj_uint16_t)srv2->port ;
809 
810 	    srv_rec.entry[srv_rec.count].server.name = srv2->target_name;
811 	    srv_rec.entry[srv_rec.count].server.alias = srv2->cname;
812 	    srv_rec.entry[srv_rec.count].server.addr_count = 0;
813 
814 	    pj_assert(srv2->addr_cnt <= PJ_DNS_MAX_IP_IN_A_REC);
815 
816 	    for (j=0; j<srv2->addr_cnt; ++j) {
817 		s->addr[j].af = srv2->addr[j].addr.sa_family;
818 		if (s->addr[j].af == pj_AF_INET())
819 		    s->addr[j].ip.v4 = srv2->addr[j].ipv4.sin_addr;
820 		else
821 		    s->addr[j].ip.v6 = srv2->addr[j].ipv6.sin6_addr;
822 		++s->addr_count;
823 	    }
824 
825 	    if (srv2->addr_cnt > 0) {
826 		++srv_rec.count;
827 		if (srv_rec.count == PJ_DNS_SRV_MAX_ADDR)
828 		    break;
829 	    }
830 	}
831 
832 	PJ_LOG(5,(query_job->objname,
833 		  "Server resolution complete, %d server entry(s) found",
834 		  srv_rec.count));
835 
836 
837 	if (srv_rec.count > 0)
838 	    status = PJ_SUCCESS;
839 	else {
840 	    status = query_job->last_error;
841 	    if (status == PJ_SUCCESS)
842 		status = PJLIB_UTIL_EDNSNOANSWERREC;
843 	}
844 
845 	/* Call the callback */
846 	(*query_job->cb)(query_job->token, status, &srv_rec);
847     }
848 
849 
850     return;
851 
852 on_error:
853     /* Check for failure */
854     if (status != PJ_SUCCESS) {
855 	PJ_PERROR(4,(query_job->objname, status,
856 		     "DNS %s record resolution error for '%.*s'.",
857 		     pj_dns_get_type_name(query_job->dns_state),
858 		     (int)query_job->domain_part.slen,
859 		     query_job->domain_part.ptr));
860 
861 	/* Cancel any pending query */
862 	pj_dns_srv_cancel_query(query_job, PJ_FALSE);
863 
864 	(*query_job->cb)(query_job->token, status, NULL);
865 	return;
866     }
867 }
868 
869 
870