aboutsummaryrefslogtreecommitdiff
path: root/networking/inetd.c
diff options
context:
space:
mode:
Diffstat (limited to 'networking/inetd.c')
-rw-r--r--networking/inetd.c101
1 files changed, 60 insertions, 41 deletions
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 @@
* 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) {