fixes #386 loop prevention code missing
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e1c55f4..e40692b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -379,6 +379,8 @@
add_libnanomsg_test (cmsg 5)
add_libnanomsg_test (bug328 5)
add_libnanomsg_test (ws_async_shutdown 5)
+ add_libnanomsg_test (reqttl 10)
+ add_libnanomsg_test (surveyttl 10)
# Platform-specific tests
if (WIN32)
diff --git a/doc/nn_getsockopt.txt b/doc/nn_getsockopt.txt
index bb36c22..7d5810f 100644
--- a/doc/nn_getsockopt.txt
+++ b/doc/nn_getsockopt.txt
@@ -112,6 +112,11 @@
Socket name for error reporting and statistics. The type of the option
is string. Default value is "N" where N is socket integer.
*This option is experimental, see <<nn_env.7.txt#,nn_env(7)>> for details*
+*NN_TTL*::
+ Retrieves the maximum number of "hops" a message can go through before
+ it is dropped. Each time the message is received (for example via
+ the <<nn_device.3.txt#,nn_device(3)>> function) counts as a single hop.
+ This provides a form of protection against inadvertent loops.
RETURN VALUE
@@ -148,4 +153,4 @@
AUTHORS
-------
link:mailto:sustrik@250bpm.com[Martin Sustrik]
-
+link:mailto:garrett@damore.org[Garrett D'Amore]
diff --git a/doc/nn_setsockopt.txt b/doc/nn_setsockopt.txt
index 25281dc..3e53371 100644
--- a/doc/nn_setsockopt.txt
+++ b/doc/nn_setsockopt.txt
@@ -90,6 +90,12 @@
Socket name for error reporting and statistics. The type of the option
is string. Default value is "socket.N" where N is socket integer.
*This option is experimental, see <<nn_env.7.txt#,nn_env(7)>> for details*
+*NN_TTL*::
+ Sets the maximum number of "hops" a message can go through before
+ it is dropped. Each time the message is received (for example via
+ the <<nn_device.3.txt#,nn_device(3)>> function) counts as a single hop.
+ This provides a form of protection against inadvertent loops.
+
RETURN VALUE
@@ -128,4 +134,5 @@
AUTHORS
-------
link:mailto:sustrik@250bpm.com[Martin Sustrik]
+link:mailto:garrett@damore.org[Garrett D'Amore]
diff --git a/src/core/sock.c b/src/core/sock.c
index 76f0984..4db7d49 100644
--- a/src/core/sock.c
+++ b/src/core/sock.c
@@ -130,6 +130,7 @@
self->rcvtimeo = -1;
self->reconnect_ivl = 100;
self->reconnect_ivl_max = 0;
+ self->maxttl = 8;
self->ep_template.sndprio = 8;
self->ep_template.rcvprio = 8;
self->ep_template.ipv4only = 1;
@@ -289,7 +290,6 @@
{
struct nn_optset *optset;
int val;
- int *dst;
/* Protocol-specific socket options. */
if (level > NN_SOL_SOCKET)
@@ -304,8 +304,10 @@
return optset->vfptr->setopt (optset, option, optval, optvallen);
}
+ nn_assert (level == NN_SOL_SOCKET);
+
/* Special-casing socket name for now as it's the only string option */
- if (level == NN_SOL_SOCKET && option == NN_SOCKET_NAME) {
+ if (option == NN_SOCKET_NAME) {
if (optvallen > 63)
return -EINVAL;
memcpy (self->socket_name, optval, optvallen);
@@ -319,66 +321,64 @@
val = *(int*) optval;
/* Generic socket-level options. */
- if (level == NN_SOL_SOCKET) {
- switch (option) {
- case NN_LINGER:
- dst = &self->linger;
- break;
- case NN_SNDBUF:
- if (nn_slow (val <= 0))
- return -EINVAL;
- dst = &self->sndbuf;
- break;
- case NN_RCVBUF:
- if (nn_slow (val <= 0))
- return -EINVAL;
- dst = &self->rcvbuf;
- break;
- case NN_RCVMAXSIZE:
- if (nn_slow (val < -1))
- return -EINVAL;
- dst = &self->rcvmaxsize;
- break;
- case NN_SNDTIMEO:
- dst = &self->sndtimeo;
- break;
- case NN_RCVTIMEO:
- dst = &self->rcvtimeo;
- break;
- case NN_RECONNECT_IVL:
- if (nn_slow (val < 0))
- return -EINVAL;
- dst = &self->reconnect_ivl;
- break;
- case NN_RECONNECT_IVL_MAX:
- if (nn_slow (val < 0))
- return -EINVAL;
- dst = &self->reconnect_ivl_max;
- break;
- case NN_SNDPRIO:
- if (nn_slow (val < 1 || val > 16))
- return -EINVAL;
- dst = &self->ep_template.sndprio;
- break;
- case NN_RCVPRIO:
- if (nn_slow (val < 1 || val > 16))
- return -EINVAL;
- dst = &self->ep_template.rcvprio;
- break;
- case NN_IPV4ONLY:
- if (nn_slow (val != 0 && val != 1))
- return -EINVAL;
- dst = &self->ep_template.ipv4only;
- break;
- default:
- return -ENOPROTOOPT;
- }
- *dst = val;
-
+ switch (option) {
+ case NN_LINGER:
+ self->linger = val;
+ return 0;
+ case NN_SNDBUF:
+ if (val <= 0)
+ return -EINVAL;
+ self->sndbuf = val;
+ return 0;
+ case NN_RCVBUF:
+ if (val <= 0)
+ return -EINVAL;
+ self->rcvbuf = val;
+ return 0;
+ case NN_RCVMAXSIZE:
+ if (val < -1)
+ return -EINVAL;
+ self->rcvmaxsize = val;
+ return 0;
+ case NN_SNDTIMEO:
+ self->sndtimeo = val;
+ return 0;
+ case NN_RCVTIMEO:
+ self->rcvtimeo = val;
+ return 0;
+ case NN_RECONNECT_IVL:
+ if (val < 0)
+ return -EINVAL;
+ self->reconnect_ivl = val;
+ return 0;
+ case NN_RECONNECT_IVL_MAX:
+ if (val < 0)
+ return -EINVAL;
+ self->reconnect_ivl_max = val;
+ return 0;
+ case NN_SNDPRIO:
+ if (val < 1 || val > 16)
+ return -EINVAL;
+ self->ep_template.sndprio = val;
+ return 0;
+ case NN_RCVPRIO:
+ if (val < 1 || val > 16)
+ return -EINVAL;
+ self->ep_template.rcvprio = val;
+ return 0;
+ case NN_IPV4ONLY:
+ if (val != 0 && val != 1)
+ return -EINVAL;
+ self->ep_template.ipv4only = val;
+ return 0;
+ case NN_MAXTTL:
+ if (val < 1 || val > 255)
+ return -EINVAL;
+ self->maxttl = val;
return 0;
}
- nn_assert (0);
+ return -ENOPROTOOPT;
}
int nn_sock_getopt (struct nn_sock *self, int level, int option,
@@ -401,79 +401,6 @@
int intval;
nn_fd fd;
- /* Generic socket-level options. */
- if (level == NN_SOL_SOCKET) {
- switch (option) {
- case NN_DOMAIN:
- intval = self->socktype->domain;
- break;
- case NN_PROTOCOL:
- intval = self->socktype->protocol;
- break;
- case NN_LINGER:
- intval = self->linger;
- break;
- case NN_SNDBUF:
- intval = self->sndbuf;
- break;
- case NN_RCVBUF:
- intval = self->rcvbuf;
- break;
- case NN_RCVMAXSIZE:
- intval = self->rcvmaxsize;
- break;
- case NN_SNDTIMEO:
- intval = self->sndtimeo;
- break;
- case NN_RCVTIMEO:
- intval = self->rcvtimeo;
- break;
- case NN_RECONNECT_IVL:
- intval = self->reconnect_ivl;
- break;
- case NN_RECONNECT_IVL_MAX:
- intval = self->reconnect_ivl_max;
- break;
- case NN_SNDPRIO:
- intval = self->ep_template.sndprio;
- break;
- case NN_RCVPRIO:
- intval = self->ep_template.rcvprio;
- break;
- case NN_IPV4ONLY:
- intval = self->ep_template.ipv4only;
- break;
- case NN_SNDFD:
- if (self->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)
- return -ENOPROTOOPT;
- fd = nn_efd_getfd (&self->sndfd);
- memcpy (optval, &fd,
- *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd));
- *optvallen = sizeof (nn_fd);
- return 0;
- case NN_RCVFD:
- if (self->socktype->flags & NN_SOCKTYPE_FLAG_NORECV)
- return -ENOPROTOOPT;
- fd = nn_efd_getfd (&self->rcvfd);
- memcpy (optval, &fd,
- *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd));
- *optvallen = sizeof (nn_fd);
- return 0;
- case NN_SOCKET_NAME:
- strncpy (optval, self->socket_name, *optvallen);
- *optvallen = strlen(self->socket_name);
- return 0;
- default:
- return -ENOPROTOOPT;
- }
-
- memcpy (optval, &intval,
- *optvallen < sizeof (int) ? *optvallen : sizeof (int));
- *optvallen = sizeof (int);
-
- return 0;
- }
-
/* Protocol-specific socket options. */
if (level > NN_SOL_SOCKET)
return rc = self->sockbase->vfptr->getopt (self->sockbase,
@@ -487,7 +414,81 @@
return optset->vfptr->getopt (optset, option, optval, optvallen);
}
- nn_assert (0);
+ nn_assert (level == NN_SOL_SOCKET);
+
+ /* Generic socket-level options. */
+ switch (option) {
+ case NN_DOMAIN:
+ intval = self->socktype->domain;
+ break;
+ case NN_PROTOCOL:
+ intval = self->socktype->protocol;
+ break;
+ case NN_LINGER:
+ intval = self->linger;
+ break;
+ case NN_SNDBUF:
+ intval = self->sndbuf;
+ break;
+ case NN_RCVBUF:
+ intval = self->rcvbuf;
+ break;
+ case NN_RCVMAXSIZE:
+ intval = self->rcvmaxsize;
+ break;
+ case NN_SNDTIMEO:
+ intval = self->sndtimeo;
+ break;
+ case NN_RCVTIMEO:
+ intval = self->rcvtimeo;
+ break;
+ case NN_RECONNECT_IVL:
+ intval = self->reconnect_ivl;
+ break;
+ case NN_RECONNECT_IVL_MAX:
+ intval = self->reconnect_ivl_max;
+ break;
+ case NN_SNDPRIO:
+ intval = self->ep_template.sndprio;
+ break;
+ case NN_RCVPRIO:
+ intval = self->ep_template.rcvprio;
+ break;
+ case NN_IPV4ONLY:
+ intval = self->ep_template.ipv4only;
+ break;
+ case NN_MAXTTL:
+ intval = self->maxttl;
+ break;
+ case NN_SNDFD:
+ if (self->socktype->flags & NN_SOCKTYPE_FLAG_NOSEND)
+ return -ENOPROTOOPT;
+ fd = nn_efd_getfd (&self->sndfd);
+ memcpy (optval, &fd,
+ *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd));
+ *optvallen = sizeof (nn_fd);
+ return 0;
+ case NN_RCVFD:
+ if (self->socktype->flags & NN_SOCKTYPE_FLAG_NORECV)
+ return -ENOPROTOOPT;
+ fd = nn_efd_getfd (&self->rcvfd);
+ memcpy (optval, &fd,
+ *optvallen < sizeof (nn_fd) ? *optvallen : sizeof (nn_fd));
+ *optvallen = sizeof (nn_fd);
+ return 0;
+ case NN_SOCKET_NAME:
+ strncpy (optval, self->socket_name, *optvallen);
+ *optvallen = strlen(self->socket_name);
+ return 0;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ memcpy (optval, &intval,
+ *optvallen < sizeof (int) ? *optvallen : sizeof (int));
+ *optvallen = sizeof (int);
+
+ return 0;
}
int nn_sock_add_ep (struct nn_sock *self, struct nn_transport *transport,
diff --git a/src/core/sock.h b/src/core/sock.h
index 4df0391..f87e7e9 100644
--- a/src/core/sock.h
+++ b/src/core/sock.h
@@ -80,6 +80,7 @@
int rcvtimeo;
int reconnect_ivl;
int reconnect_ivl_max;
+ int maxttl;
/* Endpoint-specific options. */
struct nn_ep_options ep_template;
diff --git a/src/nn.h b/src/nn.h
index 067bea3..ce5efe3 100644
--- a/src/nn.h
+++ b/src/nn.h
@@ -334,6 +334,7 @@
#define NN_IPV4ONLY 14
#define NN_SOCKET_NAME 15
#define NN_RCVMAXSIZE 16
+#define NN_MAXTTL 17
/* Send/recv options. */
#define NN_DONTWAIT 1
diff --git a/src/protocols/reqrep/xrep.c b/src/protocols/reqrep/xrep.c
index cd53876..dcd8fbf 100644
--- a/src/protocols/reqrep/xrep.c
+++ b/src/protocols/reqrep/xrep.c
@@ -1,5 +1,6 @@
/*
Copyright (c) 2012-2014 Martin Sustrik All rights reserved.
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"),
@@ -194,6 +195,7 @@
struct nn_xrep *xrep;
struct nn_pipe *pipe;
int i;
+ int maxttl;
void *data;
size_t sz;
struct nn_chunkref ref;
@@ -207,6 +209,10 @@
if (!(rc & NN_PIPE_PARSED)) {
+ sz = sizeof (maxttl);
+ rc = nn_sockbase_getopt (self, NN_MAXTTL, &maxttl, &sz);
+ errnum_assert (rc == 0, -rc);
+
/* Determine the size of the message header. */
data = nn_chunkref_data (&msg->body);
sz = nn_chunkref_size (&msg->body);
@@ -227,6 +233,12 @@
}
++i;
+ /* If we encountered too many hops, just toss the message */
+ if (i > maxttl) {
+ nn_msg_term (msg);
+ return -EAGAIN;
+ }
+
/* Split the header and the body. */
nn_assert (nn_chunkref_size (&msg->sphdr) == 0);
nn_chunkref_term (&msg->sphdr);
diff --git a/src/protocols/survey/xrespondent.c b/src/protocols/survey/xrespondent.c
index 5441bb9..6e628d2 100644
--- a/src/protocols/survey/xrespondent.c
+++ b/src/protocols/survey/xrespondent.c
@@ -1,6 +1,6 @@
/*
Copyright (c) 2012-2013 Martin Sustrik All rights reserved.
- Copyright 2015 Garrett D'Amore <garrett@damore.org>
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"),
@@ -198,6 +198,7 @@
void *data;
struct nn_chunkref ref;
struct nn_xrespondent_data *pipedata;
+ int maxttl;
xrespondent = nn_cont (self, struct nn_xrespondent, sockbase);
@@ -208,6 +209,10 @@
/* Split the header (including survey ID) from the body, if needed. */
if (!(rc & NN_PIPE_PARSED)) {
+ sz = sizeof (maxttl);
+ rc = nn_sockbase_getopt (self, NN_MAXTTL, &maxttl, &sz);
+ errnum_assert (rc == 0, -rc);
+
/* Determine the size of the message header. */
data = nn_chunkref_data (&msg->body);
sz = nn_chunkref_size (&msg->body);
@@ -228,6 +233,12 @@
}
++i;
+ /* Ignore messages with too many hops. */
+ if (i > maxttl) {
+ nn_msg_term (msg);
+ return -EAGAIN;
+ }
+
nn_assert (nn_chunkref_size (&msg->sphdr) == 0);
nn_chunkref_term (&msg->sphdr);
nn_chunkref_init (&msg->sphdr, i * sizeof (uint32_t));
diff --git a/src/transports/ipc/bipc.c b/src/transports/ipc/bipc.c
index acf994e..f1b3a2a 100644
--- a/src/transports/ipc/bipc.c
+++ b/src/transports/ipc/bipc.c
@@ -93,7 +93,6 @@
int nn_bipc_create (void *hint, struct nn_epbase **epbase)
{
struct nn_bipc *self;
- size_t sz;
int rc;
/* Allocate the new endpoint object. */
diff --git a/src/transports/ws/bws.c b/src/transports/ws/bws.c
index 06b7759..da3a53f 100644
--- a/src/transports/ws/bws.c
+++ b/src/transports/ws/bws.c
@@ -106,7 +106,6 @@
size_t sslen;
int ipv4only;
size_t ipv4onlylen;
- size_t sz;
/* Allocate the new endpoint object. */
self = nn_alloc (sizeof (struct nn_bws), "bws");
diff --git a/tests/reqttl.c b/tests/reqttl.c
new file mode 100644
index 0000000..795b43e
--- /dev/null
+++ b/tests/reqttl.c
@@ -0,0 +1,155 @@
+/*
+ Copyright (c) 2012 Martin Sustrik All rights reserved.
+ Copyright (c) 2013 GoPivotal, Inc. All rights reserved.
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
+ 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.
+*/
+
+#include "../src/nn.h"
+#include "../src/reqrep.h"
+#include "../src/tcp.h"
+
+#include "testutil.h"
+#include "../src/utils/attr.h"
+#include "../src/utils/thread.c"
+
+static char socket_address_a[128];
+static char socket_address_b[128];
+int dev0;
+int dev1;
+
+void device (NN_UNUSED void *arg)
+{
+ int rc;
+
+ /* Run the device. */
+ rc = nn_device (dev0, dev1);
+ nn_assert (rc < 0 && nn_errno () == EBADF);
+
+ /* Clean up. */
+ test_close (dev0);
+ test_close (dev1);
+}
+
+int main (int argc, const char *argv[])
+{
+ int end0;
+ int end1;
+ struct nn_thread thread1;
+ int timeo;
+ int maxttl;
+ size_t sz;
+ int rc;
+
+ int port = get_test_port(argc, argv);
+
+ test_addr_from(socket_address_a, "tcp", "127.0.0.1", port);
+ test_addr_from(socket_address_b, "tcp", "127.0.0.1", port + 1);
+
+ /* Intialise the device sockets. */
+ dev0 = test_socket (AF_SP_RAW, NN_REP);
+ dev1 = test_socket (AF_SP_RAW, NN_REQ);
+
+ test_bind (dev0, socket_address_a);
+ test_bind (dev1, socket_address_b);
+
+ /* Start the device. */
+ nn_thread_init (&thread1, device, NULL);
+
+ end0 = test_socket (AF_SP, NN_REQ);
+ end1 = test_socket (AF_SP, NN_REP);
+
+ /* Test the bi-directional device TTL */
+ test_connect (end0, socket_address_a);
+ test_connect (end1, socket_address_b);
+
+ /* Wait for TCP to establish. */
+ nn_sleep (100);
+
+ /* Pass a message between endpoints. */
+ /* Set up max receive timeout. */
+ timeo = 100;
+ test_setsockopt (end0, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo));
+ timeo = 100;
+ test_setsockopt (end1, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo));
+
+ /* Test default TTL is 8. */
+ sz = sizeof (maxttl);
+ maxttl = -1;
+ rc = nn_getsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, &sz);
+ nn_assert (rc == 0);
+ nn_assert (sz == sizeof (maxttl));
+ nn_assert (maxttl == 8);
+
+ /* Test to make sure option TTL cannot be set below 1. */
+ maxttl = -1;
+ rc = nn_setsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ nn_assert (rc < 0 && nn_errno () == EINVAL);
+ nn_assert (maxttl == -1);
+ maxttl = 0;
+ rc = nn_setsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ nn_assert (rc < 0 && nn_errno () == EINVAL);
+ nn_assert (maxttl == 0);
+
+ /* Test to set non-integer size */
+ maxttl = 8;
+ rc = nn_setsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, 1);
+ nn_assert (rc < 0 && nn_errno () == EINVAL);
+ nn_assert (maxttl == 8);
+
+ test_send (end0, "XYZ");
+
+ test_recv (end1, "XYZ");
+
+ /* Now send a reply. */
+ test_send (end1, "REPLYXYZ\n");
+ test_recv (end0, "REPLYXYZ\n");
+
+ /* Now set the max TTL. */
+ maxttl = 1;
+ test_setsockopt (end0, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ test_setsockopt (end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+
+ test_send (end0, "DROPTHIS");
+ test_drop (end1, ETIMEDOUT);
+
+ /* Now set the max TTL up. */
+ maxttl = 2;
+ test_setsockopt (end0, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ test_setsockopt (end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+
+ test_send (end0, "DONTDROP");
+ test_recv (end1, "DONTDROP");
+
+ test_send (end1, "GOTIT");
+ test_recv (end0, "GOTIT");
+
+ /* Clean up. */
+ test_close (end0);
+ test_close (end1);
+
+ /* Shut down the devices. */
+ nn_term ();
+
+ nn_thread_term (&thread1);
+
+ return 0;
+}
diff --git a/tests/surveyttl.c b/tests/surveyttl.c
new file mode 100644
index 0000000..625ed45
--- /dev/null
+++ b/tests/surveyttl.c
@@ -0,0 +1,148 @@
+/*
+ Copyright (c) 2012 Martin Sustrik All rights reserved.
+ Copyright (c) 2013 GoPivotal, Inc. All rights reserved.
+ Copyright 2016 Garrett D'Amore <garrett@damore.org>
+ 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.
+*/
+
+#include "../src/nn.h"
+#include "../src/survey.h"
+#include "../src/tcp.h"
+
+#include "testutil.h"
+#include "../src/utils/attr.h"
+#include "../src/utils/thread.c"
+
+static char socket_address_a[128];
+static char socket_address_b[128];
+int dev0;
+int dev1;
+
+void device (NN_UNUSED void *arg)
+{
+ int rc;
+
+ /* Run the device. */
+ rc = nn_device (dev0, dev1);
+ nn_assert (rc < 0 && nn_errno () == EBADF);
+
+ /* Clean up. */
+ test_close (dev0);
+ test_close (dev1);
+}
+
+int main (int argc, const char *argv[])
+{
+ int end0;
+ int end1;
+ struct nn_thread thread1;
+ int timeo;
+ int maxttl;
+ size_t sz;
+ int rc;
+
+ int port = get_test_port(argc, argv);
+
+ test_addr_from(socket_address_a, "tcp", "127.0.0.1", port);
+ test_addr_from(socket_address_b, "tcp", "127.0.0.1", port + 1);
+
+ /* Intialise the device sockets. */
+ dev0 = test_socket (AF_SP_RAW, NN_RESPONDENT);
+ dev1 = test_socket (AF_SP_RAW, NN_SURVEYOR);
+
+ test_bind (dev0, socket_address_a);
+ test_bind (dev1, socket_address_b);
+
+ /* Start the device. */
+ nn_thread_init (&thread1, device, NULL);
+
+ end0 = test_socket (AF_SP, NN_SURVEYOR);
+ end1 = test_socket (AF_SP, NN_RESPONDENT);
+
+ /* Test the bi-directional device TTL */
+ test_connect (end0, socket_address_a);
+ test_connect (end1, socket_address_b);
+
+ /* Wait for TCP to establish. */
+ nn_sleep (100);
+
+ /* Set up max receive timeout. */
+ timeo = 100;
+ test_setsockopt (end0, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo));
+ timeo = 100;
+ test_setsockopt (end1, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo));
+
+ /* Test default TTL is 8. */
+ sz = sizeof (maxttl);
+ maxttl = -1;
+ rc = nn_getsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, &sz);
+ nn_assert (rc == 0);
+ nn_assert (sz == sizeof (maxttl));
+ nn_assert (maxttl == 8);
+
+ /* Test to make sure option TTL cannot be set below 1. */
+ maxttl = -1;
+ rc = nn_setsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ nn_assert (rc < 0 && nn_errno () == EINVAL);
+ nn_assert (maxttl == -1);
+ maxttl = 0;
+ rc = nn_setsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ nn_assert (rc < 0 && nn_errno () == EINVAL);
+ nn_assert (maxttl == 0);
+
+ /* Test to set non-integer size */
+ maxttl = 8;
+ rc = nn_setsockopt(end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, 1);
+ nn_assert (rc < 0 && nn_errno () == EINVAL);
+ nn_assert (maxttl == 8);
+
+ /* Pass a message between endpoints. */
+ test_send (end0, "SURVEY");
+ test_recv (end1, "SURVEY");
+
+ /* Now send a reply. */
+ test_send (end1, "REPLYXYZ");
+ test_recv (end0, "REPLYXYZ");
+
+ /* Now set the max TTL. */
+ maxttl = 1;
+ test_setsockopt (end0, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ test_setsockopt (end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+
+ test_send (end0, "DROPTHIS");
+ test_drop (end1, ETIMEDOUT);
+
+ maxttl = 2;
+ test_setsockopt (end0, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ test_setsockopt (end1, NN_SOL_SOCKET, NN_MAXTTL, &maxttl, sizeof (maxttl));
+ test_send (end0, "DONTDROP");
+ test_recv (end1, "DONTDROP");
+
+ /* Clean up. */
+ test_close (end0);
+ test_close (end1);
+
+ /* Shut down the devices. */
+ nn_term ();
+ nn_thread_term (&thread1);
+
+ return 0;
+}