/*
 * Copyright 1993, 1994 by Ulrich Khn. All rights reserved.
 *
 * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT
 * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN
 * RISK.
 */

/*
 * File : rpc.c
 *        some utility functions for rpc
 */


#include "types.h"
#include "xdr.h"
#include "rpc.h"




/* a library-wide null authentificator */
struct opaque_auth null_auth = { AUTH_NULL, NULL, 0 };



long
xdr_size_opaque_auth(opaque_auth *ap)
{
	long r = 2*sizeof(u_long);

	r += ap->len;
	return r;
}


bool_t
xdr_opaque_auth(xdrs *x, opaque_auth *ap)
{
	xdr_enum(x, &ap->flavor);
	return xdr_opaque(x, &ap->data, &ap->len, MAX_AUTH_BYTES);
}


long
xdr_size_rpc_msg(rpc_msg *m)
{
	long len;

	len = sizeof(u_long)+sizeof(u_long);   /* xid, mtype */
	switch (m->mtype)
	{
		case CALL:
			len += 4*sizeof(u_long);
			len += 2*(sizeof(u_long)+sizeof(u_long));  /* two opaque_auth fields */
			if ((m->cbody.cred.len > MAX_AUTH_BYTES) ||
			          (m->cbody.verf.len > MAX_AUTH_BYTES))
				return 0;
			len += m->cbody.cred.len;
			len += m->cbody.verf.len;
			break;
		case REPLY:
			len += sizeof(u_long);
			switch (m->rbody.rb_stat)
			{
				case MSG_ACCEPTED:
					len += sizeof(u_long)+sizeof(u_long);  /* opaque_auth */
					if (m->rbody.rb_arpl.ar_verf.len > MAX_AUTH_BYTES)
						return 0;
					len += m->rbody.rb_arpl.ar_verf.len;
					len += sizeof(u_long);
					if (PROG_MISMATCH == m->rbody.rb_arpl.ar_stat)
						len += 2*sizeof(u_long);
					break;
				case MSG_DENIED:
					len += sizeof(u_long);
					if (RPC_MISMATCH == m->rbody.rb_rrpl.rr_stat)
						len += 2*sizeof(u_long);
					else if (AUTH_ERROR == m->rbody.rb_rrpl.rr_stat)
						len += sizeof(u_long);
					break;
			}
			break;
	}
	return len;
}


bool_t
xdr_rpc_msg(xdrs *x, rpc_msg *m)
{
	xdr_ulong(x, &m->xid);
	xdr_enum(x, &m->mtype);

	switch (m->mtype)
	{
		case CALL:
			xdr_ulong(x, &m->cbody.rpcvers);
			xdr_ulong(x, &m->cbody.prog);
			xdr_ulong(x, &m->cbody.vers);
			xdr_ulong(x, &m->cbody.proc);
			if (!xdr_opaque_auth(x, &m->cbody.cred))  return FALSE;
			if (!xdr_opaque_auth(x, &m->cbody.verf))  return FALSE;
			if (m->cbody.xproc)
			{
				if (!(*(m->cbody.xproc))(x, m->cbody.data))
					return FALSE;
			}
			break;
		case REPLY:
			xdr_enum(x, &m->rbody.rb_stat);
			if (MSG_ACCEPTED == m->rbody.rb_stat)
			{
				if (!xdr_opaque_auth(x, &m->rbody.rb_arpl.ar_verf))
					return FALSE;
				xdr_enum(x, &m->rbody.rb_arpl.ar_stat);
				if (PROG_MISMATCH == m->rbody.rb_arpl.ar_stat)
				{
					xdr_ulong(x, &m->rbody.rb_arpl.ar_mis_info.low);
					xdr_ulong(x, &m->rbody.rb_arpl.ar_mis_info.high);
				}
				if (SUCCESS == m->rbody.rb_arpl.ar_stat)
				{
					if (m->rbody.rb_arpl.ar_result.xproc)
					{
						if (!(*(m->rbody.rb_arpl.ar_result.xproc))
						                       (x, m->rbody.rb_arpl.ar_result.data))
							return FALSE;
					}
				}
			}
			else if (MSG_DENIED == m->rbody.rb_stat)
			{
				xdr_enum(x, &m->rbody.rb_rrpl.rr_stat);
				if (RPC_MISMATCH == m->rbody.rb_rrpl.rr_stat)
				{
					xdr_ulong(x, &m->rbody.rb_rrpl.rr_mis_info.low);
					xdr_ulong(x, &m->rbody.rb_rrpl.rr_mis_info.high);
				}
				else if (AUTH_ERROR == m->rbody.rb_rrpl.rr_stat)
					xdr_enum(x, &m->rbody.rb_rrpl.rr_astat);
			}
			break;
		default:
			return FALSE;
	}
	return TRUE;
}
