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); }