source: trunk/mindi-busybox/networking/fakeidentd.c @ 904

Last change on this file since 904 was 821, checked in by bruno, 13 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

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