1 /*	$NetBSD: resolve.c,v 1.4 2014/04/24 14:49:43 pettai Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 
37 #include <config.h>
38 
39 #include <krb5/roken.h>
40 #ifdef HAVE_ARPA_NAMESER_H
41 #include <arpa/nameser.h>
42 #endif
43 #ifdef HAVE_RESOLV_H
44 #include <resolv.h>
45 #endif
46 #ifdef HAVE_DNS_H
47 #include <dns.h>
48 #endif
49 #include <krb5/resolve.h>
50 
51 #include <assert.h>
52 
53 #ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */
54 #undef HAVE_RES_NSEARCH
55 #endif
56 
57 #define DECL(X) {#X, rk_ns_t_##X}
58 
59 static struct stot{
60     const char *name;
61     int type;
62 }stot[] = {
63     DECL(a),
64     DECL(aaaa),
65     DECL(ns),
66     DECL(cname),
67     DECL(soa),
68     DECL(ptr),
69     DECL(mx),
70     DECL(txt),
71     DECL(afsdb),
72     DECL(sig),
73     DECL(key),
74     DECL(srv),
75     DECL(naptr),
76     DECL(sshfp),
77     DECL(ds),
78     {NULL, 	0}
79 };
80 
81 int _resolve_debug = 0;
82 
83 ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rk_dns_string_to_type(const char * name)84 rk_dns_string_to_type(const char *name)
85 {
86     struct stot *p = stot;
87     for(p = stot; p->name; p++)
88 	if(strcasecmp(name, p->name) == 0)
89 	    return p->type;
90     return -1;
91 }
92 
93 ROKEN_LIB_FUNCTION const char * ROKEN_LIB_CALL
rk_dns_type_to_string(int type)94 rk_dns_type_to_string(int type)
95 {
96     struct stot *p = stot;
97     for(p = stot; p->name; p++)
98 	if(type == p->type)
99 	    return p->name;
100     return NULL;
101 }
102 
103 #if ((defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)) || defined(HAVE_WINDNS)
104 
105 static void
dns_free_rr(struct rk_resource_record * rr)106 dns_free_rr(struct rk_resource_record *rr)
107 {
108     if(rr->domain)
109 	free(rr->domain);
110     if(rr->u.data)
111 	free(rr->u.data);
112     free(rr);
113 }
114 
115 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_dns_free_data(struct rk_dns_reply * r)116 rk_dns_free_data(struct rk_dns_reply *r)
117 {
118     struct rk_resource_record *rr;
119     if(r->q.domain)
120 	free(r->q.domain);
121     for(rr = r->head; rr;){
122 	struct rk_resource_record *tmp = rr;
123 	rr = rr->next;
124 	dns_free_rr(tmp);
125     }
126     free (r);
127 }
128 
129 #ifndef HAVE_WINDNS
130 
131 static int
parse_record(const unsigned char * data,const unsigned char * end_data,const unsigned char ** pp,struct rk_resource_record ** ret_rr)132 parse_record(const unsigned char *data, const unsigned char *end_data,
133 	     const unsigned char **pp, struct rk_resource_record **ret_rr)
134 {
135     struct rk_resource_record *rr;
136     int type, class, ttl;
137     unsigned size;
138     int status;
139     char host[MAXDNAME];
140     const unsigned char *p = *pp;
141 
142     *ret_rr = NULL;
143 
144     status = dn_expand(data, end_data, p, host, sizeof(host));
145     if(status < 0)
146 	return -1;
147     if (p + status + 10 > end_data)
148 	return -1;
149 
150     p += status;
151     type = (p[0] << 8) | p[1];
152     p += 2;
153     class = (p[0] << 8) | p[1];
154     p += 2;
155     ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
156     p += 4;
157     size = (p[0] << 8) | p[1];
158     p += 2;
159 
160     if (p + size > end_data)
161 	return -1;
162 
163     rr = calloc(1, sizeof(*rr));
164     if(rr == NULL)
165 	return -1;
166     rr->domain = strdup(host);
167     if(rr->domain == NULL) {
168 	dns_free_rr(rr);
169 	return -1;
170     }
171     rr->type = type;
172     rr->class = class;
173     rr->ttl = ttl;
174     rr->size = size;
175     switch(type){
176     case rk_ns_t_ns:
177     case rk_ns_t_cname:
178     case rk_ns_t_ptr:
179 	status = dn_expand(data, end_data, p, host, sizeof(host));
180 	if(status < 0) {
181 	    dns_free_rr(rr);
182 	    return -1;
183 	}
184 	rr->u.txt = strdup(host);
185 	if(rr->u.txt == NULL) {
186 	    dns_free_rr(rr);
187 	    return -1;
188 	}
189 	break;
190     case rk_ns_t_mx:
191     case rk_ns_t_afsdb:{
192 	size_t hostlen;
193 
194 	status = dn_expand(data, end_data, p + 2, host, sizeof(host));
195 	if(status < 0){
196 	    dns_free_rr(rr);
197 	    return -1;
198 	}
199 	if ((size_t)status + 2 > size) {
200 	    dns_free_rr(rr);
201 	    return -1;
202 	}
203 
204 	hostlen = strlen(host);
205 	rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
206 						hostlen);
207 	if(rr->u.mx == NULL) {
208 	    dns_free_rr(rr);
209 	    return -1;
210 	}
211 	rr->u.mx->preference = (p[0] << 8) | p[1];
212 	strlcpy(rr->u.mx->domain, host, hostlen + 1);
213 	break;
214     }
215     case rk_ns_t_srv:{
216 	size_t hostlen;
217 	status = dn_expand(data, end_data, p + 6, host, sizeof(host));
218 	if(status < 0){
219 	    dns_free_rr(rr);
220 	    return -1;
221 	}
222 	if ((size_t)status + 6 > size) {
223 	    dns_free_rr(rr);
224 	    return -1;
225 	}
226 
227 	hostlen = strlen(host);
228 	rr->u.srv =
229 	    (struct srv_record*)malloc(sizeof(struct srv_record) +
230 				       hostlen);
231 	if(rr->u.srv == NULL) {
232 	    dns_free_rr(rr);
233 	    return -1;
234 	}
235 	rr->u.srv->priority = (p[0] << 8) | p[1];
236 	rr->u.srv->weight = (p[2] << 8) | p[3];
237 	rr->u.srv->port = (p[4] << 8) | p[5];
238 	strlcpy(rr->u.srv->target, host, hostlen + 1);
239 	break;
240     }
241     case rk_ns_t_txt:{
242 	if(size == 0 || size < (unsigned)(*p + 1)) {
243 	    dns_free_rr(rr);
244 	    return -1;
245 	}
246 	rr->u.txt = (char*)malloc(*p + 1);
247 	if(rr->u.txt == NULL) {
248 	    dns_free_rr(rr);
249 	    return -1;
250 	}
251 	strncpy(rr->u.txt, (const char*)(p + 1), *p);
252 	rr->u.txt[*p] = '\0';
253 	break;
254     }
255     case rk_ns_t_key : {
256 	size_t key_len;
257 
258 	if (size < 4) {
259 	    dns_free_rr(rr);
260 	    return -1;
261 	}
262 
263 	key_len = size - 4;
264 	rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
265 	if (rr->u.key == NULL) {
266 	    dns_free_rr(rr);
267 	    return -1;
268 	}
269 
270 	rr->u.key->flags     = (p[0] << 8) | p[1];
271 	rr->u.key->protocol  = p[2];
272 	rr->u.key->algorithm = p[3];
273 	rr->u.key->key_len   = key_len;
274 	memcpy (rr->u.key->key_data, p + 4, key_len);
275 	break;
276     }
277     case rk_ns_t_sig : {
278 	size_t sig_len, hostlen;
279 
280 	if(size <= 18) {
281 	    dns_free_rr(rr);
282 	    return -1;
283 	}
284 	status = dn_expand (data, end_data, p + 18, host, sizeof(host));
285 	if (status < 0) {
286 	    dns_free_rr(rr);
287 	    return -1;
288 	}
289 	if ((size_t)status + 18 > size) {
290 	    dns_free_rr(rr);
291 	    return -1;
292 	}
293 
294 	/* the signer name is placed after the sig_data, to make it
295            easy to free this structure; the size calculation below
296            includes the zero-termination if the structure itself.
297 	   don't you just love C?
298 	*/
299 	sig_len = size - 18 - status;
300 	hostlen = strlen(host);
301 	rr->u.sig = malloc(sizeof(*rr->u.sig)
302 			      + hostlen + sig_len);
303 	if (rr->u.sig == NULL) {
304 	    dns_free_rr(rr);
305 	    return -1;
306 	}
307 	rr->u.sig->type           = (p[0] << 8) | p[1];
308 	rr->u.sig->algorithm      = p[2];
309 	rr->u.sig->labels         = p[3];
310 	rr->u.sig->orig_ttl       = (p[4] << 24) | (p[5] << 16)
311 	    | (p[6] << 8) | p[7];
312 	rr->u.sig->sig_expiration = (p[8] << 24) | (p[9] << 16)
313 	    | (p[10] << 8) | p[11];
314 	rr->u.sig->sig_inception  = (p[12] << 24) | (p[13] << 16)
315 	    | (p[14] << 8) | p[15];
316 	rr->u.sig->key_tag        = (p[16] << 8) | p[17];
317 	rr->u.sig->sig_len        = sig_len;
318 	memcpy (rr->u.sig->sig_data, p + 18 + status, sig_len);
319 	rr->u.sig->signer         = &rr->u.sig->sig_data[sig_len];
320 	strlcpy(rr->u.sig->signer, host, hostlen + 1);
321 	break;
322     }
323 
324     case rk_ns_t_cert : {
325 	size_t cert_len;
326 
327 	if (size < 5) {
328 	    dns_free_rr(rr);
329 	    return -1;
330 	}
331 
332 	cert_len = size - 5;
333 	rr->u.cert = malloc (sizeof(*rr->u.cert) + cert_len - 1);
334 	if (rr->u.cert == NULL) {
335 	    dns_free_rr(rr);
336 	    return -1;
337 	}
338 
339 	rr->u.cert->type      = (p[0] << 8) | p[1];
340 	rr->u.cert->tag       = (p[2] << 8) | p[3];
341 	rr->u.cert->algorithm = p[4];
342 	rr->u.cert->cert_len  = cert_len;
343 	memcpy (rr->u.cert->cert_data, p + 5, cert_len);
344 	break;
345     }
346     case rk_ns_t_sshfp : {
347 	size_t sshfp_len;
348 
349 	if (size < 2) {
350 	    dns_free_rr(rr);
351 	    return -1;
352 	}
353 
354 	sshfp_len = size - 2;
355 
356 	rr->u.sshfp = malloc (sizeof(*rr->u.sshfp) + sshfp_len - 1);
357 	if (rr->u.sshfp == NULL) {
358 	    dns_free_rr(rr);
359 	    return -1;
360 	}
361 
362 	rr->u.sshfp->algorithm = p[0];
363 	rr->u.sshfp->type      = p[1];
364 	rr->u.sshfp->sshfp_len  = sshfp_len;
365 	memcpy (rr->u.sshfp->sshfp_data, p + 2, sshfp_len);
366 	break;
367     }
368     case rk_ns_t_ds: {
369 	size_t digest_len;
370 
371 	if (size < 4) {
372 	    dns_free_rr(rr);
373 	    return -1;
374 	}
375 
376 	digest_len = size - 4;
377 
378 	rr->u.ds = malloc (sizeof(*rr->u.ds) + digest_len - 1);
379 	if (rr->u.ds == NULL) {
380 	    dns_free_rr(rr);
381 	    return -1;
382 	}
383 
384 	rr->u.ds->key_tag     = (p[0] << 8) | p[1];
385 	rr->u.ds->algorithm   = p[2];
386 	rr->u.ds->digest_type = p[3];
387 	rr->u.ds->digest_len  = digest_len;
388 	memcpy (rr->u.ds->digest_data, p + 4, digest_len);
389 	break;
390     }
391     default:
392 	rr->u.data = (unsigned char*)malloc(size);
393 	if(size != 0 && rr->u.data == NULL) {
394 	    dns_free_rr(rr);
395 	    return -1;
396 	}
397 	if (size)
398 	    memcpy(rr->u.data, p, size);
399     }
400     *pp = p + size;
401     *ret_rr = rr;
402 
403     return 0;
404 }
405 
406 #ifndef TEST_RESOLVE
407 static
408 #endif
409 struct rk_dns_reply*
parse_reply(const unsigned char * data,size_t len)410 parse_reply(const unsigned char *data, size_t len)
411 {
412     const unsigned char *p;
413     int status;
414     size_t i;
415     char host[MAXDNAME];
416     const unsigned char *end_data = data + len;
417     struct rk_dns_reply *r;
418     struct rk_resource_record **rr;
419 
420     r = calloc(1, sizeof(*r));
421     if (r == NULL)
422 	return NULL;
423 
424     p = data;
425 
426     r->h.id = (p[0] << 8) | p[1];
427     r->h.flags = 0;
428     if (p[2] & 0x01)
429 	r->h.flags |= rk_DNS_HEADER_RESPONSE_FLAG;
430     r->h.opcode = (p[2] >> 1) & 0xf;
431     if (p[2] & 0x20)
432 	r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
433     if (p[2] & 0x40)
434 	r->h.flags |= rk_DNS_HEADER_TRUNCATED_MESSAGE;
435     if (p[2] & 0x80)
436 	r->h.flags |= rk_DNS_HEADER_RECURSION_DESIRED;
437     if (p[3] & 0x01)
438 	r->h.flags |= rk_DNS_HEADER_RECURSION_AVAILABLE;
439     if (p[3] & 0x04)
440 	r->h.flags |= rk_DNS_HEADER_AUTHORITIVE_ANSWER;
441     if (p[3] & 0x08)
442 	r->h.flags |= rk_DNS_HEADER_CHECKING_DISABLED;
443     r->h.response_code = (p[3] >> 4) & 0xf;
444     r->h.qdcount = (p[4] << 8) | p[5];
445     r->h.ancount = (p[6] << 8) | p[7];
446     r->h.nscount = (p[8] << 8) | p[9];
447     r->h.arcount = (p[10] << 8) | p[11];
448 
449     p += 12;
450 
451     if(r->h.qdcount != 1) {
452 	free(r);
453 	return NULL;
454     }
455     status = dn_expand(data, end_data, p, host, sizeof(host));
456     if(status < 0){
457 	rk_dns_free_data(r);
458 	return NULL;
459     }
460     r->q.domain = strdup(host);
461     if(r->q.domain == NULL) {
462 	rk_dns_free_data(r);
463 	return NULL;
464     }
465     if (p + status + 4 > end_data) {
466 	rk_dns_free_data(r);
467 	return NULL;
468     }
469     p += status;
470     r->q.type = (p[0] << 8 | p[1]);
471     p += 2;
472     r->q.class = (p[0] << 8 | p[1]);
473     p += 2;
474 
475     rr = &r->head;
476     for(i = 0; i < r->h.ancount; i++) {
477 	if(parse_record(data, end_data, &p, rr) != 0) {
478 	    rk_dns_free_data(r);
479 	    return NULL;
480 	}
481 	rr = &(*rr)->next;
482     }
483     for(i = 0; i < r->h.nscount; i++) {
484 	if(parse_record(data, end_data, &p, rr) != 0) {
485 	    rk_dns_free_data(r);
486 	    return NULL;
487 	}
488 	rr = &(*rr)->next;
489     }
490     for(i = 0; i < r->h.arcount; i++) {
491 	if(parse_record(data, end_data, &p, rr) != 0) {
492 	    rk_dns_free_data(r);
493 	    return NULL;
494 	}
495 	rr = &(*rr)->next;
496     }
497     *rr = NULL;
498     return r;
499 }
500 
501 #ifdef HAVE_RES_NSEARCH
502 #ifdef HAVE_RES_NDESTROY
503 #define rk_res_free(x) res_ndestroy(x)
504 #else
505 #define rk_res_free(x) res_nclose(x)
506 #endif
507 #endif
508 
509 #if defined(HAVE_DNS_SEARCH)
510 #define resolve_search(h,n,c,t,r,l) \
511     	((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize))
512 #define resolve_free_handle(h) dns_free(h)
513 #elif defined(HAVE_RES_NSEARCH)
514 #define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l)
515 #define resolve_free_handle(h) rk_res_free(h);
516 #else
517 #define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l)
518 #define handle 0
519 #define resolve_free_handle(h)
520 #endif
521 
522 
523 static struct rk_dns_reply *
dns_lookup_int(const char * domain,int rr_class,int rr_type)524 dns_lookup_int(const char *domain, int rr_class, int rr_type)
525 {
526     struct rk_dns_reply *r;
527     void *reply = NULL;
528     int size, len;
529 #if defined(HAVE_DNS_SEARCH)
530     struct sockaddr_storage from;
531     uint32_t fromsize = sizeof(from);
532     dns_handle_t handle;
533 
534     handle = dns_open(NULL);
535     if (handle == NULL)
536 	return NULL;
537 #elif defined(HAVE_RES_NSEARCH)
538     struct __res_state state;
539     struct __res_state *handle = &state;
540 
541     memset(&state, 0, sizeof(state));
542     if(res_ninit(handle))
543 	return NULL; /* is this the best we can do? */
544 #endif
545 
546     len = 1500;
547     while(1) {
548 	if (reply) {
549 	    free(reply);
550 	    reply = NULL;
551 	}
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), len);
560 	}
561 	reply = malloc(len);
562 	if (reply == NULL) {
563 	    resolve_free_handle(handle);
564 	    return NULL;
565 	}
566 
567 	size = resolve_search(handle, domain, rr_class, rr_type, reply, len);
568 
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), size);
572 	}
573 	if (size > len) {
574 	    /* resolver thinks it know better, go for it */
575 	    len = size;
576 	} else if (size > 0) {
577 	    /* got a good reply */
578 	    break;
579 	} else if (size <= 0 && len < rk_DNS_MAX_PACKET_SIZE) {
580 	    len *= 2;
581 	    if (len > rk_DNS_MAX_PACKET_SIZE)
582 		len = rk_DNS_MAX_PACKET_SIZE;
583 	} else {
584 	    /* the end, leave */
585 	    resolve_free_handle(handle);
586 	    free(reply);
587 	    return NULL;
588 	}
589     }
590 
591     len = min(len, size);
592     r = parse_reply(reply, len);
593     free(reply);
594 
595     resolve_free_handle(handle);
596 
597     return r;
598 }
599 
600 ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
rk_dns_lookup(const char * domain,const char * type_name)601 rk_dns_lookup(const char *domain, const char *type_name)
602 {
603     int type;
604 
605     type = rk_dns_string_to_type(type_name);
606     if(type == -1) {
607 	if(_resolve_debug)
608 	    fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
609 		    type_name);
610 	return NULL;
611     }
612     return dns_lookup_int(domain, rk_ns_c_in, type);
613 }
614 
615 #endif	/* !HAVE_WINDNS */
616 
617 static int
compare_srv(const void * a,const void * b)618 compare_srv(const void *a, const void *b)
619 {
620     const struct rk_resource_record *const* aa = a, *const* bb = b;
621 
622     if((*aa)->u.srv->priority == (*bb)->u.srv->priority)
623 	return ((*aa)->u.srv->weight - (*bb)->u.srv->weight);
624     return ((*aa)->u.srv->priority - (*bb)->u.srv->priority);
625 }
626 
627 /* try to rearrange the srv-records by the algorithm in RFC2782 */
628 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_dns_srv_order(struct rk_dns_reply * r)629 rk_dns_srv_order(struct rk_dns_reply *r)
630 {
631     struct rk_resource_record **srvs, **ss, **headp;
632     struct rk_resource_record *rr;
633     int num_srv = 0;
634 
635     rk_random_init();
636 
637     for(rr = r->head; rr; rr = rr->next)
638 	if(rr->type == rk_ns_t_srv)
639 	    num_srv++;
640 
641     if(num_srv == 0)
642 	return;
643 
644     srvs = malloc(num_srv * sizeof(*srvs));
645     if(srvs == NULL)
646 	return; /* XXX not much to do here */
647 
648     /* unlink all srv-records from the linked list and put them in
649        a vector */
650     for(ss = srvs, headp = &r->head; *headp; )
651 	if((*headp)->type == rk_ns_t_srv) {
652 	    *ss = *headp;
653 	    *headp = (*headp)->next;
654 	    (*ss)->next = NULL;
655 	    ss++;
656 	} else
657 	    headp = &(*headp)->next;
658 
659     /* sort them by priority and weight */
660     qsort(srvs, num_srv, sizeof(*srvs), compare_srv);
661 
662     headp = &r->head;
663 
664     for(ss = srvs; ss < srvs + num_srv; ) {
665 	int sum, rnd, count;
666 	struct rk_resource_record **ee, **tt;
667 	/* find the last record with the same priority and count the
668            sum of all weights */
669 	for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) {
670 	    assert(*tt != NULL);
671 	    if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
672 		break;
673 	    sum += (*tt)->u.srv->weight;
674 	}
675 	ee = tt;
676 	/* ss is now the first record of this priority and ee is the
677            first of the next */
678 	while(ss < ee) {
679 	    rnd = rk_random() % (sum + 1);
680 	    for(count = 0, tt = ss; ; tt++) {
681 		if(*tt == NULL)
682 		    continue;
683 		count += (*tt)->u.srv->weight;
684 		if(count >= rnd)
685 		    break;
686 	    }
687 
688 	    assert(tt < ee);
689 
690 	    /* insert the selected record at the tail (of the head) of
691                the list */
692 	    (*tt)->next = *headp;
693 	    *headp = *tt;
694 	    headp = &(*tt)->next;
695 	    sum -= (*tt)->u.srv->weight;
696 	    *tt = NULL;
697 	    while(ss < ee && *ss == NULL)
698 		ss++;
699 	}
700     }
701 
702     free(srvs);
703     return;
704 }
705 
706 #ifdef HAVE_WINDNS
707 
708 #include <WinDNS.h>
709 
710 static struct rk_resource_record *
parse_dns_record(PDNS_RECORD pRec)711 parse_dns_record(PDNS_RECORD pRec)
712 {
713     struct rk_resource_record * rr;
714 
715     if (pRec == NULL)
716 	return NULL;
717 
718     rr = calloc(1, sizeof(*rr));
719 
720     rr->domain = strdup(pRec->pName);
721     rr->type = pRec->wType;
722     rr->class = 0;
723     rr->ttl = pRec->dwTtl;
724     rr->size = 0;
725 
726     switch (rr->type) {
727     case rk_ns_t_ns:
728     case rk_ns_t_cname:
729     case rk_ns_t_ptr:
730 	rr->u.txt = strdup(pRec->Data.NS.pNameHost);
731 	if(rr->u.txt == NULL) {
732 	    dns_free_rr(rr);
733 	    return NULL;
734 	}
735 	break;
736 
737     case rk_ns_t_mx:
738     case rk_ns_t_afsdb:{
739 	size_t hostlen = strnlen(pRec->Data.MX.pNameExchange, DNS_MAX_NAME_LENGTH);
740 
741 	rr->u.mx = (struct mx_record *)malloc(sizeof(struct mx_record) +
742 					      hostlen);
743 	if (rr->u.mx == NULL) {
744 	    dns_free_rr(rr);
745 	    return NULL;
746 	}
747 
748 	strcpy_s(rr->u.mx->domain, hostlen + 1, pRec->Data.MX.pNameExchange);
749 	rr->u.mx->preference = pRec->Data.MX.wPreference;
750 	break;
751     }
752 
753     case rk_ns_t_srv:{
754 	size_t hostlen = strnlen(pRec->Data.SRV.pNameTarget, DNS_MAX_NAME_LENGTH);
755 
756 	rr->u.srv =
757 	    (struct srv_record*)malloc(sizeof(struct srv_record) +
758 				       hostlen);
759 	if(rr->u.srv == NULL) {
760 	    dns_free_rr(rr);
761 	    return NULL;
762 	}
763 
764 	rr->u.srv->priority = pRec->Data.SRV.wPriority;
765 	rr->u.srv->weight = pRec->Data.SRV.wWeight;
766 	rr->u.srv->port = pRec->Data.SRV.wPort;
767 	strcpy_s(rr->u.srv->target, hostlen + 1, pRec->Data.SRV.pNameTarget);
768 
769 	break;
770     }
771 
772     case rk_ns_t_txt:{
773 	size_t len;
774 
775 	if (pRec->Data.TXT.dwStringCount == 0) {
776 	    rr->u.txt = strdup("");
777 	    break;
778 	}
779 
780 	len = strnlen(pRec->Data.TXT.pStringArray[0], DNS_MAX_TEXT_STRING_LENGTH);
781 
782 	rr->u.txt = (char *)malloc(len + 1);
783 	strcpy_s(rr->u.txt, len + 1, pRec->Data.TXT.pStringArray[0]);
784 
785 	break;
786     }
787 
788     case rk_ns_t_key : {
789 	size_t key_len;
790 
791 	if (pRec->wDataLength < 4) {
792 	    dns_free_rr(rr);
793 	    return NULL;
794 	}
795 
796 	key_len = pRec->wDataLength - 4;
797 	rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
798 	if (rr->u.key == NULL) {
799 	    dns_free_rr(rr);
800 	    return NULL;
801 	}
802 
803 	rr->u.key->flags     = pRec->Data.KEY.wFlags;
804 	rr->u.key->protocol  = pRec->Data.KEY.chProtocol;
805 	rr->u.key->algorithm = pRec->Data.KEY.chAlgorithm;
806 	rr->u.key->key_len   = key_len;
807 	memcpy_s (rr->u.key->key_data, key_len,
808 		  pRec->Data.KEY.Key, key_len);
809 	break;
810     }
811 
812     case rk_ns_t_sig : {
813 	size_t sig_len, hostlen;
814 
815 	if(pRec->wDataLength <= 18) {
816 	    dns_free_rr(rr);
817 	    return NULL;
818 	}
819 
820 	sig_len = pRec->wDataLength;
821 
822 	hostlen = strnlen(pRec->Data.SIG.pNameSigner, DNS_MAX_NAME_LENGTH);
823 
824 	rr->u.sig = malloc(sizeof(*rr->u.sig)
825 			      + hostlen + sig_len);
826 	if (rr->u.sig == NULL) {
827 	    dns_free_rr(rr);
828 	    return NULL;
829 	}
830 	rr->u.sig->type           = pRec->Data.SIG.wTypeCovered;
831 	rr->u.sig->algorithm      = pRec->Data.SIG.chAlgorithm;
832 	rr->u.sig->labels         = pRec->Data.SIG.chLabelCount;
833 	rr->u.sig->orig_ttl       = pRec->Data.SIG.dwOriginalTtl;
834 	rr->u.sig->sig_expiration = pRec->Data.SIG.dwExpiration;
835 	rr->u.sig->sig_inception  = pRec->Data.SIG.dwTimeSigned;
836 	rr->u.sig->key_tag        = pRec->Data.SIG.wKeyTag;
837 	rr->u.sig->sig_len        = sig_len;
838 	memcpy_s (rr->u.sig->sig_data, sig_len,
839 		  pRec->Data.SIG.Signature, sig_len);
840 	rr->u.sig->signer         = &rr->u.sig->sig_data[sig_len];
841 	strcpy_s(rr->u.sig->signer, hostlen + 1, pRec->Data.SIG.pNameSigner);
842 	break;
843     }
844 
845 #ifdef DNS_TYPE_DS
846     case rk_ns_t_ds: {
847 	rr->u.ds = malloc (sizeof(*rr->u.ds) + pRec->Data.DS.wDigestLength - 1);
848 	if (rr->u.ds == NULL) {
849 	    dns_free_rr(rr);
850 	    return NULL;
851 	}
852 
853 	rr->u.ds->key_tag     = pRec->Data.DS.wKeyTag;
854 	rr->u.ds->algorithm   = pRec->Data.DS.chAlgorithm;
855 	rr->u.ds->digest_type = pRec->Data.DS.chDigestType;
856 	rr->u.ds->digest_len  = pRec->Data.DS.wDigestLength;
857 	memcpy_s (rr->u.ds->digest_data, pRec->Data.DS.wDigestLength,
858 		  pRec->Data.DS.Digest, pRec->Data.DS.wDigestLength);
859 	break;
860     }
861 #endif
862 
863     default:
864 	dns_free_rr(rr);
865 	return NULL;
866     }
867 
868     rr->next = parse_dns_record(pRec->pNext);
869     return rr;
870 }
871 
872 ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
rk_dns_lookup(const char * domain,const char * type_name)873 rk_dns_lookup(const char *domain, const char *type_name)
874 {
875     DNS_STATUS status;
876     int type;
877     PDNS_RECORD pRec = NULL;
878     struct rk_dns_reply * r = NULL;
879 
880     __try {
881 
882 	type = rk_dns_string_to_type(type_name);
883 	if(type == -1) {
884 	    if(_resolve_debug)
885 		fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
886 			type_name);
887 	    return NULL;
888 	}
889 
890 	status = DnsQuery_UTF8(domain, type, DNS_QUERY_STANDARD, NULL,
891 			       &pRec, NULL);
892 	if (status != ERROR_SUCCESS)
893 	    return NULL;
894 
895 	r = calloc(1, sizeof(*r));
896 	r->q.domain = strdup(domain);
897 	r->q.type = type;
898 	r->q.class = 0;
899 
900 	r->head = parse_dns_record(pRec);
901 
902 	if (r->head == NULL) {
903 	    rk_dns_free_data(r);
904 	    return NULL;
905 	} else {
906 	    return r;
907 	}
908 
909     } __finally {
910 
911 	if (pRec)
912 	    DnsRecordListFree(pRec, DnsFreeRecordList);
913 
914     }
915 }
916 #endif	/* HAVE_WINDNS */
917 
918 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
919 
920 ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
rk_dns_lookup(const char * domain,const char * type_name)921 rk_dns_lookup(const char *domain, const char *type_name)
922 {
923     return NULL;
924 }
925 
926 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_dns_free_data(struct rk_dns_reply * r)927 rk_dns_free_data(struct rk_dns_reply *r)
928 {
929 }
930 
931 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_dns_srv_order(struct rk_dns_reply * r)932 rk_dns_srv_order(struct rk_dns_reply *r)
933 {
934 }
935 
936 #endif
937