2 * Copyright (c) 1995 - 2004 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #ifdef HAVE_ARPA_NAMESER_H
39 #include <arpa/nameser.h>
48 RCSID("$Id: resolve.c,v 1.55 2006/04/14 13:56:00 lha Exp $");
50 #ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
51 #undef HAVE_RES_NSEARCH
54 #define DECL(X) {#X, rk_ns_t_##X}
78 int _resolve_debug
= 0;
80 int ROKEN_LIB_FUNCTION
81 dns_string_to_type(const char *name
)
83 struct stot
*p
= stot
;
84 for(p
= stot
; p
->name
; p
++)
85 if(strcasecmp(name
, p
->name
) == 0)
90 const char * ROKEN_LIB_FUNCTION
91 dns_type_to_string(int type
)
93 struct stot
*p
= stot
;
94 for(p
= stot
; p
->name
; p
++)
100 #if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)
103 dns_free_rr(struct resource_record
*rr
)
112 void ROKEN_LIB_FUNCTION
113 dns_free_data(struct dns_reply
*r
)
115 struct resource_record
*rr
;
118 for(rr
= r
->head
; rr
;){
119 struct resource_record
*tmp
= rr
;
127 parse_record(const unsigned char *data
, const unsigned char *end_data
,
128 const unsigned char **pp
, struct resource_record
**ret_rr
)
130 struct resource_record
*rr
;
131 int type
, class, ttl
, size
;
134 const unsigned char *p
= *pp
;
138 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
141 if (p
+ status
+ 10 > end_data
)
145 type
= (p
[0] << 8) | p
[1];
147 class = (p
[0] << 8) | p
[1];
149 ttl
= (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
151 size
= (p
[0] << 8) | p
[1];
154 if (p
+ size
> end_data
)
157 rr
= calloc(1, sizeof(*rr
));
160 rr
->domain
= strdup(host
);
161 if(rr
->domain
== NULL
) {
173 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
178 rr
->u
.txt
= strdup(host
);
179 if(rr
->u
.txt
== NULL
) {
188 status
= dn_expand(data
, end_data
, p
+ 2, host
, sizeof(host
));
193 if (status
+ 2 > size
) {
198 hostlen
= strlen(host
);
199 rr
->u
.mx
= (struct mx_record
*)malloc(sizeof(struct mx_record
) +
201 if(rr
->u
.mx
== NULL
) {
205 rr
->u
.mx
->preference
= (p
[0] << 8) | p
[1];
206 strlcpy(rr
->u
.mx
->domain
, host
, hostlen
+ 1);
211 status
= dn_expand(data
, end_data
, p
+ 6, host
, sizeof(host
));
216 if (status
+ 6 > size
) {
221 hostlen
= strlen(host
);
223 (struct srv_record
*)malloc(sizeof(struct srv_record
) +
225 if(rr
->u
.srv
== NULL
) {
229 rr
->u
.srv
->priority
= (p
[0] << 8) | p
[1];
230 rr
->u
.srv
->weight
= (p
[2] << 8) | p
[3];
231 rr
->u
.srv
->port
= (p
[4] << 8) | p
[5];
232 strlcpy(rr
->u
.srv
->target
, host
, hostlen
+ 1);
236 if(size
== 0 || size
< *p
+ 1) {
240 rr
->u
.txt
= (char*)malloc(*p
+ 1);
241 if(rr
->u
.txt
== NULL
) {
245 strncpy(rr
->u
.txt
, (const char*)(p
+ 1), *p
);
246 rr
->u
.txt
[*p
] = '\0';
258 rr
->u
.key
= malloc (sizeof(*rr
->u
.key
) + key_len
- 1);
259 if (rr
->u
.key
== NULL
) {
264 rr
->u
.key
->flags
= (p
[0] << 8) | p
[1];
265 rr
->u
.key
->protocol
= p
[2];
266 rr
->u
.key
->algorithm
= p
[3];
267 rr
->u
.key
->key_len
= key_len
;
268 memcpy (rr
->u
.key
->key_data
, p
+ 4, key_len
);
272 size_t sig_len
, hostlen
;
278 status
= dn_expand (data
, end_data
, p
+ 18, host
, sizeof(host
));
283 if (status
+ 18 > size
) {
288 /* the signer name is placed after the sig_data, to make it
289 easy to free this structure; the size calculation below
290 includes the zero-termination if the structure itself.
291 don't you just love C?
293 sig_len
= size
- 18 - status
;
294 hostlen
= strlen(host
);
295 rr
->u
.sig
= malloc(sizeof(*rr
->u
.sig
)
296 + hostlen
+ sig_len
);
297 if (rr
->u
.sig
== NULL
) {
301 rr
->u
.sig
->type
= (p
[0] << 8) | p
[1];
302 rr
->u
.sig
->algorithm
= p
[2];
303 rr
->u
.sig
->labels
= p
[3];
304 rr
->u
.sig
->orig_ttl
= (p
[4] << 24) | (p
[5] << 16)
305 | (p
[6] << 8) | p
[7];
306 rr
->u
.sig
->sig_expiration
= (p
[8] << 24) | (p
[9] << 16)
307 | (p
[10] << 8) | p
[11];
308 rr
->u
.sig
->sig_inception
= (p
[12] << 24) | (p
[13] << 16)
309 | (p
[14] << 8) | p
[15];
310 rr
->u
.sig
->key_tag
= (p
[16] << 8) | p
[17];
311 rr
->u
.sig
->sig_len
= sig_len
;
312 memcpy (rr
->u
.sig
->sig_data
, p
+ 18 + status
, sig_len
);
313 rr
->u
.sig
->signer
= &rr
->u
.sig
->sig_data
[sig_len
];
314 strlcpy(rr
->u
.sig
->signer
, host
, hostlen
+ 1);
318 case rk_ns_t_cert
: {
327 rr
->u
.cert
= malloc (sizeof(*rr
->u
.cert
) + cert_len
- 1);
328 if (rr
->u
.cert
== NULL
) {
333 rr
->u
.cert
->type
= (p
[0] << 8) | p
[1];
334 rr
->u
.cert
->tag
= (p
[2] << 8) | p
[3];
335 rr
->u
.cert
->algorithm
= p
[4];
336 rr
->u
.cert
->cert_len
= cert_len
;
337 memcpy (rr
->u
.cert
->cert_data
, p
+ 5, cert_len
);
340 case rk_ns_t_sshfp
: {
348 sshfp_len
= size
- 2;
350 rr
->u
.sshfp
= malloc (sizeof(*rr
->u
.sshfp
) + sshfp_len
- 1);
351 if (rr
->u
.sshfp
== NULL
) {
356 rr
->u
.sshfp
->algorithm
= p
[0];
357 rr
->u
.sshfp
->type
= p
[1];
358 rr
->u
.sshfp
->sshfp_len
= sshfp_len
;
359 memcpy (rr
->u
.sshfp
->sshfp_data
, p
+ 2, sshfp_len
);
370 digest_len
= size
- 4;
372 rr
->u
.ds
= malloc (sizeof(*rr
->u
.ds
) + digest_len
- 1);
373 if (rr
->u
.ds
== NULL
) {
378 rr
->u
.ds
->key_tag
= (p
[0] << 8) | p
[1];
379 rr
->u
.ds
->algorithm
= p
[2];
380 rr
->u
.ds
->digest_type
= p
[3];
381 rr
->u
.ds
->digest_len
= digest_len
;
382 memcpy (rr
->u
.ds
->digest_data
, p
+ 4, digest_len
);
386 rr
->u
.data
= (unsigned char*)malloc(size
);
387 if(size
!= 0 && rr
->u
.data
== NULL
) {
392 memcpy(rr
->u
.data
, p
, size
);
404 parse_reply(const unsigned char *data
, size_t len
)
406 const unsigned char *p
;
410 const unsigned char *end_data
= data
+ len
;
412 struct resource_record
**rr
;
414 r
= calloc(1, sizeof(*r
));
420 r
->h
.id
= (p
[0] << 8) | p
[1];
423 r
->h
.flags
|= rk_DNS_HEADER_RESPONSE_FLAG
;
424 r
->h
.opcode
= (p
[2] >> 1) & 0xf;
426 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
428 r
->h
.flags
|= rk_DNS_HEADER_TRUNCATED_MESSAGE
;
430 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_DESIRED
;
432 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_AVAILABLE
;
434 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
436 r
->h
.flags
|= rk_DNS_HEADER_CHECKING_DISABLED
;
437 r
->h
.response_code
= (p
[3] >> 4) & 0xf;
438 r
->h
.qdcount
= (p
[4] << 8) | p
[5];
439 r
->h
.ancount
= (p
[6] << 8) | p
[7];
440 r
->h
.nscount
= (p
[8] << 8) | p
[9];
441 r
->h
.arcount
= (p
[10] << 8) | p
[11];
445 if(r
->h
.qdcount
!= 1) {
449 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
454 r
->q
.domain
= strdup(host
);
455 if(r
->q
.domain
== NULL
) {
459 if (p
+ status
+ 4 > end_data
) {
464 r
->q
.type
= (p
[0] << 8 | p
[1]);
466 r
->q
.class = (p
[0] << 8 | p
[1]);
470 for(i
= 0; i
< r
->h
.ancount
; i
++) {
471 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
477 for(i
= 0; i
< r
->h
.nscount
; i
++) {
478 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
484 for(i
= 0; i
< r
->h
.arcount
; i
++) {
485 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
495 static struct dns_reply
*
496 dns_lookup_int(const char *domain
, int rr_class
, int rr_type
)
499 unsigned char *reply
= NULL
;
502 #ifdef HAVE_RES_NSEARCH
503 struct __res_state state
;
504 memset(&state
, 0, sizeof(state
));
505 if(res_ninit(&state
))
506 return NULL
; /* is this the best we can do? */
507 #elif defined(HAVE__RES)
508 u_long old_options
= 0;
520 if (_resolve_debug
) {
521 #ifdef HAVE_RES_NSEARCH
522 state
.options
|= RES_DEBUG
;
523 #elif defined(HAVE__RES)
524 old_options
= _res
.options
;
525 _res
.options
|= RES_DEBUG
;
527 fprintf(stderr
, "dns_lookup(%s, %d, %s), buffer size %d\n", domain
,
528 rr_class
, dns_type_to_string(rr_type
), size
);
530 reply
= malloc(size
);
532 #ifdef HAVE_RES_NSEARCH
537 #ifdef HAVE_RES_NSEARCH
538 len
= res_nsearch(&state
, domain
, rr_class
, rr_type
, reply
, size
);
540 len
= res_search(domain
, rr_class
, rr_type
, reply
, size
);
542 if (_resolve_debug
) {
543 #if defined(HAVE__RES) && !defined(HAVE_RES_NSEARCH)
544 _res
.options
= old_options
;
546 fprintf(stderr
, "dns_lookup(%s, %d, %s) --> %d\n",
547 domain
, rr_class
, dns_type_to_string(rr_type
), len
);
550 #ifdef HAVE_RES_NSEARCH
551 #ifdef HAVE_RES_NDESTROY
552 res_ndestroy(&state
);
560 } while (size
< len
&& len
< rk_DNS_MAX_PACKET_SIZE
);
561 #ifdef HAVE_RES_NSEARCH
565 len
= min(len
, size
);
566 r
= parse_reply(reply
, len
);
571 struct dns_reply
* ROKEN_LIB_FUNCTION
572 dns_lookup(const char *domain
, const char *type_name
)
576 type
= dns_string_to_type(type_name
);
579 fprintf(stderr
, "dns_lookup: unknown resource type: `%s'\n",
583 return dns_lookup_int(domain
, C_IN
, type
);
587 compare_srv(const void *a
, const void *b
)
589 const struct resource_record
*const* aa
= a
, *const* bb
= b
;
591 if((*aa
)->u
.srv
->priority
== (*bb
)->u
.srv
->priority
)
592 return ((*aa
)->u
.srv
->weight
- (*bb
)->u
.srv
->weight
);
593 return ((*aa
)->u
.srv
->priority
- (*bb
)->u
.srv
->priority
);
597 #define random() rand()
600 /* try to rearrange the srv-records by the algorithm in RFC2782 */
601 void ROKEN_LIB_FUNCTION
602 dns_srv_order(struct dns_reply
*r
)
604 struct resource_record
**srvs
, **ss
, **headp
;
605 struct resource_record
*rr
;
608 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
609 int state
[256 / sizeof(int)];
613 for(rr
= r
->head
; rr
; rr
= rr
->next
)
614 if(rr
->type
== rk_ns_t_srv
)
620 srvs
= malloc(num_srv
* sizeof(*srvs
));
622 return; /* XXX not much to do here */
624 /* unlink all srv-records from the linked list and put them in
626 for(ss
= srvs
, headp
= &r
->head
; *headp
; )
627 if((*headp
)->type
== rk_ns_t_srv
) {
629 *headp
= (*headp
)->next
;
633 headp
= &(*headp
)->next
;
635 /* sort them by priority and weight */
636 qsort(srvs
, num_srv
, sizeof(*srvs
), compare_srv
);
638 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
639 oldstate
= initstate(time(NULL
), (char*)state
, sizeof(state
));
644 for(ss
= srvs
; ss
< srvs
+ num_srv
; ) {
646 struct resource_record
**ee
, **tt
;
647 /* find the last record with the same priority and count the
648 sum of all weights */
649 for(sum
= 0, tt
= ss
; tt
< srvs
+ num_srv
; tt
++) {
651 if((*tt
)->u
.srv
->priority
!= (*ss
)->u
.srv
->priority
)
653 sum
+= (*tt
)->u
.srv
->weight
;
656 /* ss is now the first record of this priority and ee is the
659 rnd
= random() % (sum
+ 1);
660 for(count
= 0, tt
= ss
; ; tt
++) {
663 count
+= (*tt
)->u
.srv
->weight
;
670 /* insert the selected record at the tail (of the head) of
672 (*tt
)->next
= *headp
;
674 headp
= &(*tt
)->next
;
675 sum
-= (*tt
)->u
.srv
->weight
;
677 while(ss
< ee
&& *ss
== NULL
)
682 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
689 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
691 struct dns_reply
* ROKEN_LIB_FUNCTION
692 dns_lookup(const char *domain
, const char *type_name
)
697 void ROKEN_LIB_FUNCTION
698 dns_free_data(struct dns_reply
*r
)
702 void ROKEN_LIB_FUNCTION
703 dns_srv_order(struct dns_reply
*r
)