/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Client 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 CLIENT_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 0			/* 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 0		/* ioctl(), etc */
#define NEED_SYS_FILIO_H 0		/* 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 "listen.h"

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

#include "conn.h"
#include "conn_sendq.h"

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

/* VARIABLES - JONAS (31.07.2001) */

#if ARES
  extern ares_channel Ares_Channel;
#endif /* ARES */

extern struct Conf_Struct ConfS;

extern struct Client_Struct *Client_Head;
extern struct Client_Struct *Client_Tail;
extern unsigned long int G_Clients;

/* CLIENT_RESOLVE FUNCTION - JONAS (01.07.2000) */

void client_resolve(struct Client_Struct *ClientS) {

#if !ARES
    struct hostent *HostEnt = NULL;
#endif /* !ARES */

  assert(ClientS != NULL);

  if (!Host_IsHostIPToName(ClientS->ResolveFlags)) {

    DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Attempting to resolve client IP-address %s to hostname...", ClientS->HostIPS);
    Host_SetResolving(ClientS->ResolveFlags);
#if ARES
#if IPV6_SUPPORT
    if (Client_IsIPv6(ClientS)) {
      ares_gethostbyaddr(Ares_Channel, &ClientS->INAddr6, sizeof(ClientS->INAddr6), AF_INET6, (ares_host_callback) client_hostiptoname, ClientS);
    }
    else {
#endif /* IPV6_SUPPORT */
      ares_gethostbyaddr(Ares_Channel, &ClientS->INAddr, sizeof(ClientS->INAddr), AF_INET, (ares_host_callback) client_hostiptoname, ClientS);
#if IPV6_SUPPORT
    }
#endif /* IPV6_SUPPORT */
    return;
#else /* ARES */
#if IPV6_SUPPORT
    if (Client_IsIPv6(ClientS)) { HostEnt = gethostbyaddr(&ClientS->INAddr6, sizeof(ClientS->INAddr6), AF_INET6); }
    else  {
#endif
      HostEnt = gethostbyaddr(&ClientS->INAddr, sizeof(ClientS->INAddr), AF_INET);
#if IPV6_SUPPORT
    }
#endif
    client_hostiptoname(ClientS, errno, HostEnt);
#endif /* ARES */
  }

  if (!Host_IsHostNameToIP(ClientS->ResolveFlags)) {

    DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Attempting to resolve client hostname %s (PTR on IP-Address %s) to IP-address...", ClientS->HostName, ClientS->HostIPS);
    Host_SetResolving(ClientS->ResolveFlags);
    assert(ClientS->HostName != NULL);
#if ARES
#if IPV6_SUPPORT
    if (Client_IsIPv6(ClientS)) {
      ares_gethostbyname(Ares_Channel, ClientS->HostName, AF_INET6, (ares_host_callback) client_hostnametoip, ClientS);
    }
    else {
#endif /* IPV6_SUPPORT */
      ares_gethostbyname(Ares_Channel, ClientS->HostName, AF_INET, (ares_host_callback) client_hostnametoip, ClientS);
#if IPV6_SUPPORT
    }
#endif /* IPV6_SUPPORT */
    return;
#else /* ARES */
    HostEnt = gethostbyname(ClientS->HostName);
    client_hostnametoip(ClientS, errno, HostEnt);
#endif /* ARES */
  }

  Host_SetResolved(ClientS->ResolveFlags);

  sysprint(BITMASK_MAIN, "New client from %s resolved to %s.", ClientS->HostIPS, ClientS->HostName);
  ClientS->Time = NOW;

  if (G_Clients > ConfS.MaxClients) { client_close(ClientS, "The maximum number of clients has been reached."); return; }
  if (client_accesscheck(ClientS) == FALSE) { return; }

  client_notice(ClientS, "%s v%s", SHORTNAME, VERSION);
  client_notice(ClientS, "%s", COPYRIGHT);

}

/* CLIENT_HOSTIPTONAME FUNCTION - JONAS (01.03.2000) */

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

  struct Client_Struct *ClientS = ArgPT;

  assert(ClientS != NULL);

  Host_ClearResolving(ClientS->ResolveFlags);
  if (!Client_IsSocket(ClientS)) { return; } /* The user has already closed the connection */
  Host_SetHostIPToName(ClientS->ResolveFlags);

  if ((HostEnt == NULL) || (HostEnt->h_name == NULL) || (strcmp(HostEnt->h_name, "") == FALSE)) {
    DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Unable to resolve client IP-address %s to hostname: [%d] %s", ClientS->HostIPS, ErrNo, res_strerror(ErrNo));
    ClientS->HostName = strrealloc(ClientS->HostName, HOST_UNRESOLVED);
    Host_SetHostNameToIP(ClientS->ResolveFlags);
  }
  else {
    ClientS->HostName = strrealloc(ClientS->HostName, HostEnt->h_name);
    if (aerrno != AESUCCESS) {
      client_close(ClientS, "Memory allocation failure: [%d] %s", errno, strerror(errno));
      return;
    }
    Host_SetHostIPToName(ClientS->ResolveFlags);
    DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Resolved client IP-address %s to hostname %s.", ClientS->HostIPS, ClientS->HostName);
  }

#if ARES
  client_resolve(ClientS);
#endif /* ARES */

}

/* CLIENT_HOSTNAMETOIP FUNCTION - JONAS (01.03.2000) */

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

  struct Client_Struct *ClientS = ArgPT;
  const char *HostIPPT = NULL;
  struct in_addr INAddr;
#if IPV6_SUPPORT
  struct in6_addr INAddr6;
  char Host[INET6_ADDRSTRLEN+1] = "";
#endif

  assert(ClientS != NULL);

  Host_ClearResolving(ClientS->ResolveFlags);
  if (!Client_IsSocket(ClientS)) { return; } /* The user has already closed the connection */
  Host_SetHostNameToIP(ClientS->ResolveFlags);

  if ((HostEnt == NULL) || (HostEnt->h_length < 1)) {
    DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Unable to resolve client hostname %s (PTR on IP-Address %s) to IP-address: [%d] %s", ClientS->HostName, ClientS->HostIPS, ErrNo, res_strerror(ErrNo));
    ClientS->HostName = strrealloc(ClientS->HostName, HOST_UNRESOLVED);
  }
  else {
#if IPV6_SUPPORT
    if (Client_IsIPv6(ClientS)) {
      memset(&INAddr6, 0, sizeof(INAddr6));
      memcpy(&INAddr6, HostEnt->h_addr, HostEnt->h_length);
      HostIPPT = inet_ntop(AF_INET6, &INAddr6, Host, INET6_ADDRSTRLEN);
      if (HostIPPT == NULL) {
        client_close(ClientS, "inet_ntop() failed: [%d] %s", errno, strerror(errno));
        return;
      }
    }
    else {
      memset(&INAddr, 0, sizeof(INAddr));
      memcpy(&INAddr, HostEnt->h_addr, HostEnt->h_length);
      HostIPPT = inet_ntop(AF_INET, &INAddr, Host, INET6_ADDRSTRLEN);
      if (HostIPPT == NULL) {
        client_close(ClientS, "inet_ntop() failed: [%d] %s", errno, strerror(errno));
        return;
      }
    }
#else
    memset(&INAddr, 0, sizeof(INAddr));
    memcpy(&INAddr, HostEnt->h_addr, HostEnt->h_length);
    HostIPPT = inet_ntoa(INAddr);
    if (HostIPPT == NULL) {
      client_close(ClientS, "inet_ntoa() failed: [%d] %s", errno, strerror(errno));
      return;
    }
#endif
    if (strcmp(ClientS->HostIPS, HostIPPT) == FALSE) {
      DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Resolved client hostname %s (PTR on IP-Address %s) to correct IP-address %s.", ClientS->HostName, ClientS->HostIPS, ClientS->HostIPS);
    }
    else {
      DEBUGPRINT(BITMASK_DEBUG_CLIENT, "Resolved client hostname %s (PTR on IP-Address %s) to a different IP-address %s.", ClientS->HostName, ClientS->HostIPS, HostIPPT);
      ClientS->HostName = strrealloc(ClientS->HostName, HOST_UNRESOLVED);
    }
  }

#if ARES
  client_resolve(ClientS);
#endif  /* ARES */

}

/* CLIENT_ADDSEND FUNCTION - JONAS (01.07.2000) */

void client_addsend(struct Client_Struct *ClientS, const char *const LinePT, ...) {

  char Line[IRCMSGLEN+1] = "";
  char LineCRLF[IRCMSGCRLFLEN+1] = "";
  va_list Args = { 0 };
  unsigned long int Len = 0;
  unsigned long int OldLen = 0;
  unsigned long int NewLen = 0;
  char *SendBufferPT = NULL;

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

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

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

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

}

/* CLIENT_CLOSE FUNCTION - JONAS (09.06.2001) */

void client_close(struct Client_Struct *ClientS, const char *const LinePT, ...) {

  char Line[LINELEN+1] = "";
  va_list Args = { 0 };

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

  if (Client_IsSentError(ClientS)) { return; }

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

  sysprint(BITMASK_MAIN, "Closing connection to client %s (%s): %s", ClientS->HostName, ClientS->HostIPS, Line);
  if (Client_IsVerified(ClientS)) { client_noticeallbutone(ClientS, "Closing connection to client %s (%s): %s", ClientS->HostName, ClientS->HostIPS, Line); }

  Client_SetSentError(ClientS);
  ClientS->SentErrorTime = time(NULL);
  client_addsend(ClientS, "ERROR :%s", Line);

}

/* CLIENT_CLEANUP FUNCTION - JONAS (01.07.2000) */

void client_cleanup(struct Client_Struct *ClientS, const char *const LinePT, ...) {

  char Line[LINELEN+1] = "";
  va_list Args = { 0 };

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

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

  sysprint(BITMASK_MAIN, "%s", Line);
  if (Client_IsVerified(ClientS)) { client_noticeallbutone(ClientS, "%s", Line); }

  if (Client_IsSocket(ClientS)) {
    close(ClientS->FD);
    ClientS->FD = FD_NONE;
  }

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

  ClientS->Flags = 0;

  FREE(ClientS->RecvBuffer);
  FREE(ClientS->SendBuffer);

  if (ClientS->Who_Head != NULL) {
    struct Who_Struct *WhoS = NULL;
    for (WhoS = ClientS->Who_Head; WhoS != NULL; WhoS = WhoS->NextForClient) {
      WhoS->ClientS = NULL;
    }
    ClientS->Who_Head = NULL;
  }

  if (ClientS->ConnS != NULL) { client_detach(ClientS); }

}

/* CLIENT_CLOSEALL FUNCTION - JONAS (09.06.2001) */

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

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

  assert(MessagePT != NULL);

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

  for (ClientS = Client_Head ; ClientS != NULL ; ClientS = ClientS->Next) {
    if (!Client_IsSentError(ClientS)) { client_close(ClientS, "%s", Message); }
    ++Count;
  }

  return(Count);

}


syntax highlighted by Code2HTML, v. 0.9.1