/* -*- Mode:C; Tab-width:4 -*- */
/*                                                                       */
/*                                                                       */
/*                      RESTRICTED RIGHTS LEGEND                         */
/*                                                                       */
/* Use, duplication, or disclosure by the Government is subject to       */
/* restrictions as set forth in subdivision (c)(1)(ii) of the Rights in  */
/* Technical Data and Computer Software clause at 252.227-7013.          */
/*                                                                       */
/*                    TEXAS INSTRUMENTS INCORPORATED.                    */
/*                            P.O. BOX 149149                            */
/*                         AUSTIN, TEXAS 78714-9149                      */
/*                              MS 2151                                  */
/*                                                                       */
/*  Copyright (C)   1987,1988,1989,1990 Texas Instruments Incorporated.  */
/*  All rights reserved.                                                 */
/*                                                                       */
/*
 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify Sun RPC without charge, but are not authorized
 * to license or distribute it to anyone else except as part of a product or
 * program developed by the user.
 * 
 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 * 
 * Sun RPC is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 * 
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 * OR ANY PART THEREOF.
 * 
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 * 
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
#ifndef lint
static char sccsid[] = "@(#)clnt_mac.c 1.1 86/02/03 Copyr 1987 Texas Instr";
#endif
	 
	 /*
	  * clnt_mac.c, Implements a MAC based, client side RPC.
	  *
	  * Copyright (C) 1987, Texas Instruments, Inc.
	  */
	 
#include <stdio.h>
#include "rpc-types.h"
#include "::bsd:sys:socket.h"
#include "::bsd:sys:time.h"
#include "::bsd:netinet:bsd-in.h"
#include "::bsd:netdb.h"
#include "::bsd:errno.h"
#include "xdr.h"
#include "auth.h"
#include "clnt.h"
#include "rpc_msg.h"
#include "pmap_clnt.h"
#include <addinComm.h>
#include <sockets.h>
#include <micronet-accessors.h>
#include <strings.h>
	 
char *malloc();
extern int errno;

/*
 * MICRONET bases client side rpc operations
 */
static int	clntmac_call();   /* clm 10/16/89 - no longer use enum clnt_stat  */
static void		clntmac_abort();
static void		clntmac_geterr();
static bool_t	clntmac_freeres();
static void		clntmac_destroy();
	 
static struct clnt_ops mac_ops =
{
	clntmac_call,
	clntmac_abort,
	clntmac_geterr,
	clntmac_freeres,
	clntmac_destroy
  };

/* 
 * Private data kept per client handle
 */
struct cu_data
{
	int		   cu_sock;
	struct sockaddr_in cu_raddr;
	int		   cu_rlen;
	struct timeval	   cu_wait;
	struct rpc_err	   cu_error;
	XDR		   cu_outxdrs;
	u_int		   cu_sendsz;
	acb            *out_acb;
	char		   *cu_outbuf;
	u_int		   cu_recvsz;
	acb            *in_acb;
	char		   *cu_inbuf;
	struct rpc_msg call_msg;
};

/*
 * Create a MICRONET based client handle.
 * If *sockp<0, *sockp is set to a newly created MICRONET socket.
 * If raddr->sin_port is 0 a binder on the remote machine
 * is consulted for the correct port number.
 * NB: It is the clients responsibility to close *sockp.
 * NB: The rpch->cl_auth is initialized to null authentication.
 *     Caller may wish to set this something more useful.
 *
 * wait is the amount of time used between retransmitting a call if
 * no response has been heard;  retransmition occurs until the actual
 * rpc call times out.
 *
 * sendsz and recvsz are the maximum allowable packet sizes that can be
 * sent and received.
 */

CLIENT *
clntmac_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
	 struct sockaddr_in *raddr;
	 u_long program;
	 u_long version;
	 struct timeval wait;
	 register int *sockp;
	 u_int sendsz;
	 u_int recvsz;
{
	CLIENT *cl;
	register struct cu_data *cu;
	struct timeval now;
	struct sockaddr_in src_addr;
	
	
	if (!(cl = (CLIENT *) malloc(sizeof(CLIENT))))	  
	  {
#ifdef DEBUG
		  printf("clntmac_bufcreate: out of memory\n");
#endif
		  rpc_createerr.cf_stat = RPC_SYSTEMERROR;
		  rpc_createerr.cf_error.re_errno = errno;
		  goto fooy;
	  }
	sendsz = ((sendsz + 3) / 4) * 4;
	recvsz = ((recvsz + 3) / 4) * 4;
		
	if (!(cu = (struct cu_data *) malloc(sizeof(struct cu_data))))
	  {
#ifdef DEBUG
		  printf("clntmac_bufcreate: out of memory\n");
#endif
		  rpc_createerr.cf_stat = RPC_SYSTEMERROR;
		  rpc_createerr.cf_error.re_errno = errno;
		  goto fooy;
	  }

	gettimeofday(&now, (struct timezone *)0);
	if (raddr->sin_port == 0)  
	  {
		  u_short port;
		  if (!(port = pmap_getport(raddr, program, version, PF_MAC))) goto fooy;
		  raddr->sin_port = htons(port);
	  }
	
	cl->cl_ops = &mac_ops;
	cl->cl_private = (caddr_t)cu;
	cu->cu_raddr = *raddr;
	cu->cu_rlen = sizeof (cu->cu_raddr);
	cu->cu_wait = wait;
	cu->cu_sendsz = sendsz;
	cu->cu_recvsz = recvsz;

	cu->call_msg.rm_xid =  now.tv_sec ^ now.tv_usec;
	cu->call_msg.rm_direction = CALL;
	cu->call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
	cu->call_msg.rm_call.cb_prog = program;
	cu->call_msg.rm_call.cb_vers = version;

	cu->out_acb = cu->in_acb = 0;
	cu->cu_outbuf = cu->cu_inbuf = 0; 
	                                     
	if (*sockp < 0)	  
	  {
		  int dontblock = 1;
		  
		  *sockp = socket(AF_MAC, SOCK_DGRAM, PF_MAC);
		  if (*sockp < 0)
			{
				rpc_createerr.cf_stat = RPC_SYSTEMERROR;
				rpc_createerr.cf_error.re_errno = errno;
				goto fooy;
			}
		  		  
		  src_addr.sin_family = AF_MAC;
		  src_addr.sin_port = 0;
		  src_addr.sin_addr.S_un.S_addr = DriverData->my_slot;
		  bind(*sockp, &src_addr, (sizeof (cu->cu_raddr)));
	  }
	cu->cu_sock = *sockp;
	cl->cl_auth = authnone_create();
	return (cl);
  fooy:
	if (cu)
	  free((caddr_t)cu, sizeof(struct cu_data));
	  
	if (cl)
	  free((caddr_t)cl, sizeof(CLIENT));
	return ((CLIENT *) NULL);
}

CLIENT *
clntmac_create(raddr, program, version, wait, sockp)
	 struct sockaddr_in *raddr;
	 u_long program;
	 u_long version;
	 struct timeval wait;
	 register int *sockp;
{
	
	return(clntmac_bufcreate(raddr, program, version, wait, sockp,
							 MACMSGSIZE, MACMSGSIZE));
}

/* clm 10/16/89 - no longer use the enum clnt_stat type */
static int 
clntmac_call(cl, proc, xargs, argsp, xresults, resultsp, timeout)
	 register CLIENT	*cl;		/* client handle */
	 u_long		        proc;		/* procedure number */
	 xdrproc_t	        xargs;		/* xdr routine for args */
	 caddr_t		    argsp;		/* pointer to args */
	 xdrproc_t	        xresults;	/* xdr routine for results */
	 caddr_t		    resultsp;	/* pointer to results */
	 struct timeval	    timeout;	/* seconds to wait before giving up */
{
#pragma unused(timeout)			/* 11/22/89 sbw */

	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
	register XDR *xdrs;
	register int outlen;
	register int inlen;
	int readfds, fromlen;
	struct sockaddr_in from;
	struct rpc_msg reply_msg;
	XDR reply_xdrs;
	bool_t ok;

  call_again:
	
	if(cu->out_acb)
	  {
#ifdef DEBUG
		  DebugStr("There is a left over acb in cu.");
#endif
		  recvfrom_acb_return(cu->out_acb);
		  cu->out_acb = 0;
		  cu->cu_outbuf = 0; 
	  }
	
	/* Allocate the acb for the out data. */
	cu->out_acb = (acb *)sendto_acb_allocate(cu->cu_sendsz, &(cu->cu_outbuf));
                 /* clm 9/29/89 - cast */
	if(!cu->out_acb)
	  return (cu->cu_error.re_status = RPC_SYSTEMERROR);

	/* Create the xdr memory stream for buffer. */
	xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf, cu->cu_sendsz, XDR_ENCODE);

	/* Assign a new xid for this call. */
	cu->call_msg.rm_xid++;
	
	/* Place the call header in the buffer. */
	if (!xdr_callhdr(&(cu->cu_outxdrs), &(cu->call_msg)))
	  {
		  recvfrom_acb_return(cu->out_acb);
		  cu->out_acb = 0;      
		  cu->cu_outbuf = 0;
		  return (cu->cu_error.re_status = RPC_SYSTEMERROR);
	  }
	xdrs = &(cu->cu_outxdrs);
	if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
	    (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
	    (! (*xargs)(xdrs, argsp)))
	  {
		  recvfrom_acb_return(cu->out_acb);
		  cu->out_acb = 0;       
		  cu->cu_outbuf = 0;
		  return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
	  }
	outlen = (int)XDR_GETPOS(xdrs);

	if (sendto_acb(cu->cu_sock, cu->out_acb, outlen, 0, (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen)
	  {
		  cu->cu_error.re_errno = errno;
		  recvfrom_acb_return(cu->out_acb);
		  cu->out_acb = 0;      
		  cu->cu_outbuf = 0;
		  return (cu->cu_error.re_status = RPC_CANTSEND);
	  }
	/* Lose our pointer to the acb. */
	cu->out_acb = 0;     
	cu->cu_outbuf = 0;
	
	reply_msg.acpted_rply.ar_verf = _null_auth;
	reply_msg.acpted_rply.ar_results.where = resultsp;
	reply_msg.acpted_rply.ar_results.proc = xresults;
	
	readfds = 1 << cu->cu_sock;
	fromlen = sizeof(struct sockaddr);
		
	do
	  {
		  
		  /* Return acb from last unsuccessful try. */
		  if(cu->in_acb)
			{
				recvfrom_acb_return(cu->in_acb);
				cu->in_acb = 0;   
				cu->cu_inbuf = 0;
			}
		  
		  switch (select(32, &readfds, (int *)NULL, (int *)NULL, &(cu->cu_wait)))
			{
				
			  case 0:
				return (cu->cu_error.re_status = RPC_TIMEDOUT);
				break;
				
			  case -1:
				cu->cu_error.re_errno = errno;
				return (cu->cu_error.re_status = RPC_CANTRECV);
				break;
			}
		  		  
		  inlen = recvfrom_acb(cu->cu_sock, &(cu->in_acb),&(cu->cu_inbuf), cu->cu_recvsz, 0,
							   (struct sockaddr *)&from, &fromlen);
		  if (inlen < 0)
			{
				if((errno != EINTR) && (errno != EWOULDBLOCK))
				  {
					  /* Return the acb if we got one. */
					  if(cu->in_acb)
						{
							recvfrom_acb_return(cu->in_acb);
							cu->in_acb = 0;     
							cu->cu_inbuf = 0;
						}
					  cu->cu_error.re_errno = errno;
					  return (cu->cu_error.re_status = RPC_CANTRECV);
				  }
			}
	  }
	/* do While receiving stray trash. */
	while((inlen < sizeof(u_long)) || (*((u_long *)(cu->cu_inbuf)) != cu->call_msg.rm_xid));
	
	/*
	 * now decode and validate the response
	 */
	xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
	
	ok = xdr_replymsg(&reply_xdrs, &reply_msg);
	/* XDR_DESTROY(&reply_xdrs);  save a few cycles on noop destroy */
	/* Return the received acb. */
	recvfrom_acb_return(cu->in_acb);
	cu->in_acb = 0;  
	cu->cu_inbuf = 0;

	if (ok)
	  {
		  _seterr_reply(&reply_msg, &(cu->cu_error));
		  if (cu->cu_error.re_status == RPC_SUCCESS)
			{
				if (! AUTH_VALIDATE(cl->cl_auth, &reply_msg.acpted_rply.ar_verf))
				  {
					  cu->cu_error.re_status = RPC_AUTHERROR;
					  cu->cu_error.re_why = AUTH_INVALIDRESP;
				  }
				if (reply_msg.acpted_rply.ar_verf.oa_base != NULL)
				  {
					  xdrs->x_op = XDR_FREE;
					  xdr_opaque_auth(xdrs,	&(reply_msg.acpted_rply.ar_verf));
				  } 
			}  /* end successful completion */
		  else
			{				
				/* maybe our credentials need to be refreshed ... */
				if (AUTH_REFRESH(cl->cl_auth))
				  {
#ifdef DEBUG
					  DebugStr("Refreshing cred.");
#endif
					  goto call_again;
				  }
					  
				/* We really can't loop here. Is this the correct error to return? */
				cu->cu_error.re_status = RPC_AUTHERROR;
				cu->cu_error.re_why = AUTH_INVALIDRESP;				  
			}
      }  /* end of valid reply message */
	else
	  {
		  cu->cu_error.re_status = RPC_CANTDECODERES;
	  }

	return (cu->cu_error.re_status);
}

static void
clntmac_geterr(cl, errp)
	 CLIENT *cl;
	 struct rpc_err *errp;
{
	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
	
	*errp = cu->cu_error;
}


static bool_t
clntmac_freeres(cl, xdr_res, res_ptr)
	 CLIENT *cl;
	 xdrproc_t xdr_res;
	 caddr_t res_ptr;
{
	register struct cu_data *cu = (struct cu_data *)cl->cl_private;
	register XDR *xdrs = &(cu->cu_outxdrs);
	
	xdrs->x_op = XDR_FREE;
	return ((*xdr_res)(xdrs, res_ptr));
}

static void 
clntmac_abort(h)
	 CLIENT *h;
{
#pragma unused(h)		/* 11/22/89 sbw */
}

static void
clntmac_destroy(cl)
	 CLIENT *cl;
{
	register struct cu_data *cu = (struct cu_data *)cl->cl_private;

	if(cu->in_acb)
	  {
		  recvfrom_acb_return(cu->in_acb);
		  cu->in_acb = 0;  
		  cu->cu_inbuf = 0;
	  }


	if(cu->out_acb)
	  {
		  recvfrom_acb_return(cu->out_acb);
		  cu->out_acb = 0;      
		  cu->cu_outbuf = 0;
	  }

	
	XDR_DESTROY(&(cu->cu_outxdrs));
	free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
	free((caddr_t)cl, sizeof(CLIENT));
}
