d8307d
commit 108bc4049f8ae82710aec26a92ffdb4b439c83fd
d8307d
Author: Florian Weimer <fweimer@redhat.com>
d8307d
Date:   Mon Jan 21 21:26:03 2019 +0100
d8307d
d8307d
    CVE-2016-10739: getaddrinfo: Fully parse IPv4 address strings [BZ #20018]
d8307d
    
d8307d
    The IPv4 address parser in the getaddrinfo function is changed so that
d8307d
    it does not ignore trailing whitespace and all characters after it.
d8307d
    For backwards compatibility, the getaddrinfo function still recognizes
d8307d
    legacy name syntax, such as 192.000.002.010 interpreted as 192.0.2.8
d8307d
    (octal).
d8307d
    
d8307d
    This commit does not change the behavior of inet_addr and inet_aton.
d8307d
    gethostbyname already had additional sanity checks (but is switched
d8307d
    over to the new __inet_aton_exact function for completeness as well).
d8307d
    
d8307d
    To avoid sending the problematic query names over DNS, commit
d8307d
    6ca53a2453598804a2559a548a08424fca96434a ("resolv: Do not send queries
d8307d
    for non-host-names in nss_dns [BZ #24112]") is needed.
d8307d
d8307d
diff --git a/include/arpa/inet.h b/include/arpa/inet.h
d8307d
index c3f28f2baaa2ed66..19aec74275069a45 100644
d8307d
--- a/include/arpa/inet.h
d8307d
+++ b/include/arpa/inet.h
d8307d
@@ -1,10 +1,10 @@
d8307d
 #include <inet/arpa/inet.h>
d8307d
 
d8307d
 #ifndef _ISOMAC
d8307d
-extern int __inet_aton (const char *__cp, struct in_addr *__inp);
d8307d
-libc_hidden_proto (__inet_aton)
d8307d
+/* Variant of inet_aton which rejects trailing garbage.  */
d8307d
+extern int __inet_aton_exact (const char *__cp, struct in_addr *__inp);
d8307d
+libc_hidden_proto (__inet_aton_exact)
d8307d
 
d8307d
-libc_hidden_proto (inet_aton)
d8307d
 libc_hidden_proto (inet_ntop)
d8307d
 libc_hidden_proto (inet_pton)
d8307d
 extern __typeof (inet_pton) __inet_pton;
d8307d
diff --git a/nscd/gai.c b/nscd/gai.c
d8307d
index 24bdfee1db3791e2..f57f396f574a6e52 100644
d8307d
--- a/nscd/gai.c
d8307d
+++ b/nscd/gai.c
d8307d
@@ -19,7 +19,6 @@
d8307d
 
d8307d
 /* This file uses the getaddrinfo code but it compiles it without NSCD
d8307d
    support.  We just need a few symbol renames.  */
d8307d
-#define __inet_aton inet_aton
d8307d
 #define __ioctl ioctl
d8307d
 #define __getsockname getsockname
d8307d
 #define __socket socket
d8307d
diff --git a/nscd/gethstbynm3_r.c b/nscd/gethstbynm3_r.c
d8307d
index 7beb9dce9f4b350c..f792c4fcd042d13d 100644
d8307d
--- a/nscd/gethstbynm3_r.c
d8307d
+++ b/nscd/gethstbynm3_r.c
d8307d
@@ -38,8 +38,6 @@
d8307d
 #define HAVE_LOOKUP_BUFFER	1
d8307d
 #define HAVE_AF			1
d8307d
 
d8307d
-#define __inet_aton inet_aton
d8307d
-
d8307d
 /* We are nscd, so we don't want to be talking to ourselves.  */
d8307d
 #undef	USE_NSCD
d8307d
 
d8307d
diff --git a/nss/digits_dots.c b/nss/digits_dots.c
d8307d
index 39bff38865a1ac5b..5441bce16ea8b2e9 100644
d8307d
--- a/nss/digits_dots.c
d8307d
+++ b/nss/digits_dots.c
d8307d
@@ -29,7 +29,6 @@
d8307d
 #include "nsswitch.h"
d8307d
 
d8307d
 #ifdef USE_NSCD
d8307d
-# define inet_aton __inet_aton
d8307d
 # include <nscd/nscd_proto.h>
d8307d
 #endif
d8307d
 
d8307d
@@ -160,7 +159,7 @@ __nss_hostname_digits_dots_context (struct resolv_context *ctx,
d8307d
 		     255.255.255.255?  The test below will succeed
d8307d
 		     spuriously... ???  */
d8307d
 		  if (af == AF_INET)
d8307d
-		    ok = __inet_aton (name, (struct in_addr *) host_addr);
d8307d
+		    ok = __inet_aton_exact (name, (struct in_addr *) host_addr);
d8307d
 		  else
d8307d
 		    {
d8307d
 		      assert (af == AF_INET6);
d8307d
diff --git a/resolv/Makefile b/resolv/Makefile
d8307d
index 56718654eeab85a3..72a0f196506ac489 100644
d8307d
--- a/resolv/Makefile
d8307d
+++ b/resolv/Makefile
d8307d
@@ -34,6 +34,9 @@ routines := herror inet_addr inet_ntop inet_pton nsap_addr res_init \
d8307d
 tests = tst-aton tst-leaks tst-inet_ntop
d8307d
 xtests = tst-leaks2
d8307d
 
d8307d
+tests-internal += tst-inet_aton_exact
d8307d
+
d8307d
+
d8307d
 generate := mtrace-tst-leaks.out tst-leaks.mtrace tst-leaks2.mtrace
d8307d
 
d8307d
 extra-libs := libresolv libnss_dns
d8307d
@@ -54,8 +57,10 @@ tests += \
d8307d
   tst-resolv-binary \
d8307d
   tst-resolv-edns \
d8307d
   tst-resolv-network \
d8307d
+  tst-resolv-nondecimal \
d8307d
   tst-resolv-res_init-multi \
d8307d
   tst-resolv-search \
d8307d
+  tst-resolv-trailing \
d8307d
 
d8307d
 # These tests need libdl.
d8307d
 ifeq (yes,$(build-shared))
d8307d
@@ -190,9 +195,11 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \
d8307d
   $(shared-thread-library)
d8307d
 $(objpfx)tst-resolv-res_init-thread: $(libdl) $(objpfx)libresolv.so \
d8307d
   $(shared-thread-library)
d8307d
+$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
d8307d
 $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
d8307d
 $(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library)
d8307d
 $(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library)
d8307d
+$(objpfx)tst-resolv-trailing: $(objpfx)libresolv.so $(shared-thread-library)
d8307d
 $(objpfx)tst-resolv-threads: \
d8307d
   $(libdl) $(objpfx)libresolv.so $(shared-thread-library)
d8307d
 $(objpfx)tst-resolv-canonname: \
d8307d
diff --git a/resolv/Versions b/resolv/Versions
d8307d
index b05778d9654aa0f2..9a82704af75f789b 100644
d8307d
--- a/resolv/Versions
d8307d
+++ b/resolv/Versions
d8307d
@@ -27,6 +27,7 @@ libc {
d8307d
     __h_errno; __resp;
d8307d
 
d8307d
     __res_iclose;
d8307d
+    __inet_aton_exact;
d8307d
     __inet_pton_length;
d8307d
     __resolv_context_get;
d8307d
     __resolv_context_get_preinit;
d8307d
diff --git a/resolv/inet_addr.c b/resolv/inet_addr.c
d8307d
index 32f58b0e13598b32..41b6166a5bd5a44b 100644
d8307d
--- a/resolv/inet_addr.c
d8307d
+++ b/resolv/inet_addr.c
d8307d
@@ -96,26 +96,14 @@
d8307d
 #include <limits.h>
d8307d
 #include <errno.h>
d8307d
 
d8307d
-/* ASCII IPv4 Internet address interpretation routine.  The value
d8307d
-   returned is in network order.  */
d8307d
-in_addr_t
d8307d
-__inet_addr (const char *cp)
d8307d
-{
d8307d
-  struct in_addr val;
d8307d
-
d8307d
-  if (__inet_aton (cp, &val))
d8307d
-    return val.s_addr;
d8307d
-  return INADDR_NONE;
d8307d
-}
d8307d
-weak_alias (__inet_addr, inet_addr)
d8307d
-
d8307d
 /* Check whether "cp" is a valid ASCII representation of an IPv4
d8307d
    Internet address and convert it to a binary address.  Returns 1 if
d8307d
    the address is valid, 0 if not.  This replaces inet_addr, the
d8307d
    return value from which cannot distinguish between failure and a
d8307d
-   local broadcast address.  */
d8307d
-int
d8307d
-__inet_aton (const char *cp, struct in_addr *addr)
d8307d
+   local broadcast address.  Write a pointer to the first
d8307d
+   non-converted character to *endp.  */
d8307d
+static int
d8307d
+inet_aton_end (const char *cp, struct in_addr *addr, const char **endp)
d8307d
 {
d8307d
   static const in_addr_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
d8307d
   in_addr_t val;
d8307d
@@ -180,6 +168,7 @@ __inet_aton (const char *cp, struct in_addr *addr)
d8307d
 
d8307d
   if (addr != NULL)
d8307d
     addr->s_addr = res.word | htonl (val);
d8307d
+  *endp = cp;
d8307d
 
d8307d
   __set_errno (saved_errno);
d8307d
   return 1;
d8307d
@@ -188,6 +177,41 @@ __inet_aton (const char *cp, struct in_addr *addr)
d8307d
   __set_errno (saved_errno);
d8307d
   return 0;
d8307d
 }
d8307d
-weak_alias (__inet_aton, inet_aton)
d8307d
-libc_hidden_def (__inet_aton)
d8307d
-libc_hidden_weak (inet_aton)
d8307d
+
d8307d
+int
d8307d
+__inet_aton_exact (const char *cp, struct in_addr *addr)
d8307d
+{
d8307d
+  struct in_addr val;
d8307d
+  const char *endp;
d8307d
+  /* Check that inet_aton_end parsed the entire string.  */
d8307d
+  if (inet_aton_end (cp, &val, &endp) != 0 && *endp == 0)
d8307d
+    {
d8307d
+      *addr = val;
d8307d
+      return 1;
d8307d
+    }
d8307d
+  else
d8307d
+    return 0;
d8307d
+}
d8307d
+libc_hidden_def (__inet_aton_exact)
d8307d
+
d8307d
+/* inet_aton ignores trailing garbage.  */
d8307d
+int
d8307d
+__inet_aton_ignore_trailing (const char *cp, struct in_addr *addr)
d8307d
+{
d8307d
+  const char *endp;
d8307d
+  return  inet_aton_end (cp, addr, &endp);
d8307d
+}
d8307d
+weak_alias (__inet_aton_ignore_trailing, inet_aton)
d8307d
+
d8307d
+/* ASCII IPv4 Internet address interpretation routine.  The value
d8307d
+   returned is in network order.  */
d8307d
+in_addr_t
d8307d
+__inet_addr (const char *cp)
d8307d
+{
d8307d
+  struct in_addr val;
d8307d
+  const char *endp;
d8307d
+  if (inet_aton_end (cp, &val, &endp))
d8307d
+    return val.s_addr;
d8307d
+  return INADDR_NONE;
d8307d
+}
d8307d
+weak_alias (__inet_addr, inet_addr)
d8307d
diff --git a/resolv/res_init.c b/resolv/res_init.c
d8307d
index f5e52cbbb9377762..94743a252e39d64a 100644
d8307d
--- a/resolv/res_init.c
d8307d
+++ b/resolv/res_init.c
d8307d
@@ -399,8 +399,16 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
d8307d
               cp = parser->buffer + sizeof ("nameserver") - 1;
d8307d
               while (*cp == ' ' || *cp == '\t')
d8307d
                 cp++;
d8307d
+
d8307d
+              /* Ignore trailing contents on the name server line.  */
d8307d
+              {
d8307d
+                char *el;
d8307d
+                if ((el = strpbrk (cp, " \t\n")) != NULL)
d8307d
+                  *el = '\0';
d8307d
+              }
d8307d
+
d8307d
               struct sockaddr *sa;
d8307d
-              if ((*cp != '\0') && (*cp != '\n') && __inet_aton (cp, &a))
d8307d
+              if ((*cp != '\0') && (*cp != '\n') && __inet_aton_exact (cp, &a))
d8307d
                 {
d8307d
                   sa = allocate_address_v4 (a, NAMESERVER_PORT);
d8307d
                   if (sa == NULL)
d8307d
@@ -410,9 +418,6 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
d8307d
                 {
d8307d
                   struct in6_addr a6;
d8307d
                   char *el;
d8307d
-
d8307d
-                  if ((el = strpbrk (cp, " \t\n")) != NULL)
d8307d
-                    *el = '\0';
d8307d
                   if ((el = strchr (cp, SCOPE_DELIMITER)) != NULL)
d8307d
                     *el = '\0';
d8307d
                   if ((*cp != '\0') && (__inet_pton (AF_INET6, cp, &a6) > 0))
d8307d
@@ -472,7 +477,7 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
d8307d
                   char separator = *cp;
d8307d
                   *cp = 0;
d8307d
                   struct resolv_sortlist_entry e;
d8307d
-                  if (__inet_aton (net, &a))
d8307d
+                  if (__inet_aton_exact (net, &a))
d8307d
                     {
d8307d
                       e.addr = a;
d8307d
                       if (is_sort_mask (separator))
d8307d
@@ -484,7 +489,7 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
d8307d
                             cp++;
d8307d
                           separator = *cp;
d8307d
                           *cp = 0;
d8307d
-                          if (__inet_aton (net, &a))
d8307d
+                          if (__inet_aton_exact (net, &a))
d8307d
                             e.mask = a.s_addr;
d8307d
                           else
d8307d
                             e.mask = net_mask (e.addr);
d8307d
diff --git a/resolv/tst-aton.c b/resolv/tst-aton.c
d8307d
index 08110a007af909ff..eb734d7758d6ed87 100644
d8307d
--- a/resolv/tst-aton.c
d8307d
+++ b/resolv/tst-aton.c
d8307d
@@ -1,11 +1,29 @@
d8307d
+/* Test legacy IPv4 text-to-address function inet_aton.
d8307d
+   Copyright (C) 1998-2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <array_length.h>
d8307d
 #include <stdio.h>
d8307d
 #include <stdint.h>
d8307d
 #include <sys/socket.h>
d8307d
 #include <netinet/in.h>
d8307d
 #include <arpa/inet.h>
d8307d
 
d8307d
-
d8307d
-static struct tests
d8307d
+static const struct tests
d8307d
 {
d8307d
   const char *input;
d8307d
   int valid;
d8307d
@@ -16,6 +34,7 @@ static struct tests
d8307d
   { "-1", 0, 0 },
d8307d
   { "256", 1, 0x00000100 },
d8307d
   { "256.", 0, 0 },
d8307d
+  { "255a", 0, 0 },
d8307d
   { "256a", 0, 0 },
d8307d
   { "0x100", 1, 0x00000100 },
d8307d
   { "0200.0x123456", 1, 0x80123456 },
d8307d
@@ -40,7 +59,12 @@ static struct tests
d8307d
   { "1.2.256.4", 0, 0 },
d8307d
   { "1.2.3.0x100", 0, 0 },
d8307d
   { "323543357756889", 0, 0 },
d8307d
-  { "10.1.2.3.4", 0, 0},
d8307d
+  { "10.1.2.3.4", 0, 0 },
d8307d
+  { "192.0.2.1", 1, 0xc0000201 },
d8307d
+  { "192.0.2.2\nX", 1, 0xc0000202 },
d8307d
+  { "192.0.2.3 Y", 1, 0xc0000203 },
d8307d
+  { "192.0.2.3Z", 0, 0 },
d8307d
+  { "192.000.002.010", 1, 0xc0000208 },
d8307d
 };
d8307d
 
d8307d
 
d8307d
@@ -50,7 +74,7 @@ do_test (void)
d8307d
   int result = 0;
d8307d
   size_t cnt;
d8307d
 
d8307d
-  for (cnt = 0; cnt < sizeof (tests) / sizeof (tests[0]); ++cnt)
d8307d
+  for (cnt = 0; cnt < array_length (tests); ++cnt)
d8307d
     {
d8307d
       struct in_addr addr;
d8307d
 
d8307d
@@ -73,5 +97,4 @@ do_test (void)
d8307d
   return result;
d8307d
 }
d8307d
 
d8307d
-#define TEST_FUNCTION do_test ()
d8307d
-#include "../test-skeleton.c"
d8307d
+#include <support/test-driver.c>
d8307d
diff --git a/resolv/tst-inet_aton_exact.c b/resolv/tst-inet_aton_exact.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..0fdfa3d6aa9aef91
d8307d
--- /dev/null
d8307d
+++ b/resolv/tst-inet_aton_exact.c
d8307d
@@ -0,0 +1,47 @@
d8307d
+/* Test internal legacy IPv4 text-to-address function __inet_aton_exact.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <arpa/inet.h>
d8307d
+#include <support/check.h>
d8307d
+
d8307d
+static int
d8307d
+do_test (void)
d8307d
+{
d8307d
+  struct in_addr addr = { };
d8307d
+
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.1", &addr), 1);
d8307d
+  TEST_COMPARE (ntohl (addr.s_addr), 0xC0000201);
d8307d
+
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.000.002.010", &addr), 1);
d8307d
+  TEST_COMPARE (ntohl (addr.s_addr), 0xC0000208);
d8307d
+  TEST_COMPARE (__inet_aton_exact ("0xC0000234", &addr), 1);
d8307d
+  TEST_COMPARE (ntohl (addr.s_addr), 0xC0000234);
d8307d
+
d8307d
+  /* Trailing content is not accepted.  */
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.2X", &addr), 0);
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.3 Y", &addr), 0);
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.4\nZ", &addr), 0);
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.5\tT", &addr), 0);
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.6 Y", &addr), 0);
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.7\n", &addr), 0);
d8307d
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.8\t", &addr), 0);
d8307d
+
d8307d
+  return 0;
d8307d
+}
d8307d
+
d8307d
+#include <support/test-driver.c>
d8307d
diff --git a/resolv/tst-resolv-nondecimal.c b/resolv/tst-resolv-nondecimal.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..a0df6f332ae8faf7
d8307d
--- /dev/null
d8307d
+++ b/resolv/tst-resolv-nondecimal.c
d8307d
@@ -0,0 +1,139 @@
d8307d
+/* Test name resolution behavior for octal, hexadecimal IPv4 addresses.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <netdb.h>
d8307d
+#include <stdlib.h>
d8307d
+#include <support/check.h>
d8307d
+#include <support/check_nss.h>
d8307d
+#include <support/resolv_test.h>
d8307d
+#include <support/support.h>
d8307d
+
d8307d
+static void
d8307d
+response (const struct resolv_response_context *ctx,
d8307d
+          struct resolv_response_builder *b,
d8307d
+          const char *qname, uint16_t qclass, uint16_t qtype)
d8307d
+{
d8307d
+  /* The tests are not supposed send any DNS queries.  */
d8307d
+  FAIL_EXIT1 ("unexpected DNS query for %s/%d/%d", qname, qclass, qtype);
d8307d
+}
d8307d
+
d8307d
+static void
d8307d
+run_query_addrinfo (const char *query, const char *address)
d8307d
+{
d8307d
+  char *quoted_query = support_quote_string (query);
d8307d
+
d8307d
+  struct addrinfo *ai;
d8307d
+  struct addrinfo hints =
d8307d
+    {
d8307d
+     .ai_socktype = SOCK_STREAM,
d8307d
+     .ai_protocol = IPPROTO_TCP,
d8307d
+    };
d8307d
+
d8307d
+  char *context = xasprintf ("getaddrinfo \"%s\" AF_INET", quoted_query);
d8307d
+  char *expected = xasprintf ("address: STREAM/TCP %s 80\n", address);
d8307d
+  hints.ai_family = AF_INET;
d8307d
+  int ret = getaddrinfo (query, "80", &hints, &ai;;
d8307d
+  check_addrinfo (context, ai, ret, expected);
d8307d
+  if (ret == 0)
d8307d
+    freeaddrinfo (ai);
d8307d
+  free (context);
d8307d
+
d8307d
+  context = xasprintf ("getaddrinfo \"%s\" AF_UNSPEC", quoted_query);
d8307d
+  hints.ai_family = AF_UNSPEC;
d8307d
+  ret = getaddrinfo (query, "80", &hints, &ai;;
d8307d
+  check_addrinfo (context, ai, ret, expected);
d8307d
+  if (ret == 0)
d8307d
+    freeaddrinfo (ai);
d8307d
+  free (expected);
d8307d
+  free (context);
d8307d
+
d8307d
+  context = xasprintf ("getaddrinfo \"%s\" AF_INET6", quoted_query);
d8307d
+  expected = xasprintf ("flags: AI_V4MAPPED\n"
d8307d
+                        "address: STREAM/TCP ::ffff:%s 80\n",
d8307d
+                        address);
d8307d
+  hints.ai_family = AF_INET6;
d8307d
+  hints.ai_flags = AI_V4MAPPED;
d8307d
+  ret = getaddrinfo (query, "80", &hints, &ai;;
d8307d
+  check_addrinfo (context, ai, ret, expected);
d8307d
+  if (ret == 0)
d8307d
+    freeaddrinfo (ai);
d8307d
+  free (expected);
d8307d
+  free (context);
d8307d
+
d8307d
+  free (quoted_query);
d8307d
+}
d8307d
+
d8307d
+static void
d8307d
+run_query (const char *query, const char *address)
d8307d
+{
d8307d
+  char *quoted_query = support_quote_string (query);
d8307d
+  char *context = xasprintf ("gethostbyname (\"%s\")", quoted_query);
d8307d
+  char *expected = xasprintf ("name: %s\n"
d8307d
+                              "address: %s\n", query, address);
d8307d
+  check_hostent (context, gethostbyname (query), expected);
d8307d
+  free (context);
d8307d
+
d8307d
+  context = xasprintf ("gethostbyname_r \"%s\"", quoted_query);
d8307d
+  struct hostent storage;
d8307d
+  char buf[4096];
d8307d
+  struct hostent *e = NULL;
d8307d
+  TEST_COMPARE (gethostbyname_r (query, &storage, buf, sizeof (buf),
d8307d
+                                 &e, &h_errno), 0);
d8307d
+  check_hostent (context, e, expected);
d8307d
+  free (context);
d8307d
+
d8307d
+  context = xasprintf ("gethostbyname2 (\"%s\", AF_INET)", quoted_query);
d8307d
+  check_hostent (context, gethostbyname2 (query, AF_INET), expected);
d8307d
+  free (context);
d8307d
+
d8307d
+  context = xasprintf ("gethostbyname2_r \"%s\" AF_INET", quoted_query);
d8307d
+  e = NULL;
d8307d
+  TEST_COMPARE (gethostbyname2_r (query, AF_INET, &storage, buf, sizeof (buf),
d8307d
+                                  &e, &h_errno), 0);
d8307d
+  check_hostent (context, e, expected);
d8307d
+  free (context);
d8307d
+  free (expected);
d8307d
+
d8307d
+  free (quoted_query);
d8307d
+
d8307d
+  /* The gethostbyname tests are always valid for getaddrinfo, but not
d8307d
+     vice versa.  */
d8307d
+  run_query_addrinfo (query, address);
d8307d
+}
d8307d
+
d8307d
+static int
d8307d
+do_test (void)
d8307d
+{
d8307d
+  struct resolv_test *aux = resolv_test_start
d8307d
+    ((struct resolv_redirect_config)
d8307d
+     {
d8307d
+       .response_callback = response,
d8307d
+     });
d8307d
+
d8307d
+  run_query ("192.000.002.010", "192.0.2.8");
d8307d
+
d8307d
+  /* Hexadecimal numbers are not accepted by gethostbyname.  */
d8307d
+  run_query_addrinfo ("0xc0000210", "192.0.2.16");
d8307d
+  run_query_addrinfo ("192.0x234", "192.0.2.52");
d8307d
+
d8307d
+  resolv_test_end (aux);
d8307d
+
d8307d
+  return 0;
d8307d
+}
d8307d
+
d8307d
+#include <support/test-driver.c>
d8307d
diff --git a/resolv/tst-resolv-trailing.c b/resolv/tst-resolv-trailing.c
d8307d
new file mode 100644
d8307d
index 0000000000000000..7504bdae572ed8d0
d8307d
--- /dev/null
d8307d
+++ b/resolv/tst-resolv-trailing.c
d8307d
@@ -0,0 +1,136 @@
d8307d
+/* Test name resolution behavior with trailing characters.
d8307d
+   Copyright (C) 2019 Free Software Foundation, Inc.
d8307d
+   This file is part of the GNU C Library.
d8307d
+
d8307d
+   The GNU C Library is free software; you can redistribute it and/or
d8307d
+   modify it under the terms of the GNU Lesser General Public
d8307d
+   License as published by the Free Software Foundation; either
d8307d
+   version 2.1 of the License, or (at your option) any later version.
d8307d
+
d8307d
+   The GNU C Library is distributed in the hope that it will be useful,
d8307d
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
d8307d
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
d8307d
+   Lesser General Public License for more details.
d8307d
+
d8307d
+   You should have received a copy of the GNU Lesser General Public
d8307d
+   License along with the GNU C Library; if not, see
d8307d
+   <http://www.gnu.org/licenses/>.  */
d8307d
+
d8307d
+#include <array_length.h>
d8307d
+#include <netdb.h>
d8307d
+#include <support/check.h>
d8307d
+#include <support/check_nss.h>
d8307d
+#include <support/resolv_test.h>
d8307d
+#include <support/support.h>
d8307d
+
d8307d
+static void
d8307d
+response (const struct resolv_response_context *ctx,
d8307d
+          struct resolv_response_builder *b,
d8307d
+          const char *qname, uint16_t qclass, uint16_t qtype)
d8307d
+{
d8307d
+  /* The tests are not supposed send any DNS queries.  */
d8307d
+  FAIL_EXIT1 ("unexpected DNS query for %s/%d/%d", qname, qclass, qtype);
d8307d
+}
d8307d
+
d8307d
+static int
d8307d
+do_test (void)
d8307d
+{
d8307d
+  struct resolv_test *aux = resolv_test_start
d8307d
+    ((struct resolv_redirect_config)
d8307d
+     {
d8307d
+       .response_callback = response,
d8307d
+     });
d8307d
+
d8307d
+  static const char *const queries[] =
d8307d
+    {
d8307d
+     "192.0.2.1 ",
d8307d
+     "192.0.2.2\t",
d8307d
+     "192.0.2.3\n",
d8307d
+     "192.0.2.4 X",
d8307d
+     "192.0.2.5\tY",
d8307d
+     "192.0.2.6\nZ",
d8307d
+     "192.0.2. ",
d8307d
+     "192.0.2.\t",
d8307d
+     "192.0.2.\n",
d8307d
+     "192.0.2. X",
d8307d
+     "192.0.2.\tY",
d8307d
+     "192.0.2.\nZ",
d8307d
+     "2001:db8::1 ",
d8307d
+     "2001:db8::2\t",
d8307d
+     "2001:db8::3\n",
d8307d
+     "2001:db8::4 X",
d8307d
+     "2001:db8::5\tY",
d8307d
+     "2001:db8::6\nZ",
d8307d
+    };
d8307d
+  for (size_t query_idx = 0; query_idx < array_length (queries); ++query_idx)
d8307d
+    {
d8307d
+      const char *query = queries[query_idx];
d8307d
+      struct hostent storage;
d8307d
+      char buf[4096];
d8307d
+      struct hostent *e;
d8307d
+
d8307d
+      h_errno = 0;
d8307d
+      TEST_VERIFY (gethostbyname (query) == NULL);
d8307d
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
d8307d
+
d8307d
+      h_errno = 0;
d8307d
+      e = NULL;
d8307d
+      TEST_COMPARE (gethostbyname_r (query, &storage, buf, sizeof (buf),
d8307d
+                                     &e, &h_errno), 0);
d8307d
+      TEST_VERIFY (e == NULL);
d8307d
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
d8307d
+
d8307d
+      h_errno = 0;
d8307d
+      TEST_VERIFY (gethostbyname2 (query, AF_INET) == NULL);
d8307d
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
d8307d
+
d8307d
+      h_errno = 0;
d8307d
+      e = NULL;
d8307d
+      TEST_COMPARE (gethostbyname2_r (query, AF_INET,
d8307d
+                                      &storage, buf, sizeof (buf),
d8307d
+                                     &e, &h_errno), 0);
d8307d
+      TEST_VERIFY (e == NULL);
d8307d
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
d8307d
+
d8307d
+      h_errno = 0;
d8307d
+      TEST_VERIFY (gethostbyname2 (query, AF_INET6) == NULL);
d8307d
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
d8307d
+
d8307d
+      h_errno = 0;
d8307d
+      e = NULL;
d8307d
+      TEST_COMPARE (gethostbyname2_r (query, AF_INET6,
d8307d
+                                      &storage, buf, sizeof (buf),
d8307d
+                                     &e, &h_errno), 0);
d8307d
+      TEST_VERIFY (e == NULL);
d8307d
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
d8307d
+
d8307d
+      static const int gai_flags[] =
d8307d
+        {
d8307d
+         0,
d8307d
+         AI_ADDRCONFIG,
d8307d
+         AI_NUMERICHOST,
d8307d
+         AI_IDN,
d8307d
+         AI_IDN | AI_NUMERICHOST,
d8307d
+         AI_V4MAPPED,
d8307d
+         AI_V4MAPPED | AI_NUMERICHOST,
d8307d
+        };
d8307d
+      for (size_t gai_flags_idx; gai_flags_idx < array_length (gai_flags);
d8307d
+             ++gai_flags_idx)
d8307d
+        {
d8307d
+          struct addrinfo hints = { .ai_flags = gai_flags[gai_flags_idx], };
d8307d
+          struct addrinfo *ai;
d8307d
+          hints.ai_family = AF_INET;
d8307d
+          TEST_COMPARE (getaddrinfo (query, "80", &hints, &ai), EAI_NONAME);
d8307d
+          hints.ai_family = AF_INET6;
d8307d
+          TEST_COMPARE (getaddrinfo (query, "80", &hints, &ai), EAI_NONAME);
d8307d
+          hints.ai_family = AF_UNSPEC;
d8307d
+          TEST_COMPARE (getaddrinfo (query, "80", &hints, &ai), EAI_NONAME);
d8307d
+        }
d8307d
+    };
d8307d
+
d8307d
+  resolv_test_end (aux);
d8307d
+
d8307d
+  return 0;
d8307d
+}
d8307d
+
d8307d
+#include <support/test-driver.c>
d8307d
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
d8307d
index 00e0d94a8f5bb30d..6a5805c9e63a257c 100644
d8307d
--- a/sysdeps/posix/getaddrinfo.c
d8307d
+++ b/sysdeps/posix/getaddrinfo.c
d8307d
@@ -488,7 +488,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
d8307d
 	  malloc_name = true;
d8307d
 	}
d8307d
 
d8307d
-      if (__inet_aton (name, (struct in_addr *) at->addr) != 0)
d8307d
+      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
d8307d
 	{
d8307d
 	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
d8307d
 	    at->family = AF_INET;