source: MondoRescue/branches/3.2/mindi-busybox/networking/telnetd.c@ 3232

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