1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
/*
* Check decoding of SO_ERROR socket option.
*
* Copyright (c) 2018 Masatake YAMATO <yamato@redhat.com>
* Copyright (c) 2018-2022 The strace developers.
* 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, (void *) &addr, sizeof(addr)) == 0)
return port;
}
error_msg_and_skip("no ephemeral port available for test purposes");
}
int
main(void)
{
static const int sizes[] = {
-1, 0, 1,
sizeof(int) - 1,
sizeof(int),
sizeof(int) + 1,
};
TAIL_ALLOC_OBJECT_CONST_PTR(int, sock_errno);
in_port_t port = reserve_ephemeral_port();
const struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
.sin_port = htons(port),
};
for (size_t i = 0; i < ARRAY_SIZE(sizes); i++) {
/*
* 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");
if (connect(fd, (void *) &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");
*sock_errno = 0xbadc0ded;
socklen_t optlen = sizes[i];
long rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, sock_errno,
&optlen);
const char *errstr = sprintrc(rc);
if (sizes[i] > 0 && rc < 0)
perror_msg_and_skip("getsockopt");
if (sizes[i] >= (int) sizeof(*sock_errno)
&& *sock_errno != ECONNREFUSED) {
errno = *sock_errno;
perror_msg_and_skip("unexpected socket error");
}
if (sizes[i] >= (int) sizeof(*sock_errno)
&& optlen != sizeof(*sock_errno)) {
error_msg_and_skip("unexpected data size for error"
" option: %d", optlen);
}
printf("getsockopt(%d, SOL_SOCKET, SO_ERROR, ", fd);
if (sizes[i] <= 0) {
printf("%p, [%d]", sock_errno, sizes[i]);
} else if (sizes[i] < (int) sizeof(*sock_errno)) {
print_quoted_hex(sock_errno, sizes[i]);
printf(", [%u]", sizes[i]);
} else if (sizes[i] == sizeof(*sock_errno)) {
printf("[ECONNREFUSED], [%zu]", sizeof(*sock_errno));
} else {
printf("[ECONNREFUSED], [%u => %zu]",
sizes[i], sizeof(*sock_errno));
}
printf(") = %s\n", errstr);
close(fd);
}
puts("+++ exited with 0 +++");
return 0;
}
|