\begindata{text, 268803984} \textdsversion{11} \define{italic menu:[Font,Italic] attr:[FontFace Italic Int Set]} \define{bold menu:[Font,Bold] attr:[FontFace Bold Int Set]} \define{chapter menu:[Title,Chapter] attr:[Justification Centered Point 0] attr:[FontSize PreviousFontSize Point 4]} \define{section menu:[Title,Section] attr:[Justification Centered Point 0] attr:[FontSize PreviousFontSize Point 4]} \define{subsection menu:[Title,Subsection] attr:[Justification LeftJustified Point 0] attr:[FontSize PreviousFontSize Point 4]} \define{paragraph menu:[Title,Paragraph] attr:[Justification LeftJustified Point 0] attr:[FontSize PreviousFontSize Point 4]} \define{bigger menu:[Font,Bigger] attr:[FontSize PreviousFontSize Point 2]} \define{indent menu:[Region,Indent] attr:[LeftMargin LeftMargin Inch 32768] attr:[RightMargin RightMargin Inch 32768]} \define{typewriter menu:[Font,Typewriter] attr:[FontFace FixedFace Int Set] attr:[FontFamily AndyType Int 0]} \define{display menu:[Region,Display] attr:[LeftMargin LeftMargin Inch 32768] attr:[RightMargin RightMargin Inch 32768] attr:[Justification LeftJustified Point 0]} \define{example menu:[Region,Example] attr:[LeftMargin LeftMargin Inch 32768] attr:[Justification LeftJustified Point 0] attr:[FontFace FixedFace Int Set] attr:[FontFamily AndyType Int 0]} \define{description menu:[Region,Description] attr:[LeftMargin LeftMargin Inch 32768] attr:[Indent LeftEdge Inch -32768]} \define{quotation menu:[Region,Quotation] attr:[LeftMargin LeftMargin Inch 32768] attr:[RightMargin RightMargin Inch 32768] attr:[FontFace Italic Int Set]} \define{subscript menu:[Font,Subscript] attr:[Script PreviousScriptMovement Point 131072] attr:[FontSize PreviousFontSize Point -2]} \define{superscript menu:[Font,Superscript] attr:[Script PreviousScriptMovement Point -393216] attr:[FontSize PreviousFontSize Point -2]} \define{smaller menu:[Font,Smaller] attr:[FontSize PreviousFontSize Point -2]} \define{heading menu:[Title,Heading] attr:[LeftMargin LeftMargin Inch -13107] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set]} \define{majorheading menu:[Title,MajorHeading] attr:[Justification Centered Point 0] attr:[FontSize PreviousFontSize Point 4]} \define{formatnote menu:[Region,FormatNote] attr:[Flags PassThru Int Set]} \define{subheading menu:[Title,Subheading] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set]} \define{center menu:[Justify,Center] attr:[Justification Centered Point 0]} \define{flushleft menu:[Justify,FlushLeft] attr:[Justification LeftJustified Point 0]} \define{flushright menu:[Justify,FlushRight] attr:[Justification RightJustified Point 0]} \define{leftindent menu:[Region,LeftIndent] attr:[LeftMargin LeftMargin Inch 32768]} \define{article menu:[Title,Article] attr:[Justification Centered Point 0] attr:[FontSize ConstantFontSize Point 36]} \define{subpara menu:[Title,Subpara] attr:[Justification LeftJustified Point 0] attr:[FontFace Bold Int Set]} \define{subsubpara menu:[Title,Subsubpara] attr:[Justification LeftJustified Point 0] attr:[FontFace Italic Int Set]} \subsection{R Package} \paragraph{Introduction} R is a very lightweight remote procedure call package. R is built on top of the lightweight process (LWP) and external data representation (XDR) packages. The XDR package provides a single, consistent representation for data types sent over the network, independent of the machine architecture sending the data. The LWP package provides a collection of processes, all sharing the same Unix process, with non-preemptive scheduling between them. The R package provides a reliable datagram service using LWP to provide concurrent processes and XDR to provide the network data representation. The XDR package was written by Sun Microsystems. \paragraph{Description} R provides a reliable, connection-based simple packet exchange protocol running on top of IP/UDP datagrams. Programs can be \bold{clients} or \bold{servers} (occasionally both), where clients make requests to servers and receive responses from these servers. During the time a server is generating a response, the client simply blocks, waiting for the server's response. If a server does not respond in a reasonable amount of time, the client is notified that the request failed. The prototypical R server first calls r_Init, providing the internet port number on which it will be listening for requests. It then enters a perpetual request/response loop. The server receives requests with r_GetRequest and decodes the data in the request packets using \italic{xdr} routines, described below. The request packet is then freed using r_FreePacket and a new packet is allocated using r_AllocSendPacket. The return parameters are placed into the packet, again using \italic{xdr} routines, and the response is sent back to the client using r_SendResponse. The R server need not perform any processing on R connections, since they are automatically managed for the server by the R code. The prototypical R client also begins by calling r_Init. If the client is also an R server, the port passed to r_Init should be the port on which the requests will come in. Otherwise, a 0 may be passed, requesting that any free port be allocated by the system. In order to communicate with a particular server, one must make a client connection to that server. The server is specified by internet address (32 bits in network byte order) and port number (16 bits in network byte order). Once a connection has been obtained, using r_NewConn, calls may be made very simply. First, the client allocates a packet using r_AllocSendPacket. The caller places the requisite parameters into the packet using the \italic{xdr} routines and sends the packet using r_SendPacket. This procedure returns the response packet or NULL if the call timed out. After the response packet has been obtained, the response parameters can be retrieved using \italic{xdr} routines. The procedure r_SendPacket frees the request packet automatically; it is the caller's responsibility to free the response packet, if any, using r_FreePacket. When a client is through communicating with a server, it should destroy the client connection using r_FreeConnection. \subpara{External Data Representation} The Sun \italic{xdr} package provides routines for packing and unpacking objects of various types in network messages. The \italic{xdr} package provides a type, \bold{XDR}, defined in the file \italic{xdr.h}, representing \italic{xdr}'s buffer space available for the \italic{xdr} routines to use. Objects of type XDR, aside from referring to some buffer space, also contain a direction indicating whether the corresponding buffer is being packed or unpacked. The same xdr routines are used for packing and unpacking packets; the direction of the XDR object determines which operation will be performed. All R packets contain a component named \italic{xdrs} of type XDR. When a packet is returned from r_AllocSendPacket, the \italic{xdrs} component is positioned so that calls to \italic{xdr} routines will add data to the packet immediately after the header. Packets returned by r_GetRequest or r_SendPacket, on the other hand, are set up for decoding so that calls to \italic{xdr} routines will decode the data from the packet. Thus most users will not have to explicitly position the XDR objects in R packet buffers, nor explicitly set their directions; the XDR objects within packet buffers appear positioned and oriented appropriately. The simple example below should show how R and XDR interact. For example, to add (or extract) a long integer from an R packet pointed to by tp, you would write \example{xdr_long(&tp->xdrs, &longVariable);} If you wanted to do the same with the following structure: \example{struct FileId \{ long a; short b; \};} named \italic{FileId}, you would write \example{xdr_FileId(&tp->xdrs, &fileIdVariable);} where \italic{xdr_FileId} would be written: \example{long xdr_FileId (xdrStructure, parameter) XDR *xdrStructure; struct FileId *parameter; \{ if (!xdr_long(xdrStructure, ¶meter->a)) return 0; /* failed to fit in packet */ if (!xdr_short(xdrStructure, ¶meter->b)) return 0; /* or ran out of packet before data */ return 1; /* success */ \}} Note that xdr routines are supposed to return 1 if they succeed and 0 if they fail. The \bold{xdrgen} program can be used for automatically generating \italic{xdr} routines for arbitrary structures. \subpara{Authentication} R provides authenticated connections as well as unauthenticated connections, using a very simple but clever authentication scheme. The goal of the authentication scheme is to allow the exchange of encryption keys good for the life of the R connection, without allowing an observer to obtain any useful information. In order to do this, R makes use of a \bold{shared secret}, one shared between the client and the server. This secret is generally an encryption key; for example, a client's password. As part of the authentication process, the client forms a \bold{secret token}, which is essentially a structure containing a random \bold{session key}, as well as an indication of client's identity, all encrypted with the shared secret. The secret token, encrypted with the shared secret, is sent from the client to the server. The server decrypts this secret token using the shared secret and obtains the session key, along with the client's identity. Since the session key is never transmitted in the clear, the server can assume that any client sending appropriately encrypted packets must have the correct session key and thus must know the client's password (since the client formulated the secret token with the client password). Clients are identifed by 32 bit indentifiers called \bold{uids}. Session keys are 64 bit values. Secret tokens can have any representation, although they should contain a uid and session key, appropriately encrypted. In order to use authenticated R connections, instead of regular R connections, two procedures must be provided, one on the client side and one on the server side. Furthermore, before using the R connection, you must mark it as an connection authenticated as a particular user. This is done with the r_SetAuth macro: \example{r_SetAuth(clientConnection, userID);} The client must also provide a procedure, called a \bold{getkeys} procedure, which will be called occasionally by R in order to retrieve the session key and secret token associated with a particular user id. The server must provide a procedure, called a \bold{whoisthisreally} procedure, which is expected to produce a session key and expiration time, given a secret token. See the interface specification for details on how to specify the getkeys and whoisthisreally procedures. \subpara{A Simple Example} Here are sample R server and client programs. First the server: \example{#include #include #include #include /* compiled with -I/usr/andrew/include */ #include myWhoIsThisReally (aconn, asecret, asecretLen) register struct r_connection *aconn; char *asecret; long asecretLen; \{ /* really stupid secret token: "Hello, world" */ if ((asecretLen == 13) && strcmp(asecret, "Hello, world") == 0) \{ aconn->auth.who = 71; /* set authentication to 71 */ bcopy("applefoo", &aconn->auth.key, 8); /* our session key */ return 0; \} else return -1; /* incorrect secret token, don't authenticate conn */ \} main(argc, argv) int argc; char** argv; \{struct r_packet *tp; struct r_connection *tc; long temp; r_nPackets = 3; /* number of packet buffers to allocate */ r_WhoIsThisReally = myWhoIsThisReally; /* we use auth conns */ r_Init(3535); /* our portal # */ printf("back from init\\n"); while (1) \{ r_GetRequest(&tc, &tp); /* get request */ /* check that server connection is authenticated properly */ if (r_GetAuth(tc) != 71) printf("Connection is not authenticated!\\n"); xdr_long(&tp->xdrs, &temp); /* extract parameter */ r_FreePacket(tp); /* toss packet */ tp = r_AllocSendPacket(1000); /* allocate response packet */ ++temp; /* compute response */ xdr_long(&tp->xdrs, &temp); /* insert response value */ r_SendResponse(tc, tp); /* send response, freeing packet */ \} \} } And the client: \example{#include #include #include #include #include /* compile with -I/usr/andrew/include */ #include /* routine called to get session key and secret token for user */ myGetKeys (awho, asecret, aslen, aclear, aconn) long awho; /* user */ char **asecret; /* secret token */ long *aslen; /* size thereof */ struct r_encryptionKey **aclear; /* session key */ struct r_connection *aconn; \{ /* the connection */ static struct r_encryptionKey clearFoo; if (awho == 71) \{ /* disgusting kludge for testing */ *asecret = "Hello, world"; *aclear = &clearFoo; *aslen = 13; /* don't worry, caller will copy out encryption key immediately */ bcopy("applefoo", &clearFoo, 8); return 0; \} return -1; /* don't have key and token for this dude */ \} main(argc, argv) int argc; char** argv; \{ struct r_packet *tp; struct r_connection *tc; char localName[200]; long localAddr; char *remoteName; long remoteAddr; struct hostent *he; long count; register long i; long l, myPortal; /* setup defaults */ count = 1; myPortal = 2525; remoteName = localName; /* parse args */ for(i=1;ih_addr, &remoteAddr, sizeof(long)); r_GetKeys = myGetKeys; /* provide getkeys proc for auth conns */ r_nPackets = 3; /* 3 packet buffers */ r_Init(myPortal); /* initialize package */ tc = r_NewConn(remoteAddr, htons(3535)); /* call portal 3535 */ r_SetAuth(tc, 71); /* authenticate as user 71 */ r_SetLevel(tc, RSECURE); /* encrypt whole packet */ for(i=0;ixdrs, &l); /* put it in the packet */ tp = r_SendPacket(tc, tp); /* send it */ if (tp == NULL) /* test for failure */ printf("r_SendPacket failed.\\n"); else if (!xdr_long(&tp->xdrs, &l) || l != 1213) /* check ret. value */ printf("Bogus value returned.\\n"); if (tp) r_FreePacket(tp); /* toss returned packet */ \} \} Die(astr) char *astr; \{ printf("Fatal error: %s\\n", astr); exit(1); \} } \subpara{RFTP File Transfer Protocol} The amount of data transferred by an R procedure call is bounded by the size of one packet buffer. Pragmatically, it is difficult to make this arbitrarily large. When large amounts of data must be transferred, an alternative protocol must be used. RFTP is one such protocol, designed to be conveniently used along with R. RFTP supports file transfers either from the R client to the R server, or vice versa. In either case, the client, before making the r_SendPacket call to actually do the procedure call, first calls rftp_SendFile (or rftp_GetFile, if fetching a file). The client's \italic{rftp} call should specify a \italic{wait} parameter of 0, indicating that the \italic{rftp} transfer should not actually begin until rftp hears from the other side. After making the R call, the client must either call rftp_SendWait (or rftp_GetWait) or rftp_FreeConnection, depending upon whether the R call succeeded. These wait procedures return a completion code indicating whether the transfer succeeded. The codes indicating that an error occurred are all less than 0. A returned value of 0 indicates success. When called from the client (that is, with the \italic{wait} parameter equal to 0), rftp_SendFile (rftp_GetFile) returns a pointer to a newly-created \italic{rftp} connection. On the server side, things are somewhat simpler. In order to receive (send) a file, the server calls rftp_GetFile (rftp_SendFile) with the \italic{wait} parameter equal to 1, since the server desires the rftp call not return until the transfer has completed. When called from the server, rftp_GetFile (rftp_SendFile) returns an integer code, indicating whether the file transfer succeeded. These are the same error codes as returned by rftp_SendWait and rftp_GetWait, with 0 indicating success and values less than 0 indicating failure. An up-to-date list of error codes may be found in /usr/andrew/include/r/rftp.h. \subpara{RFTP Example} Here is an example of an rftp server and client, combined in one program. This example does not use R, but simply uses RFTP to transfer a file. \example{#include #include #include #include #include #include #include #include #include #include extern long rftp_window; extern long rftp_packetSize; extern long rftp_allocSize; extern long rftp_readAhead; extern long rftp_nPackets; main(argc, argv) int argc; char **argv; \{ long tHost, tPortal, tSid, fd; register long code; struct hostent *he; char *aHost; char storing, *fileName; struct stat tstat; long size, msecs; char localName[100]; register int i; /* setup defaults */ aHost = localName; storing = 0; rftp_window = 8; rftp_nPackets = 50; delay.tv_sec = delay.tv_usec = 0; for(i=1;ih_addr, &tHost, sizeof(long)); rftp_Init(storing? 2525 : 2526); if (storing) \{ fd = open(fileName, O_RDONLY); fstat(fd, &tstat); if (fd<0) return printf("File %s not found.\\n", fileName); code = rftp_SendFile(fd, tHost, 2526, 2001, 0); code = rftp_SendWait(code); close(fd); \} else \{ fd = open(fileName, O_WRONLY | O_TRUNC | O_CREAT, 0666); if (fd<0) return perror("create"); fstat(fd, &tstat); code = rftp_GetFile(fd, tHost, 2525, 2001, 1); close(fd); \} \} Die(astring) char *astring; \{ printf("Fatal error: %s", astring); exit(1); \}} \subpara{Other Packages} Two other programs may be of interest for use with R and \italic{xdr}. These are programs called \italic{xdrgen} and \italic{rgen}. The \bold{xdrgen} program can be used to generate \italic{xdr} routines for structures, given the structure's definition. The \bold{rgen} program can be used to generate procedures that can be called as regular C procedures, but that actually use R and xdr to perform a remote procedure call. Please see the help for rgen for further information on these programs. \paragraph{Interface specification} Your C programs should include these lines: \example{#include #include } \bold{r_Init}(port) u_short port; Initializes the package. If you wish, you can change the values of some of the parameters first by setting variables (see below). The port parameter should be the internet port number of the socket that you will be using. If you pass in a zero, R will pick a port for you, a convenient feature for clients. struct r_connection *\bold{r_NewConn}(host, port) u_long host; u_short port; Creates a new connection to a remote site. The host and port variables specify the address of the destination. \bold{r_FreeConnection}(conn) struct r_connection *conn; Frees a connection that was allocated with r_NewConn. struct r_packet *r_AllocSendPacket(size) int size; Returns a packet suitable for use in r_SendPacket or r_SendResponse. After you get this packet, you can call XDR routines to marshall your arguments to the remote subroutine. There is an XDR structure in the packet header. Note that r does not preallocate an opcode field to identify the remote subroutine. That is up to you. \bold{r_FreePacket}(packet) struct r_packet *packet; Frees up a packet. struct r_packet *\bold{r_SendPacket}(connection, packet) struct r_connection *connection; struct r_packet *packet; Sends a packet on a given connection. On success, the reply packet is returned. On failure, zero is returned. The returned packet is ready to have you unmarshall the arguments with XDR calls. \bold{r_GetRequest}(connectionPtr, packetPtr) struct r_connection **connectionPtr; struct r_packet **packetPtr; Gets the next incoming request. ConnectionPtr and packetPtr are filled in accordingly. If no requests are available, this call blocks. The returned packet is ready to have XDR called on it to unmarshall the arguments. \bold{r_SendResponse}(connection, packet) struct r_connection *connection; struct r_packet *packet; Sends a response to a request. Packet is the reply. You may use the same packet buffer you got from GetRequest, but you may want to allocate a new one since it will be set up for using XDR on. Allocates and deallocates are cheap in this package, so do not worry. \bold{r_NoResponse}(connection) struct r_connection *connection; Sends an error response to a request. If you received a packet from r_GetRequest, do not forget to free it. The client, instead of receiving a response, receives a return code of -1. \bold{r_SendBusy}(connection) struct r_connection *connection; Send a busy packet to a request. If you received a pcaket from r_GetRequest, do not forget to free it. The client's call will be transparently retransmitted after a short timeout period (default is two seconds). Here are the variables that you can set before calling r_Init. int \bold{r_nPackets}; Number of packets to allocate for the buffer pool. Default is 30. int \bold{r_packetSize}; Size of the packets. Default is 3000 bytes. int \bold{r_maxTimeouts}; Number of times to retry on a SendPacket. Default is 8. int \bold{r_retryInterval}; Time to wait between reties, in seconds. Default is 2. int (*\bold{r_GetKeys})(); Routine called by R to get the encryption key for the client end. To use encryption, the client side fills in the who field in the packet header before sending a packet. The r_GetKeys routine will be called by R when it needs to figure out what this who field means. Your GetKeys routine should be declared as follows: GetKeys (who, id, idLen, key, conn) long who; char **id; int *idLen; struct r_EncryptionKey **key; struct r_connection *conn; Who is the value filled in by the client in the packet. Id and idLen should be set to describe a block of data that will be sent to the remote side in the clear. The remote side is expected to be able to figure out an encryption key based on this data. For example, if the server is an authentication server, the data might be the user id and the key that both sides would use would be the user's password. Finally, key should be changed to point to an encryption key to use for this connection. R will copy all the data pointed to by the return values, so they need only survive until the next call to the GetKeys routine. The connection for which the call has come in is passed in as the last parameter, in case the who field's interpretation depends upon the host from which the request has come. Note that the connection is the last parameter for compatibility with a previous version of the package that did not provide this parameter. int (*\bold{r_WhoIsThisReally}) (); This is the corresponding routine called on the server side to provide the encryption key to be used by the server. The routine supplied here should be declared as follows: WhoIsThisReally (conn, id, idLen) struct r_connection *conn; char *id; int idLen; The id and idLen parameters describe the data sent over by the client. This data can be thought of as the secret token, in rpc2 terms. The caller is responsible for filling in the conn->auth structure, that is, an 8 byte session key (which should be passed encrypted in the secret token), a user id (who) identifying the user for whom the connection should be authenticated, and an expiration time, when the key should be discarded. All of this information should be obtainable by decrypting the secret key. Note the gratuitous difference in calling sequence between this routine and the GetKeys routine. \formatnote{.bp} \enddata{text,268803984}