578 lines
12 KiB
C
578 lines
12 KiB
C
/* 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 */
|