ONC+ RPC Developer's Guide

Exit Print View

Updated: July 2014
 
 

Expert-Level Interface

At the expert level, network selection is done the same as at the intermediate level. The only difference is in the increased level of control that the application has over the details of the CLIENT and SVCXPRT handles. These examples illustrate this control, which is exercised using the clnt_tli_create() and svc_tli_create() routines. For more information on TLI, see Oracle Solaris 11.2 Programming Interfaces 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:

  • Pass an open TLI file descriptor, which might or might not be bound

  • Pass the server's address to the client

  • Specify the send and receive buffer size

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.

Server Side of the Expert-Level Interface

Example 4–13 shows the server side of Example 4–12. It is called svcudp_create(). The server side uses svc_tli_create().

svc_tli_create() is used when the application needs a fine degree of control, particularly to:

  • Pass an open file descriptor to the application.

  • Pass the user's bind address.

  • Set the send and receive buffer sizes. The fd argument can be unbound when passed in. It is then bound to a given address, and the address is stored in a handle. If the bind address is set to NULL and the fd is initially unbound, it is bound to any suitable address.

Use rpcb_set() to register the service with rpcbind.

Example 4-13  Server for RPC Lower Level
#include <stdio.h>
#include <rpc/rpc.h>
#include <netconfig.h>
#include <netinet/in.h>
 
SVCXPRT *
svcudp_create(fd)
	register int fd;
{
	struct netconfig *nconf;
	SVCXPRT *svc;
	int madefd = FALSE;
	int port;
	void *handlep;
	struct  t_info tinfo;
 
	/* If no transports available */
	if ((handlep = setnetconfig() ) == (void *) NULL) {
		nc_perror("server");
		return((SVCXPRT *) NULL);
	}
	/*
	 * Try all the transports until it gets one which is
	 * connectionless, family is INET and, 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) {
		endnetconfig(handlep);
		return((SVCXPRT *) NULL);
	}
	if (fd == RPC_ANYFD) {
		fd = t_open(nconf->nc_device, O_RDWR, &tinfo);
		if (fd == -1) {
			(void) endnetconfig();
			return((SVCXPRT *) NULL);
		}
		madefd = TRUE;
	} else
		t_getinfo(fd, &tinfo);
	svc = svc_tli_create(fd, nconf, (struct t_bind *) NULL,
	                      tinfo.tsdu, tinfo.tsdu);
	(void) endnetconfig(handlep);
	if (svc == (SVCXPRT *) NULL) {
		if (madefd)
			(void) t_close(fd);
		return((SVCXPRT *)NULL);
	}
	return (svc);
}

The network selection here is accomplished similar to clntudp_create(). The file descriptor is not bound explicitly to a transport address because svc_tli_create() does that.

svcudp_create() can use an open fd. It opens one itself using the selected netconfig structure if none is provided.