# Digital TCP/IP Services for OpenVMS
ONC RPC Programming

Previous | Contents

Implementors of new XDR streams must make an XDR structure (with new operation routines) available to clients, using some kind of creation routine.

# 4.5 Advanced Topics

This section describes advanced techniques for passing data structures, such as linked lists (of arbitrary length). The examples in this section are written using both the XDR C library routines and the XDR data description language.

The last example in Section 4.1.2 presents a C data structure and its associated XDR routines for an individual's gross assets and liabilities. The example is duplicated here:

    struct gnumbers {
         long g_assets;
         long g_liabilities;
    };

    bool_t
    xdr_gnumbers(xdrs, gp)
         XDR *xdrs;
         struct gnumbers *gp;
    {
         if (xdr_long(xdrs, &(gp->g_assets)))
              return(xdr_long(xdrs, &(gp->g_liabilities)));
         return(FALSE);
    }

If you want to implement a linked list of such information, you could construct the following data structure:

    struct gnumbers_node {
         struct gnumbers gn_numbers;
         struct gnumbers_node *gn_next;
    };

    typedef struct gnumbers_node *gnumbers_list;

You can think of the head of the linked list as the data object; that is, the head is not merely a convenient shorthand for a structure. Similarly the gn_next field indicates whether the object has terminated. Unfortunately, if the object continues, the gn_next field is also the address of where it continues. The link addresses carry no useful information when the object is serialized.

The XDR data description of this linked list is described by the recursive declaration of gnumbers_list:

    struct gnumbers {
         int g_assets;
         int g_liabilities;
    };
    struct gnumbers_node {
         gnumbers gn_numbers;
         gnumbers_node *gn_next;
    };

Here, the boolean indicates whether there is more data following it. If the boolean is FALSE, then it is the last data field of the structure; if TRUE, then it is followed by a gnumbers structure and (recursively) by a gnumbers_list. Note that the C declaration has no boolean explicitly declared in it (though the gn_next field implicitly carries the information), while the XDR data description has no pointer explicitly declared in it. From the XDR description in the previous paragraph, you can determine how to write the XDR routines for a gnumbers_list. That is, the xdr_pointer primitive would implement the XDR union. Unfortunately---due to recursion---using XDR on a list with the following routines causes the C stack to grow linearly with respect to the number of nodes in the list:

    bool_t
    xdr_gnumbers_node(xdrs, gn)
         XDR *xdrs;
         gnumbers_node *gn;
    {
         return(xdr_gnumbers(xdrs, &gn->gn_numbers) &&
         xdr_gnumbers_list(xdrs, &gp->gn_next));
    }

    bool_t
    xdr_gnumbers_list(xdrs, gnp)
         XDR *xdrs;
         gnumbers_list *gnp;
    {
         return(xdr_pointer(xdrs, gnp,
         sizeof(struct gnumbers_node),
         xdr_gnumbers_node));
    }

The following routine combines these two mutually recursive routines into a single, non-recursive one:

    bool_t
    xdr_gnumbers_list(xdrs, gnp)
         XDR *xdrs;
         gnumbers_list *gnp;
    {
         bool_t more_data;
         gnumbers_list *nextp;

         for (;;) {
              more_data = (*gnp != NULL);
              if (!xdr_bool(xdrs, &more_data)) {
                   return(FALSE);
              }
              if (! more_data) {
                   break;
              }
              if (xdrs->x_op == XDR_FREE) {
                   nextp = &(*gnp)->gn_next;
              }
              if (!xdr_reference(xdrs, gnp,
                sizeof(struct gnumbers_node), xdr_gnumbers)) {

                  return(FALSE);
              }
              gnp = (xdrs->x_op == XDR_FREE) ?
              nextp : &(*gnp)->gn_next;
         }
         *gnp = NULL;
         return(TRUE);
    }

The first task is to find out if there is more data, so the boolean information can be serialized. Notice that this is unnecessary in the XDR_DECODE case, because the value of more_data is not known until it is deserialized in the next statement, which uses XDR on the more_data field of the XDR union. If there is no more data, this last pointer is set to NULL to indicate the list end, and a TRUE is returned to indicate completion. Setting the pointer to NULL is only important in the XDR_DECODE case, since it is already NULL in the XDR_ENCODE and XDR_FREE cases.

Next, if the direction is XDR_FREE, the value of nextp is set to indicate the location of the next pointer in the list. This is for dereferencing gnp to find the location of the next item in the list; after the next statement, the storage pointed to by gnp is deallocated and is no longer valid. This cannot be done for all directions because, in the XDR_DECODE direction, the value of gnp is not set until the next statement.

Next, XDR operates on the data in the node through the primitive xdr_reference, which is like xdr_pointer (which was used before). However, xdr_reference does not send over the boolean indicating whether there is more data; it is used instead of xdr_pointer because XDR has already been used on this information. Notice that the XDR routine passed is not the same type as an element in the list. The routine passed is xdr_gnumbers, for using XDR on gnumbers; however, each element in the list is of type gnumbers_node. The xdr_gnumbers_node is not passed because it is recursive; instead, use xdr_gnumbers, which uses XDR on all of the non-recursive parts. Note that this works only if the gn_numbers field is the first item in each element, so the addresses are identical when passed to xdr_reference.

Finally, gnp is updated to point to the next item in the list. If the direction is XDR_FREE, it is set to the previously saved value; otherwise, gnp is dereferenced to get the proper value. Although more difficult to understand than the recursive version, the non-recursive routine is much less likely to overflow the C stack. It also runs more efficiently because a lot of procedure call overhead has been removed. Most lists are small though (in the hundreds of items or less) and the recursive version should be sufficient for them.

# Chapter 5
ONC RPC Client Routines

This chapter describes the client routines that allow C programs to make procedure calls to server programs across the network.

Table 5-1 indicates the task that the routine performs.

Table 5-1 ONC RPC Client Routines
Routine | Task Category
--- | ---
auth_destroy | Destroys authentication information associated with an authentication handle (macro).
authnone_create | Creates and returns a null authentication handle for the client process.
authunix_create | Creates and returns a UNIX-style authentication handle for the client process.
authunix_create_default | Creates and returns a UNIX-style authentication handle containing default authentication information for the client process.
callrpc | Calls the remote procedure identified by the routine's arguments.
clnt_broadcast | Broadcasts a remote procedure call to all locally-connected networks using the broadcast address.
clnt_call | Calls a remote procedure (macro).
clnt_control | Changes or retrieves information about an RPC client process (macro)
clnt_create | Creates an RPC client handle for a remote server procedure.
clnt_create_vers | Creates an RPC client handle for a remote server procedure having the highest supported version number within a specified range.
clnt_destroy | Destroys a client handle (macro).
clnt_freeres | Frees the memory that RPC allocated when it decoded a remote procedure's results (macro).
clnt_geterr | Returns an error code indicating why an RPC call failed (macro).
clnt_pcreateerror | Prints an error message indicating why RPC could not create a client handle.
clnt_perrno | Prints an error message indicating why a callrpc or clnt_broadcast routine failed.
clnt_perror | Prints an error message indicating why a clnt_call routine failed.
clnt_spcreateerror | Returns a message string indicating why RPC could not create a client handle.
clnt_sperrno | Returns a message string indicating why a callrpc or clnt_broadcast routine failed.
clnt_sperror | Returns a message string indicating why a clnt_call routine failed.
clntraw_create | Creates an RPC client handle for a server procedure included in the same program as the client.
clnttcp_create | Creates an RPC client handle for a remote server procedure using the TCP transport.
clntudp_bufcreate | Creates an RPC client handle for a remote server procedure using a buffered UDP transport.
clntudp_create | Creates an RPC client handle for a remote server procedure using the UDP transport.
get_myaddress | Returns the local host's internet address.
get_myaddr_dest | Returns the local host's internet address as seen by the remote host.

# auth_destroy

> A macro that frees the memory associated with the authentication handle created by the authnone_create and authunix_create routines.

## Format

> ### #include <rpc/rpc.h>

> ### void auth_destroy(AUTH *auth_handle)

## ARGUMENTS

> ### auth_handle

> An RPC authentication handle created by the authnone_create, authunix_create, or authunix_create_default routine.

## DESCRIPTION

> Frees the memory associated with the AUTH data structure created by the authnone_create, authunix_create, or authunix_create_default routine. Be careful not to reference the data structure after calling this routine.

## Return Values

> None |

# authnone_create

> Creates a authentication handle for passing null credentials and verifiers to remote systems.

## Format

> ### #include <rpc/rpc.h>

> ### AUTH *authnone_create ( )

## ARGUMENTS

> ### None

## DESCRIPTION

> Creates and returns an authentication handle that passes null authentication information with each remote procedure call. Use this routine if the server process does not require authentication information. RPC uses this routine as the default authentication routine unless you create another authentication handle using either the authunix_create or authunix_create_default routine.

## Return Values

> AUTH* | Authentication handle containing the pertinent information.
> NULL | Indicates allocation of AUTH handle failed.

# authunix_create

> Creates and returns an RPC authentication handle that contains UNIX-style authentication information.

## Format

> ### #include <rpc/rpc.h>

> ### AUTH *authunix_create(char *host, int uid, int gid, int len, int *aup_gids );

## ARGUMENTS

> ### host

> Pointer to the name of the host on which the information was created. This is usually the name of the system running the client process.

> ### uid

> The user's user identification.

> ### gid

> The user's current group.

> ### len

> The number of elements in aup_gids array.

> > Note

> > This parameters is ignored by UCX's RPC implementation.

> ### aup_gids

> A pointer to an array of groups to which the user belongs.

> > Note

> > This parameters is ignored by UCX's RPC implementation.

## DESCRIPTION

> Implements UNIX-style authentication parameters. The client uses no encryption for its credentials and only sends null verifiers. The server sends back null verifiers or optionally a verifier that suggests a new shorthand for the credentials.

## Return Values

> AUTH* | Authentication handle containing the pertinent information.
> NULL | Indicates allocation of AUTH handle failed.

# authunix_create_default

> Returns a default authentication handle.

## Format

> ### #include <rpc/rpc.h>

> ### AUTH *authunix_create_default( )

## ARGUMENTS

> ### None

## DESCRIPTION

> Calls the authunix_create routine with the local host name, effective process ID and group ID, and the process default groups.

## Return Values

> AUTH* | Authentication handle containing the pertinent information.
> NULL | Indicates allocation of AUTH handle failed.

##

### Examples

#1

    auth_destroy(client->cl_auth)
    client->cl_auth = authunix_create_default();

> This example overrides the default authnone_create action. The client handle, client, is returned by the clnt_create, clnt_create_vers, clnttcp_create, or clntudp_create routine.

# callrpc

> Executes a remote procedure call.

## Format

> ### #include <rpc/rpc.h>

> ### int callrpc(char *host, u_long prognum, u_long versnum, u_long procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out);

## ARGUMENTS

> ### host

> A pointer to the name of the host on which the remote procedure resides.

> ### prognum

> The program number associated with the remote procedure.

> ### versnum

> The version number associated with the remote procedure.

> ### procnum

> The procedure number associated with the remote procedure.

> ### inproc

> The XDR routine used to encode the remote procedure's arguments.

> ### in

> A pointer to the remote procedure's arguments.

> ### outproc

> The XDR routine used to decode the remote procedure's results.

> ### out

> A pointer to the remote procedure's results.

## DESCRIPTION

> Calls the remote procedure associated with prognum, versnum, and procnum on the host host. This routine performs the same functions as a set of calls to the clnt_create, clnt_call, and clnt_destroy routines. This routine returns RPC_SUCCESS if it succeeds, or the value of enum clnt_stat cast to an integer if it fails. The routine clnt_perrno is handy for translating a failure status into a message.

> > Note

> > Calling remote procedures with this routine uses UDP/IP as a transport; see clntudp_create for restrictions. You do not have control of timeouts or authentication using this routine. If you want to use the TCP transport, use the clnt_create or clnttcp_create routine.

## Return Values

> RPC_SUCCESS | Indicates success.
> clnt_stat | Returns a value of type enum clnt_stat cast to type int containing the status of the callrpc operation.

# clnt_broadcast

> Executes a remote procedure call that is sent to all locally connected networks using the broadcast address.

## Format

> ### #include <rpc/rpc.h>

> ### enum clnt_stat clnt_broadcast(u_long prognum, u_long versnum, u_long procnum, xdrproc_t inproc, char * in, xdrproc_t outproc, char * out, resultproc_t eachresult);

## ARGUMENTS

> ### prognum

> The program number associated with the remote procedure.

> ### versnum

> The version number associated with the remote procedure.

> ### procnum

> The procedure number associated with the remote procedure.

> ### inproc

> The XDR routine used to encode the remote procedure's arguments.

> ### in

> A pointer to the remote procedure's arguments.

> ### outproc

> The XDR routine used to decode the remote procedure's results.

> ### out

> A pointer to the remote procedure's results.

> ### eachresult

> Called each time the routine receives a response. Specify the routine as follows:

>     int eachresult(char *resultsp, struct sockaddr_in *addr)

> resultsp is the same as the parameter passed to clnt_broadcast(), except that the remote procedure's output is decoded there. addr is a pointer to a sockaddr_in structure containing the address of the host that sent the results.

> If eachresult is NULL, the clnt_broadcast routine returns without waiting for any replies.

## DESCRIPTION

> Performs the same function as the callrpc routine, except that the call message is sent to all locally connected networks using the broadcast address. Each time it receives a response, this routine calls the eachresult routine. If eachresult returns zero, clnt_broadcast waits for more replies; otherwise it assumes success and returns RPC_SUCCESS.

> > Note

> > This routine uses the UDP protocol. Broadcast sockets are limited in size to the maximum transfer unit of the data link. For Ethernet, this value is 1400 bytes. For FDDI, this value is 4500 bytes.

## Return Values

> RPC_SUCCESS | Indicates success.
> clnt_stat | Returns the buffer of type enum clnt_stat containing the status of the clnt_broadcast operation.

# clnt_call

> A macro that calls a remote procedure.

## Format

> ### #include <rpc/rpc.h>

> ### enum clnt_stat clnt_call(CLIENT *handle, u_long procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out, struct timeval timeout);

## ARGUMENTS

> ### handle

> A pointer to a client handle created by any of the client handle creation routines.

> ### procnum

> The procedure number associated with the remote procedure.

> ### inproc

> The XDR routine used to encode the remote procedure's arguments.

> ### in

> A pointer to the remote procedure's arguments.

> ### outproc

> The XDR routine used to decode the remote procedure's results.

> ### out

> A pointer to the remote procedure's results.

> ### timeout

> A structure describing the time allowed for results to return to the client. If you have previously used the clnt_control macro with the CLSET_TIMEOUT code, this value is ignored.

## DESCRIPTION

> Use the clnt_call macro after using one of the client handle creation routines. After you are finished with the handle, return it using the clnt_destroy macro. Use the clnt_perror to print any errors that occurred.

## Return Values

> RPC_SUCCESS | Indicates success.
> clnt_stat | Returns the buffer of type enum clnt_stat containing the status of the clnt_call operation.

# clnt_control

> A macro that changes or retrieves information about an RPC client process.

## Format

> ### #include <rpc/rpc.h>

> ### bool_t clnt_control(CLIENT *handle, u_int code, char *info);

## ARGUMENTS

> ### handle

> A pointer to a client handle created by any of the client handle creation routines.

> ### code

> A code designating the type of information to be set or retrieved.

> ### info

> A pointer to a buffer containing the information for a SET operation or the results of a GET operation.

## DESCRIPTION

> For UDP and TCP transports specify any of the following for code:

> CLSET_TIMEOUT | struct timeval | Set total timeout
> CLGET_TIMEOUT | struct timeval | Get total timeout
> CLGET_SERVER_ADDR | struct sockaddr_in | Get server address
> CLGET_FD | int | Get associated socket
> CL_FD_CLOSE | void | Close socket on clnt_destroy
> CL_FD_NCLOSE | void | Leave socket open on clnt_destroy

> If you set the timeout using clnt_control, ONC RPC ignores the timeout parameter in all future clnt_call calls. The default total timeout is 25 seconds.

> For the UDP transport two additional options are available:

> CLSET_RETRY_TIMEOUT | struct timeval | Set retry timeout
> CLGET_RETRY_TIMEOUT | struct timeval | Get retry timeout

> The timeout value in these two calls is the time that UDP waits for a response before retransmitting the message to the server. The default time is 5 seconds. The retry timeout controls when UDP retransmits the request, the total timeout controls the total time that the client should wait for a response. For example, with the default settings, UDP will retry the transmission four times at 5-second intervals.

## Return Values

> TRUE | Success
> FALSE | Failure

# clnt_create

> Creates a client handle and returns its address.

## Format

> ### #include <rpc/rpc.h>

> ### CLIENT *clnt_create(char *host, u_long prognum, u_long versnum, char *protocol);

## ARGUMENTS

> ### host

> A pointer to the name of the remote host.

> ### prognum

> The program number associated with the remote procedure.

> ### versnum

> The version number associated with the remote procedure.

> ### protocol

> A pointer to a string containing the name of the protocol for transmitting and receiving RPC messages. Specify either tcp or udp.

## DESCRIPTION

> The clnt_create routine creates an RPC client handle for prognum. An RPC client handle is a structure containing information about the RPC client. The client can use the UDP or TCP transport protocol.

> This routine uses the Portmapper. You cannot control the local port.

> The default sizes of the send and receive buffers are 8800 bytes for the UDP transport, and 4000 bytes for the TCP transport. The retry time for the UDP transport is five seconds.

> Use the clnt_create routine instead of the callrpc or clnt_broadcast routines if you want to use one of the following:

> - The TCP transport
> - A non-null authentication
> - More than one active client at the same time

> You can also use the clnttcp_create routine to use the TCP protocol, or the clntudp_create routine to use the UDP protocol.

> The clnt_create routine uses the global variable rpc_createerr. rpc_createerr is a structure that contains the most recent service creation error. Use rpc_createerrif you want the client program to handle the error. The value of rpc_createerr is set by any RPC client creation routine that does not succeed.

> > Note

> > If the requested program is available on the host but the program does not support the requested version number, this routine still succeeds. A subsequent call to the clnt_call routine will discover the version mismatch. Use the clnt_create_vers routine if you want to avoid this condition.

## Return Values

> CLIENT* | Client handle containing the server information.
> NULL | Error occurred while creating the client handle. Use the clnt_pcreateerror or clnt_spcreateerror routine to obtain diagnostic information.

# clnt_create_vers

> Creates a client handle and returns its address. Seeks to use a server supporting the highest version number within a specified range.

## Format

> ### #include <rpc/rpc.h>

> ### CLIENT *clnt_create_vers(char *host, u_long prognum, u_long *versnum, u_long min_vers, u_long max_vers, char *protocol);

## ARGUMENTS

> ### host

> A pointer to the name of the remote host.

> ### prognum

> The program number associated with the remote procedure.

> ### versnum

> The version number associated with the remote procedure. This value is returned by the routine. The value is the highest version number supported by the remote server that is in the range of version numbers specified by min_vers and max_vers. The argument may remain undefined; see additional information in the Description section.

> ### min_vers

> The minimum acceptable version number for the remote procedure.

> ### max_vers

> The maximum acceptable version number for the remote procedure.

> ### protocol

> A pointer to a string containing the name of the protocol for transmitting and receiving RPC messages. Specify either tcp or udp.

## DESCRIPTION

> Previous | Next | Contents