/* 
  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_gethostbyaddr.c
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \todo documentation
*
* \brief wrapper for gethostbyaddr()
*
* get structure of type hostent for the given host address
* man 2 gethhostbyaddr
*
*
* \addtogroup libcontra
* The group of the library.
* @{
************************************************************************/

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

extern bool	libcontra_logGethostbyaddr; /*!< this is needed to control the output in the logging function */
extern bool	libcontra_isInitialized; /*!< this is needed to control the output in the logging function */

/*!
*************************************************************************
*
* \fn struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type)
*
* \author Thorsten Alteholz <libcontra@alteholz.eu>
*
* \brief wrapper around libc::gethostbyaddr()
* 
* @param[in]  *addr  pointer to address structure
* @param[in]  len    length of address structure
* @param[in]  type   address type, either AF_INET or AF_INET6
*
* \return On success, return a hostent structure
*         On error, return NIL, errno is set
*
************************************************************************/
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type)
{
  struct hostent * (*libc_gethostbyaddr)(const void *addr, socklen_t len, int type);
  struct hostent *rcstruct;
  char addrStr[INET6_ADDRSTRLEN];
  
  if (libcontra_isInitialized==false) {
    libcontra_init();
  } 

  if (libcontra_logGethostbyaddr) {
    
    libcontra_log(LOGLEVEL_DEBUG, "D: gethostbyaddr start");
    libcontra_log(LOGLEVEL_INFO, "I: gethostbyaddr type: %i (%i/%i) len: %i", type, AF_INET, AF_INET6, len);
    switch (type) {
      case AF_INET:
        inet_ntop(AF_INET, (struct in_addr *)addr, addrStr, INET6_ADDRSTRLEN);
        break;
      case AF_INET6:
        inet_ntop(AF_INET6, (struct in6_addr *)addr, addrStr, INET6_ADDRSTRLEN);
        break;
      default:
        libcontra_log(LOGLEVEL_ERROR, "E: gethostbyaddr: unknown type: %i\n", type);
        break;
    }
    libcontra_log(LOGLEVEL_INFO, "I: gethostbyaddr looking for: %s", addrStr);
  }

  *(void **)(&libc_gethostbyaddr) = dlsym(RTLD_NEXT, "gethostbyaddr");
  if(dlerror()) {
    errno = EACCES;
    return NULL;
  }

  rcstruct = (*libc_gethostbyaddr)(addr, len, type);
  
  if (libcontra_logGethostbyaddr) {
    char **aliases;
    struct in_addr **list;
    struct in6_addr **list6;
    int i;
    
    libcontra_log(LOGLEVEL_INFO, "I: gethostbyadd h_name: %s", rcstruct->h_name);
    aliases=rcstruct->h_aliases;
    list=(struct in_addr **)rcstruct->h_addr_list;
    list6=(struct in6_addr **)rcstruct->h_addr_list;
    if (*aliases==NULL) libcontra_log(LOGLEVEL_INFO, "I: gethostbyaddr alias list empty");
    while (*aliases!=NULL) {
      libcontra_log(LOGLEVEL_INFO, "I: gethostbyaddr alias of %s is %s", rcstruct->h_name, *aliases);
      aliases++;
    }
    libcontra_log(LOGLEVEL_INFO, "I: gethostbyaddr h_addrtype: %i (%i/%i) h_length: %i", rcstruct->h_addrtype, AF_INET, AF_INET6, rcstruct->h_length);
    for(i = 0; list[i] != NULL; i++) {      
      switch (type) {
        case AF_INET:
          inet_ntop(AF_INET, (const void *)list[i], addrStr, INET6_ADDRSTRLEN);
          break;
        case AF_INET6:
          inet_ntop(AF_INET6, (const void *)list6[i] , addrStr, INET6_ADDRSTRLEN);
          break;
        default:
          libcontra_log(LOGLEVEL_ERROR, "E: gethostbyaddr: unknown type: %i\n", type);
          break;
      }
      libcontra_log(LOGLEVEL_INFO, "I: gethostbyaddr h_addr_list[%i] = v(%s) ", i, addrStr);
    //inet_ntop(AF_INET, &(((struct sockaddr_in *)si)->sin_addr), addrStr, INET6_ADDRSTRLEN)  
    }
    libcontra_log(LOGLEVEL_DEBUG, "D: gethostbyaddr end");
  }
  
  return rcstruct;
}

/*! @} */

