MEDIUM: resolvers: move resolvers section parsing from cfgparse.c to dns.c

The resolver section parsing is moved from cfgparse.c to dns.c
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 4840008..0c0638e 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -909,391 +909,6 @@
 }
 
 /*
- * Parse a <resolvers> section.
- * Returns the error code, 0 if OK, or any combination of :
- *  - ERR_ABORT: must abort ASAP
- *  - ERR_FATAL: we can continue parsing but not start the service
- *  - ERR_WARN: a warning has been emitted
- *  - ERR_ALERT: an alert has been emitted
- * Only the two first ones can stop processing, the two others are just
- * indicators.
- */
-int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
-{
-	static struct resolvers *curr_resolvers = NULL;
-	const char *err;
-	int err_code = 0;
-	char *errmsg = NULL;
-
-	if (strcmp(args[0], "resolvers") == 0) { /* new resolvers section */
-		if (!*args[1]) {
-			ha_alert("parsing [%s:%d] : missing name for resolvers section.\n", file, linenum);
-			err_code |= ERR_ALERT | ERR_ABORT;
-			goto out;
-		}
-
-		err = invalid_char(args[1]);
-		if (err) {
-			ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
-				 file, linenum, *err, args[0], args[1]);
-			err_code |= ERR_ALERT | ERR_ABORT;
-			goto out;
-		}
-
-		list_for_each_entry(curr_resolvers, &sec_resolvers, list) {
-			/* Error if two resolvers owns the same name */
-			if (strcmp(curr_resolvers->id, args[1]) == 0) {
-				ha_alert("Parsing [%s:%d]: resolvers '%s' has same name as another resolvers (declared at %s:%d).\n",
-					 file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line);
-				err_code |= ERR_ALERT | ERR_ABORT;
-			}
-		}
-
-		if ((curr_resolvers = calloc(1, sizeof(*curr_resolvers))) == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
-			err_code |= ERR_ALERT | ERR_ABORT;
-			goto out;
-		}
-
-		/* default values */
-		LIST_ADDQ(&sec_resolvers, &curr_resolvers->list);
-		curr_resolvers->conf.file = strdup(file);
-		curr_resolvers->conf.line = linenum;
-		curr_resolvers->id = strdup(args[1]);
-		curr_resolvers->query_ids = EB_ROOT;
-		/* default maximum response size */
-		curr_resolvers->accepted_payload_size = 512;
-		/* default hold period for nx, other, refuse and timeout is 30s */
-		curr_resolvers->hold.nx = 30000;
-		curr_resolvers->hold.other = 30000;
-		curr_resolvers->hold.refused = 30000;
-		curr_resolvers->hold.timeout = 30000;
-		curr_resolvers->hold.obsolete = 0;
-		/* default hold period for valid is 10s */
-		curr_resolvers->hold.valid = 10000;
-		curr_resolvers->timeout.resolve = 1000;
-		curr_resolvers->timeout.retry   = 1000;
-		curr_resolvers->resolve_retries = 3;
-		curr_resolvers->nb_nameservers  = 0;
-		LIST_INIT(&curr_resolvers->nameservers);
-		LIST_INIT(&curr_resolvers->resolutions.curr);
-		LIST_INIT(&curr_resolvers->resolutions.wait);
-		HA_SPIN_INIT(&curr_resolvers->lock);
-	}
-	else if (strcmp(args[0], "nameserver") == 0) { /* nameserver definition */
-		struct dns_nameserver *newnameserver = NULL;
-		struct sockaddr_storage *sk;
-		int port1, port2;
-
-		if (!*args[2]) {
-			ha_alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
-				 file, linenum, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
-		err = invalid_char(args[1]);
-		if (err) {
-			ha_alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n",
-				 file, linenum, *err, args[1]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
-		list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
-			/* 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], newnameserver->conf.file, newnameserver->conf.line);
-				err_code |= ERR_ALERT | ERR_FATAL;
-			}
-		}
-
-		if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
-			err_code |= ERR_ALERT | ERR_ABORT;
-			goto out;
-		}
-
-		/* the nameservers are linked backward first */
-		LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
-		newnameserver->resolvers = curr_resolvers;
-		newnameserver->conf.file = strdup(file);
-		newnameserver->conf.line = linenum;
-		newnameserver->id = strdup(args[1]);
-
-		sk = str2sa_range(args[2], NULL, &port1, &port2, NULL, NULL,
-		                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_DGRAM);
-		if (!sk) {
-			ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
-		newnameserver->addr = *sk;
-	}
-	else if (strcmp(args[0], "parse-resolv-conf") == 0) {
-		struct dns_nameserver *newnameserver = NULL;
-		const char *whitespace = "\r\n\t ";
-		char *resolv_line = NULL;
-		int resolv_linenum = 0;
-		FILE *f = NULL;
-		char *address = NULL;
-		struct sockaddr_storage *sk = NULL;
-		struct protocol *proto;
-		int duplicate_name = 0;
-
-		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 resolv_out;
-		}
-
-		if ((f = fopen("/etc/resolv.conf", "r")) == NULL) {
-			ha_alert("parsing [%s:%d] : failed to open /etc/resolv.conf.\n",
-				 file, linenum);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto resolv_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 resolv_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 == resolv_line + 10)
-				continue;
-
-			if (address == NULL) {
-				ha_warning("parsing [/etc/resolv.conf:%d] : nameserver line is missing address.\n",
-					   resolv_linenum);
-				err_code |= ERR_WARN;
-				continue;
-			}
-
-			duplicate_name = 0;
-			list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
-				if (strcmp(newnameserver->id, address) == 0) {
-					ha_warning("Parsing [/etc/resolv.conf:%d] : generated name for /etc/resolv.conf nameserver '%s' conflicts with another nameserver (declared at %s:%d), it appears to be a duplicate and will be excluded.\n",
-						 resolv_linenum, address, newnameserver->conf.file, newnameserver->conf.line);
-					err_code |= ERR_WARN;
-					duplicate_name = 1;
-				}
-			}
-
-			if (duplicate_name)
-				continue;
-
-			memset(sk, 0, sizeof(*sk));
-			if (!str2ip2(address, sk, 1)) {
-				ha_warning("parsing [/etc/resolv.conf:%d] : address '%s' could not be recognized, nameserver will be excluded.\n",
-					   resolv_linenum, address);
-				err_code |= ERR_WARN;
-				continue;
-			}
-
-			set_host_port(sk, 53);
-
-			proto = protocol_by_family(sk->ss_family);
-			if (!proto || !proto->connect) {
-				ha_warning("parsing [/etc/resolv.conf:%d] : '%s' : connect() not supported for this address family.\n",
-					   resolv_linenum, address);
-				err_code |= ERR_WARN;
-				continue;
-			}
-
-			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 resolv_out;
-			}
-
-			newnameserver->conf.file = strdup("/etc/resolv.conf");
-			if (newnameserver->conf.file == NULL) {
-				ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				free(newnameserver);
-				goto resolv_out;
-			}
-
-			newnameserver->id = strdup(address);
-			if (newnameserver->id == NULL) {
-				ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				free((char *)newnameserver->conf.file);
-				free(newnameserver);
-				goto resolv_out;
-			}
-
-			newnameserver->resolvers = curr_resolvers;
-			newnameserver->conf.line = resolv_linenum;
-			newnameserver->addr = *sk;
-
-			LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
-		}
-
-resolv_out:
-		free(sk);
-		free(resolv_line);
-		if (f != NULL)
-			fclose(f);
-	}
-	else if (strcmp(args[0], "hold") == 0) { /* hold periods */
-		const char *res;
-		unsigned int time;
-
-		if (!*args[2]) {
-			ha_alert("parsing [%s:%d] : '%s' expects an <event> and a <time> as arguments.\n",
-				 file, linenum, args[0]);
-			ha_alert("<event> can be either 'valid', 'nx', 'refused', 'timeout', or 'other'\n");
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-		res = parse_time_err(args[2], &time, TIME_UNIT_MS);
-		if (res == PARSE_TIME_OVER) {
-			ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s>, maximum value is 2147483647 ms (~24.8 days).\n",
-			         file, linenum, args[1], args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-		else if (res == PARSE_TIME_UNDER) {
-			ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s>, minimum non-null value is 1 ms.\n",
-			         file, linenum, args[1], args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-		else if (res) {
-			ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s>.\n",
-				 file, linenum, *res, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-		if (strcmp(args[1], "nx") == 0)
-			curr_resolvers->hold.nx = time;
-		else if (strcmp(args[1], "other") == 0)
-			curr_resolvers->hold.other = time;
-		else if (strcmp(args[1], "refused") == 0)
-			curr_resolvers->hold.refused = time;
-		else if (strcmp(args[1], "timeout") == 0)
-			curr_resolvers->hold.timeout = time;
-		else if (strcmp(args[1], "valid") == 0)
-			curr_resolvers->hold.valid = time;
-		else if (strcmp(args[1], "obsolete") == 0)
-			curr_resolvers->hold.obsolete = time;
-		else {
-			ha_alert("parsing [%s:%d] : '%s' unknown <event>: '%s', expects either 'nx', 'timeout', 'valid', 'obsolete' or 'other'.\n",
-				 file, linenum, args[0], args[1]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
-	}
-	else if (strcmp(args[0], "accepted_payload_size") == 0) {
-		int i = 0;
-
-		if (!*args[1]) {
-			ha_alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
-				 file, linenum, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
-		i = atoi(args[1]);
-		if (i < DNS_HEADER_SIZE || i > DNS_MAX_UDP_MESSAGE) {
-			ha_alert("parsing [%s:%d] : '%s' must be between %d and %d inclusive (was %s).\n",
-				 file, linenum, args[0], DNS_HEADER_SIZE, DNS_MAX_UDP_MESSAGE, args[1]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-
-		curr_resolvers->accepted_payload_size = i;
-	}
-	else if (strcmp(args[0], "resolution_pool_size") == 0) {
-		ha_alert("parsing [%s:%d] : '%s' directive is not supported anymore (it never appeared in a stable release).\n",
-			   file, linenum, args[0]);
-		err_code |= ERR_ALERT | ERR_FATAL;
-		goto out;
-	}
-	else if (strcmp(args[0], "resolve_retries") == 0) {
-		if (!*args[1]) {
-			ha_alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
-				 file, linenum, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-		curr_resolvers->resolve_retries = atoi(args[1]);
-	}
-	else if (strcmp(args[0], "timeout") == 0) {
-		if (!*args[1]) {
-			ha_alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments.\n",
-				 file, linenum, args[0]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-		else if (strcmp(args[1], "retry") == 0 ||
-			 strcmp(args[1], "resolve") == 0) {
-			const char *res;
-			unsigned int tout;
-
-			if (!*args[2]) {
-				ha_alert("parsing [%s:%d] : '%s %s' expects <time> as argument.\n",
-					 file, linenum, args[0], args[1]);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-			res = parse_time_err(args[2], &tout, TIME_UNIT_MS);
-			if (res == PARSE_TIME_OVER) {
-				ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s %s>, maximum value is 2147483647 ms (~24.8 days).\n",
-					 file, linenum, args[2], args[0], args[1]);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-			else if (res == PARSE_TIME_UNDER) {
-				ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s %s>, minimum non-null value is 1 ms.\n",
-					 file, linenum, args[2], args[0], args[1]);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-			else if (res) {
-				ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s %s>.\n",
-					 file, linenum, *res, args[0], args[1]);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-			if (args[1][2] == 't')
-				curr_resolvers->timeout.retry = tout;
-			else
-				curr_resolvers->timeout.resolve = tout;
-		}
-		else {
-			ha_alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments got '%s'.\n",
-				 file, linenum, args[0], args[1]);
-			err_code |= ERR_ALERT | ERR_FATAL;
-			goto out;
-		}
-	}
-	else if (*args[0] != 0) {
-		ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection);
-		err_code |= ERR_ALERT | ERR_FATAL;
-		goto out;
-	}
-
- out:
-	free(errmsg);
-	return err_code;
-}
-
-/*
  * Parse a line in a <listen>, <frontend> or <backend> section.
  * Returns the error code, 0 if OK, or any combination of :
  *  - ERR_ABORT: must abort ASAP
@@ -4236,7 +3851,6 @@
 REGISTER_CONFIG_SECTION("peers",          cfg_parse_peers,     NULL);
 REGISTER_CONFIG_SECTION("mailers",        cfg_parse_mailers,   NULL);
 REGISTER_CONFIG_SECTION("namespace_list", cfg_parse_netns,     NULL);
-REGISTER_CONFIG_SECTION("resolvers",      cfg_parse_resolvers, NULL);
 
 /*
  * Local variables:
diff --git a/src/dns.c b/src/dns.c
index 9e128ca..34546ac 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -33,6 +33,7 @@
 #include <haproxy/http_rules.h>
 #include <haproxy/log.h>
 #include <haproxy/net_helper.h>
+#include <haproxy/protocol.h>
 #include <haproxy/proxy.h>
 #include <haproxy/sample.h>
 #include <haproxy/server.h>
@@ -49,6 +50,7 @@
 struct list resolv_srvrq_list = LIST_HEAD_INIT(resolv_srvrq_list);
 
 static THREAD_LOCAL uint64_t resolv_query_id_seed = 0; /* random seed */
+struct resolvers *curr_resolvers = NULL;
 
 DECLARE_STATIC_POOL(resolv_answer_item_pool, "resolv_answer_item", sizeof(struct resolv_answer_item));
 DECLARE_STATIC_POOL(resolv_resolution_pool,  "resolv_resolution",  sizeof(struct resolv_resolution));
@@ -2932,6 +2934,390 @@
 
 	return 1;
 }
+/*
+ * Parse a <resolvers> section.
+ * Returns the error code, 0 if OK, or any combination of :
+ *  - ERR_ABORT: must abort ASAP
+ *  - ERR_FATAL: we can continue parsing but not start the service
+ *  - ERR_WARN: a warning has been emitted
+ *  - ERR_ALERT: an alert has been emitted
+ * Only the two first ones can stop processing, the two others are just
+ * indicators.
+ */
+int cfg_parse_resolvers(const char *file, int linenum, char **args, int kwm)
+{
+	const char *err;
+	int err_code = 0;
+	char *errmsg = NULL;
+
+	if (strcmp(args[0], "resolvers") == 0) { /* new resolvers section */
+		if (!*args[1]) {
+			ha_alert("parsing [%s:%d] : missing name for resolvers section.\n", file, linenum);
+			err_code |= ERR_ALERT | ERR_ABORT;
+			goto out;
+		}
+
+		err = invalid_char(args[1]);
+		if (err) {
+			ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
+				 file, linenum, *err, args[0], args[1]);
+			err_code |= ERR_ALERT | ERR_ABORT;
+			goto out;
+		}
+
+		list_for_each_entry(curr_resolvers, &sec_resolvers, list) {
+			/* Error if two resolvers owns the same name */
+			if (strcmp(curr_resolvers->id, args[1]) == 0) {
+				ha_alert("Parsing [%s:%d]: resolvers '%s' has same name as another resolvers (declared at %s:%d).\n",
+					 file, linenum, args[1], curr_resolvers->conf.file, curr_resolvers->conf.line);
+				err_code |= ERR_ALERT | ERR_ABORT;
+			}
+		}
+
+		if ((curr_resolvers = calloc(1, sizeof(*curr_resolvers))) == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+			err_code |= ERR_ALERT | ERR_ABORT;
+			goto out;
+		}
+
+		/* default values */
+		LIST_ADDQ(&sec_resolvers, &curr_resolvers->list);
+		curr_resolvers->conf.file = strdup(file);
+		curr_resolvers->conf.line = linenum;
+		curr_resolvers->id = strdup(args[1]);
+		curr_resolvers->query_ids = EB_ROOT;
+		/* default maximum response size */
+		curr_resolvers->accepted_payload_size = 512;
+		/* default hold period for nx, other, refuse and timeout is 30s */
+		curr_resolvers->hold.nx = 30000;
+		curr_resolvers->hold.other = 30000;
+		curr_resolvers->hold.refused = 30000;
+		curr_resolvers->hold.timeout = 30000;
+		curr_resolvers->hold.obsolete = 0;
+		/* default hold period for valid is 10s */
+		curr_resolvers->hold.valid = 10000;
+		curr_resolvers->timeout.resolve = 1000;
+		curr_resolvers->timeout.retry   = 1000;
+		curr_resolvers->resolve_retries = 3;
+		curr_resolvers->nb_nameservers  = 0;
+		LIST_INIT(&curr_resolvers->nameservers);
+		LIST_INIT(&curr_resolvers->resolutions.curr);
+		LIST_INIT(&curr_resolvers->resolutions.wait);
+		HA_SPIN_INIT(&curr_resolvers->lock);
+	}
+	else if (strcmp(args[0], "nameserver") == 0) { /* nameserver definition */
+		struct dns_nameserver *newnameserver = NULL;
+		struct sockaddr_storage *sk;
+		int port1, port2;
+
+		if (!*args[2]) {
+			ha_alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
+				 file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+		err = invalid_char(args[1]);
+		if (err) {
+			ha_alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n",
+				 file, linenum, *err, args[1]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+		list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
+			/* 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], newnameserver->conf.file, newnameserver->conf.line);
+				err_code |= ERR_ALERT | ERR_FATAL;
+			}
+		}
+
+		if ((newnameserver = calloc(1, sizeof(*newnameserver))) == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+			err_code |= ERR_ALERT | ERR_ABORT;
+			goto out;
+		}
+
+		/* the nameservers are linked backward first */
+		LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
+		newnameserver->resolvers = curr_resolvers;
+		newnameserver->conf.file = strdup(file);
+		newnameserver->conf.line = linenum;
+		newnameserver->id = strdup(args[1]);
+
+		sk = str2sa_range(args[2], NULL, &port1, &port2, NULL, NULL,
+		                  &errmsg, NULL, NULL, PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_DGRAM);
+		if (!sk) {
+			ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+		newnameserver->addr = *sk;
+	}
+	else if (strcmp(args[0], "parse-resolv-conf") == 0) {
+		struct dns_nameserver *newnameserver = NULL;
+		const char *whitespace = "\r\n\t ";
+		char *resolv_line = NULL;
+		int resolv_linenum = 0;
+		FILE *f = NULL;
+		char *address = NULL;
+		struct sockaddr_storage *sk = NULL;
+		struct protocol *proto;
+		int duplicate_name = 0;
+
+		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 resolv_out;
+		}
+
+		if ((f = fopen("/etc/resolv.conf", "r")) == NULL) {
+			ha_alert("parsing [%s:%d] : failed to open /etc/resolv.conf.\n",
+				 file, linenum);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto resolv_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 resolv_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 == resolv_line + 10)
+				continue;
+
+			if (address == NULL) {
+				ha_warning("parsing [/etc/resolv.conf:%d] : nameserver line is missing address.\n",
+					   resolv_linenum);
+				err_code |= ERR_WARN;
+				continue;
+			}
+
+			duplicate_name = 0;
+			list_for_each_entry(newnameserver, &curr_resolvers->nameservers, list) {
+				if (strcmp(newnameserver->id, address) == 0) {
+					ha_warning("Parsing [/etc/resolv.conf:%d] : generated name for /etc/resolv.conf nameserver '%s' conflicts with another nameserver (declared at %s:%d), it appears to be a duplicate and will be excluded.\n",
+						 resolv_linenum, address, newnameserver->conf.file, newnameserver->conf.line);
+					err_code |= ERR_WARN;
+					duplicate_name = 1;
+				}
+			}
+
+			if (duplicate_name)
+				continue;
+
+			memset(sk, 0, sizeof(*sk));
+			if (!str2ip2(address, sk, 1)) {
+				ha_warning("parsing [/etc/resolv.conf:%d] : address '%s' could not be recognized, nameserver will be excluded.\n",
+					   resolv_linenum, address);
+				err_code |= ERR_WARN;
+				continue;
+			}
+
+			set_host_port(sk, 53);
+
+			proto = protocol_by_family(sk->ss_family);
+			if (!proto || !proto->connect) {
+				ha_warning("parsing [/etc/resolv.conf:%d] : '%s' : connect() not supported for this address family.\n",
+					   resolv_linenum, address);
+				err_code |= ERR_WARN;
+				continue;
+			}
+
+			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 resolv_out;
+			}
+
+			newnameserver->conf.file = strdup("/etc/resolv.conf");
+			if (newnameserver->conf.file == NULL) {
+				ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				free(newnameserver);
+				goto resolv_out;
+			}
+
+			newnameserver->id = strdup(address);
+			if (newnameserver->id == NULL) {
+				ha_alert("parsing [/etc/resolv.conf:%d] : out of memory.\n", resolv_linenum);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				free((char *)newnameserver->conf.file);
+				free(newnameserver);
+				goto resolv_out;
+			}
+
+			newnameserver->resolvers = curr_resolvers;
+			newnameserver->conf.line = resolv_linenum;
+			newnameserver->addr = *sk;
+
+			LIST_ADDQ(&curr_resolvers->nameservers, &newnameserver->list);
+		}
+
+resolv_out:
+		free(sk);
+		free(resolv_line);
+		if (f != NULL)
+			fclose(f);
+	}
+	else if (strcmp(args[0], "hold") == 0) { /* hold periods */
+		const char *res;
+		unsigned int time;
+
+		if (!*args[2]) {
+			ha_alert("parsing [%s:%d] : '%s' expects an <event> and a <time> as arguments.\n",
+				 file, linenum, args[0]);
+			ha_alert("<event> can be either 'valid', 'nx', 'refused', 'timeout', or 'other'\n");
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		res = parse_time_err(args[2], &time, TIME_UNIT_MS);
+		if (res == PARSE_TIME_OVER) {
+			ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s>, maximum value is 2147483647 ms (~24.8 days).\n",
+			         file, linenum, args[1], args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		else if (res == PARSE_TIME_UNDER) {
+			ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s>, minimum non-null value is 1 ms.\n",
+			         file, linenum, args[1], args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		else if (res) {
+			ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s>.\n",
+				 file, linenum, *res, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		if (strcmp(args[1], "nx") == 0)
+			curr_resolvers->hold.nx = time;
+		else if (strcmp(args[1], "other") == 0)
+			curr_resolvers->hold.other = time;
+		else if (strcmp(args[1], "refused") == 0)
+			curr_resolvers->hold.refused = time;
+		else if (strcmp(args[1], "timeout") == 0)
+			curr_resolvers->hold.timeout = time;
+		else if (strcmp(args[1], "valid") == 0)
+			curr_resolvers->hold.valid = time;
+		else if (strcmp(args[1], "obsolete") == 0)
+			curr_resolvers->hold.obsolete = time;
+		else {
+			ha_alert("parsing [%s:%d] : '%s' unknown <event>: '%s', expects either 'nx', 'timeout', 'valid', 'obsolete' or 'other'.\n",
+				 file, linenum, args[0], args[1]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+	}
+	else if (strcmp(args[0], "accepted_payload_size") == 0) {
+		int i = 0;
+
+		if (!*args[1]) {
+			ha_alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
+				 file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+		i = atoi(args[1]);
+		if (i < DNS_HEADER_SIZE || i > DNS_MAX_UDP_MESSAGE) {
+			ha_alert("parsing [%s:%d] : '%s' must be between %d and %d inclusive (was %s).\n",
+				 file, linenum, args[0], DNS_HEADER_SIZE, DNS_MAX_UDP_MESSAGE, args[1]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+
+		curr_resolvers->accepted_payload_size = i;
+	}
+	else if (strcmp(args[0], "resolution_pool_size") == 0) {
+		ha_alert("parsing [%s:%d] : '%s' directive is not supported anymore (it never appeared in a stable release).\n",
+			   file, linenum, args[0]);
+		err_code |= ERR_ALERT | ERR_FATAL;
+		goto out;
+	}
+	else if (strcmp(args[0], "resolve_retries") == 0) {
+		if (!*args[1]) {
+			ha_alert("parsing [%s:%d] : '%s' expects <nb> as argument.\n",
+				 file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		curr_resolvers->resolve_retries = atoi(args[1]);
+	}
+	else if (strcmp(args[0], "timeout") == 0) {
+		if (!*args[1]) {
+			ha_alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments.\n",
+				 file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		else if (strcmp(args[1], "retry") == 0 ||
+			 strcmp(args[1], "resolve") == 0) {
+			const char *res;
+			unsigned int tout;
+
+			if (!*args[2]) {
+				ha_alert("parsing [%s:%d] : '%s %s' expects <time> as argument.\n",
+					 file, linenum, args[0], args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			res = parse_time_err(args[2], &tout, TIME_UNIT_MS);
+			if (res == PARSE_TIME_OVER) {
+				ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s %s>, maximum value is 2147483647 ms (~24.8 days).\n",
+					 file, linenum, args[2], args[0], args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			else if (res == PARSE_TIME_UNDER) {
+				ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s %s>, minimum non-null value is 1 ms.\n",
+					 file, linenum, args[2], args[0], args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			else if (res) {
+				ha_alert("parsing [%s:%d]: unexpected character '%c' in argument to <%s %s>.\n",
+					 file, linenum, *res, args[0], args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			if (args[1][2] == 't')
+				curr_resolvers->timeout.retry = tout;
+			else
+				curr_resolvers->timeout.resolve = tout;
+		}
+		else {
+			ha_alert("parsing [%s:%d] : '%s' expects 'retry' or 'resolve' and <time> as arguments got '%s'.\n",
+				 file, linenum, args[0], args[1]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+	}
+	else if (*args[0] != 0) {
+		ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], cursection);
+		err_code |= ERR_ALERT | ERR_FATAL;
+		goto out;
+	}
+
+ out:
+	free(errmsg);
+	return err_code;
+}
 
+REGISTER_CONFIG_SECTION("resolvers",      cfg_parse_resolvers, NULL);
 REGISTER_POST_DEINIT(resolvers_deinit);
 REGISTER_CONFIG_POSTPARSER("dns runtime resolver", resolvers_finalize_config);