aboutsummaryrefslogtreecommitdiff
path: root/ipsvd
diff options
context:
space:
mode:
authorvda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2007-04-01 01:18:20 +0000
committervda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2007-04-01 01:18:20 +0000
commit5348a94d2a6baae045918c4cc2bdd256ee8de5b8 (patch)
treed4f6495339702c0b6d79816d0bb07ba4b6679ce8 /ipsvd
parenta4935249632f90595f8f8a5200ed7b6266cf6321 (diff)
downloadbusybox-w32-5348a94d2a6baae045918c4cc2bdd256ee8de5b8.tar.gz
busybox-w32-5348a94d2a6baae045918c4cc2bdd256ee8de5b8.tar.bz2
busybox-w32-5348a94d2a6baae045918c4cc2bdd256ee8de5b8.zip
tcpsvd: new applet
It's a GPL-ed 'clone' of Dan Bernstein's tcpserver. Author: Gerrit Pape <pape@smarden.org> http://smarden.sunsite.dk/ipsvd/ size tcpsvd.o text data bss dec hex filename 2571 4 16 2591 a1f tcpsvd.o git-svn-id: svn://busybox.net/trunk/busybox@18294 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'ipsvd')
-rw-r--r--ipsvd/Config.in14
-rw-r--r--ipsvd/Kbuild8
-rw-r--r--ipsvd/ipsvd_perhost.c55
-rw-r--r--ipsvd/ipsvd_perhost.h10
-rw-r--r--ipsvd/tcpsvd.c514
5 files changed, 601 insertions, 0 deletions
diff --git a/ipsvd/Config.in b/ipsvd/Config.in
new file mode 100644
index 000000000..fb2931d07
--- /dev/null
+++ b/ipsvd/Config.in
@@ -0,0 +1,14 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "ipsvd utilities"
7
8config TCPSVD
9 bool "tcpsvd"
10 default n
11 help
12 tcpsvd listens on a port and runs a program for each new connection
13
14endmenu
diff --git a/ipsvd/Kbuild b/ipsvd/Kbuild
new file mode 100644
index 000000000..8050921b0
--- /dev/null
+++ b/ipsvd/Kbuild
@@ -0,0 +1,8 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
4#
5# Licensed under the GPL v2, see the file LICENSE in this tarball.
6
7lib-y:=
8lib-$(CONFIG_TCPSVD) += tcpsvd.o ipsvd_perhost.o
diff --git a/ipsvd/ipsvd_perhost.c b/ipsvd/ipsvd_perhost.c
new file mode 100644
index 000000000..c6f7de339
--- /dev/null
+++ b/ipsvd/ipsvd_perhost.c
@@ -0,0 +1,55 @@
1#include "busybox.h"
2#include "ipsvd_perhost.h"
3
4static struct hcc *cc;
5static unsigned cclen;
6
7/* to be optimized */
8
9void ipsvd_perhost_init(unsigned c)
10{
11// free(cc);
12 cc = xzalloc(c * sizeof(*cc));
13 cclen = c;
14}
15
16unsigned ipsvd_perhost_add(const char *ip, unsigned maxconn, struct hcc **hccpp)
17{
18 unsigned i;
19 unsigned conn = 1;
20 int p = -1;
21
22 for (i = 0; i < cclen; ++i) {
23 if (cc[i].ip[0] == 0) {
24 if (p == -1) p = i;
25 continue;
26 }
27 if (strncmp(cc[i].ip, ip, sizeof(cc[i].ip)) == 0) {
28 conn++;
29 continue;
30 }
31 }
32 if (p == -1) return 0;
33 if (conn <= maxconn) {
34 strcpy(cc[p].ip, ip);
35 *hccpp = &cc[p];
36 }
37 return conn;
38}
39
40void ipsvd_perhost_remove(int pid)
41{
42 unsigned i;
43 for (i = 0; i < cclen; ++i) {
44 if (cc[i].pid == pid) {
45 cc[i].ip[0] = 0;
46 cc[i].pid = 0;
47 return;
48 }
49 }
50}
51
52//void ipsvd_perhost_free(void)
53//{
54// free(cc);
55//}
diff --git a/ipsvd/ipsvd_perhost.h b/ipsvd/ipsvd_perhost.h
new file mode 100644
index 000000000..05c939d89
--- /dev/null
+++ b/ipsvd/ipsvd_perhost.h
@@ -0,0 +1,10 @@
1struct hcc {
2 char ip[32 - sizeof(int)];
3 int pid;
4};
5
6void ipsvd_perhost_init(unsigned);
7unsigned ipsvd_perhost_add(const char *ip, unsigned maxconn, struct hcc **hccpp);
8void ipsvd_perhost_remove(int pid);
9//unsigned ipsvd_perhost_setpid(int pid);
10//void ipsvd_perhost_free(void);
diff --git a/ipsvd/tcpsvd.c b/ipsvd/tcpsvd.c
new file mode 100644
index 000000000..9a0a348de
--- /dev/null
+++ b/ipsvd/tcpsvd.c
@@ -0,0 +1,514 @@
1/*
2# /usr/bin/tcpsvd -v 0 1234 true
3tcpsvd: info: pid 24916 from 127.0.0.1
4tcpsvd: info: start 24916 localhost:127.0.0.1 ::127.0.0.1:47905
5tcpsvd: info: pid 24918 from 127.0.0.1
6tcpsvd: info: start 24918 localhost:127.0.0.1 ::127.0.0.1:47906
7# ./busybox tcpsvd -v 0 1234 true
8tcpsvd: info: pid 24924 from 127.0.0.1
9tcpsvd: info: start 24924 localhost:1234:127.0.0.1:1234 ::127.0.0.1:47908
10tcpsvd: info: pid 24926 from 127.0.0.1
11tcpsvd: info: start 24926 localhost:1234:127.0.0.1:1234 ::127.0.0.1:47909
12*/
13
14#include "busybox.h"
15#include "ipsvd_perhost.h"
16
17#ifdef SSLSVD
18#include "matrixSsl.h"
19#include "ssl_io.h"
20#endif
21
22
23static unsigned max_per_host; /* originally in ipsvd_check.c */
24static unsigned cur_per_host;
25static unsigned verbose;
26static unsigned cnum;
27static unsigned cmax = 30;
28
29/* Must match getopt32 in main! */
30enum {
31 OPT_c = (1 << 0),
32 OPT_C = (1 << 1),
33 OPT_i = (1 << 2),
34 OPT_x = (1 << 3),
35 OPT_u = (1 << 4),
36 OPT_l = (1 << 5),
37 OPT_E = (1 << 6),
38 OPT_b = (1 << 7),
39 OPT_h = (1 << 8),
40 OPT_p = (1 << 9),
41 OPT_t = (1 << 10),
42 OPT_v = (1 << 11),
43 OPT_V = (1 << 12),
44 OPT_U = (1 << 13),
45 OPT_slash = (1 << 14),
46 OPT_Z = (1 << 15),
47 OPT_K = (1 << 16),
48};
49
50static void connection_status(void)
51{
52 printf("%s: info: status %u/%u\n", applet_name, cnum, cmax);
53}
54
55static void sig_term_handler(int sig)
56{
57 if (verbose)
58 printf("%s: info: sigterm received, exit\n", applet_name);
59 exit(0);
60}
61
62static void sig_child_handler(int sig)
63{
64 int wstat;
65 int pid;
66
67 while ((pid = wait_nohang(&wstat)) > 0) {
68 if (max_per_host)
69 ipsvd_perhost_remove(pid);
70 if (cnum)
71 cnum--;
72 if (verbose) {
73 /* Little bloated, but tries to give accurate info
74 * how child exited. Makes easier to spot segfaulting
75 * children etc... */
76 unsigned e = 0;
77 const char *cause = "?exit";
78 if (WIFEXITED(wstat)) {
79 cause++;
80 e = WEXITSTATUS(wstat);
81 } else if (WIFSIGNALED(wstat)) {
82 cause = "signal";
83 e = WTERMSIG(wstat);
84 }
85 printf("%s: info: end %d %s %d\n",
86 applet_name, pid, cause, e);
87 }
88 }
89 if (verbose)
90 connection_status();
91}
92
93int tcpsvd_main(int argc, char **argv);
94int tcpsvd_main(int argc, char **argv)
95{
96 char *str_c, *str_C, *str_b, *str_t;
97 char *user;
98 struct hcc *hccp;
99 const char *instructs;
100 char *msg_per_host = NULL;
101 unsigned len_per_host = len_per_host; /* gcc */
102 int need_addresses;
103 int pid;
104 int sock;
105 int conn;
106 unsigned backlog = 20;
107 union {
108 struct sockaddr sa;
109 struct sockaddr_in sin;
110 USE_FEATURE_IPV6(struct sockaddr_in6 sin6;)
111 } sock_adr;
112 socklen_t sockadr_size;
113 uint16_t local_port = local_port;
114 uint16_t remote_port;
115 unsigned port;
116 char *local_hostname = NULL;
117 char *remote_hostname = (char*)""; /* "" used if no -h */
118 char *local_ip = local_ip;
119 char *remote_ip = NULL;
120 //unsigned iscdb = 0; /* = option_mask32 & OPT_x (TODO) */
121 //unsigned long timeout = 0;
122#ifndef SSLSVD
123 struct bb_uidgid_t ugid;
124#endif
125
126 /* 3+ args, -i at most once, -p implies -h, -v is counter */
127 opt_complementary = "-3:?:i--i:ph:vv";
128#ifdef SSLSVD
129 getopt32(argc, argv, "c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
130 &str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
131 &str_b, &str_t, &ssluser, &root, &cert, &key, &verbose
132 );
133#else
134 getopt32(argc, argv, "c:C:i:x:u:l:Eb:hpt:v",
135 &str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
136 &str_b, &str_t, &verbose
137 );
138#endif
139 if (option_mask32 & OPT_c)
140 cmax = xatou_range(str_c, 1, INT_MAX);
141 if (option_mask32 & OPT_C) { /* -C n[:message] */
142 max_per_host = bb_strtou(str_C, &str_C, 10);
143 if (str_C[0]) {
144 if (str_C[0] != ':')
145 bb_show_usage();
146 msg_per_host = str_C + 1;
147 len_per_host = strlen(msg_per_host);
148 }
149 }
150 if (max_per_host > cmax)
151 max_per_host = cmax;
152 if (option_mask32 & OPT_u) {
153 if (!get_uidgid(&ugid, user, 1))
154 bb_error_msg_and_die("unknown user/group: %s", user);
155 }
156 if (option_mask32 & OPT_b)
157 backlog = xatou(str_b);
158// if (option_mask32 & OPT_t) timeout = xatou(str_t);
159#ifdef SSLSVD
160 if (option_mask32 & OPT_U) ssluser = (char*)optarg; break;
161 if (option_mask32 & OPT_slash) root = (char*)optarg; break;
162 if (option_mask32 & OPT_Z) cert = (char*)optarg; break;
163 if (option_mask32 & OPT_K) key = (char*)optarg; break;
164#endif
165 argv += optind;
166 if (!argv[0][0] || LONE_CHAR(argv[0], '0'))
167 argv[0] = (char*)"0.0.0.0";
168
169 need_addresses = verbose || !(option_mask32 & OPT_E);
170
171#ifdef SSLSVD
172 sslser = user;
173 client = 0;
174 if ((getuid() == 0) && !(option_mask32 & OPT_u)) {
175 xfunc_exitcode = 100;
176 bb_error_msg_and_die("fatal: -U ssluser must be set when running as root");
177 }
178 if (option_mask32 & OPT_u)
179 if (!uidgid_get(&sslugid, ssluser, 1)) {
180 if (errno) {
181 xfunc_exitcode = 100;
182 bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser);
183 }
184 xfunc_exitcode = 111;
185 bb_error_msg_and_die("fatal: unknown user/group '%s'", ssluser);
186 }
187 if (!cert) cert = "./cert.pem";
188 if (!key) key = cert;
189 if (matrixSslOpen() < 0)
190 fatal("cannot initialize ssl");
191 if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
192 if (client)
193 fatal("cannot read cert, key, or ca file");
194 fatal("cannot read cert or key file");
195 }
196 if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
197 fatal("cannot create ssl session");
198#endif
199
200 sig_block(SIGCHLD);
201 signal(SIGCHLD, sig_child_handler);
202 signal(SIGTERM, sig_term_handler);
203 signal(SIGPIPE, SIG_IGN);
204
205 if (max_per_host)
206 ipsvd_perhost_init(cmax);
207
208 port = bb_lookup_port(argv[1], "tcp", 0);
209 sock = create_and_bind_stream_or_die(argv[0], port);
210 xlisten(sock, backlog);
211 /* ndelay_off(sock); - it is the default I think? */
212
213#ifndef SSLSVD
214 if (option_mask32 & OPT_u) {
215 /* drop permissions */
216 xsetgid(ugid.gid);
217 xsetuid(ugid.uid);
218 }
219#endif
220 bb_sanitize_stdio(); /* fd# 1,2 must be opened */
221 close(0);
222
223 if (verbose) {
224 /* we do it only for ":port" cosmetics... oh well */
225 len_and_sockaddr *lsa = xhost2sockaddr(argv[0], port);
226 char *addr = xmalloc_sockaddr2dotted(&lsa->sa, lsa->len);
227
228 printf("%s: info: listening on %s", applet_name, addr);
229 free(addr);
230#ifndef SSLSVD
231 if (option_mask32 & OPT_u)
232 printf(", uid %u, gid %u",
233 (unsigned)ugid.uid, (unsigned)ugid.uid);
234#endif
235 puts(", starting");
236 }
237
238 /* The rest is a main accept() loop */
239
240 again:
241 hccp = NULL;
242
243 while (cnum >= cmax)
244 sig_pause(); /* wait for any signal (expecting SIGCHLD) */
245
246 sockadr_size = sizeof(sock_adr);
247 sig_unblock(SIGCHLD);
248 conn = accept(sock, &sock_adr.sa, &sockadr_size);
249 sig_block(SIGCHLD);
250 if (conn == -1) {
251 if (errno != EINTR)
252 bb_perror_msg("accept");
253 goto again;
254 }
255
256 if (max_per_host) {
257 /* we drop connection immediately if cur_per_host > max_per_host
258 * (minimizing load under SYN flood) */
259 free(remote_ip);
260 remote_ip = xmalloc_sockaddr2dotted_noport(&sock_adr.sa, sockadr_size);
261 cur_per_host = ipsvd_perhost_add(remote_ip, max_per_host, &hccp);
262 if (cur_per_host > max_per_host) {
263 /* ipsvd_perhost_add detected that max is exceeded
264 * (and did not store us in connection table) */
265 if (msg_per_host) {
266 ndelay_on(conn);
267 /* don't test for errors */
268 write(conn, msg_per_host, len_per_host);
269 }
270 close(conn);
271 goto again;
272 }
273 }
274
275 cnum++;
276 if (verbose)
277 connection_status();
278
279 pid = fork();
280 if (pid == -1) {
281 bb_perror_msg("fork");
282 close(conn);
283 goto again;
284 }
285 if (pid != 0) {
286 /* parent */
287 close(conn);
288 if (hccp)
289 hccp->pid = pid;
290 goto again;
291 }
292
293 /* Child: prepare env, log, and exec prog */
294
295 close(sock);
296
297 if (!max_per_host)
298 remote_ip = xmalloc_sockaddr2dotted_noport(&sock_adr.sa, sizeof(sock_adr));
299 /* else it is already done */
300
301 remote_port = get_nport(&sock_adr.sa);
302 remote_port = ntohs(remote_port);
303
304 if (verbose) {
305 pid = getpid();
306 printf("%s: info: pid %d from %s\n", applet_name, pid, remote_ip);
307 }
308
309 if (need_addresses && (option_mask32 & OPT_h)) {
310 remote_hostname = xmalloc_sockaddr2host(&sock_adr.sa, sizeof(sock_adr));
311 if (!remote_hostname) {
312 bb_error_msg("warning: cannot look up hostname for %s", remote_ip);
313 remote_hostname = (char*)"";
314 }
315 }
316
317 sockadr_size = sizeof(sock_adr);
318 /* Errors ignored (I'm not paranoid enough to imagine kernel
319 * which doesn't know local ip) */
320 getsockname(conn, &sock_adr.sa, &sockadr_size);
321
322 if (need_addresses) {
323 local_ip = xmalloc_sockaddr2dotted_noport(&sock_adr.sa, sockadr_size);
324 local_port = get_nport(&sock_adr.sa);
325 local_port = ntohs(local_port);
326 if (!local_hostname) {
327 local_hostname = xmalloc_sockaddr2host_noport(&sock_adr.sa, sockadr_size);
328 if (!local_hostname)
329 bb_error_msg_and_die("cannot look up local hostname for %s", local_ip);
330 }
331 }
332
333 if (!(option_mask32 & OPT_E)) {
334 /* setup ucspi env */
335 xsetenv("PROTO", "TCP");
336 xsetenv("TCPLOCALIP", local_ip);
337 xsetenv("TCPLOCALPORT", utoa(local_port));
338 xsetenv("TCPLOCALHOST", local_hostname);
339 xsetenv("TCPREMOTEIP", remote_ip);
340 xsetenv("TCPREMOTEPORT", utoa(remote_port));
341 if (option_mask32 & OPT_h) {
342 xsetenv("TCPREMOTEHOST", remote_hostname);
343 }
344 xsetenv("TCPREMOTEINFO", "");
345 /* additional */
346 if (cur_per_host > 0)
347 xsetenv("TCPCONCURRENCY", utoa(cur_per_host));
348 }
349
350#if 0
351 if (instructs) {
352 ac = ipsvd_check(iscdb, &inst, &match, (char*)instructs,
353 remote_ip, remote_hostname, timeout);
354 if (ac == -1) drop2("cannot check inst", remote_ip);
355 if (ac == IPSVD_ERR) drop2("cannot read", (char*)instructs);
356 } else
357 ac = IPSVD_DEFAULT;
358#endif
359
360 if (max_per_host && verbose)
361 printf("%s: info: concurrency %u %s %u/%u\n",
362 applet_name, pid, remote_ip, cur_per_host, max_per_host);
363
364 if (verbose) {
365 printf("%s: info: start %u %s:%s :%s:%s:%u\n",
366 applet_name, pid,
367 local_hostname, local_ip,
368 remote_hostname, remote_ip, (unsigned)remote_port);
369#if 0
370 switch(ac) {
371 case IPSVD_DENY:
372 printf("deny "); break;
373 case IPSVD_DEFAULT:
374 case IPSVD_INSTRUCT:
375 printf("start "); break;
376 case IPSVD_EXEC:
377 printf("exec "); break;
378 }
379 ...
380 if (instructs) {
381 printf(" ");
382 if (iscdb) {
383 printf((char*)instructs);
384 printf("/");
385 }
386 outfix(match.s);
387 if(inst.s && inst.len && (verbose > 1)) {
388 printf(": ");
389 printf(&inst);
390 }
391 }
392 printf("\n");
393#endif
394 }
395
396#if 0
397 if (ac == IPSVD_DENY) {
398 close(conn);
399 _exit(100);
400 }
401 if (ac == IPSVD_EXEC) {
402 args[0] = "/bin/sh";
403 args[1] = "-c";
404 args[2] = inst.s;
405 args[3] = 0;
406 run = args;
407 } else
408 run = argv + 2; /* below: we use argv+2 (was using run) */
409#endif
410
411 xmove_fd(conn, 0);
412 dup2(0, 1);
413 signal(SIGTERM, SIG_DFL);
414 signal(SIGPIPE, SIG_DFL);
415 signal(SIGCHLD, SIG_DFL);
416 sig_unblock(SIGCHLD);
417
418 argv += 2;
419#ifdef SSLSVD
420 strcpy(id, utoa(pid);
421 ssl_io(0, argv);
422#else
423 BB_EXECVP(argv[0], argv);
424#endif
425 bb_perror_msg_and_die("exec '%s'", argv[0]);
426}
427
428/*
429tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name] [-i dir|-x cdb] [ -t sec] host port prog
430
431tcpsvd creates a TCP/IP socket, binds it to the address host:port,
432and listens on the socket for incoming connections.
433
434On each incoming connection, tcpsvd conditionally runs a program,
435with standard input reading from the socket, and standard output
436writing to the socket, to handle this connection. tcpsvd keeps
437listening on the socket for new connections, and can handle
438multiple connections simultaneously.
439
440tcpsvd optionally checks for special instructions depending
441on the IP address or hostname of the client that initiated
442the connection, see ipsvd-instruct(5).
443
444host
445 host either is a hostname, or a dotted-decimal IP address,
446 or 0. If host is 0, tcpsvd accepts connections to any local
447 IP address.
448port
449 tcpsvd accepts connections to host:port. port may be a name
450 from /etc/services or a number.
451prog
452 prog consists of one or more arguments. For each connection,
453 tcpsvd normally runs prog, with file descriptor 0 reading from
454 the network, and file descriptor 1 writing to the network.
455 By default it also sets up TCP-related environment variables,
456 see tcp-environ(5)
457-i dir
458 read instructions for handling new connections from the instructions
459 directory dir. See ipsvd-instruct(5) for details.
460-x cdb
461 read instructions for handling new connections from the constant database
462 cdb. The constant database normally is created from an instructions
463 directory by running ipsvd-cdb(8).
464-t sec
465 timeout. This option only takes effect if the -i option is given.
466 While checking the instructions directory, check the time of last access
467 of the file that matches the clients address or hostname if any, discard
468 and remove the file if it wasn't accessed within the last sec seconds;
469 tcpsvd does not discard or remove a file if the user's write permission
470 is not set, for those files the timeout is disabled. Default is 0,
471 which means that the timeout is disabled.
472-l name
473 local hostname. Do not look up the local hostname in DNS, but use name
474 as hostname. This option must be set if tcpsvd listens on port 53
475 to avoid loops.
476-u user[:group]
477 drop permissions. Switch user ID to user's UID, and group ID to user's
478 primary GID after creating and binding to the socket. If user is followed
479 by a colon and a group name, the group ID is switched to the GID of group
480 instead. All supplementary groups are removed.
481-c n
482 concurrency. Handle up to n connections simultaneously. Default is 30.
483 If there are n connections active, tcpsvd defers acceptance of a new
484 connection until an active connection is closed.
485-C n[:msg]
486 per host concurrency. Allow only up to n connections from the same IP
487 address simultaneously. If there are n active connections from one IP
488 address, new incoming connections from this IP address are closed
489 immediately. If n is followed by :msg, the message msg is written
490 to the client if possible, before closing the connection. By default
491 msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg.
492
493 For each accepted connection, the current per host concurrency is
494 available through the environment variable TCPCONCURRENCY. n and msg
495 can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5).
496 By default tcpsvd doesn't keep track of connections.
497-h
498 Look up the client's hostname in DNS.
499-p
500 paranoid. After looking up the client's hostname in DNS, look up the IP
501 addresses in DNS for that hostname, and forget about the hostname
502 if none of the addresses match the client's IP address. You should
503 set this option if you use hostname based instructions. The -p option
504 implies the -h option.
505-b n
506 backlog. Allow a backlog of approximately n TCP SYNs. On some systems n
507 is silently limited. Default is 20.
508-E
509 no special environment. Do not set up TCP-related environment variables.
510-v
511 verbose. Print verbose messsages to standard output.
512-vv
513 more verbose. Print more verbose messages to standard output.
514*/