aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/man/nngcat.adoc20
-rw-r--r--src/core/msgqueue.c4
-rw-r--r--src/supplemental/tls/CMakeLists.txt6
-rw-r--r--src/supplemental/tls/mbedtls/tls.c4
-rw-r--r--src/supplemental/tls/none/tls.c177
-rw-r--r--tools/nngcat/nngcat.c232
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: