// // Copyright 2023 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // Copyright 2018 Devolutions // Copyright 2018 Cody Piersall // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this // file was obtained (LICENSE.txt). A copy of the license may also be // found online at https://opensource.org/licenses/MIT. // #include #ifdef NNG_PLATFORM_POSIX #include #include #ifdef NNG_PLATFORM_SUNOS #include #endif #endif // Windows complains if we use the POSIX API. #ifdef NNG_PLATFORM_WINDOWS #include #define close(fd) _close(fd) #endif // FDC tests. static void test_sfd_connect_fail(void) { nng_socket s; NUTS_OPEN(s); NUTS_FAIL(nng_dial(s, "socket://", NULL, 0), NNG_ENOTSUP); NUTS_CLOSE(s); } void test_sfd_malformed_address(void) { nng_socket s1; NUTS_OPEN(s1); NUTS_FAIL(nng_listen(s1, "socket://junk", NULL, 0), NNG_EADDRINVAL); NUTS_CLOSE(s1); } void test_sfd_listen(void) { nng_socket s1; NUTS_OPEN(s1); NUTS_PASS(nng_listen(s1, "socket://", NULL, 0)); NUTS_CLOSE(s1); } void test_sfd_accept(void) { nng_socket s1, s2; nng_listener l; int fds[2]; NUTS_PASS(nng_socket_pair(fds)); // make sure we won't have to deal with SIGPIPE - EPIPE is better NUTS_OPEN(s1); NUTS_OPEN(s2); NUTS_PASS(nng_listener_create(&l, s1, "socket://")); NUTS_PASS(nng_listener_start(l, 0)); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[0])); NUTS_SLEEP(10); NUTS_CLOSE(s1); close(fds[1]); } void test_sfd_exchange(void) { nng_socket s1, s2; nng_listener l1, l2; int fds[2]; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s1); NUTS_OPEN(s2); NUTS_PASS(nng_listener_create(&l1, s1, "socket://")); NUTS_PASS(nng_listener_start(l1, 0)); NUTS_PASS(nng_listener_set_int(l1, NNG_OPT_SOCKET_FD, fds[0])); NUTS_PASS(nng_listener_create(&l2, s2, "socket://")); NUTS_PASS(nng_listener_start(l2, 0)); NUTS_PASS(nng_listener_set_int(l2, NNG_OPT_SOCKET_FD, fds[1])); NUTS_SLEEP(10); NUTS_SEND(s1, "hello"); NUTS_RECV(s2, "hello"); NUTS_SEND(s2, "there"); NUTS_RECV(s1, "there"); NUTS_CLOSE(s1); NUTS_CLOSE(s2); close(fds[1]); } void test_sfd_exchange_late(void) { nng_socket s1, s2; nng_listener l1, l2; int fds[2]; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s1); NUTS_OPEN(s2); NUTS_PASS(nng_listener_create(&l1, s1, "socket://")); NUTS_PASS(nng_listener_set_int(l1, NNG_OPT_SOCKET_FD, fds[0])); NUTS_PASS(nng_listener_start(l1, 0)); NUTS_PASS(nng_listener_create(&l2, s2, "socket://")); NUTS_PASS(nng_listener_set_int(l2, NNG_OPT_SOCKET_FD, fds[1])); NUTS_PASS(nng_listener_start(l2, 0)); NUTS_SLEEP(10); NUTS_SEND(s1, "hello"); NUTS_RECV(s2, "hello"); NUTS_SEND(s2, "there"); NUTS_RECV(s1, "there"); NUTS_CLOSE(s1); NUTS_CLOSE(s2); close(fds[1]); } void test_sfd_recv_max(void) { char msg[256]; char buf[256]; nng_socket s0; nng_socket s1; nng_listener l0; nng_listener l1; size_t sz; size_t scratch; int fds[2]; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s0); NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_RECVTIMEO, 100)); NUTS_PASS(nng_socket_set_size(s0, NNG_OPT_RECVMAXSZ, 200)); NUTS_PASS(nng_listener_create(&l0, s0, "socket://")); NUTS_PASS(nng_socket_get_size(s0, NNG_OPT_RECVMAXSZ, &sz)); NUTS_TRUE(sz == 200); NUTS_PASS(nng_listener_set_size(l0, NNG_OPT_RECVMAXSZ, 100)); NUTS_PASS(nng_listener_get_size(l0, NNG_OPT_RECVMAXSZ, &scratch)); NUTS_ASSERT(scratch == 100); NUTS_PASS(nng_listener_start(l0, 0)); NUTS_PASS(nng_listener_set_int(l0, NNG_OPT_SOCKET_FD, fds[0])); NUTS_OPEN(s1); NUTS_PASS(nng_listener_create(&l1, s1, "socket://")); NUTS_PASS(nng_listener_start(l1, 0)); NUTS_PASS(nng_listener_set_int(l1, NNG_OPT_SOCKET_FD, fds[1])); NUTS_PASS(nng_send(s1, msg, 95, 0)); NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); NUTS_PASS(nng_recv(s0, buf, &sz, 0)); NUTS_TRUE(sz == 95); NUTS_PASS(nng_send(s1, msg, 150, 0)); NUTS_FAIL(nng_recv(s0, buf, &sz, 0), NNG_ETIMEDOUT); NUTS_CLOSE(s0); NUTS_CLOSE(s1); } void test_sfd_large(void) { char *buf; nng_socket s0; nng_socket s1; nng_listener l0; nng_listener l1; size_t sz; nng_msg *msg; int fds[2]; sz = 1U << 20; buf = nng_alloc(sz); // a MB buf[sz - 1] = 0; memset(buf, 'A', sz - 1); NUTS_PASS(nng_socket_pair(fds)); NUTS_PASS(nng_msg_alloc(&msg, sz)); NUTS_OPEN(s0); NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_RECVTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_SENDTIMEO, 1000)); NUTS_PASS(nng_socket_set_size(s0, NNG_OPT_RECVMAXSZ, 2U << 20)); NUTS_PASS(nng_listener_create(&l0, s0, "socket://")); NUTS_PASS(nng_listener_start(l0, 0)); NUTS_PASS(nng_listener_set_int(l0, NNG_OPT_SOCKET_FD, fds[0])); NUTS_OPEN(s1); NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 1000)); NUTS_PASS(nng_socket_set_size(s1, NNG_OPT_RECVMAXSZ, 2U << 20)); NUTS_PASS(nng_listener_create(&l1, s1, "socket://")); NUTS_PASS(nng_listener_start(l1, 0)); NUTS_PASS(nng_listener_set_int(l1, NNG_OPT_SOCKET_FD, fds[1])); nng_msleep(100); nng_msg_clear(msg); NUTS_PASS(nng_msg_append(msg, buf, sz)); NUTS_PASS(nng_sendmsg(s0, msg, 0)); NUTS_PASS(nng_recvmsg(s1, &msg, 0)); NUTS_ASSERT(strcmp(nng_msg_body(msg), buf) == 0); memset(nng_msg_body(msg), 'B', sz - 1); memset(buf, 'B', sz - 1); NUTS_PASS(nng_sendmsg(s1, msg, 0)); NUTS_PASS(nng_recvmsg(s0, &msg, 0)); NUTS_ASSERT(strcmp(nng_msg_body(msg), buf) == 0); nng_msg_clear(msg); NUTS_PASS(nng_msg_append(msg, buf, sz)); NUTS_PASS(nng_sendmsg(s0, msg, 0)); NUTS_PASS(nng_recvmsg(s1, &msg, 0)); NUTS_ASSERT(strcmp(nng_msg_body(msg), buf) == 0); NUTS_CLOSE(s0); NUTS_CLOSE(s1); nng_msg_free(msg); nng_free(buf, sz); } void test_sockfd_close_pending(void) { // this test verifies that closing a socket pair that has not // started negotiation with the other side still works. int fds[2]; nng_socket s0; nng_listener l; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s0); nng_listen(s0, "socket://", &l, 0); nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[0]); nng_msleep(10); NUTS_CLOSE(s0); close(fds[1]); } void test_sockfd_close_peer(void) { // this test verifies that closing a socket peer // during negotiation is ok. int fds[2]; nng_socket s0; nng_listener l; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s0); NUTS_PASS(nng_listen(s0, "socket://", &l, 0)); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[0])); close(fds[1]); nng_msleep(100); NUTS_CLOSE(s0); } void test_sockfd_listener_sockaddr(void) { // this test verifies that closing a socket peer // during negotiation is ok. int fds[2]; nng_socket s0; nng_listener l; nng_sockaddr sa; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s0); NUTS_PASS(nng_listen(s0, "socket://", &l, 0)); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[0])); NUTS_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); NUTS_ASSERT(sa.s_family == NNG_AF_UNSPEC); close(fds[1]); NUTS_CLOSE(s0); } void test_sockfd_pipe_sockaddr(void) { // this test verifies that closing a socket peer // during negotiation is ok. int fds[2]; nng_socket s0, s1; nng_listener l; nng_sockaddr sa; nng_msg *msg; nng_pipe p; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s0); NUTS_OPEN(s1); NUTS_PASS(nng_listen(s0, "socket://", &l, 0)); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[0])); NUTS_PASS(nng_listen(s1, "socket://", &l, 0)); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[1])); NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_SENDTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_RECVTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 1000)); NUTS_SEND(s0, "something"); NUTS_PASS(nng_recvmsg(s1, &msg, 0)); p = nng_msg_get_pipe(msg); NUTS_PASS(nng_pipe_get_addr(p, NNG_OPT_LOCADDR, &sa)); NUTS_ASSERT(sa.s_family == NNG_AF_UNSPEC); NUTS_PASS(nng_pipe_get_addr(p, NNG_OPT_REMADDR, &sa)); NUTS_ASSERT(sa.s_family == NNG_AF_UNSPEC); NUTS_CLOSE(s0); NUTS_CLOSE(s1); nng_msg_free(msg); } void test_sockfd_pipe_peer(void) { // this test verifies that closing a socket peer // during negotiation is ok. int fds[2]; nng_socket s0, s1; nng_listener l; nng_msg *msg; nng_pipe p; uint64_t id; NUTS_PASS(nng_socket_pair(fds)); NUTS_OPEN(s0); NUTS_OPEN(s1); NUTS_PASS(nng_listen(s0, "socket://", &l, 0)); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[0])); NUTS_PASS(nng_listen(s1, "socket://", &l, 0)); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[1])); NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_SENDTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s0, NNG_OPT_RECVTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 1000)); NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 1000)); NUTS_SEND(s0, "something"); NUTS_PASS(nng_recvmsg(s1, &msg, 0)); p = nng_msg_get_pipe(msg); NUTS_ASSERT(nng_pipe_id(p) != -1); #if defined(NNG_PLATFORM_DARWIN) || defined(NNG_PLATFORM_LINUX) NUTS_PASS(nng_pipe_get_uint64(p, NNG_OPT_PEER_PID, &id)); NUTS_ASSERT(id == (uint64_t) getpid()); #endif #if defined(NNG_PLATFORM_DARWIN) || defined(NNG_PLATFORM_LINUX) NUTS_PASS(nng_pipe_get_uint64(p, NNG_OPT_PEER_UID, &id)); NUTS_ASSERT(id == (uint64_t) getuid()); #endif #if defined(NNG_PLATFORM_DARWIN) || defined(NNG_PLATFORM_LINUX) NUTS_PASS(nng_pipe_get_uint64(p, NNG_OPT_PEER_GID, &id)); NUTS_ASSERT(id == (uint64_t) getgid()); #endif #if defined(NNG_PLATFORM_SUNOS) NUTS_PASS(nng_pipe_get_uint64(p, NNG_OPT_PEER_ZONEID, &id)); NUTS_ASSERT(id == (uint64_t) getzoneid()); #else NUTS_FAIL( nng_pipe_get_uint64(p, NNG_OPT_PEER_ZONEID, &id), NNG_ENOTSUP); #endif nng_msg_free(msg); NUTS_CLOSE(s0); NUTS_CLOSE(s1); } void test_sfd_listen_full(void) { #ifndef NNG_SFD_LISTEN_QUEUE #define NNG_SFD_LISTEN_QUEUE 16 #endif int fds[NNG_SFD_LISTEN_QUEUE * 2]; nng_socket s; int i; nng_listener l; for (i = 0; i < NNG_SFD_LISTEN_QUEUE * 2; i += 2) { int pair[2]; NUTS_PASS(nng_socket_pair(pair)); fds[i] = pair[0]; fds[i + 1] = pair[1]; } NUTS_OPEN(s); NUTS_PASS(nng_listener_create(&l, s, "socket://")); for (i = 0; i < NNG_SFD_LISTEN_QUEUE * 2; i++) { if (i < NNG_SFD_LISTEN_QUEUE) { NUTS_PASS(nng_listener_set_int( l, NNG_OPT_SOCKET_FD, fds[i])); } else { NUTS_FAIL( nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fds[i]), NNG_ENOSPC); } } for (i = 0; i < NNG_SFD_LISTEN_QUEUE * 2; i++) { close(fds[i]); } NUTS_CLOSE(s); } void test_sfd_fd_option_type(void) { nng_socket s; nng_listener l; NUTS_OPEN(s); NUTS_PASS(nng_listener_create(&l, s, "socket://")); NUTS_FAIL( nng_listener_set_bool(l, NNG_OPT_SOCKET_FD, false), NNG_EBADTYPE); NUTS_CLOSE(s); } void test_sfd_fd_dev_zero(void) { #ifdef NNG_PLATFORM_POSIX nng_socket s; nng_listener l; int fd; // dev/zero produces a stream of zero bytes leading to protocol error NUTS_ASSERT((fd = open("/dev/zero", O_RDONLY, 0777)) >= 0); NUTS_OPEN(s); NUTS_PASS(nng_listener_create(&l, s, "socket://")); NUTS_PASS(nng_listener_set_int(l, NNG_OPT_SOCKET_FD, fd)); nng_msleep(100); NUTS_CLOSE(s); #endif } NUTS_TESTS = { { "socket connect fail", test_sfd_connect_fail }, { "socket malformed address", test_sfd_malformed_address }, #ifdef NNG_HAVE_SOCKETPAIR { "socket listen", test_sfd_listen }, { "socket accept", test_sfd_accept }, { "socket exchange", test_sfd_exchange }, { "socket exchange late", test_sfd_exchange_late }, { "socket recv max", test_sfd_recv_max }, { "socket exchange large", test_sfd_large }, { "socket close pending", test_sockfd_close_pending }, { "socket close peer", test_sockfd_close_peer }, { "socket listener address", test_sockfd_listener_sockaddr }, { "socket pipe address", test_sockfd_pipe_sockaddr }, { "socket pipe peer id", test_sockfd_pipe_peer }, { "socket listen full", test_sfd_listen_full }, { "socket bad fd type", test_sfd_fd_option_type }, { "socket dev zero", test_sfd_fd_dev_zero }, #endif { NULL, NULL }, };