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