blob: 86586600ba3b5b45d644cbc71bc895e3870617b1 [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 *
Rob Landley1b751c82005-10-28 09:24:33 +000011 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Erik Andersenf7c49ef2000-02-22 17:17:45 +000012 *
Eric Andersen28c70b32000-06-14 20:42:57 +000013 * HISTORY
14 * Revision 3.1 1994/04/17 11:31:54 too
15 * initial revision
Eric Andersencb81e642003-07-14 21:21:08 +000016 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
Eric Andersen7e1273e2001-05-07 17:57:45 +000017 * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
18 * <jam@ltsp.org>
Eric Andersen539ffc92004-02-22 12:25:47 +000019 * Modified 2004/02/11 to add ability to pass the USER variable to remote host
20 * by Fernando Silveira <swrh@gmx.net>
Erik Andersenf7c49ef2000-02-22 17:17:45 +000021 *
Erik Andersenf7c49ef2000-02-22 17:17:45 +000022 */
23
Erik Andersenf7c49ef2000-02-22 17:17:45 +000024#include <termios.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000025#include <arpa/telnet.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000026#include <netinet/in.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000027#include "busybox.h"
Erik Andersenf7c49ef2000-02-22 17:17:45 +000028
Pavel Roskin616d13b2000-07-28 19:38:27 +000029#ifdef DOTRACE
Eric Andersen28c70b32000-06-14 20:42:57 +000030#include <arpa/inet.h> /* for inet_ntoa()... */
31#define TRACE(x, y) do { if (x) printf y; } while (0)
32#else
Eric Andersenc7bda1c2004-03-15 08:29:22 +000033#define TRACE(x, y)
Eric Andersen28c70b32000-06-14 20:42:57 +000034#endif
35
Glenn L McGrath78b0e372001-06-26 02:06:08 +000036#define DATABUFSIZE 128
37#define IACBUFSIZE 128
Eric Andersen28c70b32000-06-14 20:42:57 +000038
Mark Whitley59ab0252001-01-23 22:30:04 +000039enum {
Rob Landleybc68cd12006-03-10 19:22:06 +000040 CHM_TRY = 0,
41 CHM_ON = 1,
42 CHM_OFF = 2,
43
44 UF_ECHO = 0x01,
45 UF_SGA = 0x02,
46
Mark Whitley59ab0252001-01-23 22:30:04 +000047 TS_0 = 1,
48 TS_IAC = 2,
49 TS_OPT = 3,
50 TS_SUB1 = 4,
51 TS_SUB2 = 5,
52};
Eric Andersen28c70b32000-06-14 20:42:57 +000053
54#define WriteCS(fd, str) write(fd, str, sizeof str -1)
55
56typedef unsigned char byte;
57
58/* use globals to reduce size ??? */ /* test this hypothesis later */
Eric Andersen92d23242001-03-19 23:49:41 +000059static struct Globalvars {
Eric Andersen28c70b32000-06-14 20:42:57 +000060 int netfd; /* console fd:s are 0 and 1 (and 2) */
61 /* same buffer used both for network and console read/write */
Glenn L McGrath78b0e372001-06-26 02:06:08 +000062 char buf[DATABUFSIZE]; /* allocating so static size is smaller */
Eric Andersen28c70b32000-06-14 20:42:57 +000063 byte telstate; /* telnet negotiation state from network input */
64 byte telwish; /* DO, DONT, WILL, WONT */
65 byte charmode;
66 byte telflags;
67 byte gotsig;
Paul Foxf2ddc052005-07-20 19:55:19 +000068 byte do_termios;
Eric Andersen28c70b32000-06-14 20:42:57 +000069 /* buffer to handle telnet negotiations */
Glenn L McGrath78b0e372001-06-26 02:06:08 +000070 char iacbuf[IACBUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +000071 short iaclen; /* could even use byte */
Eric Andersenc7bda1c2004-03-15 08:29:22 +000072 struct termios termios_def;
73 struct termios termios_raw;
Eric Andersen28c70b32000-06-14 20:42:57 +000074} G;
75
76#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
77
78#ifdef USE_GLOBALVAR_PTR
79struct Globalvars * Gptr;
80#define G (*Gptr)
Eric Andersen28c70b32000-06-14 20:42:57 +000081#endif
82
Rob Landley88621d72006-08-29 19:41:06 +000083static void iacflush(void)
Eric Andersen28c70b32000-06-14 20:42:57 +000084{
85 write(G.netfd, G.iacbuf, G.iaclen);
86 G.iaclen = 0;
87}
88
89/* Function prototypes */
Eric Andersencd8c4362001-11-10 11:22:46 +000090static void rawmode(void);
91static void cookmode(void);
92static void do_linemode(void);
93static void will_charmode(void);
Eric Andersen28c70b32000-06-14 20:42:57 +000094static void telopt(byte c);
95static int subneg(byte c);
Eric Andersen28c70b32000-06-14 20:42:57 +000096
97/* Some globals */
Denis Vlasenko48237b02006-11-22 23:22:06 +000098static const int one = 1;
Erik Andersenf7c49ef2000-02-22 17:17:45 +000099
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000100#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000101static char *ttype;
102#endif
103
Eric Andersen539ffc92004-02-22 12:25:47 +0000104#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
Glenn L McGrathd4004ee2004-09-14 17:24:59 +0000105static const char *autologin;
Eric Andersen539ffc92004-02-22 12:25:47 +0000106#endif
107
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000108#ifdef CONFIG_FEATURE_AUTOWIDTH
109static int win_width, win_height;
110#endif
111
Eric Andersen28c70b32000-06-14 20:42:57 +0000112static void doexit(int ev)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000113{
Eric Andersen28c70b32000-06-14 20:42:57 +0000114 cookmode();
115 exit(ev);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000116}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000117
Eric Andersencd8c4362001-11-10 11:22:46 +0000118static void conescape(void)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000119{
Eric Andersen28c70b32000-06-14 20:42:57 +0000120 char b;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000121
Eric Andersen28c70b32000-06-14 20:42:57 +0000122 if (G.gotsig) /* came from line mode... go raw */
123 rawmode();
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000124
Eric Andersen28c70b32000-06-14 20:42:57 +0000125 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
126 " l go to line mode\r\n"
127 " c go to character mode\r\n"
128 " z suspend telnet\r\n"
129 " e exit telnet\r\n");
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000130
Eric Andersen28c70b32000-06-14 20:42:57 +0000131 if (read(0, &b, 1) <= 0)
132 doexit(1);
133
134 switch (b)
135 {
136 case 'l':
137 if (!G.gotsig)
138 {
139 do_linemode();
140 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000141 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000142 break;
143 case 'c':
144 if (G.gotsig)
145 {
146 will_charmode();
147 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000148 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000149 break;
150 case 'z':
151 cookmode();
152 kill(0, SIGTSTP);
153 rawmode();
154 break;
155 case 'e':
156 doexit(0);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000157 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000158
159 WriteCS(1, "continuing...\r\n");
160
161 if (G.gotsig)
162 cookmode();
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000163
Eric Andersen28c70b32000-06-14 20:42:57 +0000164 rrturn:
165 G.gotsig = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000166
Eric Andersen28c70b32000-06-14 20:42:57 +0000167}
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000168static void handlenetoutput(int len)
Eric Andersen28c70b32000-06-14 20:42:57 +0000169{
170 /* here we could do smart tricks how to handle 0xFF:s in output
171 * stream like writing twice every sequence of FF:s (thus doing
172 * many write()s. But I think interactive telnet application does
173 * not need to be 100% 8-bit clean, so changing every 0xff:s to
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000174 * 0x7f:s
175 *
176 * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
177 * I don't agree.
178 * first - I cannot use programs like sz/rz
179 * second - the 0x0D is sent as one character and if the next
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000180 * char is 0x0A then it's eaten by a server side.
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000181 * third - whay doy you have to make 'many write()s'?
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000182 * I don't understand.
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000183 * So I implemented it. It's realy useful for me. I hope that
184 * others people will find it interesting to.
185 */
Eric Andersen28c70b32000-06-14 20:42:57 +0000186
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000187 int i, j;
Eric Andersen0cb6f352006-01-30 22:30:41 +0000188 byte * p = (byte*)G.buf;
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000189 byte outbuf[4*DATABUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +0000190
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000191 for (i = len, j = 0; i > 0; i--, p++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000192 {
193 if (*p == 0x1d)
194 {
195 conescape();
196 return;
197 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000198 outbuf[j++] = *p;
Eric Andersen28c70b32000-06-14 20:42:57 +0000199 if (*p == 0xff)
Denis Vlasenko92758142006-10-03 19:56:34 +0000200 outbuf[j++] = 0xff;
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000201 else if (*p == 0x0d)
Denis Vlasenko92758142006-10-03 19:56:34 +0000202 outbuf[j++] = 0x00;
Eric Andersen28c70b32000-06-14 20:42:57 +0000203 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000204 if (j > 0 )
Denis Vlasenko92758142006-10-03 19:56:34 +0000205 write(G.netfd, outbuf, j);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000206}
207
Eric Andersen28c70b32000-06-14 20:42:57 +0000208
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000209static void handlenetinput(int len)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000210{
Eric Andersen28c70b32000-06-14 20:42:57 +0000211 int i;
212 int cstart = 0;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000213
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000214 for (i = 0; i < len; i++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000215 {
216 byte c = G.buf[i];
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000217
Eric Andersen28c70b32000-06-14 20:42:57 +0000218 if (G.telstate == 0) /* most of the time state == 0 */
219 {
220 if (c == IAC)
221 {
222 cstart = i;
223 G.telstate = TS_IAC;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000224 }
225 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000226 else
227 switch (G.telstate)
228 {
229 case TS_0:
230 if (c == IAC)
231 G.telstate = TS_IAC;
232 else
233 G.buf[cstart++] = c;
234 break;
235
236 case TS_IAC:
237 if (c == IAC) /* IAC IAC -> 0xFF */
238 {
239 G.buf[cstart++] = c;
240 G.telstate = TS_0;
241 break;
242 }
243 /* else */
244 switch (c)
245 {
246 case SB:
247 G.telstate = TS_SUB1;
248 break;
249 case DO:
250 case DONT:
251 case WILL:
252 case WONT:
253 G.telwish = c;
254 G.telstate = TS_OPT;
255 break;
256 default:
257 G.telstate = TS_0; /* DATA MARK must be added later */
258 }
259 break;
260 case TS_OPT: /* WILL, WONT, DO, DONT */
261 telopt(c);
262 G.telstate = TS_0;
263 break;
264 case TS_SUB1: /* Subnegotiation */
265 case TS_SUB2: /* Subnegotiation */
Matt Kraai1f0c4362001-12-20 23:13:26 +0000266 if (subneg(c))
Eric Andersen28c70b32000-06-14 20:42:57 +0000267 G.telstate = TS_0;
268 break;
269 }
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000270 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000271 if (G.telstate)
272 {
273 if (G.iaclen) iacflush();
274 if (G.telstate == TS_0) G.telstate = 0;
275
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000276 len = cstart;
Eric Andersen28c70b32000-06-14 20:42:57 +0000277 }
278
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000279 if (len)
280 write(1, G.buf, len);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000281}
282
Eric Andersen28c70b32000-06-14 20:42:57 +0000283
284/* ******************************* */
285
Rob Landley88621d72006-08-29 19:41:06 +0000286static void putiac(int c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000287{
Eric Andersen28c70b32000-06-14 20:42:57 +0000288 G.iacbuf[G.iaclen++] = c;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000289}
290
Eric Andersen28c70b32000-06-14 20:42:57 +0000291
292static void putiac2(byte wwdd, byte c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000293{
Eric Andersen28c70b32000-06-14 20:42:57 +0000294 if (G.iaclen + 3 > IACBUFSIZE)
295 iacflush();
296
297 putiac(IAC);
298 putiac(wwdd);
299 putiac(c);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000300}
301
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000302#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000303static void putiac_subopt(byte c, char *str)
304{
305 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
306
307 if (G.iaclen + len > IACBUFSIZE)
308 iacflush();
309
310 putiac(IAC);
311 putiac(SB);
312 putiac(c);
313 putiac(0);
314
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000315 while (*str)
Eric Andersen7e1273e2001-05-07 17:57:45 +0000316 putiac(*str++);
317
318 putiac(IAC);
319 putiac(SE);
320}
321#endif
322
Eric Andersen539ffc92004-02-22 12:25:47 +0000323#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
324static void putiac_subopt_autologin(void)
325{
326 int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2)
327 char *user = "USER";
328
329 if (G.iaclen + len > IACBUFSIZE)
330 iacflush();
331
332 putiac(IAC);
333 putiac(SB);
334 putiac(TELOPT_NEW_ENVIRON);
335 putiac(TELQUAL_IS);
336 putiac(NEW_ENV_VAR);
337
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000338 while (*user)
Eric Andersen539ffc92004-02-22 12:25:47 +0000339 putiac(*user++);
340
341 putiac(NEW_ENV_VALUE);
342
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000343 while (*autologin)
Eric Andersen539ffc92004-02-22 12:25:47 +0000344 putiac(*autologin++);
345
346 putiac(IAC);
347 putiac(SE);
348}
349#endif
350
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000351#ifdef CONFIG_FEATURE_AUTOWIDTH
352static void putiac_naws(byte c, int x, int y)
353{
354 if (G.iaclen + 9 > IACBUFSIZE)
355 iacflush();
356
357 putiac(IAC);
358 putiac(SB);
359 putiac(c);
360
361 putiac((x >> 8) & 0xff);
362 putiac(x & 0xff);
363 putiac((y >> 8) & 0xff);
364 putiac(y & 0xff);
365
366 putiac(IAC);
367 putiac(SE);
368}
369#endif
370
Eric Andersen28c70b32000-06-14 20:42:57 +0000371/* void putiacstring (subneg strings) */
372
373/* ******************************* */
374
Eric Andersen3e6ff902001-03-09 21:24:12 +0000375static char const escapecharis[] = "\r\nEscape character is ";
Eric Andersen28c70b32000-06-14 20:42:57 +0000376
Eric Andersencd8c4362001-11-10 11:22:46 +0000377static void setConMode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000378{
379 if (G.telflags & UF_ECHO)
380 {
381 if (G.charmode == CHM_TRY) {
382 G.charmode = CHM_ON;
Matt Kraai12f417e2001-01-18 02:57:08 +0000383 printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000384 rawmode();
385 }
386 }
387 else
388 {
389 if (G.charmode != CHM_OFF) {
390 G.charmode = CHM_OFF;
Matt Kraai12f417e2001-01-18 02:57:08 +0000391 printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000392 cookmode();
393 }
394 }
395}
396
397/* ******************************* */
398
Eric Andersencd8c4362001-11-10 11:22:46 +0000399static void will_charmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000400{
401 G.charmode = CHM_TRY;
402 G.telflags |= (UF_ECHO | UF_SGA);
403 setConMode();
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000404
Eric Andersen28c70b32000-06-14 20:42:57 +0000405 putiac2(DO, TELOPT_ECHO);
406 putiac2(DO, TELOPT_SGA);
407 iacflush();
408}
409
Eric Andersencd8c4362001-11-10 11:22:46 +0000410static void do_linemode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000411{
412 G.charmode = CHM_TRY;
413 G.telflags &= ~(UF_ECHO | UF_SGA);
414 setConMode();
415
416 putiac2(DONT, TELOPT_ECHO);
417 putiac2(DONT, TELOPT_SGA);
418 iacflush();
419}
420
421/* ******************************* */
422
Rob Landley88621d72006-08-29 19:41:06 +0000423static void to_notsup(char c)
Eric Andersen28c70b32000-06-14 20:42:57 +0000424{
425 if (G.telwish == WILL) putiac2(DONT, c);
426 else if (G.telwish == DO) putiac2(WONT, c);
427}
428
Rob Landley88621d72006-08-29 19:41:06 +0000429static void to_echo(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000430{
431 /* if server requests ECHO, don't agree */
432 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
433 else if (G.telwish == DONT) return;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000434
Eric Andersen28c70b32000-06-14 20:42:57 +0000435 if (G.telflags & UF_ECHO)
436 {
437 if (G.telwish == WILL)
438 return;
439 }
440 else
441 if (G.telwish == WONT)
442 return;
443
444 if (G.charmode != CHM_OFF)
445 G.telflags ^= UF_ECHO;
446
447 if (G.telflags & UF_ECHO)
448 putiac2(DO, TELOPT_ECHO);
449 else
450 putiac2(DONT, TELOPT_ECHO);
451
452 setConMode();
453 WriteCS(1, "\r\n"); /* sudden modec */
454}
455
Rob Landley88621d72006-08-29 19:41:06 +0000456static void to_sga(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000457{
458 /* daemon always sends will/wont, client do/dont */
459
460 if (G.telflags & UF_SGA)
461 {
462 if (G.telwish == WILL)
463 return;
464 }
465 else
466 if (G.telwish == WONT)
467 return;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000468
Eric Andersen28c70b32000-06-14 20:42:57 +0000469 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
470 putiac2(DO, TELOPT_SGA);
471 else
472 putiac2(DONT, TELOPT_SGA);
473
474 return;
475}
476
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000477#ifdef CONFIG_FEATURE_TELNET_TTYPE
Rob Landley88621d72006-08-29 19:41:06 +0000478static void to_ttype(void)
Eric Andersen7e1273e2001-05-07 17:57:45 +0000479{
480 /* Tell server we will (or won't) do TTYPE */
481
482 if(ttype)
483 putiac2(WILL, TELOPT_TTYPE);
484 else
485 putiac2(WONT, TELOPT_TTYPE);
486
487 return;
488}
489#endif
490
Eric Andersen539ffc92004-02-22 12:25:47 +0000491#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
Rob Landley88621d72006-08-29 19:41:06 +0000492static void to_new_environ(void)
Eric Andersen539ffc92004-02-22 12:25:47 +0000493{
494 /* Tell server we will (or will not) do AUTOLOGIN */
495
496 if (autologin)
497 putiac2(WILL, TELOPT_NEW_ENVIRON);
498 else
499 putiac2(WONT, TELOPT_NEW_ENVIRON);
500
501 return;
502}
503#endif
504
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000505#ifdef CONFIG_FEATURE_AUTOWIDTH
Rob Landley88621d72006-08-29 19:41:06 +0000506static void to_naws(void)
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000507{
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000508 /* Tell server we will do NAWS */
509 putiac2(WILL, TELOPT_NAWS);
510 return;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000511}
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000512#endif
513
Eric Andersen28c70b32000-06-14 20:42:57 +0000514static void telopt(byte c)
515{
516 switch (c)
517 {
Eric Andersen41634062002-04-26 08:44:17 +0000518 case TELOPT_ECHO: to_echo(); break;
519 case TELOPT_SGA: to_sga(); break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000520#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen41634062002-04-26 08:44:17 +0000521 case TELOPT_TTYPE: to_ttype();break;
Eric Andersen7e1273e2001-05-07 17:57:45 +0000522#endif
Eric Andersen539ffc92004-02-22 12:25:47 +0000523#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
524 case TELOPT_NEW_ENVIRON: to_new_environ(); break;
525#endif
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000526#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen41634062002-04-26 08:44:17 +0000527 case TELOPT_NAWS: to_naws();
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000528 putiac_naws(c, win_width, win_height);
529 break;
530#endif
Eric Andersen8db361b2002-04-26 08:00:33 +0000531 default: to_notsup(c);
532 break;
Eric Andersen28c70b32000-06-14 20:42:57 +0000533 }
534}
535
536
537/* ******************************* */
538
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000539/* subnegotiation -- ignore all (except TTYPE,NAWS) */
Eric Andersen28c70b32000-06-14 20:42:57 +0000540
541static int subneg(byte c)
542{
543 switch (G.telstate)
544 {
545 case TS_SUB1:
546 if (c == IAC)
547 G.telstate = TS_SUB2;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000548#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000549 else
550 if (c == TELOPT_TTYPE)
551 putiac_subopt(TELOPT_TTYPE,ttype);
552#endif
Eric Andersen539ffc92004-02-22 12:25:47 +0000553#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
554 else
555 if (c == TELOPT_NEW_ENVIRON)
556 putiac_subopt_autologin();
557#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000558 break;
559 case TS_SUB2:
560 if (c == SE)
561 return TRUE;
562 G.telstate = TS_SUB1;
563 /* break; */
564 }
565 return FALSE;
566}
567
568/* ******************************* */
569
570static void fgotsig(int sig)
571{
572 G.gotsig = sig;
573}
574
575
Eric Andersencd8c4362001-11-10 11:22:46 +0000576static void rawmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000577{
Paul Foxf2ddc052005-07-20 19:55:19 +0000578 if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_raw);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000579}
Eric Andersen28c70b32000-06-14 20:42:57 +0000580
Eric Andersencd8c4362001-11-10 11:22:46 +0000581static void cookmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000582{
Paul Foxf2ddc052005-07-20 19:55:19 +0000583 if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_def);
Eric Andersen28c70b32000-06-14 20:42:57 +0000584}
585
Rob Landleydfba7412006-03-06 20:47:33 +0000586int telnet_main(int argc, char** argv)
Eric Andersen28c70b32000-06-14 20:42:57 +0000587{
Denis Vlasenko9de420c2007-01-10 09:28:01 +0000588 char *host;
589 int port;
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000590 int len;
Eric Andersen28c70b32000-06-14 20:42:57 +0000591#ifdef USE_POLL
592 struct pollfd ufds[2];
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000593#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000594 fd_set readfds;
595 int maxfd;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000596#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000597
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000598#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen8efe9672003-09-15 08:33:45 +0000599 get_terminal_width_height(0, &win_width, &win_height);
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000600#endif
601
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000602#ifdef CONFIG_FEATURE_TELNET_TTYPE
Denis Vlasenko621204b2006-10-27 09:03:24 +0000603 ttype = getenv("TERM");
Eric Andersen7e1273e2001-05-07 17:57:45 +0000604#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000605
Denis Vlasenko9de420c2007-01-10 09:28:01 +0000606 /* memset(&G, 0, sizeof G); - already is */
Eric Andersen28c70b32000-06-14 20:42:57 +0000607
Paul Foxf2ddc052005-07-20 19:55:19 +0000608 if (tcgetattr(0, &G.termios_def) >= 0) {
609 G.do_termios = 1;
Paul Foxf2ddc052005-07-20 19:55:19 +0000610 G.termios_raw = G.termios_def;
611 cfmakeraw(&G.termios_raw);
612 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000613
Glenn L McGrathffccf6e2003-12-20 01:47:18 +0000614 if (argc < 2)
615 bb_show_usage();
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000616
Eric Andersen539ffc92004-02-22 12:25:47 +0000617#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000618 if (1 & getopt32(argc, argv, "al:", &autologin))
Rob Landley1b751c82005-10-28 09:24:33 +0000619 autologin = getenv("USER");
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000620 argv += optind;
Eric Andersen539ffc92004-02-22 12:25:47 +0000621#else
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000622 argv++;
Eric Andersen539ffc92004-02-22 12:25:47 +0000623#endif
Denis Vlasenko6536a9b2007-01-12 10:35:23 +0000624 if (!*argv)
625 bb_show_usage();
626 host = *argv++;
627 port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
628 if (*argv) /* extra params?? */
629 bb_show_usage();
630
Denis Vlasenko9de420c2007-01-10 09:28:01 +0000631 G.netfd = create_and_connect_stream_or_die(host, port);
Eric Andersen28c70b32000-06-14 20:42:57 +0000632
Eric Andersen0b315862002-07-03 11:51:44 +0000633 setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
Eric Andersen28c70b32000-06-14 20:42:57 +0000634
635 signal(SIGINT, fgotsig);
636
637#ifdef USE_POLL
638 ufds[0].fd = 0; ufds[1].fd = G.netfd;
639 ufds[0].events = ufds[1].events = POLLIN;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000640#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000641 FD_ZERO(&readfds);
642 FD_SET(0, &readfds);
643 FD_SET(G.netfd, &readfds);
644 maxfd = G.netfd + 1;
645#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000646
Denis Vlasenko9de420c2007-01-10 09:28:01 +0000647 while (1) {
Eric Andersen28c70b32000-06-14 20:42:57 +0000648#ifndef USE_POLL
649 fd_set rfds = readfds;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000650
Eric Andersen28c70b32000-06-14 20:42:57 +0000651 switch (select(maxfd, &rfds, NULL, NULL, NULL))
652#else
653 switch (poll(ufds, 2, -1))
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000654#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000655 {
656 case 0:
657 /* timeout */
658 case -1:
659 /* error, ignore and/or log something, bay go to loop */
660 if (G.gotsig)
661 conescape();
662 else
663 sleep(1);
664 break;
665 default:
666
667#ifdef USE_POLL
668 if (ufds[0].revents) /* well, should check POLLIN, but ... */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000669#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000670 if (FD_ISSET(0, &rfds))
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000671#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000672 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000673 len = read(0, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000674
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000675 if (len <= 0)
Eric Andersen28c70b32000-06-14 20:42:57 +0000676 doexit(0);
677
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000678 TRACE(0, ("Read con: %d\n", len));
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000679
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000680 handlenetoutput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000681 }
682
683#ifdef USE_POLL
684 if (ufds[1].revents) /* well, should check POLLIN, but ... */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000685#else
Eric Andersen28c70b32000-06-14 20:42:57 +0000686 if (FD_ISSET(G.netfd, &rfds))
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000687#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000688 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000689 len = read(G.netfd, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000690
Denis Vlasenko9de420c2007-01-10 09:28:01 +0000691 if (len <= 0) {
Eric Andersen28c70b32000-06-14 20:42:57 +0000692 WriteCS(1, "Connection closed by foreign host.\r\n");
693 doexit(1);
694 }
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000695 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
Eric Andersen28c70b32000-06-14 20:42:57 +0000696
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000697 handlenetinput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000698 }
699 }
700 }
701}