~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

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
 
}