~ubuntu-branches/ubuntu/vivid/sflphone/vivid

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjlib-util/src/pjlib-util/resolver.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

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