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 ROKEN_LIB_FUNCTION
int ROKEN_LIB_CALL
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 ROKEN_LIB_FUNCTION
const char * ROKEN_LIB_CALL
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)) || defined(HAVE_WINDNS)
104 dns_free_rr(struct rk_resource_record
*rr
)
113 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
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
;
130 parse_record(const unsigned char *data
, const unsigned char *end_data
,
131 const unsigned char **pp
, struct rk_resource_record
**ret_rr
)
133 struct rk_resource_record
*rr
;
134 int type
, class, ttl
;
138 const unsigned char *p
= *pp
;
142 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
145 if (p
+ status
+ 10 > end_data
)
149 type
= (p
[0] << 8) | p
[1];
151 class = (p
[0] << 8) | p
[1];
153 ttl
= (p
[0] << 24) | (p
[1] << 16) | (p
[2] << 8) | p
[3];
155 size
= (p
[0] << 8) | p
[1];
158 if (p
+ size
> end_data
)
161 rr
= calloc(1, sizeof(*rr
));
164 rr
->domain
= strdup(host
);
165 if(rr
->domain
== NULL
) {
177 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
182 rr
->u
.txt
= strdup(host
);
183 if(rr
->u
.txt
== NULL
) {
192 status
= dn_expand(data
, end_data
, p
+ 2, host
, sizeof(host
));
197 if ((size_t)status
+ 2 > size
) {
202 hostlen
= strlen(host
);
203 rr
->u
.mx
= (struct mx_record
*)malloc(sizeof(struct mx_record
) +
205 if(rr
->u
.mx
== NULL
) {
209 rr
->u
.mx
->preference
= (p
[0] << 8) | p
[1];
210 strlcpy(rr
->u
.mx
->domain
, host
, hostlen
+ 1);
215 status
= dn_expand(data
, end_data
, p
+ 6, host
, sizeof(host
));
220 if ((size_t)status
+ 6 > size
) {
225 hostlen
= strlen(host
);
227 (struct srv_record
*)malloc(sizeof(struct srv_record
) +
229 if(rr
->u
.srv
== NULL
) {
233 rr
->u
.srv
->priority
= (p
[0] << 8) | p
[1];
234 rr
->u
.srv
->weight
= (p
[2] << 8) | p
[3];
235 rr
->u
.srv
->port
= (p
[4] << 8) | p
[5];
236 strlcpy(rr
->u
.srv
->target
, host
, hostlen
+ 1);
240 if(size
== 0 || size
< (unsigned)(*p
+ 1)) {
244 rr
->u
.txt
= (char*)malloc(*p
+ 1);
245 if(rr
->u
.txt
== NULL
) {
249 strncpy(rr
->u
.txt
, (const char*)(p
+ 1), *p
);
250 rr
->u
.txt
[*p
] = '\0';
262 rr
->u
.key
= malloc (sizeof(*rr
->u
.key
) + key_len
- 1);
263 if (rr
->u
.key
== NULL
) {
268 rr
->u
.key
->flags
= (p
[0] << 8) | p
[1];
269 rr
->u
.key
->protocol
= p
[2];
270 rr
->u
.key
->algorithm
= p
[3];
271 rr
->u
.key
->key_len
= key_len
;
272 memcpy (rr
->u
.key
->key_data
, p
+ 4, key_len
);
276 size_t sig_len
, hostlen
;
282 status
= dn_expand (data
, end_data
, p
+ 18, host
, sizeof(host
));
287 if ((size_t)status
+ 18 > size
) {
292 /* the signer name is placed after the sig_data, to make it
293 easy to free this structure; the size calculation below
294 includes the zero-termination if the structure itself.
295 don't you just love C?
297 sig_len
= size
- 18 - status
;
298 hostlen
= strlen(host
);
299 rr
->u
.sig
= malloc(sizeof(*rr
->u
.sig
)
300 + hostlen
+ sig_len
);
301 if (rr
->u
.sig
== NULL
) {
305 rr
->u
.sig
->type
= (p
[0] << 8) | p
[1];
306 rr
->u
.sig
->algorithm
= p
[2];
307 rr
->u
.sig
->labels
= p
[3];
308 rr
->u
.sig
->orig_ttl
= (p
[4] << 24) | (p
[5] << 16)
309 | (p
[6] << 8) | p
[7];
310 rr
->u
.sig
->sig_expiration
= (p
[8] << 24) | (p
[9] << 16)
311 | (p
[10] << 8) | p
[11];
312 rr
->u
.sig
->sig_inception
= (p
[12] << 24) | (p
[13] << 16)
313 | (p
[14] << 8) | p
[15];
314 rr
->u
.sig
->key_tag
= (p
[16] << 8) | p
[17];
315 rr
->u
.sig
->sig_len
= sig_len
;
316 memcpy (rr
->u
.sig
->sig_data
, p
+ 18 + status
, sig_len
);
317 rr
->u
.sig
->signer
= &rr
->u
.sig
->sig_data
[sig_len
];
318 strlcpy(rr
->u
.sig
->signer
, host
, hostlen
+ 1);
322 case rk_ns_t_cert
: {
331 rr
->u
.cert
= malloc (sizeof(*rr
->u
.cert
) + cert_len
- 1);
332 if (rr
->u
.cert
== NULL
) {
337 rr
->u
.cert
->type
= (p
[0] << 8) | p
[1];
338 rr
->u
.cert
->tag
= (p
[2] << 8) | p
[3];
339 rr
->u
.cert
->algorithm
= p
[4];
340 rr
->u
.cert
->cert_len
= cert_len
;
341 memcpy (rr
->u
.cert
->cert_data
, p
+ 5, cert_len
);
344 case rk_ns_t_sshfp
: {
352 sshfp_len
= size
- 2;
354 rr
->u
.sshfp
= malloc (sizeof(*rr
->u
.sshfp
) + sshfp_len
- 1);
355 if (rr
->u
.sshfp
== NULL
) {
360 rr
->u
.sshfp
->algorithm
= p
[0];
361 rr
->u
.sshfp
->type
= p
[1];
362 rr
->u
.sshfp
->sshfp_len
= sshfp_len
;
363 memcpy (rr
->u
.sshfp
->sshfp_data
, p
+ 2, sshfp_len
);
374 digest_len
= size
- 4;
376 rr
->u
.ds
= malloc (sizeof(*rr
->u
.ds
) + digest_len
- 1);
377 if (rr
->u
.ds
== NULL
) {
382 rr
->u
.ds
->key_tag
= (p
[0] << 8) | p
[1];
383 rr
->u
.ds
->algorithm
= p
[2];
384 rr
->u
.ds
->digest_type
= p
[3];
385 rr
->u
.ds
->digest_len
= digest_len
;
386 memcpy (rr
->u
.ds
->digest_data
, p
+ 4, digest_len
);
390 rr
->u
.data
= (unsigned char*)malloc(size
);
391 if(size
!= 0 && rr
->u
.data
== NULL
) {
396 memcpy(rr
->u
.data
, p
, size
);
408 parse_reply(const unsigned char *data
, size_t len
)
410 const unsigned char *p
;
414 const unsigned char *end_data
= data
+ len
;
415 struct rk_dns_reply
*r
;
416 struct rk_resource_record
**rr
;
418 r
= calloc(1, sizeof(*r
));
424 r
->h
.id
= (p
[0] << 8) | p
[1];
427 r
->h
.flags
|= rk_DNS_HEADER_RESPONSE_FLAG
;
428 r
->h
.opcode
= (p
[2] >> 1) & 0xf;
430 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
432 r
->h
.flags
|= rk_DNS_HEADER_TRUNCATED_MESSAGE
;
434 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_DESIRED
;
436 r
->h
.flags
|= rk_DNS_HEADER_RECURSION_AVAILABLE
;
438 r
->h
.flags
|= rk_DNS_HEADER_AUTHORITIVE_ANSWER
;
440 r
->h
.flags
|= rk_DNS_HEADER_CHECKING_DISABLED
;
441 r
->h
.response_code
= (p
[3] >> 4) & 0xf;
442 r
->h
.qdcount
= (p
[4] << 8) | p
[5];
443 r
->h
.ancount
= (p
[6] << 8) | p
[7];
444 r
->h
.nscount
= (p
[8] << 8) | p
[9];
445 r
->h
.arcount
= (p
[10] << 8) | p
[11];
449 if(r
->h
.qdcount
!= 1) {
453 status
= dn_expand(data
, end_data
, p
, host
, sizeof(host
));
458 r
->q
.domain
= strdup(host
);
459 if(r
->q
.domain
== NULL
) {
463 if (p
+ status
+ 4 > end_data
) {
468 r
->q
.type
= (p
[0] << 8 | p
[1]);
470 r
->q
.class = (p
[0] << 8 | p
[1]);
474 for(i
= 0; i
< r
->h
.ancount
; i
++) {
475 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
481 for(i
= 0; i
< r
->h
.nscount
; i
++) {
482 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
488 for(i
= 0; i
< r
->h
.arcount
; i
++) {
489 if(parse_record(data
, end_data
, &p
, rr
) != 0) {
499 #ifdef HAVE_RES_NSEARCH
500 #ifdef HAVE_RES_NDESTROY
501 #define rk_res_free(x) res_ndestroy(x)
503 #define rk_res_free(x) res_nclose(x)
507 #if defined(HAVE_DNS_SEARCH)
508 #define resolve_search(h,n,c,t,r,l) \
509 ((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize))
510 #define resolve_free_handle(h) dns_free(h)
511 #elif defined(HAVE_RES_NSEARCH)
512 #define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l)
513 #define resolve_free_handle(h) rk_res_free(h);
515 #define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l)
517 #define resolve_free_handle(h)
521 static struct rk_dns_reply
*
522 dns_lookup_int(const char *domain
, int rr_class
, int rr_type
)
524 struct rk_dns_reply
*r
;
527 #if defined(HAVE_DNS_SEARCH)
528 struct sockaddr_storage from
;
529 uint32_t fromsize
= sizeof(from
);
532 handle
= dns_open(NULL
);
535 #elif defined(HAVE_RES_NSEARCH)
536 struct __res_state state
;
537 struct __res_state
*handle
= &state
;
539 memset(&state
, 0, sizeof(state
));
540 if(res_ninit(handle
))
541 return NULL
; /* is this the best we can do? */
550 if (_resolve_debug
) {
551 #if defined(HAVE_DNS_SEARCH)
552 dns_set_debug(handle
, 1);
553 #elif defined(HAVE_RES_NSEARCH)
554 state
.options
|= RES_DEBUG
;
556 fprintf(stderr
, "dns_lookup(%s, %d, %s), buffer size %d\n", domain
,
557 rr_class
, rk_dns_type_to_string(rr_type
), len
);
561 resolve_free_handle(handle
);
565 size
= resolve_search(handle
, domain
, rr_class
, rr_type
, reply
, len
);
567 if (_resolve_debug
) {
568 fprintf(stderr
, "dns_lookup(%s, %d, %s) --> %d\n",
569 domain
, rr_class
, rk_dns_type_to_string(rr_type
), size
);
572 /* resolver thinks it know better, go for it */
574 } else if (size
> 0) {
575 /* got a good reply */
577 } else if (size
<= 0 && len
< rk_DNS_MAX_PACKET_SIZE
) {
579 if (len
> rk_DNS_MAX_PACKET_SIZE
)
580 len
= rk_DNS_MAX_PACKET_SIZE
;
583 resolve_free_handle(handle
);
589 len
= min(len
, size
);
590 r
= parse_reply(reply
, len
);
593 resolve_free_handle(handle
);
598 ROKEN_LIB_FUNCTION
struct rk_dns_reply
* ROKEN_LIB_CALL
599 rk_dns_lookup(const char *domain
, const char *type_name
)
603 type
= rk_dns_string_to_type(type_name
);
606 fprintf(stderr
, "dns_lookup: unknown resource type: `%s'\n",
610 return dns_lookup_int(domain
, rk_ns_c_in
, type
);
613 #endif /* !HAVE_WINDNS */
616 compare_srv(const void *a
, const void *b
)
618 const struct rk_resource_record
*const* aa
= a
, *const* bb
= b
;
620 if((*aa
)->u
.srv
->priority
== (*bb
)->u
.srv
->priority
)
621 return ((*aa
)->u
.srv
->weight
- (*bb
)->u
.srv
->weight
);
622 return ((*aa
)->u
.srv
->priority
- (*bb
)->u
.srv
->priority
);
625 /* try to rearrange the srv-records by the algorithm in RFC2782 */
626 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
627 rk_dns_srv_order(struct rk_dns_reply
*r
)
629 struct rk_resource_record
**srvs
, **ss
, **headp
;
630 struct rk_resource_record
*rr
;
632 unsigned int srv_found
= FALSE
;
636 for(rr
= r
->head
; rr
; rr
= rr
->next
)
637 if(rr
->type
== rk_ns_t_srv
) {
642 if(srv_found
== FALSE
)
645 srvs
= malloc(num_srv
* sizeof(*srvs
));
647 return; /* XXX not much to do here */
649 /* unlink all srv-records from the linked list and put them in
651 for(ss
= srvs
, headp
= &r
->head
; *headp
; )
652 if((*headp
)->type
== rk_ns_t_srv
) {
654 *headp
= (*headp
)->next
;
658 headp
= &(*headp
)->next
;
660 /* sort them by priority and weight */
661 qsort(srvs
, num_srv
, sizeof(*srvs
), compare_srv
);
665 for (ss
= srvs
; ss
< srvs
+ num_srv
; ) {
666 int sum
, zeros
, rnd
, count
; /* zeros -> weight scaling */
667 struct rk_resource_record
**ee
, **tt
;
670 * find the last record with the same priority and count the sum of all
673 for (sum
= 0, zeros
= 0, tt
= ss
; tt
< srvs
+ num_srv
; tt
++) {
675 if((*tt
)->u
.srv
->priority
!= (*ss
)->u
.srv
->priority
)
677 sum
+= (*tt
)->u
.srv
->weight
;
678 if ((*tt
)->u
.srv
->weight
== 0)
681 /* make sure scale (`zeros') is > 0 then scale out */
682 sum
+= zeros
? 1 : zeros
++;
687 * ss is now the first record of this priority and ee is the first of
688 * the next or the first past the end of srvs
691 rnd
= rk_random() % sum
+ 1;
692 for (count
= 0, tt
= ss
; tt
< ee
; tt
++) {
694 continue; /* this one's already been picked */
695 if ((*tt
)->u
.srv
->weight
== 0)
698 count
+= (*tt
)->u
.srv
->weight
* zeros
;
704 /* push the selected record */
705 (*tt
)->next
= *headp
;
707 headp
= &(*tt
)->next
;
709 * reduce the sum so the next iteration is sure to reach the random
710 * total after examining all the remaining records.
712 if ((*tt
)->u
.srv
->weight
== 0)
715 sum
-= (*tt
)->u
.srv
->weight
* zeros
;
717 while (ss
< ee
&& *ss
== NULL
)
730 static struct rk_resource_record
*
731 parse_dns_record(PDNS_RECORD pRec
)
733 struct rk_resource_record
* rr
;
738 rr
= calloc(1, sizeof(*rr
));
740 rr
->domain
= strdup(pRec
->pName
);
741 rr
->type
= pRec
->wType
;
743 rr
->ttl
= pRec
->dwTtl
;
750 rr
->u
.txt
= strdup(pRec
->Data
.NS
.pNameHost
);
751 if(rr
->u
.txt
== NULL
) {
759 size_t hostlen
= strnlen(pRec
->Data
.MX
.pNameExchange
, DNS_MAX_NAME_LENGTH
);
761 rr
->u
.mx
= (struct mx_record
*)malloc(sizeof(struct mx_record
) +
763 if (rr
->u
.mx
== NULL
) {
768 strcpy_s(rr
->u
.mx
->domain
, hostlen
+ 1, pRec
->Data
.MX
.pNameExchange
);
769 rr
->u
.mx
->preference
= pRec
->Data
.MX
.wPreference
;
774 size_t hostlen
= strnlen(pRec
->Data
.SRV
.pNameTarget
, DNS_MAX_NAME_LENGTH
);
777 (struct srv_record
*)malloc(sizeof(struct srv_record
) +
779 if(rr
->u
.srv
== NULL
) {
784 rr
->u
.srv
->priority
= pRec
->Data
.SRV
.wPriority
;
785 rr
->u
.srv
->weight
= pRec
->Data
.SRV
.wWeight
;
786 rr
->u
.srv
->port
= pRec
->Data
.SRV
.wPort
;
787 strcpy_s(rr
->u
.srv
->target
, hostlen
+ 1, pRec
->Data
.SRV
.pNameTarget
);
795 if (pRec
->Data
.TXT
.dwStringCount
== 0) {
796 rr
->u
.txt
= strdup("");
800 len
= strnlen(pRec
->Data
.TXT
.pStringArray
[0], DNS_MAX_TEXT_STRING_LENGTH
);
802 rr
->u
.txt
= (char *)malloc(len
+ 1);
803 strcpy_s(rr
->u
.txt
, len
+ 1, pRec
->Data
.TXT
.pStringArray
[0]);
811 if (pRec
->wDataLength
< 4) {
816 key_len
= pRec
->wDataLength
- 4;
817 rr
->u
.key
= malloc (sizeof(*rr
->u
.key
) + key_len
- 1);
818 if (rr
->u
.key
== NULL
) {
823 rr
->u
.key
->flags
= pRec
->Data
.KEY
.wFlags
;
824 rr
->u
.key
->protocol
= pRec
->Data
.KEY
.chProtocol
;
825 rr
->u
.key
->algorithm
= pRec
->Data
.KEY
.chAlgorithm
;
826 rr
->u
.key
->key_len
= key_len
;
827 memcpy_s (rr
->u
.key
->key_data
, key_len
,
828 pRec
->Data
.KEY
.Key
, key_len
);
833 size_t sig_len
, hostlen
;
835 if(pRec
->wDataLength
<= 18) {
840 sig_len
= pRec
->wDataLength
;
842 hostlen
= strnlen(pRec
->Data
.SIG
.pNameSigner
, DNS_MAX_NAME_LENGTH
);
844 rr
->u
.sig
= malloc(sizeof(*rr
->u
.sig
)
845 + hostlen
+ sig_len
);
846 if (rr
->u
.sig
== NULL
) {
850 rr
->u
.sig
->type
= pRec
->Data
.SIG
.wTypeCovered
;
851 rr
->u
.sig
->algorithm
= pRec
->Data
.SIG
.chAlgorithm
;
852 rr
->u
.sig
->labels
= pRec
->Data
.SIG
.chLabelCount
;
853 rr
->u
.sig
->orig_ttl
= pRec
->Data
.SIG
.dwOriginalTtl
;
854 rr
->u
.sig
->sig_expiration
= pRec
->Data
.SIG
.dwExpiration
;
855 rr
->u
.sig
->sig_inception
= pRec
->Data
.SIG
.dwTimeSigned
;
856 rr
->u
.sig
->key_tag
= pRec
->Data
.SIG
.wKeyTag
;
857 rr
->u
.sig
->sig_len
= sig_len
;
858 memcpy_s (rr
->u
.sig
->sig_data
, sig_len
,
859 pRec
->Data
.SIG
.Signature
, sig_len
);
860 rr
->u
.sig
->signer
= &rr
->u
.sig
->sig_data
[sig_len
];
861 strcpy_s(rr
->u
.sig
->signer
, hostlen
+ 1, pRec
->Data
.SIG
.pNameSigner
);
867 rr
->u
.ds
= malloc (sizeof(*rr
->u
.ds
) + pRec
->Data
.DS
.wDigestLength
- 1);
868 if (rr
->u
.ds
== NULL
) {
873 rr
->u
.ds
->key_tag
= pRec
->Data
.DS
.wKeyTag
;
874 rr
->u
.ds
->algorithm
= pRec
->Data
.DS
.chAlgorithm
;
875 rr
->u
.ds
->digest_type
= pRec
->Data
.DS
.chDigestType
;
876 rr
->u
.ds
->digest_len
= pRec
->Data
.DS
.wDigestLength
;
877 memcpy_s (rr
->u
.ds
->digest_data
, pRec
->Data
.DS
.wDigestLength
,
878 pRec
->Data
.DS
.Digest
, pRec
->Data
.DS
.wDigestLength
);
888 rr
->next
= parse_dns_record(pRec
->pNext
);
892 ROKEN_LIB_FUNCTION
struct rk_dns_reply
* ROKEN_LIB_CALL
893 rk_dns_lookup(const char *domain
, const char *type_name
)
897 PDNS_RECORD pRec
= NULL
;
898 struct rk_dns_reply
* r
= NULL
;
902 type
= rk_dns_string_to_type(type_name
);
905 fprintf(stderr
, "dns_lookup: unknown resource type: `%s'\n",
910 status
= DnsQuery_UTF8(domain
, type
, DNS_QUERY_STANDARD
, NULL
,
912 if (status
!= ERROR_SUCCESS
)
915 r
= calloc(1, sizeof(*r
));
916 r
->q
.domain
= strdup(domain
);
920 r
->head
= parse_dns_record(pRec
);
922 if (r
->head
== NULL
) {
932 DnsRecordListFree(pRec
, DnsFreeRecordList
);
936 #endif /* HAVE_WINDNS */
938 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
940 ROKEN_LIB_FUNCTION
struct rk_dns_reply
* ROKEN_LIB_CALL
941 rk_dns_lookup(const char *domain
, const char *type_name
)
946 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
947 rk_dns_free_data(struct rk_dns_reply
*r
)
951 ROKEN_LIB_FUNCTION
void ROKEN_LIB_CALL
952 rk_dns_srv_order(struct rk_dns_reply
*r
)