Logo Search packages:      
Sourcecode: tcpreen version File versions  Download package

getaddrinfo.c

/*
 * getaddrinfo.c - replacement functions for getaddrinfo (& co.)
 * This is not thread-safe!!!
 * $Id: getaddrinfo.c 175 2005-07-01 18:07:39Z rdenisc $
 */

/***********************************************************************
 *  Copyright (C) 2002-2004 Remi Denis-Courmont.                       *
 *  This program is free software; you can redistribute 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 Punlic License  *
 *  along with this program; if not, you can get it from:              *
 *  http://www.gnu.org/copyleft/gpl.html                               *
 ***********************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef WIN32
# ifdef HAVE_GETADDRINFO
#  undef getaddrinfo
#  define getaddrinfo stub_getaddrinfo
#  undef freeaddrinfo
#  define freeaddrinfo stub_freeaddrinfo
# endif
# ifdef HAVE_GETNAMEINFO
#  undef getnameinfo
#  define getnameinfo stub_getnameinfo
# endif
#endif

#include "getaddrinfo.h"

#include <stddef.h> /* size_t */
#include "secstr.h" /* strncpy(), strlen(), memcpy(), memset(), strchr() */
#include <stdlib.h> /* malloc(), free(), strtoul() */
#include <sys/types.h>
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <errno.h>

#ifndef HAVE_GAI_STRERROR
static struct
{
      int code;
      const char *msg;
} const __gai_errlist[] =
{
      { 0,              "Error 0" },
      { EAI_BADFLAGS,         "Invalid flag used" },
      { EAI_NONAME,           "Host or service not found" },
      { EAI_AGAIN,            "Temporary name service failure" },
      { EAI_FAIL,       "Non-recoverable name service failure" },
      { EAI_NODATA,           "No data for host name" },
      { EAI_FAMILY,           "Unsupported address family" },
      { EAI_SOCKTYPE,         "Unsupported socket type" },
      { EAI_SERVICE,          "Incompatible service for socket type" },
      { EAI_ADDRFAMILY, "Unavailable address family for host name" },
      { EAI_MEMORY,           "Memory allocation failure" },
      { EAI_SYSTEM,           "System error" },
      { 0,              NULL }
};

static const char *__gai_unknownerr = "Unrecognized error number";

/*
 * Converts an EAI_* error code into human readable english text.
 */
const char *
gai_strerror (int errnum)
{
      int i;

      for (i = 0; __gai_errlist[i].msg != NULL; i++)
            if (errnum == __gai_errlist[i].code)
                  return __gai_errlist[i].msg;

      return __gai_unknownerr;
}
# undef _EAI_POSITIVE_MAX
#endif /* ifndef HAVE_GAI_STRERROR */

#if !(defined (HAVE_GETNAMEINFO) && defined (HAVE_GETADDRINFO))
/*
 * Converts the current herrno error value into an EAI_* error code.
 * That error code is normally returned by getnameinfo() or getaddrinfo().
 */
static int
gai_error_from_herrno (void)
{
      switch(h_errno)
      {
            case HOST_NOT_FOUND:
                  return EAI_NONAME;

            case NO_ADDRESS:
# if (NO_ADDRESS != NO_DATA)
            case NO_DATA:
# endif
                  return EAI_NODATA;

            case NO_RECOVERY:
                  return EAI_FAIL;

            case TRY_AGAIN:
                  return EAI_AGAIN;
      }
      return EAI_SYSTEM;
}
#endif /* if !(HAVE_GETNAMEINFO && HAVE_GETADDRINFO) */

#ifndef HAVE_GETNAMEINFO
/*
 * getnameinfo() non-thread-safe IPv4-only implementation,
 * Address-family-independant address to hostname translation
 * (reverse DNS lookup in case of IPv4).
 *
 * This is meant for use on old IP-enabled systems that are not IPv6-aware,
 * and probably do not have getnameinfo(), but have the old gethostbyaddr()
 * function.
 *
 * GNU C library 2.0.x is known to lack this function, even though it defines
 * getaddrinfo().
 */
int
getnameinfo (const struct sockaddr *sa, int salen,
           char *host, int hostlen, char *serv, int servlen, int flags)
{
      if (((unsigned)salen < sizeof (struct sockaddr_in))
       || (sa->sa_family != AF_INET))
            return EAI_FAMILY;
      else if (flags & (~_NI_MASK))
            return EAI_BADFLAGS;
      else
      {
            const struct sockaddr_in *addr;

            addr = (const struct sockaddr_in *)sa;

            if (host != NULL)
            {
                  int solved = 0;

                  /* host name resolution */
                  if (!(flags & NI_NUMERICHOST))
                  {
                        struct hostent *hent;

                        hent = gethostbyaddr (
                                    (const void*)&addr->sin_addr,
                                    4, AF_INET);

                        if (hent != NULL)
                        {
                              secure_strncpy (host, hent->h_name,
                                          hostlen);

                              /*
                               * only keep first part of hostname
                               * if user don't want fully qualified
                               * domain name
                               */
                              if (flags & NI_NOFQDN)
                              {
                                    char *ptr;

                                    ptr = strchr (host, '.');
                                    if (ptr != NULL)
                                          *ptr = 0;
                              }

                              solved = 1;
                        }
                        else if (flags & NI_NAMEREQD)
                              return gai_error_from_herrno ();
                  }

                  if (!solved)
                        /* inet_ntoa() can't fail */
                        secure_strncpy (host,
                                    inet_ntoa (addr->sin_addr),
                                    hostlen);
            }

            if (serv != NULL)
            {
                  int solved = 0;
                  struct servent *sent = NULL;

                  /* service name resolution */
                  if (!(flags & NI_NUMERICSERV))
                  {

                        sent = getservbyport(addr->sin_port,
                                         (flags & NI_DGRAM)
                                          ? "udp" : "tcp");
                        if (sent != NULL)
                        {
                              secure_strncpy (serv, sent->s_name,
                                          servlen);
                              solved = 1;
                        }
                  }
                  if (sent == NULL)
                  {
                        snprintf (serv, servlen, "%u",
                                (unsigned int)ntohs (addr->sin_port));
                        serv[servlen - 1] = '\0';
                  }
            }
      }
      return 0;
}
#endif /* if !HAVE_GETNAMEINFO */


#ifndef HAVE_GETADDRINFO
/*
 * This functions must be used to free the memory allocated by getaddrinfo().
 */
void
freeaddrinfo (struct addrinfo *res)
{
      if (res != NULL)
      {
            if (res->ai_canonname != NULL)
                  free (res->ai_canonname);
            if (res->ai_addr != NULL)
                  free (res->ai_addr);
            if (res->ai_next != NULL)
                  free (res->ai_next);
            free (res);
      }
}


/*
 * Internal function that builds an addrinfo struct.
 */
static struct addrinfo *
makeaddrinfo (int af, int type, int proto,
            const struct sockaddr *addr, size_t addrlen,
            const char *canonname)
{
      struct addrinfo *res;

      res = (struct addrinfo *)malloc (sizeof (struct addrinfo));
      if (res != NULL)
      {
            res->ai_flags = 0;
            res->ai_family = af;
            res->ai_socktype = type;
            res->ai_protocol = proto;
            res->ai_addrlen = addrlen;
            res->ai_addr = malloc (addrlen);
            res->ai_canonname = NULL;
            res->ai_next = NULL;

            if (res->ai_addr != NULL)
            {
                  memcpy (res->ai_addr, addr, addrlen);

                  if (canonname != NULL)
                  {
                        res->ai_canonname = strdup (canonname);
                        if (res->ai_canonname != NULL)
                              return res; /* success ! */
                  }
                  else
                        return res;
            }
      }
      /* failsafe */
      freeaddrinfo (res);
      return NULL;
}


static struct addrinfo *
makeipv4info (int type, int proto, u_long ip, u_short port, const char *name)
{
      struct sockaddr_in addr;

      memset (&addr, 0, sizeof (addr));
      addr.sin_family = AF_INET;
# ifdef HAVE_SA_LEN
      addr.sin_len = sizeof (addr);
# endif
      addr.sin_port = port;
      addr.sin_addr.s_addr = ip;

      return makeaddrinfo (AF_INET, type, proto,
                        (struct sockaddr*)&addr, sizeof (addr), name);
}


/*
 * getaddrinfo() non-thread-safe IPv4-only implementation
 * Address-family-independant hostname to address resolution.
 *
 * This is meant for IPv6-unaware systems that do probably not provide
 * getaddrinfo(), but still have old function gethostbyname().
 *
 * Only UDP and TCP over IPv4 are supported here.
 */
int getaddrinfo (const char *node, const char *service,
            const struct addrinfo *hints, struct addrinfo **res)
{
      struct addrinfo *info;
      u_long ip;
      u_short port;
      int protocol = 0, flags = 0;
      const char *name = NULL;

      if (hints != NULL)
      {
            flags = hints->ai_flags;

            if (flags & ~_AI_MASK)
                  return EAI_BADFLAGS;
            /* only accept AF_INET and AF_UNSPEC */
            if (hints->ai_family && (hints->ai_family != AF_INET))
                  return EAI_FAMILY;

            /* protocol sanity check */
            switch (hints->ai_socktype)
            {
                  case SOCK_STREAM:
                        protocol = IPPROTO_TCP;
                        break;

                  case SOCK_DGRAM:
                        protocol = IPPROTO_UDP;
                        break;

                  case SOCK_RAW:
                  case 0:
                        break;

                  default:
                        return EAI_SOCKTYPE;
            }
            if (hints->ai_protocol && protocol
             && (protocol != hints->ai_protocol))
                  return EAI_SERVICE;
      }

      *res = NULL;

      /* default values */
      if (node == NULL)
      {
            if (flags & AI_PASSIVE)
                  ip = htonl (INADDR_ANY);
            else
                  ip = htonl (INADDR_LOOPBACK);
      }
      else
      if ((ip = inet_addr (node)) == INADDR_NONE)
      {
            struct hostent *entry = NULL;

            /* hostname resolution */
            if (!(flags & AI_NUMERICHOST))
                  entry = gethostbyname (node);

            if (entry == NULL)
                  return EAI_NONAME;

            if ((entry->h_length != 4) || (entry->h_addrtype != AF_INET))
                  return EAI_FAMILY;

            ip = *((u_long *) entry->h_addr);
            if (flags & AI_CANONNAME)
                  name = entry->h_name;
      }

      if ((flags & AI_CANONNAME) && (name == NULL))
            name = node;

      /* service resolution */
      if (service == NULL)
            port = 0;
      else
      {
            long d;
            char *end;

            d = strtoul (service, &end, 0);
            if (end[0] /* service is not a number */
             || (d > 65535))
            {
                  struct servent *entry;
                  const char *protoname;

                  switch (protocol)
                  {
                        case IPPROTO_TCP:
                              protoname = "tcp";
                              break;

                        case IPPROTO_UDP:
                              protoname = "udp";
                              break;

                        default:
                              protoname = NULL;
                  }

                  entry = getservbyname (service, protoname);
                  if (entry == NULL)
                        return EAI_SERVICE;

                  port = entry->s_port;
            }
            else
                  port = htons ((u_short)d);
      }

      /* building results... */
      if ((!protocol) || (protocol == IPPROTO_UDP))
      {
            info = makeipv4info (SOCK_DGRAM, IPPROTO_UDP, ip, port, name);
            if (info == NULL)
            {
                  errno = ENOMEM;
                  return EAI_SYSTEM;
            }
            if (flags & AI_PASSIVE)
                  info->ai_flags |= AI_PASSIVE;
            *res = info;
      }
      if ((!protocol) || (protocol == IPPROTO_TCP))
      {
            info = makeipv4info (SOCK_STREAM, IPPROTO_TCP, ip, port, name);
            if (info == NULL)
            {
                  errno = ENOMEM;
                  return EAI_SYSTEM;
            }
            info->ai_next = *res;
            if (flags & AI_PASSIVE)
                  info->ai_flags |= AI_PASSIVE;
            *res = info;
      }

      return 0;
}
#endif /* if !HAVE_GETADDRINFO */


Generated by  Doxygen 1.6.0   Back to index