
/*
 *  Send UDP multicast message
 *  Send UDP broadcast message
*/

// #define SEND_BCASTS_TOO Yes
#ifdef SEND_BCASTS_TOO
/* broadcast address for our network */
/* unsigned int brAddr = 0x818197ff; - only our network */
unsigned int brAddr[] = {
  0x818197ff,                   /* 129.129.151.255 */
  0x818196ff,                   /* 129.129.150.255 - same router as our subnet */
  0x81818aff,                   /* 129.129.138.255 - same router as our subnet */
};
int nrBrAddr = sizeof(brAddr) / sizeof(unsigned int);
#endif


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netdb.h>

#ifndef vaxc
# ifndef HP_RT
#  ifndef __osf__
#   ifndef linux
#    include <sys/sockio.h>
#   endif
#  endif
# else
#  include <sys/ioctl.h>
# endif
#else
#  include <UCX$INETDEF.H>
#endif


/* #define TESTING Yes */

#ifdef TESTING
#define PERROR(x) perror(x)
#else
#define PERROR(x)
#endif


#include "dgrambroadcast.h"


int McastSocket;                /* multicast socket */
struct sockaddr_in McastDestination;    /* multicast destination address */


/*********************************************************************/

// -DDESTINATION_MCAST="\"226.129.151.57\""
// which is acs-gateway with first byte replaced to 226

static char DESTINATION_MCAST[128] = { 0 };     // place to hold x.y.z.w IPv4 address

void initDestMcastAddr()
{
  struct hostent *hp;

  if (memcmp(DESTINATION_MCAST, "226.", 4) == 0) {
    return;                     // already initialized
  }

  hp = gethostbyname(SERVER_HOST);
  if (hp == (struct hostent *) 0) {
    printf("%s: unknown host", SERVER_HOST);
    exit(99);
  }

  sprintf(DESTINATION_MCAST, "226.%d.%d.%d", hp->h_addr[1] & 0xFF,
          hp->h_addr[2] & 0xFF, hp->h_addr[3] & 0xFF);
  //printf("Got DESTINATION_MCAST = '%s'\n", DESTINATION_MCAST);
}

/*********************************************************************/
void closeBroadcastSocket(sendSocket)
int sendSocket;
{
#ifdef TESTING
  printf("closeBroadcastSocket()\n");
#endif
  close(sendSocket);

  if (McastSocket >= 0)
    close(McastSocket);

}


/*********************************************************************/
int openBroadcastSocket()
{
  //unsigned char         ttl;
  int ttl;
  int sendSocket;
  int enableBroadcast;

  int status;


  initDestMcastAddr();

#ifdef TESTING
  printf("openBroadcastSocket()\n");
#endif

  sendSocket = socket(AF_INET, SOCK_DGRAM, 0);
  if (sendSocket < 0) {
    PERROR("socket");
    return sendSocket;
  }

  enableBroadcast = 1;
  status =
      setsockopt(sendSocket, SOL_SOCKET, SO_BROADCAST,
                 (char *) &enableBroadcast, sizeof(enableBroadcast));
  if (status < 0) {
    PERROR("setsockopt");
    return status;
  }


  /* open multicast socket */
  McastSocket = socket(AF_INET, SOCK_DGRAM, 0);
  if (McastSocket < 0) {
    PERROR("McastSocket");
  }
  //else { printf("Multicast socket opened\n");}

  ttl = 4;                      // with less then 2 it does not work
  if (setsockopt
      (McastSocket, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttl,
       sizeof(ttl)) < 0) {
    perror("setsockopt() failed setting IP_MULTICAST_TTL");
  }
  //else {printf("Multicast socket configured\n");}

  McastDestination.sin_family = AF_INET;
  McastDestination.sin_port = htons(SEND_PORT);
  McastDestination.sin_addr.s_addr = inet_addr(DESTINATION_MCAST);


  return sendSocket;

}

/*********************************************************************/
int openReceiveSocket(port)
unsigned short port;
{
  struct sockaddr_in name;
  int recvSocket;

  initDestMcastAddr();

#ifdef TESTING
  printf("openReceiveSocket()\n");
#endif

  recvSocket = socket(AF_INET, SOCK_DGRAM, 0);
  if (recvSocket < 0) {
    PERROR("socket");
    return recvSocket;
  }

  {
    int sopt = 1;

    if (setsockopt
        (recvSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &sopt,
         sizeof(int)) != 0) {
      perror("SO_REUSEADDR");
      printf("not fatal, continuing\n");
    }
  }

  /* Create name with wildcards. */
  name.sin_family = AF_INET;
  name.sin_addr.s_addr = INADDR_ANY;
  name.sin_port = htons(port);
  if (bind(recvSocket, (struct sockaddr *) &name, sizeof(name))) {
    PERROR("bind");
    recvSocket = -1;
    return recvSocket;
  }
#ifdef USE_MULTICAST
  {
    char group[100];            // group address string
    struct ip_mreq McastReq;    // multicast group request

    printf
        ("Receive on port 0x%04X=%d, use multicast group=\"%s\" addr=INADDR_ANY\n",
         port & 0xFFFF, port, DESTINATION_MCAST);

    // Join the multicast group from we want to receive datagrams.
    // Initialize the multicast request structure and then pass
    // it as an option to setsockopt().  The imr_multiaddr element
    // is initialized to the desired multicast group.  The
    // imr_interface element is initilized to IPADDR_ANY which
    // causes the multcast receives to come from the default
    // interface.

    // Set defaults and get group address and port number if
    // specified in command line.
    strcpy(group, DESTINATION_MCAST);

    McastReq.imr_multiaddr.s_addr = inet_addr(group);
    McastReq.imr_interface.s_addr = INADDR_ANY;

    if (setsockopt
        (recvSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &McastReq,
         sizeof(McastReq)) < 0)
      perror("setsockopt failed for IP_ADD_MEMBERSHIP");
  }
#endif

  return recvSocket;

}

/*********************************************************************/
void receiveJoinMCast(recvSocket)
int recvSocket;
{
  struct ip_mreq McastReq;      // multicast group request
  char *group;


  initDestMcastAddr();

  group = DESTINATION_MCAST;


  McastReq.imr_multiaddr.s_addr = inet_addr(group);
  McastReq.imr_interface.s_addr = INADDR_ANY;

  if (setsockopt
      (recvSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &McastReq,
       sizeof(McastReq)) < 0)
    perror("setsockopt failed for IP_ADD_MEMBERSHIP");

}

/*********************************************************************/
int selectReceiveSocket(recvSocket, usec)
int recvSocket;
int usec;
{
  struct timeval tv;
  int nfds, rm, wm, em;
  int status;

#ifdef TESTING
  printf("selectReceiveSocket()\n");
#endif

  nfds = 32;
  rm = 1 << recvSocket;
  wm = 0;
  em = 0;
  tv.tv_sec = 0;
  tv.tv_usec = usec;

#ifndef vaxc
  status =
      select(nfds, (fd_set *) & rm, (fd_set *) & wm, (fd_set *) & em, &tv);
#else
  status = select(nfds, &rm, &wm, &em, &tv);
#endif

  return status;                /* 0 = nothing to read, 1 = ready for read, -1 = error */
}

int readReceiveSocket(recvSocket, buff, len)
int recvSocket;
unsigned char *buff;
int len;
{
  int status;

#ifdef TESTING
  printf("readReceiveSocket()\n");
#endif

  memset(buff, '\0', len);
  status = read(recvSocket, buff, len);
  if (status < 0)
    PERROR("read");
  return status;
}

/*********************************************************************/
int sendBroadcastMessage(sendSocket, sendPort, sndMsg, sndMsgLen)
int sendSocket;
unsigned short sendPort;
unsigned char *sndMsg;
int sndMsgLen;
{
  int status;
#ifdef SEND_BCASTS_TOO
  int i;
  int flags;
  struct sockaddr_in sendName;
#endif


#ifdef TESTING
  printf("sendBroadcastMessage(s=%d, 0x%04X, msg, len=%d)\n", sendSocket,
         sendPort, sndMsgLen);
#endif

  /* send multicast message */
  status =
      sendto(McastSocket, (char *) sndMsg, sndMsgLen, 0,
             (struct sockaddr *) &McastDestination,
             sizeof(McastDestination));
  return status;


#ifdef SEND_BCASTS_TOO
  /* send to all broadcast addresses from the list */
  for (i = 0; i < nrBrAddr; i++) {

    sendName.sin_family = AF_INET;
    sendName.sin_addr.s_addr = htonl(brAddr[i]);
    sendName.sin_port = htons(sendPort);

    flags = 0;
    status = sendto(sendSocket, (char *) sndMsg, sndMsgLen,
                    flags, (struct sockaddr *) &sendName,
                    sizeof(sendName));

    if (status != sndMsgLen) {
/*printf("sendBroadcastMessage(s=%d, 0x%04X, msg, len=%d - FAILED)\n", brAddr[i], sendPort, sndMsgLen);*/
      PERROR("sendto");
    }
/*else{printf("sendBroadcastMessage(s=%d, 0x%04X, msg, len=%d - OK)\n", brAddr[i], sendPort, sndMsgLen);}*/
  }
#endif


  return status;

}
