about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog33
-rw-r--r--NEWS4
-rw-r--r--include/arpa/inet.h6
-rw-r--r--nscd/gai.c1
-rw-r--r--nscd/gethstbynm3_r.c2
-rw-r--r--nss/digits_dots.c3
-rw-r--r--resolv/Makefile7
-rw-r--r--resolv/Versions1
-rw-r--r--resolv/inet_addr.c62
-rw-r--r--resolv/res_init.c17
-rw-r--r--resolv/tst-aton.c35
-rw-r--r--resolv/tst-inet_aton_exact.c47
-rw-r--r--resolv/tst-resolv-nondecimal.c139
-rw-r--r--resolv/tst-resolv-trailing.c136
-rw-r--r--sysdeps/posix/getaddrinfo.c2
15 files changed, 455 insertions, 40 deletions
diff --git a/ChangeLog b/ChangeLog
index 834fca6fdc..bbf38b690a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,36 @@
+2019-01-21  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #20018]
+	CVE-2016-10739
+	resolv: Reject trailing characters in host names
+	* include/arpa/inet.h (__inet_aton_exact): Declare.
+	(inet_aton): Remove hidden prototype.  No longer used internally.
+	* nscd/gai.c (__inet_aton): Do not define.
+	* nscd/gethstbynm3_r.c (__inet_aton): Likewise.
+	* nss/digits_dots.c (__inet_aton): Likewise.
+	(__nss_hostname_digits_dots_context): Call __inet_aton_exact.
+	* resolv/Makefile (tests-internal): Add tst-inet_aton_exact.
+	(tests): Add tst-resolv-nondecimal, tst-resolv-trailing.
+	(tst-resolv-nondecimal): Link with libresolv.so and libpthread.
+	(tst-resolv-trailing): Likewise.
+	* resolv/Versions (GLIBC_PRIVATE): Export __inet_aton_exact from
+	libc.
+	* resolv/inet_addr.c (inet_aton_end): Remame from __inet_aton.
+	Make static.  Add endp parameter.
+	(__inet_aton_exact): New function.
+	(__inet_aton_ignore_trailing): New function, aliased to inet_aton.
+	(__inet_addr): Call inet_aton_end.
+	* resolv/res_init.c (res_vinit_1): Truncate nameserver for IPv4,
+	not just IPv6.  Call __inet_aton_exact.
+	* resolv/tst-aton.c: Switch to <support/test-driver.c>.
+	(tests): Make const.  Add additional test cases with trailing
+	characters.
+	(do_test): Use array_length.
+	* resolv/tst-inet_aton_exact.c: New file.
+	* resolv/tst-resolv-trailing.c: Likewise.
+	* resolv/tst-resolv-nondecimal.c: Likewise.
+	* sysdeps/posix/getaddrinfo.c (gaih_inet): Call __inet_aton_exact.
+
 2019-01-21  H.J. Lu  <hongjiu.lu@intel.com>
 
 	[BZ# 24097]
diff --git a/NEWS b/NEWS
index 7b3af113bf..d863d80f3a 100644
--- a/NEWS
+++ b/NEWS
@@ -107,6 +107,10 @@ Security related changes:
   in the assembly string/memory functions would cause a buffer overflow.
   Reported by H.J. Lu.
 
+  CVE-2016-10739: The getaddrinfo function could successfully parse IPv4
+  addresses with arbitrary trailing characters, potentially leading to data
+  or command injection issues in applications.
+
 The following bugs are resolved with this release:
 
   [The release manager will add the list generated by
diff --git a/include/arpa/inet.h b/include/arpa/inet.h
index c3f28f2baa..19aec74275 100644
--- a/include/arpa/inet.h
+++ b/include/arpa/inet.h
@@ -1,10 +1,10 @@
 #include <inet/arpa/inet.h>
 
 #ifndef _ISOMAC
-extern int __inet_aton (const char *__cp, struct in_addr *__inp);
-libc_hidden_proto (__inet_aton)
+/* Variant of inet_aton which rejects trailing garbage.  */
+extern int __inet_aton_exact (const char *__cp, struct in_addr *__inp);
+libc_hidden_proto (__inet_aton_exact)
 
-libc_hidden_proto (inet_aton)
 libc_hidden_proto (inet_ntop)
 libc_hidden_proto (inet_pton)
 extern __typeof (inet_pton) __inet_pton;
diff --git a/nscd/gai.c b/nscd/gai.c
index fd4e8e092f..801d304d47 100644
--- a/nscd/gai.c
+++ b/nscd/gai.c
@@ -19,7 +19,6 @@
 
 /* This file uses the getaddrinfo code but it compiles it without NSCD
    support.  We just need a few symbol renames.  */
-#define __inet_aton inet_aton
 #define __ioctl ioctl
 #define __getsockname getsockname
 #define __socket socket
diff --git a/nscd/gethstbynm3_r.c b/nscd/gethstbynm3_r.c
index 9f70a86a58..ff594b6d27 100644
--- a/nscd/gethstbynm3_r.c
+++ b/nscd/gethstbynm3_r.c
@@ -38,8 +38,6 @@
 #define HAVE_LOOKUP_BUFFER	1
 #define HAVE_AF			1
 
-#define __inet_aton inet_aton
-
 /* We are nscd, so we don't want to be talking to ourselves.  */
 #undef	USE_NSCD
 
diff --git a/nss/digits_dots.c b/nss/digits_dots.c
index 95015896a4..440d9955d2 100644
--- a/nss/digits_dots.c
+++ b/nss/digits_dots.c
@@ -29,7 +29,6 @@
 #include "nsswitch.h"
 
 #ifdef USE_NSCD
-# define inet_aton __inet_aton
 # include <nscd/nscd_proto.h>
 #endif
 
@@ -160,7 +159,7 @@ __nss_hostname_digits_dots_context (struct resolv_context *ctx,
 		     255.255.255.255?  The test below will succeed
 		     spuriously... ???  */
 		  if (af == AF_INET)
-		    ok = __inet_aton (name, (struct in_addr *) host_addr);
+		    ok = __inet_aton_exact (name, (struct in_addr *) host_addr);
 		  else
 		    {
 		      assert (af == AF_INET6);
diff --git a/resolv/Makefile b/resolv/Makefile
index 450e171b01..8f22e6a154 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -34,6 +34,9 @@ routines := herror inet_addr inet_ntop inet_pton nsap_addr res_init \
 tests = tst-aton tst-leaks tst-inet_ntop
 xtests = tst-leaks2
 
+tests-internal += tst-inet_aton_exact
+
+
 generate := mtrace-tst-leaks.out tst-leaks.mtrace tst-leaks2.mtrace
 
 extra-libs := libresolv libnss_dns
@@ -54,8 +57,10 @@ tests += \
   tst-resolv-binary \
   tst-resolv-edns \
   tst-resolv-network \
+  tst-resolv-nondecimal \
   tst-resolv-res_init-multi \
   tst-resolv-search \
+  tst-resolv-trailing \
 
 # These tests need libdl.
 ifeq (yes,$(build-shared))
@@ -190,9 +195,11 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \
   $(shared-thread-library)
 $(objpfx)tst-resolv-res_init-thread: $(libdl) $(objpfx)libresolv.so \
   $(shared-thread-library)
+$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-rotate: $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library)
+$(objpfx)tst-resolv-trailing: $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-threads: \
   $(libdl) $(objpfx)libresolv.so $(shared-thread-library)
 $(objpfx)tst-resolv-canonname: \
diff --git a/resolv/Versions b/resolv/Versions
index b05778d965..9a82704af7 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -27,6 +27,7 @@ libc {
     __h_errno; __resp;
 
     __res_iclose;
+    __inet_aton_exact;
     __inet_pton_length;
     __resolv_context_get;
     __resolv_context_get_preinit;
diff --git a/resolv/inet_addr.c b/resolv/inet_addr.c
index 32f58b0e13..41b6166a5b 100644
--- a/resolv/inet_addr.c
+++ b/resolv/inet_addr.c
@@ -96,26 +96,14 @@
 #include <limits.h>
 #include <errno.h>
 
-/* ASCII IPv4 Internet address interpretation routine.  The value
-   returned is in network order.  */
-in_addr_t
-__inet_addr (const char *cp)
-{
-  struct in_addr val;
-
-  if (__inet_aton (cp, &val))
-    return val.s_addr;
-  return INADDR_NONE;
-}
-weak_alias (__inet_addr, inet_addr)
-
 /* Check whether "cp" is a valid ASCII representation of an IPv4
    Internet address and convert it to a binary address.  Returns 1 if
    the address is valid, 0 if not.  This replaces inet_addr, the
    return value from which cannot distinguish between failure and a
-   local broadcast address.  */
-int
-__inet_aton (const char *cp, struct in_addr *addr)
+   local broadcast address.  Write a pointer to the first
+   non-converted character to *endp.  */
+static int
+inet_aton_end (const char *cp, struct in_addr *addr, const char **endp)
 {
   static const in_addr_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
   in_addr_t val;
@@ -180,6 +168,7 @@ __inet_aton (const char *cp, struct in_addr *addr)
 
   if (addr != NULL)
     addr->s_addr = res.word | htonl (val);
+  *endp = cp;
 
   __set_errno (saved_errno);
   return 1;
@@ -188,6 +177,41 @@ __inet_aton (const char *cp, struct in_addr *addr)
   __set_errno (saved_errno);
   return 0;
 }
-weak_alias (__inet_aton, inet_aton)
-libc_hidden_def (__inet_aton)
-libc_hidden_weak (inet_aton)
+
+int
+__inet_aton_exact (const char *cp, struct in_addr *addr)
+{
+  struct in_addr val;
+  const char *endp;
+  /* Check that inet_aton_end parsed the entire string.  */
+  if (inet_aton_end (cp, &val, &endp) != 0 && *endp == 0)
+    {
+      *addr = val;
+      return 1;
+    }
+  else
+    return 0;
+}
+libc_hidden_def (__inet_aton_exact)
+
+/* inet_aton ignores trailing garbage.  */
+int
+__inet_aton_ignore_trailing (const char *cp, struct in_addr *addr)
+{
+  const char *endp;
+  return  inet_aton_end (cp, addr, &endp);
+}
+weak_alias (__inet_aton_ignore_trailing, inet_aton)
+
+/* ASCII IPv4 Internet address interpretation routine.  The value
+   returned is in network order.  */
+in_addr_t
+__inet_addr (const char *cp)
+{
+  struct in_addr val;
+  const char *endp;
+  if (inet_aton_end (cp, &val, &endp))
+    return val.s_addr;
+  return INADDR_NONE;
+}
+weak_alias (__inet_addr, inet_addr)
diff --git a/resolv/res_init.c b/resolv/res_init.c
index 58c563898e..265e3cc6e3 100644
--- a/resolv/res_init.c
+++ b/resolv/res_init.c
@@ -399,8 +399,16 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
               cp = parser->buffer + sizeof ("nameserver") - 1;
               while (*cp == ' ' || *cp == '\t')
                 cp++;
+
+              /* Ignore trailing contents on the name server line.  */
+              {
+                char *el;
+                if ((el = strpbrk (cp, " \t\n")) != NULL)
+                  *el = '\0';
+              }
+
               struct sockaddr *sa;
-              if ((*cp != '\0') && (*cp != '\n') && __inet_aton (cp, &a))
+              if ((*cp != '\0') && (*cp != '\n') && __inet_aton_exact (cp, &a))
                 {
                   sa = allocate_address_v4 (a, NAMESERVER_PORT);
                   if (sa == NULL)
@@ -410,9 +418,6 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
                 {
                   struct in6_addr a6;
                   char *el;
-
-                  if ((el = strpbrk (cp, " \t\n")) != NULL)
-                    *el = '\0';
                   if ((el = strchr (cp, SCOPE_DELIMITER)) != NULL)
                     *el = '\0';
                   if ((*cp != '\0') && (__inet_pton (AF_INET6, cp, &a6) > 0))
@@ -472,7 +477,7 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
                   char separator = *cp;
                   *cp = 0;
                   struct resolv_sortlist_entry e;
-                  if (__inet_aton (net, &a))
+                  if (__inet_aton_exact (net, &a))
                     {
                       e.addr = a;
                       if (is_sort_mask (separator))
@@ -484,7 +489,7 @@ res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
                             cp++;
                           separator = *cp;
                           *cp = 0;
-                          if (__inet_aton (net, &a))
+                          if (__inet_aton_exact (net, &a))
                             e.mask = a.s_addr;
                           else
                             e.mask = net_mask (e.addr);
diff --git a/resolv/tst-aton.c b/resolv/tst-aton.c
index 08110a007a..eb734d7758 100644
--- a/resolv/tst-aton.c
+++ b/resolv/tst-aton.c
@@ -1,11 +1,29 @@
+/* Test legacy IPv4 text-to-address function inet_aton.
+   Copyright (C) 1998-2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-
-static struct tests
+static const struct tests
 {
   const char *input;
   int valid;
@@ -16,6 +34,7 @@ static struct tests
   { "-1", 0, 0 },
   { "256", 1, 0x00000100 },
   { "256.", 0, 0 },
+  { "255a", 0, 0 },
   { "256a", 0, 0 },
   { "0x100", 1, 0x00000100 },
   { "0200.0x123456", 1, 0x80123456 },
@@ -40,7 +59,12 @@ static struct tests
   { "1.2.256.4", 0, 0 },
   { "1.2.3.0x100", 0, 0 },
   { "323543357756889", 0, 0 },
-  { "10.1.2.3.4", 0, 0},
+  { "10.1.2.3.4", 0, 0 },
+  { "192.0.2.1", 1, 0xc0000201 },
+  { "192.0.2.2\nX", 1, 0xc0000202 },
+  { "192.0.2.3 Y", 1, 0xc0000203 },
+  { "192.0.2.3Z", 0, 0 },
+  { "192.000.002.010", 1, 0xc0000208 },
 };
 
 
@@ -50,7 +74,7 @@ do_test (void)
   int result = 0;
   size_t cnt;
 
-  for (cnt = 0; cnt < sizeof (tests) / sizeof (tests[0]); ++cnt)
+  for (cnt = 0; cnt < array_length (tests); ++cnt)
     {
       struct in_addr addr;
 
@@ -73,5 +97,4 @@ do_test (void)
   return result;
 }
 
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include <support/test-driver.c>
diff --git a/resolv/tst-inet_aton_exact.c b/resolv/tst-inet_aton_exact.c
new file mode 100644
index 0000000000..0fdfa3d6aa
--- /dev/null
+++ b/resolv/tst-inet_aton_exact.c
@@ -0,0 +1,47 @@
+/* Test internal legacy IPv4 text-to-address function __inet_aton_exact.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <arpa/inet.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  struct in_addr addr = { };
+
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.1", &addr), 1);
+  TEST_COMPARE (ntohl (addr.s_addr), 0xC0000201);
+
+  TEST_COMPARE (__inet_aton_exact ("192.000.002.010", &addr), 1);
+  TEST_COMPARE (ntohl (addr.s_addr), 0xC0000208);
+  TEST_COMPARE (__inet_aton_exact ("0xC0000234", &addr), 1);
+  TEST_COMPARE (ntohl (addr.s_addr), 0xC0000234);
+
+  /* Trailing content is not accepted.  */
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.2X", &addr), 0);
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.3 Y", &addr), 0);
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.4\nZ", &addr), 0);
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.5\tT", &addr), 0);
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.6 Y", &addr), 0);
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.7\n", &addr), 0);
+  TEST_COMPARE (__inet_aton_exact ("192.0.2.8\t", &addr), 0);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/resolv/tst-resolv-nondecimal.c b/resolv/tst-resolv-nondecimal.c
new file mode 100644
index 0000000000..a0df6f332a
--- /dev/null
+++ b/resolv/tst-resolv-nondecimal.c
@@ -0,0 +1,139 @@
+/* Test name resolution behavior for octal, hexadecimal IPv4 addresses.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+
+static void
+response (const struct resolv_response_context *ctx,
+          struct resolv_response_builder *b,
+          const char *qname, uint16_t qclass, uint16_t qtype)
+{
+  /* The tests are not supposed send any DNS queries.  */
+  FAIL_EXIT1 ("unexpected DNS query for %s/%d/%d", qname, qclass, qtype);
+}
+
+static void
+run_query_addrinfo (const char *query, const char *address)
+{
+  char *quoted_query = support_quote_string (query);
+
+  struct addrinfo *ai;
+  struct addrinfo hints =
+    {
+     .ai_socktype = SOCK_STREAM,
+     .ai_protocol = IPPROTO_TCP,
+    };
+
+  char *context = xasprintf ("getaddrinfo \"%s\" AF_INET", quoted_query);
+  char *expected = xasprintf ("address: STREAM/TCP %s 80\n", address);
+  hints.ai_family = AF_INET;
+  int ret = getaddrinfo (query, "80", &hints, &ai);
+  check_addrinfo (context, ai, ret, expected);
+  if (ret == 0)
+    freeaddrinfo (ai);
+  free (context);
+
+  context = xasprintf ("getaddrinfo \"%s\" AF_UNSPEC", quoted_query);
+  hints.ai_family = AF_UNSPEC;
+  ret = getaddrinfo (query, "80", &hints, &ai);
+  check_addrinfo (context, ai, ret, expected);
+  if (ret == 0)
+    freeaddrinfo (ai);
+  free (expected);
+  free (context);
+
+  context = xasprintf ("getaddrinfo \"%s\" AF_INET6", quoted_query);
+  expected = xasprintf ("flags: AI_V4MAPPED\n"
+                        "address: STREAM/TCP ::ffff:%s 80\n",
+                        address);
+  hints.ai_family = AF_INET6;
+  hints.ai_flags = AI_V4MAPPED;
+  ret = getaddrinfo (query, "80", &hints, &ai);
+  check_addrinfo (context, ai, ret, expected);
+  if (ret == 0)
+    freeaddrinfo (ai);
+  free (expected);
+  free (context);
+
+  free (quoted_query);
+}
+
+static void
+run_query (const char *query, const char *address)
+{
+  char *quoted_query = support_quote_string (query);
+  char *context = xasprintf ("gethostbyname (\"%s\")", quoted_query);
+  char *expected = xasprintf ("name: %s\n"
+                              "address: %s\n", query, address);
+  check_hostent (context, gethostbyname (query), expected);
+  free (context);
+
+  context = xasprintf ("gethostbyname_r \"%s\"", quoted_query);
+  struct hostent storage;
+  char buf[4096];
+  struct hostent *e = NULL;
+  TEST_COMPARE (gethostbyname_r (query, &storage, buf, sizeof (buf),
+                                 &e, &h_errno), 0);
+  check_hostent (context, e, expected);
+  free (context);
+
+  context = xasprintf ("gethostbyname2 (\"%s\", AF_INET)", quoted_query);
+  check_hostent (context, gethostbyname2 (query, AF_INET), expected);
+  free (context);
+
+  context = xasprintf ("gethostbyname2_r \"%s\" AF_INET", quoted_query);
+  e = NULL;
+  TEST_COMPARE (gethostbyname2_r (query, AF_INET, &storage, buf, sizeof (buf),
+                                  &e, &h_errno), 0);
+  check_hostent (context, e, expected);
+  free (context);
+  free (expected);
+
+  free (quoted_query);
+
+  /* The gethostbyname tests are always valid for getaddrinfo, but not
+     vice versa.  */
+  run_query_addrinfo (query, address);
+}
+
+static int
+do_test (void)
+{
+  struct resolv_test *aux = resolv_test_start
+    ((struct resolv_redirect_config)
+     {
+       .response_callback = response,
+     });
+
+  run_query ("192.000.002.010", "192.0.2.8");
+
+  /* Hexadecimal numbers are not accepted by gethostbyname.  */
+  run_query_addrinfo ("0xc0000210", "192.0.2.16");
+  run_query_addrinfo ("192.0x234", "192.0.2.52");
+
+  resolv_test_end (aux);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/resolv/tst-resolv-trailing.c b/resolv/tst-resolv-trailing.c
new file mode 100644
index 0000000000..7504bdae57
--- /dev/null
+++ b/resolv/tst-resolv-trailing.c
@@ -0,0 +1,136 @@
+/* Test name resolution behavior with trailing characters.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <array_length.h>
+#include <netdb.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+#include <support/resolv_test.h>
+#include <support/support.h>
+
+static void
+response (const struct resolv_response_context *ctx,
+          struct resolv_response_builder *b,
+          const char *qname, uint16_t qclass, uint16_t qtype)
+{
+  /* The tests are not supposed send any DNS queries.  */
+  FAIL_EXIT1 ("unexpected DNS query for %s/%d/%d", qname, qclass, qtype);
+}
+
+static int
+do_test (void)
+{
+  struct resolv_test *aux = resolv_test_start
+    ((struct resolv_redirect_config)
+     {
+       .response_callback = response,
+     });
+
+  static const char *const queries[] =
+    {
+     "192.0.2.1 ",
+     "192.0.2.2\t",
+     "192.0.2.3\n",
+     "192.0.2.4 X",
+     "192.0.2.5\tY",
+     "192.0.2.6\nZ",
+     "192.0.2. ",
+     "192.0.2.\t",
+     "192.0.2.\n",
+     "192.0.2. X",
+     "192.0.2.\tY",
+     "192.0.2.\nZ",
+     "2001:db8::1 ",
+     "2001:db8::2\t",
+     "2001:db8::3\n",
+     "2001:db8::4 X",
+     "2001:db8::5\tY",
+     "2001:db8::6\nZ",
+    };
+  for (size_t query_idx = 0; query_idx < array_length (queries); ++query_idx)
+    {
+      const char *query = queries[query_idx];
+      struct hostent storage;
+      char buf[4096];
+      struct hostent *e;
+
+      h_errno = 0;
+      TEST_VERIFY (gethostbyname (query) == NULL);
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
+
+      h_errno = 0;
+      e = NULL;
+      TEST_COMPARE (gethostbyname_r (query, &storage, buf, sizeof (buf),
+                                     &e, &h_errno), 0);
+      TEST_VERIFY (e == NULL);
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
+
+      h_errno = 0;
+      TEST_VERIFY (gethostbyname2 (query, AF_INET) == NULL);
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
+
+      h_errno = 0;
+      e = NULL;
+      TEST_COMPARE (gethostbyname2_r (query, AF_INET,
+                                      &storage, buf, sizeof (buf),
+                                     &e, &h_errno), 0);
+      TEST_VERIFY (e == NULL);
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
+
+      h_errno = 0;
+      TEST_VERIFY (gethostbyname2 (query, AF_INET6) == NULL);
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
+
+      h_errno = 0;
+      e = NULL;
+      TEST_COMPARE (gethostbyname2_r (query, AF_INET6,
+                                      &storage, buf, sizeof (buf),
+                                     &e, &h_errno), 0);
+      TEST_VERIFY (e == NULL);
+      TEST_COMPARE (h_errno, HOST_NOT_FOUND);
+
+      static const int gai_flags[] =
+        {
+         0,
+         AI_ADDRCONFIG,
+         AI_NUMERICHOST,
+         AI_IDN,
+         AI_IDN | AI_NUMERICHOST,
+         AI_V4MAPPED,
+         AI_V4MAPPED | AI_NUMERICHOST,
+        };
+      for (size_t gai_flags_idx; gai_flags_idx < array_length (gai_flags);
+             ++gai_flags_idx)
+        {
+          struct addrinfo hints = { .ai_flags = gai_flags[gai_flags_idx], };
+          struct addrinfo *ai;
+          hints.ai_family = AF_INET;
+          TEST_COMPARE (getaddrinfo (query, "80", &hints, &ai), EAI_NONAME);
+          hints.ai_family = AF_INET6;
+          TEST_COMPARE (getaddrinfo (query, "80", &hints, &ai), EAI_NONAME);
+          hints.ai_family = AF_UNSPEC;
+          TEST_COMPARE (getaddrinfo (query, "80", &hints, &ai), EAI_NONAME);
+        }
+    };
+
+  resolv_test_end (aux);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index aa69eb7e54..aa054b620f 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -488,7 +488,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
 	  malloc_name = true;
 	}
 
-      if (__inet_aton (name, (struct in_addr *) at->addr) != 0)
+      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
 	{
 	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
 	    at->family = AF_INET;