aboutsummaryrefslogtreecommitdiff
path: root/networking
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-01-14 01:29:06 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-01-14 01:29:06 +0000
commit7a431b3715299854fb423ec00d5fafc0e2c7f07b (patch)
tree4e90c9d364485ef13c2e429ab22b9b925d50ea04 /networking
parent150f402b36197d822f8a7dd835231cd67b77e959 (diff)
downloadbusybox-w32-7a431b3715299854fb423ec00d5fafc0e2c7f07b.tar.gz
busybox-w32-7a431b3715299854fb423ec00d5fafc0e2c7f07b.tar.bz2
busybox-w32-7a431b3715299854fb423ec00d5fafc0e2c7f07b.zip
By popular request reinstate fakeidentd's standalone mode.
Since this is also needed for other applets like telnetd, introduce generic driver for such things. It even supports inetd-wait ('linger') mode, when inetd hands out listen socket to child and waits to it to die, instead of handing out accepted socket and continuing listening itself (nowait mode). Code growth ~200 bytes. NB: our inetd doesn't support wait mode yet (or mabe it is buggy).
Diffstat (limited to 'networking')
-rw-r--r--networking/Kbuild3
-rw-r--r--networking/inetd.c15
-rw-r--r--networking/isrv.c337
-rw-r--r--networking/isrv_identd.c144
4 files changed, 489 insertions, 10 deletions
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
9lib-$(CONFIG_ARPING) += arping.o 9lib-$(CONFIG_ARPING) += arping.o
10lib-$(CONFIG_DNSD) += dnsd.o 10lib-$(CONFIG_DNSD) += dnsd.o
11lib-$(CONFIG_ETHER_WAKE) += ether-wake.o 11lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
12lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o 12#lib-$(CONFIG_FAKEIDENTD) += fakeidentd.o
13lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
13lib-$(CONFIG_FTPGET) += ftpgetput.o 14lib-$(CONFIG_FTPGET) += ftpgetput.o
14lib-$(CONFIG_FTPPUT) += ftpgetput.o 15lib-$(CONFIG_FTPPUT) += ftpgetput.o
15lib-$(CONFIG_HOSTNAME) += hostname.o 16lib-$(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*/
25static 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
38struct 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 */
62void isrv_want_rd(isrv_state_t *state, int fd)
63{
64 FD_SET(fd, &state->rd);
65}
66
67/* callback */
68void 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 */
77void isrv_dont_want_rd(isrv_state_t *state, int fd)
78{
79 FD_CLR(fd, &state->rd);
80}
81
82/* callback */
83void 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 */
92int 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 */
116void 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 */
136int 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
154static 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
187static 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
207void BUG_sizeof_fd_set_is_strange(void);
208static 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
256static 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 */
275void 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 = &wr;
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
14enum { 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
24typedef struct identd_buf_t {
25 int pos;
26 int fd_flag;
27 char buf[64 - 2*sizeof(int)];
28} identd_buf_t;
29
30static const char *bogouser = "nobody";
31
32static 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
50static 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
88static int do_timeout(void **paramp)
89{
90 return 1; /* terminate session */
91}
92
93static 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
101int 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}