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

solve.c

/*
 * solve.c - Secure RFC-2553-based IP resolution functions
 * $Id: solve.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 Public 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

#include <stdio.h> /* stderr */
#include "secstr.h" /* memset(), memcpy(), strcmp() */
#include <stdlib.h> /* malloc(), free() */

#include <sys/types.h> /* needed before sys/socket.h on FreeBSD */
#if HAVE_SYS_SOCKET_H
# include <sys/socket.h> /* getsockname(), getpeername() */
#endif
#if HAVE_SYS_UN_H
# include <sys/un.h>
#endif
#include <errno.h>
#include "solve.h"


/*
 * Compares 2 socket addresses. Return 0 if they are identical,
 * a positive if they are different, a negative on error. It is assumed
 * that they are of the same size (otherwise, YOU know they are
 * different anyway, don't you?).
 *
 * Only nodes are compared, services are not.
 */
static int
sockaddrcmp (const struct sockaddr *a1, size_t a1len,
             const struct sockaddr *a2, size_t a2len)
{
      char n1[NI_MAXHOST], n2[NI_MAXHOST];
      /* Normally, we'd compare addr and res->ai_addr, but there is
      no address family independant way to do this (memcmp() won't
      work in many case (at least Linux/IPv4).

      Instead, we do compare numerical address strings. This requires
      yet another (but fortunately non-blocking) call of getnameinfo.

      It is moreover assumed that service names cannot be spoofed
      (Service Name Service has not been invented, right?).
      */
      if ((a1->sa_family != a2->sa_family)
       || getnameinfo (a1, a1len, n1, sizeof (n1), NULL, 0, NI_NUMERICHOST)
       || getnameinfo (a2, a2len, n2, sizeof (n2), NULL, 0, NI_NUMERICHOST))
            return -1;

      return (strcmp (n1, n2) == 0) ? 0 : 1;
}

/*
 * Secure reverse DNS resolution.
 * NI_NOFQDN (flags option) will fail unless addr is on the same domain
 * as we are (this is absolutely normal). All other flags should work
 * correctly.
 *
 * In case of error, if *servbuf is true, the service name is ok.
 */
static int
secure_getnameinfo(const struct sockaddr *addr, size_t addrlen,
                  char *namebuf, size_t namelen,
                  char *servbuf, size_t servlen, int flags)
{
      int check;

      /* Gets service name once and for all */
      check = getnameinfo (addr, addrlen, NULL, 0, servbuf, servlen,
                  flags);
      if (check != 0)
            return check;

      /* Reverse DNS request */
      check = getnameinfo (addr, addrlen, namebuf, namelen, NULL, 0, flags);
      if ((check != 0) || (flags & NI_NUMERICHOST))
            return check; /* If numeric host name is requested, done. */
      else
      {
            struct addrinfo hints, *res, *info;

            /* Hostname DNS request (to prevent malicious users
             * from DNS spoofing us). */
            memset (&hints, 0, sizeof (hints));
            hints.ai_family = addr->sa_family;
            check = getaddrinfo (namebuf, NULL, &hints, &res);

            if (check == 0)
            {
                  for (info = res; info != NULL; info = info->ai_next)
                        if (!sockaddrcmp (addr, addrlen, info->ai_addr,
                                          info->ai_addrlen))
                        {
                              freeaddrinfo(res);
                              return 0;
                        }

                  /* DNS spoofing detected: use numeric address only */
                  freeaddrinfo (res);
            }
      }
      return getnameinfo (addr, addrlen, namebuf, namelen, NULL, 0,
                        flags|NI_NUMERICHOST);
}


/*** Generic struct addrinfo handling ***/
void
freeai (struct addrinfo *res)
{
      if (res != NULL)
      {
            freeai (res->ai_next);
            if (res->ai_addr != NULL)
                  free (res->ai_addr);
            free (res);
      }
}


struct addrinfo *
makeai (const struct sockaddr *addr, socklen_t addrlen)
{
      struct addrinfo *res = (struct addrinfo *)
                        malloc (sizeof (struct addrinfo));
      if (res == NULL)
            return NULL;

      memset (res, 0, sizeof (struct addrinfo));
      if (addr != NULL)
      {
            struct sockaddr *ad = (struct sockaddr *)
                              malloc (addrlen);
            if (ad == NULL)
            {
                  int errb = errno;
                  free (res);
                  errno = errb;
                  return NULL;
            }
            memcpy (ad, addr, addrlen);
            res->ai_addr = ad;
      }
      res->ai_addrlen = addrlen;

      return res;
}


struct addrinfo *
copyai (const struct addrinfo *src)
{
      if (src != NULL)
      {
            struct addrinfo *res;

            res = (struct addrinfo *)malloc (sizeof (struct addrinfo));
            if (res == NULL)
                  return NULL;
            memcpy (res, src, sizeof (struct addrinfo));

            if (src->ai_next != NULL)
            {
                  res->ai_next = copyai (src->ai_next);
                  if (res->ai_next == NULL)
                  {
                        int errb = errno;
                        free (res);
                        errno = errb;
                        return NULL;
                  }
            }

            if (src->ai_addr != NULL)
            {
                  res->ai_addr =
                        (struct sockaddr *)malloc (src->ai_addrlen);
                  if (res->ai_addr == NULL)
                  {
                        int errb = errno;
                        freeai (res->ai_next);
                        free (res);
                        errno = errb;
                        return NULL;
                  }

                  memcpy (res->ai_addr, src->ai_addr, src->ai_addrlen);
            }

            return res;
      }
      return NULL;
}


#if HAVE_SYS_UN_H
/*** Unix (a.k.a. "local")  addresses ***/
static int
unix_getaddrinfo (const char *path, const struct addrinfo *hints,
                  struct addrinfo **res)
{
      if (path != NULL)
      {
            struct sockaddr_un addr;
            struct addrinfo *ret;

            memset (&addr, 0, sizeof (addr));
            addr.sun_family = AF_LOCAL;
# ifdef HAVE_SA_LEN
            addr.sun_len = sizeof (addr);
# endif
            strncpy (addr.sun_path, path, sizeof (addr.sun_path));
            if (addr.sun_path[sizeof (addr.sun_path) - 1])
                  return EAI_NONAME;

            ret = makeai ((struct sockaddr *)&addr, sizeof (addr));
            if (ret == NULL)
                  return EAI_MEMORY;
            ret->ai_family = AF_LOCAL;

            ret->ai_socktype = hints->ai_socktype ?: SOCK_DGRAM;
            *res = ret;
            return 0;
      }
      return EAI_NONAME;
}
#endif


/*** Protocols family-independant addresses resolution ***/
int getnamebyaddr (const struct sockaddr *addr, size_t addrlen,
                  char *nodename, size_t nlen, char *service,
                  size_t slen, int flags)
{
      switch (addr->sa_family)
      {
            case AF_INET:
#ifdef AF_INET6
            case AF_INET6:
#endif
                  return secure_getnameinfo (addr, addrlen, nodename,
                                    nlen, service, slen, flags);

#if HAVE_SYS_UN_H
            case AF_LOCAL:
                  *nodename = 0;
                  secure_strncpy (service,
                              ((struct sockaddr_un *)addr)->sun_path,
                              slen);
                  return 0;
#endif
      }

      return EAI_FAMILY;
}


int
getaddrbyname (const char *node, const char *service,
            const struct addrinfo *hints, struct addrinfo **res)
{
      switch ((hints != NULL) ? hints->ai_family : 0)
      {
            case 0:
            case AF_INET:
#ifdef AF_INET6
            case AF_INET6:
#endif
            {
                  int check;
                  struct addrinfo *inet_res, inet_hints;

                  if (hints != NULL)
                        memcpy (&inet_hints, hints,
                              sizeof (struct addrinfo));
                  else
                        memset (&inet_hints, 0,
                              sizeof (struct addrinfo));
                  inet_hints.ai_flags |= AI_IDN;

                  // Avoids unknown service error
                  if ((node == NULL) && (service == NULL))
                              service = "0";

                  check = getaddrinfo (node, service, &inet_hints,
                                    &inet_res);
                  if (check)
                        return check;

                  *res = copyai (inet_res);
                  freeaddrinfo (inet_res);
                  return (*res == NULL) ? EAI_SYSTEM : 0;
            }

#if HAVE_SYS_UN_H
            case AF_LOCAL:
                  return (node != NULL) ? EAI_SERVICE
                        : unix_getaddrinfo (service, hints, res);
#endif
      }

      return EAI_FAMILY;
}

Generated by  Doxygen 1.6.0   Back to index