changeset 321:dfbfbaeb69c9

Add netcat server mode, -l, -L, and -t.
author Rob Landley <rob@landley.net>
date Sat, 15 Nov 2008 05:17:23 -0600
parents 7259b853cb8b
children fa0a95ae4303
files scripts/make.sh toys.h toys/netcat.c
diffstat 3 files changed, 143 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/make.sh	Wed Nov 12 18:01:35 2008 -0600
+++ b/scripts/make.sh	Sat Nov 15 05:17:23 2008 -0600
@@ -85,6 +85,6 @@
 
 echo "Compile toybox..."
 
-$DEBUG $CC $CFLAGS -I . -o toybox_unstripped $OPTIMIZE \
-  main.c lib/*.c $TOYFILES || exit 1
+$DEBUG $CC $CFLAGS -I . -o toybox_unstripped $OPTIMIZE main.c lib/*.c \
+  $TOYFILES -Wl,--as-needed,-lutil,--no-as-needed || exit 1
 $DEBUG $STRIP toybox_unstripped -o toybox || exit 1
--- a/toys.h	Wed Nov 12 18:01:35 2008 -0600
+++ b/toys.h	Sat Nov 15 05:17:23 2008 -0600
@@ -17,6 +17,7 @@
 #include <grp.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <pty.h>
 #include <pwd.h>
 #include <setjmp.h>
 #include <stdarg.h>
--- a/toys/netcat.c	Wed Nov 12 18:01:35 2008 -0600
+++ b/toys/netcat.c	Sat Nov 15 05:17:23 2008 -0600
@@ -6,28 +6,40 @@
  *
  * Not in SUSv3.
 
-USE_NETCAT(OLDTOY(nc, netcat, "i#w#l@p#s:q#f:e", TOYFLAG_BIN))
-USE_NETCAT(NEWTOY(netcat, "i#w#l@p#s:q#f:e", TOYFLAG_BIN))
+USE_NETCAT(OLDTOY(nc, netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
+USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN))
 
 config NETCAT
 	bool "netcat"
-	default n
+	default y
 	help
-	  usage: netcat [-iwlp] {IPADDR PORTNUM|-f FILENAME} [-e COMMAND]
+	  usage: netcat [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|-let} [-e COMMAND]
 
-	  -e	exec the rest of the command line
-	  -i	SECONDS delay after each line sent
 	  -w	SECONDS timeout for connection
-	  -f	filename use file (ala /dev/ttyS0) instead of network
-	  -l	listen for incoming connection (twice for persistent connection)
 	  -p	local port number
-	  -s	local source address
+	  -s	local ipv4 address
 	  -q	SECONDS quit this many seconds after EOF on stdin.
-
-	  Use -l twice with -e for a quick-and-dirty server.
+	  -f	use FILENAME (ala /dev/ttyS0) instead of network
 
 	  Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with
 	  netcat -f to connect to a serial port.
+
+
+config NETCAT_LISTEN
+	bool "netcat sever options (-let)"
+	default y
+	depends on NETCAT
+	help
+	  -t    allocate tty (must come before -l or -L)
+	  -l	listen for one incoming connection.
+	  -L	listen for multiple incoming connections (server mode).
+
+	  Any additional command line arguments after -l or -L are executed
+	  to handle each incoming connection.  If none, the connection is
+	  forwarded to stdin/stdout.
+
+	  For a quick-and-dirty server, try something like:
+		netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l
 */
 
 #include "toys.h"
@@ -38,54 +50,65 @@
 	long quit_delay;       // -q Exit after EOF from stdin after # seconds.
 	char *source_address;  // -s Bind to a specific source address.
 	long port;             // -p Bind to a specific source port.
-	long listen;           // -l Listen for connection instead of dialing out.
 	long wait;             // -w Wait # seconds for a connection.
-	long delay;            // -i delay between lines sent
 )
 
 #define TT this.netcat
 
+#define FLAG_f   1
+#define FLAG_L  32
+#define FLAG_l  64
+#define FLAG_t 128
+
 static void timeout(int signum)
 {
-	error_exit("Timeout");
+	if (TT.wait) error_exit("Timeout");
+	exit(0);
+}
+
+static void set_alarm(int seconds)
+{
+	signal(SIGALRM, seconds ? timeout : SIG_DFL);
+	alarm(seconds);
 }
 
 // Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name.
-void lookup_name(char *name, uint32_t *result)
+static void lookup_name(char *name, uint32_t *result)
 {
 	struct hostent *hostbyname;
 
-	hostbyname = gethostbyname(*toys.optargs);
-	if (!hostbyname) error_exit("name lookup failed");
+	hostbyname = gethostbyname(name);
+	if (!hostbyname) error_exit("no host '%s'", name);
 	*result = *(uint32_t *)*hostbyname->h_addr_list;
 }
 
 // Worry about a fancy lookup later.
-void lookup_port(char *str, uint16_t *port)
+static void lookup_port(char *str, uint16_t *port)
 {
   *port = SWAP_BE16(atoi(str));
 }
 
 void netcat_main(void)
 {
-	int sockfd, pollcount;
+	int sockfd=-1, pollcount=2;
 	struct pollfd pollfds[2];
 
-	if (TT.wait) {
-		signal(SIGALRM, timeout);
-		alarm(TT.wait);
-	}
+	pollfds[0].events = pollfds[1].events = POLLIN;
+	set_alarm(TT.wait);
+
+	// The argument parsing logic can't make "<2" conditional on other
+	// arguments like -f and -l, so we do it by hand here.
+	if ((toys.optflags&FLAG_f) && toys.optc!=1) toys.exithelp++;
+	if (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2) toys.exithelp++;
+
+	if (toys.exithelp) error_exit("Argument count wrong");
 
 	if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR);
 	else {
 		int temp;
 		struct sockaddr_in address;
 
-		// The argument parsing logic can't make "<2" conditional on "-f", so...
-		if (!*toys.optargs || !toys.optargs[1]) {
-			toys.exithelp++;
-			error_exit("Need address and port");
-		}
+		pollfds[1].fd = 0;
 
 		// Setup socket
 		sockfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -95,30 +118,96 @@
 		setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp));
 		memset(&address, 0, sizeof(address));
 		address.sin_family = AF_INET;
-		if (TT.port) {
-			address.sin_port = TT.port;
-			if (-1 == bind(sockfd, &address, sizeof(address)))
+		if (TT.source_address || TT.port) {
+			address.sin_port = SWAP_BE16(TT.port);
+			if (TT.source_address) 
+				lookup_name(TT.source_address, (uint32_t *)&address.sin_addr);
+			if (bind(sockfd, &address, sizeof(address)))
 				perror_exit("bind");
 		}
 
-		// Figure out where to dial out to.
-		lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr);
-		lookup_port(toys.optargs[1], &address.sin_port);
-		temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
-		if (temp<0) perror_exit("connect");
-		pollfds[0].fd = sockfd;
+		// Dial out
+
+		if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) {
+			// Figure out where to dial out to.
+			lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr);
+			lookup_port(toys.optargs[1], &address.sin_port);
+			temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
+			if (temp<0) perror_exit("connect");
+			pollfds[0].fd = sockfd;
+
+		// Listen for incoming connections
+
+		} else {
+			socklen_t len = sizeof(address);
+
+			if (listen(sockfd, 5)) error_exit("listen");
+			if (!TT.port) {
+				getsockname(sockfd, &address, &len);
+				printf("%d\n", SWAP_BE16(address.sin_port));
+				fflush(stdout);
+			}
+			// Do we need to defer calling accept() to the new thread
+			// because -l has arguments and we want it to return immediately?
+			temp = 0;
+			if ((toys.optflags&FLAG_l) && toys.optc) temp++;
+
+			for (;;) {
+				pid_t child = 0;
+
+				// For -l, call accept from the _new_ thread.
+
+				if (temp != 1) {
+					pollfds[0].fd = accept(sockfd, (struct sockaddr *)&address,
+						&len);
+					if (pollfds[0].fd<0) perror_exit("accept");
+
+					if (temp==2) {
+						close(sockfd);
+						break;
+					}
+				} else temp++;
+
+				// Do we need a tty?
+
+				if (toys.optflags&FLAG_t)
+					child = forkpty(&(pollfds[1].fd), NULL, NULL, NULL);
+
+				// Do we need to fork and/or redirect for exec?
+
+				else {
+					if (temp || (toys.optflags&FLAG_L)) child = fork();
+					if (!child && toys.optc) {
+						int fd = pollfds[0].fd;
+
+						if (!temp) close(sockfd);
+						dup2(fd, 0);
+						dup2(fd, 1);
+						dup2(fd, 2);
+						if (fd>2) close(fd);
+					}
+				}
+
+				if (child<0) error_msg("Fork failed\n");
+				if (child<1) {
+					if (!temp) break;
+					continue;
+				} 
+				if (temp) exit(0);
+				close(pollfds[0].fd);
+			}
+		}
 	}
 
 	// We have a connection.  Disarm timeout.
-	if (TT.wait) {
-		alarm(0);
-		signal(SIGALRM, SIG_DFL);
+	// (Does not play well with -L, but what _should_ that do?)
+	set_alarm(0);
+
+	if (CFG_NETCAT_LISTEN && (toys.optflags&(FLAG_L|FLAG_l) && toys.optc)) {
+		execvp(*toys.optargs, toys.optargs);
+		error_exit("Exec failed");
 	}
 
-	pollcount = 2;
-	pollfds[1].fd = 0;
-	pollfds[0].events = pollfds[1].events = POLLIN;
-
 	// Poll loop copying stdin->socket and socket->stdout.
 	for (;;) {
 		int i;
@@ -133,16 +222,19 @@
 			}
 			if (pollfds[i].revents & POLLHUP) {
 dohupnow:
-				// Close half-connect.  This is needed for things like
-				// "echo GET / | netcat landley.net 80" to work.
+				// Close half-connection.  This is needed for things like
+				// "echo GET / | netcat landley.net 80"
 				if (i) {
 					shutdown(pollfds[0].fd, SHUT_WR);
 					pollcount--;
+					set_alarm(TT.quit_delay);
 				} else goto cleanup;
 			}
 		}
 	}
 cleanup:
-	close(pollfds[0].fd);
-//	close(sockfd);
+	if (CFG_TOYBOX_FREE) {
+		close(pollfds[0].fd);
+		close(sockfd);
+	}
 }