commit 45918e237cd32bb3d9a5fde83f81f83b52584771
Author: Stefan Saraev <stefan@saraev.ca>
Date:   Tue Oct 1 12:09:07 2013 +0300

    fix dns with broken routers
    
    ref: https://fedoraproject.org/wiki/Networking/NameResolution/ADDRCONFIG

--- a/nss/getaddrinfo.c
+++ b/nss/getaddrinfo.c
@@ -730,6 +730,38 @@ gaih_inet (const char *name, const struc
   if (res_ctx == NULL)
     no_more = 1;
 
+  /* AI_ADDRCONFIG determines whether or not we should suppress any
+   * hostname lookups. If the local host has only IPv4 interfaces,
+   * then suppress lookups for IPv6 addresses, and vice versa; if
+   * the local host has only IPv6 interfaces, suppress any lookups
+   * for IPv4 addresses..
+   * 
+   * Link-local IPv6 addresses and loopback addresses of either
+   * family are ignored when determining whether or not the host has
+   * an interface of the given address family, cf. __check_pf().
+   *
+   * This logic is only applied for AF_UNSPEC. If the caller
+   * explicitly requested an address family, give him what he asked
+   * for.
+   *
+   * If we didn't find any interfaces of either address family,
+   * we ignore AI_ADDRCONFIG and return all available resutlts.  */
+  int suppress_af = 0;
+  if (req->ai_family == AF_UNSPEC)
+    {
+      struct in6addrinfo *in6ai = NULL;
+      size_t in6ailen = 0;
+      bool seen_ipv4 = false;
+      bool seen_ipv6 = false;
+      __check_pf (&seen_ipv4, &seen_ipv6, &in6ai, &in6ailen);
+      __free_in6ai (in6ai);
+
+      if(seen_ipv4 && !seen_ipv6)
+	  suppress_af = AF_INET6;
+      else if(seen_ipv6 && !seen_ipv4)
+	  suppress_af = AF_INET;
+    }
+
   while (!no_more)
     {
       no_data = 0;
@@ -737,7 +769,7 @@ gaih_inet (const char *name, const struc
 
       /* gethostbyname4_r sends out parallel A and AAAA queries and
 	 is thus only suitable for PF_UNSPEC.  */
-      if (req->ai_family == PF_UNSPEC)
+      if (req->ai_family == PF_UNSPEC && !suppress_af)
 	fct4 = __nss_lookup_function (nip, "gethostbyname4_r");
 
       if (fct4 != NULL)
@@ -826,25 +858,27 @@ gaih_inet (const char *name, const struc
 
 	  if (fct != NULL)
 	    {
-	      if (req->ai_family == AF_INET6
-		  || req->ai_family == AF_UNSPEC)
+	      if ((req->ai_family == AF_INET6
+		   || req->ai_family == AF_UNSPEC)
+		  && suppress_af != AF_INET6)
 		{
 		  if ((result = gethosts (fct, AF_INET6, name, req, tmpbuf,
 					  res, &status, &no_data)) != 0)
 		    {
 		      __resolv_context_put (res_ctx);
 		      goto out;
 		    }
 		  no_inet6_data = no_data;
 		  inet6_status = status;
 		}
-	      if (req->ai_family == AF_INET
-		  || req->ai_family == AF_UNSPEC
-		  || (req->ai_family == AF_INET6
-		      && (req->ai_flags & AI_V4MAPPED)
-		      /* Avoid generating the mapped addresses if we
-			 know we are not going to need them.  */
-		      && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
+	      if ((req->ai_family == AF_INET
+		   || req->ai_family == AF_UNSPEC
+		   || (req->ai_family == AF_INET6
+		       && (req->ai_flags & AI_V4MAPPED)
+		       /* Avoid generating the mapped addresses if we
+			  know we are not going to need them.  */
+		       && ((req->ai_flags & AI_ALL) || !res->got_ipv6)))
+		       && suppress_af != AF_INET)
 		{
 		  if ((result = gethosts (fct, AF_INET, name, req, tmpbuf,
 					  res, &status, &no_data)) != 0)
--- a/sysdeps/unix/sysv/linux/check_pf.c
+++ b/sysdeps/unix/sysv/linux/check_pf.c
@@ -224,7 +224,8 @@ make_request (int fd, pid_t pid)
 		    }
 		  else
 		    {
-		      if (!IN6_IS_ADDR_LOOPBACK (address))
+		      if (!IN6_IS_ADDR_LOOPBACK (address) &&
+			  !IN6_IS_ADDR_LINKLOCAL (address))
 			seen_ipv6 = true;
 		    }
 		}
