Merge Samba3 and Samba4 together
[amitay/samba.git] / source4 / heimdal / lib / roken / resolve.c
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 #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$");
49
50 #ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
51 #undef HAVE_RES_NSEARCH
52 #endif
53
54 #define DECL(X) {#X, rk_ns_t_##X}
55
56 static struct stot{
57     const char *name;
58     int type;
59 }stot[] = {
60     DECL(a),
61     DECL(aaaa),
62     DECL(ns),
63     DECL(cname),
64     DECL(soa),
65     DECL(ptr),
66     DECL(mx),
67     DECL(txt),
68     DECL(afsdb),
69     DECL(sig),
70     DECL(key),
71     DECL(srv),
72     DECL(naptr),
73     DECL(sshfp),
74     DECL(ds),
75     {NULL,      0}
76 };
77
78 int _resolve_debug = 0;
79
80 int ROKEN_LIB_FUNCTION
81 dns_string_to_type(const char *name)
82 {
83     struct stot *p = stot;
84     for(p = stot; p->name; p++)
85         if(strcasecmp(name, p->name) == 0)
86             return p->type;
87     return -1;
88 }
89
90 const char * ROKEN_LIB_FUNCTION
91 dns_type_to_string(int type)
92 {
93     struct stot *p = stot;
94     for(p = stot; p->name; p++)
95         if(type == p->type)
96             return p->name;
97     return NULL;
98 }
99
100 #if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)
101
102 static void
103 dns_free_rr(struct resource_record *rr)
104 {
105     if(rr->domain)
106         free(rr->domain);
107     if(rr->u.data)
108         free(rr->u.data);
109     free(rr);
110 }
111
112 void ROKEN_LIB_FUNCTION
113 dns_free_data(struct dns_reply *r)
114 {
115     struct resource_record *rr;
116     if(r->q.domain)
117         free(r->q.domain);
118     for(rr = r->head; rr;){
119         struct resource_record *tmp = rr;
120         rr = rr->next;
121         dns_free_rr(tmp);
122     }
123     free (r);
124 }
125
126 static int
127 parse_record(const unsigned char *data, const unsigned char *end_data, 
128              const unsigned char **pp, struct resource_record **ret_rr)
129 {
130     struct resource_record *rr;
131     int type, class, ttl;
132     unsigned size;
133     int status;
134     char host[MAXDNAME];
135     const unsigned char *p = *pp;
136
137     *ret_rr = NULL;
138
139     status = dn_expand(data, end_data, p, host, sizeof(host));
140     if(status < 0) 
141         return -1;
142     if (p + status + 10 > end_data)
143         return -1;
144
145     p += status;
146     type = (p[0] << 8) | p[1];
147     p += 2;
148     class = (p[0] << 8) | p[1];
149     p += 2;
150     ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
151     p += 4;
152     size = (p[0] << 8) | p[1];
153     p += 2;
154
155     if (p + size > end_data)
156         return -1;
157
158     rr = calloc(1, sizeof(*rr));
159     if(rr == NULL) 
160         return -1;
161     rr->domain = strdup(host);
162     if(rr->domain == NULL) {
163         dns_free_rr(rr);
164         return -1;
165     }
166     rr->type = type;
167     rr->class = class;
168     rr->ttl = ttl;
169     rr->size = size;
170     switch(type){
171     case rk_ns_t_ns:
172     case rk_ns_t_cname:
173     case rk_ns_t_ptr:
174         status = dn_expand(data, end_data, p, host, sizeof(host));
175         if(status < 0) {
176             dns_free_rr(rr);
177             return -1;
178         }
179         rr->u.txt = strdup(host);
180         if(rr->u.txt == NULL) {
181             dns_free_rr(rr);
182             return -1;
183         }
184         break;
185     case rk_ns_t_mx:
186     case rk_ns_t_afsdb:{
187         size_t hostlen;
188
189         status = dn_expand(data, end_data, p + 2, host, sizeof(host));
190         if(status < 0){
191             dns_free_rr(rr);
192             return -1;
193         }
194         if (status + 2 > size) {
195             dns_free_rr(rr);
196             return -1;
197         }
198
199         hostlen = strlen(host);
200         rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) + 
201                                                 hostlen);
202         if(rr->u.mx == NULL) {
203             dns_free_rr(rr);
204             return -1;
205         }
206         rr->u.mx->preference = (p[0] << 8) | p[1];
207         strlcpy(rr->u.mx->domain, host, hostlen + 1);
208         break;
209     }
210     case rk_ns_t_srv:{
211         size_t hostlen;
212         status = dn_expand(data, end_data, p + 6, host, sizeof(host));
213         if(status < 0){
214             dns_free_rr(rr);
215             return -1;
216         }
217         if (status + 6 > size) {
218             dns_free_rr(rr);
219             return -1;
220         }
221
222         hostlen = strlen(host);
223         rr->u.srv = 
224             (struct srv_record*)malloc(sizeof(struct srv_record) + 
225                                        hostlen);
226         if(rr->u.srv == NULL) {
227             dns_free_rr(rr);
228             return -1;
229         }
230         rr->u.srv->priority = (p[0] << 8) | p[1];
231         rr->u.srv->weight = (p[2] << 8) | p[3];
232         rr->u.srv->port = (p[4] << 8) | p[5];
233         strlcpy(rr->u.srv->target, host, hostlen + 1);
234         break;
235     }
236     case rk_ns_t_txt:{
237         if(size == 0 || size < *p + 1) {
238             dns_free_rr(rr);
239             return -1;
240         }
241         rr->u.txt = (char*)malloc(*p + 1);
242         if(rr->u.txt == NULL) {
243             dns_free_rr(rr);
244             return -1;
245         }
246         strncpy(rr->u.txt, (const char*)(p + 1), *p);
247         rr->u.txt[*p] = '\0';
248         break;
249     }
250     case rk_ns_t_key : {
251         size_t key_len;
252
253         if (size < 4) {
254             dns_free_rr(rr);
255             return -1;
256         }
257
258         key_len = size - 4;
259         rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
260         if (rr->u.key == NULL) {
261             dns_free_rr(rr);
262             return -1;
263         }
264
265         rr->u.key->flags     = (p[0] << 8) | p[1];
266         rr->u.key->protocol  = p[2];
267         rr->u.key->algorithm = p[3];
268         rr->u.key->key_len   = key_len;
269         memcpy (rr->u.key->key_data, p + 4, key_len);
270         break;
271     }
272     case rk_ns_t_sig : {
273         size_t sig_len, hostlen;
274
275         if(size <= 18) {
276             dns_free_rr(rr);
277             return -1;
278         }
279         status = dn_expand (data, end_data, p + 18, host, sizeof(host));
280         if (status < 0) {
281             dns_free_rr(rr);
282             return -1;
283         }
284         if (status + 18 > size) {
285             dns_free_rr(rr);
286             return -1;
287         }
288
289         /* the signer name is placed after the sig_data, to make it
290            easy to free this structure; the size calculation below
291            includes the zero-termination if the structure itself.
292            don't you just love C?
293         */
294         sig_len = size - 18 - status;
295         hostlen = strlen(host);
296         rr->u.sig = malloc(sizeof(*rr->u.sig)
297                               + hostlen + sig_len);
298         if (rr->u.sig == NULL) {
299             dns_free_rr(rr);
300             return -1;
301         }
302         rr->u.sig->type           = (p[0] << 8) | p[1];
303         rr->u.sig->algorithm      = p[2];
304         rr->u.sig->labels         = p[3];
305         rr->u.sig->orig_ttl       = (p[4] << 24) | (p[5] << 16)
306             | (p[6] << 8) | p[7];
307         rr->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16)
308             | (p[10] << 8) | p[11];
309         rr->u.sig->sig_inception  = (p[12] << 24) | (p[13] << 16)
310             | (p[14] << 8) | p[15];
311         rr->u.sig->key_tag        = (p[16] << 8) | p[17];
312         rr->u.sig->sig_len        = sig_len;
313         memcpy (rr->u.sig->sig_data, p + 18 + status, sig_len);
314         rr->u.sig->signer         = &rr->u.sig->sig_data[sig_len];
315         strlcpy(rr->u.sig->signer, host, hostlen + 1);
316         break;
317     }
318
319     case rk_ns_t_cert : {
320         size_t cert_len;
321
322         if (size < 5) {
323             dns_free_rr(rr);
324             return -1;
325         }
326
327         cert_len = size - 5;
328         rr->u.cert = malloc (sizeof(*rr->u.cert) + cert_len - 1);
329         if (rr->u.cert == NULL) {
330             dns_free_rr(rr);
331             return -1;
332         }
333
334         rr->u.cert->type      = (p[0] << 8) | p[1];
335         rr->u.cert->tag       = (p[2] << 8) | p[3];
336         rr->u.cert->algorithm = p[4];
337         rr->u.cert->cert_len  = cert_len;
338         memcpy (rr->u.cert->cert_data, p + 5, cert_len);
339         break;
340     }
341     case rk_ns_t_sshfp : {
342         size_t sshfp_len;
343
344         if (size < 2) {
345             dns_free_rr(rr);
346             return -1;
347         }
348
349         sshfp_len = size - 2;
350
351         rr->u.sshfp = malloc (sizeof(*rr->u.sshfp) + sshfp_len - 1);
352         if (rr->u.sshfp == NULL) {
353             dns_free_rr(rr);
354             return -1;
355         }
356
357         rr->u.sshfp->algorithm = p[0];
358         rr->u.sshfp->type      = p[1];
359         rr->u.sshfp->sshfp_len  = sshfp_len;
360         memcpy (rr->u.sshfp->sshfp_data, p + 2, sshfp_len);
361         break;
362     }
363     case rk_ns_t_ds: {
364         size_t digest_len;
365
366         if (size < 4) {
367             dns_free_rr(rr);
368             return -1;
369         }
370
371         digest_len = size - 4;
372
373         rr->u.ds = malloc (sizeof(*rr->u.ds) + digest_len - 1);
374         if (rr->u.ds == NULL) {
375             dns_free_rr(rr);
376             return -1;
377         }
378
379         rr->u.ds->key_tag     = (p[0] << 8) | p[1];
380         rr->u.ds->algorithm   = p[2];
381         rr->u.ds->digest_type = p[3];
382         rr->u.ds->digest_len  = digest_len;
383         memcpy (rr->u.ds->digest_data, p + 4, digest_len);
384         break;
385     }
386     default:
387         rr->u.data = (unsigned char*)malloc(size);
388         if(size != 0 && rr->u.data == NULL) {
389             dns_free_rr(rr);
390             return -1;
391         }
392         if (size)
393             memcpy(rr->u.data, p, size);
394     }
395     *pp = p + size;
396     *ret_rr = rr;
397
398     return 0;
399 }
400
401 #ifndef TEST_RESOLVE
402 static
403 #endif
404 struct dns_reply*
405 parse_reply(const unsigned char *data, size_t len)
406 {
407     const unsigned char *p;
408     int status;
409     int i;
410     char host[MAXDNAME];
411     const unsigned char *end_data = data + len;
412     struct dns_reply *r;
413     struct 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         dns_free_data(r);
453         return NULL;
454     }
455     r->q.domain = strdup(host);
456     if(r->q.domain == NULL) {
457         dns_free_data(r);
458         return NULL;
459     }
460     if (p + status + 4 > end_data) {
461         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             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             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             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 static struct dns_reply *
505 dns_lookup_int(const char *domain, int rr_class, int rr_type)
506 {
507     struct dns_reply *r;
508     unsigned char *reply = NULL;
509     int size;
510     int len;
511 #ifdef HAVE_RES_NSEARCH
512     struct __res_state state;
513     memset(&state, 0, sizeof(state));
514     if(res_ninit(&state))
515         return NULL; /* is this the best we can do? */
516 #elif defined(HAVE__RES)
517     u_long old_options = 0;
518 #endif
519     
520     size = 0;
521     len = 1000;
522     do {
523         if (reply) {
524             free(reply);
525             reply = NULL;
526         }
527         if (size <= len)
528             size = len;
529         if (_resolve_debug) {
530 #ifdef HAVE_RES_NSEARCH
531             state.options |= RES_DEBUG;
532 #elif defined(HAVE__RES)
533             old_options = _res.options;
534             _res.options |= RES_DEBUG;
535 #endif
536             fprintf(stderr, "dns_lookup(%s, %d, %s), buffer size %d\n", domain,
537                     rr_class, dns_type_to_string(rr_type), size);
538         }
539         reply = malloc(size);
540         if (reply == NULL) {
541 #ifdef HAVE_RES_NSEARCH
542             rk_res_free(&state);
543 #endif
544             return NULL;
545         }
546 #ifdef HAVE_RES_NSEARCH
547         len = res_nsearch(&state, domain, rr_class, rr_type, reply, size);
548 #else
549         len = res_search(domain, rr_class, rr_type, reply, size);
550 #endif
551         if (_resolve_debug) {
552 #if defined(HAVE__RES) && !defined(HAVE_RES_NSEARCH)
553             _res.options = old_options;
554 #endif
555             fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n",
556                     domain, rr_class, dns_type_to_string(rr_type), len);
557         }
558         if (len < 0) {
559 #ifdef HAVE_RES_NSEARCH
560             rk_res_free(&state);
561 #endif
562             free(reply);
563             return NULL;
564         }
565     } while (size < len && len < rk_DNS_MAX_PACKET_SIZE);
566 #ifdef HAVE_RES_NSEARCH
567     rk_res_free(&state);
568 #endif
569
570     len = min(len, size);
571     r = parse_reply(reply, len);
572     free(reply);
573     return r;
574 }
575
576 struct dns_reply * ROKEN_LIB_FUNCTION
577 dns_lookup(const char *domain, const char *type_name)
578 {
579     int type;
580     
581     type = dns_string_to_type(type_name);
582     if(type == -1) {
583         if(_resolve_debug)
584             fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n", 
585                     type_name);
586         return NULL;
587     }
588     return dns_lookup_int(domain, C_IN, type);
589 }
590
591 static int
592 compare_srv(const void *a, const void *b)
593 {
594     const struct resource_record *const* aa = a, *const* bb = b;
595
596     if((*aa)->u.srv->priority == (*bb)->u.srv->priority)
597         return ((*aa)->u.srv->weight - (*bb)->u.srv->weight);
598     return ((*aa)->u.srv->priority - (*bb)->u.srv->priority);
599 }
600
601 #ifndef HAVE_RANDOM
602 #define random() rand()
603 #endif
604
605 /* try to rearrange the srv-records by the algorithm in RFC2782 */
606 void ROKEN_LIB_FUNCTION
607 dns_srv_order(struct dns_reply *r)
608 {
609     struct resource_record **srvs, **ss, **headp;
610     struct resource_record *rr;
611     int num_srv = 0;
612
613 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
614     int state[256 / sizeof(int)];
615     char *oldstate;
616 #endif
617
618     for(rr = r->head; rr; rr = rr->next) 
619         if(rr->type == rk_ns_t_srv)
620             num_srv++;
621
622     if(num_srv == 0)
623         return;
624
625     srvs = malloc(num_srv * sizeof(*srvs));
626     if(srvs == NULL)
627         return; /* XXX not much to do here */
628     
629     /* unlink all srv-records from the linked list and put them in
630        a vector */
631     for(ss = srvs, headp = &r->head; *headp; )
632         if((*headp)->type == rk_ns_t_srv) {
633             *ss = *headp;
634             *headp = (*headp)->next;
635             (*ss)->next = NULL;
636             ss++;
637         } else
638             headp = &(*headp)->next;
639     
640     /* sort them by priority and weight */
641     qsort(srvs, num_srv, sizeof(*srvs), compare_srv);
642
643 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
644     oldstate = initstate(time(NULL), (char*)state, sizeof(state));
645 #endif
646
647     headp = &r->head;
648     
649     for(ss = srvs; ss < srvs + num_srv; ) {
650         int sum, rnd, count;
651         struct resource_record **ee, **tt;
652         /* find the last record with the same priority and count the
653            sum of all weights */
654         for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) {
655             assert(*tt != NULL);
656             if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
657                 break;
658             sum += (*tt)->u.srv->weight;
659         }
660         ee = tt;
661         /* ss is now the first record of this priority and ee is the
662            first of the next */
663         while(ss < ee) {
664             rnd = random() % (sum + 1);
665             for(count = 0, tt = ss; ; tt++) {
666                 if(*tt == NULL)
667                     continue;
668                 count += (*tt)->u.srv->weight;
669                 if(count >= rnd)
670                     break;
671             }
672
673             assert(tt < ee);
674
675             /* insert the selected record at the tail (of the head) of
676                the list */
677             (*tt)->next = *headp;
678             *headp = *tt;
679             headp = &(*tt)->next;
680             sum -= (*tt)->u.srv->weight;
681             *tt = NULL;
682             while(ss < ee && *ss == NULL)
683                 ss++;
684         }
685     }
686     
687 #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE)
688     setstate(oldstate);
689 #endif
690     free(srvs);
691     return;
692 }
693
694 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
695
696 struct dns_reply * ROKEN_LIB_FUNCTION
697 dns_lookup(const char *domain, const char *type_name)
698 {
699     return NULL;
700 }
701
702 void ROKEN_LIB_FUNCTION
703 dns_free_data(struct dns_reply *r)
704 {
705 }
706
707 void ROKEN_LIB_FUNCTION
708 dns_srv_order(struct dns_reply *r)
709 {
710 }
711
712 #endif