summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--appl/lib/dial.b384
-rw-r--r--appl/lib/mkfile2
-rw-r--r--dis/lib/dial.disbin0 -> 5675 bytes
-rw-r--r--lib/proto/inferno10
-rw-r--r--man/1/INDEX4
-rw-r--r--man/2/INDEX8
-rw-r--r--man/2/dial312
-rw-r--r--module/dial.m34
8 files changed, 754 insertions, 0 deletions
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
--- /dev/null
+++ b/dis/lib/dial.dis
Binary files 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;
+};