get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/809775/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 809775,
    "url": "http://patchwork.ozlabs.org/api/patches/809775/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/glibc/patch/20170904173210.3CE87439942E3@oldenburg.str.redhat.com/",
    "project": {
        "id": 41,
        "url": "http://patchwork.ozlabs.org/api/projects/41/?format=api",
        "name": "GNU C Library",
        "link_name": "glibc",
        "list_id": "libc-alpha.sourceware.org",
        "list_email": "libc-alpha@sourceware.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20170904173210.3CE87439942E3@oldenburg.str.redhat.com>",
    "list_archive_url": null,
    "date": "2017-09-04T17:32:10",
    "name": "nss_files: Avoid large buffers with many host addresses [BZ #22078]",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "41e8b2c0e4e4d657b1218ccb913835c1c07ae640",
    "submitter": {
        "id": 14312,
        "url": "http://patchwork.ozlabs.org/api/people/14312/?format=api",
        "name": "Florian Weimer",
        "email": "fweimer@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/glibc/patch/20170904173210.3CE87439942E3@oldenburg.str.redhat.com/mbox/",
    "series": [
        {
            "id": 1430,
            "url": "http://patchwork.ozlabs.org/api/series/1430/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/glibc/list/?series=1430",
            "date": "2017-09-04T17:32:10",
            "name": "nss_files: Avoid large buffers with many host addresses [BZ #22078]",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/1430/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/809775/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/809775/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<libc-alpha-return-84175-incoming=patchwork.ozlabs.org@sourceware.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "mailing list libc-alpha@sourceware.org"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=sourceware.org\n\t(client-ip=209.132.180.131; helo=sourceware.org;\n\tenvelope-from=libc-alpha-return-84175-incoming=patchwork.ozlabs.org@sourceware.org;\n\treceiver=<UNKNOWN>)",
            "ozlabs.org; dkim=pass (1024-bit key;\n\tsecure) header.d=sourceware.org header.i=@sourceware.org\n\theader.b=\"efKp57g2\"; dkim-atps=neutral",
            "sourceware.org; auth=none",
            "ext-mx02.extmail.prod.ext.phx2.redhat.com;\n\tdmarc=none (p=none dis=none) header.from=redhat.com",
            "ext-mx02.extmail.prod.ext.phx2.redhat.com;\n\tspf=fail smtp.mailfrom=fweimer@redhat.com"
        ],
        "Received": [
            "from sourceware.org (server1.sourceware.org [209.132.180.131])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3xmH3f6FQZz9s7m\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue,  5 Sep 2017 03:32:30 +1000 (AEST)",
            "(qmail 33529 invoked by alias); 4 Sep 2017 17:32:20 -0000",
            "(qmail 33370 invoked by uid 89); 4 Sep 2017 17:32:20 -0000"
        ],
        "DomainKey-Signature": "a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id\n\t:list-unsubscribe:list-subscribe:list-archive:list-post\n\t:list-help:sender:date:to:subject:mime-version:content-type\n\t:content-transfer-encoding:message-id:from; q=dns; s=default; b=\n\tJKLK4CP8AAXB0cLyyecQPyw6O8YdxTfoWnDgl+nnGrEP9tcFsKsO1L3otMuD0AJx\n\tFTW2D6UUaatAYJmUh5RCCGJcqS9oU4e3ESyCGC4FUtQTkKmMMd5S1ydaUH/+x8q8\n\t4MlmYnnF+nVEaNd+zUnxfDx63DgAyDdOmjESCgwRQMY=",
        "DKIM-Signature": "v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id\n\t:list-unsubscribe:list-subscribe:list-archive:list-post\n\t:list-help:sender:date:to:subject:mime-version:content-type\n\t:content-transfer-encoding:message-id:from; s=default; bh=KDljqy\n\tWZxUGIMAxX4WuisZ7WLjw=; b=efKp57g25kU9dh67KU5DXEw/zRGdSRagR4VzxH\n\tcAIEbvoYc1kghjG+1Bt4RQ5dI+umgzja0T0iRcAC1ysaoTTXj0PkBsH2WRmo9xBd\n\t/2AizFSLMWc7AC5AxzjPxK7RKMhh02YCC1u5TmFpjNkmc1b661y+/MOsInln5zMb\n\tvPHGU=",
        "Mailing-List": "contact libc-alpha-help@sourceware.org; run by ezmlm",
        "Precedence": "bulk",
        "List-Id": "<libc-alpha.sourceware.org>",
        "List-Unsubscribe": "<mailto:libc-alpha-unsubscribe-incoming=patchwork.ozlabs.org@sourceware.org>",
        "List-Subscribe": "<mailto:libc-alpha-subscribe@sourceware.org>",
        "List-Archive": "<http://sourceware.org/ml/libc-alpha/>",
        "List-Post": "<mailto:libc-alpha@sourceware.org>",
        "List-Help": "<mailto:libc-alpha-help@sourceware.org>,\n\t<http://sourceware.org/ml/#faqs>",
        "Sender": "libc-alpha-owner@sourceware.org",
        "X-Virus-Found": "No",
        "X-Spam-SWARE-Status": "No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0,\n\tGIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RP_MATCHES_RCVD,\n\tSPF_HELO_PASS autolearn=ham version=3.3.2 spammy=",
        "X-HELO": "mx1.redhat.com",
        "DMARC-Filter": "OpenDMARC Filter v1.3.2 mx1.redhat.com D71B8BB0C",
        "Date": "Mon, 04 Sep 2017 19:32:10 +0200",
        "To": "libc-alpha@sourceware.org",
        "Subject": "[PATCH] nss_files: Avoid large buffers with many host addresses\n\t[BZ #22078]",
        "User-Agent": "Heirloom mailx 12.5 7/5/10",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=us-ascii",
        "Content-Transfer-Encoding": "7bit",
        "Message-Id": "<20170904173210.3CE87439942E3@oldenburg.str.redhat.com>",
        "From": "fweimer@redhat.com (Florian Weimer)"
    },
    "content": "The previous implementation had at least a quadratic space\nrequirement in the number of host addresses and aliases.\n\n2017-09-04  Florian Weimer  <fweimer@redhat.com>\n\n\t[BZ #22078]\n\tAvoid large NSS buffers with many addresses, aliases.\n\t* nss/nss_files/files-hosts.c (gethostbyname3_multi): Rewrite\n\tusing dynarrays and struct alloc_buffer.\n\t* nss/Makefile (tests): Add tst-nss-files-hosts-multi.\n\t(tst-nss-files-hosts-multi): Link with -ldl.\n\t* nss/tst-nss-files-hosts-multi.c: New file.",
    "diff": "diff --git a/nss/Makefile b/nss/Makefile\nindex c9a5200f96..ecf3530188 100644\n--- a/nss/Makefile\n+++ b/nss/Makefile\n@@ -63,6 +63,7 @@ xtests\t\t\t= bug-erange\n # Tests which need libdl\n ifeq (yes,$(build-shared))\n tests += tst-nss-files-hosts-erange\n+tests += tst-nss-files-hosts-multi\n endif\n \n # If we have a thread library then we can test cancellation against\n@@ -163,3 +164,4 @@ $(objpfx)tst-cancel-getpwuid_r: $(shared-thread-library)\n endif\n \n $(objpfx)tst-nss-files-hosts-erange: $(libdl)\n+$(objpfx)tst-nss-files-hosts-multi: $(libdl)\ndiff --git a/nss/nss_files/files-hosts.c b/nss/nss_files/files-hosts.c\nindex c2cd07584c..f40673056b 100644\n--- a/nss/nss_files/files-hosts.c\n+++ b/nss/nss_files/files-hosts.c\n@@ -23,6 +23,7 @@\n #include <netdb.h>\n #include <resolv/resolv-internal.h>\n #include <scratch_buffer.h>\n+#include <alloc_buffer.h>\n \n \n /* Get implementation for some internal functions.  */\n@@ -116,24 +117,46 @@ DB_LOOKUP (hostbyaddr, ,,,\n \t   }, const void *addr, socklen_t len, int af)\n #undef EXTRA_ARGS_VALUE\n \n+/* Type of the address and alias arrays.  */\n+#define DYNARRAY_STRUCT array\n+#define DYNARRAY_ELEMENT char *\n+#define DYNARRAY_PREFIX array_\n+#include <malloc/dynarray-skeleton.c>\n+\n static enum nss_status\n gethostbyname3_multi (FILE * stream, const char *name, int af,\n \t\t      struct hostent *result, char *buffer, size_t buflen,\n \t\t      int *errnop, int *herrnop, int flags)\n {\n+  assert (af == AF_INET || af == AF_INET6);\n+\n   /* We have to get all host entries from the file.  */\n   struct scratch_buffer tmp_buffer;\n   scratch_buffer_init (&tmp_buffer);\n   struct hostent tmp_result_buf;\n-  int naddrs = 1;\n-  int naliases = 0;\n-  char *bufferend;\n+  struct array addresses;\n+  array_init (&addresses);\n+  struct array aliases;\n+  array_init (&aliases);\n   enum nss_status status;\n+  bool new_data = false;\n \n-  while (result->h_aliases[naliases] != NULL)\n-    ++naliases;\n+  /* The output buffer uses the unused space after the first\n+     result.  */\n+    struct alloc_buffer outbuf;\n+  {\n+    /* Preserve the aliases encountered so far.  */\n+    size_t i;\n+    for (i = 0; result->h_aliases[i] != NULL; ++i)\n+      array_add (&aliases, result->h_aliases[i]);\n \n-  bufferend = (char *) &result->h_aliases[naliases + 1];\n+    char *bufferend = (char *) &result->h_aliases[i + 1];\n+    outbuf = alloc_buffer_create (bufferend, buffer + buflen - bufferend);\n+  }\n+\n+  /* Preserve the addresses.  */\n+  for (size_t i = 0; result->h_addr_list[i] != NULL; ++i)\n+    array_add (&addresses, result->h_addr_list[i]);\n \n  again:\n   while ((status = internal_getent (stream, &tmp_result_buf, tmp_buffer.data,\n@@ -154,110 +177,72 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,\n \t}\n       while ((matches = 0));\n \n+      /* If the line matches, we need to copy the addresses and\n+\t aliases, so that we can reuse tmp_buffer for the next\n+\t line.  */\n       if (matches)\n \t{\n-\t  /* We could be very clever and try to recycle a few bytes\n-\t     in the buffer instead of generating new arrays.  But\n-\t     we are not doing this here since it's more work than\n-\t     it's worth.  Simply let the user provide a bit bigger\n-\t     buffer.  */\n-\t  char **new_h_addr_list;\n-\t  char **new_h_aliases;\n-\t  int newaliases = 0;\n-\t  size_t newstrlen = 0;\n-\t  int cnt;\n+\t  new_data = true;\n \n-\t  /* Count the new aliases and the length of the strings.  */\n-\t  while (tmp_result_buf.h_aliases[newaliases] != NULL)\n+\t  /* Record the addresses.  */\n+\t  for (int i = 0; tmp_result_buf.h_addr_list[i] != NULL; ++i)\n \t    {\n-\t      char *cp = tmp_result_buf.h_aliases[newaliases];\n-\t      ++newaliases;\n-\t      newstrlen += strlen (cp) + 1;\n+\t      /* Allocate the target space in the output buffer,\n+\t\t depending on the address family.  */\n+\t      void *target;\n+\t      if (af == AF_INET)\n+\t\t{\n+\t\t  assert (tmp_result_buf.h_length == 4);\n+\t\t  target = alloc_buffer_alloc (&outbuf, struct in_addr);\n+\t\t}\n+\t      else if (af == AF_INET6)\n+\t\t{\n+\t\t  assert (tmp_result_buf.h_length == 16);\n+\t\t  target = alloc_buffer_alloc (&outbuf, struct in6_addr);\n+\t\t}\n+\t      else\n+\t\t__builtin_unreachable ();\n+\n+\t      if (target == NULL)\n+\t\t{\n+\t\t  /* Request a larger output buffer.  */\n+\t\t  *errnop = ERANGE;\n+\t\t  *herrnop = NETDB_INTERNAL;\n+\t\t  status = NSS_STATUS_TRYAGAIN;\n+\t\t  goto out;\n+\t\t}\n+\t      memcpy (target, tmp_result_buf.h_addr_list[i],\n+\t\t      tmp_result_buf.h_length);\n+\t      array_add (&addresses, target);\n \t    }\n-\t  /* If the real name is different add it also to the\n-\t     aliases.  This means that there is a duplication\n-\t     in the alias list but this is really the user's\n-\t     problem.  */\n-\t  if (strcmp (old_result->h_name,\n-\t\t      tmp_result_buf.h_name) != 0)\n+\n+\t  /* Record the aliases.  */\n+\t  for (int i = 0; tmp_result_buf.h_aliases[i] != NULL; ++i)\n \t    {\n-\t      ++newaliases;\n-\t      newstrlen += strlen (tmp_result_buf.h_name) + 1;\n+\t      char *alias = tmp_result_buf.h_aliases[i];\n+\t      array_add (&aliases, alloc_buffer_copy_string (&outbuf, alias));\n \t    }\n \n-\t  /* Make sure bufferend is aligned.  */\n-\t  assert ((bufferend - (char *) 0) % sizeof (char *) == 0);\n+\t  /* If the real name is different add it also to the aliases.\n+\t     This means that there is a duplication in the alias list\n+\t     but this is really the user's problem.  */\n+\t  {\n+\t    char *new_name = tmp_result_buf.h_name;\n+\t    if (strcmp (old_result->h_name, new_name) != 0)\n+\t      array_add (&aliases,\n+\t\t\t alloc_buffer_copy_string (&outbuf, new_name));\n+\t  }\n \n-\t  /* Now we can check whether the buffer is large enough.\n-\t     16 is the maximal size of the IP address.  */\n-\t  if (bufferend + 16 + (naddrs + 2) * sizeof (char *)\n-\t      + roundup (newstrlen, sizeof (char *))\n-\t      + (naliases + newaliases + 1) * sizeof (char *)\n-\t      >= buffer + buflen)\n+\t  /* Memory allocation failures during the expansion of the\n+\t     temporary arrays.  */\n+\t  if (array_has_failed (&addresses) || array_has_failed (&aliases))\n \t    {\n-\t      *errnop = ERANGE;\n+\t      *errnop = errno;\n \t      *herrnop = NETDB_INTERNAL;\n-\t      status = NSS_STATUS_TRYAGAIN;\n+\t      status = NSS_STATUS_UNAVAIL;\n \t      goto out;\n \t    }\n \n-\t  new_h_addr_list =\n-\t    (char **) (bufferend\n-\t\t       + roundup (newstrlen, sizeof (char *))\n-\t\t       + 16);\n-\t  new_h_aliases =\n-\t    (char **) ((char *) new_h_addr_list\n-\t\t       + (naddrs + 2) * sizeof (char *));\n-\n-\t  /* Copy the old data in the new arrays.  */\n-\t  for (cnt = 0; cnt < naddrs; ++cnt)\n-\t    new_h_addr_list[cnt] = old_result->h_addr_list[cnt];\n-\n-\t  for (cnt = 0; cnt < naliases; ++cnt)\n-\t    new_h_aliases[cnt] = old_result->h_aliases[cnt];\n-\n-\t  /* Store the new strings.  */\n-\t  cnt = 0;\n-\t  while (tmp_result_buf.h_aliases[cnt] != NULL)\n-\t    {\n-\t      new_h_aliases[naliases++] = bufferend;\n-\t      bufferend = (__stpcpy (bufferend,\n-\t\t\t\t     tmp_result_buf.h_aliases[cnt])\n-\t\t\t   + 1);\n-\t      ++cnt;\n-\t    }\n-\n-\t  if (cnt < newaliases)\n-\t    {\n-\t      new_h_aliases[naliases++] = bufferend;\n-\t      bufferend = __stpcpy (bufferend,\n-\t\t\t\t    tmp_result_buf.h_name) + 1;\n-\t    }\n-\n-\t  /* Final NULL pointer.  */\n-\t  new_h_aliases[naliases] = NULL;\n-\n-\t  /* Round up the buffer end address.  */\n-\t  bufferend += (sizeof (char *)\n-\t\t\t- ((bufferend - (char *) 0)\n-\t\t\t   % sizeof (char *))) % sizeof (char *);\n-\n-\t  /* Now the new address.  */\n-\t  new_h_addr_list[naddrs++] =\n-\t    memcpy (bufferend, tmp_result_buf.h_addr,\n-\t\t    tmp_result_buf.h_length);\n-\n-\t  /* Also here a final NULL pointer.  */\n-\t  new_h_addr_list[naddrs] = NULL;\n-\n-\t  /* Store the new array pointers.  */\n-\t  old_result->h_aliases = new_h_aliases;\n-\t  old_result->h_addr_list = new_h_addr_list;\n-\n-\t  /* Compute the new buffer end.  */\n-\t  bufferend = (char *) &new_h_aliases[naliases + 1];\n-\t  assert (bufferend <= buffer + buflen);\n-\n \t  result = old_result;\n \t}\n     }\n@@ -266,16 +251,52 @@ gethostbyname3_multi (FILE * stream, const char *name, int af,\n     {\n       if (!scratch_buffer_grow (&tmp_buffer))\n \t{\n+\t  *errnop = errno;\n \t  *herrnop = NETDB_INTERNAL;\n-\t  status = NSS_STATUS_TRYAGAIN;\n+\t  status = NSS_STATUS_UNAVAIL;\n \t}\n       else\n \tgoto again;\n     }\n   else\n-    status = NSS_STATUS_SUCCESS;\n+    {\n+      /* Copy the address and alias arrays into the output buffer and\n+\t add NULL terminators.  The pointed-to elements were directly\n+\t written into the output buffer above and do not need to be\n+\t copied again.  */\n+      if (new_data)\n+\t{\n+\t  size_t addresses_count = array_size (&addresses);\n+\t  size_t aliases_count = array_size (&aliases);\n+\t  char **out_addresses = alloc_buffer_alloc_array\n+\t    (&outbuf, char *, addresses_count + 1);\n+\t  char **out_aliases = alloc_buffer_alloc_array\n+\t    (&outbuf, char *, aliases_count + 1);\n+\t  if (out_addresses == NULL || out_aliases == NULL)\n+\t    {\n+\t      /* The output buffer is not large enough.  */\n+\t      *errnop = ERANGE;\n+\t      *herrnop = NETDB_INTERNAL;\n+\t      status = NSS_STATUS_TRYAGAIN;\n+\t      goto out;\n+\t    }\n+\t  memcpy (out_addresses, array_begin (&addresses),\n+\t\t  addresses_count * sizeof (char *));\n+\t  out_addresses[addresses_count] = NULL;\n+\t  memcpy (out_aliases, array_begin (&aliases),\n+\t\t  aliases_count * sizeof (char *));\n+\t  out_aliases[aliases_count] = NULL;\n+\n+\t  result->h_addr_list = out_addresses;\n+\t  result->h_aliases = out_aliases;\n+\t}\n+\n+      status = NSS_STATUS_SUCCESS;\n+    }\n  out:\n   scratch_buffer_free (&tmp_buffer);\n+  array_free (&addresses);\n+  array_free (&aliases);\n   return status;\n }\n \ndiff --git a/nss/tst-nss-files-hosts-multi.c b/nss/tst-nss-files-hosts-multi.c\nnew file mode 100644\nindex 0000000000..c86a4e05d0\n--- /dev/null\n+++ b/nss/tst-nss-files-hosts-multi.c\n@@ -0,0 +1,327 @@\n+/* Parse /etc/hosts in multi mode with many addresses/aliases.\n+   Copyright (C) 2017 Free Software Foundation, Inc.\n+   This file is part of the GNU C Library.\n+\n+   The GNU C Library is free software; you can redistribute it and/or\n+   modify it under the terms of the GNU Lesser General Public\n+   License as published by the Free Software Foundation; either\n+   version 2.1 of the License, or (at your option) any later version.\n+\n+   The GNU C Library is distributed in the hope that it will be useful,\n+   but WITHOUT ANY WARRANTY; without even the implied warranty of\n+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+   Lesser General Public License for more details.\n+\n+   You should have received a copy of the GNU Lesser General Public\n+   License along with the GNU C Library; if not, see\n+   <http://www.gnu.org/licenses/>.  */\n+\n+#include <dlfcn.h>\n+#include <errno.h>\n+#include <gnu/lib-names.h>\n+#include <netdb.h>\n+#include <nss.h>\n+#include <stdbool.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <support/check.h>\n+#include <support/check_nss.h>\n+#include <support/namespace.h>\n+#include <support/support.h>\n+#include <support/test-driver.h>\n+#include <support/test-driver.h>\n+#include <support/xmemstream.h>\n+#include <support/xstdio.h>\n+#include <support/xunistd.h>\n+#include <sys/resource.h>\n+\n+struct support_chroot *chroot_env;\n+\n+static void\n+prepare (int argc, char **argv)\n+{\n+  chroot_env = support_chroot_create\n+    ((struct support_chroot_configuration)\n+     {\n+       .resolv_conf = \"\",\n+       .hosts = \"\",             /* See write_hosts below.  */\n+       .host_conf = \"multi on\\n\",\n+     });\n+}\n+\n+/* Create the /etc/hosts file from outside the chroot.  */\n+static void\n+write_hosts (int count)\n+{\n+  TEST_VERIFY (count > 0 && count <= 65535);\n+  FILE *fp = xfopen (chroot_env->path_hosts, \"w\");\n+  fputs (\"127.0.0.1   localhost localhost.localdomain\\n\"\n+         \"::1         localhost localhost.localdomain\\n\",\n+         fp);\n+  for (int i = 0; i < count; ++i)\n+    {\n+      fprintf (fp, \"10.4.%d.%d www4.example.com\\n\",\n+               (i / 256) & 0xff, i & 0xff);\n+      fprintf (fp, \"10.46.%d.%d www.example.com\\n\",\n+               (i / 256) & 0xff, i & 0xff);\n+      fprintf (fp, \"192.0.2.1 alias.example.com v4-%d.example.com\\n\", i);\n+      fprintf (fp, \"2001:db8::6:%x www6.example.com\\n\", i);\n+      fprintf (fp, \"2001:db8::46:%x www.example.com\\n\", i);\n+      fprintf (fp, \"2001:db8::1 alias.example.com v6-%d.example.com\\n\", i);\n+    }\n+  xfclose (fp);\n+}\n+\n+/* Parameters of a single test.  */\n+struct test_params\n+{\n+  const char *name;             /* Name to query.  */\n+  const char *marker;           /* Address marker for the name.  */\n+  int count;                    /* Number of addresses/aliases.  */\n+  int family;                   /* AF_INET, AF_INET_6 or AF_UNSPEC.  */\n+  bool canonname;               /* True if AI_CANONNAME should be enabled.  */\n+};\n+\n+/* Expected result of gethostbyname/gethostbyname2.  */\n+static char *\n+expected_ghbn (const struct test_params *params)\n+{\n+  TEST_VERIFY (params->family == AF_INET || params->family == AF_INET6);\n+\n+  struct xmemstream expected;\n+  xopen_memstream (&expected);\n+  if (strcmp (params->name, \"alias.example.com\") == 0)\n+    {\n+      fprintf (expected.out, \"name: %s\\n\", params->name);\n+      char af;\n+      if (params->family == AF_INET)\n+        af = '4';\n+      else\n+        af = '6';\n+      for (int i = 0; i < params->count; ++i)\n+        fprintf (expected.out, \"alias: v%c-%d.example.com\\n\", af, i);\n+\n+      for (int i = 0; i < params->count; ++i)\n+        if (params->family == AF_INET)\n+          fputs (\"address: 192.0.2.1\\n\", expected.out);\n+        else\n+          fputs (\"address: 2001:db8::1\\n\", expected.out);\n+    }\n+  else /* www/www4/www6 name.  */\n+    {\n+      bool do_ipv4 = params->family == AF_INET\n+        && strncmp (params->name, \"www6\", 4) != 0;\n+      bool do_ipv6 = params->family == AF_INET6\n+        && strncmp (params->name, \"www4\", 4) != 0;\n+      if (do_ipv4 || do_ipv6)\n+        {\n+          fprintf (expected.out, \"name: %s\\n\", params->name);\n+          if (do_ipv4)\n+            for (int i = 0; i < params->count; ++i)\n+              fprintf (expected.out, \"address: 10.%s.%d.%d\\n\",\n+                       params->marker, i / 256, i % 256);\n+          if (do_ipv6)\n+            for (int i = 0; i < params->count; ++i)\n+              fprintf (expected.out, \"address: 2001:db8::%s:%x\\n\",\n+                       params->marker, i);\n+        }\n+      else\n+        fputs (\"error: HOST_NOT_FOUND\\n\", expected.out);\n+    }\n+  xfclose_memstream (&expected);\n+  return expected.buffer;\n+}\n+\n+/* Expected result of getaddrinfo.  */\n+static char *\n+expected_gai (const struct test_params *params)\n+{\n+  bool do_ipv4 = false;\n+  bool do_ipv6 = false;\n+  if (params->family == AF_UNSPEC)\n+    do_ipv4 = do_ipv6 = true;\n+  else if (params->family == AF_INET)\n+    do_ipv4 = true;\n+  else if (params->family == AF_INET6)\n+    do_ipv6 = true;\n+\n+  struct xmemstream expected;\n+  xopen_memstream (&expected);\n+  if (strcmp (params->name, \"alias.example.com\") == 0)\n+    {\n+      if (params->canonname)\n+        fprintf (expected.out,\n+                 \"flags: AI_CANONNAME\\n\"\n+                 \"canonname: %s\\n\",\n+                 params->name);\n+\n+      if (do_ipv4)\n+        for (int i = 0; i < params->count; ++i)\n+          fputs (\"address: STREAM/TCP 192.0.2.1 80\\n\", expected.out);\n+      if (do_ipv6)\n+        for (int i = 0; i < params->count; ++i)\n+          fputs (\"address: STREAM/TCP 2001:db8::1 80\\n\", expected.out);\n+    }\n+  else /* www/www4/www6 name.  */\n+    {\n+      if (strncmp (params->name, \"www4\", 4) == 0)\n+        do_ipv6 = false;\n+      else if (strncmp (params->name, \"www6\", 4) == 0)\n+        do_ipv4 = false;\n+      /* Otherwise, we have www as the name, so we do both.  */\n+\n+      if (do_ipv4 || do_ipv6)\n+        {\n+          if (params->canonname)\n+            fprintf (expected.out,\n+                     \"flags: AI_CANONNAME\\n\"\n+                     \"canonname: %s\\n\",\n+                     params->name);\n+\n+          if (do_ipv4)\n+            for (int i = 0; i < params->count; ++i)\n+              fprintf (expected.out, \"address: STREAM/TCP 10.%s.%d.%d 80\\n\",\n+                       params->marker, i / 256, i % 256);\n+          if (do_ipv6)\n+            for (int i = 0; i < params->count; ++i)\n+              fprintf (expected.out,\n+                       \"address: STREAM/TCP 2001:db8::%s:%x 80\\n\",\n+                       params->marker, i);\n+        }\n+      else\n+        fputs (\"error: Name or service not known\\n\", expected.out);\n+    }\n+  xfclose_memstream (&expected);\n+  return expected.buffer;\n+}\n+\n+static void\n+run_gbhn_gai (struct test_params *params)\n+{\n+  char *ctx = xasprintf (\"name=%s marker=%s count=%d family=%d\",\n+                         params->name, params->marker, params->count,\n+                         params->family);\n+  if (test_verbose > 0)\n+    printf (\"info: %s\\n\", ctx);\n+\n+  /* Check gethostbyname, gethostbyname2.  */\n+  if (params->family == AF_INET)\n+    {\n+      char *expected = expected_ghbn (params);\n+      check_hostent (ctx, gethostbyname (params->name), expected);\n+      free (expected);\n+    }\n+  if (params->family != AF_UNSPEC)\n+    {\n+      char *expected = expected_ghbn (params);\n+      check_hostent (ctx, gethostbyname2 (params->name, params->family),\n+                     expected);\n+      free (expected);\n+    }\n+\n+  /* Check getaddrinfo.  */\n+  for (int do_canonical = 0; do_canonical < 2; ++do_canonical)\n+    {\n+      params->canonname = do_canonical;\n+      char *expected = expected_gai (params);\n+      struct addrinfo hints =\n+        {\n+          .ai_family = params->family,\n+          .ai_socktype = SOCK_STREAM,\n+          .ai_protocol = IPPROTO_TCP,\n+        };\n+      if (do_canonical)\n+        hints.ai_flags |= AI_CANONNAME;\n+      struct addrinfo *ai;\n+      int ret = getaddrinfo (params->name, \"80\", &hints, &ai);\n+      check_addrinfo (ctx, ai, ret, expected);\n+      if (ret == 0)\n+        freeaddrinfo (ai);\n+      free (expected);\n+    }\n+\n+  free (ctx);\n+}\n+\n+/* Callback for the subprocess which runs the test in a chroot.  */\n+static void\n+subprocess (void *closure)\n+{\n+  struct test_params *params = closure;\n+\n+  xchroot (chroot_env->path_chroot);\n+\n+  static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC, -1 };\n+  static const char *const names[] =\n+    {\n+      \"www.example.com\", \"www4.example.com\", \"www6.example.com\",\n+      \"alias.example.com\",\n+      NULL\n+    };\n+  static const char *const names_marker[] = { \"46\", \"4\", \"6\", \"\" };\n+\n+  for (int family_idx = 0; families[family_idx] >= 0; ++family_idx)\n+    {\n+      params->family = families[family_idx];\n+      for (int names_idx = 0; names[names_idx] != NULL; ++names_idx)\n+        {\n+          params->name = names[names_idx];\n+          params->marker = names_marker[names_idx];\n+          run_gbhn_gai (params);\n+        }\n+    }\n+}\n+\n+/* Run the test for a specific number of addresses/aliases.  */\n+static void\n+run_test (int count)\n+{\n+  write_hosts (count);\n+\n+  struct test_params params =\n+    {\n+      .count = count,\n+    };\n+\n+  support_isolate_in_subprocess (subprocess, &params);\n+}\n+\n+static int\n+do_test (void)\n+{\n+  /* This test should not use gigabytes of memory.   */\n+  {\n+    struct rlimit limit;\n+    if (getrlimit (RLIMIT_AS, &limit) != 0)\n+      {\n+        printf (\"getrlimit (RLIMIT_AS) failed: %m\\n\");\n+        return 1;\n+      }\n+    long target = 200 * 1024 * 1024;\n+    if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)\n+      {\n+        limit.rlim_cur = target;\n+        if (setrlimit (RLIMIT_AS, &limit) != 0)\n+          {\n+            printf (\"setrlimit (RLIMIT_AS) failed: %m\\n\");\n+            return 1;\n+          }\n+      }\n+  }\n+\n+  __nss_configure_lookup (\"hosts\", \"files\");\n+  if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)\n+    FAIL_EXIT1 (\"could not load \" LIBNSS_DNS_SO \": %s\", dlerror ());\n+\n+  /* Run the tests with a few different address/alias counts.  */\n+  for (int count = 1; count <= 111; ++count)\n+    run_test (count);\n+  run_test (1111);\n+  run_test (22222);\n+\n+  support_chroot_free (chroot_env);\n+  return 0;\n+}\n+\n+#define PREPARE prepare\n+#include <support/test-driver.c>\n",
    "prefixes": []
}