X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Ftelnet.c;h=e65b6918d8d9f8eb9df0a2b2142050d8f2a43bf7;hb=52933d47bd86d6d992f7290fad93d63b53f7a15f;hp=71479fce42c0660c505abe9797dba133b51b997b;hpb=cd8c436d811a5a077a0b61a6616c900173e2dcfb;p=oweals%2Fbusybox.git diff --git a/networking/telnet.c b/networking/telnet.c index 71479fce4..e65b6918d 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,26 @@ #include #include #include -#include #include "busybox.h" -#if 0 -static const int 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 #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, @@ -96,11 +73,12 @@ static struct Globalvars { byte charmode; byte telflags; byte gotsig; + byte do_termios; /* buffer to handle telnet negotiations */ 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) */ @@ -108,43 +86,42 @@ static struct Globalvars { #ifdef USE_GLOBALVAR_PTR struct Globalvars * Gptr; #define G (*Gptr) -#else -static struct Globalvars G; #endif -static inline void iacflush(void) +static 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 void setup_sockaddr_in(struct sockaddr_in * addr, int port); -static int remote_connect(struct in_addr addr, int port); 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; +static const int one = 1; #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(void) { @@ -191,10 +168,10 @@ static void conescape(void) if (G.gotsig) cookmode(); - + rrturn: G.gotsig = 0; - + } static void handlenetoutput(int len) { @@ -202,22 +179,38 @@ static void handlenetoutput(int len) * 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 = 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, len); + if (j > 0 ) + write(G.netfd, outbuf, j); } @@ -278,7 +271,7 @@ static void handlenetinput(int len) break; case TS_SUB1: /* Subnegotiation */ case TS_SUB2: /* Subnegotiation */ - if (subneg(c) == TRUE) + if (subneg(c)) G.telstate = TS_0; break; } @@ -298,7 +291,7 @@ static void handlenetinput(int len) /* ******************************* */ -static inline void putiac(int c) +static void putiac(int c) { G.iacbuf[G.iaclen++] = c; } @@ -314,32 +307,69 @@ static void putiac2(byte wwdd, byte c) putiac(c); } -#if 0 -static void putiac1(byte c) +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static void putiac_subopt(byte c, char *str) { - if (G.iaclen + 2 > IACBUFSIZE) + int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) + + if (G.iaclen + len > IACBUFSIZE) iacflush(); putiac(IAC); + putiac(SB); putiac(c); + putiac(0); + + while (*str) + putiac(*str++); + + putiac(IAC); + putiac(SE); } #endif -#ifdef CONFIG_FEATURE_TELNET_TTYPE -static void putiac_subopt(byte c, char *str) +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static void putiac_subopt_autologin(void) { - int len = strlen(str) + 6; // ( 2 + 1 + 1 + strlen + 2 ) + 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(0); - while(*str) - putiac(*str++); + putiac((x >> 8) & 0xff); + putiac(x & 0xff); + putiac((y >> 8) & 0xff); + putiac(y & 0xff); putiac(IAC); putiac(SE); @@ -379,7 +409,7 @@ 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(); @@ -398,18 +428,18 @@ static void do_linemode(void) /* ******************************* */ -static inline void to_notsup(char c) +static void to_notsup(char c) { if (G.telwish == WILL) putiac2(DONT, c); else if (G.telwish == DO) putiac2(WONT, c); } -static inline void to_echo(void) +static 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) @@ -431,7 +461,7 @@ static inline void to_echo(void) WriteCS(1, "\r\n"); /* sudden modec */ } -static inline void to_sga(void) +static void to_sga(void) { /* daemon always sends will/wont, client do/dont */ @@ -443,7 +473,7 @@ static inline void to_sga(void) else if (G.telwish == WONT) return; - + if ((G.telflags ^= UF_SGA) & UF_SGA) /* toggle */ putiac2(DO, TELOPT_SGA); else @@ -453,7 +483,7 @@ static inline void to_sga(void) } #ifdef CONFIG_FEATURE_TELNET_TTYPE -static inline void to_ttype(void) +static void to_ttype(void) { /* Tell server we will (or won't) do TTYPE */ @@ -466,23 +496,55 @@ static inline void to_ttype(void) } #endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN +static 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 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(); break; - case TELOPT_SGA: to_sga(); break; + case TELOPT_ECHO: to_echo(); break; + case TELOPT_SGA: to_sga(); break; #ifdef CONFIG_FEATURE_TELNET_TTYPE - case TELOPT_TTYPE: to_ttype(); break; + case TELOPT_TTYPE: to_ttype();break; +#endif +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + case TELOPT_NEW_ENVIRON: to_new_environ(); break; #endif - default: to_notsup(c); break; +#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) { @@ -495,6 +557,11 @@ static int subneg(byte c) 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,66 +583,87 @@ static void fgotsig(int sig) static void rawmode(void) { - tcsetattr(0, TCSADRAIN, &G.termios_raw); -} + if (G.do_termios) tcsetattr(0, TCSADRAIN, &G.termios_raw); +} 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 CONFIG_FEATURE_TELNET_TTYPE - ttype = getenv("TERM"); + ttype = getenv("TERM"); #endif memset(&G, 0, sizeof G); - if (tcgetattr(0, &G.termios_def) < 0) - exit(1); - - G.termios_raw = G.termios_def; - cfmakeraw(&G.termios_raw); - - if (argc < 2) show_usage(); - port = (argc > 2)? getport(argv[2]): 23; - - host = getserver(argv[1]); + if (tcgetattr(0, &G.termios_def) >= 0) { + G.do_termios = 1; - G.netfd = remote_connect(host, port); + G.termios_raw = G.termios_def; + cfmakeraw(&G.termios_raw); + } + + if (argc < 2) + bb_show_usage(); + +#ifdef CONFIG_FEATURE_TELNET_AUTOLOGIN + if (1 & getopt32(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_tcp_v4(&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 */ @@ -590,9 +678,9 @@ 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 { len = read(0, G.buf, DATABUFSIZE); @@ -600,15 +688,15 @@ extern int telnet_main(int argc, char** argv) doexit(0); 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 { len = read(G.netfd, G.buf, DATABUFSIZE); @@ -624,86 +712,3 @@ extern int telnet_main(int argc, char** argv) } } } - -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(void) -{ - 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: -*/