155682Smarkm/*
2233294Sstas * Copyright (c) 1995 - 2006 Kungliga Tekniska H��gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
5233294Sstas *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
9233294Sstas *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
12233294Sstas *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
16233294Sstas *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
20233294Sstas *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
34233294Sstas
3555682Smarkm#include <config.h>
36233294Sstas
3755682Smarkm#include "roken.h"
3855682Smarkm#ifdef HAVE_ARPA_NAMESER_H
3955682Smarkm#include <arpa/nameser.h>
4055682Smarkm#endif
4155682Smarkm#ifdef HAVE_RESOLV_H
4255682Smarkm#include <resolv.h>
4355682Smarkm#endif
44233294Sstas#ifdef HAVE_DNS_H
45233294Sstas#include <dns.h>
46233294Sstas#endif
4755682Smarkm#include "resolve.h"
4855682Smarkm
4990926Snectar#include <assert.h>
5055682Smarkm
51178825Sdfr#ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
52103423Snectar#undef HAVE_RES_NSEARCH
53178825Sdfr#endif
5455682Smarkm
55178825Sdfr#define DECL(X) {#X, rk_ns_t_##X}
5655682Smarkm
5755682Smarkmstatic struct stot{
5855682Smarkm    const char *name;
5955682Smarkm    int type;
6055682Smarkm}stot[] = {
61178825Sdfr    DECL(a),
62178825Sdfr    DECL(aaaa),
63178825Sdfr    DECL(ns),
64178825Sdfr    DECL(cname),
65178825Sdfr    DECL(soa),
66178825Sdfr    DECL(ptr),
67178825Sdfr    DECL(mx),
68178825Sdfr    DECL(txt),
69178825Sdfr    DECL(afsdb),
70178825Sdfr    DECL(sig),
71178825Sdfr    DECL(key),
72178825Sdfr    DECL(srv),
73178825Sdfr    DECL(naptr),
74178825Sdfr    DECL(sshfp),
75178825Sdfr    DECL(ds),
7655682Smarkm    {NULL, 	0}
7755682Smarkm};
7855682Smarkm
7972445Sassarint _resolve_debug = 0;
8055682Smarkm
81233294SstasROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
82233294Sstasrk_dns_string_to_type(const char *name)
8355682Smarkm{
8455682Smarkm    struct stot *p = stot;
8555682Smarkm    for(p = stot; p->name; p++)
8655682Smarkm	if(strcasecmp(name, p->name) == 0)
8755682Smarkm	    return p->type;
8855682Smarkm    return -1;
8955682Smarkm}
9055682Smarkm
91233294SstasROKEN_LIB_FUNCTION const char * ROKEN_LIB_CALL
92233294Sstasrk_dns_type_to_string(int type)
9355682Smarkm{
9455682Smarkm    struct stot *p = stot;
9555682Smarkm    for(p = stot; p->name; p++)
9655682Smarkm	if(type == p->type)
9755682Smarkm	    return p->name;
9855682Smarkm    return NULL;
9955682Smarkm}
10055682Smarkm
101233294Sstas#if ((defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)) || defined(HAVE_WINDNS)
102178825Sdfr
103178825Sdfrstatic void
104233294Sstasdns_free_rr(struct rk_resource_record *rr)
105178825Sdfr{
106178825Sdfr    if(rr->domain)
107178825Sdfr	free(rr->domain);
108178825Sdfr    if(rr->u.data)
109178825Sdfr	free(rr->u.data);
110178825Sdfr    free(rr);
111178825Sdfr}
112178825Sdfr
113233294SstasROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
114233294Sstasrk_dns_free_data(struct rk_dns_reply *r)
11555682Smarkm{
116233294Sstas    struct rk_resource_record *rr;
11755682Smarkm    if(r->q.domain)
11855682Smarkm	free(r->q.domain);
11955682Smarkm    for(rr = r->head; rr;){
120233294Sstas	struct rk_resource_record *tmp = rr;
12155682Smarkm	rr = rr->next;
122178825Sdfr	dns_free_rr(tmp);
12355682Smarkm    }
12455682Smarkm    free (r);
12555682Smarkm}
12655682Smarkm
127233294Sstas#ifndef HAVE_WINDNS
128233294Sstas
129107207Snectarstatic int
130233294Sstasparse_record(const unsigned char *data, const unsigned char *end_data,
131233294Sstas	     const unsigned char **pp, struct rk_resource_record **ret_rr)
132107207Snectar{
133233294Sstas    struct rk_resource_record *rr;
134233294Sstas    int type, class, ttl;
135233294Sstas    unsigned size;
136107207Snectar    int status;
137107207Snectar    char host[MAXDNAME];
138107207Snectar    const unsigned char *p = *pp;
139178825Sdfr
140178825Sdfr    *ret_rr = NULL;
141178825Sdfr
142107207Snectar    status = dn_expand(data, end_data, p, host, sizeof(host));
143233294Sstas    if(status < 0)
144107207Snectar	return -1;
145107207Snectar    if (p + status + 10 > end_data)
146107207Snectar	return -1;
147178825Sdfr
148107207Snectar    p += status;
149107207Snectar    type = (p[0] << 8) | p[1];
150107207Snectar    p += 2;
151107207Snectar    class = (p[0] << 8) | p[1];
152107207Snectar    p += 2;
153107207Snectar    ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
154107207Snectar    p += 4;
155107207Snectar    size = (p[0] << 8) | p[1];
156107207Snectar    p += 2;
157107207Snectar
158107207Snectar    if (p + size > end_data)
159107207Snectar	return -1;
160107207Snectar
161178825Sdfr    rr = calloc(1, sizeof(*rr));
162233294Sstas    if(rr == NULL)
163107207Snectar	return -1;
164178825Sdfr    rr->domain = strdup(host);
165178825Sdfr    if(rr->domain == NULL) {
166178825Sdfr	dns_free_rr(rr);
167107207Snectar	return -1;
168107207Snectar    }
169178825Sdfr    rr->type = type;
170178825Sdfr    rr->class = class;
171178825Sdfr    rr->ttl = ttl;
172178825Sdfr    rr->size = size;
173107207Snectar    switch(type){
174178825Sdfr    case rk_ns_t_ns:
175178825Sdfr    case rk_ns_t_cname:
176178825Sdfr    case rk_ns_t_ptr:
177107207Snectar	status = dn_expand(data, end_data, p, host, sizeof(host));
178107207Snectar	if(status < 0) {
179178825Sdfr	    dns_free_rr(rr);
180107207Snectar	    return -1;
181107207Snectar	}
182178825Sdfr	rr->u.txt = strdup(host);
183178825Sdfr	if(rr->u.txt == NULL) {
184178825Sdfr	    dns_free_rr(rr);
185107207Snectar	    return -1;
186107207Snectar	}
187107207Snectar	break;
188178825Sdfr    case rk_ns_t_mx:
189178825Sdfr    case rk_ns_t_afsdb:{
190120945Snectar	size_t hostlen;
191120945Snectar
192107207Snectar	status = dn_expand(data, end_data, p + 2, host, sizeof(host));
193107207Snectar	if(status < 0){
194178825Sdfr	    dns_free_rr(rr);
195107207Snectar	    return -1;
196107207Snectar	}
197233294Sstas	if ((size_t)status + 2 > size) {
198178825Sdfr	    dns_free_rr(rr);
199107207Snectar	    return -1;
200107207Snectar	}
201107207Snectar
202120945Snectar	hostlen = strlen(host);
203233294Sstas	rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
204120945Snectar						hostlen);
205178825Sdfr	if(rr->u.mx == NULL) {
206178825Sdfr	    dns_free_rr(rr);
207107207Snectar	    return -1;
208107207Snectar	}
209178825Sdfr	rr->u.mx->preference = (p[0] << 8) | p[1];
210178825Sdfr	strlcpy(rr->u.mx->domain, host, hostlen + 1);
211107207Snectar	break;
212107207Snectar    }
213178825Sdfr    case rk_ns_t_srv:{
214120945Snectar	size_t hostlen;
215107207Snectar	status = dn_expand(data, end_data, p + 6, host, sizeof(host));
216107207Snectar	if(status < 0){
217178825Sdfr	    dns_free_rr(rr);
218107207Snectar	    return -1;
219107207Snectar	}
220233294Sstas	if ((size_t)status + 6 > size) {
221178825Sdfr	    dns_free_rr(rr);
222107207Snectar	    return -1;
223107207Snectar	}
224107207Snectar
225120945Snectar	hostlen = strlen(host);
226233294Sstas	rr->u.srv =
227233294Sstas	    (struct srv_record*)malloc(sizeof(struct srv_record) +
228120945Snectar				       hostlen);
229178825Sdfr	if(rr->u.srv == NULL) {
230178825Sdfr	    dns_free_rr(rr);
231107207Snectar	    return -1;
232107207Snectar	}
233178825Sdfr	rr->u.srv->priority = (p[0] << 8) | p[1];
234178825Sdfr	rr->u.srv->weight = (p[2] << 8) | p[3];
235178825Sdfr	rr->u.srv->port = (p[4] << 8) | p[5];
236178825Sdfr	strlcpy(rr->u.srv->target, host, hostlen + 1);
237107207Snectar	break;
238107207Snectar    }
239178825Sdfr    case rk_ns_t_txt:{
240233294Sstas	if(size == 0 || size < (unsigned)(*p + 1)) {
241178825Sdfr	    dns_free_rr(rr);
242107207Snectar	    return -1;
243107207Snectar	}
244178825Sdfr	rr->u.txt = (char*)malloc(*p + 1);
245178825Sdfr	if(rr->u.txt == NULL) {
246178825Sdfr	    dns_free_rr(rr);
247107207Snectar	    return -1;
248107207Snectar	}
249178825Sdfr	strncpy(rr->u.txt, (const char*)(p + 1), *p);
250178825Sdfr	rr->u.txt[*p] = '\0';
251107207Snectar	break;
252107207Snectar    }
253178825Sdfr    case rk_ns_t_key : {
254107207Snectar	size_t key_len;
255107207Snectar
256107207Snectar	if (size < 4) {
257178825Sdfr	    dns_free_rr(rr);
258107207Snectar	    return -1;
259107207Snectar	}
260107207Snectar
261107207Snectar	key_len = size - 4;
262178825Sdfr	rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
263178825Sdfr	if (rr->u.key == NULL) {
264178825Sdfr	    dns_free_rr(rr);
265107207Snectar	    return -1;
266107207Snectar	}
267107207Snectar
268178825Sdfr	rr->u.key->flags     = (p[0] << 8) | p[1];
269178825Sdfr	rr->u.key->protocol  = p[2];
270178825Sdfr	rr->u.key->algorithm = p[3];
271178825Sdfr	rr->u.key->key_len   = key_len;
272178825Sdfr	memcpy (rr->u.key->key_data, p + 4, key_len);
273107207Snectar	break;
274107207Snectar    }
275178825Sdfr    case rk_ns_t_sig : {
276120945Snectar	size_t sig_len, hostlen;
277107207Snectar
278107207Snectar	if(size <= 18) {
279178825Sdfr	    dns_free_rr(rr);
280107207Snectar	    return -1;
281107207Snectar	}
282107207Snectar	status = dn_expand (data, end_data, p + 18, host, sizeof(host));
283107207Snectar	if (status < 0) {
284178825Sdfr	    dns_free_rr(rr);
285107207Snectar	    return -1;
286107207Snectar	}
287233294Sstas	if ((size_t)status + 18 > size) {
288178825Sdfr	    dns_free_rr(rr);
289107207Snectar	    return -1;
290107207Snectar	}
291107207Snectar
292107207Snectar	/* the signer name is placed after the sig_data, to make it
293178825Sdfr           easy to free this structure; the size calculation below
294107207Snectar           includes the zero-termination if the structure itself.
295107207Snectar	   don't you just love C?
296107207Snectar	*/
297107207Snectar	sig_len = size - 18 - status;
298120945Snectar	hostlen = strlen(host);
299178825Sdfr	rr->u.sig = malloc(sizeof(*rr->u.sig)
300120945Snectar			      + hostlen + sig_len);
301178825Sdfr	if (rr->u.sig == NULL) {
302178825Sdfr	    dns_free_rr(rr);
303107207Snectar	    return -1;
304107207Snectar	}
305178825Sdfr	rr->u.sig->type           = (p[0] << 8) | p[1];
306178825Sdfr	rr->u.sig->algorithm      = p[2];
307178825Sdfr	rr->u.sig->labels         = p[3];
308178825Sdfr	rr->u.sig->orig_ttl       = (p[4] << 24) | (p[5] << 16)
309107207Snectar	    | (p[6] << 8) | p[7];
310178825Sdfr	rr->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16)
311107207Snectar	    | (p[10] << 8) | p[11];
312178825Sdfr	rr->u.sig->sig_inception  = (p[12] << 24) | (p[13] << 16)
313107207Snectar	    | (p[14] << 8) | p[15];
314178825Sdfr	rr->u.sig->key_tag        = (p[16] << 8) | p[17];
315178825Sdfr	rr->u.sig->sig_len        = sig_len;
316178825Sdfr	memcpy (rr->u.sig->sig_data, p + 18 + status, sig_len);
317178825Sdfr	rr->u.sig->signer         = &rr->u.sig->sig_data[sig_len];
318178825Sdfr	strlcpy(rr->u.sig->signer, host, hostlen + 1);
319107207Snectar	break;
320107207Snectar    }
321107207Snectar
322178825Sdfr    case rk_ns_t_cert : {
323107207Snectar	size_t cert_len;
324107207Snectar
325107207Snectar	if (size < 5) {
326178825Sdfr	    dns_free_rr(rr);
327107207Snectar	    return -1;
328107207Snectar	}
329107207Snectar
330107207Snectar	cert_len = size - 5;
331178825Sdfr	rr->u.cert = malloc (sizeof(*rr->u.cert) + cert_len - 1);
332178825Sdfr	if (rr->u.cert == NULL) {
333178825Sdfr	    dns_free_rr(rr);
334107207Snectar	    return -1;
335107207Snectar	}
336107207Snectar
337178825Sdfr	rr->u.cert->type      = (p[0] << 8) | p[1];
338178825Sdfr	rr->u.cert->tag       = (p[2] << 8) | p[3];
339178825Sdfr	rr->u.cert->algorithm = p[4];
340178825Sdfr	rr->u.cert->cert_len  = cert_len;
341178825Sdfr	memcpy (rr->u.cert->cert_data, p + 5, cert_len);
342107207Snectar	break;
343107207Snectar    }
344178825Sdfr    case rk_ns_t_sshfp : {
345178825Sdfr	size_t sshfp_len;
346178825Sdfr
347178825Sdfr	if (size < 2) {
348178825Sdfr	    dns_free_rr(rr);
349178825Sdfr	    return -1;
350178825Sdfr	}
351178825Sdfr
352178825Sdfr	sshfp_len = size - 2;
353178825Sdfr
354178825Sdfr	rr->u.sshfp = malloc (sizeof(*rr->u.sshfp) + sshfp_len - 1);
355178825Sdfr	if (rr->u.sshfp == NULL) {
356178825Sdfr	    dns_free_rr(rr);
357178825Sdfr	    return -1;
358178825Sdfr	}
359178825Sdfr
360178825Sdfr	rr->u.sshfp->algorithm = p[0];
361178825Sdfr	rr->u.sshfp->type      = p[1];
362178825Sdfr	rr->u.sshfp->sshfp_len  = sshfp_len;
363178825Sdfr	memcpy (rr->u.sshfp->sshfp_data, p + 2, sshfp_len);
364178825Sdfr	break;
365178825Sdfr    }
366178825Sdfr    case rk_ns_t_ds: {
367178825Sdfr	size_t digest_len;
368178825Sdfr
369178825Sdfr	if (size < 4) {
370178825Sdfr	    dns_free_rr(rr);
371178825Sdfr	    return -1;
372178825Sdfr	}
373178825Sdfr
374178825Sdfr	digest_len = size - 4;
375178825Sdfr
376178825Sdfr	rr->u.ds = malloc (sizeof(*rr->u.ds) + digest_len - 1);
377178825Sdfr	if (rr->u.ds == NULL) {
378178825Sdfr	    dns_free_rr(rr);
379178825Sdfr	    return -1;
380178825Sdfr	}
381178825Sdfr
382178825Sdfr	rr->u.ds->key_tag     = (p[0] << 8) | p[1];
383178825Sdfr	rr->u.ds->algorithm   = p[2];
384178825Sdfr	rr->u.ds->digest_type = p[3];
385178825Sdfr	rr->u.ds->digest_len  = digest_len;
386178825Sdfr	memcpy (rr->u.ds->digest_data, p + 4, digest_len);
387178825Sdfr	break;
388178825Sdfr    }
389107207Snectar    default:
390178825Sdfr	rr->u.data = (unsigned char*)malloc(size);
391178825Sdfr	if(size != 0 && rr->u.data == NULL) {
392178825Sdfr	    dns_free_rr(rr);
393107207Snectar	    return -1;
394107207Snectar	}
395178825Sdfr	if (size)
396178825Sdfr	    memcpy(rr->u.data, p, size);
397107207Snectar    }
398107207Snectar    *pp = p + size;
399178825Sdfr    *ret_rr = rr;
400178825Sdfr
401107207Snectar    return 0;
402107207Snectar}
403107207Snectar
404103423Snectar#ifndef TEST_RESOLVE
405103423Snectarstatic
406103423Snectar#endif
407233294Sstasstruct rk_dns_reply*
408103423Snectarparse_reply(const unsigned char *data, size_t len)
40955682Smarkm{
410102644Snectar    const unsigned char *p;
41155682Smarkm    int status;
412233294Sstas    size_t i;
413107207Snectar    char host[MAXDNAME];
414102644Snectar    const unsigned char *end_data = data + len;
415233294Sstas    struct rk_dns_reply *r;
416233294Sstas    struct rk_resource_record **rr;
417233294Sstas
41855682Smarkm    r = calloc(1, sizeof(*r));
41955682Smarkm    if (r == NULL)
42055682Smarkm	return NULL;
42155682Smarkm
42255682Smarkm    p = data;
423178825Sdfr
424178825Sdfr    r->h.id = (p[0] << 8) | p[1];
425178825Sdfr    r->h.flags = 0;
426178825Sdfr    if (p[2] & 0x01)
427178825Sdfr	r->h.flags |= rk_DNS_HEADER_RESPONSE_FLAG;
428178825Sdfr    r->h.opcode = (p[2] >> 1) & 0xf;
429178825Sdfr    if (p[2] & 0x20)
430178825Sdfr	r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
431178825Sdfr    if (p[2] & 0x40)
432178825Sdfr	r->h.flags |= rk_DNS_HEADER_TRUNCATED_MESSAGE;
433178825Sdfr    if (p[2] & 0x80)
434178825Sdfr	r->h.flags |= rk_DNS_HEADER_RECURSION_DESIRED;
435178825Sdfr    if (p[3] & 0x01)
436178825Sdfr	r->h.flags |= rk_DNS_HEADER_RECURSION_AVAILABLE;
437178825Sdfr    if (p[3] & 0x04)
438178825Sdfr	r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
439178825Sdfr    if (p[3] & 0x08)
440178825Sdfr	r->h.flags |= rk_DNS_HEADER_CHECKING_DISABLED;
441178825Sdfr    r->h.response_code = (p[3] >> 4) & 0xf;
442178825Sdfr    r->h.qdcount = (p[4] << 8) | p[5];
443178825Sdfr    r->h.ancount = (p[6] << 8) | p[7];
444178825Sdfr    r->h.nscount = (p[8] << 8) | p[9];
445178825Sdfr    r->h.arcount = (p[10] << 8) | p[11];
446178825Sdfr
44755682Smarkm    p += 12;
448178825Sdfr
449178825Sdfr    if(r->h.qdcount != 1) {
450107207Snectar	free(r);
451107207Snectar	return NULL;
452107207Snectar    }
453102644Snectar    status = dn_expand(data, end_data, p, host, sizeof(host));
45455682Smarkm    if(status < 0){
455233294Sstas	rk_dns_free_data(r);
45655682Smarkm	return NULL;
45755682Smarkm    }
45855682Smarkm    r->q.domain = strdup(host);
45955682Smarkm    if(r->q.domain == NULL) {
460233294Sstas	rk_dns_free_data(r);
46155682Smarkm	return NULL;
46255682Smarkm    }
463102644Snectar    if (p + status + 4 > end_data) {
464233294Sstas	rk_dns_free_data(r);
465102644Snectar	return NULL;
466102644Snectar    }
46755682Smarkm    p += status;
46855682Smarkm    r->q.type = (p[0] << 8 | p[1]);
46955682Smarkm    p += 2;
47055682Smarkm    r->q.class = (p[0] << 8 | p[1]);
47155682Smarkm    p += 2;
472233294Sstas
47355682Smarkm    rr = &r->head;
474178825Sdfr    for(i = 0; i < r->h.ancount; i++) {
475107207Snectar	if(parse_record(data, end_data, &p, rr) != 0) {
476233294Sstas	    rk_dns_free_data(r);
47755682Smarkm	    return NULL;
47855682Smarkm	}
479107207Snectar	rr = &(*rr)->next;
480107207Snectar    }
481178825Sdfr    for(i = 0; i < r->h.nscount; i++) {
482107207Snectar	if(parse_record(data, end_data, &p, rr) != 0) {
483233294Sstas	    rk_dns_free_data(r);
484102644Snectar	    return NULL;
485102644Snectar	}
486107207Snectar	rr = &(*rr)->next;
487107207Snectar    }
488178825Sdfr    for(i = 0; i < r->h.arcount; i++) {
489107207Snectar	if(parse_record(data, end_data, &p, rr) != 0) {
490233294Sstas	    rk_dns_free_data(r);
491102644Snectar	    return NULL;
492102644Snectar	}
49355682Smarkm	rr = &(*rr)->next;
49455682Smarkm    }
49555682Smarkm    *rr = NULL;
49655682Smarkm    return r;
49755682Smarkm}
49855682Smarkm
499178825Sdfr#ifdef HAVE_RES_NSEARCH
500178825Sdfr#ifdef HAVE_RES_NDESTROY
501178825Sdfr#define rk_res_free(x) res_ndestroy(x)
502178825Sdfr#else
503178825Sdfr#define rk_res_free(x) res_nclose(x)
504178825Sdfr#endif
505178825Sdfr#endif
506178825Sdfr
507233294Sstas#if defined(HAVE_DNS_SEARCH)
508233294Sstas#define resolve_search(h,n,c,t,r,l) \
509233294Sstas    	((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize))
510233294Sstas#define resolve_free_handle(h) dns_free(h)
511233294Sstas#elif defined(HAVE_RES_NSEARCH)
512233294Sstas#define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l)
513233294Sstas#define resolve_free_handle(h) rk_res_free(h);
514233294Sstas#else
515233294Sstas#define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l)
516233294Sstas#define handle 0
517233294Sstas#define resolve_free_handle(h)
518233294Sstas#endif
519233294Sstas
520233294Sstas
521233294Sstasstatic struct rk_dns_reply *
52255682Smarkmdns_lookup_int(const char *domain, int rr_class, int rr_type)
52355682Smarkm{
524233294Sstas    struct rk_dns_reply *r;
525233294Sstas    void *reply = NULL;
526233294Sstas    int size, len;
527233294Sstas#if defined(HAVE_DNS_SEARCH)
528233294Sstas    struct sockaddr_storage from;
529233294Sstas    uint32_t fromsize = sizeof(from);
530233294Sstas    dns_handle_t handle;
531233294Sstas
532233294Sstas    handle = dns_open(NULL);
533233294Sstas    if (handle == NULL)
534233294Sstas	return NULL;
535233294Sstas#elif defined(HAVE_RES_NSEARCH)
536178825Sdfr    struct __res_state state;
537233294Sstas    struct __res_state *handle = &state;
538233294Sstas
539178825Sdfr    memset(&state, 0, sizeof(state));
540233294Sstas    if(res_ninit(handle))
541103423Snectar	return NULL; /* is this the best we can do? */
542102644Snectar#endif
543233294Sstas
544233294Sstas    len = 1500;
545233294Sstas    while(1) {
546178825Sdfr	if (reply) {
547178825Sdfr	    free(reply);
548178825Sdfr	    reply = NULL;
549178825Sdfr	}
550178825Sdfr	if (_resolve_debug) {
551233294Sstas#if defined(HAVE_DNS_SEARCH)
552233294Sstas	    dns_set_debug(handle, 1);
553233294Sstas#elif defined(HAVE_RES_NSEARCH)
554178825Sdfr	    state.options |= RES_DEBUG;
555102644Snectar#endif
556178825Sdfr	    fprintf(stderr, "dns_lookup(%s, %d, %s), buffer size %d\n", domain,
557233294Sstas		    rr_class, rk_dns_type_to_string(rr_type), len);
558178825Sdfr	}
559233294Sstas	reply = malloc(len);
560178825Sdfr	if (reply == NULL) {
561233294Sstas	    resolve_free_handle(handle);
562178825Sdfr	    return NULL;
563178825Sdfr	}
564233294Sstas
565233294Sstas	size = resolve_search(handle, domain, rr_class, rr_type, reply, len);
566233294Sstas
567178825Sdfr	if (_resolve_debug) {
568178825Sdfr	    fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n",
569233294Sstas		    domain, rr_class, rk_dns_type_to_string(rr_type), size);
570178825Sdfr	}
571233294Sstas	if (size > len) {
572233294Sstas	    /* resolver thinks it know better, go for it */
573233294Sstas	    len = size;
574233294Sstas	} else if (size > 0) {
575233294Sstas	    /* got a good reply */
576233294Sstas	    break;
577233294Sstas	} else if (size <= 0 && len < rk_DNS_MAX_PACKET_SIZE) {
578233294Sstas	    len *= 2;
579233294Sstas	    if (len > rk_DNS_MAX_PACKET_SIZE)
580233294Sstas		len = rk_DNS_MAX_PACKET_SIZE;
581233294Sstas	} else {
582233294Sstas	    /* the end, leave */
583233294Sstas	    resolve_free_handle(handle);
584178825Sdfr	    free(reply);
585178825Sdfr	    return NULL;
586178825Sdfr	}
587233294Sstas    }
588178825Sdfr
589178825Sdfr    len = min(len, size);
590178825Sdfr    r = parse_reply(reply, len);
591178825Sdfr    free(reply);
592233294Sstas
593233294Sstas    resolve_free_handle(handle);
594233294Sstas
595178825Sdfr    return r;
59655682Smarkm}
59755682Smarkm
598233294SstasROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
599233294Sstasrk_dns_lookup(const char *domain, const char *type_name)
60055682Smarkm{
60155682Smarkm    int type;
602233294Sstas
603233294Sstas    type = rk_dns_string_to_type(type_name);
60455682Smarkm    if(type == -1) {
60555682Smarkm	if(_resolve_debug)
606233294Sstas	    fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
60755682Smarkm		    type_name);
60855682Smarkm	return NULL;
60955682Smarkm    }
610233294Sstas    return dns_lookup_int(domain, rk_ns_c_in, type);
61155682Smarkm}
61255682Smarkm
613233294Sstas#endif	/* !HAVE_WINDNS */
614233294Sstas
61590926Snectarstatic int
61690926Snectarcompare_srv(const void *a, const void *b)
61790926Snectar{
618233294Sstas    const struct rk_resource_record *const* aa = a, *const* bb = b;
61990926Snectar
62090926Snectar    if((*aa)->u.srv->priority == (*bb)->u.srv->priority)
62190926Snectar	return ((*aa)->u.srv->weight - (*bb)->u.srv->weight);
62290926Snectar    return ((*aa)->u.srv->priority - (*bb)->u.srv->priority);
62390926Snectar}
62490926Snectar
62590926Snectar/* try to rearrange the srv-records by the algorithm in RFC2782 */
626233294SstasROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
627233294Sstasrk_dns_srv_order(struct rk_dns_reply *r)
62890926Snectar{
629233294Sstas    struct rk_resource_record **srvs, **ss, **headp;
630233294Sstas    struct rk_resource_record *rr;
63190926Snectar    int num_srv = 0;
63290926Snectar
633233294Sstas    rk_random_init();
63490926Snectar
635233294Sstas    for(rr = r->head; rr; rr = rr->next)
636178825Sdfr	if(rr->type == rk_ns_t_srv)
63790926Snectar	    num_srv++;
63890926Snectar
63990926Snectar    if(num_srv == 0)
64090926Snectar	return;
64190926Snectar
64290926Snectar    srvs = malloc(num_srv * sizeof(*srvs));
64390926Snectar    if(srvs == NULL)
64490926Snectar	return; /* XXX not much to do here */
645233294Sstas
64690926Snectar    /* unlink all srv-records from the linked list and put them in
64790926Snectar       a vector */
64890926Snectar    for(ss = srvs, headp = &r->head; *headp; )
649178825Sdfr	if((*headp)->type == rk_ns_t_srv) {
65090926Snectar	    *ss = *headp;
65190926Snectar	    *headp = (*headp)->next;
65290926Snectar	    (*ss)->next = NULL;
65390926Snectar	    ss++;
65490926Snectar	} else
65590926Snectar	    headp = &(*headp)->next;
656233294Sstas
65790926Snectar    /* sort them by priority and weight */
65890926Snectar    qsort(srvs, num_srv, sizeof(*srvs), compare_srv);
65990926Snectar
660233294Sstas    headp = &r->head;
66190926Snectar
66290926Snectar    for(ss = srvs; ss < srvs + num_srv; ) {
66390926Snectar	int sum, rnd, count;
664233294Sstas	struct rk_resource_record **ee, **tt;
66590926Snectar	/* find the last record with the same priority and count the
66690926Snectar           sum of all weights */
66790926Snectar	for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) {
668178825Sdfr	    assert(*tt != NULL);
66990926Snectar	    if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
67090926Snectar		break;
67190926Snectar	    sum += (*tt)->u.srv->weight;
67290926Snectar	}
67390926Snectar	ee = tt;
67490926Snectar	/* ss is now the first record of this priority and ee is the
67590926Snectar           first of the next */
67690926Snectar	while(ss < ee) {
677233294Sstas	    rnd = rk_random() % (sum + 1);
67890926Snectar	    for(count = 0, tt = ss; ; tt++) {
67990926Snectar		if(*tt == NULL)
68090926Snectar		    continue;
68190926Snectar		count += (*tt)->u.srv->weight;
68290926Snectar		if(count >= rnd)
68390926Snectar		    break;
68490926Snectar	    }
68590926Snectar
68690926Snectar	    assert(tt < ee);
68790926Snectar
68890926Snectar	    /* insert the selected record at the tail (of the head) of
68990926Snectar               the list */
69090926Snectar	    (*tt)->next = *headp;
69190926Snectar	    *headp = *tt;
69290926Snectar	    headp = &(*tt)->next;
69390926Snectar	    sum -= (*tt)->u.srv->weight;
69490926Snectar	    *tt = NULL;
69590926Snectar	    while(ss < ee && *ss == NULL)
69690926Snectar		ss++;
69790926Snectar	}
69890926Snectar    }
699233294Sstas
70090926Snectar    free(srvs);
70190926Snectar    return;
70290926Snectar}
70390926Snectar
704233294Sstas#ifdef HAVE_WINDNS
705233294Sstas
706233294Sstas#include <WinDNS.h>
707233294Sstas
708233294Sstasstatic struct rk_resource_record *
709233294Sstasparse_dns_record(PDNS_RECORD pRec)
710233294Sstas{
711233294Sstas    struct rk_resource_record * rr;
712233294Sstas
713233294Sstas    if (pRec == NULL)
714233294Sstas	return NULL;
715233294Sstas
716233294Sstas    rr = calloc(1, sizeof(*rr));
717233294Sstas
718233294Sstas    rr->domain = strdup(pRec->pName);
719233294Sstas    rr->type = pRec->wType;
720233294Sstas    rr->class = 0;
721233294Sstas    rr->ttl = pRec->dwTtl;
722233294Sstas    rr->size = 0;
723233294Sstas
724233294Sstas    switch (rr->type) {
725233294Sstas    case rk_ns_t_ns:
726233294Sstas    case rk_ns_t_cname:
727233294Sstas    case rk_ns_t_ptr:
728233294Sstas	rr->u.txt = strdup(pRec->Data.NS.pNameHost);
729233294Sstas	if(rr->u.txt == NULL) {
730233294Sstas	    dns_free_rr(rr);
731233294Sstas	    return NULL;
732233294Sstas	}
733233294Sstas	break;
734233294Sstas
735233294Sstas    case rk_ns_t_mx:
736233294Sstas    case rk_ns_t_afsdb:{
737233294Sstas	size_t hostlen = strnlen(pRec->Data.MX.pNameExchange, DNS_MAX_NAME_LENGTH);
738233294Sstas
739233294Sstas	rr->u.mx = (struct mx_record *)malloc(sizeof(struct mx_record) +
740233294Sstas					      hostlen);
741233294Sstas	if (rr->u.mx == NULL) {
742233294Sstas	    dns_free_rr(rr);
743233294Sstas	    return NULL;
744233294Sstas	}
745233294Sstas
746233294Sstas	strcpy_s(rr->u.mx->domain, hostlen + 1, pRec->Data.MX.pNameExchange);
747233294Sstas	rr->u.mx->preference = pRec->Data.MX.wPreference;
748233294Sstas	break;
749233294Sstas    }
750233294Sstas
751233294Sstas    case rk_ns_t_srv:{
752233294Sstas	size_t hostlen = strnlen(pRec->Data.SRV.pNameTarget, DNS_MAX_NAME_LENGTH);
753233294Sstas
754233294Sstas	rr->u.srv =
755233294Sstas	    (struct srv_record*)malloc(sizeof(struct srv_record) +
756233294Sstas				       hostlen);
757233294Sstas	if(rr->u.srv == NULL) {
758233294Sstas	    dns_free_rr(rr);
759233294Sstas	    return NULL;
760233294Sstas	}
761233294Sstas
762233294Sstas	rr->u.srv->priority = pRec->Data.SRV.wPriority;
763233294Sstas	rr->u.srv->weight = pRec->Data.SRV.wWeight;
764233294Sstas	rr->u.srv->port = pRec->Data.SRV.wPort;
765233294Sstas	strcpy_s(rr->u.srv->target, hostlen + 1, pRec->Data.SRV.pNameTarget);
766233294Sstas
767233294Sstas	break;
768233294Sstas    }
769233294Sstas
770233294Sstas    case rk_ns_t_txt:{
771233294Sstas	size_t len;
772233294Sstas
773233294Sstas	if (pRec->Data.TXT.dwStringCount == 0) {
774233294Sstas	    rr->u.txt = strdup("");
775233294Sstas	    break;
776233294Sstas	}
777233294Sstas
778233294Sstas	len = strnlen(pRec->Data.TXT.pStringArray[0], DNS_MAX_TEXT_STRING_LENGTH);
779233294Sstas
780233294Sstas	rr->u.txt = (char *)malloc(len + 1);
781233294Sstas	strcpy_s(rr->u.txt, len + 1, pRec->Data.TXT.pStringArray[0]);
782233294Sstas
783233294Sstas	break;
784233294Sstas    }
785233294Sstas
786233294Sstas    case rk_ns_t_key : {
787233294Sstas	size_t key_len;
788233294Sstas
789233294Sstas	if (pRec->wDataLength < 4) {
790233294Sstas	    dns_free_rr(rr);
791233294Sstas	    return NULL;
792233294Sstas	}
793233294Sstas
794233294Sstas	key_len = pRec->wDataLength - 4;
795233294Sstas	rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
796233294Sstas	if (rr->u.key == NULL) {
797233294Sstas	    dns_free_rr(rr);
798233294Sstas	    return NULL;
799233294Sstas	}
800233294Sstas
801233294Sstas	rr->u.key->flags     = pRec->Data.KEY.wFlags;
802233294Sstas	rr->u.key->protocol  = pRec->Data.KEY.chProtocol;
803233294Sstas	rr->u.key->algorithm = pRec->Data.KEY.chAlgorithm;
804233294Sstas	rr->u.key->key_len   = key_len;
805233294Sstas	memcpy_s (rr->u.key->key_data, key_len,
806233294Sstas		  pRec->Data.KEY.Key, key_len);
807233294Sstas	break;
808233294Sstas    }
809233294Sstas
810233294Sstas    case rk_ns_t_sig : {
811233294Sstas	size_t sig_len, hostlen;
812233294Sstas
813233294Sstas	if(pRec->wDataLength <= 18) {
814233294Sstas	    dns_free_rr(rr);
815233294Sstas	    return NULL;
816233294Sstas	}
817233294Sstas
818233294Sstas	sig_len = pRec->wDataLength;
819233294Sstas
820233294Sstas	hostlen = strnlen(pRec->Data.SIG.pNameSigner, DNS_MAX_NAME_LENGTH);
821233294Sstas
822233294Sstas	rr->u.sig = malloc(sizeof(*rr->u.sig)
823233294Sstas			      + hostlen + sig_len);
824233294Sstas	if (rr->u.sig == NULL) {
825233294Sstas	    dns_free_rr(rr);
826233294Sstas	    return NULL;
827233294Sstas	}
828233294Sstas	rr->u.sig->type           = pRec->Data.SIG.wTypeCovered;
829233294Sstas	rr->u.sig->algorithm      = pRec->Data.SIG.chAlgorithm;
830233294Sstas	rr->u.sig->labels         = pRec->Data.SIG.chLabelCount;
831233294Sstas	rr->u.sig->orig_ttl       = pRec->Data.SIG.dwOriginalTtl;
832233294Sstas	rr->u.sig->sig_expiration = pRec->Data.SIG.dwExpiration;
833233294Sstas	rr->u.sig->sig_inception  = pRec->Data.SIG.dwTimeSigned;
834233294Sstas	rr->u.sig->key_tag        = pRec->Data.SIG.wKeyTag;
835233294Sstas	rr->u.sig->sig_len        = sig_len;
836233294Sstas	memcpy_s (rr->u.sig->sig_data, sig_len,
837233294Sstas		  pRec->Data.SIG.Signature, sig_len);
838233294Sstas	rr->u.sig->signer         = &rr->u.sig->sig_data[sig_len];
839233294Sstas	strcpy_s(rr->u.sig->signer, hostlen + 1, pRec->Data.SIG.pNameSigner);
840233294Sstas	break;
841233294Sstas    }
842233294Sstas
843233294Sstas#ifdef DNS_TYPE_DS
844233294Sstas    case rk_ns_t_ds: {
845233294Sstas	rr->u.ds = malloc (sizeof(*rr->u.ds) + pRec->Data.DS.wDigestLength - 1);
846233294Sstas	if (rr->u.ds == NULL) {
847233294Sstas	    dns_free_rr(rr);
848233294Sstas	    return NULL;
849233294Sstas	}
850233294Sstas
851233294Sstas	rr->u.ds->key_tag     = pRec->Data.DS.wKeyTag;
852233294Sstas	rr->u.ds->algorithm   = pRec->Data.DS.chAlgorithm;
853233294Sstas	rr->u.ds->digest_type = pRec->Data.DS.chDigestType;
854233294Sstas	rr->u.ds->digest_len  = pRec->Data.DS.wDigestLength;
855233294Sstas	memcpy_s (rr->u.ds->digest_data, pRec->Data.DS.wDigestLength,
856233294Sstas		  pRec->Data.DS.Digest, pRec->Data.DS.wDigestLength);
857233294Sstas	break;
858233294Sstas    }
859233294Sstas#endif
860233294Sstas
861233294Sstas    default:
862233294Sstas	dns_free_rr(rr);
863233294Sstas	return NULL;
864233294Sstas    }
865233294Sstas
866233294Sstas    rr->next = parse_dns_record(pRec->pNext);
867233294Sstas    return rr;
868233294Sstas}
869233294Sstas
870233294SstasROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
871233294Sstasrk_dns_lookup(const char *domain, const char *type_name)
872233294Sstas{
873233294Sstas    DNS_STATUS status;
874233294Sstas    int type;
875233294Sstas    PDNS_RECORD pRec = NULL;
876233294Sstas    struct rk_dns_reply * r = NULL;
877233294Sstas
878233294Sstas    __try {
879233294Sstas
880233294Sstas	type = rk_dns_string_to_type(type_name);
881233294Sstas	if(type == -1) {
882233294Sstas	    if(_resolve_debug)
883233294Sstas		fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
884233294Sstas			type_name);
885233294Sstas	    return NULL;
886233294Sstas	}
887233294Sstas
888233294Sstas	status = DnsQuery_UTF8(domain, type, DNS_QUERY_STANDARD, NULL,
889233294Sstas			       &pRec, NULL);
890233294Sstas	if (status != ERROR_SUCCESS)
891233294Sstas	    return NULL;
892233294Sstas
893233294Sstas	r = calloc(1, sizeof(*r));
894233294Sstas	r->q.domain = strdup(domain);
895233294Sstas	r->q.type = type;
896233294Sstas	r->q.class = 0;
897233294Sstas
898233294Sstas	r->head = parse_dns_record(pRec);
899233294Sstas
900233294Sstas	if (r->head == NULL) {
901233294Sstas	    rk_dns_free_data(r);
902233294Sstas	    return NULL;
903233294Sstas	} else {
904233294Sstas	    return r;
905233294Sstas	}
906233294Sstas
907233294Sstas    } __finally {
908233294Sstas
909233294Sstas	if (pRec)
910233294Sstas	    DnsRecordListFree(pRec, DnsFreeRecordList);
911233294Sstas
912233294Sstas    }
913233294Sstas}
914233294Sstas#endif	/* HAVE_WINDNS */
915233294Sstas
91655682Smarkm#else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
91755682Smarkm
918233294SstasROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
919233294Sstasrk_dns_lookup(const char *domain, const char *type_name)
92055682Smarkm{
92155682Smarkm    return NULL;
92255682Smarkm}
92355682Smarkm
924233294SstasROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
925233294Sstasrk_dns_free_data(struct rk_dns_reply *r)
92655682Smarkm{
92755682Smarkm}
92855682Smarkm
929233294SstasROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
930233294Sstasrk_dns_srv_order(struct rk_dns_reply *r)
93190926Snectar{
93290926Snectar}
93390926Snectar
93455682Smarkm#endif
935