commit eb120e076edd9edb9bbc38e420a0594ddd4ebbea Author: Lars Brinkhoff Date: Sat Oct 20 10:34:00 2018 +0000 Files from ftp.ultimate.com: /pdp10/bucs20-anon/rsh. diff --git a/rsh.mid b/rsh.mid new file mode 100644 index 0000000..f770112 --- /dev/null +++ b/rsh.mid @@ -0,0 +1,343 @@ + Title RSH - Speak Un*x rshell protocol + .DecSave + +DBG==0 +Ifndef TCPX31,TCPX31=603436 ;Connection error or connection rejected +Ifndef TCPX19,TCPX19=603422 ;TCP connection allready exists + +Define Error &s + Jrst [ Hrroi 1,[Asciz s] + Jrst .Error ] +Termin + +Define JError &s + Jrst [ Hrroi 1,[Asciz s] + Jrst .Jerror ] +Termin + +Define EJError &s + Erjmp [ Hrroi 1,[Asciz s] + Jrst .Jerror ] +Termin + +x==10 ;Loop var +bp==11 ;BP in to InBuf +up==12 ;BP to 4n user +cp==13 ;BP to command text + +p==17 + +Stack: Block +String: Block +Me: Block Strlen ;local user (lower case) +InBuf: Block Strlen ;RSCAN JCL +Host: Block 1 ;4n host number +Jfn: Block 1 ;Net JFN +Prompt: Block 1 ;BP to prompt in GStr + +Chntab: 1,,TTYINT + Block 34. + +Levtab: PC1 ? PC2 ? PC3 +PC1: Block 1 +PC2: Block 1 +PC3: Block 1 + +Crlf: .BYTE 7 ? ^m ? ^j ? .BYTE + +S: Jfcl + RESET + + Movei 1,.FHSLF + Seto 3, + EPCAP + + Move p,[-StkLen,,Stack-1] + + Movei 1,.RSINI + RSCAN + JError 'RSCAN failed' + + Jumpe 1,.Usage + Movn 3,1 + Movei 1,.PRIIN + Hrroi 2,InBuf + SIN + Erjmp .Usage + + Move bp,[440700,,InBuf] + Call SkipNs +.Usage: Error 'Usage: rsh host [-u 4nuser] command' + + Call SkipSp + Jrst .Usage + +IFN DBG,[ + Move 1,bp + PSOUT + Movei 1,"/ + PBOUT +] ;DBG + + Movei 1,.GTHSN ; translate name to number + Move 2,bp + GTHST% + Erjmp [Hrroi 1,[Asciz 'Bad Host'] + Jrst .Jerror ] + Move bp,2 + Movem 3,Host + + Call SkipSp + Jrst .Usage + + Call GetMe + Move cp,bp ;Save bp (might be command) + Hrroi up,Me ;Make 4n user be me + Ildb 1,bp ;Get next char + Jumpe 1,.Usage + Caie 1,"- ;'-'? + Jrst go4it + Ildb 1,bp + Jumpe 1,.Usage + Caie 1,"u + Cain 1,"U + Trna + Error 'Bad flag' + Call SkipSp + Error 'No user' + Move up,bp ;Save user pointer + Call SkipNs ;Skip non-spaces + Error 'No command' + Setz 1, + Idpb 1,bp ;Tie off user + Move cp,bp + +go4it: Call DoOpen + +IFN DBG,[ + Move 1,up + PSOUT + Movei 1,"/ + PBOUT + Move 1,cp + PSOUT + Hrroi 1,CrLf + PSOUT +] ;DBG + + Call DoSend + HALTF + Jrst .-1 + +GetMe: GJINF + Move 2,1 ;Get user number + Hrroi 1,Me + DIRST ;Stringify + Trn + Move 1,[440700,,Me] +LcLop: Ildb 2,1 ;Make lc + Jumpe 2,R + Cail 2,"A + Caile 2,"Z + Jrst LcLop + Addi 2,"a-"A + Dpb 2,1 + Jrst LcLop +R: Ret + +DoOpen: Movei x,1023. ;higest prived port +Srclop: Hrroi 1,String + Hrroi 2,[Asciz /TCP:/] + Setz 3, + SOUT + Movei 2,(x) + Movei 3,10. + NOUT + Jfcl + Hrroi 2,[Asciz /#./] + Setz 3, + SOUT + Move 2,Host + Movei 3,10 + NOUT + jfcl + Hrroi 2,[asciz /-514;PERSIST:30;CONNECT:ACTIVE/] + Setz 3, + SOUT + Idpb 3,2 + + Movsi 1,(GJ%SHT\GJ%ACC) + Hrroi 2,String + GTJFN + JError 'Could not get jfn' + Movem 1,Jfn + + Move 2,[100000,,OF%RD+OF%WR] ;8 bit bytes +tryopn: OPENF + Skipa 3,1 ;Save error code + Jrst Won + Move 1,Jfn + RLJFN + trn + Caie 3,TCPX19 ;Usual error? + Cain 3,TCPX31 ; or this one? + Sojg x,Srclop ; Yes, search.. + JError 'Could not open' ;out of luck +Won: Ret + +DoSend: Hrroi 2,[Asciz '0'] ;Stderr port number + Call SndStr + + Move 2,up ;Send 4n user + Call Sndstr + + Hrroi 2,Me ;Send local user + Call Sndstr + + Move 2,cp ;Send command + Call Sndstr + Call Flush + +GetSts: Move 1,Jfn + BIN + EJerror 'Could not get status byte' + Jumpe 2,PiIni ;AOK, start input PIs + Hrroi 1,[Asciz '4n Error: '] + ESOUT + Jrst OK + +PiIni: Hrroi 1, [Asciz 'Open... +'] ? PSOUT + + Movei 1,.FHSLF + Move 2,[Levtab,,Chntab] + SIR + EIR + + Movsi 2,(Setz) ;Channel 0 + AIC + + Movsi 1,.TICTI ;input, channel 0 + ATI + + Movei 1,.PRIOU + RFMOD + Tlz 2,TT%DAM + SFMOD + +OK: Move 1,Jfn + BIN + Erjmp Done + Movei 1,.PRIOU + BOUT + Jrst OK + +Done: Move 1,Jfn + CLOSF + Jerror 'Close failed' + HALTF + Jrst .-1 + +TTYINT: Push P,1 + Push P,2 + Push P,3 +TTYLOP: Movei 1,.PRIIN + SIBE + Trna + Jrst TTYEND + BIN + Erjmp TTYEND + Cain 2,^M + Jrst TTYLOP + Cain 2,^Z + Jrst ContZ + Move 1, JFN + BOUT + Erjmp TTYLOP + Jrst TTYLOP +TTYEND: Call Flush + Pop P,3 + Pop P,2 + Pop P,1 + DEBRK + +ContZ: Move 1,JFN + CLOSF + trn + HRROI 1,[ASCIZ ' +EOF'] + PSOUT + HALTF + Jrst .-1 + +Flush: Move 1,Jfn + Hrroi 2,[0] + Setz 3, + SOUTR + EJError 'Flush failed' + Ret + +; Send string + nul +; 2/ bp +Sndstr: Move 1,Jfn + Setz 3, + SOUT + EJError 'Sndstr failed' + Setz 2, + BOUT + EJError 'Sndstr failed(2)' + Ret + +IOerr: Jerror 'I/O error' + +; 1/ bp to prompt +Gstr: Movem 1,Prompt +Gstr2: Move 3,Prompt + Move 1,3 + PSOUT + Hrroi 1, String + Move 2, [\RD%CRF] + RDTTY + ErJmp gstr2 + Setz 2, + Dpb 2,1 + Ret + +.Jerror: + ESOUT + Hrroi 1,[Asciz ': '] + PSOUT + Movei 1,.PRIOU + Hrloi 2,.FHSLF + Setz 3, + ERSTR + Skipa + Jfcl + HALTF + Jrst .-1 + +.Error: ESOUT + HALTF + Jrst .-1 + +Skips0: Skipa bp,2 +SkipSp: Move 2,bp + Ildb 1,2 + Jumpe 1,R + Caie 1," ;Space? + Cain 1,^I ; or Tab? + Jrst SkipS0 +Rskp: Aos (p) + Ret + +SkipN0: Skipa bp,2 +SkipNs: Move 2,bp + Ildb 1,2 + Jumpe 1,R + Caie 1," ;Space? + Cain 1,^I ; or Tab? + AosA (p) + Jrst SkipN0 + Ret + + End S diff --git a/rshd.148 b/rshd.148 new file mode 100644 index 0000000..061c339 --- /dev/null +++ b/rshd.148 @@ -0,0 +1,150 @@ +/* + * RSHD.C -- Phil Budne @ BostonU / Distributed Systems + * BSD Un*x style remote shell daemon for Twenex + * + * (c) 1986 Boston University. + * Permission granted to copy for non-profit use. + * + * Written using UTAH PCC-20. Link with RSHLIB. + * + * Scenario: + * Wait for connection, then spawn a new job loaded with RSHSRV, + * NLI with the CJ%LWP (BU -- login without password). + */ + +# include "pcc:stdio.h" +# include "pcc:tops20.h" +# include "pcc:mon_files.h" +# include "pcc:mon_fork.h" +# include "mon_crjob.h" + +main() { + int jfn; + + epcap(FHslf,-1); /* wheel up */ + + for( ; ; ) { + jfn = srvjfn(); /* wait for connect */ + if( jfn < 0 ) { + perror("could not get jfn"); + exit(1); + } /* could not open server */ + worker(jfn); + } /* forever */ +} /* main */ + +worker(jfn) +int jfn; +{ + char foreign[30]; + int host, sock; + + if( (sock = get_fsock(jfn)) > 1023 || (host = get_fhost(jfn)) < 0 || + hostname(foreign, host) < 0 ) + return( punt(jfn,"Permission denied.") ); + + printf("RSHD: contact from %s, port %d\n", foreign, sock); + if( makjob(jfn, "SYSTEM:RSHSRV.EXE") < 0 ) + printf("could not create job"); + +} /* worker */ + +/* + * jfn jfn of TCP: connection + * prog name of .EXE file or NULL + */ + +int makjob(jfn, prog) +char *prog; +int jfn; +{ + int tvt, job; + int crjblk[015], acs[5]; + register int i; + + for( i = 0; i < sizeof( crjblk ); i++ ) + crjblk[i] = 0; + + /* wait 'till attached, load file, give my caps, login w/o password */ + ac1 = Value(CJ_wta) | Value(CJ_fil) | Value(CJ_cap) | Value(CJ_lwp); + ac2 = (int) crjblk; + + crjblk[CJfil] = POINT(prog); /* BP to program to load */ + crjblk[CJtty] = NUlio; /* new job is detached */ + + if( JSYS(JScrjob,acs) == JSerr ) + return( punt(jfn, "Could not create job") ); + job = ac1; /* save job number */ + + tvt = maktvt(jfn); /* convert jfn to tvt */ + if( tvt < 0 ) { + logout(job); /* could not create TVT, kill job */ + return( punt(jfn, "Could not create net virtual terminal") ); + } /* maktvt failed */ + + ac1 = job | (0100000 << 18); /* AT%TRM */ + ac2 = 0; + ac3 = 0; + ac4 = tvt; + if( JSYS(JSatach,acs) == JSerr ) { /* attach job to terminal */ + ac1 = job; /* failed!! */ + JSYS(JSlgout,acs); /* blast job */ + return( -1 ); /* no way to send error text!! */ + } /* atach failed */ + return( job ); +} /* makjob */ + +int maktvt(jfn) +int jfn; +{ + int nvtdes, acs[5]; + + acs[1] = jfn; + if( JSYS(JSatnvt,acs) == JSerr ) + return( -1 ); + + nvtdes = acs[1]; + if( JSYS(JSrfmod,acs) == JSerr ) + return( nvtdes ); + acs[2] |= 040000<<18; /* set TT%LCA */ + JSYS(JSstpar,acs); + + acs[1] = nvtdes; + acs[2] = 031; /* .MOSLW - set width */ + acs[3] = 0; /* to zero */ + JSYS(JSmtopr,acs); + return( nvtdes ); +} + +int get_fsocket(jfn) +int jfn; +{ + int acs[5]; + + ac1 = jfn; + if( JSYS(JSgdsts,acs) == JSerr ) + return( 0377777777777 ); /* +INF as local socket */ + else + return( ac4 ); +} /* get_fsocket */ + +int get_fhost(jfn) +int jfn; +{ + int acs[5]; + + ac1 = jfn; + if( JSYS(JSgdsts,acs) == JSerr ) + return( -1 ); + else + return( ac3 ); +} /* get_fhost */ + +int srvjfn() { + return( trytcp( "TCP:514\026#;CONNECT:PASSIVE" ) ); +} /* srvjfn */ + + +/** Local Modes: * */ +/** Comment Column:40 * */ +/** End: * */ diff --git a/rshd.c b/rshd.c new file mode 100644 index 0000000..a445723 --- /dev/null +++ b/rshd.c @@ -0,0 +1,222 @@ +/* pty version */ + +/* + * RSHD.C -- Phil Budne @ BostonU / Distributed Systems + * BSD Un*x style remote shell daemon for Twenex + * + * (c) 1986 Boston University. + * Permission granted to copy for non-profit use. + * + * Written using UTAH PCC-20. Link with RSHLIB. + * + * Scenario: + * Wait for connection, then spawn a new job loaded with RSHSRV, + * NLI with the CJ%LWP (BU -- login without password). + */ + +# include "pcc:stdio.h" +# include "pcc:tops20.h" +# include "pcc:mon_files.h" +# include "pcc:mon_fork.h" +# include "mon_crjob.h" + +main() { + int jfn; + + epcap(FHslf,-1); /* wheel up */ + + for( ; ; ) { + jfn = srvjfn(); /* wait for connect */ + if( jfn < 0 ) { + perror("could not get jfn"); + exit(1); + } /* could not open server */ + worker(jfn); + } /* forever */ +} /* main */ + +worker(jfn) +int jfn; +{ + char foreign[30], fhnstr[20]; + int host, sock, pty; + + if( (sock = get_fsock(jfn)) > 1023 || (host = get_fhost(jfn)) < 0 || + hostname(foreign, host) < 0 ) + return( punt(jfn,"Permission denied.") ); + + printf("RSHD: contact from %s, port %d\n", foreign, sock); +/***** SHOULD FORK HERE *****/ + if( (pty = makjob(jfn, "USR0:RSHSRV.EXE")) < 0 ) + perror("RSHD: could not create job"); + + fhnstr[0] = 0; + sprintf(fhnstr, "%d", host); + printf("fhnstr: %s\n", fhnstr); + + { + register char *cp; + cp = fhnstr; + do { + ac1 = pty; + ac2 = *cp; + if( *cp == '\0' ) break; + JSYS(JSbout, acs); +/* printf("putting %c (%d)\n", *cp, *cp ); /**/ + } while( *cp++ != '\0' ); + jflush(jfn); + } + + if( fork() > 0 ) { /* parent */ + for( ; ; ) { + int i, acs[5]; + ac1 = pty; /* get from pty to net */ + if( JSYS(JSbin, acs) == JSerr ) + return; + i = ac2; + ac1 = jfn; + if( JSYS(JSbout, acs) == JSerr ) + return; + jflush(jfn); +/* printf("pty -> net: %o (%c)\n", i, i); /**/ + } /* forever parent */ + } /* parent */ + else { + for( ; ; ) { + ac1 = jfn; + if( JSYS(JSbin, acs) == JSerr ) + return; +/* printf("net -> pty: %o (%c)\n", ac2, ac2); /**/ + ac1 = pty; + if( JSYS(JSbout, acs) == JSerr ) + return; + } /* forever parent */ + } /* child */ +} /* worker */ + +/* + * jfn jfn of TCP: connection + * prog name of .EXE file or NULL + */ + +int makjob(jfn, prog) +char *prog; +int jfn; +{ + int pty, job; + int crjblk[015], acs[5]; + register int i; + + for( i = 0; i < sizeof( crjblk ); i++ ) + crjblk[i] = 0; + + /* wait 'till attached, load file, give my caps, login w/o password */ + ac1 = Value(CJ_wta) | Value(CJ_fil) | Value(CJ_cap) | Value(CJ_lwp); + ac2 = (int) crjblk; + + crjblk[CJfil] = POINT(prog); /* BP to program to load */ + crjblk[CJtty] = NUlio; /* new job is detached */ + + if( JSYS(JScrjob,acs) == JSerr ) + return( punt(jfn, "Could not create job") ); + job = ac1; /* save job number */ + + pty = attpty(jfn, job); /* create pty and attach */ + if( pty < 0 ) { + logout(job); /* could not create pty, kill job */ + return( punt(jfn, "Could not create pseudo terminal") ); + } /* attpty failed */ + return( pty ); +} /* makjob */ + +int attpty(jfn, job) +int jfn, job; +{ + int acs[5]; + int pty, ptyoff, maxpty; + register int i; + + ac1 = 026; /* 0,,.PTYPA */ + if( JSYS(JSgetab, acs) == JSerr ) + return( -1 ); + + maxpty = (ac1 >> 18) - 1; + ptyoff = ac1 & 0777777; + + pty = ac1; + for( i = 0; i < maxpty; i++ ) { + char fname[100]; + + fname[0] = 0; + sprintf(fname, "PTY%o:", i); + ac1 = Value(GJ_sht); + ac2 = POINT(fname); + if( JSYS(JSgtjfn,acs) == JSerr ) + return( -1 ); + + pty = ac1; + ac2 = makefield(OF_bsz,8) | /* makefield(OF_mod, 010) | */ + Value(OF_rd) | Value(OF_wr); /* 8bit, image mode, read/write */ + if( JSYS(JSopenf, acs) != JSerr ) + break; + + reljfn(pty); + pty = -1; + } /* for i */ + + if( pty < 0 ) + return( -1 ); + + ac1 = job | (0100000 << 18); /* AT%TRM */ + ac2 = 0; + ac3 = 0; + ac4 = i + ptyoff; /* terminal on pty */ + if( JSYS(JSatach,acs) == JSerr ) { /* attach job to terminal */ + close( pty ); /* toss pty */ + return( -1 ); + } /* atach failed */ + return( pty ); +} + +int get_fsocket(jfn) +int jfn; +{ + int acs[5]; + + ac1 = jfn; + if( JSYS(JSgdsts,acs) == JSerr ) + return( 0377777777777 ); /* +INF as local socket */ + else + return( ac4 ); +} /* get_fsocket */ + +int get_fhost(jfn) +int jfn; +{ + int acs[5]; + + ac1 = jfn; + if( JSYS(JSgdsts,acs) == JSerr ) + return( -1 ); + else + return( ac3 ); +} /* get_fhost */ + +int srvjfn() { + return( trytcp( "TCP:514\026#;CONNECT:PASSIVE" ) ); +} /* srvjfn */ + +jflush(jfn) +int jfn; +{ + char c[2]; + c[0] = 0; + ac1 = jfn; + ac2 = POINT(c); + ac3 = 0; + JSYS(JSsoutr, acs); +} + +/** Local Modes: * */ +/** Comment Column:40 * */ +/** End: * */ diff --git a/rshd.msg b/rshd.msg new file mode 100644 index 0000000..d76c4c9 --- /dev/null +++ b/rshd.msg @@ -0,0 +1,103 @@ +I have tried to implement an RSHD using NVTs also. I ended up with +the same technique that MRC suggested, RSHD just listens for a connect +and verifies that it comes from a 'secure' port (<1024.) on a host we +have a name for, and then CRJOBs a job loaded with "RSHSRV", creates +an NVT and ATACHes the two. + +Problem; +Since we have not read any data we don't know who to log in as. + +Solution; +I added a bit CR%LWP to CRJOB (Login Without Password), this sets a +bit in the SYSFK word of the created fork. + +Problem 2; +Some huge number of useful things under TOPS-20 are not programs but +EXEC commands.. So you hack up your EXEC to read one command from the +RSCAN buffer and then quit when entered in a new entry vector +position. + +Problem 3; +Ok so you have this running, everything looks fine -- but you notice +that when a BSD user types "rsh twenex foo", they get output, then you +see a logout message, and the connection hangs. + +So, you have RSHSRV detach and LGOUT when the hacked EXEC returns. + +Fix this, you still hang, the job never detaches. + +Well, you see TVTs are expected to talk TELNET protocol. DTACH does a +DOBE, which sends a TELNET mark-time command, to which RSHD does not +know how to respond. (You might have noticed that TVT-SMTP jobs hung +for the same reason). + +Cure; have DOBE on a TVT be a NOOP. + +Now the job logs out cleanly, but you STILL hang on your UNIX system. +This may simply be a misfeature of the BSD RSH program, it doesn't +expect anyone to implement RSH for a system that can not 'half close' +a connection. + +But wait, just try to write something that transfers large amounts of +data, say 'RCP' or 'RMT' BINARY DATA... You will find that TVT thruput +is well, awful, and you will lose data espcially anything containing +IAC. + +Problem N.1; +You want to write an RSH for tops-20 do you? Well RSH is +a secure protocol, and depends on the fact that only SuperUser can +open a connection with a local port of <1024. Any connection with a +remote local port of <1024. is to be trusted, and when the data says +the remote user name is "foo" it is to be believed. + +So you can either; leave the monitor the way it came (only ports <512 +protected), or add a new magic bit in SYSFK. If we have just done an +execute only GET% we look at the FDB of the EXE file, if it has a +special (priv'ed) bit lit, we light the SYSFK bit, and the fork is +ENCHANTED. This bit is checked at the same places (several) that +check for WOPR on a TCP open. + +Problem N.2; + +The RSH protocol (and this is used by BSD rsh), specifies that the +first data item is a foreign port (or zero) for sending back data from +the forks stderr, in fact the rsh will wait for a connect on that port +until it proceeds. This connection must originate from a secure port +.. so that the RSHSRV must be created with pre-login WOPR, or be +enchanted (note since enchanted-ness is not a capability it is never +inherited by children). + +TOPS-20 only checks local ports for uniqueness within a job, BSD never +creates the same local port twice. For a reason that escapes me (but +has to do with the STDERR connection), this causes problems. OK, +remove that code too. Whew!! FTP continues to work. + +-- +Moral, Or how I would try to do it next if I had the strength; + +First and foremost this proves that Un*x software and concepts are +completely transportable... between any two Un*x systems :-) + +a) Have RSHD listen for connects, process the data and drop a PTY +connect a job logged into whoever we please and just pass the data. +Feature; require NO monitor changes. 'Bit nasty to have the process +in between handling all the data for all the RSHs. + +b) Create a not logged in job loaded with RSHSRV, CJ%LWP set, and +either WOPR or enchanted. Does a passive listen for RSH connects. +When a connect is received, either spawn a another like us, or notify +an authority via IPCF to do so (DEC DTRSRV (datatrieve) and the new +RMSFAL do just this). RSHSRV then reads and validates the data, +creates the error pipe if needed, and LOGs in. Get a PTY, and assign +it as controlling for an EXEC, and ferry data back and forth between +the PTY and the TCP connection. This avoids having on central octopus +hacking the PTYs. + + + Good Guys RSH + 0 34359738367 + +I wrote all my code using the UTAH PCC-20 compiler, you're welcome to it!! + + Philip Budne + Boston University / Distributed Systems diff --git a/rshlib.c b/rshlib.c new file mode 100644 index 0000000..2212b13 --- /dev/null +++ b/rshlib.c @@ -0,0 +1,94 @@ +/* + * RSHLIB.C -- Phil Budne @ BostonU / Distributed Systems + * Common routines for RSHSRV/RSHD + * + * (c) 1986 Boston University. + * Permission granted to copy for non-profit use. + * + * Written using UTAH PCC-20. + * + */ + +# include "pcc:stdio.h" +# include "pcc:tops20.h" +# include "pcc:mon_files.h" +# include "pcc:mon_fork.h" +# include "mon_crjob.h" +# include "mon_networks.h" + +int punt(jfn, s) +int jfn; +char *s; +{ + write(jfn, "\01", 1); /* pass failure code */ + write(jfn, s, strlen(s)); /* pass failure string */ + write(jfn, "\n", 1); /* send newline */ + return( -1 ); +} + +reljfn(j) +int j; +{ + int acs[5]; + + acs[1] = j; + JSYS(JSrljfn,acs); +} /* reljfn */ + + +int trytcp(name) +char *name; +{ + int acs[5], jfn; + + ac1 = Value(GJ_sht); + ac2 = POINT(name); + if( JSYS(JSgtjfn,acs) == JSerr ) + return( -1 ); + jfn = ac1; /* save jfn */ + + ac2 = 0100400300000; /* 8bit, interactive, rd/wr */ + if( JSYS(JSopenf,acs) == JSerr ) { + reljfn(jfn); + return( -1 ); + } /* openf failed */ + return( jfn ); +} /* trytcp */ + +epcap(fork,i) /* enable process capabilities */ +int fork, i; +{ + int acs[5]; + + ac1 = fork; + ac3 = i; /* enable mask */ + JSYS(JSepcap,acs); /* perform enable */ +} /* epcap */ + +logout(job) +int job; +{ + int acs[5]; + + ac1 = job; + JSYS(JSlgout,acs); /* blast job */ +} /* logout */ + +int hostname(str, numb) /* convert address to name */ +char *str; +int numb; +{ + int acs[5]; + + ac1 = GThns; /* string from address */ + ac2 = POINT(str); /* bp to string */ + ac3 = numb; /* address */ + if( JSYS(JSgthst, acs) == JSerr ) + return( -1 ); + else + return( 0 ); +} /* hostname */ + +/** Local Modes: * */ +/** Comment Column:40 * */ +/** End: * */ diff --git a/rshsrv.219 b/rshsrv.219 new file mode 100644 index 0000000..3736379 --- /dev/null +++ b/rshsrv.219 @@ -0,0 +1,404 @@ +/****************************************************************/ +/* R S H S R V . C */ +/* */ +/* Phil Budne @ Boston U / DSG */ +/* (c) 1986 Boston University. */ +/* Permission granted to copy for non-profit use. */ +/* */ +/* This program is CRJOB'ed not-logged-in by RSHD. */ +/* The monitor must include support for a special bit */ +/* for the CRJOB JSYS so that when CJ%LWP is set the */ +/* job may log in without a password. */ +/* */ +/* After reading and vaidating data we fork */ +/* SYSTEM:RSHEXEC.EXE, and start it at +2 (Version!!). */ +/* RSHEXEC is an EXEC which reads one command, then */ +/* exits it also never prompts or heralds. */ +/* */ +/* This program was written using UTAH TOPS-20 PCC */ +/* */ +/****************************************************************/ + +# include +# include +# include + +# include "pcc:tops20.h" +# include "pcc:mon_files.h" +# include "pcc:mon_fork.h" +# include "mon_networks.h" +# include "mon_crjob.h" + +# define RC_emo 01:35-17 +# define PRast 2 +# define RSini 0 + +/* # define DEBUG 1 /* define to enable debugging */ + +main() { + int jfn; + +# ifndef DEBUG + if( logged_in() ) { + printf("Foo! you are already logged in!!\n"); + exit(999); + } /* already logged in */ +# endif + + setname("RSHSRV"); /* Set up name so not LOGOUT */ + + epcap(FHslf,-1); /* Enable all privs (needs ABS-SOCK) */ + + jfn = openttyraw(); /* get raw tty jfn */ + if( jfn < 0 ) punt(PRiou, "Could not open TTY:"); + doit(jfn); +# ifndef DEBUG + keel(); +# endif +} /* main */ + +logged_in() { + int acs[5]; + + JSYS(JSgjinf, acs); /* get job info */ + if( ac1 == 0 ) + return( 0 ); + else + return( 1 ); + +} /* logged_in */ + +keel() { + int acs[5]; + + JSYS(JSdtach, acs); /* hide embarrassing LOGOUT mess */ + logout(-1); /* log self out */ + JSYS(JShaltf, acs); /* halt?! */ +} /* keel */ + +int openttyraw() { + int acs[5], jfn; + + ac1 = Value(GJ_sht); /* short form */ + ac2 = POINT("TTY:"); /* get TTY: */ + if( JSYS(JSgtjfn, acs) == JSerr ) /* get jfn */ + return( -1 ); /* sigh! */ + jfn = ac1; /* save jfn */ + + ac2 = makefield(OF_bsz,8) | makefield(OF_mod, 010) | + Value(OF_rd) | Value(OF_wr); /* 8bit, image mode, read/write */ + if( JSYS(JSopenf, acs) == JSerr ) { /* open! */ + reljfn(jfn); /* failed, release jfn */ + return( -1 ); /* return failure */ + } /* openf failed */ + + return( jfn ); /* return jfn */ +} /* openttyraw */ + +# define VALJFN jfn /* JFN for validation (0/1) */ + +doit(jfn) +int jfn; +{ + char buff[10], foreign[30], fnuser[17], lcuser[39], command[200],cmd2[200]; + int errport, errjfn, acs[5], fhno, fpno, execfork, userno; + +# ifndef DEBUG + signal(SIGHUP, keel); /* logout if detached */ + /* Doesn't work? Check library.. */ + signal(SIGALRM, keel); /* keel when time runs out */ + alarm( 5 * 60 ); /* set clock for 5 minutes */ + + JSYS(JSgjinf, acs); /* get job info */ + fhno = tvtstat(ac4, 7); /* get TFH word */ + if( fhno < 0 ) + return( punt(jfn, "TFH STAT failed") ); + + fpno = tvtstat(ac4, 011); /* get TFP word */ + if( fpno < 0 ) + return( punt(jfn, "TFP STAT failed") ); + + if( fpno > 1023 ) + return( punt(jfn, "Foreign port .GT. 1023") ); + +# endif + + if( getstr(jfn, buff, 10) ) + return( punt(jfn, "could not read error socket number") ); + + errport = atoi(buff); + if( errport > 0 ) { + errjfn = getcon(fhno, errport); /* establish error stream */ + if( errjfn < 0 ) + return( punt(jfn, "could not open error socket") ); + } /* wants socket for errors */ + else errjfn = jfn; + +# ifndef DEBUG + if( hostname(foreign, fhno) < 0 ) + return( punt(VALJFN, "Could not get name for client host") ); + lowerify(foreign); +# endif + + if( getstr(jfn, lcuser, 39) ) + return( punt(VALJFN, "bad locuser") ); + + if( getstr(jfn, fnuser, 17) ) + return( punt(VALJFN, "bad frnuser") ); + + if( getstr(jfn, command, 200) ) + return( punt(VALJFN, "bad command") ); + + ac1 = Value(RC_emo); + ac2 = POINT(lcuser); + if( JSYS(JSrcusr, acs) == JSerr ) + return( punt(VALJFN, "local user does not exist") ); + userno = ac3; + +# ifndef DEBUG + if( ruserok(foreign, 0, fnuser, lcuser ) != 0 ) + return( punt(VALJFN, "Permission denied.") ); + + ac1 = userno; /* get user # in ac1 */ + ac2 = POINT(""); /* null password */ + ac3 = POINT(""); /* null account */ + if( JSYS(JSlogin, acs) == JSerr ) + return( punt(VALJFN, "LOGIN failed") ); +# endif + + ac1 = CR_cap; /* pass caps */ + if( JSYS(JScfork, acs) == JSerr ) /* create a fork */ + return( punt(VALJFN, "Could not create EXEC fork") ); + execfork = ac1; + + ac1 = Value(GJ_sht) + Value(GJ_old); + ac2 = POINT("SYSTEM:RSHEXEC.EXE"); + if( JSYS(JSgtjfn, acs) == JSerr ) + return( punt(VALJFN, "Could not find SYSTEM:RSHEXEC.EXE") ); + + ac1 = makeword( execfork, getright(ac1) ); + if( JSYS(JSget, acs) == JSerr ) + return( punt(VALJFN, "Could not GET EXEC") ); + +/* doprarg(execfork); */ + + fnstd(cmd2, command); /* convert unix path to '20 form */ + if( rcstring(cmd2) < 0 ) /* place in rscan buffer */ + return( punt(VALJFN, "Could not set up command") ); + + write(VALJFN, "", 1); /* send null (validated ok) */ + + ac1 = execfork; + ac2 = 2; /* start at +2 */ + if( JSYS(JSsfrkv, acs) == JSerr ) + return( punt2(VALJFN, "Could not start EXEC") ); + + ac1 = execfork; + if( JSYS(JSwfork, acs) == JSerr ) + return( punt2(VALJFN, "Could not wait for EXEC") ); + + write(errjfn, "", 1); + + if( errjfn != jfn ) /* if we have an error jfn */ + close(errjfn, Value(CZ_abt)); /* close (and abort) it */ +} /* doit */ + +int tvtstat(tvt, word) +int tvt, word; +{ + int acs[5], result; + + ac1 = tvt + Value(TCP_tv); /* get TVT number in A */ + ac2 = makeword(-1,word); /* get specified word */ + ac3 = makeword(-1,(int) &result); /* result */ + if( JSYS(JSstat, acs) == JSerr ) /* read TCP connection 4n host */ + return( -1 ); + else + return( result ); +} /* tvtstat */ + + +setname(s) +char *s; +{ + int acs[5]; + + ac1 = ac2 = makesix(s); + JSYS(JSsetsn, acs); +} /* setname */ + +int makesix(s) +register char *s; +{ + register int i, j; + register char c; + + j = 0; + for( i = 0; i < 6; i++ ) { + if( (c = *s++) != NULL ) { + if( c < 040 ) c = '?'; + if( c > '_' ) c = c - 040; + j = (j << 6) + c - 040; + } /* if got a char */ + else break; + } /* for */ + + if( i < 6 ) j = j << (6 - i); +} /* makesix */ + +int getstr(jfn, buff, len) /* read counted string, or till NUL */ +char buff[]; +int jfn; +{ + int acs[5]; + + ac1 = jfn; + ac2 = POINT(buff); + ac3 = len; + ac4 = 0; + + if( JSYS(JSsin,acs) == JSerr ) + return( -1 ); + + if( ac3 == 0 || (len - ac3) == 0 ) /* overflow or read nothing? */ + return( -1 ); + else + return( 0 ); +} /* getstr */ + + +/**************** FROM RCMD.C ****************/ + +extern char *index(); + +ruserok(rhost, superuser, ruser, luser) + char *rhost; + int superuser; + char *ruser, *luser; +{ + FILE *hostf; + char ahost[32]; + int first = 1; + + hostf = superuser ? (FILE *)0 : fopen("/etc/hosts.equiv", "r"); +again: + if (hostf) { + while (fgets(ahost, sizeof (ahost), hostf)) { + char *user, *idx; + if ( (idx = index(ahost, '\n')) ) + *idx = 0; + user = index(ahost, ' '); + if (user) + *user++ = 0; + if (!strcmp(rhost, ahost) && + !strcmp(ruser, user ? user : luser)) { + (void) fclose(hostf); + return (0); + } + } + (void) fclose(hostf); + } + if (first == 1) { + char buf[100]; /* BUDD */ + sprintf(buf, "ps:<%s>\026.rhosts", luser); /* BUDD */ + first = 0; + hostf = fopen(buf, "r"); /* BUDD */ + goto again; + } + return (-1); +} /* ruserok */ + +int getcon(fhost, fsock) +int fhost, fsock; +{ + char buffer[50]; + int lsock, jfn; + + for( lsock = 1; lsock < 1024; lsock++ ) { + sprintf(buffer,"TCP:%d\026#.%o-%d;CONN:ACT", lsock, fhost, fsock ); + if( (jfn = trytcp(buffer)) > 0 ) + return( jfn ); + } /* for */ + return( -1 ); +} /* getcon */ + +int punt2(jfn, s) +int jfn; +char *s; +{ + write(jfn, s, strlen(s)); /* pass failure string */ + return( -1 ); +} /* punt2 */ + +doprarg(fork) +int fork; +{ + int scjprb[8], acs[5]; + register int *sp; + + sp = scjprb; + + *sp++ = 4; /* 0 - 4 words.. */ + *sp++ = makeword(0414100, 02545); /* 1 - very magic word */ + *sp++ = 4; /* 2 - word 4 is data */ + *sp++ = 0; /* 3 - nothing */ + *sp++ = (1 << 35); /* 4 - suppress exec herald */ + + ac1 = makeword(PRast, fork); + ac2 = (int) scjprb; + ac3 = 6; + JSYS(JSprarg, acs); + +} /* doprarg */ + +lowerify(s) +register char *s; +{ + register char c; + + while( (c = *s) != NULL ) { + if( isupper( c ) ) *s = tolower( c ); + s++; + } /* while */ +} /* lowerify */ + + +rcstring(s) /* place string in RSCAN buffer */ +register char *s; +{ + char bigbuf[500], lc; + register char c, *d; + int acs[5]; + + d = bigbuf; + while( (c = *s++) != NULL ) { + lc = c; + if( c == '\n' ) { /* convert NL to CRLF */ + *d++ = '\r'; + *d++ = '\n'; + } + else if( c != '\r' ) /* discard CR */ + *d++ = c; + } /* while */ + + if( lc != '\n' ) { /* if last was not NL, add CRLF */ + *d++ = '\r'; + *d++ = '\n'; + } /* last not NL */ + + *d++ = '\0'; /* tie off with NUL */ + + ac1 = POINT(bigbuf); /* insert string into RSCAN */ + if( JSYS(JSrscan, acs) == JSerr ) + return( -1 ); + + ac1 = RSini; /* make RSCAN available as input */ + if( JSYS(JSrscan, acs) == JSerr ) + return( -1 ); + return( 0 ); + +} /* rcstring */ + +/** Local Modes: * */ +/** Comment Column:40 * */ +/** End: * */ diff --git a/rshsrv.c b/rshsrv.c new file mode 100644 index 0000000..2b36274 --- /dev/null +++ b/rshsrv.c @@ -0,0 +1,419 @@ +/* pty version */ +# define BLEH + +/****************************************************************/ +/* R S H S R V . C */ +/* */ +/* Phil Budne @ Boston U / DSG */ +/* (c) 1986 Boston University. */ +/* Permission granted to copy for non-profit use. */ +/* */ +/* This program is CRJOB'ed not-logged-in by RSHD. */ +/* The monitor must include support for a special bit */ +/* for the CRJOB JSYS so that when CJ%LWP is set the */ +/* job may log in without a password. */ +/* */ +/* After reading and vaidating data we fork */ +/* SYSTEM:RSHEXEC.EXE, and start it at +2 (Version!!). */ +/* RSHEXEC is an EXEC which reads one command, then */ +/* exits it also never prompts or heralds. */ +/* */ +/* This program was written using UTAH TOPS-20 PCC */ +/* */ +/****************************************************************/ + +# include +# include +# include + +# include "pcc:tops20.h" +# include "pcc:mon_files.h" +# include "pcc:mon_fork.h" +# include "mon_networks.h" +# include "mon_crjob.h" + +# define RC_emo 01:35-17 +# define PRast 2 +# define RSini 0 + +/* # define DEBUG 1 /* define to enable debugging */ + +main() { + int jfn; + +# ifndef DEBUG + if( logged_in() ) { + printf("Foo! you are already logged in!!\n"); + exit(999); + } /* already logged in */ +# endif + + setname("RSHSRV"); /* Set up name so not LOGOUT */ + + epcap(FHslf,-1); /* Enable all privs (needs ABS-SOCK) */ + + jfn = openttyraw(); /* get raw tty jfn */ + if( jfn < 0 ) punt(PRiou, "Could not open TTY:"); + doit(jfn); +# ifndef DEBUG + keel(); +# endif +} /* main */ + +logged_in() { + int acs[5]; + + JSYS(JSgjinf, acs); /* get job info */ + if( ac1 == 0 ) + return( 0 ); + else + return( 1 ); + +} /* logged_in */ + +keel() { + int acs[5]; + + JSYS(JSdtach, acs); /* hide embarrassing LOGOUT mess */ + logout(-1); /* log self out */ + JSYS(JShaltf, acs); /* halt?! */ +} /* keel */ + +int openttyraw() { + int acs[5], jfn; + + ac1 = Value(GJ_sht); /* short form */ + ac2 = POINT("TTY:"); /* get TTY: */ + if( JSYS(JSgtjfn, acs) == JSerr ) /* get jfn */ + return( -1 ); /* sigh! */ + jfn = ac1; /* save jfn */ + + ac2 = makefield(OF_bsz,8) | makefield(OF_mod, 010) | + Value(OF_rd) | Value(OF_wr); /* 8bit, image mode, read/write */ + if( JSYS(JSopenf, acs) == JSerr ) { /* open! */ + reljfn(jfn); /* failed, release jfn */ + return( -1 ); /* return failure */ + } /* openf failed */ + + if( JSYS(JSrfmod,acs) != JSerr ) { + ac3 &= ~0300; /* clear TT%DAM (image) */ + JSYS(JSsfmod,acs); + } + + return( jfn ); /* return jfn */ +} /* openttyraw */ + +# define VALJFN jfn /* JFN for validation (0/1) */ + +doit(jfn) +int jfn; +{ + char buff[10], foreign[30], fnuser[17], lcuser[39], command[200],cmd2[200]; + char fhnstr[20]; + int errport, errjfn, acs[5], fhno, execfork, userno; + +# ifndef DEBUG + signal(SIGHUP, keel); /* logout if detached */ + /* Doesn't work? Check library.. */ +/* signal(SIGALRM, keel); /* keel when time runs out */ +/* alarm( 5 * 60 ); /* set clock for 5 minutes */ + + if( getstr(jfn, fhnstr, 20 ) ) + return( punt(jfn, "could not get 4n host number") ); + fhno = atoi( fhnstr ); +# ifdef BLEH + printf("fhno: %d\n", fhno); +# endif + + if( getstr(jfn, buff, 10) ) + return( punt(jfn, "could not read error socket number") ); + + errport = atoi(buff); +# ifdef BLEH + printf("errport: %d\n", errport); /**/ +# endif + + if( errport > 0 ) { + errjfn = getcon(fhno, errport); /* establish error stream */ + if( errjfn < 0 ) + return( punt(jfn, "could not open error socket") ); + } /* wants socket for errors */ + else errjfn = jfn; + +# ifdef NOTDEF + if( hostname(foreign, fhno) < 0 ) + return( punt(VALJFN, "Could not get name for client host") ); + lowerify(foreign); +# endif + + if( getstr(jfn, lcuser, 39) ) + return( punt(VALJFN, "bad locuser") ); +# ifdef BLEH + printf("lc: %s\n", lcuser); /**/ +# endif + + if( getstr(jfn, fnuser, 17) ) + return( punt(VALJFN, "bad frnuser") ); +# ifdef BLEH + printf("fn: %s\n", fnuser); /**/ +# endif + + if( getstr(jfn, command, 200) ) + return( punt(VALJFN, "bad command") ); +# ifdef BLEH + printf("cm: %s\n", command); /**/ +# endif + + ac1 = Value(RC_emo); + ac2 = POINT(lcuser); + if( JSYS(JSrcusr, acs) == JSerr ) + return( punt(VALJFN, "local user does not exist") ); + userno = ac3; + +# ifndef DEBUG + if( ruserok(foreign, 0, fnuser, lcuser ) != 0 ) + return( punt(VALJFN, "Permission denied.") ); + + ac1 = userno; /* get user # in ac1 */ + ac2 = POINT(""); /* null password */ + ac3 = POINT(""); /* null account */ + if( JSYS(JSlogin, acs) == JSerr ) + return( punt(VALJFN, "LOGIN failed") ); +# endif + + ac1 = CR_cap; /* pass caps */ + if( JSYS(JScfork, acs) == JSerr ) /* create a fork */ + return( punt(VALJFN, "Could not create EXEC fork") ); + execfork = ac1; + + ac1 = Value(GJ_sht) + Value(GJ_old); + ac2 = POINT("SYSTEM:RSHEXEC.EXE"); + if( JSYS(JSgtjfn, acs) == JSerr ) + return( punt(VALJFN, "Could not find SYSTEM:RSHEXEC.EXE") ); + + ac1 = makeword( execfork, getright(ac1) ); + if( JSYS(JSget, acs) == JSerr ) + return( punt(VALJFN, "Could not GET EXEC") ); + +/* doprarg(execfork); */ + + fnstd(cmd2, command); /* convert unix path to '20 form */ + if( rcstring(cmd2) < 0 ) /* place in rscan buffer */ + return( punt(VALJFN, "Could not set up command") ); + + write(VALJFN, "", 1); /* send null (validated ok) */ + + ac1 = execfork; + ac2 = 2; /* start at +2 */ + if( JSYS(JSsfrkv, acs) == JSerr ) + return( punt2(VALJFN, "Could not start EXEC") ); + + ac1 = execfork; + if( JSYS(JSwfork, acs) == JSerr ) + return( punt2(VALJFN, "Could not wait for EXEC") ); + + write(errjfn, "", 1); + + if( errjfn != jfn ) /* if we have an error jfn */ + close(errjfn, Value(CZ_abt)); /* close (and abort) it */ +} /* doit */ + +int tvtstat(tvt, word) +int tvt, word; +{ + int acs[5], result; + + ac1 = tvt + Value(TCP_tv); /* get TVT number in A */ + ac2 = makeword(-1,word); /* get specified word */ + ac3 = makeword(-1,(int) &result); /* result */ + if( JSYS(JSstat, acs) == JSerr ) /* read TCP connection 4n host */ + return( -1 ); + else + return( result ); +} /* tvtstat */ + + +setname(s) +char *s; +{ + int acs[5]; + + ac1 = ac2 = makesix(s); + JSYS(JSsetsn, acs); +} /* setname */ + +int makesix(s) +register char *s; +{ + register int i, j; + register char c; + + j = 0; + for( i = 0; i < 6; i++ ) { + if( (c = *s++) != NULL ) { + if( c < 040 ) c = '?'; + if( c > '_' ) c = c - 040; + j = (j << 6) + c - 040; + } /* if got a char */ + else break; + } /* for */ + + if( i < 6 ) j = j << (6 - i); +} /* makesix */ + +int getstr(jfn, buff, len) /* read counted string, or till NUL */ +char buff[]; +int jfn; +{ + int acs[5]; + + ac1 = jfn; + ac2 = POINT(buff); + ac3 = len; + ac4 = 0; + + if( JSYS(JSsin,acs) == JSerr ) + return( -1 ); + + if( ac3 == 0 || (len - ac3) == 0 ) /* overflow or read nothing? */ + return( -1 ); + else + return( 0 ); +} /* getstr */ + + +/**************** FROM RCMD.C ****************/ + +extern char *index(); + +ruserok(rhost, superuser, ruser, luser) + char *rhost; + int superuser; + char *ruser, *luser; +{ + FILE *hostf; + char ahost[32]; + int first = 1; + + hostf = superuser ? (FILE *)0 : fopen("/etc/hosts.equiv", "r"); +again: + if (hostf) { + while (fgets(ahost, sizeof (ahost), hostf)) { + char *user, *idx; + if ( (idx = index(ahost, '\n')) ) + *idx = 0; + user = index(ahost, ' '); + if (user) + *user++ = 0; + if (!strcmp(rhost, ahost) && + !strcmp(ruser, user ? user : luser)) { + (void) fclose(hostf); + return (0); + } + } + (void) fclose(hostf); + } + if (first == 1) { + char buf[100]; /* BUDD */ + sprintf(buf, "ps:<%s>\026.rhosts", luser); /* BUDD */ + first = 0; + hostf = fopen(buf, "r"); /* BUDD */ + goto again; + } + return (-1); +} /* ruserok */ + +int getcon(fhost, fsock) +int fhost, fsock; +{ + char buffer[50]; + int lsock, jfn; + + for( lsock = 1; lsock < 1024; lsock++ ) { + sprintf(buffer,"TCP:%d\026#.%o-%d;CONN:ACT", lsock, fhost, fsock ); + if( (jfn = trytcp(buffer)) > 0 ) + return( jfn ); + } /* for */ + return( -1 ); +} /* getcon */ + +int punt2(jfn, s) +int jfn; +char *s; +{ + write(jfn, s, strlen(s)); /* pass failure string */ + return( -1 ); +} /* punt2 */ + +doprarg(fork) +int fork; +{ + int scjprb[8], acs[5]; + register int *sp; + + sp = scjprb; + + *sp++ = 4; /* 0 - 4 words.. */ + *sp++ = makeword(0414100, 02545); /* 1 - very magic word */ + *sp++ = 4; /* 2 - word 4 is data */ + *sp++ = 0; /* 3 - nothing */ + *sp++ = (1 << 35); /* 4 - suppress exec herald */ + + ac1 = makeword(PRast, fork); + ac2 = (int) scjprb; + ac3 = 6; + JSYS(JSprarg, acs); + +} /* doprarg */ + +lowerify(s) +register char *s; +{ + register char c; + + while( (c = *s) != NULL ) { + if( isupper( c ) ) *s = tolower( c ); + s++; + } /* while */ +} /* lowerify */ + + +rcstring(s) /* place string in RSCAN buffer */ +register char *s; +{ + char bigbuf[500], lc; + register char c, *d; + int acs[5]; + + d = bigbuf; + while( (c = *s++) != NULL ) { + lc = c; + if( c == '\n' ) { /* convert NL to CRLF */ + *d++ = '\r'; + *d++ = '\n'; + } + else if( c != '\r' ) /* discard CR */ + *d++ = c; + } /* while */ + + if( lc != '\n' ) { /* if last was not NL, add CRLF */ + *d++ = '\r'; + *d++ = '\n'; + } /* last not NL */ + + *d++ = '\0'; /* tie off with NUL */ + + ac1 = POINT(bigbuf); /* insert string into RSCAN */ + if( JSYS(JSrscan, acs) == JSerr ) + return( -1 ); + + ac1 = RSini; /* make RSCAN available as input */ + if( JSYS(JSrscan, acs) == JSerr ) + return( -1 ); + return( 0 ); + +} /* rcstring */ + +/** Local Modes: * */ +/** Comment Column:40 * */ +/** End: * */ diff --git a/rshsrv.ctl b/rshsrv.ctl new file mode 100644 index 0000000..0078b5c --- /dev/null +++ b/rshsrv.ctl @@ -0,0 +1,3 @@ +@pcc rshsrv.c +@pcc rshlib.c +@clink rshsrv,rshlib