/*
   $Header: tnsapi.c 29-jun-99.17:11:14 loramasa Exp $
*/

/* Copyright (c) 1995, 1996, 1997, 1998, 1999 by Oracle Corporation.  All rights reserved. */

/*
NAME
  tnsapi - Transport Network Session Application Program Interface.
  
DESCRIPTION
  TNS engine.

PUBLIC FUNCTIONS
  tnsapi()

PRIVATE FUNCTIONS
  tnsdopen()
  tnsdclose()
  tnsdsend()
  tnsdrecv()
  tnsdcntl()

MODIFIED
   loramasa   06/22/99 - Added OTRACE statements
   nprakash   08/11/98 - Fix bug 500193
   smckinle   05/06/98 - Lint.
   scotmac    11/18/97 - Handle is ALWAYS allocated on open and deallocated on
   mberner    08/21/97 - fixup log information
   smckinle   07/08/97 - OSD reference shouldn't be in generic file.
   mberner    06/26/97 - fix bug #490613
   mberner    06/18/97 - fix bug #491875
   mhill      06/12/97 - clean up rcs header
   jchangav   09/27/96 - Merged from NETWORK_3.0.2.0.0_SOLARIS_960926
   aswang     11/25/96 - porting exception for bug#425421
   jgraham    09/23/96 - fix compilation error
   jgraham    09/20/96 - bug 355689
   jchangav   09/10/96 - Merge NT changes from 3.0.1
*/

/*************************************************************************/
/* Include headers 							 */
/*************************************************************************/

#include <string.h>

#ifndef TNSAPI_BEFORE_V23
# ifndef SLTS_ORACLE
#  include <slts.h>
# endif
#endif

#ifndef SNLSTD
#include <snlstd.h>
#endif

#ifndef NL
# include <nl.h>
#endif

#ifndef NSTRC
# include <nstrc.h>
#endif 

#ifndef NLOTR
# include <nlotr.h>
#endif

#ifndef NSI
# include <nsi.h>
#endif

#ifndef NSGBL
# include <nsgbl.h>
#endif

#ifndef NNCI_ORACLE
# include <nnci.h>
#endif

#ifndef NNFS_ORACLE
# include <nnfs.h>
#endif

#ifndef NRI
# include <nri.h>
#endif

#ifndef TNSAPI0
# include <tnsapi0.h>
#endif

#ifndef TNSAPI
# include <tnsapi.h>
#endif

/*************************************************************************/
/* Local function prototypes						 */
/*************************************************************************/
/* DISABLE check_naming */
/*
 * These are just mirror images of 
 *   tnsopen()
 *   tnsclose()
 *   tnssend()
 *   tnsrecv()
 * 
 * It is for the sake of implementation. We don't want to implement everything
 * in just one function. See tns.h for the usage and descriptions of these
 * functions.
 */

/*
   TNS Do Open: initialize the connection handle
 */
STATICF sword tnsdopen ( /*_ tnshdl **handlep, const text *name _*/ );

/*
   TNS Do Close: close the connection handle
 */
STATICF sword tnsdclose(/*_ tnshdl **handlep _*/);

/*
   TNS Do Send: send data to the connection handle
 */
STATICF sword tnsdsend(/*_ tnshdl *handle, ub1 *data, size_t *length _*/);

/*
   TNS Do Receive: receive data from the conneciton handle
 */
STATICF sword tnsdrecv(/*_ tnshdl *handle, ub1 *data, size_t *length _*/);

/*
   TNS Do Cntl: control functions
 */
STATICF sword tnsdcntl VAFP((sword operation, tnshdl *hdl, ... ));

/*
   TNS Do init: Initialize the NL global area 
 */
STATICF dvoid *tnsdinit(/*_ void _*/);

/*
   TNS Do Global Initialization: Initialize TNS API context in NPD global area
 */
STATICF void tnsdingbl(/*_ dvoid *npd, dvoid *gbhp _*/);

/*
   TNS Do Terminate: symmetric to tnsdinit()
 */
STATICF void tnsdtrm(/*_ dvoid *npd _*/);

/*
   TNS Do global terminate: symmetric to tnsdingbl()
 */
STATICF void tnsdtrgbl(/*_ tnsgblctx ** _*/);

/*************************************************************************/
/* Public Functions							 */
/*************************************************************************/

/*
   TNS API: execute the TNS control operation. This is the TNS engine.
 */

#if defined(A_OSF) || defined(HPUX)
int tnsapi (operation)
int operation;
#else
int tnsapi VAFD((operation VAAELLIPSIS))
int operation VAFDAD
VAFDELLIPSIS
#endif /* A_OSF || HPUX */
{
    va_list list;
    tnshdl **hdlp;
    tnshdl *hdl;
    text *name;
    sword ret;
    ub1 *data;
    size_t *len;
    sword cmd;

    VASTART(list, operation);
    /*
     * depending on the operation, the number and data type of argements are 
     * different
     */
    switch(operation)
    {
      case TNSAPIOOPEN:
	hdlp = va_arg(list, tnshdl **);
	name = (text *)va_arg(list, char *);
	/* sanity check. The user should not pass in a NULL tnsp */
	if (hdlp)
	{
	    ret = tnsdopen(hdlp, (text *)name);
	}
	else
	{
	    ret = (sword) NULHDL_TNSAPIE;
	}
	break;

      case TNSAPIOCLOSE:
	hdlp = va_arg(list, tnshdl **);
	/* sanity check. The user should not pass in a NULL tnsp */
	if (hdlp)
	{
	    ret = tnsdclose(hdlp);
	}
	else
	{
	    ret = (sword) NULHDL_TNSAPIE;
	}
	break;

      case TNSAPIOSEND:
	hdl = va_arg(list, tnshdl *);
	data = va_arg(list, ub1 *);
	len = va_arg(list, size_t *);
	/* sanity check. The user should not pass in a NULL tnsp */
	if (hdl) 
	{
	    ret = tnsdsend(hdl, data, len);
	}
	else
	{
	    ret = (sword) NULHDL_TNSAPIE;
	}
	break;       

      case TNSAPIORECV:
	hdl = va_arg(list, tnshdl *);
	data = va_arg(list, ub1 *);
	len = va_arg(list, size_t *);
	/* sanity check. The user should not pass in a NULL tnsp */
	if (hdl)
	{
	    ret = tnsdrecv(hdl, data, len);
	}
	else
	{
	    ret = (sword) NULHDL_TNSAPIE;
	}
	break;  

	/*
	 * Added to test listen/anser automatically, should be removed
	 * in the first release 
	 */
	/*    case TNSAPIOLSN:
	      hdl = va_arg(list, tnshdl *);
	      if (hdl)
	      ret = tnsdcntl(TNSAPIOLSN, hdl);
	      else
	      ret = NULHDL_TNSAPIE;
	      break;
	      */
	/* end of added code */

      case TNSAPICTL:
	hdl = va_arg(list, tnshdl *);
	cmd = va_arg(list, sword);
	if (hdl)
	{
	    ret = tnsdcntl(cmd, hdl);
	}
	else
	{
	    ret = (sword) NULHDL_TNSAPIE;
	}
	break;
      
      default:
	/*
	 * Addtional control function calls, e.g., nonblocking, flushing, will
	 * be added in future releases. 
	 */
	ret = (sword) INVOP_TNSAPIE;
	break;
    }

    va_end(list);
    return ((int)ret);
}

/*
 */ 
STATICF sword tnsdopen(handlep, name)
tnshdl **handlep;
const text *name;
{
    tnshdl *hdl = *handlep;
    dvoid *npd  = (dvoid *) 0;                                 /* NPD global */
    dvoid *gbhp = (dvoid *) 0;                         /* NS per-user global */
    size_t addbufl;
    size_t canbufl;
    sword ecode;
    sword retcode = 0;
    size_t namelen;
    text namebuf[NNCIDNMAX];
    text addbuf[NNCIDNMAX];
    text canbuf[NNCIDNMAX];
   
    /*
     * Allocate the handle.
     */
    if ( (hdl = (tnshdl *)snlmalc(1, sizeof(tnshdl))) == (tnshdl *)NULL)
    {
      return((sword)MALFAIL_TNSAPIE);
    }
    else
    {
      CLRSTRUCT(*hdl);
      *handlep = hdl;
    }
   
    /*
     * Check the tnsapi_nlstdgd in NPD global, if it is null, then this is 
     * the first connection handle of this user. We need
     * to allocate the tnsapi global in NPD global area
     */
    /*
     * Initialization for, e.g. trace in NL global area, and get the NPD
     * global
     */ 
    if (!(npd = tnsdinit()) )
    {
	tnsdtrm(npd);
	snlmfre((dvoid *)hdl);
	*handlep = (tnshdl *)NULL;
	return((sword)NLINIFAIL_TNSAPIE);
    }

    TNSLOCKMUTEX(npd);
    if ( (hdl->gbl_tnshdl = (dvoid *)TNSGCTX(npd)) == NULL)
    {
	/*
	 * This is the first connection handle of this user
	 * so we need to allocate the NPD global
	 */
	if ( (hdl->gbl_tnshdl = (dvoid *)snlmal(sizeof(tnsgblctx))) == NULL)
	{
	    snlmfre((dvoid *)hdl);
	    *handlep = (tnshdl *)NULL;
	    TNSUNLOCKMUTEX(npd);
	    return ((sword)MALFAIL_TNSAPIE);
	}
	TNSSCTX(npd, hdl->gbl_tnshdl);
     
	/* Initialize the NS per-user global area, check for errors */
	nsgblini(npd, &gbhp, NULLP(nsind));

	/* Initialize TNS API global area, check for errors  */
	tnsdingbl(npd, gbhp);
    }
    TNSUNLOCKMUTEX(npd);

    /* start connection out in blocking mode */
    hdl->io_tnshdl = IO_TNSHDL_BLOCKING;
    
    /* enable the tracing */
    {
        NLDTOTFNAME("tnsopen");
        NLOTRDEFINE(NLOTRFUNCID(920), npd,(ub4*) NULL,
                    NLDTTNSAPI, NLDTTDUMMY, NLDTTDUMMY);
        NLTRENTER();
        NLOTRENTER();
 
	/* 
	 * check if this is a client or a server, create the NV binding 
	 * if necessary
	 */
	if (!name)  
	{
	    hdl->svr_tnshdl = TRUE;
	    hdl->service_tnshdl = NULLP(text);
	}
	else
	{
	    namelen = strlen((char *)name);
	    /*
	     * In the first release, we will specify that listener has to be
	     * running in order to make a connection. In future enhancement,
	     * we are going to support the case in which the listener is not
	     * available. The implementation to accomodate both cases is done
	     * for the purpose of testing, however.
	     *
	     * If name is not NULL, it could be either client or server, 
	     * In the case of a server, name is the listening end point.
	     */
         
	    /* resolve the NV string */
	    if ( *name == NLNVBEGDE)
	    {
		/* it is an explicit NVstring, so use it directly */
                NLTRUSR((NLTRTRC, ns299trc));
                NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(299)));
		if( (hdl->service_tnshdl = (text *)snlmal(namelen +1)) 
		   == (text *)NULL)
		{
		    retcode = (sword) MALFAIL_TNSAPIE;
		}
		else
		{
		    DISCARD strcpy((char *)hdl->service_tnshdl, 
				   (const char *)name);
                    NLTRUSR((NLTRTRC, ns300trc, hdl->service_tnshdl));
                    NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(300),hdl->service_tnshdl));
		    /* 
		     * in the first release, not null name always indiate that
		     * this is the client 
		     */
		    hdl->svr_tnshdl = FALSE;  
		}
	    }
	    else
	    {
		/* try to resolve it */
		if (namelen >= NNCIDNMAX)
		{
                    NLTRFTL((NLTRTRC, ns301trc));
                    NSTRCOTFTL((NLOTRTRC, NSTRCMSGNO(301)));
		    retcode = (sword) NMTOOLONG_TNSAPIE;
		}
		else
		{
		    DISCARD memcpy((dvoid *)namebuf, (const dvoid *)name,
				   namelen + 1);               
           
		    if (ecode = nnfsn2a(npd, 
					namebuf, (size_t)NNCIDNMAX, &namelen,
					addbuf, (size_t)NNCIADMAX, &addbufl,
					canbuf, (size_t)NNCIDNMAX, &canbufl))
		    {
			/* Signal error, write error into trace steam*/
                        NLTRFTL((NLTRTRC, ns302trc));
                        NSTRCOTFTL((NLOTRTRC, NSTRCMSGNO(302)));
			retcode = (sword) NMRESFAIL_TNSAPIE;
		    }
		    else 
		    {
			if ((hdl->service_tnshdl = 
			     (text *)snlmal(strlen((const char *)addbuf)+1) )
			    == (text *)NULL)
			{
			    retcode = (sword) MALFAIL_TNSAPIE;
			}
			else
			{
			    DISCARD strcpy((char *)hdl->service_tnshdl,
					   (const char *)addbuf);
                            NLTRUSR((NLTRTRC, ns300trc, hdl->service_tnshdl));
                            NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(300),
                                        hdl->service_tnshdl));
			    /* 
			     * in the first release, not null name
			     * always indiate that this is the client 
			     */
			    hdl->svr_tnshdl = FALSE;  
			}
		    } /* if nnfsn2a */
		} /* if namelen */
	    } /* if *name == NLNVBEDGE */
	} /* if !name */

	/* Increase the count in con_tnsgblctx */
	if (retcode == 0)
	{
	    TNSLOCKMUTEX(npd);
	    ((tnsgblctx *)(hdl->gbl_tnshdl))->con_tnsgblctx++;         
	    TNSUNLOCKMUTEX(npd);

	    if ((hdl->res_tnshdl=(tnsres *)snlmal(sizeof(tnsres))) 
		!= (tnsres *)NULL)
	    CLRSTRUCT(*(hdl->res_tnshdl));
	    else
	    {
		retcode = (sword) MALFAIL_TNSAPIE;
	    }
	}

	if (retcode != 0 )
	{
	    if (hdl->service_tnshdl)
	    {
		snlmfre((dvoid *)hdl->service_tnshdl);
	    }

	    snlmfre((dvoid *)hdl);
	    *handlep = (tnshdl *)NULL;
	}
        NLTREXIT();
        NLOTREXIT();
    }
    return(retcode);
}

/*
 * Initialize TNS API context in NPD global area
 */
STATICF void tnsdingbl(npd, gbhp)
dvoid *npd;
dvoid *gbhp;
{
    tnsgblctx *ctx;

    /* sanity check */
    if (npd)                          
    {
	ctx = TNSGCTX(npd);
	/* This should have been allocated already */
	if (ctx)
	{
	    /*
	     * assign the NS per user global to the corresponding field in ctx
	     * and initialize the number of open connectins of this user to 0  
	     */
	    ctx->gbh_tnsgblctx = (nsgbu *)gbhp;
	    ctx->con_tnsgblctx = 0;
	    ctx->npd_tnsgblctx = npd;
	}
    }
    return;
}

/*
 * Initialize the NL global area for, e.g., tracing 
 * return the NPD global pointer
 */
STATICF dvoid *tnsdinit()
{
    dvoid *npd = (dvoid *) 0;
    size_t curdirl;
    serc se;
    size_t actesize = 0;
    nlstdatt init;
    text curdir[TNSAPI_MFN];
    text reason[TNSAPI_ERRBUFL];

    /* clear the init structure */
    CLRSTRUCT(init);

    /* initialize parameter files: system and local, trace, ... */
    init.mask_nlstdatt = NLSTDATT_SYSPARMS | NLSTDATT_TRACE | 
    NLSTDATT_PF_ERRORS_OK;

    /* system sqlnet.ora */
    init.syspdesc_nlstdatt.nlfnsname = (text *)TNSAPI_PFILENAME;
    init.syspdesc_nlstdatt.nlfnssize = TNSAPI_PFILENAMEL;
    init.syspdesc_nlstdatt.nlfnename = (text *)TNSAPI_PEXT;
    init.syspdesc_nlstdatt.nlfnesize = TNSAPI_PEXTL;

    /* trace unique */  
    bis(init.trcdesc_nlstdatt.nlfnflag, NLFNUNIQUE);

    /* trace extension */
    init.trcdesc_nlstdatt.nlfnename = (text *)TNSAPI_TEXT;
    init.trcdesc_nlstdatt.nlfnesize = TNSAPI_TEXTL;

    /* 
     * since we could not distinguish the client and the server at this stage,
     * we could not name the client and server trace differently by setting up
     * trcdesc_nlstdatt.nlfnsname field. The traces will be distinguished by
     * their uniqueness. The default trace file would be: tnsapi.trc_<pid>
     */
    init.trcdesc_nlstdatt.nlfnsname = (text *)TNSAPI_TFILENAME;
    init.trcdesc_nlstdatt.nlfnssize = TNSAPI_TFILENAMEL;
   
    /* default trace file directory is the current directory*/
    DISCARD snlfncdir(&se, curdir, sizeof(curdir), &curdirl);
    init.trcdesc_nlstdatt.nlfndname = (text *)curdir;
    init.trcdesc_nlstdatt.nlfndsize = curdirl;
    init.trcparms_nlstdatt[NLSTDGO_TRACE_FILE] = (text *)TNSAPI_TRCFNAME;
    init.trcparms_nlstdatt[NLSTDGO_TRACE_DIR] = (text *)TNSAPI_TRCDNAME;
    init.trcparms_nlstdatt[NLSTDGO_TRACE_LEVEL] = (text *)TNSAPI_TRCLEVEL;
  
#ifndef TNSAPI_BEFORE_V23
    /* by default, it is thread safe */
    init.threadsafe_nlstdatt = NLSTDATT_THREAD_SAFE;
#endif
  
    if (nlstdgg(&npd, &init, reason, TNSAPI_ERRBUFL, &actesize))
    {
	return ((dvoid *)0);
    }

    return(npd);
} 


/*
 * Close the tns connection, reset the tns api handle. 
 */
STATICF sword tnsdclose(handlep)
tnshdl **handlep;
{
    tnshdl *hdl = *handlep;
    tnsgblctx *ctx;
    dvoid *npd;

    /* sanity check */
    if (hdl)
    {
	if (!(ctx = (tnsgblctx *)hdl->gbl_tnshdl) || (ctx->con_tnsgblctx < 1))
	{
	    return((sword)HDLUNINI_TNSAPIE);
	}
	npd = ctx->npd_tnsgblctx;

	{
            NLDTOTFNAME("tnsclose");
            NLOTRDEFINE(NLOTRFUNCID(921), npd,(ub4*) NULL,
                        NLDTTNSAPI, NLDTTDUMMY, NLDTTDUMMY);
            NLTRENTER();
            NLOTRENTER();
	    /*
	     * check if the connections is open. 
	     */
	    if (hdl->cxd_tnshdl)
	    {
		/* close down this connection, and reset cxd_tnshdl */
		DISCARD nsdisc(hdl->cxd_tnshdl, NSFIMM);    
		snlmfre((dvoid *)hdl->cxd_tnshdl);
		hdl->cxd_tnshdl = (nscxd *)NULL;
	    }

	    /*
	     * free up the memory: service, error stack
	     */ 
	    if (hdl->service_tnshdl)
	    snlmfre((dvoid *)hdl->service_tnshdl);
	    if (hdl->res_tnshdl)
	    snlmfre((dvoid *)hdl->res_tnshdl);

	    /* 
	     * If tnsopen() succeeded but the first tnssend()/tnsrecv()
	     * failed, nsgbltrm(), nlstdstp() still need to be called.
	     */
	    /* We will get a ctx back if tnsopen() succeeded */

	    TNSLOCKMUTEX(npd);

	    /* if there are other open connections of this user */
	    if (--ctx->con_tnsgblctx == 0)
	    {
               /* Terminate NS per user global context */
	       nsgbltrm((dvoid *)ctx->gbh_tnsgblctx);
         
	       /* Reset the global area of TNS API in nlstdgd */
	       TNSSCTX(ctx->npd_tnsgblctx, NULL);

	       /* Terminate TNS API global context */
	       tnsdtrgbl((tnsgblctx **)(&(hdl->gbl_tnshdl)));

	       NLTREXIT();
               NLOTREXIT();
	       /* Shutdown the NL facilities initialized with nlstdgg() */
	       tnsdtrm(npd);
               npd = (dvoid *) 0;
	    }
	    /* this is not the last open connection */
	    else
	    {
	        /* only decrease the global counter of pointers to
                   the NPD global */
		nlstdtrm(&(ctx->npd_tnsgblctx));
		NLTREXIT();
                NLOTREXIT();
	    }

            if (npd)
              TNSUNLOCKMUTEX(npd);

	    /*
	     * Free the handle and reset
	     */ 
	    snlmfre((dvoid *)hdl);
	    *handlep = (tnshdl *)NULL; 
	}
	return(0);
    }
}

/*
 * call nlstdstp, symmetric to nlstdgg() in tnsdinit() 
 */
STATICF void tnsdtrm(npd)
dvoid *npd;
{
    nlstdstp(npd);
    return;
}

/*
 * Free up the memory, reset the pointer 
 */
STATICF void tnsdtrgbl(tnsgbl)
tnsgblctx **tnsgbl;
{
    snlmfre((dvoid *)(*tnsgbl));
    *tnsgbl = (tnsgblctx *)NULL;
    return;
}


/*
 * Send data over the handle. Connection is first establised
 * on the client side before data is sent. It is an error for
 * the server to call tnssend() right after tnsopen()
 */
STATICF sword tnsdsend(hdl, data, length)
tnshdl *hdl;
ub1 *data; 
size_t *length;
{
    tnsgblctx *ctx = (tnsgblctx *)hdl->gbl_tnshdl; /* TNS API global context */
    nscxd *cxd;			                               /* NS context */
    text *address = hdl->service_tnshdl;                     /* service name */
    tnsres *tres = hdl->res_tnshdl;                           /* error stack */
    dvoid *gbhp;		                       /* NS per user global */
    dvoid *npd;			                               /* NPD global */
    sword retcode = 0;


    /* if the global handle has not been allocated, can not call this
       function */
    if ( ctx == (tnsgblctx *)NULL)
    {
	/* Signal error, write error message into trace stream */
	return((sword)HDLUNINI_TNSAPIE);
    }

    gbhp = (dvoid *)ctx->gbh_tnsgblctx;
    npd = ctx->npd_tnsgblctx;

    {
        NLDTOTFNAME("tnssend");
        NLOTRDEFINE(NLOTRFUNCID(922), npd,(ub4*) NULL,
                    NLDTTNSAPI, NLDTTDUMMY, NLDTTDUMMY);
        NLTRENTER();
        NLOTRENTER();

	/*
	 * if this is the first call to tnssend(), no cxd has been
	 * allocated yet 
	 */
	if ( hdl->cxd_tnshdl == (nscxd *)NULL)
	{

	    /*
	     * Check if this is the client or the server,
	     * Client: try to establish the connection
	     * Server: it is an error since server must first call tnsrecv()
	     */

	    if ( !hdl->svr_tnshdl )   
	    {
		/* This is the client */
      
		if ((cxd = (nscxd *)snlmal(sizeof(nscxd))) == (nscxd *)NULL)
		{
		    retcode = (sword) MALFAIL_TNSAPIE;
		}
		else
		{
		    nsinfo info;
		    nscnd cndo, cndi;
		    text buf[TNSAPI_CONBUFL];
		    buf[0] = '\0';
		    /*
		     * initialize the structures: cxd, tres
		     */
		    CLRSTRUCT(*cxd);

		    CLRSTRUCT(info);
		    CLRSTRUCT(cndo);
		    CLRSTRUCT(cndi);

		    NSSETCND(&cndo, address);           
		    NSINICND(&cndi, buf, sizeof(buf));
		    NSINFENABLENA(&info);
        
		    /* making connection, with routing */
		    if (nricall(gbhp, 
				cxd, 
				&cndo, 
				&cndi, 
				&info, 
				&(cxd->nscxdres)) < 0)
		    {
			/*
			 * write error to trace
			 * error handling, setup the error stack
			 * (tnsres *res) from cxd->nscxdres
			 * snlmfre((dvoid *)cxd); 
			 */
                        NLTRUSR((NLTRTRC, ns303trc));
                        NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(303)));
			CPSTRUCT(tres->ns_tnsres, cxd->nscxdres);
			snlmfre((dvoid *)cxd);
			retcode = tres->res_tnsres = (sword) CONFAIL_TNSAPIE;
		    }
		    else
		    {
			hdl->cxd_tnshdl = cxd;
		    }
		}
	    } /* if !hdl->svr_tnshdl */
	    else
	    {
		/*
		 * I am the server 
		 * server should NOT call tnsapi_send() until the connection is
		 * established.
		 * write the error message in to trace stream
		 * set (tnsres *res) to indicate the error
		 */
                NLTRUSR((NLTRTRC, ns304trc)); 
                NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(304)));
		retcode = tres->res_tnsres = (sword) INVSVROP_TNSAPIE;
	    } 
	}

	/* Set connection into nonblocking mode if it has been requested
	   by a call before the connection was actually established -
	   the default was blocking */
	if (!retcode && (hdl->io_tnshdl == IO_TNSHDL_NONBLOCKING))
	{
          retcode = tnsdcntl((sword) TNSAPICNONBLOCKING, hdl);
	}

	if ( !retcode )
	{
	    /*
	     * Connection has been established, client/server wants to
	     * send data.  Flush the data to transport, since the
	     * default flush mode in NS is not flushing on send 
	     */
	    if ((retcode = 
		 nssend(hdl->cxd_tnshdl, NSWDATA, data, length, NSFFLUSH)) 
		!= 0) 
	    {
		if (hdl->cxd_tnshdl->nscxdres.nsresns == NSEWOULDBLOCK)
		{
		    /* we're in nonblocking mode and would block... */
		    retcode = tres->res_tnsres = (sword) WOULDBLOCK_TNSAPIE;
		}
		else
		{
		    /* 
		     * Send failed, set the TNS API error and the NS error
		     */
                    NLTRUSR((NLTRTRC, ns305trc))
                    NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(305)));
		    retcode = tres->res_tnsres = (sword) SDFAIL_TNSAPIE;
		}
		CPSTRUCT(tres->ns_tnsres, hdl->cxd_tnshdl->nscxdres);
	    }
	}
        NLTREXIT();
        NLOTREXIT();
    }
    return(retcode);
}

/*
 * Receive data over connection handle. Connection is first established
 * on the server side before data is received. It is an error for the
 * client to call tnsrecv() after tnsopen().
 */
STATICF sword tnsdrecv(hdl, data, length)
tnshdl *hdl;
ub1 *data;
size_t *length;
{
    tnsgblctx *ctx = (tnsgblctx *)hdl->gbl_tnshdl; /* TNS API global context */
    nscxd *cxd;			                               /* NS context */
    text *address = hdl->service_tnshdl;                     /* service name */
    tnsres *tres = hdl->res_tnshdl;                           /* error stack */
    dvoid *gbhp;		                       /* NS per user global */
    sword retcode = 0;
    dvoid *npd;			                               /* NPD global */
    ub1 what;

    /* if the global handle has not been allocated, can't call this function */
    if ( ctx == (tnsgblctx *)NULL)
    {
	/* Signal error, write error message into trace stream */
	return((sword)HDLUNINI_TNSAPIE);
    }

    gbhp = (dvoid *)ctx->gbh_tnsgblctx;
    npd = ctx->npd_tnsgblctx;

    {
        NLDTOTFNAME("tnsrecv");
        NLOTRDEFINE(NLOTRFUNCID(923), npd,(ub4*) NULL,
                    NLDTTNSAPI, NLDTTDUMMY, NLDTTDUMMY);
        NLTRENTER();
        NLOTRENTER();
	/*
	 * if this is the first call to tnsrev(), no cxd has been allocated yet
         */
	if ( hdl->cxd_tnshdl == (nscxd *)NULL)
	{
	    /*
	     * Check if this is the client or the server,
	     * Server: try to establish the connection.
	     * Client: it is an error since client must first call tnssend()
	     */
	    if ( hdl->svr_tnshdl )
	    {
		/* This is the server */
		if ((cxd = (nscxd *)snlmalc(1, sizeof(nscxd)))==(nscxd *)NULL)
		{
		    retcode = (sword) MALFAIL_TNSAPIE;
		}
		else
		{ 
		    nsinfo info;
		    CLRSTRUCT(info);
		    NSINFENABLENA(&info);

		    /*
		     * In the first release, we will require the user
		     * to have listener up and and running in order to
		     * user this API. In the future releases, we are
		     * going to support the situation in which
		     * listener is not present. However, for the sake
		     * of testing, implementations for both cases have
		     * been done.  
		     */
		    if (address)
		    {
#ifdef NEVER
/* removed to improve coverage, can't be executed... */
			/* Since address is not NULL, this is the listening 
			   endpoint */
			nscnd cnda;
			nscxd lsncxd;
          
			CLRSTRUCT(cnda);
			NSSETCND(&cnda, address);

			if (nslisten(gbhp, 
				     &lsncxd, 
				     &cnda, 
				     &info, 
				     NULLP(nsres)) < 0)
			{
                          /*
                           * listen failed.
                           * write error into trace stream
                           * set up tres from cxd->nscxdres
                           */ 
                          NLTRUSR((NLTRTRC, ns306trc));
                          NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(306)));
                          /*
                           * though this is actually the error for
                           * failing to listen it is still put into
                           * tres just in case we want to check it
                           * later on 
                           */
                          CPSTRUCT(tres->ns_tnsres, lsncxd.nscxdres);
                          retcode = tres->res_tnsres = (sword) LSNFAIL_TNSAPIE;
			}
			/* nslisten succeed */
			else if (nsanswer(gbhp, cxd, NULLP(nscnd), &info, 
					  NULLP(nsres), &lsncxd) < 0)
			{
                          /*
                           * answer failed
                           * set up tres from cxd->nscxdres
                           */
                          NLTRUSR((NLTRTRC, ns307trc));
                          NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(307)));
                          CPSTRUCT(tres->ns_tnsres, cxd->nscxdres);
                          DISCARD nsdisc(&lsncxd, 0);
                          retcode = tres->res_tnsres = (sword) ANSFAIL_TNSAPIE;
			}
#endif
		    } 
		    /* inherit connection from listener */
		    else
		    {
			/*
			 * Inherit the connection from the listener
			 */
			if (nsinherit(gbhp, 
				      cxd, 
				      NULLP(nscnd), 
				      NULLP(nscnd), 
				      &info, 
				      NULLP(nsres)) < 0 )
			{
                          /*
                           * server failed in inheriting connection
                           * from listener set tres error stack from
                           * cxd->nscxdres 
                           */
                          NLTRUSR((NLTRTRC, ns308trc));
                          NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(308)));
                          CPSTRUCT(tres->ns_tnsres, cxd->nscxdres);
                          retcode = tres->res_tnsres = (sword) INHFAIL_TNSAPIE;
			}
		    } /* if (address) */

		    if (nsaccept(cxd, NULLP(nscnd)) != 0)
		    {
                      /*
                       * server failed in accepting connection.
                       * Set tres from cxd->nscxdres
                       */
                      NLTRUSR((NLTRTRC, ns309trc));
                      NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(309)));
                      CPSTRUCT(tres->ns_tnsres, cxd->nscxdres);
                      retcode = tres->res_tnsres = (sword) ACPTFAIL_TNSAPIE;
                      DISCARD nsdisc(cxd, 0);
		    }   
		    else
		    {
			hdl->cxd_tnshdl = cxd;
		    }
		} /* malloc cxd */
	    } /* if (hdl->svr_tnshdl) */
	    else
	    {
		/*
		 * This is the client. Client must NOT call tnsrecv()
		 * after tnsopen() Signal error and write error info
		 * into trace stream.  
		 */
                NLTRUSR((NLTRTRC, ns310trc));
                NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(310)));
		retcode = tres->res_tnsres = (sword) INVCLIOP_TNSAPIE;
	    }
	} /* if (cxd_tnshdl == NULL) */

	/* Set connection into nonblocking mode if it has been requested
	   by a call before the connection was actually established -
	   the default was blocking */
	if (!retcode && (hdl->io_tnshdl == IO_TNSHDL_NONBLOCKING))
	{
          retcode = tnsdcntl((sword) TNSAPICNONBLOCKING, hdl);
	}

	/* if no error so far */
	if ( !retcode ) 
	{
	    /*
	     * Connections has been established. Just receive the data
	     */
	    if ((retcode = nsrecv(hdl->cxd_tnshdl, &what, data, length, 0)) 
		!= 0)
	    {
		if (hdl->cxd_tnshdl->nscxdres.nsresns == NSEWOULDBLOCK)
		{
                  /* we're in nonblocking mode and would block... */
                  retcode = tres->res_tnsres = (sword) WOULDBLOCK_TNSAPIE;
		}
		else
		{
                  /*
                   * Receive failed, set up the tres from cxd->nscxdres;
                   */
                  NLTRUSR((NLTRTRC, ns311trc));
                  NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(311)));
                  retcode = tres->res_tnsres = (sword) RECVFAIL_TNSAPIE;
		}
		CPSTRUCT(tres->ns_tnsres, hdl->cxd_tnshdl->nscxdres);
	    }
	}
	NLTREXIT();
        NLOTREXIT();
    }
    return(retcode);
}

/*
 * In the first release, only the control operation to return the error
 * status will be implemented. However, this could be easily extended.
 */
STATICF sword tnsdcntl VAFD((opcode, hdl VAAELLIPSIS))
sword opcode VAFDAD
tnshdl *hdl VAFDAD
VAFDELLIPSIS
{
    tnsgblctx *ctx = (tnsgblctx *)hdl->gbl_tnshdl; /* TNS API global context */
    va_list varp;
    sword retcode = 0;
    dvoid *npd;
    dvoid *gbhp;		                       /* NS per user global */
    tnsres *tres = hdl->res_tnshdl;                           /* error stack */
    ub4 opt;
    
    /* if the global handle has not been allocated, cannot call this 
       function */
    if ( ctx == (tnsgblctx *)NULL)
    {
	/* Signal error, write error message into trace stream */
	return((sword)HDLUNINI_TNSAPIE);
    }

    gbhp = (dvoid *)ctx->gbh_tnsgblctx;
    npd = ctx->npd_tnsgblctx;
    {
        NLDTOTFNAME("tnscontrol");
        NLOTRDEFINE(NLOTRFUNCID(924), npd,(ub4*) NULL,
                    NLDTTNSAPI, NLDTTDUMMY, NLDTTDUMMY);
	NLTRENTER();
        NLOTRENTER();
    
	/*
	 * The handle must have been allocated already.
	 */
	VASTART(varp, hdl);

	switch(opcode)
	{
	    /* this is for future enhancement, take out in first release */
	    /*   
	       case TNSAPIOLSN:
	       hdl->svr_tnshdl = TRUE;
	       break;
	       */
	  case TNSAPICNONBLOCKING:
	    if (hdl->cxd_tnshdl)
	    {
              /* if we're connected, do this now.  Else, just set the 
                 "nonblocking" flag and this will be implicitly done
                 first time we get connected */
              opt = NSLDONTBLOCK;
              if (nscontrol(hdl->cxd_tnshdl, NSCSETL, (dvoid *)&opt) != 0)
	      {
                NLTRUSR((NLTRTRC, ns312trc));
                NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(312)));
                CPSTRUCT(tres->ns_tnsres, hdl->cxd_tnshdl->nscxdres);
                retcode = tres->res_tnsres = (sword) CTLFAIL_TNSAPIE;
              } 
	    }
	    hdl->io_tnshdl = IO_TNSHDL_NONBLOCKING;
	    break;

	  case TNSAPICBLOCKING:
	    if (hdl->cxd_tnshdl)
	    {
              opt = NSLDONTBLOCK;
              if (nscontrol(hdl->cxd_tnshdl, NSCCLRL, (dvoid *)&opt) != 0)
              {
                NLTRUSR((NLTRTRC, ns313trc));
                NSTRCOTUSR((NLOTRTRC, NSTRCMSGNO(313)));
                CPSTRUCT(tres->ns_tnsres, hdl->cxd_tnshdl->nscxdres);
                retcode = tres->res_tnsres = (sword) CTLFAIL_TNSAPIE;
              } 
	    }
	    hdl->io_tnshdl = IO_TNSHDL_BLOCKING;
	    break;
	    
	  default:
	    /*
	     * All the other control functions won't be implemented in
	     * the first release.  
	     */
	    retcode = hdl->res_tnshdl->res_tnsres = (sword) INVCTL_TNSAPIE;
	    break;
	}
	va_end(varp);
	NLTREXIT();
        NLOTREXIT();
    }
    return(retcode);
}

/* ENABLE check_naming */


