xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/roken/resolve.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1 /*	$NetBSD: resolve.c,v 1.6 2023/06/19 21:41:45 christos 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, zeros, rnd, count; /* zeros -> weight scaling */
666 	struct rk_resource_record **ee, **tt;
667 
668 	/*
669 	 * find the last record with the same priority and count the sum of all
670 	 * weights
671 	 */
672 	for (sum = 0, zeros = 0, tt = ss; tt < srvs + num_srv; tt++) {
673 	    assert(*tt != NULL);
674 	    if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
675 		break;
676 	    sum += (*tt)->u.srv->weight;
677 	    if ((*tt)->u.srv->weight == 0)
678 		zeros++;
679 	}
680 	/* make sure scale (`zeros') is > 0 then scale out */
681 	sum += zeros ? 1 : zeros++;
682 	sum *= zeros;
683 	ee = tt;
684 
685 	/*
686 	 * ss is now the first record of this priority and ee is the first of
687 	 * the next or the first past the end of srvs
688 	 */
689 	while (ss < ee) {
690 	    rnd = rk_random() % sum + 1;
691 	    for (count = 0, tt = ss; tt < ee; tt++) {
692 		if (*tt == NULL)
693 		    continue;   /* this one's already been picked */
694 		if ((*tt)->u.srv->weight == 0)
695 		    count++;
696 		else
697 		    count += (*tt)->u.srv->weight * zeros;
698 		if (count >= rnd)
699 		    break;
700 	    }
701 	    assert(tt < ee);
702 
703 	    /* push the selected record */
704 	    (*tt)->next = *headp;
705 	    *headp = *tt;
706 	    headp = &(*tt)->next;
707 	    /*
708 	     * reduce the sum so the next iteration is sure to reach the random
709 	     * total after examining all the remaining records.
710 	     */
711 	    if ((*tt)->u.srv->weight == 0)
712 		sum--;
713 	    else
714 		sum -= (*tt)->u.srv->weight * zeros;
715 	    *tt = NULL;
716 	    while (ss < ee && *ss == NULL)
717 		ss++;
718 	}
719     }
720 
721     free(srvs);
722     return;
723 }
724 
725 #ifdef HAVE_WINDNS
726 
727 #include <WinDNS.h>
728 
729 static struct rk_resource_record *
parse_dns_record(PDNS_RECORD pRec)730 parse_dns_record(PDNS_RECORD pRec)
731 {
732     struct rk_resource_record * rr;
733 
734     if (pRec == NULL)
735 	return NULL;
736 
737     rr = calloc(1, sizeof(*rr));
738 
739     rr->domain = strdup(pRec->pName);
740     rr->type = pRec->wType;
741     rr->class = 0;
742     rr->ttl = pRec->dwTtl;
743     rr->size = 0;
744 
745     switch (rr->type) {
746     case rk_ns_t_ns:
747     case rk_ns_t_cname:
748     case rk_ns_t_ptr:
749 	rr->u.txt = strdup(pRec->Data.NS.pNameHost);
750 	if(rr->u.txt == NULL) {
751 	    dns_free_rr(rr);
752 	    return NULL;
753 	}
754 	break;
755 
756     case rk_ns_t_mx:
757     case rk_ns_t_afsdb:{
758 	size_t hostlen = strnlen(pRec->Data.MX.pNameExchange, DNS_MAX_NAME_LENGTH);
759 
760 	rr->u.mx = (struct mx_record *)malloc(sizeof(struct mx_record) +
761 					      hostlen);
762 	if (rr->u.mx == NULL) {
763 	    dns_free_rr(rr);
764 	    return NULL;
765 	}
766 
767 	strcpy_s(rr->u.mx->domain, hostlen + 1, pRec->Data.MX.pNameExchange);
768 	rr->u.mx->preference = pRec->Data.MX.wPreference;
769 	break;
770     }
771 
772     case rk_ns_t_srv:{
773 	size_t hostlen = strnlen(pRec->Data.SRV.pNameTarget, DNS_MAX_NAME_LENGTH);
774 
775 	rr->u.srv =
776 	    (struct srv_record*)malloc(sizeof(struct srv_record) +
777 				       hostlen);
778 	if(rr->u.srv == NULL) {
779 	    dns_free_rr(rr);
780 	    return NULL;
781 	}
782 
783 	rr->u.srv->priority = pRec->Data.SRV.wPriority;
784 	rr->u.srv->weight = pRec->Data.SRV.wWeight;
785 	rr->u.srv->port = pRec->Data.SRV.wPort;
786 	strcpy_s(rr->u.srv->target, hostlen + 1, pRec->Data.SRV.pNameTarget);
787 
788 	break;
789     }
790 
791     case rk_ns_t_txt:{
792 	size_t len;
793 
794 	if (pRec->Data.TXT.dwStringCount == 0) {
795 	    rr->u.txt = strdup("");
796 	    break;
797 	}
798 
799 	len = strnlen(pRec->Data.TXT.pStringArray[0], DNS_MAX_TEXT_STRING_LENGTH);
800 
801 	rr->u.txt = (char *)malloc(len + 1);
802 	strcpy_s(rr->u.txt, len + 1, pRec->Data.TXT.pStringArray[0]);
803 
804 	break;
805     }
806 
807     case rk_ns_t_key : {
808 	size_t key_len;
809 
810 	if (pRec->wDataLength < 4) {
811 	    dns_free_rr(rr);
812 	    return NULL;
813 	}
814 
815 	key_len = pRec->wDataLength - 4;
816 	rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1);
817 	if (rr->u.key == NULL) {
818 	    dns_free_rr(rr);
819 	    return NULL;
820 	}
821 
822 	rr->u.key->flags     = pRec->Data.KEY.wFlags;
823 	rr->u.key->protocol  = pRec->Data.KEY.chProtocol;
824 	rr->u.key->algorithm = pRec->Data.KEY.chAlgorithm;
825 	rr->u.key->key_len   = key_len;
826 	memcpy_s (rr->u.key->key_data, key_len,
827 		  pRec->Data.KEY.Key, key_len);
828 	break;
829     }
830 
831     case rk_ns_t_sig : {
832 	size_t sig_len, hostlen;
833 
834 	if(pRec->wDataLength <= 18) {
835 	    dns_free_rr(rr);
836 	    return NULL;
837 	}
838 
839 	sig_len = pRec->wDataLength;
840 
841 	hostlen = strnlen(pRec->Data.SIG.pNameSigner, DNS_MAX_NAME_LENGTH);
842 
843 	rr->u.sig = malloc(sizeof(*rr->u.sig)
844 			      + hostlen + sig_len);
845 	if (rr->u.sig == NULL) {
846 	    dns_free_rr(rr);
847 	    return NULL;
848 	}
849 	rr->u.sig->type           = pRec->Data.SIG.wTypeCovered;
850 	rr->u.sig->algorithm      = pRec->Data.SIG.chAlgorithm;
851 	rr->u.sig->labels         = pRec->Data.SIG.chLabelCount;
852 	rr->u.sig->orig_ttl       = pRec->Data.SIG.dwOriginalTtl;
853 	rr->u.sig->sig_expiration = pRec->Data.SIG.dwExpiration;
854 	rr->u.sig->sig_inception  = pRec->Data.SIG.dwTimeSigned;
855 	rr->u.sig->key_tag        = pRec->Data.SIG.wKeyTag;
856 	rr->u.sig->sig_len        = sig_len;
857 	memcpy_s (rr->u.sig->sig_data, sig_len,
858 		  pRec->Data.SIG.Signature, sig_len);
859 	rr->u.sig->signer         = &rr->u.sig->sig_data[sig_len];
860 	strcpy_s(rr->u.sig->signer, hostlen + 1, pRec->Data.SIG.pNameSigner);
861 	break;
862     }
863 
864 #ifdef DNS_TYPE_DS
865     case rk_ns_t_ds: {
866 	rr->u.ds = malloc (sizeof(*rr->u.ds) + pRec->Data.DS.wDigestLength - 1);
867 	if (rr->u.ds == NULL) {
868 	    dns_free_rr(rr);
869 	    return NULL;
870 	}
871 
872 	rr->u.ds->key_tag     = pRec->Data.DS.wKeyTag;
873 	rr->u.ds->algorithm   = pRec->Data.DS.chAlgorithm;
874 	rr->u.ds->digest_type = pRec->Data.DS.chDigestType;
875 	rr->u.ds->digest_len  = pRec->Data.DS.wDigestLength;
876 	memcpy_s (rr->u.ds->digest_data, pRec->Data.DS.wDigestLength,
877 		  pRec->Data.DS.Digest, pRec->Data.DS.wDigestLength);
878 	break;
879     }
880 #endif
881 
882     default:
883 	dns_free_rr(rr);
884 	return NULL;
885     }
886 
887     rr->next = parse_dns_record(pRec->pNext);
888     return rr;
889 }
890 
891 ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
rk_dns_lookup(const char * domain,const char * type_name)892 rk_dns_lookup(const char *domain, const char *type_name)
893 {
894     DNS_STATUS status;
895     int type;
896     PDNS_RECORD pRec = NULL;
897     struct rk_dns_reply * r = NULL;
898 
899     __try {
900 
901 	type = rk_dns_string_to_type(type_name);
902 	if(type == -1) {
903 	    if(_resolve_debug)
904 		fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n",
905 			type_name);
906 	    return NULL;
907 	}
908 
909 	status = DnsQuery_UTF8(domain, type, DNS_QUERY_STANDARD, NULL,
910 			       &pRec, NULL);
911 	if (status != ERROR_SUCCESS)
912 	    return NULL;
913 
914 	r = calloc(1, sizeof(*r));
915 	r->q.domain = strdup(domain);
916 	r->q.type = type;
917 	r->q.class = 0;
918 
919 	r->head = parse_dns_record(pRec);
920 
921 	if (r->head == NULL) {
922 	    rk_dns_free_data(r);
923 	    return NULL;
924 	} else {
925 	    return r;
926 	}
927 
928     } __finally {
929 
930 	if (pRec)
931 	    DnsRecordListFree(pRec, DnsFreeRecordList);
932 
933     }
934 }
935 #endif	/* HAVE_WINDNS */
936 
937 #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */
938 
939 ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL
rk_dns_lookup(const char * domain,const char * type_name)940 rk_dns_lookup(const char *domain, const char *type_name)
941 {
942     return NULL;
943 }
944 
945 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_dns_free_data(struct rk_dns_reply * r)946 rk_dns_free_data(struct rk_dns_reply *r)
947 {
948 }
949 
950 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_dns_srv_order(struct rk_dns_reply * r)951 rk_dns_srv_order(struct rk_dns_reply *r)
952 {
953 }
954 
955 #endif
956