diff options
| author | andersen <andersen@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2000-06-14 20:42:57 +0000 |
|---|---|---|
| committer | andersen <andersen@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2000-06-14 20:42:57 +0000 |
| commit | 7d7a99ca6e7ee92dcd29be4ea8e7b0563ca024eb (patch) | |
| tree | 672d4d34162d111ef8166bfdb45d3cad28545bd4 | |
| parent | d6ff3328a280c7ea475daa52e8ae4d181c599506 (diff) | |
| download | busybox-w32-7d7a99ca6e7ee92dcd29be4ea8e7b0563ca024eb.tar.gz busybox-w32-7d7a99ca6e7ee92dcd29be4ea8e7b0563ca024eb.tar.bz2 busybox-w32-7d7a99ca6e7ee92dcd29be4ea8e7b0563ca024eb.zip | |
Integrate Tomi Ollila's telnet. Costs 3k. :)
-Erik
git-svn-id: svn://busybox.net/trunk/busybox@648 69ca8d6d-28ef-0310-b511-8ec308f3f277
| -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 | |||
