diff options
| author | Garrett D'Amore <garrett@damore.org> | 2018-12-22 19:23:26 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-12-22 19:23:26 -0800 |
| commit | ee16d11a59120cd4d981e0dcb90741fa4141372a (patch) | |
| tree | 98da4bc9601f30e2c9deb403fe213c9fb4335597 | |
| parent | d00483eadbee48b820a8a79163c5296953b6f5cb (diff) | |
| download | nng-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.h | 133 | ||||
| -rw-r--r-- | include/nng/supplemental/tcp/tcp.h | 9 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/core/platform.h | 15 | ||||
| -rw-r--r-- | src/platform/posix/posix_ipcconn.c | 93 | ||||
| -rw-r--r-- | src/platform/windows/win_impl.h | 4 | ||||
| -rw-r--r-- | src/platform/windows/win_ipc.h | 38 | ||||
| -rw-r--r-- | src/platform/windows/win_ipcconn.c | 48 | ||||
| -rw-r--r-- | src/platform/windows/win_ipcdial.c | 17 | ||||
| -rw-r--r-- | src/platform/windows/win_ipclisten.c | 13 | ||||
| -rw-r--r-- | src/platform/windows/win_sockaddr.c | 5 | ||||
| -rw-r--r-- | src/supplemental/ipc/CMakeLists.txt | 16 | ||||
| -rw-r--r-- | src/supplemental/ipc/ipc.c | 136 | ||||
| -rw-r--r-- | src/supplemental/util/options.c | 4 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/ipcsupp.c | 145 |
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); + }); + }); + }); + }); + }); +}) |
