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 characterBSW: con 23; # ^w bacspace wordBSL: con 21; # ^u backspace lineEOT: con 4; # ^d end of fileESC: con 27; # hold mode net: ref Connection;stdin, stdout, stderr: ref Sys->FD; # control charactersSe: con 240; # end subnegotiationNOP: con 241;Mark: con 242; # data markBreak: con 243;Interrupt: con 244;Abort: con 245; # TENEX ^OAreYouThere: con 246;Erasechar: con 247; # erase last characterEraseline: con 248; # erase lineGoAhead: con 249; # half duplex clear to sendSb: con 250; # start subnegotiationWill: con 251;Wont: con 252;Do: con 253;Dont: con 254;Iac: con 255; # optionsBinary, 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");}
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");
}