ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/telnet.b/
implement Telnet;
include "sys.m";
sys: Sys;
include "draw.m";
include "dial.m";
dial: Dial;
Connection: import dial;
Telnet: module
{
init: fn(ctxt: ref Draw->Context, args: list of string);
};
Debug: con 0;
Inbuf: adt {
fd: ref Sys->FD;
out: ref Outbuf;
buf: array of byte;
ptr: int;
nbyte: int;
};
Outbuf: adt {
buf: array of byte;
ptr: int;
};
BS: con 8; # ^h backspace character
BSW: con 23; # ^w bacspace word
BSL: con 21; # ^u backspace line
EOT: con 4; # ^d end of file
ESC: con 27; # hold mode
net: ref Connection;
stdin, stdout, stderr: ref Sys->FD;
# control characters
Se: con 240; # end subnegotiation
NOP: con 241;
Mark: con 242; # data mark
Break: con 243;
Interrupt: con 244;
Abort: con 245; # TENEX ^O
AreYouThere: con 246;
Erasechar: con 247; # erase last character
Eraseline: con 248; # erase line
GoAhead: con 249; # half duplex clear to send
Sb: con 250; # start subnegotiation
Will: con 251;
Wont: con 252;
Do: con 253;
Dont: con 254;
Iac: con 255;
# options
Binary, Echo, SGA, Stat, Timing,
Det, Term, EOR, Uid, Outmark,
Ttyloc, M3270, Padx3, Window, Speed,
Flow, Line, Xloc, Extend: con iota;
Opt: adt
{
name: string;
code: int;
noway: int;
remote: int; # remote value
local: int; # local value
};
opt := array[] of
{
Binary => Opt("binary", 0, 0, 0, 0),
Echo => Opt("echo", 1, 0, 0, 0),
SGA => Opt("suppress go ahead", 3, 0, 0, 0),
Stat => Opt("status", 5, 1, 0, 0),
Timing => Opt("timing", 6, 1, 0, 0),
Det=> Opt("det", 20, 1, 0, 0),
Term => Opt("terminal", 24, 0, 0, 0),
EOR => Opt("end of record", 25, 1, 0, 0),
Uid => Opt("uid", 26, 1, 0, 0),
Outmark => Opt("outmark", 27, 1, 0, 0),
Ttyloc => Opt("ttyloc", 28, 1, 0, 0),
M3270 => Opt("3270 mode", 29, 1, 0, 0),
Padx3 => Opt("pad x.3", 30, 1, 0, 0),
Window => Opt("window size", 31, 1, 0, 0),
Speed => Opt("speed", 32, 1, 0, 0),
Flow => Opt("flow control", 33, 1, 0, 0),
Line => Opt("line mode", 34, 1, 0, 0),
Xloc => Opt("X display loc", 35, 1, 0, 0),
Extend => Opt("Extended", 255, 1, 0, 0),
};
usage()
{
sys->fprint(stderr, "usage: telnet host [port]\n");
raise "fail:usage";
}
init(nil: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
stderr = sys->fildes(2);
stdout = sys->fildes(1);
stdin = sys->fildes(0);
dial = load Dial Dial->PATH;
if (len argv < 2)
usage();
argv = tl argv;
host := hd argv;
argv = tl argv;
port := "23";
if(argv != nil)
port = hd argv;
connect(host, port);
}
ccfd: ref Sys->FD;
connect(addr: string, port: string)
{
dest := dial->netmkaddr(addr, "tcp", port);
net = dial->dial(dest, nil);
if(net == nil) {
sys->fprint(stderr, "telnet: can't dial %s: %r\n", dest);
raise "fail:dial";
}
sys->fprint(stderr, "telnet: connected to %s\n", addr);
raw(1);
pidch := chan of int;
finished := chan of int;
spawn fromnet(pidch, finished);
spawn fromuser(pidch, finished);
pids := array[2] of {* => <-pidch};
kill(pids[<-finished == pids[0]]);
raw(0);
}
fromuser(pidch, finished: chan of int)
{
pidch <-= sys->pctl(0, nil);
b := array[1024] of byte;
while((n := sys->read(stdin, b, len b)) > 0) {
if (opt[Echo].remote == 0)
sys->write(stdout, b, n);
sys->write(net.dfd, b, n);
}
sys->fprint(stderr, "telnet: error reading stdin: %r\n");
finished <-= sys->pctl(0, nil);
}
getc(b: ref Inbuf): int
{
if(b.nbyte == 0) {
if(b.out != nil)
flushout(b.out);
b.nbyte = sys->read(b.fd, b.buf, len b.buf);
if(b.nbyte <= 0)
return -1;
b.ptr = 0;
}
b.nbyte--;
return int b.buf[b.ptr++];
}
putc(b: ref Outbuf, c: int)
{
b.buf[b.ptr++] = byte c;
if(b.ptr == len b.buf)
flushout(b);
}
flushout(b: ref Outbuf)
{
sys->write(stdout, b.buf, b.ptr);
b.ptr = 0;
}
BUFSIZE: con 2048;
fromnet(pidch, finished: chan of int)
{
pidch <-= sys->pctl(0, nil);
conout := ref Outbuf(array[BUFSIZE] of byte, 0);
netinp := ref Inbuf(net.dfd, conout, array[BUFSIZE] of byte, 0, 0);
loop: for(;;) {
c := getc(netinp);
case c {
-1 =>
break loop;
Iac =>
c = getc(netinp);
if(c != Iac) {
flushout(conout);
if(control(netinp, c) < 0)
break loop;
} else
putc(conout, c);
* =>
putc(conout, c);
}
}
sys->fprint(stderr, "telnet: remote host closed connection\n");
finished <-= sys->pctl(0, nil);
}
control(bp: ref Inbuf, c: int): int
{
r := 0;
case c {
AreYouThere =>
sys->fprint(net.dfd, "Inferno telnet\r\n");
Sb =>
r = sub(bp);
Will =>
r = will(bp);
Wont =>
r = wont(bp);
Do =>
r = doit(bp);
Dont =>
r = dont(bp);
Se =>
sys->fprint(stderr, "telnet: SE without an SB\n");
-1 =>
r = -1;
}
return r;
}
sub(bp: ref Inbuf): int
{
subneg: string;
i := 0;
for(;;){
c := getc(bp);
if(c == Iac) {
c = getc(bp);
if(c == Se)
break;
subneg[i++] = Iac;
}
if(c < 0)
return -1;
subneg[i++] = c;
}
if(i == 0)
return 0;
if (Debug)
sys->fprint(stderr, "telnet: sub(%s, %d, n = %d)\n", optname(subneg[0]), subneg[1], i);
for(i = 0; i < len opt; i++)
if(opt[i].code == subneg[0])
break;
if(i >= len opt)
return 0;
case i {
Term =>
sbsend(opt[Term].code, array of byte "network");
}
return 0;
}
sbsend(code: int, data: array of byte): int
{
buf := array[4+len data+2] of byte;
o := 4+len data;
buf[0] = byte Iac;
buf[1] = byte Sb;
buf[2] = byte code;
buf[3] = byte 0;
buf[4:] = data;
buf[o] = byte Iac;
o++;
buf[o] = byte Se;
return sys->write(net.dfd, buf, len buf);
}
will(bp: ref Inbuf): int
{
c := getc(bp);
if(c < 0)
return -1;
if (Debug)
sys->fprint(stderr, "telnet: will(%s)\n", optname(c));
for(i := 0; i < len opt; i++)
if(opt[i].code == c)
break;
if(i >= len opt) {
send3(bp, Iac, Dont, c);
return 0;
}
rv := 0;
if(opt[i].noway)
send3(bp, Iac, Dont, c);
else
if(opt[i].remote == 0)
rv |= send3(bp, Iac, Do, c);
if(opt[i].remote == 0)
rv |= change(bp, i, Will);
opt[i].remote = 1;
return rv;
}
wont(bp: ref Inbuf): int
{
c := getc(bp);
if(c < 0)
return -1;
if (Debug)
sys->fprint(stderr, "telnet: wont(%s)\n", optname(c));
for(i := 0; i < len opt; i++)
if(opt[i].code == c)
break;
if(i >= len opt)
return 0;
rv := 0;
if(opt[i].remote) {
rv |= change(bp, i, Wont);
rv |= send3(bp, Iac, Dont, c);
}
opt[i].remote = 0;
return rv;
}
doit(bp: ref Inbuf): int
{
c := getc(bp);
if(c < 0)
return -1;
if (Debug)
sys->fprint(stderr, "telnet: do(%s)\n", optname(c));
for(i := 0; i < len opt; i++)
if(opt[i].code == c)
break;
if(i >= len opt || opt[i].noway) {
send3(bp, Iac, Wont, c);
return 0;
}
rv := 0;
if(opt[i].local == 0) {
rv |= change(bp, i, Do);
rv |= send3(bp, Iac, Will, c);
}
opt[i].local = 1;
return rv;
}
dont(bp: ref Inbuf): int
{
c := getc(bp);
if(c < 0)
return -1;
if (Debug)
sys->fprint(stderr, "telnet: dont(%s)\n", optname(c));
for(i := 0; i < len opt; i++)
if(opt[i].code == c)
break;
if(i >= len opt || opt[i].noway)
return 0;
rv := 0;
if(opt[i].local){
opt[i].local = 0;
rv |= change(bp, i, Dont);
rv |= send3(bp, Iac, Wont, c);
}
opt[i].local = 0;
return rv;
}
change(bp: ref Inbuf, o: int, what: int): int
{
if(bp != nil)
{}
if(o != 0)
{}
if(what != 0)
{}
return 0;
}
send3(bp: ref Inbuf, c0: int, c1: int, c2: int): int
{
if (Debug)
sys->fprint(stderr, "telnet: reply(%s(%s))\n", negname(c1), optname(c2));
buf := array[3] of byte;
buf[0] = byte c0;
buf[1] = byte c1;
buf[2] = byte c2;
if (sys->write(bp.fd, buf, 3) != 3)
return -1;
return 0;
}
kill(pid: int): int
{
fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
if (fd == nil)
return -1;
if (sys->write(fd, array of byte "kill", 4) != 4)
return -1;
return 0;
}
negname(c: int): string
{
t := "Unknown";
case c {
Will => t = "will";
Wont => t = "wont";
Do => t = "do";
Dont => t = "dont";
}
return t;
}
optname(c: int): string
{
for (i := 0; i < len opt; i++)
if (opt[i].code == c)
return opt[i].name;
return "unknown";
}
raw(on: int)
{
if(ccfd == nil) {
ccfd = sys->open("/dev/consctl", Sys->OWRITE);
if(ccfd == nil) {
sys->fprint(stderr, "telnet: cannot open /dev/consctl: %r\n");
return;
}
}
if(on)
sys->fprint(ccfd, "rawon");
else
sys->fprint(ccfd, "rawoff");
}