Implement suggestion from Adam Slattery, (don't default to killing closing bug #1190.
[oweals/busybox.git] / telnet.c
index f276035270ba18542daff007c60a2334048f914e..ce82a0ee8829699aa584160d189e7eac4f3cca0b 100644 (file)
--- a/telnet.c
+++ b/telnet.c
  * initial revision
  * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen
  * <andersen@lineo.com> 
+ * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
+ * <jam@ltsp.org>
  *
  */
 
-
-#include "internal.h"
 #include <termios.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
+#include "busybox.h"
 
 #if 0
-#define DOTRACE 1
+static const int DOTRACE = 1;
 #endif
 
-#if DOTRACE
+#ifdef DOTRACE
 #include <arpa/inet.h> /* for inet_ntoa()... */
 #define TRACE(x, y) do { if (x) printf y; } while (0)
 #else
 #include <sys/time.h>
 #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;     
@@ -107,7 +109,7 @@ struct Globalvars {
 struct Globalvars * Gptr;
 #define G (*Gptr)
 #else
-struct Globalvars G;
+static struct Globalvars G;
 #endif
 
 static inline void iacflush()
@@ -134,14 +136,10 @@ static int local_bind(int port);
 
 /* Some globals */
 static int one = 1;
-static const char telnet_usage[] =
-       "telnet host [port]\n"
-#ifndef BB_FEATURE_TRIVIAL_HELP
-       "\nTelnet is used to establish interactive communication with another\n"
-       "computer over a network using the TELNET protocol.\n"
-#endif
-       ;
 
+#ifdef BB_FEATURE_TELNET_TTYPE
+static char *ttype;
+#endif
 
 static void doexit(int ev)
 {
@@ -199,7 +197,7 @@ 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
@@ -210,7 +208,7 @@ static void handlenetoutput()
        int i;
        byte * p = G.buf;
 
-       for (i = G.len; i > 0; i--, p++)
+       for (i = len; i > 0; i--, p++)
        {
                if (*p == 0x1d)
                {
@@ -220,16 +218,16 @@ static void handlenetoutput()
                if (*p == 0xff)
                        *p = 0x7f;
        }
-       write(G.netfd, G.buf, G.len);
+       write(G.netfd, G.buf, len);
 }
 
 
-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];
 
@@ -291,11 +289,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);
 }
 
 
@@ -328,11 +326,32 @@ static void putiac1(byte c)
 }
 #endif
 
+#ifdef BB_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
+
 /* void putiacstring (subneg strings) */
 
 /* ******************************* */
 
-char const escapecharis[] = "\r\nEscape character is ";
+static char const escapecharis[] = "\r\nEscape character is ";
 
 static void setConMode()
 {
@@ -340,7 +359,7 @@ static void setConMode()
        {
                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();
                }
        }
@@ -348,7 +367,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();
                }
        }
@@ -434,12 +453,29 @@ static inline void to_sga()
        return;
 }
 
+#ifdef BB_FEATURE_TELNET_TTYPE
+static inline void to_ttype()
+{
+       /* Tell server we will (or won't) do TTYPE */
+
+       if(ttype)
+               putiac2(WILL, TELOPT_TTYPE);
+       else
+               putiac2(WONT, TELOPT_TTYPE);
+
+       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;
+#endif
        default:                                to_notsup(c);   break;
        }
 }
@@ -447,7 +483,7 @@ static void telopt(byte c)
 
 /* ******************************* */
 
-/* subnegotiation -- ignore all */
+/* subnegotiation -- ignore all (except TTYPE) */
 
 static int subneg(byte c)
 {
@@ -456,6 +492,11 @@ static int subneg(byte c)
        case TS_SUB1:
                if (c == IAC)
                        G.telstate = TS_SUB2;
+#ifdef BB_FEATURE_TELNET_TTYPE
+               else
+               if (c == TELOPT_TTYPE)
+                       putiac_subopt(TELOPT_TTYPE,ttype);
+#endif
                break;
        case TS_SUB2:
                if (c == SE)
@@ -488,6 +529,7 @@ extern int telnet_main(int argc, char** argv)
 {
        struct in_addr host;
        int port;
+       int len;
 #ifdef USE_POLL
        struct pollfd ufds[2];
 #else  
@@ -495,6 +537,9 @@ extern int telnet_main(int argc, char** argv)
        int maxfd;
 #endif 
 
+#ifdef BB_FEATURE_TELNET_TTYPE
+    ttype = getenv("TERM");
+#endif
 
        memset(&G, 0, sizeof G);
 
@@ -502,15 +547,11 @@ 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);
+       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.netfd = remote_connect(host, port);
@@ -554,14 +595,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
@@ -570,16 +611,16 @@ 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);
                        }
                }
        }
@@ -591,7 +632,7 @@ static int getport(char * p)
 
        if ((unsigned)(port - 1 ) > 65534)
        {
-               fatalError("%s: bad port number\n", p);
+               error_msg_and_die("%s: bad port number", p);
        }
        return port;
 }
@@ -599,18 +640,15 @@ static int getport(char * p)
 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);
-       }
+       he = xgethostbyname(host);
        memcpy(&addr, he->h_addr, sizeof addr);
 
        TRACE(1, ("addr: %s\n", inet_ntoa(addr)));
-       
+
        return addr;
-}      
+}
 
 static int create_socket()
 {
@@ -619,7 +657,7 @@ static int create_socket()
 
 static void setup_sockaddr_in(struct sockaddr_in * addr, int port)
 {
-       memset(addr, 0, sizeof addr);
+       memset(addr, 0, sizeof(struct sockaddr_in));
        addr->sin_family = AF_INET;
        addr->sin_port = htons(port);
 }
@@ -658,7 +696,7 @@ static int remote_connect(struct in_addr addr, int port)
 
        if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0)
        {
-               fatalError("Unable to connect to remote host: %s\n", strerror(errno));
+               perror_msg_and_die("Unable to connect to remote host");
        }
        return s;
 }