1/*
2 * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
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.
16 *
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.
20 *
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
31 * SUCH DAMAGE.
32 */
33
34
35#include <config.h>
36
37#include "roken.h"
38#ifdef HAVE_ARPA_NAMESER_H
39#include <arpa/nameser.h>
40#endif
41#ifdef HAVE_RESOLV_H
42#include <resolv.h>
43#endif
44#ifdef HAVE_DNS_H
45#include <dns.h>
46#endif
47#include "resolve.h"
48
49#include <assert.h>
50
51#ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
52#undef HAVE_RES_NSEARCH
53#endif
54
55#define DECL(X) {#X, rk_ns_t_##X}
56
57static struct stot{
58    const char *name;
59    int type;
60}stot[] = {
61    DECL(a),
62    DECL(aaaa),
63    DECL(ns),
64    DECL(cname),
65    DECL(soa),
66    DECL(ptr),
67    DECL(mx),
68    DECL(txt),
69    DECL(afsdb),
70    DECL(sig),
71    DECL(key),
72    DECL(srv),
73    DECL(naptr),
74    DECL(sshfp),
75    DECL(ds),
76    {NULL, 	0}
77};
78
79int _resolve_debug = 0;
80
81ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
82rk_dns_string_to_type(const char *name)
83{
84    struct stot *p = stot;
85    for(p = stot; p->name; p++)
86	if(strcasecmp(name, p->name) == 0)
87	    return p->type;
88    return -1;
89}
90
91ROKEN_LIB_FUNCTION const char * ROKEN_LIB_CALL
92rk_dns_type_to_string(int type)
93{
94    struct stot *p = stot;
95    for(p = stot; p->name; p++)
96	if(type == p->type)
97	    return p->name;
98    return NULL;
99}
100
101#if ((defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)) || defined(HAVE_WINDNS)
102
103static void
104dns_free_rr(struct rk_resource_record *rr)
105{
106    if(rr->domain)
107	free(rr->domain);
108    if(rr->u.data)
109	free(rr->u.data);
110    free(rr);
111}
112
113ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
114rk_dns_free_data(struct rk_dns_reply *r)
115{
116    struct rk_resource_record *rr;
117    if(r->q.domain)
118	free(r->q.domain);
119    for(rr = r->head; rr;){
120	struct rk_resource_record *tmp = rr;
121	rr = rr->next;
122	dns_free_rr(tmp);
123    }
124    free (r);
125}
126
127#ifndef HAVE_WINDNS
128
129static int
130parse_record(const unsigned char *data, const unsigned char *end_data,
131	     const unsigned char **pp, struct rk_resource_record **ret_rr)
132{
133    struct rk_resource_record *rr;
134    int type, class, ttl;
135    unsigned size;
136    int status;
137    char host[MAXDNAME];
138    const unsigned char *p = *pp;
139
140    *ret_rr = NULL;
141
142    status = dn_expand(data, end_data, p, host, sizeof(host));
143    if(status < 0)
144	return -1;
145    if (p + status + 10 > end_data)
146	return -1;
147
148    p += status;
149    type = (p[0] << 8) | p[1];
150    p += 2;
151    class = (p[0] << 8) | p[1];
152    p += 2;
153    ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
154    p += 4;
155    size = (p[0] << 8) | p[1];
156    p += 2;
157
158    if (p + size > end_data)
159	return -1;
160
161    rr = calloc(1, sizeof(*rr));
162    if(rr == NULL)
163	return -1;
164    rr->domain = strdup(host);
165    if(rr->domain == NULL) {
166	dns_free_rr(rr);
167	return -1;
168    }
169    rr->type = type;
170    rr->class = class;
171    rr->ttl = ttl;
172    rr->size = size;
173    switch(type){
174    case rk_ns_t_ns:
175    case rk_ns_t_cname:
176    case rk_ns_t_ptr:
177	status = dn_expand(data, end_data, p, host, sizeof(host));
178	if(status < 0) {
179	    dns_free_rr(rr);
180	    return -1;
181	}
182	rr->u.txt = strdup(host);
183	if(rr->u.txt == NULL) {
184	    dns_free_rr(rr);
185	    return -1;
186	}
187	break;
188    case rk_ns_t_mx:
189    case rk_ns_t_afsdb:{
190	size_t hostlen;
191
192	status = dn_expand(data, end_data, p + 2, host, sizeof(host));
193	if(status < 0){
194	    dns_free_rr(rr);
195	    return -1;
196	}
197	if ((size_t)status + 2 > size) {
198	    dns_free_rr(rr);
199	    return -1;
200	}
201
202	hostlen = strlen(host);
203	rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
204						hostlen);
205	if(rr->u.mx == NULL) {
206	    dns_free_rr(rr);
207	    return -1;
208	}
209	rr->u.mx->preference = (p[0] << 8) | p[1];
210	strlcpy(rr->u.mx->domain, host, hostlen + 1);
211	break;
212    }
213    case rk_ns_t_srv:{
214	size_t hostlen;
215	status = dn_expand(data, end_data, p + 6, host, sizeof(host));
216	if(status < 0){
217	    dns_free_rr(rr);
218	    return -1;
219	}
220	if ((size_t)status + 6 > size) {
221	    dns_free_rr(rr);
222	    return -1;
223	}
224
225	hostlen = strlen(host);
226	rr->u.srv =
227	    (struct srv_record*)malloc(sizeof(struct srv_record) +
228				       hostlen);
229	if(rr->u.srv == NULL) {
230	    dns_free_rr(rr);
231	    return -1;
232	}
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);
237	break;
238    }
239    case rk_ns_t_txt:{
240	if(size == 0 || size < (unsigned)(*p + 1)) {
241	    dns_free_rr(rr);
242	    return -1;
243	}
244	rr->u.txt = (char*)malloc(*p + 1);
245	if(rr->u.txt == NULL) {
246	    dns_free_rr(rr);
247	    return -1;
248	}
249	strncpy(rr->u.txt, (const char*)(p + 1), *p);
250	rr->u.txt[*p] = '\0';
251	break;
252    }
253    case rk_ns_t_key : {
254	size_t key_len;
255
256	if (size < 4) {
257	    dns_free_rr(rr);
258	    return -1;
259	}
260
261	key_len = size - 4;
262	rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
263	if (rr->u.key == NULL) {
264	    dns_free_rr(rr);
265	    return -1;
266	}
267
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);
273	break;
274    }
275    case rk_ns_t_sig : {
276	size_t sig_len, hostlen;
277
278	if(size <= 18) {
279	    dns_free_rr(rr);
280	    return -1;
281	}
282	status = dn_expand (data, end_data, p + 18, host, sizeof(host));
283	if (status < 0) {
284	    dns_free_rr(rr);
285	    return -1;
286	}
287	if ((size_t)status + 18 > size) {
288	    dns_free_rr(rr);
289	    return -1;
290	}
291
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?
296	*/
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) {
302	    dns_free_rr(rr);
303	    return -1;
304	}
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);
319	break;
320    }
321
322    case rk_ns_t_cert : {
323	size_t cert_len;
324
325	if (size < 5) {
326	    dns_free_rr(rr);
327	    return -1;
328	}
329
330	cert_len = size - 5;
331	rr->u.cert = malloc (sizeof(*rr->u.cert) + cert_len - 1);
332	if (rr->u.cert == NULL) {
333	    dns_free_rr(rr);
334	    return -1;
335	}
336
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);
342	break;
343    }
344    case rk_ns_t_sshfp : {
345	size_t sshfp_len;
346
347	if (size < 2) {
348	    dns_free_rr(rr);
349	    return -1;
350	}
351
352	sshfp_len = size - 2;
353
354	rr->u.sshfp = malloc (sizeof(*rr->u.sshfp) + sshfp_len - 1);
355	if (rr->u.sshfp == NULL) {
356	    dns_free_rr(rr);
357	    return -1;
358	}
359
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);
364	break;
365    }
366    case rk_ns_t_ds: {
367	size_t digest_len;
368
369	if (size < 4) {
370	    dns_free_rr(rr);
371	    return -1;
372	}
373
374	digest_len = size - 4;
375
376	rr->u.ds = malloc (sizeof(*rr->u.ds) + digest_len - 1);
377	if (rr->u.ds == NULL) {
378	    dns_free_rr(rr);
379	    return -1;
380	}
381
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);
387	break;
388    }
389    default:
390	rr->u.data = (unsigned char*)malloc(size);
391	if(size != 0 && rr->u.data == NULL) {
392	    dns_free_rr(rr);
393	    return -1;
394	}
395	if (size)
396	    memcpy(rr->u.data, p, size);
397    }
398    *pp = p + size;
399    *ret_rr = rr;
400
401    return 0;
402}
403
404struct rk_dns_reply*
405rk_dns_parse_reply(const unsigned char *data, size_t len)
406{
407    const unsigned char *p;
408    int status;
409    size_t i;
410    char host[MAXDNAME];
411    const unsigned char *end_data = data + len;
412    struct rk_dns_reply *r;
413    struct rk_resource_record **rr;
414
415    r = calloc(1, sizeof(*r));
416    if (r == NULL)
417	return NULL;
418
419    p = data;
420
421    r->h.id = (p[0] << 8) | p[1];
422    r->h.flags = 0;
423    if (p[2] & 0x01)
424	r->h.flags |= rk_DNS_HEADER_RESPONSE_FLAG;
425    r->h.opcode = (p[2] >> 1) & 0xf;
426    if (p[2] & 0x20)
427	r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
428    if (p[2] & 0x40)
429	r->h.flags |= rk_DNS_HEADER_TRUNCATED_MESSAGE;
430    if (p[2] & 0x80)
431	r->h.flags |= rk_DNS_HEADER_RECURSION_DESIRED;
432    if (p[3] & 0x01)
433	r->h.flags |= rk_DNS_HEADER_RECURSION_AVAILABLE;
434    if (p[3] & 0x04)
435	r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
436    if (p[3] & 0x08)
437	r->h.flags |= rk_DNS_HEADER_CHECKING_DISABLED;
438    r->h.response_code = (p[3] >> 4) & 0xf;
439    r->h.qdcount = (p[4] << 8) | p[5];
440    r->h.ancount = (p[6] << 8) | p[7];
441    r->h.nscount = (p[8] << 8) | p[9];
442    r->h.arcount = (p[10] << 8) | p[11];
443
444    p += 12;
445
446    if(r->h.qdcount != 1) {
447	free(r);
448	return NULL;
449    }
450    status = dn_expand(data, end_data, p, host, sizeof(host));
451    if(status < 0){
452	rk_dns_free_data(r);
453	return NULL;
454    }
455    r->q.domain = strdup(host);
456    if(r->q.domain == NULL) {
457	rk_dns_free_data(r);
458	return NULL;
459    }
460    if (p + status + 4 > end_data) {
461	rk_dns_free_data(r);
462	return NULL;
463    }
464    p += status;
465    r->q.type = (p[0] << 8 | p[1]);
466    p += 2;
467    r->q.class = (p[0] << 8 | p[1]);
468    p += 2;
469
470    rr = &r->head;
471    for(i = 0; i < r->h.ancount; i++) {
472	if(parse_record(data, end_data, &p, rr) != 0) {
473	    rk_dns_free_data(r);
474	    return NULL;
475	}
476	rr = &(*rr)->next;
477    }
478    for(i = 0; i < r->h.nscount; i++) {
479	if(parse_record(data, end_data, &p, rr) != 0) {
480	    rk_dns_free_data(r);
481	    return NULL;
482	}
483	rr = &(*rr)->next;
484    }
485    for(i = 0; i < r->h.arcount; i++) {
486	if(parse_record(data, end_data, &p, rr) != 0) {
487	    rk_dns_free_data(r);
488	    return NULL;
489	}
490	rr = &(*rr)->next;
491    }
492    *rr = NULL;
493    return r;
494}
495
496#ifdef HAVE_RES_NSEARCH
497#ifdef HAVE_RES_NDESTROY
498#define rk_res_free(x) res_ndestroy(x)
499#else
500#define rk_res_free(x) res_nclose(x)
501#endif
502#endif
503
504#if defined(HAVE_DNS_SEARCH)
505#define resolve_search(h,n,c,t,r,l) \
506    	((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize))
507#define resolve_free_handle(h) dns_free(h)
508#elif defined(HAVE_RES_NSEARCH)
509#define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l)
510#define resolve_free_handle(h) rk_res_free(h);
511#else
512#define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l)
513#define handle 0
514#define resolve_free_handle(h)
515#endif
516
517
518static struct rk_dns_reply *
519dns_lookup_int(const char *domain, int rr_class, int rr_type)
520{
521    struct rk_dns_reply *r;
522    void *reply = NULL;
523    int size, len;
524#if defined(HAVE_DNS_SEARCH)
525    struct sockaddr_storage from;
526    uint32_t fromsize = sizeof(from);
527    dns_handle_t handle;
528
529    handle = dns_open(NULL);
530    if (handle == NULL)
531	return NULL;
532#elif defined(HAVE_RES_NSEARCH)
533    struct __res_state state;
534    struct __res_state *handle = &state;
535
536    memset(&state, 0, sizeof(state));
537    if(res_ninit(handle))
538	return NULL; /* is this the best we can do? */
539#endif
540
541    len = 1500;
542    while(1) {
543	if (reply) {
544	    free(reply);
545	    reply = NULL;
546	}
547	if (_resolve_debug) {
548#if defined(HAVE_DNS_SEARCH)
549	    dns_set_debug(handle, 1);
550#elif defined(HAVE_RES_NSEARCH)
551	    state.options |= RES_DEBUG;
552#endif
553	    fprintf(stderr, "dns_lookup(%s, %d, %s), buffer size %d\n", domain,
554		    rr_class, rk_dns_type_to_string(rr_type), len);
555	}
556	reply = malloc(len);
557	if (reply == NULL) {
558	    resolve_free_handle(handle);
559	    return NULL;
560	}
561
562	size = resolve_search(handle, domain, rr_class, rr_type, reply, len);
563
564	if (_resolve_debug) {
565	    fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n",
566		    domain, rr_class, rk_dns_type_to_string(rr_type), size);
567	}
568	if (size > len) {
569	    /* resolver thinks it know better, go for it */
570	    len = size;
571	} else if (size > 0) {
572	    /* got a good reply */
573	    break;
574	} else if (size <= 0 && len < rk_DNS_MAX_PACKET_SIZE) {
575	    len *= 2;
576	    if (len > rk_DNS_MAX_PACKET_SIZE)
577		len = rk_DNS_MAX_PACKET_SIZE;
578	} else {
579	    /* the end, leave */
580	    resolve_free_handle(handle);
581	    free(reply);
582	    return NULL;
583	}
584    }
585
586    len = min(len, size);
587
588    r = rk_dns_parse_reply(reply, len);
589    free(reply);
590
591    resolve_free_handle(handle);
592
593    return r;
594}
595
596ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
597rk_dns_lookup(const char *domain, const char *type_name)
598{
599    int type;
600
601    type = rk_dns_string_to_type(type_name);
602    if(type == -1) {
603	if(_resolve_debug)
604	    fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
605		    type_name);
606	return NULL;
607    }
608    return dns_lookup_int(domain, rk_ns_c_in, type);
609}
610
611#endif	/* !HAVE_WINDNS */
612
613static int
614compare_srv(const void *a, const void *b)
615{
616    const struct rk_resource_record *const* aa = a, *const* bb = b;
617
618    if((*aa)->u.srv->priority == (*bb)->u.srv->priority)
619	return ((*aa)->u.srv->weight - (*bb)->u.srv->weight);
620    return ((*aa)->u.srv->priority - (*bb)->u.srv->priority);
621}
622
623/* try to rearrange the srv-records by the algorithm in RFC2782 */
624ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
625rk_dns_srv_order(struct rk_dns_reply *r)
626{
627    struct rk_resource_record **srvs, **ss, **headp;
628    struct rk_resource_record *rr;
629    int num_srv = 0;
630
631    rk_random_init();
632
633    for(rr = r->head; rr; rr = rr->next)
634	if(rr->type == rk_ns_t_srv)
635	    num_srv++;
636
637    if(num_srv == 0)
638	return;
639
640    srvs = malloc(num_srv * sizeof(*srvs));
641    if(srvs == NULL)
642	return; /* XXX not much to do here */
643
644    /* unlink all srv-records from the linked list and put them in
645       a vector */
646    for(ss = srvs, headp = &r->head; *headp; )
647	if((*headp)->type == rk_ns_t_srv) {
648	    *ss = *headp;
649	    *headp = (*headp)->next;
650	    (*ss)->next = NULL;
651	    ss++;
652	} else
653	    headp = &(*headp)->next;
654
655    /* sort them by priority and weight */
656    qsort(srvs, num_srv, sizeof(*srvs), compare_srv);
657
658    headp = &r->head;
659
660    for(ss = srvs; ss < srvs + num_srv; ) {
661	int sum, rnd, count;
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++) {
666	    assert(*tt != NULL);
667	    if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
668		break;
669	    sum += (*tt)->u.srv->weight;
670	}
671	ee = tt;
672	/* ss is now the first record of this priority and ee is the
673           first of the next */
674	while(ss < ee) {
675	    rnd = rk_random() % (sum + 1);
676	    for(count = 0, tt = ss; ; tt++) {
677		if(*tt == NULL)
678		    continue;
679		count += (*tt)->u.srv->weight;
680		if(count >= rnd)
681		    break;
682	    }
683
684	    assert(tt < ee);
685
686	    /* insert the selected record at the tail (of the head) of
687               the list */
688	    (*tt)->next = *headp;
689	    *headp = *tt;
690	    headp = &(*tt)->next;
691	    sum -= (*tt)->u.srv->weight;
692	    *tt = NULL;
693	    while(ss < ee && *ss == NULL)
694		ss++;
695	}
696    }
697
698    free(srvs);
699    return;
700}
701
702#ifdef HAVE_WINDNS
703
704#include <WinDNS.h>
705
706static struct rk_resource_record *
707parse_dns_record(PDNS_RECORD pRec)
708{
709    struct rk_resource_record * rr;
710
711    if (pRec == NULL)
712	return NULL;
713
714    rr = calloc(1, sizeof(*rr));
715
716    rr->domain = strdup(pRec->pName);
717    rr->type = pRec->wType;
718    rr->class = 0;
719    rr->ttl = pRec->dwTtl;
720    rr->size = 0;
721
722    switch (rr->type) {
723    case rk_ns_t_ns:
724    case rk_ns_t_cname:
725    case rk_ns_t_ptr:
726	rr->u.txt = strdup(pRec->Data.NS.pNameHost);
727	if(rr->u.txt == NULL) {
728	    dns_free_rr(rr);
729	    return NULL;
730	}
731	break;
732
733    case rk_ns_t_mx:
734    case rk_ns_t_afsdb:{
735	size_t hostlen = strnlen(pRec->Data.MX.pNameExchange, DNS_MAX_NAME_LENGTH);
736
737	rr->u.mx = (struct mx_record *)malloc(sizeof(struct mx_record) +
738					      hostlen);
739	if (rr->u.mx == NULL) {
740	    dns_free_rr(rr);
741	    return NULL;
742	}
743
744	strcpy_s(rr->u.mx->domain, hostlen + 1, pRec->Data.MX.pNameExchange);
745	rr->u.mx->preference = pRec->Data.MX.wPreference;
746	break;
747    }
748
749    case rk_ns_t_srv:{
750	size_t hostlen = strnlen(pRec->Data.SRV.pNameTarget, DNS_MAX_NAME_LENGTH);
751
752	rr->u.srv =
753	    (struct srv_record*)malloc(sizeof(struct srv_record) +
754				       hostlen);
755	if(rr->u.srv == NULL) {
756	    dns_free_rr(rr);
757	    return NULL;
758	}
759
760	rr->u.srv->priority = pRec->Data.SRV.wPriority;
761	rr->u.srv->weight = pRec->Data.SRV.wWeight;
762	rr->u.srv->port = pRec->Data.SRV.wPort;
763	strcpy_s(rr->u.srv->target, hostlen + 1, pRec->Data.SRV.pNameTarget);
764
765	break;
766    }
767
768    case rk_ns_t_txt:{
769	size_t len;
770
771	if (pRec->Data.TXT.dwStringCount == 0) {
772	    rr->u.txt = strdup("");
773	    break;
774	}
775
776	len = strnlen(pRec->Data.TXT.pStringArray[0], DNS_MAX_TEXT_STRING_LENGTH);
777
778	rr->u.txt = (char *)malloc(len + 1);
779	strcpy_s(rr->u.txt, len + 1, pRec->Data.TXT.pStringArray[0]);
780
781	break;
782    }
783
784    case rk_ns_t_key : {
785	size_t key_len;
786
787	if (pRec->wDataLength < 4) {
788	    dns_free_rr(rr);
789	    return NULL;
790	}
791
792	key_len = pRec->wDataLength - 4;
793	rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
794	if (rr->u.key == NULL) {
795	    dns_free_rr(rr);
796	    return NULL;
797	}
798
799	rr->u.key->flags     = pRec->Data.KEY.wFlags;
800	rr->u.key->protocol  = pRec->Data.KEY.chProtocol;
801	rr->u.key->algorithm = pRec->Data.KEY.chAlgorithm;
802	rr->u.key->key_len   = key_len;
803	memcpy_s (rr->u.key->key_data, key_len,
804		  pRec->Data.KEY.Key, key_len);
805	break;
806    }
807
808    case rk_ns_t_sig : {
809	size_t sig_len, hostlen;
810
811	if(pRec->wDataLength <= 18) {
812	    dns_free_rr(rr);
813	    return NULL;
814	}
815
816	sig_len = pRec->wDataLength;
817
818	hostlen = strnlen(pRec->Data.SIG.pNameSigner, DNS_MAX_NAME_LENGTH);
819
820	rr->u.sig = malloc(sizeof(*rr->u.sig)
821			      + hostlen + sig_len);
822	if (rr->u.sig == NULL) {
823	    dns_free_rr(rr);
824	    return NULL;
825	}
826	rr->u.sig->type           = pRec->Data.SIG.wTypeCovered;
827	rr->u.sig->algorithm      = pRec->Data.SIG.chAlgorithm;
828	rr->u.sig->labels         = pRec->Data.SIG.chLabelCount;
829	rr->u.sig->orig_ttl       = pRec->Data.SIG.dwOriginalTtl;
830	rr->u.sig->sig_expiration = pRec->Data.SIG.dwExpiration;
831	rr->u.sig->sig_inception  = pRec->Data.SIG.dwTimeSigned;
832	rr->u.sig->key_tag        = pRec->Data.SIG.wKeyTag;
833	rr->u.sig->sig_len        = sig_len;
834	memcpy_s (rr->u.sig->sig_data, sig_len,
835		  pRec->Data.SIG.Signature, sig_len);
836	rr->u.sig->signer         = &rr->u.sig->sig_data[sig_len];
837	strcpy_s(rr->u.sig->signer, hostlen + 1, pRec->Data.SIG.pNameSigner);
838	break;
839    }
840
841#ifdef DNS_TYPE_DS
842    case rk_ns_t_ds: {
843	rr->u.ds = malloc (sizeof(*rr->u.ds) + pRec->Data.DS.wDigestLength - 1);
844	if (rr->u.ds == NULL) {
845	    dns_free_rr(rr);
846	    return NULL;
847	}
848
849	rr->u.ds->key_tag     = pRec->Data.DS.wKeyTag;
850	rr->u.ds->algorithm   = pRec->Data.DS.chAlgorithm;
851	rr->u.ds->digest_type = pRec->Data.DS.chDigestType;
852	rr->u.ds->digest_len  = pRec->Data.DS.wDigestLength;
853	memcpy_s (rr->u.ds->digest_data, pRec->Data.DS.wDigestLength,
854		  pRec->Data.DS.Digest, pRec->Data.DS.wDigestLength);
855	break;
856    }
857#endif
858
859    default:
860	dns_free_rr(rr);
861	return NULL;
862    }
863
864    rr->next = parse_dns_record(pRec->pNext);
865    return rr;
866}
867
868ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
869rk_dns_lookup(const char *domain, const char *type_name)
870{
871    DNS_STATUS status;
872    int type;
873    PDNS_RECORD pRec = NULL;
874    struct rk_dns_reply * r = NULL;
875
876    __try {
877
878	type = rk_dns_string_to_type(type_name);
879	if(type == -1) {
880	    if(_resolve_debug)
881		fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
882			type_name);
883	    return NULL;
884	}
885
886	status = DnsQuery_UTF8(domain, type, DNS_QUERY_STANDARD, NULL,
887			       &pRec, NULL);
888	if (status != ERROR_SUCCESS)
889	    return NULL;
890
891	r = calloc(1, sizeof(*r));
892	r->q.domain = strdup(domain);
893	r->q.type = type;
894	r->q.class = 0;
895
896	r->head = parse_dns_record(pRec);
897
898	if (r->head == NULL) {
899	    rk_dns_free_data(r);
900	    return NULL;
901	} else {
902	    return r;
903	}
904
905    } __finally {
906
907	if (pRec)
908	    DnsRecordListFree(pRec, DnsFreeRecordList);
909
910    }
911}
912#endif	/* HAVE_WINDNS */
913
914#else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
915
916ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
917rk_dns_lookup(const char *domain, const char *type_name)
918{
919    return NULL;
920}
921
922ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
923rk_dns_free_data(struct rk_dns_reply *r)
924{
925}
926
927ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
928rk_dns_srv_order(struct rk_dns_reply *r)
929{
930}
931
932#endif
933