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;
+}