blob: b21991212cacd05fa56ffc15758b45f33c4964b1 [file] [log] [blame]
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +00001/* vi: set sw=4 ts=4: */
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +00002/*
Eric Andersen08a72202002-09-30 20:52:10 +00003 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
Rob Landley1ec5b292006-05-29 07:42:02 +00006 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersen08a72202002-09-30 20:52:10 +00007 *
8 * ---------------------------------------------------------------------------
9 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
10 ****************************************************************************
11 *
12 * The telnetd manpage says it all:
13 *
14 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
15 * a client, then creating a login process which has the slave side of the
16 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
17 * master side of the pseudo-terminal, implementing the telnet protocol and
18 * passing characters between the remote client and the login process.
19 *
20 * Vladimir Oleynik <dzo@simtreas.ru> 2001
21 * Set process group corrections, initial busybox port
22 */
23
Denis Vlasenko75f8d082006-11-22 15:54:52 +000024#define DEBUG 0
Eric Andersen08a72202002-09-30 20:52:10 +000025
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000026#include "libbb.h"
Rob Landley099ed502006-08-28 09:41:49 +000027
Denis Vlasenko75f8d082006-11-22 15:54:52 +000028#if DEBUG
Eric Andersen08a72202002-09-30 20:52:10 +000029#define TELCMDS
30#define TELOPTS
31#endif
32#include <arpa/telnet.h>
Eric Andersen08a72202002-09-30 20:52:10 +000033#include <sys/syslog.h>
34
Eric Andersen08a72202002-09-30 20:52:10 +000035
Denis Vlasenko75f8d082006-11-22 15:54:52 +000036#if ENABLE_LOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000037static const char *loginpath = "/bin/login";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000038#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +000039static const char *loginpath = DEFAULT_SHELL;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000040#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +000041
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000042static const char *issuefile = "/etc/issue.net";
Eric Andersen08a72202002-09-30 20:52:10 +000043
Eric Andersen08a72202002-09-30 20:52:10 +000044/* structure that describes a session */
45
46struct tsession {
47 struct tsession *next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +000048 int sockfd_read, sockfd_write, ptyfd;
Eric Andersen08a72202002-09-30 20:52:10 +000049 int shell_pid;
50 /* two circular buffers */
51 char *buf1, *buf2;
52 int rdidx1, wridx1, size1;
53 int rdidx2, wridx2, size2;
54};
55
Denis Vlasenko88308fe2007-06-25 10:35:11 +000056/* Two buffers are directly after tsession in malloced memory.
57 * Make whole thing fit in 4k */
58enum { BUFSIZE = (4*1024 - sizeof(struct tsession)) / 2 };
59
Eric Andersen08a72202002-09-30 20:52:10 +000060/*
Eric Andersen08a72202002-09-30 20:52:10 +000061 This is how the buffers are used. The arrows indicate the movement
62 of data.
63
64 +-------+ wridx1++ +------+ rdidx1++ +----------+
65 | | <-------------- | buf1 | <-------------- | |
66 | | size1-- +------+ size1++ | |
Mike Frysingerd2c8fd62006-05-10 17:17:09 +000067 | pty | | socket |
Eric Andersen08a72202002-09-30 20:52:10 +000068 | | rdidx2++ +------+ wridx2++ | |
69 | | --------------> | buf2 | --------------> | |
70 +-------+ size2++ +------+ size2-- +----------+
71
72 Each session has got two buffers.
Eric Andersen08a72202002-09-30 20:52:10 +000073*/
74
75static int maxfd;
76
77static struct tsession *sessions;
78
79
80/*
Bernhard Reutner-Fischere0387a62006-06-07 13:31:59 +000081 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
Eric Andersen08a72202002-09-30 20:52:10 +000082 and must be removed so as to not be interpreted by the terminal). Make an
83 uninterrupted string of characters fit for the terminal. Do this by packing
84 all characters meant for the terminal sequentially towards the end of bf.
85
86 Return a pointer to the beginning of the characters meant for the terminal.
87 and make *num_totty the number of characters that should be sent to
88 the terminal.
89
90 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
91 past (bf + len) then that IAC will be left unprocessed and *processed will be
92 less than len.
93
94 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
95 what is the escape character? We aren't handling that situation here.
96
Eric Andersen3752d332003-12-19 11:30:13 +000097 CR-LF ->'s CR mapping is also done here, for convenience
Denis Vlasenko75f8d082006-11-22 15:54:52 +000098 */
Eric Andersen08a72202002-09-30 20:52:10 +000099static char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000100remove_iacs(struct tsession *ts, int *pnum_totty)
101{
Eric Andersen0cb6f352006-01-30 22:30:41 +0000102 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000103 unsigned char *ptr = ptr0;
104 unsigned char *totty = ptr;
105 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
106 int processed;
107 int num_totty;
108
109 while (ptr < end) {
110 if (*ptr != IAC) {
Eric Andersen3752d332003-12-19 11:30:13 +0000111 int c = *ptr;
Eric Andersen08a72202002-09-30 20:52:10 +0000112 *totty++ = *ptr++;
Eric Andersen3752d332003-12-19 11:30:13 +0000113 /* We now map \r\n ==> \r for pragmatic reasons.
114 * Many client implementations send \r\n when
115 * the user hits the CarriageReturn key.
116 */
117 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
118 ptr++;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000119 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000120 /*
121 * TELOPT_NAWS support!
122 */
123 if ((ptr+2) >= end) {
Eric Andersen08a72202002-09-30 20:52:10 +0000124 /* only the beginning of the IAC is in the
125 buffer we were asked to process, we can't
126 process this char. */
127 break;
128 }
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000129
130 /*
131 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
132 */
133 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
134 struct winsize ws;
135 if ((ptr+8) >= end)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000136 break; /* incomplete, can't process */
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000137 ws.ws_col = (ptr[3] << 8) | ptr[4];
138 ws.ws_row = (ptr[5] << 8) | ptr[6];
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000139 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000140 ptr += 9;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000141 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000142 /* skip 3-byte IAC non-SB cmd */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000143#if DEBUG
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000144 fprintf(stderr, "Ignoring IAC %s,%s\n",
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000145 TELCMD(ptr[1]), TELOPT(ptr[2]));
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000146#endif
147 ptr += 3;
148 }
Eric Andersen08a72202002-09-30 20:52:10 +0000149 }
150 }
151
152 processed = ptr - ptr0;
153 num_totty = totty - ptr0;
154 /* the difference between processed and num_to tty
155 is all the iacs we removed from the stream.
156 Adjust buf1 accordingly. */
157 ts->wridx1 += processed - num_totty;
158 ts->size1 -= processed - num_totty;
159 *pnum_totty = num_totty;
160 /* move the chars meant for the terminal towards the end of the
161 buffer. */
162 return memmove(ptr - num_totty, ptr0, num_totty);
163}
164
165
166static int
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000167getpty(char *line, int size)
Eric Andersen08a72202002-09-30 20:52:10 +0000168{
169 int p;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000170#if ENABLE_FEATURE_DEVPTS
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000171 p = open("/dev/ptmx", O_RDWR);
Eric Andersen08a72202002-09-30 20:52:10 +0000172 if (p > 0) {
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000173 const char *name;
Eric Andersen08a72202002-09-30 20:52:10 +0000174 grantpt(p);
175 unlockpt(p);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000176 name = ptsname(p);
177 if (!name) {
178 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
179 return -1;
180 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000181 safe_strncpy(line, name, size);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000182 return p;
Eric Andersen08a72202002-09-30 20:52:10 +0000183 }
184#else
185 struct stat stb;
186 int i;
187 int j;
188
189 strcpy(line, "/dev/ptyXX");
190
191 for (i = 0; i < 16; i++) {
192 line[8] = "pqrstuvwxyzabcde"[i];
193 line[9] = '0';
194 if (stat(line, &stb) < 0) {
195 continue;
196 }
197 for (j = 0; j < 16; j++) {
198 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000199 if (DEBUG)
200 fprintf(stderr, "Trying to open device: %s\n", line);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000201 p = open(line, O_RDWR | O_NOCTTY);
202 if (p >= 0) {
Eric Andersen08a72202002-09-30 20:52:10 +0000203 line[5] = 't';
204 return p;
205 }
206 }
207 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000208#endif /* FEATURE_DEVPTS */
Eric Andersen08a72202002-09-30 20:52:10 +0000209 return -1;
210}
211
212
213static void
214send_iac(struct tsession *ts, unsigned char command, int option)
215{
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000216 /* We rely on that there is space in the buffer for now. */
Eric Andersen08a72202002-09-30 20:52:10 +0000217 char *b = ts->buf2 + ts->rdidx2;
218 *b++ = IAC;
219 *b++ = command;
220 *b++ = option;
221 ts->rdidx2 += 3;
222 ts->size2 += 3;
223}
224
225
226static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000227make_new_session(
228 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
229 SKIP_FEATURE_TELNETD_STANDALONE(void)
230) {
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000231 const char *login_argv[2];
Eric Andersen08a72202002-09-30 20:52:10 +0000232 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000233 int fd, pid;
Eric Andersen08a72202002-09-30 20:52:10 +0000234 char tty_name[32];
Rob Landley1ec5b292006-05-29 07:42:02 +0000235 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000236
237 ts->buf1 = (char *)(&ts[1]);
238 ts->buf2 = ts->buf1 + BUFSIZE;
239
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000240 /* Got a new connection, set up a tty. */
241 fd = getpty(tty_name, 32);
242 if (fd < 0) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000243 bb_error_msg("all terminals in use");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000244 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000245 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000246 if (fd > maxfd) maxfd = fd;
247 ndelay_on(ts->ptyfd = fd);
248#if ENABLE_FEATURE_TELNETD_STANDALONE
249 if (sock_w > maxfd) maxfd = sock_w;
250 if (sock_r > maxfd) maxfd = sock_r;
251 ndelay_on(ts->sockfd_write = sock_w);
252 ndelay_on(ts->sockfd_read = sock_r);
253#else
254 ts->sockfd_write = 1;
255 /* xzalloc: ts->sockfd_read = 0; */
256 ndelay_on(0);
257 ndelay_on(1);
258#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000259 /* Make the telnet client understand we will echo characters so it
260 * should not do it locally. We don't tell the client to run linemode,
261 * because we want to handle line editing and tab completion and other
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000262 * stuff that requires char-by-char support. */
Eric Andersen08a72202002-09-30 20:52:10 +0000263 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000264 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000265 send_iac(ts, DO, TELOPT_LFLOW);
266 send_iac(ts, WILL, TELOPT_ECHO);
267 send_iac(ts, WILL, TELOPT_SGA);
268
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000269 pid = fork();
270 if (pid < 0) {
271 free(ts);
272 close(fd);
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000273 bb_perror_msg("fork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000274 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000275 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000276 if (pid > 0) {
277 /* parent */
278 ts->shell_pid = pid;
279 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000280 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000281
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000282 /* child */
Eric Andersen08a72202002-09-30 20:52:10 +0000283
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000284 /* make new session and process group */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000285 setsid();
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000286
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000287 /* open the child's side of the tty. */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000288 /* NB: setsid() disconnects from any previous ctty's. Therefore
289 * we must open child's side of the tty AFTER setsid! */
290 fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000291 dup2(fd, 0);
292 dup2(fd, 1);
293 dup2(fd, 2);
294 while (fd > 2) close(fd--);
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000295 tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
Eric Andersen08a72202002-09-30 20:52:10 +0000296
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000297 /* The pseudo-terminal allocated to the client is configured to operate in
298 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
299 tcgetattr(0, &termbuf);
300 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
301 termbuf.c_oflag |= ONLCR|XTABS;
302 termbuf.c_iflag |= ICRNL;
303 termbuf.c_iflag &= ~IXOFF;
304 /*termbuf.c_lflag &= ~ICANON;*/
305 tcsetattr(0, TCSANOW, &termbuf);
306
307 print_login_issue(issuefile, NULL);
308
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000309 /* exec shell / login /whatever */
310 login_argv[0] = loginpath;
311 login_argv[1] = NULL;
312 execv(loginpath, (char **)login_argv);
313 /* Hmmm... this gets sent to the client thru fd#2! Is it ok?? */
314 bb_perror_msg_and_die("execv %s", loginpath);
Eric Andersen08a72202002-09-30 20:52:10 +0000315}
316
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000317#if ENABLE_FEATURE_TELNETD_STANDALONE
318
Eric Andersen08a72202002-09-30 20:52:10 +0000319static void
320free_session(struct tsession *ts)
321{
322 struct tsession *t = sessions;
323
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000324 /* unlink this telnet session from the session list */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000325 if (t == ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000326 sessions = ts->next;
327 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000328 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000329 t = t->next;
330 t->next = ts->next;
331 }
332
333 kill(ts->shell_pid, SIGKILL);
Eric Andersen08a72202002-09-30 20:52:10 +0000334 wait4(ts->shell_pid, NULL, 0, NULL);
Eric Andersen08a72202002-09-30 20:52:10 +0000335 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000336 close(ts->sockfd_read);
337 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
338 close(ts->sockfd_write);
Eric Andersen08a72202002-09-30 20:52:10 +0000339 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000340
341 /* scan all sessions and find new maxfd */
Denis Vlasenko150f4022007-01-13 21:06:21 +0000342 ts = sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000343 maxfd = 0;
344 while (ts) {
345 if (maxfd < ts->ptyfd)
346 maxfd = ts->ptyfd;
347 if (maxfd < ts->sockfd_read)
348 maxfd = ts->sockfd_read;
349 if (maxfd < ts->sockfd_write)
350 maxfd = ts->sockfd_write;
351 ts = ts->next;
352 }
Eric Andersen08a72202002-09-30 20:52:10 +0000353}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000354
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000355#else /* !FEATURE_TELNETD_STANDALONE */
356
357/* Never actually called */
358void free_session(struct tsession *ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000359
360#endif
361
Eric Andersen08a72202002-09-30 20:52:10 +0000362
Denis Vlasenko06af2162007-02-03 17:28:39 +0000363int telnetd_main(int argc, char **argv);
364int telnetd_main(int argc, char **argv)
Eric Andersen08a72202002-09-30 20:52:10 +0000365{
Eric Andersen08a72202002-09-30 20:52:10 +0000366 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000367 unsigned opt;
368 int selret, maxlen, w, r;
369 struct tsession *ts;
370#if ENABLE_FEATURE_TELNETD_STANDALONE
371#define IS_INETD (opt & OPT_INETD)
372 int master_fd = -1; /* be happy, gcc */
Denis Vlasenko13858992006-10-08 12:49:22 +0000373 unsigned portnbr = 23;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000374 char *opt_bindaddr = NULL;
375 char *opt_portnbr;
376#else
377 enum {
378 IS_INETD = 1,
379 master_fd = -1,
380 portnbr = 23,
381 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000382#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000383 enum {
384 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
385 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
386 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
387 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000388
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000389 opt = getopt32(argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
Denis Vlasenko0e87d342006-09-22 08:50:29 +0000390 &issuefile, &loginpath
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000391 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
392 /* Redirect log to syslog early, if needed */
393 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
394 openlog(applet_name, 0, LOG_USER);
395 logmode = LOGMODE_SYSLOG;
396 }
Denis Vlasenko0e87d342006-09-22 08:50:29 +0000397 //if (opt & 1) // -f
398 //if (opt & 2) // -l
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000399 USE_FEATURE_TELNETD_STANDALONE(
400 if (opt & OPT_PORT) // -p
401 portnbr = xatou16(opt_portnbr);
402 //if (opt & 8) // -b
403 //if (opt & 0x10) // -F
404 //if (opt & 0x20) // -i
405 );
Eric Andersen08a72202002-09-30 20:52:10 +0000406
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000407 /* Used to check access(loginpath, X_OK) here. Pointless.
408 * exec will do this for us for free later. */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000409
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000410#if ENABLE_FEATURE_TELNETD_STANDALONE
411 if (IS_INETD) {
412 sessions = make_new_session(0, 1);
413 } else {
Denis Vlasenko9de420c2007-01-10 09:28:01 +0000414 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
Denis Vlasenkoc8717cd2006-11-22 16:10:39 +0000415 xlisten(master_fd, 1);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000416 if (!(opt & OPT_FOREGROUND))
Denis Vlasenko5a142022007-03-26 13:20:54 +0000417 bb_daemonize(DAEMON_CHDIR_ROOT);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000418 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000419#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000420 sessions = make_new_session();
Rob Landley00e76cb2005-05-10 23:53:33 +0000421#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000422
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000423 /* We don't want to die if just one session is broken */
424 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000425
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000426 again:
427 FD_ZERO(&rdfdset);
428 FD_ZERO(&wrfdset);
429 if (!IS_INETD) {
Eric Andersen08a72202002-09-30 20:52:10 +0000430 FD_SET(master_fd, &rdfdset);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000431 /* This is needed because free_session() does not
432 * take into account master_fd when it finds new
433 * maxfd among remaining fd's: */
434 if (master_fd > maxfd)
435 maxfd = master_fd;
436 }
Eric Andersen08a72202002-09-30 20:52:10 +0000437
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000438 /* select on the master socket, all telnet sockets and their
439 * ptys if there is room in their session buffers. */
440 ts = sessions;
441 while (ts) {
442 /* buf1 is used from socket to pty
443 * buf2 is used from pty to socket */
444 if (ts->size1 > 0) /* can write to pty */
445 FD_SET(ts->ptyfd, &wrfdset);
446 if (ts->size1 < BUFSIZE) /* can read from socket */
447 FD_SET(ts->sockfd_read, &rdfdset);
448 if (ts->size2 > 0) /* can write to socket */
449 FD_SET(ts->sockfd_write, &wrfdset);
450 if (ts->size2 < BUFSIZE) /* can read from pty */
451 FD_SET(ts->ptyfd, &rdfdset);
452 ts = ts->next;
453 }
454
455 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
456 if (!selret)
457 return 0;
458
459#if ENABLE_FEATURE_TELNETD_STANDALONE
460 /* First check for and accept new sessions. */
461 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000462 int fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000463 struct tsession *new_ts;
464
Denis Vlasenko2c916522007-01-12 14:57:37 +0000465 fd = accept(master_fd, NULL, 0);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000466 if (fd < 0)
467 goto again;
468 /* Create a new session and link it into our active list */
469 new_ts = make_new_session(fd, fd);
470 if (new_ts) {
471 new_ts->next = sessions;
472 sessions = new_ts;
473 } else {
474 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000475 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000476 }
477#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000478
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000479 /* Then check for data tunneling. */
480 ts = sessions;
481 while (ts) { /* For all sessions... */
482 struct tsession *next = ts->next; /* in case we free ts. */
Eric Andersen08a72202002-09-30 20:52:10 +0000483
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000484 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
485 int num_totty;
486 char *ptr;
487 /* Write to pty from buffer 1. */
488 ptr = remove_iacs(ts, &num_totty);
489 w = safe_write(ts->ptyfd, ptr, num_totty);
490 /* needed? if (w < 0 && errno == EAGAIN) continue; */
491 if (w < 0) {
492 if (IS_INETD)
493 return 0;
494 free_session(ts);
495 ts = next;
Eric Andersen08a72202002-09-30 20:52:10 +0000496 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000497 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000498 ts->wridx1 += w;
499 ts->size1 -= w;
500 if (ts->wridx1 == BUFSIZE)
Eric Andersen08a72202002-09-30 20:52:10 +0000501 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000502 }
Eric Andersen08a72202002-09-30 20:52:10 +0000503
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000504 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
505 /* Write to socket from buffer 2. */
506 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
507 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
508 /* needed? if (w < 0 && errno == EAGAIN) continue; */
509 if (w < 0) {
510 if (IS_INETD)
511 return 0;
512 free_session(ts);
513 ts = next;
514 continue;
515 }
516 ts->wridx2 += w;
517 ts->size2 -= w;
518 if (ts->wridx2 == BUFSIZE)
519 ts->wridx2 = 0;
520 }
Eric Andersen08a72202002-09-30 20:52:10 +0000521
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000522 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
523 /* Read from socket to buffer 1. */
524 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
525 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
526 if (r < 0 && errno == EAGAIN) continue;
527 if (r <= 0) {
528 if (IS_INETD)
529 return 0;
530 free_session(ts);
531 ts = next;
532 continue;
533 }
534 if (!ts->buf1[ts->rdidx1 + r - 1])
535 if (!--r)
536 continue;
537 ts->rdidx1 += r;
538 ts->size1 += r;
539 if (ts->rdidx1 == BUFSIZE)
540 ts->rdidx1 = 0;
541 }
542
543 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
544 /* Read from pty to buffer 2. */
545 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
546 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
547 if (r < 0 && errno == EAGAIN) continue;
548 if (r <= 0) {
549 if (IS_INETD)
550 return 0;
551 free_session(ts);
552 ts = next;
553 continue;
554 }
555 ts->rdidx2 += r;
556 ts->size2 += r;
557 if (ts->rdidx2 == BUFSIZE)
558 ts->rdidx2 = 0;
559 }
560
561 if (ts->size1 == 0) {
562 ts->rdidx1 = 0;
563 ts->wridx1 = 0;
564 }
565 if (ts->size2 == 0) {
566 ts->rdidx2 = 0;
567 ts->wridx2 = 0;
568 }
569 ts = next;
570 }
571 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000572}