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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
/*
* Check decoding of SO_LINGER socket option.
*
* Copyright (c) 2017-2021 Dmitry V. Levin <ldv@strace.io>
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "tests.h"
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
static const char *errstr;
static int
get_linger(int fd, void *val, socklen_t *len)
{
int rc = getsockopt(fd, SOL_SOCKET, SO_LINGER, val, len);
errstr = sprintrc(rc);
return rc;
}
static int
set_linger(int fd, void *val, socklen_t len)
{
int rc = setsockopt(fd, SOL_SOCKET, SO_LINGER, val, len);
errstr = sprintrc(rc);
return rc;
}
int
main(void)
{
TAIL_ALLOC_OBJECT_CONST_PTR(struct linger, linger);
TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len);
const unsigned int sizeof_l_onoff = sizeof(linger->l_onoff);
struct linger *const l_onoff = tail_alloc(sizeof_l_onoff);
const unsigned int sizeof_l_onoff_truncated = sizeof_l_onoff - 1;
struct linger *const l_onoff_truncated =
tail_alloc(sizeof_l_onoff_truncated);
const unsigned int sizeof_l_linger_truncated =
offsetofend(struct linger, l_linger) - 1;
struct linger *const l_linger_truncated =
tail_alloc(sizeof_l_linger_truncated);
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
perror_msg_and_skip("socket AF_UNIX SOCK_STREAM");
/* classic getsockopt */
*len = sizeof(*linger);
get_linger(fd, linger, len);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
", [%d]) = %s\n",
fd, linger->l_onoff, linger->l_linger, *len, errstr);
/* classic setsockopt */
linger->l_onoff = -15;
linger->l_linger = -42;
set_linger(fd, linger, sizeof(*linger));
printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
", %d) = %s\n",
fd, linger->l_onoff, linger->l_linger,
(unsigned int) sizeof(*linger), errstr);
/* setsockopt with optlen larger than necessary */
set_linger(fd, linger, sizeof(*linger) + 1);
printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
", %d) = %s\n",
fd, linger->l_onoff, linger->l_linger,
(unsigned int) sizeof(*linger) + 1, errstr);
/* setsockopt with optlen < 0 - EINVAL */
set_linger(fd, linger, -1U);
printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, -1) = %s\n",
fd, linger, errstr);
/* setsockopt with optlen smaller than necessary - EINVAL */
set_linger(fd, linger, sizeof(linger->l_onoff));
printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %d) = %s\n",
fd, linger, (unsigned int) sizeof(linger->l_onoff), errstr);
/* setsockopt optval EFAULT */
set_linger(fd, &linger->l_linger, sizeof(*linger));
printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %d) = %s\n",
fd, &linger->l_linger, (unsigned int) sizeof(*linger), errstr);
/* getsockopt with zero optlen */
*len = 0;
get_linger(fd, linger, len);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, [0]) = %s\n",
fd, linger, errstr);
/* getsockopt with optlen larger than necessary - shortened */
*len = sizeof(*linger) + 1;
get_linger(fd, linger, len);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
", [%u => %d]) = %s\n",
fd, linger->l_onoff, linger->l_linger,
(unsigned int) sizeof(*linger) + 1, *len, errstr);
/*
* getsockopt with optlen less than sizeof(linger->l_onoff):
* the part of struct linger.l_onoff is printed in hex.
*/
*len = sizeof_l_onoff_truncated;
get_linger(fd, l_onoff_truncated, len);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=", fd);
print_quoted_hex(l_onoff_truncated, *len);
printf("}, [%d]) = %s\n", *len, errstr);
/*
* getsockopt with optlen equals to sizeof(struct linger.l_onoff):
* struct linger.l_linger is not printed.
*/
*len = sizeof_l_onoff;
get_linger(fd, l_onoff, len);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d}"
", [%d]) = %s\n",
fd, l_onoff->l_onoff, *len, errstr);
/*
* getsockopt with optlen greater than sizeof(struct linger.l_onoff)
* but smaller than sizeof(struct linger):
* the part of struct linger.l_linger is printed in hex.
*/
*len = sizeof_l_linger_truncated;
get_linger(fd, l_linger_truncated, len);
/*
* Copy to a properly aligned structure to avoid unaligned access
* to struct linger.l_onoff field.
*/
memcpy(linger, l_linger_truncated, sizeof_l_linger_truncated);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=",
fd, linger->l_onoff);
print_quoted_hex(&linger->l_linger, sizeof_l_linger_truncated -
offsetof(struct linger, l_linger));
printf("}, [%d]) = %s\n", *len, errstr);
/* getsockopt optval EFAULT */
*len = sizeof(*linger);
get_linger(fd, &linger->l_linger, len);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, [%d]) = %s\n",
fd, &linger->l_linger, *len, errstr);
/* getsockopt optlen EFAULT */
get_linger(fd, linger, len + 1);
printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %p) = %s\n",
fd, linger, len + 1, errstr);
puts("+++ exited with 0 +++");
return 0;
}
|