/****************************************************************
 *								*
 * Copyright (c) 2001-2018 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

 /*
 * rc_srvc_xact.c ---
 *
 * Process a client's RC transaction.
 *
 */

#ifndef lint
static char     rcsid[] = "$Header:$";
#endif

#include "mdef.h"

#include "gtm_stdio.h"
#include "gtm_string.h"

#include <errno.h>

#include "gtcm.h"
#include "omi.h"
#include "gtmio.h"
#include "have_crit.h"
#include "rc.h"

typedef struct rc_clnt_err rc_clnt_err;
struct rc_clnt_err
{
	rc_clnt_err   *next;
	int4        pid;
};

GBLDEF int      rc_size_return = 0;
GBLDEF UINTPTR_T rc_auxown = 0;
GBLDEF int      rc_errno = 0;
GBLDEF int      rc_nxact = 0;
GBLDEF int      rc_nerrs = 0;
GBLDEF rof_struct *rc_overflow = (rof_struct *) 0;
GBLREF int	history;
GBLREF int	omi_pid;

#define	OUT_LINE	256 + 1

static rc_op   rc_dispatch_table[RC_OP_MAX] = {
	0,
	rc_prc_opnd,
	rc_prc_clsd,
	 /* rc_prc_exch */ 0,
	rc_prc_lock,
	rc_prc_logn,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	rc_prc_getp,
	rc_prc_getr,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	rc_prc_kill,
	rc_prc_set,
	rc_prc_setf,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	 /* rc_prc_ntrx */ 0
};

int rc_srvc_xact(omi_conn *cptr, char *xend)
{
	char           *cpte, msg[OUT_LINE], *p, *tptr;
	int             dumped = 0, pkt_len, rqlen, rv;
	rc_clnt_err   *elst, *eptr;
	rc_q_hdr      *qhdr;
	rc_rsp_page   *rpg;
	rc_xblk_hdr   *fxhdr;

	pkt_len = (int)(xend - (char *) cptr);
	/* Reserve space for the RC header in the response buffer */
	fxhdr = (rc_xblk_hdr *) cptr->xptr;
	cptr->xptr += SIZEOF(rc_xblk_hdr);
	/* If true, this not a known RC block type */
	if (fxhdr->method.value != RC_METHOD)
	{
#		ifdef DEBUG
		gtcm_cpktdmp((char *)cptr, pkt_len,"Unknown RC block type.");
		dumped = 1;
#		endif
		return -OMI_ER_PR_INVMSGFMT;
	}
	/* Check the endianism of the XBLK */
#	ifdef LTL_END
	if (fxhdr->big_endian.value)
		return -OMI_ER_PR_INVMSGFMT;
#	else				/* defined(LTL_END) */
#	ifdef BIG_END
	if (!fxhdr->big_endian.value)
		return -OMI_ER_PR_INVMSGFMT;
#	else				/* defined(BIG_END) */
	return -OMI_ER_PR_INVMSGFMT;
#	endif				/* !defined(BIG_END) */
#	endif				/* !defined(LTL_END) */
	/* Stash this for any lock operations */
	rc_auxown = (UINTPTR_T)cptr;
	/* This is the list of errors */
	elst = (rc_clnt_err *) 0;
	/* This is the offset of the first error Aq */
	fxhdr->err_aq.value = 0;
	tptr = cptr->xptr;
	/* Loop through the requests in the XBLK */
	for (qhdr = (rc_q_hdr *) 0; cptr->xptr < xend;)
	{
		qhdr = (rc_q_hdr *) tptr;
		rqlen = qhdr->a.len.value;
		if (history)
		{
			init_rc_hist(cptr->stats.id);
			save_rc_req((char *)qhdr,qhdr->a.len.value);
		}
		rv = -1;
		/* Check to see if this is a error'd PID, throw away packets from these. */
		/* 9/8/94 VTF:  protocol change: Process any LOCK/UNLOCK requests from an error'd PID */
		for (eptr = elst; eptr; eptr = eptr->next)
			if ((eptr->pid == ((qhdr->r.pid1.value << 16) | qhdr->r.pid2.value))
					&& (qhdr->r.typ.value != RC_LOCK_NAMES))
				break;
		if (eptr)
			qhdr->a.erc.value = RC_NETREQIGN;
		/*	Do we do this yet? */
		else if ((qhdr->r.typ.value > RC_OP_MAX ) || (!rc_dispatch_table[qhdr->r.typ.value]))
		{
#			ifdef DEBUG
			if (qhdr->r.typ.value == RC_EXCH_INFO)
			{
				dumped = 1;
				OMI_DBG((omi_debug, "Note: Unsupported request type (ExchangeInfo)\n"));
			}
			else
			{
				gtcm_cpktdmp((char *)qhdr, qhdr->a.len.value, "Unknown request type.");
				dumped = 1;
			}
#			endif
			qhdr->a.erc.value = RC_NETERRNTIMPL;
		} else	/* Try it */
		{
			/*
			 * Calculate the size available for a response
			 * page
			 */
			if (qhdr->r.typ.value == RC_GET_PAGE
			    || qhdr->r.typ.value == RC_GET_RECORD)
			{
				rc_size_return = (int4)((char *) fxhdr
					+ fxhdr->end.value - RC_MIN_CPT_SIZ - (char *) qhdr
					- SIZEOF(rc_rsp_page) + 1);
			}
			rc_overflow = cptr->of;
			rc_errno = RC_SUCCESS;
			if (fxhdr->free.value > fxhdr->end.value)
			{
				SNPRINTF(msg, OUT_LINE, "invalid packet :  free (%x) > end (%x).  Dumped RC header + packet",
					fxhdr->free.value, fxhdr->end.value);
				/*GTM64: Assuming length of packet wont excced MAX_INT */
				gtcm_cpktdmp((char *)fxhdr, (int4)(((char *)xend) - ((char *)fxhdr)),msg);
				if (history)
				    dump_omi_rq();
				return -OMI_ER_PR_INVMSGFMT;
			}
			rv = (*rc_dispatch_table[qhdr->r.typ.value]) (qhdr);
			if (fxhdr->free.value > fxhdr->end.value)
			{
				SNPRINTF(msg, OUT_LINE, "corrupted packet, free (%x) > end.value (%x).  Dumped RC header + packet",
					fxhdr->free.value,fxhdr->end.value);
				gtcm_cpktdmp((char *)fxhdr, (int)(((char *)xend) - ((char *)fxhdr)),msg);
				p = (char *)-1L;
				*p = 1;   /*guarenteed core dump */
			}
			if (qhdr->a.len.value > fxhdr->end.value)
			{
				SNPRINTF(msg, OUT_LINE,
					"corrupted packet, qhdr.a.len.value=%x > fxhdr end (%x).  Dumped RC header + packet",
					qhdr->a.len.value,fxhdr->end.value);
				gtcm_cpktdmp((char *)qhdr, qhdr->a.len.value, msg);
				p = (char *)-1L;
				*p = 1;   /*guarenteed core dump */
			}
			rc_nxact++;
			if (rc_errno != RC_SUCCESS)
			{
				rc_nerrs++;
				qhdr->a.erc.value = rc_errno;
				rv = -1;
#				ifdef DEBUG
				{
					SNPRINTF(msg, OUT_LINE, "RC error (0x%x).",rc_errno);
					gtcm_cpktdmp((char *)qhdr, qhdr->a.len.value, msg);
					dumped = 1;
				}
#				endif
			}
		}
		/* Keep track of erroneous PIDs */
		if (rv < 0)
		{
#			ifdef DEBUG
			if (!dumped)
				gtcm_cpktdmp((char *)qhdr, qhdr->a.len.value,
					    "RC Request returned error.");
			dumped = 1;
#			endif
			eptr = (rc_clnt_err *) malloc(SIZEOF(rc_clnt_err));
			eptr->pid = (qhdr->r.pid1.value << 16) | qhdr->r.pid2.value;
			eptr->next = elst;
			elst = eptr;
			if (fxhdr->err_aq.value == 0)
				fxhdr->err_aq.value = (char *) qhdr - (char *) fxhdr;
		}
		qhdr->r.typ.value |= 0x80;
		if (history)
			save_rc_rsp((char *)qhdr,qhdr->a.len.value);
		/* Move to the next request */
		cptr->xptr += rqlen;
		tptr = (char *)qhdr;
		tptr += RC_AQ_HDR;
		if ((qhdr->r.typ.value == (RC_GET_PAGE | 0x80)) || (qhdr->r.typ.value == (RC_GET_RECORD | 0x80)))
			break;	/* Reads are always the last request in the buffer */
		else {
			if (cptr->xptr < xend)
			{
				/* ensure that the length of the next request actually fits into this XBLK */
				assert(((rc_q_hdr *)(cptr->xptr))
				       ->a.len.value <= xend - cptr->xptr);
				memcpy(tptr, cptr->xptr, ((rc_q_hdr
					   *)(cptr->xptr))->a.len.value);
			}
		  qhdr->a.len.value = RC_AQ_HDR;
		}
	}
	/* Forget the erroneous PIDs */
	if (elst)
	{
		for (eptr = elst->next; eptr; eptr = (elst = eptr)->next)
			free(elst);
		free(elst);
	}
	/* If true, there was an XBLK, so fill in some of the response fields */
	/* There's no way that I can see to get to this point with a null qhdr. */
	if (qhdr)
	{
		fxhdr->last_aq.value = (char *) qhdr - (char *) fxhdr;
		fxhdr->cpt_tab.value = ((char *) qhdr -
					(char *) fxhdr) + qhdr->a.len.value;
		fxhdr->cpt_siz.value = fxhdr->end.value - fxhdr->cpt_tab.value;
	} else
	{
		fxhdr->last_aq.value = 0;
		fxhdr->cpt_tab.value = fxhdr->free.value;
		fxhdr->cpt_siz.value = fxhdr->end.value - fxhdr->cpt_tab.value;
	}
	if (fxhdr->free.value > fxhdr->end.value)
	{
		SNPRINTF(msg, OUT_LINE, "invalid packet :  free (%x) > end (%x).  Dumped RC header + packet",
			fxhdr->free.value, fxhdr->end.value);
		/*GTM64: Assuming length of packet wont excced MAX_INT */
		gtcm_cpktdmp((char *)fxhdr, (int4)(((char *)xend) - ((char *)fxhdr)),msg);
		if (history)
			dump_omi_rq();
		return -OMI_ER_PR_INVMSGFMT;
	} else if (fxhdr->cpt_tab.value > fxhdr->end.value)
	{
		SNPRINTF(msg, OUT_LINE, "invalid packet :  cpt_tab (%x) > end (%x).  Dumped RC header + packet",
		    fxhdr->cpt_tab.value, fxhdr->end.value);
		/*GTM64: Assuming length of packet wont excced MAX_INT */
		gtcm_cpktdmp((char *)fxhdr, (int)(((char *)xend) - ((char *)fxhdr)),msg);
		if (history)
			dump_omi_rq();
		return -OMI_ER_PR_INVMSGFMT;
	} else if (fxhdr->cpt_tab.value + fxhdr->cpt_siz.value > fxhdr->end.value)
	{
		SNPRINTF(msg, OUT_LINE, "invalid packet :  cpt_tab + cpt_siz (%x) > end (%x).  Dumped RC header + packet",
			fxhdr->cpt_tab.value + fxhdr->cpt_siz.value, fxhdr->end.value);
		/*GTM64: Assuming length of packet wont excced MAX_INT */
		gtcm_cpktdmp((char *)fxhdr, (int4)(((char *)xend) - ((char *)fxhdr)),msg);
		if (history)
			dump_omi_rq();
		return -OMI_ER_PR_INVMSGFMT;
	}
	rc_send_cpt(fxhdr, (rc_rsp_page *) qhdr);
	if (fxhdr->free.value > fxhdr->end.value)
	{
		SNPRINTF(msg, OUT_LINE, "invalid Aq packet :  free (%x) > end (%x).  Dumped RC header + packet",
			fxhdr->free.value, fxhdr->end.value);
		/*GTM64: Assuming length of packet wont excced MAX_INT */
		gtcm_cpktdmp((char *)fxhdr, (int4)(((char *)xend) - ((char *)fxhdr)),msg);
		if (history)
			dump_omi_rq();
		assert(fxhdr->free.value <= fxhdr->end.value);
		return -OMI_ER_PR_INVMSGFMT;
	}
	return fxhdr->free.value;
}
