Skip to content

Commit

Permalink
Convert UDP platform test to NUTS.
Browse files Browse the repository at this point in the history
This also restores the IPv6 sanity check.  Note that UDP is still
not supported as a transport, but this can be used to implement
higher level transports such as ZeroTier (or later QUIC).
  • Loading branch information
gdamore committed Feb 26, 2024
1 parent a34a07d commit 8ec2734
Show file tree
Hide file tree
Showing 4 changed files with 352 additions and 294 deletions.
5 changes: 3 additions & 2 deletions src/platform/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2020 Staysail Systems, Inc. <info@staystail.tech>
# Copyright 2024 Staysail Systems, Inc. <info@staystail.tech>
#
# This software is supplied under the terms of the MIT License, a
# copy of which should be located in the distribution where this
Expand All @@ -14,4 +14,5 @@ add_subdirectory(posix)
add_subdirectory(windows)

nng_test(platform_test)
nng_test(resolver_test)
nng_test(resolver_test)
nng_test(udp_test)
349 changes: 349 additions & 0 deletions src/platform/udp_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
//
// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// 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 <arpa/inet.h> // for endianness functions
#endif

#include "core/nng_impl.h"

#include <nuts.h>

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)
{
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 },
};
1 change: 0 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 8ec2734

Please sign in to comment.