blob: 8b6d5f5dc28a1232ea4e52af0b5bd7ea23528f76 [file] [log] [blame]
Erik Andersenf7c49ef2000-02-22 17:17:45 +00001/*
Erik Andersen7ab9c7e2000-05-12 19:41:47 +00002 * $Id: telnet.c,v 1.3 2000/05/12 19:41:47 erik Exp $
Erik Andersenf7c49ef2000-02-22 17:17:45 +00003 * 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
47static int STDIN = 0;
48static int STDOUT = 1;
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000049static 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 ;
Erik Andersenf7c49ef2000-02-22 17:17:45 +000055static struct termios saved_tc;
56static unsigned char options[NTELOPTS];
57static char tr_state = 0; /* telnet send and receive state */
58static unsigned char subbuffer[256];
59static 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 */
92static void telnet_init(void);
93static void telnet_start(char *host, int port);
94static 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
101static inline void telnet_sendwill(int s, int c) { SENDCOMMAND(WILL, c); }
102static inline void telnet_sendwont(int s, int c) { SENDCOMMAND(WONT, c); }
103static inline void telnet_senddo(int s, int c) { SENDCOMMAND(DO, c); }
104static inline void telnet_senddont(int s, int c) { SENDCOMMAND(DONT, c); }
105
106static void telnet_setoptions(int s)
107{
108 /*
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000109 telnet_sendwill(s, TELOPT_NAWS); TFLAG_SET(TELOPT_NAWS, WILL);
110 telnet_sendwill(s, TELOPT_TSPEED); TFLAG_SET(TELOPT_TSPEED, WILL);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000111 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 */
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000115 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);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000118 telnet_senddo(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, DO);
119 telnet_sendwill(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, WILL);
120}
121
122static 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
206static void sighandler(int sig)
207{
208 telnet_shutdown();
209 exit(0);
210}
211
212static 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
230static 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/* ******************************************************************* */
342static 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
374static 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
446static void telnet_shutdown(void)
447{
448 printf("\n");
449 tcsetattr(STDIN, TCSANOW, &saved_tc);
450}
451
452#ifdef STANDALONE_TELNET
453void usage(const char *msg)
454{
455 printf("%s", msg);
456 exit(0);
457}
458
459int main(int argc, char **argv)
460#else
461int telnet_main(int argc, char **argv)
462#endif
463{
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000464 int port = TELNETPORT;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000465
466 argc--; argv++;
467 if (argc < 1) usage(telnet_usage);
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000468 if (argc > 1) port = atoi(argv[1]);
469 telnet_init();
470 atexit(telnet_shutdown);
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000471
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000472 telnet_start(argv[0], port);
473 return 0;
Erik Andersenf7c49ef2000-02-22 17:17:45 +0000474}
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 */