(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