blob: 604f65c91ce0fbf9d5e1495d04cfda3a024932fd [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 +000040typedef struct sockaddr_in6 sockaddr_type;
41#else
Rob Landley00e76cb2005-05-10 23:53:33 +000042typedef struct sockaddr_in sockaddr_type;
43#endif
44
Denis Vlasenko75f8d082006-11-22 15:54:52 +000045#if ENABLE_LOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000046static const char *loginpath = "/bin/login";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000047#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +000048static const char *loginpath = DEFAULT_SHELL;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000049#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +000050
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000051static const char *issuefile = "/etc/issue.net";
Eric Andersen08a72202002-09-30 20:52:10 +000052
53/* shell name and arguments */
54
Denis Vlasenko75f8d082006-11-22 15:54:52 +000055static const char *argv_init[2];
Eric Andersen08a72202002-09-30 20:52:10 +000056
57/* structure that describes a session */
58
59struct tsession {
60 struct tsession *next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +000061 int sockfd_read, sockfd_write, ptyfd;
Eric Andersen08a72202002-09-30 20:52:10 +000062 int shell_pid;
63 /* two circular buffers */
64 char *buf1, *buf2;
65 int rdidx1, wridx1, size1;
66 int rdidx2, wridx2, size2;
67};
68
69/*
Eric Andersen08a72202002-09-30 20:52:10 +000070 This is how the buffers are used. The arrows indicate the movement
71 of data.
72
73 +-------+ wridx1++ +------+ rdidx1++ +----------+
74 | | <-------------- | buf1 | <-------------- | |
75 | | size1-- +------+ size1++ | |
Mike Frysingerd2c8fd62006-05-10 17:17:09 +000076 | pty | | socket |
Eric Andersen08a72202002-09-30 20:52:10 +000077 | | rdidx2++ +------+ wridx2++ | |
78 | | --------------> | buf2 | --------------> | |
79 +-------+ size2++ +------+ size2-- +----------+
80
81 Each session has got two buffers.
Eric Andersen08a72202002-09-30 20:52:10 +000082*/
83
84static int maxfd;
85
86static struct tsession *sessions;
87
88
89/*
Bernhard Reutner-Fischere0387a62006-06-07 13:31:59 +000090 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
Eric Andersen08a72202002-09-30 20:52:10 +000091 and must be removed so as to not be interpreted by the terminal). Make an
92 uninterrupted string of characters fit for the terminal. Do this by packing
93 all characters meant for the terminal sequentially towards the end of bf.
94
95 Return a pointer to the beginning of the characters meant for the terminal.
96 and make *num_totty the number of characters that should be sent to
97 the terminal.
98
99 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
100 past (bf + len) then that IAC will be left unprocessed and *processed will be
101 less than len.
102
103 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
104 what is the escape character? We aren't handling that situation here.
105
Eric Andersen3752d332003-12-19 11:30:13 +0000106 CR-LF ->'s CR mapping is also done here, for convenience
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000107 */
Eric Andersen08a72202002-09-30 20:52:10 +0000108static char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000109remove_iacs(struct tsession *ts, int *pnum_totty)
110{
Eric Andersen0cb6f352006-01-30 22:30:41 +0000111 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000112 unsigned char *ptr = ptr0;
113 unsigned char *totty = ptr;
114 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
115 int processed;
116 int num_totty;
117
118 while (ptr < end) {
119 if (*ptr != IAC) {
Eric Andersen3752d332003-12-19 11:30:13 +0000120 int c = *ptr;
Eric Andersen08a72202002-09-30 20:52:10 +0000121 *totty++ = *ptr++;
Eric Andersen3752d332003-12-19 11:30:13 +0000122 /* We now map \r\n ==> \r for pragmatic reasons.
123 * Many client implementations send \r\n when
124 * the user hits the CarriageReturn key.
125 */
126 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
127 ptr++;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000128 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000129 /*
130 * TELOPT_NAWS support!
131 */
132 if ((ptr+2) >= end) {
Eric Andersen08a72202002-09-30 20:52:10 +0000133 /* only the beginning of the IAC is in the
134 buffer we were asked to process, we can't
135 process this char. */
136 break;
137 }
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000138
139 /*
140 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
141 */
142 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
143 struct winsize ws;
144 if ((ptr+8) >= end)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000145 break; /* incomplete, can't process */
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000146 ws.ws_col = (ptr[3] << 8) | ptr[4];
147 ws.ws_row = (ptr[5] << 8) | ptr[6];
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000148 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000149 ptr += 9;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000150 } else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000151 /* skip 3-byte IAC non-SB cmd */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000152#if DEBUG
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000153 fprintf(stderr, "Ignoring IAC %s,%s\n",
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000154 TELCMD(ptr[1]), TELOPT(ptr[2]));
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000155#endif
156 ptr += 3;
157 }
Eric Andersen08a72202002-09-30 20:52:10 +0000158 }
159 }
160
161 processed = ptr - ptr0;
162 num_totty = totty - ptr0;
163 /* the difference between processed and num_to tty
164 is all the iacs we removed from the stream.
165 Adjust buf1 accordingly. */
166 ts->wridx1 += processed - num_totty;
167 ts->size1 -= processed - num_totty;
168 *pnum_totty = num_totty;
169 /* move the chars meant for the terminal towards the end of the
170 buffer. */
171 return memmove(ptr - num_totty, ptr0, num_totty);
172}
173
174
175static int
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000176getpty(char *line, int size)
Eric Andersen08a72202002-09-30 20:52:10 +0000177{
178 int p;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000179#if ENABLE_FEATURE_DEVPTS
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000180 p = open("/dev/ptmx", O_RDWR);
Eric Andersen08a72202002-09-30 20:52:10 +0000181 if (p > 0) {
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000182 const char *name;
Eric Andersen08a72202002-09-30 20:52:10 +0000183 grantpt(p);
184 unlockpt(p);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000185 name = ptsname(p);
186 if (!name) {
187 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
188 return -1;
189 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000190 safe_strncpy(line, name, size);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000191 return p;
Eric Andersen08a72202002-09-30 20:52:10 +0000192 }
193#else
194 struct stat stb;
195 int i;
196 int j;
197
198 strcpy(line, "/dev/ptyXX");
199
200 for (i = 0; i < 16; i++) {
201 line[8] = "pqrstuvwxyzabcde"[i];
202 line[9] = '0';
203 if (stat(line, &stb) < 0) {
204 continue;
205 }
206 for (j = 0; j < 16; j++) {
207 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000208 if (DEBUG)
209 fprintf(stderr, "Trying to open device: %s\n", line);
Denis Vlasenkoc6ec8c92006-10-15 18:22:05 +0000210 p = open(line, O_RDWR | O_NOCTTY);
211 if (p >= 0) {
Eric Andersen08a72202002-09-30 20:52:10 +0000212 line[5] = 't';
213 return p;
214 }
215 }
216 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000217#endif /* FEATURE_DEVPTS */
Eric Andersen08a72202002-09-30 20:52:10 +0000218 return -1;
219}
220
221
222static void
223send_iac(struct tsession *ts, unsigned char command, int option)
224{
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000225 /* We rely on that there is space in the buffer for now. */
Eric Andersen08a72202002-09-30 20:52:10 +0000226 char *b = ts->buf2 + ts->rdidx2;
227 *b++ = IAC;
228 *b++ = command;
229 *b++ = option;
230 ts->rdidx2 += 3;
231 ts->size2 += 3;
232}
233
234
235static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000236make_new_session(
237 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
238 SKIP_FEATURE_TELNETD_STANDALONE(void)
239) {
Eric Andersen08a72202002-09-30 20:52:10 +0000240 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000241 int fd, pid;
Eric Andersen08a72202002-09-30 20:52:10 +0000242 char tty_name[32];
Rob Landley1ec5b292006-05-29 07:42:02 +0000243 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000244
245 ts->buf1 = (char *)(&ts[1]);
246 ts->buf2 = ts->buf1 + BUFSIZE;
247
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000248 /* Got a new connection, set up a tty. */
249 fd = getpty(tty_name, 32);
250 if (fd < 0) {
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000251 bb_error_msg("all terminals in use");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000252 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000253 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000254 if (fd > maxfd) maxfd = fd;
255 ndelay_on(ts->ptyfd = fd);
256#if ENABLE_FEATURE_TELNETD_STANDALONE
257 if (sock_w > maxfd) maxfd = sock_w;
258 if (sock_r > maxfd) maxfd = sock_r;
259 ndelay_on(ts->sockfd_write = sock_w);
260 ndelay_on(ts->sockfd_read = sock_r);
261#else
262 ts->sockfd_write = 1;
263 /* xzalloc: ts->sockfd_read = 0; */
264 ndelay_on(0);
265 ndelay_on(1);
266#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000267 /* Make the telnet client understand we will echo characters so it
268 * should not do it locally. We don't tell the client to run linemode,
269 * because we want to handle line editing and tab completion and other
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000270 * stuff that requires char-by-char support. */
Eric Andersen08a72202002-09-30 20:52:10 +0000271 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000272 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000273 send_iac(ts, DO, TELOPT_LFLOW);
274 send_iac(ts, WILL, TELOPT_ECHO);
275 send_iac(ts, WILL, TELOPT_SGA);
276
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000277 pid = fork();
278 if (pid < 0) {
279 free(ts);
280 close(fd);
Denis Vlasenko3538b9a2006-09-06 18:36:50 +0000281 bb_perror_msg("fork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000282 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000283 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000284 if (pid > 0) {
285 /* parent */
286 ts->shell_pid = pid;
287 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000288 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000289
290 /* child */
Eric Andersen08a72202002-09-30 20:52:10 +0000291
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000292 /* open the child's side of the tty. */
293 fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
294 dup2(fd, 0);
295 dup2(fd, 1);
296 dup2(fd, 2);
297 while (fd > 2) close(fd--);
298 /* make new process group */
299 setsid();
300 tcsetpgrp(0, getpid());
Eric Andersen08a72202002-09-30 20:52:10 +0000301
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000302 /* The pseudo-terminal allocated to the client is configured to operate in
303 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
304 tcgetattr(0, &termbuf);
305 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
306 termbuf.c_oflag |= ONLCR|XTABS;
307 termbuf.c_iflag |= ICRNL;
308 termbuf.c_iflag &= ~IXOFF;
309 /*termbuf.c_lflag &= ~ICANON;*/
310 tcsetattr(0, TCSANOW, &termbuf);
311
312 print_login_issue(issuefile, NULL);
313
314 /* exec shell, with correct argv and env */
315 execv(loginpath, (char *const *)argv_init);
316 bb_perror_msg_and_die("execv");
Eric Andersen08a72202002-09-30 20:52:10 +0000317}
318
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000319#if ENABLE_FEATURE_TELNETD_STANDALONE
320
Eric Andersen08a72202002-09-30 20:52:10 +0000321static void
322free_session(struct tsession *ts)
323{
324 struct tsession *t = sessions;
325
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000326 /* unlink this telnet session from the session list */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000327 if (t == ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000328 sessions = ts->next;
329 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000330 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000331 t = t->next;
332 t->next = ts->next;
333 }
334
335 kill(ts->shell_pid, SIGKILL);
Eric Andersen08a72202002-09-30 20:52:10 +0000336 wait4(ts->shell_pid, NULL, 0, NULL);
Eric Andersen08a72202002-09-30 20:52:10 +0000337 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000338 close(ts->sockfd_read);
339 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
340 close(ts->sockfd_write);
Eric Andersen08a72202002-09-30 20:52:10 +0000341 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000342
343 /* scan all sessions and find new maxfd */
344 ts = sessions;
345 maxfd = 0;
346 while (ts) {
347 if (maxfd < ts->ptyfd)
348 maxfd = ts->ptyfd;
349 if (maxfd < ts->sockfd_read)
350 maxfd = ts->sockfd_read;
351 if (maxfd < ts->sockfd_write)
352 maxfd = ts->sockfd_write;
353 ts = ts->next;
354 }
Eric Andersen08a72202002-09-30 20:52:10 +0000355}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000356
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000357#else /* !FEATURE_TELNETD_STANDALONE */
358
359/* Never actually called */
360void free_session(struct tsession *ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000361
362#endif
363
Eric Andersen08a72202002-09-30 20:52:10 +0000364
365int
366telnetd_main(int argc, char **argv)
367{
Eric Andersen08a72202002-09-30 20:52:10 +0000368 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000369 unsigned opt;
370 int selret, maxlen, w, r;
371 struct tsession *ts;
372#if ENABLE_FEATURE_TELNETD_STANDALONE
373#define IS_INETD (opt & OPT_INETD)
374 int master_fd = -1; /* be happy, gcc */
Denis Vlasenko13858992006-10-08 12:49:22 +0000375 unsigned portnbr = 23;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000376 char *opt_bindaddr = NULL;
377 char *opt_portnbr;
378#else
379 enum {
380 IS_INETD = 1,
381 master_fd = -1,
382 portnbr = 23,
383 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000384#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000385 enum {
386 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
387 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
388 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
389 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000390
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000391 opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
Denis Vlasenko0e87d342006-09-22 08:50:29 +0000392 &issuefile, &loginpath
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000393 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
394 /* Redirect log to syslog early, if needed */
395 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
396 openlog(applet_name, 0, LOG_USER);
397 logmode = LOGMODE_SYSLOG;
398 }
Denis Vlasenko0e87d342006-09-22 08:50:29 +0000399 //if (opt & 1) // -f
400 //if (opt & 2) // -l
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000401 USE_FEATURE_TELNETD_STANDALONE(
402 if (opt & OPT_PORT) // -p
403 portnbr = xatou16(opt_portnbr);
404 //if (opt & 8) // -b
405 //if (opt & 0x10) // -F
406 //if (opt & 0x20) // -i
407 );
Eric Andersen08a72202002-09-30 20:52:10 +0000408
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000409 /* Used to check access(loginpath, X_OK) here. Pointless.
410 * exec will do this for us for free later. */
Eric Andersen08a72202002-09-30 20:52:10 +0000411 argv_init[0] = loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000412
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000413#if ENABLE_FEATURE_TELNETD_STANDALONE
414 if (IS_INETD) {
415 sessions = make_new_session(0, 1);
416 } else {
Denis Vlasenkoc8717cd2006-11-22 16:10:39 +0000417 master_fd = create_and_bind_socket_ip4or6(opt_bindaddr, portnbr);
418 xlisten(master_fd, 1);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000419 if (!(opt & OPT_FOREGROUND))
420 xdaemon(0, 0);
421 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000422#else
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000423 sessions = make_new_session();
Rob Landley00e76cb2005-05-10 23:53:33 +0000424#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000425
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000426 /* We don't want to die if just one session is broken */
427 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000428
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000429 again:
430 FD_ZERO(&rdfdset);
431 FD_ZERO(&wrfdset);
432 if (!IS_INETD) {
Eric Andersen08a72202002-09-30 20:52:10 +0000433 FD_SET(master_fd, &rdfdset);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000434 /* This is needed because free_session() does not
435 * take into account master_fd when it finds new
436 * maxfd among remaining fd's: */
437 if (master_fd > maxfd)
438 maxfd = master_fd;
439 }
Eric Andersen08a72202002-09-30 20:52:10 +0000440
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000441 /* select on the master socket, all telnet sockets and their
442 * ptys if there is room in their session buffers. */
443 ts = sessions;
444 while (ts) {
445 /* buf1 is used from socket to pty
446 * buf2 is used from pty to socket */
447 if (ts->size1 > 0) /* can write to pty */
448 FD_SET(ts->ptyfd, &wrfdset);
449 if (ts->size1 < BUFSIZE) /* can read from socket */
450 FD_SET(ts->sockfd_read, &rdfdset);
451 if (ts->size2 > 0) /* can write to socket */
452 FD_SET(ts->sockfd_write, &wrfdset);
453 if (ts->size2 < BUFSIZE) /* can read from pty */
454 FD_SET(ts->ptyfd, &rdfdset);
455 ts = ts->next;
456 }
457
458 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
459 if (!selret)
460 return 0;
461
462#if ENABLE_FEATURE_TELNETD_STANDALONE
463 /* First check for and accept new sessions. */
464 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
465 sockaddr_type sa;
466 int fd;
467 socklen_t salen;
468 struct tsession *new_ts;
469
470 salen = sizeof(sa);
471 fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
472 if (fd < 0)
473 goto again;
474 /* Create a new session and link it into our active list */
475 new_ts = make_new_session(fd, fd);
476 if (new_ts) {
477 new_ts->next = sessions;
478 sessions = new_ts;
479 } else {
480 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000481 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000482 }
483#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000484
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000485 /* Then check for data tunneling. */
486 ts = sessions;
487 while (ts) { /* For all sessions... */
488 struct tsession *next = ts->next; /* in case we free ts. */
Eric Andersen08a72202002-09-30 20:52:10 +0000489
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000490 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
491 int num_totty;
492 char *ptr;
493 /* Write to pty from buffer 1. */
494 ptr = remove_iacs(ts, &num_totty);
495 w = safe_write(ts->ptyfd, ptr, num_totty);
496 /* needed? if (w < 0 && errno == EAGAIN) continue; */
497 if (w < 0) {
498 if (IS_INETD)
499 return 0;
500 free_session(ts);
501 ts = next;
Eric Andersen08a72202002-09-30 20:52:10 +0000502 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000503 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000504 ts->wridx1 += w;
505 ts->size1 -= w;
506 if (ts->wridx1 == BUFSIZE)
Eric Andersen08a72202002-09-30 20:52:10 +0000507 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000508 }
Eric Andersen08a72202002-09-30 20:52:10 +0000509
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000510 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
511 /* Write to socket from buffer 2. */
512 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
513 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
514 /* needed? if (w < 0 && errno == EAGAIN) continue; */
515 if (w < 0) {
516 if (IS_INETD)
517 return 0;
518 free_session(ts);
519 ts = next;
520 continue;
521 }
522 ts->wridx2 += w;
523 ts->size2 -= w;
524 if (ts->wridx2 == BUFSIZE)
525 ts->wridx2 = 0;
526 }
Eric Andersen08a72202002-09-30 20:52:10 +0000527
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000528 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
529 /* Read from socket to buffer 1. */
530 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
531 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
532 if (r < 0 && errno == EAGAIN) continue;
533 if (r <= 0) {
534 if (IS_INETD)
535 return 0;
536 free_session(ts);
537 ts = next;
538 continue;
539 }
540 if (!ts->buf1[ts->rdidx1 + r - 1])
541 if (!--r)
542 continue;
543 ts->rdidx1 += r;
544 ts->size1 += r;
545 if (ts->rdidx1 == BUFSIZE)
546 ts->rdidx1 = 0;
547 }
548
549 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
550 /* Read from pty to buffer 2. */
551 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
552 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
553 if (r < 0 && errno == EAGAIN) continue;
554 if (r <= 0) {
555 if (IS_INETD)
556 return 0;
557 free_session(ts);
558 ts = next;
559 continue;
560 }
561 ts->rdidx2 += r;
562 ts->size2 += r;
563 if (ts->rdidx2 == BUFSIZE)
564 ts->rdidx2 = 0;
565 }
566
567 if (ts->size1 == 0) {
568 ts->rdidx1 = 0;
569 ts->wridx1 = 0;
570 }
571 if (ts->size2 == 0) {
572 ts->rdidx2 = 0;
573 ts->wridx2 = 0;
574 }
575 ts = next;
576 }
577 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000578}