OpenSolaris_b135/cmd/dcs/sparc/sun4u/rdr_messages.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * WARNING: The contents of this file are shared by all projects
 * that  wish to  perform  remote  Dynamic Reconfiguration  (DR)
 * operations. Copies of this file can be found in the following
 * locations:
 *
 *	Project	    Location
 *	-------	    --------
 *	Solaris	    usr/src/cmd/dcs/sparc/sun4u/%M%
 *	SMS	    src/sms/lib/librdr/%M%
 *
 * In order for proper communication to occur,  the files in the
 * above locations must match exactly. Any changes that are made
 * to this file should  be made to all of the files in the list.
 */

/*
 * This file is a module that contains an interface for performing
 * remote Dynamic Reconfiguration (DR) operations. It hides all
 * network operations such as establishing a connection, sending
 * and receiving messages, and closing a connection. It also handles
 * the packing and unpacking of messages for network transport.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <dlfcn.h>
#include <netdb.h>
#include <libdscp.h>
#include <sys/socket.h>
#include <sys/systeminfo.h>
#include <netinet/tcp.h>

#include "dcs.h"
#include "remote_cfg.h"
#include "rdr_param_types.h"
#include "rdr_messages.h"


/*
 * Structure holding information about
 * all possible variable length fields
 * that can be present in an RDR message.
 */
typedef struct {
	int	ap_id_int_size;
	int	ap_id_char_size;
	int	*ap_id_sizes;
	char	*ap_id_chars;
	int	errstring_strlen;
	int	errstring_pad_sz;
	int	options_strlen;
	int	options_pad_sz;
	int	listopts_strlen;
	int	listopts_pad_sz;
	int	function_strlen;
	int	function_pad_sz;
} rdr_variable_message_info_t;

/*
 * A table of maximum sizes for each message type. Message size is
 * validated when the message header is first received. This prevents
 * a situation where a corrupted or bad header can cause too much
 * memory to be allocated.
 *
 * The message size limits were chosen to be a very generous upper bound
 * on the amount of data each message can send. They are not intended to
 * be a precise measurement of the data size.
 */
#define	NOMSG		0
#define	SHORTMSG	(150 * 1024)		/* 150 KB */
#define	LONGMSG		(3 * 1024 * 1024)	/* 3 MB */

struct {
	ulong_t	req_max;
	ulong_t	reply_max;
} msg_sizes[] = {
	/*
	 * request	reply
	 * -------	-----
	 */
	{  NOMSG,	NOMSG	  },	/*  Invalid Opcode		*/
	{  SHORTMSG,	SHORTMSG  },	/*  RDR_SES_REQ			*/
	{  NOMSG,	NOMSG	  },	/*  RDR_SES_ESTBL		*/
	{  NOMSG,	NOMSG	  },	/*  RDR_SES_END			*/
	{  SHORTMSG,	SHORTMSG  },	/*  RDR_CONF_CHANGE_STATE	*/
	{  SHORTMSG,	SHORTMSG  },	/*  RDR_CONF_PRIVATE_FUNC	*/
	{  SHORTMSG,	SHORTMSG  },	/*  RDR_CONF_TEST		*/
	{  SHORTMSG,	LONGMSG	  },	/*  RDR_CONF_LIST_EXT		*/
	{  SHORTMSG,	NOMSG	  },	/*  RDR_CONF_HELP		*/
	{  SHORTMSG,	NOMSG	  },	/*  RDR_CONF_AP_ID_CMP		*/
	{  SHORTMSG,	NOMSG	  },	/*  RDR_CONF_ABORT_CMD		*/
	{  SHORTMSG,	SHORTMSG  },	/*  RDR_CONF_CONFIRM_CALLBACK 	*/
	{  SHORTMSG,	NOMSG	  },	/*  RDR_CONF_MSG_CALLBACK	*/
	{  SHORTMSG,	LONGMSG	  }	/*  RDR_RSRC_INFO		*/
};


#define	RDR_BAD_FD		(-1)

#define	RDR_MSG_HDR_SIZE 	sizeof (rdr_msg_hdr_t)

static const int RDR_ALIGN_64_BIT = 8;   /* 8 bytes */

/*
 * Interfaces for dynamic use of libdscp.
 */

#define	LIBDSCP_PATH	"/usr/platform/%s/lib/libdscp.so.1"

#define	LIBDSCP_BIND	"dscpBind"
#define	LIBDSCP_SECURE	"dscpSecure"
#define	LIBDSCP_AUTH	"dscpAuth"

typedef enum {
	LIBDSCP_UNKNOWN = 0,
	LIBDSCP_AVAILABLE,
	LIBDSCP_UNAVAILABLE
} dscp_status_t;

typedef struct {
	dscp_status_t	status;
	int		(*bind)(int, int, int);
	int		(*secure)(int, int);
	int		(*auth)(int, struct sockaddr *, int);
} libdscp_t;

static libdscp_t libdscp;

/*
 * Static Function Declarations
 */

/*
 * Socket Related Routines
 */
static int rdr_setopt(int fd, int name, int level);

static int rdr_bind(int fd, struct sockaddr *addr);

static int rdr_secure(int fd, struct sockaddr *addr);

static int rdr_auth(struct sockaddr *addr, int len);

static int rdr_snd(int fd, rdr_msg_hdr_t *hdr, char *data, int data_sz,
			int timeout);
static int rdr_snd_raw(int fd, char *msg, int data_sz, int timeout);

static int rdr_rcv(int fd, rdr_msg_hdr_t *hdr, char **data, int timeout);

static int rdr_rcv_raw(int fd, char *msg, int data_size, int timeout);

/*
 * Data Validation Routines
 */
static int validate_header(rdr_msg_hdr_t *hdr);


/*
 * Session Request Routines
 */
static int pack_ses_req_request(ses_req_params_t *params, char **buf,
			int *buf_size);
static int unpack_ses_req_request(ses_req_params_t *params, const char *buf);

static int pack_ses_req_reply(ses_req_params_t *params, char **buf,
			int *buf_size);
static int unpack_ses_req_reply(ses_req_params_t *params, const char *buf);


/*
 * Change State Routines
 */
static int pack_change_state_request(change_state_params_t *params,
			char **buf, int *buf_size);
static int unpack_change_state_request(change_state_params_t *params,
			const char *buf);
static int pack_change_state_reply(change_state_params_t *params,
			char **buf, int *buf_size);
static int unpack_change_state_reply(change_state_params_t *params,
			const char *buf);

/*
 * Private Func Routines
 */
static int pack_private_func_request(private_func_params_t *params,
			char **buf, int *buf_size);
static int unpack_private_func_request(private_func_params_t *params,
			const char *buf);
static int pack_private_func_reply(private_func_params_t *params,
			char **buf, int *buf_size);
static int unpack_private_func_reply(private_func_params_t *params,
			const char *buf);

/*
 * Test Routines
 */
static int pack_test_request(test_params_t *params, char **buf, int *buf_size);

static int unpack_test_request(test_params_t *params, const char *buf);

static int pack_test_reply(test_params_t *params, char **buf, int *buf_size);

static int unpack_test_reply(test_params_t *params, const char *buf);


/*
 * List Ext Routines
 */
static int pack_list_ext_request(list_ext_params_t *params, char **buf,
			int *buf_size);
static int unpack_list_ext_request(list_ext_params_t *params, const char *buf);

static int pack_list_ext_reply(list_ext_params_t *params, char **buf,
			int *buf_size);
static int unpack_list_ext_reply(list_ext_params_t *params, const char *buf);


/*
 * Help Routines
 */
static int pack_help_request(help_params_t *params, char **buf, int *buf_size);

static int unpack_help_request(help_params_t *params, const char *buf);


/*
 * Ap Id Cmp Routines
 */
static int pack_ap_id_cmp_request(ap_id_cmp_params_t *params, char **buf,
			int *buf_size);
static int unpack_ap_id_cmp_request(ap_id_cmp_params_t *params,
			const char *buf);

/*
 * Abort Routines
 */
static int pack_abort_cmd_request(abort_cmd_params_t *params, char **buf,
			int *buf_size);
static int unpack_abort_cmd_request(abort_cmd_params_t *params,
			const char *buf);

/*
 * Confirm Callback Routines
 */
static int pack_confirm_request(confirm_callback_params_t *params, char **buf,
			int *buf_size);
static int unpack_confirm_request(confirm_callback_params_t *params,
			const char *buf);
static int pack_confirm_reply(confirm_callback_params_t *params,
			char **buf, int *buf_size);
static int unpack_confirm_reply(confirm_callback_params_t *params,
			const char *buf);

/*
 * Message Callback Routines
 */
static int pack_message_request(msg_callback_params_t *params, char **buf,
			int *buf_size);
static int unpack_message_request(msg_callback_params_t *params,
			const char *buf);

/*
 * Resource Info Routines
 */
static int pack_rsrc_info_request(rsrc_info_params_t *params, char **buf,
			int *buf_size);
static int unpack_rsrc_info_request(rsrc_info_params_t *params,
			const char *buf);
static int pack_rsrc_info_reply(rsrc_info_params_t *params, char **buf,
			int *buf_size, int encoding);
static int unpack_rsrc_info_reply(rsrc_info_params_t *params, const char *buf);

/*
 * General Pack/Unpack Routines
 */
static int pack_ap_ids(int num_ap_ids, char *const *ap_ids,
			rdr_variable_message_info_t *var_msg_info);
static int unpack_ap_ids(int num_ap_ids, char **ap_ids, const char *buf,
			rdr_variable_message_info_t *var_msg_info);

/*
 * Find Variable Info Sizes
 */
static int find_options_sizes(char *options,
			rdr_variable_message_info_t *var_msg_info);
static int find_listopts_sizes(char *listopts,
			rdr_variable_message_info_t *var_msg_info);
static int find_function_sizes(char *function,
			rdr_variable_message_info_t *var_msg_info);
static int find_errstring_sizes(char **errstring,
			rdr_variable_message_info_t *var_msg_info);

/*
 * Extract Info From Buffers
 */
static int get_ap_ids_from_buf(char ***ap_id_ptr, int num_ap_ids,
			rdr_variable_message_info_t *var_msg_info,
			const char *buf);
static int get_string_from_buf(char **stringptr, int strsize, const char *buf);


/*
 * Cleanup Routines
 */
static int cleanup_ap_ids(int num_ap_ids, char **ap_ids);

static int cleanup_errstring(char **errstring);

static void cleanup_variable_ap_id_info(
			rdr_variable_message_info_t *var_msg_info);

/*
 * Functions for loading libdscp.
 */
static int load_libdscp(libdscp_t *libdscp);

/*
 * Public Functions
 */


/*
 * rdr_open:
 *
 * Establish a transport endpoint to prepare for a new
 * connection. Returns a file descriptor representing the
 * new transport if successful or RDR_BAD_FD upon failure.
 */
int
rdr_open(int family)
{
	int	newfd;


	if ((newfd = socket(family, SOCK_STREAM, 0)) == NULL) {
		return (RDR_BAD_FD);
	}

	return (newfd);
}


/*
 * rdr_init:
 *
 * Initialize a transport endpoint. This involves binding to
 * a particular port and setting any user specified socket
 * options.
 */
int
rdr_init(int fd, struct sockaddr *addr, int *opts, int num_opts, int blog)
{
	int	i;


	/* sanity checks */
	if ((fd < 0) || (addr == NULL)) {
		return (RDR_ERROR);
	}

	if ((opts == NULL) || (num_opts < 0)) {
		num_opts = 0;
	}

	/* turn on security features */
	if (rdr_secure(fd, addr) != RDR_OK) {
		return (RDR_NET_ERR);
	}

	/* bind the address, if is not already bound */
	if (rdr_bind(fd, addr) != RDR_OK) {
		return (RDR_NET_ERR);
	}

	/*
	 * Set TCP_NODELAY for this endpoint. This disables Nagle's
	 * algorithm that can cause a delay in sending small sized
	 * messages. Since most of the RDR messages are small, this
	 * is a restriction that negatively impacts performance.
	 */
	if (rdr_setopt(fd, TCP_NODELAY, IPPROTO_TCP) != RDR_OK) {
		return (RDR_NET_ERR);
	}

	/* set the user specified socket options */
	for (i = 0; i < num_opts; i++) {
		if (rdr_setopt(fd, opts[i], SOL_SOCKET) != RDR_OK) {
			return (RDR_NET_ERR);
		}
	}

	/*
	 * If blog is not zero, it is a server that is being
	 * initialized. In order for it to be able to accept
	 * connections, we have to set the size of the incoming
	 * connection queue.
	 */
	if (blog != 0) {
		if (listen(fd, blog) == -1) {
			return (RDR_NET_ERR);
		}
	}

	return (RDR_OK);
}


/*
 * rdr_connect_clnt:
 *
 * Perform the necessary steps for a client to connect to
 * a server process. The required information is the file
 * descriptor for the transport endpoint, and the remote
 * address.
 */
int
rdr_connect_clnt(int fd, struct sockaddr *addr)
{
	unsigned int	addr_len;


	/* sanity check */
	if (addr == NULL) {
		return (RDR_ERROR);
	}

	/* initialize the address length */
	switch (addr->sa_family) {

	case AF_INET:
		addr_len = sizeof (struct sockaddr_in);
		break;

	case AF_INET6:
		addr_len = sizeof (struct sockaddr_in6);
		break;

	default:
		return (RDR_ERROR);
	}

	/* attempt the connection */
	if (connect(fd, addr, addr_len) == -1) {
		return (RDR_NET_ERR);
	}

	return (RDR_OK);
}


/*
 * rdr_connect_srv:
 *
 * Perform the necessary steps for a server to connect to a
 * pending client request. The new connection is allocated a
 * new file descriptor, separate from the one used to accept
 * the connection.
 */
int
rdr_connect_srv(int fd)
{
	int			newfd;
	unsigned int		faddr_len;
	struct sockaddr_storage	faddr;


	/* accept the connection */
	faddr_len = sizeof (faddr);
	if ((newfd = accept(fd, (struct sockaddr *)&faddr, &faddr_len)) == -1) {
		return (RDR_BAD_FD);
	}

	/* if the peer doesn't authenticate properly, reject */
	if (rdr_auth((struct sockaddr *)&faddr, faddr_len) != RDR_OK) {
		(void) close(newfd);
		return (RDR_BAD_FD);
	}

	return (newfd);
}


/*
 * rdr_reject:
 *
 * Reject an incoming connection attempt. This requires
 * that the connection be accepted first.
 */
int
rdr_reject(int fd)
{
	unsigned int		faddr_len;
	struct sockaddr_storage	faddr;


	/* first accept the connection */
	faddr_len = sizeof (faddr);
	if (accept(fd, (struct sockaddr *)&faddr, &faddr_len) == -1) {
		return (RDR_NET_ERR);
	}

	/* then close it */
	(void) close(fd);

	return (RDR_OK);
}


/*
 * rdr_close:
 *
 * Close down an given connection.
 */
int
rdr_close(int fd)
{
	(void) close(fd);

	return (RDR_OK);
}


/*
 * rdr_snd_msg:
 *
 * Public interface for sending an RDR message. The data
 * passed in through hdr and param are packed for network
 * transport and sent.
 */
int
rdr_snd_msg(int fd, rdr_msg_hdr_t *hdr, cfga_params_t *param, int timeout)
{
	int	err;
	char	*pack_buf = NULL;
	int	pack_buf_sz = 0;


	/* sanity checks */
	if ((hdr == NULL) || (param == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Pack the message for transport
	 */
	switch (hdr->message_opcode) {

		case RDR_SES_REQ: {

			ses_req_params_t *rparam;
			rparam = (ses_req_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_ses_req_request(rparam,
				    &pack_buf, &pack_buf_sz);
			} else {
				err = pack_ses_req_reply(rparam,
				    &pack_buf, &pack_buf_sz);
			}

			break;
		}

		case RDR_SES_ESTBL:
		case RDR_SES_END:

			/*
			 * This is not an error condition because
			 * there is no extra information to pack.
			 */
			err = RDR_OK;
			break;

		case RDR_CONF_CHANGE_STATE: {

			change_state_params_t *cparam;
			cparam = (change_state_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_change_state_request(cparam,
				    &pack_buf, &pack_buf_sz);
			} else {
				err = pack_change_state_reply(cparam,
				    &pack_buf, &pack_buf_sz);
			}
			break;
		}

		case RDR_CONF_PRIVATE_FUNC: {

			private_func_params_t *pparam;
			pparam = (private_func_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_private_func_request(pparam,
				    &pack_buf, &pack_buf_sz);
			} else {
				err = pack_private_func_reply(pparam,
				    &pack_buf, &pack_buf_sz);
			}
			break;
		}

		case RDR_CONF_TEST: {

			test_params_t *tparam;
			tparam = (test_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_test_request(tparam,
				    &pack_buf, &pack_buf_sz);
			} else {
				err = pack_test_reply(tparam,
				    &pack_buf, &pack_buf_sz);
			}
			break;
		}

		case RDR_CONF_LIST_EXT: {

			list_ext_params_t *lparam;
			lparam = (list_ext_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_list_ext_request(lparam, &pack_buf,
				    &pack_buf_sz);
			} else {
				err = pack_list_ext_reply(lparam, &pack_buf,
				    &pack_buf_sz);
			}
			break;
		}

		case RDR_CONF_HELP: {

			help_params_t *hparam;
			hparam = (help_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_help_request(hparam,
				    &pack_buf, &pack_buf_sz);
			} else {

				/*
				 * This is not an error because help
				 * reply does not have any extra information
				 * to pack.
				 */
				err = RDR_OK;
			}
			break;
		}

		case RDR_CONF_AP_ID_CMP: {

			ap_id_cmp_params_t *aparam;
			aparam = (ap_id_cmp_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_ap_id_cmp_request(aparam,
				    &pack_buf, &pack_buf_sz);
			} else {

				/*
				 * This is not an error because ap_id_cmp
				 * reply does not have any extra information
				 * to pack.
				 */
				err = RDR_OK;
			}
			break;
		}

		case RDR_CONF_ABORT_CMD: {

			abort_cmd_params_t *aparam;
			aparam = (abort_cmd_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_abort_cmd_request(aparam,
				    &pack_buf, &pack_buf_sz);
			} else {
				/*
				 * This is not an error because session
				 * abort reply does not have any extra
				 * information to pack.
				 */
				err = RDR_OK;
			}
			break;
		}

		case RDR_CONF_CONFIRM_CALLBACK: {

			confirm_callback_params_t *cparam;
			cparam = (confirm_callback_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_confirm_request(cparam,
				    &pack_buf, &pack_buf_sz);
			} else {
				err = pack_confirm_reply(cparam, &pack_buf,
				    &pack_buf_sz);
			}
			break;
		}

		case RDR_CONF_MSG_CALLBACK: {

			msg_callback_params_t *mparam;
			mparam = (msg_callback_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_message_request(mparam,
				    &pack_buf, &pack_buf_sz);
			} else {
				/*
				 * It is an error to send a reply
				 * to a message callback.
				 */
				err = RDR_MSG_INVAL;
			}
			break;
		}

		case RDR_RSRC_INFO: {

			rsrc_info_params_t *rparam;
			rparam = (rsrc_info_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = pack_rsrc_info_request(rparam, &pack_buf,
				    &pack_buf_sz);
			} else {
				if ((hdr->major_version == 1) &&
				    (hdr->minor_version == 0)) {
					err = pack_rsrc_info_reply(rparam,
					    &pack_buf, &pack_buf_sz,
					    NV_ENCODE_NATIVE);
				} else {
					err = pack_rsrc_info_reply(rparam,
					    &pack_buf, &pack_buf_sz,
					    NV_ENCODE_XDR);
				}
			}
			break;
		}

		default:
			err = RDR_MSG_INVAL;
			break;
	}

	/* check if packed correctly */
	if (err != RDR_OK) {
		return (err);
	}

	/* send the message */
	err = rdr_snd(fd, hdr, pack_buf, pack_buf_sz, timeout);

	free((void *)pack_buf);

	return (err);
}


/*
 * rdr_rcv_msg:
 *
 * Public interface for receiving an RDR message. Data is
 * unpacked into the hdr and param paramters.
 */
int
rdr_rcv_msg(int fd, rdr_msg_hdr_t *hdr, cfga_params_t *param, int timeout)
{
	int	err;
	char	*unpack_buf = NULL;


	/* sanity checks */
	if ((hdr == NULL) || (param == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(param, 0, sizeof (cfga_params_t));

	/* receive the message */
	if ((err = rdr_rcv(fd, hdr, &unpack_buf, timeout)) != RDR_OK) {
		return (err);
	}

	/*
	 * Unpack the message
	 */
	switch (hdr->message_opcode) {

		case RDR_SES_REQ: {

			ses_req_params_t *rparam;
			rparam = (ses_req_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_ses_req_request(rparam,
				    unpack_buf);
			} else {
				err = unpack_ses_req_reply(rparam, unpack_buf);
			}
			break;
		}

		case RDR_SES_ESTBL:
		case RDR_SES_END:

			/* no information to unpack */
			(void) memset(param, 0, sizeof (cfga_params_t));
			err = RDR_OK;
			break;

		case RDR_CONF_CHANGE_STATE: {

			change_state_params_t *cparam;
			cparam = (change_state_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_change_state_request(cparam,
				    unpack_buf);
			} else {
				err = unpack_change_state_reply(cparam,
				    unpack_buf);
			}
			break;
		}

		case RDR_CONF_PRIVATE_FUNC: {

			private_func_params_t *pparam;
			pparam = (private_func_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_private_func_request(pparam,
				    unpack_buf);
			} else {
				err = unpack_private_func_reply(pparam,
				    unpack_buf);
			}
			break;
		}

		case RDR_CONF_TEST: {

			test_params_t *tparam;
			tparam = (test_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_test_request(tparam, unpack_buf);
			} else {
				err = unpack_test_reply(tparam, unpack_buf);
			}
			break;
		}

		case RDR_CONF_LIST_EXT: {

			list_ext_params_t *lparam;
			lparam = (list_ext_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_list_ext_request(lparam,
				    unpack_buf);
			} else {
				err = unpack_list_ext_reply(lparam, unpack_buf);
			}
			break;
		}

		case RDR_CONF_HELP: {

			help_params_t *hparam;
			hparam = (help_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_help_request(hparam,
				    unpack_buf);
			} else {
				/*
				 * This is not an error because help
				 * reply does not have any extra information
				 * to unpack.
				 */
				err = RDR_OK;
			}
			break;
		}

		case RDR_CONF_AP_ID_CMP: {

			ap_id_cmp_params_t *aparam;
			aparam = (ap_id_cmp_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_ap_id_cmp_request(aparam,
				    unpack_buf);
			} else {
				/*
				 * This is not an error because ap_id_cmp
				 * reply does not have any extra information
				 * to pack.
				 */
				err = RDR_OK;
			}
			break;
		}

		case RDR_CONF_ABORT_CMD: {

			abort_cmd_params_t *aparam;
			aparam = (abort_cmd_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_abort_cmd_request(aparam,
				    unpack_buf);
			} else {
				/* no information to unpack */
				(void) memset(param, 0, sizeof (cfga_params_t));
				err = RDR_OK;
			}

			break;
		}

		case RDR_CONF_CONFIRM_CALLBACK: {

			confirm_callback_params_t *cparam;
			cparam = (confirm_callback_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_confirm_request(cparam,
				    unpack_buf);
			} else {
				err = unpack_confirm_reply(cparam, unpack_buf);
			}
			break;
		}

		case RDR_CONF_MSG_CALLBACK: {

			msg_callback_params_t *mparam;
			mparam = (msg_callback_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_message_request(mparam,
				    unpack_buf);
			} else {
				/*
				 * It is an error to send a reply
				 * to a message callback.
				 */
				(void) memset(param, 0, sizeof (cfga_params_t));
				err = RDR_MSG_INVAL;
			}
			break;
		}

		case RDR_RSRC_INFO: {

			rsrc_info_params_t *rparam;
			rparam = (rsrc_info_params_t *)param;

			if (hdr->data_type == RDR_REQUEST) {
				err = unpack_rsrc_info_request(rparam,
				    unpack_buf);
			} else {
				err = unpack_rsrc_info_reply(rparam,
				    unpack_buf);
			}
			break;
		}

		default:
			err = RDR_MSG_INVAL;
			break;
	}

	free(unpack_buf);

	/* check if unpacked correctly */
	if (err != RDR_OK) {
		return (err);
	}

	return (RDR_OK);
}


/*
 * rdr_cleanup_params:
 *
 * Deallocate any memory that was allocated in unpacking a
 * message.
 */
int
rdr_cleanup_params(rdr_msg_opcode_t message_opcode, cfga_params_t *param)
{
	/* sanity check */
	if ((param == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Deallocate memory depending on
	 * the operation.
	 */
	switch (message_opcode) {

	case RDR_SES_REQ: {

		ses_req_params_t *sparam;
		sparam = (ses_req_params_t *)param;

		if (sparam->locale_str != NULL) {
			free((void *)sparam->locale_str);
			sparam->locale_str = NULL;
		}
		break;
	}

	case RDR_SES_ESTBL:
	case RDR_SES_END:

		/* nothing to deallocate */
		break;

	case RDR_CONF_CHANGE_STATE: {

		change_state_params_t *cparam;
		cparam = (change_state_params_t *)param;

		cleanup_ap_ids(cparam->num_ap_ids, (char **)cparam->ap_ids);
		cparam->ap_ids = NULL;
		if (cparam->options != NULL) {
			free((void *)cparam->options);
			cparam->options = NULL;
		}
		if (cparam->confp != NULL) {
			free((void *)cparam->confp);
			cparam->confp = NULL;
		}
		if (cparam->msgp != NULL) {
			free((void *)cparam->msgp);
			cparam->msgp = NULL;
		}
		cleanup_errstring(cparam->errstring);
		break;
	}

	case RDR_CONF_PRIVATE_FUNC: {

		private_func_params_t *pparam;
		pparam = (private_func_params_t *)param;

		cleanup_ap_ids(pparam->num_ap_ids, (char **)pparam->ap_ids);
		pparam->ap_ids = NULL;
		if (pparam->options != NULL) {
			free((void *)pparam->options);
			pparam->options = NULL;
		}
		if (pparam->confp != NULL) {
			free((void *)pparam->confp);
			pparam->confp = NULL;
		}
		if (pparam->msgp != NULL) {
			free((void *)pparam->msgp);
			pparam->msgp = NULL;
		}
		cleanup_errstring(pparam->errstring);
		break;
	}

	case RDR_CONF_TEST: {

		test_params_t *tparam;
		tparam = (test_params_t *)param;

		cleanup_ap_ids(tparam->num_ap_ids, (char **)tparam->ap_ids);
		tparam->ap_ids = NULL;
		if (tparam->options != NULL) {
			free((void *)tparam->options);
			tparam->options = NULL;
		}
		if (tparam->msgp != NULL) {
			free((void *)tparam->msgp);
			tparam->msgp = NULL;
		}
		cleanup_errstring(tparam->errstring);
		break;
	}

	case RDR_CONF_LIST_EXT: {

		list_ext_params_t *lparam;
		lparam = (list_ext_params_t *)param;

		cleanup_ap_ids(lparam->num_ap_ids, (char **)lparam->ap_ids);
		lparam->ap_ids = NULL;

		if (lparam->nlist != NULL) {
			free((void *)lparam->nlist);
			lparam->nlist = NULL;
		}
		if (lparam->ap_id_list != NULL) {
			if (*lparam->ap_id_list != NULL) {
				free((void *)*lparam->ap_id_list);
			}
			free((void *)lparam->ap_id_list);
			lparam->ap_id_list = NULL;
		}
		if (lparam->ap_id_list != NULL) {
			free((void *)lparam->ap_id_list);
			lparam->ap_id_list = NULL;
		}

		if (lparam->options != NULL) {
			free((void *)lparam->options);
			lparam->options = NULL;
		}
		if (lparam->listopts != NULL) {
			free((void *)lparam->listopts);
			lparam->listopts = NULL;
		}
		cleanup_errstring(lparam->errstring);
		break;
	}

	case RDR_CONF_HELP: {

		help_params_t *hparam;
		hparam = (help_params_t *)param;

		cleanup_ap_ids(hparam->num_ap_ids, (char **)hparam->ap_ids);
		hparam->ap_ids = NULL;
		if (hparam->msgp != NULL) {
			free((void *)hparam->msgp);
			hparam->msgp = NULL;
		}
		if (hparam->options != NULL) {
			free((void *)hparam->options);
			hparam->options = NULL;
		}
		break;
	}

	case RDR_CONF_AP_ID_CMP: {

		ap_id_cmp_params_t *aparam;
		aparam = (ap_id_cmp_params_t *)param;

		if (aparam->ap_log_id1 != NULL) {
			free((void *)aparam->ap_log_id1);
			aparam->ap_log_id1 = NULL;
		}
		if (aparam->ap_log_id2 != NULL) {
			free((void *)aparam->ap_log_id2);
			aparam->ap_log_id2 = NULL;
		}
		break;
	}

	case RDR_CONF_ABORT_CMD:

		/* nothing to deallocate */
		break;

	case RDR_CONF_CONFIRM_CALLBACK: {

		confirm_callback_params_t *cparam;
		cparam = (confirm_callback_params_t *)param;

		if (cparam->confp != NULL) {
			free((void *)cparam->confp);
			cparam->confp = NULL;
		}
		if (cparam->message != NULL) {
			free((void *)cparam->message);
			cparam->message = NULL;
		}
		break;
	}

	case RDR_CONF_MSG_CALLBACK: {

		msg_callback_params_t *mparam;
		mparam = (msg_callback_params_t *)param;

		if (mparam->msgp != NULL) {
			free((void *)mparam->msgp);
			mparam->msgp = NULL;
		}
		if (mparam->message != NULL) {
			free((void *)mparam->message);
			mparam->message = NULL;
		}
		break;
	}

	default:
		return (RDR_ERROR);
		/* NOTREACHED */
		break;

	}

	return (RDR_OK);
}

/*
 * rdr_setsockopt:
 *
 * Wrapper of the setsockopt(3SOCKET) library function.
 */
int
rdr_setsockopt(int fd, int level, int optname, const void *optval, int optlen)
{
	if (setsockopt(fd, level, optname, optval, optlen) == -1)
		return (RDR_NET_ERR);
	else
		return (RDR_OK);
}


/*
 * Private (static) Functions
 */


/*
 * rdr_setopt:
 *
 * Set the specified option for a given transport endpoint.
 * This function only sets boolean options. It does not
 * provide the ability to unset an option, or set a non-
 * boolean option.
 */
static int
rdr_setopt(int fd, int name, int level)
{
	int	on = 1;


	if (setsockopt(fd, level, name, &on, sizeof (on)) == -1) {
		return (RDR_NET_ERR);
	}

	return (RDR_OK);
}


/*
 * rdr_bind:
 *
 * Bind the specified file descriptor to a specified
 * address. If the address is already bound, no error is
 * returned. This is the expected behavior if a server
 * has been started by inetd (1M).
 */
static int
rdr_bind(int fd, struct sockaddr *addr)
{
	unsigned int		addr_len;
	int			rc;


	/* initialize the address */
	switch (addr->sa_family) {

	case AF_INET:
		addr_len = sizeof (struct sockaddr_in);
		break;

	case AF_INET6:
		addr_len = sizeof (struct sockaddr_in6);
		break;

	default:
		return (RDR_ERROR);
	}

	/* attempt to bind the address */
	rc = bind(fd, addr, addr_len);

	/*
	 * Ignore the error if EINVAL is returned. In
	 * this case, we assume that this means that
	 * the address was already bound. This is not
	 * an error for servers started by inetd (1M).
	 */
	if ((rc == -1) && (errno != EINVAL)) {
		return (RDR_NET_ERR);
	}

	/*
	 * Retreive the address information of the
	 * address that was actually bound.
	 */
	addr_len = sizeof (*addr);
	if (getsockname(fd, addr, &addr_len) == -1) {
		(void) memset(addr, 0, sizeof (*addr));
		return (RDR_NET_ERR);
	}

	return (RDR_OK);
}


/*
 * rdr_secure:
 *
 * Activate security features for a socket.
 *
 * Some platforms have libdscp, which provides additional
 * security features.  An attempt is made to load libdscp
 * and use these features.
 *
 * Nothing is done if libdscp is not available.
 */
static int
rdr_secure(int fd, struct sockaddr *addr)
{
	struct sockaddr_in	*sin;
	int			port;
	int			error;

	if (use_libdscp == 0) {
		return (RDR_OK);
	}

	if (load_libdscp(&libdscp) != 1) {
		return (RDR_ERROR);
	}

	/* LINTED E_BAD_PTR_CAST_ALIGN */
	sin = (struct sockaddr_in *)addr;
	port = ntohs(sin->sin_port);
	error = libdscp.bind(0, fd, port);

	if ((error != DSCP_OK) && (error != DSCP_ERROR_ALREADY)) {
		return (RDR_ERROR);
	}

	if (libdscp.secure(0, fd) != DSCP_OK) {
		return (RDR_ERROR);
	}
	return (RDR_OK);
}

/*
 * rdr_auth:
 *
 * Authenticate if a connection is really from the service
 * processor.  This is dependent upon functionality from
 * libdscp, so an attempt to load and use libdscp is made.
 *
 * Without libdscp, this function does nothing.
 */
static int
rdr_auth(struct sockaddr *addr, int len)
{
	if (use_libdscp != 0) {
		if ((load_libdscp(&libdscp) == 0) ||
		    (libdscp.auth(0, addr, len) != DSCP_OK)) {
			return (RDR_ERROR);
		}
	}

	return (RDR_OK);
}

/*
 * rdr_snd:
 *
 * Send a message in two stages. First the header is sent,
 * followed by the packed buffer containing the message
 * contents.
 */
static int
rdr_snd(int fd, rdr_msg_hdr_t *hdr, char *data, int data_sz, int timeout)
{
	int	err;


	/* sanity check */
	if (hdr == NULL) {
		return (RDR_ERROR);
	}

	/* ensure null pad bytes */
	hdr->pad_byte1 = 0;
	hdr->pad_byte2 = 0;

	/* initialize size information */
	hdr->data_length = data_sz;

	/* send message header */
	err = rdr_snd_raw(fd, (char *)hdr, RDR_MSG_HDR_SIZE, timeout);
	if (err != RDR_OK) {
		return (err);
	}

	/* check if more to send */
	if (data_sz == 0) {
		return (RDR_OK);
	}

	/* send message data */
	err = rdr_snd_raw(fd, data, data_sz, timeout);
	if (err != RDR_OK) {
		return (err);
	}

	return (RDR_OK);
}


/*
 * rdr_snd_raw:
 *
 * Send a raw buffer of information. This function handles
 * the low level details of the send operation.
 */
static int
rdr_snd_raw(int fd, char *msg, int data_sz, int timeout)
{
	int		err;
	int		num_bytes;
	int		bytes_left;
	char		*bufp;
	struct pollfd	pfd;


	bufp = (char *)msg;

	bytes_left = data_sz;

	pfd.fd = fd;
	pfd.events = POLLOUT;

	while (bytes_left > 0) {

		pfd.revents = 0;

		/* wait until we can send the data */
		if ((err = poll(&pfd, 1, timeout)) == -1) {

			/* poll was interrupted */
			if (errno == EINTR) {
				return (RDR_ABORTED);
			}

			return (RDR_ERROR);

		} else if (err == 0) {
			return (RDR_TIMEOUT);
		}

		/* ready to send data */
		if (pfd.revents & POLLOUT) {

			num_bytes = write(fd, bufp, bytes_left);

			if (num_bytes == -1) {

				/*
				 * Distinguish between an aborted
				 * session and other network errors.
				 */
				if (errno == EPIPE) {
					return (RDR_ABORTED);
				} else {
					return (RDR_NET_ERR);
				}
			}

			/* wrote 0 bytes, so operation was aborted */
			if (num_bytes == 0) {
				return (RDR_ABORTED);
			}

		} else {
			return (RDR_NET_ERR);
		}

		bytes_left -= num_bytes;
		bufp += num_bytes;
	}

	return (RDR_OK);
}


/*
 * rdr_rcv:
 *
 * Receive a message in two stages. First the header is
 * received, followed by the packed buffer containing the
 * message contents.
 */
static int
rdr_rcv(int fd, rdr_msg_hdr_t *hdr, char **data, int timeout)
{
	int	err;
	int	data_sz;
	char	hdr_buf[RDR_MSG_HDR_SIZE];
	char	*buf = NULL;


	/* sanity check */
	if (hdr == NULL) {
		return (RDR_ERROR);
	}

	/* receive the header */
	err = rdr_rcv_raw(fd, hdr_buf, RDR_MSG_HDR_SIZE, timeout);
	if (err != RDR_OK) {
		return (err);
	}

	/* verify that the data is good */
	/* LINTED Pointer Cast Alignment Warning */
	if (validate_header((rdr_msg_hdr_t *)hdr_buf) != RDR_OK) {
		return (RDR_MSG_INVAL);
	}

	/* LINTED Pointer Cast Alignment Warning */
	data_sz = ((rdr_msg_hdr_t *)hdr_buf)->data_length;

	buf = (char *)malloc(data_sz);
	if (!buf) {
		return (RDR_MEM_ALLOC);
	}

	if (data_sz != 0) {

		/* receive the rest of the message */
		err = rdr_rcv_raw(fd, buf, data_sz, timeout);
		if (err != RDR_OK) {
			free((void *)buf);
			return (err);
		}
	}

	/* copy out data */
	*data = buf;
	(void) memcpy(hdr, hdr_buf, RDR_MSG_HDR_SIZE);

	return (RDR_OK);
}


/*
 * rdr_rcv_raw:
 *
 * Receive a raw buffer of information. This function handles
 * the low level details of the receive operation.
 */
static int
rdr_rcv_raw(int fd, char *msg, int data_size, int timeout)
{
	int		num_bytes;
	int		err;
	int		bytes_left;
	char		*bufp;
	struct pollfd	pollfd;


	bufp = (char *)msg;
	bytes_left = data_size;

	pollfd.fd = fd;
	pollfd.events = POLLIN;

	while (bytes_left > 0) {

		errno = 0;
		pollfd.revents = 0;

		if ((err = poll(&pollfd, 1, timeout)) == -1) {

			/*
			 * In the DCA, if a session is aborted, SIGINT
			 * is delivered to all active sessions. This
			 * mistakenly causes all sessions waiting in
			 * the poll to be interrupted. So, if EINTR
			 * is returned, it is ignored. If another error
			 * occurs right away, the current session really
			 * was aborted. All other sessions won't encounter
			 * an error and will proceed normally.
			 */
			if ((errno == 0) || (errno == EINTR)) {
				continue;
			}

			return (RDR_ABORTED);

		} else if (err == 0) {
			return (RDR_TIMEOUT);
		}

		/* ready to receive data */
		if (pollfd.revents & POLLIN) {

			num_bytes = read(fd, bufp, bytes_left);

			if (num_bytes == -1) {

				/*
				 * Distinguish between an aborted
				 * session and other network errors.
				 */
				if (errno == ECONNRESET) {
					return (RDR_ABORTED);
				} else {
					return (RDR_NET_ERR);
				}
			}

			/* read 0 bytes, so operation was aborted */
			if (num_bytes == 0) {
				return (RDR_ABORTED);
			}

		} else {
			return (RDR_NET_ERR);
		}

		bytes_left -= num_bytes;
		bufp += num_bytes;
	}

	return (RDR_OK);
}


/*
 * validate_header:
 *
 * Perform a series of sanity checks on the header data that is
 * received. This gets called before the variable length data is
 * read in to make sure that the information in the header can
 * be trusted.
 */
static int
validate_header(rdr_msg_hdr_t *hdr)
{
	unsigned char	op;


	if (hdr == NULL) {
		return (RDR_ERROR);
	}

	op = hdr->message_opcode;

	/* validate opcode */
	if ((op < RDR_SES_REQ) || (op >= RDR_NUM_OPS)) {
		return (RDR_ERROR);
	}

	/* validate message size (and type) for op */
	switch (hdr->data_type) {

	case RDR_REQUEST:
		if (hdr->data_length > msg_sizes[op].req_max) {
			return (RDR_ERROR);
		}
		break;

	case RDR_REPLY:
		if (hdr->data_length > msg_sizes[op].reply_max) {
			return (RDR_ERROR);
		}
		break;

	default:
		/* invalid data type */
		return (RDR_ERROR);
	}

	/* all checks passed */
	return (RDR_OK);
}


/*
 * pack_ses_req_request:
 *
 * Handle packing a session request request message.
 */
static int
pack_ses_req_request(ses_req_params_t *params, char **buf, int *buf_size)
{
	char		*bufptr;
	int		locale_str_len;
	rdr_ses_req_t	ses_req;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Determine the size of the locale string
	 */
	if (params->locale_str != NULL) {
		locale_str_len = strlen(params->locale_str) + 1;
	} else {
		locale_str_len = 0;
	}

	/*
	 * Collect size info specific to the ses_req request message
	 * and allocate a buffer
	 */
	*buf_size = sizeof (rdr_ses_req_t);
	*buf_size += locale_str_len;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed locale size label by name
	 */
	ses_req.locale_size = locale_str_len;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &ses_req, sizeof (rdr_ses_req_t));
	bufptr += sizeof (rdr_ses_req_t);

	if (params->locale_str != NULL) {
		(void) memcpy(bufptr, params->locale_str, locale_str_len);
		bufptr += locale_str_len;
	}

	return (RDR_OK);
}


/*
 * unpack_ses_req_request:
 *
 * Handle unpacking a session request request message.
 */
static int
unpack_ses_req_request(ses_req_params_t *params, const char *buf)
{
	char		*bufptr;
	rdr_ses_req_t	ses_req_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&ses_req_data, bufptr, sizeof (rdr_ses_req_t));
	bufptr += sizeof (rdr_ses_req_t);

	/*
	 * handle getting the locale string
	 */
	if (get_string_from_buf(&(params->locale_str),
	    ses_req_data.locale_size, bufptr)) {
		return (RDR_ERROR);
	}

	return (RDR_OK);
}


/*
 * pack_ses_req_reply:
 *
 * Handle packing a session request reply message.
 */
static int
pack_ses_req_reply(ses_req_params_t *params, char **buf, int *buf_size)
{
	rdr_ses_req_reply_t	ses_req_reply_data;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the session request reply
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_ses_req_reply_t);

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed session identifier
	 */
	ses_req_reply_data.session_id = params->session_id;

	/*
	 * Copy information using memcpy
	 */
	(void) memcpy(*buf, &ses_req_reply_data, sizeof (rdr_ses_req_reply_t));

	return (RDR_OK);
}


/*
 * unpack_ses_req_request:
 *
 * Handle unpacking a session request reply message.
 */
static int
unpack_ses_req_reply(ses_req_params_t *params, const char *buf)
{
	rdr_ses_req_reply_t	*ses_req_reply_datap;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	/* LINTED Pointer Cast Alignment Warning */
	ses_req_reply_datap = (rdr_ses_req_reply_t *)buf;

	/*
	 * copy out the session information
	 */
	params->session_id = ses_req_reply_datap->session_id;

	return (RDR_OK);
}


/*
 * pack_change_state_request:
 *
 * Handle packing a change state request message.
 */
static int
pack_change_state_request(change_state_params_t *params, char **buf,
    int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_change_state_t		change_state_data;
	rdr_variable_message_info_t	var_msg_info;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (pack_ap_ids(params->num_ap_ids, params->ap_ids, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}
	if (find_options_sizes(params->options, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the change_state request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_change_state_t);
	*buf_size += var_msg_info.ap_id_int_size;
	*buf_size += var_msg_info.ap_id_char_size;
	*buf_size += var_msg_info.options_strlen;
	*buf_size += var_msg_info.options_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	change_state_data.num_ap_ids = params->num_ap_ids;
	change_state_data.ap_id_char_size = var_msg_info.ap_id_char_size;
	change_state_data.options_size = var_msg_info.options_strlen +
	    var_msg_info.options_pad_sz;

	if (params->confp != NULL) {
		change_state_data.confirm_callback_id =
		    (unsigned long)params->confp->confirm;
		change_state_data.confirm_appdata_ptr =
		    (unsigned long)params->confp->appdata_ptr;
	} else {
		change_state_data.confirm_callback_id = 0;
		change_state_data.confirm_appdata_ptr = 0;
	}
	if (params->msgp != NULL) {
		change_state_data.msg_callback_id =
		    (unsigned long)params->msgp->message_routine;
		change_state_data.msg_appdata_ptr =
		    (unsigned long)params->msgp->appdata_ptr;
	} else {
		change_state_data.msg_callback_id = 0;
		change_state_data.msg_appdata_ptr = 0;
	}

	change_state_data.flags = params->flags;
	change_state_data.timeval = params->timeval;
	change_state_data.state_change_cmd = params->state_change;
	if (params->errstring != NULL) {
		change_state_data.error_msg_ctl = RDR_GENERATE_ERR_MSGS;
	} else {
		change_state_data.error_msg_ctl = RDR_DONT_GENERATE_ERR_MSGS;
	}
	change_state_data.retries = params->retries;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &change_state_data, sizeof (rdr_change_state_t));
	bufptr += sizeof (rdr_change_state_t);

	if (var_msg_info.ap_id_sizes != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_sizes,
		    var_msg_info.ap_id_int_size);
		bufptr += var_msg_info.ap_id_int_size;
	}

	if (var_msg_info.ap_id_chars != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_chars,
		    var_msg_info.ap_id_char_size);
		bufptr += var_msg_info.ap_id_char_size;
	}

	if (params->options != NULL) {
		(void) memcpy(bufptr, params->options,
		    var_msg_info.options_strlen);
		bufptr += var_msg_info.options_strlen;
		for (i = 0; i < var_msg_info.options_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.options_pad_sz;
	}

	cleanup_variable_ap_id_info(&var_msg_info);

	return (RDR_OK);
}


/*
 * unpack_change_state_request:
 *
 * Handle unpacking a change state request message.
 */
static int
unpack_change_state_request(change_state_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_variable_message_info_t 	var_msg_info;
	rdr_change_state_t		change_state_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	bufptr = (char *)buf;
	(void) memcpy(&change_state_data, bufptr, sizeof (rdr_change_state_t));
	bufptr += sizeof (rdr_change_state_t);

	/*
	 * handle getting the ap_ids
	 */
	var_msg_info.ap_id_char_size = change_state_data.ap_id_char_size;
	if (get_ap_ids_from_buf((char ***)&(params->ap_ids),
	    change_state_data.num_ap_ids, &var_msg_info, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += var_msg_info.ap_id_int_size;
	bufptr += var_msg_info.ap_id_char_size;

	/*
	 * handle getting the options
	 */
	if (get_string_from_buf(&(params->options),
	    change_state_data.options_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += change_state_data.options_size;

	/*
	 * Set fixed address labels by name
	 */
	params->state_change = (cfga_cmd_t)change_state_data.state_change_cmd;
	params->num_ap_ids = change_state_data.num_ap_ids;

	params->confp = (struct cfga_confirm *)
	    malloc(sizeof (struct cfga_confirm));
	if (params->confp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->confp->confirm using memcpy */
	(void) memcpy((void*)params->confp,
	    &(change_state_data.confirm_callback_id), sizeof (unsigned long));
	params->confp->appdata_ptr =
	    (void*)change_state_data.confirm_appdata_ptr;

	params->msgp = (struct cfga_msg *)malloc(sizeof (struct cfga_msg));
	if (params->msgp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->msgp->message_routine using memcpy */
	(void) memcpy((void*)params->msgp,
	    &(change_state_data.msg_callback_id), sizeof (unsigned long));
	params->msgp->appdata_ptr =
	    (void*)change_state_data.msg_appdata_ptr;

	if (change_state_data.error_msg_ctl == RDR_GENERATE_ERR_MSGS) {
		params->errstring = (char **)malloc(sizeof (char *));
		if (params->errstring == NULL) {
			return (RDR_MEM_ALLOC);
		}
		*(params->errstring) = NULL;
	} else {	/* error_msg_ctl == RDR_DONT_GENERATE_ERR_MSGS */
		params->errstring = NULL;
	}
	params->flags = change_state_data.flags;
	params->timeval = change_state_data.timeval;
	params->retries = change_state_data.retries;

	return (RDR_OK);
}


/*
 * pack_change_state_reply:
 *
 * Handle packing a change state reply message.
 */
static int
pack_change_state_reply(change_state_params_t *params, char **buf,
    int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_change_state_reply_t	change_state_data;
	rdr_variable_message_info_t 	var_msg_info;


	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Set variable length fields (size info)
	 */
	if (find_errstring_sizes(params->errstring, &var_msg_info)) {
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the change_state reply
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_change_state_reply_t);
	*buf_size += var_msg_info.errstring_strlen;
	*buf_size += var_msg_info.errstring_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	change_state_data.errstring_size = var_msg_info.errstring_strlen +
	    var_msg_info.errstring_pad_sz;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &change_state_data,
	    sizeof (rdr_change_state_reply_t));
	bufptr += sizeof (rdr_change_state_reply_t);

	if ((params->errstring != NULL) && (*(params->errstring) != NULL)) {
		(void) memcpy(bufptr, *(params->errstring),
		    var_msg_info.errstring_strlen);
		bufptr += var_msg_info.errstring_strlen;
		for (i = 0; i < var_msg_info.errstring_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.errstring_pad_sz;
	}

	return (RDR_OK);
}


/*
 * unpack_change_state_reply:
 *
 * Handle unpacking a change state reply message.
 */
static int
unpack_change_state_reply(change_state_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_change_state_reply_t	change_state_data;

	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&change_state_data, bufptr,
	    sizeof (rdr_change_state_reply_t));
	bufptr += sizeof (rdr_change_state_reply_t);

	/*
	 * handle getting the errstring
	 */
	params->errstring = (char **)malloc(sizeof (char *));
	if (params->errstring == NULL) {
		return (RDR_MEM_ALLOC);
	}
	if (get_string_from_buf(params->errstring,
	    change_state_data.errstring_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += change_state_data.errstring_size;

	return (RDR_OK);
}


/*
 * pack_private_func_request:
 *
 * Handle packing a private function request message.
 */
static int
pack_private_func_request(private_func_params_t *params, char **buf,
    int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_private_func_t		private_func_data;
	rdr_variable_message_info_t	var_msg_info;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (pack_ap_ids(params->num_ap_ids, params->ap_ids, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}
	if (find_options_sizes(params->options, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}
	if (find_function_sizes(params->function, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the private_func request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_private_func_t);
	*buf_size += var_msg_info.ap_id_int_size;
	*buf_size += var_msg_info.ap_id_char_size;
	*buf_size += var_msg_info.options_strlen;
	*buf_size += var_msg_info.options_pad_sz;
	*buf_size += var_msg_info.function_strlen;
	*buf_size += var_msg_info.function_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	private_func_data.num_ap_ids = params->num_ap_ids;
	private_func_data.ap_id_char_size = var_msg_info.ap_id_char_size;
	private_func_data.options_size = var_msg_info.options_strlen +
	    var_msg_info.options_pad_sz;
	private_func_data.function_size = var_msg_info.function_strlen +
	    var_msg_info.function_pad_sz;

	if (params->confp != NULL) {
		private_func_data.confirm_callback_id =
		    (unsigned long)params->confp->confirm;
		private_func_data.confirm_appdata_ptr =
		    (unsigned long)params->confp->appdata_ptr;
	} else {
		private_func_data.confirm_callback_id = 0;
		private_func_data.confirm_appdata_ptr = 0;
	}
	if (params->msgp != NULL) {
		private_func_data.msg_callback_id =
		    (unsigned long)params->msgp->message_routine;
		private_func_data.msg_appdata_ptr =
		    (unsigned long)params->msgp->appdata_ptr;
	} else {
		private_func_data.msg_callback_id = 0;
		private_func_data.msg_appdata_ptr = 0;
	}

	private_func_data.flags = params->flags;

	if (params->errstring != NULL) {
		private_func_data.error_msg_ctl = RDR_GENERATE_ERR_MSGS;
	} else {
		private_func_data.error_msg_ctl = RDR_DONT_GENERATE_ERR_MSGS;
	}

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &private_func_data, sizeof (rdr_private_func_t));
	bufptr += sizeof (rdr_private_func_t);

	if (var_msg_info.ap_id_sizes != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_sizes,
		    var_msg_info.ap_id_int_size);
		bufptr += var_msg_info.ap_id_int_size;
	}

	if (var_msg_info.ap_id_chars != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_chars,
		    var_msg_info.ap_id_char_size);
		bufptr += var_msg_info.ap_id_char_size;
	}

	if (params->options != NULL) {
		(void) memcpy(bufptr, params->options,
		    var_msg_info.options_strlen);
		bufptr += var_msg_info.options_strlen;
		for (i = 0; i < var_msg_info.options_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.options_pad_sz;
	}

	if (params->function != NULL) {
		(void) memcpy(bufptr, params->function,
		    var_msg_info.function_strlen);
		bufptr += var_msg_info.function_strlen;
		for (i = 0; i < var_msg_info.function_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.function_pad_sz;
	}

	cleanup_variable_ap_id_info(&var_msg_info);

	return (RDR_OK);
}


/*
 * unpack_private_func_request:
 *
 * Handle unpacking a private function request message.
 */
static int
unpack_private_func_request(private_func_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_variable_message_info_t 	var_msg_info;
	rdr_private_func_t		private_func_data;


	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&private_func_data, bufptr, sizeof (rdr_private_func_t));
	bufptr += sizeof (rdr_private_func_t);

	/*
	 * handle getting the ap_ids
	 */
	var_msg_info.ap_id_char_size = private_func_data.ap_id_char_size;
	if (get_ap_ids_from_buf((char ***)&(params->ap_ids),
	    private_func_data.num_ap_ids, &var_msg_info, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += var_msg_info.ap_id_int_size;
	bufptr += var_msg_info.ap_id_char_size;

	/*
	 * handle getting the options and function
	 */
	if (get_string_from_buf(&(params->options),
	    private_func_data.options_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += private_func_data.options_size;

	if (get_string_from_buf(&(params->function),
	    private_func_data.function_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += private_func_data.function_size;

	/*
	 * Set fixed address labels by name
	 */
	params->num_ap_ids = private_func_data.num_ap_ids;

	params->confp = (struct cfga_confirm *)
	    malloc(sizeof (struct cfga_confirm));
	if (params->confp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->confp->confirm using memcpy */
	(void) memcpy((void*)params->confp,
	    &(private_func_data.confirm_callback_id), sizeof (unsigned long));
	params->confp->appdata_ptr =
	    (void*)private_func_data.confirm_appdata_ptr;

	params->msgp = (struct cfga_msg *)malloc(sizeof (struct cfga_msg));
	if (params->msgp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->msgp->message_routine using memcpy */
	(void) memcpy((void*)params->msgp,
	    &(private_func_data.msg_callback_id), sizeof (unsigned long));
	params->msgp->appdata_ptr =
	    (void*)private_func_data.msg_appdata_ptr;

	if (private_func_data.error_msg_ctl == RDR_GENERATE_ERR_MSGS) {
		params->errstring = (char **)malloc(sizeof (char *));
		if (params->errstring == NULL) {
			return (RDR_MEM_ALLOC);
		}
		*(params->errstring) = NULL;
	} else {	/* error_msg_ctl == RDR_DONT_GENERATE_ERR_MSGS */
		params->errstring = NULL;
	}
	params->flags = private_func_data.flags;

	return (RDR_OK);
}


/*
 * pack_private_func_reply:
 *
 * Handle packing a private function reply message.
 */
static int
pack_private_func_reply(private_func_params_t *params, char **buf,
    int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_private_func_reply_t	private_func_data;
	rdr_variable_message_info_t 	var_msg_info;


	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Set variable length fields (size info)
	 */
	if (find_errstring_sizes(params->errstring, &var_msg_info)) {
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the private_func reply
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_private_func_reply_t);
	*buf_size += var_msg_info.errstring_strlen;
	*buf_size += var_msg_info.errstring_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	private_func_data.errstring_size = var_msg_info.errstring_strlen +
	    var_msg_info.errstring_pad_sz;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &private_func_data,
	    sizeof (rdr_private_func_reply_t));
	bufptr += sizeof (rdr_private_func_reply_t);
	if ((params->errstring != NULL) && (*(params->errstring) != NULL)) {
		(void) memcpy(bufptr, *(params->errstring),
		    var_msg_info.errstring_strlen);
		bufptr += var_msg_info.errstring_strlen;
		for (i = 0; i < var_msg_info.errstring_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.errstring_pad_sz;
	}

	return (RDR_OK);
}


/*
 * unpack_private_func_reply:
 *
 * Handle unpacking a private function reply message.
 */
static int
unpack_private_func_reply(private_func_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_private_func_reply_t	private_func_data;

	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&private_func_data, bufptr,
	    sizeof (rdr_private_func_reply_t));
	bufptr += sizeof (rdr_private_func_reply_t);

	/*
	 * handle getting the errstring
	 */
	params->errstring = (char **)malloc(sizeof (char *));
	if (params->errstring == NULL) {
		return (RDR_MEM_ALLOC);
	}
	if (get_string_from_buf(params->errstring,
	    private_func_data.errstring_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += private_func_data.errstring_size;

	return (RDR_OK);
}


/*
 * pack_test_request:
 *
 * Handle packing a test request message.
 */
static int
pack_test_request(test_params_t *params, char **buf, int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_test_t			test_data;
	rdr_variable_message_info_t	var_msg_info;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (pack_ap_ids(params->num_ap_ids, params->ap_ids, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}
	if (find_options_sizes(params->options, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the test request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_test_t);
	*buf_size += var_msg_info.ap_id_int_size;
	*buf_size += var_msg_info.ap_id_char_size;
	*buf_size += var_msg_info.options_strlen;
	*buf_size += var_msg_info.options_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	test_data.num_ap_ids = params->num_ap_ids;
	test_data.ap_id_char_size = var_msg_info.ap_id_char_size;
	test_data.options_size = var_msg_info.options_strlen +
	    var_msg_info.options_pad_sz;

	if (params->msgp != NULL) {
		test_data.msg_callback_id =
		    (unsigned long)params->msgp->message_routine;
		test_data.msg_appdata_ptr =
		    (unsigned long)params->msgp->appdata_ptr;
	} else {
		test_data.msg_callback_id = 0;
		test_data.msg_appdata_ptr = 0;
	}

	test_data.flags = params->flags;

	if (params->errstring != NULL) {
		test_data.error_msg_ctl = RDR_GENERATE_ERR_MSGS;
	} else {
		test_data.error_msg_ctl = RDR_DONT_GENERATE_ERR_MSGS;
	}

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &test_data, sizeof (rdr_test_t));
	bufptr += sizeof (rdr_test_t);

	if (var_msg_info.ap_id_sizes != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_sizes,
		    var_msg_info.ap_id_int_size);
		bufptr += var_msg_info.ap_id_int_size;
	}

	if (var_msg_info.ap_id_chars != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_chars,
		    var_msg_info.ap_id_char_size);
		bufptr += var_msg_info.ap_id_char_size;
	}

	if (params->options != NULL) {
		(void) memcpy(bufptr, params->options,
		    var_msg_info.options_strlen);
		bufptr += var_msg_info.options_strlen;
		for (i = 0; i < var_msg_info.options_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.options_pad_sz;
	}

	cleanup_variable_ap_id_info(&var_msg_info);

	return (RDR_OK);
}


/*
 * unpack_test_request:
 *
 * Handle unpacking a test request message.
 */
static int
unpack_test_request(test_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_variable_message_info_t 	var_msg_info;
	rdr_test_t			test_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	bufptr = (char *)buf;
	(void) memcpy(&test_data, bufptr, sizeof (rdr_test_t));
	bufptr += sizeof (rdr_test_t);

	/*
	 * handle getting the ap_ids
	 */
	var_msg_info.ap_id_char_size = test_data.ap_id_char_size;
	if (get_ap_ids_from_buf((char ***)&(params->ap_ids),
	    test_data.num_ap_ids, &var_msg_info, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += var_msg_info.ap_id_int_size;
	bufptr += var_msg_info.ap_id_char_size;

	/*
	 * handle getting the options
	 */
	if (get_string_from_buf(&(params->options),
	    test_data.options_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += test_data.options_size;

	/*
	 * Set fixed address labels by name
	 */
	params->num_ap_ids = test_data.num_ap_ids;

	params->msgp = (struct cfga_msg *)malloc(sizeof (struct cfga_msg));
	if (params->msgp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->msgp->message_routine using memcpy */
	(void) memcpy((void*)params->msgp,
	    &(test_data.msg_callback_id), sizeof (unsigned long));
	params->msgp->appdata_ptr =
	    (void*)test_data.msg_appdata_ptr;

	if (test_data.error_msg_ctl == RDR_GENERATE_ERR_MSGS) {
		params->errstring = (char **)malloc(sizeof (char *));
		if (params->errstring == NULL) {
			return (RDR_MEM_ALLOC);
		}
		*(params->errstring) = NULL;
	} else {	/* error_msg_ctl == RDR_DONT_GENERATE_ERR_MSGS */
		params->errstring = NULL;
	}
	params->flags = test_data.flags;

	return (RDR_OK);
}


/*
 * pack_test_reply:
 *
 * Handle packing a test reply message.
 */
static int
pack_test_reply(test_params_t *params, char **buf, int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_test_reply_t		test_data;
	rdr_variable_message_info_t 	var_msg_info;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	/*
	 * Set variable length fields (size info)
	 */
	if (find_errstring_sizes(params->errstring, &var_msg_info)) {
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the test reply
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_test_reply_t);
	*buf_size += var_msg_info.errstring_strlen;
	*buf_size += var_msg_info.errstring_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	test_data.errstring_size = var_msg_info.errstring_strlen +
	    var_msg_info.errstring_pad_sz;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &test_data, sizeof (rdr_test_reply_t));
	bufptr += sizeof (rdr_test_reply_t);
	if ((params->errstring != NULL) && (*(params->errstring) != NULL)) {
		(void) memcpy(bufptr, *(params->errstring),
		    var_msg_info.errstring_strlen);
		bufptr += var_msg_info.errstring_strlen;
		for (i = 0; i < var_msg_info.errstring_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.errstring_pad_sz;
	}

	return (RDR_OK);
}


/*
 * unpack_test_reply:
 *
 * Handle unpacking a test reply message.
 */
static int
unpack_test_reply(test_params_t *params, const char *buf)
{
	char			*bufptr;
	rdr_test_reply_t	test_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&test_data, bufptr, sizeof (rdr_test_reply_t));
	bufptr += sizeof (rdr_test_reply_t);

	/*
	 * handle getting the errstring
	 */
	params->errstring = (char **)malloc(sizeof (char *));
	if (params->errstring == NULL) {
		return (RDR_MEM_ALLOC);
	}
	if (get_string_from_buf(params->errstring,
	    test_data.errstring_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += test_data.errstring_size;

	return (RDR_OK);
}


/*
 * pack_list_ext_request:
 *
 * Handle packing a list request message.
 */
static int
pack_list_ext_request(list_ext_params_t *params, char **buf, int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_list_ext_t 			list_ext_data;
	rdr_variable_message_info_t 	var_msg_info;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (pack_ap_ids(params->num_ap_ids, params->ap_ids, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}
	if (find_options_sizes(params->options, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}
	if (find_listopts_sizes(params->listopts, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}


	/*
	 * Collect size info specific to the list_ext request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_list_ext_t);
	*buf_size += var_msg_info.ap_id_int_size;
	*buf_size += var_msg_info.ap_id_char_size;
	*buf_size += var_msg_info.options_strlen;
	*buf_size += var_msg_info.options_pad_sz;
	*buf_size += var_msg_info.listopts_strlen;
	*buf_size += var_msg_info.listopts_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	list_ext_data.num_ap_ids = params->num_ap_ids;
	list_ext_data.ap_id_char_size = var_msg_info.ap_id_char_size;
	list_ext_data.options_size = var_msg_info.options_strlen +
	    var_msg_info.options_pad_sz;
	list_ext_data.listopts_size = var_msg_info.listopts_strlen +
	    var_msg_info.listopts_pad_sz;
	if (params->errstring != NULL) {
		list_ext_data.error_msg_ctl = RDR_GENERATE_ERR_MSGS;
	} else {
		list_ext_data.error_msg_ctl = RDR_DONT_GENERATE_ERR_MSGS;
	}
	if ((params->num_ap_ids != 0) || (params->ap_ids != NULL)) {
		list_ext_data.list_msg_ctl = RDR_LIST_ONLY_PARAM_APS;
	} else {
		list_ext_data.list_msg_ctl = RDR_LIST_ALL_APS;
	}
	list_ext_data.flags = params->flags;
	list_ext_data.permissions = params->permissions;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &list_ext_data, sizeof (rdr_list_ext_t));
	bufptr += sizeof (rdr_list_ext_t);

	if (var_msg_info.ap_id_sizes != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_sizes,
		    var_msg_info.ap_id_int_size);
		bufptr += var_msg_info.ap_id_int_size;
	}

	if (var_msg_info.ap_id_chars != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_chars,
		    var_msg_info.ap_id_char_size);
		bufptr += var_msg_info.ap_id_char_size;
	}

	if (params->options != NULL) {
		(void) memcpy(bufptr, params->options,
		    var_msg_info.options_strlen);
		bufptr += var_msg_info.options_strlen;
		for (i = 0; i < var_msg_info.options_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.options_pad_sz;
	}

	if (params->listopts != NULL) {
		(void) memcpy(bufptr, params->listopts,
		    var_msg_info.listopts_strlen);
		bufptr += var_msg_info.listopts_strlen;
		for (i = 0; i < var_msg_info.listopts_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.listopts_pad_sz;
	}

	cleanup_variable_ap_id_info(&var_msg_info);

	return (RDR_OK);
}


/*
 * unpack_list_ext_request:
 *
 * Handle unpacking a list request message.
 */
static int
unpack_list_ext_request(list_ext_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_variable_message_info_t 	var_msg_info;
	rdr_list_ext_t			list_ext_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	bufptr = (char *)buf;
	(void) memcpy(&list_ext_data, bufptr, sizeof (rdr_list_ext_t));
	bufptr += sizeof (rdr_list_ext_t);

	/*
	 * handle getting the ap_ids
	 */
	var_msg_info.ap_id_char_size = list_ext_data.ap_id_char_size;
	if (get_ap_ids_from_buf(&(params->ap_ids), list_ext_data.num_ap_ids,
	    &var_msg_info, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += var_msg_info.ap_id_int_size;
	bufptr += var_msg_info.ap_id_char_size;

	/*
	 * handle getting the options
	 */
	if (get_string_from_buf(&(params->options),
	    list_ext_data.options_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += list_ext_data.options_size;

	/*
	 * handle getting the listopts
	 */
	if (get_string_from_buf(&(params->listopts),
	    list_ext_data.listopts_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += list_ext_data.listopts_size;

	/*
	 * Set fixed address labels by name
	 */
	params->num_ap_ids = list_ext_data.num_ap_ids;

	params->ap_id_list = (rdr_list_t **)malloc(sizeof (rdr_list_t *));
	if (params->ap_id_list == NULL) {
		return (RDR_MEM_ALLOC);
	}
	*(params->ap_id_list) = NULL;

	params->nlist = (int *)malloc(sizeof (int));
	if (params->nlist == NULL) {
		return (RDR_MEM_ALLOC);
	}
	if (list_ext_data.error_msg_ctl == RDR_GENERATE_ERR_MSGS) {
		params->errstring = (char **)malloc(sizeof (char *));
		if (params->errstring == NULL) {
			return (RDR_MEM_ALLOC);
		}
		*(params->errstring) = NULL;
	} else {	/* error_msg_ctl == RDR_DONT_GENERATE_ERR_MSGS */
	    params->errstring = NULL;
	}
	params->flags = list_ext_data.flags;
	params->permissions = list_ext_data.permissions;

	return (RDR_OK);
}


/*
 * pack_list_ext_reply:
 *
 * Handle packing a list reply message.
 */
static int
pack_list_ext_reply(list_ext_params_t *params, char **buf, int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_list_ext_reply_t		list_ext_data;
	rdr_variable_message_info_t 	var_msg_info;
	int 				list_data_size;


	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Set variable length fields (size info)
	 */
	if (find_errstring_sizes(params->errstring, &var_msg_info)) {
		return (RDR_ERROR);
	}

	if (params->nlist == NULL) {
		list_data_size = 0;
	} else {
		list_data_size = *(params->nlist) * sizeof (rdr_list_t);
	}

	/*
	 * Collect size info specific to the list_ext reply
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_list_ext_reply_t);
	*buf_size += list_data_size;
	*buf_size += var_msg_info.errstring_strlen;
	*buf_size += var_msg_info.errstring_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	list_ext_data.num_ap_ids = (params->nlist) ? *(params->nlist) : 0;
	list_ext_data.errstring_size = var_msg_info.errstring_strlen +
	    var_msg_info.errstring_pad_sz;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &list_ext_data, sizeof (rdr_list_ext_reply_t));
	bufptr += sizeof (rdr_list_ext_reply_t);

	if ((params->ap_id_list != NULL) && (*(params->ap_id_list) != NULL)) {
		(void) memcpy(bufptr, *(params->ap_id_list), list_data_size);
		bufptr += list_data_size;
	} else if (list_data_size) {
		/*
		 * Something is out of sync. We were expecting
		 * some data to copy, but instead we found a
		 * NULL pointer.
		 */
		(void) free((void *)*buf);
		*buf = NULL;
		return (RDR_ERROR);
	}

	if ((params->errstring != NULL) && (*(params->errstring) != NULL)) {
		(void) memcpy(bufptr, *(params->errstring),
		    var_msg_info.errstring_strlen);
		bufptr += var_msg_info.errstring_strlen;
		for (i = 0; i < var_msg_info.errstring_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.errstring_pad_sz;
	}

	return (RDR_OK);
}


/*
 * unpack_list_ext_reply:
 *
 * Handle unpacking a list reply message.
 */
static int
unpack_list_ext_reply(list_ext_params_t *params, const char *buf)
{
	int 			list_data_size;
	char 			*bufptr;
	rdr_list_ext_reply_t	list_ext_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&list_ext_data, bufptr, sizeof (rdr_list_ext_reply_t));
	bufptr += sizeof (rdr_list_ext_reply_t);

	/*
	 * handle getting the ap_id rcfga_list_data_t's.
	 */
	if (list_ext_data.num_ap_ids > 0) {
		params->nlist = (int *)malloc(sizeof (int));
		if (params->nlist == NULL) {
			return (RDR_MEM_ALLOC);
		}
		*(params->nlist) = list_ext_data.num_ap_ids;
		params->ap_id_list = (rdr_list_t **)
		    malloc(sizeof (rdr_list_t *));
		if (params->ap_id_list == NULL) {
			return (RDR_MEM_ALLOC);
		}
		*(params->ap_id_list) = (rdr_list_t *)
		malloc(sizeof (rdr_list_t) * list_ext_data.num_ap_ids);
		if (*(params->ap_id_list) == NULL) {
			return (RDR_MEM_ALLOC);
		}
		list_data_size = list_ext_data.num_ap_ids * sizeof (rdr_list_t);
		(void) memcpy(*(params->ap_id_list), bufptr, list_data_size);
		bufptr += list_data_size;
	}

	/*
	 * handle getting the errstring
	 */
	params->errstring = (char **)malloc(sizeof (char *));
	if (params->errstring == NULL) {
		return (RDR_MEM_ALLOC);
	}
	if (get_string_from_buf(params->errstring,
			list_ext_data.errstring_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += list_ext_data.errstring_size;

	return (RDR_OK);
}


/*
 * pack_help_request:
 *
 * Handle packing a help request message.
 */
static int
pack_help_request(help_params_t *params, char **buf, int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_help_t			help_data;
	rdr_variable_message_info_t	var_msg_info;


	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (pack_ap_ids(params->num_ap_ids, params->ap_ids, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}
	if (find_options_sizes(params->options, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the help request message and
	 * and allocate a buffer
	 */
	*buf_size = sizeof (rdr_help_t);
	*buf_size += var_msg_info.ap_id_int_size;
	*buf_size += var_msg_info.ap_id_char_size;
	*buf_size += var_msg_info.options_strlen;
	*buf_size += var_msg_info.options_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	help_data.num_ap_ids = params->num_ap_ids;
	help_data.ap_id_char_size = var_msg_info.ap_id_char_size;
	help_data.options_size = var_msg_info.options_strlen +
	    var_msg_info.options_pad_sz;

	if (params->msgp != NULL) {
		help_data.msg_callback_id =
		    (unsigned long)params->msgp->message_routine;
		help_data.msg_appdata_ptr =
		    (unsigned long)params->msgp->appdata_ptr;
	} else {
		help_data.msg_callback_id = 0;
		help_data.msg_appdata_ptr = 0;
	}

	help_data.flags = params->flags;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &help_data, sizeof (rdr_help_t));
	bufptr += sizeof (rdr_help_t);

	if (var_msg_info.ap_id_sizes != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_sizes,
		    var_msg_info.ap_id_int_size);
		bufptr += var_msg_info.ap_id_int_size;
	}

	if (var_msg_info.ap_id_chars != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_chars,
		    var_msg_info.ap_id_char_size);
		bufptr += var_msg_info.ap_id_char_size;
	}

	if (params->options != NULL) {
		(void) memcpy(bufptr, params->options,
		    var_msg_info.options_strlen);
		bufptr += var_msg_info.options_strlen;
		for (i = 0; i < var_msg_info.options_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += var_msg_info.options_pad_sz;
	}

	cleanup_variable_ap_id_info(&var_msg_info);

	return (RDR_OK);
}


/*
 * unpack_help_request:
 *
 * Handle unpacking a help request message.
 */
static int
unpack_help_request(help_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_variable_message_info_t 	var_msg_info;
	rdr_help_t			help_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	bufptr = (char *)buf;
	(void) memcpy(&help_data, bufptr, sizeof (rdr_help_t));
	bufptr += sizeof (rdr_help_t);

	/*
	 * handle getting the ap_ids
	 */
	var_msg_info.ap_id_char_size = help_data.ap_id_char_size;
	if (get_ap_ids_from_buf((char ***)&(params->ap_ids),
	    help_data.num_ap_ids, &var_msg_info, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += var_msg_info.ap_id_int_size;
	bufptr += var_msg_info.ap_id_char_size;

	/*
	 * handle getting the options
	 */
	if (get_string_from_buf(&(params->options),
	    help_data.options_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += help_data.options_size;

	/*
	 * Set fixed address labels by name
	 */
	params->num_ap_ids = help_data.num_ap_ids;

	params->msgp = (struct cfga_msg *)malloc(sizeof (struct cfga_msg));
	if (params->msgp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->msgp->message_routine using memcpy */
	(void) memcpy((void*)params->msgp, &(help_data.msg_callback_id),
	    sizeof (unsigned long));

	params->msgp->appdata_ptr = (void*)help_data.msg_appdata_ptr;
	params->flags = help_data.flags;

	return (RDR_OK);
}


/*
 * pack_ap_id_cmp_request:
 *
 * Handle packing an attachment point comparison request message.
 */
static int
pack_ap_id_cmp_request(ap_id_cmp_params_t *params, char **buf, int *buf_size)
{
	int			i;
	char			*bufptr;
	rdr_ap_id_cmp_t		ap_id_cmp_data;
	int			ap_id1_strlen;
	int			ap_id1_pad_sz;
	int			ap_id2_strlen;
	int			ap_id2_pad_sz;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (params->ap_log_id1 != NULL) {
		ap_id1_strlen = strlen(params->ap_log_id1) + 1;
		ap_id1_pad_sz = RDR_ALIGN_64_BIT -
		    (ap_id1_strlen % RDR_ALIGN_64_BIT);
	} else {
		ap_id1_strlen = 0;
		ap_id1_pad_sz = 0;
	}

	if (params->ap_log_id2 != NULL) {
		ap_id2_strlen = strlen(params->ap_log_id2) + 1;
		ap_id2_pad_sz = RDR_ALIGN_64_BIT -
		    (ap_id2_strlen % RDR_ALIGN_64_BIT);
	} else {
		ap_id2_strlen = 0;
		ap_id2_pad_sz = 0;
	}

	/*
	 * Collect size info specific to the ap id compare request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_ap_id_cmp_t);
	*buf_size += ap_id1_strlen;
	*buf_size += ap_id1_pad_sz;
	*buf_size += ap_id2_strlen;
	*buf_size += ap_id2_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	ap_id_cmp_data.ap_id1_size = ap_id1_strlen + ap_id1_pad_sz;
	ap_id_cmp_data.ap_id2_size = ap_id2_strlen + ap_id2_pad_sz;


	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &ap_id_cmp_data, sizeof (rdr_ap_id_cmp_t));
	bufptr += sizeof (rdr_ap_id_cmp_t);

	if (params->ap_log_id1 != NULL) {
		(void) memcpy(bufptr, params->ap_log_id1, ap_id1_strlen);
		bufptr += ap_id1_strlen;
		for (i = 0; i < ap_id1_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += ap_id1_pad_sz;
	}

	if (params->ap_log_id2 != NULL) {
		(void) memcpy(bufptr, params->ap_log_id2, ap_id2_strlen);
		bufptr += ap_id2_strlen;
		for (i = 0; i < ap_id2_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += ap_id2_pad_sz;
	}

	return (RDR_OK);
}


/*
 * unpack_ap_id_cmp_request:
 *
 * Handle unpacking an attachment point comparison request message.
 */
static int
unpack_ap_id_cmp_request(ap_id_cmp_params_t *params, const char *buf)
{
	char			*bufptr;
	rdr_ap_id_cmp_t		ap_id_cmp_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&ap_id_cmp_data, bufptr, sizeof (rdr_ap_id_cmp_t));
	bufptr += sizeof (rdr_ap_id_cmp_t);

	/*
	 * handle getting the cmp ap ids
	 */
	if (get_string_from_buf(&(params->ap_log_id1),
	    ap_id_cmp_data.ap_id1_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += ap_id_cmp_data.ap_id1_size;

	if (get_string_from_buf(&(params->ap_log_id2),
	    ap_id_cmp_data.ap_id2_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += ap_id_cmp_data.ap_id2_size;

	return (RDR_OK);
}


/*
 * pack_abort_cmd_request:
 *
 * Handle packing an abort request message.
 */
static int
pack_abort_cmd_request(abort_cmd_params_t *params, char **buf, int *buf_size)
{
	rdr_abort_cmd_t		abort_cmd_data;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the abort cmd request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_abort_cmd_t);

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed session identifier
	 */
	abort_cmd_data.session_id = params->session_id;

	/*
	 * Copy information using memcpy
	 */
	(void) memcpy(*buf, &abort_cmd_data, sizeof (rdr_abort_cmd_t));

	return (RDR_OK);
}


/*
 * unpack_abort_cmd_request:
 *
 * Handle unpacking an abort request message.
 */
static int
unpack_abort_cmd_request(abort_cmd_params_t *params, const char *buf)
{
	rdr_abort_cmd_t		*abort_cmd_datap;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	/* LINTED Pointer Cast Alignment Warning */
	abort_cmd_datap = (rdr_abort_cmd_t *)buf;

	/*
	 * copy out the session information
	 */

	params->session_id = abort_cmd_datap->session_id;

	return (RDR_OK);
}


/*
 * pack_confirm_request:
 *
 * Handle packing a confirm callback request.
 */
static int
pack_confirm_request(confirm_callback_params_t *params, char **buf,
    int *buf_size)
{
	int				i;
	char				*bufptr;
	rdr_confirm_callback_t		confirm_callback_data;
	int 				message_strlen;
	int 				message_pad_sz;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (params->message != NULL) {
		message_strlen = strlen(params->message) + 1;
		message_pad_sz = RDR_ALIGN_64_BIT -
		    (message_strlen % RDR_ALIGN_64_BIT);
	} else {
		message_strlen = 0;
		message_pad_sz = 0;
	}


	/*
	 * Collect size info specific to the confirm callback request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_confirm_callback_t);
	*buf_size += message_strlen;
	*buf_size += message_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	if (params->confp != NULL) {
		confirm_callback_data.confirm_callback_id =
		    (unsigned long)params->confp->confirm;
		confirm_callback_data.appdata_ptr =
		    (unsigned long)params->confp->appdata_ptr;
	} else {
		confirm_callback_data.confirm_callback_id = 0;
		confirm_callback_data.appdata_ptr = 0;
	}
	confirm_callback_data.message_size = message_strlen + message_pad_sz;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;
	(void) memcpy(bufptr, &confirm_callback_data,
	    sizeof (rdr_confirm_callback_t));
	bufptr += sizeof (rdr_confirm_callback_t);

	if (params->message != NULL) {
		(void) memcpy(bufptr, params->message, message_strlen);
		bufptr += message_strlen;
		for (i = 0; i < message_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += message_pad_sz;
	}

	return (RDR_OK);
}


/*
 * unpack_confirm_request:
 *
 * Handle unpacking a confirm callback request.
 */
static int
unpack_confirm_request(confirm_callback_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_confirm_callback_t		confirm_callback_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&confirm_callback_data, bufptr,
	    sizeof (rdr_confirm_callback_t));
	bufptr += sizeof (rdr_confirm_callback_t);

	/*
	 * handle getting the message text
	 */
	if (get_string_from_buf(&(params->message),
	    confirm_callback_data.message_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += confirm_callback_data.message_size;

	/*
	 * Set fixed address labels by name
	 */
	params->confp = (struct cfga_confirm *)
	    malloc(sizeof (struct cfga_confirm));
	if (params->confp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->confp->confirm using memcpy */
	(void) memcpy((void*)params->confp,
	    &(confirm_callback_data.confirm_callback_id),
	    sizeof (unsigned long));

	params->confp->appdata_ptr =
	    (void*)confirm_callback_data.appdata_ptr;

	return (RDR_OK);
}


/*
 * pack_confirm_reply:
 *
 * Handle packing a confirm callback reply.
 */
static int
pack_confirm_reply(confirm_callback_params_t *params, char **buf, int *buf_size)
{
	char				*bufptr;
	rdr_confirm_callback_reply_t	confirm_callback_data;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the confirm callback reply
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (confirm_callback_params_t);
	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	if (params->confp != NULL) {
		confirm_callback_data.confirm_callback_id =
		    (unsigned long)params->confp->confirm;
		confirm_callback_data.appdata_ptr =
		    (unsigned long)params->confp->appdata_ptr;
	} else {
		confirm_callback_data.confirm_callback_id = 0;
		confirm_callback_data.appdata_ptr = 0;
	}
	confirm_callback_data.response = params->response;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &confirm_callback_data,
	    sizeof (rdr_confirm_callback_reply_t));

	return (RDR_OK);
}


/*
 * unpack_confirm_reply:
 *
 * Handle unpacking a confirm callback reply.
 */
static int
unpack_confirm_reply(confirm_callback_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_confirm_callback_reply_t	confirm_callback_data;

	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&confirm_callback_data, bufptr,
	    sizeof (rdr_confirm_callback_reply_t));
	bufptr += sizeof (confirm_callback_params_t);

	/*
	 * Set fixed address labels by name
	 */
	params->confp = (struct cfga_confirm *)
	    malloc(sizeof (struct cfga_confirm));
	if (params->confp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->confp->confirm using memcpy */
	(void) memcpy((void*)params->confp,
	    &(confirm_callback_data.confirm_callback_id),
	    sizeof (unsigned long));

	params->confp->appdata_ptr =
	    (void*)confirm_callback_data.appdata_ptr;
	params->response = confirm_callback_data.response;

	return (RDR_OK);
}


/*
 * pack_message_request:
 *
 * Handle packing a message callback request.
 */
static int
pack_message_request(msg_callback_params_t *params, char **buf, int *buf_size)
{
	int			i;
	char			*bufptr;
	rdr_msg_callback_t	msg_callback_data;
	int			message_strlen;
	int			message_pad_sz;

	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (params->message != NULL) {
		message_strlen = strlen(params->message) + 1;
		message_pad_sz = RDR_ALIGN_64_BIT -
		    (message_strlen % RDR_ALIGN_64_BIT);
	} else {
		message_strlen = 0;
		message_pad_sz = 0;
	}


	/*
	 * Collect size info specific to the message callback request
	 * message and allocate a buffer
	 */
	*buf_size = sizeof (rdr_msg_callback_t);
	*buf_size += message_strlen;
	*buf_size += message_pad_sz;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name
	 */
	if (params->msgp != NULL) {
		msg_callback_data.msg_callback_id =
		    (unsigned long)params->msgp->message_routine;
		msg_callback_data.appdata_ptr =
		    (unsigned long)params->msgp->appdata_ptr;
	} else {
		msg_callback_data.msg_callback_id = 0;
		msg_callback_data.appdata_ptr = 0;
	}
	msg_callback_data.message_size = message_strlen + message_pad_sz;

	/*
	 * Set variable information using memcpy
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &msg_callback_data, sizeof (rdr_msg_callback_t));
	bufptr += sizeof (rdr_msg_callback_t);

	if (params->message != NULL) {
		(void) memcpy(bufptr, params->message, message_strlen);
		bufptr += message_strlen;
		for (i = 0; i < message_pad_sz; i++) {
			bufptr[i] = 0;
		}
		bufptr += message_pad_sz;
	}

	return (RDR_OK);
}


/*
 * unpack_message_request:
 *
 * Handle unpacking a message callback request.
 */
static int
unpack_message_request(msg_callback_params_t *params, const char *buf)
{
	char			*bufptr;
	rdr_msg_callback_t	msg_callback_data;

	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&msg_callback_data, bufptr, sizeof (rdr_msg_callback_t));
	bufptr += sizeof (rdr_msg_callback_t);

	/*
	 * handle getting the message text
	 */
	if (get_string_from_buf(&(params->message),
	    msg_callback_data.message_size, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += msg_callback_data.message_size;

	/*
	 * Set fixed address labels by name
	 */
	params->msgp = (struct cfga_msg *)malloc(sizeof (struct cfga_msg));
	if (params->msgp == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/* set params->msgp->message_routine using memcpy */
	(void) memcpy((void*)params->msgp, &(msg_callback_data.msg_callback_id),
	    sizeof (unsigned long));

	params->msgp->appdata_ptr = (void*)msg_callback_data.appdata_ptr;

	return (RDR_OK);
}

/*
 * pack_rsrc_info_request:
 *
 * Handle packing a resource info request.
 */
static int
pack_rsrc_info_request(rsrc_info_params_t *params, char **buf, int *buf_size)
{
	char				*bufptr;
	rdr_rsrc_info_t			rsrc_info_data;
	rdr_variable_message_info_t 	var_msg_info;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	/*
	 * Set variable length fields and make a call to partially
	 * pack it.
	 */
	if (pack_ap_ids(params->num_ap_ids, params->ap_ids, &var_msg_info)) {
		cleanup_variable_ap_id_info(&var_msg_info);
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the resource info request
	 * message and allocate a buffer.
	 */
	*buf_size = sizeof (rdr_rsrc_info_t);
	*buf_size += var_msg_info.ap_id_int_size;
	*buf_size += var_msg_info.ap_id_char_size;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name.
	 */
	rsrc_info_data.num_ap_ids = params->num_ap_ids;
	rsrc_info_data.ap_id_char_size = var_msg_info.ap_id_char_size;
	rsrc_info_data.flags = params->flags;

	/*
	 * Set variable information using memcpy.
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &rsrc_info_data, sizeof (rdr_rsrc_info_t));
	bufptr += sizeof (rdr_rsrc_info_t);

	if (var_msg_info.ap_id_sizes != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_sizes,
		    var_msg_info.ap_id_int_size);
		bufptr += var_msg_info.ap_id_int_size;
	}

	if (var_msg_info.ap_id_chars != NULL) {
		(void) memcpy(bufptr, var_msg_info.ap_id_chars,
		    var_msg_info.ap_id_char_size);
		bufptr += var_msg_info.ap_id_char_size;
	}

	cleanup_variable_ap_id_info(&var_msg_info);

	return (RDR_OK);
}


/*
 * unpack_rsrc_info_request:
 *
 * Handle unpacking a resource info request message.
 */
static int
unpack_rsrc_info_request(rsrc_info_params_t *params, const char *buf)
{
	char				*bufptr;
	rdr_variable_message_info_t 	var_msg_info;
	rdr_rsrc_info_t			rsrc_info_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	(void) memset(&var_msg_info, 0, sizeof (rdr_variable_message_info_t));

	bufptr = (char *)buf;
	(void) memcpy(&rsrc_info_data, bufptr, sizeof (rdr_rsrc_info_t));
	bufptr += sizeof (rdr_rsrc_info_t);

	/*
	 * Handle getting the ap_ids.
	 */
	var_msg_info.ap_id_char_size = rsrc_info_data.ap_id_char_size;
	if (get_ap_ids_from_buf(&(params->ap_ids), rsrc_info_data.num_ap_ids,
	    &var_msg_info, bufptr)) {
		return (RDR_ERROR);
	}
	bufptr += var_msg_info.ap_id_int_size;
	bufptr += var_msg_info.ap_id_char_size;

	/*
	 * Set fixed address labels by name.
	 */
	params->num_ap_ids = rsrc_info_data.num_ap_ids;
	params->flags = rsrc_info_data.flags;

	return (RDR_OK);
}


/*
 * pack_rsrc_info_reply:
 *
 * Handle packing a resource info reply message.
 */
static int
pack_rsrc_info_reply(rsrc_info_params_t *params, char **buf, int *buf_size,
    int encoding)
{
	char				*bufptr;
	rdr_rsrc_info_reply_t		rsrc_info_data;
	int				pack_status;
	caddr_t				rsrc_info_bufp = NULL;
	size_t 				rsrc_info_size;


	if ((params == NULL) || (buf == NULL) || (buf_size == NULL)) {
		return (RDR_ERROR);
	}

	/*
	 * Pack snapshot handle data.
	 */
	pack_status = ri_pack(params->hdl, &rsrc_info_bufp, &rsrc_info_size,
	    encoding);
	if (pack_status != 0) {
		return (RDR_ERROR);
	}

	/*
	 * Collect size info specific to the rsrc_info reply message
	 * and allocate a buffer.
	 */
	*buf_size = sizeof (rdr_rsrc_info_reply_t);
	*buf_size += rsrc_info_size;

	*buf = (char *)malloc(*buf_size);
	if (*buf == NULL) {
		free(rsrc_info_bufp);
		return (RDR_MEM_ALLOC);
	}

	/*
	 * Set fixed address labels by name.
	 */
	rsrc_info_data.packed_hdl_size = rsrc_info_size;

	/*
	 * Set variable information using memcpy.
	 */
	bufptr = *buf;

	(void) memcpy(bufptr, &rsrc_info_data, sizeof (rdr_rsrc_info_reply_t));
	bufptr += sizeof (rdr_rsrc_info_reply_t);

	if (rsrc_info_bufp) {
		(void) memcpy(bufptr, rsrc_info_bufp, rsrc_info_size);
		free(rsrc_info_bufp);
	}

	return (RDR_OK);
}


/*
 * unpack_rsrc_info_reply:
 *
 * Handle unpacking a resource info reply message.
 */
static int
unpack_rsrc_info_reply(rsrc_info_params_t *params, const char *buf)
{
	int			unpack_status;
	char 			*bufptr;
	rdr_rsrc_info_reply_t	rsrc_info_data;


	if ((params == NULL) || (buf == NULL)) {
		return (RDR_ERROR);
	}

	bufptr = (char *)buf;
	(void) memcpy(&rsrc_info_data, bufptr, sizeof (rdr_rsrc_info_reply_t));
	bufptr += sizeof (rdr_rsrc_info_reply_t);

	/*
	 * Unpack buf into resource info handle.
	 */
	unpack_status = ri_unpack(bufptr, rsrc_info_data.packed_hdl_size,
	    &params->hdl);

	return ((unpack_status == 0) ? RDR_OK : RDR_ERROR);
}


/*
 * pack_ap_ids:
 *
 * Pack a list of attachment point identifiers into a single buffer.
 * This buffer is stored in the specified rdr_variable_message_info_t
 * and is padded to be 64-bit aligned.
 */
static int
pack_ap_ids(int num_ap_ids, char *const *ap_ids,
    rdr_variable_message_info_t *var_msg_info)
{
	int	i;
	int	ap_id_pad_sz;
	char	*bufptr;


	if (var_msg_info == NULL) {
		return (RDR_ERROR);
	}

	/*
	 * NULL is a valid value for ap_ids in the list_ext
	 * case. For list_ext, no specified attachment points
	 * indicates that _all_ attachment points should be
	 * displayed. However, if ap_ids is NULL, num_ap_ids
	 * should be 0.
	 */
	if ((ap_ids == NULL) && (num_ap_ids != 0)) {
		num_ap_ids = 0;
	}

	var_msg_info->ap_id_int_size = sizeof (int) * num_ap_ids;
	if (num_ap_ids > 0) {
		var_msg_info->ap_id_sizes = (int *)malloc(sizeof (int) *
		    var_msg_info->ap_id_int_size);
		if (var_msg_info->ap_id_sizes == NULL) {
			return (RDR_MEM_ALLOC);
		}
	}
	for (i = 0; i < num_ap_ids; i++) {
		if (ap_ids[i] != NULL) {
			var_msg_info->ap_id_sizes[i] = strlen(ap_ids[i]) + 1;
			var_msg_info->ap_id_char_size +=
			    var_msg_info->ap_id_sizes[i];
		}
	}
	if (var_msg_info->ap_id_char_size > 0) {
		ap_id_pad_sz = RDR_ALIGN_64_BIT -
		    (var_msg_info->ap_id_char_size % RDR_ALIGN_64_BIT);
		var_msg_info->ap_id_char_size += ap_id_pad_sz;
		var_msg_info->ap_id_chars = (char *)
		    malloc(var_msg_info->ap_id_char_size);
		if (var_msg_info->ap_id_chars == NULL) {
			return (RDR_MEM_ALLOC);
		}

		bufptr = var_msg_info->ap_id_chars;
		for (i = 0; i < num_ap_ids; i++) {
			(void) memcpy(bufptr, ap_ids[i],
			    var_msg_info->ap_id_sizes[i]);
			bufptr += var_msg_info->ap_id_sizes[i];
		}
		for (i = 0; i < ap_id_pad_sz; i++) {
			bufptr[i] = 0;
		}
	} else {
		ap_id_pad_sz = 0;
	}

	return (RDR_OK);
}


/*
 * unpack_ap_ids:
 *
 * Unpack a buffer containing a concatenation of a list of
 * attachment point identifiers. The resulting list of strings
 * are stored in an array in the specified rdr_variable_message_info_t.
 */
static int
unpack_ap_ids(int num_ap_ids, char **ap_ids, const char *buf,
    rdr_variable_message_info_t *var_msg_info)
{
	int	i;
	int	ap_id_size;
	int	chars_copied;
	char	*bufptr;


	if ((ap_ids == NULL) || (buf == NULL) || (var_msg_info == NULL)) {
		return (RDR_ERROR);
	}
	bufptr = (char *)buf;

	var_msg_info->ap_id_int_size = sizeof (int) * num_ap_ids;
	if (num_ap_ids > 0) {
		var_msg_info->ap_id_sizes = (int *)
		malloc(sizeof (int) * var_msg_info->ap_id_int_size);
		if (var_msg_info->ap_id_sizes == NULL) {
			return (RDR_MEM_ALLOC);
		}
		(void) memcpy(var_msg_info->ap_id_sizes, bufptr,
		    var_msg_info->ap_id_int_size);
	}
	bufptr += var_msg_info->ap_id_int_size;

	chars_copied = 0;
	for (i = 0; i < num_ap_ids; i++) {
		ap_id_size = var_msg_info->ap_id_sizes[i];
		if (ap_id_size <= 0) {
			continue;
		}
		if ((chars_copied + ap_id_size) >
			var_msg_info->ap_id_char_size) {
			return (RDR_ERROR);
		}
		ap_ids[i] = (char *)malloc(ap_id_size);
		if (ap_ids[i] == NULL) {
			return (RDR_MEM_ALLOC);
		}
		(void) memcpy(ap_ids[i], bufptr, ap_id_size);
		bufptr += ap_id_size;
		chars_copied += ap_id_size;
	}
	return (RDR_OK);
}


/*
 * find_options_sizes:
 *
 * Determine the size of a specified option string. The information
 * is stored in the specified rdr_variable_message_info_t.
 */
static int
find_options_sizes(char *options, rdr_variable_message_info_t *var_msg_info)
{
	if (var_msg_info == NULL) {
		return (RDR_ERROR);
	}
	if (options != NULL) {
		var_msg_info->options_strlen = strlen(options) + 1;
		var_msg_info->options_pad_sz = RDR_ALIGN_64_BIT -
		    (var_msg_info->options_strlen % RDR_ALIGN_64_BIT);
	} else {
		var_msg_info->options_strlen = 0;
		var_msg_info->options_pad_sz = 0;
	}
	return (RDR_OK);
}


/*
 * find_listopts_sizes:
 *
 * Determine the size of a specified list option string. The information
 * is stored in the specified rdr_variable_message_info_t.
 */
static int
find_listopts_sizes(char *listopts, rdr_variable_message_info_t *var_msg_info)
{
	if (var_msg_info == NULL) {
		return (RDR_ERROR);
	}
	if (listopts != NULL) {
		var_msg_info->listopts_strlen = strlen(listopts) + 1;
		var_msg_info->listopts_pad_sz = RDR_ALIGN_64_BIT -
		    (var_msg_info->listopts_strlen % RDR_ALIGN_64_BIT);
	} else {
		var_msg_info->listopts_strlen = 0;
		var_msg_info->listopts_pad_sz = 0;
	}
	return (RDR_OK);
}


/*
 * find_function_size:
 *
 * Determine the size of a specified private function string. The
 * information is stored in the specified rdr_variable_message_info_t.
 */
static int
find_function_sizes(char *function, rdr_variable_message_info_t *var_msg_info)
{
	if (var_msg_info == NULL) {
		return (RDR_ERROR);
	}
	if (function != NULL) {
		var_msg_info->function_strlen = strlen(function) + 1;
		var_msg_info->function_pad_sz = RDR_ALIGN_64_BIT -
		    (var_msg_info->function_strlen % RDR_ALIGN_64_BIT);
	} else {
		var_msg_info->function_strlen = 0;
		var_msg_info->function_pad_sz = 0;
	}
	return (RDR_OK);
}


/*
 * find_errstring_sizes:
 *
 * Determine the size of a specified error string. The information
 * is stored in the specified rdr_variable_message_info_t.
 */
static int
find_errstring_sizes(char **errstring,
    rdr_variable_message_info_t *var_msg_info)
{
	if ((errstring != NULL) && (*errstring != NULL)) {
		var_msg_info->errstring_strlen = strlen(*errstring) + 1;
		var_msg_info->errstring_pad_sz = RDR_ALIGN_64_BIT -
		    (var_msg_info->errstring_strlen % RDR_ALIGN_64_BIT);
	} else {
		var_msg_info->errstring_strlen = 0;
		var_msg_info->errstring_pad_sz = 0;
	}
	return (RDR_OK);
}


/*
 * get_ap_ids_from_buf:
 *
 * Unpack a buffer containing a concatenation of a list of attachment
 * point identifiers. An appropriately sized buffer is allocated and
 * the resulting list of strings are stored in an array in the specified
 * rdr_variable_message_info_t.
 */
static int
get_ap_ids_from_buf(char ***ap_id_ptr, int num_ap_ids,
    rdr_variable_message_info_t *var_msg_info, const char *buf)
{
	if ((ap_id_ptr == NULL) || (buf == NULL) || (var_msg_info == NULL)) {
		return (RDR_ERROR);
	}
	if (num_ap_ids > 0) {
		*ap_id_ptr = (char **)malloc(sizeof (char *) * num_ap_ids);
		if (*ap_id_ptr == NULL) {
			return (RDR_MEM_ALLOC);
		}
		if (unpack_ap_ids(num_ap_ids, *ap_id_ptr, buf, var_msg_info)) {
			cleanup_variable_ap_id_info(var_msg_info);
			return (RDR_ERROR);
		}

	} else if (num_ap_ids < 0) {
		return (RDR_ERROR);
	}

	cleanup_variable_ap_id_info(var_msg_info);

	return (RDR_OK);
}


/*
 * get_string_from_buf:
 *
 * Copy a string to a new buffer. Memory is allocated for the
 * new buffer and the original string is copied to the new buffer.
 * This is primarily used when a string is located in a packed
 * buffer that will eventually get deallocated.
 */
static int
get_string_from_buf(char **stringptr, int strsize, const char *buf)
{
	if (buf == NULL) {
		return (RDR_ERROR);
	}

	/*
	 * A stringptr of NULL is a valid value. The errstring param
	 * in an rconfig_xxx call is valid and is passed to this
	 * function. For example, see errstring in the call to this
	 * function in unpack_change_state_reply.
	 */
	if (stringptr != NULL) {
		if (strsize > 0) {
			*stringptr = (char *)malloc(strsize);
			if (*stringptr == NULL) {
				return (RDR_MEM_ALLOC);
			}
			(void) memcpy(*stringptr, buf, strsize);
		} else if (strsize == 0) {
			*stringptr = NULL;
		} else if (strsize < 0) {
			*stringptr = NULL;
			return (RDR_ERROR);
		}
	}
	return (RDR_OK);
}


/*
 * cleanup_ap_ids:
 *
 * Deallocate the specified array of attachment point identifiers.
 */
static int
cleanup_ap_ids(int num_ap_ids, char ** ap_ids)
{
	int	i;

	if (ap_ids == NULL) {
		return (RDR_ERROR);
	}
	for (i = 0; i < num_ap_ids; i++) {
		if (ap_ids[i] != NULL) {
			free((void *)ap_ids[i]);
			ap_ids[i] = NULL;
		}
	}
	return (RDR_OK);
}


/*
 * cleanup_errstring:
 *
 * Deallocate the specified error string.
 */
static int
cleanup_errstring(char **errstring)
{
	if (errstring) {
		if (*errstring) {
			free((void *)*errstring);
		}
		free((void *)errstring);
		errstring = NULL;
	}

	return (RDR_OK);
}


/*
 * cleanup_variable_ap_id_info:
 *
 * Deallocate the ap_id information from the specified
 * rdr_variable_message_info_t.
 */
static void
cleanup_variable_ap_id_info(rdr_variable_message_info_t *var_msg_info)
{
	if (var_msg_info != NULL) {
		if (var_msg_info->ap_id_sizes != NULL) {
			free((void *)var_msg_info->ap_id_sizes);
			var_msg_info->ap_id_sizes = NULL;
		}
		if (var_msg_info->ap_id_chars != NULL) {
			free((void *)var_msg_info->ap_id_chars);
			var_msg_info->ap_id_chars = NULL;
		}
	}
}

/*
 * load_libdscp:
 *
 * Try to dynamically link with libdscp.
 *
 * Returns:	0 if libdscp not available,
 *		1 if libdscp is available.
 */
static int
load_libdscp(libdscp_t *libdscp)
{
	int		len;
	void		*lib;
	static char	platform[100];
	static char	pathname[MAXPATHLEN];

	/*
	 * Only try to load libdscp once.  Use the saved
	 * status in the libdscp interface to know the
	 * results of previous attempts.
	 */
	if (libdscp->status == LIBDSCP_AVAILABLE) {
		return (1);
	}
	if (libdscp->status == LIBDSCP_UNAVAILABLE) {
		return (0);
	}

	/*
	 * Construct a platform specific pathname for libdscp.
	 */
	len = sysinfo(SI_PLATFORM, platform, sizeof (platform));
	if ((len < 0) || (len > sizeof (platform))) {
		return (0);
	}
	len = snprintf(pathname, MAXPATHLEN, LIBDSCP_PATH, platform);
	if (len >= MAXPATHLEN) {
		libdscp->status = LIBDSCP_UNAVAILABLE;
		return (0);
	}

	/*
	 * Try dynamically loading libdscp.
	 */
	if ((lib = dlopen(pathname, RTLD_LAZY)) == NULL) {
		libdscp->status = LIBDSCP_UNAVAILABLE;
		return (0);
	}

	/*
	 * Try to resolve all the symbols.
	 */
	libdscp->bind = (int (*)(int, int, int))dlsym(lib, LIBDSCP_BIND);
	libdscp->secure = (int (*)(int, int))dlsym(lib, LIBDSCP_SECURE);
	libdscp->auth = (int (*)(int, struct sockaddr *, int))dlsym(lib,
	    LIBDSCP_AUTH);

	if ((libdscp->bind == NULL) ||
	    (libdscp->secure == NULL) ||
	    (libdscp->auth == NULL)) {
		(void) dlclose(lib);
		libdscp->status = LIBDSCP_UNAVAILABLE;
		return (0);
	}

	/*
	 * Success.
	 * Update the status to indicate libdscp is available.
	 */
	libdscp->status = LIBDSCP_AVAILABLE;
	return (1);
}