MEDIUM: http-ana: Add IPv6 support for forwardfor and orignialto options

A network may be specified to avoid header addition for "forwardfor" and
"orignialto" option via the "except" parameter. However, only IPv4
networks/addresses are supported. This patch adds the support of IPv6.

To do so, the net_addr structure is used to store the parameter value in the
proxy structure. And ipcmp2net() function is used to perform the comparison.

This patch should fix the issue #1145. It depends on the following commit:

  * c6ce0ab MINOR: tools: Add function to compare an address to a network address
  * 5587287 MINOR: tools: Add net_addr structure describing a network addess
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index bb57be9..34816c5 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -2107,20 +2107,35 @@
 			free(curproxy->fwdfor_hdr_name);
 			curproxy->fwdfor_hdr_name = strdup(DEF_XFORWARDFOR_HDR);
 			curproxy->fwdfor_hdr_len  = strlen(DEF_XFORWARDFOR_HDR);
+			curproxy->except_xff_net.family = AF_UNSPEC;
 
 			/* loop to go through arguments - start at 2, since 0+1 = "option" "forwardfor" */
 			cur_arg = 2;
 			while (*(args[cur_arg])) {
 				if (strcmp(args[cur_arg], "except") == 0) {
+					unsigned char mask;
+					int i;
+
 					/* suboption except - needs additional argument for it */
-					if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], 1, &curproxy->except_net, &curproxy->except_mask)) {
+					if (*(args[cur_arg+1]) &&
+					    str2net(args[cur_arg+1], 1, &curproxy->except_xff_net.addr.v4.ip, &curproxy->except_xff_net.addr.v4.mask)) {
+						curproxy->except_xff_net.family = AF_INET;
+						curproxy->except_xff_net.addr.v4.ip.s_addr &= curproxy->except_xff_net.addr.v4.mask.s_addr;
+					}
+					else if (*(args[cur_arg+1]) &&
+						 str62net(args[cur_arg+1], &curproxy->except_xff_net.addr.v6.ip, &mask)) {
+						curproxy->except_xff_net.family = AF_INET6;
+						len2mask6(mask, &curproxy->except_xff_net.addr.v6.mask);
+						for (i = 0; i < 16; i++)
+							curproxy->except_xff_net.addr.v6.ip.s6_addr[i] &= curproxy->except_xff_net.addr.v6.mask.s6_addr[i];
+					}
+					else {
 						ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
 							 file, linenum, args[0], args[1], args[cur_arg]);
 						err_code |= ERR_ALERT | ERR_FATAL;
 						goto out;
 					}
 					/* flush useless bits */
-					curproxy->except_net.s_addr &= curproxy->except_mask.s_addr;
 					cur_arg += 2;
 				} else if (strcmp(args[cur_arg], "header") == 0) {
 					/* suboption header - needs additional argument for it */
@@ -2158,20 +2173,34 @@
 			free(curproxy->orgto_hdr_name);
 			curproxy->orgto_hdr_name = strdup(DEF_XORIGINALTO_HDR);
 			curproxy->orgto_hdr_len  = strlen(DEF_XORIGINALTO_HDR);
+			curproxy->except_xot_net.family = AF_UNSPEC;
 
 			/* loop to go through arguments - start at 2, since 0+1 = "option" "originalto" */
 			cur_arg = 2;
 			while (*(args[cur_arg])) {
 				if (strcmp(args[cur_arg], "except") == 0) {
+					unsigned char mask;
+					int i;
+
 					/* suboption except - needs additional argument for it */
-					if (!*(args[cur_arg+1]) || !str2net(args[cur_arg+1], 1, &curproxy->except_to, &curproxy->except_mask_to)) {
+					if (*(args[cur_arg+1]) &&
+					    str2net(args[cur_arg+1], 1, &curproxy->except_xot_net.addr.v4.ip, &curproxy->except_xot_net.addr.v4.mask)) {
+						curproxy->except_xot_net.family = AF_INET;
+						curproxy->except_xot_net.addr.v4.ip.s_addr &= curproxy->except_xot_net.addr.v4.mask.s_addr;
+					}
+					else if (*(args[cur_arg+1]) &&
+						 str62net(args[cur_arg+1], &curproxy->except_xot_net.addr.v6.ip, &mask)) {
+						curproxy->except_xot_net.family = AF_INET6;
+						len2mask6(mask, &curproxy->except_xot_net.addr.v6.mask);
+						for (i = 0; i < 16; i++)
+							curproxy->except_xot_net.addr.v6.ip.s6_addr[i] &= curproxy->except_xot_net.addr.v6.mask.s6_addr[i];
+					}
+					else {
 						ha_alert("parsing [%s:%d] : '%s %s %s' expects <address>[/mask] as argument.\n",
 							 file, linenum, args[0], args[1], args[cur_arg]);
 						err_code |= ERR_ALERT | ERR_FATAL;
 						goto out;
 					}
-					/* flush useless bits */
-					curproxy->except_to.s_addr &= curproxy->except_mask_to.s_addr;
 					cur_arg += 2;
 				} else if (strcmp(args[cur_arg], "header") == 0) {
 					/* suboption header - needs additional argument for it */
diff --git a/src/http_ana.c b/src/http_ana.c
index d18b4cc..5536c04 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -673,12 +673,8 @@
 			/* Add an X-Forwarded-For header unless the source IP is
 			 * in the 'except' network range.
 			 */
-			if ((!sess->fe->except_mask.s_addr ||
-			     (((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr & sess->fe->except_mask.s_addr)
-			     != sess->fe->except_net.s_addr) &&
-			    (!s->be->except_mask.s_addr ||
-			     (((struct sockaddr_in *)cli_conn->src)->sin_addr.s_addr & s->be->except_mask.s_addr)
-			     != s->be->except_net.s_addr)) {
+			if (ipcmp2net(cli_conn->src, &sess->fe->except_xff_net) &&
+			    ipcmp2net(cli_conn->src, &s->be->except_xff_net)) {
 				unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)cli_conn->src)->sin_addr;
 
 				/* Note: we rely on the backend to get the header name to be used for
@@ -692,23 +688,26 @@
 			}
 		}
 		else if (cli_conn && conn_get_src(cli_conn) && cli_conn->src->ss_family == AF_INET6) {
-			/* FIXME: for the sake of completeness, we should also support
-			 * 'except' here, although it is mostly useless in this case.
+			/* Add an X-Forwarded-For header unless the source IP is
+			 * in the 'except' network range.
 			 */
-			char pn[INET6_ADDRSTRLEN];
+			if (ipcmp2net(cli_conn->src, &sess->fe->except_xff_net) &&
+			    ipcmp2net(cli_conn->src, &s->be->except_xff_net)) {
+				char pn[INET6_ADDRSTRLEN];
 
-			inet_ntop(AF_INET6,
-				  (const void *)&((struct sockaddr_in6 *)(cli_conn->src))->sin6_addr,
-				  pn, sizeof(pn));
+				inet_ntop(AF_INET6,
+					  (const void *)&((struct sockaddr_in6 *)(cli_conn->src))->sin6_addr,
+					  pn, sizeof(pn));
 
-			/* Note: we rely on the backend to get the header name to be used for
-			 * x-forwarded-for, because the header is really meant for the backends.
-			 * However, if the backend did not specify any option, we have to rely
-			 * on the frontend's header name.
-			 */
-			chunk_printf(&trash, "%s", pn);
-			if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
-				goto return_int_err;
+				/* Note: we rely on the backend to get the header name to be used for
+				 * x-forwarded-for, because the header is really meant for the backends.
+				 * However, if the backend did not specify any option, we have to rely
+				 * on the frontend's header name.
+				 */
+				chunk_printf(&trash, "%s", pn);
+				if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
+					goto return_int_err;
+			}
 		}
 	}
 
@@ -717,20 +716,15 @@
 	 * asks for it.
 	 */
 	if ((sess->fe->options | s->be->options) & PR_O_ORGTO) {
+		struct ist hdr = ist2(s->be->orgto_hdr_len ? s->be->orgto_hdr_name : sess->fe->orgto_hdr_name,
+				      s->be->orgto_hdr_len ? s->be->orgto_hdr_len  : sess->fe->orgto_hdr_len);
 
-		/* FIXME: don't know if IPv6 can handle that case too. */
 		if (cli_conn && conn_get_dst(cli_conn) && cli_conn->dst->ss_family == AF_INET) {
 			/* Add an X-Original-To header unless the destination IP is
 			 * in the 'except' network range.
 			 */
-			if (cli_conn->dst->ss_family == AF_INET &&
-			    ((!sess->fe->except_mask_to.s_addr ||
-			      (((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr & sess->fe->except_mask_to.s_addr)
-			      != sess->fe->except_to.s_addr) &&
-			     (!s->be->except_mask_to.s_addr ||
-			      (((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr & s->be->except_mask_to.s_addr)
-			      != s->be->except_to.s_addr))) {
-				struct ist hdr;
+			if (ipcmp2net(cli_conn->dst, &sess->fe->except_xot_net) &&
+			    ipcmp2net(cli_conn->dst, &s->be->except_xot_net)) {
 				unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)cli_conn->dst)->sin_addr;
 
 				/* Note: we rely on the backend to get the header name to be used for
@@ -738,16 +732,33 @@
 				 * However, if the backend did not specify any option, we have to rely
 				 * on the frontend's header name.
 				 */
-				if (s->be->orgto_hdr_len)
-					hdr = ist2(s->be->orgto_hdr_name, s->be->orgto_hdr_len);
-				else
-					hdr = ist2(sess->fe->orgto_hdr_name, sess->fe->orgto_hdr_len);
-
 				chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
 				if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
 					goto return_int_err;
 			}
 		}
+		else if (cli_conn && conn_get_dst(cli_conn) && cli_conn->dst->ss_family == AF_INET6) {
+			/* Add an X-Original-To header unless the source IP is
+			 * in the 'except' network range.
+			 */
+			if (ipcmp2net(cli_conn->dst, &sess->fe->except_xot_net) &&
+			    ipcmp2net(cli_conn->dst, &s->be->except_xot_net)) {
+				char pn[INET6_ADDRSTRLEN];
+
+				inet_ntop(AF_INET6,
+					  (const void *)&((struct sockaddr_in6 *)(cli_conn->dst))->sin6_addr,
+					  pn, sizeof(pn));
+
+				/* Note: we rely on the backend to get the header name to be used for
+				 * x-forwarded-for, because the header is really meant for the backends.
+				 * However, if the backend did not specify any option, we have to rely
+				 * on the frontend's header name.
+				 */
+				chunk_printf(&trash, "%s", pn);
+				if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
+					goto return_int_err;
+			}
+		}
 	}
 
 	/* If we have no server assigned yet and we're balancing on url_param
diff --git a/src/proxy.c b/src/proxy.c
index 5edbbfe..abca609 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1229,10 +1229,8 @@
 	curproxy->no_options = defproxy->no_options;
 	curproxy->no_options2 = defproxy->no_options2;
 	curproxy->bind_proc = defproxy->bind_proc;
-	curproxy->except_net = defproxy->except_net;
-	curproxy->except_mask = defproxy->except_mask;
-	curproxy->except_to = defproxy->except_to;
-	curproxy->except_mask_to = defproxy->except_mask_to;
+	curproxy->except_xff_net = defproxy->except_xff_net;
+	curproxy->except_xot_net = defproxy->except_xot_net;
 	curproxy->retry_type = defproxy->retry_type;
 
 	if (defproxy->fwdfor_hdr_len) {