X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Ftelnet.c;h=c835c7a4525b2450fdc820428e4cdc551614852c;hb=5929edc1fac4340f99ed84e92bf3a2bedd4177c2;hp=2587193e22f670a11613c0a3a5ceea8631c6e53f;hpb=c55b8d41c15640fa1637f919b3f6eca6e781047a;p=oweals%2Fbusybox.git diff --git a/networking/telnet.c b/networking/telnet.c index 2587193e2..c835c7a45 100644 --- a/networking/telnet.c +++ b/networking/telnet.c @@ -8,27 +8,16 @@ * Created: Thu Apr 7 13:29:41 1994 too * Last modified: Fri Jun 9 14:34:24 2000 too * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. * * HISTORY * Revision 3.1 1994/04/17 11:31:54 too * initial revision - * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen - * + * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan * + * Modified 2004/02/11 to add ability to pass the USER variable to remote host + * by Fernando Silveira * */ @@ -43,38 +32,30 @@ #include #include #include -#include #include "busybox.h" #if 0 -static const int DOTRACE = 1; +enum { DOTRACE = 1 }; #endif #ifdef DOTRACE #include /* for inet_ntoa()... */ #define TRACE(x, y) do { if (x) printf y; } while (0) #else -#define TRACE(x, y) -#endif - -#if 0 -#define USE_POLL -#include -#else -#include +#define TRACE(x, y) #endif -static const int DATABUFSIZE = 128; -static const int IACBUFSIZE = 128; +#define DATABUFSIZE 128 +#define IACBUFSIZE 128 -static const int CHM_TRY = 0; -static const int CHM_ON = 1; -static const int CHM_OFF = 2; +enum { + CHM_TRY = 0, + CHM_ON = 1, + CHM_OFF = 2, -static const int UF_ECHO = 0x01; -static const int UF_SGA = 0x02; + UF_ECHO = 0x01, + UF_SGA = 0x02, -enum { TS_0 = 1, TS_IAC = 2, TS_OPT = 3, @@ -90,18 +71,18 @@ typedef unsigned char byte; static struct Globalvars { int netfd; /* console fd:s are 0 and 1 (and 2) */ /* same buffer used both for network and console read/write */ - char * buf; /* allocating so static size is smaller */ - short len; + char buf[DATABUFSIZE]; /* allocating so static size is smaller */ byte telstate; /* telnet negotiation state from network input */ byte telwish; /* DO, DONT, WILL, WONT */ byte charmode; byte telflags; byte gotsig; + byte do_termios; /* buffer to handle telnet negotiations */ - char * iacbuf; + char iacbuf[IACBUFSIZE]; short iaclen; /* could even use byte */ - struct termios termios_def; - struct termios termios_raw; + struct termios termios_def; + struct termios termios_raw; } G; #define xUSE_GLOBALVAR_PTR /* xUSE... -> don't use :D (makes smaller code) */ @@ -109,46 +90,44 @@ static struct Globalvars { #ifdef USE_GLOBALVAR_PTR struct Globalvars * Gptr; #define G (*Gptr) -#else -static struct Globalvars G; #endif -static inline void iacflush() +static inline void iacflush(void) { write(G.netfd, G.iacbuf, G.iaclen); G.iaclen = 0; } /* Function prototypes */ -static int getport(char * p); -static struct in_addr getserver(char * p); -static int create_socket(); -static void setup_sockaddr_in(struct sockaddr_in * addr, int port); -static int remote_connect(struct in_addr addr, int port); -static void rawmode(); -static void cookmode(); -static void do_linemode(); -static void will_charmode(); +static void rawmode(void); +static void cookmode(void); +static void do_linemode(void); +static void will_charmode(void); static void telopt(byte c); static int subneg(byte c); -#if 0 -static int local_bind(int port); -#endif /* Some globals */ static int one = 1; -#ifdef BB_FEATURE_TELNET_TTYPE +#ifdef CONFIG_FEATURE_TELNET_TTYPE static char *ttype; #endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static const char *autologin; +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static int win_width, win_height; +#endif + static void doexit(int ev) { cookmode(); exit(ev); -} +} -static void conescape() +static void conescape(void) { char b; @@ -193,42 +172,58 @@ static void conescape() if (G.gotsig) cookmode(); - + rrturn: G.gotsig = 0; - + } -static void handlenetoutput() +static void handlenetoutput(int len) { /* here we could do smart tricks how to handle 0xFF:s in output * stream like writing twice every sequence of FF:s (thus doing * many write()s. But I think interactive telnet application does * not need to be 100% 8-bit clean, so changing every 0xff:s to - * 0x7f:s */ - - int i; - byte * p = G.buf; - - for (i = G.len; i > 0; i--, p++) + * 0x7f:s + * + * 2002-mar-21, Przemyslaw Czerpak (druzus@polbox.com) + * I don't agree. + * first - I cannot use programs like sz/rz + * second - the 0x0D is sent as one character and if the next + * char is 0x0A then it's eaten by a server side. + * third - whay doy you have to make 'many write()s'? + * I don't understand. + * So I implemented it. It's realy useful for me. I hope that + * others people will find it interesting to. + */ + + int i, j; + byte * p = (byte*)G.buf; + byte outbuf[4*DATABUFSIZE]; + + for (i = len, j = 0; i > 0; i--, p++) { if (*p == 0x1d) { conescape(); return; } + outbuf[j++] = *p; if (*p == 0xff) - *p = 0x7f; + outbuf[j++] = 0xff; + else if (*p == 0x0d) + outbuf[j++] = 0x00; } - write(G.netfd, G.buf, G.len); + if (j > 0 ) + write(G.netfd, outbuf, j); } -static void handlenetinput() +static void handlenetinput(int len) { int i; int cstart = 0; - for (i = 0; i < G.len; i++) + for (i = 0; i < len; i++) { byte c = G.buf[i]; @@ -280,7 +275,7 @@ static void handlenetinput() break; case TS_SUB1: /* Subnegotiation */ case TS_SUB2: /* Subnegotiation */ - if (subneg(c) == TRUE) + if (subneg(c)) G.telstate = TS_0; break; } @@ -290,11 +285,11 @@ static void handlenetinput() if (G.iaclen) iacflush(); if (G.telstate == TS_0) G.telstate = 0; - G.len = cstart; + len = cstart; } - if (G.len) - write(1, G.buf, G.len); + if (len) + write(1, G.buf, len); } @@ -327,7 +322,7 @@ static void putiac1(byte c) } #endif -#ifdef BB_FEATURE_TELNET_TTYPE +#ifdef CONFIG_FEATURE_TELNET_TTYPE static void putiac_subopt(byte c, char *str) { int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) @@ -348,13 +343,61 @@ static void putiac_subopt(byte c, char *str) } #endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static void putiac_subopt_autologin(void) +{ + int len = strlen(autologin) + 6; // (2 + 1 + 1 + strlen + 2) + char *user = "USER"; + + if (G.iaclen + len > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(TELOPT_NEW_ENVIRON); + putiac(TELQUAL_IS); + putiac(NEW_ENV_VAR); + + while(*user) + putiac(*user++); + + putiac(NEW_ENV_VALUE); + + while(*autologin) + putiac(*autologin++); + + putiac(IAC); + putiac(SE); +} +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static void putiac_naws(byte c, int x, int y) +{ + if (G.iaclen + 9 > IACBUFSIZE) + iacflush(); + + putiac(IAC); + putiac(SB); + putiac(c); + + putiac((x >> 8) & 0xff); + putiac(x & 0xff); + putiac((y >> 8) & 0xff); + putiac(y & 0xff); + + putiac(IAC); + putiac(SE); +} +#endif + /* void putiacstring (subneg strings) */ /* ******************************* */ static char const escapecharis[] = "\r\nEscape character is "; -static void setConMode() +static void setConMode(void) { if (G.telflags & UF_ECHO) { @@ -376,18 +419,18 @@ static void setConMode() /* ******************************* */ -static void will_charmode() +static void will_charmode(void) { G.charmode = CHM_TRY; G.telflags |= (UF_ECHO | UF_SGA); setConMode(); - + putiac2(DO, TELOPT_ECHO); putiac2(DO, TELOPT_SGA); iacflush(); } -static void do_linemode() +static void do_linemode(void) { G.charmode = CHM_TRY; G.telflags &= ~(UF_ECHO | UF_SGA); @@ -406,12 +449,12 @@ static inline void to_notsup(char c) else if (G.telwish == DO) putiac2(WONT, c); } -static inline void to_echo() +static inline void to_echo(void) { /* if server requests ECHO, don't agree */ if (G.telwish == DO) { putiac2(WONT, TELOPT_ECHO); return; } else if (G.telwish == DONT) return; - + if (G.telflags & UF_ECHO) { if (G.telwish == WILL) @@ -433,7 +476,7 @@ static inline void to_echo() WriteCS(1, "\r\n"); /* sudden modec */ } -static inline void to_sga() +static inline void to_sga(void) { /* daemon always sends will/wont, client do/dont */ @@ -445,7 +488,7 @@ static inline void to_sga() else if (G.telwish == WONT) return; - + if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */ putiac2(DO, TELOPT_SGA); else @@ -454,8 +497,8 @@ static inline void to_sga() return; } -#ifdef BB_FEATURE_TELNET_TTYPE -static inline void to_ttype() +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static inline void to_ttype(void) { /* Tell server we will (or won't) do TTYPE */ @@ -468,23 +511,55 @@ static inline void to_ttype() } #endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static inline void to_new_environ(void) +{ + /* Tell server we will (or will not) do AUTOLOGIN */ + + if (autologin) + putiac2(WILL, TELOPT_NEW_ENVIRON); + else + putiac2(WONT, TELOPT_NEW_ENVIRON); + + return; +} +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH +static inline void to_naws(void) +{ + /* Tell server we will do NAWS */ + putiac2(WILL, TELOPT_NAWS); + return; +} +#endif + static void telopt(byte c) { switch (c) { - case TELOPT_ECHO: to_echo(c); break; - case TELOPT_SGA: to_sga(c); break; -#ifdef BB_FEATURE_TELNET_TTYPE - case TELOPT_TTYPE: to_ttype(c); break; + case TELOPT_ECHO: to_echo(); break; + case TELOPT_SGA: to_sga(); break; +#ifdef CONFIG_FEATURE_TELNET_TTYPE + case TELOPT_TTYPE: to_ttype();break; #endif - default: to_notsup(c); break; +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + case TELOPT_NEW_ENVIRON: to_new_environ(); break; +#endif +#ifdef CONFIG_FEATURE_AUTOWIDTH + case TELOPT_NAWS: to_naws(); + putiac_naws(c, win_width, win_height); + break; +#endif + default: to_notsup(c); + break; } } /* ******************************* */ -/* subnegotiation -- ignore all (except TTYPE) */ +/* subnegotiation -- ignore all (except TTYPE,NAWS) */ static int subneg(byte c) { @@ -493,10 +568,15 @@ static int subneg(byte c) case TS_SUB1: if (c == IAC) G.telstate = TS_SUB2; -#ifdef BB_FEATURE_TELNET_TTYPE +#ifdef CONFIG_FEATURE_TELNET_TTYPE else if (c == TELOPT_TTYPE) putiac_subopt(TELOPT_TTYPE,ttype); +#endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + else + if (c == TELOPT_NEW_ENVIRON) + putiac_subopt_autologin(); #endif break; case TS_SUB2: @@ -516,71 +596,89 @@ static void fgotsig(int sig) } -static void rawmode() +static void rawmode(void) { - tcsetattr(0, TCSADRAIN, &G.termios_raw); -} + if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_raw); +} -static void cookmode() +static void cookmode(void) { - tcsetattr(0, TCSADRAIN, &G.termios_def); + if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_def); } -extern int telnet_main(int argc, char** argv) +int telnet_main(int argc, char** argv) { - struct in_addr host; - int port; + int len; + struct sockaddr_in s_in; #ifdef USE_POLL struct pollfd ufds[2]; -#else +#else fd_set readfds; int maxfd; -#endif +#endif + +#ifdef CONFIG_FEATURE_AUTOWIDTH + get_terminal_width_height(0, &win_width, &win_height); +#endif -#ifdef BB_FEATURE_TELNET_TTYPE +#ifdef CONFIG_FEATURE_TELNET_TTYPE ttype = getenv("TERM"); #endif memset(&G, 0, sizeof G); - if (tcgetattr(0, &G.termios_def) < 0) - exit(1); - - G.termios_raw = G.termios_def; + if (tcgetattr(0, &G.termios_def) >= 0) { + G.do_termios = 1; - cfmakeraw(&G.termios_raw); - - if (argc < 2) show_usage(); - port = (argc > 2)? getport(argv[2]): 23; - - G.buf = xmalloc(DATABUFSIZE); - G.iacbuf = xmalloc(IACBUFSIZE); - - host = getserver(argv[1]); + G.termios_raw = G.termios_def; + cfmakeraw(&G.termios_raw); + } - G.netfd = remote_connect(host, port); + if (argc < 2) + bb_show_usage(); + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + if (1 & bb_getopt_ulflags(argc, argv, "al:", &autologin)) + autologin = getenv("USER"); + + if (optind < argc) { + bb_lookup_host(&s_in, argv[optind++]); + s_in.sin_port = bb_lookup_port((optind < argc) ? argv[optind++] : + "telnet", "tcp", 23); + if (optind < argc) + bb_show_usage(); + } else + bb_show_usage(); +#else + bb_lookup_host(&s_in, argv[1]); + s_in.sin_port = bb_lookup_port((argc == 3) ? argv[2] : "telnet", "tcp", 23); +#endif + + G.netfd = xconnect(&s_in); + + setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); signal(SIGINT, fgotsig); #ifdef USE_POLL ufds[0].fd = 0; ufds[1].fd = G.netfd; ufds[0].events = ufds[1].events = POLLIN; -#else +#else FD_ZERO(&readfds); FD_SET(0, &readfds); FD_SET(G.netfd, &readfds); maxfd = G.netfd + 1; #endif - + while (1) { #ifndef USE_POLL fd_set rfds = readfds; - + switch (select(maxfd, &rfds, NULL, NULL, NULL)) #else switch (poll(ufds, 2, -1)) -#endif +#endif { case 0: /* timeout */ @@ -595,121 +693,37 @@ extern int telnet_main(int argc, char** argv) #ifdef USE_POLL if (ufds[0].revents) /* well, should check POLLIN, but ... */ -#else +#else if (FD_ISSET(0, &rfds)) -#endif +#endif { - G.len = read(0, G.buf, DATABUFSIZE); + len = read(0, G.buf, DATABUFSIZE); - if (G.len <= 0) + if (len <= 0) doexit(0); - TRACE(0, ("Read con: %d\n", G.len)); - - handlenetoutput(); + TRACE(0, ("Read con: %d\n", len)); + + handlenetoutput(len); } #ifdef USE_POLL if (ufds[1].revents) /* well, should check POLLIN, but ... */ -#else +#else if (FD_ISSET(G.netfd, &rfds)) -#endif +#endif { - G.len = read(G.netfd, G.buf, DATABUFSIZE); + len = read(G.netfd, G.buf, DATABUFSIZE); - if (G.len <= 0) + if (len <= 0) { WriteCS(1, "Connection closed by foreign host.\r\n"); doexit(1); } - TRACE(0, ("Read netfd (%d): %d\n", G.netfd, G.len)); + TRACE(0, ("Read netfd (%d): %d\n", G.netfd, len)); - handlenetinput(); + handlenetinput(len); } } } } - -static int getport(char * p) -{ - unsigned int port = atoi(p); - - if ((unsigned)(port - 1 ) > 65534) - { - error_msg_and_die("%s: bad port number", p); - } - return port; -} - -static struct in_addr getserver(char * host) -{ - struct in_addr addr; - - struct hostent * he; - he = xgethostbyname(host); - memcpy(&addr, he->h_addr, sizeof addr); - - TRACE(1, ("addr: %s\n", inet_ntoa(addr))); - - return addr; -} - -static int create_socket() -{ - return socket(AF_INET, SOCK_STREAM, 0); -} - -static void setup_sockaddr_in(struct sockaddr_in * addr, int port) -{ - memset(addr, 0, sizeof(struct sockaddr_in)); - addr->sin_family = AF_INET; - addr->sin_port = htons(port); -} - -#if 0 -static int local_bind(int port) -{ - struct sockaddr_in s_addr; - int s = create_socket(); - - setup_sockaddr_in(&s_addr, port); - - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); - - if (bind(s, &s_addr, sizeof s_addr) < 0) - { - char * e = sys_errlist[errno]; - syserrorexit("bind"); - exit(1); - } - listen(s, 1); - - return s; -} -#endif - -static int remote_connect(struct in_addr addr, int port) -{ - struct sockaddr_in s_addr; - int s = create_socket(); - - setup_sockaddr_in(&s_addr, port); - s_addr.sin_addr = addr; - - setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); - - if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0) - { - perror_msg_and_die("Unable to connect to remote host"); - } - return s; -} - -/* -Local Variables: -c-file-style: "linux" -c-basic-offset: 4 -tab-width: 4 -End: -*/ -