diff mbox

getaddrinfo: Do not make copy the name for IPv6 scope ID extraction

Message ID 20170515154133.26BF34033AD1D@oldenburg.str.redhat.com
State New
Headers show

Commit Message

Florian Weimer May 15, 2017, 3:41 p.m. UTC
2017-05-15  Florian Weimer  <fweimer@redhat.com>

	* sysdeps/posix/getaddrinfo.c (gaih_inet): Use __inet_pton_length
	instead of inet_pton and a copy of the name.
	* resolv/resolv-internal.h (__inet_pton_length): Declare.
	* resolv/inet_pton (__inet_pton_length): Rename from __inet_pton.
	Add length argument.
	(__inet_pton): New function.
	(inet_pton4): Add length argument.
	(hex_digit_value): New function.
	(inet_pton6): Add length argument.  Call hex_digit_value.  Use
	memmove and memset to fill :: gap.
	* resolv/Makefile (tests, tsts-static, tests-internal): Add
	tst-inet_pton.
	* resolv/tst-inet_pton.c: New file.
	* support/Makefile (libsupport-routines): Add xmprotect.
	* support/xunistd.h (xmprotect): Declare.
	* support/xmprotect.c: New file.
	* inet/tst-inet6_scopeid_pton.c: Convert to support/test-driver.c
	(call_gai, check_ai): New functions.
	(expect_failure): Add logging.  Use call_gai for getaddrinfo
	testing.  Avoid process termination on first error.
	(expect_success): Likewise.  Check results with check_ai.
diff mbox

Patch

diff --git a/inet/tst-inet6_scopeid_pton.c b/inet/tst-inet6_scopeid_pton.c
index 2178c5b..a1bafa9 100644
--- a/inet/tst-inet6_scopeid_pton.c
+++ b/inet/tst-inet6_scopeid_pton.c
@@ -1,4 +1,4 @@ 
-/* Tests for __inet6_scopeid_pton.
+/* Tests for __inet6_scopeid_pton and IPv6 scopes in getaddrinfo.
    Copyright (C) 2016-2017 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -20,8 +20,13 @@ 
 #include <inttypes.h>
 #include <net-internal.h>
 #include <net/if.h>
+#include <netdb.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
 
 /* An interface which is known to the system.  */
 static const char *interface_name;
@@ -45,30 +50,103 @@  from_string (const char *address)
 {
   struct in6_addr addr;
   if (inet_pton (AF_INET6, address, &addr) != 1)
-    {
-      printf ("error: inet_pton (\"%s\") failed\n", address);
-      exit (1);
-    }
+    FAIL_EXIT1 ("inet_pton (\"%s\")", address);
   return addr;
 }
 
+/* Invoke getaddrinfo to parse ADDRESS%SCOPE.  Return true if
+   getaddrinfo was successful.  */
+static bool
+call_gai (int family, const char *address, const char *scope,
+          struct sockaddr_in6 *result)
+{
+  struct addrinfo hints =
+    {
+      .ai_family = family,
+      .ai_flags = AI_NUMERICHOST,
+      .ai_socktype = SOCK_DGRAM,
+      .ai_protocol = IPPROTO_UDP,
+    };
+  char *fulladdr = xasprintf ("%s%%%s", address, scope);
+  struct addrinfo *ai = NULL;
+  int ret = getaddrinfo (fulladdr, NULL, &hints, &ai);
+  if (ret == EAI_ADDRFAMILY || ret == EAI_NONAME)
+    {
+      if (test_verbose > 0)
+        printf ("info: getaddrinfo (\"%s\"): %s (%d)\n",
+                fulladdr, gai_strerror (ret), ret);
+      free (fulladdr);
+      return false;
+    }
+  if (ret != 0)
+    FAIL_EXIT1 ("getaddrinfo (\"%s\"): %s (%d)\n",
+                fulladdr, gai_strerror (ret), ret);
+  TEST_VERIFY_EXIT (ai != NULL);
+  TEST_VERIFY_EXIT (ai->ai_addrlen == sizeof (*result));
+  TEST_VERIFY (ai->ai_family == AF_INET6);
+  TEST_VERIFY (ai->ai_next == NULL);
+  memcpy (result, ai->ai_addr, sizeof (*result));
+  free (fulladdr);
+  freeaddrinfo (ai);
+  return true;
+}
+
+/* Verify that a successful call to getaddrinfo returned the expected
+   scope data.  */
+static void
+check_ai (const char *what, const char *addr_string, const char *scope_string,
+          const struct sockaddr_in6 *sa,
+          const struct in6_addr *addr, uint32_t scope)
+{
+  if (memcmp (addr, &sa->sin6_addr, sizeof (*addr)) != 0)
+    {
+      support_record_failure ();
+      printf ("error: getaddrinfo %s address mismatch for %s%%%s\n",
+              what, addr_string, scope_string);
+    }
+  if (sa->sin6_scope_id != scope)
+    {
+      support_record_failure ();
+      printf ("error: getaddrinfo %s scope mismatch for %s%%%s\n"
+              "  expected: %" PRIu32 "\n"
+              "  actual:   %" PRIu32 "\n",
+              what, addr_string, scope_string, scope, sa->sin6_scope_id);
+    }
+}
+
 /* Check a single address were we expected a failure.  */
 static void
 expect_failure (const char *address, const char *scope)
 {
+  if (test_verbose > 0)
+    printf ("info: expecting failure for %s%%%s\n", address, scope);
   struct in6_addr addr = from_string (address);
   uint32_t result = 1234;
   if (__inet6_scopeid_pton (&addr, scope, &result) == 0)
     {
+      support_record_failure ();
       printf ("error: unexpected success for %s%%%s\n",
               address, scope);
-      exit (1);
     }
   if (result != 1234)
     {
+      support_record_failure ();
       printf ("error: unexpected result update for %s%%%s\n",
               address, scope);
-      exit (1);
+    }
+
+  struct sockaddr_in6 sa;
+  if (call_gai (AF_UNSPEC, address, scope, &sa))
+    {
+      support_record_failure ();
+      printf ("error: unexpected getaddrinfo success for %s%%%s (AF_UNSPEC)\n",
+              address, scope);
+    }
+  if (call_gai (AF_INET6, address, scope, &sa))
+    {
+      support_record_failure ();
+      printf ("error: unexpected getaddrinfo success for %s%%%s (AF_INET6)\n",
+              address, scope);
     }
 }
 
@@ -76,21 +154,43 @@  expect_failure (const char *address, const char *scope)
 static void
 expect_success (const char *address, const char *scope, uint32_t expected)
 {
+  if (test_verbose > 0)
+    printf ("info: expecting success for %s%%%s\n", address, scope);
   struct in6_addr addr = from_string (address);
   uint32_t actual = expected + 1;
   if (__inet6_scopeid_pton (&addr, scope, &actual) != 0)
     {
+      support_record_failure ();
       printf ("error: unexpected failure for %s%%%s\n",
               address, scope);
-      exit (1);
     }
   if (actual != expected)
     {
+      support_record_failure ();
       printf ("error: unexpected result for for %s%%%s\n",
               address, scope);
       printf ("  expected: %" PRIu32 "\n", expected);
       printf ("  actual:   %" PRIu32 "\n", actual);
-      exit (1);
+    }
+
+  struct sockaddr_in6 sa;
+  memset (&sa, 0xc0, sizeof (sa));
+  if (call_gai (AF_UNSPEC, address, scope, &sa))
+    check_ai ("AF_UNSPEC", address, scope, &sa, &addr, expected);
+  else
+    {
+      support_record_failure ();
+      printf ("error: unexpected getaddrinfo failure for %s%%%s (AF_UNSPEC)\n",
+              address, scope);
+    }
+  memset (&sa, 0xc0, sizeof (sa));
+  if (call_gai (AF_INET6, address, scope, &sa))
+    check_ai ("AF_INET6", address, scope, &sa, &addr, expected);
+  else
+    {
+      support_record_failure ();
+      printf ("error: unexpected getaddrinfo failure for %s%%%s (AF_INET6)\n",
+              address, scope);
     }
 }
 
@@ -127,5 +227,4 @@  do_test (void)
   return 0;
 }
 
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include <support/test-driver.c>
diff --git a/resolv/Makefile b/resolv/Makefile
index d41fd46..4061bcf 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -57,7 +57,10 @@  ifeq (yes,$(build-shared))
 tests += \
   tst-resolv-canonname \
 
-endif
+endif # $(have-thread-library)
+
+# This test accesses __inet_ntop_range, an internal libc function.
+tests-internal += tst-inet_pton
 
 # This test sends millions of packets and is rather slow.
 xtests += tst-resolv-qtypes
diff --git a/resolv/Versions b/resolv/Versions
index e561bce..f528ed5 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -27,6 +27,7 @@  libc {
     __h_errno; __resp;
 
     __res_maybe_init; __res_iclose;
+    __inet_pton_length;
   }
 }
 
diff --git a/resolv/inet_pton.c b/resolv/inet_pton.c
index 68f0fa5..b95da47 100644
--- a/resolv/inet_pton.c
+++ b/resolv/inet_pton.c
@@ -1,3 +1,20 @@ 
+/* Copyright (C) 1996-2017 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/>.  */
+
 /*
  * Copyright (c) 1996,1999 by Internet Software Consortium.
  *
@@ -15,58 +32,53 @@ 
  * SOFTWARE.
  */
 
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
 #include <ctype.h>
-#include <string.h>
 #include <errno.h>
+#include <netinet/in.h>
+#include <resolv/resolv-internal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
 
-static int inet_pton4 (const char *src, unsigned char *dst);
-static int inet_pton6 (const char *src, unsigned char *dst);
+static int inet_pton4 (const char *src, const char *src_end, u_char *dst);
+static int inet_pton6 (const char *src, const char *src_end, u_char *dst);
 
-/* Convert from presentation format (which usually means ASCII printable)
- * to network format (which is usually some kind of binary format).
- *
- * return:
- *      1 if the address was valid for the specified address family
- *      0 if the address wasn't valid (`dst' is untouched in this case)
- *      -1 if some other error occurred (`dst' is untouched in this case, too)
- * author:
- *      Paul Vixie, 1996.
- */
 int
-__inet_pton (int af, const char *src, void *dst)
+__inet_pton_length (int af, const char *src, size_t srclen, void *dst)
 {
   switch (af)
     {
     case AF_INET:
-      return inet_pton4 (src, dst);
+      return inet_pton4 (src, src + srclen, dst);
     case AF_INET6:
-      return inet_pton6 (src, dst);
+      return inet_pton6 (src, src + srclen, dst);
     default:
       __set_errno (EAFNOSUPPORT);
       return -1;
     }
 }
+libc_hidden_def (__inet_pton_length)
+
+/* Like __inet_pton_length, but use strlen (SRC) as the length of
+   SRC.  */
+int
+__inet_pton (int af, const char *src, void *dst)
+{
+  return __inet_pton_length (af, src, strlen (src), dst);
+}
 libc_hidden_def (__inet_pton)
 weak_alias (__inet_pton, inet_pton)
 libc_hidden_weak (inet_pton)
 
-/* Like inet_atonbut without all the hexadecimal, octal and shorthand.
- *
- * return:
- *      1 if `src' is a valid dotted quad, else 0.
- * notice:
- *      does not touch `dst' unless it's returning 1.
- * author:
- *      Paul Vixie, 1996.
- */
+/* Like inet_aton but without all the hexadecimal, octal and shorthand
+   (and trailing garbage is not ignored).  Return 1 if SRC is a valid
+   dotted quad, else 0.  This function does not touch DST unless it's
+   returning 1.
+   Author: Paul Vixie, 1996.  */
 static int
-inet_pton4 (const char *src, unsigned char *dst)
+inet_pton4 (const char *src, const char *end, unsigned char *dst)
 {
   int saw_digit, octets, ch;
   unsigned char tmp[NS_INADDRSZ], *tp;
@@ -74,8 +86,9 @@  inet_pton4 (const char *src, unsigned char *dst)
   saw_digit = 0;
   octets = 0;
   *(tp = tmp) = 0;
-  while ((ch = *src++) != '\0')
+  while (src < end)
     {
+      ch = *src++;
       if (ch >= '0' && ch <= '9')
         {
           unsigned int new = *tp * 10 + (ch - '0');
@@ -108,22 +121,27 @@  inet_pton4 (const char *src, unsigned char *dst)
   return 1;
 }
 
-/* Cconvert presentation level address to network order binary form.
- *
- * return:
- *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
- * notice:
- *      (1) does not touch `dst' unless it's returning 1.
- *      (2) :: in a full address is silently ignored.
- * credit:
- *      inspired by Mark Andrews.
- * author:
- *      Paul Vixie, 1996.
- */
+/* Return the value of CH as a hexademical digit, or -1 if it is a
+   different type of character.  */
 static int
-inet_pton6 (const char *src, unsigned char *dst)
+hex_digit_value (char ch)
+{
+  if ('0' <= ch && ch <= '9')
+    return ch - '0';
+  if ('a' <= ch && ch <= 'f')
+    return ch - 'a' + 10;
+  if ('A' <= ch && ch <= 'F')
+    return ch - 'A' + 10;
+  return -1;
+}
+
+/* Convert presentation-level IPv6 address to network order binary
+   form.  Return 1 if SRC is a valid [RFC1884 2.2] address, else 0.
+   This function does not touch DST unless it's returning 1.
+   Author: Paul Vixie, 1996.  Inspired by Mark Andrews.  */
+static int
+inet_pton6 (const char *src, const char *src_endp, unsigned char *dst)
 {
-  static const char xdigits[] = "0123456789abcdef";
   unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
   const char *curtok;
   int ch, saw_xdigit;
@@ -132,22 +150,28 @@  inet_pton6 (const char *src, unsigned char *dst)
   tp = memset (tmp, '\0', NS_IN6ADDRSZ);
   endp = tp + NS_IN6ADDRSZ;
   colonp = NULL;
+
   /* Leading :: requires some special handling.  */
+  if (src == src_endp)
+    return 0;
   if (*src == ':')
-    if (*++src != ':')
-      return 0;
+    {
+      ++src;
+      if (src == src_endp || *src != ':')
+        return 0;
+    }
+
   curtok = src;
   saw_xdigit = 0;
   val = 0;
-  while ((ch = tolower (*src++)) != '\0')
+  while (src < src_endp)
     {
-      const char *pch;
-
-      pch = strchr (xdigits, ch);
-      if (pch != NULL)
+      ch = *src++;
+      int digit = hex_digit_value (ch);
+      if (digit >= 0)
 	{
 	  val <<= 4;
-	  val |= (pch - xdigits);
+	  val |= digit;
 	  if (val > 0xffff)
 	    return 0;
 	  saw_xdigit = 1;
@@ -163,10 +187,8 @@  inet_pton6 (const char *src, unsigned char *dst)
 	      colonp = tp;
 	      continue;
 	    }
-	  else if (*src == '\0')
-	    {
-	      return 0;
-	    }
+	  else if (src == src_endp)
+            return 0;
 	  if (tp + NS_INT16SZ > endp)
 	    return 0;
 	  *tp++ = (unsigned char) (val >> 8) & 0xff;
@@ -175,8 +197,8 @@  inet_pton6 (const char *src, unsigned char *dst)
 	  val = 0;
 	  continue;
 	}
-      if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
-	  inet_pton4 (curtok, tp) > 0)
+      if (ch == '.' && ((tp + NS_INADDRSZ) <= endp)
+          && inet_pton4 (curtok, src_endp, tp) > 0)
 	{
 	  tp += NS_INADDRSZ;
 	  saw_xdigit = 0;
@@ -193,20 +215,13 @@  inet_pton6 (const char *src, unsigned char *dst)
     }
   if (colonp != NULL)
     {
-      /*
-       * Since some memmove's erroneously fail to handle
-       * overlapping regions, we'll do the shift by hand.
-       */
-      const int n = tp - colonp;
-      int i;
-
+      /* Replace :: with zeros.  */
       if (tp == endp)
-	return 0;
-      for (i = 1; i <= n; i++)
-	{
-	  endp[- i] = colonp[n - i];
-	  colonp[n - i] = 0;
-	}
+        /* :: would expand to a zero-width field.  */
+        return 0;
+      size_t n = tp - colonp;
+      memmove (endp - n, colonp, n);
+      memset (colonp, 0, endp - n - colonp);
       tp = endp;
     }
   if (tp != endp)
diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
index 0d69ce1..9afaa07 100644
--- a/resolv/resolv-internal.h
+++ b/resolv/resolv-internal.h
@@ -56,4 +56,13 @@  enum
 int __res_nopt (res_state, int n0, unsigned char *buf, int buflen,
                 int anslen) attribute_hidden;
 
+/* Convert from presentation format (which usually means ASCII
+   printable) to network format (which is usually some kind of binary
+   format).  The input is in the range [SRC, SRC + SRCLEN).  The
+   output is written to DST (which has to be 4 or 16 bytes long,
+   depending on AF).  Return 0 for invalid input, 1 for success, -1
+   for an invalid address family.  */
+int __inet_pton_length (int af, const char *src, size_t srclen, void *);
+libc_hidden_proto (__inet_pton_length)
+
 #endif  /* _RESOLV_INTERNAL_H */
diff --git a/resolv/tst-inet_pton.c b/resolv/tst-inet_pton.c
new file mode 100644
index 0000000..7fffb24
--- /dev/null
+++ b/resolv/tst-inet_pton.c
@@ -0,0 +1,549 @@ 
+/* Test inet_pton functions.
+   Copyright (C) 2017 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 <resolv/resolv-internal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+/* Memory region created by next_to_fault_allocate.  */
+struct next_to_fault
+{
+  /* The user data.  */
+  char *buffer;
+  size_t length;
+
+  /* The entire allocated region.  */
+  void *region_start;
+  size_t region_size;
+};
+
+/* Allocate a buffer of SIZE bytes just before a page which is mapped
+   with PROT_NONE (so that overrunning the buffer will cause a
+   fault).  */
+static struct next_to_fault
+next_to_fault_allocate (size_t size)
+{
+  long page_size = sysconf (_SC_PAGE_SIZE);
+  TEST_VERIFY_EXIT (page_size > 0);
+  struct next_to_fault result;
+  result.region_size = roundup (size, page_size) + page_size;
+  TEST_VERIFY_EXIT (size + page_size > size);
+  TEST_VERIFY_EXIT (result.region_size > size);
+  result.region_start
+    = xmmap (NULL, result.region_size, PROT_READ | PROT_WRITE,
+             MAP_PRIVATE | MAP_ANONYMOUS, -1);
+  /* Unmap the page after the allocation.  */
+  xmprotect (result.region_start + (result.region_size - page_size),
+             page_size, PROT_NONE);
+  /* Align the allocation within the region so that it ends just
+     before the PROT_NONE page.  */
+  result.buffer = result.region_start + result.region_size - page_size - size;
+  result.length = size;
+  return result;
+}
+
+/* Deallocate the memory region allocated by
+   next_to_fault_allocate.  */
+static void
+next_to_fault_free (struct next_to_fault *ntf)
+{
+  xmunmap (ntf->region_start, ntf->region_size);
+  *ntf = (struct next_to_fault) { NULL, };
+}
+
+struct test_case
+{
+  /* The input data.  */
+  const char *input;
+
+  /* True if AF_INET parses successfully.  */
+  bool ipv4_ok;
+
+  /* True if AF_INET6 parses successfully.  */
+  bool ipv6_ok;
+
+  /* Expected result for AF_INET.  */
+  unsigned char ipv4_expected[4];
+
+  /* Expected result for AF_INET6.  */
+  unsigned char ipv6_expected[16];
+};
+
+static void
+check_result (const char *what, const struct test_case *t, int family,
+              void *result_buffer, int inet_ret)
+{
+  TEST_VERIFY_EXIT (inet_ret >= -1);
+  TEST_VERIFY_EXIT (inet_ret <= 1);
+
+  int ok;
+  const unsigned char *expected;
+  size_t result_size;
+  switch (family)
+    {
+    case AF_INET:
+      ok = t->ipv4_ok;
+      expected = t->ipv4_expected;
+      result_size = 4;
+      break;
+    case AF_INET6:
+      ok = t->ipv6_ok;
+      expected = t->ipv6_expected;
+      result_size = 16;
+      break;
+    default:
+      FAIL_EXIT1 ("invalid address family %d", family);
+    }
+
+  if (inet_ret != ok)
+    {
+      support_record_failure ();
+      printf ("error: %s return value mismatch for [[%s]], family %d\n"
+              "  expected: %d\n"
+              "  actual: %d\n",
+              what, t->input, family, ok, inet_ret);
+      return;
+    }
+  if (memcmp (result_buffer, expected, result_size) != 0)
+    {
+      support_record_failure ();
+      printf ("error: %s result mismatch for [[%s]], family %d\n",
+              what, t->input, family);
+    }
+}
+
+static void
+run_one_test (const struct test_case *t)
+{
+  size_t test_len = strlen (t->input);
+
+  struct next_to_fault ntf_out4 = next_to_fault_allocate (4);
+  struct next_to_fault ntf_out6 = next_to_fault_allocate (16);
+
+  /* inet_pton requires NUL termination.  */
+  {
+    struct next_to_fault ntf_in = next_to_fault_allocate (test_len + 1);
+    memcpy (ntf_in.buffer, t->input, test_len + 1);
+    memset (ntf_out4.buffer, 0, 4);
+    check_result ("inet_pton", t, AF_INET, ntf_out4.buffer,
+                  inet_pton (AF_INET, ntf_in.buffer, ntf_out4.buffer));
+    memset (ntf_out6.buffer, 0, 16);
+    check_result ("inet_pton", t, AF_INET6, ntf_out6.buffer,
+                  inet_pton (AF_INET6, ntf_in.buffer, ntf_out6.buffer));
+    next_to_fault_free (&ntf_in);
+  }
+
+  /* __inet_pton_length does not require NUL termination.  */
+  {
+    struct next_to_fault ntf_in = next_to_fault_allocate (test_len);
+    memcpy (ntf_in.buffer, t->input, test_len);
+    memset (ntf_out4.buffer, 0, 4);
+    check_result ("__inet_pton_length", t, AF_INET, ntf_out4.buffer,
+                  __inet_pton_length (AF_INET, ntf_in.buffer, ntf_in.length,
+                                      ntf_out4.buffer));
+    memset (ntf_out6.buffer, 0, 16);
+    check_result ("__inet_pton_length", t, AF_INET6, ntf_out6.buffer,
+                  __inet_pton_length (AF_INET6, ntf_in.buffer, ntf_in.length,
+                                      ntf_out6.buffer));
+    next_to_fault_free (&ntf_in);
+  }
+
+  next_to_fault_free (&ntf_out4);
+  next_to_fault_free (&ntf_out6);
+}
+
+/* The test cases were manually crafted and the set enhanced with
+   American Fuzzy Lop.  */
+const struct test_case test_cases[] =
+  {
+    {.input = ".:", },
+    {.input = "0.0.0.0",
+     .ipv4_ok = true,
+     .ipv4_expected = {0, 0, 0, 0},
+    },
+    {.input = "0.:", },
+    {.input = "00", },
+    {.input = "0000000", },
+    {.input = "00000000000000000", },
+    {.input = "092.", },
+    {.input = "10.0.301.2", },
+    {.input = "127.0.0.1",
+     .ipv4_ok = true,
+     .ipv4_expected = {127, 0, 0, 1},
+    },
+    {.input = "19..", },
+    {.input = "192.0.2.-1", },
+    {.input = "192.0.2.01", },
+    {.input = "192.0.2.1.", },
+    {.input = "192.0.2.1192.", },
+    {.input = "192.0.2.192.\377..", },
+    {.input = "192.0.2.256", },
+    {.input = "192.0.2.27",
+     .ipv4_ok = true,
+     .ipv4_expected = {192, 0, 2, 27},
+    },
+    {.input = "192.0.201.", },
+    {.input = "192.0.261.", },
+    {.input = "192.0.2\256", },
+    {.input = "192.0.\262.", },
+    {.input = "192.062.", },
+    {.input = "192.092.\256", },
+    {.input = "192.0\2562.", },
+    {.input = "192.192.0.2661\031", },
+    {.input = "192.192.00n2.1.", },
+    {.input = "192.192.2.190.", },
+    {.input = "192.255.255.2555", },
+    {.input = "192.92.219\023.", },
+    {.input = "192.\260.2.", },
+    {.input = "1:1::1:1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1
+     },
+    },
+    {.input = "2", },
+    {.input = "2.", },
+    {.input = "2001:db8:00001::f",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x1, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf
+     },
+    },
+    {.input = "2001:db8:10000::f", },
+    {.input = "2001:db8:1234:5678:abcd:ef01:2345:67",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x12, 0x34, 0x56, 0x78,
+       0xab, 0xcd, 0xef, 0x1, 0x23, 0x45, 0x0, 0x67
+     },
+    },
+    {.input = "2001:db8:1234:5678:abcd:ef01:2345:6789:1", },
+    {.input = "2001:db8:1234:5678:abcd:ef01:2345::6789", },
+    {.input = "2001:db8::0",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+     },
+    },
+    {.input = "2001:db8::00",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+     },
+    },
+    {.input = "2001:db8::1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1
+     },
+    },
+    {.input = "2001:db8::10",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10
+     },
+    },
+    {.input = "2001:db8::19",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19
+     },
+    },
+    {.input = "2001:db8::1::\012", },
+    {.input = "2001:db8::1::2\012", },
+    {.input = "2001:db8::2",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2
+     },
+    },
+    {.input = "2001:db8::3",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3
+     },
+    },
+    {.input = "2001:db8::4",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4
+     },
+    },
+    {.input = "2001:db8::5",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5
+     },
+    },
+    {.input = "2001:db8::6",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6
+     },
+    },
+    {.input = "2001:db8::7",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7
+     },
+    },
+    {.input = "2001:db8::8",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8
+     },
+    },
+    {.input = "2001:db8::9",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9
+     },
+    },
+    {.input = "2001:db8::A",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa
+     },
+    },
+    {.input = "2001:db8::B",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb
+     },
+    },
+    {.input = "2001:db8::C",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc
+     },
+    },
+    {.input = "2001:db8::D",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd
+     },
+    },
+    {.input = "2001:db8::E",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe
+     },
+    },
+    {.input = "2001:db8::F",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf
+     },
+    },
+    {.input = "2001:db8::a",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa
+     },
+    },
+    {.input = "2001:db8::b",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb
+     },
+    },
+    {.input = "2001:db8::c",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc
+     },
+    },
+    {.input = "2001:db8::d",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd
+     },
+    },
+    {.input = "2001:db8::e",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe
+     },
+    },
+    {.input = "2001:db8::f",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf
+     },
+    },
+    {.input = "2001:db8::ff",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x20, 0x1, 0xd, 0xb8, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff
+     },
+    },
+    {.input = "2001:db8::ffff:2\012", },
+    {.input = "22", },
+    {.input = "2222@", },
+    {.input = "255.255.255.255",
+     .ipv4_ok = true,
+     .ipv4_expected = {255, 255, 255, 255},
+    },
+    {.input = "255.255.255.255\001", },
+    {.input = "255.255.255.25555", },
+    {.input = "2:", },
+    {.input = "2:a:8:EEEE::EEEE:F:EEE8:EEEE\034*:", },
+    {.input = "2:ff:1:1:7:ff:1:1:7.", },
+    {.input = "2f:0000000000000000000000000000000000000000000000000000000000"
+     "0000000000000000000000000000000000000000000000000000000000000000000000"
+     "0G01",
+    },
+    {.input = "429495", },
+    {.input = "5::5::", },
+    {.input = "6.6.", },
+    {.input = "992.", },
+    {.input = "::",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+     },
+    },
+    {.input = "::00001",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1
+     },
+    },
+    {.input = "::1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1
+     },
+    },
+    {.input = "::10000", },
+    {.input = "::1:1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1
+     },
+    },
+    {.input = "::ff:1:1:7.0.0.1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
+       0x0, 0x1, 0x0, 0x1, 0x7, 0x0, 0x0, 0x1
+     },
+    },
+    {.input = "::ff:1:1:7:ff:1:1:7.", },
+    {.input = "::ff:1:1:7ff:1:8:7.0.0.1", },
+    {.input = "::ff:1:1:7ff:1:8f:1:1:71", },
+    {.input = "::ffff:02fff:127.0.S1", },
+    {.input = "::ffff:127.0.0.1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0xff, 0xff, 0x7f, 0x0, 0x0, 0x1
+     },
+    },
+    {.input = "::ffff:1:7.0.0.1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+       0xff, 0xff, 0x0, 0x1, 0x7, 0x0, 0x0, 0x1
+     },
+    },
+    {.input = ":\272", },
+    {.input = "A:f:ff:1:1:D:ff:1:1::7.", },
+    {.input = "AAAAA.", },
+    {.input = "D:::", },
+    {.input = "DF8F", },
+    {.input = "F::",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+     },
+    },
+    {.input = "F:A:8:EEEE:8:EEEE\034*:", },
+    {.input = "F:a:8:EEEE:8:EEEE\034*:", },
+    {.input = "F:ff:100:7ff:1:8:7.0.10.1",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0x0, 0xf, 0x0, 0xff, 0x1, 0x0, 0x7, 0xff,
+       0x0, 0x1, 0x0, 0x8, 0x7, 0x0, 0xa, 0x1
+     },
+    },
+    {.input = "d92.", },
+    {.input = "ff:00000000000000000000000000000000000000000000000000000000000"
+     "00000000000000000000000000000000000000000000000000000000000000000001",
+    },
+    {.input = "fff2:2::ff2:2:f7",
+     .ipv6_ok = true,
+     .ipv6_expected = {
+       0xff, 0xf2, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0,
+       0x0, 0x0, 0xf, 0xf2, 0x0, 0x2, 0x0, 0xf7
+     },
+    },
+    {.input = "ffff:ff:ff:fff:ff:ff:ff:", },
+    {.input = "\272:", },
+    {NULL}
+  };
+
+static int
+do_test (void)
+{
+  for (size_t i = 0; test_cases[i].input != NULL; ++i)
+    run_one_test (test_cases + i);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/support/Makefile b/support/Makefile
index 38dbd83..21e56c1 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -64,6 +64,7 @@  libsupport-routines = \
   xmalloc \
   xmemstream \
   xmmap \
+  xmprotect \
   xmunmap \
   xpoll \
   xpthread_attr_destroy \
diff --git a/support/xmprotect.c b/support/xmprotect.c
new file mode 100644
index 0000000..9410251
--- /dev/null
+++ b/support/xmprotect.c
@@ -0,0 +1,28 @@ 
+/* mprotect with error checking.
+   Copyright (C) 2017 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 <support/check.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+
+void
+xmprotect (void *addr, size_t length, int prot)
+{
+  if (mprotect (addr, length, prot) != 0)
+    FAIL_EXIT1 ("mprotect (%p, %zu, 0x%x): %m", addr, length, prot);
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 258bab5..850117f 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -39,7 +39,7 @@  void xwrite (int, const void *, size_t);
 
 /* Invoke mmap with a zero file offset.  */
 void *xmmap (void *addr, size_t length, int prot, int flags, int fd);
-
+void xmprotect (void *addr, size_t length, int prot);
 void xmunmap (void *addr, size_t length);
 
 __END_DECLS
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index dc02b11..3d9cd45 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -509,46 +509,11 @@  gaih_inet (const char *name, const struct gaih_service *service,
 	{
 	  char *scope_delim = strchr (name, SCOPE_DELIMITER);
 	  int e;
-
-	  {
-	    bool malloc_namebuf = false;
-	    char *namebuf = (char *) name;
-
-	    if (__glibc_unlikely (scope_delim != NULL))
-	      {
-		if (malloc_name)
-		  *scope_delim = '\0';
-		else
-		  {
-		    if (__libc_use_alloca (alloca_used
-					   + scope_delim - name + 1))
-		      {
-			namebuf = alloca_account (scope_delim - name + 1,
-						  alloca_used);
-			*((char *) __mempcpy (namebuf, name,
-					      scope_delim - name)) = '\0';
-		      }
-		    else
-		      {
-			namebuf = __strndup (name, scope_delim - name);
-			if (namebuf == NULL)
-			  {
-			    assert (!malloc_name);
-			    return -EAI_MEMORY;
-			  }
-			malloc_namebuf = true;
-		      }
-		  }
-	      }
-
-	    e = inet_pton (AF_INET6, namebuf, at->addr);
-
-	    if (malloc_namebuf)
-	      free (namebuf);
-	    else if (scope_delim != NULL && malloc_name)
-	      /* Undo what we did above.  */
-	      *scope_delim = SCOPE_DELIMITER;
-	  }
+	  if (scope_delim == NULL)
+	    e = inet_pton (AF_INET6, name, at->addr);
+	  else
+	    e = __inet_pton_length (AF_INET6, name, scope_delim - name,
+				    at->addr);
 	  if (e > 0)
 	    {
 	      if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)