From ef0540196bd7163f29ec3259e3fefb7a12d659dc Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Thu, 29 Nov 2007 00:07:25 +0000 Subject: 20071128-2330 --- appl/lib/dial.b | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ appl/lib/mkfile | 2 + dis/lib/dial.dis | Bin 0 -> 5675 bytes lib/proto/inferno | 10 ++ man/1/INDEX | 4 + man/2/INDEX | 8 ++ man/2/dial | 312 ++++++++++++++++++++++++++++++++++++++++++++ module/dial.m | 34 +++++ 8 files changed, 754 insertions(+) create mode 100644 appl/lib/dial.b create mode 100644 dis/lib/dial.dis create mode 100644 man/2/dial create mode 100644 module/dial.m diff --git a/appl/lib/dial.b b/appl/lib/dial.b new file mode 100644 index 00000000..42f9119b --- /dev/null +++ b/appl/lib/dial.b @@ -0,0 +1,384 @@ +implement Dial; + +include "sys.m"; + sys: Sys; + +include "dial.m"; + +# +# the dialstring is of the form '[/net/]proto!dest' +# +dial(addr: string, local: string): ref Connection +{ + if(sys == nil) + sys = load Sys Sys->PATH; + (netdir, proto, rem) := dialparse(addr); + if(netdir != nil) + return csdial(netdir, proto, rem, local); + + c := csdial("/net", proto, rem, local); + if(c != nil) + return c; + err := sys->sprint("%r"); + if(lookstr(err, "refused") >= 0) + return nil; + c = csdial("/net.alt", proto, rem, local); + if(c != nil) + return c; + # ignore the least precise one + alterr := sys->sprint("%r"); + if(lookstr(alterr, "translate")>=0 || lookstr(alterr, "does not exist")>=0) + sys->werrstr(err); + else + sys->werrstr(alterr); + return nil; +} + +# +# ask the connection server to translate +# +csdial(netdir: string, proto: string, rem: string, local: string): ref Connection +{ + fd := sys->open(netdir+"/cs", Sys->ORDWR); + if(fd == nil){ + # no connection server, don't translate + return call(netdir+"/"+proto+"/clone", rem, local); + } + + if(sys->fprint(fd, "%s!%s", proto, rem) < 0) + return nil; + + # try each recipe until we get one that works + besterr, err: string; + sys->seek(fd, big 0, 0); + for(;;){ + (clonefile, addr) := csread(fd); + if(clone == nil) + break; + c := call(redir(clonefile, netdir), addr, local); + if(c != nil) + return c; + err = sys->sprint("%r"); + if(lookstr(err, "does not exist") < 0) + besterr = err; + } + if(besterr != nil) + sys->werrstr(besterr); + else + sys->werrstr(err); + return nil; +} + +call(clonefile: string, dest: string, local: string): ref Connection +{ + (cfd, convdir) := clone(clonefile); + if(cfd == nil) + return nil; + + if(local != nil) + rv := sys->fprint(cfd, "connect %s %s", dest, local); + else + rv = sys->fprint(cfd, "connect %s", dest); + if(rv < 0) + return nil; + + fd := sys->open(convdir+"/data", Sys->ORDWR); + if(fd == nil) + return nil; + return ref Connection(fd, cfd, convdir); +} + +clone(clonefile: string): (ref Sys->FD, string) +{ + pdir := parent(clonefile); + if(pdir == nil){ + sys->werrstr(sys->sprint("bad clone file name: %q", clonefile)); + return (nil, nil); + } + cfd := sys->open(clonefile, Sys->ORDWR); + if(cfd == nil) + return (nil, nil); + lno := readchan(cfd); + if(lno == nil) + return (nil, nil); + return (cfd, pdir+"/"+lno); +} + +readchan(cfd: ref Sys->FD): string +{ + buf := array[Sys->NAMEMAX] of byte; + n := sys->read(cfd, buf, len buf); + if(n < 0) + return nil; + if(n == 0){ + sys->werrstr("empty clone file"); + return nil; + } + return string int string buf[0: n]; +} + +redir(old: string, newdir: string): string +{ + # because cs is in a different name space, replace the mount point + # assumes the mount point is directory in root (eg, /net/proto/clone) + if(len old > 1 && old[0] == '/'){ + p := lookc(old[1:], '/'); + if(p >= 0) + return newdir+"/"+old[1+p+1:]; + } + return newdir+"/"+old; +} + +lookc(s: string, c: int): int +{ + for(i := 0; i < len s; i++) + if(s[i] == c) + return i; + return -1; +} + +backc(s: string, i: int, c: int): int +{ + if(i >= len s) + return -1; + while(i >= 0 && s[i] != c) + i--; + return i; +} + +lookstr(s: string, t: string): int +{ + lt := len t; # we know it's not zero +Search: + for(i := 0; i <= len s - lt; i++){ + for(j := 0; j < lt; j++) + if(s[i+j] != t[j]) + continue Search; + return i; + } + return -1; +} + +# +# [[/netdir/]proto!]remainder +# +dialparse(addr: string): (string, string, string) +{ + p := lookc(addr, '!'); + if(p < 0) + return (nil, "net", addr); + if(addr[0] != '/' && addr[0] != '#') + return (nil, addr[0: p], addr[p+1:]); + p2 := backc(addr, p, '/'); + if(p2 <= 0) + return (addr[0: p], "net", addr[p+1:]); # plan 9 returns proto "" + return (addr[0: p2], addr[p2+1: p], addr[p+1:]); +} + +# +# announce a network service +# +announce(addr: string): ref Connection +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + (naddr, clonefile) := nettrans(addr); + if(naddr == nil) + return nil; + + (ctl, convdir) := clone(clonefile); + if(ctl == nil){ + sys->werrstr(sys->sprint("announce %r")); + return nil; + } + + if(sys->fprint(ctl, "announce %s", naddr) < 0){ + sys->werrstr(sys->sprint("announce writing %s: %r", clonefile)); + return nil; + } + + return ref Connection(nil, ctl, convdir); +} + +# +# listen for an incoming call on announced connection +# +listen(ac: ref Connection): ref Connection +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + pdir := parent(ac.dir); # ac.dir should be /netdir/N + if(pdir == nil){ + sys->werrstr(sys->sprint("listen directory format: %q", ac.dir)); + return nil; + } + + ctl := sys->open(ac.dir+"/listen", Sys->ORDWR); + if(ctl == nil){ + sys->werrstr(sys->sprint("listen opening %s: %r", ac.dir+"/listen")); + return nil; + } + + lno := readchan(ctl); + if(lno == nil){ + sys->werrstr(sys->sprint("listen reading %s/listen: %r", ac.dir)); + return nil; + } + return ref Connection(nil, ctl, pdir+"/"+lno); + +} + +# +# translate an address [[/netdir/]proto!rem] using /netdir/cs +# returning (newaddress, clonefile) +# +nettrans(addr: string): (string, string) +{ + (netdir, proto, rem) := dialparse(addr); + if(proto == nil || proto == "net"){ + sys->werrstr(sys->sprint("bad dial string: %s", addr)); + return (nil, nil); + } + if(netdir == nil) + netdir = "/net"; + + # try to translate using connection server + fd := sys->open(netdir+"/cs", Sys->ORDWR); + if(fd == nil){ + # use it untranslated + if(rem == nil){ + sys->werrstr(sys->sprint("bad dial string: %s", addr)); + return (nil, nil); + } + return (rem, netdir+"/"+proto+"/clone"); + } + if(sys->fprint(fd, "%s!%s", proto, rem) < 0) + return (nil, nil); + sys->seek(fd, big 0, 0); + (clonefile, naddr) := csread(fd); + if(clonefile == nil) + return (nil, nil); + + return (naddr, redir(clonefile, netdir)); +} + +csread(fd: ref Sys->FD): (string, string) +{ + buf := array[Sys->NAMEMAX] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return (nil, nil); + line := string buf[0: n]; + p := lookc(line, ' '); + if(p < 0) + return (nil, nil); + if(p == 0){ + sys->werrstr("cs: no translation"); + return (nil, nil); + } + return (line[0:p], line[p+1:]); +} + +# +# accept a call, return an fd to the open data file +# +accept(c: ref Connection): ref Sys->FD +{ + if(sys == nil) + sys = load Sys Sys->PATH; + sys->fprint(c.cfd, "accept %s", lastname(c.dir)); # ignore return value, network might not need accepts + return sys->open(c.dir+"/data", Sys->ORDWR); +} + +# +# reject a call, tell device the reason for the rejection +# +reject(c: ref Connection, why: string): int +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if(sys->fprint(c.cfd, "reject %s %q", lastname(c.dir), why) < 0) + return -1; + return 0; +} + +lastname(dir: string): string +{ + p := backc(dir, len dir-1, '/'); + if(p < 0) + return dir; + return dir[p+1:]; # N in /net/N +} + +parent(dir: string): string +{ + p := backc(dir, len dir-1, '/'); + if(p < 0) + return nil; + return dir[0: p]; +} + +netmkaddr(addr, net, svc: string): string +{ + if(sys == nil) + sys = load Sys Sys->PATH; + 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); +} + +netinfo(c: ref Connection): ref Conninfo +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if((dir := c.dir) == nil){ + if(c.dfd == nil) + return nil; + dir = parent(sys->fd2path(c.dfd)); + if(dir == nil) + return nil; + } + ci := ref Conninfo; + ci.dir = dir; + ci.root = parent(dir); + while((p := parent(ci.root)) != nil && p != "/") + ci.root = p; + (ok, d) := sys->stat(ci.dir); + if(ok >= 0) + ci.spec = sys->sprint("#%c%d", d.dtype, d.dev); + (ci.lsys, ci.lserv) = getendpoint(ci.dir, "local"); + (ci.rsys, ci.rserv) = getendpoint(ci.dir, "remote"); + p = parent(ci.dir); + if(p == nil) + return nil; + if(len p >= 5 && p[0:5] == "/net/") + p = p[5:]; + ci.laddr = sys->sprint("%s!%s!%s", p, ci.lsys, ci.lserv); + ci.raddr = sys->sprint("%s!%s!%s", p, ci.rsys, ci.rserv); + return ci; +} + +getendpoint(dir: string, file: string): (string, string) +{ + fd := sys->open(dir+"/"+file, Sys->OREAD); + buf := array[128] of byte; + if(fd == nil || (n := sys->read(fd, buf, len buf)) <= 0) + return ("???", "???"); # compatible, but probably poor defaults + if(n > 0 && buf[n-1] == byte '\n') + n--; + s := string buf[0: n]; + p := lookc(s, '!'); + if(p < 0) + return (s, "???"); + return (s[0:p], s[p+1:]); +} diff --git a/appl/lib/mkfile b/appl/lib/mkfile index a6eaf912..6784a568 100644 --- a/appl/lib/mkfile +++ b/appl/lib/mkfile @@ -113,6 +113,7 @@ TARG=\ string.dis\ strinttab.dis\ styx.dis\ + styxflush.dis\ styxlib.dis\ styxpersist.dis\ styxservers.dis\ @@ -234,3 +235,4 @@ csv.dis: $ROOT/module/csv.m json.dis: $ROOT/module/json.m lists.dis: $ROOT/module/lists.m vac.dis: $ROOT/module/vac.m $ROOT/module/venti.m +dial.dis: $ROOT/module/dial.m diff --git a/dis/lib/dial.dis b/dis/lib/dial.dis new file mode 100644 index 00000000..d532bc67 Binary files /dev/null and b/dis/lib/dial.dis differ diff --git a/lib/proto/inferno b/lib/proto/inferno index f710f8e7..d041465b 100644 --- a/lib/proto/inferno +++ b/lib/proto/inferno @@ -706,7 +706,9 @@ appl echo.b expr.b file2chan.b + mload.b mkfile + mpexpr.b regex.b sexprs.b sh.b @@ -963,6 +965,7 @@ appl deflate.b devpointer.b dhcpclient.b + dial.b dialog.b dict.b dis.b @@ -1091,6 +1094,7 @@ appl string.b strinttab.b styx.b + styxflush.b styxlib.b styxservers.b tables.b @@ -1588,6 +1592,7 @@ dis deflate.dis devpointer.dis dhcpclient.dis + dial.dis dialog.dis dict.dis dis.dis @@ -1686,6 +1691,7 @@ dis styx.dis styxconv + + styxflush.dis styxlib.dis styxpersist.dis styxservers.dis @@ -2191,6 +2197,8 @@ mkfiles * mnt * + spki + keys module NOTICE alphabet.m @@ -2221,6 +2229,7 @@ module debug.m devpointer.m dhcp.m + dial.m dialog.m dict.m dis.m @@ -2326,6 +2335,7 @@ module strinttab.m styx.m styxconv.m + styxflush.m styxlib.m styxpersist.m styxservers.m diff --git a/man/1/INDEX b/man/1/INDEX index 159a36d9..ac03fdad 100644 --- a/man/1/INDEX +++ b/man/1/INDEX @@ -183,6 +183,7 @@ csv sh-csv getcsv sh-csv sh-csv sh-csv expr sh-expr +mpexpr sh-expr ntest sh-expr sh-expr sh-expr file2chan sh-file2chan @@ -194,6 +195,9 @@ rread sh-file2chan rreadone sh-file2chan rwrite sh-file2chan sh-file2chan sh-file2chan +mload sh-mload +munload sh-mload +sh-mload sh-mload match sh-regex re sh-regex sh-regex sh-regex diff --git a/man/2/INDEX b/man/2/INDEX index 2c46d942..3e4da75b 100644 --- a/man/2/INDEX +++ b/man/2/INDEX @@ -44,6 +44,13 @@ dhcp dhcpclient dhcpclient dhcpclient lease dhcpclient removecfg dhcpclient +accept dial +announce dial +dial dial +listen dial +netinfo dial +netmkaddr dial +reject dial dialog dialog getstring dialog prompt dialog @@ -387,6 +394,7 @@ styx styx tmsg styx unpackdir styx styxconv styxconv +styxflush styxflush styxpersist styxpersist styxservers styxservers nametree styxservers-nametree diff --git a/man/2/dial b/man/2/dial new file mode 100644 index 00000000..8e0d15af --- /dev/null +++ b/man/2/dial @@ -0,0 +1,312 @@ +.TH DIAL 2 +.SH NAME +Dial: accept, announce, dial, listen, netinfo, netmkaddr, reject \- make network connections +.SH SYNOPSIS +.EX +include "sys.m"; +dial := load Dial Dial->PATH; + +Connection: adt +{ + dfd: ref FD; # data file + cfd: ref FD; # control file + dir: string; # pathname of line directory +}; + +announce: fn(addr: string): ref Connection; +dial: fn(addr, local: string): ref Connection; +listen: fn(c: ref Connection): ref Connection; +accept: fn(c: ref Connection): ref Sys->FD; +reject: fn(c: ref Connection, why: string); + +netmkaddr: fn(net, host, svc: string): string; + +Conninfo: adt +{ + dir: string; # connection directory + root: string; # network mount point + spec: string; # its binding spec + lsys: string; # local host address + lserv: string; # local service + rsys: string; # remote host address + rserv: string; # remote service + laddr: string; # local address in dial form + raddr: string; # remote address in dial form +}; + +netinfo: fn(c: ref Connection): ref Conninfo; +.EE +.SH DESCRIPTION +.B Dial +establishes network connections. +The description below uses the following definitions: +.TF network +.PD +.TP +.I addr +is a network address in one of the following forms: +.br +.IP +.IB network ! netaddr ! service\f1 +.br +.IB network ! netaddr\f1 +.br +.IR netaddr +.TP +.I network +Any directory listed in +.B /net +(eg, +.BR tcp ), +or the special token, +.BR net . +The special name +.B net +stands for any network that connects +the current host and +.IR netaddr . +A network name can be preceded by the full path name of a directory +of networks, using the form +.I /dir/network +(eg, +.BR /net.alt/tcp ). +.TP +.I netaddr +A host name, a domain name, a network address, +or a meta-name of the form +.BI $ attribute\f1, +which +is replaced by +.I value +from the corresponding attribute-value pair +in the connection server data base (see +.IR db (6)). +.PP +The functions +.B dial +and +.B announce +translate a given +.I addr +to an actual network address using +the connection server +.IR cs (8). +If a logical name +.I addr +corresponds to several network addresses, +for instance if a destination machine has several interfaces, +.I cs +will return them all; +.B dial +or +.B announce +will try each in turn until one works. +In particular, if +.I addr +is +.BR net , +.I cs +will return addresses on +all networks that are common to source and destination. +The translation procedure accesses +.I cs +using its interface file +.BR cs , +which is sought as follows: +first, in an explicit directory +.BI / dir +if one was given in +.IR network ; +second, in the standard directory +.BR /net ; +and finally in the directory +.BR /net.alt +.RB ( dial +only). +If the connection server cannot be found, +the +.I addr +is used as-is. +.PP +If a connection attempt is successful, the +.B dir +member of the resulting +.B Connection +will be +the path name of a +.I line directory +that has files for accessing the connection. +One line directory exists for each possible connection. +The +.B data +file in the line directory is opened to +make a connection, and read and written to communicate with the destination. +The +.B ctl +file in the line directory can be used to send commands to the line. +See +.IR ip (3) +for messages that can be written to the +.B ctl +file. +The last close of both +.B data +and +.B ctl +file will close the connection. +The +.B remote +file in the line directory contains the address called; the file +.B local +contains the local address assigned. +.PP +The function +.B dial +calls destination +.I addr +on a multiplexed network. +If the connection server returns several possible locations for +.IR addr , +.B dial +tries each in turn, until a connection is made, +or no address remains to be tried. +.B Dial +returns a reference to a +.B Connection +value containing a string +.B dir +that names the conversation directory for the connection, +a file descriptor +.B dfd +open for reading and writing the +.B data +file in that directory, and +a file descriptor +.B cfd +open for reading and writing the directory's +.B ctl +file. +If +.IR local +is non-empty, and the network allows the local address to be set, +as is the case with UDP and TCP port numbers, +the local address will be set to +.IR local . +.PP +.B Announce +and +.B listen +are the complements of +.BR dial . +.B Announce +establishes a network name to which incoming calls can be made. +In +.IR addr , +.I netaddr +gives the name or address of one of the local host's interfaces on which to listen for +calls to the given +.IR service ; +it can be +.B * +to listen for calls on any interface on +.IR network . +.B Announce +returns a reference to a +.B Connection +value in which only the +.B cfd +descriptor is open, on the control file representing the announcement. +.B Listen +takes as its only argument a reference to the +.B Connection +returned by a successful call to +.BR announce . +When a call is received, +.B listen +returns a reference to a new +.B Connection +value that refers to the conversation directory for the incoming call; +only the +.B cfd +descriptor is open. +That call can be accepted or rejected. +Use +.B accept +to obtain a file descriptor for the data file for the conversation. +Use +.B reject +to reject the incoming call; some networks will also tell the caller the reason +.IR why . +.PP +Given a +.BR Connection , +.B netinfo +returns a reference to a +.B Conninfo +value that gives details about the connection and its network. +.SH EXAMPLES +.PP +Make a call and return an open file descriptor to +use for communications: +.IP +.EX +callkremvax(): ref Sys->FD +{ + c := dial->dial("tcp!kremvax!80", nil); + if(c == nil) + return nil; + return c.dfd; +} +.EE +.PP +Call the local certificate signer: +.IP +.EX +dialsigner(service: string): ref Sys->FD +{ + c := dial->dial("net!$SIGNER!inflogin", nil); + if(c == nil) + return nil; + return c.dfd; +} +.EE +.PP +Listen for incoming calls. +.IP +.EX +listener() +{ + ac := dial->announce("tcp!*!9995"); + if(ac == nil){ + sys->print("can't announce: %r\n"); + exit; + } + for(;;){ + lc := dial->listen(ac); + if(lc == nil){ + sys->print("listen: %r\n"); + exit; + } + sys->print("incoming: %s\n", hd ctext(lc)); + spawn client(lc); + } +} + +client(c: ref Connection) +{ + dfd := dial->accept(c); + if(dfd == nil){ + sys->print("%s: can't accept: %r\n", c.dir); + exit; + } + buf := array[Sys->ATOMICIO] of byte; + while((n := sys->read(dfd, buf, len buf)) > 0) + sys->write(dfd, buf, n); +} +.EE +.SH SOURCE +.B /appl/lib/dial.b +.SH DIAGNOSTICS +The integer valued functions return 0 on success and -1 on error; +functions returning a reference return nil on error. +In those cases the system error string is set. diff --git a/module/dial.m b/module/dial.m new file mode 100644 index 00000000..1871317a --- /dev/null +++ b/module/dial.m @@ -0,0 +1,34 @@ +Dial: module +{ + PATH: con "/dis/lib/dial.dis"; + + Connection: adt + { + dfd: ref Sys->FD; + cfd: ref Sys->FD; + dir: string; + }; + + Conninfo: adt + { + dir: string; + root: string; + spec: string; + lsys: string; + lserv: string; + rsys: string; + rserv: string; + laddr: string; + raddr: string; + }; + + announce: fn(addr: string): ref Connection; + dial: fn(addr, local: string): ref Connection; + listen: fn(c: ref Connection): ref Connection; + accept: fn(c: ref Connection): ref Sys->FD; + reject: fn(c: ref Connection, why: string): int; +# parse: fn(addr: string): (string, string, string); + + netmkaddr: fn(addr, net, svc: string): string; + netinfo: fn(c: ref Connection): ref Conninfo; +}; -- cgit v1.2.3