1 /* $NetBSD: resolve.c,v 1.4 2014/04/24 14:49:43 pettai Exp $ */
4 * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #include <krb5/roken.h>
40 #ifdef HAVE_ARPA_NAMESER_H
41 #include <arpa/nameser.h>
49 #include <krb5/resolve.h>
53 #ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
54 #undef HAVE_RES_NSEARCH
57 #define DECL(X) {#X, rk_ns_t_##X}
81 int _resolve_debug
= 0;
83 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
84 rk_dns_string_to_type(const char *name
)
86 struct stot
*p
= stot
;
87 for(p
= stot
; p
->name
; p
++)
88 if(strcasecmp(name
, p
->name
) == 0)
93 ROKEN_LIB_FUNCTION
const char * ROKEN_LIB_CALL
94 rk_dns_type_to_string(int type
)
96 struct stot
*p
= stot
;
97 for(p
= stot
; p
->name
; p
++)
103 #if ((defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)) || defined(HAVE_WINDNS)
106 dns_free_rr(struct rk_resource_record
*rr
)
115 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
116 rk_dns_free_data(struct rk_dns_reply
*r
)
118 struct rk_resource_record
*rr
;
121 for(rr
= r
->head
; rr
;){
122 struct rk_resource_record
*tmp
= rr
;
132 parse_record(const unsigned char *data
, const unsigned char *end_data
,
133 const unsigned char **pp
, struct rk_resource_record
**ret_rr
)
135 struct rk_resource_record
*rr
;
136 int type
, class, ttl
;
140 const unsigned char *p
= *pp
;
144 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
147 if (p
+ status
+ 10 > end_data
)
151 type
= (p
[0] << 8) | p
[1];
153 class = (p
[0] << 8) | p
[1];
155 ttl
= (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
157 size
= (p
[0] << 8) | p
[1];
160 if (p
+ size
> end_data
)
163 rr
= calloc(1, sizeof(*rr
));
166 rr
->domain
= strdup(host
);
167 if(rr
->domain
== NULL
) {
179 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
184 rr
->u
.txt
= strdup(host
);
185 if(rr
->u
.txt
== NULL
) {
194 status
= dn_expand(data
, end_data
, p
+ 2, host
, sizeof(host
));
199 if ((size_t)status
+ 2 > size
) {
204 hostlen
= strlen(host
);
205 rr
->u
.mx
= (struct mx_record
*)malloc(sizeof(struct mx_record
) +
207 if(rr
->u
.mx
== NULL
) {
211 rr
->u
.mx
->preference
= (p
[0] << 8) | p
[1];
212 strlcpy(rr
->u
.mx
->domain
, host
, hostlen
+ 1);
217 status
= dn_expand(data
, end_data
, p
+ 6, host
, sizeof(host
));
222 if ((size_t)status
+ 6 > size
) {
227 hostlen
= strlen(host
);
229 (struct srv_record
*)malloc(sizeof(struct srv_record
) +
231 if(rr
->u
.srv
== NULL
) {
235 rr
->u
.srv
->priority
= (p
[0] << 8) | p
[1];
236 rr
->u
.srv
->weight
= (p
[2] << 8) | p
[3];
237 rr
->u
.srv
->port
= (p
[4] << 8) | p
[5];
238 strlcpy(rr
->u
.srv
->target
, host
, hostlen
+ 1);
242 if(size
== 0 || size
< (unsigned)(*p
+ 1)) {
246 rr
->u
.txt
= (char*)malloc(*p
+ 1);
247 if(rr
->u
.txt
== NULL
) {
251 strncpy(rr
->u
.txt
, (const char*)(p
+ 1), *p
);
252 rr
->u
.txt
[*p
] = '\0';
264 rr
->u
.key
= malloc (sizeof(*rr
->u
.key
) + key_len
- 1);
265 if (rr
->u
.key
== NULL
) {
270 rr
->u
.key
->flags
= (p
[0] << 8) | p
[1];
271 rr
->u
.key
->protocol
= p
[2];
272 rr
->u
.key
->algorithm
= p
[3];
273 rr
->u
.key
->key_len
= key_len
;
274 memcpy (rr
->u
.key
->key_data
, p
+ 4, key_len
);
278 size_t sig_len
, hostlen
;
284 status
= dn_expand (data
, end_data
, p
+ 18, host
, sizeof(host
));
289 if ((size_t)status
+ 18 > size
) {
294 /* the signer name is placed after the sig_data, to make it
295 easy to free this structure; the size calculation below
296 includes the zero-termination if the structure itself.
297 don't you just love C?
299 sig_len
= size
- 18 - status
;
300 hostlen
= strlen(host
);
301 rr
->u
.sig
= malloc(sizeof(*rr
->u
.sig
)
302 + hostlen
+ sig_len
);
303 if (rr
->u
.sig
== NULL
) {
307 rr
->u
.sig
->type
= (p
[0] << 8) | p
[1];
308 rr
->u
.sig
->algorithm
= p
[2];
309 rr
->u
.sig
->labels
= p
[3];
310 rr
->u
.sig
->orig_ttl
= (p
[4] << 24) | (p
[5] << 16)
311 | (p
[6] << 8) | p
[7];
312 rr
->u
.sig
->sig_expiration
= (p
[8] << 24) | (p
[9] << 16)
313 | (p
[10] << 8) | p
[11];
314 rr
->u
.sig
->sig_inception
= (p
[12] << 24) | (p
[13] << 16)
315 | (p
[14] << 8) | p
[15];
316 rr
->u
.sig
->key_tag
= (p
[16] << 8) | p
[17];
317 rr
->u
.sig
->sig_len
= sig_len
;
318 memcpy (rr
->u
.sig
->sig_data
, p
+ 18 + status
, sig_len
);
319 rr
->u
.sig
->signer
= &rr
->u
.sig
->sig_data
[sig_len
];
320 strlcpy(rr
->u
.sig
->signer
, host
, hostlen
+ 1);
324 case rk_ns_t_cert
: {
333 rr
->u
.cert
= malloc (sizeof(*rr
->u
.cert
) + cert_len
- 1);
334 if (rr
->u
.cert
== NULL
) {
339 rr
->u
.cert
->type
= (p
[0] << 8) | p
[1];
340 rr
->u
.cert
->tag
= (p
[2] << 8) | p
[3];
341 rr
->u
.cert
->algorithm
= p
[4];
342 rr
->u
.cert
->cert_len
= cert_len
;
343 memcpy (rr
->u
.cert
->cert_data
, p
+ 5, cert_len
);
346 case rk_ns_t_sshfp
: {
354 sshfp_len
= size
- 2;
356 rr
->u
.sshfp
= malloc (sizeof(*rr
->u
.sshfp
) + sshfp_len
- 1);
357 if (rr
->u
.sshfp
== NULL
) {
362 rr
->u
.sshfp
->algorithm
= p
[0];
363 rr
->u
.sshfp
->type
= p
[1];
364 rr
->u
.sshfp
->sshfp_len
= sshfp_len
;
365 memcpy (rr
->u
.sshfp
->sshfp_data
, p
+ 2, sshfp_len
);
376 digest_len
= size
- 4;
378 rr
->u
.ds
= malloc (sizeof(*rr
->u
.ds
) + digest_len
- 1);
379 if (rr
->u
.ds
== NULL
) {
384 rr
->u
.ds
->key_tag
= (p
[0] << 8) | p
[1];
385 rr
->u
.ds
->algorithm
= p
[2];
386 rr
->u
.ds
->digest_type
= p
[3];
387 rr
->u
.ds
->digest_len
= digest_len
;
388 memcpy (rr
->u
.ds
->digest_data
, p
+ 4, digest_len
);
392 rr
->u
.data
= (unsigned char*)malloc(size
);
393 if(size
!= 0 && rr
->u
.data
== NULL
) {
398 memcpy(rr
->u
.data
, p
, size
);
410 parse_reply(const unsigned char *data
, size_t len
)
412 const unsigned char *p
;
416 const unsigned char *end_data
= data
+ len
;
417 struct rk_dns_reply
*r
;
418 struct rk_resource_record
**rr
;
420 r
= calloc(1, sizeof(*r
));
426 r
->h
.id
= (p
[0] << 8) | p
[1];
429 r
->h
.flags
|= rk_DNS_HEADER_RESPONSE_FLAG
;
430 r
->h
.opcode
= (p
[2] >> 1) & 0xf;
432 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
434 r
->h
.flags
|= rk_DNS_HEADER_TRUNCATED_MESSAGE
;
436 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_DESIRED
;
438 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_AVAILABLE
;
440 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
442 r
->h
.flags
|= rk_DNS_HEADER_CHECKING_DISABLED
;
443 r
->h
.response_code
= (p
[3] >> 4) & 0xf;
444 r
->h
.qdcount
= (p
[4] << 8) | p
[5];
445 r
->h
.ancount
= (p
[6] << 8) | p
[7];
446 r
->h
.nscount
= (p
[8] << 8) | p
[9];
447 r
->h
.arcount
= (p
[10] << 8) | p
[11];
451 if(r
->h
.qdcount
!= 1) {
455 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
460 r
->q
.domain
= strdup(host
);
461 if(r
->q
.domain
== NULL
) {
465 if (p
+ status
+ 4 > end_data
) {
470 r
->q
.type
= (p
[0] << 8 | p
[1]);
472 r
->q
.class = (p
[0] << 8 | p
[1]);
476 for(i
= 0; i
< r
->h
.ancount
; i
++) {
477 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
483 for(i
= 0; i
< r
->h
.nscount
; i
++) {
484 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
490 for(i
= 0; i
< r
->h
.arcount
; i
++) {
491 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
501 #ifdef HAVE_RES_NSEARCH
502 #ifdef HAVE_RES_NDESTROY
503 #define rk_res_free(x) res_ndestroy(x)
505 #define rk_res_free(x) res_nclose(x)
509 #if defined(HAVE_DNS_SEARCH)
510 #define resolve_search(h,n,c,t,r,l) \
511 ((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize))
512 #define resolve_free_handle(h) dns_free(h)
513 #elif defined(HAVE_RES_NSEARCH)
514 #define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l)
515 #define resolve_free_handle(h) rk_res_free(h);
517 #define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l)
519 #define resolve_free_handle(h)
523 static struct rk_dns_reply
*
524 dns_lookup_int(const char *domain
, int rr_class
, int rr_type
)
526 struct rk_dns_reply
*r
;
529 #if defined(HAVE_DNS_SEARCH)
530 struct sockaddr_storage from
;
531 uint32_t fromsize
= sizeof(from
);
534 handle
= dns_open(NULL
);
537 #elif defined(HAVE_RES_NSEARCH)
538 struct __res_state state
;
539 struct __res_state
*handle
= &state
;
541 memset(&state
, 0, sizeof(state
));
542 if(res_ninit(handle
))
543 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
), len
);
563 resolve_free_handle(handle
);
567 size
= resolve_search(handle
, domain
, rr_class
, rr_type
, reply
, len
);
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
), size
);
574 /* resolver thinks it know better, go for it */
576 } else if (size
> 0) {
577 /* got a good reply */
579 } else if (size
<= 0 && len
< rk_DNS_MAX_PACKET_SIZE
) {
581 if (len
> rk_DNS_MAX_PACKET_SIZE
)
582 len
= rk_DNS_MAX_PACKET_SIZE
;
585 resolve_free_handle(handle
);
591 len
= min(len
, size
);
592 r
= parse_reply(reply
, len
);
595 resolve_free_handle(handle
);
600 ROKEN_LIB_FUNCTION
struct rk_dns_reply
* ROKEN_LIB_CALL
601 rk_dns_lookup(const char *domain
, const char *type_name
)
605 type
= rk_dns_string_to_type(type_name
);
608 fprintf(stderr
, "dns_lookup: unknown resource type: `%s'\n",
612 return dns_lookup_int(domain
, rk_ns_c_in
, type
);
615 #endif /* !HAVE_WINDNS */
618 compare_srv(const void *a
, const void *b
)
620 const struct rk_resource_record
*const* aa
= a
, *const* bb
= b
;
622 if((*aa
)->u
.srv
->priority
== (*bb
)->u
.srv
->priority
)
623 return ((*aa
)->u
.srv
->weight
- (*bb
)->u
.srv
->weight
);
624 return ((*aa
)->u
.srv
->priority
- (*bb
)->u
.srv
->priority
);
627 /* try to rearrange the srv-records by the algorithm in RFC2782 */
628 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
629 rk_dns_srv_order(struct rk_dns_reply
*r
)
631 struct rk_resource_record
**srvs
, **ss
, **headp
;
632 struct rk_resource_record
*rr
;
637 for(rr
= r
->head
; rr
; rr
= rr
->next
)
638 if(rr
->type
== rk_ns_t_srv
)
644 srvs
= malloc(num_srv
* sizeof(*srvs
));
646 return; /* XXX not much to do here */
648 /* unlink all srv-records from the linked list and put them in
650 for(ss
= srvs
, headp
= &r
->head
; *headp
; )
651 if((*headp
)->type
== rk_ns_t_srv
) {
653 *headp
= (*headp
)->next
;
657 headp
= &(*headp
)->next
;
659 /* sort them by priority and weight */
660 qsort(srvs
, num_srv
, sizeof(*srvs
), compare_srv
);
664 for(ss
= srvs
; ss
< srvs
+ num_srv
; ) {
666 struct rk_resource_record
**ee
, **tt
;
667 /* find the last record with the same priority and count the
668 sum of all weights */
669 for(sum
= 0, tt
= ss
; tt
< srvs
+ num_srv
; tt
++) {
671 if((*tt
)->u
.srv
->priority
!= (*ss
)->u
.srv
->priority
)
673 sum
+= (*tt
)->u
.srv
->weight
;
676 /* ss is now the first record of this priority and ee is the
679 rnd
= rk_random() % (sum
+ 1);
680 for(count
= 0, tt
= ss
; ; tt
++) {
683 count
+= (*tt
)->u
.srv
->weight
;
690 /* insert the selected record at the tail (of the head) of
692 (*tt
)->next
= *headp
;
694 headp
= &(*tt
)->next
;
695 sum
-= (*tt
)->u
.srv
->weight
;
697 while(ss
< ee
&& *ss
== NULL
)
710 static struct rk_resource_record
*
711 parse_dns_record(PDNS_RECORD pRec
)
713 struct rk_resource_record
* rr
;
718 rr
= calloc(1, sizeof(*rr
));
720 rr
->domain
= strdup(pRec
->pName
);
721 rr
->type
= pRec
->wType
;
723 rr
->ttl
= pRec
->dwTtl
;
730 rr
->u
.txt
= strdup(pRec
->Data
.NS
.pNameHost
);
731 if(rr
->u
.txt
== NULL
) {
739 size_t hostlen
= strnlen(pRec
->Data
.MX
.pNameExchange
, DNS_MAX_NAME_LENGTH
);
741 rr
->u
.mx
= (struct mx_record
*)malloc(sizeof(struct mx_record
) +
743 if (rr
->u
.mx
== NULL
) {
748 strcpy_s(rr
->u
.mx
->domain
, hostlen
+ 1, pRec
->Data
.MX
.pNameExchange
);
749 rr
->u
.mx
->preference
= pRec
->Data
.MX
.wPreference
;
754 size_t hostlen
= strnlen(pRec
->Data
.SRV
.pNameTarget
, DNS_MAX_NAME_LENGTH
);
757 (struct srv_record
*)malloc(sizeof(struct srv_record
) +
759 if(rr
->u
.srv
== NULL
) {
764 rr
->u
.srv
->priority
= pRec
->Data
.SRV
.wPriority
;
765 rr
->u
.srv
->weight
= pRec
->Data
.SRV
.wWeight
;
766 rr
->u
.srv
->port
= pRec
->Data
.SRV
.wPort
;
767 strcpy_s(rr
->u
.srv
->target
, hostlen
+ 1, pRec
->Data
.SRV
.pNameTarget
);
775 if (pRec
->Data
.TXT
.dwStringCount
== 0) {
776 rr
->u
.txt
= strdup("");
780 len
= strnlen(pRec
->Data
.TXT
.pStringArray
[0], DNS_MAX_TEXT_STRING_LENGTH
);
782 rr
->u
.txt
= (char *)malloc(len
+ 1);
783 strcpy_s(rr
->u
.txt
, len
+ 1, pRec
->Data
.TXT
.pStringArray
[0]);
791 if (pRec
->wDataLength
< 4) {
796 key_len
= pRec
->wDataLength
- 4;
797 rr
->u
.key
= malloc (sizeof(*rr
->u
.key
) + key_len
- 1);
798 if (rr
->u
.key
== NULL
) {
803 rr
->u
.key
->flags
= pRec
->Data
.KEY
.wFlags
;
804 rr
->u
.key
->protocol
= pRec
->Data
.KEY
.chProtocol
;
805 rr
->u
.key
->algorithm
= pRec
->Data
.KEY
.chAlgorithm
;
806 rr
->u
.key
->key_len
= key_len
;
807 memcpy_s (rr
->u
.key
->key_data
, key_len
,
808 pRec
->Data
.KEY
.Key
, key_len
);
813 size_t sig_len
, hostlen
;
815 if(pRec
->wDataLength
<= 18) {
820 sig_len
= pRec
->wDataLength
;
822 hostlen
= strnlen(pRec
->Data
.SIG
.pNameSigner
, DNS_MAX_NAME_LENGTH
);
824 rr
->u
.sig
= malloc(sizeof(*rr
->u
.sig
)
825 + hostlen
+ sig_len
);
826 if (rr
->u
.sig
== NULL
) {
830 rr
->u
.sig
->type
= pRec
->Data
.SIG
.wTypeCovered
;
831 rr
->u
.sig
->algorithm
= pRec
->Data
.SIG
.chAlgorithm
;
832 rr
->u
.sig
->labels
= pRec
->Data
.SIG
.chLabelCount
;
833 rr
->u
.sig
->orig_ttl
= pRec
->Data
.SIG
.dwOriginalTtl
;
834 rr
->u
.sig
->sig_expiration
= pRec
->Data
.SIG
.dwExpiration
;
835 rr
->u
.sig
->sig_inception
= pRec
->Data
.SIG
.dwTimeSigned
;
836 rr
->u
.sig
->key_tag
= pRec
->Data
.SIG
.wKeyTag
;
837 rr
->u
.sig
->sig_len
= sig_len
;
838 memcpy_s (rr
->u
.sig
->sig_data
, sig_len
,
839 pRec
->Data
.SIG
.Signature
, sig_len
);
840 rr
->u
.sig
->signer
= &rr
->u
.sig
->sig_data
[sig_len
];
841 strcpy_s(rr
->u
.sig
->signer
, hostlen
+ 1, pRec
->Data
.SIG
.pNameSigner
);
847 rr
->u
.ds
= malloc (sizeof(*rr
->u
.ds
) + pRec
->Data
.DS
.wDigestLength
- 1);
848 if (rr
->u
.ds
== NULL
) {
853 rr
->u
.ds
->key_tag
= pRec
->Data
.DS
.wKeyTag
;
854 rr
->u
.ds
->algorithm
= pRec
->Data
.DS
.chAlgorithm
;
855 rr
->u
.ds
->digest_type
= pRec
->Data
.DS
.chDigestType
;
856 rr
->u
.ds
->digest_len
= pRec
->Data
.DS
.wDigestLength
;
857 memcpy_s (rr
->u
.ds
->digest_data
, pRec
->Data
.DS
.wDigestLength
,
858 pRec
->Data
.DS
.Digest
, pRec
->Data
.DS
.wDigestLength
);
868 rr
->next
= parse_dns_record(pRec
->pNext
);
872 ROKEN_LIB_FUNCTION
struct rk_dns_reply
* ROKEN_LIB_CALL
873 rk_dns_lookup(const char *domain
, const char *type_name
)
877 PDNS_RECORD pRec
= NULL
;
878 struct rk_dns_reply
* r
= NULL
;
882 type
= rk_dns_string_to_type(type_name
);
885 fprintf(stderr
, "dns_lookup: unknown resource type: `%s'\n",
890 status
= DnsQuery_UTF8(domain
, type
, DNS_QUERY_STANDARD
, NULL
,
892 if (status
!= ERROR_SUCCESS
)
895 r
= calloc(1, sizeof(*r
));
896 r
->q
.domain
= strdup(domain
);
900 r
->head
= parse_dns_record(pRec
);
902 if (r
->head
== NULL
) {
912 DnsRecordListFree(pRec
, DnsFreeRecordList
);
916 #endif /* HAVE_WINDNS */
918 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
920 ROKEN_LIB_FUNCTION
struct rk_dns_reply
* ROKEN_LIB_CALL
921 rk_dns_lookup(const char *domain
, const char *type_name
)
926 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
927 rk_dns_free_data(struct rk_dns_reply
*r
)
931 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
932 rk_dns_srv_order(struct rk_dns_reply
*r
)