* HISTORY
* Revision 3.1 1994/04/17 11:31:54 too
* initial revision
- * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen
- * <andersen@lineo.com>
+ * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersee@debian.org>
+ * 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/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
-#include <netdb.h>
+#include "busybox.h"
+
+#ifdef CONFIG_FEATURE_AUTOWIDTH
+# include <sys/ioctl.h>
+#endif
#if 0
-#define DOTRACE 1
+static const int DOTRACE = 1;
#endif
#ifdef DOTRACE
#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;
#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;
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];
break;
case TS_SUB1: /* Subnegotiation */
case TS_SUB2: /* Subnegotiation */
- if (subneg(c) == TRUE)
+ if (subneg(c))
G.telstate = TS_0;
break;
}
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);
}
}
#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();
}
}
{
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();
}
}
/* ******************************* */
-static void will_charmode()
+static void will_charmode(void)
{
G.charmode = CHM_TRY;
G.telflags |= (UF_ECHO | UF_SGA);
iacflush();
}
-static void do_linemode()
+static void do_linemode(void)
{
G.charmode = CHM_TRY;
G.telflags &= ~(UF_ECHO | UF_SGA);
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; }
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 */
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)
{
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)
}
-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
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);
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);
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
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"
tab-width: 4
End:
*/
-