diff options
Diffstat (limited to 'src')
52 files changed, 4594 insertions, 3969 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 42224109..3ee94e18 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,6 +73,8 @@ set (NNG_SRCS core/sockimpl.h core/stats.c core/stats.h + core/stream.c + core/stream.h core/strs.c core/strs.h core/taskq.c @@ -183,7 +185,6 @@ add_subdirectory(transport/zerotier) add_subdirectory(supplemental/base64) add_subdirectory(supplemental/http) -add_subdirectory(supplemental/ipc) add_subdirectory(supplemental/sha1) add_subdirectory(supplemental/tcp) add_subdirectory(supplemental/tls) diff --git a/src/core/aio.c b/src/core/aio.c index b67b7467..ee3d10a5 100644 --- a/src/core/aio.c +++ b/src/core/aio.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 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 @@ -101,6 +101,12 @@ struct nng_aio { nni_list_node a_prov_node; void * a_prov_extra[4]; // Extra data used by provider + // Socket address. This turns out to be very useful, as we wind up + // needing socket addresses for numerous connection related routines. + // It would be cleaner to not have this and avoid burning the space, + // but having this hear dramatically simplifies lots of code. + nng_sockaddr a_sockaddr; + // Expire node. nni_list_node a_expire_node; }; @@ -765,3 +771,15 @@ nni_aio_sys_init(void) nni_thr_run(thr); return (0); } + +void +nni_aio_set_sockaddr(nni_aio *aio, const nng_sockaddr *sa) +{ + memcpy(&aio->a_sockaddr, sa, sizeof(*sa)); +} + +void +nni_aio_get_sockaddr(nni_aio *aio, nng_sockaddr *sa) +{ + memcpy(sa, &aio->a_sockaddr, sizeof(*sa)); +}
\ No newline at end of file diff --git a/src/core/aio.h b/src/core/aio.h index fed0acd8..304f184c 100644 --- a/src/core/aio.h +++ b/src/core/aio.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 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 @@ -148,6 +148,9 @@ extern void nni_aio_get_iov(nni_aio *, unsigned *, nni_iov **); extern void nni_aio_normalize_timeout(nni_aio *, nng_duration); extern void nni_aio_bump_count(nni_aio *, size_t); +extern void nni_aio_set_sockaddr(nni_aio *aio, const nng_sockaddr *); +extern void nni_aio_get_sockaddr(nni_aio *aio, nng_sockaddr *); + // nni_aio_schedule indicates that the AIO has begun, and is scheduled for // asychronous completion. This also starts the expiration timer. Note that // prior to this, the aio is uncancellable. If the operation has a zero diff --git a/src/core/nng_impl.h b/src/core/nng_impl.h index abf47f49..07fe44f5 100644 --- a/src/core/nng_impl.h +++ b/src/core/nng_impl.h @@ -42,6 +42,7 @@ #include "core/random.h" #include "core/reap.h" #include "core/stats.h" +#include "core/stream.h" #include "core/strs.h" #include "core/taskq.h" #include "core/thread.h" diff --git a/src/core/options.c b/src/core/options.c index 0b15d6e2..82735369 100644 --- a/src/core/options.c +++ b/src/core/options.c @@ -398,4 +398,20 @@ nni_setopt(const nni_option *opts, const char *nm, void *arg, const void *buf, opts++; } return (NNG_ENOTSUP); +} + +int +nni_chkopt(const nni_chkoption *opts, const char *nm, const void *buf, + size_t sz, nni_type t) +{ + while (opts->o_name != NULL) { + if (strcmp(opts->o_name, nm) == 0) { + if (opts->o_check == NULL) { + return (NNG_EREADONLY); + } + return (opts->o_check(buf, sz, t)); + } + opts++; + } + return (NNG_ENOTSUP); }
\ No newline at end of file diff --git a/src/core/options.h b/src/core/options.h index 9c5d4817..7b66dbfb 100644 --- a/src/core/options.h +++ b/src/core/options.h @@ -74,11 +74,20 @@ struct nni_option_s { int (*o_set)(void *, const void *, size_t, nni_type); }; +typedef struct nni_chkoption_s nni_chkoption; +struct nni_chkoption_s { + const char *o_name; + // o_check can be NULL for read-only options + int (*o_check)(const void *, size_t, nni_type); +}; + // nni_getopt and nni_setopt are helper functions to implement options // based on arrays of nni_option structures. extern int nni_getopt( const nni_option *, const char *, void *, void *, size_t *, nni_type); extern int nni_setopt( const nni_option *, const char *, void *, const void *, size_t, nni_type); +extern int nni_chkopt( + const nni_chkoption *, const char *, const void *, size_t, nni_type); #endif // CORE_OPTIONS_H diff --git a/src/core/platform.h b/src/core/platform.h index 69fa5db6..e415b438 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2018 Devolutions <info@devolutions.net> // @@ -228,53 +228,6 @@ typedef struct nni_tcp_conn nni_tcp_conn; typedef struct nni_tcp_dialer nni_tcp_dialer; typedef struct nni_tcp_listener nni_tcp_listener; -extern void nni_tcp_conn_fini(nni_tcp_conn *); - -// nni_tcp_conn_close closes the connection, which might actually be -// implemented as a shutdown() call. -// Further operations on it should return NNG_ECLOSED. -extern void nni_tcp_conn_close(nni_tcp_conn *); - -// nni_tcp_conn_send sends data in the iov buffers to the peer. -// The platform may modify the iovs. -extern void nni_tcp_conn_send(nni_tcp_conn *, nni_aio *); - -// nni_tcp_conn_recv receives data into the buffers provided by the -// I/O vector (iovs). The platform should attempt to scatter the received -// data into the iovs if possible. -// -// It is possible for the reader to return less data than is requested, -// in which case the caller is responsible for resubmitting. The platform -// must not return "zero" data however. (It is an error to attempt to -// receive zero bytes.) The platform may modify the iovs. -extern void nni_tcp_conn_recv(nni_tcp_conn *, nni_aio *); - -// nni_tcp_conn_peername gets the peer name. -extern int nni_tcp_conn_peername(nni_tcp_conn *, nni_sockaddr *); - -// nni_tcp_conn_sockname gets the local name. -extern int nni_tcp_conn_sockname(nni_tcp_conn *, nni_sockaddr *); - -// nni_tcp_conn_set_nodelay indicates that the TCP pipe should send -// data immediately, without any buffering. (Disable Nagle's algorithm.) -extern int nni_tcp_conn_set_nodelay(nni_tcp_conn *, bool); - -// nni_tcp_conn_set_keepalive indicates that the TCP pipe should send -// keepalive probes. Tuning of these keepalives is currently unsupported. -extern int nni_tcp_conn_set_keepalive(nni_tcp_conn *, bool); - -// nni_tcp_conn_setopt is like setsockopt, but uses string names. These -// are the same names from the TCP transport, generally. Examples are -// NNG_OPT_TCP_NODELAY and NNG_OPT_TCP_KEEPALIVE. -extern int nni_tcp_conn_setopt( - nni_tcp_conn *, const char *, const void *, size_t, nni_type); - -// nni_tcp_conn_getopt is like getsockopt, but uses string names. -// We support NNG_OPT_REMADDR and NNG_OPT_LOCADDR (with argument type -// nng_sockaddr), and NNG_OPT_TCP_NODELAY and NNG_OPT_TCP_KEEPALIVE. -extern int nni_tcp_conn_getopt( - nni_tcp_conn *, const char *, void *, size_t *, nni_type); - // nni_tcp_dialer_init creates a new dialer object. extern int nni_tcp_dialer_init(nni_tcp_dialer **); @@ -287,11 +240,10 @@ extern void nni_tcp_dialer_fini(nni_tcp_dialer *); // connection will be aborted. extern void nni_tcp_dialer_close(nni_tcp_dialer *); -// nni_tcp_dialer_dial attempts to create an outgoing connection, -// asynchronously, to the address specified. On success, the first (and only) +// nni_tcp_dial attempts to create an outgoing connection, +// asynchronously, to the address in the aio. On success, the first (and only) // output will be an nni_tcp_conn * associated with the remote server. -extern void nni_tcp_dialer_dial( - nni_tcp_dialer *, const nni_sockaddr *, nni_aio *); +extern void nni_tcp_dial(nni_tcp_dialer *, nni_aio *); // nni_tcp_dialer_getopt gets an option from the dialer. extern int nni_tcp_dialer_setopt( @@ -361,101 +313,12 @@ typedef struct nni_ipc_conn nni_ipc_conn; typedef struct nni_ipc_dialer nni_ipc_dialer; typedef struct nni_ipc_listener nni_ipc_listener; -// nni_ipc_conn_fini disposes of the connection. -extern void nni_ipc_conn_fini(nni_ipc_conn *); - -// nni_ipc_conn_close closes the connection, which might actually be -// implemented as a shutdown() call. -// Further operations on it should return NNG_ECLOSED. -extern void nni_ipc_conn_close(nni_ipc_conn *); - -// nni_ipc_conn_send sends data in the iov buffers to the peer. -// The platform may modify the iovs. -extern void nni_ipc_conn_send(nni_ipc_conn *, nni_aio *); - -// nni_ipc_conn_recv receives data into the buffers provided by the -// I/O vector (iovs). The platform should attempt to scatter the received -// data into the iovs if possible. -// -// It is possible for the reader to return less data than is requested, -// in which case the caller is responsible for resubmitting. The platform -// must not return "zero" data however. (It is an error to attempt to -// receive zero bytes.) The platform may modify the iovs. -extern void nni_ipc_conn_recv(nni_ipc_conn *, nni_aio *); - -// nni_ipc_conn_setopt is like setsockopt, but uses string names. These -// are the same names from the IPC transport, generally. There are no -// options that are generally settable on an IPC connection. -extern int nni_ipc_conn_setopt( - nni_ipc_conn *, const char *, const void *, size_t, nni_type); - -// nni_ipc_conn_getopt is like getsockopt, but uses string names. -// We support NNG_OPT_REMADDR and NNG_OPT_LOCADDR (with argument type -// nng_sockaddr), and on some platforms NNG_OPT_IPC_PEER_[UID,GID,ZONEID] -// (with type uint64_t.) -extern int nni_ipc_conn_getopt( - nni_ipc_conn *, const char *, void *, size_t *, nni_type); - -// nni_ipc_dialer_init creates a new dialer object. -extern int nni_ipc_dialer_init(nni_ipc_dialer **); - -// nni_ipc_dialer_fini finalizes the dialer, closing it and freeing -// all resources. -extern void nni_ipc_dialer_fini(nni_ipc_dialer *); - -// nni_ipc_dialer_close closes the dialer. -// Further operations on it should return NNG_ECLOSED. Any in-progress -// connection will be aborted. -extern void nni_ipc_dialer_close(nni_ipc_dialer *); - -// nni_ipc_dialer_dial attempts to create an outgoing connection, -// asynchronously, to the address specified. On success, the first (and only) -// output will be an nni_ipc_conn * associated with the remote server. -extern void nni_ipc_dialer_dial( - nni_ipc_dialer *, const nni_sockaddr *, nni_aio *); - -// nni_ipc_dialer_getopt is used to get options from the dialer. -// At present there aren't any defined options. -extern int nni_ipc_dialer_getopt( - nni_ipc_dialer *, const char *, void *, size_t *, nni_type); - -// nni_ipc_dialer_setopt sets an option for the dialer. There aren't -// any options defined at present. -extern int nni_ipc_dialer_setopt( - nni_ipc_dialer *, const char *, const void *, size_t, nni_type); - -// nni_ipc_listener_init creates a new listener object, unbound. -extern int nni_ipc_listener_init(nni_ipc_listener **); - -// nni_ipc_listener_fini frees the listener and all associated resources. -// It implictly closes the listener as well. -extern void nni_ipc_listener_fini(nni_ipc_listener *); - -// nni_ipc_listener_close closes the listener. This will unbind -// any bound socket, and further operations will result in NNG_ECLOSED. -extern void nni_ipc_listener_close(nni_ipc_listener *); - -// nni_ipc_listener_listen creates the socket in listening mode, bound -// to the specified address. Unlike TCP, this address does not change. -extern int nni_ipc_listener_listen(nni_ipc_listener *, const nni_sockaddr *); - -// nni_ipc_listener_accept accepts in incoming connect, asynchronously. -// On success, the first (and only) output will be an nni_ipc_conn * -// associated with the remote peer. -extern void nni_ipc_listener_accept(nni_ipc_listener *, nni_aio *); - -// nni_ipc_listener_getopt is used to get options from the listener. -// The only valid option is NNG_OPT_LOCADDR, which will only have -// a valid value if the socket is bound, otherwise the value returned -// will be of type NNG_AF_UNSPEC. -extern int nni_ipc_listener_getopt( - nni_ipc_listener *, const char *, void *, size_t *, nni_type); - -// nni_ipc_listener_setopt sets an option for the listener. The only -// valid options are NNG_OPT_IPC_SECURITY_DESCRIPTORS (Windows) and -// NNG_OPT_IPC_PERMISSIONS (POSIX). -extern int nni_ipc_listener_setopt( - nni_ipc_listener *, const char *, const void *, size_t, nni_type); +// IPC is so different from platform to platform. The following should +// be implemented. If IPC isn't supported, all of these functions should +// be stubs that just return NNG_ENOTSUP. +extern int nni_ipc_dialer_alloc(nng_stream_dialer **, const nng_url *); +extern int nni_ipc_listener_alloc(nng_stream_listener **, const nng_url *); +extern int nni_ipc_checkopt(const char *, const void *, size_t, nni_type); // // UDP support. UDP is not connection oriented, and only has the notion diff --git a/src/core/stream.c b/src/core/stream.c new file mode 100644 index 00000000..52c5b1a6 --- /dev/null +++ b/src/core/stream.c @@ -0,0 +1,366 @@ +// +// Copyright 2019 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 +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +// This provides an abstraction for byte streams, allowing polymorphic +// use of them in rather flexible contexts. + +#include <string.h> + +#include "core/nng_impl.h" +#include <nng/supplemental/tls/tls.h> + +#include "core/tcp.h" +#include "supplemental/tls/tls_api.h" +#include "supplemental/websocket/websocket.h" + +static struct { + const char *scheme; + int (*dialer_alloc)(nng_stream_dialer **, const nng_url *); + int (*listener_alloc)(nng_stream_listener **, const nng_url *); + int (*checkopt)(const char *, const void *, size_t, nni_type); + +} stream_drivers[] = { + { + .scheme = "ipc", + .dialer_alloc = nni_ipc_dialer_alloc, + .listener_alloc = nni_ipc_listener_alloc, + .checkopt = nni_ipc_checkopt, + }, + { + .scheme = "tcp", + .dialer_alloc = nni_tcp_dialer_alloc, + .listener_alloc = nni_tcp_listener_alloc, + .checkopt = nni_tcp_checkopt, + }, + { + .scheme = "tcp4", + .dialer_alloc = nni_tcp_dialer_alloc, + .listener_alloc = nni_tcp_listener_alloc, + .checkopt = nni_tcp_checkopt, + }, + { + .scheme = "tcp6", + .dialer_alloc = nni_tcp_dialer_alloc, + .listener_alloc = nni_tcp_listener_alloc, + .checkopt = nni_tcp_checkopt, + }, + { + .scheme = "tls+tcp", + .dialer_alloc = nni_tls_dialer_alloc, + .listener_alloc = nni_tls_listener_alloc, + .checkopt = nni_tls_checkopt, + }, + { + .scheme = "tls+tcp4", + .dialer_alloc = nni_tls_dialer_alloc, + .listener_alloc = nni_tls_listener_alloc, + .checkopt = nni_tls_checkopt, + }, + { + .scheme = "tls+tcp6", + .dialer_alloc = nni_tls_dialer_alloc, + .listener_alloc = nni_tls_listener_alloc, + .checkopt = nni_tls_checkopt, + }, + { + .scheme = "ws", + .dialer_alloc = nni_ws_dialer_alloc, + .listener_alloc = nni_ws_listener_alloc, + .checkopt = nni_ws_checkopt, + }, + { + .scheme = "wss", + .dialer_alloc = nni_ws_dialer_alloc, + .listener_alloc = nni_ws_listener_alloc, + .checkopt = nni_ws_checkopt, + }, + { + .scheme = NULL, + }, +}; + +void +nng_stream_close(nng_stream *s) +{ + s->s_close(s); +} + +void +nng_stream_free(nng_stream *s) +{ + if (s != NULL) { + s->s_free(s); + } +} + +void +nng_stream_send(nng_stream *s, nng_aio *aio) +{ + s->s_send(s, aio); +} + +void +nng_stream_recv(nng_stream *s, nng_aio *aio) +{ + s->s_recv(s, aio); +} + +int +nni_stream_getx( + nng_stream *s, const char *nm, void *data, size_t *szp, nni_type t) +{ + return (s->s_getx(s, nm, data, szp, t)); +} + +int +nni_stream_setx( + nng_stream *s, const char *nm, const void *data, size_t sz, nni_type t) +{ + return (s->s_setx(s, nm, data, sz, t)); +} + +void +nng_stream_dialer_close(nng_stream_dialer *d) +{ + d->sd_close(d); +} + +void +nng_stream_dialer_free(nng_stream_dialer *d) +{ + if (d != NULL) { + d->sd_free(d); + } +} + +void +nng_stream_dialer_dial(nng_stream_dialer *d, nng_aio *aio) +{ + d->sd_dial(d, aio); +} + +int +nng_stream_dialer_alloc_url(nng_stream_dialer **dp, const nng_url *url) +{ + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + + for (int i = 0; stream_drivers[i].scheme != NULL; i++) { + if (strcmp(stream_drivers[i].scheme, url->u_scheme) == 0) { + return (stream_drivers[i].dialer_alloc(dp, url)); + } + } + return (NNG_ENOTSUP); +} + +int +nng_stream_dialer_alloc(nng_stream_dialer **dp, const char *uri) +{ + nng_url *url; + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + if ((rv = nng_url_parse(&url, uri)) != 0) { + return (rv); + } + rv = nng_stream_dialer_alloc_url(dp, url); + nng_url_free(url); + return (rv); +} + +int +nni_stream_dialer_getx( + nng_stream_dialer *d, const char *nm, void *data, size_t *szp, nni_type t) +{ + return (d->sd_getx(d, nm, data, szp, t)); +} + +int +nni_stream_dialer_setx(nng_stream_dialer *d, const char *nm, const void *data, + size_t sz, nni_type t) +{ + return (d->sd_setx(d, nm, data, sz, t)); +} + +void +nng_stream_listener_close(nng_stream_listener *l) +{ + l->sl_close(l); +} +void +nng_stream_listener_free(nng_stream_listener *l) +{ + if (l != NULL) { + l->sl_free(l); + } +} +int +nng_stream_listener_listen(nng_stream_listener *l) +{ + return (l->sl_listen(l)); +} + +void +nng_stream_listener_accept(nng_stream_listener *l, nng_aio *aio) +{ + l->sl_accept(l, aio); +} + +int +nni_stream_listener_getx(nng_stream_listener *l, const char *nm, void *data, + size_t *szp, nni_type t) +{ + return (l->sl_getx(l, nm, data, szp, t)); +} + +int +nni_stream_listener_setx(nng_stream_listener *l, const char *nm, + const void *data, size_t sz, nni_type t) +{ + return (l->sl_setx(l, nm, data, sz, t)); +} + +int +nng_stream_listener_alloc_url(nng_stream_listener **lp, const nng_url *url) +{ + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + + for (int i = 0; stream_drivers[i].scheme != NULL; i++) { + if (strcmp(stream_drivers[i].scheme, url->u_scheme) == 0) { + return (stream_drivers[i].listener_alloc(lp, url)); + } + } + return (NNG_ENOTSUP); +} + +int +nng_stream_listener_alloc(nng_stream_listener **lp, const char *uri) +{ + nng_url *url; + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + + if ((rv = nng_url_parse(&url, uri)) != 0) { + return (rv); + } + rv = nng_stream_listener_alloc_url(lp, url); + nng_url_free(url); + return (rv); +} + +int +nni_stream_checkopt(const char *scheme, const char *name, const void *data, + size_t sz, nni_type t) +{ + for (int i = 0; stream_drivers[i].scheme != NULL; i++) { + if (strcmp(stream_drivers[i].scheme, scheme) != 0) { + continue; + } + if (stream_drivers[i].checkopt == NULL) { + return (NNG_ENOTSUP); + } + return (stream_drivers[i].checkopt(name, data, sz, t)); + } + return (NNG_ENOTSUP); +} + +// +// This next block sets up to define the various typed option functions. +// To make it easier to cover them all at once, we use macros. +// + +#define DEFGET(base) \ + int nng_##base##_get( \ + nng_##base *s, const char *nm, void *vp, size_t *szp) \ + { \ + return (nni_##base##_getx(s, nm, vp, szp, NNI_TYPE_OPAQUE)); \ + } + +#define DEFTYPEDGET(base, suffix, type, nnitype) \ + int nng_##base##_get_##suffix( \ + nng_##base *s, const char *nm, type *vp) \ + { \ + size_t sz = sizeof(*vp); \ + return (nni_##base##_getx(s, nm, vp, &sz, nnitype)); \ + } + +#define DEFGETALL(base) \ + DEFGET(base) \ + DEFTYPEDGET(base, int, int, NNI_TYPE_INT32) \ + DEFTYPEDGET(base, bool, bool, NNI_TYPE_BOOL) \ + DEFTYPEDGET(base, size, size_t, NNI_TYPE_SIZE) \ + DEFTYPEDGET(base, uint64, uint64_t, NNI_TYPE_UINT64) \ + DEFTYPEDGET(base, ptr, void *, NNI_TYPE_POINTER) \ + DEFTYPEDGET(base, ms, nng_duration, NNI_TYPE_DURATION) \ + DEFTYPEDGET(base, addr, nng_sockaddr, NNI_TYPE_SOCKADDR) + +DEFGETALL(stream) +DEFGETALL(stream_dialer) +DEFGETALL(stream_listener) + +#define DEFSET(base) \ + int nng_##base##_set( \ + nng_##base *s, const char *nm, const void *vp, size_t sz) \ + { \ + return (nni_##base##_setx(s, nm, vp, sz, NNI_TYPE_OPAQUE)); \ + } + +#define DEFTYPEDSETEX(base, suffix, type, len, nnitype) \ + int nng_##base##_set_##suffix(nng_##base *s, const char *nm, type v) \ + { \ + return (nni_##base##_setx(s, nm, &v, len, nnitype)); \ + } + +#define DEFTYPEDSET(base, suffix, type, nnitype) \ + int nng_##base##_set_##suffix(nng_##base *s, const char *nm, type v) \ + { \ + return (nni_##base##_setx(s, nm, &v, sizeof(v), nnitype)); \ + } + +#define DEFSTRINGSET(base) \ + int nng_##base##_set_string( \ + nng_##base *s, const char *nm, const char *v) \ + { \ + return (nni_##base##_setx(s, nm, v, \ + v != NULL ? strlen(v) + 1 : 0, NNI_TYPE_STRING)); \ + } + +#define DEFSOCKADDRSET(base) \ + int nng_##base##_set_adddr( \ + nng_##base *s, const char *nm, const nng_sockaddr *v) \ + { \ + return (nni_##base##_setx( \ + s, nm, v, sizeof(*v), NNI_TYPE_SOCKADDR)); \ + } + +#define DEFSETALL(base) \ + DEFSET(base) \ + DEFTYPEDSET(base, int, int, NNI_TYPE_INT32) \ + DEFTYPEDSET(base, bool, bool, NNI_TYPE_BOOL) \ + DEFTYPEDSET(base, size, size_t, NNI_TYPE_SIZE) \ + DEFTYPEDSET(base, ms, nng_duration, NNI_TYPE_DURATION) \ + DEFTYPEDSET(base, ptr, void *, NNI_TYPE_POINTER) \ + DEFSTRINGSET(base) \ + DEFSOCKADDRSET(base) + +DEFSETALL(stream) +DEFSETALL(stream_dialer) +DEFSETALL(stream_listener)
\ No newline at end of file diff --git a/src/core/stream.h b/src/core/stream.h new file mode 100644 index 00000000..18914979 --- /dev/null +++ b/src/core/stream.h @@ -0,0 +1,69 @@ +// +// Copyright 2019 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 +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef CORE_STREAM_H +#define CORE_STREAM_H + +// This provides an abstraction for byte streams, allowing polymorphic +// use of them in rather flexible contexts. + +#include "core/nng_impl.h" + +// Private property operations (these include the types.) +extern int nni_stream_getx( + nng_stream *, const char *, void *, size_t *, nni_type); +extern int nni_stream_setx( + nng_stream *, const char *, const void *, size_t, nni_type); + +extern int nni_stream_dialer_getx( + nng_stream_dialer *, const char *, void *, size_t *, nni_type); +extern int nni_stream_dialer_setx( + nng_stream_dialer *, const char *, const void *, size_t, nni_type); + +extern int nni_stream_listener_getx( + nng_stream_listener *, const char *, void *, size_t *, nni_type); +extern int nni_stream_listener_setx( + nng_stream_listener *, const char *, const void *, size_t, nni_type); + +extern int nni_stream_checkopt( + const char *, const char *, const void *, size_t, nni_type); + +// This is the common implementation of a connected byte stream. It should be +// the first element of any implementation. Applications are not permitted to +// access it directly. +struct nng_stream { + void (*s_free)(void *); + void (*s_close)(void *); + void (*s_recv)(void *, nng_aio *); + void (*s_send)(void *, nng_aio *); + int (*s_getx)(void *, const char *, void *, size_t *, nni_type); + int (*s_setx)(void *, const char *, const void *, size_t, nni_type); +}; + +// Dialer implementation. Stream dialers create streams. +struct nng_stream_dialer { + void (*sd_free)(void *); + void (*sd_close)(void *); + void (*sd_dial)(void *, nng_aio *); + int (*sd_getx)(void *, const char *, void *, size_t *, nni_type); + int (*sd_setx)(void *, const char *, const void *, size_t, nni_type); +}; + +// Listener implementation. Stream listeners accept connections and create +// streams. +struct nng_stream_listener { + void (*sl_free)(void *); + void (*sl_close)(void *); + int (*sl_listen)(void *); + void (*sl_accept)(void *, nng_aio *); + int (*sl_getx)(void *, const char *, void *, size_t *, nni_type); + int (*sl_setx)(void *, const char *, const void *, size_t, nni_type); +}; + +#endif // CORE_STREAM_H diff --git a/src/core/tcp.h b/src/core/tcp.h new file mode 100644 index 00000000..ac4398d0 --- /dev/null +++ b/src/core/tcp.h @@ -0,0 +1,24 @@ +// +// Copyright 2019 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 +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef CORE_TCP_H +#define CORE_TCP_H + +#include "core/nng_impl.h" + +// These are interfaces we use for TCP internally. These are not exposed +// to the public API. + +extern int nni_tcp_dialer_alloc(nng_stream_dialer **, const nng_url *); +extern int nni_tcp_listener_alloc(nng_stream_listener **, const nng_url *); + +// nni_tcp_checkopt is used to validate (generically) options. +extern int nni_tcp_checkopt(const char *, const void *, size_t, nni_type); + +#endif // CORE_TCP_H diff --git a/src/core/transport.c b/src/core/transport.c index 54c4bcfe..071ea0c7 100644 --- a/src/core/transport.c +++ b/src/core/transport.c @@ -1,6 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -122,6 +123,16 @@ nni_tran_chkopt(const char *name, const void *v, size_t sz, int typ) const nni_tran_listener_ops *lops; const nni_option * o; + // Check option entry point is cleaner than endpoint hacks. + if (t->t_tran.tran_checkopt != NULL) { + rv = t->t_tran.tran_checkopt(name, v, sz, typ); + if (rv != NNG_ENOTSUP) { + nni_mtx_unlock(&nni_tran_lk); + return (rv); + } + continue; + } + // Generally we look for endpoint options. We check both // dialers and listeners. dops = t->t_tran.tran_dialer; diff --git a/src/core/transport.h b/src/core/transport.h index f3d252c4..84d9c1d9 100644 --- a/src/core/transport.h +++ b/src/core/transport.h @@ -183,6 +183,12 @@ struct nni_tran { // tran_fini, if not NULL, is called during library deinitialization. // It should release any global resources, close any open files, etc. void (*tran_fini)(void); + + // tran_chkopt is used to check option validity; this is used as + // an initial filter on the data, without actually setting anything. + // This can be useful, for example, before any transports are + // configured on the socket. + int (*tran_checkopt)(const char *, const void *, size_t, nni_type); }; // These APIs are used by the framework internally, and not for use by diff --git a/src/core/url.c b/src/core/url.c index 5e2317ea..b2cf77f8 100644 --- a/src/core/url.c +++ b/src/core/url.c @@ -231,8 +231,21 @@ nni_url_default_port(const char *scheme) const char *s; for (int i = 0; (s = nni_url_default_ports[i].scheme) != NULL; i++) { - if (strcmp(s, scheme) == 0) { + size_t l = strlen(s); + if (strncmp(s, scheme, strlen(s)) != 0) { + continue; + } + // It can have a suffix of either "4" or "6" to restrict + // the address family. This is an NNG extension. + switch (scheme[l]) { + case '\0': return (nni_url_default_ports[i].port); + case '4': + case '6': + if (scheme[l + 1] == '\0') { + return (nni_url_default_ports[i].port); + } + break; } } return (""); @@ -463,19 +476,23 @@ error: void nni_url_free(nni_url *url) { - nni_strfree(url->u_rawurl); - nni_strfree(url->u_scheme); - nni_strfree(url->u_userinfo); - nni_strfree(url->u_host); - nni_strfree(url->u_hostname); - nni_strfree(url->u_port); - nni_strfree(url->u_path); - nni_strfree(url->u_query); - nni_strfree(url->u_fragment); - nni_strfree(url->u_requri); - NNI_FREE_STRUCT(url); + if (url != NULL) { + nni_strfree(url->u_rawurl); + nni_strfree(url->u_scheme); + nni_strfree(url->u_userinfo); + nni_strfree(url->u_host); + nni_strfree(url->u_hostname); + nni_strfree(url->u_port); + nni_strfree(url->u_path); + nni_strfree(url->u_query); + nni_strfree(url->u_fragment); + nni_strfree(url->u_requri); + NNI_FREE_STRUCT(url); + } } +#define URL_COPYSTR(d, s) ((s != NULL) && ((d = nni_strdup(s)) == NULL)) + int nni_url_clone(nni_url **dstp, const nni_url *src) { @@ -484,7 +501,6 @@ nni_url_clone(nni_url **dstp, const nni_url *src) if ((dst = NNI_ALLOC_STRUCT(dst)) == NULL) { return (NNG_ENOMEM); } -#define URL_COPYSTR(d, s) ((s != NULL) && ((d = nni_strdup(s)) == NULL)) if (URL_COPYSTR(dst->u_rawurl, src->u_rawurl) || URL_COPYSTR(dst->u_scheme, src->u_scheme) || URL_COPYSTR(dst->u_userinfo, src->u_userinfo) || @@ -498,7 +514,8 @@ nni_url_clone(nni_url **dstp, const nni_url *src) nni_url_free(dst); return (NNG_ENOMEM); } -#undef URL_COPYSTR *dstp = dst; return (0); } + +#undef URL_COPYSTR diff --git a/src/core/url.h b/src/core/url.h index b96401bd..2358f6ba 100644 --- a/src/core/url.h +++ b/src/core/url.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 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 diff --git a/src/platform/posix/posix_ipc.h b/src/platform/posix/posix_ipc.h index bbe11f0d..f570b172 100644 --- a/src/platform/posix/posix_ipc.h +++ b/src/platform/posix/posix_ipc.h @@ -1,7 +1,7 @@ // // Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -10,6 +10,7 @@ // #include "core/nng_impl.h" +#include "core/stream.h" #ifdef NNG_PLATFORM_POSIX #include "platform/posix/posix_aio.h" @@ -17,6 +18,7 @@ #include <sys/types.h> // For mode_t struct nni_ipc_conn { + nng_stream stream; nni_posix_pfd * pfd; nni_list readq; nni_list writeq; @@ -24,27 +26,17 @@ struct nni_ipc_conn { nni_mtx mtx; nni_aio * dial_aio; nni_ipc_dialer *dialer; - nni_reap_item reap; }; struct nni_ipc_dialer { - nni_list connq; // pending connections - bool closed; - nni_mtx mtx; + nng_stream_dialer sd; + nni_list connq; // pending connections + bool closed; + nni_mtx mtx; + nng_sockaddr sa; }; -struct nni_ipc_listener { - nni_posix_pfd *pfd; - nng_sockaddr sa; - nni_list acceptq; - bool started; - bool closed; - char * path; - mode_t perms; - nni_mtx mtx; -}; - -extern int nni_posix_ipc_conn_init(nni_ipc_conn **, nni_posix_pfd *); -extern void nni_posix_ipc_conn_start(nni_ipc_conn *); +extern int nni_posix_ipc_init(nni_ipc_conn **, nni_posix_pfd *); +extern void nni_posix_ipc_start(nni_ipc_conn *); #endif // NNG_PLATFORM_POSIX diff --git a/src/platform/posix/posix_ipcconn.c b/src/platform/posix/posix_ipcconn.c index 48bd75a4..07ec6213 100644 --- a/src/platform/posix/posix_ipcconn.c +++ b/src/platform/posix/posix_ipcconn.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -39,8 +39,10 @@ #include "posix_ipc.h" +typedef struct nni_ipc_conn ipc_conn; + static void -ipc_conn_dowrite(nni_ipc_conn *c) +ipc_dowrite(ipc_conn *c) { nni_aio *aio; int fd; @@ -122,7 +124,7 @@ ipc_conn_dowrite(nni_ipc_conn *c) } static void -ipc_conn_doread(nni_ipc_conn *c) +ipc_doread(ipc_conn *c) { nni_aio *aio; int fd; @@ -199,9 +201,10 @@ ipc_conn_doread(nni_ipc_conn *c) } } -void -nni_ipc_conn_close(nni_ipc_conn *c) +static void +ipc_close(void *arg) { + ipc_conn *c = arg; nni_mtx_lock(&c->mtx); if (!c->closed) { nni_aio *aio; @@ -217,20 +220,20 @@ nni_ipc_conn_close(nni_ipc_conn *c) } static void -ipc_conn_cb(nni_posix_pfd *pfd, int events, void *arg) +ipc_cb(nni_posix_pfd *pfd, int events, void *arg) { - nni_ipc_conn *c = arg; + ipc_conn *c = arg; if (events & (POLLHUP | POLLERR | POLLNVAL)) { - nni_ipc_conn_close(c); + ipc_close(c); return; } nni_mtx_lock(&c->mtx); if (events & POLLIN) { - ipc_conn_doread(c); + ipc_doread(c); } if (events & POLLOUT) { - ipc_conn_dowrite(c); + ipc_dowrite(c); } events = 0; if (!nni_list_empty(&c->writeq)) { @@ -246,9 +249,9 @@ ipc_conn_cb(nni_posix_pfd *pfd, int events, void *arg) } static void -ipc_conn_cancel(nni_aio *aio, void *arg, int rv) +ipc_cancel(nni_aio *aio, void *arg, int rv) { - nni_ipc_conn *c = arg; + ipc_conn *c = arg; nni_mtx_lock(&c->mtx); if (nni_aio_list_active(aio)) { @@ -258,18 +261,18 @@ ipc_conn_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&c->mtx); } -void -nni_ipc_conn_send(nni_ipc_conn *c, nni_aio *aio) +static void +ipc_send(void *arg, nni_aio *aio) { - - int rv; + ipc_conn *c = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; } nni_mtx_lock(&c->mtx); - if ((rv = nni_aio_schedule(aio, ipc_conn_cancel, c)) != 0) { + if ((rv = nni_aio_schedule(aio, ipc_cancel, c)) != 0) { nni_mtx_unlock(&c->mtx); nni_aio_finish_error(aio, rv); return; @@ -277,7 +280,7 @@ nni_ipc_conn_send(nni_ipc_conn *c, nni_aio *aio) nni_aio_list_append(&c->writeq, aio); if (nni_list_first(&c->writeq) == aio) { - ipc_conn_dowrite(c); + ipc_dowrite(c); // If we are still the first thing on the list, that // means we didn't finish the job, so arm the poller to // complete us. @@ -288,17 +291,18 @@ nni_ipc_conn_send(nni_ipc_conn *c, nni_aio *aio) nni_mtx_unlock(&c->mtx); } -void -nni_ipc_conn_recv(nni_ipc_conn *c, nni_aio *aio) +static void +ipc_recv(void *arg, nni_aio *aio) { - int rv; + ipc_conn *c = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; } nni_mtx_lock(&c->mtx); - if ((rv = nni_aio_schedule(aio, ipc_conn_cancel, c)) != 0) { + if ((rv = nni_aio_schedule(aio, ipc_cancel, c)) != 0) { nni_mtx_unlock(&c->mtx); nni_aio_finish_error(aio, rv); return; @@ -310,7 +314,7 @@ nni_ipc_conn_recv(nni_ipc_conn *c, nni_aio *aio) // many cases. We also need not arm a list if it was already // armed. if (nni_list_first(&c->readq) == aio) { - ipc_conn_doread(c); + ipc_doread(c); // If we are still the first thing on the list, that // means we didn't finish the job, so arm the poller to // complete us. @@ -322,8 +326,8 @@ nni_ipc_conn_recv(nni_ipc_conn *c, nni_aio *aio) } static int -ipc_conn_peerid(nni_ipc_conn *c, uint64_t *euid, uint64_t *egid, - uint64_t *prid, uint64_t *znid) +ipc_peerid(ipc_conn *c, uint64_t *euid, uint64_t *egid, uint64_t *prid, + uint64_t *znid) { int fd = nni_posix_pfd_fd(c->pfd); #if defined(NNG_HAVE_GETPEEREID) @@ -403,43 +407,43 @@ ipc_conn_peerid(nni_ipc_conn *c, uint64_t *euid, uint64_t *egid, #endif } -int -ipc_conn_get_peer_uid(void *arg, void *buf, size_t *szp, nni_type t) +static int +ipc_get_peer_uid(void *arg, void *buf, size_t *szp, nni_type t) { - nni_ipc_conn *c = arg; - int rv; - uint64_t ignore; - uint64_t id; + ipc_conn *c = arg; + int rv; + uint64_t ignore; + uint64_t id; - if ((rv = ipc_conn_peerid(c, &id, &ignore, &ignore, &ignore)) != 0) { + if ((rv = ipc_peerid(c, &id, &ignore, &ignore, &ignore)) != 0) { return (rv); } return (nni_copyout_u64(id, buf, szp, t)); } static int -ipc_conn_get_peer_gid(void *arg, void *buf, size_t *szp, nni_type t) +ipc_get_peer_gid(void *arg, void *buf, size_t *szp, nni_type t) { - nni_ipc_conn *c = arg; - int rv; - uint64_t ignore; - uint64_t id; + ipc_conn *c = arg; + int rv; + uint64_t ignore; + uint64_t id; - if ((rv = ipc_conn_peerid(c, &ignore, &id, &ignore, &ignore)) != 0) { + if ((rv = ipc_peerid(c, &ignore, &id, &ignore, &ignore)) != 0) { return (rv); } return (nni_copyout_u64(id, buf, szp, t)); } static int -ipc_conn_get_peer_zoneid(void *arg, void *buf, size_t *szp, nni_type t) +ipc_get_peer_zoneid(void *arg, void *buf, size_t *szp, nni_type t) { - nni_ipc_conn *c = arg; - int rv; - uint64_t ignore; - uint64_t id; + ipc_conn *c = arg; + int rv; + uint64_t ignore; + uint64_t id; - if ((rv = ipc_conn_peerid(c, &ignore, &ignore, &ignore, &id)) != 0) { + if ((rv = ipc_peerid(c, &ignore, &ignore, &ignore, &id)) != 0) { return (rv); } if (id == (uint64_t) -1) { @@ -450,14 +454,14 @@ ipc_conn_get_peer_zoneid(void *arg, void *buf, size_t *szp, nni_type t) } static int -ipc_conn_get_peer_pid(void *arg, void *buf, size_t *szp, nni_type t) +ipc_get_peer_pid(void *arg, void *buf, size_t *szp, nni_type t) { - nni_ipc_conn *c = arg; - int rv; - uint64_t ignore; - uint64_t id; + ipc_conn *c = arg; + int rv; + uint64_t ignore; + uint64_t id; - if ((rv = ipc_conn_peerid(c, &ignore, &ignore, &id, &ignore)) != 0) { + if ((rv = ipc_peerid(c, &ignore, &ignore, &id, &ignore)) != 0) { return (rv); } if (id == (uint64_t) -1) { @@ -468,9 +472,9 @@ ipc_conn_get_peer_pid(void *arg, void *buf, size_t *szp, nni_type t) } static int -ipc_conn_get_addr(void *arg, void *buf, size_t *szp, nni_type t) +ipc_get_addr(void *arg, void *buf, size_t *szp, nni_type t) { - nni_ipc_conn * c = arg; + ipc_conn * c = arg; nni_sockaddr sa; struct sockaddr_storage ss; socklen_t sslen = sizeof(ss); @@ -486,36 +490,17 @@ ipc_conn_get_addr(void *arg, void *buf, size_t *szp, nni_type t) return (nni_copyout_sockaddr(&sa, buf, szp, t)); } -int -nni_posix_ipc_conn_init(nni_ipc_conn **cp, nni_posix_pfd *pfd) -{ - nni_ipc_conn *c; - - if ((c = NNI_ALLOC_STRUCT(c)) == NULL) { - return (NNG_ENOMEM); - } - - c->closed = false; - c->pfd = pfd; - - nni_mtx_init(&c->mtx); - nni_aio_list_init(&c->readq); - nni_aio_list_init(&c->writeq); - - *cp = c; - return (0); -} - void -nni_posix_ipc_conn_start(nni_ipc_conn *c) +nni_posix_ipc_start(nni_ipc_conn *c) { - nni_posix_pfd_set_cb(c->pfd, ipc_conn_cb, c); + nni_posix_pfd_set_cb(c->pfd, ipc_cb, c); } -void -nni_ipc_conn_fini(nni_ipc_conn *c) +static void +ipc_free(void *arg) { - nni_ipc_conn_close(c); + ipc_conn *c = arg; + ipc_close(c); nni_posix_pfd_fini(c->pfd); nni_mtx_lock(&c->mtx); // not strictly needed, but shut up TSAN c->pfd = NULL; @@ -525,46 +510,72 @@ nni_ipc_conn_fini(nni_ipc_conn *c) NNI_FREE_STRUCT(c); } -static const nni_option ipc_conn_options[] = { +static const nni_option ipc_options[] = { { .o_name = NNG_OPT_LOCADDR, - .o_get = ipc_conn_get_addr, + .o_get = ipc_get_addr, }, { .o_name = NNG_OPT_REMADDR, - .o_get = ipc_conn_get_addr, + .o_get = ipc_get_addr, }, { .o_name = NNG_OPT_IPC_PEER_PID, - .o_get = ipc_conn_get_peer_pid, + .o_get = ipc_get_peer_pid, }, { .o_name = NNG_OPT_IPC_PEER_UID, - .o_get = ipc_conn_get_peer_uid, + .o_get = ipc_get_peer_uid, }, { .o_name = NNG_OPT_IPC_PEER_GID, - .o_get = ipc_conn_get_peer_gid, + .o_get = ipc_get_peer_gid, }, { .o_name = NNG_OPT_IPC_PEER_ZONEID, - .o_get = ipc_conn_get_peer_zoneid, + .o_get = ipc_get_peer_zoneid, }, { .o_name = NULL, }, }; -int -nni_ipc_conn_getopt( - nni_ipc_conn *c, const char *name, void *val, size_t *szp, nni_type t) +static int +ipc_getx(void *arg, const char *name, void *val, size_t *szp, nni_type t) { - return (nni_getopt(ipc_conn_options, name, c, val, szp, t)); + ipc_conn *c = arg; + return (nni_getopt(ipc_options, name, c, val, szp, t)); +} + +static int +ipc_setx(void *arg, const char *name, const void *val, size_t sz, nni_type t) +{ + ipc_conn *c = arg; + return (nni_setopt(ipc_options, name, c, val, sz, t)); } int -nni_ipc_conn_setopt( - nni_ipc_conn *c, const char *name, const void *val, size_t sz, nni_type t) +nni_posix_ipc_init(nni_ipc_conn **cp, nni_posix_pfd *pfd) { - return (nni_setopt(ipc_conn_options, name, c, val, sz, t)); -}
\ No newline at end of file + ipc_conn *c; + + if ((c = NNI_ALLOC_STRUCT(c)) == NULL) { + return (NNG_ENOMEM); + } + + c->closed = false; + c->pfd = pfd; + c->stream.s_free = ipc_free; + c->stream.s_close = ipc_close; + c->stream.s_send = ipc_send; + c->stream.s_recv = ipc_recv; + c->stream.s_getx = ipc_getx; + c->stream.s_setx = ipc_setx; + + nni_mtx_init(&c->mtx); + nni_aio_list_init(&c->readq); + nni_aio_list_init(&c->writeq); + + *cp = c; + return (0); +} diff --git a/src/platform/posix/posix_ipcdial.c b/src/platform/posix/posix_ipcdial.c index d3dc2109..3182b390 100644 --- a/src/platform/posix/posix_ipcdial.c +++ b/src/platform/posix/posix_ipcdial.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -29,25 +29,13 @@ #include "posix_ipc.h" -// Dialer stuff. -int -nni_ipc_dialer_init(nni_ipc_dialer **dp) -{ - nni_ipc_dialer *d; +typedef struct nni_ipc_dialer ipc_dialer; - if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { - return (NNG_ENOMEM); - } - nni_mtx_init(&d->mtx); - d->closed = false; - nni_aio_list_init(&d->connq); - *dp = d; - return (0); -} - -void -nni_ipc_dialer_close(nni_ipc_dialer *d) +// Dialer stuff. +static void +ipc_dialer_close(void *arg) { + ipc_dialer *d = arg; nni_mtx_lock(&d->mtx); if (!d->closed) { nni_aio *aio; @@ -58,9 +46,8 @@ nni_ipc_dialer_close(nni_ipc_dialer *d) if ((c = nni_aio_get_prov_extra(aio, 0)) != NULL) { c->dial_aio = NULL; nni_aio_set_prov_extra(aio, 0, NULL); - nni_ipc_conn_close(c); - nni_reap( - &c->reap, (nni_cb) nni_ipc_conn_fini, c); + nng_stream_close(&c->stream); + nng_stream_free(&c->stream); } nni_aio_finish_error(aio, NNG_ECLOSED); } @@ -68,10 +55,11 @@ nni_ipc_dialer_close(nni_ipc_dialer *d) nni_mtx_unlock(&d->mtx); } -void -nni_ipc_dialer_fini(nni_ipc_dialer *d) +static void +ipc_dialer_free(void *arg) { - nni_ipc_dialer_close(d); + ipc_dialer *d = arg; + ipc_dialer_close(d); nni_mtx_fini(&d->mtx); NNI_FREE_STRUCT(d); } @@ -94,7 +82,7 @@ ipc_dialer_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&d->mtx); nni_aio_finish_error(aio, rv); - nni_ipc_conn_fini(c); + nng_stream_free(&c->stream); } static void @@ -137,13 +125,13 @@ ipc_dialer_cb(nni_posix_pfd *pfd, int ev, void *arg) nni_mtx_unlock(&d->mtx); if (rv != 0) { - nni_ipc_conn_close(c); - nni_ipc_conn_fini(c); + nng_stream_close(&c->stream); + nng_stream_free(&c->stream); nni_aio_finish_error(aio, rv); return; } - nni_posix_ipc_conn_start(c); + nni_posix_ipc_start(c); nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); } @@ -151,8 +139,9 @@ ipc_dialer_cb(nni_posix_pfd *pfd, int ev, void *arg) // We don't give local address binding support. Outbound dialers always // get an ephemeral port. void -nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) +ipc_dialer_dial(void *arg, nni_aio *aio) { + ipc_dialer * d = arg; nni_ipc_conn * c; nni_posix_pfd * pfd = NULL; struct sockaddr_storage ss; @@ -164,7 +153,7 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) return; } - if (((sslen = nni_posix_nn2sockaddr(&ss, sa)) == 0) || + if (((sslen = nni_posix_nn2sockaddr(&ss, &d->sa)) == 0) || (ss.ss_family != AF_UNIX)) { nni_aio_finish_error(aio, NNG_EADDRINVAL); return; @@ -182,7 +171,7 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) nni_aio_finish_error(aio, rv); return; } - if ((rv = nni_posix_ipc_conn_init(&c, pfd)) != 0) { + if ((rv = nni_posix_ipc_init(&c, pfd)) != 0) { nni_posix_pfd_fini(pfd); nni_aio_finish_error(aio, rv); return; @@ -222,7 +211,7 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) // on loopback, and probably not on every platform. nni_aio_set_prov_extra(aio, 0, NULL); nni_mtx_unlock(&d->mtx); - nni_posix_ipc_conn_start(c); + nni_posix_ipc_start(c); nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); return; @@ -230,7 +219,7 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) error: nni_aio_set_prov_extra(aio, 0, NULL); nni_mtx_unlock(&d->mtx); - nni_reap(&c->reap, (nni_cb) nni_ipc_conn_fini, c); + nng_stream_free(&c->stream); nni_aio_finish_error(aio, rv); } @@ -241,15 +230,44 @@ static const nni_option ipc_dialer_options[] = { }; int -nni_ipc_dialer_getopt( - nni_ipc_dialer *d, const char *name, void *buf, size_t *szp, nni_type t) +ipc_dialer_getx(void *arg, const char *nm, void *buf, size_t *szp, nni_type t) +{ + ipc_dialer *d = arg; + return (nni_getopt(ipc_dialer_options, nm, d, buf, szp, t)); +} + +int +ipc_dialer_setx( + void *arg, const char *nm, const void *buf, size_t sz, nni_type t) { - return (nni_getopt(ipc_dialer_options, name, d, buf, szp, t)); + ipc_dialer *d = arg; + return (nni_setopt(ipc_dialer_options, nm, d, buf, sz, t)); } int -nni_ipc_dialer_setopt(nni_ipc_dialer *d, const char *name, const void *buf, - size_t sz, nni_type t) +nni_ipc_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) { - return (nni_setopt(ipc_dialer_options, name, d, buf, sz, t)); -}
\ No newline at end of file + ipc_dialer *d; + + if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || + (strlen(url->u_path) == 0) || + (strlen(url->u_path) >= NNG_MAXADDRLEN)) { + return (NNG_EADDRINVAL); + } + if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { + return (NNG_ENOMEM); + } + nni_mtx_init(&d->mtx); + nni_aio_list_init(&d->connq); + d->closed = false; + d->sa.s_ipc.sa_family = NNG_AF_IPC; + strcpy(d->sa.s_ipc.sa_path, url->u_path); + d->sd.sd_free = ipc_dialer_free; + d->sd.sd_close = ipc_dialer_close; + d->sd.sd_dial = ipc_dialer_dial; + d->sd.sd_getx = ipc_dialer_getx; + d->sd.sd_setx = ipc_dialer_setx; + + *dp = (void *) d; + return (0); +} diff --git a/src/platform/posix/posix_ipclisten.c b/src/platform/posix/posix_ipclisten.c index 11b56ab0..2b5d0edd 100644 --- a/src/platform/posix/posix_ipclisten.c +++ b/src/platform/posix/posix_ipclisten.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -29,28 +29,20 @@ #include "posix_ipc.h" -int -nni_ipc_listener_init(nni_ipc_listener **lp) -{ - nni_ipc_listener *l; - if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { - return (NNG_ENOMEM); - } - - nni_mtx_init(&l->mtx); - - l->pfd = NULL; - l->closed = false; - l->started = false; - l->perms = 0; - - nni_aio_list_init(&l->acceptq); - *lp = l; - return (0); -} +typedef struct { + nng_stream_listener sl; + nni_posix_pfd * pfd; + nng_sockaddr sa; + nni_list acceptq; + bool started; + bool closed; + char * path; + mode_t perms; + nni_mtx mtx; +} ipc_listener; static void -ipc_listener_doclose(nni_ipc_listener *l) +ipc_listener_doclose(ipc_listener *l) { nni_aio *aio; char * path; @@ -71,16 +63,17 @@ ipc_listener_doclose(nni_ipc_listener *l) } } -void -nni_ipc_listener_close(nni_ipc_listener *l) +static void +ipc_listener_close(void *arg) { + ipc_listener *l = arg; nni_mtx_lock(&l->mtx); ipc_listener_doclose(l); nni_mtx_unlock(&l->mtx); } static void -ipc_listener_doaccept(nni_ipc_listener *l) +ipc_listener_doaccept(ipc_listener *l) { nni_aio *aio; @@ -138,7 +131,7 @@ ipc_listener_doaccept(nni_ipc_listener *l) continue; } - if ((rv = nni_posix_ipc_conn_init(&c, pfd)) != 0) { + if ((rv = nni_posix_ipc_init(&c, pfd)) != 0) { nni_posix_pfd_fini(pfd); nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); @@ -146,7 +139,7 @@ ipc_listener_doaccept(nni_ipc_listener *l) } nni_aio_list_remove(aio); - nni_posix_ipc_conn_start(c); + nni_posix_ipc_start(c); nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); } @@ -155,7 +148,7 @@ ipc_listener_doaccept(nni_ipc_listener *l) static void ipc_listener_cb(nni_posix_pfd *pfd, int events, void *arg) { - nni_ipc_listener *l = arg; + ipc_listener *l = arg; NNI_ARG_UNUSED(pfd); nni_mtx_lock(&l->mtx); @@ -173,7 +166,7 @@ ipc_listener_cb(nni_posix_pfd *pfd, int events, void *arg) static void ipc_listener_cancel(nni_aio *aio, void *arg, int rv) { - nni_ipc_listener *l = arg; + ipc_listener *l = arg; // This is dead easy, because we'll ignore the completion if there // isn't anything to do the accept on! @@ -222,16 +215,16 @@ ipc_remove_stale(const char *path) static int ipc_listener_get_addr(void *arg, void *buf, size_t *szp, nni_type t) { - nni_ipc_listener *l = arg; + ipc_listener *l = arg; return (nni_copyout_sockaddr(&l->sa, buf, szp, t)); } static int ipc_listener_set_perms(void *arg, const void *buf, size_t sz, nni_type t) { - nni_ipc_listener *l = arg; - int mode; - int rv; + ipc_listener *l = arg; + int mode; + int rv; if ((rv = nni_copyin_int(&mode, buf, sz, 0, S_IFMT, t)) != 0) { return (rv); @@ -239,16 +232,14 @@ ipc_listener_set_perms(void *arg, const void *buf, size_t sz, nni_type t) if ((mode & S_IFMT) != 0) { return (NNG_EINVAL); } - if (l != NULL) { - mode |= S_IFSOCK; // set IFSOCK to ensure non-zero - nni_mtx_lock(&l->mtx); - if (l->started) { - nni_mtx_unlock(&l->mtx); - return (NNG_EBUSY); - } - l->perms = mode; + mode |= S_IFSOCK; // set IFSOCK to ensure non-zero + nni_mtx_lock(&l->mtx); + if (l->started) { nni_mtx_unlock(&l->mtx); + return (NNG_EBUSY); } + l->perms = mode; + nni_mtx_unlock(&l->mtx); return (0); } @@ -266,23 +257,26 @@ static const nni_option ipc_listener_options[] = { }, }; -int -nni_ipc_listener_getopt( - nni_ipc_listener *l, const char *name, void *buf, size_t *szp, nni_type t) +static int +ipc_listener_getx( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { + ipc_listener *l = arg; return (nni_getopt(ipc_listener_options, name, l, buf, szp, t)); } -int -nni_ipc_listener_setopt(nni_ipc_listener *l, const char *name, const void *buf, - size_t sz, nni_type t) +static int +ipc_listener_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) { + ipc_listener *l = arg; return (nni_setopt(ipc_listener_options, name, l, buf, sz, t)); } int -nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) +ipc_listener_listen(void *arg) { + ipc_listener * l = arg; socklen_t len; struct sockaddr_storage ss; int rv; @@ -290,7 +284,7 @@ nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) nni_posix_pfd * pfd; char * path; - if (((len = nni_posix_nn2sockaddr(&ss, sa)) == 0) || + if (((len = nni_posix_nn2sockaddr(&ss, &l->sa)) == 0) || (ss.ss_family != AF_UNIX)) { return (NNG_EADDRINVAL); } @@ -304,7 +298,7 @@ nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) nni_mtx_unlock(&l->mtx); return (NNG_ECLOSED); } - path = nni_strdup(sa->s_ipc.sa_path); + path = nni_strdup(l->sa.s_ipc.sa_path); if (path == NULL) { return (NNG_ENOMEM); } @@ -352,15 +346,15 @@ nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) l->pfd = pfd; l->started = true; l->path = path; - l->sa = *sa; nni_mtx_unlock(&l->mtx); return (0); } -void -nni_ipc_listener_fini(nni_ipc_listener *l) +static void +ipc_listener_free(void *arg) { + ipc_listener * l = arg; nni_posix_pfd *pfd; nni_mtx_lock(&l->mtx); @@ -375,10 +369,11 @@ nni_ipc_listener_fini(nni_ipc_listener *l) NNI_FREE_STRUCT(l); } -void -nni_ipc_listener_accept(nni_ipc_listener *l, nni_aio *aio) +static void +ipc_listener_accept(void *arg, nni_aio *aio) { - int rv; + ipc_listener *l = arg; + int rv; // Accept is simpler than the connect case. With accept we just // need to wait for the socket to be readable to indicate an incoming @@ -410,3 +405,69 @@ nni_ipc_listener_accept(nni_ipc_listener *l, nni_aio *aio) } nni_mtx_unlock(&l->mtx); } + +int +nni_ipc_listener_alloc(nng_stream_listener **lp, const nng_url *url) +{ + ipc_listener *l; + + if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || + (strlen(url->u_path) == 0) || + (strlen(url->u_path) >= NNG_MAXADDRLEN)) { + return (NNG_EADDRINVAL); + } + + if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { + return (NNG_ENOMEM); + } + + nni_mtx_init(&l->mtx); + nni_aio_list_init(&l->acceptq); + + l->pfd = NULL; + l->closed = false; + l->started = false; + l->perms = 0; + l->sa.s_ipc.sa_family = NNG_AF_IPC; + strcpy(l->sa.s_ipc.sa_path, url->u_path); + l->sl.sl_free = ipc_listener_free; + l->sl.sl_close = ipc_listener_close; + l->sl.sl_listen = ipc_listener_listen; + l->sl.sl_accept = ipc_listener_accept; + l->sl.sl_getx = ipc_listener_getx; + l->sl.sl_setx = ipc_listener_setx; + + *lp = (void *) l; + return (0); +} + +static int +ipc_check_perms(const void *buf, size_t sz, nni_type t) +{ + int32_t mode; + int rv; + + if ((rv = nni_copyin_int(&mode, buf, sz, 0, S_IFMT, t)) != 0) { + return (rv); + } + if ((mode & S_IFMT) != 0) { + return (NNG_EINVAL); + } + return (0); +} + +static const nni_chkoption ipc_chkopts[] = { + { + .o_name = NNG_OPT_IPC_PERMISSIONS, + .o_check = ipc_check_perms, + }, + { + .o_name = NULL, + }, +}; + +int +nni_ipc_checkopt(const char *name, const void *data, size_t sz, nni_type t) +{ + return (nni_chkopt(ipc_chkopts, name, data, sz, t)); +} diff --git a/src/platform/posix/posix_resolv_gai.c b/src/platform/posix/posix_resolv_gai.c index b4d63b59..bb6db188 100644 --- a/src/platform/posix/posix_resolv_gai.c +++ b/src/platform/posix/posix_resolv_gai.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 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 @@ -321,10 +321,11 @@ resolv_worker(void *notused) // Check to make sure we were not canceled. if ((aio = item->aio) != NULL) { - nng_sockaddr *sa = nni_aio_get_input(aio, 0); + nni_aio_set_prov_extra(aio, 0, NULL); item->aio = NULL; - memcpy(sa, &item->sa, sizeof(*sa)); + + nni_aio_set_sockaddr(aio, &item->sa); nni_aio_finish(aio, rv, 0); NNI_FREE_STRUCT(item); diff --git a/src/platform/posix/posix_tcp.h b/src/platform/posix/posix_tcp.h index 788fcbf8..1638df61 100644 --- a/src/platform/posix/posix_tcp.h +++ b/src/platform/posix/posix_tcp.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2018 Devolutions <info@devolutions.net> // @@ -14,6 +14,7 @@ #include "platform/posix/posix_aio.h" struct nni_tcp_conn { + nng_stream stream; nni_posix_pfd * pfd; nni_list readq; nni_list writeq; @@ -23,26 +24,5 @@ struct nni_tcp_conn { nni_tcp_dialer *dialer; nni_reap_item reap; }; - -struct nni_tcp_dialer { - nni_list connq; // pending connections - bool closed; - bool nodelay; - bool keepalive; - struct sockaddr_storage src; - size_t srclen; - nni_mtx mtx; -}; - -struct nni_tcp_listener { - nni_posix_pfd *pfd; - nni_list acceptq; - bool started; - bool closed; - bool nodelay; - bool keepalive; - nni_mtx mtx; -}; - -extern int nni_posix_tcp_conn_init(nni_tcp_conn **, nni_posix_pfd *); -extern void nni_posix_tcp_conn_start(nni_tcp_conn *, int, int); +extern int nni_posix_tcp_init(nni_tcp_conn **, nni_posix_pfd *); +extern void nni_posix_tcp_start(nni_tcp_conn *, int, int); diff --git a/src/platform/posix/posix_tcpconn.c b/src/platform/posix/posix_tcpconn.c index ef6ee8e3..0d3c274d 100644 --- a/src/platform/posix/posix_tcpconn.c +++ b/src/platform/posix/posix_tcpconn.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2019 Devolutions <info@devolutions.net> // @@ -36,7 +36,7 @@ #include "posix_tcp.h" static void -tcp_conn_dowrite(nni_tcp_conn *c) +tcp_dowrite(nni_tcp_conn *c) { nni_aio *aio; int fd; @@ -118,7 +118,7 @@ tcp_conn_dowrite(nni_tcp_conn *c) } static void -tcp_conn_doread(nni_tcp_conn *c) +tcp_doread(nni_tcp_conn *c) { nni_aio *aio; int fd; @@ -195,9 +195,10 @@ tcp_conn_doread(nni_tcp_conn *c) } } -void -nni_tcp_conn_close(nni_tcp_conn *c) +static void +tcp_close(void *arg) { + nni_tcp_conn *c = arg; nni_mtx_lock(&c->mtx); if (!c->closed) { nni_aio *aio; @@ -212,21 +213,44 @@ nni_tcp_conn_close(nni_tcp_conn *c) nni_mtx_unlock(&c->mtx); } +// tcp_fini may block briefly waiting for the pollq thread. +// To get that out of our context, we simply reap this. +static void +tcp_fini(void *arg) +{ + nni_tcp_conn *c = arg; + tcp_close(c); + nni_posix_pfd_fini(c->pfd); + nni_mtx_lock(&c->mtx); // not strictly needed, but shut up TSAN + c->pfd = NULL; + nni_mtx_unlock(&c->mtx); + nni_mtx_fini(&c->mtx); + + NNI_FREE_STRUCT(c); +} + static void -tcp_conn_cb(nni_posix_pfd *pfd, int events, void *arg) +tcp_free(void *arg) +{ + nni_tcp_conn *c = arg; + nni_reap(&c->reap, tcp_fini, arg); +} + +static void +tcp_cb(nni_posix_pfd *pfd, int events, void *arg) { nni_tcp_conn *c = arg; if (events & (POLLHUP | POLLERR | POLLNVAL)) { - nni_tcp_conn_close(c); + tcp_close(c); return; } nni_mtx_lock(&c->mtx); if (events & POLLIN) { - tcp_conn_doread(c); + tcp_doread(c); } if (events & POLLOUT) { - tcp_conn_dowrite(c); + tcp_dowrite(c); } events = 0; if (!nni_list_empty(&c->writeq)) { @@ -242,7 +266,7 @@ tcp_conn_cb(nni_posix_pfd *pfd, int events, void *arg) } static void -tcp_conn_cancel(nni_aio *aio, void *arg, int rv) +tcp_cancel(nni_aio *aio, void *arg, int rv) { nni_tcp_conn *c = arg; @@ -254,18 +278,18 @@ tcp_conn_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&c->mtx); } -void -nni_tcp_conn_send(nni_tcp_conn *c, nni_aio *aio) +static void +tcp_send(void *arg, nni_aio *aio) { - - int rv; + nni_tcp_conn *c = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; } nni_mtx_lock(&c->mtx); - if ((rv = nni_aio_schedule(aio, tcp_conn_cancel, c)) != 0) { + if ((rv = nni_aio_schedule(aio, tcp_cancel, c)) != 0) { nni_mtx_unlock(&c->mtx); nni_aio_finish_error(aio, rv); return; @@ -273,7 +297,7 @@ nni_tcp_conn_send(nni_tcp_conn *c, nni_aio *aio) nni_aio_list_append(&c->writeq, aio); if (nni_list_first(&c->writeq) == aio) { - tcp_conn_dowrite(c); + tcp_dowrite(c); // If we are still the first thing on the list, that // means we didn't finish the job, so arm the poller to // complete us. @@ -284,17 +308,18 @@ nni_tcp_conn_send(nni_tcp_conn *c, nni_aio *aio) nni_mtx_unlock(&c->mtx); } -void -nni_tcp_conn_recv(nni_tcp_conn *c, nni_aio *aio) +static void +tcp_recv(void *arg, nni_aio *aio) { - int rv; + nni_tcp_conn *c = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; } nni_mtx_lock(&c->mtx); - if ((rv = nni_aio_schedule(aio, tcp_conn_cancel, c)) != 0) { + if ((rv = nni_aio_schedule(aio, tcp_cancel, c)) != 0) { nni_mtx_unlock(&c->mtx); nni_aio_finish_error(aio, rv); return; @@ -306,7 +331,7 @@ nni_tcp_conn_recv(nni_tcp_conn *c, nni_aio *aio) // many cases. We also need not arm a list if it was already // armed. if (nni_list_first(&c->readq) == aio) { - tcp_conn_doread(c); + tcp_doread(c); // If we are still the first thing on the list, that // means we didn't finish the job, so arm the poller to // complete us. @@ -317,59 +342,8 @@ nni_tcp_conn_recv(nni_tcp_conn *c, nni_aio *aio) nni_mtx_unlock(&c->mtx); } -int -nni_tcp_conn_peername(nni_tcp_conn *c, nni_sockaddr *sa) -{ - struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); - int fd = nni_posix_pfd_fd(c->pfd); - - if (getpeername(fd, (void *) &ss, &sslen) != 0) { - return (nni_plat_errno(errno)); - } - return (nni_posix_sockaddr2nn(sa, &ss)); -} - -int -nni_tcp_conn_sockname(nni_tcp_conn *c, nni_sockaddr *sa) -{ - struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); - int fd = nni_posix_pfd_fd(c->pfd); - - if (getsockname(fd, (void *) &ss, &sslen) != 0) { - return (nni_plat_errno(errno)); - } - return (nni_posix_sockaddr2nn(sa, &ss)); -} - -int -nni_tcp_conn_set_keepalive(nni_tcp_conn *c, bool keep) -{ - int val = keep ? 1 : 0; - int fd = nni_posix_pfd_fd(c->pfd); - - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) { - return (nni_plat_errno(errno)); - } - return (0); -} - -int -nni_tcp_conn_set_nodelay(nni_tcp_conn *c, bool nodelay) -{ - - int val = nodelay ? 1 : 0; - int fd = nni_posix_pfd_fd(c->pfd); - - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != 0) { - return (nni_plat_errno(errno)); - } - return (0); -} - static int -tcp_conn_get_peername(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_peername(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn * c = arg; struct sockaddr_storage ss; @@ -388,7 +362,7 @@ tcp_conn_get_peername(void *arg, void *buf, size_t *szp, nni_type t) } static int -tcp_conn_get_sockname(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_sockname(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn * c = arg; struct sockaddr_storage ss; @@ -407,7 +381,7 @@ tcp_conn_get_sockname(void *arg, void *buf, size_t *szp, nni_type t) } static int -tcp_conn_set_nodelay(void *arg, const void *buf, size_t sz, nni_type t) +tcp_set_nodelay(void *arg, const void *buf, size_t sz, nni_type t) { nni_tcp_conn *c = arg; int fd; @@ -427,7 +401,7 @@ tcp_conn_set_nodelay(void *arg, const void *buf, size_t sz, nni_type t) } static int -tcp_conn_set_keepalive(void *arg, const void *buf, size_t sz, nni_type t) +tcp_set_keepalive(void *arg, const void *buf, size_t sz, nni_type t) { nni_tcp_conn *c = arg; int fd; @@ -447,7 +421,7 @@ tcp_conn_set_keepalive(void *arg, const void *buf, size_t sz, nni_type t) } static int -tcp_conn_get_nodelay(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_nodelay(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn *c = arg; int fd = nni_posix_pfd_fd(c->pfd); @@ -462,7 +436,7 @@ tcp_conn_get_nodelay(void *arg, void *buf, size_t *szp, nni_type t) } static int -tcp_conn_get_keepalive(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_keepalive(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn *c = arg; int fd = nni_posix_pfd_fd(c->pfd); @@ -476,46 +450,46 @@ tcp_conn_get_keepalive(void *arg, void *buf, size_t *szp, nni_type t) return (nni_copyout_bool(val, buf, szp, t)); } -static const nni_option tcp_conn_options[] = { +static const nni_option tcp_options[] = { { .o_name = NNG_OPT_REMADDR, - .o_get = tcp_conn_get_peername, + .o_get = tcp_get_peername, }, { .o_name = NNG_OPT_LOCADDR, - .o_get = tcp_conn_get_sockname, + .o_get = tcp_get_sockname, }, { .o_name = NNG_OPT_TCP_NODELAY, - .o_get = tcp_conn_get_nodelay, - .o_set = tcp_conn_set_nodelay, + .o_get = tcp_get_nodelay, + .o_set = tcp_set_nodelay, }, { .o_name = NNG_OPT_TCP_KEEPALIVE, - .o_get = tcp_conn_get_keepalive, - .o_set = tcp_conn_set_keepalive, + .o_get = tcp_get_keepalive, + .o_set = tcp_set_keepalive, }, { .o_name = NULL, }, }; -int -nni_tcp_conn_getopt( - nni_tcp_conn *c, const char *name, void *buf, size_t *szp, nni_type t) +static int +tcp_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - return (nni_getopt(tcp_conn_options, name, c, buf, szp, t)); + nni_tcp_conn *c = arg; + return (nni_getopt(tcp_options, name, c, buf, szp, t)); } -int -nni_tcp_conn_setopt( - nni_tcp_conn *c, const char *name, const void *buf, size_t sz, nni_type t) +static int +tcp_setx(void *arg, const char *name, const void *buf, size_t sz, nni_type t) { - return (nni_setopt(tcp_conn_options, name, c, buf, sz, t)); + nni_tcp_conn *c = arg; + return (nni_setopt(tcp_options, name, c, buf, sz, t)); } int -nni_posix_tcp_conn_init(nni_tcp_conn **cp, nni_posix_pfd *pfd) +nni_posix_tcp_init(nni_tcp_conn **cp, nni_posix_pfd *pfd) { nni_tcp_conn *c; @@ -530,12 +504,19 @@ nni_posix_tcp_conn_init(nni_tcp_conn **cp, nni_posix_pfd *pfd) nni_aio_list_init(&c->readq); nni_aio_list_init(&c->writeq); + c->stream.s_free = tcp_free; + c->stream.s_close = tcp_close; + c->stream.s_recv = tcp_recv; + c->stream.s_send = tcp_send; + c->stream.s_getx = tcp_getx; + c->stream.s_setx = tcp_setx; + *cp = c; return (0); } void -nni_posix_tcp_conn_start(nni_tcp_conn *c, int nodelay, int keepalive) +nni_posix_tcp_start(nni_tcp_conn *c, int nodelay, int keepalive) { // Configure the initial socket options. (void) setsockopt(nni_posix_pfd_fd(c->pfd), IPPROTO_TCP, TCP_NODELAY, @@ -543,18 +524,5 @@ nni_posix_tcp_conn_start(nni_tcp_conn *c, int nodelay, int keepalive) (void) setsockopt(nni_posix_pfd_fd(c->pfd), SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(int)); - nni_posix_pfd_set_cb(c->pfd, tcp_conn_cb, c); -} - -void -nni_tcp_conn_fini(nni_tcp_conn *c) -{ - nni_tcp_conn_close(c); - nni_posix_pfd_fini(c->pfd); - nni_mtx_lock(&c->mtx); // not strictly needed, but shut up TSAN - c->pfd = NULL; - nni_mtx_unlock(&c->mtx); - nni_mtx_fini(&c->mtx); - - NNI_FREE_STRUCT(c); + nni_posix_pfd_set_cb(c->pfd, tcp_cb, c); } diff --git a/src/platform/posix/posix_tcpdial.c b/src/platform/posix/posix_tcpdial.c index cfb3482c..21ad862d 100644 --- a/src/platform/posix/posix_tcpdial.c +++ b/src/platform/posix/posix_tcpdial.c @@ -29,6 +29,16 @@ #include "posix_tcp.h" +struct nni_tcp_dialer { + nni_list connq; // pending connections + bool closed; + bool nodelay; + bool keepalive; + struct sockaddr_storage src; + size_t srclen; + nni_mtx mtx; +}; + // Dialer stuff. int nni_tcp_dialer_init(nni_tcp_dialer **dp) @@ -58,9 +68,8 @@ nni_tcp_dialer_close(nni_tcp_dialer *d) if ((c = nni_aio_get_prov_extra(aio, 0)) != NULL) { c->dial_aio = NULL; nni_aio_set_prov_extra(aio, 0, NULL); - nni_tcp_conn_close(c); - nni_reap( - &c->reap, (nni_cb) nni_tcp_conn_fini, c); + nng_stream_close(&c->stream); + nng_stream_free(&c->stream); } nni_aio_finish_error(aio, NNG_ECLOSED); } @@ -94,7 +103,7 @@ tcp_dialer_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&d->mtx); nni_aio_finish_error(aio, rv); - nni_tcp_conn_fini(c); + nng_stream_free(&c->stream); } static void @@ -142,13 +151,13 @@ tcp_dialer_cb(nni_posix_pfd *pfd, int ev, void *arg) nni_mtx_unlock(&d->mtx); if (rv != 0) { - nni_tcp_conn_close(c); - nni_tcp_conn_fini(c); + nng_stream_close(&c->stream); + nng_stream_free(&c->stream); nni_aio_finish_error(aio, rv); return; } - nni_posix_tcp_conn_start(c, nd, ka); + nni_posix_tcp_start(c, nd, ka); nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); } @@ -156,7 +165,7 @@ tcp_dialer_cb(nni_posix_pfd *pfd, int ev, void *arg) // We don't give local address binding support. Outbound dialers always // get an ephemeral port. void -nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) +nni_tcp_dial(nni_tcp_dialer *d, nni_aio *aio) { nni_tcp_conn * c; nni_posix_pfd * pfd = NULL; @@ -166,12 +175,14 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) int rv; int ka; int nd; + nng_sockaddr sa; if (nni_aio_begin(aio) != 0) { return; } - if (((sslen = nni_posix_nn2sockaddr(&ss, sa)) == 0) || + nni_aio_get_sockaddr(aio, &sa); + if (((sslen = nni_posix_nn2sockaddr(&ss, &sa)) == 0) || ((ss.ss_family != AF_INET) && (ss.ss_family != AF_INET6))) { nni_aio_finish_error(aio, NNG_EADDRINVAL); return; @@ -189,7 +200,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) nni_aio_finish_error(aio, rv); return; } - if ((rv = nni_posix_tcp_conn_init(&c, pfd)) != 0) { + if ((rv = nni_posix_tcp_init(&c, pfd)) != 0) { nni_posix_pfd_fini(pfd); nni_aio_finish_error(aio, rv); return; @@ -232,7 +243,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) nd = d->nodelay ? 1 : 0; ka = d->keepalive ? 1 : 0; nni_mtx_unlock(&d->mtx); - nni_posix_tcp_conn_start(c, nd, ka); + nni_posix_tcp_start(c, nd, ka); nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); return; @@ -240,7 +251,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) error: nni_aio_set_prov_extra(aio, 0, NULL); nni_mtx_unlock(&d->mtx); - nni_reap(&c->reap, (nni_cb) nni_tcp_conn_fini, c); + nng_stream_free(&c->stream); nni_aio_finish_error(aio, rv); } diff --git a/src/platform/posix/posix_tcplisten.c b/src/platform/posix/posix_tcplisten.c index 1e1b84b1..1edeccbc 100644 --- a/src/platform/posix/posix_tcplisten.c +++ b/src/platform/posix/posix_tcplisten.c @@ -29,6 +29,16 @@ #include "posix_tcp.h" +struct nni_tcp_listener { + nni_posix_pfd *pfd; + nni_list acceptq; + bool started; + bool closed; + bool nodelay; + bool keepalive; + nni_mtx mtx; +}; + int nni_tcp_listener_init(nni_tcp_listener **lp) { @@ -133,7 +143,7 @@ tcp_listener_doaccept(nni_tcp_listener *l) continue; } - if ((rv = nni_posix_tcp_conn_init(&c, pfd)) != 0) { + if ((rv = nni_posix_tcp_init(&c, pfd)) != 0) { nni_posix_pfd_fini(pfd); nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); @@ -143,7 +153,7 @@ tcp_listener_doaccept(nni_tcp_listener *l) ka = l->keepalive ? 1 : 0; nd = l->nodelay ? 1 : 0; nni_aio_list_remove(aio); - nni_posix_tcp_conn_start(c, nd, ka); + nni_posix_tcp_start(c, nd, ka); nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); } diff --git a/src/platform/windows/win_ipc.h b/src/platform/windows/win_ipc.h index e8e83957..d410b980 100644 --- a/src/platform/windows/win_ipc.h +++ b/src/platform/windows/win_ipc.h @@ -1,7 +1,7 @@ // // Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -19,45 +19,6 @@ #define IPC_PIPE_PREFIX "\\\\.\\pipe\\" -struct nni_ipc_conn { - HANDLE f; - nni_win_io recv_io; - nni_win_io send_io; - nni_win_io conn_io; - nni_list recv_aios; - nni_list send_aios; - nni_aio * conn_aio; - nng_sockaddr sa; - bool dialer; - int recv_rv; - int send_rv; - int conn_rv; - bool closed; - nni_mtx mtx; - nni_cv cv; - nni_reap_item reap; -}; - -struct nni_ipc_dialer { - bool closed; // dialers are locked by the worker lock - nni_list aios; - nni_list_node node; // node on worker list -}; - -struct nni_ipc_listener { - char * path; - bool started; - bool closed; - HANDLE f; - SECURITY_ATTRIBUTES sec_attr; - nni_list aios; - nni_mtx mtx; - nni_cv cv; - nni_win_io io; - nni_sockaddr sa; - int rv; -}; - -extern int nni_win_ipc_conn_init(nni_ipc_conn **, HANDLE); +extern int nni_win_ipc_init(nng_stream **, HANDLE, const nng_sockaddr *, bool); #endif // NNG_PLATFORM_WIN_WINIPC_H diff --git a/src/platform/windows/win_ipcconn.c b/src/platform/windows/win_ipcconn.c index ded9ed76..4d267dd9 100644 --- a/src/platform/windows/win_ipcconn.c +++ b/src/platform/windows/win_ipcconn.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -15,10 +15,30 @@ #include <stdio.h> -#define CONN(c) ((nni_ipc_conn *) (c)) +#define CONN(c) ((ipc_conn *) (c)) + +typedef struct ipc_conn { + nng_stream stream; + HANDLE f; + nni_win_io recv_io; + nni_win_io send_io; + nni_win_io conn_io; + nni_list recv_aios; + nni_list send_aios; + nni_aio * conn_aio; + nng_sockaddr sa; + bool dialer; + int recv_rv; + int send_rv; + int conn_rv; + bool closed; + nni_mtx mtx; + nni_cv cv; + nni_reap_item reap; +} ipc_conn; static void -ipc_recv_start(nni_ipc_conn *c) +ipc_recv_start(ipc_conn *c) { nni_aio *aio; unsigned idx; @@ -75,8 +95,8 @@ again: static void ipc_recv_cb(nni_win_io *io, int rv, size_t num) { - nni_aio * aio; - nni_ipc_conn *c = io->ptr; + nni_aio * aio; + ipc_conn *c = io->ptr; nni_mtx_lock(&c->mtx); if ((aio = nni_list_first(&c->recv_aios)) == NULL) { // Should indicate that it was closed. @@ -103,7 +123,7 @@ ipc_recv_cb(nni_win_io *io, int rv, size_t num) static void ipc_recv_cancel(nni_aio *aio, void *arg, int rv) { - nni_ipc_conn *c = arg; + ipc_conn *c = arg; nni_mtx_lock(&c->mtx); if (aio == nni_list_first(&c->recv_aios)) { c->recv_rv = rv; @@ -116,10 +136,11 @@ ipc_recv_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&c->mtx); } -void -nni_ipc_conn_recv(nni_ipc_conn *c, nni_aio *aio) +static void +ipc_recv(void *arg, nni_aio *aio) { - int rv; + ipc_conn *c = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -143,7 +164,7 @@ nni_ipc_conn_recv(nni_ipc_conn *c, nni_aio *aio) } static void -ipc_send_start(nni_ipc_conn *c) +ipc_send_start(ipc_conn *c) { nni_aio *aio; unsigned idx; @@ -200,8 +221,8 @@ again: static void ipc_send_cb(nni_win_io *io, int rv, size_t num) { - nni_aio * aio; - nni_ipc_conn *c = io->ptr; + nni_aio * aio; + ipc_conn *c = io->ptr; nni_mtx_lock(&c->mtx); if ((aio = nni_list_first(&c->send_aios)) == NULL) { // Should indicate that it was closed. @@ -229,7 +250,7 @@ ipc_send_cb(nni_win_io *io, int rv, size_t num) static void ipc_send_cancel(nni_aio *aio, void *arg, int rv) { - nni_ipc_conn *c = arg; + ipc_conn *c = arg; nni_mtx_lock(&c->mtx); if (aio == nni_list_first(&c->send_aios)) { c->send_rv = rv; @@ -242,10 +263,11 @@ ipc_send_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&c->mtx); } -void -nni_ipc_conn_send(nni_ipc_conn *c, nni_aio *aio) +static void +ipc_send(void *arg, nni_aio *aio) { - int rv; + ipc_conn *c = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -268,35 +290,10 @@ nni_ipc_conn_send(nni_ipc_conn *c, nni_aio *aio) nni_mtx_unlock(&c->mtx); } -int -nni_win_ipc_conn_init(nni_ipc_conn **connp, HANDLE p) -{ - nni_ipc_conn *c; - int rv; - - if ((c = NNI_ALLOC_STRUCT(c)) == NULL) { - return (NNG_ENOMEM); - } - c->f = INVALID_HANDLE_VALUE; - nni_mtx_init(&c->mtx); - nni_cv_init(&c->cv, &c->mtx); - nni_aio_list_init(&c->recv_aios); - nni_aio_list_init(&c->send_aios); - - if (((rv = nni_win_io_init(&c->recv_io, ipc_recv_cb, c)) != 0) || - ((rv = nni_win_io_init(&c->send_io, ipc_send_cb, c)) != 0)) { - nni_ipc_conn_fini(c); - return (rv); - } - - c->f = p; - *connp = c; - return (0); -} - -void -nni_ipc_conn_close(nni_ipc_conn *c) +static void +ipc_close(void *arg) { + ipc_conn *c = arg; nni_mtx_lock(&c->mtx); if (!c->closed) { c->closed = true; @@ -316,7 +313,7 @@ nni_ipc_conn_close(nni_ipc_conn *c) } static void -ipc_conn_reap(nni_ipc_conn *c) +ipc_conn_reap(ipc_conn *c) { nni_mtx_lock(&c->mtx); while ((!nni_list_empty(&c->recv_aios)) || @@ -337,10 +334,11 @@ ipc_conn_reap(nni_ipc_conn *c) NNI_FREE_STRUCT(c); } -void -nni_ipc_conn_fini(nni_ipc_conn *c) +static void +ipc_free(void *arg) { - nni_ipc_conn_close(c); + ipc_conn *c = arg; + ipc_close(c); nni_reap(&c->reap, (nni_cb) ipc_conn_reap, CONN(c)); } @@ -386,16 +384,51 @@ static const nni_option ipc_conn_options[] = { }, }; -int -nni_ipc_conn_setopt(nni_ipc_conn *c, const char *name, const void *val, - size_t sz, nni_opt_type t) +static int +ipc_setx(void *arg, const char *nm, const void *val, size_t sz, nni_opt_type t) +{ + ipc_conn *c = arg; + return (nni_setopt(ipc_conn_options, nm, c, val, sz, t)); +} + +static int +ipc_getx(void *arg, const char *nm, void *val, size_t *szp, nni_opt_type t) { - return (nni_setopt(ipc_conn_options, name, c, val, sz, t)); + ipc_conn *c = arg; + return (nni_getopt(ipc_conn_options, nm, c, val, szp, t)); } int -nni_ipc_conn_getopt( - nni_ipc_conn *c, const char *name, void *val, size_t *szp, nni_opt_type t) +nni_win_ipc_init( + nng_stream **connp, HANDLE p, const nng_sockaddr *sa, bool dialer) { - return (nni_getopt(ipc_conn_options, name, c, val, szp, t)); + ipc_conn *c; + int rv; + + if ((c = NNI_ALLOC_STRUCT(c)) == NULL) { + return (NNG_ENOMEM); + } + c->f = INVALID_HANDLE_VALUE; + nni_mtx_init(&c->mtx); + nni_cv_init(&c->cv, &c->mtx); + nni_aio_list_init(&c->recv_aios); + nni_aio_list_init(&c->send_aios); + c->dialer = dialer; + c->sa = *sa; + c->stream.s_free = ipc_free; + c->stream.s_close = ipc_close; + c->stream.s_send = ipc_send; + c->stream.s_recv = ipc_recv; + c->stream.s_getx = ipc_getx; + c->stream.s_setx = ipc_setx; + + if (((rv = nni_win_io_init(&c->recv_io, ipc_recv_cb, c)) != 0) || + ((rv = nni_win_io_init(&c->send_io, ipc_send_cb, c)) != 0)) { + ipc_free(c); + return (rv); + } + + c->f = p; + *connp = (void *) c; + return (0); } diff --git a/src/platform/windows/win_ipcdial.c b/src/platform/windows/win_ipcdial.c index 98d848ae..be2a82b3 100644 --- a/src/platform/windows/win_ipcdial.c +++ b/src/platform/windows/win_ipcdial.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -15,19 +15,14 @@ #include <stdio.h> -int -nni_ipc_dialer_init(nni_ipc_dialer **dp) -{ - nni_ipc_dialer *d; - - if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { - return (NNG_ENOMEM); - } - d->closed = false; - nni_aio_list_init(&d->aios); - *dp = d; - return (0); -} +typedef struct ipc_dialer { + nng_stream_dialer sd; + bool closed; // dialers are locked by the worker lock + nni_list aios; + nni_list_node node; // node on worker list + char * path; + nni_sockaddr sa; +} ipc_dialer; // Windows IPC is a bit different on the client side. There is no // support for asynchronous connection, but we can fake it with a @@ -52,7 +47,7 @@ ipc_dial_thr(void *arg) nni_mtx_lock(&w->mtx); for (;;) { - nni_ipc_dialer *d; + ipc_dialer *d; if (w->exit) { break; @@ -63,21 +58,19 @@ ipc_dial_thr(void *arg) } while ((d = nni_list_first(&w->workers)) != NULL) { - nni_ipc_conn *c; - nni_aio * aio; - HANDLE f; - int rv; - char * path; + nng_stream *c; + nni_aio * aio; + HANDLE f; + int rv; if ((aio = nni_list_first(&d->aios)) == NULL) { nni_list_remove(&w->workers, d); continue; } - path = nni_aio_get_prov_extra(aio, 0); - - f = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + f = CreateFileA(d->path, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, + NULL); if (f == INVALID_HANDLE_VALUE) { switch ((rv = GetLastError())) { @@ -99,29 +92,20 @@ ipc_dial_thr(void *arg) break; } nni_list_remove(&d->aios, aio); - nni_aio_set_prov_extra(aio, 0, NULL); - nni_strfree(path); nni_aio_finish_error(aio, rv); continue; } nni_list_remove(&d->aios, aio); - nni_aio_set_prov_extra(aio, 0, NULL); if (((rv = nni_win_io_register(f)) != 0) || - ((rv = nni_win_ipc_conn_init(&c, f)) != 0)) { + ((rv = nni_win_ipc_init(&c, f, &d->sa, true)) != + 0)) { DisconnectNamedPipe(f); CloseHandle(f); nni_aio_finish_error(aio, rv); - nni_strfree(path); continue; } - c->dialer = true; - c->sa.s_ipc.sa_family = NNG_AF_IPC; - snprintf(c->sa.s_ipc.sa_path, - sizeof(c->sa.s_ipc.sa_path), "%s", - path + strlen(IPC_PIPE_PREFIX)); - nni_strfree(path); nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); } @@ -140,27 +124,23 @@ ipc_dial_thr(void *arg) static void ipc_dial_cancel(nni_aio *aio, void *arg, int rv) { - nni_ipc_dialer *d = arg; - ipc_dial_work * w = &ipc_connecter; + ipc_dialer * d = arg; + ipc_dial_work *w = &ipc_connecter; nni_mtx_lock(&w->mtx); if (nni_aio_list_active(aio)) { - char *path; if (nni_list_active(&w->waiters, d)) { nni_list_remove(&w->waiters, d); nni_cv_wake(&w->cv); } nni_aio_list_remove(aio); - path = nni_aio_get_prov_extra(aio, 0); - nni_aio_set_prov_extra(aio, 0, NULL); - nni_strfree(path); nni_aio_finish_error(aio, rv); } nni_mtx_unlock(&w->mtx); } -void -nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) +static void +ipc_dialer_dial(ipc_dialer *d, nni_aio *aio) { ipc_dial_work *w = &ipc_connecter; char * path; @@ -169,12 +149,8 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) if (nni_aio_begin(aio) != 0) { return; } - if (sa->s_family != NNG_AF_IPC) { - nni_aio_finish_error(aio, NNG_EADDRINVAL); - return; - } if ((rv = nni_asprintf( - &path, IPC_PIPE_PREFIX "%s", sa->s_ipc.sa_path)) != 0) { + &path, IPC_PIPE_PREFIX "%s", d->sa.s_ipc.sa_path)) != 0) { nni_aio_finish_error(aio, rv); return; } @@ -182,19 +158,16 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) nni_mtx_lock(&w->mtx); if ((rv = nni_aio_schedule(aio, ipc_dial_cancel, d)) != 0) { nni_mtx_unlock(&w->mtx); - nni_strfree(path); nni_aio_finish_error(aio, rv); return; } if (d->closed) { nni_mtx_unlock(&w->mtx); - nni_strfree(path); nni_aio_finish_error(aio, NNG_ECLOSED); return; } - nni_aio_set_prov_extra(aio, 0, path); nni_list_append(&d->aios, aio); if (nni_list_first(&d->aios) == aio) { nni_list_append(&w->waiters, d); @@ -203,16 +176,10 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio) nni_mtx_unlock(&w->mtx); } -void -nni_ipc_dialer_fini(nni_ipc_dialer *d) -{ - nni_ipc_dialer_close(d); - NNI_FREE_STRUCT(d); -} - -void -nni_ipc_dialer_close(nni_ipc_dialer *d) +static void +ipc_dialer_close(void *arg) { + ipc_dialer * d = arg; ipc_dial_work *w = &ipc_connecter; nni_aio * aio; @@ -228,6 +195,17 @@ nni_ipc_dialer_close(nni_ipc_dialer *d) nni_mtx_unlock(&w->mtx); } +static void +ipc_dialer_free(void *arg) +{ + ipc_dialer *d = arg; + ipc_dialer_close(d); + if (d->path) { + nni_strfree(d->path); + } + NNI_FREE_STRUCT(d); +} + static const nni_option ipc_dialer_options[] = { { .o_name = NULL, @@ -235,17 +213,50 @@ static const nni_option ipc_dialer_options[] = { }; int -nni_ipc_dialer_setopt(nni_ipc_dialer *d, const char *name, const void *buf, - size_t sz, nni_type t) +ipc_dialer_setx( + void *arg, const char *nm, const void *buf, size_t sz, nni_type t) +{ + ipc_dialer *d = arg; + return (nni_setopt(ipc_dialer_options, nm, d, buf, sz, t)); +} + +int +ipc_dialer_getx(void *arg, const char *nm, void *buf, size_t *szp, nni_type t) { - return (nni_setopt(ipc_dialer_options, name, d, buf, sz, t)); + ipc_dialer *d = arg; + return (nni_getopt(ipc_dialer_options, nm, d, buf, szp, t)); } int -nni_ipc_dialer_getopt( - nni_ipc_dialer *d, const char *name, void *buf, size_t *szp, nni_type t) +nni_ipc_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) { - return (nni_getopt(ipc_dialer_options, name, d, buf, szp, t)); + ipc_dialer *d; + int rv; + + if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || + (strlen(url->u_path) == 0)) { + return (NNG_EADDRINVAL); + } + if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { + return (NNG_ENOMEM); + } + + if ((rv = nni_asprintf(&d->path, IPC_PIPE_PREFIX "%s", url->u_path)) != + 0) { + NNI_FREE_STRUCT(d); + return (rv); + } + snprintf(d->sa.s_ipc.sa_path, NNG_MAXADDRLEN, "%s", url->u_path); + d->sa.s_ipc.sa_family = NNG_AF_IPC; + d->closed = false; + d->sd.sd_free = ipc_dialer_free; + d->sd.sd_close = ipc_dialer_close; + d->sd.sd_dial = ipc_dialer_dial; + d->sd.sd_getx = ipc_dialer_getx; + d->sd.sd_setx = ipc_dialer_setx; + nni_aio_list_init(&d->aios); + *dp = (void *) d; + return (0); } int @@ -254,8 +265,8 @@ nni_win_ipc_sysinit(void) int rv; ipc_dial_work *worker = &ipc_connecter; - NNI_LIST_INIT(&worker->workers, nni_ipc_dialer, node); - NNI_LIST_INIT(&worker->waiters, nni_ipc_dialer, node); + NNI_LIST_INIT(&worker->workers, ipc_dialer, node); + NNI_LIST_INIT(&worker->waiters, ipc_dialer, node); nni_mtx_init(&worker->mtx); nni_cv_init(&worker->cv, &worker->mtx); diff --git a/src/platform/windows/win_ipclisten.c b/src/platform/windows/win_ipclisten.c index 4b3660ec..a3922d06 100644 --- a/src/platform/windows/win_ipclisten.c +++ b/src/platform/windows/win_ipclisten.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -15,12 +15,27 @@ #include <stdio.h> +typedef struct { + nng_stream_listener sl; + char * path; + bool started; + bool closed; + HANDLE f; + SECURITY_ATTRIBUTES sec_attr; + nni_list aios; + nni_mtx mtx; + nni_cv cv; + nni_win_io io; + nni_sockaddr sa; + int rv; +} ipc_listener; + static void -ipc_accept_done(nni_ipc_listener *l, int rv) +ipc_accept_done(ipc_listener *l, int rv) { - nni_aio * aio; - HANDLE f; - nni_ipc_conn *c; + nni_aio * aio; + HANDLE f; + nng_stream *c; aio = nni_list_first(&l->aios); nni_list_remove(&l->aios, aio); @@ -50,24 +65,21 @@ ipc_accept_done(nni_ipc_listener *l, int rv) } if (((rv = nni_win_io_register(f)) != 0) || - ((rv = nni_win_ipc_conn_init(&c, l->f)) != 0)) { + ((rv = nni_win_ipc_init(&c, l->f, &l->sa, false)) != 0)) { DisconnectNamedPipe(l->f); DisconnectNamedPipe(f); CloseHandle(f); nni_aio_finish_error(aio, rv); return; } - l->f = f; - c->sa.s_ipc.sa_family = NNG_AF_IPC; - snprintf(c->sa.s_ipc.sa_path, sizeof(c->sa.s_ipc.sa_path), "%s", - l->path + strlen(IPC_PIPE_PREFIX)); - c->dialer = false; + // Install the replacement pipe. + l->f = f; nni_aio_set_output(aio, 0, c); nni_aio_finish(aio, 0, 0); } static void -ipc_accept_start(nni_ipc_listener *l) +ipc_accept_start(ipc_listener *l) { nni_aio *aio; @@ -102,7 +114,7 @@ ipc_accept_start(nni_ipc_listener *l) static void ipc_accept_cb(nni_win_io *io, int rv, size_t cnt) { - nni_ipc_listener *l = io->ptr; + ipc_listener *l = io->ptr; NNI_ARG_UNUSED(cnt); @@ -122,37 +134,12 @@ ipc_accept_cb(nni_win_io *io, int rv, size_t cnt) nni_mtx_unlock(&l->mtx); } -int -nni_ipc_listener_init(nni_ipc_listener **lp) -{ - nni_ipc_listener *l; - int rv; - - if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { - return (NNG_ENOMEM); - } - if ((rv = nni_win_io_init(&l->io, ipc_accept_cb, l)) != 0) { - NNI_FREE_STRUCT(l); - return (rv); - } - l->started = false; - l->closed = false; - l->sec_attr.nLength = sizeof(l->sec_attr); - l->sec_attr.lpSecurityDescriptor = NULL; - l->sec_attr.bInheritHandle = FALSE; - nni_aio_list_init(&l->aios); - nni_mtx_init(&l->mtx); - nni_cv_init(&l->cv, &l->mtx); - *lp = l; - return (0); -} - static int ipc_listener_set_sec_desc(void *arg, const void *buf, size_t sz, nni_type t) { - nni_ipc_listener *l = arg; - void * desc; - int rv; + ipc_listener *l = arg; + void * desc; + int rv; if ((rv = nni_copyin_ptr(&desc, buf, sz, t)) != 0) { return (rv); @@ -160,22 +147,20 @@ ipc_listener_set_sec_desc(void *arg, const void *buf, size_t sz, nni_type t) if (!IsValidSecurityDescriptor((SECURITY_DESCRIPTOR *) desc)) { return (NNG_EINVAL); } - if (l != NULL) { - nni_mtx_lock(&l->mtx); - if (l->started) { - nni_mtx_unlock(&l->mtx); - return (NNG_EBUSY); - } - l->sec_attr.lpSecurityDescriptor = desc; + nni_mtx_lock(&l->mtx); + if (l->started) { nni_mtx_unlock(&l->mtx); + return (NNG_EBUSY); } + l->sec_attr.lpSecurityDescriptor = desc; + nni_mtx_unlock(&l->mtx); return (0); } static int ipc_listener_get_addr(void *arg, void *buf, size_t *szp, nni_type t) { - nni_ipc_listener *l = arg; + ipc_listener *l = arg; return ((nni_copyout_sockaddr(&l->sa, buf, szp, t))); } @@ -194,25 +179,28 @@ static const nni_option ipc_listener_options[] = { }; int -nni_ipc_listener_setopt(nni_ipc_listener *l, const char *name, const void *buf, - size_t sz, nni_type t) +ipc_listener_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) { + ipc_listener *l = arg; return (nni_setopt(ipc_listener_options, name, l, buf, sz, t)); } int -nni_ipc_listener_getopt( - nni_ipc_listener *l, const char *name, void *buf, size_t *szp, nni_type t) +ipc_listener_getx( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { + ipc_listener *l = arg; return (nni_getopt(ipc_listener_options, name, l, buf, szp, t)); } -int -nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) +static int +ipc_listener_listen(void *arg) { - int rv; - HANDLE f; - char * path; + ipc_listener *l = arg; + int rv; + HANDLE f; + char * path; nni_mtx_lock(&l->mtx); if (l->started) { @@ -223,7 +211,7 @@ nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) nni_mtx_unlock(&l->mtx); return (NNG_ECLOSED); } - rv = nni_asprintf(&path, IPC_PIPE_PREFIX "%s", sa->s_ipc.sa_path); + rv = nni_asprintf(&path, IPC_PIPE_PREFIX "%s", l->sa.s_ipc.sa_path); if (rv != 0) { nni_mtx_unlock(&l->mtx); return (rv); @@ -255,7 +243,6 @@ nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) l->f = f; l->path = path; l->started = true; - l->sa = *sa; nni_mtx_unlock(&l->mtx); return (0); } @@ -263,7 +250,7 @@ nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa) static void ipc_accept_cancel(nni_aio *aio, void *arg, int rv) { - nni_ipc_listener *l = arg; + ipc_listener *l = arg; nni_mtx_unlock(&l->mtx); if (aio == nni_list_first(&l->aios)) { @@ -277,9 +264,10 @@ ipc_accept_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&l->mtx); } -void -nni_ipc_listener_accept(nni_ipc_listener *l, nni_aio *aio) +static void +ipc_listener_accept(void *arg, nni_aio *aio) { + ipc_listener *l = arg; if (nni_aio_begin(aio) != 0) { return; } @@ -301,9 +289,10 @@ nni_ipc_listener_accept(nni_ipc_listener *l, nni_aio *aio) nni_mtx_unlock(&l->mtx); } -void -nni_ipc_listener_close(nni_ipc_listener *l) +static void +ipc_listener_close(void *arg) { + ipc_listener *l = arg; nni_mtx_lock(&l->mtx); if (!l->closed) { l->closed = true; @@ -316,9 +305,10 @@ nni_ipc_listener_close(nni_ipc_listener *l) nni_mtx_unlock(&l->mtx); } -void -nni_ipc_listener_fini(nni_ipc_listener *l) +static void +ipc_listener_free(void *arg) { + ipc_listener *l = arg; nni_mtx_lock(&l->mtx); while (!nni_list_empty(&l->aios)) { nni_cv_wait(&l->cv); @@ -330,3 +320,72 @@ nni_ipc_listener_fini(nni_ipc_listener *l) nni_mtx_fini(&l->mtx); NNI_FREE_STRUCT(l); } + +int +nni_ipc_listener_alloc(nng_stream_listener **lp, const nng_url *url) +{ + ipc_listener *l; + int rv; + + if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || + (strlen(url->u_path) == 0)) { + return (NNG_EADDRINVAL); + } + if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { + return (NNG_ENOMEM); + } + if ((rv = nni_win_io_init(&l->io, ipc_accept_cb, l)) != 0) { + NNI_FREE_STRUCT(l); + return (rv); + } + l->started = false; + l->closed = false; + l->sec_attr.nLength = sizeof(l->sec_attr); + l->sec_attr.lpSecurityDescriptor = NULL; + l->sec_attr.bInheritHandle = FALSE; + l->sa.s_ipc.sa_family = NNG_AF_IPC; + l->sl.sl_free = ipc_listener_free; + l->sl.sl_close = ipc_listener_close; + l->sl.sl_listen = ipc_listener_listen; + l->sl.sl_accept = ipc_listener_accept; + l->sl.sl_getx = ipc_listener_getx; + l->sl.sl_setx = ipc_listener_setx; + snprintf(l->sa.s_ipc.sa_path, NNG_MAXADDRLEN, "%s", url->u_path); + nni_aio_list_init(&l->aios); + nni_mtx_init(&l->mtx); + nni_cv_init(&l->cv, &l->mtx); + *lp = (void *) l; + return (0); +} + +static int +ipc_check_sec_desc(const void *buf, size_t sz, nni_type t) +{ + void *desc; + int rv; + + if ((rv = nni_copyin_ptr(&desc, buf, sz, t)) != 0) { + return (rv); + } + if (!IsValidSecurityDescriptor((SECURITY_DESCRIPTOR *) desc)) { + return (NNG_EINVAL); + } + + return (0); +} + +static const nni_chkoption ipc_chkopts[] = { + { + .o_name = NNG_OPT_IPC_SECURITY_DESCRIPTOR, + .o_check = ipc_check_sec_desc, + }, + { + .o_name = NULL, + }, +}; + +int +nni_ipc_checkopt(const char *name, const void *data, size_t sz, nni_type t) +{ + return (nni_chkopt(ipc_chkopts, name, data, sz, t)); +} diff --git a/src/platform/windows/win_resolv.c b/src/platform/windows/win_resolv.c index d361a1e8..e01dba3b 100644 --- a/src/platform/windows/win_resolv.c +++ b/src/platform/windows/win_resolv.c @@ -134,25 +134,27 @@ resolv_task(resolv_item *item) } } - if (probe != NULL) { + if ((probe != NULL) && (item->aio != NULL)) { struct sockaddr_in * sin; struct sockaddr_in6 *sin6; - nni_sockaddr * sa = &item->sa; + nni_sockaddr sa; switch (probe->ai_addr->sa_family) { case AF_INET: - rv = 0; - sin = (void *) probe->ai_addr; - sa->s_in.sa_family = NNG_AF_INET; - sa->s_in.sa_port = item->port; - sa->s_in.sa_addr = sin->sin_addr.s_addr; + rv = 0; + sin = (void *) probe->ai_addr; + sa.s_in.sa_family = NNG_AF_INET; + sa.s_in.sa_port = item->port; + sa.s_in.sa_addr = sin->sin_addr.s_addr; + nni_aio_set_sockaddr(item->aio, &sa); break; case AF_INET6: - rv = 0; - sin6 = (void *) probe->ai_addr; - sa->s_in6.sa_family = NNG_AF_INET6; - sa->s_in6.sa_port = item->port; - memcpy(sa->s_in6.sa_addr, sin6->sin6_addr.s6_addr, 16); + rv = 0; + sin6 = (void *) probe->ai_addr; + sa.s_in6.sa_family = NNG_AF_INET6; + sa.s_in6.sa_port = item->port; + memcpy(sa.s_in6.sa_addr, sin6->sin6_addr.s6_addr, 16); + nni_aio_set_sockaddr(item->aio, &sa); break; } } @@ -294,10 +296,9 @@ resolv_worker(void *notused) // Check to make sure we were not canceled. if ((aio = item->aio) != NULL) { - nng_sockaddr *sa = nni_aio_get_input(aio, 0); nni_aio_set_prov_extra(aio, 0, NULL); item->aio = NULL; - memcpy(sa, &item->sa, sizeof(*sa)); + nni_aio_finish(aio, rv, 0); NNI_FREE_STRUCT(item); diff --git a/src/platform/windows/win_tcp.h b/src/platform/windows/win_tcp.h index 1b34aa29..b37b2353 100644 --- a/src/platform/windows/win_tcp.h +++ b/src/platform/windows/win_tcp.h @@ -1,7 +1,7 @@ // // Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -17,6 +17,7 @@ #include "core/nng_impl.h" struct nni_tcp_conn { + nng_stream ops; SOCKET s; nni_win_io recv_io; nni_win_io send_io; @@ -37,18 +38,6 @@ struct nni_tcp_conn { nni_cv cv; }; -struct nni_tcp_dialer { - LPFN_CONNECTEX connectex; // looked up name via ioctl - nni_list aios; // in flight connections - bool closed; - bool nodelay; // initial value for child conns - bool keepalive; // initial value for child conns - SOCKADDR_STORAGE src; - size_t srclen; - nni_mtx mtx; - nni_reap_item reap; -}; - struct nni_tcp_listener { SOCKET s; nni_list aios; diff --git a/src/platform/windows/win_tcpconn.c b/src/platform/windows/win_tcpconn.c index 54d22dea..c77bbc72 100644 --- a/src/platform/windows/win_tcpconn.c +++ b/src/platform/windows/win_tcpconn.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2018 Devolutions <info@devolutions.net> // @@ -110,8 +110,8 @@ tcp_recv_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&c->mtx); } -void -nni_tcp_conn_recv(nni_tcp_conn *c, nni_aio *aio) +static void +tcp_recv(nni_tcp_conn *c, nni_aio *aio) { int rv; @@ -225,10 +225,11 @@ tcp_send_cb(nni_win_io *io, int rv, size_t num) nni_aio_finish_synch(aio, rv, num); } -void -nni_tcp_conn_send(nni_tcp_conn *c, nni_aio *aio) +static void +tcp_send(void *arg, nni_aio *aio) { - int rv; + nni_tcp_conn *c = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -251,49 +252,10 @@ nni_tcp_conn_send(nni_tcp_conn *c, nni_aio *aio) nni_mtx_unlock(&c->mtx); } -int -nni_win_tcp_conn_init(nni_tcp_conn **connp, SOCKET s) -{ - nni_tcp_conn *c; - int rv; - BOOL yes; - DWORD no; - - // Don't inherit the handle (CLOEXEC really). - SetHandleInformation((HANDLE) s, HANDLE_FLAG_INHERIT, 0); - - if ((c = NNI_ALLOC_STRUCT(c)) == NULL) { - return (NNG_ENOMEM); - } - c->s = INVALID_SOCKET; - nni_mtx_init(&c->mtx); - nni_cv_init(&c->cv, &c->mtx); - nni_aio_list_init(&c->recv_aios); - nni_aio_list_init(&c->send_aios); - c->conn_aio = NULL; - - if (((rv = nni_win_io_init(&c->recv_io, tcp_recv_cb, c)) != 0) || - ((rv = nni_win_io_init(&c->send_io, tcp_send_cb, c)) != 0) || - ((rv = nni_win_io_register((HANDLE) s)) != 0)) { - nni_tcp_conn_fini(c); - return (rv); - } - - no = 0; - (void) setsockopt( - s, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &no, sizeof(no)); - yes = 1; - (void) setsockopt( - s, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(yes)); - - c->s = s; - *connp = c; - return (0); -} - -void -nni_tcp_conn_close(nni_tcp_conn *c) +static void +tcp_close(void *arg) { + nni_tcp_conn *c = arg; nni_mtx_lock(&c->mtx); if (!c->closed) { c->closed = true; @@ -310,50 +272,8 @@ nni_tcp_conn_close(nni_tcp_conn *c) nni_mtx_unlock(&c->mtx); } -int -nni_tcp_conn_peername(nni_tcp_conn *c, nni_sockaddr *sa) -{ - if (nni_win_sockaddr2nn(sa, &c->peername) < 0) { - return (NNG_EADDRINVAL); - } - return (0); -} - -int -nni_tcp_conn_sockname(nni_tcp_conn *c, nni_sockaddr *sa) -{ - if (nni_win_sockaddr2nn(sa, &c->sockname) < 0) { - return (NNG_EADDRINVAL); - } - return (0); -} - -int -nni_tcp_conn_set_nodelay(nni_tcp_conn *c, bool val) -{ - BOOL b; - b = val ? TRUE : FALSE; - if (setsockopt( - c->s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)) != 0) { - return (nni_win_error(WSAGetLastError())); - } - return (0); -} - -int -nni_tcp_conn_set_keepalive(nni_tcp_conn *c, bool val) -{ - BOOL b; - b = val ? TRUE : FALSE; - if (setsockopt( - c->s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b)) != 0) { - return (nni_win_error(WSAGetLastError())); - } - return (0); -} - static int -tcp_conn_get_peername(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_peername(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn *c = arg; nng_sockaddr sa; @@ -365,7 +285,7 @@ tcp_conn_get_peername(void *arg, void *buf, size_t *szp, nni_type t) } static int -tcp_conn_get_sockname(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_sockname(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn *c = arg; nng_sockaddr sa; @@ -377,7 +297,7 @@ tcp_conn_get_sockname(void *arg, void *buf, size_t *szp, nni_type t) } static int -tcp_conn_set_nodelay(void *arg, const void *buf, size_t sz, nni_type t) +tcp_set_nodelay(void *arg, const void *buf, size_t sz, nni_type t) { nni_tcp_conn *c = arg; bool val; @@ -395,7 +315,7 @@ tcp_conn_set_nodelay(void *arg, const void *buf, size_t sz, nni_type t) } static int -tcp_conn_set_keepalive(void *arg, const void *buf, size_t sz, nni_type t) +tcp_set_keepalive(void *arg, const void *buf, size_t sz, nni_type t) { nni_tcp_conn *c = arg; bool val; @@ -414,7 +334,7 @@ tcp_conn_set_keepalive(void *arg, const void *buf, size_t sz, nni_type t) } static int -tcp_conn_get_nodelay(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_nodelay(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn *c = arg; BOOL b = 0; @@ -428,7 +348,7 @@ tcp_conn_get_nodelay(void *arg, void *buf, size_t *szp, nni_type t) } static int -tcp_conn_get_keepalive(void *arg, void *buf, size_t *szp, nni_type t) +tcp_get_keepalive(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn *c = arg; BOOL b = 0; @@ -441,48 +361,49 @@ tcp_conn_get_keepalive(void *arg, void *buf, size_t *szp, nni_type t) return (nni_copyout_bool(b, buf, szp, t)); } -static const nni_option tcp_conn_options[] = { +static const nni_option tcp_options[] = { { .o_name = NNG_OPT_REMADDR, - .o_get = tcp_conn_get_peername, + .o_get = tcp_get_peername, }, { .o_name = NNG_OPT_LOCADDR, - .o_get = tcp_conn_get_sockname, + .o_get = tcp_get_sockname, }, { .o_name = NNG_OPT_TCP_NODELAY, - .o_get = tcp_conn_get_nodelay, - .o_set = tcp_conn_set_nodelay, + .o_get = tcp_get_nodelay, + .o_set = tcp_set_nodelay, }, { .o_name = NNG_OPT_TCP_KEEPALIVE, - .o_get = tcp_conn_get_keepalive, - .o_set = tcp_conn_set_keepalive, + .o_get = tcp_get_keepalive, + .o_set = tcp_set_keepalive, }, { .o_name = NULL, }, }; -int -nni_tcp_conn_getopt( - nni_tcp_conn *c, const char *name, void *buf, size_t *szp, nni_type t) +static int +tcp_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - return (nni_getopt(tcp_conn_options, name, c, buf, szp, t)); + nni_tcp_conn *c = arg; + return (nni_getopt(tcp_options, name, c, buf, szp, t)); } -int -nni_tcp_conn_setopt( - nni_tcp_conn *c, const char *name, const void *buf, size_t sz, nni_type t) +static int +tcp_setx(void *arg, const char *name, const void *buf, size_t sz, nni_type t) { - return (nni_setopt(tcp_conn_options, name, c, buf, sz, t)); + nni_tcp_conn *c = arg; + return (nni_setopt(tcp_options, name, c, buf, sz, t)); } -void -nni_tcp_conn_fini(nni_tcp_conn *c) +static void +tcp_free(void *arg) { - nni_tcp_conn_close(c); + nni_tcp_conn *c = arg; + tcp_close(c); nni_mtx_lock(&c->mtx); while ((!nni_list_empty(&c->recv_aios)) || @@ -502,3 +423,49 @@ nni_tcp_conn_fini(nni_tcp_conn *c) nni_mtx_fini(&c->mtx); NNI_FREE_STRUCT(c); } + +int +nni_win_tcp_conn_init(nni_tcp_conn **connp, SOCKET s) +{ + nni_tcp_conn *c; + int rv; + BOOL yes; + DWORD no; + + // Don't inherit the handle (CLOEXEC really). + SetHandleInformation((HANDLE) s, HANDLE_FLAG_INHERIT, 0); + + if ((c = NNI_ALLOC_STRUCT(c)) == NULL) { + return (NNG_ENOMEM); + } + c->s = INVALID_SOCKET; + nni_mtx_init(&c->mtx); + nni_cv_init(&c->cv, &c->mtx); + nni_aio_list_init(&c->recv_aios); + nni_aio_list_init(&c->send_aios); + c->conn_aio = NULL; + c->ops.s_close = tcp_close; + c->ops.s_free = tcp_free; + c->ops.s_send = tcp_send; + c->ops.s_recv = tcp_recv; + c->ops.s_getx = tcp_getx; + c->ops.s_setx = tcp_setx; + + if (((rv = nni_win_io_init(&c->recv_io, tcp_recv_cb, c)) != 0) || + ((rv = nni_win_io_init(&c->send_io, tcp_send_cb, c)) != 0) || + ((rv = nni_win_io_register((HANDLE) s)) != 0)) { + tcp_free(c); + return (rv); + } + + no = 0; + (void) setsockopt( + s, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &no, sizeof(no)); + yes = 1; + (void) setsockopt( + s, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(yes)); + + c->s = s; + *connp = c; + return (0); +} diff --git a/src/platform/windows/win_tcpdial.c b/src/platform/windows/win_tcpdial.c index 64b4e800..6bb3d92a 100644 --- a/src/platform/windows/win_tcpdial.c +++ b/src/platform/windows/win_tcpdial.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2018 Devolutions <info@devolutions.net> // @@ -16,6 +16,18 @@ #include <malloc.h> #include <stdio.h> +struct nni_tcp_dialer { + LPFN_CONNECTEX connectex; // looked up name via ioctl + nni_list aios; // in flight connections + bool closed; + bool nodelay; // initial value for child conns + bool keepalive; // initial value for child conns + SOCKADDR_STORAGE src; // source address + size_t srclen; + nni_mtx mtx; + nni_reap_item reap; +}; + int nni_tcp_dialer_init(nni_tcp_dialer **dp) { @@ -137,7 +149,7 @@ tcp_dial_cb(nni_win_io *io, int rv, size_t cnt) nni_mtx_unlock(&d->mtx); if (rv != 0) { - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); } else { DWORD yes = 1; @@ -156,19 +168,22 @@ tcp_dial_cb(nni_win_io *io, int rv, size_t cnt) } void -nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) +nni_tcp_dial(nni_tcp_dialer *d, nni_aio *aio) { SOCKET s; SOCKADDR_STORAGE ss; int len; nni_tcp_conn * c; int rv; + nng_sockaddr sa; + + nni_aio_get_sockaddr(aio, &sa); if (nni_aio_begin(aio) != 0) { return; } - if ((len = nni_win_nn2sockaddr(&ss, sa)) <= 0) { + if ((len = nni_win_nn2sockaddr(&ss, &sa)) <= 0) { nni_aio_finish_error(aio, NNG_EADDRINVAL); return; } @@ -179,7 +194,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) } if ((rv = nni_win_tcp_conn_init(&c, s)) != 0) { - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); return; } @@ -194,7 +209,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) nni_mtx_lock(&d->mtx); if (d->closed) { nni_mtx_unlock(&d->mtx); - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, NNG_ECLOSED); return; } @@ -212,7 +227,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) if (bind(s, (SOCKADDR *) &c->sockname, len) != 0) { rv = nni_win_error(GetLastError()); nni_mtx_unlock(&d->mtx); - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); return; } @@ -221,7 +236,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) nni_aio_set_prov_extra(aio, 0, c); if ((rv = nni_aio_schedule(aio, tcp_dial_cancel, d)) != 0) { nni_mtx_unlock(&d->mtx); - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); return; } @@ -234,8 +249,7 @@ nni_tcp_dialer_dial(nni_tcp_dialer *d, const nni_sockaddr *sa, nni_aio *aio) if ((rv = GetLastError()) != ERROR_IO_PENDING) { nni_aio_list_remove(aio); nni_mtx_unlock(&d->mtx); - - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); return; } diff --git a/src/platform/windows/win_tcplisten.c b/src/platform/windows/win_tcplisten.c index 9cf16985..e98a0c37 100644 --- a/src/platform/windows/win_tcplisten.c +++ b/src/platform/windows/win_tcplisten.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2018 Devolutions <info@devolutions.net> // @@ -98,7 +98,7 @@ tcp_accept_cb(nni_win_io *io, int rv, size_t cnt) nni_mtx_unlock(&l->mtx); if (rv != 0) { - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); return; } @@ -309,7 +309,7 @@ nni_tcp_listener_accept(nni_tcp_listener *l, nni_aio *aio) ((rv = nni_aio_schedule(aio, tcp_accept_cancel, l)) != 0)) { nni_aio_set_prov_extra(aio, 0, NULL); nni_mtx_unlock(&l->mtx); - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); return; } @@ -320,7 +320,7 @@ nni_tcp_listener_accept(nni_tcp_listener *l, nni_aio *aio) // Fast failure (synchronous.) nni_aio_list_remove(aio); nni_mtx_unlock(&l->mtx); - nni_tcp_conn_fini(c); + nng_stream_free(&c->ops); nni_aio_finish_error(aio, rv); return; } diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h index 569e8532..45738318 100644 --- a/src/supplemental/http/http_api.h +++ b/src/supplemental/http/http_api.h @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -96,9 +96,7 @@ extern void *nni_http_conn_get_ctx(nni_http_conn *); // These initialization functions create stream for HTTP transactions. // They should only be used by the server or client HTTP implementations, // and are not for use by other code. -extern int nni_http_conn_init_tcp(nni_http_conn **, nni_tcp_conn *); -extern int nni_http_conn_init_tls( - nni_http_conn **, struct nng_tls_config *, nni_tcp_conn *); +extern int nni_http_conn_init(nni_http_conn **, nng_stream *); extern void nni_http_conn_close(nni_http_conn *); extern void nni_http_conn_fini(nni_http_conn *); @@ -207,6 +205,11 @@ extern int nni_http_server_set_tls(nni_http_server *, struct nng_tls_config *); extern int nni_http_server_get_tls( nni_http_server *, struct nng_tls_config **); +extern int nni_http_server_setx( + nni_http_server *, const char *, const void *, size_t, nni_type); +extern int nni_http_server_getx( + nni_http_server *, const char *, void *, size_t *, nni_type); + // nni_http_server_start starts listening on the supplied port. extern int nni_http_server_start(nni_http_server *); @@ -350,6 +353,11 @@ extern int nni_http_client_set_tls(nni_http_client *, struct nng_tls_config *); extern int nni_http_client_get_tls( nni_http_client *, struct nng_tls_config **); +extern int nni_http_client_setx( + nni_http_client *, const char *, const void *, size_t, nni_type); +extern int nni_http_client_getx( + nni_http_client *, const char *, void *, size_t *, nni_type); + extern void nni_http_client_connect(nni_http_client *, nni_aio *); // nni_http_transact_conn is used to perform a round-trip exchange (i.e. a diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c index 798cbe14..c35dcaa8 100644 --- a/src/supplemental/http/http_client.c +++ b/src/supplemental/http/http_client.c @@ -1,6 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -14,25 +15,20 @@ #include <string.h> #include "core/nng_impl.h" -#include "nng/supplemental/tls/tls.h" #include "supplemental/tls/tls_api.h" +#include <nng/supplemental/tls/tls.h> + #include "http_api.h" static nni_mtx http_txn_lk; struct nng_http_client { - nni_list aios; - nni_mtx mtx; - bool closed; - bool resolving; - nng_tls_config *tls; - nni_aio * aio; - nng_sockaddr sa; - nni_tcp_dialer *dialer; - char * host; - char * port; - nni_url * url; + nni_list aios; + nni_mtx mtx; + bool closed; + nni_aio * aio; + nng_stream_dialer *dialer; }; static void @@ -43,9 +39,7 @@ http_dial_start(nni_http_client *c) if ((aio = nni_list_first(&c->aios)) == NULL) { return; } - c->resolving = true; - nni_aio_set_input(c->aio, 0, &c->sa); - nni_tcp_resolv(c->host, c->port, NNG_AF_UNSPEC, 0, c->aio); + nng_stream_dialer_dial(c->dialer, c->aio); } static void @@ -54,7 +48,7 @@ http_dial_cb(void *arg) nni_http_client *c = arg; nni_aio * aio; int rv; - nni_tcp_conn * tcp; + nng_stream * stream; nni_http_conn * conn; nni_mtx_lock(&c->mtx); @@ -63,9 +57,9 @@ http_dial_cb(void *arg) if ((aio = nni_list_first(&c->aios)) == NULL) { // User abandoned request, and no residuals left. nni_mtx_unlock(&c->mtx); - if ((rv == 0) && !c->resolving) { - tcp = nni_aio_get_output(c->aio, 0); - nni_tcp_conn_fini(tcp); + if (rv == 0) { + stream = nni_aio_get_output(c->aio, 0); + nng_stream_free(stream); } return; } @@ -78,28 +72,16 @@ http_dial_cb(void *arg) return; } - if (c->resolving) { - // This was a DNS lookup -- advance to normal TCP connect. - c->resolving = false; - nni_tcp_dialer_dial(c->dialer, &c->sa, c->aio); - nni_mtx_unlock(&c->mtx); - return; - } - nni_aio_list_remove(aio); - tcp = nni_aio_get_output(c->aio, 0); - NNI_ASSERT(tcp != NULL); + stream = nni_aio_get_output(c->aio, 0); + NNI_ASSERT(stream != NULL); - if (c->tls != NULL) { - rv = nni_http_conn_init_tls(&conn, c->tls, tcp); - } else { - rv = nni_http_conn_init_tcp(&conn, tcp); - } + rv = nni_http_conn_init(&conn, stream); http_dial_start(c); nni_mtx_unlock(&c->mtx); if (rv != 0) { - // the conn_init function will have already discard tcp. + // the conn_init function will have already discard stream. nni_aio_finish_error(aio, rv); return; } @@ -112,16 +94,8 @@ void nni_http_client_fini(nni_http_client *c) { nni_aio_fini(c->aio); - nni_tcp_dialer_fini(c->dialer); + nng_stream_dialer_free(c->dialer); nni_mtx_fini(&c->mtx); -#ifdef NNG_SUPP_TLS - if (c->tls != NULL) { - nni_tls_config_fini(c->tls); - } -#endif - nni_strfree(c->host); - nni_strfree(c->port); - NNI_FREE_STRUCT(c); } @@ -130,59 +104,37 @@ nni_http_client_init(nni_http_client **cp, const nni_url *url) { int rv; nni_http_client *c; + nng_url myurl; + + // Rewrite URLs to either TLS or TCP. + memcpy(&myurl, url, sizeof(myurl)); + if ((strcmp(url->u_scheme, "http") == 0) || + (strcmp(url->u_scheme, "ws") == 0)) { + myurl.u_scheme = "tcp"; + } else if ((strcmp(url->u_scheme, "https") == 0) || + (strcmp(url->u_scheme, "wss") == 0)) { + myurl.u_scheme = "tls+tcp"; + } else { + return (NNG_EADDRINVAL); + } if (strlen(url->u_hostname) == 0) { // We require a valid hostname. return (NNG_EADDRINVAL); } - if ((strcmp(url->u_scheme, "http") != 0) && -#ifdef NNG_SUPP_TLS - (strcmp(url->u_scheme, "https") != 0) && - (strcmp(url->u_scheme, "wss") != 0) && -#endif - (strcmp(url->u_scheme, "ws") != 0)) { - return (NNG_EADDRINVAL); - } if ((c = NNI_ALLOC_STRUCT(c)) == NULL) { return (NNG_ENOMEM); } nni_mtx_init(&c->mtx); nni_aio_list_init(&c->aios); - if (((c->host = nni_strdup(url->u_hostname)) == NULL) || - ((strlen(url->u_port) != 0) && - ((c->port = nni_strdup(url->u_port)) == NULL))) { - nni_http_client_fini(c); - return (NNG_ENOMEM); - } - -#ifdef NNG_SUPP_TLS - if ((strcmp(url->u_scheme, "https") == 0) || - (strcmp(url->u_scheme, "wss") == 0)) { - rv = nni_tls_config_init(&c->tls, NNG_TLS_MODE_CLIENT); - if (rv != 0) { - nni_http_client_fini(c); - return (rv); - } - // Take the server name right from the client URL. We only - // consider the name, as the port is never part of the - // certificate. - rv = nng_tls_config_server_name(c->tls, url->u_hostname); - if (rv != 0) { - nni_http_client_fini(c); - return (rv); - } - // Note that the application has to supply the location of - // certificates. We could probably use a default based - // on environment or common locations used by OpenSSL, but - // as there is no way to *unload* the cert file, lets not - // do that. (We might want to consider a mode to reset.) + if ((rv = nng_stream_dialer_alloc_url(&c->dialer, &myurl)) != 0) { + nni_http_client_fini(c); + return (rv); } -#endif - if (((rv = nni_tcp_dialer_init(&c->dialer)) != 0) || - ((rv = nni_aio_init(&c->aio, http_dial_cb, c)) != 0)) { + if ((rv = nni_aio_init(&c->aio, http_dial_cb, c)) != 0) { nni_http_client_fini(c); return (rv); } @@ -192,46 +144,37 @@ nni_http_client_init(nni_http_client **cp, const nni_url *url) } int -nni_http_client_set_tls(nni_http_client *c, struct nng_tls_config *tls) +nni_http_client_set_tls(nni_http_client *c, nng_tls_config *tls) { -#ifdef NNG_SUPP_TLS - struct nng_tls_config *old; - nni_mtx_lock(&c->mtx); - old = c->tls; - c->tls = tls; - if (tls != NULL) { - nni_tls_config_hold(tls); - } - nni_mtx_unlock(&c->mtx); - if (old != NULL) { - nni_tls_config_fini(old); - } - return (0); -#else - NNI_ARG_UNUSED(c); - NNI_ARG_UNUSED(tls); - return (NNG_EINVAL); -#endif + int rv; + rv = nni_stream_dialer_setx(c->dialer, NNG_OPT_TLS_CONFIG, &tls, + sizeof(tls), NNI_TYPE_POINTER); + return (rv); } int -nni_http_client_get_tls(nni_http_client *c, struct nng_tls_config **tlsp) +nni_http_client_get_tls(nni_http_client *c, nng_tls_config **tlsp) { -#ifdef NNG_SUPP_TLS - nni_mtx_lock(&c->mtx); - if (c->tls == NULL) { - nni_mtx_unlock(&c->mtx); - return (NNG_EINVAL); - } - nni_tls_config_hold(c->tls); - *tlsp = c->tls; - nni_mtx_unlock(&c->mtx); - return (0); -#else - NNI_ARG_UNUSED(c); - NNI_ARG_UNUSED(tlsp); - return (NNG_ENOTSUP); -#endif + size_t sz = sizeof(*tlsp); + int rv; + rv = nni_stream_dialer_getx( + c->dialer, NNG_OPT_TLS_CONFIG, tlsp, &sz, NNI_TYPE_POINTER); + return (rv); +} + +int +nni_http_client_setx(nni_http_client *c, const char *name, const void *buf, + size_t sz, nni_type t) +{ + // We have no local options, but we just pass them straight through. + return (nni_stream_dialer_setx(c->dialer, name, buf, sz, t)); +} + +int +nni_http_client_getx( + nni_http_client *c, const char *name, void *buf, size_t *szp, nni_type t) +{ + return (nni_stream_dialer_getx(c->dialer, name, buf, szp, t)); } static void diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c index 7c6159cd..1fc2c34e 100644 --- a/src/supplemental/http/http_conn.c +++ b/src/supplemental/http/http_conn.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -40,25 +40,6 @@ enum write_flavor { HTTP_WR_RES, }; -typedef void (*http_read_fn)(void *, nni_aio *); -typedef void (*http_write_fn)(void *, nni_aio *); -typedef void (*http_close_fn)(void *); -typedef void (*http_fini_fn)(void *); -typedef int (*http_addr_fn)(void *, nni_sockaddr *); -typedef int (*http_getopt_fn)( - void *, const char *, void *, size_t *, nni_type); -typedef int (*http_setopt_fn)( - void *, const char *, const void *, size_t, nni_type); - -typedef struct { - http_read_fn h_read; - http_write_fn h_write; - http_getopt_fn h_getopt; - http_setopt_fn h_setopt; - http_close_fn h_close; - http_fini_fn h_fini; -} http_tran; - #define SET_RD_FLAVOR(aio, f) \ nni_aio_set_prov_extra(aio, 0, ((void *) (intptr_t)(f))) #define GET_RD_FLAVOR(aio) (int) ((intptr_t) nni_aio_get_prov_extra(aio, 0)) @@ -67,17 +48,11 @@ typedef struct { #define GET_WR_FLAVOR(aio) (int) ((intptr_t) nni_aio_get_prov_extra(aio, 0)) struct nng_http_conn { - void * sock; - http_read_fn rd; - http_write_fn wr; - http_setopt_fn setopt; - http_getopt_fn getopt; - http_close_fn close; - http_fini_fn fini; - void * ctx; - bool closed; - nni_list rdq; // high level http read requests - nni_list wrq; // high level http write requests + nng_stream *sock; + void * ctx; + bool closed; + nni_list rdq; // high level http read requests + nni_list wrq; // high level http write requests nni_aio *rd_uaio; // user aio for read nni_aio *wr_uaio; // user aio for write @@ -138,7 +113,7 @@ http_close(nni_http_conn *conn) } if (conn->sock != NULL) { - conn->close(conn->sock); + nng_stream_close(conn->sock); } } @@ -204,7 +179,7 @@ http_rd_buf(nni_http_conn *conn, nni_aio *aio) // to get *any* data for a partial RAW read.) nni_aio_set_data(conn->rd_aio, 1, NULL); nni_aio_set_iov(conn->rd_aio, niov, iov); - conn->rd(conn->sock, conn->rd_aio); + nng_stream_recv(conn->sock, conn->rd_aio); return (NNG_EAGAIN); case HTTP_RD_REQ: @@ -220,7 +195,7 @@ http_rd_buf(nni_http_conn *conn, nni_aio *aio) iov1.iov_len = conn->rd_bufsz - conn->rd_put; nni_aio_set_iov(conn->rd_aio, 1, &iov1); nni_aio_set_data(conn->rd_aio, 1, aio); - conn->rd(conn->sock, conn->rd_aio); + nng_stream_recv(conn->sock, conn->rd_aio); } return (rv); @@ -237,7 +212,7 @@ http_rd_buf(nni_http_conn *conn, nni_aio *aio) iov1.iov_len = conn->rd_bufsz - conn->rd_put; nni_aio_set_iov(conn->rd_aio, 1, &iov1); nni_aio_set_data(conn->rd_aio, 1, aio); - conn->rd(conn->sock, conn->rd_aio); + nng_stream_recv(conn->sock, conn->rd_aio); } return (rv); @@ -254,7 +229,7 @@ http_rd_buf(nni_http_conn *conn, nni_aio *aio) iov1.iov_len = conn->rd_bufsz - conn->rd_put; nni_aio_set_iov(conn->rd_aio, 1, &iov1); nni_aio_set_data(conn->rd_aio, 1, aio); - conn->rd(conn->sock, conn->rd_aio); + nng_stream_recv(conn->sock, conn->rd_aio); } return (rv); } @@ -428,7 +403,7 @@ http_wr_start(nni_http_conn *conn) nni_aio_get_iov(aio, &niov, &iov); nni_aio_set_iov(conn->wr_aio, niov, iov); - conn->wr(conn->sock, conn->wr_aio); + nng_stream_send(conn->sock, conn->wr_aio); } static void @@ -475,7 +450,7 @@ http_wr_cb(void *arg) if (nni_aio_iov_count(aio) > 0) { // We have more to transmit - start another and leave // (we will get called again when it is done). - conn->wr(conn->sock, aio); + nng_stream_send(conn->sock, aio); nni_mtx_unlock(&conn->mtx); return; } @@ -680,7 +655,7 @@ nni_http_conn_getopt( if (conn->closed) { rv = NNG_ECLOSED; } else { - rv = conn->getopt(conn->sock, name, buf, szp, t); + rv = nni_stream_getx(conn->sock, name, buf, szp, t); } nni_mtx_unlock(&conn->mtx); return (rv); @@ -695,7 +670,7 @@ nni_http_conn_setopt(nni_http_conn *conn, const char *name, const void *buf, if (conn->closed) { rv = NNG_ECLOSED; } else { - rv = conn->setopt(conn->sock, name, buf, sz, t); + rv = nni_stream_setx(conn->sock, name, buf, sz, t); } nni_mtx_unlock(&conn->mtx); return (rv); @@ -709,8 +684,8 @@ nni_http_conn_fini(nni_http_conn *conn) nni_mtx_lock(&conn->mtx); http_close(conn); - if ((conn->sock != NULL) && (conn->fini != NULL)) { - conn->fini(conn->sock); + if (conn->sock != NULL) { + nng_stream_free(conn->sock); conn->sock = NULL; } nni_mtx_unlock(&conn->mtx); @@ -723,7 +698,7 @@ nni_http_conn_fini(nni_http_conn *conn) } static int -http_init(nni_http_conn **connp, http_tran *tran, void *data) +http_init(nni_http_conn **connp, nng_stream *data) { nni_http_conn *conn; int rv; @@ -747,73 +722,19 @@ http_init(nni_http_conn **connp, http_tran *tran, void *data) return (rv); } - conn->sock = data; - conn->rd = tran->h_read; - conn->wr = tran->h_write; - conn->close = tran->h_close; - conn->fini = tran->h_fini; - conn->getopt = tran->h_getopt; - conn->setopt = tran->h_setopt; + conn->sock = data; *connp = conn; return (0); } -static http_tran http_tcp_ops = { - .h_read = (http_read_fn) nni_tcp_conn_recv, - .h_write = (http_write_fn) nni_tcp_conn_send, - .h_close = (http_close_fn) nni_tcp_conn_close, - .h_fini = (http_fini_fn) nni_tcp_conn_fini, - .h_getopt = (http_getopt_fn) nni_tcp_conn_getopt, - .h_setopt = (http_setopt_fn) nni_tcp_conn_setopt, -}; - int -nni_http_conn_init_tcp(nni_http_conn **connp, nni_tcp_conn *tcp) +nni_http_conn_init(nni_http_conn **connp, nng_stream *stream) { int rv; - if ((rv = http_init(connp, &http_tcp_ops, tcp)) != 0) { - nni_tcp_conn_fini(tcp); - } - return (rv); -} - -#ifdef NNG_SUPP_TLS -static http_tran http_tls_ops = { - .h_read = (http_read_fn) nni_tls_recv, - .h_write = (http_write_fn) nni_tls_send, - .h_close = (http_close_fn) nni_tls_close, - .h_fini = (http_fini_fn) nni_tls_fini, - .h_getopt = (http_getopt_fn) nni_tls_getopt, - .h_setopt = (http_setopt_fn) nni_tls_setopt, -}; - -int -nni_http_conn_init_tls( - nni_http_conn **connp, struct nng_tls_config *cfg, nni_tcp_conn *tcp) -{ - nni_tls *tls; - int rv; - - if ((rv = nni_tls_init(&tls, cfg, tcp)) != 0) { - nni_tcp_conn_fini(tcp); - return (rv); - } - - if ((rv = http_init(connp, &http_tls_ops, tls)) != 0) { - nni_tls_fini(tls); + if ((rv = http_init(connp, stream)) != 0) { + nng_stream_free(stream); } return (rv); } -#else -int -nni_http_conn_init_tls( - nni_http_conn **connp, struct nng_tls_config *cfg, nni_tcp_conn *tcp) -{ - NNI_ARG_UNUSED(connp); - NNI_ARG_UNUSED(cfg); - nni_tcp_conn_fini(tcp); - return (NNG_ENOTSUP); -} -#endif // NNG_SUPP_TLS diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c index a6343d87..939273b7 100644 --- a/src/supplemental/http/http_server.c +++ b/src/supplemental/http/http_server.c @@ -1,7 +1,8 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2018 QXSoftware <lh563566994@126.com> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -68,22 +69,21 @@ typedef struct http_error { } http_error; struct nng_http_server { - nng_sockaddr addr; - nni_list_node node; - int refcnt; - int starts; - nni_list handlers; - nni_list conns; - nni_mtx mtx; - bool closed; - nng_tls_config * tls; - nni_aio * accaio; - nni_tcp_listener *listener; - char * port; - char * hostname; - nni_list errors; - nni_mtx errors_mtx; - nni_reap_item reap; + nng_sockaddr addr; + nni_list_node node; + int refcnt; + int starts; + nni_list handlers; + nni_list conns; + nni_mtx mtx; + bool closed; + nni_aio * accaio; + nng_stream_listener *listener; + char * port; + char * hostname; + nni_list errors; + nni_mtx errors_mtx; + nni_reap_item reap; }; int @@ -720,13 +720,13 @@ http_sconn_cbdone(void *arg) } static int -http_sconn_init(http_sconn **scp, nni_http_server *s, nni_tcp_conn *tcp) +http_sconn_init(http_sconn **scp, nng_stream *stream) { http_sconn *sc; int rv; if ((sc = NNI_ALLOC_STRUCT(sc)) == NULL) { - nni_tcp_conn_fini(tcp); + nng_stream_free(stream); return (NNG_ENOMEM); } @@ -741,11 +741,7 @@ http_sconn_init(http_sconn **scp, nni_http_server *s, nni_tcp_conn *tcp) return (rv); } - if (s->tls != NULL) { - rv = nni_http_conn_init_tls(&sc->conn, s->tls, tcp); - } else { - rv = nni_http_conn_init_tcp(&sc->conn, tcp); - } + rv = nni_http_conn_init(&sc->conn, stream); if (rv != 0) { http_sconn_close(sc); return (rv); @@ -760,7 +756,7 @@ http_server_acccb(void *arg) { nni_http_server *s = arg; nni_aio * aio = s->accaio; - nni_tcp_conn * tcp; + nng_stream * stream; http_sconn * sc; int rv; @@ -768,22 +764,22 @@ http_server_acccb(void *arg) if ((rv = nni_aio_result(aio)) != 0) { if (!s->closed) { // try again? - nni_tcp_listener_accept(s->listener, s->accaio); + nng_stream_listener_accept(s->listener, s->accaio); } nni_mtx_unlock(&s->mtx); return; } - tcp = nni_aio_get_output(aio, 0); + stream = nni_aio_get_output(aio, 0); if (s->closed) { // If we're closing, then reject this one. - nni_tcp_conn_fini(tcp); + nng_stream_free(stream); nni_mtx_unlock(&s->mtx); return; } - if (http_sconn_init(&sc, s, tcp) != 0) { - // The TCP structure is already cleaned up. + if (http_sconn_init(&sc, stream) != 0) { + // The stream structure is already cleaned up. // Start another accept attempt. - nni_tcp_listener_accept(s->listener, s->accaio); + nng_stream_listener_accept(s->listener, s->accaio); nni_mtx_unlock(&s->mtx); return; } @@ -792,7 +788,7 @@ http_server_acccb(void *arg) sc->handler = NULL; nni_http_read_req(sc->conn, sc->req, sc->rxaio); - nni_tcp_listener_accept(s->listener, s->accaio); + nng_stream_listener_accept(s->listener, s->accaio); nni_mtx_unlock(&s->mtx); } @@ -812,20 +808,13 @@ http_server_fini(nni_http_server *s) nni_mtx_unlock(&s->mtx); return; } - if (s->listener != NULL) { - nni_tcp_listener_fini(s->listener); - } + nng_stream_listener_free(s->listener); while ((h = nni_list_first(&s->handlers)) != NULL) { nni_list_remove(&s->handlers, h); h->refcnt--; nni_http_handler_fini(h); } nni_mtx_unlock(&s->mtx); -#ifdef NNG_SUPP_TLS - if (s->tls != NULL) { - nni_tls_config_fini(s->tls); - } -#endif nni_mtx_lock(&s->errors_mtx); while ((epage = nni_list_first(&s->errors)) != NULL) { nni_list_remove(&s->errors, epage); @@ -847,16 +836,20 @@ http_server_init(nni_http_server **serverp, const nni_url *url) { nni_http_server *s; int rv; - nni_aio * aio; - - if ((strcmp(url->u_scheme, "http") != 0) && -#ifdef NNG_SUPP_TLS - (strcmp(url->u_scheme, "https") != 0) && - (strcmp(url->u_scheme, "wss") != 0) && -#endif - (strcmp(url->u_scheme, "ws") != 0)) { + nng_url myurl; + + // Rewrite URLs to either TLS or TCP. + memcpy(&myurl, url, sizeof(myurl)); + if ((strcmp(url->u_scheme, "http") == 0) || + (strcmp(url->u_scheme, "ws") == 0)) { + myurl.u_scheme = "tcp"; + } else if ((strcmp(url->u_scheme, "https") == 0) || + (strcmp(url->u_scheme, "wss") == 0)) { + myurl.u_scheme = "tls+tcp"; + } else { return (NNG_EADDRINVAL); } + if ((s = NNI_ALLOC_STRUCT(s)) == NULL) { return (NNG_ENOMEM); } @@ -884,34 +877,11 @@ http_server_init(nni_http_server **serverp, const nni_url *url) return (NNG_ENOMEM); } -#ifdef NNG_SUPP_TLS - if ((strcmp(url->u_scheme, "https") == 0) || - (strcmp(url->u_scheme, "wss") == 0)) { - rv = nni_tls_config_init(&s->tls, NNG_TLS_MODE_SERVER); - if (rv != 0) { - http_server_fini(s); - return (rv); - } - } -#endif - - // Do the DNS lookup *now*. This means that this is - // synchronous, but it should be fast, since it should either - // resolve as a number, or resolve locally, without having to - // hit up DNS. - if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { - http_server_fini(s); - return (rv); - } - nni_aio_set_input(aio, 0, &s->addr); - nni_tcp_resolv(s->hostname, s->port, NNG_AF_UNSPEC, true, aio); - nni_aio_wait(aio); - rv = nni_aio_result(aio); - nni_aio_fini(aio); - if (rv != 0) { + if ((rv = nng_stream_listener_alloc_url(&s->listener, &myurl)) != 0) { http_server_fini(s); return (rv); } + s->refcnt = 1; *serverp = s; return (0); @@ -950,15 +920,10 @@ static int http_server_start(nni_http_server *s) { int rv; - if ((rv = nni_tcp_listener_init(&s->listener)) != 0) { - return (rv); - } - if ((rv = nni_tcp_listener_listen(s->listener, &s->addr)) != 0) { - nni_tcp_listener_fini(s->listener); - s->listener = NULL; + if ((rv = nng_stream_listener_listen(s->listener)) != 0) { return (rv); } - nni_tcp_listener_accept(s->listener, s->accaio); + nng_stream_listener_accept(s->listener, s->accaio); return (0); } @@ -992,7 +957,7 @@ http_server_stop(nni_http_server *s) // Close the TCP endpoint that is listening. if (s->listener) { - nni_tcp_listener_close(s->listener); + nng_stream_listener_close(s->listener); } // Stopping the server is a hard stop -- it aborts any work @@ -1764,50 +1729,37 @@ nni_http_handler_init_static(nni_http_handler **hpp, const char *uri, } int -nni_http_server_set_tls(nni_http_server *s, nng_tls_config *tcfg) +nni_http_server_set_tls(nni_http_server *s, nng_tls_config *tls) { -#ifdef NNG_SUPP_TLS - nng_tls_config *old; - nni_mtx_lock(&s->mtx); - if (s->starts) { - nni_mtx_unlock(&s->mtx); - return (NNG_EBUSY); - } - old = s->tls; - s->tls = tcfg; - if (tcfg) { - nni_tls_config_hold(tcfg); - } - nni_mtx_unlock(&s->mtx); - if (old) { - nni_tls_config_fini(old); - } - return (0); -#else - NNI_ARG_UNUSED(s); - NNI_ARG_UNUSED(tcfg); - return (NNG_ENOTSUP); -#endif + int rv; + rv = nni_stream_listener_setx(s->listener, NNG_OPT_TLS_CONFIG, &tls, + sizeof(tls), NNI_TYPE_POINTER); + return (rv); } int -nni_http_server_get_tls(nni_http_server *s, nng_tls_config **tp) +nni_http_server_get_tls(nni_http_server *s, nng_tls_config **tlsp) { -#ifdef NNG_SUPP_TLS - nni_mtx_lock(&s->mtx); - if (s->tls == NULL) { - nni_mtx_unlock(&s->mtx); - return (NNG_EINVAL); - } - nni_tls_config_hold(s->tls); - *tp = s->tls; - nni_mtx_unlock(&s->mtx); - return (0); -#else - NNI_ARG_UNUSED(s); - NNI_ARG_UNUSED(tp); - return (NNG_ENOTSUP); -#endif + size_t sz = sizeof(*tlsp); + int rv; + rv = nni_stream_listener_getx( + s->listener, NNG_OPT_TLS_CONFIG, tlsp, &sz, NNI_TYPE_POINTER); + return (rv); +} + +int +nni_http_server_setx(nni_http_server *s, const char *name, const void *buf, + size_t sz, nni_type t) +{ + // We have no local options, but we just pass them straight through. + return (nni_stream_listener_setx(s->listener, name, buf, sz, t)); +} + +int +nni_http_server_getx( + nni_http_server *s, const char *name, void *buf, size_t *szp, nni_type t) +{ + return (nni_stream_listener_getx(s->listener, name, buf, szp, t)); } void diff --git a/src/supplemental/ipc/CMakeLists.txt b/src/supplemental/ipc/CMakeLists.txt deleted file mode 100644 index 3bc0e4de..00000000 --- a/src/supplemental/ipc/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright 2018 Capitar IT Group BV <info@capitar.com> -# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> -# Copyright 2018 Devolutions <info@devolutions.net> -# -# 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. -# - -set(_SRCS supplemental/ipc/ipc.c - ${PROJECT_SOURCE_DIR}/include/nng/supplemental/ipc/ipc.h -) - -set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) diff --git a/src/supplemental/ipc/ipc.c b/src/supplemental/ipc/ipc.c deleted file mode 100644 index cf78dfbd..00000000 --- a/src/supplemental/ipc/ipc.c +++ /dev/null @@ -1,138 +0,0 @@ -// -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> -// Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> -// -// 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 <stddef.h> -#include <stdint.h> - -#include <nng/nng.h> -#include <nng/supplemental/ipc/ipc.h> - -#include "core/nng_impl.h" - -// This is our "public" IPC API. This allows applications to access -// basic IPC functions, using our AIO framework. Most applications will -// not need this. - -// We treat nng_ipc as nni_ipc_conn, nng_ipc_dialer as nni_ipc_dialer, -// and nng_ipc_listener as nni_ipc_listener. We cast through void to -// provide isolation of the names in a way that makes the compiler happy. -// It turns out we can pretty much just wrap the platform API for IPC that -// we have already created. - -void -nng_ipc_close(nng_ipc *ipc) -{ - nni_ipc_conn_close((void *) ipc); -} - -void -nng_ipc_free(nng_ipc *ipc) -{ - nni_ipc_conn_fini((void *) ipc); -} - -void -nng_ipc_send(nng_ipc *ipc, nng_aio *aio) -{ - nni_ipc_conn_send((void *) ipc, aio); -} - -void -nng_ipc_recv(nng_ipc *ipc, nng_aio *aio) -{ - nni_ipc_conn_recv((void *) ipc, aio); -} - -int -nng_ipc_setopt(nng_ipc *ipc, const char *name, const void *val, size_t sz) -{ - return ( - nni_ipc_conn_setopt((void *) ipc, name, val, sz, NNI_TYPE_OPAQUE)); -} - -int -nng_ipc_getopt(nng_ipc *ipc, const char *name, void *val, size_t *szp) -{ - return (nni_ipc_conn_getopt( - (void *) ipc, name, val, szp, NNI_TYPE_OPAQUE)); -} - -int -nng_ipc_dialer_alloc(nng_ipc_dialer **dp) -{ - nni_ipc_dialer *d; - int rv; - - if ((rv = nni_init()) != 0) { - return (rv); - } - if ((rv = nni_ipc_dialer_init(&d)) == 0) { - *dp = (void *) d; - } - return (rv); -} - -void -nng_ipc_dialer_close(nng_ipc_dialer *d) -{ - nni_ipc_dialer_close((void *) d); -} - -void -nng_ipc_dialer_free(nng_ipc_dialer *d) -{ - nni_ipc_dialer_fini((void *) d); -} - -void -nng_ipc_dialer_dial(nng_ipc_dialer *d, const nng_sockaddr *sa, nng_aio *aio) -{ - nni_ipc_dialer_dial((void *) d, sa, aio); -} - -int -nng_ipc_listener_alloc(nng_ipc_listener **lp) -{ - nni_ipc_listener *l; - int rv; - - if ((rv = nni_init()) != 0) { - return (rv); - } - if ((rv = nni_ipc_listener_init(&l)) == 0) { - *lp = (void *) l; - } - return (rv); -} - -void -nng_ipc_listener_close(nng_ipc_listener *l) -{ - nni_ipc_listener_close((void *) l); -} - -void -nng_ipc_listener_free(nng_ipc_listener *l) -{ - nni_ipc_listener_fini((void *) l); -} - -int -nng_ipc_listener_listen(nng_ipc_listener *l, const nng_sockaddr *sa) -{ - return (nni_ipc_listener_listen((void *) l, sa)); -} - -void -nng_ipc_listener_accept(nng_ipc_listener *l, nng_aio *aio) -{ - nni_ipc_listener_accept((void *) l, aio); -} diff --git a/src/supplemental/tcp/CMakeLists.txt b/src/supplemental/tcp/CMakeLists.txt index ef82b098..09f917f8 100644 --- a/src/supplemental/tcp/CMakeLists.txt +++ b/src/supplemental/tcp/CMakeLists.txt @@ -1,6 +1,6 @@ # # Copyright 2018 Capitar IT Group BV <info@capitar.com> -# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +# Copyright 2019 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 @@ -8,8 +8,6 @@ # found online at https://opensource.org/licenses/MIT. # -set(_SRCS supplemental/tcp/tcp.c - ${PROJECT_SOURCE_DIR}/include/nng/supplemental/tcp/tcp.h -) +set(_SRCS supplemental/tcp/tcp.c) set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) diff --git a/src/supplemental/tcp/tcp.c b/src/supplemental/tcp/tcp.c index b8410b16..3ec396b8 100644 --- a/src/supplemental/tcp/tcp.c +++ b/src/supplemental/tcp/tcp.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -11,144 +11,428 @@ #include <stddef.h> #include <stdint.h> +#include <string.h> #include <nng/nng.h> -#include <nng/supplemental/tcp/tcp.h> #include "core/nng_impl.h" +#include "core/tcp.h" -// This is our "public" TCP API. This allows applications to access -// basic TCP functions, using our AIO framework. Most applications will -// not need this. +typedef struct { + nng_stream_dialer ops; + char * host; + char * port; + int af; // address family + bool closed; + nng_sockaddr sa; + nni_tcp_dialer * d; // platform dialer implementation + nni_aio * resaio; // resolver aio + nni_aio * conaio; // platform connection aio + nni_list resaios; + nni_list conaios; + nni_mtx mtx; +} tcp_dialer; -// We treat nng_tcp as nni_tcp_conn, nng_tcp_dialer as nni_tcp_dialer, -// and nng_tcp_listener as nni_tcp_listener. We cast through void to -// provide isolation of the names in a way that makes the compiler happy. -// It turns out we can pretty much just wrap the platform API for TCP that -// we have already created. - -void -nng_tcp_close(nng_tcp *tcp) +static void +tcp_dial_cancel(nni_aio *aio, void *arg, int rv) { - nni_tcp_conn_close((void *) tcp); -} + tcp_dialer *d = arg; -void -nng_tcp_free(nng_tcp *tcp) -{ - nni_tcp_conn_fini((void *) tcp); + nni_mtx_lock(&d->mtx); + if (nni_aio_list_active(aio)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + + if (nni_list_empty(&d->conaios)) { + nni_aio_abort(d->conaio, NNG_ECANCELED); + } + if (nni_list_empty(&d->resaios)) { + nni_aio_abort(d->resaio, NNG_ECANCELED); + } + } + nni_mtx_unlock(&d->mtx); } -void -nng_tcp_send(nng_tcp *tcp, nng_aio *aio) +static void +tcp_dial_res_cb(void *arg) { - nni_tcp_conn_send((void *) tcp, aio); + tcp_dialer *d = arg; + nni_aio * aio; + int rv; + + nni_mtx_lock(&d->mtx); + if (d->closed || ((aio = nni_list_first(&d->resaios)) == NULL)) { + // ignore this. + while ((aio = nni_list_first(&d->resaios)) != NULL) { + nni_list_remove(&d->resaios, aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + } + nni_mtx_unlock(&d->mtx); + return; + } + + nni_list_remove(&d->resaios, aio); + + if ((rv = nni_aio_result(d->resaio)) != 0) { + nni_aio_finish_error(aio, rv); + } else { + nng_sockaddr sa; + nni_aio_get_sockaddr(d->resaio, &sa); + nni_aio_set_sockaddr(aio, &sa); + nni_list_append(&d->conaios, aio); + if (nni_list_first(&d->conaios) == aio) { + nni_aio_set_sockaddr(d->conaio, &sa); + nni_tcp_dial(d->d, d->conaio); + } + } + + if (!nni_list_empty(&d->resaios)) { + nni_tcp_resolv(d->host, d->port, d->af, 0, d->resaio); + } + nni_mtx_unlock(&d->mtx); } -void -nng_tcp_recv(nng_tcp *tcp, nng_aio *aio) +static void +tcp_dial_con_cb(void *arg) { - nni_tcp_conn_recv((void *) tcp, aio); + tcp_dialer *d = arg; + nng_aio * aio; + int rv; + + nni_mtx_lock(&d->mtx); + rv = nni_aio_result(d->conaio); + if ((d->closed) || ((aio = nni_list_first(&d->conaios)) == NULL)) { + if (rv == 0) { + // Make sure we discard the underlying connection. + nng_stream_free(nni_aio_get_output(d->conaio, 0)); + nni_aio_set_output(d->conaio, 0, NULL); + } + nni_mtx_unlock(&d->mtx); + return; + } + nni_list_remove(&d->conaios, aio); + if (rv != 0) { + nni_aio_finish_error(aio, rv); + } else { + nni_aio_set_output(aio, 0, nni_aio_get_output(d->conaio, 0)); + nni_aio_finish(aio, 0, 0); + } + + if ((aio = nni_list_first(&d->conaios)) != NULL) { + nng_sockaddr sa; + nni_aio_get_sockaddr(aio, &sa); + nni_aio_set_sockaddr(d->conaio, &sa); + nni_tcp_dial(d->d, d->conaio); + } + nni_mtx_unlock(&d->mtx); } -int -nng_tcp_getopt(nng_tcp *tcp, const char *name, void *buf, size_t *szp) +static void +tcp_dialer_close(void *arg) { - return (nni_tcp_conn_getopt( - (void *) tcp, name, buf, szp, NNI_TYPE_OPAQUE)); + tcp_dialer *d = arg; + nni_aio * aio; + nni_mtx_lock(&d->mtx); + d->closed = true; + while ((aio = nni_list_first(&d->resaios)) != NULL) { + nni_list_remove(&d->resaios, aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + } + while ((aio = nni_list_first(&d->conaios)) != NULL) { + nni_list_remove(&d->conaios, aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + } + nni_tcp_dialer_close(d->d); + nni_mtx_unlock(&d->mtx); } -int -nng_tcp_setopt(nng_tcp *tcp, const char *name, const void *buf, size_t sz) +static void +tcp_dialer_free(void *arg) { - return ( - nni_tcp_conn_setopt((void *) tcp, name, buf, sz, NNI_TYPE_OPAQUE)); + tcp_dialer *d = arg; + + if (d == NULL) { + return; + } + + if (d->d != NULL) { + nni_tcp_dialer_close(d->d); + nni_tcp_dialer_fini(d->d); + } + nni_strfree(d->host); + nni_strfree(d->port); + nni_aio_fini(d->resaio); + nni_aio_fini(d->conaio); + nni_mtx_fini(&d->mtx); + NNI_FREE_STRUCT(d); } -int -nng_tcp_dialer_alloc(nng_tcp_dialer **dp) +static void +tcp_dialer_dial(void *arg, nng_aio *aio) { - nni_tcp_dialer *d; - int rv; - - if ((rv = nni_init()) != 0) { - return (rv); + tcp_dialer *d = arg; + int rv; + if (nni_aio_begin(aio) != 0) { + return; + } + nni_mtx_lock(&d->mtx); + if (d->closed) { + nni_mtx_unlock(&d->mtx); + nni_aio_finish_error(aio, NNG_ECLOSED); + return; + } + if ((rv = nni_aio_schedule(aio, tcp_dial_cancel, d)) != 0) { + nni_mtx_unlock(&d->mtx); + nni_aio_finish_error(aio, rv); + return; } - if ((rv = nni_tcp_dialer_init(&d)) == 0) { - *dp = (void *) d; + if (d->host != NULL) { + nni_list_append(&d->resaios, aio); + if (nni_list_first(&d->resaios) == aio) { + nni_tcp_resolv(d->host, d->port, d->af, 0, d->resaio); + } + } else { + nni_list_append(&d->conaios, aio); + if (nni_list_first(&d->conaios) == aio) { + nni_aio_set_sockaddr(d->conaio, &d->sa); + nni_tcp_dial(d->d, d->conaio); + } } - return (rv); + nni_mtx_unlock(&d->mtx); } -void -nng_tcp_dialer_close(nng_tcp_dialer *d) +static int +tcp_dialer_getx( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - nni_tcp_dialer_close((void *) d); + tcp_dialer *d = arg; + return (nni_tcp_dialer_getopt(d->d, name, buf, szp, t)); } -void -nng_tcp_dialer_free(nng_tcp_dialer *d) +static int +tcp_dialer_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) { - nni_tcp_dialer_fini((void *) d); + tcp_dialer *d = arg; + return (nni_tcp_dialer_setopt(d->d, name, buf, sz, t)); } -void -nng_tcp_dialer_dial(nng_tcp_dialer *d, const nng_sockaddr *sa, nng_aio *aio) +static int +tcp_dialer_alloc(tcp_dialer **dp) { - nni_tcp_dialer_dial((void *) d, sa, aio); + int rv; + tcp_dialer *d; + + if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { + return (NNG_ENOMEM); + } + + nni_mtx_init(&d->mtx); + nni_aio_list_init(&d->resaios); + nni_aio_list_init(&d->conaios); + + if (((rv = nni_aio_init(&d->resaio, tcp_dial_res_cb, d)) != 0) || + ((rv = nni_aio_init(&d->conaio, tcp_dial_con_cb, d)) != 0) || + ((rv = nni_tcp_dialer_init(&d->d)) != 0)) { + tcp_dialer_free(d); + return (rv); + } + + d->ops.sd_close = tcp_dialer_close; + d->ops.sd_free = tcp_dialer_free; + d->ops.sd_dial = tcp_dialer_dial; + d->ops.sd_getx = tcp_dialer_getx; + d->ops.sd_setx = tcp_dialer_setx; + + *dp = d; + return (0); } int -nng_tcp_listener_alloc(nng_tcp_listener **lp) +nni_tcp_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) { - nni_tcp_listener *l; - int rv; + tcp_dialer *d; + int rv; + const char *p; if ((rv = nni_init()) != 0) { return (rv); } - if ((rv = nni_tcp_listener_init(&l)) == 0) { - *lp = (void *) l; + + if ((rv = tcp_dialer_alloc(&d)) != 0) { + return (rv); } - return (rv); + + if (((p = url->u_port) == NULL) || (strlen(p) == 0)) { + p = nni_url_default_port(url->u_scheme); + } + + if ((strlen(p) == 0) || (strlen(url->u_hostname) == 0)) { + // Dialer needs both a destination hostname and port. + tcp_dialer_free(d); + return (NNG_EADDRINVAL); + } + + if (strchr(url->u_scheme, '4') != NULL) { + d->af = NNG_AF_INET; + } else if (strchr(url->u_scheme, '6') != NULL) { + d->af = NNG_AF_INET6; + } else { + d->af = NNG_AF_UNSPEC; + } + + if (((d->host = nng_strdup(url->u_hostname)) == NULL) || + ((d->port = nng_strdup(p)) == NULL)) { + tcp_dialer_free(d); + return (NNG_ENOMEM); + } + + *dp = (void *) d; + return (0); } -void -nng_tcp_listener_close(nng_tcp_listener *l) +typedef struct { + nng_stream_listener ops; + nni_tcp_listener * l; + nng_sockaddr sa; +} tcp_listener; + +static void +tcp_listener_close(void *arg) { - nni_tcp_listener_close((void *) l); + tcp_listener *l = arg; + nni_tcp_listener_close(l->l); } -void -nng_tcp_listener_free(nng_tcp_listener *l) +static void +tcp_listener_free(void *arg) { - nni_tcp_listener_fini((void *) l); + tcp_listener *l = arg; + nni_tcp_listener_fini(l->l); + NNI_FREE_STRUCT(l); } -int -nng_tcp_listener_listen(nng_tcp_listener *l, const nng_sockaddr *sa) +static int +tcp_listener_listen(void *arg) +{ + tcp_listener *l = arg; + return (nni_tcp_listener_listen(l->l, &l->sa)); +} + +static void +tcp_listener_accept(void *arg, nng_aio *aio) { - return (nni_tcp_listener_listen((void *) l, sa)); + tcp_listener *l = arg; + nni_tcp_listener_accept(l->l, aio); } -void -nng_tcp_listener_accept(nng_tcp_listener *l, nng_aio *aio) +static int +tcp_listener_getx( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - nni_tcp_listener_accept((void *) l, aio); + tcp_listener *l = arg; + return (nni_tcp_listener_getopt(l->l, name, buf, szp, t)); +} + +static int +tcp_listener_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) +{ + tcp_listener *l = arg; + return (nni_tcp_listener_setopt(l->l, name, buf, sz, t)); +} + +static int +tcp_listener_alloc_addr(nng_stream_listener **lp, const nng_sockaddr *sa) +{ + tcp_listener *l; + int rv; + + if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { + return (NNG_ENOMEM); + } + if ((rv = nni_tcp_listener_init(&l->l)) != 0) { + NNI_FREE_STRUCT(l); + return (rv); + } + l->sa = *sa; + + l->ops.sl_free = tcp_listener_free; + l->ops.sl_close = tcp_listener_close; + l->ops.sl_listen = tcp_listener_listen; + l->ops.sl_accept = tcp_listener_accept; + l->ops.sl_getx = tcp_listener_getx; + l->ops.sl_setx = tcp_listener_setx; + + *lp = (void *) l; + return (0); } int -nng_tcp_listener_getopt( - nng_tcp_listener *l, const char *name, void *buf, size_t *szp) +nni_tcp_listener_alloc(nng_stream_listener **lp, const nng_url *url) { - return (nni_tcp_listener_getopt( - (void *) l, name, buf, szp, NNI_TYPE_OPAQUE)); + nni_aio * aio; + int af; + int rv; + nng_sockaddr sa; + const char * h; + + if ((rv = nni_init()) != 0) { + return (rv); + } + if (strchr(url->u_scheme, '4') != NULL) { + af = NNG_AF_INET; + } else if (strchr(url->u_scheme, '6') != NULL) { + af = NNG_AF_INET6; + } else { + af = NNG_AF_UNSPEC; + } + + if ((rv = nng_aio_alloc(&aio, NULL, NULL)) != 0) { + return (rv); + } + + h = url->u_hostname; + + // Wildcard special case, which means bind to INADDR_ANY. + if ((h != NULL) && ((strcmp(h, "*") == 0) || (strlen(h) == 0))) { + h = NULL; + } + nni_tcp_resolv(h, url->u_port, af, 1, aio); + nni_aio_wait(aio); + + if ((rv = nni_aio_result(aio)) != 0) { + nni_aio_fini(aio); + return (rv); + } + nni_aio_get_sockaddr(aio, &sa); + nni_aio_fini(aio); + + return (tcp_listener_alloc_addr(lp, &sa)); } +static int +tcp_check_bool(const void *val, size_t sz, nni_type t) +{ + return (nni_copyin_bool(NULL, val, sz, t)); +} + +static const nni_chkoption tcp_chkopts[] = { + { + .o_name = NNG_OPT_TCP_KEEPALIVE, + .o_check = tcp_check_bool, + }, + { + .o_name = NNG_OPT_TCP_NODELAY, + .o_check = tcp_check_bool, + }, + { + .o_name = NULL, + }, +}; + int -nng_tcp_listener_setopt( - nng_tcp_listener *l, const char *name, const void *buf, size_t sz) +nni_tcp_checkopt(const char *name, const void *data, size_t sz, nni_type t) { - return (nni_tcp_listener_setopt( - (void *) l, name, buf, sz, NNI_TYPE_OPAQUE)); -}
\ No newline at end of file + return (nni_chkopt(tcp_chkopts, name, data, sz, t)); +} diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c index 9f1e8f83..d3816747 100644 --- a/src/supplemental/tls/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/tls.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2019 Devolutions <info@devolutions.net> // @@ -28,10 +28,9 @@ #include "mbedtls/ssl.h" #include "core/nng_impl.h" +#include "core/tcp.h" #include "supplemental/tls/tls_api.h" -#include <nng/supplemental/tls/tls.h> - // Implementation note. This implementation buffers data between the TLS // encryption layer (mbedTLS) and the underlying TCP socket. As a result, // there may be some additional latency caused by buffer draining and @@ -59,8 +58,9 @@ typedef struct nni_tls_certkey { nni_list_node node; } nni_tls_certkey; -struct nni_tls { - nni_tcp_conn * tcp; +typedef struct { + nni_tls_common com; + nng_stream * tcp; mbedtls_ssl_context ctx; nng_tls_config * cfg; // kept so we can release it nni_mtx lk; @@ -81,7 +81,7 @@ struct nni_tls { nni_list sends; // upper side sends nni_list recvs; // upper recv aios nni_aio * handshake; // handshake aio (upper) -}; +} tls; struct nng_tls_config { mbedtls_ssl_config cfg_ctx; @@ -100,18 +100,18 @@ struct nng_tls_config { nni_list certkeys; }; -static void nni_tls_send_cb(void *); -static void nni_tls_recv_cb(void *); +static void tls_send_cb(void *); +static void tls_recv_cb(void *); -static void nni_tls_do_send(nni_tls *); -static void nni_tls_do_recv(nni_tls *); -static void nni_tls_do_handshake(nni_tls *); +static void tls_do_send(tls *); +static void tls_do_recv(tls *); +static void tls_do_handshake(tls *); -static int nni_tls_net_send(void *, const unsigned char *, size_t); -static int nni_tls_net_recv(void *, unsigned char *, size_t); +static int tls_net_send(void *, const unsigned char *, size_t); +static int tls_net_recv(void *, unsigned char *, size_t); static void -nni_tls_dbg(void *ctx, int level, const char *file, int line, const char *s) +tls_dbg(void *ctx, int level, const char *file, int line, const char *s) { char buf[128]; NNI_ARG_UNUSED(ctx); @@ -121,7 +121,7 @@ nni_tls_dbg(void *ctx, int level, const char *file, int line, const char *s) } static int -nni_tls_get_entropy(void *arg, unsigned char *buf, size_t len) +tls_get_entropy(void *arg, unsigned char *buf, size_t len) { NNI_ARG_UNUSED(arg); while (len) { @@ -137,7 +137,7 @@ nni_tls_get_entropy(void *arg, unsigned char *buf, size_t len) } static int -nni_tls_random(void *arg, unsigned char *buf, size_t sz) +tls_random(void *arg, unsigned char *buf, size_t sz) { #ifdef NNG_TLS_USE_CTR_DRBG int rv; @@ -149,7 +149,7 @@ nni_tls_random(void *arg, unsigned char *buf, size_t sz) nni_mtx_unlock(&cfg->rng_lk); return (rv); #else - return (nni_tls_get_entropy(arg, buf, sz)); + return (tls_get_entropy(arg, buf, sz)); #endif } @@ -230,15 +230,15 @@ nni_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode) #ifdef NNG_TLS_USE_CTR_DRBG mbedtls_ctr_drbg_init(&cfg->rng_ctx); rv = mbedtls_ctr_drbg_seed( - &cfg->rng_ctx, nni_tls_get_entropy, NULL, NULL, 0); + &cfg->rng_ctx, tls_get_entropy, NULL, NULL, 0); if (rv != 0) { nni_tls_config_fini(cfg); return (rv); } #endif - mbedtls_ssl_conf_rng(&cfg->cfg_ctx, nni_tls_random, cfg); + mbedtls_ssl_conf_rng(&cfg->cfg_ctx, tls_random, cfg); - mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, nni_tls_dbg, cfg); + mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, tls_dbg, cfg); *cpp = cfg; return (0); @@ -252,41 +252,12 @@ nni_tls_config_hold(nng_tls_config *cfg) nni_mtx_unlock(&cfg->lk); } -void -nni_tls_fini(nni_tls *tp) -{ - // Shut it all down first. - if (tp != NULL) { - if (tp->tcp) { - nni_tcp_conn_close(tp->tcp); - } - nni_aio_stop(tp->tcp_send); - nni_aio_stop(tp->tcp_recv); - - // And finalize / free everything. - if (tp->tcp) { - nni_tcp_conn_fini(tp->tcp); - } - nni_aio_fini(tp->tcp_send); - nni_aio_fini(tp->tcp_recv); - mbedtls_ssl_free(&tp->ctx); - nni_mtx_fini(&tp->lk); - nni_free(tp->recvbuf, NNG_TLS_MAX_RECV_SIZE); - nni_free(tp->sendbuf, NNG_TLS_MAX_RECV_SIZE); - if (tp->cfg != NULL) { - // release the hold we got on it - nni_tls_config_fini(tp->cfg); - } - NNI_FREE_STRUCT(tp); - } -} - -// nni_tls_mkerr converts an mbed error to an NNG error. In all cases +// tls_mkerr converts an mbed error to an NNG error. In all cases // we just encode with NNG_ETRANERR. static struct { int tls; int nng; -} nni_tls_errs[] = { +} tls_errs[] = { { MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE, NNG_EPEERAUTH }, { MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED, NNG_EPEERAUTH }, { MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED, NNG_EPEERAUTH }, @@ -300,60 +271,74 @@ static struct { }; static int -nni_tls_mkerr(int err) +tls_mkerr(int err) { - for (int i = 0; nni_tls_errs[i].tls != 0; i++) { - if (nni_tls_errs[i].tls == err) { - return (nni_tls_errs[i].nng); + for (int i = 0; tls_errs[i].tls != 0; i++) { + if (tls_errs[i].tls == err) { + return (tls_errs[i].nng); } } return (NNG_ECRYPTO); } -int -nni_tls_init(nni_tls **tpp, nng_tls_config *cfg, nni_tcp_conn *tcp) +// The common code should call this only after it has released +// it's upper layer stuff. +static void +tls_free(void *arg) { - nni_tls *tp; - int rv; - bool on = true; + tls *tls = arg; + + // Shut it all down first. + if (tls != NULL) { + if (tls->tcp != NULL) { + nng_stream_close(tls->tcp); + } + nni_aio_stop(tls->tcp_send); + nni_aio_stop(tls->tcp_recv); - // During the handshake, disable Nagle to shorten the - // negotiation. Once things are set up the caller can - // re-enable Nagle if so desired. - (void) nni_tcp_conn_setopt( - tcp, NNG_OPT_TCP_NODELAY, &on, sizeof(on), NNI_TYPE_BOOL); + nni_aio_fini(tls->com.aio); + nng_tls_config_free(tls->com.cfg); - if ((tp = NNI_ALLOC_STRUCT(tp)) == NULL) { - return (NNG_ENOMEM); + // And finalize / free everything. + nng_stream_free(tls->tcp); + nni_aio_fini(tls->tcp_send); + nni_aio_fini(tls->tcp_recv); + mbedtls_ssl_free(&tls->ctx); + if (tls->recvbuf != NULL) { + nni_free(tls->recvbuf, NNG_TLS_MAX_RECV_SIZE); + } + if (tls->sendbuf != NULL) { + nni_free(tls->sendbuf, NNG_TLS_MAX_RECV_SIZE); + } + nni_mtx_fini(&tls->lk); + NNI_FREE_STRUCT(tls); } +} + +int +nni_tls_start(nng_stream *arg, nng_stream *tcp) +{ + tls * tp = (void *) arg; + nng_tls_config *cfg = tp->com.cfg; + int rv; + if ((tp->recvbuf = nni_zalloc(NNG_TLS_MAX_RECV_SIZE)) == NULL) { - NNI_FREE_STRUCT(tp); return (NNG_ENOMEM); } if ((tp->sendbuf = nni_zalloc(NNG_TLS_MAX_SEND_SIZE)) == NULL) { - nni_free(tp->sendbuf, NNG_TLS_MAX_RECV_SIZE); - NNI_FREE_STRUCT(tp); return (NNG_ENOMEM); } nni_mtx_lock(&cfg->lk); // No more changes allowed to config. cfg->active = true; - cfg->refcnt++; - tp->cfg = cfg; nni_mtx_unlock(&cfg->lk); - nni_aio_list_init(&tp->sends); - nni_aio_list_init(&tp->recvs); - nni_mtx_init(&tp->lk); mbedtls_ssl_init(&tp->ctx); - mbedtls_ssl_set_bio( - &tp->ctx, tp, nni_tls_net_send, nni_tls_net_recv, NULL); + mbedtls_ssl_set_bio(&tp->ctx, tp, tls_net_send, tls_net_recv, NULL); if ((rv = mbedtls_ssl_setup(&tp->ctx, &cfg->cfg_ctx)) != 0) { - rv = nni_tls_mkerr(rv); - nni_tls_fini(tp); - return (rv); + return (tls_mkerr(rv)); } if (cfg->server_name) { @@ -362,25 +347,23 @@ nni_tls_init(nni_tls **tpp, nng_tls_config *cfg, nni_tcp_conn *tcp) tp->tcp = tcp; - if (((rv = nni_aio_init(&tp->tcp_send, nni_tls_send_cb, tp)) != 0) || - ((rv = nni_aio_init(&tp->tcp_recv, nni_tls_recv_cb, tp)) != 0)) { - nni_tls_fini(tp); + if (((rv = nni_aio_init(&tp->tcp_send, tls_send_cb, tp)) != 0) || + ((rv = nni_aio_init(&tp->tcp_recv, tls_recv_cb, tp)) != 0)) { return (rv); } nni_mtx_lock(&tp->lk); // Kick off a handshake operation. - nni_tls_do_handshake(tp); + tls_do_handshake(tp); nni_mtx_unlock(&tp->lk); - *tpp = tp; return (0); } static void -nni_tls_cancel(nni_aio *aio, void *arg, int rv) +tls_cancel(nni_aio *aio, void *arg, int rv) { - nni_tls *tp = arg; + tls *tp = arg; nni_mtx_lock(&tp->lk); if (nni_aio_list_active(aio)) { nni_aio_list_remove(aio); @@ -389,33 +372,16 @@ nni_tls_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&tp->lk); } +// tls_send_cb is called when the underlying TCP send completes. static void -nni_tls_fail(nni_tls *tp, int rv) -{ - nni_aio *aio; - tp->tls_closed = true; - nni_tcp_conn_close(tp->tcp); - tp->tcp_closed = true; - while ((aio = nni_list_first(&tp->recvs)) != NULL) { - nni_list_remove(&tp->recvs, aio); - nni_aio_finish_error(aio, rv); - } - while ((aio = nni_list_first(&tp->sends)) != NULL) { - nni_list_remove(&tp->recvs, aio); - nni_aio_finish_error(aio, rv); - } -} - -// nni_tls_send_cb is called when the underlying TCP send completes. -static void -nni_tls_send_cb(void *ctx) +tls_send_cb(void *ctx) { - nni_tls *tp = ctx; + tls * tp = ctx; nni_aio *aio = tp->tcp_send; nni_mtx_lock(&tp->lk); if (nni_aio_result(aio) != 0) { - nni_tcp_conn_close(tp->tcp); + nng_stream_close(tp->tcp); tp->tcp_closed = true; } else { size_t n = nni_aio_count(aio); @@ -428,25 +394,24 @@ nni_tls_send_cb(void *ctx) iov.iov_len = tp->sendlen; nni_aio_set_iov(aio, 1, &iov); nni_aio_set_timeout(aio, NNG_DURATION_INFINITE); - nni_tcp_conn_send(tp->tcp, aio); + nng_stream_send(tp->tcp, aio); nni_mtx_unlock(&tp->lk); return; } tp->sendoff = 0; tp->sending = false; } - if (!tp->hsdone) { - nni_tls_do_handshake(tp); - } + + tls_do_handshake(tp); if (tp->hsdone) { - nni_tls_do_send(tp); - nni_tls_do_recv(tp); + tls_do_send(tp); + tls_do_recv(tp); } nni_mtx_unlock(&tp->lk); } static void -nni_tls_recv_start(nni_tls *tp) +tls_recv_start(tls *tp) { nni_aio *aio; nni_iov iov; @@ -467,13 +432,13 @@ nni_tls_recv_start(nni_tls *tp) iov.iov_len = NNG_TLS_MAX_RECV_SIZE; nni_aio_set_iov(aio, 1, &iov); nni_aio_set_timeout(tp->tcp_recv, NNG_DURATION_INFINITE); - nni_tcp_conn_recv(tp->tcp, aio); + nng_stream_recv(tp->tcp, aio); } static void -nni_tls_recv_cb(void *ctx) +tls_recv_cb(void *ctx) { - nni_tls *tp = ctx; + tls * tp = ctx; nni_aio *aio = tp->tcp_recv; nni_mtx_lock(&tp->lk); @@ -481,7 +446,7 @@ nni_tls_recv_cb(void *ctx) if (nni_aio_result(aio) != 0) { // Close the underlying TCP channel, but permit data we // already received to continue to be received. - nni_tcp_conn_close(tp->tcp); + nng_stream_close(tp->tcp); tp->tcp_closed = true; } else { NNI_ASSERT(tp->recvlen == 0); @@ -492,12 +457,10 @@ nni_tls_recv_cb(void *ctx) // If we were closed (above), the upper layer will detect and // react properly. Otherwise the upper layer will consume // data. - if (!tp->hsdone) { - nni_tls_do_handshake(tp); - } + tls_do_handshake(tp); if (tp->hsdone) { - nni_tls_do_recv(tp); - nni_tls_do_send(tp); + tls_do_recv(tp); + tls_do_send(tp); } nni_mtx_unlock(&tp->lk); @@ -511,10 +474,10 @@ nni_tls_recv_cb(void *ctx) // ridiculous over queueing. This is always called with the pipe // lock held, and never blocks. static int -nni_tls_net_send(void *ctx, const unsigned char *buf, size_t len) +tls_net_send(void *ctx, const unsigned char *buf, size_t len) { - nni_tls *tp = ctx; - nni_iov iov; + tls * tp = ctx; + nni_iov iov; if (len > NNG_TLS_MAX_SEND_SIZE) { len = NNG_TLS_MAX_SEND_SIZE; @@ -538,22 +501,24 @@ nni_tls_net_send(void *ctx, const unsigned char *buf, size_t len) iov.iov_len = len; nni_aio_set_iov(tp->tcp_send, 1, &iov); nni_aio_set_timeout(tp->tcp_send, NNG_DURATION_INFINITE); - nni_tcp_conn_send(tp->tcp, tp->tcp_send); + nng_stream_send(tp->tcp, tp->tcp_send); return (len); } static int -nni_tls_net_recv(void *ctx, unsigned char *buf, size_t len) +tls_net_recv(void *ctx, unsigned char *buf, size_t len) { - nni_tls *tp = ctx; + tls *tp = ctx; // We should already be running with the pipe lock held, // as we are running in that context. - if (tp->tcp_closed && tp->recvlen == 0) { - return (MBEDTLS_ERR_NET_RECV_FAILED); - } if (tp->recvlen == 0) { + if (tp->tcp_closed) { + // The underlying TCP transport has closed, and we + // have no more data in our receive buffer. + return (MBEDTLS_ERR_NET_RECV_FAILED); + } len = MBEDTLS_ERR_SSL_WANT_READ; } else { if (len > tp->recvlen) { @@ -564,16 +529,15 @@ nni_tls_net_recv(void *ctx, unsigned char *buf, size_t len) tp->recvlen -= len; } - nni_tls_recv_start(tp); + tls_recv_start(tp); return ((int) len); } -// nni_tls_send is the exported send function. It has a similar -// calling convention as the platform TCP pipe. -void -nni_tls_send(nni_tls *tp, nni_aio *aio) +static void +tls_send(void *arg, nni_aio *aio) { - int rv; + int rv; + tls *tp = arg; if (nni_aio_begin(aio) != 0) { return; @@ -584,20 +548,21 @@ nni_tls_send(nni_tls *tp, nni_aio *aio) nni_aio_finish_error(aio, NNG_ECLOSED); return; } - if ((rv = nni_aio_schedule(aio, nni_tls_cancel, tp)) != 0) { + if ((rv = nni_aio_schedule(aio, tls_cancel, tp)) != 0) { nni_mtx_unlock(&tp->lk); nni_aio_finish_error(aio, rv); return; } nni_list_append(&tp->sends, aio); - nni_tls_do_send(tp); + tls_do_send(tp); nni_mtx_unlock(&tp->lk); } -void -nni_tls_recv(nni_tls *tp, nni_aio *aio) +static void +tls_recv(void *arg, nni_aio *aio) { - int rv; + int rv; + tls *tp = arg; if (nni_aio_begin(aio) != 0) { return; @@ -608,22 +573,22 @@ nni_tls_recv(nni_tls *tp, nni_aio *aio) nni_aio_finish_error(aio, NNG_ECLOSED); return; } - if ((rv = nni_aio_schedule(aio, nni_tls_cancel, tp)) != 0) { + if ((rv = nni_aio_schedule(aio, tls_cancel, tp)) != 0) { nni_mtx_unlock(&tp->lk); nni_aio_finish_error(aio, rv); return; } nni_list_append(&tp->recvs, aio); - nni_tls_do_recv(tp); + tls_do_recv(tp); nni_mtx_unlock(&tp->lk); } static int tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t) { - nni_tls *tp = arg; - bool v = (mbedtls_ssl_get_verify_result(&tp->ctx) == 0); + tls *tp = arg; + bool v = (mbedtls_ssl_get_verify_result(&tp->ctx) == 0); return (nni_copyout_bool(v, buf, szp, t)); } @@ -638,26 +603,28 @@ static const nni_option tls_options[] = { }, }; -int -nni_tls_setopt( - nni_tls *tp, const char *name, const void *buf, size_t sz, nni_type t) +static int +tls_setx(void *arg, const char *name, const void *buf, size_t sz, nni_type t) { - int rv; + tls * tp = arg; + int rv; + nng_stream *tcp; - if ((rv = nni_tcp_conn_setopt(tp->tcp, name, buf, sz, t)) != - NNG_ENOTSUP) { + tcp = (tp != NULL) ? tp->tcp : NULL; + + if ((rv = nni_stream_setx(tcp, name, buf, sz, t)) != NNG_ENOTSUP) { return (rv); } return (nni_setopt(tls_options, name, tp, buf, sz, t)); } -int -nni_tls_getopt( - nni_tls *tp, const char *name, void *buf, size_t *szp, nni_type t) +static int +tls_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - int rv; + tls *tp = arg; + int rv; - if ((rv = nni_tcp_conn_getopt(tp->tcp, name, buf, szp, t)) != + if ((rv = nni_stream_getx(tp->tcp, name, buf, szp, t)) != NNG_ENOTSUP) { return (rv); } @@ -665,11 +632,12 @@ nni_tls_getopt( } static void -nni_tls_do_handshake(nni_tls *tp) +tls_do_handshake(tls *tp) { - int rv; + int rv; + nni_aio *aio; - if (tp->tls_closed) { + if (tp->hsdone || tp->tls_closed) { return; } rv = mbedtls_ssl_handshake(&tp->ctx); @@ -686,15 +654,24 @@ nni_tls_do_handshake(nni_tls *tp) default: // some other error occurred, this causes us to tear it down - nni_tls_fail(tp, nni_tls_mkerr(rv)); + nng_stream_close(tp->tcp); + tp->tls_closed = true; + tp->tcp_closed = true; + rv = tls_mkerr(rv); + + while (((aio = nni_list_first(&tp->recvs)) != NULL) || + ((aio = nni_list_first(&tp->sends)) != NULL)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + } } } -// nni_tls_do_send is called to try to send more data if we have not +// tls_do_send is called to try to send more data if we have not // yet completed the I/O. It also completes any transactions that // *have* completed. It must be called with the lock held. static void -nni_tls_do_send(nni_tls *tp) +tls_do_send(tls *tp) { nni_aio *aio; @@ -732,7 +709,7 @@ nni_tls_do_send(nni_tls *tp) // Want better diagnostics. nni_aio_list_remove(aio); if (n < 0) { - nni_aio_finish_error(aio, nni_tls_mkerr(n)); + nni_aio_finish_error(aio, tls_mkerr(n)); } else { nni_aio_finish(aio, 0, n); } @@ -740,7 +717,7 @@ nni_tls_do_send(nni_tls *tp) } static void -nni_tls_do_recv(nni_tls *tp) +tls_do_recv(tls *tp) { nni_aio *aio; @@ -777,16 +754,17 @@ nni_tls_do_recv(nni_tls *tp) nni_aio_list_remove(aio); if (n < 0) { - nni_aio_finish_error(aio, nni_tls_mkerr(n)); + nni_aio_finish_error(aio, tls_mkerr(n)); } else { nni_aio_finish(aio, 0, n); } } } -void -nni_tls_close(nni_tls *tp) +static void +tls_close(void *arg) { + tls * tp = arg; nni_aio *aio; nni_aio_close(tp->tcp_send); @@ -795,12 +773,8 @@ nni_tls_close(nni_tls *tp) nni_mtx_lock(&tp->lk); tp->tls_closed = true; - while ((aio = nni_list_first(&tp->sends)) != NULL) { - nni_aio_list_remove(aio); - nni_aio_finish_error(aio, NNG_ECLOSED); - } - - while ((aio = nni_list_first(&tp->recvs)) != NULL) { + while (((aio = nni_list_first(&tp->sends)) != NULL) || + ((aio = nni_list_first(&tp->recvs)) != NULL)) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, NNG_ECLOSED); } @@ -813,11 +787,36 @@ nni_tls_close(nni_tls *tp) // connection at this point. (void) mbedtls_ssl_close_notify(&tp->ctx); } else { - nni_tcp_conn_close(tp->tcp); + nng_stream_close(tp->tcp); } nni_mtx_unlock(&tp->lk); } +// This allocates a TLS structure, that can be used by the caller. +// The reason we have this API is so that the base structure can be +// embedded in the parent structure. +int +nni_tls_alloc(nng_stream **tlsp) +{ + tls *tp; + + if ((tp = NNI_ALLOC_STRUCT(tp)) == NULL) { + return (NNG_ENOMEM); + } + nni_aio_list_init(&tp->sends); + nni_aio_list_init(&tp->recvs); + nni_mtx_init(&tp->lk); + tp->com.ops.s_close = tls_close; + tp->com.ops.s_free = tls_free; + tp->com.ops.s_send = tls_send; + tp->com.ops.s_recv = tls_recv; + tp->com.ops.s_getx = tls_getx; + tp->com.ops.s_setx = tls_setx; + + *tlsp = (void *) tp; + return (0); +} + int nng_tls_config_server_name(nng_tls_config *cfg, const char *name) { @@ -882,14 +881,14 @@ nng_tls_config_ca_chain( pem = (const uint8_t *) certs; len = strlen(certs) + 1; if ((rv = mbedtls_x509_crt_parse(&cfg->ca_certs, pem, len)) != 0) { - rv = nni_tls_mkerr(rv); + rv = tls_mkerr(rv); goto err; } if (crl != NULL) { pem = (const uint8_t *) crl; len = strlen(crl) + 1; if ((rv = mbedtls_x509_crl_parse(&cfg->crl, pem, len)) != 0) { - rv = nni_tls_mkerr(rv); + rv = tls_mkerr(rv); goto err; } } @@ -919,7 +918,7 @@ nng_tls_config_own_cert( pem = (const uint8_t *) cert; len = strlen(cert) + 1; if ((rv = mbedtls_x509_crt_parse(&ck->crt, pem, len)) != 0) { - rv = nni_tls_mkerr(rv); + rv = tls_mkerr(rv); goto err; } @@ -928,7 +927,7 @@ nng_tls_config_own_cert( rv = mbedtls_pk_parse_key(&ck->key, pem, len, (const uint8_t *) pass, pass != NULL ? strlen(pass) : 0); if (rv != 0) { - rv = nni_tls_mkerr(rv); + rv = tls_mkerr(rv); goto err; } @@ -941,7 +940,7 @@ nng_tls_config_own_cert( rv = mbedtls_ssl_conf_own_cert(&cfg->cfg_ctx, &ck->crt, &ck->key); if (rv != 0) { nni_mtx_unlock(&cfg->lk); - rv = nni_tls_mkerr(rv); + rv = tls_mkerr(rv); goto err; } diff --git a/src/supplemental/tls/none/tls.c b/src/supplemental/tls/none/tls.c index 257bb6b1..a1e70b73 100644 --- a/src/supplemental/tls/none/tls.c +++ b/src/supplemental/tls/none/tls.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2018 Devolutions <info@devolutions.net> // @@ -20,8 +20,6 @@ #include "core/nng_impl.h" #include "supplemental/tls/tls_api.h" -#include <nng/supplemental/tls/tls.h> - void nni_tls_config_fini(nng_tls_config *cfg) { @@ -42,68 +40,6 @@ nni_tls_config_hold(nng_tls_config *cfg) NNI_ARG_UNUSED(cfg); } -void -nni_tls_fini(nni_tls *tp) -{ - NNI_ARG_UNUSED(tp); -} - -int -nni_tls_init(nni_tls **tpp, nng_tls_config *cfg, nni_tcp_conn *tcp) -{ - NNI_ARG_UNUSED(tpp); - NNI_ARG_UNUSED(cfg); - NNI_ARG_UNUSED(tcp); - - return (NNG_ENOTSUP); -} - -// nni_tls_send is the exported send function. It has a similar -// calling convention as the platform TCP pipe. -void -nni_tls_send(nni_tls *tp, nni_aio *aio) -{ - NNI_ARG_UNUSED(tp); - nni_aio_finish_error(aio, NNG_ENOTSUP); -} - -void -nni_tls_recv(nni_tls *tp, nni_aio *aio) -{ - NNI_ARG_UNUSED(tp); - nni_aio_finish_error(aio, NNG_ENOTSUP); -} - -void -nni_tls_close(nni_tls *tp) -{ - NNI_ARG_UNUSED(tp); -} - -int -nni_tls_getopt( - nni_tls *tp, const char *name, void *buf, size_t *szp, nni_type t) -{ - NNI_ARG_UNUSED(tp); - NNI_ARG_UNUSED(name); - NNI_ARG_UNUSED(buf); - NNI_ARG_UNUSED(szp); - NNI_ARG_UNUSED(t); - return (NNG_ENOTSUP); -} - -int -nni_tls_setopt( - nni_tls *tp, const char *name, const void *buf, size_t sz, nni_type t) -{ - NNI_ARG_UNUSED(tp); - NNI_ARG_UNUSED(name); - NNI_ARG_UNUSED(buf); - NNI_ARG_UNUSED(sz); - NNI_ARG_UNUSED(t); - return (NNG_ENOTSUP); -} - int nng_tls_config_server_name(nng_tls_config *cfg, const char *name) { @@ -190,3 +126,29 @@ nng_tls_config_free(nng_tls_config *cfg) { NNI_ARG_UNUSED(cfg); } + +int +nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) +{ + NNI_ARG_UNUSED(dp); + NNI_ARG_UNUSED(url); + return (NNG_ENOTSUP); +} + +int +nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url) +{ + NNI_ARG_UNUSED(lp); + NNI_ARG_UNUSED(url); + return (NNG_ENOTSUP); +} + +int +nni_tls_checkopt(const char *nm, const void *buf, size_t sz, nni_type t) +{ + NNI_ARG_UNUSED(nm); + NNI_ARG_UNUSED(buf); + NNI_ARG_UNUSED(sz); + NNI_ARG_UNUSED(t); + return (NNG_ENOTSUP); +}
\ No newline at end of file diff --git a/src/supplemental/tls/tls_api.h b/src/supplemental/tls/tls_api.h index 22dd68a0..4e6146b1 100644 --- a/src/supplemental/tls/tls_api.h +++ b/src/supplemental/tls/tls_api.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2019 Devolutions <info@devolutions.net> // @@ -12,12 +12,28 @@ #ifndef NNG_SUPPLEMENTAL_TLS_TLS_API_H #define NNG_SUPPLEMENTAL_TLS_TLS_API_H -#include <stdbool.h> - #include <nng/supplemental/tls/tls.h> -// nni_tls represents the context for a single TLS stream. -typedef struct nni_tls nni_tls; +// This nni_tls_common structure represents the "base" structure for +// an implementation to extend. One of these must be the first member +// of the implementation specific TLS stream struct. +typedef struct { + nng_stream ops; + nni_aio * aio; // system aio for connect/accept + nni_aio * uaio; // user aio for connect/accept + nng_tls_config *cfg; +} nni_tls_common; + +// The implementation supplies this function to create the TLS connection +// object. All fields will be zeroed. +extern int nni_tls_alloc(nng_stream **); +extern int nni_tls_dialer_alloc(nng_stream_dialer **, const nng_url *); +extern int nni_tls_listener_alloc(nng_stream_listener **, const nng_url *); +extern int nni_tls_checkopt(const char *, const void *, size_t, nni_type); + +// nni_tls_start is called by the common TLS dialer/listener completions +// to start the TLS stream activity. This may also do allocations, etc. +extern int nni_tls_start(nng_stream *, nng_stream *); // nni_tls_config_init creates a new TLS configuration object. // The object is created with a reference count of one. @@ -34,30 +50,4 @@ extern void nni_tls_config_fini(nng_tls_config *); // the configuration object is created with a hold on it. extern void nni_tls_config_hold(nng_tls_config *); -extern int nni_tls_init(nni_tls **, nng_tls_config *, nni_tcp_conn *); -extern void nni_tls_close(nni_tls *); -extern void nni_tls_fini(nni_tls *); -extern void nni_tls_send(nni_tls *, nng_aio *); -extern void nni_tls_recv(nni_tls *, nng_aio *); - -extern int nni_tls_setopt( - nni_tls *, const char *, const void *, size_t, nni_type); -extern int nni_tls_getopt(nni_tls *, const char *, void *, size_t *, nni_type); - -extern int nni_tls_set( - nng_tls *, const char *, const void *, size_t, nni_type); -extern int nni_tls_get(nng_tls *, const char *, void *, size_t *, nni_type); - -extern int nni_tls_dialer_setopt( - nng_tls_dialer *, const char *, const void *, size_t, nni_type); - -extern int nni_tls_dialer_getopt( - nng_tls_dialer *, const char *, void *, size_t *, nni_type); - -extern int nni_tls_listener_setopt( - nng_tls_listener *, const char *, const void *, size_t, nni_type); - -extern int nni_tls_listener_getopt( - nng_tls_listener *, const char *, void *, size_t *, nni_type); - #endif // NNG_SUPPLEMENTAL_TLS_TLS_API_H diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c index f93ca0ba..990d3add 100644 --- a/src/supplemental/tls/tls_common.c +++ b/src/supplemental/tls/tls_common.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2019 Devolutions <info@devolutions.net> // @@ -15,9 +15,9 @@ #include <string.h> #include "core/nng_impl.h" +#include "core/tcp.h" #include "supplemental/tls/tls_api.h" -#include <nng/supplemental/tcp/tcp.h> #include <nng/supplemental/tls/tls.h> // This file contains common code for TLS, and is only compiled if we @@ -25,126 +25,29 @@ // parts of TLS support that are invariant relative to different TLS // libraries, such as dialer and listener support. -struct nng_tls_s { - nni_tls * c; - nni_aio * aio; // system aio for connect/accept - nni_aio * uaio; // user aio for connect/accept - nng_tls_config *cfg; -}; - -// We use a union and share an "endpoint" for both dialers and listeners. -// This allows us to reuse the bulk of the code for things like option -// handlers for both dialers and listeners. -typedef union tls_tcp_ep_u { - nni_tcp_dialer * d; - nni_tcp_listener *l; -} tls_tcp_ep; - -typedef struct nng_tls_ep_s { - tls_tcp_ep tcp; - nng_tls_config *cfg; - nni_mtx lk; -} tls_ep; - -void -nng_tls_close(nng_tls *tls) -{ - nni_tls_close(tls->c); -} +typedef struct { + nng_stream_dialer ops; + nng_stream_dialer *d; // underlying TCP dialer + nng_tls_config * cfg; + nni_mtx lk; // protects the config +} tls_dialer; -void -nng_tls_free(nng_tls *tls) -{ - if (tls != NULL) { - nni_tls_fini(tls->c); - nni_aio_fini(tls->aio); - nng_tls_config_free(tls->cfg); - NNI_FREE_STRUCT(tls); - } -} - -void -nng_tls_send(nng_tls *tls, nng_aio *aio) -{ - nni_tls_send(tls->c, aio); -} - -void -nng_tls_recv(nng_tls *tls, nng_aio *aio) -{ - nni_tls_recv(tls->c, aio); -} - -int -nni_tls_get(nng_tls *tls, const char *name, void *buf, size_t *szp, nni_type t) -{ - return (nni_tls_getopt(tls->c, name, buf, szp, t)); -} - -int -nni_tls_set( - nng_tls *tls, const char *name, const void *buf, size_t sz, nni_type t) -{ - return (nni_tls_setopt(tls->c, name, buf, sz, t)); -} - -int -nng_tls_getopt(nng_tls *tls, const char *name, void *buf, size_t *szp) -{ - return (nni_tls_getopt(tls->c, name, buf, szp, NNI_TYPE_OPAQUE)); -} - -int -nng_tls_setopt(nng_tls *tls, const char *name, const void *buf, size_t sz) -{ - return (nni_tls_setopt(tls->c, name, buf, sz, NNI_TYPE_OPAQUE)); -} - -int -nng_tls_dialer_alloc(nng_tls_dialer **dp) -{ - tls_ep *ep; - int rv; - - if ((rv = nni_init()) != 0) { - return (rv); - } - if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { - return (NNG_ENOMEM); - } - nni_mtx_init(&ep->lk); - - if ((rv = nni_tcp_dialer_init(&ep->tcp.d)) != 0) { - nni_mtx_fini(&ep->lk); - NNI_FREE_STRUCT(ep); - return (rv); - } - if ((rv = nng_tls_config_alloc(&ep->cfg, NNG_TLS_MODE_CLIENT)) != 0) { - nni_tcp_dialer_fini(ep->tcp.d); - nni_mtx_fini(&ep->lk); - NNI_FREE_STRUCT(ep); - return (rv); - } - *dp = (void *) ep; - return (rv); -} - -void -nng_tls_dialer_close(nng_tls_dialer *d) +static void +tls_dialer_close(void *arg) { - tls_ep *ep = (void *) d; - nni_tcp_dialer_close(ep->tcp.d); + tls_dialer *d = arg; + nng_stream_dialer_close(d->d); } -void -nng_tls_dialer_free(nng_tls_dialer *d) +static void +tls_dialer_free(void *arg) { - tls_ep *ep = (void *) d; - if (ep != NULL) { - nni_tcp_dialer_fini(ep->tcp.d); - nng_tls_config_free(ep->cfg); - nni_mtx_fini(&ep->lk); - NNI_FREE_STRUCT(ep); + tls_dialer *d; + if ((d = arg) != NULL) { + nng_stream_dialer_free(d->d); + nng_tls_config_free(d->cfg); + nni_mtx_fini(&d->lk); + NNI_FREE_STRUCT(d); } } @@ -154,28 +57,28 @@ nng_tls_dialer_free(nng_tls_dialer *d) static void tls_conn_cb(void *arg) { - nng_tls * tls = arg; - nni_tcp_conn *tcp; - int rv; + nng_stream * tls = arg; + nni_tls_common *com = arg; + nng_stream * tcp; + int rv; - if ((rv = nni_aio_result(tls->aio)) != 0) { - nni_aio_finish_error(tls->uaio, rv); - nng_tls_free(tls); + if ((rv = nni_aio_result(com->aio)) != 0) { + nni_aio_finish_error(com->uaio, rv); + nng_stream_free(tls); return; } - tcp = nni_aio_get_output(tls->aio, 0); + tcp = nni_aio_get_output(com->aio, 0); - rv = nni_tls_init(&tls->c, tls->cfg, tcp); - if (rv != 0) { - nni_aio_finish_error(tls->uaio, rv); - nni_tcp_conn_fini(tcp); - nng_tls_free(tls); + if ((rv = nni_tls_start(tls, tcp)) != 0) { + nni_aio_finish_error(com->uaio, rv); + nng_stream_free(tcp); + nng_stream_free(tls); return; } - nni_aio_set_output(tls->uaio, 0, tls); - nni_aio_finish(tls->uaio, 0, 0); + nni_aio_set_output(com->uaio, 0, tls); + nni_aio_finish(com->uaio, 0, 0); } // Dialer cancel is called when the user has indicated that they no longer @@ -183,49 +86,52 @@ tls_conn_cb(void *arg) static void tls_conn_cancel(nni_aio *aio, void *arg, int rv) { - nng_tls *tls = arg; - NNI_ASSERT(tls->uaio == aio); + nni_tls_common *com = arg; + NNI_ASSERT(com->uaio == aio); // Just pass this down. If the connection is already done, this // will have no effect. - nni_aio_abort(tls->aio, rv); + nni_aio_abort(com->aio, rv); } -void -nng_tls_dialer_dial(nng_tls_dialer *d, const nng_sockaddr *sa, nng_aio *aio) +static void +tls_dialer_dial(void *arg, nng_aio *aio) { - int rv; - nng_tls *tls; - tls_ep * ep = (void *) d; + tls_dialer * d = arg; + int rv; + nng_stream * tls; + nni_tls_common *com; if (nni_aio_begin(aio) != 0) { return; } - if ((tls = NNI_ALLOC_STRUCT(tls)) == NULL) { - nni_aio_finish_error(aio, NNG_ENOMEM); + if ((rv = nni_tls_alloc(&tls)) != 0) { + nni_aio_finish_error(aio, rv); return; } - if ((rv = nni_aio_init(&tls->aio, tls_conn_cb, tls)) != 0) { + + com = (void *) tls; + if ((rv = nni_aio_init(&com->aio, tls_conn_cb, tls)) != 0) { nni_aio_finish_error(aio, rv); - NNI_FREE_STRUCT(tls); + nng_stream_free(tls); return; } - tls->uaio = aio; + com->uaio = aio; // Save a copy of the TLS configuration. This way we don't have // to ensure that the dialer outlives the connection, because the // only shared data is the configuration which is reference counted. - nni_mtx_lock(&ep->lk); - tls->cfg = ep->cfg; - nng_tls_config_hold(tls->cfg); - nni_mtx_unlock(&ep->lk); + nni_mtx_lock(&d->lk); + com->cfg = d->cfg; + nng_tls_config_hold(com->cfg); + nni_mtx_unlock(&d->lk); if ((rv = nni_aio_schedule(aio, tls_conn_cancel, tls)) != 0) { nni_aio_finish_error(aio, rv); - nng_tls_free(tls); + nng_stream_free(tls); return; } - nni_tcp_dialer_dial(ep->tcp.d, sa, tls->aio); + nng_stream_dialer_dial(d->d, com->aio); } static int @@ -241,11 +147,12 @@ tls_check_string(const void *v, size_t sz, nni_opt_type t) } static int -tls_ep_set_config(void *arg, const void *buf, size_t sz, nni_type t) +tls_dialer_set_config(void *arg, const void *buf, size_t sz, nni_type t) { int rv; nng_tls_config *cfg; - tls_ep * ep; + tls_dialer * d = arg; + nng_tls_config *old; if ((rv = nni_copyin_ptr((void **) &cfg, buf, sz, t)) != 0) { return (rv); @@ -253,308 +160,523 @@ tls_ep_set_config(void *arg, const void *buf, size_t sz, nni_type t) if (cfg == NULL) { return (NNG_EINVAL); } - if ((ep = arg) != NULL) { - nng_tls_config *old; - - nni_mtx_lock(&ep->lk); - old = ep->cfg; - nng_tls_config_hold(cfg); - ep->cfg = cfg; - nni_mtx_unlock(&ep->lk); - if (old != NULL) { - nng_tls_config_free(old); - } + nni_mtx_lock(&d->lk); + old = d->cfg; + nng_tls_config_hold(cfg); + d->cfg = cfg; + nni_mtx_unlock(&d->lk); + if (old != NULL) { + nng_tls_config_free(old); } return (0); } static int -tls_ep_get_config(void *arg, void *buf, size_t *szp, nni_type t) +tls_dialer_get_config(void *arg, void *buf, size_t *szp, nni_type t) { - tls_ep * ep = arg; + tls_dialer * d = arg; nng_tls_config *cfg; int rv; - nni_mtx_lock(&ep->lk); - if ((cfg = ep->cfg) != NULL) { + nni_mtx_lock(&d->lk); + if ((cfg = d->cfg) != NULL) { nng_tls_config_hold(cfg); } if ((rv = nni_copyout_ptr(cfg, buf, szp, t)) != 0) { nng_tls_config_free(cfg); } - nni_mtx_unlock(&ep->lk); + nni_mtx_unlock(&d->lk); return (rv); } static int -tls_ep_set_server_name(void *arg, const void *buf, size_t sz, nni_type t) +tls_dialer_set_server_name(void *arg, const void *buf, size_t sz, nni_type t) { - tls_ep *ep = arg; - int rv; - if ((rv = tls_check_string(buf, sz, t)) != 0) { - return (rv); - } - if ((ep = arg) != NULL) { - nni_mtx_lock(&ep->lk); - rv = nng_tls_config_server_name(ep->cfg, buf); - nni_mtx_unlock(&ep->lk); + tls_dialer *d = arg; + int rv; + if ((rv = tls_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&d->lk); + rv = nng_tls_config_server_name(d->cfg, buf); + nni_mtx_unlock(&d->lk); } return (rv); } static int -tls_ep_set_auth_mode(void *arg, const void *buf, size_t sz, nni_type t) +tls_dialer_set_auth_mode(void *arg, const void *buf, size_t sz, nni_type t) { - int mode; - int rv; - tls_ep *ep; + int mode; + int rv; + tls_dialer *d = arg; rv = nni_copyin_int(&mode, buf, sz, NNG_TLS_AUTH_MODE_NONE, NNG_TLS_AUTH_MODE_REQUIRED, t); - if ((rv == 0) && ((ep = arg) != NULL)) { - nni_mtx_lock(&ep->lk); - rv = nng_tls_config_auth_mode(ep->cfg, mode); - nni_mtx_unlock(&ep->lk); + if (rv == 0) { + nni_mtx_lock(&d->lk); + rv = nng_tls_config_auth_mode(d->cfg, mode); + nni_mtx_unlock(&d->lk); } return (rv); } static int -tls_ep_set_ca_file(void *arg, const void *buf, size_t sz, nni_opt_type t) +tls_dialer_set_ca_file(void *arg, const void *buf, size_t sz, nni_opt_type t) { - tls_ep *ep; - int rv; + tls_dialer *d = arg; + int rv; - if (((rv = tls_check_string(buf, sz, t)) == 0) && - ((ep = arg) != NULL)) { - nni_mtx_lock(&ep->lk); - rv = nng_tls_config_ca_file(ep->cfg, buf); - nni_mtx_unlock(&ep->lk); + if ((rv = tls_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&d->lk); + rv = nng_tls_config_ca_file(d->cfg, buf); + nni_mtx_unlock(&d->lk); } return (rv); } static int -tls_ep_set_cert_key_file(void *arg, const void *buf, size_t sz, nni_opt_type t) +tls_dialer_set_cert_key_file( + void *arg, const void *buf, size_t sz, nni_opt_type t) { - tls_ep *ep; - int rv; + tls_dialer *d = arg; + int rv; - if (((rv = tls_check_string(buf, sz, t)) == 0) && - ((ep = arg) != NULL)) { - nni_mtx_lock(&ep->lk); - rv = nng_tls_config_cert_key_file(ep->cfg, buf, NULL); - nni_mtx_unlock(&ep->lk); + if ((rv = tls_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&d->lk); + rv = nng_tls_config_cert_key_file(d->cfg, buf, NULL); + nni_mtx_unlock(&d->lk); } return (rv); } -static const nni_option tls_ep_opts[] = { +static const nni_option tls_dialer_opts[] = { { .o_name = NNG_OPT_TLS_CONFIG, - .o_get = tls_ep_get_config, - .o_set = tls_ep_set_config, + .o_get = tls_dialer_get_config, + .o_set = tls_dialer_set_config, }, { .o_name = NNG_OPT_TLS_SERVER_NAME, - .o_set = tls_ep_set_server_name, + .o_set = tls_dialer_set_server_name, }, { .o_name = NNG_OPT_TLS_CA_FILE, - .o_set = tls_ep_set_ca_file, + .o_set = tls_dialer_set_ca_file, }, { .o_name = NNG_OPT_TLS_CERT_KEY_FILE, - .o_set = tls_ep_set_cert_key_file, + .o_set = tls_dialer_set_cert_key_file, }, { .o_name = NNG_OPT_TLS_AUTH_MODE, - .o_set = tls_ep_set_auth_mode, + .o_set = tls_dialer_set_auth_mode, }, { .o_name = NULL, }, }; -// private version of getopt and setopt take the type -int -nni_tls_dialer_getopt( - nng_tls_dialer *d, const char *name, void *buf, size_t *szp, nni_type t) +static int +tls_dialer_getx( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - int rv; - tls_ep *ep = (void *) d; + tls_dialer *d = arg; + int rv; - rv = nni_tcp_dialer_getopt(ep->tcp.d, name, buf, szp, t); + rv = nni_stream_dialer_getx(d->d, name, buf, szp, t); if (rv == NNG_ENOTSUP) { - rv = nni_getopt(tls_ep_opts, name, ep, buf, szp, t); + rv = nni_getopt(tls_dialer_opts, name, d, buf, szp, t); } return (rv); } -int -nni_tls_dialer_setopt(nng_tls_dialer *d, const char *name, const void *buf, - size_t sz, nni_type t) +static int +tls_dialer_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) { - int rv; - tls_ep *ep = (void *) d; + tls_dialer *d = arg; + int rv; - rv = nni_tcp_dialer_setopt( - ep != NULL ? ep->tcp.d : NULL, name, buf, sz, t); + rv = nni_stream_dialer_setx(d->d, name, buf, sz, t); if (rv == NNG_ENOTSUP) { - rv = nni_setopt(tls_ep_opts, name, ep, buf, sz, t); + rv = nni_setopt(tls_dialer_opts, name, d, buf, sz, t); } return (rv); } -// public versions of option handlers here - -int -nng_tls_dialer_getopt( - nng_tls_dialer *d, const char *name, void *buf, size_t *szp) -{ - return (nni_tls_dialer_getopt(d, name, buf, szp, NNI_TYPE_OPAQUE)); -} - int -nng_tls_dialer_setopt( - nng_tls_dialer *d, const char *name, const void *buf, size_t sz) +nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) { - return (nni_tls_dialer_setopt(d, name, buf, sz, NNI_TYPE_OPAQUE)); -} + tls_dialer *d; + int rv; + nng_url myurl; -void -nng_tls_listener_close(nng_tls_listener *l) -{ - tls_ep *ep = (void *) l; - nni_tcp_listener_close(ep->tcp.l); -} - -void -nng_tls_listener_free(nng_tls_listener *l) -{ - tls_ep *ep = (void *) l; - if (ep != NULL) { - nng_tls_listener_close(l); - nng_tls_config_free(ep->cfg); - nni_mtx_fini(&ep->lk); - NNI_FREE_STRUCT(ep); - } -} - -int -nng_tls_listener_alloc(nng_tls_listener **lp) -{ - tls_ep *ep; - int rv; + memcpy(&myurl, url, sizeof(myurl)); + myurl.u_scheme = url->u_scheme + strlen("tls+"); if ((rv = nni_init()) != 0) { return (rv); } - if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { + if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { return (NNG_ENOMEM); } - nni_mtx_init(&ep->lk); + nni_mtx_init(&d->lk); - if ((rv = nni_tcp_listener_init(&ep->tcp.l)) != 0) { - nni_mtx_fini(&ep->lk); - NNI_FREE_STRUCT(ep); + if ((rv = nng_stream_dialer_alloc_url(&d->d, &myurl)) != 0) { + nni_mtx_fini(&d->lk); + NNI_FREE_STRUCT(d); return (rv); } - if ((rv = nng_tls_config_alloc(&ep->cfg, NNG_TLS_MODE_SERVER)) != 0) { - nni_tcp_listener_fini(ep->tcp.l); - nni_mtx_fini(&ep->lk); - NNI_FREE_STRUCT(ep); + if ((rv = nng_tls_config_alloc(&d->cfg, NNG_TLS_MODE_CLIENT)) != 0) { + nng_stream_dialer_free(d->d); + nni_mtx_fini(&d->lk); + NNI_FREE_STRUCT(d); return (rv); } - *lp = (void *) ep; - return (0); + + // Set the expected outbound hostname + nng_tls_config_server_name(d->cfg, url->u_hostname); + + d->ops.sd_close = tls_dialer_close; + d->ops.sd_free = tls_dialer_free; + d->ops.sd_dial = tls_dialer_dial; + d->ops.sd_getx = tls_dialer_getx; + d->ops.sd_setx = tls_dialer_setx; + *dp = (void *) d; + return (rv); } -int -nng_tls_listener_listen(nng_tls_listener *l, const nng_sockaddr *sa) +typedef struct { + nng_stream_listener ops; + nng_stream_listener *l; + nng_tls_config * cfg; + nni_mtx lk; +} tls_listener; + +static void +tls_listener_close(void *arg) { - tls_ep *ep = (void *) l; - return (nni_tcp_listener_listen(ep->tcp.l, sa)); + tls_listener *l = arg; + nng_stream_listener_close(l->l); } -void -nng_tls_listener_accept(nng_tls_listener *l, nng_aio *aio) +static void +tls_listener_free(void *arg) { - int rv; - nng_tls *tls; - tls_ep * ep = (void *) l; + tls_listener *l; + if ((l = arg) != NULL) { + tls_listener_close(l); + nng_tls_config_free(l->cfg); + nni_mtx_fini(&l->lk); + NNI_FREE_STRUCT(l); + } +} + +static int +tls_listener_listen(void *arg) +{ + tls_listener *l = arg; + return (nng_stream_listener_listen(l->l)); +} + +static void +tls_listener_accept(void *arg, nng_aio *aio) +{ + tls_listener * l = arg; + int rv; + nng_stream * tls; + nni_tls_common *com; if (nni_aio_begin(aio) != 0) { return; } - if ((tls = NNI_ALLOC_STRUCT(tls)) == NULL) { - nni_aio_finish_error(aio, NNG_ENOMEM); + if ((rv = nni_tls_alloc(&tls)) != 0) { + nni_aio_finish_error(aio, rv); return; } - if ((rv = nni_aio_init(&tls->aio, tls_conn_cb, tls)) != 0) { + com = (void *) tls; + if ((rv = nni_aio_init(&com->aio, tls_conn_cb, tls)) != 0) { nni_aio_finish_error(aio, rv); - NNI_FREE_STRUCT(tls); + nng_stream_free(tls); return; } - tls->uaio = aio; + com->uaio = aio; // Save a copy of the TLS configuration. This way we don't have // to ensure that the dialer outlives the connection, because the // only shared data is the configuration which is reference counted. - nni_mtx_lock(&ep->lk); - tls->cfg = ep->cfg; - nng_tls_config_hold(tls->cfg); - nni_mtx_unlock(&ep->lk); + nni_mtx_lock(&l->lk); + com->cfg = l->cfg; + nng_tls_config_hold(com->cfg); + nni_mtx_unlock(&l->lk); if ((rv = nni_aio_schedule(aio, tls_conn_cancel, tls)) != 0) { nni_aio_finish_error(aio, rv); - nng_tls_free(tls); + nng_stream_free(tls); return; } - nni_tcp_listener_accept(ep->tcp.l, tls->aio); + nng_stream_listener_accept(l->l, com->aio); } -int -nni_tls_listener_getopt( - nng_tls_listener *l, const char *name, void *buf, size_t *szp, nni_type t) +static int +tls_listener_set_config(void *arg, const void *buf, size_t sz, nni_type t) { - int rv; - tls_ep *ep = (void *) l; + int rv; + nng_tls_config *cfg; + tls_listener * l = arg; + nng_tls_config *old; - rv = nni_tcp_listener_getopt(ep->tcp.l, name, buf, szp, t); - if (rv == NNG_ENOTSUP) { - rv = nni_getopt(tls_ep_opts, name, ep, buf, szp, t); + if ((rv = nni_copyin_ptr((void **) &cfg, buf, sz, t)) != 0) { + return (rv); + } + if (cfg == NULL) { + return (NNG_EINVAL); + } + + nni_mtx_lock(&l->lk); + old = l->cfg; + nng_tls_config_hold(cfg); + l->cfg = cfg; + nni_mtx_unlock(&l->lk); + if (old != NULL) { + nng_tls_config_free(old); + } + return (0); +} + +static int +tls_listener_get_config(void *arg, void *buf, size_t *szp, nni_type t) +{ + tls_listener * l = arg; + nng_tls_config *cfg; + int rv; + nni_mtx_lock(&l->lk); + if ((cfg = l->cfg) != NULL) { + nng_tls_config_hold(cfg); + } + if ((rv = nni_copyout_ptr(cfg, buf, szp, t)) != 0) { + nng_tls_config_free(cfg); } + nni_mtx_unlock(&l->lk); return (rv); } -int -nni_tls_listener_setopt(nng_tls_listener *l, const char *name, const void *buf, - size_t sz, nni_type t) +static int +tls_listener_set_server_name(void *arg, const void *buf, size_t sz, nni_type t) +{ + tls_listener *l = arg; + int rv; + if ((rv = tls_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&l->lk); + rv = nng_tls_config_server_name(l->cfg, buf); + nni_mtx_unlock(&l->lk); + } + return (rv); +} + +static int +tls_listener_set_auth_mode(void *arg, const void *buf, size_t sz, nni_type t) { - int rv; - tls_ep *ep = (void *) l; + int mode; + int rv; + tls_listener *l = arg; - rv = nni_tcp_listener_setopt( - ep != NULL ? ep->tcp.l : NULL, name, buf, sz, t); + rv = nni_copyin_int(&mode, buf, sz, NNG_TLS_AUTH_MODE_NONE, + NNG_TLS_AUTH_MODE_REQUIRED, t); + if (rv == 0) { + nni_mtx_lock(&l->lk); + rv = nng_tls_config_auth_mode(l->cfg, mode); + nni_mtx_unlock(&l->lk); + } + return (rv); +} + +static int +tls_listener_set_ca_file(void *arg, const void *buf, size_t sz, nni_opt_type t) +{ + tls_listener *l = arg; + int rv; + + if ((rv = tls_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&l->lk); + rv = nng_tls_config_ca_file(l->cfg, buf); + nni_mtx_unlock(&l->lk); + } + return (rv); +} + +static int +tls_listener_set_cert_key_file( + void *arg, const void *buf, size_t sz, nni_opt_type t) +{ + tls_listener *l = arg; + int rv; + + if ((rv = tls_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&l->lk); + rv = nng_tls_config_cert_key_file(l->cfg, buf, NULL); + nni_mtx_unlock(&l->lk); + } + return (rv); +} + +static const nni_option tls_listener_opts[] = { + { + .o_name = NNG_OPT_TLS_CONFIG, + .o_get = tls_listener_get_config, + .o_set = tls_listener_set_config, + }, + { + .o_name = NNG_OPT_TLS_SERVER_NAME, + .o_set = tls_listener_set_server_name, + }, + { + .o_name = NNG_OPT_TLS_CA_FILE, + .o_set = tls_listener_set_ca_file, + }, + { + .o_name = NNG_OPT_TLS_CERT_KEY_FILE, + .o_set = tls_listener_set_cert_key_file, + }, + { + .o_name = NNG_OPT_TLS_AUTH_MODE, + .o_set = tls_listener_set_auth_mode, + }, + { + .o_name = NULL, + }, +}; + +static int +tls_listener_getx( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) +{ + int rv; + tls_listener *l = arg; + + rv = nni_stream_listener_getx(l->l, name, buf, szp, t); if (rv == NNG_ENOTSUP) { - rv = nni_setopt(tls_ep_opts, name, ep, buf, sz, t); + rv = nni_getopt(tls_listener_opts, name, l, buf, szp, t); } return (rv); } -// public versions of option handlers here +static int +tls_listener_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) +{ + int rv; + tls_listener *l = arg; + + rv = nni_stream_listener_setx(l->l, name, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_setopt(tls_listener_opts, name, l, buf, sz, t); + } + return (rv); +} int -nng_tls_listener_getopt( - nng_tls_listener *l, const char *name, void *buf, size_t *szp) +nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url) { - return (nni_tls_listener_getopt(l, name, buf, szp, NNI_TYPE_OPAQUE)); + tls_listener *l; + int rv; + nng_url myurl; + + memcpy(&myurl, url, sizeof(myurl)); + myurl.u_scheme = url->u_scheme + strlen("tls+"); + + if ((rv = nni_init()) != 0) { + return (rv); + } + if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { + return (NNG_ENOMEM); + } + nni_mtx_init(&l->lk); + + if ((rv = nng_stream_listener_alloc_url(&l->l, &myurl)) != 0) { + nni_mtx_fini(&l->lk); + NNI_FREE_STRUCT(l); + return (rv); + } + if ((rv = nng_tls_config_alloc(&l->cfg, NNG_TLS_MODE_SERVER)) != 0) { + nng_stream_listener_free(l->l); + nni_mtx_fini(&l->lk); + NNI_FREE_STRUCT(l); + return (rv); + } + l->ops.sl_free = tls_listener_free; + l->ops.sl_close = tls_listener_close; + l->ops.sl_accept = tls_listener_accept; + l->ops.sl_listen = tls_listener_listen; + l->ops.sl_getx = tls_listener_getx; + l->ops.sl_setx = tls_listener_setx; + *lp = (void *) l; + return (0); } +// The following checks exist for socket configuration, when we need to +// configure an option on a socket before any transport is configured +// underneath. + +static int +tls_check_config(const void *buf, size_t sz, nni_type t) +{ + int rv; + nng_tls_config *cfg; + + if ((rv = nni_copyin_ptr((void **) &cfg, buf, sz, t)) != 0) { + return (rv); + } + if (cfg == NULL) { + return (NNG_EINVAL); + } + return (0); +} + +static int +tls_check_auth_mode(const void *buf, size_t sz, nni_type t) +{ + int mode; + int rv; + + rv = nni_copyin_int(&mode, buf, sz, NNG_TLS_AUTH_MODE_NONE, + NNG_TLS_AUTH_MODE_REQUIRED, t); + return (rv); +} + +static const nni_chkoption tls_chkopts[] = { + { + .o_name = NNG_OPT_TLS_CONFIG, + .o_check = tls_check_config, + }, + { + .o_name = NNG_OPT_TLS_SERVER_NAME, + .o_check = tls_check_string, + }, + { + .o_name = NNG_OPT_TLS_CA_FILE, + .o_check = tls_check_string, + }, + { + .o_name = NNG_OPT_TLS_CERT_KEY_FILE, + .o_check = tls_check_string, + }, + { + .o_name = NNG_OPT_TLS_AUTH_MODE, + .o_check = tls_check_auth_mode, + }, + { + .o_name = NULL, + }, +}; + int -nng_tls_listener_setopt( - nng_tls_listener *l, const char *name, const void *buf, size_t sz) +nni_tls_checkopt(const char *name, const void *data, size_t sz, nni_type t) { - return (nni_tls_listener_setopt(l, name, buf, sz, NNI_TYPE_OPAQUE)); + int rv; + + rv = nni_chkopt(tls_chkopts, name, data, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_stream_checkopt("tcp", name, data, sz, t); + } + return (rv); } diff --git a/src/supplemental/websocket/CMakeLists.txt b/src/supplemental/websocket/CMakeLists.txt index 22ee955d..3b102c80 100644 --- a/src/supplemental/websocket/CMakeLists.txt +++ b/src/supplemental/websocket/CMakeLists.txt @@ -12,5 +12,7 @@ if (NNG_SUPP_WEBSOCKET) set(_SRCS supplemental/websocket/websocket.c supplemental/websocket/websocket.h) +else() + set(_SRCS supplemental/websocket/stubs.c) endif() set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) diff --git a/src/supplemental/websocket/stub.c b/src/supplemental/websocket/stub.c new file mode 100644 index 00000000..94e7f1b5 --- /dev/null +++ b/src/supplemental/websocket/stub.c @@ -0,0 +1,40 @@ +// +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Devolutions <info@devolutions.net> +// +// 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 "core/nng_impl.h" + +// This stub file exists to support configuration of the stream subsystem +// when websocket support is unconfigured. + +int +nni_ws_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) +{ + NNI_ARG_UNUSED(dp); + NNI_ARG_UNUSED(url); + return (NNG_ENOTSUP); +} + +int +nni_ws_listener_alloc(nng_stream_listener **lp, const nng_url *url) +{ + NNI_ARG_UNUSED(lp); + NNI_ARG_UNUSED(url); + return (NNG_ENOTSUP); +} + +int +nni_ws_checkopt(const char *name, const void *data, size_t sz, nni_type t) +{ + NNI_ARG_UNUSED(name); + NNI_ARG_UNUSED(data); + NNI_ARG_UNUSED(sz); + NNI_ARG_UNUSED(t); + return (NNG_ENOTSUP); +} diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index 3d3a68cb..4cecf430 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -18,11 +18,25 @@ #include "supplemental/http/http_api.h" #include "supplemental/sha1/sha1.h" +#include <nng/transport/ws/websocket.h> + #include "websocket.h" +// This should be removed or handled differently in the future. +typedef int (*nni_ws_listen_hook)(void *, nng_http_req *, nng_http_res *); + +// We have chosen to be a bit more stringent in the size of the frames that +// we send, while we more generously allow larger incoming frames. These +// may be tuned by options. +#define WS_DEF_RECVMAX (1U << 20) // 1MB Message limit (message mode only) +#define WS_DEF_MAXRXFRAME (1U << 20) // 1MB Frame size (recv) +#define WS_DEF_MAXTXFRAME (1U << 16) // 64KB Frame size (send) + +// Alias for checking the prefix of a string. +#define startswith(s, t) (strncmp(s, t, strlen(t)) == 0) + // Pre-defined types for some prototypes. These are from other subsystems. typedef struct ws_frame ws_frame; -typedef struct ws_msg ws_msg; typedef struct ws_header { nni_list_node node; @@ -31,17 +45,20 @@ typedef struct ws_header { } ws_header; struct nni_ws { + nng_stream ops; nni_list_node node; nni_reap_item reap; bool server; bool closed; bool ready; bool wclose; + bool isstream; + bool inmsg; nni_mtx mtx; - nni_list txmsgs; - nni_list rxmsgs; nni_list sendq; nni_list recvq; + nni_list txq; + nni_list rxq; ws_frame * txframe; ws_frame * rxframe; nni_aio * txaio; // physical aios @@ -57,26 +74,31 @@ struct nni_ws { char * reshdrs; size_t maxframe; size_t fragsize; + size_t recvmax; // largest message size nni_ws_listener *listener; nni_ws_dialer * dialer; }; struct nni_ws_listener { - nni_http_server * server; - char * proto; - nni_mtx mtx; - nni_cv cv; - nni_list pend; - nni_list reply; - nni_list aios; - nni_url * url; - bool started; - bool closed; - nni_http_handler * handler; - nni_ws_listen_hook hookfn; - void * hookarg; - nni_list headers; // response headers - size_t maxframe; + nng_stream_listener ops; + nni_http_server * server; + char * proto; + nni_mtx mtx; + nni_cv cv; + nni_list pend; + nni_list reply; + nni_list aios; + nng_url * url; + bool started; + bool closed; + bool isstream; + nni_http_handler * handler; + nni_ws_listen_hook hookfn; + void * hookarg; + nni_list headers; // response headers + size_t maxframe; + size_t fragsize; + size_t recvmax; // largest message size }; // The dialer tracks user aios in two lists. The first list is for aios @@ -86,18 +108,21 @@ struct nni_ws_listener { // completion of an earlier connection. (We don't want to establish // requests when we already have connects negotiating.) struct nni_ws_dialer { - nni_http_req * req; - nni_http_res * res; - nni_http_client *client; - nni_mtx mtx; - nni_cv cv; - char * proto; - nni_url * url; - nni_list wspend; // ws structures still negotiating - bool closed; - nng_sockaddr sa; - nni_list headers; // request headers - size_t maxframe; + nng_stream_dialer ops; + nni_http_req * req; + nni_http_res * res; + nni_http_client * client; + nni_mtx mtx; + nni_cv cv; + char * proto; + nng_url * url; + nni_list wspend; // ws structures still negotiating + bool closed; + bool isstream; + nni_list headers; // request headers + size_t maxframe; + size_t fragsize; + size_t recvmax; }; typedef enum ws_type { @@ -133,16 +158,7 @@ struct ws_frame { bool masked; size_t bufsz; // allocated size uint8_t * buf; - ws_msg * wmsg; -}; - -struct ws_msg { - nni_list frames; - nni_list_node node; - nni_ws * ws; - nni_aio * aio; - uint8_t * buf; - size_t bufsz; + nng_aio * aio; }; static void ws_send_close(nni_ws *ws, uint16_t code); @@ -150,6 +166,127 @@ static void ws_conn_cb(void *); static void ws_close_cb(void *); static void ws_read_cb(void *); static void ws_write_cb(void *); +static void ws_close_error(nni_ws *ws, uint16_t code); + +static void ws_str_free(void *); +static void ws_str_close(void *); +static void ws_str_send(void *, nng_aio *); +static void ws_str_recv(void *, nng_aio *); +static int ws_str_getx(void *, const char *, void *, size_t *, nni_type); +static int ws_str_setx(void *, const char *, const void *, size_t, nni_type); + +static void ws_listener_close(void *); +static void ws_listener_free(void *); + +static int +ws_check_string(const void *v, size_t sz, nni_opt_type t) +{ + if ((t != NNI_TYPE_OPAQUE) && (t != NNI_TYPE_STRING)) { + return (NNG_EBADTYPE); + } + if (nni_strnlen(v, sz) >= sz) { + return (NNG_EINVAL); + } + return (0); +} + +static int +ws_set_header_ext(nni_list *l, const char *n, const char *v, bool strip_dups) +{ + ws_header *hdr; + char * nv; + + if ((nv = nni_strdup(v)) == NULL) { + return (NNG_ENOMEM); + } + + if (strip_dups) { + NNI_LIST_FOREACH (l, hdr) { + if (nni_strcasecmp(hdr->name, n) == 0) { + nni_strfree(hdr->value); + hdr->value = nv; + return (0); + } + } + } + + if ((hdr = NNI_ALLOC_STRUCT(hdr)) == NULL) { + nni_strfree(nv); + return (NNG_ENOMEM); + } + if ((hdr->name = nni_strdup(n)) == NULL) { + nni_strfree(nv); + NNI_FREE_STRUCT(hdr); + return (NNG_ENOMEM); + } + hdr->value = nv; + nni_list_append(l, hdr); + return (0); +} + +static int +ws_set_header(nni_list *l, const char *n, const char *v) +{ + return (ws_set_header_ext(l, n, v, true)); +} + +static int +ws_set_headers(nni_list *l, const char *str) +{ + char * dupstr; + size_t duplen; + char * n; + char * v; + char * nl; + int rv; + + if ((dupstr = nni_strdup(str)) == NULL) { + return (NNG_ENOMEM); + } + duplen = strlen(dupstr) + 1; // so we can free it later + + n = dupstr; + for (;;) { + if ((v = strchr(n, ':')) == NULL) { + // Note that this also means that if + // a bare word is present, we ignore it. + break; + } + *v = '\0'; + v++; + while (*v == ' ') { + // Skip leading whitespace. Not strictly + // necessary, but still a good idea. + v++; + } + nl = v; + // Find the end of the line -- should be CRLF, but can + // also be unterminated or just LF if user + while ((*nl != '\0') && (*nl != '\r') && (*nl != '\n')) { + nl++; + } + while ((*nl == '\r') || (*nl == '\n')) { + *nl = '\0'; + nl++; + } + + // Note that this can lead to a partial failure. As this + // is most likely ENOMEM, don't worry too much about it. + // This method does *not* eliminate duplicates. + if ((rv = ws_set_header_ext(l, n, v, false)) != 0) { + goto done; + } + + // Advance to the next name. + n = nl; + } + + rv = 0; + +done: + nni_free(dupstr, duplen); + return (rv); +} // This looks, case independently for a word in a list, which is either // space or comma separated. @@ -204,30 +341,13 @@ ws_make_accept(const char *key, char *accept) static void ws_frame_fini(ws_frame *frame) { - if (frame->bufsz) { + if (frame->bufsz != 0) { nni_free(frame->buf, frame->bufsz); } NNI_FREE_STRUCT(frame); } static void -ws_msg_fini(ws_msg *wm) -{ - ws_frame *frame; - - NNI_ASSERT(!nni_list_node_active(&wm->node)); - while ((frame = nni_list_first(&wm->frames)) != NULL) { - nni_list_remove(&wm->frames, frame); - ws_frame_fini(frame); - } - if (wm->bufsz != 0) { - nni_free(wm->buf, wm->bufsz); - } - - NNI_FREE_STRUCT(wm); -} - -static void ws_mask_frame(ws_frame *frame) { uint32_t r; @@ -263,31 +383,19 @@ ws_unmask_frame(ws_frame *frame) static int ws_msg_init_control( - ws_msg **wmp, nni_ws *ws, uint8_t op, const uint8_t *buf, size_t len) + ws_frame **framep, nni_ws *ws, uint8_t op, const uint8_t *buf, size_t len) { - ws_msg * wm; ws_frame *frame; if (len > 125) { return (NNG_EINVAL); } - if ((wm = NNI_ALLOC_STRUCT(wm)) == NULL) { - return (NNG_ENOMEM); - } - wm->buf = NULL; - wm->bufsz = 0; - if ((frame = NNI_ALLOC_STRUCT(frame)) == NULL) { - ws_msg_fini(wm); return (NNG_ENOMEM); } - NNI_LIST_INIT(&wm->frames, ws_frame, node); memcpy(frame->sdata, buf, len); - - nni_list_append(&wm->frames, frame); - frame->wmsg = wm; frame->len = len; frame->final = true; frame->op = op; @@ -303,114 +411,100 @@ ws_msg_init_control( ws_mask_frame(frame); } - wm->aio = NULL; - wm->ws = ws; - *wmp = wm; + *framep = frame; return (0); } static int -ws_msg_init_tx(ws_msg **wmp, nni_ws *ws, nni_msg *msg, nni_aio *aio) +ws_frame_prep_tx(nni_ws *ws, ws_frame *frame) { - ws_msg * wm; + nng_aio *aio = frame->aio; + nni_iov *iov; + unsigned niov; size_t len; - size_t maxfrag = ws->fragsize; // make this tunable. (1MB default) uint8_t *buf; - uint8_t op; - - if ((wm = NNI_ALLOC_STRUCT(wm)) == NULL) { - return (NNG_ENOMEM); - } - NNI_LIST_INIT(&wm->frames, ws_frame, node); - - len = nni_msg_len(msg) + nni_msg_header_len(msg); - wm->bufsz = len; - if ((wm->buf = nni_alloc(len)) == NULL) { - NNI_FREE_STRUCT(wm); - return (NNG_ENOMEM); - } - buf = wm->buf; - memcpy(buf, nni_msg_header(msg), nni_msg_header_len(msg)); - memcpy(buf + nni_msg_header_len(msg), nni_msg_body(msg), - nni_msg_len(msg)); - - op = WS_BINARY; // to start -- no support for sending TEXT frames - - // do ... while because we want at least one frame (even for empty - // messages.) Headers get their own frame, if present. Best bet - // is to try not to have a header when coming here. - do { - ws_frame *frame; - if ((frame = NNI_ALLOC_STRUCT(frame)) == NULL) { - ws_msg_fini(wm); + // Figure out how much we need for the entire aio. + frame->len = 0; + nni_aio_get_iov(aio, &niov, &iov); + for (unsigned i = 0; i < niov; i++) { + frame->len += iov[i].iov_len; + } + + if ((frame->len > ws->fragsize) && (ws->fragsize > 0)) { + // Limit it to a single frame per policy (fragsize), as needed. + frame->len = ws->fragsize; + // For stream mode, we constrain ourselves to one frame + // per message. Submitter may see a partial transmit, and + // should resubmit as needed. For message mode, we will + // continue to resubmit. + frame->final = ws->isstream ? true : false; + } else { + // It all fits in this frame (which might not be the first), + // so we're done. + frame->final = true; + } + // Potentially allocate space for the data if we need to. + // Note that an empty message is legal. + if ((frame->bufsz < frame->len) && (frame->len > 0)) { + frame->buf = nni_alloc(frame->len); + if (frame->buf == NULL) { return (NNG_ENOMEM); } - nni_list_append(&wm->frames, frame); - frame->wmsg = wm; - frame->len = len > maxfrag ? maxfrag : len; - frame->buf = buf; - frame->op = op; - - buf += frame->len; - len -= frame->len; - op = WS_CONT; - - if (len == 0) { - frame->final = true; - } - frame->head[0] = frame->op; - frame->hlen = 2; - if (frame->final) { - frame->head[0] |= 0x80; // final frame bit - } - if (frame->len < 126) { - frame->head[1] = frame->len & 0x7f; - } else if (frame->len < 65536) { - frame->head[1] = 126; - NNI_PUT16(frame->head + 2, (frame->len & 0xffff)); - frame->hlen += 2; - } else { - frame->head[1] = 127; - NNI_PUT64(frame->head + 2, (uint64_t) frame->len); - frame->hlen += 8; - } + } + buf = frame->buf; - if (ws->server) { - frame->masked = false; - } else { - ws_mask_frame(frame); + // Now copy the data into the frame. + len = frame->len; + while (len != 0) { + size_t n = len; + if (n > iov->iov_len) { + n = iov->iov_len; } + memcpy(buf, iov->iov_buf, n); + iov++; + len -= n; + buf += n; + } - } while (len); - - wm->aio = aio; - wm->ws = ws; - *wmp = wm; - return (0); -} + if (nni_aio_count(aio) == 0) { + // This is the first frame. + frame->op = WS_BINARY; + } else { + frame->op = WS_CONT; + } -static int -ws_msg_init_rx(ws_msg **wmp, nni_ws *ws, nni_aio *aio) -{ - ws_msg *wm; + // Populate the frame header. + frame->head[0] = frame->op; + frame->hlen = 2; + if (frame->final) { + frame->head[0] |= 0x80; // final frame bit + } + if (frame->len < 126) { + frame->head[1] = frame->len & 0x7f; + } else if (frame->len < 65536) { + frame->head[1] = 126; + NNI_PUT16(frame->head + 2, (frame->len & 0xffff)); + frame->hlen += 2; + } else { + frame->head[1] = 127; + NNI_PUT64(frame->head + 2, (uint64_t) frame->len); + frame->hlen += 8; + } - if ((wm = NNI_ALLOC_STRUCT(wm)) == NULL) { - return (NNG_ENOMEM); + // If we are on the client, then we need to mask the frame. + frame->masked = false; + if (!ws->server) { + ws_mask_frame(frame); } - NNI_LIST_INIT(&wm->frames, ws_frame, node); - wm->aio = aio; - wm->ws = ws; - *wmp = wm; return (0); } static void ws_close_cb(void *arg) { - nni_ws * ws = arg; - ws_msg * wm; - nni_aio *aio; + nni_ws * ws = arg; + ws_frame *frame; nni_aio_close(ws->txaio); nni_aio_close(ws->rxaio); @@ -422,31 +516,13 @@ ws_close_cb(void *arg) nni_http_conn_close(ws->http); - // This list (receive) should be empty. - while ((wm = nni_list_first(&ws->rxmsgs)) != NULL) { - nni_list_remove(&ws->rxmsgs, wm); - if ((aio = wm->aio) != NULL) { - wm->aio = NULL; - nni_aio_list_remove(aio); - nni_aio_finish_error(aio, NNG_ECLOSED); - } - ws_msg_fini(wm); - } - - while ((wm = nni_list_first(&ws->txmsgs)) != NULL) { - nni_list_remove(&ws->txmsgs, wm); - if ((aio = wm->aio) != NULL) { - wm->aio = NULL; - nni_aio_list_remove(aio); - nni_aio_finish_error(aio, NNG_ECLOSED); + while ((frame = nni_list_first(&ws->txq)) != NULL) { + nni_list_remove(&ws->txq, frame); + if (frame->aio != NULL) { + nni_aio_list_remove(frame->aio); + nni_aio_finish_error(frame->aio, NNG_ECLOSED); } - ws_msg_fini(wm); - } - ws->txframe = NULL; - - if (ws->rxframe != NULL) { - ws_frame_fini(ws->rxframe); - ws->rxframe = NULL; + ws_frame_fini(frame); } // Any txframe should have been killed with its wmsg. @@ -456,19 +532,13 @@ ws_close_cb(void *arg) static void ws_close(nni_ws *ws, uint16_t code) { - ws_msg *wm; + nng_aio *aio; // Receive stuff gets aborted always. No further receives // once we get a close. - while ((wm = nni_list_first(&ws->rxmsgs)) != NULL) { - nni_aio *aio; - nni_list_remove(&ws->rxmsgs, wm); - if ((aio = wm->aio) != NULL) { - wm->aio = NULL; - nni_aio_list_remove(aio); - nni_aio_finish_error(aio, NNG_ECLOSED); - } - ws_msg_fini(wm); + while ((aio = nni_list_first(&ws->recvq)) != NULL) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); } // If were closing "gracefully", then don't abort in-flight @@ -487,7 +557,6 @@ static void ws_start_write(nni_ws *ws) { ws_frame *frame; - ws_msg * wm; nni_iov iov[2]; int niov; @@ -495,13 +564,11 @@ ws_start_write(nni_ws *ws) return; // busy } - if ((wm = nni_list_first(&ws->txmsgs)) == NULL) { - // Nothing to send. - return; + if ((frame = nni_list_first(&ws->txq)) == NULL) { + return; // nothing to send } - - frame = nni_list_first(&wm->frames); NNI_ASSERT(frame != NULL); + nni_list_remove(&ws->txq, frame); // Push it out. ws->txframe = frame; @@ -534,7 +601,6 @@ ws_write_cb(void *arg) { nni_ws * ws = arg; ws_frame *frame; - ws_msg * wm; nni_aio * aio; int rv; @@ -545,17 +611,20 @@ ws_write_cb(void *arg) return; } ws->txframe = NULL; + if (frame->op == WS_CLOSE) { // If this was a close frame, we are done. // No other messages may succeed.. - while ((wm = nni_list_first(&ws->txmsgs)) != NULL) { - nni_list_remove(&ws->txmsgs, wm); - if ((aio = wm->aio) != NULL) { - wm->aio = NULL; + ws->txframe = NULL; + ws_frame_fini(frame); + while ((frame = nni_list_first(&ws->txq)) != NULL) { + nni_list_remove(&ws->txq, frame); + if ((aio = frame->aio) != NULL) { + frame->aio = NULL; nni_aio_list_remove(aio); nni_aio_finish_error(aio, NNG_ECLOSED); + ws_frame_fini(frame); } - ws_msg_fini(wm); } if (ws->wclose) { ws->wclose = false; @@ -565,54 +634,55 @@ ws_write_cb(void *arg) return; } - wm = frame->wmsg; - aio = wm->aio; - + aio = frame->aio; if ((rv = nni_aio_result(ws->txaio)) != 0) { - - nni_list_remove(&ws->txmsgs, wm); - ws_msg_fini(wm); + frame->aio = NULL; if (aio != NULL) { - wm->aio = NULL; nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); } - + ws_frame_fini(frame); ws->closed = true; nni_http_conn_close(ws->http); nni_mtx_unlock(&ws->mtx); return; } - // good frame, was it the last - nni_list_remove(&wm->frames, frame); - ws_frame_fini(frame); - - // if we still have more frames to transmit, then schedule it. - if (!nni_list_empty(&wm->frames)) { - ws_start_write(ws); - nni_mtx_unlock(&ws->mtx); - return; + if (aio != NULL) { + nni_aio_iov_advance(aio, frame->len); + nni_aio_bump_count(aio, frame->len); + if (frame->final) { + frame->aio = NULL; + nni_aio_list_remove(aio); + } else { + // Clear the aio so that we won't attempt to finish + // it outside the lock + aio = NULL; + } } - if (aio != NULL) { - wm->aio = NULL; - nni_aio_list_remove(aio); + if (frame->final) { + ws_frame_fini(frame); + } else { + // This one cannot fail here, since we only do allocation + // at initial scheduling. + ws_frame_prep_tx(ws, frame); + // Schedule at end. This permits other frames to interleave. + nni_list_append(&ws->txq, frame); } - nni_list_remove(&ws->txmsgs, wm); - // Write the next frame. ws_start_write(ws); nni_mtx_unlock(&ws->mtx); - // discard while not holding lock (just deallocations) - ws_msg_fini(wm); - + // We attempt to finish the operation synchronously, outside the lock. if (aio != NULL) { - nng_msg *msg = nni_aio_get_msg(aio); - nni_aio_set_msg(aio, NULL); - nni_aio_finish_synch(aio, 0, nni_msg_len(msg)); - nni_msg_free(msg); + nni_msg *msg; + // Successful send, don't leak the message! + if ((msg = nni_aio_get_msg(aio)) != NULL) { + nni_aio_set_msg(aio, NULL); + nni_msg_free(msg); + } + nni_aio_finish_synch(aio, 0, nni_aio_count(aio)); } } @@ -620,7 +690,6 @@ static void ws_write_cancel(nni_aio *aio, void *arg, int rv) { nni_ws * ws = arg; - ws_msg * wm; ws_frame *frame; // Is this aio active? We can tell by looking at the active tx frame. @@ -630,17 +699,17 @@ ws_write_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&ws->mtx); return; } - wm = nni_aio_get_prov_extra(aio, 0); - if (((frame = ws->txframe) != NULL) && (frame->wmsg == wm)) { + frame = nni_aio_get_prov_extra(aio, 0); + if (frame == ws->txframe) { nni_aio_abort(ws->txaio, rv); // We will wait for callback on the txaio to finish aio. - } else if (nni_list_active(&ws->txmsgs, wm)) { + } else { // If scheduled, just need to remove node and complete it. - nni_list_remove(&ws->txmsgs, wm); - wm->aio = NULL; + nni_list_remove(&ws->txq, frame); + frame->aio = NULL; nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); - ws_msg_fini(wm); + ws_frame_fini(frame); } nni_mtx_unlock(&ws->mtx); } @@ -648,10 +717,10 @@ ws_write_cancel(nni_aio *aio, void *arg, int rv) static void ws_send_close(nni_ws *ws, uint16_t code) { - ws_msg * wm; - uint8_t buf[sizeof(uint16_t)]; - int rv; - nni_aio *aio; + ws_frame *frame; + uint8_t buf[sizeof(uint16_t)]; + int rv; + nni_aio * aio; NNI_PUT16(buf, code); @@ -665,125 +734,45 @@ ws_send_close(nni_ws *ws, uint16_t code) return; } ws->wclose = true; - rv = ws_msg_init_control(&wm, ws, WS_CLOSE, buf, sizeof(buf)); + rv = ws_msg_init_control(&frame, ws, WS_CLOSE, buf, sizeof(buf)); if (rv != 0) { ws->wclose = false; nni_aio_finish_error(aio, rv); return; } - // Close frames get priority! if ((rv = nni_aio_schedule(aio, ws_cancel_close, ws)) != 0) { ws->wclose = false; nni_aio_finish_error(aio, rv); + ws_frame_fini(frame); return; } - nni_list_prepend(&ws->txmsgs, wm); + // This gets inserted at the head. + nni_list_prepend(&ws->txq, frame); ws_start_write(ws); } static void ws_send_control(nni_ws *ws, uint8_t op, uint8_t *buf, size_t len) { - ws_msg *wm; + ws_frame *frame; // Note that we do not care if this works or not. So no AIO needed. if ((ws->closed) || - (ws_msg_init_control(&wm, ws, op, buf, len) != 0)) { + (ws_msg_init_control(&frame, ws, op, buf, len) != 0)) { return; } // Control frames at head of list. (Note that this may preempt // the close frame or other ping/pong requests. Oh well.) - nni_list_prepend(&ws->txmsgs, wm); + nni_list_prepend(&ws->txq, frame); ws_start_write(ws); } -static const nni_option ws_options[] = { - { - .o_name = NULL, - }, -}; - -int -nni_ws_getopt(nni_ws *ws, const char *name, void *buf, size_t *szp, nni_type t) -{ - int rv; - - nni_mtx_lock(&ws->mtx); - if (ws->closed) { - nni_mtx_unlock(&ws->mtx); - return (NNG_ECLOSED); - } - rv = nni_http_conn_getopt(ws->http, name, buf, szp, t); - if (rv == NNG_ENOTSUP) { - rv = nni_getopt(ws_options, name, ws, buf, szp, t); - } - nni_mtx_unlock(&ws->mtx); - return (rv); -} - -int -nni_ws_setopt( - nni_ws *ws, const char *name, const void *buf, size_t sz, nni_type t) -{ - int rv; - - nni_mtx_lock(&ws->mtx); - if (ws->closed) { - nni_mtx_unlock(&ws->mtx); - return (NNG_ECLOSED); - } - rv = nni_http_conn_setopt(ws->http, name, buf, sz, t); - if (rv == NNG_ENOTSUP) { - rv = nni_setopt(ws_options, name, ws, buf, sz, t); - } - nni_mtx_unlock(&ws->mtx); - return (rv); -} - -void -nni_ws_send_msg(nni_ws *ws, nni_aio *aio) -{ - ws_msg * wm; - nni_msg *msg; - int rv; - - msg = nni_aio_get_msg(aio); - - if (nni_aio_begin(aio) != 0) { - return; - } - if ((rv = ws_msg_init_tx(&wm, ws, msg, aio)) != 0) { - nni_aio_finish_error(aio, rv); - return; - } - - nni_mtx_lock(&ws->mtx); - if (ws->closed) { - nni_mtx_unlock(&ws->mtx); - nni_aio_finish_error(aio, NNG_ECLOSED); - ws_msg_fini(wm); - return; - } - if ((rv = nni_aio_schedule(aio, ws_write_cancel, ws)) != 0) { - nni_mtx_unlock(&ws->mtx); - nni_aio_finish_error(aio, rv); - ws_msg_fini(wm); - return; - } - nni_aio_set_prov_extra(aio, 0, wm); - nni_list_append(&ws->sendq, aio); - nni_list_append(&ws->txmsgs, wm); - ws_start_write(ws); - nni_mtx_unlock(&ws->mtx); -} - static void ws_start_read(nni_ws *ws) { ws_frame *frame; - ws_msg * wm; nni_aio * aio; nni_iov iov; @@ -791,18 +780,18 @@ ws_start_read(nni_ws *ws) return; // already reading or closed } - if ((wm = nni_list_first(&ws->rxmsgs)) == NULL) { - return; // no body expecting a message. + // If nobody is waiting for recv, and we already have a data + // frame, stop reading. This keeps us from buffering infinitely. + if (nni_list_empty(&ws->recvq) && !nni_list_empty(&ws->rxq)) { + return; } if ((frame = NNI_ALLOC_STRUCT(frame)) == NULL) { - nni_list_remove(&ws->rxmsgs, wm); - if ((aio = wm->aio) != NULL) { - wm->aio = NULL; + if ((aio = nni_list_first(&ws->recvq)) != NULL) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, NNG_ENOMEM); } - ws_msg_fini(wm); + ws_close(ws, WS_CLOSE_INTERNAL); return; } @@ -820,34 +809,143 @@ ws_start_read(nni_ws *ws) } static void -ws_read_frame_cb(nni_ws *ws, ws_frame *frame, ws_msg **wmp, nni_aio **aiop) +ws_read_finish_str(nni_ws *ws) { - ws_msg *wm = nni_list_first(&ws->rxmsgs); + for (;;) { + nni_aio * aio; + nni_iov * iov; + unsigned niov; + ws_frame *frame; - switch (frame->op) { - case WS_CONT: - if (wm == NULL) { - ws_close(ws, WS_CLOSE_GOING_AWAY); + if ((aio = nni_list_first(&ws->recvq)) == NULL) { return; } - if (nni_list_empty(&wm->frames)) { + + if ((frame = nni_list_first(&ws->rxq)) == NULL) { + return; + } + + // Discard 0 length frames -- in stream mode they are not used. + if (frame->len == 0) { + nni_list_remove(&ws->rxq, frame); + ws_frame_fini(frame); + continue; + } + + // We are completing this aio one way or the other. + nni_aio_list_remove(aio); + nni_aio_get_iov(aio, &niov, &iov); + + while ((frame != NULL) && (niov != 0)) { + size_t n; + + if ((n = frame->len) > iov->iov_len) { + // This eats the entire iov. + n = iov->iov_len; + } + iov->iov_buf = ((uint8_t *) iov->iov_buf) + n; + iov->iov_len -= n; + if (iov->iov_len == 0) { + iov++; + niov--; + } + + if (frame->len == n) { + nni_list_remove(&ws->rxq, frame); + ws_frame_fini(frame); + frame = nni_list_first(&ws->rxq); + } else { + frame->len -= n; + frame->buf += n; + } + + nni_aio_bump_count(aio, n); + } + + nni_aio_finish(aio, 0, nni_aio_count(aio)); + } +} + +static void +ws_read_finish_msg(nni_ws *ws) +{ + nni_aio * aio; + size_t len; + ws_frame *frame; + nni_msg * msg; + int rv; + uint8_t * body; + + // If we have no data, no waiter, or have not received the complete + // message yet, then there is nothing to do. + if (ws->inmsg || nni_list_empty(&ws->rxq) || + ((aio = nni_list_first(&ws->recvq)) == NULL)) { + return; + } + + // At this point, we have both a complete message in the queue (and + // there should not be any frames other than the for the message), + // and a waiting reader. + len = 0; + NNI_LIST_FOREACH (&ws->rxq, frame) { + len += frame->len; + } + + nni_aio_list_remove(aio); + + if ((rv = nni_msg_alloc(&msg, len)) != 0) { + nni_aio_finish_error(aio, rv); + ws_close_error(ws, WS_CLOSE_INTERNAL); + return; + } + body = nni_msg_body(msg); + while ((frame = nni_list_first(&ws->rxq)) != NULL) { + nni_list_remove(&ws->rxq, frame); + memcpy(body, frame->buf, frame->len); + body += frame->len; + ws_frame_fini(frame); + } + + nni_aio_set_msg(aio, msg); + nni_aio_bump_count(aio, nni_msg_len(msg)); + nni_aio_finish(aio, 0, nni_msg_len(msg)); +} + +static void +ws_read_finish(nni_ws *ws) +{ + if (ws->isstream) { + ws_read_finish_str(ws); + } else { + ws_read_finish_msg(ws); + } +} + +static void +ws_read_frame_cb(nni_ws *ws, ws_frame *frame) +{ + switch (frame->op) { + case WS_CONT: + if (!ws->inmsg) { ws_close(ws, WS_CLOSE_PROTOCOL_ERR); return; } + if (frame->final) { + ws->inmsg = false; + } ws->rxframe = NULL; - nni_list_append(&wm->frames, frame); + nni_list_append(&ws->rxq, frame); break; case WS_BINARY: - if (wm == NULL) { - ws_close(ws, WS_CLOSE_GOING_AWAY); - return; - } - if (!nni_list_empty(&wm->frames)) { + if (ws->inmsg) { ws_close(ws, WS_CLOSE_PROTOCOL_ERR); return; } + if (!frame->final) { + ws->inmsg = true; + } ws->rxframe = NULL; - nni_list_append(&wm->frames, frame); + nni_list_append(&ws->rxq, frame); break; case WS_TEXT: // No support for text mode at present. @@ -880,23 +978,13 @@ ws_read_frame_cb(nni_ws *ws, ws_frame *frame, ws_msg **wmp, nni_aio **aiop) return; } - // If this was the last (final) frame, then complete it. But - // we have to look at the msg, since we might have got a - // control frame. - if (((frame = nni_list_last(&wm->frames)) != NULL) && frame->final) { - nni_list_remove(&ws->rxmsgs, wm); - *wmp = wm; - *aiop = wm->aio; - nni_aio_list_remove(wm->aio); - wm->aio = NULL; - } + ws_read_finish(ws); } static void ws_read_cb(void *arg) { - nni_ws * ws = arg; - ws_msg * wm; + nni_ws * ws = arg; nni_aio * aio = ws->rxaio; ws_frame *frame; int rv; @@ -976,6 +1064,21 @@ ws_read_cb(void *arg) nni_mtx_unlock(&ws->mtx); return; } + // For message mode, also check to make sure that the overall + // length of the message has not exceeded our recvmax. + // (Protect against an infinite stream of small messages!) + if ((!ws->isstream) && (ws->recvmax > 0)) { + size_t totlen = frame->len; + ws_frame *fr2; + NNI_LIST_FOREACH (&ws->rxq, fr2) { + totlen += fr2->len; + } + if (totlen > ws->recvmax) { + ws_close(ws, WS_CLOSE_TOO_BIG); + nni_mtx_unlock(&ws->mtx); + return; + } + } // Check for masking. (We don't actually do the unmask // here, because we don't have data yet.) @@ -1023,134 +1126,40 @@ ws_read_cb(void *arg) // At this point, we have a complete frame. ws_unmask_frame(frame); // idempotent - wm = NULL; - aio = NULL; - ws_read_frame_cb(ws, frame, &wm, &aio); + ws_read_frame_cb(ws, frame); ws_start_read(ws); nni_mtx_unlock(&ws->mtx); - - // Got a good message, so we have to do the work to send it up. - if (wm != NULL) { - size_t len = 0; - nni_msg *msg; - uint8_t *body; - int rv; - - NNI_LIST_FOREACH (&wm->frames, frame) { - len += frame->len; - } - if ((rv = nni_msg_alloc(&msg, len)) != 0) { - nni_aio_finish_error(aio, rv); - ws_msg_fini(wm); - nni_ws_close_error(ws, WS_CLOSE_INTERNAL); - return; - } - body = nni_msg_body(msg); - NNI_LIST_FOREACH (&wm->frames, frame) { - memcpy(body, frame->buf, frame->len); - body += frame->len; - } - nni_aio_set_msg(aio, msg); - nni_aio_finish_synch(aio, 0, nni_msg_len(msg)); - ws_msg_fini(wm); - } } static void ws_read_cancel(nni_aio *aio, void *arg, int rv) { nni_ws *ws = arg; - ws_msg *wm; nni_mtx_lock(&ws->mtx); - if (!nni_aio_list_active(aio)) { - nni_mtx_unlock(&ws->mtx); - return; - } - wm = nni_aio_get_prov_extra(aio, 0); - if (wm == nni_list_first(&ws->rxmsgs)) { - // Cancellation will percolate back up. - nni_aio_abort(ws->rxaio, rv); - } else if (nni_list_active(&ws->rxmsgs, wm)) { - nni_list_remove(&ws->rxmsgs, wm); - ws_msg_fini(wm); + if (nni_aio_list_active(aio)) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); } nni_mtx_unlock(&ws->mtx); } -void -nni_ws_recv_msg(nni_ws *ws, nni_aio *aio) -{ - ws_msg *wm; - int rv; - - if (nni_aio_begin(aio) != 0) { - return; - } - if ((rv = ws_msg_init_rx(&wm, ws, aio)) != 0) { - nni_aio_finish_error(aio, rv); - return; - } - nni_mtx_lock(&ws->mtx); - if ((rv = nni_aio_schedule(aio, ws_read_cancel, ws)) != 0) { - nni_mtx_unlock(&ws->mtx); - ws_msg_fini(wm); - nni_aio_finish_error(aio, rv); - return; - } - nni_aio_set_prov_extra(aio, 0, wm); - nni_list_append(&ws->recvq, aio); - nni_list_append(&ws->rxmsgs, wm); - ws_start_read(ws); - - nni_mtx_unlock(&ws->mtx); -} - -void -nni_ws_close_error(nni_ws *ws, uint16_t code) +static void +ws_close_error(nni_ws *ws, uint16_t code) { nni_mtx_lock(&ws->mtx); ws_close(ws, code); nni_mtx_unlock(&ws->mtx); } -void -nni_ws_close(nni_ws *ws) -{ - nni_ws_close_error(ws, WS_CLOSE_NORMAL_CLOSE); -} - -const char * -nni_ws_request_headers(nni_ws *ws) -{ - nni_mtx_lock(&ws->mtx); - if (ws->reqhdrs == NULL) { - ws->reqhdrs = nni_http_req_headers(ws->req); - } - nni_mtx_unlock(&ws->mtx); - return (ws->reqhdrs); -} - -const char * -nni_ws_response_headers(nni_ws *ws) -{ - nni_mtx_lock(&ws->mtx); - if (ws->reshdrs == NULL) { - ws->reshdrs = nni_http_res_headers(ws->res); - } - nni_mtx_unlock(&ws->mtx); - return (ws->reshdrs); -} - static void ws_fini(void *arg) { - nni_ws *ws = arg; - ws_msg *wm; + nni_ws * ws = arg; + ws_frame *frame; + nng_aio * aio; - nni_ws_close(ws); + ws_close_error(ws, WS_CLOSE_NORMAL_CLOSE); // Give a chance for the close frame to drain. if (ws->closeaio) { @@ -1175,25 +1184,29 @@ ws_fini(void *arg) } nni_mtx_lock(&ws->mtx); - while ((wm = nni_list_first(&ws->rxmsgs)) != NULL) { - nni_list_remove(&ws->rxmsgs, wm); - if (wm->aio) { - nni_aio_finish_error(wm->aio, NNG_ECLOSED); - } - ws_msg_fini(wm); + while ((frame = nni_list_first(&ws->rxq)) != NULL) { + nni_list_remove(&ws->rxq, frame); + ws_frame_fini(frame); } - while ((wm = nni_list_first(&ws->txmsgs)) != NULL) { - nni_list_remove(&ws->txmsgs, wm); - if (wm->aio) { - nni_aio_finish_error(wm->aio, NNG_ECLOSED); - } - ws_msg_fini(wm); + while ((frame = nni_list_first(&ws->txq)) != NULL) { + nni_list_remove(&ws->txq, frame); + ws_frame_fini(frame); } - if (ws->rxframe) { + if (ws->rxframe != NULL) { ws_frame_fini(ws->rxframe); } + if (ws->txframe != NULL) { + ws_frame_fini(ws->txframe); + } + + while (((aio = nni_list_first(&ws->recvq)) != NULL) || + ((aio = nni_list_first(&ws->sendq)) != NULL)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_ECLOSED); + } + nni_mtx_unlock(&ws->mtx); if (ws->http) { @@ -1217,8 +1230,8 @@ ws_fini(void *arg) NNI_FREE_STRUCT(ws); } -void -nni_ws_fini(nni_ws *ws) +static void +ws_reap(nni_ws *ws) { nni_reap(&ws->reap, ws_fini, ws); } @@ -1232,8 +1245,8 @@ ws_http_cb_listener(nni_ws *ws, nni_aio *aio) nni_mtx_lock(&l->mtx); nni_list_remove(&l->reply, ws); if (nni_aio_result(aio) != 0) { - nni_ws_fini(ws); nni_mtx_unlock(&l->mtx); + ws_reap(ws); return; } ws->ready = true; @@ -1320,14 +1333,14 @@ ws_http_cb_dialer(nni_ws *ws, nni_aio *aio) (!ws_contains_word(ptr, "upgrade")) || ((ptr = GETH("Upgrade")) == NULL) || (strcmp(ptr, "websocket") != 0)) { - nni_ws_close_error(ws, WS_CLOSE_PROTOCOL_ERR); + ws_close_error(ws, WS_CLOSE_PROTOCOL_ERR); rv = NNG_EPROTO; goto err; } if (d->proto != NULL) { if (((ptr = GETH("Sec-WebSocket-Protocol")) == NULL) || (!ws_contains_word(d->proto, ptr))) { - nni_ws_close_error(ws, WS_CLOSE_PROTOCOL_ERR); + ws_close_error(ws, WS_CLOSE_PROTOCOL_ERR); rv = NNG_EPROTO; goto err; } @@ -1358,7 +1371,7 @@ err: } nni_mtx_unlock(&d->mtx); - nni_ws_fini(ws); + ws_reap(ws); } static void @@ -1384,8 +1397,8 @@ ws_init(nni_ws **wsp) return (NNG_ENOMEM); } nni_mtx_init(&ws->mtx); - NNI_LIST_INIT(&ws->rxmsgs, ws_msg, node); - NNI_LIST_INIT(&ws->txmsgs, ws_msg, node); + NNI_LIST_INIT(&ws->rxq, ws_frame, node); + NNI_LIST_INIT(&ws->txq, ws_frame, node); nni_aio_list_init(&ws->sendq); nni_aio_list_init(&ws->recvq); @@ -1401,17 +1414,25 @@ ws_init(nni_ws **wsp) nni_aio_set_timeout(ws->closeaio, 100); nni_aio_set_timeout(ws->httpaio, 2000); + ws->ops.s_close = ws_str_close; + ws->ops.s_free = ws_str_free; + ws->ops.s_send = ws_str_send; + ws->ops.s_recv = ws_str_recv; + ws->ops.s_getx = ws_str_getx; + ws->ops.s_setx = ws_str_setx; + ws->fragsize = 1 << 20; // we won't send a frame larger than this *wsp = ws; return (0); } -void -nni_ws_listener_fini(nni_ws_listener *l) +static void +ws_listener_free(void *arg) { - ws_header *hdr; + nni_ws_listener *l = arg; + ws_header * hdr; - nni_ws_listener_close(l); + ws_listener_close(l); nni_mtx_lock(&l->mtx); while (!nni_list_empty(&l->reply)) { @@ -1437,7 +1458,7 @@ nni_ws_listener_fini(nni_ws_listener *l) NNI_FREE_STRUCT(hdr); } if (l->url) { - nni_url_free(l->url); + nng_url_free(l->url); } NNI_FREE_STRUCT(l); } @@ -1456,6 +1477,7 @@ ws_handler(nni_aio *aio) uint16_t status; int rv; char key[29]; + ws_header * hdr; req = nni_aio_get_input(aio, 0); h = nni_aio_get_input(aio, 1); @@ -1542,6 +1564,20 @@ ws_handler(nni_aio *aio) goto err; } + // Set any user supplied headers. This is better than using a hook + // for most things, because it is loads easier. + NNI_LIST_FOREACH (&l->headers, hdr) { + if (SETH(hdr->name, hdr->value) != 0) { + status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR; + nni_http_res_free(res); + goto err; + } + } + + // The hook function gives us the ability to intercept the HTTP + // response altogether. Its best not to do this unless you really + // need to, because it's much more complex. But if you want to set + // up an HTTP Authorization handler this might be the only choice. if (l->hookfn != NULL) { rv = l->hookfn(l->hookarg, req, res); if (rv != 0) { @@ -1583,8 +1619,9 @@ ws_handler(nni_aio *aio) ws->res = res; ws->server = true; ws->maxframe = l->maxframe; - - // XXX: Inherit fragmentation? (Frag is limited for now). + ws->fragsize = l->fragsize; + ws->recvmax = l->recvmax; + ws->isstream = l->isstream; nni_list_append(&l->reply, ws); nni_aio_set_data(ws->httpaio, 0, l); @@ -1603,71 +1640,6 @@ err: } } -int -nni_ws_listener_init(nni_ws_listener **wslp, nni_url *url) -{ - nni_ws_listener *l; - int rv; - char * host; - - if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { - return (NNG_ENOMEM); - } - nni_mtx_init(&l->mtx); - nni_cv_init(&l->cv, &l->mtx); - nni_aio_list_init(&l->aios); - - NNI_LIST_INIT(&l->pend, nni_ws, node); - NNI_LIST_INIT(&l->reply, nni_ws, node); - - // make a private copy of the url structure. - if ((rv = nni_url_clone(&l->url, url)) != 0) { - nni_ws_listener_fini(l); - return (rv); - } - - host = l->url->u_hostname; - if (strlen(host) == 0) { - host = NULL; - } - rv = nni_http_handler_init(&l->handler, url->u_path, ws_handler); - if (rv != 0) { - nni_ws_listener_fini(l); - return (rv); - } - - if (((rv = nni_http_handler_set_host(l->handler, host)) != 0) || - ((rv = nni_http_handler_set_data(l->handler, l, 0)) != 0) || - ((rv = nni_http_server_init(&l->server, url)) != 0)) { - nni_ws_listener_fini(l); - return (rv); - } - - l->maxframe = 0; - *wslp = l; - return (0); -} - -int -nni_ws_listener_proto(nni_ws_listener *l, const char *proto) -{ - int rv = 0; - char *ns; - nni_mtx_lock(&l->mtx); - if (l->started) { - rv = NNG_EBUSY; - } else if ((ns = nni_strdup(proto)) == NULL) { - rv = NNG_ENOMEM; - } else { - if (l->proto != NULL) { - nni_strfree(l->proto); - } - l->proto = ns; - } - nni_mtx_unlock(&l->mtx); - return (rv); -} - static void ws_accept_cancel(nni_aio *aio, void *arg, int rv) { @@ -1681,11 +1653,12 @@ ws_accept_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&l->mtx); } -void -nni_ws_listener_accept(nni_ws_listener *l, nni_aio *aio) +static void +ws_listener_accept(void *arg, nni_aio *aio) { - nni_ws *ws; - int rv; + nni_ws_listener *l = arg; + nni_ws * ws; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -1717,10 +1690,11 @@ nni_ws_listener_accept(nni_ws_listener *l, nni_aio *aio) nni_mtx_unlock(&l->mtx); } -void -nni_ws_listener_close(nni_ws_listener *l) +static void +ws_listener_close(void *arg) { - nni_ws *ws; + nni_ws_listener *l = arg; + nni_ws * ws; nni_mtx_lock(&l->mtx); if (l->closed) { nni_mtx_unlock(&l->mtx); @@ -1733,18 +1707,30 @@ nni_ws_listener_close(nni_ws_listener *l) l->started = false; } NNI_LIST_FOREACH (&l->pend, ws) { - nni_ws_close_error(ws, WS_CLOSE_GOING_AWAY); + ws_close_error(ws, WS_CLOSE_GOING_AWAY); } NNI_LIST_FOREACH (&l->reply, ws) { - nni_ws_close_error(ws, WS_CLOSE_GOING_AWAY); + ws_close_error(ws, WS_CLOSE_GOING_AWAY); } nni_mtx_unlock(&l->mtx); } -int -nni_ws_listener_listen(nni_ws_listener *l) +// XXX: Consider replacing this with an option. +void +nni_ws_listener_hook( + nni_ws_listener *l, nni_ws_listen_hook hookfn, void *hookarg) { - int rv; + nni_mtx_lock(&l->mtx); + l->hookfn = hookfn; + l->hookarg = hookarg; + nni_mtx_unlock(&l->mtx); +} + +static int +ws_listener_listen(void *arg) +{ + nni_ws_listener *l = arg; + int rv; nni_mtx_lock(&l->mtx); if (l->closed) { @@ -1777,42 +1763,290 @@ nni_ws_listener_listen(nni_ws_listener *l) return (0); } -void -nni_ws_listener_hook( - nni_ws_listener *l, nni_ws_listen_hook hookfn, void *hookarg) +static int +ws_listener_set_size( + nni_ws_listener *l, size_t *valp, const void *buf, size_t sz, nni_type t) { - nni_mtx_lock(&l->mtx); - l->hookfn = hookfn; - l->hookarg = hookarg; - nni_mtx_unlock(&l->mtx); + size_t val; + int rv; + + // Max size is limited to 4 GB, but you really never want to have + // to have a larger value. If you think you need that, you're doing it + // wrong. You *can* set the size to 0 for unlimited. + if ((rv = nni_copyin_size(&val, buf, sz, 0, NNI_MAXSZ, t)) == 0) { + nni_mtx_lock(&l->mtx); + *valp = val; + nni_mtx_unlock(&l->mtx); + } + return (rv); } -int -nni_ws_listener_set_tls(nni_ws_listener *l, nng_tls_config *tls) +static int +ws_listener_get_size( + nni_ws_listener *l, size_t *valp, void *buf, size_t *szp, nni_type t) { - int rv; + size_t val; nni_mtx_lock(&l->mtx); - rv = nni_http_server_set_tls(l->server, tls); + val = *valp; nni_mtx_unlock(&l->mtx); + return (nni_copyout_size(val, buf, szp, t)); +} + +static int +ws_listener_set_maxframe(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_listener *l = arg; + return (ws_listener_set_size(l, &l->maxframe, buf, sz, t)); +} + +static int +ws_listener_get_maxframe(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_listener *l = arg; + return (ws_listener_get_size(l, &l->maxframe, buf, szp, t)); +} + +static int +ws_listener_set_fragsize(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_listener *l = arg; + return (ws_listener_set_size(l, &l->fragsize, buf, sz, t)); +} + +static int +ws_listener_get_fragsize(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_listener *l = arg; + return (ws_listener_get_size(l, &l->fragsize, buf, szp, t)); +} + +static int +ws_listener_set_recvmax(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_listener *l = arg; + return (ws_listener_set_size(l, &l->recvmax, buf, sz, t)); +} + +static int +ws_listener_get_recvmax(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_listener *l = arg; + return (ws_listener_get_size(l, &l->recvmax, buf, szp, t)); +} + +static int +ws_listener_set_res_headers(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_listener *l = arg; + int rv; + + if ((rv = ws_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&l->mtx); + rv = ws_set_headers(&l->headers, buf); + nni_mtx_unlock(&l->mtx); + } return (rv); } -int -nni_ws_listener_get_tls(nni_ws_listener *l, nng_tls_config **tlsp) +static int +ws_listener_set_proto(void *arg, const void *buf, size_t sz, nni_type t) { - int rv; + nni_ws_listener *l = arg; + int rv; + + if ((rv = ws_check_string(buf, sz, t)) == 0) { + char *ns; + if ((ns = nni_strdup(buf)) == NULL) { + rv = NNG_ENOMEM; + } else { + nni_mtx_lock(&l->mtx); + if (l->proto != NULL) { + nni_strfree(l->proto); + } + l->proto = ns; + nni_mtx_unlock(&l->mtx); + } + } + return (rv); +} + +static int +ws_listener_get_proto(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_listener *l = arg; + int rv; nni_mtx_lock(&l->mtx); - rv = nni_http_server_get_tls(l->server, tlsp); + rv = nni_copyout_str(l->proto != NULL ? l->proto : "", buf, szp, t); nni_mtx_unlock(&l->mtx); return (rv); } -void -nni_ws_listener_set_maxframe(nni_ws_listener *l, size_t maxframe) +static int +ws_listener_set_msgmode(void *arg, const void *buf, size_t sz, nni_type t) { + nni_ws_listener *l = arg; + int rv; + bool b; + + if ((rv = nni_copyin_bool(&b, buf, sz, t)) == 0) { + nni_mtx_lock(&l->mtx); + l->isstream = !b; + nni_mtx_unlock(&l->mtx); + } + return (rv); +} + +static int +ws_listener_get_url(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_listener *l = arg; + int rv; + nni_mtx_lock(&l->mtx); - l->maxframe = maxframe; + rv = nni_copyout_str(l->url->u_rawurl, buf, szp, t); nni_mtx_unlock(&l->mtx); + return (rv); +} + +static const nni_option ws_listener_options[] = { + { + .o_name = NNI_OPT_WS_MSGMODE, + .o_set = ws_listener_set_msgmode, + }, + { + .o_name = NNG_OPT_WS_RECVMAXFRAME, + .o_set = ws_listener_set_maxframe, + .o_get = ws_listener_get_maxframe, + }, + { + .o_name = NNG_OPT_WS_SENDMAXFRAME, + .o_set = ws_listener_set_fragsize, + .o_get = ws_listener_get_fragsize, + }, + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_set = ws_listener_set_recvmax, + .o_get = ws_listener_get_recvmax, + }, + { + .o_name = NNG_OPT_WS_RESPONSE_HEADERS, + .o_set = ws_listener_set_res_headers, + // XXX: Get not implemented yet; likely of marginal value. + }, + { + .o_name = NNG_OPT_WS_PROTOCOL, + .o_set = ws_listener_set_proto, + .o_get = ws_listener_get_proto, + }, + { + .o_name = NNG_OPT_URL, + .o_get = ws_listener_get_url, + }, + { + .o_name = NULL, + }, +}; + +static int +ws_listener_set_header(nni_ws_listener *l, const char *name, const void *buf, + size_t sz, nni_type t) +{ + int rv; + name += strlen(NNG_OPT_WS_RESPONSE_HEADER); + if ((rv = ws_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&l->mtx); + rv = ws_set_header(&l->headers, name, buf); + nni_mtx_unlock(&l->mtx); + } + return (rv); +} + +static int +ws_listener_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) +{ + nni_ws_listener *l = arg; + int rv; + + rv = nni_setopt(ws_listener_options, name, l, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_http_server_setx(l->server, name, buf, sz, t); + } + + if (rv == NNG_ENOTSUP) { + if (startswith(name, NNG_OPT_WS_RESPONSE_HEADER)) { + rv = ws_listener_set_header(l, name, buf, sz, t); + } + } + return (rv); +} + +static int +ws_listener_getx( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) +{ + nni_ws_listener *l = arg; + int rv; + + rv = nni_getopt(ws_listener_options, name, l, buf, szp, t); + if (rv == NNG_ENOTSUP) { + rv = nni_http_server_getx(l->server, name, buf, szp, t); + } + return (rv); +} + +int +nni_ws_listener_alloc(nng_stream_listener **wslp, const nng_url *url) +{ + nni_ws_listener *l; + int rv; + char * host; + + if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { + return (NNG_ENOMEM); + } + nni_mtx_init(&l->mtx); + nni_cv_init(&l->cv, &l->mtx); + nni_aio_list_init(&l->aios); + + NNI_LIST_INIT(&l->pend, nni_ws, node); + NNI_LIST_INIT(&l->reply, nni_ws, node); + + // make a private copy of the url structure. + if ((rv = nng_url_clone(&l->url, url)) != 0) { + ws_listener_free(l); + return (rv); + } + + host = l->url->u_hostname; + if (strlen(host) == 0) { + host = NULL; + } + rv = nni_http_handler_init(&l->handler, url->u_path, ws_handler); + if (rv != 0) { + ws_listener_free(l); + return (rv); + } + + if (((rv = nni_http_handler_set_host(l->handler, host)) != 0) || + ((rv = nni_http_handler_set_data(l->handler, l, 0)) != 0) || + ((rv = nni_http_server_init(&l->server, url)) != 0)) { + ws_listener_free(l); + return (rv); + } + + l->fragsize = WS_DEF_MAXTXFRAME; + l->maxframe = WS_DEF_MAXRXFRAME; + l->recvmax = WS_DEF_RECVMAX; + l->isstream = false; + l->ops.sl_free = ws_listener_free; + l->ops.sl_close = ws_listener_close; + l->ops.sl_accept = ws_listener_accept; + l->ops.sl_listen = ws_listener_listen; + l->ops.sl_setx = ws_listener_setx; + l->ops.sl_getx = ws_listener_getx; + *wslp = (void *) l; + return (0); } void @@ -1846,7 +2080,7 @@ ws_conn_cb(void *arg) nni_cv_wake(&d->cv); } nni_mtx_unlock(&d->mtx); - nni_ws_fini(ws); + ws_reap(ws); } else { nni_mtx_unlock(&d->mtx); } @@ -1861,7 +2095,7 @@ ws_conn_cb(void *arg) // This request was canceled for some reason. nni_http_conn_fini(http); nni_mtx_unlock(&ws->mtx); - nni_ws_fini(ws); + ws_reap(ws); return; } @@ -1908,13 +2142,14 @@ err: if (req != NULL) { nni_http_req_free(req); } - nni_ws_fini(ws); + ws_reap(ws); } -void -nni_ws_dialer_fini(nni_ws_dialer *d) +static void +ws_dialer_free(void *arg) { - ws_header *hdr; + nni_ws_dialer *d = arg; + ws_header * hdr; nni_mtx_lock(&d->mtx); while (!nni_list_empty(&d->wspend)) { @@ -1933,65 +2168,18 @@ nni_ws_dialer_fini(nni_ws_dialer *d) nni_http_client_fini(d->client); } if (d->url) { - nni_url_free(d->url); + nng_url_free(d->url); } nni_cv_fini(&d->cv); nni_mtx_fini(&d->mtx); NNI_FREE_STRUCT(d); } -int -nni_ws_dialer_init(nni_ws_dialer **dp, nni_url *url) -{ - nni_ws_dialer *d; - int rv; - - if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { - return (NNG_ENOMEM); - } - NNI_LIST_INIT(&d->headers, ws_header, node); - NNI_LIST_INIT(&d->wspend, nni_ws, node); - nni_mtx_init(&d->mtx); - nni_cv_init(&d->cv, &d->mtx); - - if ((rv = nni_url_clone(&d->url, url)) != 0) { - nni_ws_dialer_fini(d); - return (rv); - } - - if ((rv = nni_http_client_init(&d->client, url)) != 0) { - nni_ws_dialer_fini(d); - return (rv); - } - d->maxframe = 0; - *dp = d; - return (0); -} - -int -nni_ws_dialer_set_tls(nni_ws_dialer *d, nng_tls_config *tls) -{ - int rv; - nni_mtx_lock(&d->mtx); - rv = nni_http_client_set_tls(d->client, tls); - nni_mtx_unlock(&d->mtx); - return (rv); -} - -int -nni_ws_dialer_get_tls(nni_ws_dialer *d, nng_tls_config **tlsp) -{ - int rv; - nni_mtx_lock(&d->mtx); - rv = nni_http_client_get_tls(d->client, tlsp); - nni_mtx_unlock(&d->mtx); - return (rv); -} - -void -nni_ws_dialer_close(nni_ws_dialer *d) +static void +ws_dialer_close(void *arg) { - nni_ws *ws; + nni_ws_dialer *d = arg; + nni_ws * ws; nni_mtx_lock(&d->mtx); if (d->closed) { nni_mtx_unlock(&d->mtx); @@ -2005,24 +2193,6 @@ nni_ws_dialer_close(nni_ws_dialer *d) nni_mtx_unlock(&d->mtx); } -int -nni_ws_dialer_proto(nni_ws_dialer *d, const char *proto) -{ - int rv = 0; - char *ns; - nni_mtx_lock(&d->mtx); - if ((ns = nni_strdup(proto)) == NULL) { - rv = NNG_ENOMEM; - } else { - if (d->proto != NULL) { - nni_strfree(d->proto); - } - d->proto = ns; - } - nni_mtx_unlock(&d->mtx); - return (rv); -} - static void ws_dial_cancel(nni_aio *aio, void *arg, int rv) { @@ -2038,11 +2208,12 @@ ws_dial_cancel(nni_aio *aio, void *arg, int rv) nni_mtx_unlock(&ws->mtx); } -void -nni_ws_dialer_dial(nni_ws_dialer *d, nni_aio *aio) +static void +ws_dialer_dial(void *arg, nni_aio *aio) { - nni_ws *ws; - int rv; + nni_ws_dialer *d = arg; + nni_ws * ws; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -2055,72 +2226,275 @@ nni_ws_dialer_dial(nni_ws_dialer *d, nni_aio *aio) if (d->closed) { nni_mtx_unlock(&d->mtx); nni_aio_finish_error(aio, NNG_ECLOSED); - ws_fini(ws); + ws_reap(ws); return; } if ((rv = nni_aio_schedule(aio, ws_dial_cancel, ws)) != 0) { nni_mtx_unlock(&d->mtx); nni_aio_finish_error(aio, rv); - ws_fini(ws); + ws_reap(ws); return; } ws->dialer = d; ws->useraio = aio; ws->server = false; ws->maxframe = d->maxframe; + ws->isstream = d->isstream; nni_list_append(&d->wspend, ws); nni_http_client_connect(d->client, ws->connaio); nni_mtx_unlock(&d->mtx); } static int -ws_set_header(nni_list *l, const char *n, const char *v) +ws_dialer_set_msgmode(void *arg, const void *buf, size_t sz, nni_type t) { - ws_header *hdr; - char * nv; + nni_ws_dialer *d = arg; + int rv; + bool b; - if ((nv = nni_strdup(v)) == NULL) { - return (NNG_ENOMEM); + if ((rv = nni_copyin_bool(&b, buf, sz, t)) == 0) { + nni_mtx_lock(&d->mtx); + d->isstream = !b; + nni_mtx_unlock(&d->mtx); } + return (rv); +} - NNI_LIST_FOREACH (l, hdr) { - if (nni_strcasecmp(hdr->name, n) == 0) { - nni_strfree(hdr->value); - hdr->value = nv; - return (0); - } - } +static int +ws_dialer_set_size( + nni_ws_dialer *d, size_t *valp, const void *buf, size_t sz, nni_type t) +{ + size_t val; + int rv; - if ((hdr = NNI_ALLOC_STRUCT(hdr)) == NULL) { - nni_strfree(nv); - return (NNG_ENOMEM); - } - if ((hdr->name = nni_strdup(n)) == NULL) { - nni_strfree(nv); - NNI_FREE_STRUCT(hdr); - return (NNG_ENOMEM); + // Max size is limited to 4 GB, but you really never want to have + // to have a larger value. If you think you need that, you're doing it + // wrong. You *can* set the size to 0 for unlimited. + if ((rv = nni_copyin_size(&val, buf, sz, 0, NNI_MAXSZ, t)) == 0) { + nni_mtx_lock(&d->mtx); + *valp = val; + nni_mtx_unlock(&d->mtx); } - hdr->value = nv; - nni_list_append(l, hdr); - return (0); + return (rv); } -int -nni_ws_dialer_header(nni_ws_dialer *d, const char *n, const char *v) +static int +ws_dialer_get_size( + nni_ws_dialer *d, size_t *valp, void *buf, size_t *szp, nni_type t) { - int rv; + size_t val; nni_mtx_lock(&d->mtx); - rv = ws_set_header(&d->headers, n, v); + val = *valp; nni_mtx_unlock(&d->mtx); + return (nni_copyout_size(val, buf, szp, t)); +} + +static int +ws_dialer_set_maxframe(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_dialer *d = arg; + return (ws_dialer_set_size(d, &d->maxframe, buf, sz, t)); +} + +static int +ws_dialer_get_maxframe(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_dialer *d = arg; + return (ws_dialer_get_size(d, &d->maxframe, buf, szp, t)); +} + +static int +ws_dialer_set_fragsize(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_dialer *d = arg; + return (ws_dialer_set_size(d, &d->fragsize, buf, sz, t)); +} + +static int +ws_dialer_get_fragsize(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_dialer *d = arg; + return (ws_dialer_get_size(d, &d->fragsize, buf, szp, t)); +} + +static int +ws_dialer_set_recvmax(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_dialer *d = arg; + return (ws_dialer_set_size(d, &d->recvmax, buf, sz, t)); +} + +static int +ws_dialer_get_recvmax(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws_dialer *d = arg; + return (ws_dialer_get_size(d, &d->recvmax, buf, szp, t)); +} + +static int +ws_dialer_set_req_headers(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_dialer *d = arg; + int rv; + + if ((rv = ws_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&d->mtx); + rv = ws_set_headers(&d->headers, buf); + nni_mtx_unlock(&d->mtx); + } return (rv); } -void -nni_ws_dialer_set_maxframe(nni_ws_dialer *d, size_t maxframe) +static int +ws_dialer_set_proto(void *arg, const void *buf, size_t sz, nni_type t) +{ + nni_ws_dialer *d = arg; + int rv; + + if ((rv = ws_check_string(buf, sz, t)) == 0) { + char *ns; + if ((ns = nni_strdup(buf)) == NULL) { + rv = NNG_ENOMEM; + } else { + nni_mtx_lock(&d->mtx); + if (d->proto != NULL) { + nni_strfree(d->proto); + } + d->proto = ns; + nni_mtx_unlock(&d->mtx); + } + } + return (rv); +} + +static int +ws_dialer_get_proto(void *arg, void *buf, size_t *szp, nni_type t) { + nni_ws_dialer *d = arg; + int rv; nni_mtx_lock(&d->mtx); - d->maxframe = maxframe; + rv = nni_copyout_str(d->proto != NULL ? d->proto : "", buf, szp, t); nni_mtx_unlock(&d->mtx); + return (rv); +} + +static const nni_option ws_dialer_options[] = { + { + .o_name = NNI_OPT_WS_MSGMODE, + .o_set = ws_dialer_set_msgmode, + }, + { + .o_name = NNG_OPT_WS_RECVMAXFRAME, + .o_set = ws_dialer_set_maxframe, + .o_get = ws_dialer_get_maxframe, + }, + { + .o_name = NNG_OPT_WS_SENDMAXFRAME, + .o_set = ws_dialer_set_fragsize, + .o_get = ws_dialer_get_fragsize, + }, + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_set = ws_dialer_set_recvmax, + .o_get = ws_dialer_get_recvmax, + }, + { + .o_name = NNG_OPT_WS_REQUEST_HEADERS, + .o_set = ws_dialer_set_req_headers, + // XXX: Get not implemented yet; likely of marginal value. + }, + { + .o_name = NNG_OPT_WS_PROTOCOL, + .o_set = ws_dialer_set_proto, + .o_get = ws_dialer_get_proto, + }, + { + .o_name = NULL, + }, +}; + +static int +ws_dialer_set_header( + nni_ws_dialer *d, const char *name, const void *buf, size_t sz, nni_type t) +{ + int rv; + name += strlen(NNG_OPT_WS_REQUEST_HEADER); + if ((rv = ws_check_string(buf, sz, t)) == 0) { + nni_mtx_lock(&d->mtx); + rv = ws_set_header(&d->headers, name, buf); + nni_mtx_unlock(&d->mtx); + } + return (rv); +} + +static int +ws_dialer_setx( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) +{ + nni_ws_dialer *d = arg; + int rv; + + rv = nni_setopt(ws_dialer_options, name, d, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_http_client_setx(d->client, name, buf, sz, t); + } + + if (rv == NNG_ENOTSUP) { + if (startswith(name, NNG_OPT_WS_REQUEST_HEADER)) { + rv = ws_dialer_set_header(d, name, buf, sz, t); + } + } + return (rv); +} + +static int +ws_dialer_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t) +{ + nni_ws_dialer *d = arg; + int rv; + + rv = nni_getopt(ws_dialer_options, name, d, buf, szp, t); + if (rv == NNG_ENOTSUP) { + rv = nni_http_client_getx(d->client, name, buf, szp, t); + } + return (rv); +} + +int +nni_ws_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) +{ + nni_ws_dialer *d; + int rv; + + if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { + return (NNG_ENOMEM); + } + NNI_LIST_INIT(&d->headers, ws_header, node); + NNI_LIST_INIT(&d->wspend, nni_ws, node); + nni_mtx_init(&d->mtx); + nni_cv_init(&d->cv, &d->mtx); + + if ((rv = nng_url_clone(&d->url, url)) != 0) { + ws_dialer_free(d); + return (rv); + } + + if ((rv = nni_http_client_init(&d->client, url)) != 0) { + ws_dialer_free(d); + return (rv); + } + d->isstream = true; + d->recvmax = WS_DEF_RECVMAX; + d->maxframe = WS_DEF_MAXRXFRAME; + d->fragsize = WS_DEF_MAXTXFRAME; + + d->ops.sd_free = ws_dialer_free; + d->ops.sd_close = ws_dialer_close; + d->ops.sd_dial = ws_dialer_dial; + d->ops.sd_setx = ws_dialer_setx; + d->ops.sd_getx = ws_dialer_getx; + *dp = (void *) d; + return (0); } // Dialer does not get a hook chance, as it can examine the request @@ -2130,3 +2504,293 @@ nni_ws_dialer_set_maxframe(nni_ws_dialer *d, size_t maxframe) // The implementation will send periodic PINGs, and respond with // PONGs. + +static void +ws_str_free(void *arg) +{ + nni_ws *ws = arg; + nni_reap(&ws->reap, ws_fini, ws); +} + +static void +ws_str_close(void *arg) +{ + nni_ws *ws = arg; + ws_close_error(ws, WS_CLOSE_NORMAL_CLOSE); +} + +static void +ws_str_send(void *arg, nni_aio *aio) +{ + nni_ws * ws = arg; + int rv; + ws_frame *frame; + + if (nni_aio_begin(aio) != 0) { + return; + } + + if (!ws->isstream) { + nni_msg *msg; + unsigned niov; + nni_iov iov[2]; + if ((msg = nni_aio_get_msg(aio)) == NULL) { + nni_aio_finish_error(aio, NNG_EINVAL); + return; + } + niov = 0; + if (nng_msg_header_len(msg) > 0) { + iov[niov].iov_len = nni_msg_header_len(msg); + iov[niov].iov_buf = nni_msg_header(msg); + niov++; + } + iov[niov].iov_len = nni_msg_len(msg); + iov[niov].iov_buf = nni_msg_body(msg); + niov++; + + // Scribble into the iov for now. + nni_aio_set_iov(aio, niov, iov); + } + + if ((frame = NNI_ALLOC_STRUCT(frame)) == NULL) { + nni_aio_finish_error(aio, NNG_ENOMEM); + return; + } + frame->aio = aio; + if ((rv = ws_frame_prep_tx(ws, frame)) != 0) { + nni_aio_finish_error(aio, rv); + ws_frame_fini(frame); + return; + } + + nni_mtx_lock(&ws->mtx); + + if (ws->closed) { + nni_mtx_unlock(&ws->mtx); + nni_aio_finish_error(aio, NNG_ECLOSED); + ws_frame_fini(frame); + return; + } + if ((rv = nni_aio_schedule(aio, ws_write_cancel, ws)) != 0) { + nni_mtx_unlock(&ws->mtx); + nni_aio_finish_error(aio, rv); + ws_frame_fini(frame); + return; + } + nni_aio_set_prov_extra(aio, 0, frame); + nni_list_append(&ws->sendq, aio); + nni_list_append(&ws->txq, frame); + ws_start_write(ws); + nni_mtx_unlock(&ws->mtx); +} + +static void +ws_str_recv(void *arg, nng_aio *aio) +{ + nni_ws *ws = arg; + int rv; + + if (nni_aio_begin(aio) != 0) { + return; + } + nni_mtx_lock(&ws->mtx); + if ((rv = nni_aio_schedule(aio, ws_read_cancel, ws)) != 0) { + nni_mtx_unlock(&ws->mtx); + nni_aio_finish_error(aio, rv); + return; + } + nni_list_append(&ws->recvq, aio); + if (nni_list_first(&ws->recvq) == aio) { + ws_read_finish_msg(ws); + } + ws_start_read(ws); + + nni_mtx_unlock(&ws->mtx); +} + +static int +ws_get_request_headers(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws *ws = arg; + nni_mtx_lock(&ws->mtx); + if (ws->reqhdrs == NULL) { + ws->reqhdrs = nni_http_req_headers(ws->req); + } + nni_mtx_unlock(&ws->mtx); + return (nni_copyout_str(ws->reqhdrs, buf, szp, t)); +} + +static int +ws_get_response_headers(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws *ws = arg; + nni_mtx_lock(&ws->mtx); + if (ws->reshdrs == NULL) { + ws->reshdrs = nni_http_res_headers(ws->res); + } + nni_mtx_unlock(&ws->mtx); + return (nni_copyout_str(ws->reshdrs, buf, szp, t)); +} + +static int +ws_get_request_uri(void *arg, void *buf, size_t *szp, nni_type t) +{ + nni_ws *ws = arg; + return (nni_copyout_str(nni_http_req_get_uri(ws->req), buf, szp, t)); +} + +static const nni_option ws_options[] = { + { + .o_name = NNG_OPT_WS_REQUEST_HEADERS, + .o_get = ws_get_request_headers, + }, + { + .o_name = NNG_OPT_WS_RESPONSE_HEADERS, + .o_get = ws_get_response_headers, + }, + { + .o_name = NNG_OPT_WS_REQUEST_URI, + .o_get = ws_get_request_uri, + }, + { + .o_name = NULL, + }, +}; + +static int +ws_str_setx(void *arg, const char *nm, const void *buf, size_t sz, nni_type t) +{ + nni_ws *ws = arg; + int rv; + + // Headers can only be set. + nni_mtx_lock(&ws->mtx); + if (ws->closed) { + nni_mtx_unlock(&ws->mtx); + return (NNG_ECLOSED); + } + nni_mtx_unlock(&ws->mtx); + rv = nni_http_conn_setopt(ws->http, nm, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_setopt(ws_options, nm, ws, buf, sz, t); + } + if (rv == NNG_ENOTSUP) { + if (startswith(nm, NNG_OPT_WS_REQUEST_HEADER) || + startswith(nm, NNG_OPT_WS_RESPONSE_HEADER)) { + return (NNG_EREADONLY); + } + } + + return (rv); +} + +static int +ws_get_req_header( + nni_ws *ws, const char *nm, void *buf, size_t *szp, nni_type t) +{ + const char *s; + nm += strlen(NNG_OPT_WS_REQUEST_HEADER); + s = nni_http_req_get_header(ws->req, nm); + if (s == NULL) { + return (NNG_ENOENT); + } + return (nni_copyout_str(s, buf, szp, t)); +} + +static int +ws_get_res_header( + nni_ws *ws, const char *nm, void *buf, size_t *szp, nni_type t) +{ + const char *s; + nm += strlen(NNG_OPT_WS_RESPONSE_HEADER); + s = nni_http_res_get_header(ws->res, nm); + if (s == NULL) { + return (NNG_ENOENT); + } + return (nni_copyout_str(s, buf, szp, t)); +} + +static int +ws_str_getx(void *arg, const char *nm, void *buf, size_t *szp, nni_type t) +{ + nni_ws *ws = arg; + int rv; + + nni_mtx_lock(&ws->mtx); + if (ws->closed) { + nni_mtx_unlock(&ws->mtx); + return (NNG_ECLOSED); + } + nni_mtx_unlock(&ws->mtx); + rv = nni_http_conn_getopt(ws->http, nm, buf, szp, t); + if (rv == NNG_ENOTSUP) { + rv = nni_getopt(ws_options, nm, ws, buf, szp, t); + } + // Check for generic headers... + if (rv == NNG_ENOTSUP) { + if (startswith(nm, NNG_OPT_WS_REQUEST_HEADER)) { + rv = ws_get_req_header(ws, nm, buf, szp, t); + } else if (startswith(nm, NNG_OPT_WS_RESPONSE_HEADER)) { + rv = ws_get_res_header(ws, nm, buf, szp, t); + } + } + return (rv); +} + +static int +ws_check_size(const void *buf, size_t sz, nni_type t) +{ + return (nni_copyin_size(NULL, buf, sz, 0, NNI_MAXSZ, t)); +} + +static const nni_chkoption ws_chkopts[] = { + { + .o_name = NNG_OPT_WS_SENDMAXFRAME, + .o_check = ws_check_size, + }, + { + .o_name = NNG_OPT_WS_RECVMAXFRAME, + .o_check = ws_check_size, + }, + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_check = ws_check_size, + }, + { + .o_name = NNG_OPT_WS_PROTOCOL, + .o_check = ws_check_string, + }, + { + .o_name = NNG_OPT_WS_REQUEST_HEADERS, + .o_check = ws_check_string, + }, + { + .o_name = NNG_OPT_WS_RESPONSE_HEADERS, + .o_check = ws_check_string, + }, + { + .o_name = NULL, + }, +}; + +int +nni_ws_checkopt(const char *name, const void *data, size_t sz, nni_type t) +{ + int rv; + + rv = nni_chkopt(ws_chkopts, name, data, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_stream_checkopt("tcp", name, data, sz, t); + } + if (rv == NNG_ENOTSUP) { + rv = nni_stream_checkopt("tls+tcp", name, data, sz, t); + } + if (rv == NNG_ENOTSUP) { + if (startswith(name, NNG_OPT_WS_REQUEST_HEADER) || + startswith(name, NNG_OPT_WS_RESPONSE_HEADER)) { + rv = ws_check_string(data, sz, t); + } + } + // Potentially, add checks for header options. + return (rv); +} diff --git a/src/supplemental/websocket/websocket.h b/src/supplemental/websocket/websocket.h index 88b4bfb4..bbc58d30 100644 --- a/src/supplemental/websocket/websocket.h +++ b/src/supplemental/websocket/websocket.h @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -18,7 +18,10 @@ typedef struct nni_ws nni_ws; typedef struct nni_ws_listener nni_ws_listener; typedef struct nni_ws_dialer nni_ws_dialer; -typedef int (*nni_ws_listen_hook)(void *, nng_http_req *, nng_http_res *); +// Internal option, not for normal use (at present). This sets the +// dialer/listener into message mode. This is used by the SP transport. +// This is a boolean. +#define NNI_OPT_WS_MSGMODE "ws:msgmode" // Specify URL as ws://[<host>][:port][/path] // If host is missing, INADDR_ANY is assumed. If port is missing, @@ -26,44 +29,10 @@ typedef int (*nni_ws_listen_hook)(void *, nng_http_req *, nng_http_res *); // on INADDR_ANY port 80, with path "/". For connect side, INADDR_ANY // makes no sense. (TBD: return NNG_EADDRINVAL, or try loopback?) -extern int nni_ws_listener_init(nni_ws_listener **, nni_url *); -extern void nni_ws_listener_fini(nni_ws_listener *); -extern void nni_ws_listener_close(nni_ws_listener *); -extern int nni_ws_listener_proto(nni_ws_listener *, const char *); -extern int nni_ws_listener_listen(nni_ws_listener *); -extern void nni_ws_listener_accept(nni_ws_listener *, nng_aio *); -extern void nni_ws_listener_hook( - nni_ws_listener *, nni_ws_listen_hook, void *); -extern int nni_ws_listener_set_tls(nni_ws_listener *, nng_tls_config *); -extern int nni_ws_listener_get_tls(nni_ws_listener *, nng_tls_config **s); -extern void nni_ws_listener_set_maxframe(nni_ws_listener *, size_t); - -extern int nni_ws_dialer_init(nni_ws_dialer **, nni_url *); -extern void nni_ws_dialer_fini(nni_ws_dialer *); -extern void nni_ws_dialer_close(nni_ws_dialer *); -extern int nni_ws_dialer_proto(nni_ws_dialer *, const char *); -extern int nni_ws_dialer_header(nni_ws_dialer *, const char *, const char *); -extern void nni_ws_dialer_set_maxframe(nni_ws_dialer *, size_t); -extern void nni_ws_dialer_dial(nni_ws_dialer *, nng_aio *); -extern int nni_ws_dialer_set_tls(nni_ws_dialer *, nng_tls_config *); -extern int nni_ws_dialer_get_tls(nni_ws_dialer *, nng_tls_config **); - -// Dialer does not get a hook chance, as it can examine the request and reply -// after dial is done; this is not a 3-way handshake, so the dialer does -// not confirm the server's response at the HTTP level. (It can still issue -// a websocket close). - -extern void nni_ws_send_msg(nni_ws *, nng_aio *); -extern void nni_ws_recv_msg(nni_ws *, nng_aio *); -extern void nni_ws_close(nni_ws *); -extern void nni_ws_close_error(nni_ws *, uint16_t); -extern void nni_ws_fini(nni_ws *); -extern const char *nni_ws_response_headers(nni_ws *); -extern const char *nni_ws_request_headers(nni_ws *); -extern int nni_ws_getopt(nni_ws *, const char *, void *, size_t *, nni_type); -extern int nni_ws_setopt( - nni_ws *, const char *, const void *, size_t, nni_type); - -// The implementation will send periodic PINGs, and respond with PONGs. +// Much of the websocket API is still "private", meeaning you should not +// rely upon it being around. +extern int nni_ws_listener_alloc(nng_stream_listener **, const nni_url *); +extern int nni_ws_dialer_alloc(nng_stream_dialer **, const nni_url *); +extern int nni_ws_checkopt(const char *, const void *, size_t, nni_type); #endif // NNG_SUPPLEMENTAL_WEBSOCKET_WEBSOCKET_H diff --git a/src/transport/ipc/ipc.c b/src/transport/ipc/ipc.c index 609b1811..e0d83be0 100644 --- a/src/transport/ipc/ipc.c +++ b/src/transport/ipc/ipc.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -27,7 +27,7 @@ typedef struct ipctran_ep ipctran_ep; // ipc_pipe is one end of an IPC connection. struct ipctran_pipe { - nni_ipc_conn * conn; + nng_stream * conn; uint16_t peer; uint16_t proto; size_t rcvmax; @@ -58,17 +58,17 @@ struct ipctran_pipe { }; struct ipctran_ep { - nni_mtx mtx; - nni_sockaddr sa; - size_t rcvmax; - uint16_t proto; - nni_list pipes; - bool fini; - nni_ipc_dialer * dialer; - nni_ipc_listener *listener; - nni_reap_item reap; - nni_dialer * ndialer; - nni_listener * nlistener; + nni_mtx mtx; + nni_sockaddr sa; + size_t rcvmax; + uint16_t proto; + nni_list pipes; + bool fini; + nng_stream_dialer * dialer; + nng_stream_listener *listener; + nni_reap_item reap; + nni_dialer * ndialer; + nni_listener * nlistener; }; static void ipctran_pipe_send_start(ipctran_pipe *); @@ -104,7 +104,7 @@ ipctran_pipe_close(void *arg) nni_aio_close(p->negoaio); nni_aio_close(p->connaio); - nni_ipc_conn_close(p->conn); + nng_stream_close(p->conn); } static void @@ -145,9 +145,7 @@ ipctran_pipe_fini(void *arg) nni_aio_fini(p->txaio); nni_aio_fini(p->negoaio); nni_aio_fini(p->connaio); - if (p->conn != NULL) { - nni_ipc_conn_fini(p->conn); - } + nng_stream_free(p->conn); if (p->rxmsg) { nni_msg_free(p->rxmsg); } @@ -160,7 +158,7 @@ ipctran_pipe_reap(ipctran_pipe *p) { if (!nni_atomic_flag_test_and_set(&p->reaped)) { if (p->conn != NULL) { - nni_ipc_conn_close(p->conn); + nng_stream_close(p->conn); } nni_reap(&p->reap, ipctran_pipe_fini, p); } @@ -195,7 +193,6 @@ ipctran_pipe_alloc(ipctran_pipe **pipep, ipctran_ep *ep) p->proto = ep->proto; p->rcvmax = ep->rcvmax; - p->sa = ep->sa; p->ep = ep; *pipep = p; @@ -244,7 +241,7 @@ ipctran_pipe_conn_cb(void *arg) iov.iov_len = 8; iov.iov_buf = &p->txhead[0]; nni_aio_set_iov(p->negoaio, 1, &iov); - nni_ipc_conn_send(p->conn, p->negoaio); + nng_stream_send(p->conn, p->negoaio); nni_mtx_unlock(&ep->mtx); } @@ -278,7 +275,7 @@ ipctran_pipe_nego_cb(void *arg) iov.iov_buf = &p->txhead[p->gottxhead]; nni_aio_set_iov(aio, 1, &iov); // send it down... - nni_ipc_conn_send(p->conn, aio); + nng_stream_send(p->conn, aio); nni_mtx_unlock(&p->ep->mtx); return; } @@ -287,7 +284,7 @@ ipctran_pipe_nego_cb(void *arg) iov.iov_len = p->wantrxhead - p->gotrxhead; iov.iov_buf = &p->rxhead[p->gotrxhead]; nni_aio_set_iov(aio, 1, &iov); - nni_ipc_conn_recv(p->conn, aio); + nng_stream_recv(p->conn, aio); nni_mtx_unlock(&p->ep->mtx); return; } @@ -343,7 +340,7 @@ ipctran_pipe_send_cb(void *arg) n = nni_aio_count(txaio); nni_aio_iov_advance(txaio, n); if (nni_aio_iov_count(txaio) != 0) { - nni_ipc_conn_send(p->conn, txaio); + nng_stream_send(p->conn, txaio); nni_mtx_unlock(&p->mtx); return; } @@ -385,7 +382,7 @@ ipctran_pipe_recv_cb(void *arg) nni_aio_iov_advance(rxaio, n); if (nni_aio_iov_count(rxaio) != 0) { // Was this a partial read? If so then resubmit for the rest. - nni_ipc_conn_recv(p->conn, rxaio); + nng_stream_recv(p->conn, rxaio); nni_mtx_unlock(&p->mtx); return; } @@ -429,7 +426,7 @@ ipctran_pipe_recv_cb(void *arg) iov.iov_len = (size_t) len; nni_aio_set_iov(rxaio, 1, &iov); - nni_ipc_conn_recv(p->conn, rxaio); + nng_stream_recv(p->conn, rxaio); nni_mtx_unlock(&p->mtx); return; } @@ -531,7 +528,7 @@ ipctran_pipe_send_start(ipctran_pipe *p) niov++; } nni_aio_set_iov(txaio, niov, iov); - nni_ipc_conn_send(p->conn, txaio); + nng_stream_send(p->conn, txaio); } static void @@ -604,7 +601,7 @@ ipctran_pipe_recv_start(ipctran_pipe *p) iov.iov_len = sizeof(p->rxhead); nni_aio_set_iov(rxaio, 1, &iov); - nni_ipc_conn_recv(p->conn, rxaio); + nng_stream_recv(p->conn, rxaio); } static void @@ -670,12 +667,8 @@ ipctran_ep_fini(void *arg) nni_mtx_unlock(&ep->mtx); return; } - if (ep->dialer != NULL) { - nni_ipc_dialer_fini(ep->dialer); - } - if (ep->listener != NULL) { - nni_ipc_listener_fini(ep->listener); - } + nng_stream_dialer_free(ep->dialer); + nng_stream_listener_free(ep->listener); nni_mtx_unlock(&ep->mtx); nni_mtx_fini(&ep->mtx); NNI_FREE_STRUCT(ep); @@ -694,14 +687,14 @@ ipctran_ep_close(void *arg) nni_aio_close(p->txaio); nni_aio_close(p->rxaio); if (p->conn != NULL) { - nni_ipc_conn_close(p->conn); + nng_stream_close(p->conn); } } if (ep->dialer != NULL) { - nni_ipc_dialer_close(ep->dialer); + nng_stream_dialer_close(ep->dialer); } if (ep->listener != NULL) { - nni_ipc_listener_close(ep->listener); + nng_stream_listener_close(ep->listener); } nni_mtx_unlock(&ep->mtx); } @@ -711,7 +704,6 @@ ipctran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer) { ipctran_ep *ep; int rv; - size_t sz; nni_sock * sock = nni_dialer_sock(ndialer); if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { @@ -720,17 +712,10 @@ ipctran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer) nni_mtx_init(&ep->mtx); NNI_LIST_INIT(&ep->pipes, ipctran_pipe, node); - sz = sizeof(ep->sa.s_ipc.sa_path); - ep->sa.s_ipc.sa_family = NNG_AF_IPC; - ep->proto = nni_sock_proto_id(sock); - ep->ndialer = ndialer; - - if (nni_strlcpy(ep->sa.s_ipc.sa_path, url->u_path, sz) >= sz) { - ipctran_ep_fini(ep); - return (NNG_EADDRINVAL); - } + ep->proto = nni_sock_proto_id(sock); + ep->ndialer = ndialer; - if ((rv = nni_ipc_dialer_init(&ep->dialer)) != 0) { + if ((rv = nng_stream_dialer_alloc_url(&ep->dialer, url)) != 0) { ipctran_ep_fini(ep); return (rv); } @@ -744,7 +729,6 @@ ipctran_ep_init_listener(void **dp, nni_url *url, nni_listener *nlistener) { ipctran_ep *ep; int rv; - size_t sz; nni_sock * sock = nni_listener_sock(nlistener); if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { @@ -753,17 +737,10 @@ ipctran_ep_init_listener(void **dp, nni_url *url, nni_listener *nlistener) nni_mtx_init(&ep->mtx); NNI_LIST_INIT(&ep->pipes, ipctran_pipe, node); - sz = sizeof(ep->sa.s_ipc.sa_path); - ep->sa.s_ipc.sa_family = NNG_AF_IPC; - ep->proto = nni_sock_proto_id(sock); - ep->nlistener = nlistener; + ep->proto = nni_sock_proto_id(sock); + ep->nlistener = nlistener; - if (nni_strlcpy(ep->sa.s_ipc.sa_path, url->u_path, sz) >= sz) { - ipctran_ep_fini(ep); - return (NNG_EADDRINVAL); - } - - if ((rv = nni_ipc_listener_init(&ep->listener)) != 0) { + if ((rv = nng_stream_listener_alloc_url(&ep->listener, url)) != 0) { ipctran_ep_fini(ep); return (rv); } @@ -797,7 +774,7 @@ ipctran_ep_connect(void *arg, nni_aio *aio) return; } p->useraio = aio; - nni_ipc_dialer_dial(ep->dialer, &p->sa, p->connaio); + nng_stream_dialer_dial(ep->dialer, p->connaio); nni_mtx_unlock(&ep->mtx); } @@ -839,7 +816,7 @@ ipctran_ep_bind(void *arg) int rv; nni_mtx_lock(&ep->mtx); - rv = nni_ipc_listener_listen(ep->listener, &ep->sa); + rv = nng_stream_listener_listen(ep->listener); nni_mtx_unlock(&ep->mtx); return (rv); } @@ -869,7 +846,7 @@ ipctran_ep_accept(void *arg, nni_aio *aio) return; } p->useraio = aio; - nni_ipc_listener_accept(ep->listener, p->connaio); + nng_stream_listener_accept(ep->listener, p->connaio); nni_mtx_unlock(&ep->mtx); } @@ -879,8 +856,7 @@ ipctran_pipe_getopt( { ipctran_pipe *p = arg; - // We defer to the platform getopt code for IPC connections. - return (nni_ipc_conn_getopt(p->conn, name, buf, szp, t)); + return (nni_stream_getx(p->conn, name, buf, szp, t)); } static nni_tran_pipe_ops ipctran_pipe_ops = { @@ -915,7 +891,7 @@ ipctran_dialer_getopt( rv = nni_getopt(ipctran_ep_options, name, ep, buf, szp, t); if (rv == NNG_ENOTSUP) { - rv = nni_ipc_dialer_getopt(ep->dialer, name, buf, szp, t); + rv = nni_stream_dialer_getx(ep->dialer, name, buf, szp, t); } return (rv); } @@ -929,8 +905,7 @@ ipctran_dialer_setopt( rv = nni_setopt(ipctran_ep_options, name, ep, buf, sz, t); if (rv == NNG_ENOTSUP) { - rv = nni_ipc_dialer_setopt( - ep != NULL ? ep->dialer : NULL, name, buf, sz, t); + rv = nni_stream_dialer_setx(ep->dialer, name, buf, sz, t); } return (rv); } @@ -944,7 +919,7 @@ ipctran_listener_getopt( rv = nni_getopt(ipctran_ep_options, name, ep, buf, szp, t); if (rv == NNG_ENOTSUP) { - rv = nni_ipc_listener_getopt(ep->listener, name, buf, szp, t); + rv = nni_stream_listener_getx(ep->listener, name, buf, szp, t); } return (rv); } @@ -958,8 +933,34 @@ ipctran_listener_setopt( rv = nni_setopt(ipctran_ep_options, name, ep, buf, sz, t); if (rv == NNG_ENOTSUP) { - rv = nni_ipc_listener_setopt( - ep != NULL ? ep->listener : NULL, name, buf, sz, t); + rv = nni_stream_listener_setx(ep->listener, name, buf, sz, t); + } + return (rv); +} + +static int +ipctran_check_recvmaxsz(const void *v, size_t sz, nni_type t) +{ + return (nni_copyin_size(NULL, v, sz, 0, NNI_MAXSZ, t)); +} + +static nni_chkoption ipctran_checkopts[] = { + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_check = ipctran_check_recvmaxsz, + }, + { + .o_name = NULL, + }, +}; + +static int +ipctran_checkopt(const char *name, const void *buf, size_t sz, nni_type t) +{ + int rv; + rv = nni_chkopt(ipctran_checkopts, name, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_stream_checkopt("ipc", name, buf, sz, t); } return (rv); } @@ -991,6 +992,7 @@ static nni_tran ipc_tran = { .tran_pipe = &ipctran_pipe_ops, .tran_init = ipctran_init, .tran_fini = ipctran_fini, + .tran_checkopt = ipctran_checkopt, }; int diff --git a/src/transport/tcp/tcp.c b/src/transport/tcp/tcp.c index 30695918..3d757ee7 100644 --- a/src/transport/tcp/tcp.c +++ b/src/transport/tcp/tcp.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -14,6 +14,7 @@ #include <string.h> #include "core/nng_impl.h" +#include "core/tcp.h" // TCP transport. Platform specific TCP operations must be // supplied as well. @@ -23,19 +24,16 @@ typedef struct tcptran_ep tcptran_ep; // tcp_pipe is one end of a TCP connection. struct tcptran_pipe { - nni_tcp_conn * conn; + nng_stream * conn; nni_pipe * npipe; uint16_t peer; uint16_t proto; size_t rcvmax; - bool nodelay; - bool keepalive; bool closed; nni_list_node node; tcptran_ep * ep; nni_atomic_flag reaped; nni_reap_item reap; - nni_sockaddr sa; uint8_t txlen[sizeof(uint64_t)]; uint8_t rxlen[sizeof(uint64_t)]; size_t gottxhead; @@ -49,30 +47,25 @@ struct tcptran_pipe { nni_aio * rxaio; nni_aio * negoaio; nni_aio * connaio; - nni_aio * rslvaio; nni_msg * rxmsg; nni_mtx mtx; }; struct tcptran_ep { - nni_mtx mtx; - uint16_t af; - uint16_t proto; - size_t rcvmax; - bool nodelay; - bool keepalive; - bool fini; - nni_url * url; - const char * host; // for dialers - nng_sockaddr src; - nng_sockaddr sa; - nng_sockaddr bsa; - nni_list pipes; - nni_reap_item reap; - nni_tcp_dialer * dialer; - nni_tcp_listener *listener; - nni_dialer * ndialer; - nni_listener * nlistener; + nni_mtx mtx; + uint16_t af; + uint16_t proto; + size_t rcvmax; + bool fini; + nni_url * url; + const char * host; // for dialers + nng_sockaddr src; + nni_list pipes; + nni_reap_item reap; + nng_stream_dialer * dialer; + nng_stream_listener *listener; + nni_dialer * ndialer; + nni_listener * nlistener; }; static void tcptran_pipe_send_start(tcptran_pipe *); @@ -81,7 +74,6 @@ static void tcptran_pipe_send_cb(void *); static void tcptran_pipe_recv_cb(void *); static void tcptran_pipe_conn_cb(void *); static void tcptran_pipe_nego_cb(void *); -static void tcptran_pipe_rslv_cb(void *); static void tcptran_ep_fini(void *); static int @@ -108,9 +100,8 @@ tcptran_pipe_close(void *arg) nni_aio_close(p->txaio); nni_aio_close(p->negoaio); nni_aio_close(p->connaio); - nni_aio_close(p->rslvaio); - nni_tcp_conn_close(p->conn); + nng_stream_close(p->conn); } static void @@ -122,7 +113,6 @@ tcptran_pipe_stop(void *arg) nni_aio_stop(p->txaio); nni_aio_stop(p->negoaio); nni_aio_stop(p->connaio); - nni_aio_stop(p->rslvaio); } static int @@ -153,10 +143,7 @@ tcptran_pipe_fini(void *arg) nni_aio_fini(p->txaio); nni_aio_fini(p->negoaio); nni_aio_fini(p->connaio); - nni_aio_fini(p->rslvaio); - if (p->conn != NULL) { - nni_tcp_conn_fini(p->conn); - } + nng_stream_free(p->conn); nni_msg_free(p->rxmsg); nni_mtx_fini(&p->mtx); NNI_FREE_STRUCT(p); @@ -167,7 +154,7 @@ tcptran_pipe_reap(tcptran_pipe *p) { if (!nni_atomic_flag_test_and_set(&p->reaped)) { if (p->conn != NULL) { - nni_tcp_conn_close(p->conn); + nng_stream_close(p->conn); } nni_reap(&p->reap, tcptran_pipe_fini, p); } @@ -185,7 +172,6 @@ tcptran_pipe_alloc(tcptran_pipe **pipep, tcptran_ep *ep) nni_mtx_init(&p->mtx); if (((rv = nni_aio_init(&p->txaio, tcptran_pipe_send_cb, p)) != 0) || ((rv = nni_aio_init(&p->rxaio, tcptran_pipe_recv_cb, p)) != 0) || - ((rv = nni_aio_init(&p->rslvaio, tcptran_pipe_rslv_cb, p)) != 0) || ((rv = nni_aio_init(&p->connaio, tcptran_pipe_conn_cb, p)) != 0) || ((rv = nni_aio_init(&p->negoaio, tcptran_pipe_nego_cb, p)) != 0)) { tcptran_pipe_fini(p); @@ -196,12 +182,10 @@ tcptran_pipe_alloc(tcptran_pipe **pipep, tcptran_ep *ep) nni_atomic_flag_reset(&p->reaped); nni_list_append(&ep->pipes, p); - p->keepalive = ep->keepalive; - p->nodelay = ep->nodelay; - p->rcvmax = ep->rcvmax; - p->proto = ep->proto; - p->ep = ep; - *pipep = p; + p->rcvmax = ep->rcvmax; + p->proto = ep->proto; + p->ep = ep; + *pipep = p; return (0); } @@ -215,7 +199,6 @@ tcptran_pipe_conn_cancel(nni_aio *aio, void *arg, int rv) if (aio == p->useraio) { nni_aio_close(p->negoaio); nni_aio_close(p->connaio); - nni_aio_close(p->rslvaio); p->useraio = NULL; nni_aio_finish_error(aio, rv); tcptran_pipe_reap(p); @@ -224,33 +207,6 @@ tcptran_pipe_conn_cancel(nni_aio *aio, void *arg, int rv) } static void -tcptran_pipe_rslv_cb(void *arg) -{ - tcptran_pipe *p = arg; - tcptran_ep * ep = p->ep; - nni_aio * aio = p->rslvaio; - nni_aio * uaio; - int rv; - - nni_mtx_lock(&ep->mtx); - if ((uaio = p->useraio) == NULL) { - nni_mtx_unlock(&ep->mtx); - tcptran_pipe_reap(p); - return; - } - if ((rv = nni_aio_result(aio)) != 0) { - p->useraio = NULL; - nni_mtx_unlock(&ep->mtx); - nni_aio_finish_error(uaio, rv); - tcptran_pipe_reap(p); - return; - } - - nni_tcp_dialer_dial(ep->dialer, &p->sa, p->connaio); - nni_mtx_unlock(&ep->mtx); -} - -static void tcptran_pipe_conn_cb(void *arg) { tcptran_pipe *p = arg; @@ -293,7 +249,7 @@ tcptran_pipe_conn_cb(void *arg) iov.iov_len = 8; iov.iov_buf = &p->txlen[0]; nni_aio_set_iov(p->negoaio, 1, &iov); - nni_tcp_conn_send(p->conn, p->negoaio); + nng_stream_send(p->conn, p->negoaio); nni_mtx_unlock(&ep->mtx); } @@ -330,7 +286,7 @@ tcptran_pipe_nego_cb(void *arg) iov.iov_buf = &p->txlen[p->gottxhead]; // send it down... nni_aio_set_iov(aio, 1, &iov); - nni_tcp_conn_send(p->conn, aio); + nng_stream_send(p->conn, aio); nni_mtx_unlock(&ep->mtx); return; } @@ -339,7 +295,7 @@ tcptran_pipe_nego_cb(void *arg) iov.iov_len = p->wantrxhead - p->gotrxhead; iov.iov_buf = &p->rxlen[p->gotrxhead]; nni_aio_set_iov(aio, 1, &iov); - nni_tcp_conn_recv(p->conn, aio); + nng_stream_recv(p->conn, aio); nni_mtx_unlock(&ep->mtx); return; } @@ -354,10 +310,6 @@ tcptran_pipe_nego_cb(void *arg) NNI_GET16(&p->rxlen[4], p->peer); p->useraio = NULL; - (void) nni_tcp_conn_setopt(p->conn, NNG_OPT_TCP_NODELAY, &p->nodelay, - sizeof(p->nodelay), NNI_TYPE_BOOL); - (void) nni_tcp_conn_setopt(p->conn, NNG_OPT_TCP_KEEPALIVE, - &p->keepalive, sizeof(p->keepalive), NNI_TYPE_BOOL); nni_mtx_unlock(&ep->mtx); @@ -400,7 +352,7 @@ tcptran_pipe_send_cb(void *arg) n = nni_aio_count(txaio); nni_aio_iov_advance(txaio, n); if (nni_aio_iov_count(txaio) > 0) { - nni_tcp_conn_send(p->conn, txaio); + nng_stream_send(p->conn, txaio); nni_mtx_unlock(&p->mtx); return; } @@ -437,7 +389,7 @@ tcptran_pipe_recv_cb(void *arg) n = nni_aio_count(rxaio); nni_aio_iov_advance(rxaio, n); if (nni_aio_iov_count(rxaio) > 0) { - nni_tcp_conn_recv(p->conn, rxaio); + nng_stream_recv(p->conn, rxaio); nni_mtx_unlock(&p->mtx); return; } @@ -469,7 +421,7 @@ tcptran_pipe_recv_cb(void *arg) iov.iov_len = (size_t) len; nni_aio_set_iov(rxaio, 1, &iov); - nni_tcp_conn_recv(p->conn, rxaio); + nng_stream_recv(p->conn, rxaio); nni_mtx_unlock(&p->mtx); return; } @@ -566,7 +518,7 @@ tcptran_pipe_send_start(tcptran_pipe *p) niov++; } nni_aio_set_iov(txaio, niov, iov); - nni_tcp_conn_send(p->conn, txaio); + nng_stream_send(p->conn, txaio); } static void @@ -639,7 +591,7 @@ tcptran_pipe_recv_start(tcptran_pipe *p) iov.iov_len = sizeof(p->rxlen); nni_aio_set_iov(rxaio, 1, &iov); - nni_tcp_conn_recv(p->conn, rxaio); + nng_stream_recv(p->conn, rxaio); } static void @@ -678,7 +630,7 @@ tcptran_pipe_getopt( void *arg, const char *name, void *buf, size_t *szp, nni_type t) { tcptran_pipe *p = arg; - return (nni_tcp_conn_getopt(p->conn, name, buf, szp, t)); + return (nni_stream_getx(p->conn, name, buf, szp, t)); } static void @@ -692,12 +644,8 @@ tcptran_ep_fini(void *arg) nni_mtx_unlock(&ep->mtx); return; } - if (ep->dialer != NULL) { - nni_tcp_dialer_fini(ep->dialer); - } - if (ep->listener != NULL) { - nni_tcp_listener_fini(ep->listener); - } + nng_stream_dialer_free(ep->dialer); + nng_stream_listener_free(ep->listener); nni_mtx_unlock(&ep->mtx); nni_mtx_fini(&ep->mtx); NNI_FREE_STRUCT(ep); @@ -713,41 +661,89 @@ tcptran_ep_close(void *arg) NNI_LIST_FOREACH (&ep->pipes, p) { nni_aio_close(p->negoaio); nni_aio_close(p->connaio); - nni_aio_close(p->rslvaio); nni_aio_close(p->txaio); nni_aio_close(p->rxaio); if (p->conn != NULL) { - nni_tcp_conn_close(p->conn); + nng_stream_close(p->conn); } } if (ep->dialer != NULL) { - nni_tcp_dialer_close(ep->dialer); + nng_stream_dialer_close(ep->dialer); } if (ep->listener != NULL) { - nni_tcp_listener_close(ep->listener); + nng_stream_listener_close(ep->listener); } nni_mtx_unlock(&ep->mtx); } +// This parses off the optional source address that this transport uses. +// The special handling of this URL format is quite honestly an historical +// mistake, which we would remove if we could. static int -tcptran_dialer_init(void **dp, nni_url *url, nni_dialer *ndialer) +tcptran_url_parse_source(nni_url *url, nng_sockaddr *sa, const nni_url *surl) { - tcptran_ep * ep; - int rv; - uint16_t af; - char * host; - nng_sockaddr srcsa; - nni_sock * sock = nni_dialer_sock(ndialer); + int af; + char * semi; + char * src; + size_t len; + int rv; + nni_aio *aio; + + // We modify the URL. This relies on the fact that the underlying + // transport does not free this, so we can just use references. + + url->u_scheme = surl->u_scheme; + url->u_port = surl->u_port; + url->u_hostname = surl->u_hostname; + + if ((semi = strchr(url->u_hostname, ';')) == NULL) { + memset(sa, 0, sizeof(*sa)); + return (0); + } - if (strcmp(url->u_scheme, "tcp") == 0) { + len = (size_t)(semi - url->u_hostname); + url->u_hostname = semi + 1; + + if (strcmp(surl->u_scheme, "tcp") == 0) { af = NNG_AF_UNSPEC; - } else if (strcmp(url->u_scheme, "tcp4") == 0) { + } else if (strcmp(surl->u_scheme, "tcp4") == 0) { af = NNG_AF_INET; - } else if (strcmp(url->u_scheme, "tcp6") == 0) { + } else if (strcmp(surl->u_scheme, "tcp6") == 0) { af = NNG_AF_INET6; } else { return (NNG_EADDRINVAL); } + + if ((src = nni_alloc(len + 1)) == NULL) { + return (NNG_ENOMEM); + } + memcpy(src, surl->u_hostname, len); + src[len] = '\0'; + + if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { + nni_free(src, len + 1); + return (rv); + } + + nni_tcp_resolv(src, 0, af, 1, aio); + nni_aio_wait(aio); + if ((rv = nni_aio_result(aio)) == 0) { + nni_aio_get_sockaddr(aio, sa); + } + nni_aio_fini(aio); + nni_free(src, len + 1); + return (rv); +} + +static int +tcptran_dialer_init(void **dp, nni_url *url, nni_dialer *ndialer) +{ + tcptran_ep * ep; + int rv; + nng_sockaddr srcsa; + nni_sock * sock = nni_dialer_sock(ndialer); + nni_url myurl; + // Check for invalid URL components. if ((strlen(url->u_path) != 0) && (strcmp(url->u_path, "/") != 0)) { return (NNG_EADDRINVAL); @@ -758,62 +754,27 @@ tcptran_dialer_init(void **dp, nni_url *url, nni_dialer *ndialer) return (NNG_EADDRINVAL); } + if ((rv = tcptran_url_parse_source(&myurl, &srcsa, url)) != 0) { + return (NNG_EADDRINVAL); + } + if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { return (NNG_ENOMEM); } nni_mtx_init(&ep->mtx); NNI_LIST_INIT(&ep->pipes, tcptran_pipe, node); - ep->af = af; - ep->proto = nni_sock_proto_id(sock); - ep->nodelay = true; - ep->keepalive = false; - ep->url = url; - ep->ndialer = ndialer; - - // Detect an embedded local interface name in the hostname. This - // syntax is only valid with dialers. - if ((host = strchr(url->u_hostname, ';')) != NULL) { - size_t len; - char * src = NULL; - nni_aio *aio; - len = (uintptr_t) host - (uintptr_t) url->u_hostname; - host++; - if ((len < 2) || (strlen(host) == 0)) { - tcptran_ep_fini(ep); - return (NNG_EADDRINVAL); - } - if ((src = nni_alloc(len + 1)) == NULL) { - tcptran_ep_fini(ep); - return (NNG_ENOMEM); - } - memcpy(src, url->u_hostname, len); - src[len] = 0; - - if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { - tcptran_ep_fini(ep); - nni_strfree(src); - return (rv); - } - nni_aio_set_input(aio, 0, &srcsa); - nni_tcp_resolv(src, 0, af, 1, aio); - nni_aio_wait(aio); - rv = nni_aio_result(aio); - nni_aio_fini(aio); - nni_strfree(src); - ep->host = host; - } else { - srcsa.s_family = NNG_AF_UNSPEC; - ep->host = url->u_hostname; - rv = 0; - } + ep->proto = nni_sock_proto_id(sock); + ep->url = url; + ep->ndialer = ndialer; - if ((rv != 0) || ((rv = nni_tcp_dialer_init(&ep->dialer)) != 0)) { + if ((rv != 0) || + ((rv = nng_stream_dialer_alloc_url(&ep->dialer, &myurl)) != 0)) { tcptran_ep_fini(ep); return (rv); } if ((srcsa.s_family != NNG_AF_UNSPEC) && - ((rv = nni_tcp_dialer_setopt(ep->dialer, NNG_OPT_LOCADDR, &srcsa, + ((rv = nni_stream_dialer_setx(ep->dialer, NNG_OPT_LOCADDR, &srcsa, sizeof(srcsa), NNI_TYPE_SOCKADDR)) != 0)) { tcptran_ep_fini(ep); return (rv); @@ -826,21 +787,8 @@ tcptran_listener_init(void **lp, nni_url *url, nni_listener *nlistener) { tcptran_ep *ep; int rv; - char * host; - nni_aio * aio; - uint16_t af; nni_sock * sock = nni_listener_sock(nlistener); - if (strcmp(url->u_scheme, "tcp") == 0) { - af = NNG_AF_UNSPEC; - } else if (strcmp(url->u_scheme, "tcp4") == 0) { - af = NNG_AF_INET; - } else if (strcmp(url->u_scheme, "tcp6") == 0) { - af = NNG_AF_INET6; - } else { - return (NNG_EADDRINVAL); - } - // Check for invalid URL components. if ((strlen(url->u_path) != 0) && (strcmp(url->u_path, "/") != 0)) { return (NNG_EADDRINVAL); @@ -855,46 +803,14 @@ tcptran_listener_init(void **lp, nni_url *url, nni_listener *nlistener) } nni_mtx_init(&ep->mtx); NNI_LIST_INIT(&ep->pipes, tcptran_pipe, node); - ep->af = af; ep->proto = nni_sock_proto_id(sock); - ep->nodelay = true; - ep->keepalive = false; ep->url = url; ep->nlistener = nlistener; - if (strlen(url->u_hostname) == 0) { - host = NULL; - } else { - host = url->u_hostname; - } - - // XXX: We are doing lookup at listener initialization. There is - // a valid argument that this should be done at bind time, but that - // would require making bind asynchronous. In some ways this would - // be worse than the cost of just waiting here. We always recommend - // using local IP addresses rather than names when possible. - - if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { + if ((rv = nng_stream_listener_alloc_url(&ep->listener, url)) != 0) { tcptran_ep_fini(ep); return (rv); } - nni_aio_set_input(aio, 0, &ep->sa); - nni_tcp_resolv(host, url->u_port, af, 1, aio); - nni_aio_wait(aio); - rv = nni_aio_result(aio); - nni_aio_fini(aio); - - if (rv != 0) { - tcptran_ep_fini(ep); - return (rv); - } - - if ((rv = nni_tcp_listener_init(&ep->listener)) != 0) { - tcptran_ep_fini(ep); - return (rv); - } - - ep->bsa = ep->sa; *lp = ep; return (0); @@ -917,17 +833,13 @@ tcptran_ep_connect(void *arg, nni_aio *aio) return; } if ((rv = nni_aio_schedule(aio, tcptran_pipe_conn_cancel, p)) != 0) { - nni_list_remove(&ep->pipes, p); - p->ep = NULL; nni_mtx_unlock(&ep->mtx); nni_aio_finish_error(aio, rv); tcptran_pipe_reap(p); return; } p->useraio = aio; - // Start the name resolution before we try connecting. - nni_aio_set_input(p->rslvaio, 0, &p->sa); - nni_tcp_resolv(ep->host, ep->url->u_port, ep->af, 0, p->rslvaio); + nng_stream_dialer_dial(ep->dialer, p->connaio); nni_mtx_unlock(&ep->mtx); } @@ -941,16 +853,18 @@ tcptran_ep_get_url(void *arg, void *v, size_t *szp, nni_opt_type t) char ipstr[48]; // max for IPv6 addresses including [] char portstr[6]; // max for 16-bit port nng_sockaddr sa; - size_t sz = sizeof(sa); int rv; - rv = nni_tcp_listener_getopt(ep->listener, NNG_OPT_LOCADDR, - &sa, &sz, NNI_TYPE_SOCKADDR); + rv = nng_stream_listener_get_addr( + ep->listener, NNG_OPT_LOCADDR, &sa); if (rv != 0) { return (rv); } nni_ntop(&sa, ipstr, portstr); - snprintf(ustr, sizeof(ustr), "tcp://%s:%s", ipstr, portstr); + snprintf(ustr, sizeof(ustr), + sa.s_family == NNG_AF_INET6 ? "tcp://[%s]:%s" + : "tcp://%s:%s", + ipstr, portstr); return (nni_copyout_str(ustr, v, szp, t)); } @@ -995,8 +909,7 @@ tcptran_ep_bind(void *arg) int rv; nni_mtx_lock(&ep->mtx); - ep->bsa = ep->sa; - rv = nni_tcp_listener_listen(ep->listener, &ep->bsa); + rv = nng_stream_listener_listen(ep->listener); nni_mtx_unlock(&ep->mtx); return (rv); @@ -1027,7 +940,7 @@ tcptran_ep_accept(void *arg, nni_aio *aio) return; } p->useraio = aio; - nni_tcp_listener_accept(ep->listener, p->connaio); + nng_stream_listener_accept(ep->listener, p->connaio); nni_mtx_unlock(&ep->mtx); } @@ -1065,7 +978,7 @@ tcptran_dialer_getopt( tcptran_ep *ep = arg; int rv; - rv = nni_tcp_dialer_getopt(ep->dialer, name, buf, szp, t); + rv = nni_stream_dialer_getx(ep->dialer, name, buf, szp, t); if (rv == NNG_ENOTSUP) { rv = nni_getopt(tcptran_ep_opts, name, ep, buf, szp, t); } @@ -1079,8 +992,7 @@ tcptran_dialer_setopt( tcptran_ep *ep = arg; int rv; - rv = nni_tcp_dialer_setopt( - ep != NULL ? ep->dialer : NULL, name, buf, sz, t); + rv = nni_stream_dialer_setx(ep->dialer, name, buf, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_setopt(tcptran_ep_opts, name, ep, buf, sz, t); } @@ -1094,7 +1006,7 @@ tcptran_listener_getopt( tcptran_ep *ep = arg; int rv; - rv = nni_tcp_listener_getopt(ep->listener, name, buf, szp, t); + rv = nni_stream_listener_getx(ep->listener, name, buf, szp, t); if (rv == NNG_ENOTSUP) { rv = nni_getopt(tcptran_ep_opts, name, ep, buf, szp, t); } @@ -1108,14 +1020,40 @@ tcptran_listener_setopt( tcptran_ep *ep = arg; int rv; - rv = nni_tcp_listener_setopt( - ep != NULL ? ep->listener : NULL, name, buf, sz, t); + rv = nni_stream_listener_setx(ep->listener, name, buf, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_setopt(tcptran_ep_opts, name, ep, buf, sz, t); } return (rv); } +static int +tcptran_check_recvmaxsz(const void *v, size_t sz, nni_type t) +{ + return (nni_copyin_size(NULL, v, sz, 0, NNI_MAXSZ, t)); +} + +static nni_chkoption tcptran_checkopts[] = { + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_check = tcptran_check_recvmaxsz, + }, + { + .o_name = NULL, + }, +}; + +static int +tcptran_checkopt(const char *name, const void *buf, size_t sz, nni_type t) +{ + int rv; + rv = nni_chkopt(tcptran_checkopts, name, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_stream_checkopt("tcp", name, buf, sz, t); + } + return (rv); +} + static nni_tran_dialer_ops tcptran_dialer_ops = { .d_init = tcptran_dialer_init, .d_fini = tcptran_ep_fini, @@ -1143,6 +1081,7 @@ static nni_tran tcp_tran = { .tran_pipe = &tcptran_pipe_ops, .tran_init = tcptran_init, .tran_fini = tcptran_fini, + .tran_checkopt = tcptran_checkopt, }; static nni_tran tcp4_tran = { @@ -1153,6 +1092,7 @@ static nni_tran tcp4_tran = { .tran_pipe = &tcptran_pipe_ops, .tran_init = tcptran_init, .tran_fini = tcptran_fini, + .tran_checkopt = tcptran_checkopt, }; static nni_tran tcp6_tran = { @@ -1163,6 +1103,7 @@ static nni_tran tcp6_tran = { .tran_pipe = &tcptran_pipe_ops, .tran_init = tcptran_init, .tran_fini = tcptran_fini, + .tran_checkopt = tcptran_checkopt, }; int diff --git a/src/transport/tls/tls.c b/src/transport/tls/tls.c index 25bfb0dd..d68cb8b1 100644 --- a/src/transport/tls/tls.c +++ b/src/transport/tls/tls.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -30,7 +30,7 @@ typedef struct tlstran_pipe tlstran_pipe; // tlstran_pipe is one end of a TLS connection. struct tlstran_pipe { - nng_tls * tls; + nng_stream * tls; nni_pipe * npipe; uint16_t peer; uint16_t proto; @@ -54,29 +54,28 @@ struct tlstran_pipe { nni_aio * rxaio; nni_aio * negoaio; nni_aio * connaio; - nni_aio * rslvaio; nni_msg * rxmsg; nni_mtx mtx; }; // Stuff that is common to both dialers and listeners. struct tlstran_ep { - nni_mtx mtx; - uint16_t af; - uint16_t proto; - size_t rcvmax; - bool fini; - int authmode; - nni_url * url; - nni_list pipes; - nni_reap_item reap; - nng_tls_dialer * dialer; - nng_tls_listener *listener; - const char * host; - nng_sockaddr src; - nng_sockaddr sa; - nni_dialer * ndialer; - nni_listener * nlistener; + nni_mtx mtx; + uint16_t af; + uint16_t proto; + size_t rcvmax; + bool fini; + int authmode; + nni_url * url; + nni_list pipes; + nni_reap_item reap; + nng_stream_dialer * dialer; + nng_stream_listener *listener; + const char * host; + nng_sockaddr src; + nng_sockaddr sa; + nni_dialer * ndialer; + nni_listener * nlistener; }; static void tlstran_pipe_send_start(tlstran_pipe *); @@ -85,7 +84,6 @@ static void tlstran_pipe_send_cb(void *); static void tlstran_pipe_recv_cb(void *); static void tlstran_pipe_conn_cb(void *); static void tlstran_pipe_nego_cb(void *); -static void tlstran_pipe_rslv_cb(void *); static void tlstran_ep_fini(void *); static int @@ -108,9 +106,8 @@ tlstran_pipe_close(void *arg) nni_aio_close(p->txaio); nni_aio_close(p->negoaio); nni_aio_close(p->connaio); - nni_aio_close(p->rslvaio); - nng_tls_close(p->tls); + nng_stream_close(p->tls); } static void @@ -122,7 +119,6 @@ tlstran_pipe_stop(void *arg) nni_aio_stop(p->txaio); nni_aio_stop(p->negoaio); nni_aio_stop(p->connaio); - nni_aio_stop(p->rslvaio); } static int @@ -152,8 +148,7 @@ tlstran_pipe_fini(void *arg) nni_aio_fini(p->txaio); nni_aio_fini(p->negoaio); nni_aio_fini(p->connaio); - nni_aio_fini(p->rslvaio); - nng_tls_free(p->tls); + nng_stream_free(p->tls); nni_msg_free(p->rxmsg); NNI_FREE_STRUCT(p); } @@ -171,7 +166,6 @@ tlstran_pipe_alloc(tlstran_pipe **pipep, tlstran_ep *ep) if (((rv = nni_aio_init(&p->txaio, tlstran_pipe_send_cb, p)) != 0) || ((rv = nni_aio_init(&p->rxaio, tlstran_pipe_recv_cb, p)) != 0) || - ((rv = nni_aio_init(&p->rslvaio, tlstran_pipe_rslv_cb, p)) != 0) || ((rv = nni_aio_init(&p->connaio, tlstran_pipe_conn_cb, p)) != 0) || ((rv = nni_aio_init(&p->negoaio, tlstran_pipe_nego_cb, p)) != 0)) { tlstran_pipe_fini(p); @@ -194,7 +188,7 @@ tlstran_pipe_reap(tlstran_pipe *p) { if (!nni_atomic_flag_test_and_set(&p->reaped)) { if (p->tls != NULL) { - nng_tls_close(p->tls); + nng_stream_close(p->tls); } nni_reap(&p->reap, tlstran_pipe_fini, p); } @@ -210,7 +204,6 @@ tlstran_pipe_conn_cancel(nni_aio *aio, void *arg, int rv) p->useraio = NULL; nni_aio_close(p->negoaio); nni_aio_close(p->connaio); - nni_aio_close(p->rslvaio); nni_aio_finish_error(aio, rv); tlstran_pipe_reap(p); } @@ -218,33 +211,6 @@ tlstran_pipe_conn_cancel(nni_aio *aio, void *arg, int rv) } static void -tlstran_pipe_rslv_cb(void *arg) -{ - tlstran_pipe *p = arg; - tlstran_ep * ep = p->ep; - nni_aio * aio = p->rslvaio; - nni_aio * uaio; - int rv; - - nni_mtx_lock(&ep->mtx); - if ((uaio = p->useraio) == NULL) { - nni_mtx_unlock(&ep->mtx); - tlstran_pipe_reap(p); - return; - } - - if ((rv = nni_aio_result(aio)) != 0) { - p->useraio = NULL; - nni_mtx_unlock(&ep->mtx); - nni_aio_finish_error(uaio, rv); - tlstran_pipe_reap(p); - return; - } - nng_tls_dialer_dial(ep->dialer, &p->sa, p->connaio); - nni_mtx_unlock(&ep->mtx); -} - -static void tlstran_pipe_conn_cb(void *arg) { tlstran_pipe *p = arg; @@ -288,7 +254,7 @@ tlstran_pipe_conn_cb(void *arg) iov.iov_len = 8; iov.iov_buf = &p->txlen[0]; nni_aio_set_iov(p->negoaio, 1, &iov); - nng_tls_send(p->tls, p->negoaio); + nng_stream_send(p->tls, p->negoaio); nni_mtx_unlock(&ep->mtx); } @@ -325,7 +291,7 @@ tlstran_pipe_nego_cb(void *arg) iov.iov_buf = &p->txlen[p->gottxhead]; nni_aio_set_iov(aio, 1, &iov); // send it down... - nng_tls_send(p->tls, aio); + nng_stream_send(p->tls, aio); nni_mtx_unlock(&ep->mtx); return; } @@ -334,7 +300,7 @@ tlstran_pipe_nego_cb(void *arg) iov.iov_len = p->wantrxhead - p->gotrxhead; iov.iov_buf = &p->rxlen[p->gotrxhead]; nni_aio_set_iov(aio, 1, &iov); - nng_tls_recv(p->tls, aio); + nng_stream_recv(p->tls, aio); nni_mtx_unlock(&ep->mtx); return; } @@ -390,7 +356,7 @@ tlstran_pipe_send_cb(void *arg) n = nni_aio_count(txaio); nni_aio_iov_advance(txaio, n); if (nni_aio_iov_count(txaio) > 0) { - nng_tls_send(p->tls, txaio); + nng_stream_send(p->tls, txaio); nni_mtx_unlock(&p->mtx); return; } @@ -426,7 +392,7 @@ tlstran_pipe_recv_cb(void *arg) nni_aio_iov_advance(rxaio, n); if (nni_aio_iov_count(rxaio) > 0) { // Was this a partial read? If so then resubmit for the rest. - nng_tls_recv(p->tls, rxaio); + nng_stream_recv(p->tls, rxaio); nni_mtx_unlock(&p->mtx); return; } @@ -458,7 +424,7 @@ tlstran_pipe_recv_cb(void *arg) iov.iov_len = (size_t) len; nni_aio_set_iov(rxaio, 1, &iov); - nng_tls_recv(p->tls, rxaio); + nng_stream_recv(p->tls, rxaio); nni_mtx_unlock(&p->mtx); return; } @@ -548,7 +514,7 @@ tlstran_pipe_send_start(tlstran_pipe *p) } nni_aio_set_iov(txaio, niov, iov); - nng_tls_send(p->tls, txaio); + nng_stream_send(p->tls, txaio); } static void @@ -609,7 +575,7 @@ tlstran_pipe_recv_start(tlstran_pipe *p) iov.iov_len = sizeof(p->rxlen); nni_aio_set_iov(rxaio, 1, &iov); - nng_tls_recv(p->tls, rxaio); + nng_stream_recv(p->tls, rxaio); } static void @@ -654,8 +620,8 @@ tlstran_ep_fini(void *arg) nni_mtx_unlock(&ep->mtx); return; } - nng_tls_dialer_free(ep->dialer); - nng_tls_listener_free(ep->listener); + nng_stream_dialer_free(ep->dialer); + nng_stream_listener_free(ep->listener); nni_mtx_unlock(&ep->mtx); nni_mtx_fini(&ep->mtx); @@ -672,42 +638,89 @@ tlstran_ep_close(void *arg) NNI_LIST_FOREACH (&ep->pipes, p) { nni_aio_close(p->negoaio); nni_aio_close(p->connaio); - nni_aio_close(p->rslvaio); nni_aio_close(p->txaio); nni_aio_close(p->rxaio); if (p->tls != NULL) { - nng_tls_close(p->tls); + nng_stream_close(p->tls); } } if (ep->dialer != NULL) { - nng_tls_dialer_close(ep->dialer); + nng_stream_dialer_close(ep->dialer); } if (ep->listener != NULL) { - nng_tls_listener_close(ep->listener); + nng_stream_listener_close(ep->listener); } nni_mtx_unlock(&ep->mtx); } +// This parses off the optional source address that this transport uses. +// The special handling of this URL format is quite honestly an historical +// mistake, which we would remove if we could. static int -tlstran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer) +tlstran_url_parse_source(nni_url *url, nng_sockaddr *sa, const nni_url *surl) { - tlstran_ep * ep; - int rv; - uint16_t af; - char * host; - nng_sockaddr srcsa; - nni_sock * sock = nni_dialer_sock(ndialer); + int af; + char * semi; + char * src; + size_t len; + int rv; + nni_aio *aio; - if (strcmp(url->u_scheme, "tls+tcp") == 0) { + // We modify the URL. This relies on the fact that the underlying + // transport does not free this, so we can just use references. + + url->u_scheme = surl->u_scheme; + url->u_port = surl->u_port; + url->u_hostname = surl->u_hostname; + + if ((semi = strchr(url->u_hostname, ';')) == NULL) { + memset(sa, 0, sizeof(*sa)); + return (0); + } + + len = (size_t)(semi - url->u_hostname); + url->u_hostname = semi + 1; + + if (strcmp(surl->u_scheme, "tls+tcp") == 0) { af = NNG_AF_UNSPEC; - } else if (strcmp(url->u_scheme, "tls+tcp4") == 0) { + } else if (strcmp(surl->u_scheme, "tls+tcp4") == 0) { af = NNG_AF_INET; - } else if (strcmp(url->u_scheme, "tls+tcp6") == 0) { + } else if (strcmp(surl->u_scheme, "tls+tcp6") == 0) { af = NNG_AF_INET6; } else { return (NNG_EADDRINVAL); } + if ((src = nni_alloc(len + 1)) == NULL) { + return (NNG_ENOMEM); + } + memcpy(src, surl->u_hostname, len); + src[len] = '\0'; + + if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { + nni_free(src, len + 1); + return (rv); + } + + nni_tcp_resolv(src, 0, af, 1, aio); + nni_aio_wait(aio); + if ((rv = nni_aio_result(aio)) == 0) { + nni_aio_get_sockaddr(aio, sa); + } + nni_aio_fini(aio); + nni_free(src, len + 1); + return (rv); +} + +static int +tlstran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer) +{ + tlstran_ep * ep; + int rv; + nng_sockaddr srcsa; + nni_sock * sock = nni_dialer_sock(ndialer); + nni_url myurl; + // Check for invalid URL components. if ((strlen(url->u_path) != 0) && (strcmp(url->u_path, "/") != 0)) { return (NNG_EADDRINVAL); @@ -717,6 +730,11 @@ tlstran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer) (strlen(url->u_port) == 0)) { return (NNG_EADDRINVAL); } + + if ((rv = tlstran_url_parse_source(&myurl, &srcsa, url)) != 0) { + return (NNG_EADDRINVAL); + } + if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { return (NNG_ENOMEM); } @@ -726,56 +744,20 @@ tlstran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer) ep->authmode = NNG_TLS_AUTH_MODE_REQUIRED; ep->url = url; - ep->af = af; ep->proto = nni_sock_proto_id(sock); ep->ndialer = ndialer; - // Detect an embedded local interface name in the hostname. This - // syntax is only valid with dialers. - if ((host = strchr(url->u_hostname, ';')) != NULL) { - size_t len; - char * src = NULL; - nni_aio *aio; - len = (uintptr_t) host - (uintptr_t) url->u_hostname; - host++; - if ((len < 2) || (strlen(host) == 0)) { - tlstran_ep_fini(ep); - return (NNG_EADDRINVAL); - } - if ((src = nni_alloc(len + 1)) == NULL) { - tlstran_ep_fini(ep); - return (NNG_ENOMEM); - } - memcpy(src, url->u_hostname, len); - src[len] = 0; - - if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { - tlstran_ep_fini(ep); - nni_strfree(src); - return (rv); - } - nni_aio_set_input(aio, 0, &srcsa); - nni_tcp_resolv(src, 0, af, 1, aio); - nni_aio_wait(aio); - rv = nni_aio_result(aio); - nni_aio_fini(aio); - nni_strfree(src); - ep->host = host; - } else { - srcsa.s_family = NNG_AF_UNSPEC; - ep->host = url->u_hostname; - rv = 0; + if ((rv != 0) || + ((rv = nng_stream_dialer_alloc_url(&ep->dialer, &myurl)) != 0)) { + tlstran_ep_fini(ep); + return (rv); } - - if ((rv != 0) || ((rv = nng_tls_dialer_alloc(&ep->dialer)) != 0) || - ((rv = nng_tls_dialer_setopt(ep->dialer, NNG_OPT_TLS_AUTH_MODE, - &ep->authmode, sizeof(ep->authmode))) != 0) || - ((rv = nng_tls_dialer_setopt(ep->dialer, NNG_OPT_TLS_SERVER_NAME, - ep->host, strlen(ep->host) + 1)) != 0)) { + if ((srcsa.s_family != NNG_AF_UNSPEC) && + ((rv = nni_stream_dialer_setx(ep->dialer, NNG_OPT_LOCADDR, &srcsa, + sizeof(srcsa), NNI_TYPE_SOCKADDR)) != 0)) { tlstran_ep_fini(ep); return (rv); } - *dp = ep; return (0); } @@ -841,9 +823,11 @@ tlstran_ep_init_listener(void **lp, nni_url *url, nni_listener *nlistener) rv = nni_aio_result(aio); nni_aio_fini(aio); - if ((rv != 0) || ((rv = nng_tls_listener_alloc(&ep->listener)) != 0) || - ((rv = nng_tls_listener_setopt(ep->listener, NNG_OPT_TLS_AUTH_MODE, - &ep->authmode, sizeof(ep->authmode))) != 0)) { + if ((rv != 0) || + ((rv = nng_stream_listener_alloc_url(&ep->listener, url)) != 0) || + ((rv = nni_stream_listener_setx(ep->listener, + NNG_OPT_TLS_AUTH_MODE, &ep->authmode, sizeof(ep->authmode), + NNI_TYPE_INT32)) != 0)) { tlstran_ep_fini(ep); return (rv); } @@ -876,8 +860,7 @@ tlstran_ep_connect(void *arg, nni_aio *aio) return; } p->useraio = aio; - nni_aio_set_input(p->rslvaio, 0, &p->sa); - nni_tcp_resolv(ep->host, ep->url->u_port, ep->af, 0, p->rslvaio); + nng_stream_dialer_dial(ep->dialer, p->connaio); nni_mtx_unlock(&ep->mtx); } @@ -888,7 +871,7 @@ tlstran_ep_bind(void *arg) int rv; nni_mtx_lock(&ep->mtx); - rv = nng_tls_listener_listen(ep->listener, &ep->sa); + rv = nng_stream_listener_listen(ep->listener); nni_mtx_unlock(&ep->mtx); return (rv); @@ -919,7 +902,7 @@ tlstran_ep_accept(void *arg, nni_aio *aio) } p->useraio = aio; - nng_tls_listener_accept(ep->listener, p->connaio); + nng_stream_listener_accept(ep->listener, p->connaio); nni_mtx_unlock(&ep->mtx); } @@ -929,8 +912,7 @@ tlstran_ep_set_recvmaxsz(void *arg, const void *v, size_t sz, nni_opt_type t) tlstran_ep *ep = arg; size_t val; int rv; - if (((rv = nni_copyin_size(&val, v, sz, 0, NNI_MAXSZ, t)) == 0) && - (ep != NULL)) { + if ((rv = nni_copyin_size(&val, v, sz, 0, NNI_MAXSZ, t)) == 0) { nni_mtx_lock(&ep->mtx); ep->rcvmax = val; nni_mtx_unlock(&ep->mtx); @@ -963,7 +945,7 @@ tlstran_ep_get_url(void *arg, void *v, size_t *szp, nni_opt_type t) if (ep->dialer != NULL) { return (nni_copyout_str(ep->url->u_rawurl, v, szp, t)); } - rv = nni_tls_listener_getopt( + rv = nni_stream_listener_getx( ep->listener, NNG_OPT_LOCADDR, &sa, &sz, NNI_TYPE_SOCKADDR); if (rv != 0) { return (rv); @@ -990,12 +972,19 @@ tlstran_pipe_getopt( tlstran_pipe *p = arg; int rv; - if ((rv = nni_tls_get(p->tls, name, buf, szp, t)) == NNG_ENOTSUP) { + if ((rv = nni_stream_getx(p->tls, name, buf, szp, t)) == NNG_ENOTSUP) { rv = nni_getopt(tlstran_pipe_opts, name, p, buf, szp, t); } return (rv); } +static int +tlstran_check_recvmaxsz(const void *v, size_t sz, nni_type t) +{ + size_t val; + return (nni_copyin_size(&val, v, sz, 0, NNI_MAXSZ, t)); +} + static nni_tran_pipe_ops tlstran_pipe_ops = { .p_init = tlstran_pipe_init, .p_fini = tlstran_pipe_fini, @@ -1023,6 +1012,16 @@ static nni_option tlstran_ep_options[] = { }, }; +static nni_chkoption tlstran_checkopts[] = { + { + .o_name = NNG_OPT_RECVMAXSZ, + .o_check = tlstran_check_recvmaxsz, + }, + { + .o_name = NULL, + }, +}; + static int tlstran_dialer_getopt( void *arg, const char *name, void *buf, size_t *szp, nni_type t) @@ -1030,7 +1029,7 @@ tlstran_dialer_getopt( int rv; tlstran_ep *ep = arg; - rv = nni_tls_dialer_getopt(ep->dialer, name, buf, szp, t); + rv = nni_stream_dialer_getx(ep->dialer, name, buf, szp, t); if (rv == NNG_ENOTSUP) { rv = nni_getopt(tlstran_ep_options, name, ep, buf, szp, t); } @@ -1044,7 +1043,7 @@ tlstran_dialer_setopt( int rv; tlstran_ep *ep = arg; - rv = nni_tls_dialer_setopt( + rv = nni_stream_dialer_setx( ep != NULL ? ep->dialer : NULL, name, buf, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_setopt(tlstran_ep_options, name, ep, buf, sz, t); @@ -1059,7 +1058,7 @@ tlstran_listener_getopt( int rv; tlstran_ep *ep = arg; - rv = nni_tls_listener_getopt(ep->listener, name, buf, szp, t); + rv = nni_stream_listener_getx(ep->listener, name, buf, szp, t); if (rv == NNG_ENOTSUP) { rv = nni_getopt(tlstran_ep_options, name, ep, buf, szp, t); } @@ -1073,7 +1072,7 @@ tlstran_listener_setopt( int rv; tlstran_ep *ep = arg; - rv = nni_tls_listener_setopt( + rv = nni_stream_listener_setx( ep != NULL ? ep->listener : NULL, name, buf, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_setopt(tlstran_ep_options, name, ep, buf, sz, t); @@ -1081,6 +1080,17 @@ tlstran_listener_setopt( return (rv); } +static int +tlstran_checkopt(const char *name, const void *buf, size_t sz, nni_type t) +{ + int rv; + rv = nni_chkopt(tlstran_checkopts, name, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_stream_checkopt("tls+tcp", name, buf, sz, t); + } + return (rv); +} + static nni_tran_dialer_ops tlstran_dialer_ops = { .d_init = tlstran_ep_init_dialer, .d_fini = tlstran_ep_fini, @@ -1108,6 +1118,7 @@ static nni_tran tls_tran = { .tran_pipe = &tlstran_pipe_ops, .tran_init = tlstran_init, .tran_fini = tlstran_fini, + .tran_checkopt = tlstran_checkopt, }; static nni_tran tls4_tran = { @@ -1118,6 +1129,7 @@ static nni_tran tls4_tran = { .tran_pipe = &tlstran_pipe_ops, .tran_init = tlstran_init, .tran_fini = tlstran_fini, + .tran_checkopt = tlstran_checkopt, }; static nni_tran tls6_tran = { @@ -1128,6 +1140,7 @@ static nni_tran tls6_tran = { .tran_pipe = &tlstran_pipe_ops, .tran_init = tlstran_init, .tran_fini = tlstran_fini, + .tran_checkopt = tlstran_checkopt, }; int diff --git a/src/transport/ws/websocket.c b/src/transport/ws/websocket.c index bf10f7e0..3424480a 100644 --- a/src/transport/ws/websocket.c +++ b/src/transport/ws/websocket.c @@ -1,7 +1,7 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> -// Copyright 2018 Devolutions <info@devolutions.net> +// Copyright 2019 Devolutions <info@devolutions.net> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -26,56 +26,43 @@ typedef struct ws_dialer ws_dialer; typedef struct ws_listener ws_listener; typedef struct ws_pipe ws_pipe; -typedef struct ws_hdr { - nni_list_node node; - char * name; - char * value; -} ws_hdr; - struct ws_dialer { - uint16_t lproto; // local protocol - uint16_t rproto; // remote protocol - size_t rcvmax; - char * prname; - nni_list aios; - nni_mtx mtx; - nni_aio * connaio; - nni_ws_dialer *dialer; - nni_list headers; // req headers - bool started; - nni_dialer * ndialer; + uint16_t lproto; // local protocol + uint16_t rproto; // remote protocol + nni_list aios; + nni_mtx mtx; + nni_aio * connaio; + nng_stream_dialer *dialer; + bool started; + nni_dialer * ndialer; }; struct ws_listener { - uint16_t lproto; // local protocol - uint16_t rproto; // remote protocol - size_t rcvmax; - char * prname; - nni_list aios; - nni_mtx mtx; - nni_aio * accaio; - nni_ws_listener *listener; - nni_list headers; // res headers - bool started; - nni_listener * nlistener; + uint16_t lproto; // local protocol + uint16_t rproto; // remote protocol + nni_list aios; + nni_mtx mtx; + nni_aio * accaio; + nng_stream_listener *listener; + bool started; + nni_listener * nlistener; }; struct ws_pipe { - nni_mtx mtx; - nni_pipe *npipe; - size_t rcvmax; - bool closed; - uint16_t rproto; - uint16_t lproto; - nni_aio * user_txaio; - nni_aio * user_rxaio; - nni_aio * txaio; - nni_aio * rxaio; - nni_ws * ws; + nni_mtx mtx; + nni_pipe * npipe; + bool closed; + uint16_t rproto; + uint16_t lproto; + nni_aio * user_txaio; + nni_aio * user_rxaio; + nni_aio * txaio; + nni_aio * rxaio; + nng_stream *ws; }; static void -ws_pipe_send_cb(void *arg) +wstran_pipe_send_cb(void *arg) { ws_pipe *p = arg; nni_aio *taio; @@ -98,7 +85,7 @@ ws_pipe_send_cb(void *arg) } static void -ws_pipe_recv_cb(void *arg) +wstran_pipe_recv_cb(void *arg) { ws_pipe *p = arg; nni_aio *raio = p->rxaio; @@ -124,7 +111,7 @@ ws_pipe_recv_cb(void *arg) } static void -ws_pipe_recv_cancel(nni_aio *aio, void *arg, int rv) +wstran_pipe_recv_cancel(nni_aio *aio, void *arg, int rv) { ws_pipe *p = arg; nni_mtx_lock(&p->mtx); @@ -139,7 +126,7 @@ ws_pipe_recv_cancel(nni_aio *aio, void *arg, int rv) } static void -ws_pipe_recv(void *arg, nni_aio *aio) +wstran_pipe_recv(void *arg, nni_aio *aio) { ws_pipe *p = arg; int rv; @@ -148,18 +135,18 @@ ws_pipe_recv(void *arg, nni_aio *aio) return; } nni_mtx_lock(&p->mtx); - if ((rv = nni_aio_schedule(aio, ws_pipe_recv_cancel, p)) != 0) { + if ((rv = nni_aio_schedule(aio, wstran_pipe_recv_cancel, p)) != 0) { nni_mtx_unlock(&p->mtx); nni_aio_finish_error(aio, rv); return; } p->user_rxaio = aio; - nni_ws_recv_msg(p->ws, p->rxaio); + nng_stream_recv(p->ws, p->rxaio); nni_mtx_unlock(&p->mtx); } static void -ws_pipe_send_cancel(nni_aio *aio, void *arg, int rv) +wstran_pipe_send_cancel(nni_aio *aio, void *arg, int rv) { ws_pipe *p = arg; nni_mtx_lock(&p->mtx); @@ -174,7 +161,7 @@ ws_pipe_send_cancel(nni_aio *aio, void *arg, int rv) } static void -ws_pipe_send(void *arg, nni_aio *aio) +wstran_pipe_send(void *arg, nni_aio *aio) { ws_pipe *p = arg; int rv; @@ -183,7 +170,7 @@ ws_pipe_send(void *arg, nni_aio *aio) return; } nni_mtx_lock(&p->mtx); - if ((rv = nni_aio_schedule(aio, ws_pipe_send_cancel, p)) != 0) { + if ((rv = nni_aio_schedule(aio, wstran_pipe_send_cancel, p)) != 0) { nni_mtx_unlock(&p->mtx); nni_aio_finish_error(aio, rv); return; @@ -192,12 +179,12 @@ ws_pipe_send(void *arg, nni_aio *aio) nni_aio_set_msg(p->txaio, nni_aio_get_msg(aio)); nni_aio_set_msg(aio, NULL); - nni_ws_send_msg(p->ws, p->txaio); + nng_stream_send(p->ws, p->txaio); nni_mtx_unlock(&p->mtx); } static void -ws_pipe_stop(void *arg) +wstran_pipe_stop(void *arg) { ws_pipe *p = arg; @@ -206,7 +193,7 @@ ws_pipe_stop(void *arg) } static int -ws_pipe_init(void *arg, nni_pipe *npipe) +wstran_pipe_init(void *arg, nni_pipe *npipe) { ws_pipe *p = arg; p->npipe = npipe; @@ -214,22 +201,20 @@ ws_pipe_init(void *arg, nni_pipe *npipe) } static void -ws_pipe_fini(void *arg) +wstran_pipe_fini(void *arg) { ws_pipe *p = arg; nni_aio_fini(p->rxaio); nni_aio_fini(p->txaio); - if (p->ws) { - nni_ws_fini(p->ws); - } + nng_stream_free(p->ws); nni_mtx_fini(&p->mtx); NNI_FREE_STRUCT(p); } static void -ws_pipe_close(void *arg) +wstran_pipe_close(void *arg) { ws_pipe *p = arg; @@ -237,12 +222,12 @@ ws_pipe_close(void *arg) nni_aio_close(p->txaio); nni_mtx_lock(&p->mtx); - nni_ws_close(p->ws); + nng_stream_close(p->ws); nni_mtx_unlock(&p->mtx); } static int -ws_pipe_alloc(ws_pipe **pipep, void *ws) +wstran_pipe_alloc(ws_pipe **pipep, void *ws) { ws_pipe *p; int rv; @@ -253,9 +238,9 @@ ws_pipe_alloc(ws_pipe **pipep, void *ws) nni_mtx_init(&p->mtx); // Initialize AIOs. - if (((rv = nni_aio_init(&p->txaio, ws_pipe_send_cb, p)) != 0) || - ((rv = nni_aio_init(&p->rxaio, ws_pipe_recv_cb, p)) != 0)) { - ws_pipe_fini(p); + if (((rv = nni_aio_init(&p->txaio, wstran_pipe_send_cb, p)) != 0) || + ((rv = nni_aio_init(&p->rxaio, wstran_pipe_recv_cb, p)) != 0)) { + wstran_pipe_fini(p); return (rv); } p->ws = ws; @@ -265,46 +250,20 @@ ws_pipe_alloc(ws_pipe **pipep, void *ws) } static uint16_t -ws_pipe_peer(void *arg) +wstran_pipe_peer(void *arg) { ws_pipe *p = arg; return (p->rproto); } -// We have very different approaches for server and client. -// Servers use the HTTP server framework, and a request methodology. - -static int -ws_hook(void *arg, nni_http_req *req, nni_http_res *res) -{ - ws_listener *l = arg; - ws_hdr * h; - NNI_ARG_UNUSED(req); - - // Eventually we'll want user customizable hooks. - // For now we just set the headers we want. - - NNI_LIST_FOREACH (&l->headers, h) { - int rv; - rv = nng_http_res_set_header(res, h->name, h->value); - if (rv != 0) { - return (rv); - } - } - return (0); -} - static int ws_listener_bind(void *arg) { ws_listener *l = arg; int rv; - nni_ws_listener_set_maxframe(l->listener, l->rcvmax); - nni_ws_listener_hook(l->listener, ws_hook, l); - - if ((rv = nni_ws_listener_listen(l->listener)) == 0) { + if ((rv = nng_stream_listener_listen(l->listener)) == 0) { l->started = true; } return (rv); @@ -324,7 +283,7 @@ ws_listener_cancel(nni_aio *aio, void *arg, int rv) } static void -ws_listener_accept(void *arg, nni_aio *aio) +wstran_listener_accept(void *arg, nni_aio *aio) { ws_listener *l = arg; int rv; @@ -343,13 +302,13 @@ ws_listener_accept(void *arg, nni_aio *aio) } nni_list_append(&l->aios, aio); if (aio == nni_list_first(&l->aios)) { - nni_ws_listener_accept(l->listener, l->accaio); + nng_stream_listener_accept(l->listener, l->accaio); } nni_mtx_unlock(&l->mtx); } static void -ws_dialer_cancel(nni_aio *aio, void *arg, int rv) +wstran_dialer_cancel(nni_aio *aio, void *arg, int rv) { ws_dialer *d = arg; @@ -362,7 +321,7 @@ ws_dialer_cancel(nni_aio *aio, void *arg, int rv) } static void -ws_dialer_connect(void *arg, nni_aio *aio) +wstran_dialer_connect(void *arg, nni_aio *aio) { ws_dialer *d = arg; int rv; @@ -370,20 +329,9 @@ ws_dialer_connect(void *arg, nni_aio *aio) if (nni_aio_begin(aio) != 0) { return; } - if (!d->started) { - ws_hdr *h; - NNI_LIST_FOREACH (&d->headers, h) { - int rv = - nni_ws_dialer_header(d->dialer, h->name, h->value); - if (rv != 0) { - nni_aio_finish_error(aio, rv); - return; - } - } - } nni_mtx_lock(&d->mtx); - if ((rv = nni_aio_schedule(aio, ws_dialer_cancel, d)) != 0) { + if ((rv = nni_aio_schedule(aio, wstran_dialer_cancel, d)) != 0) { nni_mtx_unlock(&d->mtx); nni_aio_finish_error(aio, rv); return; @@ -391,225 +339,11 @@ ws_dialer_connect(void *arg, nni_aio *aio) NNI_ASSERT(nni_list_empty(&d->aios)); d->started = true; nni_list_append(&d->aios, aio); - nni_ws_dialer_set_maxframe(d->dialer, d->rcvmax); - nni_ws_dialer_dial(d->dialer, d->connaio); - nni_mtx_unlock(&d->mtx); -} - -static int -ws_check_string(const void *v, size_t sz, nni_opt_type t) -{ - if ((t != NNI_TYPE_OPAQUE) && (t != NNI_TYPE_STRING)) { - return (NNG_EBADTYPE); - } - if (nni_strnlen(v, sz) >= sz) { - return (NNG_EINVAL); - } - return (0); -} - -static int -ws_dialer_set_recvmaxsz(void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_dialer *d = arg; - size_t val; - int rv; - - if (((rv = nni_copyin_size(&val, v, sz, 0, NNI_MAXSZ, t)) == 0) && - (d != NULL)) { - nni_mtx_lock(&d->mtx); - d->rcvmax = val; - nni_mtx_unlock(&d->mtx); - nni_ws_dialer_set_maxframe(d->dialer, val); - } - return (rv); -} - -static int -ws_dialer_get_recvmaxsz(void *arg, void *v, size_t *szp, nni_opt_type t) -{ - ws_dialer *d = arg; - int rv; - nni_mtx_lock(&d->mtx); - rv = nni_copyout_size(d->rcvmax, v, szp, t); + nng_stream_dialer_dial(d->dialer, d->connaio); nni_mtx_unlock(&d->mtx); - return (rv); -} - -static int -ws_listener_set_recvmaxsz(void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_listener *l = arg; - size_t val; - int rv; - - if (((rv = nni_copyin_size(&val, v, sz, 0, NNI_MAXSZ, t)) == 0) && - (l != NULL)) { - nni_mtx_lock(&l->mtx); - l->rcvmax = val; - nni_mtx_unlock(&l->mtx); - nni_ws_listener_set_maxframe(l->listener, val); - } - return (rv); -} - -static int -ws_listener_get_recvmaxsz(void *arg, void *v, size_t *szp, nni_opt_type t) -{ - ws_listener *l = arg; - int rv; - nni_mtx_lock(&l->mtx); - rv = nni_copyout_size(l->rcvmax, v, szp, t); - nni_mtx_unlock(&l->mtx); - return (rv); -} - -static int -ws_set_headers(nni_list *headers, const char *v) -{ - char * dupstr; - size_t duplen; - char * name; - char * value; - char * nl; - nni_list l; - ws_hdr * h; - int rv; - - NNI_LIST_INIT(&l, ws_hdr, node); - if ((dupstr = nni_strdup(v)) == NULL) { - return (NNG_ENOMEM); - } - duplen = strlen(dupstr) + 1; // so we can free it later - name = dupstr; - for (;;) { - if ((value = strchr(name, ':')) == NULL) { - // Note that this also means that if - // a bare word is present, we ignore it. - break; - } - *value = '\0'; - value++; - while (*value == ' ') { - // Skip leading whitespace. Not strictly - // necessary, but still a good idea. - value++; - } - nl = value; - // Find the end of the line -- should be CRLF, but can - // also be unterminated or just LF if user - while ((*nl != '\0') && (*nl != '\r') && (*nl != '\n')) { - nl++; - } - while ((*nl == '\r') || (*nl == '\n')) { - *nl = '\0'; - nl++; - } - - if ((h = NNI_ALLOC_STRUCT(h)) == NULL) { - rv = NNG_ENOMEM; - goto done; - } - nni_list_append(&l, h); - if (((h->name = nni_strdup(name)) == NULL) || - ((h->value = nni_strdup(value)) == NULL)) { - rv = NNG_ENOMEM; - goto done; - } - - name = nl; - } - - while ((h = nni_list_first(headers)) != NULL) { - nni_list_remove(headers, h); - nni_strfree(h->name); - nni_strfree(h->value); - NNI_FREE_STRUCT(h); - } - while ((h = nni_list_first(&l)) != NULL) { - nni_list_remove(&l, h); - nni_list_append(headers, h); - } - rv = 0; - -done: - while ((h = nni_list_first(&l)) != NULL) { - nni_list_remove(&l, h); - nni_strfree(h->name); - nni_strfree(h->value); - NNI_FREE_STRUCT(h); - } - nni_free(dupstr, duplen); - return (rv); -} - -static int -ws_dialer_set_reqhdrs(void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_dialer *d = arg; - int rv; - - if (((rv = ws_check_string(v, sz, t)) == 0) && (d != NULL)) { - if (d->started) { - return (NNG_EBUSY); - } - nni_mtx_lock(&d->mtx); - rv = ws_set_headers(&d->headers, v); - nni_mtx_unlock(&d->mtx); - } - return (rv); -} - -static int -ws_listener_set_reshdrs(void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_listener *l = arg; - int rv; - - if (((rv = ws_check_string(v, sz, t)) == 0) && (l != NULL)) { - if (l->started) { - return (NNG_EBUSY); - } - nni_mtx_lock(&l->mtx); - rv = ws_set_headers(&l->headers, v); - nni_mtx_unlock(&l->mtx); - } - return (rv); -} - -static int -ws_pipe_get_reshdrs(void *arg, void *v, size_t *szp, nni_opt_type t) -{ - ws_pipe * p = arg; - const char *s; - - if ((s = nni_ws_response_headers(p->ws)) == NULL) { - return (NNG_ENOMEM); - } - return (nni_copyout_str(s, v, szp, t)); -} - -static int -ws_pipe_get_reqhdrs(void *arg, void *v, size_t *szp, nni_opt_type t) -{ - ws_pipe * p = arg; - const char *s; - - if ((s = nni_ws_request_headers(p->ws)) == NULL) { - return (NNG_ENOMEM); - } - return (nni_copyout_str(s, v, szp, t)); } static const nni_option ws_pipe_options[] = { - { - .o_name = NNG_OPT_WS_REQUEST_HEADERS, - .o_get = ws_pipe_get_reqhdrs, - }, - { - .o_name = NNG_OPT_WS_RESPONSE_HEADERS, - .o_get = ws_pipe_get_reshdrs, - }, // terminate list { .o_name = NULL, @@ -617,113 +351,62 @@ static const nni_option ws_pipe_options[] = { }; static int -ws_pipe_getopt(void *arg, const char *name, void *buf, size_t *szp, nni_type t) +wstran_pipe_getopt( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { ws_pipe *p = arg; int rv; - if ((rv = nni_ws_getopt(p->ws, name, buf, szp, t)) == NNG_ENOTSUP) { + if ((rv = nni_stream_getx(p->ws, name, buf, szp, t)) == NNG_ENOTSUP) { rv = nni_getopt(ws_pipe_options, name, p, buf, szp, t); } return (rv); } static nni_tran_pipe_ops ws_pipe_ops = { - .p_init = ws_pipe_init, - .p_fini = ws_pipe_fini, - .p_stop = ws_pipe_stop, - .p_send = ws_pipe_send, - .p_recv = ws_pipe_recv, - .p_close = ws_pipe_close, - .p_peer = ws_pipe_peer, - .p_getopt = ws_pipe_getopt, -}; - -static nni_option ws_dialer_options[] = { - { - .o_name = NNG_OPT_RECVMAXSZ, - .o_get = ws_dialer_get_recvmaxsz, - .o_set = ws_dialer_set_recvmaxsz, - }, - { - .o_name = NNG_OPT_WS_REQUEST_HEADERS, - .o_set = ws_dialer_set_reqhdrs, - }, - // terminate list - { - .o_name = NULL, - }, -}; - -static nni_option ws_listener_options[] = { - { - .o_name = NNG_OPT_RECVMAXSZ, - .o_get = ws_listener_get_recvmaxsz, - .o_set = ws_listener_set_recvmaxsz, - }, - { - .o_name = NNG_OPT_WS_RESPONSE_HEADERS, - .o_set = ws_listener_set_reshdrs, - }, - // terminate list - { - .o_name = NULL, - }, + .p_init = wstran_pipe_init, + .p_fini = wstran_pipe_fini, + .p_stop = wstran_pipe_stop, + .p_send = wstran_pipe_send, + .p_recv = wstran_pipe_recv, + .p_close = wstran_pipe_close, + .p_peer = wstran_pipe_peer, + .p_getopt = wstran_pipe_getopt, }; static void -ws_dialer_fini(void *arg) +wstran_dialer_fini(void *arg) { ws_dialer *d = arg; - ws_hdr * hdr; nni_aio_stop(d->connaio); - if (d->dialer != NULL) { - nni_ws_dialer_fini(d->dialer); - } + nng_stream_dialer_free(d->dialer); nni_aio_fini(d->connaio); - while ((hdr = nni_list_first(&d->headers)) != NULL) { - nni_list_remove(&d->headers, hdr); - nni_strfree(hdr->name); - nni_strfree(hdr->value); - NNI_FREE_STRUCT(hdr); - } - nni_strfree(d->prname); nni_mtx_fini(&d->mtx); NNI_FREE_STRUCT(d); } static void -ws_listener_fini(void *arg) +wstran_listener_fini(void *arg) { ws_listener *l = arg; - ws_hdr * hdr; nni_aio_stop(l->accaio); - if (l->listener != NULL) { - nni_ws_listener_fini(l->listener); - } + nng_stream_listener_free(l->listener); nni_aio_fini(l->accaio); - while ((hdr = nni_list_first(&l->headers)) != NULL) { - nni_list_remove(&l->headers, hdr); - nni_strfree(hdr->name); - nni_strfree(hdr->value); - NNI_FREE_STRUCT(hdr); - } - nni_strfree(l->prname); nni_mtx_fini(&l->mtx); NNI_FREE_STRUCT(l); } static void -ws_connect_cb(void *arg) +wstran_connect_cb(void *arg) { - ws_dialer *d = arg; - ws_pipe * p; - nni_aio * caio = d->connaio; - nni_aio * uaio; - int rv; - nni_ws * ws = NULL; + ws_dialer * d = arg; + ws_pipe * p; + nni_aio * caio = d->connaio; + nni_aio * uaio; + int rv; + nng_stream *ws = NULL; nni_mtx_lock(&d->mtx); if (nni_aio_result(caio) == 0) { @@ -731,9 +414,7 @@ ws_connect_cb(void *arg) } if ((uaio = nni_list_first(&d->aios)) == NULL) { // The client stopped caring about this! - if (ws != NULL) { - nni_ws_fini(ws); - } + nng_stream_free(ws); nni_mtx_unlock(&d->mtx); return; } @@ -741,11 +422,10 @@ ws_connect_cb(void *arg) NNI_ASSERT(nni_list_empty(&d->aios)); if ((rv = nni_aio_result(caio)) != 0) { nni_aio_finish_error(uaio, rv); - } else if ((rv = ws_pipe_alloc(&p, ws)) != 0) { - nni_ws_fini(ws); + } else if ((rv = wstran_pipe_alloc(&p, ws)) != 0) { + nng_stream_free(ws); nni_aio_finish_error(uaio, rv); } else { - p->rcvmax = d->rcvmax; p->rproto = d->rproto; p->lproto = d->lproto; @@ -756,25 +436,25 @@ ws_connect_cb(void *arg) } static void -ws_dialer_close(void *arg) +wstran_dialer_close(void *arg) { ws_dialer *d = arg; nni_aio_close(d->connaio); - nni_ws_dialer_close(d->dialer); + nng_stream_dialer_close(d->dialer); } static void -ws_listener_close(void *arg) +wstran_listener_close(void *arg) { ws_listener *l = arg; nni_aio_close(l->accaio); - nni_ws_listener_close(l->listener); + nng_stream_listener_close(l->listener); } static void -ws_accept_cb(void *arg) +wstran_accept_cb(void *arg) { ws_listener *l = arg; nni_aio * aaio = l->accaio; @@ -789,16 +469,15 @@ ws_accept_cb(void *arg) nni_aio_finish_error(uaio, rv); } } else { - nni_ws *ws = nni_aio_get_output(aaio, 0); + nng_stream *ws = nni_aio_get_output(aaio, 0); if (uaio != NULL) { ws_pipe *p; // Make a pipe nni_aio_list_remove(uaio); - if ((rv = ws_pipe_alloc(&p, ws)) != 0) { - nni_ws_close(ws); + if ((rv = wstran_pipe_alloc(&p, ws)) != 0) { + nng_stream_close(ws); nni_aio_finish_error(uaio, rv); } else { - p->rcvmax = l->rcvmax; p->rproto = l->rproto; p->lproto = l->lproto; @@ -808,37 +487,40 @@ ws_accept_cb(void *arg) } } if (!nni_list_empty(&l->aios)) { - nni_ws_listener_accept(l->listener, aaio); + nng_stream_listener_accept(l->listener, aaio); } nni_mtx_unlock(&l->mtx); } static int -ws_dialer_init(void **dp, nni_url *url, nni_dialer *ndialer) +wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer) { - ws_dialer * d; - nni_sock * s = nni_dialer_sock(ndialer); - const char *n; - int rv; + ws_dialer *d; + nni_sock * s = nni_dialer_sock(ndialer); + int rv; + char prname[64]; if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { return (NNG_ENOMEM); } nni_mtx_init(&d->mtx); - NNI_LIST_INIT(&d->headers, ws_hdr, node); nni_aio_list_init(&d->aios); d->lproto = nni_sock_proto_id(s); d->rproto = nni_sock_peer_id(s); d->ndialer = ndialer; - n = nni_sock_peer_name(s); - if (((rv = nni_ws_dialer_init(&d->dialer, url)) != 0) || - ((rv = nni_aio_init(&d->connaio, ws_connect_cb, d)) != 0) || - ((rv = nni_asprintf(&d->prname, "%s.sp.nanomsg.org", n)) != 0) || - ((rv = nni_ws_dialer_proto(d->dialer, d->prname)) != 0)) { - ws_dialer_fini(d); + snprintf(prname, sizeof(prname), "%s.sp.nanomsg.org", + nni_sock_peer_name(s)); + + if (((rv = nni_ws_dialer_alloc(&d->dialer, url)) != 0) || + ((rv = nni_aio_init(&d->connaio, wstran_connect_cb, d)) != 0) || + ((rv = nng_stream_dialer_set_bool( + d->dialer, NNI_OPT_WS_MSGMODE, true)) != 0) || + ((rv = nng_stream_dialer_set_string( + d->dialer, NNG_OPT_WS_PROTOCOL, prname)) != 0)) { + wstran_dialer_fini(d); return (rv); } @@ -847,31 +529,34 @@ ws_dialer_init(void **dp, nni_url *url, nni_dialer *ndialer) } static int -ws_listener_init(void **lp, nni_url *url, nni_listener *nlistener) +wstran_listener_init(void **lp, nng_url *url, nni_listener *nlistener) { ws_listener *l; - const char * n; int rv; - nni_sock * sock = nni_listener_sock(nlistener); + nni_sock * s = nni_listener_sock(nlistener); + char prname[64]; if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { return (NNG_ENOMEM); } nni_mtx_init(&l->mtx); - NNI_LIST_INIT(&l->headers, ws_hdr, node); nni_aio_list_init(&l->aios); - l->lproto = nni_sock_proto_id(sock); - l->rproto = nni_sock_peer_id(sock); - n = nni_sock_proto_name(sock); + l->lproto = nni_sock_proto_id(s); + l->rproto = nni_sock_peer_id(s); l->nlistener = nlistener; - if (((rv = nni_ws_listener_init(&l->listener, url)) != 0) || - ((rv = nni_aio_init(&l->accaio, ws_accept_cb, l)) != 0) || - ((rv = nni_asprintf(&l->prname, "%s.sp.nanomsg.org", n)) != 0) || - ((rv = nni_ws_listener_proto(l->listener, l->prname)) != 0)) { - ws_listener_fini(l); + snprintf(prname, sizeof(prname), "%s.sp.nanomsg.org", + nni_sock_proto_name(s)); + + if (((rv = nni_ws_listener_alloc(&l->listener, url)) != 0) || + ((rv = nni_aio_init(&l->accaio, wstran_accept_cb, l)) != 0) || + ((rv = nng_stream_listener_set_bool( + l->listener, NNI_OPT_WS_MSGMODE, true)) != 0) || + ((rv = nng_stream_listener_set_string( + l->listener, NNG_OPT_WS_PROTOCOL, prname)) != 0)) { + wstran_listener_fini(l); return (rv); } *lp = l; @@ -879,350 +564,143 @@ ws_listener_init(void **lp, nni_url *url, nni_listener *nlistener) } static int -ws_tran_init(void) +wstran_init(void) { return (0); } static void -ws_tran_fini(void) +wstran_fini(void) { } -static nni_tran_dialer_ops ws_dialer_ops = { - .d_init = ws_dialer_init, - .d_fini = ws_dialer_fini, - .d_connect = ws_dialer_connect, - .d_close = ws_dialer_close, - .d_options = ws_dialer_options, -}; - -static nni_tran_listener_ops ws_listener_ops = { - .l_init = ws_listener_init, - .l_fini = ws_listener_fini, - .l_bind = ws_listener_bind, - .l_accept = ws_listener_accept, - .l_close = ws_listener_close, - .l_options = ws_listener_options, -}; - -static nni_tran ws_tran = { - .tran_version = NNI_TRANSPORT_VERSION, - .tran_scheme = "ws", - .tran_dialer = &ws_dialer_ops, - .tran_listener = &ws_listener_ops, - .tran_pipe = &ws_pipe_ops, - .tran_init = ws_tran_init, - .tran_fini = ws_tran_fini, +static const nni_option wstran_ep_opts[] = { + // terminate list + { + .o_name = NULL, + }, }; -int -nng_ws_register(void) -{ - return (nni_tran_register(&ws_tran)); -} - -#ifdef NNG_TRANSPORT_WSS - -static int -wss_dialer_get_tlsconfig(void *arg, void *v, size_t *szp, nni_opt_type t) -{ - ws_dialer * d = arg; - nng_tls_config *tls; - int rv; - - if (((rv = nni_ws_dialer_get_tls(d->dialer, &tls)) != 0) || - ((rv = nni_copyout_ptr(tls, v, szp, t)) != 0)) { - return (rv); - } - return (0); -} - -static int -wss_listener_get_tlsconfig(void *arg, void *v, size_t *szp, nni_opt_type t) -{ - ws_listener * l = arg; - nng_tls_config *tls; - int rv; - - if (((rv = nni_ws_listener_get_tls(l->listener, &tls)) != 0) || - ((rv = nni_copyout_ptr(tls, v, szp, t)) != 0)) { - return (rv); - } - return (0); -} - -static int -wss_dialer_set_tlsconfig(void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_dialer * d = arg; - nng_tls_config *cfg; - int rv; - - if ((rv = nni_copyin_ptr((void **) &cfg, v, sz, t)) != 0) { - return (rv); - } - if (cfg == NULL) { - return (NNG_EINVAL); - } - if (d != NULL) { - rv = nni_ws_dialer_set_tls(d->dialer, cfg); - } - return (rv); -} - static int -wss_listener_set_tlsconfig(void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_listener * l = arg; - nng_tls_config *cfg; - int rv; - - if ((rv = nni_copyin_ptr((void **) &cfg, v, sz, t)) != 0) { - return (rv); - } - if (cfg == NULL) { - return (NNG_EINVAL); - } - if (l != NULL) { - rv = nni_ws_listener_set_tls(l->listener, cfg); - } - return (rv); -} - -static int -wss_dialer_set_cert_key_file( - void *arg, const void *v, size_t sz, nni_opt_type t) +wstran_dialer_getopt( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { ws_dialer *d = arg; int rv; - if (((rv = ws_check_string(v, sz, t)) == 0) && (d != NULL)) { - nng_tls_config *tls; - - if ((rv = nni_ws_dialer_get_tls(d->dialer, &tls)) != 0) { - return (rv); - } - rv = nng_tls_config_cert_key_file(tls, v, NULL); - nni_tls_config_fini(tls); + rv = nni_stream_dialer_getx(d->dialer, name, buf, szp, t); + if (rv == NNG_ENOTSUP) { + rv = nni_getopt(wstran_ep_opts, name, d, buf, szp, t); } return (rv); } static int -wss_listener_set_cert_key_file( - void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_listener *l = arg; - int rv; - - if (((rv = ws_check_string(v, sz, t)) == 0) && (l != NULL)) { - nng_tls_config *tls; - - if ((rv = nni_ws_listener_get_tls(l->listener, &tls)) != 0) { - return (rv); - } - rv = nng_tls_config_cert_key_file(tls, v, NULL); - nni_tls_config_fini(tls); - } - return (rv); -} - -static int -wss_dialer_set_ca_file(void *arg, const void *v, size_t sz, nni_opt_type t) +wstran_dialer_setopt( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) { ws_dialer *d = arg; int rv; - if (((rv = ws_check_string(v, sz, t)) == 0) && (d != NULL)) { - nng_tls_config *tls; - - if ((rv = nni_ws_dialer_get_tls(d->dialer, &tls)) != 0) { - return (rv); - } - rv = nng_tls_config_ca_file(tls, v); - nni_tls_config_fini(tls); + rv = nni_stream_dialer_setx(d->dialer, name, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_setopt(wstran_ep_opts, name, d, buf, sz, t); } return (rv); } static int -wss_listener_set_ca_file(void *arg, const void *v, size_t sz, nni_opt_type t) +wstran_listener_getopt( + void *arg, const char *name, void *buf, size_t *szp, nni_type t) { ws_listener *l = arg; int rv; - if (((rv = ws_check_string(v, sz, t)) == 0) && (l != NULL)) { - nng_tls_config *tls; - - if ((rv = nni_ws_listener_get_tls(l->listener, &tls)) != 0) { - return (rv); - } - rv = nng_tls_config_ca_file(tls, v); - nni_tls_config_fini(tls); - } - return (rv); -} - -static int -wss_dialer_set_auth_mode(void *arg, const void *v, size_t sz, nni_opt_type t) -{ - ws_dialer *d = arg; - int rv; - int mode; - - rv = nni_copyin_int(&mode, v, sz, NNG_TLS_AUTH_MODE_NONE, - NNG_TLS_AUTH_MODE_REQUIRED, t); - - if ((rv == 0) && (d != NULL)) { - nng_tls_config *tls; - - if ((rv = nni_ws_dialer_get_tls(d->dialer, &tls)) != 0) { - return (rv); - } - rv = nng_tls_config_auth_mode(tls, mode); - nni_tls_config_fini(tls); + rv = nni_stream_listener_getx(l->listener, name, buf, szp, t); + if (rv == NNG_ENOTSUP) { + rv = nni_getopt(wstran_ep_opts, name, l, buf, szp, t); } return (rv); } static int -wss_listener_set_auth_mode(void *arg, const void *v, size_t sz, nni_opt_type t) +wstran_listener_setopt( + void *arg, const char *name, const void *buf, size_t sz, nni_type t) { ws_listener *l = arg; int rv; - int mode; - - rv = nni_copyin_int(&mode, v, sz, NNG_TLS_AUTH_MODE_NONE, - NNG_TLS_AUTH_MODE_REQUIRED, t); - if ((rv == 0) && (l != NULL)) { - nng_tls_config *tls; - - if ((rv = nni_ws_listener_get_tls(l->listener, &tls)) != 0) { - return (rv); - } - rv = nng_tls_config_auth_mode(tls, mode); - nni_tls_config_fini(tls); + rv = nni_stream_listener_setx(l->listener, name, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_setopt(wstran_ep_opts, name, l, buf, sz, t); } return (rv); } +static nni_chkoption wstran_checkopts[] = { + { + .o_name = NULL, + }, +}; + static int -wss_dialer_set_tls_server_name( - void *arg, const void *v, size_t sz, nni_opt_type t) +wstran_checkopt(const char *name, const void *buf, size_t sz, nni_type t) { - ws_dialer *d = arg; - int rv; - - if (((rv = ws_check_string(v, sz, t)) == 0) && (d != NULL)) { - nng_tls_config *tls; - - if ((rv = nni_ws_dialer_get_tls(d->dialer, &tls)) != 0) { - return (rv); - } - - rv = nng_tls_config_server_name(tls, v); - nni_tls_config_fini(tls); + int rv; + rv = nni_chkopt(wstran_checkopts, name, buf, sz, t); + if (rv == NNG_ENOTSUP) { + rv = nni_stream_checkopt("ws", name, buf, sz, t); } return (rv); } -static nni_option wss_dialer_options[] = { - { - .o_name = NNG_OPT_RECVMAXSZ, - .o_get = ws_dialer_get_recvmaxsz, - .o_set = ws_dialer_set_recvmaxsz, - }, - { - .o_name = NNG_OPT_WS_REQUEST_HEADERS, - .o_set = ws_dialer_set_reqhdrs, - }, - { - .o_name = NNG_OPT_TLS_CONFIG, - .o_get = wss_dialer_get_tlsconfig, - .o_set = wss_dialer_set_tlsconfig, - }, - { - .o_name = NNG_OPT_TLS_CERT_KEY_FILE, - .o_set = wss_dialer_set_cert_key_file, - }, - { - .o_name = NNG_OPT_TLS_CA_FILE, - .o_set = wss_dialer_set_ca_file, - }, - { - .o_name = NNG_OPT_TLS_AUTH_MODE, - .o_set = wss_dialer_set_auth_mode, - }, - { - .o_name = NNG_OPT_TLS_SERVER_NAME, - .o_set = wss_dialer_set_tls_server_name, - }, - // terminate list - { - .o_name = NULL, - }, +static nni_tran_dialer_ops ws_dialer_ops = { + .d_init = wstran_dialer_init, + .d_fini = wstran_dialer_fini, + .d_connect = wstran_dialer_connect, + .d_close = wstran_dialer_close, + .d_setopt = wstran_dialer_setopt, + .d_getopt = wstran_dialer_getopt, }; -static nni_option wss_listener_options[] = { - { - .o_name = NNG_OPT_RECVMAXSZ, - .o_get = ws_listener_get_recvmaxsz, - .o_set = ws_listener_set_recvmaxsz, - }, - { - .o_name = NNG_OPT_WS_RESPONSE_HEADERS, - .o_set = ws_listener_set_reshdrs, - }, - { - .o_name = NNG_OPT_TLS_CONFIG, - .o_get = wss_listener_get_tlsconfig, - .o_set = wss_listener_set_tlsconfig, - }, - { - .o_name = NNG_OPT_TLS_CERT_KEY_FILE, - .o_set = wss_listener_set_cert_key_file, - }, - { - .o_name = NNG_OPT_TLS_CA_FILE, - .o_set = wss_listener_set_ca_file, - }, - { - .o_name = NNG_OPT_TLS_AUTH_MODE, - .o_set = wss_listener_set_auth_mode, - }, - // terminate list - { - .o_name = NULL, - }, +static nni_tran_listener_ops ws_listener_ops = { + .l_init = wstran_listener_init, + .l_fini = wstran_listener_fini, + .l_bind = ws_listener_bind, + .l_accept = wstran_listener_accept, + .l_close = wstran_listener_close, + .l_setopt = wstran_listener_setopt, + .l_getopt = wstran_listener_getopt, }; -static nni_tran_dialer_ops wss_dialer_ops = { - .d_init = ws_dialer_init, - .d_fini = ws_dialer_fini, - .d_connect = ws_dialer_connect, - .d_close = ws_dialer_close, - .d_options = wss_dialer_options, +static nni_tran ws_tran = { + .tran_version = NNI_TRANSPORT_VERSION, + .tran_scheme = "ws", + .tran_dialer = &ws_dialer_ops, + .tran_listener = &ws_listener_ops, + .tran_pipe = &ws_pipe_ops, + .tran_init = wstran_init, + .tran_fini = wstran_fini, + .tran_checkopt = wstran_checkopt, }; -static nni_tran_listener_ops wss_listener_ops = { - .l_init = ws_listener_init, - .l_fini = ws_listener_fini, - .l_bind = ws_listener_bind, - .l_accept = ws_listener_accept, - .l_close = ws_listener_close, - .l_options = wss_listener_options, -}; +int +nng_ws_register(void) +{ + return (nni_tran_register(&ws_tran)); +} + +#ifdef NNG_TRANSPORT_WSS static nni_tran wss_tran = { .tran_version = NNI_TRANSPORT_VERSION, .tran_scheme = "wss", - .tran_dialer = &wss_dialer_ops, - .tran_listener = &wss_listener_ops, + .tran_dialer = &ws_dialer_ops, + .tran_listener = &ws_listener_ops, .tran_pipe = &ws_pipe_ops, - .tran_init = ws_tran_init, - .tran_fini = ws_tran_fini, + .tran_init = wstran_init, + .tran_fini = wstran_fini, + .tran_checkopt = wstran_checkopt, }; int |
