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