/tcpreen-1.4.4/libsolve/solve.c
C | 322 lines | 225 code | 43 blank | 54 comment | 54 complexity | 976c424227c4dbfb90e2b4a6fc21578d MD5 | raw file
Possible License(s): GPL-2.0
- /*
- * 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;
- }