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 /networking | |
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
Diffstat (limited to 'networking')
-rw-r--r-- | networking/inetd.c | 101 |
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 @@ | |||
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) { |