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