resolve.c revision 107207
1/*
2 * Copyright (c) 1995 - 2002 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#ifdef HAVE_CONFIG_H
35#include <config.h>
36#endif
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#include "resolve.h"
45
46#include <assert.h>
47
48RCSID("$Id: resolve.c,v 1.36.4.1 2002/10/21 14:48:15 joda Exp $");
49
50#undef HAVE_RES_NSEARCH
51#if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)
52
53#define DECL(X) {#X, T_##X}
54
55static struct stot{
56    const char *name;
57    int type;
58}stot[] = {
59    DECL(A),
60    DECL(NS),
61    DECL(CNAME),
62    DECL(SOA),
63    DECL(PTR),
64    DECL(MX),
65    DECL(TXT),
66    DECL(AFSDB),
67    DECL(SIG),
68    DECL(KEY),
69    DECL(SRV),
70    DECL(NAPTR),
71    {NULL, 	0}
72};
73
74int _resolve_debug = 0;
75
76int
77dns_string_to_type(const char *name)
78{
79    struct stot *p = stot;
80    for(p = stot; p->name; p++)
81	if(strcasecmp(name, p->name) == 0)
82	    return p->type;
83    return -1;
84}
85
86const char *
87dns_type_to_string(int type)
88{
89    struct stot *p = stot;
90    for(p = stot; p->name; p++)
91	if(type == p->type)
92	    return p->name;
93    return NULL;
94}
95
96void
97dns_free_data(struct dns_reply *r)
98{
99    struct resource_record *rr;
100    if(r->q.domain)
101	free(r->q.domain);
102    for(rr = r->head; rr;){
103	struct resource_record *tmp = rr;
104	if(rr->domain)
105	    free(rr->domain);
106	if(rr->u.data)
107	    free(rr->u.data);
108	rr = rr->next;
109	free(tmp);
110    }
111    free (r);
112}
113
114static int
115parse_record(const unsigned char *data, const unsigned char *end_data,
116	     const unsigned char **pp, struct resource_record **rr)
117{
118    int type, class, ttl, size;
119    int status;
120    char host[MAXDNAME];
121    const unsigned char *p = *pp;
122    status = dn_expand(data, end_data, p, host, sizeof(host));
123    if(status < 0)
124	return -1;
125    if (p + status + 10 > end_data)
126	return -1;
127    p += status;
128    type = (p[0] << 8) | p[1];
129    p += 2;
130    class = (p[0] << 8) | p[1];
131    p += 2;
132    ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
133    p += 4;
134    size = (p[0] << 8) | p[1];
135    p += 2;
136
137    if (p + size > end_data)
138	return -1;
139
140    *rr = calloc(1, sizeof(**rr));
141    if(*rr == NULL)
142	return -1;
143    (*rr)->domain = strdup(host);
144    if((*rr)->domain == NULL) {
145	free(*rr);
146	return -1;
147    }
148    (*rr)->type = type;
149    (*rr)->class = class;
150    (*rr)->ttl = ttl;
151    (*rr)->size = size;
152    switch(type){
153    case T_NS:
154    case T_CNAME:
155    case T_PTR:
156	status = dn_expand(data, end_data, p, host, sizeof(host));
157	if(status < 0) {
158	    free(*rr);
159	    return -1;
160	}
161	(*rr)->u.txt = strdup(host);
162	if((*rr)->u.txt == NULL) {
163	    free(*rr);
164	    return -1;
165	}
166	break;
167    case T_MX:
168    case T_AFSDB:{
169	status = dn_expand(data, end_data, p + 2, host, sizeof(host));
170	if(status < 0){
171	    free(*rr);
172	    return -1;
173	}
174	if (status + 2 > size) {
175	    free(*rr);
176	    return -1;
177	}
178
179	(*rr)->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
180						strlen(host));
181	if((*rr)->u.mx == NULL) {
182	    free(*rr);
183	    return -1;
184	}
185	(*rr)->u.mx->preference = (p[0] << 8) | p[1];
186	strcpy((*rr)->u.mx->domain, host);
187	break;
188    }
189    case T_SRV:{
190	status = dn_expand(data, end_data, p + 6, host, sizeof(host));
191	if(status < 0){
192	    free(*rr);
193	    return -1;
194	}
195	if (status + 6 > size) {
196	    free(*rr);
197	    return -1;
198	}
199
200	(*rr)->u.srv =
201	    (struct srv_record*)malloc(sizeof(struct srv_record) +
202				       strlen(host));
203	if((*rr)->u.srv == NULL) {
204	    free(*rr);
205	    return -1;
206	}
207	(*rr)->u.srv->priority = (p[0] << 8) | p[1];
208	(*rr)->u.srv->weight = (p[2] << 8) | p[3];
209	(*rr)->u.srv->port = (p[4] << 8) | p[5];
210	strcpy((*rr)->u.srv->target, host);
211	break;
212    }
213    case T_TXT:{
214	if(size == 0 || size < *p + 1) {
215	    free(*rr);
216	    return -1;
217	}
218	(*rr)->u.txt = (char*)malloc(*p + 1);
219	if((*rr)->u.txt == NULL) {
220	    free(*rr);
221	    return -1;
222	}
223	strncpy((*rr)->u.txt, (char*)p + 1, *p);
224	(*rr)->u.txt[*p] = '\0';
225	break;
226    }
227    case T_KEY : {
228	size_t key_len;
229
230	if (size < 4) {
231	    free(*rr);
232	    return -1;
233	}
234
235	key_len = size - 4;
236	(*rr)->u.key = malloc (sizeof(*(*rr)->u.key) + key_len - 1);
237	if ((*rr)->u.key == NULL) {
238	    free(*rr);
239	    return -1;
240	}
241
242	(*rr)->u.key->flags     = (p[0] << 8) | p[1];
243	(*rr)->u.key->protocol  = p[2];
244	(*rr)->u.key->algorithm = p[3];
245	(*rr)->u.key->key_len   = key_len;
246	memcpy ((*rr)->u.key->key_data, p + 4, key_len);
247	break;
248    }
249    case T_SIG : {
250	size_t sig_len;
251
252	if(size <= 18) {
253	    free(*rr);
254	    return -1;
255	}
256	status = dn_expand (data, end_data, p + 18, host, sizeof(host));
257	if (status < 0) {
258	    free(*rr);
259	    return -1;
260	}
261	if (status + 18 > size) {
262	    free(*rr);
263	    return -1;
264	}
265
266	/* the signer name is placed after the sig_data, to make it
267           easy to free this struture; the size calculation below
268           includes the zero-termination if the structure itself.
269	   don't you just love C?
270	*/
271	sig_len = size - 18 - status;
272	(*rr)->u.sig = malloc(sizeof(*(*rr)->u.sig)
273			      + strlen(host) + sig_len);
274	if ((*rr)->u.sig == NULL) {
275	    free(*rr);
276	    return -1;
277	}
278	(*rr)->u.sig->type           = (p[0] << 8) | p[1];
279	(*rr)->u.sig->algorithm      = p[2];
280	(*rr)->u.sig->labels         = p[3];
281	(*rr)->u.sig->orig_ttl       = (p[4] << 24) | (p[5] << 16)
282	    | (p[6] << 8) | p[7];
283	(*rr)->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16)
284	    | (p[10] << 8) | p[11];
285	(*rr)->u.sig->sig_inception  = (p[12] << 24) | (p[13] << 16)
286	    | (p[14] << 8) | p[15];
287	(*rr)->u.sig->key_tag        = (p[16] << 8) | p[17];
288	(*rr)->u.sig->sig_len        = sig_len;
289	memcpy ((*rr)->u.sig->sig_data, p + 18 + status, sig_len);
290	(*rr)->u.sig->signer         = &(*rr)->u.sig->sig_data[sig_len];
291	strcpy((*rr)->u.sig->signer, host);
292	break;
293    }
294
295    case T_CERT : {
296	size_t cert_len;
297
298	if (size < 5) {
299	    free(*rr);
300	    return -1;
301	}
302
303	cert_len = size - 5;
304	(*rr)->u.cert = malloc (sizeof(*(*rr)->u.cert) + cert_len - 1);
305	if ((*rr)->u.cert == NULL) {
306	    free(*rr);
307	    return -1;
308	}
309
310	(*rr)->u.cert->type      = (p[0] << 8) | p[1];
311	(*rr)->u.cert->tag       = (p[2] << 8) | p[3];
312	(*rr)->u.cert->algorithm = p[4];
313	(*rr)->u.cert->cert_len  = cert_len;
314	memcpy ((*rr)->u.cert->cert_data, p + 5, cert_len);
315	break;
316    }
317    default:
318	(*rr)->u.data = (unsigned char*)malloc(size);
319	if(size != 0 && (*rr)->u.data == NULL) {
320	    free(*rr);
321	    return -1;
322	}
323	memcpy((*rr)->u.data, p, size);
324    }
325    *pp = p + size;
326    return 0;
327}
328
329#ifndef TEST_RESOLVE
330static
331#endif
332struct dns_reply*
333parse_reply(const unsigned char *data, size_t len)
334{
335    const unsigned char *p;
336    int status;
337    int i;
338    char host[MAXDNAME];
339    const unsigned char *end_data = data + len;
340    struct dns_reply *r;
341    struct resource_record **rr;
342
343    r = calloc(1, sizeof(*r));
344    if (r == NULL)
345	return NULL;
346
347    p = data;
348#if 0
349    /* doesn't work on Crays */
350    memcpy(&r->h, p, sizeof(HEADER));
351    p += sizeof(HEADER);
352#else
353    memcpy(&r->h, p, 12); /* XXX this will probably be mostly garbage */
354    p += 12;
355#endif
356    if(ntohs(r->h.qdcount) != 1) {
357	free(r);
358	return NULL;
359    }
360    status = dn_expand(data, end_data, p, host, sizeof(host));
361    if(status < 0){
362	dns_free_data(r);
363	return NULL;
364    }
365    r->q.domain = strdup(host);
366    if(r->q.domain == NULL) {
367	dns_free_data(r);
368	return NULL;
369    }
370    if (p + status + 4 > end_data) {
371	dns_free_data(r);
372	return NULL;
373    }
374    p += status;
375    r->q.type = (p[0] << 8 | p[1]);
376    p += 2;
377    r->q.class = (p[0] << 8 | p[1]);
378    p += 2;
379
380    rr = &r->head;
381    for(i = 0; i < ntohs(r->h.ancount); i++) {
382	if(parse_record(data, end_data, &p, rr) != 0) {
383	    dns_free_data(r);
384	    return NULL;
385	}
386	rr = &(*rr)->next;
387    }
388    for(i = 0; i < ntohs(r->h.nscount); i++) {
389	if(parse_record(data, end_data, &p, rr) != 0) {
390	    dns_free_data(r);
391	    return NULL;
392	}
393	rr = &(*rr)->next;
394    }
395    for(i = 0; i < ntohs(r->h.arcount); i++) {
396	if(parse_record(data, end_data, &p, rr) != 0) {
397	    dns_free_data(r);
398	    return NULL;
399	}
400	rr = &(*rr)->next;
401    }
402    *rr = NULL;
403    return r;
404}
405
406static struct dns_reply *
407dns_lookup_int(const char *domain, int rr_class, int rr_type)
408{
409    unsigned char reply[1024];
410    int len;
411#ifdef HAVE_RES_NSEARCH
412    struct __res_state stat;
413    memset(&stat, 0, sizeof(stat));
414    if(res_ninit(&stat))
415	return NULL; /* is this the best we can do? */
416#elif defined(HAVE__RES)
417    u_long old_options = 0;
418#endif
419
420    if (_resolve_debug) {
421#ifdef HAVE_RES_NSEARCH
422	stat.options |= RES_DEBUG;
423#elif defined(HAVE__RES)
424        old_options = _res.options;
425	_res.options |= RES_DEBUG;
426#endif
427	fprintf(stderr, "dns_lookup(%s, %d, %s)\n", domain,
428		rr_class, dns_type_to_string(rr_type));
429    }
430#ifdef HAVE_RES_NSEARCH
431    len = res_nsearch(&stat, domain, rr_class, rr_type, reply, sizeof(reply));
432#else
433    len = res_search(domain, rr_class, rr_type, reply, sizeof(reply));
434#endif
435    if (_resolve_debug) {
436#if defined(HAVE__RES) && !defined(HAVE_RES_NSEARCH)
437        _res.options = old_options;
438#endif
439	fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n",
440		domain, rr_class, dns_type_to_string(rr_type), len);
441    }
442#ifdef HAVE_RES_NSEARCH
443    res_nclose(&stat);
444#endif
445    if(len < 0) {
446	return NULL;
447    } else {
448	len = min(len, sizeof(reply));
449	return parse_reply(reply, len);
450    }
451}
452
453struct dns_reply *
454dns_lookup(const char *domain, const char *type_name)
455{
456    int type;
457
458    type = dns_string_to_type(type_name);
459    if(type == -1) {
460	if(_resolve_debug)
461	    fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
462		    type_name);
463	return NULL;
464    }
465    return dns_lookup_int(domain, C_IN, type);
466}
467
468static int
469compare_srv(const void *a, const void *b)
470{
471    const struct resource_record *const* aa = a, *const* bb = b;
472
473    if((*aa)->u.srv->priority == (*bb)->u.srv->priority)
474	return ((*aa)->u.srv->weight - (*bb)->u.srv->weight);
475    return ((*aa)->u.srv->priority - (*bb)->u.srv->priority);
476}
477
478#ifndef HAVE_RANDOM
479#define random() rand()
480#endif
481
482/* try to rearrange the srv-records by the algorithm in RFC2782 */
483void
484dns_srv_order(struct dns_reply *r)
485{
486    struct resource_record **srvs, **ss, **headp;
487    struct resource_record *rr;
488    int num_srv = 0;
489
490#if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
491    int state[256 / sizeof(int)];
492    char *oldstate;
493#endif
494
495    for(rr = r->head; rr; rr = rr->next)
496	if(rr->type == T_SRV)
497	    num_srv++;
498
499    if(num_srv == 0)
500	return;
501
502    srvs = malloc(num_srv * sizeof(*srvs));
503    if(srvs == NULL)
504	return; /* XXX not much to do here */
505
506    /* unlink all srv-records from the linked list and put them in
507       a vector */
508    for(ss = srvs, headp = &r->head; *headp; )
509	if((*headp)->type == T_SRV) {
510	    *ss = *headp;
511	    *headp = (*headp)->next;
512	    (*ss)->next = NULL;
513	    ss++;
514	} else
515	    headp = &(*headp)->next;
516
517    /* sort them by priority and weight */
518    qsort(srvs, num_srv, sizeof(*srvs), compare_srv);
519
520#if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
521    oldstate = initstate(time(NULL), (char*)state, sizeof(state));
522#endif
523
524    headp = &r->head;
525
526    for(ss = srvs; ss < srvs + num_srv; ) {
527	int sum, rnd, count;
528	struct resource_record **ee, **tt;
529	/* find the last record with the same priority and count the
530           sum of all weights */
531	for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) {
532	    if(*tt == NULL)
533		continue;
534	    if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
535		break;
536	    sum += (*tt)->u.srv->weight;
537	}
538	ee = tt;
539	/* ss is now the first record of this priority and ee is the
540           first of the next */
541	while(ss < ee) {
542	    rnd = random() % (sum + 1);
543	    for(count = 0, tt = ss; ; tt++) {
544		if(*tt == NULL)
545		    continue;
546		count += (*tt)->u.srv->weight;
547		if(count >= rnd)
548		    break;
549	    }
550
551	    assert(tt < ee);
552
553	    /* insert the selected record at the tail (of the head) of
554               the list */
555	    (*tt)->next = *headp;
556	    *headp = *tt;
557	    headp = &(*tt)->next;
558	    sum -= (*tt)->u.srv->weight;
559	    *tt = NULL;
560	    while(ss < ee && *ss == NULL)
561		ss++;
562	}
563    }
564
565#if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
566    setstate(oldstate);
567#endif
568    free(srvs);
569    return;
570}
571
572#else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
573
574struct dns_reply *
575dns_lookup(const char *domain, const char *type_name)
576{
577    return NULL;
578}
579
580void
581dns_free_data(struct dns_reply *r)
582{
583}
584
585void
586dns_srv_order(struct dns_reply *r)
587{
588}
589
590#endif
591
592#ifdef TEST
593int
594main(int argc, char **argv)
595{
596    struct dns_reply *r;
597    struct resource_record *rr;
598    r = dns_lookup(argv[1], argv[2]);
599    if(r == NULL){
600	printf("No reply.\n");
601	return 1;
602    }
603    if(r->q.type == T_SRV)
604	dns_srv_order(r);
605
606    for(rr = r->head; rr;rr=rr->next){
607	printf("%-30s %-5s %-6d ", rr->domain, dns_type_to_string(rr->type), rr->ttl);
608	switch(rr->type){
609	case T_NS:
610	case T_CNAME:
611	case T_PTR:
612	    printf("%s\n", (char*)rr->u.data);
613	    break;
614	case T_A:
615	    printf("%s\n", inet_ntoa(*rr->u.a));
616	    break;
617	case T_MX:
618	case T_AFSDB:{
619	    printf("%d %s\n", rr->u.mx->preference, rr->u.mx->domain);
620	    break;
621	}
622	case T_SRV:{
623	    struct srv_record *srv = rr->u.srv;
624	    printf("%d %d %d %s\n", srv->priority, srv->weight,
625		   srv->port, srv->target);
626	    break;
627	}
628	case T_TXT: {
629	    printf("%s\n", rr->u.txt);
630	    break;
631	}
632	case T_SIG : {
633	    struct sig_record *sig = rr->u.sig;
634	    const char *type_string = dns_type_to_string (sig->type);
635
636	    printf ("type %u (%s), algorithm %u, labels %u, orig_ttl %u, sig_expiration %u, sig_inception %u, key_tag %u, signer %s\n",
637		    sig->type, type_string ? type_string : "",
638		    sig->algorithm, sig->labels, sig->orig_ttl,
639		    sig->sig_expiration, sig->sig_inception, sig->key_tag,
640		    sig->signer);
641	    break;
642	}
643	case T_KEY : {
644	    struct key_record *key = rr->u.key;
645
646	    printf ("flags %u, protocol %u, algorithm %u\n",
647		    key->flags, key->protocol, key->algorithm);
648	    break;
649	}
650	default:
651	    printf("\n");
652	    break;
653	}
654    }
655
656    return 0;
657}
658#endif
659