(root)/
strace-6.5/
tests-m32/
so_error.c
       1  /*
       2   * Check decoding of SO_ERROR socket option.
       3   *
       4   * Copyright (c) 2018 Masatake YAMATO <yamato@redhat.com>
       5   * Copyright (c) 2018-2022 The strace developers.
       6   * All rights reserved.
       7   *
       8   * SPDX-License-Identifier: GPL-2.0-or-later
       9   */
      10  
      11  #include "tests.h"
      12  
      13  #include <errno.h>
      14  #include <fcntl.h>
      15  #include <netinet/in.h>
      16  #include <stdio.h>
      17  #include <sys/select.h>
      18  #include <sys/socket.h>
      19  #include <sys/types.h>
      20  #include <unistd.h>
      21  
      22  static in_port_t
      23  reserve_ephemeral_port(void)
      24  {
      25  	int sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      26  	if (sd < 0)
      27  		perror_msg_and_skip("server socket AF_UNIX SOCK_STREAM");
      28  
      29  	struct sockaddr_in addr = {
      30  		.sin_family = AF_INET,
      31  		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
      32  	};
      33  
      34  	/*
      35  	 * The range is defined in /proc/sys/net/ipv4/ip_local_port_range.
      36  	 * We use the default range here.
      37  	 */
      38  	for (in_port_t port = 49152; port < 61000; port++) {
      39  		/* Just bind here. No listen. */
      40  		addr.sin_port = htons(port);
      41  		if (bind(sd, (void *) &addr, sizeof(addr)) == 0)
      42  			return port;
      43  	}
      44  	error_msg_and_skip("no ephemeral port available for test purposes");
      45  }
      46  
      47  int
      48  main(void)
      49  {
      50  	static const int sizes[] = {
      51  		-1, 0, 1,
      52  		sizeof(int) - 1,
      53  		sizeof(int),
      54  		sizeof(int) + 1,
      55  	};
      56  
      57  	TAIL_ALLOC_OBJECT_CONST_PTR(int, sock_errno);
      58  	in_port_t port = reserve_ephemeral_port();
      59  	const struct sockaddr_in addr = {
      60  		.sin_family = AF_INET,
      61  		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
      62  		.sin_port = htons(port),
      63  	};
      64  
      65  	for (size_t i = 0; i < ARRAY_SIZE(sizes); i++) {
      66  		/*
      67  		 * Connect to the reserved port in NONBLOCK mode.
      68  		 * The port is reserved but not listened. So
      69  		 * the client doing "connect" gets error asynchronously.
      70  		 */
      71  		int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      72  		if (fd < 0)
      73  			perror_msg_and_skip("socket AF_UNIX SOCK_STREAM");
      74  
      75  		int flag = fcntl(fd, F_GETFL);
      76  		if (flag < 0)
      77  			perror_msg_and_skip("fcntl F_GETFL");
      78  		flag |= O_NONBLOCK;
      79  		if (fcntl(fd, F_SETFL, flag) < 0)
      80  			perror_msg_and_skip("fcntl F_SETFL");
      81  
      82  		if (connect(fd, (void *) &addr, sizeof(addr)) == 0)
      83  			error_msg_and_skip("connect unexpectedly succeeded");
      84  		if (errno != EINPROGRESS)
      85  			perror_msg_and_skip("connect failed for unexpected reason");
      86  
      87  		struct timeval to = {
      88  			.tv_sec =  1,
      89  			.tv_usec = 0,
      90  		};
      91  		fd_set wfds;
      92  		FD_ZERO(&wfds);
      93  		FD_SET(fd, &wfds);
      94  		if (select(fd + 1, NULL, &wfds, NULL, &to) < 0)
      95  			perror_msg_and_skip("select");
      96  
      97  		*sock_errno = 0xbadc0ded;
      98  		socklen_t optlen = sizes[i];
      99  		long rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, sock_errno,
     100  				     &optlen);
     101  		const char *errstr = sprintrc(rc);
     102  		if (sizes[i] > 0 && rc < 0)
     103  			perror_msg_and_skip("getsockopt");
     104  		if (sizes[i] >= (int) sizeof(*sock_errno)
     105  		    && *sock_errno != ECONNREFUSED) {
     106  			errno = *sock_errno;
     107  			perror_msg_and_skip("unexpected socket error");
     108  		}
     109  		if (sizes[i] >= (int) sizeof(*sock_errno)
     110  		    && optlen != sizeof(*sock_errno)) {
     111  			error_msg_and_skip("unexpected data size for error"
     112  					   " option: %d", optlen);
     113  		}
     114  
     115  		printf("getsockopt(%d, SOL_SOCKET, SO_ERROR, ", fd);
     116  		if (sizes[i] <= 0) {
     117  			printf("%p, [%d]", sock_errno, sizes[i]);
     118  		} else if (sizes[i] < (int) sizeof(*sock_errno)) {
     119  			print_quoted_hex(sock_errno, sizes[i]);
     120  			printf(", [%u]", sizes[i]);
     121  		} else if (sizes[i] == sizeof(*sock_errno)) {
     122  			printf("[ECONNREFUSED], [%zu]", sizeof(*sock_errno));
     123  		} else {
     124  			printf("[ECONNREFUSED], [%u => %zu]",
     125  			       sizes[i], sizeof(*sock_errno));
     126  		}
     127  		printf(") = %s\n", errstr);
     128  
     129  		close(fd);
     130  	}
     131  
     132  	puts("+++ exited with 0 +++");
     133  	return 0;
     134  }