diff options
Diffstat (limited to 'mit-pthreads/net/res_internal.c')
-rw-r--r-- | mit-pthreads/net/res_internal.c | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/mit-pthreads/net/res_internal.c b/mit-pthreads/net/res_internal.c new file mode 100644 index 00000000000..4eab65bf5aa --- /dev/null +++ b/mit-pthreads/net/res_internal.c @@ -0,0 +1,576 @@ +/* + * Copyright (c) 1985 Regents of the University of California. + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +/*static char *sccsid = "from: @(#)res_internal.c 6.22 (Berkeley) 3/19/91";*/ +static char *rcsid = "$Id$"; +#endif /* LIBC_SCCS and not lint */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <resolv.h> +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include "res_internal.h" + +#define DEFAULT_RETRIES 4 + +pthread_mutex_t host_iterate_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_once_t init_once = PTHREAD_ONCE_INIT; +static pthread_key_t key; +static int init_status; + +static void _res_init_global(void); +static void set_options(const char *options, const char *source); +static pthread_ipaddr_type net_mask(struct in_addr in); +static int qcomp(const void *arg1, const void *arg2); + +static struct __res_state start; +/* We want to define _res for partial binary compatibility with libraries. */ +#undef _res +struct __res_state _res = { + RES_TIMEOUT, /* retransmition time interval */ + 4, /* number of times to retransmit */ + RES_DEFAULT, /* options flags */ + 1, /* number of name servers */ +}; + +struct hostent *_res_parse_answer(querybuf *answer, int anslen, int iquery, + struct hostent *result, char *buf, + int bufsize, int *errval) +{ + struct res_data *data = _res_init(); + register HEADER *hp; + register u_char *cp; + register int n; + u_char *eom; + char *aliases[__NETDB_MAXALIASES], *addrs[__NETDB_MAXADDRS]; + char *bp = buf, **ap = aliases, **hap = addrs; + int type, class, ancount, qdcount, getclass = C_ANY, iquery_done = 0; + + eom = answer->buf + anslen; + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->ancount); + qdcount = ntohs(hp->qdcount); + bp = buf; + cp = answer->buf + sizeof(HEADER); + + /* Read in the hostname if this is an address lookup. */ + if (qdcount) { + if (iquery) { + if ((n = dn_expand((u_char *) answer->buf, + (u_char *) eom, (u_char *) cp, (u_char *) bp, + bufsize - (bp - buf))) < 0) { + *errval = NO_RECOVERY; + return ((struct hostent *) NULL); + } + cp += n + QFIXEDSZ; + result->h_name = bp; + bp += strlen(bp) + 1; + } else { + cp += __dn_skipname(cp, eom) + QFIXEDSZ; + } + while (--qdcount > 0) + cp += __dn_skipname(cp, eom) + QFIXEDSZ; + } else if (iquery) { + *errval = (hp->aa) ? HOST_NOT_FOUND : TRY_AGAIN; + return ((struct hostent *) NULL); + } + + /* Read in the answers. */ + *ap = NULL; + *hap = NULL; + while (--ancount >= 0 && cp < eom) { + if ((n = dn_expand((u_char *) answer->buf, (u_char *) eom, + (u_char *) cp, (u_char *) bp, + bufsize - (bp - buf))) < 0) + break; + cp += n; + type = _getshort(cp); + cp += sizeof(u_short); + class = _getshort(cp); + cp += sizeof(u_short) + sizeof(pthread_ipaddr_type); + n = _getshort(cp); + cp += sizeof(u_short); + if (type == T_CNAME) { + cp += n; + if (ap >= aliases + __NETDB_MAXALIASES - 1) + continue; + *ap++ = bp; + bp += strlen(bp) + 1; + continue; + } + if (iquery && type == T_PTR) { + if ((n = dn_expand((u_char *) answer->buf, (u_char *) eom, + (u_char *) cp, (u_char *) bp, + bufsize - (bp - buf))) < 0) + break; + cp += n; + result->h_name = bp; + bp += strlen(bp) + 1; + iquery_done = 1; + break; + } + if (iquery || type != T_A) { +#ifdef DEBUG_RESOLVER + if (data->state.options & RES_DEBUG) + printf("unexpected answer type %d, size %d\n", + type, n); +#endif + cp += n; + continue; + } + if (hap > addrs) { + if (n != result->h_length) { + cp += n; + continue; + } + if (class != getclass) { + cp += n; + continue; + } + } else { + result->h_length = n; + getclass = class; + result->h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC; + if (!iquery) { + result->h_name = bp; + bp += strlen(bp) + 1; + } + } + bp = ALIGN(bp, pthread_ipaddr_type); + if (bp + n >= buf + bufsize) { + errno = ERANGE; + return NULL; + } + memcpy(bp, cp, n); + cp += n; + if (hap >= addrs + __NETDB_MAXADDRS - 1) + continue; + *hap++ = bp; + bp += n; + cp += n; + } + + if (hap > addrs || iquery_done) { + *ap++ = NULL; + *hap++ = NULL; + if (data->state.nsort) + qsort(addrs, hap - addrs, sizeof(struct in_addr), qcomp); + if (SP(bp, char *, (hap - addrs) + (ap - aliases)) > buf + bufsize) { + errno = ERANGE; + return NULL; + } + result->h_addr_list = (char **) ALIGN(bp, char *); + memcpy(result->h_addr_list, addrs, (hap - addrs) * sizeof(char *)); + result->h_aliases = result->h_addr_list + (hap - addrs); + memcpy(result->h_aliases, aliases, (ap - aliases) * sizeof(char *)); + return result; + } else { + *errval = TRY_AGAIN; + return NULL; + } +} + +/* Performs global initialization. */ +struct res_data *_res_init() +{ + struct res_data *data; + + /* Make sure the global initializations have been done. */ + pthread_once(&init_once, _res_init_global); + if (init_status < 0) + return NULL; + + /* Initialize thread-specific data for this thread if it hasn't + * been done already. */ + data = (struct res_data *) pthread_getspecific(key); + if (!data) { + data = (struct res_data *) malloc(sizeof(struct res_data)); + if (data == NULL) + return NULL; + if (pthread_setspecific(key, data) < 0) { + free(data); + return NULL; + } + data->buf = NULL; + data->state = start; + data->errval = NO_RECOVERY; + data->sock = -1; + } + return data; +} + +static void _res_init_global() +{ + int result; + char line[BUFSIZ], buf[BUFSIZ], *domain, *p, *net; + int i, localdomain_set = 0, num_servers = 0, num_sorts = 0; + FILE *fp; + struct in_addr addr; + + /* Assume an error state until we finish. */ + init_status = -1; + + /* Initialize the key for thread-specific data. */ + result = pthread_key_create(&key, free); + if (result < 0) + return; + + /* Initialize starting state. */ + start.retrans = RES_TIMEOUT; + start.retry = DEFAULT_RETRIES; + start.options = RES_DEFAULT; + start.id = 0; + start.nscount = 1; + start.nsaddr.sin_addr.s_addr = INADDR_ANY; + start.nsaddr.sin_family = AF_INET; + start.nsaddr.sin_port = htons(NAMESERVER_PORT); + start.nscount = 1; + start.ndots = 1; + start.pfcode = 0; + strncpy(start.lookups, "f", sizeof(start.lookups)); + + /* Look for a LOCALDOMAIN definition. */ + domain = getenv("LOCALDOMAIN"); + if (domain != NULL) { + strncpy(start.defdname, domain, sizeof(start.defdname)); + domain = start.defdname; + localdomain_set = 1; + + /* Construct a search path from the LOCALDOMAIN value, which is + * a space-separated list of strings. For backwards-compatibility, + * a newline terminates the list. */ + i = 0; + while (*domain && i < MAXDNSRCH) { + start.dnsrch[i] = domain; + while (*domain && !isspace(*domain)) + domain++; + if (!*domain || *domain == '\n') { + *domain = 0; + break; + } + *domain++ = 0; + while (isspace(*domain)) + domain++; + i++; + } + } + + /* Look for a config file and read it in. */ + fp = fopen(_PATH_RESCONF, "r"); + if (fp != NULL) { + strncpy(start.lookups, "bf", sizeof(start.lookups)); + + /* Read in the configuration file. */ + while (fgets(line, sizeof(line), fp)) { + + /* Ignore blank lines and comments. */ + if (*line == ';' || *line == '#' || !*line) + continue; + + if (strncmp(line, "domain", 6) == 0) { + + /* Read in the default domain, and initialize a one- + * element search path. Skip the domain line if we + * already got one from the LOCALDOMAIN environment + * variable. */ + if (localdomain_set) + continue; + + /* Look for the next word in the line. */ + p = line + 6; + while (*p == ' ' || *p == '\t') + p++; + if (!*p || *p == '\n') + continue; + + /* Copy in the domain, and null-terminate it at the + * first tab or newline. */ + strncpy(start.defdname, p, sizeof(start.defdname) - 1); + p = strpbrk(start.defdname, "\t\n"); + if (p) + *p = 0; + + start.dnsrch[0] = start.defdname; + start.dnsrch[1] = NULL; + + } else if (strncmp(line, "lookup", 6) == 0) { + + /* Get a list of lookup types. */ + memset(start.lookups, 0, sizeof(start.lookups)); + + /* Find the next word in the line. */ + p = line + 6; + while (isspace(*p)) + p++; + + i = 0; + while (*p && i < MAXDNSLUS) { + /* Add a lookup type. */ + if (*p == 'y' || *p == 'b' || *p == 'f') + start.lookups[i++] = *p; + + /* Find the next word. */ + while (*p && !isspace(*p)) + p++; + while (isspace(*p)) + p++; + } + + } else if (strncmp(line, "search", 6) == 0) { + + /* Read in a space-separated list of domains to search + * when a name is not fully-qualified. Skip this line + * if the LOCALDOMAIN environment variable was set. */ + if (localdomain_set) + continue; + + /* Look for the next word on the line. */ + p = line + 6; + while (*p == ' ' || *p == '\t') + p++; + if (!*p || *p == '\n') + continue; + + /* Copy the rest of the line into start.defdname. */ + strncpy(start.defdname, p, sizeof(start.defdname) - 1); + domain = start.defdname; + p = strchr(domain, '\n'); + if (*p) + *p = 0; + + /* Construct a search path from the line, which is a + * space-separated list of strings. */ + i = 0; + while (*domain && i < MAXDNSRCH) { + start.dnsrch[i] = domain; + while (*domain && !isspace(*domain)) + domain++; + if (!*domain || *domain == '\n') { + *domain = 0; + break; + } + *domain++ = 0; + while (isspace(*domain)) + domain++; + i++; + } + + } else if (strncmp(line, "nameserver", 10) == 0) { + + /* Add an address to the list of name servers we can + * connect to. */ + + /* Look for the next word in the line. */ + p = line + 10; + while (*p == ' ' || *p == '\t') + p++; + if (*p && *p != '\n' && inet_aton(p, &addr)) { + start.nsaddr_list[num_servers].sin_addr = addr; + start.nsaddr_list[num_servers].sin_family = AF_INET; + start.nsaddr_list[num_servers].sin_port = + htons(NAMESERVER_PORT); + if (++num_servers >= MAXNS) + break; + } + + } else if (strncmp(line, "sortlist", 8) == 0) { + + p = line + 8; + while (num_sorts < MAXRESOLVSORT) { + + /* Find the next word in the line. */ + p = line + 8; + while (*p == ' ' || *p == '\t') + p++; + + /* Read in an IP address and netmask. */ + if (sscanf(p, "%[0-9./]s", buf) != 1) + break; + net = strchr(buf, '/'); + if (net) + *net = 0; + + /* Translate the address into an IP address + * and netmask. */ + if (inet_aton(buf, &addr)) { + start.sort_list[num_sorts].addr = addr; + if (net && inet_aton(net + 1, &addr)) { + start.sort_list[num_sorts].mask = addr.s_addr; + } else { + start.sort_list[num_sorts].mask = + net_mask(start.sort_list[num_sorts].addr); + } + num_sorts++; + } + + /* Skip past this word. */ + if (net) + *net = '/'; + p += strlen(buf); + } + + } + } + fclose(fp); + } + + /* If we don't have a default domain, strip off the first + * component of this machine's domain name, and make a one- + * element search path consisting of the default domain. */ + if (*start.defdname == 0) { + if (gethostname(buf, sizeof(start.defdname) - 1) == 0) { + p = strchr(buf, '.'); + if (p) + strcpy(start.defdname, p + 1); + } + start.dnsrch[0] = start.defdname; + start.dnsrch[1] = NULL; + } + + p = getenv("RES_OPTIONS"); + if (p) + set_options(p, "env"); + + start.options |= RES_INIT; + _res = start; + init_status = 0; +} + +static void set_options(const char *options, const char *source) +{ + const char *p = options; + int i; + + while (*p) { + + /* Skip leading and inner runs of spaces. */ + while (*p == ' ' || *p == '\t') + p++; + + /* Search for and process individual options. */ + if (strncmp(p, "ndots:", 6) == 0) { + i = atoi(p + 6); + start.ndots = (i <= RES_MAXNDOTS) ? i : RES_MAXNDOTS; + } else if (!strncmp(p, "debug", 5)) + start.options |= RES_DEBUG; + else if (!strncmp(p, "usevc", 5)) + start.options |= RES_USEVC; + else if (!strncmp(p, "stayopen", 8)) + start.options |= RES_STAYOPEN; + + /* Skip to next run of spaces */ + while (*p && *p != ' ' && *p != '\t') + p++; + } +} + +static pthread_ipaddr_type net_mask(struct in_addr in) +{ + pthread_ipaddr_type i = ntohl(in.s_addr); + + if (IN_CLASSA(i)) + return htonl(IN_CLASSA_NET); + if (IN_CLASSB(i)) + return htonl(IN_CLASSB_NET); + return htonl(IN_CLASSC_NET); +} + +/* Get the error value for this thread, or NO_RECOVERY if none has been + * successfully set. The screw case to worry about here is if + * __res_init() fails for a resolver routine because it can't allocate + * or set the thread-specific data, and then __res_init() succeeds here. + * Because __res_init() sets errval to NO_RECOVERY after a successful + * initialization, we return NO_RECOVERY in that case, which is correct. */ +int _res_get_error() +{ + struct res_data *data; + + data = _res_init(); + return (data) ? data->errval : NO_RECOVERY; +} + +struct __res_state *_res_status() +{ + struct res_data *data; + + data = _res_init(); + return (data) ? &data->state : NULL; +} + +static int qcomp(const void *arg1, const void *arg2) +{ + const struct in_addr **a1 = (const struct in_addr **) arg1; + const struct in_addr **a2 = (const struct in_addr **) arg2; + struct __res_state *state = _res_status(); + + int pos1, pos2; + + for (pos1 = 0; pos1 < state->nsort; pos1++) { + if (state->sort_list[pos1].addr.s_addr == + ((*a1)->s_addr & state->sort_list[pos1].mask)) + break; + } + for (pos2 = 0; pos2 < state->nsort; pos2++) { + if (state->sort_list[pos2].addr.s_addr == + ((*a2)->s_addr & state->sort_list[pos2].mask)) + break; + } + return pos1 - pos2; +} + +/* + * This routine is for closing the socket if a virtual circuit is used and + * the program wants to close it. We don't use this routine, but libc + * might reference it. + * + * This routine is not expected to be user visible. + */ +void _res_close() +{ + struct res_data *data; + + data = _res_init(); + if (data && data->sock != -1) { + (void) close(data->sock); + data->sock = -1; + } +} |