blob: 5a11ffb8c6acb2c5086a86ce68030b54b00ec03a [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 *
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +00006 * Licensed under GPL, 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
112 Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
113 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];
263 struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
264
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
269 ts->sockfd_read = 0;
270 ts->sockfd_write = 1;
271#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000272 ts->sockfd = sockfd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000273#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000274
275 ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
276 ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
277
278 /* Got a new connection, set up a tty and spawn a shell. */
279
280 pty = getpty(tty_name);
281
282 if (pty < 0) {
Mike Frysinger772a3462006-05-10 17:14:32 +0000283 syslog(LOG_ERR, "All terminals in use!");
Eric Andersen08a72202002-09-30 20:52:10 +0000284 return 0;
285 }
286
287 if (pty > maxfd)
288 maxfd = pty;
289
290 ts->ptyfd = pty;
291
292 /* Make the telnet client understand we will echo characters so it
293 * should not do it locally. We don't tell the client to run linemode,
294 * because we want to handle line editing and tab completion and other
295 * stuff that requires char-by-char support.
296 */
297
298 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000299 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000300 send_iac(ts, DO, TELOPT_LFLOW);
301 send_iac(ts, WILL, TELOPT_ECHO);
302 send_iac(ts, WILL, TELOPT_SGA);
303
Eric Andersen08a72202002-09-30 20:52:10 +0000304 if ((pid = fork()) < 0) {
Mike Frysinger62ec21d2006-05-10 15:59:07 +0000305 syslog(LOG_ERR, "Could not fork");
Eric Andersen08a72202002-09-30 20:52:10 +0000306 }
307 if (pid == 0) {
308 /* In child, open the child's side of the tty. */
309 int i;
310
311 for(i = 0; i <= maxfd; i++)
312 close(i);
313 /* make new process group */
314 setsid();
315
316 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
Eric Andersen36adca82004-06-22 10:07:17 +0000317 syslog(LOG_ERR, "Could not open tty");
Eric Andersen08a72202002-09-30 20:52:10 +0000318 exit(1);
Mike Frysinger62ec21d2006-05-10 15:59:07 +0000319 }
Eric Andersen08a72202002-09-30 20:52:10 +0000320 dup(0);
321 dup(0);
322
323 tcsetpgrp(0, getpid());
324
325 /* The pseudo-terminal allocated to the client is configured to operate in
326 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
327 */
328
329 tcgetattr(0, &termbuf);
330 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
331 termbuf.c_oflag |= ONLCR|XTABS;
332 termbuf.c_iflag |= ICRNL;
333 termbuf.c_iflag &= ~IXOFF;
334 /*termbuf.c_lflag &= ~ICANON;*/
335 tcsetattr(0, TCSANOW, &termbuf);
336
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000337 print_login_issue(issuefile, NULL);
338
Eric Andersen08a72202002-09-30 20:52:10 +0000339 /* exec shell, with correct argv and env */
340 execv(loginpath, (char *const *)argv_init);
341
342 /* NOT REACHED */
Eric Andersen36adca82004-06-22 10:07:17 +0000343 syslog(LOG_ERR, "execv error");
Eric Andersen08a72202002-09-30 20:52:10 +0000344 exit(1);
345 }
346
347 ts->shell_pid = pid;
348
349 return ts;
350}
351
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000352#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000353static void
354free_session(struct tsession *ts)
355{
356 struct tsession *t = sessions;
357
358 /* Unlink this telnet session from the session list. */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000359 if (t == ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000360 sessions = ts->next;
361 else {
362 while(t->next != ts)
363 t = t->next;
364 t->next = ts->next;
365 }
366
367 kill(ts->shell_pid, SIGKILL);
368
369 wait4(ts->shell_pid, NULL, 0, NULL);
370
371 close(ts->ptyfd);
372 close(ts->sockfd);
373
Mike Frysinger731f81c2006-05-10 15:23:12 +0000374 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
Eric Andersen08a72202002-09-30 20:52:10 +0000375 maxfd--;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000376 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
Eric Andersen08a72202002-09-30 20:52:10 +0000377 maxfd--;
378
379 free(ts);
380}
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000381#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000382
383int
384telnetd_main(int argc, char **argv)
385{
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000386#ifndef CONFIG_FEATURE_TELNETD_INETD
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000387 sockaddr_type sa;
Eric Andersen08a72202002-09-30 20:52:10 +0000388 int master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000389#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000390 fd_set rdfdset, wrfdset;
391 int selret;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000392#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000393 int on = 1;
394 int portnbr = 23;
Rob Landley64a5f962005-11-10 22:37:40 +0000395 struct in_addr bind_addr = { .s_addr = 0x0 };
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000396#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000397 int c;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000398 static const char options[] =
399#ifdef CONFIG_FEATURE_TELNETD_INETD
400 "f:l:";
401#else /* CONFIG_EATURE_TELNETD_INETD */
Rob Landley64a5f962005-11-10 22:37:40 +0000402 "f:l:p:b:";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000403#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen86f2cce2003-04-25 12:32:37 +0000404 int maxlen, w, r;
Eric Andersen08a72202002-09-30 20:52:10 +0000405
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000406#ifndef CONFIG_LOGIN
407 loginpath = DEFAULT_SHELL;
408#endif
409
Eric Andersen08a72202002-09-30 20:52:10 +0000410 for (;;) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000411 c = getopt( argc, argv, options);
Eric Andersen08a72202002-09-30 20:52:10 +0000412 if (c == EOF) break;
413 switch (c) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000414 case 'f':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000415 issuefile = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000416 break;
417 case 'l':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000418 loginpath = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000419 break;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000420#ifndef CONFIG_FEATURE_TELNETD_INETD
421 case 'p':
422 portnbr = atoi(optarg);
423 break;
Rob Landley64a5f962005-11-10 22:37:40 +0000424 case 'b':
425 if (inet_aton(optarg, &bind_addr) == 0)
426 bb_show_usage();
427 break;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000428#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000429 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000430 bb_show_usage();
Eric Andersen08a72202002-09-30 20:52:10 +0000431 }
432 }
433
434 if (access(loginpath, X_OK) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000435 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
Eric Andersen08a72202002-09-30 20:52:10 +0000436 }
437
438 argv_init[0] = loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000439
Eric Andersen36adca82004-06-22 10:07:17 +0000440 openlog(bb_applet_name, 0, LOG_USER);
441
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000442#ifdef CONFIG_FEATURE_TELNETD_INETD
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000443 maxfd = 1;
Glenn L McGrath8eb214e2003-01-22 21:09:48 +0000444 sessions = make_new_session();
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000445#else /* CONFIG_EATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000446 sessions = 0;
447
448 /* Grab a TCP socket. */
449
Bernhard Reutner-Fischerdac7ff12006-04-12 17:55:51 +0000450 master_fd = bb_xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
Eric Andersen08a72202002-09-30 20:52:10 +0000451 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
452
453 /* Set it to listen to specified port. */
454
455 memset((void *)&sa, 0, sizeof(sa));
Rob Landley00e76cb2005-05-10 23:53:33 +0000456#ifdef CONFIG_FEATURE_IPV6
457 sa.sin6_family = AF_INET6;
458 sa.sin6_port = htons(portnbr);
Rob Landley64a5f962005-11-10 22:37:40 +0000459 /* sa.sin6_addr = bind_addr6; */
Rob Landley00e76cb2005-05-10 23:53:33 +0000460#else
Eric Andersen08a72202002-09-30 20:52:10 +0000461 sa.sin_family = AF_INET;
462 sa.sin_port = htons(portnbr);
Rob Landley64a5f962005-11-10 22:37:40 +0000463 sa.sin_addr = bind_addr;
Rob Landley00e76cb2005-05-10 23:53:33 +0000464#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000465
Bernhard Reutner-Fischer67f641e2006-04-12 18:24:37 +0000466 bb_xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa));
467 bb_xlisten(master_fd, 1);
Bernhard Reutner-Fischer2c998512006-04-12 18:09:26 +0000468 bb_xdaemon(0, 0);
Eric Andersen08a72202002-09-30 20:52:10 +0000469
470 maxfd = master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000471#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000472
473 do {
474 struct tsession *ts;
475
476 FD_ZERO(&rdfdset);
477 FD_ZERO(&wrfdset);
478
479 /* select on the master socket, all telnet sockets and their
480 * ptys if there is room in their respective session buffers.
481 */
482
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000483#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000484 FD_SET(master_fd, &rdfdset);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000485#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000486
487 ts = sessions;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000488#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000489 while (ts) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000490#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000491 /* buf1 is used from socket to pty
492 * buf2 is used from pty to socket
493 */
494 if (ts->size1 > 0) {
495 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
496 }
497 if (ts->size1 < BUFSIZE) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000498#ifdef CONFIG_FEATURE_TELNETD_INETD
499 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
500#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000501 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000502#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000503 }
504 if (ts->size2 > 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000505#ifdef CONFIG_FEATURE_TELNETD_INETD
506 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
507#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000508 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000509#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000510 }
511 if (ts->size2 < BUFSIZE) {
512 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
513 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000514#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000515 ts = ts->next;
516 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000517#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000518
519 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
520
521 if (!selret)
522 break;
523
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000524#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000525 /* First check for and accept new sessions. */
526 if (FD_ISSET(master_fd, &rdfdset)) {
Mike Frysinger06b00e82006-05-10 17:18:11 +0000527 int fd;
528 socklen_t salen;
Eric Andersen08a72202002-09-30 20:52:10 +0000529
530 salen = sizeof(sa);
531 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
532 &salen)) < 0) {
533 continue;
534 } else {
535 /* Create a new session and link it into
536 our active list. */
537 struct tsession *new_ts = make_new_session(fd);
538 if (new_ts) {
539 new_ts->next = sessions;
540 sessions = new_ts;
541 if (fd > maxfd)
542 maxfd = fd;
543 } else {
544 close(fd);
545 }
546 }
547 }
548
549 /* Then check for data tunneling. */
550
551 ts = sessions;
552 while (ts) { /* For all sessions... */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000553#endif /* CONFIG_FEATURE_TELNETD_INETD */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000554#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000555 struct tsession *next = ts->next; /* in case we free ts. */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000556#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000557
Eric Andersen08a72202002-09-30 20:52:10 +0000558 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
559 int num_totty;
560 char *ptr;
561 /* Write to pty from buffer 1. */
562
563 ptr = remove_iacs(ts, &num_totty);
564
565 w = write(ts->ptyfd, ptr, num_totty);
566 if (w < 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000567#ifdef CONFIG_FEATURE_TELNETD_INETD
568 exit(0);
569#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000570 free_session(ts);
571 ts = next;
572 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000573#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000574 }
575 ts->wridx1 += w;
576 ts->size1 -= w;
577 if (ts->wridx1 == BUFSIZE)
578 ts->wridx1 = 0;
579 }
580
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000581#ifdef CONFIG_FEATURE_TELNETD_INETD
582 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
583#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000584 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000585#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000586 /* Write to socket from buffer 2. */
587 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000588#ifdef CONFIG_FEATURE_TELNETD_INETD
589 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
590 if (w < 0)
591 exit(0);
592#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000593 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
594 if (w < 0) {
595 free_session(ts);
596 ts = next;
597 continue;
598 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000599#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000600 ts->wridx2 += w;
601 ts->size2 -= w;
602 if (ts->wridx2 == BUFSIZE)
603 ts->wridx2 = 0;
604 }
605
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000606#ifdef CONFIG_FEATURE_TELNETD_INETD
607 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
608#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000609 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000610#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000611 /* Read from socket to buffer 1. */
612 maxlen = MIN(BUFSIZE - ts->rdidx1,
613 BUFSIZE - ts->size1);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000614#ifdef CONFIG_FEATURE_TELNETD_INETD
615 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
616 if (!r || (r < 0 && errno != EINTR))
617 exit(0);
618#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000619 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
620 if (!r || (r < 0 && errno != EINTR)) {
621 free_session(ts);
622 ts = next;
623 continue;
624 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000625#endif /* CONFIG_FEATURE_TELNETD_INETD */
Mike Frysinger731f81c2006-05-10 15:23:12 +0000626 if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
Eric Andersen08a72202002-09-30 20:52:10 +0000627 r--;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000628 if (!r)
Eric Andersen08a72202002-09-30 20:52:10 +0000629 continue;
630 }
631 ts->rdidx1 += r;
632 ts->size1 += r;
633 if (ts->rdidx1 == BUFSIZE)
634 ts->rdidx1 = 0;
635 }
636
637 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
638 /* Read from pty to buffer 2. */
639 maxlen = MIN(BUFSIZE - ts->rdidx2,
640 BUFSIZE - ts->size2);
641 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
642 if (!r || (r < 0 && errno != EINTR)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000643#ifdef CONFIG_FEATURE_TELNETD_INETD
644 exit(0);
645#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000646 free_session(ts);
647 ts = next;
648 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000649#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000650 }
651 ts->rdidx2 += r;
652 ts->size2 += r;
653 if (ts->rdidx2 == BUFSIZE)
654 ts->rdidx2 = 0;
655 }
656
657 if (ts->size1 == 0) {
658 ts->rdidx1 = 0;
659 ts->wridx1 = 0;
660 }
661 if (ts->size2 == 0) {
662 ts->rdidx2 = 0;
663 ts->wridx2 = 0;
664 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000665#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000666 ts = next;
667 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000668#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000669
670 } while (1);
671
672 return 0;
673}