Trusted Solaris Developer's Guide

Client-Server Application

This is a simple client-server application to show how security attributes are sent and received with RPC library routines. Command line arguments supply the server name and a user ID, and the server process retrieves the user ID sent by the client, multiplies the input by 2, and sends the result to the client. To run the programs, compile them with the libraries listed in "Header Files and Libraries".

Header File

The following header file rpc_test.h is required for the example application to compile.

#include <rpc/rpc.h>
#include <rpc/types.h>
#define RPC_TEST_PROG ((u_long)1234567890)
#define RPC_TEST_VERS ((u_long)1)
#define RPC_TEST_DOUBLE1 ((u_long)1)
#define RPC_TEST_EXIT1 ((u_long)2)

Client Program

This part of the client program accepts command line inputs and creates a client handle.

#include <stdio.h>
#include <stdlib.h>
#include <rpc/rpc.h>
#include <netdb.h>
#include <tsix/t6attrs.h>
#include "rpc_test.h"

extern int
main(int argc, char *argv[])
{
	struct timeval time_out;
	CLIENT *handlep;
	enum clnt_stat stat;
	int input, output;
	uid_t uid;
	if (argc < 2 || argc > 3) {
		fprintf(stderr,
			"Usage: simple_rpc_clnt_test HOSTNAME [UID]\n");
		exit(1);
	}

	handlep = clnt_create(argv[1], RPC_TEST_PROG,
		RPC_TEST_VERS, "udp");
		if (handlep == (CLIENT *) NULL) {
			fprintf(stderr, "Couldn't create client%s.\n",
				clnt_spcreateerror(""));
		exit(1);
	}

This part of the client program sets the client handle to point to the space allocated for the user ID to be input from the command line, sets the user ID value, sends the value to the server process, and waits for the server response. The client prints out the server response before it exits.

The client program needs the net_setid privilege in its effective set to send a changed outgoing user ID. The code comments indicate where privilege bracketing should occur.

	if (argc == 3) {
		handlep->cl_tsol_outgoing_attrsp = t6alloc_blk(T6M_UID);
		if (handlep->cl_tsol_outgoing_attrsp == NULL) {
			fprintf(stderr, "Can't create attr buffer\n");
			exit(1);
		}

		printf ("Sending UID %s\n", argv[2]);
		uid = atoi(argv[2]);
		if (t6set_attr(T6_UID, &uid,
			handlep->cl_tsol_outgoing_attrsp) != 0) {
			fprintf(stderr, "Error returned by t6set_attr.\n");
			exit(1);
		}
	}
	time_out.tv_sec = 30;
	time_out.tv_usec = 0;
	input = 3;

/* Turn net_uid on in the effective set */
	stat = clnt_call(handlep, RPC_TEST_DOUBLE1, xdr_int,
		(caddr_t) &input, xdr_int, (caddr_t) &output, time_out);
	if (stat != RPC_SUCCESS) {
		fprintf(stderr, "Call failed. %s.\n",
			clnt_sperror(handlep, ""));
			exit(1);
	}
/* Turn off the net_uid privilege */

	printf("Response received: %d\n", output);
	(void) clnt_destroy(handlep);

	return (0);
}

Server Program

The server program sets the server handle to point to the space allocated space for all security attributes.

#include <stdio.h>
#include <stdlib.h>
#include <rpc/rpc.h>
#include <tsix/t6attrs.h>
#include "rpc_test.h"
static void proc_1(struct svc_req *rqstp, SVCXPRT *transp);
extern int
main(int argc, char *argv[])
{
	SVCXPRT *handlep;
	struct netconfig *netconfigp;
	netconfigp = getnetconfigent("udp");
	if (netconfigp == NULL) {
		fprintf(stderr, "Cannot find netconfig entry for udp.\n");
		exit(1);
	}

	handlep = svc_tp_create(proc_1, RPC_TEST_PROG,
		RPC_TEST_VERS, netconfigp);

	if (handlep == NULL) {
		fprintf(stderr, "Cannot create service.\n");
		exit(1);
	}
	freenetconfigent(netconfigp);
	handlep->xp_tsol_incoming_attrsp = t6alloc_blk(T6M_ALL_ATTRS);
	if (handlep->xp_tsol_incoming_attrsp == NULL) {
		fprintf(stderr, "Can't create attr buffer\n");
		exit(1);
	}
	svc_run();
	return (0);
}

Remote Procedure

The remote procedure receives the user ID from command line arguments, and multiplies the input by 2, sends the result to the client and prints the response before exiting.

static void
proc_1(struct svc_req *rqstp, SVCXPRT *handlep)
{
	int input;
	int result;
	uid_t *uidp;

	switch(rqstp->rq_proc) {
	case NULLPROC:
			svc_sendreply(handlep, xdr_void, NULL);
			break;
	case RPC_TEST_DOUBLE1:
			if (!svc_getargs(handlep, xdr_int, (caddr_t) &input)) {
				fprintf(stderr, "Error from svc_getargs\n");
				svcerr_systemerr(handlep);
			}
			uidp = (uid_t *) t6get_attr(T6_UID,
				handlep->xp_tsol_incoming_attrsp);
			if (uidp == NULL)
				fprintf(stderr, "Error from t6get_attr.\n");
			else printf("Client's UID is %d\n", *uidp);
			result = 2 * input;
			if (!svc_sendreply(handlep, xdr_int, (caddr_t) &result)) {
				fprintf(stderr, "Error from sendreply\n");
				svcerr_systemerr(handlep);
			}
			svc_freeargs(handlep, xdr_int, (caddr_t) &input);
			break;

	default:
			fprintf(stderr, "Call to unexpected procedure number %d\n",
				rqstp->rq_proc);
		svcerr_noproc(handlep);
		break;
	}
}

Running the Simple Application

The client process takes the server host name and a user ID as input parameters and prints that it is sending the specified user ID:


owl%  phoenix
phoenix% owl 2570
Sending UID 2570

The server retrieves the user ID and prints it out as follows:


Client's UID is 2570

The client process prints the server response and then exits:


Response received: 6
phoenix%