diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-12-29 14:21:20 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-12-30 19:05:41 -0800 |
| commit | 6a50035b242b972c1d9b659ba63e037a0a8afe71 (patch) | |
| tree | fe2600235a01e72d1e7bd5fad1d5e2ea62aada2e | |
| parent | a0364185784895c4bc748a6e6453a132d618c96c (diff) | |
| download | nng-6a50035b242b972c1d9b659ba63e037a0a8afe71.tar.gz nng-6a50035b242b972c1d9b659ba63e037a0a8afe71.tar.bz2 nng-6a50035b242b972c1d9b659ba63e037a0a8afe71.zip | |
fixes #166 Websocket TLS mapping
This introduces the wss:// scheme, which is available and works like
the ws:// scheme if TLS is enabled in the library.
The library modularization is refactored somewhat, to make it easier
to use. There is now a single NNG_ENABLE_TLS that enables TLS support
under the hood.
This also adds a new option for the TLS transport, NNG_OPT_TLS_CONFIG
(and a similar one for WSS, NNG_OPT_TLS_WSS_CONFIG) that offer access
to the underlying TLS configuration object, which now has a public API
to go with it as well.
Note that it is also possible to use pure HTTPS using the *private*
API, which will be exposed in a public form soon.
33 files changed, 1274 insertions, 477 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index df8a58f5..5aa87034 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ include (CheckSymbolExists) include (CheckStructHasMember) include (CheckLibraryExists) include (CheckCSourceCompiles) +include (CMakeDependentOption) include (GNUInstallDirs) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) @@ -98,93 +99,126 @@ option (NNG_ENABLE_COVERAGE "Enable coverage reporting." OFF) # Enable access to private APIs for our own use. add_definitions (-DNNG_PRIVATE) +option (NNG_ENABLE_TLS "Enable TLS protocol (requires mbedTLS" OFF) +if (NNG_ENABLE_TLS) + add_definitions(-DNNG_SUPP_TLS) + set(NNG_SUPP_TLS ON) + endif() + option (NNG_PROTO_BUS0 "Enable BUSv0 protocol." ON) if (NNG_PROTO_BUS0) add_definitions (-DNNG_HAVE_BUS0) endif () +mark_as_advanced(NNG_PROTO_BUS0) option (NNG_PROTO_PAIR0 "Enable PAIRv0 protocol." ON) if (NNG_PROTO_PAIR0) add_definitions (-DNNG_HAVE_PAIR0) endif () +mark_as_advanced(NNG_PROTO_PAIR0) option (NNG_PROTO_PAIR1 "Enable PAIRv1 protocol." ON) if (NNG_PROTO_PAIR1) add_definitions (-DNNG_HAVE_PAIR1) endif () +mark_as_advanced(NNG_PROTO_PAIR1) option (NNG_PROTO_REQ0 "Enable REQv0 protocol." ON) if (NNG_PROTO_REQ0) add_definitions (-DNNG_HAVE_REQ0) endif () +mark_as_advanced(NNG_PROTO_REQ0) option (NNG_PROTO_REP0 "Enable REPv0 protocol." ON) if (NNG_PROTO_REP0) add_definitions (-DNNG_HAVE_REP0) endif () +mark_as_advanced(NNG_PROTO_REP0) option (NNG_PROTO_PUB0 "Enable PUBv0 protocol." ON) if (NNG_PROTO_PUB0) add_definitions (-DNNG_HAVE_PUB0) endif () +mark_as_advanced(NNG_PROTO_PUB0) option (NNG_PROTO_SUB0 "Enable SUBv0 protocol." ON) if (NNG_PROTO_SUB0) add_definitions (-DNNG_HAVE_SUB0) endif () +mark_as_advanced(NNG_PROTO_SUB0) option (NNG_PROTO_PUSH0 "Enable PUSHv0 protocol." ON) if (NNG_PROTO_PUSH0) add_definitions (-DNNG_HAVE_PUSH0) endif () +mark_as_advanced(NNG_PROTO_PUSH0) option (NNG_PROTO_PULL0 "Enable PULLv0 protocol." ON) if (NNG_PROTO_PULL0) add_definitions (-DNNG_HAVE_PULL0) endif () +mark_as_advanced(NNG_PROTO_PULL0) option (NNG_PROTO_SURVEYOR0 "Enable SURVEYORv0 protocol." ON) if (NNG_PROTO_SURVEYOR0) add_definitions (-DNNG_HAVE_SURVEYOR0) endif () +mark_as_advanced(NNG_PROTO_SURVEYOR0) option (NNG_PROTO_RESPONDENT0 "Enable RESPONDENTv0 protocol." ON) if (NNG_PROTO_RESPONDENT0) add_definitions (-DNNG_HAVE_RESPONDENT0) endif () +mark_as_advanced(NNG_PROTO_RESPONDENT0) option (NNG_TRANSPORT_INPROC "Enable inproc transport." ON) if (NNG_TRANSPORT_INPROC) - add_definitions (-DNNG_HAVE_INPROC) + add_definitions (-DNNG_TRANSPORT_INPROC) endif () +mark_as_advanced(NNG_TRANSPORT_INPROC) option (NNG_TRANSPORT_IPC "Enable IPC transport." ON) if (NNG_TRANSPORT_IPC) - add_definitions (-DNNG_HAVE_IPC) + add_definitions (-DNNG_TRANSPORT_IPC) endif () +mark_as_advanced(NNG_TRANSPORT_IPC) option (NNG_TRANSPORT_TCP "Enable TCP transport." ON) if (NNG_TRANSPORT_TCP) - add_definitions (-DNNG_HAVE_TCP) + add_definitions (-DNNG_TRANSPORT_TCP) endif () +mark_as_advanced(NNG_TRANSPORT_TCP) + +CMAKE_DEPENDENT_OPTION(NNG_TRANSPORT_TLS "Enable TLS transport" ON + "NNG_ENABLE_TLS" OFF) + +if (NNG_TRANSPORT_TLS) + set(NNG_SUPP_TLS ON) + add_definitions (-DNNG_TRANSPORT_TLS) +endif() +mark_as_advanced(NNG_TRANSPORT_TLS) option (NNG_TRANSPORT_WS "Enable WebSocket transport." ON) if (NNG_TRANSPORT_WS) - add_definitions (-DNNG_HAVE_WEBSOCKET) + add_definitions (-DNNG_TRANSPORT_WS) + set(NNG_SUPP_WEBSOCKET ON) +endif () +mark_as_advanced(NNG_TRANSPORT_WS) + +CMAKE_DEPENDENT_OPTION(NNG_TRANSPORT_WSS "Enable WSS transport" ON + "NNG_ENABLE_TLS" OFF) +if (NNG_TRANSPORT_WSS) + add_definitions (-DNNG_TRANSPORT_WSS) set(NNG_SUPP_WEBSOCKET ON) endif () +mark_as_advanced(NNG_TRANSPORT_WSS) option (NNG_TRANSPORT_ZEROTIER "Enable ZeroTier transport (requires libzerotiercore)." OFF) if (NNG_TRANSPORT_ZEROTIER) - add_definitions (-DNNG_HAVE_ZEROTIER) + add_definitions (-DNNG_TRANSPORT_ZEROTIER) endif () +mark_as_advanced(NNG_TRANSPORT_ZEROTIER) -option (NNG_TRANSPORT_TLS "Enable TLS transport (requires mbedtls)" OFF) -if (NNG_TRANSPORT_TLS) - set(NNG_MBEDTLS_ENABLE ON) - add_definitions (-DNNG_MBEDTLS_ENABLE) - add_definitions (-DNNG_HAVE_TLS) -endif() # dependencies if (NNG_SUPP_WEBSOCKET) @@ -193,6 +227,11 @@ if (NNG_SUPP_WEBSOCKET) set(NNG_SUPP_SHA1 ON) endif() +# Extra defines. +if (NNG_SUPP_TLS) + add_definitions (-DNNG_SUPP_TLS) +endif() + # Platform checks. if (NNG_ENABLE_COVERAGE) diff --git a/README.adoc b/README.adoc index 29c8e1c2..8912d879 100644 --- a/README.adoc +++ b/README.adoc @@ -11,10 +11,10 @@ can start using it for development, as we believe we are getting closer to release readiness. Review and testing feedback are appreciated however; but please understand -that the project is still quite preliminary. +that the project is still preliminary. -This is a work in progress, and is *not* suitable for production use or -publication. When the library is ready for broader consumption, an +This is a work in progress, and is *not* suitable for production use. +When the library is ready for broader consumption, an announcement will be posted on the nanomsg mailing list and website. Some https://nanomsg.github.io/nng/man/[manual pages] are posted @@ -24,26 +24,36 @@ If you are looking for the current production version of nanomsg, please see the https://github.com/nanomsg/nanomsg[nanomsg repo]. If you want to build and test yourself, you need CMake version 3.1, and -you can use standard CMake build recipes. On a Linux/UNIX system, -you can for example do: +you can use standard CMake build recipes. (We highly recommend using +https://ninja-build.org[Ninja] as it is much faster than traditional +build systems.) On a Linux/UNIX system, if you have Ninja already +installed, you can for example do: [source,sh] ---- $ mkdir build - $ cmake .. - $ make - $ make test + $ cmake -G Ninja .. + $ ninja + $ ninja test ---- -If you want to enable the TLS transport, which is not supported by legacy -nanomsg, use -DNNG_TRANSPORT_TLS=ON in your cmake command line. You will -need to have the ARM mbedTLS library installed. (This is available in -Xenial Ubuntu as libmbedtls-dev). - -The API is not yet documented, but if you want to explore using the newer -API, pleaes have a look at the `nng.h` header file. A legacy compatible -`nng_compat.h` header is available and offers API compatibility with legacy -_nanomsg_. +This library can be compiled with support for TLS, which enables +the use of the "tls+tcp://" and "wss://" schemes. In order to this, +configure with `-DNNG_ENABLE_TLS=ON`. + +NOTE: The `NNG_ENABLE_TLS` library depends on the ARM +https://tls.mbed.org[mbedTLS] library. This library is available +in packaged form for many systems, and can be built for just about +any of the others. However, please be aware of the licensing +implications, because the mbedTLS library carries other licensing +requirements (either Apache or GPL) than _nng_. +either Apache or GPL licenses. + +Documentation is a work in progres -- initial pages are +https://nanomsg.github.io/nng[online], but there is much missing. +You can also explore the `nng.h` header file, which provides the public +API. A legacy compatible `nng_compat.h` header is available and +offers API compatibility with legacy _nanomsg_. == Commercial Support diff --git a/docs/nng_tls.adoc b/docs/nng_tls.adoc index e6eafdd5..a6737d01 100644 --- a/docs/nng_tls.adoc +++ b/docs/nng_tls.adoc @@ -157,6 +157,17 @@ Transport Options The following transport options are available. Note that setting these must be done before the transport is started. +`NNG_OPT_TLS_CONFIG`:: + +This option is used to set or obtain the TLS configuration +object (type `nng_tls_config *`), which is passed as a pointer. +Setting this option adds a reference to the object; obtaining the +object pointer does not. (Therefore when retrieving this option, +care must be taken not to access it after the endpoint is closed.) + +Note that configuration object is not modifiable once it has been +used in a running TLS stream. + `NNG_OPT_TLS_CA_CERT`:: This is a write-only binay object containing a certificate @@ -232,6 +243,7 @@ if the `NNG_OPT_TLS_AUTH_MODE` option is set to SEE ALSO -------- <<nng.adoc#,nng(7)>> +<<nng_tls_init#,nng_tls_init(3)>> COPYRIGHT --------- diff --git a/docs/nng_ws.adoc b/docs/nng_ws.adoc index 8073e158..b6185757 100644 --- a/docs/nng_ws.adoc +++ b/docs/nng_ws.adoc @@ -25,6 +25,7 @@ SYNOPSIS #include <nng/transport/websocket/ws.h> int nng_ws_register(void); +int nng_wss_register(void); ---------- DESCRIPTION @@ -46,6 +47,11 @@ register the transport by calling `nng_ws_register`. This function returns zero on success, or an nng error value if the transport cannot be initialized for any reason. +If TLS support is enabled in the library, secure WebSockets (over TLS v1.2) +can be used as well, but the secure transport may have to be registered using +the `nng_wss_register` function. (Note that this function will not be +present if TLS support was not enabled in the library.) + URI Format ~~~~~~~~~~ @@ -57,6 +63,10 @@ a path of `/` is assumed.) For example, the URI `ws://localhost/app/pubsub` would use port 80 on localhost, with the path `/app/pubsub`. +Secure WebSockets (if enabled) use the scheme `wss://`, and the default +TCP port number of 443. Otherwise the format is the same as for regular +WebSockets. + When specifying IPv6 addresses, the address must be enclosed in square brackets (`[]`) to avoid confusion with the final colon separating the port. @@ -141,6 +151,15 @@ by CRLF sequences, that can be used to add furthe headers to the HTTP response sent when connecting. This option can be set on listeners, and retrieved from pipes. +`NNG_OPT_WSS_TLS_CONFIG`:: + +This option is used on an endpoint to access the underlying TLS +configuration object. The value is of type `nng_tls_config *`. +Note that attempts to set this object may fail on a listener if +the server is already running. Furthermore, attempts to modify the +configuration object will fail if it is already in active use. +This object is only available for `wss://` endpoints. + // We should also look at a hook mechanism for listeners. Probably this could // look like NNG_OPT_WS_LISTEN_HOOK_FUNC which would take a function pointer // along the lines of int hook(void *, char *req_headers, char **res_headers), @@ -150,6 +169,7 @@ and retrieved from pipes. SEE ALSO -------- <<nng.adoc#,nng(7)>> +<<nng_tls_init#,nng_tls_init(3)>> COPYRIGHT --------- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6fae89e0..45dcb4fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,8 +123,8 @@ endif() add_subdirectory(supplemental/base64) add_subdirectory(supplemental/http) -add_subdirectory(supplemental/mbedtls) add_subdirectory(supplemental/sha1) +add_subdirectory(supplemental/tls) add_subdirectory(supplemental/websocket) add_subdirectory(protocol/bus0) diff --git a/src/core/options.c b/src/core/options.c index 1417d0b3..ef7420d6 100644 --- a/src/core/options.c +++ b/src/core/options.c @@ -187,6 +187,19 @@ nni_getopt_size(size_t u, void *val, size_t *sizep) } int +nni_getopt_ptr(void *ptr, void *val, size_t *sizep) +{ + size_t sz = sizeof(ptr); + + if (sz > *sizep) { + sz = *sizep; + } + *sizep = sizeof(ptr); + memcpy(val, &ptr, sz); + return (0); +} + +int nni_setopt_buf(nni_msgq *mq, const void *val, size_t sz) { int len; diff --git a/src/core/options.h b/src/core/options.h index d373851f..e9aa16dd 100644 --- a/src/core/options.h +++ b/src/core/options.h @@ -58,6 +58,9 @@ extern int nni_setopt_size(size_t *, const void *, size_t, size_t, size_t); // nni_getopt_size obtains a size_t option. extern int nni_getopt_size(size_t, void *, size_t *); +// nni_getopt_ptr obtains a pointer option. +extern int nni_getopt_ptr(void *, void *, size_t *); + extern int nni_chkopt_ms(const void *, size_t); extern int nni_chkopt_int(const void *, size_t, int, int); extern int nni_chkopt_size(const void *, size_t, size_t, size_t); diff --git a/src/core/transport.c b/src/core/transport.c index 31da773f..9c129a72 100644 --- a/src/core/transport.c +++ b/src/core/transport.c @@ -56,12 +56,12 @@ nni_tran_register(const nni_tran *tran) nni_mtx_lock(&nni_tran_lk); // Check to see if the transport is already registered... NNI_LIST_FOREACH (&nni_tran_list, t) { - if (tran->tran_init == t->t_tran.tran_init) { - nni_mtx_unlock(&nni_tran_lk); - // Same transport, duplicate registration. - return (0); - } if (strcmp(tran->tran_scheme, t->t_tran.tran_scheme) == 0) { + if (tran->tran_init == t->t_tran.tran_init) { + // duplicate. + nni_mtx_unlock(&nni_tran_lk); + return (0); + } nni_mtx_unlock(&nni_tran_lk); return (NNG_ESTATE); } @@ -208,25 +208,30 @@ nni_tran_chkopt(const char *name, const void *v, size_t sz) typedef int (*nni_tran_ctor)(void); +// These are just the statically compiled in constructors. +// In the future we might want to support dynamic additions. static nni_tran_ctor nni_tran_ctors[] = { -#ifdef NNG_HAVE_INPROC +#ifdef NNG_TRANSPORT_INPROC nng_inproc_register, #endif -#ifdef NNG_HAVE_IPC +#ifdef NNG_TRANSPORT_IPC nng_ipc_register, #endif -#ifdef NNG_HAVE_TCP +#ifdef NNG_TRANSPORT_TCP nng_tcp_register, #endif -#ifdef NNG_HAVE_TLS +#ifdef NNG_TRANSPORT_TLS nng_tls_register, #endif -#ifdef NNG_HAVE_ZEROTIER - nng_zt_register, -#endif -#ifdef NNG_HAVE_WEBSOCKET +#ifdef NNG_TRANSPORT_WS nng_ws_register, #endif +#ifdef NNG_TRANSPORT_WSS + nng_wss_register, +#endif +#ifdef NNG_TRANSPORT_ZEROTIER + nng_zt_register, +#endif NULL, }; @@ -405,6 +405,12 @@ nng_dialer_setopt_uint64(nng_dialer id, const char *name, uint64_t val) } int +nng_dialer_setopt_ptr(nng_dialer id, const char *name, void *val) +{ + return (nng_dialer_setopt(id, name, &val, sizeof(val))); +} + +int nng_dialer_getopt(nng_dialer id, const char *name, void *val, size_t *szp) { return (nng_ep_getopt(id, name, val, szp, NNI_EP_MODE_DIAL)); @@ -432,6 +438,13 @@ nng_dialer_getopt_uint64(nng_dialer id, const char *name, uint64_t *valp) } int +nng_dialer_getopt_ptr(nng_dialer id, const char *name, void **valp) +{ + size_t sz = sizeof(*valp); + return (nng_dialer_getopt(id, name, valp, &sz)); +} + +int nng_dialer_getopt_ms(nng_dialer id, const char *name, nng_duration *valp) { size_t sz = sizeof(*valp); @@ -470,6 +483,12 @@ nng_listener_setopt_uint64(nng_listener id, const char *name, uint64_t val) } int +nng_listener_setopt_ptr(nng_listener id, const char *name, void *val) +{ + return (nng_listener_setopt(id, name, &val, sizeof(val))); +} + +int nng_listener_getopt(nng_listener id, const char *name, void *val, size_t *szp) { return (nng_ep_getopt(id, name, val, szp, NNI_EP_MODE_LISTEN)); @@ -497,6 +516,13 @@ nng_listener_getopt_uint64(nng_listener id, const char *name, uint64_t *valp) } int +nng_listener_getopt_ptr(nng_listener id, const char *name, void **valp) +{ + size_t sz = sizeof(*valp); + return (nng_listener_getopt(id, name, valp, &sz)); +} + +int nng_listener_getopt_ms(nng_listener id, const char *name, nng_duration *valp) { size_t sz = sizeof(*valp); @@ -588,6 +614,12 @@ nng_setopt_uint64(nng_socket sid, const char *name, uint64_t val) } int +nng_setopt_ptr(nng_socket sid, const char *name, void *val) +{ + return (nng_setopt(sid, name, &val, sizeof(val))); +} + +int nng_getopt_int(nng_socket sid, const char *name, int *valp) { size_t sz = sizeof(*valp); @@ -616,6 +648,13 @@ nng_getopt_ms(nng_socket sid, const char *name, nng_duration *valp) } int +nng_getopt_ptr(nng_socket sid, const char *name, void **valp) +{ + size_t sz = sizeof(*valp); + return (nng_getopt(sid, name, valp, &sz)); +} + +int nng_device(nng_socket s1, nng_socket s2) { int rv; @@ -21,6 +21,7 @@ extern "C" { #endif +#include <stdbool.h> #include <stddef.h> #include <stdint.h> @@ -90,6 +91,7 @@ NNG_DECL int nng_getopt_int(nng_socket, const char *, int *); NNG_DECL int nng_getopt_ms(nng_socket, const char *, nng_duration *); NNG_DECL int nng_getopt_size(nng_socket, const char *, size_t *); NNG_DECL int nng_getopt_uint64(nng_socket, const char *, uint64_t *); +NNG_DECL int nng_getopt_ptr(nng_socket, const char *, void **); // nng_listen creates a listening endpoint with no special options, // and starts it listening. It is functionally equivalent to the legacy @@ -138,6 +140,7 @@ NNG_DECL int nng_dialer_setopt_int(nng_dialer, const char *, int); NNG_DECL int nng_dialer_setopt_ms(nng_dialer, const char *, nng_duration); NNG_DECL int nng_dialer_setopt_size(nng_dialer, const char *, size_t); NNG_DECL int nng_dialer_setopt_uint64(nng_dialer, const char *, uint64_t); +NNG_DECL int nng_dialer_setopt_ptr(nng_dialer, const char *, void *); // nng_dialer_getopt obtains the option for a dialer. This will // fail for options that a particular dialer is not interested in, @@ -147,6 +150,7 @@ NNG_DECL int nng_dialer_getopt_int(nng_dialer, const char *, int *); NNG_DECL int nng_dialer_getopt_ms(nng_dialer, const char *, nng_duration *); NNG_DECL int nng_dialer_getopt_size(nng_dialer, const char *, size_t *); NNG_DECL int nng_dialer_getopt_uint64(nng_dialer, const char *, uint64_t *); +NNG_DECL int nng_dialer_getopt_ptr(nng_dialer, const char *, void **); // nng_listener_setopt sets an option for a dialer. This value is // not stored in the socket. Subsequent setopts on the socket may @@ -158,6 +162,7 @@ NNG_DECL int nng_listener_setopt_int(nng_listener, const char *, int); NNG_DECL int nng_listener_setopt_ms(nng_listener, const char *, nng_duration); NNG_DECL int nng_listener_setopt_size(nng_listener, const char *, size_t); NNG_DECL int nng_listener_setopt_uint64(nng_listener, const char *, uint64_t); +NNG_DECL int nng_listener_setopt_ptr(nng_listener, const char *, void *); // nng_listener_getopt obtains the option for a listener. This will // fail for options that a particular listener is not interested in, @@ -169,6 +174,7 @@ NNG_DECL int nng_listener_getopt_ms( NNG_DECL int nng_listener_getopt_size(nng_listener, const char *, size_t *); NNG_DECL int nng_listener_getopt_uint64( nng_listener, const char *, uint64_t *); +NNG_DECL int nng_listener_getopt_ptr(nng_listener, const char *, void **); // nng_strerror returns a human readable string associated with the error // code supplied. @@ -574,6 +580,73 @@ enum nng_sockaddr_family { NNG_AF_ZT = 5, // ZeroTier }; +// For some transports, we need TLS configuration. This +// section lets us work with TLS configurations. Note +// that these symbols are only actually present at link time +// if TLS support is enabled in your build. Note also that +// a TLS configuration cannot be changed once it is in use. +typedef struct nng_tls_config nng_tls_config; + +typedef enum nng_tls_mode { + NNG_TLS_MODE_CLIENT = 0, + NNG_TLS_MODE_SERVER = 1, +} nng_tls_mode; + +typedef enum nng_tls_auth_mode { + NNG_TLS_AUTH_MODE_NONE = 0, // No verification is performed + NNG_TLS_AUTH_MODE_OPTIONAL = 1, // Verify cert if presented + NNG_TLS_AUTH_MODE_REQUIRED = 2, // Verify cert, close if invalid +} nng_tls_auth_mode; + +// nng_tls_config init creates a TLS configuration using +// reasonable defaults. This configuration can be shared +// with multiple pipes or services/servers. +NNG_DECL int nng_tls_config_init(nng_tls_config **, nng_tls_mode); + +NNG_DECL void nng_tls_config_fini(nng_tls_config *); + +// nng_tls_config_server_name sets the server name. This is +// called by clients to set the name that the server supplied +// certificate should be matched against. This can also cause +// the SNI to be sent to the server to tell it which cert to +// use if it supports more than one. +NNG_DECL int nng_tls_config_server_name(nng_tls_config *, const char *); + +// nng_tls_config_ca_cert configures one or more CAs used for validation +// of peer certificates. Multiple CAs (and their chains) may be configured +// by either calling this multiple times, or by specifying a list of +// certificates as concatenated data. The certs may be in PEM or DER +// format. +NNG_DECL int nng_tls_config_ca_cert(nng_tls_config *, const uint8_t *, size_t); + +// nng_tls_config_clr loads a certificate revocation list. Again, these +// are in X.509 format (either PEM or DER). +NNG_DECL int nng_tls_config_crl(nng_tls_config *, const uint8_t *, size_t); + +// nng_tls_config_cert is used to load our own certificate. For servers, +// this may be called more than once to configure multiple different keys, +// for example with different algorithms depending on what the peer supports. +// On the client, only a single option is available. +NNG_DECL int nng_tls_config_cert(nng_tls_config *, const uint8_t *, size_t); + +// nng_tls_config_key is used to pass our own private key. +NNG_DECL int nng_tls_config_key(nng_tls_config *, const uint8_t *, size_t); + +// nng_tls_config_pass is used to pass a password used to decrypt +// private keys that are encrypted. +NNG_DECL int nng_tls_config_pass(nng_tls_config *, const char *); + +// nng_tls_config_validate_peer is used to enable validation of the peer +// and it's certificate. If disabled, the peer's certificate will still +// be available, but may not be valid. +NNG_DECL int nng_tls_config_validate_peer(nng_tls_config *, bool); + +// nng_tls_config_auth_mode is used to configure the authentication mode use. +// The default is that servers have this off (i.e. no client authentication) +// and clients have it on (they verify the server), which matches typical +// practice. +NNG_DECL int nng_tls_config_auth_mode(nng_tls_config *, nng_tls_auth_mode); + #ifdef __cplusplus } #endif diff --git a/src/supplemental/http/client.c b/src/supplemental/http/client.c index 8c082a17..4bb517ce 100644 --- a/src/supplemental/http/client.c +++ b/src/supplemental/http/client.c @@ -14,6 +14,8 @@ #include <string.h> #include "core/nng_impl.h" +#include "supplemental/tls/tls.h" + #include "http.h" struct nni_http_client { @@ -21,7 +23,7 @@ struct nni_http_client { nni_list aios; nni_mtx mtx; bool closed; - bool tls; + nng_tls_config * tls; nni_aio * connaio; nni_plat_tcp_ep *tep; }; @@ -39,7 +41,6 @@ http_conn_done(void *arg) nni_aio * aio; int rv; nni_plat_tcp_pipe *p; - nni_http_tran t; nni_http * http; nni_mtx_lock(&c->mtx); @@ -60,17 +61,13 @@ http_conn_done(void *arg) return; } - t.h_data = p; - t.h_write = (void *) nni_plat_tcp_pipe_send; - t.h_read = (void *) nni_plat_tcp_pipe_recv; - t.h_close = (void *) nni_plat_tcp_pipe_close; - t.h_sock_addr = (void *) nni_plat_tcp_pipe_sockname; - t.h_peer_addr = (void *) nni_plat_tcp_pipe_peername; - t.h_fini = (void *) nni_plat_tcp_pipe_fini; - - if ((rv = nni_http_init(&http, &t)) != 0) { + if (c->tls != NULL) { + rv = nni_http_init_tls(&http, c->tls, p); + } else { + rv = nni_http_init_tcp(&http, p); + } + if (rv != 0) { nni_aio_finish_error(aio, rv); - nni_plat_tcp_pipe_fini(p); nni_mtx_unlock(&c->mtx); return; } @@ -90,6 +87,11 @@ nni_http_client_fini(nni_http_client *c) nni_aio_fini(c->connaio); nni_plat_tcp_ep_fini(c->tep); nni_mtx_fini(&c->mtx); +#ifdef NNG_SUPP_TLS + if (c->tls != NULL) { + nng_tls_config_fini(c->tls); + } +#endif NNI_FREE_STRUCT(c); } @@ -119,6 +121,25 @@ nni_http_client_init(nni_http_client **cp, nng_sockaddr *sa) return (0); } +#ifdef NNG_SUPP_TLS +int +nni_http_client_set_tls(nni_http_client *c, nng_tls_config *tls) +{ + 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) { + nng_tls_config_fini(old); + } + return (0); +} +#endif + static void http_connect_cancel(nni_aio *aio, int rv) { diff --git a/src/supplemental/http/http.c b/src/supplemental/http/http.c index f414c5c7..df5c7588 100644 --- a/src/supplemental/http/http.c +++ b/src/supplemental/http/http.c @@ -12,6 +12,8 @@ #include <string.h> #include "core/nng_impl.h" +#include "supplemental/tls/tls.h" + #include "http.h" // We insist that individual headers fit in 8K. @@ -33,6 +35,15 @@ enum write_flavor { HTTP_WR_RES, }; +typedef struct nni_http_tran { + void (*h_read)(void *, nni_aio *); + void (*h_write)(void *, nni_aio *); + int (*h_sock_addr)(void *, nni_sockaddr *); + int (*h_peer_addr)(void *, nni_sockaddr *); + void (*h_close)(void *); + void (*h_fini)(void *); +} nni_http_tran; + #define SET_RD_FLAVOR(aio, f) (aio)->a_prov_extra[0] = ((void *) (intptr_t)(f)) #define GET_RD_FLAVOR(aio) (int) ((intptr_t) aio->a_prov_extra[0]) #define SET_WR_FLAVOR(aio, f) (aio)->a_prov_extra[0] = ((void *) (intptr_t)(f)) @@ -76,14 +87,17 @@ http_close(nni_http *http) http->closed = true; if (nni_list_first(&http->wrq)) { nni_aio_cancel(http->wr_aio, NNG_ECLOSED); - while ((aio = nni_list_first(&http->wrq)) != NULL) { + // Abort all operations except the one in flight. + while ((aio = nni_list_last(&http->wrq)) != + nni_list_first(&http->wrq)) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, NNG_ECLOSED); } } if (nni_list_first(&http->rdq)) { nni_aio_cancel(http->rd_aio, NNG_ECLOSED); - while ((aio = nni_list_first(&http->rdq)) != NULL) { + while ((aio = nni_list_last(&http->rdq)) != + nni_list_first(&http->rdq)) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, NNG_ECLOSED); } @@ -591,8 +605,8 @@ nni_http_fini(nni_http *http) NNI_FREE_STRUCT(http); } -int -nni_http_init(nni_http **httpp, nni_http_tran *tran) +static int +http_init(nni_http **httpp, nni_http_tran *tran, void *data) { nni_http *http; int rv; @@ -609,21 +623,71 @@ nni_http_init(nni_http **httpp, nni_http_tran *tran) nni_aio_list_init(&http->rdq); nni_aio_list_init(&http->wrq); - if (((rv = nni_aio_init(&http->wr_aio, http_wr_cb, http)) != 0) || - ((rv = nni_aio_init(&http->rd_aio, http_rd_cb, http)) != 0)) { - nni_http_fini(http); - return (rv); - } + http->sock = data; http->rd_bufsz = HTTP_BUFSIZE; http->rd = tran->h_read; http->wr = tran->h_write; http->close = tran->h_close; http->fini = tran->h_fini; - http->sock = tran->h_data; http->sock_addr = tran->h_sock_addr; http->peer_addr = tran->h_peer_addr; + if (((rv = nni_aio_init(&http->wr_aio, http_wr_cb, http)) != 0) || + ((rv = nni_aio_init(&http->rd_aio, http_rd_cb, http)) != 0)) { + nni_http_fini(http); + return (rv); + } + *httpp = http; return (0); } + +static nni_http_tran http_tcp_ops = { + .h_read = (void *) nni_plat_tcp_pipe_recv, + .h_write = (void *) nni_plat_tcp_pipe_send, + .h_close = (void *) nni_plat_tcp_pipe_close, + .h_fini = (void *) nni_plat_tcp_pipe_fini, + .h_sock_addr = (void *) nni_plat_tcp_pipe_sockname, + .h_peer_addr = (void *) nni_plat_tcp_pipe_peername, +}; + +int +nni_http_init_tcp(nni_http **hpp, void *tcp) +{ + return (http_init(hpp, &http_tcp_ops, tcp)); +} + +#ifdef NNG_SUPP_TLS +static nni_http_tran http_tls_ops = { + .h_read = (void *) nni_tls_recv, + .h_write = (void *) nni_tls_send, + .h_close = (void *) nni_tls_close, + .h_fini = (void *) nni_tls_fini, + .h_sock_addr = (void *) nni_tls_sockname, + .h_peer_addr = (void *) nni_tls_peername, +}; + +int +nni_http_init_tls(nni_http **hpp, nng_tls_config *cfg, void *tcp) +{ + nni_tls *tls; + int rv; + + if ((rv = nni_tls_init(&tls, cfg, tcp)) != 0) { + nni_plat_tcp_pipe_fini(tcp); + return (rv); + } + + return (http_init(hpp, &http_tls_ops, tls)); +} +#else +int +nni_http_init_tls(nni_http **hpp, nng_tls_config *cfg, void *tcp) +{ + NNI_ARG_UNUSED(hpp); + NNI_ARG_UNUSED(cfg); + nni_plat_tcp_pipe_fini(tcp); + return (NNG_ENOTSUP); +} +#endif // NNG_SUPP_TLS
\ No newline at end of file diff --git a/src/supplemental/http/http.h b/src/supplemental/http/http.h index 3ecce4c8..d817ba37 100644 --- a/src/supplemental/http/http.h +++ b/src/supplemental/http/http.h @@ -18,16 +18,6 @@ typedef struct nni_http_msg nni_http_msg; typedef struct nni_http_res nni_http_res; typedef struct nni_http_entity nni_http_entity; -typedef struct nni_http_tran { - void *h_data; - void (*h_read)(void *, nni_aio *); - void (*h_write)(void *, nni_aio *); - int (*h_sock_addr)(void *, nni_sockaddr *); - int (*h_peer_addr)(void *, nni_sockaddr *); - void (*h_close)(void *); - void (*h_fini)(void *); -} nni_http_tran; - typedef struct nni_http_req nni_http_req; extern int nni_http_req_init(nni_http_req **); @@ -146,7 +136,8 @@ enum { NNI_HTTP_STATUS_CONTINUE = 100, // the connection. typedef struct nni_http nni_http; -extern int nni_http_init(nni_http **, nni_http_tran *); +extern int nni_http_init_tcp(nni_http **, void *); +extern int nni_http_init_tls(nni_http **, nng_tls_config *, void *); extern void nni_http_close(nni_http *); extern void nni_http_fini(nni_http *); @@ -254,6 +245,11 @@ extern int nni_http_server_add_handler( extern void nni_http_server_del_handler(nni_http_server *, void *); +// nni_http_server_set_tls adds a TLS configuration to the server, +// and enables the use of it. This returns NNG_EBUSY if the server is +// already started. +extern int nni_http_server_set_tls(nni_http_server *, nng_tls_config *); + // nni_http_server_start starts listening on the supplied port. extern int nni_http_server_start(nni_http_server *); @@ -281,16 +277,13 @@ extern int nni_http_server_add_static(nni_http_server *, const char *host, extern int nni_http_server_add_file(nni_http_server *, const char *host, const char *ctype, const char *uri, const char *path); -// TLS will use -// extern int nni_http_server_start_tls(nni_http_server *, nng_sockaddr *, -// nni_tls_config *); - // Client stuff. typedef struct nni_http_client nni_http_client; extern int nni_http_client_init(nni_http_client **, nng_sockaddr *); extern void nni_http_client_fini(nni_http_client *); +extern int nni_http_client_set_tls(nni_http_client *, nng_tls_config *); extern void nni_http_client_connect(nni_http_client *, nni_aio *); #endif // NNG_SUPPLEMENTAL_HTTP_HTTP_H diff --git a/src/supplemental/http/server.c b/src/supplemental/http/server.c index ea7f15ce..ba74a138 100644 --- a/src/supplemental/http/server.c +++ b/src/supplemental/http/server.c @@ -14,6 +14,8 @@ #include <string.h> #include "core/nng_impl.h" +#include "supplemental/tls/tls.h" + #include "http.h" static int http_server_sys_init(void); @@ -50,7 +52,6 @@ typedef struct http_sconn { nni_aio * rxaio; nni_aio * txaio; nni_aio * txdataio; - nni_http_tran tran; nni_reap_item reap; } http_sconn; @@ -64,7 +65,7 @@ struct nni_http_server { nni_mtx mtx; nni_cv cv; bool closed; - bool tls; + nng_tls_config * tls; nni_aio * accaio; nni_plat_tcp_ep *tep; }; @@ -105,28 +106,48 @@ http_sconn_fini(http_sconn *sc) } static void -http_sconn_close(http_sconn *sc) +http_sconn_close_locked(http_sconn *sc) { nni_http_server *s; s = sc->server; + nni_http *h; + if (sc->closed) { + return; + } NNI_ASSERT(!sc->finished); - nni_mtx_lock(&s->mtx); - if (!sc->closed) { - nni_http *h; - sc->closed = true; - // Close the underlying transport. - if (nni_list_node_active(&sc->node)) { - nni_list_remove(&s->conns, sc); - if (nni_list_empty(&s->conns)) { - nni_cv_wake(&s->cv); - } - } - if ((h = sc->http) != NULL) { - nni_http_close(h); + + sc->closed = true; + // Close the underlying transport. + if (nni_list_node_active(&sc->node)) { + nni_list_remove(&s->conns, sc); + if (nni_list_empty(&s->conns)) { + nni_cv_wake(&s->cv); } - http_sconn_fini(sc); } + nni_aio_cancel(sc->rxaio, NNG_ECLOSED); + nni_aio_cancel(sc->txaio, NNG_ECLOSED); + nni_aio_cancel(sc->txdataio, NNG_ECLOSED); + nni_aio_cancel(sc->cbaio, NNG_ECLOSED); + + if ((h = sc->http) != NULL) { + nni_http_close(h); + } + http_sconn_fini(sc); +} + +static void +http_sconn_close(http_sconn *sc) +{ + nni_http_server *s; + s = sc->server; + + if (sc->closed) { + return; + } + + nni_mtx_lock(&s->mtx); + http_sconn_close_locked(sc); nni_mtx_unlock(&s->mtx); } @@ -484,14 +505,16 @@ http_sconn_cbdone(void *arg) } static int -http_sconn_init(http_sconn **scp, nni_plat_tcp_pipe *tcp) +http_sconn_init(http_sconn **scp, nni_http_server *s, nni_plat_tcp_pipe *tcp) { http_sconn *sc; int rv; if ((sc = NNI_ALLOC_STRUCT(sc)) == NULL) { + nni_plat_tcp_pipe_fini(tcp); return (NNG_ENOMEM); } + if (((rv = nni_http_req_init(&sc->req)) != 0) || ((rv = nni_aio_init(&sc->rxaio, http_sconn_rxdone, sc)) != 0) || ((rv = nni_aio_init(&sc->txaio, http_sconn_txdone, sc)) != 0) || @@ -502,18 +525,13 @@ http_sconn_init(http_sconn **scp, nni_plat_tcp_pipe *tcp) http_sconn_close(sc); return (rv); } - // XXX: for HTTPS we would then try to do the TLS negotiation here. - // That would use a different set of tran values. - sc->tran.h_data = tcp; - sc->tran.h_read = (void *) nni_plat_tcp_pipe_recv; - sc->tran.h_write = (void *) nni_plat_tcp_pipe_send; - sc->tran.h_close = (void *) nni_plat_tcp_pipe_close; // close implied - sc->tran.h_fini = (void *) nni_plat_tcp_pipe_fini; - sc->tran.h_sock_addr = (void *) nni_plat_tcp_pipe_sockname; - sc->tran.h_peer_addr = (void *) nni_plat_tcp_pipe_peername; - - if ((rv = nni_http_init(&sc->http, &sc->tran)) != 0) { + if (s->tls != NULL) { + rv = nni_http_init_tls(&sc->http, s->tls, tcp); + } else { + rv = nni_http_init_tcp(&sc->http, tcp); + } + if (rv != 0) { http_sconn_close(sc); return (rv); } @@ -539,9 +557,8 @@ http_server_acccb(void *arg) return; } tcp = nni_aio_get_pipe(aio); - if (http_sconn_init(&sc, tcp) != 0) { - nni_plat_tcp_pipe_close(tcp); - nni_plat_tcp_pipe_fini(tcp); + if (http_sconn_init(&sc, s, tcp) != 0) { + // The TCP structure is already cleaned up. nni_plat_tcp_ep_accept(s->tep, s->accaio); return; @@ -590,6 +607,11 @@ http_server_fini(nni_http_server *s) http_handler_fini(h); } nni_mtx_unlock(&s->mtx); +#ifdef NNG_SUPP_TLS + if (s->tls != NULL) { + nng_tls_config_fini(s->tls); + } +#endif nni_aio_fini(s->accaio); nni_cv_fini(&s->cv); nni_mtx_fini(&s->mtx); @@ -726,11 +748,7 @@ http_server_stop(nni_http_server *s) // Stopping the server is a hard stop -- it aborts any work being // done by clients. (No graceful shutdown). NNI_LIST_FOREACH (&s->conns, sc) { - nni_list_remove(&s->conns, sc); - if (sc->http != NULL) { - nni_http_close(sc->http); - } - http_sconn_fini(sc); + http_sconn_close_locked(sc); } nni_cv_wake(&s->cv); } @@ -1073,6 +1091,29 @@ nni_http_server_add_static(nni_http_server *s, const char *host, return (0); } +#ifdef NNG_SUPP_TLS +int +nni_http_server_set_tls(nni_http_server *s, nng_tls_config *tcfg) +{ + 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) { + nng_tls_config_fini(old); + } + return (0); +} +#endif + static int http_server_sys_init(void) { diff --git a/src/supplemental/mbedtls/CMakeLists.txt b/src/supplemental/mbedtls/CMakeLists.txt deleted file mode 100644 index 5c2de10b..00000000 --- a/src/supplemental/mbedtls/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright 2017 Garrett D'Amore <garrett@damore.org> -# Copyright 2017 Capitar IT Group BV <info@capitar.com> -# -# This software is supplied under the terms of the MIT License, a -# copy of which should be located in the distribution where this -# file was obtained (LICENSE.txt). A copy of the license may also be -# found online at https://opensource.org/licenses/MIT. -# - -# MBEDTLS library - -# This requires the mbedTLS library be installed somewhere. You can -# point this at a suitable installation of mbedTLS by setting -# MBEDTLS_ROOT_DIR to point at the root of the installation (prefix). - -# It is possible to minimize the mbedTLS library quite a bit. We do -# not require legacy algorithms, the net_sockets layer, the filesystem -# I/O, as well as various other tidbits. We provide an entropy source, -# so you can disable that in mbedTLS too. You may disable fallback support, -# as we only support TLS v1.2 at present. (You may also therefore remove -# code to support older versions of TLS/SSL.) You may also remove DTLS, -# since we're not using it now (nor are we likely to in the near feature). -# Also you may remove support for ZLIB compression, we don't use it either -# (and it would be insecure to do so.) PEM and X509 writing (encoding) -# is not needed (but parse support is!) You may also remove session support, -# as we don't use that either. -# -# (Look for a sample config.h in this directory, if you want to build -# a minimized version just for nng.) - -# What we do require is support for TLSv1.2 - -if (NNG_MBEDTLS_ENABLE) - set(SUPP_SOURCES supplemental/mbedtls/tls.c supplemental/tls.h) - Find_Package(mbedTLS REQUIRED) - - # If it isn't already in the link list, add the TLS libraries there. - # or something, so we take care not to duplicate it). - list(FIND NNG_REQUIRED_LIBRARIES ${MBEDTLS_TLS_LIBRARY} _index) - if (_index EQUAL -1) - set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} ${MBEDTLS_LIBRARIES}) - set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} PARENT_SCOPE) - endif() - - # Likewise for the include search path. - list(FIND NNG_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR} _index) - if (_index EQUAL -1) - set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} ${MBEDTLS_INCLUDE_DIR}) - set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} PARENT_SCOPE) - endif() -endif() - -set(NNG_SOURCES ${NNG_SOURCES} ${SUPP_SOURCES} PARENT_SCOPE) diff --git a/src/supplemental/tls.h b/src/supplemental/tls.h deleted file mode 100644 index da2fe8cd..00000000 --- a/src/supplemental/tls.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// Copyright 2017 Staysail Systems, Inc. <info@staysail.tech> -// Copyright 2017 Capitar IT Group BV <info@capitar.com> -// -// This software is supplied under the terms of the MIT License, a -// copy of which should be located in the distribution where this -// file was obtained (LICENSE.txt). A copy of the license may also be -// found online at https://opensource.org/licenses/MIT. -// - -#ifndef NNG_SUPPLEMENTAL_TLS_H -#define NNG_SUPPLEMENTAL_TLS_H - -// nni_tls represents the context for a single TLS stream. -typedef struct nni_tls nni_tls; - -// nni_tls_config is the context for full TLS configuration, normally -// associated with an endpoint, for example. -typedef struct nni_tls_config nni_tls_config; - -#define NNI_TLS_CONFIG_SERVER 1 -#define NNI_TLS_CONFIG_CLIENT 0 - -extern int nni_tls_config_init(nni_tls_config **, int); -extern void nni_tls_config_fini(nni_tls_config *); - -// nni_tls_config_server_name is used by clients to set the server name -// that they expect to be talking to. This may also support the SNI -// extension for virtual hosting. -extern int nni_tls_config_server_name(nni_tls_config *, const char *); - -// nni_tls_config_ca_cert configures one or more CAs used for validation -// of peer certificates. Multiple CAs (and their chains) may be configured -// by either calling this multiple times, or by specifying a list of -// certificates as concatenated data. The certs may be in PEM or DER -// format. -extern int nni_tls_config_ca_cert(nni_tls_config *, const uint8_t *, size_t); - -// nni_tls_config_clr loads a certificate revocation list. Again, these -// are in X.509 format (either PEM or DER). -extern int nni_tls_config_crl(nni_tls_config *, const uint8_t *, size_t); - -// nni_tls_config_cert is used to load our own certificate. For servers, -// this may be called more than once to configure multiple different keys, -// for example with different algorithms depending on what the peer supports. -// On the client, only a single option is available. -extern int nni_tls_config_cert(nni_tls_config *, const uint8_t *crt, size_t); -extern int nni_tls_config_key(nni_tls_config *, const uint8_t *, size_t); -extern int nni_tls_config_pass(nni_tls_config *, const char *); - -// nni_tls_config_validate_peer is used to enable validation of the peer -// and it's certificate. If disabled, the peer's certificate will still -// be available, but may not be valid. -extern int nni_tls_config_validate_peer(nni_tls_config *, bool); - -// nni_tls_config_auth_mode is a read-ony option that is used to configure -// the authentication mode use. The default is that servers have this off -// (i.e. no client authentication) and clients have it on (they verify -// the server), which matches typical practice. -extern int nni_tls_config_auth_mode(nni_tls_config *, int); -#define NNI_TLS_CONFIG_AUTH_MODE_NONE 0 // No verification is performed -#define NNI_TLS_CONFIG_AUTH_MODE_OPTIONAL 1 // Verify cert if presented -#define NNI_TLS_CONFIG_AUTH_MODE_REQUIRED 2 // Verify cert, close if invalid - -extern int nni_tls_init(nni_tls **, nni_tls_config *, nni_plat_tcp_pipe *); -extern void nni_tls_close(nni_tls *); -extern void nni_tls_fini(nni_tls *); -extern void nni_tls_send(nni_tls *, nni_aio *); -extern void nni_tls_recv(nni_tls *, nni_aio *); - -// nni_tls_verified returns true if the peer, or false if the peer did not -// verify. (During the handshake phase, the peer is not verified, so this -// might return false if executed too soon. The verification status will -// be accurate once the handshake is finished, however. -extern int nni_tls_verified(nni_tls *); - -// nni_tls_ciphersuite_name returns the name of the ciphersuite in use. -extern const char *nni_tls_ciphersuite_name(nni_tls *); - -// TBD: getting additional peer certificate information... - -extern void nni_tls_strerror(int, char *, size_t); // review this - -#endif // NNG_SUPPLEMENTAL_TLS_H diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt new file mode 100644 index 00000000..e78f1c13 --- /dev/null +++ b/src/supplemental/tls/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright 2017 Capitar IT Group BV <info@capitar.com> +# Copyright 2017 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. +# + +if (NNG_SUPP_TLS) + set(NNG_SUPP_TLS_MBEDTLS ON) + set(TLS_SOURCES supplemental/tls/tls.h) +endif() + +# For now we only support the ARM mbedTLS library. +if (NNG_SUPP_TLS_MBEDTLS) + + Find_Package(mbedTLS REQUIRED) + + # If it isn't already in the link list, add the TLS libraries there. + # or something, so we take care not to duplicate it). + list(FIND NNG_REQUIRED_LIBRARIES ${MBEDTLS_TLS_LIBRARY} _index) + if (_index EQUAL -1) + set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} ${MBEDTLS_LIBRARIES}) + set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} PARENT_SCOPE) + endif() + + # Likewise for the include search path. + list(FIND NNG_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR} _index) + if (_index EQUAL -1) + set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} ${MBEDTLS_INCLUDE_DIR}) + set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} PARENT_SCOPE) + endif() + set(TLS_SOURCES ${TLS_SOURCES} supplemental/tls/mbedtls/tls.c) +endif() + +set(NNG_SOURCES ${NNG_SOURCES} ${TLS_SOURCES} PARENT_SCOPE) diff --git a/src/supplemental/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c index d64447ac..3bbf4a33 100644 --- a/src/supplemental/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/tls.c @@ -8,7 +8,6 @@ // found online at https://opensource.org/licenses/MIT. // -#ifdef NNG_MBEDTLS_ENABLE #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -29,7 +28,7 @@ #include "core/nng_impl.h" -#include "supplemental/tls.h" +#include "supplemental/tls/tls.h" // Implementation note. This implementation buffers data between the TLS // encryption layer (mbedTLS) and the underlying TCP socket. As a result, @@ -66,6 +65,7 @@ typedef struct nni_tls_certkey { struct nni_tls { nni_plat_tcp_pipe * tcp; mbedtls_ssl_context ctx; + nng_tls_config * cfg; // kept so we can release it nni_mtx lk; nni_aio * tcp_send; nni_aio * tcp_recv; @@ -86,7 +86,7 @@ struct nni_tls { nni_aio * handshake; // handshake aio (upper) }; -struct nni_tls_config { +struct nng_tls_config { mbedtls_ssl_config cfg_ctx; nni_mtx lk; bool active; @@ -100,6 +100,8 @@ struct nni_tls_config { bool have_ca_certs; bool have_crl; + int refcnt; // servers increment the reference + nni_list certkeys; }; @@ -142,7 +144,7 @@ nni_tls_random(void *arg, unsigned char *buf, size_t sz) { #ifdef NNG_TLS_USE_CTR_DRBG int rv; - nni_tls_config *cfg = arg; + nng_tls_config *cfg = arg; NNI_ARG_UNUSED(arg); nni_mtx_lock(&cfg->rng_lk); @@ -155,10 +157,18 @@ nni_tls_random(void *arg, unsigned char *buf, size_t sz) } void -nni_tls_config_fini(nni_tls_config *cfg) +nng_tls_config_fini(nng_tls_config *cfg) { nni_tls_certkey *ck; + nni_mtx_lock(&cfg->lk); + cfg->refcnt--; + if (cfg->refcnt != 0) { + nni_mtx_unlock(&cfg->lk); + return; + } + nni_mtx_unlock(&cfg->lk); + mbedtls_ssl_config_free(&cfg->cfg_ctx); #ifdef NNG_TLS_USE_CTR_DRBG mbedtls_ctr_drbg_free(&cfg->rng_ctx); @@ -189,9 +199,9 @@ nni_tls_config_fini(nni_tls_config *cfg) } int -nni_tls_config_init(nni_tls_config **cpp, int mode) +nng_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode) { - nni_tls_config *cfg; + nng_tls_config *cfg; int rv; int sslmode; int authmode; @@ -199,8 +209,9 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) if ((cfg = NNI_ALLOC_STRUCT(cfg)) == NULL) { return (NNG_ENOMEM); } + cfg->refcnt = 1; nni_mtx_init(&cfg->lk); - if (mode == NNI_TLS_CONFIG_SERVER) { + if (mode == NNG_TLS_MODE_SERVER) { sslmode = MBEDTLS_SSL_IS_SERVER; authmode = MBEDTLS_SSL_VERIFY_NONE; } else { @@ -216,7 +227,7 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) rv = mbedtls_ssl_config_defaults(&cfg->cfg_ctx, sslmode, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); if (rv != 0) { - nni_tls_config_fini(cfg); + nng_tls_config_fini(cfg); return (rv); } @@ -231,7 +242,7 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) rv = mbedtls_ctr_drbg_seed( &cfg->rng_ctx, nni_tls_get_entropy, NULL, NULL, 0); if (rv != 0) { - nni_tls_config_fini(cfg); + nng_tls_config_fini(cfg); return (rv); } #endif @@ -244,6 +255,14 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) } void +nni_tls_config_hold(nng_tls_config *cfg) +{ + nni_mtx_lock(&cfg->lk); + cfg->refcnt++; + nni_mtx_unlock(&cfg->lk); +} + +void nni_tls_fini(nni_tls *tp) { // Shut it all down first. @@ -263,6 +282,10 @@ nni_tls_fini(nni_tls *tp) 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 + nng_tls_config_fini(tp->cfg); + } NNI_FREE_STRUCT(tp); } @@ -290,7 +313,7 @@ nni_tls_mkerr(int err) } int -nni_tls_init(nni_tls **tpp, nni_tls_config *cfg, nni_plat_tcp_pipe *tcp) +nni_tls_init(nni_tls **tpp, nng_tls_config *cfg, nni_plat_tcp_pipe *tcp) { nni_tls *tp; int rv; @@ -348,6 +371,8 @@ nni_tls_init(nni_tls **tpp, nni_tls_config *cfg, nni_plat_tcp_pipe *tcp) } cfg->active = true; } + cfg->refcnt++; + tp->cfg = cfg; nni_mtx_unlock(&cfg->lk); nni_aio_list_init(&tp->sends); @@ -595,6 +620,18 @@ nni_tls_recv(nni_tls *tp, nni_aio *aio) nni_mtx_unlock(&tp->lk); } +int +nni_tls_peername(nni_tls *tp, nni_sockaddr *sa) +{ + return (nni_plat_tcp_pipe_peername(tp->tcp, sa)); +} + +int +nni_tls_sockname(nni_tls *tp, nni_sockaddr *sa) +{ + return (nni_plat_tcp_pipe_sockname(tp->tcp, sa)); +} + void nni_tls_do_handshake(nni_tls *tp) { @@ -758,7 +795,7 @@ nni_tls_verified(nni_tls *tp) } int -nni_tls_config_server_name(nni_tls_config *cfg, const char *name) +nng_tls_config_server_name(nng_tls_config *cfg, const char *name) { int rv; nni_mtx_lock(&cfg->lk); @@ -776,7 +813,7 @@ nni_tls_config_server_name(nni_tls_config *cfg, const char *name) } int -nni_tls_config_auth_mode(nni_tls_config *cfg, int mode) +nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode) { nni_mtx_lock(&cfg->lk); if (cfg->active) { @@ -784,15 +821,15 @@ nni_tls_config_auth_mode(nni_tls_config *cfg, int mode) return (NNG_ESTATE); } switch (mode) { - case NNI_TLS_CONFIG_AUTH_MODE_NONE: + case NNG_TLS_AUTH_MODE_NONE: mbedtls_ssl_conf_authmode( &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_NONE); break; - case NNI_TLS_CONFIG_AUTH_MODE_OPTIONAL: + case NNG_TLS_AUTH_MODE_OPTIONAL: mbedtls_ssl_conf_authmode( &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_OPTIONAL); break; - case NNI_TLS_CONFIG_AUTH_MODE_REQUIRED: + case NNG_TLS_AUTH_MODE_REQUIRED: mbedtls_ssl_conf_authmode( &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_REQUIRED); break; @@ -844,7 +881,7 @@ nni_tls_copy_key_cert_material( } int -nni_tls_config_cert(nni_tls_config *cfg, const uint8_t *key, size_t sz) +nng_tls_config_cert(nng_tls_config *cfg, const uint8_t *key, size_t sz) { int rv = 0; nni_tls_certkey *ck; @@ -885,7 +922,7 @@ err: } int -nni_tls_config_key(nni_tls_config *cfg, const uint8_t *key, size_t sz) +nng_tls_config_key(nng_tls_config *cfg, const uint8_t *key, size_t sz) { int rv = 0; nni_tls_certkey *ck; @@ -924,7 +961,7 @@ err: } int -nni_tls_config_pass(nni_tls_config *cfg, const char *pass) +nng_tls_config_pass(nng_tls_config *cfg, const char *pass) { int rv = 0; nni_tls_certkey *ck; @@ -964,7 +1001,7 @@ err: } int -nni_tls_config_ca_cert(nni_tls_config *cfg, const uint8_t *data, size_t sz) +nng_tls_config_ca_cert(nng_tls_config *cfg, const uint8_t *data, size_t sz) { uint8_t *tmp; size_t len = sz; @@ -998,7 +1035,7 @@ err: } int -nni_tls_config_crl(nni_tls_config *cfg, const uint8_t *data, size_t sz) +nng_tls_config_crl(nng_tls_config *cfg, const uint8_t *data, size_t sz) { int rv; uint8_t *tmp; @@ -1028,4 +1065,3 @@ err: nni_free(tmp, len); return (rv); } -#endif // NNG_MBEDTLS_ENABLE
\ No newline at end of file diff --git a/src/supplemental/tls/tls.h b/src/supplemental/tls/tls.h new file mode 100644 index 00000000..0c9e791f --- /dev/null +++ b/src/supplemental/tls/tls.h @@ -0,0 +1,45 @@ +// +// Copyright 2017 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2017 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#ifndef NNG_SUPPLEMENTAL_TLS_TLS_H +#define NNG_SUPPLEMENTAL_TLS_TLS_H + +// nni_tls represents the context for a single TLS stream. +typedef struct nni_tls nni_tls; + +// nni_tls_config_hold is used to get a hold on the config +// object, preventing it from being released inadvertently. +// The hold is released with a call to nng_tls_config_fini(). +// Note that a hold need not be acquired at creation, since +// 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_plat_tcp_pipe *); +extern void nni_tls_close(nni_tls *); +extern void nni_tls_fini(nni_tls *); +extern void nni_tls_send(nni_tls *, nni_aio *); +extern void nni_tls_recv(nni_tls *, nni_aio *); +extern int nni_tls_sockname(nni_tls *, nni_sockaddr *); +extern int nni_tls_peername(nni_tls *, nni_sockaddr *); + +// nni_tls_verified returns true if the peer, or false if the peer did not +// verify. (During the handshake phase, the peer is not verified, so this +// might return false if executed too soon. The verification status will +// be accurate once the handshake is finished, however. +extern int nni_tls_verified(nni_tls *); + +// nni_tls_ciphersuite_name returns the name of the ciphersuite in use. +extern const char *nni_tls_ciphersuite_name(nni_tls *); + +// TBD: getting additional peer certificate information... + +extern void nni_tls_strerror(int, char *, size_t); // review this + +#endif // NNG_SUPPLEMENTAL_TLS_TLS_H diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index 06e1c70a..c7181595 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -55,7 +55,6 @@ struct nni_ws { }; struct nni_ws_listener { - nni_tls_config * tls; nni_http_server * server; char * proto; char * url; @@ -82,7 +81,6 @@ 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_tls_config * tls; nni_http_req * req; nni_http_res * res; nni_http_client *client; @@ -1296,6 +1294,13 @@ nni_ws_listener_fini(nni_ws_listener *l) { ws_header *hdr; + nni_ws_listener_close(l); + + if (l->server != NULL) { + nni_http_server_fini(l->server); + l->server = NULL; + } + nni_mtx_fini(&l->mtx); nni_strfree(l->url); nni_strfree(l->proto); @@ -1560,6 +1565,8 @@ nni_ws_listener_init(nni_ws_listener **wslp, const char *url) { nni_ws_listener *l; int rv; + nni_aio * aio; + nni_sockaddr sa; if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { return (NNG_ENOMEM); @@ -1582,6 +1589,24 @@ nni_ws_listener_init(nni_ws_listener **wslp, const char *url) l->handler.h_host = l->host; l->handler.h_cb = ws_handler; + if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { + nni_ws_listener_fini(l); + return (rv); + } + aio->a_addr = &sa; + nni_plat_tcp_resolv(l->host, l->serv, NNG_AF_UNSPEC, true, aio); + nni_aio_wait(aio); + rv = nni_aio_result(aio); + nni_aio_fini(aio); + if (rv != 0) { + nni_ws_listener_fini(l); + return (rv); + } + if ((rv = nni_http_server_init(&l->server, &sa)) != 0) { + nni_ws_listener_fini(l); + return (rv); + } + *wslp = l; return (0); } @@ -1658,10 +1683,10 @@ nni_ws_listener_close(nni_ws_listener *l) return; } l->closed = true; - if (l->server != NULL) { + if (l->started) { nni_http_server_del_handler(l->server, l->hp); - nni_http_server_fini(l->server); - l->server = NULL; + nni_http_server_stop(l->server); + l->started = false; } NNI_LIST_FOREACH (&l->pend, ws) { nni_ws_close_error(ws, WS_CLOSE_GOING_AWAY); @@ -1675,9 +1700,7 @@ nni_ws_listener_close(nni_ws_listener *l) int nni_ws_listener_listen(nni_ws_listener *l) { - nng_sockaddr sa; - nni_aio * aio; - int rv; + int rv; nni_mtx_lock(&l->mtx); if (l->closed) { @@ -1689,25 +1712,6 @@ nni_ws_listener_listen(nni_ws_listener *l) return (NNG_ESTATE); } - if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { - nni_mtx_unlock(&l->mtx); - return (rv); - } - aio->a_addr = &sa; - nni_plat_tcp_resolv(l->host, l->serv, NNG_AF_UNSPEC, true, aio); - nni_aio_wait(aio); - rv = nni_aio_result(aio); - nni_aio_fini(aio); - if (rv != 0) { - nni_mtx_unlock(&l->mtx); - return (rv); - } - - if ((rv = nni_http_server_init(&l->server, &sa)) != 0) { - nni_mtx_unlock(&l->mtx); - return (rv); - } - rv = nni_http_server_add_handler(&l->hp, l->server, &l->handler, l); if (rv != 0) { nni_http_server_fini(l->server); @@ -1716,12 +1720,12 @@ nni_ws_listener_listen(nni_ws_listener *l) return (rv); } - // XXX: DEAL WITH HTTPS here. - if ((rv = nni_http_server_start(l->server)) != 0) { nni_http_server_del_handler(l->server, l->hp); nni_http_server_fini(l->server); l->server = NULL; + nni_mtx_unlock(&l->mtx); + return (rv); } l->started = true; @@ -1740,11 +1744,17 @@ nni_ws_listener_hook( nni_mtx_unlock(&l->mtx); } -void -nni_ws_listener_tls(nni_ws_listener *l, nni_tls_config *tls) +#ifdef NNG_SUPP_TLS +int +nni_ws_listener_set_tls(nni_ws_listener *l, nng_tls_config *tls) { - // We need to add this later. + int rv; + nni_mtx_lock(&l->mtx); + rv = nni_http_server_set_tls(l->server, tls); + nni_mtx_unlock(&l->mtx); + return (rv); } +#endif void ws_conn_cb(void *arg) @@ -1931,6 +1941,18 @@ nni_ws_dialer_init(nni_ws_dialer **dp, const char *url) return (0); } +#ifdef NNG_SUPP_TLS +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); +} +#endif + void nni_ws_dialer_close(nni_ws_dialer *d) { diff --git a/src/supplemental/websocket/websocket.h b/src/supplemental/websocket/websocket.h index 95147a9f..ccf549df 100644 --- a/src/supplemental/websocket/websocket.h +++ b/src/supplemental/websocket/websocket.h @@ -12,9 +12,8 @@ #define NNG_SUPPLEMENTAL_WEBSOCKET_WEBSOCKET_H // Pre-defined types for some prototypes. These are from other subsystems. -typedef struct nni_tls_config nni_tls_config; -typedef struct nni_http_req nni_http_req; -typedef struct nni_http_res nni_http_res; +typedef struct nni_http_req nni_http_req; +typedef struct nni_http_res nni_http_res; typedef struct nni_ws nni_ws; typedef struct nni_ws_listener nni_ws_listener; @@ -36,7 +35,7 @@ extern int nni_ws_listener_listen(nni_ws_listener *); extern void nni_ws_listener_accept(nni_ws_listener *, nni_aio *); extern void nni_ws_listener_hook( nni_ws_listener *, nni_ws_listen_hook, void *); -extern void nni_ws_listener_tls(nni_ws_listener *, nni_tls_config *); +extern int nni_ws_listener_set_tls(nni_ws_listener *, nng_tls_config *); extern int nni_ws_dialer_init(nni_ws_dialer **, const char *); extern void nni_ws_dialer_fini(nni_ws_dialer *); @@ -44,6 +43,7 @@ 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_dial(nni_ws_dialer *, nni_aio *); +extern int nni_ws_dialer_set_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 diff --git a/src/transport/tls/tls.c b/src/transport/tls/tls.c index 9c794e64..f2ca6d35 100644 --- a/src/transport/tls/tls.c +++ b/src/transport/tls/tls.c @@ -14,7 +14,7 @@ #include "core/nng_impl.h" -#include "supplemental/tls.h" +#include "supplemental/tls/tls.h" #include "tls.h" // TLS over TCP transport. Platform specific TCP operations must be @@ -61,7 +61,7 @@ struct nni_tls_ep { nni_aio * aio; nni_aio * user_aio; nni_mtx mtx; - nni_tls_config * cfg; + nng_tls_config * cfg; }; static void nni_tls_pipe_send_cb(void *); @@ -477,7 +477,7 @@ nni_tls_pipe_getopt_locaddr(void *arg, void *v, size_t *szp) nng_sockaddr sa; memset(&sa, 0, sizeof(sa)); - if ((rv = nni_plat_tcp_pipe_sockname(p->tcp, &sa)) == 0) { + if ((rv = nni_tls_sockname(p->tls, &sa)) == 0) { rv = nni_getopt_sockaddr(&sa, v, szp); } return (rv); @@ -491,7 +491,7 @@ nni_tls_pipe_getopt_remaddr(void *arg, void *v, size_t *szp) nng_sockaddr sa; memset(&sa, 0, sizeof(sa)); - if ((rv = nni_plat_tcp_pipe_peername(p->tcp, &sa)) == 0) { + if ((rv = nni_tls_peername(p->tls, &sa)) == 0) { rv = nni_getopt_sockaddr(&sa, v, szp); } return (rv); @@ -589,7 +589,7 @@ nni_tls_ep_fini(void *arg) nni_plat_tcp_ep_fini(ep->tep); } if (ep->cfg) { - nni_tls_config_fini(ep->cfg); + nng_tls_config_fini(ep->cfg); } nni_aio_fini(ep->aio); nni_mtx_fini(&ep->mtx); @@ -599,18 +599,18 @@ nni_tls_ep_fini(void *arg) static int nni_tls_ep_init(void **epp, const char *url, nni_sock *sock, int mode) { - nni_tls_ep * ep; - int rv; - char buf[NNG_MAXADDRLEN + 1]; - char * rhost; - char * rserv; - char * lhost; - char * lserv; - nni_sockaddr rsa, lsa; - nni_aio * aio; - int passive; - int tlsmode; - int authmode; + nni_tls_ep * ep; + int rv; + char buf[NNG_MAXADDRLEN + 1]; + char * rhost; + char * rserv; + char * lhost; + char * lserv; + nni_sockaddr rsa, lsa; + nni_aio * aio; + int passive; + nng_tls_mode tlsmode; + nng_tls_auth_mode authmode; // Make a copy of the url (to allow for destructive operations) if (nni_strlcpy(buf, url, sizeof(buf)) >= sizeof(buf)) { @@ -628,12 +628,12 @@ nni_tls_ep_init(void **epp, const char *url, nni_sock *sock, int mode) } if (mode == NNI_EP_MODE_DIAL) { passive = 0; - tlsmode = NNI_TLS_CONFIG_CLIENT; - authmode = NNI_TLS_CONFIG_AUTH_MODE_REQUIRED; + tlsmode = NNG_TLS_MODE_CLIENT; + authmode = NNG_TLS_AUTH_MODE_REQUIRED; } else { passive = 1; - tlsmode = NNI_TLS_CONFIG_SERVER; - authmode = NNI_TLS_CONFIG_AUTH_MODE_NONE; + tlsmode = NNG_TLS_MODE_SERVER; + authmode = NNG_TLS_AUTH_MODE_NONE; } // XXX: arguably we could defer this part to the point we do a bind @@ -684,15 +684,15 @@ nni_tls_ep_init(void **epp, const char *url, nni_sock *sock, int mode) } if (((rv = nni_plat_tcp_ep_init(&ep->tep, &lsa, &rsa, mode)) != 0) || - ((rv = nni_tls_config_init(&ep->cfg, tlsmode)) != 0) || - ((rv = nni_tls_config_auth_mode(ep->cfg, authmode)) != 0) || + ((rv = nng_tls_config_init(&ep->cfg, tlsmode)) != 0) || + ((rv = nng_tls_config_auth_mode(ep->cfg, authmode)) != 0) || ((rv = nni_aio_init(&ep->aio, nni_tls_ep_cb, ep)) != 0)) { nni_strfree(rhost); nni_tls_ep_fini(ep); return (rv); } - if ((tlsmode == NNI_TLS_CONFIG_CLIENT) && (rhost != NULL)) { - if ((rv = nni_tls_config_server_name(ep->cfg, rhost)) != 0) { + if ((tlsmode == NNG_TLS_MODE_CLIENT) && (rhost != NULL)) { + if ((rv = nng_tls_config_server_name(ep->cfg, rhost)) != 0) { nni_strfree(rhost); nni_tls_ep_fini(ep); return (rv); @@ -868,6 +868,38 @@ nni_tls_ep_getopt_linger(void *arg, void *v, size_t *szp) } static int +tls_setopt_config(void *arg, const void *data, size_t sz) +{ + nni_tls_ep * ep = arg; + nng_tls_config *cfg, *old; + + if (sz != sizeof(cfg)) { + return (NNG_EINVAL); + } + memcpy(&cfg, data, sz); + if (cfg == NULL) { + return (NNG_EINVAL); + } + if (ep == NULL) { + return (0); + } + old = ep->cfg; + nni_tls_config_hold(cfg); + ep->cfg = cfg; + if (old != NULL) { + nng_tls_config_fini(old); + } + return (0); +} + +static int +tls_getopt_config(void *arg, void *v, size_t *szp) +{ + nni_tls_ep *ep = arg; + return (nni_getopt_ptr(ep->cfg, v, szp)); +} + +static int tls_setopt_ca_cert(void *arg, const void *data, size_t sz) { nni_tls_ep *ep = arg; @@ -875,7 +907,7 @@ tls_setopt_ca_cert(void *arg, const void *data, size_t sz) if (ep == NULL) { return (0); } - return (nni_tls_config_ca_cert(ep->cfg, data, sz)); + return (nng_tls_config_ca_cert(ep->cfg, data, sz)); } static int @@ -886,7 +918,7 @@ tls_setopt_cert(void *arg, const void *data, size_t sz) if (ep == NULL) { return (0); } - return (nni_tls_config_cert(ep->cfg, data, sz)); + return (nng_tls_config_cert(ep->cfg, data, sz)); } static int @@ -897,7 +929,7 @@ tls_setopt_private_key(void *arg, const void *data, size_t sz) if (ep == NULL) { return (0); } - return (nni_tls_config_key(ep->cfg, data, sz)); + return (nng_tls_config_key(ep->cfg, data, sz)); } static int @@ -914,13 +946,9 @@ tls_setopt_pass(void *arg, const void *data, size_t sz) if (ep == NULL) { return (0); } - return (nni_tls_config_pass(ep->cfg, data)); + return (nng_tls_config_pass(ep->cfg, data)); } -int nng_tls_auth_mode_none = NNI_TLS_CONFIG_AUTH_MODE_NONE; -int nng_tls_auth_mode_required = NNI_TLS_CONFIG_AUTH_MODE_REQUIRED; -int nng_tls_auth_mode_optional = NNI_TLS_CONFIG_AUTH_MODE_OPTIONAL; - static int tls_getopt_auth_mode(void *arg, void *v, size_t *szp) { @@ -938,9 +966,9 @@ tls_setopt_auth_mode(void *arg, const void *data, size_t sz) rv = nni_setopt_int(&mode, data, sz, -100, 100); if (rv == 0) { switch (mode) { - case NNI_TLS_CONFIG_AUTH_MODE_NONE: - case NNI_TLS_CONFIG_AUTH_MODE_OPTIONAL: - case NNI_TLS_CONFIG_AUTH_MODE_REQUIRED: + case NNG_TLS_AUTH_MODE_NONE: + case NNG_TLS_AUTH_MODE_OPTIONAL: + case NNG_TLS_AUTH_MODE_REQUIRED: break; default: rv = NNG_EINVAL; @@ -952,7 +980,7 @@ tls_setopt_auth_mode(void *arg, const void *data, size_t sz) return (rv); } - if ((rv = nni_tls_config_auth_mode(ep->cfg, mode)) == 0) { + if ((rv = nng_tls_config_auth_mode(ep->cfg, mode)) == 0) { ep->authmode = mode; } return (rv); @@ -998,6 +1026,11 @@ static nni_tran_ep_option nni_tls_ep_options[] = { .eo_setopt = nni_tls_ep_setopt_linger, }, { + .eo_name = NNG_OPT_TLS_CONFIG, + .eo_getopt = tls_getopt_config, + .eo_setopt = tls_setopt_config, + }, + { .eo_name = NNG_OPT_TLS_CA_CERT, .eo_getopt = NULL, .eo_setopt = tls_setopt_ca_cert, diff --git a/src/transport/tls/tls.h b/src/transport/tls/tls.h index 4317ae55..b36ee774 100644 --- a/src/transport/tls/tls.h +++ b/src/transport/tls/tls.h @@ -49,14 +49,12 @@ NNG_DECL int nng_tls_register(void); // and off on servers/listeners, by default. #define NNG_OPT_TLS_AUTH_MODE "tls:auth-mode" -extern int nng_tls_auth_mode_required; -extern int nng_tls_auth_mode_none; -extern int nng_tls_auth_mode_optional; - // NNG_OPT_TLS_AUTH_VERIFIED is a boolean that can be read on pipes, // indicating whether the peer certificate is verified. #define NNG_OPT_TLS_AUTH_VERIFIED "tls:auth-verified" +#define NNG_OPT_TLS_CONFIG "tls:config" + // XXX: TBD: Ciphersuite selection and reporting. Session reuse? #endif // NNG_TRANSPORT_TLS_TLS_H diff --git a/src/transport/ws/websocket.c b/src/transport/ws/websocket.c index 4f948fb7..1162338e 100644 --- a/src/transport/ws/websocket.c +++ b/src/transport/ws/websocket.c @@ -15,6 +15,7 @@ #include "core/nng_impl.h" #include "supplemental/http/http.h" +#include "supplemental/tls/tls.h" #include "supplemental/websocket/websocket.h" #include "websocket.h" @@ -42,6 +43,7 @@ struct ws_ep { nni_ws_listener *listener; nni_ws_dialer * dialer; nni_list headers; // to send, res or req + nng_tls_config * tls; }; struct ws_pipe { @@ -368,9 +370,6 @@ ws_ep_setopt_headers(ws_ep *ep, const void *v, size_t sz) ws_hdr * h; int rv; - if (nni_strnlen(v, sz) >= sz) { - return (NNG_EINVAL); - } if (ep == NULL) { return (0); } @@ -449,7 +448,11 @@ ws_ep_setopt_reqhdrs(void *arg, const void *v, size_t sz) { ws_ep *ep = arg; - if (ep->mode == NNI_EP_MODE_LISTEN) { + if (nni_strnlen(v, sz) >= sz) { + return (NNG_EINVAL); + } + + if ((ep != NULL) && (ep->mode == NNI_EP_MODE_LISTEN)) { return (NNG_EREADONLY); } return (ws_ep_setopt_headers(ep, v, sz)); @@ -460,7 +463,11 @@ ws_ep_setopt_reshdrs(void *arg, const void *v, size_t sz) { ws_ep *ep = arg; - if (ep->mode == NNI_EP_MODE_DIAL) { + if (nni_strnlen(v, sz) >= sz) { + return (NNG_EINVAL); + } + + if ((ep != NULL) && (ep->mode == NNI_EP_MODE_DIAL)) { return (NNG_EREADONLY); } return (ws_ep_setopt_headers(ep, v, sz)); @@ -594,6 +601,11 @@ ws_ep_fini(void *arg) nni_strfree(ep->addr); nni_strfree(ep->protoname); nni_mtx_fini(&ep->mtx); +#ifdef NNG_TRANSPORT_WSS + if (ep->tls) { + nng_tls_config_fini(ep->tls); + } +#endif NNI_FREE_STRUCT(ep); } @@ -689,10 +701,21 @@ ws_ep_init(void **epp, const char *url, nni_sock *sock, int mode) if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { return (NNG_ENOMEM); } - nni_mtx_init(&ep->mtx); NNI_LIST_INIT(&ep->headers, ws_hdr, node); +#ifdef NNG_TRANSPORT_WSS + if (strncmp(url, "wss://", 4) == 0) { + rv = nng_tls_config_init(&ep->tls, + mode == NNI_EP_MODE_DIAL ? NNG_TLS_MODE_CLIENT + : NNG_TLS_MODE_SERVER); + if (rv != 0) { + NNI_FREE_STRUCT(ep); + return (rv); + } + } +#endif + // List of pipes (server only). nni_aio_list_init(&ep->aios); @@ -758,3 +781,100 @@ nng_ws_register(void) { return (nni_tran_register(&ws_tran)); } + +#ifdef NNG_TRANSPORT_WSS + +static int +wss_ep_getopt_tlsconfig(void *arg, void *v, size_t *szp) +{ + ws_ep *ep = arg; + return (nni_getopt_ptr(ep->tls, v, szp)); +} + +static int +wss_ep_setopt_tlsconfig(void *arg, const void *v, size_t sz) +{ + ws_ep * ep = arg; + nng_tls_config *cfg; + int rv; + + if (sz != sizeof(cfg)) { + return (NNG_EINVAL); + } + memcpy(&cfg, v, sz); + if (cfg == NULL) { + // NULL is clearly invalid. + return (NNG_EINVAL); + } + if (ep == NULL) { + return (0); + } + nni_mtx_lock(&ep->mtx); + if (ep->mode == NNI_EP_MODE_LISTEN) { + rv = nni_ws_listener_set_tls(ep->listener, cfg); + } else { + rv = nni_ws_dialer_set_tls(ep->dialer, cfg); + } + if (rv == 0) { + if (ep->tls != NULL) { + nng_tls_config_fini(ep->tls); + } + nni_tls_config_hold(cfg); + ep->tls = cfg; + } + nni_mtx_unlock(&ep->mtx); + return (rv); +} + +static nni_tran_ep_option wss_ep_options[] = { + { + .eo_name = NNG_OPT_RECVMAXSZ, + .eo_getopt = ws_ep_getopt_recvmaxsz, + .eo_setopt = ws_ep_setopt_recvmaxsz, + }, + { + .eo_name = NNG_OPT_WSS_REQUEST_HEADERS, + .eo_getopt = NULL, + .eo_setopt = ws_ep_setopt_reqhdrs, + }, + { + .eo_name = NNG_OPT_WSS_RESPONSE_HEADERS, + .eo_getopt = NULL, + .eo_setopt = ws_ep_setopt_reshdrs, + }, + { + .eo_name = NNG_OPT_WSS_TLS_CONFIG, + .eo_getopt = wss_ep_getopt_tlsconfig, + .eo_setopt = wss_ep_setopt_tlsconfig, + }, + + // terminate list + { NULL, NULL, NULL }, +}; + +static nni_tran_ep wss_ep_ops = { + .ep_init = ws_ep_init, + .ep_fini = ws_ep_fini, + .ep_connect = ws_ep_connect, + .ep_bind = ws_ep_bind, + .ep_accept = ws_ep_accept, + .ep_close = ws_ep_close, + .ep_options = wss_ep_options, +}; + +static nni_tran wss_tran = { + .tran_version = NNI_TRANSPORT_VERSION, + .tran_scheme = "wss", + .tran_ep = &wss_ep_ops, + .tran_pipe = &ws_pipe_ops, + .tran_init = ws_tran_init, + .tran_fini = ws_tran_fini, +}; + +int +nng_wss_register(void) +{ + return (nni_tran_register(&wss_tran)); +} + +#endif // NNG_TRANSPORT_WSS diff --git a/src/transport/ws/websocket.h b/src/transport/ws/websocket.h index 2e86aaf0..a0d8a6cc 100644 --- a/src/transport/ws/websocket.h +++ b/src/transport/ws/websocket.h @@ -15,12 +15,30 @@ NNG_DECL int nng_ws_register(void); -// NNG_OPT_TLS_REQUEST_HEADERS is a string containing the +// NNG_OPT_WS_REQUEST_HEADERS is a string containing the // request headers, formatted as CRLF terminated lines. #define NNG_OPT_WS_REQUEST_HEADERS "ws:request-headers" -// NNG_OPT_TLS_RESPONSE_HEADERS is a string containing the +// NNG_OPT_WS_RESPONSE_HEADERS is a string containing the // response headers, formatted as CRLF terminated lines. #define NNG_OPT_WS_RESPONSE_HEADERS "ws:response-headers" +// NNG_OPT_WSS_TLS_CONFIG is a pointer to a an nng_tls_config +// object. This property is only available for wss:// style +// endpoints. Note that when configuring the object, a hold +// is placed on the TLS configuration. When retrieving the +// object, no hold is placed, and so the caller must take care +// not to use the configuration object after the endpoint it +// is associated with is removed. Furthermore, as this is a +// pointer, applications must take care to pass only valid +// data -- incorrect pointer values will lead to undefined +// behavior. +#define NNG_OPT_WSS_TLS_CONFIG "wss:tls-config" + +// These aliases are for WSS naming consistency. +#define NNG_OPT_WSS_REQUEST_HEADERS NNG_OPT_WS_REQUEST_HEADERS +#define NNG_OPT_WSS_RESPONSE_HEADERS NNG_OPT_WS_RESPONSE_HEADERS + +NNG_DECL int nng_wss_register(void); + #endif // NNG_TRANSPORT_WS_WEBSOCKET_H diff --git a/src/transport/zerotier/CMakeLists.txt b/src/transport/zerotier/CMakeLists.txt index c1bb0c35..a4271eb7 100644 --- a/src/transport/zerotier/CMakeLists.txt +++ b/src/transport/zerotier/CMakeLists.txt @@ -11,6 +11,7 @@ # ZeroTier protocol set (NNG_TRANSPORT_ZEROTIER_SOURCE "" CACHE PATH "Location of ZeroTier source tree.") +mark_as_advanced(NNG_TRANSPORT_ZEROTIER_SOURCE) if (NNG_TRANSPORT_ZEROTIER) diff --git a/src/transport/zerotier/zerotier.c b/src/transport/zerotier/zerotier.c index 3cb2871a..f6594eb3 100644 --- a/src/transport/zerotier/zerotier.c +++ b/src/transport/zerotier/zerotier.c @@ -8,7 +8,6 @@ // found online at https://opensource.org/licenses/MIT. // -#ifdef NNG_HAVE_ZEROTIER #include <ctype.h> #include <stdio.h> #include <stdlib.h> @@ -2804,5 +2803,3 @@ nng_zt_register(void) { return (nni_tran_register(&zt_tran)); } - -#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7f37c589..5f1a834f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -46,19 +46,21 @@ if (NNG_TESTS) list (APPEND all_tests convey_test) set (TEST_PORT 12100) - macro (add_nng_test NAME TIMEOUT) - list (APPEND all_tests ${NAME}) - add_executable (${NAME} ${NAME}.c convey.c) - target_link_libraries (${NAME} ${PROJECT_NAME}_static) - target_link_libraries (${NAME} ${NNG_REQUIRED_LIBRARIES}) - target_compile_definitions(${NAME} PUBLIC -DNNG_STATIC_LIB) - if (CMAKE_THREAD_LIBS_INIT) - target_link_libraries (${NAME} "${CMAKE_THREAD_LIBS_INIT}") - endif() + macro (add_nng_test NAME TIMEOUT COND) + if (${COND}) + list (APPEND all_tests ${NAME}) + add_executable (${NAME} ${NAME}.c convey.c) + target_link_libraries (${NAME} ${PROJECT_NAME}_static) + target_link_libraries (${NAME} ${NNG_REQUIRED_LIBRARIES}) + target_compile_definitions(${NAME} PUBLIC -DNNG_STATIC_LIB) + if (CMAKE_THREAD_LIBS_INIT) + target_link_libraries (${NAME} "${CMAKE_THREAD_LIBS_INIT}") + endif() - add_test (NAME ${NAME} COMMAND ${NAME} -v -p TEST_PORT=${TEST_PORT}) - set_tests_properties (${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) - math (EXPR TEST_PORT "${TEST_PORT}+20") + add_test (NAME ${NAME} COMMAND ${NAME} -v -p TEST_PORT=${TEST_PORT}) + set_tests_properties (${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) + math (EXPR TEST_PORT "${TEST_PORT}+20") + endif() endmacro (add_nng_test) # Compatibility tests are only added if all of the legacy protocols @@ -111,57 +113,61 @@ if (NNG_TESTS) endif() endmacro (add_nng_cpp_test) + macro (add_nng_proto_test NAME TIMEOUT P1 P2) + if (${P1} AND ${P2}) + add_nng_test(${NAME} ${TIMEOUT} ON) + else() + message (STATUS "Protocol test ${NAME} disabled (unconfigured)") + endif() + endmacro() else () - macro (add_nng_test NAME TIMEOUT) + macro (add_nng_test NAME TIMEOUT COND) endmacro (add_nng_test) macro (add_nng_compat_test NAME TIMEOUT) endmacro (add_nng_compat_test) macro (add_nng_cpp_test NAME TIMEOUT) endmacro (add_nng_cpp_test) + macro (add_nng_proto_test NAME TIMEOUT P1 P2) + endmacro() endif () -add_nng_test(aio 5) -add_nng_test(bus 5) -add_nng_test(files 5) -add_nng_test(idhash 5) -add_nng_test(inproc 5) -add_nng_test(ipc 5) -add_nng_test(list 5) -add_nng_test(platform 5) -add_nng_test(reqrep 5) -add_nng_test(pipeline 5) -add_nng_test(pollfd 5) -add_nng_test(pubsub 5) -add_nng_test(reconnect 5) -add_nng_test(resolv 10) -add_nng_test(sock 5) -add_nng_test(survey 5) -add_nng_test(synch 5) -add_nng_test(transport 5) -add_nng_test(tls 10) -add_nng_test(tcp 5) -add_nng_test(tcp6 5) -add_nng_test(scalability 20) -add_nng_test(message 5) -add_nng_test(device 5) -add_nng_test(errors 2) -add_nng_test(pair1 5) -add_nng_test(udp 5) -add_nng_test(zt 60) -add_nng_test(multistress 60) -add_nng_test(ws 30) - -if (NNG_SUPP_BASE64) - add_nng_test(base64 5) -endif() -if (NNG_SUPP_HTTP) - add_nng_test(httpclient 30) - add_nng_test(httpserver 30) -endif() -if (NNG_SUPP_SHA1) - add_nng_test(sha1 5) -endif() +add_nng_test(aio 5 ON) +add_nng_test(base64 5 NNG_SUPP_BASE64) +add_nng_test(device 5 ON) +add_nng_test(errors 2 ON) +add_nng_test(files 5 ON) +add_nng_test(httpclient 30 NNG_SUPP_HTTP) +add_nng_test(httpserver 30 NNG_SUPP_HTTP) +add_nng_test(idhash 5 ON) +add_nng_test(inproc 5 NNG_TRANSPORT_INPROC) +add_nng_test(ipc 5 NNG_TRANSPORT_IPC) +add_nng_test(list 5 ON) +add_nng_test(message 5 ON) +add_nng_test(multistress 60 ON) +add_nng_test(platform 5 ON) +add_nng_test(pollfd 5 ON) +add_nng_test(reconnect 5 ON) +add_nng_test(resolv 10 ON) +add_nng_test(scalability 20 ON) +add_nng_test(sha1 5 NNG_SUPP_SHA1) +add_nng_test(sock 5 ON) +add_nng_test(synch 5 ON) +add_nng_test(tls 10 NNG_TRANSPORT_TLS) +add_nng_test(tcp 5 NNG_TRANSPORT_TCP) +add_nng_test(tcp6 5 NNG_TRANSPORT_TCP) +add_nng_test(transport 5 ON) +add_nng_test(udp 5 ON) +add_nng_test(ws 30 NNG_TRANSPORT_WS) +add_nng_test(wss 30 NNG_TRANSPORT_WSS) +add_nng_test(zt 60 NNG_TRANSPORT_ZEROTIER) + +add_nng_proto_test(bus 5 NNG_PROTO_BUS0 NNG_PROTO_BUS0) +add_nng_test(pipeline 5 NNG_PROTO_PULL0 NNG_PROTO_PIPELINE0) +add_nng_proto_test(pair1 5 NNG_PROTO_PAIR1 NNG_PROTO_PAIR1) +add_nng_proto_test(pubsub 5 NNG_PROTO_PUB0 NNG_PROTO_SUB0) +add_nng_proto_test(reqrep 5 NNG_PROTO_REQ0 NNG_PROTO_REP0) +add_nng_test(survey 5 NNG_PROTO_SURVEYOR0 NNG_PROTO_RESPONDENT0) # compatbility tests # We only support these if ALL the legacy protocols are supported. This diff --git a/tests/multistress.c b/tests/multistress.c index 7088ac14..6a41a551 100644 --- a/tests/multistress.c +++ b/tests/multistress.c @@ -44,7 +44,7 @@ const char *inproc_template = "inproc://nng_multistress_%d"; const char *ipc_template = "ipc:///tmp/nng_multistress_%d"; const char *templates[] = { -#ifdef NNG_HAVE_TCP +#ifdef NNG_TRANSPORT_TCP "tcp://127.0.0.1:%d", #endif // It would be nice to test TCPv6, but CI doesn't support it. @@ -52,10 +52,10 @@ const char *templates[] = { #ifdef NNG_TEST_TCPV6 "tcp://[::1]:%d", #endif -#ifdef NNG_HAVE_INPROC +#ifdef NNG_TRANSPORT_INPROC "inproc://nng_multistress_%d", #endif -#ifdef NNG_HAVE_IPC +#ifdef NNG_TRANSPORT_IPC "ipc:///tmp/nng_multistress_%d", #endif }; diff --git a/tests/tls.c b/tests/tls.c index fa44d9c9..70b22fea 100644 --- a/tests/tls.c +++ b/tests/tls.c @@ -105,20 +105,54 @@ check_props_v4(nng_msg *msg, nng_listener l, nng_dialer d) } static int -init_tls(trantest *tt) +init_dialer_tls(trantest *tt, nng_dialer d) { - const char *own[3]; - - So(nng_setopt(tt->reqsock, NNG_OPT_TLS_CA_CERT, server_cert, - sizeof(server_cert)) == 0); - own[0] = server_cert; - own[1] = server_key; - own[2] = NULL; - So(nng_setopt(tt->repsock, NNG_OPT_TLS_CERT, server_cert, - sizeof(server_cert)) == 0); - So(nng_setopt(tt->repsock, NNG_OPT_TLS_PRIVATE_KEY, server_key, - sizeof(server_key)) == 0); + nng_tls_config *cfg; + int rv; + + if ((rv = nng_tls_config_init(&cfg, NNG_TLS_MODE_CLIENT)) != 0) { + return (rv); + } + if ((rv = nng_tls_config_ca_cert( + cfg, (void *) server_cert, sizeof(server_cert))) != 0) { + goto out; + } + if ((rv = nng_tls_config_server_name(cfg, "127.0.0.1")) != 0) { + goto out; + } + nng_tls_config_auth_mode(cfg, NNG_TLS_AUTH_MODE_NONE); + rv = nng_dialer_setopt_ptr(d, NNG_OPT_TLS_CONFIG, cfg); + +out: + nng_tls_config_fini(cfg); + return (rv); +} +static int +init_listener_tls(trantest *tt, nng_listener l) +{ + nng_tls_config *cfg; + int rv; + + if ((rv = nng_tls_config_init(&cfg, NNG_TLS_MODE_SERVER)) != 0) { + return (rv); + } + if ((rv = nng_tls_config_cert( + cfg, (void *) server_cert, sizeof(server_cert))) != 0) { + nng_tls_config_fini(cfg); + return (rv); + } + if ((rv = nng_tls_config_key( + cfg, (void *) server_key, sizeof(server_key))) != 0) { + nng_tls_config_fini(cfg); + return (rv); + } + + if ((rv = nng_listener_setopt_ptr(l, NNG_OPT_TLS_CONFIG, cfg)) != 0) { + nng_tls_config_fini(cfg); + return (rv); + } + nng_tls_config_fini(cfg); return (0); } @@ -126,8 +160,10 @@ TestMain("TLS Transport", { static trantest tt; - tt.init = init_tls; - tt.tmpl = "tls+tcp://127.0.0.1:%u"; + tt.dialer_init = init_dialer_tls; + tt.listener_init = init_listener_tls; + tt.tmpl = "tls+tcp://127.0.0.1:%u"; + tt.proptest = check_props_v4; atexit(nng_fini); trantest_test(&tt); diff --git a/tests/trantest.h b/tests/trantest.h index b1b0ff80..324668dd 100644 --- a/tests/trantest.h +++ b/tests/trantest.h @@ -26,41 +26,42 @@ typedef int (*trantest_proptest_t)(nng_msg *, nng_listener, nng_dialer); typedef struct trantest trantest; struct trantest { - const char * tmpl; - char addr[NNG_MAXADDRLEN + 1]; - nng_socket reqsock; - nng_socket repsock; - nni_tran * tran; - nng_dialer dialer; - nng_listener listener; + const char *tmpl; + char addr[NNG_MAXADDRLEN + 1]; + nng_socket reqsock; + nng_socket repsock; + nni_tran * tran; int (*init)(struct trantest *); void (*fini)(struct trantest *); - int (*dialer_init)(struct trantest *); - int (*listener_init)(struct trantest *); + int (*dialer_init)(struct trantest *, nng_dialer); + int (*listener_init)(struct trantest *, nng_listener); int (*proptest)(nng_msg *, nng_listener, nng_dialer); void *private; // transport specific private data }; unsigned trantest_port = 0; -#ifndef NNG_HAVE_ZEROTIER +#ifndef NNG_TRANSPORT_ZEROTIER #define nng_zt_register notransport #endif -#ifndef NNG_HAVE_INPROC +#ifndef NNG_TRANSPORT_INPROC #define nng_inproc_register notransport #endif -#ifndef NNG_HAVE_IPC +#ifndef NNG_TRANSPORT_IPC #define nng_ipc_register notransport #endif -#ifndef NNG_HAVE_TCP +#ifndef NNG_TRANSPORT_TCP #define nng_tcp_register notransport #endif -#ifndef NNG_HAVE_TLS +#ifndef NNG_TRANSPORT_TLS #define nng_tls_register notransport #endif -#ifndef NNG_HAVE_WEBSOCKET +#ifndef NNG_TRANSPORT_WS #define nng_ws_register notransport #endif +#ifndef NNG_TRANSPORT_WSS +#define nng_wss_register notransport +#endif int notransport(void) @@ -76,24 +77,27 @@ notransport(void) void trantest_checktran(const char *url) { -#ifndef NNG_HAVE_ZEROTIER - CHKTRAN(url, "zt:"); -#endif -#ifndef NNG_HAVE_INPROC +#ifndef NNG_TRANSPORT_INPROC CHKTRAN(url, "inproc:"); #endif -#ifndef NNG_HAVE_IPC +#ifndef NNG_TRANSPORT_IPC CHKTRAN(url, "ipc:"); #endif -#ifndef NNG_HAVE_TCP +#ifndef NNG_TRANSPORT_TCP CHKTRAN(url, "tcp:"); #endif -#ifndef NNG_HAVE_TLS +#ifndef NNG_TRANSPORT_TLS CHKTRAN(url, "tls+tcp:"); #endif -#ifndef NNG_HAVE_WEBSOCKET +#ifndef NNG_TRANSPORT_WS CHKTRAN(url, "ws:"); #endif +#ifndef NNG_TRANSPORT_WSS + CHKTRAN(url, "wss:"); +#endif +#ifndef NNG_TRANSPORT_ZEROTIER + CHKTRAN(url, "zt:"); +#endif (void) url; } @@ -149,13 +153,53 @@ trantest_fini(trantest *tt) } int -trantest_dial(trantest *tt) +trantest_dial(trantest *tt, nng_dialer *dp) { - So(nng_dialer_create(&tt->dialer, tt->reqsock, tt->addr) == 0); + nng_dialer d; + int rv; + *dp = 0; + + rv = nng_dialer_create(&d, tt->reqsock, tt->addr); + if (rv != 0) { + return (rv); + } if (tt->dialer_init != NULL) { - So(tt->dialer_init(tt) == 0); + if ((rv = tt->dialer_init(tt, d)) != 0) { + nng_dialer_close(d); + return (rv); + } + } + if ((rv = nng_dialer_start(d, 0)) != 0) { + nng_dialer_close(d); + return (rv); } - return (nng_dialer_start(tt->dialer, 0)); + *dp = d; + return (0); +} + +int +trantest_listen(trantest *tt, nng_listener *lp) +{ + int rv; + nng_listener l; + *lp = 0; + + rv = nng_listener_create(&l, tt->repsock, tt->addr); + if (rv != 0) { + return (rv); + } + if (tt->listener_init != NULL) { + if ((rv = tt->listener_init(tt, l)) != 0) { + nng_listener_close(l); + return (rv); + } + } + if ((rv = nng_listener_start(l, 0)) != 0) { + nng_listener_close(l); + return (rv); + } + *lp = l; + return (rv); } void @@ -174,11 +218,11 @@ trantest_conn_refused(trantest *tt) Convey("Connection refused works", { nng_dialer d = 0; - So(nng_dial(tt->reqsock, tt->addr, &d, 0) == NNG_ECONNREFUSED); + So(trantest_dial(tt, &d) == NNG_ECONNREFUSED); So(d == 0); - So(nng_dial(tt->repsock, tt->addr, &d, 0) == NNG_ECONNREFUSED); + So(trantest_dial(tt, &d) == NNG_ECONNREFUSED); So(d == 0); - }) + }); } void @@ -187,13 +231,13 @@ trantest_duplicate_listen(trantest *tt) Convey("Duplicate listen rejected", { nng_listener l; int rv; - rv = nng_listen(tt->repsock, tt->addr, &l, 0); + rv = trantest_listen(tt, &l); So(rv == 0); So(l != 0); l = 0; - So(nng_listen(tt->repsock, tt->addr, &l, 0) == NNG_EADDRINUSE); + So(trantest_listen(tt, &l) == NNG_EADDRINUSE); So(l == 0); - }) + }); } void @@ -202,11 +246,11 @@ trantest_listen_accept(trantest *tt) Convey("Listen and accept", { nng_listener l; nng_dialer d; - So(nng_listen(tt->repsock, tt->addr, &l, 0) == 0); + So(trantest_listen(tt, &l) == 0); So(l != 0); d = 0; - So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0); + So(trantest_dial(tt, &d) == 0); So(d != 0); }) } @@ -216,6 +260,7 @@ trantest_send_recv(trantest *tt) { Convey("Send and recv", { nng_listener l; + nng_dialer d; nng_msg * send; nng_msg * recv; size_t len; @@ -223,9 +268,10 @@ trantest_send_recv(trantest *tt) char url[NNG_MAXADDRLEN]; size_t sz; - So(nng_listen(tt->repsock, tt->addr, &l, 0) == 0); + So(trantest_listen(tt, &l) == 0); So(l != 0); - So(trantest_dial(tt) == 0); + So(trantest_dial(tt, &d) == 0); + So(d != 0); nng_msleep(200); // listener may be behind slightly @@ -269,9 +315,9 @@ trantest_check_properties(trantest *tt, trantest_proptest_t f) nng_msg * recv; int rv; - So(nng_listen(tt->repsock, tt->addr, &l, 0) == 0); + So(trantest_listen(tt, &l) == 0); So(l != 0); - So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0); + So(trantest_dial(tt, &d) == 0); So(d != 0); nng_msleep(200); // listener may be behind slightly @@ -311,9 +357,9 @@ trantest_send_recv_large(trantest *tt) data[i] = nni_random() & 0xff; } - So(nng_listen(tt->repsock, tt->addr, &l, 0) == 0); + So(trantest_listen(tt, &l) == 0); So(l != 0); - So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0); + So(trantest_dial(tt, &d) == 0); So(d != 0); nng_msleep(200); // listener may be behind slightly diff --git a/tests/wss.c b/tests/wss.c new file mode 100644 index 00000000..2f701117 --- /dev/null +++ b/tests/wss.c @@ -0,0 +1,206 @@ +// +// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// Copyright 2017 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "convey.h" +#include "nng.h" +#include "protocol/pair1/pair.h" +#include "transport/ws/websocket.h" +#include "trantest.h" + +#include "stubs.h" +// TCP tests. + +#ifndef _WIN32 +#include <arpa/inet.h> +#endif + +// These keys are for demonstration purposes ONLY. DO NOT USE. +// The certificate is valid for 100 years, because I don't want to +// have to regenerate it ever again. The CN is 127.0.0.1, and self-signed. +// +// Generated using openssl: +// +// % openssl ecparam -name secp521r1 -noout -genkey -out key.key +// % openssl req -new -key key.key -out cert.csr +// % openssl x509 -req -in cert.csr -days 36500 -out cert.crt -signkey key.key +// +// Relevant metadata: +// +// Certificate: +// Data: +// Version: 1 (0x0) +// Serial Number: 9808857926806240008 (0x882010509b8f7b08) +// Signature Algorithm: ecdsa-with-SHA1 +// Issuer: C=US, ST=CA, L=San Diego, O=nanomsg, CN=127.0.0.1 +// Validity +// Not Before: Nov 17 20:08:06 2017 GMT +// Not After : Oct 24 20:08:06 2117 GMT +// Subject: C=US, ST=CA, L=San Diego, O=nanomsg, CN=127.0.0.1 +// +static const char server_cert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICIjCCAYMCCQDaC9ARg31kIjAKBggqhkjOPQQDAjBUMQswCQYDVQQGEwJVUzEL\n" + "MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNhbiBEaWVnbzEQMA4GA1UECgwHbmFub21z\n" + "ZzESMBAGA1UEAwwJMTI3LjAuMC4xMCAXDTE3MTExNzIwMjczMloYDzIxMTcxMDI0\n" + "MjAyNzMyWjBUMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNh\n" + "biBEaWVnbzEQMA4GA1UECgwHbmFub21zZzESMBAGA1UEAwwJMTI3LjAuMC4xMIGb\n" + "MBAGByqGSM49AgEGBSuBBAAjA4GGAAQAN7vDK6GEiSguMsOuhfOvGyiVc37Sog0b\n" + "UkpaiS6+SagTmXFSN1Rgh9isxKFYJvcCtAko3v0I8rAVQucdhf5B3hEBMQlbBIuM\n" + "rMKT6ZQJ+eiwyb4O3Scgd7DoL3tc/kOqijwB/5hJ4sZdquDKP5DDFe5fAf4MNtzY\n" + "4C+iApWlKq/LoXkwCgYIKoZIzj0EAwIDgYwAMIGIAkIBOuJAWmNSdd6Ovmr6Ebg3\n" + "UF9ZrsNwARd9BfYbBk5OQhUOjCLB6d8aLi49WOm1WoRvOS5PaVvmvSfNhaw8b5nV\n" + "hnYCQgC+EmJ6C3bEcZrndhfbqvCaOGkc7/SrKhC6fS7mJW4wL90QUV9WjQ2Ll6X5\n" + "PxkSj7s0SvD6T8j7rju5LDgkdZc35A==\n" + "-----END CERTIFICATE-----\n"; + +static const char server_key[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MIHcAgEBBEIB20OHMntU2UJW2yuQn2f+bLsuhTT5KRGorcocnqxatWLvxuF1cfUA\n" + "TjQxRRS6BIUvFt1fMIklp9qedJF00JHy4qWgBwYFK4EEACOhgYkDgYYABAA3u8Mr\n" + "oYSJKC4yw66F868bKJVzftKiDRtSSlqJLr5JqBOZcVI3VGCH2KzEoVgm9wK0CSje\n" + "/QjysBVC5x2F/kHeEQExCVsEi4yswpPplAn56LDJvg7dJyB3sOgve1z+Q6qKPAH/\n" + "mEnixl2q4Mo/kMMV7l8B/gw23NjgL6IClaUqr8uheQ==\n" + "-----END EC PRIVATE KEY-----\n"; + +static int +check_props_v4(nng_msg *msg, nng_listener l, nng_dialer d) +{ + nng_pipe p; + size_t z; + p = nng_msg_get_pipe(msg); + So(p > 0); + + Convey("Local address property works", { + nng_sockaddr la; + z = sizeof(nng_sockaddr); + So(nng_pipe_getopt(p, NNG_OPT_LOCADDR, &la, &z) == 0); + So(z == sizeof(la)); + So(la.s_un.s_family == NNG_AF_INET); + So(la.s_un.s_in.sa_port == htons(trantest_port - 1)); + So(la.s_un.s_in.sa_port != 0); + So(la.s_un.s_in.sa_addr == htonl(0x7f000001)); + }); + + Convey("Remote address property works", { + nng_sockaddr ra; + z = sizeof(nng_sockaddr); + So(nng_pipe_getopt(p, NNG_OPT_REMADDR, &ra, &z) == 0); + So(z == sizeof(ra)); + So(ra.s_un.s_family == NNG_AF_INET); + So(ra.s_un.s_in.sa_port != 0); + So(ra.s_un.s_in.sa_addr == htonl(0x7f000001)); + }); + + Convey("Request header property works", { + char * buf; + size_t len; + z = 0; + buf = NULL; + So(nng_pipe_getopt(p, NNG_OPT_WS_REQUEST_HEADERS, buf, &z) == + 0); + So(z > 0); + len = z; + So((buf = nni_alloc(len)) != NULL); + So(nng_pipe_getopt(p, NNG_OPT_WS_REQUEST_HEADERS, buf, &z) == + 0); + So(strstr(buf, "Sec-WebSocket-Key") != NULL); + So(z == len); + nni_free(buf, len); + }); + + Convey("Response header property works", { + char * buf; + size_t len; + z = 0; + buf = NULL; + So(nng_pipe_getopt(p, NNG_OPT_WS_RESPONSE_HEADERS, buf, &z) == + 0); + So(z > 0); + len = z; + So((buf = nni_alloc(len)) != NULL); + So(nng_pipe_getopt(p, NNG_OPT_WS_RESPONSE_HEADERS, buf, &z) == + 0); + So(strstr(buf, "Sec-WebSocket-Accept") != NULL); + So(z == len); + nni_free(buf, len); + }); + + return (0); +} + +static int +init_dialer_wss(trantest *tt, nng_dialer d) +{ + nng_tls_config *cfg; + int rv; + + if ((rv = nng_tls_config_init(&cfg, NNG_TLS_MODE_CLIENT)) != 0) { + return (rv); + } + if ((rv = nng_tls_config_ca_cert( + cfg, (void *) server_cert, sizeof(server_cert))) != 0) { + goto out; + } + if ((rv = nng_tls_config_server_name(cfg, "127.0.0.1")) != 0) { + goto out; + } + nng_tls_config_auth_mode(cfg, NNG_TLS_AUTH_MODE_NONE); + rv = nng_dialer_setopt_ptr(d, NNG_OPT_WSS_TLS_CONFIG, cfg); + +out: + nng_tls_config_fini(cfg); + return (rv); +} + +static int +init_listener_wss(trantest *tt, nng_listener l) +{ + nng_tls_config *cfg; + int rv; + + if ((rv = nng_tls_config_init(&cfg, NNG_TLS_MODE_SERVER)) != 0) { + return (rv); + } + if ((rv = nng_tls_config_cert( + cfg, (void *) server_cert, sizeof(server_cert))) != 0) { + nng_tls_config_fini(cfg); + return (rv); + } + if ((rv = nng_tls_config_key( + cfg, (void *) server_key, sizeof(server_key))) != 0) { + nng_tls_config_fini(cfg); + return (rv); + } + + if ((rv = nng_listener_setopt_ptr(l, NNG_OPT_WSS_TLS_CONFIG, cfg)) != + 0) { + // We can wind up with EBUSY from the server + // already running. + if (rv != NNG_EBUSY) { + nng_tls_config_fini(cfg); + return (rv); + } + } + nng_tls_config_fini(cfg); + return (0); +} + +TestMain("WebSocket Secure (TLS) Transport", { + static trantest tt; + + tt.dialer_init = init_dialer_wss; + tt.listener_init = init_listener_wss; + tt.tmpl = "wss://127.0.0.1:%u/test"; + tt.proptest = check_props_v4; + + trantest_test(&tt); + + nng_fini(); +}) @@ -39,7 +39,7 @@ mkdir(const char *path, int mode) #include <unistd.h> #endif // WIN32 -#ifndef NNG_HAVE_ZEROTIER +#ifndef NNG_TRANSPORT_ZEROTIER #define nng_zt_network_status_ok 0 #endif |
