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