diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/platform/ipc_stream_test.c | 169 | ||||
| -rw-r--r-- | src/platform/posix/posix_debug.c | 3 | ||||
| -rw-r--r-- | src/platform/posix/posix_ipclisten.c | 61 | ||||
| -rw-r--r-- | src/platform/posix/posix_tcplisten.c | 66 | ||||
| -rw-r--r-- | src/platform/tcp_stream_test.c | 251 | ||||
| -rw-r--r-- | src/platform/windows/win_tcplisten.c | 76 |
6 files changed, 626 insertions, 0 deletions
diff --git a/src/platform/ipc_stream_test.c b/src/platform/ipc_stream_test.c index caf7861a..77c307bc 100644 --- a/src/platform/ipc_stream_test.c +++ b/src/platform/ipc_stream_test.c @@ -97,6 +97,8 @@ test_ipc_stream(void) nng_stream_listener_close(l); nng_stream_dialer_close(d); + nng_stream_listener_stop(l); + nng_stream_dialer_stop(d); nng_stream_listener_free(l); nng_stream_dialer_free(d); nng_stream_close(c1); @@ -105,7 +107,174 @@ test_ipc_stream(void) nng_stream_free(c2); } +void +test_ipc_listen_activation(void) +{ +#if defined(NNG_PLATFORM_POSIX) + nng_stream_listener *l1; + nng_stream_listener *l2; + char *addr; + int fd; + nng_aio *aio1; + nng_aio *aio2; + nng_stream_dialer *d; + nng_stream *c1, *c2; + + NUTS_ADDR(addr, "ipc"); + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + NUTS_PASS(nng_aio_alloc(&aio2, NULL, NULL)); + + nng_aio_set_timeout(aio1, 2000); + nng_aio_set_timeout(aio2, 2000); + + NUTS_PASS(nng_stream_listener_alloc(&l1, addr)); + NUTS_PASS(nng_stream_listener_listen(l1)); + + NUTS_PASS(nng_stream_dialer_alloc(&d, addr)); + + NUTS_PASS(nng_stream_listener_get_int(l1, NNG_OPT_LISTEN_FD, &fd)); + NUTS_PASS(nng_stream_listener_alloc(&l2, addr)); + NUTS_PASS(nng_stream_listener_set_int(l2, NNG_OPT_LISTEN_FD, fd)); + nng_stream_dialer_dial(d, aio2); + nng_stream_listener_accept(l2, aio1); + + nng_aio_wait(aio1); + nng_aio_wait(aio2); + + NUTS_PASS(nng_aio_result(aio1)); + NUTS_PASS(nng_aio_result(aio2)); + + c1 = nng_aio_get_output(aio1, 0); + c2 = nng_aio_get_output(aio2, 0); + + char buf1[4]; + char buf2[4]; + nng_iov iov1; + nng_iov iov2; + + iov1.iov_buf = buf1; + iov1.iov_len = sizeof(buf1); + + iov2.iov_buf = buf2; + iov2.iov_len = sizeof(buf2); + + nng_aio_set_iov(aio1, 1, &iov1); + nng_aio_set_iov(aio2, 1, &iov2); + + snprintf(buf1, sizeof(buf1), "abc"); + + nng_stream_send(c1, aio1); + nng_stream_recv(c2, aio2); + + nng_aio_wait(aio1); + nng_aio_wait(aio2); + + NUTS_PASS(nng_aio_result(aio1)); + NUTS_PASS(nng_aio_result(aio2)); + + NUTS_MATCH(buf1, buf2); + + nng_stream_listener_free(l1); + nng_stream_listener_free(l2); + nng_stream_free(c1); + nng_stream_free(c2); + nng_aio_free(aio1); + nng_aio_free(aio2); +#else + NUTS_SKIP("Not POSIX"); +#endif +} + +void +test_ipc_listen_activation_busy(void) +{ +#if defined(NNG_PLATFORM_POSIX) + nng_stream_listener *l1; + int fd; + char *addr; + + NUTS_ADDR(addr, "ipc"); + NUTS_PASS(nng_stream_listener_alloc(&l1, "tcp://")); + NUTS_PASS(nng_stream_listener_listen(l1)); + NUTS_PASS(nng_stream_listener_get_int(l1, NNG_OPT_LISTEN_FD, &fd)); + NUTS_FAIL( + nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, fd), NNG_EBUSY); + nng_stream_listener_free(l1); +#else + NUTS_SKIP("Not POSIX"); +#endif +} + +void +test_ipc_listen_activation_closed(void) +{ +#if defined(NNG_PLATFORM_POSIX) + nng_stream_listener *l1; + nng_stream_listener *l2; + int fd; + char *addr; + + NUTS_ADDR(addr, "ipc"); + NUTS_PASS(nng_stream_listener_alloc(&l1, "ipc:///")); + NUTS_PASS(nng_stream_listener_alloc(&l2, addr)); + NUTS_PASS(nng_stream_listener_listen(l2)); + NUTS_PASS(nng_stream_listener_get_int(l2, NNG_OPT_LISTEN_FD, &fd)); + nng_stream_listener_close(l1); + NUTS_FAIL(nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, fd), + NNG_ECLOSED); + nng_stream_listener_free(l1); + nng_stream_listener_free(l2); +#else + NUTS_SKIP("Not POSIX"); +#endif +} + +void +test_ipc_listen_activation_wrong_family(void) +{ +#if !defined(NNG_PLATFORM_POSIX) || !defined(NNG_TRANSPORT_TCP) + NUTS_SKIP("Not POSIX or no TCP"); +#else + nng_stream_listener *l1; + nng_stream_listener *l2; + int fd; + char *addr; + + NUTS_ADDR(addr, "tcp"); + NUTS_PASS(nng_stream_listener_alloc(&l1, "ipc:///")); + NUTS_PASS(nng_stream_listener_alloc(&l2, addr)); + NUTS_PASS(nng_stream_listener_listen(l2)); + NUTS_PASS(nng_stream_listener_get_int(l2, NNG_OPT_LISTEN_FD, &fd)); + NUTS_FAIL(nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, fd), + NNG_EADDRINVAL); + nng_stream_listener_free(l1); + nng_stream_listener_free(l2); +#endif +} + +void +test_ipc_listen_activation_bogus_fd(void) +{ +#if defined(NNG_PLATFORM_POSIX) + nng_stream_listener *l1; + + NUTS_PASS(nng_stream_listener_alloc(&l1, "ipc:///")); + NUTS_FAIL(nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, 12345), + NNG_ECLOSED); + nng_stream_listener_free(l1); +#else + NUTS_SKIP("Not POSIX"); +#endif +} + NUTS_TESTS = { { "ipc stream", test_ipc_stream }, + { "ipc socket activation", test_ipc_listen_activation }, + { "ipc socket activation busy", test_ipc_listen_activation_busy }, + { "pc socket activation closed", test_ipc_listen_activation_closed }, + { "ipc socket activation wrong family", + test_ipc_listen_activation_wrong_family }, + { "ipc socket activation bogus fd", + test_ipc_listen_activation_bogus_fd }, { NULL, NULL }, }; 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, @@ -262,6 +316,13 @@ static const nni_option ipc_listener_options[] = { .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 <arpa/inet.h> #include <errno.h> @@ -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, @@ -422,6 +481,13 @@ static const nni_option tcp_listener_options[] = { .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, }, }; diff --git a/src/platform/tcp_stream_test.c b/src/platform/tcp_stream_test.c index e28fb46c..a60cf1bd 100644 --- a/src/platform/tcp_stream_test.c +++ b/src/platform/tcp_stream_test.c @@ -172,10 +172,261 @@ test_tcp_listen_port_zero_not_bound(void) nng_stream_listener_free(l); } +void +test_tcp_listen_empty_address(void) +{ + nng_stream_listener *l; + + // start a listening stream listener but do not call accept + NUTS_PASS(nng_stream_listener_alloc(&l, "tcp4://")); + NUTS_PASS(nng_stream_listener_listen(l)); + nng_stream_listener_free(l); +} + +void +test_tcp_listen_activation(void) +{ +#if defined(NNG_PLATFORM_POSIX) + nng_stream_listener *l1; + nng_stream_listener *l2; + char *addr; + int fd; + int port; + nng_aio *aio1; + nng_aio *aio2; + nng_stream_dialer *d; + nng_stream *c1, *c2; + char url[32]; + + NUTS_ADDR_ZERO(addr, "tcp4"); + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + NUTS_PASS(nng_aio_alloc(&aio2, NULL, NULL)); + + nng_aio_set_timeout(aio1, 2000); + nng_aio_set_timeout(aio2, 2000); + + NUTS_PASS(nng_stream_listener_alloc(&l1, addr)); + NUTS_PASS(nng_stream_listener_listen(l1)); + NUTS_PASS( + nng_stream_listener_get_int(l1, NNG_OPT_TCP_BOUND_PORT, &port)); + + snprintf(url, sizeof(url), "tcp4://127.0.0.1:%u", port); + NUTS_PASS(nng_stream_dialer_alloc(&d, url)); + + NUTS_PASS(nng_stream_listener_get_int(l1, NNG_OPT_LISTEN_FD, &fd)); + NUTS_PASS(nng_stream_listener_alloc(&l2, "tcp4://")); + NUTS_PASS(nng_stream_listener_set_int(l2, NNG_OPT_LISTEN_FD, fd)); + nng_stream_dialer_dial(d, aio2); + nng_stream_listener_accept(l2, aio1); + + nng_aio_wait(aio1); + nng_aio_wait(aio2); + + NUTS_PASS(nng_aio_result(aio1)); + NUTS_PASS(nng_aio_result(aio2)); + + c1 = nng_aio_get_output(aio1, 0); + c2 = nng_aio_get_output(aio2, 0); + + char buf1[4]; + char buf2[4]; + nng_iov iov1; + nng_iov iov2; + + iov1.iov_buf = buf1; + iov1.iov_len = sizeof(buf1); + + iov2.iov_buf = buf2; + iov2.iov_len = sizeof(buf2); + + nng_aio_set_iov(aio1, 1, &iov1); + nng_aio_set_iov(aio2, 1, &iov2); + + snprintf(buf1, sizeof(buf1), "abc"); + + nng_stream_send(c1, aio1); + nng_stream_recv(c2, aio2); + + nng_aio_wait(aio1); + nng_aio_wait(aio2); + + NUTS_PASS(nng_aio_result(aio1)); + NUTS_PASS(nng_aio_result(aio2)); + + NUTS_MATCH(buf1, buf2); + + nng_stream_listener_free(l1); + nng_stream_listener_free(l2); + nng_stream_dialer_free(d); + nng_stream_free(c1); + nng_stream_free(c2); + nng_aio_free(aio1); + nng_aio_free(aio2); +#elif defined(NNG_PLATFORM_WINDOWS) + // Windows requires that we not have created an I/O completion port yet + // on the incoming FD. + nng_stream_listener *l2; + SOCKET s; + char *addr; + int port; + nng_aio *aio1; + nng_aio *aio2; + nng_stream_dialer *d; + nng_stream *c1, *c2; + char url[32]; + SOCKADDR_IN sin; + + NUTS_ADDR_ZERO(addr, "tcp4"); + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + NUTS_PASS(nng_aio_alloc(&aio2, NULL, NULL)); + + nng_aio_set_timeout(aio1, 2000); + nng_aio_set_timeout(aio2, 2000); + + s = socket(AF_INET, SOCK_STREAM, 0); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = 0; + int len = sizeof(sin); + NUTS_ASSERT(bind(s, (SOCKADDR *) &sin, sizeof(sin)) == 0); + NUTS_ASSERT(getsockname(s, (SOCKADDR *) &sin, &len) == 0); + port = ntohs(sin.sin_port); + NUTS_ASSERT(listen(s, SOMAXCONN) == 0); + + NUTS_PASS(nng_stream_listener_alloc(&l2, "tcp4://")); + NUTS_PASS(nng_stream_listener_set_int(l2, NNG_OPT_LISTEN_FD, (int) s)); + snprintf(url, sizeof(url), "tcp://127.0.0.1:%u", port); + NUTS_PASS(nng_stream_dialer_alloc(&d, url)); + nng_stream_listener_accept(l2, aio1); + + nng_stream_dialer_dial(d, aio2); + + nng_aio_wait(aio1); + NUTS_PASS(nng_aio_result(aio1)); + + nng_aio_wait(aio2); + NUTS_PASS(nng_aio_result(aio2)); + + c1 = nng_aio_get_output(aio1, 0); + c2 = nng_aio_get_output(aio2, 0); + + char buf1[4]; + char buf2[4]; + nng_iov iov1; + nng_iov iov2; + + iov1.iov_buf = buf1; + iov1.iov_len = sizeof(buf1); + + iov2.iov_buf = buf2; + iov2.iov_len = sizeof(buf2); + + nng_aio_set_iov(aio1, 1, &iov1); + nng_aio_set_iov(aio2, 1, &iov2); + + snprintf(buf1, sizeof(buf1), "abc"); + + nng_stream_send(c1, aio1); + nng_stream_recv(c2, aio2); + + nng_aio_wait(aio1); + nng_aio_wait(aio2); + + NUTS_PASS(nng_aio_result(aio1)); + NUTS_PASS(nng_aio_result(aio2)); + + NUTS_MATCH(buf1, buf2); + + nng_stream_listener_free(l2); + nng_stream_dialer_free(d); + nng_stream_free(c1); + nng_stream_free(c2); + nng_aio_free(aio1); + nng_aio_free(aio2); +#else + NUTS_SKIP("Not Windows or POSIX"); +#endif +} + +void +test_tcp_listen_activation_busy(void) +{ + nng_stream_listener *l1; + int fd; + + NUTS_PASS(nng_stream_listener_alloc(&l1, "tcp://")); + NUTS_PASS(nng_stream_listener_listen(l1)); + NUTS_PASS(nng_stream_listener_get_int(l1, NNG_OPT_LISTEN_FD, &fd)); + NUTS_FAIL( + nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, fd), NNG_EBUSY); + nng_stream_listener_free(l1); +} + +void +test_tcp_listen_activation_closed(void) +{ + nng_stream_listener *l1; + nng_stream_listener *l2; + int fd; + + NUTS_PASS(nng_stream_listener_alloc(&l1, "tcp://")); + NUTS_PASS(nng_stream_listener_alloc(&l2, "tcp://")); + NUTS_PASS(nng_stream_listener_listen(l2)); + NUTS_PASS(nng_stream_listener_get_int(l2, NNG_OPT_LISTEN_FD, &fd)); + nng_stream_listener_close(l1); + NUTS_FAIL(nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, fd), + NNG_ECLOSED); + nng_stream_listener_free(l1); + nng_stream_listener_free(l2); +} + +void +test_tcp_listen_activation_wrong_family(void) +{ + +#if !defined(NNG_PLATFORM_POSIX) || !defined(NNG_TRANSPORT_IPC) + NUTS_SKIP("Not posix or no IPC"); +#else + nng_stream_listener *l1; + nng_stream_listener *l2; + int fd; + char *addr; + + NUTS_ADDR(addr, "ipc"); + NUTS_PASS(nng_stream_listener_alloc(&l1, "tcp://")); + NUTS_PASS(nng_stream_listener_alloc(&l2, addr)); + NUTS_PASS(nng_stream_listener_listen(l2)); + NUTS_PASS(nng_stream_listener_get_int(l2, NNG_OPT_LISTEN_FD, &fd)); + NUTS_FAIL(nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, fd), + NNG_EADDRINVAL); + nng_stream_listener_free(l1); + nng_stream_listener_free(l2); +#endif +} + +void +test_tcp_listen_activation_bogus_fd(void) +{ + nng_stream_listener *l1; + + NUTS_PASS(nng_stream_listener_alloc(&l1, "tcp://")); + NUTS_FAIL(nng_stream_listener_set_int(l1, NNG_OPT_LISTEN_FD, 12345), + NNG_ECLOSED); + nng_stream_listener_free(l1); +} + NUTS_TESTS = { { "tcp stream", test_tcp_stream }, { "tcp listen accept cancel", test_tcp_listen_accept_cancel }, { "tcp listen port zero not bound", test_tcp_listen_port_zero_not_bound }, + { "tcp listen empty address", test_tcp_listen_empty_address }, + { "tcp socket activation", test_tcp_listen_activation }, + { "tcp socket activation busy", test_tcp_listen_activation_busy }, + { "tcp socket activation closed", test_tcp_listen_activation_closed }, + { "tcp socket activation wrong family", + test_tcp_listen_activation_wrong_family }, + { "tcp socket activation bogus fd", + test_tcp_listen_activation_bogus_fd }, { NULL, NULL }, }; diff --git a/src/platform/windows/win_tcplisten.c b/src/platform/windows/win_tcplisten.c index 0dd5ae16..91ecccb0 100644 --- a/src/platform/windows/win_tcplisten.c +++ b/src/platform/windows/win_tcplisten.c @@ -429,6 +429,75 @@ 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; + SOCKADDR_STORAGE ss; + int 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_win_error(GetLastError())); + } + + if (((nni_win_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); + } + + int yes = 1; + (void) setsockopt( + l->s, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(yes)); + + l->ss = ss; + l->s = (SOCKET) fd; + if ((rv = nni_win_io_register((HANDLE) l->s)) != 0) { + l->s = INVALID_SOCKET; + nni_mtx_unlock(&l->mtx); + return (rv); + } + 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((int) l->s, buf, szp, t); + nni_mtx_unlock(&l->mtx); + return (rv); +} +#endif + static const nni_option tcp_listener_options[] = { { .o_name = NNG_OPT_LOCADDR, @@ -449,6 +518,13 @@ static const nni_option tcp_listener_options[] = { .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, }, }; |
