diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in index 906ebd3e69..a8986c5a45 100644 --- a/lib/isc/Makefile.in +++ b/lib/isc/Makefile.in @@ -55,7 +55,7 @@ OBJS = pk11.@O@ pk11_result.@O@ \ lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \ md.@O@ mem.@O@ mutexblock.@O@ \ netmgr/netmgr.@O@ netmgr/tcp.@O@ netmgr/udp.@O@ \ - netmgr/tcpdns.@O@ netmgr/uverr2result.@O@ \ + netmgr/tcpdns.@O@ netmgr/uverr2result.@O@ netmgr/uv-compat.@O@ \ netaddr.@O@ netscope.@O@ nonce.@O@ openssl_shim.@O@ pool.@O@ \ parseint.@O@ portset.@O@ queue.@O@ quota.@O@ \ radix.@O@ random.@O@ ratelimiter.@O@ \ diff --git a/lib/isc/netmgr/Makefile.in b/lib/isc/netmgr/Makefile.in index 74f0439521..f58caf5071 100644 --- a/lib/isc/netmgr/Makefile.in +++ b/lib/isc/netmgr/Makefile.in @@ -25,10 +25,10 @@ CDEFINES = CWARNINGS = # Alphabetically -OBJS = netmgr.@O@ tcp.@O@ udp.@O@ tcpdns.@O@ uverr2result.@O@ +OBJS = netmgr.@O@ tcp.@O@ udp.@O@ tcpdns.@O@ uverr2result.@O@ uv-compat.@O@ # Alphabetically -SRCS = netmgr.c tcp.c udp.c tcpdns.c uverr2result.c +SRCS = netmgr.c tcp.c udp.c tcpdns.c uverr2result.c uv-compat.c TARGETS = ${OBJS} diff --git a/lib/isc/netmgr/uv-compat.c b/lib/isc/netmgr/uv-compat.c new file mode 100644 index 0000000000..101c3c7201 --- /dev/null +++ b/lib/isc/netmgr/uv-compat.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include "uv-compat.h" + +/* + * XXXWPK: This code goes into libuv internals and it's platform dependent. + * It's ugly, we shouldn't do it, but the alternative with passing sockets + * over IPC sockets is even worse, and causes all kind of different + * problems. We should try to push these things upstream. + */ + +#ifdef WIN32 +/* This code is adapted from libuv/src/win/internal.h */ +typedef enum { + UV__IPC_SOCKET_XFER_NONE = 0, + UV__IPC_SOCKET_XFER_TCP_CONNECTION, + UV__IPC_SOCKET_XFER_TCP_SERVER +} uv__ipc_socket_xfer_type_t; + +typedef struct { + WSAPROTOCOL_INFOW socket_info; + uint32_t delayed_error; +} uv__ipc_socket_xfer_info_t; + +int +uv__tcp_xfer_import(uv_tcp_t *tcp, uv__ipc_socket_xfer_type_t xfer_type, + uv__ipc_socket_xfer_info_t *xfer_info); +int +uv__tcp_xfer_export(uv_tcp_t *handle, int pid, + uv__ipc_socket_xfer_info_t *xfer_info); + +int +isc_uv_export(uv_stream_t *stream, isc_uv_stream_info_t *info) { + if (stream->type != UV_TCP) { + return (-1); + } + if (uv__tcp_xfer_export((uv_tcp_t *) stream, GetCurrentProcessId(), + &info->socket_info) == -1) { + return (-1); + } + + info->type = UV_TCP; +} + +int +isc_uv_import(uv_stream_t *stream, isc_uv_stream_info_t *info) { + uv__ipc_socket_xfer_info_t xfer_info; + + if (stream->type != UV_TCP || info->type != UV_TCP) { + return (-1); + } + xfer_info.socket_info = info->socket_info; + + return (uv__tcp_xfer_import((uv_tcp_t *) stream, + UV__IPC_SOCKET_XFER_TCP_SERVER, + &xfer_info)); +} +#else /* WIN32 */ +/* Adapted from libuv/src/unix/internal.h */ +#include +#include + +static int +isc_uv__cloexec(int fd, int set) { + int r; + + /* + * This #ifdef is taken directly from the libuv sources. + * We use FIOCLEX and FIONCLEX ioctl() calls when possible, + * but on some platforms are not implemented, or defined but + * not implemented correctly. On those, we use the FD_CLOEXEC + * fcntl() call, which adds extra system call overhead, but + * works. + */ +#if defined(_AIX) || \ + defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__linux__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) + do { + r = ioctl(fd, set ? FIOCLEX : FIONCLEX); + } while (r == -1 && errno == EINTR); +#else /* FIOCLEX/FIONCLEX unsupported */ + int flags; + + do { + r = fcntl(fd, F_GETFD); + } while (r == -1 && errno == EINTR); + + if (r == -1) { + return (-1); + } + + if (!!(r & FD_CLOEXEC) == !!set) { + return (0); + } + + if (set) { + flags = r | FD_CLOEXEC; + } else { + flags = r & ~FD_CLOEXEC; + } + + do { + r = fcntl(fd, F_SETFD, flags); + } while (r == -1 && errno == EINTR); +#endif /* FIOCLEX/FIONCLEX unsupported */ + + if (r != 0) { + return (-1); + } + + return (0); +} + +int +isc_uv_export(uv_stream_t *stream, isc_uv_stream_info_t *info) { + int oldfd, fd; + int err; + + if (stream->type != UV_TCP) { + return (-1); + } + err = uv_fileno((uv_handle_t *) stream, (uv_os_fd_t *) &oldfd); + + if (err != 0) { + return (err); + } + + fd = dup(oldfd); + if (fd == -1) { + return (-1); + } + + err = isc_uv__cloexec(fd, 1); + if (err != 0) { + close(fd); + return (err); + } + + info->type = stream->type; + info->fd = fd; + return (0); +} + +int +isc_uv_import(uv_stream_t *stream, isc_uv_stream_info_t *info) { + if (info->type != UV_TCP) { + return (-1); + } + + uv_tcp_t *tcp = (uv_tcp_t *) stream; + return (uv_tcp_open(tcp, info->fd)); +} +#endif diff --git a/lib/isc/netmgr/uv-compat.h b/lib/isc/netmgr/uv-compat.h index 6ec2f3237b..092c87976c 100644 --- a/lib/isc/netmgr/uv-compat.h +++ b/lib/isc/netmgr/uv-compat.h @@ -13,22 +13,59 @@ #include /* - * Those functions were introduced in newer libuv, we still + * 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. */ #ifndef HAVE_UV_HANDLE_GET_DATA -static inline void* -uv_handle_get_data(const uv_handle_t* handle) { - return (handle->data); +static inline void * +uv_handle_get_data(const uv_handle_t *handle) { + return (handle->data); } #endif #ifndef HAVE_UV_HANDLE_SET_DATA static inline void -uv_handle_set_data(uv_handle_t* handle, void* data) { - handle->data = data; +uv_handle_set_data(uv_handle_t *handle, void *data) { + handle->data = data; }; #endif + +/* + * These functions are not available in libuv, but they're very internal + * to libuv. We should try to get them merged upstream. + */ + +/* + * A sane way to pass listening TCP socket to child threads, without using + * IPC (as the libuv example shows) but a version of the uv_export() and + * uv_import() functions that were unfortunately removed from libuv. + * This is based on the original libuv code. + */ + +typedef struct isc_uv_stream_info_s isc_uv_stream_info_t; + +struct isc_uv_stream_info_s { + uv_handle_type type; +#ifdef WIN32 + WSAPROTOCOL_INFOW socket_info; +#else + int fd; +#endif +}; + +int +isc_uv_export(uv_stream_t *stream, isc_uv_stream_info_t *info); +/*%< + * Exports uv_stream_t as isc_uv_stream_info_t value, which could + * be used to initialize shared streams within the same process. + */ + +int +isc_uv_import(uv_stream_t *stream, isc_uv_stream_info_t *info); +/*%< + * Imports uv_stream_info_t value into uv_stream_t to initialize a + * shared stream. + */ diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index fa20fe059d..618737983a 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -683,6 +683,8 @@ isc_timermgr_destroy isc_timermgr_poke isc_tm_timegm isc_tm_strptime +isc_uv_export +isc_uv_import isc_win32os_versioncheck openlog @IF PKCS11 diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index e41b625e3a..9fd2d149fe 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -446,6 +446,7 @@ copy InstallFiles ..\Build\Release\ + diff --git a/util/copyrights b/util/copyrights index a675fce20b..d15bd6424f 100644 --- a/util/copyrights +++ b/util/copyrights @@ -2257,6 +2257,7 @@ ./lib/isc/netmgr/tcp.c C 2019,2020 ./lib/isc/netmgr/tcpdns.c C 2019,2020 ./lib/isc/netmgr/udp.c C 2019,2020 +./lib/isc/netmgr/uv-compat.c C 2020 ./lib/isc/netmgr/uv-compat.h C 2019,2020 ./lib/isc/netmgr/uverr2result.c C 2019,2020 ./lib/isc/netscope.c C 2002,2004,2005,2006,2007,2016,2018,2019,2020 @@ -2604,3 +2605,4 @@ ./win32utils/GeoIP.diff X 2013,2018,2019,2020 ./win32utils/bind9.sln.in X 2013,2014,2015,2016,2017,2018,2019,2020 ./win32utils/index.html HTML 2006,2007,2008,2012,2013,2014,2015,2016,2018,2019,2020 +./win32utils/libuv.diff X 2020 diff --git a/win32utils/libuv.diff b/win32utils/libuv.diff new file mode 100644 index 0000000000..41f3796ccc --- /dev/null +++ b/win32utils/libuv.diff @@ -0,0 +1,27 @@ +To make TCP listening properly multithreaded, we need to have the +uv_export() and uv_import() functions that were removed from libuv. +The alternative is passing sockets over IPC, which is complicated and +error prone. + +To make it simple, we export two internal functions from libuv; they will +be used in lib/isc/netmgr/uv-compat.c by our versions of the uv_export() +and uv_import() functions. + +diff --git a/src/win/internal.h b/src/win/internal.h +index 058ddb8e..a9dc4168 100644 +--- a/src/win/internal.h ++++ b/src/win/internal.h +@@ -92,11 +92,11 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, + void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp); + void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); + +-int uv__tcp_xfer_export(uv_tcp_t* handle, ++UV_EXTERN int uv__tcp_xfer_export(uv_tcp_t* handle, + int pid, + uv__ipc_socket_xfer_type_t* xfer_type, + uv__ipc_socket_xfer_info_t* xfer_info); +-int uv__tcp_xfer_import(uv_tcp_t* tcp, ++UV_EXTERN int uv__tcp_xfer_import(uv_tcp_t* tcp, + uv__ipc_socket_xfer_type_t xfer_type, + uv__ipc_socket_xfer_info_t* xfer_info); +