Path: blob/main/tools/regression/sockets/unix_bindconnect/unix_bindconnect.c
48254 views
/*-1* Copyright (c) 2005 Robert N. M. Watson2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/types.h>27#include <sys/socket.h>28#include <sys/un.h>2930#include <err.h>31#include <errno.h>32#include <limits.h>33#include <stdio.h>34#include <string.h>35#include <unistd.h>3637/*38* Simple regression test to exercise some error cases relating to the use of39* bind() and connect() on UNIX domain sockets. In particular, make sure40* that when two sockets rendezvous using the file system name space, they41* get the expected success/failure cases.42*43* TODO:44* - Check that the resulting file mode/owner are right.45* - Do the same tests with UNIX domain sockets.46* - Check the results of getsockaddr() and getpeeraddr().47*/4849#define SOCK_NAME_ONE "socket.1"50#define SOCK_NAME_TWO "socket.2"5152#define UNWIND_MAX 10245354static int unwind_len;55static struct unwind {56char u_path[PATH_MAX];57} unwind_list[UNWIND_MAX];5859static void60push_path(const char *path)61{6263if (unwind_len >= UNWIND_MAX)64err(-1, "push_path: one path too many (%s)", path);6566strlcpy(unwind_list[unwind_len].u_path, path, PATH_MAX);67unwind_len++;68}6970static void71unwind(void)72{73int i;7475for (i = unwind_len - 1; i >= 0; i--) {76unlink(unwind_list[i].u_path);77rmdir(unwind_list[i].u_path);78}79}8081static int82bind_test(const char *directory_path)83{84char socket_path[PATH_MAX];85struct sockaddr_un sun;86int sock1, sock2;8788sock1 = socket(PF_UNIX, SOCK_STREAM, 0);89if (sock1 < 0) {90warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");91return (-1);92}9394if (snprintf(socket_path, sizeof(socket_path), "%s/%s",95directory_path, SOCK_NAME_ONE) >= PATH_MAX) {96warn("bind_test: snprintf(socket_path)");97close(sock1);98return (-1);99}100101bzero(&sun, sizeof(sun));102sun.sun_len = sizeof(sun);103sun.sun_family = AF_UNIX;104if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)105>= (int)sizeof(sun.sun_path)) {106warn("bind_test: snprintf(sun.sun_path)");107close(sock1);108return (-1);109}110111if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {112warn("bind_test: bind(sun) #1");113close(sock1);114return (-1);115}116117push_path(socket_path);118119/*120* Once a STREAM UNIX domain socket has been bound, it can't be121* rebound. Expected error is EINVAL.122*/123if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {124warnx("bind_test: bind(sun) #2 succeeded");125close(sock1);126return (-1);127}128if (errno != EINVAL) {129warn("bind_test: bind(sun) #2");130close(sock1);131return (-1);132}133134sock2 = socket(PF_UNIX, SOCK_STREAM, 0);135if (sock2 < 0) {136warn("bind_test: socket(PF_UNIX, SOCK_STREAM, 0)");137close(sock1);138return (-1);139}140141/*142* Since a socket is already bound to the pathname, it can't be bound143* to a second socket. Expected error is EADDRINUSE.144*/145if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {146warnx("bind_test: bind(sun) #3 succeeded");147close(sock1);148close(sock2);149return (-1);150}151if (errno != EADDRINUSE) {152warn("bind_test: bind(sun) #2");153close(sock1);154close(sock2);155return (-1);156}157158close(sock1);159160/*161* The socket bound to the pathname has been closed, but the pathname162* can't be reused without first being unlinked. Expected error is163* EADDRINUSE.164*/165if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {166warnx("bind_test: bind(sun) #4 succeeded");167close(sock2);168return (-1);169}170if (errno != EADDRINUSE) {171warn("bind_test: bind(sun) #4");172close(sock2);173return (-1);174}175176unlink(socket_path);177178/*179* The pathname is now free, so the socket should be able to bind to180* it.181*/182if (bind(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {183warn("bind_test: bind(sun) #5");184close(sock2);185return (-1);186}187188close(sock2);189return (0);190}191192static int193connect_test(const char *directory_path)194{195char socket_path[PATH_MAX];196struct sockaddr_un sun;197int sock1, sock2;198199sock1 = socket(PF_UNIX, SOCK_STREAM, 0);200if (sock1 < 0) {201warn("connect_test: socket(PF_UNIX, SOCK_STREAM, 0)");202return (-1);203}204205if (snprintf(socket_path, sizeof(socket_path), "%s/%s",206directory_path, SOCK_NAME_TWO) >= PATH_MAX) {207warn("connect_test: snprintf(socket_path)");208close(sock1);209return (-1);210}211212bzero(&sun, sizeof(sun));213sun.sun_len = sizeof(sun);214sun.sun_family = AF_UNIX;215if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", socket_path)216>= (int)sizeof(sun.sun_path)) {217warn("connect_test: snprintf(sun.sun_path)");218close(sock1);219return (-1);220}221222/*223* Try connecting to a path that doesn't yet exist. Should fail with224* ENOENT.225*/226if (connect(sock1, (struct sockaddr *)&sun, sizeof(sun)) == 0) {227warnx("connect_test: connect(sun) #1 succeeded");228close(sock1);229return (-1);230}231if (errno != ENOENT) {232warn("connect_test: connect(sun) #1");233close(sock1);234return (-1);235}236237if (bind(sock1, (struct sockaddr *)&sun, sizeof(sun)) < 0) {238warn("connect_test: bind(sun) #1");239close(sock1);240return (-1);241}242243if (listen(sock1, 3) < 0) {244warn("connect_test: listen(sock1)");245close(sock1);246return (-1);247}248249push_path(socket_path);250251sock2 = socket(PF_UNIX, SOCK_STREAM, 0);252if (sock2 < 0) {253warn("socket(PF_UNIX, SOCK_STREAM, 0)");254close(sock1);255return (-1);256}257258/*259* Do a simple connect and make sure that works.260*/261if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) < 0) {262warn("connect(sun) #2");263close(sock1);264return (-1);265}266267close(sock2);268269close(sock1);270271sock2 = socket(PF_UNIX, SOCK_STREAM, 0);272if (sock2 < 0) {273warn("socket(PF_UNIX, SOCK_STREAM, 0)");274return (-1);275}276277/*278* Confirm that once the listen socket is closed, we get a279* connection refused (ECONNREFUSED) when attempting to connect to280* the pathname.281*/282if (connect(sock2, (struct sockaddr *)&sun, sizeof(sun)) == 0) {283warnx("connect(sun) #3 succeeded");284close(sock2);285return (-1);286}287if (errno != ECONNREFUSED) {288warn("connect(sun) #3");289close(sock2);290return (-1);291}292293close(sock2);294unlink(socket_path);295return (0);296}297int298main(void)299{300char directory_path[PATH_MAX];301int error;302303strlcpy(directory_path, "/tmp/unix_bind.XXXXXXX", PATH_MAX);304if (mkdtemp(directory_path) == NULL)305err(-1, "mkdtemp");306push_path(directory_path);307308error = bind_test(directory_path);309310if (error == 0)311error = connect_test(directory_path);312313unwind();314return (error);315}316317318