9c3f116d2ec1c89e18a0967c96c4060b17e98122
[oweals/busybox.git] / networking / nc.c
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
20 static void timeout(int signum UNUSED_PARAM)
21 {
22         bb_error_msg_and_die("timed out");
23 }
24
25 int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
26 int 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         IF_NOT_NC_SERVER(const) unsigned do_listen = 0;
33         IF_NOT_NC_EXTRA (const) unsigned wsecs = 0;
34         IF_NOT_NC_EXTRA (const) unsigned delay = 0;
35         IF_NOT_NC_EXTRA (const int execparam = 0;)
36         IF_NC_EXTRA     (char **execparam = NULL;)
37         fd_set readfds, testfds;
38         int opt; /* must be signed (getopt returns -1) */
39
40         if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
41                 /* getopt32 is _almost_ usable:
42                 ** it cannot handle "... -e prog -prog-opt" */
43                 while ((opt = getopt(argc, argv,
44                         "" IF_NC_SERVER("lp:") IF_NC_EXTRA("w:i:f:e:") )) > 0
45                 ) {
46                         if (ENABLE_NC_SERVER && opt == 'l')
47                                 IF_NC_SERVER(do_listen++);
48                         else if (ENABLE_NC_SERVER && opt == 'p')
49                                 IF_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0));
50                         else if (ENABLE_NC_EXTRA && opt == 'w')
51                                 IF_NC_EXTRA( wsecs = xatou(optarg));
52                         else if (ENABLE_NC_EXTRA && opt == 'i')
53                                 IF_NC_EXTRA( delay = xatou(optarg));
54                         else if (ENABLE_NC_EXTRA && opt == 'f')
55                                 IF_NC_EXTRA( cfd = xopen(optarg, O_RDWR));
56                         else if (ENABLE_NC_EXTRA && opt == 'e' && optind <= argc) {
57                                 /* We cannot just 'break'. We should let getopt finish.
58                                 ** Or else we won't be able to find where
59                                 ** 'host' and 'port' params are
60                                 ** (think "nc -w 60 host port -e prog"). */
61                                 IF_NC_EXTRA(
62                                         char **p;
63                                         // +2: one for progname (optarg) and one for NULL
64                                         execparam = xzalloc(sizeof(char*) * (argc - optind + 2));
65                                         p = execparam;
66                                         *p++ = optarg;
67                                         while (optind < argc) {
68                                                 *p++ = argv[optind++];
69                                         }
70                                 )
71                                 /* optind points to argv[arvc] (NULL) now.
72                                 ** FIXME: we assume that getopt will not count options
73                                 ** possibly present on "-e prog args" and will not
74                                 ** include them into final value of optind
75                                 ** which is to be used ...  */
76                         } else bb_show_usage();
77                 }
78                 argv += optind; /* ... here! */
79                 argc -= optind;
80                 // -l and -f don't mix
81                 if (do_listen && cfd) bb_show_usage();
82                 // File mode needs need zero arguments, listen mode needs zero or one,
83                 // client mode needs one or two
84                 if (cfd) {
85                         if (argc) bb_show_usage();
86                 } else if (do_listen) {
87                         if (argc > 1) bb_show_usage();
88                 } else {
89                         if (!argc || argc > 2) bb_show_usage();
90                 }
91         } else {
92                 if (argc != 3) bb_show_usage();
93                 argc--;
94                 argv++;
95         }
96
97         if (wsecs) {
98                 signal(SIGALRM, timeout);
99                 alarm(wsecs);
100         }
101
102         if (!cfd) {
103                 if (do_listen) {
104                         sfd = create_and_bind_stream_or_die(argv[0], lport);
105                         xlisten(sfd, do_listen); /* can be > 1 */
106 #if 0  /* nc-1.10 does not do this (without -v) */
107                         /* If we didn't specify a port number,
108                          * query and print it after listen() */
109                         if (!lport) {
110                                 len_and_sockaddr lsa;
111                                 lsa.len = LSA_SIZEOF_SA;
112                                 getsockname(sfd, &lsa.u.sa, &lsa.len);
113                                 lport = get_nport(&lsa.u.sa);
114                                 fdprintf(2, "%d\n", ntohs(lport));
115                         }
116 #endif
117                         close_on_exec_on(sfd);
118  accept_again:
119                         cfd = accept(sfd, NULL, 0);
120                         if (cfd < 0)
121                                 bb_perror_msg_and_die("accept");
122                         if (!execparam)
123                                 close(sfd);
124                 } else {
125                         cfd = create_and_connect_stream_or_die(argv[0],
126                                 argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0);
127                 }
128         }
129
130         if (wsecs) {
131                 alarm(0);
132                 /* Non-ignored siganls revert to SIG_DFL on exec anyway */
133                 /*signal(SIGALRM, SIG_DFL);*/
134         }
135
136         /* -e given? */
137         if (execparam) {
138                 signal(SIGCHLD, SIG_IGN);
139                 // With more than one -l, repeatedly act as server.
140                 if (do_listen > 1 && vfork()) {
141                         /* parent */
142                         // This is a bit weird as cleanup goes, since we wind up with no
143                         // stdin/stdout/stderr.  But it's small and shouldn't hurt anything.
144                         // We check for cfd == 0 above.
145                         logmode = LOGMODE_NONE;
146                         close(0);
147                         close(1);
148                         close(2);
149                         goto accept_again;
150                 }
151                 /* child (or main thread if no multiple -l) */
152                 xmove_fd(cfd, 0);
153                 xdup2(0, 1);
154                 xdup2(0, 2);
155                 IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
156                 /* Don't print stuff or it will go over the wire.... */
157                 _exit(127);
158         }
159
160         // Select loop copying stdin to cfd, and cfd to stdout.
161
162         FD_ZERO(&readfds);
163         FD_SET(cfd, &readfds);
164         FD_SET(STDIN_FILENO, &readfds);
165
166         for (;;) {
167                 int fd;
168                 int ofd;
169                 int nread;
170
171                 testfds = readfds;
172
173                 if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0)
174                         bb_perror_msg_and_die("select");
175
176 #define iobuf bb_common_bufsiz1
177                 for (fd = 0; fd < FD_SETSIZE; fd++) {
178                         if (FD_ISSET(fd, &testfds)) {
179                                 nread = safe_read(fd, iobuf, sizeof(iobuf));
180                                 if (fd == cfd) {
181                                         if (nread < 1)
182                                                 exit(EXIT_SUCCESS);
183                                         ofd = STDOUT_FILENO;
184                                 } else {
185                                         if (nread<1) {
186                                                 // Close outgoing half-connection so they get EOF, but
187                                                 // leave incoming alone so we can see response.
188                                                 shutdown(cfd, 1);
189                                                 FD_CLR(STDIN_FILENO, &readfds);
190                                         }
191                                         ofd = cfd;
192                                 }
193                                 xwrite(ofd, iobuf, nread);
194                                 if (delay > 0) sleep(delay);
195                         }
196                 }
197         }
198 }
199 #endif