aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2018-12-22 19:23:26 -0800
committerGitHub <noreply@github.com>2018-12-22 19:23:26 -0800
commitee16d11a59120cd4d981e0dcb90741fa4141372a (patch)
tree98da4bc9601f30e2c9deb403fe213c9fb4335597
parentd00483eadbee48b820a8a79163c5296953b6f5cb (diff)
downloadnng-ee16d11a59120cd4d981e0dcb90741fa4141372a.tar.gz
nng-ee16d11a59120cd4d981e0dcb90741fa4141372a.tar.bz2
nng-ee16d11a59120cd4d981e0dcb90741fa4141372a.zip
fixes #823 Define public IPC (#824)
This introduces a basic IPC API, modeled on the TCP API, for direct access. Only connection options are exposed at present -- we need to add options for dialers and listeners (and particularly listener settings for permissions and security attributes.) Documentation is still outstanding, but a very limited test suite exists.
-rw-r--r--include/nng/supplemental/ipc/ipc.h133
-rw-r--r--include/nng/supplemental/tcp/tcp.h9
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/core/platform.h15
-rw-r--r--src/platform/posix/posix_ipcconn.c93
-rw-r--r--src/platform/windows/win_impl.h4
-rw-r--r--src/platform/windows/win_ipc.h38
-rw-r--r--src/platform/windows/win_ipcconn.c48
-rw-r--r--src/platform/windows/win_ipcdial.c17
-rw-r--r--src/platform/windows/win_ipclisten.c13
-rw-r--r--src/platform/windows/win_sockaddr.c5
-rw-r--r--src/supplemental/ipc/CMakeLists.txt16
-rw-r--r--src/supplemental/ipc/ipc.c136
-rw-r--r--src/supplemental/util/options.c4
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/ipcsupp.c145
16 files changed, 624 insertions, 54 deletions
diff --git a/include/nng/supplemental/ipc/ipc.h b/include/nng/supplemental/ipc/ipc.h
new file mode 100644
index 00000000..f7091ec8
--- /dev/null
+++ b/include/nng/supplemental/ipc/ipc.h
@@ -0,0 +1,133 @@
+//
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Devolutions <infos@devolutions.net>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#ifndef NNG_SUPPLEMENTAL_IPC_IPC_H
+#define NNG_SUPPLEMENTAL_IPC_IPC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <nng/nng.h>
+#include <nng/transport/ipc/ipc.h> // For IPC option names.
+
+// This is our "public" IPC API. This allows applications to access
+// basic IPC functions, using our AIO framework. Most applications will
+// not need this. This supports both UNIX domain sockets (AF_LOCAL),
+// and Windows Named Pipes, depending on the underlying platform.
+
+// nng_ipc represents a single IPC connection. This is generally
+// a connected stream.
+typedef struct nng_ipc_s nng_ipc;
+
+// nng_ipc_dialer is a dialer object used to create outgoing connections.
+// This is a bit different than a typical BSD socket API, but doing it
+// helps keep the API orthogonal to the listener API.
+typedef struct nng_ipc_dialer_s nng_ipc_dialer;
+
+// nng_ipc_listener is a listener object. This is used to accept incoming
+// connections.
+typedef struct nng_ipc_listener_s nng_ipc_listener;
+
+// nng_ipc_close closes the connection, but does not release the
+// underlying object. Operations that may be pending on the connect,
+// as well as further operations, will result in NNG_ECLOSED.
+NNG_DECL void nng_ipc_close(nng_ipc *);
+
+// nng_ipc_free frees the IPC connection, closing it if it is open.
+// This is necessary to release the resources of the IPC object.
+// (It is an error to refer to the IPC object after this is called.)
+NNG_DECL void nng_ipc_free(nng_ipc *);
+
+// nng_ipc_send sends the data in the aio, which should be stored in
+// an iov for the message. Note that the iov in the aio may be modified,
+// so applications should not assume otherwise.
+NNG_DECL void nng_ipc_send(nng_ipc *, nng_aio *);
+
+// nng_ipc_recv receives data into the iov supplied. It is possible for
+// the callback to be executed with less data read than requested. (This
+// is actually pretty likely for bulk transfers.) The caller should update
+// the iov's and resubmit as needed.
+NNG_DECL void nng_ipc_recv(nng_ipc *, nng_aio *);
+
+// nng_ipc_getopt is used to get options. The options available are:
+//
+// NNG_OPT_REMADDR - nng_sockaddr for the connection.
+// NNG_OPT_LOCADD - nng_sockaddr for the connection.
+// NNG_OPT_IPC_PEER_UID - peer user ID (if available), uint64_t
+// NNG_OPT_IPC_PEER_GID - peer group ID (if available), uint64_t
+// NNG_OPT_IPC_PEER_ZONEID - peer zone ID (illumos/Solaris only), uint64_t
+NNG_DECL int nng_ipc_getopt(nng_ipc *, const char *, void *, size_t *);
+
+// nng_ipc_setopt is used to set options. There are presently no such
+// options defined for connections.
+NNG_DECL int nng_ipc_setopt(nng_ipc *, const char *, const void *, size_t);
+
+// nng_ipc_dialer_alloc is used to allocate an IPC dialer.
+NNG_DECL int nng_ipc_dialer_alloc(nng_ipc_dialer **);
+
+// nng_ipc_dialer_close closes the dialer, aborting any pending outbound
+// connection attempts (and preventing any new ones) with NNG_ECLOSED.
+// This does not free the resources associated with the dialer, so the
+// application should still call nng_ipc_dialer_free. Connections already
+// established by the dialer are unaffected by this call.
+NNG_DECL void nng_ipc_dialer_close(nng_ipc_dialer *);
+
+// nng_ipc_dialer_free is used to free the dialer. This implicitly calls
+// nng_ipc_dialer_close, then releases the resources associated with the
+// dialer. It is therefore an error for the application to attempt to use
+// the dialer after this call.
+NNG_DECL void nng_ipc_dialer_free(nng_ipc_dialer *);
+
+// nng_ipc_dialer_dial attempts to create a new connection (nng_ipc *)
+// may making an outbound connect call. If this succeeds, the aio
+// will return a suitable nng_ipc * in the first output of the aio.
+// (I.e. nng_aio_get_output(aio, 0).) The destination address to dial
+// is stored in the 2nd argument.
+NNG_DECL void nng_ipc_dialer_dial(
+ nng_ipc_dialer *, const nng_sockaddr *, nng_aio *);
+
+// nng_ipc_listener_alloc creates a listener.
+NNG_DECL int nng_ipc_listener_alloc(nng_ipc_listener **);
+
+// nng_ipc_listener_close closes the listener, unbinding it from
+// any active path if it was previously bound with nng_ipc_listener_listen.
+// This does not completely release the resources associated with the
+// listener, so nng_ipc_listener_free should still be called.
+// Any pending accept calls will be aborted with NNG_ECLOSED, and any
+// future attempts will also result in NNG_ECLOSED. Connections already
+// established by this listener are unaffected by this call.
+NNG_DECL void nng_ipc_listener_close(nng_ipc_listener *);
+
+// nng_ipc_listener_free frees the listener. This causes any other
+// outstanding accept calls to return NNG_ECLOSED. The listener cannot
+// be used by the application after this is called. This implictly
+// includes a call to nng_ipc_listener_close().
+NNG_DECL void nng_ipc_listener_free(nng_ipc_listener *);
+
+// nng_ipc_listener_listen binds to the IPC address and arranges for
+// the IPC path to be created and bound. It does not accept any new
+// incoming connections. This operation is synchronous.
+NNG_DECL int nng_ipc_listener_listen(nng_ipc_listener *, const nng_sockaddr *);
+
+// nng_ipc_listener_accept accepts an incoming connection (creating an
+// nng_ipc * object), and returns it in the nng_aio as the first output
+// on success.
+NNG_DECL void nng_ipc_listener_accept(nng_ipc_listener *, nng_aio *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NNG_SUPPLEMENTAL_IPC_IPC_H
diff --git a/include/nng/supplemental/tcp/tcp.h b/include/nng/supplemental/tcp/tcp.h
index 992bfd81..9b83d678 100644
--- a/include/nng/supplemental/tcp/tcp.h
+++ b/include/nng/supplemental/tcp/tcp.h
@@ -37,8 +37,8 @@ typedef struct nng_tcp_dialer_s nng_tcp_dialer;
// connections.
typedef struct nng_tcp_listener_s nng_tcp_listener;
-// nng_tcp_conn close closes the connection, but does not release
-// the underlying object. Operations that may be pending on the connect,
+// nng_tcp_close closes the connection, but does not release the
+// underlying object. Operations that may be pending on the connect,
// as well as further operations, will result in NNG_ECLOSED.
NNG_DECL void nng_tcp_close(nng_tcp *);
@@ -47,7 +47,7 @@ NNG_DECL void nng_tcp_close(nng_tcp *);
// (It is an error to refer to the TCP object after this is called.)
NNG_DECL void nng_tcp_free(nng_tcp *);
-// nng_tcp_conn_send sends the data in the aio, which should be stored in
+// nng_tcp_send sends the data in the aio, which should be stored in
// an iov for the message. Note that the iov in the aio may be modified,
// so applications should not assume otherwise.
NNG_DECL void nng_tcp_send(nng_tcp *, nng_aio *);
@@ -58,8 +58,7 @@ NNG_DECL void nng_tcp_send(nng_tcp *, nng_aio *);
// the iov's and resubmit as needed.
NNG_DECL void nng_tcp_recv(nng_tcp *, nng_aio *);
-// nng_tcp_sockname obtains an nng_sockaddr associated with our local
-// address.
+// nng_tcp_sockname obtains an nng_sockaddr associated with our local address.
NNG_DECL int nng_tcp_sockname(nng_tcp *, nng_sockaddr *);
// nng_tcp_peername obtains an nng_sockaddr associated with our peer.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c0380a0f..c31539c2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -183,6 +183,7 @@ add_subdirectory(transport/zerotier)
add_subdirectory(supplemental/base64)
add_subdirectory(supplemental/http)
+add_subdirectory(supplemental/ipc)
add_subdirectory(supplemental/sha1)
add_subdirectory(supplemental/tcp)
add_subdirectory(supplemental/tls)
diff --git a/src/core/platform.h b/src/core/platform.h
index 0a0709d1..2b7eb9f1 100644
--- a/src/core/platform.h
+++ b/src/core/platform.h
@@ -1,6 +1,7 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Devolutions <infos@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
@@ -375,6 +376,20 @@ extern int nni_ipc_conn_get_peer_pid(nni_ipc_conn *, uint64_t *);
// NB: Only illumos & SunOS systems have the notion of "zones".
extern int nni_ipc_conn_get_peer_zoneid(nni_ipc_conn *, uint64_t *);
+// nni_ipc_conn_setopt is like setsockopt, but uses string names. These
+// are the same names from the IPC transport, generally. There are no
+// options that are generally settable on an IPC connection.
+// NNG_OPT_REMADDR, NNG_OPT_LOCADDR, NNG_OPT_IPC_PERMISSIONS,
+// NNG_OPT_
+extern int nni_ipc_conn_setopt(
+ nni_ipc_conn *, const char *, const void *, size_t);
+
+// nni_ipc_conn_getopt is like getsockopt, but uses string names.
+// We support NNG_OPT_REMADDR and NNG_OPT_LOCADDR (with argument type
+// nng_sockaddr), and on some platforms NNG_OPT_IPC_PEER_[UID,GID,ZONEID]
+// (with type uint64_t.)
+extern int nni_ipc_conn_getopt(nni_ipc_conn *, const char *, void *, size_t *);
+
// nni_ipc_dialer_init creates a new dialer object.
extern int nni_ipc_dialer_init(nni_ipc_dialer **);
diff --git a/src/platform/posix/posix_ipcconn.c b/src/platform/posix/posix_ipcconn.c
index 0dae8bae..595ebe62 100644
--- a/src/platform/posix/posix_ipcconn.c
+++ b/src/platform/posix/posix_ipcconn.c
@@ -1,6 +1,7 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Devolutions <infos@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
@@ -10,8 +11,6 @@
#include "core/nng_impl.h"
-#ifdef NNG_PLATFORM_POSIX
-
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
@@ -40,6 +39,8 @@
#include "posix_ipc.h"
+#include <nng/transport/ipc/ipc.h>
+
static void
ipc_conn_dowrite(nni_ipc_conn *c)
{
@@ -464,6 +465,19 @@ nni_ipc_conn_get_peer_pid(nni_ipc_conn *c, uint64_t *pid)
}
int
+nni_ipc_conn_sockname(nni_ipc_conn *c, nni_sockaddr *sa)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen = sizeof(ss);
+ int fd = nni_posix_pfd_fd(c->pfd);
+
+ if (getsockname(fd, (void *) &ss, &sslen) != 0) {
+ return (nni_plat_errno(errno));
+ }
+ return (nni_posix_sockaddr2nn(sa, &ss));
+}
+
+int
nni_posix_ipc_conn_init(nni_ipc_conn **cp, nni_posix_pfd *pfd)
{
nni_ipc_conn *c;
@@ -502,4 +516,77 @@ nni_ipc_conn_fini(nni_ipc_conn *c)
NNI_FREE_STRUCT(c);
}
-#endif // NNG_PLATFORM_POSIX
+int
+nni_ipc_conn_setopt(
+ nni_ipc_conn *c, const char *name, const void *val, size_t sz)
+{
+ NNI_ARG_UNUSED(c);
+ NNI_ARG_UNUSED(val);
+ NNI_ARG_UNUSED(sz);
+ if ((strcmp(name, NNG_OPT_LOCADDR) == 0) ||
+ (strcmp(name, NNG_OPT_REMADDR) == 0) ||
+ (strcmp(name, NNG_OPT_IPC_PEER_PID) == 0) ||
+ (strcmp(name, NNG_OPT_IPC_PEER_UID) == 0) ||
+ (strcmp(name, NNG_OPT_IPC_PEER_GID) == 0) ||
+ (strcmp(name, NNG_OPT_IPC_PEER_ZONEID) == 0)) {
+ return (NNG_EREADONLY);
+ }
+ return (NNG_ENOTSUP);
+}
+
+int
+nni_ipc_conn_getopt(nni_ipc_conn *c, const char *name, void *val, size_t *szp)
+{
+ int rv;
+ uint64_t *idp = val;
+
+ if ((strcmp(name, NNG_OPT_LOCADDR) == 0) ||
+ (strcmp(name, NNG_OPT_REMADDR) == 0)) {
+ nng_sockaddr *sa = val;
+ if (*szp < sizeof(*sa)) {
+ return (NNG_EINVAL);
+ }
+
+ if ((rv = nni_ipc_conn_sockname(c, sa)) == 0) {
+ *szp = sizeof(*sa);
+ }
+ return (rv);
+ }
+ if (strcmp(name, NNG_OPT_IPC_PEER_PID) == 0) {
+ if (*szp < sizeof(*idp)) {
+ return (NNG_EINVAL);
+ }
+ if ((rv = nni_ipc_conn_get_peer_pid(c, idp)) == 0) {
+ *szp = sizeof(*idp);
+ }
+ return (rv);
+ }
+ if (strcmp(name, NNG_OPT_IPC_PEER_UID) == 0) {
+ if (*szp < sizeof(*idp)) {
+ return (NNG_EINVAL);
+ }
+ if ((rv = nni_ipc_conn_get_peer_uid(c, idp)) == 0) {
+ *szp = sizeof(*idp);
+ }
+ return (rv);
+ }
+ if (strcmp(name, NNG_OPT_IPC_PEER_GID) == 0) {
+ if (*szp < sizeof(*idp)) {
+ return (NNG_EINVAL);
+ }
+ if ((rv = nni_ipc_conn_get_peer_gid(c, idp)) == 0) {
+ *szp = sizeof(*idp);
+ }
+ return (rv);
+ }
+ if (strcmp(name, NNG_OPT_IPC_PEER_ZONEID) == 0) {
+ if (*szp < sizeof(*idp)) {
+ return (NNG_EINVAL);
+ }
+ if ((rv = nni_ipc_conn_get_peer_zoneid(c, idp)) == 0) {
+ *szp = sizeof(*idp);
+ }
+ return (rv);
+ }
+ return (NNG_ENOTSUP);
+}
diff --git a/src/platform/windows/win_impl.h b/src/platform/windows/win_impl.h
index 462e1707..fd4c9343 100644
--- a/src/platform/windows/win_impl.h
+++ b/src/platform/windows/win_impl.h
@@ -11,8 +11,6 @@
#ifndef PLATFORM_WIN_IMPL_H
#define PLATFORM_WIN_IMPL_H
-#ifdef NNG_PLATFORM_WINDOWS
-
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
@@ -106,6 +104,4 @@ extern int nni_win_nn2sockaddr(SOCKADDR_STORAGE *, const nni_sockaddr *);
#define NNG_PLATFORM_DIR_SEP "\\"
-#endif // NNG_PLATFORM_WINDOWS
-
#endif // PLATFORM_WIN_IMPL_H
diff --git a/src/platform/windows/win_ipc.h b/src/platform/windows/win_ipc.h
index 51ce5548..9a4e245a 100644
--- a/src/platform/windows/win_ipc.h
+++ b/src/platform/windows/win_ipc.h
@@ -1,6 +1,7 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Devolutions <infos@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
@@ -14,26 +15,27 @@
// This header file is private to the IPC (named pipes) support for Windows.
#include "core/nng_impl.h"
+#include "win_impl.h"
-#ifdef NNG_PLATFORM_WINDOWS
+#define IPC_PIPE_PREFIX "\\\\.\\pipe\\"
struct nni_ipc_conn {
- HANDLE f;
- nni_win_io recv_io;
- nni_win_io send_io;
- nni_win_io conn_io;
- nni_list recv_aios;
- nni_list send_aios;
- nni_aio * conn_aio;
- nni_ipc_dialer * dialer;
- nni_ipc_listener *listener;
- int recv_rv;
- int send_rv;
- int conn_rv;
- bool closed;
- nni_mtx mtx;
- nni_cv cv;
- nni_reap_item reap;
+ HANDLE f;
+ nni_win_io recv_io;
+ nni_win_io send_io;
+ nni_win_io conn_io;
+ nni_list recv_aios;
+ nni_list send_aios;
+ nni_aio * conn_aio;
+ nng_sockaddr sa;
+ bool dialer;
+ int recv_rv;
+ int send_rv;
+ int conn_rv;
+ bool closed;
+ nni_mtx mtx;
+ nni_cv cv;
+ nni_reap_item reap;
};
struct nni_ipc_dialer {
@@ -57,6 +59,4 @@ struct nni_ipc_listener {
extern int nni_win_ipc_conn_init(nni_ipc_conn **, HANDLE);
-#endif // NNG_PLATFORM_WINDOWS
-
#endif // NNG_PLATFORM_WIN_WINIPC_H
diff --git a/src/platform/windows/win_ipcconn.c b/src/platform/windows/win_ipcconn.c
index d4e7f5ac..107ffb9d 100644
--- a/src/platform/windows/win_ipcconn.c
+++ b/src/platform/windows/win_ipcconn.c
@@ -1,6 +1,7 @@
//
// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Devolutions <infos@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
@@ -10,12 +11,12 @@
#include "core/nng_impl.h"
-#ifdef NNG_PLATFORM_WINDOWS
-
#include "win_ipc.h"
#include <stdio.h>
+#include <nng/transport/ipc/ipc.h>
+
static void
ipc_recv_start(nni_ipc_conn *c)
{
@@ -385,4 +386,45 @@ nni_ipc_conn_get_peer_pid(nni_ipc_conn *c, uint64_t *pid)
return (0);
}
-#endif // NNG_PLATFORM_WINDOWS
+int
+nni_ipc_conn_setopt(
+ nni_ipc_conn *c, const char *name, const void *val, size_t sz)
+{
+ NNI_ARG_UNUSED(c);
+ NNI_ARG_UNUSED(val);
+ NNI_ARG_UNUSED(sz);
+ if ((strcmp(name, NNG_OPT_LOCADDR) == 0) ||
+ (strcmp(name, NNG_OPT_REMADDR) == 0) ||
+ (strcmp(name, NNG_OPT_IPC_PEER_PID) == 0)) {
+ return (NNG_EREADONLY);
+ }
+ return (NNG_ENOTSUP);
+}
+
+int
+nni_ipc_conn_getopt(nni_ipc_conn *c, const char *name, void *val, size_t *szp)
+{
+ if ((strcmp(name, NNG_OPT_LOCADDR) == 0) ||
+ (strcmp(name, NNG_OPT_REMADDR) == 0)) {
+ if (*szp < sizeof(c->sa)) {
+ return (NNG_EINVAL);
+ }
+
+ memcpy(val, &c->sa, sizeof(c->sa));
+ *szp = sizeof(c->sa);
+ return (0);
+ }
+ if (strcmp(name, NNG_OPT_IPC_PEER_PID) == 0) {
+ int rv;
+ uint64_t *idp = val;
+ if (*szp < sizeof(*idp)) {
+ return (NNG_EINVAL);
+ }
+ if ((rv = nni_ipc_conn_get_peer_pid(c, idp)) == 0) {
+ ;
+ *szp = sizeof(*idp);
+ }
+ return (rv);
+ }
+ return (NNG_ENOTSUP);
+}
diff --git a/src/platform/windows/win_ipcdial.c b/src/platform/windows/win_ipcdial.c
index 67865687..4c3d5c6c 100644
--- a/src/platform/windows/win_ipcdial.c
+++ b/src/platform/windows/win_ipcdial.c
@@ -10,8 +10,6 @@
#include "core/nng_impl.h"
-#ifdef NNG_PLATFORM_WINDOWS
-
#include "win_ipc.h"
#include <stdio.h>
@@ -108,16 +106,21 @@ ipc_dial_thr(void *arg)
nni_list_remove(&d->aios, aio);
nni_aio_set_prov_extra(aio, 0, NULL);
- nni_strfree(path);
if (((rv = nni_win_io_register(f)) != 0) ||
((rv = nni_win_ipc_conn_init(&c, f)) != 0)) {
DisconnectNamedPipe(f);
CloseHandle(f);
nni_aio_finish_error(aio, rv);
+ nni_strfree(path);
continue;
}
- c->dialer = d;
+ c->dialer = true;
+ c->sa.s_ipc.sa_family = NNG_AF_IPC;
+ snprintf(c->sa.s_ipc.sa_path,
+ sizeof(c->sa.s_ipc.sa_path), "%s",
+ path + strlen(IPC_PIPE_PREFIX));
+ nni_strfree(path);
nni_aio_set_output(aio, 0, c);
nni_aio_finish(aio, 0, 0);
}
@@ -169,8 +172,8 @@ nni_ipc_dialer_dial(nni_ipc_dialer *d, const nni_sockaddr *sa, nni_aio *aio)
nni_aio_finish_error(aio, NNG_EADDRINVAL);
return;
}
- if ((rv = nni_asprintf(&path, "\\\\.\\pipe\\%s", sa->s_ipc.sa_path)) !=
- 0) {
+ if ((rv = nni_asprintf(
+ &path, IPC_PIPE_PREFIX "%s", sa->s_ipc.sa_path)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
@@ -261,5 +264,3 @@ nni_win_ipc_sysfini(void)
nni_cv_fini(&worker->cv);
nni_mtx_fini(&worker->mtx);
}
-
-#endif // NNG_PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_ipclisten.c b/src/platform/windows/win_ipclisten.c
index 0fc1c9fc..d0fd6ef9 100644
--- a/src/platform/windows/win_ipclisten.c
+++ b/src/platform/windows/win_ipclisten.c
@@ -10,8 +10,6 @@
#include "core/nng_impl.h"
-#ifdef NNG_PLATFORM_WINDOWS
-
#include "win_ipc.h"
#include <stdio.h>
@@ -58,8 +56,11 @@ ipc_accept_done(nni_ipc_listener *l, int rv)
nni_aio_finish_error(aio, rv);
return;
}
- l->f = f;
- c->listener = l;
+ l->f = f;
+ c->sa.s_ipc.sa_family = NNG_AF_IPC;
+ snprintf(c->sa.s_ipc.sa_path, sizeof(c->sa.s_ipc.sa_path), "%s",
+ l->path + strlen(IPC_PIPE_PREFIX));
+ c->dialer = false;
nni_aio_set_output(aio, 0, c);
nni_aio_finish(aio, 0, 0);
}
@@ -185,7 +186,7 @@ nni_ipc_listener_listen(nni_ipc_listener *l, const nni_sockaddr *sa)
nni_mtx_unlock(&l->mtx);
return (NNG_ECLOSED);
}
- rv = nni_asprintf(&path, "\\\\.\\pipe\\%s", sa->s_ipc.sa_path);
+ rv = nni_asprintf(&path, IPC_PIPE_PREFIX "%s", sa->s_ipc.sa_path);
if (rv != 0) {
nni_mtx_unlock(&l->mtx);
return (rv);
@@ -292,5 +293,3 @@ nni_ipc_listener_fini(nni_ipc_listener *l)
nni_mtx_fini(&l->mtx);
NNI_FREE_STRUCT(l);
}
-
-#endif // NNG_PLATFORM_WINDOWS
diff --git a/src/platform/windows/win_sockaddr.c b/src/platform/windows/win_sockaddr.c
index fbb960b7..d1fed2c7 100644
--- a/src/platform/windows/win_sockaddr.c
+++ b/src/platform/windows/win_sockaddr.c
@@ -9,8 +9,7 @@
//
#include "core/nng_impl.h"
-
-#ifdef NNG_PLATFORM_WINDOWS
+#include "win_impl.h"
#include <string.h>
@@ -69,5 +68,3 @@ nni_win_sockaddr2nn(nni_sockaddr *sa, const SOCKADDR_STORAGE *ss)
}
return (-1);
}
-
-#endif // NNG_PLATFORM_WINDOWS
diff --git a/src/supplemental/ipc/CMakeLists.txt b/src/supplemental/ipc/CMakeLists.txt
new file mode 100644
index 00000000..691522f0
--- /dev/null
+++ b/src/supplemental/ipc/CMakeLists.txt
@@ -0,0 +1,16 @@
+#
+# Copyright 2018 Capitar IT Group BV <info@capitar.com>
+# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+# Copyright 2018 Devolutions <infos@devolutions.net>
+#
+# This software is supplied under the terms of the MIT License, a
+# copy of which should be located in the distribution where this
+# file was obtained (LICENSE.txt). A copy of the license may also be
+# found online at https://opensource.org/licenses/MIT.
+#
+
+set(_SRCS supplemental/ipc/ipc.c
+ ${PROJECT_SOURCE_DIR}/include/nng/supplemental/ipc/ipc.h
+)
+
+set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE)
diff --git a/src/supplemental/ipc/ipc.c b/src/supplemental/ipc/ipc.c
new file mode 100644
index 00000000..36c18563
--- /dev/null
+++ b/src/supplemental/ipc/ipc.c
@@ -0,0 +1,136 @@
+//
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Devolutions <infos@devolutions.net>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <nng/nng.h>
+#include <nng/supplemental/ipc/ipc.h>
+
+#include "core/nng_impl.h"
+
+// This is our "public" IPC API. This allows applications to access
+// basic IPC functions, using our AIO framework. Most applications will
+// not need this.
+
+// We treat nng_ipc as nni_ipc_conn, nng_ipc_dialer as nni_ipc_dialer,
+// and nng_ipc_listener as nni_ipc_listener. We cast through void to
+// provide isolation of the names in a way that makes the compiler happy.
+// It turns out we can pretty much just wrap the platform API for IPC that
+// we have already created.
+
+void
+nng_ipc_close(nng_ipc *ipc)
+{
+ nni_ipc_conn_close((void *) ipc);
+}
+
+void
+nng_ipc_free(nng_ipc *ipc)
+{
+ nni_ipc_conn_fini((void *) ipc);
+}
+
+void
+nng_ipc_send(nng_ipc *ipc, nng_aio *aio)
+{
+ nni_ipc_conn_send((void *) ipc, aio);
+}
+
+void
+nng_ipc_recv(nng_ipc *ipc, nng_aio *aio)
+{
+ nni_ipc_conn_recv((void *) ipc, aio);
+}
+
+int
+nng_ipc_setopt(nng_ipc *ipc, const char *name, const void *val, size_t sz)
+{
+ return (nni_ipc_conn_setopt((void *) ipc, name, val, sz));
+}
+
+int
+nng_ipc_getopt(nng_ipc *ipc, const char *name, void *val, size_t *szp)
+{
+ return (nni_ipc_conn_getopt((void *) ipc, name, val, szp));
+}
+
+int
+nng_ipc_dialer_alloc(nng_ipc_dialer **dp)
+{
+ nni_ipc_dialer *d;
+ int rv;
+
+ if ((rv = nni_init()) != 0) {
+ return (rv);
+ }
+ if ((rv = nni_ipc_dialer_init(&d)) == 0) {
+ *dp = (void *) d;
+ }
+ return (rv);
+}
+
+void
+nng_ipc_dialer_close(nng_ipc_dialer *d)
+{
+ nni_ipc_dialer_close((void *) d);
+}
+
+void
+nng_ipc_dialer_free(nng_ipc_dialer *d)
+{
+ nni_ipc_dialer_fini((void *) d);
+}
+
+void
+nng_ipc_dialer_dial(nng_ipc_dialer *d, const nng_sockaddr *sa, nng_aio *aio)
+{
+ nni_ipc_dialer_dial((void *) d, sa, aio);
+}
+
+int
+nng_ipc_listener_alloc(nng_ipc_listener **lp)
+{
+ nni_ipc_listener *l;
+ int rv;
+
+ if ((rv = nni_init()) != 0) {
+ return (rv);
+ }
+ if ((rv = nni_ipc_listener_init(&l)) == 0) {
+ *lp = (void *) l;
+ }
+ return (rv);
+}
+
+void
+nng_ipc_listener_close(nng_ipc_listener *l)
+{
+ nni_ipc_listener_close((void *) l);
+}
+
+void
+nng_ipc_listener_free(nng_ipc_listener *l)
+{
+ nni_ipc_listener_fini((void *) l);
+}
+
+int
+nng_ipc_listener_listen(nng_ipc_listener *l, const nng_sockaddr *sa)
+{
+ return (nni_ipc_listener_listen((void *) l, sa));
+}
+
+void
+nng_ipc_listener_accept(nng_ipc_listener *l, nng_aio *aio)
+{
+ nni_ipc_listener_accept((void *) l, aio);
+}
diff --git a/src/supplemental/util/options.c b/src/supplemental/util/options.c
index c8dafed8..961e0bb2 100644
--- a/src/supplemental/util/options.c
+++ b/src/supplemental/util/options.c
@@ -11,8 +11,10 @@
#include <stdlib.h>
#include <string.h>
+#include <nng/nng.h>
+#include <nng/supplemental/util/options.h>
+
#include "core/nng_impl.h"
-#include "nng/supplemental/util/options.h"
// Call with optidx set to 1 to start parsing.
int
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 2755f081..ffba5deb 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -150,6 +150,7 @@ add_nng_test1(idhash 30 NNG_STATIC_LIB)
add_nng_test1(inproc 5 NNG_TRANSPORT_INPROC)
add_nng_test1(ipc 5 NNG_TRANSPORT_IPC)
add_nng_test1(ipcperms 5 NNG_TRANSPORT_IPC)
+add_nng_test1(ipcsupp 10 NNG_TRANSPORT_IPC)
add_nng_test1(ipcwinsec 5 NNG_TRANSPORT_IPC)
add_nng_test1(list 5 NNG_STATIC_LIB)
add_nng_test(message 5)
diff --git a/tests/ipcsupp.c b/tests/ipcsupp.c
new file mode 100644
index 00000000..6ea0639a
--- /dev/null
+++ b/tests/ipcsupp.c
@@ -0,0 +1,145 @@
+//
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2018 Devolutions <infos@devolutions.net>
+//
+// This software is supplied under the terms of the MIT License, a
+// copy of which should be located in the distribution where this
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+#include <string.h>
+
+#include <nng/nng.h>
+#include <nng/supplemental/ipc/ipc.h>
+
+#include "convey.h"
+#include "stubs.h"
+
+static uint8_t loopback[4] = { 127, 0, 0, 1 };
+
+TestMain("Supplemental IPC", {
+ atexit(nng_fini);
+ Convey("We can create a dialer and listener", {
+ nng_ipc_dialer * d;
+ nng_ipc_listener *l;
+ So(nng_ipc_dialer_alloc(&d) == 0);
+ So(nng_ipc_listener_alloc(&l) == 0);
+ Reset({
+ nng_ipc_listener_close(l);
+ nng_ipc_dialer_close(d);
+ nng_ipc_listener_free(l);
+ nng_ipc_dialer_free(d);
+ });
+ Convey("Listener listens (wildcard)", {
+ nng_sockaddr sa;
+ uint32_t ip;
+
+ memcpy(&ip, loopback, 4);
+
+ sa.s_ipc.sa_family = NNG_AF_IPC;
+ snprintf(sa.s_ipc.sa_path, sizeof(sa.s_ipc.sa_path),
+ "%s", "/tmp/ipc_supp_test");
+
+ So(nng_ipc_listener_listen(l, &sa) == 0);
+
+ Convey("We can dial it", {
+ nng_aio *daio = NULL;
+ nng_aio *laio = NULL;
+ nng_aio *maio = NULL;
+ nng_ipc *c1 = NULL;
+ nng_ipc *c2 = NULL;
+
+ So(nng_aio_alloc(&daio, NULL, NULL) == 0);
+ So(nng_aio_alloc(&laio, NULL, NULL) == 0);
+ So(nng_aio_alloc(&maio, NULL, NULL) == 0);
+
+ Reset({
+ nng_aio_free(daio);
+ nng_aio_free(laio);
+ if (c1 != NULL) {
+ nng_ipc_close(c1);
+ nng_ipc_free(c1);
+ }
+ if (c2 != NULL) {
+ nng_ipc_close(c2);
+ nng_ipc_free(c2);
+ }
+ });
+
+ nng_ipc_dialer_dial(d, &sa, daio);
+ nng_ipc_listener_accept(l, laio);
+
+ nng_aio_wait(daio);
+ nng_aio_wait(laio);
+
+ So(nng_aio_result(daio) == 0);
+ So(nng_aio_result(laio) == 0);
+
+ c1 = nng_aio_get_output(daio, 0);
+ c2 = nng_aio_get_output(laio, 0);
+ So(c1 != NULL);
+ So(c2 != NULL);
+
+ Convey("They exchange messages", {
+ nng_aio * aio1;
+ nng_aio * aio2;
+ nng_iov iov;
+ nng_sockaddr sa2;
+ char buf1[5];
+ char buf2[5];
+
+ So(nng_aio_alloc(&aio1, NULL, NULL) ==
+ 0);
+ So(nng_aio_alloc(&aio2, NULL, NULL) ==
+ 0);
+
+ Reset({
+ nng_aio_free(aio1);
+ nng_aio_free(aio2);
+ });
+
+ // This relies on send completing for
+ // for just 5 bytes, and on recv doing
+ // the same. Technically this isn't
+ // guaranteed, but it would be weird
+ // to split such a small payload.
+ memcpy(buf1, "TEST", 5);
+ memset(buf2, 0, 5);
+ iov.iov_buf = buf1;
+ iov.iov_len = 5;
+
+ nng_aio_set_iov(aio1, 1, &iov);
+
+ iov.iov_buf = buf2;
+ iov.iov_len = 5;
+ nng_aio_set_iov(aio2, 1, &iov);
+ nng_ipc_send(c1, aio1);
+ nng_ipc_recv(c2, aio2);
+ nng_aio_wait(aio1);
+ nng_aio_wait(aio2);
+
+ So(nng_aio_result(aio1) == 0);
+ So(nng_aio_count(aio1) == 5);
+
+ So(nng_aio_result(aio2) == 0);
+ So(nng_aio_count(aio2) == 5);
+
+ So(memcmp(buf1, buf2, 5) == 0);
+
+ Convey("Socket name matches", {
+ size_t rsz = sizeof(sa2);
+ So(nng_ipc_getopt(c2,
+ NNG_OPT_LOCADDR, &sa2,
+ &rsz) == 0);
+ So(sa2.s_ipc.sa_family ==
+ NNG_AF_IPC);
+ So(strcmp(sa2.s_ipc.sa_path,
+ sa.s_ipc.sa_path) == 0);
+ });
+ });
+ });
+ });
+ });
+})