~ubuntu-branches/ubuntu/raring/sflphone/raring

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Francois Marier
  • Date: 2011-11-25 13:24:12 UTC
  • mfrom: (4.1.10 sid)
  • Revision ID: package-import@ubuntu.com-20111125132412-dc4qvhyosk74cd42
Tags: 1.0.1-4
Don't assume that arch:all packages will get built (closes: #649726)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: srv_resolver.c 3553 2011-05-05 06:14:19Z 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/srv_resolver.h>
 
21
#include <pjlib-util/errno.h>
 
22
#include <pj/array.h>
 
23
#include <pj/assert.h>
 
24
#include <pj/log.h>
 
25
#include <pj/os.h>
 
26
#include <pj/pool.h>
 
27
#include <pj/rand.h>
 
28
#include <pj/string.h>
 
29
 
 
30
 
 
31
#define THIS_FILE   "srv_resolver.c"
 
32
 
 
33
#define ADDR_MAX_COUNT      PJ_DNS_MAX_IP_IN_A_REC
 
34
 
 
35
struct common
 
36
{
 
37
    pj_dns_type              type;          /**< Type of this structure.*/
 
38
};
 
39
 
 
40
struct srv_target
 
41
{
 
42
    struct common           common;
 
43
    pj_dns_srv_async_query *parent;
 
44
    pj_str_t                target_name;
 
45
    pj_dns_async_query     *q_a;
 
46
    char                    target_buf[PJ_MAX_HOSTNAME];
 
47
    pj_str_t                cname;
 
48
    char                    cname_buf[PJ_MAX_HOSTNAME];
 
49
    unsigned                port;
 
50
    unsigned                priority;
 
51
    unsigned                weight;
 
52
    unsigned                sum;
 
53
    unsigned                addr_cnt;
 
54
    pj_in_addr              addr[ADDR_MAX_COUNT];
 
55
};
 
56
 
 
57
struct pj_dns_srv_async_query
 
58
{
 
59
    struct common            common;
 
60
    char                    *objname;
 
61
 
 
62
    pj_dns_type              dns_state;     /**< DNS type being resolved.   */
 
63
    pj_dns_resolver         *resolver;      /**< Resolver SIP instance.     */
 
64
    void                    *token;
 
65
    pj_dns_async_query      *q_srv;
 
66
    pj_dns_srv_resolver_cb  *cb;
 
67
    pj_status_t              last_error;
 
68
 
 
69
    /* Original request: */
 
70
    unsigned                 option;
 
71
    pj_str_t                 full_name;
 
72
    pj_str_t                 domain_part;
 
73
    pj_uint16_t              def_port;
 
74
 
 
75
    /* SRV records and their resolved IP addresses: */
 
76
    unsigned                 srv_cnt;
 
77
    struct srv_target        srv[PJ_DNS_SRV_MAX_ADDR];
 
78
 
 
79
    /* Number of hosts in SRV records that the IP address has been resolved */
 
80
    unsigned                 host_resolved;
 
81
 
 
82
};
 
83
 
 
84
 
 
85
/* Async resolver callback, forward decl. */
 
86
static void dns_callback(void *user_data,
 
87
                         pj_status_t status,
 
88
                         pj_dns_parsed_packet *pkt);
 
89
 
 
90
 
 
91
 
 
92
/*
 
93
 * The public API to invoke DNS SRV resolution.
 
94
 */
 
95
PJ_DEF(pj_status_t) pj_dns_srv_resolve( const pj_str_t *domain_name,
 
96
                                        const pj_str_t *res_name,
 
97
                                        unsigned def_port,
 
98
                                        pj_pool_t *pool,
 
99
                                        pj_dns_resolver *resolver,
 
100
                                        unsigned option,
 
101
                                        void *token,
 
102
                                        pj_dns_srv_resolver_cb *cb,
 
103
                                        pj_dns_srv_async_query **p_query)
 
104
{
 
105
    int len;
 
106
    pj_str_t target_name;
 
107
    pj_dns_srv_async_query *query_job;
 
108
    pj_status_t status;
 
109
 
 
110
    PJ_ASSERT_RETURN(domain_name && domain_name->slen &&
 
111
                     res_name && res_name->slen &&
 
112
                     pool && resolver && cb, PJ_EINVAL);
 
113
 
 
114
    /* Build full name */
 
115
    len = domain_name->slen + res_name->slen + 2;
 
116
    target_name.ptr = (char*) pj_pool_alloc(pool, len);
 
117
    pj_strcpy(&target_name, res_name);
 
118
    if (res_name->ptr[res_name->slen-1] != '.')
 
119
        pj_strcat2(&target_name, ".");
 
120
    len = target_name.slen;
 
121
    pj_strcat(&target_name, domain_name);
 
122
    target_name.ptr[target_name.slen] = '\0';
 
123
 
 
124
 
 
125
    /* Build the query_job state */
 
126
    query_job = PJ_POOL_ZALLOC_T(pool, pj_dns_srv_async_query);
 
127
    query_job->common.type = PJ_DNS_TYPE_SRV;
 
128
    query_job->objname = target_name.ptr;
 
129
    query_job->resolver = resolver;
 
130
    query_job->token = token;
 
131
    query_job->cb = cb;
 
132
    query_job->option = option;
 
133
    query_job->full_name = target_name;
 
134
    query_job->domain_part.ptr = target_name.ptr + len;
 
135
    query_job->domain_part.slen = target_name.slen - len;
 
136
    query_job->def_port = (pj_uint16_t)def_port;
 
137
 
 
138
    /* Start the asynchronous query_job */
 
139
 
 
140
    query_job->dns_state = PJ_DNS_TYPE_SRV;
 
141
 
 
142
    PJ_LOG(5, (query_job->objname, 
 
143
               "Starting async DNS %s query_job: target=%.*s:%d",
 
144
               pj_dns_get_type_name(query_job->dns_state),
 
145
               (int)target_name.slen, target_name.ptr,
 
146
               def_port));
 
147
 
 
148
    status = pj_dns_resolver_start_query(resolver, &target_name, 
 
149
                                         query_job->dns_state, 0, 
 
150
                                         &dns_callback,
 
151
                                         query_job, &query_job->q_srv);
 
152
    if (status==PJ_SUCCESS && p_query)
 
153
        *p_query = query_job;
 
154
 
 
155
    return status;
 
156
}
 
157
 
 
158
 
 
159
/*
 
160
 * Cancel pending query.
 
161
 */
 
162
PJ_DEF(pj_status_t) pj_dns_srv_cancel_query(pj_dns_srv_async_query *query,
 
163
                                            pj_bool_t notify)
 
164
{
 
165
    pj_bool_t has_pending = PJ_FALSE;
 
166
    unsigned i;
 
167
 
 
168
    if (query->q_srv) {
 
169
        pj_dns_resolver_cancel_query(query->q_srv, PJ_FALSE);
 
170
        query->q_srv = NULL;
 
171
        has_pending = PJ_TRUE;
 
172
    }
 
173
 
 
174
    for (i=0; i<query->srv_cnt; ++i) {
 
175
        struct srv_target *srv = &query->srv[i];
 
176
        if (srv->q_a) {
 
177
            pj_dns_resolver_cancel_query(srv->q_a, PJ_FALSE);
 
178
            srv->q_a = NULL;
 
179
            has_pending = PJ_TRUE;
 
180
        }
 
181
    }
 
182
 
 
183
    if (has_pending && notify && query->cb) {
 
184
        (*query->cb)(query->token, PJ_ECANCELLED, NULL);
 
185
    }
 
186
 
 
187
    return has_pending? PJ_SUCCESS : PJ_EINVALIDOP;
 
188
}
 
189
 
 
190
 
 
191
#define SWAP(type,ptr1,ptr2) if (ptr1 != ptr2) { \
 
192
                                type tmp; \
 
193
                                pj_memcpy(&tmp, ptr1, sizeof(type)); \
 
194
                                pj_memcpy(ptr1, ptr2, sizeof(type)); \
 
195
                                (ptr1)->target_name.ptr = (ptr1)->target_buf;\
 
196
                                pj_memcpy(ptr2, &tmp, sizeof(type)); \
 
197
                                (ptr2)->target_name.ptr = (ptr2)->target_buf;\
 
198
                             } else {}
 
199
 
 
200
 
 
201
/* Build server entries in the query_job based on received SRV response */
 
202
static void build_server_entries(pj_dns_srv_async_query *query_job, 
 
203
                                 pj_dns_parsed_packet *response)
 
204
{
 
205
    unsigned i;
 
206
 
 
207
    /* Save the Resource Records in DNS answer into SRV targets. */
 
208
    query_job->srv_cnt = 0;
 
209
    for (i=0; i<response->hdr.anscount && 
 
210
              query_job->srv_cnt < PJ_DNS_SRV_MAX_ADDR; ++i) 
 
211
    {
 
212
        pj_dns_parsed_rr *rr = &response->ans[i];
 
213
        struct srv_target *srv = &query_job->srv[query_job->srv_cnt];
 
214
 
 
215
        if (rr->type != PJ_DNS_TYPE_SRV) {
 
216
            PJ_LOG(4,(query_job->objname, 
 
217
                      "Received non SRV answer for SRV query_job!"));
 
218
            continue;
 
219
        }
 
220
 
 
221
        if (rr->rdata.srv.target.slen > PJ_MAX_HOSTNAME) {
 
222
            PJ_LOG(4,(query_job->objname, "Hostname is too long!"));
 
223
            continue;
 
224
        }
 
225
 
 
226
        /* Build the SRV entry for RR */
 
227
        pj_bzero(srv, sizeof(*srv));
 
228
        srv->target_name.ptr = srv->target_buf;
 
229
        pj_strncpy(&srv->target_name, &rr->rdata.srv.target,
 
230
                   sizeof(srv->target_buf));
 
231
        srv->port = rr->rdata.srv.port;
 
232
        srv->priority = rr->rdata.srv.prio;
 
233
        srv->weight = rr->rdata.srv.weight;
 
234
        
 
235
        ++query_job->srv_cnt;
 
236
    }
 
237
 
 
238
    if (query_job->srv_cnt == 0) {
 
239
        PJ_LOG(4,(query_job->objname, 
 
240
                  "Could not find SRV record in DNS answer!"));
 
241
        return;
 
242
    }
 
243
 
 
244
    /* First pass: 
 
245
     *  order the entries based on priority.
 
246
     */
 
247
    for (i=0; i<query_job->srv_cnt-1; ++i) {
 
248
        unsigned min = i, j;
 
249
        for (j=i+1; j<query_job->srv_cnt; ++j) {
 
250
            if (query_job->srv[j].priority < query_job->srv[min].priority)
 
251
                min = j;
 
252
        }
 
253
        SWAP(struct srv_target, &query_job->srv[i], &query_job->srv[min]);
 
254
    }
 
255
 
 
256
    /* Second pass:
 
257
     *  pick one host among hosts with the same priority, according
 
258
     *  to its weight. The idea is when one server fails, client should
 
259
     *  contact the next server with higher priority rather than contacting
 
260
     *  server with the same priority as the failed one.
 
261
     *
 
262
     *  The algorithm for selecting server among servers with the same
 
263
     *  priority is described in RFC 2782.
 
264
     */
 
265
    for (i=0; i<query_job->srv_cnt; ++i) {
 
266
        unsigned j, count=1, sum;
 
267
 
 
268
        /* Calculate running sum for servers with the same priority */
 
269
        sum = query_job->srv[i].sum = query_job->srv[i].weight;
 
270
        for (j=i+1; j<query_job->srv_cnt && 
 
271
                    query_job->srv[j].priority == query_job->srv[i].priority; ++j)
 
272
        {
 
273
            sum += query_job->srv[j].weight;
 
274
            query_job->srv[j].sum = sum;
 
275
            ++count;
 
276
        }
 
277
 
 
278
        if (count > 1) {
 
279
            unsigned r;
 
280
 
 
281
            /* Elect one random number between zero and the total sum of
 
282
             * weight (inclusive).
 
283
             */
 
284
            r = pj_rand() % (sum + 1);
 
285
 
 
286
            /* Select the first server which running sum is greater than or
 
287
             * equal to the random number.
 
288
             */
 
289
            for (j=i; j<i+count; ++j) {
 
290
                if (query_job->srv[j].sum >= r)
 
291
                    break;
 
292
            }
 
293
 
 
294
            /* Must have selected one! */
 
295
            pj_assert(j != i+count);
 
296
 
 
297
            /* Put this entry in front (of entries with same priority) */
 
298
            SWAP(struct srv_target, &query_job->srv[i], &query_job->srv[j]);
 
299
 
 
300
            /* Remove all other entries (of the same priority) */
 
301
            while (count > 1) {
 
302
                pj_array_erase(query_job->srv, sizeof(struct srv_target), 
 
303
                               query_job->srv_cnt, i+1);
 
304
                --count;
 
305
                --query_job->srv_cnt;
 
306
            }
 
307
        }
 
308
    }
 
309
 
 
310
    /* Since we've been moving around SRV entries, update the pointers
 
311
     * in target_name.
 
312
     */
 
313
    for (i=0; i<query_job->srv_cnt; ++i) {
 
314
        query_job->srv[i].target_name.ptr = query_job->srv[i].target_buf;
 
315
    }
 
316
 
 
317
    /* Check for Additional Info section if A records are available, and
 
318
     * fill in the IP address (so that we won't need to resolve the A 
 
319
     * record with another DNS query_job). 
 
320
     */
 
321
    for (i=0; i<response->hdr.arcount; ++i) {
 
322
        pj_dns_parsed_rr *rr = &response->arr[i];
 
323
        unsigned j;
 
324
 
 
325
        if (rr->type != PJ_DNS_TYPE_A)
 
326
            continue;
 
327
 
 
328
        /* Yippeaiyee!! There is an "A" record! 
 
329
         * Update the IP address of the corresponding SRV record.
 
330
         */
 
331
        for (j=0; j<query_job->srv_cnt; ++j) {
 
332
            if (pj_stricmp(&rr->name, &query_job->srv[j].target_name)==0) {
 
333
                unsigned cnt = query_job->srv[j].addr_cnt;
 
334
                query_job->srv[j].addr[cnt].s_addr = rr->rdata.a.ip_addr.s_addr;
 
335
                /* Only increment host_resolved once per SRV record */
 
336
                if (query_job->srv[j].addr_cnt == 0)
 
337
                    ++query_job->host_resolved;
 
338
                ++query_job->srv[j].addr_cnt;
 
339
                break;
 
340
            }
 
341
        }
 
342
 
 
343
        /* Not valid message; SRV entry might have been deleted in
 
344
         * server selection process.
 
345
         */
 
346
        /*
 
347
        if (j == query_job->srv_cnt) {
 
348
            PJ_LOG(4,(query_job->objname, 
 
349
                      "Received DNS SRV answer with A record, but "
 
350
                      "couldn't find matching name (name=%.*s)",
 
351
                      (int)rr->name.slen,
 
352
                      rr->name.ptr));
 
353
        }
 
354
        */
 
355
    }
 
356
 
 
357
    /* Rescan again the name specified in the SRV record to see if IP
 
358
     * address is specified as the target name (unlikely, but well, who 
 
359
     * knows..).
 
360
     */
 
361
    for (i=0; i<query_job->srv_cnt; ++i) {
 
362
        pj_in_addr addr;
 
363
 
 
364
        if (query_job->srv[i].addr_cnt != 0) {
 
365
            /* IP address already resolved */
 
366
            continue;
 
367
        }
 
368
 
 
369
        if (pj_inet_aton(&query_job->srv[i].target_name, &addr) != 0) {
 
370
            query_job->srv[i].addr[query_job->srv[i].addr_cnt++] = addr;
 
371
            ++query_job->host_resolved;
 
372
        }
 
373
    }
 
374
 
 
375
    /* Print resolved entries to the log */
 
376
    PJ_LOG(5,(query_job->objname, 
 
377
              "SRV query_job for %.*s completed, "
 
378
              "%d of %d total entries selected%c",
 
379
              (int)query_job->full_name.slen,
 
380
              query_job->full_name.ptr,
 
381
              query_job->srv_cnt,
 
382
              response->hdr.anscount,
 
383
              (query_job->srv_cnt ? ':' : ' ')));
 
384
 
 
385
    for (i=0; i<query_job->srv_cnt; ++i) {
 
386
        const char *addr;
 
387
 
 
388
        if (query_job->srv[i].addr_cnt != 0)
 
389
            addr = pj_inet_ntoa(query_job->srv[i].addr[0]);
 
390
        else
 
391
            addr = "-";
 
392
 
 
393
        PJ_LOG(5,(query_job->objname, 
 
394
                  " %d: SRV %d %d %d %.*s (%s)",
 
395
                  i, query_job->srv[i].priority, 
 
396
                  query_job->srv[i].weight, 
 
397
                  query_job->srv[i].port, 
 
398
                  (int)query_job->srv[i].target_name.slen, 
 
399
                  query_job->srv[i].target_name.ptr,
 
400
                  addr));
 
401
    }
 
402
}
 
403
 
 
404
 
 
405
/* Start DNS A record queries for all SRV records in the query_job structure */
 
406
static pj_status_t resolve_hostnames(pj_dns_srv_async_query *query_job)
 
407
{
 
408
    unsigned i;
 
409
    pj_status_t err=PJ_SUCCESS, status;
 
410
 
 
411
    query_job->dns_state = PJ_DNS_TYPE_A;
 
412
    for (i=0; i<query_job->srv_cnt; ++i) {
 
413
        struct srv_target *srv = &query_job->srv[i];
 
414
 
 
415
        PJ_LOG(5, (query_job->objname, 
 
416
                   "Starting async DNS A query_job for %.*s",
 
417
                   (int)srv->target_name.slen, 
 
418
                   srv->target_name.ptr));
 
419
 
 
420
        srv->common.type = PJ_DNS_TYPE_A;
 
421
        srv->parent = query_job;
 
422
 
 
423
        status = pj_dns_resolver_start_query(query_job->resolver,
 
424
                                             &srv->target_name,
 
425
                                             PJ_DNS_TYPE_A, 0,
 
426
                                             &dns_callback,
 
427
                                             srv, &srv->q_a);
 
428
        if (status != PJ_SUCCESS) {
 
429
            query_job->host_resolved++;
 
430
            err = status;
 
431
        }
 
432
    }
 
433
    
 
434
    return (query_job->host_resolved == query_job->srv_cnt) ? err : PJ_SUCCESS;
 
435
}
 
436
 
 
437
/* 
 
438
 * This callback is called by PJLIB-UTIL DNS resolver when asynchronous
 
439
 * query_job has completed (successfully or with error).
 
440
 */
 
441
static void dns_callback(void *user_data,
 
442
                         pj_status_t status,
 
443
                         pj_dns_parsed_packet *pkt)
 
444
{
 
445
    struct common *common = (struct common*) user_data;
 
446
    pj_dns_srv_async_query *query_job;
 
447
    struct srv_target *srv = NULL;
 
448
    unsigned i;
 
449
 
 
450
    if (common->type == PJ_DNS_TYPE_SRV) {
 
451
        query_job = (pj_dns_srv_async_query*) common;
 
452
        srv = NULL;
 
453
    } else if (common->type == PJ_DNS_TYPE_A) {
 
454
        srv = (struct srv_target*) common;
 
455
        query_job = srv->parent;
 
456
    } else {
 
457
        pj_assert(!"Unexpected user data!");
 
458
        return;
 
459
    }
 
460
 
 
461
    /* Proceed to next stage */
 
462
    if (query_job->dns_state == PJ_DNS_TYPE_SRV) {
 
463
 
 
464
        /* We are getting SRV response */
 
465
 
 
466
        query_job->q_srv = NULL;
 
467
 
 
468
        if (status == PJ_SUCCESS && pkt->hdr.anscount != 0) {
 
469
            /* Got SRV response, build server entry. If A records are available
 
470
             * in additional records section of the DNS response, save them too.
 
471
             */
 
472
            build_server_entries(query_job, pkt);
 
473
 
 
474
        } else if (status != PJ_SUCCESS) {
 
475
            char errmsg[PJ_ERR_MSG_SIZE];
 
476
 
 
477
            /* Update query_job last error */
 
478
            query_job->last_error = status;
 
479
 
 
480
            pj_strerror(status, errmsg, sizeof(errmsg));
 
481
            PJ_LOG(4,(query_job->objname, 
 
482
                      "DNS SRV resolution failed for %.*s: %s", 
 
483
                      (int)query_job->full_name.slen, 
 
484
                      query_job->full_name.ptr,
 
485
                      errmsg));
 
486
 
 
487
            /* Trigger error when fallback is disabled */
 
488
            if ((query_job->option &
 
489
                 (PJ_DNS_SRV_FALLBACK_A | PJ_DNS_SRV_FALLBACK_AAAA)) == 0) 
 
490
            {
 
491
                goto on_error;
 
492
            }
 
493
        }
 
494
 
 
495
        /* If we can't build SRV record, assume the original target is
 
496
         * an A record and resolve with DNS A resolution.
 
497
         */
 
498
        if (query_job->srv_cnt == 0) {
 
499
            /* Looks like we aren't getting any SRV responses.
 
500
             * Resolve the original target as A record by creating a 
 
501
             * single "dummy" srv record and start the hostname resolution.
 
502
             */
 
503
            PJ_LOG(4, (query_job->objname, 
 
504
                       "DNS SRV resolution failed for %.*s, trying "
 
505
                       "resolving A record for %.*s",
 
506
                       (int)query_job->full_name.slen, 
 
507
                       query_job->full_name.ptr,
 
508
                       (int)query_job->domain_part.slen,
 
509
                       query_job->domain_part.ptr));
 
510
 
 
511
            /* Create a "dummy" srv record using the original target */
 
512
            i = query_job->srv_cnt++;
 
513
            pj_bzero(&query_job->srv[i], sizeof(query_job->srv[i]));
 
514
            query_job->srv[i].target_name = query_job->domain_part;
 
515
            query_job->srv[i].priority = 0;
 
516
            query_job->srv[i].weight = 0;
 
517
            query_job->srv[i].port = query_job->def_port;
 
518
        } 
 
519
        
 
520
 
 
521
        /* Resolve server hostnames (DNS A record) for hosts which don't have
 
522
         * A record yet.
 
523
         */
 
524
        if (query_job->host_resolved != query_job->srv_cnt) {
 
525
            status = resolve_hostnames(query_job);
 
526
            if (status != PJ_SUCCESS)
 
527
                goto on_error;
 
528
 
 
529
            /* Must return now. Callback may have been called and query_job
 
530
             * may have been destroyed.
 
531
             */
 
532
            return;
 
533
        }
 
534
 
 
535
    } else if (query_job->dns_state == PJ_DNS_TYPE_A) {
 
536
 
 
537
        /* Clear the outstanding job */
 
538
        srv->q_a = NULL;
 
539
 
 
540
        /* Check that we really have answer */
 
541
        if (status==PJ_SUCCESS && pkt->hdr.anscount != 0) {
 
542
            pj_dns_a_record rec;
 
543
 
 
544
            /* Parse response */
 
545
            status = pj_dns_parse_a_response(pkt, &rec);
 
546
            if (status != PJ_SUCCESS)
 
547
                goto on_error;
 
548
 
 
549
            pj_assert(rec.addr_count != 0);
 
550
 
 
551
            /* Update CNAME alias, if present. */
 
552
            if (rec.alias.slen) {
 
553
                pj_assert(rec.alias.slen <= (int)sizeof(srv->cname_buf));
 
554
                srv->cname.ptr = srv->cname_buf;
 
555
                pj_strcpy(&srv->cname, &rec.alias);
 
556
            } else {
 
557
                srv->cname.slen = 0;
 
558
            }
 
559
 
 
560
            /* Update IP address of the corresponding hostname or CNAME */
 
561
            if (srv->addr_cnt < ADDR_MAX_COUNT) {
 
562
                srv->addr[srv->addr_cnt++].s_addr = rec.addr[0].s_addr;
 
563
 
 
564
                PJ_LOG(5,(query_job->objname, 
 
565
                          "DNS A for %.*s: %s",
 
566
                          (int)srv->target_name.slen, 
 
567
                          srv->target_name.ptr,
 
568
                          pj_inet_ntoa(rec.addr[0])));
 
569
            }
 
570
 
 
571
            /* Check for multiple IP addresses */
 
572
            for (i=1; i<rec.addr_count && srv->addr_cnt < ADDR_MAX_COUNT; ++i)
 
573
            {
 
574
                srv->addr[srv->addr_cnt++].s_addr = rec.addr[i].s_addr;
 
575
 
 
576
                PJ_LOG(5,(query_job->objname, 
 
577
                          "Additional DNS A for %.*s: %s",
 
578
                          (int)srv->target_name.slen, 
 
579
                          srv->target_name.ptr,
 
580
                          pj_inet_ntoa(rec.addr[i])));
 
581
            }
 
582
 
 
583
        } else if (status != PJ_SUCCESS) {
 
584
            char errmsg[PJ_ERR_MSG_SIZE];
 
585
 
 
586
            /* Update last error */
 
587
            query_job->last_error = status;
 
588
 
 
589
            /* Log error */
 
590
            pj_strerror(status, errmsg, sizeof(errmsg));
 
591
            PJ_LOG(4,(query_job->objname, "DNS A record resolution failed: %s", 
 
592
                      errmsg));
 
593
        }
 
594
 
 
595
        ++query_job->host_resolved;
 
596
 
 
597
    } else {
 
598
        pj_assert(!"Unexpected state!");
 
599
        query_job->last_error = status = PJ_EINVALIDOP;
 
600
        goto on_error;
 
601
    }
 
602
 
 
603
    /* Check if all hosts have been resolved */
 
604
    if (query_job->host_resolved == query_job->srv_cnt) {
 
605
        /* Got all answers, build server addresses */
 
606
        pj_dns_srv_record srv_rec;
 
607
 
 
608
        srv_rec.count = 0;
 
609
        for (i=0; i<query_job->srv_cnt; ++i) {
 
610
            unsigned j;
 
611
            struct srv_target *srv = &query_job->srv[i];
 
612
 
 
613
            srv_rec.entry[srv_rec.count].priority = srv->priority;
 
614
            srv_rec.entry[srv_rec.count].weight = srv->weight;
 
615
            srv_rec.entry[srv_rec.count].port = (pj_uint16_t)srv->port ;
 
616
 
 
617
            srv_rec.entry[srv_rec.count].server.name = srv->target_name;
 
618
            srv_rec.entry[srv_rec.count].server.alias = srv->cname;
 
619
            srv_rec.entry[srv_rec.count].server.addr_count = 0;
 
620
 
 
621
            pj_assert(srv->addr_cnt <= PJ_DNS_MAX_IP_IN_A_REC);
 
622
 
 
623
            for (j=0; j<srv->addr_cnt; ++j) {
 
624
                srv_rec.entry[srv_rec.count].server.addr[j].s_addr = 
 
625
                    srv->addr[j].s_addr;
 
626
                ++srv_rec.entry[srv_rec.count].server.addr_count;
 
627
            }
 
628
 
 
629
            if (srv->addr_cnt > 0) {
 
630
                ++srv_rec.count;
 
631
                if (srv_rec.count == PJ_DNS_SRV_MAX_ADDR)
 
632
                    break;
 
633
            }
 
634
        }
 
635
 
 
636
        PJ_LOG(5,(query_job->objname, 
 
637
                  "Server resolution complete, %d server entry(s) found",
 
638
                  srv_rec.count));
 
639
 
 
640
 
 
641
        if (srv_rec.count > 0)
 
642
            status = PJ_SUCCESS;
 
643
        else {
 
644
            status = query_job->last_error;
 
645
            if (status == PJ_SUCCESS)
 
646
                status = PJLIB_UTIL_EDNSNOANSWERREC;
 
647
        }
 
648
 
 
649
        /* Call the callback */
 
650
        (*query_job->cb)(query_job->token, status, &srv_rec);
 
651
    }
 
652
 
 
653
 
 
654
    return;
 
655
 
 
656
on_error:
 
657
    /* Check for failure */
 
658
    if (status != PJ_SUCCESS) {
 
659
        char errmsg[PJ_ERR_MSG_SIZE];
 
660
        PJ_UNUSED_ARG(errmsg);
 
661
        PJ_LOG(4,(query_job->objname, 
 
662
                  "DNS %s record resolution error for '%.*s'."
 
663
                  " Err=%d (%s)",
 
664
                  pj_dns_get_type_name(query_job->dns_state),
 
665
                  (int)query_job->domain_part.slen,
 
666
                  query_job->domain_part.ptr,
 
667
                  status,
 
668
                  pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
 
669
        (*query_job->cb)(query_job->token, status, NULL);
 
670
        return;
 
671
    }
 
672
}
 
673
 
 
674