blob: 574fe8dabe9273c005f3115b4bf802c596689659 [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
57#define TRACE(x, y)
58#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;
99 /* buffer to handle telnet negotiations */
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000100 char iacbuf[IACBUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +0000101 short iaclen; /* could even use byte */
102 struct termios termios_def;
103 struct termios termios_raw;
104} G;
105
106#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
107
108#ifdef USE_GLOBALVAR_PTR
109struct Globalvars * Gptr;
110#define G (*Gptr)
Eric Andersen28c70b32000-06-14 20:42:57 +0000111#endif
112
Eric Andersencd8c4362001-11-10 11:22:46 +0000113static inline void iacflush(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000114{
115 write(G.netfd, G.iacbuf, G.iaclen);
116 G.iaclen = 0;
117}
118
119/* Function prototypes */
Eric Andersencd8c4362001-11-10 11:22:46 +0000120static void rawmode(void);
121static void cookmode(void);
122static void do_linemode(void);
123static void will_charmode(void);
Eric Andersen28c70b32000-06-14 20:42:57 +0000124static void telopt(byte c);
125static int subneg(byte c);
Eric Andersen28c70b32000-06-14 20:42:57 +0000126
127/* Some globals */
128static int one = 1;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000129
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000130#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000131static char *ttype;
132#endif
133
Eric Andersen539ffc92004-02-22 12:25:47 +0000134#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
135static char *autologin;
136#endif
137
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000138#ifdef CONFIG_FEATURE_AUTOWIDTH
139static int win_width, win_height;
140#endif
141
Eric Andersen28c70b32000-06-14 20:42:57 +0000142static void doexit(int ev)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000143{
Eric Andersen28c70b32000-06-14 20:42:57 +0000144 cookmode();
145 exit(ev);
146}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000147
Eric Andersencd8c4362001-11-10 11:22:46 +0000148static void conescape(void)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000149{
Eric Andersen28c70b32000-06-14 20:42:57 +0000150 char b;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000151
Eric Andersen28c70b32000-06-14 20:42:57 +0000152 if (G.gotsig) /* came from line mode... go raw */
153 rawmode();
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000154
Eric Andersen28c70b32000-06-14 20:42:57 +0000155 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
156 " l go to line mode\r\n"
157 " c go to character mode\r\n"
158 " z suspend telnet\r\n"
159 " e exit telnet\r\n");
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000160
Eric Andersen28c70b32000-06-14 20:42:57 +0000161 if (read(0, &b, 1) <= 0)
162 doexit(1);
163
164 switch (b)
165 {
166 case 'l':
167 if (!G.gotsig)
168 {
169 do_linemode();
170 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000171 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000172 break;
173 case 'c':
174 if (G.gotsig)
175 {
176 will_charmode();
177 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000178 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000179 break;
180 case 'z':
181 cookmode();
182 kill(0, SIGTSTP);
183 rawmode();
184 break;
185 case 'e':
186 doexit(0);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000187 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000188
189 WriteCS(1, "continuing...\r\n");
190
191 if (G.gotsig)
192 cookmode();
193
194 rrturn:
195 G.gotsig = 0;
196
197}
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000198static void handlenetoutput(int len)
Eric Andersen28c70b32000-06-14 20:42:57 +0000199{
200 /* here we could do smart tricks how to handle 0xFF:s in output
201 * stream like writing twice every sequence of FF:s (thus doing
202 * many write()s. But I think interactive telnet application does
203 * not need to be 100% 8-bit clean, so changing every 0xff:s to
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000204 * 0x7f:s
205 *
206 * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
207 * I don't agree.
208 * first - I cannot use programs like sz/rz
209 * second - the 0x0D is sent as one character and if the next
210 * char is 0x0A then it's eaten by a server side.
211 * third - whay doy you have to make 'many write()s'?
212 * I don't understand.
213 * So I implemented it. It's realy useful for me. I hope that
214 * others people will find it interesting to.
215 */
Eric Andersen28c70b32000-06-14 20:42:57 +0000216
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000217 int i, j;
Eric Andersen28c70b32000-06-14 20:42:57 +0000218 byte * p = G.buf;
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000219 byte outbuf[4*DATABUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +0000220
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000221 for (i = len, j = 0; i > 0; i--, p++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000222 {
223 if (*p == 0x1d)
224 {
225 conescape();
226 return;
227 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000228 outbuf[j++] = *p;
Eric Andersen28c70b32000-06-14 20:42:57 +0000229 if (*p == 0xff)
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000230 outbuf[j++] = 0xff;
231 else if (*p == 0x0d)
232 outbuf[j++] = 0x00;
Eric Andersen28c70b32000-06-14 20:42:57 +0000233 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000234 if (j > 0 )
235 write(G.netfd, outbuf, j);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000236}
237
Eric Andersen28c70b32000-06-14 20:42:57 +0000238
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000239static void handlenetinput(int len)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000240{
Eric Andersen28c70b32000-06-14 20:42:57 +0000241 int i;
242 int cstart = 0;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000243
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000244 for (i = 0; i < len; i++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000245 {
246 byte c = G.buf[i];
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000247
Eric Andersen28c70b32000-06-14 20:42:57 +0000248 if (G.telstate == 0) /* most of the time state == 0 */
249 {
250 if (c == IAC)
251 {
252 cstart = i;
253 G.telstate = TS_IAC;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000254 }
255 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000256 else
257 switch (G.telstate)
258 {
259 case TS_0:
260 if (c == IAC)
261 G.telstate = TS_IAC;
262 else
263 G.buf[cstart++] = c;
264 break;
265
266 case TS_IAC:
267 if (c == IAC) /* IAC IAC -> 0xFF */
268 {
269 G.buf[cstart++] = c;
270 G.telstate = TS_0;
271 break;
272 }
273 /* else */
274 switch (c)
275 {
276 case SB:
277 G.telstate = TS_SUB1;
278 break;
279 case DO:
280 case DONT:
281 case WILL:
282 case WONT:
283 G.telwish = c;
284 G.telstate = TS_OPT;
285 break;
286 default:
287 G.telstate = TS_0; /* DATA MARK must be added later */
288 }
289 break;
290 case TS_OPT: /* WILL, WONT, DO, DONT */
291 telopt(c);
292 G.telstate = TS_0;
293 break;
294 case TS_SUB1: /* Subnegotiation */
295 case TS_SUB2: /* Subnegotiation */
Matt Kraai1f0c4362001-12-20 23:13:26 +0000296 if (subneg(c))
Eric Andersen28c70b32000-06-14 20:42:57 +0000297 G.telstate = TS_0;
298 break;
299 }
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000300 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000301 if (G.telstate)
302 {
303 if (G.iaclen) iacflush();
304 if (G.telstate == TS_0) G.telstate = 0;
305
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000306 len = cstart;
Eric Andersen28c70b32000-06-14 20:42:57 +0000307 }
308
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000309 if (len)
310 write(1, G.buf, len);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000311}
312
Eric Andersen28c70b32000-06-14 20:42:57 +0000313
314/* ******************************* */
315
316static inline void putiac(int c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000317{
Eric Andersen28c70b32000-06-14 20:42:57 +0000318 G.iacbuf[G.iaclen++] = c;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000319}
320
Eric Andersen28c70b32000-06-14 20:42:57 +0000321
322static void putiac2(byte wwdd, byte c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000323{
Eric Andersen28c70b32000-06-14 20:42:57 +0000324 if (G.iaclen + 3 > IACBUFSIZE)
325 iacflush();
326
327 putiac(IAC);
328 putiac(wwdd);
329 putiac(c);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000330}
331
Eric Andersen28c70b32000-06-14 20:42:57 +0000332#if 0
333static void putiac1(byte c)
334{
335 if (G.iaclen + 2 > IACBUFSIZE)
336 iacflush();
337
338 putiac(IAC);
339 putiac(c);
340}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000341#endif
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000342
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000343#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000344static void putiac_subopt(byte c, char *str)
345{
346 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
347
348 if (G.iaclen + len > IACBUFSIZE)
349 iacflush();
350
351 putiac(IAC);
352 putiac(SB);
353 putiac(c);
354 putiac(0);
355
356 while(*str)
357 putiac(*str++);
358
359 putiac(IAC);
360 putiac(SE);
361}
362#endif
363
Eric Andersen539ffc92004-02-22 12:25:47 +0000364#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
365static void putiac_subopt_autologin(void)
366{
367 int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2)
368 char *user = "USER";
369
370 if (G.iaclen + len > IACBUFSIZE)
371 iacflush();
372
373 putiac(IAC);
374 putiac(SB);
375 putiac(TELOPT_NEW_ENVIRON);
376 putiac(TELQUAL_IS);
377 putiac(NEW_ENV_VAR);
378
379 while(*user)
380 putiac(*user++);
381
382 putiac(NEW_ENV_VALUE);
383
384 while(*autologin)
385 putiac(*autologin++);
386
387 putiac(IAC);
388 putiac(SE);
389}
390#endif
391
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000392#ifdef CONFIG_FEATURE_AUTOWIDTH
393static void putiac_naws(byte c, int x, int y)
394{
395 if (G.iaclen + 9 > IACBUFSIZE)
396 iacflush();
397
398 putiac(IAC);
399 putiac(SB);
400 putiac(c);
401
402 putiac((x >> 8) & 0xff);
403 putiac(x & 0xff);
404 putiac((y >> 8) & 0xff);
405 putiac(y & 0xff);
406
407 putiac(IAC);
408 putiac(SE);
409}
410#endif
411
Eric Andersen28c70b32000-06-14 20:42:57 +0000412/* void putiacstring (subneg strings) */
413
414/* ******************************* */
415
Eric Andersen3e6ff902001-03-09 21:24:12 +0000416static char const escapecharis[] = "\r\nEscape character is ";
Eric Andersen28c70b32000-06-14 20:42:57 +0000417
Eric Andersencd8c4362001-11-10 11:22:46 +0000418static void setConMode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000419{
420 if (G.telflags & UF_ECHO)
421 {
422 if (G.charmode == CHM_TRY) {
423 G.charmode = CHM_ON;
Matt Kraai12f417e2001-01-18 02:57:08 +0000424 printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000425 rawmode();
426 }
427 }
428 else
429 {
430 if (G.charmode != CHM_OFF) {
431 G.charmode = CHM_OFF;
Matt Kraai12f417e2001-01-18 02:57:08 +0000432 printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000433 cookmode();
434 }
435 }
436}
437
438/* ******************************* */
439
Eric Andersencd8c4362001-11-10 11:22:46 +0000440static void will_charmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000441{
442 G.charmode = CHM_TRY;
443 G.telflags |= (UF_ECHO | UF_SGA);
444 setConMode();
445
446 putiac2(DO, TELOPT_ECHO);
447 putiac2(DO, TELOPT_SGA);
448 iacflush();
449}
450
Eric Andersencd8c4362001-11-10 11:22:46 +0000451static void do_linemode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000452{
453 G.charmode = CHM_TRY;
454 G.telflags &= ~(UF_ECHO | UF_SGA);
455 setConMode();
456
457 putiac2(DONT, TELOPT_ECHO);
458 putiac2(DONT, TELOPT_SGA);
459 iacflush();
460}
461
462/* ******************************* */
463
464static inline void to_notsup(char c)
465{
466 if (G.telwish == WILL) putiac2(DONT, c);
467 else if (G.telwish == DO) putiac2(WONT, c);
468}
469
Eric Andersencd8c4362001-11-10 11:22:46 +0000470static inline void to_echo(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000471{
472 /* if server requests ECHO, don't agree */
473 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
474 else if (G.telwish == DONT) return;
475
476 if (G.telflags & UF_ECHO)
477 {
478 if (G.telwish == WILL)
479 return;
480 }
481 else
482 if (G.telwish == WONT)
483 return;
484
485 if (G.charmode != CHM_OFF)
486 G.telflags ^= UF_ECHO;
487
488 if (G.telflags & UF_ECHO)
489 putiac2(DO, TELOPT_ECHO);
490 else
491 putiac2(DONT, TELOPT_ECHO);
492
493 setConMode();
494 WriteCS(1, "\r\n"); /* sudden modec */
495}
496
Eric Andersencd8c4362001-11-10 11:22:46 +0000497static inline void to_sga(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000498{
499 /* daemon always sends will/wont, client do/dont */
500
501 if (G.telflags & UF_SGA)
502 {
503 if (G.telwish == WILL)
504 return;
505 }
506 else
507 if (G.telwish == WONT)
508 return;
509
510 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
511 putiac2(DO, TELOPT_SGA);
512 else
513 putiac2(DONT, TELOPT_SGA);
514
515 return;
516}
517
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000518#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersencd8c4362001-11-10 11:22:46 +0000519static inline void to_ttype(void)
Eric Andersen7e1273e2001-05-07 17:57:45 +0000520{
521 /* Tell server we will (or won't) do TTYPE */
522
523 if(ttype)
524 putiac2(WILL, TELOPT_TTYPE);
525 else
526 putiac2(WONT, TELOPT_TTYPE);
527
528 return;
529}
530#endif
531
Eric Andersen539ffc92004-02-22 12:25:47 +0000532#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
533static inline void to_new_environ(void)
534{
535 /* Tell server we will (or will not) do AUTOLOGIN */
536
537 if (autologin)
538 putiac2(WILL, TELOPT_NEW_ENVIRON);
539 else
540 putiac2(WONT, TELOPT_NEW_ENVIRON);
541
542 return;
543}
544#endif
545
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000546#ifdef CONFIG_FEATURE_AUTOWIDTH
Tim Rikered8e0362002-04-26 07:53:39 +0000547static inline void to_naws(void)
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000548{
549 /* Tell server we will do NAWS */
550 putiac2(WILL, TELOPT_NAWS);
551 return;
552}
553#endif
554
Eric Andersen28c70b32000-06-14 20:42:57 +0000555static void telopt(byte c)
556{
557 switch (c)
558 {
Eric Andersen41634062002-04-26 08:44:17 +0000559 case TELOPT_ECHO: to_echo(); break;
560 case TELOPT_SGA: to_sga(); break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000561#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen41634062002-04-26 08:44:17 +0000562 case TELOPT_TTYPE: to_ttype();break;
Eric Andersen7e1273e2001-05-07 17:57:45 +0000563#endif
Eric Andersen539ffc92004-02-22 12:25:47 +0000564#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
565 case TELOPT_NEW_ENVIRON: to_new_environ(); break;
566#endif
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000567#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen41634062002-04-26 08:44:17 +0000568 case TELOPT_NAWS: to_naws();
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000569 putiac_naws(c, win_width, win_height);
570 break;
571#endif
Eric Andersen8db361b2002-04-26 08:00:33 +0000572 default: to_notsup(c);
573 break;
Eric Andersen28c70b32000-06-14 20:42:57 +0000574 }
575}
576
577
578/* ******************************* */
579
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000580/* subnegotiation -- ignore all (except TTYPE,NAWS) */
Eric Andersen28c70b32000-06-14 20:42:57 +0000581
582static int subneg(byte c)
583{
584 switch (G.telstate)
585 {
586 case TS_SUB1:
587 if (c == IAC)
588 G.telstate = TS_SUB2;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000589#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000590 else
591 if (c == TELOPT_TTYPE)
592 putiac_subopt(TELOPT_TTYPE,ttype);
593#endif
Eric Andersen539ffc92004-02-22 12:25:47 +0000594#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
595 else
596 if (c == TELOPT_NEW_ENVIRON)
597 putiac_subopt_autologin();
598#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000599 break;
600 case TS_SUB2:
601 if (c == SE)
602 return TRUE;
603 G.telstate = TS_SUB1;
604 /* break; */
605 }
606 return FALSE;
607}
608
609/* ******************************* */
610
611static void fgotsig(int sig)
612{
613 G.gotsig = sig;
614}
615
616
Eric Andersencd8c4362001-11-10 11:22:46 +0000617static void rawmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000618{
619 tcsetattr(0, TCSADRAIN, &G.termios_raw);
620}
621
Eric Andersencd8c4362001-11-10 11:22:46 +0000622static void cookmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000623{
624 tcsetattr(0, TCSADRAIN, &G.termios_def);
625}
626
627extern int telnet_main(int argc, char** argv)
628{
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000629 int len;
Eric Andersene6dc4392003-10-31 09:31:46 +0000630 struct sockaddr_in s_in;
Eric Andersen28c70b32000-06-14 20:42:57 +0000631#ifdef USE_POLL
632 struct pollfd ufds[2];
633#else
634 fd_set readfds;
635 int maxfd;
636#endif
637
Eric Andersen539ffc92004-02-22 12:25:47 +0000638#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
639 int opt;
640#endif
641
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000642#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen8efe9672003-09-15 08:33:45 +0000643 get_terminal_width_height(0, &win_width, &win_height);
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000644#endif
645
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000646#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000647 ttype = getenv("TERM");
648#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000649
650 memset(&G, 0, sizeof G);
651
652 if (tcgetattr(0, &G.termios_def) < 0)
653 exit(1);
654
655 G.termios_raw = G.termios_def;
Eric Andersen28c70b32000-06-14 20:42:57 +0000656 cfmakeraw(&G.termios_raw);
657
Glenn L McGrathffccf6e2003-12-20 01:47:18 +0000658 if (argc < 2)
659 bb_show_usage();
Eric Andersen28c70b32000-06-14 20:42:57 +0000660
Eric Andersen539ffc92004-02-22 12:25:47 +0000661#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN
662 autologin = NULL;
663 while ((opt = getopt(argc, argv, "al:")) != EOF) {
664 switch (opt) {
665 case 'l':
666 autologin = bb_xstrdup(optarg);
667 break;
668 case 'a':
669 autologin = getenv("USER");
670 break;
671 case '?':
672 bb_show_usage();
673 break;
674 }
675 }
676 if (optind < argc) {
677 bb_lookup_host(&s_in, argv[optind++]);
678 s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] :
679 "telnet", "tcp", 23);
680 if (optind < argc)
681 bb_show_usage();
682 } else
683 bb_show_usage();
684#else
Glenn L McGrathffccf6e2003-12-20 01:47:18 +0000685 bb_lookup_host(&s_in, argv[1]);
Glenn L McGrath036dbaa2004-01-17 05:03:31 +0000686 s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23);
Eric Andersen539ffc92004-02-22 12:25:47 +0000687#endif
Eric Andersen0b315862002-07-03 11:51:44 +0000688
Eric Andersene6dc4392003-10-31 09:31:46 +0000689 G.netfd = xconnect(&s_in);
Eric Andersen28c70b32000-06-14 20:42:57 +0000690
Eric Andersen0b315862002-07-03 11:51:44 +0000691 setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
Eric Andersen28c70b32000-06-14 20:42:57 +0000692
693 signal(SIGINT, fgotsig);
694
695#ifdef USE_POLL
696 ufds[0].fd = 0; ufds[1].fd = G.netfd;
697 ufds[0].events = ufds[1].events = POLLIN;
698#else
699 FD_ZERO(&readfds);
700 FD_SET(0, &readfds);
701 FD_SET(G.netfd, &readfds);
702 maxfd = G.netfd + 1;
703#endif
704
705 while (1)
706 {
707#ifndef USE_POLL
708 fd_set rfds = readfds;
709
710 switch (select(maxfd, &rfds, NULL, NULL, NULL))
711#else
712 switch (poll(ufds, 2, -1))
713#endif
714 {
715 case 0:
716 /* timeout */
717 case -1:
718 /* error, ignore and/or log something, bay go to loop */
719 if (G.gotsig)
720 conescape();
721 else
722 sleep(1);
723 break;
724 default:
725
726#ifdef USE_POLL
727 if (ufds[0].revents) /* well, should check POLLIN, but ... */
728#else
729 if (FD_ISSET(0, &rfds))
730#endif
731 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000732 len = read(0, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000733
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000734 if (len <= 0)
Eric Andersen28c70b32000-06-14 20:42:57 +0000735 doexit(0);
736
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000737 TRACE(0, ("Read con: %d\n", len));
Eric Andersen28c70b32000-06-14 20:42:57 +0000738
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000739 handlenetoutput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000740 }
741
742#ifdef USE_POLL
743 if (ufds[1].revents) /* well, should check POLLIN, but ... */
744#else
745 if (FD_ISSET(G.netfd, &rfds))
746#endif
747 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000748 len = read(G.netfd, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000749
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000750 if (len <= 0)
Eric Andersen28c70b32000-06-14 20:42:57 +0000751 {
752 WriteCS(1, "Connection closed by foreign host.\r\n");
753 doexit(1);
754 }
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000755 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
Eric Andersen28c70b32000-06-14 20:42:57 +0000756
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000757 handlenetinput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000758 }
759 }
760 }
761}
762
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000763/*
Eric Andersen28c70b32000-06-14 20:42:57 +0000764Local Variables:
765c-file-style: "linux"
766c-basic-offset: 4
767tab-width: 4
768End:
769*/