/*
 * $Id: rpcutil.c,v 1.8 2006/06/14 10:50:49 c4chris Exp $
 */

#include "os.h"

/*
 * rpcutil.c - routines for emulating RPC library functions without really
 *	       receiving packets.
 *
 * David A. Curry				Jeffrey C. Mogul
 * Purdue University				Digital Equipment Corporation
 * Engineering Computer Network			Western Research Laboratory
 * 1285 Electrical Engineering Building		250 University Avenue
 * West Lafayette, IN 47907-1285		Palo Alto, CA 94301
 * davy@ecn.purdue.edu				mogul@decwrl.dec.com
 * 
 */
#include <sys/param.h>
#include <netinet/in.h>
#ifdef SVR4
#include <sys/tiuser.h>
#endif
#ifdef USE_LINUX
/* This yucky hack is needed because glibc's headers are wrong wrt the
 * correct size needed in RPC stuff on 64-bit machines.  There even is
 * a comment in <rpc/types.h> to that effect...  */
#define u_long uint32_t
#endif
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#ifdef SVR4
#include <rpc/clnt_soc.h>
#endif
#include <rpc/rpc_msg.h>
#include <rpc/svc.h>
#ifdef USE_LINUX
#undef u_long
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#define NFSSERVER	1

#ifdef sun
#include <sys/vfs.h>
#include <nfs/nfs.h>
#endif

#ifdef ultrix
#include <sys/types.h>
#include <sys/time.h>
#include <nfs/nfs.h>
#endif

#ifdef sgi
#include <sys/time.h>
#ifdef IRIX6
#include <sys/vfs.h>
#include <sys/fs/nfs.h>
#else
#include "sgi.map.h"
#endif
#endif

#ifdef __osf__
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mount.h>
#include <nfs/nfs.h>
#endif

#ifdef LINUX
#include "linux.map.h"
#endif

#include "nfswatch.h"
#include "externs.h"
#include "rpcdefs.h"

/* get rpc arguments		*/
static bool_t rpcxdr_getargs(SVCXPRT *, xdrproc_t, caddr_t);

/*
 * Operations on the SVCXPRT structure.  We're only going to use
 * the one to get arguments from it.
 */
static struct xp_ops xp_ops = {
	NULL, NULL, rpcxdr_getargs, NULL, NULL, NULL
#ifdef sun
	, NULL  /* Solaris has an additional xp_control field...  */
#endif
};

static SVCXPRT	*xprt;		/* the service description		*/

/*
 * setup_rpcxdr - set up for decoding RPC XDR stuff.  Sort of a svcudp_create
 *		  without the socket code.
 */
void
setup_rpcxdr(void)
{
	register struct svcudp_data *su;

	/*
	 * Allocate the SVCXPRT structure.
	 */
	if ((xprt = (SVCXPRT *) malloc(sizeof(SVCXPRT))) == NULL) {
		(void) fprintf(stderr, "%s: out of memory.\n", pname);
		finish(-1);
	}

	/*
	 * Allocate UDP service data.
	 */
	if ((su = (struct svcudp_data *) malloc(sizeof(struct svcudp_data))) == NULL) {
		(void) fprintf(stderr, "%s: out of memory.\n", pname);
		finish(-1);
	}

	/*
	 * This is the maximum size of a packet.
	 */
	su->su_iosz = ((UDPMSGSIZE + 3) / 4) * 4;

	/*
	 * Get a buffer to store stuff in.
	 */
	if ((rpc_buffer(xprt) = (char *) malloc(su->su_iosz)) == NULL) {
		(void) fprintf(stderr, "%s: out of memory.\n", pname);
		finish(-1);
	}

	/*
	 * Fill in the SVCXPRT structure.  This is a standard RPC routine.
	 */
	(void) xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz,
		XDR_DECODE);

	xprt->xp_ops = &xp_ops;
	xprt->xp_p2 = (caddr_t) su;
	xprt->xp_verf.oa_base = su->su_verfbody;
}

#ifdef LINUX
/*
 * Try with my version of xdr_callmsg(), because the one in glibc is
 * wrong on 64-bit machines.
 */
static bool_t
my_xdr_callmsg(XDR *xdrs, struct rpc_msg *cmsg)
{
  if (xdr_u_int (xdrs, &(cmsg->rm_xid))
      && xdr_enum (xdrs, (void *) & (cmsg->rm_direction))
      && (cmsg->rm_direction == CALL)
      && xdr_u_int (xdrs, &(cmsg->rm_call.cb_rpcvers))
      && (cmsg->rm_call.cb_rpcvers == RPC_MSG_VERSION)
      && xdr_u_int (xdrs, &(cmsg->rm_call.cb_prog))
      && xdr_u_int (xdrs, &(cmsg->rm_call.cb_vers))
      && xdr_u_int (xdrs, &(cmsg->rm_call.cb_proc))
      && xdr_opaque_auth (xdrs, &(cmsg->rm_call.cb_cred)))
        return xdr_opaque_auth (xdrs, &(cmsg->rm_call.cb_verf));
    return FALSE;
}
#endif

/*
 * udprpc_recv - pretend we've received an RPC packet - this is sort of like
 *		 svcudp_recv.
 */
int
udprpc_recv(char *data, u_int length, struct rpc_msg *msg, SVCXPRT **xp)
{
	register XDR *xdrs;
	register struct svcudp_data *su;

	su = su_data(xprt);
	xdrs = &(su->su_xdrs);

	/*
	 * Too short.
	 */
	if (length < (4 * sizeof(u_int32)))
		return(FALSE);

	if (length > truncation)
		length = truncation;

	/*
	 * Copy the data.
	 */
	(void) bcopy(data, rpc_buffer(xprt), min(length, su->su_iosz));

	xdrs->x_op = XDR_DECODE;

	/*
	 * Set the XDR routines to the start of the buffer.
	 */
	(void) XDR_SETPOS(xdrs, 0);

	/*
	 * Decode the RPC message structure.
	 */
#ifdef LINUX
	if (!my_xdr_callmsg(xdrs, msg))
		return(FALSE);
#else
	if (!xdr_callmsg(xdrs, msg))
		return(FALSE);
#endif

	su->su_xid = msg->rm_xid;
	*xp = xprt;

	return(TRUE);
}

/*
 * rpcxdr_getargs - called by SVC_GETARGS.
 */
static bool_t
rpcxdr_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, caddr_t args_ptr)
{
	return((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr));
}
