diff options
author | Axel Beckert <abe@deuxchevaux.org> | 2011-10-05 00:05:26 +0200 |
---|---|---|
committer | Axel Beckert <abe@deuxchevaux.org> | 2011-10-05 00:05:26 +0200 |
commit | be44f424d366f2b3ddcfd857763562c4a7e0580a (patch) | |
tree | 33c41fceae0b89144b5bc14b96456fa8444f1281 /teln.c | |
parent | ed169177fbfd1fedb0750f3ba18737aba5596451 (diff) | |
download | screen-be44f424d366f2b3ddcfd857763562c4a7e0580a.tar.gz |
Imported Upstream version 4.1.0~20110819git450e8f3upstream/4.1.0_20110819git450e8f3
Diffstat (limited to 'teln.c')
-rw-r--r-- | teln.c | 580 |
1 files changed, 580 insertions, 0 deletions
@@ -0,0 +1,580 @@ +/* Copyright (c) 2008, 2009 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Micah Cowan (micah@cowan.name) + * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net) + * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007 + * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) + * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) + * Copyright (c) 1987 Oliver Laumann + * + * 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 3, 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 (see the file COPYING); if not, see + * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + **************************************************************** + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <netdb.h> + +#include "config.h" + +#ifdef BUILTIN_TELNET + +#include "screen.h" +#include "extern.h" + +extern struct win *fore; +extern struct layer *flayer; +extern int visual_bell; +extern char screenterm[]; + +static void TelReply __P((struct win *, char *, int)); +static void TelDocmd __P((struct win *, int, int)); +static void TelDosub __P((struct win *)); + +#define TEL_DEFPORT 23 +#define TEL_CONNECTING (-2) + +#define TC_IAC 255 +#define TC_DONT 254 +#define TC_DO 253 +#define TC_WONT 252 +#define TC_WILL 251 +#define TC_SB 250 +#define TC_BREAK 243 +#define TC_SE 240 + +#define TC_S "S b swWdDc" + +#define TO_BINARY 0 +#define TO_ECHO 1 +#define TO_SGA 3 +#define TO_TM 6 +#define TO_TTYPE 24 +#define TO_NAWS 31 +#define TO_TSPEED 32 +#define TO_LFLOW 33 +#define TO_LINEMODE 34 +#define TO_XDISPLOC 35 +#define TO_NEWENV 39 + +#define TO_S "be c t wsf xE E" + + +static unsigned char tn_init[] = { + TC_IAC, TC_DO, TO_SGA, + TC_IAC, TC_WILL, TO_TTYPE, + TC_IAC, TC_WILL, TO_NAWS, + TC_IAC, TC_WILL, TO_LFLOW, +}; + +static void +tel_connev_fn(ev, data) +struct event *ev; +char *data; +{ + struct win *p = (struct win *)data; + if (connect(p->w_ptyfd, (struct sockaddr *)&p->w_telsa, sizeof(p->w_telsa)) && errno != EISCONN) + { + char buf[1024]; + buf[0] = ' '; + strncpy(buf + 1, strerror(errno), sizeof(buf) - 2); + buf[sizeof(buf) - 1] = 0; + WriteString(p, buf, strlen(buf)); + WindowDied(p, 0, 0); + return; + } + WriteString(p, "connected.\r\n", 12); + evdeq(&p->w_telconnev); + p->w_telstate = 0; +} + +int +TelOpen(args) +char **args; +{ + int fd; + int on = 1; + + if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) + { + Msg(errno, "TelOpen: socket"); + return -1; + } + if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on))) + Msg(errno, "TelOpen: setsockopt SO_OOBINLINE"); + return fd; +} + +int +TelConnect(p) +struct win *p; +{ + int port = TEL_DEFPORT; + struct hostent *hp; + char **args; + char buf[256]; + + args = p->w_cmdargs + 1; + + if (!*args) + { + Msg(0, "Usage: screen //telnet host [port]"); + return -1; + } + if (args[1]) + port = atoi(args[1]); + p->w_telsa.sin_family = AF_INET; + if((p->w_telsa.sin_addr.s_addr = inet_addr(*args)) == -1) + { + if ((hp = gethostbyname(*args)) == NULL) + { + Msg(0, "unknown host: %s", *args); + return -1; + } + if (hp->h_length != sizeof(p->w_telsa.sin_addr.s_addr) || hp->h_addrtype != AF_INET) + { + Msg(0, "Bad address type for %s", hp->h_name); + return -1; + } + bcopy((char *)hp->h_addr,(char *)&p->w_telsa.sin_addr.s_addr, hp->h_length); + p->w_telsa.sin_family = hp->h_addrtype; + } + p->w_telsa.sin_port = htons(port); + if (port != TEL_DEFPORT) + sprintf(buf, "Trying %s %d...", inet_ntoa(p->w_telsa.sin_addr), port); + else + sprintf(buf, "Trying %s...", inet_ntoa(p->w_telsa.sin_addr)); + WriteString(p, buf, strlen(buf)); + if (connect(p->w_ptyfd, (struct sockaddr *)&p->w_telsa, sizeof(p->w_telsa))) + { + if (errno == EINPROGRESS) + { + p->w_telstate = TEL_CONNECTING; + p->w_telconnev.fd = p->w_ptyfd; + p->w_telconnev.handler = tel_connev_fn; + p->w_telconnev.data = (char *)p; + p->w_telconnev.type = EV_WRITE; + p->w_telconnev.pri = 1; + debug("telnet connect in progress...\n"); + evenq(&p->w_telconnev); + } + else + { + Msg(errno, "TelOpen: connect"); + return -1; + } + } + else + WriteString(p, "connected.\r\n", 12); + if (port == TEL_DEFPORT) + TelReply(p, (char *)tn_init, sizeof(tn_init)); + return 0; +} + +int +TelIsline(p) +struct win *p; +{ + return !fore->w_telropts[TO_SGA]; +} + +void +TelProcessLine(bufpp, lenp) +char **bufpp; +int *lenp; +{ + int echo = !fore->w_telropts[TO_ECHO]; + unsigned char c; + char *tb; + int tl; + + char *buf = *bufpp; + int l = *lenp; + while (l--) + { + c = *(unsigned char *)buf++; + if (fore->w_telbufl + 2 >= IOSIZE) + { + WBell(fore, visual_bell); + continue; + } + if (c == '\r') + { + if (echo) + WriteString(fore, "\r\n", 2); + fore->w_telbuf[fore->w_telbufl++] = '\r'; + fore->w_telbuf[fore->w_telbufl++] = '\n'; + tb = fore->w_telbuf; + tl = fore->w_telbufl; + LayProcess(&tb, &tl); + fore->w_telbufl = 0; + continue; + } + if (c == '\b' && fore->w_telbufl > 0) + { + if (echo) + { + WriteString(fore, (char *)&c, 1); + WriteString(fore, " ", 1); + WriteString(fore, (char *)&c, 1); + } + fore->w_telbufl--; + } + if ((c >= 0x20 && c <= 0x7e) || c >= 0xa0) + { + if (echo) + WriteString(fore, (char *)&c, 1); + fore->w_telbuf[fore->w_telbufl++] = c; + } + } + *lenp = 0; +} + +int +DoTelnet(buf, lenp, f) +char *buf; +int *lenp; +int f; +{ + int echo = !fore->w_telropts[TO_ECHO]; + int cmode = fore->w_telropts[TO_SGA]; + int bin = fore->w_telropts[TO_BINARY]; + char *p = buf, *sbuf; + int trunc = 0; + int c; + int l = *lenp; + + sbuf = p; + while (l-- > 0) + { + c = *(unsigned char *)p++; + if (c == TC_IAC || (c == '\r' && (l ==0 || *p != '\n') && cmode && !bin)) + { + if (cmode && echo) + { + WriteString(fore, sbuf, p - sbuf); + sbuf = p; + } + if (f-- <= 0) + { + trunc++; + l--; + } + if (l < 0) + { + p--; /* drop char */ + break; + } + if (l) + bcopy(p, p + 1, l); + if (c == TC_IAC) + *p++ = c; + else if (c == '\r') + *p++ = 0; + else if (c == '\n') + { + p[-1] = '\r'; + *p++ = '\n'; + } + } + } + *lenp = p - buf; + return trunc; +} + +/* modifies data in-place, returns new length */ +int +TelIn(p, buf, len, free) +struct win *p; +char *buf; +int len; +int free; +{ + char *rp, *wp; + int c; + + rp = wp = buf; + while (len-- > 0) + { + c = *(unsigned char *)rp++; + + if (p->w_telstate >= TC_WILL && p->w_telstate <= TC_DONT) + { + TelDocmd(p, p->w_telstate, c); + p->w_telstate = 0; + continue; + } + if (p->w_telstate == TC_SB || p->w_telstate == TC_SE) + { + if (p->w_telstate == TC_SE && c == TC_IAC) + p->w_telsubidx--; + if (p->w_telstate == TC_SE && c == TC_SE) + { + p->w_telsubidx--; + TelDosub(p); + p->w_telstate = 0; + continue; + } + if (p->w_telstate == TC_SB && c == TC_IAC) + p->w_telstate = TC_SE; + else + p->w_telstate = TC_SB; + p->w_telsubbuf[p->w_telsubidx] = c; + if (p->w_telsubidx < sizeof(p->w_telsubbuf) - 1) + p->w_telsubidx++; + continue; + } + if (p->w_telstate == TC_IAC) + { + if ((c >= TC_WILL && c <= TC_DONT) || c == TC_SB) + { + p->w_telsubidx = 0; + p->w_telstate = c; + continue; + } + p->w_telstate = 0; + if (c != TC_IAC) + continue; + } + else if (c == TC_IAC) + { + p->w_telstate = c; + continue; + } + if (p->w_telstate == '\r') + { + p->w_telstate = 0; + if (c == 0) + continue; /* suppress trailing \0 */ + } + else if (c == '\n' && !p->w_telropts[TO_SGA]) + { + /* oops... simulate terminal line mode: insert \r */ + if (wp + 1 == rp) + { + if (free-- > 0) + { + if (len) + bcopy(rp, rp + 1, len); + rp++; + *wp++ = '\r'; + } + } + else + *wp++ = '\r'; + } + if (c == '\r') + p->w_telstate = c; + *wp++ = c; + } + return wp - buf; +} + +static void +TelReply(p, str, len) +struct win *p; +char *str; +int len; +{ + if (len <= 0) + return; + if (p->w_inlen + len > IOSIZE) + { + Msg(0, "Warning: telnet protocol overrun!"); + return; + } + bcopy(str, p->w_inbuf + p->w_inlen, len); + p->w_inlen += len; +} + +static void +TelDocmd(p, cmd, opt) +struct win *p; +int cmd, opt; +{ + unsigned char b[3]; + int repl = 0; + + if (cmd == TC_WONT) + debug2("[<-WONT %c %d]\n", TO_S[opt], opt); + if (cmd == TC_WILL) + debug2("[<-WILL %c %d]\n", TO_S[opt], opt); + if (cmd == TC_DONT) + debug2("[<-DONT %c %d]\n", TO_S[opt], opt); + if (cmd == TC_DO) + debug2("[<-DO %c %d]\n", TO_S[opt], opt); + + switch(cmd) + { + case TC_WILL: + if (p->w_telropts[opt] || opt == TO_TM) + return; + repl = TC_DONT; + if (opt == TO_ECHO || opt == TO_SGA || opt == TO_BINARY) + { + p->w_telropts[opt] = 1; + /* setcon(); */ + repl = TC_DO; + } + break; + case TC_WONT: + if (!p->w_telropts[opt] || opt == TO_TM) + return; + repl = TC_DONT; +#if 0 + if (opt == TO_ECHO || opt == TO_SGA) + setcon(); +#endif + p->w_telropts[opt] = 0; + break; + case TC_DO: + if (p->w_telmopts[opt]) + return; + repl = TC_WONT; + if (opt == TO_TTYPE || opt == TO_SGA || opt == TO_BINARY || opt == TO_NAWS || opt == TO_TM || opt == TO_LFLOW) + { + repl = TC_WILL; + p->w_telmopts[opt] = 1; + } + p->w_telmopts[TO_TM] = 0; + break; + case TC_DONT: + if (!p->w_telmopts[opt]) + return; + repl = TC_WONT; + p->w_telmopts[opt] = 0; + break; + } + b[0] = TC_IAC; + b[1] = repl; + b[2] = opt; + + if (repl == TC_WONT) + debug2("[->WONT %c %d]\n", TO_S[opt], opt); + if (repl == TC_WILL) + debug2("[->WILL %c %d]\n", TO_S[opt], opt); + if (repl == TC_DONT) + debug2("[->DONT %c %d]\n", TO_S[opt], opt); + if (repl == TC_DO) + debug2("[->DO %c %d]\n", TO_S[opt], opt); + + TelReply(p, (char *)b, 3); + if (cmd == TC_DO && opt == TO_NAWS) + TelWindowSize(p); +} + + +static void +TelDosub(p) +struct win *p; +{ + char trepl[20 + 6 + 1]; + int l; + + switch(p->w_telsubbuf[0]) + { + case TO_TTYPE: + if (p->w_telsubidx != 2 || p->w_telsubbuf[1] != 1) + return; + l = strlen(screenterm); + if (l >= 20) + break; + sprintf(trepl, "%c%c%c%c%s%c%c", TC_IAC, TC_SB, TO_TTYPE, 0, screenterm, TC_IAC, TC_SE); + TelReply(p, trepl, l + 6); + break; + case TO_LFLOW: + if (p->w_telsubidx != 2) + return; + debug1("[FLOW %d]\r\n", p->w_telsubbuf[1]); + break; + default: + break; + } +} + +void +TelBreak(p) +struct win *p; +{ + static unsigned char tel_break[] = { TC_IAC, TC_BREAK }; + TelReply(p, (char *)tel_break, 2); +} + +void +TelWindowSize(p) +struct win *p; +{ + char s[20], trepl[20], *t; + int i; + + debug2("TelWindowSize %d %d\n", p->w_width, p->w_height); + if (p->w_width == 0 || p->w_height == 0 || !p->w_telmopts[TO_NAWS]) + return; + sprintf(s, "%c%c%c%c%c%c%c%c%c", TC_SB, TC_SB, TO_NAWS, p->w_width / 256, p->w_width & 255, p->w_height / 256, p->w_height & 255, TC_SE, TC_SE); + t = trepl; + for (i = 0; i < 9; i++) + if ((unsigned char)(*t++ = s[i]) == TC_IAC) + *t++ = TC_IAC; + trepl[0] = TC_IAC; + t[-2] = TC_IAC; + debug(" - sending"); + for (i = 0; trepl + i < t; i++) + debug1(" %02x", (unsigned char)trepl[i]); + debug("\n"); + TelReply(p, trepl, t - trepl); +} + +static char tc_s[] = TC_S; +static char to_s[] = TO_S; + +void +TelStatus(p, buf, l) +struct win *p; +char *buf; +int l; +{ + int i; + + *buf++ = '['; + for (i = 0; to_s[i]; i++) + { + if (to_s[i] == ' ' || p->w_telmopts[i] == 0) + continue; + *buf++ = to_s[i]; + } + *buf++ = ':'; + for (i = 0; to_s[i]; i++) + { + if (to_s[i] == ' ' || p->w_telropts[i] == 0) + continue; + *buf++ = to_s[i]; + } + if (p->w_telstate == TEL_CONNECTING) + buf[-1] = 'C'; + else if (p->w_telstate && p->w_telstate != '\r') + { + *buf++ = ':'; + *buf++ = tc_s[p->w_telstate - TC_SE]; + } + *buf++ = ']'; + *buf = 0; + return; +} + +#endif /* BUILTIN_TELNET */ |