1 /*
2 * Check decoding of SO_LINGER socket option.
3 *
4 * Copyright (c) 2017-2021 Dmitry V. Levin <ldv@strace.io>
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11
12 #include <stddef.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/socket.h>
16 #include <unistd.h>
17
18 static const char *errstr;
19
20 static int
21 get_linger(int fd, void *val, socklen_t *len)
22 {
23 int rc = getsockopt(fd, SOL_SOCKET, SO_LINGER, val, len);
24 errstr = sprintrc(rc);
25 return rc;
26 }
27
28 static int
29 set_linger(int fd, void *val, socklen_t len)
30 {
31 int rc = setsockopt(fd, SOL_SOCKET, SO_LINGER, val, len);
32 errstr = sprintrc(rc);
33 return rc;
34 }
35
36 int
37 main(void)
38 {
39 TAIL_ALLOC_OBJECT_CONST_PTR(struct linger, linger);
40 TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len);
41
42 const unsigned int sizeof_l_onoff = sizeof(linger->l_onoff);
43 struct linger *const l_onoff = tail_alloc(sizeof_l_onoff);
44
45 const unsigned int sizeof_l_onoff_truncated = sizeof_l_onoff - 1;
46 struct linger *const l_onoff_truncated =
47 tail_alloc(sizeof_l_onoff_truncated);
48
49 const unsigned int sizeof_l_linger_truncated =
50 offsetofend(struct linger, l_linger) - 1;
51 struct linger *const l_linger_truncated =
52 tail_alloc(sizeof_l_linger_truncated);
53
54 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
55 if (fd < 0)
56 perror_msg_and_skip("socket AF_UNIX SOCK_STREAM");
57
58 /* classic getsockopt */
59 *len = sizeof(*linger);
60 get_linger(fd, linger, len);
61 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
62 ", [%d]) = %s\n",
63 fd, linger->l_onoff, linger->l_linger, *len, errstr);
64
65 /* classic setsockopt */
66 linger->l_onoff = -15;
67 linger->l_linger = -42;
68 set_linger(fd, linger, sizeof(*linger));
69 printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
70 ", %d) = %s\n",
71 fd, linger->l_onoff, linger->l_linger,
72 (unsigned int) sizeof(*linger), errstr);
73
74 /* setsockopt with optlen larger than necessary */
75 set_linger(fd, linger, sizeof(*linger) + 1);
76 printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
77 ", %d) = %s\n",
78 fd, linger->l_onoff, linger->l_linger,
79 (unsigned int) sizeof(*linger) + 1, errstr);
80
81 /* setsockopt with optlen < 0 - EINVAL */
82 set_linger(fd, linger, -1U);
83 printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, -1) = %s\n",
84 fd, linger, errstr);
85
86 /* setsockopt with optlen smaller than necessary - EINVAL */
87 set_linger(fd, linger, sizeof(linger->l_onoff));
88 printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %d) = %s\n",
89 fd, linger, (unsigned int) sizeof(linger->l_onoff), errstr);
90
91 /* setsockopt optval EFAULT */
92 set_linger(fd, &linger->l_linger, sizeof(*linger));
93 printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %d) = %s\n",
94 fd, &linger->l_linger, (unsigned int) sizeof(*linger), errstr);
95
96 /* getsockopt with zero optlen */
97 *len = 0;
98 get_linger(fd, linger, len);
99 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, [0]) = %s\n",
100 fd, linger, errstr);
101
102 /* getsockopt with optlen larger than necessary - shortened */
103 *len = sizeof(*linger) + 1;
104 get_linger(fd, linger, len);
105 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=%d}"
106 ", [%u => %d]) = %s\n",
107 fd, linger->l_onoff, linger->l_linger,
108 (unsigned int) sizeof(*linger) + 1, *len, errstr);
109
110 /*
111 * getsockopt with optlen less than sizeof(linger->l_onoff):
112 * the part of struct linger.l_onoff is printed in hex.
113 */
114 *len = sizeof_l_onoff_truncated;
115 get_linger(fd, l_onoff_truncated, len);
116 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=", fd);
117 print_quoted_hex(l_onoff_truncated, *len);
118 printf("}, [%d]) = %s\n", *len, errstr);
119
120 /*
121 * getsockopt with optlen equals to sizeof(struct linger.l_onoff):
122 * struct linger.l_linger is not printed.
123 */
124 *len = sizeof_l_onoff;
125 get_linger(fd, l_onoff, len);
126 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d}"
127 ", [%d]) = %s\n",
128 fd, l_onoff->l_onoff, *len, errstr);
129
130 /*
131 * getsockopt with optlen greater than sizeof(struct linger.l_onoff)
132 * but smaller than sizeof(struct linger):
133 * the part of struct linger.l_linger is printed in hex.
134 */
135 *len = sizeof_l_linger_truncated;
136 get_linger(fd, l_linger_truncated, len);
137 /*
138 * Copy to a properly aligned structure to avoid unaligned access
139 * to struct linger.l_onoff field.
140 */
141 memcpy(linger, l_linger_truncated, sizeof_l_linger_truncated);
142 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=",
143 fd, linger->l_onoff);
144 print_quoted_hex(&linger->l_linger, sizeof_l_linger_truncated -
145 offsetof(struct linger, l_linger));
146 printf("}, [%d]) = %s\n", *len, errstr);
147
148 /* getsockopt optval EFAULT */
149 *len = sizeof(*linger);
150 get_linger(fd, &linger->l_linger, len);
151 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, [%d]) = %s\n",
152 fd, &linger->l_linger, *len, errstr);
153
154 /* getsockopt optlen EFAULT */
155 get_linger(fd, linger, len + 1);
156 printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %p) = %s\n",
157 fd, linger, len + 1, errstr);
158
159 puts("+++ exited with 0 +++");
160 return 0;
161 }