blob: 8f9ce7e944e6813e4ac678adbce222169b92b9ef [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
28 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen
29 * <andersen@lineo.com>
Eric Andersen7e1273e2001-05-07 17:57:45 +000030 * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
31 * <jam@ltsp.org>
Erik Andersenf7c49ef2000-02-22 17:17:45 +000032 *
Erik Andersenf7c49ef2000-02-22 17:17:45 +000033 */
34
Erik Andersenf7c49ef2000-02-22 17:17:45 +000035#include <termios.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000036#include <unistd.h>
37#include <errno.h>
38#include <stdlib.h>
39#include <stdarg.h>
40#include <string.h>
41#include <signal.h>
42#include <arpa/telnet.h>
Erik Andersenf7c49ef2000-02-22 17:17:45 +000043#include <sys/types.h>
44#include <sys/socket.h>
Eric Andersen28c70b32000-06-14 20:42:57 +000045#include <netinet/in.h>
46#include <netdb.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000047#include "busybox.h"
Erik Andersenf7c49ef2000-02-22 17:17:45 +000048
Eric Andersen0e28e1f2002-04-26 07:20:47 +000049#ifdef CONFIG_FEATURE_AUTOWIDTH
50# include <sys/ioctl.h>
51#endif
52
Eric Andersen28c70b32000-06-14 20:42:57 +000053#if 0
Mark Whitley59ab0252001-01-23 22:30:04 +000054static const int DOTRACE = 1;
Eric Andersen28c70b32000-06-14 20:42:57 +000055#endif
56
Pavel Roskin616d13b2000-07-28 19:38:27 +000057#ifdef DOTRACE
Eric Andersen28c70b32000-06-14 20:42:57 +000058#include <arpa/inet.h> /* for inet_ntoa()... */
59#define TRACE(x, y) do { if (x) printf y; } while (0)
60#else
61#define TRACE(x, y)
62#endif
63
64#if 0
65#define USE_POLL
66#include <sys/poll.h>
67#else
68#include <sys/time.h>
69#endif
70
Glenn L McGrath78b0e372001-06-26 02:06:08 +000071#define DATABUFSIZE 128
72#define IACBUFSIZE 128
Eric Andersen28c70b32000-06-14 20:42:57 +000073
Mark Whitley59ab0252001-01-23 22:30:04 +000074static const int CHM_TRY = 0;
75static const int CHM_ON = 1;
76static const int CHM_OFF = 2;
Eric Andersen28c70b32000-06-14 20:42:57 +000077
Mark Whitley59ab0252001-01-23 22:30:04 +000078static const int UF_ECHO = 0x01;
79static const int UF_SGA = 0x02;
Eric Andersen28c70b32000-06-14 20:42:57 +000080
Mark Whitley59ab0252001-01-23 22:30:04 +000081enum {
82 TS_0 = 1,
83 TS_IAC = 2,
84 TS_OPT = 3,
85 TS_SUB1 = 4,
86 TS_SUB2 = 5,
87};
Eric Andersen28c70b32000-06-14 20:42:57 +000088
89#define WriteCS(fd, str) write(fd, str, sizeof str -1)
90
91typedef unsigned char byte;
92
93/* use globals to reduce size ??? */ /* test this hypothesis later */
Eric Andersen92d23242001-03-19 23:49:41 +000094static struct Globalvars {
Eric Andersen28c70b32000-06-14 20:42:57 +000095 int netfd; /* console fd:s are 0 and 1 (and 2) */
96 /* same buffer used both for network and console read/write */
Glenn L McGrath78b0e372001-06-26 02:06:08 +000097 char buf[DATABUFSIZE]; /* allocating so static size is smaller */
Eric Andersen28c70b32000-06-14 20:42:57 +000098 byte telstate; /* telnet negotiation state from network input */
99 byte telwish; /* DO, DONT, WILL, WONT */
100 byte charmode;
101 byte telflags;
102 byte gotsig;
103 /* buffer to handle telnet negotiations */
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000104 char iacbuf[IACBUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +0000105 short iaclen; /* could even use byte */
106 struct termios termios_def;
107 struct termios termios_raw;
108} G;
109
110#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
111
112#ifdef USE_GLOBALVAR_PTR
113struct Globalvars * Gptr;
114#define G (*Gptr)
115#else
Eric Andersen92d23242001-03-19 23:49:41 +0000116static struct Globalvars G;
Eric Andersen28c70b32000-06-14 20:42:57 +0000117#endif
118
Eric Andersencd8c4362001-11-10 11:22:46 +0000119static inline void iacflush(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000120{
121 write(G.netfd, G.iacbuf, G.iaclen);
122 G.iaclen = 0;
123}
124
125/* Function prototypes */
126static int getport(char * p);
127static struct in_addr getserver(char * p);
Eric Andersen28c70b32000-06-14 20:42:57 +0000128static void setup_sockaddr_in(struct sockaddr_in * addr, int port);
129static int remote_connect(struct in_addr addr, int port);
Eric Andersencd8c4362001-11-10 11:22:46 +0000130static void rawmode(void);
131static void cookmode(void);
132static void do_linemode(void);
133static void will_charmode(void);
Eric Andersen28c70b32000-06-14 20:42:57 +0000134static void telopt(byte c);
135static int subneg(byte c);
136#if 0
137static int local_bind(int port);
138#endif
139
140/* Some globals */
141static int one = 1;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000142
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000143#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000144static char *ttype;
145#endif
146
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000147#ifdef CONFIG_FEATURE_AUTOWIDTH
148static int win_width, win_height;
149#endif
150
Eric Andersen28c70b32000-06-14 20:42:57 +0000151static void doexit(int ev)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000152{
Eric Andersen28c70b32000-06-14 20:42:57 +0000153 cookmode();
154 exit(ev);
155}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000156
Eric Andersencd8c4362001-11-10 11:22:46 +0000157static void conescape(void)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000158{
Eric Andersen28c70b32000-06-14 20:42:57 +0000159 char b;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000160
Eric Andersen28c70b32000-06-14 20:42:57 +0000161 if (G.gotsig) /* came from line mode... go raw */
162 rawmode();
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000163
Eric Andersen28c70b32000-06-14 20:42:57 +0000164 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
165 " l go to line mode\r\n"
166 " c go to character mode\r\n"
167 " z suspend telnet\r\n"
168 " e exit telnet\r\n");
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000169
Eric Andersen28c70b32000-06-14 20:42:57 +0000170 if (read(0, &b, 1) <= 0)
171 doexit(1);
172
173 switch (b)
174 {
175 case 'l':
176 if (!G.gotsig)
177 {
178 do_linemode();
179 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000180 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000181 break;
182 case 'c':
183 if (G.gotsig)
184 {
185 will_charmode();
186 goto rrturn;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000187 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000188 break;
189 case 'z':
190 cookmode();
191 kill(0, SIGTSTP);
192 rawmode();
193 break;
194 case 'e':
195 doexit(0);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000196 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000197
198 WriteCS(1, "continuing...\r\n");
199
200 if (G.gotsig)
201 cookmode();
202
203 rrturn:
204 G.gotsig = 0;
205
206}
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000207static void handlenetoutput(int len)
Eric Andersen28c70b32000-06-14 20:42:57 +0000208{
209 /* here we could do smart tricks how to handle 0xFF:s in output
210 * stream like writing twice every sequence of FF:s (thus doing
211 * many write()s. But I think interactive telnet application does
212 * not need to be 100% 8-bit clean, so changing every 0xff:s to
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000213 * 0x7f:s
214 *
215 * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
216 * I don't agree.
217 * first - I cannot use programs like sz/rz
218 * second - the 0x0D is sent as one character and if the next
219 * char is 0x0A then it's eaten by a server side.
220 * third - whay doy you have to make 'many write()s'?
221 * I don't understand.
222 * So I implemented it. It's realy useful for me. I hope that
223 * others people will find it interesting to.
224 */
Eric Andersen28c70b32000-06-14 20:42:57 +0000225
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000226 int i, j;
Eric Andersen28c70b32000-06-14 20:42:57 +0000227 byte * p = G.buf;
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000228 byte outbuf[4*DATABUFSIZE];
Eric Andersen28c70b32000-06-14 20:42:57 +0000229
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000230 for (i = len, j = 0; i > 0; i--, p++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000231 {
232 if (*p == 0x1d)
233 {
234 conescape();
235 return;
236 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000237 outbuf[j++] = *p;
Eric Andersen28c70b32000-06-14 20:42:57 +0000238 if (*p == 0xff)
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000239 outbuf[j++] = 0xff;
240 else if (*p == 0x0d)
241 outbuf[j++] = 0x00;
Eric Andersen28c70b32000-06-14 20:42:57 +0000242 }
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000243 if (j > 0 )
244 write(G.netfd, outbuf, j);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000245}
246
Eric Andersen28c70b32000-06-14 20:42:57 +0000247
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000248static void handlenetinput(int len)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000249{
Eric Andersen28c70b32000-06-14 20:42:57 +0000250 int i;
251 int cstart = 0;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000252
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000253 for (i = 0; i < len; i++)
Eric Andersen28c70b32000-06-14 20:42:57 +0000254 {
255 byte c = G.buf[i];
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000256
Eric Andersen28c70b32000-06-14 20:42:57 +0000257 if (G.telstate == 0) /* most of the time state == 0 */
258 {
259 if (c == IAC)
260 {
261 cstart = i;
262 G.telstate = TS_IAC;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000263 }
264 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000265 else
266 switch (G.telstate)
267 {
268 case TS_0:
269 if (c == IAC)
270 G.telstate = TS_IAC;
271 else
272 G.buf[cstart++] = c;
273 break;
274
275 case TS_IAC:
276 if (c == IAC) /* IAC IAC -> 0xFF */
277 {
278 G.buf[cstart++] = c;
279 G.telstate = TS_0;
280 break;
281 }
282 /* else */
283 switch (c)
284 {
285 case SB:
286 G.telstate = TS_SUB1;
287 break;
288 case DO:
289 case DONT:
290 case WILL:
291 case WONT:
292 G.telwish = c;
293 G.telstate = TS_OPT;
294 break;
295 default:
296 G.telstate = TS_0; /* DATA MARK must be added later */
297 }
298 break;
299 case TS_OPT: /* WILL, WONT, DO, DONT */
300 telopt(c);
301 G.telstate = TS_0;
302 break;
303 case TS_SUB1: /* Subnegotiation */
304 case TS_SUB2: /* Subnegotiation */
Matt Kraai1f0c4362001-12-20 23:13:26 +0000305 if (subneg(c))
Eric Andersen28c70b32000-06-14 20:42:57 +0000306 G.telstate = TS_0;
307 break;
308 }
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000309 }
Eric Andersen28c70b32000-06-14 20:42:57 +0000310 if (G.telstate)
311 {
312 if (G.iaclen) iacflush();
313 if (G.telstate == TS_0) G.telstate = 0;
314
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000315 len = cstart;
Eric Andersen28c70b32000-06-14 20:42:57 +0000316 }
317
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000318 if (len)
319 write(1, G.buf, len);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000320}
321
Eric Andersen28c70b32000-06-14 20:42:57 +0000322
323/* ******************************* */
324
325static inline void putiac(int c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000326{
Eric Andersen28c70b32000-06-14 20:42:57 +0000327 G.iacbuf[G.iaclen++] = c;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000328}
329
Eric Andersen28c70b32000-06-14 20:42:57 +0000330
331static void putiac2(byte wwdd, byte c)
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000332{
Eric Andersen28c70b32000-06-14 20:42:57 +0000333 if (G.iaclen + 3 > IACBUFSIZE)
334 iacflush();
335
336 putiac(IAC);
337 putiac(wwdd);
338 putiac(c);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000339}
340
Eric Andersen28c70b32000-06-14 20:42:57 +0000341#if 0
342static void putiac1(byte c)
343{
344 if (G.iaclen + 2 > IACBUFSIZE)
345 iacflush();
346
347 putiac(IAC);
348 putiac(c);
349}
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000350#endif
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000351
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000352#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000353static void putiac_subopt(byte c, char *str)
354{
355 int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
356
357 if (G.iaclen + len > IACBUFSIZE)
358 iacflush();
359
360 putiac(IAC);
361 putiac(SB);
362 putiac(c);
363 putiac(0);
364
365 while(*str)
366 putiac(*str++);
367
368 putiac(IAC);
369 putiac(SE);
370}
371#endif
372
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000373#ifdef CONFIG_FEATURE_AUTOWIDTH
374static void putiac_naws(byte c, int x, int y)
375{
376 if (G.iaclen + 9 > IACBUFSIZE)
377 iacflush();
378
379 putiac(IAC);
380 putiac(SB);
381 putiac(c);
382
383 putiac((x >> 8) & 0xff);
384 putiac(x & 0xff);
385 putiac((y >> 8) & 0xff);
386 putiac(y & 0xff);
387
388 putiac(IAC);
389 putiac(SE);
390}
391#endif
392
Eric Andersen28c70b32000-06-14 20:42:57 +0000393/* void putiacstring (subneg strings) */
394
395/* ******************************* */
396
Eric Andersen3e6ff902001-03-09 21:24:12 +0000397static char const escapecharis[] = "\r\nEscape character is ";
Eric Andersen28c70b32000-06-14 20:42:57 +0000398
Eric Andersencd8c4362001-11-10 11:22:46 +0000399static void setConMode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000400{
401 if (G.telflags & UF_ECHO)
402 {
403 if (G.charmode == CHM_TRY) {
404 G.charmode = CHM_ON;
Matt Kraai12f417e2001-01-18 02:57:08 +0000405 printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000406 rawmode();
407 }
408 }
409 else
410 {
411 if (G.charmode != CHM_OFF) {
412 G.charmode = CHM_OFF;
Matt Kraai12f417e2001-01-18 02:57:08 +0000413 printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
Eric Andersen28c70b32000-06-14 20:42:57 +0000414 cookmode();
415 }
416 }
417}
418
419/* ******************************* */
420
Eric Andersencd8c4362001-11-10 11:22:46 +0000421static void will_charmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000422{
423 G.charmode = CHM_TRY;
424 G.telflags |= (UF_ECHO | UF_SGA);
425 setConMode();
426
427 putiac2(DO, TELOPT_ECHO);
428 putiac2(DO, TELOPT_SGA);
429 iacflush();
430}
431
Eric Andersencd8c4362001-11-10 11:22:46 +0000432static void do_linemode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000433{
434 G.charmode = CHM_TRY;
435 G.telflags &= ~(UF_ECHO | UF_SGA);
436 setConMode();
437
438 putiac2(DONT, TELOPT_ECHO);
439 putiac2(DONT, TELOPT_SGA);
440 iacflush();
441}
442
443/* ******************************* */
444
445static inline void to_notsup(char c)
446{
447 if (G.telwish == WILL) putiac2(DONT, c);
448 else if (G.telwish == DO) putiac2(WONT, c);
449}
450
Eric Andersencd8c4362001-11-10 11:22:46 +0000451static inline void to_echo(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000452{
453 /* if server requests ECHO, don't agree */
454 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
455 else if (G.telwish == DONT) return;
456
457 if (G.telflags & UF_ECHO)
458 {
459 if (G.telwish == WILL)
460 return;
461 }
462 else
463 if (G.telwish == WONT)
464 return;
465
466 if (G.charmode != CHM_OFF)
467 G.telflags ^= UF_ECHO;
468
469 if (G.telflags & UF_ECHO)
470 putiac2(DO, TELOPT_ECHO);
471 else
472 putiac2(DONT, TELOPT_ECHO);
473
474 setConMode();
475 WriteCS(1, "\r\n"); /* sudden modec */
476}
477
Eric Andersencd8c4362001-11-10 11:22:46 +0000478static inline void to_sga(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000479{
480 /* daemon always sends will/wont, client do/dont */
481
482 if (G.telflags & UF_SGA)
483 {
484 if (G.telwish == WILL)
485 return;
486 }
487 else
488 if (G.telwish == WONT)
489 return;
490
491 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
492 putiac2(DO, TELOPT_SGA);
493 else
494 putiac2(DONT, TELOPT_SGA);
495
496 return;
497}
498
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000499#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersencd8c4362001-11-10 11:22:46 +0000500static inline void to_ttype(void)
Eric Andersen7e1273e2001-05-07 17:57:45 +0000501{
502 /* Tell server we will (or won't) do TTYPE */
503
504 if(ttype)
505 putiac2(WILL, TELOPT_TTYPE);
506 else
507 putiac2(WONT, TELOPT_TTYPE);
508
509 return;
510}
511#endif
512
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000513#ifdef CONFIG_FEATURE_AUTOWIDTH
Tim Rikered8e0362002-04-26 07:53:39 +0000514static inline void to_naws(void)
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000515{
516 /* Tell server we will do NAWS */
517 putiac2(WILL, TELOPT_NAWS);
518 return;
519}
520#endif
521
Eric Andersen28c70b32000-06-14 20:42:57 +0000522static void telopt(byte c)
523{
524 switch (c)
525 {
Eric Andersen41634062002-04-26 08:44:17 +0000526 case TELOPT_ECHO: to_echo(); break;
527 case TELOPT_SGA: to_sga(); break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000528#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen41634062002-04-26 08:44:17 +0000529 case TELOPT_TTYPE: to_ttype();break;
Eric Andersen7e1273e2001-05-07 17:57:45 +0000530#endif
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000531#ifdef CONFIG_FEATURE_AUTOWIDTH
Eric Andersen41634062002-04-26 08:44:17 +0000532 case TELOPT_NAWS: to_naws();
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000533 putiac_naws(c, win_width, win_height);
534 break;
535#endif
Eric Andersen8db361b2002-04-26 08:00:33 +0000536 default: to_notsup(c);
537 break;
Eric Andersen28c70b32000-06-14 20:42:57 +0000538 }
539}
540
541
542/* ******************************* */
543
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000544/* subnegotiation -- ignore all (except TTYPE,NAWS) */
Eric Andersen28c70b32000-06-14 20:42:57 +0000545
546static int subneg(byte c)
547{
548 switch (G.telstate)
549 {
550 case TS_SUB1:
551 if (c == IAC)
552 G.telstate = TS_SUB2;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000553#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000554 else
555 if (c == TELOPT_TTYPE)
556 putiac_subopt(TELOPT_TTYPE,ttype);
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{
578 tcsetattr(0, TCSADRAIN, &G.termios_raw);
579}
580
Eric Andersencd8c4362001-11-10 11:22:46 +0000581static void cookmode(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000582{
583 tcsetattr(0, TCSADRAIN, &G.termios_def);
584}
585
586extern int telnet_main(int argc, char** argv)
587{
588 struct in_addr 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];
593#else
594 fd_set readfds;
595 int maxfd;
596#endif
597
Eric Andersen0e28e1f2002-04-26 07:20:47 +0000598#ifdef CONFIG_FEATURE_AUTOWIDTH
599 struct winsize winp;
600 if( ioctl(0, TIOCGWINSZ, &winp) == 0 ) {
601 win_width = winp.ws_col;
602 win_height = winp.ws_row;
603 }
604#endif
605
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000606#ifdef CONFIG_FEATURE_TELNET_TTYPE
Eric Andersen7e1273e2001-05-07 17:57:45 +0000607 ttype = getenv("TERM");
608#endif
Eric Andersen28c70b32000-06-14 20:42:57 +0000609
610 memset(&G, 0, sizeof G);
611
612 if (tcgetattr(0, &G.termios_def) < 0)
613 exit(1);
614
615 G.termios_raw = G.termios_def;
Eric Andersen28c70b32000-06-14 20:42:57 +0000616 cfmakeraw(&G.termios_raw);
617
Eric Andersen67991cf2001-02-14 21:23:06 +0000618 if (argc < 2) show_usage();
Eric Andersen28c70b32000-06-14 20:42:57 +0000619 port = (argc > 2)? getport(argv[2]): 23;
620
Eric Andersen28c70b32000-06-14 20:42:57 +0000621 host = getserver(argv[1]);
622
623 G.netfd = remote_connect(host, port);
624
625 signal(SIGINT, fgotsig);
626
627#ifdef USE_POLL
628 ufds[0].fd = 0; ufds[1].fd = G.netfd;
629 ufds[0].events = ufds[1].events = POLLIN;
630#else
631 FD_ZERO(&readfds);
632 FD_SET(0, &readfds);
633 FD_SET(G.netfd, &readfds);
634 maxfd = G.netfd + 1;
635#endif
636
637 while (1)
638 {
639#ifndef USE_POLL
640 fd_set rfds = readfds;
641
642 switch (select(maxfd, &rfds, NULL, NULL, NULL))
643#else
644 switch (poll(ufds, 2, -1))
645#endif
646 {
647 case 0:
648 /* timeout */
649 case -1:
650 /* error, ignore and/or log something, bay go to loop */
651 if (G.gotsig)
652 conescape();
653 else
654 sleep(1);
655 break;
656 default:
657
658#ifdef USE_POLL
659 if (ufds[0].revents) /* well, should check POLLIN, but ... */
660#else
661 if (FD_ISSET(0, &rfds))
662#endif
663 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000664 len = read(0, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000665
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000666 if (len <= 0)
Eric Andersen28c70b32000-06-14 20:42:57 +0000667 doexit(0);
668
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000669 TRACE(0, ("Read con: %d\n", len));
Eric Andersen28c70b32000-06-14 20:42:57 +0000670
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000671 handlenetoutput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000672 }
673
674#ifdef USE_POLL
675 if (ufds[1].revents) /* well, should check POLLIN, but ... */
676#else
677 if (FD_ISSET(G.netfd, &rfds))
678#endif
679 {
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000680 len = read(G.netfd, G.buf, DATABUFSIZE);
Eric Andersen28c70b32000-06-14 20:42:57 +0000681
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000682 if (len <= 0)
Eric Andersen28c70b32000-06-14 20:42:57 +0000683 {
684 WriteCS(1, "Connection closed by foreign host.\r\n");
685 doexit(1);
686 }
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000687 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len));
Eric Andersen28c70b32000-06-14 20:42:57 +0000688
Glenn L McGrath78b0e372001-06-26 02:06:08 +0000689 handlenetinput(len);
Eric Andersen28c70b32000-06-14 20:42:57 +0000690 }
691 }
692 }
693}
694
695static int getport(char * p)
696{
697 unsigned int port = atoi(p);
698
699 if ((unsigned)(port - 1 ) > 65534)
700 {
Matt Kraaidd19c692001-01-31 19:00:21 +0000701 error_msg_and_die("%s: bad port number", p);
Eric Andersen28c70b32000-06-14 20:42:57 +0000702 }
703 return port;
704}
705
706static struct in_addr getserver(char * host)
707{
708 struct in_addr addr;
Matt Kraaic55b8d42001-05-16 15:40:51 +0000709
Eric Andersen28c70b32000-06-14 20:42:57 +0000710 struct hostent * he;
Matt Kraaic55b8d42001-05-16 15:40:51 +0000711 he = xgethostbyname(host);
Eric Andersen28c70b32000-06-14 20:42:57 +0000712 memcpy(&addr, he->h_addr, sizeof addr);
713
714 TRACE(1, ("addr: %s\n", inet_ntoa(addr)));
Matt Kraaic55b8d42001-05-16 15:40:51 +0000715
Eric Andersen28c70b32000-06-14 20:42:57 +0000716 return addr;
Matt Kraaic55b8d42001-05-16 15:40:51 +0000717}
Eric Andersen28c70b32000-06-14 20:42:57 +0000718
Eric Andersencd8c4362001-11-10 11:22:46 +0000719static int create_socket(void)
Eric Andersen28c70b32000-06-14 20:42:57 +0000720{
721 return socket(AF_INET, SOCK_STREAM, 0);
722}
723
724static void setup_sockaddr_in(struct sockaddr_in * addr, int port)
725{
Eric Andersen8b960322001-03-17 05:43:39 +0000726 memset(addr, 0, sizeof(struct sockaddr_in));
Eric Andersen28c70b32000-06-14 20:42:57 +0000727 addr->sin_family = AF_INET;
728 addr->sin_port = htons(port);
729}
730
731#if 0
732static int local_bind(int port)
733{
734 struct sockaddr_in s_addr;
735 int s = create_socket();
736
737 setup_sockaddr_in(&s_addr, port);
738
739 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
740
741 if (bind(s, &s_addr, sizeof s_addr) < 0)
742 {
743 char * e = sys_errlist[errno];
744 syserrorexit("bind");
745 exit(1);
746 }
747 listen(s, 1);
748
749 return s;
750}
751#endif
752
753static int remote_connect(struct in_addr addr, int port)
754{
755 struct sockaddr_in s_addr;
756 int s = create_socket();
757
758 setup_sockaddr_in(&s_addr, port);
759 s_addr.sin_addr = addr;
760
761 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
762
763 if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0)
764 {
Matt Kraai1fa1ade2000-12-18 03:57:16 +0000765 perror_msg_and_die("Unable to connect to remote host");
Eric Andersen28c70b32000-06-14 20:42:57 +0000766 }
767 return s;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000768}
769
770/*
Eric Andersen28c70b32000-06-14 20:42:57 +0000771Local Variables:
772c-file-style: "linux"
773c-basic-offset: 4
774tab-width: 4
775End:
776*/