/*		TCP/IP based Transport Service for RPC		       ts_tcp.c
**		======================================
**
**  This module provides a transport service for the RPC system developed
**  by CERN/DD Online group in 1986-90.
**
**  This particular module uses the existing TCP/IP protocol.
**  See the RPC User Manual for details of address syntax.
**
**  See also the RPC internals manual, which describes how modules
**  similar to this one work, also giving flow charts and message formats.
**
**  Authors:
**	Tim Berners-Lee CERN/DD
**
**  History:
**	30 Apr 90   Code extracted from general ts.c module. TBL
**	18 Jun 90   Modified to work on the cray, too.
**	 5 Mar 91   Transient connection code put in and tested on VAX/ultrix
**	24 May 91   Moved tcp_connect() before tcp_write() (fwd referece)
**
**  Compilation switches:
**
**	vms	    For VMS/Wollongong TCP. Works with VMS/SRI Multinet too.
**
**	SELECT	    The select() call is supported by the socket library.
**		    If not, can't have multiple clients.
*/


/*	Module parameters:
**	-----------------
**
**  These may be undefined and redefined by syspec.h
*/
#define LISTEN_BACKLOG 2	/* Number of pending connect requests (TCP)*/

#define INET_VMS_ERROR	0x08558002  /* Add to (errno<<3) to make VMS error */

#define WILDCARD '*'	    /* Wildcard used in addressing */

#define NETCLOSE close	    /* Routine to close a TCP-IP socket		*/
#define NETREAD  read	    /* Routine to read from a TCP-IP socket	*/
#define NETWRITE write	    /* Routine to write to a TCP-IP socket	*/

# define FIRST_TCP_PORT 5000	/* First port number to try to bind to  */
# define LAST_TCP_PORT  5999	/* Last one to try before giving up     */


#include <stdio.h>
#include "syspec.h"

#define TS			/* Require visibility of TS interface */
#define TS_INTERNALS		/* Require visibility of TS internals */
#include "rpcrts.h"		/* Define all general RPC data structures   */

#include "rpc_code.h"		/* Coding convention macros */

/*
**      Definitions for pointers
*/
#define NIL_MESSAGE (rpc_message *)0
extern char *malloc();
extern void free();
extern char *strncpy();


/*                      External Routines
**			-----------------
*/
 
extern void rpc_new();                  /* Allocate rpc_message */
extern void rpc_dispose();              /* Deallocate rpc_message */
extern void rpc_get_string();		/* Get Pascal or C string parameter */
 
#ifdef AST				/* Always used by CATS !!!!!! */
extern void sys_setast();		/* En/Disable ASTs */
rpc_status ts_when_receive();           /* forward (ASTs only) */
#endif


/*			Forward Declarations
**			--------------------
*/
#ifdef __STDC__
PRIVATE rpc_status tcp_close(socket_type soc);
#else
PRIVATE rpc_status tcp_close();
#endif

/*	Encode INET status (as in sys/errno.h)			   inet_status()
**	------------------
**
**    The error number is put into the "message number" field,
**    and given a facility number 57 hex, with bits "Non-VMS" and
**   "Local error message" set, and severity "ERROR".
*/
#ifdef vms
extern int uerrno;	/* Deposit of error info (as perr errno.h) */
extern int vmserrno;	/* Deposit of VMS error info */
extern volatile noshare int errno;  /* noshare to avoid PSECT conflict */
#else
#ifndef VM
extern int errno;
#endif
#endif
rpc_status inet_status(where)
    char    *where;
{
    CTRACE(tfp, "RPC/TCP: Error %d in `errno' after call to %s() failed.\n",
	    errno,  where);
#ifdef vms
    CTRACE(tfp, "         Unix error number (uerrno) = %ld dec\n", uerrno);
    CTRACE(tfp, "         VMS error (vmserrno)       = %lx hex\n", vmserrno);
    if (vmserrno) {
	if (vmserrno & 0x8000)
	    return (vmserrno | INET_VMS_ERROR);	/* Add facility & severity */
	else
	    return vmserrno;
    }
#else
    return ((unsigned long)errno<<3) | INET_VMS_ERROR;
#endif
}

/*	Return RPC address of this socket		        tcp_my_address()
**	---------------------------------
**
**  This routine returns a string indicating the RPC address of the socket.
**
**  On entry,
**	soc	    should be a valid socket pointer
**	addrstr	    points to a buffer to contain a string.
**	addrlen    indicates the length of the string buffer
**
**  On exit,
**	*addrstr    has the contact address for an external node wishing to contact
**		    this socket written into it, zero terminated, without the
**		    protocol on the end.
*/
#ifdef __STDC__
PRIVATE rpc_status tcp_my_address(
    socket_type	    soc,
    char *          addrstr,
    int		    addrlen)
#else
PRIVATE rpc_status tcp_my_address(soc, addrstr, addrlen)
    socket_type	    soc;
    char *	    addrstr;
    int		    addrlen;
#endif
{   rpc_name	local_string;
    rpc_name	node_name;

    gethostname(node_name, RPC_NAME_LENGTH);
    sprintf(local_string,
	"%s:%d",
	node_name,
	ntohs(soc->mdp.soc_tcp.soc_address.sin_port));

    if (strlen(local_string)+1 > addrlen) return RPC_S_BUFFER_OVERFLOW;

    strcpy(addrstr, local_string);
    return RPC_S_NORMAL;
} /* tcp_my_address */

/*	Return RPC address of peer		              tcp_peer_address()
**	--------------------------
**
**  On entry,
**	soc	    should be a valid socket pointer
**	address	    points to a buffer to contain an rpc_name.
**
**  On exit,
**	*address    has the contact address for the peer node.
**		    blank filled.
**		    26 useful characters in the address.
**
**  Bug: Peer node name not extracted: Number only given.
*/
#ifdef __STDC__
PRIVATE rpc_status tcp_peer_address(
    socket_type	    soc,
    char * 	    addrstr,
    int		    addrlen)
#else
PRIVATE rpc_status tcp_peer_address(soc, addrstr, addrlen)
    socket_type	    soc;
    char *	    addrstr;
    int		    addrlen;
#endif
{   int		i;
    rpc_name	local_string;
    unsigned char * p = (unsigned char *)&soc->mdp.soc_tcp.soc_address.sin_addr;

    sprintf(local_string,
	"%d.%d.%d.%d:%d",
	(int)p[0], (int)p[1], (int)p[2], (int)p[3],
	ntohs(soc->mdp.soc_tcp.soc_address.sin_port));

    if (strlen(local_string)+1 > addrlen) return RPC_S_BUFFER_OVERFLOW;

    strcpy(addrstr, local_string);
    return RPC_S_NORMAL;
}


/*	Connect a parsed socket
**	-----------------------
*/
#ifdef __STDC__
PRIVATE rpc_status tcp_connect(socket_type soc)
#else
PRIVATE rpc_status tcp_connect(soc)
    socket_type	soc;
#endif
{
    if (soc->soc_protocol->reliable)
	soc->mdp.soc_tcp.soc_s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    else
	soc->mdp.soc_tcp.soc_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  
    if (soc->mdp.soc_tcp.soc_s<0)
	return inet_status("socket");
	
    CTRACE(tfp, "RPC/IP: Opened socket number %d\n", soc->mdp.soc_tcp.soc_s);
    if (connect(soc->mdp.soc_tcp.soc_s,
		&soc->mdp.soc_tcp.soc_address,
		sizeof(soc->mdp.soc_tcp.soc_address))<0)
	return inet_status("connect");

    soc->mdp.soc_tcp.soc_connected = TRUE;
    CTRACE(tfp, "RPC/TCP: Socket connected OK.\n");
    return RPC_S_NORMAL;
}


/*      Send a message over the given socket			    tcp_write()
**	------------------------------------
**
** On entry,
**      pmessage        points to a pointer to the message, with fields:
**	    m_next	    (undefined)
**	    m_socket	    valid
**	    m_index	    Length of message to be sent in bytes,
**			    exclusing any bytes needed for the protocol.
**	    m_status	    (undefined)
**
** On exit,
**
**      The pointer to the message may have been changed. In this case,
**      it will point to a message with the m_socket and m_status fields
**      set, but without the data.  This routine may therefore be DESTRUCTIVE.
**	In fact, this implementation is not.
**
**	returns:	Status of operation.
**
**	A failure to transmit is returned as a bad status except when it is
**	received on a slave socket which returns zero bytes sent, in which
**	case it is assumed that the write was ok but that the peer exited with
**	the data before the acknowledgement could get back.
*/
#ifdef __STDC__
PRIVATE rpc_status tcp_write(rpc_message_pointer *ppmessage)
#else
PRIVATE rpc_status tcp_write(ppmessage)
    rpc_message_pointer *ppmessage;
#endif
{
    rpc_status		status;
    int tcp_status;	/* Number of bytes in a slice, or <0 => error. */
    int done;	/* Number of bytes transferred so far */

    register rpc_message *mes = *ppmessage;
    register struct socket_struct *soc = mes->m_socket;
    unsigned int bufsize = mes->m_index;
    
    rpc_byte *p = mes->body.rpc_b;

    mes->m_status = check_socket(soc);
    if (BAD(mes->m_status)) return status;

    if (!soc->mdp.soc_tcp.soc_connected){
    	status = tcp_connect(soc);		/* Reconnect disconnected socket */
        if (!soc->mdp.soc_tcp.soc_connected)	/* If still disconnected, forget it */
	    return status;
    }
    
    if (rpc_trace) {
	    UTRACE(tfp, "RPC/TCP: Sending message, ");
	    hex_message(mes, mes->m_index);
    }
    
/*  Store the length of the message just before it:
*/
    *--p = (rpc_byte)(bufsize & 0xff);
    *--p = (rpc_byte)((bufsize >> 8) & 0xff);
    *--p = (rpc_byte)((bufsize >> 16) & 0xff);
    *--p = (rpc_byte)((bufsize >> 24) & 0xff);

/*  The following didn't work on the Cray :
    mes->header.ip.length = htonl((long)bufsize); Store size just before msg */

    mes->m_status = RPC_S_NORMAL;		  /* So far. */

    for(done=0; done<4+bufsize;)
    {
       tcp_status = NETWRITE(soc->mdp.soc_tcp.soc_s,
		p+done,
		bufsize+4);

        if (tcp_status<=0) {
	    mes->m_status = inet_status("netwrite");
	    break;
	}
        done = done+tcp_status;
	CTRACE(tfp, "RPC/TCP: netwrite() wrote %d bytes out of %d\n",
			    tcp_status, bufsize+4);
    }

    return mes->m_status;

} /* tcp_write */

/*      Receive message						      tcp_read()
**	---------------
**
**  This procedure waits for a message to arrive on a socket and returns the
**  message. A suitable message buffer is passed to the routine, but a
**  different one may be returned. If the socket passed is the master socket
**  for mutiple connections, then the message recieved will carry the socket
**  number of the slave socket on which the message actually arrived.
**
** On entry:
**      ppmessage       is pointer to pointer to buffer, fields:
**	    m_socket	    The socket to be used - possibly a master socket
**	    m_index	    don't care
**	    m_status	    don't care
**	    m_next	    don't care
**
**      timeout         -1 for infinite, 0 for poll, else in units of 10ms
**
** On exit,
**	returns		The status of the operation.
**      *pmessage       may have been modified to point to another message
**                      from the pool. Fields:
**	    m_socket	    The (possible slave) socket the msg came on.
**	    m_status	    The status of the operation (= return value).
**	    m_index	    The number of bytes recieved if good status
**	    m_next	    junk.
**
*/
#ifdef __STDC__
PRIVATE rpc_status tcp_read(
        rpc_message_pointer *ppmessage,
        int		    timeout)
#else
PRIVATE rpc_status tcp_read(ppmessage, timeout)
        rpc_message_pointer *ppmessage;
        int		    timeout;
#endif
{
    int tcp_status;			/* Number of bytes in a slice, or <0 => error */
    rpc_long	length;		/* length of the message received */
    rpc_long	done;		/* Number of bytes transferred so far */
    register socket_type   rsoc; /* Socket to actually receive on */

    rpc_message * mes = *ppmessage;	/* Buffer provided for data */
    socket_type	soc = mes->m_socket;	/* Socket read requested on */
    rpc_byte *	p;			/* Pointer to message including length*/

    for(;;) {

/*  If it's a master socket, then find a slave:
*/
	if (soc->mdp.soc_tcp.soc_role == master) {
#ifdef SELECT
	    fd_set		read_chans;
	    fd_set		write_chans;
	    fd_set		except_chans;
	    int			nchans;	    /* Number of channels for SELECT */
	    int			nfound;	    /* Number of ready channels */
	    socket_type		scan;
	    struct timeval	max_wait;   /* timeout in form for select() */

	    FD_ZERO(&write_chans);	    /* Clear the write mask */
	    FD_ZERO(&except_chans);	    /* Clear the exception mask */


/*  If timeout is required, the timeout structure is set up. Otherwise
**  (timeout<0) a zero is passed instead of a pointer to the struct timeval.
*/
	    if (timeout>=0) {
		max_wait.tv_sec = timeout/100;
		max_wait.tv_usec = (timeout%100)*10000;
	    }

	    for (;;) {	/* Loop while connections keep coming */

/*  The read mask expresses interest in the master channel for incomming
**  connections) or any slave channel (for incomming messages).
*/
		FD_ZERO(&read_chans);	    /* Set upthe read mask */
		FD_SET(nchans=soc->mdp.soc_tcp.soc_s, &read_chans);
		for(scan=soc->mdp.soc_tcp.soc_slaves;
			    scan; scan=scan->soc_next) {
		    int chan=scan->mdp.soc_tcp.soc_s;
		    FD_SET(chan, &read_chans);
		    if (nchans<chan) nchans = chan; /* Find maximum chan # */
		}

/*  Wait for incoming connection or message
*/
		CTRACE(tfp,"RPC/TCP: Waiting for connection or message\n");
		FLUSH_TRACE
		nfound=select(nchans+1, &read_chans,
		    &write_chans, &except_chans,
		    timeout >= 0 ? &max_wait : 0);

		if (nfound<0) return inet_status("accept");
		if (nfound==0) return RPC_S_TIMEOUT;

/*  If an incomming connection has arrived, accept the new socket:
*/
		if (FD_ISSET(soc->mdp.soc_tcp.soc_s, &read_chans)) {
		    rsoc = (socket_type)malloc(sizeof(socket_descriptor));
						    /* Make new slave */

		    rsoc->soc_next = soc->mdp.soc_tcp.soc_slaves;  /* list */
		    soc->mdp.soc_tcp.soc_slaves = rsoc;
		    rsoc->mdp.soc_tcp.soc_master = soc;

		    rsoc->soc_protocol = soc->soc_protocol;
		    rsoc->mdp.soc_tcp.soc_role = slave;
		    rsoc->mdp.soc_tcp.soc_slaves = NULL;
		    rsoc->mdp.soc_tcp.soc_connected = TRUE;

		    rsoc->mdp.soc_tcp.soc_addrlen=
			sizeof(rsoc->mdp.soc_tcp.soc_address);
		    CTRACE(tfp, "RPC/TCP: New incomming connection:\n");
		    FLUSH_TRACE
		    tcp_status = accept(soc->mdp.soc_tcp.soc_s,
				    &rsoc->mdp.soc_tcp.soc_address,
				    &rsoc->mdp.soc_tcp.soc_addrlen);
		    if (tcp_status<0)
			return inet_status("accept");
		    rsoc->mdp.soc_tcp.soc_s = tcp_status;  /* socket number */
		    CTRACE(tfp, "RPC/TCP: Accepted new socket %d\n",
			tcp_status);

		    nfound--;

		} /* If new connection */

/*  If a message has arrived on one of the channels, take that channel:
*/
		if(nfound) {
		    for(rsoc=soc->mdp.soc_tcp.soc_slaves;
				 rsoc; rsoc=rsoc->soc_next)
			if(FD_ISSET(rsoc->mdp.soc_tcp.soc_s, &read_chans))
			    break;
		    
		    break;			/* Found input socket */
		} /* if message waiting */

	    } /* loop on event */

#else	/* SELECT not supported */

	    if (!(soc->mdp.soc_tcp.soc_slaves)) { /* No slaves: must accept */

		rsoc = (socket_type)malloc(sizeof(socket_descriptor));
					    	/* Make new slave */

		rsoc->soc_next = soc->mdp.soc_tcp.soc_slaves;  /* put on list */
		soc->mdp.soc_tcp.soc_slaves = rsoc;
		rsoc->mdp.soc_tcp.soc_master = soc;

		rsoc->soc_protocol = soc->soc_protocol;
		rsoc->mdp.soc_tcp.soc_role = slave;	/* Set fields up */
		rsoc->mdp.soc_tcp.soc_slaves = NULL;
		rsoc->mdp.soc_tcp.soc_connected = TRUE;

		rsoc->mdp.soc_tcp.soc_addrlen=
		    sizeof(rsoc->mdp.soc_tcp.soc_address);
		CTRACE(tfp, "RPC/TCP: Waiting for incomming connection...\n");
		FLUSH_TRACE
		tcp_status = accept(soc->mdp.soc_tcp.soc_s,
				&rsoc->mdp.soc_tcp.soc_address,
				&rsoc->mdp.soc_tcp.soc_addrlen);
		if (tcp_status<0)
		    return inet_status("accept");
		rsoc->mdp.soc_tcp.soc_s = tcp_status;	/* socket number */
		CTRACE(tfp, "RPC/TCP: Accepted socket %d\n", tcp_status);
	    } /* end if no slaves */

	    rsoc = soc->mdp.soc_tcp.soc_slaves;	/* Take the only slave */
#endif
	} else /* Not master */

	    rsoc = soc;

/*  Read the message now on whatever channel there is:
*/
	CTRACE(tfp,"RPC/TCP: Reading socket %d\n", rsoc->mdp.soc_tcp.soc_s);
	FLUSH_TRACE
	p = mes->body.rpc_b-4;
	for(done=0; done<4;) {
	    tcp_status = NETREAD(rsoc->mdp.soc_tcp.soc_s,	/* Get length */
		    p+done,
		    4);
	    if (tcp_status <=0) goto connection_fail;
	    done = done+tcp_status;
	    CTRACE(tfp,
		"RPC/TCP: netread() read %d bytes of header\n", tcp_status);
	}

/*	Won't work on Cray:
	length = ntohl(mes->header.ip.length);	     total size of message */

	length = *p++;			/* Unpack length of the front */
	length = (length<<8)+(*p++);
	length = (length<<8)+(*p++);
	length = (length<<8)+(*p++);

	if (length<2 || length>RPC_BUFFER_SIZE) {
	    CTRACE(tfp, "RPC/TCP: Message length received was %x hex! ***\n",
								length);
	    (*ppmessage)->m_status = RPC_S_BAD_MESSAGE_HEADER;
	    return RPC_S_BAD_MESSAGE_HEADER;    /* framing else overflow */
	}

	done = 0;
	while (done < length) {
	    tcp_status = NETREAD(rsoc->mdp.soc_tcp.soc_s,	/* Get a slice */
		    &mes->body.rpc_b[done],
		    length-done);
	    if (tcp_status <=0) goto connection_fail;
	    done = done+tcp_status;
	    CTRACE(tfp, "RPC/TCP: netread() read %d bytes out of %d\n",
		tcp_status, length);
	}

	mes->m_index = length;		    /* Return actual length received */

	mes->m_socket = rsoc;	    /* Fill in back pointer to socket */
	mes->m_status = RPC_S_NORMAL;

	if (rpc_trace) {
		UTRACE(tfp, "RPC/TCP: Received message, ");
		hex_message(mes, mes->m_index);
	}

	if (soc->mdp.soc_tcp.soc_role == transient) {
	    CTRACE(tfp, "RPC/TCP: Closing transient socket %d\n",
	    	 soc->mdp.soc_tcp.soc_s);
	    soc->mdp.soc_tcp.soc_connected = FALSE;
	    if (NETCLOSE(soc->mdp.soc_tcp.soc_s) < 0)
		return inet_status("netclose of transient connection");
	}

	return RPC_S_NORMAL;

/*  If tcp_status <=0 0:
 */
    connection_fail:
        if (tcp_status<0) return inet_status("netread");
	else {
	    CTRACE(tfp, "RPC/TCP: Socket %d disconnected by peer\n",
		rsoc->mdp.soc_tcp.soc_s);
	    if (soc->mdp.soc_tcp.soc_role==master) {
		(void) tcp_close(rsoc);
	    } else
		return INET_VMS_ERROR+(ECONNRESET<<3);
        }

    }; /* for loop */
/*NOTREACHED*/
}


/*      Asynchronous Receive
**	--------------------
**
** On entry,
**      pmessage        is the address of a suitable message buffer
**      action          is the address of the routine to be called
**      user_1          is not used at the moment.
**
** On exit,
**      the parameters are stored only.  Exits immediately.
**
** On arrival of the next message:
**      The routine *action() is called, and passed the message as a parameter.
**      If the buffer *pmessage is not used, it will be reused elsewhere and
**      a different buffer passed to *action().
*/
#ifdef __STDC__
PRIVATE rpc_status tcp_aread(
        rpc_message_pointer     pmessage,
        rpc_pointer		action,
        rpc_integer		user_1)
#else
PRIVATE rpc_status tcp_aread(pmessage, action, user_1)
        rpc_message_pointer     pmessage;
        rpc_pointer		action;
        rpc_integer		user_1;
#endif
{
    return RPC_S_NOT_IMPLEMENTED;
} /* tcp_aread */



/*	Open a transport connection				      tcp_open()
**	---------------------------
**
**  On entry,
**	soc points to a recently created socket, fields as follows:
**
**	    soc_last_call_sent	    NULL
**	    soc_last_reply_sent	    NULL
**	    soc_next-call_tid	    0
**	    soc_protocol		    valid pointer to protocol structure
**
**	    and otherwise uninitialised.
**
**	tsap	points to a zero-terminated string representing the address
**		(with no .TCP on it). See the RPC User Maual for details of the
**		syntax of this string.
**
**  TSAP Syntax:
**		node_name | byte.byte.byte.byte  { : portnumber }
**
**	e.g.	100.1.2.5:4
**		uxocb1:53
**
** An omitted port number causes the creation of a new port.
** An omitted node number or name causes any connecting node to be serviced.
** A completely null tsap indicates a passive server started by daemon.
*/

#ifdef __STDC__
PRIVATE rpc_status tcp_open(socket_type soc, char * tsap)
#else
PRIVATE rpc_status tcp_open(soc, tsap)
    socket_type	soc;
    char	*tsap;
#endif
{

    BOOLEAN dynamic_allocation;		    /* Search for free port? */
    rpc_status	status = RPC_S_NORMAL;	    /* (unless something goes wrong)*/
    soc->mdp.soc_tcp.soc_slaves = 0;	    /* Has no slaves by default	    */
    soc->mdp.soc_tcp.soc_master = 0;	    /* Has no master by default	    */

/*  Deal with PASSIVE socket:
**
**	A passive TSAP is one which has been created by the inet daemon.
**	It is indicated by a void TSAP name.  In this case, the inet
**	daemon has started this process and given it, as stdin, the connection
**	which it is to use.
*/
    if (*tsap == 0) {			/* void tsap => passive */

	dynamic_allocation = FALSE;		/* not dynamically allocated */
	soc->mdp.soc_tcp.soc_role = passive;	/* Passive: started by daemon */

#ifdef vms
	{   unsigned short channel;	    /* VMS I/O channel */
	    struct string_descriptor {	    /* This is NOT a proper descriptor*/
		    int size;		    /*  but it will work.	      */
		    char *ptr;		    /* Should be word,byte,byte,long  */
	    } sys_input = {10, "SYS$INPUT:"};
	    rpc_status	status;		    /* Returned status of assign */
	    extern rpc_status sys$assign();

	    status = sys$assign(&sys_input, &channel, 0, 0);
	    soc->mdp.soc_tcp.soc_s = channel;	/* The channel is stdin */
	    CTRACE(tfp, "RPC/IP: Opened PASSIVE socket %d\n", channel);
	    return status;
	}	
#else
	soc->mdp.soc_tcp.soc_s = 0;	    /* The channel is stdin */
	CTRACE(tfp, "RPC/IP: PASSIVE socket 0 assumed from inet daemon\n");
	return RPC_S_NORMAL;
#endif

/*  Parse the name (if not PASSIVE)
*/
    } else {				/* Non-void TSAP */

	register char   *p = tsap;	/* pointer to string */
		 char   *q;
	struct hostent  *phost;	    /* Pointer to host - See netdb.h */

	register struct sockaddr_in* sin = &soc->mdp.soc_tcp.soc_address;

/*  Set up defaults:
*/
	sin->sin_family = AF_INET;	    /* Family = internet, host order  */
	sin->sin_port = 0;		    /* Default: new port,    */
	dynamic_allocation = TRUE;	    /*  dynamically allocated */
	soc->mdp.soc_tcp.soc_role = single; /* Single by default */

/*  Check for special characters:
*/
	if (*p == WILDCARD) {		/* Any node */
	    soc->mdp.soc_tcp.soc_role = master;
	    p++;

	} else if (*p == '#') {			/* Transient connections */
	    soc->mdp.soc_tcp.soc_role = transient;
	    p++;
	}

/*  Strip off trailing port number if any:
*/
	for(q=p; *q; q++)
	    if (*q==':') {
		*q++ = 0;		/* Terminate node string */
		sin->sin_port = htons((unsigned short)parse_cardinal(
					    &status, &q, (unsigned int)65535));
		if (!(status &1)) return status;
		if (*q) return RPC_S_SYNTAX_4;  /* Junk follows port number */
		dynamic_allocation = FALSE;
		break;	    /* Exit for loop before we skip the zero */
	    } /*if*/

/* Get node name:
*/
	if (*p == 0) {
	    sin->sin_addr.s_addr = INADDR_ANY; /* Default: any address */

	} else if (*p>='0' && *p<='9') {   /* Numeric node address: */
	    sin->sin_addr.s_addr = inet_addr(p); /* See arpa/inet.h */

	} else {		    /* Alphanumeric node name: */
	    phost=gethostbyname(p);	/* See netdb.h */
	    if (!phost) {
		CTRACE(tfp, "RPC/IP: Can't find internet node name `%s'.\n",p);
		return inet_status("gethostbyname");  /* Fail? */
	    }
	    memcpy(&sin->sin_addr, phost->h_addr, phost->h_length);
	}

	CTRACE(tfp, 
	    "RPC/TCP: Parsed address as port %4x, inet %d.%d.%d.%d\n",
		    (unsigned int)ntohs(sin->sin_port),
		    (int)*((unsigned char *)(&sin->sin_addr)+0),
		    (int)*((unsigned char *)(&sin->sin_addr)+1),
		    (int)*((unsigned char *)(&sin->sin_addr)+2),
		    (int)*((unsigned char *)(&sin->sin_addr)+3));

    } /* scope of p */

    
/*  Master socket for server:
*/
    if (soc->mdp.soc_tcp.soc_role == master) {


/*  Create internet socket
*/
	if (soc->soc_protocol->reliable)
	    soc->mdp.soc_tcp.soc_s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	else
	    soc->mdp.soc_tcp.soc_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      
	if (soc->mdp.soc_tcp.soc_s<0)
	    return inet_status("socket");
	CTRACE(tfp, "RPC/IP: Opened master socket number %d\n",
		 soc->mdp.soc_tcp.soc_s);

/*  If the port number was not specified, then we search for a free one.
*/
	if (dynamic_allocation) {
	    unsigned short try;
	    for (try=FIRST_TCP_PORT; try<=LAST_TCP_PORT; try++) { 
		soc->mdp.soc_tcp.soc_address.sin_port = htons(try);
		if (bind(soc->mdp.soc_tcp.soc_s,
			&soc->mdp.soc_tcp.soc_address,
			sizeof(soc->mdp.soc_tcp.soc_address)) == 0)
		    break;
		if (try == LAST_TCP_PORT)
		    return inet_status("bind");
	    }
	    CTRACE(tfp, "RPC/IP:  Bound to port %u.\n",
		    ntohs(soc->mdp.soc_tcp.soc_address.sin_port));
	} else {					/* Port was specified */
	    if (bind(soc->mdp.soc_tcp.soc_s,
		     &soc->mdp.soc_tcp.soc_address,
		     sizeof(soc->mdp.soc_tcp.soc_address))<0)
		return inet_status("bind");
	}
	if (listen(soc->mdp.soc_tcp.soc_s, LISTEN_BACKLOG)<0)
	    return inet_status("listen");

	CTRACE(tfp, "RPC/TCP: Master socket(), bind() and listen() all OK\n");

	return RPC_S_NORMAL;

/*  Transient connection socket:
*/
    } else if (soc->mdp.soc_tcp.soc_role == transient) {

	soc->mdp.soc_tcp.soc_connected = FALSE;
	CTRACE(tfp, "RPC/TCP: Socket not created yet: transient connections\n");
	return RPC_S_NORMAL;

/*  Normal single socket:
*/
    } else { /* Normal single */

	return tcp_connect(soc);
	
    } /* if transient */
    /*NOTREACHED*/
} /* tcp_open */


/*	Close a Transport Connection				     tcp_close()
**	----------------------------
**
**  On entry,
**	soc	Must be a socket opened by tcp_open().
**
**  On exit,
**	returns	a status of the close operation.
*/

#ifdef __STDC__
PRIVATE rpc_status tcp_close(socket_type soc)
#else
PRIVATE rpc_status tcp_close(soc)
    socket_type	soc;
#endif
{
    register socket_type scan;

/*  Close slaves:
 */
    while (scan = soc->mdp.soc_tcp.soc_slaves)
	(void)tcp_close(scan);			    /* dispose */

/*  Close this socket itself: First, remove it from any queue of slaves
 */
    if (scan = soc->mdp.soc_tcp.soc_master) {
	if (scan->mdp.soc_tcp.soc_slaves == soc) {
	    scan->mdp.soc_tcp.soc_slaves = soc->soc_next;
	} else {
	    scan = scan->mdp.soc_tcp.soc_slaves;
	    while (scan->soc_next == soc) scan = scan->soc_next;
	    scan->soc_next = soc->soc_next;
	}
    }

/*  Now do the TCP close:
 */
    if (soc->mdp.soc_tcp.soc_connected) {
	CTRACE(tfp, "RPC/TCP: Closing socket %d\n", soc->mdp.soc_tcp.soc_s);
	if (NETCLOSE(soc->mdp.soc_tcp.soc_s) < 0)
	    return inet_status("netclose");
    }
    free(soc);	    /* Deallocate the socket itself */
    return RPC_S_NORMAL;

} /* tcp_close */


/*	Data structure defining this medium:
**	-----------------------------------
*/

PRIVATE rpc_protocol tcp_protocol = {
    0,				/* Link to next (not yet used) */
    "tcp",			/* Name of medium used in address strings */
    1,				/* Yes, reliable */
    tcp_open,
    tcp_close,
    tcp_write,
    tcp_read,
    tcp_aread,
    tcp_my_address,
    tcp_peer_address,
};


/*	Register this protocol with the RPC system
**	------------------------------------------
*/

#ifdef __STDC__
rpc_status use_tcp(void)
#else
rpc_status use_tcp()
#endif
{
    (void) rpc_use(&tcp_protocol);
}