Contents of /trunk/mkinitrd-magellan/busybox/networking/fakeidentd.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 8436 byte(s)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 8436 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* NB: this file is to be removed soon. See isrv_identd.c */ |
2 | |
3 | /* vi: set sw=4 ts=4: */ |
4 | /* |
5 | * A fake identd server |
6 | * |
7 | * Adapted to busybox by Thomas Lundquist <thomasez@zelow.no> |
8 | * Original Author: Tomi Ollila <too@iki.fi> |
9 | * http://www.guru-group.fi/~too/sw/ |
10 | * |
11 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
12 | */ |
13 | |
14 | /* Ident crash course |
15 | * |
16 | * Incoming requests are of form "6191, 23\r\n" - peer asks us |
17 | * "which user connected from your port 6191 to my port 23?" |
18 | * We should answer: |
19 | * "6193, 23 : USERID : UNIX : username\r\n" |
20 | * and close the connection. |
21 | * We can also reply: |
22 | * "6195, 23 : USERID : OTHER[,US-ASCII] : username\r\n" |
23 | * "6195, 23 : ERROR : INVALID-PORT/NO-USER/HIDDEN-USER/UNKNOWN-ERROR\r\n" |
24 | * but we probably will never want that. |
25 | */ |
26 | |
27 | #include "busybox.h" |
28 | |
29 | #define SANE_INETD_ONLY_VERSION |
30 | |
31 | #ifdef SANE_INETD_ONLY_VERSION |
32 | |
33 | int fakeidentd_main(int argc, char **argv) |
34 | { |
35 | char buf[64]; |
36 | const char *bogouser = "nobody"; |
37 | char *cur = buf; |
38 | int rem = sizeof(buf)-1; |
39 | |
40 | if (argv[1]) |
41 | bogouser = argv[1]; |
42 | |
43 | alarm(30); |
44 | while (1) { |
45 | char *p; |
46 | int sz = safe_read(0, cur, rem); |
47 | if (sz < 0) return 1; |
48 | cur[sz] = '\0'; |
49 | p = strpbrk(cur, "\r\n"); |
50 | if (p) { |
51 | *p = '\0'; |
52 | break; |
53 | } |
54 | cur += sz; |
55 | rem -= sz; |
56 | if (!rem || !sz) |
57 | break; |
58 | } |
59 | printf("%s : USERID : UNIX : %s\r\n", buf, bogouser); |
60 | return 0; |
61 | } |
62 | |
63 | #else |
64 | |
65 | /* Welcome to the bloaty horrors */ |
66 | |
67 | #include <sys/syslog.h> |
68 | #include <sys/uio.h> |
69 | |
70 | #define MAXCONNS 20 |
71 | #define MAXIDLETIME 45 |
72 | |
73 | static const char ident_substr[] = " : USERID : UNIX : "; |
74 | enum { ident_substr_len = sizeof(ident_substr) - 1 }; |
75 | #define PIDFILE "/var/run/identd.pid" |
76 | |
77 | /* |
78 | * We have to track the 'first connection socket' so that we |
79 | * don't go around closing file descriptors for non-clients. |
80 | * |
81 | * descriptor setup normally |
82 | * 0 = server socket |
83 | * 1 = syslog fd (hopefully -- otherwise this won't work) |
84 | * 2 = connection socket after detached from tty. standard error before that |
85 | * 3 - 2 + MAXCONNS = rest connection sockets |
86 | * |
87 | * To try to make sure that syslog fd is what is "requested", the that fd |
88 | * is closed before openlog() call. It can only severely fail if fd 0 |
89 | * is initially closed. |
90 | */ |
91 | #define FCS 2 |
92 | |
93 | /* |
94 | * FD of the connection is always the index of the connection structure |
95 | * in `conns' array + FCS |
96 | */ |
97 | static struct { |
98 | time_t lasttime; |
99 | int len; |
100 | char buf[20]; |
101 | } conns[MAXCONNS]; |
102 | |
103 | /* When using global variables, bind those at least to a structure. */ |
104 | static struct { |
105 | const char *identuser; |
106 | fd_set readfds; |
107 | int conncnt; |
108 | } G; |
109 | |
110 | static char *bind_ip_address; |
111 | |
112 | static int chmatch(char c, char *chars) |
113 | { |
114 | for (; *chars; chars++) |
115 | if (c == *chars) |
116 | return 1; |
117 | return 0; |
118 | } |
119 | |
120 | static int skipchars(char **p, char *chars) |
121 | { |
122 | while (chmatch(**p, chars)) |
123 | (*p)++; |
124 | if (**p == '\r' || **p == '\n') |
125 | return 0; |
126 | return 1; |
127 | } |
128 | |
129 | static int parseAddrs(char *ptr, char **myaddr, char **heraddr) |
130 | { |
131 | /* parse <port-on-server> , <port-on-client> */ |
132 | |
133 | if (!skipchars(&ptr, " \t")) |
134 | return -1; |
135 | |
136 | *myaddr = ptr; |
137 | |
138 | if (!skipchars(&ptr, "1234567890")) |
139 | return -1; |
140 | |
141 | if (!chmatch(*ptr, " \t,")) |
142 | return -1; |
143 | |
144 | *ptr++ = '\0'; |
145 | |
146 | if (!skipchars(&ptr, " \t,") ) |
147 | return -1; |
148 | |
149 | *heraddr = ptr; |
150 | |
151 | skipchars(&ptr, "1234567890"); |
152 | |
153 | if (!chmatch(*ptr, " \n\r")) |
154 | return -1; |
155 | |
156 | *ptr = '\0'; |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | static void replyError(int s, char *buf) |
162 | { |
163 | struct iovec iv[3]; |
164 | iv[0].iov_base = "0, 0 : ERROR : "; iv[0].iov_len = 15; |
165 | iv[1].iov_base = buf; iv[1].iov_len = strlen(buf); |
166 | iv[2].iov_base = "\r\n"; iv[2].iov_len = 2; |
167 | writev(s, iv, 3); |
168 | } |
169 | |
170 | static void reply(int s, char *buf) |
171 | { |
172 | char *myaddr, *heraddr; |
173 | |
174 | myaddr = heraddr = NULL; |
175 | |
176 | if (parseAddrs(buf, &myaddr, &heraddr)) |
177 | replyError(s, "X-INVALID-REQUEST"); |
178 | else { |
179 | struct iovec iv[6]; |
180 | iv[0].iov_base = myaddr; iv[0].iov_len = strlen(myaddr); |
181 | iv[1].iov_base = ", "; iv[1].iov_len = 2; |
182 | iv[2].iov_base = heraddr; iv[2].iov_len = strlen(heraddr); |
183 | iv[3].iov_base = (void *)ident_substr; iv[3].iov_len = ident_substr_len; |
184 | iv[4].iov_base = (void *)G.identuser; iv[4].iov_len = strlen(G.identuser); |
185 | iv[5].iov_base = "\r\n"; iv[5].iov_len = 2; |
186 | writev(s, iv, 6); |
187 | } |
188 | } |
189 | |
190 | static void movefd(int from, int to) |
191 | { |
192 | if (from != to) { |
193 | dup2(from, to); |
194 | close(from); |
195 | } |
196 | } |
197 | |
198 | static void deleteConn(int s) |
199 | { |
200 | int i = s - FCS; |
201 | |
202 | close(s); |
203 | |
204 | G.conncnt--; |
205 | |
206 | /* |
207 | * Most of the time there is 0 connections. Most often that there |
208 | * is connections, there is just one connection. When this one connection |
209 | * closes, i == G.conncnt = 0 -> no copying. |
210 | * When there is more than one connection, the oldest connections closes |
211 | * earlier on average. When this happens, the code below starts copying |
212 | * the connection structure w/ highest index to the place which which is |
213 | * just deleted. This means that the connection structures are no longer |
214 | * in chronological order. I'd quess this means that when there is more |
215 | * than 1 connection, on average every other connection structure needs |
216 | * to be copied over the time all these connections are deleted. |
217 | */ |
218 | if (i != G.conncnt) { |
219 | memcpy(&conns[i], &conns[G.conncnt], sizeof(conns[0])); |
220 | movefd(G.conncnt + FCS, s); |
221 | } |
222 | |
223 | FD_CLR(G.conncnt + FCS, &G.readfds); |
224 | } |
225 | |
226 | static int closeOldest(void) |
227 | { |
228 | time_t min = conns[0].lasttime; |
229 | int idx = 0; |
230 | int i; |
231 | |
232 | for (i = 1; i < MAXCONNS; i++) |
233 | if (conns[i].lasttime < min) |
234 | idx = i; |
235 | |
236 | replyError(idx + FCS, "X-SERVER-TOO-BUSY"); |
237 | close(idx + FCS); |
238 | |
239 | return idx; |
240 | } |
241 | |
242 | static int checkInput(char *buf, int len, int l) |
243 | { |
244 | int i; |
245 | for (i = len; i < len + l; ++i) |
246 | if (buf[i] == '\n') |
247 | return 1; |
248 | return 0; |
249 | } |
250 | |
251 | /* May succeed. If not, won't care. */ |
252 | static const char *to_unlink; |
253 | static void writepid(void) |
254 | { |
255 | int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0664); |
256 | if (fd < 0) |
257 | return; |
258 | to_unlink = PIDFILE; |
259 | fdprintf(fd, "%d\n", getpid()); |
260 | close(fd); |
261 | } |
262 | |
263 | static void handlexitsigs(int signum) |
264 | { |
265 | if (to_unlink) |
266 | if (unlink(to_unlink) < 0) |
267 | close(open(to_unlink, O_WRONLY|O_CREAT|O_TRUNC, 0644)); |
268 | exit(0); |
269 | } |
270 | |
271 | int fakeidentd_main(int argc, char **argv) |
272 | { |
273 | int fd; |
274 | pid_t pid; |
275 | |
276 | /* FD_ZERO(&G.readfds); - in bss, already zeroed */ |
277 | FD_SET(0, &G.readfds); |
278 | |
279 | /* handle -b <ip> parameter */ |
280 | getopt32(argc, argv, "b:", &bind_ip_address); |
281 | /* handle optional REPLY STRING */ |
282 | if (optind < argc) |
283 | G.identuser = argv[optind]; |
284 | else |
285 | G.identuser = "nobody"; |
286 | |
287 | writepid(); |
288 | signal(SIGTERM, handlexitsigs); |
289 | signal(SIGINT, handlexitsigs); |
290 | signal(SIGQUIT, handlexitsigs); |
291 | signal(SIGHUP, SIG_IGN); |
292 | signal(SIGPIPE, SIG_IGN); /* ignore closed connections when writing */ |
293 | |
294 | fd = create_and_bind_stream_or_die(bind_ip_address, bb_lookup_port("identd", "tcp", 113)); |
295 | xlisten(fd, 5); |
296 | |
297 | pid = fork(); |
298 | if (pid < 0) |
299 | bb_perror_msg_and_die("fork"); |
300 | if (pid != 0) /* parent */ |
301 | exit(0); |
302 | /* child */ |
303 | setsid(); |
304 | movefd(fd, 0); |
305 | while (fd) |
306 | close(fd--); |
307 | openlog(applet_name, 0, LOG_DAEMON); |
308 | logmode = LOGMODE_SYSLOG; |
309 | |
310 | /* main loop where we process all events and never exit */ |
311 | while (1) { |
312 | fd_set rfds = G.readfds; |
313 | struct timeval tv = { 15, 0 }; |
314 | int i; |
315 | int tim = time(NULL); |
316 | |
317 | select(G.conncnt + FCS, &rfds, NULL, NULL, G.conncnt? &tv: NULL); |
318 | |
319 | for (i = G.conncnt - 1; i >= 0; i--) { |
320 | int s = i + FCS; |
321 | |
322 | if (FD_ISSET(s, &rfds)) { |
323 | char *buf = conns[i].buf; |
324 | unsigned len = conns[i].len; |
325 | unsigned l; |
326 | |
327 | l = read(s, buf + len, sizeof(conns[0].buf) - len); |
328 | if (l > 0) { |
329 | if (checkInput(buf, len, l)) { |
330 | reply(s, buf); |
331 | goto deleteconn; |
332 | } else if (len + l >= sizeof(conns[0].buf)) { |
333 | replyError(s, "X-INVALID-REQUEST"); |
334 | goto deleteconn; |
335 | } else { |
336 | conns[i].len += l; |
337 | } |
338 | } else { |
339 | goto deleteconn; |
340 | } |
341 | conns[i].lasttime = tim; |
342 | continue; |
343 | deleteconn: |
344 | deleteConn(s); |
345 | } else { |
346 | /* implement as time_after() in linux kernel sources ... */ |
347 | if (conns[i].lasttime + MAXIDLETIME <= tim) { |
348 | replyError(s, "X-TIMEOUT"); |
349 | deleteConn(s); |
350 | } |
351 | } |
352 | } |
353 | |
354 | if (FD_ISSET(0, &rfds)) { |
355 | int s = accept(0, NULL, 0); |
356 | |
357 | if (s < 0) { |
358 | if (errno != EINTR) |
359 | bb_perror_msg("accept"); |
360 | } else { |
361 | if (G.conncnt == MAXCONNS) |
362 | i = closeOldest(); |
363 | else |
364 | i = G.conncnt++; |
365 | |
366 | movefd(s, i + FCS); /* move if not already there */ |
367 | FD_SET(i + FCS, &G.readfds); |
368 | conns[i].len = 0; |
369 | conns[i].lasttime = time(NULL); |
370 | } |
371 | } |
372 | } /* end of while (1) */ |
373 | |
374 | return 0; |
375 | } |
376 | |
377 | #endif /* !SANE_INETD_ONLY_VERSION */ |