(DEFINE-FILE-INFO READTABLE "XCL" PACKAGE (DEFPACKAGE "RPC2" (USE "LISP"
"XCL")))
(IL:FILECREATED "22-Oct-87 18:32:06" IL:{SAFE}</B/JFINGER/RPC/X>RPCRPC.LSP\;146 41786
IL:|changes| IL:|to:| (IL:VARIABLES *RPC-WELL-KNOWN-SOCKETS*)
IL:|previous| IL:|date:| "15-Oct-87 18:52:30"
IL:{SAFE}</B/JFINGER/RPC/X>RPCRPC.LSP\;145)
(IL:PRETTYCOMPRINT IL:RPCRPCCOMS)
(IL:RPAQQ IL:RPCRPCCOMS
((IL:* IL:* "RPC Variables and Structures")
(IL:* "Copyright (c) 1987 by the Leland Stanford Junior University. All rights reserved."
)
(IL:* "Written by Jeff Finger at the SUMEX-AIM Computing Resource at Stanford University"
"under support from National Institutes of Health Grant NIH 5P41 RR00785."
)
(IL:P (IN-PACKAGE "RPC2"))
(IL:VARIABLES *DEBUG* *RPC-CALL* *RPC-VERSION* *RPC-PROGRAMS*
*MSEC-UNTIL-TIMEOUT* *MSEC-BETWEEN-TRIES*
*INTERNAL-TIME-UNITS-PER-MSEC* *RPC-REPLY-STATS*
*RPC-ACCEPT-STATS* *RPC-REJECT-STATS*
*RPC-AUTHENTICATION-STATS* *RPC-OK-TO-CACHE*
*RPC-SOCKET-CACHE* *XID-COUNT* *RPC-DEF-IN-PROGRESS*
*RPC-WELL-KNOWN-SOCKETS* *RPC-PROTOCOLS* *RPCSTREAM*
*RPC-PGNAME* *RPC-PCNAME*)
(IL:* IL:* "Define RPC Program")
(IL:FUNCTIONS DEFINE-REMOTE-PROGRAM DEFINE-REMOTE-PROG
CONS-UP-RPC-PROCS CLEAR-ANY-NAME-CONFLICTS DEF-RPC-TYPES
DEF-RPC-INHERITS DEF-RPC-PROCEDURES DEF-RPC-PROCEDURE
DEF-RPC-CONSTANTS UNDEFINE-REMOTE-PROGRAM XDR-GENCODE-MAKEFCN
XDR-GENCODE-INLINE)
(IL:* IL:* "Remote Procedure Call")
(IL:FUNCTIONS REMOTE-PROCEDURE-CALL SETUP-RPC PERFORM-RPC
RPC-RESOLVE-HOST RPC-RESOLVE-PROG RPC-RESOLVE-PROC
RPC-FIND-SOCKET ENCODE-RPC-ARGS ACTUALLY-DO-THE-RPC
EXCHANGE-UDP-PACKETS EXCHANGE-TCP-PACKETS PARSE-RPC-REPLY
CREATE-XID)
(IL:* IL:* "RPC Utility Functions")
(IL:FUNCTIONS GET-REPLY-STAT GET-ACCEPT-STAT GET-REJECT-STAT
GET-AUTHENTICATION-STAT GET-PROTOCOL-NUMBER
FIND-CACHED-SOCKET)
(IL:* IL:* "RPC Error Messages")
(IL:FUNCTIONS RPC-ERROR-PRM-MISMATCH RPC-ERROR-PRM-UNAVAILABLE
RPC-ERROR-PRC-UNAVAILABLE RPC-ERROR-GARBAGE-ARGS
RPC-ERROR-MISMATCH RPC-ERROR-AUTHENTICATION)
(IL:* IL:* "Authentication")
(IL:STRUCTURES AUTHENTICATION)
(IL:VARIABLES *AUTHENTICATION-TYPEDEF* *NULL-AUTHENTICATION*)
(IL:FUNCTIONS CREATE-UNIX-AUTHENTICATION ENCODE-AUTHENTICATION
DECODE-AUTHENTICATION)))
(IL:* IL:* "RPC Variables and Structures")
(IL:*
"Copyright (c) 1987 by the Leland Stanford Junior University. All rights reserved."
)
(IL:*
"Written by Jeff Finger at the SUMEX-AIM Computing Resource at Stanford University"
"under support from National Institutes of Health Grant NIH 5P41 RR00785.")
(IN-PACKAGE "RPC2")
(DEFGLOBALPARAMETER *DEBUG* NIL "T for printout, NUMBER for even more.")
(DEFCONSTANT *RPC-CALL* 0 "Constant 0 in packet means RPC call, 1 means reply")
(DEFCONSTANT *RPC-VERSION* 2 "This code will only work for SUN RPC Version 2")
(DEFGLOBALVAR *RPC-PROGRAMS* NIL "\
A list of RPC-PROGRAM structs.\
\
This list is consulted by various routines to find infomation about known\
remote programs.\
\
It is assumed that a given NAME field uniquely identifies a (NUMBER, VERSION, PROTOCOL).\
On the other hand, there may be several NAMEs (and hence, several RPC-STRUCTs) for\
a given (NUMBER, VERSION, PROTOCOL).\
\
"
)
(DEFPARAMETER *MSEC-UNTIL-TIMEOUT* 10000
"Total time in msec before giving up on UDP exchange with remote host")
(DEFPARAMETER *MSEC-BETWEEN-TRIES* 1000 "Time in msec between UDP retries")
(DEFCONSTANT *INTERNAL-TIME-UNITS-PER-MSEC* (/ INTERNAL-TIME-UNITS-PER-SECOND
1000)
"This gets used in EXCHANGE-UDP-PACKETS."
)
(DEFCONSTANT *RPC-REPLY-STATS* '((0 . ACCEPTED)
(1 . REJECTED))
"\
Assoc list for internal use by PARSE-RPC-REPLY.\
"
)
(DEFCONSTANT *RPC-ACCEPT-STATS* '((0 . SUCCESS)
(1 . PROGRAM-UNAVAILABLE)
(2 . PROGRAM-MISMATCH)
(3 . PROCEDURE-UNAVAILABLE)
(4 . GARBAGE-ARGUMENTS))
"\
Assoc list for internal use by PARSE-RPC-REPLY.\
"
)
(DEFCONSTANT *RPC-REJECT-STATS* '((0 . RPC-MISMATCH)
(1 . AUTHENTICATION-ERROR))
"\
Assoc list for internal use by PARSE-RPC-REPLY.\
"
)
(DEFCONSTANT *RPC-AUTHENTICATION-STATS* '((1 . BAD-CREDENTIAL)
(2 . REJECTED-CREDENTIAL)
(3 . BAD-VERIFIER)
(4 . REJECTED-VERIFIER)
(5 TOO-WEAK)) "NIL" )
(DEFPARAMETER *RPC-OK-TO-CACHE* T "\
If NIL, does not attempt to cache socket numbers for non-well-known sockets\
"
)
(DEFVAR *RPC-SOCKET-CACHE* NIL "\
A list of (<iphost-address> <remote-program-name> <remote-program-version>\
<protocol> <ipsocket-number>) quintuples."
)
(DEFVAR *XID-COUNT* 0
"Contains the XID stamp of the next remote procedure call")
(DEFVAR *RPC-DEF-IN-PROGRESS* NIL "Used for debugging only")
(DEFGLOBALVAR *RPC-WELL-KNOWN-SOCKETS* `((* 100000 2 UDP 111)
(* 100000 2 TCP 111)
(* 100003 2 UDP 2049)) "\
List of well-known RPC programs and their sockets.\
Each element is a list:\
(host-address prog-number prog-version protocol socket-number)\
\
Host-address may be *, in which case it matches any host address.\
Protocol should be either rpc2::UDP or rpc2::TCP.\
"
)
(DEFVAR *RPC-PROTOCOLS* '((TCP . 6)
(UDP . 17)) )
(DEFVAR *RPCSTREAM* NIL "This global is not used exceptin debugging.\
It holds a copy of the RPC-STREAM even after the RPC-CALL returns."
)
(DEFGLOBALVAR *RPC-PGNAME* NIL
"Name of RPC Program. Used only for *debug* printout.")
(DEFGLOBALVAR *RPC-PCNAME* NIL
"Name of RPC Procedure. Used only for *debug* printout.")
(IL:* IL:* "Define RPC Program")
(DEFMACRO DEFINE-REMOTE-PROGRAM (NAME NUMBER VERSION PROTOCOL &KEY CONSTANTS
TYPES INHERITS PROCEDURES) "
This macro expands into code to add a new RPC-PROGRAM struct to
*RPC-PROGRAMS*. The generated code checks first to see that there
are no name conflicts with existing remote programs and then adds the new
structure to *RPC-PROGRAMS*.
" (LET ((ENAME (EVAL NAME))
(ENUMBER (EVAL NUMBER))
(EVERSION (EVAL VERSION))
(EPROTOCOL (OR (EVAL PROTOCOL)
'UDP))
(ECONSTANTS (EVAL CONSTANTS))
(ETYPES (EVAL TYPES))
(EINHERITS (EVAL INHERITS))
(EPROCEDURES (EVAL PROCEDURES)))
(CHECK-TYPE ENAME SYMBOL)
(CHECK-TYPE ENUMBER NUMBER)
(CHECK-TYPE EVERSION NUMBER)
(COND
((MEMBER EPROTOCOL '(UDP TCP))
T)
((EQUAL "UDP" (STRING EPROTOCOL))
(SETQ EPROTOCOL 'UDP))
((EQUAL "TCP" (STRING EPROTOCOL))
(SETQ EPROTOCOL 'TCP))
((ERROR "~a is unknown prototype." EPROTOCOL)))
(LET ((RPROG (DEFINE-REMOTE-PROG ENAME ENUMBER EVERSION EPROTOCOL
ECONSTANTS ETYPES EINHERITS EPROCEDURES)))
`(LET ((DUMMY (FORMAT-T "Defining remote program ~a, version ~a~%"
',ENAME
',EVERSION))
(NEWPROG (MAKE-RPC-PROGRAM :NUMBER ,ENUMBER :VERSION
,EVERSION :NAME ',ENAME :PROTOCOL
',EPROTOCOL :TYPES
',(RPC-PROGRAM-TYPES RPROG) :CONSTANTS
',(RPC-PROGRAM-CONSTANTS RPROG) :INHERITS
',(RPC-PROGRAM-INHERITS RPROG) :PROCEDURES
,(CONS-UP-RPC-PROCS (RPC-PROGRAM-PROCEDURES
RPROG)))))
(IF (CLEAR-ANY-NAME-CONFLICTS ',ENAME ',ENUMBER
',EVERSION
',EPROTOCOL)
(PROGN (UNDEFINE-REMOTE-PROGRAM ',ENAME
',ENUMBER
',EVERSION)
(PUSH NEWPROG *RPC-PROGRAMS*)
',ENAME)
(PROGN (FORMAT-T "Old RPC program not overwritten.~%")
NIL))))))
(DEFUN DEFINE-REMOTE-PROG (NAME NUMBER VERSION PROTOCOL CONSTANTS TYPES
INHERITS PROCEDURES)
(IL:* IL:|;;| "This guy does the work, so that DEFINE-REMOTE-PROGRAM can cons up the macro easily.")
(IL:* IL:|;;| "An RPC-PROGRAM struct RPROG is passed back to DEFINE-REMOTE-PROGRAM. Its innards are then used by DEFINE-REMOTE-PROGRAM to build up the big cons that will cons up the proper RPC-PROGRAM later.")
(LET (RPROG)
(FORMAT-T "Building XDR routines for remote program ~a, version ~a~%"
NAME VERSION)
(SETQ RPROG (MAKE-RPC-PROGRAM :NUMBER NUMBER :VERSION VERSION :NAME NAME
:PROTOCOL PROTOCOL)
*RPC-DEF-IN-PROGRESS* RPROG)
(SETF (RPC-PROGRAM-TYPES RPROG)
(DEF-RPC-TYPES RPROG TYPES))
(SETF (RPC-PROGRAM-INHERITS RPROG)
(DEF-RPC-INHERITS RPROG INHERITS))
(SETF (RPC-PROGRAM-CONSTANTS RPROG)
(DEF-RPC-CONSTANTS RPROG CONSTANTS))
(SETF (RPC-PROGRAM-PROCEDURES RPROG)
(DEF-RPC-PROCEDURES RPROG PROCEDURES))
RPROG))
(DEFUN CONS-UP-RPC-PROCS (PROCS) "\
Given a list of RPC-PROCEDURE structs, conses up code to produce that set of\
RPC-PROCEDURE structs.\
"
`(LIST
,@(MAP
'LIST
#'(LAMBDA
(PROC)
`(MAKE-RPC-PROCEDURE
:NAME
',(RPC-PROCEDURE-NAME PROC) :PROCNUM
',(RPC-PROCEDURE-PROCNUM PROC) :ARGTYPES
,(IF (RPC-PROCEDURE-ARGTYPES PROC)
`(LIST ,@(MAP 'LIST #'(LAMBDA (FCN)
(LIST 'FUNCTION FCN))
(RPC-PROCEDURE-ARGTYPES PROC)))) :RESULTTYPES
,(IF (RPC-PROCEDURE-RESULTTYPES PROC)
`(LIST ,@(MAP 'LIST #'(LAMBDA (FCN)
(LIST 'FUNCTION FCN))
(RPC-PROCEDURE-RESULTTYPES PROC)))))) PROCS)))
(DEFUN CLEAR-ANY-NAME-CONFLICTS (NAME NUMBER VERSION PROTOCOL) "\
Determines whether a proposed (NAME, NUMBER, VERSION, PROTOCOL) would violate\
the assumption that a NAME uniquely specifies the other three components.\
\
If there exists a violation, the user is given a chance to remove the old program.\
\
Returns T if no violation of assumption (or violation is resolved by removing old program),\
Returns NIL if there is an unresolved violation.\
\
"
(LET (OLDRPC)
(COND
((AND (SETQ OLDRPC (FIND-RPC-PROGRAM :NAME NAME))
(OR (/= NUMBER (RPC-PROGRAM-NUMBER OLDRPC))
(/= VERSION (RPC-PROGRAM-VERSION OLDRPC))
(NOT (EQL PROTOCOL (RPC-PROGRAM-PROTOCOL OLDRPC)))))
(FORMAT *QUERY-IO* "Remote program name conflict with existing program:~% Name ~a, Protocol ~A, Number ~a, Version ~a~%"
NAME (RPC-PROGRAM-PROTOCOL OLDRPC)
(RPC-PROGRAM-NUMBER OLDRPC)
(RPC-PROGRAM-VERSION OLDRPC))
(AND (YES-OR-NO-P "Do you want to remove the old program? ")
(UNDEFINE-REMOTE-PROGRAM (RPC-PROGRAM-NAME OLDRPC)
(RPC-PROGRAM-NUMBER OLDRPC)
(RPC-PROGRAM-VERSION OLDRPC)
(RPC-PROGRAM-PROTOCOL OLDRPC))))
(T T))))
(DEFUN DEF-RPC-TYPES (CONTEXT TYPEDEFS) "\
Essentially a no-op, as typedefs are copied directly from the DEFINE-REMOTE-PROGRAM\
into the RPC-PROGRAM struct. Just prints out the name of each type as it is encountered.\
"
(IF TYPEDEFS (FORMAT-T " Types~%"))
(DOLIST (I TYPEDEFS)
(FORMAT-T " ~A~%" (FIRST I)))
TYPEDEFS)
(DEFUN DEF-RPC-INHERITS (CONTEXT PROGLIST) "\
Checks remote program inherited by this one to make sure that it exists.\
Issues a warning if it cannot find the program to be inherited.\
"
(IL:* IL:\; "")
(IF PROGLIST (FORMAT-T " Inherits~%"))
(DOLIST (PRG PROGLIST PROGLIST)
(FORMAT-T " ~A~%" PRG)
(IF (NOT (AND (SYMBOLP PRG)
(FIND-RPC-PROGRAM :NAME PRG)))
(WARN
"Trying to inherit from remote program ~a, but ~a not found.~%"
PRG PRG))))
(DEFUN DEF-RPC-PROCEDURES (CONTEXT PROCS)
"Returns a list of RPC-PROCEDURE structs returned by DEF-RPC-PROCEDURE."
(CHECK-TYPE PROCS LIST "A list of RPC procedure declarations")
(IF PROCS (FORMAT-T " Procedures~%"))
(MAP 'LIST #'(LAMBDA (PROC)
(DEF-RPC-PROCEDURE CONTEXT PROC)) PROCS))
(DEFUN DEF-RPC-PROCEDURE (CONTEXT PROC) "\
For a procedure specified to DEFINE-REMOTE-PROGRAM's :PROCEDURES argument,\
creates and returns an RPC-PROCEDURE struct. \
\
XDR procedure code is generated via the call to XDR-GENCODE-MAKEFCN.\
"
(CHECK-TYPE (FIRST PROC)
(AND SYMBOL (NOT NULL))
"a non-null symbol naming the RPC procedure.")
(CHECK-TYPE (SECOND PROC)
(INTEGER 0 *)
"a non-negative integer RPC procedure number")
(CHECK-TYPE (THIRD PROC)
LIST)
(CHECK-TYPE (FOURTH PROC)
LIST)
(LET ((RP (MAKE-RPC-PROCEDURE)))
(SETF (RPC-PROCEDURE-NAME RP)
(FIRST PROC))
(SETF (RPC-PROCEDURE-PROCNUM RP)
(SECOND PROC))
(SETF (RPC-PROCEDURE-ARGTYPES RP)
(MAP 'LIST #'(LAMBDA (TD)
(XDR-GENCODE-MAKEFCN CONTEXT TD 'WRITE))
(THIRD PROC)))
(SETF (RPC-PROCEDURE-RESULTTYPES RP)
(MAP 'LIST #'(LAMBDA (TD)
(XDR-GENCODE-MAKEFCN CONTEXT TD 'READ))
(FOURTH PROC)))
(FORMAT-T " ~A~%" (RPC-PROCEDURE-NAME RP))
RP))
(DEFUN DEF-RPC-CONSTANTS (CONTEXT PAIRS) "\
Checks that constants specified to DEFINE-REMOTE-PROGRAM are syntactically\
reasonable.\
"
(IF PAIRS (FORMAT-T " Constants~%"))
(DOLIST (PAIR PAIRS)
(CHECK-TYPE (FIRST PAIR)
(AND (NOT NULL)
SYMBOL))
(CHECK-TYPE (SECOND PAIR)
(AND (NOT NULL)
NUMBER))
(FORMAT-T " ~A~%" (FIRST PAIR)))
PAIRS)
(DEFUN UNDEFINE-REMOTE-PROGRAM (NAME NUMBER VERSION &OPTIONAL
(PROTOCOL 'UDP)) "\
If finds NAME-NUMBER-VERSION-PROTOCOL match in *RPC-PROGRAMS*, deletes.\
If finds NUMBER-VERSION match with NAME mismatch, asks first.\
If deletes something, returns NAME of DELETED program, otherwise NIL."
(IL:* IL:\; "")
(LET ((RPC (FIND-RPC-PROGRAM :NUMBER NUMBER :VERSION VERSION :NAME NAME
:PROTOCOL PROTOCOL)))
(IF RPC (IF (OR (EQL NAME (RPC-PROGRAM-NAME RPC))
(YES-OR-NO-P
"Do you really want to remove/overwrite RPC program ~a?"
(RPC-PROGRAM-NAME RPC)))
(PROGN (SETQ *RPC-PROGRAMS* (DELETE RPC *RPC-PROGRAMS*))
(RPC-PROGRAM-NAME RPC))))))
(DEFUN XDR-GENCODE-MAKEFCN (CONTEXT TYPEDEF OPER &OPTIONAL COMPILESW) "\
Calls XDR-CODEGEN to generate an XDR function for TYPEDEF.\
If COMPILESW, then compiles the function. COMPILESW is not\
used anymore since DEFINE-REMOTE-PROGRAM became a macro.\
"
(LET ((CODE (XDR-CODEGEN CONTEXT TYPEDEF OPER)))
(IF COMPILESW (COMPILE NIL CODE)
CODE)))
(DEFMACRO XDR-GENCODE-INLINE (CONTEXT TYPEDEF OPER &REST VARS) "NIL"
(IL:* IL:|;;| " Note that using a NIL context is valid here. It just means that no typedefs from other Remote Program Definitions are available.")
"NIL"
`(FUNCALL #',(XDR-CODEGEN CONTEXT (EVAL TYPEDEF)
(EVAL OPER)) ,.VARS))
(IL:* IL:* "Remote Procedure Call")
(DEFUN REMOTE-PROCEDURE-CALL (DESTINATION PROGRAM PROCID ARGLIST &KEY
(PROTOCOL 'UDP)
REMOTESOCKET VERSION CREDENTIALS
DYNAMIC-PROGNUM (DYNAMIC-VERSION 1)
(ERRORFLG T)
LEAVE-STREAM-OPEN
(MSEC-UNTIL-TIMEOUT *MSEC-UNTIL-TIMEOUT*)
(MSEC-BETWEEN-TRIES *MSEC-BETWEEN-TRIES*)
RESULTS) "\
This is the high-level way of making a remote procedure call (PERFORM-RPC is the low-level\
way).\
\
REMOTE-PROCEDURE-CALL resolves all the arguments, creates a new RPC-STREAM, makes the call, optionally closes the RPC-STREAM, and returns the results of the call.\
\
The resolution of arguments is designed such that all arguments may be either\
unresolved (e.g., a remote host name), or already resolved (e.g., an IP address).\
"
(WHEN (NUMBERP *DEBUG*)
(FORMAT-T "Remote-Procedure-Call...~%")
(FORMAT-T " Destination=~A~%" DESTINATION)
(FORMAT-T " Program=~A~%" PROGRAM)
(FORMAT-T " ProcID=~A~%" PROCID)
(FORMAT-T " ArgList=~A~%" ARGLIST))
(MULTIPLE-VALUE-BIND (DESTADDR DESTSOCKET RPROG RPROC RPCSTREAM)
(SETUP-RPC DESTINATION PROGRAM PROCID REMOTESOCKET VERSION
DYNAMIC-PROGNUM DYNAMIC-VERSION PROTOCOL)
(SETQ RPCSTREAM (OPEN-RPCSTREAM (RPC-PROGRAM-PROTOCOL RPROG)
DESTADDR DESTSOCKET))
(SETQ RESULTS
(PERFORM-RPC DESTADDR DESTSOCKET RPROG RPROC RPCSTREAM ARGLIST
CREDENTIALS :ERRORFLG ERRORFLG :MSEC-UNTIL-TIMEOUT
MSEC-UNTIL-TIMEOUT :MSEC-BETWEEN-TRIES
MSEC-BETWEEN-TRIES))
(UNLESS LEAVE-STREAM-OPEN (CLOSE-RPCSTREAM RPCSTREAM))
RESULTS))
(DEFUN SETUP-RPC (DESTINATION PROGRAM PROCID &OPTIONAL DESTSOCKET VERSION
DYNAMIC-PROGNUM DYNAMIC-VERSION (PROTOCOL 'UDP)) "\
Resolves arguments to REMOTE-PROCEDURE-CALL. Takes arguments in more or less\
any reasonable form and returns multiple values (destination-address, socket-number,\
RPC-PROGRAM struct, RPC-PROCEDURE struct).\
\
See individual RPC-RESOLVE-* programs for details on what inputs are acceptable.\
"
(LET* ((DESTADDR (RPC-RESOLVE-HOST DESTINATION))
(RPROG (RPC-RESOLVE-PROG PROGRAM VERSION PROTOCOL))
(DUMMY (IL:* IL:\;
" This code may set RPROG")
(WHEN DYNAMIC-PROGNUM (SETF RPROG (COPY-RPC-PROGRAM RPROG))
(SETF (RPC-PROGRAM-NUMBER RPROG)
DYNAMIC-PROGNUM)
(SETF (RPC-PROGRAM-VERSION RPROG)
DYNAMIC-VERSION)))
(RPROC (RPC-RESOLVE-PROC RPROG PROCID))
(SOCKET (OR DESTSOCKET (RPC-FIND-SOCKET DESTADDR RPROG
(RPC-PROGRAM-PROTOCOL RPROG)))))
(VALUES DESTADDR SOCKET RPROG RPROC)))
(DEFUN PERFORM-RPC (DESTADDR DESTSOCKET RPROG RPROC STREAM ARGLIST CREDENTIALS
&KEY (ERRORFLG T)
(MSEC-UNTIL-TIMEOUT *MSEC-UNTIL-TIMEOUT*)
(MSEC-BETWEEN-TRIES *MSEC-BETWEEN-TRIES*))
"\
The low-level remote procedure call function.\
"
(LET (RETVALS)
(REINITIALIZE-RPCSTREAM STREAM DESTADDR DESTSOCKET)
(PROGN
(IL:* IL:|;;| " These are for debugging printouts only")
(SETQ *RPCSTREAM* STREAM)
(SETQ *RPC-PGNAME* (RPC-PROGRAM-NAME RPROG))
(SETQ *RPC-PCNAME* (RPC-PROCEDURE-NAME RPROC)))
(XDR-UNSIGNED STREAM (CREATE-XID))
(XDR-UNSIGNED STREAM *RPC-CALL*)
(XDR-UNSIGNED STREAM *RPC-VERSION*)
(XDR-UNSIGNED STREAM (RPC-PROGRAM-NUMBER RPROG))
(XDR-UNSIGNED STREAM (RPC-PROGRAM-VERSION RPROG))
(XDR-UNSIGNED STREAM (RPC-PROCEDURE-PROCNUM RPROC))
(ENCODE-AUTHENTICATION STREAM CREDENTIALS)
(ENCODE-AUTHENTICATION STREAM *NULL-AUTHENTICATION*)
(ENCODE-RPC-ARGS STREAM ARGLIST RPROG RPROC)
(SETQ RETVALS (CATCH 'GOFORIT (ACTUALLY-DO-THE-RPC STREAM
MSEC-UNTIL-TIMEOUT
MSEC-BETWEEN-TRIES ERRORFLG)
(PARSE-RPC-REPLY STREAM (RPC-PROCEDURE-RESULTTYPES
RPROC)
ERRORFLG)))
(WHEN (AND (NUMBERP *DEBUG*)
(> *DEBUG* 0))
(FORMAT-T " Values Returned by RPC: ~A~%" RETVALS))
RETVALS))
(DEFUN RPC-RESOLVE-HOST (DESTINATION) "\
Takes an IPADDRESS, symbol, or string and tries to find an IPADDRESS for a remote\
host. Signals an error if it cannot resolve the host.\
"
(COND
((NUMBERP DESTINATION)
DESTINATION)
((AND (SYMBOLP DESTINATION)
(IL:IPHOSTADDRESS DESTINATION)))
((AND (STRINGP DESTINATION)
(IL:IPHOSTADDRESS (INTERN DESTINATION))))
(T (ERROR "Could not find an IP net address for DESTINATION ~a"
DESTINATION))))
(DEFUN RPC-RESOLVE-PROG (PROGRAM &OPTIONAL VERSION PROTOCOL) "\
Takes an RPC-PROGRAM, a number, a symbol, or a string along with an optional VERSION and PROTOCOL and tries to find the matching RPC-PROGRAM.\
Signals an error if it cannot find the intended program.\
"
(COND
((TYPEP PROGRAM 'RPC-PROGRAM)
PROGRAM)
((AND (TYPEP PROGRAM 'SYMBOL)
(FIND-RPC-PROGRAM :NAME PROGRAM :VERSION VERSION :PROTOCOL PROTOCOL
)))
((AND (NUMBERP PROGRAM)
(FIND-RPC-PROGRAM :NUMBER PROGRAM :VERSION VERSION :PROTOCOL
PROTOCOL)))
((AND (STRINGP PROGRAM)
(FIND-RPC-PROGRAM :NAME (INTERN PROGRAM)
:VERSION VERSION :PROTOCOL PROTOCOL)))
(T (ERROR "Could not find definition for program ~a~a~a.~%" PROGRAM
(IF VERSION (FORMAT NIL ", version ~a" VERSION)
"")
(IF PROTOCOL (FORMAT NIL ", protocol ~a" PROTOCOL)
"")))))
(DEFUN RPC-RESOLVE-PROC (RPROG PROCID) "\
Given an RPC-PROGRAM struct RPROG, tries to find and return an RPC-PROCEDURE in\
RPROG specified by a number, string, symbol, or RPC-PROCEDURE.\
\
Signals an error if it cannot find the intended rpc-procedure\
"
(COND
((TYPEP PROCID 'RPC-PROCEDURE)
PROCID)
((AND (OR (NUMBERP PROCID)
(SYMBOLP PROCID))
(FIND-RPC-PROCEDURE (RPC-PROGRAM-PROCEDURES RPROG)
PROCID)))
((AND (STRINGP PROCID)
(FIND-RPC-PROCEDURE (RPC-PROGRAM-PROCEDURES RPROG)
(INTERN PROCID))))
(T (ERROR "Could not find definition for program ~a, procedure ~a~%"
(RPC-PROGRAM-NAME RPROG)
PROCID))))
(DEFUN RPC-FIND-SOCKET (DESTADDR PRG PROTOCOL) "\
Tries to find and return a remote socket number.\
\
(1) Looks in *RPC-WELL-KNOWN-SOCKETS*,\
(2) Looks in *RPC-SOCKET-CACHE*, but only if *RPC-OK-TO-CACHE*,\
(3) Requests socket number via remote procedure call to Portmapper\
on remote machine. If found and *RPC-OK-TO-CACHE*, caches the new\
socket number on *RPC-SOCKET-CACHE*.\
(4) If all the above have failed, signals an error.\
"
(LET ((PROGNUM (RPC-PROGRAM-NUMBER PRG))
(PROGVERS (RPC-PROGRAM-VERSION PRG))
SKT)
(COND
((SETQ SKT (FIND-CACHED-SOCKET '* PROGNUM PROGVERS PROTOCOL
*RPC-WELL-KNOWN-SOCKETS*))
(IF *DEBUG* (FORMAT-T
"Cached well-known socket ~a found for program ~a~%"
SKT (RPC-PROGRAM-NAME PRG)))
SKT)
((AND *RPC-OK-TO-CACHE*
(SETQ SKT (FIND-CACHED-SOCKET DESTADDR PROGNUM PROGVERS
PROTOCOL *RPC-SOCKET-CACHE*)))
(IF *DEBUG* (FORMAT-T
"Cached non-well-known socket ~a found for program ~a~%"
SKT (RPC-PROGRAM-NAME PRG)))
SKT)
((PROGN (IF *DEBUG* (FORMAT-T
"Looking up socket for program ~a on ~a.~%"
(RPC-PROGRAM-NAME PRG)
DESTADDR))
(SETQ SKT (FIRST (REMOTE-PROCEDURE-CALL
DESTADDR
'PORTMAPPER
'LOOKUP
`(,(RPC-PROGRAM-NUMBER PRG)
,(RPC-PROGRAM-VERSION PRG)
,(GET-PROTOCOL-NUMBER PROTOCOL) 0)
:REMOTESOCKET 111)))
(IF *DEBUG* (FORMAT-T
"Socket ~a found via portampper on ~a for program ~a~%"
SKT DESTADDR (RPC-PROGRAM-NAME PRG)))
(IF (AND *RPC-OK-TO-CACHE* (> SKT 0))
(PUSH `(,DESTADDR ,PROGNUM ,PROGVERS ,PROTOCOL
,SKT) *RPC-SOCKET-CACHE*)
SKT)
(IF (> SKT 0)
SKT)))
((ERROR "Could not find remote socket number for~%~
Host ~a, Remote Program ~a, Number ~a, Version ~a, Protocol ~a" DESTADDR
(RPC-PROGRAM-NAME PRG)
PROGNUM PROGVERS PROTOCOL)))))
(DEFUN ENCODE-RPC-ARGS (STREAM ARGLIST RPC-PROG RPC-PROC) "\
Takes a list of arguments and the corresponding list of XDR procedures and\
converts the arguments into XDR, writing them into the RPC-STREAM.\
"
(WHEN (AND (NUMBERP *DEBUG*)
(> *DEBUG* 0))
(FORMAT-T " RPC Arguments: ~A~%" ARGLIST))
(DO ((XDR-FNS (RPC-PROCEDURE-ARGTYPES RPC-PROC)
(REST XDR-FNS))
(ARGS ARGLIST (REST ARGS)))
((OR (NULL ARGS)
(NULL XDR-FNS))
(IF (OR XDR-FNS ARGS)
(ERROR "Mismatch of arguments and parameters to RPC call.~
Number or arguments:~a, Number of parameters:~a" (LENGTH ARGLIST)
(LENGTH (RPC-PROCEDURE-ARGTYPES RPC-PROC)))
(RPC-PROCEDURE-NAME RPC-PROC)))
(FUNCALL (FIRST XDR-FNS)
STREAM
(FIRST ARGS))))
(DEFUN ACTUALLY-DO-THE-RPC (STREAM MSEC-UNTIL-TIMEOUT MSEC-BETWEEN-TRIES
ERRORFLG) "\
Calls the appropriate function (for the protocol) to actually send the packets over\
the net and await an answer.\
"
(ECASE (RPC-STREAM-PROTOCOL STREAM)
(UDP (EXCHANGE-UDP-PACKETS STREAM MSEC-UNTIL-TIMEOUT
MSEC-BETWEEN-TRIES ERRORFLG))
(TCP (EXCHANGE-TCP-PACKETS STREAM MSEC-UNTIL-TIMEOUT ERRORFLG))))
(DEFUN EXCHANGE-UDP-PACKETS (STREAM MSEC-UNTIL-TIMEOUT MSEC-BETWEEN-TRIES
ERRORFLG) "\
Given the specified timeout and time between tries, this routine continues\
to send out UDP packets until it either gets a reply or times out.\
"
(IF (AND (NUMBERP *DEBUG*)
(> *DEBUG* 5))
(BREAK "Packet ready to go from PACKET of *RPCSTREAM*"))
(DO* ((INIT-TIME (GET-INTERNAL-REAL-TIME))
(FINAL-TIME (+ INIT-TIME (* MSEC-UNTIL-TIMEOUT
*INTERNAL-TIME-UNITS-PER-MSEC*))))
((>= (GET-INTERNAL-REAL-TIME)
FINAL-TIME)
(CASE ERRORFLG (:NOERRORS (THROW 'GOFORIT NIL))
(:RETURNERRORS (THROW 'GOFORIT '(ERROR TIMEOUT)))
(OTHERWISE (ERROR "Timeout of RPC Call"))))
(WHEN *DEBUG* (FORMAT-T
"Trying RPC Call: Program ~a, Procedure ~a...~%"
*RPC-PGNAME* *RPC-PCNAME*))
(IF (SETF (RPC-STREAM-INSTREAM STREAM)
(IL:UDP.EXCHANGE (RPC-STREAM-IPSOCKET STREAM)
(RPC-STREAM-OUTSTREAM STREAM)
MSEC-BETWEEN-TRIES))
(PROGN (WHEN *DEBUG* (FORMAT-T "It returned!~%")
(AND (NUMBERP *DEBUG*)
(> *DEBUG* 5)
(BREAK
"Reply Packet in INSTREAM of RPC-STREAM *RPCSTREAM*"
)))
(RETURN T)))))
(DEFUN EXCHANGE-TCP-PACKETS (RPCSTREAM TIMEOUT &OPTIONAL ERRORFLG) "
Given the specified timeout, this routine writes onto the TCP stream and
waits until it either gets a reply or times out.
"
(IL:* IL:|;;| "Yes, I know EXCHANGE-TCP-PACKETS is a misnomer, but I wanted it to parallel Exchange-UDP-Packets")
(LET* ((OUTSTRING (RPC-STREAM-OUTSTRING RPCSTREAM))
(OUTSTREAM (RPC-STREAM-OUTSTREAM RPCSTREAM))
(INSTREAM (RPC-STREAM-INSTREAM RPCSTREAM))
(EVENT (IL:TCP.SOCKET.EVENT (IL:TCP.STREAM.SOCKET (
RPC-STREAM-OUTSTREAM
RPCSTREAM)))))
(WHEN (NUMBERP *DEBUG*)
(INSPECT-STRING1 OUTSTRING (RPC-STREAM-OUTBYTEPTR RPCSTREAM))
(AND (> *DEBUG* 4)
(BREAK "Ready to write to tcp stream")))
(RM-FORCEOUTPUT RPCSTREAM T)
(IL:FORCEOUTPUT OUTSTREAM T)
(IF *DEBUG* (FORMAT-T
"Output forced out. Will wait ~a msec for reply~%"
TIMEOUT))
(IL:AWAIT.EVENT (IL:TCP.SOCKET.EVENT (IL:TCP.STREAM.SOCKET
(RPC-STREAM-OUTSTREAM RPCSTREAM))
)
TIMEOUT NIL)
(IF (IL:READP INSTREAM)
(PROGN (IF *DEBUG* (FORMAT-T "It returned!!!!~%"))
(RM-NEW-INPUT-RECORD RPCSTREAM)
T)
(CASE ERRORFLG (:NOERRORS (THROW 'GOFORIT NIL))
(:RETURNERRORS (THROW 'GOFORIT '(ERROR TIMEOUT)))
(OTHERWISE (ERROR "Timeout of TCP Call after ~a msec.~%"
TIMEOUT))))))
(DEFUN PARSE-RPC-REPLY (RPCSTREAM RETTYPES &OPTIONAL ERRORFLG) "\
Parses a reply message. If all goes well, returns a list of the values returned (or T if RETTYPES is NIL).\
\
If RPC was REJECTED, or ACCEPTED but with an ACCEPT-STAT other than SUCCESS,\
then (Following Courier) the response depends on the value of ERRORFLG:\
If ERRORFLG = 'NOERROR, then returns NIL\
If ERRORFLG = 'RETURNERRORS, then returns a list of the form\
(ERROR reply-stat accept-or-reject-stat otherinfo)\
If ERRORFLG = anything else, signals Lisp error.\
\
"
(IL:* IL:\; " ")
(LET (XID MSGTYPE REPLY-STAT VERF ACCEPT-STAT REJECT-STAT)
(SETQ XID (XDR-UNSIGNED RPCSTREAM))
(SETQ MSGTYPE (XDR-UNSIGNED RPCSTREAM))
(IF (NOT (EQL MSGTYPE 1))
(ERROR "RPC message is not a reply. MSGTYPE is ~A" MSGTYPE))
(CASE (GET-REPLY-STAT (SETQ REPLY-STAT (XDR-UNSIGNED RPCSTREAM)))
(ACCEPTED (SETQ VERF (DECODE-AUTHENTICATION RPCSTREAM))
(CASE (GET-ACCEPT-STAT (SETQ ACCEPT-STAT
(XDR-UNSIGNED RPCSTREAM)))
(SUCCESS (IF (NULL RETTYPES)
T
(DO ((RS RETTYPES (CDR RS))
(VALS))
((NULL RS)
(NREVERSE VALS))
(PUSH (FUNCALL (CAR RS)
RPCSTREAM)
VALS))))
(PROGRAM-MISMATCH (RPC-ERROR-PRM-MISMATCH
ERRORFLG REPLY-STAT ACCEPT-STAT
(XDR-UNSIGNED RPCSTREAM)
(XDR-UNSIGNED RPCSTREAM)))
(PROGRAM-UNAVAILABLE (RPC-ERROR-PRM-UNAVAILABLE
ERRORFLG REPLY-STAT
ACCEPT-STAT))
(PROCEDURE-UNAVAILABLE (RPC-ERROR-PRC-UNAVAILABLE
ERRORFLG REPLY-STAT
ACCEPT-STAT))
(GARBAGE-ARGUMENTS (RPC-ERROR-GARBAGE-ARGS ERRORFLG
REPLY-STAT ACCEPT-STAT))))
(REJECTED (CASE (GET-REJECT-STAT (SETQ REJECT-STAT
(XDR-UNSIGNED RPCSTREAM)))
(RPC-MISMATCH (RPC-ERROR-MISMATCH
ERRORFLG REPLY-STAT ACCEPT-STAT
(XDR-UNSIGNED RPCSTREAM)
(XDR-UNSIGNED RPCSTREAM)))
(AUTHENTICATION-ERROR (RPC-ERROR-AUTHENTICATION
ERRORFLG REPLY-STAT
REJECT-STAT
(XDR-UNSIGNED RPCSTREAM)))
(OTHERWISE (ERROR "Unknown RPC reply status: ~A"
REPLY-STAT)))))))
(DEFUN CREATE-XID NIL
"Returns a number to use as the ID of a given transmisssion."
(SETQ *XID-COUNT* (LOGAND TWOTO32MINUSONE
(+ 1 *XID-COUNT*))))
(IL:* IL:* "RPC Utility Functions")
(DEFUN GET-REPLY-STAT (NUMBER)
"Map number to corresponding reply-stat symbol of remote procedure call"
(CDR (ASSOC NUMBER *RPC-REPLY-STATS*)))
(DEFUN GET-ACCEPT-STAT (NUMBER)
"Map number to corresponding accept-stat symbol of remote procedure call"
(CDR (ASSOC NUMBER *RPC-ACCEPT-STATS*)))
(DEFUN GET-REJECT-STAT (NUMBER)
"Map number to corresponding reject-stat symbol of remote procedure call"
(CDR (ASSOC NUMBER *RPC-REJECT-STATS*)))
(DEFUN GET-AUTHENTICATION-STAT (NUMBER) "Map number to corresponding authentication-stat symbol of remote procedure call"
(CDR (ASSOC NUMBER *RPC-AUTHENTICATION-STATS*)))
(DEFUN GET-PROTOCOL-NUMBER (PROTOCOL) "Map protocol name (e.g., RPC2::UDP) to corresponding protocol number (e.g., 17)"
(OR (CDR (ASSOC PROTOCOL *RPC-PROTOCOLS*))
(ERROR "Could not find number for protocol ~a in *RPC-PROTOCOLS*"
PROTOCOL)))
(DEFUN FIND-CACHED-SOCKET (DESTADDR PROGNUM PROGVERS PROTOCOL CACHE) "Looks up a given (DESTADDR, PROGNUM, PROGVERS, PROTOCOL) in the specified CACHE."
(FIFTH (FIND-IF #'(LAMBDA (QUINT)
(AND (EQL (FIRST QUINT)
DESTADDR)
(EQL (SECOND QUINT)
PROGNUM)
(EQL (THIRD QUINT)
PROGVERS)
(EQL (FOURTH QUINT)
PROTOCOL))) CACHE)))
(IL:* IL:* "RPC Error Messages")
(DEFUN RPC-ERROR-PRM-MISMATCH (ERRORFLG REPLY-STAT ACCEPT-STAT LOW HIGH) "NIL"
(CASE ERRORFLG (:NOERRORS NIL)
(:RETURNERRORS `(ERROR ,(GET-REPLY-STAT REPLY-STAT)
,(GET-ACCEPT-STAT ACCEPT-STAT)
`(,LOW ,HIGH)))
(OTHERWISE (ERROR "RPC Program Mismatch: High: ~A Low: ~A" LOW HIGH))))
(DEFUN RPC-ERROR-PRM-UNAVAILABLE (ERRORFLG REPLY-STAT ACCEPT-STAT) "NIL"
(CASE ERRORFLG (:NOERRORS NIL)
(:RETURNERRORS `(ERROR ,(GET-REPLY-STAT REPLY-STAT)
,(GET-ACCEPT-STAT ACCEPT-STAT)))
(OTHERWISE (ERROR "RPC Program Unavailable"))))
(DEFUN RPC-ERROR-PRC-UNAVAILABLE (ERRORFLG REPLY-STAT ACCEPT-STAT) "NIL"
(CASE ERRORFLG (:NOERRORS NIL)
(:RETURNERRORS `(ERROR ,(GET-REPLY-STAT REPLY-STAT)
,(GET-ACCEPT-STAT ACCEPT-STAT)))
(OTHERWISE (ERROR "RPC Procedure Unavailable"))))
(DEFUN RPC-ERROR-GARBAGE-ARGS (ERRORFLG REPLY-STAT ACCEPT-STAT) "NIL"
(CASE ERRORFLG (:NOERRORS NIL)
(:RETURNERRORS `(ERROR ,(GET-REPLY-STAT REPLY-STAT)
,(GET-ACCEPT-STAT ACCEPT-STAT)))
(OTHERWISE (ERROR "RPC Garbage Arguments"))))
(DEFUN RPC-ERROR-MISMATCH (ERRORFLG REPLY-STAT REJECT-STAT LOW HIGH) "NIL"
(CASE ERRORFLG (:NOERRORS NIL)
(:RETURNERRORS `(ERROR ,(GET-REPLY-STAT REPLY-STAT)
,(GET-REJECT-STAT REJECT-STAT)
`(,LOW ,HIGH)))
(OTHERWISE (ERROR "RPC Mismatch: High: ~A Low: ~A" LOW HIGH))))
(DEFUN RPC-ERROR-AUTHENTICATION (ERRORFLG REPLY-STAT REJECT-STAT
AUTHENTICATION-STAT) "NIL"
(CASE ERRORFLG (:NOERRORS NIL)
(:RETURNERRORS `(ERROR ,(GET-REPLY-STAT REPLY-STAT)
,(GET-REJECT-STAT REJECT-STAT)
,(GET-AUTHENTICATION-STAT AUTHENTICATION-STAT))
)
(OTHERWISE (ERROR "Authorization Error: ~A" (GET-AUTHENTICATION-STAT
AUTHENTICATION-STAT)))))
(IL:* IL:* "Authentication")
(DEFSTRUCT AUTHENTICATION "Sun RPC Version 2 Authentication Record" TYPE
(IL:* IL:\; "0 = NULL")
(IL:* IL:\; "1 = Unix")
(IL:* IL:\; "2 = Short")
STRING
(IL:* IL:\; "")
(IL:* IL:\; "Encoding of any fields of that type authentication. String is a Common Lisp string rather than an XDR-STRING.")
)
(DEFCONSTANT *AUTHENTICATION-TYPEDEF* '(:STRUCT AUTHENTICATION
(TYPE (:ENUMERATION (:NULL 0)
(:UNIX 1)
(:SHORT 2)))
(STRING :STRING)) "NIL" )
(DEFCONSTANT *NULL-AUTHENTICATION* (MAKE-AUTHENTICATION :TYPE :NULL :STRING "")
)
(DEFUN CREATE-UNIX-AUTHENTICATION (STAMP MACHINE-NAME UID GID GIDS) "\
Given the fields of a Unix authentication, creates an AUTHENTICATION struct with\
these fields encoded as a string.\
"
(LET ((UNIX-AUTH (MAKE-AUTHENTICATION))
(TEMPSTREAM (CREATE-STRING-RPC-STREAM)))
(XDR-UNSIGNED TEMPSTREAM STAMP)
(XDR-STRING TEMPSTREAM MACHINE-NAME)
(XDR-UNSIGNED TEMPSTREAM UID)
(XDR-UNSIGNED TEMPSTREAM GID)
(XDR-GENCODE-INLINE NIL '(:COUNTED-ARRAY :UNSIGNED) 'WRITE TEMPSTREAM
GIDS)
(SETF (AUTHENTICATION-TYPE UNIX-AUTH)
:UNIX)
(SETF (AUTHENTICATION-STRING UNIX-AUTH)
(GET-OUTPUT-STREAM-STRING (RPC-STREAM-OUTSTREAM TEMPSTREAM)))
UNIX-AUTH))
(DEFUN ENCODE-AUTHENTICATION (RPCSTREAM AUTH) "\
Given an AUTHENTICATION struct, converts the struct to its XDR encoding and writes it to\
the RPC-STREAM specified.\
"
(IF (NULL AUTH)
(SETQ AUTH *NULL-AUTHENTICATION*))
(CHECK-TYPE AUTH AUTHENTICATION)
(XDR-GENCODE-INLINE NIL *AUTHENTICATION-TYPEDEF* 'WRITE RPCSTREAM AUTH))
(DEFUN DECODE-AUTHENTICATION (RPCSTREAM) "\
Reads an authentication from specified RPC-STREAM and returns it as an AUTHENTICATION\
struct.\
"
(XDR-GENCODE-INLINE NIL *AUTHENTICATION-TYPEDEF* 'READ RPCSTREAM))
(IL:PUTPROPS IL:RPCRPC.LSP IL:COPYRIGHT (IL:NONE))
(IL:DECLARE\: IL:DONTCOPY
(IL:FILEMAP (NIL)))
IL:STOP