blob: 2fbdc3bb34e5554191aa1df95edb8efe6cf3bad2 [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 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02006 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
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 *
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020014 * 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.
Eric Andersen08a72202002-09-30 20:52:10 +000019 *
20 * Vladimir Oleynik <dzo@simtreas.ru> 2001
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020021 * Set process group corrections, initial busybox port
Eric Andersen08a72202002-09-30 20:52:10 +000022 */
Pere Orga5bc8c002011-04-11 03:29:49 +020023
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"
Pere Orga5bc8c002011-04-11 03:29:49 +020029//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
Denis Vlasenko75f8d082006-11-22 15:54:52 +000044#define DEBUG 0
Eric Andersen08a72202002-09-30 20:52:10 +000045
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000046#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020047#include "common_bufsiz.h"
Bernhard Reutner-Fischerf4701962008-01-27 12:50:12 +000048#include <syslog.h>
Rob Landley099ed502006-08-28 09:41:49 +000049
Denis Vlasenko75f8d082006-11-22 15:54:52 +000050#if DEBUG
Denys Vlasenko3a416112010-04-05 22:10:38 +020051# define TELCMDS
52# define TELOPTS
Eric Andersen08a72202002-09-30 20:52:10 +000053#endif
54#include <arpa/telnet.h>
Eric Andersen08a72202002-09-30 20:52:10 +000055
Denys Vlasenko3a416112010-04-05 22:10:38 +020056
Eric Andersen08a72202002-09-30 20:52:10 +000057struct tsession {
58 struct tsession *next;
Denis Vlasenkod814c982009-02-02 23:43:57 +000059 pid_t shell_pid;
Denys Vlasenko1d77db82009-06-10 13:38:08 +020060 int sockfd_read;
61 int sockfd_write;
62 int ptyfd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +000063
Eric Andersen08a72202002-09-30 20:52:10 +000064 /* two circular buffers */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000065 /*char *buf1, *buf2;*/
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020066/*#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)
Eric Andersen08a72202002-09-30 20:52:10 +000070 int rdidx1, wridx1, size1;
71 int rdidx2, wridx2, size2;
72};
73
Denis Vlasenko88308fe2007-06-25 10:35:11 +000074/* Two buffers are directly after tsession in malloced memory.
75 * Make whole thing fit in 4k */
Denis Vlasenko59d7c432007-10-15 15:19:36 +000076enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
Denis Vlasenko88308fe2007-06-25 10:35:11 +000077
Eric Andersen08a72202002-09-30 20:52:10 +000078
Denis Vlasenko59d7c432007-10-15 15:19:36 +000079/* Globals */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020080struct globals {
81 struct tsession *sessions;
82 const char *loginpath;
83 const char *issuefile;
84 int maxfd;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010085} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020086#define G (*(struct globals*)bb_common_bufsiz1)
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020087#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +020088 setup_common_bufsiz(); \
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +020089 G.loginpath = "/bin/login"; \
90 G.issuefile = "/etc/issue.net"; \
91} while (0)
Eric Andersen08a72202002-09-30 20:52:10 +000092
93
94/*
Denis Vlasenko59d7c432007-10-15 15:19:36 +000095 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.
Eric Andersen08a72202002-09-30 20:52:10 +000099
Denys Vlasenko4c721042010-04-04 23:45:09 +0200100 Return a pointer to the beginning of the characters meant for the terminal
Eric Andersen08a72202002-09-30 20:52:10 +0000101 and make *num_totty the number of characters that should be sent to
102 the terminal.
103
Denys Vlasenko4c721042010-04-04 23:45:09 +0200104 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000105 past (bf + len) then that IAC will be left unprocessed and *processed
106 will be less than len.
Eric Andersen08a72202002-09-30 20:52:10 +0000107
Denis Vlasenko10916c52007-10-15 17:28:00 +0000108 CR-LF ->'s CR mapping is also done here, for convenience.
109
110 NB: may fail to remove iacs which wrap around buffer!
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000111 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000112static unsigned char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000113remove_iacs(struct tsession *ts, int *pnum_totty)
114{
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200115 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000116 unsigned char *ptr = ptr0;
117 unsigned char *totty = ptr;
118 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
Eric Andersen08a72202002-09-30 20:52:10 +0000119 int num_totty;
120
121 while (ptr < end) {
122 if (*ptr != IAC) {
Denis Vlasenko10916c52007-10-15 17:28:00 +0000123 char c = *ptr;
124
125 *totty++ = c;
126 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000127 /* We map \r\n ==> \r for pragmatic reasons.
Eric Andersen3752d332003-12-19 11:30:13 +0000128 * Many client implementations send \r\n when
129 * the user hits the CarriageReturn key.
Denys Vlasenkoaca464d2012-09-13 13:00:49 +0200130 * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
Eric Andersen3752d332003-12-19 11:30:13 +0000131 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000132 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
Eric Andersen3752d332003-12-19 11:30:13 +0000133 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000134 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000135 }
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000136
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) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200153 /* Only the beginning of the IAC is in the
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000154 buffer we were asked to process, we can't
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200155 process this char */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000156 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)
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200164 break; /* incomplete, can't process */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000165 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 */
172#if DEBUG
173 fprintf(stderr, "Ignoring IAC %s,%s\n",
174 TELCMD(ptr[1]), TELOPT(ptr[2]));
175#endif
176 ptr += 3;
Eric Andersen08a72202002-09-30 20:52:10 +0000177 }
178
Denis Vlasenko0de37e12007-10-17 11:08:53 +0000179 num_totty = totty - ptr0;
Eric Andersen08a72202002-09-30 20:52:10 +0000180 *pnum_totty = num_totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200181 /* The difference between ptr and totty is number of iacs
182 we removed from the stream. Adjust buf1 accordingly */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000183 if ((ptr - totty) == 0) /* 99.999% of cases */
184 return ptr0;
185 ts->wridx1 += ptr - totty;
186 ts->size1 -= ptr - totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200187 /* Move chars meant for the terminal towards the end of the buffer */
Eric Andersen08a72202002-09-30 20:52:10 +0000188 return memmove(ptr - num_totty, ptr0, num_totty);
189}
190
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000191/*
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000192 * Converting single IAC into double on output
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000193 */
194static size_t iac_safe_write(int fd, const char *buf, size_t count)
195{
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000196 const char *IACptr;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000197 size_t wr, rc, total;
198
199 total = 0;
200 while (1) {
201 if (count == 0)
202 return total;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000203 if (*buf == (char)IAC) {
204 static const char IACIAC[] ALIGN1 = { IAC, IAC };
205 rc = safe_write(fd, IACIAC, 2);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000206 if (rc != 2)
207 break;
208 buf++;
209 total++;
210 count--;
211 continue;
212 }
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000213 /* count != 0, *buf != IAC */
214 IACptr = memchr(buf, IAC, count);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000215 wr = count;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000216 if (IACptr)
217 wr = IACptr - buf;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000218 rc = safe_write(fd, buf, wr);
219 if (rc != wr)
220 break;
221 buf += rc;
222 total += rc;
223 count -= rc;
224 }
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;
232}
Eric Andersen08a72202002-09-30 20:52:10 +0000233
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200234/* Must match getopt32 string */
235enum {
236 OPT_WATCHCHILD = (1 << 2), /* -K */
237 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200238 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200239 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200240 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
241 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200242};
243
Eric Andersen08a72202002-09-30 20:52:10 +0000244static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000245make_new_session(
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200246 IF_FEATURE_TELNETD_STANDALONE(int sock)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000247 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000248) {
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200249#if !ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenkoff0e8752010-05-10 04:16:43 +0200250 enum { sock = 0 };
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200251#endif
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000252 const char *login_argv[2];
Eric Andersen08a72202002-09-30 20:52:10 +0000253 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000254 int fd, pid;
Denis Vlasenko85c24712008-03-17 09:04:04 +0000255 char tty_name[GETPTY_BUFSIZE];
Rob Landley1ec5b292006-05-29 07:42:02 +0000256 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000257
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000258 /*ts->buf1 = (char *)(ts + 1);*/
259 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
Eric Andersen08a72202002-09-30 20:52:10 +0000260
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200261 /* Got a new connection, set up a tty */
Bernhard Reutner-Fischerae4342c2008-05-19 08:18:50 +0000262 fd = xgetpty(tty_name);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200263 if (fd > G.maxfd)
264 G.maxfd = fd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000265 ts->ptyfd = fd;
266 ndelay_on(fd);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200267 close_on_exec_on(fd);
268
Denis Vlasenko6d044352008-11-09 00:44:40 +0000269 /* SO_KEEPALIVE by popular demand */
Denys Vlasenkoc52cbea2015-08-24 19:48:03 +0200270 setsockopt_keepalive(sock);
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200271#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200272 ts->sockfd_read = sock;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000273 ndelay_on(sock);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200274 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
Denis Vlasenko9e237672007-10-17 11:18:49 +0000275 sock++; /* so use fd 1 for output */
276 ndelay_on(sock);
277 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000278 ts->sockfd_write = sock;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200279 if (sock > G.maxfd)
280 G.maxfd = sock;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000281#else
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000282 /* ts->sockfd_read = 0; - done by xzalloc */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000283 ts->sockfd_write = 1;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000284 ndelay_on(0);
285 ndelay_on(1);
286#endif
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200287
Eric Andersen08a72202002-09-30 20:52:10 +0000288 /* 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
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000291 * stuff that requires char-by-char support. */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000292 {
293 static const char iacs_to_send[] ALIGN1 = {
294 IAC, DO, TELOPT_ECHO,
295 IAC, DO, TELOPT_NAWS,
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200296 /* This requires telnetd.ctrlSQ.patch (incomplete) */
297 /*IAC, DO, TELOPT_LFLOW,*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000298 IAC, WILL, TELOPT_ECHO,
299 IAC, WILL, TELOPT_SGA
300 };
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000301 /* This confuses iac_safe_write(), it will try to duplicate
302 * each IAC... */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200303 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000304 //ts->rdidx2 = sizeof(iacs_to_send);
305 //ts->size2 = sizeof(iacs_to_send);
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000306 /* So just stuff it into TCP stream! (no error check...) */
307#if ENABLE_FEATURE_TELNETD_STANDALONE
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000308 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000309#else
310 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
311#endif
312 /*ts->rdidx2 = 0; - xzalloc did it */
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000313 /*ts->size2 = 0;*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000314 }
Eric Andersen08a72202002-09-30 20:52:10 +0000315
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100316 fflush_all();
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000317 pid = vfork(); /* NOMMU-friendly */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000318 if (pid < 0) {
319 free(ts);
320 close(fd);
Denis Vlasenko23c81282007-10-16 22:01:23 +0000321 /* sock will be closed by caller */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000322 bb_perror_msg("vfork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000323 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000324 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000325 if (pid > 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000326 /* Parent */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000327 ts->shell_pid = pid;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000328 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000329 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000330
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000331 /* Child */
332 /* Careful - we are after vfork! */
Eric Andersen08a72202002-09-30 20:52:10 +0000333
Denis Vlasenkod814c982009-02-02 23:43:57 +0000334 /* Restore default signal handling ASAP */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000335 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000336
Denys Vlasenko58c3d212010-11-30 09:17:30 +0100337 pid = getpid();
338
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200339 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
Denis Vlasenkod814c982009-02-02 23:43:57 +0000350 /* Make new session and process group */
351 setsid();
352
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200353 /* Open the child's side of the tty */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000354 /* NB: setsid() disconnects from any previous ctty's. Therefore
355 * we must open child's side of the tty AFTER setsid! */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200356 close(0);
Denis Vlasenko1101d1c2008-07-21 09:22:28 +0000357 xopen(tty_name, O_RDWR); /* becomes our ctty */
358 xdup2(0, 1);
359 xdup2(0, 2);
Denys Vlasenko3a416112010-04-05 22:10:38 +0200360 tcsetpgrp(0, pid); /* switch this tty's process group to us */
361
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200362 /* The pseudo-terminal allocated to the client is configured to operate
363 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000364 tcgetattr(0, &termbuf);
365 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000366 termbuf.c_oflag |= ONLCR | XTABS;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000367 termbuf.c_iflag |= ICRNL;
368 termbuf.c_iflag &= ~IXOFF;
369 /*termbuf.c_lflag &= ~ICANON;*/
Denis Vlasenko202ac502008-11-05 13:20:58 +0000370 tcsetattr_stdin_TCSANOW(&termbuf);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000371
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100372 /* Uses FILE-based I/O to stdout, but does fflush_all(),
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000373 * so should be safe with vfork.
374 * I fear, though, that some users will have ridiculously big
Denis Vlasenko018b1552007-11-06 01:38:46 +0000375 * 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!) */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200378 print_login_issue(G.issuefile, tty_name);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000379
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000380 /* Exec shell / login / whatever */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200381 login_argv[0] = G.loginpath;
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000382 login_argv[1] = NULL;
Denis Vlasenko018b1552007-11-06 01:38:46 +0000383 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200384 * 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 */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200388 BB_EXECVP(G.loginpath, (char **)login_argv);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000389 /* _exit is safer with vfork, and we shouldn't send message
Denis Vlasenko2450c452007-10-15 22:09:15 +0000390 * to remote clients anyway */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200391 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
Eric Andersen08a72202002-09-30 20:52:10 +0000392}
393
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000394#if ENABLE_FEATURE_TELNETD_STANDALONE
395
Eric Andersen08a72202002-09-30 20:52:10 +0000396static void
397free_session(struct tsession *ts)
398{
Denys Vlasenko3a416112010-04-05 22:10:38 +0200399 struct tsession *t;
Eric Andersen08a72202002-09-30 20:52:10 +0000400
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000401 if (option_mask32 & OPT_INETD)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000402 exit(EXIT_SUCCESS);
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000403
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000404 /* Unlink this telnet session from the session list */
Denys Vlasenko3a416112010-04-05 22:10:38 +0200405 t = G.sessions;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000406 if (t == ts)
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200407 G.sessions = ts->next;
Eric Andersen08a72202002-09-30 20:52:10 +0000408 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000409 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000410 t = t->next;
411 t->next = ts->next;
412 }
413
Denis Vlasenkof472b232007-10-16 21:35:17 +0000414#if 0
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000415 /* 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. */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000418 kill(ts->shell_pid, SIGKILL);
Denis Vlasenkod814c982009-02-02 23:43:57 +0000419 waitpid(ts->shell_pid, NULL, 0);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000420#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000421 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000422 close(ts->sockfd_read);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000423 /* 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
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000425 * we do not reach this */
Eric Andersen08a72202002-09-30 20:52:10 +0000426 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000427
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000428 /* Scan all sessions and find new maxfd */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200429 G.maxfd = 0;
430 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000431 while (ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200432 if (G.maxfd < ts->ptyfd)
433 G.maxfd = ts->ptyfd;
434 if (G.maxfd < ts->sockfd_read)
435 G.maxfd = ts->sockfd_read;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000436#if 0
437 /* Again, sockfd_write == sockfd_read here */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200438 if (G.maxfd < ts->sockfd_write)
439 G.maxfd = ts->sockfd_write;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000440#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000441 ts = ts->next;
442 }
Eric Andersen08a72202002-09-30 20:52:10 +0000443}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000444
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000445#else /* !FEATURE_TELNETD_STANDALONE */
446
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000447/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000448#define free_session(ts) return 0
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000449
450#endif
451
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000452static void handle_sigchld(int sig UNUSED_PARAM)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000453{
454 pid_t pid;
455 struct tsession *ts;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200456 int save_errno = errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000457
Denis Vlasenko018b1552007-11-06 01:38:46 +0000458 /* Looping: more than one child may have exited */
459 while (1) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000460 pid = wait_any_nohang(NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000461 if (pid <= 0)
462 break;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200463 ts = G.sessions;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000464 while (ts) {
465 if (ts->shell_pid == pid) {
466 ts->shell_pid = -1;
Denys Vlasenkoda921262015-01-05 15:37:58 +0100467 update_utmp_DEAD_PROCESS(pid);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000468 break;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000469 }
470 ts = ts->next;
471 }
472 }
Denys Vlasenko3a416112010-04-05 22:10:38 +0200473
474 errno = save_errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000475}
476
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000477int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000478int telnetd_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen08a72202002-09-30 20:52:10 +0000479{
Eric Andersen08a72202002-09-30 20:52:10 +0000480 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000481 unsigned opt;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000482 int count;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000483 struct tsession *ts;
484#if ENABLE_FEATURE_TELNETD_STANDALONE
485#define IS_INETD (opt & OPT_INETD)
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200486 int master_fd = master_fd; /* for compiler */
487 int sec_linger = sec_linger;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000488 char *opt_bindaddr = NULL;
489 char *opt_portnbr;
490#else
491 enum {
492 IS_INETD = 1,
493 master_fd = -1,
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000494 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000495#endif
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200496 INIT_G();
497
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200498 /* -w NUM, and implies -F. -w and -i don't mix */
Denys Vlasenko237bedd2016-07-06 21:58:02 +0200499 IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:i--w:w--i";)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000500 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000501 * don't need to guess whether it's ok to pass -i to us */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200502 opt = getopt32(argv, "f:l:Ki"
503 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
Denys Vlasenko237bedd2016-07-06 21:58:02 +0200504 IF_FEATURE_TELNETD_INETD_WAIT("Sw:+"),
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200505 &G.issuefile, &G.loginpath
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200506 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
507 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
508 );
Denis Vlasenko2450c452007-10-15 22:09:15 +0000509 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
Denis Vlasenko008eda22007-10-16 10:47:27 +0000515 * behavior with/without -F, -i */
Denis Vlasenko018b1552007-11-06 01:38:46 +0000516 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000517 }
518 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000519 /* Redirect log to syslog early, if needed */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200520 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000521 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000522 logmode = LOGMODE_SYSLOG;
523 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000524#if ENABLE_FEATURE_TELNETD_STANDALONE
525 if (IS_INETD) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200526 G.sessions = make_new_session(0);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200527 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200528 return 1; /* make_new_session printed error message */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000529 } else {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200530 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 }
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200538 close_on_exec_on(master_fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000539 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000540#else
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200541 G.sessions = make_new_session();
542 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200543 return 1; /* make_new_session printed error message */
Rob Landley00e76cb2005-05-10 23:53:33 +0000544#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000545
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000546 /* We don't want to die if just one session is broken */
547 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000548
Denis Vlasenko2450c452007-10-15 22:09:15 +0000549 if (opt & OPT_WATCHCHILD)
550 signal(SIGCHLD, handle_sigchld);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000551 else /* prevent dead children from becoming zombies */
552 signal(SIGCHLD, SIG_IGN);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000553
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000554/*
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200555 This is how the buffers are used. The arrows indicate data flow.
556
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000557 +-------+ 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*/
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000572 again:
573 FD_ZERO(&rdfdset);
574 FD_ZERO(&wrfdset);
Eric Andersen08a72202002-09-30 20:52:10 +0000575
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000576 /* 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. */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200580 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000581 while (ts) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200582 struct tsession *next = ts->next; /* in case we free ts */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000583 if (ts->shell_pid == -1) {
Denis Vlasenko018b1552007-11-06 01:38:46 +0000584 /* Child died and we detected that */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000585 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 }
598 if (!IS_INETD) {
599 FD_SET(master_fd, &rdfdset);
600 /* This is needed because free_session() does not
Denis Vlasenko018b1552007-11-06 01:38:46 +0000601 * take master_fd into account when it finds new
Denis Vlasenko2450c452007-10-15 22:09:15 +0000602 * maxfd among remaining fd's */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200603 if (master_fd > G.maxfd)
604 G.maxfd = master_fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000605 }
606
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200607 {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200608 struct timeval *tv_ptr = NULL;
Denys Vlasenko36df0482009-10-19 16:07:28 +0200609#if ENABLE_FEATURE_TELNETD_INETD_WAIT
610 struct timeval tv;
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200611 if ((opt & OPT_WAIT) && !G.sessions) {
612 tv.tv_sec = sec_linger;
613 tv.tv_usec = 0;
614 tv_ptr = &tv;
615 }
Denys Vlasenko36df0482009-10-19 16:07:28 +0200616#endif
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200617 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
618 }
619 if (count == 0) /* "telnetd -w SEC" timed out */
620 return 0;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000621 if (count < 0)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000622 goto again; /* EINTR or ENOMEM */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000623
624#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200625 /* Check for and accept new sessions */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000626 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000627 int fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000628 struct tsession *new_ts;
629
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000630 fd = accept(master_fd, NULL, NULL);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000631 if (fd < 0)
632 goto again;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200633 close_on_exec_on(fd);
634
635 /* Create a new session and link it into active list */
636 new_ts = make_new_session(fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000637 if (new_ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200638 new_ts->next = G.sessions;
639 G.sessions = new_ts;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000640 } else {
641 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000642 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000643 }
644#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000645
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200646 /* Then check for data tunneling */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200647 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000648 while (ts) { /* For all sessions... */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200649 struct tsession *next = ts->next; /* in case we free ts */
Eric Andersen08a72202002-09-30 20:52:10 +0000650
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000651 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000652 int num_totty;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000653 unsigned char *ptr;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200654 /* Write to pty from buffer 1 */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000655 ptr = remove_iacs(ts, &num_totty);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000656 count = safe_write(ts->ptyfd, ptr, num_totty);
657 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000658 if (errno == EAGAIN)
659 goto skip1;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000660 goto kill_session;
Eric Andersen08a72202002-09-30 20:52:10 +0000661 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000662 ts->size1 -= count;
663 ts->wridx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000664 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
Eric Andersen08a72202002-09-30 20:52:10 +0000665 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000666 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000667 skip1:
668 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200669 /* Write to socket from buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000670 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200671 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000672 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000673 if (errno == EAGAIN)
674 goto skip2;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000675 goto kill_session;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000676 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000677 ts->size2 -= count;
678 ts->wridx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000679 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000680 ts->wridx2 = 0;
681 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000682 skip2:
Denis Vlasenko10916c52007-10-15 17:28:00 +0000683 /* 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,
Denis Vlasenkof472b232007-10-16 21:35:17 +0000686 * we rely instead on this code making it virtually impossible
Denis Vlasenko10916c52007-10-15 17:28:00 +0000687 * to have wrapped iac (people don't type at 2k/second).
688 * It also allows for bigger reads in common case. */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000689 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 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000697
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000698 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200699 /* Read from socket to buffer 1 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000700 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200701 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000702 if (count <= 0) {
703 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000704 goto skip3;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000705 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000706 }
707 /* Ignore trailing NUL if it is there */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200708 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
Denis Vlasenkof472b232007-10-16 21:35:17 +0000709 --count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000710 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000711 ts->size1 += count;
712 ts->rdidx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000713 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
714 ts->rdidx1 = 0;
715 }
716 skip3:
717 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200718 /* Read from pty to buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000719 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200720 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000721 if (count <= 0) {
722 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000723 goto skip4;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000724 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000725 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000726 ts->size2 += count;
727 ts->rdidx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000728 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
729 ts->rdidx2 = 0;
730 }
731 skip4:
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000732 ts = next;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000733 continue;
734 kill_session:
Denys Vlasenko3a416112010-04-05 22:10:38 +0200735 if (ts->shell_pid > 0)
Denys Vlasenkoda921262015-01-05 15:37:58 +0100736 update_utmp_DEAD_PROCESS(ts->shell_pid);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000737 free_session(ts);
738 ts = next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000739 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000740
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000741 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000742}