diff options
| -rw-r--r-- | docs/man/nngcat.adoc | 20 | ||||
| -rw-r--r-- | src/core/msgqueue.c | 4 | ||||
| -rw-r--r-- | src/supplemental/tls/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/supplemental/tls/mbedtls/tls.c | 4 | ||||
| -rw-r--r-- | src/supplemental/tls/none/tls.c | 177 | ||||
| -rw-r--r-- | tools/nngcat/nngcat.c | 232 |
6 files changed, 376 insertions, 67 deletions
diff --git a/docs/man/nngcat.adoc b/docs/man/nngcat.adoc index 7c7acef6..49e4b05a 100644 --- a/docs/man/nngcat.adoc +++ b/docs/man/nngcat.adoc @@ -208,6 +208,26 @@ the data. *--send-timeout*=_SEC_:: Give up trying to send a message after _SEC_ seconds. +=== TLS Options + +These options are only present if TLS is configured; they are ignored +when using addresses that are not secured with TLS. + +*-k, --insecure*:: + Skip peer validation. + +*-E, --cert*=_FILE_:: + Load own certificate from _FILE_. + +*--key*=_FILE_:: + Load own key from _FILE_. Should be used in conjuction with *--cert*. If + not specified, and *--cert* is specified, then a single file containing both + the private key and the associated certificate is assumed. + +*--cacert*=_FILE_:: + Load CA certificates from _FILE_. These CAs ("Certificate Authorities") are + used as trust roots when validating certificates presented by peers. + == EXAMPLES .Echo service using request/reply. diff --git a/src/core/msgqueue.c b/src/core/msgqueue.c index de4708fe..8246279f 100644 --- a/src/core/msgqueue.c +++ b/src/core/msgqueue.c @@ -528,8 +528,6 @@ nni_msgq_resize(nni_msgq *mq, int cap) nni_msg * msg; nni_msg **newq, **oldq; int oldget; - int oldput; - int oldcap; int oldlen; int oldalloc; @@ -564,8 +562,6 @@ nni_msgq_resize(nni_msgq *mq, int cap) oldq = mq->mq_msgs; oldget = mq->mq_get; - oldput = mq->mq_put; - oldcap = mq->mq_cap; oldalloc = mq->mq_alloc; oldlen = mq->mq_len; diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt index 1d0dd08d..3f77732d 100644 --- a/src/supplemental/tls/CMakeLists.txt +++ b/src/supplemental/tls/CMakeLists.txt @@ -1,6 +1,6 @@ # -# Copyright 2017 Capitar IT Group BV <info@capitar.com> -# Copyright 2017 Staysail Systems, Inc. <info@staysail.tech> +# Copyright 2018 Capitar IT Group BV <info@capitar.com> +# Copyright 2018 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 @@ -34,6 +34,8 @@ if (NNG_SUPP_TLS_MBEDTLS) set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} PARENT_SCOPE) endif() set(TLS_SOURCES ${TLS_SOURCES} supplemental/tls/mbedtls/tls.c) +else() + set(TLS_SOURCES ${TLS_SOURCES} supplemental/tls/none/tls.c) endif() set(NNG_DEFINES ${NNG_DEFINES} ${TLS_DEFINES} PARENT_SCOPE) diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c index 8d934d61..1e008668 100644 --- a/src/supplemental/tls/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/tls.c @@ -497,7 +497,7 @@ nni_tls_recv_cb(void *ctx) // The chunk size we accept is 64k at a time, which prevents // ridiculous over queueing. This is always called with the pipe // lock held, and never blocks. -int +static int nni_tls_net_send(void *ctx, const unsigned char *buf, size_t len) { nni_tls *tp = ctx; @@ -605,7 +605,7 @@ nni_tls_sockname(nni_tls *tp, nni_sockaddr *sa) return (nni_plat_tcp_pipe_sockname(tp->tcp, sa)); } -void +static void nni_tls_do_handshake(nni_tls *tp) { int rv; diff --git a/src/supplemental/tls/none/tls.c b/src/supplemental/tls/none/tls.c new file mode 100644 index 00000000..beaf322c --- /dev/null +++ b/src/supplemental/tls/none/tls.c @@ -0,0 +1,177 @@ +// +// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// This file is only used when TLS support is not build into the library. +// We provide stub functions only to satisfy linkage. + +#include "core/nng_impl.h" +#include "supplemental/tls/tls.h" + +void +nni_tls_config_fini(nng_tls_config *cfg) +{ + NNI_ARG_UNUSED(cfg); +} + +int +nni_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode) +{ + NNI_ARG_UNUSED(cpp); + NNI_ARG_UNUSED(mode); + return (NNG_ENOTSUP); +} + +void +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_plat_tcp_pipe *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); +} + +int +nni_tls_peername(nni_tls *tp, nni_sockaddr *sa) +{ + NNI_ARG_UNUSED(tp); + NNI_ARG_UNUSED(sa); + return (NNG_ENOTSUP); +} + +int +nni_tls_sockname(nni_tls *tp, nni_sockaddr *sa) +{ + NNI_ARG_UNUSED(tp); + NNI_ARG_UNUSED(sa); + return (NNG_ENOTSUP); +} + +void +nni_tls_close(nni_tls *tp) +{ + NNI_ARG_UNUSED(tp); +} + +const char * +nni_tls_ciphersuite_name(nni_tls *tp) +{ + NNI_ARG_UNUSED(tp); + return (NULL); +} + +bool +nni_tls_verified(nni_tls *tp) +{ + NNI_ARG_UNUSED(tp); + return (false); +} + +int +nng_tls_config_server_name(nng_tls_config *cfg, const char *name) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(name); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(mode); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_ca_chain( + nng_tls_config *cfg, const char *certs, const char *crl) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(certs); + NNI_ARG_UNUSED(crl); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_own_cert( + nng_tls_config *cfg, const char *cert, const char *key, const char *pass) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(key); + NNI_ARG_UNUSED(pass); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_ca_file(nng_tls_config *cfg, const char *path) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(path); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_cert_key_file( + nng_tls_config *cfg, const char *path, const char *pass) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(path); + NNI_ARG_UNUSED(pass); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_alloc(nng_tls_config **cfgp, nng_tls_mode mode) +{ + + NNI_ARG_UNUSED(cfgp); + NNI_ARG_UNUSED(mode); + return (NNG_ENOTSUP); +} + +void +nng_tls_config_free(nng_tls_config *cfg) +{ + NNI_ARG_UNUSED(cfg); +}
\ No newline at end of file diff --git a/tools/nngcat/nngcat.c b/tools/nngcat/nngcat.c index 728f0c3f..04afe919 100644 --- a/tools/nngcat/nngcat.c +++ b/tools/nngcat/nngcat.c @@ -41,9 +41,15 @@ nng_duration sendtimeo = NNG_DURATION_INFINITE; nng_duration recvtimeo = NNG_DURATION_INFINITE; void * data = NULL; size_t datalen = 0; -size_t datacap = 0; int compat = 0; int async = 0; +int insecure = 0; +void * cacert = NULL; +size_t cacertlen = 0; +void * keyfile = NULL; +size_t keylen = 0; +void * certfile = NULL; +size_t certlen = 0; // Options, must start at 1 because zero is sentinel. enum options { @@ -85,6 +91,10 @@ enum options { OPT_DIAL_IPC, OPT_LISTEN_LOCAL, OPT_DIAL_LOCAL, + OPT_INSECURE, + OPT_CACERT, + OPT_KEYFILE, + OPT_CERTFILE, }; static nng_optspec opts[] = { @@ -159,6 +169,15 @@ static nng_optspec opts[] = { .o_val = OPT_DIAL_LOCAL, .o_arg = true, }, + { .o_name = "insecure", .o_short = 'k', .o_val = OPT_INSECURE }, + { .o_name = "cacert", .o_val = OPT_CACERT, .o_arg = true }, + { .o_name = "key", .o_val = OPT_KEYFILE, .o_arg = true }, + { + .o_name = "cert", + .o_short = 'E', + .o_val = OPT_CERTFILE, + .o_arg = true, + }, // Sentinel. { .o_name = NULL, .o_val = 0 }, @@ -243,6 +262,70 @@ intarg(const char *val, int maxv) return (v); } +// This reads a file into memory. Care is taken to ensure that +// the buffer is one byte larger and contains a terminating +// NUL. (Useful for key files and such.) +static void +loadfile(const char *path, void **datap, size_t *lenp) +{ + FILE * f; + size_t cap; + char * data; + size_t len; + if ((f = fopen(path, "r")) == NULL) { + fatal("Cannot open file %s: %s", path, strerror(errno)); + } + cap = 4096; + len = 0; + if ((data = malloc(cap)) == NULL) { + fatal("Out of memory."); + } + data[0] = '\0'; + for (;;) { + size_t n; + // Read until end of file, reallocating as needed. + if (len == (cap - 1)) { + void *old = data; + cap *= 2; + if ((data = realloc(old, cap)) == NULL) { + fatal("Out of memory"); + } + } + n = fread(data + len, 1, cap - len, f); + if (n == 0) { + if (ferror(f)) { + fatal("Read file %s failed: %s", path, + strerror(errno)); + } + break; + } + len += n; + data[len] = '\0'; + } + fclose(f); + *datap = data; + *lenp = len; +} + +static void +configtls(nng_tls_config *tls) +{ + int rv = 0; + if (insecure) { + rv = nng_tls_config_auth_mode(tls, NNG_TLS_AUTH_MODE_NONE); + } + if ((rv == 0) && (certfile != NULL)) { + keyfile = keyfile ? keyfile : certfile; + rv = nng_tls_config_own_cert(tls, certfile, keyfile, NULL); + } + if ((rv == 0) && (cacert != NULL)) { + rv = nng_tls_config_ca_chain(tls, cacert, NULL); + } + if (rv != 0) { + fatal("Unable to configure TLS: %s", nng_strerror(rv)); + } +} + struct addr { struct addr *next; int mode; @@ -335,7 +418,8 @@ printmsg(char *buf, size_t len) putchar('"'); putchar('\n'); break; - case OPT_MSGPACK: // MsgPack, we just encode prefix + len, then raw. + case OPT_MSGPACK: // MsgPack, we just encode prefix + len, then + // raw. if (len < 256) { putchar('\xc4'); putchar(len & 0xff); @@ -352,7 +436,8 @@ printmsg(char *buf, size_t len) } fwrite(buf, 1, len, stdout); break; - case OPT_HEX: // Hex, quoted C string encoded with hex literals. + case OPT_HEX: // Hex, quoted C string encoded with hex + // literals. putchar('"'); for (size_t i = 0; i < len; i++) { printf("\\x%02x", (uint8_t) buf[i]); @@ -375,8 +460,8 @@ recvloop(nng_socket sock) switch (rv) { case NNG_ETIMEDOUT: case NNG_ESTATE: - // Either a regular timeout, or we reached the end - // of an event like a survey completing. + // Either a regular timeout, or we reached the + // end of an event like a survey completing. return; case 0: printmsg(nng_msg_body(msg), nng_msg_len(msg)); @@ -443,9 +528,9 @@ sendloop(nng_socket sock) break; } // We sleep, but we account for time spent, so that our - // interval appears more or less constant. Of course if we - // took more than the interval here, then we skip the sleep - // altogether. + // interval appears more or less constant. Of course + // if we took more than the interval here, then we skip + // the sleep altogether. if ((delta >= 0) && (delta < interval)) { nng_msleep(interval - delta); } @@ -490,8 +575,9 @@ sendrecv(nng_socket sock) break; } - // We would like to use recvloop, but we need to reset our - // timeout each time, as the timer counts down towards zero. + // We would like to use recvloop, but we need to reset + // our timeout each time, as the timer counts down + // towards zero. for (;;) { delta = (nng_duration)(nng_clock() - start); @@ -529,9 +615,9 @@ sendrecv(nng_socket sock) delta = (nng_duration)(end - start); // We sleep, but we account for time spent, so that our - // interval appears more or less constant. Of course if we - // took more than the interval here, then we skip the sleep - // altogether. + // interval appears more or less constant. Of course + // if we took more than the interval here, then we skip + // the sleep altogether. if ((delta >= 0) && (delta < interval)) { nng_msleep(interval - delta); } @@ -552,7 +638,6 @@ main(int ac, const char **av) struct topic **topicend; nng_socket sock; int port; - FILE * f; idx = 1; addrend = &addrs; @@ -655,56 +740,49 @@ main(int ac, const char **av) break; case OPT_FILE: if (data != NULL) { - fatal("Data (--file, --data) may be specified " + fatal("Data (--file, --data) may be " + "specified " "only once."); } - if ((f = fopen(arg, "r")) == NULL) { - fatal("Cannot open file %s: %s", arg, - strerror(errno)); - } - for (;;) { - size_t n; - // Read until end of file, reallocating as - // needed. - if (datacap == 0) { - data = malloc(4096); - datacap = 4096; - } else if (datacap == datalen) { - void *odata = data; - datacap *= 2; - data = realloc(odata, datacap); - if (data == NULL) { - free(odata); - } - } - if (data == NULL) { - fatal("Out of memory."); - } - n = fread((char *) data + datalen, 1, - datacap - datalen, f); - if (n == 0) { - if (ferror(f)) { - fatal( - "Read file %s failed: %s", - arg, strerror(errno)); - } - break; - } - } - fclose(f); + loadfile(arg, &data, &datalen); break; case OPT_DATA: if (data != NULL) { - fatal("Data (--file, --data) may be specified " + fatal("Data (--file, --data) may be " + "specified " "only once."); } if ((data = malloc(strlen(arg) + 1)) == NULL) { fatal("Out of memory."); } memcpy(data, arg, strlen(arg) + 1); - datacap = strlen(arg) + 1; datalen = strlen(arg); break; + case OPT_CACERT: + if (cacert != NULL) { + fatal("CA Certificate (--cacert) may be " + "specified only once."); + } + loadfile(arg, &cacert, &cacertlen); + break; + case OPT_KEYFILE: + if (keyfile != NULL) { + fatal( + "Key (--key) may be specified only once."); + } + loadfile(arg, &keyfile, &keylen); + break; + case OPT_CERTFILE: + if (certfile != NULL) { + fatal("Cert (--cert) may be specified " + "only " + "once."); + } + loadfile(arg, &certfile, &certlen); + break; + case OPT_INSECURE: + insecure = 1; + break; } } switch (rv) { @@ -725,7 +803,8 @@ main(int ac, const char **av) if (compat) { if (async != 0) { - fatal("Option --async and --compat are incompatible."); + fatal("Option --async and --compat are " + "incompatible."); } if (proto == OPT_PAIR) { proto = OPT_PAIR0; @@ -761,8 +840,17 @@ main(int ac, const char **av) } break; case OPT_PUSH0: - case OPT_SURVEY0: case OPT_PUB0: + if (format != 0) { + fatal("Protocol does not support --format " + "options."); + } + if (data == NULL) { + fatal("Protocol requires either --file or " + "--data."); + } + break; + case OPT_SURVEY0: case OPT_REQ0: if (data == NULL) { fatal("Protocol requires either --file or " @@ -890,22 +978,48 @@ main(int ac, const char **av) fatal("Unable to set send timeout: %s", nng_strerror(rv)); } - // XXX: TBD: This is where we should add other socket options, - // like TLS configuration, timeouts, etc. - for (struct addr *a = addrs; a != NULL; a = a->next) { - char *act; + char * act; + nng_listener l; + nng_dialer d; + nng_tls_config *tls; switch (a->mode) { case OPT_DIAL: case OPT_DIAL_IPC: case OPT_DIAL_LOCAL: - rv = nng_dial(sock, a->val, NULL, async); + rv = nng_dialer_create(&d, sock, a->val); + if (rv != 0) { + fatal("Unable to create dialer for %s: %s", + a->val, nng_strerror(rv)); + } + rv = nng_dialer_getopt_ptr( + d, NNG_OPT_TLS_CONFIG, (void **) &tls); + if (rv == 0) { + configtls(tls); + } else if (rv != NNG_ENOTSUP) { + fatal("Unable to get TLS config: %s", + nng_strerror(rv)); + } + rv = nng_dialer_start(d, async); act = "dial"; break; case OPT_LISTEN: case OPT_LISTEN_IPC: case OPT_LISTEN_LOCAL: - rv = nng_listen(sock, a->val, NULL, async); + rv = nng_listener_create(&l, sock, a->val); + if (rv != 0) { + fatal("Unable to create listener for %s: %s", + a->val, nng_strerror(rv)); + } + rv = nng_listener_getopt_ptr( + l, NNG_OPT_TLS_CONFIG, (void **) &tls); + if (rv == 0) { + configtls(tls); + } else if (rv != NNG_ENOTSUP) { + fatal("Unable to get TLS config: %s", + nng_strerror(rv)); + } + rv = nng_listener_start(l, async); act = "listen"; break; default: |
