/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2024 Oxide Computer Company */ /* * Test that setting timeout options on a UNIX stream socket after connection * works, in that the timeout values are accepted and subsequently returned. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool pass = true; typedef struct to_test { char *name; /* Name of the test */ int option; /* Socket option */ time_t sec; /* Number of seconds */ suseconds_t usec; /* and microseconds */ } to_test_t; static to_test_t tests[] = { { .name = "Set 5s receive", .option = SO_RCVTIMEO, .sec = 5, .usec = 0 }, { .name = "Set 5s send", .option = SO_SNDTIMEO, .sec = 5, .usec = 0 }, { .name = "Set 15410s receive", .option = SO_RCVTIMEO, .sec = 15410, .usec = 0 }, { .name = "Set 15410s send", .option = SO_SNDTIMEO, .sec = 15410, .usec = 0 }, { .name = "Set 0s receive", .option = SO_RCVTIMEO, .sec = 0, .usec = 0 }, { .name = "Set 0s send", .option = SO_SNDTIMEO, .sec = 0, .usec = 0 }, { .name = "Set 5.5s receive", .option = SO_RCVTIMEO, .sec = 5, .usec = MICROSEC / 2, }, { .name = "Set 5.5s send", .option = SO_SNDTIMEO, .sec = 5, .usec = MICROSEC / 2, } }; static int server(const char *sockpath) { struct sockaddr_un addr; int sock; sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock == -1) err(EXIT_FAILURE, "failed to create socket"); addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path)); if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1) err(EXIT_FAILURE, "bind failed"); if (listen(sock, 0) == -1) err(EXIT_FAILURE, "listen failed"); return (sock); } static int client(const char *sockpath) { struct sockaddr_un addr; int sock; sock = socket(PF_LOCAL, SOCK_STREAM, 0); if (sock == -1) err(EXIT_FAILURE, "failed to create socket"); addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, sockpath, sizeof (addr.sun_path)); if (connect(sock, (struct sockaddr *)&addr, sizeof (addr)) == -1) err(EXIT_FAILURE, "could not connect to server socket"); return (sock); } static void __PRINTFLIKE(2) fail(const to_test_t *t, const char *fmt, ...) { va_list ap; fprintf(stderr, "[FAIL] %s: ", t->name); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); pass = false; } int main(int argc, const char **argv) { char sockpath[] = "/tmp/to.testsock.XXXXXX"; int sfd, cfd; if (mktemp(sockpath) == NULL) err(EXIT_FAILURE, "Failed to make temporary socket path"); sfd = server(sockpath); cfd = client(sockpath); for (uint_t i = 0; i < ARRAY_SIZE(tests); i++) { const to_test_t *t = &tests[i]; struct timeval tv = { 0 }; socklen_t optlen; tv.tv_sec = t->sec; tv.tv_usec = t->usec; optlen = sizeof (tv); if (setsockopt(cfd, SOL_SOCKET, t->option, &tv, optlen) != 0) { fail(t, "setsockopt error: %s", strerror(errno)); pass = false; continue; } bzero(&tv, sizeof (tv)); if (getsockopt(cfd, SOL_SOCKET, t->option, &tv, &optlen) != 0) { fail(t, "getsockopt error: %s", strerror(errno)); pass = false; continue; } if (optlen != sizeof (tv)) { fail(t, "getsockopt returned incorrect length: %ld" " vs. %zd", (long)optlen, sizeof (tv)); continue; } if (tv.tv_sec != t->sec) { fail(t, "returned tv_sec value mismatch: %ld " "vs. expected %ld", tv.tv_sec, t->sec); continue; } if (tv.tv_usec != t->usec) { fail(t, "returned tv_usec value mismatch: %ld " "vs. expected %ld", tv.tv_usec, t->usec); continue; } printf("[PASS] %s\n", t->name); } close(cfd); close(sfd); unlink(sockpath); return (pass ? 0 : EXIT_FAILURE); }