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