1implement Listen; 2include "sys.m"; 3 sys: Sys; 4include "draw.m"; 5include "arg.m"; 6include "keyring.m"; 7 keyring: Keyring; 8include "security.m"; 9 auth: Auth; 10include "dial.m"; 11 dial: Dial; 12include "sh.m"; 13 sh: Sh; 14 Context: import sh; 15 16Listen: module { 17 init: fn(nil: ref Draw->Context, argv: list of string); 18}; 19 20badmodule(p: string) 21{ 22 sys->fprint(stderr(), "listen: cannot load %s: %r\n", p); 23 raise "fail:bad module"; 24} 25 26serverkey: ref Keyring->Authinfo; 27verbose := 0; 28 29init(drawctxt: ref Draw->Context, argv: list of string) 30{ 31 sys = load Sys Sys->PATH; 32 keyring = load Keyring Keyring->PATH; 33 auth = load Auth Auth->PATH; 34 if (auth == nil) 35 badmodule(Auth->PATH); 36 dial = load Dial Dial->PATH; 37 if (dial == nil) 38 badmodule(Dial->PATH); 39 sh = load Sh Sh->PATH; 40 if (sh == nil) 41 badmodule(Sh->PATH); 42 arg := load Arg Arg->PATH; 43 if (arg == nil) 44 badmodule(Arg->PATH); 45 auth->init(); 46 algs: list of string; 47 arg->init(argv); 48 keyfile: string; 49 initscript: string; 50 doauth := 1; 51 synchronous := 0; 52 trusted := 0; 53 arg->setusage("listen [-i {initscript}] [-Ast] [-k keyfile] [-a alg]... addr command [arg...]"); 54 while ((opt := arg->opt()) != 0) { 55 case opt { 56 'a' => 57 algs = arg->earg() :: algs; 58 'A' => 59 doauth = 0; 60 'f' or 61 'k' => 62 keyfile = arg->earg(); 63 if (! (keyfile[0] == '/' || (len keyfile > 2 && keyfile[0:2] == "./"))) 64 keyfile = "/usr/" + user() + "/keyring/" + keyfile; 65 'i' => 66 initscript = arg->earg(); 67 'v' => 68 verbose = 1; 69 's' => 70 synchronous = 1; 71 't' => 72 trusted = 1; 73 * => 74 arg->usage(); 75 } 76 } 77 if (doauth && algs == nil) 78 algs = getalgs(); 79 if (algs != nil) { 80 if (keyfile == nil) 81 keyfile = "/usr/" + user() + "/keyring/default"; 82 serverkey = keyring->readauthinfo(keyfile); 83 if (serverkey == nil) { 84 sys->fprint(stderr(), "listen: cannot read %s: %r\n", keyfile); 85 raise "fail:bad keyfile"; 86 } 87 } 88 if(!trusted){ 89 sys->unmount(nil, "/mnt/keys"); # should do for now 90 # become none? 91 } 92 93 argv = arg->argv(); 94 n := len argv; 95 if (n < 2) 96 arg->usage(); 97 arg = nil; 98 99 sync := chan[1] of string; 100 spawn listen(drawctxt, hd argv, tl argv, algs, initscript, sync); 101 e := <-sync; 102 if(e != nil) 103 raise "fail:" + e; 104 if(synchronous){ 105 e = <-sync; 106 if(e != nil) 107 raise "fail:" + e; 108 } 109} 110 111listen(drawctxt: ref Draw->Context, addr: string, argv: list of string, 112 algs: list of string, initscript: string, sync: chan of string) 113{ 114 { 115 listen1(drawctxt, addr, argv, algs, initscript, sync); 116 } exception e { 117 "fail:*" => 118 sync <-= e; 119 } 120} 121 122listen1(drawctxt: ref Draw->Context, addr: string, argv: list of string, 123 algs: list of string, initscript: string, sync: chan of string) 124{ 125 sys->pctl(Sys->FORKFD, nil); 126 127 ctxt := Context.new(drawctxt); 128 acon := dial->announce(addr); 129 if (acon == nil) { 130 sys->fprint(stderr(), "listen: failed to announce on '%s': %r\n", addr); 131 sync <-= "cannot announce"; 132 exit; 133 } 134 ctxt.set("user", nil); 135 if (initscript != nil) { 136 ctxt.setlocal("net", ref Sh->Listnode(nil, acon.dir) :: nil); 137 ctxt.run(ref Sh->Listnode(nil, initscript) :: nil, 0); 138 initscript = nil; 139 } 140 141 # make sure the shell command is parsed only once. 142 cmd := sh->stringlist2list(argv); 143 if((hd argv) != nil && (hd argv)[0] == '{'){ 144 (c, e) := sh->parse(hd argv); 145 if(c == nil){ 146 sys->fprint(stderr(), "listen: %s\n", e); 147 sync <-= "parse error"; 148 exit; 149 } 150 cmd = ref Sh->Listnode(c, hd argv) :: tl cmd; 151 } 152 153 sync <-= nil; 154 listench := chan of (int, ref Dial->Connection); 155 authch := chan of (string, ref Dial->Connection); 156 spawn listener(listench, acon, addr); 157 for (;;) { 158 user := ""; 159 ccon: ref Dial->Connection; 160 alt { 161 (lok, c) := <-listench => 162 if (lok == -1){ 163 sync <-= "listen"; 164 exit; 165 } 166 if (algs != nil) { 167 spawn authenticator(authch, c, algs, addr); 168 continue; 169 } 170 ccon = c; 171 (user, ccon) = <-authch => 172 ; 173 } 174 if (user != nil) 175 ctxt.set("user", sh->stringlist2list(user :: nil)); 176 ctxt.set("net", ref Sh->Listnode(nil, ccon.dir) :: nil); 177 178 # XXX could do this in a separate process too, to 179 # allow new connections to arrive and start authenticating 180 # while the shell command is still running. 181 sys->dup(ccon.dfd.fd, 0); 182 sys->dup(ccon.dfd.fd, 1); 183 ccon.dfd = ccon.cfd = nil; 184 ctxt.run(cmd, 0); 185 sys->dup(2, 0); 186 sys->dup(2, 1); 187 } 188} 189 190listener(listench: chan of (int, ref Dial->Connection), c: ref Dial->Connection, addr: string) 191{ 192 for (;;) { 193 nc := dial->listen(c); 194 if (nc == nil) { 195 sys->fprint(stderr(), "listen: listen error on '%s': %r\n", addr); 196 listench <-= (-1, nc); 197 exit; 198 } 199 if (verbose) 200 sys->fprint(stderr(), "listen: got connection on %s from %s", 201 addr, readfile(nc.dir + "/remote")); 202 nc.dfd = dial->accept(nc); 203 if (nc.dfd == nil) 204 sys->fprint(stderr(), "listen: cannot open %s: %r\n", nc.dir + "/data"); 205 else{ 206 if(nc.cfd != nil) 207 sys->fprint(nc.cfd, "keepalive"); 208 listench <-= (0, nc); 209 } 210 } 211} 212 213authenticator(authch: chan of (string, ref Dial->Connection), 214 c: ref Dial->Connection, algs: list of string, addr: string) 215{ 216 err: string; 217 (c.dfd, err) = auth->server(algs, serverkey, c.dfd, 0); 218 if (c.dfd == nil) { 219 sys->fprint(stderr(), "listen: auth on %s failed: %s\n", addr, err); 220 return; 221 } 222 if (verbose) 223 sys->fprint(stderr(), "listen: authenticated on %s as %s\n", addr, err); 224 authch <-= (err, c); 225} 226 227stderr(): ref Sys->FD 228{ 229 return sys->fildes(2); 230} 231 232user(): string 233{ 234 u := readfile("/dev/user"); 235 if (u == nil) 236 return "nobody"; 237 return u; 238} 239 240readfile(f: string): string 241{ 242 fd := sys->open(f, sys->OREAD); 243 if(fd == nil) 244 return nil; 245 246 buf := array[1024] of byte; 247 n := sys->read(fd, buf, len buf); 248 if(n < 0) 249 return nil; 250 251 return string buf[0:n]; 252} 253 254getalgs(): list of string 255{ 256 sslctl := readfile("#D/clone"); 257 if (sslctl == nil) { 258 sslctl = readfile("#D/ssl/clone"); 259 if (sslctl == nil) 260 return nil; 261 sslctl = "#D/ssl/" + sslctl; 262 } else 263 sslctl = "#D/" + sslctl; 264 (nil, algs) := sys->tokenize(readfile(sslctl + "/encalgs") + " " + readfile(sslctl + "/hashalgs"), " \t\n"); 265 return "none" :: algs; 266} 267