/*- * SPDX-License-Identifier: (BSD-3-Clause AND ISC) * * Copyright (c) 1985, 1989, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 2026 Dag-Erling Smørgrav * * 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 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. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "port_before.h" #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "port_after.h" /* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */ #include #include "res_config.h" #include "res_private.h" static void res_readconf(res_state, const char *, int); static void res_setdefdname(res_state, const char *, const char *); static void res_setdnsrch(res_state, const char *, const char *); static int res_setnsaddr(res_state, int, const char *, const char *); static void res_setoptions(res_state, const char *, const char *); #ifdef RESOLVSORT static int res_setsortlist(res_state, int, const char *, const char *); #endif /* RESOLVSORT */ #define res_space(ch) \ isspace((unsigned char)(ch)) #define res_comment(ch) \ ((ch) == '#' || (ch) == ';') #define res_match(str, end, word) \ (strncmp((str), (word), (end) - (str)) == 0 && \ (word)[(end) - (str)] == '\0') /* * Resolver state default settings. */ /*% * Set up default settings. If the configuration file exist, the values * there will have precedence. Otherwise, the server address is set to * the loopback address the default domain name comes from gethostname(). * * The configuration file should always be used, since it is the only way * to specify options and a default domain. If you are running a server * on your local machine, you should say "nameserver 127.0.0.1" or * "nameserver ::1" in the configuration file. * * Return 0 if completes successfully, -1 on error */ int res_ninit(res_state statp) { return (__res_vinit(statp, 0)); } /*% This function has to be reachable by res_data.c but not publicly. */ int __res_vinit(res_state statp, int preinit) { char buf[NI_MAXHOST]; union res_sockaddr_union u[] = { { .sin = { .sin_family = AF_INET, #ifdef HAVE_SA_LEN .sin_len = sizeof(struct sockaddr_in), #endif .sin_port = htons(NAMESERVER_PORT), .sin_addr = { htonl(INADDR_LOOPBACK) }, } }, { .sin6 = { .sin6_family = AF_INET6, #ifdef HAVE_SA_LEN .sin6_len = sizeof(struct sockaddr_in6), #endif .sin6_port = htons(NAMESERVER_PORT), .sin6_addr = IN6ADDR_LOOPBACK_INIT, } }, }; char *cp, **pp; int dots; int maxns = MAXNS; RES_SET_H_ERRNO(statp, 0); if (statp->_u._ext.ext != NULL) res_ndestroy(statp); if (!preinit) { statp->retrans = RES_TIMEOUT; statp->retry = RES_DFLRETRY; statp->options = RES_DEFAULT; } statp->id = res_nrandomid(statp); statp->nscount = 0; statp->ndots = 1; statp->pfcode = 0; statp->_vcsock = -1; statp->_flags = 0; statp->qhook = NULL; statp->rhook = NULL; statp->_u._ext.nscount = 0; statp->_u._ext.ext = malloc(sizeof(*statp->_u._ext.ext)); if (statp->_u._ext.ext != NULL) { memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext)); statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr; strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa"); strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int"); statp->_u._ext.ext->reload_period = 2; } else { /* * Historically res_init() rarely, if at all, failed. * Examples and applications exist which do not check * our return code. Furthermore several applications * simply call us to get the systems domainname. So * rather then immediately fail here we store the * failure, which is returned later, in h_errno. And * prevent the collection of 'nameserver' information * by setting maxns to 0. Thus applications that fail * to check our return code wont be able to make * queries anyhow. */ RES_SET_H_ERRNO(statp, NETDB_INTERNAL); maxns = 0; } #ifdef RESOLVSORT statp->nsort = 0; #endif res_setservers(statp, u, nitems(u)); /* read the configuration file */ res_readconf(statp, _PATH_RESCONF, maxns); /* Allow user to override the local domain definition */ if ((cp = secure_getenv("LOCALDOMAIN")) != NULL) { while (res_space(*cp)) cp++; res_setdnsrch(statp, cp, strchr(cp, '\0')); } /* guess default domain if not set */ if (statp->defdname[0] == 0 && gethostname(buf, sizeof(statp->defdname) - 1) == 0 && (cp = strchr(buf, '.')) != NULL) strcpy(statp->defdname, cp + 1); /* find components of local domain that might be searched */ if (statp->dnsrch[0] == NULL) { pp = statp->dnsrch; *pp++ = statp->defdname; *pp = NULL; dots = 0; for (cp = statp->defdname; *cp; cp++) dots += (*cp == '.'); cp = statp->defdname; while (pp < statp->dnsrch + MAXDFLSRCH) { if (dots < LOCALDOMAINPARTS) break; cp = strchr(cp, '.') + 1; /*%< we know there is one */ *pp++ = cp; dots--; } *pp = NULL; #ifdef DEBUG if (statp->options & RES_DEBUG) { printf(";; res_init()... default dnsrch list:\n"); for (pp = statp->dnsrch; *pp; pp++) printf(";;\t%s\n", *pp); printf(";;\t..END..\n"); } #endif } if (issetugid()) statp->options |= RES_NOALIASES; else if ((cp = getenv("RES_OPTIONS")) != NULL) res_setoptions(statp, cp, "env"); statp->options |= RES_INIT; return (statp->res_h_errno); } /*% * Read the resolver configuration file. */ static void res_readconf(res_state statp, const char *resconf, int maxns) { struct stat sb; struct timespec now; FILE *fp; char *line = NULL, *eol, *eow; char *cp; ssize_t len; size_t sz = 0; int nserv = 0; #ifdef RESOLVSORT int nsort = 0; #endif if ((fp = fopen(resconf, "re")) == NULL) return; if (statp->_u._ext.ext != NULL) { if (_fstat(fileno(fp), &sb) == 0) { statp->_u._ext.ext->conf_mtim = sb.st_mtim; if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == 0) { statp->_u._ext.ext->conf_stat = now.tv_sec; } } } while ((len = getline(&line, &sz, fp)) != -1) { /* search for comment or end of line */ eol = line; while (eol < line + len && !res_comment(*eol)) eol++; /* now backtrack over whitespace */ while (eol > line && res_space(eol[-1])) eol--; *eol = '\0'; /* skip blank line */ if (eol == line) continue; /* isolate the first word */ eow = line; while (eow < eol && !res_space(*eow)) eow++; /* read default domain name */ if (res_match(line, eow, "domain")) { cp = eow; while (cp < eol && res_space(*cp)) cp++; res_setdefdname(statp, cp, eol); continue; } /* set search list */ if (res_match(line, eow, "search")) { cp = eow; while (cp < eol && res_space(*cp)) cp++; res_setdnsrch(statp, cp, eol); continue; } /* read nameservers to query */ if (res_match(line, eow, "nameserver")) { cp = eow; while (cp < eol && res_space(*cp)) cp++; if (cp == eol) continue; if (nserv < maxns) { if (res_setnsaddr(statp, nserv, cp, eol)) nserv++; #ifdef DEBUG } else { printf(";; nameserver overflow: %.*s\n", (int)(eol - cp), cp); #endif } continue; } #ifdef RESOLVSORT /* read result sort order */ if (res_match(line, eow, "sortlist")) { while (nsort < MAXRESOLVSORT) { cp = eow; while (cp < eol && res_space(*cp)) cp++; if (cp == eol) break; eow = cp; while (eow < eol && !res_space(*eow)) eow++; if (res_setsortlist(statp, nsort, cp, eow)) nsort++; } #ifdef DEBUG while (cp < eol && res_space(*cp)) cp++; if (cp < eol) { printf(";; sortlist overflow: %.*s\n", (int)(eol - cp), cp); } #endif continue; } #endif /* RESOLVSORT */ /* read resolver options */ if (res_match(line, eow, "options")) { cp = eow; while (res_space(*cp)) cp++; res_setoptions(statp, cp, "conf"); continue; } } free(line); if (nserv > 0) statp->nscount = nserv; #ifdef RESOLVSORT statp->nsort = nsort; #endif (void)fclose(fp); } /*% * Set the default domain to the first word of the string. Assumes no * leading space. */ static void res_setdefdname(res_state statp, const char *str, const char *end) { const char *sp; /* find the first space */ sp = str; while (sp < end && !res_space(*sp)) sp++; /* check length of first word */ if (sp == str || sp - str >= sizeof(statp->defdname)) { statp->defdname[0] = '\0'; statp->dnsrch[0] = NULL; return; } /* copy the first word into defdname */ memcpy(statp->defdname, str, sp - str); statp->defdname[sp - str] = '\0'; statp->dnsrch[0] = NULL; #ifdef DEBUG printf(";; domain %s\n", statp->defdname); #endif } /*% * Set the default domain to the first word of the string and split the * string up into the search path. Assumes no leading space. */ static void res_setdnsrch(res_state statp, const char *str, const char *end) { char *sp, *wp, *np; int n; /* trim trailing space */ while (end > str && res_space(end[-1])) end--; /* check length of string */ if (end == str || end - str >= sizeof(statp->defdname)) { statp->defdname[0] = '\0'; statp->dnsrch[0] = NULL; return; } /* copy entire string into defdname */ memcpy(statp->defdname, str, end - str); statp->defdname[end - str] = '\0'; /* split string into words */ for (wp = statp->defdname, n = 0; *wp != '\0' && n < MAXDNSRCH; wp = np) { /* find the end of the word */ sp = wp; while (*sp != '\0' && !res_space(*sp)) sp++; /* find the start of the next word */ np = sp; while (*np != '\0' && res_space(*np)) np++; /* assign to the next slot */ if (sp > wp) { statp->dnsrch[n] = wp; *sp = '\0'; #ifdef DEBUG if (n == 0) printf(";; domain %s\n;; search", statp->defdname); printf(" %s", statp->dnsrch[n]); #endif n++; } } #ifdef DEBUG printf("\n"); #endif /* terminate the list */ statp->dnsrch[n] = NULL; } /*% * Resolve the string and insert the result in the specified slot in the * name server list. Assumes no leading or trailing space. Returns 1 if * an entry was inserted, 0 otherwise. */ static int res_setnsaddr(res_state statp, int i, const char *str, const char *end) { char addr[48], port[8]; _Static_assert(sizeof(addr) > INET6_ADDRSTRLEN, "address buffer too small for AF_INET6"); _Static_assert(sizeof(addr) > INET_ADDRSTRLEN, "address buffer too small for AF_INET"); struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_flags = AI_NUMERICHOST, }; struct addrinfo *ai; int ret = 0; /* prepare port number */ snprintf(port, sizeof(port), "%u", NAMESERVER_PORT); /* check length */ if (end - str >= sizeof(addr)) { #ifdef DEBUG printf(";; nameserver too long: %.*s\n", (int)(end - str), str); #endif return (0); } /* resolve the address */ memcpy(addr, str, end - str); addr[end - str] = '\0'; if (getaddrinfo(addr, port, &hints, &ai) != 0) { #ifdef DEBUG printf(";; nameserver invalid: %s\n", addr); #endif return (0); } /* copy the sockaddr */ if (ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) { if (statp->_u._ext.ext != NULL) { memcpy(&statp->_u._ext.ext->nsaddrs[i], ai->ai_addr, ai->ai_addrlen); ret = 1; } if (ai->ai_addrlen <= sizeof(statp->nsaddr_list[i])) { memcpy(&statp->nsaddr_list[i], ai->ai_addr, ai->ai_addrlen); ret = 1; } else { statp->nsaddr_list[i].sin_family = 0; } #ifdef DEBUG printf(";; nameserver %s\n", addr); #endif } else { #ifdef DEBUG printf(";; sockaddr too big: %s\n", addr); #endif } freeaddrinfo(ai); return (ret); } #ifdef RESOLVSORT /*% * Parse a sortlist entry and insert it into the specified slot in the * sortlist. Assumes no leading or trailing space. Returns 1 if an entry * was inserted, 0 otherwise. */ static int res_setsortlist(res_state statp, int i, const char *str, const char *end) { struct __res_state_ext *ext = statp->_u._ext.ext; char addr[48], mask[48], *dst, *numend; _Static_assert(sizeof(addr) > INET6_ADDRSTRLEN, "address buffer too small for AF_INET6"); _Static_assert(sizeof(addr) > INET_ADDRSTRLEN, "address buffer too small for AF_INET"); _Static_assert(sizeof(mask) > INET6_ADDRSTRLEN, "mask buffer too small for AF_INET6"); _Static_assert(sizeof(mask) > INET_ADDRSTRLEN, "mask buffer too small for AF_INET"); struct in_addr a; struct in6_addr a6; uint8_t *u; long num; /* copy the address */ dst = addr; while (str < end && !res_space(*str) && *str != '/') { *dst++ = *str++; if (dst == addr + sizeof(addr)) return (0); } *dst = '\0'; /* copy the mask, if there is one */ dst = mask; if (str < end) { if (*str++ != '/') return (0); if (end - str >= sizeof(mask)) return (0); memcpy(mask, str, end - str); dst += end - str; } *dst = '\0'; /* IPv4 case */ if (inet_pton(AF_INET, addr, &a) == 1) { statp->sort_list[i].addr = a; if (strchr(mask, '.') && inet_pton(AF_INET, mask, &a) == 1) { statp->sort_list[i].mask = a.s_addr; } else if (*mask != '\0') { if ((num = strtol(mask, &numend, 10)) < 0 || num > 32 || *numend != '\0') return (0); statp->sort_list[i].mask = num == 0 ? 0 : htonl(0xffffffffU << (32 - num)); } else if (IN_CLASSA(ntohl(a.s_addr))) { statp->sort_list[i].mask = htonl(IN_CLASSA_NET); } else if (IN_CLASSB(ntohl(a.s_addr))) { statp->sort_list[i].mask = htonl(IN_CLASSB_NET); } else if (IN_CLASSC(ntohl(a.s_addr))) { statp->sort_list[i].mask = htonl(IN_CLASSC_NET); } else { statp->sort_list[i].mask = htonl(IN_NETMASK_DEFAULT); } ext->sort_list[i].af = AF_INET; ext->sort_list[i].addr.ina = statp->sort_list[i].addr; ext->sort_list[i].mask.ina.s_addr = statp->sort_list[i].mask; #ifdef DEBUG inet_ntop(AF_INET, &statp->sort_list[i].addr, addr, sizeof(addr)); a.s_addr = statp->sort_list[i].mask; inet_ntop(AF_INET, &a, mask, sizeof(mask)); printf(";; sortlist %s/%s\n", addr, mask); #endif return (1); } /* IPv6 case */ if (inet_pton(AF_INET6, addr, &a6) == 1) { statp->sort_list[i].addr.s_addr = INADDR_NONE; statp->sort_list[i].mask = INADDR_NONE; ext->sort_list[i].af = AF_INET6; ext->sort_list[i].addr.in6a = a6; if (*mask != '\0') { /* prefix length */ if ((num = strtol(mask, &numend, 10)) < 0 || num > 128 || *numend != '\0') return (0); } else { num = 64; } #ifdef DEBUG inet_ntop(AF_INET6, &a6, addr, sizeof(addr)); printf(";; sortlist %s/%ld\n", addr, num); #endif a6 = (struct in6_addr)IN6ADDR_ANY_INIT; for (u = a6.s6_addr8; num > 0; num -= 8) *u++ = num > 8 ? 0xffU : 0xffU << (8 - num); ext->sort_list[i].mask.in6a = a6; return (1); } /* invalid */ return (0); } #endif /* RESOLVSORT */ /*% * Split the string up into words and interpret them as resolver options. */ static void res_setoptions(res_state statp, const char *options, const char *source) { const char *cp = options, *sp, *ep; char *numend; long num; struct __res_state_ext *ext = statp->_u._ext.ext; #ifdef DEBUG if (statp->options & RES_DEBUG) { printf(";; res_setoptions(\"%s\", \"%s\")...\n", options, source); } #endif for (;;) { /* skip leading and inner runs of spaces */ while (res_space(*cp)) cp++; /* find the separator if there is one */ sp = cp; while (*sp != '\0' && !res_space(*sp) && *sp != ':') sp++; /* find the end of the option */ ep = sp; while (*ep != '\0' && !res_space(*ep)) ep++; /* end of option line */ if (ep == cp) break; /* search for and process individual options */ /* note: sp < ep if implies *sp == ':' */ if (res_match(cp, sp, "ndots") && sp < ep) { num = strtol(sp + 1, &numend, 10); if (numend == ep && num >= 0 && num <= RES_MAXNDOTS) { statp->ndots = num; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\tndots=%d\n", statp->ndots); #endif } } else if (res_match(cp, sp, "timeout") && sp < ep) { num = strtol(sp + 1, &numend, 10); if (numend == ep && num > 0 && num <= RES_MAXRETRANS) { statp->retrans = num; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\ttimeout=%d\n", statp->retrans); #endif } } else if (res_match(cp, sp, "attempts") && sp < ep) { num = strtol(sp + 1, &numend, 10); if (numend == ep && num > 0 && num <= RES_MAXRETRY) { statp->retry = num; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\tattempts=%d\n", statp->retry); #endif } } else if (res_match(cp, ep, "debug")) { #ifdef DEBUG if (!(statp->options & RES_DEBUG)) { printf(";; res_setoptions(\"%s\", \"%s\")..\n", options, source); } #endif statp->options |= RES_DEBUG; #ifdef DEBUG printf(";;\tdebug\n"); #endif } else if (res_match(cp, ep, "no-debug")) { statp->options &= ~RES_DEBUG; } else if (res_match(cp, ep, "no-rotate")) { statp->options &= ~RES_ROTATE; } else if (res_match(cp, ep, "no-tld-query") || res_match(cp, ep, "no_tld_query")) { statp->options |= RES_NOTLDQUERY; } else if (res_match(cp, ep, "inet6")) { statp->options |= RES_USE_INET6; } else if (res_match(cp, ep, "insecure1")) { statp->options |= RES_INSECURE1; } else if (res_match(cp, ep, "insecure2")) { statp->options |= RES_INSECURE2; } else if (res_match(cp, ep, "blast")) { statp->options |= RES_BLAST; } else if (res_match(cp, ep, "rotate")) { statp->options |= RES_ROTATE; } else if (res_match(cp, ep, "usevc")) { statp->options |= RES_USEVC; } else if (res_match(cp, ep, "no-check-names")) { statp->options |= RES_NOCHECKNAME; } else if (res_match(cp, sp, "reload-period") && sp < ep) { num = strtol(sp + 1, &numend, 10); if (numend == ep && num > 0 && num <= USHRT_MAX) { if (ext != NULL) ext->reload_period = (u_short)num; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\treload-period %hu\n", (u_short)num); #endif } #ifdef RES_USE_EDNS0 } else if (res_match(cp, ep, "edns0")) { statp->options |= RES_USE_EDNS0; #endif #ifndef _LIBC } else if (res_match(cp, ep, "dname")) { statp->options |= RES_USE_DNAME; } else if (res_match(cp, sp, "nibble") && sp < ep) { sp++; if (ext != NULL && ep - sp < sizeof(ext->nsuffix)) { memcpy(ext->nsuffix, sp, ep - sp); ext->nsuffix[ep - sp] = '\0'; } } else if (res_match(cp, sp, "nibble2") && sp < ep) { sp++; if (ext != NULL && ep - sp < sizeof(ext->nsuffix2)) { memcpy(ext->nsuffix2, sp, ep - sp); ext->nsuffix2[ep - sp] = '\0'; } } else if (res_match(cp, sp, "v6revmode") && sp < ep) { /* "nibble" and "bitstring" used to be valid */ if (res_match(sp + 1, ep, "single")) statp->options |= RES_NO_NIBBLE2; else if (res_match(sp + 1, ep, "both")) statp->options &= ~RES_NO_NIBBLE2; #ifdef DEBUG else printf(";;\t%.*s: unrecognized value: %.*s\n", (int)(sp - cp), cp, (int)(ep - sp - 1), sp + 1); #endif #endif /* !_LIBC */ } else { #ifdef DEBUG printf(";;\tunrecognized option: %.*s\n", (int)(ep - cp), cp); #endif } cp = ep; } } void freebsd15_res_rndinit(res_state statp) { (void)statp; } __sym_compat(__res_rndinit, freebsd15_res_rndinit, FBSD_1.4); u_int res_nrandomid(res_state statp) { (void) statp; return ((u_int)(arc4random() & 0xffff)); } /*% * This routine is for closing the socket if a virtual circuit is used and * the program wants to close it. This provides support for endhostent() * which expects to close the socket. * * This routine is not expected to be user visible. */ void res_nclose(res_state statp) { int ns; if (statp->_vcsock >= 0) { (void) _close(statp->_vcsock); statp->_vcsock = -1; statp->_flags &= ~(RES_F_VC | RES_F_CONN); } for (ns = 0; ns < statp->_u._ext.nscount; ns++) { if (statp->_u._ext.nssocks[ns] != -1) { (void) _close(statp->_u._ext.nssocks[ns]); statp->_u._ext.nssocks[ns] = -1; } } } void res_ndestroy(res_state statp) { res_nclose(statp); if (statp->_u._ext.ext != NULL) { free(statp->_u._ext.ext); statp->_u._ext.ext = NULL; } statp->options &= ~RES_INIT; } void res_setservers(res_state statp, const union res_sockaddr_union *set, int cnt) { int i, nserv; size_t size; /* close open servers */ res_nclose(statp); /* cause rtt times to be forgotten */ statp->_u._ext.nscount = 0; nserv = 0; for (i = 0; i < cnt && nserv < MAXNS; i++) { switch (set->sin.sin_family) { case AF_INET: size = sizeof(set->sin); if (statp->_u._ext.ext) memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &set->sin, size); if (size <= sizeof(statp->nsaddr_list[nserv])) memcpy(&statp->nsaddr_list[nserv], &set->sin, size); else statp->nsaddr_list[nserv].sin_family = 0; nserv++; break; case AF_INET6: size = sizeof(set->sin6); if (statp->_u._ext.ext) memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &set->sin6, size); if (size <= sizeof(statp->nsaddr_list[nserv])) memcpy(&statp->nsaddr_list[nserv], &set->sin6, size); else statp->nsaddr_list[nserv].sin_family = 0; nserv++; break; default: break; } set++; } statp->nscount = nserv; } int res_getservers(res_state statp, union res_sockaddr_union *set, int cnt) { int i; size_t size; u_int16_t family; for (i = 0; i < statp->nscount && i < cnt; i++) { if (statp->_u._ext.ext) family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family; else family = statp->nsaddr_list[i].sin_family; switch (family) { case AF_INET: size = sizeof(set->sin); if (statp->_u._ext.ext) memcpy(&set->sin, &statp->_u._ext.ext->nsaddrs[i], size); else memcpy(&set->sin, &statp->nsaddr_list[i], size); break; case AF_INET6: size = sizeof(set->sin6); if (statp->_u._ext.ext) memcpy(&set->sin6, &statp->_u._ext.ext->nsaddrs[i], size); else memcpy(&set->sin6, &statp->nsaddr_list[i], size); break; default: set->sin.sin_family = 0; break; } set++; } return (statp->nscount); }