resolv: Mirror the entire resolver configuration in struct resolv_conf

Message ID 40d78bcf-4ef7-9a75-4093-30c271cf49ad@redhat.com
State Not applicable
Headers

Commit Message

Florian Weimer July 4, 2017, 11:57 a.m. UTC
  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
  

Patch

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