aboutsummaryrefslogtreecommitdiff
path: root/networking
diff options
context:
space:
mode:
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}