From 04df5a7a23325a7e9b42ee3d4b306fa222326d99 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 12 Nov 2013 15:30:47 -0500 Subject: [PATCH] Listening socket support via /dev/tcp/HOST/:PORT In addition to the ability to create normal outgoing socket connections (/dev/tcp/HOST/PORT) this adds the ability to create a listen socket connection. The creation of the listener will block until a new connection is created via the listener. Signed-off-by: Joel Martin --- lib/sh/netopen.c | 110 +++++++++++++++++++++++++++++++---------- tests/misc/dev-tcp-serv.tests | 44 +++++++++++++++++ 2 files changed, 127 insertions(+), 27 deletions(-) create mode 100644 tests/misc/dev-tcp-serv.tests diff --git a/lib/sh/netopen.c b/lib/sh/netopen.c index 736d413..0a2a9f1 100644 --- a/lib/sh/netopen.c +++ b/lib/sh/netopen.c @@ -69,9 +69,9 @@ extern int inet_aton __P((const char *, struct in_addr *)); #ifndef HAVE_GETADDRINFO static int _getaddr __P((char *, struct in_addr *)); static int _getserv __P((char *, int, unsigned short *)); -static int _netopen4 __P((char *, char *, int)); +static int _netopen4 __P((char *, char *, int, int)); #else /* HAVE_GETADDRINFO */ -static int _netopen6 __P((char *, char *, int)); +static int _netopen6 __P((char *, char *, int, int)); #endif static int _netopen __P((char *, char *, int)); @@ -154,14 +154,16 @@ _getserv (serv, proto, pp) * traditional BSD mechanisms. Returns the connected socket or -1 on error. */ static int -_netopen4(host, serv, typ) +_netopen4(host, serv, typ, listener) char *host, *serv; - int typ; + int typ, listener; { struct in_addr ina; struct sockaddr_in sin; unsigned short p; - int s, e; + int optval = 1; + int s, c, e; + const char *step; if (_getaddr(host, &ina) == 0) { @@ -189,16 +191,39 @@ _netopen4(host, serv, typ) return (-1); } - if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0) + if (!listener) + { + step = "connect"; + if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0) + goto cleanup; + } + else { - e = errno; - sys_error("connect"); + //internal_error("Listening (4) on: %s:%s %c\n", host, serv, typ); + step = "setsockopt"; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) <0) + goto cleanup; + step = "bind"; + if (bind(s, (struct sockaddr*)&sin, sizeof(sin)) < 0) + goto cleanup; + step = "listen"; + if (listen(s, 1) < 0) + goto cleanup; + step = "accept"; + if ((c = accept(s, (struct sockaddr*)NULL, NULL)) < 0) + goto cleanup; close(s); - errno = e; - return (-1); + s = c; } return(s); + + cleanup: + e = errno; + sys_error(step); + close(s); + errno = e; + return (-1); } #endif /* ! HAVE_GETADDRINFO */ @@ -209,13 +234,15 @@ _netopen4(host, serv, typ) * on error. */ static int -_netopen6 (host, serv, typ) +_netopen6 (host, serv, typ, listener) char *host, *serv; - int typ; + int typ, listener; { - int s, e; + int s, c, e; struct addrinfo hints, *res, *res0; + int optval = 1; int gerr; + const char *step; memset ((char *)&hints, 0, sizeof (hints)); /* XXX -- if problems with IPv6, set to PF_INET for IPv4 only */ @@ -247,22 +274,45 @@ _netopen6 (host, serv, typ) freeaddrinfo (res0); return -1; } - if (connect (s, res->ai_addr, res->ai_addrlen) < 0) + if (!listener) { - if (res->ai_next) - { - close (s); - continue; - } - e = errno; - sys_error ("connect"); - close (s); - freeaddrinfo (res0); - errno = e; - return -1; + step = "connect"; + if (connect (s, res->ai_addr, res->ai_addrlen) < 0) + goto cleanup; + } + else + { + //internal_error("Listening 6 on: %s:%s %c\n", host, serv, typ); + step = "setsockopt"; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) + goto cleanup; + step = "bind"; + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) + goto cleanup; + step = "listen"; + if (listen(s, 1) < 0) + goto cleanup; + step = "accept"; + if ((c = accept(s, (struct sockaddr*)NULL, NULL)) < 0) + goto cleanup; + close(s); + s = c; } freeaddrinfo (res0); break; + + cleanup: + if (res->ai_next) + { + close (s); + continue; + } + e = errno; + sys_error (step); + close (s); + freeaddrinfo (res0); + errno = e; + return -1; } return s; } @@ -278,10 +328,16 @@ _netopen(host, serv, typ) char *host, *serv; int typ; { + int listener = 0; + if (serv[0] == ':') { + listener = 1; + serv++; + } + #ifdef HAVE_GETADDRINFO - return (_netopen6 (host, serv, typ)); + return (_netopen6 (host, serv, typ, listener)); #else - return (_netopen4 (host, serv, typ)); + return (_netopen4 (host, serv, typ, listener)); #endif } diff --git a/tests/misc/dev-tcp-serv.tests b/tests/misc/dev-tcp-serv.tests new file mode 100644 index 0000000..53a89a7 --- /dev/null +++ b/tests/misc/dev-tcp-serv.tests @@ -0,0 +1,44 @@ +#!/bin/bash + +# Start a background server +( +CNT=0 +while true; do + exec 9<> /dev/tcp/127.0.0.1/:13567 || break + echo "CNT: $CNT" >&9 + CNT=$(( CNT + 1 )) + exec 9>&- # close it +done +) & + +spid=$! + +# cleanup the background server on exit +cleanup() { + kill $spid +} +trap "cleanup" TERM QUIT INT EXIT + +# Wait for the server to startup +sleep 1 + +# Connect to the server and get the first response +exec 10<> /dev/tcp/127.0.0.1/13567 +read -u 10 -r LINE +exec 10>&- +echo "Got server response: $LINE" +if [ "${LINE}" != "CNT: 0" ]; then + echo "Expected: CNT: 0" + exit 1 +fi + +# Connect to the server and get the second response +exec 10<> /dev/tcp/127.0.0.1/13568 +read -u 10 -r LINE +echo "Got server response: $LINE" +if [ "${LINE}" != "CNT: 1" ]; then + echo "Expected: CNT: 1" + exit 1 +fi + +exit 0 -- 1.7.9.5