- be C99 friendly. Anonymous unions are a GNU extension. This change is
[platform/upstream/busybox.git] / ipsvd / tcpudp.c
1 /* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
2  * which are released into public domain by the author.
3  * Homepage: http://smarden.sunsite.dk/ipsvd/
4  *
5  * Copyright (C) 2007 Denis Vlasenko.
6  *
7  * Licensed under GPLv2, see file LICENSE in this tarball for details.
8  */
9
10 /* Based on ipsvd ipsvd-0.12.1. This tcpsvd accepts all options
11  * which are supported by one from ipsvd-0.12.1, but not all are
12  * functional. See help text at the end of this file for details.
13  *
14  * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused.
15  *
16  * Output of verbose mode matches original (modulo bugs and
17  * unimplemented stuff). Unnatural splitting of IP and PORT
18  * is retained (personally I prefer one-value "IP:PORT" notation -
19  * it is a natural string representation of struct sockaddr_XX).
20  *
21  * TCPORIGDST{IP,PORT} is busybox-specific addition
22  *
23  * udp server is hacked up by reusing TCP code. It has the following
24  * limitation inherent in Unix DGRAM sockets implementation:
25  * - local IP address is retrieved (using recvmsg voodoo) but
26  *   child's socket is not bound to it (bind cannot be called on
27  *   already bound socket). Thus it still can emit outgoing packets
28  *   with wrong source IP...
29  * - don't know how to retrieve ORIGDST for udp.
30  */
31
32 #include <limits.h>
33 #include <linux/netfilter_ipv4.h> /* wants <limits.h> */
34
35 #include "libbb.h"
36 #include "ipsvd_perhost.h"
37
38 #ifdef SSLSVD
39 #include "matrixSsl.h"
40 #include "ssl_io.h"
41 #endif
42
43 struct globals {
44         unsigned verbose;
45         unsigned max_per_host;
46         unsigned cur_per_host;
47         unsigned cnum;
48         unsigned cmax;
49 };
50 #define G (*(struct globals*)&bb_common_bufsiz1)
51 #define verbose      (G.verbose     )
52 #define max_per_host (G.max_per_host)
53 #define cur_per_host (G.cur_per_host)
54 #define cnum         (G.cnum        )
55 #define cmax         (G.cmax        )
56 #define INIT_G() \
57         do { \
58                 cmax = 30; \
59         } while (0)
60
61
62 static void xsetenv_proto(const char *proto, const char *n, const char *v)
63 {
64         putenv(xasprintf("%s%s=%s", proto, n, v));
65 }
66
67 static void sig_term_handler(int sig)
68 {
69         if (verbose)
70                 printf("%s: info: sigterm received, exit\n", applet_name);
71         exit(0);
72 }
73
74 /* Little bloated, but tries to give accurate info how child exited.
75  * Makes easier to spot segfaulting children etc... */
76 static void print_waitstat(unsigned pid, int wstat)
77 {
78         unsigned e = 0;
79         const char *cause = "?exit";
80
81         if (WIFEXITED(wstat)) {
82                 cause++;
83                 e = WEXITSTATUS(wstat);
84         } else if (WIFSIGNALED(wstat)) {
85                 cause = "signal";
86                 e = WTERMSIG(wstat);
87         }
88         printf("%s: info: end %d %s %d\n", applet_name, pid, cause, e);
89 }
90
91 /* Must match getopt32 in main! */
92 enum {
93         OPT_c = (1 << 0),
94         OPT_C = (1 << 1),
95         OPT_i = (1 << 2),
96         OPT_x = (1 << 3),
97         OPT_u = (1 << 4),
98         OPT_l = (1 << 5),
99         OPT_E = (1 << 6),
100         OPT_b = (1 << 7),
101         OPT_h = (1 << 8),
102         OPT_p = (1 << 9),
103         OPT_t = (1 << 10),
104         OPT_v = (1 << 11),
105         OPT_V = (1 << 12),
106         OPT_U = (1 << 13), /* from here: sslsvd only */
107         OPT_slash = (1 << 14),
108         OPT_Z = (1 << 15),
109         OPT_K = (1 << 16),
110 };
111
112 static void connection_status(void)
113 {
114         /* "only 1 client max" desn't need this */
115         if (cmax > 1)
116                 printf("%s: info: status %u/%u\n", applet_name, cnum, cmax);
117 }
118
119 static void sig_child_handler(int sig)
120 {
121         int wstat;
122         int pid;
123
124         while ((pid = wait_any_nohang(&wstat)) > 0) {
125                 if (max_per_host)
126                         ipsvd_perhost_remove(pid);
127                 if (cnum)
128                         cnum--;
129                 if (verbose)
130                         print_waitstat(pid, wstat);
131         }
132         if (verbose)
133                 connection_status();
134 }
135
136 int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
137 int tcpudpsvd_main(int argc, char **argv)
138 {
139         char *str_c, *str_C, *str_b, *str_t;
140         char *user;
141         struct hcc *hccp;
142         const char *instructs;
143         char *msg_per_host = NULL;
144         unsigned len_per_host = len_per_host; /* gcc */
145 #ifndef SSLSVD
146         struct bb_uidgid_t ugid;
147 #endif
148         bool need_hostnames, need_remote_ip, tcp;
149         uint16_t local_port;
150         char *local_hostname = NULL;
151         char *remote_hostname = (char*)""; /* "" used if no -h */
152         char *local_addr = local_addr; /* gcc */
153         char *remote_addr = remote_addr; /* gcc */
154         char *remote_ip = remote_addr; /* gcc */
155         len_and_sockaddr *lsa;
156         len_and_sockaddr local, remote;
157         socklen_t sa_len;
158         int pid;
159         int sock;
160         int conn;
161         unsigned backlog = 20;
162
163         INIT_G();
164
165         tcp = (applet_name[0] == 't');
166
167         /* 3+ args, -i at most once, -p implies -h, -v is counter */
168         opt_complementary = "-3:i--i:ph:vv";
169 #ifdef SSLSVD
170         getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
171                 &str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
172                 &str_b, &str_t, &ssluser, &root, &cert, &key, &verbose
173         );
174 #else
175         getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v",
176                 &str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
177                 &str_b, &str_t, &verbose
178         );
179 #endif
180         if (option_mask32 & OPT_c)
181                 cmax = xatou_range(str_c, 1, INT_MAX);
182         if (option_mask32 & OPT_C) { /* -C n[:message] */
183                 max_per_host = bb_strtou(str_C, &str_C, 10);
184                 if (str_C[0]) {
185                         if (str_C[0] != ':')
186                                 bb_show_usage();
187                         msg_per_host = str_C + 1;
188                         len_per_host = strlen(msg_per_host);
189                 }
190         }
191         if (max_per_host > cmax)
192                 max_per_host = cmax;
193         if (option_mask32 & OPT_u) {
194                 if (!get_uidgid(&ugid, user, 1))
195                         bb_error_msg_and_die("unknown user/group: %s", user);
196         }
197         if (option_mask32 & OPT_b)
198                 backlog = xatou(str_b);
199 #ifdef SSLSVD
200         if (option_mask32 & OPT_U) ssluser = optarg;
201         if (option_mask32 & OPT_slash) root = optarg;
202         if (option_mask32 & OPT_Z) cert = optarg;
203         if (option_mask32 & OPT_K) key = optarg;
204 #endif
205         argv += optind;
206         if (!argv[0][0] || LONE_CHAR(argv[0], '0'))
207                 argv[0] = (char*)"0.0.0.0";
208
209         /* Per-IP flood protection is not thought-out for UDP */
210         if (!tcp)
211                 max_per_host = 0;
212
213         /* stdout is used for logging, don't buffer */
214         setlinebuf(stdout);
215         bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */
216
217         need_hostnames = verbose || !(option_mask32 & OPT_E);
218         need_remote_ip = max_per_host || need_hostnames;
219
220 #ifdef SSLSVD
221         sslser = user;
222         client = 0;
223         if ((getuid() == 0) && !(option_mask32 & OPT_u)) {
224                 xfunc_exitcode = 100;
225                 bb_error_msg_and_die("fatal: -U ssluser must be set when running as root");
226         }
227         if (option_mask32 & OPT_u)
228                 if (!uidgid_get(&sslugid, ssluser, 1)) {
229                         if (errno) {
230                                 bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser);
231                         }
232                         bb_error_msg_and_die("fatal: unknown user/group '%s'", ssluser);
233                 }
234         if (!cert) cert = "./cert.pem";
235         if (!key) key = cert;
236         if (matrixSslOpen() < 0)
237                 fatal("cannot initialize ssl");
238         if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
239                 if (client)
240                         fatal("cannot read cert, key, or ca file");
241                 fatal("cannot read cert or key file");
242         }
243         if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
244                 fatal("cannot create ssl session");
245 #endif
246
247         sig_block(SIGCHLD);
248         signal(SIGCHLD, sig_child_handler);
249         signal(SIGTERM, sig_term_handler);
250         signal(SIGPIPE, SIG_IGN);
251
252         if (max_per_host)
253                 ipsvd_perhost_init(cmax);
254
255         local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
256         lsa = xhost2sockaddr(argv[0], local_port);
257         sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
258         setsockopt_reuseaddr(sock);
259         sa_len = lsa->len; /* I presume sockaddr len stays the same */
260         xbind(sock, &lsa->u.sa, sa_len);
261         if (tcp)
262                 xlisten(sock, backlog);
263         else /* udp: needed for recv_from_to to work: */
264                 socket_want_pktinfo(sock);
265         /* ndelay_off(sock); - it is the default I think? */
266
267 #ifndef SSLSVD
268         if (option_mask32 & OPT_u) {
269                 /* drop permissions */
270                 xsetgid(ugid.gid);
271                 xsetuid(ugid.uid);
272         }
273 #endif
274
275         if (verbose) {
276                 char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
277                 printf("%s: info: listening on %s", applet_name, addr);
278                 free(addr);
279 #ifndef SSLSVD
280                 if (option_mask32 & OPT_u)
281                         printf(", uid %u, gid %u",
282                                 (unsigned)ugid.uid, (unsigned)ugid.gid);
283 #endif
284                 puts(", starting");
285         }
286
287         /* Main accept() loop */
288
289  again:
290         hccp = NULL;
291
292         while (cnum >= cmax)
293                 sig_pause(); /* wait for any signal (expecting SIGCHLD) */
294
295         /* Accept a connection to fd #0 */
296  again1:
297         close(0);
298  again2:
299         sig_unblock(SIGCHLD);
300         if (tcp) {
301                 remote.len = sa_len;
302                 conn = accept(sock, &remote.u.sa, &remote.len);
303         } else {
304                 /* In case recv_from_to won't be able to recover local addr.
305                  * Also sets port - recv_from_to is unable to do it. */
306                 local = *lsa;
307                 conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len);
308         }
309         sig_block(SIGCHLD);
310         if (conn < 0) {
311                 if (errno != EINTR)
312                         bb_perror_msg(tcp ? "accept" : "recv");
313                 goto again2;
314         }
315         xmove_fd(tcp ? conn : sock, 0);
316
317         if (max_per_host) {
318                 /* Drop connection immediately if cur_per_host > max_per_host
319                  * (minimizing load under SYN flood) */
320                 remote_ip = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
321                 cur_per_host = ipsvd_perhost_add(remote_ip, max_per_host, &hccp);
322                 if (cur_per_host > max_per_host) {
323                         /* ipsvd_perhost_add detected that max is exceeded
324                          * (and did not store ip in connection table) */
325                         free(remote_ip);
326                         if (msg_per_host) {
327                                 /* don't block or test for errors */
328                                 ndelay_on(0);
329                                 write(0, msg_per_host, len_per_host);
330                         }
331                         goto again1;
332                 }
333         }
334
335         if (!tcp) {
336                 /* Voodoo magic: making udp sockets each receive its own
337                  * packets is not trivial, and I still not sure
338                  * I do it 100% right.
339                  * 1) we have to do it before fork()
340                  * 2) order is important - is it right now? */
341
342                 /* Make plain write/send work for this socket by supplying default
343                  * destination address. This also restricts incoming packets
344                  * to ones coming from this remote IP. */
345                 xconnect(0, &remote.u.sa, sa_len);
346         /* hole? at this point we have no wildcard udp socket...
347          * can this cause clients to get "port unreachable" icmp?
348          * Yup, time window is very small, but it exists (is it?) */
349                 /* Open new non-connected UDP socket for further clients */
350                 sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
351                 setsockopt_reuseaddr(sock);
352                 xbind(sock, &lsa->u.sa, sa_len);
353                 socket_want_pktinfo(sock);
354
355                 /* Doesn't work:
356                  * we cannot replace fd #0 - we will lose pending packet
357                  * which is already buffered for us! And we cannot use fd #1
358                  * instead - it will "intercept" all following packets, but child
359                  * does not expect data coming *from fd #1*! */
360 #if 0
361                 /* Make it so that local addr is fixed to localp->u.sa
362                  * and we don't accidentally accept packets to other local IPs. */
363                 /* NB: we possibly bind to the _very_ same_ address & port as the one
364                  * already bound in parent! This seems to work in Linux.
365                  * (otherwise we can move socket to fd #0 only if bind succeeds) */
366                 close(0);
367                 set_nport(localp, htons(local_port));
368                 xmove_fd(xsocket(localp->u.sa.sa_family, SOCK_DGRAM, 0), 0);
369                 setsockopt_reuseaddr(0); /* crucial */
370                 xbind(0, &localp->u.sa, localp->len);
371 #endif
372         }
373
374         pid = fork();
375         if (pid == -1) {
376                 bb_perror_msg("fork");
377                 goto again;
378         }
379
380
381         if (pid != 0) {
382                 /* parent */
383                 cnum++;
384                 if (verbose)
385                         connection_status();
386                 if (hccp)
387                         hccp->pid = pid;
388                 goto again;
389         }
390
391         /* Child: prepare env, log, and exec prog */
392
393         /* Closing tcp listening socket */
394         if (tcp)
395                 close(sock);
396
397         if (need_remote_ip)
398                 remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
399
400         if (need_hostnames) {
401                 if (option_mask32 & OPT_h) {
402                         remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
403                         if (!remote_hostname) {
404                                 bb_error_msg("warning: cannot look up hostname for %s", remote_addr);
405                                 remote_hostname = (char*)"";
406                         }
407                 }
408                 /* Find out local IP peer connected to.
409                  * Errors ignored (I'm not paranoid enough to imagine kernel
410                  * which doesn't know local IP). */
411                 if (tcp) {
412                         local.len = sa_len;
413                         getsockname(0, &local.u.sa, &local.len);
414                 }
415                 local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
416                 if (!local_hostname) {
417                         local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
418                         if (!local_hostname)
419                                 bb_error_msg_and_die("warning: cannot look up hostname for %s"+9, local_addr);
420                 }
421         }
422
423         if (verbose) {
424                 pid = getpid();
425                 printf("%s: info: pid %u from %s\n", applet_name, pid, remote_addr);
426                 if (max_per_host)
427                         printf("%s: info: concurrency %u %s %u/%u\n",
428                                 applet_name, pid, remote_ip, cur_per_host, max_per_host);
429                 printf("%s: info: start %u %s:%s :%s:%s\n",
430                         applet_name, pid,
431                         local_hostname, local_addr,
432                         remote_hostname, remote_addr);
433         }
434
435         if (!(option_mask32 & OPT_E)) {
436                 /* setup ucspi env */
437                 const char *proto = tcp ? "TCP" : "UDP";
438
439                 /* Extract "original" destination addr:port
440                  * from Linux firewall. Useful when you redirect
441                  * an outbond connection to local handler, and it needs
442                  * to know where it originally tried to connect */
443                 if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &lsa->u.sa, &lsa->len) == 0) {
444                         char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
445                         xsetenv("TCPORIGDSTADDR", addr);
446                         free(addr);
447                 }
448                 xsetenv("PROTO", proto);
449                 xsetenv_proto(proto, "LOCALADDR", local_addr);
450                 xsetenv_proto(proto, "LOCALHOST", local_hostname);
451                 xsetenv_proto(proto, "REMOTEADDR", remote_addr);
452                 if (option_mask32 & OPT_h) {
453                         xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
454                 }
455                 xsetenv_proto(proto, "REMOTEINFO", "");
456                 /* additional */
457                 if (cur_per_host > 0) /* can not be true for udp */
458                         xsetenv("TCPCONCURRENCY", utoa(cur_per_host));
459         }
460
461         dup2(0, 1);
462
463         signal(SIGTERM, SIG_DFL);
464         signal(SIGPIPE, SIG_DFL);
465         signal(SIGCHLD, SIG_DFL);
466         sig_unblock(SIGCHLD);
467
468         argv += 2;
469 #ifdef SSLSVD
470         strcpy(id, utoa(pid);
471         ssl_io(0, argv);
472 #else
473         BB_EXECVP(argv[0], argv);
474 #endif
475         bb_perror_msg_and_die("exec '%s'", argv[0]);
476 }
477
478 /*
479 tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name]
480         [-i dir|-x cdb] [ -t sec] host port prog
481
482 tcpsvd creates a TCP/IP socket, binds it to the address host:port,
483 and listens on the socket for incoming connections.
484
485 On each incoming connection, tcpsvd conditionally runs a program,
486 with standard input reading from the socket, and standard output
487 writing to the socket, to handle this connection. tcpsvd keeps
488 listening on the socket for new connections, and can handle
489 multiple connections simultaneously.
490
491 tcpsvd optionally checks for special instructions depending
492 on the IP address or hostname of the client that initiated
493 the connection, see ipsvd-instruct(5).
494
495 host
496     host either is a hostname, or a dotted-decimal IP address,
497     or 0. If host is 0, tcpsvd accepts connections to any local
498     IP address.
499     * busybox accepts IPv6 addresses and host:port pairs too
500       In this case second parameter is ignored
501 port
502     tcpsvd accepts connections to host:port. port may be a name
503     from /etc/services or a number.
504 prog
505     prog consists of one or more arguments. For each connection,
506     tcpsvd normally runs prog, with file descriptor 0 reading from
507     the network, and file descriptor 1 writing to the network.
508     By default it also sets up TCP-related environment variables,
509     see tcp-environ(5)
510 -i dir
511     read instructions for handling new connections from the instructions
512     directory dir. See ipsvd-instruct(5) for details.
513     * ignored by busyboxed version
514 -x cdb
515     read instructions for handling new connections from the constant database
516     cdb. The constant database normally is created from an instructions
517     directory by running ipsvd-cdb(8).
518     * ignored by busyboxed version
519 -t sec
520     timeout. This option only takes effect if the -i option is given.
521     While checking the instructions directory, check the time of last access
522     of the file that matches the clients address or hostname if any, discard
523     and remove the file if it wasn't accessed within the last sec seconds;
524     tcpsvd does not discard or remove a file if the user's write permission
525     is not set, for those files the timeout is disabled. Default is 0,
526     which means that the timeout is disabled.
527     * ignored by busyboxed version
528 -l name
529     local hostname. Do not look up the local hostname in DNS, but use name
530     as hostname. This option must be set if tcpsvd listens on port 53
531     to avoid loops.
532 -u user[:group]
533     drop permissions. Switch user ID to user's UID, and group ID to user's
534     primary GID after creating and binding to the socket. If user is followed
535     by a colon and a group name, the group ID is switched to the GID of group
536     instead. All supplementary groups are removed.
537 -c n
538     concurrency. Handle up to n connections simultaneously. Default is 30.
539     If there are n connections active, tcpsvd defers acceptance of a new
540     connection until an active connection is closed.
541 -C n[:msg]
542     per host concurrency. Allow only up to n connections from the same IP
543     address simultaneously. If there are n active connections from one IP
544     address, new incoming connections from this IP address are closed
545     immediately. If n is followed by :msg, the message msg is written
546     to the client if possible, before closing the connection. By default
547     msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg.
548
549     For each accepted connection, the current per host concurrency is
550     available through the environment variable TCPCONCURRENCY. n and msg
551     can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5).
552     By default tcpsvd doesn't keep track of connections.
553 -h
554     Look up the client's hostname in DNS.
555 -p
556     paranoid. After looking up the client's hostname in DNS, look up the IP
557     addresses in DNS for that hostname, and forget about the hostname
558     if none of the addresses match the client's IP address. You should
559     set this option if you use hostname based instructions. The -p option
560     implies the -h option.
561     * ignored by busyboxed version
562 -b n
563     backlog. Allow a backlog of approximately n TCP SYNs. On some systems n
564     is silently limited. Default is 20.
565 -E
566     no special environment. Do not set up TCP-related environment variables.
567 -v
568     verbose. Print verbose messsages to standard output.
569 -vv
570     more verbose. Print more verbose messages to standard output.
571     * no difference between -v and -vv in busyboxed version
572 */