aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO_config_nommu6
-rw-r--r--ipsvd/tcpudp.c231
-rw-r--r--networking/inetd.c101
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#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_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 847CONFIG_TCPSVD=y
848# CONFIG_UDPSVD is not set 848CONFIG_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 */
68static 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
62static void xsetenv_proto(const char *proto, const char *n, const char *v) 75static 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
82static 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
67static void sig_term_handler(int sig) 94static 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
119static void sig_child_handler(int sig) 146static 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) {