summaryrefslogtreecommitdiffstats
path: root/teln.c
diff options
context:
space:
mode:
Diffstat (limited to 'teln.c')
-rw-r--r--teln.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/teln.c b/teln.c
new file mode 100644
index 0000000..ff18bf7
--- /dev/null
+++ b/teln.c
@@ -0,0 +1,578 @@
+/* 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
+ * https://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 <stdio.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[];
+extern int af;
+
+static void TelReply __P((struct win *, char *, int));
+static void TelDocmd __P((struct win *, int, int));
+static void TelDosub __P((struct win *));
+
+// why TEL_DEFPORT has "
+#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
+TelOpenAndConnect(struct win *p) {
+ int fd, on = 1;
+ char buf[256];
+
+ struct addrinfo hints, *res0, *res;
+
+ if (!(p->w_cmdargs[1])) {
+ Msg(0, "Usage: screen //telnet host [port]");
+ return -1;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ if(getaddrinfo(p->w_cmdargs[1], p->w_cmdargs[2] ? p->w_cmdargs[2] : TEL_DEFPORT, &hints, &res0)) {
+ Msg(0, "unknown host: %s", p->w_cmdargs[1]);
+ return -1;
+ }
+
+ for(res = res0; res; res = res->ai_next) {
+ if((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
+ if(res->ai_next)
+ continue;
+ else {
+ Msg(errno, "TelOpenAndConnect: socket");
+ freeaddrinfo(res0);
+ return -1;
+ }
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)))
+ Msg(errno, "TelOpenAndConnect: setsockopt SO_OOBINLINE");
+
+ if (p->w_cmdargs[2] && strcmp(p->w_cmdargs[2], TEL_DEFPORT))
+ snprintf(buf, 256, "Trying %s %s...", p->w_cmdargs[1], p->w_cmdargs[2]);
+ else
+ snprintf(buf, 256, "Trying %s...", p->w_cmdargs[1]);
+
+ WriteString(p, buf, strlen(buf));
+ if (connect(fd, res->ai_addr, res->ai_addrlen)) {
+ if (errno == EINPROGRESS) {
+ p->w_telstate = TEL_CONNECTING;
+ p->w_telconnev.fd = fd;
+ 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 {
+ close(fd);
+ if(res->ai_next)
+ continue;
+ else {
+ Msg(errno, "TelOpenAndConnect: connect");
+ freeaddrinfo(res0);
+ return -1;
+ }
+ }
+ }
+ else
+ WriteString(p, "connected.\r\n", 12);
+
+ if (!(p->w_cmdargs[2] && strcmp(p->w_cmdargs[2], TEL_DEFPORT)))
+ TelReply(p, (char *)tn_init, sizeof(tn_init));
+
+ p->w_ptyfd = fd;
+ memcpy(&p->w_telsa, &res->ai_addr, sizeof(res->ai_addr));
+ freeaddrinfo(res0);
+ return 0;
+ }
+ return -1;
+}
+
+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[MAXTERMLEN + 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 >= MAXTERMLEN)
+ 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 */