MEDIUM: dns: add a "resolve-net" option which allow to prefer an ip in a network

This options prioritize th choice of an ip address matching a network. This is
useful with clouds to prefer a local ip. In some cases, a cloud high
avalailibility service can be announced with many ip addresses on many
differents datacenters. The latency between datacenter is not negligible, so
this patch permitsto prefers a local datacenter. If none address matchs the
configured network, another address is selected.
diff --git a/src/server.c b/src/server.c
index 84dad38..30722fc 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1020,6 +1020,10 @@
 			newsrv->dns_opts.family_prio = curproxy->defsrv.dns_opts.family_prio;
 			if (newsrv->dns_opts.family_prio == AF_UNSPEC)
 				newsrv->dns_opts.family_prio = AF_INET6;
+			memcpy(newsrv->dns_opts.pref_net,
+			       curproxy->defsrv.dns_opts.pref_net,
+			       sizeof(newsrv->dns_opts.pref_net));
+			newsrv->dns_opts.pref_net_nb = curproxy->defsrv.dns_opts.pref_net_nb;
 
 			cur_arg = 3;
 		} else {
@@ -1090,6 +1094,62 @@
 				}
 				cur_arg += 2;
 			}
+			else if (!strcmp(args[cur_arg], "resolve-net")) {
+				char *p, *e;
+				unsigned char mask;
+				struct dns_options *opt;
+
+				if (!args[cur_arg + 1] || args[cur_arg + 1][0] == '\0') {
+					Alert("parsing [%s:%d]: '%s' expects a list of networks.\n",
+					      file, linenum, args[cur_arg]);
+					err_code |= ERR_ALERT | ERR_FATAL;
+					goto out;
+				}
+
+				opt = &newsrv->dns_opts;
+
+				/* Split arguments by comma, and convert it from ipv4 or ipv6
+				 * string network in in_addr or in6_addr.
+				 */
+				p = args[cur_arg + 1];
+				e = p;
+				while (*p != '\0') {
+					/* If no room avalaible, return error. */
+					if (opt->pref_net_nb > SRV_MAX_PREF_NET) {
+						Alert("parsing [%s:%d]: '%s' exceed %d networks.\n",
+						      file, linenum, args[cur_arg], SRV_MAX_PREF_NET);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					/* look for end or comma. */
+					while (*e != ',' && *e != '\0')
+						e++;
+					if (*e == ',') {
+						*e = '\0';
+						e++;
+					}
+					if (str2net(p, 0, &opt->pref_net[opt->pref_net_nb].addr.in4,
+					                  &opt->pref_net[opt->pref_net_nb].mask.in4)) {
+						/* Try to convert input string from ipv4 or ipv6 network. */
+						opt->pref_net[opt->pref_net_nb].family = AF_INET;
+					} else if (str62net(p, &opt->pref_net[opt->pref_net_nb].addr.in6,
+					                     &mask)) {
+						/* Try to convert input string from ipv6 network. */
+						len2mask6(mask, &opt->pref_net[opt->pref_net_nb].mask.in6);
+						opt->pref_net[opt->pref_net_nb].family = AF_INET6;
+					} else {
+						/* All network conversions fail, retrun error. */
+						Alert("parsing [%s:%d]: '%s': invalid network '%s'.\n",
+						      file, linenum, args[cur_arg], p);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					opt->pref_net_nb++;
+					p = e;
+				}
+
+				cur_arg += 2;
+			}
 			else if (!strcmp(args[cur_arg], "rise")) {
 				if (!*args[cur_arg + 1]) {
 					Alert("parsing [%s:%d]: '%s' expects an integer argument.\n",