From: Masatake YAMATO Date: Mon, 17 Dec 2018 19:16:11 +0000 (+0900) Subject: net: enhance decoding of getsockopt(SO_ERROR) X-Git-Tag: v4.26~45 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=692dbd2c3d2052025e3d4b121d94689f24458651;p=strace net: enhance decoding of getsockopt(SO_ERROR) * net.c (print_get_error): New function decoding error number returned as option value for SO_ERROR option. (print_getsockopt) : 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 Signed-off-by: Dmitry V. Levin --- diff --git a/net.c b/net.c index 3058e2d2..bcab3294 100644 --- 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; diff --git a/tests/.gitignore b/tests/.gitignore index e25d4f92..918b95be 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -510,6 +510,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 31443ffd..9f7e605c 100644 --- a/tests/gen_tests.in +++ b/tests/gen_tests.in @@ -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 diff --git a/tests/pure_executables.list b/tests/pure_executables.list index 2c6eabd7..b8120f2b 100755 --- a/tests/pure_executables.list +++ b/tests/pure_executables.list @@ -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 index 00000000..2592a3c1 --- /dev/null +++ b/tests/so_error.c @@ -0,0 +1,103 @@ +/* + * Check decoding of SO_ERROR socket option. + * + * Copyright (c) 2018 Masatake YAMATO + * All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "tests.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}