/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Connection Functions
 * ----------------------------------------------------------------
 * Copyright (C) 1997-2007 Jonas Kvinge <jonas@night-light.net>
 * All rights reserved.
 *
 * 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 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Last modified by:
 * Jonas Kvinge (24.11.2007)
 *
 */

#define CONN_CONNECTION_C

#define NEED_SYS_TYPES_H 1		/* Extra types */
#define NEED_SYS_PARAM_H 1		/* Some systems need this */
#define NEED_LIMITS_H 0			/* Kernel limits */
#define NEED_STDARG_H 1			/* va_list, etc */
#define NEED_ERRNO_H 1			/* errno */
#define NEED_CTYPE_H 1			/* isdigit(), etc */
#define NEED_NETINET_IN_H 1		/* in_addr, sockaddr_in, etc */
#define NEED_ARPA_INET_H 1		/* inet_ntoa(), inet_aton(), etc */
#define NEED_STDIO_H 1			/* Standard C UNIX functions */
#define NEED_STDLIB_H 1			/* malloc(), exit(), atoi(), etc */
#define NEED_TIME_H 1			/* time(), etc */
#define NEED_SYSCTL_H 0			/* sysctl(), etc */
#define NEED_SYS_STAT_H 0		/* chmod(), mkdir(), etc */
#define NEED_SYS_UIO_H 0		/* iovec, etc */
#define NEED_FCNTL_H 1			/* open(), creat(), fcntl(), etc */
#define NEED_SYS_IOCTL_H 1		/* ioctl(), etc */
#define NEED_SYS_FILIO_H 1		/* Solaris need this for ioctl(), etc */
#define NEED_UNISTD_H 1			/* Unix standard functions */
#define NEED_STRING_H 1			/* C string functions */
#define NEED_SIGNAL_H 0			/* Signal functions */
#define NEED_SYS_SOCKET_H 1		/* Socket functions */
#define NEED_NETDB_H 1			/* Network database functions */
#define NEED_ARPA_NAMESER_H 0		/* Nameserver definitions */
#define NEED_GETUSERPW_HEADERS 0 	/* Functions to retrive system passwords */
#define NEED_ARES 1			/* Functions needed for ares */
#define NEED_SSL 1			/* Needed for SSL support */

#include "includes.h"
#include "irc.h"

#include "conf.h"
#include "access_conf.h"
#include "conn_conf.h"

#include "conn.h"
#include "conn_io.h"
#if SSL_SUPPORT
  #include "conn_io_ssl.h"
#endif
#include "conn_connection.h"
#include "conn_sendq.h"
#include "conn_log.h"
#include "conn_parser.h"
#include "conn_ignore.h"

#include "client.h"
#include "client_connection.h"
#include "client_notice.h"

#include "chan.h"
#include "chan_user.h"

/* VARIABLES - JONAS (31.07.2001) */

extern struct Conn_Struct *Conn_Head;

extern struct Conf_Struct ConfS;
extern unsigned short int Root;

#if ARES
  extern ares_channel Ares_Channel;
#endif

#if SSL_SUPPORT
  extern SSL_CTX *IRCPROXY_SSL_CLIENT_CTX;
#endif

extern struct Client_Struct *Client_Head;

/* CONN_CONNECT FUNCTION - JONAS (17.07.2001) */

void conn_connect(struct Conn_Struct *ConnS) {

  signed long int Result = 0;
#if !ARES
    struct hostent *HostEnt = NULL;
#endif

  assert(ConnS != NULL);

  if (!Conn_IsConnectProc(ConnS)) {

    Conn_SetConnectProc(ConnS);
    ConnS->ConnectProcTime = NOW;

    if (ConnS->ConnServerTry == NULL) {

      struct ConnServer_Struct *ConnServer = NULL;

      for (ConnServer = ConnS->Server_Head ; ConnServer != NULL ; ConnServer = ConnServer->Next) {
        if ((ConnS->ConnServerTry == NULL) || (ConnServer->Tries < ConnS->ConnServerTry->Tries)) { ConnS->ConnServerTry = ConnServer; }
      }
      if (ConnS->ConnServerTry == NULL) { conn_disconnect(ConnS, "Connection %s: No servers to try.", ConnS->Name); return; }
    }

    ++ConnS->ConnServerTry->Tries;
    ConnS->ServerHostName = strrealloc(ConnS->ServerHostName, ConnS->ConnServerTry->Host);
    ConnS->ServerName = strrealloc(ConnS->ServerName, ConnS->ConnServerTry->Host);
    ConnS->ServerPortH = ConnS->ConnServerTry->Port;
    ConnS->ServerPortN = htons(ConnS->ConnServerTry->Port);
    ConnS->ServerPass = strrealloc(ConnS->ServerPass, ConnS->ConnServerTry->Pass);
    ConnS->ConnServerTry = NULL;

  }

  if (!Host_IsHostToIP(ConnS->ResolveFlags)) {

    if (ConnS->Host[0] == '*') {
#if IPV6_SUPPORT
      if (ConnConf_IsIPv6(ConnS)) {
        memset(&ConnS->INAddr6, 0, sizeof(ConnS->INAddr6));
        ConnS->INAddr6 = in6addr_any;
      }
      else {
#endif /* IPV6_SUPPORT */
        memset(&ConnS->INAddr, 0, sizeof(ConnS->INAddr));
        ConnS->INAddr.s_addr = INADDR_ANY;
#if IPV6_SUPPORT
      }
#endif /* IPV6_SUPPORT */
      Host_SetHostToIP(ConnS->ResolveFlags);
    }
    else {
#if IPV6_SUPPORT
      if (ConnConf_IsIPv6(ConnS)) {
        memset(&ConnS->INAddr6, 0, sizeof(ConnS->INAddr6));
        Result = inet_pton(AF_INET6, ConnS->Host, &ConnS->INAddr6);
      }
      else {
        memset(&ConnS->INAddr, 0, sizeof(ConnS->INAddr));
        Result = inet_pton(AF_INET6, ConnS->Host, &ConnS->INAddr);
      }
#else /* IPV6_SUPPORT */
      memset(&ConnS->INAddr, 0, sizeof(ConnS->INAddr));
      Result = inet_aton(ConnS->Host, &ConnS->INAddr);
#endif /* IPV6_SUPPORT */
      if (Result <= 0) {
        ConnS->HostName = strrealloc(ConnS->HostName, ConnS->Host);
        if (aerrno != AESUCCESS) {
          conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
          return;
        }
        Host_SetResolving(ConnS->ResolveFlags);
        client_noticealluser(ConnS->User, "Connection %s: Resolving local hostname %s to IP-address.", ConnS->Name, ConnS->HostName);
#if ARES
#if IPV6_SUPPORT
        if (ConnConf_IsIPv6(ConnS)) { ares_gethostbyname(Ares_Channel, ConnS->HostName, AF_INET6, (ares_host_callback) conn_hosttoip, ConnS); }
        else {
#endif /* IPV6_SUPPORT */
          ares_gethostbyname(Ares_Channel, ConnS->HostName, AF_INET, (ares_host_callback) conn_hosttoip, ConnS);
#if IPV6_SUPPORT
        }
#endif /* IPV6_SUPPORT */
        return;
#else /* ARES */
#if IPV6_SUPPORT
        if (ConnConf_IsIPv6(ConnS)) { HostEnt = gethostbyname2(ConnS->HostName, AF_INET6); }
        else { HostEnt = gethostbyname2(ConnS->HostName, AF_INET); }
#else /* IPV6_SUPPORT */
        HostEnt = gethostbyname(ConnS->HostName);
#endif /* IPV6_SUPPORT */
        conn_hosttoip(ConnS, errno, HostEnt);
        if (!Host_IsHostToIP(ConnS->ResolveFlags)) { return; }
#endif /* ARES */
      }
      else {
        ConnS->HostIPS = strrealloc(ConnS->HostIPS, ConnS->Host);
        if (aerrno != AESUCCESS) {
          conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
          return;
        }
        ConnS->HostName = strrealloc(ConnS->HostName, ConnS->Host);
        if (aerrno != AESUCCESS) {
          conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
          return;
        }
        Host_SetHostToIP(ConnS->ResolveFlags);
      }
    }
  }
  Host_SetResolved(ConnS->ResolveFlags);

 if (!Host_IsHostToIP(ConnS->ServerResolveFlags)) {
#if IPV6_SUPPORT
    if (ConnConf_IsIPv6(ConnS)) {
      memset(&ConnS->ServerINAddr6, 0, sizeof(ConnS->ServerINAddr6));
      Result = inet_pton(AF_INET6, ConnS->ServerHostName, &ConnS->ServerINAddr6);
    }
    else {
      memset(&ConnS->ServerINAddr, 0, sizeof(ConnS->ServerINAddr));
      Result = inet_pton(AF_INET6, ConnS->ServerHostName, &ConnS->ServerINAddr);
    }
#else /* IPV6_SUPPORT */
    memset(&ConnS->ServerINAddr, 0, sizeof(ConnS->ServerINAddr));
    Result = inet_aton(ConnS->ServerHostName, &ConnS->ServerINAddr);
#endif /* IPV6_SUPPORT */
    if (Result <= 0) {
      Host_SetResolving(ConnS->ServerResolveFlags);
      client_noticealluser(ConnS->User, "Connection %s: Resolving server hostname %s to IP-address.", ConnS->Name, ConnS->ServerHostName);
#if ARES
#if IPV6_SUPPORT
      if (ConnConf_IsIPv6(ConnS)) { ares_gethostbyname(Ares_Channel, ConnS->ServerHostName, AF_INET6, (ares_host_callback) conn_serverhosttoip, ConnS); }
      else {
#endif /* IPV6_SUPPORT */
        ares_gethostbyname(Ares_Channel, ConnS->ServerHostName, AF_INET, (ares_host_callback) conn_serverhosttoip, ConnS);
#if IPV6_SUPPORT
      }
#endif /* IPV6_SUPPORT */
      return;
#else /* ARES */
#if IPV6_SUPPORT
      if (ConnConf_IsIPv6(ConnS)) { HostEnt = gethostbyname2(ConnS->ServerHostName, AF_INET6); }
      else { HostEnt = gethostbyname2(ConnS->ServerHostName, AF_INET); }
#else /* IPV6_SUPPORT */
      HostEnt = gethostbyname(ConnS->ServerHostName);
#endif /* IPV6_SUPPORT */
      conn_serverhosttoip(ConnS, errno, HostEnt);
      if (!Host_IsHostToIP(ConnS->ServerResolveFlags)) { return; }
#endif /* ARES */
    }
    else {
      ConnS->ServerHostIPS = strrealloc(ConnS->ServerHostIPS, ConnS->ServerHostName);
      if (aerrno != AESUCCESS) {
        conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
        return;
      }
      Host_SetHostToIP(ConnS->ServerResolveFlags);
    }
  }
  Host_SetResolved(ConnS->ServerResolveFlags);

  if (!Conn_IsSocket(ConnS)) {

#if !WIN32
    sysseteuidbyuser(ConnS->User);
#endif
#if IPV6_SUPPORT
    if (ConnConf_IsIPv6(ConnS)) { Result = socket(AF_INET6, SOCK_STREAM, 0); }
    else {
#endif
      Result = socket(AF_INET, SOCK_STREAM, 0);

#if IPV6_SUPPORT
    }
#endif
#if !WIN32
    sysseteuidnormal();
#endif
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to create socket: [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
    ConnS->FD = Result;
    Conn_SetSocket(ConnS);

#if SSL_SUPPORT
    if (ConnConf_IsSSL(ConnS)) {
      ConnS->SSL_H = SSL_new(IRCPROXY_SSL_CLIENT_CTX);
      if (ConnS->SSL_H == NULL) {
        conn_disconnect(ConnS, "Connection %s: Failed to create a new SSL structure: [%d] %s", ConnS->Name, errno, strerror(errno));
        return;
      }

      SSL_set_connect_state(ConnS->SSL_H);

      Result = SSL_set_fd(ConnS->SSL_H, ConnS->FD);
      if (Result <= 0) {
        signed long int sslerrno = SSL_get_error(ConnS->SSL_H, Result);
        conn_disconnect(ConnS, "Connection %s: Unable to connect the SSL object with the file descriptor: [%ld] %s", ConnS->Name, sslerrno, ERR_error_string(sslerrno, NULL));
        return;
      }

    }
#endif

  }

  if (!Conn_IsSockOPT(ConnS)) {

    unsigned long int Flags = 0;

#if defined(NBLOCK_SYSV)
    Flags = 1;
    Result = ioctl(ConnS->FD, FIONBIO, &Flags);
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to set socket in non-blocking mode using ioctl(): [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
#else
    Result = fcntl(ConnS->FD, F_GETFL);
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to get socket flags using fcntl(): [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
    Flags = Result;
#if defined(NBLOCK_BSD)
    Flags |= O_NDELAY;
#elif defined(NBLOCK_POSIX)
    Flags |= O_NONBLOCK;
#else
    #warning "This system does not support non-blocking sockets?"
    Flags |= O_NONBLOCK;
#endif
    Result = fcntl(ConnS->FD, F_SETFL, Flags);
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to set socket in non-blocking mode using fcntl(): [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
#endif

    Conn_SetSockOPT(ConnS);

  }

  if ((CONN_SOCKKEEPALIVEOPT == TRUE) && (!Conn_IsKeepAliveOPT(ConnS))) {

    unsigned long int OPT = 1;

    Result = setsockopt(ConnS->FD, SOL_SOCKET, SO_KEEPALIVE, &OPT, sizeof(OPT));
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to set Keep Alive option for socket: [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }

    Conn_SetKeepAliveOPT(ConnS);

  }

  if (!Conn_IsBound(ConnS)) {

    struct sockaddr_in SockAddr = { 0 };
#if IPV6_SUPPORT
    struct sockaddr_in6 SockAddr6 = { 0 };
#endif

#if IPV6_SUPPORT
    if (ConnConf_IsIPv6(ConnS)) {
      memset(&SockAddr6, 0, sizeof(SockAddr6));
      SockAddr6.sin6_family = AF_INET6;
      SockAddr6.sin6_addr = ConnS->INAddr6;
      SockAddr6.sin6_port = 0;
      Result = bind(ConnS->FD, (struct sockaddr *) &SockAddr6, sizeof(SockAddr6));
    }
    else {
#endif
      memset(&SockAddr, 0, sizeof(SockAddr));
      SockAddr.sin_family = AF_INET;
      SockAddr.sin_addr = ConnS->INAddr;
      SockAddr.sin_port = 0;
      Result = bind(ConnS->FD, (struct sockaddr *) &SockAddr, sizeof(SockAddr));
#if IPV6_SUPPORT
    }
#endif
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to bind socket to %s(%s): [%d] %s", ConnS->Name, ConnS->HostName, ConnS->HostIPS, errno, strerror(errno));
      return;
    }
    Conn_SetBound(ConnS);

  }

  if (!Conn_IsConnect(ConnS)) {

    struct sockaddr_in SockAddr = { 0 };
#if IPV6_SUPPORT
    struct sockaddr_in6 SockAddr6 = { 0 };
#endif

    client_noticealluser(ConnS->User, "Connection %s: Connecting to server %s(%s):%ld%s.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, (ConnConf_IsSSL(ConnS) ? " (SSL)" : ""));

#if IPV6_SUPPORT
    if (ConnConf_IsIPv6(ConnS)) {
      memset(&SockAddr6, 0, sizeof(SockAddr6));
      SockAddr6.sin6_family = AF_INET6;
      SockAddr6.sin6_addr = ConnS->ServerINAddr6;
      SockAddr6.sin6_port = ConnS->ServerPortN;
      Result = connect(ConnS->FD, (struct sockaddr *) &SockAddr6, sizeof(SockAddr6));
    }
    else {
#endif
      memset(&SockAddr, 0, sizeof(SockAddr));
      SockAddr.sin_family = AF_INET;
      SockAddr.sin_addr = ConnS->ServerINAddr;
      SockAddr.sin_port = ConnS->ServerPortN;
      Result = connect(ConnS->FD, (struct sockaddr *) &SockAddr, sizeof(SockAddr));
#if IPV6_SUPPORT
    }
#endif
    if (Result <= ERROR) {
      if (errno == EINPROGRESS) {
        Conn_SetConnecting(ConnS);
        ConnS->ConnectTime = NOW;
        conn_logon(ConnS);
        return;
      }
      else {
        conn_disconnect(ConnS, "Connection %s: Unable to connect to %s(%s):%ld%s: [%d] %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, (ConnConf_IsSSL(ConnS) ? " (SSL)" : ""), errno, strerror(errno));
        return;
      }
    }
    else {
      Conn_SetConnected(ConnS);
      ConnS->ConnectTime = NOW;
      ConnS->ConnectedTime = NOW;
      conn_logon(ConnS);
      client_noticealluser(ConnS->User, "Connection %s: Socket connected to %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH);
    }
  }

  if (!Conn_IsConnected(ConnS)) {

    int Error = 0;
    getsockopt_optlen_type ErrorLen = sizeof(Error);

    Result = getsockopt(ConnS->FD, SOL_SOCKET, SO_ERROR, &Error, &ErrorLen);
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to get connection result for %s(%s):%ld%s: [%d] %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, (ConnConf_IsSSL(ConnS) ? " (SSL)" : ""), errno, strerror(errno));
      return;
    }
    if (Error != SUCCESS) {
      conn_disconnect(ConnS, "Connection %s: Unable to connect to %s(%s):%ld%s: [%d] %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, (ConnConf_IsSSL(ConnS) ? " (SSL)" : ""), Error, strerror(Error));
      return;
    }

    client_noticealluser(ConnS->User, "Connection %s: Socket connected to %s(%s):%ld%s.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, (ConnConf_IsSSL(ConnS) ? " (SSL)" : ""));
    Conn_ClearConnecting(ConnS);
    Conn_SetConnected(ConnS);
    ConnS->ConnectedTime = NOW;

  }

  if (!Conn_IsSockName(ConnS)) {

    struct sockaddr_in SockAddr = { 0 };
    getsockname_namelen_type SockAddrLen = sizeof(SockAddr);

    memset(&SockAddr, 0, sizeof(SockAddr));
    Result = getsockname(ConnS->FD, (struct sockaddr *) &SockAddr, &SockAddrLen);
    if (Result <= ERROR) {
      conn_disconnect(ConnS, "Connection %s: Unable to get socket name for %s(%s):%ld%s: [%d] %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, (ConnConf_IsSSL(ConnS) ? " (SSL)" : ""), errno, strerror(errno));
      return;
    }

    ConnS->PortN = SockAddr.sin_port;
    ConnS->PortH = ntohs(ConnS->PortN);
    Conn_SetSockName(ConnS);

  }

#if SSL_SUPPORT

  if ((ConnConf_IsSSL(ConnS)) && (!Conn_IsSSLHandshake(ConnS))) {
    DEBUGPRINT(BITMASK_MAIN, "Connection %s: Doing SSL Handshake.", ConnS->Name);
    Result = SSL_connect(ConnS->SSL_H);
    if (Result <= 0) {
      signed long int sslerrno = SSL_get_error(ConnS->SSL_H, Result);
      if ((sslerrno == SSL_ERROR_WANT_READ) || (sslerrno == SSL_ERROR_WANT_WRITE)) { return; }
      conn_disconnect(ConnS, "Connection %s: Failed SSL Handshake for %s(%s):%ld: [%ld] %s.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, sslerrno, ERR_error_string(sslerrno, NULL));
      return;
    }
    Conn_SetSSLHandshake(ConnS);
    ConnS->Cert = SSL_get_peer_certificate(ConnS->SSL_H);
    sysprint(BITMASK_MAIN, "Connection %s: SSL Handshake completed successfully.", ConnS->Name);
  }

#endif

  Conn_ClearConnectProc(ConnS); /* MISSION COMPLETE! */

}

/* CONN_DISCONNECT FUNCTION - JONAS (01.07.2000) */

void conn_disconnect(struct Conn_Struct *ConnS, const char *const LinePT, ...) {

  char Line[IRCNOTICELEN+1] = "";
  va_list Args = { 0 };
  struct Client_Struct *ClientS = NULL;

  assert(ConnS != NULL);
  assert(LinePT != NULL);

  va_start(Args, LinePT);
  vsnprintf(Line, IRCNOTICELEN+1, LinePT, Args);
  va_end(Args);

  client_noticealluser(ConnS->User, "%s", Line);

  if (Conn_IsWelcome(ConnS)) {
    for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {
      if (ClientS->ConnS == ConnS) { client_close(ClientS, "Disconnected from attached connection %s server %s(%s):%ld.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH); }
    }
  }

#if SSL_SUPPORT
  if (ConnConf_IsSSL(ConnS)) {
    if (ConnS->Cert != NULL) {
      X509_free(ConnS->Cert);
      ConnS->Cert = NULL;
    }
    if (ConnS->SSL_H != NULL) {
      SSL_shutdown(ConnS->SSL_H);
      SSL_free(ConnS->SSL_H);
      ConnS->SSL_H = NULL;
    }
  }
#endif /* SSL_SUPPORT */

  if (Conn_IsSocket(ConnS)) {
    close(ConnS->FD);
    ConnS->FD = FD_NONE;
  }

  conn_initconnection(ConnS);

  if (ConnS->ConnServerTry != NULL) { conn_connect(ConnS); }

}

/* CONN_HOSTTOIP FUNCTION - JONAS (01.03.2000) */

#if HAVE_CARES_CALLBACK_TIMEOUTS
void conn_hosttoip(void *ArgPT, int ErrNo, int Timeouts, struct hostent *HostEnt) {
#else
void conn_hosttoip(void *ArgPT, int ErrNo, struct hostent *HostEnt) {
#endif

  struct Conn_Struct *ConnS = ArgPT;
  const char *HostIPPT = NULL;
#if IPV6_SUPPORT
  char Host[INET6_ADDRSTRLEN+1] = "";
#endif

  assert(ConnS != NULL);

  Host_ClearResolving(ConnS->ResolveFlags);

  if ((HostEnt == NULL) || (HostEnt->h_length < 1)) {
    conn_disconnect(ConnS, "Connection %s: Unable to resolve hostname %s to IP-address: [%d] %s", ConnS->Name, ConnS->HostName, ErrNo, res_strerror(ErrNo));
    return;
  }
#if IPV6_SUPPORT
  if (ConnConf_IsIPv6(ConnS)) {
    memset(&ConnS->INAddr6, 0, sizeof(ConnS->INAddr6));
    memcpy(&ConnS->INAddr6, HostEnt->h_addr, HostEnt->h_length);
    HostIPPT = inet_ntop(AF_INET6, &ConnS->INAddr6, Host, INET6_ADDRSTRLEN);
    if (HostIPPT == NULL) {
      conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
  }
  else {
    memset(&ConnS->INAddr, 0, sizeof(ConnS->INAddr));
    memcpy(&ConnS->INAddr, HostEnt->h_addr, HostEnt->h_length);
    HostIPPT = inet_ntop(AF_INET, &ConnS->INAddr, Host, INET6_ADDRSTRLEN);
    if (HostIPPT == NULL) {
      conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
  }
#else /* IPV6_SUPPORT */
  memset(&ConnS->INAddr, 0, sizeof(ConnS->INAddr));
  memcpy(&ConnS->INAddr, HostEnt->h_addr, HostEnt->h_length);
  HostIPPT = inet_ntoa(ConnS->INAddr);
  if (HostIPPT == NULL) {
    conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
    return;
  }
#endif /* IPV6_SUPPORT */
  ConnS->HostIPS = strrealloc(ConnS->HostIPS, HostIPPT);
  if (ConnS->HostIPS == NULL) {
    conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
    return;
  }

  client_noticealluser(ConnS->User, "Connection %s: Resolved hostname %s to IP-address %s.", ConnS->Name, ConnS->HostName, ConnS->HostIPS);

  Host_SetHostToIP(ConnS->ResolveFlags);

#if ARES
  conn_connect(ConnS);
#endif

}

/* CONN_SERVERHOSTTOIP FUNCTION - JONAS (01.03.2000) */

#if HAVE_CARES_CALLBACK_TIMEOUTS
void conn_serverhosttoip(void *ArgPT, int ErrNo, int Timeouts, struct hostent *HostEnt) {
#else
void conn_serverhosttoip(void *ArgPT, int ErrNo, struct hostent *HostEnt) {
#endif

  struct Conn_Struct *ConnS = ArgPT;
  const char *HostIPPT = NULL;
#if IPV6_SUPPORT
  char Host[INET6_ADDRSTRLEN+1] = "";
#endif

  assert(ConnS != NULL);

  Host_ClearResolving(ConnS->ServerResolveFlags);

  if ((HostEnt == NULL) || (HostEnt->h_length < 1)) {
    conn_disconnect(ConnS, "Connection %s: Unable to resolve server hostname %s to IP-Address: [%d] %s", ConnS->Name, ConnS->ServerHostName, ErrNo, res_strerror(ErrNo));
    return;
  }

#if IPV6_SUPPORT
  if (ConnConf_IsIPv6(ConnS)) {
    memset(&ConnS->ServerINAddr6, 0, sizeof(ConnS->ServerINAddr6));
    memcpy(&ConnS->ServerINAddr6, HostEnt->h_addr, HostEnt->h_length);
    HostIPPT = inet_ntop(AF_INET6, &ConnS->ServerINAddr6, Host, INET6_ADDRSTRLEN);
    if (HostIPPT == NULL) {
      conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
  }
  else {
    memset(&ConnS->ServerINAddr, 0, sizeof(ConnS->ServerINAddr));
    memcpy(&ConnS->ServerINAddr, HostEnt->h_addr, HostEnt->h_length);
    HostIPPT = inet_ntop(AF_INET, &ConnS->ServerINAddr, Host, INET6_ADDRSTRLEN);
    if (HostIPPT == NULL) {
      conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
      return;
    }
  }
#else /* IPV6_SUPPORT */
  memset(&ConnS->ServerINAddr, 0, sizeof(ConnS->ServerINAddr));
  memcpy(&ConnS->ServerINAddr, HostEnt->h_addr, HostEnt->h_length);
  HostIPPT = inet_ntoa(ConnS->ServerINAddr);
  if (HostIPPT == NULL) {
    conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
    return;
  }
#endif /* IPV6_SUPPORT */
  ConnS->ServerHostIPS = strrealloc(ConnS->ServerHostIPS, HostIPPT);
  if (ConnS->ServerHostIPS == NULL) {
    conn_disconnect(ConnS, "Connection %s: Memory allocation failure: [%d] %s", ConnS->Name, errno, strerror(errno));
    return;
  }

  client_noticealluser(ConnS->User, "Connection %s: Resolved server hostname %s to IP-address %s.", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS);

  Host_SetHostToIP(ConnS->ServerResolveFlags);

#if ARES
  conn_connect(ConnS);
#endif

}

/* CONN_LOGON FUNCTION - JONAS (01.07.2000) */

void conn_logon(struct Conn_Struct *ConnS) {

  char *NickPT = ConnS->Nick;
  char *InfoPT = ConnS->Info;

  assert(ConnS != NULL);

  if (ConnS->NumClients <= 0) {
    NickPT = ConnS->AwayNick;
    Conn_SetSentAwayNick(ConnS);
  }
  else {
    NickPT = ConnS->Nick;
    Conn_SetSentNick(ConnS);
  }

if ((ConfS.UnixPasswd == TRUE) && (Root == TRUE)) {
  if (strcmp(ConnS->Info, "0") == FALSE) {
    InfoPT = sysgetnamefromuser(ConnS->User);
    if (InfoPT == NULL) { InfoPT = "0"; }
  }
}

  if ((ConnS->ServerPass != NULL) && (strcmp(ConnS->ServerPass, CONN_CONF_SERVERPASS_NONE) != FALSE)) {
    conn_addsendq(ConnS, "PASS %s", ConnS->ServerPass);
  }
  conn_addsendq(ConnS, "NICK %s", NickPT);
  conn_addsendq(ConnS, "USER %s 0 0 :%s", ConnS->User, InfoPT);

}

/* CONN_ADDSEND FUNCTION - JONAS (01.07.2000) */

void conn_addsend(struct Conn_Struct *ConnS, const char *const LinePT) {

  char LineCRLF[IRCMSGCRLFLEN+1] = "";
  unsigned long int Len = 0;
  unsigned long int OldLen = 0;
  unsigned long int NewLen = 0;
  char *SendBufferPT = NULL;

  assert(ConnS != NULL);
  assert(LinePT != NULL);
  assert(Conn_IsSocket(ConnS));

  snprintf(LineCRLF, IRCMSGCRLFLEN+1, "%s\r\n", LinePT);

  Len = strlen(LineCRLF);
  if (ConnS->SendBuffer == NULL) { OldLen = 0; }
  else { OldLen = strlen(ConnS->SendBuffer); }
  NewLen = OldLen + Len + 1;
  SendBufferPT = realloc(ConnS->SendBuffer, NewLen);
  if (SendBufferPT == NULL) { return; }
  ConnS->SendBuffer = SendBufferPT;
  SendBufferPT = SendBufferPT + OldLen;
  strcpy(SendBufferPT, LineCRLF);

}

/* CONN_QUIT FUNCTION - JONAS (09.06.2001) */

void conn_quit(struct Conn_Struct *ConnS, const char *const MessagePT, ...) {

  char Message[IRCQUITMSGLEN+1] = "";
  va_list Args = { 0 };

  assert(ConnS != NULL);
  assert(Conn_IsWelcome(ConnS));
  assert(MessagePT != NULL);

  va_start(Args, MessagePT);
  vsnprintf(Message, IRCQUITMSGLEN+1, MessagePT, Args);
  va_end(Args);

  if (!Conn_IsSentQuit(ConnS)) {
    Conn_SetSentQuit(ConnS);
    ConnS->SentQuitTime = time(NULL);
    conn_initsendq(ConnS);
    conn_initflushl(ConnS);
    conn_initflushb(ConnS);
    conn_addsendq(ConnS, "QUIT :%s", Message);
    client_noticealluser(ConnS->User, "Connection %s: Quitting %s(%s):%ld \"%s\": %s", ConnS->Name, ConnS->ServerHostName, ConnS->ServerHostIPS, ConnS->ServerPortH, ConnS->ServerName, Message);
  }

}

/* CONN_CLOSEALL FUNCTION - JONAS (09.06.2001) */

unsigned short int conn_closeall(const char *const MessagePT, ...) {

  char Message[LINELEN+1] = "";
  va_list Args = { 0 };
  unsigned short int Count = 0;
  struct Conn_Struct *ConnS = NULL;

  assert(MessagePT != NULL);

  va_start(Args, MessagePT);
  vsnprintf(Message, LINELEN+1, MessagePT, Args);
  va_end(Args);

  for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) {
    if (!Conn_IsRemove(ConnS)) {
      if ((Host_IsResolving(ConnS->ResolveFlags)) || (Host_IsResolving(ConnS->ServerResolveFlags))) {
#if HAVE_ARES_CANCELQUERY
        ares_cancelquery(Ares_Channel, ConnS);
#endif
      }
      if (Conn_IsSocket(ConnS)) {
        if (Conn_IsWelcome(ConnS)) {
          if (!Conn_IsSentQuit(ConnS)) { conn_quit(ConnS, "%s", Message); }
        }
        else {
          conn_disconnect(ConnS, "Connection %s: %s", ConnS->Name, Message);
        }
      }
    }
    Conn_SetRemove(ConnS);
    ++Count;
  }

  return(Count);

}


syntax highlighted by Code2HTML, v. 0.9.1