OSDN Git Service

Lots of updates. Finished implementing BB_FEATURE_TRIVIAL_HELP
[android-x86/external-busybox.git] / networking / telnet.c
1 /*
2  * $Id: telnet.c,v 1.3 2000/05/12 19:41:47 erik Exp $
3  * Mini telnet implementation for busybox
4  *
5  * Copyright (C) 2000 by Randolph Chung <tausq@debian.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  * This version of telnet is adapted (but very heavily modified) from the 
22  * telnet in netkit-telnet 0.16, which is:
23  *
24  * Copyright (c) 1989 The Regents of the University of California.
25  * All rights reserved.
26  *
27  * Original copyright notice is retained at the end of this file.
28  */
29
30 #include "internal.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <ctype.h>
35 #include <signal.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <termios.h>
39 #include <netinet/in.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/ioctl.h>
43 #define TELOPTS
44 #include <arpa/telnet.h>
45 #include <arpa/inet.h>
46
47 static int STDIN = 0;
48 static int STDOUT = 1;
49 static const char *telnet_usage = "telnet host [port]\n"
50 #ifndef BB_FEATURE_TRIVIAL_HELP
51         "\nProvides interactive communication with another\n"
52         "networked host using the TELNET protocol\n"
53 #endif
54         ;
55 static struct termios saved_tc;
56 static unsigned char options[NTELOPTS];
57 static char tr_state = 0; /* telnet send and receive state */
58 static unsigned char subbuffer[256];
59 static unsigned char *subpointer, *subend;
60 #define SB_CLEAR()      subpointer = subbuffer;
61 #define SB_TERM()       { subend = subpointer; SB_CLEAR(); }
62 #define SB_ACCUM(c)     if (subpointer < (subbuffer+sizeof subbuffer)) { *subpointer++ = (c); }
63 #define SB_GET()        (*subpointer++)
64 #define SB_PEEK()       (*subpointer)
65 #define SB_EOF()        (subpointer >= subend)
66 #define SB_LEN()        (subend - subpointer)
67
68 #define TELNETPORT              23
69 #define MASK_WILL               0x01
70 #define MASK_WONT               0x04
71 #define MASK_DO                 0x10
72 #define MASK_DONT               0x40
73
74 #define TFLAG_ISSET(opt, flag) (options[opt] & MASK_##flag)
75 #define TFLAG_SET(opt, flag) (options[opt] |= MASK_##flag)
76 #define TFLAG_CLR(opt, flag) (options[opt] &= ~MASK_##flag)
77
78 #define PERROR(ctx) do { fprintf(stderr, "%s: %s\n", ctx, strerror(errno)); \
79                          return; } while (0)
80                                                  
81 #define TS_DATA         0
82 #define TS_IAC          1
83 #define TS_WILL         2
84 #define TS_WONT         3
85 #define TS_DO           4
86 #define TS_DONT         5
87 #define TS_CR           6
88 #define TS_SB           7               /* sub-option collection */
89 #define TS_SE           8               /* looking for sub-option end */
90
91 /* prototypes */
92 static void telnet_init(void);
93 static void telnet_start(char *host, int port);
94 static void telnet_shutdown(void);
95 /* ******************************************************************* */
96 #define SENDCOMMAND(c,o) \
97         char buf[3]; \
98         buf[0] = IAC; buf[1] = c; buf[2] = o; \
99         write(s, buf, sizeof(buf)); 
100
101 static inline void telnet_sendwill(int s, int c) { SENDCOMMAND(WILL, c); }
102 static inline void telnet_sendwont(int s, int c) { SENDCOMMAND(WONT, c); }
103 static inline void telnet_senddo(int s, int c) { SENDCOMMAND(DO, c); }
104 static inline void telnet_senddont(int s, int c) { SENDCOMMAND(DONT, c); }
105
106 static void telnet_setoptions(int s)
107 {
108         /*
109         telnet_sendwill(s, TELOPT_NAWS); TFLAG_SET(TELOPT_NAWS, WILL);
110         telnet_sendwill(s, TELOPT_TSPEED); TFLAG_SET(TELOPT_TSPEED, WILL);
111         telnet_sendwill(s, TELOPT_NEW_ENVIRON); TFLAG_SET(TELOPT_NEW_ENVIRON, WILL);
112         telnet_senddo(s, TELOPT_STATUS); TFLAG_SET(TELOPT_STATUS, DO);
113         telnet_sendwill(s, TELOPT_TTYPE); TFLAG_SET(TELOPT_TTYPE, WILL);
114         */
115         telnet_senddo(s, TELOPT_SGA); TFLAG_SET(TELOPT_SGA, DO);
116         telnet_sendwill(s, TELOPT_LFLOW); TFLAG_SET(TELOPT_LFLOW, WILL);
117         telnet_sendwill(s, TELOPT_LINEMODE); TFLAG_SET(TELOPT_LINEMODE, WILL);
118         telnet_senddo(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, DO);
119         telnet_sendwill(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, WILL);
120 }
121
122 static void telnet_suboptions(int net)
123 {
124         char buf[256];
125         switch (SB_GET()) {
126                 case TELOPT_TTYPE:
127                         if (TFLAG_ISSET(TELOPT_TTYPE, WONT)) return;
128                         if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
129                         return;
130                 } else {
131                         const char *name = getenv("TERM");
132                                 if (name) {
133                                         snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c", IAC, SB,
134                                                 TELOPT_TTYPE, TELQUAL_IS, name, IAC, SE);
135                                         write(net, buf, strlen(name)+6);
136                                 }
137                         }
138                 break;
139                 case TELOPT_TSPEED:
140                         if (TFLAG_ISSET(TELOPT_TSPEED, WONT)) return;
141                 if (SB_EOF()) return;
142                 if (SB_GET() == TELQUAL_SEND) {
143                                 /*
144                         long oospeed, iispeed;
145                         netoring.printf("%c%c%c%c%ld,%ld%c%c", IAC, SB, TELOPT_TSPEED,
146                       TELQUAL_IS, oospeed, iispeed, IAC, SE);
147                     */
148                         }
149                 break;
150                 /*
151                 case TELOPT_LFLOW:
152                         if (TFLAG_ISSET(TELOPT_LFLOW, WONT)) return;
153                         if (SB_EOF()) return;
154                 switch(SB_GET()) {
155                         case 1: localflow = 1; break;
156                                 case 0: localflow = 0; break;
157                                 default: return;
158                         }
159                 break;
160                 case TELOPT_LINEMODE:
161                         if (TFLAG_ISSET(TELOPT_LINEMODE, WONT)) return;
162                         if (SB_EOF()) return;
163                         switch (SB_GET()) {
164                         case WILL: lm_will(subpointer, SB_LEN()); break;
165                                 case WONT: lm_wont(subpointer, SB_LEN()); break;
166                                 case DO: lm_do(subpointer, SB_LEN()); break;
167                                 case DONT: lm_dont(subpointer, SB_LEN()); break;
168                                 case LM_SLC: slc(subpointer, SB_LEN()); break;
169                                 case LM_MODE: lm_mode(subpointer, SB_LEN(), 0); break;
170                                 default: break;
171                         }
172                 break;
173                 case TELOPT_ENVIRON:
174                         if (SB_EOF()) return;
175                         switch(SB_PEEK()) {
176                                 case TELQUAL_IS:
177                                 case TELQUAL_INFO:
178                                         if (TFLAG_ISSET(TELOPT_ENVIRON, DONT)) return;
179                                         break;
180                                 case TELQUAL_SEND:
181                                         if (TFLAG_ISSET(TELOPT_ENVIRON, WONT)) return;
182                                         break;
183                                 default:
184                                         return;
185                         }
186                         env_opt(subpointer, SB_LEN());
187                         break;
188                 */
189                 case TELOPT_XDISPLOC:
190                         if (TFLAG_ISSET(TELOPT_XDISPLOC, WONT)) return;
191                 if (SB_EOF()) return;
192                         if (SB_GET() == TELQUAL_SEND) {
193                                 const char *dp = getenv("DISPLAY");
194                                 if (dp) {
195                                         snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c", IAC, SB,
196                                                 TELOPT_XDISPLOC, TELQUAL_IS, dp, IAC, SE);
197                                         write(net, buf, strlen(dp)+6);
198                                 }
199                         }
200                 break;
201         default:
202                         break;
203         }
204 }
205
206 static void sighandler(int sig)
207 {
208         telnet_shutdown();
209         exit(0);
210 }
211
212 static int telnet_send(int tty, int net)
213 {
214         int ret;
215         unsigned char ch;
216
217         while ((ret = read(tty, &ch, 1)) > 0) {
218                 if (ch == 29) { /* 29 -- ctrl-] */
219                         /* do something here? */
220                         exit(0);
221                 } else {
222                         ret = write(net, &ch, 1);
223                         break;
224                 }
225         }
226         if (ret == -1 && errno == EWOULDBLOCK) return 1;
227         return ret;
228 }
229
230 static int telnet_recv(int net, int tty)
231 {
232         /* globals: tr_state - telnet receive state */
233         int ret, c = 0;
234         unsigned char ch;
235
236         while ((ret = read(net, &ch, 1)) > 0) {
237                 c = ch;
238                 /* printf("%02X ", c); fflush(stdout); */
239                 switch (tr_state) {
240                         case TS_DATA:
241                                 if (c == IAC) {
242                                         tr_state = TS_IAC;
243                                         break;
244                                 } else {
245                                         write(tty, &c, 1);
246                                 }
247                                 break;
248                         case TS_IAC:
249                                 switch (c) {
250                                         case WILL:
251                                                 tr_state = TS_WILL; break;
252                                         case WONT:
253                                                 tr_state = TS_WONT; break;
254                                         case DO:
255                                                 tr_state = TS_DO; break;
256                                         case DONT:
257                                                 tr_state = TS_DONT; break;
258                                         case SB:
259                                                 SB_CLEAR();
260                                                 tr_state = TS_SB; break;
261                                         case IAC:
262                                                 write(tty, &c, 1); /* fallthrough */
263                                         default:
264                                                 tr_state = TS_DATA;
265                                 }
266                         
267                         /* subnegotiation -- ignored for now */
268                         case TS_SB:
269                                 if (c == IAC) tr_state = TS_SE;
270                                 else SB_ACCUM(c);
271                                 break;
272                         case TS_SE:
273                                 if (c == IAC) {
274                                         SB_ACCUM(IAC);
275                                         tr_state = TS_SB;
276                                 } else if (c == SE) {
277                                         SB_ACCUM(IAC);
278                                         SB_ACCUM(SE);
279                                         subpointer -= 2;
280                                         SB_TERM();
281                                         telnet_suboptions(net);
282                                         tr_state = TS_DATA;
283                                 }
284                             /* otherwise this is an error, but we ignore it for now */
285                                 break;
286                         /* handle incoming requests */
287                         case TS_WILL: 
288                                 printf("WILL %s\n", telopts[c]);
289                                 if (!TFLAG_ISSET(c, DO)) {
290                                         if (c == TELOPT_BINARY) {
291                                                 TFLAG_SET(c, DO);
292                                                 TFLAG_CLR(c, DONT);
293                                                 telnet_senddo(net, c);
294                                         } else {
295                                                 TFLAG_SET(c, DONT);
296                                                 telnet_senddont(net, c);
297                                         }
298                                 }
299                                 telnet_senddont(net, c);
300                                 tr_state = TS_DATA;
301                                 break;
302                         case TS_WONT:
303                                 printf("WONT %s\n", telopts[c]);
304                                 if (!TFLAG_ISSET(c, DONT)) {
305                                         TFLAG_SET(c, DONT);
306                                         TFLAG_CLR(c, DO);
307                                         telnet_senddont(net, c);
308                                 }
309                                 tr_state = TS_DATA;
310                                 break;
311                         case TS_DO:
312                                 printf("DO %s\n", telopts[c]);
313                                 if (!TFLAG_ISSET(c, WILL)) {
314                                         if (c == TELOPT_BINARY) {
315                                                 TFLAG_SET(c, WILL);
316                                                 TFLAG_CLR(c, WONT);
317                                                 telnet_sendwill(net, c);
318                                         } else {
319                                                 TFLAG_SET(c, WONT);
320                                                 telnet_sendwont(net, c);
321                                         }
322                                 }
323                                 tr_state = TS_DATA;
324                                 break;
325                         case TS_DONT:
326                                 printf("DONT %s\n", telopts[c]);
327                                 if (!TFLAG_ISSET(c, WONT)) {
328                                         TFLAG_SET(c, WONT);
329                                         TFLAG_CLR(c, WILL);
330                                         telnet_sendwont(net, c);
331                                 }
332                                 tr_state = TS_DATA;
333                                 break;
334                 }
335                                         
336         }
337         if (ret == -1 && errno == EWOULDBLOCK) return 1;
338         return ret;
339 }
340
341 /* ******************************************************************* */
342 static void telnet_init(void)
343 {
344         struct termios tmp_tc;
345         cc_t esc = (']' & 0x1f); /* ctrl-] */
346         
347         memset(options, 0, sizeof(options));
348         SB_CLEAR();
349
350         tcgetattr(STDIN, &saved_tc);
351
352         tmp_tc = saved_tc;
353     tmp_tc.c_lflag &= ~ECHO; /* echo */
354         tmp_tc.c_oflag |= ONLCR; /* NL->CRLF translation */
355         tmp_tc.c_iflag |= ICRNL; 
356         tmp_tc.c_iflag &= ~(IXANY|IXOFF|IXON); /* no flow control */
357         tmp_tc.c_lflag |= ISIG; /* trap signals */
358         tmp_tc.c_lflag &= ~ICANON; /* edit mode  */
359    
360         /* misc settings, compat with default telnet stuff */
361         tmp_tc.c_oflag &= ~TABDLY;
362         
363         /* 8-bit clean */
364         tmp_tc.c_iflag &= ~ISTRIP;
365         tmp_tc.c_cflag &= ~(CSIZE|PARENB);
366         tmp_tc.c_cflag |= saved_tc.c_cflag & (CSIZE|PARENB);
367         tmp_tc.c_oflag |= OPOST;
368
369         /* set escape character */
370         tmp_tc.c_cc[VEOL] = esc;
371         tcsetattr(STDIN, TCSADRAIN, &tmp_tc);
372 }
373
374 static void telnet_start(char *hostname, int port)
375 {
376     struct hostent *host = 0;
377         struct sockaddr_in addr;
378     int s, c;
379         fd_set rfds, wfds;
380         
381         memset(&addr, 0, sizeof(addr));
382         host = gethostbyname(hostname);
383         if (!host) {
384                 fprintf(stderr, "Unknown host: %s\n", hostname);
385                 return;
386         }
387         addr.sin_family = host->h_addrtype;
388         memcpy(&addr.sin_addr, host->h_addr, sizeof(addr.sin_addr));
389         addr.sin_port = htons(port);
390
391         printf("Trying %s...\n", inet_ntoa(addr.sin_addr));
392         
393         if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) PERROR("socket");
394         if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
395             PERROR("connect");
396         printf("Connected to %s\n", hostname);
397         printf("Escape character is ^]\n");
398
399         signal(SIGINT, sighandler);
400         signal(SIGQUIT, sighandler);
401         signal(SIGPIPE, sighandler);
402         signal(SIGWINCH, sighandler);
403
404         /* make inputs nonblocking */
405         c = 1;
406         ioctl(s, FIONBIO, &c);
407         ioctl(STDIN, FIONBIO, &c);
408         
409         if (port == TELNETPORT) telnet_setoptions(s);
410         
411         /* shuttle data back and forth between tty and socket */
412         while (1) {
413                 FD_ZERO(&rfds);
414                 FD_ZERO(&wfds);
415         
416                 FD_SET(s, &rfds);
417                 /* FD_SET(s, &wfds); */
418                 FD_SET(STDIN, &rfds);
419                 /* FD_SET(STDOUT, &wfds); */
420         
421                 if ((c = select(s+1, &rfds, &wfds, 0, 0))) {
422                         if (c == -1) {
423                             /* handle errors */
424                                 PERROR("select");
425                         }
426                         if (FD_ISSET(s, &rfds)) {
427                                 /* input from network */
428                                 FD_CLR(s, &rfds);
429                                 c = telnet_recv(s, STDOUT);
430                                 if (c == 0) break;
431                                 if (c < 0) PERROR("telnet_recv");
432                         }
433                         if (FD_ISSET(STDIN, &rfds)) {
434                                 /* input from tty */
435                                 FD_CLR(STDIN, &rfds);
436                                 c = telnet_send(STDIN, s);
437                                 if (c == 0) break;
438                                 if (c < 0) PERROR("telnet_send");
439                         }
440                 }
441         }
442         
443     return;
444 }
445
446 static void telnet_shutdown(void)
447 {
448         printf("\n");
449         tcsetattr(STDIN, TCSANOW, &saved_tc);
450 }
451
452 #ifdef STANDALONE_TELNET
453 void usage(const char *msg)
454 {
455         printf("%s", msg);
456         exit(0);
457 }
458
459 int main(int argc, char **argv)
460 #else
461 int telnet_main(int argc, char **argv)
462 #endif
463 {
464     int port = TELNETPORT;
465         
466     argc--; argv++;
467     if (argc < 1) usage(telnet_usage);
468     if (argc > 1) port = atoi(argv[1]);
469     telnet_init();
470     atexit(telnet_shutdown);
471
472     telnet_start(argv[0], port);
473     return 0;
474 }
475
476 /*
477  * Copyright (c) 1988, 1990 Regents of the University of California.
478  * All rights reserved.
479  *
480  * Redistribution and use in source and binary forms, with or without
481  * modification, are permitted provided that the following conditions
482  * are met:
483  * 1. Redistributions of source code must retain the above copyright
484  *    notice, this list of conditions and the following disclaimer.
485  * 2. Redistributions in binary form must reproduce the above copyright
486  *    notice, this list of conditions and the following disclaimer in the
487  *    documentation and/or other materials provided with the distribution.
488  * 3. All advertising materials mentioning features or use of this software
489  *    must display the following acknowledgement:
490  *      This product includes software developed by the University of
491  *      California, Berkeley and its contributors.
492  * 4. Neither the name of the University nor the names of its contributors
493  *    may be used to endorse or promote products derived from this software
494  *    without specific prior written permission.
495  *
496  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
497  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
498  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
499  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
500  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
501  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
502  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
503  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
504  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
505  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
506  * SUCH DAMAGE.
507  */