blob: 491c66fd151abc186acf5a3966da4d5b0b348d24 [file] [log] [blame]
Glenn L McGrathd4004ee2004-09-14 17:24:59 +00001/* $Id: telnetd.c,v 1.13 2004/09/14 17:24:58 bug1 Exp $
Eric Andersen08a72202002-09-30 20:52:10 +00002 *
3 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
6 * This file is distributed under the Gnu Public License (GPL),
7 * please see the file LICENSE for further information.
8 *
9 * ---------------------------------------------------------------------------
10 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
11 ****************************************************************************
12 *
13 * The telnetd manpage says it all:
14 *
15 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
16 * a client, then creating a login process which has the slave side of the
17 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
18 * master side of the pseudo-terminal, implementing the telnet protocol and
19 * passing characters between the remote client and the login process.
20 *
21 * Vladimir Oleynik <dzo@simtreas.ru> 2001
22 * Set process group corrections, initial busybox port
23 */
24
25/*#define DEBUG 1 */
26
27#include <sys/time.h>
28#include <sys/socket.h>
29#include <sys/wait.h>
Glenn L McGrath90ed9a02004-02-22 09:45:57 +000030#include <sys/ioctl.h>
Eric Andersen08a72202002-09-30 20:52:10 +000031#include <string.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <errno.h>
35#include <netinet/in.h>
36#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
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000052#ifdef CONFIG_LOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000053static const char *loginpath = "/bin/login";
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000054#else
Glenn L McGrathd4004ee2004-09-14 17:24:59 +000055static const char *loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000056#endif
57static const char *issuefile = "/etc/issue.net";
Eric Andersen08a72202002-09-30 20:52:10 +000058
59/* shell name and arguments */
60
61static const char *argv_init[] = {NULL, NULL};
62
63/* structure that describes a session */
64
65struct tsession {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000066#ifdef CONFIG_FEATURE_TELNETD_INETD
67 int sockfd_read, sockfd_write, ptyfd;
68#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +000069 struct tsession *next;
70 int sockfd, ptyfd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +000071#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +000072 int shell_pid;
73 /* two circular buffers */
74 char *buf1, *buf2;
75 int rdidx1, wridx1, size1;
76 int rdidx2, wridx2, size2;
77};
78
79/*
80
81 This is how the buffers are used. The arrows indicate the movement
82 of data.
83
84 +-------+ wridx1++ +------+ rdidx1++ +----------+
85 | | <-------------- | buf1 | <-------------- | |
86 | | size1-- +------+ size1++ | |
87 | pty | | socket |
88 | | rdidx2++ +------+ wridx2++ | |
89 | | --------------> | buf2 | --------------> | |
90 +-------+ size2++ +------+ size2-- +----------+
91
92 Each session has got two buffers.
93
94*/
95
96static int maxfd;
97
98static struct tsession *sessions;
99
100
101/*
102
103 Remove all IAC's from the buffer pointed to by bf (recieved IACs are ignored
104 and must be removed so as to not be interpreted by the terminal). Make an
105 uninterrupted string of characters fit for the terminal. Do this by packing
106 all characters meant for the terminal sequentially towards the end of bf.
107
108 Return a pointer to the beginning of the characters meant for the terminal.
109 and make *num_totty the number of characters that should be sent to
110 the terminal.
111
112 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
113 past (bf + len) then that IAC will be left unprocessed and *processed will be
114 less than len.
115
116 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
117 what is the escape character? We aren't handling that situation here.
118
Eric Andersen3752d332003-12-19 11:30:13 +0000119 CR-LF ->'s CR mapping is also done here, for convenience
120
Eric Andersen08a72202002-09-30 20:52:10 +0000121 */
122static char *
123remove_iacs(struct tsession *ts, int *pnum_totty) {
124 unsigned char *ptr0 = ts->buf1 + ts->wridx1;
125 unsigned char *ptr = ptr0;
126 unsigned char *totty = ptr;
127 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
128 int processed;
129 int num_totty;
130
131 while (ptr < end) {
132 if (*ptr != IAC) {
Eric Andersen3752d332003-12-19 11:30:13 +0000133 int c = *ptr;
Eric Andersen08a72202002-09-30 20:52:10 +0000134 *totty++ = *ptr++;
Eric Andersen3752d332003-12-19 11:30:13 +0000135 /* We now map \r\n ==> \r for pragmatic reasons.
136 * Many client implementations send \r\n when
137 * the user hits the CarriageReturn key.
138 */
139 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
140 ptr++;
Eric Andersen08a72202002-09-30 20:52:10 +0000141 }
142 else {
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000143 /*
144 * TELOPT_NAWS support!
145 */
146 if ((ptr+2) >= end) {
Eric Andersen08a72202002-09-30 20:52:10 +0000147 /* only the beginning of the IAC is in the
148 buffer we were asked to process, we can't
149 process this char. */
150 break;
151 }
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000152
153 /*
154 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
155 */
156 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
157 struct winsize ws;
158 if ((ptr+8) >= end)
159 break; /* incomplete, can't process */
160 ws.ws_col = (ptr[3] << 8) | ptr[4];
161 ws.ws_row = (ptr[5] << 8) | ptr[6];
162 (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
163 ptr += 9;
164 }
165 else {
166 /* skip 3-byte IAC non-SB cmd */
167#ifdef DEBUG
168 fprintf(stderr, "Ignoring IAC %s,%s\n",
169 TELCMD(*(ptr+1)), TELOPT(*(ptr+2)));
170#endif
171 ptr += 3;
172 }
Eric Andersen08a72202002-09-30 20:52:10 +0000173 }
174 }
175
176 processed = ptr - ptr0;
177 num_totty = totty - ptr0;
178 /* the difference between processed and num_to tty
179 is all the iacs we removed from the stream.
180 Adjust buf1 accordingly. */
181 ts->wridx1 += processed - num_totty;
182 ts->size1 -= processed - num_totty;
183 *pnum_totty = num_totty;
184 /* move the chars meant for the terminal towards the end of the
185 buffer. */
186 return memmove(ptr - num_totty, ptr0, num_totty);
187}
188
189
190static int
191getpty(char *line)
192{
193 int p;
Glenn L McGrathf234e7c2002-11-10 22:26:19 +0000194#ifdef CONFIG_FEATURE_DEVPTS
Eric Andersen08a72202002-09-30 20:52:10 +0000195 p = open("/dev/ptmx", 2);
196 if (p > 0) {
197 grantpt(p);
198 unlockpt(p);
199 strcpy(line, ptsname(p));
200 return(p);
201 }
202#else
203 struct stat stb;
204 int i;
205 int j;
206
207 strcpy(line, "/dev/ptyXX");
208
209 for (i = 0; i < 16; i++) {
210 line[8] = "pqrstuvwxyzabcde"[i];
211 line[9] = '0';
212 if (stat(line, &stb) < 0) {
213 continue;
214 }
215 for (j = 0; j < 16; j++) {
216 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
217 if ((p = open(line, O_RDWR | O_NOCTTY)) >= 0) {
218 line[5] = 't';
219 return p;
220 }
221 }
222 }
Glenn L McGrathf234e7c2002-11-10 22:26:19 +0000223#endif /* CONFIG_FEATURE_DEVPTS */
Eric Andersen08a72202002-09-30 20:52:10 +0000224 return -1;
225}
226
227
228static void
229send_iac(struct tsession *ts, unsigned char command, int option)
230{
231 /* We rely on that there is space in the buffer for now. */
232 char *b = ts->buf2 + ts->rdidx2;
233 *b++ = IAC;
234 *b++ = command;
235 *b++ = option;
236 ts->rdidx2 += 3;
237 ts->size2 += 3;
238}
239
240
241static struct tsession *
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000242#ifdef CONFIG_FEATURE_TELNETD_INETD
243make_new_session(void)
244#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000245make_new_session(int sockfd)
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000246#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000247{
248 struct termios termbuf;
249 int pty, pid;
250 char tty_name[32];
251 struct tsession *ts = malloc(sizeof(struct tsession) + BUFSIZE * 2);
252
253 ts->buf1 = (char *)(&ts[1]);
254 ts->buf2 = ts->buf1 + BUFSIZE;
255
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000256#ifdef CONFIG_FEATURE_TELNETD_INETD
257 ts->sockfd_read = 0;
258 ts->sockfd_write = 1;
259#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000260 ts->sockfd = sockfd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000261#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000262
263 ts->rdidx1 = ts->wridx1 = ts->size1 = 0;
264 ts->rdidx2 = ts->wridx2 = ts->size2 = 0;
265
266 /* Got a new connection, set up a tty and spawn a shell. */
267
268 pty = getpty(tty_name);
269
270 if (pty < 0) {
Eric Andersen36adca82004-06-22 10:07:17 +0000271 syslog(LOG_ERR, "All network ports in use!");
Eric Andersen08a72202002-09-30 20:52:10 +0000272 return 0;
273 }
274
275 if (pty > maxfd)
276 maxfd = pty;
277
278 ts->ptyfd = pty;
279
280 /* Make the telnet client understand we will echo characters so it
281 * should not do it locally. We don't tell the client to run linemode,
282 * because we want to handle line editing and tab completion and other
283 * stuff that requires char-by-char support.
284 */
285
286 send_iac(ts, DO, TELOPT_ECHO);
Glenn L McGrath90ed9a02004-02-22 09:45:57 +0000287 send_iac(ts, DO, TELOPT_NAWS);
Eric Andersen08a72202002-09-30 20:52:10 +0000288 send_iac(ts, DO, TELOPT_LFLOW);
289 send_iac(ts, WILL, TELOPT_ECHO);
290 send_iac(ts, WILL, TELOPT_SGA);
291
292
293 if ((pid = fork()) < 0) {
Eric Andersen36adca82004-06-22 10:07:17 +0000294 syslog(LOG_ERR, "Can`t forking");
Eric Andersen08a72202002-09-30 20:52:10 +0000295 }
296 if (pid == 0) {
297 /* In child, open the child's side of the tty. */
298 int i;
299
300 for(i = 0; i <= maxfd; i++)
301 close(i);
302 /* make new process group */
303 setsid();
304
305 if (open(tty_name, O_RDWR /*| O_NOCTTY*/) < 0) {
Eric Andersen36adca82004-06-22 10:07:17 +0000306 syslog(LOG_ERR, "Could not open tty");
Eric Andersen08a72202002-09-30 20:52:10 +0000307 exit(1);
308 }
309 dup(0);
310 dup(0);
311
312 tcsetpgrp(0, getpid());
313
314 /* The pseudo-terminal allocated to the client is configured to operate in
315 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
316 */
317
318 tcgetattr(0, &termbuf);
319 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
320 termbuf.c_oflag |= ONLCR|XTABS;
321 termbuf.c_iflag |= ICRNL;
322 termbuf.c_iflag &= ~IXOFF;
323 /*termbuf.c_lflag &= ~ICANON;*/
324 tcsetattr(0, TCSANOW, &termbuf);
325
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000326 print_login_issue(issuefile, NULL);
327
Eric Andersen08a72202002-09-30 20:52:10 +0000328 /* exec shell, with correct argv and env */
329 execv(loginpath, (char *const *)argv_init);
330
331 /* NOT REACHED */
Eric Andersen36adca82004-06-22 10:07:17 +0000332 syslog(LOG_ERR, "execv error");
Eric Andersen08a72202002-09-30 20:52:10 +0000333 exit(1);
334 }
335
336 ts->shell_pid = pid;
337
338 return ts;
339}
340
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000341#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000342static void
343free_session(struct tsession *ts)
344{
345 struct tsession *t = sessions;
346
347 /* Unlink this telnet session from the session list. */
348 if(t == ts)
349 sessions = ts->next;
350 else {
351 while(t->next != ts)
352 t = t->next;
353 t->next = ts->next;
354 }
355
356 kill(ts->shell_pid, SIGKILL);
357
358 wait4(ts->shell_pid, NULL, 0, NULL);
359
360 close(ts->ptyfd);
361 close(ts->sockfd);
362
363 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
364 maxfd--;
365 if(ts->ptyfd == maxfd || ts->sockfd == maxfd)
366 maxfd--;
367
368 free(ts);
369}
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000370#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000371
372int
373telnetd_main(int argc, char **argv)
374{
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000375#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000376 struct sockaddr_in sa;
377 int master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000378#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000379 fd_set rdfdset, wrfdset;
380 int selret;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000381#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000382 int on = 1;
383 int portnbr = 23;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000384#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000385 int c;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000386 static const char options[] =
387#ifdef CONFIG_FEATURE_TELNETD_INETD
388 "f:l:";
389#else /* CONFIG_EATURE_TELNETD_INETD */
390 "f:l:p:";
391#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen86f2cce2003-04-25 12:32:37 +0000392 int maxlen, w, r;
Eric Andersen08a72202002-09-30 20:52:10 +0000393
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000394#ifndef CONFIG_LOGIN
395 loginpath = DEFAULT_SHELL;
396#endif
397
Eric Andersen08a72202002-09-30 20:52:10 +0000398 for (;;) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000399 c = getopt( argc, argv, options);
Eric Andersen08a72202002-09-30 20:52:10 +0000400 if (c == EOF) break;
401 switch (c) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000402 case 'f':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000403 issuefile = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000404 break;
405 case 'l':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000406 loginpath = optarg;
Eric Andersen08a72202002-09-30 20:52:10 +0000407 break;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000408#ifndef CONFIG_FEATURE_TELNETD_INETD
409 case 'p':
410 portnbr = atoi(optarg);
411 break;
412#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000413 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000414 bb_show_usage();
Eric Andersen08a72202002-09-30 20:52:10 +0000415 }
416 }
417
418 if (access(loginpath, X_OK) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000419 bb_error_msg_and_die ("'%s' unavailable.", loginpath);
Eric Andersen08a72202002-09-30 20:52:10 +0000420 }
421
422 argv_init[0] = loginpath;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000423
Eric Andersen36adca82004-06-22 10:07:17 +0000424 openlog(bb_applet_name, 0, LOG_USER);
425
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000426#ifdef CONFIG_FEATURE_TELNETD_INETD
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000427 maxfd = 1;
Glenn L McGrath8eb214e2003-01-22 21:09:48 +0000428 sessions = make_new_session();
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000429#else /* CONFIG_EATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000430 sessions = 0;
431
432 /* Grab a TCP socket. */
433
434 master_fd = socket(AF_INET, SOCK_STREAM, 0);
435 if (master_fd < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000436 bb_perror_msg_and_die("socket");
Eric Andersen08a72202002-09-30 20:52:10 +0000437 }
438 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
439
440 /* Set it to listen to specified port. */
441
442 memset((void *)&sa, 0, sizeof(sa));
443 sa.sin_family = AF_INET;
444 sa.sin_port = htons(portnbr);
445
446 if (bind(master_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000447 bb_perror_msg_and_die("bind");
Eric Andersen08a72202002-09-30 20:52:10 +0000448 }
449
450 if (listen(master_fd, 1) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000451 bb_perror_msg_and_die("listen");
Eric Andersen08a72202002-09-30 20:52:10 +0000452 }
453
454 if (daemon(0, 0) < 0)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000455 bb_perror_msg_and_die("daemon");
Eric Andersen08a72202002-09-30 20:52:10 +0000456
457
458 maxfd = master_fd;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000459#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000460
461 do {
462 struct tsession *ts;
463
464 FD_ZERO(&rdfdset);
465 FD_ZERO(&wrfdset);
466
467 /* select on the master socket, all telnet sockets and their
468 * ptys if there is room in their respective session buffers.
469 */
470
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000471#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000472 FD_SET(master_fd, &rdfdset);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000473#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000474
475 ts = sessions;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000476#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000477 while (ts) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000478#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000479 /* buf1 is used from socket to pty
480 * buf2 is used from pty to socket
481 */
482 if (ts->size1 > 0) {
483 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
484 }
485 if (ts->size1 < BUFSIZE) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000486#ifdef CONFIG_FEATURE_TELNETD_INETD
487 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
488#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000489 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000490#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000491 }
492 if (ts->size2 > 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000493#ifdef CONFIG_FEATURE_TELNETD_INETD
494 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
495#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000496 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000497#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000498 }
499 if (ts->size2 < BUFSIZE) {
500 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
501 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000502#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000503 ts = ts->next;
504 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000505#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000506
507 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
508
509 if (!selret)
510 break;
511
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000512#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000513 /* First check for and accept new sessions. */
514 if (FD_ISSET(master_fd, &rdfdset)) {
515 int fd, salen;
516
517 salen = sizeof(sa);
518 if ((fd = accept(master_fd, (struct sockaddr *)&sa,
519 &salen)) < 0) {
520 continue;
521 } else {
522 /* Create a new session and link it into
523 our active list. */
524 struct tsession *new_ts = make_new_session(fd);
525 if (new_ts) {
526 new_ts->next = sessions;
527 sessions = new_ts;
528 if (fd > maxfd)
529 maxfd = fd;
530 } else {
531 close(fd);
532 }
533 }
534 }
535
536 /* Then check for data tunneling. */
537
538 ts = sessions;
539 while (ts) { /* For all sessions... */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000540#endif /* CONFIG_FEATURE_TELNETD_INETD */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000541#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000542 struct tsession *next = ts->next; /* in case we free ts. */
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000543#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000544
Eric Andersen08a72202002-09-30 20:52:10 +0000545 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
546 int num_totty;
547 char *ptr;
548 /* Write to pty from buffer 1. */
549
550 ptr = remove_iacs(ts, &num_totty);
551
552 w = write(ts->ptyfd, ptr, num_totty);
553 if (w < 0) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000554#ifdef CONFIG_FEATURE_TELNETD_INETD
555 exit(0);
556#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000557 free_session(ts);
558 ts = next;
559 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000560#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000561 }
562 ts->wridx1 += w;
563 ts->size1 -= w;
564 if (ts->wridx1 == BUFSIZE)
565 ts->wridx1 = 0;
566 }
567
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000568#ifdef CONFIG_FEATURE_TELNETD_INETD
569 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
570#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000571 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000572#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000573 /* Write to socket from buffer 2. */
574 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000575#ifdef CONFIG_FEATURE_TELNETD_INETD
576 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
577 if (w < 0)
578 exit(0);
579#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000580 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
581 if (w < 0) {
582 free_session(ts);
583 ts = next;
584 continue;
585 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000586#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000587 ts->wridx2 += w;
588 ts->size2 -= w;
589 if (ts->wridx2 == BUFSIZE)
590 ts->wridx2 = 0;
591 }
592
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000593#ifdef CONFIG_FEATURE_TELNETD_INETD
594 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
595#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000596 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000597#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000598 /* Read from socket to buffer 1. */
599 maxlen = MIN(BUFSIZE - ts->rdidx1,
600 BUFSIZE - ts->size1);
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000601#ifdef CONFIG_FEATURE_TELNETD_INETD
602 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
603 if (!r || (r < 0 && errno != EINTR))
604 exit(0);
605#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000606 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
607 if (!r || (r < 0 && errno != EINTR)) {
608 free_session(ts);
609 ts = next;
610 continue;
611 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000612#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000613 if(!*(ts->buf1 + ts->rdidx1 + r - 1)) {
614 r--;
615 if(!r)
616 continue;
617 }
618 ts->rdidx1 += r;
619 ts->size1 += r;
620 if (ts->rdidx1 == BUFSIZE)
621 ts->rdidx1 = 0;
622 }
623
624 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
625 /* Read from pty to buffer 2. */
626 maxlen = MIN(BUFSIZE - ts->rdidx2,
627 BUFSIZE - ts->size2);
628 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
629 if (!r || (r < 0 && errno != EINTR)) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000630#ifdef CONFIG_FEATURE_TELNETD_INETD
631 exit(0);
632#else /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000633 free_session(ts);
634 ts = next;
635 continue;
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000636#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000637 }
638 ts->rdidx2 += r;
639 ts->size2 += r;
640 if (ts->rdidx2 == BUFSIZE)
641 ts->rdidx2 = 0;
642 }
643
644 if (ts->size1 == 0) {
645 ts->rdidx1 = 0;
646 ts->wridx1 = 0;
647 }
648 if (ts->size2 == 0) {
649 ts->rdidx2 = 0;
650 ts->wridx2 = 0;
651 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000652#ifndef CONFIG_FEATURE_TELNETD_INETD
Eric Andersen08a72202002-09-30 20:52:10 +0000653 ts = next;
654 }
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000655#endif /* CONFIG_FEATURE_TELNETD_INETD */
Eric Andersen08a72202002-09-30 20:52:10 +0000656
657 } while (1);
658
659 return 0;
660}