implement Socksnet;
include "sys.m";
sys: Sys;
Qid: import Sys;
include "draw.m";
include "tables.m";
tables: Tables;
Table: import tables;
include "string.m";
str: String;
include "styx.m";
styx: Styx;
Rmsg, Tmsg: import styx;
include "styxservers.m";
styxservers: Styxservers;
Fid, Styxserver, Navigator, Navop, Eperm: import styxservers;
readstr: import styxservers;
nametree: Nametree;
Tree: import nametree;
include "ip.m";
ip: IP;
IPaddr: import ip;
Socksnet: module {
init: fn(nil: ref Draw->Context, argv: list of string);
};
Cvshift: con 4;
Qroot, Qtopdir, Qclone, Qconv,
Qdata, Qremote, Qlocal, Qstatus, Qctl: con iota;
server: string;
nomod(p: string)
{
sys->fprint(sys->fildes(2), "socksnet: cannot load %s: %r\n", p);
raise "fail:bad module";
}
init(nil: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
tables = load Tables Tables->PATH;
str = load String String->PATH;
styx = load Styx Styx->PATH;
if(styx == nil)
nomod(Styx->PATH);
styx->init();
styxservers = load Styxservers Styxservers->PATH;
if(styxservers == nil)
nomod(Styxservers->PATH);
styxservers->init(styx);
nametree = load Nametree Nametree->PATH;
nametree->init();
ip = load IP IP->PATH;
ip->init();
if(len argv != 2){
sys->fprint(sys->fildes(2), "usage: socksnet socks_server\n");
raise "fail:usage";
}
sys->pctl(Sys->FORKNS|Sys->FORKFD, nil);
server = hd tl argv;
(tree, treeop) := nametree->start();
p := qmk(Qroot, 0);
tree.create(p, dir(".", 8r555|Sys->DMDIR, qmk(Qroot, 0)));
tree.create(p, dir("socks", 8r555|Sys->DMDIR, qmk(Qtopdir, 0)));
tree.create(qmk(Qtopdir, 0), dir("clone", 8r666, qmk(Qclone, 0)));
(tchan, srv) := Styxserver.new(sys->fildes(0), Navigator.new(treeop), p);
serveloop(tchan, tree, srv);
tree.quit();
}
Req: adt {
pid: int;
};
Conv: adt {
id: int;
fd: ref Sys->FD;
local: string;
remote: string;
refs: int;
};
serveloop(tchan: chan of ref Tmsg, tree: ref Tree, srv: ref Styxserver)
{
blocking := Table[ref Req].new(11, nil);
convs := Table[ref Conv].new(11, nil);
freeconvs: list of ref Conv;
maxcid := 0;
replies := chan of ref Rmsg;
for(;;)alt{
r := <-replies =>
blocking.del(r.tag);
srv.reply(r);
gm := <-tchan =>
if(gm == nil)
return;
pick m := gm {
Flush =>
req := blocking.find(m.oldtag);
if(req != nil)
kill(req.pid);
srv.reply(ref Rmsg.Flush(m.tag));
Open =>
(fid, mode, nil, err) := srv.canopen(m);
if(fid == nil){
srv.reply(ref Rmsg.Error(m.tag, err));
break;
}
case qtype(fid.path){
Qclone =>
nc: ref Conv;
if(freeconvs == nil){
nc = ref Conv(maxcid++, nil, nil, nil, 0);
convs.add(nc.id, nc);
p := qmk(Qconv, nc.id);
tree.create(qmk(Qtopdir, 0), dir(string nc.id, 8r555|Sys->DMDIR, p));
tree.create(p, dir("ctl", 8r666, qmk(Qctl, nc.id)));
tree.create(p, dir("data", 8r666, qmk(Qdata, nc.id)));
tree.create(p, dir("remote", 8r444, qmk(Qremote, nc.id)));
tree.create(p, dir("local", 8r444, qmk(Qlocal, nc.id)));
tree.create(p, dir("status", 8r444, qmk(Qstatus, nc.id)));
}else
(nc, freeconvs) = (hd freeconvs, tl freeconvs);
fid.open(mode, Qid(qmk(Qctl, nc.id), 0, 0));
srv.reply(ref Rmsg.Open(m.tag, Qid(fid.path, 0, fid.qtype), 0));
nc.refs++;
Qdata =>
conv := convs.find(qconv(fid.path));
if(conv.fd == nil)
srv.reply(ref Rmsg.Error(m.tag, "no convection"));
else{
srv.default(gm);
conv.refs++;
}
* =>
srv.default(gm);
}
Write =>
(fid, err) := srv.canwrite(m);
if(fid == nil){
srv.reply(ref Rmsg.Error(m.tag, err));
break;
}
case qtype(fid.path) {
Qctl =>
t := str->unquoted(string m.data);
if(t == nil){
srv.reply(ref Rmsg.Error(m.tag, "no request"));
break;
}
case hd t {
"connect" =>
spawn connectproc(sync := chan of int, m, hd tl t, convs.find(qconv(fid.path)), replies);
blocking.add(m.tag, ref Req(<-sync));
* =>
srv.reply(ref Rmsg.Error(m.tag, "unknown request"));
}
Qdata =>
spawn writeproc(sync := chan of int, m, convs.find(qconv(fid.path)), replies);
blocking.add(m.tag, ref Req(<-sync));
* =>
srv.reply(ref Rmsg.Error(m.tag, Eperm));
}
Read =>
(fid, err) := srv.canread(m);
if(fid == nil){
srv.reply(ref Rmsg.Error(m.tag, err));
break;
}
case qtype(fid.path) {
Qctl =>
srv.reply(readstr(m, string convs.find(qconv(fid.path)).id));
Qdata =>
spawn readproc(sync := chan of int, m, convs.find(qconv(fid.path)), replies);
blocking.add(m.tag, ref Req(<-sync));
Qstatus =>
s := "Open";
if(convs.find(qconv(fid.path)).fd == nil)
s = "Closed";
srv.reply(readstr(m, s));
* =>
srv.default(m);
}
Clunk =>
fid := srv.getfid(m.fid);
if(fid == nil){
srv.reply(ref Rmsg.Error(m.tag, "bad fid"));
break;
}
case qtype(fid.path) {
Qdata or
Qctl =>
conv := convs.find(qconv(fid.path));
if(--conv.refs == 0){
conv.fd = nil;
freeconvs = conv :: freeconvs;
}
}
srv.default(gm);
* =>
srv.default(gm);
}
}
}
connectproc(sync: chan of int, m: ref Tmsg.Write, addr: string, conv: ref Conv, r: chan of ref Rmsg)
{
sync <-= sys->pctl(0, nil);
err := connect(conv, "tcp!"+addr);
if(err != nil)
r <-= ref Rmsg.Error(m.tag, err);
else
r <-= ref Rmsg.Write(m.tag, len m.data);
}
connect(conv: ref Conv, addr: string): string
{
ipaddr, ipport: array of byte;
(nil, ipa, err) := csquery(addr);
if(ipa == nil)
return err;
(ipaddr, ipport, err) = mkipaddr(ipa);
if(err != nil)
return err;
(rc, c) := dial(netmkaddr(server, "tcp", "socks"));
if(rc == -1)
return sys->sprint("cannot dial socks server: %r");
d := array[1+1+2+4+1] of byte;
d[0] = byte 4; # version
d[1] = byte 1; # tcp
d[2:] = ipport;
d[4:] = ipaddr;
d[8] = byte 0; # username
if(sys->write(c.dfd, d, len d) < 0)
return (sys->sprint("write: %r"));
d = array[8] of byte;
n := sys->read(c.dfd, d, len d);
if(n != 8)
return "too few bytes in socks reply";
if(d[0] != byte 0 || d[1] != byte 16r5a)
return "request rejected";
conv.fd = c.dfd;
return nil;
}
writeproc(sync: chan of int, m: ref Tmsg.Write, conv: ref Conv, r: chan of ref Rmsg)
{
sync <-= sys->pctl(0, nil);
n := sys->write(conv.fd, m.data, len m.data);
if(n < 0)
r <-= ref Rmsg.Error(m.tag, sys->sprint("%r"));
else
r <-= ref Rmsg.Write(m.tag, n);
}
readproc(sync: chan of int, m: ref Tmsg.Read, conv: ref Conv, r: chan of ref Rmsg)
{
sync <-= sys->pctl(0, nil);
data := array[m.count] of byte;
n := sys->read(conv.fd, data, m.count);
if(n < 0)
r <-= ref Rmsg.Error(m.tag, sys->sprint("%r"));
else
r <-= ref Rmsg.Read(m.tag, data[0:n]);
}
mkipaddr(addr: string): (array of byte, array of byte, string)
{
(nt, toks) := sys->tokenize(addr, "!");
if(nt != 2)
return (nil, nil, "bad address");
(ok, ipaddr) := IPaddr.parse(hd toks);
if(ok < 0)
return (nil, nil, "bad ip address");
pn := int hd tl toks;
port := array[2] of byte;
port[0] = byte (pn >> 8);
port[1] = byte pn;
return (ipaddr.v4(), port, nil);
}
csquery(addr: string): (string, string, string)
{
fd := sys->open("/net/cs", Sys->ORDWR);
if(fd == nil){
(nt, toks) := sys->tokenize(addr, "!");
if(nt != 3)
return (nil, nil, "bad address");
return ("/net/"+hd toks, hd tl toks + "!" + hd tl tl toks, nil);
}
if(sys->fprint(fd, "%s", addr) == -1)
return (nil, nil, sys->sprint("%r"));
sys->seek(fd, big 0, Sys->SEEKSTART);
buf := array[100] of byte;
n := sys->read(fd, buf, len buf);
if(n < 0)
return (nil, nil, sys->sprint("%r"));
buf = buf[0:n];
(nt, toks) := sys->tokenize(string buf[0:n], " ");
if(nt != 2)
return (nil, nil, "bad tokens from cs");
return (hd toks, hd tl toks, nil);
}
reads(fd: ref Sys->FD): (int, string)
{
buf := array[100] of byte;
n := sys->read(fd, buf, len buf);
if(n == -1)
return (-1, nil);
return (0, string buf[0:n]);
}
dropsuffix(s: string, suf: string): (int, string)
{
if(len suf > len s)
return (0, s);
if(s[len s - len suf:] != suf)
return (0, s);
return (1, s[0:len s - len suf]);
}
# same as sys->dial, except ignore the network part of the csquery response
# and always make a tcp connection.
dial(addr: string): (int, Sys->Connection)
{
c: Sys->Connection;
(dir, ipa, err) := csquery(addr);
if(err != nil){
sys->werrstr(err);
return (-1, c);
}
# XXX should honour non /net, non-socks net directories
dir = "/net/tcp";
c.cfd = sys->open(dir+"/clone", Sys->ORDWR);
if(c.cfd == nil)
return (-1, c);
if(sys->fprint(c.cfd, "connect %s", ipa) == -1)
return (-1, c);
sys->seek(c.cfd, big 0, Sys->SEEKSTART);
(r, s) := reads(c.cfd);
if(r == -1)
return (-1, c);
c.dfd = sys->open(dir+"/"+string int s+"/data", Sys->ORDWR);
if(c.dfd == nil)
return (-1, c);
return (0, c);
}
kill(pid: int)
{
sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill");
}
qtype(qid: big): int
{
return int qid & ((1 << Cvshift) - 1);
}
qconv(qid: big): int
{
return int qid >> Cvshift;
}
qmk(qtype, qconv: int): big
{
return big ((qconv << Cvshift) | qtype);
}
dir(name: string, perm: int, qid: big): Sys->Dir
{
d := sys->zerodir;
d.name = name;
d.uid = "me";
d.gid = "me";
d.qid.path = qid;
if (perm & Sys->DMDIR)
d.qid.qtype = Sys->QTDIR;
else
d.qid.qtype = Sys->QTFILE;
d.mode = perm;
return d;
}
netmkaddr(addr, net, svc: string): string
{
if(net == nil)
net = "net";
(n, nil) := sys->tokenize(addr, "!");
if(n <= 1){
if(svc== nil)
return sys->sprint("%s!%s", net, addr);
return sys->sprint("%s!%s!%s", net, addr, svc);
}
if(svc == nil || n > 2)
return addr;
return sys->sprint("%s!%s", addr, svc);
}
|