]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/posix/getaddrinfo.c
Fix a few error cases in *name4_r lookup handling.
[thirdparty/glibc.git] / sysdeps / posix / getaddrinfo.c
index 8746725d7afefc10e625f561078cdc27c2b097e5..62c38f69be1ac34cefb14feec7a03ba47a144f1c 100644 (file)
@@ -40,6 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <errno.h>
 #include <ifaddrs.h>
 #include <netdb.h>
+#include <nss.h>
 #include <resolv.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -60,6 +61,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <not-cancel.h>
 #include <nscd/nscd-client.h>
 #include <nscd/nscd_proto.h>
+#include <resolv/res_hconf.h>
 
 #ifdef HAVE_LIBIDN
 extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
@@ -91,21 +93,14 @@ struct gaih_servtuple
 
 static const struct gaih_servtuple nullserv;
 
-struct gaih_addrtuple
-  {
-    struct gaih_addrtuple *next;
-    char *name;
-    int family;
-    uint32_t addr[4];
-    uint32_t scopeid;
-  };
 
 struct gaih_typeproto
   {
     int socktype;
     int protocol;
-    char name[4];
-    int protoflag;
+    uint8_t protoflag;
+    bool defaultflag;
+    char name[8];
   };
 
 /* Values for `protoflag'.  */
@@ -114,11 +109,21 @@ struct gaih_typeproto
 
 static const struct gaih_typeproto gaih_inet_typeproto[] =
 {
-  { 0, 0, "", 0 },
-  { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
-  { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
-  { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
-  { 0, 0, "", 0 }
+  { 0, 0, 0, false, "" },
+  { SOCK_STREAM, IPPROTO_TCP, 0, true, "tcp" },
+  { SOCK_DGRAM, IPPROTO_UDP, 0, true, "udp" },
+#if defined SOCK_DCCP && defined IPPROTO_DCCP
+  { SOCK_DCCP, IPPROTO_DCCP, 0, false, "dccp" },
+#endif
+#ifdef IPPROTO_UDPLITE
+  { SOCK_DGRAM, IPPROTO_UDPLITE, 0, false, "udplite" },
+#endif
+#ifdef IPPROTO_SCTP
+  { SOCK_STREAM, IPPROTO_SCTP, 0, false, "sctp" },
+  { SOCK_SEQPACKET, IPPROTO_SCTP, 0, false, "sctp" },
+#endif
+  { SOCK_RAW, 0, GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, true, "raw" },
+  { 0, 0, 0, false, "" }
 };
 
 struct gaih
@@ -202,6 +207,7 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
       if (herrno == NETDB_INTERNAL)                                          \
        {                                                                     \
          __set_h_errno (herrno);                                             \
+         _res.options = old_res_options;                                     \
          return -EAI_SYSTEM;                                                 \
        }                                                                     \
       if (herrno == TRY_AGAIN)                                               \
@@ -246,6 +252,10 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
  }
 
 
+typedef enum nss_status (*nss_gethostbyname4_r)
+  (const char *name, struct gaih_addrtuple **pat,
+   char *buffer, size_t buflen, int *errnop,
+   int *h_errnop, int32_t *ttlp);
 typedef enum nss_status (*nss_gethostbyname3_r)
   (const char *name, int af, struct hostent *host,
    char *buffer, size_t buflen, int *errnop,
@@ -365,18 +375,19 @@ gaih_inet (const char *name, const struct gaih_service *service,
             we know about.  */
          struct gaih_servtuple **lastp = &st;
          for (++tp; tp->name[0]; ++tp)
-           {
-             struct gaih_servtuple *newp;
+           if (tp->defaultflag)
+             {
+               struct gaih_servtuple *newp;
 
-             newp = __alloca (sizeof (struct gaih_servtuple));
-             newp->next = NULL;
-             newp->socktype = tp->socktype;
-             newp->protocol = tp->protocol;
-             newp->port = port;
+               newp = __alloca (sizeof (struct gaih_servtuple));
+               newp->next = NULL;
+               newp->socktype = tp->socktype;
+               newp->protocol = tp->protocol;
+               newp->port = port;
 
-             *lastp = newp;
-             lastp = &newp->next;
-           }
+               *lastp = newp;
+               lastp = &newp->next;
+             }
        }
     }
 
@@ -649,7 +660,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
                  goto process_list;
                }
-             else if (err != 0 && __nss_not_use_nscd_hosts == 0)
+             else if (err == 0)
+               /* The database contains a negative entry.  */
+               return 0;
+             else if (__nss_not_use_nscd_hosts == 0)
                {
                  if (herrno == NETDB_INTERNAL && errno == ENOMEM)
                    return -EAI_MEMORY;
@@ -670,6 +684,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
                                             "dns [!UNAVAIL=return] files",
                                             &nip);
 
+         /* Initialize configurations.  */
+         if (__builtin_expect (!_res_hconf.initialized, 0))
+           _res_hconf_init ();
          if (__res_maybe_init (&_res, 0) == -1)
            no_more = 1;
 
@@ -680,92 +697,139 @@ gaih_inet (const char *name, const struct gaih_service *service,
          old_res_options = _res.options;
          _res.options &= ~RES_USE_INET6;
 
-         size_t tmpbuflen = 512;
+         size_t tmpbuflen = 1024;
          char *tmpbuf = alloca (tmpbuflen);
 
          while (!no_more)
            {
-             nss_gethostbyname3_r fct = NULL;
-             if (req->ai_flags & AI_CANONNAME)
-               /* No need to use this function if we do not look for
-                  the canonical name.  The function does not exist in
-                  all NSS modules and therefore the lookup would
-                  often fail.  */
-               fct = __nss_lookup_function (nip, "gethostbyname3_r");
-             if (fct == NULL)
-               /* We are cheating here.  The gethostbyname2_r function does
-                  not have the same interface as gethostbyname3_r but the
-                  extra arguments the latter takes are added at the end.
-                  So the gethostbyname2_r code will just ignore them.  */
-               fct = __nss_lookup_function (nip, "gethostbyname2_r");
-
-             if (fct != NULL)
+             nss_gethostbyname4_r fct4
+               = __nss_lookup_function (nip, "gethostbyname4_r");
+             if (fct4 != NULL)
                {
-                 if (req->ai_family == AF_INET6
-                     || req->ai_family == AF_UNSPEC)
+                 int herrno;
+
+                 while (1)
                    {
-                     gethosts (AF_INET6, struct in6_addr);
-                     no_inet6_data = no_data;
-                     inet6_status = status;
+                     rc = 0;
+                     status = DL_CALL_FCT (fct4, (name, pat, tmpbuf,
+                                                  tmpbuflen, &rc, &herrno,
+                                                  NULL));
+                     if (status == NSS_STATUS_SUCCESS)
+                       break;
+                     if (status != NSS_STATUS_TRYAGAIN
+                         || rc != ERANGE || herrno != NETDB_INTERNAL)
+                       {
+                         if (status == NSS_STATUS_TRYAGAIN
+                             && herrno == TRY_AGAIN)
+                           no_data = EAI_AGAIN;
+                         else
+                           no_data = herrno == NO_DATA;
+                         break;
+                       }
+                     tmpbuf = extend_alloca (tmpbuf,
+                                             tmpbuflen, 2 * tmpbuflen);
                    }
-                 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) || !got_ipv6)))
+
+                 no_inet6_data = no_data;
+
+                 if (status == NSS_STATUS_SUCCESS)
                    {
-                     gethosts (AF_INET, struct in_addr);
+                     if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
+                       canon = (*pat)->name;
 
-                     if (req->ai_family == AF_INET)
+                     while (*pat != NULL)
+                       pat = &((*pat)->next);
+                   }
+               }
+             else
+               {
+                 nss_gethostbyname3_r fct = NULL;
+                 if (req->ai_flags & AI_CANONNAME)
+                   /* No need to use this function if we do not look for
+                      the canonical name.  The function does not exist in
+                      all NSS modules and therefore the lookup would
+                      often fail.  */
+                   fct = __nss_lookup_function (nip, "gethostbyname3_r");
+                 if (fct == NULL)
+                   /* We are cheating here.  The gethostbyname2_r
+                      function does not have the same interface as
+                      gethostbyname3_r but the extra arguments the
+                      latter takes are added at the end.  So the
+                      gethostbyname2_r code will just ignore them.  */
+                   fct = __nss_lookup_function (nip, "gethostbyname2_r");
+
+                 if (fct != NULL)
+                   {
+                     if (req->ai_family == AF_INET6
+                         || req->ai_family == AF_UNSPEC)
                        {
+                         gethosts (AF_INET6, struct in6_addr);
                          no_inet6_data = no_data;
                          inet6_status = status;
                        }
-                   }
-
-                 /* If we found one address for AF_INET or AF_INET6,
-                    don't continue the search.  */
-                 if (inet6_status == NSS_STATUS_SUCCESS
-                     || status == NSS_STATUS_SUCCESS)
-                   {
-                     if ((req->ai_flags & AI_CANONNAME) != 0 && canon == NULL)
+                     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) || !got_ipv6)))
                        {
-                         /* If we need the canonical name, get it
-                            from the same service as the result.  */
-                         nss_getcanonname_r cfct;
-                         int herrno;
+                         gethosts (AF_INET, struct in_addr);
 
-                         cfct = __nss_lookup_function (nip, "getcanonname_r");
-                         if (cfct != NULL)
+                         if (req->ai_family == AF_INET)
                            {
-                             const size_t max_fqdn_len = 256;
-                             char *buf = alloca (max_fqdn_len);
-                             char *s;
-
-                             if (DL_CALL_FCT (cfct, (at->name ?: name, buf,
-                                                     max_fqdn_len, &s, &rc,
-                                                     &herrno))
-                                 == NSS_STATUS_SUCCESS)
-                               canon = s;
-                             else
-                               /* Set to name now to avoid using
-                                  gethostbyaddr.  */
-                               canon = name;
+                             no_inet6_data = no_data;
+                             inet6_status = status;
                            }
                        }
 
-                     break;
-                   }
+                     /* If we found one address for AF_INET or AF_INET6,
+                        don't continue the search.  */
+                     if (inet6_status == NSS_STATUS_SUCCESS
+                         || status == NSS_STATUS_SUCCESS)
+                       {
+                         if ((req->ai_flags & AI_CANONNAME) != 0
+                             && canon == NULL)
+                           {
+                             /* If we need the canonical name, get it
+                                from the same service as the result.  */
+                             nss_getcanonname_r cfct;
+                             int herrno;
+
+                             cfct = __nss_lookup_function (nip,
+                                                           "getcanonname_r");
+                             if (cfct != NULL)
+                               {
+                                 const size_t max_fqdn_len = 256;
+                                 char *buf = alloca (max_fqdn_len);
+                                 char *s;
+
+                                 if (DL_CALL_FCT (cfct, (at->name ?: name,
+                                                         buf, max_fqdn_len,
+                                                         &s, &rc, &herrno))
+                                     == NSS_STATUS_SUCCESS)
+                                   canon = s;
+                                 else
+                                   /* Set to name now to avoid using
+                                      gethostbyaddr.  */
+                                   canon = name;
+                               }
+                           }
+
+                         break;
+                       }
 
-                 /* We can have different states for AF_INET and
-                    AF_INET6.  Try to find a useful one for both.  */
-                 if (inet6_status == NSS_STATUS_TRYAGAIN)
-                   status = NSS_STATUS_TRYAGAIN;
-                 else if (status == NSS_STATUS_UNAVAIL
-                          && inet6_status != NSS_STATUS_UNAVAIL)
-                   status = inet6_status;
+                     /* We can have different states for AF_INET and
+                        AF_INET6.  Try to find a useful one for both.  */
+                     if (inet6_status == NSS_STATUS_TRYAGAIN)
+                       status = NSS_STATUS_TRYAGAIN;
+                     else if (status == NSS_STATUS_UNAVAIL
+                              && inet6_status != NSS_STATUS_UNAVAIL)
+                       status = inet6_status;
+                   }
+                 else
+                   status = NSS_STATUS_UNAVAIL;
                }
 
              if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
@@ -823,9 +887,6 @@ gaih_inet (const char *name, const struct gaih_service *service,
        }
     }
 
-  if (pai == NULL)
-    return 0;
-
   {
     struct gaih_servtuple *st2;
     struct gaih_addrtuple *at2 = at;
@@ -897,7 +958,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
                    return -EAI_IDN_ENCODE;
                  }
                /* In case the output string is the same as the input
-                  string no new string has been allocated.  Otherwise
+                  string no new string has been allocated and we
                   make a copy.  */
                if (out == canon)
                  goto make_copy;
@@ -1056,7 +1117,10 @@ get_scope (const struct sockaddr_in6 *in6)
     {
       if (! IN6_IS_ADDR_MULTICAST (&in6->sin6_addr))
        {
-         if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr))
+         if (IN6_IS_ADDR_LINKLOCAL (&in6->sin6_addr)
+             /* RFC 4291 2.5.3 says that the loopback address is to be
+                treated like a link-local address.  */
+             || IN6_IS_ADDR_LOOPBACK (&in6->sin6_addr))
            scope = 2;
          else if (IN6_IS_ADDR_SITELOCAL (&in6->sin6_addr))
            scope = 5;
@@ -1105,22 +1169,22 @@ static const struct prefixentry *labels;
 static const struct prefixentry default_labels[] =
   {
     /* See RFC 3484 for the details.  */
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
-      128, 0 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      16, 2 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      96, 3 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
-      96, 4 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }
+      }, 128, 0 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 16, 2 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 96, 3 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }
+      }, 96, 4 },
     /* The next two entries differ from RFC 3484.  We need to treat
        IPv6 site-local addresses special because they are never NATed,
        unlike site-locale IPv4 addresses.  If this would not happen, on
@@ -1128,23 +1192,23 @@ static const struct prefixentry default_labels[] =
        sorting would prefer the IPv6 site-local addresses, causing
        unnecessary delays when trying to connect to a global IPv6 address
        through a site-local IPv6 address.  */
-    { { .in6_u
-       = { .u6_addr8 = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      10, 5 },
-    { { .in6_u
-       = { .u6_addr8 = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      7, 6 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 10, 5 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 7, 6 },
     /* Additional rule for Teredo tunnels.  */
-    { { .in6_u
-       = { .u6_addr8 = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      32, 7 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      0, 1 }
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 32, 7 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 0, 1 }
   };
 
 
@@ -1155,26 +1219,26 @@ static const struct prefixentry *precedence;
 static const struct prefixentry default_precedence[] =
   {
     /* See RFC 3484 for the details.  */
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } },
-      128, 50 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      16, 30 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      96, 20 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } } },
-      96, 10 },
-    { { .in6_u
-       = { .u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } },
-      0, 40 }
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }
+      }, 128, 50 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 16, 30 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 96, 20 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } }
+      }, 96, 10 },
+    { { .__in6_u
+       = { .__u6_addr8 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
+      }, 0, 40 }
   };
 
 
@@ -1189,20 +1253,14 @@ match_prefix (const struct sockaddr_in6 *in6,
     {
       const struct sockaddr_in *in = (const struct sockaddr_in *) in6;
 
-      /* Convert to IPv6 address.  */
+      /* Construct a V4-to-6 mapped address.  */
       in6_mem.sin6_family = PF_INET6;
       in6_mem.sin6_port = in->sin_port;
       in6_mem.sin6_flowinfo = 0;
-      if (in->sin_addr.s_addr == htonl (0x7f000001))
-       in6_mem.sin6_addr = (struct in6_addr) IN6ADDR_LOOPBACK_INIT;
-      else
-       {
-         /* Construct a V4-to-6 mapped address.  */
-         memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
-         in6_mem.sin6_addr.s6_addr16[5] = 0xffff;
-         in6_mem.sin6_addr.s6_addr32[3] = in->sin_addr.s_addr;
-         in6_mem.sin6_scope_id = 0;
-       }
+      memset (&in6_mem.sin6_addr, '\0', sizeof (in6_mem.sin6_addr));
+      in6_mem.sin6_addr.s6_addr16[5] = 0xffff;
+      in6_mem.sin6_addr.s6_addr32[3] = in->sin_addr.s_addr;
+      in6_mem.sin6_scope_id = 0;
 
       in6 = &in6_mem;
     }
@@ -1775,6 +1833,7 @@ gaiconf_init (void)
                    *cp++ = '\0';
                  if (inet_pton (AF_INET6, val1, &prefix))
                    {
+                     bits = 128;
                      if (IN6_IS_ADDR_V4MAPPED (&prefix)
                          && (cp == NULL
                              || (bits = strtoul (cp, &endp, 10)) != ULONG_MAX
@@ -2050,7 +2109,7 @@ getaddrinfo (const char *name, const char *service,
        {
          /* If we haven't seen both IPv4 and IPv6 interfaces we can
             narrow down the search.  */
-         if (! seen_ipv4 || ! seen_ipv6)
+         if ((! seen_ipv4 || ! seen_ipv6) && (seen_ipv4 || seen_ipv6))
            {
              local_hints = *hints;
              local_hints.ai_family = seen_ipv4 ? PF_INET : PF_INET6;
@@ -2087,11 +2146,7 @@ getaddrinfo (const char *name, const char *service,
   else
     pservice = NULL;
 
-  struct addrinfo **end;
-  if (pai)
-    end = &p;
-  else
-    end = NULL;
+  struct addrinfo **end = &p;
 
   unsigned int naddrs = 0;
   if (hints->ai_family == AF_UNSPEC || hints->ai_family == AF_INET
@@ -2105,12 +2160,11 @@ getaddrinfo (const char *name, const char *service,
 
          return -(last_i & GAIH_EAI);
        }
-      if (end)
-       while (*end)
-         {
-           end = &((*end)->ai_next);
-           ++nresults;
-         }
+      while (*end)
+       {
+         end = &((*end)->ai_next);
+         ++nresults;
+       }
     }
   else
     {
@@ -2306,9 +2360,6 @@ getaddrinfo (const char *name, const char *service,
       return 0;
     }
 
-  if (pai == NULL && last_i == 0)
-    return 0;
-
   return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
 }
 libc_hidden_def (getaddrinfo)