diff options
| author | Garrett D'Amore <garrett@damore.org> | 2020-11-01 22:05:35 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2020-11-08 17:50:24 -0800 |
| commit | fc6882305f0b5e06e58a0a25740f422d133015b5 (patch) | |
| tree | 714b1fa4656253c8731a8f3f0861c24715440f95 | |
| parent | 4bf06d03f6ebead7f4e0603a2da3b1b891887878 (diff) | |
| download | nng-fc6882305f0b5e06e58a0a25740f422d133015b5.tar.gz nng-fc6882305f0b5e06e58a0a25740f422d133015b5.tar.bz2 nng-fc6882305f0b5e06e58a0a25740f422d133015b5.zip | |
fixes #1041 Abstract socket address for IPC
fixes #1326 Linux IPC could use fchmod
fixes #1327 getsockname on ipc may not work
This introduces an abstract:// style transport, which on Linux
results in using the abstract socket with the given name (not
including the leading NULL byte). A new NNG_AF_ABSTRACT is
provided. Auto bind abstract sockets are also supported.
While here we have inlined the aios for the POSIX ipc pipe
objects, eliminating at least one set of failure paths, and
have also performed various other cleanups.
A unix:// alias is available on POSIX systems, which acts just
like ipc:// (and is fact just an alias). This is supplied so
that in the future we can add support for AF_UNIX on Windows.
We've also absorbed the ipcperms test into the new ipc_test suite.
Finally we are now enforcing that IPC path names on Windows are
not over the maximum size, rather than just silently truncating
them.
29 files changed, 1754 insertions, 1016 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 77233435..d46c42b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,6 +193,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") add_definitions(-DNNG_PLATFORM_POSIX) add_definitions(-DNNG_PLATFORM_LINUX) add_definitions(-DNNG_USE_EVENTFD) + add_definitions(-DNNG_HAVE_ABSTRACT_SOCKETS) # Windows subsystem for Linux -- smells like Linux, but it has # some differences (SO_REUSEADDR for one). if (CMAKE_SYSTEM_VERSION MATCHES "Microsoft") @@ -385,6 +386,20 @@ macro(nng_test NAME) endif () endmacro() +macro(nng_test_if COND NAME) + if (${COND} AND NNG_TESTS) + add_executable(${NAME} ${NAME}.c ${ARGN}) + target_link_libraries(${NAME} ${PROJECT_NAME}_testlib) + target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/tests + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/include) + add_test(NAME ${PROJECT_NAME}.${NAME} COMMAND ${NAME} -t) + set_tests_properties(${PROJECT_NAME}.${NAME} PROPERTIES TIMEOUT 180) + endif () +endmacro() + + function(nng_sources_testlib) if (NNG_TESTS) foreach (f ${ARGN}) diff --git a/docs/man/nng_ipc.7.adoc b/docs/man/nng_ipc.7.adoc index 0d75e8d3..856a00ac 100644 --- a/docs/man/nng_ipc.7.adoc +++ b/docs/man/nng_ipc.7.adoc @@ -1,6 +1,6 @@ = nng_ipc(7) // -// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // // This document is supplied under the terms of the MIT License, a @@ -38,7 +38,9 @@ Other platforms may have different implementation strategies. This transport is generally built-in to the core, so no extra steps to use it should be necessary. -=== URI Format +=== URI Formats + +==== Traditional Names (((URI, `ipc://`))) This transport uses URIs using the scheme `ipc://`, followed by a path @@ -58,15 +60,47 @@ To ensure maximum portability and safety, absolute paths are recommended whenever possible. NOTE: If compatibility with legacy _nanomsg_ applications is required, -then pathnames must not be longer than 122 bytes, including the final +then path names must not be longer than 122 bytes, including the final `NUL` byte. This is because legacy versions of _nanomsg_ cannot express URLs longer than 128 bytes, including the `ipc://` prefix. +==== UNIX Aliases + +(((URI, `unix://`))) +The `unix://` scheme is an alias for `ipc://` and can be used inter-changeably, but only +on POSIX systems. + +The purpose of this scheme is to support a future transport making use of `AF_UNIX` +on Windows systems, at which time it will be necessary to discriminate between +the Named Pipes and the `AF_UNIX` based transports. + +==== Abstract Names + +(((URI, `abstract://`))) +On Linux, this transport also can support abstract sockets. +Abstract sockets use a URI-encoded name after the scheme, which allows arbitrary values to be conveyed +in the path, including embedded `NUL` bytes. +For example, the name `"a\0b"` would be represented as `abstract://a%00b`. + +TIP: An empty name may be used with a listener to request "`auto bind`" be used to select a name. +In this case the system will allocate a free name. +The name assigned may be retrieved using `NNG_OPT_LOCADDR`. + +NOTE: _NNG_ cannot represent an abstract socket with the empty name. + +NOTE: Abstract names do not include the leading `NUL` byte used in the low-level socket address. + +Abstract sockets do not have any representation in the file system, and are automatically freed by +the system when no longer in use. +Abstract sockets ignore socket permissions, but it is still possible to determine the credentials +of the peer with `NNG_OPT_PEER_UID`, etc. + === Socket Address When using an xref:nng_sockaddr.5.adoc[`nng_sockaddr`] structure, -the actual structure is of type xref:nng_sockaddr_ipc.5.adoc[`nng_sockaddr_ipc`]. +the actual structure is of type xref:nng_sockaddr_ipc.5.adoc[`nng_sockaddr_ipc`], +except for abstract sockets, which use xref:nng_sockaddr_abstract.5.adoc[`nng_sockaddr_abstract`]. === Transport Options diff --git a/docs/man/nng_sockaddr.5.adoc b/docs/man/nng_sockaddr.5.adoc index cf347f66..cd10cab4 100644 --- a/docs/man/nng_sockaddr.5.adoc +++ b/docs/man/nng_sockaddr.5.adoc @@ -20,21 +20,23 @@ nng_sockaddr - socket address #include <nng/nng.h> typedef union nng_sockaddr { - uint16_t s_family; - nng_sockaddr_ipc s_ipc; - nng_sockaddr_inproc s_inproc; - nng_sockaddr_in s_in; - nng_sockaddr_in6 s_in6; - nng_sockaddr_zt s_zt; + uint16_t s_family; + nng_sockaddr_ipc s_ipc; + nng_sockaddr_inproc s_inproc; + nng_sockaddr_in s_in; + nng_sockaddr_in6 s_in6; + nng_sockaddr_zt s_zt; + nng_sockaddr_abstract s_abstract; } nng_sockaddr; enum sockaddr_family { - NNG_AF_UNSPEC = 0, - NNG_AF_INPROC = 1, - NNG_AF_IPC = 2, - NNG_AF_INET = 3, - NNG_AF_INET6 = 4, - NNG_AF_ZT = 5, + NNG_AF_UNSPEC = 0, + NNG_AF_INPROC = 1, + NNG_AF_IPC = 2, + NNG_AF_INET = 3, + NNG_AF_INET6 = 4, + NNG_AF_ZT = 5, + NNG_AF_ABSTRACT = 6 }; ---- @@ -70,12 +72,14 @@ The values of `s_family` are as follows: `NNG_AF_INET`:: Address for TCP/IP (v4) communication. The `s_in` member is valid. `NNG_AF_INET6`:: Address for TCP/IP (v6) communication. The `s_in6` member is valid. `NNG_AF_ZT`:: Address for ZeroTier transport (xref:nng_zerotier.7.adoc[nng_zerotier(7)]). The `s_zt` member is valid. +`NNG_AF_ABSTRACT`:: Address for an abstract UNIX domain socket. The `s_abstract` member is valid. Please see the manual pages for each individual type for more information. == SEE ALSO [.text-left] +xref:nng_sockaddr_abstract.5.adoc[nng_sockaddr_abstract(5)], xref:nng_sockaddr_in.5.adoc[nng_sockaddr_in(5)], xref:nng_sockaddr_in6.5.adoc[nng_sockaddr_in6(5)], xref:nng_sockaddr_inproc.5.adoc[nng_sockaddr_inproc(5)], diff --git a/docs/man/nng_sockaddr_abstract.5.adoc b/docs/man/nng_sockaddr_abstract.5.adoc new file mode 100644 index 00000000..ccdbb6c5 --- /dev/null +++ b/docs/man/nng_sockaddr_abstract.5.adoc @@ -0,0 +1,77 @@ += nng_sockaddr_abstract(5) +// +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> +// +// This document 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. +// + +== NAME + +nng_sockaddr_abstract - abstract IPC socket address + +== SYNOPSIS + +[source,c] +---- +#include <nng/nng.h> + +enum sockaddr_family { + NNG_AF_ABSTRACT = 6, +}; + +typedef struct { + uint16_t sa_family; + uint16_t sa_len; + char sa_name[107]; +} nng_sockaddr_abstract; +---- + +== DESCRIPTION + +(((socket, address, abstract))) An `nng_sockaddr_abstract` is the flavor of xref:nng_sockaddr.5.adoc[`nng_sockaddr`] +used to represent addresses associated with IPC communication using "`abstract`" sockets. + +Abstract sockets are only supported on Linux at present. +These sockets have a name that is simply an array of bytes, with no special meaning. +Abstract sockets also have no presence in the file system, do not honor any permissions, and are automatically +cleaned up by the operating system when no longer in use. + +NOTE: Support for abstract sockets is a new feature in _NNG_, and it is only available on Linux. +As such their use is not recommended for portable applications. + +The following structure members are present: + +`sa_family`:: +This field will always have the value ((`NNG_AF_ABSTRACT`)). + +`sa_len`:: +This field provides the name of the length stored in `sa_name`. + +`sa_name`:: +This field holds the name of the abstract socket. +The bytes of name can have any value, including zero. + +NOTE: The name does _not_ include the leading `NUL` byte used on Linux to discriminate between +abstract and path name sockets. + +=== Auto Bind + +An empty value (`sa_len` equal to zero) can be used on listening sockets to request the +system allocate a name. +On Linux systems, this will result in a name with either 5 or 8 hexadecimal ASCII characters. +The name chosen can be obtained by retrieving the `NNG_OPT_LOCADDR` option on the listening entity. + +NOTE: Because a zero length name is used to indicate auto bind be used, it is impossible to specify +an actual empty name. This name are not supported by _NNG_, although the underlying system can support it. +The use of that name is inadvisable anyway. + + +== SEE ALSO + +[.text-left] +xref:nng_sockaddr.5.adoc[nng_sockaddr(5)], +xref:nng_ipc.7.adoc[nng_ipc(7)] +xref:nng.7.adoc[nng(7)] diff --git a/include/nng/nng.h b/include/nng/nng.h index fe979d10..dd7aed35 100644 --- a/include/nng/nng.h +++ b/include/nng/nng.h @@ -115,14 +115,11 @@ struct nng_sockaddr_inproc { uint16_t sa_family; char sa_name[NNG_MAXADDRLEN]; }; -typedef struct nng_sockaddr_inproc nng_sockaddr_inproc; struct nng_sockaddr_path { uint16_t sa_family; char sa_path[NNG_MAXADDRLEN]; }; -typedef struct nng_sockaddr_path nng_sockaddr_path; -typedef struct nng_sockaddr_path nng_sockaddr_ipc; struct nng_sockaddr_in6 { uint16_t sa_family; @@ -130,10 +127,6 @@ struct nng_sockaddr_in6 { uint8_t sa_addr[16]; uint32_t sa_scope; }; -typedef struct nng_sockaddr_in6 nng_sockaddr_in6; -typedef struct nng_sockaddr_in6 nng_sockaddr_udp6; -typedef struct nng_sockaddr_in6 nng_sockaddr_tcp6; - struct nng_sockaddr_in { uint16_t sa_family; uint16_t sa_port; @@ -147,27 +140,48 @@ struct nng_sockaddr_zt { uint32_t sa_port; }; -typedef struct nng_sockaddr_in nng_sockaddr_in; -typedef struct nng_sockaddr_in nng_sockaddr_udp; -typedef struct nng_sockaddr_in nng_sockaddr_tcp; -typedef struct nng_sockaddr_zt nng_sockaddr_zt; +struct nng_sockaddr_abstract { + uint16_t sa_family; + uint16_t sa_len; // will be 0 - 107 max. + uint8_t sa_name[107]; // 108 linux/windows, without leading NUL +}; + +// nng_sockaddr_storage is the the size required to store any nng_sockaddr. +// This size must not change, and no individual nng_sockaddr type may grow +// larger than this without breaking binary compatibility. +struct nng_sockaddr_storage { + uint16_t sa_family; + uint16_t sa_pad[64]; +}; + +typedef struct nng_sockaddr_inproc nng_sockaddr_inproc; +typedef struct nng_sockaddr_path nng_sockaddr_path; +typedef struct nng_sockaddr_path nng_sockaddr_ipc; +typedef struct nng_sockaddr_in nng_sockaddr_in; +typedef struct nng_sockaddr_in6 nng_sockaddr_in6; +typedef struct nng_sockaddr_zt nng_sockaddr_zt; +typedef struct nng_sockaddr_abstract nng_sockaddr_abstract; +typedef struct nng_sockaddr_storage nng_sockaddr_storage; typedef union nng_sockaddr { - uint16_t s_family; - nng_sockaddr_ipc s_ipc; - nng_sockaddr_inproc s_inproc; - nng_sockaddr_in6 s_in6; - nng_sockaddr_in s_in; - nng_sockaddr_zt s_zt; + uint16_t s_family; + nng_sockaddr_ipc s_ipc; + nng_sockaddr_inproc s_inproc; + nng_sockaddr_in6 s_in6; + nng_sockaddr_in s_in; + nng_sockaddr_zt s_zt; + nng_sockaddr_abstract s_abstract; + nng_sockaddr_storage s_storage; } nng_sockaddr; enum nng_sockaddr_family { - NNG_AF_UNSPEC = 0, - NNG_AF_INPROC = 1, - NNG_AF_IPC = 2, - NNG_AF_INET = 3, - NNG_AF_INET6 = 4, - NNG_AF_ZT = 5 // ZeroTier + NNG_AF_UNSPEC = 0, + NNG_AF_INPROC = 1, + NNG_AF_IPC = 2, + NNG_AF_INET = 3, + NNG_AF_INET6 = 4, + NNG_AF_ZT = 5, // ZeroTier + NNG_AF_ABSTRACT = 6 }; // Scatter/gather I/O. diff --git a/src/core/stream.c b/src/core/stream.c index 2112f5ef..9309a3a0 100644 --- a/src/core/stream.c +++ b/src/core/stream.c @@ -32,7 +32,23 @@ static struct { .listener_alloc = nni_ipc_listener_alloc, .checkopt = nni_ipc_checkopt, }, +#ifdef NNG_PLATFORM_POSIX { + .scheme = "unix", + .dialer_alloc = nni_ipc_dialer_alloc, + .listener_alloc = nni_ipc_listener_alloc, + .checkopt = nni_ipc_checkopt, + }, +#endif +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + { + .scheme = "abstract", + .dialer_alloc = nni_ipc_dialer_alloc, + .listener_alloc = nni_ipc_listener_alloc, + .checkopt = nni_ipc_checkopt, + }, +#endif + { .scheme = "tcp", .dialer_alloc = nni_tcp_dialer_alloc, .listener_alloc = nni_tcp_listener_alloc, diff --git a/src/core/url.c b/src/core/url.c index 2f1e3a78..f0cd16ad 100644 --- a/src/core/url.c +++ b/src/core/url.c @@ -1,5 +1,5 @@ // -// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2020 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 @@ -17,8 +17,8 @@ #include "url.h" -static char -url_hexval(char c) +static uint8_t +url_hex_val(char c) { if ((c >= '0') && (c <= '9')) { return (c - '0'); @@ -44,21 +44,21 @@ url_utf8_validate(void *arg) int nb; while (*s) { - if ((s[0] & 0x80) == 0) { + if ((s[0] & 0x80u) == 0) { s++; continue; } - if ((s[0] & 0xe0) == 0xc0) { + if ((s[0] & 0xe0u) == 0xc0) { // 0x80 thru 0x7ff - v = (s[0] & 0x1f); + v = (s[0] & 0x1fu); minv = 0x80; nb = 1; - } else if ((s[0] & 0xf0) == 0xe0) { - v = (s[0] & 0xf); + } else if ((s[0] & 0xf0u) == 0xe0) { + v = (s[0] & 0xfu); minv = 0x800; nb = 2; - } else if ((s[0] & 0xf8) == 0xf0) { - v = (s[0] & 0x7); + } else if ((s[0] & 0xf8u) == 0xf0) { + v = (s[0] & 0x7u); minv = 0x10000; nb = 3; } else { @@ -68,12 +68,12 @@ url_utf8_validate(void *arg) } s++; for (int i = 0; i < nb; i++) { - if ((s[0] & 0xc0) != 0x80) { + if ((s[0] & 0xc0u) != 0x80) { return (NNG_EINVAL); // not continuation } s++; - v <<= 6; - v += s[0] & 0x3f; + v <<= 6u; + v += s[0] & 0x3fu; } if (v < minv) { return (NNG_EINVAL); @@ -88,14 +88,42 @@ url_utf8_validate(void *arg) return (0); } +size_t +nni_url_decode(uint8_t *out, const char *in, size_t max_len) +{ + size_t len; + uint8_t c; + + len = 0; + while ((c = (uint8_t) *in) != '\0') { + if (len >= max_len) { + return ((size_t) -1); + } + if (c == '%') { + in++; + if ((!isxdigit(in[0])) || (!isxdigit(in[1]))) { + return ((size_t) -1); + } + out[len] = url_hex_val(*in++); + out[len] <<= 4u; + out[len] += url_hex_val(*in++); + len++; + } else { + out[len++] = c; + in++; + } + } + return (len); +} + static int url_canonify_uri(char **outp, const char *in) { - char * out; - size_t src, dst, len; - int c; - int rv; - bool skip; + char * out; + size_t src, dst, len; + uint8_t c; + int rv; + bool skip; // We know that the transform is strictly "reducing". if ((out = nni_strdup(in)) == NULL) { @@ -112,9 +140,9 @@ url_canonify_uri(char **outp, const char *in) nni_free(out, len); return (NNG_EINVAL); } - c = url_hexval(out[src + 1]); + c = url_hex_val(out[src + 1]); c *= 16; - c += url_hexval(out[src + 2]); + c += url_hex_val(out[src + 2]); // If it's a safe character, decode, otherwise leave // it alone. We also decode valid high-bytes for // UTF-8, which will let us validate them and use @@ -127,8 +155,8 @@ url_canonify_uri(char **outp, const char *in) out[dst++] = (char) c; } else { out[dst++] = '%'; - out[dst++] = (char) toupper(out[src + 1]); - out[dst++] = (char) toupper(out[src + 2]); + out[dst++] = toupper((uint8_t) out[src + 1]); + out[dst++] = toupper((uint8_t) out[src + 2]); } src += 3; continue; @@ -311,9 +339,11 @@ nni_url_parse(nni_url **urlp, const char *raw) // path names are not canonicalized, which means that the address and // URL properties for relative paths won't be portable to other // processes unless they are in the same directory. When in doubt, - // we recommend using absolute paths, such as ipc:///var/run/mysocket. + // we recommend using absolute paths, such as ipc:///var/run/socket. if ((strcmp(url->u_scheme, "ipc") == 0) || + (strcmp(url->u_scheme, "unix") == 0) || + (strcmp(url->u_scheme, "abstract") == 0) || (strcmp(url->u_scheme, "inproc") == 0)) { if ((url->u_path = nni_strdup(s)) == NULL) { rv = NNG_ENOMEM; @@ -388,7 +418,6 @@ nni_url_parse(nni_url **urlp, const char *raw) url->u_path[len] = '\0'; s += len; - len = 0; // Look for query info portion. if (s[0] == '?') { @@ -500,7 +529,10 @@ nni_url_asprintf(char **str, const nni_url *url) const char *hostob = ""; const char *hostcb = ""; - if ((strcmp(scheme, "ipc") == 0) || (strcmp(scheme, "inproc") == 0)) { + if ((strcmp(scheme, "ipc") == 0) || (strcmp(scheme, "inproc") == 0) || + (strcmp(scheme, "unix") == 0) || + (strcmp(scheme, "ipc+abstract") == 0) || + (strcmp(scheme, "unix+abstract") == 0)) { return (nni_asprintf(str, "%s://%s", scheme, url->u_path)); } diff --git a/src/core/url.h b/src/core/url.h index afb8a882..e846bf37 100644 --- a/src/core/url.h +++ b/src/core/url.h @@ -1,5 +1,5 @@ // -// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2020 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 @@ -19,5 +19,6 @@ extern int nni_url_clone(nni_url **, const nni_url *); extern const char *nni_url_default_port(const char *); extern int nni_url_asprintf(char **, const nni_url *); extern int nni_url_asprintf_port(char **, const nni_url *, int); +extern size_t nni_url_decode(uint8_t *, const char *, size_t); #endif // CORE_URL_H diff --git a/src/platform/posix/posix_impl.h b/src/platform/posix/posix_impl.h index 2a6fd6a1..978afc01 100644 --- a/src/platform/posix/posix_impl.h +++ b/src/platform/posix/posix_impl.h @@ -37,7 +37,7 @@ #ifdef NNG_PLATFORM_POSIX_SOCKADDR #include <sys/socket.h> -extern int nni_posix_sockaddr2nn(nni_sockaddr *, const void *); +extern int nni_posix_sockaddr2nn(nni_sockaddr *, const void *, size_t); extern size_t nni_posix_nn2sockaddr(void *, const nni_sockaddr *); #endif diff --git a/src/platform/posix/posix_ipc.h b/src/platform/posix/posix_ipc.h index 894a5e96..2c2c9af7 100644 --- a/src/platform/posix/posix_ipc.h +++ b/src/platform/posix/posix_ipc.h @@ -29,6 +29,7 @@ struct nni_ipc_conn { nni_mtx mtx; nni_aio * dial_aio; nni_ipc_dialer *dialer; + nng_sockaddr sa; nni_reap_item reap; }; @@ -42,7 +43,8 @@ struct nni_ipc_dialer { nni_atomic_bool fini; }; -extern int nni_posix_ipc_alloc(nni_ipc_conn **, nni_ipc_dialer *); +extern int nni_posix_ipc_alloc( + nni_ipc_conn **, nni_sockaddr *, nni_ipc_dialer *); extern void nni_posix_ipc_init(nni_ipc_conn *, nni_posix_pfd *); extern void nni_posix_ipc_start(nni_ipc_conn *); extern void nni_posix_ipc_dialer_rele(nni_ipc_dialer *); diff --git a/src/platform/posix/posix_ipcconn.c b/src/platform/posix/posix_ipcconn.c index c5760e53..cbf2610e 100644 --- a/src/platform/posix/posix_ipcconn.c +++ b/src/platform/posix/posix_ipcconn.c @@ -385,7 +385,7 @@ ipc_peerid(ipc_conn *c, uint64_t *euid, uint64_t *egid, uint64_t *prid, *prid = (uint64_t) pid; } } -#endif // NNG_HAVE_LOCALPEERPID +#endif // NNG_HAVE_LOCALPEERPID return (0); #else if (fd < 0) { @@ -466,20 +466,8 @@ ipc_get_peer_pid(void *arg, void *buf, size_t *szp, nni_type t) static int ipc_get_addr(void *arg, void *buf, size_t *szp, nni_type t) { - ipc_conn * c = arg; - nni_sockaddr sa; - struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); - int fd = nni_posix_pfd_fd(c->pfd); - int rv; - - if (getsockname(fd, (void *) &ss, &sslen) != 0) { - return (nni_plat_errno(errno)); - } - if ((rv = nni_posix_sockaddr2nn(&sa, &ss)) != 0) { - return (rv); - } - return (nni_copyout_sockaddr(&sa, buf, szp, t)); + ipc_conn *c = arg; + return (nni_copyout_sockaddr(&c->sa, buf, szp, t)); } void @@ -557,7 +545,7 @@ ipc_setx(void *arg, const char *name, const void *val, size_t sz, nni_type t) } int -nni_posix_ipc_alloc(nni_ipc_conn **cp, nni_ipc_dialer *d) +nni_posix_ipc_alloc(nni_ipc_conn **cp, nni_sockaddr *sa, nni_ipc_dialer *d) { ipc_conn *c; @@ -573,6 +561,7 @@ nni_posix_ipc_alloc(nni_ipc_conn **cp, nni_ipc_dialer *d) c->stream.s_recv = ipc_recv; c->stream.s_getx = ipc_getx; c->stream.s_setx = ipc_setx; + c->sa = *sa; nni_mtx_init(&c->mtx); nni_aio_list_init(&c->readq); diff --git a/src/platform/posix/posix_ipcdial.c b/src/platform/posix/posix_ipcdial.c index 65061767..b25f7340 100644 --- a/src/platform/posix/posix_ipcdial.c +++ b/src/platform/posix/posix_ipcdial.c @@ -178,7 +178,7 @@ ipc_dialer_dial(void *arg, nni_aio *aio) nni_atomic_inc64(&d->ref); - if ((rv = nni_posix_ipc_alloc(&c, d)) != 0) { + if ((rv = nni_posix_ipc_alloc(&c, &d->sa, d)) != 0) { (void) close(fd); nni_posix_ipc_dialer_rele(d); nni_aio_finish_error(aio, rv); @@ -263,20 +263,46 @@ int nni_ipc_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) { ipc_dialer *d; + size_t len; - if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || - (strlen(url->u_path) == 0) || - (strlen(url->u_path) >= NNG_MAXADDRLEN)) { - return (NNG_EADDRINVAL); - } if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { return (NNG_ENOMEM); } + + if ((strcmp(url->u_scheme, "ipc") == 0) || + (strcmp(url->u_scheme, "unix") == 0)) { + if ((url->u_path == NULL) || + ((len = strlen(url->u_path)) == 0) || + (len > NNG_MAXADDRLEN)) { + NNI_FREE_STRUCT(d); + return (NNG_EADDRINVAL); + } + d->sa.s_ipc.sa_family = NNG_AF_IPC; + nni_strlcpy(d->sa.s_ipc.sa_path, url->u_path, NNG_MAXADDRLEN); + +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + } else if (strcmp(url->u_scheme, "abstract") == 0) { + + // path is url encoded. + len = nni_url_decode(d->sa.s_abstract.sa_name, url->u_path, + sizeof(d->sa.s_abstract.sa_name)); + if (len == (size_t) -1) { + NNI_FREE_STRUCT(d); + return (NNG_EADDRINVAL); + } + + d->sa.s_abstract.sa_family = NNG_AF_ABSTRACT; + d->sa.s_abstract.sa_len = len; +#endif + + } else { + NNI_FREE_STRUCT(d); + return (NNG_EADDRINVAL); + } + nni_mtx_init(&d->mtx); nni_aio_list_init(&d->connq); - d->closed = false; - d->sa.s_ipc.sa_family = NNG_AF_IPC; - strcpy(d->sa.s_ipc.sa_path, url->u_path); + d->closed = false; d->sd.sd_free = ipc_dialer_free; d->sd.sd_close = ipc_dialer_close; d->sd.sd_dial = ipc_dialer_dial; diff --git a/src/platform/posix/posix_ipclisten.c b/src/platform/posix/posix_ipclisten.c index 43febcfb..9c0b55db 100644 --- a/src/platform/posix/posix_ipclisten.c +++ b/src/platform/posix/posix_ipclisten.c @@ -124,7 +124,7 @@ ipc_listener_doaccept(ipc_listener *l) } } - if ((rv = nni_posix_ipc_alloc(&c, NULL)) != 0) { + if ((rv = nni_posix_ipc_alloc(&c, &l->sa, NULL)) != 0) { (void) close(newfd); nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); @@ -188,8 +188,12 @@ ipc_remove_stale(const char *path) struct sockaddr_un sa; size_t sz; + if (path == NULL) { + return (0); + } + sa.sun_family = AF_UNIX; - sz = sizeof(sa.sun_path); + sz = sizeof(sa.sun_path); if (nni_strlcpy(sa.sun_path, path, sz) >= sz) { return (NNG_EADDRINVAL); @@ -231,6 +235,11 @@ ipc_listener_set_perms(void *arg, const void *buf, size_t sz, nni_type t) if ((rv = nni_copyin_int(&mode, buf, sz, 0, S_IFMT, t)) != 0) { return (rv); } + if (l->sa.s_family == NNG_AF_ABSTRACT) { + // We ignore permissions on abstract sockets. + // They succeed, but have no effect. + return (0); + } if ((mode & S_IFMT) != 0) { return (NNG_EINVAL); } @@ -275,6 +284,23 @@ ipc_listener_setx( return (nni_setopt(ipc_listener_options, name, l, buf, sz, t)); } +#ifndef NNG_PLATFORM_LINUX +static int +ipc_listener_chmod(ipc_listener *l, const char *path) +{ + if (path == NULL) { + return (0); + } + if (l->perms == 0) { + return (0); + } + if (chmod(path, l->perms & ~S_IFMT) != 0) { + return (-1); + } + return (0); +} +#endif + int ipc_listener_listen(void *arg) { @@ -286,8 +312,7 @@ ipc_listener_listen(void *arg) nni_posix_pfd * pfd; char * path; - if (((len = nni_posix_nn2sockaddr(&ss, &l->sa)) == 0) || - (ss.ss_family != AF_UNIX)) { + if ((len = nni_posix_nn2sockaddr(&ss, &l->sa)) < sizeof(sa_family_t)) { return (NNG_EADDRINVAL); } @@ -300,9 +325,20 @@ ipc_listener_listen(void *arg) nni_mtx_unlock(&l->mtx); return (NNG_ECLOSED); } - path = nni_strdup(l->sa.s_ipc.sa_path); - if (path == NULL) { - return (NNG_ENOMEM); + + switch (l->sa.s_family) { + case NNG_AF_IPC: + if ((path = nni_strdup(l->sa.s_ipc.sa_path)) == NULL) { + nni_mtx_unlock(&l->mtx); + return (NNG_ENOMEM); + } + break; + case NNG_AF_ABSTRACT: + path = NULL; + break; + default: + nni_mtx_unlock(&l->mtx); + return (NNG_EADDRINVAL); } if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0) { @@ -311,37 +347,69 @@ ipc_listener_listen(void *arg) nni_strfree(path); return (rv); } - - if ((rv = nni_posix_pfd_init(&pfd, fd)) != 0) { - nni_mtx_unlock(&l->mtx); - nni_strfree(path); - (void) close(fd); - return (rv); + // Linux supports fchmod on a socket, which will + // be race condition free. +#ifdef NNG_PLATFORM_LINUX + if ((l->perms != 0) && (path != NULL)) { + if (fchmod(fd, l->perms & ~S_IFMT) != 0) { + rv = nni_plat_errno(errno); + nni_mtx_unlock(&l->mtx); + (void) close(fd); + nni_strfree(path); + return (rv); + } } +#endif if ((rv = bind(fd, (struct sockaddr *) &ss, len)) != 0) { - if ((errno == EEXIST) || (errno == EADDRINUSE)) { + if ((l->sa.s_family == NNG_AF_IPC) && + ((errno == EEXIST) || (errno == EADDRINUSE))) { ipc_remove_stale(path); rv = bind(fd, (struct sockaddr *) &ss, len); } + if (rv != 0) { + nni_strfree(path); + path = NULL; + } } - if (rv != 0) { + if ((rv != 0) || +#ifndef NNG_PLATFORM_LINUX + // Linux uses fchmod instead (which is race free). + (ipc_listener_chmod(l, path) != 0) || +#endif + (listen(fd, 128) != 0)) { rv = nni_plat_errno(errno); + } + if ((rv != 0) || ((rv = nni_posix_pfd_init(&pfd, fd)) != 0)) { nni_mtx_unlock(&l->mtx); + (void) close(fd); + if (path != NULL) { + unlink(path); + } nni_strfree(path); - nni_posix_pfd_fini(pfd); return (rv); } - if (((l->perms != 0) && (chmod(path, l->perms & ~S_IFMT) != 0)) || - (listen(fd, 128) != 0)) { - rv = nni_plat_errno(errno); - (void) unlink(path); - nni_mtx_unlock(&l->mtx); - nni_strfree(path); - nni_posix_pfd_fini(pfd); - return (rv); +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + // If the original address was for a system assigned value, + // then figure out what we got. This is analogous to TCP + // binding to port 0. + if ((l->sa.s_family == NNG_AF_ABSTRACT) && + (l->sa.s_abstract.sa_len == 0)) { + struct sockaddr_un *su = (void *) &ss; + len = sizeof(ss); + if ((getsockname(fd, (struct sockaddr *) &ss, &len) == 0) && + (len > sizeof(sa_family_t)) && + (len <= sizeof(l->sa.s_abstract.sa_name)) && + (su->sun_path[0] == '\0')) { + len -= sizeof(sa_family_t); + len--; // don't count the leading NUL. + l->sa.s_abstract.sa_len = len; + memcpy( + l->sa.s_abstract.sa_name, &su->sun_path[1], len); + } } +#endif nni_posix_pfd_set_cb(pfd, ipc_listener_cb, l); @@ -412,26 +480,49 @@ int nni_ipc_listener_alloc(nng_stream_listener **lp, const nng_url *url) { ipc_listener *l; - - if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || - (strlen(url->u_path) == 0) || - (strlen(url->u_path) >= NNG_MAXADDRLEN)) { - return (NNG_EADDRINVAL); - } + size_t len; if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { return (NNG_ENOMEM); } + if ((strcmp(url->u_scheme, "ipc") == 0) || + (strcmp(url->u_scheme, "unix") == 0)) { + if ((url->u_path == NULL) || + ((len = strlen(url->u_path)) == 0) || + (len > NNG_MAXADDRLEN)) { + NNI_FREE_STRUCT(l); + return (NNG_EADDRINVAL); + } + l->sa.s_ipc.sa_family = NNG_AF_IPC; + nni_strlcpy(l->sa.s_ipc.sa_path, url->u_path, NNG_MAXADDRLEN); + +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + } else if (strcmp(url->u_scheme, "abstract") == 0) { + // path is url encoded. + len = nni_url_decode(l->sa.s_abstract.sa_name, url->u_path, + sizeof(l->sa.s_abstract.sa_name)); + if (len == (size_t) -1) { + NNI_FREE_STRUCT(l); + return (NNG_EADDRINVAL); + } + + l->sa.s_abstract.sa_family = NNG_AF_ABSTRACT; + l->sa.s_abstract.sa_len = len; +#endif + + } else { + NNI_FREE_STRUCT(l); + return (NNG_EADDRINVAL); + } + nni_mtx_init(&l->mtx); nni_aio_list_init(&l->acceptq); - l->pfd = NULL; - l->closed = false; - l->started = false; - l->perms = 0; - l->sa.s_ipc.sa_family = NNG_AF_IPC; - strcpy(l->sa.s_ipc.sa_path, url->u_path); + l->pfd = NULL; + l->closed = false; + l->started = false; + l->perms = 0; l->sl.sl_free = ipc_listener_free; l->sl.sl_close = ipc_listener_close; l->sl.sl_listen = ipc_listener_listen; diff --git a/src/platform/posix/posix_resolv_gai.c b/src/platform/posix/posix_resolv_gai.c index 888cc9b7..36a0924e 100644 --- a/src/platform/posix/posix_resolv_gai.c +++ b/src/platform/posix/posix_resolv_gai.c @@ -419,7 +419,8 @@ parse_ip(const char *addr, nng_sockaddr *sa, bool want_port) rv = nni_plat_errno(rv); goto done; } - nni_posix_sockaddr2nn(sa, (void *) results->ai_addr); + nni_posix_sockaddr2nn( + sa, (void *) results->ai_addr, results->ai_addrlen); freeaddrinfo(results); done: diff --git a/src/platform/posix/posix_sockaddr.c b/src/platform/posix/posix_sockaddr.c index dc795cfe..a569132c 100644 --- a/src/platform/posix/posix_sockaddr.c +++ b/src/platform/posix/posix_sockaddr.c @@ -25,13 +25,14 @@ size_t nni_posix_nn2sockaddr(void *sa, const nni_sockaddr *na) { - struct sockaddr_in * sin; - struct sockaddr_in6 * sin6; - struct sockaddr_un * spath; - const nng_sockaddr_in * nsin; - const nng_sockaddr_in6 * nsin6; - const nng_sockaddr_path *nspath; - size_t sz; + struct sockaddr_in * sin; + struct sockaddr_in6 * sin6; + struct sockaddr_un * spath; + const nng_sockaddr_in * nsin; + const nng_sockaddr_in6 * nsin6; + const nng_sockaddr_path * nspath; + const nng_sockaddr_abstract *nsabs; + size_t sz; if ((sa == NULL) || (na == NULL)) { return (0); @@ -70,12 +71,33 @@ nni_posix_nn2sockaddr(void *sa, const nni_sockaddr *na) } spath->sun_family = PF_UNIX; return (sizeof(*spath)); + + case NNG_AF_ABSTRACT: + spath = (void *) sa; + nsabs = &na->s_abstract; + if (nsabs->sa_len >= sizeof (spath->sun_path)) { + return (0); + } + memset(spath, 0, sizeof(*spath)); + spath->sun_family = PF_UNIX; + spath->sun_path[0] = '\0'; // abstract starts with nul + + // We support auto-bind with an empty string. There is + // a subtle caveat here, which is that we cannot bind to + // the *empty* name. + if (nsabs->sa_len == 0) { + return (sizeof (sa_family_t)); // auto bind + } else { + memcpy(&spath->sun_path[1], nsabs->sa_name, + nsabs->sa_len); + return (sizeof(sa_family_t) + 1 + nsabs->sa_len); + } } return (0); } int -nni_posix_sockaddr2nn(nni_sockaddr *na, const void *sa) +nni_posix_sockaddr2nn(nni_sockaddr *na, const void *sa, size_t sz) { const struct sockaddr_in * sin; const struct sockaddr_in6 *sin6; @@ -83,12 +105,16 @@ nni_posix_sockaddr2nn(nni_sockaddr *na, const void *sa) nng_sockaddr_in * nsin; nng_sockaddr_in6 * nsin6; nng_sockaddr_path * nspath; + nng_sockaddr_abstract * nsabs; if ((na == NULL) || (sa == NULL)) { return (-1); } switch (((struct sockaddr *) sa)->sa_family) { case AF_INET: + if (sz < sizeof(*sin)) { + return (-1); + } sin = (void *) sa; nsin = &na->s_in; nsin->sa_family = NNG_AF_INET; @@ -96,6 +122,9 @@ nni_posix_sockaddr2nn(nni_sockaddr *na, const void *sa) nsin->sa_addr = sin->sin_addr.s_addr; break; case AF_INET6: + if (sz < sizeof(*sin6)) { + return (-1); + } sin6 = (void *) sa; nsin6 = &na->s_in6; nsin6->sa_family = NNG_AF_INET6; @@ -104,11 +133,31 @@ nni_posix_sockaddr2nn(nni_sockaddr *na, const void *sa) memcpy(nsin6->sa_addr, sin6->sin6_addr.s6_addr, 16); break; case AF_UNIX: - spath = (void *) sa; - nspath = &na->s_ipc; - nspath->sa_family = NNG_AF_IPC; - (void) snprintf(nspath->sa_path, sizeof(nspath->sa_path), "%s", - spath->sun_path); + // AF_UNIX can be NNG_AF_IPC, or NNG_AF_ABSTRACT. + spath = (void *) sa; + if ((sz < sizeof(sa_family_t)) || (sz > sizeof(*spath))) { + return (-1); + } + // Now we need to look more closely. + sz -= sizeof(sa_family_t); + if (sz == 0) { + // Unnamed socket. These will be treated using + // auto-bind if we actually listen to them, and + // it is impossible to connect them. + nsabs = &na->s_abstract; + nsabs->sa_family = NNG_AF_ABSTRACT; + nsabs->sa_len = 0; + } else if (spath->sun_path[0] == 0) { + nsabs = &na->s_abstract; + nsabs->sa_family = NNG_AF_ABSTRACT; + nsabs->sa_len = sz - 1; + memcpy(nsabs->sa_name, &spath->sun_path[1], sz - 1); + } else { + nspath = &na->s_ipc; + nspath->sa_family = NNG_AF_IPC; + nni_strlcpy(nspath->sa_path, spath->sun_path, + sizeof(nspath->sa_path)); + } break; default: // We should never see this - the OS should always be diff --git a/src/platform/posix/posix_tcpconn.c b/src/platform/posix/posix_tcpconn.c index 2a209984..3c8c3715 100644 --- a/src/platform/posix/posix_tcpconn.c +++ b/src/platform/posix/posix_tcpconn.c @@ -335,15 +335,15 @@ tcp_get_peername(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn * c = arg; struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); + socklen_t len = sizeof(ss); int fd = nni_posix_pfd_fd(c->pfd); int rv; nng_sockaddr sa; - if (getpeername(fd, (void *) &ss, &sslen) != 0) { + if (getpeername(fd, (void *) &ss, &len) != 0) { return (nni_plat_errno(errno)); } - if ((rv = nni_posix_sockaddr2nn(&sa, &ss)) == 0) { + if ((rv = nni_posix_sockaddr2nn(&sa, &ss, len)) == 0) { rv = nni_copyout_sockaddr(&sa, buf, szp, t); } return (rv); @@ -354,15 +354,15 @@ tcp_get_sockname(void *arg, void *buf, size_t *szp, nni_type t) { nni_tcp_conn * c = arg; struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); + socklen_t len = sizeof(ss); int fd = nni_posix_pfd_fd(c->pfd); int rv; nng_sockaddr sa; - if (getsockname(fd, (void *) &ss, &sslen) != 0) { + if (getsockname(fd, (void *) &ss, &len) != 0) { return (nni_plat_errno(errno)); } - if ((rv = nni_posix_sockaddr2nn(&sa, &ss)) == 0) { + if ((rv = nni_posix_sockaddr2nn(&sa, &ss, len)) == 0) { rv = nni_copyout_sockaddr(&sa, buf, szp, t); } return (rv); diff --git a/src/platform/posix/posix_tcpdial.c b/src/platform/posix/posix_tcpdial.c index 9b3a91f5..767717af 100644 --- a/src/platform/posix/posix_tcpdial.c +++ b/src/platform/posix/posix_tcpdial.c @@ -321,7 +321,7 @@ tcp_dialer_get_locaddr(void *arg, void *buf, size_t *szp, nni_type t) nng_sockaddr sa; nni_mtx_lock(&d->mtx); - if (nni_posix_sockaddr2nn(&sa, &d->src) != 0) { + if (nni_posix_sockaddr2nn(&sa, &d->src, d->srclen) != 0) { sa.s_family = NNG_AF_UNSPEC; } nni_mtx_unlock(&d->mtx); @@ -336,13 +336,13 @@ tcp_dialer_set_locaddr(void *arg, const void *buf, size_t sz, nni_type t) struct sockaddr_storage ss; struct sockaddr_in * sin; struct sockaddr_in6 * sin6; - size_t sslen; + size_t len; int rv; if ((rv = nni_copyin_sockaddr(&sa, buf, sz, t)) != 0) { return (rv); } - if ((sslen = nni_posix_nn2sockaddr(&ss, &sa)) == 0) { + if ((len = nni_posix_nn2sockaddr(&ss, &sa)) == 0) { return (NNG_EADDRINVAL); } // Ensure we are either IPv4 or IPv6, and port is not set. (We @@ -370,7 +370,7 @@ tcp_dialer_set_locaddr(void *arg, const void *buf, size_t sz, nni_type t) return (NNG_ECLOSED); } d->src = ss; - d->srclen = sslen; + d->srclen = len; nni_mtx_unlock(&d->mtx); } return (0); diff --git a/src/platform/posix/posix_tcplisten.c b/src/platform/posix/posix_tcplisten.c index a463a198..3a2ea62b 100644 --- a/src/platform/posix/posix_tcplisten.c +++ b/src/platform/posix/posix_tcplisten.c @@ -333,7 +333,7 @@ tcp_listener_get_locaddr(void *arg, void *buf, size_t *szp, nni_type t) socklen_t len = sizeof(ss); (void) getsockname( nni_posix_pfd_fd(l->pfd), (void *) &ss, &len); - (void) nni_posix_sockaddr2nn(&sa, &ss); + (void) nni_posix_sockaddr2nn(&sa, &ss, len); } else { sa.s_family = NNG_AF_UNSPEC; } diff --git a/src/platform/posix/posix_udp.c b/src/platform/posix/posix_udp.c index b8939d68..735953fa 100644 --- a/src/platform/posix/posix_udp.c +++ b/src/platform/posix/posix_udp.c @@ -1,5 +1,5 @@ // -// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2020 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 @@ -91,7 +91,8 @@ nni_posix_udp_dorecv(nni_plat_udp *udp) // We need to store the address information. // It is incumbent on the AIO submitter to supply // storage for the address. - nni_posix_sockaddr2nn(sa, (void *) &ss); + nni_posix_sockaddr2nn( + sa, (void *) &ss, hdr.msg_namelen); } nni_list_remove(q, aio); nni_aio_finish(aio, rv, cnt); @@ -319,7 +320,7 @@ nni_plat_udp_sockname(nni_plat_udp *udp, nni_sockaddr *sa) if (getsockname(udp->udp_fd, (struct sockaddr *) &ss, &sz) < 0) { return (nni_plat_errno(errno)); } - return (nni_posix_sockaddr2nn(sa, &ss)); + return (nni_posix_sockaddr2nn(sa, &ss, sz)); } #endif // NNG_PLATFORM_POSIX diff --git a/src/platform/windows/win_ipcdial.c b/src/platform/windows/win_ipcdial.c index 65d1b544..c210b7be 100644 --- a/src/platform/windows/win_ipcdial.c +++ b/src/platform/windows/win_ipcdial.c @@ -228,7 +228,8 @@ nni_ipc_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) int rv; if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || - (strlen(url->u_path) == 0)) { + (strlen(url->u_path) == 0)|| + (strlen(url->u_path) >= NNG_MAXADDRLEN)) { return (NNG_EADDRINVAL); } if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { diff --git a/src/platform/windows/win_ipclisten.c b/src/platform/windows/win_ipclisten.c index a3922d06..65a7a8d7 100644 --- a/src/platform/windows/win_ipclisten.c +++ b/src/platform/windows/win_ipclisten.c @@ -1,5 +1,5 @@ // -// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // Copyright 2019 Devolutions <info@devolutions.net> // @@ -328,7 +328,8 @@ nni_ipc_listener_alloc(nng_stream_listener **lp, const nng_url *url) int rv; if ((strcmp(url->u_scheme, "ipc") != 0) || (url->u_path == NULL) || - (strlen(url->u_path) == 0)) { + (strlen(url->u_path) == 0) || + (strlen(url->u_path) >= NNG_MAXADDRLEN)) { return (NNG_EADDRINVAL); } if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { diff --git a/src/transport/ipc/CMakeLists.txt b/src/transport/ipc/CMakeLists.txt index 7405e765..d9b771a9 100644 --- a/src/transport/ipc/CMakeLists.txt +++ b/src/transport/ipc/CMakeLists.txt @@ -14,4 +14,5 @@ mark_as_advanced(NNG_TRANSPORT_IPC) nng_sources_if(NNG_TRANSPORT_IPC ipc.c) nng_headers_if(NNG_TRANSPORT_IPC nng/transport/ipc/ipc.h) -nng_defines_if(NNG_TRANSPORT_IPC NNG_TRANSPORT_IPC)
\ No newline at end of file +nng_defines_if(NNG_TRANSPORT_IPC NNG_TRANSPORT_IPC) +nng_test(ipc_test)
\ No newline at end of file diff --git a/src/transport/ipc/ipc.c b/src/transport/ipc/ipc.c index c09b56be..a0b22506 100644 --- a/src/transport/ipc/ipc.c +++ b/src/transport/ipc/ipc.c @@ -21,198 +21,193 @@ // Windows named pipes. Other platforms could use other mechanisms, // but all implementations on the platform must use the same mechanism. -typedef struct ipctran_pipe ipctran_pipe; -typedef struct ipctran_ep ipctran_ep; +typedef struct ipc_pipe ipc_pipe; +typedef struct ipc_ep ipc_ep; // ipc_pipe is one end of an IPC connection. -struct ipctran_pipe { +struct ipc_pipe { nng_stream * conn; uint16_t peer; uint16_t proto; - size_t rcvmax; + size_t rcv_max; bool closed; nni_sockaddr sa; - ipctran_ep * ep; - nni_pipe * npipe; + ipc_ep * ep; + nni_pipe * pipe; nni_list_node node; nni_atomic_flag reaped; nni_reap_item reap; - uint8_t txhead[1 + sizeof(uint64_t)]; - uint8_t rxhead[1 + sizeof(uint64_t)]; - size_t gottxhead; - size_t gotrxhead; - size_t wanttxhead; - size_t wantrxhead; - nni_list recvq; - nni_list sendq; - nni_aio * txaio; - nni_aio * rxaio; - nni_aio * negoaio; - nni_msg * rxmsg; + uint8_t tx_head[1 + sizeof(uint64_t)]; + uint8_t rx_head[1 + sizeof(uint64_t)]; + size_t got_tx_head; + size_t got_rx_head; + size_t want_tx_head; + size_t want_rx_head; + nni_list recv_q; + nni_list send_q; + nni_aio tx_aio; + nni_aio rx_aio; + nni_aio neg_aio; + nni_msg * rx_msg; nni_mtx mtx; }; -struct ipctran_ep { +struct ipc_ep { nni_mtx mtx; nni_sockaddr sa; - size_t rcvmax; + size_t rcv_max; uint16_t proto; bool started; bool closed; bool fini; - int refcnt; + int ref_cnt; nng_stream_dialer * dialer; nng_stream_listener *listener; - nni_aio * useraio; - nni_aio * connaio; - nni_aio * timeaio; - nni_list busypipes; // busy pipes -- ones passed to socket - nni_list waitpipes; // pipes waiting to match to socket - nni_list negopipes; // pipes busy negotiating + nni_aio * user_aio; + nni_aio * conn_aio; + nni_aio * time_aio; + nni_list busy_pipes; // busy pipes -- ones passed to socket + nni_list wait_pipes; // pipes waiting to match to socket + nni_list neg_pipes; // pipes busy negotiating nni_reap_item reap; - nni_stat_item st_rcvmaxsz; + nni_stat_item st_rcv_max; }; -static void ipctran_pipe_send_start(ipctran_pipe *); -static void ipctran_pipe_recv_start(ipctran_pipe *); -static void ipctran_pipe_send_cb(void *); -static void ipctran_pipe_recv_cb(void *); -static void ipctran_pipe_nego_cb(void *); -static void ipctran_ep_fini(void *); +static void ipc_pipe_send_start(ipc_pipe *p); +static void ipc_pipe_recv_start(ipc_pipe *p); +static void ipc_pipe_send_cb(void *); +static void ipc_pipe_recv_cb(void *); +static void ipc_pipe_neg_cb(void *); +static void ipc_ep_fini(void *); static int -ipctran_init(void) +ipc_tran_init(void) { return (0); } static void -ipctran_fini(void) +ipc_tran_fini(void) { } static void -ipctran_pipe_close(void *arg) +ipc_pipe_close(void *arg) { - ipctran_pipe *p = arg; + ipc_pipe *p = arg; nni_mtx_lock(&p->mtx); p->closed = true; nni_mtx_unlock(&p->mtx); - nni_aio_close(p->rxaio); - nni_aio_close(p->txaio); - nni_aio_close(p->negoaio); + nni_aio_close(&p->rx_aio); + nni_aio_close(&p->tx_aio); + nni_aio_close(&p->neg_aio); nng_stream_close(p->conn); } static void -ipctran_pipe_stop(void *arg) +ipc_pipe_stop(void *arg) { - ipctran_pipe *p = arg; + ipc_pipe *p = arg; - nni_aio_stop(p->rxaio); - nni_aio_stop(p->txaio); - nni_aio_stop(p->negoaio); + nni_aio_stop(&p->rx_aio); + nni_aio_stop(&p->tx_aio); + nni_aio_stop(&p->neg_aio); } static int -ipctran_pipe_init(void *arg, nni_pipe *npipe) +ipc_pipe_init(void *arg, nni_pipe *pipe) { - ipctran_pipe *p = arg; - p->npipe = npipe; + ipc_pipe *p = arg; + p->pipe = pipe; return (0); } static void -ipctran_pipe_fini(void *arg) +ipc_pipe_fini(void *arg) { - ipctran_pipe *p = arg; - ipctran_ep * ep; + ipc_pipe *p = arg; + ipc_ep * ep; - ipctran_pipe_stop(p); + ipc_pipe_stop(p); if ((ep = p->ep) != NULL) { nni_mtx_lock(&ep->mtx); nni_list_node_remove(&p->node); - ep->refcnt--; - if (ep->fini && (ep->refcnt == 0)) { - nni_reap(&ep->reap, ipctran_ep_fini, ep); + ep->ref_cnt--; + if (ep->fini && (ep->ref_cnt == 0)) { + nni_reap(&ep->reap, ipc_ep_fini, ep); } nni_mtx_unlock(&ep->mtx); } - nni_aio_free(p->rxaio); - nni_aio_free(p->txaio); - nni_aio_free(p->negoaio); + nni_aio_fini(&p->rx_aio); + nni_aio_fini(&p->tx_aio); + nni_aio_fini(&p->neg_aio); nng_stream_free(p->conn); - if (p->rxmsg) { - nni_msg_free(p->rxmsg); + if (p->rx_msg) { + nni_msg_free(p->rx_msg); } nni_mtx_fini(&p->mtx); NNI_FREE_STRUCT(p); } static void -ipctran_pipe_reap(ipctran_pipe *p) +ipc_pipe_reap(ipc_pipe *p) { if (!nni_atomic_flag_test_and_set(&p->reaped)) { if (p->conn != NULL) { nng_stream_close(p->conn); } - nni_reap(&p->reap, ipctran_pipe_fini, p); + nni_reap(&p->reap, ipc_pipe_fini, p); } } static int -ipctran_pipe_alloc(ipctran_pipe **pipep) +ipc_pipe_alloc(ipc_pipe **pipe_p) { - ipctran_pipe *p; - int rv; + ipc_pipe *p; if ((p = NNI_ALLOC_STRUCT(p)) == NULL) { return (NNG_ENOMEM); } nni_mtx_init(&p->mtx); - if (((rv = nni_aio_alloc(&p->txaio, ipctran_pipe_send_cb, p)) != 0) || - ((rv = nni_aio_alloc(&p->rxaio, ipctran_pipe_recv_cb, p)) != 0) || - ((rv = nni_aio_alloc(&p->negoaio, ipctran_pipe_nego_cb, p)) != - 0)) { - ipctran_pipe_fini(p); - return (rv); - } - nni_aio_list_init(&p->sendq); - nni_aio_list_init(&p->recvq); + nni_aio_init(&p->tx_aio, ipc_pipe_send_cb, p); + nni_aio_init(&p->rx_aio, ipc_pipe_recv_cb, p); + nni_aio_init(&p->neg_aio, ipc_pipe_neg_cb, p); + nni_aio_list_init(&p->send_q); + nni_aio_list_init(&p->recv_q); nni_atomic_flag_reset(&p->reaped); - *pipep = p; + *pipe_p = p; return (0); } static void -ipctran_ep_match(ipctran_ep *ep) +ipc_ep_match(ipc_ep *ep) { - nni_aio * aio; - ipctran_pipe *p; + nni_aio * aio; + ipc_pipe *p; - if (((aio = ep->useraio) == NULL) || - ((p = nni_list_first(&ep->waitpipes)) == NULL)) { + if (((aio = ep->user_aio) == NULL) || + ((p = nni_list_first(&ep->wait_pipes)) == NULL)) { return; } - nni_list_remove(&ep->waitpipes, p); - nni_list_append(&ep->busypipes, p); - ep->useraio = NULL; - p->rcvmax = ep->rcvmax; + nni_list_remove(&ep->wait_pipes, p); + nni_list_append(&ep->busy_pipes, p); + ep->user_aio = NULL; + p->rcv_max = ep->rcv_max; nni_aio_set_output(aio, 0, p); nni_aio_finish(aio, 0, 0); } static void -ipctran_pipe_nego_cb(void *arg) +ipc_pipe_neg_cb(void *arg) { - ipctran_pipe *p = arg; - ipctran_ep * ep = p->ep; - nni_aio * aio = p->negoaio; - nni_aio * uaio; - int rv; + ipc_pipe *p = arg; + ipc_ep * ep = p->ep; + nni_aio * aio = &p->neg_aio; + nni_aio * user_aio; + int rv; nni_mtx_lock(&ep->mtx); if ((rv = nni_aio_result(aio)) != 0) { @@ -220,25 +215,25 @@ ipctran_pipe_nego_cb(void *arg) } // We start transmitting before we receive. - if (p->gottxhead < p->wanttxhead) { - p->gottxhead += nni_aio_count(aio); - } else if (p->gotrxhead < p->wantrxhead) { - p->gotrxhead += nni_aio_count(aio); + if (p->got_tx_head < p->want_tx_head) { + p->got_tx_head += nni_aio_count(aio); + } else if (p->got_rx_head < p->want_rx_head) { + p->got_rx_head += nni_aio_count(aio); } - if (p->gottxhead < p->wanttxhead) { + if (p->got_tx_head < p->want_tx_head) { nni_iov iov; - iov.iov_len = p->wanttxhead - p->gottxhead; - iov.iov_buf = &p->txhead[p->gottxhead]; + iov.iov_len = p->want_tx_head - p->got_tx_head; + iov.iov_buf = &p->tx_head[p->got_tx_head]; nni_aio_set_iov(aio, 1, &iov); // send it down... nng_stream_send(p->conn, aio); nni_mtx_unlock(&p->ep->mtx); return; } - if (p->gotrxhead < p->wantrxhead) { + if (p->got_rx_head < p->want_rx_head) { nni_iov iov; - iov.iov_len = p->wantrxhead - p->gotrxhead; - iov.iov_buf = &p->rxhead[p->gotrxhead]; + iov.iov_len = p->want_rx_head - p->got_rx_head; + iov.iov_buf = &p->rx_head[p->got_rx_head]; nni_aio_set_iov(aio, 1, &iov); nng_stream_recv(p->conn, aio); nni_mtx_unlock(&p->ep->mtx); @@ -246,21 +241,21 @@ ipctran_pipe_nego_cb(void *arg) } // We have both sent and received the headers. Lets check the // receive side header. - if ((p->rxhead[0] != 0) || (p->rxhead[1] != 'S') || - (p->rxhead[2] != 'P') || (p->rxhead[3] != 0) || - (p->rxhead[6] != 0) || (p->rxhead[7] != 0)) { + if ((p->rx_head[0] != 0) || (p->rx_head[1] != 'S') || + (p->rx_head[2] != 'P') || (p->rx_head[3] != 0) || + (p->rx_head[6] != 0) || (p->rx_head[7] != 0)) { rv = NNG_EPROTO; goto error; } - NNI_GET16(&p->rxhead[4], p->peer); + NNI_GET16(&p->rx_head[4], p->peer); // We are all ready now. We put this in the wait list, and // then try to run the matcher. - nni_list_remove(&ep->negopipes, p); - nni_list_append(&ep->waitpipes, p); + nni_list_remove(&ep->neg_pipes, p); + nni_list_append(&ep->wait_pipes, p); - ipctran_ep_match(ep); + ipc_ep_match(ep); nni_mtx_unlock(&ep->mtx); return; @@ -269,34 +264,34 @@ error: nng_stream_close(p->conn); // If we are waiting to negotiate on a client side, then a failure // here has to be passed to the user app. - if ((uaio = ep->useraio) != NULL) { - ep->useraio = NULL; - nni_aio_finish_error(uaio, rv); + if ((user_aio = ep->user_aio) != NULL) { + ep->user_aio = NULL; + nni_aio_finish_error(user_aio, rv); } nni_mtx_unlock(&ep->mtx); - ipctran_pipe_reap(p); + ipc_pipe_reap(p); } static void -ipctran_pipe_send_cb(void *arg) +ipc_pipe_send_cb(void *arg) { - ipctran_pipe *p = arg; - int rv; - nni_aio * aio; - size_t n; - nni_msg * msg; - nni_aio * txaio = p->txaio; + ipc_pipe *p = arg; + int rv; + nni_aio * aio; + size_t n; + nni_msg * msg; + nni_aio * tx_aio = &p->tx_aio; nni_mtx_lock(&p->mtx); - if ((rv = nni_aio_result(txaio)) != 0) { - nni_pipe_bump_error(p->npipe, rv); + if ((rv = nni_aio_result(tx_aio)) != 0) { + nni_pipe_bump_error(p->pipe, rv); // Intentionally we do not queue up another transfer. // There's an excellent chance that the pipe is no longer // usable, with a partial transfer. // The protocol should see this error, and close the // pipe itself, we hope. - while ((aio = nni_list_first(&p->sendq)) != NULL) { + while ((aio = nni_list_first(&p->send_q)) != NULL) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); } @@ -304,21 +299,21 @@ ipctran_pipe_send_cb(void *arg) return; } - n = nni_aio_count(txaio); - nni_aio_iov_advance(txaio, n); - if (nni_aio_iov_count(txaio) != 0) { - nng_stream_send(p->conn, txaio); + n = nni_aio_count(tx_aio); + nni_aio_iov_advance(tx_aio, n); + if (nni_aio_iov_count(tx_aio) != 0) { + nng_stream_send(p->conn, tx_aio); nni_mtx_unlock(&p->mtx); return; } - aio = nni_list_first(&p->sendq); + aio = nni_list_first(&p->send_q); nni_aio_list_remove(aio); - ipctran_pipe_send_start(p); + ipc_pipe_send_start(p); msg = nni_aio_get_msg(aio); n = nni_msg_len(msg); - nni_pipe_bump_tx(p->npipe, n); + nni_pipe_bump_tx(p->pipe, n); nni_mtx_unlock(&p->mtx); nni_aio_set_msg(aio, NULL); @@ -327,29 +322,29 @@ ipctran_pipe_send_cb(void *arg) } static void -ipctran_pipe_recv_cb(void *arg) +ipc_pipe_recv_cb(void *arg) { - ipctran_pipe *p = arg; - nni_aio * aio; - int rv; - size_t n; - nni_msg * msg; - nni_aio * rxaio = p->rxaio; + ipc_pipe *p = arg; + nni_aio * aio; + int rv; + size_t n; + nni_msg * msg; + nni_aio * rx_aio = &p->rx_aio; nni_mtx_lock(&p->mtx); - if ((rv = nni_aio_result(rxaio)) != 0) { + if ((rv = nni_aio_result(rx_aio)) != 0) { // Error on receive. This has to cause an error back - // to the user. Also, if we had allocated an rxmsg, lets + // to the user. Also, if we had allocated an rx_msg, lets // toss it. goto error; } - n = nni_aio_count(rxaio); - nni_aio_iov_advance(rxaio, n); - if (nni_aio_iov_count(rxaio) != 0) { + n = nni_aio_count(rx_aio); + nni_aio_iov_advance(rx_aio, n); + if (nni_aio_iov_count(rx_aio) != 0) { // Was this a partial read? If so then resubmit for the rest. - nng_stream_recv(p->conn, rxaio); + nng_stream_recv(p->conn, rx_aio); nni_mtx_unlock(&p->mtx); return; } @@ -357,21 +352,21 @@ ipctran_pipe_recv_cb(void *arg) // If we don't have a message yet, we were reading the message // header, which is just the length. This tells us the size of the // message to allocate and how much more to expect. - if (p->rxmsg == NULL) { + if (p->rx_msg == NULL) { uint64_t len; // Check to make sure we got msg type 1. - if (p->rxhead[0] != 1) { + if (p->rx_head[0] != 1) { rv = NNG_EPROTO; goto error; } // We should have gotten a message header. - NNI_GET64(p->rxhead + 1, len); + NNI_GET64(p->rx_head + 1, len); // Make sure the message payload is not too big. If it is // the caller will shut down the pipe. - if ((len > p->rcvmax) && (p->rcvmax > 0)) { + if ((len > p->rcv_max) && (p->rcv_max > 0)) { rv = NNG_EMSGSIZE; goto error; } @@ -381,7 +376,7 @@ ipctran_pipe_recv_cb(void *arg) // lock for the read side in the future, so that we allow // transmits to proceed normally. In practice this is // unlikely to be much of an issue though. - if ((rv = nni_msg_alloc(&p->rxmsg, (size_t) len)) != 0) { + if ((rv = nni_msg_alloc(&p->rx_msg, (size_t) len)) != 0) { goto error; } @@ -389,11 +384,11 @@ ipctran_pipe_recv_cb(void *arg) nni_iov iov; // Submit the rest of the data for a read -- we want to // read the entire message now. - iov.iov_buf = nni_msg_body(p->rxmsg); + iov.iov_buf = nni_msg_body(p->rx_msg); iov.iov_len = (size_t) len; - nni_aio_set_iov(rxaio, 1, &iov); - nng_stream_recv(p->conn, rxaio); + nni_aio_set_iov(rx_aio, 1, &iov); + nng_stream_recv(p->conn, rx_aio); nni_mtx_unlock(&p->mtx); return; } @@ -402,13 +397,13 @@ ipctran_pipe_recv_cb(void *arg) // Otherwise we got a message read completely. Let the user know the // good news. - aio = nni_list_first(&p->recvq); + aio = nni_list_first(&p->recv_q); nni_aio_list_remove(aio); - msg = p->rxmsg; - p->rxmsg = NULL; - n = nni_msg_len(msg); - nni_pipe_bump_rx(p->npipe, n); - ipctran_pipe_recv_start(p); + msg = p->rx_msg; + p->rx_msg = NULL; + n = nni_msg_len(msg); + nni_pipe_bump_rx(p->pipe, n); + ipc_pipe_recv_start(p); nni_mtx_unlock(&p->mtx); nni_aio_set_msg(aio, msg); @@ -416,13 +411,13 @@ ipctran_pipe_recv_cb(void *arg) return; error: - while ((aio = nni_list_first(&p->recvq)) != NULL) { + while ((aio = nni_list_first(&p->recv_q)) != NULL) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, rv); } - msg = p->rxmsg; - p->rxmsg = NULL; - nni_pipe_bump_error(p->npipe, rv); + msg = p->rx_msg; + p->rx_msg = NULL; + nni_pipe_bump_error(p->pipe, rv); // Intentionally, we do not queue up another receive. // The protocol should notice this error and close the pipe. nni_mtx_unlock(&p->mtx); @@ -431,9 +426,9 @@ error: } static void -ipctran_pipe_send_cancel(nni_aio *aio, void *arg, int rv) +ipc_pipe_send_cancel(nni_aio *aio, void *arg, int rv) { - ipctran_pipe *p = arg; + ipc_pipe *p = arg; nni_mtx_lock(&p->mtx); if (!nni_aio_list_active(aio)) { @@ -441,10 +436,10 @@ ipctran_pipe_send_cancel(nni_aio *aio, void *arg, int rv) return; } // If this is being sent, then cancel the pending transfer. - // The callback on the txaio will cause the user aio to + // The callback on the tx_aio will cause the user aio to // be canceled too. - if (nni_list_first(&p->sendq) == aio) { - nni_aio_abort(p->txaio, rv); + if (nni_list_first(&p->send_q) == aio) { + nni_aio_abort(&p->tx_aio, rv); nni_mtx_unlock(&p->mtx); return; } @@ -455,23 +450,22 @@ ipctran_pipe_send_cancel(nni_aio *aio, void *arg, int rv) } static void -ipctran_pipe_send_start(ipctran_pipe *p) +ipc_pipe_send_start(ipc_pipe *p) { nni_aio *aio; - nni_aio *txaio; nni_msg *msg; - int niov; + int nio; nni_iov iov[3]; uint64_t len; if (p->closed) { - while ((aio = nni_list_first(&p->sendq)) != NULL) { - nni_list_remove(&p->sendq, aio); + while ((aio = nni_list_first(&p->send_q)) != NULL) { + nni_list_remove(&p->send_q, aio); nni_aio_finish_error(aio, NNG_ECLOSED); } return; } - if ((aio = nni_list_first(&p->sendq)) == NULL) { + if ((aio = nni_list_first(&p->send_q)) == NULL) { return; } @@ -479,54 +473,53 @@ ipctran_pipe_send_start(ipctran_pipe *p) msg = nni_aio_get_msg(aio); len = nni_msg_len(msg) + nni_msg_header_len(msg); - p->txhead[0] = 1; // message type, 1. - NNI_PUT64(p->txhead + 1, len); + p->tx_head[0] = 1; // message type, 1. + NNI_PUT64(p->tx_head + 1, len); - txaio = p->txaio; - niov = 0; - iov[0].iov_buf = p->txhead; - iov[0].iov_len = sizeof(p->txhead); - niov++; + nio = 0; + iov[0].iov_buf = p->tx_head; + iov[0].iov_len = sizeof(p->tx_head); + nio++; if (nni_msg_header_len(msg) > 0) { - iov[niov].iov_buf = nni_msg_header(msg); - iov[niov].iov_len = nni_msg_header_len(msg); - niov++; + iov[nio].iov_buf = nni_msg_header(msg); + iov[nio].iov_len = nni_msg_header_len(msg); + nio++; } if (nni_msg_len(msg) > 0) { - iov[niov].iov_buf = nni_msg_body(msg); - iov[niov].iov_len = nni_msg_len(msg); - niov++; + iov[nio].iov_buf = nni_msg_body(msg); + iov[nio].iov_len = nni_msg_len(msg); + nio++; } - nni_aio_set_iov(txaio, niov, iov); - nng_stream_send(p->conn, txaio); + nni_aio_set_iov(&p->tx_aio, nio, iov); + nng_stream_send(p->conn, &p->tx_aio); } static void -ipctran_pipe_send(void *arg, nni_aio *aio) +ipc_pipe_send(void *arg, nni_aio *aio) { - ipctran_pipe *p = arg; - int rv; + ipc_pipe *p = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; } nni_mtx_lock(&p->mtx); - if ((rv = nni_aio_schedule(aio, ipctran_pipe_send_cancel, p)) != 0) { + if ((rv = nni_aio_schedule(aio, ipc_pipe_send_cancel, p)) != 0) { nni_mtx_unlock(&p->mtx); nni_aio_finish_error(aio, rv); return; } - nni_list_append(&p->sendq, aio); - if (nni_list_first(&p->sendq) == aio) { - ipctran_pipe_send_start(p); + nni_list_append(&p->send_q, aio); + if (nni_list_first(&p->send_q) == aio) { + ipc_pipe_send_start(p); } nni_mtx_unlock(&p->mtx); } static void -ipctran_pipe_recv_cancel(nni_aio *aio, void *arg, int rv) +ipc_pipe_recv_cancel(nni_aio *aio, void *arg, int rv) { - ipctran_pipe *p = arg; + ipc_pipe *p = arg; nni_mtx_lock(&p->mtx); if (!nni_aio_list_active(aio)) { @@ -534,10 +527,10 @@ ipctran_pipe_recv_cancel(nni_aio *aio, void *arg, int rv) return; } // If receive in progress, then cancel the pending transfer. - // The callback on the rxaio will cause the user aio to + // The callback on the rx_aio will cause the user aio to // be canceled too. - if (nni_list_first(&p->recvq) == aio) { - nni_aio_abort(p->rxaio, rv); + if (nni_list_first(&p->recv_q) == aio) { + nni_aio_abort(&p->rx_aio, rv); nni_mtx_unlock(&p->mtx); return; } @@ -547,38 +540,36 @@ ipctran_pipe_recv_cancel(nni_aio *aio, void *arg, int rv) } static void -ipctran_pipe_recv_start(ipctran_pipe *p) +ipc_pipe_recv_start(ipc_pipe *p) { - nni_aio *rxaio; - nni_iov iov; - NNI_ASSERT(p->rxmsg == NULL); + nni_iov iov; + NNI_ASSERT(p->rx_msg == NULL); if (p->closed) { nni_aio *aio; - while ((aio = nni_list_first(&p->recvq)) != NULL) { - nni_list_remove(&p->recvq, aio); + while ((aio = nni_list_first(&p->recv_q)) != NULL) { + nni_list_remove(&p->recv_q, aio); nni_aio_finish_error(aio, NNG_ECLOSED); } return; } - if (nni_list_empty(&p->recvq)) { + if (nni_list_empty(&p->recv_q)) { return; } // Schedule a read of the IPC header. - rxaio = p->rxaio; - iov.iov_buf = p->rxhead; - iov.iov_len = sizeof(p->rxhead); - nni_aio_set_iov(rxaio, 1, &iov); + iov.iov_buf = p->rx_head; + iov.iov_len = sizeof(p->rx_head); + nni_aio_set_iov(&p->rx_aio, 1, &iov); - nng_stream_recv(p->conn, rxaio); + nng_stream_recv(p->conn, &p->rx_aio); } static void -ipctran_pipe_recv(void *arg, nni_aio *aio) +ipc_pipe_recv(void *arg, nni_aio *aio) { - ipctran_pipe *p = arg; - int rv; + ipc_pipe *p = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -589,130 +580,130 @@ ipctran_pipe_recv(void *arg, nni_aio *aio) nni_aio_finish_error(aio, NNG_ECLOSED); return; } - if ((rv = nni_aio_schedule(aio, ipctran_pipe_recv_cancel, p)) != 0) { + if ((rv = nni_aio_schedule(aio, ipc_pipe_recv_cancel, p)) != 0) { nni_mtx_unlock(&p->mtx); nni_aio_finish_error(aio, rv); return; } - nni_list_append(&p->recvq, aio); - if (nni_list_first(&p->recvq) == aio) { - ipctran_pipe_recv_start(p); + nni_list_append(&p->recv_q, aio); + if (nni_list_first(&p->recv_q) == aio) { + ipc_pipe_recv_start(p); } nni_mtx_unlock(&p->mtx); } static uint16_t -ipctran_pipe_peer(void *arg) +ipc_pipe_peer(void *arg) { - ipctran_pipe *p = arg; + ipc_pipe *p = arg; return (p->peer); } static void -ipctran_pipe_start(ipctran_pipe *p, nng_stream *conn, ipctran_ep *ep) +ipc_pipe_start(ipc_pipe *p, nng_stream *conn, ipc_ep *ep) { nni_iov iov; - ep->refcnt++; + ep->ref_cnt++; p->conn = conn; p->ep = ep; p->proto = ep->proto; - p->txhead[0] = 0; - p->txhead[1] = 'S'; - p->txhead[2] = 'P'; - p->txhead[3] = 0; - NNI_PUT16(&p->txhead[4], p->proto); - NNI_PUT16(&p->txhead[6], 0); - - p->gotrxhead = 0; - p->gottxhead = 0; - p->wantrxhead = 8; - p->wanttxhead = 8; - iov.iov_len = 8; - iov.iov_buf = &p->txhead[0]; - nni_aio_set_iov(p->negoaio, 1, &iov); - nni_list_append(&ep->negopipes, p); - - nni_aio_set_timeout(p->negoaio, 10000); // 10 sec timeout to negotiate - nng_stream_send(p->conn, p->negoaio); + p->tx_head[0] = 0; + p->tx_head[1] = 'S'; + p->tx_head[2] = 'P'; + p->tx_head[3] = 0; + NNI_PUT16(&p->tx_head[4], p->proto); + NNI_PUT16(&p->tx_head[6], 0); + + p->got_rx_head = 0; + p->got_tx_head = 0; + p->want_rx_head = 8; + p->want_tx_head = 8; + iov.iov_len = 8; + iov.iov_buf = &p->tx_head[0]; + nni_aio_set_iov(&p->neg_aio, 1, &iov); + nni_list_append(&ep->neg_pipes, p); + + nni_aio_set_timeout(&p->neg_aio, 10000); // 10 sec timeout to negotiate + nng_stream_send(p->conn, &p->neg_aio); } static void -ipctran_ep_close(void *arg) +ipc_ep_close(void *arg) { - ipctran_ep * ep = arg; - ipctran_pipe *p; + ipc_ep * ep = arg; + ipc_pipe *p; nni_mtx_lock(&ep->mtx); ep->closed = true; - nni_aio_close(ep->timeaio); + nni_aio_close(ep->time_aio); if (ep->dialer != NULL) { nng_stream_dialer_close(ep->dialer); } if (ep->listener != NULL) { nng_stream_listener_close(ep->listener); } - NNI_LIST_FOREACH (&ep->negopipes, p) { - ipctran_pipe_close(p); + NNI_LIST_FOREACH (&ep->neg_pipes, p) { + ipc_pipe_close(p); } - NNI_LIST_FOREACH (&ep->waitpipes, p) { - ipctran_pipe_close(p); + NNI_LIST_FOREACH (&ep->wait_pipes, p) { + ipc_pipe_close(p); } - NNI_LIST_FOREACH (&ep->busypipes, p) { - ipctran_pipe_close(p); + NNI_LIST_FOREACH (&ep->busy_pipes, p) { + ipc_pipe_close(p); } - if (ep->useraio != NULL) { - nni_aio_finish_error(ep->useraio, NNG_ECLOSED); - ep->useraio = NULL; + if (ep->user_aio != NULL) { + nni_aio_finish_error(ep->user_aio, NNG_ECLOSED); + ep->user_aio = NULL; } nni_mtx_unlock(&ep->mtx); } static void -ipctran_ep_fini(void *arg) +ipc_ep_fini(void *arg) { - ipctran_ep *ep = arg; + ipc_ep *ep = arg; nni_mtx_lock(&ep->mtx); ep->fini = true; - if (ep->refcnt != 0) { + if (ep->ref_cnt != 0) { nni_mtx_unlock(&ep->mtx); return; } nni_mtx_unlock(&ep->mtx); - nni_aio_stop(ep->timeaio); - nni_aio_stop(ep->connaio); + nni_aio_stop(ep->time_aio); + nni_aio_stop(ep->conn_aio); nng_stream_dialer_free(ep->dialer); nng_stream_listener_free(ep->listener); - nni_aio_free(ep->timeaio); - nni_aio_free(ep->connaio); + nni_aio_free(ep->time_aio); + nni_aio_free(ep->conn_aio); nni_mtx_fini(&ep->mtx); NNI_FREE_STRUCT(ep); } static void -ipctran_timer_cb(void *arg) +ipc_ep_timer_cb(void *arg) { - ipctran_ep *ep = arg; + ipc_ep *ep = arg; nni_mtx_lock(&ep->mtx); - if (nni_aio_result(ep->timeaio) == 0) { - nng_stream_listener_accept(ep->listener, ep->connaio); + if (nni_aio_result(ep->time_aio) == 0) { + nng_stream_listener_accept(ep->listener, ep->conn_aio); } nni_mtx_unlock(&ep->mtx); } static void -ipctran_accept_cb(void *arg) +ipc_ep_accept_cb(void *arg) { - ipctran_ep * ep = arg; - nni_aio * aio = ep->connaio; - ipctran_pipe *p; - int rv; - nng_stream * conn; + ipc_ep * ep = arg; + nni_aio * aio = ep->conn_aio; + ipc_pipe * p; + int rv; + nng_stream *conn; nni_mtx_lock(&ep->mtx); if ((rv = nni_aio_result(aio)) != 0) { @@ -720,26 +711,26 @@ ipctran_accept_cb(void *arg) } conn = nni_aio_get_output(aio, 0); - if ((rv = ipctran_pipe_alloc(&p)) != 0) { + if ((rv = ipc_pipe_alloc(&p)) != 0) { nng_stream_free(conn); goto error; } if (ep->closed) { - ipctran_pipe_fini(p); + ipc_pipe_fini(p); nng_stream_free(conn); rv = NNG_ECLOSED; goto error; } - ipctran_pipe_start(p, conn, ep); - nng_stream_listener_accept(ep->listener, ep->connaio); + ipc_pipe_start(p, conn, ep); + nng_stream_listener_accept(ep->listener, ep->conn_aio); nni_mtx_unlock(&ep->mtx); return; error: // When an error here occurs, let's send a notice up to the consumer. // That way it can be reported properly. - if ((aio = ep->useraio) != NULL) { - ep->useraio = NULL; + if ((aio = ep->user_aio) != NULL) { + ep->user_aio = NULL; nni_aio_finish_error(aio, rv); } @@ -747,12 +738,12 @@ error: case NNG_ENOMEM: case NNG_ENOFILES: - nng_sleep_aio(10, ep->timeaio); + nng_sleep_aio(10, ep->time_aio); break; default: if (!ep->closed) { - nng_stream_listener_accept(ep->listener, ep->connaio); + nng_stream_listener_accept(ep->listener, ep->conn_aio); } break; } @@ -760,32 +751,32 @@ error: } static void -ipctran_dial_cb(void *arg) +ipc_ep_dial_cb(void *arg) { - ipctran_ep * ep = arg; - nni_aio * aio = ep->connaio; - ipctran_pipe *p; - int rv; - nng_stream * conn; + ipc_ep * ep = arg; + nni_aio * aio = ep->conn_aio; + ipc_pipe * p; + int rv; + nng_stream *conn; if ((rv = nni_aio_result(aio)) != 0) { goto error; } conn = nni_aio_get_output(aio, 0); - if ((rv = ipctran_pipe_alloc(&p)) != 0) { + if ((rv = ipc_pipe_alloc(&p)) != 0) { nng_stream_free(conn); goto error; } nni_mtx_lock(&ep->mtx); if (ep->closed) { - ipctran_pipe_fini(p); + ipc_pipe_fini(p); nng_stream_free(conn); rv = NNG_ECLOSED; nni_mtx_unlock(&ep->mtx); goto error; } else { - ipctran_pipe_start(p, conn, ep); + ipc_pipe_start(p, conn, ep); } nni_mtx_unlock(&ep->mtx); return; @@ -794,98 +785,98 @@ error: // Error connecting. We need to pass this straight back // to the user. nni_mtx_lock(&ep->mtx); - if ((aio = ep->useraio) != NULL) { - ep->useraio = NULL; + if ((aio = ep->user_aio) != NULL) { + ep->user_aio = NULL; nni_aio_finish_error(aio, rv); } nni_mtx_unlock(&ep->mtx); } static int -ipctran_ep_init(ipctran_ep **epp, nni_sock *sock) +ipc_ep_init(ipc_ep **epp, nni_sock *sock) { - ipctran_ep *ep; + ipc_ep *ep; if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { return (NNG_ENOMEM); } nni_mtx_init(&ep->mtx); - NNI_LIST_INIT(&ep->busypipes, ipctran_pipe, node); - NNI_LIST_INIT(&ep->waitpipes, ipctran_pipe, node); - NNI_LIST_INIT(&ep->negopipes, ipctran_pipe, node); + NNI_LIST_INIT(&ep->busy_pipes, ipc_pipe, node); + NNI_LIST_INIT(&ep->wait_pipes, ipc_pipe, node); + NNI_LIST_INIT(&ep->neg_pipes, ipc_pipe, node); ep->proto = nni_sock_proto_id(sock); - nni_stat_init(&ep->st_rcvmaxsz, "rcvmaxsz", "maximum receive size"); - nni_stat_set_type(&ep->st_rcvmaxsz, NNG_STAT_LEVEL); - nni_stat_set_unit(&ep->st_rcvmaxsz, NNG_UNIT_BYTES); + nni_stat_init(&ep->st_rcv_max, "rcvmaxsz", "maximum receive size"); + nni_stat_set_type(&ep->st_rcv_max, NNG_STAT_LEVEL); + nni_stat_set_unit(&ep->st_rcv_max, NNG_UNIT_BYTES); *epp = ep; return (0); } static int -ipctran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer) +ipc_ep_init_dialer(void **dp, nni_url *url, nni_dialer *dialer) { - ipctran_ep *ep; - int rv; - nni_sock * sock = nni_dialer_sock(ndialer); + ipc_ep * ep; + int rv; + nni_sock *sock = nni_dialer_sock(dialer); - if ((rv = ipctran_ep_init(&ep, sock)) != 0) { + if ((rv = ipc_ep_init(&ep, sock)) != 0) { return (rv); } - if (((rv = nni_aio_alloc(&ep->connaio, ipctran_dial_cb, ep)) != 0) || + if (((rv = nni_aio_alloc(&ep->conn_aio, ipc_ep_dial_cb, ep)) != 0) || ((rv = nng_stream_dialer_alloc_url(&ep->dialer, url)) != 0)) { - ipctran_ep_fini(ep); + ipc_ep_fini(ep); return (rv); } - nni_dialer_add_stat(ndialer, &ep->st_rcvmaxsz); + nni_dialer_add_stat(dialer, &ep->st_rcv_max); *dp = ep; return (0); } static int -ipctran_ep_init_listener(void **dp, nni_url *url, nni_listener *nlistener) +ipc_ep_init_listener(void **dp, nni_url *url, nni_listener *listener) { - ipctran_ep *ep; - int rv; - nni_sock * sock = nni_listener_sock(nlistener); + ipc_ep * ep; + int rv; + nni_sock *sock = nni_listener_sock(listener); - if ((rv = ipctran_ep_init(&ep, sock)) != 0) { + if ((rv = ipc_ep_init(&ep, sock)) != 0) { return (rv); } - if (((rv = nni_aio_alloc(&ep->connaio, ipctran_accept_cb, ep)) != 0) || - ((rv = nni_aio_alloc(&ep->timeaio, ipctran_timer_cb, ep)) != 0) || + if (((rv = nni_aio_alloc(&ep->conn_aio, ipc_ep_accept_cb, ep)) != 0) || + ((rv = nni_aio_alloc(&ep->time_aio, ipc_ep_timer_cb, ep)) != 0) || ((rv = nng_stream_listener_alloc_url(&ep->listener, url)) != 0)) { - ipctran_ep_fini(ep); + ipc_ep_fini(ep); return (rv); } - nni_listener_add_stat(nlistener, &ep->st_rcvmaxsz); + nni_listener_add_stat(listener, &ep->st_rcv_max); *dp = ep; return (0); } static void -ipctran_ep_cancel(nni_aio *aio, void *arg, int rv) +ipc_ep_cancel(nni_aio *aio, void *arg, int rv) { - ipctran_ep *ep = arg; + ipc_ep *ep = arg; nni_mtx_lock(&ep->mtx); - if (aio == ep->useraio) { - ep->useraio = NULL; + if (aio == ep->user_aio) { + ep->user_aio = NULL; nni_aio_finish_error(aio, rv); } nni_mtx_unlock(&ep->mtx); } static void -ipctran_ep_connect(void *arg, nni_aio *aio) +ipc_ep_connect(void *arg, nni_aio *aio) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -896,64 +887,64 @@ ipctran_ep_connect(void *arg, nni_aio *aio) nni_aio_finish_error(aio, NNG_ECLOSED); return; } - if (ep->useraio != NULL) { + if (ep->user_aio != NULL) { nni_mtx_unlock(&ep->mtx); nni_aio_finish_error(aio, NNG_EBUSY); return; } - if ((rv = nni_aio_schedule(aio, ipctran_ep_cancel, ep)) != 0) { + if ((rv = nni_aio_schedule(aio, ipc_ep_cancel, ep)) != 0) { nni_mtx_unlock(&ep->mtx); nni_aio_finish_error(aio, rv); return; } - ep->useraio = aio; - nng_stream_dialer_dial(ep->dialer, ep->connaio); + ep->user_aio = aio; + nng_stream_dialer_dial(ep->dialer, ep->conn_aio); nni_mtx_unlock(&ep->mtx); } static int -ipctran_ep_get_recvmaxsz(void *arg, void *v, size_t *szp, nni_type t) +ipc_ep_get_recv_max_sz(void *arg, void *v, size_t *szp, nni_type t) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; nni_mtx_lock(&ep->mtx); - rv = nni_copyout_size(ep->rcvmax, v, szp, t); + rv = nni_copyout_size(ep->rcv_max, v, szp, t); nni_mtx_unlock(&ep->mtx); return (rv); } static int -ipctran_ep_set_recvmaxsz(void *arg, const void *v, size_t sz, nni_type t) +ipc_ep_set_recv_max_sz(void *arg, const void *v, size_t sz, nni_type t) { - ipctran_ep *ep = arg; - size_t val; - int rv; + ipc_ep *ep = arg; + size_t val; + int rv; if ((rv = nni_copyin_size(&val, v, sz, 0, NNI_MAXSZ, t)) == 0) { - ipctran_pipe *p; + ipc_pipe *p; nni_mtx_lock(&ep->mtx); - ep->rcvmax = val; - NNI_LIST_FOREACH (&ep->waitpipes, p) { - p->rcvmax = val; + ep->rcv_max = val; + NNI_LIST_FOREACH (&ep->wait_pipes, p) { + p->rcv_max = val; } - NNI_LIST_FOREACH (&ep->negopipes, p) { - p->rcvmax = val; + NNI_LIST_FOREACH (&ep->neg_pipes, p) { + p->rcv_max = val; } - NNI_LIST_FOREACH (&ep->busypipes, p) { - p->rcvmax = val; + NNI_LIST_FOREACH (&ep->busy_pipes, p) { + p->rcv_max = val; } - nni_stat_set_value(&ep->st_rcvmaxsz, val); + nni_stat_set_value(&ep->st_rcv_max, val); nni_mtx_unlock(&ep->mtx); } return (rv); } static int -ipctran_ep_bind(void *arg) +ipc_ep_bind(void *arg) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; nni_mtx_lock(&ep->mtx); rv = nng_stream_listener_listen(ep->listener); @@ -962,10 +953,10 @@ ipctran_ep_bind(void *arg) } static void -ipctran_ep_accept(void *arg, nni_aio *aio) +ipc_ep_accept(void *arg, nni_aio *aio) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; if (nni_aio_begin(aio) != 0) { return; @@ -976,52 +967,51 @@ ipctran_ep_accept(void *arg, nni_aio *aio) nni_mtx_unlock(&ep->mtx); return; } - if (ep->useraio != NULL) { + if (ep->user_aio != NULL) { nni_aio_finish_error(aio, NNG_EBUSY); nni_mtx_unlock(&ep->mtx); return; } - if ((rv = nni_aio_schedule(aio, ipctran_ep_cancel, ep)) != 0) { + if ((rv = nni_aio_schedule(aio, ipc_ep_cancel, ep)) != 0) { nni_mtx_unlock(&ep->mtx); nni_aio_finish_error(aio, rv); return; } - ep->useraio = aio; + ep->user_aio = aio; if (!ep->started) { ep->started = true; - nng_stream_listener_accept(ep->listener, ep->connaio); + nng_stream_listener_accept(ep->listener, ep->conn_aio); } else { - ipctran_ep_match(ep); + ipc_ep_match(ep); } nni_mtx_unlock(&ep->mtx); } static int -ipctran_pipe_getopt( - void *arg, const char *name, void *buf, size_t *szp, nni_type t) +ipc_pipe_get(void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - ipctran_pipe *p = arg; + ipc_pipe *p = arg; return (nni_stream_getx(p->conn, name, buf, szp, t)); } -static nni_tran_pipe_ops ipctran_pipe_ops = { - .p_init = ipctran_pipe_init, - .p_fini = ipctran_pipe_fini, - .p_stop = ipctran_pipe_stop, - .p_send = ipctran_pipe_send, - .p_recv = ipctran_pipe_recv, - .p_close = ipctran_pipe_close, - .p_peer = ipctran_pipe_peer, - .p_getopt = ipctran_pipe_getopt, +static nni_tran_pipe_ops ipc_tran_pipe_ops = { + .p_init = ipc_pipe_init, + .p_fini = ipc_pipe_fini, + .p_stop = ipc_pipe_stop, + .p_send = ipc_pipe_send, + .p_recv = ipc_pipe_recv, + .p_close = ipc_pipe_close, + .p_peer = ipc_pipe_peer, + .p_getopt = ipc_pipe_get, }; -static const nni_option ipctran_ep_options[] = { +static const nni_option ipc_ep_options[] = { { .o_name = NNG_OPT_RECVMAXSZ, - .o_get = ipctran_ep_get_recvmaxsz, - .o_set = ipctran_ep_set_recvmaxsz, + .o_get = ipc_ep_get_recv_max_sz, + .o_set = ipc_ep_set_recv_max_sz, }, // terminate list { @@ -1030,13 +1020,12 @@ static const nni_option ipctran_ep_options[] = { }; static int -ipctran_dialer_getopt( - void *arg, const char *name, void *buf, size_t *szp, nni_type t) +ipc_dialer_get(void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; - rv = nni_getopt(ipctran_ep_options, name, ep, buf, szp, t); + rv = nni_getopt(ipc_ep_options, name, ep, buf, szp, t); if (rv == NNG_ENOTSUP) { rv = nni_stream_dialer_getx(ep->dialer, name, buf, szp, t); } @@ -1044,13 +1033,13 @@ ipctran_dialer_getopt( } static int -ipctran_dialer_setopt( +ipc_dialer_set( void *arg, const char *name, const void *buf, size_t sz, nni_type t) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; - rv = nni_setopt(ipctran_ep_options, name, ep, buf, sz, t); + rv = nni_setopt(ipc_ep_options, name, ep, buf, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_stream_dialer_setx(ep->dialer, name, buf, sz, t); } @@ -1058,13 +1047,13 @@ ipctran_dialer_setopt( } static int -ipctran_listener_getopt( +ipc_listener_get( void *arg, const char *name, void *buf, size_t *szp, nni_type t) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; - rv = nni_getopt(ipctran_ep_options, name, ep, buf, szp, t); + rv = nni_getopt(ipc_ep_options, name, ep, buf, szp, t); if (rv == NNG_ENOTSUP) { rv = nni_stream_listener_getx(ep->listener, name, buf, szp, t); } @@ -1072,13 +1061,13 @@ ipctran_listener_getopt( } static int -ipctran_listener_setopt( +ipc_listener_set( void *arg, const char *name, const void *buf, size_t sz, nni_type t) { - ipctran_ep *ep = arg; - int rv; + ipc_ep *ep = arg; + int rv; - rv = nni_setopt(ipctran_ep_options, name, ep, buf, sz, t); + rv = nni_setopt(ipc_ep_options, name, ep, buf, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_stream_listener_setx(ep->listener, name, buf, sz, t); } @@ -1086,15 +1075,15 @@ ipctran_listener_setopt( } static int -ipctran_check_recvmaxsz(const void *v, size_t sz, nni_type t) +ipc_check_recv_max_sz(const void *v, size_t sz, nni_type t) { return (nni_copyin_size(NULL, v, sz, 0, NNI_MAXSZ, t)); } -static nni_chkoption ipctran_checkopts[] = { +static nni_chkoption ipc_check_opts[] = { { .o_name = NNG_OPT_RECVMAXSZ, - .o_check = ipctran_check_recvmaxsz, + .o_check = ipc_check_recv_max_sz, }, { .o_name = NULL, @@ -1102,48 +1091,86 @@ static nni_chkoption ipctran_checkopts[] = { }; static int -ipctran_checkopt(const char *name, const void *buf, size_t sz, nni_type t) +ipc_check_opt(const char *name, const void *buf, size_t sz, nni_type t) { int rv; - rv = nni_chkopt(ipctran_checkopts, name, buf, sz, t); + rv = nni_chkopt(ipc_check_opts, name, buf, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_stream_checkopt("ipc", name, buf, sz, t); } return (rv); } -static nni_tran_dialer_ops ipctran_dialer_ops = { - .d_init = ipctran_ep_init_dialer, - .d_fini = ipctran_ep_fini, - .d_connect = ipctran_ep_connect, - .d_close = ipctran_ep_close, - .d_getopt = ipctran_dialer_getopt, - .d_setopt = ipctran_dialer_setopt, +static nni_tran_dialer_ops ipc_dialer_ops = { + .d_init = ipc_ep_init_dialer, + .d_fini = ipc_ep_fini, + .d_connect = ipc_ep_connect, + .d_close = ipc_ep_close, + .d_getopt = ipc_dialer_get, + .d_setopt = ipc_dialer_set, }; -static nni_tran_listener_ops ipctran_listener_ops = { - .l_init = ipctran_ep_init_listener, - .l_fini = ipctran_ep_fini, - .l_bind = ipctran_ep_bind, - .l_accept = ipctran_ep_accept, - .l_close = ipctran_ep_close, - .l_getopt = ipctran_listener_getopt, - .l_setopt = ipctran_listener_setopt, +static nni_tran_listener_ops ipc_listener_ops = { + .l_init = ipc_ep_init_listener, + .l_fini = ipc_ep_fini, + .l_bind = ipc_ep_bind, + .l_accept = ipc_ep_accept, + .l_close = ipc_ep_close, + .l_getopt = ipc_listener_get, + .l_setopt = ipc_listener_set, }; static nni_tran ipc_tran = { .tran_version = NNI_TRANSPORT_VERSION, .tran_scheme = "ipc", - .tran_dialer = &ipctran_dialer_ops, - .tran_listener = &ipctran_listener_ops, - .tran_pipe = &ipctran_pipe_ops, - .tran_init = ipctran_init, - .tran_fini = ipctran_fini, - .tran_checkopt = ipctran_checkopt, + .tran_dialer = &ipc_dialer_ops, + .tran_listener = &ipc_listener_ops, + .tran_pipe = &ipc_tran_pipe_ops, + .tran_init = ipc_tran_init, + .tran_fini = ipc_tran_fini, + .tran_checkopt = ipc_check_opt, +}; + +#ifdef NNG_PLATFORM_POSIX +static nni_tran ipc_tran_unix = { + .tran_version = NNI_TRANSPORT_VERSION, + .tran_scheme = "unix", + .tran_dialer = &ipc_dialer_ops, + .tran_listener = &ipc_listener_ops, + .tran_pipe = &ipc_tran_pipe_ops, + .tran_init = ipc_tran_init, + .tran_fini = ipc_tran_fini, + .tran_checkopt = ipc_check_opt, +}; +#endif + +#ifdef NNG_HAVE_ABSTRACT_SOCKETS +static nni_tran ipc_tran_abstract = { + .tran_version = NNI_TRANSPORT_VERSION, + .tran_scheme = "abstract", + .tran_dialer = &ipc_dialer_ops, + .tran_listener = &ipc_listener_ops, + .tran_pipe = &ipc_tran_pipe_ops, + .tran_init = ipc_tran_init, + .tran_fini = ipc_tran_fini, + .tran_checkopt = ipc_check_opt, }; +#endif int nng_ipc_register(void) { - return (nni_tran_register(&ipc_tran)); + int rv; + if (((rv = nni_tran_register(&ipc_tran)) != 0) +#ifdef NNG_PLATFORM_POSIX + || ((rv = nni_tran_register(&ipc_tran_unix)) != 0) +#endif +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + || ((rv = nni_tran_register(&ipc_tran_abstract)) != 0) +#endif + ) { + return (rv); + } + + return (0); } diff --git a/src/transport/ipc/ipc_test.c b/src/transport/ipc/ipc_test.c new file mode 100644 index 00000000..23353387 --- /dev/null +++ b/src/transport/ipc/ipc_test.c @@ -0,0 +1,318 @@ +// +// Copyright 2020 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. +// + +#include <nng/nng.h> +#include <nng/protocol/pair0/pair.h> +#include <nng/supplemental/util/platform.h> + +#include <testutil.h> + +#include <acutest.h> + +#ifdef NNG_PLATFORM_POSIX +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#endif + +void +test_path_too_long(void) +{ + nng_socket s1; + char addr[256]; + + // All our names have to be less than 128 bytes. + memset(addr, 'a', 255); + addr[255] = 0; + memcpy(addr, "ipc://", strlen("ipc://")); + + TEST_ASSERT(strlen(addr) == 255); + TEST_NNG_PASS(nng_pair0_open(&s1)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_FAIL(nng_listen(s1, addr, NULL, 0), NNG_EADDRINVAL); + TEST_NNG_FAIL( + nng_dial(s1, addr, NULL, NNG_FLAG_NONBLOCK), NNG_EADDRINVAL); + + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_ipc_dialer_perms(void) +{ + nng_socket s; + nng_dialer d; + char addr[64]; + + testutil_scratch_addr("ipc", sizeof(addr), addr); + + TEST_NNG_PASS(nng_pair0_open(&s)); + TEST_NNG_PASS(nng_dialer_create(&d, s, addr)); + TEST_NNG_FAIL(nng_dialer_setopt_int(d, NNG_OPT_IPC_PERMISSIONS, 0444), + NNG_ENOTSUP); + + TEST_NNG_PASS(nng_close(s)); +} + +void +test_ipc_listener_perms(void) +{ + nng_socket s; + nng_listener l; + char addr[64]; +#ifndef _WIN32 + char * path; + struct stat st; +#endif + + testutil_scratch_addr("ipc", sizeof(addr), addr); + + TEST_NNG_PASS(nng_pair0_open(&s)); + TEST_NNG_PASS(nng_listener_create(&l, s, addr)); + +#ifdef _WIN32 + TEST_NNG_FAIL( + nng_listener_setopt_int(l, NNG_OPT_IPC_PERMISSIONS, 0444), + NNG_ENOTSUP); +#else + path = &addr[strlen("ipc://")]; + + // Attempt to set invalid permissions fails. + TEST_NNG_FAIL( + nng_listener_setopt_int(l, NNG_OPT_IPC_PERMISSIONS, S_IFREG), + NNG_EINVAL); + + TEST_NNG_PASS( + nng_listener_setopt_int(l, NNG_OPT_IPC_PERMISSIONS, 0444)); + TEST_NNG_PASS(nng_listener_start(l, 0)); + TEST_CHECK(stat(path, &st) == 0); + TEST_CHECK((st.st_mode & 0777) == 0444); + + // Now that it's running, we cannot set it. + TEST_NNG_FAIL( + nng_listener_setopt_int(l, NNG_OPT_IPC_PERMISSIONS, 0644), + NNG_EBUSY); +#endif + + TEST_NNG_PASS(nng_close(s)); +} + +void +test_abstract_sockets(void) +{ +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + nng_socket s1; + nng_socket s2; + char addr[64]; + nng_pipe p1; + nng_pipe p2; + nng_sockaddr sa1; + nng_sockaddr sa2; + char * prefix = "abstract://"; + testutil_scratch_addr("abstract", sizeof(addr), addr); + + TEST_NNG_PASS(nng_pair0_open(&s1)); + TEST_NNG_PASS(nng_pair0_open(&s2)); + TEST_NNG_PASS(testutil_marry_ex(s1, s2, addr, &p1, &p2)); + TEST_NNG_PASS(nng_pipe_get_addr(p1, NNG_OPT_REMADDR, &sa1)); + TEST_NNG_PASS(nng_pipe_get_addr(p2, NNG_OPT_LOCADDR, &sa2)); + TEST_CHECK(sa1.s_family == sa2.s_family); + TEST_CHECK(sa1.s_family == NNG_AF_ABSTRACT); + TEST_CHECK(sa1.s_abstract.sa_len == strlen(addr) - strlen(prefix)); + TEST_CHECK(sa2.s_abstract.sa_len == strlen(addr) - strlen(prefix)); + TEST_NNG_SEND_STR(s1, "ping"); + TEST_NNG_RECV_STR(s2, "ping"); + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +#endif +} + +void +test_abstract_auto_bind(void) +{ +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + nng_socket s1; + nng_socket s2; + char addr[40]; + char name[12]; + nng_sockaddr sa; + nng_listener l; + size_t len; + + snprintf(addr, sizeof(addr), "abstract://"); + + TEST_NNG_PASS(nng_pair0_open(&s1)); + TEST_NNG_PASS(nng_pair0_open(&s2)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s2, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s2, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_PASS(nng_listen(s1, addr, &l, 0)); + + TEST_NNG_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); + // Under linux there are either 8 or 5 hex characters. + TEST_CHECK(sa.s_family == NNG_AF_ABSTRACT); + TEST_CHECK(sa.s_abstract.sa_len < 10); + + len = sa.s_abstract.sa_len; + memcpy(name, sa.s_abstract.sa_name, len); + name[len] = '\0'; + TEST_CHECK(strlen(name) == len); + + (void) snprintf(addr, sizeof(addr), "abstract://%s", name); + TEST_NNG_PASS(nng_dial(s2, addr, NULL, 0)); + + // first send the ping + TEST_NNG_SEND_STR(s1, "ping"); + TEST_NNG_RECV_STR(s2, "ping"); + + TEST_NNG_SEND_STR(s2, "pong"); + TEST_NNG_RECV_STR(s1, "pong"); + + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +#endif +} + +void +test_abstract_too_long(void) +{ +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + nng_socket s1; + char addr[256]; + + // All our names have to be less than 128 bytes. + memset(addr, 'a', 255); + addr[255] = 0; + memcpy(addr, "abstract://", strlen("abstract://")); + + TEST_ASSERT(strlen(addr) == 255); + TEST_NNG_PASS(nng_pair0_open(&s1)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_FAIL(nng_listen(s1, addr, NULL, 0), NNG_EADDRINVAL); + TEST_NNG_FAIL( + nng_dial(s1, addr, NULL, NNG_FLAG_NONBLOCK), NNG_EADDRINVAL); + + TEST_NNG_PASS(nng_close(s1)); +#endif +} + +void +test_abstract_null(void) +{ +#ifdef NNG_HAVE_ABSTRACT_SOCKETS + nng_socket s1; + nng_socket s2; + char addr[64]; + char name[40]; + char rng[20]; + + nng_sockaddr sa; + nng_listener l; + size_t len; + + snprintf(rng, sizeof(rng), "%08x%08x", nng_random(), nng_random()); + snprintf(name, sizeof(name), "a%%00b_%s", rng); + snprintf(addr, sizeof(addr), "abstract://%s", name); + + TEST_NNG_PASS(nng_pair0_open(&s1)); + TEST_NNG_PASS(nng_pair0_open(&s2)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s2, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s2, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_PASS(nng_listen(s1, addr, &l, 0)); + + TEST_NNG_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); + // Under linux there are either 8 or 5 hex characters. + TEST_CHECK(sa.s_family == NNG_AF_ABSTRACT); + TEST_CHECK(sa.s_abstract.sa_len < 32); + len = sa.s_abstract.sa_len; + TEST_CHECK(len == 20); + TEST_CHECK(sa.s_abstract.sa_name[0] == 'a'); + TEST_CHECK(sa.s_abstract.sa_name[1] == '\0'); + TEST_CHECK(sa.s_abstract.sa_name[2] == 'b'); + TEST_CHECK(sa.s_abstract.sa_name[3] == '_'); + TEST_CHECK(memcmp(&sa.s_abstract.sa_name[4], rng, 16) == 0); + + TEST_NNG_PASS(nng_dial(s2, addr, NULL, 0)); + + // first send the ping + TEST_NNG_SEND_STR(s1, "1234"); + TEST_NNG_RECV_STR(s2, "1234"); + + TEST_NNG_SEND_STR(s2, "5678"); + TEST_NNG_RECV_STR(s1, "5678"); + + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +#endif +} + +void +test_unix_alias(void) +{ +#ifdef NNG_PLATFORM_POSIX + nng_socket s1; + nng_socket s2; + char addr1[32]; + char addr2[32]; + char rng[20]; + nng_sockaddr sa1; + nng_sockaddr sa2; + nng_msg * msg; + nng_pipe p; + + // Presumes /tmp. + + (void) snprintf( + rng, sizeof(rng), "%08x%08x", nng_random(), nng_random()); + snprintf(addr1, sizeof(addr1), "ipc:///tmp/%s", rng); + snprintf(addr2, sizeof(addr2), "unix:///tmp/%s", rng); + + TEST_NNG_PASS(nng_pair0_open(&s1)); + TEST_NNG_PASS(nng_pair0_open(&s2)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s2, NNG_OPT_SENDTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_PASS(nng_setopt_ms(s2, NNG_OPT_RECVTIMEO, 1000)); + TEST_NNG_PASS(nng_listen(s1, addr1, NULL, 0)); + TEST_NNG_PASS(nng_dial(s2, addr2, NULL, 0)); + + // first send the ping + TEST_NNG_SEND_STR(s1, "ping"); + TEST_NNG_PASS(nng_recvmsg(s2, &msg, 0)); + TEST_ASSERT(msg != NULL); + TEST_CHECK(nng_msg_len(msg) == 5); + TEST_STREQUAL(nng_msg_body(msg), "ping"); + p = nng_msg_get_pipe(msg); + TEST_NNG_PASS(nng_pipe_get_addr(p, NNG_OPT_REMADDR, &sa1)); + TEST_NNG_PASS(nng_pipe_get_addr(p, NNG_OPT_REMADDR, &sa2)); + TEST_CHECK(sa1.s_family == sa2.s_family); + TEST_CHECK(sa1.s_family == NNG_AF_IPC); + TEST_STREQUAL(sa1.s_ipc.sa_path, sa2.s_ipc.sa_path); + nng_msg_free(msg); + + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +#endif +} + +TEST_LIST = { + { "ipc path too long", test_path_too_long }, + { "ipc dialer perms", test_ipc_dialer_perms }, + { "ipc listener perms", test_ipc_listener_perms }, + { "ipc abstract sockets", test_abstract_sockets }, + { "ipc abstract auto bind", test_abstract_auto_bind }, + { "ipc abstract name too long", test_abstract_too_long }, + { "ipc abstract embedded null", test_abstract_null }, + { "ipc unix alias", test_unix_alias }, + { NULL, NULL }, +};
\ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 737aa4d7..a7835826 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -132,6 +132,7 @@ nng_test(platform) nng_test(reconnect) nng_test(resolv) nng_test(sock) +nng_test(url) add_nng_test(device 5) @@ -141,7 +142,6 @@ add_nng_test1(httpclient 60 NNG_SUPP_HTTP) add_nng_test1(httpserver 30 NNG_SUPP_HTTP) add_nng_test(inproc 5) add_nng_test(ipc 5) -add_nng_test(ipcperms 5) add_nng_test(ipcsupp 10) add_nng_test(ipcwinsec 5) add_nng_test(list 5) @@ -161,7 +161,6 @@ add_nng_test(tcp 180) add_nng_test(tcp6 60) add_nng_test(transport 5) add_nng_test(udp 5) -add_nng_test(url 5) add_nng_test(ws 30) add_nng_test(wss 30) add_nng_test1(zt 60 NNG_TRANSPORT_ZEROTIER) diff --git a/tests/ipcperms.c b/tests/ipcperms.c deleted file mode 100644 index 5f4a847c..00000000 --- a/tests/ipcperms.c +++ /dev/null @@ -1,120 +0,0 @@ -// -// 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. -// - -#ifndef _WIN32 -#include <errno.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/un.h> -#include <unistd.h> -#endif - -#include <nng/nng.h> -#include <nng/protocol/reqrep0/rep.h> -#include <nng/protocol/reqrep0/req.h> -#include <nng/transport/ipc/ipc.h> - -#include "convey.h" -#include "stubs.h" -#include "trantest.h" - -#define ADDR "/tmp/ipc_perms_test" - -#if defined(__sun) -#define honor_chmod() false -#else -#define honor_chmod() true -#endif - -// Inproc tests. - -#ifdef _WIN32 -TestMain("IPC Permissions", { - atexit(nng_fini); - Convey("Given a socket and an IPC listener", { - nng_socket s; - nng_listener l; - - So(nng_rep0_open(&s) == 0); - Reset({ nng_close(s); }); - So(nng_listener_create(&l, s, "ipc://" ADDR) == 0); - Convey("We cannot set perms on Windows", { - So(nng_listener_setopt_int(l, NNG_OPT_IPC_PERMISSIONS, - 0444) == NNG_ENOTSUP); - }); - }); -}) -#else -TestMain("IPC Permissions", { - atexit(nng_fini); - - Convey("Given a socket and an IPC listener", { - nng_socket s; - nng_listener l; - - So(nng_rep0_open(&s) == 0); - Reset({ - nng_close(s); - unlink(ADDR); - }); - So(nng_listener_create(&l, s, "ipc://" ADDR) == 0); - Convey("We can set perms on POSIX", { - struct stat st; - So(nng_listener_setopt_int( - l, NNG_OPT_IPC_PERMISSIONS, 0444) == 0); - So(nng_listener_start(l, 0) == 0); - So(stat(ADDR, &st) == 0); - So((st.st_mode & 0777) == 0444); - - Convey("And permissions are honored", { - struct sockaddr_un sa; - int cfd; - - if (geteuid() == 0) { - Skip("Running as root"); - } - if (!honor_chmod()) { - Skip("System does not honor chmod"); - } - strcpy(sa.sun_path, ADDR); - sa.sun_family = AF_UNIX; - So((cfd = socket(AF_UNIX, SOCK_STREAM, 0)) >= - 0); - Reset({ close(cfd); }); - So(connect(cfd, (void *) &sa, sizeof(sa)) < 0); - So(errno == EACCES); - }); - }); - - Convey("We cannot set perms after it is started", { - So(nng_listener_start(l, 0) == 0); - So(nng_listener_setopt_int( - l, NNG_OPT_IPC_PERMISSIONS, 0444) == NNG_EBUSY); - }); - - Convey("We cannot set bogus permissions", { - So(nng_listener_setopt_int(l, NNG_OPT_IPC_PERMISSIONS, - S_IFREG) == NNG_EINVAL); - }); - }); - - Convey("We cannot set perms on an IPC dialer", { - nng_socket s; - nng_dialer d; - - So(nng_rep0_open(&s) == 0); - Reset({ nng_close(s); }); - So(nng_dialer_create(&d, s, "ipc://" ADDR) == 0); - So(nng_dialer_setopt_int(d, NNG_OPT_IPC_PERMISSIONS, 0444) == - NNG_ENOTSUP); - }); -}) -#endif diff --git a/tests/testutil.c b/tests/testutil.c index 58de1524..7068e394 100644 --- a/tests/testutil.c +++ b/tests/testutil.c @@ -136,7 +136,8 @@ testutil_htonl(uint32_t in) void testutil_scratch_addr(const char *scheme, size_t sz, char *addr) { - if (strcmp(scheme, "inproc") == 0) { + if ((strcmp(scheme, "inproc") == 0) || + (strcmp(scheme, "abstract") == 0)) { (void) snprintf(addr, sz, "%s://testutil%04x%04x%04x%04x", scheme, nng_random(), nng_random(), nng_random(), nng_random()); @@ -158,7 +159,8 @@ testutil_scratch_addr(const char *scheme, size_t sz, char *addr) return; } - if (strncmp(scheme, "ipc", 3) == 0) { + if ((strncmp(scheme, "ipc", 3) == 0) || + (strncmp(scheme, "unix", 4) == 0)) { #ifdef _WIN32 // Windows doesn't place IPC names in the filesystem. (void) snprintf(addr, sz, "%s://testutil%04x%04x%04x%04x", @@ -476,10 +478,10 @@ stream_xfr_alloc(nng_stream *s, void (*submit)(nng_stream *, nng_aio *), nng_aio_begin(x->upper_aio); - x->s = s; - x->rem = size; - x->base = buf; - x->submit = submit; + x->s = s; + x->rem = size; + x->base = buf; + x->submit = submit; return (x); } diff --git a/tests/testutil.h b/tests/testutil.h index 15028106..eb7ab7d1 100644 --- a/tests/testutil.h +++ b/tests/testutil.h @@ -63,8 +63,8 @@ extern int testutil_marry_ex( // success, an NNG error number otherwise.) extern void *testutil_stream_send_start(nng_stream *, void *, size_t); extern void *testutil_stream_recv_start(nng_stream *, void *, size_t); -extern int testutil_stream_send_wait(void *); -extern int testutil_stream_recv_wait(void *); +extern int testutil_stream_send_wait(void *); +extern int testutil_stream_recv_wait(void *); // These are TLS certificates. The client and server are signed with the // root. The server uses CN 127.0.0.1. Other details are bogus, but @@ -111,6 +111,16 @@ extern const char *testutil_client_crt; strcmp(string, buf_) == 0, "%s == %s", string, buf_); \ } while (0) +#define TEST_STREQUAL(s1, s2) \ + do { \ + TEST_CHECK_(strcmp(s1, s2) == 0, "%s == %s", s1, s2); \ + } while (0) + +#define TEST_NULL(x) \ + do { \ + TEST_CHECK_((x) == NULL, "%p == NULL", x); \ + } while (0) + #ifdef __cplusplus }; #endif diff --git a/tests/url.c b/tests/url.c index 9b44dda4..847b7df3 100644 --- a/tests/url.c +++ b/tests/url.c @@ -8,325 +8,472 @@ // found online at https://opensource.org/licenses/MIT. // + +#include "acutest.h" + #include <string.h> #include <nng/nng.h> -#include "convey.h" +#include "core/url.h" + +#include "testutil.h" + +void +test_url_host(void) +{ + nng_url *url; + + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com")); + TEST_ASSERT(url != NULL); + TEST_CHECK(strcmp(url->u_scheme, "http") == 0); + TEST_CHECK(strcmp(url->u_host, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_hostname, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_port, "80") == 0); + TEST_CHECK(strcmp(url->u_path, "") == 0); + TEST_CHECK(strcmp(url->u_requri, "") == 0); + TEST_CHECK(url->u_query == NULL); + TEST_CHECK(url->u_fragment == NULL); + TEST_CHECK(url->u_userinfo == NULL); + nng_url_free(url); +} + +void +test_url_host_port(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com:1234")); + TEST_ASSERT(url != NULL); + TEST_CHECK(strcmp(url->u_scheme, "http") == 0); + TEST_CHECK(strcmp(url->u_host, "www.google.com:1234") == 0); + TEST_CHECK(strcmp(url->u_hostname, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_port, "1234") == 0); + TEST_CHECK(strcmp(url->u_path, "") == 0); + TEST_CHECK(strcmp(url->u_requri, "") == 0); + TEST_CHECK(url->u_query == NULL); + TEST_CHECK(url->u_fragment == NULL); + TEST_CHECK(url->u_userinfo == NULL); + nng_url_free(url); +} + +void +test_url_host_port_path(void) +{ + nng_url *url; + + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.google.com:1234/somewhere")); + TEST_ASSERT(url != NULL); + TEST_CHECK(strcmp(url->u_scheme, "http") == 0); + TEST_CHECK(strcmp(url->u_host, "www.google.com:1234") == 0); + TEST_CHECK(strcmp(url->u_hostname, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_port, "1234") == 0); + TEST_CHECK(strcmp(url->u_path, "/somewhere") == 0); + TEST_CHECK(strcmp(url->u_requri, "/somewhere") == 0); + TEST_CHECK(url->u_userinfo == NULL); + TEST_CHECK(url->u_query == NULL); + TEST_CHECK(url->u_fragment == NULL); + nng_url_free(url); +} + +void +test_url_user_info(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse( + &url, "http://garrett@www.google.com:1234/somewhere")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_userinfo, "garrett"); + TEST_STREQUAL(url->u_host, "www.google.com:1234"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "1234"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_requri, "/somewhere"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + nng_url_free(url); +} + +void +test_url_path_query_param(void) +{ + nng_url *url; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.google.com/somewhere?result=yes")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_query, "result=yes"); + TEST_STREQUAL(url->u_requri, "/somewhere?result=yes"); + TEST_NULL(url->u_userinfo); + TEST_NULL(url->u_fragment); + nng_url_free(url); +} + +void +test_url_query_param_anchor(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, + "http://www.google.com/" + "somewhere?result=yes#chapter1")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_query, "result=yes"); + TEST_STREQUAL(url->u_fragment, "chapter1"); + TEST_STREQUAL(url->u_requri, "/somewhere?result=yes#chapter1"); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_path_anchor(void) +{ + nng_url *url; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.google.com/somewhere#chapter2")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_fragment, "chapter2"); + TEST_STREQUAL(url->u_requri, "/somewhere#chapter2"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_anchor(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com#chapter3")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_fragment, "chapter3"); + TEST_STREQUAL(url->u_requri, "#chapter3"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_query_param(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com?color=red")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_query, "color=red"); + TEST_STREQUAL(url->u_requri, "?color=red"); + TEST_ASSERT(url != NULL); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_v6_host(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://[::1]")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "[::1]"); + TEST_STREQUAL(url->u_hostname, "::1"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_v6_host_port(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://[::1]:29")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "[::1]:29"); + TEST_STREQUAL(url->u_hostname, "::1"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "29"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_v6_host_port_path(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://[::1]:29/bottles")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "[::1]:29"); + TEST_STREQUAL(url->u_hostname, "::1"); + TEST_STREQUAL(url->u_path, "/bottles"); + TEST_STREQUAL(url->u_port, "29"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_tcp_port(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "tcp://:9876/")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "tcp"); + TEST_STREQUAL(url->u_host, ":9876"); + TEST_STREQUAL(url->u_hostname, ""); + TEST_STREQUAL(url->u_path, "/"); + TEST_STREQUAL(url->u_port, "9876"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_bare_ws(void) +{ + nng_url *url; + + TEST_NNG_PASS(nng_url_parse(&url, "ws://")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "ws"); + TEST_STREQUAL(url->u_host, ""); + TEST_STREQUAL(url->u_hostname, ""); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_ws_wildcard(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "ws://*:12345/foobar")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "ws"); + TEST_STREQUAL(url->u_host, ":12345"); + TEST_STREQUAL(url->u_hostname, ""); + TEST_STREQUAL(url->u_path, "/foobar"); + TEST_STREQUAL(url->u_port, "12345"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} -TestMain("URLs", { +void +test_url_ssh(void) +{ nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "ssh://user@host.example.com")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "ssh"); + TEST_STREQUAL(url->u_host, "host.example.com"); + TEST_STREQUAL(url->u_hostname, "host.example.com"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "22"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_STREQUAL(url->u_userinfo, "user"); + nng_url_free(url); +} + +void +test_url_bad_scheme(void) +{ + nng_url *url = NULL; + TEST_NNG_FAIL(nng_url_parse(&url, "www.google.com"), NNG_EINVAL); + TEST_NULL(url); + TEST_NNG_FAIL(nng_url_parse(&url, "http:www.google.com"), NNG_EINVAL); + TEST_NULL(url); +} + +void +test_url_bad_ipv6(void) +{ + nng_url *url = NULL; + TEST_NNG_FAIL(nng_url_parse(&url, "http://[::1"), NNG_EINVAL); + TEST_NULL(url); + TEST_NNG_FAIL(nng_url_parse(&url, "http://[::1]bogus"), NNG_EINVAL); + TEST_NULL(url); +} + +void +test_url_canonify(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS(nng_url_parse( + &url, "hTTp://www.EXAMPLE.com/bogus/.%2e/%7egarrett")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.example.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/~garrett"); + nng_url_free(url); +} + +void +test_url_path_resolve(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.x.com//abc/def/./x/..///./../y")); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.x.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/abc/y"); + nng_url_free(url); +} + +void +test_url_query_info_pass(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.x.com/?/abc/def/./x/.././../y")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.x.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/"); + TEST_STREQUAL(url->u_query, "/abc/def/./x/.././../y"); + nng_url_free(url); +} + +void +test_url_bad_utf8(void) +{ + nng_url *url = NULL; + TEST_NNG_FAIL(nng_url_parse(&url, "http://x.com/x%80x"), NNG_EINVAL); + TEST_NULL(url); + TEST_NNG_FAIL(nng_url_parse(&url, "http://x.com/x%c0%81"), NNG_EINVAL); + TEST_NULL(url); +} + +void +test_url_good_utf8(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.x.com/%c2%a2_cents")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.x.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/\xc2\xa2_cents"); + nng_url_free(url); +} + +void +test_url_decode(void) +{ + uint8_t out[16]; + size_t len; + + out[3] = 'x'; + len = nni_url_decode(out, "abc", 3); + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "abc", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "x%00y", 3); // embedded NULL + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "x\x00y", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "%3987", 3); + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "987", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "78%39", 3); + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "789", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "", 5); + TEST_CHECK(len == 0); + TEST_CHECK(memcmp(out, "789", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "be", 2); + TEST_CHECK(len == 2); + TEST_CHECK(memcmp(out, "be9", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "78%39", 2); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "", 2); + TEST_CHECK(len == 0); + + len = nni_url_decode(out, "78%", 5); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "78%xy", 5); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "78%1$", 5); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "%%20", 5); + TEST_CHECK(len == (size_t) -1); +} - Convey("http://www.google.com", { - So(nng_url_parse(&url, "http://www.google.com") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_requri, "") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("http://www.google.com:1234", { - So(nng_url_parse(&url, "http://www.google.com:1234") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com:1234") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_port, "1234") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_requri, "") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("http://www.google.com:1234/somewhere", { - So(nng_url_parse( - &url, "http://www.google.com:1234/somewhere") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com:1234") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_port, "1234") == 0); - So(strcmp(url->u_path, "/somewhere") == 0); - So(strcmp(url->u_requri, "/somewhere") == 0); - So(url->u_userinfo == NULL); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - nng_url_free(url); - }); - Convey("http://garrett@www.google.com:1234/somewhere", { - So(nng_url_parse(&url, - "http://garrett@www.google.com:1234/somewhere") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_userinfo, "garrett") == 0); - So(strcmp(url->u_host, "www.google.com:1234") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_port, "1234") == 0); - So(strcmp(url->u_path, "/somewhere") == 0); - So(strcmp(url->u_requri, "/somewhere") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - nng_url_free(url); - }); - Convey("http://www.google.com/somewhere?result=yes", { - So(nng_url_parse(&url, - "http://www.google.com/somewhere?result=yes") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "/somewhere") == 0); - So(strcmp(url->u_query, "result=yes") == 0); - So(strcmp(url->u_requri, "/somewhere?result=yes") == 0); - So(url->u_userinfo == NULL); - So(url->u_fragment == NULL); - nng_url_free(url); - }); - Convey("http://www.google.com/somewhere?result=yes#chapter1", { - So(nng_url_parse(&url, - "http://www.google.com/" - "somewhere?result=yes#chapter1") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "/somewhere") == 0); - So(strcmp(url->u_query, "result=yes") == 0); - So(strcmp(url->u_fragment, "chapter1") == 0); - So(strcmp(url->u_requri, "/somewhere?result=yes#chapter1") == - 0); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - Convey("http://www.google.com/somewhere#chapter2", { - So(nng_url_parse( - &url, "http://www.google.com/somewhere#chapter2") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "/somewhere") == 0); - So(strcmp(url->u_fragment, "chapter2") == 0); - So(strcmp(url->u_requri, "/somewhere#chapter2") == 0); - So(url->u_query == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - Convey("http://www.google.com#chapter3", { - So(nng_url_parse(&url, "http://www.google.com#chapter3") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_fragment, "chapter3") == 0); - So(strcmp(url->u_requri, "#chapter3") == 0); - So(url->u_query == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - Convey("http://www.google.com?color=red", { - So(nng_url_parse(&url, "http://www.google.com?color=red") == - 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "www.google.com") == 0); - So(strcmp(url->u_hostname, "www.google.com") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_query, "color=red") == 0); - So(strcmp(url->u_requri, "?color=red") == 0); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("http://[::1]", { - So(nng_url_parse(&url, "http://[::1]") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "[::1]") == 0); - So(strcmp(url->u_hostname, "::1") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_port, "80") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("http://[::1]:29", { - So(nng_url_parse(&url, "http://[::1]:29") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "[::1]:29") == 0); - So(strcmp(url->u_hostname, "::1") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_port, "29") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - Convey("http://[::1]:29/bottles", { - So(nng_url_parse(&url, "http://[::1]:29/bottles") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_host, "[::1]:29") == 0); - So(strcmp(url->u_hostname, "::1") == 0); - So(strcmp(url->u_path, "/bottles") == 0); - So(strcmp(url->u_port, "29") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("tcp://:9876/", { - So(nng_url_parse(&url, "tcp://:9876/") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "tcp") == 0); - So(strcmp(url->u_host, ":9876") == 0); - So(strcmp(url->u_hostname, "") == 0); - So(strcmp(url->u_path, "/") == 0); - So(strcmp(url->u_port, "9876") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("ws://", { - So(nng_url_parse(&url, "ws://") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "ws") == 0); - So(strcmp(url->u_host, "") == 0); - So(strcmp(url->u_hostname, "") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_port, "80") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("ws://*:12345/foobar", { - So(nng_url_parse(&url, "ws://*:12345/foobar") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "ws") == 0); - So(strcmp(url->u_host, ":12345") == 0); - So(strcmp(url->u_hostname, "") == 0); - So(strcmp(url->u_path, "/foobar") == 0); - So(strcmp(url->u_port, "12345") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(url->u_userinfo == NULL); - nng_url_free(url); - }); - - Convey("ssh://user@host.example.com", { - So(nng_url_parse(&url, "ssh://user@host.example.com") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "ssh") == 0); - So(strcmp(url->u_host, "host.example.com") == 0); - So(strcmp(url->u_hostname, "host.example.com") == 0); - So(strcmp(url->u_path, "") == 0); - So(strcmp(url->u_port, "22") == 0); - So(url->u_query == NULL); - So(url->u_fragment == NULL); - So(strcmp(url->u_userinfo, "user") == 0); - nng_url_free(url); - }); - - Convey("Negative www.google.com", { - url = NULL; - So(nng_url_parse(&url, "www.google.com") == NNG_EINVAL); - So(url == NULL); - }); - - Convey("Negative http:www.google.com", { - url = NULL; - So(nng_url_parse(&url, "http:www.google.com") == NNG_EINVAL); - So(url == NULL); - }); - - Convey("Negative http://[::1", { - url = NULL; - So(nng_url_parse(&url, "http://[::1") == NNG_EINVAL); - So(url == NULL); - }); - - Convey("Negative http://[::1]bogus", { - url = NULL; - So(nng_url_parse(&url, "http://[::1]bogus") == NNG_EINVAL); - So(url == NULL); - }); - - Convey("Canonicalization works", { - url = NULL; - So(nng_url_parse(&url, - "hTTp://www.EXAMPLE.com/bogus/.%2e/%7egarrett") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_hostname, "www.example.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "/~garrett") == 0); - nng_url_free(url); - }); - - Convey("Path resolution works", { - url = NULL; - So(nng_url_parse(&url, - "http://www.x.com//abc/def/./x/..///./../y") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_hostname, "www.x.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "/abc/y") == 0); - nng_url_free(url); - }); - - Convey("Query info unmolested", { - url = NULL; - So(nng_url_parse( - &url, "http://www.x.com/?/abc/def/./x/.././../y") == 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_hostname, "www.x.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "/") == 0); - So(strcmp(url->u_query, "/abc/def/./x/.././../y") == 0); - nng_url_free(url); - }); - - Convey("Bad UTF-8 fails", { - url = NULL; - So(nng_url_parse(&url, "http://x.com/x%80x") == NNG_EINVAL); - So(nng_url_parse(&url, "http://x.com/x%c0%81") == NNG_EINVAL); - }); - - Convey("Valid UTF-8 works", { - url = NULL; - So(nng_url_parse(&url, "http://www.x.com/%c2%a2_centsign") == - 0); - So(url != NULL); - So(strcmp(url->u_scheme, "http") == 0); - So(strcmp(url->u_hostname, "www.x.com") == 0); - So(strcmp(url->u_port, "80") == 0); - So(strcmp(url->u_path, "/\xc2\xa2_centsign") == 0); - nng_url_free(url); - }); -}) +TEST_LIST = { + { "url host", test_url_host }, + { "url host port", test_url_host_port }, + { "url host port path", test_url_host_port_path }, + { "url user info", test_url_user_info }, + { "url path query param", test_url_path_query_param }, + { "url query param anchor", test_url_query_param_anchor }, + { "url path anchor", test_url_path_anchor }, + { "url anchor", test_url_anchor }, + { "url query param", test_url_query_param }, + { "url v6 host", test_url_v6_host }, + { "url v6 host port", test_url_v6_host_port }, + { "url v6 host port path", test_url_v6_host_port_path }, + { "url tcp port", test_url_tcp_port }, + { "url bare ws", test_url_bare_ws }, + { "url ws wildcard", test_url_ws_wildcard }, + { "url ssh", test_url_ssh }, + { "url bad scheme", test_url_bad_scheme }, + { "url bad v6", test_url_bad_ipv6 }, + { "url canonify", test_url_canonify }, + { "url path resolve", test_url_path_resolve }, + { "url query info pass", test_url_query_info_pass }, + { "url bad utf8", test_url_bad_utf8 }, + { "url good utf8", test_url_good_utf8 }, + { "url decode", test_url_decode }, + { NULL, NULL }, +};
\ No newline at end of file |
