/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * This software is the proprietary information of Sun Microsystems, Inc.
 * Use is subject to license terms.
 * 
 * Copyright 2004 Sun Microsystems, Inc.  Tous droits réservés.
 * Ce logiciel est propriété de Sun Microsystems, Inc.
 * Distribué par des licences qui en restreignent l'utilisation.
 *
 * ident        "@(#)mfDiscovery.c 1.20     04/09/15 SMI"
 *
 */


/*
 *
 * mfDiscovery.c - Discovery
 *
 *                                                                 
 */

#include "mfConstant.h"
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <strings.h>
#include <locale.h>
#include <wchar.h>

#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <netdb.h> 
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h> /* provides IPPROTO_IP */
#include <arpa/inet.h>
#include <net/if.h> /* provides ifreq and IFNAMSIZ and if_nameindex */
#include <inttypes.h>



#ifdef sun
#include <sys/sockio.h>
#endif

#ifdef linux
#include <linux/sockios.h>
#endif

#include "mfConstant.h"
#include "mfDiscovery.h"






/* static variables */

static int discoveryResponderserverCreated = 0;       /* Connector server boolean flag */
static pthread_mutex_t mutexDiscoveryResponderserver; /* connector server mutex */


static char *responseMessage;
static int   responseMessageLength = 0 ;

static char mcastgroup[80];
static char mcastgroupvalue[80];
static char mcastport[80];
static int mcastportvalue;
static int disableloopback=0;




void getMagicNumber(char *buffer, char *magicNumber) 
{
    if ((buffer != NULL) && (magicNumber !=NULL))
	{
	    strncpy(magicNumber,buffer,6);
	}

} 

void getMsgType(char *buffer, char *msgtype) 
{
    if ((buffer != NULL) && (msgtype != NULL))
	{
	    strncpy(msgtype,buffer+8,8);
	}

} 

void getHdrSize(char *buffer, short *hdrsize)
{
    short stmp = 0;
    if ((buffer != NULL) && (hdrsize != NULL))
        {
            memcpy(&stmp,buffer+6,sizeof(short));
            *hdrsize = ntohs(stmp);
        }
}

void *discoveryResponderServerThread(void *param) {

  struct sockaddr_in addr;
     struct sockaddr_in destaddr;
     struct in_addr outIfAddr;

     int fd, nbytes, addrlen, value;
     struct ip_mreq mreq;
    
     char buffer[MAXBUFSIZE];
     char magicNumber[6];
     char msgtype[8];
     int i1;

/* create what looks like an ordinary UDP socket */
     if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) {
          perror("socket");
          pthread_exit((void *) NULL);
     }

     memset(&addr,0,sizeof(addr));
     addr.sin_family=AF_INET;
     addr.sin_addr.s_addr=htonl(INADDR_ANY);
     addr.sin_port=htons(HELLO_PORT);


    /* Allow multiple sockets to bind to this "interface:port" for multicast */
     value=1;
     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0) {
        perror("setsockopt: SO_REUSEADDR");
	/* fix bugId: 5072840 */
        pthread_exit((void *) NULL);
     }

     /* bind to receive address */
     if (bind(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0) {
          perror("bind");
          pthread_exit((void *) NULL);
     }

     /* sets multicast group */
     mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP);


     /* fix bug 5065408:
        provide discovery support for S8 */
     /* forces the Loopback IF */
     if (disableloopback) {
       mreq.imr_interface.s_addr=htonl(INADDR_ANY);
       outIfAddr.s_addr = htonl(INADDR_ANY);
       DEBUG_PRINTF("mreq.imr_interface: used INADDR_ANY interface\n");
     }
     else {     
       mreq.imr_interface.s_addr=htonl(INADDR_LOOPBACK);
       outIfAddr.s_addr = htonl(INADDR_LOOPBACK);
       DEBUG_PRINTF("mreq.imr_interface: used INADDR_LOOPBACK interface\n");
     }

     /* use setsockopt() to request that the kernel join a multicast group on the wanted IF*/
     if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) {
          perror("setsockopt");
          pthread_exit((void *) NULL);
     }


     if (  setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &outIfAddr, sizeof(struct in_addr)) < 0) {
             perror ("error from setsockopt IP_MULTICAST_IF");
             pthread_exit((void *) NULL);
     }


     /* set up destination address */
     memset(&destaddr,0,sizeof(destaddr));
     destaddr.sin_family=AF_INET;
     destaddr.sin_addr.s_addr=inet_addr(HELLO_GROUP);
     destaddr.sin_port=htons(HELLO_PORT);

     /*sends a RESP @ startup*/
     if (sendto(fd,responseMessage,responseMessageLength,0,(struct sockaddr *) &destaddr,
                     sizeof(destaddr)) < 0) {
               perror("sendto");
               pthread_exit((void *) NULL);;
     }



     /* now just enter a read-print loop */
     while (1) {
	  memset(&buffer,0,MAXBUFSIZE);
	  addrlen=sizeof(addr);
	  if ((nbytes=recvfrom(fd,buffer,MAXBUFSIZE,0,
			       (struct sockaddr *) &addr,&addrlen)) < 0) {
	       perror("recvfrom");
	       DEBUG_PRINTF("discoveryResponderServerThread: recvfrom failed  \n");
	       pthread_exit((void *) NULL);;
	  }


	  DEBUG_PRINTF2("nbbytes received %d\n",nbytes);
	  for (i1=0; i1 <nbytes ; i1++) {
	    DEBUG_PRINTF4("buff[%d] = %x , %c\n",i1,buffer[i1],buffer[i1]);
	  }
	  
	  

	  getMagicNumber(buffer,magicNumber);
	  getMsgType(buffer,msgtype);

	  
	  DEBUG_PRINTF2("nbbytes received %d\n",nbytes);

	  for (i1=0; i1 <6 ; i1++) {
	    DEBUG_PRINTF3("magic[%d] = %c\n",i1,magicNumber[i1]);
	  }

	  DEBUG_PRINTF("\n\n");
	  for (i1=0; i1 <8 ; i1++) {
	    DEBUG_PRINTF3("msgtype[%d] = %c\n",i1,msgtype[i1]);
	  }
	  
	  if ((strncmp(magicNumber,DISCOVERY_MAGIC_NUMBER,6) == 0) && 
	      (strncmp(msgtype,DISCOVERY_TAG,8) == 0)) {

	      /*sends a RESP in reply to a DISC */
	      if (sendto(fd,(void * ) responseMessage,responseMessageLength,0,(struct sockaddr *) &destaddr,
			 sizeof(destaddr)) < 0) {
		  perror("sendto");


		  DEBUG_PRINTF("discoveryResponderServerThread: sendto failed \n");
		  pthread_exit((void *) NULL); 
	      }
		    
	    DEBUG_PRINTF("DISC received, Sending RESP\n");
	  }
     }
}



int checkValidityGroup(char *group, char *groupvalue) {
  int rc;
  int val1;
  int val2;
  int val3;
  int val4;

  if (group == NULL) {
    return MF_FAILURE;
  }
 
  rc = sscanf(group,"%d.%d.%d.%d",
	      &val1,
	      &val2,
	      &val3,
	      &val4);
 
    if ((rc ==  4) &&
	(((val1 >= 1) && (val1 <=255)) &&
	 ((val2 >= 1) && (val2 <=255))  &&
	 ((val3 >= 1) && (val3 <=255))  &&
	 ((val4 >= 1) && (val4 <=255)))
	)
      
      {
	sprintf(groupvalue,
		"%d.%d.%d.%d",
		val1,
		val2,
		val3,
		val4);
	return MF_SUCCESS;
      }
    else {
      return MF_FAILURE;
    }
}




int checkValidityPort(char *port, int *portvalue) {
  int rc;
  int value;

  if (port == NULL) {
    return MF_FAILURE;
  }
 
  rc = sscanf(port,"%d",&value) ;
  
  if ((rc == 1) &&
      (value >= 1) &&
      (value <=65535)) {
    *portvalue = value;
    return MF_SUCCESS;
  }
  else {
    return MF_FAILURE;
  }
}
 

int checklineMcastGroup(char *line, char *group)
{
  char *ptrmcastgroup;
  char *ptrequal;

  ptrmcastgroup = strstr(line,MCASTGROUP);
  ptrequal = strchr(line,'=');
   
  if ((ptrmcastgroup != NULL) && (ptrequal != NULL)) {
    strcpy(group,ptrequal+1);
    return MF_SUCCESS;
  }

  return MF_FAILURE;
}


int checklineMcastPort(char *line, char *port )
{

  char *ptrmcastgroup;
  char *ptrequal;

  ptrmcastgroup = strstr(line,MCASTPORT);
  ptrequal = strchr(line,'=');
   
  if ((ptrmcastgroup != NULL) && (ptrequal != NULL)) {
    strcpy(port,ptrequal+1);
    return MF_SUCCESS;
  }

  return MF_FAILURE;

} 



int checklineMcastDisableLoopBack(char *line, int *found)
{

  char *ptrmcastgroup;
  char *ptrequal;

  *found = 0;
  ptrmcastgroup = strstr(line,MCASTDISABLELOOPBACK);
  ptrequal = strchr(line,'=');
   
  if ((ptrmcastgroup != NULL) && (ptrequal != NULL)) {
    if (strncasecmp(ptrequal+1,"true",4) == 0) {
      *found = 1;
    }
    else {
      *found = 0;
    }
    DEBUG_PRINTF2("McastDisableLoopBack value %d\n", *found); 
    return MF_SUCCESS;
  }
  return MF_FAILURE;

} 








void uint64ToByteArray(uint64_t timeStamp, unsigned char *timeStampByteArray)
{
    *(timeStampByteArray + 0) = ( ( timeStamp >> 56 ) & 0xff );
    *(timeStampByteArray + 1) = ( ( timeStamp >> 48 ) & 0xff );
    *(timeStampByteArray + 2) = ( ( timeStamp >> 40 ) & 0xff );
    *(timeStampByteArray + 3) = ( ( timeStamp >> 32 ) & 0xff );
    *(timeStampByteArray + 4) = ( ( timeStamp >> 24 ) & 0xff );
    *(timeStampByteArray + 5) = ( ( timeStamp >> 16 ) & 0xff );
    *(timeStampByteArray + 6) = ( ( timeStamp >> 8 ) & 0xff );
    *(timeStampByteArray + 7) = ( ( timeStamp ) & 0xff );
}



/*
 * Function: mfDiscoveryParse 
 * This function parses the agent
 * property configuration file 
 *  
 *
 */


int mfDiscoveryParse(char *fname) 
{
  FILE *fagent;
  char line[256];
  int found;


  bzero(mcastgroup,strlen(mcastgroup));
  bzero(mcastport,strlen(mcastport));

  fagent = fopen(fname,"r");

  if (fagent == NULL) {
    return MF_FAILURE;
  }

  while (fgets(line,256, fagent) != NULL) {
    checklineMcastGroup(line, mcastgroup);
    checklineMcastPort(line,mcastport);
    if (checklineMcastDisableLoopBack(line,&found) == MF_SUCCESS) {
	disableloopback = found;
	DEBUG_PRINTF2("McastDisableLoopBack %d\n",disableloopback);
	}

  }

  fclose(fagent);
  return MF_SUCCESS;
}





/*
 * Function: jesmf_create_discoveryresponderserver 
 *
 * This function creates the connector server 
 * 
 *
 * returns code:
 *     MF_SUCCES  (success) 
 *     MF_EXIST   (resource server already exist)
 *     EAGAIN     (cf pthread ERRORS)
 *     EINVAL     (cf pthread ERRORS)
 *     EPERM      (cf pthread ERRORS)
 */


int mfDiscoveryResponder(char *productName, 
			 char *productInstance, 
			 char *uri, 
			 unsigned char *userData)
{
  discoveryObject_t responseMessageHeader; /* response message */
  pthread_t tid_discoveryresponderserver;
  int productNamesize = 0 ;
  int productInstancesize = 0;
  int urisize = 0; 
  int userDatasize = 0;
  int res1size;
  int res2size;
  int res3size;
  int res4size;
  int res5size;

 

  int rc;

  struct timeval tv; /* BS fix for BugId 5068417 Provide Timestamp in discovery */
  uint64_t timeStamp; /* BS fix for BugId 5068417 */
  uint64_t millisec1; /* BS fix for BugId 5068417 */
  uint64_t millisec2; /* BS fix for BugId 5068417 */
  unsigned char *timeStampByteArray;
  short timeStampByteArraySize = 0;


  /* initiliase results size to 0 */
  res1size = 0;
  res2size = 0;
  res3size = 0;
  res4size = 0;
  res5size = 0;


  setlocale(LC_ALL,"");
  
  pthread_mutex_lock(&mutexDiscoveryResponderserver);

  /* BS fix for BugId 5068417 */
  gettimeofday(&tv, NULL); 
  /* java expects milliseconds, cast before multiplying or you might loose data*/
  millisec1 = (uint64_t) tv.tv_sec;
  millisec1 = (millisec1 * 1000);
  millisec2 = (uint64_t) tv.tv_usec;
  millisec2 = (millisec2 / 1000);
  timeStamp = (millisec1 + millisec2);
  timeStampByteArray = malloc (sizeof(uint64_t) + 1);
  bzero((void *) timeStampByteArray, sizeof(uint64_t));

  /* fix BugId 5085807 */ 
  uint64ToByteArray(timeStamp,timeStampByteArray);
  /*snprintf((char *) timeStampByteArray,sizeof(uint64_t),"%02X",htonl(timeStamp));*/
  rc = mfDiscoveryParse(AGENT_PROPERTY_FILE);

  if ((rc == MF_SUCCESS) && 
      (checkValidityGroup(mcastgroup,mcastgroupvalue) == MF_SUCCESS) && 
      (checkValidityPort(mcastport,&mcastportvalue) == MF_SUCCESS)) {
    DEBUG_PRINTF2("mcastgroup %s\n",mcastgroup);
    DEBUG_PRINTF2("mcastport  %d\n",mcastportvalue);
  }
  else {
    strcpy(mcastgroupvalue, DEFAULT_MCAST_GROUP);
    mcastportvalue = DEFAULT_MCAST_PORT;
    printf("DEFAULT_MCAST_GROUP %s\n", DEFAULT_MCAST_GROUP);
    printf("mcastgroupvalue %s\n",mcastgroupvalue);
    DEBUG_PRINTF2("(default) mcastgroup %s\n",mcastgroupvalue);
    DEBUG_PRINTF2("(default) mcastportvalue  %d\n",mcastportvalue);
  }


  if (disableloopback) {
    DEBUG_PRINTF2("Loopback disabled: disableloopback %d\n",disableloopback);

  }
  else {
    DEBUG_PRINTF2("Loopback enabled: disableloopback %d\n",disableloopback);
  }


 
  if (productName !=NULL) {
    productNamesize = strlen(productName);
    DEBUG_PRINTF2("productNamesize %d\n",productNamesize);
  }
 
  
  if (productInstance !=NULL) {
    productInstancesize = strlen(productInstance) ;
  }
 
  
  if (uri != NULL) {
    urisize = strlen(uri);
  }
  

  if (userData != NULL) {
    userDatasize = strlen((char *) userData) ;
  }




  /* BS fix for BugId 5068417 */
  if (timeStampByteArray != NULL) {
    timeStampByteArraySize = sizeof(uint64_t) ;

    /* fix for bugId 5082461
       Discovery broken on X86 */
    res1size = timeStampByteArraySize;
  }

  
  /* definition of res2size res3size  res4size res5size
     to be provided */


  if (discoveryResponderserverCreated == 0) {

    bzero((void *) &responseMessageHeader, sizeof(discoveryObject_t));
    
    /* fills up response message data strucure */
    /*header */
    strcpy(responseMessageHeader.magic, DISCOVERY_MAGIC_NUMBER);
    responseMessageHeader.hdrsize =  htons(DISCOVERY_HEADER_SIZE);
    strcpy(responseMessageHeader.msgtype, DISCOVERY_RESPONSE);
    responseMessageHeader.securityLevel = htons(0) ;
    responseMessageHeader.jesversion = htons(JES_VERSION);
    responseMessageHeader.discversion =  htons(DISC_VERSION);
    responseMessageHeader.productsize = htons(productNamesize);
    responseMessageHeader.productinstancesize = htons(productInstancesize);
    responseMessageHeader.urisize = htons(urisize);
    responseMessageHeader.userdatasize = htons(userDatasize);

    /* BS fix for BugId 5068417 res1 is now used for TimeStamp*/
    responseMessageHeader.res1size = htons(timeStampByteArraySize);

    responseMessageHeader.res2size = htons(0) ;
    responseMessageHeader.res3size = htons(0) ;
    responseMessageHeader.res4size = htons(0) ;
    responseMessageHeader.res5size = htons(0) ;
 
  

    /* Allocate276 bloc for memory response */
    responseMessageLength = DISCOVERY_HEADER_SIZE + productNamesize + productInstancesize + urisize + userDatasize 
      + res1size + res2size + res3size  + res4size + res5size;
    DEBUG_PRINTF2("DISCOVERY_HEADER_SIZE %d\n",DISCOVERY_HEADER_SIZE);
    DEBUG_PRINTF2("productNamesize %d\n",productNamesize);
    DEBUG_PRINTF2("productInstancesize %d\n",productInstancesize);
    DEBUG_PRINTF2("urisize %d\n",urisize);
    DEBUG_PRINTF2("userDatasize %d\n",userDatasize);
    DEBUG_PRINTF2("res1size  %d\n",res1size);
    DEBUG_PRINTF2("res2size %d\n",res2size);
    DEBUG_PRINTF2("res3size %d\n",res3size);
    DEBUG_PRINTF2("res4size %d\n",res4size);
    DEBUG_PRINTF2("res5siz %d\n",res5size);
    DEBUG_PRINTF2("responseMessageLength %d\n",responseMessageLength);
    responseMessage = (char *) malloc(responseMessageLength);
    bzero(responseMessage,responseMessageLength);


    /* copy HEADER */
    memcpy((void *) responseMessage,(void *) &responseMessageHeader,DISCOVERY_HEADER_SIZE);


    /* COPY PAYLOAD */
    /* copy the following fields:
       -productName
       -productInstance
       -uri
       -userData
    */

    if (productNamesize != 0) {
      memcpy((void *) (responseMessage+DISCOVERY_HEADER_SIZE), 
	      (void *) productName, 
	      productNamesize);
      
      DEBUG_PRINTF2("ProductName ---> %s\n",productName);
      DEBUG_PRINTF2("productNamesize %d\n", productNamesize);
  
    }
    
    if (productInstancesize != 0) { 
      memcpy((void *) (responseMessage+ DISCOVERY_HEADER_SIZE+productNamesize), 
	      (void *) productInstance, 
	      productInstancesize);
    }
    
    
    if (urisize != 0) { 
      memcpy((void *) (responseMessage+ DISCOVERY_HEADER_SIZE+productNamesize+productInstancesize), 
	      (void *) uri, 
	      urisize);
    }

    if (userDatasize != 0) { 
      memcpy(&responseMessage[DISCOVERY_HEADER_SIZE+productNamesize+productInstancesize+urisize], 
	      userData, 
	      userDatasize);
    }

    /* BS fix for BugId 5068417 res1 is now used for TimeStamp*/
    if (timeStampByteArraySize != 0) { 
      memcpy(&responseMessage[DISCOVERY_HEADER_SIZE+productNamesize+productInstancesize+urisize+userDatasize], 
	      timeStampByteArray, 
	      timeStampByteArraySize);
      DEBUG_PRINTF2("timeStamp in dec ---> %" PRId64 "\n",timeStamp);
      DEBUG_PRINTF2("timeStamp in hex ---> %" PRIx64 "\n",timeStamp);

    }

   





    
    /* create discovery thread */
    rc = pthread_create(&tid_discoveryresponderserver, 
			NULL, 
			discoveryResponderServerThread,
			NULL);
  }
    
  else {
    rc = MF_EXIST;
  }
  pthread_mutex_unlock(&mutexDiscoveryResponderserver);
 
  return rc;


}
       
