/*****************************************************************************
*
*                         NCSA DTM version 2.3
*                               May 1, 1992
*
* NCSA DTM Version 2.3 source code and documentation are in the public
* domain.  Specifically, we give to the public domain all rights for future
* licensing of the source code, all resale rights, and all publishing rights.
*
* We ask, but do not require, that the following message be included in all
* derived works:
*
* Portions developed at the National Center for Supercomputing Applications at
* the University of Illinois at Urbana-Champaign.
*
* THE UNIVERSITY OF ILLINOIS GIVES NO WARRANTY, EXPRESSED OR IMPLIED, FOR THE
* SOFTWARE AND/OR DOCUMENTATION PROVIDED, INCLUDING, WITHOUT LIMITATION,
* WARRANTY OF MERCHANTABILITY AND WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE
*
*****************************************************************************/

/*********************************************************************
**
**  $Header: /X11/mosaic/cvsroot/xmosaic3/libdtm/nmsg.c,v 1.3 1996/02/18 23:40:15 spowers Exp $
**
**********************************************************************/
/*
#ifdef RCSLOG

 $Log: nmsg.c,v $
 Revision 1.3  1996/02/18 23:40:15  spowers
 PROTO -> DTM_PROTO

 Revision 1.2  1995/10/13 06:33:20  spowers
 Solaris support added.

 Revision 1.1.1.1  1995/01/11 00:03:01  alanb
 New CVS source tree, Mosaic 2.5 beta 4

 * Revision 2.5  1994/12/29  23:40:08  alanb
 * I'm committing with a new symbolic revision number.
 *
 * Revision 1.1.1.1  1994/12/28  21:37:33  alanb
 *
 * Revision 1.2  1993/10/06  06:19:08  ebina
 *  Ditto const shit
 *
 * Revision 1.1.1.1  1993/07/04  00:03:13  marca
 * Mosaic for X version 2 distribution
 *
 * Revision 1.1  1993/01/18  21:50:33  marca
 * I think I got it now.
 *
 * Revision 1.15  92/04/30  20:25:27  jplevyak
 * Changed Version to 2.3.
 *

#endif
*/
/*
	Purpose	: Set of library calls for name server applications to use.
	Notes	:
			This file contains functions to send and receive
			DTM control messages such MREG, MROUTE etc.

			The fd parameter should be removed from all calls
			for portability.
*/

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef _ARCH_MSDOS
#include <nmpcip.h>
#include <errno.h>
#include "uio.h"
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/uio.h>
#include <sys/errno.h>
#endif
#include "dtmint.h"
#include "dtmnserv.h"
#include <stdio.h>
#include "debug.h"

/*
    STATIC FUNCTION PROTOTYPES
*/
static int dtm_send_control DTM_PROTO(( int fd, char *msg, int msize,
        char *sendto_addr ));


/*
	Function to send a DTM control message ( messages like MREG, MROUTE etc. )
	Notes	:
*/

#ifdef DTM_PROTOTYPES
static int dtm_send_control(int fd,char *msg,int msize,char *sendto_addr )
#else
static int dtm_send_control( fd, msg, msize, sendto_addr )
	int		fd ;
	char	*msg ;
	int		msize ;
	char	*sendto_addr ;		/* read-only */
#endif
{
	S_ADDR	sockaddr ;			/* destination address  */
	struct	iovec	iov[ 1 ] ;	/* message iovec */
	int		status ;			/* return value from a call */
    int32   tmp ;               /* temporary */
	char	sendaddr[ MAX132 ] ;
	int		fLogical;

	DBGFLOW( "dtm_send_control called\n" );
	DBGINT( "dtm_send_control: sendto_addr is '%s'\n", sendto_addr );

	/* Get destination address in sockaddr_in structure */

	strncpy( sendaddr, sendto_addr, (MAX132 - 1)) ;
	sendaddr[ MAX132 - 1 ] = '\0' ;

	sockaddr.sin_family = AF_INET ;
	CHECK_ERR( dtm_init_sockaddr( &sockaddr, sendaddr, &fLogical));

	/* Prepare iovec and send message length */

	tmp = msize ;
	STDINT( tmp );
	iov[ 0 ].iov_base = (char *)&tmp ;
	iov[ 0 ].iov_len = 4 ;

	if((status = dtm_writev_buffer( fd, iov, 1, 4, (struct sockaddr *)&sockaddr,
			sizeof( struct sockaddr_in ))) < 0 )
	{
		DBGFLOW( "dtm_send_control: message length send error\n" );
		return status ;
	}

	/* Prepare iovec and send message */

	iov[ 0 ].iov_base = msg ;
	iov[ 0 ].iov_len = msize ;

	return dtm_writev_buffer( fd, iov, 1, msize, (struct sockaddr *)&sockaddr,
					sizeof( struct sockaddr_in )) ;
}

/*
	DTMsendDiscard
	Function to send a discard message setting the discard state.
*/
#ifdef DTM_PROTOTYPES
int DTMsendDiscard(int fd,char *sendto_addr,int set_or_clear )
#else
int DTMsendDiscard( fd, sendto_addr, set_or_clear )
	int		fd;				/* output socket */
	char	*sendto_addr;	/* destination address addr:port number */
	int		set_or_clear;
#endif
{
	char	mbuf[ MAX132 ];	/* buffer to build messages */

	DBGFLOW( "DTMsendDiscard called\n" );

	sprintf( mbuf, MDISCARD, MROUTEID, set_or_clear );

	DBGFLOW( "DTMsendDiscard: Message:- " );
	DBGFLOW( mbuf ); DBGFLOW( "\n" );

	return dtm_send_control( fd, mbuf, (strlen( mbuf ) + 1), sendto_addr );
}

/*
	Function to send a routing message to designated address.
	Notes	: 	delcount parameter to be added, currently
			forced to zero.
			Destination address, and array of addresses are in
			format IPaddr( dotted decimal ):portid.

    int     fd;                output socket
    char    *sendto_addr;      destination address addr:port number
    int     addcount;          number of addresses to connect to
    char    **add_addresses;   array of addresses to connect to
    int     delcount;          number of addresses to connect to
    char    **del_addresses;   array of addresses to connect to
*/

#ifdef DTM_PROTOTYPES
int DTMsendRoute(int fd,char *sendto_addr,int addcount,char **add_addresses,
                        int delcount, char **del_addresses )
#else
int DTMsendRoute( fd, sendto_addr, addcount, add_addresses,
						delcount, del_addresses )
	int		fd;				/* output socket */
	char	*sendto_addr;	/* destination address addr:port number */
	int		addcount;		/* number of addresses to connect to */
	char	**add_addresses;/* array of addresses to connect to */
	int		delcount;		/* number of addresses to connect to */
	char	**del_addresses;/* array of addresses to connect to */
#endif
{
	char	mbuf[ MAX132 ];	/* buffer to build messages */

	DBGFLOW( "DTMsendRoute called\n" );

	/* Prepare MROUTE message and send it */

	sprintf( mbuf, MROUTE, MROUTEID, delcount, addcount ) ;
	while( delcount-- ) {
		strncat( mbuf, " ", (MAX132-1));
		strncat( mbuf, del_addresses[ delcount ], (MAX132 - 1));
	}
	while( addcount-- ) {
		strncat( mbuf, " ", (MAX132-1));
		strncat( mbuf, add_addresses[ addcount ], (MAX132 - 1));
	}
	mbuf[ MAX132 - 1 ] = '\0' ;

	DBGFLOW( "DTMsendRoute: Message:- " );
	DBGFLOW( mbuf ); DBGFLOW( "\n" );

	return dtm_send_control( fd, mbuf, (strlen( mbuf ) + 1), sendto_addr );
}

/*
	Function to send ack to routing message to designated address.
	NOTE: this function is not used yet as the name server has
		not implemented the receive portion.
*/

#ifdef DTM_PROTOTYPES
int dtm_nsend_ackroute( char *portname )
#else
int dtm_nsend_ackroute( portname )
	char	*portname ;
#endif
{
	int		fd ;
	S_ADDR	addr;
	char	*sendto_addr ;
	char	*refname = dtm_get_refname() ;
	char	mbuf[ MAX132 ];	/* buffer to build messages */

	CHECK_ERR( sendto_addr = dtm_get_naddr( &addr, &fd ));

	DBGFLOW( "dtm_nsend_ackroute called\n" );

	/* Prepare Ackroute, send it */

	sprintf( mbuf, MACKROUTE, MACKROUTEID, refname, portname );
	mbuf[ MAX132 - 1 ] = '\0' ;

	DBGFLOW( "dtm_nsend_ackroute: Message:- " );
	DBGFLOW( mbuf ); DBGFLOW( "\n" );

	return dtm_send_control( fd, mbuf, (strlen( mbuf ) + 1), sendto_addr );
}

/*
	Function to send self's socket address to name server process.
*/

#ifdef DTM_PROTOTYPES
dtm_nsend_sockaddr(int fd,char *sendto_addr,char *refname,char *portname,
            S_ADDR *sockaddr )
#else
dtm_nsend_sockaddr( fd, sendto_addr, refname, portname, sockaddr )
	int		fd ;
	char	*sendto_addr ;
	char	*refname ;
	char	*portname ;
	S_ADDR	*sockaddr ;
#endif
{
	struct	in_addr inaddr ;
	char	mbuf[ MAX132 ];	/* buffer to build messages */

	DBGFLOW( "dtm_nsend_sockaddr called\n" );

	/* Prepare MREG, send it */

	inaddr.s_addr = sockaddr -> sin_addr.s_addr ;
#ifdef _ARCH_MSDOS
	sprintf( mbuf, MREG, MREGID, refname, portname,
         inet_ntoa( inaddr.s_addr ), ntohs( sockaddr -> sin_port ) );
#else
    sprintf( mbuf, MREG, MREGID, refname, portname,
		 inet_ntoa( inaddr ), ntohs( sockaddr -> sin_port ) );
#endif
	mbuf[ MAX132 - 1 ] = '\0' ;

	DBGFLOW( "dtm_nsend_sockaddr: Message:- " ); DBGFLOW( mbuf ); DBGFLOW( "\n" );

	return dtm_send_control( fd, mbuf, (strlen( mbuf ) + 1), sendto_addr );
}

/*
	Function to recieve registation messages from other processes.
	Notes	:	does not block, return length is message
			received, 0 otherwise

*/
#ifdef DTM_PROTOTYPES
int DTMrecvRegistration(int fd,char *buffer,int len)
#else
int DTMrecvRegistration(fd, buffer, len)
  int	fd, len;
  char	*buffer;
#endif
{
  int	count;

	if( dtm_quick_select(fd, &count) )
		return dtm_recv_header(fd, buffer, len);
        else
		return 0;
}


/*
	Function to create a nameserver port for receiving registration
		messages and sending routing messages.

*/

#ifdef DTM_PROTOTYPES
int DTMmakeNameServerPort(char *portid)
#else
int DTMmakeNameServerPort(portid)
  char	*portid;
#endif
{
  struct sockaddr_in	saddr;


  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = htonl(0);
  saddr.sin_port = htons(0);

  if (strcmp(portid, ":0") != 0)
    saddr.sin_port = htons((unsigned short)atoi(strchr(portid, ':')+1));

  return dtm_socket_init( &saddr, OUTPORTTYPE, NOT_LOGICAL_NAME );
}


/*

	Fucntion to get the port address of the name server port.

++++	should be a socket.c function that returns bound address of
		socket file descriptor

*/

#ifdef DTM_PROTOTYPES
int DTMgetNameServerAddr(int fd,char *name,int len)
#else
int DTMgetNameServerAddr(fd, name, len)
  int	fd, len;
  char	*name;
#endif
{
  char			pnum[8];
  struct sockaddr_in	saddr;
  int			saddr_size = sizeof (struct sockaddr_in);

  if (dtm_get_ipaddr(name) == 0)  {
    DTMerrno = DTMHOST;
    return DTMERROR;
    }

  if (getsockname( fd, (struct sockaddr *)&saddr, &saddr_size ) < 0 )  {
#ifndef _ARCH_MSDOS
	extern	int	errno ;
#endif
    DBGINT( "dtm_socket_init: Unable to get sin_port, errno %d\n", errno );
    DTMerrno = DTMSOCK ;
    return -1 ;
    }

  sprintf(pnum, ":%d", (int)ntohs( saddr.sin_port ));
  strcat(name, pnum);

  return 0;
}
