blob: 87f44ce51ff7d7fc484cbf45e56e4823c9a62c93 [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 */
Mike Frysinger772a3462006-05-10 17:14:32 +000025#undef DEBUG
Eric Andersen08a72202002-09-30 20:52:10 +000026
Rob Landley099ed502006-08-28 09:41:49 +000027#include "busybox.h"
28
Eric Andersen08a72202002-09-30 20:52:10 +000029#ifdef DEBUG
30#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
Rob Landley00e76cb2005-05-10 23:53:33 +000039#ifdef CONFIG_FEATURE_IPV6
40#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
47
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000048#ifdef CONFIG_LOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000049static const char *loginpath = "/bin/login";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000050#else
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000051static const char *loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000052#endif
53static const char *issuefile = "/etc/issue.net";
Eric Andersen08a72202002-09-30 20:52:10 +000054
55/* shell name and arguments */
56
57static const char *argv_init[] = {NULL, NULL};
58
59/* structure that describes a session */
60
61struct tsession {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000062#ifdef CONFIG_FEATURE_TELNETD_INETD
63 int sockfd_read, sockfd_write, ptyfd;
64#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +000065 struct tsession *next;
66 int sockfd, ptyfd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000067#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +000068 int shell_pid;
69 /* two circular buffers */
70 char *buf1, *buf2;
71 int rdidx1, wridx1, size1;
72 int rdidx2, wridx2, size2;
73};
74
75/*
76
77 This is how the buffers are used. The arrows indicate the movement
78 of data.
79
80 +-------+ wridx1++ +------+ rdidx1++ +----------+
81 | | <-------------- | buf1 | <-------------- | |
82 | | size1-- +------+ size1++ | |
Mike Frysingerd2c8fd62006-05-10 17:17:09 +000083 | pty | | socket |
Eric Andersen08a72202002-09-30 20:52:10 +000084 | | rdidx2++ +------+ wridx2++ | |
85 | | --------------> | buf2 | --------------> | |
86 +-------+ size2++ +------+ size2-- +----------+
87
88 Each session has got two buffers.
89
90*/
91
92static int maxfd;
93
94static struct tsession *sessions;
95
96
97/*
98
Bernhard Reutner-Fischere0387a62006-06-07 13:31:59 +000099 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
Eric Andersen08a72202002-09-30 20:52:10 +0000100 and must be removed so as to not be interpreted by the terminal). Make an
101 uninterrupted string of characters fit for the terminal. Do this by packing
102 all characters meant for the terminal sequentially towards the end of bf.
103
104 Return a pointer to the beginning of the characters meant for the terminal.
105 and make *num_totty the number of characters that should be sent to
106 the terminal.
107
108 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
109 past (bf + len) then that IAC will be left unprocessed and *processed will be
110 less than len.
111
112 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
113 what is the escape character? We aren't handling that situation here.
114
Eric Andersen3752d332003-12-19 11:30:13 +0000115 CR-LF ->'s CR mapping is also done here, for convenience
116
Eric Andersen08a72202002-09-30 20:52:10 +0000117 */
118static char *
119remove_iacs(struct tsession *ts, int *pnum_totty) {
Eric Andersen0cb6f352006-01-30 22:30:41 +0000120 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000121 unsigned char *ptr = ptr0;
122 unsigned char *totty = ptr;
123 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
124 int processed;
125 int num_totty;
126
127 while (ptr < end) {
128 if (*ptr != IAC) {
Eric Andersen3752d332003-12-19 11:30:13 +0000129 int c = *ptr;
Eric Andersen08a72202002-09-30 20:52:10 +0000130 *totty++ = *ptr++;
Eric Andersen3752d332003-12-19 11:30:13 +0000131 /* We now map \r\n ==> \r for pragmatic reasons.
132 * Many client implementations send \r\n when
133 * the user hits the CarriageReturn key.
134 */
135 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
136 ptr++;
Eric Andersen08a72202002-09-30 20:52:10 +0000137 }
138 else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000139 /*
140 * TELOPT_NAWS support!
141 */
142 if ((ptr+2) >= end) {
Eric Andersen08a72202002-09-30 20:52:10 +0000143 /* only the beginning of the IAC is in the
144 buffer we were asked to process, we can't
145 process this char. */
146 break;
147 }
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000148
149 /*
150 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
151 */
152 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
153 struct winsize ws;
154 if ((ptr+8) >= end)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000155 break; /* incomplete, can't process */
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000156 ws.ws_col = (ptr[3] << 8) | ptr[4];
157 ws.ws_row = (ptr[5] << 8) | ptr[6];
158 (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
159 ptr += 9;
160 }
161 else {
162 /* skip 3-byte IAC non-SB cmd */
163#ifdef DEBUG
164 fprintf(stderr, "Ignoring IAC %s,%s\n",
165 TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
166#endif
167 ptr += 3;
168 }
Eric Andersen08a72202002-09-30 20:52:10 +0000169 }
170 }
171
172 processed = ptr - ptr0;
173 num_totty = totty - ptr0;
174 /* the difference between processed and num_to tty
175 is all the iacs we removed from the stream.
176 Adjust buf1 accordingly. */
177 ts->wridx1 += processed - num_totty;
178 ts->size1 -= processed - num_totty;
179 *pnum_totty = num_totty;
180 /* move the chars meant for the terminal towards the end of the
181 buffer. */
182 return memmove(ptr - num_totty, ptr0, num_totty);
183}
184
185
186static int
187getpty(char *line)
188{
189 int p;
Glenn L McGrathf234e7c2002-11-10 22:26:19 +0000190#ifdef CONFIG_FEATURE_DEVPTS
Eric Andersen08a72202002-09-30 20:52:10 +0000191 p = open("/dev/ptmx", 2);
192 if (p > 0) {
193 grantpt(p);
194 unlockpt(p);
195 strcpy(line, ptsname(p));
196 return(p);
197 }
198#else
199 struct stat stb;
200 int i;
201 int j;
202
203 strcpy(line, "/dev/ptyXX");
204
205 for (i = 0; i < 16; i++) {
206 line[8] = "pqrstuvwxyzabcde"[i];
207 line[9] = '0';
208 if (stat(line, &stb) < 0) {
209 continue;
210 }
211 for (j = 0; j < 16; j++) {
212 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
Mike Frysinger772a3462006-05-10 17:14:32 +0000213#ifdef DEBUG
214 fprintf(stderr, "Trying to open device: %s\n", line);
215#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000216 if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
217 line[5] = 't';
218 return p;
219 }
220 }
221 }
Glenn L McGrathf234e7c2002-11-10 22:26:19 +0000222#endif /* CONFIG_FEATURE_DEVPTS */
Eric Andersen08a72202002-09-30 20:52:10 +0000223 return -1;
224}
225
226
227static void
228send_iac(struct tsession *ts, unsigned char command, int option)
229{
230 /* We rely on that there is space in the buffer for now. */
231 char *b = ts->buf2 + ts->rdidx2;
232 *b++ = IAC;
233 *b++ = command;
234 *b++ = option;
235 ts->rdidx2 += 3;
236 ts->size2 += 3;
237}
238
239
240static struct tsession *
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000241#ifdef CONFIG_FEATURE_TELNETD_INETD
242make_new_session(void)
243#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000244make_new_session(int sockfd)
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000245#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000246{
247 struct termios termbuf;
248 int pty, pid;
249 char tty_name[32];
Rob Landley1ec5b292006-05-29 07:42:02 +0000250 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000251
252 ts->buf1 = (char *)(&ts[1]);
253 ts->buf2 = ts->buf1 + BUFSIZE;
254
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000255#ifdef CONFIG_FEATURE_TELNETD_INETD
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000256 ts->sockfd_write = 1;
257#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000258 ts->sockfd = sockfd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000259#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000260
Eric Andersen08a72202002-09-30 20:52:10 +0000261 /* Got a new connection, set up a tty and spawn a shell. */
262
263 pty = getpty(tty_name);
264
265 if (pty < 0) {
Mike Frysinger772a3462006-05-10 17:14:32 +0000266 syslog(LOG_ERR, "All terminals in use!");
Eric Andersen08a72202002-09-30 20:52:10 +0000267 return 0;
268 }
269
270 if (pty > maxfd)
271 maxfd = pty;
272
273 ts->ptyfd = pty;
274
275 /* Make the telnet client understand we will echo characters so it
276 * should not do it locally. We don't tell the client to run linemode,
277 * because we want to handle line editing and tab completion and other
278 * stuff that requires char-by-char support.
279 */
280
281 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000282 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000283 send_iac(ts, DO, TELOPT_LFLOW);
284 send_iac(ts, WILL, TELOPT_ECHO);
285 send_iac(ts, WILL, TELOPT_SGA);
286
Eric Andersen08a72202002-09-30 20:52:10 +0000287 if ((pid = fork()) < 0) {
Mike Frysinger62ec21d2006-05-10 15:59:07 +0000288 syslog(LOG_ERR, "Could not fork");
Eric Andersen08a72202002-09-30 20:52:10 +0000289 }
290 if (pid == 0) {
291 /* In child, open the child's side of the tty. */
292 int i;
293
294 for(i = 0; i <= maxfd; i++)
295 close(i);
296 /* make new process group */
297 setsid();
298
299 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
Eric Andersen36adca82004-06-22 10:07:17 +0000300 syslog(LOG_ERR, "Could not open tty");
Eric Andersen08a72202002-09-30 20:52:10 +0000301 exit(1);
Mike Frysinger62ec21d2006-05-10 15:59:07 +0000302 }
Eric Andersen08a72202002-09-30 20:52:10 +0000303 dup(0);
304 dup(0);
305
306 tcsetpgrp(0, getpid());
307
308 /* The pseudo-terminal allocated to the client is configured to operate in
309 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
310 */
311
312 tcgetattr(0, &termbuf);
313 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
314 termbuf.c_oflag |= ONLCR|XTABS;
315 termbuf.c_iflag |= ICRNL;
316 termbuf.c_iflag &= ~IXOFF;
317 /*termbuf.c_lflag &= ~ICANON;*/
318 tcsetattr(0, TCSANOW, &termbuf);
319
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000320 print_login_issue(issuefile, NULL);
321
Eric Andersen08a72202002-09-30 20:52:10 +0000322 /* exec shell, with correct argv and env */
323 execv(loginpath, (char *const *)argv_init);
324
325 /* NOT REACHED */
Eric Andersen36adca82004-06-22 10:07:17 +0000326 syslog(LOG_ERR, "execv error");
Eric Andersen08a72202002-09-30 20:52:10 +0000327 exit(1);
328 }
329
330 ts->shell_pid = pid;
331
332 return ts;
333}
334
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000335#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000336static void
337free_session(struct tsession *ts)
338{
339 struct tsession *t = sessions;
340
341 /* Unlink this telnet session from the session list. */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000342 if (t == ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000343 sessions = ts->next;
344 else {
345 while(t->next != ts)
346 t = t->next;
347 t->next = ts->next;
348 }
349
350 kill(ts->shell_pid, SIGKILL);
351
352 wait4(ts->shell_pid, NULL, 0, NULL);
353
354 close(ts->ptyfd);
355 close(ts->sockfd);
356
Mike Frysinger731f81c2006-05-10 15:23:12 +0000357 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
Eric Andersen08a72202002-09-30 20:52:10 +0000358 maxfd--;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000359 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
Eric Andersen08a72202002-09-30 20:52:10 +0000360 maxfd--;
361
362 free(ts);
363}
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000364#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000365
366int
367telnetd_main(int argc, char **argv)
368{
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000369#ifndef CONFIG_FEATURE_TELNETD_INETD
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000370 sockaddr_type sa;
Eric Andersen08a72202002-09-30 20:52:10 +0000371 int master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000372#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000373 fd_set rdfdset, wrfdset;
374 int selret;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000375#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000376 int on = 1;
377 int portnbr = 23;
Rob Landley64a5f962005-11-10 22:37:40 +0000378 struct in_addr bind_addr = { .s_addr = 0x0 };
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000379#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000380 int c;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000381 static const char options[] =
382#ifdef CONFIG_FEATURE_TELNETD_INETD
383 "f:l:";
384#else /* CONFIG_EATURE_TELNETD_INETD */
Rob Landley64a5f962005-11-10 22:37:40 +0000385 "f:l:p:b:";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000386#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen86f2cce2003-04-25 12:32:37 +0000387 int maxlen, w, r;
Eric Andersen08a72202002-09-30 20:52:10 +0000388
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000389#ifndef CONFIG_LOGIN
390 loginpath = DEFAULT_SHELL;
391#endif
392
Eric Andersen08a72202002-09-30 20:52:10 +0000393 for (;;) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000394 c = getopt( argc, argv, options);
Eric Andersen08a72202002-09-30 20:52:10 +0000395 if (c == EOF) break;
396 switch (c) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000397 case 'f':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000398 issuefile = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000399 break;
400 case 'l':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000401 loginpath = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000402 break;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000403#ifndef CONFIG_FEATURE_TELNETD_INETD
404 case 'p':
405 portnbr = atoi(optarg);
406 break;
Rob Landley64a5f962005-11-10 22:37:40 +0000407 case 'b':
408 if (inet_aton(optarg, &bind_addr) == 0)
409 bb_show_usage();
410 break;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000411#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000412 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000413 bb_show_usage();
Eric Andersen08a72202002-09-30 20:52:10 +0000414 }
415 }
416
417 if (access(loginpath, X_OK) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000418 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
Eric Andersen08a72202002-09-30 20:52:10 +0000419 }
420
421 argv_init[0] = loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000422
Eric Andersen36adca82004-06-22 10:07:17 +0000423 openlog(bb_applet_name, 0, LOG_USER);
424
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000425#ifdef CONFIG_FEATURE_TELNETD_INETD
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000426 maxfd = 1;
Glenn L McGrath8eb214e2003-01-22 21:09:48 +0000427 sessions = make_new_session();
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000428#else /* CONFIG_EATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000429 sessions = 0;
430
431 /* Grab a TCP socket. */
432
Rob Landley099ed502006-08-28 09:41:49 +0000433 master_fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
Eric Andersen08a72202002-09-30 20:52:10 +0000434 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
435
436 /* Set it to listen to specified port. */
437
438 memset((void *)&sa, 0, sizeof(sa));
Rob Landley00e76cb2005-05-10 23:53:33 +0000439#ifdef CONFIG_FEATURE_IPV6
440 sa.sin6_family = AF_INET6;
441 sa.sin6_port = htons(portnbr);
Rob Landley64a5f962005-11-10 22:37:40 +0000442 /* sa.sin6_addr = bind_addr6; */
Rob Landley00e76cb2005-05-10 23:53:33 +0000443#else
Eric Andersen08a72202002-09-30 20:52:10 +0000444 sa.sin_family = AF_INET;
445 sa.sin_port = htons(portnbr);
Rob Landley64a5f962005-11-10 22:37:40 +0000446 sa.sin_addr = bind_addr;
Rob Landley00e76cb2005-05-10 23:53:33 +0000447#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000448
Rob Landley099ed502006-08-28 09:41:49 +0000449 xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa));
450 xlisten(master_fd, 1);
451 xdaemon(0, 0);
Eric Andersen08a72202002-09-30 20:52:10 +0000452
453 maxfd = master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000454#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000455
456 do {
457 struct tsession *ts;
458
459 FD_ZERO(&rdfdset);
460 FD_ZERO(&wrfdset);
461
462 /* select on the master socket, all telnet sockets and their
463 * ptys if there is room in their respective session buffers.
464 */
465
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000466#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000467 FD_SET(master_fd, &rdfdset);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000468#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000469
470 ts = sessions;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000471#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000472 while (ts) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000473#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000474 /* buf1 is used from socket to pty
475 * buf2 is used from pty to socket
476 */
477 if (ts->size1 > 0) {
478 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
479 }
480 if (ts->size1 < BUFSIZE) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000481#ifdef CONFIG_FEATURE_TELNETD_INETD
482 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
483#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000484 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000485#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000486 }
487 if (ts->size2 > 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000488#ifdef CONFIG_FEATURE_TELNETD_INETD
489 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
490#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000491 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000492#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000493 }
494 if (ts->size2 < BUFSIZE) {
495 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
496 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000497#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000498 ts = ts->next;
499 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000500#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000501
502 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
503
504 if (!selret)
505 break;
506
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000507#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000508 /* First check for and accept new sessions. */
509 if (FD_ISSET(master_fd, &rdfdset)) {
Mike Frysinger06b00e82006-05-10 17:18:11 +0000510 int fd;
511 socklen_t salen;
Eric Andersen08a72202002-09-30 20:52:10 +0000512
513 salen = sizeof(sa);
514 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
515 &salen)) < 0) {
516 continue;
517 } else {
518 /* Create a new session and link it into
519 our active list. */
520 struct tsession *new_ts = make_new_session(fd);
521 if (new_ts) {
522 new_ts->next = sessions;
523 sessions = new_ts;
524 if (fd > maxfd)
525 maxfd = fd;
526 } else {
527 close(fd);
528 }
529 }
530 }
531
532 /* Then check for data tunneling. */
533
534 ts = sessions;
535 while (ts) { /* For all sessions... */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000536#endif /* CONFIG_FEATURE_TELNETD_INETD */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000537#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000538 struct tsession *next = ts->next; /* in case we free ts. */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000539#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000540
Eric Andersen08a72202002-09-30 20:52:10 +0000541 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
542 int num_totty;
543 char *ptr;
544 /* Write to pty from buffer 1. */
545
546 ptr = remove_iacs(ts, &num_totty);
547
548 w = write(ts->ptyfd, ptr, num_totty);
549 if (w < 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000550#ifdef CONFIG_FEATURE_TELNETD_INETD
551 exit(0);
552#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000553 free_session(ts);
554 ts = next;
555 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000556#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000557 }
558 ts->wridx1 += w;
559 ts->size1 -= w;
560 if (ts->wridx1 == BUFSIZE)
561 ts->wridx1 = 0;
562 }
563
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000564#ifdef CONFIG_FEATURE_TELNETD_INETD
565 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
566#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000567 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000568#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000569 /* Write to socket from buffer 2. */
570 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000571#ifdef CONFIG_FEATURE_TELNETD_INETD
572 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
573 if (w < 0)
574 exit(0);
575#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000576 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
577 if (w < 0) {
578 free_session(ts);
579 ts = next;
580 continue;
581 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000582#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000583 ts->wridx2 += w;
584 ts->size2 -= w;
585 if (ts->wridx2 == BUFSIZE)
586 ts->wridx2 = 0;
587 }
588
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000589#ifdef CONFIG_FEATURE_TELNETD_INETD
590 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
591#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000592 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000593#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000594 /* Read from socket to buffer 1. */
595 maxlen = MIN(BUFSIZE - ts->rdidx1,
596 BUFSIZE - ts->size1);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000597#ifdef CONFIG_FEATURE_TELNETD_INETD
598 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
599 if (!r || (r < 0 && errno != EINTR))
600 exit(0);
601#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000602 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
603 if (!r || (r < 0 && errno != EINTR)) {
604 free_session(ts);
605 ts = next;
606 continue;
607 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000608#endif /* CONFIG_FEATURE_TELNETD_INETD */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000609 if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
Eric Andersen08a72202002-09-30 20:52:10 +0000610 r--;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000611 if (!r)
Eric Andersen08a72202002-09-30 20:52:10 +0000612 continue;
613 }
614 ts->rdidx1 += r;
615 ts->size1 += r;
616 if (ts->rdidx1 == BUFSIZE)
617 ts->rdidx1 = 0;
618 }
619
620 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
621 /* Read from pty to buffer 2. */
622 maxlen = MIN(BUFSIZE - ts->rdidx2,
623 BUFSIZE - ts->size2);
624 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
625 if (!r || (r < 0 && errno != EINTR)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000626#ifdef CONFIG_FEATURE_TELNETD_INETD
627 exit(0);
628#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000629 free_session(ts);
630 ts = next;
631 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000632#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000633 }
634 ts->rdidx2 += r;
635 ts->size2 += r;
636 if (ts->rdidx2 == BUFSIZE)
637 ts->rdidx2 = 0;
638 }
639
640 if (ts->size1 == 0) {
641 ts->rdidx1 = 0;
642 ts->wridx1 = 0;
643 }
644 if (ts->size2 == 0) {
645 ts->rdidx2 = 0;
646 ts->wridx2 = 0;
647 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000648#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000649 ts = next;
650 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000651#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000652
653 } while (1);
654
655 return 0;
656}