[SCM] Socket Wrapper Repository - branch master updated

Stefan Metzmacher metze at samba.org
Mon Jun 22 14:46:27 UTC 2020


The branch, master has been updated
       via  b94af54 socket_wrapper.c: let swrap_vioctl() handle SIOCOUTQ/TIOCOUTQ/FIONWRITE explicitly
       via  64cee56 socket_wrapper.c: make FIONREAD handling more robust in swrap_vioctl()
       via  309103f test_echo_tcp_socket_options.c: add tests for TCP_INFO
       via  c73d8a4 socket_wrapper.c: implement getsockopt(TCP_INFO) if the platform supports it
       via  1c20b95 doc: Document SOCKET_WRAPPER_DIR_ALLOW_ORIG
       via  19c3e87 swrap: Add SOCKET_WRAPPER_DIR_ALLOW_ORIG to allow fall back
       via  dd42895 swrap: Abort if socket wrapper directory is too long to be usable
       via  fc1e98f swrap: Add abstractions to construct Unix domain socket paths
       via  80e0f42 swrap: Abort on failure to use SOCKET_WRAPPER_DIR
      from  49aeafa Revert "socket_wrapper.c: implement getsockopt(TCP_INFO) if the platform supports it"

https://git.samba.org/?p=socket_wrapper.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit b94af548f42e561207db7cacbe09c9e3286de2d3
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Jun 8 14:21:25 2020 +0200

    socket_wrapper.c: let swrap_vioctl() handle SIOCOUTQ/TIOCOUTQ/FIONWRITE explicitly
    
    They are used to ask for the number of unacked bytes in the send queue,
    with AF_UNIX sockets get strange result, on linux 5.3 I get more bytes
    reported than I sent into the socket. All bytes reach the destination
    directly, so we can just always report 0 unacked bytes.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=11897
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    (cherry picked from commit f317ebcdcdd626ed9e06de2eb60031306994c803)

commit 64cee569a77e9d9d92e0dc0d2793536c9fb4a45e
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Jun 8 14:18:44 2020 +0200

    socket_wrapper.c: make FIONREAD handling more robust in swrap_vioctl()
    
    We should only dereference the va args when the kernel already checked
    they are valid.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=11897
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    (cherry picked from commit c95b7cb1d7b9348472276edceff71889aa676d25)

commit 309103f464af19d6190f89de959c6dc41ba58c85
Author: Stefan Metzmacher <metze at samba.org>
Date:   Fri Jun 19 20:52:23 2020 +0200

    test_echo_tcp_socket_options.c: add tests for TCP_INFO
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    (cherry picked from commit a37c0175492fb1b35257b785c71dea4e4f6d4750)

commit c73d8a43c07ffbd9f4a7010b99363ffa53b4723c
Author: Stefan Metzmacher <metze at samba.org>
Date:   Mon Jun 8 10:32:28 2020 +0200

    socket_wrapper.c: implement getsockopt(TCP_INFO) if the platform supports it
    
    This just implements a few basics, which are required by Samba.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=11897
    
    Signed-off-by: Stefan Metzmacher <metze at samba.org>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    (cherry picked from commit 300de6e099ea82ee5361918de8c3abb389e0782d)

commit 1c20b9596422c9cb92fc539493df45b03d91457b
Author: Martin Schwenke <martin at meltin.net>
Date:   Thu May 14 09:57:24 2020 +1000

    doc: Document SOCKET_WRAPPER_DIR_ALLOW_ORIG
    
    Signed-off-by: Martin Schwenke <martin at meltin.net>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit 19c3e87570954cb7fb4f6c105cbec9eabf4539b0
Author: Martin Schwenke <martin at meltin.net>
Date:   Thu May 14 10:01:54 2020 +1000

    swrap: Add SOCKET_WRAPPER_DIR_ALLOW_ORIG to allow fall back
    
    Instead of failing when the path returned by realpath(3) is too long,
    if SOCKET_WRAPPER_DIR_ALLOW_ORIG is set then fall back to the
    original value.  If this original path is too long or something else
    fails then abort.
    
    Signed-off-by: Martin Schwenke <martin at meltin.net>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit dd428959713998d3f973bcf2762d489ef27d9f13
Author: Martin Schwenke <martin at meltin.net>
Date:   Wed May 13 13:45:02 2020 +1000

    swrap: Abort if socket wrapper directory is too long to be usable
    
    If the socket wrapper directory path is too long to allow reliable
    construction of the required Unix domain socket paths then
    convert_in_un_alloc() can return ENFILE if paths are truncated in
    unfortunate ways.  This can be very hard to debug since, for example,
    bind(2) should never return ENFILE.
    
    Instead, abort if the path returned by realpath(3) is unusable.
    
    The code structure is slightly weird but this accommodates an
    additional change.
    
    Signed-off-by: Martin Schwenke <martin at meltin.net>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit fc1e98f8ca51ca4de7551e61b6b9c6c99d183b10
Author: Martin Schwenke <martin at meltin.net>
Date:   Wed May 13 13:23:21 2020 +1000

    swrap: Add abstractions to construct Unix domain socket paths
    
    These include overflow checks but the results of the checks are not
    yet used.
    
    Signed-off-by: Martin Schwenke <martin at meltin.net>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit 80e0f42b19d9d73a50c1cf7c5a19d1a2dac70d4e
Author: Martin Schwenke <martin at meltin.net>
Date:   Sat Jun 13 20:50:42 2020 +1000

    swrap: Abort on failure to use SOCKET_WRAPPER_DIR
    
    If SOCKET_WRAPPER_DIR is set the intention is to use socket wrapper.
    Returning NULL means socket wrapper is disabled.  The only sure way to
    avoid running without socket wrapper is to abort.
    
    Signed-off-by: Martin Schwenke <martin at meltin.net>
    Reviewed-by: Andreas Schneider <asn at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 ConfigureChecks.cmake                |   1 +
 config.h.cmake                       |   1 +
 doc/socket_wrapper.1                 |   7 +-
 doc/socket_wrapper.1.txt             |  10 ++
 src/socket_wrapper.c                 | 209 +++++++++++++++++++++++++++++++----
 tests/echo_srv.c                     |  32 ++++++
 tests/test_echo_tcp_socket_options.c |  50 +++++++++
 7 files changed, 289 insertions(+), 21 deletions(-)


Changeset truncated at 500 lines:

diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index 4d5adc4..4a2f55e 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -43,6 +43,7 @@ int main(void){ return 0; }
 endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2)
 
 # HEADERS
+check_include_file(netinet/tcp_fsm.h HAVE_NETINET_TCP_FSM_H)
 check_include_file(sys/filio.h HAVE_SYS_FILIO_H)
 check_include_file(sys/signalfd.h HAVE_SYS_SIGNALFD_H)
 check_include_file(sys/eventfd.h HAVE_SYS_EVENTFD_H)
diff --git a/config.h.cmake b/config.h.cmake
index 36050b5..d3ceb23 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -9,6 +9,7 @@
 
 /************************** HEADER FILES *************************/
 
+#cmakedefine HAVE_NETINET_TCP_FSM_H 1
 #cmakedefine HAVE_SYS_FILIO_H 1
 #cmakedefine HAVE_SYS_SIGNALFD_H 1
 #cmakedefine HAVE_SYS_EVENTFD_H 1
diff --git a/doc/socket_wrapper.1 b/doc/socket_wrapper.1
index e244f10..ec7f4b8 100644
--- a/doc/socket_wrapper.1
+++ b/doc/socket_wrapper.1
@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: socket_wrapper
 .\"    Author: Samba Team
-.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
+.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
 .\"      Date: 2018-11-28
 .\"    Manual: \ \&
 .\"    Source: \ \&
@@ -157,6 +157,11 @@ If you need to see what is going on in socket_wrapper itself or try to find a bu
 .RS 4
 This allows you to disable deep binding in socket_wrapper\&. This is useful for running valgrind tools or sanitizers like (address, undefined, thread)\&.
 .RE
+.PP
+\fBSOCKET_WRAPPER_DIR_ALLOW_ORIG\fR
+.RS 4
+SOCKET_WRAPPER_DIR is resolved by socket_wrapper using realpath(3)\&. Given that Unix sockets are constructed relative to this directory, the resulting path can sometimes be too long to allow valid socket paths to be constructed due to length restrictions\&. Setting this variable (to any value) allows socket_wrapper to fall back to the original value of SOCKET_WRAPPER_DIR if realpath(3) makes it too long to be usable\&.
+.RE
 .SH "EXAMPLE"
 .sp
 .if n \{\
diff --git a/doc/socket_wrapper.1.txt b/doc/socket_wrapper.1.txt
index c00d582..d070fbf 100644
--- a/doc/socket_wrapper.1.txt
+++ b/doc/socket_wrapper.1.txt
@@ -95,6 +95,16 @@ debug symbols.
 This allows you to disable deep binding in socket_wrapper. This is useful for
 running valgrind tools or sanitizers like (address, undefined, thread).
 
+*SOCKET_WRAPPER_DIR_ALLOW_ORIG*::
+
+SOCKET_WRAPPER_DIR is resolved by socket_wrapper using realpath(3).
+Given that Unix sockets are constructed relative to this directory,
+the resulting path can sometimes be too long to allow valid socket
+paths to be constructed due to length restrictions.  Setting this
+variable (to any value) allows socket_wrapper to fall back to the
+original value of SOCKET_WRAPPER_DIR if realpath(3) makes it too long
+to be usable.
+
 EXAMPLE
 -------
 
diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c
index 5b7c9ea..ffdd31a 100644
--- a/src/socket_wrapper.c
+++ b/src/socket_wrapper.c
@@ -66,6 +66,9 @@
 #include <sys/un.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#ifdef HAVE_NETINET_TCP_FSM_H
+#include <netinet/tcp_fsm.h>
+#endif
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -264,6 +267,7 @@ struct socket_info
 	int defer_connect;
 	int pktinfo;
 	int tcp_nodelay;
+	int listening;
 
 	/* The unix path so we can unlink it on close() */
 	struct sockaddr_un un_addr;
@@ -1370,10 +1374,69 @@ static void swrap_set_next_free(struct socket_info *si, int next_free)
 	sic->meta.next_free = next_free;
 }
 
+static int swrap_un_path(struct sockaddr_un *un,
+			 const char *swrap_dir,
+			 char type,
+			 unsigned int iface,
+			 unsigned int prt)
+{
+	int ret;
+
+	ret = snprintf(un->sun_path,
+		       sizeof(un->sun_path),
+		       "%s/"SOCKET_FORMAT,
+		       swrap_dir,
+		       type,
+		       iface,
+		       prt);
+	if ((size_t)ret >= sizeof(un->sun_path)) {
+		return ENAMETOOLONG;
+	}
+
+	return 0;
+}
+
+static int swrap_un_path_EINVAL(struct sockaddr_un *un,
+				const char *swrap_dir)
+{
+	int ret;
+
+	ret = snprintf(un->sun_path,
+		       sizeof(un->sun_path),
+		       "%s/EINVAL",
+		       swrap_dir);
+
+	if ((size_t)ret >= sizeof(un->sun_path)) {
+		return ENAMETOOLONG;
+	}
+
+	return 0;
+}
+
+static bool swrap_dir_usable(const char *swrap_dir)
+{
+	struct sockaddr_un un;
+	int ret;
+
+	ret = swrap_un_path(&un, swrap_dir, SOCKET_TYPE_CHAR_TCP, 0, 0);
+	if (ret == 0) {
+		return true;
+	}
+
+	ret = swrap_un_path_EINVAL(&un, swrap_dir);
+	if (ret == 0) {
+		return true;
+	}
+
+	return false;
+}
+
 static char *socket_wrapper_dir(void)
 {
 	char *swrap_dir = NULL;
 	char *s = getenv("SOCKET_WRAPPER_DIR");
+	char *t;
+	bool ok;
 
 	if (s == NULL) {
 		SWRAP_LOG(SWRAP_LOG_WARN, "SOCKET_WRAPPER_DIR not set");
@@ -1385,9 +1448,43 @@ static char *socket_wrapper_dir(void)
 		SWRAP_LOG(SWRAP_LOG_ERROR,
 			  "Unable to resolve socket_wrapper dir path: %s",
 			  strerror(errno));
-		return NULL;
+		abort();
+	}
+
+	ok = swrap_dir_usable(swrap_dir);
+	if (ok) {
+		goto done;
+	}
+
+	free(swrap_dir);
+
+	ok = swrap_dir_usable(s);
+	if (!ok) {
+		SWRAP_LOG(SWRAP_LOG_ERROR, "SOCKET_WRAPPER_DIR is too long");
+		abort();
+	}
+
+	t = getenv("SOCKET_WRAPPER_DIR_ALLOW_ORIG");
+	if (t == NULL) {
+		SWRAP_LOG(SWRAP_LOG_ERROR,
+			  "realpath(SOCKET_WRAPPER_DIR) too long and "
+			  "SOCKET_WRAPPER_DIR_ALLOW_ORIG not set");
+		abort();
+
 	}
 
+	swrap_dir = strdup(s);
+	if (swrap_dir == NULL) {
+		SWRAP_LOG(SWRAP_LOG_ERROR,
+			  "Unable to duplicate socket_wrapper dir path");
+		abort();
+	}
+
+	SWRAP_LOG(SWRAP_LOG_WARN,
+		  "realpath(SOCKET_WRAPPER_DIR) too long, "
+		  "using original SOCKET_WRAPPER_DIR\n");
+
+done:
 	SWRAP_LOG(SWRAP_LOG_TRACE, "socket_wrapper_dir: %s", swrap_dir);
 	return swrap_dir;
 }
@@ -1932,16 +2029,14 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i
 	}
 
 	if (is_bcast) {
-		snprintf(un->sun_path, sizeof(un->sun_path),
-			 "%s/EINVAL", swrap_dir);
+		swrap_un_path_EINVAL(un, swrap_dir);
 		SWRAP_LOG(SWRAP_LOG_DEBUG, "un path [%s]", un->sun_path);
 		SAFE_FREE(swrap_dir);
 		/* the caller need to do more processing */
 		return 0;
 	}
 
-	snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
-		 swrap_dir, type, iface, prt);
+	swrap_un_path(un, swrap_dir, type, iface, prt);
 	SWRAP_LOG(SWRAP_LOG_DEBUG, "un path [%s]", un->sun_path);
 
 	SAFE_FREE(swrap_dir);
@@ -2109,8 +2204,7 @@ static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr *in
 	if (prt == 0) {
 		/* handle auto-allocation of ephemeral ports */
 		for (prt = 5001; prt < 10000; prt++) {
-			snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
-				 swrap_dir, type, iface, prt);
+			swrap_un_path(un, swrap_dir, type, iface, prt);
 			if (stat(un->sun_path, &st) == 0) continue;
 
 			set_port(si->family, prt, &si->myname);
@@ -2126,8 +2220,7 @@ static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr *in
 		}
 	}
 
-	snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
-		 swrap_dir, type, iface, prt);
+	swrap_un_path(un, swrap_dir, type, iface, prt);
 	SWRAP_LOG(SWRAP_LOG_DEBUG, "un path [%s]", un->sun_path);
 
 	SAFE_FREE(swrap_dir);
@@ -3719,9 +3812,11 @@ static int swrap_auto_bind(int fd, struct socket_info *si, int family)
 
 	for (i = 0; i < SOCKET_MAX_SOCKETS; i++) {
 		port = autobind_start + i;
-		snprintf(un_addr.sa.un.sun_path, sizeof(un_addr.sa.un.sun_path),
-			 "%s/"SOCKET_FORMAT, swrap_dir, type,
-			 socket_wrapper_default_iface(), port);
+		swrap_un_path(&un_addr.sa.un,
+			      swrap_dir,
+			      type,
+			      socket_wrapper_default_iface(),
+			      port);
 		if (stat(un_addr.sa.un.sun_path, &st) == 0) continue;
 
 		ret = libc_bind(fd, &un_addr.sa.s, un_addr.sa_socklen);
@@ -4097,6 +4192,9 @@ static int swrap_listen(int s, int backlog)
 	}
 
 	ret = libc_listen(s, backlog);
+	if (ret == 0) {
+		si->listening = 1;
+	}
 
 out:
 	SWRAP_UNLOCK_SI(si);
@@ -4446,6 +4544,56 @@ static int swrap_getsockopt(int s, int level, int optname,
 			ret = 0;
 			goto done;
 #endif /* TCP_NODELAY */
+#ifdef TCP_INFO
+		case TCP_INFO: {
+			struct tcp_info info;
+			socklen_t ilen = sizeof(info);
+
+#ifdef HAVE_NETINET_TCP_FSM_H
+/* This is FreeBSD */
+# define __TCP_LISTEN TCPS_LISTEN
+# define __TCP_ESTABLISHED TCPS_ESTABLISHED
+# define __TCP_CLOSE TCPS_CLOSED
+#else
+/* This is Linux */
+# define __TCP_LISTEN TCP_LISTEN
+# define __TCP_ESTABLISHED TCP_ESTABLISHED
+# define __TCP_CLOSE TCP_CLOSE
+#endif
+
+			ZERO_STRUCT(info);
+			if (si->listening) {
+				info.tcpi_state = __TCP_LISTEN;
+			} else if (si->connected) {
+				/*
+				 * For now we just fake a few values
+				 * supported both by FreeBSD and Linux
+				 */
+				info.tcpi_state = __TCP_ESTABLISHED;
+				info.tcpi_rto = 200000;  /* 200 msec */
+				info.tcpi_rtt = 5000;    /* 5 msec */
+				info.tcpi_rttvar = 5000; /* 5 msec */
+			} else {
+				info.tcpi_state = __TCP_CLOSE;
+				info.tcpi_rto = 1000000;  /* 1 sec */
+				info.tcpi_rtt = 0;
+				info.tcpi_rttvar = 250000; /* 250 msec */
+			}
+
+			if (optval == NULL || optlen == NULL ||
+			    *optlen < (socklen_t)ilen) {
+				errno = EINVAL;
+				ret = -1;
+				goto done;
+			}
+
+			*optlen = ilen;
+			memcpy(optval, &info, ilen);
+
+			ret = 0;
+			goto done;
+		}
+#endif /* TCP_INFO */
 		default:
 			break;
 		}
@@ -4578,7 +4726,7 @@ static int swrap_vioctl(int s, unsigned long int r, va_list va)
 {
 	struct socket_info *si = find_socket_info(s);
 	va_list ap;
-	int value;
+	int *value_ptr = NULL;
 	int rc;
 
 	if (!si) {
@@ -4593,14 +4741,34 @@ static int swrap_vioctl(int s, unsigned long int r, va_list va)
 
 	switch (r) {
 	case FIONREAD:
-		value = *((int *)va_arg(ap, int *));
+		if (rc == 0) {
+			value_ptr = ((int *)va_arg(ap, int *));
+		}
 
 		if (rc == -1 && errno != EAGAIN && errno != ENOBUFS) {
 			swrap_pcap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
-		} else if (value == 0) { /* END OF FILE */
+		} else if (value_ptr != NULL && *value_ptr == 0) { /* END OF FILE */
 			swrap_pcap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
 		}
 		break;
+#ifdef FIONWRITE
+	case FIONWRITE:
+		/* this is FreeBSD */
+		FALL_THROUGH; /* to TIOCOUTQ */
+#endif /* FIONWRITE */
+	case TIOCOUTQ: /* same as SIOCOUTQ on Linux */
+		/*
+		 * This may return more bytes then the application
+		 * sent into the socket, for tcp it should
+		 * return the number of unacked bytes.
+		 *
+		 * On AF_UNIX, all bytes are immediately acked!
+		 */
+		if (rc == 0) {
+			value_ptr = ((int *)va_arg(ap, int *));
+			*value_ptr = 0;
+		}
+		break;
 	}
 
 	va_end(ap);
@@ -5529,9 +5697,11 @@ static ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags,
 		}
 
 		for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
-			snprintf(un_addr.sa.un.sun_path,
-				 sizeof(un_addr.sa.un.sun_path),
-				 "%s/"SOCKET_FORMAT, swrap_dir, type, iface, prt);
+			swrap_un_path(&un_addr.sa.un,
+				      swrap_dir,
+				      type,
+				      iface,
+				      prt);
 			if (stat(un_addr.sa.un.sun_path, &st) != 0) continue;
 
 			/* ignore the any errors in broadcast sends */
@@ -6038,8 +6208,7 @@ static ssize_t swrap_sendmsg(int s, const struct msghdr *omsg, int flags)
 		}
 
 		for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
-			snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT,
-				 swrap_dir, type, iface, prt);
+			swrap_un_path(&un_addr, swrap_dir, type, iface, prt);
 			if (stat(un_addr.sun_path, &st) != 0) continue;
 
 			msg.msg_name = &un_addr;           /* optional address */
diff --git a/tests/echo_srv.c b/tests/echo_srv.c
index 93778f2..87c85f7 100644
--- a/tests/echo_srv.c
+++ b/tests/echo_srv.c
@@ -9,6 +9,10 @@
 
 #include <arpa/inet.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef HAVE_NETINET_TCP_FSM_H
+#include <netinet/tcp_fsm.h>
+#endif
 #include <netdb.h>
 #include <resolv.h>
 
@@ -315,6 +319,34 @@ static int setup_srv(struct echo_srv_opts *opts, int *_sock)
             perror("listen");
             return ret;
         }
+#ifdef TCP_INFO
+        {
+            struct tcp_info info;
+            socklen_t optlen = sizeof(info);
+
+            ZERO_STRUCT(info);
+            ret = getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &optlen);
+            if (ret == -1) {
+                ret = errno;
+                perror("TCP_INFO failed");
+                close(sock);
+                return ret;
+            }
+#ifdef HAVE_NETINET_TCP_FSM_H
+/* This is FreeBSD */
+# define __TCP_LISTEN TCPS_LISTEN
+#else
+/* This is Linux */
+# define __TCP_LISTEN TCP_LISTEN
+#endif
+            if (info.tcpi_state != __TCP_LISTEN) {
+                errno = ret = ERANGE;
+                perror("not __TCP_LISTEN => ERANGE...");
+                close(sock);
+                return ret;
+            }
+        }
+#endif /* TCP_INFO */
     }
 
     *_sock = sock;
diff --git a/tests/test_echo_tcp_socket_options.c b/tests/test_echo_tcp_socket_options.c
index dfa46fe..5e5661d 100644
--- a/tests/test_echo_tcp_socket_options.c
+++ b/tests/test_echo_tcp_socket_options.c
@@ -11,12 +11,25 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#ifdef HAVE_NETINET_TCP_FSM_H
+#include <netinet/tcp_fsm.h>
+#endif
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 
+#ifdef HAVE_NETINET_TCP_FSM_H
+/* This is FreeBSD */
+# define __TCP_ESTABLISHED TCPS_ESTABLISHED
+# define __TCP_CLOSE TCPS_CLOSED
+#else
+/* This is Linux */
+# define __TCP_ESTABLISHED TCP_ESTABLISHED
+# define __TCP_CLOSE TCP_CLOSE
+#endif
+
 #ifndef ZERO_STRUCT
 #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
 #endif
@@ -300,6 +313,9 @@ static void test_sockopt_tcp(void **state)
 		.sa_socklen = sizeof(struct sockaddr_in),
 	};
 	int opt = -1;
+#ifdef TCP_INFO
+	struct tcp_info info;
+#endif
 	socklen_t optlen = sizeof(int);
 	int rc;
 
@@ -318,9 +334,27 @@ static void test_sockopt_tcp(void **state)
 		       &addr.sa.in.sin_addr);
 	assert_int_equal(rc, 1);
 
+#ifdef TCP_INFO
+	ZERO_STRUCT(info);
+	optlen = sizeof(info);
+	rc = getsockopt(s, IPPROTO_TCP, TCP_INFO, &info, &optlen);
+	assert_return_code(rc, errno);
+	assert_int_equal(optlen, sizeof(info));
+	printf("info.tcpi_state=0x%x\n", info.tcpi_state);


-- 
Socket Wrapper Repository



More information about the samba-cvs mailing list