- patch from Denis Vlasenko to add and use bb_xsocket() and to use
[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     Copyright (C) 1998  Charles P. Wright
4
5     0.0.1   6K      It works.
6     0.0.2   5K      Smaller and you can also check the exit condition if you wish.
7     0.0.3           Uses select()
8
9     19980918 Busy Boxed! Dave Cinege
10     19990512 Uses Select. Charles P. Wright
11     19990513 Fixes stdin stupidity and uses buffers.  Charles P. Wright
12
13     Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
14 */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <signal.h>
21
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27 #include <sys/time.h>
28 #include <sys/ioctl.h>
29 #include "busybox.h"
30
31 static void timeout(int signum)
32 {
33         bb_error_msg_and_die("Timed out");
34 }
35
36 int nc_main(int argc, char **argv)
37 {
38         int do_listen = 0, lport = 0, delay = 0, wsecs = 0, tmpfd, opt, sfd, x;
39
40 #define buf bb_common_bufsiz1
41
42 #ifdef CONFIG_NC_GAPING_SECURITY_HOLE
43         char *pr00gie = NULL;
44 #endif
45
46         struct sockaddr_in address;
47         struct hostent *hostinfo;
48
49         fd_set readfds, testfds;
50
51         while ((opt = getopt(argc, argv, "lp:i:e:w:")) > 0) {
52                 switch (opt) {
53                         case 'l':
54                                 do_listen++;
55                                 break;
56                         case 'p':
57                                 lport = bb_lookup_port(optarg, "tcp", 0);
58                                 break;
59                         case 'i':
60                                 delay = atoi(optarg);
61                                 break;
62 #ifdef CONFIG_NC_GAPING_SECURITY_HOLE
63                         case 'e':
64                                 pr00gie = optarg;
65                                 break;
66 #endif
67                         case 'w':
68                                 wsecs = atoi(optarg);
69                                 break;
70                         default:
71                                 bb_show_usage();
72                 }
73         }
74
75         if ((do_listen && optind != argc) || (!do_listen && optind + 2 != argc))
76                 bb_show_usage();
77
78         sfd = bb_xsocket(AF_INET, SOCK_STREAM, 0);
79         x = 1;
80         if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x)) == -1)
81                 bb_perror_msg_and_die("reuseaddr");
82         address.sin_family = AF_INET;
83
84         if (wsecs) {
85                 signal(SIGALRM, timeout);
86                 alarm(wsecs);
87         }
88
89         if (lport != 0) {
90                 memset(&address.sin_addr, 0, sizeof(address.sin_addr));
91                 address.sin_port = lport;
92
93                 if (bind(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
94                         bb_perror_msg_and_die("bind");
95         }
96
97         if (do_listen) {
98                 socklen_t addrlen = sizeof(address);
99
100                 if (listen(sfd, 1) < 0)
101                         bb_perror_msg_and_die("listen");
102
103                 if ((tmpfd = accept(sfd, (struct sockaddr *) &address, &addrlen)) < 0)
104                         bb_perror_msg_and_die("accept");
105
106                 close(sfd);
107                 sfd = tmpfd;
108         } else {
109                 hostinfo = xgethostbyname(argv[optind]);
110
111                 address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
112                 address.sin_port = bb_lookup_port(argv[optind+1], "tcp", 0);
113
114                 if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0)
115                         bb_perror_msg_and_die("connect");
116         }
117
118         if (wsecs) {
119                 alarm(0);
120                 signal(SIGALRM, SIG_DFL);
121         }
122
123 #ifdef CONFIG_NC_GAPING_SECURITY_HOLE
124         /* -e given? */
125         if (pr00gie) {
126                 dup2(sfd, 0);
127                 close(sfd);
128                 dup2(0, 1);
129                 dup2(0, 2);
130                 execl(pr00gie, pr00gie, NULL);
131                 /* Don't print stuff or it will go over the wire.... */
132                 _exit(-1);
133         }
134 #endif /* CONFIG_NC_GAPING_SECURITY_HOLE */
135
136         FD_ZERO(&readfds);
137         FD_SET(sfd, &readfds);
138         FD_SET(STDIN_FILENO, &readfds);
139
140         while (1) {
141                 int fd;
142                 int ofd;
143                 int nread;
144
145                 testfds = readfds;
146
147                 if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0)
148                         bb_perror_msg_and_die("select");
149
150                 for (fd = 0; fd < FD_SETSIZE; fd++) {
151                         if (FD_ISSET(fd, &testfds)) {
152                                 if ((nread = safe_read(fd, buf, sizeof(buf))) < 0)
153                                         bb_perror_msg_and_die(bb_msg_read_error);
154
155                                 if (fd == sfd) {
156                                         if (nread == 0)
157                                                 exit(0);
158                                         ofd = STDOUT_FILENO;
159                                 } else {
160                                         if (nread <= 0) {
161                                                 shutdown(sfd, 1 /* send */ );
162                                                 close(STDIN_FILENO);
163                                                 FD_CLR(STDIN_FILENO, &readfds);
164                                         }
165                                         ofd = sfd;
166                                 }
167
168                                 if (bb_full_write(ofd, buf, nread) < 0)
169                                         bb_perror_msg_and_die(bb_msg_write_error);
170                                 if (delay > 0) {
171                                         sleep(delay);
172                                 }
173                         }
174                 }
175         }
176 }