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