diff options
| -rw-r--r-- | include/libbb.h | 2 | ||||
| -rw-r--r-- | include/usage.h | 22 | ||||
| -rw-r--r-- | libbb/xfuncs.c | 24 | ||||
| -rw-r--r-- | networking/Kbuild | 3 | ||||
| -rw-r--r-- | networking/inetd.c | 15 | ||||
| -rw-r--r-- | networking/isrv.c | 337 | ||||
| -rw-r--r-- | networking/isrv_identd.c | 144 |
7 files changed, 523 insertions, 24 deletions
diff --git a/include/libbb.h b/include/libbb.h index 4060498b8..c191dc2a0 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
| @@ -278,6 +278,8 @@ extern int wait4pid(int pid); | |||
| 278 | extern void xsetgid(gid_t gid); | 278 | extern void xsetgid(gid_t gid); |
| 279 | extern void xsetuid(uid_t uid); | 279 | extern void xsetuid(uid_t uid); |
| 280 | extern void xdaemon(int nochdir, int noclose); | 280 | extern void xdaemon(int nochdir, int noclose); |
| 281 | /* More clever/thorough xdaemon */ | ||
| 282 | extern void bb_sanitize_stdio(int daemonize); | ||
| 281 | extern void xchdir(const char *path); | 283 | extern void xchdir(const char *path); |
| 282 | extern void xsetenv(const char *key, const char *value); | 284 | extern void xsetenv(const char *key, const char *value); |
| 283 | extern int xopen(const char *pathname, int flags); | 285 | extern int xopen(const char *pathname, int flags); |
diff --git a/include/usage.h b/include/usage.h index 0275df3f0..2b51fad72 100644 --- a/include/usage.h +++ b/include/usage.h | |||
| @@ -826,22 +826,16 @@ | |||
| 826 | "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" \ | 826 | "\\( and \\) or null; if \\( and \\) are not used, they return the number\n" \ |
| 827 | "of characters matched or 0." | 827 | "of characters matched or 0." |
| 828 | 828 | ||
| 829 | #if 0 /* bloaty */ | ||
| 830 | #define fakeidentd_trivial_usage \ | 829 | #define fakeidentd_trivial_usage \ |
| 831 | "[-b ip] [STRING]" | 830 | "[-fiw] [-b ADDR] [STRING]" |
| 832 | #define fakeidentd_full_usage \ | 831 | #define fakeidentd_full_usage \ |
| 833 | "Return a set string to auth requests" \ | 832 | "Provide fake ident (auth) service" \ |
| 834 | "\n\nOptions:\n" \ | 833 | "\n\nOptions:" \ |
| 835 | " -b Bind to ip address\n" \ | 834 | "\n -f Run in foreground" \ |
| 836 | " STRING The ident answer string (default is nobody)" | 835 | "\n -i Inetd mode" \ |
| 837 | #else /* inetd-only */ | 836 | "\n -w Inetd 'wait' mode" \ |
| 838 | #define fakeidentd_trivial_usage \ | 837 | "\n -b ADDR Bind to specified address" \ |
| 839 | "[username]" | 838 | "\n STRING Ident answer string (default is 'nobody')" |
| 840 | #define fakeidentd_full_usage \ | ||
| 841 | "Return a (faked) ident response.\n" \ | ||
| 842 | "This applet is meant to run from inetd.\n" \ | ||
| 843 | "Optional argument is the username to return (default is 'nobody')." | ||
| 844 | #endif | ||
| 845 | 839 | ||
| 846 | #define false_trivial_usage \ | 840 | #define false_trivial_usage \ |
| 847 | "" | 841 | "" |
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index 207537929..6a6bdced3 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c | |||
| @@ -509,6 +509,30 @@ void xdaemon(int nochdir, int noclose) | |||
| 509 | } | 509 | } |
| 510 | #endif | 510 | #endif |
| 511 | 511 | ||
| 512 | void bb_sanitize_stdio(int daemonize) | ||
| 513 | { | ||
| 514 | int fd; | ||
| 515 | /* Mega-paranoid */ | ||
| 516 | fd = xopen(bb_dev_null, O_RDWR); | ||
| 517 | while (fd < 2) | ||
| 518 | fd = dup(fd); /* have 0,1,2 open at least to /dev/null */ | ||
| 519 | if (daemonize) { | ||
| 520 | pid_t pid = fork(); | ||
| 521 | if (pid < 0) /* wtf? */ | ||
| 522 | bb_perror_msg_and_die("fork"); | ||
| 523 | if (pid) /* parent */ | ||
| 524 | exit(0); | ||
| 525 | /* child */ | ||
| 526 | setsid(); | ||
| 527 | /* if daemonizing, make sure we detach from stdio */ | ||
| 528 | dup2(fd, 0); | ||
| 529 | dup2(fd, 1); | ||
| 530 | dup2(fd, 2); | ||
| 531 | } | ||
| 532 | while (fd > 2) | ||
| 533 | close(fd--); /* close everything after fd#2 */ | ||
| 534 | } | ||
| 535 | |||
| 512 | // Die with an error message if we can't open a new socket. | 536 | // Die with an error message if we can't open a new socket. |
| 513 | int xsocket(int domain, int type, int protocol) | 537 | int xsocket(int domain, int type, int protocol) |
| 514 | { | 538 | { |
diff --git a/networking/Kbuild b/networking/Kbuild index 4c29e45a8..bb024c9b7 100644 --- a/networking/Kbuild +++ b/networking/Kbuild | |||
| @@ -9,7 +9,8 @@ lib-$(CONFIG_ARP) += arp.o interface.o | |||
| 9 | lib-$(CONFIG_ARPING) += arping.o | 9 | lib-$(CONFIG_ARPING) += arping.o |
| 10 | lib-$(CONFIG_DNSD) += dnsd.o | 10 | lib-$(CONFIG_DNSD) += dnsd.o |
| 11 | lib-$(CONFIG_ETHER_WAKE) += ether-wake.o | 11 | lib-$(CONFIG_ETHER_WAKE) += ether-wake.o |
| 12 | lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o | 12 | #lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o |
| 13 | lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o | ||
| 13 | lib-$(CONFIG_FTPGET) += ftpgetput.o | 14 | lib-$(CONFIG_FTPGET) += ftpgetput.o |
| 14 | lib-$(CONFIG_FTPPUT) += ftpgetput.o | 15 | lib-$(CONFIG_FTPPUT) += ftpgetput.o |
| 15 | lib-$(CONFIG_HOSTNAME) += hostname.o | 16 | lib-$(CONFIG_HOSTNAME) += hostname.o |
diff --git a/networking/inetd.c b/networking/inetd.c index 93c16bf60..f9f3b51b6 100644 --- a/networking/inetd.c +++ b/networking/inetd.c | |||
| @@ -1289,31 +1289,28 @@ inetd_main(int argc, char *argv[]) | |||
| 1289 | if (CONFIG == NULL) | 1289 | if (CONFIG == NULL) |
| 1290 | bb_error_msg_and_die("non-root must specify a config file"); | 1290 | bb_error_msg_and_die("non-root must specify a config file"); |
| 1291 | 1291 | ||
| 1292 | if (!(opt & 2)) { | ||
| 1293 | #ifdef BB_NOMMU | 1292 | #ifdef BB_NOMMU |
| 1293 | if (!(opt & 2)) { | ||
| 1294 | /* reexec for vfork() do continue parent */ | 1294 | /* reexec for vfork() do continue parent */ |
| 1295 | vfork_daemon_rexec(0, 0, argc, argv, "-f"); | 1295 | vfork_daemon_rexec(0, 0, argc, argv, "-f"); |
| 1296 | } | ||
| 1297 | bb_sanitize_stdio(0); | ||
| 1296 | #else | 1298 | #else |
| 1297 | xdaemon(0, 0); | 1299 | bb_sanitize_stdio(!(opt & 2)); |
| 1298 | #endif | 1300 | #endif |
| 1299 | } else { | ||
| 1300 | setsid(); | ||
| 1301 | } | ||
| 1302 | logmode = LOGMODE_SYSLOG; | 1301 | logmode = LOGMODE_SYSLOG; |
| 1303 | 1302 | ||
| 1304 | if (uid == 0) { | 1303 | if (uid == 0) { |
| 1305 | gid_t gid = getgid(); | ||
| 1306 | |||
| 1307 | /* If run by hand, ensure groups vector gets trashed */ | 1304 | /* If run by hand, ensure groups vector gets trashed */ |
| 1305 | gid_t gid = getgid(); | ||
| 1308 | setgroups(1, &gid); | 1306 | setgroups(1, &gid); |
| 1309 | } | 1307 | } |
| 1310 | 1308 | ||
| 1311 | { | 1309 | { |
| 1312 | FILE *fp = fopen(_PATH_INETDPID, "w"); | 1310 | FILE *fp = fopen(_PATH_INETDPID, "w"); |
| 1313 | |||
| 1314 | if (fp != NULL) { | 1311 | if (fp != NULL) { |
| 1315 | fprintf(fp, "%u\n", getpid()); | 1312 | fprintf(fp, "%u\n", getpid()); |
| 1316 | (void) fclose(fp); | 1313 | fclose(fp); |
| 1317 | } | 1314 | } |
| 1318 | } | 1315 | } |
| 1319 | 1316 | ||
diff --git a/networking/isrv.c b/networking/isrv.c new file mode 100644 index 000000000..02ca1d787 --- /dev/null +++ b/networking/isrv.c | |||
| @@ -0,0 +1,337 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Generic non-forking server infrastructure. | ||
| 4 | * Intended to make writing telnetd-type servers easier. | ||
| 5 | * | ||
| 6 | * Copyright (C) 2007 Denis Vlasenko | ||
| 7 | * | ||
| 8 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include "busybox.h" | ||
| 12 | #include "isrv.h" | ||
| 13 | |||
| 14 | #define DEBUG 0 | ||
| 15 | |||
| 16 | #if DEBUG | ||
| 17 | #define DPRINTF(args...) bb_error_msg(args) | ||
| 18 | #else | ||
| 19 | #define DPRINTF(args...) ((void)0) | ||
| 20 | #endif | ||
| 21 | |||
| 22 | /* Helpers */ | ||
| 23 | |||
| 24 | #if 0 /*def _POSIX_MONOTONIC_CLOCK*/ | ||
| 25 | static time_t monotonic_time(void) | ||
| 26 | { | ||
| 27 | struct timespec ts; | ||
| 28 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) | ||
| 29 | time(&ts.tv_sec); | ||
| 30 | return ts.tv_sec; | ||
| 31 | } | ||
| 32 | #else | ||
| 33 | #define monotonic_time() (time(NULL)) | ||
| 34 | #endif | ||
| 35 | |||
| 36 | /* Opaque structure */ | ||
| 37 | |||
| 38 | struct isrv_state_t { | ||
| 39 | short *fd2peer; /* one per registered fd */ | ||
| 40 | void **param_tbl; /* one per registered peer */ | ||
| 41 | /* one per registered peer; doesn't exist if !timeout */ | ||
| 42 | time_t *timeo_tbl; | ||
| 43 | int (*new_peer)(isrv_state_t *state, int fd); | ||
| 44 | time_t curtime; | ||
| 45 | int timeout; | ||
| 46 | int fd_count; | ||
| 47 | int peer_count; | ||
| 48 | int wr_count; | ||
| 49 | fd_set rd; | ||
| 50 | fd_set wr; | ||
| 51 | }; | ||
| 52 | #define FD2PEER (state->fd2peer) | ||
| 53 | #define PARAM_TBL (state->param_tbl) | ||
| 54 | #define TIMEO_TBL (state->timeo_tbl) | ||
| 55 | #define CURTIME (state->curtime) | ||
| 56 | #define TIMEOUT (state->timeout) | ||
| 57 | #define FD_COUNT (state->fd_count) | ||
| 58 | #define PEER_COUNT (state->peer_count) | ||
| 59 | #define WR_COUNT (state->wr_count) | ||
| 60 | |||
| 61 | /* callback */ | ||
| 62 | void isrv_want_rd(isrv_state_t *state, int fd) | ||
| 63 | { | ||
| 64 | FD_SET(fd, &state->rd); | ||
| 65 | } | ||
| 66 | |||
| 67 | /* callback */ | ||
| 68 | void isrv_want_wr(isrv_state_t *state, int fd) | ||
| 69 | { | ||
| 70 | if (!FD_ISSET(fd, &state->wr)) { | ||
| 71 | WR_COUNT++; | ||
| 72 | FD_SET(fd, &state->wr); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /* callback */ | ||
| 77 | void isrv_dont_want_rd(isrv_state_t *state, int fd) | ||
| 78 | { | ||
| 79 | FD_CLR(fd, &state->rd); | ||
| 80 | } | ||
| 81 | |||
| 82 | /* callback */ | ||
| 83 | void isrv_dont_want_wr(isrv_state_t *state, int fd) | ||
| 84 | { | ||
| 85 | if (FD_ISSET(fd, &state->wr)) { | ||
| 86 | WR_COUNT--; | ||
| 87 | FD_CLR(fd, &state->wr); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | /* callback */ | ||
| 92 | int isrv_register_fd(isrv_state_t *state, int peer, int fd) | ||
| 93 | { | ||
| 94 | int n; | ||
| 95 | |||
| 96 | DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd); | ||
| 97 | |||
| 98 | if (FD_COUNT >= FD_SETSIZE) return -1; | ||
| 99 | if (FD_COUNT <= fd) { | ||
| 100 | n = FD_COUNT; | ||
| 101 | FD_COUNT = fd + 1; | ||
| 102 | |||
| 103 | DPRINTF("register_fd: FD_COUNT %d", FD_COUNT); | ||
| 104 | |||
| 105 | FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0])); | ||
| 106 | while (n < fd) FD2PEER[n++] = -1; | ||
| 107 | } | ||
| 108 | |||
| 109 | DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer); | ||
| 110 | |||
| 111 | FD2PEER[fd] = peer; | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | /* callback */ | ||
| 116 | void isrv_close_fd(isrv_state_t *state, int fd) | ||
| 117 | { | ||
| 118 | DPRINTF("close_fd(%d)", fd); | ||
| 119 | |||
| 120 | close(fd); | ||
| 121 | isrv_dont_want_rd(state, fd); | ||
| 122 | if (WR_COUNT) isrv_dont_want_wr(state, fd); | ||
| 123 | |||
| 124 | FD2PEER[fd] = -1; | ||
| 125 | if (fd == FD_COUNT-1) { | ||
| 126 | do fd--; while (fd >= 0 && FD2PEER[fd] == -1); | ||
| 127 | FD_COUNT = fd + 1; | ||
| 128 | |||
| 129 | DPRINTF("close_fd: FD_COUNT %d", FD_COUNT); | ||
| 130 | |||
| 131 | FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0])); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 135 | /* callback */ | ||
| 136 | int isrv_register_peer(isrv_state_t *state, void *param) | ||
| 137 | { | ||
| 138 | int n; | ||
| 139 | |||
| 140 | if (PEER_COUNT >= FD_SETSIZE) return -1; | ||
| 141 | n = PEER_COUNT++; | ||
| 142 | |||
| 143 | DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT); | ||
| 144 | |||
| 145 | PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0])); | ||
| 146 | PARAM_TBL[n] = param; | ||
| 147 | if (TIMEOUT) { | ||
| 148 | TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0])); | ||
| 149 | TIMEO_TBL[n] = CURTIME; | ||
| 150 | } | ||
| 151 | return n; | ||
| 152 | } | ||
| 153 | |||
| 154 | static void remove_peer(isrv_state_t *state, int peer) | ||
| 155 | { | ||
| 156 | int movesize; | ||
| 157 | int fd; | ||
| 158 | |||
| 159 | DPRINTF("remove_peer(%d)", peer); | ||
| 160 | |||
| 161 | fd = FD_COUNT - 1; | ||
| 162 | while (fd >= 0) { | ||
| 163 | if (FD2PEER[fd] == peer) { | ||
| 164 | isrv_close_fd(state, fd); | ||
| 165 | fd--; | ||
| 166 | continue; | ||
| 167 | } | ||
| 168 | if (FD2PEER[fd] > peer) | ||
| 169 | FD2PEER[fd]--; | ||
| 170 | fd--; | ||
| 171 | } | ||
| 172 | |||
| 173 | PEER_COUNT--; | ||
| 174 | DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT); | ||
| 175 | |||
| 176 | movesize = (PEER_COUNT - peer) * sizeof(void*); | ||
| 177 | if (movesize > 0) { | ||
| 178 | memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize); | ||
| 179 | if (TIMEOUT) | ||
| 180 | memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize); | ||
| 181 | } | ||
| 182 | PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0])); | ||
| 183 | if (TIMEOUT) | ||
| 184 | TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0])); | ||
| 185 | } | ||
| 186 | |||
| 187 | static void handle_accept(isrv_state_t *state, int fd) | ||
| 188 | { | ||
| 189 | int n, newfd; | ||
| 190 | |||
| 191 | fcntl(fd, F_SETFL, (int)(PARAM_TBL[0]) | O_NONBLOCK); | ||
| 192 | newfd = accept(fd, NULL, 0); | ||
| 193 | fcntl(fd, F_SETFL, (int)(PARAM_TBL[0])); | ||
| 194 | if (newfd < 0) { | ||
| 195 | if (errno == EAGAIN) return; | ||
| 196 | /* Most probably someone gave us wrong fd type | ||
| 197 | * (for example, non-socket) */ | ||
| 198 | bb_perror_msg_and_die("accept"); | ||
| 199 | } | ||
| 200 | |||
| 201 | DPRINTF("new_peer(%d)", newfd); | ||
| 202 | n = state->new_peer(state, newfd); | ||
| 203 | if (n) | ||
| 204 | remove_peer(state, n); /* unsuccesful peer start */ | ||
| 205 | } | ||
| 206 | |||
| 207 | void BUG_sizeof_fd_set_is_strange(void); | ||
| 208 | static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **)) | ||
| 209 | { | ||
| 210 | enum { LONG_CNT = sizeof(fd_set) / sizeof(long) }; | ||
| 211 | int fds_pos; | ||
| 212 | int fd, peer; | ||
| 213 | int fd_cnt = FD_COUNT; | ||
| 214 | |||
| 215 | if (LONG_CNT * sizeof(long) != sizeof(fd_set)) | ||
| 216 | BUG_sizeof_fd_set_is_strange(); | ||
| 217 | |||
| 218 | fds_pos = 0; | ||
| 219 | while (1) { | ||
| 220 | /* Find next nonzero bit */ | ||
| 221 | while (fds_pos < LONG_CNT) { | ||
| 222 | if (((long*)fds)[fds_pos] == 0) { | ||
| 223 | fds_pos++; | ||
| 224 | continue; | ||
| 225 | } | ||
| 226 | /* Found non-zero word */ | ||
| 227 | fd = fds_pos * sizeof(long)*8; /* word# -> bit# */ | ||
| 228 | while (1) { | ||
| 229 | if (FD_ISSET(fd, fds)) { | ||
| 230 | FD_CLR(fd, fds); | ||
| 231 | goto found_fd; | ||
| 232 | } | ||
| 233 | fd++; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | break; /* all words are zero */ | ||
| 237 | found_fd: | ||
| 238 | if (fd >= fd_cnt) /* paranoia */ | ||
| 239 | break; | ||
| 240 | DPRINTF("handle_fd_set: fd %d is active", fd); | ||
| 241 | peer = FD2PEER[fd]; | ||
| 242 | if (peer == 0) { | ||
| 243 | handle_accept(state, fd); | ||
| 244 | continue; | ||
| 245 | } | ||
| 246 | DPRINTF("h(fd:%d)", fd); | ||
| 247 | if (h(fd, &PARAM_TBL[peer])) { | ||
| 248 | /* this peer is gone */ | ||
| 249 | remove_peer(state, peer); | ||
| 250 | } else if (TIMEOUT) { | ||
| 251 | TIMEO_TBL[peer] = monotonic_time(); | ||
| 252 | } | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **)) | ||
| 257 | { | ||
| 258 | int n, peer; | ||
| 259 | peer = PEER_COUNT-1; | ||
| 260 | /* peer 0 is not checked */ | ||
| 261 | while (peer > 0) { | ||
| 262 | DPRINTF("peer %d: time diff %d", peer, (int)(CURTIME - TIMEO_TBL[peer])); | ||
| 263 | |||
| 264 | if ((CURTIME - TIMEO_TBL[peer]) > TIMEOUT) { | ||
| 265 | DPRINTF("peer %d: do_timeout()", peer); | ||
| 266 | n = do_timeout(&PARAM_TBL[peer]); | ||
| 267 | if (n) | ||
| 268 | remove_peer(state, peer); | ||
| 269 | } | ||
| 270 | peer--; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | /* Driver */ | ||
| 275 | void isrv_run( | ||
| 276 | int listen_fd, | ||
| 277 | int (*new_peer)(isrv_state_t *state, int fd), | ||
| 278 | int (*do_rd)(int fd, void **), | ||
| 279 | int (*do_wr)(int fd, void **), | ||
| 280 | int (*do_timeout)(void **), | ||
| 281 | int timeout, | ||
| 282 | int exit_if_no_clients) | ||
| 283 | { | ||
| 284 | isrv_state_t *state = xzalloc(sizeof(*state)); | ||
| 285 | state->new_peer = new_peer; | ||
| 286 | state->timeout = timeout; | ||
| 287 | |||
| 288 | /* register "peer" #0 - it will accept new connections */ | ||
| 289 | isrv_register_peer(state, NULL); | ||
| 290 | isrv_register_fd(state, /*peer:*/ 0, listen_fd); | ||
| 291 | isrv_want_rd(state, listen_fd); | ||
| 292 | /* remember flags to make blocking<->nonblocking switch faster */ | ||
| 293 | PARAM_TBL[0] = (void*) (fcntl(listen_fd, F_GETFL, 0)); | ||
| 294 | |||
| 295 | while (1) { | ||
| 296 | struct timeval tv; | ||
| 297 | fd_set rd; | ||
| 298 | fd_set wr; | ||
| 299 | fd_set *wrp = NULL; | ||
| 300 | int n; | ||
| 301 | |||
| 302 | tv.tv_sec = timeout; | ||
| 303 | tv.tv_usec = 0; | ||
| 304 | rd = state->rd; | ||
| 305 | if (WR_COUNT) { | ||
| 306 | wr = state->wr; | ||
| 307 | wrp = ≀ | ||
| 308 | } | ||
| 309 | |||
| 310 | DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...", FD_COUNT, timeout); | ||
| 311 | n = select(FD_COUNT, &rd, wrp, NULL, timeout ? &tv : NULL); | ||
| 312 | DPRINTF("run: ...select:%d", n); | ||
| 313 | |||
| 314 | if (n < 0) { | ||
| 315 | if (errno != EINTR) | ||
| 316 | bb_perror_msg("select"); | ||
| 317 | continue; | ||
| 318 | } | ||
| 319 | |||
| 320 | if (exit_if_no_clients && n == 0 && PEER_COUNT <= 1) | ||
| 321 | break; | ||
| 322 | |||
| 323 | if (timeout) { | ||
| 324 | time_t t = monotonic_time(); | ||
| 325 | if (t != CURTIME) { | ||
| 326 | CURTIME = t; | ||
| 327 | handle_timeout(state, do_timeout); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | if (n > 0) { | ||
| 331 | handle_fd_set(state, &rd, do_rd); | ||
| 332 | if (wrp) | ||
| 333 | handle_fd_set(state, wrp, do_wr); | ||
| 334 | } | ||
| 335 | } | ||
| 336 | DPRINTF("run: bailout"); | ||
| 337 | } | ||
diff --git a/networking/isrv_identd.c b/networking/isrv_identd.c new file mode 100644 index 000000000..b9481f8d3 --- /dev/null +++ b/networking/isrv_identd.c | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Fake identd server. | ||
| 4 | * | ||
| 5 | * Copyright (C) 2007 Denis Vlasenko | ||
| 6 | * | ||
| 7 | * Licensed under GPL version 2, see file LICENSE in this tarball for details. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <syslog.h> | ||
| 11 | #include "busybox.h" | ||
| 12 | #include "isrv.h" | ||
| 13 | |||
| 14 | enum { TIMEOUT = 20 }; | ||
| 15 | |||
| 16 | /* Why use alarm(TIMEOUT-1)? | ||
| 17 | * isrv's internal select() will run with timeout=TIMEOUT. | ||
| 18 | * If nothing happens during TIMEOUT-1 seconds (no accept/read), | ||
| 19 | * then ALL sessions timed out by now. Instead of closing them one-by-one | ||
| 20 | * (isrv calls do_timeout for each 'stale' session), | ||
| 21 | * SIGALRM triggered by alarm(TIMEOUT-1) will kill us, terminating them all. | ||
| 22 | */ | ||
| 23 | |||
| 24 | typedef struct identd_buf_t { | ||
| 25 | int pos; | ||
| 26 | int fd_flag; | ||
| 27 | char buf[64 - 2*sizeof(int)]; | ||
| 28 | } identd_buf_t; | ||
| 29 | |||
| 30 | static const char *bogouser = "nobody"; | ||
| 31 | |||
| 32 | static int new_peer(isrv_state_t *state, int fd) | ||
| 33 | { | ||
| 34 | int peer; | ||
| 35 | identd_buf_t *buf = xzalloc(sizeof(*buf)); | ||
| 36 | |||
| 37 | alarm(TIMEOUT - 1); | ||
| 38 | |||
| 39 | peer = isrv_register_peer(state, buf); | ||
| 40 | if (peer < 0) | ||
| 41 | return 0; /* failure */ | ||
| 42 | if (isrv_register_fd(state, peer, fd) < 0) | ||
| 43 | return peer; /* failure, unregister peer */ | ||
| 44 | |||
| 45 | buf->fd_flag = fcntl(fd, F_GETFL, 0) | O_NONBLOCK; | ||
| 46 | isrv_want_rd(state, fd); | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | static int do_rd(int fd, void **paramp) | ||
| 51 | { | ||
| 52 | identd_buf_t *buf = *paramp; | ||
| 53 | char *cur, *p; | ||
| 54 | int sz; | ||
| 55 | |||
| 56 | alarm(TIMEOUT - 1); | ||
| 57 | |||
| 58 | cur = buf->buf + buf->pos; | ||
| 59 | |||
| 60 | fcntl(fd, F_SETFL, buf->fd_flag | O_NONBLOCK); | ||
| 61 | sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos); | ||
| 62 | |||
| 63 | if (sz < 0) { | ||
| 64 | if (errno != EAGAIN) | ||
| 65 | goto term; /* terminate this session if !EAGAIN */ | ||
| 66 | goto ok; | ||
| 67 | } | ||
| 68 | |||
| 69 | buf->pos += sz; | ||
| 70 | buf->buf[buf->pos] = '\0'; | ||
| 71 | p = strpbrk(cur, "\r\n"); | ||
| 72 | if (p) | ||
| 73 | *p = '\0'; | ||
| 74 | if (p || !sz || buf->pos == sizeof(buf->buf)) { | ||
| 75 | /* fd is still in nonblocking mode - we never block here */ | ||
| 76 | fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser); | ||
| 77 | goto term; | ||
| 78 | } | ||
| 79 | ok: | ||
| 80 | fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK); | ||
| 81 | return 0; | ||
| 82 | term: | ||
| 83 | fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK); | ||
| 84 | free(buf); | ||
| 85 | return 1; | ||
| 86 | } | ||
| 87 | |||
| 88 | static int do_timeout(void **paramp) | ||
| 89 | { | ||
| 90 | return 1; /* terminate session */ | ||
| 91 | } | ||
| 92 | |||
| 93 | static void inetd_mode(void) | ||
| 94 | { | ||
| 95 | identd_buf_t *buf = xzalloc(sizeof(*buf)); | ||
| 96 | /* We do NOT want nonblocking I/O here! */ | ||
| 97 | buf->fd_flag = fcntl(0, F_GETFL, 0); | ||
| 98 | while (do_rd(0, (void*)&buf) == 0) /* repeat */; | ||
| 99 | } | ||
| 100 | |||
| 101 | int fakeidentd_main(int argc, char **argv) | ||
| 102 | { | ||
| 103 | enum { | ||
| 104 | OPT_foreground = 0x1, | ||
| 105 | OPT_inetd = 0x2, | ||
| 106 | OPT_inetdwait = 0x4, | ||
| 107 | OPT_nodeamon = 0x7, | ||
| 108 | OPT_bindaddr = 0x8, | ||
| 109 | }; | ||
| 110 | |||
| 111 | const char *bind_address = NULL; | ||
| 112 | unsigned opt; | ||
| 113 | int fd; | ||
| 114 | |||
| 115 | opt = getopt32(argc, argv, "fiwb:", &bind_address); | ||
| 116 | if (optind < argc) | ||
| 117 | bogouser = argv[optind]; | ||
| 118 | |||
| 119 | /* Daemonize if no -f or -i or -w */ | ||
| 120 | bb_sanitize_stdio(!(opt & OPT_nodeamon)); | ||
| 121 | if (!(opt & OPT_nodeamon)) { | ||
| 122 | openlog(applet_name, 0, LOG_DAEMON); | ||
| 123 | logmode = LOGMODE_SYSLOG; | ||
| 124 | } | ||
| 125 | |||
| 126 | if (opt & OPT_inetd) { | ||
| 127 | inetd_mode(); | ||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 131 | /* Ignore closed connections when writing */ | ||
| 132 | signal(SIGPIPE, SIG_IGN); | ||
| 133 | |||
| 134 | if (opt & OPT_inetdwait) { | ||
| 135 | fd = 0; | ||
| 136 | } else { | ||
| 137 | fd = create_and_bind_stream_or_die(bind_address, | ||
| 138 | bb_lookup_port("identd", "tcp", 113)); | ||
| 139 | xlisten(fd, 5); | ||
| 140 | } | ||
| 141 | |||
| 142 | isrv_run(fd, new_peer, do_rd, NULL, do_timeout, TIMEOUT, 1); | ||
| 143 | return 0; | ||
| 144 | } | ||
