1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2024 Oxide Computer Company
14 */
15
16 /*
17 * Test that setting timeout options on a UNIX stream socket after connection
18 * works, in that the timeout values are accepted and subsequently returned.
19 */
20
21 #include <err.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <unistd.h>
29
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/stdbool.h>
33 #include <sys/sysmacros.h>
34 #include <sys/time.h>
35 #include <sys/types.h>
36 #include <sys/un.h>
37
38 static bool pass = true;
39
40 typedef struct to_test {
41 char *name; /* Name of the test */
42 int option; /* Socket option */
43 time_t sec; /* Number of seconds */
44 suseconds_t usec; /* and microseconds */
45 } to_test_t;
46
47 static to_test_t tests[] = {
48 {
49 .name = "Set 5s receive",
50 .option = SO_RCVTIMEO,
51 .sec = 5,
52 .usec = 0
53 }, {
54 .name = "Set 5s send",
55 .option = SO_SNDTIMEO,
56 .sec = 5,
57 .usec = 0
58 }, {
59 .name = "Set 15410s receive",
60 .option = SO_RCVTIMEO,
61 .sec = 15410,
62 .usec = 0
63 }, {
64 .name = "Set 15410s send",
65 .option = SO_SNDTIMEO,
66 .sec = 15410,
67 .usec = 0
68 }, {
69 .name = "Set 0s receive",
70 .option = SO_RCVTIMEO,
71 .sec = 0,
72 .usec = 0
73 }, {
74 .name = "Set 0s send",
75 .option = SO_SNDTIMEO,
76 .sec = 0,
77 .usec = 0
78 }, {
79 .name = "Set 5.5s receive",
80 .option = SO_RCVTIMEO,
81 .sec = 5,
82 .usec = MICROSEC / 2,
83 }, {
84 .name = "Set 5.5s send",
85 .option = SO_SNDTIMEO,
86 .sec = 5,
87 .usec = MICROSEC / 2,
88 }
89 };
90
91 static int
server(const char * sockpath)92 server(const char *sockpath)
93 {
94 struct sockaddr_un addr;
95 int sock;
96
97 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
98 if (sock == -1)
99 err(EXIT_FAILURE, "failed to create socket");
100 addr.sun_family = AF_UNIX;
101 strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
102 if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
103 err(EXIT_FAILURE, "bind failed");
104 if (listen(sock, 0) == -1)
105 err(EXIT_FAILURE, "listen failed");
106
107 return (sock);
108 }
109
110 static int
client(const char * sockpath)111 client(const char *sockpath)
112 {
113 struct sockaddr_un addr;
114 int sock;
115
116 sock = socket(PF_LOCAL, SOCK_STREAM, 0);
117 if (sock == -1)
118 err(EXIT_FAILURE, "failed to create socket");
119 addr.sun_family = AF_UNIX;
120 strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path));
121 if (connect(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1)
122 err(EXIT_FAILURE, "could not connect to server socket");
123
124 return (sock);
125 }
126
127 static void __PRINTFLIKE(2)
fail(const to_test_t * t,const char * fmt,...)128 fail(const to_test_t *t, const char *fmt, ...)
129 {
130 va_list ap;
131
132 fprintf(stderr, "[FAIL] %s: ", t->name);
133 va_start(ap, fmt);
134 vfprintf(stderr, fmt, ap);
135 va_end(ap);
136 fprintf(stderr, "\n");
137 pass = false;
138 }
139
140 int
main(int argc,const char ** argv)141 main(int argc, const char **argv)
142 {
143 char sockpath[] = "/tmp/to.testsock.XXXXXX";
144 int sfd, cfd;
145
146 if (mktemp(sockpath) == NULL)
147 err(EXIT_FAILURE, "Failed to make temporary socket path");
148
149 sfd = server(sockpath);
150 cfd = client(sockpath);
151
152 for (uint_t i = 0; i < ARRAY_SIZE(tests); i++) {
153 const to_test_t *t = &tests[i];
154 struct timeval tv = { 0 };
155 socklen_t optlen;
156
157 tv.tv_sec = t->sec;
158 tv.tv_usec = t->usec;
159 optlen = sizeof (tv);
160 if (setsockopt(cfd, SOL_SOCKET, t->option, &tv, optlen) != 0) {
161 fail(t, "setsockopt error: %s", strerror(errno));
162 pass = false;
163 continue;
164 }
165
166 bzero(&tv, sizeof (tv));
167 if (getsockopt(cfd, SOL_SOCKET, t->option, &tv, &optlen) != 0) {
168 fail(t, "getsockopt error: %s", strerror(errno));
169 pass = false;
170 continue;
171 }
172
173 if (optlen != sizeof (tv)) {
174 fail(t,
175 "getsockopt returned incorrect length: %ld"
176 " vs. %zd", (long)optlen, sizeof (tv));
177 continue;
178 }
179
180 if (tv.tv_sec != t->sec) {
181 fail(t, "returned tv_sec value mismatch: %ld "
182 "vs. expected %ld", tv.tv_sec, t->sec);
183 continue;
184 }
185 if (tv.tv_usec != t->usec) {
186 fail(t, "returned tv_usec value mismatch: %ld "
187 "vs. expected %ld", tv.tv_usec, t->usec);
188 continue;
189 }
190
191 printf("[PASS] %s\n", t->name);
192 }
193
194 close(cfd);
195 close(sfd);
196 unlink(sockpath);
197
198 return (pass ? 0 : EXIT_FAILURE);
199 }
200