Home | History | Annotate | Line # | Download | only in unix
      1 /*	$NetBSD: net.c,v 1.1 2024/02/18 20:57:57 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #include <stdbool.h>
     17 #include <sys/types.h>
     18 
     19 #if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__)
     20 #if defined(HAVE_SYS_PARAM_H)
     21 #include <sys/param.h>
     22 #endif /* if defined(HAVE_SYS_PARAM_H) */
     23 #include <sys/sysctl.h>
     24 #endif /* if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) */
     25 #include <errno.h>
     26 #include <fcntl.h>
     27 #include <sys/uio.h>
     28 #include <unistd.h>
     29 
     30 #include <isc/log.h>
     31 #include <isc/net.h>
     32 #include <isc/netdb.h>
     33 #include <isc/once.h>
     34 #include <isc/strerr.h>
     35 #include <isc/string.h>
     36 #include <isc/util.h>
     37 
     38 #ifndef socklen_t
     39 #define socklen_t unsigned int
     40 #endif /* ifndef socklen_t */
     41 
     42 /*%
     43  * Definitions about UDP port range specification.  This is a total mess of
     44  * portability variants: some use sysctl (but the sysctl names vary), some use
     45  * system-specific interfaces, some have the same interface for IPv4 and IPv6,
     46  * some separate them, etc...
     47  */
     48 
     49 /*%
     50  * The last resort defaults: use all non well known port space
     51  */
     52 #ifndef ISC_NET_PORTRANGELOW
     53 #define ISC_NET_PORTRANGELOW 1024
     54 #endif /* ISC_NET_PORTRANGELOW */
     55 #ifndef ISC_NET_PORTRANGEHIGH
     56 #define ISC_NET_PORTRANGEHIGH 65535
     57 #endif /* ISC_NET_PORTRANGEHIGH */
     58 
     59 #ifdef HAVE_SYSCTLBYNAME
     60 
     61 /*%
     62  * sysctl variants
     63  */
     64 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
     65 #define USE_SYSCTL_PORTRANGE
     66 #define SYSCTL_V4PORTRANGE_LOW	"net.inet.ip.portrange.hifirst"
     67 #define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.portrange.hilast"
     68 #define SYSCTL_V6PORTRANGE_LOW	"net.inet.ip.portrange.hifirst"
     69 #define SYSCTL_V6PORTRANGE_HIGH "net.inet.ip.portrange.hilast"
     70 #endif /* if defined(__FreeBSD__) || defined(__APPLE__) || \
     71 	* defined(__DragonFly__) */
     72 
     73 #ifdef __NetBSD__
     74 #define USE_SYSCTL_PORTRANGE
     75 #define SYSCTL_V4PORTRANGE_LOW	"net.inet.ip.anonportmin"
     76 #define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.anonportmax"
     77 #define SYSCTL_V6PORTRANGE_LOW	"net.inet6.ip6.anonportmin"
     78 #define SYSCTL_V6PORTRANGE_HIGH "net.inet6.ip6.anonportmax"
     79 #endif /* ifdef __NetBSD__ */
     80 
     81 #else /* !HAVE_SYSCTLBYNAME */
     82 
     83 #ifdef __OpenBSD__
     84 #define USE_SYSCTL_PORTRANGE
     85 #define SYSCTL_V4PORTRANGE_LOW                                         \
     86 	{                                                              \
     87 		CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HIFIRSTAUTO \
     88 	}
     89 #define SYSCTL_V4PORTRANGE_HIGH                                       \
     90 	{                                                             \
     91 		CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HILASTAUTO \
     92 	}
     93 /* Same for IPv6 */
     94 #define SYSCTL_V6PORTRANGE_LOW	SYSCTL_V4PORTRANGE_LOW
     95 #define SYSCTL_V6PORTRANGE_HIGH SYSCTL_V4PORTRANGE_HIGH
     96 #endif /* ifdef __OpenBSD__ */
     97 
     98 #endif /* HAVE_SYSCTLBYNAME */
     99 
    100 static isc_once_t once_ipv6only = ISC_ONCE_INIT;
    101 #ifdef __notyet__
    102 static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT;
    103 #endif /* ifdef __notyet__ */
    104 
    105 #ifndef ISC_CMSG_IP_TOS
    106 #ifdef __APPLE__
    107 #define ISC_CMSG_IP_TOS 0 /* As of 10.8.2. */
    108 #else			  /* ! __APPLE__ */
    109 #define ISC_CMSG_IP_TOS 1
    110 #endif /* ! __APPLE__ */
    111 #endif /* ! ISC_CMSG_IP_TOS */
    112 
    113 static isc_once_t once = ISC_ONCE_INIT;
    114 static isc_once_t once_dscp = ISC_ONCE_INIT;
    115 
    116 static isc_result_t ipv4_result = ISC_R_NOTFOUND;
    117 static isc_result_t ipv6_result = ISC_R_NOTFOUND;
    118 static isc_result_t unix_result = ISC_R_NOTFOUND;
    119 static isc_result_t ipv6only_result = ISC_R_NOTFOUND;
    120 static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND;
    121 static unsigned int dscp_result = 0;
    122 
    123 static isc_result_t
    124 try_proto(int domain) {
    125 	int s;
    126 	isc_result_t result = ISC_R_SUCCESS;
    127 	char strbuf[ISC_STRERRORSIZE];
    128 
    129 	s = socket(domain, SOCK_STREAM, 0);
    130 	if (s == -1) {
    131 		switch (errno) {
    132 #ifdef EAFNOSUPPORT
    133 		case EAFNOSUPPORT:
    134 #endif /* ifdef EAFNOSUPPORT */
    135 #ifdef EPFNOSUPPORT
    136 		case EPFNOSUPPORT:
    137 #endif /* ifdef EPFNOSUPPORT */
    138 #ifdef EPROTONOSUPPORT
    139 		case EPROTONOSUPPORT:
    140 #endif /* ifdef EPROTONOSUPPORT */
    141 #ifdef EINVAL
    142 		case EINVAL:
    143 #endif /* ifdef EINVAL */
    144 			return (ISC_R_NOTFOUND);
    145 		default:
    146 			strerror_r(errno, strbuf, sizeof(strbuf));
    147 			UNEXPECTED_ERROR(__FILE__, __LINE__,
    148 					 "socket() failed: %s", strbuf);
    149 			return (ISC_R_UNEXPECTED);
    150 		}
    151 	}
    152 
    153 	if (domain == PF_INET6) {
    154 		struct sockaddr_in6 sin6;
    155 		unsigned int len;
    156 
    157 		/*
    158 		 * Check to see if IPv6 is broken, as is common on Linux.
    159 		 */
    160 		len = sizeof(sin6);
    161 		if (getsockname(s, (struct sockaddr *)&sin6, (void *)&len) < 0)
    162 		{
    163 			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    164 				      ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
    165 				      "retrieving the address of an IPv6 "
    166 				      "socket from the kernel failed.");
    167 			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    168 				      ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
    169 				      "IPv6 is not supported.");
    170 			result = ISC_R_NOTFOUND;
    171 		} else {
    172 			if (len == sizeof(struct sockaddr_in6)) {
    173 				result = ISC_R_SUCCESS;
    174 			} else {
    175 				isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    176 					      ISC_LOGMODULE_SOCKET,
    177 					      ISC_LOG_ERROR,
    178 					      "IPv6 structures in kernel and "
    179 					      "user space do not match.");
    180 				isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    181 					      ISC_LOGMODULE_SOCKET,
    182 					      ISC_LOG_ERROR,
    183 					      "IPv6 is not supported.");
    184 				result = ISC_R_NOTFOUND;
    185 			}
    186 		}
    187 	}
    188 
    189 	(void)close(s);
    190 
    191 	return (result);
    192 }
    193 
    194 static void
    195 initialize_action(void) {
    196 	ipv4_result = try_proto(PF_INET);
    197 	ipv6_result = try_proto(PF_INET6);
    198 #ifdef ISC_PLATFORM_HAVESYSUNH
    199 	unix_result = try_proto(PF_UNIX);
    200 #endif /* ifdef ISC_PLATFORM_HAVESYSUNH */
    201 }
    202 
    203 static void
    204 initialize(void) {
    205 	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
    206 }
    207 
    208 isc_result_t
    209 isc_net_probeipv4(void) {
    210 	initialize();
    211 	return (ipv4_result);
    212 }
    213 
    214 isc_result_t
    215 isc_net_probeipv6(void) {
    216 	initialize();
    217 	return (ipv6_result);
    218 }
    219 
    220 isc_result_t
    221 isc_net_probeunix(void) {
    222 	initialize();
    223 	return (unix_result);
    224 }
    225 
    226 static void
    227 try_ipv6only(void) {
    228 #ifdef IPV6_V6ONLY
    229 	int s, on;
    230 	char strbuf[ISC_STRERRORSIZE];
    231 #endif /* ifdef IPV6_V6ONLY */
    232 	isc_result_t result;
    233 
    234 	result = isc_net_probeipv6();
    235 	if (result != ISC_R_SUCCESS) {
    236 		ipv6only_result = result;
    237 		return;
    238 	}
    239 
    240 #ifndef IPV6_V6ONLY
    241 	ipv6only_result = ISC_R_NOTFOUND;
    242 	return;
    243 #else  /* ifndef IPV6_V6ONLY */
    244 	/* check for TCP sockets */
    245 	s = socket(PF_INET6, SOCK_STREAM, 0);
    246 	if (s == -1) {
    247 		strerror_r(errno, strbuf, sizeof(strbuf));
    248 		UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s",
    249 				 strbuf);
    250 		ipv6only_result = ISC_R_UNEXPECTED;
    251 		return;
    252 	}
    253 
    254 	on = 1;
    255 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
    256 		ipv6only_result = ISC_R_NOTFOUND;
    257 		goto close;
    258 	}
    259 
    260 	close(s);
    261 
    262 	/* check for UDP sockets */
    263 	s = socket(PF_INET6, SOCK_DGRAM, 0);
    264 	if (s == -1) {
    265 		strerror_r(errno, strbuf, sizeof(strbuf));
    266 		UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s",
    267 				 strbuf);
    268 		ipv6only_result = ISC_R_UNEXPECTED;
    269 		return;
    270 	}
    271 
    272 	on = 1;
    273 	if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
    274 		ipv6only_result = ISC_R_NOTFOUND;
    275 		goto close;
    276 	}
    277 
    278 	ipv6only_result = ISC_R_SUCCESS;
    279 
    280 close:
    281 	close(s);
    282 	return;
    283 #endif /* IPV6_V6ONLY */
    284 }
    285 
    286 static void
    287 initialize_ipv6only(void) {
    288 	RUNTIME_CHECK(isc_once_do(&once_ipv6only, try_ipv6only) ==
    289 		      ISC_R_SUCCESS);
    290 }
    291 
    292 #ifdef __notyet__
    293 static void
    294 try_ipv6pktinfo(void) {
    295 	int s, on;
    296 	char strbuf[ISC_STRERRORSIZE];
    297 	isc_result_t result;
    298 	int optname;
    299 
    300 	result = isc_net_probeipv6();
    301 	if (result != ISC_R_SUCCESS) {
    302 		ipv6pktinfo_result = result;
    303 		return;
    304 	}
    305 
    306 	/* we only use this for UDP sockets */
    307 	s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    308 	if (s == -1) {
    309 		strerror_r(errno, strbuf, sizeof(strbuf));
    310 		UNEXPECTED_ERROR(__FILE__, __LINE__, "socket() failed: %s",
    311 				 strbuf);
    312 		ipv6pktinfo_result = ISC_R_UNEXPECTED;
    313 		return;
    314 	}
    315 
    316 #ifdef IPV6_RECVPKTINFO
    317 	optname = IPV6_RECVPKTINFO;
    318 #else  /* ifdef IPV6_RECVPKTINFO */
    319 	optname = IPV6_PKTINFO;
    320 #endif /* ifdef IPV6_RECVPKTINFO */
    321 	on = 1;
    322 	if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) {
    323 		ipv6pktinfo_result = ISC_R_NOTFOUND;
    324 		goto close;
    325 	}
    326 
    327 	ipv6pktinfo_result = ISC_R_SUCCESS;
    328 
    329 close:
    330 	close(s);
    331 	return;
    332 }
    333 
    334 static void
    335 initialize_ipv6pktinfo(void) {
    336 	RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, try_ipv6pktinfo) ==
    337 		      ISC_R_SUCCESS);
    338 }
    339 #endif /* ifdef __notyet__ */
    340 
    341 isc_result_t
    342 isc_net_probe_ipv6only(void) {
    343 	initialize_ipv6only();
    344 	return (ipv6only_result);
    345 }
    346 
    347 isc_result_t
    348 isc_net_probe_ipv6pktinfo(void) {
    349 /*
    350  * XXXWPK if pktinfo were supported then we could listen on :: for ipv6 and get
    351  * the information about the destination address from pktinfo structure passed
    352  * in recvmsg but this method is not portable and libuv doesn't support it - so
    353  * we need to listen on all interfaces.
    354  * We should verify that this doesn't impact performance (we already do it for
    355  * ipv4) and either remove all the ipv6pktinfo detection code from above
    356  * or think of fixing libuv.
    357  */
    358 #ifdef __notyet__
    359 	initialize_ipv6pktinfo();
    360 #endif /* ifdef __notyet__ */
    361 	return (ipv6pktinfo_result);
    362 }
    363 
    364 #if ISC_CMSG_IP_TOS || defined(IPV6_TCLASS)
    365 
    366 static socklen_t
    367 cmsg_len(socklen_t len) {
    368 #ifdef CMSG_LEN
    369 	return (CMSG_LEN(len));
    370 #else  /* ifdef CMSG_LEN */
    371 	socklen_t hdrlen;
    372 
    373 	/*
    374 	 * Cast NULL so that any pointer arithmetic performed by CMSG_DATA
    375 	 * is correct.
    376 	 */
    377 	hdrlen = (socklen_t)CMSG_DATA(((struct cmsghdr *)NULL));
    378 	return (hdrlen + len);
    379 #endif /* ifdef CMSG_LEN */
    380 }
    381 
    382 static socklen_t
    383 cmsg_space(socklen_t len) {
    384 #ifdef CMSG_SPACE
    385 	return (CMSG_SPACE(len));
    386 #else  /* ifdef CMSG_SPACE */
    387 	struct msghdr msg;
    388 	struct cmsghdr *cmsgp;
    389 	/*
    390 	 * XXX: The buffer length is an ad-hoc value, but should be enough
    391 	 * in a practical sense.
    392 	 */
    393 	char dummybuf[sizeof(struct cmsghdr) + 1024];
    394 
    395 	memset(&msg, 0, sizeof(msg));
    396 	msg.msg_control = dummybuf;
    397 	msg.msg_controllen = sizeof(dummybuf);
    398 
    399 	cmsgp = (struct cmsghdr *)dummybuf;
    400 	cmsgp->cmsg_len = cmsg_len(len);
    401 
    402 	cmsgp = CMSG_NXTHDR(&msg, cmsgp);
    403 	if (cmsgp != NULL) {
    404 		return ((char *)cmsgp - (char *)msg.msg_control);
    405 	} else {
    406 		return (0);
    407 	}
    408 #endif /* ifdef CMSG_SPACE */
    409 }
    410 
    411 /*
    412  * Make a fd non-blocking.
    413  */
    414 static isc_result_t
    415 make_nonblock(int fd) {
    416 	int ret;
    417 	int flags;
    418 	char strbuf[ISC_STRERRORSIZE];
    419 #ifdef USE_FIONBIO_IOCTL
    420 	int on = 1;
    421 
    422 	ret = ioctl(fd, FIONBIO, (char *)&on);
    423 #else  /* ifdef USE_FIONBIO_IOCTL */
    424 	flags = fcntl(fd, F_GETFL, 0);
    425 	flags |= PORT_NONBLOCK;
    426 	ret = fcntl(fd, F_SETFL, flags);
    427 #endif /* ifdef USE_FIONBIO_IOCTL */
    428 
    429 	if (ret == -1) {
    430 		strerror_r(errno, strbuf, sizeof(strbuf));
    431 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    432 #ifdef USE_FIONBIO_IOCTL
    433 				 "ioctl(%d, FIONBIO, &on): %s", fd,
    434 #else  /* ifdef USE_FIONBIO_IOCTL */
    435 				 "fcntl(%d, F_SETFL, %d): %s", fd, flags,
    436 #endif /* ifdef USE_FIONBIO_IOCTL */
    437 				 strbuf);
    438 
    439 		return (ISC_R_UNEXPECTED);
    440 	}
    441 
    442 	return (ISC_R_SUCCESS);
    443 }
    444 
    445 static bool
    446 cmsgsend(int s, int level, int type, struct addrinfo *res) {
    447 	char strbuf[ISC_STRERRORSIZE];
    448 	struct sockaddr_storage ss;
    449 	socklen_t len = sizeof(ss);
    450 	struct msghdr msg;
    451 	union {
    452 		struct cmsghdr h;
    453 		unsigned char b[256];
    454 	} control;
    455 	struct cmsghdr *cmsgp;
    456 	int dscp = (46 << 2); /* Expedited forwarding. */
    457 	struct iovec iovec;
    458 	char buf[1] = { 0 };
    459 	isc_result_t result;
    460 
    461 	if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
    462 		strerror_r(errno, strbuf, sizeof(strbuf));
    463 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    464 			      ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10),
    465 			      "bind: %s", strbuf);
    466 		return (false);
    467 	}
    468 
    469 	if (getsockname(s, (struct sockaddr *)&ss, &len) < 0) {
    470 		strerror_r(errno, strbuf, sizeof(strbuf));
    471 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    472 			      ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10),
    473 			      "getsockname: %s", strbuf);
    474 		return (false);
    475 	}
    476 
    477 	iovec.iov_base = buf;
    478 	iovec.iov_len = sizeof(buf);
    479 
    480 	memset(&msg, 0, sizeof(msg));
    481 	msg.msg_name = (struct sockaddr *)&ss;
    482 	msg.msg_namelen = len;
    483 	msg.msg_iov = &iovec;
    484 	msg.msg_iovlen = 1;
    485 	msg.msg_control = (void *)&control;
    486 	msg.msg_controllen = 0;
    487 	msg.msg_flags = 0;
    488 
    489 	cmsgp = msg.msg_control;
    490 
    491 	switch (type) {
    492 #ifdef IP_TOS
    493 	case IP_TOS:
    494 		memset(cmsgp, 0, cmsg_space(sizeof(char)));
    495 		cmsgp->cmsg_level = level;
    496 		cmsgp->cmsg_type = type;
    497 		cmsgp->cmsg_len = cmsg_len(sizeof(char));
    498 		*(unsigned char *)CMSG_DATA(cmsgp) = dscp;
    499 		msg.msg_controllen += cmsg_space(sizeof(char));
    500 		break;
    501 #endif /* ifdef IP_TOS */
    502 #ifdef IPV6_TCLASS
    503 	case IPV6_TCLASS:
    504 		memset(cmsgp, 0, cmsg_space(sizeof(dscp)));
    505 		cmsgp->cmsg_level = level;
    506 		cmsgp->cmsg_type = type;
    507 		cmsgp->cmsg_len = cmsg_len(sizeof(dscp));
    508 		memmove(CMSG_DATA(cmsgp), &dscp, sizeof(dscp));
    509 		msg.msg_controllen += cmsg_space(sizeof(dscp));
    510 		break;
    511 #endif /* ifdef IPV6_TCLASS */
    512 	default:
    513 		UNREACHABLE();
    514 	}
    515 
    516 	if (sendmsg(s, &msg, 0) < 0) {
    517 		int debug = ISC_LOG_DEBUG(10);
    518 		const char *typestr;
    519 		switch (errno) {
    520 #ifdef ENOPROTOOPT
    521 		case ENOPROTOOPT:
    522 #endif /* ifdef ENOPROTOOPT */
    523 #ifdef EOPNOTSUPP
    524 		case EOPNOTSUPP:
    525 #endif /* ifdef EOPNOTSUPP */
    526 		case EINVAL:
    527 		case EPERM:
    528 			break;
    529 		default:
    530 			debug = ISC_LOG_NOTICE;
    531 		}
    532 		strerror_r(errno, strbuf, sizeof(strbuf));
    533 		if (debug != ISC_LOG_NOTICE) {
    534 			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    535 				      ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10),
    536 				      "sendmsg: %s", strbuf);
    537 		} else {
    538 			typestr = (type == IP_TOS) ? "IP_TOS" : "IPV6_TCLASS";
    539 			UNEXPECTED_ERROR(__FILE__, __LINE__,
    540 					 "probing "
    541 					 "sendmsg() with %s=%02x failed: %s",
    542 					 typestr, dscp, strbuf);
    543 		}
    544 		return (false);
    545 	}
    546 
    547 	/*
    548 	 * Make sure the message actually got sent.
    549 	 */
    550 	result = make_nonblock(s);
    551 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    552 
    553 	iovec.iov_base = buf;
    554 	iovec.iov_len = sizeof(buf);
    555 
    556 	memset(&msg, 0, sizeof(msg));
    557 	msg.msg_name = (struct sockaddr *)&ss;
    558 	msg.msg_namelen = sizeof(ss);
    559 	msg.msg_iov = &iovec;
    560 	msg.msg_iovlen = 1;
    561 	msg.msg_control = NULL;
    562 	msg.msg_controllen = 0;
    563 	msg.msg_flags = 0;
    564 
    565 	if (recvmsg(s, &msg, 0) < 0) {
    566 		return (false);
    567 	}
    568 
    569 	return (true);
    570 }
    571 #endif /* if ISC_CMSG_IP_TOS || defined(IPV6_TCLASS) */
    572 
    573 static void
    574 try_dscp_v4(void) {
    575 #ifdef IP_TOS
    576 	char strbuf[ISC_STRERRORSIZE];
    577 	struct addrinfo hints, *res0;
    578 	int s, dscp = 0, n;
    579 #ifdef IP_RECVTOS
    580 	int on = 1;
    581 #endif /* IP_RECVTOS */
    582 
    583 	memset(&hints, 0, sizeof(hints));
    584 	hints.ai_family = AF_INET;
    585 	hints.ai_socktype = SOCK_DGRAM;
    586 	hints.ai_protocol = IPPROTO_UDP;
    587 #ifdef AI_NUMERICHOST
    588 	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
    589 #else  /* ifdef AI_NUMERICHOST */
    590 	hints.ai_flags = AI_PASSIVE;
    591 #endif /* ifdef AI_NUMERICHOST */
    592 
    593 	n = getaddrinfo("127.0.0.1", NULL, &hints, &res0);
    594 	if (n != 0 || res0 == NULL) {
    595 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    596 			      ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10),
    597 			      "getaddrinfo(127.0.0.1): %s", gai_strerror(n));
    598 		return;
    599 	}
    600 
    601 	s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol);
    602 
    603 	if (s == -1) {
    604 		strerror_r(errno, strbuf, sizeof(strbuf));
    605 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    606 			      ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10),
    607 			      "socket: %s", strbuf);
    608 		freeaddrinfo(res0);
    609 		return;
    610 	}
    611 
    612 	if (setsockopt(s, IPPROTO_IP, IP_TOS, &dscp, sizeof(dscp)) == 0) {
    613 		dscp_result |= ISC_NET_DSCPSETV4;
    614 	}
    615 
    616 #ifdef IP_RECVTOS
    617 	on = 1;
    618 	if (setsockopt(s, IPPROTO_IP, IP_RECVTOS, &on, sizeof(on)) == 0) {
    619 		dscp_result |= ISC_NET_DSCPRECVV4;
    620 	}
    621 #endif /* IP_RECVTOS */
    622 
    623 #if ISC_CMSG_IP_TOS
    624 	if (cmsgsend(s, IPPROTO_IP, IP_TOS, res0)) {
    625 		dscp_result |= ISC_NET_DSCPPKTV4;
    626 	}
    627 #endif /* ISC_CMSG_IP_TOS */
    628 
    629 	freeaddrinfo(res0);
    630 	close(s);
    631 
    632 #endif /* IP_TOS */
    633 }
    634 
    635 static void
    636 try_dscp_v6(void) {
    637 #ifdef IPV6_TCLASS
    638 	char strbuf[ISC_STRERRORSIZE];
    639 	struct addrinfo hints, *res0;
    640 	int s, dscp = 0, n;
    641 #if defined(IPV6_RECVTCLASS)
    642 	int on = 1;
    643 #endif /* IPV6_RECVTCLASS */
    644 
    645 	memset(&hints, 0, sizeof(hints));
    646 	hints.ai_family = AF_INET6;
    647 	hints.ai_socktype = SOCK_DGRAM;
    648 	hints.ai_protocol = IPPROTO_UDP;
    649 #ifdef AI_NUMERICHOST
    650 	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
    651 #else  /* ifdef AI_NUMERICHOST */
    652 	hints.ai_flags = AI_PASSIVE;
    653 #endif /* ifdef AI_NUMERICHOST */
    654 
    655 	n = getaddrinfo("::1", NULL, &hints, &res0);
    656 	if (n != 0 || res0 == NULL) {
    657 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    658 			      ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10),
    659 			      "getaddrinfo(::1): %s", gai_strerror(n));
    660 		return;
    661 	}
    662 
    663 	s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol);
    664 	if (s == -1) {
    665 		strerror_r(errno, strbuf, sizeof(strbuf));
    666 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    667 			      ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10),
    668 			      "socket: %s", strbuf);
    669 		freeaddrinfo(res0);
    670 		return;
    671 	}
    672 	if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &dscp, sizeof(dscp)) == 0)
    673 	{
    674 		dscp_result |= ISC_NET_DSCPSETV6;
    675 	}
    676 
    677 #ifdef IPV6_RECVTCLASS
    678 	on = 1;
    679 	if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVTCLASS, &on, sizeof(on)) == 0)
    680 	{
    681 		dscp_result |= ISC_NET_DSCPRECVV6;
    682 	}
    683 #endif /* IPV6_RECVTCLASS */
    684 
    685 	if (cmsgsend(s, IPPROTO_IPV6, IPV6_TCLASS, res0)) {
    686 		dscp_result |= ISC_NET_DSCPPKTV6;
    687 	}
    688 
    689 	freeaddrinfo(res0);
    690 	close(s);
    691 
    692 #endif /* IPV6_TCLASS */
    693 }
    694 
    695 static void
    696 try_dscp(void) {
    697 	try_dscp_v4();
    698 	try_dscp_v6();
    699 }
    700 
    701 static void
    702 initialize_dscp(void) {
    703 	RUNTIME_CHECK(isc_once_do(&once_dscp, try_dscp) == ISC_R_SUCCESS);
    704 }
    705 
    706 unsigned int
    707 isc_net_probedscp(void) {
    708 	initialize_dscp();
    709 	return (dscp_result);
    710 }
    711 
    712 #if defined(USE_SYSCTL_PORTRANGE)
    713 #if defined(HAVE_SYSCTLBYNAME)
    714 static isc_result_t
    715 getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) {
    716 	int port_low, port_high;
    717 	size_t portlen;
    718 	const char *sysctlname_lowport, *sysctlname_hiport;
    719 
    720 	if (af == AF_INET) {
    721 		sysctlname_lowport = SYSCTL_V4PORTRANGE_LOW;
    722 		sysctlname_hiport = SYSCTL_V4PORTRANGE_HIGH;
    723 	} else {
    724 		sysctlname_lowport = SYSCTL_V6PORTRANGE_LOW;
    725 		sysctlname_hiport = SYSCTL_V6PORTRANGE_HIGH;
    726 	}
    727 	portlen = sizeof(port_low);
    728 	if (sysctlbyname(sysctlname_lowport, &port_low, &portlen, NULL, 0) < 0)
    729 	{
    730 		return (ISC_R_FAILURE);
    731 	}
    732 	portlen = sizeof(port_high);
    733 	if (sysctlbyname(sysctlname_hiport, &port_high, &portlen, NULL, 0) < 0)
    734 	{
    735 		return (ISC_R_FAILURE);
    736 	}
    737 	if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) {
    738 		return (ISC_R_RANGE);
    739 	}
    740 
    741 	*low = (in_port_t)port_low;
    742 	*high = (in_port_t)port_high;
    743 
    744 	return (ISC_R_SUCCESS);
    745 }
    746 #else  /* !HAVE_SYSCTLBYNAME */
    747 static isc_result_t
    748 getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) {
    749 	int mib_lo4[4] = SYSCTL_V4PORTRANGE_LOW;
    750 	int mib_hi4[4] = SYSCTL_V4PORTRANGE_HIGH;
    751 	int mib_lo6[4] = SYSCTL_V6PORTRANGE_LOW;
    752 	int mib_hi6[4] = SYSCTL_V6PORTRANGE_HIGH;
    753 	int *mib_lo, *mib_hi, miblen;
    754 	int port_low, port_high;
    755 	size_t portlen;
    756 
    757 	if (af == AF_INET) {
    758 		mib_lo = mib_lo4;
    759 		mib_hi = mib_hi4;
    760 		miblen = sizeof(mib_lo4) / sizeof(mib_lo4[0]);
    761 	} else {
    762 		mib_lo = mib_lo6;
    763 		mib_hi = mib_hi6;
    764 		miblen = sizeof(mib_lo6) / sizeof(mib_lo6[0]);
    765 	}
    766 
    767 	portlen = sizeof(port_low);
    768 	if (sysctl(mib_lo, miblen, &port_low, &portlen, NULL, 0) < 0) {
    769 		return (ISC_R_FAILURE);
    770 	}
    771 
    772 	portlen = sizeof(port_high);
    773 	if (sysctl(mib_hi, miblen, &port_high, &portlen, NULL, 0) < 0) {
    774 		return (ISC_R_FAILURE);
    775 	}
    776 
    777 	if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) {
    778 		return (ISC_R_RANGE);
    779 	}
    780 
    781 	*low = (in_port_t)port_low;
    782 	*high = (in_port_t)port_high;
    783 
    784 	return (ISC_R_SUCCESS);
    785 }
    786 #endif /* HAVE_SYSCTLBYNAME */
    787 #endif /* USE_SYSCTL_PORTRANGE */
    788 
    789 isc_result_t
    790 isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
    791 	int result = ISC_R_FAILURE;
    792 #if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux)
    793 	FILE *fp;
    794 #endif /* if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) */
    795 
    796 	REQUIRE(low != NULL && high != NULL);
    797 
    798 #if defined(USE_SYSCTL_PORTRANGE)
    799 	result = getudpportrange_sysctl(af, low, high);
    800 #elif defined(__linux)
    801 
    802 	UNUSED(af);
    803 
    804 	/*
    805 	 * Linux local ports are address family agnostic.
    806 	 */
    807 	fp = fopen("/proc/sys/net/ipv4/ip_local_port_range", "r");
    808 	if (fp != NULL) {
    809 		int n;
    810 		unsigned int l, h;
    811 
    812 		n = fscanf(fp, "%u %u", &l, &h);
    813 		if (n == 2 && (l & ~0xffff) == 0 && (h & ~0xffff) == 0) {
    814 			*low = l;
    815 			*high = h;
    816 			result = ISC_R_SUCCESS;
    817 		}
    818 		fclose(fp);
    819 	}
    820 #else  /* if defined(USE_SYSCTL_PORTRANGE) */
    821 	UNUSED(af);
    822 #endif /* if defined(USE_SYSCTL_PORTRANGE) */
    823 
    824 	if (result != ISC_R_SUCCESS) {
    825 		*low = ISC_NET_PORTRANGELOW;
    826 		*high = ISC_NET_PORTRANGEHIGH;
    827 	}
    828 
    829 	return (ISC_R_SUCCESS); /* we currently never fail in this function */
    830 }
    831 
    832 void
    833 isc_net_disableipv4(void) {
    834 	initialize();
    835 	if (ipv4_result == ISC_R_SUCCESS) {
    836 		ipv4_result = ISC_R_DISABLED;
    837 	}
    838 }
    839 
    840 void
    841 isc_net_disableipv6(void) {
    842 	initialize();
    843 	if (ipv6_result == ISC_R_SUCCESS) {
    844 		ipv6_result = ISC_R_DISABLED;
    845 	}
    846 }
    847 
    848 void
    849 isc_net_enableipv4(void) {
    850 	initialize();
    851 	if (ipv4_result == ISC_R_DISABLED) {
    852 		ipv4_result = ISC_R_SUCCESS;
    853 	}
    854 }
    855 
    856 void
    857 isc_net_enableipv6(void) {
    858 	initialize();
    859 	if (ipv6_result == ISC_R_DISABLED) {
    860 		ipv6_result = ISC_R_SUCCESS;
    861 	}
    862 }
    863