X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Ftelnet.c;h=0c794b62f3b1196f2852230e8be16e3be0051e89;hb=1652855fbc830f41c7ee56ece1a30b328c4fb395;hp=40cf7a128a8ab9bfe76fb5584e34c1725a0c191a;hpb=616d13bcd1a431eb21d1d3e48b17be6c26443c1d;p=oweals%2Fbusybox.git diff --git a/networking/telnet.c b/networking/telnet.c index 40cf7a128..0c794b62f 100644 --- a/networking/telnet.c +++ b/networking/telnet.c @@ -25,13 +25,12 @@ * 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 + * * */ - -#include "internal.h" #include #include #include @@ -43,10 +42,14 @@ #include #include #include -#include +#include "busybox.h" + +#ifdef CONFIG_FEATURE_AUTOWIDTH +# include +#endif #if 0 -#define DOTRACE 1 +static const int DOTRACE = 1; #endif #ifdef DOTRACE @@ -63,39 +66,40 @@ #include #endif -#define DATABUFSIZE 128 -#define IACBUFSIZE 128 +#define DATABUFSIZE 128 +#define IACBUFSIZE 128 -#define CHM_TRY 0 -#define CHM_ON 1 -#define CHM_OFF 2 +static const int CHM_TRY = 0; +static const int CHM_ON = 1; +static const int CHM_OFF = 2; -#define UF_ECHO 0x01 -#define UF_SGA 0x02 +static const int UF_ECHO = 0x01; +static const int UF_SGA = 0x02; -#define TS_0 1 -#define TS_IAC 2 -#define TS_OPT 3 -#define TS_SUB1 4 -#define TS_SUB2 5 +enum { + TS_0 = 1, + TS_IAC = 2, + TS_OPT = 3, + TS_SUB1 = 4, + TS_SUB2 = 5, +}; #define WriteCS(fd, str) write(fd, str, sizeof str -1) typedef unsigned char byte; /* use globals to reduce size ??? */ /* test this hypothesis later */ -struct Globalvars { +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; /* buffer to handle telnet negotiations */ - char * iacbuf; + char iacbuf[IACBUFSIZE]; short iaclen; /* could even use byte */ struct termios termios_def; struct termios termios_raw; @@ -106,42 +110,40 @@ struct Globalvars { #ifdef USE_GLOBALVAR_PTR struct Globalvars * Gptr; #define G (*Gptr) -#else -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 CONFIG_FEATURE_TELNET_TTYPE +static char *ttype; +#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; @@ -191,37 +193,53 @@ static void conescape() 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; + * 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 = G.buf; + byte outbuf[4*DATABUFSIZE]; - for (i = G.len; i > 0; i--, p++) + 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]; @@ -273,7 +291,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; } @@ -283,11 +301,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); } @@ -320,19 +338,60 @@ static void putiac1(byte c) } #endif +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static void putiac_subopt(byte c, char *str) +{ + 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_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) */ /* ******************************* */ -char const escapecharis[] = "\r\nEscape character is "; +static char const escapecharis[] = "\r\nEscape character is "; -static void setConMode() +static void setConMode(void) { if (G.telflags & UF_ECHO) { if (G.charmode == CHM_TRY) { G.charmode = CHM_ON; - fprintf(stdout, "\r\nEntering character mode%s'^]'.\r\n", escapecharis); + printf("\r\nEntering character mode%s'^]'.\r\n", escapecharis); rawmode(); } } @@ -340,7 +399,7 @@ static void setConMode() { if (G.charmode != CHM_OFF) { G.charmode = CHM_OFF; - fprintf(stdout, "\r\nEntering line mode%s'^C'.\r\n", escapecharis); + printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis); cookmode(); } } @@ -348,7 +407,7 @@ static void setConMode() /* ******************************* */ -static void will_charmode() +static void will_charmode(void) { G.charmode = CHM_TRY; G.telflags |= (UF_ECHO | UF_SGA); @@ -359,7 +418,7 @@ static void will_charmode() iacflush(); } -static void do_linemode() +static void do_linemode(void) { G.charmode = CHM_TRY; G.telflags &= ~(UF_ECHO | UF_SGA); @@ -378,7 +437,7 @@ 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; } @@ -405,7 +464,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 */ @@ -426,20 +485,52 @@ static inline void to_sga() return; } +#ifdef CONFIG_FEATURE_TELNET_TTYPE +static inline void to_ttype(void) +{ + /* Tell server we will (or won't) do TTYPE */ + + if(ttype) + putiac2(WILL, TELOPT_TTYPE); + else + putiac2(WONT, TELOPT_TTYPE); + + 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; - default: to_notsup(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 +#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 */ +/* subnegotiation -- ignore all (except TTYPE,NAWS) */ static int subneg(byte c) { @@ -448,6 +539,11 @@ static int subneg(byte c) case TS_SUB1: if (c == IAC) G.telstate = TS_SUB2; +#ifdef CONFIG_FEATURE_TELNET_TTYPE + else + if (c == TELOPT_TTYPE) + putiac_subopt(TELOPT_TTYPE,ttype); +#endif break; case TS_SUB2: if (c == SE) @@ -466,20 +562,21 @@ static void fgotsig(int sig) } -static void rawmode() +static void rawmode(void) { tcsetattr(0, TCSADRAIN, &G.termios_raw); } -static void cookmode() +static void cookmode(void) { tcsetattr(0, TCSADRAIN, &G.termios_def); } extern int telnet_main(int argc, char** argv) { - struct in_addr host; - int port; + char *host; + char *port; + int len; #ifdef USE_POLL struct pollfd ufds[2]; #else @@ -487,6 +584,17 @@ extern int telnet_main(int argc, char** argv) int maxfd; #endif +#ifdef CONFIG_FEATURE_AUTOWIDTH + struct winsize winp; + if( ioctl(0, TIOCGWINSZ, &winp) == 0 ) { + win_width = winp.ws_col; + win_height = winp.ws_row; + } +#endif + +#ifdef CONFIG_FEATURE_TELNET_TTYPE + ttype = getenv("TERM"); +#endif memset(&G, 0, sizeof G); @@ -494,18 +602,16 @@ extern int telnet_main(int argc, char** argv) exit(1); G.termios_raw = G.termios_def; - cfmakeraw(&G.termios_raw); - if (argc < 2) usage(telnet_usage); - port = (argc > 2)? getport(argv[2]): 23; + if (argc < 2) show_usage(); + port = (argc > 2)? argv[2] : "23"; - G.buf = xmalloc(DATABUFSIZE); - G.iacbuf = xmalloc(IACBUFSIZE); + host = argv[1]; - host = getserver(argv[1]); + G.netfd = xconnect(host, port); - G.netfd = remote_connect(host, port); + setsockopt(G.netfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one); signal(SIGINT, fgotsig); @@ -546,14 +652,14 @@ extern int telnet_main(int argc, char** argv) if (FD_ISSET(0, &rfds)) #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)); + TRACE(0, ("Read con: %d\n", len)); - handlenetoutput(); + handlenetoutput(len); } #ifdef USE_POLL @@ -562,99 +668,21 @@ extern int telnet_main(int argc, char** argv) if (FD_ISSET(G.netfd, &rfds)) #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) - { - fatalError("%s: bad port number\n", p); - } - return port; -} - -static struct in_addr getserver(char * host) -{ - struct in_addr addr; - - struct hostent * he; - if ((he = gethostbyname(host)) == NULL) - { - fatalError("%s: Unkonwn host\n", 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 addr); - 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) - { - fatalError("Unable to connect to remote host: %s\n", strerror(errno)); - } - return s; -} - /* Local Variables: c-file-style: "linux" @@ -662,4 +690,3 @@ c-basic-offset: 4 tab-width: 4 End: */ -