Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/networking/telnet.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 816 - (hide annotations) (download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 12477 byte(s)
-updated to busybox-1.13.4
1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * telnet implementation for busybox
4     *
5     * 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
10     *
11     * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
12     *
13     * HISTORY
14     * Revision 3.1 1994/04/17 11:31:54 too
15     * initial revision
16     * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
17     * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
18     * <jam@ltsp.org>
19     * Modified 2004/02/11 to add ability to pass the USER variable to remote host
20     * by Fernando Silveira <swrh@gmx.net>
21     *
22     */
23    
24     #include <termios.h>
25     #include <arpa/telnet.h>
26     #include <netinet/in.h>
27 niro 816 #include "libbb.h"
28 niro 532
29     #ifdef DOTRACE
30     #define TRACE(x, y) do { if (x) printf y; } while (0)
31     #else
32     #define TRACE(x, y)
33     #endif
34    
35 niro 816 enum {
36     DATABUFSIZE = 128,
37     IACBUFSIZE = 128,
38 niro 532
39     CHM_TRY = 0,
40     CHM_ON = 1,
41     CHM_OFF = 2,
42    
43     UF_ECHO = 0x01,
44     UF_SGA = 0x02,
45    
46     TS_0 = 1,
47     TS_IAC = 2,
48     TS_OPT = 3,
49     TS_SUB1 = 4,
50     TS_SUB2 = 5,
51     };
52    
53     typedef unsigned char byte;
54    
55 niro 816 enum { netfd = 3 };
56    
57     struct globals {
58     int iaclen; /* could even use byte, but it's a loss on x86 */
59 niro 532 byte telstate; /* telnet negotiation state from network input */
60     byte telwish; /* DO, DONT, WILL, WONT */
61     byte charmode;
62     byte telflags;
63     byte do_termios;
64 niro 816 #if ENABLE_FEATURE_TELNET_TTYPE
65     char *ttype;
66     #endif
67     #if ENABLE_FEATURE_TELNET_AUTOLOGIN
68     const char *autologin;
69     #endif
70     #if ENABLE_FEATURE_AUTOWIDTH
71     unsigned win_width, win_height;
72     #endif
73     /* same buffer used both for network and console read/write */
74     char buf[DATABUFSIZE];
75 niro 532 /* buffer to handle telnet negotiations */
76     char iacbuf[IACBUFSIZE];
77     struct termios termios_def;
78     struct termios termios_raw;
79 niro 816 };
80     #define G (*(struct globals*)&bb_common_bufsiz1)
81     void BUG_telnet_globals_too_big(void);
82     #define INIT_G() do { \
83     if (sizeof(G) > COMMON_BUFSIZE) \
84     BUG_telnet_globals_too_big(); \
85     /* memset(&G, 0, sizeof G); - already is */ \
86     } while (0)
87 niro 532
88     /* Function prototypes */
89     static void rawmode(void);
90     static void cookmode(void);
91     static void do_linemode(void);
92     static void will_charmode(void);
93     static void telopt(byte c);
94     static int subneg(byte c);
95    
96 niro 816 static void iacflush(void)
97     {
98     write(netfd, G.iacbuf, G.iaclen);
99     G.iaclen = 0;
100     }
101 niro 532
102 niro 816 #define write_str(fd, str) write(fd, str, sizeof(str) - 1)
103 niro 532
104 niro 816 static void doexit(int ev) NORETURN;
105 niro 532 static void doexit(int ev)
106     {
107     cookmode();
108     exit(ev);
109     }
110    
111     static void conescape(void)
112     {
113     char b;
114    
115 niro 816 if (bb_got_signal) /* came from line mode... go raw */
116 niro 532 rawmode();
117    
118 niro 816 write_str(1, "\r\nConsole escape. Commands are:\r\n\n"
119 niro 532 " l go to line mode\r\n"
120     " c go to character mode\r\n"
121     " z suspend telnet\r\n"
122     " e exit telnet\r\n");
123    
124 niro 816 if (read(STDIN_FILENO, &b, 1) <= 0)
125     doexit(EXIT_FAILURE);
126 niro 532
127 niro 816 switch (b) {
128 niro 532 case 'l':
129 niro 816 if (!bb_got_signal) {
130 niro 532 do_linemode();
131     goto rrturn;
132     }
133     break;
134     case 'c':
135 niro 816 if (bb_got_signal) {
136 niro 532 will_charmode();
137     goto rrturn;
138     }
139     break;
140     case 'z':
141     cookmode();
142     kill(0, SIGTSTP);
143     rawmode();
144     break;
145     case 'e':
146 niro 816 doexit(EXIT_SUCCESS);
147 niro 532 }
148    
149 niro 816 write_str(1, "continuing...\r\n");
150 niro 532
151 niro 816 if (bb_got_signal)
152 niro 532 cookmode();
153    
154     rrturn:
155 niro 816 bb_got_signal = 0;
156 niro 532
157     }
158 niro 816
159 niro 532 static void handlenetoutput(int len)
160     {
161 niro 816 /* here we could do smart tricks how to handle 0xFF:s in output
162     * stream like writing twice every sequence of FF:s (thus doing
163     * many write()s. But I think interactive telnet application does
164     * not need to be 100% 8-bit clean, so changing every 0xff:s to
165     * 0x7f:s
166 niro 532 *
167 niro 816 * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com)
168     * I don't agree.
169     * first - I cannot use programs like sz/rz
170     * second - the 0x0D is sent as one character and if the next
171     * char is 0x0A then it's eaten by a server side.
172     * third - whay doy you have to make 'many write()s'?
173     * I don't understand.
174     * So I implemented it. It's realy useful for me. I hope that
175     * others people will find it interesting too.
176 niro 532 */
177    
178     int i, j;
179     byte * p = (byte*)G.buf;
180     byte outbuf[4*DATABUFSIZE];
181    
182 niro 816 for (i = len, j = 0; i > 0; i--, p++) {
183     if (*p == 0x1d) {
184 niro 532 conescape();
185     return;
186     }
187     outbuf[j++] = *p;
188     if (*p == 0xff)
189     outbuf[j++] = 0xff;
190     else if (*p == 0x0d)
191     outbuf[j++] = 0x00;
192     }
193 niro 816 if (j > 0)
194     write(netfd, outbuf, j);
195 niro 532 }
196    
197     static void handlenetinput(int len)
198     {
199     int i;
200     int cstart = 0;
201    
202 niro 816 for (i = 0; i < len; i++) {
203 niro 532 byte c = G.buf[i];
204    
205 niro 816 if (G.telstate == 0) { /* most of the time state == 0 */
206     if (c == IAC) {
207 niro 532 cstart = i;
208     G.telstate = TS_IAC;
209     }
210 niro 816 } else
211     switch (G.telstate) {
212     case TS_0:
213     if (c == IAC)
214     G.telstate = TS_IAC;
215     else
216     G.buf[cstart++] = c;
217     break;
218 niro 532
219 niro 816 case TS_IAC:
220     if (c == IAC) { /* IAC IAC -> 0xFF */
221     G.buf[cstart++] = c;
222     G.telstate = TS_0;
223     break;
224     }
225     /* else */
226     switch (c) {
227     case SB:
228     G.telstate = TS_SUB1;
229     break;
230     case DO:
231     case DONT:
232     case WILL:
233     case WONT:
234     G.telwish = c;
235     G.telstate = TS_OPT;
236     break;
237     default:
238     G.telstate = TS_0; /* DATA MARK must be added later */
239     }
240     break;
241     case TS_OPT: /* WILL, WONT, DO, DONT */
242     telopt(c);
243     G.telstate = TS_0;
244     break;
245     case TS_SUB1: /* Subnegotiation */
246     case TS_SUB2: /* Subnegotiation */
247     if (subneg(c))
248     G.telstate = TS_0;
249     break;
250     }
251 niro 532 }
252 niro 816 if (G.telstate) {
253     if (G.iaclen) iacflush();
254 niro 532 if (G.telstate == TS_0) G.telstate = 0;
255     len = cstart;
256     }
257    
258     if (len)
259 niro 816 write(STDOUT_FILENO, G.buf, len);
260 niro 532 }
261    
262     static void putiac(int c)
263     {
264     G.iacbuf[G.iaclen++] = c;
265     }
266    
267     static void putiac2(byte wwdd, byte c)
268     {
269     if (G.iaclen + 3 > IACBUFSIZE)
270     iacflush();
271    
272     putiac(IAC);
273     putiac(wwdd);
274     putiac(c);
275     }
276    
277 niro 816 #if ENABLE_FEATURE_TELNET_TTYPE
278 niro 532 static void putiac_subopt(byte c, char *str)
279     {
280     int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 )
281    
282     if (G.iaclen + len > IACBUFSIZE)
283     iacflush();
284    
285     putiac(IAC);
286     putiac(SB);
287     putiac(c);
288     putiac(0);
289    
290     while (*str)
291     putiac(*str++);
292    
293     putiac(IAC);
294     putiac(SE);
295     }
296     #endif
297    
298 niro 816 #if ENABLE_FEATURE_TELNET_AUTOLOGIN
299 niro 532 static void putiac_subopt_autologin(void)
300     {
301 niro 816 int len = strlen(G.autologin) + 6; // (2 + 1 + 1 + strlen + 2)
302     const char *user = "USER";
303 niro 532
304     if (G.iaclen + len > IACBUFSIZE)
305     iacflush();
306    
307     putiac(IAC);
308     putiac(SB);
309     putiac(TELOPT_NEW_ENVIRON);
310     putiac(TELQUAL_IS);
311     putiac(NEW_ENV_VAR);
312    
313     while (*user)
314     putiac(*user++);
315    
316     putiac(NEW_ENV_VALUE);
317    
318 niro 816 while (*G.autologin)
319     putiac(*G.autologin++);
320 niro 532
321     putiac(IAC);
322     putiac(SE);
323     }
324     #endif
325    
326 niro 816 #if ENABLE_FEATURE_AUTOWIDTH
327 niro 532 static void putiac_naws(byte c, int x, int y)
328     {
329     if (G.iaclen + 9 > IACBUFSIZE)
330     iacflush();
331    
332     putiac(IAC);
333     putiac(SB);
334     putiac(c);
335    
336     putiac((x >> 8) & 0xff);
337     putiac(x & 0xff);
338     putiac((y >> 8) & 0xff);
339     putiac(y & 0xff);
340    
341     putiac(IAC);
342     putiac(SE);
343     }
344     #endif
345    
346 niro 816 static char const escapecharis[] ALIGN1 = "\r\nEscape character is ";
347 niro 532
348     static void setConMode(void)
349     {
350 niro 816 if (G.telflags & UF_ECHO) {
351 niro 532 if (G.charmode == CHM_TRY) {
352     G.charmode = CHM_ON;
353     printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis);
354     rawmode();
355     }
356 niro 816 } else {
357 niro 532 if (G.charmode != CHM_OFF) {
358     G.charmode = CHM_OFF;
359     printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
360     cookmode();
361     }
362     }
363     }
364    
365     static void will_charmode(void)
366     {
367     G.charmode = CHM_TRY;
368     G.telflags |= (UF_ECHO | UF_SGA);
369     setConMode();
370    
371     putiac2(DO, TELOPT_ECHO);
372     putiac2(DO, TELOPT_SGA);
373     iacflush();
374     }
375    
376     static void do_linemode(void)
377     {
378     G.charmode = CHM_TRY;
379     G.telflags &= ~(UF_ECHO | UF_SGA);
380     setConMode();
381    
382     putiac2(DONT, TELOPT_ECHO);
383     putiac2(DONT, TELOPT_SGA);
384     iacflush();
385     }
386    
387     static void to_notsup(char c)
388     {
389 niro 816 if (G.telwish == WILL)
390     putiac2(DONT, c);
391     else if (G.telwish == DO)
392     putiac2(WONT, c);
393 niro 532 }
394    
395     static void to_echo(void)
396     {
397     /* if server requests ECHO, don't agree */
398 niro 816 if (G.telwish == DO) {
399     putiac2(WONT, TELOPT_ECHO);
400     return;
401     }
402     if (G.telwish == DONT)
403     return;
404 niro 532
405 niro 816 if (G.telflags & UF_ECHO) {
406 niro 532 if (G.telwish == WILL)
407     return;
408 niro 816 } else if (G.telwish == WONT)
409     return;
410 niro 532
411     if (G.charmode != CHM_OFF)
412     G.telflags ^= UF_ECHO;
413    
414     if (G.telflags & UF_ECHO)
415     putiac2(DO, TELOPT_ECHO);
416     else
417     putiac2(DONT, TELOPT_ECHO);
418    
419     setConMode();
420 niro 816 write_str(1, "\r\n"); /* sudden modec */
421 niro 532 }
422    
423     static void to_sga(void)
424     {
425     /* daemon always sends will/wont, client do/dont */
426    
427 niro 816 if (G.telflags & UF_SGA) {
428 niro 532 if (G.telwish == WILL)
429     return;
430 niro 816 } else if (G.telwish == WONT)
431     return;
432 niro 532
433 niro 816 G.telflags ^= UF_SGA; /* toggle */
434     if (G.telflags & UF_SGA)
435 niro 532 putiac2(DO, TELOPT_SGA);
436     else
437     putiac2(DONT, TELOPT_SGA);
438     }
439    
440 niro 816 #if ENABLE_FEATURE_TELNET_TTYPE
441 niro 532 static void to_ttype(void)
442     {
443     /* Tell server we will (or won't) do TTYPE */
444    
445 niro 816 if (G.ttype)
446 niro 532 putiac2(WILL, TELOPT_TTYPE);
447     else
448     putiac2(WONT, TELOPT_TTYPE);
449     }
450     #endif
451    
452 niro 816 #if ENABLE_FEATURE_TELNET_AUTOLOGIN
453 niro 532 static void to_new_environ(void)
454     {
455     /* Tell server we will (or will not) do AUTOLOGIN */
456    
457 niro 816 if (G.autologin)
458 niro 532 putiac2(WILL, TELOPT_NEW_ENVIRON);
459     else
460     putiac2(WONT, TELOPT_NEW_ENVIRON);
461     }
462     #endif
463    
464 niro 816 #if ENABLE_FEATURE_AUTOWIDTH
465 niro 532 static void to_naws(void)
466     {
467     /* Tell server we will do NAWS */
468     putiac2(WILL, TELOPT_NAWS);
469     }
470     #endif
471    
472     static void telopt(byte c)
473     {
474 niro 816 switch (c) {
475     case TELOPT_ECHO:
476     to_echo(); break;
477     case TELOPT_SGA:
478     to_sga(); break;
479     #if ENABLE_FEATURE_TELNET_TTYPE
480     case TELOPT_TTYPE:
481     to_ttype(); break;
482 niro 532 #endif
483 niro 816 #if ENABLE_FEATURE_TELNET_AUTOLOGIN
484     case TELOPT_NEW_ENVIRON:
485     to_new_environ(); break;
486 niro 532 #endif
487 niro 816 #if ENABLE_FEATURE_AUTOWIDTH
488     case TELOPT_NAWS:
489     to_naws();
490     putiac_naws(c, G.win_width, G.win_height);
491     break;
492 niro 532 #endif
493 niro 816 default:
494     to_notsup(c);
495     break;
496 niro 532 }
497     }
498    
499     /* subnegotiation -- ignore all (except TTYPE,NAWS) */
500     static int subneg(byte c)
501     {
502 niro 816 switch (G.telstate) {
503 niro 532 case TS_SUB1:
504     if (c == IAC)
505     G.telstate = TS_SUB2;
506 niro 816 #if ENABLE_FEATURE_TELNET_TTYPE
507 niro 532 else
508     if (c == TELOPT_TTYPE)
509 niro 816 putiac_subopt(TELOPT_TTYPE, G.ttype);
510 niro 532 #endif
511 niro 816 #if ENABLE_FEATURE_TELNET_AUTOLOGIN
512 niro 532 else
513     if (c == TELOPT_NEW_ENVIRON)
514     putiac_subopt_autologin();
515     #endif
516     break;
517     case TS_SUB2:
518     if (c == SE)
519     return TRUE;
520     G.telstate = TS_SUB1;
521     /* break; */
522     }
523     return FALSE;
524     }
525    
526     static void rawmode(void)
527     {
528 niro 816 if (G.do_termios)
529     tcsetattr(0, TCSADRAIN, &G.termios_raw);
530 niro 532 }
531    
532     static void cookmode(void)
533     {
534 niro 816 if (G.do_termios)
535     tcsetattr(0, TCSADRAIN, &G.termios_def);
536 niro 532 }
537    
538 niro 816 /* poll gives smaller (-70 bytes) code */
539     #define USE_POLL 1
540    
541     int telnet_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
542     int telnet_main(int argc UNUSED_PARAM, char **argv)
543 niro 532 {
544     char *host;
545     int port;
546     int len;
547     #ifdef USE_POLL
548     struct pollfd ufds[2];
549     #else
550     fd_set readfds;
551     int maxfd;
552     #endif
553    
554 niro 816 INIT_G();
555    
556     #if ENABLE_FEATURE_AUTOWIDTH
557     get_terminal_width_height(0, &G.win_width, &G.win_height);
558 niro 532 #endif
559    
560 niro 816 #if ENABLE_FEATURE_TELNET_TTYPE
561     G.ttype = getenv("TERM");
562 niro 532 #endif
563    
564     if (tcgetattr(0, &G.termios_def) >= 0) {
565     G.do_termios = 1;
566     G.termios_raw = G.termios_def;
567     cfmakeraw(&G.termios_raw);
568     }
569    
570 niro 816 #if ENABLE_FEATURE_TELNET_AUTOLOGIN
571     if (1 & getopt32(argv, "al:", &G.autologin))
572     G.autologin = getenv("USER");
573 niro 532 argv += optind;
574     #else
575     argv++;
576     #endif
577     if (!*argv)
578     bb_show_usage();
579     host = *argv++;
580     port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
581     if (*argv) /* extra params?? */
582     bb_show_usage();
583    
584 niro 816 xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
585 niro 532
586 niro 816 setsockopt(netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
587 niro 532
588 niro 816 signal(SIGINT, record_signo);
589 niro 532
590     #ifdef USE_POLL
591 niro 816 ufds[0].fd = 0; ufds[1].fd = netfd;
592 niro 532 ufds[0].events = ufds[1].events = POLLIN;
593     #else
594     FD_ZERO(&readfds);
595 niro 816 FD_SET(STDIN_FILENO, &readfds);
596     FD_SET(netfd, &readfds);
597     maxfd = netfd + 1;
598 niro 532 #endif
599    
600     while (1) {
601     #ifndef USE_POLL
602     fd_set rfds = readfds;
603    
604     switch (select(maxfd, &rfds, NULL, NULL, NULL))
605     #else
606     switch (poll(ufds, 2, -1))
607     #endif
608     {
609     case 0:
610     /* timeout */
611     case -1:
612     /* error, ignore and/or log something, bay go to loop */
613 niro 816 if (bb_got_signal)
614 niro 532 conescape();
615     else
616     sleep(1);
617     break;
618     default:
619    
620     #ifdef USE_POLL
621     if (ufds[0].revents) /* well, should check POLLIN, but ... */
622     #else
623 niro 816 if (FD_ISSET(STDIN_FILENO, &rfds))
624 niro 532 #endif
625     {
626 niro 816 len = read(STDIN_FILENO, G.buf, DATABUFSIZE);
627 niro 532 if (len <= 0)
628 niro 816 doexit(EXIT_SUCCESS);
629 niro 532 TRACE(0, ("Read con: %d\n", len));
630     handlenetoutput(len);
631     }
632    
633     #ifdef USE_POLL
634     if (ufds[1].revents) /* well, should check POLLIN, but ... */
635     #else
636 niro 816 if (FD_ISSET(netfd, &rfds))
637 niro 532 #endif
638     {
639 niro 816 len = read(netfd, G.buf, DATABUFSIZE);
640 niro 532 if (len <= 0) {
641 niro 816 write_str(1, "Connection closed by foreign host\r\n");
642     doexit(EXIT_FAILURE);
643 niro 532 }
644 niro 816 TRACE(0, ("Read netfd (%d): %d\n", netfd, len));
645 niro 532 handlenetinput(len);
646     }
647     }
648 niro 816 } /* while (1) */
649 niro 532 }