/***************************************
  $Revision: 1.8 $

  Example code: A socket module.

  Status: NOT REVUED, NOT TESTED

  +html+ <DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+   <LI>Adapted from <A HREF="http://www.ibrado.com/sock-faq/sfaq.html#faq65">sample source code</A>.
  +html+ </UL>
  +html+ </DL>
  +html+ <PRE>
  +html+ </PRE>
 
  ******************/ /******************
  Modification History:
        ottrey (08/03/1999) Created from sockhelp.c.
        ottrey (08/03/1998) Heavily butchered.
        joao   (22/06/1999) Modified socket creation and accepts.
  ******************/ /******************
 REMINDER: PUT THE PROPER COPYRIGHT NOTICE HERE
  ***************************************/
#include <arpa/inet.h>
#include "socket.h"
#include "constants.h"
#include "stubs.h"

#include "iproutines.h"

extern int h_errno;


/*+ String sizes +*/
#define STR_S   63
#define STR_M   255
#define STR_L   1023
#define STR_XL  4095
#define STR_XXL 16383

static void log_print(const char *arg) {
  FILE *logf;

  if (CO_get_socket_logging() == 1) {
    if (strcmp(CO_get_socket_logfile(), "stdout") == 0) {
      printf(arg);
    }
    else {
      logf = fopen(CO_get_socket_logfile(), "a");
      fprintf(logf, arg);
      fclose(logf);
    }
  }

} /* log_print() */
 
/* SK_atoport() */
/*++++++++++++++++++++++++++++++++++++++
   Take a service name, and a service type, and return a port number.  If the
   service name is not found, it tries it as a decimal number.  The number
   returned is byte ordered for the network.

  char *service   Service name (or port number).

  char *proto     Protocol (eg "tcp").

  More:
  +html+ <PRE>
  Authors:
        ottrey

  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
int SK_atoport(const char *service, const char *proto) {
  int port;
  long int lport;
  struct servent *serv;
  char *errpos;

  /* First try to read it from /etc/services */
  serv = getservbyname(service, proto);
  if (serv != NULL)
    port = serv->s_port;
  else { /* Not in services, maybe a number? */
    lport = strtol(service,&errpos,0);
    if ( (errpos[0] != 0) || (lport < 1) || (lport > 65535) )
      return -1; /* Invalid port address */
    port = htons(lport);
  }
  return port;
} /* SK_atoport() */


/* SK_close_listening_socket() */
/*++++++++++++++++++++++++++++++++++++++
  XXX Note: Not sure how long this function will last.  Shouldn't _really_ need it.

  More:
  +html+ <PRE>
  Authors:
        ottrey

  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
/*void SK_close_listening_socket() {
  close(listening_socket);         
} */ /* SK_close_listening_socket */

static void func_atexit(void) {
  printf("SK: func_atexit() called\n");
}

static void func_sighup(int n) {
  printf("SK: func_sighup(%d) called\n", n);
}

static void func_sigint(int n) {
  printf("SK: func_sigint(%d) called\n", n);
}


void SK_close(int socket) {
  char print_buf[STR_M];

  sprintf(print_buf, "Closing socket... %d\n", socket); log_print(print_buf); strcpy(print_buf, "");

  close(socket);
}

/* SK_getsock() */
/*++++++++++++++++++++++++++++++++++++++

   This function creates a socket and binds to it

   int      SK_getsock       The new socket

   int      socket_type      SOCK_STREAM or SOCK_DGRAM (TCP or UDP sockets)

   u_short  port             The port to listen on.  Remember that ports < 1024 are
                             reserved for the root user.  Must be passed in network byte
                             order (see "man htons").

   uint32_t bind_address     Address to bind to, in network order.
  More:
  +html+ <PRE>
  Authors:
        ottrey
	joao

  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
int SK_getsock(int socket_type, u_short port, uint32_t bind_address) {
  struct sockaddr_in address;
  int listening_socket;
  int reuse_addr = 1;

  /* Setup internet address information.  
     This is used with the bind() call */
  memset((char *) &address, 0, sizeof(address));
  address.sin_family = AF_INET;
  address.sin_port = port;
  address.sin_addr.s_addr = bind_address;

  /* Map all of the signals and exit routine */
  atexit(func_atexit);
  /* signal.h has a full list of signal names */
  signal(SIGHUP, func_sighup);
  signal(SIGINT, func_sigint);

  listening_socket = socket(AF_INET, socket_type, 0);
  if (listening_socket < 0) {
    perror("socket");
    exit(EXIT_FAILURE);
  }

  setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse_addr, sizeof(reuse_addr));

  if (bind(listening_socket, (struct sockaddr *) &address, sizeof(address)) < 0) {
    perror("bind");
    close(listening_socket);
    exit(EXIT_FAILURE);
  }


  if (socket_type == SOCK_STREAM) {
    listen(listening_socket, 5); /* Queue up to five connections before
                                  having them automatically rejected. */
  }

  return listening_socket;
} /* SK_getsock() */

/*++++++++++++++++++++++++++++++++++++++

   Wait for an incoming connection on the specified socket

   int	SK_accept_connection The socket for communicating to the client

   int  listening_socket     The socket that the server is bound to

  More:
  +html+ <PRE>
  Authors:
	joao
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
int SK_accept_connection(int listening_socket) {
  int connected_socket = -1;
  char print_buf[STR_L];

  while(connected_socket < 0) {
    sprintf(print_buf, "Going to accept connections on socket : %d\n",listening_socket); log_print(print_buf); strcpy(print_buf, "");
/* XXX joao - ? - why is this here?
fflush(NULL);
*/

    connected_socket = accept(listening_socket, NULL, NULL);
    if (connected_socket < 0) {
      /* Either a real error occured, or blocking was interrupted for
         some reason.  Only abort execution if a real error occured. */
      if (errno != EINTR) {
        perror("accept");
        close(listening_socket);
        exit(EXIT_FAILURE);
      } else {
        continue;    /* don't return - do the accept again */
      }
    }
  }
  sprintf(print_buf, "client connected.\n"); log_print(print_buf); strcpy(print_buf, "");

  return connected_socket;
}

/* sock_read() */
/*++++++++++++++++++++++++++++++++++++++

   This is just like the read() system call, except that it will make
   sure that all your data goes through the socket.

   int    sock_read  The number of bytes read.

   int    sockfd    The socket file descriptor.

   char   *buf      The buffer to be read from the socket.

   size_t count     The number of bytes in the buffer.

  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE>
  ++++++++++++++++++++++++++++++++++++++*/
static int sock_read(int sockfd, char *buf, size_t count, unsigned timeout) {
  size_t bytes_read = 0;
  int this_read;

  while (bytes_read < count) {
    do
      this_read = read(sockfd, buf, count - bytes_read);
    while ( (this_read < 0) && (errno == EINTR) );
    if (this_read < 0)
      return this_read;
    else if (this_read == 0)
      return bytes_read;
    bytes_read += this_read;
    buf += this_read;
  }

  return count;

} /* sock_read() */


/* sock_write() */
/*++++++++++++++++++++++++++++++++++++++

   This is just like the write() system call, accept that it will
   make sure that all data is transmitted.

   int    sockfd  The socket file descriptor.

   char   *buf    The buffer to be written to the socket.

   size_t count   The number of bytes in the buffer.

  More:
  +html+ <PRE>
  Authors:
        ottrey

  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
static int sock_write(int sockfd, const char *buf, size_t count, unsigned timeout) {
  size_t  bytes_sent = 0;
  int     this_write;

/*
  printf("sock_write = { sockfd=[%d], buf=[%s], count=[%d]\n", sockfd, buf, count);
*/
  while (bytes_sent < count) {
    do
      this_write = write(sockfd, buf, count - bytes_sent);
    while ( (this_write < 0) && (errno == EINTR) );
    if (this_write <= 0)
      return this_write;
    bytes_sent += this_write;
    buf += this_write;
  }
  return count;
} /* sock_write() */


/* SK_gets() */
/*++++++++++++++++++++++++++++++++++++++

   This function reads from a socket, until it recieves a linefeed
   character.  It fills the buffer "str" up to the maximum size "count".

   int SK_gets  The total_count of bytes read.

   int    sockfd    The socket file descriptor.

   char   *str      The buffer to be written from the socket.

   size_t count     The number of bytes in the buffer.

  More:
  +html+ <PRE>
  Authors:
        ottrey

  Side Effects:
        This function will return -1 if the socket is closed during the read operation.

        Note that if a single line exceeds the length of count, the extra data
        will be read and discarded!  You have been warned.

  To Do:
        Capture the control-c properly!

  +html+ </PRE>

  ++++++++++++++++++++++++++++++++++++++*/
int SK_gets(int sockfd, char *str, size_t count, unsigned timeout) {
  int bytes_read;
  int total_count = 0;
  char *current_position;
  char last_read = 0;

  int control_c = 0;

  current_position = str;
  while (last_read != 10) {
    bytes_read = read(sockfd, &last_read, 1);
    if (bytes_read <= 0) {
      /* The other side may have closed unexpectedly */
      return SK_DISCONNECT; 
      /* Is this effective on other platforms than linux? */
    }
    if ( (total_count < count) && (last_read != 10) && (last_read !=13) ) {
      *current_position = last_read;
      current_position++;
      total_count++;
    }

    if (last_read == -1) {
      bytes_read = read(sockfd, &last_read, 1);
      if (last_read == -12) {
        printf("Client pressed Control-c.\n");
        control_c = 1;
        printf("returning SK_INTERRUPT\n");
        return SK_INTERRUPT;
      }
    }
  }
  if (count > 0) {
    *current_position = 0;
  }

  return total_count;

} /* SK_gets() */


/* SK_puts() */
/*++++++++++++++++++++++++++++++++++++++

   This function writes a character string out to a socket.

   int SK_puts  The total_count of bytes written, 
                or errors (represented as negative numbers)

   int    sockfd    The socket file descriptor.

   char   *str      The buffer to be written from the socket.

   unsigned timeout  timeout in seconds

  More:
  +html+ <PRE>
  Authors:
        ottrey

  Side Effects:
        This function will return -1 if the socket is closed during the write operation.

        Note that if a single line exceeds the length of count, the extra data
        will be read and discarded!  You have been warned.

  +html+ </PRE>

  ++++++++++++++++++++++++++++++++++++++*/
int SK_puts(int sockfd, const char *str, unsigned timeout) {

  return sock_write(sockfd, str, strlen(str), timeout);

} /* SK_puts() */

/* SK_putc() */
/*++++++++++++++++++++++++++++++++++++++

   int SK_putc This function writes a single character out to a socket.

   int sockfd        socket
   char ch           character
   unsigned timeout  timeout in seconds

   return number of chars written 

  ++++++++++++++++++++++++++++++++++++++*/
int SK_putc(int sockfd, char ch, unsigned timeout) {
  return sock_write(sockfd, &ch, 1, timeout);
}/* SK_putc() */

/*++++++++++++++++++++++++++++++++++++++

   This function reads a single character from a socket.

   returns EOF when no character can be read. 

  ++++++++++++++++++++++++++++++++++++++*/
int SK_getc(int sockfd, unsigned timeout) {
  char ch;

  if( read(sockfd, &ch, 1) <= 0 ) {
    return EOF;
  }
  else {
    return ch;
  }
}/* SK_getc() */

/* SK_getpeername() */
/*++++++++++++++++++++++++++++++++++++++

   This function will tell you who is at the other end of a connected stream socket.
   XXX It's not working.
   XXX ? MB it is...

   int    sockfd    The socket file descriptor.

  More:
  +html+ <PRE>
  Authors:
        ottrey
  +html+ </PRE>

  ++++++++++++++++++++++++++++++++++++++*/
char *SK_getpeername(int sockfd) 
{
  char *hostaddress=NULL;
  struct sockaddr_in addr_in;
  int namelen=sizeof(addr_in);
 
  if (getpeername(sockfd, (struct sockaddr *)&addr_in, &namelen) != -1) {
    hostaddress = (char *)malloc(16); /* max length of a valid IPv4 + \0 */
    strcpy(hostaddress, inet_ntoa(addr_in.sin_addr));  /* XXX MT-UNSAFE */
  }

  return hostaddress;
  
} /* SK_getpeername() */

/* SK_getpeerip */
int SK_getpeerip(int sockfd, ip_addr_t *ip) {
  struct sockaddr_in addr_in;
  int namelen=sizeof(addr_in);
  int ret=-1;

  memset(& addr_in, 0, sizeof(struct sockaddr_in));

  if (getpeername(sockfd, (struct sockaddr *)(& addr_in), &namelen) != -1) {
    ret=0;
    IP_addr_s2b(ip, &addr_in, namelen);
  }
  
  return ret;
}

/*-------------------------------------------------------------------
 *   CD varieties of the functions: broken connections get registered
 *   in the connection structure within the query environment 
 *   as side effects.
 * -----------------------------------------------------------------*/

/* SK_cd_puts() */
/*++++++++++++++++++++++++++++++++++++++

   This function writes a character string out to a socket.

   int SK_qe_puts  The total_count of bytes written, 
                or errors (represented as negative numbers)

   sk_conn_st *condat connection data

   char   *str       The buffer to be written from the socket.

   unsigned timeout  timeout in seconds

  More:
       if the connection structure has bad status for this connection
       from previous calls, no write will be attempted.

  +html+ <PRE>
  Authors:
        marek

  Side Effects:
       broken connections get registered
       in the connection structure within the query environment 
	
  +html+ </PRE>

  ++++++++++++++++++++++++++++++++++++++*/
int SK_cd_puts(sk_conn_st *condat, const char *str) {
  int res=SK_puts(condat->sock, str, condat->wr_timeout );

  if( res < 0 ){
    switch( - res ) {
      /* dont know what to do and how to log */
    case SK_DISCONNECT:
    case SK_INTERRUPT:
      /*("Thread received a control-c\n");*/
    case SK_TIMEOUT:
      /*("Reading timed out\n");*/
      break;
    default:
      /* unexpected error code. bail out */
      die;
    }
  }
} /* SK_cd_puts() */

/* SK_cd_gets() */
/*++++++++++++++++++++++++++++++++++++++

   Wrapper around SK_gets.

   int SK_qe_gets  The total_count of bytes read, 
                   or errors (represented as negative numbers)

   sk_conn_st *condat connection data

   char   *str       The buffer to be written from the socket.

  More:
       if the connection structure has bad status for this connection
       from previous calls, no write will be attempted.

  +html+ <PRE>
  Authors:
        marek
	
  Side Effects:
       broken connections get registered
       in the connection structure within the query environment 
       
  +html+ </PRE>

  ++++++++++++++++++++++++++++++++++++++*/
int SK_cd_gets(sk_conn_st *condat, char *str, size_t count) {
  int res=SK_gets(condat->sock, str, count, condat->wr_timeout);
		  
  if( res < 0 ){
    switch( res ) {
      /* dont know what to do and how to log */
    case SK_DISCONNECT:
    case SK_INTERRUPT:
      /*("Thread received a control-c\n");*/
    case SK_TIMEOUT:
      /*("Reading timed out\n");*/
      break;
    default:
      /* unexpected error code. bail out */
      die;
    }
  }
} /* SK_cd_gets() */


int SK_cd_close(sk_conn_st *condat) {
  SK_close(condat->sock);
} /* SK_cd_close() */
