[PATCH v3] net: enhance decoding of getsockopt(SO_ERROR)

Masatake YAMATO yamato at redhat.com
Mon Dec 17 19:16:11 UTC 2018


* net.c (print_get_error): New function decoding error
number returned as option value for SO_ERROR option.
(print_getsockopt) <case SO_ERROR>: Call print_get_error.

* tests/so_error.c: New test.
* tests/gen_tests.in (so_error): Likewise.
* tests/pure_executables.list: Add so_error.
* tests/.gitignore: Likewise.

Change in v2:
* Use print_xlat_ex to print errno.
  Suggested by ldv.

Changes in v3:
* Update tests/.gitignore.
* Delete unused local var.
* Update error messages.
* Change comment style.
* Use sizeof to variable, not type.
* Remoev 'else' to make if block stand alone.
* Move local variable to for initializer.
* Move '{' block starter to the end of if line from beginning
  of a line.
  All suggesetd by ldv.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 net.c                       |  20 ++++++
 tests/.gitignore            |   1 +
 tests/gen_tests.in          |   1 +
 tests/pure_executables.list |   1 +
 tests/so_error.c            | 124 ++++++++++++++++++++++++++++++++++++
 5 files changed, 147 insertions(+)
 create mode 100644 tests/so_error.c

diff --git a/net.c b/net.c
index f65b6619..cc1f0f5e 100644
--- a/net.c
+++ b/net.c
@@ -671,6 +671,23 @@ print_get_ucred(struct tcb *const tcp, const kernel_ulong_t addr,
 	tprints("}");
 }
 
+static void
+print_get_error(struct tcb *const tcp, const kernel_ulong_t addr,
+		unsigned int len)
+{
+	unsigned int err;
+
+	if (len > sizeof(err))
+		err = sizeof(err);
+
+	if (umoven_or_printaddr(tcp, addr, len, &err))
+		return;
+
+	tprints("[");
+	print_xlat_ex(err, err_name(err), XLAT_STYLE_FMT_U);
+	tprints("]");
+}
+
 #ifdef PACKET_STATISTICS
 static void
 print_tpacket_stats(struct tcb *const tcp, const kernel_ulong_t addr,
@@ -786,6 +803,9 @@ print_getsockopt(struct tcb *const tcp, const unsigned int level,
 			else
 				printaddr(addr);
 			return;
+		case SO_ERROR:
+			print_get_error(tcp, addr, rlen);
+			return;
 		}
 		break;
 
diff --git a/tests/.gitignore b/tests/.gitignore
index 58729054..adfa5005 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -499,6 +499,7 @@ sigprocmask
 sigreturn
 sigsuspend
 sleep
+so_error
 so_linger
 so_peercred
 so_peercred-Xabbrev
diff --git a/tests/gen_tests.in b/tests/gen_tests.in
index 7374add8..d6b4a068 100644
--- a/tests/gen_tests.in
+++ b/tests/gen_tests.in
@@ -431,6 +431,7 @@ sigpending	-a15
 sigprocmask	-a34
 sigreturn	-esignal='!USR1'
 sigsuspend	-a19 -esignal=none
+so_error	-e trace=getsockopt
 so_linger	-e trace=getsockopt,setsockopt
 so_peercred	-e trace=getsockopt
 so_peercred-Xabbrev	-e trace=getsockopt -Xabbrev
diff --git a/tests/pure_executables.list b/tests/pure_executables.list
index a4e9020a..53adb99f 100755
--- a/tests/pure_executables.list
+++ b/tests/pure_executables.list
@@ -410,6 +410,7 @@ sigpending
 sigprocmask
 sigreturn
 sigsuspend
+so_error
 so_linger
 so_peercred
 so_peercred-Xabbrev
diff --git a/tests/so_error.c b/tests/so_error.c
new file mode 100644
index 00000000..a2650ece
--- /dev/null
+++ b/tests/so_error.c
@@ -0,0 +1,124 @@
+/*
+ * Check decoding of SO_ERROR socket option.
+ *
+ * Copyright (c) 2018 Masatake YAMATO <yamato at redhat.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tests.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static in_port_t
+reserve_ephemeral_port(void)
+{
+	int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sd < 0)
+		perror_msg_and_skip("server socket AF_UNIX SOCK_STREAM");
+
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+	};
+
+	/*
+	 * The range is defined in /proc/sys/net/ipv4/ip_local_port_range.
+	 * We use default range here.
+	 */
+	for (in_port_t port = 49152; port < 61000; port++)
+	{
+		/* Just bind here. No listen. */
+		addr.sin_port = htons(port);
+		if (bind(sd, &addr, sizeof(addr)) == 0)
+			return port;
+	}
+	error_msg_and_skip("no ephemeral port available for test purpose");
+}
+
+int
+main(void)
+{
+	in_port_t port = reserve_ephemeral_port ();
+
+	/*
+	 * Connect to the reserved port in NONBLOCK mode.
+	 * The port is reserved but not listened. So
+	 * the client doing "connect" gets error asynchronously.
+	 */
+	int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (fd < 0)
+		perror_msg_and_skip("socket AF_UNIX SOCK_STREAM");
+
+	int flag = fcntl(fd, F_GETFL);
+	if (flag < 0)
+		perror_msg_and_skip("fcntl F_GETFL");
+	flag |= O_NONBLOCK;
+	if (fcntl(fd, F_SETFL, flag) < 0)
+		perror_msg_and_skip("fcntl F_SETFL");
+
+	struct sockaddr_in addr = {
+		.sin_family = AF_INET,
+		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+		.sin_port = htons(port),
+	};
+	if (connect(fd, &addr, sizeof(addr)) == 0)
+		error_msg_and_skip("successfully connected unexpectedly");
+	if (errno != EINPROGRESS)
+		perror_msg_and_skip("connect failed for unexpected reason");
+
+	struct timeval to = {
+		.tv_sec =  1,
+		.tv_usec = 0,
+	};
+	fd_set wfds;
+	FD_ZERO(&wfds);
+	FD_SET(fd, &wfds);
+	if (select(fd + 1, NULL, &wfds, NULL, &to) < 0)
+		perror_msg_and_skip("select");
+
+	int sock_errno;
+	socklen_t optlen = sizeof(sock_errno);
+	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen) < 0)
+		perror_msg_and_skip("getsockopt");
+	if (sock_errno != ECONNREFUSED) {
+		errno = sock_errno;
+		perror_msg_and_skip("unexpected socket error");
+	}
+	if (optlen != sizeof(sock_errno))
+		error_msg_and_skip("unexpected data size for error option: %d",
+			optlen);
+
+	printf("getsockopt(%d, SOL_SOCKET, SO_ERROR, [ECONNREFUSED], [%u]) = 0\n",
+	       fd, optlen);
+	puts("+++ exited with 0 +++");
+	return 0;
+}
-- 
2.17.2



More information about the Strace-devel mailing list