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/resolver.h>
21 #include <pjlib-util/errno.h>
22 #include <pj/compat/socket.h>
23 #include <pj/assert.h>
24 #include <pj/ctype.h>
25 #include <pj/except.h>
26 #include <pj/hash.h>
27 #include <pj/ioqueue.h>
28 #include <pj/log.h>
29 #include <pj/os.h>
30 #include <pj/pool.h>
31 #include <pj/pool_buf.h>
32 #include <pj/rand.h>
33 #include <pj/string.h>
34 #include <pj/sock.h>
35 #include <pj/timer.h>
36
37
38 #define THIS_FILE "resolver.c"
39
40
41 /* Check that maximum DNS nameservers is not too large.
42 * This has got todo with the datatype to index the nameserver in the query.
43 */
44 #if PJ_DNS_RESOLVER_MAX_NS > 256
45 # error "PJ_DNS_RESOLVER_MAX_NS is too large (max=256)"
46 #endif
47
48
49 #define RES_HASH_TABLE_SIZE 127 /**< Hash table size (must be 2^n-1 */
50 #define PORT 53 /**< Default NS port. */
51 #define Q_HASH_TABLE_SIZE 127 /**< Query hash table size */
52 #define TIMER_SIZE 127 /**< Initial number of timers. */
53 #define MAX_FD 3 /**< Maximum internal sockets. */
54
55 #define RES_BUF_SZ PJ_DNS_RESOLVER_RES_BUF_SIZE
56 #define UDPSZ PJ_DNS_RESOLVER_MAX_UDP_SIZE
57 #define TMP_SZ PJ_DNS_RESOLVER_TMP_BUF_SIZE
58
59
60 /* Nameserver state */
61 enum ns_state
62 {
63 STATE_PROBING,
64 STATE_ACTIVE,
65 STATE_BAD,
66 };
67
68 static const char *state_names[3] =
69 {
70 "Probing",
71 "Active",
72 "Bad"
73 };
74
75
76 /*
77 * Each nameserver entry.
78 * A name server is identified by its socket address (IP and port).
79 * Each NS will have a flag to indicate whether it's properly functioning.
80 */
81 struct nameserver
82 {
83 pj_sockaddr addr; /**< Server address. */
84
85 enum ns_state state; /**< Nameserver state. */
86 pj_time_val state_expiry; /**< Time set next state. */
87 pj_time_val rt_delay; /**< Response time. */
88
89
90 /* For calculating rt_delay: */
91 pj_uint16_t q_id; /**< Query ID. */
92 pj_time_val sent_time; /**< Time this query is sent. */
93 };
94
95
96 /* Child query list head
97 * See comments on pj_dns_async_query below.
98 */
99 struct query_head
100 {
101 PJ_DECL_LIST_MEMBER(pj_dns_async_query);
102 };
103
104
105 /* Key to look for outstanding query and/or cached response */
106 struct res_key
107 {
108 pj_uint16_t qtype; /**< Query type. */
109 char name[PJ_MAX_HOSTNAME]; /**< Name being queried */
110 };
111
112
113 /*
114 * This represents each asynchronous query entry.
115 * This entry will be put in two hash tables, the first one keyed on the DNS
116 * transaction ID to match response with the query, and the second one keyed
117 * on "res_key" structure above to match a new request against outstanding
118 * requests.
119 *
120 * An asynchronous entry may have child entries; child entries are subsequent
121 * queries to the same resource while there is pending query on the same
122 * DNS resource name and type. When a query has child entries, once the
123 * response is received (or error occurs), the response will trigger callback
124 * invocations for all childs entries.
125 *
126 * Note: when application cancels the query, the callback member will be
127 * set to NULL, but for simplicity, the query will be let running.
128 */
129 struct pj_dns_async_query
130 {
131 PJ_DECL_LIST_MEMBER(pj_dns_async_query); /**< List member. */
132
133 pj_dns_resolver *resolver; /**< The resolver instance. */
134 pj_uint16_t id; /**< Transaction ID. */
135
136 unsigned transmit_cnt; /**< Number of transmissions. */
137
138 struct res_key key; /**< Key to index this query. */
139 pj_hash_entry_buf hbufid; /**< Hash buffer 1 */
140 pj_hash_entry_buf hbufkey; /**< Hash buffer 2 */
141 pj_timer_entry timer_entry; /**< Timer to manage timeouts */
142 unsigned options; /**< Query options. */
143 void *user_data; /**< Application data. */
144 pj_dns_callback *cb; /**< Callback to be called. */
145 struct query_head child_head; /**< Child queries list head. */
146 };
147
148
149 /* This structure is used to keep cached response entry.
150 * The cache is a hash table keyed on "res_key" structure above.
151 */
152 struct cached_res
153 {
154 PJ_DECL_LIST_MEMBER(struct cached_res);
155
156 pj_pool_t *pool; /**< Cache's pool. */
157 struct res_key key; /**< Resource key. */
158 pj_hash_entry_buf hbuf; /**< Hash buffer */
159 pj_time_val expiry_time; /**< Expiration time. */
160 pj_dns_parsed_packet *pkt; /**< The response packet. */
161 unsigned ref_cnt; /**< Reference counter. */
162 };
163
164
165 /* Resolver entry */
166 struct pj_dns_resolver
167 {
168 pj_str_t name; /**< Resolver instance name for id. */
169
170 /* Internals */
171 pj_pool_t *pool; /**< Internal pool. */
172 pj_grp_lock_t *grp_lock; /**< Group lock protection. */
173 pj_bool_t own_timer; /**< Do we own timer? */
174 pj_timer_heap_t *timer; /**< Timer instance. */
175 pj_bool_t own_ioqueue; /**< Do we own ioqueue? */
176 pj_ioqueue_t *ioqueue; /**< Ioqueue instance. */
177 char tmp_pool[TMP_SZ];/**< Temporary pool buffer. */
178
179 /* Socket */
180 pj_sock_t udp_sock; /**< UDP socket. */
181 pj_ioqueue_key_t *udp_key; /**< UDP socket ioqueue key. */
182 unsigned char udp_rx_pkt[UDPSZ];/**< UDP receive buffer. */
183 unsigned char udp_tx_pkt[UDPSZ];/**< UDP transmit buffer. */
184 pj_ioqueue_op_key_t udp_op_rx_key; /**< UDP read operation key. */
185 pj_ioqueue_op_key_t udp_op_tx_key; /**< UDP write operation key. */
186 pj_sockaddr udp_src_addr; /**< Source address of packet */
187 int udp_addr_len; /**< Source address length. */
188
189 #if PJ_HAS_IPV6
190 /* IPv6 socket */
191 pj_sock_t udp6_sock; /**< UDP socket. */
192 pj_ioqueue_key_t *udp6_key; /**< UDP socket ioqueue key. */
193 unsigned char udp6_rx_pkt[UDPSZ];/**< UDP receive buffer. */
194 //unsigned char udp6_tx_pkt[UDPSZ];/**< UDP transmit buffer. */
195 pj_ioqueue_op_key_t udp6_op_rx_key;/**< UDP read operation key. */
196 pj_ioqueue_op_key_t udp6_op_tx_key;/**< UDP write operation key. */
197 pj_sockaddr udp6_src_addr; /**< Source address of packet */
198 int udp6_addr_len; /**< Source address length. */
199 #endif
200
201 /* Settings */
202 pj_dns_settings settings; /**< Resolver settings. */
203
204 /* Nameservers */
205 unsigned ns_count; /**< Number of name servers. */
206 struct nameserver ns[PJ_DNS_RESOLVER_MAX_NS]; /**< Array of NS. */
207
208 /* Last DNS transaction ID used. */
209 pj_uint16_t last_id;
210
211 /* Hash table for cached response */
212 pj_hash_table_t *hrescache; /**< Cached response in hash table */
213
214 /* Pending asynchronous query, hashed by transaction ID. */
215 pj_hash_table_t *hquerybyid;
216
217 /* Pending asynchronous query, hashed by "res_key" */
218 pj_hash_table_t *hquerybyres;
219
220 /* Query entries free list */
221 struct query_head query_free_nodes;
222 };
223
224
225 /* Callback from ioqueue when packet is received */
226 static void on_read_complete(pj_ioqueue_key_t *key,
227 pj_ioqueue_op_key_t *op_key,
228 pj_ssize_t bytes_read);
229
230 /* Callback to be called when query has timed out */
231 static void on_timeout( pj_timer_heap_t *timer_heap,
232 struct pj_timer_entry *entry);
233
234 /* Select which nameserver to use */
235 static pj_status_t select_nameservers(pj_dns_resolver *resolver,
236 unsigned *count,
237 unsigned servers[]);
238
239 /* Destructor */
240 static void dns_resolver_on_destroy(void *member);
241
242 /* Close UDP socket */
close_sock(pj_dns_resolver * resv)243 static void close_sock(pj_dns_resolver *resv)
244 {
245 /* Close existing socket */
246 if (resv->udp_key != NULL) {
247 pj_ioqueue_unregister(resv->udp_key);
248 resv->udp_key = NULL;
249 resv->udp_sock = PJ_INVALID_SOCKET;
250 } else if (resv->udp_sock != PJ_INVALID_SOCKET) {
251 pj_sock_close(resv->udp_sock);
252 resv->udp_sock = PJ_INVALID_SOCKET;
253 }
254
255 #if PJ_HAS_IPV6
256 if (resv->udp6_key != NULL) {
257 pj_ioqueue_unregister(resv->udp6_key);
258 resv->udp6_key = NULL;
259 resv->udp6_sock = PJ_INVALID_SOCKET;
260 } else if (resv->udp6_sock != PJ_INVALID_SOCKET) {
261 pj_sock_close(resv->udp6_sock);
262 resv->udp6_sock = PJ_INVALID_SOCKET;
263 }
264 #endif
265 }
266
267
268 /* Initialize UDP socket */
init_sock(pj_dns_resolver * resv)269 static pj_status_t init_sock(pj_dns_resolver *resv)
270 {
271 pj_ioqueue_callback socket_cb;
272 pj_sockaddr bound_addr;
273 pj_ssize_t rx_pkt_size;
274 pj_status_t status;
275
276 /* Create the UDP socket */
277 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &resv->udp_sock);
278 if (status != PJ_SUCCESS)
279 return status;
280
281 /* Bind to any address/port */
282 status = pj_sock_bind_in(resv->udp_sock, 0, 0);
283 if (status != PJ_SUCCESS)
284 return status;
285
286 /* Register to ioqueue */
287 pj_bzero(&socket_cb, sizeof(socket_cb));
288 socket_cb.on_read_complete = &on_read_complete;
289 status = pj_ioqueue_register_sock2(resv->pool, resv->ioqueue,
290 resv->udp_sock, resv->grp_lock,
291 resv, &socket_cb, &resv->udp_key);
292 if (status != PJ_SUCCESS)
293 return status;
294
295 pj_ioqueue_op_key_init(&resv->udp_op_rx_key, sizeof(resv->udp_op_rx_key));
296 pj_ioqueue_op_key_init(&resv->udp_op_tx_key, sizeof(resv->udp_op_tx_key));
297
298 /* Start asynchronous read to the UDP socket */
299 rx_pkt_size = sizeof(resv->udp_rx_pkt);
300 resv->udp_addr_len = sizeof(resv->udp_src_addr);
301 status = pj_ioqueue_recvfrom(resv->udp_key, &resv->udp_op_rx_key,
302 resv->udp_rx_pkt, &rx_pkt_size,
303 PJ_IOQUEUE_ALWAYS_ASYNC,
304 &resv->udp_src_addr, &resv->udp_addr_len);
305 if (status != PJ_EPENDING)
306 return status;
307
308
309 #if PJ_HAS_IPV6
310 /* Also setup IPv6 socket */
311
312 /* Create the UDP socket */
313 status = pj_sock_socket(pj_AF_INET6(), pj_SOCK_DGRAM(), 0,
314 &resv->udp6_sock);
315 if (status != PJ_SUCCESS) {
316 /* Skip IPv6 socket on system without IPv6 (see ticket #1953) */
317 if (status == PJ_STATUS_FROM_OS(OSERR_EAFNOSUPPORT)) {
318 PJ_LOG(3,(resv->name.ptr,
319 "System does not support IPv6, resolver will "
320 "ignore any IPv6 nameservers"));
321 return PJ_SUCCESS;
322 }
323 return status;
324 }
325
326 /* Bind to any address/port */
327 pj_sockaddr_init(pj_AF_INET6(), &bound_addr, NULL, 0);
328 status = pj_sock_bind(resv->udp6_sock, &bound_addr,
329 pj_sockaddr_get_len(&bound_addr));
330 if (status != PJ_SUCCESS)
331 return status;
332
333 /* Register to ioqueue */
334 pj_bzero(&socket_cb, sizeof(socket_cb));
335 socket_cb.on_read_complete = &on_read_complete;
336 status = pj_ioqueue_register_sock2(resv->pool, resv->ioqueue,
337 resv->udp6_sock, resv->grp_lock,
338 resv, &socket_cb, &resv->udp6_key);
339 if (status != PJ_SUCCESS)
340 return status;
341
342 pj_ioqueue_op_key_init(&resv->udp6_op_rx_key,
343 sizeof(resv->udp6_op_rx_key));
344 pj_ioqueue_op_key_init(&resv->udp6_op_tx_key,
345 sizeof(resv->udp6_op_tx_key));
346
347 /* Start asynchronous read to the UDP socket */
348 rx_pkt_size = sizeof(resv->udp6_rx_pkt);
349 resv->udp6_addr_len = sizeof(resv->udp6_src_addr);
350 status = pj_ioqueue_recvfrom(resv->udp6_key, &resv->udp6_op_rx_key,
351 resv->udp6_rx_pkt, &rx_pkt_size,
352 PJ_IOQUEUE_ALWAYS_ASYNC,
353 &resv->udp6_src_addr, &resv->udp6_addr_len);
354 if (status != PJ_EPENDING)
355 return status;
356 #else
357 PJ_UNUSED_ARG(bound_addr);
358 #endif
359
360 return PJ_SUCCESS;
361 }
362
363
364 /* Initialize DNS settings with default values */
pj_dns_settings_default(pj_dns_settings * s)365 PJ_DEF(void) pj_dns_settings_default(pj_dns_settings *s)
366 {
367 pj_bzero(s, sizeof(pj_dns_settings));
368 s->qretr_delay = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_DELAY;
369 s->qretr_count = PJ_DNS_RESOLVER_QUERY_RETRANSMIT_COUNT;
370 s->cache_max_ttl = PJ_DNS_RESOLVER_MAX_TTL;
371 s->good_ns_ttl = PJ_DNS_RESOLVER_GOOD_NS_TTL;
372 s->bad_ns_ttl = PJ_DNS_RESOLVER_BAD_NS_TTL;
373 }
374
375
376 /*
377 * Create the resolver.
378 */
pj_dns_resolver_create(pj_pool_factory * pf,const char * name,unsigned options,pj_timer_heap_t * timer,pj_ioqueue_t * ioqueue,pj_dns_resolver ** p_resolver)379 PJ_DEF(pj_status_t) pj_dns_resolver_create( pj_pool_factory *pf,
380 const char *name,
381 unsigned options,
382 pj_timer_heap_t *timer,
383 pj_ioqueue_t *ioqueue,
384 pj_dns_resolver **p_resolver)
385 {
386 pj_pool_t *pool;
387 pj_dns_resolver *resv;
388 pj_status_t status;
389
390 /* Sanity check */
391 PJ_ASSERT_RETURN(pf && p_resolver, PJ_EINVAL);
392
393 if (name == NULL)
394 name = THIS_FILE;
395
396 /* Create and initialize resolver instance */
397 pool = pj_pool_create(pf, name, 4000, 4000, NULL);
398 if (!pool)
399 return PJ_ENOMEM;
400
401 /* Create pool and name */
402 resv = PJ_POOL_ZALLOC_T(pool, struct pj_dns_resolver);
403 resv->pool = pool;
404 resv->udp_sock = PJ_INVALID_SOCKET;
405 pj_strdup2_with_null(pool, &resv->name, name);
406
407 /* Create group lock */
408 status = pj_grp_lock_create_w_handler(pool, NULL, resv,
409 &dns_resolver_on_destroy,
410 &resv->grp_lock);
411 if (status != PJ_SUCCESS)
412 goto on_error;
413
414 pj_grp_lock_add_ref(resv->grp_lock);
415
416 /* Timer, ioqueue, and settings */
417 resv->timer = timer;
418 resv->ioqueue = ioqueue;
419 resv->last_id = 1;
420
421 pj_dns_settings_default(&resv->settings);
422 resv->settings.options = options;
423
424 /* Create the timer heap if one is not specified */
425 if (resv->timer == NULL) {
426 resv->own_timer = PJ_TRUE;
427 status = pj_timer_heap_create(pool, TIMER_SIZE, &resv->timer);
428 if (status != PJ_SUCCESS)
429 goto on_error;
430 }
431
432 /* Create the ioqueue if one is not specified */
433 if (resv->ioqueue == NULL) {
434 resv->own_ioqueue = PJ_TRUE;
435 status = pj_ioqueue_create(pool, MAX_FD, &resv->ioqueue);
436 if (status != PJ_SUCCESS)
437 goto on_error;
438 }
439
440 /* Response cache hash table */
441 resv->hrescache = pj_hash_create(pool, RES_HASH_TABLE_SIZE);
442
443 /* Query hash table and free list. */
444 resv->hquerybyid = pj_hash_create(pool, Q_HASH_TABLE_SIZE);
445 resv->hquerybyres = pj_hash_create(pool, Q_HASH_TABLE_SIZE);
446 pj_list_init(&resv->query_free_nodes);
447
448 /* Initialize the UDP socket */
449 status = init_sock(resv);
450 if (status != PJ_SUCCESS)
451 goto on_error;
452
453 /* Looks like everything is okay */
454 *p_resolver = resv;
455 return PJ_SUCCESS;
456
457 on_error:
458 pj_dns_resolver_destroy(resv, PJ_FALSE);
459 return status;
460 }
461
462
dns_resolver_on_destroy(void * member)463 void dns_resolver_on_destroy(void *member)
464 {
465 pj_dns_resolver *resolver = (pj_dns_resolver*)member;
466 pj_pool_safe_release(&resolver->pool);
467 }
468
469
470 /*
471 * Destroy DNS resolver instance.
472 */
pj_dns_resolver_destroy(pj_dns_resolver * resolver,pj_bool_t notify)473 PJ_DEF(pj_status_t) pj_dns_resolver_destroy( pj_dns_resolver *resolver,
474 pj_bool_t notify)
475 {
476 pj_hash_iterator_t it_buf, *it;
477 PJ_ASSERT_RETURN(resolver, PJ_EINVAL);
478
479 if (notify) {
480 /*
481 * Notify pending queries if requested.
482 */
483 it = pj_hash_first(resolver->hquerybyid, &it_buf);
484 while (it) {
485 pj_dns_async_query *q = (pj_dns_async_query *)
486 pj_hash_this(resolver->hquerybyid, it);
487 pj_dns_async_query *cq;
488 if (q->cb)
489 (*q->cb)(q->user_data, PJ_ECANCELLED, NULL);
490
491 cq = q->child_head.next;
492 while (cq != (pj_dns_async_query*)&q->child_head) {
493 if (cq->cb)
494 (*cq->cb)(cq->user_data, PJ_ECANCELLED, NULL);
495 cq = cq->next;
496 }
497 it = pj_hash_next(resolver->hquerybyid, it);
498 }
499 }
500
501 /* Destroy cached entries */
502 it = pj_hash_first(resolver->hrescache, &it_buf);
503 while (it) {
504 struct cached_res *cache;
505
506 cache = (struct cached_res*) pj_hash_this(resolver->hrescache, it);
507 pj_hash_set(NULL, resolver->hrescache, &cache->key,
508 sizeof(cache->key), 0, NULL);
509 pj_pool_release(cache->pool);
510
511 it = pj_hash_first(resolver->hrescache, &it_buf);
512 }
513
514 if (resolver->own_timer && resolver->timer) {
515 pj_timer_heap_destroy(resolver->timer);
516 resolver->timer = NULL;
517 }
518
519 close_sock(resolver);
520
521 if (resolver->own_ioqueue && resolver->ioqueue) {
522 pj_ioqueue_destroy(resolver->ioqueue);
523 resolver->ioqueue = NULL;
524 }
525
526 pj_grp_lock_dec_ref(resolver->grp_lock);
527
528 return PJ_SUCCESS;
529 }
530
531
532
533 /*
534 * Configure name servers for the DNS resolver.
535 */
pj_dns_resolver_set_ns(pj_dns_resolver * resolver,unsigned count,const pj_str_t servers[],const pj_uint16_t ports[])536 PJ_DEF(pj_status_t) pj_dns_resolver_set_ns( pj_dns_resolver *resolver,
537 unsigned count,
538 const pj_str_t servers[],
539 const pj_uint16_t ports[])
540 {
541 unsigned i;
542 pj_time_val now;
543 pj_status_t status;
544
545 PJ_ASSERT_RETURN(resolver && count && servers, PJ_EINVAL);
546 PJ_ASSERT_RETURN(count < PJ_DNS_RESOLVER_MAX_NS, PJ_EINVAL);
547
548 pj_grp_lock_acquire(resolver->grp_lock);
549
550 if (count > PJ_DNS_RESOLVER_MAX_NS)
551 count = PJ_DNS_RESOLVER_MAX_NS;
552
553 resolver->ns_count = 0;
554 pj_bzero(resolver->ns, sizeof(resolver->ns));
555
556 pj_gettimeofday(&now);
557
558 for (i=0; i<count; ++i) {
559 struct nameserver *ns = &resolver->ns[i];
560
561 status = pj_sockaddr_init(pj_AF_INET(), &ns->addr, &servers[i],
562 (pj_uint16_t)(ports ? ports[i] : PORT));
563 if (status != PJ_SUCCESS)
564 status = pj_sockaddr_init(pj_AF_INET6(), &ns->addr, &servers[i],
565 (pj_uint16_t)(ports ? ports[i] : PORT));
566 if (status != PJ_SUCCESS) {
567 pj_grp_lock_release(resolver->grp_lock);
568 return PJLIB_UTIL_EDNSINNSADDR;
569 }
570
571 ns->state = STATE_ACTIVE;
572 ns->state_expiry = now;
573 ns->rt_delay.sec = 10;
574 }
575
576 resolver->ns_count = count;
577
578 pj_grp_lock_release(resolver->grp_lock);
579 return PJ_SUCCESS;
580 }
581
582
583
584 /*
585 * Modify the resolver settings.
586 */
pj_dns_resolver_set_settings(pj_dns_resolver * resolver,const pj_dns_settings * st)587 PJ_DEF(pj_status_t) pj_dns_resolver_set_settings(pj_dns_resolver *resolver,
588 const pj_dns_settings *st)
589 {
590 PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL);
591
592 pj_grp_lock_acquire(resolver->grp_lock);
593 pj_memcpy(&resolver->settings, st, sizeof(*st));
594 pj_grp_lock_release(resolver->grp_lock);
595 return PJ_SUCCESS;
596 }
597
598
599 /*
600 * Get the resolver current settings.
601 */
pj_dns_resolver_get_settings(pj_dns_resolver * resolver,pj_dns_settings * st)602 PJ_DEF(pj_status_t) pj_dns_resolver_get_settings( pj_dns_resolver *resolver,
603 pj_dns_settings *st)
604 {
605 PJ_ASSERT_RETURN(resolver && st, PJ_EINVAL);
606
607 pj_grp_lock_acquire(resolver->grp_lock);
608 pj_memcpy(st, &resolver->settings, sizeof(*st));
609 pj_grp_lock_release(resolver->grp_lock);
610 return PJ_SUCCESS;
611 }
612
613
614 /*
615 * Poll for events from the resolver.
616 */
pj_dns_resolver_handle_events(pj_dns_resolver * resolver,const pj_time_val * timeout)617 PJ_DEF(void) pj_dns_resolver_handle_events(pj_dns_resolver *resolver,
618 const pj_time_val *timeout)
619 {
620 PJ_ASSERT_ON_FAIL(resolver, return);
621
622 pj_grp_lock_acquire(resolver->grp_lock);
623 pj_timer_heap_poll(resolver->timer, NULL);
624 pj_grp_lock_release(resolver->grp_lock);
625
626 pj_ioqueue_poll(resolver->ioqueue, timeout);
627 }
628
629
630 /* Get one query node from the free node, if any, or allocate
631 * a new one.
632 */
alloc_qnode(pj_dns_resolver * resolver,unsigned options,void * user_data,pj_dns_callback * cb)633 static pj_dns_async_query *alloc_qnode(pj_dns_resolver *resolver,
634 unsigned options,
635 void *user_data,
636 pj_dns_callback *cb)
637 {
638 pj_dns_async_query *q;
639
640 /* Merge query options with resolver options */
641 options |= resolver->settings.options;
642
643 if (!pj_list_empty(&resolver->query_free_nodes)) {
644 q = resolver->query_free_nodes.next;
645 pj_list_erase(q);
646 pj_bzero(q, sizeof(*q));
647 } else {
648 q = PJ_POOL_ZALLOC_T(resolver->pool, pj_dns_async_query);
649 }
650
651 /* Init query */
652 q->resolver = resolver;
653 q->options = options;
654 q->user_data = user_data;
655 q->cb = cb;
656 pj_list_init(&q->child_head);
657
658 return q;
659 }
660
661
662 /*
663 * Transmit query.
664 */
transmit_query(pj_dns_resolver * resolver,pj_dns_async_query * q)665 static pj_status_t transmit_query(pj_dns_resolver *resolver,
666 pj_dns_async_query *q)
667 {
668 unsigned pkt_size;
669 unsigned i, server_cnt, send_cnt;
670 unsigned servers[PJ_DNS_RESOLVER_MAX_NS];
671 pj_time_val now;
672 pj_str_t name;
673 pj_time_val delay;
674 pj_status_t status;
675
676 /* Select which nameserver(s) to send requests to. */
677 server_cnt = PJ_ARRAY_SIZE(servers);
678 status = select_nameservers(resolver, &server_cnt, servers);
679 if (status != PJ_SUCCESS) {
680 return status;
681 }
682
683 if (server_cnt == 0) {
684 return PJLIB_UTIL_EDNSNOWORKINGNS;
685 }
686
687 /* Start retransmit/timeout timer for the query */
688 pj_assert(q->timer_entry.id == 0);
689 q->timer_entry.id = 1;
690 q->timer_entry.user_data = q;
691 q->timer_entry.cb = &on_timeout;
692
693 delay.sec = 0;
694 delay.msec = resolver->settings.qretr_delay;
695 pj_time_val_normalize(&delay);
696 status = pj_timer_heap_schedule_w_grp_lock(resolver->timer,
697 &q->timer_entry,
698 &delay, 1,
699 resolver->grp_lock);
700 if (status != PJ_SUCCESS) {
701 return status;
702 }
703
704 /* Check if the socket is available for sending */
705 if (pj_ioqueue_is_pending(resolver->udp_key, &resolver->udp_op_tx_key)
706 #if PJ_HAS_IPV6
707 || (resolver->udp6_key &&
708 pj_ioqueue_is_pending(resolver->udp6_key,
709 &resolver->udp6_op_tx_key))
710 #endif
711 )
712 {
713 ++q->transmit_cnt;
714 PJ_LOG(4,(resolver->name.ptr,
715 "Socket busy in transmitting DNS %s query for %s%s",
716 pj_dns_get_type_name(q->key.qtype),
717 q->key.name,
718 (q->transmit_cnt < resolver->settings.qretr_count?
719 ", will try again later":"")));
720 return PJ_SUCCESS;
721 }
722
723 /* Create DNS query packet */
724 pkt_size = sizeof(resolver->udp_tx_pkt);
725 name = pj_str(q->key.name);
726 status = pj_dns_make_query(resolver->udp_tx_pkt, &pkt_size,
727 q->id, q->key.qtype, &name);
728 if (status != PJ_SUCCESS) {
729 pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
730 return status;
731 }
732
733 /* Get current time. */
734 pj_gettimeofday(&now);
735
736 /* Send the packet to name servers */
737 send_cnt = 0;
738 for (i=0; i<server_cnt; ++i) {
739 char addr[PJ_INET6_ADDRSTRLEN];
740 pj_ssize_t sent = (pj_ssize_t) pkt_size;
741 struct nameserver *ns = &resolver->ns[servers[i]];
742
743 if (ns->addr.addr.sa_family == pj_AF_INET()) {
744 status = pj_ioqueue_sendto(resolver->udp_key,
745 &resolver->udp_op_tx_key,
746 resolver->udp_tx_pkt, &sent, 0,
747 &ns->addr,
748 pj_sockaddr_get_len(&ns->addr));
749 if (status == PJ_SUCCESS || status == PJ_EPENDING)
750 send_cnt++;
751 }
752 #if PJ_HAS_IPV6
753 else if (resolver->udp6_key) {
754 status = pj_ioqueue_sendto(resolver->udp6_key,
755 &resolver->udp6_op_tx_key,
756 resolver->udp_tx_pkt, &sent, 0,
757 &ns->addr,
758 pj_sockaddr_get_len(&ns->addr));
759 if (status == PJ_SUCCESS || status == PJ_EPENDING)
760 send_cnt++;
761 }
762 #endif
763 else {
764 continue;
765 }
766
767 PJ_PERROR(4,(resolver->name.ptr, status,
768 "%s %d bytes to NS %d (%s:%d): DNS %s query for %s",
769 (q->transmit_cnt==0? "Transmitting":"Re-transmitting"),
770 (int)pkt_size, servers[i],
771 pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
772 pj_sockaddr_get_port(&ns->addr),
773 pj_dns_get_type_name(q->key.qtype),
774 q->key.name));
775
776 if (ns->q_id == 0) {
777 ns->q_id = q->id;
778 ns->sent_time = now;
779 }
780 }
781
782 if (send_cnt == 0) {
783 pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
784 return PJLIB_UTIL_EDNSNOWORKINGNS;
785 }
786
787 ++q->transmit_cnt;
788
789 return PJ_SUCCESS;
790 }
791
792
793 /*
794 * Initialize resource key for hash table lookup.
795 */
init_res_key(struct res_key * key,int type,const pj_str_t * name)796 static void init_res_key(struct res_key *key, int type, const pj_str_t *name)
797 {
798 unsigned i;
799 pj_size_t len;
800 char *dst = key->name;
801 const char *src = name->ptr;
802
803 pj_bzero(key, sizeof(struct res_key));
804 key->qtype = (pj_uint16_t)type;
805
806 len = name->slen;
807 if (len > PJ_MAX_HOSTNAME) len = PJ_MAX_HOSTNAME;
808
809 /* Copy key, in lowercase */
810 for (i=0; i<len; ++i) {
811 *dst++ = (char)pj_tolower(*src++);
812 }
813 }
814
815
816 /* Allocate new cache entry */
alloc_entry(pj_dns_resolver * resolver)817 static struct cached_res *alloc_entry(pj_dns_resolver *resolver)
818 {
819 pj_pool_t *pool;
820 struct cached_res *cache;
821
822 pool = pj_pool_create(resolver->pool->factory, "dnscache",
823 RES_BUF_SZ, 256, NULL);
824 cache = PJ_POOL_ZALLOC_T(pool, struct cached_res);
825 cache->pool = pool;
826 cache->ref_cnt = 1;
827
828 return cache;
829 }
830
831 /* Re-allocate cache entry, to free cached packet */
reset_entry(struct cached_res ** p_cached)832 static void reset_entry(struct cached_res **p_cached)
833 {
834 pj_pool_t *pool;
835 struct cached_res *cache = *p_cached;
836 unsigned ref_cnt;
837
838 pool = cache->pool;
839 ref_cnt = cache->ref_cnt;
840
841 pj_pool_reset(pool);
842
843 cache = PJ_POOL_ZALLOC_T(pool, struct cached_res);
844 cache->pool = pool;
845 cache->ref_cnt = ref_cnt;
846 *p_cached = cache;
847 }
848
849 /* Put unused/expired cached entry to the free list */
free_entry(pj_dns_resolver * resolver,struct cached_res * cache)850 static void free_entry(pj_dns_resolver *resolver, struct cached_res *cache)
851 {
852 PJ_UNUSED_ARG(resolver);
853 pj_pool_release(cache->pool);
854 }
855
856
857 /*
858 * Create and start asynchronous DNS query for a single resource.
859 */
pj_dns_resolver_start_query(pj_dns_resolver * resolver,const pj_str_t * name,int type,unsigned options,pj_dns_callback * cb,void * user_data,pj_dns_async_query ** p_query)860 PJ_DEF(pj_status_t) pj_dns_resolver_start_query( pj_dns_resolver *resolver,
861 const pj_str_t *name,
862 int type,
863 unsigned options,
864 pj_dns_callback *cb,
865 void *user_data,
866 pj_dns_async_query **p_query)
867 {
868 pj_time_val now;
869 struct res_key key;
870 struct cached_res *cache;
871 pj_dns_async_query *q, *p_q = NULL;
872 pj_uint32_t hval;
873 pj_status_t status = PJ_SUCCESS;
874
875 /* Validate arguments */
876 PJ_ASSERT_RETURN(resolver && name && type, PJ_EINVAL);
877
878 /* Check name is not too long. */
879 PJ_ASSERT_RETURN(name->slen>0 && name->slen < PJ_MAX_HOSTNAME,
880 PJ_ENAMETOOLONG);
881
882 /* Check type */
883 PJ_ASSERT_RETURN(type > 0 && type < 0xFFFF, PJ_EINVAL);
884
885 /* Build resource key for looking up hash tables */
886 init_res_key(&key, type, name);
887
888 /* Start working with the resolver */
889 pj_grp_lock_acquire(resolver->grp_lock);
890
891 /* Get current time. */
892 pj_gettimeofday(&now);
893
894 /* First, check if we have cached response for the specified name/type,
895 * and the cached entry has not expired.
896 */
897 hval = 0;
898 cache = (struct cached_res *) pj_hash_get(resolver->hrescache, &key,
899 sizeof(key), &hval);
900 if (cache) {
901 /* We've found a cached entry. */
902
903 /* Check for expiration */
904 if (PJ_TIME_VAL_GT(cache->expiry_time, now)) {
905
906 /* Log */
907 PJ_LOG(5,(resolver->name.ptr,
908 "Picked up DNS %s record for %.*s from cache, ttl=%d",
909 pj_dns_get_type_name(type),
910 (int)name->slen, name->ptr,
911 (int)(cache->expiry_time.sec - now.sec)));
912
913 /* Map DNS Rcode in the response into PJLIB status name space */
914 status = PJ_DNS_GET_RCODE(cache->pkt->hdr.flags);
915 status = PJ_STATUS_FROM_DNS_RCODE(status);
916
917 /* Workaround for deadlock problem. Need to increment the cache's
918 * ref counter first before releasing mutex, so the cache won't be
919 * destroyed by other thread while in callback.
920 */
921 cache->ref_cnt++;
922 pj_grp_lock_release(resolver->grp_lock);
923
924 /* This cached response is still valid. Just return this
925 * response to caller.
926 */
927 if (cb) {
928 (*cb)(user_data, status, cache->pkt);
929 }
930
931 /* Done. No host resolution is necessary */
932 pj_grp_lock_acquire(resolver->grp_lock);
933
934 /* Decrement the ref counter. Also check if it is time to free
935 * the cache (as it has been expired).
936 */
937 cache->ref_cnt--;
938 if (cache->ref_cnt <= 0)
939 free_entry(resolver, cache);
940
941 /* Must return PJ_SUCCESS */
942 status = PJ_SUCCESS;
943
944 /*
945 * We cannot write to *p_query after calling cb because what
946 * p_query points to may have been freed by cb.
947 * Refer to ticket #1974.
948 */
949 pj_grp_lock_release(resolver->grp_lock);
950 return status;
951 }
952
953 /* At this point, we have a cached entry, but this entry has expired.
954 * Remove this entry from the cached list.
955 */
956 pj_hash_set(NULL, resolver->hrescache, &key, sizeof(key), 0, NULL);
957
958 /* Also free the cache, if it is not being used (by callback). */
959 cache->ref_cnt--;
960 if (cache->ref_cnt <= 0)
961 free_entry(resolver, cache);
962
963 /* Must continue with creating a query now */
964 }
965
966 /* Next, check if we have pending query on the same resource */
967 q = (pj_dns_async_query *) pj_hash_get(resolver->hquerybyres, &key,
968 sizeof(key), NULL);
969 if (q) {
970 /* Yes, there's another pending query to the same key.
971 * Just create a new child query and add this query to
972 * pending query's child queries.
973 */
974 pj_dns_async_query *nq;
975
976 nq = alloc_qnode(resolver, options, user_data, cb);
977 pj_list_push_back(&q->child_head, nq);
978
979 /* Done. This child query will be notified once the "parent"
980 * query completes.
981 */
982 p_q = nq;
983 status = PJ_SUCCESS;
984 goto on_return;
985 }
986
987 /* There's no pending query to the same key, initiate a new one. */
988 q = alloc_qnode(resolver, options, user_data, cb);
989
990 /* Save the ID and key */
991 /* TODO: dnsext-forgery-resilient: randomize id for security */
992 q->id = resolver->last_id++;
993 if (resolver->last_id == 0)
994 resolver->last_id = 1;
995 pj_memcpy(&q->key, &key, sizeof(struct res_key));
996
997 /* Send the query */
998 status = transmit_query(resolver, q);
999 if (status != PJ_SUCCESS) {
1000 pj_list_push_back(&resolver->query_free_nodes, q);
1001 goto on_return;
1002 }
1003
1004 /* Add query entry to the hash tables */
1005 pj_hash_set_np(resolver->hquerybyid, &q->id, sizeof(q->id),
1006 0, q->hbufid, q);
1007 pj_hash_set_np(resolver->hquerybyres, &q->key, sizeof(q->key),
1008 0, q->hbufkey, q);
1009
1010 p_q = q;
1011
1012 on_return:
1013 if (p_query)
1014 *p_query = p_q;
1015
1016 pj_grp_lock_release(resolver->grp_lock);
1017 return status;
1018 }
1019
1020
1021 /*
1022 * Cancel a pending query.
1023 */
pj_dns_resolver_cancel_query(pj_dns_async_query * query,pj_bool_t notify)1024 PJ_DEF(pj_status_t) pj_dns_resolver_cancel_query(pj_dns_async_query *query,
1025 pj_bool_t notify)
1026 {
1027 pj_dns_callback *cb;
1028
1029 PJ_ASSERT_RETURN(query, PJ_EINVAL);
1030
1031 pj_grp_lock_acquire(query->resolver->grp_lock);
1032
1033 if (query->timer_entry.id == 1) {
1034 pj_timer_heap_cancel_if_active(query->resolver->timer,
1035 &query->timer_entry, 0);
1036 }
1037
1038 cb = query->cb;
1039 query->cb = NULL;
1040
1041 if (notify)
1042 (*cb)(query->user_data, PJ_ECANCELLED, NULL);
1043
1044 pj_grp_lock_release(query->resolver->grp_lock);
1045 return PJ_SUCCESS;
1046 }
1047
1048
1049 /*
1050 * DNS response containing A packet.
1051 */
pj_dns_parse_a_response(const pj_dns_parsed_packet * pkt,pj_dns_a_record * rec)1052 PJ_DEF(pj_status_t) pj_dns_parse_a_response(const pj_dns_parsed_packet *pkt,
1053 pj_dns_a_record *rec)
1054 {
1055 enum { MAX_SEARCH = 20 };
1056 pj_str_t hostname, alias = {NULL, 0}, *resname;
1057 pj_size_t bufstart = 0;
1058 pj_size_t bufleft = sizeof(rec->buf_);
1059 unsigned i, ansidx, search_cnt=0;
1060
1061 PJ_ASSERT_RETURN(pkt && rec, PJ_EINVAL);
1062
1063 /* Init the record */
1064 pj_bzero(rec, sizeof(pj_dns_a_record));
1065
1066 /* Return error if there's error in the packet. */
1067 if (PJ_DNS_GET_RCODE(pkt->hdr.flags))
1068 return PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(pkt->hdr.flags));
1069
1070 /* Return error if there's no query section */
1071 if (pkt->hdr.qdcount == 0)
1072 return PJLIB_UTIL_EDNSINANSWER;
1073
1074 /* Return error if there's no answer */
1075 if (pkt->hdr.anscount == 0)
1076 return PJLIB_UTIL_EDNSNOANSWERREC;
1077
1078 /* Get the hostname from the query. */
1079 hostname = pkt->q[0].name;
1080
1081 /* Copy hostname to the record */
1082 if (hostname.slen > (int)bufleft) {
1083 return PJ_ENAMETOOLONG;
1084 }
1085
1086 pj_memcpy(&rec->buf_[bufstart], hostname.ptr, hostname.slen);
1087 rec->name.ptr = &rec->buf_[bufstart];
1088 rec->name.slen = hostname.slen;
1089
1090 bufstart += hostname.slen;
1091 bufleft -= hostname.slen;
1092
1093 /* Find the first RR which name matches the hostname */
1094 for (ansidx=0; ansidx < pkt->hdr.anscount; ++ansidx) {
1095 if (pj_stricmp(&pkt->ans[ansidx].name, &hostname)==0)
1096 break;
1097 }
1098
1099 if (ansidx == pkt->hdr.anscount)
1100 return PJLIB_UTIL_EDNSNOANSWERREC;
1101
1102 resname = &hostname;
1103
1104 /* Keep following CNAME records. */
1105 while (pkt->ans[ansidx].type == PJ_DNS_TYPE_CNAME &&
1106 search_cnt++ < MAX_SEARCH)
1107 {
1108 resname = &pkt->ans[ansidx].rdata.cname.name;
1109
1110 if (!alias.slen)
1111 alias = *resname;
1112
1113 for (i=0; i < pkt->hdr.anscount; ++i) {
1114 if (pj_stricmp(resname, &pkt->ans[i].name)==0) {
1115 break;
1116 }
1117 }
1118
1119 if (i==pkt->hdr.anscount)
1120 return PJLIB_UTIL_EDNSNOANSWERREC;
1121
1122 ansidx = i;
1123 }
1124
1125 if (search_cnt >= MAX_SEARCH)
1126 return PJLIB_UTIL_EDNSINANSWER;
1127
1128 if (pkt->ans[ansidx].type != PJ_DNS_TYPE_A)
1129 return PJLIB_UTIL_EDNSINANSWER;
1130
1131 /* Copy alias to the record, if present. */
1132 if (alias.slen) {
1133 if (alias.slen > (int)bufleft)
1134 return PJ_ENAMETOOLONG;
1135
1136 pj_memcpy(&rec->buf_[bufstart], alias.ptr, alias.slen);
1137 rec->alias.ptr = &rec->buf_[bufstart];
1138 rec->alias.slen = alias.slen;
1139
1140 bufstart += alias.slen;
1141 bufleft -= alias.slen;
1142 }
1143
1144 /* Get the IP addresses. */
1145 for (i=0; i < pkt->hdr.anscount; ++i) {
1146 if (pkt->ans[i].type == PJ_DNS_TYPE_A &&
1147 pj_stricmp(&pkt->ans[i].name, resname)==0 &&
1148 rec->addr_count < PJ_DNS_MAX_IP_IN_A_REC)
1149 {
1150 rec->addr[rec->addr_count++].s_addr =
1151 pkt->ans[i].rdata.a.ip_addr.s_addr;
1152 }
1153 }
1154
1155 if (rec->addr_count == 0)
1156 return PJLIB_UTIL_EDNSNOANSWERREC;
1157
1158 return PJ_SUCCESS;
1159 }
1160
1161
1162 /*
1163 * DNS response containing A and/or AAAA packet.
1164 */
pj_dns_parse_addr_response(const pj_dns_parsed_packet * pkt,pj_dns_addr_record * rec)1165 PJ_DEF(pj_status_t) pj_dns_parse_addr_response(
1166 const pj_dns_parsed_packet *pkt,
1167 pj_dns_addr_record *rec)
1168 {
1169 enum { MAX_SEARCH = 20 };
1170 pj_str_t hostname, alias = {NULL, 0}, *resname;
1171 pj_size_t bufstart = 0;
1172 pj_size_t bufleft;
1173 unsigned i, ansidx, cnt=0;
1174
1175 PJ_ASSERT_RETURN(pkt && rec, PJ_EINVAL);
1176
1177 /* Init the record */
1178 pj_bzero(rec, sizeof(*rec));
1179
1180 bufleft = sizeof(rec->buf_);
1181
1182 /* Return error if there's error in the packet. */
1183 if (PJ_DNS_GET_RCODE(pkt->hdr.flags))
1184 return PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(pkt->hdr.flags));
1185
1186 /* Return error if there's no query section */
1187 if (pkt->hdr.qdcount == 0)
1188 return PJLIB_UTIL_EDNSINANSWER;
1189
1190 /* Return error if there's no answer */
1191 if (pkt->hdr.anscount == 0)
1192 return PJLIB_UTIL_EDNSNOANSWERREC;
1193
1194 /* Get the hostname from the query. */
1195 hostname = pkt->q[0].name;
1196
1197 /* Copy hostname to the record */
1198 if (hostname.slen > (int)bufleft) {
1199 return PJ_ENAMETOOLONG;
1200 }
1201
1202 pj_memcpy(&rec->buf_[bufstart], hostname.ptr, hostname.slen);
1203 rec->name.ptr = &rec->buf_[bufstart];
1204 rec->name.slen = hostname.slen;
1205
1206 bufstart += hostname.slen;
1207 bufleft -= hostname.slen;
1208
1209 /* Find the first RR which name matches the hostname. */
1210 for (ansidx=0; ansidx < pkt->hdr.anscount; ++ansidx) {
1211 if (pj_stricmp(&pkt->ans[ansidx].name, &hostname)==0)
1212 break;
1213 }
1214
1215 if (ansidx == pkt->hdr.anscount)
1216 return PJLIB_UTIL_EDNSNOANSWERREC;
1217
1218 resname = &hostname;
1219
1220 /* Keep following CNAME records. */
1221 while (pkt->ans[ansidx].type == PJ_DNS_TYPE_CNAME &&
1222 cnt++ < MAX_SEARCH)
1223 {
1224 resname = &pkt->ans[ansidx].rdata.cname.name;
1225
1226 if (!alias.slen)
1227 alias = *resname;
1228
1229 for (i=0; i < pkt->hdr.anscount; ++i) {
1230 if (pj_stricmp(resname, &pkt->ans[i].name)==0)
1231 break;
1232 }
1233
1234 if (i==pkt->hdr.anscount)
1235 return PJLIB_UTIL_EDNSNOANSWERREC;
1236
1237 ansidx = i;
1238 }
1239
1240 if (cnt >= MAX_SEARCH)
1241 return PJLIB_UTIL_EDNSINANSWER;
1242
1243 if (pkt->ans[ansidx].type != PJ_DNS_TYPE_A &&
1244 pkt->ans[ansidx].type != PJ_DNS_TYPE_AAAA)
1245 {
1246 return PJLIB_UTIL_EDNSINANSWER;
1247 }
1248
1249 /* Copy alias to the record, if present. */
1250 if (alias.slen) {
1251 if (alias.slen > (int)bufleft)
1252 return PJ_ENAMETOOLONG;
1253
1254 pj_memcpy(&rec->buf_[bufstart], alias.ptr, alias.slen);
1255 rec->alias.ptr = &rec->buf_[bufstart];
1256 rec->alias.slen = alias.slen;
1257
1258 bufstart += alias.slen;
1259 bufleft -= alias.slen;
1260 }
1261
1262 /* Get the IP addresses. */
1263 cnt = 0;
1264 for (i=0; i < pkt->hdr.anscount && cnt < PJ_DNS_MAX_IP_IN_A_REC ; ++i) {
1265 if ((pkt->ans[i].type == PJ_DNS_TYPE_A ||
1266 pkt->ans[i].type == PJ_DNS_TYPE_AAAA) &&
1267 pj_stricmp(&pkt->ans[i].name, resname)==0)
1268 {
1269 if (pkt->ans[i].type == PJ_DNS_TYPE_A) {
1270 rec->addr[cnt].af = pj_AF_INET();
1271 rec->addr[cnt].ip.v4 = pkt->ans[i].rdata.a.ip_addr;
1272 } else {
1273 rec->addr[cnt].af = pj_AF_INET6();
1274 rec->addr[cnt].ip.v6 = pkt->ans[i].rdata.aaaa.ip_addr;
1275 }
1276 ++cnt;
1277 }
1278 }
1279 rec->addr_count = cnt;
1280
1281 if (cnt == 0)
1282 return PJLIB_UTIL_EDNSNOANSWERREC;
1283
1284 return PJ_SUCCESS;
1285 }
1286
1287
1288 /* Set nameserver state */
set_nameserver_state(pj_dns_resolver * resolver,unsigned index,enum ns_state state,const pj_time_val * now)1289 static void set_nameserver_state(pj_dns_resolver *resolver,
1290 unsigned index,
1291 enum ns_state state,
1292 const pj_time_val *now)
1293 {
1294 struct nameserver *ns = &resolver->ns[index];
1295 enum ns_state old_state = ns->state;
1296 char addr[PJ_INET6_ADDRSTRLEN];
1297
1298 ns->state = state;
1299 ns->state_expiry = *now;
1300
1301 if (state == STATE_PROBING)
1302 ns->state_expiry.sec += ((resolver->settings.qretr_count + 2) *
1303 resolver->settings.qretr_delay) / 1000;
1304 else if (state == STATE_ACTIVE)
1305 ns->state_expiry.sec += resolver->settings.good_ns_ttl;
1306 else
1307 ns->state_expiry.sec += resolver->settings.bad_ns_ttl;
1308
1309 PJ_LOG(5, (resolver->name.ptr, "Nameserver %s:%d state changed %s --> %s",
1310 pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
1311 pj_sockaddr_get_port(&ns->addr),
1312 state_names[old_state], state_names[state]));
1313 }
1314
1315
1316 /* Select which nameserver(s) to use. Note this may return multiple
1317 * name servers. The algorithm to select which nameservers to be
1318 * sent the request to is as follows:
1319 * - select the first nameserver that is known to be good for the
1320 * last PJ_DNS_RESOLVER_GOOD_NS_TTL interval.
1321 * - for all NSes, if last_known_good >= PJ_DNS_RESOLVER_GOOD_NS_TTL,
1322 * include the NS to re-check again that the server is still good,
1323 * unless the NS is known to be bad in the last PJ_DNS_RESOLVER_BAD_NS_TTL
1324 * interval.
1325 * - for all NSes, if last_known_bad >= PJ_DNS_RESOLVER_BAD_NS_TTL,
1326 * also include the NS to re-check again that the server is still bad.
1327 */
select_nameservers(pj_dns_resolver * resolver,unsigned * count,unsigned servers[])1328 static pj_status_t select_nameservers(pj_dns_resolver *resolver,
1329 unsigned *count,
1330 unsigned servers[])
1331 {
1332 unsigned i, max_count=*count;
1333 int min;
1334 pj_time_val now;
1335
1336 pj_assert(max_count > 0);
1337
1338 *count = 0;
1339 servers[0] = 0xFFFF;
1340
1341 /* Check that nameservers are configured. */
1342 if (resolver->ns_count == 0)
1343 return PJLIB_UTIL_EDNSNONS;
1344
1345 pj_gettimeofday(&now);
1346
1347 /* Select one Active nameserver with best response time. */
1348 for (min=-1, i=0; i<resolver->ns_count; ++i) {
1349 struct nameserver *ns = &resolver->ns[i];
1350
1351 if (ns->state != STATE_ACTIVE)
1352 continue;
1353
1354 if (min == -1)
1355 min = i;
1356 else if (PJ_TIME_VAL_LT(ns->rt_delay, resolver->ns[min].rt_delay))
1357 min = i;
1358 }
1359 if (min != -1) {
1360 servers[0] = min;
1361 ++(*count);
1362 }
1363
1364 /* Scan nameservers. */
1365 for (i=0; i<resolver->ns_count && *count < max_count; ++i) {
1366 struct nameserver *ns = &resolver->ns[i];
1367
1368 if (PJ_TIME_VAL_LTE(ns->state_expiry, now)) {
1369 if (ns->state == STATE_PROBING) {
1370 set_nameserver_state(resolver, i, STATE_BAD, &now);
1371 } else {
1372 set_nameserver_state(resolver, i, STATE_PROBING, &now);
1373 if ((int)i != min) {
1374 servers[*count] = i;
1375 ++(*count);
1376 }
1377 }
1378 } else if (ns->state == STATE_PROBING && (int)i != min) {
1379 servers[*count] = i;
1380 ++(*count);
1381 }
1382 }
1383
1384 return PJ_SUCCESS;
1385 }
1386
1387
1388 /* Update name server status */
report_nameserver_status(pj_dns_resolver * resolver,const pj_sockaddr * ns_addr,const pj_dns_parsed_packet * pkt)1389 static void report_nameserver_status(pj_dns_resolver *resolver,
1390 const pj_sockaddr *ns_addr,
1391 const pj_dns_parsed_packet *pkt)
1392 {
1393 unsigned i;
1394 int rcode;
1395 pj_uint32_t q_id;
1396 pj_time_val now;
1397 pj_bool_t is_good;
1398
1399 /* Only mark nameserver as "bad" if it returned non-parseable response or
1400 * it returned the following status codes
1401 */
1402 if (pkt) {
1403 rcode = PJ_DNS_GET_RCODE(pkt->hdr.flags);
1404 q_id = pkt->hdr.id;
1405 } else {
1406 rcode = 0;
1407 q_id = (pj_uint32_t)-1;
1408 }
1409
1410 /* Some nameserver is reported to respond with PJ_DNS_RCODE_SERVFAIL for
1411 * missing AAAA record, and the standard doesn't seem to specify that
1412 * SERVFAIL should prevent the server to be contacted again for other
1413 * queries. So let's not mark nameserver as bad for SERVFAIL response.
1414 */
1415 if (!pkt || /* rcode == PJ_DNS_RCODE_SERVFAIL || */
1416 rcode == PJ_DNS_RCODE_REFUSED ||
1417 rcode == PJ_DNS_RCODE_NOTAUTH)
1418 {
1419 is_good = PJ_FALSE;
1420 } else {
1421 is_good = PJ_TRUE;
1422 }
1423
1424
1425 /* Mark time */
1426 pj_gettimeofday(&now);
1427
1428 /* Recheck all nameservers. */
1429 for (i=0; i<resolver->ns_count; ++i) {
1430 struct nameserver *ns = &resolver->ns[i];
1431
1432 if (pj_sockaddr_cmp(&ns->addr, ns_addr) == 0) {
1433 if (q_id == ns->q_id) {
1434 /* Calculate response time */
1435 pj_time_val rt = now;
1436 PJ_TIME_VAL_SUB(rt, ns->sent_time);
1437 ns->rt_delay = rt;
1438 ns->q_id = 0;
1439 }
1440 set_nameserver_state(resolver, i,
1441 (is_good ? STATE_ACTIVE : STATE_BAD), &now);
1442 break;
1443 }
1444 }
1445 }
1446
1447
1448 /* Update response cache */
update_res_cache(pj_dns_resolver * resolver,const struct res_key * key,pj_status_t status,pj_bool_t set_expiry,const pj_dns_parsed_packet * pkt)1449 static void update_res_cache(pj_dns_resolver *resolver,
1450 const struct res_key *key,
1451 pj_status_t status,
1452 pj_bool_t set_expiry,
1453 const pj_dns_parsed_packet *pkt)
1454 {
1455 struct cached_res *cache;
1456 pj_uint32_t hval=0, ttl;
1457
1458 /* If status is unsuccessful, clear the same entry from the cache */
1459 if (status != PJ_SUCCESS) {
1460 cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key,
1461 sizeof(*key), &hval);
1462 /* Remove the entry before releasing its pool (see ticket #1710) */
1463 pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
1464
1465 /* Free the entry */
1466 if (cache && --cache->ref_cnt <= 0)
1467 free_entry(resolver, cache);
1468 }
1469
1470
1471 /* Calculate expiration time. */
1472 if (set_expiry) {
1473 if (pkt->hdr.anscount == 0 || status != PJ_SUCCESS) {
1474 /* If we don't have answers for the name, then give a different
1475 * ttl value (note: PJ_DNS_RESOLVER_INVALID_TTL may be zero,
1476 * which means that invalid names won't be kept in the cache)
1477 */
1478 ttl = PJ_DNS_RESOLVER_INVALID_TTL;
1479
1480 } else {
1481 /* Otherwise get the minimum TTL from the answers */
1482 unsigned i;
1483 ttl = 0xFFFFFFFF;
1484 for (i=0; i<pkt->hdr.anscount; ++i) {
1485 if (pkt->ans[i].ttl < ttl)
1486 ttl = pkt->ans[i].ttl;
1487 }
1488 }
1489 } else {
1490 ttl = 0xFFFFFFFF;
1491 }
1492
1493 /* Apply maximum TTL */
1494 if (ttl > resolver->settings.cache_max_ttl)
1495 ttl = resolver->settings.cache_max_ttl;
1496
1497 /* Get a cache response entry */
1498 cache = (struct cached_res *) pj_hash_get(resolver->hrescache, key,
1499 sizeof(*key), &hval);
1500
1501 /* If TTL is zero, clear the same entry in the hash table */
1502 if (ttl == 0) {
1503 /* Remove the entry before releasing its pool (see ticket #1710) */
1504 pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
1505
1506 /* Free the entry */
1507 if (cache && --cache->ref_cnt <= 0)
1508 free_entry(resolver, cache);
1509 return;
1510 }
1511
1512 if (cache == NULL) {
1513 cache = alloc_entry(resolver);
1514 } else {
1515 /* Remove the entry before resetting its pool (see ticket #1710) */
1516 pj_hash_set(NULL, resolver->hrescache, key, sizeof(*key), hval, NULL);
1517
1518 if (cache->ref_cnt > 1) {
1519 /* When cache entry is being used by callback (to app),
1520 * just decrement ref_cnt so it will be freed after
1521 * the callback returns and allocate new entry.
1522 */
1523 cache->ref_cnt--;
1524 cache = alloc_entry(resolver);
1525 } else {
1526 /* Reset cache to avoid bloated cache pool */
1527 reset_entry(&cache);
1528 }
1529 }
1530
1531 /* Duplicate the packet.
1532 * We don't need to keep the NS and AR sections from the packet,
1533 * so exclude from duplication. We do need to keep the Query
1534 * section since DNS A parser needs the query section to know
1535 * the name being requested.
1536 */
1537 pj_dns_packet_dup(cache->pool, pkt,
1538 PJ_DNS_NO_NS | PJ_DNS_NO_AR,
1539 &cache->pkt);
1540
1541 /* Calculate expiration time */
1542 if (set_expiry) {
1543 pj_gettimeofday(&cache->expiry_time);
1544 cache->expiry_time.sec += ttl;
1545 } else {
1546 cache->expiry_time.sec = 0x7FFFFFFFL;
1547 cache->expiry_time.msec = 0;
1548 }
1549
1550 /* Copy key to the cached response */
1551 pj_memcpy(&cache->key, key, sizeof(*key));
1552
1553 /* Update the hash table */
1554 pj_hash_set_np(resolver->hrescache, &cache->key, sizeof(*key), hval,
1555 cache->hbuf, cache);
1556
1557 }
1558
1559
1560 /* Callback to be called when query has timed out */
on_timeout(pj_timer_heap_t * timer_heap,struct pj_timer_entry * entry)1561 static void on_timeout( pj_timer_heap_t *timer_heap,
1562 struct pj_timer_entry *entry)
1563 {
1564 pj_dns_resolver *resolver;
1565 pj_dns_async_query *q, *cq;
1566 pj_status_t status;
1567
1568 PJ_UNUSED_ARG(timer_heap);
1569
1570 q = (pj_dns_async_query *) entry->user_data;
1571 resolver = q->resolver;
1572
1573 pj_grp_lock_acquire(resolver->grp_lock);
1574
1575 /* Recheck that this query is still pending, since there is a slight
1576 * possibility of race condition (timer elapsed while at the same time
1577 * response arrives)
1578 */
1579 if (pj_hash_get(resolver->hquerybyid, &q->id, sizeof(q->id), NULL)==NULL) {
1580 /* Yeah, this query is done. */
1581 pj_grp_lock_release(resolver->grp_lock);
1582 return;
1583 }
1584
1585 /* Invalidate id. */
1586 q->timer_entry.id = 0;
1587
1588 /* Check to see if we should retransmit instead of time out */
1589 if (q->transmit_cnt < resolver->settings.qretr_count) {
1590 status = transmit_query(resolver, q);
1591 if (status == PJ_SUCCESS) {
1592 pj_grp_lock_release(resolver->grp_lock);
1593 return;
1594 } else {
1595 /* Error occurs */
1596 PJ_PERROR(4,(resolver->name.ptr, status,
1597 "Error transmitting request"));
1598
1599 /* Let it fallback to timeout section below */
1600 }
1601 }
1602
1603 /* Clear hash table entries */
1604 pj_hash_set(NULL, resolver->hquerybyid, &q->id, sizeof(q->id), 0, NULL);
1605 pj_hash_set(NULL, resolver->hquerybyres, &q->key, sizeof(q->key), 0, NULL);
1606
1607 /* Workaround for deadlock problem in #1565 (similar to #1108) */
1608 pj_grp_lock_release(resolver->grp_lock);
1609
1610 /* Call application callback, if any. */
1611 if (q->cb)
1612 (*q->cb)(q->user_data, PJ_ETIMEDOUT, NULL);
1613
1614 /* Call application callback for child queries. */
1615 cq = q->child_head.next;
1616 while (cq != (void*)&q->child_head) {
1617 if (cq->cb)
1618 (*cq->cb)(cq->user_data, PJ_ETIMEDOUT, NULL);
1619 cq = cq->next;
1620 }
1621
1622 /* Workaround for deadlock problem in #1565 (similar to #1108) */
1623 pj_grp_lock_acquire(resolver->grp_lock);
1624
1625 /* Clear data */
1626 q->timer_entry.id = 0;
1627 q->user_data = NULL;
1628
1629 /* Put child entries into recycle list */
1630 cq = q->child_head.next;
1631 while (cq != (void*)&q->child_head) {
1632 pj_dns_async_query *next = cq->next;
1633 pj_list_push_back(&resolver->query_free_nodes, cq);
1634 cq = next;
1635 }
1636
1637 /* Put query entry into recycle list */
1638 pj_list_push_back(&resolver->query_free_nodes, q);
1639
1640 pj_grp_lock_release(resolver->grp_lock);
1641 }
1642
1643
1644 /* Callback from ioqueue when packet is received */
on_read_complete(pj_ioqueue_key_t * key,pj_ioqueue_op_key_t * op_key,pj_ssize_t bytes_read)1645 static void on_read_complete(pj_ioqueue_key_t *key,
1646 pj_ioqueue_op_key_t *op_key,
1647 pj_ssize_t bytes_read)
1648 {
1649 pj_dns_resolver *resolver;
1650 pj_pool_t *pool = NULL;
1651 pj_dns_parsed_packet *dns_pkt;
1652 pj_dns_async_query *q;
1653 char addr[PJ_INET6_ADDRSTRLEN];
1654 pj_sockaddr *src_addr;
1655 int *src_addr_len;
1656 unsigned char *rx_pkt;
1657 pj_ssize_t rx_pkt_size;
1658 pj_status_t status;
1659 PJ_USE_EXCEPTION;
1660
1661
1662 resolver = (pj_dns_resolver *) pj_ioqueue_get_user_data(key);
1663 pj_assert(resolver);
1664
1665 #if PJ_HAS_IPV6
1666 if (key == resolver->udp6_key) {
1667 src_addr = &resolver->udp6_src_addr;
1668 src_addr_len = &resolver->udp6_addr_len;
1669 rx_pkt = resolver->udp6_rx_pkt;
1670 rx_pkt_size = sizeof(resolver->udp6_rx_pkt);
1671 } else
1672 #endif
1673 {
1674 src_addr = &resolver->udp_src_addr;
1675 src_addr_len = &resolver->udp_addr_len;
1676 rx_pkt = resolver->udp_rx_pkt;
1677 rx_pkt_size = sizeof(resolver->udp_rx_pkt);
1678 }
1679
1680 pj_grp_lock_acquire(resolver->grp_lock);
1681
1682
1683 /* Check for errors */
1684 if (bytes_read < 0) {
1685 status = (pj_status_t)-bytes_read;
1686 PJ_PERROR(4,(resolver->name.ptr, status, "DNS resolver read error"));
1687
1688 goto read_next_packet;
1689 }
1690
1691 PJ_LOG(5,(resolver->name.ptr,
1692 "Received %d bytes DNS response from %s:%d",
1693 (int)bytes_read,
1694 pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
1695 pj_sockaddr_get_port(src_addr)));
1696
1697
1698 /* Check for zero packet */
1699 if (bytes_read == 0)
1700 goto read_next_packet;
1701
1702 /* Create temporary pool from a fixed buffer */
1703 pool = pj_pool_create_on_buf("restmp", resolver->tmp_pool,
1704 sizeof(resolver->tmp_pool));
1705
1706 /* Parse DNS response */
1707 status = -1;
1708 dns_pkt = NULL;
1709 PJ_TRY {
1710 status = pj_dns_parse_packet(pool, rx_pkt,
1711 (unsigned)bytes_read, &dns_pkt);
1712 }
1713 PJ_CATCH_ANY {
1714 status = PJ_ENOMEM;
1715 }
1716 PJ_END;
1717
1718 /* Update nameserver status */
1719 report_nameserver_status(resolver, src_addr, dns_pkt);
1720
1721 /* Handle parse error */
1722 if (status != PJ_SUCCESS) {
1723 PJ_PERROR(3,(resolver->name.ptr, status,
1724 "Error parsing DNS response from %s:%d",
1725 pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
1726 pj_sockaddr_get_port(src_addr)));
1727 goto read_next_packet;
1728 }
1729
1730 /* Find the query based on the transaction ID */
1731 q = (pj_dns_async_query*)
1732 pj_hash_get(resolver->hquerybyid, &dns_pkt->hdr.id,
1733 sizeof(dns_pkt->hdr.id), NULL);
1734 if (!q) {
1735 PJ_LOG(5,(resolver->name.ptr,
1736 "DNS response from %s:%d id=%d discarded",
1737 pj_sockaddr_print(src_addr, addr, sizeof(addr), 2),
1738 pj_sockaddr_get_port(src_addr),
1739 (unsigned)dns_pkt->hdr.id));
1740 goto read_next_packet;
1741 }
1742
1743 /* Map DNS Rcode in the response into PJLIB status name space */
1744 status = PJ_STATUS_FROM_DNS_RCODE(PJ_DNS_GET_RCODE(dns_pkt->hdr.flags));
1745
1746 /* Cancel query timeout timer. */
1747 pj_assert(q->timer_entry.id != 0);
1748 pj_timer_heap_cancel(resolver->timer, &q->timer_entry);
1749 q->timer_entry.id = 0;
1750
1751 /* Clear hash table entries */
1752 pj_hash_set(NULL, resolver->hquerybyid, &q->id, sizeof(q->id), 0, NULL);
1753 pj_hash_set(NULL, resolver->hquerybyres, &q->key, sizeof(q->key), 0, NULL);
1754
1755 /* Workaround for deadlock problem in #1108 */
1756 pj_grp_lock_release(resolver->grp_lock);
1757
1758 /* Notify applications first, to allow application to modify the
1759 * record before it is saved to the hash table.
1760 */
1761 if (q->cb)
1762 (*q->cb)(q->user_data, status, dns_pkt);
1763
1764 /* If query has subqueries, notify subqueries's application callback */
1765 if (!pj_list_empty(&q->child_head)) {
1766 pj_dns_async_query *child_q;
1767
1768 child_q = q->child_head.next;
1769 while (child_q != (pj_dns_async_query*)&q->child_head) {
1770 if (child_q->cb)
1771 (*child_q->cb)(child_q->user_data, status, dns_pkt);
1772 child_q = child_q->next;
1773 }
1774 }
1775
1776 /* Workaround for deadlock problem in #1108 */
1777 pj_grp_lock_acquire(resolver->grp_lock);
1778
1779 /* Truncated responses MUST NOT be saved (cached). */
1780 if (PJ_DNS_GET_TC(dns_pkt->hdr.flags) == 0) {
1781 /* Save/update response cache. */
1782 update_res_cache(resolver, &q->key, status, PJ_TRUE, dns_pkt);
1783 }
1784
1785 /* Recycle query objects, starting with the child queries */
1786 if (!pj_list_empty(&q->child_head)) {
1787 pj_dns_async_query *child_q;
1788
1789 child_q = q->child_head.next;
1790 while (child_q != (pj_dns_async_query*)&q->child_head) {
1791 pj_dns_async_query *next = child_q->next;
1792 pj_list_erase(child_q);
1793 pj_list_push_back(&resolver->query_free_nodes, child_q);
1794 child_q = next;
1795 }
1796 }
1797 pj_list_push_back(&resolver->query_free_nodes, q);
1798
1799 read_next_packet:
1800 if (pool) {
1801 /* needed just in case PJ_HAS_POOL_ALT_API is set */
1802 pj_pool_release(pool);
1803 }
1804
1805 status = pj_ioqueue_recvfrom(key, op_key, rx_pkt, &rx_pkt_size,
1806 PJ_IOQUEUE_ALWAYS_ASYNC,
1807 src_addr, src_addr_len);
1808
1809 if (status != PJ_EPENDING && status != PJ_ECANCELLED) {
1810 PJ_PERROR(4,(resolver->name.ptr, status,
1811 "DNS resolver ioqueue read error"));
1812
1813 pj_assert(!"Unhandled error");
1814 }
1815
1816 pj_grp_lock_release(resolver->grp_lock);
1817 }
1818
1819
1820 /*
1821 * Put the specified DNS packet into DNS cache. This function is mainly used
1822 * for testing the resolver, however it can also be used to inject entries
1823 * into the resolver.
1824 */
pj_dns_resolver_add_entry(pj_dns_resolver * resolver,const pj_dns_parsed_packet * pkt,pj_bool_t set_ttl)1825 PJ_DEF(pj_status_t) pj_dns_resolver_add_entry( pj_dns_resolver *resolver,
1826 const pj_dns_parsed_packet *pkt,
1827 pj_bool_t set_ttl)
1828 {
1829 struct res_key key;
1830
1831 /* Sanity check */
1832 PJ_ASSERT_RETURN(resolver && pkt, PJ_EINVAL);
1833
1834 /* Packet must be a DNS response */
1835 PJ_ASSERT_RETURN(PJ_DNS_GET_QR(pkt->hdr.flags) & 1, PJ_EINVAL);
1836
1837 /* Make sure there are answers in the packet */
1838 PJ_ASSERT_RETURN((pkt->hdr.anscount && pkt->ans) ||
1839 (pkt->hdr.qdcount && pkt->q),
1840 PJLIB_UTIL_EDNSNOANSWERREC);
1841
1842 pj_grp_lock_acquire(resolver->grp_lock);
1843
1844 /* Build resource key for looking up hash tables */
1845 pj_bzero(&key, sizeof(struct res_key));
1846 if (pkt->hdr.anscount) {
1847 /* Make sure name is not too long. */
1848 PJ_ASSERT_RETURN(pkt->ans[0].name.slen < PJ_MAX_HOSTNAME,
1849 PJ_ENAMETOOLONG);
1850
1851 init_res_key(&key, pkt->ans[0].type, &pkt->ans[0].name);
1852
1853 } else {
1854 /* Make sure name is not too long. */
1855 PJ_ASSERT_RETURN(pkt->q[0].name.slen < PJ_MAX_HOSTNAME,
1856 PJ_ENAMETOOLONG);
1857
1858 init_res_key(&key, pkt->q[0].type, &pkt->q[0].name);
1859 }
1860
1861 /* Insert entry. */
1862 update_res_cache(resolver, &key, PJ_SUCCESS, set_ttl, pkt);
1863
1864 pj_grp_lock_release(resolver->grp_lock);
1865
1866 return PJ_SUCCESS;
1867 }
1868
1869
1870 /*
1871 * Get the total number of response in the response cache.
1872 */
pj_dns_resolver_get_cached_count(pj_dns_resolver * resolver)1873 PJ_DEF(unsigned) pj_dns_resolver_get_cached_count(pj_dns_resolver *resolver)
1874 {
1875 unsigned count;
1876
1877 PJ_ASSERT_RETURN(resolver, 0);
1878
1879 pj_grp_lock_acquire(resolver->grp_lock);
1880 count = pj_hash_count(resolver->hrescache);
1881 pj_grp_lock_release(resolver->grp_lock);
1882
1883 return count;
1884 }
1885
1886
1887 /*
1888 * Dump resolver state to the log.
1889 */
pj_dns_resolver_dump(pj_dns_resolver * resolver,pj_bool_t detail)1890 PJ_DEF(void) pj_dns_resolver_dump(pj_dns_resolver *resolver,
1891 pj_bool_t detail)
1892 {
1893 #if PJ_LOG_MAX_LEVEL >= 3
1894 unsigned i;
1895 pj_time_val now;
1896
1897 pj_grp_lock_acquire(resolver->grp_lock);
1898
1899 pj_gettimeofday(&now);
1900
1901 PJ_LOG(3,(resolver->name.ptr, " Dumping resolver state:"));
1902
1903 PJ_LOG(3,(resolver->name.ptr, " Name servers:"));
1904 for (i=0; i<resolver->ns_count; ++i) {
1905 char addr[PJ_INET6_ADDRSTRLEN];
1906 struct nameserver *ns = &resolver->ns[i];
1907
1908 PJ_LOG(3,(resolver->name.ptr,
1909 " NS %d: %s:%d (state=%s until %ds, rtt=%d ms)",
1910 i,
1911 pj_sockaddr_print(&ns->addr, addr, sizeof(addr), 2),
1912 pj_sockaddr_get_port(&ns->addr),
1913 state_names[ns->state],
1914 ns->state_expiry.sec - now.sec,
1915 PJ_TIME_VAL_MSEC(ns->rt_delay)));
1916 }
1917
1918 PJ_LOG(3,(resolver->name.ptr, " Nb. of cached responses: %u",
1919 pj_hash_count(resolver->hrescache)));
1920 if (detail) {
1921 pj_hash_iterator_t itbuf, *it;
1922 it = pj_hash_first(resolver->hrescache, &itbuf);
1923 while (it) {
1924 struct cached_res *cache;
1925 cache = (struct cached_res*)pj_hash_this(resolver->hrescache, it);
1926 PJ_LOG(3,(resolver->name.ptr,
1927 " Type %s: %s",
1928 pj_dns_get_type_name(cache->key.qtype),
1929 cache->key.name));
1930 it = pj_hash_next(resolver->hrescache, it);
1931 }
1932 }
1933 PJ_LOG(3,(resolver->name.ptr, " Nb. of pending queries: %u (%u)",
1934 pj_hash_count(resolver->hquerybyid),
1935 pj_hash_count(resolver->hquerybyres)));
1936 if (detail) {
1937 pj_hash_iterator_t itbuf, *it;
1938 it = pj_hash_first(resolver->hquerybyid, &itbuf);
1939 while (it) {
1940 struct pj_dns_async_query *q;
1941 q = (pj_dns_async_query*) pj_hash_this(resolver->hquerybyid, it);
1942 PJ_LOG(3,(resolver->name.ptr,
1943 " Type %s: %s",
1944 pj_dns_get_type_name(q->key.qtype),
1945 q->key.name));
1946 it = pj_hash_next(resolver->hquerybyid, it);
1947 }
1948 }
1949 PJ_LOG(3,(resolver->name.ptr, " Nb. of pending query free nodes: %u",
1950 pj_list_size(&resolver->query_free_nodes)));
1951 PJ_LOG(3,(resolver->name.ptr, " Nb. of timer entries: %u",
1952 pj_timer_heap_count(resolver->timer)));
1953 PJ_LOG(3,(resolver->name.ptr, " Pool capacity: %d, used size: %d",
1954 pj_pool_get_capacity(resolver->pool),
1955 pj_pool_get_used_size(resolver->pool)));
1956
1957 pj_grp_lock_release(resolver->grp_lock);
1958 #endif
1959 }
1960
1961