blob: 4404064fc7004f278a6b7e4e98d0ea9b5432a651 [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"
29//usage: "\nOptions:"
30//usage: "\n -l LOGIN Exec LOGIN on connect"
31//usage: "\n -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue"
32//usage: "\n -K Close connection as soon as login exits"
33//usage: "\n (normally wait until all programs close slave pty)"
34//usage: IF_FEATURE_TELNETD_STANDALONE(
35//usage: "\n -p PORT Port to listen on"
36//usage: "\n -b ADDR[:PORT] Address to bind to"
37//usage: "\n -F Run in foreground"
38//usage: "\n -i Inetd mode"
39//usage: IF_FEATURE_TELNETD_INETD_WAIT(
40//usage: "\n -w SEC Inetd 'wait' mode, linger time SEC"
41//usage: "\n -S Log to syslog (implied by -i or without -F and -w)"
42//usage: )
43//usage: )
44
Denis Vlasenko75f8d082006-11-22 15:54:52 +000045#define DEBUG 0
Eric Andersen08a72202002-09-30 20:52:10 +000046
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000047#include "libbb.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 Vlasenkoa4bcbd02009-06-09 23:01:24 +020086#define G (*(struct globals*)&bb_common_bufsiz1)
87#define INIT_G() do { \
88 G.loginpath = "/bin/login"; \
89 G.issuefile = "/etc/issue.net"; \
90} while (0)
Eric Andersen08a72202002-09-30 20:52:10 +000091
92
93/*
Denis Vlasenko59d7c432007-10-15 15:19:36 +000094 Remove all IAC's from buf1 (received IACs are ignored and must be removed
95 so as to not be interpreted by the terminal). Make an uninterrupted
96 string of characters fit for the terminal. Do this by packing
97 all characters meant for the terminal sequentially towards the end of buf.
Eric Andersen08a72202002-09-30 20:52:10 +000098
Denys Vlasenko4c721042010-04-04 23:45:09 +020099 Return a pointer to the beginning of the characters meant for the terminal
Eric Andersen08a72202002-09-30 20:52:10 +0000100 and make *num_totty the number of characters that should be sent to
101 the terminal.
102
Denys Vlasenko4c721042010-04-04 23:45:09 +0200103 Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000104 past (bf + len) then that IAC will be left unprocessed and *processed
105 will be less than len.
Eric Andersen08a72202002-09-30 20:52:10 +0000106
Denis Vlasenko10916c52007-10-15 17:28:00 +0000107 CR-LF ->'s CR mapping is also done here, for convenience.
108
109 NB: may fail to remove iacs which wrap around buffer!
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000110 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000111static unsigned char *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000112remove_iacs(struct tsession *ts, int *pnum_totty)
113{
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200114 unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
Eric Andersen08a72202002-09-30 20:52:10 +0000115 unsigned char *ptr = ptr0;
116 unsigned char *totty = ptr;
117 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
Eric Andersen08a72202002-09-30 20:52:10 +0000118 int num_totty;
119
120 while (ptr < end) {
121 if (*ptr != IAC) {
Denis Vlasenko10916c52007-10-15 17:28:00 +0000122 char c = *ptr;
123
124 *totty++ = c;
125 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000126 /* We map \r\n ==> \r for pragmatic reasons.
Eric Andersen3752d332003-12-19 11:30:13 +0000127 * Many client implementations send \r\n when
128 * the user hits the CarriageReturn key.
129 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000130 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
Eric Andersen3752d332003-12-19 11:30:13 +0000131 ptr++;
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000132 continue;
Eric Andersen08a72202002-09-30 20:52:10 +0000133 }
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000134
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) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200151 /* Only the beginning of the IAC is in the
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000152 buffer we were asked to process, we can't
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200153 process this char */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000154 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)
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200162 break; /* incomplete, can't process */
Denis Vlasenkob0150d22008-11-07 01:58:21 +0000163 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;
Eric Andersen08a72202002-09-30 20:52:10 +0000175 }
176
Denis Vlasenko0de37e12007-10-17 11:08:53 +0000177 num_totty = totty - ptr0;
Eric Andersen08a72202002-09-30 20:52:10 +0000178 *pnum_totty = num_totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200179 /* The difference between ptr and totty is number of iacs
180 we removed from the stream. Adjust buf1 accordingly */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000181 if ((ptr - totty) == 0) /* 99.999% of cases */
182 return ptr0;
183 ts->wridx1 += ptr - totty;
184 ts->size1 -= ptr - totty;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200185 /* Move chars meant for the terminal towards the end of the buffer */
Eric Andersen08a72202002-09-30 20:52:10 +0000186 return memmove(ptr - num_totty, ptr0, num_totty);
187}
188
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000189/*
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000190 * Converting single IAC into double on output
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000191 */
192static size_t iac_safe_write(int fd, const char *buf, size_t count)
193{
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000194 const char *IACptr;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000195 size_t wr, rc, total;
196
197 total = 0;
198 while (1) {
199 if (count == 0)
200 return total;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000201 if (*buf == (char)IAC) {
202 static const char IACIAC[] ALIGN1 = { IAC, IAC };
203 rc = safe_write(fd, IACIAC, 2);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000204 if (rc != 2)
205 break;
206 buf++;
207 total++;
208 count--;
209 continue;
210 }
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000211 /* count != 0, *buf != IAC */
212 IACptr = memchr(buf, IAC, count);
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000213 wr = count;
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000214 if (IACptr)
215 wr = IACptr - buf;
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000216 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}
Eric Andersen08a72202002-09-30 20:52:10 +0000231
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200232/* Must match getopt32 string */
233enum {
234 OPT_WATCHCHILD = (1 << 2), /* -K */
235 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200236 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200237 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200238 OPT_SYSLOG = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
239 OPT_WAIT = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200240};
241
Eric Andersen08a72202002-09-30 20:52:10 +0000242static struct tsession *
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000243make_new_session(
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200244 IF_FEATURE_TELNETD_STANDALONE(int sock)
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000245 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000246) {
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200247#if !ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenkoff0e8752010-05-10 04:16:43 +0200248 enum { sock = 0 };
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200249#endif
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000250 const char *login_argv[2];
Eric Andersen08a72202002-09-30 20:52:10 +0000251 struct termios termbuf;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000252 int fd, pid;
Denis Vlasenko85c24712008-03-17 09:04:04 +0000253 char tty_name[GETPTY_BUFSIZE];
Rob Landley1ec5b292006-05-29 07:42:02 +0000254 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
Eric Andersen08a72202002-09-30 20:52:10 +0000255
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000256 /*ts->buf1 = (char *)(ts + 1);*/
257 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
Eric Andersen08a72202002-09-30 20:52:10 +0000258
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200259 /* Got a new connection, set up a tty */
Bernhard Reutner-Fischerae4342c2008-05-19 08:18:50 +0000260 fd = xgetpty(tty_name);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200261 if (fd > G.maxfd)
262 G.maxfd = fd;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000263 ts->ptyfd = fd;
264 ndelay_on(fd);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200265 close_on_exec_on(fd);
266
Denis Vlasenko6d044352008-11-09 00:44:40 +0000267 /* SO_KEEPALIVE by popular demand */
268 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200269#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200270 ts->sockfd_read = sock;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000271 ndelay_on(sock);
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200272 if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
Denis Vlasenko9e237672007-10-17 11:18:49 +0000273 sock++; /* so use fd 1 for output */
274 ndelay_on(sock);
275 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000276 ts->sockfd_write = sock;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200277 if (sock > G.maxfd)
278 G.maxfd = sock;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000279#else
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000280 /* ts->sockfd_read = 0; - done by xzalloc */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000281 ts->sockfd_write = 1;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000282 ndelay_on(0);
283 ndelay_on(1);
284#endif
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200285
Eric Andersen08a72202002-09-30 20:52:10 +0000286 /* 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
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000289 * stuff that requires char-by-char support. */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000290 {
291 static const char iacs_to_send[] ALIGN1 = {
292 IAC, DO, TELOPT_ECHO,
293 IAC, DO, TELOPT_NAWS,
Denys Vlasenkofb132e42010-10-29 11:46:52 +0200294 /* This requires telnetd.ctrlSQ.patch (incomplete) */
295 /*IAC, DO, TELOPT_LFLOW,*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000296 IAC, WILL, TELOPT_ECHO,
297 IAC, WILL, TELOPT_SGA
298 };
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000299 /* This confuses iac_safe_write(), it will try to duplicate
300 * each IAC... */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200301 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000302 //ts->rdidx2 = sizeof(iacs_to_send);
303 //ts->size2 = sizeof(iacs_to_send);
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000304 /* So just stuff it into TCP stream! (no error check...) */
305#if ENABLE_FEATURE_TELNETD_STANDALONE
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000306 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
Denis Vlasenko81c6a912008-11-12 21:14:50 +0000307#else
308 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
309#endif
310 /*ts->rdidx2 = 0; - xzalloc did it */
Denis Vlasenko9f2f8082008-11-11 02:56:39 +0000311 /*ts->size2 = 0;*/
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000312 }
Eric Andersen08a72202002-09-30 20:52:10 +0000313
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100314 fflush_all();
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000315 pid = vfork(); /* NOMMU-friendly */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000316 if (pid < 0) {
317 free(ts);
318 close(fd);
Denis Vlasenko23c81282007-10-16 22:01:23 +0000319 /* sock will be closed by caller */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000320 bb_perror_msg("vfork");
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000321 return NULL;
Eric Andersen08a72202002-09-30 20:52:10 +0000322 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000323 if (pid > 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000324 /* Parent */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000325 ts->shell_pid = pid;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000326 return ts;
Eric Andersen08a72202002-09-30 20:52:10 +0000327 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000328
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000329 /* Child */
330 /* Careful - we are after vfork! */
Eric Andersen08a72202002-09-30 20:52:10 +0000331
Denis Vlasenkod814c982009-02-02 23:43:57 +0000332 /* Restore default signal handling ASAP */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000333 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000334
Denys Vlasenko58c3d212010-11-30 09:17:30 +0100335 pid = getpid();
336
Denys Vlasenkof6916db2010-04-06 17:43:29 +0200337 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
Denis Vlasenkod814c982009-02-02 23:43:57 +0000348 /* Make new session and process group */
349 setsid();
350
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200351 /* Open the child's side of the tty */
Denis Vlasenko9af7c9d2007-01-19 21:19:35 +0000352 /* NB: setsid() disconnects from any previous ctty's. Therefore
353 * we must open child's side of the tty AFTER setsid! */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200354 close(0);
Denis Vlasenko1101d1c2008-07-21 09:22:28 +0000355 xopen(tty_name, O_RDWR); /* becomes our ctty */
356 xdup2(0, 1);
357 xdup2(0, 2);
Denys Vlasenko3a416112010-04-05 22:10:38 +0200358 tcsetpgrp(0, pid); /* switch this tty's process group to us */
359
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200360 /* The pseudo-terminal allocated to the client is configured to operate
361 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000362 tcgetattr(0, &termbuf);
363 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000364 termbuf.c_oflag |= ONLCR | XTABS;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000365 termbuf.c_iflag |= ICRNL;
366 termbuf.c_iflag &= ~IXOFF;
367 /*termbuf.c_lflag &= ~ICANON;*/
Denis Vlasenko202ac502008-11-05 13:20:58 +0000368 tcsetattr_stdin_TCSANOW(&termbuf);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000369
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100370 /* Uses FILE-based I/O to stdout, but does fflush_all(),
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000371 * so should be safe with vfork.
372 * I fear, though, that some users will have ridiculously big
Denis Vlasenko018b1552007-11-06 01:38:46 +0000373 * 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!) */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200376 print_login_issue(G.issuefile, tty_name);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000377
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000378 /* Exec shell / login / whatever */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200379 login_argv[0] = G.loginpath;
Denis Vlasenko88308fe2007-06-25 10:35:11 +0000380 login_argv[1] = NULL;
Denis Vlasenko018b1552007-11-06 01:38:46 +0000381 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200382 * 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 */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200386 BB_EXECVP(G.loginpath, (char **)login_argv);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000387 /* _exit is safer with vfork, and we shouldn't send message
Denis Vlasenko2450c452007-10-15 22:09:15 +0000388 * to remote clients anyway */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200389 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
Eric Andersen08a72202002-09-30 20:52:10 +0000390}
391
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000392#if ENABLE_FEATURE_TELNETD_STANDALONE
393
Eric Andersen08a72202002-09-30 20:52:10 +0000394static void
395free_session(struct tsession *ts)
396{
Denys Vlasenko3a416112010-04-05 22:10:38 +0200397 struct tsession *t;
Eric Andersen08a72202002-09-30 20:52:10 +0000398
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000399 if (option_mask32 & OPT_INETD)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000400 exit(EXIT_SUCCESS);
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000401
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000402 /* Unlink this telnet session from the session list */
Denys Vlasenko3a416112010-04-05 22:10:38 +0200403 t = G.sessions;
Mike Frysinger731f81c2006-05-10 15:23:12 +0000404 if (t == ts)
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200405 G.sessions = ts->next;
Eric Andersen08a72202002-09-30 20:52:10 +0000406 else {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000407 while (t->next != ts)
Eric Andersen08a72202002-09-30 20:52:10 +0000408 t = t->next;
409 t->next = ts->next;
410 }
411
Denis Vlasenkof472b232007-10-16 21:35:17 +0000412#if 0
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000413 /* 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. */
Denis Vlasenkof472b232007-10-16 21:35:17 +0000416 kill(ts->shell_pid, SIGKILL);
Denis Vlasenkod814c982009-02-02 23:43:57 +0000417 waitpid(ts->shell_pid, NULL, 0);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000418#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000419 close(ts->ptyfd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000420 close(ts->sockfd_read);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000421 /* 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
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000423 * we do not reach this */
Eric Andersen08a72202002-09-30 20:52:10 +0000424 free(ts);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000425
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000426 /* Scan all sessions and find new maxfd */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200427 G.maxfd = 0;
428 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000429 while (ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200430 if (G.maxfd < ts->ptyfd)
431 G.maxfd = ts->ptyfd;
432 if (G.maxfd < ts->sockfd_read)
433 G.maxfd = ts->sockfd_read;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000434#if 0
435 /* Again, sockfd_write == sockfd_read here */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200436 if (G.maxfd < ts->sockfd_write)
437 G.maxfd = ts->sockfd_write;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000438#endif
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000439 ts = ts->next;
440 }
Eric Andersen08a72202002-09-30 20:52:10 +0000441}
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000442
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000443#else /* !FEATURE_TELNETD_STANDALONE */
444
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000445/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
Denis Vlasenkoe87b8682007-10-17 14:33:31 +0000446#define free_session(ts) return 0
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000447
448#endif
449
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000450static void handle_sigchld(int sig UNUSED_PARAM)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000451{
452 pid_t pid;
453 struct tsession *ts;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200454 int save_errno = errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000455
Denis Vlasenko018b1552007-11-06 01:38:46 +0000456 /* Looping: more than one child may have exited */
457 while (1) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000458 pid = wait_any_nohang(NULL);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000459 if (pid <= 0)
460 break;
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200461 ts = G.sessions;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000462 while (ts) {
463 if (ts->shell_pid == pid) {
464 ts->shell_pid = -1;
Denys Vlasenko3a416112010-04-05 22:10:38 +0200465// 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);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000474 break;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000475 }
476 ts = ts->next;
477 }
478 }
Denys Vlasenko3a416112010-04-05 22:10:38 +0200479
480 errno = save_errno;
Denis Vlasenko2450c452007-10-15 22:09:15 +0000481}
482
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000483int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000484int telnetd_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen08a72202002-09-30 20:52:10 +0000485{
Eric Andersen08a72202002-09-30 20:52:10 +0000486 fd_set rdfdset, wrfdset;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000487 unsigned opt;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000488 int count;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000489 struct tsession *ts;
490#if ENABLE_FEATURE_TELNETD_STANDALONE
491#define IS_INETD (opt & OPT_INETD)
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200492 int master_fd = master_fd; /* for compiler */
493 int sec_linger = sec_linger;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000494 char *opt_bindaddr = NULL;
495 char *opt_portnbr;
496#else
497 enum {
498 IS_INETD = 1,
499 master_fd = -1,
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000500 };
Glenn L McGrathc2b91862003-09-12 11:27:15 +0000501#endif
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200502 INIT_G();
503
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200504 /* -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";)
Denis Vlasenko2450c452007-10-15 22:09:15 +0000506 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000507 * don't need to guess whether it's ok to pass -i to us */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200508 opt = getopt32(argv, "f:l:Ki"
509 IF_FEATURE_TELNETD_STANDALONE("p:b:F")
510 IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200511 &G.issuefile, &G.loginpath
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200512 IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
513 IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
514 );
Denis Vlasenko2450c452007-10-15 22:09:15 +0000515 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
Denis Vlasenko008eda22007-10-16 10:47:27 +0000521 * behavior with/without -F, -i */
Denis Vlasenko018b1552007-11-06 01:38:46 +0000522 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000523 }
524 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000525 /* Redirect log to syslog early, if needed */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200526 if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
Denis Vlasenko5e4fda02009-03-08 23:46:48 +0000527 openlog(applet_name, LOG_PID, LOG_DAEMON);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000528 logmode = LOGMODE_SYSLOG;
529 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000530#if ENABLE_FEATURE_TELNETD_STANDALONE
531 if (IS_INETD) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200532 G.sessions = make_new_session(0);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200533 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200534 return 1; /* make_new_session printed error message */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000535 } else {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200536 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 }
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200544 close_on_exec_on(master_fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000545 }
Rob Landley00e76cb2005-05-10 23:53:33 +0000546#else
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200547 G.sessions = make_new_session();
548 if (!G.sessions) /* pty opening or vfork problem, exit */
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200549 return 1; /* make_new_session printed error message */
Rob Landley00e76cb2005-05-10 23:53:33 +0000550#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000551
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000552 /* We don't want to die if just one session is broken */
553 signal(SIGPIPE, SIG_IGN);
Eric Andersen08a72202002-09-30 20:52:10 +0000554
Denis Vlasenko2450c452007-10-15 22:09:15 +0000555 if (opt & OPT_WATCHCHILD)
556 signal(SIGCHLD, handle_sigchld);
Denis Vlasenko018b1552007-11-06 01:38:46 +0000557 else /* prevent dead children from becoming zombies */
558 signal(SIGCHLD, SIG_IGN);
Denis Vlasenko2450c452007-10-15 22:09:15 +0000559
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000560/*
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200561 This is how the buffers are used. The arrows indicate data flow.
562
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000563 +-------+ 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*/
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000578 again:
579 FD_ZERO(&rdfdset);
580 FD_ZERO(&wrfdset);
Eric Andersen08a72202002-09-30 20:52:10 +0000581
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000582 /* 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. */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200586 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000587 while (ts) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200588 struct tsession *next = ts->next; /* in case we free ts */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000589 if (ts->shell_pid == -1) {
Denis Vlasenko018b1552007-11-06 01:38:46 +0000590 /* Child died and we detected that */
Denis Vlasenko2450c452007-10-15 22:09:15 +0000591 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
Denis Vlasenko018b1552007-11-06 01:38:46 +0000607 * take master_fd into account when it finds new
Denis Vlasenko2450c452007-10-15 22:09:15 +0000608 * maxfd among remaining fd's */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200609 if (master_fd > G.maxfd)
610 G.maxfd = master_fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000611 }
612
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200613 {
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200614 struct timeval *tv_ptr = NULL;
Denys Vlasenko36df0482009-10-19 16:07:28 +0200615#if ENABLE_FEATURE_TELNETD_INETD_WAIT
616 struct timeval tv;
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200617 if ((opt & OPT_WAIT) && !G.sessions) {
618 tv.tv_sec = sec_linger;
619 tv.tv_usec = 0;
620 tv_ptr = &tv;
621 }
Denys Vlasenko36df0482009-10-19 16:07:28 +0200622#endif
Denys Vlasenkoed1667e2009-09-04 02:21:13 +0200623 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
624 }
625 if (count == 0) /* "telnetd -w SEC" timed out */
626 return 0;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000627 if (count < 0)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000628 goto again; /* EINTR or ENOMEM */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000629
630#if ENABLE_FEATURE_TELNETD_STANDALONE
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200631 /* Check for and accept new sessions */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000632 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000633 int fd;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000634 struct tsession *new_ts;
635
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000636 fd = accept(master_fd, NULL, NULL);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000637 if (fd < 0)
638 goto again;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200639 close_on_exec_on(fd);
640
641 /* Create a new session and link it into active list */
642 new_ts = make_new_session(fd);
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000643 if (new_ts) {
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200644 new_ts->next = G.sessions;
645 G.sessions = new_ts;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000646 } else {
647 close(fd);
Eric Andersen08a72202002-09-30 20:52:10 +0000648 }
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000649 }
650#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000651
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200652 /* Then check for data tunneling */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200653 ts = G.sessions;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000654 while (ts) { /* For all sessions... */
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200655 struct tsession *next = ts->next; /* in case we free ts */
Eric Andersen08a72202002-09-30 20:52:10 +0000656
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000657 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000658 int num_totty;
Denis Vlasenko10916c52007-10-15 17:28:00 +0000659 unsigned char *ptr;
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200660 /* Write to pty from buffer 1 */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000661 ptr = remove_iacs(ts, &num_totty);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000662 count = safe_write(ts->ptyfd, ptr, num_totty);
663 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000664 if (errno == EAGAIN)
665 goto skip1;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000666 goto kill_session;
Eric Andersen08a72202002-09-30 20:52:10 +0000667 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000668 ts->size1 -= count;
669 ts->wridx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000670 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
Eric Andersen08a72202002-09-30 20:52:10 +0000671 ts->wridx1 = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000672 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000673 skip1:
674 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200675 /* Write to socket from buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000676 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200677 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000678 if (count < 0) {
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000679 if (errno == EAGAIN)
680 goto skip2;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000681 goto kill_session;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000682 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000683 ts->size2 -= count;
684 ts->wridx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000685 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000686 ts->wridx2 = 0;
687 }
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000688 skip2:
Denis Vlasenko10916c52007-10-15 17:28:00 +0000689 /* 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,
Denis Vlasenkof472b232007-10-16 21:35:17 +0000692 * we rely instead on this code making it virtually impossible
Denis Vlasenko10916c52007-10-15 17:28:00 +0000693 * to have wrapped iac (people don't type at 2k/second).
694 * It also allows for bigger reads in common case. */
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000695 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 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000703
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000704 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200705 /* Read from socket to buffer 1 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000706 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200707 count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000708 if (count <= 0) {
709 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000710 goto skip3;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000711 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000712 }
713 /* Ignore trailing NUL if it is there */
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200714 if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
Denis Vlasenkof472b232007-10-16 21:35:17 +0000715 --count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000716 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000717 ts->size1 += count;
718 ts->rdidx1 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000719 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
720 ts->rdidx1 = 0;
721 }
722 skip3:
723 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
Denys Vlasenko1d77db82009-06-10 13:38:08 +0200724 /* Read from pty to buffer 2 */
Denis Vlasenko10916c52007-10-15 17:28:00 +0000725 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
Denys Vlasenkoa4bcbd02009-06-09 23:01:24 +0200726 count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
Denis Vlasenko10916c52007-10-15 17:28:00 +0000727 if (count <= 0) {
728 if (count < 0 && errno == EAGAIN)
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000729 goto skip4;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000730 goto kill_session;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000731 }
Denis Vlasenko10916c52007-10-15 17:28:00 +0000732 ts->size2 += count;
733 ts->rdidx2 += count;
Denis Vlasenko59d7c432007-10-15 15:19:36 +0000734 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
735 ts->rdidx2 = 0;
736 }
737 skip4:
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000738 ts = next;
Denis Vlasenkof472b232007-10-16 21:35:17 +0000739 continue;
740 kill_session:
Denys Vlasenko3a416112010-04-05 22:10:38 +0200741 if (ts->shell_pid > 0)
742 update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
Denis Vlasenkof472b232007-10-16 21:35:17 +0000743 free_session(ts);
744 ts = next;
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000745 }
Denis Vlasenkof472b232007-10-16 21:35:17 +0000746
Denis Vlasenko75f8d082006-11-22 15:54:52 +0000747 goto again;
Eric Andersen08a72202002-09-30 20:52:10 +0000748}