/*
 * ----------------------------------------------------------------
 * Night Light IRC Proxy - Ident Daemon - 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 (03.07.2007)
 *
 */

#define IDENT_IO_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 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 0			/* Network database functions */
#define NEED_ARPA_NAMESER_H 0		/* Nameserver definitions */
#define NEED_GETUSERPW_HEADERS 0 	/* Functions to retrive system passwords */
#define NEED_ARES 0			/* Functions needed for ares */
#define NEED_SSL 1			/* Needed for SSL support */

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

#include "ident_conn.h"

#include "conn.h"

/* VARIABLES - JONAS (03.07.2003) */

struct IdentConn_Struct *IdentConn_Head = NULL;
struct IdentConn_Struct *IdentConn_Tail = NULL;
unsigned long int G_IdentConns = 0;

extern char *Ident_Conf_DefaultUser;
extern struct Conn_Struct *Conn_Head;

/* IDENT_CONN_ADD FUNCTION - JONAS (03.07.2003) */

struct IdentConn_Struct *ident_conn_add(const char *const HostIPSPT, signed long int FD) {

  struct IdentConn_Struct *IdentConnS = NULL;
  struct IdentConn_Struct *IdentConn_NEW = NULL;

  assert(HostIPSPT != NULL);

  IdentConn_NEW = malloc(sizeof(struct IdentConn_Struct));
  if (IdentConn_NEW == NULL) {
    aerrno = AEMALLOC;
    return(NULL);
  }
  memset(IdentConn_NEW, 0, sizeof(struct IdentConn_Struct));

  ident_conn_initconnection(IdentConn_NEW);

  IdentConn_NEW->HostIPS = strdup(HostIPSPT);
  if (IdentConn_NEW->HostIPS == NULL) {
    free(IdentConn_NEW->HostIPS);
    free(IdentConn_NEW);
    aerrno = AEMALLOC;
    return(NULL);
  }
  IdentConn_NEW->FD = FD;
  IdentConn_NEW->Time = NOW;
  IdentConn_SetSocket(IdentConn_NEW);

  if (IdentConn_Head == NULL) {
    IdentConn_Head = IdentConn_NEW;
    IdentConn_Tail = IdentConn_NEW;
  }
  else {
    IdentConnS = IdentConn_Tail;
    IdentConnS->Next = IdentConn_NEW;
    IdentConn_NEW->Prev = IdentConnS;
    IdentConn_Tail = IdentConn_NEW;
  }

  G_IdentConns++;

  aerrno = AESUCCESS;
  return(IdentConn_NEW);

}

/* IDENT_CONN_REM FUNCTION - JONAS (03.07.2003) */

void ident_conn_rem(struct IdentConn_Struct *IdentConnS) {

  assert(IdentConnS != NULL);

  if (IdentConnS->Prev == NULL) { IdentConn_Head = IdentConnS->Next; }
  else { IdentConnS->Prev->Next = IdentConnS->Next; }

  if (IdentConnS->Next == NULL) { IdentConn_Tail = IdentConnS->Prev; }
  else { IdentConnS->Next->Prev = IdentConnS->Prev; }

  FREE(IdentConnS->RecvBuffer);
  FREE(IdentConnS->SendBuffer);
  free(IdentConnS->HostIPS);
  free(IdentConnS);

  G_IdentConns--;

}

/* IDENT_CONN_INITCONNECTION FUNCTION - JONAS (03.07.2003) */

void ident_conn_initconnection(struct IdentConn_Struct *IdentConnS) {

  assert(IdentConnS != NULL);

  IdentConnS->Flags = 0;
  IdentConnS->FD = FD_NONE;

  memset(&IdentConnS->INAddr, 0, sizeof(IdentConnS->INAddr));
#if IPV6_SUPPORT
  memset(&IdentConnS->INAddr6, 0, sizeof(IdentConnS->INAddr6));
#endif

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

}

/* IDENT_CONN_DISCONNECT FUNCTION - JONAS (03.07.2003) */

void ident_conn_disconnect(struct IdentConn_Struct *IdentConnS) {

  assert(IdentConnS != NULL);

  if (IdentConn_IsSocket(IdentConnS)) {
    close(IdentConnS->FD);
    IdentConnS->FD = FD_NONE;
  }

  ident_conn_initconnection(IdentConnS);

}

/* IDENT_CONN_FDS FUNCTION - JONAS (03.07.2003) */

void ident_conn_fds(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) {

  struct IdentConn_Struct *IdentConnS = NULL;
  struct IdentConn_Struct *IdentConnS_DEL = NULL;
  time_t Duration = 0;

  for (IdentConnS = IdentConn_Head ; IdentConnS != NULL ;) {

    Duration = (NOW - IdentConnS->Time);
    if (Duration >= IDENTCONN_TIMEOUT) { ident_conn_disconnect(IdentConnS); }

    if (IdentConn_IsSocket(IdentConnS)) {
      FD_SET(IdentConnS->FD, ReadFDS);
      if (IdentConnS->SendBuffer != NULL) { FD_SET(IdentConnS->FD, WriteFDS); }
      IdentConnS = IdentConnS->Next;
      continue;
    }
    else {
      IdentConnS_DEL = IdentConnS;
      IdentConnS = IdentConnS->Next;
      ident_conn_rem(IdentConnS_DEL);
      continue;
    }

  }

}

/* IDENT_CONN_IO FUNCTION - JONAS (03.07.2003) */

void ident_conn_io(fd_set *ReadFDS, fd_set *WriteFDS, unsigned long int *FDS) {

  struct IdentConn_Struct *IdentConnS = NULL;

  assert(ReadFDS != NULL);
  assert(WriteFDS != NULL);

  for (IdentConnS = IdentConn_Head ; IdentConnS != NULL ; IdentConnS = IdentConnS->Next) {

    assert(*FDS >= 0);
    if (*FDS <= 0) { return; }

    if (IdentConn_IsSocket(IdentConnS)) {
      if (FD_ISSET(IdentConnS->FD, ReadFDS)) { *FDS = *FDS - 1; }
      if (FD_ISSET(IdentConnS->FD, WriteFDS)) { *FDS = *FDS - 1; }
    }

    if (IdentConn_IsSocket(IdentConnS)) {
      if (FD_ISSET(IdentConnS->FD, ReadFDS)) { ident_conn_recv(IdentConnS); }
    }
    if (IdentConn_IsSocket(IdentConnS)) {
      if (FD_ISSET(IdentConnS->FD, WriteFDS)) { ident_conn_send(IdentConnS); }
    }

  }

}

/* IDENT_CONN_RECV FUNCTION - JONAS (03.07.2003) */

void ident_conn_recv(struct IdentConn_Struct *IdentConnS) {

  signed long int Result = 0;
  unsigned long int OldLen = 0;
  unsigned long int NewLen = 0;
  char RecvBuffer[RECVBUFFERLEN+1] = "";
  char *RecvBufferPT = NULL;

  assert(IdentConnS != NULL);
  assert(IdentConn_IsSocket(IdentConnS));
  assert(IdentConnS->FD != FD_NONE);

  do {
    memset(&RecvBuffer, 0, sizeof(RecvBuffer));
    Result = recv(IdentConnS->FD, RecvBuffer, RECVBUFFERLEN, MSG_NOSIGNAL);
    if (Result <= ERROR) {
      if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)) { break; }
      ident_conn_disconnect(IdentConnS);
      return;
    }
    if (Result == 0) {
      if (IdentConnS->RecvBuffer != NULL) { ident_conn_parse(IdentConnS); }
      ident_conn_disconnect(IdentConnS);
      return;
    }

    if (IdentConnS->RecvBuffer == NULL) { OldLen = 0; }
    else { OldLen = strlen(IdentConnS->RecvBuffer); }
    NewLen = OldLen + Result + 1;
    RecvBufferPT = realloc(IdentConnS->RecvBuffer, NewLen);
    if (RecvBufferPT == NULL) {
      ident_conn_disconnect(IdentConnS);
      return;
    }
    IdentConnS->RecvBuffer = RecvBufferPT;
    RecvBufferPT += OldLen;
    strcpy(RecvBufferPT, RecvBuffer);
  }
  while (Result >= RECVBUFFERLEN);

  ident_conn_parse(IdentConnS);

}

/* IDENT_CONN_SEND FUNCTION - JONAS (03.07.2003) */

void ident_conn_send(struct IdentConn_Struct *IdentConnS) {

  char *SendBufferPT = NULL;
  char SendBuffer[SENDBUFFERLEN+1] = "";
  unsigned long int SendLen = 0;
  unsigned long int SentLen = 0;
  signed long int Result = 0;

  assert(IdentConnS != NULL);
  assert(IdentConn_IsSocket(IdentConnS));
  assert(IdentConnS->FD != FD_NONE);
  assert(IdentConnS->SendBuffer != NULL);

  for (SendBufferPT = IdentConnS->SendBuffer ; *SendBufferPT != '\0' ; SendBufferPT += SentLen) {
    SendLen = strlen(SendBufferPT);
    if (SendLen > SENDBUFFERLEN) { SendLen = SENDBUFFERLEN; }
    memset(&SendBuffer, 0, sizeof(SendBuffer));
    strncpy(SendBuffer, SendBufferPT, SendLen);
    Result = send(IdentConnS->FD, SendBuffer, SendLen, MSG_NOSIGNAL);
    if (Result <= ERROR) {
      if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR) || (errno == ENOMEM) || (errno == ENOBUFS)) {
        /*
         * EAGAIN/EWOULDBLOCK -- THE SOCKET IMPLEMENTATION CAN'T HANDLE MORE DATA.
         * EINTR - INTERRUPTED BY A SIGNAL.
         * ENOMEM - NO MEMORY LEFT.
         * ENOBUFS - NO BUFFER SPACE AVAILABLE.
         *
         * COPY WHATS LEFT TO THE THE START OF THE SENDBUFFER, REALLOCATE THE MEMORY AND BAIL OUT - JONAS (01.12.1999)
         *
         */

        unsigned long int Len = 0;

        Len = strlen(SendBufferPT) + 1;
        memmove(IdentConnS->SendBuffer, SendBufferPT, Len);
        SendBufferPT = realloc(IdentConnS->SendBuffer, Len);
        assert(SendBufferPT != NULL);
        IdentConnS->SendBuffer = SendBufferPT;
        return;
      }
      ident_conn_disconnect(IdentConnS);
      return;
    }
    SentLen = Result;
    assert(SentLen == SendLen);    
  }

  FREE(IdentConnS->SendBuffer);

  ident_conn_disconnect(IdentConnS);

}

/* IDENT_CONN_ADDSEND FUNCTION - JONAS (03.07.2003) */

void ident_conn_addsend(struct IdentConn_Struct *IdentConnS, const char *const LinePT, ...) {

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

  assert(IdentConnS != NULL);
  assert(LinePT != NULL);
  assert(IdentConn_IsSocket(IdentConnS));

  va_start(Args, LinePT);
  vsnprintf(Line, LINELEN+1, LinePT, Args);
  va_end(Args);
  snprintf(LineCRLF, LINELEN+1, "%s\r\n", Line);

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

}

/* IDENT_CONN_PARSE FUNCTION - JONAS (03.07.2003) */

void ident_conn_parse(struct IdentConn_Struct *IdentConnS) {

  char *BufferPT = NULL;
  char *LinePT = NULL;
  char *DumbPT = NULL;
  unsigned long int Count = 0;
  unsigned long int Len = 0;
  char *TempPT = NULL;

  assert(IdentConnS != NULL);

  BufferPT = IdentConnS->RecvBuffer;

  for (Count = 1 ; BufferPT != NULL ; Count++) {
    DumbPT = strchr(BufferPT, '\n');
    if (DumbPT == NULL) {
      if (Count == 1) { return; }
      if (BufferPT != NULL) {
        Len = strlen(BufferPT) + 1;
        memmove(IdentConnS->RecvBuffer, BufferPT, Len);
        TempPT = realloc(IdentConnS->RecvBuffer, Len);
        if (TempPT == NULL) {
          ident_conn_disconnect(IdentConnS);
          return;
        }
        IdentConnS->RecvBuffer = TempPT;
        return;
      }
      FREE(IdentConnS->RecvBuffer);
      return;
    }
    LinePT = strtok(BufferPT, "\n");
    BufferPT = strtok(NULL, "\0");
    if (LinePT == NULL) { continue; }
    Len = strlen(LinePT);
    if (LinePT[Len-1] == '\r') { LinePT[Len-1] = '\0'; }
    ident_conn_parse_message(IdentConnS, LinePT);
    if (IdentConnS->RecvBuffer == NULL) { return; }
  }
  FREE(IdentConnS->RecvBuffer);

}

/* IDENT_CONN_PARSE_MESSAGE FUNCTION - JONAS (03.07.2003) */

void ident_conn_parse_message(struct IdentConn_Struct *IdentConnS, char *MessagePT) {

  char *Port1PT = NULL;
  char *Port2PT = NULL;
  char *TempPT = NULL;

  unsigned long int Port1 = 0;
  unsigned long int Port2 = 0;

  struct Conn_Struct *ConnS = NULL;

  assert(IdentConnS != NULL);
  assert(MessagePT != NULL);

  DEBUGPRINT(BITMASK_DEBUG_IDENT, "Ident query from %s: %s", IdentConnS->HostIPS, MessagePT);

  while (MessagePT[0] == ' ') { ++MessagePT; }
  if (MessagePT[0] == '\0') {
    ident_conn_addsend(IdentConnS, "0 , 0 : ERROR : X-INVALID-REQUEST");
    return;
  }

  Port1PT = MessagePT;
  StrMovePastToken(MessagePT, ',');
  if (MessagePT == NULL) {
    ident_conn_addsend(IdentConnS, "0 , 0 : ERROR : X-INVALID-REQUEST");
    return;
  }
  while (MessagePT[0] == ' ') { ++MessagePT; }
  Port2PT = MessagePT;

  while((TempPT = strchr(Port1PT, ' ')) != NULL) { *TempPT = '\0'; }
  while((TempPT = strchr(Port2PT, ' ')) != NULL) { *TempPT = '\0'; }

  Port1 = strtoul(Port1PT, NULL, 0);
  Port2 = strtoul(Port2PT, NULL, 0);

  for (ConnS = Conn_Head ; ConnS != NULL ; ConnS = ConnS->Next) {
    if ((ConnS->PortH == Port1) && (ConnS->ServerPortH == Port2)) {
      ident_conn_addsend(IdentConnS, "%ld , %ld : USERID : UNIX : %s", Port1, Port2, ConnS->User);
      return;
    }
  }

  if ((Ident_Conf_DefaultUser == NULL) || (strcmp(Ident_Conf_DefaultUser, "NO-USER") == FALSE)) {
    ident_conn_addsend(IdentConnS, "%ld , %ld : ERROR : NO-USER", Port1, Port2);
    return;
  }
  else {
    ident_conn_addsend(IdentConnS, "%ld , %ld : USERID : UNIX : %s", Port1, Port2, Ident_Conf_DefaultUser);
    return;
  }

}

/* IDENT_CONN_CLOSEALL FUNCTION - JONAS (03.07.2003) */

unsigned short int ident_conn_closeall(void) {

  struct IdentConn_Struct *IdentConnS = NULL;
  struct IdentConn_Struct *IdentConnS_DEL = NULL;

  for (IdentConnS = IdentConn_Head ; IdentConnS != NULL ;) {
    if (IdentConn_IsSocket(IdentConnS)) { ident_conn_disconnect(IdentConnS); }
    IdentConnS_DEL = IdentConnS;
    IdentConnS = IdentConnS->Next;
    free(IdentConnS_DEL);
  }

  return(0);

}



syntax highlighted by Code2HTML, v. 0.9.1