]> granicus.if.org Git - strace/commitdiff
net: enhance decoding of getsockopt(SO_ERROR)
authorMasatake YAMATO <yamato@redhat.com>
Mon, 17 Dec 2018 19:16:11 +0000 (04:16 +0900)
committerDmitry V. Levin <ldv@altlinux.org>
Mon, 17 Dec 2018 19:19:23 +0000 (19:19 +0000)
* 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.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
Signed-off-by: Dmitry V. Levin <ldv@altlinux.org>
net.c
tests/.gitignore
tests/gen_tests.in
tests/pure_executables.list
tests/so_error.c [new file with mode: 0644]

diff --git a/net.c b/net.c
index 3058e2d2bb350a28f1c31f68ba0db32652b1f00c..bcab329469a56bf2e931af3b864faae73149eb26 100644 (file)
--- a/net.c
+++ b/net.c
@@ -651,6 +651,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,
@@ -766,6 +783,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;
 
index e25d4f920c3caf48cc582483bb0c98dc0e9e7cc2..918b95be0dd1e9f7dd2ba0d987b6d0a6829d2045 100644 (file)
@@ -510,6 +510,7 @@ sigprocmask
 sigreturn
 sigsuspend
 sleep
+so_error
 so_linger
 so_peercred
 so_peercred-Xabbrev
index 31443ffdf28188206e0958024f80266ea4db4f04..9f7e605c706a055cb9fe1cc4e7a511a15f5f888e 100644 (file)
@@ -421,6 +421,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
index 2c6eabd74fe63dab9a02a0ffbd2b76f6d845000b..b8120f2b41277929c2462be0035eea2aa4636218 100755 (executable)
@@ -421,6 +421,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 (file)
index 0000000..2592a3c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Check decoding of SO_ERROR socket option.
+ *
+ * Copyright (c) 2018 Masatake YAMATO <yamato@redhat.com>
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#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 the 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 purposes");
+}
+
+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("connect unexpectedly succeeded");
+       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;
+}