OSDN Git Service

Extract usage information into a separate file.
[android-x86/external-busybox.git] / networking / telnet.c
1 /* 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  * 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  *
25  * 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> 
30  *
31  */
32
33
34 #include "internal.h"
35 #include <termios.h>
36 #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>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <netdb.h>
47
48 #if 0
49 #define DOTRACE 1
50 #endif
51
52 #if DOTRACE
53 #include <arpa/inet.h> /* for inet_ntoa()... */
54 #define TRACE(x, y) do { if (x) printf y; } while (0)
55 #else
56 #define TRACE(x, y) 
57 #endif
58
59 #if 0
60 #define USE_POLL
61 #include <sys/poll.h>
62 #else
63 #include <sys/time.h>
64 #endif
65
66 #define DATABUFSIZE 128
67 #define IACBUFSIZE 128
68
69 #define CHM_TRY 0
70 #define CHM_ON  1
71 #define CHM_OFF 2
72
73 #define UF_ECHO 0x01
74 #define UF_SGA  0x02
75
76 #define TS_0    1
77 #define TS_IAC  2
78 #define TS_OPT  3
79 #define TS_SUB1 4
80 #define TS_SUB2 5
81
82 #define WriteCS(fd, str) write(fd, str, sizeof str -1)
83
84 typedef unsigned char byte;
85
86 /* use globals to reduce size ??? */ /* test this hypothesis later */
87 struct Globalvars {
88         int             netfd; /* console fd:s are 0 and 1 (and 2) */
89     /* same buffer used both for network and console read/write */
90         char *  buf; /* allocating so static size is smaller */
91         short   len;
92         byte    telstate; /* telnet negotiation state from network input */
93         byte    telwish;  /* DO, DONT, WILL, WONT */
94         byte    charmode;
95         byte    telflags;
96         byte    gotsig;
97         /* buffer to handle telnet negotiations */
98         char *  iacbuf;
99         short   iaclen; /* could even use byte */
100         struct termios termios_def;     
101         struct termios termios_raw;     
102 } G;
103
104 #define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
105
106 #ifdef USE_GLOBALVAR_PTR
107 struct Globalvars * Gptr;
108 #define G (*Gptr)
109 #else
110 struct Globalvars G;
111 #endif
112
113 static inline void iacflush()
114 {
115         write(G.netfd, G.iacbuf, G.iaclen);
116         G.iaclen = 0;
117 }
118
119 /* Function prototypes */
120 static int getport(char * p);
121 static struct in_addr getserver(char * p);
122 static int create_socket();
123 static void setup_sockaddr_in(struct sockaddr_in * addr, int port);
124 static int remote_connect(struct in_addr addr, int port);
125 static void rawmode();
126 static void cookmode();
127 static void do_linemode();
128 static void will_charmode();
129 static void telopt(byte c);
130 static int subneg(byte c);
131 #if 0
132 static int local_bind(int port);
133 #endif
134
135 /* Some globals */
136 static int one = 1;
137
138 static void doexit(int ev)
139 {
140         cookmode();
141         exit(ev);
142 }       
143
144 static void conescape()
145 {
146         char b;
147
148         if (G.gotsig)   /* came from line  mode... go raw */
149                 rawmode();
150
151         WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
152                         " l     go to line mode\r\n"
153                         " c     go to character mode\r\n"
154                         " z     suspend telnet\r\n"
155                         " e     exit telnet\r\n");
156
157         if (read(0, &b, 1) <= 0)
158                 doexit(1);
159
160         switch (b)
161         {
162         case 'l':
163                 if (!G.gotsig)
164                 {
165                         do_linemode();
166                         goto rrturn;
167                 }
168                 break;
169         case 'c':
170                 if (G.gotsig)
171                 {
172                         will_charmode();
173                         goto rrturn;
174                 }
175                 break;
176         case 'z':
177                 cookmode();
178                 kill(0, SIGTSTP);
179                 rawmode();
180                 break;
181         case 'e':
182                 doexit(0);
183         }
184
185         WriteCS(1, "continuing...\r\n");
186
187         if (G.gotsig)
188                 cookmode();
189         
190  rrturn:
191         G.gotsig = 0;
192         
193 }
194 static void handlenetoutput()
195 {
196         /*      here we could do smart tricks how to handle 0xFF:s in output
197          *      stream  like writing twice every sequence of FF:s (thus doing
198          *      many write()s. But I think interactive telnet application does
199          *      not need to be 100% 8-bit clean, so changing every 0xff:s to
200          *      0x7f:s */
201
202         int i;
203         byte * p = G.buf;
204
205         for (i = G.len; i > 0; i--, p++)
206         {
207                 if (*p == 0x1d)
208                 {
209                         conescape();
210                         return;
211                 }
212                 if (*p == 0xff)
213                         *p = 0x7f;
214         }
215         write(G.netfd, G.buf, G.len);
216 }
217
218
219 static void handlenetinput()
220 {
221         int i;
222         int cstart = 0;
223
224         for (i = 0; i < G.len; i++)
225         {
226                 byte c = G.buf[i];
227
228                 if (G.telstate == 0) /* most of the time state == 0 */
229                 {
230                         if (c == IAC)
231                         {
232                                 cstart = i;
233                                 G.telstate = TS_IAC;
234                         }
235                 }
236                 else
237                         switch (G.telstate)
238                          {
239                          case TS_0:
240                                  if (c == IAC)
241                                          G.telstate = TS_IAC;
242                                  else
243                                          G.buf[cstart++] = c;
244                                  break;
245
246                          case TS_IAC:
247                                  if (c == IAC) /* IAC IAC -> 0xFF */
248                                  {
249                                          G.buf[cstart++] = c;
250                                          G.telstate = TS_0;
251                                          break;
252                                  }
253                                  /* else */
254                                  switch (c)
255                                  {
256                                  case SB:
257                                          G.telstate = TS_SUB1;
258                                          break;
259                                  case DO:
260                                  case DONT:
261                                  case WILL:
262                                  case WONT:
263                                          G.telwish =  c;
264                                          G.telstate = TS_OPT;
265                                          break;
266                                  default:
267                                          G.telstate = TS_0;     /* DATA MARK must be added later */
268                                  }
269                                  break;
270                          case TS_OPT: /* WILL, WONT, DO, DONT */
271                                  telopt(c);
272                                  G.telstate = TS_0;
273                                  break;
274                          case TS_SUB1: /* Subnegotiation */
275                          case TS_SUB2: /* Subnegotiation */
276                                  if (subneg(c) == TRUE)
277                                          G.telstate = TS_0;
278                                  break;
279                          }
280         }
281         if (G.telstate)
282         {
283                 if (G.iaclen)                   iacflush();
284                 if (G.telstate == TS_0) G.telstate = 0;
285
286                 G.len = cstart;
287         }
288
289         if (G.len)
290                 write(1, G.buf, G.len);
291 }
292
293
294 /* ******************************* */
295
296 static inline void putiac(int c)
297 {
298         G.iacbuf[G.iaclen++] = c;
299 }
300
301
302 static void putiac2(byte wwdd, byte c)
303 {
304         if (G.iaclen + 3 > IACBUFSIZE)
305                 iacflush();
306
307         putiac(IAC);
308         putiac(wwdd);
309         putiac(c);
310 }
311
312 #if 0
313 static void putiac1(byte c)
314 {
315         if (G.iaclen + 2 > IACBUFSIZE)
316                 iacflush();
317
318         putiac(IAC);
319         putiac(c);
320 }
321 #endif
322
323 /* void putiacstring (subneg strings) */
324
325 /* ******************************* */
326
327 char const escapecharis[] = "\r\nEscape character is ";
328
329 static void setConMode()
330 {
331         if (G.telflags & UF_ECHO)
332         {
333                 if (G.charmode == CHM_TRY) {
334                         G.charmode = CHM_ON;
335                         fprintf(stdout, "\r\nEntering character mode%s'^]'.\r\n", escapecharis);
336                         rawmode();
337                 }
338         }
339         else
340         {
341                 if (G.charmode != CHM_OFF) {
342                         G.charmode = CHM_OFF;
343                         fprintf(stdout, "\r\nEntering line mode%s'^C'.\r\n", escapecharis);
344                         cookmode();
345                 }
346         }
347 }
348
349 /* ******************************* */
350
351 static void will_charmode()
352 {
353         G.charmode = CHM_TRY;
354         G.telflags |= (UF_ECHO | UF_SGA);
355         setConMode();
356   
357         putiac2(DO, TELOPT_ECHO);
358         putiac2(DO, TELOPT_SGA);
359         iacflush();
360 }
361
362 static void do_linemode()
363 {
364         G.charmode = CHM_TRY;
365         G.telflags &= ~(UF_ECHO | UF_SGA);
366         setConMode();
367
368         putiac2(DONT, TELOPT_ECHO);
369         putiac2(DONT, TELOPT_SGA);
370         iacflush();
371 }
372
373 /* ******************************* */
374
375 static inline void to_notsup(char c)
376 {
377         if      (G.telwish == WILL)     putiac2(DONT, c);
378         else if (G.telwish == DO)       putiac2(WONT, c);
379 }
380
381 static inline void to_echo()
382 {
383         /* if server requests ECHO, don't agree */
384         if      (G.telwish == DO) {     putiac2(WONT, TELOPT_ECHO);     return; }
385         else if (G.telwish == DONT)     return;
386   
387         if (G.telflags & UF_ECHO)
388         {
389                 if (G.telwish == WILL)
390                         return;
391         }
392         else
393                 if (G.telwish == WONT)
394                         return;
395
396         if (G.charmode != CHM_OFF)
397                 G.telflags ^= UF_ECHO;
398
399         if (G.telflags & UF_ECHO)
400                 putiac2(DO, TELOPT_ECHO);
401         else
402                 putiac2(DONT, TELOPT_ECHO);
403
404         setConMode();
405         WriteCS(1, "\r\n");  /* sudden modec */
406 }
407
408 static inline void to_sga()
409 {
410         /* daemon always sends will/wont, client do/dont */
411
412         if (G.telflags & UF_SGA)
413         {
414                 if (G.telwish == WILL)
415                         return;
416         }
417         else
418                 if (G.telwish == WONT)
419                         return;
420   
421         if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
422                 putiac2(DO, TELOPT_SGA);
423         else
424                 putiac2(DONT, TELOPT_SGA);
425
426         return;
427 }
428
429 static void telopt(byte c)
430 {
431         switch (c)
432         {
433         case TELOPT_ECHO:               to_echo(c);             break;
434         case TELOPT_SGA:                to_sga(c);              break;
435         default:                                to_notsup(c);   break;
436         }
437 }
438
439
440 /* ******************************* */
441
442 /* subnegotiation -- ignore all */
443
444 static int subneg(byte c)
445 {
446         switch (G.telstate)
447         {
448         case TS_SUB1:
449                 if (c == IAC)
450                         G.telstate = TS_SUB2;
451                 break;
452         case TS_SUB2:
453                 if (c == SE)
454                         return TRUE;
455                 G.telstate = TS_SUB1;
456                 /* break; */
457         }
458         return FALSE;
459 }
460
461 /* ******************************* */
462
463 static void fgotsig(int sig)
464 {
465         G.gotsig = sig;
466 }
467
468
469 static void rawmode()
470 {
471         tcsetattr(0, TCSADRAIN, &G.termios_raw);
472 }       
473
474 static void cookmode()
475 {
476         tcsetattr(0, TCSADRAIN, &G.termios_def);
477 }
478
479 extern int telnet_main(int argc, char** argv)
480 {
481         struct in_addr host;
482         int port;
483 #ifdef USE_POLL
484         struct pollfd ufds[2];
485 #else   
486         fd_set readfds;
487         int maxfd;
488 #endif  
489
490
491         memset(&G, 0, sizeof G);
492
493         if (tcgetattr(0, &G.termios_def) < 0)
494                 exit(1);
495         
496         G.termios_raw = G.termios_def;
497
498         cfmakeraw(&G.termios_raw);
499         
500         if (argc < 2)   usage(telnet_usage);
501         port = (argc > 2)? getport(argv[2]): 23;
502         
503         G.buf = xmalloc(DATABUFSIZE);
504         G.iacbuf = xmalloc(IACBUFSIZE);
505         
506         host = getserver(argv[1]);
507
508         G.netfd = remote_connect(host, port);
509
510         signal(SIGINT, fgotsig);
511
512 #ifdef USE_POLL
513         ufds[0].fd = 0; ufds[1].fd = G.netfd;
514         ufds[0].events = ufds[1].events = POLLIN;
515 #else   
516         FD_ZERO(&readfds);
517         FD_SET(0, &readfds);
518         FD_SET(G.netfd, &readfds);
519         maxfd = G.netfd + 1;
520 #endif
521         
522         while (1)
523         {
524 #ifndef USE_POLL
525                 fd_set rfds = readfds;
526                 
527                 switch (select(maxfd, &rfds, NULL, NULL, NULL))
528 #else
529                 switch (poll(ufds, 2, -1))
530 #endif                  
531                 {
532                 case 0:
533                         /* timeout */
534                 case -1:
535                         /* error, ignore and/or log something, bay go to loop */
536                         if (G.gotsig)
537                                 conescape();
538                         else
539                                 sleep(1);
540                         break;
541                 default:
542
543 #ifdef USE_POLL
544                         if (ufds[0].revents) /* well, should check POLLIN, but ... */
545 #else                           
546                         if (FD_ISSET(0, &rfds))
547 #endif                          
548                         {
549                                 G.len = read(0, G.buf, DATABUFSIZE);
550
551                                 if (G.len <= 0)
552                                         doexit(0);
553
554                                 TRACE(0, ("Read con: %d\n", G.len));
555                                 
556                                 handlenetoutput();
557                         }
558
559 #ifdef USE_POLL
560                         if (ufds[1].revents) /* well, should check POLLIN, but ... */
561 #else                           
562                         if (FD_ISSET(G.netfd, &rfds))
563 #endif                          
564                         {
565                                 G.len = read(G.netfd, G.buf, DATABUFSIZE);
566
567                                 if (G.len <= 0)
568                                 {
569                                         WriteCS(1, "Connection closed by foreign host.\r\n");
570                                         doexit(1);
571                                 }
572                                 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, G.len));
573
574                                 handlenetinput();
575                         }
576                 }
577         }
578 }
579
580 static int getport(char * p)
581 {
582         unsigned int port = atoi(p);
583
584         if ((unsigned)(port - 1 ) > 65534)
585         {
586                 fatalError("%s: bad port number\n", p);
587         }
588         return port;
589 }
590
591 static struct in_addr getserver(char * host)
592 {
593         struct in_addr addr;
594         
595         struct hostent * he;
596         if ((he = gethostbyname(host)) == NULL)
597         {
598                 fatalError("%s: Unkonwn host\n", host);
599         }
600         memcpy(&addr, he->h_addr, sizeof addr);
601
602         TRACE(1, ("addr: %s\n", inet_ntoa(addr)));
603         
604         return addr;
605 }       
606
607 static int create_socket()
608 {
609         return socket(AF_INET, SOCK_STREAM, 0);
610 }
611
612 static void setup_sockaddr_in(struct sockaddr_in * addr, int port)
613 {
614         memset(addr, 0, sizeof addr);
615         addr->sin_family = AF_INET;
616         addr->sin_port = htons(port);
617 }
618   
619 #if 0
620 static int local_bind(int port)
621 {
622         struct sockaddr_in s_addr;
623         int s = create_socket();
624   
625         setup_sockaddr_in(&s_addr, port);
626   
627         setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
628   
629         if (bind(s, &s_addr, sizeof s_addr) < 0)
630         {
631                 char * e = sys_errlist[errno];
632                 syserrorexit("bind");
633                 exit(1);
634         }
635         listen(s, 1);
636         
637         return s;
638 }
639 #endif
640
641 static int remote_connect(struct in_addr addr, int port)
642 {
643         struct sockaddr_in s_addr;
644         int s = create_socket();
645
646         setup_sockaddr_in(&s_addr, port);
647         s_addr.sin_addr = addr;
648
649         setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
650
651         if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0)
652         {
653                 fatalError("Unable to connect to remote host: %s\n", strerror(errno));
654         }
655         return s;
656 }
657
658 /*
659 Local Variables:
660 c-file-style: "linux"
661 c-basic-offset: 4
662 tab-width: 4
663 End:
664 */
665