diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-17 08:35:44 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-17 08:35:44 +0000 |
| commit | aefed941c26aade36c5b51c8ef5ea6f740cc0e5d (patch) | |
| tree | 761f70714a935be54cac8ec4efb196638a092ae9 | |
| parent | 9f153f610fe82528bcd0f976fd9ce2122c516b2d (diff) | |
| download | busybox-w32-aefed941c26aade36c5b51c8ef5ea6f740cc0e5d.tar.gz busybox-w32-aefed941c26aade36c5b51c8ef5ea6f740cc0e5d.tar.bz2 busybox-w32-aefed941c26aade36c5b51c8ef5ea6f740cc0e5d.zip | |
tcpsvd,udpsvd: make them NOMMU-capable
inetd: make udp nowait work
function old new delta
inetd_main 1797 2036 +239
tcpudpsvd_main 1839 1973 +134
xsetenv_plain - 39 +39
xsetenv_proto 23 40 +17
bump_nofile 169 170 +1
sig_term_handler 72 69 -3
sig_child_handler 239 233 -6
connection_status 37 31 -6
parse_one_line 1102 1092 -10
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 4/4 up/down: 430/-25) Total: 405 bytes
text data bss dec hex filename
798437 661 7428 806526 c4e7e busybox_old
798734 661 7428 806823 c4fa7 busybox_unstripped
| -rw-r--r-- | TODO_config_nommu | 6 | ||||
| -rw-r--r-- | ipsvd/tcpudp.c | 231 | ||||
| -rw-r--r-- | networking/inetd.c | 101 |
3 files changed, 198 insertions, 140 deletions
diff --git a/TODO_config_nommu b/TODO_config_nommu index b0221bbc3..8bf7b215c 100644 --- a/TODO_config_nommu +++ b/TODO_config_nommu | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | # | 1 | # |
| 2 | # Automatically generated make config: don't edit | 2 | # Automatically generated make config: don't edit |
| 3 | # Busybox version: 1.10.0.svn | 3 | # Busybox version: 1.10.0.svn |
| 4 | # Thu Mar 6 20:18:05 2008 | 4 | # Sat Mar 8 01:11:42 2008 |
| 5 | # | 5 | # |
| 6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
| 7 | 7 | ||
| @@ -844,5 +844,5 @@ CONFIG_LPQ=y | |||
| 844 | # | 844 | # |
| 845 | # ipsvd utilities | 845 | # ipsvd utilities |
| 846 | # | 846 | # |
| 847 | # CONFIG_TCPSVD is not set | 847 | CONFIG_TCPSVD=y |
| 848 | # CONFIG_UDPSVD is not set | 848 | CONFIG_UDPSVD=y |
diff --git a/ipsvd/tcpudp.c b/ipsvd/tcpudp.c index 5362af5fd..2e8dffa0f 100644 --- a/ipsvd/tcpudp.c +++ b/ipsvd/tcpudp.c | |||
| @@ -13,12 +13,12 @@ | |||
| 13 | * | 13 | * |
| 14 | * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused. | 14 | * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused. |
| 15 | * | 15 | * |
| 16 | * Output of verbose mode matches original (modulo bugs and | 16 | * Busybox version exports TCPLOCALADDR instead of |
| 17 | * unimplemented stuff). Unnatural splitting of IP and PORT | 17 | * TCPLOCALIP + TCPLOCALPORT pair. ADDR more closely matches reality |
| 18 | * is retained (personally I prefer one-value "IP:PORT" notation - | 18 | * (which is "struct sockaddr_XXX". Port is not a separate entity, |
| 19 | * it is a natural string representation of struct sockaddr_XX). | 19 | * it's just a part of (AF_INET[6]) sockaddr!). |
| 20 | * | 20 | * |
| 21 | * TCPORIGDST{IP,PORT} is busybox-specific addition | 21 | * TCPORIGDSTADDR is Busybox-specific addition. |
| 22 | * | 22 | * |
| 23 | * udp server is hacked up by reusing TCP code. It has the following | 23 | * udp server is hacked up by reusing TCP code. It has the following |
| 24 | * limitation inherent in Unix DGRAM sockets implementation: | 24 | * limitation inherent in Unix DGRAM sockets implementation: |
| @@ -46,6 +46,8 @@ struct globals { | |||
| 46 | unsigned cur_per_host; | 46 | unsigned cur_per_host; |
| 47 | unsigned cnum; | 47 | unsigned cnum; |
| 48 | unsigned cmax; | 48 | unsigned cmax; |
| 49 | char **env_cur; | ||
| 50 | char *env_var[1]; /* actually bigger */ | ||
| 49 | }; | 51 | }; |
| 50 | #define G (*(struct globals*)&bb_common_bufsiz1) | 52 | #define G (*(struct globals*)&bb_common_bufsiz1) |
| 51 | #define verbose (G.verbose ) | 53 | #define verbose (G.verbose ) |
| @@ -53,21 +55,46 @@ struct globals { | |||
| 53 | #define cur_per_host (G.cur_per_host) | 55 | #define cur_per_host (G.cur_per_host) |
| 54 | #define cnum (G.cnum ) | 56 | #define cnum (G.cnum ) |
| 55 | #define cmax (G.cmax ) | 57 | #define cmax (G.cmax ) |
| 58 | #define env_cur (G.env_cur ) | ||
| 59 | #define env_var (G.env_var ) | ||
| 56 | #define INIT_G() \ | 60 | #define INIT_G() \ |
| 57 | do { \ | 61 | do { \ |
| 58 | cmax = 30; \ | 62 | cmax = 30; \ |
| 63 | env_cur = &env_var[0]; \ | ||
| 59 | } while (0) | 64 | } while (0) |
| 60 | 65 | ||
| 61 | 66 | ||
| 67 | /* We have to be careful about leaking memory in repeated setenv's */ | ||
| 68 | static void xsetenv_plain(const char *n, const char *v) | ||
| 69 | { | ||
| 70 | char *var = xasprintf("%s=%s", n, v); | ||
| 71 | *env_cur++ = var; | ||
| 72 | putenv(var); | ||
| 73 | } | ||
| 74 | |||
| 62 | static void xsetenv_proto(const char *proto, const char *n, const char *v) | 75 | static void xsetenv_proto(const char *proto, const char *n, const char *v) |
| 63 | { | 76 | { |
| 64 | putenv(xasprintf("%s%s=%s", proto, n, v)); | 77 | char *var = xasprintf("%s%s=%s", proto, n, v); |
| 78 | *env_cur++ = var; | ||
| 79 | putenv(var); | ||
| 80 | } | ||
| 81 | |||
| 82 | static void undo_xsetenv(void) | ||
| 83 | { | ||
| 84 | char **pp = env_cur = &env_var[0]; | ||
| 85 | while (*pp) { | ||
| 86 | char *var = *pp; | ||
| 87 | *strchrnul(var, '=') = '\0'; | ||
| 88 | unsetenv(var); | ||
| 89 | free(var); | ||
| 90 | *pp++ = NULL; | ||
| 91 | } | ||
| 65 | } | 92 | } |
| 66 | 93 | ||
| 67 | static void sig_term_handler(int sig) | 94 | static void sig_term_handler(int sig) |
| 68 | { | 95 | { |
| 69 | if (verbose) | 96 | if (verbose) |
| 70 | printf("%s: info: sigterm received, exit\n", applet_name); | 97 | bb_error_msg("got signal %u, exit", sig); |
| 71 | kill_myself_with_sig(sig); | 98 | kill_myself_with_sig(sig); |
| 72 | } | 99 | } |
| 73 | 100 | ||
| @@ -85,7 +112,7 @@ static void print_waitstat(unsigned pid, int wstat) | |||
| 85 | cause = "signal"; | 112 | cause = "signal"; |
| 86 | e = WTERMSIG(wstat); | 113 | e = WTERMSIG(wstat); |
| 87 | } | 114 | } |
| 88 | printf("%s: info: end %d %s %d\n", applet_name, pid, cause, e); | 115 | bb_error_msg("end %d %s %d", pid, cause, e); |
| 89 | } | 116 | } |
| 90 | 117 | ||
| 91 | /* Must match getopt32 in main! */ | 118 | /* Must match getopt32 in main! */ |
| @@ -113,7 +140,7 @@ static void connection_status(void) | |||
| 113 | { | 140 | { |
| 114 | /* "only 1 client max" desn't need this */ | 141 | /* "only 1 client max" desn't need this */ |
| 115 | if (cmax > 1) | 142 | if (cmax > 1) |
| 116 | printf("%s: info: status %u/%u\n", applet_name, cnum, cmax); | 143 | bb_error_msg("status %u/%u", cnum, cmax); |
| 117 | } | 144 | } |
| 118 | 145 | ||
| 119 | static void sig_child_handler(int sig) | 146 | static void sig_child_handler(int sig) |
| @@ -145,13 +172,11 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 145 | #ifndef SSLSVD | 172 | #ifndef SSLSVD |
| 146 | struct bb_uidgid_t ugid; | 173 | struct bb_uidgid_t ugid; |
| 147 | #endif | 174 | #endif |
| 148 | bool need_hostnames, need_remote_ip, tcp; | 175 | bool tcp; |
| 149 | uint16_t local_port; | 176 | uint16_t local_port; |
| 150 | char *local_hostname = NULL; | 177 | char *preset_local_hostname = NULL; |
| 151 | char *remote_hostname = (char*)""; /* "" used if no -h */ | 178 | char *remote_hostname = remote_hostname; /* for compiler */ |
| 152 | char *local_addr = local_addr; /* gcc */ | 179 | char *remote_addr = remote_addr; /* for compiler */ |
| 153 | char *remote_addr = remote_addr; /* gcc */ | ||
| 154 | char *remote_ip = remote_addr; /* gcc */ | ||
| 155 | len_and_sockaddr *lsa; | 180 | len_and_sockaddr *lsa; |
| 156 | len_and_sockaddr local, remote; | 181 | len_and_sockaddr local, remote; |
| 157 | socklen_t sa_len; | 182 | socklen_t sa_len; |
| @@ -168,12 +193,12 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 168 | opt_complementary = "-3:i--i:ph:vv"; | 193 | opt_complementary = "-3:i--i:ph:vv"; |
| 169 | #ifdef SSLSVD | 194 | #ifdef SSLSVD |
| 170 | getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", | 195 | getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:", |
| 171 | &str_c, &str_C, &instructs, &instructs, &user, &local_hostname, | 196 | &str_c, &str_C, &instructs, &instructs, &user, &preset_local_hostname, |
| 172 | &str_b, &str_t, &ssluser, &root, &cert, &key, &verbose | 197 | &str_b, &str_t, &ssluser, &root, &cert, &key, &verbose |
| 173 | ); | 198 | ); |
| 174 | #else | 199 | #else |
| 175 | getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", | 200 | getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v", |
| 176 | &str_c, &str_C, &instructs, &instructs, &user, &local_hostname, | 201 | &str_c, &str_C, &instructs, &instructs, &user, &preset_local_hostname, |
| 177 | &str_b, &str_t, &verbose | 202 | &str_b, &str_t, &verbose |
| 178 | ); | 203 | ); |
| 179 | #endif | 204 | #endif |
| @@ -210,26 +235,21 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 210 | if (!tcp) | 235 | if (!tcp) |
| 211 | max_per_host = 0; | 236 | max_per_host = 0; |
| 212 | 237 | ||
| 213 | /* stdout is used for logging, don't buffer */ | ||
| 214 | setlinebuf(stdout); | ||
| 215 | bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ | 238 | bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */ |
| 216 | 239 | ||
| 217 | need_hostnames = verbose || !(option_mask32 & OPT_E); | ||
| 218 | need_remote_ip = max_per_host || need_hostnames; | ||
| 219 | |||
| 220 | #ifdef SSLSVD | 240 | #ifdef SSLSVD |
| 221 | sslser = user; | 241 | sslser = user; |
| 222 | client = 0; | 242 | client = 0; |
| 223 | if ((getuid() == 0) && !(option_mask32 & OPT_u)) { | 243 | if ((getuid() == 0) && !(option_mask32 & OPT_u)) { |
| 224 | xfunc_exitcode = 100; | 244 | xfunc_exitcode = 100; |
| 225 | bb_error_msg_and_die("fatal: -U ssluser must be set when running as root"); | 245 | bb_error_msg_and_die("-U ssluser must be set when running as root"); |
| 226 | } | 246 | } |
| 227 | if (option_mask32 & OPT_u) | 247 | if (option_mask32 & OPT_u) |
| 228 | if (!uidgid_get(&sslugid, ssluser, 1)) { | 248 | if (!uidgid_get(&sslugid, ssluser, 1)) { |
| 229 | if (errno) { | 249 | if (errno) { |
| 230 | bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser); | 250 | bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser); |
| 231 | } | 251 | } |
| 232 | bb_error_msg_and_die("fatal: unknown user/group '%s'", ssluser); | 252 | bb_error_msg_and_die("unknown user/group '%s'", ssluser); |
| 233 | } | 253 | } |
| 234 | if (!cert) cert = "./cert.pem"; | 254 | if (!cert) cert = "./cert.pem"; |
| 235 | if (!key) key = cert; | 255 | if (!key) key = cert; |
| @@ -246,7 +266,7 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 246 | 266 | ||
| 247 | sig_block(SIGCHLD); | 267 | sig_block(SIGCHLD); |
| 248 | signal(SIGCHLD, sig_child_handler); | 268 | signal(SIGCHLD, sig_child_handler); |
| 249 | signal(SIGTERM, sig_term_handler); | 269 | bb_signals(BB_SIGS_FATAL, sig_term_handler); |
| 250 | signal(SIGPIPE, SIG_IGN); | 270 | signal(SIGPIPE, SIG_IGN); |
| 251 | 271 | ||
| 252 | if (max_per_host) | 272 | if (max_per_host) |
| @@ -254,6 +274,8 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 254 | 274 | ||
| 255 | local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); | 275 | local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0); |
| 256 | lsa = xhost2sockaddr(argv[0], local_port); | 276 | lsa = xhost2sockaddr(argv[0], local_port); |
| 277 | argv += 2; | ||
| 278 | |||
| 257 | sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); | 279 | sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); |
| 258 | setsockopt_reuseaddr(sock); | 280 | setsockopt_reuseaddr(sock); |
| 259 | sa_len = lsa->len; /* I presume sockaddr len stays the same */ | 281 | sa_len = lsa->len; /* I presume sockaddr len stays the same */ |
| @@ -274,14 +296,13 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 274 | 296 | ||
| 275 | if (verbose) { | 297 | if (verbose) { |
| 276 | char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); | 298 | char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); |
| 277 | printf("%s: info: listening on %s", applet_name, addr); | 299 | bb_error_msg("listening on %s, starting", addr); |
| 278 | free(addr); | 300 | free(addr); |
| 279 | #ifndef SSLSVD | 301 | #ifndef SSLSVD |
| 280 | if (option_mask32 & OPT_u) | 302 | if (option_mask32 & OPT_u) |
| 281 | printf(", uid %u, gid %u", | 303 | printf(", uid %u, gid %u", |
| 282 | (unsigned)ugid.uid, (unsigned)ugid.gid); | 304 | (unsigned)ugid.uid, (unsigned)ugid.gid); |
| 283 | #endif | 305 | #endif |
| 284 | puts(", starting"); | ||
| 285 | } | 306 | } |
| 286 | 307 | ||
| 287 | /* Main accept() loop */ | 308 | /* Main accept() loop */ |
| @@ -297,14 +318,15 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 297 | close(0); | 318 | close(0); |
| 298 | again2: | 319 | again2: |
| 299 | sig_unblock(SIGCHLD); | 320 | sig_unblock(SIGCHLD); |
| 321 | local.len = remote.len = sa_len; | ||
| 300 | if (tcp) { | 322 | if (tcp) { |
| 301 | remote.len = sa_len; | ||
| 302 | conn = accept(sock, &remote.u.sa, &remote.len); | 323 | conn = accept(sock, &remote.u.sa, &remote.len); |
| 303 | } else { | 324 | } else { |
| 304 | /* In case recv_from_to won't be able to recover local addr. | 325 | /* 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. */ | 326 | * Also sets port - recv_from_to is unable to do it. */ |
| 306 | local = *lsa; | 327 | local = *lsa; |
| 307 | conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len); | 328 | conn = recv_from_to(sock, NULL, 0, MSG_DONTWAIT | MSG_PEEK, |
| 329 | &remote.u.sa, &local.u.sa, sa_len); | ||
| 308 | } | 330 | } |
| 309 | sig_block(SIGCHLD); | 331 | sig_block(SIGCHLD); |
| 310 | if (conn < 0) { | 332 | if (conn < 0) { |
| @@ -317,19 +339,19 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 317 | if (max_per_host) { | 339 | if (max_per_host) { |
| 318 | /* Drop connection immediately if cur_per_host > max_per_host | 340 | /* Drop connection immediately if cur_per_host > max_per_host |
| 319 | * (minimizing load under SYN flood) */ | 341 | * (minimizing load under SYN flood) */ |
| 320 | remote_ip = xmalloc_sockaddr2dotted_noport(&remote.u.sa); | 342 | remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa); |
| 321 | cur_per_host = ipsvd_perhost_add(remote_ip, max_per_host, &hccp); | 343 | cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp); |
| 322 | if (cur_per_host > max_per_host) { | 344 | if (cur_per_host > max_per_host) { |
| 323 | /* ipsvd_perhost_add detected that max is exceeded | 345 | /* ipsvd_perhost_add detected that max is exceeded |
| 324 | * (and did not store ip in connection table) */ | 346 | * (and did not store ip in connection table) */ |
| 325 | free(remote_ip); | 347 | free(remote_addr); |
| 326 | if (msg_per_host) { | 348 | if (msg_per_host) { |
| 327 | /* don't block or test for errors */ | 349 | /* don't block or test for errors */ |
| 328 | ndelay_on(0); | 350 | send(0, msg_per_host, len_per_host, MSG_DONTWAIT); |
| 329 | write(0, msg_per_host, len_per_host); | ||
| 330 | } | 351 | } |
| 331 | goto again1; | 352 | goto again1; |
| 332 | } | 353 | } |
| 354 | /* NB: remote_addr is not leaked, it is stored in conn table */ | ||
| 333 | } | 355 | } |
| 334 | 356 | ||
| 335 | if (!tcp) { | 357 | if (!tcp) { |
| @@ -372,19 +394,21 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 372 | #endif | 394 | #endif |
| 373 | } | 395 | } |
| 374 | 396 | ||
| 375 | pid = fork(); | 397 | pid = vfork(); |
| 376 | if (pid == -1) { | 398 | if (pid == -1) { |
| 377 | bb_perror_msg("fork"); | 399 | bb_perror_msg("vfork"); |
| 378 | goto again; | 400 | goto again; |
| 379 | } | 401 | } |
| 380 | 402 | ||
| 381 | if (pid != 0) { | 403 | if (pid != 0) { |
| 382 | /* parent */ | 404 | /* Parent */ |
| 383 | cnum++; | 405 | cnum++; |
| 384 | if (verbose) | 406 | if (verbose) |
| 385 | connection_status(); | 407 | connection_status(); |
| 386 | if (hccp) | 408 | if (hccp) |
| 387 | hccp->pid = pid; | 409 | hccp->pid = pid; |
| 410 | /* clean up changes done by vforked child */ | ||
| 411 | undo_xsetenv(); | ||
| 388 | goto again; | 412 | goto again; |
| 389 | } | 413 | } |
| 390 | 414 | ||
| @@ -394,78 +418,93 @@ int tcpudpsvd_main(int argc, char **argv) | |||
| 394 | if (tcp) | 418 | if (tcp) |
| 395 | close(sock); | 419 | close(sock); |
| 396 | 420 | ||
| 397 | if (need_remote_ip) | 421 | { /* vfork alert! every xmalloc in this block should be freed! */ |
| 398 | remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); | 422 | char *local_hostname = local_hostname; /* for compiler */ |
| 399 | 423 | char *local_addr = NULL; | |
| 400 | if (need_hostnames) { | 424 | char *free_me0 = NULL; |
| 401 | if (option_mask32 & OPT_h) { | 425 | char *free_me1 = NULL; |
| 402 | remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); | 426 | char *free_me2 = NULL; |
| 403 | if (!remote_hostname) { | 427 | |
| 404 | bb_error_msg("warning: cannot look up hostname for %s", remote_addr); | 428 | if (verbose || !(option_mask32 & OPT_E)) { |
| 405 | remote_hostname = (char*)""; | 429 | if (!max_per_host) /* remote_addr is not yet known */ |
| 430 | free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa); | ||
| 431 | if (option_mask32 & OPT_h) { | ||
| 432 | free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa); | ||
| 433 | if (!remote_hostname) { | ||
| 434 | bb_error_msg("cannot look up hostname for %s", remote_addr); | ||
| 435 | remote_hostname = remote_addr; | ||
| 436 | } | ||
| 437 | } | ||
| 438 | /* Find out local IP peer connected to. | ||
| 439 | * Errors ignored (I'm not paranoid enough to imagine kernel | ||
| 440 | * which doesn't know local IP). */ | ||
| 441 | if (tcp) | ||
| 442 | getsockname(0, &local.u.sa, &local.len); | ||
| 443 | /* else: for UDP it is done earlier by parent */ | ||
| 444 | local_addr = xmalloc_sockaddr2dotted(&local.u.sa); | ||
| 445 | if (option_mask32 & OPT_h) { | ||
| 446 | local_hostname = preset_local_hostname; | ||
| 447 | if (!local_hostname) { | ||
| 448 | free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); | ||
| 449 | if (!local_hostname) | ||
| 450 | bb_error_msg_and_die("cannot look up hostname for %s", local_addr); | ||
| 451 | } | ||
| 452 | /* else: local_hostname is not NULL, but is NOT malloced! */ | ||
| 406 | } | 453 | } |
| 407 | } | 454 | } |
| 408 | /* Find out local IP peer connected to. | 455 | if (verbose) { |
| 409 | * Errors ignored (I'm not paranoid enough to imagine kernel | 456 | pid = getpid(); |
| 410 | * which doesn't know local IP). */ | 457 | if (max_per_host) { |
| 411 | if (tcp) { | 458 | bb_error_msg("concurrency %s %u/%u", |
| 412 | local.len = sa_len; | 459 | remote_addr, |
| 413 | getsockname(0, &local.u.sa, &local.len); | 460 | cur_per_host, max_per_host); |
| 414 | } | 461 | } |
| 415 | local_addr = xmalloc_sockaddr2dotted(&local.u.sa); | 462 | bb_error_msg((option_mask32 & OPT_h) |
| 416 | if (!local_hostname) { | 463 | ? "start %u %s-%s (%s-%s)" |
| 417 | local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa); | 464 | : "start %u %s-%s", |
| 418 | if (!local_hostname) | 465 | pid, |
| 419 | bb_error_msg_and_die("warning: cannot look up hostname for %s"+9, local_addr); | 466 | local_addr, remote_addr, |
| 467 | local_hostname, remote_hostname); | ||
| 420 | } | 468 | } |
| 421 | } | ||
| 422 | 469 | ||
| 423 | if (verbose) { | 470 | if (!(option_mask32 & OPT_E)) { |
| 424 | pid = getpid(); | 471 | /* setup ucspi env */ |
| 425 | printf("%s: info: pid %u from %s\n", applet_name, pid, remote_addr); | 472 | const char *proto = tcp ? "TCP" : "UDP"; |
| 426 | if (max_per_host) | 473 | |
| 427 | printf("%s: info: concurrency %u %s %u/%u\n", | 474 | /* Extract "original" destination addr:port |
| 428 | applet_name, pid, remote_ip, cur_per_host, max_per_host); | 475 | * from Linux firewall. Useful when you redirect |
| 429 | printf("%s: info: start %u %s:%s :%s:%s\n", | 476 | * an outbond connection to local handler, and it needs |
| 430 | applet_name, pid, | 477 | * to know where it originally tried to connect */ |
| 431 | local_hostname, local_addr, | 478 | if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) { |
| 432 | remote_hostname, remote_addr); | 479 | char *addr = xmalloc_sockaddr2dotted(&local.u.sa); |
| 433 | } | 480 | xsetenv_plain("TCPORIGDSTADDR", addr); |
| 434 | 481 | free(addr); | |
| 435 | if (!(option_mask32 & OPT_E)) { | 482 | } |
| 436 | /* setup ucspi env */ | 483 | xsetenv_plain("PROTO", proto); |
| 437 | const char *proto = tcp ? "TCP" : "UDP"; | 484 | xsetenv_proto(proto, "LOCALADDR", local_addr); |
| 438 | 485 | xsetenv_proto(proto, "REMOTEADDR", remote_addr); | |
| 439 | /* Extract "original" destination addr:port | 486 | if (option_mask32 & OPT_h) { |
| 440 | * from Linux firewall. Useful when you redirect | 487 | xsetenv_proto(proto, "LOCALHOST", local_hostname); |
| 441 | * an outbond connection to local handler, and it needs | 488 | xsetenv_proto(proto, "REMOTEHOST", remote_hostname); |
| 442 | * to know where it originally tried to connect */ | 489 | } |
| 443 | if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &lsa->u.sa, &lsa->len) == 0) { | 490 | //compat? xsetenv_proto(proto, "REMOTEINFO", ""); |
| 444 | char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa); | 491 | /* additional */ |
| 445 | xsetenv("TCPORIGDSTADDR", addr); | 492 | if (cur_per_host > 0) /* can not be true for udp */ |
| 446 | free(addr); | 493 | xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host)); |
| 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 | } | 494 | } |
| 455 | xsetenv_proto(proto, "REMOTEINFO", ""); | 495 | free(local_addr); |
| 456 | /* additional */ | 496 | free(free_me0); |
| 457 | if (cur_per_host > 0) /* can not be true for udp */ | 497 | free(free_me1); |
| 458 | xsetenv("TCPCONCURRENCY", utoa(cur_per_host)); | 498 | free(free_me2); |
| 459 | } | 499 | } |
| 460 | 500 | ||
| 461 | dup2(0, 1); | 501 | xdup2(0, 1); |
| 462 | 502 | ||
| 463 | signal(SIGTERM, SIG_DFL); | 503 | signal(SIGTERM, SIG_DFL); |
| 464 | signal(SIGPIPE, SIG_DFL); | 504 | signal(SIGPIPE, SIG_DFL); |
| 465 | signal(SIGCHLD, SIG_DFL); | 505 | signal(SIGCHLD, SIG_DFL); |
| 466 | sig_unblock(SIGCHLD); | 506 | sig_unblock(SIGCHLD); |
| 467 | 507 | ||
| 468 | argv += 2; | ||
| 469 | #ifdef SSLSVD | 508 | #ifdef SSLSVD |
| 470 | strcpy(id, utoa(pid)); | 509 | strcpy(id, utoa(pid)); |
| 471 | ssl_io(0, argv); | 510 | ssl_io(0, argv); |
diff --git a/networking/inetd.c b/networking/inetd.c index 0620188d6..41824dbc8 100644 --- a/networking/inetd.c +++ b/networking/inetd.c | |||
| @@ -130,8 +130,13 @@ | |||
| 130 | * tening service socket, and must accept at least one connection request | 130 | * tening service socket, and must accept at least one connection request |
| 131 | * before exiting. Such a server would normally accept and process incoming | 131 | * before exiting. Such a server would normally accept and process incoming |
| 132 | * connection requests until a timeout. | 132 | * connection requests until a timeout. |
| 133 | * | 133 | */ |
| 134 | * In short: "stream" can be "wait" or "nowait"; "dgram" must be "wait". | 134 | |
| 135 | /* Despite of above doc saying that dgram services must use "wait", | ||
| 136 | * "udp nowait" servers are implemented in busyboxed inetd. | ||
| 137 | * IPv6 addresses are also implemented. However, they may look ugly - | ||
| 138 | * ":::service..." means "address '::' (IPv6 wildcard addr)":"service"... | ||
| 139 | * You have to put "tcp6"/"udp6" in protocol field to select IPv6. | ||
| 135 | */ | 140 | */ |
| 136 | 141 | ||
| 137 | /* Here's the scoop concerning the user[:group] feature: | 142 | /* Here's the scoop concerning the user[:group] feature: |
| @@ -832,9 +837,6 @@ static NOINLINE servtab_t *parse_one_line(void) | |||
| 832 | if (sep->se_socktype == SOCK_DGRAM) { | 837 | if (sep->se_socktype == SOCK_DGRAM) { |
| 833 | if (sep->se_proto_no == IPPROTO_TCP) | 838 | if (sep->se_proto_no == IPPROTO_TCP) |
| 834 | goto parse_err; | 839 | goto parse_err; |
| 835 | /* "udp nowait" is a small fork bomb :) */ | ||
| 836 | if (!sep->se_wait) | ||
| 837 | goto parse_err; | ||
| 838 | } | 840 | } |
| 839 | 841 | ||
| 840 | /* check if the hostname specifier is a comma separated list | 842 | /* check if the hostname specifier is a comma separated list |
| @@ -1195,7 +1197,7 @@ int inetd_main(int argc, char **argv) | |||
| 1195 | 1197 | ||
| 1196 | for (;;) { | 1198 | for (;;) { |
| 1197 | int ready_fd_cnt; | 1199 | int ready_fd_cnt; |
| 1198 | int ctrl, accepted_fd; | 1200 | int ctrl, accepted_fd, new_udp_fd; |
| 1199 | fd_set readable; | 1201 | fd_set readable; |
| 1200 | 1202 | ||
| 1201 | if (maxsock < 0) | 1203 | if (maxsock < 0) |
| @@ -1220,12 +1222,43 @@ int inetd_main(int argc, char **argv) | |||
| 1220 | ready_fd_cnt--; | 1222 | ready_fd_cnt--; |
| 1221 | ctrl = sep->se_fd; | 1223 | ctrl = sep->se_fd; |
| 1222 | accepted_fd = -1; | 1224 | accepted_fd = -1; |
| 1223 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { | 1225 | new_udp_fd = -1; |
| 1224 | ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL); | 1226 | if (!sep->se_wait) { |
| 1225 | if (ctrl < 0) { | 1227 | if (sep->se_socktype == SOCK_STREAM) { |
| 1226 | if (errno != EINTR) | 1228 | ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL); |
| 1227 | bb_perror_msg("accept (for %s)", sep->se_service); | 1229 | if (ctrl < 0) { |
| 1228 | continue; | 1230 | if (errno != EINTR) |
| 1231 | bb_perror_msg("accept (for %s)", sep->se_service); | ||
| 1232 | continue; | ||
| 1233 | } | ||
| 1234 | } | ||
| 1235 | /* "nowait" udp */ | ||
| 1236 | if (sep->se_socktype == SOCK_DGRAM | ||
| 1237 | && sep->se_family != AF_UNIX | ||
| 1238 | ) { | ||
| 1239 | /* How udp "nowait" works: | ||
| 1240 | * child peeks at (received and buffered by kernel) UDP packet, | ||
| 1241 | * performs connect() on the socket so that it is linked only | ||
| 1242 | * to this peer. But this also affects parent, because descriptors | ||
| 1243 | * are shared after fork() a-la dup(). When parent performs | ||
| 1244 | * select(), it will see this descriptor connected to the peer (!) | ||
| 1245 | * and still readable, will act on it and mess things up | ||
| 1246 | * (can create many copies of same child, etc). | ||
| 1247 | * Parent must create and use new socket instead. */ | ||
| 1248 | new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0); | ||
| 1249 | if (new_udp_fd < 0) { /* error: eat packet, forget about it */ | ||
| 1250 | udp_err: | ||
| 1251 | recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT); | ||
| 1252 | continue; | ||
| 1253 | } | ||
| 1254 | setsockopt_reuseaddr(new_udp_fd); | ||
| 1255 | /* TODO: better do bind after vfork in parent, | ||
| 1256 | * so that we don't have two wildcard bound sockets | ||
| 1257 | * even for a brief moment? */ | ||
| 1258 | if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) { | ||
| 1259 | close(new_udp_fd); | ||
| 1260 | goto udp_err; | ||
| 1261 | } | ||
| 1229 | } | 1262 | } |
| 1230 | } | 1263 | } |
| 1231 | 1264 | ||
| @@ -1283,10 +1316,15 @@ int inetd_main(int argc, char **argv) | |||
| 1283 | 1316 | ||
| 1284 | if (pid > 0) { /* parent */ | 1317 | if (pid > 0) { /* parent */ |
| 1285 | if (sep->se_wait) { | 1318 | if (sep->se_wait) { |
| 1319 | /* tcp wait: we passed listening socket to child, | ||
| 1320 | * will wait for child to terminate */ | ||
| 1286 | sep->se_wait = pid; | 1321 | sep->se_wait = pid; |
| 1287 | remove_fd_from_set(sep->se_fd); | 1322 | remove_fd_from_set(sep->se_fd); |
| 1288 | /* we passed listening socket to child, | 1323 | } |
| 1289 | * will wait for child to terminate */ | 1324 | if (new_udp_fd >= 0) { |
| 1325 | /* udp nowait: child connected the socket, | ||
| 1326 | * we created and will use new, unconnected one */ | ||
| 1327 | xmove_fd(new_udp_fd, sep->se_fd); | ||
| 1290 | } | 1328 | } |
| 1291 | restore_sigmask(&omask); | 1329 | restore_sigmask(&omask); |
| 1292 | maybe_close(accepted_fd); | 1330 | maybe_close(accepted_fd); |
| @@ -1313,39 +1351,20 @@ int inetd_main(int argc, char **argv) | |||
| 1313 | #endif | 1351 | #endif |
| 1314 | /* child */ | 1352 | /* child */ |
| 1315 | setsid(); | 1353 | setsid(); |
| 1316 | #if 0 | 1354 | /* "nowait" udp */ |
| 1317 | /* This does not work. | 1355 | if (new_udp_fd >= 0) { |
| 1318 | * Actually, it _almost_ works. The idea behind it is: child | ||
| 1319 | * can peek at (already received and buffered by kernel) UDP packet, | ||
| 1320 | * and perform connect() on the socket so that it is linked only | ||
| 1321 | * to this peer. But this also affects parent, because descriptors | ||
| 1322 | * are shared after fork() a-la dup(). When parent returns to | ||
| 1323 | * select(), it will see this descriptor attached to the peer (!) | ||
| 1324 | * and likely still readable, will act on it and mess things up | ||
| 1325 | * (can create many copies of same child, etc). | ||
| 1326 | * If child will create new socket instead, then bind() and | ||
| 1327 | * connect() it to peer's address, descriptor aliasing problem | ||
| 1328 | * is solved, but first packet cannot be "transferred" to the new | ||
| 1329 | * socket. It is not a problem if child can account for this, | ||
| 1330 | * but our child will exec - and exec'ed program does not know | ||
| 1331 | * about this "lost packet" problem! Pity... */ | ||
| 1332 | /* "nowait" udp[6]. Hmmm... */ | ||
| 1333 | if (!sep->se_wait | ||
| 1334 | && sep->se_socktype == SOCK_DGRAM | ||
| 1335 | && sep->se_family != AF_UNIX | ||
| 1336 | ) { | ||
| 1337 | len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family); | 1356 | len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family); |
| 1338 | /* peek at the packet and remember peer addr */ | 1357 | /* peek at the packet and remember peer addr */ |
| 1339 | int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT, | 1358 | int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT, |
| 1340 | &lsa->u.sa, &lsa->len); | 1359 | &lsa->u.sa, &lsa->len); |
| 1341 | if (r >= 0) | 1360 | if (r < 0) |
| 1342 | /* make this socket "connected" to peer addr: | 1361 | goto do_exit1; |
| 1343 | * only packets from this peer will be recv'ed, | 1362 | /* make this socket "connected" to peer addr: |
| 1344 | * and bare write()/send() will work on it */ | 1363 | * only packets from this peer will be recv'ed, |
| 1345 | connect(ctrl, &lsa->u.sa, lsa->len); | 1364 | * and bare write()/send() will work on it */ |
| 1365 | connect(ctrl, &lsa->u.sa, lsa->len); | ||
| 1346 | free(lsa); | 1366 | free(lsa); |
| 1347 | } | 1367 | } |
| 1348 | #endif | ||
| 1349 | /* prepare env and exec program */ | 1368 | /* prepare env and exec program */ |
| 1350 | pwd = getpwnam(sep->se_user); | 1369 | pwd = getpwnam(sep->se_user); |
| 1351 | if (pwd == NULL) { | 1370 | if (pwd == NULL) { |
