blob: 549488507127e2c1cb04858729df8444c6f04fd5 [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_FEATURE_IPV6
Rob Landley00e76cb2005-05-10 23:53:33 +000040#define SOCKET_TYPE AF_INET6
41typedef struct sockaddr_in6 sockaddr_type;
42#else
43#define SOCKET_TYPE AF_INET
44typedef struct sockaddr_in sockaddr_type;
45#endif
46
Denis Vlasenko75f8d082006-11-22 15:54:52 +000047#if ENABLE_LOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000048static const char *loginpath = "/bin/login";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000049#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +000050static const char *loginpath = DEFAULT_SHELL;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000051#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +000052
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000053static const char *issuefile = "/etc/issue.net";
Eric Andersen08a72202002-09-30 20:52:10 +000054
55/* shell name and arguments */
56
Denis Vlasenko75f8d082006-11-22 15:54:52 +000057static const char *argv_init[2];
Eric Andersen08a72202002-09-30 20:52:10 +000058
59/* structure that describes a session */
60
61struct tsession {
62 struct tsession *next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +000063 int sockfd_read, sockfd_write, ptyfd;
Eric Andersen08a72202002-09-30 20:52:10 +000064 int shell_pid;
65 /* two circular buffers */
66 char *buf1, *buf2;
67 int rdidx1, wridx1, size1;
68 int rdidx2, wridx2, size2;
69};
70
71/*
Eric Andersen08a72202002-09-30 20:52:10 +000072 This is how the buffers are used. The arrows indicate the movement
73 of data.
74
75 +-------+ wridx1++ +------+ rdidx1++ +----------+
76 | | <-------------- | buf1 | <-------------- | |
77 | | size1-- +------+ size1++ | |
Mike Frysingerd2c8fd62006-05-10 17:17:09 +000078 | pty | | socket |
Eric Andersen08a72202002-09-30 20:52:10 +000079 | | rdidx2++ +------+ wridx2++ | |
80 | | --------------> | buf2 | --------------> | |
81 +-------+ size2++ +------+ size2-- +----------+
82
83 Each session has got two buffers.
Eric Andersen08a72202002-09-30 20:52:10 +000084*/
85
86static int maxfd;
87
88static struct tsession *sessions;
89
90
91/*
Bernhard Reutner-Fischere0387a62006-06-07 13:31:59 +000092 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
Eric Andersen08a72202002-09-30 20:52:10 +000093 and must be removed so as to not be interpreted by the terminal). Make an
94 uninterrupted string of characters fit for the terminal. Do this by packing
95 all characters meant for the terminal sequentially towards the end of bf.
96
97 Return a pointer to the beginning of the characters meant for the terminal.
98 and make *num_totty the number of characters that should be sent to
99 the terminal.
100
101 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
102 past (bf + len) then that IAC will be left unprocessed and *processed will be
103 less than len.
104
105 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
106 what is the escape character? We aren't handling that situation here.
107
Eric Andersen3752d332003-12-19 11:30:13 +0000108 CR-LF ->'s CR mapping is also done here, for convenience
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000109 */
Eric Andersen08a72202002-09-30 20:52:10 +0000110static char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000111remove_iacs(struct tsession *ts, int *pnum_totty)
112{
Eric Andersen0cb6f352006-01-30 22:30:41 +0000113 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000114 unsigned char *ptr = ptr0;
115 unsigned char *totty = ptr;
116 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
117 int processed;
118 int num_totty;
119
120 while (ptr < end) {
121 if (*ptr != IAC) {
Eric Andersen3752d332003-12-19 11:30:13 +0000122 int c = *ptr;
Eric Andersen08a72202002-09-30 20:52:10 +0000123 *totty++ = *ptr++;
Eric Andersen3752d332003-12-19 11:30:13 +0000124 /* We now map \r\n ==> \r for pragmatic reasons.
125 * Many client implementations send \r\n when
126 * the user hits the CarriageReturn key.
127 */
128 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
129 ptr++;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000130 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000131 /*
132 * TELOPT_NAWS support!
133 */
134 if ((ptr+2) >= end) {
Eric Andersen08a72202002-09-30 20:52:10 +0000135 /* only the beginning of the IAC is in the
136 buffer we were asked to process, we can't
137 process this char. */
138 break;
139 }
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000140
141 /*
142 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
143 */
144 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
145 struct winsize ws;
146 if ((ptr+8) >= end)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000147 break; /* incomplete, can't process */
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000148 ws.ws_col = (ptr[3] << 8) | ptr[4];
149 ws.ws_row = (ptr[5] << 8) | ptr[6];
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000150 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000151 ptr += 9;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000152 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000153 /* skip 3-byte IAC non-SB cmd */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000154#if DEBUG
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000155 fprintf(stderr, "Ignoring IAC %s,%s\n",
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000156 TELCMD(ptr[1]), TELOPT(ptr[2]));
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000157#endif
158 ptr += 3;
159 }
Eric Andersen08a72202002-09-30 20:52:10 +0000160 }
161 }
162
163 processed = ptr - ptr0;
164 num_totty = totty - ptr0;
165 /* the difference between processed and num_to tty
166 is all the iacs we removed from the stream.
167 Adjust buf1 accordingly. */
168 ts->wridx1 += processed - num_totty;
169 ts->size1 -= processed - num_totty;
170 *pnum_totty = num_totty;
171 /* move the chars meant for the terminal towards the end of the
172 buffer. */
173 return memmove(ptr - num_totty, ptr0, num_totty);
174}
175
176
177static int
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000178getpty(char *line, int size)
Eric Andersen08a72202002-09-30 20:52:10 +0000179{
180 int p;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000181#if ENABLE_FEATURE_DEVPTS
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000182 p = open("/dev/ptmx", O_RDWR);
Eric Andersen08a72202002-09-30 20:52:10 +0000183 if (p > 0) {
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000184 const char *name;
Eric Andersen08a72202002-09-30 20:52:10 +0000185 grantpt(p);
186 unlockpt(p);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000187 name = ptsname(p);
188 if (!name) {
189 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
190 return -1;
191 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000192 safe_strncpy(line, name, size);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000193 return p;
Eric Andersen08a72202002-09-30 20:52:10 +0000194 }
195#else
196 struct stat stb;
197 int i;
198 int j;
199
200 strcpy(line, "/dev/ptyXX");
201
202 for (i = 0; i < 16; i++) {
203 line[8] = "pqrstuvwxyzabcde"[i];
204 line[9] = '0';
205 if (stat(line, &stb) < 0) {
206 continue;
207 }
208 for (j = 0; j < 16; j++) {
209 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000210 if (DEBUG)
211 fprintf(stderr, "Trying to open device: %s\n", line);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000212 p = open(line, O_RDWR | O_NOCTTY);
213 if (p >= 0) {
Eric Andersen08a72202002-09-30 20:52:10 +0000214 line[5] = 't';
215 return p;
216 }
217 }
218 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000219#endif /* FEATURE_DEVPTS */
Eric Andersen08a72202002-09-30 20:52:10 +0000220 return -1;
221}
222
223
224static void
225send_iac(struct tsession *ts, unsigned char command, int option)
226{
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000227 /* We rely on that there is space in the buffer for now. */
Eric Andersen08a72202002-09-30 20:52:10 +0000228 char *b = ts->buf2 + ts->rdidx2;
229 *b++ = IAC;
230 *b++ = command;
231 *b++ = option;
232 ts->rdidx2 += 3;
233 ts->size2 += 3;
234}
235
236
237static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000238make_new_session(
239 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
240 SKIP_FEATURE_TELNETD_STANDALONE(void)
241) {
Eric Andersen08a72202002-09-30 20:52:10 +0000242 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000243 int fd, pid;
Eric Andersen08a72202002-09-30 20:52:10 +0000244 char tty_name[32];
Rob Landley1ec5b292006-05-29 07:42:02 +0000245 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000246
247 ts->buf1 = (char *)(&ts[1]);
248 ts->buf2 = ts->buf1 + BUFSIZE;
249
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000250 /* Got a new connection, set up a tty. */
251 fd = getpty(tty_name, 32);
252 if (fd < 0) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000253 bb_error_msg("all terminals in use");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000254 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000255 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000256 if (fd > maxfd) maxfd = fd;
257 ndelay_on(ts->ptyfd = fd);
258#if ENABLE_FEATURE_TELNETD_STANDALONE
259 if (sock_w > maxfd) maxfd = sock_w;
260 if (sock_r > maxfd) maxfd = sock_r;
261 ndelay_on(ts->sockfd_write = sock_w);
262 ndelay_on(ts->sockfd_read = sock_r);
263#else
264 ts->sockfd_write = 1;
265 /* xzalloc: ts->sockfd_read = 0; */
266 ndelay_on(0);
267 ndelay_on(1);
268#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000269 /* Make the telnet client understand we will echo characters so it
270 * should not do it locally. We don't tell the client to run linemode,
271 * because we want to handle line editing and tab completion and other
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000272 * stuff that requires char-by-char support. */
Eric Andersen08a72202002-09-30 20:52:10 +0000273 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000274 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000275 send_iac(ts, DO, TELOPT_LFLOW);
276 send_iac(ts, WILL, TELOPT_ECHO);
277 send_iac(ts, WILL, TELOPT_SGA);
278
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000279 pid = fork();
280 if (pid < 0) {
281 free(ts);
282 close(fd);
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000283 bb_perror_msg("fork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000284 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000285 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000286 if (pid > 0) {
287 /* parent */
288 ts->shell_pid = pid;
289 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000290 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000291
292 /* child */
Eric Andersen08a72202002-09-30 20:52:10 +0000293
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000294 /* open the child's side of the tty. */
295 fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
296 dup2(fd, 0);
297 dup2(fd, 1);
298 dup2(fd, 2);
299 while (fd > 2) close(fd--);
300 /* make new process group */
301 setsid();
302 tcsetpgrp(0, getpid());
Eric Andersen08a72202002-09-30 20:52:10 +0000303
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000304 /* The pseudo-terminal allocated to the client is configured to operate in
305 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
306 tcgetattr(0, &termbuf);
307 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
308 termbuf.c_oflag |= ONLCR|XTABS;
309 termbuf.c_iflag |= ICRNL;
310 termbuf.c_iflag &= ~IXOFF;
311 /*termbuf.c_lflag &= ~ICANON;*/
312 tcsetattr(0, TCSANOW, &termbuf);
313
314 print_login_issue(issuefile, NULL);
315
316 /* exec shell, with correct argv and env */
317 execv(loginpath, (char *const *)argv_init);
318 bb_perror_msg_and_die("execv");
Eric Andersen08a72202002-09-30 20:52:10 +0000319}
320
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000321#if ENABLE_FEATURE_TELNETD_STANDALONE
322
Eric Andersen08a72202002-09-30 20:52:10 +0000323static void
324free_session(struct tsession *ts)
325{
326 struct tsession *t = sessions;
327
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000328 /* unlink this telnet session from the session list */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000329 if (t == ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000330 sessions = ts->next;
331 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000332 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000333 t = t->next;
334 t->next = ts->next;
335 }
336
337 kill(ts->shell_pid, SIGKILL);
Eric Andersen08a72202002-09-30 20:52:10 +0000338 wait4(ts->shell_pid, NULL, 0, NULL);
Eric Andersen08a72202002-09-30 20:52:10 +0000339 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000340 close(ts->sockfd_read);
341 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
342 close(ts->sockfd_write);
Eric Andersen08a72202002-09-30 20:52:10 +0000343 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000344
345 /* scan all sessions and find new maxfd */
346 ts = sessions;
347 maxfd = 0;
348 while (ts) {
349 if (maxfd < ts->ptyfd)
350 maxfd = ts->ptyfd;
351 if (maxfd < ts->sockfd_read)
352 maxfd = ts->sockfd_read;
353 if (maxfd < ts->sockfd_write)
354 maxfd = ts->sockfd_write;
355 ts = ts->next;
356 }
Eric Andersen08a72202002-09-30 20:52:10 +0000357}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000358
359static int
360create_socket(int port, const char *opt_bindaddr)
361{
362 static const int on = 1;
363 int fd;
364 sockaddr_type sa;
365#if !ENABLE_FEATURE_IPV6
366 struct in_addr bind_addr = { .s_addr = 0x0 };
367
368 /* TODO: generic string -> sockaddr converter */
369 if (opt_bindaddr && inet_aton(opt_bindaddr, &bind_addr) == 0)
370 bb_show_usage();
371#endif
372 fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
373 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
374 memset((void *)&sa, 0, sizeof(sa));
375#if ENABLE_FEATURE_IPV6
376 sa.sin6_family = AF_INET6;
377 sa.sin6_port = htons(port);
378 /* sa.sin6_addr = bind_addr6; */
379#else
380 sa.sin_family = AF_INET;
381 sa.sin_port = htons(port);
382 sa.sin_addr = bind_addr;
383#endif
384 xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
385 xlisten(fd, 1);
386 return fd;
387}
388
389#else /* !FEATURE_TELNETD_STANDALONE */
390
391/* Never actually called */
392void free_session(struct tsession *ts);
393int create_socket(int port, const char *opt_bindaddr);
394
395#endif
396
Eric Andersen08a72202002-09-30 20:52:10 +0000397
398int
399telnetd_main(int argc, char **argv)
400{
Eric Andersen08a72202002-09-30 20:52:10 +0000401 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000402 unsigned opt;
403 int selret, maxlen, w, r;
404 struct tsession *ts;
405#if ENABLE_FEATURE_TELNETD_STANDALONE
406#define IS_INETD (opt & OPT_INETD)
407 int master_fd = -1; /* be happy, gcc */
Denis Vlasenko13858992006-10-08 12:49:22 +0000408 unsigned portnbr = 23;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000409 char *opt_bindaddr = NULL;
410 char *opt_portnbr;
411#else
412 enum {
413 IS_INETD = 1,
414 master_fd = -1,
415 portnbr = 23,
416 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000417#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000418 enum {
419 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
420 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
421 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
422 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000423
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000424 opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
Denis Vlasenko0e87d342006-09-22 08:50:29 +0000425 &issuefile, &loginpath
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000426 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
427 /* Redirect log to syslog early, if needed */
428 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
429 openlog(applet_name, 0, LOG_USER);
430 logmode = LOGMODE_SYSLOG;
431 }
Denis Vlasenko0e87d342006-09-22 08:50:29 +0000432 //if (opt & 1) // -f
433 //if (opt & 2) // -l
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000434 USE_FEATURE_TELNETD_STANDALONE(
435 if (opt & OPT_PORT) // -p
436 portnbr = xatou16(opt_portnbr);
437 //if (opt & 8) // -b
438 //if (opt & 0x10) // -F
439 //if (opt & 0x20) // -i
440 );
Eric Andersen08a72202002-09-30 20:52:10 +0000441
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000442 /* Used to check access(loginpath, X_OK) here. Pointless.
443 * exec will do this for us for free later. */
Eric Andersen08a72202002-09-30 20:52:10 +0000444 argv_init[0] = loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000445
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000446#if ENABLE_FEATURE_TELNETD_STANDALONE
447 if (IS_INETD) {
448 sessions = make_new_session(0, 1);
449 } else {
450 master_fd = create_socket(portnbr, opt_bindaddr);
451 if (!(opt & OPT_FOREGROUND))
452 xdaemon(0, 0);
453 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000454#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000455 sessions = make_new_session();
Rob Landley00e76cb2005-05-10 23:53:33 +0000456#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000457
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000458 /* We don't want to die if just one session is broken */
459 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000460
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000461 again:
462 FD_ZERO(&rdfdset);
463 FD_ZERO(&wrfdset);
464 if (!IS_INETD) {
Eric Andersen08a72202002-09-30 20:52:10 +0000465 FD_SET(master_fd, &rdfdset);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000466 /* This is needed because free_session() does not
467 * take into account master_fd when it finds new
468 * maxfd among remaining fd's: */
469 if (master_fd > maxfd)
470 maxfd = master_fd;
471 }
Eric Andersen08a72202002-09-30 20:52:10 +0000472
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000473 /* select on the master socket, all telnet sockets and their
474 * ptys if there is room in their session buffers. */
475 ts = sessions;
476 while (ts) {
477 /* buf1 is used from socket to pty
478 * buf2 is used from pty to socket */
479 if (ts->size1 > 0) /* can write to pty */
480 FD_SET(ts->ptyfd, &wrfdset);
481 if (ts->size1 < BUFSIZE) /* can read from socket */
482 FD_SET(ts->sockfd_read, &rdfdset);
483 if (ts->size2 > 0) /* can write to socket */
484 FD_SET(ts->sockfd_write, &wrfdset);
485 if (ts->size2 < BUFSIZE) /* can read from pty */
486 FD_SET(ts->ptyfd, &rdfdset);
487 ts = ts->next;
488 }
489
490 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
491 if (!selret)
492 return 0;
493
494#if ENABLE_FEATURE_TELNETD_STANDALONE
495 /* First check for and accept new sessions. */
496 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
497 sockaddr_type sa;
498 int fd;
499 socklen_t salen;
500 struct tsession *new_ts;
501
502 salen = sizeof(sa);
503 fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
504 if (fd < 0)
505 goto again;
506 /* Create a new session and link it into our active list */
507 new_ts = make_new_session(fd, fd);
508 if (new_ts) {
509 new_ts->next = sessions;
510 sessions = new_ts;
511 } else {
512 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000513 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000514 }
515#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000516
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000517 /* Then check for data tunneling. */
518 ts = sessions;
519 while (ts) { /* For all sessions... */
520 struct tsession *next = ts->next; /* in case we free ts. */
Eric Andersen08a72202002-09-30 20:52:10 +0000521
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000522 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
523 int num_totty;
524 char *ptr;
525 /* Write to pty from buffer 1. */
526 ptr = remove_iacs(ts, &num_totty);
527 w = safe_write(ts->ptyfd, ptr, num_totty);
528 /* needed? if (w < 0 && errno == EAGAIN) continue; */
529 if (w < 0) {
530 if (IS_INETD)
531 return 0;
532 free_session(ts);
533 ts = next;
Eric Andersen08a72202002-09-30 20:52:10 +0000534 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000535 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000536 ts->wridx1 += w;
537 ts->size1 -= w;
538 if (ts->wridx1 == BUFSIZE)
Eric Andersen08a72202002-09-30 20:52:10 +0000539 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000540 }
Eric Andersen08a72202002-09-30 20:52:10 +0000541
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000542 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
543 /* Write to socket from buffer 2. */
544 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
545 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
546 /* needed? if (w < 0 && errno == EAGAIN) continue; */
547 if (w < 0) {
548 if (IS_INETD)
549 return 0;
550 free_session(ts);
551 ts = next;
552 continue;
553 }
554 ts->wridx2 += w;
555 ts->size2 -= w;
556 if (ts->wridx2 == BUFSIZE)
557 ts->wridx2 = 0;
558 }
Eric Andersen08a72202002-09-30 20:52:10 +0000559
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000560 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
561 /* Read from socket to buffer 1. */
562 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
563 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
564 if (r < 0 && errno == EAGAIN) continue;
565 if (r <= 0) {
566 if (IS_INETD)
567 return 0;
568 free_session(ts);
569 ts = next;
570 continue;
571 }
572 if (!ts->buf1[ts->rdidx1 + r - 1])
573 if (!--r)
574 continue;
575 ts->rdidx1 += r;
576 ts->size1 += r;
577 if (ts->rdidx1 == BUFSIZE)
578 ts->rdidx1 = 0;
579 }
580
581 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
582 /* Read from pty to buffer 2. */
583 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
584 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
585 if (r < 0 && errno == EAGAIN) continue;
586 if (r <= 0) {
587 if (IS_INETD)
588 return 0;
589 free_session(ts);
590 ts = next;
591 continue;
592 }
593 ts->rdidx2 += r;
594 ts->size2 += r;
595 if (ts->rdidx2 == BUFSIZE)
596 ts->rdidx2 = 0;
597 }
598
599 if (ts->size1 == 0) {
600 ts->rdidx1 = 0;
601 ts->wridx1 = 0;
602 }
603 if (ts->size2 == 0) {
604 ts->rdidx2 = 0;
605 ts->wridx2 = 0;
606 }
607 ts = next;
608 }
609 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000610}