1/* vi: set sw=4 ts=4: */ 2/* nc: mini-netcat - built from the ground up for LRP 3 * 4 * Copyright (C) 1998, 1999 Charles P. Wright 5 * Copyright (C) 1998 Dave Cinege 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 */ 9 10#include "libbb.h" 11 12#if ENABLE_DESKTOP 13#include "nc_bloaty.c" 14#else 15 16/* Lots of small differences in features 17 * when compared to "standard" nc 18 */ 19 20static void timeout(int signum) 21{ 22 bb_error_msg_and_die("timed out"); 23} 24 25int nc_main(int argc, char **argv); 26int nc_main(int argc, char **argv) 27{ 28 /* sfd sits _here_ only because of "repeat" option (-l -l). */ 29 int sfd = sfd; /* for gcc */ 30 int cfd = 0; 31 unsigned lport = 0; 32 SKIP_NC_SERVER(const) unsigned do_listen = 0; 33 SKIP_NC_EXTRA (const) unsigned wsecs = 0; 34 SKIP_NC_EXTRA (const) unsigned delay = 0; 35 SKIP_NC_EXTRA (const int execparam = 0;) 36 USE_NC_EXTRA (char **execparam = NULL;) 37 len_and_sockaddr *lsa; 38 fd_set readfds, testfds; 39 int opt; /* must be signed (getopt returns -1) */ 40 41 if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { 42 /* getopt32 is _almost_ usable: 43 ** it cannot handle "... -e prog -prog-opt" */ 44 while ((opt = getopt(argc, argv, 45 "" USE_NC_SERVER("lp:") USE_NC_EXTRA("w:i:f:e:") )) > 0 46 ) { 47 if (ENABLE_NC_SERVER && opt=='l') USE_NC_SERVER(do_listen++); 48 else if (ENABLE_NC_SERVER && opt=='p') { 49 USE_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0)); 50 } 51 else if (ENABLE_NC_EXTRA && opt=='w') USE_NC_EXTRA( wsecs = xatou(optarg)); 52 else if (ENABLE_NC_EXTRA && opt=='i') USE_NC_EXTRA( delay = xatou(optarg)); 53 else if (ENABLE_NC_EXTRA && opt=='f') USE_NC_EXTRA( cfd = xopen(optarg, O_RDWR)); 54 else if (ENABLE_NC_EXTRA && opt=='e' && optind<=argc) { 55 /* We cannot just 'break'. We should let getopt finish. 56 ** Or else we won't be able to find where 57 ** 'host' and 'port' params are 58 ** (think "nc -w 60 host port -e prog"). */ 59 USE_NC_EXTRA( 60 char **p; 61 // +2: one for progname (optarg) and one for NULL 62 execparam = xzalloc(sizeof(char*) * (argc - optind + 2)); 63 p = execparam; 64 *p++ = optarg; 65 while (optind < argc) { 66 *p++ = argv[optind++]; 67 } 68 ) 69 } else bb_show_usage(); 70 } 71 argv += optind; /* ... here! */ 72 argc -= optind; 73 // -l and -f don't mix 74 if (do_listen && cfd) bb_show_usage(); 75 // Listen or file modes need zero arguments, client mode needs 2 76 if (do_listen || cfd) { 77 if (argc) bb_show_usage(); 78 } else { 79 if (!argc || argc > 2) bb_show_usage(); 80 } 81 } else { 82 if (argc != 3) bb_show_usage(); 83 argc--; 84 argv++; 85 } 86 87 if (wsecs) { 88 signal(SIGALRM, timeout); 89 alarm(wsecs); 90 } 91 92 if (!cfd) { 93 if (do_listen) { 94 /* create_and_bind_stream_or_die(NULL, lport) 95 * would've work wonderfully, but we need 96 * to know lsa */ 97 sfd = xsocket_stream(&lsa); 98 if (lport) 99 set_nport(lsa, htons(lport)); 100 setsockopt_reuseaddr(sfd); 101 xbind(sfd, &lsa->sa, lsa->len); 102 xlisten(sfd, do_listen); /* can be > 1 */ 103 /* If we didn't specify a port number, 104 * query and print it after listen() */ 105 if (!lport) { 106 socklen_t addrlen = lsa->len; 107 getsockname(sfd, &lsa->sa, &addrlen); 108 lport = get_nport(&lsa->sa); 109 fdprintf(2, "%d\n", ntohs(lport)); 110 } 111 fcntl(sfd, F_SETFD, FD_CLOEXEC); 112 accept_again: 113 cfd = accept(sfd, NULL, 0); 114 if (cfd < 0) 115 bb_perror_msg_and_die("accept"); 116 if (!execparam) 117 close(sfd); 118 } else { 119 cfd = create_and_connect_stream_or_die(argv[0], 120 argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0); 121 } 122 } 123 124 if (wsecs) { 125 alarm(0); 126 signal(SIGALRM, SIG_DFL); 127 } 128 129 /* -e given? */ 130 if (execparam) { 131 signal(SIGCHLD, SIG_IGN); 132 // With more than one -l, repeatedly act as server. 133 if (do_listen > 1 && vfork()) { 134 /* parent */ 135 // This is a bit weird as cleanup goes, since we wind up with no 136 // stdin/stdout/stderr. But it's small and shouldn't hurt anything. 137 // We check for cfd == 0 above. 138 logmode = LOGMODE_NONE; 139 close(0); 140 close(1); 141 close(2); 142 goto accept_again; 143 } 144 /* child (or main thread if no multiple -l) */ 145 if (cfd) { 146 dup2(cfd, 0); 147 close(cfd); 148 } 149 dup2(0, 1); 150 dup2(0, 2); 151 USE_NC_EXTRA(BB_EXECVP(execparam[0], execparam);) 152 /* Don't print stuff or it will go over the wire.... */ 153 _exit(127); 154 } 155 156 // Select loop copying stdin to cfd, and cfd to stdout. 157 158 FD_ZERO(&readfds); 159 FD_SET(cfd, &readfds); 160 FD_SET(STDIN_FILENO, &readfds); 161 162 for (;;) { 163 int fd; 164 int ofd; 165 int nread; 166 167 testfds = readfds; 168 169 if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0) 170 bb_perror_msg_and_die("select"); 171 172#define iobuf bb_common_bufsiz1 173 for (fd = 0; fd < FD_SETSIZE; fd++) { 174 if (FD_ISSET(fd, &testfds)) { 175 nread = safe_read(fd, iobuf, sizeof(iobuf)); 176 if (fd == cfd) { 177 if (nread < 1) 178 exit(0); 179 ofd = STDOUT_FILENO; 180 } else { 181 if (nread<1) { 182 // Close outgoing half-connection so they get EOF, but 183 // leave incoming alone so we can see response. 184 shutdown(cfd, 1); 185 FD_CLR(STDIN_FILENO, &readfds); 186 } 187 ofd = cfd; 188 } 189 xwrite(ofd, iobuf, nread); 190 if (delay > 0) sleep(delay); 191 } 192 } 193 } 194} 195#endif 196