Hello HAProxy ML,

On 04/10/2017 05:00 PM, Baptiste wrote:


On Mon, Apr 10, 2017 at 2:30 PM, Willy Tarreau <[email protected]
<mailto:[email protected]>> wrote:

    On Mon, Apr 10, 2017 at 10:02:29AM +0200, Frederic Lecaille wrote:
    > With server templates, haproxy could preallocate 'server' objects which
    > would derive from 'default-server' (with same settings as default server
    > settings), but with remaining parameters which are unknown at parsing time
    > (for instance their names, addresses, anything else). In fact here, names 
or
    > addresses are particular settings: a default server has not any default 
name
    > or default address.

    Absolutely. And this combined with the recent features of dynamic
    consistent
    cookies and with Baptiste's upcoming DNS patches will easily result
    in pretty
    dynamic backends!

    Willy


I just had a look at the implementation of server-templates. To make it
work with DNS resolution, we need to find a way to provide a fqdn to
 the default-server directive. This might not be too complicated.
After this, the magic will happen!!!!

After this first patches for server template new feature, here is a new set attached to this mail which takes into an account what have been discussed with Baptiste and Willy.

#0001 patch fixes a minor bug which may be backported to haproxy 1.7 and 1.6.

#0002 upto #0005 patches implement server template feature.

"server-template" new keyword is added and supported in "backend" and "listen" sections.

Its syntax:
  server-temlate <prefix> <nb | range> <fqdn>[:port] <params*>

This may be used to initialize a list of servers with the same parameters, especially the same FQDN as requested by Baptiste.

#0006 patch updates the documentation.

Fill free to review/comment/test as needed.

Regards,

Fred.


>From ceeb3a4e39242924dd061438f0be27ed58d648a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= <[email protected]>
Date: Thu, 20 Apr 2017 13:36:25 +0200
Subject: [PATCH 6/6] DOC: Add documentation for new "server-template" keyword.

---
 doc/configuration.txt | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index e6ea2cf..488d8e2 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1969,6 +1969,7 @@ rspirep                                   -          X         X         X
 rsprep                                    -          X         X         X
 server                                    -          -         X         X
 server-state-file-name                    X          -         X         X
+server-template                           -          -         X         X
 source                                    X          -         X         X
 srvtimeout                  (deprecated)  X          -         X         X
 stats admin                               -          X         X         X
@@ -7521,6 +7522,44 @@ server-state-file-name [<file>]
   See also: "server-state-file-base", "load-server-state-from-file", and
   "show servers state"
 
+server-template <prefix> <nb | range> <fqdn>[:<port>] [params*]
+  Set a template for this backend to initialize servers with shared parameters.
+  This server names are built from <prefix> and <nb | range> parameters.
+  May be used in sections :   defaults | frontend | listen | backend
+                                 no    |    no    |   yes  |   yes
+
+  Arguments:
+    <prefix>  a prefix for the server names to be built.
+
+    <nb | range>
+              If <nb> is provided, this template initializes <nb> servers
+              with 1 upto <nb> as server name suffixes. A range <nb_low>-<nb_high>
+              may also been used to use <nb_low> upto <nb_high> as server name
+              suffixes.
+
+    <fqdn>    A FQDN for all the servers this template initializes.
+
+    <port>    Same meaning as "server" <port> argument (see "server" keyword).
+
+    <params*>
+              Remaining server parameter among all those supported by "server"
+              keyword.
+
+  Examples:
+    # Initializes 5 servers with srv_1, srv_2 and srv_3 as names,
+    # google.com as FQDN, and health-check enabled.
+    server-template srv 1-3 google.com:80 check
+
+    # or
+    server-template srv 3 google.com:80 check
+
+    # would be equivalent to:
+    server srv1 google.com:80 check
+    server srv2 google.com:80 check
+    server srv3 google.com:80 check
+
+
+
 source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | client | clientip } ]
 source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | hdr_ip(<hdr>[,<occ>]) } ]
 source <addr>[:<port>] [interface <name>]
-- 
2.1.4

>From 960693685da4d73f7c3c4ee6065aa7f5ae7134aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= <[email protected]>
Date: Fri, 14 Apr 2017 13:28:00 +0200
Subject: [PATCH 5/6] MINOR: server: Add server_template_init() function to
 initialize servers from a templates.

This patch adds server_template_init() function used to initialize servers
from server templates. It is called just after having parsed a 'server-template'
line.
---
 src/server.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 132 insertions(+), 2 deletions(-)

diff --git a/src/server.c b/src/server.c
index 9f07788..69c1ec3 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1650,14 +1650,73 @@ static void srv_ssl_settings_cpy(struct server *srv, struct server *src)
 #endif
 
 /*
+ * Allocate <srv> server dns resolution.
+ * May be safely called with a default server as <src> argument (without hostname).
+ */
+static void srv_alloc_dns_resolution(struct server *srv, const char *hostname)
+{
+	char *hostname_dn;
+	int hostname_dn_len;
+	struct dns_resolution *dst_dns_rslt;
+
+	if (!hostname)
+		return;
+
+	srv->hostname = strdup(hostname);
+	dst_dns_rslt = calloc(1, sizeof *dst_dns_rslt);
+	hostname_dn_len = dns_str_to_dn_label_len(hostname);
+	hostname_dn = calloc(hostname_dn_len + 1, sizeof(char));
+
+	if (!srv->hostname || !dst_dns_rslt || !hostname_dn)
+		goto err;
+
+	srv->resolution = dst_dns_rslt;
+	srv->resolution->hostname_dn = hostname_dn;
+	srv->resolution->hostname_dn_len = hostname_dn_len;
+
+	if (!dns_str_to_dn_label(srv->hostname,
+	                         srv->resolution->hostname_dn,
+	                         srv->resolution->hostname_dn_len + 1))
+		goto err;
+
+	srv->resolution->requester = srv;
+	srv->resolution->requester_cb = snr_resolution_cb;
+	srv->resolution->requester_error_cb = snr_resolution_error_cb;
+	srv->resolution->status = RSLV_STATUS_NONE;
+	srv->resolution->step = RSLV_STEP_NONE;
+	/* a first resolution has been done by the configuration parser */
+	srv->resolution->last_resolution = 0;
+
+	return;
+
+ err:
+	free(srv->hostname);
+	srv->hostname = NULL;
+	free(hostname_dn);
+	free(dst_dns_rslt);
+}
+
+/*
  * Copy <src> server settings to <srv> server allocating
  * everything needed.
+ * This function is not supposed to be called at any time, but only
+ * during server settings parsing or during server allocations from
+ * a server template, and just after having calloc()'ed a new server.
+ * So, <src> may only be a default server (when parsing server settings)
+ * or a server template (during server allocations from a server template).
+ * <srv_tmpl> distinguishes these two cases (must be 1 if <srv> is a template,
+ * 0 if not).
  */
-static void srv_settings_cpy(struct server *srv, struct server *src)
+static void srv_settings_cpy(struct server *srv, struct server *src, int srv_tmpl)
 {
 	/* Connection source settings copy */
 	srv_conn_src_cpy(srv, src);
 
+	if (srv_tmpl) {
+		srv->addr = src->addr;
+		srv->svc_port = src->svc_port;
+	}
+
 	srv->pp_opts = src->pp_opts;
 	if (src->rdr_pfx != NULL) {
 		srv->rdr_pfx = strdup(src->rdr_pfx);
@@ -1999,6 +2058,75 @@ static int srv_tmpl_parse_range(struct server *srv, const char *arg, int *nb_low
 	return 0;
 }
 
+static inline void srv_set_id_from_prefix(struct server *srv, const char *prefix, int nb)
+{
+	chunk_printf(&trash, "%s%d", prefix, nb);
+	free(srv->id);
+	srv->id = strdup(trash.str);
+}
+
+/*
+ * Initialize as much as possible servers from <srv> server template.
+ * Note that a server template is a special server with
+ * a few different parameters than a server which has
+ * been parsed mostly the same way as a server.
+ * Returns the number of servers succesfully allocated,
+ * 'srv' template included.
+ */
+static int server_template_init(struct server *srv, struct proxy *px)
+{
+	int i;
+	struct server *newsrv;
+
+	for (i = srv->tmpl_info.nb_low + 1; i <= srv->tmpl_info.nb_high; i++) {
+		int check_init_state;
+		int agent_init_state;
+
+		newsrv = new_server(px);
+		if (!newsrv)
+			goto err;
+
+		srv_settings_cpy(newsrv, srv, 1);
+		srv_alloc_dns_resolution(newsrv, srv->hostname);
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+		if (newsrv->sni_expr) {
+			newsrv->ssl_ctx.sni = srv_sni_sample_parse_expr(newsrv, px, NULL, 0, NULL);
+			if (!newsrv->ssl_ctx.sni)
+				goto err;
+		}
+#endif
+		/* Set this new server ID. */
+		srv_set_id_from_prefix(newsrv, srv->tmpl_info.prefix, i);
+
+		/* Initial checks states. */
+		check_init_state = CHK_ST_CONFIGURED | CHK_ST_ENABLED;
+		agent_init_state = CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
+
+		if (do_health_check_init(newsrv, px->options2 & PR_O2_CHK_ANY, check_init_state) ||
+		    do_server_agent_check_init(newsrv, agent_init_state))
+			goto err;
+
+		/* Linked backwards first. This will be restablished after parsing. */
+		newsrv->next = px->srv;
+		px->srv = newsrv;
+	}
+	srv_set_id_from_prefix(srv, srv->tmpl_info.prefix, srv->tmpl_info.nb_low);
+
+	return i - srv->tmpl_info.nb_low;
+
+ err:
+	srv_set_id_from_prefix(srv, srv->tmpl_info.prefix, srv->tmpl_info.nb_low);
+	if (newsrv)  {
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+		release_sample_expr(newsrv->ssl_ctx.sni);
+#endif
+		free_check(&newsrv->agent);
+		free_check(&newsrv->check);
+	}
+	free(newsrv);
+	return i - srv->tmpl_info.nb_low;
+}
+
 int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy)
 {
 	struct server *newsrv = NULL;
@@ -2174,7 +2302,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			}
 
 			/* Copy default server settings to new server settings. */
-			srv_settings_cpy(newsrv, &curproxy->defsrv);
+			srv_settings_cpy(newsrv, &curproxy->defsrv, 0);
 			cur_arg++;
 		} else {
 			newsrv = &curproxy->defsrv;
@@ -2609,6 +2737,8 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			err_code |= server_finalize_init(file, linenum, args, cur_arg, newsrv, curproxy);
 		if (err_code & ERR_FATAL)
 			goto out;
+		if (srv_tmpl)
+			server_template_init(newsrv, curproxy);
 	}
 	free(fqdn);
 	return 0;
-- 
2.1.4

>From 93d44078107c151978073d4e009f70db410639ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= <[email protected]>
Date: Thu, 13 Apr 2017 18:24:23 +0200
Subject: [PATCH 4/6] MINOR: server: Add 'server-template' new keyword
 supported in backend sections.

This patch makes backend sections support 'server-template' new keyword.
Such 'server-template' objects are parsed similarly to a 'server' object
by parse_server() function, but its first arguments are as follows:
    server-template <ID prefix> <nb | range> <ip | fqdn>:<port> ...

The remaining arguments are the same as for 'server' lines.

With such server template declarations, servers may be allocated with IDs
built from <ID prefix> and <nb | range> arguments.

For instance declaring:
    server-template foo 1-5 google.com:80 ...
or
    server-template foo 5 google.com:80 ...

would be equivalent to declare:
    server foo1 google.com:80 ...
    server foo2 google.com:80 ...
    server foo3 google.com:80 ...
    server foo4 google.com:80 ...
    server foo5 google.com:80 ...
---
 include/common/standard.h |   9 +++-
 include/types/server.h    |   9 ++++
 src/cfgparse.c            |   4 +-
 src/server.c              | 107 +++++++++++++++++++++++++++++++++++++++-------
 src/standard.c            |  25 +++++++++--
 5 files changed, 133 insertions(+), 21 deletions(-)

diff --git a/include/common/standard.h b/include/common/standard.h
index be719f7..6827111 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -259,13 +259,20 @@ unsigned int round_2dig(unsigned int i);
 extern const char *invalid_char(const char *name);
 
 /*
- * Checks <domainname> for invalid characters. Valid chars are [A-Za-z0-9_.-].
+ * Checks <name> for invalid characters. Valid chars are [A-Za-z0-9_.-].
  * If an invalid character is found, a pointer to it is returned.
  * If everything is fine, NULL is returned.
  */
 extern const char *invalid_domainchar(const char *name);
 
 /*
+ * Checks <name> for invalid characters. Valid chars are [A-Za-z_.-].
+ * If an invalid character is found, a pointer to it is returned.
+ * If everything is fine, NULL is returned.
+ */
+extern const char *invalid_prefix_char(const char *name);
+
+/*
  * converts <str> to a locally allocated struct sockaddr_storage *, and a
  * port range consisting in two integers. The low and high end are always set
  * even if the port is unspecified, in which case (0,0) is returned. The low
diff --git a/include/types/server.h b/include/types/server.h
index bfaa941..8d68dcb 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -274,6 +274,15 @@ struct server {
 		int line;			/* line where the section appears */
 		struct eb32_node id;		/* place in the tree of used IDs */
 	} conf;					/* config information */
+	/* Template information used only for server objects which
+	 * serve as template filled at parsing time and used during
+	 * server allocations from server templates.
+	 */
+	struct {
+		char *prefix;
+		int nb_low;
+		int nb_high;
+	} tmpl_info;
 };
 
 /* Descriptor for a "server" keyword. The ->parse() function returns 0 in case of
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 348b9e8..d44949a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2859,7 +2859,9 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
 	curproxy->conf.args.line = linenum;
 
 	/* Now let's parse the proxy-specific keywords */
-	if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) {
+	if (!strcmp(args[0], "server")         ||
+	    !strcmp(args[0], "default-server") ||
+	    !strcmp(args[0], "server-template")) {
 		err_code |= parse_server(file, linenum, args, curproxy, &defproxy);
 		if (err_code & ERR_FATAL)
 			goto out;
diff --git a/src/server.c b/src/server.c
index f25a7a0..9f07788 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1969,18 +1969,53 @@ static int server_finalize_init(const char *file, int linenum, char **args, int
 	return 0;
 }
 
+/*
+ * Parse as much as possible such a range string argument: low[-high]
+ * Set <nb_low> and <nb_high> values so that they may be reused by this loop
+ * for(int i = nb_low; i <= nb_high; i++)... with nb_low >= 1.
+ * Fails if 'low' < 0 or 'high' is present and not higher than 'low'.
+ * Returns 0 if succeeded, -1 if not.
+ */
+static int srv_tmpl_parse_range(struct server *srv, const char *arg, int *nb_low, int *nb_high)
+{
+	char *nb_high_arg;
+
+	*nb_high = 0;
+	chunk_printf(&trash, "%s", arg);
+	*nb_low = atoi(trash.str);
+
+	if ((nb_high_arg = strchr(trash.str, '-'))) {
+		*nb_high_arg++ = '\0';
+		*nb_high = atoi(nb_high_arg);
+	}
+	else {
+		*nb_high += *nb_low;
+		*nb_low = 1;
+	}
+
+	if (*nb_low < 0 || *nb_high < *nb_low)
+		return -1;
+
+	return 0;
+}
+
 int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy)
 {
 	struct server *newsrv = NULL;
-	const char *err;
+	const char *err = NULL;
 	char *errmsg = NULL;
 	int err_code = 0;
 	unsigned val;
 	char *fqdn = NULL;
 
-	if (!strcmp(args[0], "server") || !strcmp(args[0], "default-server")) {  /* server address */
+	if (!strcmp(args[0], "server")         ||
+	    !strcmp(args[0], "default-server") ||
+	    !strcmp(args[0], "server-template")) {
 		int cur_arg;
 		int defsrv = (*args[0] == 'd');
+		int srv = !defsrv && !strcmp(args[0], "server");
+		int srv_tmpl = !defsrv && !srv;
+		int tmpl_range_low = 0, tmpl_range_high = 0;
 
 		if (!defsrv && curproxy == defproxy) {
 			Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@@ -1990,21 +2025,49 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 		else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_ALERT | ERR_FATAL;
 
-		if (!defsrv && !*args[2]) {
-			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;
+		/* There is no mandatory first arguments for default server. */
+		if (srv) {
+			if (!*args[2]) {
+				/* 'server' line number of argument check. */
+				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]);
+		}
+		else if (srv_tmpl) {
+			if (!*args[3]) {
+				/* 'server-template' line number of argument check. */
+				Alert("parsing [%s:%d] : '%s' expects <prefix> <nb | range> <addr>[:<port>] as arguments.\n",
+					  file, linenum, args[0]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			err = invalid_prefix_char(args[1]);
 		}
 
-		err = invalid_char(args[1]);
-		if (err && !defsrv) {
-			Alert("parsing [%s:%d] : character '%c' is not permitted in server name '%s'.\n",
-			      file, linenum, *err, args[1]);
+		if (err) {
+			Alert("parsing [%s:%d] : character '%c' is not permitted in %s %s '%s'.\n",
+			      file, linenum, *err, args[0], srv ? "name" : "prefix", args[1]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
 
+		cur_arg = 2;
+		if (srv_tmpl) {
+			/* Parse server-template <nb | range> arg. */
+			if (srv_tmpl_parse_range(newsrv, args[cur_arg], &tmpl_range_low, &tmpl_range_high) < 0) {
+				Alert("parsing [%s:%d] : Wrong %s number or range arg '%s'.\n",
+					  file, linenum, args[0], args[cur_arg]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			cur_arg++;
+		}
+
 		if (!defsrv) {
 			struct sockaddr_storage *sk;
 			int port1, port2, port;
@@ -2018,12 +2081,24 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 				goto out;
 			}
 
+			if (srv_tmpl) {
+				newsrv->tmpl_info.nb_low = tmpl_range_low;
+				newsrv->tmpl_info.nb_high = tmpl_range_high;
+			}
+
 			/* the servers are linked backwards first */
 			newsrv->next = curproxy->srv;
 			curproxy->srv = newsrv;
 			newsrv->conf.file = strdup(file);
 			newsrv->conf.line = linenum;
-			newsrv->id = strdup(args[1]);
+			/* Note: for a server template, its id is its prefix.
+			 * This is a temporary id which will be used for server allocations to come
+			 * after parsing.
+			 */
+			if (srv)
+				newsrv->id = strdup(args[1]);
+			else
+				newsrv->tmpl_info.prefix = strdup(args[1]);
 
 			/* several ways to check the port component :
 			 *  - IP    => port=+0, relative (IPv4 only)
@@ -2032,7 +2107,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			 *  - IP:+N => port=+N, relative
 			 *  - IP:-N => port=-N, relative
 			 */
-			sk = str2sa_range(args[2], &port, &port1, &port2, &errmsg, NULL, &fqdn, 0);
+			sk = str2sa_range(args[cur_arg], &port, &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;
@@ -2073,7 +2148,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 				goto skip_name_resolution;
 			if ((dns_str_to_dn_label(newsrv->hostname, curr_resolution->hostname_dn, curr_resolution->hostname_dn_len + 1)) == NULL) {
 				Alert("parsing [%s:%d] : Invalid hostname '%s'\n",
-				      file, linenum, args[2]);
+				      file, linenum, args[cur_arg]);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
@@ -2093,14 +2168,14 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 
 			if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
 				Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
-				      file, linenum, newsrv->addr.ss_family, args[2]);
+				      file, linenum, newsrv->addr.ss_family, args[cur_arg]);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
 			}
 
 			/* Copy default server settings to new server settings. */
 			srv_settings_cpy(newsrv, &curproxy->defsrv);
-			cur_arg = 3;
+			cur_arg++;
 		} else {
 			newsrv = &curproxy->defsrv;
 			cur_arg = 1;
diff --git a/src/standard.c b/src/standard.c
index 99f7066..6abedb4 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -592,17 +592,18 @@ const char *invalid_char(const char *name)
 }
 
 /*
- * Checks <domainname> for invalid characters. Valid chars are [A-Za-z0-9_.-].
+ * Checks <name> for invalid characters. Valid chars are [_.-] and those
+ * accepted by <f> function.
  * If an invalid character is found, a pointer to it is returned.
  * If everything is fine, NULL is returned.
  */
-const char *invalid_domainchar(const char *name) {
+static inline const char *__invalid_char(const char *name, int (*f)(int)) {
 
 	if (!*name)
 		return name;
 
 	while (*name) {
-		if (!isalnum((int)(unsigned char)*name) && *name != '.' &&
+		if (!f((int)(unsigned char)*name) && *name != '.' &&
 		    *name != '_' && *name != '-')
 			return name;
 
@@ -613,6 +614,24 @@ const char *invalid_domainchar(const char *name) {
 }
 
 /*
+ * Checks <name> for invalid characters. Valid chars are [A-Za-z0-9_.-].
+ * If an invalid character is found, a pointer to it is returned.
+ * If everything is fine, NULL is returned.
+ */
+const char *invalid_domainchar(const char *name) {
+	return __invalid_char(name, isalnum);
+}
+
+/*
+ * Checks <name> for invalid characters. Valid chars are [A-Za-z_.-].
+ * If an invalid character is found, a pointer to it is returned.
+ * If everything is fine, NULL is returned.
+ */
+const char *invalid_prefix_char(const char *name) {
+	return __invalid_char(name, isalpha);
+}
+
+/*
  * converts <str> to a struct sockaddr_storage* provided by the caller. The
  * caller must have zeroed <sa> first, and may have set sa->ss_family to force
  * parse a specific address format. If the ss_family is 0 or AF_UNSPEC, then
-- 
2.1.4

>From 9b18429362bf296c348f40fd02792b385d237332 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= <[email protected]>
Date: Thu, 30 Mar 2017 17:32:36 +0200
Subject: [PATCH 3/6] MINOR: server: Extract the code which finalizes server
 initializations after 'server' lines parsing.

This patch moves the code which is responsible of finalizing server initializations
after having fully parsed a 'server' line (health-check, agent check and SNI expression
initializations) from parse_server() to new functions.
---
 src/server.c | 343 +++++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 218 insertions(+), 125 deletions(-)

diff --git a/src/server.c b/src/server.c
index cd3a2b4..f25a7a0 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1529,20 +1529,26 @@ const char *server_parse_maxconn_change_request(struct server *sv,
 }
 
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-static int server_parse_sni_expr(struct server *newsrv, struct proxy *px, char **err)
+static struct sample_expr *srv_sni_sample_parse_expr(struct server *srv, struct proxy *px,
+                                                     const char *file, int linenum, char **err)
 {
 	int idx;
-	struct sample_expr *expr;
 	const char *args[] = {
-		newsrv->sni_expr,
+		srv->sni_expr,
 		NULL,
 	};
 
 	idx = 0;
-	proxy->conf.args.ctx = ARGC_SRV;
+	px->conf.args.ctx = ARGC_SRV;
+
+	return sample_parse_expr((char **)args, &idx, file, linenum, err, &px->conf.args);
+}
+
+static int server_parse_sni_expr(struct server *newsrv, struct proxy *px, char **err)
+{
+	struct sample_expr *expr;
 
-	expr = sample_parse_expr((char **)args, &idx, px->conf.file, px->conf.line,
-	                         err, &proxy->conf.args);
+	expr = srv_sni_sample_parse_expr(newsrv, px, px->conf.file, px->conf.line, err);
 	if (!expr) {
 		memprintf(err, "error detected while parsing sni expression : %s", *err);
 		return ERR_ALERT | ERR_FATAL;
@@ -1552,7 +1558,7 @@ static int server_parse_sni_expr(struct server *newsrv, struct proxy *px, char *
 		memprintf(err, "error detected while parsing sni expression : "
 		          " fetch method '%s' extracts information from '%s', "
 		          "none of which is available here.\n",
-		          args[0], sample_src_names(expr->fetch->use));
+		          newsrv->sni_expr, sample_src_names(expr->fetch->use));
 		return ERR_ALERT | ERR_FATAL;
 	}
 
@@ -1762,6 +1768,207 @@ static struct server *new_server(struct proxy *proxy)
 	return srv;
 }
 
+/*
+ * Validate <srv> server health-check settings.
+ * Returns 0 if everything is OK, -1 if not.
+ */
+static int server_healthcheck_validate(const char *file, int linenum, struct server *srv)
+{
+	struct tcpcheck_rule *r = NULL;
+	struct list *l;
+
+	/*
+	 * We need at least a service port, a check port or the first tcp-check rule must
+	 * be a 'connect' one when checking an IPv4/IPv6 server.
+	 */
+	if ((srv_check_healthcheck_port(&srv->check) != 0) ||
+	    (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
+		return 0;
+
+	r = (struct tcpcheck_rule *)srv->proxy->tcpcheck_rules.n;
+	if (!r) {
+		Alert("parsing [%s:%d] : server %s has neither service port nor check port. "
+			  "Check has been disabled.\n",
+			  file, linenum, srv->id);
+		return -1;
+	}
+
+	/* search the first action (connect / send / expect) in the list */
+	l = &srv->proxy->tcpcheck_rules;
+	list_for_each_entry(r, l, list) {
+		if (r->action != TCPCHK_ACT_COMMENT)
+			break;
+	}
+
+	if ((r->action != TCPCHK_ACT_CONNECT) || !r->port) {
+		Alert("parsing [%s:%d] : server %s has neither service port nor check port "
+			  "nor tcp_check rule 'connect' with port information. Check has been disabled.\n",
+			  file, linenum, srv->id);
+		return -1;
+	}
+
+	/* scan the tcp-check ruleset to ensure a port has been configured */
+	l = &srv->proxy->tcpcheck_rules;
+	list_for_each_entry(r, l, list) {
+		if ((r->action == TCPCHK_ACT_CONNECT) && (!r->port)) {
+			Alert("parsing [%s:%d] : server %s has neither service port nor check port, "
+				  "and a tcp_check rule 'connect' with no port information. Check has been disabled.\n",
+				  file, linenum, srv->id);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize <srv> health-check structure.
+ * Returns the error string in case of memory allocation failure, NULL if not.
+ */
+static const char *do_health_check_init(struct server *srv, int check_type, int state)
+{
+	const char *ret;
+
+	if (!srv->do_check)
+		return NULL;
+
+	ret = init_check(&srv->check, check_type);
+	if (ret)
+		return ret;
+
+	if (srv->resolution)
+		srv->resolution->opts = &srv->dns_opts;
+
+	srv->check.state |= state;
+	global.maxsock++;
+
+	return NULL;
+}
+
+static int server_health_check_init(const char *file, int linenum,
+                                    struct server *srv, struct proxy *curproxy)
+{
+	const char *ret;
+
+	if (!srv->do_check)
+		return 0;
+
+	if (srv->trackit) {
+		Alert("parsing [%s:%d]: unable to enable checks and tracking at the same time!\n",
+			file, linenum);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	if (server_healthcheck_validate(file, linenum, srv) < 0)
+		return ERR_ALERT | ERR_ABORT;
+
+	/* note: check type will be set during the config review phase */
+	ret = do_health_check_init(srv, 0, CHK_ST_CONFIGURED | CHK_ST_ENABLED);
+	if (ret) {
+		Alert("parsing [%s:%d] : %s.\n", file, linenum, ret);
+		return ERR_ALERT | ERR_ABORT;
+	}
+
+	return 0;
+}
+
+/*
+ * Initialize <srv> agent check structure.
+ * Returns the error string in case of memory allocation failure, NULL if not.
+ */
+static const char *do_server_agent_check_init(struct server *srv, int state)
+{
+	const char *ret;
+
+	if (!srv->do_agent)
+		return NULL;
+
+	ret = init_check(&srv->agent, PR_O2_LB_AGENT_CHK);
+	if (ret)
+		return ret;
+
+	if (!srv->agent.inter)
+		srv->agent.inter = srv->check.inter;
+
+	srv->agent.state |= state;
+	global.maxsock++;
+
+	return NULL;
+}
+
+static int server_agent_check_init(const char *file, int linenum,
+                                   struct server *srv, struct proxy *curproxy)
+{
+	const char *ret;
+
+	if (!srv->do_agent)
+		return 0;
+
+	if (!srv->agent.port) {
+		Alert("parsing [%s:%d] : server %s does not have agent port. Agent check has been disabled.\n",
+			  file, linenum, srv->id);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	ret = do_server_agent_check_init(srv, CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT);
+	if (ret) {
+		Alert("parsing [%s:%d] : %s.\n", file, linenum, ret);
+		return ERR_ALERT | ERR_ABORT;
+	}
+
+	return 0;
+}
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+static int server_sni_expr_init(const char *file, int linenum, char **args, int cur_arg,
+                                struct server *srv, struct proxy *proxy)
+{
+	int ret;
+	char *err = NULL;
+
+	if (!srv->sni_expr)
+		return 0;
+
+	ret = server_parse_sni_expr(srv, proxy, &err);
+	if (!ret)
+	    return 0;
+
+	display_parser_err(file, linenum, args, cur_arg, &err);
+	free(err);
+
+	return ret;
+}
+#endif
+
+/*
+ * Server initializations finalization.
+ * Initialize health check, agent check and SNI expression if enabled.
+ * Must not be called for a default server instance.
+ */
+static int server_finalize_init(const char *file, int linenum, char **args, int cur_arg,
+                                struct server *srv, struct proxy *px)
+{
+	int ret;
+
+	if ((ret = server_health_check_init(file, linenum, srv, px)) != 0 ||
+	    (ret = server_agent_check_init(file, linenum, srv, px)) != 0) {
+		return ret;
+	}
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+	if ((ret = server_sni_expr_init(file, linenum, args, cur_arg, srv, px)) != 0)
+		return ret;
+#endif
+
+	if (srv->flags & SRV_F_BACKUP)
+		px->srv_bck++;
+	else
+		px->srv_act++;
+	srv_lb_commit_status(srv);
+
+	return 0;
+}
+
 int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy)
 {
 	struct server *newsrv = NULL;
@@ -2323,124 +2530,10 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			}
 		}
 
-		/* This check is done only for 'server' instances. */
-		if (!defsrv && newsrv->do_check) {
-			const char *ret;
-
-			if (newsrv->trackit) {
-				Alert("parsing [%s:%d]: unable to enable checks and tracking at the same time!\n",
-					file, linenum);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-
-			/*
-			 * We need at least a service port, a check port or the first tcp-check rule must
-			 * be a 'connect' one when checking an IPv4/IPv6 server.
-			 */
-			if ((srv_check_healthcheck_port(&newsrv->check) == 0) &&
-			    (is_inet_addr(&newsrv->check.addr) ||
-			     (!is_addr(&newsrv->check.addr) && is_inet_addr(&newsrv->addr)))) {
-				struct tcpcheck_rule *r = NULL;
-				struct list *l;
-
-				r = (struct tcpcheck_rule *)newsrv->proxy->tcpcheck_rules.n;
-				if (!r) {
-					Alert("parsing [%s:%d] : server %s has neither service port nor check port. Check has been disabled.\n",
-					      file, linenum, newsrv->id);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				/* search the first action (connect / send / expect) in the list */
-				l = &newsrv->proxy->tcpcheck_rules;
-				list_for_each_entry(r, l, list) {
-					if (r->action != TCPCHK_ACT_COMMENT)
-						break;
-				}
-				if ((r->action != TCPCHK_ACT_CONNECT) || !r->port) {
-					Alert("parsing [%s:%d] : server %s has neither service port nor check port nor tcp_check rule 'connect' with port information. Check has been disabled.\n",
-					      file, linenum, newsrv->id);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				else {
-					/* scan the tcp-check ruleset to ensure a port has been configured */
-					l = &newsrv->proxy->tcpcheck_rules;
-					list_for_each_entry(r, l, list) {
-						if ((r->action == TCPCHK_ACT_CONNECT) && (!r->port)) {
-							Alert("parsing [%s:%d] : server %s has neither service port nor check port, and a tcp_check rule 'connect' with no port information. Check has been disabled.\n",
-							      file, linenum, newsrv->id);
-							err_code |= ERR_ALERT | ERR_FATAL;
-							goto out;
-						}
-					}
-				}
-			}
-
-			/* note: check type will be set during the config review phase */
-			ret = init_check(&newsrv->check, 0);
-			if (ret) {
-				Alert("parsing [%s:%d] : %s.\n", file, linenum, ret);
-				err_code |= ERR_ALERT | ERR_ABORT;
-				goto out;
-			}
-
-			if (newsrv->resolution)
-				newsrv->resolution->opts = &newsrv->dns_opts;
-
-			newsrv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
-			global.maxsock++;
-		}
-
-		if (!defsrv && newsrv->do_agent) {
-			const char *ret;
-
-			if (!newsrv->agent.port) {
-				Alert("parsing [%s:%d] : server %s does not have agent port. Agent check has been disabled.\n",
-				      file, linenum, newsrv->id);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-
-			if (!newsrv->agent.inter)
-				newsrv->agent.inter = newsrv->check.inter;
-
-			ret = init_check(&newsrv->agent, PR_O2_LB_AGENT_CHK);
-			if (ret) {
-				Alert("parsing [%s:%d] : %s.\n", file, linenum, ret);
-				err_code |= ERR_ALERT | ERR_ABORT;
-				goto out;
-			}
-
-			newsrv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
-			global.maxsock++;
-		}
-
-		if (!defsrv) {
-			if (newsrv->flags & SRV_F_BACKUP)
-				curproxy->srv_bck++;
-			else
-				curproxy->srv_act++;
-
-			srv_lb_commit_status(newsrv);
-		}
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-		if (!defsrv && newsrv->sni_expr) {
-			int code;
-			char *err;
-
-			err = NULL;
-
-			code = server_parse_sni_expr(newsrv, curproxy, &err);
-			err_code |= code;
-			if (code) {
-				display_parser_err(file, linenum, args, cur_arg, &err);
-				free(err);
-				if (code & ERR_FATAL)
-					goto out;
-			}
-		}
-#endif
+		if (!defsrv)
+			err_code |= server_finalize_init(file, linenum, args, cur_arg, newsrv, curproxy);
+		if (err_code & ERR_FATAL)
+			goto out;
 	}
 	free(fqdn);
 	return 0;
-- 
2.1.4

>From 43a0bece055224dc05a284d12e59a2dc2f1f97ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= <[email protected]>
Date: Thu, 30 Mar 2017 14:18:30 +0200
Subject: [PATCH 2/6] MINOR: server: Extract the code responsible of copying
 default-server settings.

This patch moves the code responsible of copying default server settings
to a new server instance from parse_server() function to new defsrv_*_cpy()
functions which may be used both during server lines parsing and during server
templates initializations to come.

These defsrv_*_cpy() do not make any reference to anything else than default
server settings.
---
 src/server.c | 334 ++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 191 insertions(+), 143 deletions(-)

diff --git a/src/server.c b/src/server.c
index c9a19ed..cd3a2b4 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1575,6 +1575,193 @@ static void display_parser_err(const char *file, int linenum, char **args, int c
 		      file, linenum, args[0], args[1], args[cur_arg]);
 }
 
+static void srv_conn_src_sport_range_cpy(struct server *srv,
+                                            struct server *src)
+{
+	int range_sz;
+
+	range_sz = src->conn_src.sport_range->size;
+	if (range_sz > 0) {
+		srv->conn_src.sport_range = port_range_alloc_range(range_sz);
+		if (srv->conn_src.sport_range != NULL) {
+			int i;
+
+			for (i = 0; i < range_sz; i++) {
+				srv->conn_src.sport_range->ports[i] =
+					src->conn_src.sport_range->ports[i];
+			}
+		}
+	}
+}
+
+/*
+ * Copy <src> server connection source settings to <srv> server everything needed.
+ */
+static void srv_conn_src_cpy(struct server *srv, struct server *src)
+{
+	srv->conn_src.opts = src->conn_src.opts;
+	srv->conn_src.source_addr = src->conn_src.source_addr;
+
+	/* Source port range copy. */
+	if (src->conn_src.sport_range != NULL)
+		srv_conn_src_sport_range_cpy(srv, src);
+
+#ifdef CONFIG_HAP_TRANSPARENT
+	if (src->conn_src.bind_hdr_name != NULL) {
+		srv->conn_src.bind_hdr_name = strdup(src->conn_src.bind_hdr_name);
+		srv->conn_src.bind_hdr_len = strlen(src->conn_src.bind_hdr_name);
+	}
+	srv->conn_src.bind_hdr_occ = src->conn_src.bind_hdr_occ;
+	srv->conn_src.tproxy_addr  = src->conn_src.tproxy_addr;
+#endif
+	if (src->conn_src.iface_name != NULL)
+		srv->conn_src.iface_name = strdup(src->conn_src.iface_name);
+}
+
+/*
+ * Copy <src> server SSL settings to <srv> server allocating
+ * everything needed.
+ */
+#if defined(USE_OPENSSL)
+static void srv_ssl_settings_cpy(struct server *srv, struct server *src)
+{
+	if (src->ssl_ctx.ca_file != NULL)
+		srv->ssl_ctx.ca_file = strdup(src->ssl_ctx.ca_file);
+	if (src->ssl_ctx.crl_file != NULL)
+		srv->ssl_ctx.crl_file = strdup(src->ssl_ctx.crl_file);
+	if (src->ssl_ctx.client_crt != NULL)
+		srv->ssl_ctx.client_crt = strdup(src->ssl_ctx.client_crt);
+
+	srv->ssl_ctx.verify = src->ssl_ctx.verify;
+
+	if (src->ssl_ctx.verify_host != NULL)
+		srv->ssl_ctx.verify_host = strdup(src->ssl_ctx.verify_host);
+	if (src->ssl_ctx.ciphers != NULL)
+		srv->ssl_ctx.ciphers = strdup(src->ssl_ctx.ciphers);
+	if (src->sni_expr != NULL)
+		srv->sni_expr = strdup(src->sni_expr);
+}
+#endif
+
+/*
+ * Copy <src> server settings to <srv> server allocating
+ * everything needed.
+ */
+static void srv_settings_cpy(struct server *srv, struct server *src)
+{
+	/* Connection source settings copy */
+	srv_conn_src_cpy(srv, src);
+
+	srv->pp_opts = src->pp_opts;
+	if (src->rdr_pfx != NULL) {
+		srv->rdr_pfx = strdup(src->rdr_pfx);
+		srv->rdr_len = src->rdr_len;
+	}
+	if (src->cookie != NULL) {
+		srv->cookie = strdup(src->cookie);
+		srv->cklen  = src->cklen;
+	}
+	srv->use_ssl                  = src->use_ssl;
+	srv->check.addr = srv->agent.addr = src->check.addr;
+	srv->check.use_ssl            = src->check.use_ssl;
+	srv->check.port               = src->check.port;
+	/* Note: 'flags' field has potentially been already initialized. */
+	srv->flags                   |= src->flags;
+	srv->do_check                 = src->do_check;
+	srv->do_agent                 = src->do_agent;
+	if (srv->check.port)
+		srv->flags |= SRV_F_CHECKPORT;
+	srv->check.inter              = src->check.inter;
+	srv->check.fastinter          = src->check.fastinter;
+	srv->check.downinter          = src->check.downinter;
+	srv->agent.use_ssl            = src->agent.use_ssl;
+	srv->agent.port               = src->agent.port;
+	if (src->agent.send_string != NULL)
+		srv->agent.send_string = strdup(src->agent.send_string);
+	srv->agent.send_string_len    = src->agent.send_string_len;
+	srv->agent.inter              = src->agent.inter;
+	srv->agent.fastinter          = src->agent.fastinter;
+	srv->agent.downinter          = src->agent.downinter;
+	srv->maxqueue                 = src->maxqueue;
+	srv->minconn                  = src->minconn;
+	srv->maxconn                  = src->maxconn;
+	srv->slowstart                = src->slowstart;
+	srv->observe                  = src->observe;
+	srv->onerror                  = src->onerror;
+	srv->onmarkeddown             = src->onmarkeddown;
+	srv->onmarkedup               = src->onmarkedup;
+	if (src->trackit != NULL)
+		srv->trackit = strdup(src->trackit);
+	srv->consecutive_errors_limit = src->consecutive_errors_limit;
+	srv->uweight = srv->iweight   = src->iweight;
+
+	srv->check.send_proxy         = src->check.send_proxy;
+	/* health: up, but will fall down at first failure */
+	srv->check.rise = srv->check.health = src->check.rise;
+	srv->check.fall               = src->check.fall;
+
+	/* Here we check if 'disabled' is the default server state */
+	if (src->admin & (SRV_ADMF_CMAINT | SRV_ADMF_FMAINT)) {
+		srv->admin |= SRV_ADMF_CMAINT | SRV_ADMF_FMAINT;
+		srv->state        = SRV_ST_STOPPED;
+		srv->check.state |= CHK_ST_PAUSED;
+		srv->check.health = 0;
+	}
+
+	/* health: up but will fall down at first failure */
+	srv->agent.rise	= srv->agent.health = src->agent.rise;
+	srv->agent.fall	              = src->agent.fall;
+
+	if (src->resolvers_id != NULL)
+		srv->resolvers_id = strdup(src->resolvers_id);
+	srv->dns_opts.family_prio = src->dns_opts.family_prio;
+	if (srv->dns_opts.family_prio == AF_UNSPEC)
+		srv->dns_opts.family_prio = AF_INET6;
+	memcpy(srv->dns_opts.pref_net,
+	       src->dns_opts.pref_net,
+	       sizeof srv->dns_opts.pref_net);
+	srv->dns_opts.pref_net_nb     = src->dns_opts.pref_net_nb;
+
+	srv->init_addr_methods        = src->init_addr_methods;
+	srv->init_addr                = src->init_addr;
+#if defined(USE_OPENSSL)
+	srv_ssl_settings_cpy(srv, src);
+#endif
+#ifdef TCP_USER_TIMEOUT
+	srv->tcp_ut = src->tcp_ut;
+#endif
+}
+
+static struct server *new_server(struct proxy *proxy)
+{
+	struct server *srv;
+
+	srv = calloc(1, sizeof *srv);
+	if (!srv)
+		return NULL;
+
+	srv->obj_type = OBJ_TYPE_SERVER;
+	srv->proxy = proxy;
+	LIST_INIT(&srv->actconns);
+	LIST_INIT(&srv->pendconns);
+	LIST_INIT(&srv->priv_conns);
+	LIST_INIT(&srv->idle_conns);
+	LIST_INIT(&srv->safe_conns);
+
+	srv->state = SRV_ST_RUNNING; /* early server setup */
+	srv->last_change = now.tv_sec;
+
+	srv->check.status = HCHK_STATUS_INI;
+	srv->check.server = srv;
+	srv->check.tcpcheck_rules = &proxy->tcpcheck_rules;
+
+	srv->agent.status = HCHK_STATUS_INI;
+	srv->agent.server = srv;
+	srv->xprt  = srv->check.xprt = srv->agent.xprt = xprt_get(XPRT_RAW);
+
+	return srv;
+}
+
 int parse_server(const char *file, int linenum, char **args, struct proxy *curproxy, struct proxy *defproxy)
 {
 	struct server *newsrv = NULL;
@@ -1617,7 +1804,8 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			struct protocol *proto;
 			struct dns_resolution *curr_resolution;
 
-			if ((newsrv = calloc(1, sizeof(*newsrv))) == NULL) {
+			newsrv = new_server(curproxy);
+			if (!newsrv) {
 				Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
 				err_code |= ERR_ALERT | ERR_ABORT;
 				goto out;
@@ -1626,20 +1814,8 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			/* the servers are linked backwards first */
 			newsrv->next = curproxy->srv;
 			curproxy->srv = newsrv;
-			newsrv->proxy = curproxy;
 			newsrv->conf.file = strdup(file);
 			newsrv->conf.line = linenum;
-
-			newsrv->obj_type = OBJ_TYPE_SERVER;
-			LIST_INIT(&newsrv->actconns);
-			LIST_INIT(&newsrv->pendconns);
-			LIST_INIT(&newsrv->priv_conns);
-			LIST_INIT(&newsrv->idle_conns);
-			LIST_INIT(&newsrv->safe_conns);
-			newsrv->flags = 0;
-			newsrv->admin = 0;
-			newsrv->state = SRV_ST_RUNNING; /* early server setup */
-			newsrv->last_change = now.tv_sec;
 			newsrv->id = strdup(args[1]);
 
 			/* several ways to check the port component :
@@ -1707,7 +1883,6 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
  skip_name_resolution:
 			newsrv->addr = *sk;
 			newsrv->svc_port = port;
-			newsrv->xprt  = newsrv->check.xprt = newsrv->agent.xprt = xprt_get(XPRT_RAW);
 
 			if (!newsrv->hostname && !protocol_by_family(newsrv->addr.ss_family)) {
 				Alert("parsing [%s:%d] : Unknown protocol family %d '%s'\n",
@@ -1716,135 +1891,8 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 				goto out;
 			}
 
-			/*
-			 * In this section we copy default-server connection source settings to
-			 * the new server object connection source
-			 * (newsrv->conn_src <- curproxy->defsrv.conn_src).
-			 */
-			newsrv->conn_src.opts = curproxy->defsrv.conn_src.opts;
-			newsrv->conn_src.source_addr = curproxy->defsrv.conn_src.source_addr;
-			if (curproxy->defsrv.conn_src.sport_range != NULL) {
-				int i, def_sport_range_sz;
-				struct server *default_srv;
-
-				default_srv = &curproxy->defsrv;
-				def_sport_range_sz = default_srv->conn_src.sport_range->size;
-				if (def_sport_range_sz > 0) {
-					newsrv->conn_src.sport_range = port_range_alloc_range(def_sport_range_sz);
-					if (newsrv->conn_src.sport_range) {
-						for (i = 0; i < def_sport_range_sz; i++)
-							newsrv->conn_src.sport_range->ports[i] = default_srv->conn_src.sport_range->ports[i];
-					}
-				}
-			}
-#ifdef CONFIG_HAP_TRANSPARENT
-			if (curproxy->defsrv.conn_src.bind_hdr_name != NULL) {
-				newsrv->conn_src.bind_hdr_name = strdup(curproxy->defsrv.conn_src.bind_hdr_name);
-				newsrv->conn_src.bind_hdr_len = strlen(curproxy->defsrv.conn_src.bind_hdr_name);
-			}
-			newsrv->conn_src.bind_hdr_occ = curproxy->defsrv.conn_src.bind_hdr_occ;
-			newsrv->conn_src.tproxy_addr = curproxy->defsrv.conn_src.tproxy_addr;
-#endif
-			if (curproxy->defsrv.conn_src.iface_name != NULL)
-				newsrv->conn_src.iface_name = strdup(curproxy->defsrv.conn_src.iface_name);
-
-			newsrv->pp_opts		= curproxy->defsrv.pp_opts;
-			if (curproxy->defsrv.rdr_pfx != NULL) {
-				newsrv->rdr_pfx = strdup(curproxy->defsrv.rdr_pfx);
-				newsrv->rdr_len = curproxy->defsrv.rdr_len;
-			}
-			if (curproxy->defsrv.cookie != NULL) {
-				newsrv->cookie = strdup(curproxy->defsrv.cookie);
-				newsrv->cklen = curproxy->defsrv.cklen;
-			}
-			newsrv->use_ssl		= curproxy->defsrv.use_ssl;
-			newsrv->check.addr = newsrv->agent.addr = curproxy->defsrv.check.addr;
-			newsrv->check.use_ssl	= curproxy->defsrv.check.use_ssl;
-			newsrv->check.port	= curproxy->defsrv.check.port;
-			/* Note: 'flags' field has potentially been already initialized. */
-			newsrv->flags       |= curproxy->defsrv.flags;
-			newsrv->do_check    = curproxy->defsrv.do_check;
-			newsrv->do_agent    = curproxy->defsrv.do_agent;
-			if (newsrv->check.port)
-				newsrv->flags |= SRV_F_CHECKPORT;
-			newsrv->check.inter	= curproxy->defsrv.check.inter;
-			newsrv->check.fastinter	= curproxy->defsrv.check.fastinter;
-			newsrv->check.downinter	= curproxy->defsrv.check.downinter;
-			newsrv->agent.use_ssl	= curproxy->defsrv.agent.use_ssl;
-			newsrv->agent.port	= curproxy->defsrv.agent.port;
-			if (curproxy->defsrv.agent.send_string != NULL)
-				newsrv->agent.send_string = strdup(curproxy->defsrv.agent.send_string);
-			newsrv->agent.send_string_len = curproxy->defsrv.agent.send_string_len;
-			newsrv->agent.inter	= curproxy->defsrv.agent.inter;
-			newsrv->agent.fastinter	= curproxy->defsrv.agent.fastinter;
-			newsrv->agent.downinter	= curproxy->defsrv.agent.downinter;
-			newsrv->maxqueue	= curproxy->defsrv.maxqueue;
-			newsrv->minconn		= curproxy->defsrv.minconn;
-			newsrv->maxconn		= curproxy->defsrv.maxconn;
-			newsrv->slowstart	= curproxy->defsrv.slowstart;
-			newsrv->observe         = curproxy->defsrv.observe;
-			newsrv->onerror		= curproxy->defsrv.onerror;
-			newsrv->onmarkeddown    = curproxy->defsrv.onmarkeddown;
-			newsrv->onmarkedup      = curproxy->defsrv.onmarkedup;
-			if (curproxy->defsrv.trackit != NULL)
-				newsrv->trackit = strdup(curproxy->defsrv.trackit);
-			newsrv->consecutive_errors_limit
-						= curproxy->defsrv.consecutive_errors_limit;
-			newsrv->uweight = newsrv->iweight
-						= curproxy->defsrv.iweight;
-
-			newsrv->check.status	= HCHK_STATUS_INI;
-			newsrv->check.send_proxy = curproxy->defsrv.check.send_proxy;
-			newsrv->check.rise	= curproxy->defsrv.check.rise;
-			newsrv->check.fall	= curproxy->defsrv.check.fall;
-			newsrv->check.health	= newsrv->check.rise;	/* up, but will fall down at first failure */
-			/* Here we check if 'disabled' is the default server state */
-			if (curproxy->defsrv.admin & (SRV_ADMF_CMAINT | SRV_ADMF_FMAINT)) {
-				newsrv->admin |= SRV_ADMF_CMAINT | SRV_ADMF_FMAINT;
-				newsrv->state = SRV_ST_STOPPED;
-				newsrv->check.state |= CHK_ST_PAUSED;
-				newsrv->check.health = 0;
-			}
-			newsrv->check.server	= newsrv;
-			newsrv->check.tcpcheck_rules	= &curproxy->tcpcheck_rules;
-
-			newsrv->agent.status	= HCHK_STATUS_INI;
-			newsrv->agent.rise	= curproxy->defsrv.agent.rise;
-			newsrv->agent.fall	= curproxy->defsrv.agent.fall;
-			newsrv->agent.health	= newsrv->agent.rise;	/* up, but will fall down at first failure */
-			newsrv->agent.server	= newsrv;
-			if (curproxy->defsrv.resolvers_id != NULL)
-				newsrv->resolvers_id = strdup(curproxy->defsrv.resolvers_id);
-			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;
-			newsrv->init_addr_methods = curproxy->defsrv.init_addr_methods;
-			newsrv->init_addr         = curproxy->defsrv.init_addr;
-#if defined(USE_OPENSSL)
-			/* SSL config. */
-			if (curproxy->defsrv.ssl_ctx.ca_file != NULL)
-				newsrv->ssl_ctx.ca_file = strdup(curproxy->defsrv.ssl_ctx.ca_file);
-			if (curproxy->defsrv.ssl_ctx.crl_file != NULL)
-				newsrv->ssl_ctx.crl_file = strdup(curproxy->defsrv.ssl_ctx.crl_file);
-			if (curproxy->defsrv.ssl_ctx.client_crt != NULL)
-				newsrv->ssl_ctx.client_crt = strdup(curproxy->defsrv.ssl_ctx.client_crt);
-			newsrv->ssl_ctx.verify = curproxy->defsrv.ssl_ctx.verify;
-			if (curproxy->defsrv.ssl_ctx.verify_host != NULL)
-				newsrv->ssl_ctx.verify_host = strdup(curproxy->defsrv.ssl_ctx.verify_host);
-			if (curproxy->defsrv.ssl_ctx.ciphers != NULL)
-				newsrv->ssl_ctx.ciphers = strdup(curproxy->defsrv.ssl_ctx.ciphers);
-			if (curproxy->defsrv.sni_expr != NULL)
-				newsrv->sni_expr = strdup(curproxy->defsrv.sni_expr);
-#endif
-
-#ifdef TCP_USER_TIMEOUT
-			newsrv->tcp_ut = curproxy->defsrv.tcp_ut;
-#endif
-
+			/* Copy default server settings to new server settings. */
+			srv_settings_cpy(newsrv, &curproxy->defsrv);
 			cur_arg = 3;
 		} else {
 			newsrv = &curproxy->defsrv;
-- 
2.1.4

>From a33291931e26c88330cf8722ccb5e399b8284d7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= <[email protected]>
Date: Thu, 20 Apr 2017 12:17:50 +0200
Subject: [PATCH 1/6] BUG/MINOR: server: missing default server 'resolvers'
 setting duplication.

'resolvers' setting was not duplicated from default server setting to
new server instances when parsing 'server' lines.
This fix is simple: strdup() default resolvers <id> string argument after
having allocated a new server when parsing 'server' lines.

This patch must be backported to 1.7 and 1.6.
---
 src/server.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/server.c b/src/server.c
index 878293f..c9a19ed 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1813,6 +1813,8 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 			newsrv->agent.fall	= curproxy->defsrv.agent.fall;
 			newsrv->agent.health	= newsrv->agent.rise;	/* up, but will fall down at first failure */
 			newsrv->agent.server	= newsrv;
+			if (curproxy->defsrv.resolvers_id != NULL)
+				newsrv->resolvers_id = strdup(curproxy->defsrv.resolvers_id);
 			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;
@@ -1939,6 +1941,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 				cur_arg += 2;
 			}
 			else if (!strcmp(args[cur_arg], "resolvers")) {
+				free(newsrv->resolvers_id);
 				newsrv->resolvers_id = strdup(args[cur_arg + 1]);
 				cur_arg += 2;
 			}
-- 
2.1.4

Reply via email to