blob: 076728fb02e10bbd9d1b4b86f038e31dc71b3ca9 [file] [log] [blame]
Erik Andersenf7c49ef2000-02-22 17:17:45 +00001/* 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
48static int STDIN = 0;
49static int STDOUT = 1;
50static const char *telnet_usage = "telnet host [port]\n\n";
51static struct termios saved_tc;
52static unsigned char options[NTELOPTS];
53static char tr_state = 0; /* telnet send and receive state */
54static unsigned char subbuffer[256];
55static 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 */
88static void telnet_init(void);
89static void telnet_start(char *host, int port);
90static 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
97static inline void telnet_sendwill(int s, int c) { SENDCOMMAND(WILL, c); }
98static inline void telnet_sendwont(int s, int c) { SENDCOMMAND(WONT, c); }
99static inline void telnet_senddo(int s, int c) { SENDCOMMAND(DO, c); }
100static inline void telnet_senddont(int s, int c) { SENDCOMMAND(DONT, c); }
101
102static 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
118static 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
202static void sighandler(int sig)
203{
204 telnet_shutdown();
205 exit(0);
206}
207
208static 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
226static 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/* ******************************************************************* */
338static 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
370static 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
442static void telnet_shutdown(void)
443{
444 printf("\n");
445 tcsetattr(STDIN, TCSANOW, &saved_tc);
446}
447
448#ifdef STANDALONE_TELNET
449void usage(const char *msg)
450{
451 printf("%s", msg);
452 exit(0);
453}
454
455int main(int argc, char **argv)
456#else
457int 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 */