diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt index 2b2288e17..6a49d1fe4 100644 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2020 Staysail Systems, Inc. +# Copyright 2024 Staysail Systems, Inc. # # This software is supplied under the terms of the MIT License, a # copy of which should be located in the distribution where this @@ -14,4 +14,5 @@ add_subdirectory(posix) add_subdirectory(windows) nng_test(platform_test) -nng_test(resolver_test) \ No newline at end of file +nng_test(resolver_test) +nng_test(udp_test) diff --git a/src/platform/udp_test.c b/src/platform/udp_test.c new file mode 100644 index 000000000..9520f8d03 --- /dev/null +++ b/src/platform/udp_test.c @@ -0,0 +1,350 @@ +// +// Copyright 2024 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// +// 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. +// + +// Basic UDP tests. + +#include "nng/nng.h" + +#ifndef _WIN32 +#include // for endianness functions +#endif + +#include "core/nng_impl.h" + +#include + +void +test_udp_pair(void) +{ + nng_sockaddr sa1; + nng_sockaddr sa2; + nni_plat_udp *u1; + nni_plat_udp *u2; + uint32_t loopback; + nng_aio *aio1; + nng_aio *aio2; + nng_iov iov1, iov2; + char msg[] = "hello"; + char rbuf[1024]; + nng_sockaddr to; + nng_sockaddr from; + + NUTS_PASS(nni_init()); + + loopback = htonl(0x7f000001); // 127.0.0.1 + + sa1.s_in.sa_family = NNG_AF_INET; + sa1.s_in.sa_addr = loopback; + sa1.s_in.sa_port = 0; // wild card port binding + + sa2.s_in.sa_family = NNG_AF_INET; + sa2.s_in.sa_addr = loopback; + sa2.s_in.sa_port = 0; + + NUTS_PASS(nni_plat_udp_open(&u1, &sa1)); + NUTS_PASS(nni_plat_udp_open(&u2, &sa2)); + + NUTS_PASS(nni_plat_udp_sockname(u1, &sa1)); + NUTS_PASS(nni_plat_udp_sockname(u2, &sa2)); + + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + NUTS_PASS(nng_aio_alloc(&aio2, NULL, NULL)); + + to = sa2; + iov1.iov_buf = msg; + iov1.iov_len = strlen(msg) + 1; + NUTS_PASS(nng_aio_set_iov(aio1, 1, &iov1)); + NUTS_PASS(nng_aio_set_input(aio1, 0, &to)); + + iov2.iov_buf = rbuf; + iov2.iov_len = 1024; + NUTS_PASS(nng_aio_set_iov(aio2, 1, &iov2)); + NUTS_PASS(nng_aio_set_input(aio2, 0, &from)); + + nni_plat_udp_recv(u2, aio2); + nni_plat_udp_send(u1, aio1); + nng_aio_wait(aio1); + nng_aio_wait(aio2); + + NUTS_PASS(nng_aio_result(aio1)); + NUTS_PASS(nng_aio_result(aio2)); + NUTS_ASSERT(nng_aio_count(aio2) == strlen(msg) + 1); + NUTS_ASSERT(strcmp(rbuf, msg) == 0); + NUTS_ASSERT(from.s_in.sa_family == sa1.s_in.sa_family); + NUTS_ASSERT(from.s_in.sa_addr == sa1.s_in.sa_addr); + NUTS_ASSERT(from.s_in.sa_port == sa1.s_in.sa_port); + + nng_aio_free(aio1); + nng_aio_free(aio2); + nni_plat_udp_close(u1); + nni_plat_udp_close(u2); +} + +void +test_udp_multi_send_recv(void) +{ + nng_sockaddr sa1, sa2, sa3, sa4; + nni_plat_udp *u1; + nni_plat_udp *u2; + uint32_t loopback; + nng_aio *aio1, *aio2, *aio3, *aio4; + nng_iov iov1, iov2, iov3, iov4; + char msg1[] = "hello"; + char msg2[] = "there"; + char rbuf1[32]; + char rbuf2[32]; + nng_sockaddr to; + + NUTS_PASS(nni_init()); + + loopback = htonl(0x7f000001); // 127.0.0.1 + + sa1.s_in.sa_family = NNG_AF_INET; + sa1.s_in.sa_addr = loopback; + sa1.s_in.sa_port = 0; // wild card port binding + + sa2.s_in.sa_family = NNG_AF_INET; + sa2.s_in.sa_addr = loopback; + sa2.s_in.sa_port = 0; + + NUTS_PASS(nni_plat_udp_open(&u1, &sa1)); + NUTS_PASS(nni_plat_udp_open(&u2, &sa2)); + + NUTS_PASS(nni_plat_udp_sockname(u1, &sa1)); + NUTS_PASS(nni_plat_udp_sockname(u2, &sa2)); + + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + NUTS_PASS(nng_aio_alloc(&aio2, NULL, NULL)); + NUTS_PASS(nng_aio_alloc(&aio3, NULL, NULL)); + NUTS_PASS(nng_aio_alloc(&aio4, NULL, NULL)); + + to = sa2; + iov1.iov_buf = msg1; + iov1.iov_len = strlen(msg1) + 1; + NUTS_PASS(nng_aio_set_iov(aio1, 1, &iov1)); + NUTS_PASS(nng_aio_set_input(aio1, 0, &to)); + + to = sa2; + iov2.iov_buf = msg2; + iov2.iov_len = strlen(msg2) + 1; + NUTS_PASS(nng_aio_set_iov(aio2, 1, &iov2)); + NUTS_PASS(nng_aio_set_input(aio2, 0, &to)); + + iov3.iov_buf = rbuf1; + iov3.iov_len = sizeof(rbuf1); + NUTS_PASS(nng_aio_set_iov(aio3, 1, &iov3)); + NUTS_PASS(nng_aio_set_input(aio3, 0, &sa3)); + + iov4.iov_buf = rbuf2; + iov4.iov_len = sizeof(rbuf2); + NUTS_PASS(nng_aio_set_iov(aio4, 1, &iov4)); + NUTS_PASS(nng_aio_set_input(aio4, 0, &sa4)); + + nni_plat_udp_recv(u2, aio4); + nni_plat_udp_recv(u2, aio3); + nni_plat_udp_send(u1, aio2); + nng_msleep(100); // to keep order clear + nni_plat_udp_send(u1, aio1); + nng_aio_wait(aio1); + nng_aio_wait(aio2); + nng_aio_wait(aio3); + nng_aio_wait(aio4); + + NUTS_PASS(nng_aio_result(aio1)); + NUTS_PASS(nng_aio_result(aio2)); + NUTS_PASS(nng_aio_result(aio3)); + NUTS_PASS(nng_aio_result(aio4)); + NUTS_ASSERT(nng_aio_count(aio3) == strlen(msg1) + 1); + NUTS_ASSERT(nng_aio_count(aio4) == strlen(msg2) + 1); + NUTS_ASSERT(strcmp(rbuf1, msg1) == 0); + NUTS_ASSERT(strcmp(rbuf2, msg2) == 0); + + NUTS_PASS(nni_plat_udp_sockname(u1, &sa2)); + NUTS_ASSERT(sa2.s_in.sa_family == sa3.s_in.sa_family); + NUTS_ASSERT(sa2.s_in.sa_addr == sa3.s_in.sa_addr); + NUTS_ASSERT(sa2.s_in.sa_port == sa3.s_in.sa_port); + + NUTS_ASSERT(sa2.s_in.sa_family == sa4.s_in.sa_family); + NUTS_ASSERT(sa2.s_in.sa_addr == sa4.s_in.sa_addr); + NUTS_ASSERT(sa2.s_in.sa_port == sa4.s_in.sa_port); + + nng_aio_free(aio1); + nng_aio_free(aio2); + nng_aio_free(aio3); + nng_aio_free(aio4); + nni_plat_udp_close(u1); + nni_plat_udp_close(u2); +} + +void +test_udp_send_no_addr(void) +{ + nng_sockaddr sa1; + nni_plat_udp *u1; + uint32_t loopback; + nng_aio *aio1; + nng_iov iov1; + char msg[] = "hello"; + + NUTS_PASS(nni_init()); + + loopback = htonl(0x7f000001); // 127.0.0.1 + + sa1.s_in.sa_family = NNG_AF_INET; + sa1.s_in.sa_addr = loopback; + sa1.s_in.sa_port = 0; // wild card port binding + + NUTS_PASS(nni_plat_udp_open(&u1, &sa1)); + NUTS_PASS(nni_plat_udp_sockname(u1, &sa1)); + + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + + iov1.iov_buf = msg; + iov1.iov_len = strlen(msg) + 1; + NUTS_PASS(nng_aio_set_iov(aio1, 1, &iov1)); + + nni_plat_udp_send(u1, aio1); + nng_aio_wait(aio1); + + NUTS_FAIL(nng_aio_result(aio1), NNG_EADDRINVAL); + + nng_aio_free(aio1); + nni_plat_udp_close(u1); +} + +void +test_udp_send_ipc(void) +{ + nng_sockaddr sa1; + nng_sockaddr sa2; + nni_plat_udp *u1; + uint32_t loopback; + nng_aio *aio1; + nng_iov iov1; + char msg[] = "hello"; + + NUTS_PASS(nni_init()); + + loopback = htonl(0x7f000001); // 127.0.0.1 + + sa1.s_in.sa_family = NNG_AF_INET; + sa1.s_in.sa_addr = loopback; + sa1.s_in.sa_port = 0; // wild card port binding + + sa2.s_ipc.sa_family = NNG_AF_IPC; + strcat(sa2.s_ipc.sa_path, "/tmp/bogus"); + + NUTS_PASS(nni_plat_udp_open(&u1, &sa1)); + NUTS_PASS(nni_plat_udp_sockname(u1, &sa1)); + + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + + iov1.iov_buf = msg; + iov1.iov_len = strlen(msg) + 1; + NUTS_PASS(nng_aio_set_iov(aio1, 1, &iov1)); + NUTS_PASS(nng_aio_set_input(aio1, 0, &sa2)); + + nni_plat_udp_send(u1, aio1); + nng_aio_wait(aio1); + + NUTS_FAIL(nng_aio_result(aio1), NNG_EADDRINVAL); + + nng_aio_free(aio1); + nni_plat_udp_close(u1); +} + +void +test_udp_bogus_bind(void) +{ + nni_plat_udp *u; + nng_sockaddr sa; + int rv; + + sa.s_ipc.sa_family = NNG_AF_IPC; + strcpy(sa.s_ipc.sa_path, "/tmp/t"); + rv = nni_plat_udp_open(&u, &sa); + // Some platforms reject IPC addresses altogether (Windows), + // whereas others just say it's not supported with UDP. + NUTS_ASSERT((rv == NNG_ENOTSUP) || (rv == NNG_EADDRINVAL)); + + // NULL address also bad. + NUTS_FAIL(nni_plat_udp_open(&u, NULL), NNG_EADDRINVAL); +} + +void +test_udp_duplicate_bind(void) +{ + nni_plat_udp *u1; + nni_plat_udp *u2; + nng_sockaddr sa; + + sa.s_in.sa_family = NNG_AF_INET; + sa.s_in.sa_addr = htonl(0x7f000001); + + NUTS_PASS(nni_init()); + NUTS_PASS(nni_plat_udp_open(&u1, &sa)); + NUTS_PASS(nni_plat_udp_sockname(u1, &sa)); + NUTS_FAIL(nni_plat_udp_open(&u2, &sa), NNG_EADDRINUSE); + nni_plat_udp_close(u1); +} + +// #ifdef NNG_ENABLE_IPV6 +void +test_udp_send_v6_from_v4(void) +{ +#ifdef NNG_ENABLE_IPV6 + int rv; + nni_plat_udp *u1; + nng_sockaddr sa; + nng_aio *aio1; + nng_iov iov1; + char *msg = "nope"; + const uint8_t google[16] = { 0x26, 0x07, 0xf8, 0xb0, 0x40, 0x07, 0x40, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0e }; + + sa.s_in.sa_family = NNG_AF_INET; + sa.s_in.sa_addr = htonl(0x7f000001); + + NUTS_PASS(nni_init()); + NUTS_PASS(nng_aio_alloc(&aio1, NULL, NULL)); + NUTS_PASS(nni_plat_udp_open(&u1, &sa)); + + sa.s_in6.sa_family = NNG_AF_INET6; + memcpy(sa.s_in6.sa_addr, google, 16); + sa.s_in6.sa_port = htons(80); + + iov1.iov_len = strlen(msg) + 1; + iov1.iov_buf = msg; + nng_aio_set_iov(aio1, 1, &iov1); + nng_aio_set_input(aio1, 0, &sa); + + nni_plat_udp_send(u1, aio1); + nng_aio_wait(aio1); + rv = nng_aio_result(aio1); + NUTS_ASSERT((rv == NNG_EADDRINVAL) || (rv == NNG_ENOTSUP) || + (rv == NNG_EUNREACHABLE)); + + nng_aio_free(aio1); + nni_plat_udp_close(u1); +} +#endif // NNG_ENABLE_IPV6 + +NUTS_TESTS = { + { "udp pair", test_udp_pair }, + { "udp send recv multi", test_udp_multi_send_recv }, + { "udp send no address", test_udp_send_no_addr }, + { "udp send ipc address", test_udp_send_ipc }, + { "udp bogus bind", test_udp_bogus_bind }, + { "udp duplicate bind", test_udp_duplicate_bind }, +#ifdef NNG_ENABLE_IPV6 + { "udp send v6 from v4", test_udp_send_v6_from_v4 }, +#endif + { NULL, NULL }, +}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 022fe49b1..40828eff9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -142,7 +142,6 @@ add_nng_test(tls 60) add_nng_test(tcpsupp 10) add_nng_test(tcp 180) add_nng_test(tcp6 60) -add_nng_test(udp 5) add_nng_test(ws 30) add_nng_test(wss 30) add_nng_test1(zt 60 NNG_TRANSPORT_ZEROTIER) diff --git a/tests/udp.c b/tests/udp.c deleted file mode 100644 index 4f9eabe59..000000000 --- a/tests/udp.c +++ /dev/null @@ -1,291 +0,0 @@ -// -// Copyright 2024 Staysail Systems, Inc. -// Copyright 2018 Capitar IT Group BV -// -// 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. -// - -// Basic UDP tests. - -#ifndef _WIN32 -#include -#endif - -#include "convey.h" -#include "core/nng_impl.h" -#include "trantest.h" - -TestMain("UDP support", { - nni_init(); - - trantest_port = trantest_port ? trantest_port : 5555; - - Convey("We can start a pair of UDP ports", { - nng_sockaddr sa1; - nng_sockaddr sa2; - uint16_t p1; - uint16_t p2; - nni_plat_udp *u1; - nni_plat_udp *u2; - uint32_t loopback; - - loopback = htonl(0x7f000001); // 127.0.0.1 - - p1 = trantest_port++; - p2 = trantest_port++; - - sa1.s_in.sa_family = NNG_AF_INET; - sa1.s_in.sa_addr = loopback; - sa1.s_in.sa_port = htons(p1); - - sa2.s_in.sa_family = NNG_AF_INET; - sa2.s_in.sa_addr = loopback; - sa2.s_in.sa_port = htons(p2); - - So(nni_plat_udp_open(&u1, &sa1) == 0); - So(nni_plat_udp_open(&u2, &sa2) == 0); - Reset({ - nni_plat_udp_close(u1); - nni_plat_udp_close(u2); - }); - - Convey("They can talk to each other", { - char msg[] = "hello"; - char rbuf[1024]; - nng_sockaddr to; - nng_sockaddr from; - nng_aio *aio1; - nng_aio *aio2; - nng_iov iov1; - nng_iov iov2; - - So(nng_aio_alloc(&aio1, NULL, NULL) == 0); - So(nng_aio_alloc(&aio2, NULL, NULL) == 0); - - to = sa2; - iov1.iov_buf = msg; - iov1.iov_len = strlen(msg) + 1; - So(nng_aio_set_iov(aio1, 1, &iov1) == 0); - nng_aio_set_input(aio1, 0, &to); - - iov2.iov_buf = rbuf; - iov2.iov_len = 1024; - So(nng_aio_set_iov(aio2, 1, &iov2) == 0); - nng_aio_set_input(aio2, 0, &from); - - nni_plat_udp_recv(u2, aio2); - nni_plat_udp_send(u1, aio1); - nng_aio_wait(aio1); - nng_aio_wait(aio2); - - So(nng_aio_result(aio1) == 0); - So(nng_aio_result(aio2) == 0); - - So(nng_aio_count(aio2) == strlen(msg) + 1); - So(strcmp(msg, rbuf) == 0); - - So(from.s_in.sa_family == sa1.s_in.sa_family); - So(from.s_in.sa_port == sa1.s_in.sa_port); - So(from.s_in.sa_addr == sa1.s_in.sa_addr); - - // Set up some calls that will ever complete, so - // we test cancellation, etc. - nni_plat_udp_recv(u2, aio2); - nni_plat_udp_send(u2, aio1); - - nng_aio_free(aio1); - nng_aio_free(aio2); - }); - - Convey("Sending without an address fails", { - nng_aio *aio1; - char *msg = "nope"; - nng_iov iov; - - So(nng_aio_alloc(&aio1, NULL, NULL) == 0); - - iov.iov_buf = msg; - iov.iov_len = strlen(msg) + 1; - So(nng_aio_set_iov(aio1, 1, &iov) == 0); - - nni_plat_udp_send(u1, aio1); - nng_aio_wait(aio1); - So(nng_aio_result(aio1) == NNG_EADDRINVAL); - nng_aio_free(aio1); - }); - - Convey("Multiple operations work", { - char msg1[] = "hello"; - char msg2[] = "there"; - char rbuf1[32]; - char rbuf2[32]; - nng_sockaddr to1; - nng_sockaddr to2; - nng_sockaddr from1; - nng_sockaddr from2; - nng_aio *aio1; - nng_aio *aio2; - nng_aio *aio3; - nng_aio *aio4; - nng_iov iov1; - nng_iov iov2; - nng_iov iov3; - nng_iov iov4; - - So(nng_aio_alloc(&aio1, NULL, NULL) == 0); - So(nng_aio_alloc(&aio2, NULL, NULL) == 0); - So(nng_aio_alloc(&aio3, NULL, NULL) == 0); - So(nng_aio_alloc(&aio4, NULL, NULL) == 0); - - to1 = sa2; - iov1.iov_buf = msg1; - iov1.iov_len = strlen(msg1) + 1; - So(nng_aio_set_iov(aio1, 1, &iov1) == 0); - nng_aio_set_input(aio1, 0, &to1); - - to2 = sa2; - iov2.iov_buf = msg2; - iov2.iov_len = strlen(msg2) + 1; - So(nng_aio_set_iov(aio2, 1, &iov2) == 0); - nng_aio_set_input(aio2, 0, &to2); - - iov3.iov_buf = rbuf1; - iov3.iov_len = 1024; - So(nng_aio_set_iov(aio3, 1, &iov3) == 0); - nng_aio_set_input(aio3, 0, &from1); - - iov4.iov_buf = rbuf2; - iov4.iov_len = 1024; - So(nng_aio_set_iov(aio4, 1, &iov4) == 0); - nng_aio_set_input(aio4, 0, &from2); - - nni_plat_udp_recv(u2, aio4); - nni_plat_udp_recv(u2, aio3); - nni_plat_udp_send(u1, aio2); - // This delay here is required to test for a race - // condition that does not occur if it is absent. - nng_msleep(1); - nni_plat_udp_send(u1, aio1); - - nng_aio_wait(aio2); - nng_aio_wait(aio1); - nng_aio_wait(aio3); - nng_aio_wait(aio4); - - So(nng_aio_result(aio1) == 0); - So(nng_aio_result(aio2) == 0); - So(nng_aio_result(aio3) == 0); - So(nng_aio_result(aio4) == 0); - - So(from1.s_in.sa_family == sa1.s_in.sa_family); - So(from1.s_in.sa_port == sa1.s_in.sa_port); - So(from1.s_in.sa_addr == sa1.s_in.sa_addr); - - nng_aio_free(aio1); - nng_aio_free(aio2); - nng_aio_free(aio3); - nng_aio_free(aio4); - }); - - Convey("Sending without an address fails", { - nng_aio *aio1; - char *msg = "nope"; - nng_iov iov; - - So(nng_aio_alloc(&aio1, NULL, NULL) == 0); - iov.iov_buf = msg; - iov.iov_len = strlen(msg) + 1; - So(nng_aio_set_iov(aio1, 1, &iov) == 0); - - nni_plat_udp_send(u1, aio1); - nng_aio_wait(aio1); - So(nng_aio_result(aio1) == NNG_EADDRINVAL); - nng_aio_free(aio1); - }); - - // When we refactor this test suite for NUTS, reenable this - // test. test. #ifdef NNG_ENABLE_IPV6 Convey("Sending - // to an IPv6 address on IPv4 fails", { - // nng_aio *aio1; char *msg = "nope"; - // nng_sockaddr sa; int rv; - // nng_iov iov; - // - // sa.s_in6.sa_family = NNG_AF_INET6; - // // address is for google.com - // inet_ntop(AF_INET6, - // "2607:f8b0:4007:804::200e", (void *) - // sa.s_in6.sa_addr, 16); sa.s_in6.sa_port = 80; - // So(nng_aio_alloc(&aio1, NULL, NULL) == - // 0); iov.iov_buf = msg; - // iov.iov_len = strlen(msg) + 1; - // So(nng_aio_set_iov(aio1, 1, &iov) == 0); - // nng_aio_set_input(aio1, 0, &sa); - // - // nni_plat_udp_send(u1, aio1); - // nng_aio_wait(aio1); - // So((rv = nng_aio_result(aio1)) != 0); - // So(rv == NNG_EADDRINVAL || rv == - // NNG_ENOTSUP || rv == - // NNG_EUNREACHABLE || rv == NNG_EINVAL); - // nng_aio_free(aio1); - // }); - // #endif - - Convey("Sending to an IPC address fails", { - nng_aio *aio1; - char *msg = "nope"; - nng_sockaddr sa; - int rv; - nng_iov iov; - - sa.s_ipc.sa_family = NNG_AF_IPC; - strcat(sa.s_ipc.sa_path, "/tmp/junk"); - So(nng_aio_alloc(&aio1, NULL, NULL) == 0); - iov.iov_buf = msg; - iov.iov_len = strlen(msg) + 1; - So(nng_aio_set_iov(aio1, 1, &iov) == 0); - nng_aio_set_input(aio1, 0, &sa); - - nni_plat_udp_send(u1, aio1); - nng_aio_wait(aio1); - So((rv = nng_aio_result(aio1)) != 0); - So(rv == NNG_EADDRINVAL || rv == NNG_ENOTSUP || - rv == NNG_EUNREACHABLE || rv == NNG_EINVAL); - nng_aio_free(aio1); - }); - }); - - Convey("Cannot open using bogus sockaddr", { - nni_plat_udp *u; - nng_sockaddr sa; - int rv; - - sa.s_ipc.sa_family = NNG_AF_IPC; - strcpy(sa.s_ipc.sa_path, "/tmp/t"); - So((rv = nni_plat_udp_open(&u, &sa)) != 0); - // Some platforms reject IPC addresses altogether (Windows), - // whereas others just say it's not supported with UDP. - So((rv == NNG_ENOTSUP) || (rv == NNG_EADDRINVAL)); - - // NULL address also bad. - So((rv = nni_plat_udp_open(&u, NULL)) == NNG_EADDRINVAL); - }); - - Convey("Duplicate binds fail", { - nni_plat_udp *u1; - nni_plat_udp *u2; - nng_sockaddr sa; - uint16_t p1; - - p1 = trantest_port++; - sa.s_in.sa_family = NNG_AF_INET; - sa.s_in.sa_port = htons(p1); - sa.s_in.sa_addr = htonl(0x7f000001); - So(nni_plat_udp_open(&u1, &sa) == 0); - So(nni_plat_udp_open(&u2, &sa) == NNG_EADDRINUSE); - nni_plat_udp_close(u1); - }); -})