blob: 24160057bbd9df946fafbe466381c41eca35eabf [file] [log] [blame]
Eric Andersen28c70b32000-06-14 20:42:57 +00001/* vi: set sw=4 ts=4: */
Erik Andersenf7c49ef2000-02-22 17:17:45 +00002/*
Eric Andersen28c70b32000-06-14 20:42:57 +00003 * telnet implementation for busybox
Erik Andersenf7c49ef2000-02-22 17:17:45 +00004 *
Eric Andersen28c70b32000-06-14 20:42:57 +00005 * Author: Tomi Ollila <too@iki.fi>
6 * Copyright (C) 1994-2000 by Tomi Ollila
7 *
8 * Created: Thu Apr 7 13:29:41 1994 too
9 * Last modified: Fri Jun 9 14:34:24 2000 too
Erik Andersenf7c49ef2000-02-22 17:17:45 +000010 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
Eric Andersen28c70b32000-06-14 20:42:57 +000025 * HISTORY
26 * Revision 3.1 1994/04/17 11:31:54 too
27 * initial revision
Eric Andersencb81e642003-07-14 21:21:08 +000028 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
Eric Andersen7e1273e2001-05-07 17:57:45 +000029 * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
30 * <jam@ltsp.org>
Eric Andersen539ffc92004-02-22 12:25:47 +000031 * Modified 2004/02/11 to add ability to pass the USER variable to remote host
32 * by Fernando Silveira <swrh@gmx.net>
Erik Andersenf7c49ef2000-02-22 17:17:45 +000033 *
Erik Andersenf7c49ef2000-02-22 17:17:45 +000034 */
35
Erik Andersenf7c49ef2000-02-22 17:17:45 +000036#include <termios.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000037#include <unistd.h>
38#include <errno.h>
39#include <stdlib.h>
40#include <stdarg.h>
Eric Andersen2276d832002-07-11 11:11:56 +000041#include <string.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000042#include <signal.h>
43#include <arpa/telnet.h>
Erik Andersenf7c49ef2000-02-22 17:17:45 +000044#include <sys/types.h>
45#include <sys/socket.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000046#include <netinet/in.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000047#include "busybox.h"
Erik Andersenf7c49ef2000-02-22 17:17:45 +000048
Eric Andersen28c70b32000-06-14 20:42:57 +000049#if 0
Mark Whitley59ab0252001-01-23 22:30:04 +000050static const int DOTRACE = 1;
Eric Andersen28c70b32000-06-14 20:42:57 +000051#endif
52
Pavel Roskin616d13b2000-07-28 19:38:27 +000053#ifdef DOTRACE
Eric Andersen28c70b32000-06-14 20:42:57 +000054#include <arpa/inet.h> /* for inet_ntoa()... */
55#define TRACE(x, y) do { if (x) printf y; } while (0)
56#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +000057#define TRACE(x, y)
Eric Andersen28c70b32000-06-14 20:42:57 +000058#endif
59
60#if 0
61#define USE_POLL
62#include <sys/poll.h>
63#else
64#include <sys/time.h>
65#endif
66
Glenn L McGrath78b0e372001-06-26 02:06:08 +000067#define DATABUFSIZE 128
68#define IACBUFSIZE 128
Eric Andersen28c70b32000-06-14 20:42:57 +000069
Mark Whitley59ab0252001-01-23 22:30:04 +000070static const int CHM_TRY = 0;
71static const int CHM_ON = 1;
72static const int CHM_OFF = 2;
Eric Andersen28c70b32000-06-14 20:42:57 +000073
Mark Whitley59ab0252001-01-23 22:30:04 +000074static const int UF_ECHO = 0x01;
75static const int UF_SGA = 0x02;
Eric Andersen28c70b32000-06-14 20:42:57 +000076
Mark Whitley59ab0252001-01-23 22:30:04 +000077enum {
78 TS_0 = 1,
79 TS_IAC = 2,
80 TS_OPT = 3,
81 TS_SUB1 = 4,
82 TS_SUB2 = 5,
83};
Eric Andersen28c70b32000-06-14 20:42:57 +000084
85#define WriteCS(fd, str) write(fd, str, sizeof str -1)
86
87typedef unsigned char byte;
88
89/* use globals to reduce size ??? */ /* test this hypothesis later */
Eric Andersen92d23242001-03-19 23:49:41 +000090static struct Globalvars {
Eric Andersen28c70b32000-06-14 20:42:57 +000091 int netfd; /* console fd:s are 0 and 1 (and 2) */
92 /* same buffer used both for network and console read/write */
Glenn L McGrath78b0e372001-06-26 02:06:08 +000093 char buf[DATABUFSIZE]; /* allocating so static size is smaller */
Eric Andersen28c70b32000-06-14 20:42:57 +000094 byte telstate; /* telnet negotiation state from network input */
95 byte telwish; /* DO, DONT, WILL, WONT */
96 byte charmode;
97 byte telflags;
98 byte gotsig;
Paul Foxf2ddc052005-07-20 19:55:19 +000099 byte do_termios;
Eric Andersen28c70b32000-06-14 20:42:57 +0000100 /* buffer to handle telnet negotiations */
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000101 char iacbuf[IACBUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +0000102 short iaclen; /* could even use byte */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000103 struct termios termios_def;
104 struct termios termios_raw;
Eric Andersen28c70b32000-06-14 20:42:57 +0000105} G;
106
107#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
108
109#ifdef USE_GLOBALVAR_PTR
110struct Globalvars * Gptr;
111#define G (*Gptr)
Eric Andersen28c70b32000-06-14 20:42:57 +0000112#endif
113
Eric Andersencd8c4362001-11-10 11:22:46 +0000114static inline void iacflush(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000115{
116 write(G.netfd, G.iacbuf, G.iaclen);
117 G.iaclen = 0;
118}
119
120/* Function prototypes */
Eric Andersencd8c4362001-11-10 11:22:46 +0000121static void rawmode(void);
122static void cookmode(void);
123static void do_linemode(void);
124static void will_charmode(void);
Eric Andersen28c70b32000-06-14 20:42:57 +0000125static void telopt(byte c);
126static int subneg(byte c);
Eric Andersen28c70b32000-06-14 20:42:57 +0000127
128/* Some globals */
129static int one = 1;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000130
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000131#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000132static char *ttype;
133#endif
134
Eric Andersen539ffc92004-02-22 12:25:47 +0000135#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000136static const char *autologin;
Eric Andersen539ffc92004-02-22 12:25:47 +0000137#endif
138
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000139#ifdef CONFIG_FEATURE_AUTOWIDTH
140static int win_width, win_height;
141#endif
142
Eric Andersen28c70b32000-06-14 20:42:57 +0000143static void doexit(int ev)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000144{
Eric Andersen28c70b32000-06-14 20:42:57 +0000145 cookmode();
146 exit(ev);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000147}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000148
Eric Andersencd8c4362001-11-10 11:22:46 +0000149static void conescape(void)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000150{
Eric Andersen28c70b32000-06-14 20:42:57 +0000151 char b;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000152
Eric Andersen28c70b32000-06-14 20:42:57 +0000153 if (G.gotsig) /* came from line mode... go raw */
154 rawmode();
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000155
Eric Andersen28c70b32000-06-14 20:42:57 +0000156 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
157 " l go to line mode\r\n"
158 " c go to character mode\r\n"
159 " z suspend telnet\r\n"
160 " e exit telnet\r\n");
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000161
Eric Andersen28c70b32000-06-14 20:42:57 +0000162 if (read(0, &b, 1) <= 0)
163 doexit(1);
164
165 switch (b)
166 {
167 case 'l':
168 if (!G.gotsig)
169 {
170 do_linemode();
171 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000172 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000173 break;
174 case 'c':
175 if (G.gotsig)
176 {
177 will_charmode();
178 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000179 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000180 break;
181 case 'z':
182 cookmode();
183 kill(0, SIGTSTP);
184 rawmode();
185 break;
186 case 'e':
187 doexit(0);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000188 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000189
190 WriteCS(1, "continuing...\r\n");
191
192 if (G.gotsig)
193 cookmode();
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000194
Eric Andersen28c70b32000-06-14 20:42:57 +0000195 rrturn:
196 G.gotsig = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000197
Eric Andersen28c70b32000-06-14 20:42:57 +0000198}
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000199static void handlenetoutput(int len)
Eric Andersen28c70b32000-06-14 20:42:57 +0000200{
201 /* here we could do smart tricks how to handle 0xFF:s in output
202 * stream like writing twice every sequence of FF:s (thus doing
203 * many write()s. But I think interactive telnet application does
204 * not need to be 100% 8-bit clean, so changing every 0xff:s to
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000205 * 0x7f:s
206 *
207 * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
208 * I don't agree.
209 * first - I cannot use programs like sz/rz
210 * second - the 0x0D is sent as one character and if the next
211 * char is 0x0A then it's eaten by a server side.
212 * third - whay doy you have to make 'many write()s'?
213 * I don't understand.
214 * So I implemented it. It's realy useful for me. I hope that
215 * others people will find it interesting to.
216 */
Eric Andersen28c70b32000-06-14 20:42:57 +0000217
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000218 int i, j;
Eric Andersen28c70b32000-06-14 20:42:57 +0000219 byte * p = G.buf;
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000220 byte outbuf[4*DATABUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +0000221
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000222 for (i = len, j = 0; i > 0; i--, p++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000223 {
224 if (*p == 0x1d)
225 {
226 conescape();
227 return;
228 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000229 outbuf[j++] = *p;
Eric Andersen28c70b32000-06-14 20:42:57 +0000230 if (*p == 0xff)
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000231 outbuf[j++] = 0xff;
232 else if (*p == 0x0d)
233 outbuf[j++] = 0x00;
Eric Andersen28c70b32000-06-14 20:42:57 +0000234 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000235 if (j > 0 )
236 write(G.netfd, outbuf, j);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000237}
238
Eric Andersen28c70b32000-06-14 20:42:57 +0000239
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000240static void handlenetinput(int len)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000241{
Eric Andersen28c70b32000-06-14 20:42:57 +0000242 int i;
243 int cstart = 0;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000244
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000245 for (i = 0; i < len; i++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000246 {
247 byte c = G.buf[i];
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000248
Eric Andersen28c70b32000-06-14 20:42:57 +0000249 if (G.telstate == 0) /* most of the time state == 0 */
250 {
251 if (c == IAC)
252 {
253 cstart = i;
254 G.telstate = TS_IAC;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000255 }
256 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000257 else
258 switch (G.telstate)
259 {
260 case TS_0:
261 if (c == IAC)
262 G.telstate = TS_IAC;
263 else
264 G.buf[cstart++] = c;
265 break;
266
267 case TS_IAC:
268 if (c == IAC) /* IAC IAC -> 0xFF */
269 {
270 G.buf[cstart++] = c;
271 G.telstate = TS_0;
272 break;
273 }
274 /* else */
275 switch (c)
276 {
277 case SB:
278 G.telstate = TS_SUB1;
279 break;
280 case DO:
281 case DONT:
282 case WILL:
283 case WONT:
284 G.telwish = c;
285 G.telstate = TS_OPT;
286 break;
287 default:
288 G.telstate = TS_0; /* DATA MARK must be added later */
289 }
290 break;
291 case TS_OPT: /* WILL, WONT, DO, DONT */
292 telopt(c);
293 G.telstate = TS_0;
294 break;
295 case TS_SUB1: /* Subnegotiation */
296 case TS_SUB2: /* Subnegotiation */
Matt Kraai1f0c4362001-12-20 23:13:26 +0000297 if (subneg(c))
Eric Andersen28c70b32000-06-14 20:42:57 +0000298 G.telstate = TS_0;
299 break;
300 }
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000301 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000302 if (G.telstate)
303 {
304 if (G.iaclen) iacflush();
305 if (G.telstate == TS_0) G.telstate = 0;
306
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000307 len = cstart;
Eric Andersen28c70b32000-06-14 20:42:57 +0000308 }
309
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000310 if (len)
311 write(1, G.buf, len);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000312}
313
Eric Andersen28c70b32000-06-14 20:42:57 +0000314
315/* ******************************* */
316
317static inline void putiac(int c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000318{
Eric Andersen28c70b32000-06-14 20:42:57 +0000319 G.iacbuf[G.iaclen++] = c;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000320}
321
Eric Andersen28c70b32000-06-14 20:42:57 +0000322
323static void putiac2(byte wwdd, byte c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000324{
Eric Andersen28c70b32000-06-14 20:42:57 +0000325 if (G.iaclen + 3 > IACBUFSIZE)
326 iacflush();
327
328 putiac(IAC);
329 putiac(wwdd);
330 putiac(c);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000331}
332
Eric Andersen28c70b32000-06-14 20:42:57 +0000333#if 0
334static void putiac1(byte c)
335{
336 if (G.iaclen + 2 > IACBUFSIZE)
337 iacflush();
338
339 putiac(IAC);
340 putiac(c);
341}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000342#endif
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000343
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000344#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000345static void putiac_subopt(byte c, char *str)
346{
347 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
348
349 if (G.iaclen + len > IACBUFSIZE)
350 iacflush();
351
352 putiac(IAC);
353 putiac(SB);
354 putiac(c);
355 putiac(0);
356
357 while(*str)
358 putiac(*str++);
359
360 putiac(IAC);
361 putiac(SE);
362}
363#endif
364
Eric Andersen539ffc92004-02-22 12:25:47 +0000365#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
366static void putiac_subopt_autologin(void)
367{
368 int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2)
369 char *user = "USER";
370
371 if (G.iaclen + len > IACBUFSIZE)
372 iacflush();
373
374 putiac(IAC);
375 putiac(SB);
376 putiac(TELOPT_NEW_ENVIRON);
377 putiac(TELQUAL_IS);
378 putiac(NEW_ENV_VAR);
379
380 while(*user)
381 putiac(*user++);
382
383 putiac(NEW_ENV_VALUE);
384
385 while(*autologin)
386 putiac(*autologin++);
387
388 putiac(IAC);
389 putiac(SE);
390}
391#endif
392
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000393#ifdef CONFIG_FEATURE_AUTOWIDTH
394static void putiac_naws(byte c, int x, int y)
395{
396 if (G.iaclen + 9 > IACBUFSIZE)
397 iacflush();
398
399 putiac(IAC);
400 putiac(SB);
401 putiac(c);
402
403 putiac((x >> 8) & 0xff);
404 putiac(x & 0xff);
405 putiac((y >> 8) & 0xff);
406 putiac(y & 0xff);
407
408 putiac(IAC);
409 putiac(SE);
410}
411#endif
412
Eric Andersen28c70b32000-06-14 20:42:57 +0000413/* void putiacstring (subneg strings) */
414
415/* ******************************* */
416
Eric Andersen3e6ff902001-03-09 21:24:12 +0000417static char const escapecharis[] = "\r\nEscape character is ";
Eric Andersen28c70b32000-06-14 20:42:57 +0000418
Eric Andersencd8c4362001-11-10 11:22:46 +0000419static void setConMode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000420{
421 if (G.telflags & UF_ECHO)
422 {
423 if (G.charmode == CHM_TRY) {
424 G.charmode = CHM_ON;
Matt Kraai12f417e2001-01-18 02:57:08 +0000425 printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000426 rawmode();
427 }
428 }
429 else
430 {
431 if (G.charmode != CHM_OFF) {
432 G.charmode = CHM_OFF;
Matt Kraai12f417e2001-01-18 02:57:08 +0000433 printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000434 cookmode();
435 }
436 }
437}
438
439/* ******************************* */
440
Eric Andersencd8c4362001-11-10 11:22:46 +0000441static void will_charmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000442{
443 G.charmode = CHM_TRY;
444 G.telflags |= (UF_ECHO | UF_SGA);
445 setConMode();
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000446
Eric Andersen28c70b32000-06-14 20:42:57 +0000447 putiac2(DO, TELOPT_ECHO);
448 putiac2(DO, TELOPT_SGA);
449 iacflush();
450}
451
Eric Andersencd8c4362001-11-10 11:22:46 +0000452static void do_linemode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000453{
454 G.charmode = CHM_TRY;
455 G.telflags &= ~(UF_ECHO | UF_SGA);
456 setConMode();
457
458 putiac2(DONT, TELOPT_ECHO);
459 putiac2(DONT, TELOPT_SGA);
460 iacflush();
461}
462
463/* ******************************* */
464
465static inline void to_notsup(char c)
466{
467 if (G.telwish == WILL) putiac2(DONT, c);
468 else if (G.telwish == DO) putiac2(WONT, c);
469}
470
Eric Andersencd8c4362001-11-10 11:22:46 +0000471static inline void to_echo(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000472{
473 /* if server requests ECHO, don't agree */
474 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
475 else if (G.telwish == DONT) return;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000476
Eric Andersen28c70b32000-06-14 20:42:57 +0000477 if (G.telflags & UF_ECHO)
478 {
479 if (G.telwish == WILL)
480 return;
481 }
482 else
483 if (G.telwish == WONT)
484 return;
485
486 if (G.charmode != CHM_OFF)
487 G.telflags ^= UF_ECHO;
488
489 if (G.telflags & UF_ECHO)
490 putiac2(DO, TELOPT_ECHO);
491 else
492 putiac2(DONT, TELOPT_ECHO);
493
494 setConMode();
495 WriteCS(1, "\r\n"); /* sudden modec */
496}
497
Eric Andersencd8c4362001-11-10 11:22:46 +0000498static inline void to_sga(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000499{
500 /* daemon always sends will/wont, client do/dont */
501
502 if (G.telflags & UF_SGA)
503 {
504 if (G.telwish == WILL)
505 return;
506 }
507 else
508 if (G.telwish == WONT)
509 return;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000510
Eric Andersen28c70b32000-06-14 20:42:57 +0000511 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
512 putiac2(DO, TELOPT_SGA);
513 else
514 putiac2(DONT, TELOPT_SGA);
515
516 return;
517}
518
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000519#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersencd8c4362001-11-10 11:22:46 +0000520static inline void to_ttype(void)
Eric Andersen7e1273e2001-05-07 17:57:45 +0000521{
522 /* Tell server we will (or won't) do TTYPE */
523
524 if(ttype)
525 putiac2(WILL, TELOPT_TTYPE);
526 else
527 putiac2(WONT, TELOPT_TTYPE);
528
529 return;
530}
531#endif
532
Eric Andersen539ffc92004-02-22 12:25:47 +0000533#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
534static inline void to_new_environ(void)
535{
536 /* Tell server we will (or will not) do AUTOLOGIN */
537
538 if (autologin)
539 putiac2(WILL, TELOPT_NEW_ENVIRON);
540 else
541 putiac2(WONT, TELOPT_NEW_ENVIRON);
542
543 return;
544}
545#endif
546
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000547#ifdef CONFIG_FEATURE_AUTOWIDTH
Tim Rikered8e0362002-04-26 07:53:39 +0000548static inline void to_naws(void)
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000549{
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000550 /* Tell server we will do NAWS */
551 putiac2(WILL, TELOPT_NAWS);
552 return;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000553}
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000554#endif
555
Eric Andersen28c70b32000-06-14 20:42:57 +0000556static void telopt(byte c)
557{
558 switch (c)
559 {
Eric Andersen41634062002-04-26 08:44:17 +0000560 case TELOPT_ECHO: to_echo(); break;
561 case TELOPT_SGA: to_sga(); break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000562#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen41634062002-04-26 08:44:17 +0000563 case TELOPT_TTYPE: to_ttype();break;
Eric Andersen7e1273e2001-05-07 17:57:45 +0000564#endif
Eric Andersen539ffc92004-02-22 12:25:47 +0000565#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
566 case TELOPT_NEW_ENVIRON: to_new_environ(); break;
567#endif
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000568#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen41634062002-04-26 08:44:17 +0000569 case TELOPT_NAWS: to_naws();
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000570 putiac_naws(c, win_width, win_height);
571 break;
572#endif
Eric Andersen8db361b2002-04-26 08:00:33 +0000573 default: to_notsup(c);
574 break;
Eric Andersen28c70b32000-06-14 20:42:57 +0000575 }
576}
577
578
579/* ******************************* */
580
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000581/* subnegotiation -- ignore all (except TTYPE,NAWS) */
Eric Andersen28c70b32000-06-14 20:42:57 +0000582
583static int subneg(byte c)
584{
585 switch (G.telstate)
586 {
587 case TS_SUB1:
588 if (c == IAC)
589 G.telstate = TS_SUB2;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000590#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000591 else
592 if (c == TELOPT_TTYPE)
593 putiac_subopt(TELOPT_TTYPE,ttype);
594#endif
Eric Andersen539ffc92004-02-22 12:25:47 +0000595#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
596 else
597 if (c == TELOPT_NEW_ENVIRON)
598 putiac_subopt_autologin();
599#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000600 break;
601 case TS_SUB2:
602 if (c == SE)
603 return TRUE;
604 G.telstate = TS_SUB1;
605 /* break; */
606 }
607 return FALSE;
608}
609
610/* ******************************* */
611
612static void fgotsig(int sig)
613{
614 G.gotsig = sig;
615}
616
617
Eric Andersencd8c4362001-11-10 11:22:46 +0000618static void rawmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000619{
Paul Foxf2ddc052005-07-20 19:55:19 +0000620 if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_raw);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000621}
Eric Andersen28c70b32000-06-14 20:42:57 +0000622
Eric Andersencd8c4362001-11-10 11:22:46 +0000623static void cookmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000624{
Paul Foxf2ddc052005-07-20 19:55:19 +0000625 if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_def);
Eric Andersen28c70b32000-06-14 20:42:57 +0000626}
627
628extern int telnet_main(int argc, char** argv)
629{
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000630 int len;
Eric Andersene6dc4392003-10-31 09:31:46 +0000631 struct sockaddr_in s_in;
Eric Andersen28c70b32000-06-14 20:42:57 +0000632#ifdef USE_POLL
633 struct pollfd ufds[2];
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000634#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000635 fd_set readfds;
636 int maxfd;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000637#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000638
Eric Andersen539ffc92004-02-22 12:25:47 +0000639#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
640 int opt;
641#endif
642
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000643#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen8efe9672003-09-15 08:33:45 +0000644 get_terminal_width_height(0, &win_width, &win_height);
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000645#endif
646
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000647#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000648 ttype = getenv("TERM");
649#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000650
651 memset(&G, 0, sizeof G);
652
Paul Foxf2ddc052005-07-20 19:55:19 +0000653 if (tcgetattr(0, &G.termios_def) >= 0) {
654 G.do_termios = 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000655
Paul Foxf2ddc052005-07-20 19:55:19 +0000656 G.termios_raw = G.termios_def;
657 cfmakeraw(&G.termios_raw);
658 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000659
Glenn L McGrathffccf6e2003-12-20 01:47:18 +0000660 if (argc < 2)
661 bb_show_usage();
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000662
Eric Andersen539ffc92004-02-22 12:25:47 +0000663#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
664 autologin = NULL;
665 while ((opt = getopt(argc, argv, "al:")) != EOF) {
666 switch (opt) {
667 case 'l':
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000668 autologin = optarg;
Eric Andersen539ffc92004-02-22 12:25:47 +0000669 break;
670 case 'a':
671 autologin = getenv("USER");
672 break;
673 case '?':
674 bb_show_usage();
675 break;
676 }
677 }
678 if (optind < argc) {
679 bb_lookup_host(&s_in, argv[optind++]);
680 s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] :
681 "telnet", "tcp", 23);
682 if (optind < argc)
683 bb_show_usage();
684 } else
685 bb_show_usage();
686#else
Glenn L McGrathffccf6e2003-12-20 01:47:18 +0000687 bb_lookup_host(&s_in, argv[1]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000688 s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23);
Eric Andersen539ffc92004-02-22 12:25:47 +0000689#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000690
Eric Andersene6dc4392003-10-31 09:31:46 +0000691 G.netfd = xconnect(&s_in);
Eric Andersen28c70b32000-06-14 20:42:57 +0000692
Eric Andersen0b315862002-07-03 11:51:44 +0000693 setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
Eric Andersen28c70b32000-06-14 20:42:57 +0000694
695 signal(SIGINT, fgotsig);
696
697#ifdef USE_POLL
698 ufds[0].fd = 0; ufds[1].fd = G.netfd;
699 ufds[0].events = ufds[1].events = POLLIN;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000700#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000701 FD_ZERO(&readfds);
702 FD_SET(0, &readfds);
703 FD_SET(G.netfd, &readfds);
704 maxfd = G.netfd + 1;
705#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000706
Eric Andersen28c70b32000-06-14 20:42:57 +0000707 while (1)
708 {
709#ifndef USE_POLL
710 fd_set rfds = readfds;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000711
Eric Andersen28c70b32000-06-14 20:42:57 +0000712 switch (select(maxfd, &rfds, NULL, NULL, NULL))
713#else
714 switch (poll(ufds, 2, -1))
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000715#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000716 {
717 case 0:
718 /* timeout */
719 case -1:
720 /* error, ignore and/or log something, bay go to loop */
721 if (G.gotsig)
722 conescape();
723 else
724 sleep(1);
725 break;
726 default:
727
728#ifdef USE_POLL
729 if (ufds[0].revents) /* well, should check POLLIN, but ... */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000730#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000731 if (FD_ISSET(0, &rfds))
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000732#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000733 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000734 len = read(0, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000735
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000736 if (len <= 0)
Eric Andersen28c70b32000-06-14 20:42:57 +0000737 doexit(0);
738
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000739 TRACE(0, ("Read con: %d\n", len));
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000740
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000741 handlenetoutput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000742 }
743
744#ifdef USE_POLL
745 if (ufds[1].revents) /* well, should check POLLIN, but ... */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000746#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000747 if (FD_ISSET(G.netfd, &rfds))
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000748#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000749 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000750 len = read(G.netfd, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000751
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000752 if (len <= 0)
Eric Andersen28c70b32000-06-14 20:42:57 +0000753 {
754 WriteCS(1, "Connection closed by foreign host.\r\n");
755 doexit(1);
756 }
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000757 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
Eric Andersen28c70b32000-06-14 20:42:57 +0000758
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000759 handlenetinput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000760 }
761 }
762 }
763}
764
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000765/*
Eric Andersen28c70b32000-06-14 20:42:57 +0000766Local Variables:
767c-file-style: "linux"
768c-basic-offset: 4
769tab-width: 4
770End:
771*/