xref: /netbsd-src/external/mpl/dhcp/bind/dist/lib/isc/netmgr/uv-compat.c (revision 4afad4b7fa6d4a0d3dedf41d1587a7250710ae54)
1 /*	$NetBSD: uv-compat.c,v 1.1 2024/02/18 20:57:55 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 "uv-compat.h"
17 #include <unistd.h>
18 
19 #include <isc/util.h>
20 
21 #include "netmgr-int.h"
22 
23 #if UV_VERSION_HEX < UV_VERSION(1, 27, 0)
24 int
isc_uv_udp_connect(uv_udp_t * handle,const struct sockaddr * addr)25 isc_uv_udp_connect(uv_udp_t *handle, const struct sockaddr *addr) {
26 	int err = 0;
27 
28 	do {
29 		int addrlen = (addr->sa_family == AF_INET)
30 				      ? sizeof(struct sockaddr_in)
31 				      : sizeof(struct sockaddr_in6);
32 #ifdef WIN32
33 		err = connect(handle->socket, addr, addrlen);
34 #else  /* WIN32 */
35 		err = connect(handle->io_watcher.fd, addr, addrlen);
36 #endif /* WIN32 */
37 	} while (err == -1 && errno == EINTR);
38 
39 	if (err) {
40 #ifdef WIN32
41 		return (uv_translate_sys_error(err));
42 #else /* WIN32 */
43 #if UV_VERSION_HEX >= UV_VERSION(1, 10, 0)
44 		return (uv_translate_sys_error(errno));
45 #else
46 		return (-errno);
47 #endif /* UV_VERSION_HEX >= UV_VERSION(1, 10, 0) */
48 #endif /* WIN32 */
49 	}
50 
51 	return (0);
52 }
53 #endif /* UV_VERSION_HEX < UV_VERSION(1, 27, 0) */
54 
55 #if UV_VERSION_HEX < UV_VERSION(1, 32, 0)
56 int
uv_tcp_close_reset(uv_tcp_t * handle,uv_close_cb close_cb)57 uv_tcp_close_reset(uv_tcp_t *handle, uv_close_cb close_cb) {
58 	if (setsockopt(handle->io_watcher.fd, SOL_SOCKET, SO_LINGER,
59 		       &(struct linger){ 1, 0 }, sizeof(struct linger)) == -1)
60 	{
61 #if UV_VERSION_HEX >= UV_VERSION(1, 10, 0)
62 		return (uv_translate_sys_error(errno));
63 #else
64 		return (-errno);
65 #endif /* UV_VERSION_HEX >= UV_VERSION(1, 10, 0) */
66 	}
67 
68 	uv_close((uv_handle_t *)handle, close_cb);
69 	return (0);
70 }
71 #endif /* UV_VERSION_HEX < UV_VERSION(1, 32, 0) */
72 
73 int
isc_uv_udp_freebind(uv_udp_t * handle,const struct sockaddr * addr,unsigned int flags)74 isc_uv_udp_freebind(uv_udp_t *handle, const struct sockaddr *addr,
75 		    unsigned int flags) {
76 	int r;
77 	uv_os_sock_t fd;
78 
79 	r = uv_fileno((const uv_handle_t *)handle, (uv_os_fd_t *)&fd);
80 	if (r < 0) {
81 		return (r);
82 	}
83 
84 #if defined(WIN32)
85 	REQUIRE(fd != INVALID_SOCKET);
86 #endif
87 
88 	r = uv_udp_bind(handle, addr, flags);
89 	if (r == UV_EADDRNOTAVAIL &&
90 	    isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS)
91 	{
92 		/*
93 		 * Retry binding with IP_FREEBIND (or equivalent option) if the
94 		 * address is not available. This helps with IPv6 tentative
95 		 * addresses which are reported by the route socket, although
96 		 * named is not yet able to properly bind to them.
97 		 */
98 		r = uv_udp_bind(handle, addr, flags);
99 	}
100 
101 	return (r);
102 }
103 
104 static int
isc__uv_tcp_bind_now(uv_tcp_t * handle,const struct sockaddr * addr,unsigned int flags)105 isc__uv_tcp_bind_now(uv_tcp_t *handle, const struct sockaddr *addr,
106 		     unsigned int flags) {
107 	int r;
108 	struct sockaddr_storage sname;
109 	int snamelen = sizeof(sname);
110 
111 	r = uv_tcp_bind(handle, addr, flags);
112 	if (r < 0) {
113 		return (r);
114 	}
115 
116 	/*
117 	 * uv_tcp_bind() uses a delayed error, initially returning
118 	 * success even if bind() fails. By calling uv_tcp_getsockname()
119 	 * here we can find out whether the bind() call was successful.
120 	 */
121 	r = uv_tcp_getsockname(handle, (struct sockaddr *)&sname, &snamelen);
122 	if (r < 0) {
123 		return (r);
124 	}
125 
126 	return (0);
127 }
128 
129 int
isc_uv_tcp_freebind(uv_tcp_t * handle,const struct sockaddr * addr,unsigned int flags)130 isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr,
131 		    unsigned int flags) {
132 	int r;
133 	uv_os_sock_t fd;
134 
135 	r = uv_fileno((const uv_handle_t *)handle, (uv_os_fd_t *)&fd);
136 	if (r < 0) {
137 		return (r);
138 	}
139 
140 	r = isc__uv_tcp_bind_now(handle, addr, flags);
141 	if (r == UV_EADDRNOTAVAIL &&
142 	    isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS)
143 	{
144 		/*
145 		 * Retry binding with IP_FREEBIND (or equivalent option) if the
146 		 * address is not available. This helps with IPv6 tentative
147 		 * addresses which are reported by the route socket, although
148 		 * named is not yet able to properly bind to them.
149 		 */
150 		r = isc__uv_tcp_bind_now(handle, addr, flags);
151 	}
152 
153 	return (r);
154 }
155