blob: 5c0463bab6ef51a6b1b260fcd2f3254de62ca3bc [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
24/*#define DEBUG 1 */
Denis Vlasenko75f8d082006-11-22 15:54:52 +000025#define DEBUG 0
Eric Andersen08a72202002-09-30 20:52:10 +000026
Rob Landley099ed502006-08-28 09:41:49 +000027#include "busybox.h"
28
Denis Vlasenko75f8d082006-11-22 15:54:52 +000029#if DEBUG
Eric Andersen08a72202002-09-30 20:52:10 +000030#define TELCMDS
31#define TELOPTS
32#endif
33#include <arpa/telnet.h>
Eric Andersen08a72202002-09-30 20:52:10 +000034#include <sys/syslog.h>
35
Eric Andersen08a72202002-09-30 20:52:10 +000036
37#define BUFSIZE 4000
38
Denis Vlasenko75f8d082006-11-22 15:54:52 +000039#if ENABLE_LOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000040static const char *loginpath = "/bin/login";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000041#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +000042static const char *loginpath = DEFAULT_SHELL;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000043#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +000044
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000045static const char *issuefile = "/etc/issue.net";
Eric Andersen08a72202002-09-30 20:52:10 +000046
47/* shell name and arguments */
48
Denis Vlasenko75f8d082006-11-22 15:54:52 +000049static const char *argv_init[2];
Eric Andersen08a72202002-09-30 20:52:10 +000050
51/* structure that describes a session */
52
53struct tsession {
54 struct tsession *next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +000055 int sockfd_read, sockfd_write, ptyfd;
Eric Andersen08a72202002-09-30 20:52:10 +000056 int shell_pid;
57 /* two circular buffers */
58 char *buf1, *buf2;
59 int rdidx1, wridx1, size1;
60 int rdidx2, wridx2, size2;
61};
62
63/*
Eric Andersen08a72202002-09-30 20:52:10 +000064 This is how the buffers are used. The arrows indicate the movement
65 of data.
66
67 +-------+ wridx1++ +------+ rdidx1++ +----------+
68 | | <-------------- | buf1 | <-------------- | |
69 | | size1-- +------+ size1++ | |
Mike Frysingerd2c8fd62006-05-10 17:17:09 +000070 | pty | | socket |
Eric Andersen08a72202002-09-30 20:52:10 +000071 | | rdidx2++ +------+ wridx2++ | |
72 | | --------------> | buf2 | --------------> | |
73 +-------+ size2++ +------+ size2-- +----------+
74
75 Each session has got two buffers.
Eric Andersen08a72202002-09-30 20:52:10 +000076*/
77
78static int maxfd;
79
80static struct tsession *sessions;
81
82
83/*
Bernhard Reutner-Fischere0387a62006-06-07 13:31:59 +000084 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
Eric Andersen08a72202002-09-30 20:52:10 +000085 and must be removed so as to not be interpreted by the terminal). Make an
86 uninterrupted string of characters fit for the terminal. Do this by packing
87 all characters meant for the terminal sequentially towards the end of bf.
88
89 Return a pointer to the beginning of the characters meant for the terminal.
90 and make *num_totty the number of characters that should be sent to
91 the terminal.
92
93 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
94 past (bf + len) then that IAC will be left unprocessed and *processed will be
95 less than len.
96
97 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
98 what is the escape character? We aren't handling that situation here.
99
Eric Andersen3752d332003-12-19 11:30:13 +0000100 CR-LF ->'s CR mapping is also done here, for convenience
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000101 */
Eric Andersen08a72202002-09-30 20:52:10 +0000102static char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000103remove_iacs(struct tsession *ts, int *pnum_totty)
104{
Eric Andersen0cb6f352006-01-30 22:30:41 +0000105 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000106 unsigned char *ptr = ptr0;
107 unsigned char *totty = ptr;
108 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
109 int processed;
110 int num_totty;
111
112 while (ptr < end) {
113 if (*ptr != IAC) {
Eric Andersen3752d332003-12-19 11:30:13 +0000114 int c = *ptr;
Eric Andersen08a72202002-09-30 20:52:10 +0000115 *totty++ = *ptr++;
Eric Andersen3752d332003-12-19 11:30:13 +0000116 /* We now map \r\n ==> \r for pragmatic reasons.
117 * Many client implementations send \r\n when
118 * the user hits the CarriageReturn key.
119 */
120 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
121 ptr++;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000122 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000123 /*
124 * TELOPT_NAWS support!
125 */
126 if ((ptr+2) >= end) {
Eric Andersen08a72202002-09-30 20:52:10 +0000127 /* only the beginning of the IAC is in the
128 buffer we were asked to process, we can't
129 process this char. */
130 break;
131 }
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000132
133 /*
134 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
135 */
136 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
137 struct winsize ws;
138 if ((ptr+8) >= end)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000139 break; /* incomplete, can't process */
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000140 ws.ws_col = (ptr[3] << 8) | ptr[4];
141 ws.ws_row = (ptr[5] << 8) | ptr[6];
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000142 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000143 ptr += 9;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000144 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000145 /* skip 3-byte IAC non-SB cmd */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000146#if DEBUG
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000147 fprintf(stderr, "Ignoring IAC %s,%s\n",
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000148 TELCMD(ptr[1]), TELOPT(ptr[2]));
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000149#endif
150 ptr += 3;
151 }
Eric Andersen08a72202002-09-30 20:52:10 +0000152 }
153 }
154
155 processed = ptr - ptr0;
156 num_totty = totty - ptr0;
157 /* the difference between processed and num_to tty
158 is all the iacs we removed from the stream.
159 Adjust buf1 accordingly. */
160 ts->wridx1 += processed - num_totty;
161 ts->size1 -= processed - num_totty;
162 *pnum_totty = num_totty;
163 /* move the chars meant for the terminal towards the end of the
164 buffer. */
165 return memmove(ptr - num_totty, ptr0, num_totty);
166}
167
168
169static int
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000170getpty(char *line, int size)
Eric Andersen08a72202002-09-30 20:52:10 +0000171{
172 int p;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000173#if ENABLE_FEATURE_DEVPTS
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000174 p = open("/dev/ptmx", O_RDWR);
Eric Andersen08a72202002-09-30 20:52:10 +0000175 if (p > 0) {
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000176 const char *name;
Eric Andersen08a72202002-09-30 20:52:10 +0000177 grantpt(p);
178 unlockpt(p);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000179 name = ptsname(p);
180 if (!name) {
181 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
182 return -1;
183 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000184 safe_strncpy(line, name, size);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000185 return p;
Eric Andersen08a72202002-09-30 20:52:10 +0000186 }
187#else
188 struct stat stb;
189 int i;
190 int j;
191
192 strcpy(line, "/dev/ptyXX");
193
194 for (i = 0; i < 16; i++) {
195 line[8] = "pqrstuvwxyzabcde"[i];
196 line[9] = '0';
197 if (stat(line, &stb) < 0) {
198 continue;
199 }
200 for (j = 0; j < 16; j++) {
201 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000202 if (DEBUG)
203 fprintf(stderr, "Trying to open device: %s\n", line);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000204 p = open(line, O_RDWR | O_NOCTTY);
205 if (p >= 0) {
Eric Andersen08a72202002-09-30 20:52:10 +0000206 line[5] = 't';
207 return p;
208 }
209 }
210 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000211#endif /* FEATURE_DEVPTS */
Eric Andersen08a72202002-09-30 20:52:10 +0000212 return -1;
213}
214
215
216static void
217send_iac(struct tsession *ts, unsigned char command, int option)
218{
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000219 /* We rely on that there is space in the buffer for now. */
Eric Andersen08a72202002-09-30 20:52:10 +0000220 char *b = ts->buf2 + ts->rdidx2;
221 *b++ = IAC;
222 *b++ = command;
223 *b++ = option;
224 ts->rdidx2 += 3;
225 ts->size2 += 3;
226}
227
228
229static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000230make_new_session(
231 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
232 SKIP_FEATURE_TELNETD_STANDALONE(void)
233) {
Eric Andersen08a72202002-09-30 20:52:10 +0000234 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000235 int fd, pid;
Eric Andersen08a72202002-09-30 20:52:10 +0000236 char tty_name[32];
Rob Landley1ec5b292006-05-29 07:42:02 +0000237 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000238
239 ts->buf1 = (char *)(&ts[1]);
240 ts->buf2 = ts->buf1 + BUFSIZE;
241
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000242 /* Got a new connection, set up a tty. */
243 fd = getpty(tty_name, 32);
244 if (fd < 0) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000245 bb_error_msg("all terminals in use");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000246 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000247 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000248 if (fd > maxfd) maxfd = fd;
249 ndelay_on(ts->ptyfd = fd);
250#if ENABLE_FEATURE_TELNETD_STANDALONE
251 if (sock_w > maxfd) maxfd = sock_w;
252 if (sock_r > maxfd) maxfd = sock_r;
253 ndelay_on(ts->sockfd_write = sock_w);
254 ndelay_on(ts->sockfd_read = sock_r);
255#else
256 ts->sockfd_write = 1;
257 /* xzalloc: ts->sockfd_read = 0; */
258 ndelay_on(0);
259 ndelay_on(1);
260#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000261 /* Make the telnet client understand we will echo characters so it
262 * should not do it locally. We don't tell the client to run linemode,
263 * because we want to handle line editing and tab completion and other
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000264 * stuff that requires char-by-char support. */
Eric Andersen08a72202002-09-30 20:52:10 +0000265 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000266 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000267 send_iac(ts, DO, TELOPT_LFLOW);
268 send_iac(ts, WILL, TELOPT_ECHO);
269 send_iac(ts, WILL, TELOPT_SGA);
270
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000271 pid = fork();
272 if (pid < 0) {
273 free(ts);
274 close(fd);
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000275 bb_perror_msg("fork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000276 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000277 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000278 if (pid > 0) {
279 /* parent */
280 ts->shell_pid = pid;
281 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000282 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000283
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000284 /* child */
Eric Andersen08a72202002-09-30 20:52:10 +0000285
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000286 /* make new process group */
287 setsid();
288 tcsetpgrp(0, getpid());
289 /* ^^^ strace says: "ioctl(0, TIOCSPGRP, [pid]) = -1 ENOTTY" -- ??! */
290
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000291 /* open the child's side of the tty. */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000292 /* NB: setsid() disconnects from any previous ctty's. Therefore
293 * we must open child's side of the tty AFTER setsid! */
294 fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000295 dup2(fd, 0);
296 dup2(fd, 1);
297 dup2(fd, 2);
298 while (fd > 2) close(fd--);
Eric Andersen08a72202002-09-30 20:52:10 +0000299
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000300 /* The pseudo-terminal allocated to the client is configured to operate in
301 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
302 tcgetattr(0, &termbuf);
303 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
304 termbuf.c_oflag |= ONLCR|XTABS;
305 termbuf.c_iflag |= ICRNL;
306 termbuf.c_iflag &= ~IXOFF;
307 /*termbuf.c_lflag &= ~ICANON;*/
308 tcsetattr(0, TCSANOW, &termbuf);
309
310 print_login_issue(issuefile, NULL);
311
312 /* exec shell, with correct argv and env */
313 execv(loginpath, (char *const *)argv_init);
314 bb_perror_msg_and_die("execv");
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 Vlasenko75f8d082006-11-22 15:54:52 +0000389 opt = getopt32(argc, 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. */
Eric Andersen08a72202002-09-30 20:52:10 +0000409 argv_init[0] = loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000410
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000411#if ENABLE_FEATURE_TELNETD_STANDALONE
412 if (IS_INETD) {
413 sessions = make_new_session(0, 1);
414 } else {
Denis Vlasenko9de420c2007-01-10 09:28:01 +0000415 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
Denis Vlasenkoc8717cd2006-11-22 16:10:39 +0000416 xlisten(master_fd, 1);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000417 if (!(opt & OPT_FOREGROUND))
Denis Vlasenko5a142022007-03-26 13:20:54 +0000418 bb_daemonize(DAEMON_CHDIR_ROOT);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000419 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000420#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000421 sessions = make_new_session();
Rob Landley00e76cb2005-05-10 23:53:33 +0000422#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000423
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000424 /* We don't want to die if just one session is broken */
425 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000426
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000427 again:
428 FD_ZERO(&rdfdset);
429 FD_ZERO(&wrfdset);
430 if (!IS_INETD) {
Eric Andersen08a72202002-09-30 20:52:10 +0000431 FD_SET(master_fd, &rdfdset);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000432 /* This is needed because free_session() does not
433 * take into account master_fd when it finds new
434 * maxfd among remaining fd's: */
435 if (master_fd > maxfd)
436 maxfd = master_fd;
437 }
Eric Andersen08a72202002-09-30 20:52:10 +0000438
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000439 /* select on the master socket, all telnet sockets and their
440 * ptys if there is room in their session buffers. */
441 ts = sessions;
442 while (ts) {
443 /* buf1 is used from socket to pty
444 * buf2 is used from pty to socket */
445 if (ts->size1 > 0) /* can write to pty */
446 FD_SET(ts->ptyfd, &wrfdset);
447 if (ts->size1 < BUFSIZE) /* can read from socket */
448 FD_SET(ts->sockfd_read, &rdfdset);
449 if (ts->size2 > 0) /* can write to socket */
450 FD_SET(ts->sockfd_write, &wrfdset);
451 if (ts->size2 < BUFSIZE) /* can read from pty */
452 FD_SET(ts->ptyfd, &rdfdset);
453 ts = ts->next;
454 }
455
456 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
457 if (!selret)
458 return 0;
459
460#if ENABLE_FEATURE_TELNETD_STANDALONE
461 /* First check for and accept new sessions. */
462 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000463 int fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000464 struct tsession *new_ts;
465
Denis Vlasenko2c916522007-01-12 14:57:37 +0000466 fd = accept(master_fd, NULL, 0);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000467 if (fd < 0)
468 goto again;
469 /* Create a new session and link it into our active list */
470 new_ts = make_new_session(fd, fd);
471 if (new_ts) {
472 new_ts->next = sessions;
473 sessions = new_ts;
474 } else {
475 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000476 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000477 }
478#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000479
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000480 /* Then check for data tunneling. */
481 ts = sessions;
482 while (ts) { /* For all sessions... */
483 struct tsession *next = ts->next; /* in case we free ts. */
Eric Andersen08a72202002-09-30 20:52:10 +0000484
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000485 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
486 int num_totty;
487 char *ptr;
488 /* Write to pty from buffer 1. */
489 ptr = remove_iacs(ts, &num_totty);
490 w = safe_write(ts->ptyfd, ptr, num_totty);
491 /* needed? if (w < 0 && errno == EAGAIN) continue; */
492 if (w < 0) {
493 if (IS_INETD)
494 return 0;
495 free_session(ts);
496 ts = next;
Eric Andersen08a72202002-09-30 20:52:10 +0000497 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000498 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000499 ts->wridx1 += w;
500 ts->size1 -= w;
501 if (ts->wridx1 == BUFSIZE)
Eric Andersen08a72202002-09-30 20:52:10 +0000502 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000503 }
Eric Andersen08a72202002-09-30 20:52:10 +0000504
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000505 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
506 /* Write to socket from buffer 2. */
507 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
508 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
509 /* needed? if (w < 0 && errno == EAGAIN) continue; */
510 if (w < 0) {
511 if (IS_INETD)
512 return 0;
513 free_session(ts);
514 ts = next;
515 continue;
516 }
517 ts->wridx2 += w;
518 ts->size2 -= w;
519 if (ts->wridx2 == BUFSIZE)
520 ts->wridx2 = 0;
521 }
Eric Andersen08a72202002-09-30 20:52:10 +0000522
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000523 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
524 /* Read from socket to buffer 1. */
525 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
526 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
527 if (r < 0 && errno == EAGAIN) continue;
528 if (r <= 0) {
529 if (IS_INETD)
530 return 0;
531 free_session(ts);
532 ts = next;
533 continue;
534 }
535 if (!ts->buf1[ts->rdidx1 + r - 1])
536 if (!--r)
537 continue;
538 ts->rdidx1 += r;
539 ts->size1 += r;
540 if (ts->rdidx1 == BUFSIZE)
541 ts->rdidx1 = 0;
542 }
543
544 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
545 /* Read from pty to buffer 2. */
546 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
547 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
548 if (r < 0 && errno == EAGAIN) continue;
549 if (r <= 0) {
550 if (IS_INETD)
551 return 0;
552 free_session(ts);
553 ts = next;
554 continue;
555 }
556 ts->rdidx2 += r;
557 ts->size2 += r;
558 if (ts->rdidx2 == BUFSIZE)
559 ts->rdidx2 = 0;
560 }
561
562 if (ts->size1 == 0) {
563 ts->rdidx1 = 0;
564 ts->wridx1 = 0;
565 }
566 if (ts->size2 == 0) {
567 ts->rdidx2 = 0;
568 ts->wridx2 = 0;
569 }
570 ts = next;
571 }
572 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000573}