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) { |