ONC+ Developer's Guide

Client Side of the Expert-Level Interface

Example 4–12 shows a version of clntudp_create(), the client creation routine for UDP transport, using clnt_tli_create(). The example shows how to do network selection based on the family of the transport you choose. clnt_tli_create() is used to create a client handle and to:


Example 4–12 Client for RPC Lower Level

#include <stdio.h>

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

#include <netinet/in.h>
/*
 * In earlier implementations of RPC,
 * only TCP/IP and UDP/IP were supported.
 * This version of clntudp_create()
 * is based on TLI/Streams.
 */
CLIENT *
clntudp_create(raddr, prog, vers, wait, sockp)
	struct sockaddr_in *raddr;		/* Remote address */
	rpcprog_t prog;							/* Program number */
	prcvers_t vers;							/* Version number */
	struct timeval wait;				/* Time to wait */
	int *sockp;								/* fd pointer */
{
	CLIENT *cl;								/* Client handle */
	int madefd = FALSE;					/* Is fd opened here */
	int fd = *sockp;						/* TLI fd */
	struct t_bind *tbind;				/* bind address */
	struct netconfig *nconf;			/* netconfig structure */
	void *handlep;
 
	if ((handlep = setnetconfig() ) == (void *) NULL) {
		/* Error starting network configuration */
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		return((CLIENT *) NULL);
	}
	/*
	 * Try all the transports until it gets one that is
	 * connectionless, family is INET, and preferred name is UDP
	 */
	while (nconf = getnetconfig( handlep)) {
		if ((nconf->nc_semantics == NC_TPI_CLTS) &&

		     (strcmp( nconf->nc_protofmly, NC_INET ) == 0) &&
		     (strcmp( nconf->nc_proto, NC_UDP ) == 0))
		 break;
	}
	if (nconf == (struct netconfig *) NULL)
		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
		goto err;
	}
	if (fd == RPC_ANYFD) {
		fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
		if (fd == -1) {
			rpc_createerr.cf_stat = RPC_SYSTEMERROR;
			goto err;
		}
	}
	if (raddr->sin_port == 0) { /* remote addr unknown */
		u_short sport;
		/*
		 * rpcb_getport() is a user-provided routine that calls
		 * rpcb_getaddr and translates the netbuf address to port
		 * number in host byte order.
		 */
		sport = rpcb_getport(raddr, prog, vers, nconf);
		if (sport == 0) {
			rpc_createerr.cf_stat = RPC_PROGUNAVAIL;
			goto err;
		}
		raddr->sin_port = htons(sport);
	}
	/* Transform sockaddr_in to netbuf */
	tbind = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
	if (tbind == (struct t_bind *) NULL)
		rpc_createerr.cf_stat = RPC_SYSTEMERROR;
		goto err;
	}
	if (t_bind->addr.maxlen < sizeof( struct sockaddr_in))
		goto err;
	(void) memcpy( tbind->addr.buf, (char *)raddr,
	               sizeof(struct sockaddr_in));
	tbind->addr.len = sizeof(struct sockaddr_in);
	/* Bind fd */
	if (t_bind( fd, NULL, NULL) == -1) {
		rpc_createerr.ct_stat = RPC_TLIERROR;
		goto err;
	}
	cl = clnt_tli_create(fd, nconf, &(tbind->addr), prog, vers,
	                      tinfo.tsdu, tinfo.tsdu);
	/* Close the netconfig file */
	(void) endnetconfig( handlep);
	(void) t_free((char *) tbind, T_BIND);
	if (cl) {
		*sockp = fd;
		if (madefd == TRUE) {
			/* fd should be closed while destroying the handle */
			(void)clnt_control(cl,CLSET_FD_CLOSE, (char *)NULL);
		}
		/* Set the retry time */
		(void) clnt_control( l, CLSET_RETRY_TIMEOUT,
		                     (char *) &wait);
		return(cl);
	}
err:
	if (madefd == TRUE)
		(void) t_close(fd);
	(void) endnetconfig(handlep);
	return((CLIENT *) NULL);
}

The network is selected using setnetconfig(), getnetconfig(), and endnetconfig(). endnetconfig() is not called until after the call to clnt_tli_create(), near the end of the example.

clntudp_create() can be passed an open TLI fd. If passed none (fd == RPC_ANYFD), clntudp_create() opens its own using the netconfig structure for UDP to find the name of the device to pass to t_open().

If the remote address is not known (raddr->sin_port == 0), it is obtained from the remote rpcbind.

After the client handle has been created, you can modify it using calls to clnt_control(). The RPC library closes the file descriptor when destroying the handle, as it does with a call to clnt_destroy() when it opens the fd itself. The RPC library then sets the retry timeout period.