aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2018-05-22 10:27:47 -0700
committerGarrett D'Amore <garrett@damore.org>2018-05-22 10:29:06 -0700
commit959eabe2675a3b8be9bc2b2459cc899a5a64b283 (patch)
tree373fba9ab3c7cf4c769efb1c42f896694bf16d25
parent67f5ed6e5c0dd7bdd9002bbb519ab34f35fef8dd (diff)
downloadnng-959eabe2675a3b8be9bc2b2459cc899a5a64b283.tar.gz
nng-959eabe2675a3b8be9bc2b2459cc899a5a64b283.tar.bz2
nng-959eabe2675a3b8be9bc2b2459cc899a5a64b283.zip
fixes #474 websocket listen on ws://*:<x> fails
fixes #464 Support NN_WS_MSG_TYPE option (compat) fixes #415 websocket does not honor recv maxsize This fixes a significant (and security) issue in websocket, where the code does not honor a maximum receive size. We've exposed new API (internal) to set the limit on the frame size, and we've changed the default to *unlimited* for that internal API. (But the default for SP sockets, which are the only consumers at present, is still 1MB just like all other SP transports.)
-rw-r--r--src/compat/nanomsg/nn.c33
-rw-r--r--src/core/url.c6
-rw-r--r--src/supplemental/http/http_server.c35
-rw-r--r--src/supplemental/websocket/websocket.c46
-rw-r--r--src/supplemental/websocket/websocket.h6
-rw-r--r--src/transport/ws/websocket.c8
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/compat_ws.c281
8 files changed, 385 insertions, 31 deletions
diff --git a/src/compat/nanomsg/nn.c b/src/compat/nanomsg/nn.c
index 564e60d8..bc27832e 100644
--- a/src/compat/nanomsg/nn.c
+++ b/src/compat/nanomsg/nn.c
@@ -749,6 +749,33 @@ nn_setignore(nng_socket s, const void *valp, size_t sz)
}
static int
+nn_getwsmsgtype(nng_socket s, void *valp, size_t *szp)
+{
+ int val = NN_WS_MSG_TYPE_BINARY;
+ NNI_ARG_UNUSED(s);
+ memcpy(valp, &val, *szp < sizeof(val) ? *szp : sizeof(val));
+ *szp = sizeof(val);
+ return (0);
+}
+
+static int
+nn_setwsmsgtype(nng_socket s, const void *valp, size_t sz)
+{
+ int val;
+ NNI_ARG_UNUSED(s);
+ if (sz != sizeof(val)) {
+ nn_seterror(NNG_EINVAL);
+ return (-1);
+ }
+ memcpy(&val, valp, sizeof(val));
+ if (val != NN_WS_MSG_TYPE_BINARY) {
+ nn_seterror(NNG_EINVAL);
+ return (-1);
+ }
+ return (0);
+}
+
+static int
nn_settcpnodelay(nng_socket s, const void *valp, size_t sz)
{
bool val;
@@ -1039,6 +1066,12 @@ static const struct {
.nnopt = NN_TCP_NODELAY,
.get = nn_gettcpnodelay,
.set = nn_settcpnodelay,
+ },
+ {
+ .nnlevel = NN_WS,
+ .nnopt = NN_WS_MSG_TYPE,
+ .get = nn_getwsmsgtype,
+ .set = nn_setwsmsgtype,
}
// XXX: IPV4ONLY, SNDPRIO, RCVPRIO
};
diff --git a/src/core/url.c b/src/core/url.c
index 93f1298e..7d45f82d 100644
--- a/src/core/url.c
+++ b/src/core/url.c
@@ -421,6 +421,12 @@ nni_url_parse(nni_url **urlp, const char *raw)
s++; // skip over ']', only used with IPv6 addresses
}
if (s[0] == ':') {
+ // If a colon was present, but no port value present, then
+ // that is an error.
+ if (s[1] == '\0') {
+ rv = NNG_EINVAL;
+ goto error;
+ }
url->u_port = nni_strdup(s + 1);
} else {
url->u_port = nni_strdup(nni_url_default_port(url->u_scheme));
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index c7738aee..5c6d1dcc 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -30,7 +30,7 @@ static nni_initializer http_server_initializer = {
struct nng_http_handler {
nni_list_node node;
- char * path;
+ char * uri;
char * method;
char * host;
bool tree;
@@ -75,14 +75,18 @@ struct nng_http_server {
int
nni_http_handler_init(
- nni_http_handler **hp, const char *path, void (*cb)(nni_aio *))
+ nni_http_handler **hp, const char *uri, void (*cb)(nni_aio *))
{
nni_http_handler *h;
if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
return (NNG_ENOMEM);
}
- if (((h->path = nni_strdup(path)) == NULL) ||
+ // Default for HTTP is /.
+ if ((uri == NULL) || (strlen(uri) == 0)) {
+ uri = "/";
+ }
+ if (((h->uri = nni_strdup(uri)) == NULL) ||
((h->method = nni_strdup("GET")) == NULL)) {
nni_http_handler_fini(h);
return (NNG_ENOMEM);
@@ -108,7 +112,7 @@ nni_http_handler_fini(nni_http_handler *h)
h->dtor(h->data);
}
nni_strfree(h->host);
- nni_strfree(h->path);
+ nni_strfree(h->uri);
nni_strfree(h->method);
NNI_FREE_STRUCT(h);
}
@@ -133,7 +137,7 @@ nni_http_handler_get_data(nni_http_handler *h)
const char *
nni_http_handler_get_uri(nni_http_handler *h)
{
- return (h->path);
+ return (h->uri);
}
int
@@ -383,8 +387,7 @@ http_uri_canonify(char *path)
}
*dst = '\0';
- // XXX: this is where we should also remove /./ and /../ references.
- return (path);
+ return ((strlen(path) != 0) ? path : "/");
}
static void
@@ -527,8 +530,8 @@ http_sconn_rxdone(void *arg)
}
}
- len = strlen(h->path);
- if (strncmp(path, h->path, len) != 0) {
+ len = strlen(h->uri);
+ if (strncmp(path, h->uri, len) != 0) {
continue;
}
switch (path[len]) {
@@ -957,11 +960,11 @@ nni_http_server_add_handler(nni_http_server *s, nni_http_handler *h)
// Must have a legal method (and not one that is HEAD), path,
// and handler. (The reason HEAD is verboten is that we supply
// it automatically as part of GET support.)
- if (((len = strlen(h->path)) == 0) || (h->path[0] != '/') ||
+ if (((len = strlen(h->uri)) == 0) || (h->uri[0] != '/') ||
(h->cb == NULL)) {
return (NNG_EINVAL);
}
- while ((len > 0) && (h->path[len - 1] == '/')) {
+ while ((len > 0) && (h->uri[len - 1] == '/')) {
len--; // ignore trailing '/' (this collapses them)
}
@@ -992,22 +995,22 @@ nni_http_server_add_handler(nni_http_server *s, nni_http_handler *h)
continue;
}
- len2 = strlen(h2->path);
+ len2 = strlen(h2->uri);
- while ((len2 > 0) && (h2->path[len2 - 1] == '/')) {
+ while ((len2 > 0) && (h2->uri[len2 - 1] == '/')) {
len2--; // ignore trailing '/'
}
- if (strncmp(h->path, h2->path, len > len2 ? len2 : len) != 0) {
+ if (strncmp(h->uri, h2->uri, len > len2 ? len2 : len) != 0) {
continue; // prefixes don't match.
}
if (len2 > len) {
- if ((h2->path[len] == '/') && (h->tree)) {
+ if ((h2->uri[len] == '/') && (h->tree)) {
nni_mtx_unlock(&s->mtx);
return (NNG_EADDRINUSE);
}
} else if (len > len2) {
- if ((h->path[len2] == '/') && (h2->tree)) {
+ if ((h->uri[len2] == '/') && (h2->tree)) {
nni_mtx_unlock(&s->mtx);
return (NNG_EADDRINUSE);
}
diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c
index 6d4d3c13..18491190 100644
--- a/src/supplemental/websocket/websocket.c
+++ b/src/supplemental/websocket/websocket.c
@@ -75,6 +75,7 @@ struct nni_ws_listener {
nni_ws_listen_hook hookfn;
void * hookarg;
nni_list headers; // response headers
+ size_t maxframe;
};
// The dialer tracks user aios in two lists. The first list is for aios
@@ -94,6 +95,7 @@ struct nni_ws_dialer {
bool closed;
nng_sockaddr sa;
nni_list headers; // request headers
+ size_t maxframe;
};
typedef enum ws_type {
@@ -945,7 +947,7 @@ ws_read_cb(void *arg)
break;
}
- if (frame->len > ws->maxframe) {
+ if ((frame->len > ws->maxframe) && (ws->maxframe > 0)) {
ws_close(ws, WS_CLOSE_TOO_BIG);
nni_mtx_unlock(&ws->mtx);
return;
@@ -1380,7 +1382,6 @@ ws_init(nni_ws **wsp)
nni_aio_set_timeout(ws->httpaio, 2000);
ws->fragsize = 1 << 20; // we won't send a frame larger than this
- ws->maxframe = (1 << 20) * 10; // default limit on incoming frame size
*wsp = ws;
return (0);
}
@@ -1557,12 +1558,13 @@ ws_handler(nni_aio *aio)
status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
goto err;
}
- ws->http = conn;
- ws->req = req;
- ws->res = res;
- ws->mode = NNI_EP_MODE_LISTEN;
+ ws->http = conn;
+ ws->req = req;
+ ws->res = res;
+ ws->mode = NNI_EP_MODE_LISTEN;
+ ws->maxframe = l->maxframe;
- // XXX: Inherit fragmentation and message size limits!
+ // XXX: Inherit fragmentation? (Frag is limited for now).
nni_list_append(&l->reply, ws);
nni_aio_set_data(ws->httpaio, 0, l);
@@ -1621,7 +1623,8 @@ nni_ws_listener_init(nni_ws_listener **wslp, nni_url *url)
return (rv);
}
- *wslp = l;
+ l->maxframe = 0;
+ *wslp = l;
return (0);
}
@@ -1785,6 +1788,14 @@ nni_ws_listener_get_tls(nni_ws_listener *l, nng_tls_config **tlsp)
}
void
+nni_ws_listener_set_maxframe(nni_ws_listener *l, size_t maxframe)
+{
+ nni_mtx_lock(&l->mtx);
+ l->maxframe = maxframe;
+ nni_mtx_unlock(&l->mtx);
+}
+
+void
ws_conn_cb(void *arg)
{
nni_ws_dialer *d;
@@ -1920,8 +1931,8 @@ nni_ws_dialer_init(nni_ws_dialer **dp, nni_url *url)
nni_ws_dialer_fini(d);
return (rv);
}
-
- *dp = d;
+ d->maxframe = 0;
+ *dp = d;
return (0);
}
@@ -2021,9 +2032,10 @@ nni_ws_dialer_dial(nni_ws_dialer *d, nni_aio *aio)
ws_fini(ws);
return;
}
- ws->dialer = d;
- ws->useraio = aio;
- ws->mode = NNI_EP_MODE_DIAL;
+ ws->dialer = d;
+ ws->useraio = aio;
+ ws->mode = NNI_EP_MODE_DIAL;
+ ws->maxframe = d->maxframe;
nni_list_append(&d->wspend, ws);
nni_http_client_connect(d->client, ws->connaio);
nni_mtx_unlock(&d->mtx);
@@ -2071,6 +2083,14 @@ nni_ws_dialer_header(nni_ws_dialer *d, const char *n, const char *v)
return (rv);
}
+void
+nni_ws_dialer_set_maxframe(nni_ws_dialer *d, size_t maxframe)
+{
+ nni_mtx_lock(&d->mtx);
+ d->maxframe = maxframe;
+ nni_mtx_unlock(&d->mtx);
+}
+
// Dialer does not get a hook chance, as it can examine the request
// and reply after dial is done; this is not a 3-way handshake, so
// the dialer does not confirm the server's response at the HTTP
diff --git a/src/supplemental/websocket/websocket.h b/src/supplemental/websocket/websocket.h
index 546f41a9..9936b10f 100644
--- a/src/supplemental/websocket/websocket.h
+++ b/src/supplemental/websocket/websocket.h
@@ -33,14 +33,16 @@ extern int nni_ws_listener_listen(nni_ws_listener *);
extern void nni_ws_listener_accept(nni_ws_listener *, nng_aio *);
extern void nni_ws_listener_hook(
nni_ws_listener *, nni_ws_listen_hook, void *);
-extern int nni_ws_listener_set_tls(nni_ws_listener *, nng_tls_config *);
-extern int nni_ws_listener_get_tls(nni_ws_listener *, nng_tls_config **s);
+extern int nni_ws_listener_set_tls(nni_ws_listener *, nng_tls_config *);
+extern int nni_ws_listener_get_tls(nni_ws_listener *, nng_tls_config **s);
+extern void nni_ws_listener_set_maxframe(nni_ws_listener *, size_t);
extern int nni_ws_dialer_init(nni_ws_dialer **, nni_url *);
extern void nni_ws_dialer_fini(nni_ws_dialer *);
extern void nni_ws_dialer_close(nni_ws_dialer *);
extern int nni_ws_dialer_proto(nni_ws_dialer *, const char *);
extern int nni_ws_dialer_header(nni_ws_dialer *, const char *, const char *);
+extern void nni_ws_dialer_set_maxframe(nni_ws_dialer *, size_t);
extern void nni_ws_dialer_dial(nni_ws_dialer *, nng_aio *);
extern int nni_ws_dialer_set_tls(nni_ws_dialer *, nng_tls_config *);
extern int nni_ws_dialer_get_tls(nni_ws_dialer *, nng_tls_config **);
diff --git a/src/transport/ws/websocket.c b/src/transport/ws/websocket.c
index 36deddac..97175d49 100644
--- a/src/transport/ws/websocket.c
+++ b/src/transport/ws/websocket.c
@@ -284,7 +284,9 @@ ws_ep_bind(void *arg)
ws_ep *ep = arg;
int rv;
+ nni_ws_listener_set_maxframe(ep->listener, ep->rcvmax);
nni_ws_listener_hook(ep->listener, ws_hook, ep);
+
if ((rv = nni_ws_listener_listen(ep->listener)) == 0) {
ep->started = true;
}
@@ -359,6 +361,7 @@ ws_ep_connect(void *arg, nni_aio *aio)
NNI_ASSERT(nni_list_empty(&ep->aios));
ep->started = true;
nni_list_append(&ep->aios, aio);
+ nni_ws_dialer_set_maxframe(ep->dialer, ep->rcvmax);
nni_ws_dialer_dial(ep->dialer, ep->connaio);
nni_mtx_unlock(&ep->mtx);
}
@@ -373,6 +376,11 @@ ws_ep_setopt_recvmaxsz(void *arg, const void *v, size_t sz, int typ)
rv = nni_copyin_size(&val, v, sz, 0, NNI_MAXSZ, typ);
if ((rv == 0) && (ep != NULL)) {
ep->rcvmax = val;
+ if (ep->mode == NNI_EP_MODE_DIAL) {
+ nni_ws_dialer_set_maxframe(ep->dialer, ep->rcvmax);
+ } else {
+ nni_ws_listener_set_maxframe(ep->listener, ep->rcvmax);
+ }
}
return (rv);
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6a8d5e86..e7d751ac 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -197,6 +197,7 @@ add_nng_compat_test(compat_reqttl 5)
add_nng_compat_test(compat_shutdown 5)
add_nng_compat_test(compat_surveyttl 5)
add_nng_compat_test(compat_tcp 60)
+add_nng_compat_test(compat_ws 60)
# These are special tests for compat mode, not inherited from the
# legacy libnanomsg suite.
diff --git a/tests/compat_ws.c b/tests/compat_ws.c
new file mode 100644
index 00000000..6d019364
--- /dev/null
+++ b/tests/compat_ws.c
@@ -0,0 +1,281 @@
+/*
+ Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+ Copyright 2018 Capitar IT Group BV <info@capitar.com>
+ Copyright (c) 2012 250bpm s.r.o. All rights reserved.
+ Copyright (c) 2014-2016 Jack R. Dunaway. All rights reserved.
+ Copyright 2016 Franklin "Snaipe" Mathieu <franklinmathieu@gmail.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom
+ the Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+*/
+
+// This file began life in nanomsg, but we have made some significant changes
+// to it. Specifically, NNG has no support for text frames, requires that
+// maximum receive frame sizes be set before connections are established,
+// has support for IPv6, and does not support local interface binding.
+// We have improved the maximum receive size test, and verified that option
+// setting for the frame type conforms to NNG constraints.
+
+#include <nanomsg/nn.h>
+#include <nanomsg/pair.h>
+#include <nanomsg/ws.h>
+
+#include "compat_testutil.h"
+
+static char socket_address[128];
+
+/* Basic tests for WebSocket transport. */
+
+#if 0 // NNG has no support for text frames.
+/* test_text() verifies that we drop messages properly when sending invalid
+ UTF-8, but not when we send valid data. */
+void test_text ()
+{
+ int sb;
+ int sc;
+ int opt;
+ uint8_t bad[20];
+
+ /* Negative testing... bad UTF-8 data for text. */
+ sb = test_socket (AF_SP, NN_PAIR);
+ sc = test_socket (AF_SP, NN_PAIR);
+
+ opt = NN_WS_MSG_TYPE_TEXT;
+ test_setsockopt (sb, NN_WS, NN_WS_MSG_TYPE, &opt, sizeof (opt));
+
+ opt = NN_WS_MSG_TYPE_TEXT;
+ test_setsockopt (sc, NN_WS, NN_WS_MSG_TYPE, &opt, sizeof (opt));
+ opt = 500;
+ test_setsockopt (sb, NN_SOL_SOCKET, NN_RCVTIMEO, &opt, sizeof (opt));
+
+ test_bind (sb, socket_address);
+ test_connect (sc, socket_address);
+
+ test_send (sc, "GOOD");
+ test_recv (sb, "GOOD");
+
+ /* and the bad ... */
+ strcpy ((char *)bad, "BAD.");
+ bad[2] = (char)0xDD;
+ test_send (sc, (char *)bad);
+
+ /* Make sure we dropped the frame. */
+ test_drop (sb, ETIMEDOUT);
+
+ test_close (sb);
+ test_close (sc);
+
+ return;
+}
+#endif
+
+int main (int argc, const char *argv[])
+{
+ int rc;
+ int sb;
+ int sc;
+ int sb2;
+ int opt;
+ size_t sz;
+ int i;
+ char any_address[128];
+
+ test_addr_from (socket_address, "ws", "127.0.0.1",
+ get_test_port (argc, argv));
+
+ test_addr_from (any_address, "ws", "*",
+ get_test_port (argc, argv));
+
+ /* Try closing bound but unconnected socket. */
+ sb = test_socket (AF_SP, NN_PAIR);
+ test_bind (sb, any_address);
+ test_close (sb);
+
+ /* Try closing a TCP socket while it not connected. At the same time
+ test specifying the local address for the connection. */
+ sc = test_socket (AF_SP, NN_PAIR);
+ test_connect (sc, socket_address);
+ test_close (sc);
+
+ /* Open the socket anew. */
+ sc = test_socket (AF_SP, NN_PAIR);
+
+ /* Check socket options. */
+ sz = sizeof (opt);
+ rc = nn_getsockopt (sc, NN_WS, NN_WS_MSG_TYPE, &opt, &sz);
+ errno_assert (rc == 0);
+ nn_assert (sz == sizeof (opt));
+ nn_assert (opt == NN_WS_MSG_TYPE_BINARY);
+
+ /* Default port 80 should be assumed if not explicitly declared. */
+ rc = nn_connect (sc, "ws://127.0.0.1");
+ errno_assert (rc >= 0);
+
+ /* Try using invalid address strings. */
+ rc = nn_connect (sc, "ws://*:");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+ rc = nn_connect (sc, "ws://*:1000000");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+ rc = nn_connect (sc, "ws://*:some_port");
+ nn_assert (rc < 0);
+#if 0 // NNG doesn't support device binding
+ rc = nn_connect (sc, "ws://eth10000;127.0.0.1:5555");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == ENODEV);
+#endif
+
+ rc = nn_bind (sc, "ws://127.0.0.1:");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+ rc = nn_bind (sc, "ws://127.0.0.1:1000000");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+#if 0 // NNG doesn't support device binding
+ rc = nn_bind (sc, "ws://eth10000:5555");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == ENODEV);
+#endif
+
+ rc = nn_connect (sc, "ws://:5555");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+ rc = nn_connect (sc, "ws://-hostname:5555");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+ rc = nn_connect (sc, "ws://abc.123.---.#:5555");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+#if 0 // Nothing wrong with this under NNG. Valid IPv6 URL
+ rc = nn_connect (sc, "ws://[::1]:5555");
+ nn_assert (rc < 0);
+#endif
+ errno_assert (nn_errno () == EINVAL);
+ rc = nn_connect (sc, "ws://abc...123:5555");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+ rc = nn_connect (sc, "ws://.123:5555");
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+
+ test_close (sc);
+
+ sb = test_socket (AF_SP, NN_PAIR);
+ test_bind (sb, socket_address);
+ sc = test_socket (AF_SP, NN_PAIR);
+ test_connect (sc, socket_address);
+
+ nn_sleep(100);
+ /* Ping-pong test. */
+ for (i = 0; i != 100; ++i) {
+
+ test_send (sc, "ABC");
+ test_recv (sb, "ABC");
+
+ test_send (sb, "DEF");
+ test_recv (sc, "DEF");
+ }
+
+ /* Batch transfer test. */
+ for (i = 0; i != 100; ++i) {
+ test_send (sc, "0123456789012345678901234567890123456789");
+ }
+ for (i = 0; i != 100; ++i) {
+ test_recv (sb, "0123456789012345678901234567890123456789");
+ }
+
+ test_close (sc);
+ test_close (sb);
+
+ /* Test two sockets binding to the same address. */
+ sb = test_socket (AF_SP, NN_PAIR);
+ test_bind (sb, socket_address);
+ sb2 = test_socket (AF_SP, NN_PAIR);
+
+ rc = nn_bind (sb2, socket_address);
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EADDRINUSE);
+ test_close(sb);
+ test_close(sb2);
+
+ /* Test that NN_RCVMAXSIZE can be -1, but not lower */
+ sb = test_socket (AF_SP, NN_PAIR);
+ opt = -1;
+ rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt));
+ nn_assert (rc >= 0);
+ opt = -2;
+ rc = nn_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt));
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+ test_close (sb);
+
+ /* Test NN_RCVMAXSIZE limit */
+ sb = test_socket (AF_SP, NN_PAIR);
+ opt = 4;
+ test_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt));
+ test_bind (sb, socket_address);
+ sc = test_socket (AF_SP, NN_PAIR);
+ test_connect (sc, socket_address);
+ opt = 1000;
+ test_setsockopt (sc, NN_SOL_SOCKET, NN_SNDTIMEO, &opt, sizeof (opt));
+ nn_assert (opt == 1000);
+ opt = 1000;
+ test_setsockopt (sb, NN_SOL_SOCKET, NN_RCVTIMEO, &opt, sizeof (opt));
+ nn_assert (opt == 1000);
+ test_send (sc, "ABC");
+ test_recv (sb, "ABC");
+ test_send (sc, "ABCD");
+ test_recv (sb, "ABCD");
+ test_send (sc, "ABCDE");
+ test_drop (sb, ETIMEDOUT);
+ test_close (sc);
+
+ /* Increase the size limit, reconnect, then try sending again. */
+ opt = 5;
+ test_setsockopt (sb, NN_SOL_SOCKET, NN_RCVMAXSIZE, &opt, sizeof (opt));
+
+ sc = test_socket (AF_SP, NN_PAIR);
+ test_connect (sc, socket_address);
+ nn_sleep(200);
+
+ test_send (sc, "ABCDE");
+ test_recv (sb, "ABCDE");
+ test_close (sb);
+ test_close (sc);
+
+#if 0 // NNG doesn't support text frames.
+ test_text ();
+#else
+ opt = NN_WS_MSG_TYPE_TEXT;
+ rc = nn_setsockopt (sc, NN_WS, NN_WS_MSG_TYPE, &opt, sizeof (opt));
+ nn_assert (rc < 0);
+ errno_assert (nn_errno () == EINVAL);
+
+ opt = NN_WS_MSG_TYPE_BINARY;
+ test_setsockopt (sb, NN_WS, NN_WS_MSG_TYPE, &opt, sizeof (opt));
+#endif
+
+ /* Test closing a socket that is waiting to connect. */
+ sc = test_socket (AF_SP, NN_PAIR);
+ test_connect (sc, socket_address);
+ nn_sleep (100);
+ test_close (sc);
+
+ return 0;
+}