diff options
Diffstat (limited to 'appl/cmd/ip/nppp')
| -rw-r--r-- | appl/cmd/ip/nppp/mkfile | 24 | ||||
| -rw-r--r-- | appl/cmd/ip/nppp/modem.b | 469 | ||||
| -rw-r--r-- | appl/cmd/ip/nppp/modem.m | 47 | ||||
| -rw-r--r-- | appl/cmd/ip/nppp/pppchat.b | 322 | ||||
| -rw-r--r-- | appl/cmd/ip/nppp/ppplink.b | 782 | ||||
| -rw-r--r-- | appl/cmd/ip/nppp/ppptest.b | 90 | ||||
| -rw-r--r-- | appl/cmd/ip/nppp/script.b | 171 | ||||
| -rw-r--r-- | appl/cmd/ip/nppp/script.m | 15 |
8 files changed, 1920 insertions, 0 deletions
diff --git a/appl/cmd/ip/nppp/mkfile b/appl/cmd/ip/nppp/mkfile new file mode 100644 index 00000000..0f803acd --- /dev/null +++ b/appl/cmd/ip/nppp/mkfile @@ -0,0 +1,24 @@ +<../../../../mkconfig + +TARG=\ + ppplink.dis\ + pppchat.dis\ + modem.dis\ + script.dis\ +# ppptest.dis\ + +MODULES=\ + modem.m\ + script.m\ + +SYSMODULES=\ + sys.m\ + draw.m\ + tk.m\ + dict.m\ + string.m\ + lock.m\ + +DISBIN=$ROOT/dis/ip/nppp + +<$ROOT/mkfiles/mkdis diff --git a/appl/cmd/ip/nppp/modem.b b/appl/cmd/ip/nppp/modem.b new file mode 100644 index 00000000..f8c81396 --- /dev/null +++ b/appl/cmd/ip/nppp/modem.b @@ -0,0 +1,469 @@ +implement Modem; + +# +# Copyright © 1998-2001 Vita Nuova Holdings Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; + +include "lock.m"; + lock: Lock; + Semaphore: import lock; + +include "draw.m"; + +include "modem.m"; + +hangupcmd := "ATH0"; # was ATZH0 but some modem versions on Umec hung on ATZ + +# modem return codes +Ok, Success, Failure, Abort, Noise, Found: con iota; + +maxspeed: con 115200; + +# +# modem return messages +# +Msg: adt { + text: string; + code: int; +}; + +msgs: array of Msg = array [] of { + ("OK", Ok), + ("NO CARRIER", Failure), + ("ERROR", Failure), + ("NO DIALTONE", Failure), + ("BUSY", Failure), + ("NO ANSWER", Failure), + ("CONNECT", Success), +}; + +kill(pid: int) +{ + fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil || sys->fprint(fd, "kill") < 0) + sys->print("modem: can't kill %d: %r\n", pid); +} + +# +# prepare a modem port +# +openserial(d: ref Device): string +{ + d.data = nil; + d.ctl = nil; + + d.data = sys->open(d.local, Sys->ORDWR); + if(d.data == nil) + return sys->sprint("can't open %s: %r", d.local); + + d.ctl = sys->open(d.local+"ctl", Sys->ORDWR); + if(d.ctl == nil) + return sys->sprint("can't open %s: %r", d.local+"ctl"); + + d.speed = maxspeed; + d.avail = nil; + return nil; +} + +# +# shut down the monitor (if any) and return the connection +# + +Device.close(m: self ref Device): ref Sys->Connection +{ + if(m.pid != 0){ + kill(m.pid); + m.pid = 0; + } + if(m.data == nil) + return nil; + mc := ref sys->Connection(m.data, m.ctl, nil); + m.ctl = nil; + m.data = nil; + return mc; +} + +# +# Send a string to the modem +# + +Device.send(d: self ref Device, x: string): string +{ + a := array of byte x; + f := sys->write(d.data, a, len a); + if(f != len a) { + # let's attempt to close & reopen the modem + d.close(); + err := openserial(d); + if(err != nil) + return err; + f = sys->write(d.data,a, len a); + if(f < 0) + return sys->sprint("%r"); + if(f != len a) + return "short write"; + } + if(d.trace) + sys->print("->%s\n",x); + return nil; +} + +# +# apply a string of commands to modem & look for a response +# + +apply(d: ref Device, s: string, substr: string, secs: int): int +{ + m := Ok; + buf := ""; + for(i := 0; i < len s; i++){ + c := s[i]; + buf[len buf] = c; # assume no Unicode + if(c == '\r' || i == (len s -1)){ + if(c != '\r') + buf[len buf] = '\r'; + if(d.send(buf) != nil) + return Abort; + (m, nil) = readmsg(d, secs, substr); + buf = ""; + } + } + return m; +} + +# +# get modem into command mode if it isn't already +# +GUARDTIME: con 1100; # usual default for S12=50 in units of 1/50 sec; allow 100ms fuzz + +attention(d: ref Device): int +{ + for(i := 0; i < 3; i++){ + if(apply(d, hangupcmd, nil, 2) == Ok) + return Ok; + sys->sleep(GUARDTIME); + if(d.send("+++") != nil) + return Abort; + sys->sleep(GUARDTIME); + (nil, msg) := readmsg(d, 0, nil); + if(msg != nil && d.trace) + sys->print("status: %s\n", msg); + } + return Failure; +} + +# +# apply a command type +# + +applyspecial(d: ref Device, cmd: string): int +{ + if(cmd == nil) + return Failure; + return apply(d, cmd, nil, 2); +} + +# +# hang up any connections in progress and close the device +# +Device.onhook(d: self ref Device) +{ + # hang up the modem + monitoring(d); + if(attention(d) != Ok) + sys->print("modem: no attention\n"); + + # hangup the stream (eg, for ppp) and toggle the lines to the modem + if(d.ctl != nil) { + sys->fprint(d.ctl,"d0\n"); + sys->fprint(d.ctl,"r0\n"); + sys->fprint(d.ctl, "h\n"); # hangup on native serial + sys->sleep(250); + sys->fprint(d.ctl,"r1\n"); + sys->fprint(d.ctl,"d1\n"); + } + + d.close(); +} + +# +# does string s contain t anywhere? +# + +contains(s, t: string): int +{ + if(t == nil) + return 1; + if(s == nil) + return 0; + n := len t; + for(i := 0; i+n <= len s; i++) + if(s[i:i+n] == t) + return 1; + return 0; +} + +# +# read till we see a message or we time out +# +readmsg(d: ref Device, secs: int, substr: string): (int, string) +{ + found := 0; + msecs := secs*1000; + limit := 1000; # pretty arbitrary + s := ""; + + for(start := sys->millisec(); sys->millisec() <= start+msecs;){ + a := d.getinput(1); + if(len a == 0){ + if(limit){ + sys->sleep(1); + continue; + } + break; + } + if(a[0] == byte '\n' || a[0] == byte '\r' || limit == 0){ + if (len s) { + if (s[(len s)-1] == '\r') + s[(len s)-1] = '\n'; + sys->print("<-%s\n",s); + } + if(substr != nil && contains(s, substr)) + found = 1; + for(k := 0; k < len msgs; k++) + if(len s >= len msgs[k].text && + s[0:len msgs[k].text] == msgs[k].text){ + if(found) + return (Found, s); + return (msgs[k].code, s); + } + start = sys->millisec(); + s = ""; + continue; + } + s[len s] = int a[0]; + limit--; + } + s = "no response from modem"; + if(found) + return (Found, s); + + return (Noise, s); +} + +# +# get baud rate from a connect message +# + +getspeed(msg: string, speed: int): int +{ + p := msg[7:]; # skip "CONNECT" + while(p[0] == ' ' || p[0] == '\t') + p = p[1:]; + s := int p; + if(s <= 0) + return speed; + else + return s; +} + +# +# set speed and RTS/CTS modem flow control +# + +setspeed(d: ref Device, baud: int) +{ + if(d != nil && d.ctl != nil){ + sys->fprint(d.ctl, "b%d", baud); + sys->fprint(d.ctl, "m1"); + } +} + +monitoring(d: ref Device) +{ + # if no monitor then spawn one + if(d.pid == 0) { + pidc := chan of int; + spawn monitor(d, pidc, nil); + d.pid = <-pidc; + } +} + +# +# a process to read input from a modem. +# +monitor(d: ref Device, pidc: chan of int, errc: chan of string) +{ + err := openserial(d); + pidc <-= sys->pctl(0, nil); + if(err != nil && errc != nil) + errc <-= err; + a := array[Sys->ATOMICIO] of byte; + for(;;) { + d.lock.obtain(); + d.status = "Idle"; + d.remote = ""; + setspeed(d, d.speed); + d.lock.release(); + # shuttle bytes + while((n := sys->read(d.data, a, len a)) > 0){ + d.lock.obtain(); + if (len d.avail < Sys->ATOMICIO) { + na := array[len d.avail + n] of byte; + na[0:] = d.avail[0:]; + na[len d.avail:] = a[0:n]; + d.avail = na; + } + d.lock.release(); + } + # on an error, try reopening the device + d.data = nil; + d.ctl = nil; + err = openserial(d); + if(err != nil && errc != nil) + errc <-= err; + } +} + +# +# return up to n bytes read from the modem by monitor() +# +Device.getinput(d: self ref Device, n: int): array of byte +{ + if(d==nil || n <= 0) + return nil; + a: array of byte; + d.lock.obtain(); + if(len d.avail != 0){ + if(n > len d.avail) + n = len d.avail; + a = d.avail[0:n]; + d.avail = d.avail[n:]; + } + d.lock.release(); + return a; +} + +Device.getc(d: self ref Device, msec: int): int +{ + start := sys->millisec(); + while((b := d.getinput(1)) == nil) { + if (msec && sys->millisec() > start+msec) + return 0; + sys->sleep(1); + } + return int b[0]; +} + +init(): string +{ + sys = load Sys Sys->PATH; + lock = load Lock Lock->PATH; + if(lock == nil) + return sys->sprint("can't load %s: %r", Lock->PATH); + lock->init(); + return nil; +} + +Device.new(modeminfo: ref ModemInfo, trace: int): ref Device +{ + d := ref Device; + d.lock = Semaphore.new(); + d.local = modeminfo.path; + d.pid = 0; + d.speed = 0; + d.t = *modeminfo; + if(d.t.hangup == nil) + d.t.hangup = hangupcmd; + d.trace = trace | 1; # always trace for now + return d; +} + +# +# dial a number +# +Device.dial(d: self ref Device, number: string): string +{ + monitoring(d); + + # modem type should already be established, but just in case + if(d.trace) + sys->print("modem: attention\n"); + x := attention(d); + if (x != Ok && d.trace) + return "bad response from modem"; + # + # extended Hayes commands, meaning depends on modem + # + sys->print("modem: init\n"); + if(d.t.country != nil) + applyspecial(d, d.t.country); + + if(d.t.init != nil) + applyspecial(d, d.t.init); + + if(d.t.other != nil) + applyspecial(d, d.t.other); + + applyspecial(d, d.t.errorcorrection); + + compress := Abort; + if(d.t.mnponly != nil) + compress = applyspecial(d, d.t.mnponly); + if(d.t.compression != nil) + compress = applyspecial(d, d.t.compression); + + rateadjust := Abort; + if(compress != Ok) + rateadjust = applyspecial(d, d.t.rateadjust); + applyspecial(d, d.t.flowctl); + + # finally, dialout + if(d.trace) + sys->print("modem: dial\n"); + if((dt := d.t.dialtype) == nil) + dt = "ATDT"; + err := d.send(sys->sprint("%s%s\r", dt, number)); + if(err != nil){ + if(d.trace) + sys->print("modem: can't dial %s: %s\n", number, err); + return err; + } + + (i, msg) := readmsg(d, 120, nil); + if(i != Success){ + if(d.trace) + sys->print("modem: modem error reply: %s\n", msg); + return msg; + } + + connectspeed := getspeed(msg, d.speed); + + # change line rate if not compressing + if(rateadjust == Ok) + setspeed(d, connectspeed); + + if(d.ctl != nil){ + if(d != nil) + sys->fprint(d.ctl, "s%d", connectspeed); # set DCE speed (if device implements it) + sys->fprint(d.ctl, "c1"); # enable CD monitoring + } + + return nil; +} + +dumpa(a: array of byte): string +{ + s := ""; + for(i:=0; i<len a; i++){ + b := int a[i]; + if(b >= ' ' && b < 16r7f) + s[len s] = b; + else + s += sys->sprint("\\%.2x", b); + } + return s; +} diff --git a/appl/cmd/ip/nppp/modem.m b/appl/cmd/ip/nppp/modem.m new file mode 100644 index 00000000..6e84b0e3 --- /dev/null +++ b/appl/cmd/ip/nppp/modem.m @@ -0,0 +1,47 @@ +Modem: module +{ + PATH: con "/dis/ip/nppp/modem.dis"; + + ModemInfo: adt { + path: string; + init: string; + country: string; + other: string; + errorcorrection:string; + compression: string; + flowctl: string; + rateadjust: string; + mnponly: string; + dialtype: string; + hangup: string; + }; + + Device: adt { + lock: ref Lock->Semaphore; + # modem stuff + ctl: ref Sys->FD; + data: ref Sys->FD; + + local: string; + remote: string; + status: string; + speed: int; + t: ModemInfo; + trace: int; + + # input reader + avail: array of byte; + pid: int; + + new: fn(i: ref ModemInfo, trace: int): ref Device; + dial: fn(m: self ref Device, number: string): string; + getc: fn(m: self ref Device, msec: int): int; + getinput: fn(m: self ref Device, n: int): array of byte; + send: fn(m: self ref Device, x: string): string; + close: fn(m: self ref Device): ref Sys->Connection; + onhook: fn(m: self ref Device); + }; + + init: fn(): string; + +}; diff --git a/appl/cmd/ip/nppp/pppchat.b b/appl/cmd/ip/nppp/pppchat.b new file mode 100644 index 00000000..77202b18 --- /dev/null +++ b/appl/cmd/ip/nppp/pppchat.b @@ -0,0 +1,322 @@ +implement Dialupchat; + +# +# Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; + +include "draw.m"; + draw: Draw; + Point, Rect: import draw; + +include "tk.m"; + tk: Tk; + +include "wmlib.m"; + wmlib: Wmlib; + +include "translate.m"; + translate: Translate; + Dict: import translate; + dict: ref Dict; + +Dialupchat: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +# Dimension constant for ISP Connect window +WIDTH: con 300; +HEIGHT: con 58; + +LightGreen: con "#00FF80"; # colour for successful blob +Blobx: con 8; +Gapx: con 4; +BARW: con (Blobx+Gapx)*10; # Progress bar width +BARH: con 18; # Progress bar height +DIALQUANTA : con 1000; +ICONQUANTA : con 5000; + +pppquanta := DIALQUANTA; + +Maxstep: con 9; + +init(ctxt: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + wmlib = load Wmlib Wmlib->PATH; + wmlib->init(); + + translate = load Translate Translate->PATH; + if(translate != nil) { + translate->init(); + dictname := translate->mkdictname("", "pppchat"); + dicterr: string; + (dict, dicterr) = translate->opendict(dictname); + if(dicterr != nil) + sys->fprint(sys->fildes(2), "pppchat: can't open %s: %s\n", dictname, dicterr); + }else + sys->fprint(sys->fildes(2), "pppchat: can't load %s: %r\n", Translate->PATH); + + tkargs: string; + if(args != nil) { + tkargs = hd args; + args = tl args; + } + + sys->pctl(Sys->NEWPGRP, nil); + + pppfd := sys->open("/chan/pppctl", Sys->ORDWR); + if(pppfd == nil) + error(sys->sprint("can't open /chan/pppctl: %r")); + + (t, wmctl) := wmlib->titlebar(ctxt.screen, tkargs, X("Dialup Connection"), Wmlib->Hide); + + cmd := chan of string; + tk->namechan(t, cmd, "cmd"); + + pb := Progressbar.mk(t, ".f.prog.c", (BARW, BARH)); + + config_win := array[] of { + "frame .f", + "frame .f.prog", + "frame .f.b", + + pb.tkcreate(), + "pack .f.prog.c -pady 6 -side top", + + "label .f.stat -fg blue -text {"+X("Initialising connection...")+"}", + "pack .f.stat -side top -fill x -expand 1 -anchor n", + + "pack .f -side top -expand 1 -padx 5 -pady 3 -fill both -anchor w", + "pack .f.prog -side top -expand 1 -fill x", + "button .f.b.done -text {"+X("Cancel")+"} -command {send cmd cancel}", + "pack .f.b.done -side right -padx 1 -pady 1 -anchor s", + "button .f.b.retry -text {"+X("Retry")+"} -command {send cmd retry} -state disabled", + "pack .f.b.retry -side left -padx 1 -pady 1 -anchor s", + "pack .f.b -side top -expand 1 -fill x", + + "pack propagate . 0", + "update", + }; + + for(i := 0; i < len config_win; i++) + tkcmd(t, config_win[i]); + + connected := 0; + winmapped := 1; + timecount := 0; + xmin := 0; + x := 0; + turn := 0; + + pppquanta = DIALQUANTA; + ticks := chan of int; + spawn ppptimer(ticks); + + statuslines := chan of (string, string); + pids := chan of int; + spawn ctlreader(pppfd, pids, statuslines); + ctlpid := <-pids; + +Work: + for(;;) alt { + + s := <-wmctl => + if(s == "exit") + s = "task"; + if(s == "task"){ + spawn wmlib->titlectl(t, s); + continue; + } + wmlib->titlectl(t, s); + + press := <-cmd => + case press { + "cancel" or "disconnect" => + tkcmd(t, sys->sprint(".f.stat configure -text '%s", X("Disconnecting"))); + tkcmd(t, "update"); + if(sys->fprint(pppfd, "hangup") < 0){ + err := sys->sprint("%r"); + tkcmd(t, sys->sprint(".f.stat configure -text '%s: %s", X("Error disconnecting"), X(err))); + sys->fprint(sys->fildes(2), "pppchat: can't disconnect: %s\n", err); + } + break Work; + "retry" => + if(sys->fprint(pppfd, "connect") < 0){ + err := sys->sprint("%r"); + } + } + + <-ticks => + ticks <-= 1; + if(!connected){ + if(pb != nil){ + if((turn ^= 1) == 0) + pb.setcolour("white"); + else + pb.setcolour(LightGreen); + } + tkcmd(t, "raise .; update"); + } + + (status, err) := <-statuslines => + if(status == nil){ + status = "0 1 empty status"; + if(err != nil) + sys->print("pppchat: !%s\n", err); + } else + sys->print("pppchat: %s\n", status); + (nf, flds) := sys->tokenize(status, " \t\n"); +# for(i = 0; i < len status; i++) +# if(status[i] == ' ' || status[i] == '\t') { +# status = status[i+1:]; +# break; +# } + if(nf < 3) + break; + step := int hd flds; flds = tl flds; + nstep := int hd flds; flds = tl flds; + if(step < 0) + raise "pppchat: bad step"; + case hd flds { + "error:" => + tkcmd(t, ".f.stat configure -fg red -text '"+X(status)); + tkcmd(t, ".f.b.retry configure -state normal"); + tkcmd(t, "update"); + wmlib->unhide(); + winmapped = 1; + pb.stepto(step, "red"); + #break Work; + * => + pb.setcolour(LightGreen); + pb.stepto(step, LightGreen); + } + turn = 0; + statusmsg := X(status); + tkcmd(t, ".f.stat configure -text '"+statusmsg); + tkcmd(t, "raise .; update"); + + case hd flds { + "up" or "done" => + if(!connected){ + connected = 1; + } + pppquanta = ICONQUANTA; + + # display connection speed + if(tl flds != nil) + tkcmd(t, ".f.stat configure -text {"+statusmsg+" "+"SPEED"+" hd tl flds}"); + else + tkcmd(t, ".f.stat configure -text {"+statusmsg+"}"); + tkcmd(t, ".f.b.done configure -text Disconnect -command 'send cmd disconnect"); + tkcmd(t, "update"); + sys->sleep(2000); + tkcmd(t, "pack forget .f.prog; update"); + spawn wmlib->titlectl(t, "task"); + winmapped = 0; + } + tkcmd(t, "update"); + } + <-ticks; + ticks <-= 0; # stop ppptimer + kill(ctlpid); +} + +ppptimer(ticks: chan of int) +{ + do{ + sys->sleep(pppquanta); + ticks <-= 1; + }while(<-ticks); +} + +ctlreader(fd: ref Sys->FD, pidc: chan of int, lines: chan of (string, string)) +{ + pidc <-= sys->pctl(0, nil); + buf := array[128] of byte; + while((n := sys->read(fd, buf, len buf)) > 0) + lines <-= (string buf[0:n], nil); + if(n < 0) + lines <-= (nil, sys->sprint("%r")); + else + lines <-= (nil, nil); +} + +Progressbar: adt { + t: ref Tk->Toplevel; + canvas: string; + csize: Point; + blobs: list of string; + + mk: fn(t: ref Tk->Toplevel, canvas: string, csize: Point): ref Progressbar; + tkcreate: fn(pb: self ref Progressbar): string; + setcolour: fn(pb: self ref Progressbar, c: string); + stepto: fn(pb: self ref Progressbar, step: int, col: string); + destroy: fn(pb: self ref Progressbar); +}; + +Progressbar.mk(t: ref Tk->Toplevel, canvas: string, csize: Point): ref Progressbar +{ + return ref Progressbar(t, canvas, csize, nil); +} + +Progressbar.tkcreate(pb: self ref Progressbar): string +{ + return sys->sprint("canvas %s -width %d -height %d", pb.canvas, pb.csize.x, pb.csize.y); +} + +Progressbar.setcolour(pb: self ref Progressbar, colour: string) +{ + if(pb.blobs != nil) + tkcmd(pb.t, sys->sprint("%s itemconfigure %s -fill %s; update", pb.canvas, hd pb.blobs, colour)); +} + +Progressbar.stepto(pb: self ref Progressbar, step: int, col: string) +{ + for(nblob := len pb.blobs; nblob > step+1; nblob--){ + tkcmd(pb.t, sys->sprint("%s delete %s", pb.canvas, hd pb.blobs)); + pb.blobs = tl pb.blobs; + } + if(nblob == step+1) + return; + p := Point(step*(Blobx+Gapx), 0); + r := Rect(p, p.add((Blobx, pb.csize.y-2))); + pb.blobs = tkcmd(pb.t, sys->sprint("%s create rectangle %d %d %d %d -fill %s", pb.canvas, r.min.x,r.min.y, r.max.x,r.max.y, col)) :: pb.blobs; +} + +Progressbar.destroy(pb: self ref Progressbar) +{ + tk->cmd(pb.t, "destroy "+pb.canvas); # ignore errors +} + +tkcmd(t: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(t, s); + if(e != nil && e[0] == '!') + sys->print("pppchat: tk error: %s [%s]\n", e, s); + return e; +} + +kill(pid: int) +{ + if(pid > 0 && (fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE)) != nil) + sys->fprint(fd, "kill"); +} + +error(s: string) +{ + sys->fprint(sys->fildes(2), "pppchat: %s\n", s); + raise "fail:error"; +} + +X(s: string): string +{ + if(dict != nil) + return dict.xlate(s); + return s; +} diff --git a/appl/cmd/ip/nppp/ppplink.b b/appl/cmd/ip/nppp/ppplink.b new file mode 100644 index 00000000..5f0e9686 --- /dev/null +++ b/appl/cmd/ip/nppp/ppplink.b @@ -0,0 +1,782 @@ +implement PPPlink; + +# +# Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved. +# + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "arg.m"; + +include "cfgfile.m"; + cfg: CfgFile; + ConfigFile: import cfg; + +include "lock.m"; +include "modem.m"; +include "script.m"; + +include "sh.m"; + +include "translate.m"; + translate: Translate; + Dict: import translate; + dict: ref Dict; + +PPPlink: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +PPPInfo: adt { + ipaddr: string; + ipmask: string; + peeraddr: string; + maxmtu: string; + username: string; + password: string; +}; + +modeminfo: ref Modem->ModemInfo; +context: ref Draw->Context; +pppinfo: ref PPPInfo; +scriptinfo: ref Script->ScriptInfo; +isp_number: string; +lastCdir: ref Sys->Dir; # state of file when last read +netdir := "/net"; + +Packet: adt { + src: array of byte; + dst: array of byte; + data: array of byte; +}; + +DEFAULT_ISP_DB_PATH: con "/services/ppp/isp.cfg"; # contains pppinfo & scriptinfo +DEFAULT_MODEM_DB_PATH: con "/services/ppp/modem.cfg"; # contains modeminfo +MODEM_DB_PATH: con "modem.cfg"; # contains modeminfo +ISP_DB_PATH: con "isp.cfg"; # contains pppinfo & scriptinfo + +primary := 0; +framing := 1; + +Disconnected, Modeminit, Dialling, Modemup, Scriptstart, Scriptdone, Startingppp, Startedppp, Login, Linkup: con iota; +Error: con -1; + +Ignorems: con 10*1000; # time to ignore outgoing packets between dial attempts + +statustext := array[] of { +Disconnected => "Disconnected", +Modeminit => "Initializing Modem", +Dialling => "Dialling Service Provider", +Modemup => "Logging Into Network", +Scriptstart => "Executing Login Script", +Scriptdone => "Script Execution Complete", +Startingppp => "Logging Into Network", +Startedppp => "Logging Into Network", +Login => "Verifying Password", +Linkup => "Connected", +}; + +usage() +{ + sys->fprint(sys->fildes(2), "usage: ppplink [-P] [-f] [-m mtu] [local [remote]]\n"); + raise "fail:usage"; +} + +init(ctxt: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + translate = load Translate Translate->PATH; + if(translate != nil) { + translate->init(); + dictname := translate->mkdictname("", "pppclient"); + (dict, nil) = translate->opendict(dictname); + } + mtu := 1450; + + arg := load Arg Arg->PATH; + if(arg == nil) + error(0, sys->sprint("can't load %s: %r", Arg->PATH)); + arg->init(args); + while((c := arg->opt()) != 0) + case c { + 'm' => + if((s := arg->arg()) == nil || !(s[0]>='0' && s[0]<='9')) + usage(); + mtu = int s; + 'P' => + primary = 1; + 'f' => + framing = 0; + * => + usage(); + } + args = arg->argv(); + arg = nil; + localip := "10.9.8.7"; # should be something locally unique + fake := 1; + if(args != nil){ + fake = 0; + localip = hd args; + args = tl args; + } + + cerr := configinit(); + if(cerr != nil) + error(0, sys->sprint("can't configure: %s", cerr)); + context = ctxt; + + # make default (for now) + # if packet appears, start ppp and reset routing until it stops + + (cfd, dir, err) := getifc(); + if(err != nil) + error(0, err); + + if(sys->fprint(cfd, "bind pkt") < 0) + error(0, sys->sprint("can't bind pkt: %r")); + if(sys->fprint(cfd, "add %s 255.255.255.0 10.9.8.0 %d", localip, mtu) < 0) + error(0, sys->sprint("can't add ppp addresses: %r")); + if(primary && addroute("0", "0", localip) < 0) + error(0, sys->sprint("can't add default route: %r")); + dfd := sys->open(dir+"/data", Sys->ORDWR); + if(dfd == nil) + error(0, sys->sprint("can't open %s: %r", dir)); + + sys->pctl(Sys->NEWPGRP, nil); + + packets := chan of ref Packet; + spawn netreader(dfd, dir, localip, fake, packets); + + logger := chan of (int, string); + iocmd := sys->file2chan("/chan", "pppctl"); + if(iocmd == nil) + error(0, sys->sprint("can't create /chan/pppctl: %r")); + spawn servestatus(iocmd.read, logger); + + starteduser := 0; + lasttime := 0; + + for(;;) alt{ + (nil, data, nil, wc) := <-iocmd.write => # remote io control + if(wc == nil) + break; + (nil, flds) := sys->tokenize(string data, " \t"); + if(len flds > 1){ + case hd flds { + "cancel" or "disconnect" or "hangup" => + ; # ignore it + "connect" => + # start connection ... + ; + * => + wreply(wc, (0, "illegal request")); + continue; + } + } + wreply(wc, (len data, nil)); + + pkt := <-packets => + sys->print("ppplink: received packet %s->%s: %d bytes\n", ipa(pkt.src), ipa(pkt.dst), len pkt.data); + if(abs(sys->millisec()-lasttime) < Ignorems){ + sys->print("ppplink: ignored, not enough time elapsed yet between dial attempts\n"); + break; + } + (ok, stat) := sys->stat(ISP_DB_PATH); + if(ok < 0 || lastCdir == nil || !samefile(*lastCdir, stat)){ + cerr = configinit(); + if(cerr != nil){ + sys->print("ppplink: can't reconfigure: %s\n", cerr); + # use existing configuration + } + } + if(!starteduser){ + sync := chan of int; + spawn userinterface(sync); + starteduser = <-sync; + } + (ppperr, pppdir) := makeconnection(packets, logger, iocmd.write); + lasttime = sys->millisec(); + if(ppperr == nil){ + sys->print("ppplink: connected on %s\n", pppdir); + # converse ... +sys->sleep(120*1000); + }else{ + sys->print("ppplink: ppp connect error: %s\n", ppperr); + hangup(pppdir); + } + } +} + +servestatus(reader: chan of (int, int, int, Sys->Rread), updates: chan of (int, string)) +{ + statuspending := 0; + statusreq: (int, int, Sys->Rread); + step := Disconnected; + statuslist := statusline(step, step, nil) :: nil; + + for(;;) alt{ + (off, nbytes, fid, rc) := <-reader=> + if(rc == nil){ + statuspending = 0; + if(step == Disconnected) + statuslist = nil; + break; + } + if(statuslist == nil){ + if(statuspending){ + alt{ + rc <-= (nil, "pppctl file already in use") => ; + * => ; + } + break; + } + statusreq = (nbytes, fid, rc); + statuspending = 1; + break; + } + alt{ + rc <-= reads(hd statuslist, 0, nbytes) => + statuslist = tl statuslist; + * => ; + } + + (code, arg) := <-updates => + # convert to string + if(code != Error) + step = code; + status := statusline(step, code, arg); + if(code == Error) + step = Disconnected; + statuslist = appends(statuslist, status); + sys->print("status: %d %d %s\n", step, code, status); + if(statuspending){ + (nbytes, nil, rc) := statusreq; + statuspending = 0; + alt{ + rc <-= reads(hd statuslist, 0, nbytes) => + statuslist = tl statuslist; + * => + ; + } + } + } +} + +makeconnection(packets: chan of ref Packet, logger: chan of (int, string), writer: chan of (int, array of byte, int, Sys->Rwrite)): (string, string) +{ + result := chan of (string, string); + sync := chan of int; + spawn pppconnect(result, sync, logger); + pid := <-sync; + for(;;) alt{ + (err, pppdir) := <-result => + # pppconnect finished + return (err, pppdir); + + pkt := <-packets => + # ignore packets whilst connecting + sys->print("ppplink: ignored packet %s->%s: %d byten", ipa(pkt.src), ipa(pkt.dst), len pkt.data); + + (nil, data, nil, wc) := <-writer => # user control + if(wc == nil) + break; + (nil, flds) := sys->tokenize(string data, " \t"); + if(len flds > 1){ + case hd flds { + "connect" => + ; # ignore it + "cancel" or "disconnect" or "hangup"=> + kill(pid, "killgrp"); + wreply(wc, (len data, nil)); + return ("cancelled", nil); + * => + wreply(wc, (0, "illegal request")); + continue; + } + } + wreply(wc, (len data, nil)); + } +} + +wreply(wc: chan of (int, string), v: (int, string)) +{ + alt{ + wc <-= v => ; + * => ; + } +} + +appends(l: list of string, s: string): list of string +{ + if(l == nil) + return s :: nil; + return hd l :: appends(tl l, s); +} + +statusline(step: int, code: int, arg: string): string +{ + s: string; + if(code >= 0 && code < len statustext){ + n := "step"; + if(code == Linkup) + n = "connect"; + s = sys->sprint("%d %d %s %s", step, len statustext, n, X(statustext[code])); + }else + s = sys->sprint("%d %d error", step, len statustext); + if(arg != nil) + s += sys->sprint(": %s", arg); + return s; +} + +getifc(): (ref Sys->FD, string, string) +{ + clonefile := netdir+"/ipifc/clone"; + cfd := sys->open(clonefile, Sys->ORDWR); + if(cfd == nil) + return (nil, nil, sys->sprint("can't open %s: %r", clonefile)); + buf := array[32] of byte; + n := sys->read(cfd, buf, len buf); + if(n <= 0) + return (nil, nil, sys->sprint("can't read %s: %r", clonefile)); + return (cfd, netdir+"/ipifc/" + string buf[0:n], nil); +} + +addroute(addr, mask, gate: string): int +{ + fd := sys->open(netdir+"/iproute", Sys->OWRITE); + if(fd == nil) + return -1; + return sys->fprint(fd, "add %s %s %s", addr, mask, gate); +} + +# uchar vihl; /* Version and header length */ +# uchar tos; /* Type of service */ +# uchar length[2]; /* packet length */ +# uchar id[2]; /* ip->identification */ +# uchar frag[2]; /* Fragment information */ +# uchar ttl; /* Time to live */ +# uchar proto; /* Protocol */ +# uchar cksum[2]; /* Header checksum */ +# uchar src[4]; /* IP source */ +# uchar dst[4]; /* IP destination */ +IPhdrlen: con 20; + +netreader(dfd: ref Sys->FD, dir: string, localip: string, fake: int, outc: chan of ref Packet) +{ + buf := array [32*1024] of byte; + while((n := sys->read(dfd, buf, len buf)) > 0){ + if(n < IPhdrlen){ + sys->print("ppplink: received short packet: %d bytes\n", n); + continue; + } + pkt := ref Packet; + if(n < 9*1024){ + pkt.data = array[n] of byte; + pkt.data[0:] = buf[0:n]; + }else{ + pkt.data = buf[0:n]; + buf = array[32*1024] of byte; + } + pkt.src = pkt.data[12:]; + pkt.dst = pkt.data[16:]; + outc <-= pkt; + } + if(n < 0) + error(1, sys->sprint("packet interface read error: %r")); + else if(n == 0) + error(1, "packet interface: end of file"); +} + +ipa(a: array of byte): string +{ + if(len a < 4) + return "???"; + return sys->sprint("%d.%d.%d.%d", int a[0], int a[1], int a[2], int a[3]); +} + +reads(str: string, off, nbytes: int): (array of byte, string) +{ + bstr := array of byte str; + slen := len bstr; + if(off < 0 || off >= slen) + return (nil, nil); + if(off + nbytes > slen) + nbytes = slen - off; + if(nbytes <= 0) + return (nil, nil); + return (bstr[off:off+nbytes], nil); +} + +readppplog(log: chan of (int, string), errfile: string, pidc: chan of int) +{ + pidc <-= sys->pctl(0, nil); + src := sys->open(errfile, Sys->OREAD); + if(src == nil) + log <-= (Error, sys->sprint("can't open %s: %r", errfile)); + + buf := array[1024] of byte; + connected := 0; + lasterror := ""; + + while((count := sys->read(src, buf, len buf)) > 0) { + (nil, tokens) := sys->tokenize(string buf[:count],"\n"); + for(; tokens != nil; tokens = tl tokens) { + case hd tokens { + "no error" => + log <-= (Linkup, nil); + lasterror = nil; + connected = 1; + "permission denied" => + lasterror = X("Username or Password Incorrect"); + log <-= (Error, lasterror); + "write to hungup channel" => + lasterror = X("Remote Host Hung Up"); + log <-= (Error, lasterror); + * => + lasterror = X(hd tokens); + log <-= (Error, lasterror); + } + } + } + if(count == 0 && connected && lasterror == nil){ # should change ip/pppmedium.c instead? + #hangup(nil); + log <-= (Error, X("Lost Connection")); + } +} + +dialup(mi: ref Modem->ModemInfo, number: string, scriptinfo: ref Script->ScriptInfo, logchan: chan of (int, string)): (string, ref Sys->Connection) +{ + logchan <-= (Modeminit, nil); + + # open & init the modem + + modeminfo = mi; + modem := load Modem Modem->PATH; + if(modem == nil) + return (sys->sprint("can't load %s: %r", Modem->PATH), nil); + err := modem->init(); + if(err != nil) + return (sys->sprint("couldn't init modem: %s", err), nil); + Device: import modem; + d := Device.new(modeminfo, 1); + logchan <-= (Dialling, number); + err = d.dial(number); + if(err != nil){ + d.close(); + return (err, nil); + } + logchan <-= (Modemup, nil); + + # login script + + if(scriptinfo != nil) { + logchan <-= (Scriptstart, nil); + err = runscript(modem, d, scriptinfo); + if(err != nil){ + d.close(); + return (err, nil); + } + logchan <-= (Scriptdone, nil); + } + + mc := d.close(); + return (nil, mc); + +} + +startppp(logchan: chan of (int, string), pppinfo: ref PPPInfo): (string, string) +{ + (ifd, dir, err) := getifc(); + if(ifd == nil) + return (err, nil); + + sync := chan of int; + spawn readppplog(logchan, dir + "/err", sync); # unbind gives eof on err + <-sync; + + if(pppinfo.ipaddr == nil) + pppinfo.ipaddr = "-"; +# if(pppinfo.ipmask == nil) +# pppinfo.ipmask = "255.255.255.255"; + if(pppinfo.peeraddr == nil) + pppinfo.peeraddr = "-"; + if(pppinfo.maxmtu == nil) + pppinfo.maxmtu = "-"; +# if(pppinfo.maxmtu <= 0) +# pppinfo.maxmtu = mtu; +# if(pppinfo.maxmtu < 576) +# pppinfo.maxmtu = 576; + if(pppinfo.username == nil) + pppinfo.username = "-"; + if(pppinfo.password == nil) + pppinfo.password = "-"; + + ifc := "bind ppp "+modeminfo.path+" "+ pppinfo.ipaddr+" "+pppinfo.peeraddr+" "+pppinfo.maxmtu + +" "+string framing+" "+pppinfo.username+" "+pppinfo.password; + + if(sys->fprint(ifd, "%s", ifc) < 0) + return (sys->sprint("can't bind ppp to %s: %r", dir), nil); + + sys->print("ppplink: %s\n", ifc); + + return (nil, dir); +} + +runscript(modem: Modem, dev: ref Modem->Device, scriptinfo: ref Script->ScriptInfo): string +{ + script := load Script Script->PATH; + if(script == nil) + return sys->sprint("can't load %s: %r", Script->PATH); + err := script->init(modem); + if(err != nil) + return err; + return script->execute(dev, scriptinfo); +} + +hangup(pppdir: string) +{ + sys->print("ppplink: hangup...\n"); + if(pppdir != nil){ # shut down the PPP link + fd := sys->open(pppdir + "/ctl", Sys->OWRITE); + if(fd == nil || sys->fprint(fd, "unbind") < 0) + sys->print("ppplink: hangup: can't unbind ppp on %s: %r\n", pppdir); + fd = nil; + } + modem := load Modem Modem->PATH; + if(modem == nil) { + sys->print("ppplink: hangup: can't load %s: %r", Modem->PATH); + return; + } + err := modem->init(); + if(err != nil){ + sys->print("ppplink: hangup: couldn't init modem: %s", err); + return; + } + Device: import modem; + d := Device.new(modeminfo, 1); + if(d != nil){ + d.onhook(); + d.close(); + } +} + +kill(pid: int, msg: string) +{ + fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + if(fd == nil || sys->fprint(fd, "%s", msg) < 0) + sys->print("pppclient: can't %s %d: %r\n", msg, pid); +} + +error(dokill: int, s: string) +{ + sys->fprint(sys->fildes(2), "ppplink: %s\n", s); + if(dokill) + kill(sys->pctl(0, nil), "killgrp"); + raise "fail:error"; +} + +X(s : string) : string +{ + if(dict != nil) + return dict.xlate(s); + return s; +} + +cfile(file: string): string +{ + if(len file > 0 && file[0] == '/') + return file; + return "/usr/"+user()+"/config/"+file; +} + +user(): string +{ + fd := sys->open("/dev/user", Sys->OREAD); + buf := array[64] of byte; + if(fd != nil && (n := sys->read(fd, buf, len buf)) > 0) + return string buf[0:n]; + return "inferno"; # hmmm. +} + +cfvalue(c: ref ConfigFile, key: string) :string +{ + s := ""; + for(values := c.getcfg(key); values != nil; values = tl values){ + if(s != "") + s[len s] = ' '; + s += hd values; + } + return s; +} + +configinit(): string +{ + cfg = load CfgFile CfgFile->PATH; + if(cfg == nil) + return sys->sprint("can't load %s: %r", CfgFile->PATH); + + # Modem Configuration + + modemdb := cfile(MODEM_DB_PATH); + cfg->verify(DEFAULT_MODEM_DB_PATH, modemdb); + modemcfg := cfg->init(modemdb); + if(modemcfg == nil) + return sys->sprint("can't open %s: %r", modemdb); + modeminfo = ref Modem->ModemInfo; + modeminfo.path = cfvalue(modemcfg, "PATH"); + modeminfo.init = cfvalue(modemcfg, "INIT"); + modeminfo.country = cfvalue(modemcfg, "COUNTRY"); + modeminfo.other = cfvalue(modemcfg, "OTHER"); + modeminfo.errorcorrection = cfvalue(modemcfg,"CORRECT"); + modeminfo.compression = cfvalue(modemcfg,"COMPRESS"); + modeminfo.flowctl = cfvalue(modemcfg,"FLOWCTL"); + modeminfo.rateadjust = cfvalue(modemcfg,"RATEADJ"); + modeminfo.mnponly = cfvalue(modemcfg,"MNPONLY"); + modeminfo.dialtype = cfvalue(modemcfg,"DIALING"); + if(modeminfo.dialtype!="ATDP") + modeminfo.dialtype="ATDT"; + + ispdb := cfile(ISP_DB_PATH); + cfg->verify(DEFAULT_ISP_DB_PATH, ispdb); + sys->print("cfg->init(%s)\n", ispdb); + + # ISP Configuration + pppcfg := cfg->init(ispdb); + if(pppcfg == nil) + return sys->sprint("can't read or create ISP configuration file %s: %r", ispdb); + (ok, stat) := sys->stat(ispdb); + if(ok >= 0) + lastCdir = ref stat; + + pppinfo = ref PPPInfo; + isp_number = cfvalue(pppcfg, "NUMBER"); + pppinfo.ipaddr = cfvalue(pppcfg,"IPADDR"); + pppinfo.ipmask = cfvalue(pppcfg,"IPMASK"); + pppinfo.peeraddr = cfvalue(pppcfg,"PEERADDR"); + pppinfo.maxmtu = cfvalue(pppcfg,"MAXMTU"); + pppinfo.username = cfvalue(pppcfg,"USERNAME"); + pppinfo.password = cfvalue(pppcfg,"PASSWORD"); + + info := pppcfg.getcfg("SCRIPT"); + if(info != nil) { + scriptinfo = ref Script->ScriptInfo; + scriptinfo.path = hd info; + scriptinfo.username = pppinfo.username; + scriptinfo.password = pppinfo.password; + } else + scriptinfo = nil; + + info = pppcfg.getcfg("TIMEOUT"); + if(info != nil) + scriptinfo.timeout = int (hd info); + cfg = nil; # unload it + + if(modeminfo.path == nil) + return "no modem device configured"; + if(isp_number == nil) + return "no telephone number configured for ISP"; + + return nil; +} + +isipaddr(a: string): int +{ + i, c, ac, np : int = 0; + + for(i = 0; i < len a; i++) { + c = a[i]; + if(c >= '0' && c <= '9') { + np = 10*np + c - '0'; + continue; + } + if(c == '.' && np) { + ac++; + if(np > 255) + return 0; + np = 0; + continue; + } + return 0; + } + return np && np < 256 && ac == 3; +} + +userinterface(sync: chan of int) +{ + pppgui := load Command "pppchat.dis"; + if(pppgui == nil){ + sys->fprint(sys->fildes(2), "ppplink: can't load %s: %r\n", "/dis/svc/nppp/pppchat.dis"); + # TO DO: should be optional + sync <-= 0; + } + + sys->pctl(Sys->NEWPGRP|Sys->NEWFD, list of {0, 1, 2}); + sync <-= sys->pctl(0, nil); + pppgui->init(context, "pppchat" :: nil); +} + +pppconnect(result: chan of (string, string), sync: chan of int, status: chan of (int, string)) +{ + sys->pctl(Sys->NEWPGRP|Sys->NEWFD, list of {0, 1, 2}); + sync <-= sys->pctl(0, nil); + pppdir: string; + (err, mc) := dialup(modeminfo, isp_number, scriptinfo, status); # mc keeps connection open until startppp binds it to ppp + if(err == nil){ + if(0 && (cfd := mc.cfd) != nil){ + sys->fprint(cfd, "m1"); # cts/rts flow control/fifo's on + sys->fprint(cfd, "q64000"); # increase queue size to 64k + sys->fprint(cfd, "n1"); # nonblocking writes on + sys->fprint(cfd, "r1"); # rts on + sys->fprint(cfd, "d1"); # dtr on + } + status <-= (Startingppp, nil); + (err, pppdir) = startppp(status, pppinfo); + if(err == nil){ + status <-= (Startedppp, nil); + result <-= (nil, pppdir); + return; + } + } + status <-= (Error, err); + result <-= (err, nil); +} + +getspeed(file: string): string +{ + return findrate("/dev/modemstat", "rcvrate" :: "baud" :: nil); +} + +findrate(file: string, opt: list of string): string +{ + fd := sys->open(file, sys->OREAD); + if(fd == nil) + return nil; + buf := array [1024] of byte; + n := sys->read(fd, buf, len buf); + if(n <= 1) + return nil; + (nil, flds) := sys->tokenize(string buf[0:n], " \t\r\n"); + for(; flds != nil; flds = tl flds) + for(l := opt; l != nil; l = tl l) + if(hd flds == hd l) + return hd tl flds; + return nil; +} + +samefile(d1, d2: Sys->Dir): int +{ + return d1.dev==d2.dev && d1.dtype==d2.dtype && + d1.qid.path==d2.qid.path && d1.qid.vers==d2.qid.vers && + d1.mtime==d2.mtime; +} + +abs(n: int): int +{ + if(n < 0) + return -n; + return n; +} diff --git a/appl/cmd/ip/nppp/ppptest.b b/appl/cmd/ip/nppp/ppptest.b new file mode 100644 index 00000000..af8e16e0 --- /dev/null +++ b/appl/cmd/ip/nppp/ppptest.b @@ -0,0 +1,90 @@ +# Last change: R 24 May 2001 11:05 am +implement PPPTest; + +include "sys.m"; + sys: Sys; +include "draw.m"; + +include "lock.m"; +include "modem.m"; +include "script.m"; +include "pppclient.m"; +include "pppgui.m"; + +PPPTest: module { + init: fn(nil: ref Draw->Context, args: list of string); +}; +usage() +{ + sys->print("ppptest device modem_init tel user password \n"); + sys->print("Example: ppptest /dev/modem atw2 4125678 rome xxxxxxxx\n"); + exit; + +} +init( ctxt: ref Draw->Context, argv: list of string ) +{ + sys = load Sys Sys->PATH; + + mi: Modem->ModemInfo; + pi: PPPClient->PPPInfo; + tel : string; +# si: Script->ScriptInfo; + argv = tl argv; + if(argv == nil) + usage(); + else + mi.path = hd argv; + + argv = tl argv; + if(argv == nil) + usage(); + else + mi.init = hd argv; + argv = tl argv; + if(argv == nil) + usage(); + else + tel = hd argv; + argv = tl argv; + if(argv == nil) + usage(); + else + pi.username = hd argv; + argv = tl argv; + if(argv==nil) + usage(); + else + pi.password = hd argv; + + + #si.path = "rdid.script"; + #si.username = "ericvh"; + #si.password = "foobar"; + #si.timeout = 60; + + + ppp := load PPPClient PPPClient->PATH; + + logger := chan of int; + + spawn ppp->connect( ref mi, tel, nil, ref pi, logger ); + + pppgui := load PPPGUI PPPGUI->PATH; + (respchan, err) := pppgui->init(ctxt, logger, ppp, nil); + if(err != nil){ + sys->print("ppptest: can't %s: %s\n", PPPGUI->PATH, err); + exit; + } + + event := 0; + while(1) { + event =<- respchan; + sys->print("GUI event received: %d\n",event); + if(event) { + sys->print("success"); + exit; + } else { + raise "fail: Couldn't connect to ISP"; + } + } +} diff --git a/appl/cmd/ip/nppp/script.b b/appl/cmd/ip/nppp/script.b new file mode 100644 index 00000000..d929ff7a --- /dev/null +++ b/appl/cmd/ip/nppp/script.b @@ -0,0 +1,171 @@ +implement Script; + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "string.m"; + str: String; + +include "lock.m"; +include "modem.m"; + modem: Modem; + Device: import modem; + +include "script.m"; + +Scriptlim: con 32*1024; # should be enough for all + +init(mm: Modem): string +{ + sys = load Sys Sys->PATH; + modem = mm; + str = load String String->PATH; + if(str == nil) + return sys->sprint("can't load %s: %r", String->PATH); + return nil; +} + +execute(m: ref Modem->Device, scriptinfo: ref ScriptInfo): string +{ + if(scriptinfo.path != nil) { + if(m.trace) + sys->print("script: using %s\n",scriptinfo.path); + # load the script + err: string; + (scriptinfo.content, err) = scriptload(scriptinfo.path); + if(err != nil) + return err; + }else{ + if(m.trace) + sys->print("script: using inline script\n"); + } + + if(scriptinfo.timeout == 0) + scriptinfo.timeout = 20; + + tend := sys->millisec() + 1000*scriptinfo.timeout; + + for(conv := scriptinfo.content; conv != nil; conv = tl conv){ + e, s: string = nil; + p := hd conv; + if(len p == 0) + continue; + if(m.trace) + sys->print("script: %s\n",p); + if(p[0] == '-') { # just send + if(len p == 1) + continue; + s = p[1:]; + } else { + (n, esl) := sys->tokenize(p, "-"); + if(n > 0) { + e = hd esl; + esl = tl esl; + if(n > 1) + s = hd esl; + } + } + if(e != nil) { + if(match(m, special(e,scriptinfo), tend-sys->millisec()) == 0) { + if(m.trace) + sys->print("script: match failed\n"); + return "script failed"; + } + } + if(s != nil) + m.send(special(s, scriptinfo)); + } + if(m.trace) + sys->print("script: done\n"); + return nil; +} + +match(m: ref Modem->Device, s: string, msec: int): int +{ + for(;;) { + c := m.getc(msec); + if(c == '\r') + c = '\n'; + if(m.trace) + sys->print("%c",c); + if(c == 0) + return 0; + head: + while(c == s[0]) { + i := 1; + while(i < len s) { + c = m.getc(msec); + if(c == '\r') + c = '\n'; + if(m.trace) + sys->print("%c",c); + if(c == 0) + return 0; + if(c != s[i]) + continue head; + i++; + } + return 1; + } + if(c == '~') + return 1; # assume PPP for now + } +} + +# +# Expand special script sequences +# +special(s: string, scriptinfo: ref ScriptInfo): string +{ + if(s == "$username") # special variable + s = scriptinfo.username; + else if(s == "$password") + s = scriptinfo.password; + return deparse(s); +} + +deparse(s: string): string +{ + r: string = ""; + for(i:=0; i < len s; i++) { + c := s[i]; + if(c == '\\' && i+1 < len s) { + c = s[++i]; + case c { + 't' => c = '\t'; + 'n' => c = '\n'; + 'r' => c = '\r'; + 'b' => c = '\b'; + 'a' => c = '\a'; + 'v' => c = '\v'; + '0' => c = '\0'; + '$' => c = '$'; + 'u' => + if(i+4 < len s) { + i++; + (c, nil) = str->toint(s[i:i+4], 16); + i+=3; + } + } + } + r[len r] = c; + } + return r; +} + +scriptload(path: string): (list of string, string) +{ + dfd := sys->open(path, Sys->OREAD); + if(dfd == nil) + return (nil, sys->sprint("can't open script %s: %r", path)); + + b := array[Scriptlim] of byte; + n := sys->read(dfd, b, len b); + if(n < 0) + return (nil, sys->sprint("can't read script %s: %r", path)); + + (nil, script) := sys->tokenize(string b[0:n], "\n"); + return (script, nil); +} diff --git a/appl/cmd/ip/nppp/script.m b/appl/cmd/ip/nppp/script.m new file mode 100644 index 00000000..a1f66e06 --- /dev/null +++ b/appl/cmd/ip/nppp/script.m @@ -0,0 +1,15 @@ +Script: module +{ + PATH: con "/dis/ip/nppp/script.dis"; + + ScriptInfo: adt { + path: string; + content: list of string; + timeout: int; + username: string; + password: string; + }; + + init: fn(m: Modem): string; + execute: fn(m: ref Modem->Device, scriptinfo: ref ScriptInfo): string; +}; |
