aboutsummaryrefslogtreecommitdiff
path: root/src/compat
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2018-02-23 14:48:46 -0800
committerGarrett D'Amore <garrett@damore.org>2018-02-23 14:48:46 -0800
commitc457bddfae64521ea9861f2211e6cb25858559b3 (patch)
tree4c0ef1c7450198ce14ef4fbcdddb5d47e911fb4f /src/compat
parentc31c9147320741904b604e2172f99d5ca08eb417 (diff)
downloadnng-c457bddfae64521ea9861f2211e6cb25858559b3.tar.gz
nng-c457bddfae64521ea9861f2211e6cb25858559b3.tar.bz2
nng-c457bddfae64521ea9861f2211e6cb25858559b3.zip
Move compatibility header so that <nanomsg/nn.h> works.
Basically, we have moved the compat stuff into a separate directory. Compatibility layer users will have to update their compile flags, but should be able to avoid changing any *source* files with this change.
Diffstat (limited to 'src/compat')
-rw-r--r--src/compat/nanomsg/CMakeLists.txt15
-rw-r--r--src/compat/nanomsg/nn.c728
-rw-r--r--src/compat/nanomsg/nn.h315
3 files changed, 1058 insertions, 0 deletions
diff --git a/src/compat/nanomsg/CMakeLists.txt b/src/compat/nanomsg/CMakeLists.txt
new file mode 100644
index 00000000..d1a692b7
--- /dev/null
+++ b/src/compat/nanomsg/CMakeLists.txt
@@ -0,0 +1,15 @@
+#
+# 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
+# file was obtained (LICENSE.txt). A copy of the license may also be
+# found online at https://opensource.org/licenses/MIT.
+#
+
+set(COMPAT_SOURCES compat/nanomsg/nn.c)
+set(COMPAT_HEADERS compat/nanomsg/nn.h)
+
+set(NNG_SOURCES ${NNG_SOURCES} ${COMPAT_SOURCES} PARENT_SCOPE)
+set(NNG_HEADERS ${NNG_HEADERS} ${COMPAT_HEADERS} PARENT_SCOPE)
diff --git a/src/compat/nanomsg/nn.c b/src/compat/nanomsg/nn.c
new file mode 100644
index 00000000..1d5bcfbb
--- /dev/null
+++ b/src/compat/nanomsg/nn.c
@@ -0,0 +1,728 @@
+//
+// Copyright 2018 Garrett D'Amore <garrett@damore.org>
+// 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 "nn.h"
+#include "nng.h"
+#include "protocol/bus0/bus.h"
+#include "protocol/pair0/pair.h"
+#include "protocol/pipeline0/pull.h"
+#include "protocol/pipeline0/push.h"
+#include "protocol/pubsub0/pub.h"
+#include "protocol/pubsub0/sub.h"
+#include "protocol/reqrep0/rep.h"
+#include "protocol/reqrep0/req.h"
+#include "protocol/survey0/respond.h"
+#include "protocol/survey0/survey.h"
+
+#include <stdio.h>
+#include <string.h>
+
+// This file supplies the legacy compatibility API. Applications should
+// avoid using these if at all possible, and instead use the new style APIs.
+
+static const struct {
+ int nerr;
+ int perr;
+} nn_errnos[] = {
+ // clang-format off
+ { NNG_EINTR, EINTR },
+ { NNG_ENOMEM, ENOMEM },
+ { NNG_EINVAL, EINVAL },
+ { NNG_EBUSY, EBUSY },
+ { NNG_ETIMEDOUT, ETIMEDOUT },
+ { NNG_ECONNREFUSED, ECONNREFUSED },
+ { NNG_ECLOSED, EBADF },
+ { NNG_EAGAIN, EAGAIN },
+ { NNG_ENOTSUP, ENOTSUP },
+ { NNG_EADDRINUSE, EADDRINUSE },
+ { NNG_ESTATE, EFSM },
+ { NNG_ENOENT, ENOENT },
+ { NNG_EPROTO, EPROTO },
+ { NNG_EUNREACHABLE, EHOSTUNREACH },
+ { NNG_EADDRINVAL, EADDRNOTAVAIL },
+ { NNG_EPERM, EACCES },
+ { NNG_EMSGSIZE, EMSGSIZE },
+ { NNG_ECONNABORTED, ECONNABORTED },
+ { NNG_ECONNRESET, ECONNRESET },
+ { NNG_ECANCELED, EBADF },
+ { 0, 0 },
+ // clang-format on
+};
+
+const char *
+nn_strerror(int err)
+{
+ int i;
+ static char msgbuf[32];
+
+ for (i = 0; nn_errnos[i].perr != 0; i++) {
+ if (nn_errnos[i].perr == err) {
+ return (nng_strerror(nn_errnos[i].nerr));
+ }
+ }
+ if (err == EIO) {
+ return ("Unknown I/O error");
+ }
+
+ // Arguably we could use strerror() here, but we should only
+ // be getting errnos we understand at this point.
+ (void) snprintf(msgbuf, sizeof(msgbuf), "Unknown error %d", err);
+ return (msgbuf);
+}
+
+static void
+nn_seterror(int err)
+{
+ int i;
+
+ for (i = 0; nn_errnos[i].nerr != 0; i++) {
+ if (nn_errnos[i].nerr == err) {
+ errno = nn_errnos[i].perr;
+ return;
+ }
+ }
+ // No idea...
+ errno = EIO;
+}
+
+int
+nn_errno(void)
+{
+ return (errno);
+}
+
+static const struct {
+ uint16_t p_id;
+ int (*p_open)(nng_socket *);
+} nn_protocols[] = {
+// clang-format off
+#ifdef NNG_HAVE_BUS0
+ { NN_BUS, nng_bus0_open },
+#endif
+#ifdef NNG_HAVE_PAIR0
+ { NN_PAIR, nng_pair0_open },
+#endif
+#ifdef NNG_HAVE_PUSH0
+ { NN_PUSH, nng_push0_open },
+#endif
+#ifdef NNG_HAVE_PULL0
+ { NN_PULL, nng_pull0_open },
+#endif
+#ifdef NNG_HAVE_PUB0
+ { NN_PUB, nng_pub0_open },
+#endif
+#ifdef NNG_HAVE_SUB0
+ { NN_SUB, nng_sub0_open },
+#endif
+#ifdef NNG_HAVE_REQ0
+ { NN_REQ, nng_req0_open },
+#endif
+#ifdef NNG_HAVE_REP0
+ { NN_REP, nng_rep0_open },
+#endif
+#ifdef NNG_HAVE_SURVEYOR0
+ { NN_SURVEYOR, nng_surveyor0_open },
+#endif
+#ifdef NNG_HAVE_RESPONDENT0
+ { NN_RESPONDENT, nng_respondent0_open },
+#endif
+ { 0, NULL },
+ // clang-format on
+};
+
+int
+nn_socket(int domain, int protocol)
+{
+ nng_socket sock;
+ int rv;
+ int i;
+
+ if ((domain != AF_SP) && (domain != AF_SP_RAW)) {
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+
+ for (i = 0; nn_protocols[i].p_id != 0; i++) {
+ if (nn_protocols[i].p_id == protocol) {
+ break;
+ }
+ }
+ if (nn_protocols[i].p_open == NULL) {
+ errno = ENOTSUP;
+ return (-1);
+ }
+
+ if ((rv = nn_protocols[i].p_open(&sock)) != 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ if (domain == AF_SP_RAW) {
+ if ((rv = nng_setopt_int(sock, NNG_OPT_RAW, 1)) != 0) {
+ nn_seterror(rv);
+ nng_close(sock);
+ return (-1);
+ }
+ }
+ return ((int) sock);
+}
+
+int
+nn_close(int s)
+{
+ int rv;
+
+ if ((rv = nng_close((nng_socket) s)) != 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ return (0);
+}
+
+int
+nn_bind(int s, const char *addr)
+{
+ int rv;
+ nng_listener l;
+
+ if ((rv = nng_listen((nng_socket) s, addr, &l, 0)) != 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ return ((int) l);
+}
+
+int
+nn_connect(int s, const char *addr)
+{
+ int rv;
+ nng_dialer d;
+
+ if ((rv = nng_dial((nng_socket) s, addr, &d, NNG_FLAG_NONBLOCK)) !=
+ 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ return ((int) d);
+}
+
+int
+nn_shutdown(int s, int ep)
+{
+ int rv;
+ (void) s; // Unused
+
+ // Socket is wired into the endpoint... so passing a bad endpoint
+ // ID can result in affecting the wrong socket. But this requires
+ // a buggy application, and because we don't recycle endpoints
+ // until wrap, its unlikely to actually come up in practice.
+ // Note that listeners and dialers share the same namespace
+ // in the core, so we can close either one this way.
+
+ if (((rv = nng_dialer_close((nng_dialer) ep)) != 0) &&
+ ((rv = nng_listener_close((nng_listener) ep)) != 0)) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ return (0);
+}
+
+void *
+nn_allocmsg(size_t size, int type)
+{
+ nng_msg *msg;
+ int rv;
+
+ // Validate type and non-zero size. This also checks for overflow.
+ if ((type != 0) || (size < 1) || ((size + sizeof(msg) < size))) {
+ nn_seterror(NNG_EINVAL);
+ return (NULL);
+ }
+
+ // So our "messages" from nn are really going to be nng messages
+ // but to make this work, we use a bit of headroom in the message
+ // to stash the message header.
+ if ((rv = nng_msg_alloc(&msg, size + (sizeof(msg)))) != 0) {
+ nn_seterror(rv);
+ return (NULL);
+ }
+
+ // This counts on message bodies being aligned sensibly.
+ *(nng_msg **) (nng_msg_body(msg)) = msg;
+
+ // We are counting on the implementation of nn_msg_trim to not
+ // reallocate the message but just to leave the prefix inplace.
+ (void) nng_msg_trim(msg, sizeof(msg));
+
+ return (nng_msg_body(msg));
+}
+
+int
+nn_freemsg(void *ptr)
+{
+ nng_msg *msg;
+
+ msg = *(nng_msg **) (((char *) ptr) - sizeof(msg));
+ nng_msg_free(msg);
+ return (0);
+}
+
+void *
+nn_reallocmsg(void *ptr, size_t len)
+{
+ nng_msg *msg;
+ int rv;
+
+ if ((len + sizeof(msg)) < len) {
+ // overflowed!
+ nn_seterror(NNG_EINVAL);
+ return (NULL);
+ }
+
+ // This counts on message bodies being aligned sensibly.
+ msg = *(nng_msg **) (((char *) ptr) - sizeof(msg));
+
+ // We need to realloc the requested len, plus size for our header.
+ if ((rv = nng_msg_realloc(msg, len + sizeof(msg))) != 0) {
+ // We don't free the old message. Code is free to cope
+ // as it sees fit.
+ nn_seterror(rv);
+ return (NULL);
+ }
+ // Stash the msg header pointer
+ *(nng_msg **) (nng_msg_body(msg)) = msg;
+ nng_msg_trim(msg, sizeof(msg));
+ return (nng_msg_body(msg));
+}
+
+static int
+nn_flags(int flags)
+{
+ switch (flags) {
+ case 0:
+ return (0);
+
+ case NN_DONTWAIT:
+ return (NNG_FLAG_NONBLOCK);
+
+ default:
+ nn_seterror(NNG_EINVAL);
+ return (-1);
+ }
+}
+
+int
+nn_send(int s, const void *buf, size_t len, int flags)
+{
+ struct nn_iovec iov;
+ struct nn_msghdr hdr;
+
+ iov.iov_base = (void *) buf;
+ iov.iov_len = len;
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ hdr.msg_control = NULL;
+ hdr.msg_controllen = 0;
+
+ return (nn_sendmsg(s, &hdr, flags));
+}
+
+int
+nn_recv(int s, void *buf, size_t len, int flags)
+{
+ struct nn_iovec iov;
+ struct nn_msghdr hdr;
+
+ iov.iov_base = buf;
+ iov.iov_len = len;
+
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ hdr.msg_control = NULL;
+ hdr.msg_controllen = 0;
+
+ return (nn_recvmsg(s, &hdr, flags));
+}
+
+int
+nn_recvmsg(int s, struct nn_msghdr *mh, int flags)
+{
+ int rv;
+ nng_msg *msg;
+ size_t len;
+ int keep = 0;
+
+ if ((flags = nn_flags(flags)) == -1) {
+ return (-1);
+ }
+ if (mh == NULL) {
+ nn_seterror(NNG_EINVAL);
+ return (-1);
+ }
+ if (mh->msg_iovlen < 0) {
+ nn_seterror(NNG_EMSGSIZE);
+ return (-1);
+ }
+
+ if ((rv = nng_recvmsg((nng_socket) s, &msg, flags)) != 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ if ((mh->msg_iovlen == 1) && (mh->msg_iov[0].iov_len == NN_MSG)) {
+ // Receiver wants to have a dynamically allocated message.
+ // There can only be one of these.
+ if ((rv = nng_msg_insert(msg, &msg, sizeof(msg))) != 0) {
+ nng_msg_free(msg);
+ nn_seterror(rv);
+ return (-1);
+ }
+ nng_msg_trim(msg, sizeof(msg));
+ *(void **) (mh->msg_iov[0].iov_base) = nng_msg_body(msg);
+ len = nng_msg_len(msg);
+ keep = 1; // Do not discard message!
+ } else {
+ // copyout to multiple iovecs.
+ char * ptr = nng_msg_body(msg);
+ int i;
+ size_t n;
+ len = nng_msg_len(msg);
+
+ for (i = 0; i < mh->msg_iovlen; i++) {
+ if ((n = mh->msg_iov[i].iov_len) == NN_MSG) {
+ // This is forbidden!
+ nn_seterror(NNG_EINVAL);
+ nng_msg_free(msg);
+ return (-1);
+ }
+ if (n > len) {
+ n = len;
+ }
+ memcpy(mh->msg_iov[i].iov_base, ptr, n);
+ len -= n;
+ ptr += n;
+ }
+
+ // If we copied everything, len will be zero, otherwise,
+ // it represents the amount of data that we were unable to
+ // copyout. The caller is responsible for noticing this,
+ // as there is no API to pass this information out.
+ len = nng_msg_len(msg);
+ }
+
+ // If the caller has requested control information (header details),
+ // we grab it.
+ if (mh->msg_control != NULL) {
+ char * cdata;
+ size_t clen;
+ size_t tlen;
+ size_t spsz;
+ struct nn_cmsghdr *hdr;
+ unsigned char * ptr;
+
+ spsz = nng_msg_header_len(msg);
+ clen = NN_CMSG_SPACE(sizeof(spsz) + spsz);
+
+ if ((tlen = mh->msg_controllen) == NN_MSG) {
+ // Ideally we'd use the same msg, but we would need
+ // to set up reference counts on the message, so
+ // instead we just make a new message.
+ nng_msg *nmsg;
+
+ rv = nng_msg_alloc(&nmsg, clen + sizeof(nmsg));
+ if (rv != 0) {
+ nng_msg_free(msg);
+ nn_seterror(rv);
+ return (-1);
+ }
+ memcpy(nng_msg_body(nmsg), &nmsg, sizeof(nmsg));
+ nng_msg_trim(nmsg, sizeof(nmsg));
+ cdata = nng_msg_body(nmsg);
+ *(void **) mh->msg_control = cdata;
+ tlen = clen;
+ } else {
+ cdata = mh->msg_control;
+ memset(cdata, 0,
+ tlen > sizeof(*hdr) ? sizeof(*hdr) : tlen);
+ }
+
+ if (clen <= tlen) {
+ ptr = NN_CMSG_DATA(cdata);
+ hdr = (void *) cdata;
+ hdr->cmsg_len = clen;
+ hdr->cmsg_level = PROTO_SP;
+ hdr->cmsg_type = SP_HDR;
+
+ memcpy(ptr, &spsz, sizeof(spsz));
+ ptr += sizeof(spsz);
+ memcpy(ptr, nng_msg_header(msg), spsz);
+ }
+ }
+
+ if (!keep) {
+ nng_msg_free(msg);
+ }
+ return ((int) len);
+}
+
+int
+nn_sendmsg(int s, const struct nn_msghdr *mh, int flags)
+{
+ nng_msg *msg = NULL;
+ nng_msg *cmsg = NULL;
+ char * cdata;
+ int keep = 0;
+ size_t sz;
+ int rv;
+
+ if ((flags = nn_flags(flags)) == -1) {
+ return (-1);
+ }
+
+ if (mh == NULL) {
+ nn_seterror(NNG_EINVAL);
+ return (-1);
+ }
+
+ if (mh->msg_iovlen < 0) {
+ nn_seterror(NNG_EMSGSIZE);
+ return (-1);
+ }
+
+ if ((mh->msg_iovlen == 1) && (mh->msg_iov[0].iov_len == NN_MSG)) {
+ char *bufp = *(char **) (mh->msg_iov[0].iov_base);
+
+ msg = *(nng_msg **) (bufp - sizeof(msg));
+ keep = 1; // keep the message on error
+ } else {
+ char *ptr;
+ int i;
+
+ sz = 0;
+ // Get the total message size.
+ for (i = 0; i < mh->msg_iovlen; i++) {
+ sz += mh->msg_iov[i].iov_len;
+ }
+ if ((rv = nng_msg_alloc(&msg, sz)) != 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ // Now copy it out.
+ ptr = nng_msg_body(msg);
+ for (i = 0; i < mh->msg_iovlen; i++) {
+ memcpy(ptr, mh->msg_iov[i].iov_base,
+ mh->msg_iov[i].iov_len);
+ ptr += mh->msg_iov[i].iov_len;
+ }
+ }
+
+ // Now suck up the control data...
+ // This POSIX-inspired API is one of the most painful for
+ // usability we've ever seen.
+ cmsg = NULL;
+ if ((cdata = mh->msg_control) != NULL) {
+ size_t clen;
+ size_t offs;
+ size_t spsz;
+ struct nn_cmsghdr *chdr;
+ unsigned char * data;
+
+ if ((clen = mh->msg_controllen) == NN_MSG) {
+ // Underlying data is a message. This is awkward,
+ // because we have to copy the data, but we should
+ // only free this message on success. So we save the
+ // message now.
+ cdata = *(void **) cdata;
+ cmsg = *(nng_msg **) (cdata - sizeof(cmsg));
+ clen = nng_msg_len(cmsg);
+ } else {
+ clen = mh->msg_controllen;
+ }
+
+ offs = 0;
+ while ((offs + sizeof(NN_CMSG_LEN(0))) < clen) {
+ chdr = (void *) (cdata + offs);
+ if ((chdr->cmsg_level != PROTO_SP) ||
+ (chdr->cmsg_type != SP_HDR)) {
+ offs += chdr->cmsg_len;
+ }
+
+ // SP header in theory. Starts with size, then
+ // any backtrace details.
+ if (chdr->cmsg_len < sizeof(size_t)) {
+ offs += chdr->cmsg_len;
+ continue;
+ }
+ data = NN_CMSG_DATA(chdr);
+ memcpy(&spsz, data, sizeof(spsz));
+ if ((spsz + sizeof(spsz)) > chdr->cmsg_len) {
+ // Truncated header? Ignore it.
+ offs += chdr->cmsg_len;
+ continue;
+ }
+ data += sizeof(spsz);
+ rv = nng_msg_header_append(msg, data, spsz);
+ if (rv != 0) {
+ if (!keep) {
+ nng_msg_free(msg);
+ }
+ nn_seterror(rv);
+ return (-1);
+ }
+
+ break;
+ }
+ }
+
+ sz = nng_msg_len(msg);
+ if ((rv = nng_sendmsg((nng_socket) s, msg, flags)) != 0) {
+ if (!keep) {
+ nng_msg_free(msg);
+ }
+ nn_seterror(rv);
+ return (-1);
+ }
+
+ if (cmsg != NULL) {
+ // We sent successfully, so free up the control message.
+ nng_msg_free(cmsg);
+ }
+ return ((int) sz);
+}
+
+// options which we convert -- most of the array is initialized at run time.
+static const struct {
+ int nnlevel;
+ int nnopt;
+ const char *opt;
+} options[] = {
+ { NN_SOL_SOCKET, NN_LINGER, NNG_OPT_LINGER }, // review
+ { NN_SOL_SOCKET, NN_SNDBUF, NNG_OPT_SENDBUF },
+ { NN_SOL_SOCKET, NN_RCVBUF, NNG_OPT_RECVBUF },
+ { NN_SOL_SOCKET, NN_RECONNECT_IVL, NNG_OPT_RECONNMINT },
+ { NN_SOL_SOCKET, NN_RECONNECT_IVL_MAX, NNG_OPT_RECONNMAXT },
+ { NN_SOL_SOCKET, NN_SNDFD, NNG_OPT_SENDFD },
+ { NN_SOL_SOCKET, NN_RCVFD, NNG_OPT_RECVFD },
+ { NN_SOL_SOCKET, NN_RCVMAXSIZE, NNG_OPT_RECVMAXSZ },
+ { NN_SOL_SOCKET, NN_MAXTTL, NNG_OPT_MAXTTL },
+ { NN_SOL_SOCKET, NN_RCVTIMEO, NNG_OPT_RECVTIMEO },
+ { NN_SOL_SOCKET, NN_SNDTIMEO, NNG_OPT_SENDTIMEO },
+ { NN_SOL_SOCKET, NN_DOMAIN, NNG_OPT_DOMAIN },
+ { NN_SOL_SOCKET, NN_SOCKET_NAME, NNG_OPT_SOCKNAME },
+ { NN_REQ, NN_REQ_RESEND_IVL, NNG_OPT_REQ_RESENDTIME },
+ { NN_SUB, NN_SUB_SUBSCRIBE, NNG_OPT_SUB_SUBSCRIBE },
+ { NN_SUB, NN_SUB_UNSUBSCRIBE, NNG_OPT_SUB_UNSUBSCRIBE },
+ { NN_SURVEYOR, NN_SURVEYOR_DEADLINE, NNG_OPT_SURVEYOR_SURVEYTIME },
+ // XXX: IPV4ONLY, SNDPRIO, RCVPRIO
+};
+
+int
+nn_getsockopt(int s, int nnlevel, int nnopt, void *valp, size_t *szp)
+{
+ const char *name = NULL;
+ int rv;
+
+ for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
+ if ((options[i].nnlevel == nnlevel) &&
+ (options[i].nnopt == nnopt)) {
+ name = options[i].opt;
+ break;
+ }
+ }
+
+ if (name == NULL) {
+ errno = ENOPROTOOPT;
+ return (-1);
+ }
+
+ if ((rv = nng_getopt((nng_socket) s, name, valp, szp)) != 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+nn_setsockopt(int s, int nnlevel, int nnopt, const void *valp, size_t sz)
+{
+ const char *name = NULL;
+ int rv;
+
+ for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
+ if ((options[i].nnlevel == nnlevel) &&
+ (options[i].nnopt == nnopt)) {
+ name = options[i].opt;
+ break;
+ }
+ }
+ if (name == NULL) {
+ return (ENOPROTOOPT);
+ }
+
+ if ((rv = nng_setopt((nng_socket) s, name, valp, sz)) != 0) {
+ nn_seterror(rv);
+ return (-1);
+ }
+ return (0);
+}
+
+struct nn_cmsghdr *
+nn_cmsg_next(struct nn_msghdr *mh, struct nn_cmsghdr *first)
+{
+ size_t clen;
+ char * data;
+
+ // We only support SP headers, so there can be at most one header.
+ if (first != NULL) {
+ return (NULL);
+ }
+ if ((clen = mh->msg_controllen) == NN_MSG) {
+ nng_msg *msg;
+ data = *((void **) (mh->msg_control));
+ msg = *(nng_msg **) (data - sizeof(msg));
+ clen = nng_msg_len(msg);
+ } else {
+ data = mh->msg_control;
+ }
+
+ if (first == NULL) {
+ first = (void *) data;
+ } else {
+ first = first + first->cmsg_len;
+ }
+
+ if (((char *) first + sizeof(*first)) > (data + clen)) {
+ return (NULL);
+ }
+ return (first);
+}
+
+int
+nn_device(int s1, int s2)
+{
+ int rv;
+
+ rv = nng_device((nng_socket) s1, (nng_socket) s2);
+ // rv must always be nonzero
+ nn_seterror(rv);
+ return (-1);
+}
+
+// nn_term is suitable only for shutting down the entire library,
+// and is not thread-safe with other functions.
+void
+nn_term(void)
+{
+ // This function is relatively toxic, since it can affect
+ // all sockets in the process, including those
+ // in use by libraries, etc. Accordingly, do not use this
+ // in a library -- only e.g. atexit() and similar.
+ nng_closeall();
+}
diff --git a/src/compat/nanomsg/nn.h b/src/compat/nanomsg/nn.h
new file mode 100644
index 00000000..8c5cee6f
--- /dev/null
+++ b/src/compat/nanomsg/nn.h
@@ -0,0 +1,315 @@
+//
+// 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.
+//
+
+#ifndef NNG_COMPAT_H
+#define NNG_COMPAT_H
+
+// This header contains interfaces that are intended to offer compatibility
+// with nanomsg v1.0. These are not the "preferred" interfaces for nng,
+// and consumers should only use thse if they are porting software that
+// previously used nanomsg. New programs should use the nng native APIs.
+
+// Note that compatibility promises are limited to public portions of the
+// nanomsg API, and specifically do NOT extend to the ABI. Furthermore,
+// there may be other limitations around less commonly used portions of the
+// API; for example only SP headers may be transported in control data for
+// messages, there is almost no compatibility offered for statistics.
+// Error values may differ from those returned by nanomsg as well; the nng
+// error reporting facility expresses only a subset of the possibilities of
+// nanomsg.
+
+// Note that unlinke nanomsg, nng does not aggressively recycle socket or
+// endpoint IDs, which means applications which made assumptions that these
+// would be relatively small integers (e.g. to use them as array indices)
+// may break. (No promise about values was ever made.)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// clang-format gets in the way of most of this file.
+// We turn it off, at least until it gets smarter about aligning
+// macro definitions or we adopt enums or somesuch.
+// clang-format off
+
+// NNG_DECL is used on declarations to deal with scope.
+// For building Windows DLLs, it should be the appropriate
+// __declspec(). (We recommend *not* building this library
+// as a DLL, but instead linking it statically for your projects
+// to minimize questions about link dependencies later.)
+#ifndef NN_DECL
+#if defined(_WIN32) && !defined(NNG_STATIC_LIB)
+#if defined(NNG_SHARED_LIB)
+#define NN_DECL __declspec(dllexport)
+#else
+#define NN_DECL __declspec(dllimport)
+#endif // NNG_SHARED_LIB
+#else
+#define NN_DECL extern
+#endif // _WIN32 && !NNG_STATIC_LIB
+#endif // NN_DECL
+
+#define AF_SP 1
+#define AF_SP_RAW 2
+
+// Protocol stuff
+#define NN_PROTO_PAIR 1
+#define NN_PROTO_PUBSUB 2
+#define NN_PROTO_REQREP 3
+#define NN_PROTO_PIPELINE 5
+#define NN_PROTO_SURVEY 6
+#define NN_PROTO_BUS 7
+
+#define NN_PAIR (NN_PROTO_PAIR * 16 + 0)
+#define NN_PAIR_v0 (NN_PROTO_PAIR * 16 + 0)
+#define NN_PAIR_V1 (NN_PROTO_PAIR * 16 + 1)
+#define NN_PUB (NN_PROTO_PUBSUB * 16 + 0)
+#define NN_SUB (NN_PROTO_PUBSUB * 16 + 1)
+#define NN_REQ (NN_PROTO_REQREP * 16 + 0)
+#define NN_REP (NN_PROTO_REQREP * 16 + 1)
+#define NN_PUSH (NN_PROTO_PIPELINE * 16 + 0)
+#define NN_PULL (NN_PROTO_PIPELINE * 16 + 1)
+#define NN_SURVEYOR (NN_PROTO_SURVEY * 16 + 2)
+#define NN_RESPONDENT (NN_PROTO_SURVEY * 16 + 3)
+#define NN_BUS (NN_PROTO_BUS * 16 + 0)
+
+#define NN_SOCKADDR_MAX 128
+#define NN_SOL_SOCKET 0
+
+// Flag for send/recv (nonblocking)
+#define NN_DONTWAIT 1
+
+// CMSG data type
+#define PROTO_SP 1
+#define SP_HDR 1
+
+// Errnos. Legacy nanomsg uses posix errnos where possible.
+// If a define is not set, use add NN_ERRBASE. nng does not
+// return all of these values, so there may be some loss of
+// of information for edge cases, but we don't expect that to be
+// a problem really.
+#define NN_ERRBASE (0x10000000)
+#ifndef ENOTSUP
+#define ENOTSUP (NN_ERRBASE+1)
+#endif
+#ifndef EPROTONOSUPPORT
+#define EPROTONOSUPPORT (NN_ERRBASE+2)
+#endif
+#ifndef ENOBUFS
+#define ENOBUFS (NN_ERRBASE+3)
+#endif
+#ifndef ENETDOWN
+#define ENETDOWN (NN_ERRBASE+4)
+#endif
+#ifndef EADDRINUSE
+#define EADDRINUSE (NN_ERRBASE+5)
+#endif
+#ifndef EADDRNOTAVAIL
+#define EADDRNOTAVAIL (NN_ERRBASE+6)
+#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK (NN_ERRBASE+7)
+#endif
+#ifndef EAGAIN
+#define EAGAIN (NN_ERRBASE+8)
+#endif
+#ifndef EBADF
+#define EBADF (NN_ERRBASE+9)
+#endif
+#ifndef EINVAL
+#define EINVAL (NN_ERRBASE+10)
+#endif
+#ifndef EMFILE
+#define EMFILE (NN_ERRBASE+11)
+#endif
+#ifndef EFAULT
+#define EFAULT (NN_ERRBASE+12)
+#endif
+#ifndef EACCES
+#define EACCES (NN_ERRBASE+13)
+#endif
+#ifndef ENETRESET
+#define ENETRESET (NN_ERRBASE+14)
+#endif
+#ifndef ENETUNREACH
+#define ENETUNREACH (NN_ERRBASE+15)
+#endif
+#ifndef EHOSTUNREACH
+#define EHOSTUNREACH (NN_ERRBASE+16)
+#endif
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT (NN_ERRBASE+17)
+#endif
+#ifndef EINPROGRESS
+#define EINPROGRESS (NN_ERRBASE+18)
+#endif
+#ifndef EPROTO
+#define EPROTO (NN_ERRBASE+19)
+#endif
+#ifndef ECONNREFUSED
+#define ECONNREFUSED (NN_ERRBASE+20)
+#endif
+#ifndef ENOTCONN
+#define ENOTCONN (NN_ERRBASE+21)
+#endif
+#ifndef EMSGSIZE
+#define EMSGSIZE (NN_ERRBASE+22)
+#endif
+#ifndef ETIMEDOUT
+#define ETIMEDOUT (NN_ERRBASE+23)
+#endif
+#ifndef ECONNABORTED
+#define ECONNABORTED (NN_ERRBASE+24)
+#endif
+#ifndef ECONNRESET
+#define ECONNRESET (NN_ERRBASE+25)
+#endif
+#ifndef ENOPROTOOPT
+#define ENOPROTOOPT (NN_ERRBASE+26)
+#endif
+#ifndef EISCONN
+#define EISCONN (NN_ERRBASE+27)
+#endif
+#ifndef ESOCKNOSUPPORT
+#define ESOCKNOSPPORT (NN_ERRBASE+28)
+#endif
+#ifndef ETERM
+#define ETERM (NN_ERRBASE+29)
+#endif
+#ifndef EFSM
+#define EFSM (NN_ERRBASE+30)
+#endif
+#ifndef ENOENT
+#define ENOENT (NN_ERRBASE+31)
+#endif
+#ifndef EIO
+#define EIO (NN_ERRBASE+32)
+#endif
+
+// Socket options
+#define NN_LINGER 1
+#define NN_SNDBUF 2
+#define NN_RCVBUF 3
+#define NN_SNDTIMEO 4
+#define NN_RCVTIMEO 5
+#define NN_RECONNECT_IVL 6
+#define NN_RECONNECT_IVL_MAX 7
+#define NN_SNDPRIO 8
+#define NN_RCVPRIO 9
+#define NN_SNDFD 10
+#define NN_RCVFD 11
+#define NN_DOMAIN 12
+#define NN_PROTOCOL 13
+#define NN_IPV4ONLY 14
+#define NN_SOCKET_NAME 15
+#define NN_RCVMAXSIZE 16
+#define NN_MAXTTL 17
+
+// Protocol-specific options. To simplify thins we encode the protocol
+// level in the option.
+#define NN_SUB_SUBSCRIBE (NN_SUB * 16 + 1)
+#define NN_SUB_UNSUBSCRIBE (NN_SUB * 16 + 2)
+#define NN_REQ_RESEND_IVL (NN_REQ * 16 + 1)
+#define NN_SURVEYOR_DEADLINE (NN_SURVEYOR * 16 + 1)
+
+// Level options for tranports
+#define NN_INPROC (-1)
+#define NN_IPC (-2)
+#define NN_IPC_SEC_ATTR 1
+#define NN_IPC_OUTBUFSZ 2
+#define NN_IPC_INBUFSZ 3
+#define NN_TCP (-3)
+#define NN_TCP_NODELAY 1
+#define NN_WS (-4)
+#define NN_WS_MSG_TYPE 1
+#define NN_WS_MSG_TYPE_TEXT 1
+#define NN_WS_MSG_TYPE_BINARY 2
+
+// from this point on formatting is fine
+// clang-format on
+
+// Poll stuff
+#define NN_POLLIN 1
+#define NN_POLLOUT 2
+struct nn_pollfd {
+ int fd;
+ uint16_t events;
+ uint16_t revents;
+};
+
+// Magical size for allocation
+#define NN_MSG ((size_t) -1)
+
+struct nn_iovec {
+ void * iov_base;
+ size_t iov_len;
+};
+
+struct nn_msghdr {
+ struct nn_iovec *msg_iov;
+ int msg_iovlen;
+ void * msg_control;
+ size_t msg_controllen;
+};
+
+struct nn_cmsghdr {
+ size_t cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+};
+
+#define NN_CMSG_ALIGN(len) \
+ (((len) + sizeof(size_t) - 1) & (size_t) ~(sizeof(size_t) - 1))
+
+// Unlike old nanomsg, we explicitly only support the SP header as attached
+// cmsg data. It turns out that old nanomsg didn't really store anything
+// useful otherwise anyway. (One specific exception was that it stored the
+// message type of text or binary for the websocket transport. We don't think
+// anyone used that in practice though.)
+#define NN_CMSG_FIRSTHDR(mh) nn_cmsg_next((struct nn_msghdr *) (mh), NULL)
+#define NN_CMSG_NXTHDR(mh, ch) \
+ nn_cmsg_next((struct nn_msghdr *) (mh), (struct nn_cmsghdr *) ch)
+#define NN_CMSG_DATA(ch) ((unsigned char *) (((struct nn_cmsghdr *) (ch)) + 1))
+#define NN_CMSG_SPACE(len) \
+ (NN_CMSG_ALIGN(len) + NN_CMSG_ALIGN(sizeof(struct nn_cmsghdr)))
+#define NN_CMSG_LEN(len) (NN_CMSG_ALIGN(sizeof(struct nn_cmsghdr)) + (len))
+
+NN_DECL struct nn_cmsghdr *nn_cmsg_next(
+ struct nn_msghdr *, struct nn_cmsghdr *);
+NN_DECL int nn_socket(int, int);
+NN_DECL int nn_setsockopt(int, int, int, const void *, size_t);
+NN_DECL int nn_getsockopt(int, int, int, void *, size_t *);
+NN_DECL int nn_bind(int, const char *);
+NN_DECL int nn_connect(int, const char *);
+NN_DECL int nn_shutdown(int, int);
+NN_DECL int nn_send(int, const void *, size_t, int);
+NN_DECL int nn_recv(int, void *, size_t, int);
+NN_DECL int nn_sendmsg(int, const struct nn_msghdr *, int);
+NN_DECL int nn_recvmsg(int, struct nn_msghdr *, int);
+NN_DECL int nn_close(int);
+NN_DECL int nn_poll(struct nn_pollfd *, int, int);
+NN_DECL int nn_device(int, int);
+NN_DECL uint64_t nn_get_statistic(int, int);
+NN_DECL void * nn_allocmsg(size_t, int);
+NN_DECL void * nn_reallocmsg(void *, size_t);
+NN_DECL int nn_freemsg(void *);
+NN_DECL int nn_errno(void);
+NN_DECL const char *nn_strerror(int);
+NN_DECL void nn_term(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NNG_COMPAT_H