diff --git a/lib/isc/include/isc/uv.h b/lib/isc/include/isc/uv.h new file mode 100644 index 0000000000..e1c46a2334 --- /dev/null +++ b/lib/isc/include/isc/uv.h @@ -0,0 +1,124 @@ +/* + * 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 +#include + +#include + +/* + * These functions were introduced in newer libuv, but we still + * want BIND9 compile on older ones so we emulate them. + * They're inline to avoid conflicts when running with a newer + * library version. + */ + +#define UV_VERSION(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) + +#if !defined(UV__ERR) +#define UV__ERR(x) (-(x)) +#endif + +#if UV_VERSION_HEX < UV_VERSION(1, 19, 0) +static inline void * +uv_handle_get_data(const uv_handle_t *handle) { + return (handle->data); +} + +static inline void +uv_handle_set_data(uv_handle_t *handle, void *data) { + handle->data = data; +} + +static inline void * +uv_req_get_data(const uv_req_t *req) { + return (req->data); +} + +static inline void +uv_req_set_data(uv_req_t *req, void *data) { + req->data = data; +} +#endif /* UV_VERSION_HEX < UV_VERSION(1, 19, 0) */ + +#if UV_VERSION_HEX < UV_VERSION(1, 32, 0) +int +uv_tcp_close_reset(uv_tcp_t *handle, uv_close_cb close_cb); +#endif + +#if UV_VERSION_HEX < UV_VERSION(1, 34, 0) +#define uv_sleep(msec) usleep(msec * 1000) +#endif /* UV_VERSION_HEX < UV_VERSION(1, 34, 0) */ + +#if UV_VERSION_HEX < UV_VERSION(1, 27, 0) +int +isc_uv_udp_connect(uv_udp_t *handle, const struct sockaddr *addr); +/*%< + * Associate the UDP handle to a remote address and port, so every message sent + * by this handle is automatically sent to that destination. + * + * NOTE: This is just a limited shim for uv_udp_connect() as it requires the + * handle to be bound. + */ +#else /* UV_VERSION_HEX < UV_VERSION(1, 27, 0) */ +#define isc_uv_udp_connect uv_udp_connect +#endif /* UV_VERSION_HEX < UV_VERSION(1, 27, 0) */ + +#if UV_VERSION_HEX < UV_VERSION(1, 12, 0) +#include +#include + +static inline int +uv_os_getenv(const char *name, char *buffer, size_t *size) { + size_t len; + char *buf = getenv(name); + + if (buf == NULL) { + return (UV_ENOENT); + } + + len = strlen(buf) + 1; + if (len > *size) { + *size = len; + return (UV_ENOBUFS); + } + + *size = len; + memmove(buffer, buf, len); + + return (0); +} + +#define uv_os_setenv(name, value) setenv(name, value, 0) +#endif /* UV_VERSION_HEX < UV_VERSION(1, 12, 0) */ + +int +isc_uv_udp_freebind(uv_udp_t *handle, const struct sockaddr *addr, + unsigned int flags); + +int +isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr, + unsigned int flags); + +#define isc__nm_uverr2result(x) \ + isc___nm_uverr2result(x, true, __FILE__, __LINE__, __func__) +isc_result_t +isc___nm_uverr2result(int uverr, bool dolog, const char *file, + unsigned int line, const char *func); + +/* FIXME: Deduplicate with netmgr-int.h */ +#define UV_RUNTIME_CHECK(func, ret) \ + if (ret != 0) { \ + isc_error_fatal(__FILE__, __LINE__, "%s failed: %s\n", #func, \ + uv_strerror(ret)); \ + } diff --git a/lib/isc/include/isc/work.h b/lib/isc/include/isc/work.h new file mode 100644 index 0000000000..e60f5ee002 --- /dev/null +++ b/lib/isc/include/isc/work.h @@ -0,0 +1,21 @@ +/* + * 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 + +#include +#include + +void +isc_queue_work(isc_loop_t *loop, isc_work_cb work_cb, + isc_after_work_cb after_work_cb, void *cbarg); diff --git a/lib/isc/netmgr/uv-compat.c b/lib/isc/uv.c similarity index 51% rename from lib/isc/netmgr/uv-compat.c rename to lib/isc/uv.c index b7c0f7b0e2..b811308b6a 100644 --- a/lib/isc/netmgr/uv-compat.c +++ b/lib/isc/uv.c @@ -11,12 +11,10 @@ * information regarding copyright ownership. */ -#include "uv-compat.h" #include #include - -#include "netmgr-int.h" +#include #if UV_VERSION_HEX < UV_VERSION(1, 27, 0) int @@ -60,6 +58,50 @@ uv_tcp_close_reset(uv_tcp_t *handle, uv_close_cb close_cb) { } #endif /* UV_VERSION_HEX < UV_VERSION(1, 32, 0) */ +#define setsockopt_on(socket, level, name) \ + setsockopt(socket, level, name, &(int){ 1 }, sizeof(int)) + +static isc_result_t +isc__socket_freebind(uv_os_sock_t fd, sa_family_t sa_family) { + /* + * Set the IP_FREEBIND (or equivalent option) on the uv_handle. + */ +#ifdef IP_FREEBIND + UNUSED(sa_family); + if (setsockopt_on(fd, IPPROTO_IP, IP_FREEBIND) == -1) { + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +#elif defined(IP_BINDANY) || defined(IPV6_BINDANY) + if (sa_family == AF_INET) { +#if defined(IP_BINDANY) + if (setsockopt_on(fd, IPPROTO_IP, IP_BINDANY) == -1) { + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +#endif + } else if (sa_family == AF_INET6) { +#if defined(IPV6_BINDANY) + if (setsockopt_on(fd, IPPROTO_IPV6, IPV6_BINDANY) == -1) { + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +#endif + } + return (ISC_R_NOTIMPLEMENTED); +#elif defined(SO_BINDANY) + UNUSED(sa_family); + if (setsockopt_on(fd, SOL_SOCKET, SO_BINDANY) == -1) { + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +#else + UNUSED(fd); + UNUSED(sa_family); + return (ISC_R_NOTIMPLEMENTED); +#endif +} + int isc_uv_udp_freebind(uv_udp_t *handle, const struct sockaddr *addr, unsigned int flags) { @@ -73,7 +115,7 @@ isc_uv_udp_freebind(uv_udp_t *handle, const struct sockaddr *addr, r = uv_udp_bind(handle, addr, flags); if (r == UV_EADDRNOTAVAIL && - isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS) + isc__socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS) { /* * Retry binding with IP_FREEBIND (or equivalent option) if the @@ -125,7 +167,7 @@ isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr, r = isc__uv_tcp_bind_now(handle, addr, flags); if (r == UV_EADDRNOTAVAIL && - isc__nm_socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS) + isc__socket_freebind(fd, addr->sa_family) == ISC_R_SUCCESS) { /* * Retry binding with IP_FREEBIND (or equivalent option) if the @@ -138,3 +180,85 @@ isc_uv_tcp_freebind(uv_tcp_t *handle, const struct sockaddr *addr, return (r); } + +/*% + * Convert a libuv error value into an isc_result_t. The + * list of supported error values is not complete; new users + * of this function should add any expected errors that are + * not already there. + */ +isc_result_t +isc___nm_uverr2result(int uverr, bool dolog, const char *file, + unsigned int line, const char *func) { + switch (uverr) { + case 0: + return (ISC_R_SUCCESS); + case UV_ENOTDIR: + case UV_ELOOP: + case UV_EINVAL: /* XXX sometimes this is not for files */ + case UV_ENAMETOOLONG: + case UV_EBADF: + return (ISC_R_INVALIDFILE); + case UV_ENOENT: + return (ISC_R_FILENOTFOUND); + case UV_EAGAIN: + return (ISC_R_NOCONN); + case UV_EACCES: + case UV_EPERM: + return (ISC_R_NOPERM); + case UV_EEXIST: + return (ISC_R_FILEEXISTS); + case UV_EIO: + return (ISC_R_IOERROR); + case UV_ENOMEM: + return (ISC_R_NOMEMORY); + case UV_ENFILE: + case UV_EMFILE: + return (ISC_R_TOOMANYOPENFILES); + case UV_ENOSPC: + return (ISC_R_DISCFULL); + case UV_EPIPE: + case UV_ECONNRESET: + case UV_ECONNABORTED: + return (ISC_R_CONNECTIONRESET); + case UV_ENOTCONN: + return (ISC_R_NOTCONNECTED); + case UV_ETIMEDOUT: + return (ISC_R_TIMEDOUT); + case UV_ENOBUFS: + return (ISC_R_NORESOURCES); + case UV_EAFNOSUPPORT: + return (ISC_R_FAMILYNOSUPPORT); + case UV_ENETDOWN: + return (ISC_R_NETDOWN); + case UV_EHOSTDOWN: + return (ISC_R_HOSTDOWN); + case UV_ENETUNREACH: + return (ISC_R_NETUNREACH); + case UV_EHOSTUNREACH: + return (ISC_R_HOSTUNREACH); + case UV_EADDRINUSE: + return (ISC_R_ADDRINUSE); + case UV_EADDRNOTAVAIL: + return (ISC_R_ADDRNOTAVAIL); + case UV_ECONNREFUSED: + return (ISC_R_CONNREFUSED); + case UV_ECANCELED: + return (ISC_R_CANCELED); + case UV_EOF: + return (ISC_R_EOF); + case UV_EMSGSIZE: + return (ISC_R_MAXSIZE); + case UV_ENOTSUP: + return (ISC_R_FAMILYNOSUPPORT); + default: + if (dolog) { + UNEXPECTED_ERROR( + file, line, + "unable to convert libuv " + "error code in %s to isc_result: %d: %s", + func, uverr, uv_strerror(uverr)); + } + return (ISC_R_UNEXPECTED); + } +} diff --git a/lib/isc/work.c b/lib/isc/work.c new file mode 100644 index 0000000000..a89e593686 --- /dev/null +++ b/lib/isc/work.c @@ -0,0 +1,67 @@ +/* + * 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 +#include + +#include +#include +#include + +#include "loop_p.h" + +static void +isc__work_cb(uv_work_t *req) { + isc_work_t *work = uv_req_get_data((uv_req_t *)req); + + work->work_cb(work->cbarg); +} + +static void +isc__after_work_cb(uv_work_t *req, int status) { + isc_result_t result = ISC_R_SUCCESS; + isc_work_t *work = uv_req_get_data((uv_req_t *)req); + + if (status != 0) { + result = isc__nm_uverr2result(status); + } + + work->after_work_cb(work->cbarg, result); + + isc_mem_put(work->mctx, work, sizeof(*work)); +} + +void +isc_queue_work(isc_loop_t *loop, isc_work_cb work_cb, + isc_after_work_cb after_work_cb, void *cbarg) { + isc_work_t *work = NULL; + int r; + + REQUIRE(VALID_LOOP(loop)); + REQUIRE(work_cb != NULL); + REQUIRE(after_work_cb != NULL); + + work = isc_mem_get(loop->mctx, sizeof(*work)); + *work = (isc_work_t){ + .work_cb = work_cb, + .after_work_cb = after_work_cb, + .cbarg = cbarg, + }; + isc_mem_attach(loop->mctx, &work->mctx); + + uv_req_set_data((uv_req_t *)&work->work, work); + + r = uv_queue_work(&loop->loop, &work->work, isc__work_cb, + isc__after_work_cb); + UV_RUNTIME_CHECK(uv_queue_work, r); +}