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(-)
@@ -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