turn off v4 conversion stuff
[heimdal.git] / lib / roken / resolve.c
blob419c8d94e0cdb4a58a726174505f57608e12d2d9
1 /*
2 * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
35 #include <config.h>
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"
49 #include <assert.h>
51 #ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
52 #undef HAVE_RES_NSEARCH
53 #endif
55 #define DECL(X) {#X, rk_ns_t_##X}
57 static 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}
79 int _resolve_debug = 0;
81 int ROKEN_LIB_FUNCTION
82 rk_dns_string_to_type(const char *name)
84 struct stot *p = stot;
85 for(p = stot; p->name; p++)
86 if(strcasecmp(name, p->name) == 0)
87 return p->type;
88 return -1;
91 const char * ROKEN_LIB_FUNCTION
92 rk_dns_type_to_string(int type)
94 struct stot *p = stot;
95 for(p = stot; p->name; p++)
96 if(type == p->type)
97 return p->name;
98 return NULL;
101 #if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)
103 static void
104 dns_free_rr(struct rk_resource_record *rr)
106 if(rr->domain)
107 free(rr->domain);
108 if(rr->u.data)
109 free(rr->u.data);
110 free(rr);
113 void ROKEN_LIB_FUNCTION
114 rk_dns_free_data(struct rk_dns_reply *r)
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);
124 free (r);
127 static int
128 parse_record(const unsigned char *data, const unsigned char *end_data,
129 const unsigned char **pp, struct rk_resource_record **ret_rr)
131 struct rk_resource_record *rr;
132 int type, class, ttl;
133 unsigned size;
134 int status;
135 char host[MAXDNAME];
136 const unsigned char *p = *pp;
138 *ret_rr = NULL;
140 status = dn_expand(data, end_data, p, host, sizeof(host));
141 if(status < 0)
142 return -1;
143 if (p + status + 10 > end_data)
144 return -1;
146 p += status;
147 type = (p[0] << 8) | p[1];
148 p += 2;
149 class = (p[0] << 8) | p[1];
150 p += 2;
151 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
152 p += 4;
153 size = (p[0] << 8) | p[1];
154 p += 2;
156 if (p + size > end_data)
157 return -1;
159 rr = calloc(1, sizeof(*rr));
160 if(rr == NULL)
161 return -1;
162 rr->domain = strdup(host);
163 if(rr->domain == NULL) {
164 dns_free_rr(rr);
165 return -1;
167 rr->type = type;
168 rr->class = class;
169 rr->ttl = ttl;
170 rr->size = size;
171 switch(type){
172 case rk_ns_t_ns:
173 case rk_ns_t_cname:
174 case rk_ns_t_ptr:
175 status = dn_expand(data, end_data, p, host, sizeof(host));
176 if(status < 0) {
177 dns_free_rr(rr);
178 return -1;
180 rr->u.txt = strdup(host);
181 if(rr->u.txt == NULL) {
182 dns_free_rr(rr);
183 return -1;
185 break;
186 case rk_ns_t_mx:
187 case rk_ns_t_afsdb:{
188 size_t hostlen;
190 status = dn_expand(data, end_data, p + 2, host, sizeof(host));
191 if(status < 0){
192 dns_free_rr(rr);
193 return -1;
195 if (status + 2 > size) {
196 dns_free_rr(rr);
197 return -1;
200 hostlen = strlen(host);
201 rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
202 hostlen);
203 if(rr->u.mx == NULL) {
204 dns_free_rr(rr);
205 return -1;
207 rr->u.mx->preference = (p[0] << 8) | p[1];
208 strlcpy(rr->u.mx->domain, host, hostlen + 1);
209 break;
211 case rk_ns_t_srv:{
212 size_t hostlen;
213 status = dn_expand(data, end_data, p + 6, host, sizeof(host));
214 if(status < 0){
215 dns_free_rr(rr);
216 return -1;
218 if (status + 6 > size) {
219 dns_free_rr(rr);
220 return -1;
223 hostlen = strlen(host);
224 rr->u.srv =
225 (struct srv_record*)malloc(sizeof(struct srv_record) +
226 hostlen);
227 if(rr->u.srv == NULL) {
228 dns_free_rr(rr);
229 return -1;
231 rr->u.srv->priority = (p[0] << 8) | p[1];
232 rr->u.srv->weight = (p[2] << 8) | p[3];
233 rr->u.srv->port = (p[4] << 8) | p[5];
234 strlcpy(rr->u.srv->target, host, hostlen + 1);
235 break;
237 case rk_ns_t_txt:{
238 if(size == 0 || size < *p + 1) {
239 dns_free_rr(rr);
240 return -1;
242 rr->u.txt = (char*)malloc(*p + 1);
243 if(rr->u.txt == NULL) {
244 dns_free_rr(rr);
245 return -1;
247 strncpy(rr->u.txt, (const char*)(p + 1), *p);
248 rr->u.txt[*p] = '\0';
249 break;
251 case rk_ns_t_key : {
252 size_t key_len;
254 if (size < 4) {
255 dns_free_rr(rr);
256 return -1;
259 key_len = size - 4;
260 rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
261 if (rr->u.key == NULL) {
262 dns_free_rr(rr);
263 return -1;
266 rr->u.key->flags = (p[0] << 8) | p[1];
267 rr->u.key->protocol = p[2];
268 rr->u.key->algorithm = p[3];
269 rr->u.key->key_len = key_len;
270 memcpy (rr->u.key->key_data, p + 4, key_len);
271 break;
273 case rk_ns_t_sig : {
274 size_t sig_len, hostlen;
276 if(size <= 18) {
277 dns_free_rr(rr);
278 return -1;
280 status = dn_expand (data, end_data, p + 18, host, sizeof(host));
281 if (status < 0) {
282 dns_free_rr(rr);
283 return -1;
285 if (status + 18 > size) {
286 dns_free_rr(rr);
287 return -1;
290 /* the signer name is placed after the sig_data, to make it
291 easy to free this structure; the size calculation below
292 includes the zero-termination if the structure itself.
293 don't you just love C?
295 sig_len = size - 18 - status;
296 hostlen = strlen(host);
297 rr->u.sig = malloc(sizeof(*rr->u.sig)
298 + hostlen + sig_len);
299 if (rr->u.sig == NULL) {
300 dns_free_rr(rr);
301 return -1;
303 rr->u.sig->type = (p[0] << 8) | p[1];
304 rr->u.sig->algorithm = p[2];
305 rr->u.sig->labels = p[3];
306 rr->u.sig->orig_ttl = (p[4] << 24) | (p[5] << 16)
307 | (p[6] << 8) | p[7];
308 rr->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16)
309 | (p[10] << 8) | p[11];
310 rr->u.sig->sig_inception = (p[12] << 24) | (p[13] << 16)
311 | (p[14] << 8) | p[15];
312 rr->u.sig->key_tag = (p[16] << 8) | p[17];
313 rr->u.sig->sig_len = sig_len;
314 memcpy (rr->u.sig->sig_data, p + 18 + status, sig_len);
315 rr->u.sig->signer = &rr->u.sig->sig_data[sig_len];
316 strlcpy(rr->u.sig->signer, host, hostlen + 1);
317 break;
320 case rk_ns_t_cert : {
321 size_t cert_len;
323 if (size < 5) {
324 dns_free_rr(rr);
325 return -1;
328 cert_len = size - 5;
329 rr->u.cert = malloc (sizeof(*rr->u.cert) + cert_len - 1);
330 if (rr->u.cert == NULL) {
331 dns_free_rr(rr);
332 return -1;
335 rr->u.cert->type = (p[0] << 8) | p[1];
336 rr->u.cert->tag = (p[2] << 8) | p[3];
337 rr->u.cert->algorithm = p[4];
338 rr->u.cert->cert_len = cert_len;
339 memcpy (rr->u.cert->cert_data, p + 5, cert_len);
340 break;
342 case rk_ns_t_sshfp : {
343 size_t sshfp_len;
345 if (size < 2) {
346 dns_free_rr(rr);
347 return -1;
350 sshfp_len = size - 2;
352 rr->u.sshfp = malloc (sizeof(*rr->u.sshfp) + sshfp_len - 1);
353 if (rr->u.sshfp == NULL) {
354 dns_free_rr(rr);
355 return -1;
358 rr->u.sshfp->algorithm = p[0];
359 rr->u.sshfp->type = p[1];
360 rr->u.sshfp->sshfp_len = sshfp_len;
361 memcpy (rr->u.sshfp->sshfp_data, p + 2, sshfp_len);
362 break;
364 case rk_ns_t_ds: {
365 size_t digest_len;
367 if (size < 4) {
368 dns_free_rr(rr);
369 return -1;
372 digest_len = size - 4;
374 rr->u.ds = malloc (sizeof(*rr->u.ds) + digest_len - 1);
375 if (rr->u.ds == NULL) {
376 dns_free_rr(rr);
377 return -1;
380 rr->u.ds->key_tag = (p[0] << 8) | p[1];
381 rr->u.ds->algorithm = p[2];
382 rr->u.ds->digest_type = p[3];
383 rr->u.ds->digest_len = digest_len;
384 memcpy (rr->u.ds->digest_data, p + 4, digest_len);
385 break;
387 default:
388 rr->u.data = (unsigned char*)malloc(size);
389 if(size != 0 && rr->u.data == NULL) {
390 dns_free_rr(rr);
391 return -1;
393 if (size)
394 memcpy(rr->u.data, p, size);
396 *pp = p + size;
397 *ret_rr = rr;
399 return 0;
402 #ifndef TEST_RESOLVE
403 static
404 #endif
405 struct rk_dns_reply*
406 parse_reply(const unsigned char *data, size_t len)
408 const unsigned char *p;
409 int status;
410 int i;
411 char host[MAXDNAME];
412 const unsigned char *end_data = data + len;
413 struct rk_dns_reply *r;
414 struct rk_resource_record **rr;
416 r = calloc(1, sizeof(*r));
417 if (r == NULL)
418 return NULL;
420 p = data;
422 r->h.id = (p[0] << 8) | p[1];
423 r->h.flags = 0;
424 if (p[2] & 0x01)
425 r->h.flags |= rk_DNS_HEADER_RESPONSE_FLAG;
426 r->h.opcode = (p[2] >> 1) & 0xf;
427 if (p[2] & 0x20)
428 r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
429 if (p[2] & 0x40)
430 r->h.flags |= rk_DNS_HEADER_TRUNCATED_MESSAGE;
431 if (p[2] & 0x80)
432 r->h.flags |= rk_DNS_HEADER_RECURSION_DESIRED;
433 if (p[3] & 0x01)
434 r->h.flags |= rk_DNS_HEADER_RECURSION_AVAILABLE;
435 if (p[3] & 0x04)
436 r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
437 if (p[3] & 0x08)
438 r->h.flags |= rk_DNS_HEADER_CHECKING_DISABLED;
439 r->h.response_code = (p[3] >> 4) & 0xf;
440 r->h.qdcount = (p[4] << 8) | p[5];
441 r->h.ancount = (p[6] << 8) | p[7];
442 r->h.nscount = (p[8] << 8) | p[9];
443 r->h.arcount = (p[10] << 8) | p[11];
445 p += 12;
447 if(r->h.qdcount != 1) {
448 free(r);
449 return NULL;
451 status = dn_expand(data, end_data, p, host, sizeof(host));
452 if(status < 0){
453 rk_dns_free_data(r);
454 return NULL;
456 r->q.domain = strdup(host);
457 if(r->q.domain == NULL) {
458 rk_dns_free_data(r);
459 return NULL;
461 if (p + status + 4 > end_data) {
462 rk_dns_free_data(r);
463 return NULL;
465 p += status;
466 r->q.type = (p[0] << 8 | p[1]);
467 p += 2;
468 r->q.class = (p[0] << 8 | p[1]);
469 p += 2;
471 rr = &r->head;
472 for(i = 0; i < r->h.ancount; i++) {
473 if(parse_record(data, end_data, &p, rr) != 0) {
474 rk_dns_free_data(r);
475 return NULL;
477 rr = &(*rr)->next;
479 for(i = 0; i < r->h.nscount; i++) {
480 if(parse_record(data, end_data, &p, rr) != 0) {
481 rk_dns_free_data(r);
482 return NULL;
484 rr = &(*rr)->next;
486 for(i = 0; i < r->h.arcount; i++) {
487 if(parse_record(data, end_data, &p, rr) != 0) {
488 rk_dns_free_data(r);
489 return NULL;
491 rr = &(*rr)->next;
493 *rr = NULL;
494 return r;
497 #ifdef HAVE_RES_NSEARCH
498 #ifdef HAVE_RES_NDESTROY
499 #define rk_res_free(x) res_ndestroy(x)
500 #else
501 #define rk_res_free(x) res_nclose(x)
502 #endif
503 #endif
505 #if defined(HAVE_DNS_SEARCH)
506 #define resolve_search(h,n,c,t,r,l) \
507 ((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize))
508 #define resolve_free_handle(h) dns_free(h)
509 #elif defined(HAVE_RES_NSEARCH)
510 #define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l)
511 #define resolve_free_handle(h) rk_res_free(h);
512 #else
513 #define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l)
514 #define handle 0
515 #define resolve_free_handle(h)
516 #endif
519 static struct rk_dns_reply *
520 dns_lookup_int(const char *domain, int rr_class, int rr_type)
522 struct rk_dns_reply *r;
523 void *reply = NULL;
524 int size;
525 int len;
526 #if defined(HAVE_DNS_SEARCH)
527 struct sockaddr_storage from;
528 uint32_t fromsize = sizeof(from);
529 dns_handle_t handle;
531 handle = dns_open(NULL);
532 if (handle == NULL)
533 return NULL;
534 #elif defined(HAVE_RES_NSEARCH)
535 struct __res_state state;
536 struct __res_state *handle = &state;
538 memset(&state, 0, sizeof(state));
539 if(res_ninit(handle))
540 return NULL; /* is this the best we can do? */
541 #endif
543 size = 0;
544 len = 1000;
545 do {
546 if (reply) {
547 free(reply);
548 reply = NULL;
550 if (size <= len)
551 size = len;
552 if (_resolve_debug) {
553 #if defined(HAVE_DNS_SEARCH)
554 dns_set_debug(handle, 1);
555 #elif defined(HAVE_RES_NSEARCH)
556 state.options |= RES_DEBUG;
557 #endif
558 fprintf(stderr, "dns_lookup(%s, %d, %s), buffer size %d\n", domain,
559 rr_class, rk_dns_type_to_string(rr_type), size);
561 reply = malloc(size);
562 if (reply == NULL) {
563 resolve_free_handle(handle);
564 return NULL;
567 len = resolve_search(handle, domain, rr_class, rr_type, reply, size);
569 if (_resolve_debug) {
570 fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n",
571 domain, rr_class, rk_dns_type_to_string(rr_type), len);
573 if (len <= 0) {
574 resolve_free_handle(handle);
575 free(reply);
576 return NULL;
578 } while (size < len && len < rk_DNS_MAX_PACKET_SIZE);
579 resolve_free_handle(handle);
581 len = min(len, size);
582 r = parse_reply(reply, len);
583 free(reply);
584 return r;
587 struct rk_dns_reply * ROKEN_LIB_FUNCTION
588 rk_dns_lookup(const char *domain, const char *type_name)
590 int type;
592 type = rk_dns_string_to_type(type_name);
593 if(type == -1) {
594 if(_resolve_debug)
595 fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
596 type_name);
597 return NULL;
599 return dns_lookup_int(domain, rk_ns_c_in, type);
602 static int
603 compare_srv(const void *a, const void *b)
605 const struct rk_resource_record *const* aa = a, *const* bb = b;
607 if((*aa)->u.srv->priority == (*bb)->u.srv->priority)
608 return ((*aa)->u.srv->weight - (*bb)->u.srv->weight);
609 return ((*aa)->u.srv->priority - (*bb)->u.srv->priority);
612 #ifndef HAVE_RANDOM
613 #define random() rand()
614 #endif
616 /* try to rearrange the srv-records by the algorithm in RFC2782 */
617 void ROKEN_LIB_FUNCTION
618 rk_dns_srv_order(struct rk_dns_reply *r)
620 struct rk_resource_record **srvs, **ss, **headp;
621 struct rk_resource_record *rr;
622 int num_srv = 0;
624 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
625 int state[256 / sizeof(int)];
626 char *oldstate;
627 #endif
629 for(rr = r->head; rr; rr = rr->next)
630 if(rr->type == rk_ns_t_srv)
631 num_srv++;
633 if(num_srv == 0)
634 return;
636 srvs = malloc(num_srv * sizeof(*srvs));
637 if(srvs == NULL)
638 return; /* XXX not much to do here */
640 /* unlink all srv-records from the linked list and put them in
641 a vector */
642 for(ss = srvs, headp = &r->head; *headp; )
643 if((*headp)->type == rk_ns_t_srv) {
644 *ss = *headp;
645 *headp = (*headp)->next;
646 (*ss)->next = NULL;
647 ss++;
648 } else
649 headp = &(*headp)->next;
651 /* sort them by priority and weight */
652 qsort(srvs, num_srv, sizeof(*srvs), compare_srv);
654 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
655 oldstate = initstate(time(NULL), (char*)state, sizeof(state));
656 #endif
658 headp = &r->head;
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;
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 = 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;
684 assert(tt < ee);
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++;
698 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
699 setstate(oldstate);
700 #endif
701 free(srvs);
702 return;
705 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
707 struct rk_dns_reply * ROKEN_LIB_FUNCTION
708 rk_dns_lookup(const char *domain, const char *type_name)
710 return NULL;
713 void ROKEN_LIB_FUNCTION
714 rk_dns_free_data(struct rk_dns_reply *r)
718 void ROKEN_LIB_FUNCTION
719 rk_dns_srv_order(struct rk_dns_reply *r)
723 #endif