diff options
Diffstat (limited to 'networking/nc.c')
-rw-r--r-- | networking/nc.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/networking/nc.c b/networking/nc.c new file mode 100644 index 000000000..5fd9242cc --- /dev/null +++ b/networking/nc.c | |||
@@ -0,0 +1,201 @@ | |||
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 "busybox.h" | ||
11 | |||
12 | static void timeout(int signum) | ||
13 | { | ||
14 | bb_error_msg_and_die("timed out"); | ||
15 | } | ||
16 | |||
17 | int nc_main(int argc, char **argv) | ||
18 | { | ||
19 | int sfd = 0; | ||
20 | int cfd = 0; | ||
21 | SKIP_NC_SERVER(const) unsigned do_listen = 0; | ||
22 | SKIP_NC_SERVER(const) unsigned lport = 0; | ||
23 | SKIP_NC_EXTRA (const) unsigned wsecs = 0; | ||
24 | SKIP_NC_EXTRA (const) unsigned delay = 0; | ||
25 | SKIP_NC_EXTRA (const int execparam = 0;) | ||
26 | USE_NC_EXTRA (char **execparam = NULL;) | ||
27 | struct sockaddr_in address; | ||
28 | fd_set readfds, testfds; | ||
29 | int opt; /* must be signed (getopt returns -1) */ | ||
30 | |||
31 | memset(&address, 0, sizeof(address)); | ||
32 | |||
33 | if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { | ||
34 | /* getopt32 is _almost_ usable: | ||
35 | ** it cannot handle "... -e prog -prog-opt" */ | ||
36 | while ((opt = getopt(argc, argv, | ||
37 | "" USE_NC_SERVER("lp:") USE_NC_EXTRA("w:i:f:e:") )) > 0 | ||
38 | ) { | ||
39 | if (ENABLE_NC_SERVER && opt=='l') USE_NC_SERVER(do_listen++); | ||
40 | else if (ENABLE_NC_SERVER && opt=='p') USE_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0)); | ||
41 | else if (ENABLE_NC_EXTRA && opt=='w') USE_NC_EXTRA( wsecs = xatou(optarg)); | ||
42 | else if (ENABLE_NC_EXTRA && opt=='i') USE_NC_EXTRA( delay = xatou(optarg)); | ||
43 | else if (ENABLE_NC_EXTRA && opt=='f') USE_NC_EXTRA( cfd = xopen(optarg, O_RDWR)); | ||
44 | else if (ENABLE_NC_EXTRA && opt=='e' && optind<=argc) { | ||
45 | /* We cannot just 'break'. We should let getopt finish. | ||
46 | ** Or else we won't be able to find where | ||
47 | ** 'host' and 'port' params are | ||
48 | ** (think "nc -w 60 host port -e prog"). */ | ||
49 | USE_NC_EXTRA( | ||
50 | char **p; | ||
51 | // +2: one for progname (optarg) and one for NULL | ||
52 | execparam = xzalloc(sizeof(char*) * (argc - optind + 2)); | ||
53 | p = execparam; | ||
54 | *p++ = optarg; | ||
55 | while (optind < argc) { | ||
56 | *p++ = argv[optind++]; | ||
57 | } | ||
58 | ) | ||
59 | /* optind points to argv[arvc] (NULL) now. | ||
60 | ** FIXME: we assume that getopt will not count options | ||
61 | ** possibly present on "-e prog args" and will not | ||
62 | ** include them into final value of optind | ||
63 | ** which is to be used ... */ | ||
64 | } else bb_show_usage(); | ||
65 | } | ||
66 | argv += optind; /* ... here! */ | ||
67 | argc -= optind; | ||
68 | // -l and -f don't mix | ||
69 | if (do_listen && cfd) bb_show_usage(); | ||
70 | // Listen or file modes need zero arguments, client mode needs 2 | ||
71 | opt = ((do_listen || cfd) ? 0 : 2); | ||
72 | if (argc != opt) | ||
73 | bb_show_usage(); | ||
74 | } else { | ||
75 | if (argc != 3) bb_show_usage(); | ||
76 | argc--; | ||
77 | argv++; | ||
78 | } | ||
79 | |||
80 | if (wsecs) { | ||
81 | signal(SIGALRM, timeout); | ||
82 | alarm(wsecs); | ||
83 | } | ||
84 | |||
85 | if (!cfd) { | ||
86 | sfd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
87 | fcntl(sfd, F_SETFD, FD_CLOEXEC); | ||
88 | setsockopt_reuseaddr(sfd); | ||
89 | address.sin_family = AF_INET; | ||
90 | |||
91 | // Set local port. | ||
92 | |||
93 | if (lport != 0) { | ||
94 | address.sin_port = lport; | ||
95 | xbind(sfd, (struct sockaddr *) &address, sizeof(address)); | ||
96 | } | ||
97 | |||
98 | if (do_listen) { | ||
99 | socklen_t addrlen = sizeof(address); | ||
100 | |||
101 | xlisten(sfd, do_listen); | ||
102 | |||
103 | // If we didn't specify a port number, query and print it to stderr. | ||
104 | |||
105 | if (!lport) { | ||
106 | socklen_t len = sizeof(address); | ||
107 | getsockname(sfd, &address, &len); | ||
108 | fdprintf(2, "%d\n", SWAP_BE16(address.sin_port)); | ||
109 | } | ||
110 | repeatyness: | ||
111 | cfd = accept(sfd, (struct sockaddr *) &address, &addrlen); | ||
112 | if (cfd < 0) | ||
113 | bb_perror_msg_and_die("accept"); | ||
114 | |||
115 | if (!execparam) close(sfd); | ||
116 | } else { | ||
117 | struct hostent *hostinfo; | ||
118 | hostinfo = xgethostbyname(argv[0]); | ||
119 | |||
120 | address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list; | ||
121 | address.sin_port = bb_lookup_port(argv[1], "tcp", 0); | ||
122 | |||
123 | if (connect(sfd, (struct sockaddr *) &address, sizeof(address)) < 0) | ||
124 | bb_perror_msg_and_die("connect"); | ||
125 | cfd = sfd; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | if (wsecs) { | ||
130 | alarm(0); | ||
131 | signal(SIGALRM, SIG_DFL); | ||
132 | } | ||
133 | |||
134 | /* -e given? */ | ||
135 | if (execparam) { | ||
136 | if (cfd) { | ||
137 | signal(SIGCHLD, SIG_IGN); | ||
138 | dup2(cfd, 0); | ||
139 | close(cfd); | ||
140 | } | ||
141 | dup2(0, 1); | ||
142 | dup2(0, 2); | ||
143 | |||
144 | // With more than one -l, repeatedly act as server. | ||
145 | |||
146 | if (do_listen > 1 && vfork()) { | ||
147 | // This is a bit weird as cleanup goes, since we wind up with no | ||
148 | // stdin/stdout/stderr. But it's small and shouldn't hurt anything. | ||
149 | // We check for cfd == 0 above. | ||
150 | logmode = LOGMODE_NONE; | ||
151 | close(0); | ||
152 | close(1); | ||
153 | close(2); | ||
154 | |||
155 | goto repeatyness; | ||
156 | } | ||
157 | USE_NC_EXTRA(execvp(execparam[0], execparam);) | ||
158 | /* Don't print stuff or it will go over the wire.... */ | ||
159 | _exit(127); | ||
160 | } | ||
161 | |||
162 | // Select loop copying stdin to cfd, and cfd to stdout. | ||
163 | |||
164 | FD_ZERO(&readfds); | ||
165 | FD_SET(cfd, &readfds); | ||
166 | FD_SET(STDIN_FILENO, &readfds); | ||
167 | |||
168 | for (;;) { | ||
169 | int fd; | ||
170 | int ofd; | ||
171 | int nread; | ||
172 | |||
173 | testfds = readfds; | ||
174 | |||
175 | if (select(FD_SETSIZE, &testfds, NULL, NULL, NULL) < 0) | ||
176 | bb_perror_msg_and_die("select"); | ||
177 | |||
178 | for (fd = 0; fd < FD_SETSIZE; fd++) { | ||
179 | if (FD_ISSET(fd, &testfds)) { | ||
180 | nread = safe_read(fd, bb_common_bufsiz1, | ||
181 | sizeof(bb_common_bufsiz1)); | ||
182 | |||
183 | if (fd == cfd) { | ||
184 | if (nread<1) exit(0); | ||
185 | ofd = STDOUT_FILENO; | ||
186 | } else { | ||
187 | if (nread<1) { | ||
188 | // Close outgoing half-connection so they get EOF, but | ||
189 | // leave incoming alone so we can see response. | ||
190 | shutdown(cfd, 1); | ||
191 | FD_CLR(STDIN_FILENO, &readfds); | ||
192 | } | ||
193 | ofd = cfd; | ||
194 | } | ||
195 | |||
196 | xwrite(ofd, bb_common_bufsiz1, nread); | ||
197 | if (delay > 0) sleep(delay); | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | } | ||