/* * 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: res_internal.c,v 1.1 1998/07/21 13:19:47 peter Exp $"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include #include #include #include #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; } }