[MINOR] Allow to specify a domain for a cookie

This patch allows to specify a domain used when inserting a cookie
providing a session stickiness. Usefull for example with wildcard domains.

The patch adds one new variable to the struct proxy: cookiedomain.
When set the domain is appended to a Set-Cookie header.

Domain name is validated using the new invalid_domainchar() function.
It is basically invalid_char() limited to [A-Za-z0-9_.-]. Yes, the test
is too trivial and does not cover all wrong situations, but the main
purpose is to detect most common mistakes, not intentional abuses.

The underscore ("_") character is not RFC-valid but as it is
often (mis)used so I decided to allow it.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 7dbadd1..9ca8ae6 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -895,8 +895,33 @@
 			else if (!strcmp(args[cur_arg], "prefix")) {
 				curproxy->options |= PR_O_COOK_PFX;
 			}
+			else if (!strcmp(args[cur_arg], "domain")) {
+				if (!*args[cur_arg + 1]) {
+					Alert("parsing [%s:%d]: '%s' expects <domain> as argument.\n",
+						file, linenum, args[cur_arg]);
+					return -1;
+				}
+
+				if (*args[cur_arg + 1] != '.' || !strchr(args[cur_arg + 1] + 1, '.')) {
+					/* rfc2109, 4.3.2 Rejecting Cookies */
+					Alert("parsing [%s:%d]: domain '%s' contains no embedded"
+						" dots or does not start with a dot.\n",
+						file, linenum, args[cur_arg + 1]);
+					return -1;
+				}
+
+				err = invalid_domainchar(args[cur_arg + 1]);
+				if (err) {
+					Alert("parsing [%s:%d]: character '%c' is not permitted in domain name '%s'.\n",
+						file, linenum, *err, args[cur_arg + 1]);
+					return -1;
+				}
+
+				curproxy->cookiedomain = strdup(args[cur_arg + 1]);
+				cur_arg++;
+			}
 			else {
-				Alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'prefix', 'indirect', 'nocache' and 'postonly' options.\n",
+				Alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'prefix', 'indirect', 'nocache' and 'postonly', 'domain' options.\n",
 				      file, linenum, args[0]);
 				return -1;
 			}
diff --git a/src/proto_http.c b/src/proto_http.c
index 2c07030..3caeba1 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3222,6 +3222,9 @@
 				      t->be->cookie_name,
 				      t->srv->cookie ? t->srv->cookie : "; Expires=Thu, 01-Jan-1970 00:00:01 GMT");
 
+			if (t->be->cookiedomain)
+				len += sprintf(trash+len, "; domain=%s", t->be->cookiedomain);
+
 			if (unlikely(http_header_add_tail2(rep, &txn->rsp, &txn->hdr_idx,
 							   trash, len)) < 0)
 				goto return_bad_resp;
diff --git a/src/standard.c b/src/standard.c
index 7f749f9..a855528 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -141,6 +141,27 @@
 }
 
 /*
+ * Checks <domainname> 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) {
+
+	if (!*name)
+		return name;
+
+	while (*name) {
+		if (!isalnum((int)*name) && *name != '.' &&
+		    *name != '_' && *name != '-')
+			return name;
+
+		name++;
+	}
+
+	return NULL;
+}
+
+/*
  * converts <str> to a struct sockaddr_in* which is locally allocated.
  * The format is "addr:port", where "addr" can be a dotted IPv4 address,
  * a host name, or empty or "*" to indicate INADDR_ANY.