From 9c6b2929d8019dcc11935550bc3520ae39c2964e Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Mon, 30 Dec 2024 11:45:36 -0800 Subject: fixes #863 socket activation: for TCP and IPC (POSIX only) This introduces a new option "NNG_OPT_LISTEN_FD", understood by TCP, TLS, and (on POSIX systems) IPC. This option is used to pass a file descriptor or handle (Windows) that is already listening (ready for ACCEPT to be called). For TCP and TLS, the socket must be of type AF_INET or AF_INET6, and for IPC it must be of type AF_UNIX. --- src/platform/posix/posix_debug.c | 3 ++ src/platform/posix/posix_ipclisten.c | 61 +++++++++++++++++++++++++++++++++ src/platform/posix/posix_tcplisten.c | 66 ++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) (limited to 'src/platform/posix') diff --git a/src/platform/posix/posix_debug.c b/src/platform/posix/posix_debug.c index 912000cc..3c0a6604 100644 --- a/src/platform/posix/posix_debug.c +++ b/src/platform/posix/posix_debug.c @@ -90,6 +90,9 @@ static struct { { ENFILE, NNG_ENOFILES }, { EMFILE, NNG_ENOFILES }, { EEXIST, NNG_EEXIST }, +#ifdef ENOTSOCK + { ENOTSOCK, NNG_EINVAL }, +#endif // must be last { 0, 0 }, // clang-format on diff --git a/src/platform/posix/posix_ipclisten.c b/src/platform/posix/posix_ipclisten.c index aa6ffda8..d13ac063 100644 --- a/src/platform/posix/posix_ipclisten.c +++ b/src/platform/posix/posix_ipclisten.c @@ -252,6 +252,60 @@ ipc_listener_set_perms(void *arg, const void *buf, size_t sz, nni_type t) return (0); } +static int +ipc_listener_set_listen_fd(void *arg, const void *buf, size_t sz, nni_type t) +{ + ipc_listener *l = arg; + int fd; + struct sockaddr_storage ss; + socklen_t len = sizeof(ss); + int rv; + + if ((rv = nni_copyin_int(&fd, buf, sz, 0, NNI_MAXINT, t)) != 0) { + return (rv); + } + + if (getsockname(fd, (void *) &ss, &len) != 0) { + return (nni_plat_errno(errno)); + } + + if (((nni_posix_sockaddr2nn(&l->sa, &ss, len)) != 0) || + (ss.ss_family != AF_UNIX)) { + return (NNG_EADDRINVAL); + } + + nni_mtx_lock(&l->mtx); + if (l->started) { + nni_mtx_unlock(&l->mtx); + return (NNG_EBUSY); + } + if (l->closed) { + nni_mtx_unlock(&l->mtx); + return (NNG_ECLOSED); + } + nni_posix_pfd_init(&l->pfd, fd, ipc_listener_cb, l); + l->started = true; + nni_mtx_unlock(&l->mtx); + return (0); +} + +#ifdef NNG_TEST_LIB +// this is readable only for test code -- user code should never rely on this +static int +ipc_listener_get_listen_fd(void *arg, void *buf, size_t *szp, nni_type t) +{ + int rv; + ipc_listener *l = arg; + + nni_mtx_lock(&l->mtx); + NNI_ASSERT(l->started); + NNI_ASSERT(!l->closed); + rv = nni_copyout_int(nni_posix_pfd_fd(&l->pfd), buf, szp, t); + nni_mtx_unlock(&l->mtx); + return (rv); +} +#endif + static const nni_option ipc_listener_options[] = { { .o_name = NNG_OPT_LOCADDR, @@ -261,6 +315,13 @@ static const nni_option ipc_listener_options[] = { .o_name = NNG_OPT_IPC_PERMISSIONS, .o_set = ipc_listener_set_perms, }, + { + .o_name = NNG_OPT_LISTEN_FD, + .o_set = ipc_listener_set_listen_fd, +#ifdef NNG_TEST_LIB + .o_get = ipc_listener_get_listen_fd, +#endif + }, { .o_name = NULL, }, diff --git a/src/platform/posix/posix_tcplisten.c b/src/platform/posix/posix_tcplisten.c index 6e7f4d14..29b5ea65 100644 --- a/src/platform/posix/posix_tcplisten.c +++ b/src/platform/posix/posix_tcplisten.c @@ -10,6 +10,7 @@ // #include "core/nng_impl.h" +#include "nng/nng.h" #include #include @@ -402,6 +403,64 @@ tcp_listener_get_port(void *arg, void *buf, size_t *szp, nni_type t) return (nni_copyout_int(port, buf, szp, t)); } +static int +tcp_listener_set_listen_fd(void *arg, const void *buf, size_t sz, nni_type t) +{ + tcp_listener *l = arg; + int fd; + struct sockaddr_storage ss; + socklen_t len = sizeof(ss); + int rv; + + if ((rv = nni_copyin_int(&fd, buf, sz, 0, NNI_MAXINT, t)) != 0) { + return (rv); + } + + if (getsockname(fd, (void *) &ss, &len) != 0) { + return (nni_plat_errno(errno)); + } + + if (((nni_posix_sockaddr2nn(&l->sa, &ss, len)) != 0) || +#ifdef NNG_ENABLE_IPV6 + ((ss.ss_family != AF_INET) && (ss.ss_family != AF_INET6)) +#else + (ss.ss_family != AF_INET) +#endif + ) { + return (NNG_EADDRINVAL); + } + + nni_mtx_lock(&l->mtx); + if (l->started) { + nni_mtx_unlock(&l->mtx); + return (NNG_EBUSY); + } + if (l->closed) { + nni_mtx_unlock(&l->mtx); + return (NNG_ECLOSED); + } + nni_posix_pfd_init(&l->pfd, fd, tcp_listener_cb, l); + l->started = true; + nni_mtx_unlock(&l->mtx); + return (0); +} + +#ifdef NNG_TEST_LIB +// this is readable only for test code -- user code should never rely on this +static int +tcp_listener_get_listen_fd(void *arg, void *buf, size_t *szp, nni_type t) +{ + int rv; + tcp_listener *l = arg; + nni_mtx_lock(&l->mtx); + NNI_ASSERT(l->started); + NNI_ASSERT(!l->closed); + rv = nni_copyout_int(nni_posix_pfd_fd(&l->pfd), buf, szp, t); + nni_mtx_unlock(&l->mtx); + return (rv); +} +#endif + static const nni_option tcp_listener_options[] = { { .o_name = NNG_OPT_LOCADDR, @@ -421,6 +480,13 @@ static const nni_option tcp_listener_options[] = { .o_name = NNG_OPT_TCP_BOUND_PORT, .o_get = tcp_listener_get_port, }, + { + .o_name = NNG_OPT_LISTEN_FD, + .o_set = tcp_listener_set_listen_fd, +#ifdef NNG_TEST_LIB + .o_get = tcp_listener_get_listen_fd, +#endif + }, { .o_name = NULL, }, -- cgit v1.2.3-70-g09d2