diff options
author | rwatson <rwatson@FreeBSD.org> | 2004-11-02 17:59:12 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2004-11-02 17:59:12 +0000 |
commit | 2db191c4fec119597f76b66174d2613bce9e6de6 (patch) | |
tree | 202fdfd1897c29481ef5c445b7141cba12636570 | |
parent | 7dde7f3287638e58b27d3d796a647a9a2b660842 (diff) | |
download | FreeBSD-src-2db191c4fec119597f76b66174d2613bce9e6de6.zip FreeBSD-src-2db191c4fec119597f76b66174d2613bce9e6de6.tar.gz |
Add a small regression test that opens a TCP socket, listens on it,
performs a non-blocking connect from another socket, and then closes
the listen socket rather than accepting. This is intended to
exercise the close path in which connections are aborted due to a
close on the listen socket while the connection is in the listen
queue.
-rw-r--r-- | tools/regression/sockets/listenclose/Makefile | 9 | ||||
-rw-r--r-- | tools/regression/sockets/listenclose/listenclose.c | 111 |
2 files changed, 120 insertions, 0 deletions
diff --git a/tools/regression/sockets/listenclose/Makefile b/tools/regression/sockets/listenclose/Makefile new file mode 100644 index 0000000..d2b1fc2 --- /dev/null +++ b/tools/regression/sockets/listenclose/Makefile @@ -0,0 +1,9 @@ +# +# $FreeBSD$ +# + +PROG= listenclose +NOMAN= yes +WARNS= 2 + +.include <bsd.prog.mk> diff --git a/tools/regression/sockets/listenclose/listenclose.c b/tools/regression/sockets/listenclose/listenclose.c new file mode 100644 index 0000000..3053b1d --- /dev/null +++ b/tools/regression/sockets/listenclose/listenclose.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2004 Robert N. M. Watson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +/* + * The listenclose regression test is designed to catch kernel bugs that may + * trigger as a result of performing a close on a listen() socket with as-yet + * unaccepted connections in its queues. This results in the connections + * being aborted, which is a not-often-followed code path. To do this, we + * create a local TCP socket, build a non-blocking connection to it, and then + * close the accept socket. The connection must be non-blocking or the + * program will block and as such connect() will not return as accept() is + * never called. + */ + +int +main(int argc, char *argv[]) +{ + int listen_sock, connect_sock; + struct sockaddr_in sin; + socklen_t len; + u_short port; + int arg; + + listen_sock = socket(PF_INET, SOCK_STREAM, 0); + if (listen_sock == -1) + errx(-1, + "socket(PF_INET, SOCK_STREAM, 0) for listen socket: %s", + strerror(errno)); + + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = 0; + + if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) + errx(-1, "bind(%s, %d) for listen socket: %s", + inet_ntoa(sin.sin_addr), 0, strerror(errno)); + + len = sizeof(sin); + if (getsockname(listen_sock, (struct sockaddr *)&sin, &len) < 0) + errx(-1, "getsockname() for listen socket: %s", + strerror(errno)); + port = sin.sin_port; + + if (listen(listen_sock, -1) < 0) + errx(-1, "listen() for listen socket: %s", strerror(errno)); + + connect_sock = socket(PF_INET, SOCK_STREAM, 0); + if (connect_sock == -1) + errx(-1, "socket(PF_INET, SOCK_STREAM, 0) for connect " + "socket: %s", strerror(errno)); + + arg = O_NONBLOCK; + if (fcntl(connect_sock, F_SETFL, &arg) < 0) + errx(-1, "socket(PF_INET, SOCK_STREAM, 0) for connect socket" + ": %s", strerror(errno)); + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = port; + + if (connect(connect_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) + errx(-1, "connect() for connect socket: %s", strerror(errno)); + + close(listen_sock); + + return (0); +} |