This patch implements the 'parse-resolv-conf' directive for the resolvers
section, it was previously discussed on the list in this thread:
https://www.mail-archive.com/haproxy@formilux.org/msg29600.html

(Is it preferred to use the discussion thread to present the patch?)

It's a little inefficient, in that it will re-parse /etc/resolv.conf every
time the directive is encountered, but that didn't seem too bad to me since
that file is (usually?) tiny. I'm happy to do an alternate approach if we
think it's necessary.

I also opted to just use the nameserver's address as its name in the
resolvers section, as I thought that would have the highest probability of
avoiding name conflicts with other configured nameservers. Again I'm open
to feedback though.

Thanks!

Ben
From 98129271f32e6b9bc00880c967f9acf1233fed9b Mon Sep 17 00:00:00 2001
From: Ben Draut <dra...@gmail.com>
Date: Tue, 24 Apr 2018 21:08:37 -0600
Subject: [PATCH] MINOR: config: Implement `parse-resolv-conf` directive

This introduces a new directive for the `resolvers` section:
`parse-resolv-conf`. When present, it will attempt to add any
nameservers in `/etc/resolv.conf` to the list of nameservers
for the current `resolvers` section.

[Mailing list thread][1].

[1]: https://www.mail-archive.com/haproxy@formilux.org/msg29600.html
---
 doc/configuration.txt |  6 ++++
 src/cfgparse.c        | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index dcc87fdc..69ea8730 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -12020,6 +12020,11 @@ nameserver <id> <ip>:<port>
     <ip>   : IP address of the server
     <port> : port where the DNS service actually runs
 
+parse-resolv-conf
+  Adds all nameservers found in /etc/resolv.conf to this resolvers nameservers
+  list. Ordered as if each nameserver in /etc/resolv.conf was individually
+  placed in the resolvers section in place of this directive.
+
 hold <status> <period>
   Defines <period> during which the last name resolution should be kept based
   on last resolution <status>
@@ -12063,6 +12068,7 @@ timeout <event> <time>
    resolvers mydns
      nameserver dns1 10.0.0.1:53
      nameserver dns2 10.0.0.2:53
+     parse-resolv-conf
      resolve_retries       3
      timeout resolve       1s
      timeout retry         1s
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 621af6c8..1f293679 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2239,7 +2239,7 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
 			/* Error if two resolvers owns the same name */
 			if (strcmp(newnameserver->id, args[1]) == 0) {
 				ha_alert("Parsing [%s:%d]: nameserver '%s' has same name as another nameserver (declared at %s:%d).\n",
-					 file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line);
+					 file, linenum, args[1],newnameserver->conf.file, newnameserver->conf.line);
 				err_code |= ERR_ALERT | ERR_FATAL;
 			}
 		}
@@ -2288,6 +2288,100 @@ int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
 
 		newnameserver->addr = *sk;
 	}
+	else if (strcmp(args[0], "parse-resolv-conf") == 0) {
+		const char *whitespace = "\r\n\t ";
+		char *resolv_line = NULL;
+		int resolv_linenum = 0;
+		FILE *f;
+		char *address = NULL;
+		struct sockaddr_storage *sk;
+		struct protocol *proto;
+
+		if ((resolv_line = malloc(sizeof(*resolv_line) * LINESIZE)) == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n",
+				 file, linenum);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+		if ((f = fopen("/etc/resolv.conf", "r")) == NULL) {
+			free(resolv_line);
+			ha_alert("parsing [%s:%d] : failed to open /etc/resolv.conf.\n",
+				 file, linenum);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+
+		sk = calloc(1, sizeof(*sk));
+		if (sk == NULL) {
+			ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+		while (fgets(resolv_line, LINESIZE, f) != NULL) {
+			resolv_linenum++;
+			if (strncmp(resolv_line, "nameserver", 10) != 0)
+				continue;
+
+			address = strtok(resolv_line + 10, whitespace);
+			if (address == NULL) {
+				ha_warning("parsing [/etc/resolv.conf:%d] : nameserver line is missing address.\n",
+					   resolv_linenum);
+				err_code |= ERR_WARN;
+				continue;
+			}
+
+			list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
+				if (strcmp(newnameserver->id, address) == 0) {
+					ha_alert("Parsing [/etc/resolv.conf:%d] : generated name for /etc/resolv.conf nameserver '%s' conflicts with another nameserver (declared at %s:%d).\n",
+						 resolv_linenum, address, newnameserver->conf.file, newnameserver->conf.line);
+					err_code |= ERR_ALERT | ERR_FATAL;
+				}
+			}
+
+			if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) {
+				ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
+			newnameserver->resolvers = curr_resolvers;
+			newnameserver->conf.file = strdup("/etc/resolv.conf");
+			newnameserver->conf.line = resolv_linenum;
+			newnameserver->id = strdup(address);
+
+			memset(sk, 0, sizeof(*sk));
+			sk = str2ip2(address, sk, 1);
+			if (!sk) {
+				ha_alert("parsing [/etc/resolv.conf:%d] : failed to populate sockaddr_storage for address '%s'\n",
+					 resolv_linenum, address);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			proto = protocol_by_family(sk->ss_family);
+			if (!proto || !proto->connect) {
+				ha_alert("parsing [/etc/resolv.conf:%d] : '%s' : connect() not supported for this address family.\n",
+					 resolv_linenum, address);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			set_host_port(sk, 53);
+			newnameserver->addr = *sk;
+		}
+
+		free(sk);
+		free(resolv_line);
+		if (fclose(f) != 0) {
+			ha_warning("parsing [%s:%d] : failed to close handle to /etc/resolv.conf.\n",
+				   file, linenum);
+			err_code |= ERR_WARN;
+		}
+	}
 	else if (strcmp(args[0], "hold") == 0) { /* hold periods */
 		const char *res;
 		unsigned int time;
-- 
2.14.1

Reply via email to