/**************************************************************************\
*          Copyright (c) 1992 INRIA Sophia Antipolis, FRANCE.              *
*                                                                          *
* Permission to use, copy, modify, and distribute this material for any    *
* purpose and without fee is hereby granted, provided that the above       *
* copyright notice and this permission notice appear in all copies.        *
* WE MAKE NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS     *
* MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS   *
* OR IMPLIED WARRANTIES.                                                   *
\**************************************************************************/

#include "protocol.h"

char *localhost;
int UNICAST=0;    
struct sockaddr_in addr;
static int len_addr;
static char counter;

/*******************************************************************************/
int createsocket(port,group)
char *port;
char *group;
{
    return(CreateReceiveSocket(&addr,&len_addr,port,group));
}

/****************************************************************************\
*                  SENDING & RECEIVING CONTROL PACKETS                       *
\****************************************************************************/

int CreateSendSocket(addr, len_addr, port, group, ttl)
     struct sockaddr_in *addr;
     int *len_addr;
     char *port;
     char *group; /* IP address style "225.23.23.6" = "a4.a3.a2.a1" */
     char *ttl;
{
  struct hostent *hp;
  char hostname[64];
  int sock;
  unsigned int iport, i1, i2, i3, i4;

  if(sscanf(group, "%u.%u.%u.%u", &i4, &i3, &i2, &i1) != 4){
    fprintf(stderr, "CreateSendSocket: Invalid group %s\n", group);
    return(-1);
  }

  iport = atoi(port);
  if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
    fprintf(stderr, "Cannot create datagram socket\n");
    return(-1);
  }
  *len_addr = sizeof(*addr);
  memset((char *)addr, 0, *len_addr);
  addr->sin_port = htons((u_short)iport);
  addr->sin_addr.s_addr = htonl((i4<<24) | (i3<<16) | (i2<<8) | i1);
  addr->sin_family = AF_INET;
  if(UNICAST == FALSE){
#ifdef MULTICAST
    setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, ttl, sizeof(*ttl));
#else
    fprintf(stderr, "MULTICAST mode not supported, use only unicast option\n");
    exit(-1);
#endif /* MULTICAST */
  }
  return(sock);
}

/*******************************************************************************/
int CreateReceiveSocket(addr, len_addr, port, group)
     struct sockaddr_in *addr;
     int *len_addr;
     char *port;
     char *group; /* IP address style "225.23.23.6" = "a4.a3.a2.a1" */
{
  int i, sock, optlen, optval;
  unsigned int iport, i1, i2, i3, i4;
  struct hostent *hp;
  char hostname[64];
#ifdef MULTICAST
  int one=1;
  int join;
  struct ip_mreq imr;
#endif 

  if(sscanf(group, "%u.%u.%u.%u", &i4, &i3, &i2, &i1) != 4){
    fprintf(stderr, "CreateReceiveSocket: Invalid group %s\n", group);
    return(-1);
  }

  *len_addr = sizeof(*addr);
  iport = atoi(port);
  if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
    fprintf(stderr, "Cannot create datagram socket\n");
    exit(-1);
  }
  if (localhost == NULL) {
      gethostname(hostname, 64);
  } else {
      strcpy(hostname,localhost);
  }

  if((hp=gethostbyname(hostname)) == 0){
      fprintf(stderr, "Unknown host %s\n", hostname);
      exit(0);
  }
  if(UNICAST){
    memset((char *) addr, 0, *len_addr);
    memcpy((char *) addr, hp->h_addr, sizeof(addr));
  }else{
#ifdef MULTICAST
      imr.imr_multiaddr.s_addr = (i4<<24) | (i3<<16) | (i2<<8) | i1;
      imr.imr_multiaddr.s_addr = htonl(imr.imr_multiaddr.s_addr);
      if (hp->h_addr_list == NULL) {
	  fprintf(stderr, "No addresses in h_addr_list for %s\n", hostname);
	  exit(1);
      }

      join = 0;
#ifndef SOLARIS
      for (i=0; hp->h_addr_list[i] != NULL; i++) {
	  memcpy((char *)&imr.imr_interface.s_addr,
		 hp->h_addr_list[i], hp->h_length);
	  if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
			sizeof(struct ip_mreq)) != -1) {
	      memcpy(myipaddress, hp->h_addr_list[i], 4);
	      join = 1;
	      break;
	  }
      } 
#else      
      memcpy((char *)&imr.imr_interface.s_addr,
	     hp->h_addr, hp->h_length);
      if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &imr,
		     sizeof(struct ip_mreq)) != -1) {
	  join = 1;
      }
	  memcpy(myipaddress, hp->h_addr,4);
#endif      
      if (!join) {
          fprintf(stderr, "Cannot join group %s\n", group);
          exit(1);
      }
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
/* #if defined (__alpha) || defined (sgi) */
#if !(defined SOLARIS || SUNOS || LINUX)
      setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
#endif
#else
      fprintf(stderr, "MULTICAST mode not implemented, use unicast option\n");
      exit(-1);
#endif /* MULTICAST */
  }
  addr->sin_family = AF_INET;
  addr->sin_port = htons((u_short)iport);
  addr->sin_addr.s_addr = INADDR_ANY;
  if (bind(sock, (struct sockaddr *) addr, *len_addr) < 0){
    fprintf(stderr, "Cannot bind socket to %d port\n", iport);
    exit(1);
  }
  optlen = 4;
  optval = 50000;
  if(setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *) &optval, optlen) != 0){
      perror("setsockopt failed");
      getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&optval, &optlen);
      fprintf(stderr,"SO_RCVBUF = %d\n", optval);
  }
    return(sock);
}

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