30f9a7a26344e07bb0f6f976e214d49ff5d9158a
[oweals/busybox.git] / networking / isrv_identd.c
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
14 enum { TIMEOUT = 20 };
15
16 typedef struct identd_buf_t {
17         int pos;
18         int fd_flag;
19         char buf[64 - 2*sizeof(int)];
20 } identd_buf_t;
21
22 static const char *bogouser = "nobody";
23
24 static int new_peer(isrv_state_t *state, int fd)
25 {
26         int peer;
27         identd_buf_t *buf = xzalloc(sizeof(*buf));
28
29         peer = isrv_register_peer(state, buf);
30         if (peer < 0)
31                 return 0; /* failure */
32         if (isrv_register_fd(state, peer, fd) < 0)
33                 return peer; /* failure, unregister peer */
34
35         buf->fd_flag = fcntl(fd, F_GETFL, 0) | O_NONBLOCK;
36         isrv_want_rd(state, fd);
37         return 0;
38 }
39
40 static int do_rd(int fd, void **paramp)
41 {
42         identd_buf_t *buf = *paramp;
43         char *cur, *p;
44         int sz;
45
46         cur = buf->buf + buf->pos;
47
48         fcntl(fd, F_SETFL, buf->fd_flag);
49         sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
50
51         if (sz < 0) {
52                 if (errno != EAGAIN)
53                         goto term; /* terminate this session if !EAGAIN */
54                 goto ok;
55         }
56
57         buf->pos += sz;
58         buf->buf[buf->pos] = '\0';
59         p = strpbrk(cur, "\r\n");
60         if (p)
61                 *p = '\0';
62         if (p || !sz || buf->pos == sizeof(buf->buf)) {
63                 /* fd is still in nonblocking mode - we never block here */
64                 fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
65                 goto term;
66         }
67  ok:
68         fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
69         return 0;
70  term:
71         fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
72         free(buf);
73         return 1;
74 }
75
76 static int do_timeout(void **paramp)
77 {
78         return 1; /* terminate session */
79 }
80
81 static void inetd_mode(void)
82 {
83         identd_buf_t *buf = xzalloc(sizeof(*buf));
84         /* We do NOT want nonblocking I/O here! */
85         buf->fd_flag = fcntl(0, F_GETFL, 0);
86         do
87                 alarm(TIMEOUT);
88         while (do_rd(0, (void*)&buf) == 0) /* repeat */;
89 }
90
91 int fakeidentd_main(int argc, char **argv)
92 {
93         enum {
94                 OPT_foreground = 0x1,
95                 OPT_inetd      = 0x2,
96                 OPT_inetdwait  = 0x4,
97                 OPT_nodeamon   = 0x7,
98                 OPT_bindaddr   = 0x8,
99         };
100
101         const char *bind_address = NULL;
102         unsigned opt;
103         int fd;
104
105         opt = getopt32(argc, argv, "fiwb:", &bind_address);
106         if (optind < argc)
107                 bogouser = argv[optind];
108
109         /* Daemonize if no -f or -i or -w */
110         bb_sanitize_stdio(!(opt & OPT_nodeamon));
111         if (!(opt & OPT_nodeamon)) {
112                 openlog(applet_name, 0, LOG_DAEMON);
113                 logmode = LOGMODE_SYSLOG;
114         }
115
116         if (opt & OPT_inetd) {
117                 inetd_mode();
118                 return 0;
119         }
120
121         /* Ignore closed connections when writing */
122         signal(SIGPIPE, SIG_IGN);
123
124         if (opt & OPT_inetdwait) {
125                 fd = 0;
126         } else {
127                 fd = create_and_bind_stream_or_die(bind_address,
128                                 bb_lookup_port("identd", "tcp", 113));
129                 xlisten(fd, 5);
130         }
131
132         isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout,
133                         TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0);
134         return 0;
135 }