Home | History | Annotate | Line # | Download | only in roken
      1 /*	$NetBSD: resolve.c,v 1.6 2023/06/19 21:41:45 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995 - 2006 Kungliga Tekniska Hgskolan
      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
     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
     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
    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
    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
    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*
    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 *
    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
    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
    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
    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 *
    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
    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
    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
    946 rk_dns_free_data(struct rk_dns_reply *r)
    947 {
    948 }
    949 
    950 ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
    951 rk_dns_srv_order(struct rk_dns_reply *r)
    952 {
    953 }
    954 
    955 #endif
    956