From 1cca9484db69971f652dfef48778da0dc56dad12 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Mon, 10 Jul 2006 19:45:20 +0000 Subject: [PATCH] Upgrade netcat a lot. Make -e able to take the rest of the command line as what to exec. Add -f mode and a brief explanation of how to use it to replace minicom. Add -l -l mode so you can turn any command into a server. And group all of netcat's command line options under two CONFIG entries, so if you disable both it doesn't use getopt at all. --- include/libbb.h | 1 + include/usage.h | 42 ++++++--- networking/Config.in | 16 +++- networking/nc.c | 204 ++++++++++++++++++++++--------------------- 4 files changed, 146 insertions(+), 117 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 6d6213901..af45511d4 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/include/usage.h b/include/usage.h index 3c1fb18e9..7c63e2f85 100644 --- a/include/usage.h +++ b/include/usage.h @@ -2105,23 +2105,41 @@ USE_FEATURE_MDEV_CONFIG( \ " or\n" \ "$ nameif -c /etc/my_mactab_file\n" \ -#ifdef CONFIG_NC_GAPING_SECURITY_HOLE -# define USAGE_NC_EXEC(a) a +#if ENABLE_NC_SERVER || ENABLE_NC_EXTRA +#define NC_BR1 "[" +#define NC_BR2 "]" #else -# define USAGE_NC_EXEC(a) +#define NC_BR1 +#define NC_BR2 #endif + #define nc_trivial_usage \ - "[OPTIONS] [IP] [port]" + "[" \ + NC_BR1 USE_NC_SERVER("-lp")USE_NC_EXTRA("iwf") NC_BR2 \ + " ["USE_NC_EXTRA("FILENAME|")"{IPADDR PORTNUM}]"USE_NC_EXTRA(" [-e COMMAND]") #define nc_full_usage \ - "Netcat opens a pipe to IP:port\n\n" \ - "Options:\n" \ - "\t-l\t\tlisten mode, for inbound connects\n" \ - "\t-p PORT\t\tlocal port number\n" \ - "\t-i SECS\t\tdelay interval for lines sent\n" \ - USAGE_NC_EXEC( \ - "\t-e PROG\t\tprogram to exec after connect (dangerous!)\n" \ + "Netcat opens a pipe, either to IP:port\n\n" \ + "Options:" \ + USE_NC_EXTRA( \ + "\n\t-e\t\texec rest of command line after connect\n" \ + "\t-i SECS\t\tdelay interval for lines sent\n" \ + "\t-w SECS\t\ttimeout for connect\n" \ + "\t-f filename\tuse file (ala /dev/ttyS0) instead of network" \ ) \ - "\t-w SECS\t\ttimeout for connects and final net reads" + USE_NC_SERVER( \ + "\n\t-l\t\tlisten mode, for inbound connects\n" \ + USE_NC_EXTRA("\t\t\t(use -l twice with -e for persistent server)\n") \ + "\t-p PORT\t\tlocal port number" \ + ) + + +#define nc_notes_usage "" \ + USE_NC_EXTRA( \ + "To use netcat as a terminal emulator on a serial port:\n\n" \ + "$ stty 115200 -F /dev/ttyS0\n" \ + "$ stty raw -echo -ctlecho && nc -f /dev/ttyS0\n" \ + ) "" + #define nc_example_usage \ "$ nc foobar.somedomain.com 25\n" \ "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n" \ diff --git a/networking/Config.in b/networking/Config.in index e95ae0d74..e5eb11caa 100644 --- a/networking/Config.in +++ b/networking/Config.in @@ -446,13 +446,21 @@ config CONFIG_NC A simple Unix utility which reads and writes data across network connections. -config CONFIG_NC_GAPING_SECURITY_HOLE - bool "gaping security hole" +config CONFIG_NC_SERVER + bool "Netcat server options (-lp)" default n depends on CONFIG_NC help - Add support for executing a program after making or receiving a - successful connection (-e option). + Allow netcat to act as a server. + +config CONFIG_NC_EXTRA + bool "Netcat extensions (-eiw and filename)" + default n + depends on CONFIG_NC + help + Add -e (support for executing the rest of the command line after + making or receiving a successful connection), -i (delay interval for + lines sent), -w (timeout for initial connection). config CONFIG_NETSTAT bool "netstat" diff --git a/networking/nc.c b/networking/nc.c index 24f1aa5a5..bda0c407b 100644 --- a/networking/nc.c +++ b/networking/nc.c @@ -1,32 +1,16 @@ /* vi: set sw=4 ts=4: */ /* nc: mini-netcat - built from the ground up for LRP - Copyright (C) 1998 Charles P. Wright + * + * Copyright (C) 1998, 1999 Charles P. Wright + * Copyright (C) 1998 Dave Cinege + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ - 0.0.1 6K It works. - 0.0.2 5K Smaller and you can also check the exit condition if you wish. - 0.0.3 Uses select() - - 19980918 Busy Boxed! Dave Cinege - 19990512 Uses Select. Charles P. Wright - 19990513 Fixes stdin stupidity and uses buffers. Charles P. Wright - - Licensed under GPLv2 or later, see file LICENSE in this tarball for details. -*/ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include #include "busybox.h" +#define xread bb_xread + static void timeout(int signum) { bb_error_msg_and_die("Timed out"); @@ -34,79 +18,87 @@ static void timeout(int signum) int nc_main(int argc, char **argv) { - int do_listen = 0, lport = 0, delay = 0, wsecs = 0, tmpfd, opt, sfd, x; - -#ifdef CONFIG_NC_GAPING_SECURITY_HOLE - char *pr00gie = NULL; -#endif - + int do_listen = 0, lport = 0, delay = 0, wsecs = 0, execflag = 0, opt, + sfd = 0, cfd; struct sockaddr_in address; struct hostent *hostinfo; - fd_set readfds, testfds; + char *infile = NULL; - while ((opt = getopt(argc, argv, "lp:i:e:w:")) > 0) { - switch (opt) { - case 'l': - do_listen++; - break; - case 'p': + memset(&address, 0, sizeof(address)); + + if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { + while ((opt = getopt(argc, argv, "lp:" USE_NC_EXTRA("i:ew:f:"))) > 0) { + if (ENABLE_NC_SERVER && opt=='l') do_listen++; + else if (ENABLE_NC_SERVER && opt=='p') lport = bb_lookup_port(optarg, "tcp", 0); + else if (ENABLE_NC_EXTRA && opt=='w') wsecs = atoi(optarg); + else if (ENABLE_NC_EXTRA && opt=='i') delay = atoi(optarg); + else if (ENABLE_NC_EXTRA && opt=='f') infile = optarg; + else if (ENABLE_NC_EXTRA && opt=='e' && optind!=argc) { + execflag++; break; - case 'i': - delay = atoi(optarg); - break; -#ifdef CONFIG_NC_GAPING_SECURITY_HOLE - case 'e': - pr00gie = optarg; - break; -#endif - case 'w': - wsecs = atoi(optarg); - break; - default: - bb_show_usage(); + } else bb_show_usage(); } } - if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc)) - bb_show_usage(); + + // For listen or file we need zero arguments, dialout is 2. + // For exec we need at least one more argument at the end, more ok - sfd = bb_xsocket(AF_INET, SOCK_STREAM, 0); - x = 1; - if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1) - bb_perror_msg_and_die("reuseaddr"); - address.sin_family = AF_INET; + opt = (do_listen || infile) ? 0 : 2 + execflag; + if (execflag ? argc-optind < opt : argc-optind!=opt || + (infile && do_listen)) + bb_show_usage(); if (wsecs) { signal(SIGALRM, timeout); alarm(wsecs); } + + if (infile) cfd = bb_xopen(infile, O_RDWR); + else { + opt = 1; + sfd = bb_xsocket(AF_INET, SOCK_STREAM, 0); + fcntl(sfd, F_SETFD, FD_CLOEXEC); + setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); + address.sin_family = AF_INET; - if (lport != 0) { - memset(&address.sin_addr, 0, sizeof(address.sin_addr)); - address.sin_port = lport; + // Set local port. - bb_xbind(sfd, (struct sockaddr *) &address, sizeof(address)); - } + if (lport != 0) { + address.sin_port = lport; - if (do_listen) { - socklen_t addrlen = sizeof(address); + bb_xbind(sfd, (struct sockaddr *) &address, sizeof(address)); + } - bb_xlisten(sfd, 1); - if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0) - bb_perror_msg_and_die("accept"); + if (do_listen) { + socklen_t addrlen = sizeof(address); - close(sfd); - sfd = tmpfd; - } else { - hostinfo = xgethostbyname(argv[optind]); + bb_xlisten(sfd, do_listen); - address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; - address.sin_port = bb_lookup_port(argv[optind+1], "tcp", 0); + // If we didn't specify a port number, query and print it to stderr. - if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) - bb_perror_msg_and_die("connect"); + if (!lport) { + socklen_t len = sizeof(address); + getsockname(sfd, &address, &len); + fdprintf(2, "%d\n", SWAP_BE16(address.sin_port)); + } +repeatyness: + if ((cfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0) + bb_perror_msg_and_die("accept"); + + if (!execflag) close(sfd); + } else { + hostinfo = xgethostbyname(argv[optind]); + + address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; + address.sin_port = bb_lookup_port(argv[optind+1], "tcp", 0); + + if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) + bb_perror_msg_and_die("connect"); + cfd = sfd; + } } if (wsecs) { @@ -114,24 +106,40 @@ int nc_main(int argc, char **argv) signal(SIGALRM, SIG_DFL); } -#ifdef CONFIG_NC_GAPING_SECURITY_HOLE /* -e given? */ - if (pr00gie) { - dup2(sfd, 0); - close(sfd); + if (execflag) { + if(cfd) { + signal(SIGCHLD, SIG_IGN); + dup2(cfd, 0); + close(cfd); + } dup2(0, 1); dup2(0, 2); - execl(pr00gie, pr00gie, NULL); - /* Don't print stuff or it will go over the wire.... */ - _exit(-1); - } -#endif /* CONFIG_NC_GAPING_SECURITY_HOLE */ + // With more than one -l, repeatedly act as server. + + if (do_listen>1 && vfork()) { + // This is a bit weird as cleanup goes, since we wind up with no + // stdin/stdout/stderr. But it's small and shouldn't hurt anything. + // We check for cfd == 0 above. + close(0); + close(1); + close(2); + + goto repeatyness; + } + execvp(argv[optind], argv+optind); + /* Don't print stuff or it will go over the wire.... */ + _exit(127); + } + + // Select loop copying stdin to cfd, and cfd to stdout. + FD_ZERO(&readfds); - FD_SET(sfd, &readfds); + FD_SET(cfd, &readfds); FD_SET(STDIN_FILENO, &readfds); - while (1) { + for (;;) { int fd; int ofd; int nread; @@ -143,30 +151,24 @@ int nc_main(int argc, char **argv) for (fd = 0; fd < FD_SETSIZE; fd++) { if (FD_ISSET(fd, &testfds)) { - if ((nread = safe_read(fd, bb_common_bufsiz1, - sizeof(bb_common_bufsiz1))) < 0) - { - bb_perror_msg_and_die(bb_msg_read_error); - } + nread = xread(fd, bb_common_bufsiz1, sizeof(bb_common_bufsiz1)); - if (fd == sfd) { - if (nread == 0) - exit(0); + if (fd == cfd) { + if (!nread) exit(0); ofd = STDOUT_FILENO; } else { - if (nread <= 0) { - shutdown(sfd, 1 /* send */ ); - close(STDIN_FILENO); + if (!nread) { + // Close outgoing half-connection so they get EOF, but + // leave incoming alone so we can see response. + shutdown(cfd, 1); FD_CLR(STDIN_FILENO, &readfds); } - ofd = sfd; + ofd = cfd; } if (bb_full_write(ofd, bb_common_bufsiz1, nread) < 0) bb_perror_msg_and_die(bb_msg_write_error); - if (delay > 0) { - sleep(delay); - } + if (delay > 0) sleep(delay); } } }