public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
@ 2017-06-30 19:38 Florian Weimer
  2017-07-04  7:27 ` Andreas Schwab
  0 siblings, 1 reply; 17+ messages in thread
From: Florian Weimer @ 2017-06-30 19:38 UTC (permalink / raw)
  To: libc-alpha

This commit adds the remaining unchanging members (which are loaded
from /etc/resolv.conf) to struct resolv_conf.

The extended name server list is currently not used by the stub
resolver.  The switch depends on a cleanup: The _u._ext.nssocks
array stores just a single socket, and needs to be replaced with
a single socket value.

(The compatibility gethostname implementation does not use the
extended addres sort list, either.  Updating the compat code is
not worthwhile.)

2017-06-30  Florian Weimer  <fweimer@redhat.com>

	Mirror the entire resolver configuration in struct resolv_conf.
	* resolv/resolv_context.h (__resolv_context_nameserver_count)
	(__resolv_context_nameserver): New functions.
	(__resolv_context_sort_count, __resolv_context_sort_entry):
	Likewise.
	* resolv/nss_dns/dns-host.c (getanswer_r): Add struct
	resolv_context parameter.
	(gethostbyname3_context, _nss_dns_gethostbyaddr2_r): Adjust.
	(addrsort): Switch to struct resolv_context.
	* resolv/resolv_conf.h (struct resolv_sortlist_entry): Define.
	(struct resolv_conf): Add nameserver_list, nameserver_list_size,
	sort_list, sort_list_size, options, retrans, retry, ndots members.
	* resolv/resolv_conf.c (same_address_v4, same_address_v6)
	(same_address): New functions.
	(resolv_conf_matches): Compare the new array members.
	(__resolv_conf_allocate): Allocate and copy the new array members.
	(update_from_conf): Copy the entire configuration.
	* resolv/res_init.c (struct nameserver_list, struct sort_list):
	Define using dynarray.
	(struct resolv_conf_parser): Add nameserver_list, sort_list,
	template members.
	(resolv_conf_parser_init): Add preinit argument.  Initialize the
	new members.
	(resolv_conf_parser_free): Deallocate the new arrays.
	(allocate_address_v4): New function.
	(res_setoptions): Switch to struct resolv_conf_parser * parameter.
	(res_vinit_1): Drop res_state parameter.  Write all parsed date to
	the parser object instead.  Use allocate_address_v4 to allocate
	IPv4 addresses.
	(__res_vinit): Adjust.
	* resolv/tst-resolv-res_init-skeleton.c (print_resp): Print the
	extended name server list.
	(test_cases): Adjust.

diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 9d7ceb1..7cd54ab 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -108,7 +108,8 @@ typedef union querybuf
   u_char buf[MAXPACKET];
 } querybuf;
 
-static enum nss_status getanswer_r (const querybuf *answer, int anslen,
+static enum nss_status getanswer_r (struct resolv_context *ctx,
+				    const querybuf *answer, int anslen,
 				    const char *qname, int qtype,
 				    struct hostent *result, char *buffer,
 				    size_t buflen, int *errnop, int *h_errnop,
@@ -264,8 +265,9 @@ gethostbyname3_context (struct resolv_context *ctx,
       result->h_length = INADDRSZ;
     }
 
-  status = getanswer_r (host_buffer.buf, n, name, type, result, buffer, buflen,
-			errnop, h_errnop, map, ttlp, canonp);
+  status = getanswer_r
+    (ctx, host_buffer.buf, n, name, type, result, buffer, buflen,
+     errnop, h_errnop, map, ttlp, canonp);
   if (host_buffer.buf != orig_host_buffer)
     free (host_buffer.buf);
   return status;
@@ -522,8 +524,9 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
       return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
     }
 
-  status = getanswer_r (host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen,
-			errnop, h_errnop, 0 /* XXX */, ttlp, NULL);
+  status = getanswer_r
+    (ctx, host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen,
+     errnop, h_errnop, 0 /* XXX */, ttlp, NULL);
   if (host_buffer.buf != orig_host_buffer)
     free (host_buffer.buf);
   if (status != NSS_STATUS_SUCCESS)
@@ -553,25 +556,27 @@ _nss_dns_gethostbyaddr_r (const void *addr, socklen_t len, int af,
 				    errnop, h_errnop, NULL);
 }
 
-static void addrsort (char **ap, int num);
-
 static void
-addrsort (char **ap, int num)
+addrsort (struct resolv_context *ctx, char **ap, int num)
 {
   int i, j;
   char **p;
   short aval[MAX_NR_ADDRS];
   int needsort = 0;
+  size_t nsort = __resolv_context_sort_count (ctx);
 
   p = ap;
   if (num > MAX_NR_ADDRS)
     num = MAX_NR_ADDRS;
   for (i = 0; i < num; i++, p++)
     {
-      for (j = 0 ; (unsigned)j < _res.nsort; j++)
-	if (_res.sort_list[j].addr.s_addr ==
-	    (((struct in_addr *)(*p))->s_addr & _res.sort_list[j].mask))
-	  break;
+      for (j = 0 ; (unsigned)j < nsort; j++)
+	{
+	  struct resolv_sortlist_entry e
+	    = __resolv_context_sort_entry (ctx, j);
+	  if (e.addr.s_addr == (((struct in_addr *)(*p))->s_addr & e.mask))
+	    break;
+	}
       aval[i] = j;
       if (needsort == 0 && i > 0 && j < aval[i-1])
 	needsort = i;
@@ -598,7 +603,8 @@ addrsort (char **ap, int num)
 }
 
 static enum nss_status
-getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
+getanswer_r (struct resolv_context *ctx,
+	     const querybuf *answer, int anslen, const char *qname, int qtype,
 	     struct hostent *result, char *buffer, size_t buflen,
 	     int *errnop, int *h_errnop, int map, int32_t *ttlp, char **canonp)
 {
@@ -961,8 +967,9 @@ getanswer_r (const querybuf *answer, int anslen, const char *qname, int qtype,
        * in its return structures - should give it the "best"
        * address in that case, not some random one
        */
-      if (_res.nsort && haveanswer > 1 && qtype == T_A)
-	addrsort (host_data->h_addr_ptrs, haveanswer);
+      if (haveanswer > 1 && qtype == T_A
+	  && __resolv_context_sort_count (ctx) > 0)
+	addrsort (ctx, host_data->h_addr_ptrs, haveanswer);
 
       if (result->h_name == NULL)
 	{
diff --git a/resolv/res_init.c b/resolv/res_init.c
index 5941d37..80a21fb 100644
--- a/resolv/res_init.c
+++ b/resolv/res_init.c
@@ -104,7 +104,6 @@
 #include <errno.h>
 #include <resolv_conf.h>
 
-static void res_setoptions (res_state, const char *);
 static uint32_t net_mask (struct in_addr);
 
 unsigned long long int __res_initstamp;
@@ -124,28 +123,70 @@ is_sort_mask (char ch)
   return ch == '/' || ch == '&';
 }
 
+/* Array of name server addresses.  */
+#define DYNARRAY_STRUCT nameserver_list
+#define DYNARRAY_ELEMENT const struct sockaddr *
+#define DYNARRAY_ELEMENT_FREE(e) free ((struct sockaddr *) *(e))
+#define DYNARRAY_INITIAL_SIZE 3
+#define DYNARRAY_PREFIX nameserver_list_
+#include <malloc/dynarray-skeleton.c>
+
 /* Array of strings for the search array.  The backing store is
    managed separately.  */
 #define DYNARRAY_STRUCT search_list
 #define DYNARRAY_ELEMENT const char *
-#define DYNARRAY_INITIAL_SIZE 4
+#define DYNARRAY_INITIAL_SIZE 6
 #define DYNARRAY_PREFIX search_list_
 #include <malloc/dynarray-skeleton.c>
 
+/* Array of name server addresses.  */
+#define DYNARRAY_STRUCT sort_list
+#define DYNARRAY_ELEMENT struct resolv_sortlist_entry
+#define DYNARRAY_INITIAL_SIZE 0
+#define DYNARRAY_PREFIX sort_list_
+#include <malloc/dynarray-skeleton.c>
+
 /* resolv.conf parser state and results.  */
 struct resolv_conf_parser
 {
   char *buffer;            /* Temporary buffer for reading lines.  */
+
+  struct nameserver_list nameserver_list; /* Nameserver addresses.  */
+
   char *search_list_store; /* Backing storage for search list entries.  */
   struct search_list search_list; /* Points into search_list_store.  */
+
+  struct sort_list sort_list;   /* Address preference sorting list.  */
+
+  /* Configuration template.  The non-array elements are filled in
+     directly.  The array elements are updated prior to the call to
+     __resolv_conf_attach.  */
+  struct resolv_conf template;
 };
 
 static void
-resolv_conf_parser_init (struct resolv_conf_parser *parser)
+resolv_conf_parser_init (struct resolv_conf_parser *parser,
+                         const struct __res_state *preinit)
 {
   parser->buffer = NULL;
   parser->search_list_store = NULL;
+  nameserver_list_init (&parser->nameserver_list);
   search_list_init (&parser->search_list);
+  sort_list_init (&parser->sort_list);
+
+  if (preinit != NULL)
+    {
+      parser->template.retrans = preinit->retrans;
+      parser->template.retry = preinit->retry;
+      parser->template.options = preinit->options | RES_INIT;
+    }
+  else
+    {
+      parser->template.retrans = RES_TIMEOUT;
+      parser->template.retry = RES_DFLRETRY;
+      parser->template.options = RES_DEFAULT | RES_INIT;
+    }
+  parser->template.ndots = 1;
 }
 
 static void
@@ -153,7 +194,23 @@ resolv_conf_parser_free (struct resolv_conf_parser *parser)
 {
   free (parser->buffer);
   free (parser->search_list_store);
+  nameserver_list_free (&parser->nameserver_list);
   search_list_free (&parser->search_list);
+  sort_list_free (&parser->sort_list);
+}
+
+/* Allocate a struct sockaddr_in object on the heap, with the
+   specified address and port.  */
+static struct sockaddr *
+allocate_address_v4 (struct in_addr a, uint16_t port)
+{
+  struct sockaddr_in *sa4 = malloc (sizeof (*sa4));
+  if (sa4 == NULL)
+    return NULL;
+  sa4->sin_family = AF_INET;
+  sa4->sin_addr = a;
+  sa4->sin_port = htons (port);
+  return (struct sockaddr *) sa4;
 }
 
 /* Try to obtain the domain name from the host name and store it in
@@ -181,40 +238,17 @@ domain_from_hostname (char **result)
   return true;
 }
 
+static void res_setoptions (struct resolv_conf_parser *, const char *options);
+
 /* Internal helper function for __res_vinit, to aid with resource
    deallocation and error handling.  Return true on success, false on
    failure.  */
 static bool
-res_vinit_1 (res_state statp, bool preinit, FILE *fp,
-             struct resolv_conf_parser *parser)
+res_vinit_1 (FILE *fp, struct resolv_conf_parser *parser)
 {
   char *cp;
   size_t buffer_size = 0;
-  int nserv = 0;    /* Number of nameservers read from file.  */
-  bool have_serv6 = false;
   bool haveenv = false;
-  int nsort = 0;
-  char *net;
-
-  if (!preinit)
-    {
-      statp->retrans = RES_TIMEOUT;
-      statp->retry = RES_DFLRETRY;
-      statp->options = RES_DEFAULT;
-      statp->id = res_randomid ();
-    }
-
-  statp->nscount = 0;
-  statp->defdname[0] = '\0';
-  statp->ndots = 1;
-  statp->pfcode = 0;
-  statp->_vcsock = -1;
-  statp->_flags = 0;
-  statp->__glibc_unused_qhook = NULL;
-  statp->__glibc_unused_rhook = NULL;
-  statp->_u._ext.nscount = 0;
-  for (int n = 0; n < MAXNS; n++)
-    statp->_u._ext.nsaddrs[n] = NULL;
 
   /* Allow user to override the local domain definition.  */
   if ((cp = getenv ("LOCALDOMAIN")) != NULL)
@@ -350,19 +384,19 @@ res_vinit_1 (res_state statp, bool preinit, FILE *fp,
               continue;
             }
           /* Read nameservers to query.  */
-          if (MATCH (parser->buffer, "nameserver") && nserv < MAXNS)
+          if (MATCH (parser->buffer, "nameserver"))
             {
               struct in_addr a;
 
               cp = parser->buffer + sizeof ("nameserver") - 1;
               while (*cp == ' ' || *cp == '\t')
                 cp++;
+              struct sockaddr *sa;
               if ((*cp != '\0') && (*cp != '\n') && __inet_aton (cp, &a))
                 {
-                  statp->nsaddr_list[nserv].sin_addr = a;
-                  statp->nsaddr_list[nserv].sin_family = AF_INET;
-                  statp->nsaddr_list[nserv].sin_port = htons (NAMESERVER_PORT);
-                  nserv++;
+                  sa = allocate_address_v4 (a, NAMESERVER_PORT);
+                  if (sa == NULL)
+                    return false;
                 }
               else
                 {
@@ -392,13 +426,18 @@ res_vinit_1 (res_state statp, bool preinit, FILE *fp,
                            compatibility.  */
                         __inet6_scopeid_pton
                           (&a6, el + 1, &sa6->sin6_scope_id);
-
-                      statp->nsaddr_list[nserv].sin_family = 0;
-                      statp->_u._ext.nsaddrs[nserv] = sa6;
-                      statp->_u._ext.nssocks[nserv] = -1;
-                      have_serv6 = true;
-                      nserv++;
+                      sa = (struct sockaddr *) sa6;
                     }
+                  else
+                    /* IPv6 address parse failure.  */
+                    sa = NULL;
+                }
+              if (sa != NULL)
+                {
+                  const struct sockaddr **p = nameserver_list_emplace
+                    (&parser->nameserver_list);
+                  if (p != NULL)
+                    *p = sa;
                 }
               continue;
             }
@@ -407,21 +446,22 @@ res_vinit_1 (res_state statp, bool preinit, FILE *fp,
               struct in_addr a;
 
               cp = parser->buffer + sizeof ("sortlist") - 1;
-              while (nsort < MAXRESOLVSORT)
+              while (true)
                 {
                   while (*cp == ' ' || *cp == '\t')
                     cp++;
                   if (*cp == '\0' || *cp == '\n' || *cp == ';')
                     break;
-                  net = cp;
+                  char *net = cp;
                   while (*cp && !is_sort_mask (*cp) && *cp != ';'
                          && isascii (*cp) && !isspace (*cp))
                     cp++;
                   char separator = *cp;
                   *cp = 0;
+                  struct resolv_sortlist_entry e;
                   if (__inet_aton (net, &a))
                     {
-                      statp->sort_list[nsort].addr = a;
+                      e.addr = a;
                       if (is_sort_mask (separator))
                         {
                           *cp++ = separator;
@@ -432,15 +472,13 @@ res_vinit_1 (res_state statp, bool preinit, FILE *fp,
                           separator = *cp;
                           *cp = 0;
                           if (__inet_aton (net, &a))
-                            statp->sort_list[nsort].mask = a.s_addr;
+                            e.mask = a.s_addr;
                           else
-                            statp->sort_list[nsort].mask
-                              = net_mask (statp->sort_list[nsort].addr);
+                            e.mask = net_mask (e.addr);
                         }
                       else
-                        statp->sort_list[nsort].mask
-                          = net_mask (statp->sort_list[nsort].addr);
-                      nsort++;
+                        e.mask = net_mask (e.addr);
+                      sort_list_add (&parser->sort_list, e);
                     }
                   *cp = separator;
                 }
@@ -448,23 +486,22 @@ res_vinit_1 (res_state statp, bool preinit, FILE *fp,
             }
           if (MATCH (parser->buffer, "options"))
             {
-              res_setoptions (statp, parser->buffer + sizeof ("options") - 1);
+              res_setoptions (parser, parser->buffer + sizeof ("options") - 1);
               continue;
             }
         }
-      statp->nscount = nserv;
-      if (have_serv6)
-        /* We try IPv6 servers again.  */
-        statp->ipv6_unavail = false;
-      statp->nsort = nsort;
       fclose (fp);
     }
-  if (__glibc_unlikely (statp->nscount == 0))
+  if (__glibc_unlikely (nameserver_list_size (&parser->nameserver_list) == 0))
     {
-      statp->nsaddr.sin_addr = __inet_makeaddr (IN_LOOPBACKNET, 1);
-      statp->nsaddr.sin_family = AF_INET;
-      statp->nsaddr.sin_port = htons (NAMESERVER_PORT);
-      statp->nscount = 1;
+      const struct sockaddr **p
+        = nameserver_list_emplace (&parser->nameserver_list);
+      if (p == NULL)
+        return false;
+      *p = allocate_address_v4 (__inet_makeaddr (IN_LOOPBACKNET, 1),
+                                NAMESERVER_PORT);
+      if (*p == NULL)
+        return false;
     }
 
   if (search_list_size (&parser->search_list) == 0)
@@ -481,15 +518,16 @@ res_vinit_1 (res_state statp, bool preinit, FILE *fp,
     }
 
   if ((cp = getenv ("RES_OPTIONS")) != NULL)
-    res_setoptions (statp, cp);
+    res_setoptions (parser, cp);
 
-  if (search_list_has_failed (&parser->search_list))
+  if (nameserver_list_has_failed (&parser->nameserver_list)
+      || search_list_has_failed (&parser->search_list)
+      || sort_list_has_failed (&parser->sort_list))
     {
       __set_errno (ENOMEM);
       return false;
     }
 
-  statp->options |= RES_INIT;
   return true;
 }
 
@@ -525,17 +563,27 @@ __res_vinit (res_state statp, int preinit)
       }
 
   struct resolv_conf_parser parser;
-  resolv_conf_parser_init (&parser);
-  bool ok = res_vinit_1 (statp, preinit, fp, &parser);
+  if (preinit)
+    {
+      resolv_conf_parser_init (&parser, statp);
+      statp->id = res_randomid ();
+    }
+  else
+    resolv_conf_parser_init (&parser, NULL);
 
+  bool ok = res_vinit_1 (fp, &parser);
   if (ok)
     {
-      struct resolv_conf init =
-        {
-          .search_list = search_list_begin (&parser.search_list),
-          .search_list_size = search_list_size (&parser.search_list),
-        };
-      struct resolv_conf *conf = __resolv_conf_allocate (&init);
+      parser.template.nameserver_list
+        = nameserver_list_begin (&parser.nameserver_list);
+      parser.template.nameserver_list_size
+        = nameserver_list_size (&parser.nameserver_list);
+      parser.template.search_list = search_list_begin (&parser.search_list);
+      parser.template.search_list_size
+        = search_list_size (&parser.search_list);
+      parser.template.sort_list = sort_list_begin (&parser.sort_list);
+      parser.template.sort_list_size = sort_list_size (&parser.sort_list);
+      struct resolv_conf *conf = __resolv_conf_allocate (&parser.template);
       if (conf == NULL)
         ok = false;
       else
@@ -547,18 +595,13 @@ __res_vinit (res_state statp, int preinit)
   resolv_conf_parser_free (&parser);
 
   if (!ok)
-    {
-      /* Deallocate the name server addresses which have been
-         allocated.  */
-      for (int n = 0; n < MAXNS; n++)
-        free (statp->_u._ext.nsaddrs[n]);
-      return -1;
-    }
-  return 0;
+    return -1;
+  else
+    return 0;
 }
 
 static void
-res_setoptions (res_state statp, const char *options)
+res_setoptions (struct resolv_conf_parser *parser, const char *options)
 {
   const char *cp = options;
 
@@ -572,25 +615,25 @@ res_setoptions (res_state statp, const char *options)
         {
           int i = atoi (cp + sizeof ("ndots:") - 1);
           if (i <= RES_MAXNDOTS)
-            statp->ndots = i;
+            parser->template.ndots = i;
           else
-            statp->ndots = RES_MAXNDOTS;
+            parser->template.ndots = RES_MAXNDOTS;
         }
       else if (!strncmp (cp, "timeout:", sizeof ("timeout:") - 1))
         {
           int i = atoi (cp + sizeof ("timeout:") - 1);
           if (i <= RES_MAXRETRANS)
-            statp->retrans = i;
+            parser->template.retrans = i;
           else
-            statp->retrans = RES_MAXRETRANS;
+            parser->template.retrans = RES_MAXRETRANS;
         }
       else if (!strncmp (cp, "attempts:", sizeof ("attempts:") - 1))
         {
           int i = atoi (cp + sizeof ("attempts:") - 1);
           if (i <= RES_MAXRETRY)
-            statp->retry = i;
+            parser->template.retry = i;
           else
-            statp->retry = RES_MAXRETRY;
+            parser->template.retry = RES_MAXRETRY;
         }
       else
         {
@@ -616,9 +659,9 @@ res_setoptions (res_state statp, const char *options)
             if (strncmp (cp, options[i].str, options[i].len) == 0)
               {
                 if (options[i].clear)
-                  statp->options &= options[i].flag;
+                  parser->template.options &= options[i].flag;
                 else
-                  statp->options |= options[i].flag;
+                  parser->template.options |= options[i].flag;
                 break;
               }
         }
diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c
index 3a730ff..cd2f60d 100644
--- a/resolv/resolv_conf.c
+++ b/resolv/resolv_conf.c
@@ -106,11 +106,80 @@ resolv_conf_get_1 (const struct __res_state *resp)
   return conf;
 }
 
+/* Return true if both IPv4 addresses are equal.  */
+static bool
+same_address_v4 (const struct sockaddr_in *left,
+                 const struct sockaddr_in *right)
+{
+  return left->sin_addr.s_addr == right->sin_addr.s_addr
+    && left->sin_port == right->sin_port;
+}
+
+/* Return true if both IPv6 addresses are equal.  This ignores the
+   flow label.  */
+static bool
+same_address_v6 (const struct sockaddr_in6 *left,
+                 const struct sockaddr_in6 *right)
+{
+  return memcmp (&left->sin6_addr, &right->sin6_addr,
+                 sizeof (left->sin6_addr)) == 0
+    && left->sin6_port == right->sin6_port
+    && left->sin6_scope_id == right->sin6_scope_id;
+}
+
+static bool
+same_address (const struct sockaddr *left, const struct sockaddr *right)
+{
+  if (left->sa_family != right->sa_family)
+    return false;
+  switch (left->sa_family)
+    {
+    case AF_INET:
+      return same_address_v4 ((const struct sockaddr_in *) left,
+                              (const struct sockaddr_in *) right);
+    case AF_INET6:
+      return same_address_v6 ((const struct sockaddr_in6 *) left,
+                              (const struct sockaddr_in6 *) right);
+    }
+  return false;
+}
+
 /* Check that *RESP and CONF match.  Used by __resolv_conf_get.  */
 static bool
 resolv_conf_matches (const struct __res_state *resp,
                      const struct resolv_conf *conf)
 {
+  /* NB: Do not compare the options, retrans, retry, ndots.  These can
+     be changed by applicaiton.  */
+
+  /* Check that the name servers in *RESP have not been modified by
+     the application.  */
+  {
+    size_t nserv = conf->nameserver_list_size;
+    if (nserv > MAXNS)
+      nserv = MAXNS;
+    /* _ext.nscount is 0 until initialized by res_send.c.  */
+    if (resp->nscount != nserv
+        && (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
+      return false;
+    for (size_t i = 0; i < nserv; ++i)
+      {
+        if (resp->nsaddr_list[i].sin_family == 0)
+          {
+            if (resp->_u._ext.nsaddrs[i]->sin6_family != AF_INET6)
+              return false;
+            if (!same_address ((struct sockaddr *) resp->_u._ext.nsaddrs[i],
+                               conf->nameserver_list[i]))
+              return false;
+          }
+        else if (resp->nsaddr_list[i].sin_family != AF_INET)
+          return false;
+        else if (!same_address ((struct sockaddr *) &resp->nsaddr_list[i],
+                                conf->nameserver_list[i]))
+          return false;
+      }
+  }
+
   /* Check that the search list in *RESP has not been modified by the
      application.  */
   {
@@ -139,6 +208,18 @@ resolv_conf_matches (const struct __res_state *resp,
           }
       }
   }
+
+  /* Check that the sort list has not been modified.  */
+  {
+    size_t nsort = conf->sort_list_size;
+    if (nsort > MAXRESOLVSORT)
+      nsort = MAXRESOLVSORT;
+    for (size_t i = 0; i < nsort; ++i)
+      if (resp->sort_list[i].addr.s_addr != conf->sort_list[i].addr.s_addr
+          || resp->sort_list[i].mask != conf->sort_list[i].mask)
+        return false;
+  }
+
   return true;
 }
 
@@ -172,7 +253,28 @@ __resolv_conf_put (struct resolv_conf *conf)
 struct resolv_conf *
 __resolv_conf_allocate (const struct resolv_conf *init)
 {
-  /* Space needed by the strings.  */
+  /* Allocate in decreasing order of alignment.  */
+  _Static_assert (__alignof__ (const char *const *)
+                  <= __alignof__ (struct resolv_conf), "alignment");
+  _Static_assert (__alignof__ (struct sockaddr_in6)
+                  <= __alignof__ (const char *const *), "alignment");
+  _Static_assert (__alignof__ (struct sockaddr_in)
+                  ==  __alignof__ (struct sockaddr_in6), "alignment");
+  _Static_assert (__alignof__ (struct resolv_sortlist_entry)
+                  <= __alignof__ (struct sockaddr_in), "alignment");
+
+  /* Space needed by the nameserver addresses.  */
+  size_t address_space = 0;
+  for (size_t i = 0; i < init->nameserver_list_size; ++i)
+    if (init->nameserver_list[i]->sa_family == AF_INET)
+      address_space += sizeof (struct sockaddr_in);
+    else
+      {
+        assert (init->nameserver_list[i]->sa_family == AF_INET6);
+        address_space += sizeof (struct sockaddr_in6);
+      }
+
+  /* Space needed by the search list strings.  */
   size_t string_space = 0;
   for (size_t i = 0; i < init->search_list_size; ++i)
     string_space += strlen (init->search_list[i]) + 1;
@@ -181,7 +283,10 @@ __resolv_conf_allocate (const struct resolv_conf *init)
   void *ptr;
   struct alloc_buffer buffer = alloc_buffer_allocate
     (sizeof (struct resolv_conf)
+     + init->nameserver_list_size * sizeof (init->nameserver_list[0])
+     + address_space
      + init->search_list_size * sizeof (init->search_list[0])
+     + init->sort_list_size * sizeof (init->sort_list[0])
      + string_space,
      &ptr);
   struct resolv_conf *conf
@@ -193,16 +298,57 @@ __resolv_conf_allocate (const struct resolv_conf *init)
 
   /* Initialize the contents.  */
   conf->__refcount = 1;
+  conf->retrans = init->retrans;
+  conf->retry = init->retry;
+  conf->options = init->options;
+  conf->ndots = init->ndots;
   conf->initstamp = __res_initstamp;
 
-  /* Allocate and fill the search list array.  */
+  /* Allocate the arrays with pointers.  These must come first because
+     they have the highets alignment.  */
+  conf->nameserver_list_size = init->nameserver_list_size;
+  const struct sockaddr **nameserver_array = alloc_buffer_alloc_array
+    (&buffer, const struct sockaddr *, init->nameserver_list_size);
+  conf->nameserver_list = nameserver_array;
+
+  conf->search_list_size = init->search_list_size;
+  const char **search_array = alloc_buffer_alloc_array
+    (&buffer, const char *, init->search_list_size);
+  conf->search_list = search_array;
+
+  /* Fill the name server list array.  */
+  for (size_t i = 0; i < init->nameserver_list_size; ++i)
+    if (init->nameserver_list[i]->sa_family == AF_INET)
+      {
+        struct sockaddr_in *sa = alloc_buffer_alloc
+          (&buffer, struct sockaddr_in);
+        *sa = *(struct sockaddr_in *) init->nameserver_list[i];
+        nameserver_array[i] = (struct sockaddr *) sa;
+      }
+    else
+      {
+        struct sockaddr_in6 *sa = alloc_buffer_alloc
+          (&buffer, struct sockaddr_in6);
+        *sa = *(struct sockaddr_in6 *) init->nameserver_list[i];
+        nameserver_array[i] = (struct sockaddr *) sa;
+      }
+
+  /* Allocate and fill the sort list array.  */
+  {
+    conf->sort_list_size = init->sort_list_size;
+    struct resolv_sortlist_entry *array = alloc_buffer_alloc_array
+      (&buffer, struct resolv_sortlist_entry, init->sort_list_size);
+    conf->sort_list = array;
+    for (size_t i = 0; i < init->sort_list_size; ++i)
+      array[i] = init->sort_list[i];
+  }
+
+  /* Fill the search list array.  This must come last because the
+     strings are the least aligned part of the allocation.  */
   {
-    conf->search_list_size = init->search_list_size;
-    const char **array = alloc_buffer_alloc_array
-      (&buffer, const char *, init->search_list_size);
-    conf->search_list = array;
     for (size_t i = 0; i < init->search_list_size; ++i)
-      array[i] = alloc_buffer_copy_string (&buffer, init->search_list[i]);
+      search_array[i] = alloc_buffer_copy_string
+        (&buffer, init->search_list[i]);
   }
 
   assert (!alloc_buffer_has_failed (&buffer));
@@ -213,6 +359,56 @@ __resolv_conf_allocate (const struct resolv_conf *init)
 static __attribute__ ((nonnull (1, 2), warn_unused_result)) bool
 update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
 {
+  resp->defdname[0] = '\0';
+  resp->pfcode = 0;
+  resp->_vcsock = -1;
+  resp->_flags = 0;
+  resp->ipv6_unavail = false;
+  resp->__glibc_unused_qhook = NULL;
+  resp->__glibc_unused_rhook = NULL;
+
+  resp->retrans = conf->retrans;
+  resp->retry = conf->retry;
+  resp->options = conf->options;
+  resp->ndots = conf->ndots;
+
+  /* Copy the name server addresses.  */
+  {
+    resp->nscount = 0;
+    resp->_u._ext.nscount = 0;
+    size_t nserv = conf->nameserver_list_size;
+    if (nserv > MAXNS)
+      nserv = MAXNS;
+    for (size_t i = 0; i < nserv; i++)
+      {
+        if (conf->nameserver_list[i]->sa_family == AF_INET)
+          {
+            resp->nsaddr_list[i]
+              = *(struct sockaddr_in *)conf->nameserver_list[i];
+            resp->_u._ext.nsaddrs[i] = NULL;
+          }
+        else
+          {
+            assert (conf->nameserver_list[i]->sa_family == AF_INET6);
+            resp->nsaddr_list[i].sin_family = 0;
+            /* Make a defensive copy of the name server address, in
+               case the application overwrites it.  */
+            struct sockaddr_in6 *sa = malloc (sizeof (*sa));
+            if (sa == NULL)
+              {
+                for (size_t j = 0; j < i; ++j)
+                  free (resp->_u._ext.nsaddrs[j]);
+                return false;
+              }
+            *sa = *(struct sockaddr_in6 *)conf->nameserver_list[i];
+            resp->_u._ext.nsaddrs[i] = sa;
+          }
+        resp->_u._ext.nssocks[i] = -1;
+      }
+    resp->nscount = nserv;
+    /* Leave resp->_u._ext.nscount at 0.  res_send.c handles this.  */
+  }
+
   /* Fill in the prefix of the search list.  It is truncated either at
      MAXDNSRCH, or if reps->defdname has insufficient space.  */
   {
@@ -231,6 +427,19 @@ update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
     resp->dnsrch[i] = NULL;
   }
 
+  /* Copy the sort list.  */
+  {
+    size_t nsort = conf->sort_list_size;
+    if (nsort > MAXRESOLVSORT)
+      nsort = MAXRESOLVSORT;
+    for (size_t i = 0; i < nsort; ++i)
+      {
+        resp->sort_list[i].addr = conf->sort_list[i].addr;
+        resp->sort_list[i].mask = conf->sort_list[i].mask;
+      }
+    resp->nsort = nsort;
+  }
+
   /* The overlapping parts of both configurations should agree after
      initialization.  */
   assert (resolv_conf_matches (resp, conf));
diff --git a/resolv/resolv_conf.h b/resolv/resolv_conf.h
index 80a0b93..7ca80cd 100644
--- a/resolv/resolv_conf.h
+++ b/resolv/resolv_conf.h
@@ -19,9 +19,17 @@
 #ifndef RESOLV_STATE_H
 #define RESOLV_STATE_H
 
+#include <netinet/in.h>
 #include <stdbool.h>
 #include <stddef.h>
 
+/* This type corresponds to members of the _res.sort_list array.  */
+struct resolv_sortlist_entry
+{
+  struct in_addr addr;
+  uint32_t mask;
+};
+
 /* Extended resolver state associated with res_state objects.  Client
    code can reach this state through a struct resolv_context
    object.  */
@@ -36,9 +44,24 @@ struct resolv_conf
      zero.  For internal use within resolv_conf only.  */
   size_t __refcount;
 
+  /* List of IPv4 and IPv6 name server addresses.  */
+  const struct sockaddr **nameserver_list;
+  size_t nameserver_list_size;
+
   /* The domain names forming the search list.  */
   const char *const *search_list;
   size_t search_list_size;
+
+  /* IPv4 address preference rules.  */
+  const struct resolv_sortlist_entry *sort_list;
+  size_t sort_list_size;
+
+  /* _res.options has type unsigned long, but we can only use 32 bits
+     for portability across all architectures.  */
+  unsigned int options;
+  unsigned int retrans;         /* Timeout.  */
+  unsigned int retry;           /* Number of times to retry.  */
+  unsigned int ndots; /* Dots needed for initial non-search query.  */
 };
 
 /* The functions below are for use by the res_init resolv.conf parser
diff --git a/resolv/resolv_context.h b/resolv/resolv_context.h
index 0f4d47d..e2f7ad6 100644
--- a/resolv/resolv_context.h
+++ b/resolv/resolv_context.h
@@ -112,6 +112,67 @@ __resolv_context_search_list (const struct resolv_context *ctx, size_t index)
   return NULL;
 }
 
+/* Return the number of name servers.  */
+static __attribute__ ((nonnull (1), unused)) size_t
+__resolv_context_nameserver_count (const struct resolv_context *ctx)
+{
+  if (ctx->conf != NULL)
+    return ctx->conf->nameserver_list_size;
+  else
+    return ctx->resp->nscount;
+}
+
+/* Return a pointer to the socket address of the name server INDEX, or
+   NULL if the index is out of bounds.  */
+static __attribute__ ((nonnull (1), unused)) const struct sockaddr *
+__resolv_context_nameserver (const struct resolv_context *ctx, size_t index)
+{
+  if (ctx->conf != NULL)
+    {
+      if (index < ctx->conf->nameserver_list_size)
+        return ctx->conf->nameserver_list[index];
+    }
+  else
+    if (index < ctx->resp->nscount)
+      {
+        if (ctx->resp->nsaddr_list[index].sin_family != 0)
+          return (const struct sockaddr *) &ctx->resp->nsaddr_list[index];
+        else
+          return (const struct sockaddr *) &ctx->resp->_u._ext.nsaddrs[index];
+      }
+  return NULL;
+}
+
+/* Return the number of sort list entries.  */
+static __attribute__ ((nonnull (1), unused)) size_t
+__resolv_context_sort_count (const struct resolv_context *ctx)
+{
+  if (ctx->conf != NULL)
+    return ctx->conf->sort_list_size;
+  else
+    return ctx->resp->nsort;
+}
+
+/* Return the sort list entry at INDEX.  */
+static __attribute__ ((nonnull (1), unused)) struct resolv_sortlist_entry
+__resolv_context_sort_entry (const struct resolv_context *ctx, size_t index)
+{
+  if (ctx->conf != NULL)
+    {
+      if (index < ctx->conf->sort_list_size)
+        return ctx->conf->sort_list[index];
+      /* Fall through.  */
+    }
+  else if (index < ctx->resp->nsort)
+    return (struct resolv_sortlist_entry)
+      {
+        .addr = ctx->resp->sort_list[index].addr,
+        .mask = ctx->resp->sort_list[index].mask,
+      };
+
+  return (struct resolv_sortlist_entry) { .mask = 0, };
+}
+
 /* Called during thread shutdown to free the associated resolver
    context (mostly in response to cancellation, otherwise the
    __resolv_context_get/__resolv_context_put pairing will already have
diff --git a/resolv/tst-resolv-res_init-skeleton.c b/resolv/tst-resolv-res_init-skeleton.c
index d377a6b..aace94d 100644
--- a/resolv/tst-resolv-res_init-skeleton.c
+++ b/resolv/tst-resolv-res_init-skeleton.c
@@ -242,6 +242,46 @@ print_resp (FILE *fp, res_state resp)
         }
     }
 
+  /* The extended name server list.  */
+  {
+    size_t i = 0;
+    while (true)
+      {
+        const struct sockaddr *addr = __resolv_context_nameserver (ctx, i);
+        if (addr == NULL)
+          break;
+        size_t addrlen;
+        switch (addr->sa_family)
+          {
+          case AF_INET:
+            addrlen = sizeof (struct sockaddr_in);
+            break;
+          case AF_INET6:
+            addrlen = sizeof (struct sockaddr_in6);
+            break;
+          default:
+            FAIL_EXIT1 ("invalid address family %d", addr->sa_family);
+          }
+
+        char host[NI_MAXHOST];
+        char service[NI_MAXSERV];
+        int ret = getnameinfo (addr, addrlen,
+                               host, sizeof (host), service, sizeof (service),
+                               NI_NUMERICHOST | NI_NUMERICSERV);
+
+        if (ret != 0)
+          {
+            if (ret == EAI_SYSTEM)
+              fprintf (fp, "; error: getnameinfo: %m\n");
+            else
+              fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret));
+          }
+        else
+          fprintf (fp, "; nameserver[%zu]: [%s]:%s\n", i, host, service);
+        ++i;
+      }
+  }
+
   TEST_VERIFY (!ferror (fp));
 
   __resolv_context_put (ctx);
@@ -391,12 +431,14 @@ struct test_case test_cases[] =
      .expected = "search example.com\n"
      "; search[0]: example.com\n"
      "nameserver 127.0.0.1\n"
+     "; nameserver[0]: [127.0.0.1]:53\n"
     },
     {.name = "empty file with LOCALDOMAIN",
      .conf = "",
      .expected = "search example.net\n"
      "; search[0]: example.net\n"
-     "nameserver 127.0.0.1\n",
+     "nameserver 127.0.0.1\n"
+     "; nameserver[0]: [127.0.0.1]:53\n",
      .localdomain = "example.net",
     },
     {.name = "empty file with RES_OPTIONS",
@@ -404,7 +446,8 @@ struct test_case test_cases[] =
      .expected = "options attempts:5 edns0\n"
      "search example.com\n"
      "; search[0]: example.com\n"
-     "nameserver 127.0.0.1\n",
+     "nameserver 127.0.0.1\n"
+     "; nameserver[0]: [127.0.0.1]:53\n",
      .res_options = "edns0 attempts:5",
     },
     {.name = "empty file with RES_OPTIONS and LOCALDOMAIN",
@@ -412,7 +455,8 @@ struct test_case test_cases[] =
      .expected = "options attempts:5 edns0\n"
      "search example.org\n"
      "; search[0]: example.org\n"
-     "nameserver 127.0.0.1\n",
+     "nameserver 127.0.0.1\n"
+     "; nameserver[0]: [127.0.0.1]:53\n",
      .localdomain = "example.org",
      .res_options = "edns0 attempts:5",
     },
@@ -424,6 +468,7 @@ struct test_case test_cases[] =
      "; search[0]: corp.example.com\n"
      "; search[1]: example.com\n"
      "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
     },
     {.name = "whitespace",
      .conf = "# This test covers comment and whitespace processing "
@@ -440,6 +485,8 @@ struct test_case test_cases[] =
      "; search[1]: example.com\n"
      "nameserver 192.0.2.1\n"
      "nameserver 192.0.2.2\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
+     "; nameserver[1]: [192.0.2.2]:53\n"
     },
     {.name = "domain",
      .conf = "domain example.net\n"
@@ -447,6 +494,7 @@ struct test_case test_cases[] =
      .expected = "search example.net\n"
      "; search[0]: example.net\n"
      "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
     },
     {.name = "domain space",
      .conf = "domain example.net \n"
@@ -454,6 +502,7 @@ struct test_case test_cases[] =
      .expected = "search example.net\n"
      "; search[0]: example.net\n"
      "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
     },
     {.name = "domain tab",
      .conf = "domain example.net\t\n"
@@ -461,6 +510,7 @@ struct test_case test_cases[] =
      .expected = "search example.net\n"
      "; search[0]: example.net\n"
      "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
     },
     {.name = "domain override",
      .conf = "search example.com example.org\n"
@@ -469,6 +519,7 @@ struct test_case test_cases[] =
      .expected = "search example.net\n"
      "; search[0]: example.net\n"
      "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
     },
     {.name = "option values, multiple servers",
      .conf = "options\tinet6\tndots:3 edns0\tattempts:5\ttimeout:19\n"
@@ -485,6 +536,9 @@ struct test_case test_cases[] =
      "nameserver 192.0.2.1\n"
      "nameserver ::1\n"
      "nameserver 192.0.2.2\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
+     "; nameserver[1]: [::1]:53\n"
+     "; nameserver[2]: [192.0.2.2]:53\n"
     },
     {.name = "out-of-range option vales",
      .conf = "options use-vc timeout:999 attempts:999 ndots:99\n"
@@ -493,6 +547,7 @@ struct test_case test_cases[] =
      "search example.com\n"
      "; search[0]: example.com\n"
      "nameserver 127.0.0.1\n"
+     "; nameserver[0]: [127.0.0.1]:53\n"
     },
     {.name = "repeated directives",
      .conf = "options ndots:3 use-vc\n"
@@ -505,6 +560,7 @@ struct test_case test_cases[] =
      "search example.org\n"
      "; search[0]: example.org\n"
      "nameserver 127.0.0.1\n"
+     "; nameserver[0]: [127.0.0.1]:53\n"
     },
     {.name = "many name servers, sortlist",
      .conf = "options single-request\n"
@@ -528,6 +584,14 @@ struct test_case test_cases[] =
      "nameserver 192.0.2.1\n"
      "nameserver 192.0.2.2\n"
      "nameserver 192.0.2.3\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
+     "; nameserver[1]: [192.0.2.2]:53\n"
+     "; nameserver[2]: [192.0.2.3]:53\n"
+     "; nameserver[3]: [192.0.2.4]:53\n"
+     "; nameserver[4]: [192.0.2.5]:53\n"
+     "; nameserver[5]: [192.0.2.6]:53\n"
+     "; nameserver[6]: [192.0.2.7]:53\n"
+     "; nameserver[7]: [192.0.2.8]:53\n"
     },
     {.name = "IPv4 and IPv6 nameservers",
      .conf = "options single-request\n"
@@ -554,6 +618,14 @@ struct test_case test_cases[] =
      "nameserver 192.0.2.1\n"
      "nameserver 2001:db8::2\n"
      "nameserver 192.0.2.3\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
+     "; nameserver[1]: [2001:db8::2]:53\n"
+     "; nameserver[2]: [192.0.2.3]:53\n"
+     "; nameserver[3]: [2001:db8::4]:53\n"
+     "; nameserver[4]: [192.0.2.5]:53\n"
+     "; nameserver[5]: [2001:db8::6]:53\n"
+     "; nameserver[6]: [192.0.2.7]:53\n"
+     "; nameserver[7]: [2001:db8::8]:53\n",
     },
     {.name = "garbage after nameserver",
      .conf = "nameserver 192.0.2.1 garbage\n"
@@ -563,6 +635,8 @@ struct test_case test_cases[] =
      "; search[0]: example.com\n"
      "nameserver 192.0.2.1\n"
      "nameserver 192.0.2.3\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
+     "; nameserver[1]: [192.0.2.3]:53\n"
     },
     {.name = "RES_OPTIONS is cummulative",
      .conf = "options timeout:7 ndots:2 use-vc\n"
@@ -570,7 +644,8 @@ struct test_case test_cases[] =
      .expected = "options ndots:3 timeout:7 attempts:5 use-vc edns0\n"
      "search example.com\n"
      "; search[0]: example.com\n"
-     "nameserver 192.0.2.1\n",
+     "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n",
      .res_options = "attempts:5 ndots:3 edns0 ",
     },
     {.name = "many search list entries (bug 19569)",
@@ -588,7 +663,8 @@ struct test_case test_cases[] =
      "; search[5]: example.com\n"
      "; search[6]: example.org\n"
      "; search[7]: example.net\n"
-     "nameserver 192.0.2.1\n",
+     "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
     },
     {.name = "very long search list entries (bug 21475)",
      .conf = "nameserver 192.0.2.1\n"
@@ -603,7 +679,8 @@ struct test_case test_cases[] =
      "; search[2]: " H63 "." D63 ".example.net\n"
 #undef H63
 #undef D63
-     "nameserver 192.0.2.1\n",
+     "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n"
     },
     { NULL }
   };

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-06-30 19:38 [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf Florian Weimer
@ 2017-07-04  7:27 ` Andreas Schwab
  2017-07-04  8:00   ` Florian Weimer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2017-07-04  7:27 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

This is breaking rpm:

https://build.opensuse.org/package/live_build_log/home:Andreas_Schwab:glibc/glibc/f/x86_64

rpm: resolv_conf.c:550: update_from_conf: Assertion `resolv_conf_matches (resp, conf)' failed.

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04  7:27 ` Andreas Schwab
@ 2017-07-04  8:00   ` Florian Weimer
  2017-07-04  8:08     ` Andreas Schwab
  2017-07-04 11:59     ` Andreas Schwab
  0 siblings, 2 replies; 17+ messages in thread
From: Florian Weimer @ 2017-07-04  8:00 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: libc-alpha

On 07/04/2017 09:27 AM, Andreas Schwab wrote:
> This is breaking rpm:
> 
> https://build.opensuse.org/package/live_build_log/home:Andreas_Schwab:glibc/glibc/f/x86_64
> 
> rpm: resolv_conf.c:550: update_from_conf: Assertion `resolv_conf_matches (resp, conf)' failed.

This has to be a property of the build root.  What's the contents of
/etc/resolv.conf?  What's the host name?

Thanks,
Florian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04  8:00   ` Florian Weimer
@ 2017-07-04  8:08     ` Andreas Schwab
  2017-07-04  9:34       ` Florian Weimer
  2017-07-04 11:59     ` Andreas Schwab
  1 sibling, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2017-07-04  8:08 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:

> This has to be a property of the build root.  What's the contents of
> /etc/resolv.conf?

There is none.

>  What's the host name?

Whatever the name of the build worker.

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04  8:08     ` Andreas Schwab
@ 2017-07-04  9:34       ` Florian Weimer
  2017-07-04 10:00         ` Andreas Schwab
  0 siblings, 1 reply; 17+ messages in thread
From: Florian Weimer @ 2017-07-04  9:34 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: libc-alpha

On 07/04/2017 10:08 AM, Andreas Schwab wrote:
> On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:
> 
>> This has to be a property of the build root.  What's the contents of
>> /etc/resolv.conf?
> 
> There is none.

The test suite covers this, and I don't see any failures.  I don't see
it in manual testing, either.

>>  What's the host name?
> 
> Whatever the name of the build worker.

Sorry, I cannot reproduce this.

Do you have any custom patches for resolv.conf handling?  Could you post
a link to your source repository?

(I've just fixed another resolv.conf handling problem, but it's
unrelated to your problem, I assume.)

Thanks,
Florian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04  9:34       ` Florian Weimer
@ 2017-07-04 10:00         ` Andreas Schwab
  2017-07-04 10:09           ` Florian Weimer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2017-07-04 10:00 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:

> Do you have any custom patches for resolv.conf handling?  

No.

> Could you post a link to your source repository?

Follow the links at the top.

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 10:00         ` Andreas Schwab
@ 2017-07-04 10:09           ` Florian Weimer
  2017-07-04 10:14             ` Andreas Schwab
  0 siblings, 1 reply; 17+ messages in thread
From: Florian Weimer @ 2017-07-04 10:09 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: libc-alpha

On 07/04/2017 12:00 PM, Andreas Schwab wrote:
> On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:
> 
>> Do you have any custom patches for resolv.conf handling?  
> 
> No.
> 
>> Could you post a link to your source repository?
> 
> Follow the links at the top.

I meant a link to something like the SRPM or the source repository from
which the SRPM is built.

I think the only way in which I could help is to provide a custom
logging patch which you could install temporarily during the build, and
we'll then see where it fails.  Would that work for you?

Thanks,
Florian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 10:09           ` Florian Weimer
@ 2017-07-04 10:14             ` Andreas Schwab
  2017-07-04 10:42               ` Florian Weimer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2017-07-04 10:14 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:

> I meant a link to something like the SRPM or the source repository from
> which the SRPM is built.

Yes, that's exactly linked from the top (Overview etc.).

> I think the only way in which I could help is to provide a custom
> logging patch which you could install temporarily during the build, and
> we'll then see where it fails.  Would that work for you?

Sure.  You can also build it yourself locally (you need an OBS account
for that though).

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 10:14             ` Andreas Schwab
@ 2017-07-04 10:42               ` Florian Weimer
  2017-07-04 11:40                 ` Andreas Schwab
  0 siblings, 1 reply; 17+ messages in thread
From: Florian Weimer @ 2017-07-04 10:42 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: libc-alpha

On 07/04/2017 12:14 PM, Andreas Schwab wrote:
> On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:
> 
>> I meant a link to something like the SRPM or the source repository from
>> which the SRPM is built.
> 
> Yes, that's exactly linked from the top (Overview etc.).

If I do that, I end up at

http://software.opensuse.org//download.html?project=home%3AAndreas_Schwab%3Aglibc&package=glibc

which offers only glibc-2.22-118.1.src.rpm as the newest version.

>> I think the only way in which I could help is to provide a custom
>> logging patch which you could install temporarily during the build, and
>> we'll then see where it fails.  Would that work for you?
> 
> Sure.  You can also build it yourself locally (you need an OBS account
> for that though).

I'll send you a patch instead.  I suspect it might come in handy in
other cases, too.

Florian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 10:42               ` Florian Weimer
@ 2017-07-04 11:40                 ` Andreas Schwab
  2017-07-04 11:57                   ` Florian Weimer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2017-07-04 11:40 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:

> On 07/04/2017 12:14 PM, Andreas Schwab wrote:
>> On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:
>> 
>>> I meant a link to something like the SRPM or the source repository from
>>> which the SRPM is built.
>> 
>> Yes, that's exactly linked from the top (Overview etc.).
>
> If I do that, I end up at
>
> http://software.opensuse.org//download.html?project=home%3AAndreas_Schwab%3Aglibc&package=glibc
>
> which offers only glibc-2.22-118.1.src.rpm as the newest version.

Since the build failed no srpm was created, and publishing is disabled
anyway.  If you have an OBS account you can check out the sources via
command line.

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 11:40                 ` Andreas Schwab
@ 2017-07-04 11:57                   ` Florian Weimer
  0 siblings, 0 replies; 17+ messages in thread
From: Florian Weimer @ 2017-07-04 11:57 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: GNU C Library

[-- Attachment #1: Type: text/plain, Size: 2078 bytes --]

On 07/04/2017 01:40 PM, Andreas Schwab wrote:
> On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:
> 
>> On 07/04/2017 12:14 PM, Andreas Schwab wrote:
>>> On Jul 04 2017, Florian Weimer <fweimer@redhat.com> wrote:
>>>
>>>> I meant a link to something like the SRPM or the source repository from
>>>> which the SRPM is built.
>>>
>>> Yes, that's exactly linked from the top (Overview etc.).
>>
>> If I do that, I end up at
>>
>> http://software.opensuse.org//download.html?project=home%3AAndreas_Schwab%3Aglibc&package=glibc
>>
>> which offers only glibc-2.22-118.1.src.rpm as the newest version.
> 
> Since the build failed no srpm was created, and publishing is disabled
> anyway.  If you have an OBS account you can check out the sources via
> command line.

Okay, in this case, please try the attached patch to get more logging.
It's on to of commit 89f6307c5d270ed4f11cee373031fa9f2222f2b9.  (It adds
two missing conformance checks as well, but they go in the wrong
direction for this; i.e. they would cause only additional asserts.)

With the patch, I get output like this on mismatch:

Fatal glibc error: resolv.conf mismatch after loading
resolv_conf options: 0x000002c1
resolv_conf nameserver 0: [10.16.36.29]:53
resolv_conf nameserver 1: [10.11.5.19]:53
resolv_conf nameserver 2: [10.5.30.160]:53
resolv_conf search list 0: khw.lab.eng.bos.redhat.com
resolv_conf search list 1: redhat.com
resolv_conf search list 2: str.redhat.com
resolv_conf search list 3: corp.redhat.com
resolv_conf search list 4: engineering.redhat.com
resolv_conf search list 5: debian.org
resolv_conf search list 6: enyo.de
_res options: 0x000002c1
_res nscount: 2
_res nameserver 0: [10.16.36.29]:53
_res nameserver 1: [10.11.5.19]:53
_res _ext nscount: 0
_res default domain: khw.lab.eng.bos.redhat.com
_res search list 0: khw.lab.eng.bos.redhat.com
_res search list 1: redhat.com
_res search list 2: str.redhat.com
_res search list 3: corp.redhat.com
_res search list 4: engineering.redhat.com
_res search list 5: debian.org
_res nsort: 0
mismatch detected at nscount

Thanks,
Florian

[-- Attachment #2: 0001-resolv-Add-logging-to-resolv_conf-processing.patch --]
[-- Type: text/x-patch, Size: 9866 bytes --]

From 51c852cb13f9cbca3803b05372bc5a5f4b12bebb Mon Sep 17 00:00:00 2001
Message-Id: <51c852cb13f9cbca3803b05372bc5a5f4b12bebb.1499169412.git.fweimer@redhat.com>
From: Florian Weimer <fweimer@redhat.com>
Date: Tue, 4 Jul 2017 13:56:33 +0200
Subject: [PATCH] resolv: Add logging to resolv_conf processing
To: libc-alpha@sourceware.org

---
 resolv/resolv_conf.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 178 insertions(+), 14 deletions(-)

diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c
index 0ed36cd..375c072 100644
--- a/resolv/resolv_conf.c
+++ b/resolv/resolv_conf.c
@@ -19,9 +19,11 @@
 #include <resolv_conf.h>
 
 #include <alloc_buffer.h>
+#include <arpa/inet.h>
 #include <assert.h>
 #include <libc-lock.h>
 #include <resolv-internal.h>
+#include <stdio.h>
 #include <sys/stat.h>
 
 /* _res._u._ext.__glibc_extension_index is used as an index into a
@@ -257,9 +259,9 @@ same_address (const struct sockaddr *left, const struct sockaddr *right)
 }
 
 /* Check that *RESP and CONF match.  Used by __resolv_conf_get.  */
-static bool
+static __attribute__ ((noinline, noclone)) bool
 resolv_conf_matches (const struct __res_state *resp,
-                     const struct resolv_conf *conf)
+                     const struct resolv_conf *conf, FILE *fp)
 {
   /* NB: Do not compare the options, retrans, retry, ndots.  These can
      be changed by applicaiton.  */
@@ -272,23 +274,46 @@ resolv_conf_matches (const struct __res_state *resp,
       nserv = MAXNS;
     /* _ext.nscount is 0 until initialized by res_send.c.  */
     if (resp->nscount != nserv
-        && (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
-      return false;
+        || (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
+      {
+        if (fp != NULL)
+          fputs ("mismatch detected at nscount\n", fp);
+        return false;
+      }
     for (size_t i = 0; i < nserv; ++i)
       {
         if (resp->nsaddr_list[i].sin_family == 0)
           {
             if (resp->_u._ext.nsaddrs[i]->sin6_family != AF_INET6)
-              return false;
+              {
+                if (fp != NULL)
+                  fprintf
+                    (fp, "extended address family at %zu is not AF_INET6\n",
+                     i);
+                return false;
+              }
             if (!same_address ((struct sockaddr *) resp->_u._ext.nsaddrs[i],
                                conf->nameserver_list[i]))
-              return false;
+              {
+                if (fp != NULL)
+                  fprintf (fp, "extended address %zu does not match\n", i);
+                return false;
+              }
           }
         else if (resp->nsaddr_list[i].sin_family != AF_INET)
-          return false;
+          {
+            if (fp != NULL)
+              fprintf
+                (fp, "basic address family at %zu is not AF_INET\n", i);
+            return false;
+          }
         else if (!same_address ((struct sockaddr *) &resp->nsaddr_list[i],
                                 conf->nameserver_list[i]))
-          return false;
+          {
+            if (fp != NULL)
+              fprintf (fp, "basic address %zu does not match\n", i);
+            return false;
+          }
       }
   }
 
@@ -297,7 +322,11 @@ resolv_conf_matches (const struct __res_state *resp,
   {
     if (!(resp->dnsrch[0] == resp->defdname
           && resp->dnsrch[MAXDNSRCH] == NULL))
-      return false;
+      {
+        if (fp != NULL)
+          fputs ("mismatch detected at dnsrch\n", fp);
+        return false;
+      }
     size_t search_list_size = 0;
     for (size_t i = 0; i < conf->search_list_size; ++i)
       {
@@ -305,7 +334,11 @@ resolv_conf_matches (const struct __res_state *resp,
           {
             search_list_size += strlen (resp->dnsrch[i]) + 1;
             if (strcmp (resp->dnsrch[i], conf->search_list[i]) != 0)
-              return false;
+              {
+                if (fp != NULL)
+                  fprintf (fp, "mismatch at search entry %zu\n", i);
+                return false;
+              }
           }
         else
           {
@@ -316,6 +349,8 @@ resolv_conf_matches (const struct __res_state *resp,
             if (i == MAXDNSRCH || search_list_size > sizeof (resp->dnsrch))
               break;
             /* Otherwise, a mismatch indicates a match failure.  */
+            if (fp != NULL)
+              fprintf (fp, "mismatch at search entry %zu (truncation)\n", i);
             return false;
           }
       }
@@ -326,10 +361,20 @@ resolv_conf_matches (const struct __res_state *resp,
     size_t nsort = conf->sort_list_size;
     if (nsort > MAXRESOLVSORT)
       nsort = MAXRESOLVSORT;
+    if (resp->nsort != nsort)
+      {
+        if (fp != NULL)
+          fputs ("mismatch detected at nsort\n", fp);
+        return false;
+      }
     for (size_t i = 0; i < nsort; ++i)
       if (resp->sort_list[i].addr.s_addr != conf->sort_list[i].addr.s_addr
           || resp->sort_list[i].mask != conf->sort_list[i].mask)
-        return false;
+        {
+          if (fp != NULL)
+            fprintf (fp, "mismatch at sort list entry %zu\n", i);
+          return false;
+        }
   }
 
   return true;
@@ -341,7 +386,7 @@ __resolv_conf_get (struct __res_state *resp)
   struct resolv_conf *conf = resolv_conf_get_1 (resp);
   if (conf == NULL)
     return NULL;
-  if (resolv_conf_matches (resp, conf))
+  if (resolv_conf_matches (resp, conf, NULL))
     return conf;
   __resolv_conf_put (conf);
   return NULL;
@@ -462,8 +507,116 @@ __resolv_conf_allocate (const struct resolv_conf *init)
   return conf;
 }
 
+static void
+format_sockaddr (const void *ptr, FILE *fp)
+{
+  const struct sockaddr *sa = ptr;
+  char addr[50];
+  uint16_t port;
+  switch (sa->sa_family)
+    {
+    case AF_INET:
+      {
+        const struct sockaddr_in *sin = ptr;
+        if (inet_ntop (AF_INET, &sin->sin_addr, addr, sizeof (addr)) == NULL)
+          {
+            fputs ("<invalid>", fp);
+            return;
+          }
+        port = sin->sin_port;
+        break;
+      }
+    case AF_INET6:
+      {
+        const struct sockaddr_in6 *sin6 = ptr;
+        if (inet_ntop (AF_INET6, &sin6->sin6_addr, addr, sizeof (addr)) == NULL)
+          {
+            fputs ("<invalid>", fp);
+            return;
+          }
+        port = sin6->sin6_port;
+        break;
+      }
+    default:
+      fprintf (fp, "<invalid address family %d>", sa->sa_family);
+      return;
+    }
+  fprintf (fp, "[%s]:%u", addr, ntohs (port));
+}
+
+static void
+format_ipv4 (uint32_t addr, FILE *fp)
+{
+  char buf[50];
+  if (inet_ntop (AF_INET, &addr, buf, sizeof (buf)) == NULL)
+    fputs ("<invalid>", fp);
+  else
+    fputs (buf, fp);
+}
+
+static void
+print_conf (const struct resolv_conf *conf, FILE *fp)
+{
+  fprintf (fp, "resolv_conf options: 0x%08x\n", conf->options);
+  for (size_t i = 0; i < conf->nameserver_list_size; ++i)
+    {
+      fprintf (fp, "resolv_conf nameserver %zu: ", i);
+      format_sockaddr (conf->nameserver_list[i], fp);
+      _IO_putc ('\n', fp);
+    }
+  for (size_t i = 0; i < conf->search_list_size; ++i)
+    fprintf (fp, "resolv_conf search list %zu: %s\n", i, conf->search_list[i]);
+  for (size_t i = 0; i < conf->sort_list_size; ++i)
+    {
+      fprintf (fp, "resolv_conf sortlist %zu: ", i);
+      format_ipv4 (conf->sort_list[i].addr.s_addr, fp);
+      _IO_putc ('/', fp);
+      format_ipv4 (conf->sort_list[i].mask, fp);
+      _IO_putc ('\n', fp);
+    }
+}
+
+static void
+print_resp (const struct __res_state *resp, FILE *fp)
+{
+  fprintf (fp, "_res options: 0x%08lx\n", resp->options);
+  fprintf (fp, "_res nscount: %d\n", resp->nscount);
+  for (size_t i = 0; i < resp->nscount; ++i)
+    {
+      fprintf (fp, "_res nameserver %zu: ", i);
+      format_sockaddr (&resp->nsaddr_list[i], fp);
+      _IO_putc ('\n', fp);
+    }
+  fprintf (fp, "_res _ext nscount: %d\n", resp->_u._ext.nscount);
+  for (size_t i = 0; i < resp->_u._ext.nscount; ++i)
+    {
+      fprintf (fp, "_res _ext nameserver %zu: ", i);
+      if (resp->_u._ext.nsaddrs[i] == NULL)
+        fputs ("(null)", fp);
+      else
+        format_sockaddr (resp->_u._ext.nsaddrs[i], fp);
+      _IO_putc ('\n', fp);
+    }
+  fprintf (fp, "_res default domain: %s\n", resp->defdname);
+  for (size_t i = 0; i < MAXDNSRCH; ++i)
+    if (resp->dnsrch[i] == NULL)
+      break;
+    else
+      fprintf (fp, "_res search list %zu: %s\n", i, resp->dnsrch[i]);
+  fprintf (fp, "_res nsort: %d\n", resp->nsort);
+  for (size_t i = 0; i < resp->nsort; ++i)
+    {
+      fprintf (fp, "_res sortlist %zu: ", i);
+      format_ipv4 (resp->sort_list[i].addr.s_addr, fp);
+      _IO_putc ('/', fp);
+      format_ipv4 (resp->sort_list[i].mask, fp);
+      _IO_putc ('\n', fp);
+    }
+}
+
 /* Update *RESP from the extended state.  */
-static __attribute__ ((nonnull (1, 2), warn_unused_result)) bool
+static __attribute__ ((nonnull (1, 2), warn_unused_result,
+                       noinline, noclone)) bool
 update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
 {
   resp->defdname[0] = '\0';
@@ -549,7 +702,18 @@ update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
 
   /* The overlapping parts of both configurations should agree after
      initialization.  */
-  assert (resolv_conf_matches (resp, conf));
+  if (!resolv_conf_matches (resp, conf, NULL))
+    {
+      char *buffer = NULL;
+      size_t buffer_length = 0;
+      FILE *fp = __open_memstream (&buffer, &buffer_length);
+      fputs ("Fatal glibc error: resolv.conf mismatch after loading\n", fp);
+      print_conf (conf, fp);
+      print_resp (resp, fp);
+      resolv_conf_matches (resp, conf, fp);
+      fclose (fp);
+      __libc_fatal (buffer);
+    }
   return true;
 }
 
-- 
2.9.4


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04  8:00   ` Florian Weimer
  2017-07-04  8:08     ` Andreas Schwab
@ 2017-07-04 11:59     ` Andreas Schwab
  2017-07-04 12:13       ` Florian Weimer
  1 sibling, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2017-07-04 11:59 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

(gdb) p *resp
$1 = {retrans = 5, retry = 2, options = 705, nscount = 1, nsaddr_list = {{
      sin_family = 2, sin_port = 13568, sin_addr = {s_addr = 16777343}, 
      sin_zero = "\272\272\272\272\272\272\272\272"}, {sin_family = 0, 
      sin_port = 0, sin_addr = {s_addr = 0}, 
      sin_zero = "\000\000\000\000\000\000\000"}, {sin_family = 0, 
      sin_port = 0, sin_addr = {s_addr = 0}, 
      sin_zero = "\000\000\000\000\000\000\000"}}, id = 0, dnsrch = {0x0, 0x0, 
    0x0, 0x0, 0x0, 0x0, 0x0}, defdname = '\000' <repeats 255 times>, 
  pfcode = 0, ndots = 1, nsort = 0, ipv6_unavail = 0, unused = 0, sort_list = {
    {addr = {s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
        s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
        s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
        s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
        s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}}, 
  __glibc_unused_qhook = 0x0, __glibc_unused_rhook = 0x0, res_h_errno = 0, 
  _vcsock = -1, _flags = 0, _u = {
    pad = "\000\000\000\000\000\000\000\000\377\377\377\377", '\000' <repeats 39 times>, _ext = {nscount = 0, nsmap = {0, 0, 0}, nssocks = {-1, 0, 0}, 
      nscount6 = 0, nsinit = 0, nsaddrs = {0x0, 0x0, 0x0}, 
      __glibc_extension_index = 0}}}
(gdb) p *conf
$2 = {__refcount = 3, nameserver_list = 0x606f38, nameserver_list_size = 1, 
  search_list = 0x606f40, search_list_size = 0, sort_list = 0x606f50, 
  sort_list_size = 0, options = 705, retrans = 5, retry = 2, ndots = 1}
(gdb) p conf.nameserver_list[0]
$3 = (const struct sockaddr *) 0x606f40
(gdb) p *$
$4 = {sa_family = 2, 
  sa_data = "\000\065\177\000\000\001\272\272\272\272\272\272\272\272"}

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 11:59     ` Andreas Schwab
@ 2017-07-04 12:13       ` Florian Weimer
  2017-07-04 12:16         ` Andreas Schwab
  0 siblings, 1 reply; 17+ messages in thread
From: Florian Weimer @ 2017-07-04 12:13 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: libc-alpha

On 07/04/2017 01:59 PM, Andreas Schwab wrote:
> (gdb) p *resp
> $1 = {retrans = 5, retry = 2, options = 705, nscount = 1, nsaddr_list = {{
>       sin_family = 2, sin_port = 13568, sin_addr = {s_addr = 16777343}, 
>       sin_zero = "\272\272\272\272\272\272\272\272"},

Seem this is [127.0.0.1]:53.

 {sin_family = 0,
>       sin_port = 0, sin_addr = {s_addr = 0}, 
>       sin_zero = "\000\000\000\000\000\000\000"}, {sin_family = 0, 
>       sin_port = 0, sin_addr = {s_addr = 0}, 
>       sin_zero = "\000\000\000\000\000\000\000"}}, id = 0, dnsrch = {0x0, 0x0, 
>     0x0, 0x0, 0x0, 0x0, 0x0}, defdname = '\000' <repeats 255 times>, 
>   pfcode = 0, ndots = 1, nsort = 0, ipv6_unavail = 0, unused = 0, sort_list = {
>     {addr = {s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
>         s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
>         s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
>         s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}, {addr = {
>         s_addr = 0}, mask = 0}, {addr = {s_addr = 0}, mask = 0}}, 
>   __glibc_unused_qhook = 0x0, __glibc_unused_rhook = 0x0, res_h_errno = 0, 
>   _vcsock = -1, _flags = 0, _u = {
>     pad = "\000\000\000\000\000\000\000\000\377\377\377\377", '\000' <repeats 39 times>, _ext = {nscount = 0, nsmap = {0, 0, 0}, nssocks = {-1, 0, 0}, 
>       nscount6 = 0, nsinit = 0, nsaddrs = {0x0, 0x0, 0x0}, 
>       __glibc_extension_index = 0}}}
> (gdb) p *conf
> $2 = {__refcount = 3, nameserver_list = 0x606f38, nameserver_list_size = 1, 
>   search_list = 0x606f40, search_list_size = 0, sort_list = 0x606f50, 
>   sort_list_size = 0, options = 705, retrans = 5, retry = 2, ndots = 1}
> (gdb) p conf.nameserver_list[0]
> $3 = (const struct sockaddr *) 0x606f40
> (gdb) p *$
> $4 = {sa_family = 2, 
>   sa_data = "\000\065\177\000\000\001\272\272\272\272\272\272\272\272"}

That looks like [127.0.0.1]:53, too.

So I don't see the mismatch.  Let's see what the additional logging will
reveal.  Sorry about this mess, but I don't see the bug so far.

Thanks,
Florian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 12:13       ` Florian Weimer
@ 2017-07-04 12:16         ` Andreas Schwab
  2017-07-04 12:21           ` Florian Weimer
  0 siblings, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2017-07-04 12:16 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha

298         if (!(resp->dnsrch[0] == resp->defdname
299               && resp->dnsrch[MAXDNSRCH] == NULL))

Andreas.

-- 
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 12:16         ` Andreas Schwab
@ 2017-07-04 12:21           ` Florian Weimer
  2017-07-04 12:55             ` Florian Weimer
  0 siblings, 1 reply; 17+ messages in thread
From: Florian Weimer @ 2017-07-04 12:21 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: libc-alpha

On 07/04/2017 02:16 PM, Andreas Schwab wrote:
> 298         if (!(resp->dnsrch[0] == resp->defdname
> 299               && resp->dnsrch[MAXDNSRCH] == NULL))

Ah.  I assume this breaks for defname[0] == '\0' and dnsrch[0] == NULL.

It seems that this is triggered by having a host name which does not
contain a dot.  I'll fix this and add a test case.  Thanks.

Florian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 12:21           ` Florian Weimer
@ 2017-07-04 12:55             ` Florian Weimer
  2017-07-04 14:10               ` Florian Weimer
  0 siblings, 1 reply; 17+ messages in thread
From: Florian Weimer @ 2017-07-04 12:55 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: libc-alpha

On 07/04/2017 02:21 PM, Florian Weimer wrote:
> On 07/04/2017 02:16 PM, Andreas Schwab wrote:
>> 298         if (!(resp->dnsrch[0] == resp->defdname
>> 299               && resp->dnsrch[MAXDNSRCH] == NULL))
> 
> Ah.  I assume this breaks for defname[0] == '\0' and dnsrch[0] == NULL.
> 
> It seems that this is triggered by having a host name which does not
> contain a dot.  I'll fix this and add a test case.  Thanks.

And this is the fix I'm going to commit.

Thanks,
Florian

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf
  2017-07-04 12:55             ` Florian Weimer
@ 2017-07-04 14:10               ` Florian Weimer
  0 siblings, 0 replies; 17+ messages in thread
From: Florian Weimer @ 2017-07-04 14:10 UTC (permalink / raw)
  To: GNU C Library

[-- Attachment #1: Type: text/plain, Size: 568 bytes --]

On 07/04/2017 02:55 PM, Florian Weimer wrote:
> On 07/04/2017 02:21 PM, Florian Weimer wrote:
>> On 07/04/2017 02:16 PM, Andreas Schwab wrote:
>>> 298         if (!(resp->dnsrch[0] == resp->defdname
>>> 299               && resp->dnsrch[MAXDNSRCH] == NULL))
>>
>> Ah.  I assume this breaks for defname[0] == '\0' and dnsrch[0] == NULL.
>>
>> It seems that this is triggered by having a host name which does not
>> contain a dot.  I'll fix this and add a test case.  Thanks.
> 
> And this is the fix I'm going to commit.

Trying again to send the attachment.

Florian


[-- Attachment #2: resolv_conf-matching.patch --]
[-- Type: text/x-patch, Size: 5200 bytes --]

resolv: Fix resolv_conf _res matching

A dot-less host name without an /etc/resolv.conf file caused an
assertion failure in update_from_conf because the function would not
deal correctly with the empty search list case.

Thanks to Andreas Schwab for debugging assistence.

2017-07-04  Florian Weimer  <fweimer@redhat.com>

	* resolv/resolv_conf.c (resolv_conf_matches): Tighten check for name
	server and sort list counts.  Fix improper check for empty search
	path (completely missing domain name) leading to assertion failure
	in update_from_conf.
	* resolv/tst-resolv-res_init-skeleton.c (struct test_case): Add
	hostname member.
	(run_res_init): Set host name if requested.
	(test_cases): Update.

diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c
index 0ed36cd..f391d30c2 100644
--- a/resolv/resolv_conf.c
+++ b/resolv/resolv_conf.c
@@ -272,7 +272,7 @@ resolv_conf_matches (const struct __res_state *resp,
       nserv = MAXNS;
     /* _ext.nscount is 0 until initialized by res_send.c.  */
     if (resp->nscount != nserv
-        && (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
+        || (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
       return false;
     for (size_t i = 0; i < nserv; ++i)
       {
@@ -295,9 +295,25 @@ resolv_conf_matches (const struct __res_state *resp,
   /* Check that the search list in *RESP has not been modified by the
      application.  */
   {
-    if (!(resp->dnsrch[0] == resp->defdname
-          && resp->dnsrch[MAXDNSRCH] == NULL))
+    if (resp->dnsrch[0] == NULL)
+      {
+        /* Empty search list.  No default domain name.  */
+        return conf->search_list_size == 0 && resp->defdname[0] == '\0';
+      }
+
+    if (resp->dnsrch[0] != resp->defdname)
+      /* If the search list is not empty, it must start with the
+         default domain name.  */
       return false;
+
+    size_t nsearch;
+    for (nsearch = 0; nsearch < MAXDNSRCH; ++nsearch)
+      if (resp->dnsrch[nsearch] == NULL)
+        break;
+    if (nsearch > MAXDNSRCH)
+      /* Search list is not null-terminated.  */
+      return false;
+
     size_t search_list_size = 0;
     for (size_t i = 0; i < conf->search_list_size; ++i)
       {
@@ -326,6 +342,8 @@ resolv_conf_matches (const struct __res_state *resp,
     size_t nsort = conf->sort_list_size;
     if (nsort > MAXRESOLVSORT)
       nsort = MAXRESOLVSORT;
+    if (resp->nsort != nsort)
+      return false;
     for (size_t i = 0; i < nsort; ++i)
       if (resp->sort_list[i].addr.s_addr != conf->sort_list[i].addr.s_addr
           || resp->sort_list[i].mask != conf->sort_list[i].mask)
diff --git a/resolv/tst-resolv-res_init-skeleton.c b/resolv/tst-resolv-res_init-skeleton.c
index 9e496a3..8f395d8 100644
--- a/resolv/tst-resolv-res_init-skeleton.c
+++ b/resolv/tst-resolv-res_init-skeleton.c
@@ -307,6 +307,10 @@ struct test_case
   /* Setting for the RES_OPTIONS environment variable.  NULL if the
      variable is not to be set.  */
   const char *res_options;
+
+  /* Override the system host name.  NULL means that no change is made
+     and the default is used (test_hostname).  */
+  const char *hostname;
 };
 
 enum test_init
@@ -358,6 +362,14 @@ run_res_init (void *closure)
     setenv ("LOCALDOMAIN", ctx->t->localdomain, 1);
   if (ctx->t->res_options != NULL)
     setenv ("RES_OPTIONS", ctx->t->res_options, 1);
+  if (ctx->t->hostname != NULL)
+    {
+      /* This test needs its own namespace, to avoid changing the host
+         name for the parent, too.  */
+      TEST_VERIFY_EXIT (unshare (CLONE_NEWUTS) == 0);
+      if (sethostname (ctx->t->hostname, strlen (ctx->t->hostname)) != 0)
+        FAIL_EXIT1 ("sethostname (\"%s\"): %m", ctx->t->hostname);
+    }
 
   switch (ctx->init)
     {
@@ -434,6 +446,12 @@ struct test_case test_cases[] =
      "nameserver 127.0.0.1\n"
      "; nameserver[0]: [127.0.0.1]:53\n"
     },
+    {.name = "empty file, no-dot hostname",
+     .conf = "",
+     .expected = "nameserver 127.0.0.1\n"
+     "; nameserver[0]: [127.0.0.1]:53\n",
+     .hostname = "example",
+    },
     {.name = "empty file with LOCALDOMAIN",
      .conf = "",
      .expected = "search example.net\n"
@@ -462,8 +480,7 @@ struct test_case test_cases[] =
      .res_options = "edns0 attempts:5",
     },
     {.name = "basic",
-     .conf = "domain example.net\n"
-     "search corp.example.com example.com\n"
+     .conf =  "search corp.example.com example.com\n"
      "nameserver 192.0.2.1\n",
      .expected = "search corp.example.com example.com\n"
      "; search[0]: corp.example.com\n"
@@ -471,6 +488,16 @@ struct test_case test_cases[] =
      "nameserver 192.0.2.1\n"
      "; nameserver[0]: [192.0.2.1]:53\n"
     },
+    {.name = "basic with no-dot hostname",
+     .conf = "search corp.example.com example.com\n"
+     "nameserver 192.0.2.1\n",
+     .expected = "search corp.example.com example.com\n"
+     "; search[0]: corp.example.com\n"
+     "; search[1]: example.com\n"
+     "nameserver 192.0.2.1\n"
+     "; nameserver[0]: [192.0.2.1]:53\n",
+     .hostname = "example",
+    },
     {.name = "basic no-reload",
      .conf = "options no-reload\n"
      "search corp.example.com example.com\n"


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2017-07-04 14:10 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-30 19:38 [PATCH] resolv: Mirror the entire resolver configuration in struct resolv_conf Florian Weimer
2017-07-04  7:27 ` Andreas Schwab
2017-07-04  8:00   ` Florian Weimer
2017-07-04  8:08     ` Andreas Schwab
2017-07-04  9:34       ` Florian Weimer
2017-07-04 10:00         ` Andreas Schwab
2017-07-04 10:09           ` Florian Weimer
2017-07-04 10:14             ` Andreas Schwab
2017-07-04 10:42               ` Florian Weimer
2017-07-04 11:40                 ` Andreas Schwab
2017-07-04 11:57                   ` Florian Weimer
2017-07-04 11:59     ` Andreas Schwab
2017-07-04 12:13       ` Florian Weimer
2017-07-04 12:16         ` Andreas Schwab
2017-07-04 12:21           ` Florian Weimer
2017-07-04 12:55             ` Florian Weimer
2017-07-04 14:10               ` Florian Weimer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).