summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2000-06-14 20:42:57 +0000
committerEric Andersen <andersen@codepoet.org>2000-06-14 20:42:57 +0000
commit28c70b3a16c17319c184a90eb8f164527c98b4f8 (patch)
tree672d4d34162d111ef8166bfdb45d3cad28545bd4
parente97da4007913bd29d8a18d038de29d9d4549163d (diff)
downloadbusybox-w32-28c70b3a16c17319c184a90eb8f164527c98b4f8.tar.gz
busybox-w32-28c70b3a16c17319c184a90eb8f164527c98b4f8.tar.bz2
busybox-w32-28c70b3a16c17319c184a90eb8f164527c98b4f8.zip
Integrate Tomi Ollila's telnet. Costs 3k. :)
-Erik
-rw-r--r--Changelog2
-rw-r--r--TODO1
-rw-r--r--busybox.def.h1
-rw-r--r--networking/telnet.c1026
-rw-r--r--telnet.c1026
-rw-r--r--tests/cp_tests.mk133
6 files changed, 1274 insertions, 915 deletions
diff --git a/Changelog b/Changelog
index bb3cc4aa3..d5c72684e 100644
--- a/Changelog
+++ b/Changelog
@@ -17,6 +17,8 @@
17 * Added 'grep -v' option (inverted search) and updated 17 * Added 'grep -v' option (inverted search) and updated
18 docs accordingly. -beppu 18 docs accordingly. -beppu
19 * Wrote which 19 * Wrote which
20 * Replaced the telnet implementation with one written by Tomi Ollila <too@iki.fi>
21 It works great and costs 3k.
20 * BusyBox sh (lash) now supports being used as a standalone shell. When 22 * BusyBox sh (lash) now supports being used as a standalone shell. When
21 BB_FEATURE_STANDALONE_SHELL is defined, all the busybox commands may 23 BB_FEATURE_STANDALONE_SHELL is defined, all the busybox commands may
22 be invoked as shell internals. Best used when compiling staticly 24 be invoked as shell internals. Best used when compiling staticly
diff --git a/TODO b/TODO
index cf19e4761..d36414cc5 100644
--- a/TODO
+++ b/TODO
@@ -18,7 +18,6 @@ around to it some time. If you have any good ideas, please let me know.
18 18
19Bugs that need fixing before the 0.44 release goes out the door: 19Bugs that need fixing before the 0.44 release goes out the door:
20 20
21 - merge telnet client from Tomi Ollila
22 - "more" doesn't accept " " to scroll by one page when BB_FEATURE_USE_TERMIOS 21 - "more" doesn't accept " " to scroll by one page when BB_FEATURE_USE_TERMIOS
23 is not on. 22 is not on.
24 - doc / website updates to tell what the real mailing list is for busybox. 23 - doc / website updates to tell what the real mailing list is for busybox.
diff --git a/busybox.def.h b/busybox.def.h
index 50ad88ce0..3556b1e24 100644
--- a/busybox.def.h
+++ b/busybox.def.h
@@ -89,7 +89,6 @@
89#define BB_TAR 89#define BB_TAR
90#define BB_TEE 90#define BB_TEE
91#define BB_TEST 91#define BB_TEST
92// Don't bother turning BB_TELNET on. It doesn't work properly yet.
93//#define BB_TELNET 92//#define BB_TELNET
94#define BB_TOUCH 93#define BB_TOUCH
95#define BB_TR 94#define BB_TR
diff --git a/networking/telnet.c b/networking/telnet.c
index 8b6d5f5dc..f27603527 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -1,8 +1,12 @@
1/* vi: set sw=4 ts=4: */
1/* 2/*
2 * $Id: telnet.c,v 1.3 2000/05/12 19:41:47 erik Exp $ 3 * telnet implementation for busybox
3 * Mini telnet implementation for busybox
4 * 4 *
5 * Copyright (C) 2000 by Randolph Chung <tausq@debian.org> 5 * Author: Tomi Ollila <too@iki.fi>
6 * Copyright (C) 1994-2000 by Tomi Ollila
7 *
8 * Created: Thu Apr 7 13:29:41 1994 too
9 * Last modified: Fri Jun 9 14:34:24 2000 too
6 * 10 *
7 * This program is free software; you can redistribute it and/or modify 11 * 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 12 * it under the terms of the GNU General Public License as published by
@@ -18,490 +22,652 @@
18 * along with this program; if not, write to the Free Software 22 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * 24 *
21 * This version of telnet is adapted (but very heavily modified) from the 25 * HISTORY
22 * telnet in netkit-telnet 0.16, which is: 26 * Revision 3.1 1994/04/17 11:31:54 too
23 * 27 * initial revision
24 * Copyright (c) 1989 The Regents of the University of California. 28 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen
25 * All rights reserved. 29 * <andersen@lineo.com>
26 * 30 *
27 * Original copyright notice is retained at the end of this file.
28 */ 31 */
29 32
33
30#include "internal.h" 34#include "internal.h"
31#include <stdio.h> 35#include <termios.h>
32#include <stdlib.h>
33#include <unistd.h> 36#include <unistd.h>
34#include <ctype.h>
35#include <signal.h>
36#include <errno.h> 37#include <errno.h>
37#include <netdb.h> 38#include <stdlib.h>
38#include <termios.h> 39#include <stdarg.h>
39#include <netinet/in.h> 40#include <string.h>
41#include <signal.h>
42#include <arpa/telnet.h>
40#include <sys/types.h> 43#include <sys/types.h>
41#include <sys/socket.h> 44#include <sys/socket.h>
42#include <sys/ioctl.h> 45#include <netinet/in.h>
43#define TELOPTS 46#include <netdb.h>
44#include <arpa/telnet.h> 47
45#include <arpa/inet.h> 48#if 0
49#define DOTRACE 1
50#endif
51
52#if DOTRACE
53#include <arpa/inet.h> /* for inet_ntoa()... */
54#define TRACE(x, y) do { if (x) printf y; } while (0)
55#else
56#define TRACE(x, y)
57#endif
58
59#if 0
60#define USE_POLL
61#include <sys/poll.h>
62#else
63#include <sys/time.h>
64#endif
65
66#define DATABUFSIZE 128
67#define IACBUFSIZE 128
46 68
47static int STDIN = 0; 69#define CHM_TRY 0
48static int STDOUT = 1; 70#define CHM_ON 1
49static const char *telnet_usage = "telnet host [port]\n" 71#define CHM_OFF 2
72
73#define UF_ECHO 0x01
74#define UF_SGA 0x02
75
76#define TS_0 1
77#define TS_IAC 2
78#define TS_OPT 3
79#define TS_SUB1 4
80#define TS_SUB2 5
81
82#define WriteCS(fd, str) write(fd, str, sizeof str -1)
83
84typedef unsigned char byte;
85
86/* use globals to reduce size ??? */ /* test this hypothesis later */
87struct Globalvars {
88 int netfd; /* console fd:s are 0 and 1 (and 2) */
89 /* same buffer used both for network and console read/write */
90 char * buf; /* allocating so static size is smaller */
91 short len;
92 byte telstate; /* telnet negotiation state from network input */
93 byte telwish; /* DO, DONT, WILL, WONT */
94 byte charmode;
95 byte telflags;
96 byte gotsig;
97 /* buffer to handle telnet negotiations */
98 char * iacbuf;
99 short iaclen; /* could even use byte */
100 struct termios termios_def;
101 struct termios termios_raw;
102} G;
103
104#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
105
106#ifdef USE_GLOBALVAR_PTR
107struct Globalvars * Gptr;
108#define G (*Gptr)
109#else
110struct Globalvars G;
111#endif
112
113static inline void iacflush()
114{
115 write(G.netfd, G.iacbuf, G.iaclen);
116 G.iaclen = 0;
117}
118
119/* Function prototypes */
120static int getport(char * p);
121static struct in_addr getserver(char * p);
122static int create_socket();
123static void setup_sockaddr_in(struct sockaddr_in * addr, int port);
124static int remote_connect(struct in_addr addr, int port);
125static void rawmode();
126static void cookmode();
127static void do_linemode();
128static void will_charmode();
129static void telopt(byte c);
130static int subneg(byte c);
131#if 0
132static int local_bind(int port);
133#endif
134
135/* Some globals */
136static int one = 1;
137static const char telnet_usage[] =
138 "telnet host [port]\n"
50#ifndef BB_FEATURE_TRIVIAL_HELP 139#ifndef BB_FEATURE_TRIVIAL_HELP
51 "\nProvides interactive communication with another\n" 140 "\nTelnet is used to establish interactive communication with another\n"
52 "networked host using the TELNET protocol\n" 141 "computer over a network using the TELNET protocol.\n"
53#endif 142#endif
54 ; 143 ;
55static struct termios saved_tc; 144
56static unsigned char options[NTELOPTS]; 145
57static char tr_state = 0; /* telnet send and receive state */ 146static void doexit(int ev)
58static unsigned char subbuffer[256]; 147{
59static unsigned char *subpointer, *subend; 148 cookmode();
60#define SB_CLEAR() subpointer = subbuffer; 149 exit(ev);
61#define SB_TERM() { subend = subpointer; SB_CLEAR(); } 150}
62#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { *subpointer++ = (c); } 151
63#define SB_GET() (*subpointer++) 152static void conescape()
64#define SB_PEEK() (*subpointer) 153{
65#define SB_EOF() (subpointer >= subend) 154 char b;
66#define SB_LEN() (subend - subpointer) 155
67 156 if (G.gotsig) /* came from line mode... go raw */
68#define TELNETPORT 23 157 rawmode();
69#define MASK_WILL 0x01 158
70#define MASK_WONT 0x04 159 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
71#define MASK_DO 0x10 160 " l go to line mode\r\n"
72#define MASK_DONT 0x40 161 " c go to character mode\r\n"
73 162 " z suspend telnet\r\n"
74#define TFLAG_ISSET(opt, flag) (options[opt] & MASK_##flag) 163 " e exit telnet\r\n");
75#define TFLAG_SET(opt, flag) (options[opt] |= MASK_##flag) 164
76#define TFLAG_CLR(opt, flag) (options[opt] &= ~MASK_##flag) 165 if (read(0, &b, 1) <= 0)
77 166 doexit(1);
78#define PERROR(ctx) do { fprintf(stderr, "%s: %s\n", ctx, strerror(errno)); \ 167
79 return; } while (0) 168 switch (b)
80 169 {
81#define TS_DATA 0 170 case 'l':
82#define TS_IAC 1 171 if (!G.gotsig)
83#define TS_WILL 2 172 {
84#define TS_WONT 3 173 do_linemode();
85#define TS_DO 4 174 goto rrturn;
86#define TS_DONT 5 175 }
87#define TS_CR 6 176 break;
88#define TS_SB 7 /* sub-option collection */ 177 case 'c':
89#define TS_SE 8 /* looking for sub-option end */ 178 if (G.gotsig)
90 179 {
91/* prototypes */ 180 will_charmode();
92static void telnet_init(void); 181 goto rrturn;
93static void telnet_start(char *host, int port); 182 }
94static void telnet_shutdown(void); 183 break;
95/* ******************************************************************* */ 184 case 'z':
96#define SENDCOMMAND(c,o) \ 185 cookmode();
97 char buf[3]; \ 186 kill(0, SIGTSTP);
98 buf[0] = IAC; buf[1] = c; buf[2] = o; \ 187 rawmode();
99 write(s, buf, sizeof(buf)); 188 break;
100 189 case 'e':
101static inline void telnet_sendwill(int s, int c) { SENDCOMMAND(WILL, c); } 190 doexit(0);
102static inline void telnet_sendwont(int s, int c) { SENDCOMMAND(WONT, c); } 191 }
103static inline void telnet_senddo(int s, int c) { SENDCOMMAND(DO, c); } 192
104static inline void telnet_senddont(int s, int c) { SENDCOMMAND(DONT, c); } 193 WriteCS(1, "continuing...\r\n");
105 194
106static void telnet_setoptions(int s) 195 if (G.gotsig)
196 cookmode();
197
198 rrturn:
199 G.gotsig = 0;
200
201}
202static void handlenetoutput()
107{ 203{
108 /* 204 /* here we could do smart tricks how to handle 0xFF:s in output
109 telnet_sendwill(s, TELOPT_NAWS); TFLAG_SET(TELOPT_NAWS, WILL); 205 * stream like writing twice every sequence of FF:s (thus doing
110 telnet_sendwill(s, TELOPT_TSPEED); TFLAG_SET(TELOPT_TSPEED, WILL); 206 * many write()s. But I think interactive telnet application does
111 telnet_sendwill(s, TELOPT_NEW_ENVIRON); TFLAG_SET(TELOPT_NEW_ENVIRON, WILL); 207 * not need to be 100% 8-bit clean, so changing every 0xff:s to
112 telnet_senddo(s, TELOPT_STATUS); TFLAG_SET(TELOPT_STATUS, DO); 208 * 0x7f:s */
113 telnet_sendwill(s, TELOPT_TTYPE); TFLAG_SET(TELOPT_TTYPE, WILL); 209
114 */ 210 int i;
115 telnet_senddo(s, TELOPT_SGA); TFLAG_SET(TELOPT_SGA, DO); 211 byte * p = G.buf;
116 telnet_sendwill(s, TELOPT_LFLOW); TFLAG_SET(TELOPT_LFLOW, WILL); 212
117 telnet_sendwill(s, TELOPT_LINEMODE); TFLAG_SET(TELOPT_LINEMODE, WILL); 213 for (i = G.len; i > 0; i--, p++)
118 telnet_senddo(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, DO); 214 {
119 telnet_sendwill(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, WILL); 215 if (*p == 0x1d)
216 {
217 conescape();
218 return;
219 }
220 if (*p == 0xff)
221 *p = 0x7f;
222 }
223 write(G.netfd, G.buf, G.len);
120} 224}
121 225
122static void telnet_suboptions(int net) 226
227static void handlenetinput()
123{ 228{
124 char buf[256]; 229 int i;
125 switch (SB_GET()) { 230 int cstart = 0;
126 case TELOPT_TTYPE: 231
127 if (TFLAG_ISSET(TELOPT_TTYPE, WONT)) return; 232 for (i = 0; i < G.len; i++)
128 if (SB_EOF() || SB_GET() != TELQUAL_SEND) { 233 {
129 return; 234 byte c = G.buf[i];
130 } else { 235
131 const char *name = getenv("TERM"); 236 if (G.telstate == 0) /* most of the time state == 0 */
132 if (name) { 237 {
133 snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c", IAC, SB, 238 if (c == IAC)
134 TELOPT_TTYPE, TELQUAL_IS, name, IAC, SE); 239 {
135 write(net, buf, strlen(name)+6); 240 cstart = i;
136 } 241 G.telstate = TS_IAC;
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 } 242 }
200 break; 243 }
201 default: 244 else
202 break; 245 switch (G.telstate)
246 {
247 case TS_0:
248 if (c == IAC)
249 G.telstate = TS_IAC;
250 else
251 G.buf[cstart++] = c;
252 break;
253
254 case TS_IAC:
255 if (c == IAC) /* IAC IAC -> 0xFF */
256 {
257 G.buf[cstart++] = c;
258 G.telstate = TS_0;
259 break;
260 }
261 /* else */
262 switch (c)
263 {
264 case SB:
265 G.telstate = TS_SUB1;
266 break;
267 case DO:
268 case DONT:
269 case WILL:
270 case WONT:
271 G.telwish = c;
272 G.telstate = TS_OPT;
273 break;
274 default:
275 G.telstate = TS_0; /* DATA MARK must be added later */
276 }
277 break;
278 case TS_OPT: /* WILL, WONT, DO, DONT */
279 telopt(c);
280 G.telstate = TS_0;
281 break;
282 case TS_SUB1: /* Subnegotiation */
283 case TS_SUB2: /* Subnegotiation */
284 if (subneg(c) == TRUE)
285 G.telstate = TS_0;
286 break;
287 }
203 } 288 }
289 if (G.telstate)
290 {
291 if (G.iaclen) iacflush();
292 if (G.telstate == TS_0) G.telstate = 0;
293
294 G.len = cstart;
295 }
296
297 if (G.len)
298 write(1, G.buf, G.len);
204} 299}
205 300
206static void sighandler(int sig) 301
302/* ******************************* */
303
304static inline void putiac(int c)
207{ 305{
208 telnet_shutdown(); 306 G.iacbuf[G.iaclen++] = c;
209 exit(0);
210} 307}
211 308
212static int telnet_send(int tty, int net) 309
310static void putiac2(byte wwdd, byte c)
213{ 311{
214 int ret; 312 if (G.iaclen + 3 > IACBUFSIZE)
215 unsigned char ch; 313 iacflush();
216 314
217 while ((ret = read(tty, &ch, 1)) > 0) { 315 putiac(IAC);
218 if (ch == 29) { /* 29 -- ctrl-] */ 316 putiac(wwdd);
219 /* do something here? */ 317 putiac(c);
220 exit(0); 318}
221 } else { 319
222 ret = write(net, &ch, 1); 320#if 0
223 break; 321static void putiac1(byte c)
322{
323 if (G.iaclen + 2 > IACBUFSIZE)
324 iacflush();
325
326 putiac(IAC);
327 putiac(c);
328}
329#endif
330
331/* void putiacstring (subneg strings) */
332
333/* ******************************* */
334
335char const escapecharis[] = "\r\nEscape character is ";
336
337static void setConMode()
338{
339 if (G.telflags & UF_ECHO)
340 {
341 if (G.charmode == CHM_TRY) {
342 G.charmode = CHM_ON;
343 fprintf(stdout, "\r\nEntering character mode%s'^]'.\r\n", escapecharis);
344 rawmode();
345 }
346 }
347 else
348 {
349 if (G.charmode != CHM_OFF) {
350 G.charmode = CHM_OFF;
351 fprintf(stdout, "\r\nEntering line mode%s'^C'.\r\n", escapecharis);
352 cookmode();
224 } 353 }
225 } 354 }
226 if (ret == -1 && errno == EWOULDBLOCK) return 1;
227 return ret;
228} 355}
229 356
230static int telnet_recv(int net, int tty) 357/* ******************************* */
358
359static void will_charmode()
231{ 360{
232 /* globals: tr_state - telnet receive state */ 361 G.charmode = CHM_TRY;
233 int ret, c = 0; 362 G.telflags |= (UF_ECHO | UF_SGA);
234 unsigned char ch; 363 setConMode();
235 364
236 while ((ret = read(net, &ch, 1)) > 0) { 365 putiac2(DO, TELOPT_ECHO);
237 c = ch; 366 putiac2(DO, TELOPT_SGA);
238 /* printf("%02X ", c); fflush(stdout); */ 367 iacflush();
239 switch (tr_state) { 368}
240 case TS_DATA: 369
241 if (c == IAC) { 370static void do_linemode()
242 tr_state = TS_IAC; 371{
243 break; 372 G.charmode = CHM_TRY;
244 } else { 373 G.telflags &= ~(UF_ECHO | UF_SGA);
245 write(tty, &c, 1); 374 setConMode();
246 } 375
247 break; 376 putiac2(DONT, TELOPT_ECHO);
248 case TS_IAC: 377 putiac2(DONT, TELOPT_SGA);
249 switch (c) { 378 iacflush();
250 case WILL: 379}
251 tr_state = TS_WILL; break; 380
252 case WONT: 381/* ******************************* */
253 tr_state = TS_WONT; break; 382
254 case DO: 383static inline void to_notsup(char c)
255 tr_state = TS_DO; break; 384{
256 case DONT: 385 if (G.telwish == WILL) putiac2(DONT, c);
257 tr_state = TS_DONT; break; 386 else if (G.telwish == DO) putiac2(WONT, c);
258 case SB: 387}
259 SB_CLEAR(); 388
260 tr_state = TS_SB; break; 389static inline void to_echo()
261 case IAC: 390{
262 write(tty, &c, 1); /* fallthrough */ 391 /* if server requests ECHO, don't agree */
263 default: 392 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
264 tr_state = TS_DATA; 393 else if (G.telwish == DONT) return;
265 } 394
266 395 if (G.telflags & UF_ECHO)
267 /* subnegotiation -- ignored for now */ 396 {
268 case TS_SB: 397 if (G.telwish == WILL)
269 if (c == IAC) tr_state = TS_SE; 398 return;
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 } 399 }
337 if (ret == -1 && errno == EWOULDBLOCK) return 1; 400 else
338 return ret; 401 if (G.telwish == WONT)
402 return;
403
404 if (G.charmode != CHM_OFF)
405 G.telflags ^= UF_ECHO;
406
407 if (G.telflags & UF_ECHO)
408 putiac2(DO, TELOPT_ECHO);
409 else
410 putiac2(DONT, TELOPT_ECHO);
411
412 setConMode();
413 WriteCS(1, "\r\n"); /* sudden modec */
339} 414}
340 415
341/* ******************************************************************* */ 416static inline void to_sga()
342static void telnet_init(void)
343{ 417{
344 struct termios tmp_tc; 418 /* daemon always sends will/wont, client do/dont */
345 cc_t esc = (']' & 0x1f); /* ctrl-] */ 419
346 420 if (G.telflags & UF_SGA)
347 memset(options, 0, sizeof(options)); 421 {
348 SB_CLEAR(); 422 if (G.telwish == WILL)
349 423 return;
350 tcgetattr(STDIN, &saved_tc); 424 }
351 425 else
352 tmp_tc = saved_tc; 426 if (G.telwish == WONT)
353 tmp_tc.c_lflag &= ~ECHO; /* echo */ 427 return;
354 tmp_tc.c_oflag |= ONLCR; /* NL->CRLF translation */ 428
355 tmp_tc.c_iflag |= ICRNL; 429 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
356 tmp_tc.c_iflag &= ~(IXANY|IXOFF|IXON); /* no flow control */ 430 putiac2(DO, TELOPT_SGA);
357 tmp_tc.c_lflag |= ISIG; /* trap signals */ 431 else
358 tmp_tc.c_lflag &= ~ICANON; /* edit mode */ 432 putiac2(DONT, TELOPT_SGA);
359 433
360 /* misc settings, compat with default telnet stuff */ 434 return;
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} 435}
373 436
374static void telnet_start(char *hostname, int port) 437static void telopt(byte c)
375{ 438{
376 struct hostent *host = 0; 439 switch (c)
377 struct sockaddr_in addr; 440 {
378 int s, c; 441 case TELOPT_ECHO: to_echo(c); break;
379 fd_set rfds, wfds; 442 case TELOPT_SGA: to_sga(c); break;
380 443 default: to_notsup(c); break;
381 memset(&addr, 0, sizeof(addr));
382 host = gethostbyname(hostname);
383 if (!host) {
384 fprintf(stderr, "Unknown host: %s\n", hostname);
385 return;
386 } 444 }
387 addr.sin_family = host->h_addrtype; 445}
388 memcpy(&addr.sin_addr, host->h_addr, sizeof(addr.sin_addr)); 446
389 addr.sin_port = htons(port); 447
448/* ******************************* */
390 449
391 printf("Trying %s...\n", inet_ntoa(addr.sin_addr)); 450/* subnegotiation -- ignore all */
451
452static int subneg(byte c)
453{
454 switch (G.telstate)
455 {
456 case TS_SUB1:
457 if (c == IAC)
458 G.telstate = TS_SUB2;
459 break;
460 case TS_SUB2:
461 if (c == SE)
462 return TRUE;
463 G.telstate = TS_SUB1;
464 /* break; */
465 }
466 return FALSE;
467}
468
469/* ******************************* */
470
471static void fgotsig(int sig)
472{
473 G.gotsig = sig;
474}
475
476
477static void rawmode()
478{
479 tcsetattr(0, TCSADRAIN, &G.termios_raw);
480}
481
482static void cookmode()
483{
484 tcsetattr(0, TCSADRAIN, &G.termios_def);
485}
486
487extern int telnet_main(int argc, char** argv)
488{
489 struct in_addr host;
490 int port;
491#ifdef USE_POLL
492 struct pollfd ufds[2];
493#else
494 fd_set readfds;
495 int maxfd;
496#endif
497
498
499 memset(&G, 0, sizeof G);
500
501 if (tcgetattr(0, &G.termios_def) < 0)
502 exit(1);
392 503
393 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) PERROR("socket"); 504 G.termios_raw = G.termios_def;
394 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) 505
395 PERROR("connect"); 506 cfmakeraw(&G.termios_raw);
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 507
409 if (port == TELNETPORT) telnet_setoptions(s); 508 if (argc < 2) usage(telnet_usage);
509 port = (argc > 2)? getport(argv[2]): 23;
410 510
411 /* shuttle data back and forth between tty and socket */ 511 G.buf = xmalloc(DATABUFSIZE);
412 while (1) { 512 G.iacbuf = xmalloc(IACBUFSIZE);
413 FD_ZERO(&rfds);
414 FD_ZERO(&wfds);
415 513
416 FD_SET(s, &rfds); 514 host = getserver(argv[1]);
417 /* FD_SET(s, &wfds); */ 515
418 FD_SET(STDIN, &rfds); 516 G.netfd = remote_connect(host, port);
419 /* FD_SET(STDOUT, &wfds); */ 517
518 signal(SIGINT, fgotsig);
519
520#ifdef USE_POLL
521 ufds[0].fd = 0; ufds[1].fd = G.netfd;
522 ufds[0].events = ufds[1].events = POLLIN;
523#else
524 FD_ZERO(&readfds);
525 FD_SET(0, &readfds);
526 FD_SET(G.netfd, &readfds);
527 maxfd = G.netfd + 1;
528#endif
420 529
421 if ((c = select(s+1, &rfds, &wfds, 0, 0))) { 530 while (1)
422 if (c == -1) { 531 {
423 /* handle errors */ 532#ifndef USE_POLL
424 PERROR("select"); 533 fd_set rfds = readfds;
425 } 534
426 if (FD_ISSET(s, &rfds)) { 535 switch (select(maxfd, &rfds, NULL, NULL, NULL))
427 /* input from network */ 536#else
428 FD_CLR(s, &rfds); 537 switch (poll(ufds, 2, -1))
429 c = telnet_recv(s, STDOUT); 538#endif
430 if (c == 0) break; 539 {
431 if (c < 0) PERROR("telnet_recv"); 540 case 0:
541 /* timeout */
542 case -1:
543 /* error, ignore and/or log something, bay go to loop */
544 if (G.gotsig)
545 conescape();
546 else
547 sleep(1);
548 break;
549 default:
550
551#ifdef USE_POLL
552 if (ufds[0].revents) /* well, should check POLLIN, but ... */
553#else
554 if (FD_ISSET(0, &rfds))
555#endif
556 {
557 G.len = read(0, G.buf, DATABUFSIZE);
558
559 if (G.len <= 0)
560 doexit(0);
561
562 TRACE(0, ("Read con: %d\n", G.len));
563
564 handlenetoutput();
432 } 565 }
433 if (FD_ISSET(STDIN, &rfds)) { 566
434 /* input from tty */ 567#ifdef USE_POLL
435 FD_CLR(STDIN, &rfds); 568 if (ufds[1].revents) /* well, should check POLLIN, but ... */
436 c = telnet_send(STDIN, s); 569#else
437 if (c == 0) break; 570 if (FD_ISSET(G.netfd, &rfds))
438 if (c < 0) PERROR("telnet_send"); 571#endif
572 {
573 G.len = read(G.netfd, G.buf, DATABUFSIZE);
574
575 if (G.len <= 0)
576 {
577 WriteCS(1, "Connection closed by foreign host.\r\n");
578 doexit(1);
579 }
580 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, G.len));
581
582 handlenetinput();
439 } 583 }
440 } 584 }
441 } 585 }
442
443 return;
444} 586}
445 587
446static void telnet_shutdown(void) 588static int getport(char * p)
447{ 589{
448 printf("\n"); 590 unsigned int port = atoi(p);
449 tcsetattr(STDIN, TCSANOW, &saved_tc); 591
592 if ((unsigned)(port - 1 ) > 65534)
593 {
594 fatalError("%s: bad port number\n", p);
595 }
596 return port;
450} 597}
451 598
452#ifdef STANDALONE_TELNET 599static struct in_addr getserver(char * host)
453void usage(const char *msg)
454{ 600{
455 printf("%s", msg); 601 struct in_addr addr;
456 exit(0); 602
603 struct hostent * he;
604 if ((he = gethostbyname(host)) == NULL)
605 {
606 fatalError("%s: Unkonwn host\n", host);
607 }
608 memcpy(&addr, he->h_addr, sizeof addr);
609
610 TRACE(1, ("addr: %s\n", inet_ntoa(addr)));
611
612 return addr;
613}
614
615static int create_socket()
616{
617 return socket(AF_INET, SOCK_STREAM, 0);
457} 618}
458 619
459int main(int argc, char **argv) 620static void setup_sockaddr_in(struct sockaddr_in * addr, int port)
460#else 621{
461int telnet_main(int argc, char **argv) 622 memset(addr, 0, sizeof addr);
462#endif 623 addr->sin_family = AF_INET;
624 addr->sin_port = htons(port);
625}
626
627#if 0
628static int local_bind(int port)
463{ 629{
464 int port = TELNETPORT; 630 struct sockaddr_in s_addr;
631 int s = create_socket();
632
633 setup_sockaddr_in(&s_addr, port);
634
635 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
636
637 if (bind(s, &s_addr, sizeof s_addr) < 0)
638 {
639 char * e = sys_errlist[errno];
640 syserrorexit("bind");
641 exit(1);
642 }
643 listen(s, 1);
465 644
466 argc--; argv++; 645 return s;
467 if (argc < 1) usage(telnet_usage); 646}
468 if (argc > 1) port = atoi(argv[1]); 647#endif
469 telnet_init(); 648
470 atexit(telnet_shutdown); 649static int remote_connect(struct in_addr addr, int port)
471 650{
472 telnet_start(argv[0], port); 651 struct sockaddr_in s_addr;
473 return 0; 652 int s = create_socket();
653
654 setup_sockaddr_in(&s_addr, port);
655 s_addr.sin_addr = addr;
656
657 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
658
659 if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0)
660 {
661 fatalError("Unable to connect to remote host: %s\n", strerror(errno));
662 }
663 return s;
474} 664}
475 665
476/* 666/*
477 * Copyright (c) 1988, 1990 Regents of the University of California. 667Local Variables:
478 * All rights reserved. 668c-file-style: "linux"
479 * 669c-basic-offset: 4
480 * Redistribution and use in source and binary forms, with or without 670tab-width: 4
481 * modification, are permitted provided that the following conditions 671End:
482 * are met: 672*/
483 * 1. Redistributions of source code must retain the above copyright 673
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 */
diff --git a/telnet.c b/telnet.c
index 8b6d5f5dc..f27603527 100644
--- a/telnet.c
+++ b/telnet.c
@@ -1,8 +1,12 @@
1/* vi: set sw=4 ts=4: */
1/* 2/*
2 * $Id: telnet.c,v 1.3 2000/05/12 19:41:47 erik Exp $ 3 * telnet implementation for busybox
3 * Mini telnet implementation for busybox
4 * 4 *
5 * Copyright (C) 2000 by Randolph Chung <tausq@debian.org> 5 * Author: Tomi Ollila <too@iki.fi>
6 * Copyright (C) 1994-2000 by Tomi Ollila
7 *
8 * Created: Thu Apr 7 13:29:41 1994 too
9 * Last modified: Fri Jun 9 14:34:24 2000 too
6 * 10 *
7 * This program is free software; you can redistribute it and/or modify 11 * 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 12 * it under the terms of the GNU General Public License as published by
@@ -18,490 +22,652 @@
18 * along with this program; if not, write to the Free Software 22 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * 24 *
21 * This version of telnet is adapted (but very heavily modified) from the 25 * HISTORY
22 * telnet in netkit-telnet 0.16, which is: 26 * Revision 3.1 1994/04/17 11:31:54 too
23 * 27 * initial revision
24 * Copyright (c) 1989 The Regents of the University of California. 28 * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen
25 * All rights reserved. 29 * <andersen@lineo.com>
26 * 30 *
27 * Original copyright notice is retained at the end of this file.
28 */ 31 */
29 32
33
30#include "internal.h" 34#include "internal.h"
31#include <stdio.h> 35#include <termios.h>
32#include <stdlib.h>
33#include <unistd.h> 36#include <unistd.h>
34#include <ctype.h>
35#include <signal.h>
36#include <errno.h> 37#include <errno.h>
37#include <netdb.h> 38#include <stdlib.h>
38#include <termios.h> 39#include <stdarg.h>
39#include <netinet/in.h> 40#include <string.h>
41#include <signal.h>
42#include <arpa/telnet.h>
40#include <sys/types.h> 43#include <sys/types.h>
41#include <sys/socket.h> 44#include <sys/socket.h>
42#include <sys/ioctl.h> 45#include <netinet/in.h>
43#define TELOPTS 46#include <netdb.h>
44#include <arpa/telnet.h> 47
45#include <arpa/inet.h> 48#if 0
49#define DOTRACE 1
50#endif
51
52#if DOTRACE
53#include <arpa/inet.h> /* for inet_ntoa()... */
54#define TRACE(x, y) do { if (x) printf y; } while (0)
55#else
56#define TRACE(x, y)
57#endif
58
59#if 0
60#define USE_POLL
61#include <sys/poll.h>
62#else
63#include <sys/time.h>
64#endif
65
66#define DATABUFSIZE 128
67#define IACBUFSIZE 128
46 68
47static int STDIN = 0; 69#define CHM_TRY 0
48static int STDOUT = 1; 70#define CHM_ON 1
49static const char *telnet_usage = "telnet host [port]\n" 71#define CHM_OFF 2
72
73#define UF_ECHO 0x01
74#define UF_SGA 0x02
75
76#define TS_0 1
77#define TS_IAC 2
78#define TS_OPT 3
79#define TS_SUB1 4
80#define TS_SUB2 5
81
82#define WriteCS(fd, str) write(fd, str, sizeof str -1)
83
84typedef unsigned char byte;
85
86/* use globals to reduce size ??? */ /* test this hypothesis later */
87struct Globalvars {
88 int netfd; /* console fd:s are 0 and 1 (and 2) */
89 /* same buffer used both for network and console read/write */
90 char * buf; /* allocating so static size is smaller */
91 short len;
92 byte telstate; /* telnet negotiation state from network input */
93 byte telwish; /* DO, DONT, WILL, WONT */
94 byte charmode;
95 byte telflags;
96 byte gotsig;
97 /* buffer to handle telnet negotiations */
98 char * iacbuf;
99 short iaclen; /* could even use byte */
100 struct termios termios_def;
101 struct termios termios_raw;
102} G;
103
104#define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */
105
106#ifdef USE_GLOBALVAR_PTR
107struct Globalvars * Gptr;
108#define G (*Gptr)
109#else
110struct Globalvars G;
111#endif
112
113static inline void iacflush()
114{
115 write(G.netfd, G.iacbuf, G.iaclen);
116 G.iaclen = 0;
117}
118
119/* Function prototypes */
120static int getport(char * p);
121static struct in_addr getserver(char * p);
122static int create_socket();
123static void setup_sockaddr_in(struct sockaddr_in * addr, int port);
124static int remote_connect(struct in_addr addr, int port);
125static void rawmode();
126static void cookmode();
127static void do_linemode();
128static void will_charmode();
129static void telopt(byte c);
130static int subneg(byte c);
131#if 0
132static int local_bind(int port);
133#endif
134
135/* Some globals */
136static int one = 1;
137static const char telnet_usage[] =
138 "telnet host [port]\n"
50#ifndef BB_FEATURE_TRIVIAL_HELP 139#ifndef BB_FEATURE_TRIVIAL_HELP
51 "\nProvides interactive communication with another\n" 140 "\nTelnet is used to establish interactive communication with another\n"
52 "networked host using the TELNET protocol\n" 141 "computer over a network using the TELNET protocol.\n"
53#endif 142#endif
54 ; 143 ;
55static struct termios saved_tc; 144
56static unsigned char options[NTELOPTS]; 145
57static char tr_state = 0; /* telnet send and receive state */ 146static void doexit(int ev)
58static unsigned char subbuffer[256]; 147{
59static unsigned char *subpointer, *subend; 148 cookmode();
60#define SB_CLEAR() subpointer = subbuffer; 149 exit(ev);
61#define SB_TERM() { subend = subpointer; SB_CLEAR(); } 150}
62#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { *subpointer++ = (c); } 151
63#define SB_GET() (*subpointer++) 152static void conescape()
64#define SB_PEEK() (*subpointer) 153{
65#define SB_EOF() (subpointer >= subend) 154 char b;
66#define SB_LEN() (subend - subpointer) 155
67 156 if (G.gotsig) /* came from line mode... go raw */
68#define TELNETPORT 23 157 rawmode();
69#define MASK_WILL 0x01 158
70#define MASK_WONT 0x04 159 WriteCS(1, "\r\nConsole escape. Commands are:\r\n\n"
71#define MASK_DO 0x10 160 " l go to line mode\r\n"
72#define MASK_DONT 0x40 161 " c go to character mode\r\n"
73 162 " z suspend telnet\r\n"
74#define TFLAG_ISSET(opt, flag) (options[opt] & MASK_##flag) 163 " e exit telnet\r\n");
75#define TFLAG_SET(opt, flag) (options[opt] |= MASK_##flag) 164
76#define TFLAG_CLR(opt, flag) (options[opt] &= ~MASK_##flag) 165 if (read(0, &b, 1) <= 0)
77 166 doexit(1);
78#define PERROR(ctx) do { fprintf(stderr, "%s: %s\n", ctx, strerror(errno)); \ 167
79 return; } while (0) 168 switch (b)
80 169 {
81#define TS_DATA 0 170 case 'l':
82#define TS_IAC 1 171 if (!G.gotsig)
83#define TS_WILL 2 172 {
84#define TS_WONT 3 173 do_linemode();
85#define TS_DO 4 174 goto rrturn;
86#define TS_DONT 5 175 }
87#define TS_CR 6 176 break;
88#define TS_SB 7 /* sub-option collection */ 177 case 'c':
89#define TS_SE 8 /* looking for sub-option end */ 178 if (G.gotsig)
90 179 {
91/* prototypes */ 180 will_charmode();
92static void telnet_init(void); 181 goto rrturn;
93static void telnet_start(char *host, int port); 182 }
94static void telnet_shutdown(void); 183 break;
95/* ******************************************************************* */ 184 case 'z':
96#define SENDCOMMAND(c,o) \ 185 cookmode();
97 char buf[3]; \ 186 kill(0, SIGTSTP);
98 buf[0] = IAC; buf[1] = c; buf[2] = o; \ 187 rawmode();
99 write(s, buf, sizeof(buf)); 188 break;
100 189 case 'e':
101static inline void telnet_sendwill(int s, int c) { SENDCOMMAND(WILL, c); } 190 doexit(0);
102static inline void telnet_sendwont(int s, int c) { SENDCOMMAND(WONT, c); } 191 }
103static inline void telnet_senddo(int s, int c) { SENDCOMMAND(DO, c); } 192
104static inline void telnet_senddont(int s, int c) { SENDCOMMAND(DONT, c); } 193 WriteCS(1, "continuing...\r\n");
105 194
106static void telnet_setoptions(int s) 195 if (G.gotsig)
196 cookmode();
197
198 rrturn:
199 G.gotsig = 0;
200
201}
202static void handlenetoutput()
107{ 203{
108 /* 204 /* here we could do smart tricks how to handle 0xFF:s in output
109 telnet_sendwill(s, TELOPT_NAWS); TFLAG_SET(TELOPT_NAWS, WILL); 205 * stream like writing twice every sequence of FF:s (thus doing
110 telnet_sendwill(s, TELOPT_TSPEED); TFLAG_SET(TELOPT_TSPEED, WILL); 206 * many write()s. But I think interactive telnet application does
111 telnet_sendwill(s, TELOPT_NEW_ENVIRON); TFLAG_SET(TELOPT_NEW_ENVIRON, WILL); 207 * not need to be 100% 8-bit clean, so changing every 0xff:s to
112 telnet_senddo(s, TELOPT_STATUS); TFLAG_SET(TELOPT_STATUS, DO); 208 * 0x7f:s */
113 telnet_sendwill(s, TELOPT_TTYPE); TFLAG_SET(TELOPT_TTYPE, WILL); 209
114 */ 210 int i;
115 telnet_senddo(s, TELOPT_SGA); TFLAG_SET(TELOPT_SGA, DO); 211 byte * p = G.buf;
116 telnet_sendwill(s, TELOPT_LFLOW); TFLAG_SET(TELOPT_LFLOW, WILL); 212
117 telnet_sendwill(s, TELOPT_LINEMODE); TFLAG_SET(TELOPT_LINEMODE, WILL); 213 for (i = G.len; i > 0; i--, p++)
118 telnet_senddo(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, DO); 214 {
119 telnet_sendwill(s, TELOPT_BINARY); TFLAG_SET(TELOPT_BINARY, WILL); 215 if (*p == 0x1d)
216 {
217 conescape();
218 return;
219 }
220 if (*p == 0xff)
221 *p = 0x7f;
222 }
223 write(G.netfd, G.buf, G.len);
120} 224}
121 225
122static void telnet_suboptions(int net) 226
227static void handlenetinput()
123{ 228{
124 char buf[256]; 229 int i;
125 switch (SB_GET()) { 230 int cstart = 0;
126 case TELOPT_TTYPE: 231
127 if (TFLAG_ISSET(TELOPT_TTYPE, WONT)) return; 232 for (i = 0; i < G.len; i++)
128 if (SB_EOF() || SB_GET() != TELQUAL_SEND) { 233 {
129 return; 234 byte c = G.buf[i];
130 } else { 235
131 const char *name = getenv("TERM"); 236 if (G.telstate == 0) /* most of the time state == 0 */
132 if (name) { 237 {
133 snprintf(buf, sizeof(buf), "%c%c%c%c%s%c%c", IAC, SB, 238 if (c == IAC)
134 TELOPT_TTYPE, TELQUAL_IS, name, IAC, SE); 239 {
135 write(net, buf, strlen(name)+6); 240 cstart = i;
136 } 241 G.telstate = TS_IAC;
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 } 242 }
200 break; 243 }
201 default: 244 else
202 break; 245 switch (G.telstate)
246 {
247 case TS_0:
248 if (c == IAC)
249 G.telstate = TS_IAC;
250 else
251 G.buf[cstart++] = c;
252 break;
253
254 case TS_IAC:
255 if (c == IAC) /* IAC IAC -> 0xFF */
256 {
257 G.buf[cstart++] = c;
258 G.telstate = TS_0;
259 break;
260 }
261 /* else */
262 switch (c)
263 {
264 case SB:
265 G.telstate = TS_SUB1;
266 break;
267 case DO:
268 case DONT:
269 case WILL:
270 case WONT:
271 G.telwish = c;
272 G.telstate = TS_OPT;
273 break;
274 default:
275 G.telstate = TS_0; /* DATA MARK must be added later */
276 }
277 break;
278 case TS_OPT: /* WILL, WONT, DO, DONT */
279 telopt(c);
280 G.telstate = TS_0;
281 break;
282 case TS_SUB1: /* Subnegotiation */
283 case TS_SUB2: /* Subnegotiation */
284 if (subneg(c) == TRUE)
285 G.telstate = TS_0;
286 break;
287 }
203 } 288 }
289 if (G.telstate)
290 {
291 if (G.iaclen) iacflush();
292 if (G.telstate == TS_0) G.telstate = 0;
293
294 G.len = cstart;
295 }
296
297 if (G.len)
298 write(1, G.buf, G.len);
204} 299}
205 300
206static void sighandler(int sig) 301
302/* ******************************* */
303
304static inline void putiac(int c)
207{ 305{
208 telnet_shutdown(); 306 G.iacbuf[G.iaclen++] = c;
209 exit(0);
210} 307}
211 308
212static int telnet_send(int tty, int net) 309
310static void putiac2(byte wwdd, byte c)
213{ 311{
214 int ret; 312 if (G.iaclen + 3 > IACBUFSIZE)
215 unsigned char ch; 313 iacflush();
216 314
217 while ((ret = read(tty, &ch, 1)) > 0) { 315 putiac(IAC);
218 if (ch == 29) { /* 29 -- ctrl-] */ 316 putiac(wwdd);
219 /* do something here? */ 317 putiac(c);
220 exit(0); 318}
221 } else { 319
222 ret = write(net, &ch, 1); 320#if 0
223 break; 321static void putiac1(byte c)
322{
323 if (G.iaclen + 2 > IACBUFSIZE)
324 iacflush();
325
326 putiac(IAC);
327 putiac(c);
328}
329#endif
330
331/* void putiacstring (subneg strings) */
332
333/* ******************************* */
334
335char const escapecharis[] = "\r\nEscape character is ";
336
337static void setConMode()
338{
339 if (G.telflags & UF_ECHO)
340 {
341 if (G.charmode == CHM_TRY) {
342 G.charmode = CHM_ON;
343 fprintf(stdout, "\r\nEntering character mode%s'^]'.\r\n", escapecharis);
344 rawmode();
345 }
346 }
347 else
348 {
349 if (G.charmode != CHM_OFF) {
350 G.charmode = CHM_OFF;
351 fprintf(stdout, "\r\nEntering line mode%s'^C'.\r\n", escapecharis);
352 cookmode();
224 } 353 }
225 } 354 }
226 if (ret == -1 && errno == EWOULDBLOCK) return 1;
227 return ret;
228} 355}
229 356
230static int telnet_recv(int net, int tty) 357/* ******************************* */
358
359static void will_charmode()
231{ 360{
232 /* globals: tr_state - telnet receive state */ 361 G.charmode = CHM_TRY;
233 int ret, c = 0; 362 G.telflags |= (UF_ECHO | UF_SGA);
234 unsigned char ch; 363 setConMode();
235 364
236 while ((ret = read(net, &ch, 1)) > 0) { 365 putiac2(DO, TELOPT_ECHO);
237 c = ch; 366 putiac2(DO, TELOPT_SGA);
238 /* printf("%02X ", c); fflush(stdout); */ 367 iacflush();
239 switch (tr_state) { 368}
240 case TS_DATA: 369
241 if (c == IAC) { 370static void do_linemode()
242 tr_state = TS_IAC; 371{
243 break; 372 G.charmode = CHM_TRY;
244 } else { 373 G.telflags &= ~(UF_ECHO | UF_SGA);
245 write(tty, &c, 1); 374 setConMode();
246 } 375
247 break; 376 putiac2(DONT, TELOPT_ECHO);
248 case TS_IAC: 377 putiac2(DONT, TELOPT_SGA);
249 switch (c) { 378 iacflush();
250 case WILL: 379}
251 tr_state = TS_WILL; break; 380
252 case WONT: 381/* ******************************* */
253 tr_state = TS_WONT; break; 382
254 case DO: 383static inline void to_notsup(char c)
255 tr_state = TS_DO; break; 384{
256 case DONT: 385 if (G.telwish == WILL) putiac2(DONT, c);
257 tr_state = TS_DONT; break; 386 else if (G.telwish == DO) putiac2(WONT, c);
258 case SB: 387}
259 SB_CLEAR(); 388
260 tr_state = TS_SB; break; 389static inline void to_echo()
261 case IAC: 390{
262 write(tty, &c, 1); /* fallthrough */ 391 /* if server requests ECHO, don't agree */
263 default: 392 if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; }
264 tr_state = TS_DATA; 393 else if (G.telwish == DONT) return;
265 } 394
266 395 if (G.telflags & UF_ECHO)
267 /* subnegotiation -- ignored for now */ 396 {
268 case TS_SB: 397 if (G.telwish == WILL)
269 if (c == IAC) tr_state = TS_SE; 398 return;
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 } 399 }
337 if (ret == -1 && errno == EWOULDBLOCK) return 1; 400 else
338 return ret; 401 if (G.telwish == WONT)
402 return;
403
404 if (G.charmode != CHM_OFF)
405 G.telflags ^= UF_ECHO;
406
407 if (G.telflags & UF_ECHO)
408 putiac2(DO, TELOPT_ECHO);
409 else
410 putiac2(DONT, TELOPT_ECHO);
411
412 setConMode();
413 WriteCS(1, "\r\n"); /* sudden modec */
339} 414}
340 415
341/* ******************************************************************* */ 416static inline void to_sga()
342static void telnet_init(void)
343{ 417{
344 struct termios tmp_tc; 418 /* daemon always sends will/wont, client do/dont */
345 cc_t esc = (']' & 0x1f); /* ctrl-] */ 419
346 420 if (G.telflags & UF_SGA)
347 memset(options, 0, sizeof(options)); 421 {
348 SB_CLEAR(); 422 if (G.telwish == WILL)
349 423 return;
350 tcgetattr(STDIN, &saved_tc); 424 }
351 425 else
352 tmp_tc = saved_tc; 426 if (G.telwish == WONT)
353 tmp_tc.c_lflag &= ~ECHO; /* echo */ 427 return;
354 tmp_tc.c_oflag |= ONLCR; /* NL->CRLF translation */ 428
355 tmp_tc.c_iflag |= ICRNL; 429 if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */
356 tmp_tc.c_iflag &= ~(IXANY|IXOFF|IXON); /* no flow control */ 430 putiac2(DO, TELOPT_SGA);
357 tmp_tc.c_lflag |= ISIG; /* trap signals */ 431 else
358 tmp_tc.c_lflag &= ~ICANON; /* edit mode */ 432 putiac2(DONT, TELOPT_SGA);
359 433
360 /* misc settings, compat with default telnet stuff */ 434 return;
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} 435}
373 436
374static void telnet_start(char *hostname, int port) 437static void telopt(byte c)
375{ 438{
376 struct hostent *host = 0; 439 switch (c)
377 struct sockaddr_in addr; 440 {
378 int s, c; 441 case TELOPT_ECHO: to_echo(c); break;
379 fd_set rfds, wfds; 442 case TELOPT_SGA: to_sga(c); break;
380 443 default: to_notsup(c); break;
381 memset(&addr, 0, sizeof(addr));
382 host = gethostbyname(hostname);
383 if (!host) {
384 fprintf(stderr, "Unknown host: %s\n", hostname);
385 return;
386 } 444 }
387 addr.sin_family = host->h_addrtype; 445}
388 memcpy(&addr.sin_addr, host->h_addr, sizeof(addr.sin_addr)); 446
389 addr.sin_port = htons(port); 447
448/* ******************************* */
390 449
391 printf("Trying %s...\n", inet_ntoa(addr.sin_addr)); 450/* subnegotiation -- ignore all */
451
452static int subneg(byte c)
453{
454 switch (G.telstate)
455 {
456 case TS_SUB1:
457 if (c == IAC)
458 G.telstate = TS_SUB2;
459 break;
460 case TS_SUB2:
461 if (c == SE)
462 return TRUE;
463 G.telstate = TS_SUB1;
464 /* break; */
465 }
466 return FALSE;
467}
468
469/* ******************************* */
470
471static void fgotsig(int sig)
472{
473 G.gotsig = sig;
474}
475
476
477static void rawmode()
478{
479 tcsetattr(0, TCSADRAIN, &G.termios_raw);
480}
481
482static void cookmode()
483{
484 tcsetattr(0, TCSADRAIN, &G.termios_def);
485}
486
487extern int telnet_main(int argc, char** argv)
488{
489 struct in_addr host;
490 int port;
491#ifdef USE_POLL
492 struct pollfd ufds[2];
493#else
494 fd_set readfds;
495 int maxfd;
496#endif
497
498
499 memset(&G, 0, sizeof G);
500
501 if (tcgetattr(0, &G.termios_def) < 0)
502 exit(1);
392 503
393 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) PERROR("socket"); 504 G.termios_raw = G.termios_def;
394 if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) 505
395 PERROR("connect"); 506 cfmakeraw(&G.termios_raw);
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 507
409 if (port == TELNETPORT) telnet_setoptions(s); 508 if (argc < 2) usage(telnet_usage);
509 port = (argc > 2)? getport(argv[2]): 23;
410 510
411 /* shuttle data back and forth between tty and socket */ 511 G.buf = xmalloc(DATABUFSIZE);
412 while (1) { 512 G.iacbuf = xmalloc(IACBUFSIZE);
413 FD_ZERO(&rfds);
414 FD_ZERO(&wfds);
415 513
416 FD_SET(s, &rfds); 514 host = getserver(argv[1]);
417 /* FD_SET(s, &wfds); */ 515
418 FD_SET(STDIN, &rfds); 516 G.netfd = remote_connect(host, port);
419 /* FD_SET(STDOUT, &wfds); */ 517
518 signal(SIGINT, fgotsig);
519
520#ifdef USE_POLL
521 ufds[0].fd = 0; ufds[1].fd = G.netfd;
522 ufds[0].events = ufds[1].events = POLLIN;
523#else
524 FD_ZERO(&readfds);
525 FD_SET(0, &readfds);
526 FD_SET(G.netfd, &readfds);
527 maxfd = G.netfd + 1;
528#endif
420 529
421 if ((c = select(s+1, &rfds, &wfds, 0, 0))) { 530 while (1)
422 if (c == -1) { 531 {
423 /* handle errors */ 532#ifndef USE_POLL
424 PERROR("select"); 533 fd_set rfds = readfds;
425 } 534
426 if (FD_ISSET(s, &rfds)) { 535 switch (select(maxfd, &rfds, NULL, NULL, NULL))
427 /* input from network */ 536#else
428 FD_CLR(s, &rfds); 537 switch (poll(ufds, 2, -1))
429 c = telnet_recv(s, STDOUT); 538#endif
430 if (c == 0) break; 539 {
431 if (c < 0) PERROR("telnet_recv"); 540 case 0:
541 /* timeout */
542 case -1:
543 /* error, ignore and/or log something, bay go to loop */
544 if (G.gotsig)
545 conescape();
546 else
547 sleep(1);
548 break;
549 default:
550
551#ifdef USE_POLL
552 if (ufds[0].revents) /* well, should check POLLIN, but ... */
553#else
554 if (FD_ISSET(0, &rfds))
555#endif
556 {
557 G.len = read(0, G.buf, DATABUFSIZE);
558
559 if (G.len <= 0)
560 doexit(0);
561
562 TRACE(0, ("Read con: %d\n", G.len));
563
564 handlenetoutput();
432 } 565 }
433 if (FD_ISSET(STDIN, &rfds)) { 566
434 /* input from tty */ 567#ifdef USE_POLL
435 FD_CLR(STDIN, &rfds); 568 if (ufds[1].revents) /* well, should check POLLIN, but ... */
436 c = telnet_send(STDIN, s); 569#else
437 if (c == 0) break; 570 if (FD_ISSET(G.netfd, &rfds))
438 if (c < 0) PERROR("telnet_send"); 571#endif
572 {
573 G.len = read(G.netfd, G.buf, DATABUFSIZE);
574
575 if (G.len <= 0)
576 {
577 WriteCS(1, "Connection closed by foreign host.\r\n");
578 doexit(1);
579 }
580 TRACE(0, ("Read netfd (%d): %d\n", G.netfd, G.len));
581
582 handlenetinput();
439 } 583 }
440 } 584 }
441 } 585 }
442
443 return;
444} 586}
445 587
446static void telnet_shutdown(void) 588static int getport(char * p)
447{ 589{
448 printf("\n"); 590 unsigned int port = atoi(p);
449 tcsetattr(STDIN, TCSANOW, &saved_tc); 591
592 if ((unsigned)(port - 1 ) > 65534)
593 {
594 fatalError("%s: bad port number\n", p);
595 }
596 return port;
450} 597}
451 598
452#ifdef STANDALONE_TELNET 599static struct in_addr getserver(char * host)
453void usage(const char *msg)
454{ 600{
455 printf("%s", msg); 601 struct in_addr addr;
456 exit(0); 602
603 struct hostent * he;
604 if ((he = gethostbyname(host)) == NULL)
605 {
606 fatalError("%s: Unkonwn host\n", host);
607 }
608 memcpy(&addr, he->h_addr, sizeof addr);
609
610 TRACE(1, ("addr: %s\n", inet_ntoa(addr)));
611
612 return addr;
613}
614
615static int create_socket()
616{
617 return socket(AF_INET, SOCK_STREAM, 0);
457} 618}
458 619
459int main(int argc, char **argv) 620static void setup_sockaddr_in(struct sockaddr_in * addr, int port)
460#else 621{
461int telnet_main(int argc, char **argv) 622 memset(addr, 0, sizeof addr);
462#endif 623 addr->sin_family = AF_INET;
624 addr->sin_port = htons(port);
625}
626
627#if 0
628static int local_bind(int port)
463{ 629{
464 int port = TELNETPORT; 630 struct sockaddr_in s_addr;
631 int s = create_socket();
632
633 setup_sockaddr_in(&s_addr, port);
634
635 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
636
637 if (bind(s, &s_addr, sizeof s_addr) < 0)
638 {
639 char * e = sys_errlist[errno];
640 syserrorexit("bind");
641 exit(1);
642 }
643 listen(s, 1);
465 644
466 argc--; argv++; 645 return s;
467 if (argc < 1) usage(telnet_usage); 646}
468 if (argc > 1) port = atoi(argv[1]); 647#endif
469 telnet_init(); 648
470 atexit(telnet_shutdown); 649static int remote_connect(struct in_addr addr, int port)
471 650{
472 telnet_start(argv[0], port); 651 struct sockaddr_in s_addr;
473 return 0; 652 int s = create_socket();
653
654 setup_sockaddr_in(&s_addr, port);
655 s_addr.sin_addr = addr;
656
657 setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one);
658
659 if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0)
660 {
661 fatalError("Unable to connect to remote host: %s\n", strerror(errno));
662 }
663 return s;
474} 664}
475 665
476/* 666/*
477 * Copyright (c) 1988, 1990 Regents of the University of California. 667Local Variables:
478 * All rights reserved. 668c-file-style: "linux"
479 * 669c-basic-offset: 4
480 * Redistribution and use in source and binary forms, with or without 670tab-width: 4
481 * modification, are permitted provided that the following conditions 671End:
482 * are met: 672*/
483 * 1. Redistributions of source code must retain the above copyright 673
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 */
diff --git a/tests/cp_tests.mk b/tests/cp_tests.mk
index 2082d7386..1dc4c5ac4 100644
--- a/tests/cp_tests.mk
+++ b/tests/cp_tests.mk
@@ -14,7 +14,13 @@ clean:: cp_clean
14cp_clean: 14cp_clean:
15 - rm -rf cp_tests cp_*.{gnu,bb} cp 15 - rm -rf cp_tests cp_*.{gnu,bb} cp
16 16
17cp_tests: cp_clean cp 17cp_tests: cp_clean cp check_exists check_simple_cp check_cp_symlnk \
18 check_cp_symlink_w_a check_cp_files_to_dir check_cp_files_to_dir_w_d \
19 check_cp_files_to_dir_w_p check_cp_files_to_dir_w_p_and_d \
20 check_cp_dir_to_dir_wo_a check_cp_dir_to_dir_w_a \
21 check_cp_dir_to_dir_w_a_take_two
22
23check_exists:
18 @echo; 24 @echo;
19 @echo "No output from diff means busybox cp is functioning properly."; 25 @echo "No output from diff means busybox cp is functioning properly.";
20 @echo "Some tests might show timestamp differences that are Ok."; 26 @echo "Some tests might show timestamp differences that are Ok.";
@@ -27,6 +33,7 @@ cp_tests: cp_clean cp
27 @echo; 33 @echo;
28 mkdir cp_tests; 34 mkdir cp_tests;
29 35
36check_simple_cp:
30 @echo Copy a file to a copy of the file; 37 @echo Copy a file to a copy of the file;
31 @echo ------------------------------; 38 @echo ------------------------------;
32 cd cp_tests; \ 39 cd cp_tests; \
@@ -52,6 +59,7 @@ cp_tests: cp_clean cp
52 @echo; 59 @echo;
53 rm -rf cp_tests/*; 60 rm -rf cp_tests/*;
54 61
62check_cp_symlnk:
55 @echo; echo Copy a file pointed to by a symlink; 63 @echo; echo Copy a file pointed to by a symlink;
56 @echo ------------------------------; 64 @echo ------------------------------;
57 cd cp_tests; \ 65 cd cp_tests; \
@@ -84,6 +92,7 @@ cp_tests: cp_clean cp
84 @echo; 92 @echo;
85 rm -rf cp_tests/* 93 rm -rf cp_tests/*
86 94
95check_cp_symlink_w_a:
87 @echo; echo Copy a symlink, useing the -a switch.; 96 @echo; echo Copy a symlink, useing the -a switch.;
88 @echo ------------------------------; 97 @echo ------------------------------;
89 cd cp_tests; \ 98 cd cp_tests; \
@@ -115,38 +124,8 @@ cp_tests: cp_clean cp
115 @echo; 124 @echo;
116 rm -rf cp_tests/*; 125 rm -rf cp_tests/*;
117 126
118 @echo; echo Copy a directory into another directory with the -a switch;
119 @echo ------------------------------;
120 cd cp_tests; \
121 mkdir here there; \
122 echo A file > here/afile; \
123 mkdir here/adir; \
124 touch here/adir/afileinadir; \
125 ln -s $$(pwd) here/alink;
126
127 @echo;
128 cd cp_tests; \
129 ls -lR . > ../cp_a_dir_dir.gnu; \
130 ${GCP} -a here/ there/; \
131 ls -lR . >> ../cp_a_dir_dir.gnu;
132
133 @echo;
134 rm -rf cp_tests/there/*;
135
136 sleep 1;
137
138 @echo;
139 cd cp_tests; \
140 ls -lR . > ../cp_a_dir_dir.bb; \
141 ${BCP} -a here/ there/; \
142 ls -lR . >> ../cp_a_dir_dir.bb;
143
144 @echo;
145 diff -u cp_a_dir_dir.gnu cp_a_dir_dir.bb;
146
147 @echo;
148 rm -rf cp_tests/*;
149 127
128check_cp_files_to_dir:
150 # Copy a set of files to a directory. 129 # Copy a set of files to a directory.
151 @echo; echo Copy a set of files to a directory.; 130 @echo; echo Copy a set of files to a directory.;
152 @echo ------------------------------; 131 @echo ------------------------------;
@@ -174,6 +153,7 @@ cp_tests: cp_clean cp
174 @echo; 153 @echo;
175 rm -rf cp_tests/*; 154 rm -rf cp_tests/*;
176 155
156check_cp_files_to_dir_w_d:
177 # Copy a set of files to a directory with the -d switch. 157 # Copy a set of files to a directory with the -d switch.
178 @echo; echo Copy a set of files to a directory with the -d switch.; 158 @echo; echo Copy a set of files to a directory with the -d switch.;
179 @echo ------------------------------; 159 @echo ------------------------------;
@@ -203,6 +183,7 @@ cp_tests: cp_clean cp
203 @echo; 183 @echo;
204 rm -rf cp_tests/{afile{1,2},symlink1,there1}; 184 rm -rf cp_tests/{afile{1,2},symlink1,there1};
205 185
186check_cp_files_to_dir_w_p:
206 # Copy a set of files to a directory with the -p switch. 187 # Copy a set of files to a directory with the -p switch.
207 @echo; echo Copy a set of files to a directory with the -p switch.; 188 @echo; echo Copy a set of files to a directory with the -p switch.;
208 @echo ------------------------------; 189 @echo ------------------------------;
@@ -234,6 +215,8 @@ cp_tests: cp_clean cp
234 @echo; 215 @echo;
235 rm -rf cp_tests/{afile{1,2},symlink1,there1}; 216 rm -rf cp_tests/{afile{1,2},symlink1,there1};
236 217
218
219check_cp_files_to_dir_w_p_and_d:
237 @echo; echo Copy a set of files to a directory with -p and -d switches. 220 @echo; echo Copy a set of files to a directory with -p and -d switches.
238 @echo ------------------------------; 221 @echo ------------------------------;
239 cd cp_tests; \ 222 cd cp_tests; \
@@ -264,6 +247,37 @@ cp_tests: cp_clean cp
264 @echo; 247 @echo;
265 rm -rf cp_tests/{afile{1,2},symlink1,there1}; 248 rm -rf cp_tests/{afile{1,2},symlink1,there1};
266 249
250check_cp_dir_to_dir_wo_a:
251 # Copy a directory to another directory, without the -a switch.
252 @echo; echo Copy a directory to another directory, without the -a switch.
253 @echo ------------------------------;
254 @echo There should be an error message about cannot cp a dir to a subdir of itself.
255 cd cp_tests; \
256 touch a b c; \
257 mkdir adir; \
258 ls -lR . > ../cp_a_star_adir.gnu; \
259 ${GCP} -a * adir; \
260 ls -lR . >> ../cp_a_star_adir.gnu;
261
262 @echo
263 @echo There should be an error message about cannot cp a dir to a subdir of itself.
264 cd cp_tests; \
265 rm -rf adir; \
266 mkdir adir; \
267 ls -lR . > ../cp_a_star_adir.bb; \
268 ${BCP} -a * adir; \
269 ls -lR . >> ../cp_a_star_adir.bb;
270
271 @echo;
272 diff -u cp_a_star_adir.gnu cp_a_star_adir.bb;
273
274 # Done
275 @echo;
276 rm -rf cp_tests;
277 @echo; echo Done.
278
279
280check_cp_dir_to_dir_w_a:
267 @echo; echo Copy a directory into another directory with the -a switch. 281 @echo; echo Copy a directory into another directory with the -a switch.
268 @echo ------------------------------; 282 @echo ------------------------------;
269 cd cp_tests; \ 283 cd cp_tests; \
@@ -298,30 +312,43 @@ cp_tests: cp_clean cp
298 @echo; 312 @echo;
299 rm -rf cp_tests/dir{a,b}; 313 rm -rf cp_tests/dir{a,b};
300 314
301 # Copy a directory to another directory, without the -a switch. 315
302 @echo; echo Copy a directory to another directory, without the -a switch. 316check_cp_dir_to_dir_w_a_take_two:
317 @echo; echo Copy a directory into another directory with the -a switch;
303 @echo ------------------------------; 318 @echo ------------------------------;
304 @echo There should be an error message about cannot cp a dir to a subdir of itself. 319 mkdir -p cp_tests/gnu; \
305 cd cp_tests; \ 320 mkdir -p cp_tests/bb; \
306 touch a b c; \ 321 cd cp_tests; \
307 mkdir adir; \ 322 mkdir here there; \
308 ls -lR . > ../cp_a_star_adir.gnu; \ 323 echo A file > here/afile; \
309 ${GCP} -a * adir; \ 324 mkdir here/adir; \
310 ls -lR . >> ../cp_a_star_adir.gnu; 325 touch here/adir/afileinadir; \
326 ln -s $$(pwd) here/alink;
311 327
312 @echo 328 @echo;
313 @echo There should be an error message about cannot cp a dir to a subdir of itself. 329 cd cp_tests/gnu; \
314 cd cp_tests; \ 330 ls -lR . > ../../cp_a_dir_dir.gnu; \
315 rm -rf adir; \ 331 ${GCP} -a here/ there/; \
316 mkdir adir; \ 332 ls -lR . >> ../../cp_a_dir_dir.gnu;
317 ls -lR . > ../cp_a_star_adir.bb; \
318 ${BCP} -a * adir; \
319 ls -lR . >> ../cp_a_star_adir.bb;
320 333
321 @echo; 334 @echo;
322 diff -u cp_a_star_adir.gnu cp_a_star_adir.bb; 335 rm -rf cp_tests/there/*;
336
337 sleep 1;
323 338
324 # Done
325 @echo; 339 @echo;
326 rm -rf cp_tests; 340 cd cp_tests/bb; \
327 @echo; echo Done. 341 ls -lR . > ../../cp_a_dir_dir.bb; \
342 ${BCP} -a here/ there/; \
343 ls -lR . >> ../../cp_a_dir_dir.bb;
344
345 @echo;
346 echo "Erik 1"
347 diff -u cp_a_dir_dir.gnu cp_a_dir_dir.bb;
348 echo "Erik 2"
349
350 @echo;
351 echo "Erik 3"
352 rm -rf cp_tests/*;
353
354