OSDN Git Service

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