/************************************************/
/*                                              */
/* File        : spy_inet.c                     */
/* Description : Internet spy library           */
/*                                              */
/* Author: Sfiligoi Igor                        */
/*                                              */
/* Created      : 14.12.1998                    */
/* Last modified: 15.12.1998                    */
/*                                              */
/************************************************/

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

#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>

#include <errno.h>
#include <Error.h>

/* FARM */
#include <farmprog_defs.h>

#include "spy_inet.h"

int spy_open_inet(char   *raddr,         /* IN : address of the farm (can be a.b.c.d or 1.2.3.4) */
		  int     rport,         /* IN : port of the farm */
		  SPY_ext_id *sid)       /* OUT: SPY_id */
{
  struct sockaddr_in sin_me;
  int sock_remote;

  sock_remote = socket(AF_INET,SOCK_STREAM,0);
  if (sock_remote<0)
    {
      ErrorSetF(0,"spy_open_inet","Error in socket.");
      return(SPY_ERROR_UNKNOWN);
    }

  if (atoi(raddr) > 0 ) /* node given as 1.2.3.4 ? */
    {
      sin_me.sin_family      = AF_INET;
      sin_me.sin_addr.s_addr = inet_addr(raddr);
    } 
  else			     /* ... or as a.b.c.d */
    {
      struct hostent *addr;
      unsigned int addr_tmp;
      if ( (addr=gethostbyname(raddr)) == NULL )
	{
	  ErrorSetF(0,"spy_open_inet",
		    "%s is a bad hostname",raddr);
	  close(sock_remote);
	  return(SPY_ERROR_UNKNOWN);
	}
      sin_me.sin_family = addr->h_addrtype;
      memcpy((char*)&addr_tmp, addr->h_addr, addr->h_length);
      sin_me.sin_addr.s_addr = addr_tmp;
    }
  sin_me.sin_port = htons(rport);

  if (connect(sock_remote,
	      (struct sockaddr *) &sin_me, 
	      sizeof(struct sockaddr_in))<0)
    {
      ErrorSetF(0,"spy_open_inet","Error in connect");
      close(sock_remote);
      return(SPY_ERROR_UNKNOWN);
    }
 
  *sid = (SPY_ext_id) malloc(sizeof(SPY_ext_id_base));

  (*sid)->spy_type = SPY_TYPE_INET;
  (*sid)->last_event_nr = 0;
  (*sid)->cid = sock_remote;

  return SPY_ERROR_OK;
}

int spy_close_inet(SPY_ext_id sid)
{
  close(sid->cid);

  free(sid);

  return SPY_ERROR_OK;
}


/* read the requested len */
static int spy_read(int stream_id,char *buf,int len)
{
  int index,count;

  index = 0;
  for (index=0; index<len;)
    {
      count = read(stream_id,&(buf[index]),len-index);
      if (((count<0) && (errno != EINTR)) ||
          (count==0))
	break;  /* connection closed */
                /* do not exit if a new command arrives */
      
      if (count<0) count = 0; /* probably a new command arrived */
      index+=count;
    }

  return index;
}

/* EINTR safe write */
static int spy_write(int stream_id,void *buf,int len)
{
  int count;

  /* if new command arrives, retry */
  do
    count = write(stream_id,buf,len);
  while ((count<0) && (errno==EINTR));

  return count;
}

int spy_get_inet(SPY_ext_id sid,/* IN : SPY_id returned by spy_open_inet */
		 char **buf,    /* OUT: pointer to the data */
		                /* Should be disposed by the caller */
		 int *bufsize)  /* OUT: buffer size */
{
  int sock_remote;
  int buffer_size;
  char *abuf;

  /* Ask for a buffer */
  {
    int data[3];
    int count;
    
    ((unsigned int *) data)[0] = FARMSPYD_MAGIC_NUMBER;
    data[1] = sid->last_event_nr;
    data[2] = -1;
    
    count = spy_write(sock_remote,data,3*sizeof(int));
    if (count!=3*sizeof(int))
      { /* connection closed */
	ErrorSetF(SPY_ERROR_UNKNOWN,"spy_get_inet","Write header failed, connection closed.");
	return SPY_ERROR_UNKNOWN; 
      }
  }
	  
  /* Read the received header */
  {
    int count;
    int msg[3];
    
    count = spy_read(sock_remote,(char *) msg,
		     3*sizeof(int));
    if (count<3*sizeof(int))
      { /* connection closed */
	ErrorSetF(SPY_ERROR_UNKNOWN,"spy_get_inet","Read header failed, connection closed.");
	return SPY_ERROR_UNKNOWN; 
      }
    
    if (((unsigned int *) msg)[0]!=FARMSPYD_MAGIC_NUMBER)
      {
	ErrorSetF(0,"spy_get_inet",
		  "Invalid magic number received from farm daemon (%x,%x), closing connection.",
		  ((unsigned int *) msg)[0],FARMSPYD_MAGIC_NUMBER);
	return(Error_YES);
      }
    
    buffer_size = msg[2];
    if (buffer_size==0)
      {
	*buf = NULL;
	*bufsize = 0;
	return SPY_ERROR_EMPTY;
      }

    sid->last_event_nr = msg[1];
  }
  
  /* Allocate space */
  {
    abuf = (char *) malloc(buffer_size);
  }
  
  /* Read the data in the buffer */
  {
    int count;
    
    count = spy_read(sock_remote,abuf,
		     buffer_size);
    if (count<buffer_size)
      { /* connection closed */
	free(abuf);
	ErrorSetF(SPY_ERROR_UNKNOWN,"spy_get_inet","Read data failed, connection closed.");
	return SPY_ERROR_UNKNOWN; 
      }
  }

  /* Fill the structures */
  *buf = abuf;
  *bufsize = buffer_size;

  return SPY_ERROR_OK;
}