diff options
author | Eric Andersen <andersen@codepoet.org> | 2000-06-14 20:42:57 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2000-06-14 20:42:57 +0000 |
commit | 28c70b3a16c17319c184a90eb8f164527c98b4f8 (patch) | |
tree | 672d4d34162d111ef8166bfdb45d3cad28545bd4 | |
parent | e97da4007913bd29d8a18d038de29d9d4549163d (diff) | |
download | busybox-w32-28c70b3a16c17319c184a90eb8f164527c98b4f8.tar.gz busybox-w32-28c70b3a16c17319c184a90eb8f164527c98b4f8.tar.bz2 busybox-w32-28c70b3a16c17319c184a90eb8f164527c98b4f8.zip |
Integrate Tomi Ollila's telnet. Costs 3k. :)
-Erik
-rw-r--r-- | Changelog | 2 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | busybox.def.h | 1 | ||||
-rw-r--r-- | networking/telnet.c | 1026 | ||||
-rw-r--r-- | telnet.c | 1026 | ||||
-rw-r--r-- | tests/cp_tests.mk | 133 |
6 files changed, 1274 insertions, 915 deletions
@@ -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 |
@@ -18,7 +18,6 @@ around to it some time. If you have any good ideas, please let me know. | |||
18 | 18 | ||
19 | Bugs that need fixing before the 0.44 release goes out the door: | 19 | Bugs 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 | ||
47 | static int STDIN = 0; | 69 | #define CHM_TRY 0 |
48 | static int STDOUT = 1; | 70 | #define CHM_ON 1 |
49 | static 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 | |||
84 | typedef unsigned char byte; | ||
85 | |||
86 | /* use globals to reduce size ??? */ /* test this hypothesis later */ | ||
87 | struct 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 | ||
107 | struct Globalvars * Gptr; | ||
108 | #define G (*Gptr) | ||
109 | #else | ||
110 | struct Globalvars G; | ||
111 | #endif | ||
112 | |||
113 | static inline void iacflush() | ||
114 | { | ||
115 | write(G.netfd, G.iacbuf, G.iaclen); | ||
116 | G.iaclen = 0; | ||
117 | } | ||
118 | |||
119 | /* Function prototypes */ | ||
120 | static int getport(char * p); | ||
121 | static struct in_addr getserver(char * p); | ||
122 | static int create_socket(); | ||
123 | static void setup_sockaddr_in(struct sockaddr_in * addr, int port); | ||
124 | static int remote_connect(struct in_addr addr, int port); | ||
125 | static void rawmode(); | ||
126 | static void cookmode(); | ||
127 | static void do_linemode(); | ||
128 | static void will_charmode(); | ||
129 | static void telopt(byte c); | ||
130 | static int subneg(byte c); | ||
131 | #if 0 | ||
132 | static int local_bind(int port); | ||
133 | #endif | ||
134 | |||
135 | /* Some globals */ | ||
136 | static int one = 1; | ||
137 | static 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 | ; |
55 | static struct termios saved_tc; | 144 | |
56 | static unsigned char options[NTELOPTS]; | 145 | |
57 | static char tr_state = 0; /* telnet send and receive state */ | 146 | static void doexit(int ev) |
58 | static unsigned char subbuffer[256]; | 147 | { |
59 | static 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++) | 152 | static 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(); |
92 | static void telnet_init(void); | 181 | goto rrturn; |
93 | static void telnet_start(char *host, int port); | 182 | } |
94 | static 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': | |
101 | static inline void telnet_sendwill(int s, int c) { SENDCOMMAND(WILL, c); } | 190 | doexit(0); |
102 | static inline void telnet_sendwont(int s, int c) { SENDCOMMAND(WONT, c); } | 191 | } |
103 | static inline void telnet_senddo(int s, int c) { SENDCOMMAND(DO, c); } | 192 | |
104 | static inline void telnet_senddont(int s, int c) { SENDCOMMAND(DONT, c); } | 193 | WriteCS(1, "continuing...\r\n"); |
105 | 194 | ||
106 | static void telnet_setoptions(int s) | 195 | if (G.gotsig) |
196 | cookmode(); | ||
197 | |||
198 | rrturn: | ||
199 | G.gotsig = 0; | ||
200 | |||
201 | } | ||
202 | static 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 | ||
122 | static void telnet_suboptions(int net) | 226 | |
227 | static 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 | ||
206 | static void sighandler(int sig) | 301 | |
302 | /* ******************************* */ | ||
303 | |||
304 | static inline void putiac(int c) | ||
207 | { | 305 | { |
208 | telnet_shutdown(); | 306 | G.iacbuf[G.iaclen++] = c; |
209 | exit(0); | ||
210 | } | 307 | } |
211 | 308 | ||
212 | static int telnet_send(int tty, int net) | 309 | |
310 | static 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; | 321 | static 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 | |||
335 | char const escapecharis[] = "\r\nEscape character is "; | ||
336 | |||
337 | static 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 | ||
230 | static int telnet_recv(int net, int tty) | 357 | /* ******************************* */ |
358 | |||
359 | static 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) { | 370 | static 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: | 383 | static 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; | 389 | static 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 | /* ******************************************************************* */ | 416 | static inline void to_sga() |
342 | static 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 | ||
374 | static void telnet_start(char *hostname, int port) | 437 | static 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 | |||
452 | static 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 | |||
471 | static void fgotsig(int sig) | ||
472 | { | ||
473 | G.gotsig = sig; | ||
474 | } | ||
475 | |||
476 | |||
477 | static void rawmode() | ||
478 | { | ||
479 | tcsetattr(0, TCSADRAIN, &G.termios_raw); | ||
480 | } | ||
481 | |||
482 | static void cookmode() | ||
483 | { | ||
484 | tcsetattr(0, TCSADRAIN, &G.termios_def); | ||
485 | } | ||
486 | |||
487 | extern 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 | ||
446 | static void telnet_shutdown(void) | 588 | static 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 | 599 | static struct in_addr getserver(char * host) |
453 | void 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 | |||
615 | static int create_socket() | ||
616 | { | ||
617 | return socket(AF_INET, SOCK_STREAM, 0); | ||
457 | } | 618 | } |
458 | 619 | ||
459 | int main(int argc, char **argv) | 620 | static void setup_sockaddr_in(struct sockaddr_in * addr, int port) |
460 | #else | 621 | { |
461 | int 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 | ||
628 | static 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); | 649 | static 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. | 667 | Local Variables: |
478 | * All rights reserved. | 668 | c-file-style: "linux" |
479 | * | 669 | c-basic-offset: 4 |
480 | * Redistribution and use in source and binary forms, with or without | 670 | tab-width: 4 |
481 | * modification, are permitted provided that the following conditions | 671 | End: |
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 | */ | ||
@@ -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 | ||
47 | static int STDIN = 0; | 69 | #define CHM_TRY 0 |
48 | static int STDOUT = 1; | 70 | #define CHM_ON 1 |
49 | static 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 | |||
84 | typedef unsigned char byte; | ||
85 | |||
86 | /* use globals to reduce size ??? */ /* test this hypothesis later */ | ||
87 | struct 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 | ||
107 | struct Globalvars * Gptr; | ||
108 | #define G (*Gptr) | ||
109 | #else | ||
110 | struct Globalvars G; | ||
111 | #endif | ||
112 | |||
113 | static inline void iacflush() | ||
114 | { | ||
115 | write(G.netfd, G.iacbuf, G.iaclen); | ||
116 | G.iaclen = 0; | ||
117 | } | ||
118 | |||
119 | /* Function prototypes */ | ||
120 | static int getport(char * p); | ||
121 | static struct in_addr getserver(char * p); | ||
122 | static int create_socket(); | ||
123 | static void setup_sockaddr_in(struct sockaddr_in * addr, int port); | ||
124 | static int remote_connect(struct in_addr addr, int port); | ||
125 | static void rawmode(); | ||
126 | static void cookmode(); | ||
127 | static void do_linemode(); | ||
128 | static void will_charmode(); | ||
129 | static void telopt(byte c); | ||
130 | static int subneg(byte c); | ||
131 | #if 0 | ||
132 | static int local_bind(int port); | ||
133 | #endif | ||
134 | |||
135 | /* Some globals */ | ||
136 | static int one = 1; | ||
137 | static 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 | ; |
55 | static struct termios saved_tc; | 144 | |
56 | static unsigned char options[NTELOPTS]; | 145 | |
57 | static char tr_state = 0; /* telnet send and receive state */ | 146 | static void doexit(int ev) |
58 | static unsigned char subbuffer[256]; | 147 | { |
59 | static 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++) | 152 | static 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(); |
92 | static void telnet_init(void); | 181 | goto rrturn; |
93 | static void telnet_start(char *host, int port); | 182 | } |
94 | static 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': | |
101 | static inline void telnet_sendwill(int s, int c) { SENDCOMMAND(WILL, c); } | 190 | doexit(0); |
102 | static inline void telnet_sendwont(int s, int c) { SENDCOMMAND(WONT, c); } | 191 | } |
103 | static inline void telnet_senddo(int s, int c) { SENDCOMMAND(DO, c); } | 192 | |
104 | static inline void telnet_senddont(int s, int c) { SENDCOMMAND(DONT, c); } | 193 | WriteCS(1, "continuing...\r\n"); |
105 | 194 | ||
106 | static void telnet_setoptions(int s) | 195 | if (G.gotsig) |
196 | cookmode(); | ||
197 | |||
198 | rrturn: | ||
199 | G.gotsig = 0; | ||
200 | |||
201 | } | ||
202 | static 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 | ||
122 | static void telnet_suboptions(int net) | 226 | |
227 | static 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 | ||
206 | static void sighandler(int sig) | 301 | |
302 | /* ******************************* */ | ||
303 | |||
304 | static inline void putiac(int c) | ||
207 | { | 305 | { |
208 | telnet_shutdown(); | 306 | G.iacbuf[G.iaclen++] = c; |
209 | exit(0); | ||
210 | } | 307 | } |
211 | 308 | ||
212 | static int telnet_send(int tty, int net) | 309 | |
310 | static 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; | 321 | static 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 | |||
335 | char const escapecharis[] = "\r\nEscape character is "; | ||
336 | |||
337 | static 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 | ||
230 | static int telnet_recv(int net, int tty) | 357 | /* ******************************* */ |
358 | |||
359 | static 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) { | 370 | static 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: | 383 | static 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; | 389 | static 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 | /* ******************************************************************* */ | 416 | static inline void to_sga() |
342 | static 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 | ||
374 | static void telnet_start(char *hostname, int port) | 437 | static 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 | |||
452 | static 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 | |||
471 | static void fgotsig(int sig) | ||
472 | { | ||
473 | G.gotsig = sig; | ||
474 | } | ||
475 | |||
476 | |||
477 | static void rawmode() | ||
478 | { | ||
479 | tcsetattr(0, TCSADRAIN, &G.termios_raw); | ||
480 | } | ||
481 | |||
482 | static void cookmode() | ||
483 | { | ||
484 | tcsetattr(0, TCSADRAIN, &G.termios_def); | ||
485 | } | ||
486 | |||
487 | extern 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 | ||
446 | static void telnet_shutdown(void) | 588 | static 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 | 599 | static struct in_addr getserver(char * host) |
453 | void 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 | |||
615 | static int create_socket() | ||
616 | { | ||
617 | return socket(AF_INET, SOCK_STREAM, 0); | ||
457 | } | 618 | } |
458 | 619 | ||
459 | int main(int argc, char **argv) | 620 | static void setup_sockaddr_in(struct sockaddr_in * addr, int port) |
460 | #else | 621 | { |
461 | int 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 | ||
628 | static 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); | 649 | static 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. | 667 | Local Variables: |
478 | * All rights reserved. | 668 | c-file-style: "linux" |
479 | * | 669 | c-basic-offset: 4 |
480 | * Redistribution and use in source and binary forms, with or without | 670 | tab-width: 4 |
481 | * modification, are permitted provided that the following conditions | 671 | End: |
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 | |||
14 | cp_clean: | 14 | cp_clean: |
15 | - rm -rf cp_tests cp_*.{gnu,bb} cp | 15 | - rm -rf cp_tests cp_*.{gnu,bb} cp |
16 | 16 | ||
17 | cp_tests: cp_clean cp | 17 | cp_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 | |||
23 | check_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 | ||
36 | check_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 | ||
62 | check_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 | ||
95 | check_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 | ||
128 | check_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 | ||
156 | check_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 | ||
186 | check_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 | |||
219 | check_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 | ||
250 | check_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 | |||
280 | check_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. | 316 | check_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 | |||