xref: /freebsd/lib/libc/resolv/res_init.c (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
1 /*-
2  * SPDX-License-Identifier: (BSD-3-Clause AND ISC)
3  *
4  * Copyright (c) 1985, 1989, 1993
5  *    The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 2026 Dag-Erling Smørgrav
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  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
35  *
36  * Permission to use, copy, modify, and distribute this software for any
37  * purpose with or without fee is hereby granted, provided that the above
38  * copyright notice and this permission notice appear in all copies, and that
39  * the name of Digital Equipment Corporation not be used in advertising or
40  * publicity pertaining to distribution of the document or software without
41  * specific, written prior permission.
42  *
43  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
44  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
45  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
46  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
47  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
48  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
49  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
50  * SOFTWARE.
51  */
52 
53 /*
54  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
55  * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
56  *
57  * Permission to use, copy, modify, and distribute this software for any
58  * purpose with or without fee is hereby granted, provided that the above
59  * copyright notice and this permission notice appear in all copies.
60  *
61  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
62  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
63  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
64  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
65  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
66  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
67  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
68  */
69 
70 #include "port_before.h"
71 
72 #include "namespace.h"
73 
74 #include <sys/param.h>
75 #include <sys/socket.h>
76 #include <sys/stat.h>
77 #include <sys/time.h>
78 
79 #include <netinet/in.h>
80 #include <arpa/inet.h>
81 #include <arpa/nameser.h>
82 
83 #include <ctype.h>
84 #include <limits.h>
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88 #include <unistd.h>
89 #include <netdb.h>
90 
91 #include "un-namespace.h"
92 
93 #include "port_after.h"
94 
95 /* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */
96 #include <resolv.h>
97 
98 #include "res_config.h"
99 #include "res_private.h"
100 
101 static void res_readconf(res_state, const char *, int);
102 static void res_setdefdname(res_state, const char *, const char *);
103 static void res_setdnsrch(res_state, const char *, const char *);
104 static int res_setnsaddr(res_state, int, const char *, const char *);
105 static void res_setoptions(res_state, const char *, const char *);
106 
107 #ifdef RESOLVSORT
108 static int res_setsortlist(res_state, int, const char *, const char *);
109 #endif /* RESOLVSORT */
110 
111 #define res_space(ch)							\
112 	isspace((unsigned char)(ch))
113 #define res_comment(ch)							\
114 	((ch) == '#' || (ch) == ';')
115 #define res_match(str, end, word)					\
116 	(strncmp((str), (word), (end) - (str)) == 0 &&			\
117 	    (word)[(end) - (str)] == '\0')
118 
119 /*
120  * Resolver state default settings.
121  */
122 
123 /*%
124  * Set up default settings.  If the configuration file exist, the values
125  * there will have precedence.  Otherwise, the server address is set to
126  * the loopback address the default domain name comes from gethostname().
127  *
128  * The configuration file should always be used, since it is the only way
129  * to specify options and a default domain.  If you are running a server
130  * on your local machine, you should say "nameserver 127.0.0.1" or
131  * "nameserver ::1" in the configuration file.
132  *
133  * Return 0 if completes successfully, -1 on error
134  */
135 int
136 res_ninit(res_state statp)
137 {
138 	return (__res_vinit(statp, 0));
139 }
140 
141 /*% This function has to be reachable by res_data.c but not publicly. */
142 int
143 __res_vinit(res_state statp, int preinit)
144 {
145 	char buf[NI_MAXHOST];
146 	union res_sockaddr_union u[] = {
147 		{ .sin = {
148 			.sin_family = AF_INET,
149 #ifdef HAVE_SA_LEN
150 			.sin_len = sizeof(struct sockaddr_in),
151 #endif
152 			.sin_port = htons(NAMESERVER_PORT),
153 			.sin_addr = { htonl(INADDR_LOOPBACK) },
154 		} },
155 		{ .sin6 = {
156 			.sin6_family = AF_INET6,
157 #ifdef HAVE_SA_LEN
158 			.sin6_len = sizeof(struct sockaddr_in6),
159 #endif
160 			.sin6_port = htons(NAMESERVER_PORT),
161 			.sin6_addr = IN6ADDR_LOOPBACK_INIT,
162 		} },
163 	};
164 	char *cp, **pp;
165 	int dots;
166 	int maxns = MAXNS;
167 
168 	RES_SET_H_ERRNO(statp, 0);
169 	if (statp->_u._ext.ext != NULL)
170 		res_ndestroy(statp);
171 
172 	if (!preinit) {
173 		statp->retrans = RES_TIMEOUT;
174 		statp->retry = RES_DFLRETRY;
175 		statp->options = RES_DEFAULT;
176 	}
177 
178 	statp->id = res_nrandomid(statp);
179 
180 	statp->nscount = 0;
181 	statp->ndots = 1;
182 	statp->pfcode = 0;
183 	statp->_vcsock = -1;
184 	statp->_flags = 0;
185 	statp->qhook = NULL;
186 	statp->rhook = NULL;
187 	statp->_u._ext.nscount = 0;
188 	statp->_u._ext.ext = malloc(sizeof(*statp->_u._ext.ext));
189 	if (statp->_u._ext.ext != NULL) {
190 		memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext));
191 		statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr;
192 		strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa");
193 		strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int");
194 		statp->_u._ext.ext->reload_period = 2;
195 	} else {
196 		/*
197 		 * Historically res_init() rarely, if at all, failed.
198 		 * Examples and applications exist which do not check
199 		 * our return code.  Furthermore several applications
200 		 * simply call us to get the systems domainname.  So
201 		 * rather then immediately fail here we store the
202 		 * failure, which is returned later, in h_errno.  And
203 		 * prevent the collection of 'nameserver' information
204 		 * by setting maxns to 0.  Thus applications that fail
205 		 * to check our return code wont be able to make
206 		 * queries anyhow.
207 		 */
208 		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
209 		maxns = 0;
210 	}
211 #ifdef RESOLVSORT
212 	statp->nsort = 0;
213 #endif
214 	res_setservers(statp, u, nitems(u));
215 
216 	/* read the configuration file */
217 	res_readconf(statp, _PATH_RESCONF, maxns);
218 
219 	/* Allow user to override the local domain definition */
220 	if ((cp = secure_getenv("LOCALDOMAIN")) != NULL) {
221 		while (res_space(*cp))
222 			cp++;
223 		res_setdnsrch(statp, cp, strchr(cp, '\0'));
224 	}
225 
226 	/* guess default domain if not set */
227 	if (statp->defdname[0] == 0 &&
228 	    gethostname(buf, sizeof(statp->defdname) - 1) == 0 &&
229 	    (cp = strchr(buf, '.')) != NULL)
230 		strcpy(statp->defdname, cp + 1);
231 
232 	/* find components of local domain that might be searched */
233 	if (statp->dnsrch[0] == NULL) {
234 		pp = statp->dnsrch;
235 		*pp++ = statp->defdname;
236 		*pp = NULL;
237 
238 		dots = 0;
239 		for (cp = statp->defdname; *cp; cp++)
240 			dots += (*cp == '.');
241 
242 		cp = statp->defdname;
243 		while (pp < statp->dnsrch + MAXDFLSRCH) {
244 			if (dots < LOCALDOMAINPARTS)
245 				break;
246 			cp = strchr(cp, '.') + 1;    /*%< we know there is one */
247 			*pp++ = cp;
248 			dots--;
249 		}
250 		*pp = NULL;
251 #ifdef DEBUG
252 		if (statp->options & RES_DEBUG) {
253 			printf(";; res_init()... default dnsrch list:\n");
254 			for (pp = statp->dnsrch; *pp; pp++)
255 				printf(";;\t%s\n", *pp);
256 			printf(";;\t..END..\n");
257 		}
258 #endif
259 	}
260 
261 	if (issetugid())
262 		statp->options |= RES_NOALIASES;
263 	else if ((cp = getenv("RES_OPTIONS")) != NULL)
264 		res_setoptions(statp, cp, "env");
265 	statp->options |= RES_INIT;
266 	return (statp->res_h_errno);
267 }
268 
269 /*%
270  * Read the resolver configuration file.
271  */
272 static void
273 res_readconf(res_state statp, const char *resconf, int maxns)
274 {
275 	struct stat sb;
276 	struct timespec now;
277 	FILE *fp;
278 	char *line = NULL, *eol, *eow;
279 	char *cp;
280 	ssize_t len;
281 	size_t sz = 0;
282 	int nserv = 0;
283 #ifdef RESOLVSORT
284 	int nsort = 0;
285 #endif
286 
287 	if ((fp = fopen(resconf, "re")) == NULL)
288 		return;
289 	if (statp->_u._ext.ext != NULL) {
290 		if (_fstat(fileno(fp), &sb) == 0) {
291 			statp->_u._ext.ext->conf_mtim = sb.st_mtim;
292 			if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == 0) {
293 				statp->_u._ext.ext->conf_stat = now.tv_sec;
294 			}
295 		}
296 	}
297 
298 	while ((len = getline(&line, &sz, fp)) != -1) {
299 		/* search for comment or end of line */
300 		eol = line;
301 		while (eol < line + len && !res_comment(*eol))
302 			eol++;
303 
304 		/* now backtrack over whitespace */
305 		while (eol > line && res_space(eol[-1]))
306 			eol--;
307 		*eol = '\0';
308 
309 		/* skip blank line */
310 		if (eol == line)
311 			continue;
312 
313 		/* isolate the first word */
314 		eow = line;
315 		while (eow < eol && !res_space(*eow))
316 			eow++;
317 
318 		/* read default domain name */
319 		if (res_match(line, eow, "domain")) {
320 			cp = eow;
321 			while (cp < eol && res_space(*cp))
322 				cp++;
323 			res_setdefdname(statp, cp, eol);
324 			continue;
325 		}
326 
327 		/* set search list */
328 		if (res_match(line, eow, "search")) {
329 			cp = eow;
330 			while (cp < eol && res_space(*cp))
331 				cp++;
332 			res_setdnsrch(statp, cp, eol);
333 			continue;
334 		}
335 
336 		/* read nameservers to query */
337 		if (res_match(line, eow, "nameserver")) {
338 			cp = eow;
339 			while (cp < eol && res_space(*cp))
340 				cp++;
341 			if (cp == eol)
342 				continue;
343 			if (nserv < maxns) {
344 				if (res_setnsaddr(statp, nserv, cp, eol))
345 					nserv++;
346 #ifdef DEBUG
347 			} else {
348 				printf(";; nameserver overflow: %.*s\n",
349 				    (int)(eol - cp), cp);
350 #endif
351 			}
352 			continue;
353 		}
354 #ifdef RESOLVSORT
355 		/* read result sort order */
356 		if (res_match(line, eow, "sortlist")) {
357 			while (nsort < MAXRESOLVSORT) {
358 				cp = eow;
359 				while (cp < eol && res_space(*cp))
360 					cp++;
361 				if (cp == eol)
362 					break;
363 				eow = cp;
364 				while (eow < eol && !res_space(*eow))
365 					eow++;
366 				if (res_setsortlist(statp, nsort, cp, eow))
367 					nsort++;
368 			}
369 #ifdef DEBUG
370 			while (cp < eol && res_space(*cp))
371 				cp++;
372 			if (cp < eol) {
373 				printf(";; sortlist overflow: %.*s\n",
374 				    (int)(eol - cp), cp);
375 			}
376 #endif
377 			continue;
378 		}
379 #endif /* RESOLVSORT */
380 		/* read resolver options */
381 		if (res_match(line, eow, "options")) {
382 			cp = eow;
383 			while (res_space(*cp))
384 				cp++;
385 			res_setoptions(statp, cp, "conf");
386 			continue;
387 		}
388 	}
389 	free(line);
390 	if (nserv > 0)
391 		statp->nscount = nserv;
392 #ifdef RESOLVSORT
393 	statp->nsort = nsort;
394 #endif
395 	(void)fclose(fp);
396 }
397 
398 /*%
399  * Set the default domain to the first word of the string.  Assumes no
400  * leading space.
401  */
402 static void
403 res_setdefdname(res_state statp, const char *str, const char *end)
404 {
405 	const char *sp;
406 
407 	/* find the first space */
408 	sp = str;
409 	while (sp < end && !res_space(*sp))
410 		sp++;
411 
412 	/* check length of first word */
413 	if (sp == str || sp - str >= sizeof(statp->defdname)) {
414 		statp->defdname[0] = '\0';
415 		statp->dnsrch[0] = NULL;
416 		return;
417 	}
418 
419 	/* copy the first word into defdname */
420 	memcpy(statp->defdname, str, sp - str);
421 	statp->defdname[sp - str] = '\0';
422 	statp->dnsrch[0] = NULL;
423 #ifdef DEBUG
424 	printf(";; domain %s\n", statp->defdname);
425 #endif
426 }
427 
428 /*%
429  * Set the default domain to the first word of the string and split the
430  * string up into the search path.  Assumes no leading space.
431  */
432 static void
433 res_setdnsrch(res_state statp, const char *str, const char *end)
434 {
435 	char *sp, *wp, *np;
436 	int n;
437 
438 	/* trim trailing space */
439 	while (end > str && res_space(end[-1]))
440 		end--;
441 
442 	/* check length of string */
443 	if (end == str || end - str >= sizeof(statp->defdname)) {
444 		statp->defdname[0] = '\0';
445 		statp->dnsrch[0] = NULL;
446 		return;
447 	}
448 
449 	/* copy entire string into defdname */
450 	memcpy(statp->defdname, str, end - str);
451 	statp->defdname[end - str] = '\0';
452 
453 	/* split string into words */
454 	for (wp = statp->defdname, n = 0;
455 	     *wp != '\0' && n < MAXDNSRCH;
456 	     wp = np) {
457 		/* find the end of the word */
458 		sp = wp;
459 		while (*sp != '\0' && !res_space(*sp))
460 			sp++;
461 
462 		/* find the start of the next word */
463 		np = sp;
464 		while (*np != '\0' && res_space(*np))
465 			np++;
466 
467 		/* assign to the next slot */
468 		if (sp > wp) {
469 			statp->dnsrch[n] = wp;
470 			*sp = '\0';
471 #ifdef DEBUG
472 			if (n == 0)
473 				printf(";; domain %s\n;; search", statp->defdname);
474 			printf(" %s", statp->dnsrch[n]);
475 #endif
476 			n++;
477 		}
478 	}
479 #ifdef DEBUG
480 	printf("\n");
481 #endif
482 
483 	/* terminate the list */
484 	statp->dnsrch[n] = NULL;
485 }
486 
487 /*%
488  * Resolve the string and insert the result in the specified slot in the
489  * name server list.  Assumes no leading or trailing space.  Returns 1 if
490  * an entry was inserted, 0 otherwise.
491  */
492 static int
493 res_setnsaddr(res_state statp, int i, const char *str, const char *end)
494 {
495 	char addr[48], port[8];
496 	_Static_assert(sizeof(addr) > INET6_ADDRSTRLEN,
497 	    "address buffer too small for AF_INET6");
498 	_Static_assert(sizeof(addr) > INET_ADDRSTRLEN,
499 	    "address buffer too small for AF_INET");
500 	struct addrinfo hints = {
501 		.ai_family = AF_UNSPEC,
502 		.ai_socktype = SOCK_DGRAM,
503 		.ai_flags = AI_NUMERICHOST,
504 	};
505 	struct addrinfo *ai;
506 	int ret = 0;
507 
508 	/* prepare port number */
509 	snprintf(port, sizeof(port), "%u", NAMESERVER_PORT);
510 
511 	/* check length */
512 	if (end - str >= sizeof(addr)) {
513 #ifdef DEBUG
514 		printf(";; nameserver too long: %.*s\n",
515 		    (int)(end - str), str);
516 #endif
517 		return (0);
518 	}
519 
520 	/* resolve the address */
521 	memcpy(addr, str, end - str);
522 	addr[end - str] = '\0';
523 	if (getaddrinfo(addr, port, &hints, &ai) != 0) {
524 #ifdef DEBUG
525 		printf(";; nameserver invalid: %s\n", addr);
526 #endif
527 		return (0);
528 	}
529 
530 	/* copy the sockaddr */
531 	if (ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) {
532 		if (statp->_u._ext.ext != NULL) {
533 			memcpy(&statp->_u._ext.ext->nsaddrs[i],
534 			    ai->ai_addr, ai->ai_addrlen);
535 			ret = 1;
536 		}
537 		if (ai->ai_addrlen <= sizeof(statp->nsaddr_list[i])) {
538 			memcpy(&statp->nsaddr_list[i],
539 			    ai->ai_addr, ai->ai_addrlen);
540 			ret = 1;
541 		} else {
542 			statp->nsaddr_list[i].sin_family = 0;
543 		}
544 #ifdef DEBUG
545 		printf(";; nameserver %s\n", addr);
546 #endif
547 	} else {
548 #ifdef DEBUG
549 		printf(";; sockaddr too big: %s\n", addr);
550 #endif
551 	}
552 	freeaddrinfo(ai);
553 	return (ret);
554 }
555 
556 #ifdef RESOLVSORT
557 /*%
558  * Parse a sortlist entry and insert it into the specified slot in the
559  * sortlist.  Assumes no leading or trailing space.  Returns 1 if an entry
560  * was inserted, 0 otherwise.
561  */
562 static int
563 res_setsortlist(res_state statp, int i, const char *str, const char *end)
564 {
565 	struct __res_state_ext *ext = statp->_u._ext.ext;
566 	char addr[48], mask[48], *dst, *numend;
567 	_Static_assert(sizeof(addr) > INET6_ADDRSTRLEN,
568 	    "address buffer too small for AF_INET6");
569 	_Static_assert(sizeof(addr) > INET_ADDRSTRLEN,
570 	    "address buffer too small for AF_INET");
571 	_Static_assert(sizeof(mask) > INET6_ADDRSTRLEN,
572 	    "mask buffer too small for AF_INET6");
573 	_Static_assert(sizeof(mask) > INET_ADDRSTRLEN,
574 	    "mask buffer too small for AF_INET");
575 	struct in_addr a;
576 	struct in6_addr a6;
577 	uint8_t *u;
578 	long num;
579 
580 	/* copy the address */
581 	dst = addr;
582 	while (str < end && !res_space(*str) && *str != '/') {
583 		*dst++ = *str++;
584 		if (dst == addr + sizeof(addr))
585 			return (0);
586 	}
587 	*dst = '\0';
588 
589 	/* copy the mask, if there is one */
590 	dst = mask;
591 	if (str < end) {
592 		if (*str++ != '/')
593 			return (0);
594 		if (end - str >= sizeof(mask))
595 			return (0);
596 		memcpy(mask, str, end - str);
597 		dst += end - str;
598 	}
599 	*dst = '\0';
600 
601 	/* IPv4 case */
602 	if (inet_pton(AF_INET, addr, &a) == 1) {
603 		statp->sort_list[i].addr = a;
604 		if (strchr(mask, '.') &&
605 		    inet_pton(AF_INET, mask, &a) == 1) {
606 			statp->sort_list[i].mask = a.s_addr;
607 		} else if (*mask != '\0') {
608 			if ((num = strtol(mask, &numend, 10)) < 0 ||
609 			    num > 32 || *numend != '\0')
610 				return (0);
611 			statp->sort_list[i].mask =
612 			    num == 0 ? 0 :
613 			    htonl(0xffffffffU << (32 - num));
614 		} else if (IN_CLASSA(ntohl(a.s_addr))) {
615 			statp->sort_list[i].mask =
616 			    htonl(IN_CLASSA_NET);
617 		} else if (IN_CLASSB(ntohl(a.s_addr))) {
618 			statp->sort_list[i].mask =
619 			    htonl(IN_CLASSB_NET);
620 		} else if (IN_CLASSC(ntohl(a.s_addr))) {
621 			statp->sort_list[i].mask =
622 			    htonl(IN_CLASSC_NET);
623 		} else {
624 			statp->sort_list[i].mask =
625 			    htonl(IN_NETMASK_DEFAULT);
626 		}
627 		ext->sort_list[i].af = AF_INET;
628 		ext->sort_list[i].addr.ina =
629 		    statp->sort_list[i].addr;
630 		ext->sort_list[i].mask.ina.s_addr =
631 		    statp->sort_list[i].mask;
632 #ifdef DEBUG
633 		inet_ntop(AF_INET, &statp->sort_list[i].addr,
634 		    addr, sizeof(addr));
635 		a.s_addr = statp->sort_list[i].mask;
636 		inet_ntop(AF_INET, &a, mask, sizeof(mask));
637 		printf(";; sortlist %s/%s\n", addr, mask);
638 #endif
639 		return (1);
640 	}
641 
642 	/* IPv6 case */
643 	if (inet_pton(AF_INET6, addr, &a6) == 1) {
644 		statp->sort_list[i].addr.s_addr = INADDR_NONE;
645 		statp->sort_list[i].mask = INADDR_NONE;
646 		ext->sort_list[i].af = AF_INET6;
647 		ext->sort_list[i].addr.in6a = a6;
648 		if (*mask != '\0') {
649 			/* prefix length */
650 			if ((num = strtol(mask, &numend, 10)) < 0 ||
651 			    num > 128 || *numend != '\0')
652 				return (0);
653 		} else {
654 			num = 64;
655 		}
656 #ifdef DEBUG
657 		inet_ntop(AF_INET6, &a6, addr, sizeof(addr));
658 		printf(";; sortlist %s/%ld\n", addr, num);
659 #endif
660 		a6 = (struct in6_addr)IN6ADDR_ANY_INIT;
661 		for (u = a6.s6_addr8; num > 0; num -= 8)
662 			*u++ = num > 8 ? 0xffU : 0xffU << (8 - num);
663 		ext->sort_list[i].mask.in6a = a6;
664 		return (1);
665 	}
666 
667 	/* invalid */
668 	return (0);
669 }
670 #endif /* RESOLVSORT */
671 
672 /*%
673  * Split the string up into words and interpret them as resolver options.
674  */
675 static void
676 res_setoptions(res_state statp, const char *options, const char *source)
677 {
678 	const char *cp = options, *sp, *ep;
679 	char *numend;
680 	long num;
681 	struct __res_state_ext *ext = statp->_u._ext.ext;
682 
683 #ifdef DEBUG
684 	if (statp->options & RES_DEBUG) {
685 		printf(";; res_setoptions(\"%s\", \"%s\")...\n",
686 		       options, source);
687 	}
688 #endif
689 	for (;;) {
690 		/* skip leading and inner runs of spaces */
691 		while (res_space(*cp))
692 			cp++;
693 		/* find the separator if there is one */
694 		sp = cp;
695 		while (*sp != '\0' && !res_space(*sp) && *sp != ':')
696 			sp++;
697 		/* find the end of the option */
698 		ep = sp;
699 		while (*ep != '\0' && !res_space(*ep))
700 			ep++;
701 		/* end of option line */
702 		if (ep == cp)
703 			break;
704 		/* search for and process individual options */
705 		/* note: sp < ep if implies *sp == ':' */
706 		if (res_match(cp, sp, "ndots") && sp < ep) {
707 			num = strtol(sp + 1, &numend, 10);
708 			if (numend == ep && num >= 0 && num <= RES_MAXNDOTS) {
709 				statp->ndots = num;
710 #ifdef DEBUG
711 				if (statp->options & RES_DEBUG)
712 					printf(";;\tndots=%d\n", statp->ndots);
713 #endif
714 			}
715 		} else if (res_match(cp, sp, "timeout") && sp < ep) {
716 			num = strtol(sp + 1, &numend, 10);
717 			if (numend == ep && num > 0 && num <= RES_MAXRETRANS) {
718 				statp->retrans = num;
719 #ifdef DEBUG
720 				if (statp->options & RES_DEBUG)
721 					printf(";;\ttimeout=%d\n",
722 					    statp->retrans);
723 #endif
724 			}
725 		} else if (res_match(cp, sp, "attempts") && sp < ep) {
726 			num = strtol(sp + 1, &numend, 10);
727 			if (numend == ep && num > 0 && num <= RES_MAXRETRY) {
728 				statp->retry = num;
729 #ifdef DEBUG
730 				if (statp->options & RES_DEBUG)
731 					printf(";;\tattempts=%d\n",
732 					    statp->retry);
733 #endif
734 			}
735 		} else if (res_match(cp, ep, "debug")) {
736 #ifdef DEBUG
737 			if (!(statp->options & RES_DEBUG)) {
738 				printf(";; res_setoptions(\"%s\", \"%s\")..\n",
739 				       options, source);
740 			}
741 #endif
742 			statp->options |= RES_DEBUG;
743 #ifdef DEBUG
744 			printf(";;\tdebug\n");
745 #endif
746 		} else if (res_match(cp, ep, "no-debug")) {
747 			statp->options &= ~RES_DEBUG;
748 		} else if (res_match(cp, ep, "no-rotate")) {
749 			statp->options &= ~RES_ROTATE;
750 		} else if (res_match(cp, ep, "no-tld-query") ||
751 		    res_match(cp, ep, "no_tld_query")) {
752 			statp->options |= RES_NOTLDQUERY;
753 		} else if (res_match(cp, ep, "inet6")) {
754 			statp->options |= RES_USE_INET6;
755 		} else if (res_match(cp, ep, "insecure1")) {
756 			statp->options |= RES_INSECURE1;
757 		} else if (res_match(cp, ep, "insecure2")) {
758 			statp->options |= RES_INSECURE2;
759 		} else if (res_match(cp, ep, "blast")) {
760 			statp->options |= RES_BLAST;
761 		} else if (res_match(cp, ep, "rotate")) {
762 			statp->options |= RES_ROTATE;
763 		} else if (res_match(cp, ep, "usevc")) {
764 			statp->options |= RES_USEVC;
765 		} else if (res_match(cp, ep, "no-check-names")) {
766 			statp->options |= RES_NOCHECKNAME;
767 		} else if (res_match(cp, sp, "reload-period") && sp < ep) {
768 			num = strtol(sp + 1, &numend, 10);
769 			if (numend == ep && num > 0 && num <= USHRT_MAX) {
770 				if (ext != NULL)
771 					ext->reload_period = (u_short)num;
772 #ifdef DEBUG
773 				if (statp->options & RES_DEBUG)
774 					printf(";;\treload-period %hu\n",
775 					    (u_short)num);
776 #endif
777 			}
778 #ifdef RES_USE_EDNS0
779 		} else if (res_match(cp, ep, "edns0")) {
780 			statp->options |= RES_USE_EDNS0;
781 #endif
782 #ifndef _LIBC
783 		} else if (res_match(cp, ep, "dname")) {
784 			statp->options |= RES_USE_DNAME;
785 		} else if (res_match(cp, sp, "nibble") && sp < ep) {
786 			sp++;
787 			if (ext != NULL && ep - sp < sizeof(ext->nsuffix)) {
788 				memcpy(ext->nsuffix, sp, ep - sp);
789 				ext->nsuffix[ep - sp] = '\0';
790 			}
791 		} else if (res_match(cp, sp, "nibble2") && sp < ep) {
792 			sp++;
793 			if (ext != NULL && ep - sp < sizeof(ext->nsuffix2)) {
794 				memcpy(ext->nsuffix2, sp, ep - sp);
795 				ext->nsuffix2[ep - sp] = '\0';
796 			}
797 		} else if (res_match(cp, sp, "v6revmode") && sp < ep) {
798 			/* "nibble" and "bitstring" used to be valid */
799 			if (res_match(sp + 1, ep, "single"))
800 				statp->options |= RES_NO_NIBBLE2;
801 			else if (res_match(sp + 1, ep, "both"))
802 				statp->options &= ~RES_NO_NIBBLE2;
803 #ifdef DEBUG
804 			else
805 				printf(";;\t%.*s: unrecognized value: %.*s\n",
806 				    (int)(sp - cp), cp,
807 				    (int)(ep - sp - 1), sp + 1);
808 #endif
809 #endif /* !_LIBC */
810 		} else {
811 #ifdef DEBUG
812 			printf(";;\tunrecognized option: %.*s\n",
813 			    (int)(ep - cp), cp);
814 #endif
815 		}
816 		cp = ep;
817 	}
818 }
819 
820 void
821 freebsd15_res_rndinit(res_state statp)
822 {
823 	(void)statp;
824 }
825 __sym_compat(__res_rndinit, freebsd15_res_rndinit, FBSD_1.4);
826 
827 u_int
828 res_nrandomid(res_state statp)
829 {
830 	(void) statp;
831 
832 	return ((u_int)(arc4random() & 0xffff));
833 }
834 
835 /*%
836  * This routine is for closing the socket if a virtual circuit is used and
837  * the program wants to close it.  This provides support for endhostent()
838  * which expects to close the socket.
839  *
840  * This routine is not expected to be user visible.
841  */
842 void
843 res_nclose(res_state statp)
844 {
845 	int ns;
846 
847 	if (statp->_vcsock >= 0) {
848 		(void) _close(statp->_vcsock);
849 		statp->_vcsock = -1;
850 		statp->_flags &= ~(RES_F_VC | RES_F_CONN);
851 	}
852 	for (ns = 0; ns < statp->_u._ext.nscount; ns++) {
853 		if (statp->_u._ext.nssocks[ns] != -1) {
854 			(void) _close(statp->_u._ext.nssocks[ns]);
855 			statp->_u._ext.nssocks[ns] = -1;
856 		}
857 	}
858 }
859 
860 void
861 res_ndestroy(res_state statp)
862 {
863 	res_nclose(statp);
864 	if (statp->_u._ext.ext != NULL) {
865 		free(statp->_u._ext.ext);
866 		statp->_u._ext.ext = NULL;
867 	}
868 	statp->options &= ~RES_INIT;
869 }
870 
871 void
872 res_setservers(res_state statp, const union res_sockaddr_union *set, int cnt)
873 {
874 	int i, nserv;
875 	size_t size;
876 
877 	/* close open servers */
878 	res_nclose(statp);
879 
880 	/* cause rtt times to be forgotten */
881 	statp->_u._ext.nscount = 0;
882 
883 	nserv = 0;
884 	for (i = 0; i < cnt && nserv < MAXNS; i++) {
885 		switch (set->sin.sin_family) {
886 		case AF_INET:
887 			size = sizeof(set->sin);
888 			if (statp->_u._ext.ext)
889 				memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
890 					&set->sin, size);
891 			if (size <= sizeof(statp->nsaddr_list[nserv]))
892 				memcpy(&statp->nsaddr_list[nserv],
893 					&set->sin, size);
894 			else
895 				statp->nsaddr_list[nserv].sin_family = 0;
896 			nserv++;
897 			break;
898 		case AF_INET6:
899 			size = sizeof(set->sin6);
900 			if (statp->_u._ext.ext)
901 				memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
902 					&set->sin6, size);
903 			if (size <= sizeof(statp->nsaddr_list[nserv]))
904 				memcpy(&statp->nsaddr_list[nserv],
905 					&set->sin6, size);
906 			else
907 				statp->nsaddr_list[nserv].sin_family = 0;
908 			nserv++;
909 			break;
910 		default:
911 			break;
912 		}
913 		set++;
914 	}
915 	statp->nscount = nserv;
916 }
917 
918 int
919 res_getservers(res_state statp, union res_sockaddr_union *set, int cnt)
920 {
921 	int i;
922 	size_t size;
923 	u_int16_t family;
924 
925 	for (i = 0; i < statp->nscount && i < cnt; i++) {
926 		if (statp->_u._ext.ext)
927 			family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family;
928 		else
929 			family = statp->nsaddr_list[i].sin_family;
930 
931 		switch (family) {
932 		case AF_INET:
933 			size = sizeof(set->sin);
934 			if (statp->_u._ext.ext)
935 				memcpy(&set->sin,
936 				       &statp->_u._ext.ext->nsaddrs[i],
937 				       size);
938 			else
939 				memcpy(&set->sin, &statp->nsaddr_list[i],
940 				       size);
941 			break;
942 		case AF_INET6:
943 			size = sizeof(set->sin6);
944 			if (statp->_u._ext.ext)
945 				memcpy(&set->sin6,
946 				       &statp->_u._ext.ext->nsaddrs[i],
947 				       size);
948 			else
949 				memcpy(&set->sin6, &statp->nsaddr_list[i],
950 				       size);
951 			break;
952 		default:
953 			set->sin.sin_family = 0;
954 			break;
955 		}
956 		set++;
957 	}
958 	return (statp->nscount);
959 }
960