blob: 9f0eedcc80e9591edeca4a5e4a07832c713f5f7d [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
Eric Andersen08a72202002-09-30 20:52:10 +000027#include <sys/socket.h>
28#include <sys/wait.h>
Glenn L McGrath90ed9a02004-02-22 09:45:57 +000029#include <sys/ioctl.h>
Eric Andersen08a72202002-09-30 20:52:10 +000030#include <string.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <errno.h>
34#include <netinet/in.h>
Rob Landley64a5f962005-11-10 22:37:40 +000035#include <arpa/inet.h>
Eric Andersen08a72202002-09-30 20:52:10 +000036#include <fcntl.h>
37#include <stdio.h>
38#include <signal.h>
39#include <termios.h>
40#ifdef DEBUG
41#define TELCMDS
42#define TELOPTS
43#endif
44#include <arpa/telnet.h>
45#include <ctype.h>
46#include <sys/syslog.h>
47
48#include "busybox.h"
49
50#define BUFSIZE 4000
51
Rob Landley00e76cb2005-05-10 23:53:33 +000052#ifdef CONFIG_FEATURE_IPV6
53#define SOCKET_TYPE AF_INET6
54typedef struct sockaddr_in6 sockaddr_type;
55#else
56#define SOCKET_TYPE AF_INET
57typedef struct sockaddr_in sockaddr_type;
58#endif
59
60
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000061#ifdef CONFIG_LOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000062static const char *loginpath = "/bin/login";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000063#else
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000064static const char *loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000065#endif
66static const char *issuefile = "/etc/issue.net";
Eric Andersen08a72202002-09-30 20:52:10 +000067
68/* shell name and arguments */
69
70static const char *argv_init[] = {NULL, NULL};
71
72/* structure that describes a session */
73
74struct tsession {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000075#ifdef CONFIG_FEATURE_TELNETD_INETD
76 int sockfd_read, sockfd_write, ptyfd;
77#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +000078 struct tsession *next;
79 int sockfd, ptyfd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000080#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +000081 int shell_pid;
82 /* two circular buffers */
83 char *buf1, *buf2;
84 int rdidx1, wridx1, size1;
85 int rdidx2, wridx2, size2;
86};
87
88/*
89
90 This is how the buffers are used. The arrows indicate the movement
91 of data.
92
93 +-------+ wridx1++ +------+ rdidx1++ +----------+
94 | | <-------------- | buf1 | <-------------- | |
95 | | size1-- +------+ size1++ | |
Mike Frysingerd2c8fd62006-05-10 17:17:09 +000096 | pty | | socket |
Eric Andersen08a72202002-09-30 20:52:10 +000097 | | rdidx2++ +------+ wridx2++ | |
98 | | --------------> | buf2 | --------------> | |
99 +-------+ size2++ +------+ size2-- +----------+
100
101 Each session has got two buffers.
102
103*/
104
105static int maxfd;
106
107static struct tsession *sessions;
108
109
110/*
111
Bernhard Reutner-Fischere0387a62006-06-07 13:31:59 +0000112 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
Eric Andersen08a72202002-09-30 20:52:10 +0000113 and must be removed so as to not be interpreted by the terminal). Make an
114 uninterrupted string of characters fit for the terminal. Do this by packing
115 all characters meant for the terminal sequentially towards the end of bf.
116
117 Return a pointer to the beginning of the characters meant for the terminal.
118 and make *num_totty the number of characters that should be sent to
119 the terminal.
120
121 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
122 past (bf + len) then that IAC will be left unprocessed and *processed will be
123 less than len.
124
125 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
126 what is the escape character? We aren't handling that situation here.
127
Eric Andersen3752d332003-12-19 11:30:13 +0000128 CR-LF ->'s CR mapping is also done here, for convenience
129
Eric Andersen08a72202002-09-30 20:52:10 +0000130 */
131static char *
132remove_iacs(struct tsession *ts, int *pnum_totty) {
Eric Andersen0cb6f352006-01-30 22:30:41 +0000133 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000134 unsigned char *ptr = ptr0;
135 unsigned char *totty = ptr;
136 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
137 int processed;
138 int num_totty;
139
140 while (ptr < end) {
141 if (*ptr != IAC) {
Eric Andersen3752d332003-12-19 11:30:13 +0000142 int c = *ptr;
Eric Andersen08a72202002-09-30 20:52:10 +0000143 *totty++ = *ptr++;
Eric Andersen3752d332003-12-19 11:30:13 +0000144 /* We now map \r\n ==> \r for pragmatic reasons.
145 * Many client implementations send \r\n when
146 * the user hits the CarriageReturn key.
147 */
148 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
149 ptr++;
Eric Andersen08a72202002-09-30 20:52:10 +0000150 }
151 else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000152 /*
153 * TELOPT_NAWS support!
154 */
155 if ((ptr+2) >= end) {
Eric Andersen08a72202002-09-30 20:52:10 +0000156 /* only the beginning of the IAC is in the
157 buffer we were asked to process, we can't
158 process this char. */
159 break;
160 }
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000161
162 /*
163 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
164 */
165 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
166 struct winsize ws;
167 if ((ptr+8) >= end)
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000168 break; /* incomplete, can't process */
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000169 ws.ws_col = (ptr[3] << 8) | ptr[4];
170 ws.ws_row = (ptr[5] << 8) | ptr[6];
171 (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
172 ptr += 9;
173 }
174 else {
175 /* skip 3-byte IAC non-SB cmd */
176#ifdef DEBUG
177 fprintf(stderr, "Ignoring IAC %s,%s\n",
178 TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
179#endif
180 ptr += 3;
181 }
Eric Andersen08a72202002-09-30 20:52:10 +0000182 }
183 }
184
185 processed = ptr - ptr0;
186 num_totty = totty - ptr0;
187 /* the difference between processed and num_to tty
188 is all the iacs we removed from the stream.
189 Adjust buf1 accordingly. */
190 ts->wridx1 += processed - num_totty;
191 ts->size1 -= processed - num_totty;
192 *pnum_totty = num_totty;
193 /* move the chars meant for the terminal towards the end of the
194 buffer. */
195 return memmove(ptr - num_totty, ptr0, num_totty);
196}
197
198
199static int
200getpty(char *line)
201{
202 int p;
Glenn L McGrathf234e7c2002-11-10 22:26:19 +0000203#ifdef CONFIG_FEATURE_DEVPTS
Eric Andersen08a72202002-09-30 20:52:10 +0000204 p = open("/dev/ptmx", 2);
205 if (p > 0) {
206 grantpt(p);
207 unlockpt(p);
208 strcpy(line, ptsname(p));
209 return(p);
210 }
211#else
212 struct stat stb;
213 int i;
214 int j;
215
216 strcpy(line, "/dev/ptyXX");
217
218 for (i = 0; i < 16; i++) {
219 line[8] = "pqrstuvwxyzabcde"[i];
220 line[9] = '0';
221 if (stat(line, &stb) < 0) {
222 continue;
223 }
224 for (j = 0; j < 16; j++) {
225 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
Mike Frysinger772a3462006-05-10 17:14:32 +0000226#ifdef DEBUG
227 fprintf(stderr, "Trying to open device: %s\n", line);
228#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000229 if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
230 line[5] = 't';
231 return p;
232 }
233 }
234 }
Glenn L McGrathf234e7c2002-11-10 22:26:19 +0000235#endif /* CONFIG_FEATURE_DEVPTS */
Eric Andersen08a72202002-09-30 20:52:10 +0000236 return -1;
237}
238
239
240static void
241send_iac(struct tsession *ts, unsigned char command, int option)
242{
243 /* We rely on that there is space in the buffer for now. */
244 char *b = ts->buf2 + ts->rdidx2;
245 *b++ = IAC;
246 *b++ = command;
247 *b++ = option;
248 ts->rdidx2 += 3;
249 ts->size2 += 3;
250}
251
252
253static struct tsession *
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000254#ifdef CONFIG_FEATURE_TELNETD_INETD
255make_new_session(void)
256#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000257make_new_session(int sockfd)
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000258#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000259{
260 struct termios termbuf;
261 int pty, pid;
262 char tty_name[32];
Rob Landley1ec5b292006-05-29 07:42:02 +0000263 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000264
265 ts->buf1 = (char *)(&ts[1]);
266 ts->buf2 = ts->buf1 + BUFSIZE;
267
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000268#ifdef CONFIG_FEATURE_TELNETD_INETD
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000269 ts->sockfd_write = 1;
270#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000271 ts->sockfd = sockfd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000272#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000273
Eric Andersen08a72202002-09-30 20:52:10 +0000274 /* Got a new connection, set up a tty and spawn a shell. */
275
276 pty = getpty(tty_name);
277
278 if (pty < 0) {
Mike Frysinger772a3462006-05-10 17:14:32 +0000279 syslog(LOG_ERR, "All terminals in use!");
Eric Andersen08a72202002-09-30 20:52:10 +0000280 return 0;
281 }
282
283 if (pty > maxfd)
284 maxfd = pty;
285
286 ts->ptyfd = pty;
287
288 /* Make the telnet client understand we will echo characters so it
289 * should not do it locally. We don't tell the client to run linemode,
290 * because we want to handle line editing and tab completion and other
291 * stuff that requires char-by-char support.
292 */
293
294 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000295 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000296 send_iac(ts, DO, TELOPT_LFLOW);
297 send_iac(ts, WILL, TELOPT_ECHO);
298 send_iac(ts, WILL, TELOPT_SGA);
299
Eric Andersen08a72202002-09-30 20:52:10 +0000300 if ((pid = fork()) < 0) {
Mike Frysinger62ec21d2006-05-10 15:59:07 +0000301 syslog(LOG_ERR, "Could not fork");
Eric Andersen08a72202002-09-30 20:52:10 +0000302 }
303 if (pid == 0) {
304 /* In child, open the child's side of the tty. */
305 int i;
306
307 for(i = 0; i <= maxfd; i++)
308 close(i);
309 /* make new process group */
310 setsid();
311
312 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
Eric Andersen36adca82004-06-22 10:07:17 +0000313 syslog(LOG_ERR, "Could not open tty");
Eric Andersen08a72202002-09-30 20:52:10 +0000314 exit(1);
Mike Frysinger62ec21d2006-05-10 15:59:07 +0000315 }
Eric Andersen08a72202002-09-30 20:52:10 +0000316 dup(0);
317 dup(0);
318
319 tcsetpgrp(0, getpid());
320
321 /* The pseudo-terminal allocated to the client is configured to operate in
322 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
323 */
324
325 tcgetattr(0, &termbuf);
326 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
327 termbuf.c_oflag |= ONLCR|XTABS;
328 termbuf.c_iflag |= ICRNL;
329 termbuf.c_iflag &= ~IXOFF;
330 /*termbuf.c_lflag &= ~ICANON;*/
331 tcsetattr(0, TCSANOW, &termbuf);
332
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000333 print_login_issue(issuefile, NULL);
334
Eric Andersen08a72202002-09-30 20:52:10 +0000335 /* exec shell, with correct argv and env */
336 execv(loginpath, (char *const *)argv_init);
337
338 /* NOT REACHED */
Eric Andersen36adca82004-06-22 10:07:17 +0000339 syslog(LOG_ERR, "execv error");
Eric Andersen08a72202002-09-30 20:52:10 +0000340 exit(1);
341 }
342
343 ts->shell_pid = pid;
344
345 return ts;
346}
347
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000348#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000349static void
350free_session(struct tsession *ts)
351{
352 struct tsession *t = sessions;
353
354 /* Unlink this telnet session from the session list. */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000355 if (t == ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000356 sessions = ts->next;
357 else {
358 while(t->next != ts)
359 t = t->next;
360 t->next = ts->next;
361 }
362
363 kill(ts->shell_pid, SIGKILL);
364
365 wait4(ts->shell_pid, NULL, 0, NULL);
366
367 close(ts->ptyfd);
368 close(ts->sockfd);
369
Mike Frysinger731f81c2006-05-10 15:23:12 +0000370 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
Eric Andersen08a72202002-09-30 20:52:10 +0000371 maxfd--;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000372 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
Eric Andersen08a72202002-09-30 20:52:10 +0000373 maxfd--;
374
375 free(ts);
376}
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000377#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000378
379int
380telnetd_main(int argc, char **argv)
381{
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000382#ifndef CONFIG_FEATURE_TELNETD_INETD
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000383 sockaddr_type sa;
Eric Andersen08a72202002-09-30 20:52:10 +0000384 int master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000385#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000386 fd_set rdfdset, wrfdset;
387 int selret;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000388#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000389 int on = 1;
390 int portnbr = 23;
Rob Landley64a5f962005-11-10 22:37:40 +0000391 struct in_addr bind_addr = { .s_addr = 0x0 };
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000392#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000393 int c;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000394 static const char options[] =
395#ifdef CONFIG_FEATURE_TELNETD_INETD
396 "f:l:";
397#else /* CONFIG_EATURE_TELNETD_INETD */
Rob Landley64a5f962005-11-10 22:37:40 +0000398 "f:l:p:b:";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000399#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen86f2cce2003-04-25 12:32:37 +0000400 int maxlen, w, r;
Eric Andersen08a72202002-09-30 20:52:10 +0000401
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000402#ifndef CONFIG_LOGIN
403 loginpath = DEFAULT_SHELL;
404#endif
405
Eric Andersen08a72202002-09-30 20:52:10 +0000406 for (;;) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000407 c = getopt( argc, argv, options);
Eric Andersen08a72202002-09-30 20:52:10 +0000408 if (c == EOF) break;
409 switch (c) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000410 case 'f':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000411 issuefile = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000412 break;
413 case 'l':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000414 loginpath = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000415 break;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000416#ifndef CONFIG_FEATURE_TELNETD_INETD
417 case 'p':
418 portnbr = atoi(optarg);
419 break;
Rob Landley64a5f962005-11-10 22:37:40 +0000420 case 'b':
421 if (inet_aton(optarg, &bind_addr) == 0)
422 bb_show_usage();
423 break;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000424#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000425 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000426 bb_show_usage();
Eric Andersen08a72202002-09-30 20:52:10 +0000427 }
428 }
429
430 if (access(loginpath, X_OK) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000431 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
Eric Andersen08a72202002-09-30 20:52:10 +0000432 }
433
434 argv_init[0] = loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000435
Eric Andersen36adca82004-06-22 10:07:17 +0000436 openlog(bb_applet_name, 0, LOG_USER);
437
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000438#ifdef CONFIG_FEATURE_TELNETD_INETD
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000439 maxfd = 1;
Glenn L McGrath8eb214e2003-01-22 21:09:48 +0000440 sessions = make_new_session();
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000441#else /* CONFIG_EATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000442 sessions = 0;
443
444 /* Grab a TCP socket. */
445
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +0000446 master_fd = bb_xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
Eric Andersen08a72202002-09-30 20:52:10 +0000447 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
448
449 /* Set it to listen to specified port. */
450
451 memset((void *)&sa, 0, sizeof(sa));
Rob Landley00e76cb2005-05-10 23:53:33 +0000452#ifdef CONFIG_FEATURE_IPV6
453 sa.sin6_family = AF_INET6;
454 sa.sin6_port = htons(portnbr);
Rob Landley64a5f962005-11-10 22:37:40 +0000455 /* sa.sin6_addr = bind_addr6; */
Rob Landley00e76cb2005-05-10 23:53:33 +0000456#else
Eric Andersen08a72202002-09-30 20:52:10 +0000457 sa.sin_family = AF_INET;
458 sa.sin_port = htons(portnbr);
Rob Landley64a5f962005-11-10 22:37:40 +0000459 sa.sin_addr = bind_addr;
Rob Landley00e76cb2005-05-10 23:53:33 +0000460#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000461
Bernhard Reutner-Fischer67f641e2006-04-12 18:24:37 +0000462 bb_xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa));
463 bb_xlisten(master_fd, 1);
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +0000464 bb_xdaemon(0, 0);
Eric Andersen08a72202002-09-30 20:52:10 +0000465
466 maxfd = master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000467#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000468
469 do {
470 struct tsession *ts;
471
472 FD_ZERO(&rdfdset);
473 FD_ZERO(&wrfdset);
474
475 /* select on the master socket, all telnet sockets and their
476 * ptys if there is room in their respective session buffers.
477 */
478
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000479#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000480 FD_SET(master_fd, &rdfdset);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000481#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000482
483 ts = sessions;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000484#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000485 while (ts) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000486#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000487 /* buf1 is used from socket to pty
488 * buf2 is used from pty to socket
489 */
490 if (ts->size1 > 0) {
491 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
492 }
493 if (ts->size1 < BUFSIZE) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000494#ifdef CONFIG_FEATURE_TELNETD_INETD
495 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
496#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000497 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000498#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000499 }
500 if (ts->size2 > 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000501#ifdef CONFIG_FEATURE_TELNETD_INETD
502 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
503#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000504 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000505#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000506 }
507 if (ts->size2 < BUFSIZE) {
508 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
509 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000510#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000511 ts = ts->next;
512 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000513#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000514
515 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
516
517 if (!selret)
518 break;
519
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000520#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000521 /* First check for and accept new sessions. */
522 if (FD_ISSET(master_fd, &rdfdset)) {
Mike Frysinger06b00e82006-05-10 17:18:11 +0000523 int fd;
524 socklen_t salen;
Eric Andersen08a72202002-09-30 20:52:10 +0000525
526 salen = sizeof(sa);
527 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
528 &salen)) < 0) {
529 continue;
530 } else {
531 /* Create a new session and link it into
532 our active list. */
533 struct tsession *new_ts = make_new_session(fd);
534 if (new_ts) {
535 new_ts->next = sessions;
536 sessions = new_ts;
537 if (fd > maxfd)
538 maxfd = fd;
539 } else {
540 close(fd);
541 }
542 }
543 }
544
545 /* Then check for data tunneling. */
546
547 ts = sessions;
548 while (ts) { /* For all sessions... */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000549#endif /* CONFIG_FEATURE_TELNETD_INETD */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000550#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000551 struct tsession *next = ts->next; /* in case we free ts. */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000552#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000553
Eric Andersen08a72202002-09-30 20:52:10 +0000554 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
555 int num_totty;
556 char *ptr;
557 /* Write to pty from buffer 1. */
558
559 ptr = remove_iacs(ts, &num_totty);
560
561 w = write(ts->ptyfd, ptr, num_totty);
562 if (w < 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000563#ifdef CONFIG_FEATURE_TELNETD_INETD
564 exit(0);
565#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000566 free_session(ts);
567 ts = next;
568 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000569#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000570 }
571 ts->wridx1 += w;
572 ts->size1 -= w;
573 if (ts->wridx1 == BUFSIZE)
574 ts->wridx1 = 0;
575 }
576
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000577#ifdef CONFIG_FEATURE_TELNETD_INETD
578 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
579#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000580 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000581#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000582 /* Write to socket from buffer 2. */
583 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000584#ifdef CONFIG_FEATURE_TELNETD_INETD
585 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
586 if (w < 0)
587 exit(0);
588#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000589 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
590 if (w < 0) {
591 free_session(ts);
592 ts = next;
593 continue;
594 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000595#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000596 ts->wridx2 += w;
597 ts->size2 -= w;
598 if (ts->wridx2 == BUFSIZE)
599 ts->wridx2 = 0;
600 }
601
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000602#ifdef CONFIG_FEATURE_TELNETD_INETD
603 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
604#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000605 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000606#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000607 /* Read from socket to buffer 1. */
608 maxlen = MIN(BUFSIZE - ts->rdidx1,
609 BUFSIZE - ts->size1);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000610#ifdef CONFIG_FEATURE_TELNETD_INETD
611 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
612 if (!r || (r < 0 && errno != EINTR))
613 exit(0);
614#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000615 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
616 if (!r || (r < 0 && errno != EINTR)) {
617 free_session(ts);
618 ts = next;
619 continue;
620 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000621#endif /* CONFIG_FEATURE_TELNETD_INETD */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000622 if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
Eric Andersen08a72202002-09-30 20:52:10 +0000623 r--;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000624 if (!r)
Eric Andersen08a72202002-09-30 20:52:10 +0000625 continue;
626 }
627 ts->rdidx1 += r;
628 ts->size1 += r;
629 if (ts->rdidx1 == BUFSIZE)
630 ts->rdidx1 = 0;
631 }
632
633 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
634 /* Read from pty to buffer 2. */
635 maxlen = MIN(BUFSIZE - ts->rdidx2,
636 BUFSIZE - ts->size2);
637 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
638 if (!r || (r < 0 && errno != EINTR)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000639#ifdef CONFIG_FEATURE_TELNETD_INETD
640 exit(0);
641#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000642 free_session(ts);
643 ts = next;
644 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000645#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000646 }
647 ts->rdidx2 += r;
648 ts->size2 += r;
649 if (ts->rdidx2 == BUFSIZE)
650 ts->rdidx2 = 0;
651 }
652
653 if (ts->size1 == 0) {
654 ts->rdidx1 = 0;
655 ts->wridx1 = 0;
656 }
657 if (ts->size2 == 0) {
658 ts->rdidx2 = 0;
659 ts->wridx2 = 0;
660 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000661#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000662 ts = next;
663 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000664#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000665
666 } while (1);
667
668 return 0;
669}