/* ********************************************
 * *                                          *
 * *     UDP SOCKET SUPPORT ROUTINES          *
 * *                                          *
 * ********************************************
 * File: libs/user/UdpSupport.C
 *
 *                       COPYRIGHT (c) 2004  BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */
#ifdef HPUX
#ifdef HPUX11
#define MULTICASTING_SUPPORTED 1
#else
/* Sorry, HPUX 10.20 does not support multicasting */
#define MULTICASTING_SUPPORTED 0
#endif  /* #ifdef HPUX11 */
#else
#define MULTICASTING_SUPPORTED 1
#endif  /* #ifdef HPUX */

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>

#if MULTICASTING_SUPPORTED
#include <net/if.h> 
#endif  /* #if MULTICASTING_SUPPORTED */

#define INCLUDE_NETINET_IN_PROTOTYPES 1
#include <netinet/in.h>
#include <netinet/tcp.h>
#define INCLUDE_STDIO_PROTOTYPES 1
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#define INCLUDE_SYSSOCKET_PROTOTYPES 1
#include <sys/socket.h>
#include <unistd.h>

#include "sigtypes.h"

#include "user_lib.h"

/* ==============================================
 * Open a socket for UDP writing
 */
int OpenUdpWriter
(UINT2 iPort_a,             /* Port number to write to */
 const char *sAddress_a,    /* Address to write to */
 const char *sInterface_a,  /* Interface name needed for multicasting only */
 int   iFlags_a
 )
{
  int iSockFD, ierror;
  struct sockaddr_in BroadcastAddr;
  /* ------------------------------------------ */

  iSockFD = socket(AF_INET,SOCK_DGRAM,0);
  if (iSockFD == -1)
    {
      sig_signal( errno_to_message_va( errno, 1, "socket()" ) ) ;
      return iSockFD;
    }

  /* We now have a valid opened socket, it must be closed if we exit. */
  /* Allow broadcast addresses */
  if (iFlags_a&UDP_FLG_MULTICAST)
    {
#if MULTICASTING_SUPPORTED
      struct in_addr   Inaddr;
      struct ifreq     Ifreq;
      u_char iHops = 5;
      ierror = setsockopt( iSockFD, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&iHops, 
			   sizeof(iHops) );
      if (ierror < 0)
	{
	  sig_signal( errno_to_message_va( errno, 1, "setsockopt()") ) ;
	  close( iSockFD );
	  return -1;
	}

      strncpy(Ifreq.ifr_name, sInterface_a, IFNAMSIZ);
      ierror = ioctl(iSockFD, SIOCGIFADDR, &Ifreq );
      if (ierror<0)
	{
	  sig_signal( errno_to_message_va( errno, 1, "ioctl()") ) ;
	  close( iSockFD );
	  return -1;
	}
      Inaddr = ((struct sockaddr_in *)&Ifreq.ifr_addr)->sin_addr;
      ierror = setsockopt( iSockFD, IPPROTO_IP, IP_MULTICAST_IF, &Inaddr, 
			   sizeof(Inaddr) );
      if (ierror < 0)
	{
	  sig_signal( errno_to_message_va( errno, 1, "setsockopt()") ) ;
	  close( iSockFD );
	  return -1;
	}
#endif  /* #if MULTICASTING_SUPPORTED */
    }
  else if (!(iFlags_a&UDP_FLG_UNICAST))
    {
      int iflag = TRUE;
      ierror = setsockopt( iSockFD, SOL_SOCKET, SO_BROADCAST, (char *)&iflag, 
			   sizeof(iflag) );
      if (ierror < 0)
	{
	  sig_signal( errno_to_message_va( errno, 1, "setsockopt()") ) ;
	  close( iSockFD );
	  return -1;
	}
    }

  memset( &BroadcastAddr, 0, sizeof(BroadcastAddr) );
  BroadcastAddr.sin_family = AF_INET;
  BroadcastAddr.sin_port = htons(iPort_a);
  ierror = inet_aton( sAddress_a, &BroadcastAddr.sin_addr );
  if (ierror == 0)
    {
      fprintf( stderr, "Syntax error in  broadcast address '%s'\n", sAddress_a );
      close( iSockFD );
      return -1;
    }

  ierror = connect(iSockFD, (struct sockaddr *)(void*)&BroadcastAddr, sizeof(BroadcastAddr));
  if (ierror < 0)
    {
      sig_signal( errno_to_message_va( errno, 1, "connect()" ));
      close( iSockFD );
      return -1;
    }

  return iSockFD;

}

/* ==============================================
 * Open a socket for UDP reading
 */
int OpenUdpReader
(UINT2 iPort_a,              /* Port number to read from */
 const char *sAddress_a,     /* Address to bind to (can be 0.0.0.0) */
 const char *sInterface_a,   /* Interface name needed for multicasting only */
 struct in_addr *pAddress_a, /* Returns the local address for those who might want it */  
 int   iFlags_a
 )
{
  int iSockFD, ierror;
  struct sockaddr_in ReadAddr;
  /* ------------------------------------------ */

  memset( pAddress_a, 0, sizeof(*pAddress_a) );
  iSockFD = socket(AF_INET,SOCK_DGRAM,0);
  if (iSockFD == -1)
    {
      sig_signal( errno_to_message_va( errno, 1, "socket()" ) ) ;
      return iSockFD;
    }

  /* We now have a valid opened socket, it must be closed if we exit. */

  /* Allow multiple readers. Not applicable for unicast */
  if (!(iFlags_a&UDP_FLG_UNICAST))
    {
      int iflag = TRUE;
      ierror = setsockopt( iSockFD, SOL_SOCKET, SO_REUSEADDR, (char *)&iflag, 
			   sizeof(iflag) );
      if (ierror < 0)
	{
	  sig_signal( errno_to_message_va( errno, 1, "setsockopt()") ) ;
	  close( iSockFD );
	  return -1;
	}
    }

  /* Pick who we want to read from */
  memset( &ReadAddr, 0, sizeof(ReadAddr) );
  ReadAddr.sin_family = AF_INET;
  ReadAddr.sin_port = htons(iPort_a);
  ierror = inet_aton( sAddress_a, &ReadAddr.sin_addr );
  if (ierror == 0)
    {
      fprintf( stderr, "Syntax error in read address '%s'\n", sAddress_a );
      close( iSockFD );
      return -1;
    }

  ierror = bind(iSockFD, (struct sockaddr *)(void*)&ReadAddr, sizeof(ReadAddr));
  if (ierror < 0)
    {
      sig_signal( errno_to_message_va( errno, 1, "bind()" ));
      close( iSockFD );
      return -1;
    }

  if (iFlags_a&UDP_FLG_MULTICAST)
    {
#if MULTICASTING_SUPPORTED
      struct ip_mreq Mreq;
      struct ifreq   Ifreq;

      Mreq.imr_multiaddr = ReadAddr.sin_addr;
      strncpy( Ifreq.ifr_name, sInterface_a, IFNAMSIZ );
      ierror = ioctl(iSockFD, SIOCGIFADDR, &Ifreq );
      if (ierror<0)
	{
	  sig_signal( errno_to_message_va( errno, 1, "ioctl()") ) ;
	  close( iSockFD );
	  return -1;
	}
      *pAddress_a        = ((struct sockaddr_in *)&Ifreq.ifr_addr)->sin_addr;
      Mreq.imr_interface = ((struct sockaddr_in *)&Ifreq.ifr_addr)->sin_addr;
      ierror = setsockopt( iSockFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, &Mreq, 
			   sizeof(Mreq) );
      if (ierror < 0)
	{
	  sig_signal( errno_to_message_va( errno, 1, "setsockopt()") ) ;
	  close( iSockFD );
	  return -1;
	}
#endif  /* #if MULTICASTING_SUPPORTED */

    }


  return iSockFD;

}
