MEDIUM: http: Add new 'set-src' option to http-request

This option enables overriding source IP address in a HTTP request. It is
useful when we want to set custom source IP (e.g. front proxy rewrites address,
but provides the correct one in headers) or we wan't to mask source IP address
for privacy or compliance.

It acts on any expression which produces correct IP address.
diff --git a/src/proto_http.c b/src/proto_http.c
index c244792..0ebc2c4 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3674,6 +3674,27 @@
 						stkctr_set_flags(&s->stkctr[http_req_trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
 				}
 			}
+			break;
+
+		case HTTP_REQ_ACT_SET_SRC:
+			if ((cli_conn = objt_conn(sess->origin)) && conn_ctrl_ready(cli_conn)) {
+				struct sample *smp;
+
+				smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.act.p[0], SMP_T_ADDR);
+
+				if (smp) {
+					if (smp->type == SMP_T_IPV4) {
+						((struct sockaddr_in *)&cli_conn->addr.from)->sin_family = AF_INET;
+						((struct sockaddr_in *)&cli_conn->addr.from)->sin_addr.s_addr = smp->data.ipv4.s_addr;
+						((struct sockaddr_in *)&cli_conn->addr.from)->sin_port = 0;
+					} else if (smp->type == SMP_T_IPV6) {
+						((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_family = AF_INET6;
+						memcpy(&((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_addr, &smp->data.ipv6, sizeof(struct in6_addr));
+						((struct sockaddr_in6 *)&cli_conn->addr.from)->sin6_port = 0;
+					}
+				}
+			}
+			break;
 		}
 	}
 
@@ -9528,6 +9549,39 @@
 		proxy->conf.lfs_line = proxy->conf.args.line;
 
 		cur_arg += 2;
+	} else if (strncmp(args[0], "set-src", 7) == 0) {
+		struct sample_expr *expr;
+		unsigned int where;
+		char *err = NULL;
+
+		cur_arg = 1;
+		proxy->conf.args.ctx = ARGC_HRQ;
+
+		expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
+		if (!expr) {
+			Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
+			      file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
+			free(err);
+			goto out_err;
+		}
+
+		where = 0;
+		if (proxy->cap & PR_CAP_FE)
+			where |= SMP_VAL_FE_HRQ_HDR;
+		if (proxy->cap & PR_CAP_BE)
+			where |= SMP_VAL_BE_HRQ_HDR;
+
+		if (!(expr->fetch->val & where)) {
+			Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule :"
+			      " fetch method '%s' extracts information from '%s', none of which is available here.\n",
+			      file, linenum, proxy_type_str(proxy), proxy->id, args[0],
+			      args[cur_arg-1], sample_src_names(expr->fetch->use));
+			free(expr);
+			goto out_err;
+		}
+
+		rule->arg.act.p[0] = expr;
+		rule->action = HTTP_REQ_ACT_SET_SRC;
 	} else if (((custom = action_http_req_custom(args[0])) != NULL)) {
 		char *errmsg = NULL;
 		cur_arg = 1;
@@ -9539,7 +9593,7 @@
 			goto out_err;
 		}
 	} else {
-		Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'set-var', but got '%s'%s.\n",
+		Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'set-var', 'set-src', but got '%s'%s.\n",
 		      file, linenum, args[0], *args[0] ? "" : " (missing argument)");
 		goto out_err;
 	}