/**********************************************************************
 * vanessa_socket.h                                       November 1999
 * Horms                                             horms@vergenet.net
 *
 * Open a tcp socket to a server (we are the client)
 *
 * vanessa_socket
 * Library to simplify handling of TCP sockets
 * Copyright (C) 2000  Horms
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307 USA
 *
 **********************************************************************/

#ifndef TCP_PIPE_NYM
#define TCP_PIPE_NYM

#include <ctype.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <syslog.h>

#include <vanessa_logger.h>

/* Needed for Solaris */
#ifdef sun
#define timeradd(a, b, result)                                                \
  do {                                                                        \
    (result)->tv_sec = (a)->tv_sec + (b)->tv_sec;                             \
    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec;                          \
    if ((result)->tv_usec >= 1000000)                                         \
      {                                                                       \
        ++(result)->tv_sec;                                                   \
        (result)->tv_usec -= 1000000;                                         \
      }                                                                       \
  } while (0)

#define timersub(a, b, result)                                                \
  do {                                                                        \
    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
    if ((result)->tv_usec < 0) {                                              \
      --(result)->tv_sec;                                                     \
      (result)->tv_usec += 1000000;                                           \
    }                                                                         \
  } while (0)
#endif /* sun */

typedef unsigned int vanessa_socket_flag_t;

#define VANESSA_SOCKET_NONE      (vanessa_socket_flag_t) 0
#define VANESSA_SOCKET_NO_LOOKUP (vanessa_socket_flag_t) 1
#define VANESSA_SOCKET_NO_FROM   (vanessa_socket_flag_t) 2


#ifndef INPORT_ANY
#define INPORT_ANY     ((int)0)
#endif



/**********************************************************************
 * vanessa_socket_client_open_sockaddr_in
 * Open a socket connection as a client
 * pre: to: sockaddr structure specifying address and port to connect 
 *          to.
 *      flag: If VANESSA_SOCKET_NO_LOOKUP then no host and port lookups
 *            will be performed
 * post: socket is opened
 * return: open socket
 *         -1 on error
 **********************************************************************/

int vanessa_socket_client_open_sockaddr_in(
  struct sockaddr_in to,
  const vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_client_open
 * Open a socket connection as a client
 * pre: host: hostname or ipaddress to open socket to
 *      port: name or number to open
 *      flag: If VANESSA_SOCKET_NO_LOOKUP then no host and port lookups
 *            will be performed
 * post: socket is opened
 * return: open socket
 *         -1 on error
 **********************************************************************/

int vanessa_socket_client_open(
  const char *host, 
  const char *port, 
  const vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_client_open_src_sockaddr_in
 * Open a socket connection as a client
 * pre: from: sockaddr structure specifying address and port to connect
 *            from. 
 *            If from.sin_addr.s_addr==INADDR_ANY then the operating 
 *            system will select an appropriate source address.
 *            If from.sin_port==INPORT_ANY then the operating system 
 *            will select an appropriate source address.
 *      to: sockaddr structure specifying address and port to connect 
 *          to.
 *      flag: Logical or of VANESSA_SOCKET_NO_LOOKUP and 
 *            VANESSA_SOCKET_NO_FROM
 *            If flag&VANESSA_SOCKET_NO_LOOKUP then no host and port 
 *            lookups will be performed 
 *            If flag&VANESSA_SOCKET_NO_FROM then the from parameter 
 *            will not be used and the operating system will select a 
 *            source address and port
 * post: socket is opened
 * return: open socket
 *         -1 on error
 **********************************************************************/

int vanessa_socket_client_open_src_sockaddr_in(
  struct sockaddr_in from,
  struct sockaddr_in to,
  const vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_client_src_open
 * Open a socket connection as a client
 * pre: src_host: hostname or ipaddress to open socket to
 *                If NULL then the operating system will select
 *                an appropriate source address.
 *      src_port: name or number to open
 *                If NULL then the operating system will select
 *                an appropriate source port.
 *      dst_host: hostname or ipaddress to open socket to
 *      dst_port: name or number to open
 *      flag: Logical or of VANESSA_SOCKET_NO_LOOKUP and 
 *            VANESSA_SOCKET_NO_FROM
 *            If flag&VANESSA_SOCKET_NO_LOOKUP then no host and port 
 *            lookups will be performed 
 *            If flag&VANESSA_SOCKET_NO_FROM then the from parameter 
 *            will not be used and the operating system will select a 
 *            source address and port
 * post: socket is opened
 * return: open socket
 *         -1 on error
 **********************************************************************/

int vanessa_socket_client_src_open(
  const char *src_host, 
  const char *src_port, 
  const char *dst_host, 
  const char *dst_port, 
  const vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_port_portno
 * port number of a service given as a string either
 * the port number or the service name as per /etc/services
 * pre: port name as per /etc/services or port number as a string
 *      flag: Flags. If the VANESSA_SOCKET_NO_LOOKUP bit is set then
 *            no service lookups will be performed. That is the
 *            port given as an argument should be an port number
 * return: port number
 *         -1 on error or if port name cannot be found in /etc/services
 **********************************************************************/

int vanessa_socket_port_portno(
  const char *port, 
  const vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_str_is_digit
 * Test if a null terminated string is composed entirely of digits (0-9)
 * pre: String
 * return: 1 if string contains only digits and null terminator
 *         0 if string is NULL
 *         0 otherwise
 **********************************************************************/


int vanessa_socket_str_is_digit(const char *str);



/**********************************************************************
 * Notes on read_func and write_func
 *
 * The vanessa_socket_pipe_*func() group of functions take 
 * read_func, write_func and data arguments. read_func and write_func
 * are intended to allow the application to provide its own low
 * level reading and writing routines, for instance to allow
 * reading and writing to SSL/TLS. The data argument is intended
 * to be an opaque data type which may be used by read_func and
 * write_func. These functions should behave as follows.
 * vanessa_socket_pipe_fd_read and vanessa_socket_pipe_fd_write
 * are examples of implementations of these functions that
 * allow reading and writing using read(2) and write(2).
 *
 * read_func
 * pre: fd: file descriptor to read bytes from
 *      buf: buffer to read bytes into
 *      count: maximum number of bytes to read
 *      data: opaque data that may be passed from the application and
 *            used by read_func
 * post: A maximum of count bytes are read into buf.
 *       If an error occurs then it may be logged by this function.
 * return: Number of bytes read (may be zero)
 *         -1 on error
 *
 * write_func
 * pre: fd: file descriptor to write bytes to
 *      buf: buffer to write bytes from
 *      count: maximum number of bytes to write
 *      data: opaque data that may be passed from the application and
 *            used by write_func
 * post: A maximum of count bytes are written from buf
 *       If an error occurs then it may be logged by this function.
 *       installed logger. See vanessa_socket_logger_set().
 * return: Number of bytes read (may be zero)
 *         -1 on error
 **********************************************************************/



/**********************************************************************
 * vanessa_socket_pipe_fd_read
 * Read bytes from fd
 * Intended to be passed to vanessa_socket_pipe_*func()
 * pre: fd: file descriptor to read bytes from
 *      buf: buffer to read bytes into
 *      count: maximum number of bytes to read
 *      data: not used
 * post: A maximum of count bytes are read into buf from fd using read(2)
 *       If an error occurs the errno is read and logged using the
 *       installed logger. See vanessa_socket_logger_set().
 * return: Number of bytes read (may be zero)
 *         -1 on error
 **********************************************************************/

ssize_t vanessa_socket_pipe_fd_read(int fd, void *buf, size_t count, 
                                    void *data);


/**********************************************************************
 * vanessa_socket_pipe_fd_write
 * Write bytes to fd
 * Intended to be passed to vanessa_socket_pipe_*func()
 * pre: fd: file descriptor to write bytes to
 *      buf: buffer to write bytes from
 *      count: maximum number of bytes to write
 *      data: not used
 * post: A maximum of count bytes are written to fd from buf using write(2)
 *       If an error occurs the errno is read and logged using the
 *       installed logger. See vanessa_socket_logger_set().
 * return: Number of bytes read (may be zero)
 *         -1 on error
 **********************************************************************/

ssize_t vanessa_socket_pipe_fd_write(int fd, const void *buf, size_t count, 
                                      void *data);


/**********************************************************************
 * vanessa_socket_pipe_func
 * pipe data between two pairs of file descriptors until there is an 
 * error, timeout or one or both the file descriptors are closed.
 * pre: rfd_a: one of the read file descriptors
 *      wfd_a: one of the write file descriptors
 *      rfd_b: the other read file descriptor
 *      wfd_b: the other write file descriptor
 *      buffer:   allocated buffer to read data into
 *      buffer_length: size of buffer in bytes
 *      idle_timeout:  timeout in seconds to wait for input
 *                     timeout of 0 = infinite timeout
 *      return_a_read_bytes: Pointer to int where number
 *                           of bytes read from a will be recorded.
 *      return_b_read_bytes: Pointer to int where number
 *                           of bytes read from b will be recorded.
 *      read_func: Function to use for low level reading.
 *      write_func: Function to use for low level writing.
 *      fd_a_data: opaque data relating to rfd_a and wfd_a, 
 *                 to pass to read_func and write_func
 *      fd_b_data: opaque data relating to rfd_b and wfd_b, 
 *                 to pass to read_func and write_func
 * post: bytes are read from io_a and written to io_b and vice versa
 * return: -1 on error
 *         1 on idle timeout
 *         0 otherwise (one of io_a or io_b closes gracefully)
 **********************************************************************/

int vanessa_socket_pipe_func(
  int rfd_a,
  int wfd_a,
  int rfd_b,
  int wfd_b,
  unsigned char *buffer, 
  int buffer_length,
  int idle_timeout,
  int *return_a_read_bytes,
  int *return_b_read_bytes,
  ssize_t (*read_func)(int fd, void *buf, size_t count, void *data),
  ssize_t (*write_func)(int fd, const void *buf, size_t count, void *data),
  void *fd_a_data,
  void *fd_b_data
);


/**********************************************************************
 * vanessa_socket_pipe
 * pipe data between two pairs of file descriptors until there is an 
 * error, timeout or one or both the file descriptors are closed.
 * pre: rfd_a: one of the read file descriptors
 *      wfd_a: one of the write file descriptors
 *      rfd_b: the other read file descriptor
 *      wfd_b: the other write file descriptor
 *      buffer:   allocated buffer to read data into
 *      buffer_length: size of buffer in bytes
 *      idle_timeout:  timeout in seconds to wait for input
 *                     timeout of 0 = infinite timeout
 *      return_a_read_bytes: Pointer to int where number
 *                           of bytes read from a will be recorded.
 *      return_b_read_bytes: Pointer to int where number
 *                           of bytes read from b will be recorded.
 * post: bytes are read from io_a and written to io_b and vice versa
 * return: -1 on error
 *         1 on idle timeout
 *         0 otherwise (one of io_a or io_b closes gracefully)
 **********************************************************************/

#define vanessa_socket_pipe( \
  rfd_a,  \
  wfd_a,  \
  rfd_b,  \
  wfd_b,  \
  buffer, \
  buffer_length, \
  idle_timeout, \
  return_a_read_bytes, \
  return_b_read_bytes \
) \
  vanessa_socket_pipe_func( \
    rfd_a,  \
    wfd_a,  \
    rfd_b,  \
    wfd_b,  \
    buffer, \
    buffer_length,  \
    idle_timeout,  \
    return_a_read_bytes, \
    return_b_read_bytes,  \
    vanessa_socket_pipe_fd_read, \
    vanessa_socket_pipe_fd_write,  \
    NULL, \
    NULL \
  )


/**********************************************************************
 * vanessa_socket_pipe_read_write_func
 * Read data from one file io_t and write to another
 * pre: rfd: file descriptor to read from
 *      wfd: file descriptor to write to
 *      buffer: allocated buffer to store read data in
 *      buffer_length: size of the buffer
 *      read_func: function to use for low level reading
 *      write_func: function to use for low level writing
 *      rfd_data: opaque data, relating to rfd, to pass to read_func
 *      wfd_data: opaque data, relating to wfd, to pass to write_func
 * post: at most buffer_length bytes are read from in_fd and written 
 *       to out_fd. 
 * return: bytes read on success
 *         0 on EOF
 *         -1 on error
 **********************************************************************/

int vanessa_socket_pipe_read_write_func(
  int rfd, 
  int wfd, 
  unsigned char *buffer, 
  int buffer_length,
  ssize_t (*read_func)(int fd, void *buf, size_t count, void *data),
  ssize_t (*write_func)(int fd, const void *buf, size_t count, void *data),
  void *rfd_data,
  void *wfd_data
);


/**********************************************************************
 * vanessa_socket_pipe_read_write
 * Read data from one file io_t and write to another
 * pre: rfd: file descriptor to read from
 *      wfd: file descriptor to write to
 *      buffer: allocated buffer to store read data in
 *      buffer_length: size of the buffer
 * post: at most buffer_length bytes are read from in_fd and written 
 *       to out_fd. 
 * return: bytes read on success
 *         0 on EOF
 *         -1 on error
 **********************************************************************/

#define vanessa_socket_pipe_read_write(rfd, wfd, buffer, buffer_length) \
  vanessa_socket_pipe_read_write_func(\
    rfd, wfd, \
    buffer, buffer_length, \
    vanessa_socket_pipe_fd_read, vanessa_socket_pipe_fd_write, \
    NULL, NULL \
  )


/**********************************************************************
 * vanessa_socket_pipe_write_bytes_func
 * write a n bytes of a buffer to fd
 * Pre: fd: file descriptor to write to
 *      buffer: buffer to write
 *      n: number or bytes to write
 *      write_func: function to use for low level writing
 *      fd_data: opaque data relating to fd, to pass to write_func
 * Return: -1 on error
 *         0 otherwise
 **********************************************************************/

int vanessa_socket_pipe_write_bytes_func(
  int fd,
  const unsigned char *buffer, 
  const ssize_t n,
  ssize_t (*write_func)(int fd, const void *buf, size_t count, void *data),
  void *fd_data
);


/**********************************************************************
 * vanessa_socket_pipe_write_bytes
 * write a n bytes of a buffer to fd
 * Pre: fd: file descriptor to write to
 *      buffer: buffer to write
 *      n: number or bytes to write
 * Return: -1 on error
 *         0 otherwise
 **********************************************************************/

#define vanessa_socket_pipe_write_bytes(fd, buffer, n) \
  vanessa_socket_pipe_write_bytes_func(fd, buffer, n, \
    vanessa_socket_pipe_fd_write, NULL)


/**********************************************************************
 * vanessa_socket_server_connect
 * Listen on a tcp port for incoming client connections 
 * When one is received fork
 * In the Child: close the listening file descriptor
 *               return the file descriptor that is 
 *               a socket connection to the client
 * In the Server: close the socket to the client and loop
 * pre: port: port to listen to, an ASCII representation of a number
 *            or an entry from /etc/services
 *      interface_address: If NULL bind to all interfaces, else
 *                         bind to interface(es) with this address.
 *      maximum_connections: maximum number of active connections to
 *                           handle. If 0 then an number of connections 
 *                           is unlimited.
 *      return_from: pointer to a struct_in addr where the 
 *                   connecting client's IP address will
 *                   be placed. Ignored if NULL
 *      return_to: pointer to a in addr where the IP address the 
 *                 server accepted the connection on will be placed.
 *                 Ignored if NULL
 *      flag: If VANESSA_SOCKET_NO_LOOKUP then no host and port lookups
 *            will be performed
 * post: Client sockets are returned in child processes
 *       In the parent process the function doesn't exit, other 
 *       than on error.
 *       if return_from is non-null, it is seeded with clients address
 * return: open client socket, if connection is accepted.
 *         -1 on error
 **********************************************************************/

int vanessa_socket_server_connect(
  const char *port,
  const char *interface_address,
  const unsigned int maximum_connections,
  struct sockaddr_in *return_from,
  struct sockaddr_in *return_to,
  vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_server_connect_sockaddr_in
 * Listen on a tcp port for incoming client connections 
 * When one is received fork
 * In the Child: close the listening file descriptor
 *               return the file descriptor that is 
 *               a socket connection to the client
 * In the Server: close the socket to the client and loop
 * pre: from: sockaddr_in to bind to
 *      maximum_connections: maximum number of active connections to
 *                           handle. If 0 then an number of connections
 *                           is unlimited.
 *      return_from: pointer to a struct_in addr where the 
 *                   connecting client's IP address will
 *                   be placed. Ignored if NULL
 *      return_to: pointer to a in addr where the IP address the 
 *                 server accepted the connection on will be placed.
 *                 Ignored if NULL
 *      flag: ignored
 * post: Client sockets are returned in child processes
 *       In the parent process the function doesn't exit, other 
 *       than on error.
 *       if return_from is non-null, it is seeded with clients address
 * return: open client socket, if connection is accepted.
 *         -1 on error
 **********************************************************************/

int vanessa_socket_server_connect_sockaddr_in(
  struct sockaddr_in from,
  const unsigned int maximum_connections,
  struct sockaddr_in *return_from,
  struct sockaddr_in *return_to,
  vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_server_reaper
 * A signal handler that waits for SIGCHLD and runs wait3 to free
 * the resources of any exited children. This stops zombie processes
 * from hanging around.
 * pre: SIGCHLD is received by the process
 * post: Resources of any exited children are freed
 * return: 0
 **********************************************************************/

void vanessa_socket_server_reaper(void);


/**********************************************************************
 * vanessa_socket_host_in_addr
 * A host is given as a string either as a host name or IP address
 * as a dotted quad. The host is used to seed an in_addr structure
 * with a binary representation of the IP address of the host
 * in network byte order.
 * pre: host: hostname or IP address
 *            If NULL then INADDR_ANY will be converted to network
 *            byte order and used as the address.
 *      in: pointer to an in_addr structure.
 *      flag: Flags. If the VANESSA_SOCKET_NO_LOOKUP bit is set then
 *            no hostname lookups will be performed. That is the
 *            host given as an argument should be an IP address
 * post: none
 * return: 0 on success
 *         -1 on error
 **********************************************************************/

int vanessa_socket_host_in_addr(
  const char *host, 
  struct in_addr *in,
  const vanessa_socket_flag_t flag
);


/**********************************************************************
 * vanessa_socket_host_port_sockaddr_in
 * A host is given as a string either as a host name or IP address
 * as a dotted quad. A port number of a service is given as either a the
 * port number of the service name as per /etc/services. This is used
 * to seed a sockaddr_in structure.
 * pre: host: hostname or IP address
 *            If NULL then INADDR_ANY will be converted to network
 *            byte order and used as the address.
 *      port: port name as per /etc/services or port number as a string
 *      addr: pointer to an sockaddr_in structure.
 *      flag: Flags. If the VANESSA_SOCKET_NO_LOOKUP bit is set then
 *            no lookups will be performed. That is the
 *            host given as an argument should be an IP address and
 *            the port should be a port number
 * post: none
 * return: 0 on success
 *         -1 on error
 **********************************************************************/

int vanessa_socket_host_port_sockaddr_in(
  const char *host,
  const char *port,
  struct sockaddr_in *addr,
  const vanessa_socket_flag_t flag
);


/**********************************************************************
 * Logging functionality
 **********************************************************************/

extern vanessa_logger_t *vanessa_socket_logger;

/**********************************************************************
 * vanessa_socket_logger_set
 * set the logger function to use
 * No logging will take place if logger is set to NULL (default)
 * That is you _must_ call this function to enable logging.
 * pre: logger: pointer to a vanessa_logger
 * post: logger for vanessa_socket is set to logger
 * return: none
 **********************************************************************/

#define vanessa_socket_logger_set(_vl) vanessa_socket_logger=_vl


/**********************************************************************
 * vanessa_socket_logger_unset
 * set logger to NULL
 * That is no logging will take place
 * pre: none
 * post: logger is NULL
 * return: none
 **********************************************************************/

#define vanessa_socket_logger_unset() vanessa_socket_logger_set(NULL)



#endif
