diff options
author | stas <stas@FreeBSD.org> | 2011-10-05 07:23:29 +0000 |
---|---|---|
committer | stas <stas@FreeBSD.org> | 2011-10-05 07:23:29 +0000 |
commit | 33f661cecdad3182ee66b47805fa4bb212e0da6c (patch) | |
tree | 57a48e7e9b592f2d5b713e80a4455820625c2b7b /lib/roken/resolve.c | |
parent | 100a21d381c0fe86cfacf55ea7b18ec0bd2322bf (diff) | |
download | FreeBSD-src-33f661cecdad3182ee66b47805fa4bb212e0da6c.zip FreeBSD-src-33f661cecdad3182ee66b47805fa4bb212e0da6c.tar.gz |
- Import Heimdal 1.5 distribution.
Diffstat (limited to 'lib/roken/resolve.c')
-rw-r--r-- | lib/roken/resolve.c | 462 |
1 files changed, 347 insertions, 115 deletions
diff --git a/lib/roken/resolve.c b/lib/roken/resolve.c index 8f8fec7..b27f37a 100644 --- a/lib/roken/resolve.c +++ b/lib/roken/resolve.c @@ -1,23 +1,23 @@ /* - * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan + * Copyright (c) 1995 - 2006 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: - * + * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * + * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -31,9 +31,9 @@ * SUCH DAMAGE. */ -#ifdef HAVE_CONFIG_H + #include <config.h> -#endif + #include "roken.h" #ifdef HAVE_ARPA_NAMESER_H #include <arpa/nameser.h> @@ -41,12 +41,13 @@ #ifdef HAVE_RESOLV_H #include <resolv.h> #endif +#ifdef HAVE_DNS_H +#include <dns.h> +#endif #include "resolve.h" #include <assert.h> -RCSID("$Id: resolve.c 19869 2007-01-12 16:03:14Z lha $"); - #ifdef _AIX /* AIX have broken res_nsearch() in 5.1 (5.0 also ?) */ #undef HAVE_RES_NSEARCH #endif @@ -77,8 +78,8 @@ static struct stot{ int _resolve_debug = 0; -int ROKEN_LIB_FUNCTION -dns_string_to_type(const char *name) +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +rk_dns_string_to_type(const char *name) { struct stot *p = stot; for(p = stot; p->name; p++) @@ -87,8 +88,8 @@ dns_string_to_type(const char *name) return -1; } -const char * ROKEN_LIB_FUNCTION -dns_type_to_string(int type) +ROKEN_LIB_FUNCTION const char * ROKEN_LIB_CALL +rk_dns_type_to_string(int type) { struct stot *p = stot; for(p = stot; p->name; p++) @@ -97,10 +98,10 @@ dns_type_to_string(int type) return NULL; } -#if (defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND) +#if ((defined(HAVE_RES_SEARCH) || defined(HAVE_RES_NSEARCH)) && defined(HAVE_DN_EXPAND)) || defined(HAVE_WINDNS) static void -dns_free_rr(struct resource_record *rr) +dns_free_rr(struct rk_resource_record *rr) { if(rr->domain) free(rr->domain); @@ -109,26 +110,29 @@ dns_free_rr(struct resource_record *rr) free(rr); } -void ROKEN_LIB_FUNCTION -dns_free_data(struct dns_reply *r) +ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL +rk_dns_free_data(struct rk_dns_reply *r) { - struct resource_record *rr; + struct rk_resource_record *rr; if(r->q.domain) free(r->q.domain); for(rr = r->head; rr;){ - struct resource_record *tmp = rr; + struct rk_resource_record *tmp = rr; rr = rr->next; dns_free_rr(tmp); } free (r); } +#ifndef HAVE_WINDNS + static int -parse_record(const unsigned char *data, const unsigned char *end_data, - const unsigned char **pp, struct resource_record **ret_rr) +parse_record(const unsigned char *data, const unsigned char *end_data, + const unsigned char **pp, struct rk_resource_record **ret_rr) { - struct resource_record *rr; - int type, class, ttl, size; + struct rk_resource_record *rr; + int type, class, ttl; + unsigned size; int status; char host[MAXDNAME]; const unsigned char *p = *pp; @@ -136,7 +140,7 @@ parse_record(const unsigned char *data, const unsigned char *end_data, *ret_rr = NULL; status = dn_expand(data, end_data, p, host, sizeof(host)); - if(status < 0) + if(status < 0) return -1; if (p + status + 10 > end_data) return -1; @@ -155,7 +159,7 @@ parse_record(const unsigned char *data, const unsigned char *end_data, return -1; rr = calloc(1, sizeof(*rr)); - if(rr == NULL) + if(rr == NULL) return -1; rr->domain = strdup(host); if(rr->domain == NULL) { @@ -190,13 +194,13 @@ parse_record(const unsigned char *data, const unsigned char *end_data, dns_free_rr(rr); return -1; } - if (status + 2 > size) { + if ((size_t)status + 2 > size) { dns_free_rr(rr); return -1; } hostlen = strlen(host); - rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) + + rr->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) + hostlen); if(rr->u.mx == NULL) { dns_free_rr(rr); @@ -213,14 +217,14 @@ parse_record(const unsigned char *data, const unsigned char *end_data, dns_free_rr(rr); return -1; } - if (status + 6 > size) { + if ((size_t)status + 6 > size) { dns_free_rr(rr); return -1; } hostlen = strlen(host); - rr->u.srv = - (struct srv_record*)malloc(sizeof(struct srv_record) + + rr->u.srv = + (struct srv_record*)malloc(sizeof(struct srv_record) + hostlen); if(rr->u.srv == NULL) { dns_free_rr(rr); @@ -233,7 +237,7 @@ parse_record(const unsigned char *data, const unsigned char *end_data, break; } case rk_ns_t_txt:{ - if(size == 0 || size < *p + 1) { + if(size == 0 || size < (unsigned)(*p + 1)) { dns_free_rr(rr); return -1; } @@ -280,7 +284,7 @@ parse_record(const unsigned char *data, const unsigned char *end_data, dns_free_rr(rr); return -1; } - if (status + 18 > size) { + if ((size_t)status + 18 > size) { dns_free_rr(rr); return -1; } @@ -400,17 +404,17 @@ parse_record(const unsigned char *data, const unsigned char *end_data, #ifndef TEST_RESOLVE static #endif -struct dns_reply* +struct rk_dns_reply* parse_reply(const unsigned char *data, size_t len) { const unsigned char *p; int status; - int i; + size_t i; char host[MAXDNAME]; const unsigned char *end_data = data + len; - struct dns_reply *r; - struct resource_record **rr; - + struct rk_dns_reply *r; + struct rk_resource_record **rr; + r = calloc(1, sizeof(*r)); if (r == NULL) return NULL; @@ -448,16 +452,16 @@ parse_reply(const unsigned char *data, size_t len) } status = dn_expand(data, end_data, p, host, sizeof(host)); if(status < 0){ - dns_free_data(r); + rk_dns_free_data(r); return NULL; } r->q.domain = strdup(host); if(r->q.domain == NULL) { - dns_free_data(r); + rk_dns_free_data(r); return NULL; } if (p + status + 4 > end_data) { - dns_free_data(r); + rk_dns_free_data(r); return NULL; } p += status; @@ -465,25 +469,25 @@ parse_reply(const unsigned char *data, size_t len) p += 2; r->q.class = (p[0] << 8 | p[1]); p += 2; - + rr = &r->head; for(i = 0; i < r->h.ancount; i++) { if(parse_record(data, end_data, &p, rr) != 0) { - dns_free_data(r); + rk_dns_free_data(r); return NULL; } rr = &(*rr)->next; } for(i = 0; i < r->h.nscount; i++) { if(parse_record(data, end_data, &p, rr) != 0) { - dns_free_data(r); + rk_dns_free_data(r); return NULL; } rr = &(*rr)->next; } for(i = 0; i < r->h.arcount; i++) { if(parse_record(data, end_data, &p, rr) != 0) { - dns_free_data(r); + rk_dns_free_data(r); return NULL; } rr = &(*rr)->next; @@ -500,71 +504,87 @@ parse_reply(const unsigned char *data, size_t len) #endif #endif -static struct dns_reply * +#if defined(HAVE_DNS_SEARCH) +#define resolve_search(h,n,c,t,r,l) \ + ((int)dns_search(h,n,c,t,r,l,(struct sockaddr *)&from,&fromsize)) +#define resolve_free_handle(h) dns_free(h) +#elif defined(HAVE_RES_NSEARCH) +#define resolve_search(h,n,c,t,r,l) res_nsearch(h,n,c,t,r,l) +#define resolve_free_handle(h) rk_res_free(h); +#else +#define resolve_search(h,n,c,t,r,l) res_search(n,c,t,r,l) +#define handle 0 +#define resolve_free_handle(h) +#endif + + +static struct rk_dns_reply * dns_lookup_int(const char *domain, int rr_class, int rr_type) { - struct dns_reply *r; - unsigned char *reply = NULL; - int size; - int len; -#ifdef HAVE_RES_NSEARCH + struct rk_dns_reply *r; + void *reply = NULL; + int size, len; +#if defined(HAVE_DNS_SEARCH) + struct sockaddr_storage from; + uint32_t fromsize = sizeof(from); + dns_handle_t handle; + + handle = dns_open(NULL); + if (handle == NULL) + return NULL; +#elif defined(HAVE_RES_NSEARCH) struct __res_state state; + struct __res_state *handle = &state; + memset(&state, 0, sizeof(state)); - if(res_ninit(&state)) + if(res_ninit(handle)) return NULL; /* is this the best we can do? */ -#elif defined(HAVE__RES) - u_long old_options = 0; #endif - - size = 0; - len = 1000; - do { + + len = 1500; + while(1) { if (reply) { free(reply); reply = NULL; } - if (size <= len) - size = len; if (_resolve_debug) { -#ifdef HAVE_RES_NSEARCH +#if defined(HAVE_DNS_SEARCH) + dns_set_debug(handle, 1); +#elif defined(HAVE_RES_NSEARCH) state.options |= RES_DEBUG; -#elif defined(HAVE__RES) - old_options = _res.options; - _res.options |= RES_DEBUG; #endif fprintf(stderr, "dns_lookup(%s, %d, %s), buffer size %d\n", domain, - rr_class, dns_type_to_string(rr_type), size); + rr_class, rk_dns_type_to_string(rr_type), len); } - reply = malloc(size); + reply = malloc(len); if (reply == NULL) { -#ifdef HAVE_RES_NSEARCH - rk_res_free(&state); -#endif + resolve_free_handle(handle); return NULL; } -#ifdef HAVE_RES_NSEARCH - len = res_nsearch(&state, domain, rr_class, rr_type, reply, size); -#else - len = res_search(domain, rr_class, rr_type, reply, size); -#endif + + size = resolve_search(handle, domain, rr_class, rr_type, reply, len); + if (_resolve_debug) { -#if defined(HAVE__RES) && !defined(HAVE_RES_NSEARCH) - _res.options = old_options; -#endif fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n", - domain, rr_class, dns_type_to_string(rr_type), len); - } - if (len < 0) { -#ifdef HAVE_RES_NSEARCH - rk_res_free(&state); -#endif + domain, rr_class, rk_dns_type_to_string(rr_type), size); + } + if (size > len) { + /* resolver thinks it know better, go for it */ + len = size; + } else if (size > 0) { + /* got a good reply */ + break; + } else if (size <= 0 && len < rk_DNS_MAX_PACKET_SIZE) { + len *= 2; + if (len > rk_DNS_MAX_PACKET_SIZE) + len = rk_DNS_MAX_PACKET_SIZE; + } else { + /* the end, leave */ + resolve_free_handle(handle); free(reply); return NULL; } - } while (size < len && len < rk_DNS_MAX_PACKET_SIZE); -#ifdef HAVE_RES_NSEARCH - rk_res_free(&state); -#endif + } len = min(len, size); r = parse_reply(reply, len); @@ -572,41 +592,39 @@ dns_lookup_int(const char *domain, int rr_class, int rr_type) return r; } -struct dns_reply * ROKEN_LIB_FUNCTION -dns_lookup(const char *domain, const char *type_name) +ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL +rk_dns_lookup(const char *domain, const char *type_name) { int type; - - type = dns_string_to_type(type_name); + + type = rk_dns_string_to_type(type_name); if(type == -1) { if(_resolve_debug) - fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n", + fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n", type_name); return NULL; } - return dns_lookup_int(domain, C_IN, type); + return dns_lookup_int(domain, rk_ns_c_in, type); } +#endif /* !HAVE_WINDNS */ + static int compare_srv(const void *a, const void *b) { - const struct resource_record *const* aa = a, *const* bb = b; + const struct rk_resource_record *const* aa = a, *const* bb = b; if((*aa)->u.srv->priority == (*bb)->u.srv->priority) return ((*aa)->u.srv->weight - (*bb)->u.srv->weight); return ((*aa)->u.srv->priority - (*bb)->u.srv->priority); } -#ifndef HAVE_RANDOM -#define random() rand() -#endif - /* try to rearrange the srv-records by the algorithm in RFC2782 */ -void ROKEN_LIB_FUNCTION -dns_srv_order(struct dns_reply *r) +ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL +rk_dns_srv_order(struct rk_dns_reply *r) { - struct resource_record **srvs, **ss, **headp; - struct resource_record *rr; + struct rk_resource_record **srvs, **ss, **headp; + struct rk_resource_record *rr; int num_srv = 0; #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) @@ -614,7 +632,9 @@ dns_srv_order(struct dns_reply *r) char *oldstate; #endif - for(rr = r->head; rr; rr = rr->next) + rk_random_init(); + + for(rr = r->head; rr; rr = rr->next) if(rr->type == rk_ns_t_srv) num_srv++; @@ -624,7 +644,7 @@ dns_srv_order(struct dns_reply *r) srvs = malloc(num_srv * sizeof(*srvs)); if(srvs == NULL) return; /* XXX not much to do here */ - + /* unlink all srv-records from the linked list and put them in a vector */ for(ss = srvs, headp = &r->head; *headp; ) @@ -635,7 +655,7 @@ dns_srv_order(struct dns_reply *r) ss++; } else headp = &(*headp)->next; - + /* sort them by priority and weight */ qsort(srvs, num_srv, sizeof(*srvs), compare_srv); @@ -644,10 +664,10 @@ dns_srv_order(struct dns_reply *r) #endif headp = &r->head; - + for(ss = srvs; ss < srvs + num_srv; ) { int sum, rnd, count; - struct resource_record **ee, **tt; + struct rk_resource_record **ee, **tt; /* find the last record with the same priority and count the sum of all weights */ for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) { @@ -660,7 +680,7 @@ dns_srv_order(struct dns_reply *r) /* ss is now the first record of this priority and ee is the first of the next */ while(ss < ee) { - rnd = random() % (sum + 1); + rnd = rk_random() % (sum + 1); for(count = 0, tt = ss; ; tt++) { if(*tt == NULL) continue; @@ -682,7 +702,7 @@ dns_srv_order(struct dns_reply *r) ss++; } } - + #if defined(HAVE_INITSTATE) && defined(HAVE_SETSTATE) setstate(oldstate); #endif @@ -690,21 +710,233 @@ dns_srv_order(struct dns_reply *r) return; } +#ifdef HAVE_WINDNS + +#include <WinDNS.h> + +static struct rk_resource_record * +parse_dns_record(PDNS_RECORD pRec) +{ + struct rk_resource_record * rr; + + if (pRec == NULL) + return NULL; + + rr = calloc(1, sizeof(*rr)); + + rr->domain = strdup(pRec->pName); + rr->type = pRec->wType; + rr->class = 0; + rr->ttl = pRec->dwTtl; + rr->size = 0; + + switch (rr->type) { + case rk_ns_t_ns: + case rk_ns_t_cname: + case rk_ns_t_ptr: + rr->u.txt = strdup(pRec->Data.NS.pNameHost); + if(rr->u.txt == NULL) { + dns_free_rr(rr); + return NULL; + } + break; + + case rk_ns_t_mx: + case rk_ns_t_afsdb:{ + size_t hostlen = strnlen(pRec->Data.MX.pNameExchange, DNS_MAX_NAME_LENGTH); + + rr->u.mx = (struct mx_record *)malloc(sizeof(struct mx_record) + + hostlen); + if (rr->u.mx == NULL) { + dns_free_rr(rr); + return NULL; + } + + strcpy_s(rr->u.mx->domain, hostlen + 1, pRec->Data.MX.pNameExchange); + rr->u.mx->preference = pRec->Data.MX.wPreference; + break; + } + + case rk_ns_t_srv:{ + size_t hostlen = strnlen(pRec->Data.SRV.pNameTarget, DNS_MAX_NAME_LENGTH); + + rr->u.srv = + (struct srv_record*)malloc(sizeof(struct srv_record) + + hostlen); + if(rr->u.srv == NULL) { + dns_free_rr(rr); + return NULL; + } + + rr->u.srv->priority = pRec->Data.SRV.wPriority; + rr->u.srv->weight = pRec->Data.SRV.wWeight; + rr->u.srv->port = pRec->Data.SRV.wPort; + strcpy_s(rr->u.srv->target, hostlen + 1, pRec->Data.SRV.pNameTarget); + + break; + } + + case rk_ns_t_txt:{ + size_t len; + + if (pRec->Data.TXT.dwStringCount == 0) { + rr->u.txt = strdup(""); + break; + } + + len = strnlen(pRec->Data.TXT.pStringArray[0], DNS_MAX_TEXT_STRING_LENGTH); + + rr->u.txt = (char *)malloc(len + 1); + strcpy_s(rr->u.txt, len + 1, pRec->Data.TXT.pStringArray[0]); + + break; + } + + case rk_ns_t_key : { + size_t key_len; + + if (pRec->wDataLength < 4) { + dns_free_rr(rr); + return NULL; + } + + key_len = pRec->wDataLength - 4; + rr->u.key = malloc (sizeof(*rr->u.key) + key_len - 1); + if (rr->u.key == NULL) { + dns_free_rr(rr); + return NULL; + } + + rr->u.key->flags = pRec->Data.KEY.wFlags; + rr->u.key->protocol = pRec->Data.KEY.chProtocol; + rr->u.key->algorithm = pRec->Data.KEY.chAlgorithm; + rr->u.key->key_len = key_len; + memcpy_s (rr->u.key->key_data, key_len, + pRec->Data.KEY.Key, key_len); + break; + } + + case rk_ns_t_sig : { + size_t sig_len, hostlen; + + if(pRec->wDataLength <= 18) { + dns_free_rr(rr); + return NULL; + } + + sig_len = pRec->wDataLength; + + hostlen = strnlen(pRec->Data.SIG.pNameSigner, DNS_MAX_NAME_LENGTH); + + rr->u.sig = malloc(sizeof(*rr->u.sig) + + hostlen + sig_len); + if (rr->u.sig == NULL) { + dns_free_rr(rr); + return NULL; + } + rr->u.sig->type = pRec->Data.SIG.wTypeCovered; + rr->u.sig->algorithm = pRec->Data.SIG.chAlgorithm; + rr->u.sig->labels = pRec->Data.SIG.chLabelCount; + rr->u.sig->orig_ttl = pRec->Data.SIG.dwOriginalTtl; + rr->u.sig->sig_expiration = pRec->Data.SIG.dwExpiration; + rr->u.sig->sig_inception = pRec->Data.SIG.dwTimeSigned; + rr->u.sig->key_tag = pRec->Data.SIG.wKeyTag; + rr->u.sig->sig_len = sig_len; + memcpy_s (rr->u.sig->sig_data, sig_len, + pRec->Data.SIG.Signature, sig_len); + rr->u.sig->signer = &rr->u.sig->sig_data[sig_len]; + strcpy_s(rr->u.sig->signer, hostlen + 1, pRec->Data.SIG.pNameSigner); + break; + } + +#ifdef DNS_TYPE_DS + case rk_ns_t_ds: { + rr->u.ds = malloc (sizeof(*rr->u.ds) + pRec->Data.DS.wDigestLength - 1); + if (rr->u.ds == NULL) { + dns_free_rr(rr); + return NULL; + } + + rr->u.ds->key_tag = pRec->Data.DS.wKeyTag; + rr->u.ds->algorithm = pRec->Data.DS.chAlgorithm; + rr->u.ds->digest_type = pRec->Data.DS.chDigestType; + rr->u.ds->digest_len = pRec->Data.DS.wDigestLength; + memcpy_s (rr->u.ds->digest_data, pRec->Data.DS.wDigestLength, + pRec->Data.DS.Digest, pRec->Data.DS.wDigestLength); + break; + } +#endif + + default: + dns_free_rr(rr); + return NULL; + } + + rr->next = parse_dns_record(pRec->pNext); + return rr; +} + +ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL +rk_dns_lookup(const char *domain, const char *type_name) +{ + DNS_STATUS status; + int type; + PDNS_RECORD pRec = NULL; + struct rk_dns_reply * r = NULL; + + __try { + + type = rk_dns_string_to_type(type_name); + if(type == -1) { + if(_resolve_debug) + fprintf(stderr, "dns_lookup: unknown resource type: `%s'\n", + type_name); + return NULL; + } + + status = DnsQuery_UTF8(domain, type, DNS_QUERY_STANDARD, NULL, + &pRec, NULL); + if (status != ERROR_SUCCESS) + return NULL; + + r = calloc(1, sizeof(*r)); + r->q.domain = strdup(domain); + r->q.type = type; + r->q.class = 0; + + r->head = parse_dns_record(pRec); + + if (r->head == NULL) { + rk_dns_free_data(r); + return NULL; + } else { + return r; + } + + } __finally { + + if (pRec) + DnsRecordListFree(pRec, DnsFreeRecordList); + + } +} +#endif /* HAVE_WINDNS */ + #else /* NOT defined(HAVE_RES_SEARCH) && defined(HAVE_DN_EXPAND) */ -struct dns_reply * ROKEN_LIB_FUNCTION -dns_lookup(const char *domain, const char *type_name) +ROKEN_LIB_FUNCTION struct rk_dns_reply * ROKEN_LIB_CALL +rk_dns_lookup(const char *domain, const char *type_name) { return NULL; } -void ROKEN_LIB_FUNCTION -dns_free_data(struct dns_reply *r) +ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL +rk_dns_free_data(struct rk_dns_reply *r) { } -void ROKEN_LIB_FUNCTION -dns_srv_order(struct dns_reply *r) +ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL +rk_dns_srv_order(struct rk_dns_reply *r) { } |