summaryrefslogtreecommitdiffstats
path: root/lib/isc/netmgr/uv-compat.c
blob: b7c0f7b0e29250297a8d52f56f0e093e9ba10232 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
 *
 * SPDX-License-Identifier: MPL-2.0
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
 */

#include "uv-compat.h"
#include <unistd.h>

#include <isc/util.h>

#include "netmgr-int.h"

#if UV_VERSION_HEX < UV_VERSION(1, 27, 0)
int
isc_uv_udp_connect(uv_udp_t *handle, const struct sockaddr *addr) {
	int err = 0;

	do {
		int addrlen = (addr->sa_family == AF_INET)
				      ? sizeof(struct sockaddr_in)
				      : sizeof(struct sockaddr_in6);
		err = connect(handle->io_watcher.fd, addr, addrlen);
	} while (err == -1 && errno == EINTR);

	if (err) {
#if UV_VERSION_HEX >= UV_VERSION(1, 10, 0)
		return (uv_translate_sys_error(errno));
#else
		return (-errno);
#endif /* UV_VERSION_HEX >= UV_VERSION(1, 10, 0) */
	}

	return (0);
}
#endif /* UV_VERSION_HEX < UV_VERSION(1, 27, 0) */

#if UV_VERSION_HEX < UV_VERSION(1, 32, 0)
int
uv_tcp_close_reset(uv_tcp_t *handle, uv_close_cb close_cb) {
	if (setsockopt(handle->io_watcher.fd, SOL_SOCKET, SO_LINGER,
		       &(struct linger){ 1, 0 }, sizeof(struct linger)) == -1)
	{
#if UV_VERSION_HEX >= UV_VERSION(1, 10, 0)
		return (uv_translate_sys_error(errno));
#else
		return (-errno);
#endif /* UV_VERSION_HEX >= UV_VERSION(1, 10, 0) */
	}

	uv_close((uv_handle_t *)handle, close_cb);
	return (0);
}
#endif /* UV_VERSION_HEX < UV_VERSION(1, 32, 0) */

int
isc_uv_udp_freebind(uv_udp_t *handle, const struct sockaddr *addr,
		    unsigned int flags) {
	int r;
	uv_os_sock_t fd;

	r = uv_fileno((const uv_handle_t *)handle, (uv_os_fd_t *)&fd);
	if (r < 0) {
		return (r);
	}

	r = uv_udp_bind(handle, addr, flags);
	if (r == UV_EADDRNOTAVAIL &&
	    isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS)
	{
		/*
		 * Retry binding with IP_FREEBIND (or equivalent option) if the
		 * address is not available. This helps with IPv6 tentative
		 * addresses which are reported by the route socket, although
		 * named is not yet able to properly bind to them.
		 */
		r = uv_udp_bind(handle, addr, flags);
	}

	return (r);
}

static int
isc__uv_tcp_bind_now(uv_tcp_t *handle, const struct sockaddr *addr,
		     unsigned int flags) {
	int r;
	struct sockaddr_storage sname;
	int snamelen = sizeof(sname);

	r = uv_tcp_bind(handle, addr, flags);
	if (r < 0) {
		return (r);
	}

	/*
	 * uv_tcp_bind() uses a delayed error, initially returning
	 * success even if bind() fails. By calling uv_tcp_getsockname()
	 * here we can find out whether the bind() call was successful.
	 */
	r = uv_tcp_getsockname(handle, (struct sockaddr *)&sname, &snamelen);
	if (r < 0) {
		return (r);
	}

	return (0);
}

int
isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr,
		    unsigned int flags) {
	int r;
	uv_os_sock_t fd;

	r = uv_fileno((const uv_handle_t *)handle, (uv_os_fd_t *)&fd);
	if (r < 0) {
		return (r);
	}

	r = isc__uv_tcp_bind_now(handle, addr, flags);
	if (r == UV_EADDRNOTAVAIL &&
	    isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS)
	{
		/*
		 * Retry binding with IP_FREEBIND (or equivalent option) if the
		 * address is not available. This helps with IPv6 tentative
		 * addresses which are reported by the route socket, although
		 * named is not yet able to properly bind to them.
		 */
		r = isc__uv_tcp_bind_now(handle, addr, flags);
	}

	return (r);
}