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