MAJOR: server: postpone address resolution

Server addresses are not resolved anymore upon the first pass so that we
don't fail if an address cannot be resolved by the libc. Instead they are
processed all at once after the configuration is fully loaded, by the new
function srv_init_addr(). This function only acts on the server's address
if this address uses an FQDN, which appears in server->hostname.

For now the function does two things, to followup with HAProxy's historical
default behavior:

  1. apply server IP address found in server-state file if runtime DNS
     resolution is enabled for this server

  2. use the DNS resolver provided by the libc

If none of the 2 options above can find an IP address, then an error is
returned.

All of this will be needed to support the new server parameter "init-addr".
For now, the biggest user-visible change is that all server resolution errors
are dumped at once instead of causing a startup failure one by one.
diff --git a/include/proto/server.h b/include/proto/server.h
index a7cc5d5..c496689 100644
--- a/include/proto/server.h
+++ b/include/proto/server.h
@@ -46,6 +46,8 @@
 struct server *server_find_best_match(struct proxy *bk, char *name, int id, int *diff);
 void apply_server_state(void);
 void srv_compute_all_admin_states(struct proxy *px);
+int srv_set_addr_via_libc(struct server *srv, int *err_code);
+int srv_init_addr(void);
 
 /* functions related to server name resolution */
 int snr_update_srv_status(struct server *s);
diff --git a/include/types/server.h b/include/types/server.h
index 5d89212..19da2ad 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -227,6 +227,7 @@
 
 	char *resolvers_id;			/* resolvers section used by this server */
 	char *hostname;				/* server hostname */
+	char *lastaddr;				/* the address string provided by the server-state file */
 	struct dns_resolution *resolution;	/* server name resolution */
 	struct dns_options dns_opts;
 
diff --git a/src/haproxy.c b/src/haproxy.c
index 896d116..4806c56 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -958,6 +958,13 @@
 	for (px = proxy; px; px = px->next)
 		srv_compute_all_admin_states(px);
 
+	/* Apply servers' configured address */
+	err_code |= srv_init_addr();
+	if (err_code & (ERR_ABORT|ERR_FATAL)) {
+		Alert("Failed to initialize server(s) addr.\n");
+		exit(1);
+	}
+
 	if (global.mode & MODE_CHECK) {
 		struct peers *pr;
 		struct proxy *px;
diff --git a/src/server.c b/src/server.c
index f38ee8d..11add3c 100644
--- a/src/server.c
+++ b/src/server.c
@@ -34,6 +34,7 @@
 #include <proto/dns.h>
 
 static void srv_update_state(struct server *srv, int version, char **params);
+static int srv_apply_lastaddr(struct server *srv, int *err_code);
 
 /* List head of all known server keywords */
 static struct srv_kw_list srv_keywords = {
@@ -973,7 +974,7 @@
 			 *  - IP:+N => port=+N, relative
 			 *  - IP:-N => port=-N, relative
 			 */
-			sk = str2sa_range(args[2], &port1, &port2, &errmsg, NULL, &fqdn, 1);
+			sk = str2sa_range(args[2], &port1, &port2, &errmsg, NULL, &fqdn, 0);
 			if (!sk) {
 				Alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, args[0], args[1], errmsg);
 				err_code |= ERR_ALERT | ERR_FATAL;
@@ -2257,23 +2258,9 @@
 			}
 			server_recalc_eweight(srv);
 
-			/* update server IP only if DNS resolution is used on the server */
+			/* load server IP only if DNS resolution is used on the server */
 			if (srv->resolution) {
-				struct sockaddr_storage addr;
-
-				memset(&addr, 0, sizeof(struct sockaddr_storage));
-
-				if (str2ip2(params[0], &addr, AF_UNSPEC)) {
-					int port;
-
-					/* save the port, applies the new IP then reconfigure the port */
-					port = get_host_port(&srv->addr);
-					srv->addr.ss_family = addr.ss_family;
-					str2ip2(params[0], &srv->addr, srv->addr.ss_family);
-					set_host_port(&srv->addr, port);
-				}
-				else
-					chunk_appendf(msg, ", can't parse IP: %s", params[0]);
+				srv->lastaddr = strdup(params[0]);
 			}
 			break;
 		default:
@@ -3089,6 +3076,84 @@
 	return 1;
 }
 
+/* Sets the server's address (srv->addr) from srv->hostname using the libc's
+ * resolver. This is suited for initial address configuration. Returns 0 on
+ * success otherwise a non-zero error code. In case of error, *err_code, if
+ * not NULL, is filled up.
+ */
+int srv_set_addr_via_libc(struct server *srv, int *err_code)
+{
+	if (str2ip2(srv->hostname, &srv->addr, 1) == NULL) {
+		Alert("parsing [%s:%d] : 'server %s' : invalid address: '%s'\n",
+		      srv->conf.file, srv->conf.line, srv->id, srv->hostname);
+		if (err_code)
+			*err_code |= ERR_ALERT | ERR_FATAL;
+		return 1;
+	}
+	return 0;
+}
+
+/* Sets the server's address (srv->addr) from srv->lastaddr which was filled
+ * from the state file. This is suited for initial address configuration.
+ * Returns 0 on success otherwise a non-zero error code. In case of error,
+ * *err_code, if not NULL, is filled up.
+ */
+static int srv_apply_lastaddr(struct server *srv, int *err_code)
+{
+	if (!str2ip2(srv->lastaddr, &srv->addr, 0)) {
+		if (err_code)
+			*err_code |= ERR_WARN;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * This function parses all backends and all servers within each backend
+ * and performs servers' addr resolution based on information provided by:
+ *   - configuration file
+ *   - server-state file (states provided by an 'old' haproxy process)
+ *
+ * Returns 0 if no error, otherwise, a combination of ERR_ flags.
+ */
+int srv_init_addr(void)
+{
+	struct proxy *curproxy;
+	int return_code = 0;
+
+	curproxy = proxy;
+	while (curproxy) {
+		struct server *srv;
+		int err_code = 0;
+
+		/* servers are in backend only */
+		if (!(curproxy->cap & PR_CAP_BE))
+			goto srv_init_addr_next;
+
+		for (srv = curproxy->srv; srv; srv = srv->next) {
+			err_code = 0;
+
+			if (srv->lastaddr) {
+				if (srv_apply_lastaddr(srv, &err_code) == 0)
+					continue;
+				return_code |= err_code;
+			}
+
+			if (srv->hostname) {
+				if (srv_set_addr_via_libc(srv, &err_code) == 0)
+					continue;
+				return_code = err_code;
+			}
+		}
+
+ srv_init_addr_next:
+		curproxy = curproxy->next;
+	}
+
+	return return_code;
+}
+
+
 /*
  * Local variables:
  *  c-indent-level: 8