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
This commit is contained in:
parent
9f153f610f
commit
aefed941c2
@ -1,7 +1,7 @@
|
||||
#
|
||||
# Automatically generated make config: don't edit
|
||||
# Busybox version: 1.10.0.svn
|
||||
# Thu Mar 6 20:18:05 2008
|
||||
# Sat Mar 8 01:11:42 2008
|
||||
#
|
||||
CONFIG_HAVE_DOT_CONFIG=y
|
||||
|
||||
@ -844,5 +844,5 @@ CONFIG_LPQ=y
|
||||
#
|
||||
# ipsvd utilities
|
||||
#
|
||||
# CONFIG_TCPSVD is not set
|
||||
# CONFIG_UDPSVD is not set
|
||||
CONFIG_TCPSVD=y
|
||||
CONFIG_UDPSVD=y
|
||||
|
231
ipsvd/tcpudp.c
231
ipsvd/tcpudp.c
@ -13,12 +13,12 @@
|
||||
*
|
||||
* Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused.
|
||||
*
|
||||
* Output of verbose mode matches original (modulo bugs and
|
||||
* unimplemented stuff). Unnatural splitting of IP and PORT
|
||||
* is retained (personally I prefer one-value "IP:PORT" notation -
|
||||
* it is a natural string representation of struct sockaddr_XX).
|
||||
* Busybox version exports TCPLOCALADDR instead of
|
||||
* TCPLOCALIP + TCPLOCALPORT pair. ADDR more closely matches reality
|
||||
* (which is "struct sockaddr_XXX". Port is not a separate entity,
|
||||
* it's just a part of (AF_INET[6]) sockaddr!).
|
||||
*
|
||||
* TCPORIGDST{IP,PORT} is busybox-specific addition
|
||||
* TCPORIGDSTADDR is Busybox-specific addition.
|
||||
*
|
||||
* udp server is hacked up by reusing TCP code. It has the following
|
||||
* limitation inherent in Unix DGRAM sockets implementation:
|
||||
@ -46,6 +46,8 @@ struct globals {
|
||||
unsigned cur_per_host;
|
||||
unsigned cnum;
|
||||
unsigned cmax;
|
||||
char **env_cur;
|
||||
char *env_var[1]; /* actually bigger */
|
||||
};
|
||||
#define G (*(struct globals*)&bb_common_bufsiz1)
|
||||
#define verbose (G.verbose )
|
||||
@ -53,21 +55,46 @@ struct globals {
|
||||
#define cur_per_host (G.cur_per_host)
|
||||
#define cnum (G.cnum )
|
||||
#define cmax (G.cmax )
|
||||
#define env_cur (G.env_cur )
|
||||
#define env_var (G.env_var )
|
||||
#define INIT_G() \
|
||||
do { \
|
||||
cmax = 30; \
|
||||
env_cur = &env_var[0]; \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* We have to be careful about leaking memory in repeated setenv's */
|
||||
static void xsetenv_plain(const char *n, const char *v)
|
||||
{
|
||||
char *var = xasprintf("%s=%s", n, v);
|
||||
*env_cur++ = var;
|
||||
putenv(var);
|
||||
}
|
||||
|
||||
static void xsetenv_proto(const char *proto, const char *n, const char *v)
|
||||
{
|
||||
putenv(xasprintf("%s%s=%s", proto, n, v));
|
||||
char *var = xasprintf("%s%s=%s", proto, n, v);
|
||||
*env_cur++ = var;
|
||||
putenv(var);
|
||||
}
|
||||
|
||||
static void undo_xsetenv(void)
|
||||
{
|
||||
char **pp = env_cur = &env_var[0];
|
||||
while (*pp) {
|
||||
char *var = *pp;
|
||||
*strchrnul(var, '=') = '\0';
|
||||
unsetenv(var);
|
||||
free(var);
|
||||
*pp++ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void sig_term_handler(int sig)
|
||||
{
|
||||
if (verbose)
|
||||
printf("%s: info: sigterm received, exit\n", applet_name);
|
||||
bb_error_msg("got signal %u, exit", sig);
|
||||
kill_myself_with_sig(sig);
|
||||
}
|
||||
|
||||
@ -85,7 +112,7 @@ static void print_waitstat(unsigned pid, int wstat)
|
||||
cause = "signal";
|
||||
e = WTERMSIG(wstat);
|
||||
}
|
||||
printf("%s: info: end %d %s %d\n", applet_name, pid, cause, e);
|
||||
bb_error_msg("end %d %s %d", pid, cause, e);
|
||||
}
|
||||
|
||||
/* Must match getopt32 in main! */
|
||||
@ -113,7 +140,7 @@ static void connection_status(void)
|
||||
{
|
||||
/* "only 1 client max" desn't need this */
|
||||
if (cmax > 1)
|
||||
printf("%s: info: status %u/%u\n", applet_name, cnum, cmax);
|
||||
bb_error_msg("status %u/%u", cnum, cmax);
|
||||
}
|
||||
|
||||
static void sig_child_handler(int sig)
|
||||
@ -145,13 +172,11 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
#ifndef SSLSVD
|
||||
struct bb_uidgid_t ugid;
|
||||
#endif
|
||||
bool need_hostnames, need_remote_ip, tcp;
|
||||
bool tcp;
|
||||
uint16_t local_port;
|
||||
char *local_hostname = NULL;
|
||||
char *remote_hostname = (char*)""; /* "" used if no -h */
|
||||
char *local_addr = local_addr; /* gcc */
|
||||
char *remote_addr = remote_addr; /* gcc */
|
||||
char *remote_ip = remote_addr; /* gcc */
|
||||
char *preset_local_hostname = NULL;
|
||||
char *remote_hostname = remote_hostname; /* for compiler */
|
||||
char *remote_addr = remote_addr; /* for compiler */
|
||||
len_and_sockaddr *lsa;
|
||||
len_and_sockaddr local, remote;
|
||||
socklen_t sa_len;
|
||||
@ -168,12 +193,12 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
opt_complementary = "-3:i--i:ph:vv";
|
||||
#ifdef SSLSVD
|
||||
getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
|
||||
&str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
|
||||
&str_c, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
|
||||
&str_b, &str_t, &ssluser, &root, &cert, &key, &verbose
|
||||
);
|
||||
#else
|
||||
getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v",
|
||||
&str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
|
||||
&str_c, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
|
||||
&str_b, &str_t, &verbose
|
||||
);
|
||||
#endif
|
||||
@ -210,26 +235,21 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
if (!tcp)
|
||||
max_per_host = 0;
|
||||
|
||||
/* stdout is used for logging, don't buffer */
|
||||
setlinebuf(stdout);
|
||||
bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */
|
||||
|
||||
need_hostnames = verbose || !(option_mask32 & OPT_E);
|
||||
need_remote_ip = max_per_host || need_hostnames;
|
||||
|
||||
#ifdef SSLSVD
|
||||
sslser = user;
|
||||
client = 0;
|
||||
if ((getuid() == 0) && !(option_mask32 & OPT_u)) {
|
||||
xfunc_exitcode = 100;
|
||||
bb_error_msg_and_die("fatal: -U ssluser must be set when running as root");
|
||||
bb_error_msg_and_die("-U ssluser must be set when running as root");
|
||||
}
|
||||
if (option_mask32 & OPT_u)
|
||||
if (!uidgid_get(&sslugid, ssluser, 1)) {
|
||||
if (errno) {
|
||||
bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser);
|
||||
}
|
||||
bb_error_msg_and_die("fatal: unknown user/group '%s'", ssluser);
|
||||
bb_error_msg_and_die("unknown user/group '%s'", ssluser);
|
||||
}
|
||||
if (!cert) cert = "./cert.pem";
|
||||
if (!key) key = cert;
|
||||
@ -246,7 +266,7 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
|
||||
sig_block(SIGCHLD);
|
||||
signal(SIGCHLD, sig_child_handler);
|
||||
signal(SIGTERM, sig_term_handler);
|
||||
bb_signals(BB_SIGS_FATAL, sig_term_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
if (max_per_host)
|
||||
@ -254,6 +274,8 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
|
||||
local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
|
||||
lsa = xhost2sockaddr(argv[0], local_port);
|
||||
argv += 2;
|
||||
|
||||
sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
|
||||
setsockopt_reuseaddr(sock);
|
||||
sa_len = lsa->len; /* I presume sockaddr len stays the same */
|
||||
@ -274,14 +296,13 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
|
||||
if (verbose) {
|
||||
char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
|
||||
printf("%s: info: listening on %s", applet_name, addr);
|
||||
bb_error_msg("listening on %s, starting", addr);
|
||||
free(addr);
|
||||
#ifndef SSLSVD
|
||||
if (option_mask32 & OPT_u)
|
||||
printf(", uid %u, gid %u",
|
||||
(unsigned)ugid.uid, (unsigned)ugid.gid);
|
||||
#endif
|
||||
puts(", starting");
|
||||
}
|
||||
|
||||
/* Main accept() loop */
|
||||
@ -297,14 +318,15 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
close(0);
|
||||
again2:
|
||||
sig_unblock(SIGCHLD);
|
||||
local.len = remote.len = sa_len;
|
||||
if (tcp) {
|
||||
remote.len = sa_len;
|
||||
conn = accept(sock, &remote.u.sa, &remote.len);
|
||||
} else {
|
||||
/* In case recv_from_to won't be able to recover local addr.
|
||||
* Also sets port - recv_from_to is unable to do it. */
|
||||
local = *lsa;
|
||||
conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.u.sa, &local.u.sa, sa_len);
|
||||
conn = recv_from_to(sock, NULL, 0, MSG_DONTWAIT | MSG_PEEK,
|
||||
&remote.u.sa, &local.u.sa, sa_len);
|
||||
}
|
||||
sig_block(SIGCHLD);
|
||||
if (conn < 0) {
|
||||
@ -317,19 +339,19 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
if (max_per_host) {
|
||||
/* Drop connection immediately if cur_per_host > max_per_host
|
||||
* (minimizing load under SYN flood) */
|
||||
remote_ip = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
|
||||
cur_per_host = ipsvd_perhost_add(remote_ip, max_per_host, &hccp);
|
||||
remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
|
||||
cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp);
|
||||
if (cur_per_host > max_per_host) {
|
||||
/* ipsvd_perhost_add detected that max is exceeded
|
||||
* (and did not store ip in connection table) */
|
||||
free(remote_ip);
|
||||
free(remote_addr);
|
||||
if (msg_per_host) {
|
||||
/* don't block or test for errors */
|
||||
ndelay_on(0);
|
||||
write(0, msg_per_host, len_per_host);
|
||||
send(0, msg_per_host, len_per_host, MSG_DONTWAIT);
|
||||
}
|
||||
goto again1;
|
||||
}
|
||||
/* NB: remote_addr is not leaked, it is stored in conn table */
|
||||
}
|
||||
|
||||
if (!tcp) {
|
||||
@ -372,19 +394,21 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
#endif
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
pid = vfork();
|
||||
if (pid == -1) {
|
||||
bb_perror_msg("fork");
|
||||
bb_perror_msg("vfork");
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (pid != 0) {
|
||||
/* parent */
|
||||
/* Parent */
|
||||
cnum++;
|
||||
if (verbose)
|
||||
connection_status();
|
||||
if (hccp)
|
||||
hccp->pid = pid;
|
||||
/* clean up changes done by vforked child */
|
||||
undo_xsetenv();
|
||||
goto again;
|
||||
}
|
||||
|
||||
@ -394,78 +418,93 @@ int tcpudpsvd_main(int argc, char **argv)
|
||||
if (tcp)
|
||||
close(sock);
|
||||
|
||||
if (need_remote_ip)
|
||||
remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
|
||||
{ /* vfork alert! every xmalloc in this block should be freed! */
|
||||
char *local_hostname = local_hostname; /* for compiler */
|
||||
char *local_addr = NULL;
|
||||
char *free_me0 = NULL;
|
||||
char *free_me1 = NULL;
|
||||
char *free_me2 = NULL;
|
||||
|
||||
if (need_hostnames) {
|
||||
if (option_mask32 & OPT_h) {
|
||||
remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
|
||||
if (!remote_hostname) {
|
||||
bb_error_msg("warning: cannot look up hostname for %s", remote_addr);
|
||||
remote_hostname = (char*)"";
|
||||
if (verbose || !(option_mask32 & OPT_E)) {
|
||||
if (!max_per_host) /* remote_addr is not yet known */
|
||||
free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
|
||||
if (option_mask32 & OPT_h) {
|
||||
free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
|
||||
if (!remote_hostname) {
|
||||
bb_error_msg("cannot look up hostname for %s", remote_addr);
|
||||
remote_hostname = remote_addr;
|
||||
}
|
||||
}
|
||||
/* Find out local IP peer connected to.
|
||||
* Errors ignored (I'm not paranoid enough to imagine kernel
|
||||
* which doesn't know local IP). */
|
||||
if (tcp)
|
||||
getsockname(0, &local.u.sa, &local.len);
|
||||
/* else: for UDP it is done earlier by parent */
|
||||
local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
|
||||
if (option_mask32 & OPT_h) {
|
||||
local_hostname = preset_local_hostname;
|
||||
if (!local_hostname) {
|
||||
free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
|
||||
if (!local_hostname)
|
||||
bb_error_msg_and_die("cannot look up hostname for %s", local_addr);
|
||||
}
|
||||
/* else: local_hostname is not NULL, but is NOT malloced! */
|
||||
}
|
||||
}
|
||||
/* Find out local IP peer connected to.
|
||||
* Errors ignored (I'm not paranoid enough to imagine kernel
|
||||
* which doesn't know local IP). */
|
||||
if (tcp) {
|
||||
local.len = sa_len;
|
||||
getsockname(0, &local.u.sa, &local.len);
|
||||
if (verbose) {
|
||||
pid = getpid();
|
||||
if (max_per_host) {
|
||||
bb_error_msg("concurrency %s %u/%u",
|
||||
remote_addr,
|
||||
cur_per_host, max_per_host);
|
||||
}
|
||||
bb_error_msg((option_mask32 & OPT_h)
|
||||
? "start %u %s-%s (%s-%s)"
|
||||
: "start %u %s-%s",
|
||||
pid,
|
||||
local_addr, remote_addr,
|
||||
local_hostname, remote_hostname);
|
||||
}
|
||||
local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
|
||||
if (!local_hostname) {
|
||||
local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
|
||||
if (!local_hostname)
|
||||
bb_error_msg_and_die("warning: cannot look up hostname for %s"+9, local_addr);
|
||||
|
||||
if (!(option_mask32 & OPT_E)) {
|
||||
/* setup ucspi env */
|
||||
const char *proto = tcp ? "TCP" : "UDP";
|
||||
|
||||
/* Extract "original" destination addr:port
|
||||
* from Linux firewall. Useful when you redirect
|
||||
* an outbond connection to local handler, and it needs
|
||||
* to know where it originally tried to connect */
|
||||
if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) {
|
||||
char *addr = xmalloc_sockaddr2dotted(&local.u.sa);
|
||||
xsetenv_plain("TCPORIGDSTADDR", addr);
|
||||
free(addr);
|
||||
}
|
||||
xsetenv_plain("PROTO", proto);
|
||||
xsetenv_proto(proto, "LOCALADDR", local_addr);
|
||||
xsetenv_proto(proto, "REMOTEADDR", remote_addr);
|
||||
if (option_mask32 & OPT_h) {
|
||||
xsetenv_proto(proto, "LOCALHOST", local_hostname);
|
||||
xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
|
||||
}
|
||||
//compat? xsetenv_proto(proto, "REMOTEINFO", "");
|
||||
/* additional */
|
||||
if (cur_per_host > 0) /* can not be true for udp */
|
||||
xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host));
|
||||
}
|
||||
free(local_addr);
|
||||
free(free_me0);
|
||||
free(free_me1);
|
||||
free(free_me2);
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
pid = getpid();
|
||||
printf("%s: info: pid %u from %s\n", applet_name, pid, remote_addr);
|
||||
if (max_per_host)
|
||||
printf("%s: info: concurrency %u %s %u/%u\n",
|
||||
applet_name, pid, remote_ip, cur_per_host, max_per_host);
|
||||
printf("%s: info: start %u %s:%s :%s:%s\n",
|
||||
applet_name, pid,
|
||||
local_hostname, local_addr,
|
||||
remote_hostname, remote_addr);
|
||||
}
|
||||
|
||||
if (!(option_mask32 & OPT_E)) {
|
||||
/* setup ucspi env */
|
||||
const char *proto = tcp ? "TCP" : "UDP";
|
||||
|
||||
/* Extract "original" destination addr:port
|
||||
* from Linux firewall. Useful when you redirect
|
||||
* an outbond connection to local handler, and it needs
|
||||
* to know where it originally tried to connect */
|
||||
if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &lsa->u.sa, &lsa->len) == 0) {
|
||||
char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
|
||||
xsetenv("TCPORIGDSTADDR", addr);
|
||||
free(addr);
|
||||
}
|
||||
xsetenv("PROTO", proto);
|
||||
xsetenv_proto(proto, "LOCALADDR", local_addr);
|
||||
xsetenv_proto(proto, "LOCALHOST", local_hostname);
|
||||
xsetenv_proto(proto, "REMOTEADDR", remote_addr);
|
||||
if (option_mask32 & OPT_h) {
|
||||
xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
|
||||
}
|
||||
xsetenv_proto(proto, "REMOTEINFO", "");
|
||||
/* additional */
|
||||
if (cur_per_host > 0) /* can not be true for udp */
|
||||
xsetenv("TCPCONCURRENCY", utoa(cur_per_host));
|
||||
}
|
||||
|
||||
dup2(0, 1);
|
||||
xdup2(0, 1);
|
||||
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
sig_unblock(SIGCHLD);
|
||||
|
||||
argv += 2;
|
||||
#ifdef SSLSVD
|
||||
strcpy(id, utoa(pid));
|
||||
ssl_io(0, argv);
|
||||
|
@ -130,8 +130,13 @@
|
||||
* tening service socket, and must accept at least one connection request
|
||||
* before exiting. Such a server would normally accept and process incoming
|
||||
* connection requests until a timeout.
|
||||
*
|
||||
* In short: "stream" can be "wait" or "nowait"; "dgram" must be "wait".
|
||||
*/
|
||||
|
||||
/* Despite of above doc saying that dgram services must use "wait",
|
||||
* "udp nowait" servers are implemented in busyboxed inetd.
|
||||
* IPv6 addresses are also implemented. However, they may look ugly -
|
||||
* ":::service..." means "address '::' (IPv6 wildcard addr)":"service"...
|
||||
* You have to put "tcp6"/"udp6" in protocol field to select IPv6.
|
||||
*/
|
||||
|
||||
/* Here's the scoop concerning the user[:group] feature:
|
||||
@ -832,9 +837,6 @@ static NOINLINE servtab_t *parse_one_line(void)
|
||||
if (sep->se_socktype == SOCK_DGRAM) {
|
||||
if (sep->se_proto_no == IPPROTO_TCP)
|
||||
goto parse_err;
|
||||
/* "udp nowait" is a small fork bomb :) */
|
||||
if (!sep->se_wait)
|
||||
goto parse_err;
|
||||
}
|
||||
|
||||
/* check if the hostname specifier is a comma separated list
|
||||
@ -1195,7 +1197,7 @@ int inetd_main(int argc, char **argv)
|
||||
|
||||
for (;;) {
|
||||
int ready_fd_cnt;
|
||||
int ctrl, accepted_fd;
|
||||
int ctrl, accepted_fd, new_udp_fd;
|
||||
fd_set readable;
|
||||
|
||||
if (maxsock < 0)
|
||||
@ -1220,12 +1222,43 @@ int inetd_main(int argc, char **argv)
|
||||
ready_fd_cnt--;
|
||||
ctrl = sep->se_fd;
|
||||
accepted_fd = -1;
|
||||
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
|
||||
ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
|
||||
if (ctrl < 0) {
|
||||
if (errno != EINTR)
|
||||
bb_perror_msg("accept (for %s)", sep->se_service);
|
||||
continue;
|
||||
new_udp_fd = -1;
|
||||
if (!sep->se_wait) {
|
||||
if (sep->se_socktype == SOCK_STREAM) {
|
||||
ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
|
||||
if (ctrl < 0) {
|
||||
if (errno != EINTR)
|
||||
bb_perror_msg("accept (for %s)", sep->se_service);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* "nowait" udp */
|
||||
if (sep->se_socktype == SOCK_DGRAM
|
||||
&& sep->se_family != AF_UNIX
|
||||
) {
|
||||
/* How udp "nowait" works:
|
||||
* child peeks at (received and buffered by kernel) UDP packet,
|
||||
* performs connect() on the socket so that it is linked only
|
||||
* to this peer. But this also affects parent, because descriptors
|
||||
* are shared after fork() a-la dup(). When parent performs
|
||||
* select(), it will see this descriptor connected to the peer (!)
|
||||
* and still readable, will act on it and mess things up
|
||||
* (can create many copies of same child, etc).
|
||||
* Parent must create and use new socket instead. */
|
||||
new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0);
|
||||
if (new_udp_fd < 0) { /* error: eat packet, forget about it */
|
||||
udp_err:
|
||||
recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT);
|
||||
continue;
|
||||
}
|
||||
setsockopt_reuseaddr(new_udp_fd);
|
||||
/* TODO: better do bind after vfork in parent,
|
||||
* so that we don't have two wildcard bound sockets
|
||||
* even for a brief moment? */
|
||||
if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) {
|
||||
close(new_udp_fd);
|
||||
goto udp_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1283,10 +1316,15 @@ int inetd_main(int argc, char **argv)
|
||||
|
||||
if (pid > 0) { /* parent */
|
||||
if (sep->se_wait) {
|
||||
/* tcp wait: we passed listening socket to child,
|
||||
* will wait for child to terminate */
|
||||
sep->se_wait = pid;
|
||||
remove_fd_from_set(sep->se_fd);
|
||||
/* we passed listening socket to child,
|
||||
* will wait for child to terminate */
|
||||
}
|
||||
if (new_udp_fd >= 0) {
|
||||
/* udp nowait: child connected the socket,
|
||||
* we created and will use new, unconnected one */
|
||||
xmove_fd(new_udp_fd, sep->se_fd);
|
||||
}
|
||||
restore_sigmask(&omask);
|
||||
maybe_close(accepted_fd);
|
||||
@ -1313,39 +1351,20 @@ int inetd_main(int argc, char **argv)
|
||||
#endif
|
||||
/* child */
|
||||
setsid();
|
||||
#if 0
|
||||
/* This does not work.
|
||||
* Actually, it _almost_ works. The idea behind it is: child
|
||||
* can peek at (already received and buffered by kernel) UDP packet,
|
||||
* and perform connect() on the socket so that it is linked only
|
||||
* to this peer. But this also affects parent, because descriptors
|
||||
* are shared after fork() a-la dup(). When parent returns to
|
||||
* select(), it will see this descriptor attached to the peer (!)
|
||||
* and likely still readable, will act on it and mess things up
|
||||
* (can create many copies of same child, etc).
|
||||
* If child will create new socket instead, then bind() and
|
||||
* connect() it to peer's address, descriptor aliasing problem
|
||||
* is solved, but first packet cannot be "transferred" to the new
|
||||
* socket. It is not a problem if child can account for this,
|
||||
* but our child will exec - and exec'ed program does not know
|
||||
* about this "lost packet" problem! Pity... */
|
||||
/* "nowait" udp[6]. Hmmm... */
|
||||
if (!sep->se_wait
|
||||
&& sep->se_socktype == SOCK_DGRAM
|
||||
&& sep->se_family != AF_UNIX
|
||||
) {
|
||||
/* "nowait" udp */
|
||||
if (new_udp_fd >= 0) {
|
||||
len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family);
|
||||
/* peek at the packet and remember peer addr */
|
||||
int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
|
||||
&lsa->u.sa, &lsa->len);
|
||||
if (r >= 0)
|
||||
/* make this socket "connected" to peer addr:
|
||||
* only packets from this peer will be recv'ed,
|
||||
* and bare write()/send() will work on it */
|
||||
connect(ctrl, &lsa->u.sa, lsa->len);
|
||||
if (r < 0)
|
||||
goto do_exit1;
|
||||
/* make this socket "connected" to peer addr:
|
||||
* only packets from this peer will be recv'ed,
|
||||
* and bare write()/send() will work on it */
|
||||
connect(ctrl, &lsa->u.sa, lsa->len);
|
||||
free(lsa);
|
||||
}
|
||||
#endif
|
||||
/* prepare env and exec program */
|
||||
pwd = getpwnam(sep->se_user);
|
||||
if (pwd == NULL) {
|
||||
|
Loading…
Reference in New Issue
Block a user