/* 
  Copyright 2018-2023 Thorsten Alteholz <libcontra@alteholz.eu>

  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; version 2 of the License

  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 (see the file COPYING included with this
  distribution); if not, write to the Free Software Foundation, Inc.,
  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*!
*************************************************************************
*
* \file libcontra.c
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \todo documentation
*
* \brief library for connection tracking
*
* for more info see documents in docu directory
*
*
* \addtogroup libcontra
* The group of the library.
* @{
************************************************************************/

#include <libcontra.h>
#include <libcontra_log.h>
#include <netdb.h>

pthread_mutex_t libcontra_lock;  /*!< this is needed to control the output in the logging function */
bool	libcontra_isInitialized=false;  /*!< this is needed to control the output in the logging function */
bool	libcontra_logConnect=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logSocket=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logClose=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logListen=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logPoll=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logBind=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logAccept=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logSend=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logRecv=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logSetsockopt=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logGetsockopt=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logGethostbyname=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logGethostbyaddr=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logFreeaddrinfo=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logGetaddrinfo=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logInit=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_logStdio=false; /*!< this is needed to control the output in the logging function */
bool	libcontra_configResolve=false; /*!< this is needed to control the output in the logging function */
int	libcontra_loglevel=0; /*!< this is needed to control the output in the logging function */
char*	libcontra_filename=NULL;  /*!< this is needed to control the output in the logging function */
FILE*	libcontra_file=NULL; /*!< this is needed to control the output in the logging function */


/*!
*************************************************************************
*
* \fn void libcontra_init()
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \todo documentation
*
* \bug up to now, there is no know bug
*
* \brief initialize libcontra
*
* This function will be called whenever libcontra is used for the
* first time within a process.
* ENV variables will be evaluated.
*
************************************************************************/
void libcontra_init()
{
  char *env;

  if (pthread_mutex_init(&libcontra_lock, NULL) != 0) {
    printf("\n mutex init failed\n");
    return;
  }

  /* check environment variables */
  env=getenv("CONTRA_CONFIG_RESOLVE");
  if (env!=NULL) {
    if (strstr(env,"yes")!=NULL) libcontra_configResolve=true;
    if (strstr(env,"true")!=NULL) libcontra_configResolve=true;
  }

  env=getenv("CONTRA_LOG_STDIO");
  if (env!=NULL) {
    if (strstr(env,"yes")!=NULL) libcontra_logStdio=true;
    if (strstr(env,"true")!=NULL) libcontra_logStdio=true;
  }

  env=getenv("CONTRA_LOG_LEVEL");
  if (env!=NULL) {
    libcontra_loglevel=atoi(env);
    if (libcontra_loglevel>LOGLEVEL_MAX) libcontra_loglevel=LOGLEVEL_MAX;
  }

  libcontra_filename=getenv("CONTRA_LOG_FILE");
  if (libcontra_filename != NULL) {
    libcontra_file=fopen(libcontra_filename,"a");
  }

  env=getenv("CONTRA_LOG_FUNCTIONS");
  if (env!=NULL) {
    if ((strstr(env,"init")!=NULL)          || (strstr(env,"all")!=NULL)) libcontra_logInit=true;
    if ((strstr(env,"connect")!=NULL)       || (strstr(env,"all")!=NULL)) libcontra_logConnect=true;
    if ((strstr(env,"socket")!=NULL)        || (strstr(env,"all")!=NULL)) libcontra_logSocket=true;
    if ((strstr(env,"close")!=NULL)         || (strstr(env,"all")!=NULL)) libcontra_logClose=true;
    if ((strstr(env,"listen")!=NULL)        || (strstr(env,"all")!=NULL)) libcontra_logListen=true;
    if ((strstr(env,"poll")!=NULL)          || (strstr(env,"all")!=NULL)) libcontra_logPoll=true;
    if ((strstr(env,"bind")!=NULL)          || (strstr(env,"all")!=NULL)) libcontra_logBind=true;
    if ((strstr(env,"accept")!=NULL)        || (strstr(env,"all")!=NULL)) libcontra_logAccept=true;
    if ((strstr(env,"send")!=NULL)          || (strstr(env,"all")!=NULL)) libcontra_logSend=true;
    if ((strstr(env,"recv")!=NULL)          || (strstr(env,"all")!=NULL)) libcontra_logRecv=true;
    if ((strstr(env,"setsockopt")!=NULL)    || (strstr(env,"all")!=NULL)) libcontra_logSetsockopt=true;
    if ((strstr(env,"getsockopt")!=NULL)    || (strstr(env,"all")!=NULL)) libcontra_logGetsockopt=true;
    if ((strstr(env,"gethostbyname")!=NULL) || (strstr(env,"all")!=NULL)) libcontra_logGethostbyname=true;
    if ((strstr(env,"gethostbyaddr")!=NULL) || (strstr(env,"all")!=NULL)) libcontra_logGethostbyaddr=true;
    if ((strstr(env,"freeaddrinfo")!=NULL)  || (strstr(env,"all")!=NULL)) libcontra_logFreeaddrinfo=true;
    if ((strstr(env,"getaddrinfo")!=NULL)   || (strstr(env,"all")!=NULL)) libcontra_logGetaddrinfo=true;
  }

  /* */
  if (libcontra_logInit==true) {
    if (libcontra_logInit)		libcontra_log(LOGLEVEL_INFO, "D: log infos in init()");
					libcontra_log(LOGLEVEL_INFO, "D: log level %i",libcontra_loglevel);
    if (libcontra_logStdio)		libcontra_log(LOGLEVEL_INFO, "D: log to stdio");
    if (libcontra_file!=NULL)		libcontra_log(LOGLEVEL_INFO, "D: log to FILE");
    if (libcontra_logConnect)		libcontra_log(LOGLEVEL_INFO, "D: log infos in connect()");
    if (libcontra_configResolve)	libcontra_log(LOGLEVEL_INFO, "D: resolve IP addresses");
  }
  /* */
  libcontra_isInitialized=true;
}

/*!
*************************************************************************
*
* \fn char * libcontra_af2string(int family)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \todo documentation
*
* \bug up to now, there is no know bug
*
* \brief mapping between AF_* and string
*
* This funtion will be called to translate a numerical value to
* some kind of human readable string
*
************************************************************************/
char * libcontra_af2string(int family)
{
  switch (family) {
    case AF_UNSPEC:       return "0 Unspecified";
    case AF_LOCAL:        return "1 Local to host (pipes and file-domain)";
    case AF_INET:         return "2 IP protocol family";
    case AF_AX25:         return "3 Amateur Radio AX.25";
    case AF_IPX:          return "4 Novell Internet Protocol";
    case AF_APPLETALK:    return "5 Appletalk DDP";
    case AF_NETROM:       return "6 Amateur radio NetROM";
    case AF_BRIDGE:       return "7 Multiprotocol bridge";
    case AF_ATMPVC:       return "8 ATM PVCs";
    case AF_X25:          return "9 Reserved for X.25 project";
    case AF_INET6:        return "10 IP version 6";
    case AF_ROSE:         return "11 Amateur Radio X.25 PLP";
    case AF_DECnet:       return "12 Reserved for DECnet project";
    case AF_NETBEUI:      return "13 Reserved for 802.2LLC project";
    case AF_SECURITY:     return "14 Security callback pseudo AF";
    case AF_KEY:          return "15 PF_KEY key management API";
    case AF_NETLINK:      return "16 Alias to emulate 4.4BSD";
    case AF_PACKET:       return "17 Packet family";
    case AF_ASH:          return "18 Ash";
    case AF_ECONET:       return "19 Acorn Econet";
    case AF_ATMSVC:       return "20 ATM SVCs";
    case AF_RDS:          return "21 RDS sockets";
    case AF_SNA:          return "22 Linux SNA Project";
    case AF_IRDA:         return "23 IRDA sockets";
    case AF_PPPOX:        return "24 PPPoX sockets";
    case AF_WANPIPE:      return "25 Wanpipe API sockets";
    case AF_LLC:          return "26 Linux LLC";
    case AF_IB:           return "27 Native InfiniBand address";
    case AF_MPLS:         return "28 MPLS";
    case AF_CAN:          return "29 Controller Area Network";
    case AF_TIPC:         return "30 TIPC sockets";
    case AF_BLUETOOTH:    return "31 Bluetooth sockets";
    case AF_IUCV:         return "32 IUCV sockets";
    case AF_RXRPC:        return "33 RxRPC sockets";
    case AF_ISDN:         return "34 mISDN sockets";
    case AF_PHONET:       return "35 Phonet sockets";
    case AF_IEEE802154:   return "36 IEEE 802.15.4 sockets";
    case AF_CAIF:         return "37 CAIF sockets";
    case AF_ALG:          return "38 Algorithm sockets";
    case AF_NFC:          return "39 NFC sockets";
    case AF_VSOCK:        return "40 vSockets";
    case AF_KCM:          return "41 Kernel Connection Multiplexor";
    case AF_QIPCRTR:      return "42 Qualcomm IPC Router";
    case AF_SMC:          return "43 SMC sockets";
    case AF_XDP:          return "44 XDP sockets";
    default: return "default";
  }
  return "totally unknonw";
}

/*!
*************************************************************************
*
* \fn char * libcontra_sock2string(int type)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief mapping between SOCK_* and string
*
* This funtion will be called to translate a numerical value to
* some kind of human readable string
*
************************************************************************/
char * libcontra_sock2string(int type)
{
  bool isNonblock = type & SOCK_NONBLOCK;
  bool isCloexec = type & SOCK_CLOEXEC;
  
  type = (type & ~SOCK_NONBLOCK) & ~SOCK_CLOEXEC;
  
  switch (type) {
    case SOCK_STREAM:     return "1 Sequenced, reliable, connection-based byte streams";
    case SOCK_DGRAM:      return "2 Connectionless, unreliable datagrams of fixed maximum length";
    case SOCK_RAW:        return "3 Raw protocol interface";
    case SOCK_RDM:        return "4 Reliably-delivered messages";
    case SOCK_SEQPACKET:  return "5 Sequenced, reliable, connection-based, datagrams of fixed maximum length";
    case SOCK_DCCP:       return "6 Datagram Congestion Control Protocol";
    case SOCK_PACKET:     return "10 Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level";
    default: return "maybe unknown";
  }
  return "totally unknonw";
}

/*!
*************************************************************************
*
* \fn char * libcontra_oname2string(int optionName)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief mapping between SO_* and string
*
* This funtion will be called to translate a numerical value to
* some kind of human readable string
*
************************************************************************/
char * libcontra_oname2string(int optionName)
{
  switch (optionName) {
    case SO_ACCEPTCONN:   return "Socket is accepting connection";
    case SO_BROADCAST:   return "Transmission of broadcast messages is supported";
    case SO_DEBUG:   return "Debugging information is being recorded";
    case SO_DONTROUTE:   return "Bypass normal routing";
    case SO_ERROR:   return "Socket error status";
    case SO_KEEPALIVE:   return "Connections are kept alive with periodic messages";
    case SO_LINGER:   return "Socket lingers on close";
    case SO_OOBINLINE:   return "Out-of-band data is transmitted in line";
    case SO_RCVBUF:   return "Receive buffer size";
    case SO_RCVLOWAT:   return "Receive ``low water mark''";
    case SO_RCVTIMEO:   return "Receive timeout";
    case SO_REUSEADDR:   return "Reuse of local addresses is supported";
    case SO_SNDBUF:   return "Send buffer size";
    case SO_SNDLOWAT:   return "Send ``low water mark''";
    case SO_SNDTIMEO:   return "Send timeout";
    case SO_TYPE:   return "Socket type"; 
    default: "maybe unknown";
  }
  return "totally unknown";
}

/*!
*************************************************************************
*
* \fn char * libcontra_eai2string(int type)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief mapping between EAI_* and string
*
* This funtion will be called to translate a numerical value to
* some kind of human readable string
*
************************************************************************/
char * libcontra_eai2string(int type)
{
  switch (type) {
    case EAI_AGAIN:    return "1 Sequenced, reliable, connection-based byte streams";
    case EAI_BADFLAGS: return "2 Connectionless, unreliable datagrams of fixed maximum length";
    case EAI_FAIL:     return "3 Raw protocol interface";
    case EAI_FAMILY:   return "4 Reliably-delivered messages";
    case EAI_MEMORY:   return "5 Sequenced, reliable, connection-based, datagrams of fixed maximum length";
    case EAI_NONAME:   return "6 Datagram Congestion Control Protocol";
    case EAI_SERVICE:  return "10 Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level";
    case EAI_SOCKTYPE: return "10 Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level";
    case EAI_SYSTEM:   return "10 Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level";
    case EAI_OVERFLOW: return "10 Linux specific way of getting packets at the dev level. For writing rarp and other similar things on the user level";
    default: return "maybe unknown";
  }
  return "totally unknonw";
}

/*! @} */

