getaddrinfo with PF_UNSPEC and /etc/hosts

Ben Collins bcollins@debian.org
Wed Nov 14 04:25:00 GMT 2001


On Thu, Nov 22, 2001 at 10:41:31PM +0200, Pekka Savola wrote:
> On 22 Nov 2001, Ulrich Drepper wrote:
> 
> > Pekka Savola <pekkas@netcore.fi> writes:
> > 
> > > It was pointed out by Rafal Wojtczuk on Owl development list that telnet
> > > that ships with Red Hat Linux 7.2 does not behave the way one would expect
> > > when resolving names using /etc/hosts.  That is, if you have:
> > > [...]
> > 
> > And you are unable to read the glibc documentation why?  The BUGS file
> > clearly document this.
> 
> Sorry, I didn't notice this.
> 
> For more info, e.g.:
> 
> http://sources.redhat.com/ml/libc-alpha/2000-11/msg00172.html
> http://bugs.gnu.org/cgi-bin/gnatsweb.pl?debug=&database=default&cmd=view+audit-trail&cmd=view&pr=1663
> http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=60743
> 
> Debian appears to have a patch that AFAICS hacks around this
> (debian/patches/glibc22-getaddrinfo.dpatch), I haven't tested to see if 
> this is the case.

The test has been tested for quite some time. IMO, it is a security
concern, but I was somewhat blown off when I last brought this up. The
"fix" is a real hack job, but it works, and has been tested since
2001-01-09 (2.2 + CVS), with no ill affects that I know of.

It's attached below for the interested.

2001-01-09  Ben Collins  <bcollins@debian.org>

	* sysdeps/posix/getaddrinfo.c(gethosts): Accept name to lookup,
	so we can explicitly pass it to __gethostbyname2_r(). Also make
	sure we decrement succeeded if that fails.
	(tryname): New macro to do lookup handling, broken out of
	gaih_inet().
	(gaih_inet): Make use of tryname macro and special case a dotted
	lookup to avoid searching IPv6 domain names on an PF_UNSPEC
	lookup.

-- 
 .----------=======-=-======-=========-----------=====------------=-=-----.
/                   Ben Collins    --    Debian GNU/Linux                  \
`  bcollins@debian.org  --  bcollins@openldap.org  --  bcollins@linux.com  '
 `---=========------=======-------------=-=-----=-===-======-------=--=---'
-------------- next part --------------
--- glibc/sysdeps/posix/getaddrinfo.c~	2000/11/18 08:30:02	1.34
+++ glibc/sysdeps/posix/getaddrinfo.c	2001/01/09 01:46:49
@@ -264,7 +264,7 @@
   return 0;
 }
 
-#define gethosts(_family, _type)				\
+#define gethosts(_family, _type, _name)				\
  {								\
   int i, herrno;						\
   size_t tmpbuflen;						\
@@ -275,11 +275,12 @@
   do {								\
     tmpbuflen *= 2;						\
     tmpbuf = __alloca (tmpbuflen);				\
-    rc = __gethostbyname2_r (name, _family, &th, tmpbuf,	\
+    rc = __gethostbyname2_r (_name, _family, &th, tmpbuf,	\
          tmpbuflen, &h, &herrno);				\
   } while (rc == ERANGE && herrno == NETDB_INTERNAL);		\
   if (rc != 0)							\
     {								\
+      succeeded--;						\
       if (herrno == NETDB_INTERNAL)				\
 	{							\
 	  __set_h_errno (herrno);				\
@@ -307,6 +308,26 @@
     }								\
  }
 
+/* If we are looking for both IPv4 and IPv6 address we don't
+   want the lookup functions to automatically promote IPv4
+   addresses to IPv6 addresses.  Currently this is decided
+   by setting the RES_USE_INET6 bit in _res.options.  */
+#define tryname(__name)						\
+  {								\
+    succeeded = req->ai_family == AF_UNSPEC ? 2 : 1;		\
+    if (req->ai_family == AF_UNSPEC)				\
+      _res.options &= ~RES_USE_INET6;				\
+    if (req->ai_family == AF_UNSPEC ||				\
+	req->ai_family == AF_INET6)				\
+      gethosts (AF_INET6, struct in6_addr, __name);		\
+    no_inet6_data = no_data;					\
+    if (req->ai_family == AF_UNSPEC)				\
+      _res.options = old_res_options;				\
+    if (req->ai_family == AF_UNSPEC ||				\
+	req->ai_family == AF_INET)				\
+    gethosts (AF_INET, struct in_addr, __name);			\
+  }
+
 static int
 gaih_inet (const char *name, const struct gaih_service *service,
 	   const struct addrinfo *req, struct addrinfo **pai)
@@ -316,6 +337,10 @@
   struct gaih_addrtuple *at = NULL;
   int rc;
 
+  /* Make sure we are initialized now */
+  if (!(_res.options & RES_INIT))
+    res_ninit(&_res);
+
   if (req->ai_protocol || req->ai_socktype)
     {
       ++tp;
@@ -489,34 +514,47 @@
 	  struct gaih_addrtuple **pat = &at;
 	  int no_data = 0;
 	  int no_inet6_data;
+	  int succeeded = -1;
 	  int old_res_options = _res.options;
 
-	  /* If we are looking for both IPv4 and IPv6 address we don't
-	     want the lookup functions to automatically promote IPv4
-	     addresses to IPv6 addresses.  Currently this is decided
-	     by setting the RES_USE_INET6 bit in _res.options.  */
-	  if (req->ai_family == AF_UNSPEC)
-	    _res.options &= ~RES_USE_INET6;
-
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
-	    gethosts (AF_INET6, struct in6_addr);
-	  no_inet6_data = no_data;
+	  /* If we have a dotted name, first try an absolute lookup.  This
+	     avoids problem where the resolver will try to search domains
+	     even though we may have a fqdn, which is bad since ipv6 lookups
+	     are performed first.  */
+	  if (strchr(name, '.') != NULL)
+	    {
+	      int namelen = strlen(name);
+	      char *newname = __alloca(namelen + 2);
 
-	  if (req->ai_family == AF_UNSPEC)
-	    _res.options = old_res_options;
+	      strcpy(newname, name);
+	      newname[namelen] = '.';
+	      newname[namelen+1] = '\0';
 
-	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
-	    gethosts (AF_INET, struct in_addr);
+	      tryname(newname);
+	    }
 
-	  if (no_data != 0 && no_inet6_data != 0)
+	  /* If the previous didn't work, or we don't have a dotted name,
+	     we try again like normal, letting the resolver manage domain
+	     searching.  */
+	  if (succeeded <= 0)
 	    {
-	      /* If both requests timed out report this.  */
-	      if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
-		return -EAI_AGAIN;
-
-	      /* We made requests but they turned out no data.  The name
-		 is known, though.  */
-	      return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+	      _res.options = old_res_options;
+
+	      if (succeeded == 0) /* we've tried absolute, now try searches */
+		_res.ndots = 99;
+
+	      tryname(name);
+
+	      if (no_data != 0 && no_inet6_data != 0)
+	        {
+		  /* If both requests timed out report this.  */
+		  if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
+		    return -EAI_AGAIN;
+
+		  /* We made requests but they turned out no data.  The name
+		     is known, though.  */
+		  return (GAIH_OKIFUNSPEC | -EAI_NODATA);
+		}
 	    }
 	}
 


More information about the Libc-alpha mailing list