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