Skip to content

Commit

Permalink
fconn WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
gdamore committed Dec 26, 2023
1 parent 46a3883 commit 241b210
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 43 deletions.
5 changes: 4 additions & 1 deletion cmake/NNGOptions.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2023 Staysail Systems, Inc. <info@staysail.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 Down Expand Up @@ -127,6 +127,9 @@ CMAKE_DEPENDENT_OPTION(NNG_TRANSPORT_WSS "Enable WSS transport." ON
"NNG_ENABLE_TLS" OFF)
mark_as_advanced(NNG_TRANSPORT_WSS)

option (NNG_TRANSPORT_FDC "Enable File Descriptor transport (EXPERIMENTAL)" ON)
mark_as_advanced(NNG_TRANSPORT_FDC)

# ZeroTier
option (NNG_TRANSPORT_ZEROTIER "Enable ZeroTier transport (requires libzerotiercore)." OFF)
mark_as_advanced(NNG_TRANSPORT_ZEROTIER)
Expand Down
20 changes: 15 additions & 5 deletions include/nng/nng.h
Original file line number Diff line number Diff line change
Expand Up @@ -709,8 +709,8 @@ NNG_DECL nng_listener nng_pipe_listener(nng_pipe);

// TLS options are only used when the underlying transport supports TLS.

// NNG_OPT_TLS_CONFIG is a pointer to an nng_tls_config object. Generally
// this can used with endpoints, although once an endpoint is started, or
// NNG_OPT_TLS_CONFIG is a pointer to a nng_tls_config object. Generally
// this can be used with endpoints, although once an endpoint is started, or
// once a configuration is used, the value becomes read-only. Note that
// when configuring the object, a hold is placed on the TLS configuration,
// using a reference count. When retrieving the object, no such hold is
Expand All @@ -730,7 +730,7 @@ NNG_DECL nng_listener nng_pipe_listener(nng_pipe);

// NNG_OPT_TLS_CERT_KEY_FILE names a single file that contains a certificate
// and key identifying the endpoint. This is a write-only value. This can be
// set multiple times for times for different keys/certs corresponding to
// set multiple times for different keys/certs corresponding to
// different algorithms on listeners, whereas dialers only support one. The
// file must contain both cert and key as PEM blocks, and the key must
// not be encrypted. (If more flexibility is needed, use the TLS configuration
Expand All @@ -750,13 +750,13 @@ NNG_DECL nng_listener nng_pipe_listener(nng_pipe);
#define NNG_OPT_TLS_SERVER_NAME "tls-server-name"

// NNG_OPT_TLS_VERIFIED returns a boolean indicating whether the peer has
// been verified (true) or not (false). Typically this is read-only, and
// been verified (true) or not (false). Typically, this is read-only, and
// only available for pipes. This option may return incorrect results if
// peer authentication is disabled with `NNG_TLS_AUTH_MODE_NONE`.
#define NNG_OPT_TLS_VERIFIED "tls-verified"

// NNG_OPT_TLS_PEER_CN returns the string with the common name
// of the peer certificate. Typically this is read-only and
// of the peer certificate. Typically, this is read-only and
// only available for pipes. This option may return incorrect results if
// peer authentication is disabled with `NNG_TLS_AUTH_MODE_NONE`.
#define NNG_OPT_TLS_PEER_CN "tls-peer-cn"
Expand Down Expand Up @@ -893,6 +893,16 @@ NNG_DECL nng_listener nng_pipe_listener(nng_pipe);
// peers that cannot be coerced into sending binary frames.
#define NNG_OPT_WS_RECV_TEXT "ws:recv-text"

// NNG_OPT_FDC_FD is a write-only integer property that is used to
// file descriptors (or FILE HANDLE objects on Windows) to a
// fdconn:// based listener. This file descriptor will be taken
// over and used as a stream connection. The protocol is compatible
// with SP over TCP. This facility is experimental, and intended to
// allow use with descriptors created via socketpair() or similar.
// Note that unidirectional pipes (such as those from pipe(2) or mkfifo)
// are not supported.
#define NNG_OPT_FDC_FD "fdc:fd"

// XXX: TBD: priorities, ipv4only

// Statistics. These are for informational purposes only, and subject
Expand Down
2 changes: 1 addition & 1 deletion src/core/dialer.c
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ int
nni_dialer_start(nni_dialer *d, unsigned flags)
{
int rv = 0;
nni_aio *aio;
nni_aio *aio = NULL;

if (nni_atomic_flag_test_and_set(&d->d_started)) {
return (NNG_ESTATE);
Expand Down
7 changes: 4 additions & 3 deletions src/core/fdconn.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,13 @@ fdc_start_conn(fdc_listener *l, nni_aio *aio)
l->listen_q[i] = l->listen_q[i + 1];
}
l->listen_cnt--;
if ((rv = nni_fdc_conn_alloc(&c, fd)) == 0) {
if ((rv = nni_fdc_conn_alloc(&c, fd)) != 0) {
nni_aio_finish_error(aio, rv);
nni_fdc_close_fd(fd);
} else {
nni_aio_set_output(aio, 0, c);
nni_aio_finish(aio, 0, 0);
}
nni_aio_set_output(aio, 0, c);
nni_aio_finish(aio, 0, 0);
}

static void
Expand Down
6 changes: 3 additions & 3 deletions src/core/fdconn.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@

#include "core/nng_impl.h"

#define NNG_OPT_FDC_FD "fdc:fd"

// the nni_fdc_conn struct is provided by platform code to wrap
// an arbitrary byte stream file descriptor (UNIX) or handle (Windows)
// with a nng_stream.
typedef struct nni_fdc_conn nni_fdc_conn;
extern int nni_fdc_conn_alloc(nni_fdc_conn **cp, int fd);
extern int nni_fdc_dialer_alloc(nng_stream_dialer **, const nng_url *);
extern int nni_fdc_listener_alloc(nng_stream_listener **, const nng_url *);

// this is used to close a file descriptor, in case we cannot
// create a connection (or if the listener is closed before the
// connection is accepted.)
extern int nni_fdc_close_fd(int fd);
extern void nni_fdc_close_fd(int fd);

#endif // CORE_FDC_H
10 changes: 9 additions & 1 deletion src/core/stream.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2023 Staysail Systems, Inc. <info@staysail.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 @@ -15,6 +15,7 @@
#include "core/nng_impl.h"
#include <nng/supplemental/tls/tls.h>

#include "core/fdconn.h"
#include "core/tcp.h"
#include "supplemental/tls/tls_api.h"
#include "supplemental/websocket/websocket.h"
Expand Down Expand Up @@ -94,6 +95,13 @@ static struct {
.dialer_alloc = nni_ws_dialer_alloc,
.listener_alloc = nni_ws_listener_alloc,
},
#ifdef NNG_TRANSPORT_FDC
{
.scheme = "fdconn",
.dialer_alloc = nni_fdc_dialer_alloc,
.listener_alloc = nni_fdc_listener_alloc,
},
#endif
{
.scheme = NULL,
},
Expand Down
7 changes: 7 additions & 0 deletions src/platform/posix/posix_fdconn.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <poll.h>
#include <stdbool.h>
#include <sys/uio.h>
#include <unistd.h>

#include "core/fdconn.h"
#include "platform/posix/posix_aio.h"
Expand Down Expand Up @@ -380,3 +381,9 @@ nni_fdc_conn_alloc(nni_fdc_conn **cp, int fd)
*cp = c;
return (0);
}

void
nni_fdc_close_fd(int fd)
{
close(fd);
}
8 changes: 7 additions & 1 deletion src/sp/transport.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2021 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2023 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
Expand Down Expand Up @@ -70,6 +70,9 @@ extern void nni_sp_wss_register(void);
#ifdef NNG_TRANSPORT_ZEROTIER
extern void nni_sp_zt_register(void);
#endif
#ifdef NNG_TRANSPORT_FDC
extern void nni_sp_fdc_register(void);
#endif

void
nni_sp_tran_sys_init(void)
Expand All @@ -95,6 +98,9 @@ nni_sp_tran_sys_init(void)
#ifdef NNG_TRANSPORT_ZEROTIER
nni_sp_zt_register();
#endif
#ifdef NNG_TRANSPORT_FDC
nni_sp_fdc_register();
#endif
}

// nni_sp_tran_sys_fini finalizes the entire transport system, including all
Expand Down
5 changes: 2 additions & 3 deletions src/sp/transport/fdconn/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
# File Descriptor (or Handle) based connections
nng_directory(fdconn)

nng_sources(fdconn.c)
# nng_headers_if(NNG_TRANSPORT_FDCONN nng/transport/fdconn/fdconn.h)
nng_defines(NNG_TRANSPORT_FDCONN)
nng_sources_if(NNG_TRANSPORT_FDC fdconn.c)
nng_defines_if(NNG_TRANSPORT_FDC NNG_TRANSPORT_FDC)
nng_test(fdc_test)
95 changes: 75 additions & 20 deletions src/sp/transport/fdconn/fdc_test.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2023 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 Devolutions <info@devolutions.net>
// Copyright 2018 Cody Piersall <cody.piersall@gmail.com>
Expand All @@ -10,7 +10,6 @@
// found online at https://opensource.org/licenses/MIT.
//


#include <nuts.h>

// FDC tests.
Expand Down Expand Up @@ -46,67 +45,123 @@ test_tcp_local_address_connect(void)
}

void
test_tcp_non_local_address(void)
test_fdc_malformed_address(void)
{
nng_socket s1;

NUTS_OPEN(s1);
NUTS_FAIL(nng_dial(s1, "tcp://8.8.8.8;127.0.0.1:80", NULL, 0),
NNG_EADDRINVAL);
NUTS_FAIL(nng_listen(s1, "fdconn://junk", NULL, 0), NNG_EADDRINVAL);
NUTS_CLOSE(s1);
}

void
test_fdc_malformed_address(void)
test_fdc_listen(void)
{
nng_socket s1;

NUTS_OPEN(s1);
NUTS_FAIL(
nng_dial(s1, "fdconn://junk", NULL, 0), NNG_EADDRINVAL);
NUTS_PASS(nng_listen(s1, "fdconn://", NULL, 0));
NUTS_CLOSE(s1);
}

#include <sys/socket.h>
void
test_fdc_accept(void)
{
nng_socket s1, s2;
nng_listener l;
int fds[2];

NUTS_PASS(socketpair(PF_UNIX, SOCK_STREAM, 0, fds));
// make sure we won't have to deal with SIGPIPE - EPIPE is better
signal(SIGPIPE, SIG_IGN);
NUTS_OPEN(s1);
NUTS_OPEN(s2);
NUTS_PASS(nng_listener_create(&l, s1, "fdconn://"));
NUTS_PASS(nng_listener_start(l, 0));
NUTS_PASS(nng_listener_set_int(l, NNG_OPT_FDC_FD, fds[0]));
NUTS_SLEEP(10);
NUTS_CLOSE(s1);
close(fds[1]);
}


void
test_tcp_recv_max(void)
test_fdc_exchange(void)
{
nng_socket s1, s2;
nng_listener l1, l2;
int fds[2];

NUTS_PASS(socketpair(PF_UNIX, SOCK_STREAM, 0, fds));
// make sure we won't have to deal with SIGPIPE - EPIPE is better
signal(SIGPIPE, SIG_IGN);
NUTS_OPEN(s1);
NUTS_OPEN(s2);
NUTS_PASS(nng_listener_create(&l1, s1, "fdconn://"));
NUTS_PASS(nng_listener_start(l1, 0));
NUTS_PASS(nng_listener_set_int(l1, NNG_OPT_FDC_FD, fds[0]));
NUTS_PASS(nng_listener_create(&l2, s2, "fdconn://"));
NUTS_PASS(nng_listener_start(l2, 0));
NUTS_PASS(nng_listener_set_int(l2, NNG_OPT_FDC_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_fdc_recv_max(void)
{
char msg[256];
char buf[256];
nng_socket s0;
nng_socket s1;
nng_listener l;
nng_listener l0;
nng_listener l1;
size_t sz;
char *addr;
int fds[2];

NUTS_ADDR(addr, "tcp");
NUTS_PASS(socketpair(PF_UNIX, SOCK_STREAM, 0, 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(&l, s0, addr));
NUTS_PASS(nng_listener_create(&l0, s0, "fdconn://"));
NUTS_PASS(nng_socket_get_size(s0, NNG_OPT_RECVMAXSZ, &sz));
NUTS_TRUE(sz == 200);
NUTS_PASS(nng_listener_set_size(l, NNG_OPT_RECVMAXSZ, 100));
NUTS_PASS(nng_listener_start(l, 0));
NUTS_PASS(nng_listener_set_size(l0, NNG_OPT_RECVMAXSZ, 100));
NUTS_PASS(nng_listener_start(l0, 0));
NUTS_PASS(nng_listener_set_int(l0, NNG_OPT_FDC_FD, fds[0]));

NUTS_OPEN(s1);
NUTS_PASS(nng_dial(s1, addr, NULL, 0));
NUTS_PASS(nng_listener_create(&l1, s1, "fdconn://"));
NUTS_PASS(nng_listener_start(l1, 0));
NUTS_PASS(nng_listener_set_int(l1, NNG_OPT_FDC_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_PASS(nng_close(s0));
NUTS_CLOSE(s0);
NUTS_CLOSE(s1);
}

NUTS_TESTS = {

{ "tcp fdc connect fail", test_fdc_connect_fail },
{ "tcp local address connect", test_tcp_local_address_connect },
// { "tcp non-local address", test_tcp_non_local_address },
{ "fdc connect fail", test_fdc_connect_fail },
{ "fdc malformed address", test_fdc_malformed_address },
{ "fdc listen", test_fdc_listen },
{ "fdc accept", test_fdc_accept },
{ "fdc exchange", test_fdc_exchange },
{ "fdc recv max", test_fdc_recv_max },

// { "fdc recv max", test_tcp_recv_max },
{ NULL, NULL },
};
8 changes: 3 additions & 5 deletions src/sp/transport/fdconn/fdconn.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,11 +812,9 @@ fdc_tran_listener_init(void **lp, nng_url *url, nni_listener *nlistener)
int rv;
nni_sock *sock = nni_listener_sock(nlistener);

// Check for invalid URL components.
if ((strlen(url->u_path) != 0) && (strcmp(url->u_path, "/") != 0)) {
return (NNG_EADDRINVAL);
}
if ((url->u_fragment != NULL) || (url->u_userinfo != NULL) ||
// Check for invalid URL components -- we only accept a bare scheme
if ((strlen(url->u_host) != 0) || (strlen(url->u_path) != 0) ||
(url->u_fragment != NULL) || (url->u_userinfo != NULL) ||
(url->u_query != NULL)) {
return (NNG_EADDRINVAL);
}
Expand Down

0 comments on commit 241b210

Please sign in to comment.