source: MondoRescue/branches/3.3/mindi-busybox/networking/telnetd.c@ 3621

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 20.5 KB
RevLine 
[821]1/* vi: set sw=4 ts=4: */
2/*
3 * Simple telnet server
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5 *
[2725]6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]7 *
8 * ---------------------------------------------------------------------------
9 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
10 ****************************************************************************
11 *
12 * The telnetd manpage says it all:
13 *
[2725]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.
[821]19 *
20 * Vladimir Oleynik <dzo@simtreas.ru> 2001
[2725]21 * Set process group corrections, initial busybox port
[821]22 */
[3232]23
24//usage:#define telnetd_trivial_usage
25//usage: "[OPTIONS]"
26//usage:#define telnetd_full_usage "\n\n"
27//usage: "Handle incoming telnet connections"
28//usage: IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n"
29//usage: "\n -l LOGIN Exec LOGIN on connect"
30//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue"
31//usage: "\n -K Close connection as soon as login exits"
32//usage: "\n (normally wait until all programs close slave pty)"
33//usage: IF_FEATURE_TELNETD_STANDALONE(
34//usage: "\n -p PORT Port to listen on"
35//usage: "\n -b ADDR[:PORT] Address to bind to"
36//usage: "\n -F Run in foreground"
37//usage: "\n -i Inetd mode"
38//usage: IF_FEATURE_TELNETD_INETD_WAIT(
39//usage: "\n -w SEC Inetd 'wait' mode, linger time SEC"
40//usage: "\n -S Log to syslog (implied by -i or without -F and -w)"
41//usage: )
42//usage: )
43
[1765]44#define DEBUG 0
[821]45
[1765]46#include "libbb.h"
[3621]47#include "common_bufsiz.h"
[2725]48#include <syslog.h>
[1765]49
50#if DEBUG
[2725]51# define TELCMDS
52# define TELOPTS
[821]53#endif
54#include <arpa/telnet.h>
55
56
57struct tsession {
[1765]58 struct tsession *next;
[2725]59 pid_t shell_pid;
60 int sockfd_read;
61 int sockfd_write;
62 int ptyfd;
63
[821]64 /* two circular buffers */
[2725]65 /*char *buf1, *buf2;*/
66/*#define TS_BUF1(ts) ts->buf1*/
67/*#define TS_BUF2(ts) TS_BUF2(ts)*/
68#define TS_BUF1(ts) ((unsigned char*)(ts + 1))
69#define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
[821]70 int rdidx1, wridx1, size1;
71 int rdidx2, wridx2, size2;
72};
73
[1765]74/* Two buffers are directly after tsession in malloced memory.
75 * Make whole thing fit in 4k */
[2725]76enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
[1765]77
[821]78
[2725]79/* Globals */
80struct globals {
81 struct tsession *sessions;
82 const char *loginpath;
83 const char *issuefile;
84 int maxfd;
85} FIX_ALIASING;
[3621]86#define G (*(struct globals*)bb_common_bufsiz1)
[2725]87#define INIT_G() do { \
[3621]88 setup_common_bufsiz(); \
[2725]89 G.loginpath = "/bin/login"; \
90 G.issuefile = "/etc/issue.net"; \
91} while (0)
[821]92
93
94/*
[2725]95 Remove all IAC's from buf1 (received IACs are ignored and must be removed
96 so as to not be interpreted by the terminal). Make an uninterrupted
97 string of characters fit for the terminal. Do this by packing
98 all characters meant for the terminal sequentially towards the end of buf.
[821]99
[2725]100 Return a pointer to the beginning of the characters meant for the terminal
[821]101 and make *num_totty the number of characters that should be sent to
102 the terminal.
103
[2725]104 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
105 past (bf + len) then that IAC will be left unprocessed and *processed
106 will be less than len.
[821]107
[2725]108 CR-LF ->'s CR mapping is also done here, for convenience.
[821]109
[2725]110 NB: may fail to remove iacs which wrap around buffer!
[1765]111 */
[2725]112static unsigned char *
[1765]113remove_iacs(struct tsession *ts, int *pnum_totty)
114{
[2725]115 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
[821]116 unsigned char *ptr = ptr0;
117 unsigned char *totty = ptr;
118 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
119 int num_totty;
120
121 while (ptr < end) {
122 if (*ptr != IAC) {
[2725]123 char c = *ptr;
124
125 *totty++ = c;
126 ptr++;
127 /* We map \r\n ==> \r for pragmatic reasons.
[821]128 * Many client implementations send \r\n when
129 * the user hits the CarriageReturn key.
[3232]130 * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
[821]131 */
[2725]132 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
[821]133 ptr++;
[2725]134 continue;
135 }
[821]136
[2725]137 if ((ptr+1) >= end)
138 break;
139 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
140 ptr += 2;
141 continue;
142 }
143 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
144 *totty++ = ptr[1];
145 ptr += 2;
146 continue;
147 }
148
149 /*
150 * TELOPT_NAWS support!
151 */
152 if ((ptr+2) >= end) {
153 /* Only the beginning of the IAC is in the
154 buffer we were asked to process, we can't
155 process this char */
156 break;
157 }
158 /*
159 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
160 */
161 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
162 struct winsize ws;
163 if ((ptr+8) >= end)
164 break; /* incomplete, can't process */
165 ws.ws_col = (ptr[3] << 8) | ptr[4];
166 ws.ws_row = (ptr[5] << 8) | ptr[6];
167 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
168 ptr += 9;
169 continue;
170 }
171 /* skip 3-byte IAC non-SB cmd */
[1765]172#if DEBUG
[2725]173 fprintf(stderr, "Ignoring IAC %s,%s\n",
174 TELCMD(ptr[1]), TELOPT(ptr[2]));
[821]175#endif
[2725]176 ptr += 3;
[821]177 }
178
179 num_totty = totty - ptr0;
180 *pnum_totty = num_totty;
[2725]181 /* The difference between ptr and totty is number of iacs
182 we removed from the stream. Adjust buf1 accordingly */
183 if ((ptr - totty) == 0) /* 99.999% of cases */
184 return ptr0;
185 ts->wridx1 += ptr - totty;
186 ts->size1 -= ptr - totty;
187 /* Move chars meant for the terminal towards the end of the buffer */
[821]188 return memmove(ptr - num_totty, ptr0, num_totty);
189}
190
[2725]191/*
192 * Converting single IAC into double on output
193 */
194static size_t iac_safe_write(int fd, const char *buf, size_t count)
[821]195{
[2725]196 const char *IACptr;
197 size_t wr, rc, total;
[821]198
[2725]199 total = 0;
200 while (1) {
201 if (count == 0)
202 return total;
203 if (*buf == (char)IAC) {
204 static const char IACIAC[] ALIGN1 = { IAC, IAC };
205 rc = safe_write(fd, IACIAC, 2);
206 if (rc != 2)
207 break;
208 buf++;
209 total++;
210 count--;
[821]211 continue;
212 }
[2725]213 /* count != 0, *buf != IAC */
214 IACptr = memchr(buf, IAC, count);
215 wr = count;
216 if (IACptr)
217 wr = IACptr - buf;
218 rc = safe_write(fd, buf, wr);
219 if (rc != wr)
220 break;
221 buf += rc;
222 total += rc;
223 count -= rc;
[821]224 }
[2725]225 /* here: rc - result of last short write */
226 if ((ssize_t)rc < 0) { /* error? */
227 if (total == 0)
228 return rc;
229 rc = 0;
230 }
231 return total + rc;
[821]232}
233
[2725]234/* Must match getopt32 string */
235enum {
236 OPT_WATCHCHILD = (1 << 2), /* -K */
237 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
238 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
239 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
240 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
241 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
242};
[821]243
244static struct tsession *
[1765]245make_new_session(
[2725]246 IF_FEATURE_TELNETD_STANDALONE(int sock)
247 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
[1765]248) {
[2725]249#if !ENABLE_FEATURE_TELNETD_STANDALONE
250 enum { sock = 0 };
251#endif
[1765]252 const char *login_argv[2];
[821]253 struct termios termbuf;
[1765]254 int fd, pid;
[2725]255 char tty_name[GETPTY_BUFSIZE];
[821]256 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
257
[2725]258 /*ts->buf1 = (char *)(ts + 1);*/
259 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
[821]260
[2725]261 /* Got a new connection, set up a tty */
262 fd = xgetpty(tty_name);
263 if (fd > G.maxfd)
264 G.maxfd = fd;
265 ts->ptyfd = fd;
266 ndelay_on(fd);
267 close_on_exec_on(fd);
268
269 /* SO_KEEPALIVE by popular demand */
[3621]270 setsockopt_keepalive(sock);
[2725]271#if ENABLE_FEATURE_TELNETD_STANDALONE
272 ts->sockfd_read = sock;
273 ndelay_on(sock);
274 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
275 sock++; /* so use fd 1 for output */
276 ndelay_on(sock);
[1765]277 }
[2725]278 ts->sockfd_write = sock;
279 if (sock > G.maxfd)
280 G.maxfd = sock;
[1765]281#else
[2725]282 /* ts->sockfd_read = 0; - done by xzalloc */
[821]283 ts->sockfd_write = 1;
[1765]284 ndelay_on(0);
285 ndelay_on(1);
286#endif
[2725]287
[821]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
[1765]291 * stuff that requires char-by-char support. */
[2725]292 {
293 static const char iacs_to_send[] ALIGN1 = {
294 IAC, DO, TELOPT_ECHO,
295 IAC, DO, TELOPT_NAWS,
296 /* This requires telnetd.ctrlSQ.patch (incomplete) */
297 /*IAC, DO, TELOPT_LFLOW,*/
298 IAC, WILL, TELOPT_ECHO,
299 IAC, WILL, TELOPT_SGA
300 };
301 /* This confuses iac_safe_write(), it will try to duplicate
302 * each IAC... */
303 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
304 //ts->rdidx2 = sizeof(iacs_to_send);
305 //ts->size2 = sizeof(iacs_to_send);
306 /* So just stuff it into TCP stream! (no error check...) */
307#if ENABLE_FEATURE_TELNETD_STANDALONE
308 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
309#else
310 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
311#endif
312 /*ts->rdidx2 = 0; - xzalloc did it */
313 /*ts->size2 = 0;*/
314 }
[821]315
[2725]316 fflush_all();
317 pid = vfork(); /* NOMMU-friendly */
[1765]318 if (pid < 0) {
319 free(ts);
320 close(fd);
[2725]321 /* sock will be closed by caller */
322 bb_perror_msg("vfork");
[1765]323 return NULL;
[821]324 }
[1765]325 if (pid > 0) {
[2725]326 /* Parent */
[1765]327 ts->shell_pid = pid;
328 return ts;
329 }
[821]330
[2725]331 /* Child */
332 /* Careful - we are after vfork! */
[821]333
[2725]334 /* Restore default signal handling ASAP */
335 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
336
[3232]337 pid = getpid();
338
[2725]339 if (ENABLE_FEATURE_UTMP) {
340 len_and_sockaddr *lsa = get_peer_lsa(sock);
341 char *hostname = NULL;
342 if (lsa) {
343 hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
344 free(lsa);
345 }
346 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
347 free(hostname);
348 }
349
350 /* Make new session and process group */
[1765]351 setsid();
[821]352
[2725]353 /* Open the child's side of the tty */
[1765]354 /* NB: setsid() disconnects from any previous ctty's. Therefore
355 * we must open child's side of the tty AFTER setsid! */
[2725]356 close(0);
357 xopen(tty_name, O_RDWR); /* becomes our ctty */
358 xdup2(0, 1);
359 xdup2(0, 2);
360 tcsetpgrp(0, pid); /* switch this tty's process group to us */
[821]361
[2725]362 /* The pseudo-terminal allocated to the client is configured to operate
363 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
[1765]364 tcgetattr(0, &termbuf);
365 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
[2725]366 termbuf.c_oflag |= ONLCR | XTABS;
[1765]367 termbuf.c_iflag |= ICRNL;
368 termbuf.c_iflag &= ~IXOFF;
369 /*termbuf.c_lflag &= ~ICANON;*/
[2725]370 tcsetattr_stdin_TCSANOW(&termbuf);
[821]371
[2725]372 /* Uses FILE-based I/O to stdout, but does fflush_all(),
373 * so should be safe with vfork.
374 * I fear, though, that some users will have ridiculously big
375 * issue files, and they may block writing to fd 1,
376 * (parent is supposed to read it, but parent waits
377 * for vforked child to exec!) */
378 print_login_issue(G.issuefile, tty_name);
[821]379
[2725]380 /* Exec shell / login / whatever */
381 login_argv[0] = G.loginpath;
[1765]382 login_argv[1] = NULL;
[2725]383 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
384 * exec external program.
385 * NB: sock is either 0 or has CLOEXEC set on it.
386 * fd has CLOEXEC set on it too. These two fds will be closed here.
387 */
388 BB_EXECVP(G.loginpath, (char **)login_argv);
389 /* _exit is safer with vfork, and we shouldn't send message
390 * to remote clients anyway */
391 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
[1765]392}
[821]393
[1765]394#if ENABLE_FEATURE_TELNETD_STANDALONE
[821]395
396static void
397free_session(struct tsession *ts)
398{
[2725]399 struct tsession *t;
[821]400
[2725]401 if (option_mask32 & OPT_INETD)
402 exit(EXIT_SUCCESS);
403
404 /* Unlink this telnet session from the session list */
405 t = G.sessions;
[821]406 if (t == ts)
[2725]407 G.sessions = ts->next;
[821]408 else {
[1765]409 while (t->next != ts)
[821]410 t = t->next;
411 t->next = ts->next;
412 }
413
[2725]414#if 0
415 /* It was said that "normal" telnetd just closes ptyfd,
416 * doesn't send SIGKILL. When we close ptyfd,
417 * kernel sends SIGHUP to processes having slave side opened. */
[821]418 kill(ts->shell_pid, SIGKILL);
[2725]419 waitpid(ts->shell_pid, NULL, 0);
420#endif
[821]421 close(ts->ptyfd);
[1765]422 close(ts->sockfd_read);
[2725]423 /* We do not need to close(ts->sockfd_write), it's the same
424 * as sockfd_read unless we are in inetd mode. But in inetd mode
425 * we do not reach this */
[821]426 free(ts);
427
[2725]428 /* Scan all sessions and find new maxfd */
429 G.maxfd = 0;
430 ts = G.sessions;
[1765]431 while (ts) {
[2725]432 if (G.maxfd < ts->ptyfd)
433 G.maxfd = ts->ptyfd;
434 if (G.maxfd < ts->sockfd_read)
435 G.maxfd = ts->sockfd_read;
436#if 0
437 /* Again, sockfd_write == sockfd_read here */
438 if (G.maxfd < ts->sockfd_write)
439 G.maxfd = ts->sockfd_write;
440#endif
[1765]441 ts = ts->next;
[821]442 }
[1765]443}
[821]444
[1765]445#else /* !FEATURE_TELNETD_STANDALONE */
[821]446
[2725]447/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
448#define free_session(ts) return 0
[821]449
[1765]450#endif
[821]451
[2725]452static void handle_sigchld(int sig UNUSED_PARAM)
453{
454 pid_t pid;
455 struct tsession *ts;
456 int save_errno = errno;
[821]457
[2725]458 /* Looping: more than one child may have exited */
459 while (1) {
460 pid = wait_any_nohang(NULL);
461 if (pid <= 0)
462 break;
463 ts = G.sessions;
464 while (ts) {
465 if (ts->shell_pid == pid) {
466 ts->shell_pid = -1;
[3621]467 update_utmp_DEAD_PROCESS(pid);
[2725]468 break;
469 }
470 ts = ts->next;
471 }
472 }
473
474 errno = save_errno;
475}
476
477int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
478int telnetd_main(int argc UNUSED_PARAM, char **argv)
[1765]479{
480 fd_set rdfdset, wrfdset;
481 unsigned opt;
[2725]482 int count;
[1765]483 struct tsession *ts;
484#if ENABLE_FEATURE_TELNETD_STANDALONE
485#define IS_INETD (opt & OPT_INETD)
[2725]486 int master_fd = master_fd; /* for compiler */
487 int sec_linger = sec_linger;
[1765]488 char *opt_bindaddr = NULL;
489 char *opt_portnbr;
490#else
491 enum {
492 IS_INETD = 1,
493 master_fd = -1,
494 };
495#endif
[2725]496 INIT_G();
[821]497
[2725]498 /* -w NUM, and implies -F. -w and -i don't mix */
499 IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
500 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
501 * don't need to guess whether it's ok to pass -i to us */
502 opt = getopt32(argv, "f:l:Ki"
503 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
504 IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
505 &G.issuefile, &G.loginpath
506 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
507 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
508 );
509 if (!IS_INETD /*&& !re_execed*/) {
510 /* inform that we start in standalone mode?
511 * May be useful when people forget to give -i */
512 /*bb_error_msg("listening for connections");*/
513 if (!(opt & OPT_FOREGROUND)) {
514 /* DAEMON_CHDIR_ROOT was giving inconsistent
515 * behavior with/without -F, -i */
516 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
517 }
518 }
[1765]519 /* Redirect log to syslog early, if needed */
[2725]520 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
521 openlog(applet_name, LOG_PID, LOG_DAEMON);
[1765]522 logmode = LOGMODE_SYSLOG;
523 }
524#if ENABLE_FEATURE_TELNETD_STANDALONE
525 if (IS_INETD) {
[2725]526 G.sessions = make_new_session(0);
527 if (!G.sessions) /* pty opening or vfork problem, exit */
528 return 1; /* make_new_session printed error message */
[1765]529 } else {
[2725]530 master_fd = 0;
531 if (!(opt & OPT_WAIT)) {
532 unsigned portnbr = 23;
533 if (opt & OPT_PORT)
534 portnbr = xatou16(opt_portnbr);
535 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
536 xlisten(master_fd, 1);
537 }
538 close_on_exec_on(master_fd);
[1765]539 }
[821]540#else
[2725]541 G.sessions = make_new_session();
542 if (!G.sessions) /* pty opening or vfork problem, exit */
543 return 1; /* make_new_session printed error message */
[821]544#endif
545
[1765]546 /* We don't want to die if just one session is broken */
547 signal(SIGPIPE, SIG_IGN);
[821]548
[2725]549 if (opt & OPT_WATCHCHILD)
550 signal(SIGCHLD, handle_sigchld);
551 else /* prevent dead children from becoming zombies */
552 signal(SIGCHLD, SIG_IGN);
553
554/*
555 This is how the buffers are used. The arrows indicate data flow.
556
557 +-------+ wridx1++ +------+ rdidx1++ +----------+
558 | | <-------------- | buf1 | <-------------- | |
559 | | size1-- +------+ size1++ | |
560 | pty | | socket |
561 | | rdidx2++ +------+ wridx2++ | |
562 | | --------------> | buf2 | --------------> | |
563 +-------+ size2++ +------+ size2-- +----------+
564
565 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
566 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
567
568 Each session has got two buffers. Buffers are circular. If sizeN == 0,
569 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
570 rdidxN == wridxN.
571*/
[1765]572 again:
573 FD_ZERO(&rdfdset);
574 FD_ZERO(&wrfdset);
[2725]575
576 /* Select on the master socket, all telnet sockets and their
577 * ptys if there is room in their session buffers.
578 * NB: scalability problem: we recalculate entire bitmap
579 * before each select. Can be a problem with 500+ connections. */
580 ts = G.sessions;
581 while (ts) {
582 struct tsession *next = ts->next; /* in case we free ts */
583 if (ts->shell_pid == -1) {
584 /* Child died and we detected that */
585 free_session(ts);
586 } else {
587 if (ts->size1 > 0) /* can write to pty */
588 FD_SET(ts->ptyfd, &wrfdset);
589 if (ts->size1 < BUFSIZE) /* can read from socket */
590 FD_SET(ts->sockfd_read, &rdfdset);
591 if (ts->size2 > 0) /* can write to socket */
592 FD_SET(ts->sockfd_write, &wrfdset);
593 if (ts->size2 < BUFSIZE) /* can read from pty */
594 FD_SET(ts->ptyfd, &rdfdset);
595 }
596 ts = next;
597 }
[1765]598 if (!IS_INETD) {
599 FD_SET(master_fd, &rdfdset);
600 /* This is needed because free_session() does not
[2725]601 * take master_fd into account when it finds new
602 * maxfd among remaining fd's */
603 if (master_fd > G.maxfd)
604 G.maxfd = master_fd;
[1765]605 }
[821]606
[2725]607 {
608 struct timeval *tv_ptr = NULL;
609#if ENABLE_FEATURE_TELNETD_INETD_WAIT
610 struct timeval tv;
611 if ((opt & OPT_WAIT) && !G.sessions) {
612 tv.tv_sec = sec_linger;
613 tv.tv_usec = 0;
614 tv_ptr = &tv;
615 }
616#endif
617 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
[1765]618 }
[2725]619 if (count == 0) /* "telnetd -w SEC" timed out */
[1765]620 return 0;
[2725]621 if (count < 0)
622 goto again; /* EINTR or ENOMEM */
[821]623
[1765]624#if ENABLE_FEATURE_TELNETD_STANDALONE
[2725]625 /* Check for and accept new sessions */
[1765]626 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
627 int fd;
628 struct tsession *new_ts;
[821]629
[2725]630 fd = accept(master_fd, NULL, NULL);
[1765]631 if (fd < 0)
632 goto again;
[2725]633 close_on_exec_on(fd);
634
635 /* Create a new session and link it into active list */
636 new_ts = make_new_session(fd);
[1765]637 if (new_ts) {
[2725]638 new_ts->next = G.sessions;
639 G.sessions = new_ts;
[1765]640 } else {
641 close(fd);
[821]642 }
[1765]643 }
644#endif
[821]645
[2725]646 /* Then check for data tunneling */
647 ts = G.sessions;
[1765]648 while (ts) { /* For all sessions... */
[2725]649 struct tsession *next = ts->next; /* in case we free ts */
[821]650
[2725]651 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
[1765]652 int num_totty;
[2725]653 unsigned char *ptr;
654 /* Write to pty from buffer 1 */
[1765]655 ptr = remove_iacs(ts, &num_totty);
[2725]656 count = safe_write(ts->ptyfd, ptr, num_totty);
657 if (count < 0) {
658 if (errno == EAGAIN)
659 goto skip1;
660 goto kill_session;
[821]661 }
[2725]662 ts->size1 -= count;
663 ts->wridx1 += count;
664 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
[1765]665 ts->wridx1 = 0;
[821]666 }
[2725]667 skip1:
668 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
669 /* Write to socket from buffer 2 */
670 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
671 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
672 if (count < 0) {
673 if (errno == EAGAIN)
674 goto skip2;
675 goto kill_session;
[821]676 }
[2725]677 ts->size2 -= count;
678 ts->wridx2 += count;
679 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
[1765]680 ts->wridx2 = 0;
681 }
[2725]682 skip2:
683 /* Should not be needed, but... remove_iacs is actually buggy
684 * (it cannot process iacs which wrap around buffer's end)!
685 * Since properly fixing it requires writing bigger code,
686 * we rely instead on this code making it virtually impossible
687 * to have wrapped iac (people don't type at 2k/second).
688 * It also allows for bigger reads in common case. */
[1765]689 if (ts->size1 == 0) {
690 ts->rdidx1 = 0;
691 ts->wridx1 = 0;
692 }
693 if (ts->size2 == 0) {
694 ts->rdidx2 = 0;
695 ts->wridx2 = 0;
696 }
[2725]697
698 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
699 /* Read from socket to buffer 1 */
700 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
701 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
702 if (count <= 0) {
703 if (count < 0 && errno == EAGAIN)
704 goto skip3;
705 goto kill_session;
706 }
707 /* Ignore trailing NUL if it is there */
708 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
709 --count;
710 }
711 ts->size1 += count;
712 ts->rdidx1 += count;
713 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
714 ts->rdidx1 = 0;
715 }
716 skip3:
717 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
718 /* Read from pty to buffer 2 */
719 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
720 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
721 if (count <= 0) {
722 if (count < 0 && errno == EAGAIN)
723 goto skip4;
724 goto kill_session;
725 }
726 ts->size2 += count;
727 ts->rdidx2 += count;
728 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
729 ts->rdidx2 = 0;
730 }
731 skip4:
[1765]732 ts = next;
[2725]733 continue;
734 kill_session:
735 if (ts->shell_pid > 0)
[3621]736 update_utmp_DEAD_PROCESS(ts->shell_pid);
[2725]737 free_session(ts);
738 ts = next;
[1765]739 }
[2725]740
[1765]741 goto again;
[821]742}
Note: See TracBrowser for help on using the repository browser.