2 * Copyright (c) 1995 - 2006 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>
51 #ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
52 #undef HAVE_RES_NSEARCH
55 #define DECL(X) {#X, rk_ns_t_##X}
79 int _resolve_debug
= 0;
81 int ROKEN_LIB_FUNCTION
82 rk_dns_string_to_type(const char *name
)
84 struct stot
*p
= stot
;
85 for(p
= stot
; p
->name
; p
++)
86 if(strcasecmp(name
, p
->name
) == 0)
91 const char * ROKEN_LIB_FUNCTION
92 rk_dns_type_to_string(int type
)
94 struct stot
*p
= stot
;
95 for(p
= stot
; p
->name
; p
++)
101 #if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)
104 dns_free_rr(struct rk_resource_record
*rr
)
113 void ROKEN_LIB_FUNCTION
114 rk_dns_free_data(struct rk_dns_reply
*r
)
116 struct rk_resource_record
*rr
;
119 for(rr
= r
->head
; rr
;){
120 struct rk_resource_record
*tmp
= rr
;
128 parse_record(const unsigned char *data
, const unsigned char *end_data
,
129 const unsigned char **pp
, struct rk_resource_record
**ret_rr
)
131 struct rk_resource_record
*rr
;
132 int type
, class, ttl
;
136 const unsigned char *p
= *pp
;
140 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
143 if (p
+ status
+ 10 > end_data
)
147 type
= (p
[0] << 8) | p
[1];
149 class = (p
[0] << 8) | p
[1];
151 ttl
= (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
153 size
= (p
[0] << 8) | p
[1];
156 if (p
+ size
> end_data
)
159 rr
= calloc(1, sizeof(*rr
));
162 rr
->domain
= strdup(host
);
163 if(rr
->domain
== NULL
) {
175 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
180 rr
->u
.txt
= strdup(host
);
181 if(rr
->u
.txt
== NULL
) {
190 status
= dn_expand(data
, end_data
, p
+ 2, host
, sizeof(host
));
195 if (status
+ 2 > size
) {
200 hostlen
= strlen(host
);
201 rr
->u
.mx
= (struct mx_record
*)malloc(sizeof(struct mx_record
) +
203 if(rr
->u
.mx
== NULL
) {
207 rr
->u
.mx
->preference
= (p
[0] << 8) | p
[1];
208 strlcpy(rr
->u
.mx
->domain
, host
, hostlen
+ 1);
213 status
= dn_expand(data
, end_data
, p
+ 6, host
, sizeof(host
));
218 if (status
+ 6 > size
) {
223 hostlen
= strlen(host
);
225 (struct srv_record
*)malloc(sizeof(struct srv_record
) +
227 if(rr
->u
.srv
== NULL
) {
231 rr
->u
.srv
->priority
= (p
[0] << 8) | p
[1];
232 rr
->u
.srv
->weight
= (p
[2] << 8) | p
[3];
233 rr
->u
.srv
->port
= (p
[4] << 8) | p
[5];
234 strlcpy(rr
->u
.srv
->target
, host
, hostlen
+ 1);
238 if(size
== 0 || size
< *p
+ 1) {
242 rr
->u
.txt
= (char*)malloc(*p
+ 1);
243 if(rr
->u
.txt
== NULL
) {
247 strncpy(rr
->u
.txt
, (const char*)(p
+ 1), *p
);
248 rr
->u
.txt
[*p
] = '\0';
260 rr
->u
.key
= malloc (sizeof(*rr
->u
.key
) + key_len
- 1);
261 if (rr
->u
.key
== NULL
) {
266 rr
->u
.key
->flags
= (p
[0] << 8) | p
[1];
267 rr
->u
.key
->protocol
= p
[2];
268 rr
->u
.key
->algorithm
= p
[3];
269 rr
->u
.key
->key_len
= key_len
;
270 memcpy (rr
->u
.key
->key_data
, p
+ 4, key_len
);
274 size_t sig_len
, hostlen
;
280 status
= dn_expand (data
, end_data
, p
+ 18, host
, sizeof(host
));
285 if (status
+ 18 > size
) {
290 /* the signer name is placed after the sig_data, to make it
291 easy to free this structure; the size calculation below
292 includes the zero-termination if the structure itself.
293 don't you just love C?
295 sig_len
= size
- 18 - status
;
296 hostlen
= strlen(host
);
297 rr
->u
.sig
= malloc(sizeof(*rr
->u
.sig
)
298 + hostlen
+ sig_len
);
299 if (rr
->u
.sig
== NULL
) {
303 rr
->u
.sig
->type
= (p
[0] << 8) | p
[1];
304 rr
->u
.sig
->algorithm
= p
[2];
305 rr
->u
.sig
->labels
= p
[3];
306 rr
->u
.sig
->orig_ttl
= (p
[4] << 24) | (p
[5] << 16)
307 | (p
[6] << 8) | p
[7];
308 rr
->u
.sig
->sig_expiration
= (p
[8] << 24) | (p
[9] << 16)
309 | (p
[10] << 8) | p
[11];
310 rr
->u
.sig
->sig_inception
= (p
[12] << 24) | (p
[13] << 16)
311 | (p
[14] << 8) | p
[15];
312 rr
->u
.sig
->key_tag
= (p
[16] << 8) | p
[17];
313 rr
->u
.sig
->sig_len
= sig_len
;
314 memcpy (rr
->u
.sig
->sig_data
, p
+ 18 + status
, sig_len
);
315 rr
->u
.sig
->signer
= &rr
->u
.sig
->sig_data
[sig_len
];
316 strlcpy(rr
->u
.sig
->signer
, host
, hostlen
+ 1);
320 case rk_ns_t_cert
: {
329 rr
->u
.cert
= malloc (sizeof(*rr
->u
.cert
) + cert_len
- 1);
330 if (rr
->u
.cert
== NULL
) {
335 rr
->u
.cert
->type
= (p
[0] << 8) | p
[1];
336 rr
->u
.cert
->tag
= (p
[2] << 8) | p
[3];
337 rr
->u
.cert
->algorithm
= p
[4];
338 rr
->u
.cert
->cert_len
= cert_len
;
339 memcpy (rr
->u
.cert
->cert_data
, p
+ 5, cert_len
);
342 case rk_ns_t_sshfp
: {
350 sshfp_len
= size
- 2;
352 rr
->u
.sshfp
= malloc (sizeof(*rr
->u
.sshfp
) + sshfp_len
- 1);
353 if (rr
->u
.sshfp
== NULL
) {
358 rr
->u
.sshfp
->algorithm
= p
[0];
359 rr
->u
.sshfp
->type
= p
[1];
360 rr
->u
.sshfp
->sshfp_len
= sshfp_len
;
361 memcpy (rr
->u
.sshfp
->sshfp_data
, p
+ 2, sshfp_len
);
372 digest_len
= size
- 4;
374 rr
->u
.ds
= malloc (sizeof(*rr
->u
.ds
) + digest_len
- 1);
375 if (rr
->u
.ds
== NULL
) {
380 rr
->u
.ds
->key_tag
= (p
[0] << 8) | p
[1];
381 rr
->u
.ds
->algorithm
= p
[2];
382 rr
->u
.ds
->digest_type
= p
[3];
383 rr
->u
.ds
->digest_len
= digest_len
;
384 memcpy (rr
->u
.ds
->digest_data
, p
+ 4, digest_len
);
388 rr
->u
.data
= (unsigned char*)malloc(size
);
389 if(size
!= 0 && rr
->u
.data
== NULL
) {
394 memcpy(rr
->u
.data
, p
, size
);
406 parse_reply(const unsigned char *data
, size_t len
)
408 const unsigned char *p
;
412 const unsigned char *end_data
= data
+ len
;
413 struct rk_dns_reply
*r
;
414 struct rk_resource_record
**rr
;
416 r
= calloc(1, sizeof(*r
));
422 r
->h
.id
= (p
[0] << 8) | p
[1];
425 r
->h
.flags
|= rk_DNS_HEADER_RESPONSE_FLAG
;
426 r
->h
.opcode
= (p
[2] >> 1) & 0xf;
428 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
430 r
->h
.flags
|= rk_DNS_HEADER_TRUNCATED_MESSAGE
;
432 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_DESIRED
;
434 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_AVAILABLE
;
436 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
438 r
->h
.flags
|= rk_DNS_HEADER_CHECKING_DISABLED
;
439 r
->h
.response_code
= (p
[3] >> 4) & 0xf;
440 r
->h
.qdcount
= (p
[4] << 8) | p
[5];
441 r
->h
.ancount
= (p
[6] << 8) | p
[7];
442 r
->h
.nscount
= (p
[8] << 8) | p
[9];
443 r
->h
.arcount
= (p
[10] << 8) | p
[11];
447 if(r
->h
.qdcount
!= 1) {
451 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
456 r
->q
.domain
= strdup(host
);
457 if(r
->q
.domain
== NULL
) {
461 if (p
+ status
+ 4 > end_data
) {
466 r
->q
.type
= (p
[0] << 8 | p
[1]);
468 r
->q
.class = (p
[0] << 8 | p
[1]);
472 for(i
= 0; i
< r
->h
.ancount
; i
++) {
473 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
479 for(i
= 0; i
< r
->h
.nscount
; i
++) {
480 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
486 for(i
= 0; i
< r
->h
.arcount
; i
++) {
487 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
497 #ifdef HAVE_RES_NSEARCH
498 #ifdef HAVE_RES_NDESTROY
499 #define rk_res_free(x) res_ndestroy(x)
501 #define rk_res_free(x) res_nclose(x)
505 #if defined(HAVE_DNS_SEARCH)
506 #define resolve_search(h,n,c,t,r,l) \
507 ((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize))
508 #define resolve_free_handle(h) dns_free(h)
509 #elif defined(HAVE_RES_NSEARCH)
510 #define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l)
511 #define resolve_free_handle(h) rk_res_free(h);
513 #define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l)
515 #define resolve_free_handle(h)
519 static struct rk_dns_reply
*
520 dns_lookup_int(const char *domain
, int rr_class
, int rr_type
)
522 struct rk_dns_reply
*r
;
526 #if defined(HAVE_DNS_SEARCH)
527 struct sockaddr_storage from
;
528 uint32_t fromsize
= sizeof(from
);
531 handle
= dns_open(NULL
);
534 #elif defined(HAVE_RES_NSEARCH)
535 struct __res_state state
;
536 struct __res_state
*handle
= &state
;
538 memset(&state
, 0, sizeof(state
));
539 if(res_ninit(handle
))
540 return NULL
; /* is this the best we can do? */
552 if (_resolve_debug
) {
553 #if defined(HAVE_DNS_SEARCH)
554 dns_set_debug(handle
, 1);
555 #elif defined(HAVE_RES_NSEARCH)
556 state
.options
|= RES_DEBUG
;
558 fprintf(stderr
, "dns_lookup(%s, %d, %s), buffer size %d\n", domain
,
559 rr_class
, rk_dns_type_to_string(rr_type
), size
);
561 reply
= malloc(size
);
563 resolve_free_handle(handle
);
567 len
= resolve_search(handle
, domain
, rr_class
, rr_type
, reply
, size
);
569 if (_resolve_debug
) {
570 fprintf(stderr
, "dns_lookup(%s, %d, %s) --> %d\n",
571 domain
, rr_class
, rk_dns_type_to_string(rr_type
), len
);
574 resolve_free_handle(handle
);
578 } while (size
< len
&& len
< rk_DNS_MAX_PACKET_SIZE
);
579 resolve_free_handle(handle
);
581 len
= min(len
, size
);
582 r
= parse_reply(reply
, len
);
587 struct rk_dns_reply
* ROKEN_LIB_FUNCTION
588 rk_dns_lookup(const char *domain
, const char *type_name
)
592 type
= rk_dns_string_to_type(type_name
);
595 fprintf(stderr
, "dns_lookup: unknown resource type: `%s'\n",
599 return dns_lookup_int(domain
, rk_ns_c_in
, type
);
603 compare_srv(const void *a
, const void *b
)
605 const struct rk_resource_record
*const* aa
= a
, *const* bb
= b
;
607 if((*aa
)->u
.srv
->priority
== (*bb
)->u
.srv
->priority
)
608 return ((*aa
)->u
.srv
->weight
- (*bb
)->u
.srv
->weight
);
609 return ((*aa
)->u
.srv
->priority
- (*bb
)->u
.srv
->priority
);
613 #define random() rand()
616 /* try to rearrange the srv-records by the algorithm in RFC2782 */
617 void ROKEN_LIB_FUNCTION
618 rk_dns_srv_order(struct rk_dns_reply
*r
)
620 struct rk_resource_record
**srvs
, **ss
, **headp
;
621 struct rk_resource_record
*rr
;
624 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
625 int state
[256 / sizeof(int)];
629 for(rr
= r
->head
; rr
; rr
= rr
->next
)
630 if(rr
->type
== rk_ns_t_srv
)
636 srvs
= malloc(num_srv
* sizeof(*srvs
));
638 return; /* XXX not much to do here */
640 /* unlink all srv-records from the linked list and put them in
642 for(ss
= srvs
, headp
= &r
->head
; *headp
; )
643 if((*headp
)->type
== rk_ns_t_srv
) {
645 *headp
= (*headp
)->next
;
649 headp
= &(*headp
)->next
;
651 /* sort them by priority and weight */
652 qsort(srvs
, num_srv
, sizeof(*srvs
), compare_srv
);
654 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
655 oldstate
= initstate(time(NULL
), (char*)state
, sizeof(state
));
660 for(ss
= srvs
; ss
< srvs
+ num_srv
; ) {
662 struct rk_resource_record
**ee
, **tt
;
663 /* find the last record with the same priority and count the
664 sum of all weights */
665 for(sum
= 0, tt
= ss
; tt
< srvs
+ num_srv
; tt
++) {
667 if((*tt
)->u
.srv
->priority
!= (*ss
)->u
.srv
->priority
)
669 sum
+= (*tt
)->u
.srv
->weight
;
672 /* ss is now the first record of this priority and ee is the
675 rnd
= random() % (sum
+ 1);
676 for(count
= 0, tt
= ss
; ; tt
++) {
679 count
+= (*tt
)->u
.srv
->weight
;
686 /* insert the selected record at the tail (of the head) of
688 (*tt
)->next
= *headp
;
690 headp
= &(*tt
)->next
;
691 sum
-= (*tt
)->u
.srv
->weight
;
693 while(ss
< ee
&& *ss
== NULL
)
698 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
705 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
707 struct rk_dns_reply
* ROKEN_LIB_FUNCTION
708 rk_dns_lookup(const char *domain
, const char *type_name
)
713 void ROKEN_LIB_FUNCTION
714 rk_dns_free_data(struct rk_dns_reply
*r
)
718 void ROKEN_LIB_FUNCTION
719 rk_dns_srv_order(struct rk_dns_reply
*r
)