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 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 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) 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 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