/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "networklib.h"

int string2int(const char * str) {
/* -----------------------------------------------------------------------------
 * Convert a string to int 
 *
 * @param  const char *, the given string
 * @return int   -1 if the given string do not represent an integer
 * -------------------------------------------------------------------------- */
  const char *pts = str;
  int result_int = 0;

  while(*pts != '\0') {
    if ((*pts >= 48) && (*pts <= 57))
      result_int = result_int*10 + (*pts - 48);
    else
      return -1;
    pts++;
  }
  return result_int;
}

int read_buffer(int fd, void * buffer, int count) {
/* -----------------------------------------------------------------------------
 * Read to buffer
 *
 * @param  int, the file descriptor to read on
 * @param  void *, the buffer where to store
 * @param  int, the max number of character to read
 * @return int  the number of character read or -1 if an error occurss
 * -------------------------------------------------------------------------- */
  int status = 0, n;

  while(status < count) {
    n = read(fd, (void *)&((char *)buffer)[status], count - status);
    if (n < 1) {
    /* end-of-file (0) or an error occurs (-1) */
      if (status == 0)
        return n;
      else
        return status;
    }
    status += n;
  }
  return status;
}

int write_buffer(int fd, const void * buffer, int count) {
/* -----------------------------------------------------------------------------
 * Write buffer to a file descriptor
 *
 * @param  int, the file descriptor to write on
 * @param  void *, the buffer that must be written to file descriptor
 * @param  int, the max number of character to read
 * @return int  the number of character written or -1 if an error occurs
 * -------------------------------------------------------------------------- */
  int status = 0, n;

  while(status < count) {
    n = write(fd, (const void *)&((char *)buffer)[status], count - status);
    if (n < 1) {
    /* an error occurs (-1) */
      if (status == 0)
        return n;
      else
        return status;
    }
    status += n;
  }
  return status;
}

int xsend(int socket, int flags, const char * str, ...) {
/* -----------------------------------------------------------------------------
 * Write buffer to a socket
 *
 * @param  int, the socket file descriptor to write on
 * @param  int, flags that are passed to send() function
 * @param  const char *, printf format
 * @param  ...
 * @return int  the number of character written or -1 if an error occurs
 * -------------------------------------------------------------------------- */
  va_list args;
  char *result = NULL;
  int retour;
  va_start(args, str);
  vasprintf(&result, str, args);
  va_end(args);

  if (result != NULL) {
    retour = send(socket, result, strlen(result), flags);
    free(result);
    switch(errno) {
      case EBADF:
      case ENOTSOCK:
      case EFAULT:
      case EMSGSIZE:
      case EAGAIN:
      case ENOBUFS:
      case EINTR:
      case ENOMEM:
      case EINVAL:
      case EPIPE:
        return -1;
        break;
      default:
        return retour;
        break;
    }
  }
  return -1;
}

int readline(int fd, char * buffer, int maxlen) {
/* -----------------------------------------------------------------------------
 * Read line to buffer
 *
 * @param  int, the file descriptor to read on
 * @param  char *, the buffer where to store
 * @param  int, the max number of character to read
 * @return int  the number of character read
 * -------------------------------------------------------------------------- */
  int count = 0, status;

  while(count < maxlen) {
    /* Read one character */
    if ((status = read_buffer(fd, (void *)&((char *)buffer)[count], 1)) <= 0) {
      /* Nothing to read */
      return count;
    }
    else {
      if (buffer[count] == '\n'){
        buffer[count + 1] = '\0';
        return count + 1;
      }
    }
    count += status;
  }
  buffer[count] = 0;
  return 0;
}

int copy(int fd_in, int fd_out, unsigned long maxlen) {
/* -----------------------------------------------------------------------------
 * Copy data to one file descriptor to another
 *
 * @param  int, the file descriptor to read on
 * @param  int, the file descriptor to write on
 * @param  int, the max number of character to read
 * @return int
 * -------------------------------------------------------------------------- */
  char buffer[BUF_SIZE];
  int data_in;
  unsigned long remaining;

  remaining = maxlen;

  while ((remaining != 0) || (maxlen == 0)) {
    if ((remaining == 0) || (BUF_SIZE < remaining)) {
      data_in = read(fd_in, buffer, BUF_SIZE);
    }
    else {
      data_in = read(fd_in, buffer, remaining);
    }

    if (data_in < 1) return data_in;
    write_buffer(fd_out, buffer, data_in);
    if (maxlen != 0) remaining -= data_in;
  }
  return 1;
}

char * getfqdn(const char * host) {
/* -----------------------------------------------------------------------------
 * Get full hostname by IP address or a name
 *
 * @param  const char *, the IP address in human readable format or a host
 * @return char *
 * -------------------------------------------------------------------------- */
  struct hostent *host_info;
  u_long ip_addr;
  static char fqdn[200];

  if (isipv4(host)) {
    ip_addr = inet_addr(host);
    host_info = gethostbyaddr((char *)&ip_addr, sizeof(struct in_addr), AF_INET);
  }
  else {
    host_info = gethostbyname(host);
  }
  if (!host_info) {
    logmsg(LOG_ERR, "%s (%d)", strerror(errno), errno);
    return (char *)NULL;
  }
  if (host_info->h_aliases[0]) {
    strncpy(fqdn, host_info->h_aliases[0], sizeof(fqdn));
  }
  else {
    strncpy(fqdn, host_info->h_name, sizeof(fqdn));
  }
  return fqdn;
}

char * getmyfqdn() {
/* -----------------------------------------------------------------------------
 * Get full hostname of the current machine
 *
 * @return char *
 * -------------------------------------------------------------------------- */
  char hostname[200];
  gethostname(hostname, sizeof(hostname));
  return getfqdn(hostname);
}

char *get_host(const char *host) {
/* -----------------------------------------------------------------------------
 * Get full hostname by IP address or a name
 *
 * @param  const char *, the IP address in human readable format or a host
 * @return char *,
 *   if they are alternate names for the given parameters
 *   they will be returned with a \n separator
 * -------------------------------------------------------------------------- */
  struct hostent *host_info;
  static char hosts[300];
  u_long ip_addr;
  char **aliases;

  if (isipv4(host)) {
    ip_addr = inet_addr(host);
    host_info = gethostbyaddr((char *)&ip_addr, sizeof(struct in_addr), AF_INET);
  }
  else {
    host_info = gethostbyname(host);
  }
  if (!host_info) {
    logmsg(LOG_ERR, "%s (%d)", hstrerror(h_errno), h_errno);
    return (char *)NULL;
  }
  snprintf(hosts, sizeof(hosts), "%s", host_info->h_name);
  for (aliases = host_info->h_aliases; *aliases != 0; aliases++) {
    strlcat(hosts, "\n", sizeof(hosts));
    strlcat(hosts, *aliases, sizeof(hosts));
  }
  return hosts;
}

char *get_ip(const char *host) {
/* -----------------------------------------------------------------------------
 * Get IP addressby host
 *
 * @param  const char *, the hostname
 * @return char *
 *   if they are alternate IP addresses for the given parameters
 *   they will be returned with a \n separator
 * -------------------------------------------------------------------------- */
  struct hostent *host_info;
  static char ip[300];
  u_long ip_addr;
  struct in_addr in;
  char **ip_list;

  if (isipv4(host)) {
    ip_addr = inet_addr(host);
    host_info = gethostbyaddr((char *)&ip_addr, sizeof(struct in_addr), AF_INET);
  }
  else {
    host_info = gethostbyname(host);
  }
  if (!host_info) {
    logmsg(LOG_ERR, "%s (%d)", hstrerror(h_errno), h_errno);
    return (char *)NULL;
  }
  *ip = '\0';
  for (ip_list = host_info->h_addr_list; *ip_list != 0; ip_list++) {
    memcpy(&in.s_addr, *ip_list, sizeof (in.s_addr));
    strlcat(ip, "\n", sizeof(ip));
    strlcat(ip, inet_ntoa(in), sizeof(ip));
  }
  return ip+1;
}

int isipv4(const char *ip) {
/* -----------------------------------------------------------------------------
 * Check if the given IP is a valid IPV4 address in human readable format
 * e.g : 127.0.0.1
 *
 * @param  const char *, the IP to check
 * @return int
 * -------------------------------------------------------------------------- */
  const int dots = 3;
  const char *pts = ip;
  int number = 0, count_dots = 0;

  while(*pts != '\0') {
    if ((*pts >= 48) && (*pts <= 57)) {
      number = number*10 + (*pts - 48);
    }
    else if (*pts == '.') {
      if ((number >= 0) && (number <= 255)) {
        number = 0;
        count_dots++;
      }
      else {
        return 0;
      }
    }
    else {
      return 0;
    }
    pts++;
  }

  if ((count_dots == dots) && (number >= 0) && (number <= 255))
    return 1;
  else
    return 0;
}


unsigned int sockaddr_localinfo(int sockid, struct sockaddr_in * socketaddr) {
/* -----------------------------------------------------------------------------
 * Get local peer info from a socket file descriptor
 *
 * @param  int, the socket file descriptor
 * @param  struct sockaddr_in *, the struct info to fill
 * @return int
 * -------------------------------------------------------------------------- */
  socklen_t size;
  size = sizeof(struct sockaddr_in);
  if (getsockname(sockid, (struct sockaddr *)socketaddr, &size) != 0) {
    logmsg(LOG_ERR, "%s (%d)", strerror(errno), errno);
    return 0;
  }
  return 1;
}

short getlocalport(int sockid) {
/* -----------------------------------------------------------------------------
 * Get local port info from a socket file descriptor
 *
 * @param  int, the socket file descriptor
 * @return short, the local port or -1
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  if (sockaddr_localinfo(sockid, &socketaddr)) {
    return ntohs(socketaddr.sin_port);
  }
  return -1;
}

char * getlocaladdr(int sockid) {
/* -----------------------------------------------------------------------------
 * Get local IP address info from a socket file descriptor
 *
 * @param  int, the socket file descriptor
 * @return char *
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  if (sockaddr_localinfo(sockid, &socketaddr)) {
    return inet_ntoa(socketaddr.sin_addr);
  }
  return NULL;
}

unsigned int sockaddr_peerinfo(int sockid, struct sockaddr_in * socketaddr) {
/* -----------------------------------------------------------------------------
 * Get Remote peer info from a socket file descriptor
 *
 * @param  int, the socket file descriptor
 * @param  struct sockaddr_in *, the struct info to fill
 * @return int
 * -------------------------------------------------------------------------- */
  socklen_t size;
  size = sizeof(struct sockaddr_in);
  if (getpeername(sockid, (struct sockaddr *)socketaddr, &size) != 0) {
    logmsg(LOG_ERR, "%s (%d)", strerror(errno), errno);
    return 0;
  }
  return 1;
}

short getforeignport(int sockid) {
/* -----------------------------------------------------------------------------
 * Get remote port info from a socket file descriptor
 *
 * @param  int, the socket file descriptor
 * @return short, the local port or -1
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  if (sockaddr_peerinfo(sockid, &socketaddr)) {
    return ntohs(socketaddr.sin_port);
  }
  return -1;
}

char *getforeignaddr(int sockid) {
/* -----------------------------------------------------------------------------
 * Get remote IP address info from a socket file descriptor
 *
 * @param  int, the socket file descriptor
 * @return char *
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  if (sockaddr_peerinfo(sockid, &socketaddr)) {
    return inet_ntoa(socketaddr.sin_addr);
  }
  return NULL;
}

void socketaddr_init(struct sockaddr_in * socketaddr) {
/* -----------------------------------------------------------------------------
 * Init a sockaddr_in struct
 *
 * @param  struct sockaddr_in
 * -------------------------------------------------------------------------- */
  memset(socketaddr, 0, sizeof(struct sockaddr_in));
  socketaddr->sin_family = AF_INET;
}

int socketaddr_service(struct sockaddr_in * socketaddr, const char * service, const char * proto) {
/* -----------------------------------------------------------------------------
 * Init a sockaddr_in struct to the given service and protocol
 *
 * @param  struct sockaddr_in
 * @param  const char *, name service or port number
 * @param  const char *, name protocol
 * @return int
 * -------------------------------------------------------------------------- */
  struct servent * service_info;
  int port;

  if (strcmp(service, "0") == 0) {
    socketaddr->sin_port = 0;
  }
  else {
    service_info = getservbyname(service, proto);
    if (service_info) {
      socketaddr->sin_port = service_info->s_port;
    }
    else {
      /* service is a port number */
      port = string2int(service);
      if (port != -1) {
        socketaddr->sin_port = htons((u_short)port);
      }
      else {
        logmsg(LOG_ERR, "%s (%d)", strerror(errno), errno);
        return 0;
      }
    }
  }
  return 1;
}

int socketaddr_host(struct sockaddr_in * socketaddr, const char * host) {
/* -----------------------------------------------------------------------------
 * Init a sockaddr_in struct to the given host
 *
 * @param  struct sockaddr_in
 * @param  const char *, hostname or IP address
 * @return int
 * -------------------------------------------------------------------------- */
  struct hostent * host_info;
  host_info = gethostbyname(host);
  if (!host_info) {
    logmsg(LOG_ERR, "%s (%d)\n", strerror(errno), errno);
    return 0;
  }

  memcpy(&socketaddr->sin_addr, host_info->h_addr, host_info->h_length);
  return 1;
}

int resolveproto(const char * proto) {
/* -----------------------------------------------------------------------------
 * Resolve protocol by name
 *
 * @param  const char *, name protocol like "tcp", "udp"
 * @return int or -1
 * -------------------------------------------------------------------------- */
  struct protoent * proto_info;
  proto_info = getprotobyname(proto);
  if (!proto_info) {
    logmsg(LOG_ERR, "%s (%d)\n", strerror(errno), errno);
    return -1;
  }
  return proto_info->p_proto;
}

int prototype(const char * proto) {
/* -----------------------------------------------------------------------------
 * Resolve IPV4 protocol by name
 *
 * @param  const char *, name protocol like "tcp", "udp"
 * @return int or -1
 * -------------------------------------------------------------------------- */
  if (strcmp(proto, "tcp") == 0) return SOCK_STREAM;
  if (strcmp(proto, "udp") == 0) return SOCK_DGRAM;
  logmsg(LOG_ERR, "Unknown protocol %s", proto);
  return -1;
}

#ifdef USE_UNIX_SOCKET
int create_unix_socket (const char *filename) {
/* -----------------------------------------------------------------------------
 * Create an Unix socket
 *
 * @param  const char *, socket file name
 * @return int, the server socket file descriptor or -1
 * -------------------------------------------------------------------------- */
  struct sockaddr_un socketaddr;
  int sockid;
  int trueval = TRUE;
  size_t size;

  sockid = socket (PF_UNIX, SOCK_STREAM, 0);
  if (sockid < 0) {
    logmsg(LOG_ERR, "%s (%d) : Create Unix Socket error", strerror(errno), errno);
    return -1;
  }

  socketaddr.sun_family = AF_UNIX;
  strlcpy(socketaddr.sun_path, filename, sizeof(socketaddr.sun_path));
#if defined __Linux__
  size = offsetof(struct sockaddr_un, sun_path)
				 + strlen (socketaddr.sun_path) + 1;
#endif
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
  socketaddr.sun_len = sizeof(socketaddr.sun_len)
                       + sizeof(socketaddr.sun_family)
                       + strlen (socketaddr.sun_path) + 1;
  size = socketaddr.sun_len;
#endif
  setsockopt(sockid, SOL_SOCKET, SO_REUSEADDR, &trueval, sizeof(trueval));

  if (!access(socketaddr.sun_path, F_OK))
    unlink(socketaddr.sun_path);
  if (bind (sockid, (struct sockaddr *) &socketaddr, size) < 0) {
    logmsg(LOG_ERR, "%s (%d) : Bind Unix Socket error", strerror(errno), errno);
    return -1;
  }

  if (listen(sockid, 5) < 0) {
    logmsg(LOG_ERR, "%s (%d) : Listen Unix Socket error", strerror(errno), errno);
    return -1;
  }

  return sockid;
}
#endif

int open_unix_fd(const char *filename) {
/* -----------------------------------------------------------------------------
 * Connect to an Unix socket
 *
 * @param  const char *, the socket file name
 * @return int, the client socket file descriptor
 * -------------------------------------------------------------------------- */

  struct sockaddr_un socketaddr;
  int sockid;

  socketaddr.sun_family = AF_UNIX;
  strlcpy(socketaddr.sun_path, filename, sizeof(socketaddr.sun_path));
  sockid = socket(PF_UNIX, SOCK_STREAM, 0);
  if (sockid == -1) return -1;

  if (connect(sockid, (struct sockaddr *)&socketaddr, sizeof(socketaddr)) < 0) {
    logmsg(LOG_ERR, "%s (%d)\n", strerror(errno), errno);
    return -1;
  }
  return sockid;
}

int client_connect(const char * host, const char * port, const char * proto) {
/* -----------------------------------------------------------------------------
 * Connect to a hostname, with the given port and protocol
 *
 * @param  const char *, hostname
 * @param  const char *, port number or name service
 * @param  const char *, name protocol
 * @return int, the client socket descriptor or -1
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  int sockid;

  socketaddr_init(&socketaddr);
  socketaddr_service(&socketaddr, port, proto);
  socketaddr_host(&socketaddr, host);

  sockid = socket(PF_INET, prototype(proto), resolveproto(proto));
  if (sockid < 0) {
    logmsg(LOG_ERR, "%s (%d) : Create Socket error", strerror(errno), errno);
    return -1;
  }

  if (connect(sockid, (struct sockaddr *)&socketaddr, sizeof(socketaddr)) < 0) {
    logmsg(LOG_ERR, "%s (%d) : Connect Socket error", strerror(errno), errno);
    return -1;
  }
  return sockid;
}

int server_init(const char * port, const char * proto) {
/* -----------------------------------------------------------------------------
 * Init a server with the given port and protocol
 *
 * @param  const char *, port number or name service
 * @param  const char *, name protocol
 * @return int, the server socket descriptor or -1
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  int mastersock;
  int trueval = TRUE;

  socketaddr_init(&socketaddr);
  socketaddr.sin_addr.s_addr = INADDR_ANY;
  if (port != NULL)
    socketaddr_service(&socketaddr, port, proto);

  mastersock = socket(PF_INET, prototype(proto), resolveproto(proto));
  if (mastersock < 0) {
    logmsg(LOG_ERR, "%s (%d) : Create Server Socket error", strerror(errno), errno);
    return -1;
  }

  setsockopt(mastersock, SOL_SOCKET, SO_REUSEADDR, &trueval, sizeof(trueval));
  if (bind(mastersock, (struct sockaddr *) &socketaddr, sizeof(socketaddr)) < 0) {
    logmsg(LOG_ERR, "%s (%d) : Bind Socket error", strerror(errno), errno);
    return -1;
  }

  if (prototype(proto) == SOCK_STREAM) {
    if (listen(mastersock, 5) < 0) {
      logmsg(LOG_ERR, "%s (%d) : Listen Socket error", strerror(errno), errno);
      return -1;
    }
  }

  return mastersock;
}


syntax highlighted by Code2HTML, v. 0.9.1