Hi.

I have seen https://github.com/haproxy/haproxy/issues/1542 which requests that feature.

Now I have tried to "port" the https://github.com/brentcetinich/haproxy/commit/bc258bff030677d855a6a84fec881398e8f1e082 to the current dev branch and attached the patch.

I'm pretty sure that there are some issues in the patch and I'm happy to make some rounds to fix the issues :-)

One question for me is, as I'm not that fit anymore in C and datatype, does this `0x10000` still fits into 32bit?

```from the Patch

+++ b/include/haproxy/server-t.h
@@ -154,6 +154,7 @@ enum srv_initaddr {
 #define SRV_F_NON_PURGEABLE 0x2000       /* this server cannot be removed at runtime */
 #define SRV_F_DEFSRV_USE_SSL 0x4000      /* default-server uses SSL */
 #define SRV_F_DELETED 0x8000             /* srv is deleted but not yet purged 
*/

+#define SRV_F_UPSTREAM_PROXY_TUNNEL 0x10000  /* this server uses a upstream proxy tunnel with CONNECT method */

```

Another Question raised to me is: Why are not "TRACE(...)" entries in src/connection.c only DPRINTF?

On that way a big thanks to brentcetinich for his great work for the initil work to that patch.

Regards

Alex

On 2024-05-23 (Do.) 22:32, Aleksandar Lazic wrote:
Hi.

I follow the development more or less closely and I must say I not always understand all changes :-).

Just for my clarification is the following setup now possible with HAProxy with all the new shiny features  :-)

client => frontend
              |
              \-> backend server dest1 IP:port
                    |
                    \-> call "CONNECT IP:PORT" on upstream proxy
                          |
                          \-> TCP FLOW to destination IP


I know there is the http://docs.haproxy.org/2.9/configuration.html#5.2-socks4 option but sadly not too much enterprise Proxies admins offers socks4 nowadays.

I think the Scenario is still not possible but I would like to have a second eye opinion on that.

Maybe somebody on the list have a working solution for the scenario and can share it, maybe only via direct mail. ¯\_(ツ)_/¯

Regards
Alex
From bf4e7c44ed939a2a9e119ca9b13b46efe9d43ab9 Mon Sep 17 00:00:00 2001
From: Aleksandar Lazic <al-hapr...@none.at>
Date: Thu, 23 May 2024 23:52:58 +0200
Subject: [PATCH] FEATURE/MAJOR: Add upstream-proxy-tunnel feature

This commit makes it possible for HAProxy to reach
target server via a upstream http proxy.

This patch is based on the work of @brentcetinich
and refer to gh #1542
---
 include/haproxy/connection-t.h |  14 +++-
 include/haproxy/connection.h   |   3 +
 include/haproxy/server-t.h     |   8 +-
 include/haproxy/tcpcheck-t.h   |   1 +
 src/backend.c                  |   5 ++
 src/connection.c               |  90 +++++++++++++++++++++
 src/proto_quic.c               |   4 +
 src/proto_tcp.c                |   2 +
 src/server.c                   | 138 ++++++++++++++++++++-------------
 src/sock.c                     |   3 +
 src/tcpcheck.c                 |   3 +
 src/xprt_handshake.c           |  11 +++
 12 files changed, 225 insertions(+), 57 deletions(-)

diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h
index 6ee0940be4..660c7bc7ba 100644
--- a/include/haproxy/connection-t.h
+++ b/include/haproxy/connection-t.h
@@ -132,8 +132,12 @@ enum {
 	CO_FL_ACCEPT_PROXY  = 0x02000000,  /* receive a valid PROXY protocol header */
 	CO_FL_ACCEPT_CIP    = 0x04000000,  /* receive a valid NetScaler Client IP header */
 
+	/*  STOLEN unused : 0x00000040, 0x00000080 */
+	CO_FL_UPSTREAM_PROXY_TUNNEL_SEND	= 0x00000040,  /* handshaking with upstream http proxy, going to send the handshake */
+	CO_FL_UPSTREAM_PROXY_TUNNEL_RECV = 0x00000080,  /* handshaking with upstream http proxy, going to check if handshake succeed */
+
 	/* below we have all handshake flags grouped into one */
-	CO_FL_HANDSHAKE     = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
+	CO_FL_HANDSHAKE     = CO_FL_SEND_PROXY | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV | CO_FL_UPSTREAM_PROXY_TUNNEL_SEND,
 	CO_FL_WAIT_XPRT     = CO_FL_WAIT_L4_CONN | CO_FL_HANDSHAKE | CO_FL_WAIT_L6_CONN,
 
 	CO_FL_SSL_WAIT_HS   = 0x08000000,  /* wait for an SSL handshake to complete */
@@ -155,6 +159,10 @@ enum {
 
 	/* below we have all SOCKS handshake flags grouped into one */
 	CO_FL_SOCKS4        = CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
+
+	/* below we have all upstream http proxy tunnel handshake flags grouped into one */
+	CO_FL_UPSTREAM_PROXY_TUNNEL        = CO_FL_UPSTREAM_PROXY_TUNNEL_SEND | CO_FL_UPSTREAM_PROXY_TUNNEL_RECV,
+
 };
 
 /* This function is used to report flags in debugging tools. Please reflect
@@ -241,6 +249,8 @@ enum {
 	CO_ERR_SSL_FATAL,        /* SSL fatal error during a SSL_read or SSL_write */
 
 	CO_ER_REVERSE,           /* Error during reverse connect */
+
+	CO_ER_PROXY_CONNECT_SEND, /* Upstream http proxy write error during handshake */
 };
 
 /* error return codes for accept_conn() */
@@ -526,7 +536,7 @@ struct connection {
 	/* first cache line */
 	enum obj_type obj_type;       /* differentiates connection from applet context */
 	unsigned char err_code;       /* CO_ER_* */
-	signed short send_proxy_ofs;  /* <0 = offset to (re)send from the end, >0 = send all (reused for SOCKS4) */
+	signed short send_proxy_ofs;  /* <0 = offset to (re)send from the end, >0 = send all (reused for SOCKS4 and upstream http proxy) */
 	unsigned int flags;           /* CO_FL_* */
 	const struct protocol *ctrl;  /* operations at the socket layer */
 	const struct xprt_ops *xprt;  /* operations at the transport layer */
diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
index 91ddcd6a53..3541000f38 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -74,6 +74,9 @@ int conn_ctrl_drain(struct connection *conn);
 int conn_send_socks4_proxy_request(struct connection *conn);
 int conn_recv_socks4_proxy_response(struct connection *conn);
 
+/* upstream http proxy handshake */
+int conn_send_upstream_proxy_tunnel_request(struct connection *conn);
+
 /* If we delayed the mux creation because we were waiting for the handshake, do it now */
 int conn_create_mux(struct connection *conn);
 int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake);
diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h
index d59ca40ef4..8b21e32229 100644
--- a/include/haproxy/server-t.h
+++ b/include/haproxy/server-t.h
@@ -154,6 +154,7 @@ enum srv_initaddr {
 #define SRV_F_NON_PURGEABLE 0x2000       /* this server cannot be removed at runtime */
 #define SRV_F_DEFSRV_USE_SSL 0x4000      /* default-server uses SSL */
 #define SRV_F_DELETED 0x8000             /* srv is deleted but not yet purged */
+#define SRV_F_UPSTREAM_PROXY_TUNNEL 0x10000  /* this server uses a upstream proxy tunnel with CONNECT method */
 
 /* configured server options for send-proxy (server->pp_opts) */
 #define SRV_PP_V1               0x0001   /* proxy protocol version 1 */
@@ -467,9 +468,10 @@ struct server {
 	struct guid_node guid;			/* GUID global tree node */
 
 	/* warning, these structs are huge, keep them at the bottom */
-	struct conn_src conn_src;               /* connection source settings */
-	struct sockaddr_storage addr;           /* the address to connect to, doesn't include the port */
-	struct sockaddr_storage socks4_addr;	/* the address of the SOCKS4 Proxy, including the port */
+	struct conn_src conn_src;                           /* connection source settings */
+	struct sockaddr_storage addr;                       /* the address to connect to, doesn't include the port */
+	struct sockaddr_storage socks4_addr;                /* the address of the SOCKS4 Proxy, including the port */
+	struct sockaddr_storage upstream_proxy_tunnel_addr; /* the address of the upstream PROXY for proxy tunnel, including the port */
 
 	EXTRA_COUNTERS(extra_counters);
 };
diff --git a/include/haproxy/tcpcheck-t.h b/include/haproxy/tcpcheck-t.h
index 22310eee05..ee861aaea0 100644
--- a/include/haproxy/tcpcheck-t.h
+++ b/include/haproxy/tcpcheck-t.h
@@ -36,6 +36,7 @@
 #define TCPCHK_OPT_IMPLICIT        0x0010  /* Implicit connect */
 #define TCPCHK_OPT_SOCKS4          0x0020  /* check the connection via socks4 proxy */
 #define TCPCHK_OPT_HAS_DATA        0x0040  /* data should be sent after connection */
+/* TODO: add upstream HTTP PROXY Check */
 
 enum tcpcheck_send_type {
 	TCPCHK_SEND_UNDEF = 0,  /* Send is not parsed. */
diff --git a/src/backend.c b/src/backend.c
index e5444988c2..69460583e1 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1725,6 +1725,11 @@ int connect_server(struct stream *s)
 			srv_conn->flags |= CO_FL_SOCKS4;
 		}
 
+		if (srv && (srv->flags & SRV_F_UPSTREAM_PROXY_TUNNEL)) {
+			srv_conn->send_proxy_ofs = 1; /* TODO: maybe dont reuse this? */
+			srv_conn->flags |= CO_FL_UPSTREAM_PROXY_TUNNEL;
+		}
+
 #if defined(USE_OPENSSL) && defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
 		/* if websocket stream, try to update connection ALPN. */
 		if (unlikely(s->flags & SF_WEBSOCKET) &&
diff --git a/src/connection.c b/src/connection.c
index 97af8f65e5..41c7594106 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -1783,6 +1783,96 @@ int conn_recv_socks4_proxy_response(struct connection *conn)
 	return 0;
 }
 
+/* TODO: use *TRACE* features from HAProxy instead of DPRINTF */
+int conn_send_proxy_tunnel_request(struct connection *conn) {
+
+	char addr[256];
+	int getaddr;
+	char proxy_connect[512]; /* fqdn + port (connect string) + headers */
+	int connect_length;
+
+	if (!conn_ctrl_ready(conn))
+		goto out_error;
+
+	if (!conn_get_dst(conn))
+		goto out_error;
+
+	getaddr = addr_to_str(conn->dst, addr, sizeof(addr));
+
+	/* 0  => the address family is not supported
+	 * -1 => is returned upon error
+	*/
+	if(getaddr == 0 || getaddr == -1 )
+		goto out_error;	
+
+	connect_length = snprintf(proxy_connect, sizeof(proxy_connect), "CONNECT %s:%u HTTP/1.1\r\n\r\n",
+							  addr,
+							  ntohs(get_net_port(conn->dst)));
+
+	if (conn->send_proxy_ofs > 0) {
+		/*
+		 * This is the first call to send the request
+		 */
+		conn->send_proxy_ofs = -(int) connect_length;
+	}
+
+	if (conn->send_proxy_ofs < 0) {
+		int ret = 0;
+
+		/* we are sending the htt-connect req_line here. If the data layer
+		 * has a pending write, we'll also set MSG_MORE.
+		 */
+		ret = conn_ctrl_send(
+				conn,
+				((char *) (proxy_connect)),
+				-conn->send_proxy_ofs,
+				(conn->subs && conn->subs->events & SUB_RETRY_SEND) ? CO_SFL_MSG_MORE : 0);
+
+		DPRINTF(stderr, "HTTP TUNNEL HS FD[%04X]: Before send remain is [%d], sent [%d]\n",
+				conn->handle.fd, -conn->send_proxy_ofs, ret);
+
+		if (ret < 0) {
+			goto out_error;
+		}
+
+		conn->send_proxy_ofs += ret; /* becomes zero once complete */
+		if (conn->send_proxy_ofs != 0) {
+			goto out_wait;
+		}
+	}
+
+	/* OK we've the whole request sent */
+	conn->flags &= ~CO_FL_UPSTREAM_PROXY_TUNNEL_SEND;
+
+
+	/* The connection is ready now, simply return and let the connection
+	 * handler notify upper layers if needed.
+	 */
+	conn->flags &= ~CO_FL_WAIT_L4_CONN;
+
+	/* This is for the proxy header so don't change */
+	if (conn->flags & CO_FL_SEND_PROXY) {
+		/*
+		 * Get the send_proxy_ofs ready for the send_proxy due to we are
+		 * reusing the "send_proxy_ofs", and SOCKS4 handshake should be done
+		 * before sending PROXY Protocol.
+		 */
+		conn->send_proxy_ofs = 1;
+	}
+	return 1;
+
+	out_error:
+	/* Write error on the file descriptor */
+	conn->flags |= CO_FL_ERROR;
+	if (conn->err_code == CO_ER_NONE) {
+		conn->err_code = CO_ER_PROXY_CONNECT_SEND;
+	}
+	return 0;
+
+	out_wait:
+	return 0;
+}
+
 /* registers proto mux list <list>. Modifies the list element! */
 void register_mux_proto(struct mux_proto_list *list)
 {
diff --git a/src/proto_quic.c b/src/proto_quic.c
index 93a24af4b4..cbf8c2c313 100644
--- a/src/proto_quic.c
+++ b/src/proto_quic.c
@@ -415,6 +415,10 @@ int quic_connect_server(struct connection *conn, int flags)
                 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
 
 	addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
+
+	/* TODO: Can an upstream proxy be used for quic? */
+	addr = (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL) ? &srv->upstream_proxy_tunnel_addr : conn->dst;
+
 	if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
 		if (errno == EINPROGRESS || errno == EALREADY) {
 			/* common case, let's wait for connect status */
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 63be775083..10bdb9b0df 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -453,6 +453,8 @@ int tcp_connect_server(struct connection *conn, int flags)
                 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
 
 	addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : conn->dst;
+	addr = (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL) ? &srv->upstream_proxy_tunnel_addr : conn->dst;
+
 	if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
 		if (errno == EINPROGRESS || errno == EALREADY) {
 			/* common case, let's wait for connect status */
diff --git a/src/server.c b/src/server.c
index 48984fe5c9..df8a2f8f9d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1915,6 +1915,38 @@ static int srv_parse_socks4(char **args, int *cur_arg,
 	return ERR_ALERT | ERR_FATAL;
 }
 
+/* Parse the "upstream-proxy-tunnel" server keyword */
+static int srv_parse_upstream_proxy_tunnel(char **args, int *cur_arg,
+								  struct proxy *curproxy, struct server *newsrv, char **err) {
+	char *errmsg;
+	int port_low, port_high;
+	struct sockaddr_storage *sk;
+
+	errmsg = NULL;
+
+	if (!*args[*cur_arg + 1]) {
+		memprintf(err, "'%s' expects <addr>:<port> as argument.\n", args[*cur_arg]);
+		goto err;
+	}
+
+	/* 'sk' is statically allocated (no need to be freed). */
+	sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, NULL, NULL, NULL,
+	                  &errmsg, NULL, NULL,
+	                  PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_CONNECT);
+	if (!sk) {
+		memprintf(err, "'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
+		goto err;
+	}
+
+	newsrv->flags |= SRV_F_UPSTREAM_PROXY_TUNNEL;
+	newsrv->upstream_proxy_tunnel_addr = *sk;
+
+	return 0;
+
+ err:
+	free(errmsg);
+	return ERR_ALERT | ERR_FATAL;
+}
 
 /* parse the "tfo" server keyword */
 static int srv_parse_tfo(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
@@ -2263,54 +2295,55 @@ void srv_compute_all_admin_states(struct proxy *px)
  * Note: -1 as ->skip value means that the number of arguments are variable.
  */
 static struct srv_kw_list srv_kws = { "ALL", { }, {
-	{ "backup",               srv_parse_backup,               0,  1,  1 }, /* Flag as backup server */
-	{ "cookie",               srv_parse_cookie,               1,  1,  1 }, /* Assign a cookie to the server */
-	{ "disabled",             srv_parse_disabled,             0,  1,  1 }, /* Start the server in 'disabled' state */
-	{ "enabled",              srv_parse_enabled,              0,  1,  0 }, /* Start the server in 'enabled' state */
-	{ "error-limit",          srv_parse_error_limit,          1,  1,  1 }, /* Configure the consecutive count of check failures to consider a server on error */
-	{ "guid",                 srv_parse_guid,                 1,  0,  1 }, /* Set global unique ID of the server */
-	{ "ws",                   srv_parse_ws,                   1,  1,  1 }, /* websocket protocol */
-	{ "hash-key",             srv_parse_hash_key,             1,  1,  1 }, /* Configure how chash keys are computed */
-	{ "id",                   srv_parse_id,                   1,  0,  1 }, /* set id# of server */
-	{ "init-addr",            srv_parse_init_addr,            1,  1,  0 }, /* */
-	{ "log-bufsize",          srv_parse_log_bufsize,          1,  1,  0 }, /* Set the ring bufsize for log server (only for log backends) */
-	{ "log-proto",            srv_parse_log_proto,            1,  1,  0 }, /* Set the protocol for event messages, only relevant in a log or ring section */
-	{ "maxconn",              srv_parse_maxconn,              1,  1,  1 }, /* Set the max number of concurrent connection */
-	{ "maxqueue",             srv_parse_maxqueue,             1,  1,  1 }, /* Set the max number of connection to put in queue */
-	{ "max-reuse",            srv_parse_max_reuse,            1,  1,  0 }, /* Set the max number of requests on a connection, -1 means unlimited */
-	{ "minconn",              srv_parse_minconn,              1,  1,  1 }, /* Enable a dynamic maxconn limit */
-	{ "namespace",            srv_parse_namespace,            1,  1,  0 }, /* Namespace the server socket belongs to (if supported) */
-	{ "no-backup",            srv_parse_no_backup,            0,  1,  1 }, /* Flag as non-backup server */
-	{ "no-send-proxy",        srv_parse_no_send_proxy,        0,  1,  1 }, /* Disable use of PROXY V1 protocol */
-	{ "no-send-proxy-v2",     srv_parse_no_send_proxy_v2,     0,  1,  1 }, /* Disable use of PROXY V2 protocol */
-	{ "no-tfo",               srv_parse_no_tfo,               0,  1,  1 }, /* Disable use of TCP Fast Open */
-	{ "non-stick",            srv_parse_non_stick,            0,  1,  0 }, /* Disable stick-table persistence */
-	{ "observe",              srv_parse_observe,              1,  1,  1 }, /* Enables health adjusting based on observing communication with the server */
-	{ "on-error",             srv_parse_on_error,             1,  1,  1 }, /* Configure the action on check failure */
-	{ "on-marked-down",       srv_parse_on_marked_down,       1,  1,  1 }, /* Configure the action when a server is marked down */
-	{ "on-marked-up",         srv_parse_on_marked_up,         1,  1,  1 }, /* Configure the action when a server is marked up */
-	{ "pool-low-conn",        srv_parse_pool_low_conn,        1,  1,  1 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
-	{ "pool-max-conn",        srv_parse_pool_max_conn,        1,  1,  1 }, /* Set the max number of orphan idle connections, -1 means unlimited */
-	{ "pool-purge-delay",     srv_parse_pool_purge_delay,     1,  1,  1 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
-	{ "proto",                srv_parse_proto,                1,  1,  1 }, /* Set the proto to use for all outgoing connections */
-	{ "proxy-v2-options",     srv_parse_proxy_v2_options,     1,  1,  1 }, /* options for send-proxy-v2 */
-	{ "redir",                srv_parse_redir,                1,  1,  0 }, /* Enable redirection mode */
-	{ "resolve-net",          srv_parse_resolve_net,          1,  1,  0 }, /* Set the preferred network range for name resolution */
-	{ "resolve-opts",         srv_parse_resolve_opts,         1,  1,  0 }, /* Set options for name resolution */
-	{ "resolve-prefer",       srv_parse_resolve_prefer,       1,  1,  0 }, /* Set the preferred family for name resolution */
-	{ "resolvers",            srv_parse_resolvers,            1,  1,  0 }, /* Configure the resolver to use for name resolution */
-	{ "send-proxy",           srv_parse_send_proxy,           0,  1,  1 }, /* Enforce use of PROXY V1 protocol */
-	{ "send-proxy-v2",        srv_parse_send_proxy_v2,        0,  1,  1 }, /* Enforce use of PROXY V2 protocol */
-	{ "set-proxy-v2-tlv-fmt", srv_parse_set_proxy_v2_tlv_fmt, 0,  1,  1 }, /* Set TLV of PROXY V2 protocol */
-	{ "shard",                srv_parse_shard,                1,  1,  1 }, /* Server shard (only in peers protocol context) */
-	{ "slowstart",            srv_parse_slowstart,            1,  1,  1 }, /* Set the warm-up timer for a previously failed server */
-	{ "source",               srv_parse_source,              -1,  1,  1 }, /* Set the source address to be used to connect to the server */
-	{ "stick",                srv_parse_stick,                0,  1,  0 }, /* Enable stick-table persistence */
-	{ "tfo",                  srv_parse_tfo,                  0,  1,  1 }, /* enable TCP Fast Open of server */
-	{ "track",                srv_parse_track,                1,  1,  1 }, /* Set the current state of the server, tracking another one */
-	{ "socks4",               srv_parse_socks4,               1,  1,  0 }, /* Set the socks4 proxy of the server*/
-	{ "usesrc",               srv_parse_usesrc,               0,  1,  1 }, /* safe-guard against usesrc without preceding <source> keyword */
-	{ "weight",               srv_parse_weight,               1,  1,  1 }, /* Set the load-balancing weight */
+	{ "backup",                srv_parse_backup,                0,  1,  1 }, /* Flag as backup server */
+	{ "cookie",                srv_parse_cookie,                1,  1,  1 }, /* Assign a cookie to the server */
+	{ "disabled",              srv_parse_disabled,              0,  1,  1 }, /* Start the server in 'disabled' state */
+	{ "enabled",               srv_parse_enabled,               0,  1,  0 }, /* Start the server in 'enabled' state */
+	{ "error-limit",           srv_parse_error_limit,           1,  1,  1 }, /* Configure the consecutive count of check failures to consider a server on error */
+	{ "guid",                  srv_parse_guid,                  1,  0,  1 }, /* Set global unique ID of the server */
+	{ "ws",                    srv_parse_ws,                    1,  1,  1 }, /* websocket protocol */
+	{ "hash-key",              srv_parse_hash_key,              1,  1,  1 }, /* Configure how chash keys are computed */
+	{ "id",                    srv_parse_id,                    1,  0,  1 }, /* set id# of server */
+	{ "init-addr",             srv_parse_init_addr,             1,  1,  0 }, /* */
+	{ "log-bufsize",           srv_parse_log_bufsize,           1,  1,  0 }, /* Set the ring bufsize for log server (only for log backends) */
+	{ "log-proto",             srv_parse_log_proto,             1,  1,  0 }, /* Set the protocol for event messages, only relevant in a log or ring section */
+	{ "maxconn",               srv_parse_maxconn,               1,  1,  1 }, /* Set the max number of concurrent connection */
+	{ "maxqueue",              srv_parse_maxqueue,              1,  1,  1 }, /* Set the max number of connection to put in queue */
+	{ "max-reuse",             srv_parse_max_reuse,             1,  1,  0 }, /* Set the max number of requests on a connection, -1 means unlimited */
+	{ "minconn",               srv_parse_minconn,               1,  1,  1 }, /* Enable a dynamic maxconn limit */
+	{ "namespace",             srv_parse_namespace,             1,  1,  0 }, /* Namespace the server socket belongs to (if supported) */
+	{ "no-backup",             srv_parse_no_backup,             0,  1,  1 }, /* Flag as non-backup server */
+	{ "no-send-proxy",         srv_parse_no_send_proxy,         0,  1,  1 }, /* Disable use of PROXY V1 protocol */
+	{ "no-send-proxy-v2",      srv_parse_no_send_proxy_v2,      0,  1,  1 }, /* Disable use of PROXY V2 protocol */
+	{ "no-tfo",                srv_parse_no_tfo,                0,  1,  1 }, /* Disable use of TCP Fast Open */
+	{ "non-stick",             srv_parse_non_stick,             0,  1,  0 }, /* Disable stick-table persistence */
+	{ "observe",               srv_parse_observe,               1,  1,  1 }, /* Enables health adjusting based on observing communication with the server */
+	{ "on-error",              srv_parse_on_error,              1,  1,  1 }, /* Configure the action on check failure */
+	{ "on-marked-down",        srv_parse_on_marked_down,        1,  1,  1 }, /* Configure the action when a server is marked down */
+	{ "on-marked-up",          srv_parse_on_marked_up,          1,  1,  1 }, /* Configure the action when a server is marked up */
+	{ "pool-low-conn",         srv_parse_pool_low_conn,         1,  1,  1 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
+	{ "pool-max-conn",         srv_parse_pool_max_conn,         1,  1,  1 }, /* Set the max number of orphan idle connections, -1 means unlimited */
+	{ "pool-purge-delay",      srv_parse_pool_purge_delay,      1,  1,  1 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
+	{ "proto",                 srv_parse_proto,                 1,  1,  1 }, /* Set the proto to use for all outgoing connections */
+	{ "proxy-v2-options",      srv_parse_proxy_v2_options,      1,  1,  1 }, /* options for send-proxy-v2 */
+	{ "redir",                 srv_parse_redir,                 1,  1,  0 }, /* Enable redirection mode */
+	{ "resolve-net",           srv_parse_resolve_net,           1,  1,  0 }, /* Set the preferred network range for name resolution */
+	{ "resolve-opts",          srv_parse_resolve_opts,          1,  1,  0 }, /* Set options for name resolution */
+	{ "resolve-prefer",        srv_parse_resolve_prefer,        1,  1,  0 }, /* Set the preferred family for name resolution */
+	{ "resolvers",             srv_parse_resolvers,             1,  1,  0 }, /* Configure the resolver to use for name resolution */
+	{ "send-proxy",            srv_parse_send_proxy,            0,  1,  1 }, /* Enforce use of PROXY V1 protocol */
+	{ "send-proxy-v2",         srv_parse_send_proxy_v2,         0,  1,  1 }, /* Enforce use of PROXY V2 protocol */
+	{ "set-proxy-v2-tlv-fmt",  srv_parse_set_proxy_v2_tlv_fmt,  0,  1,  1 }, /* Set TLV of PROXY V2 protocol */
+	{ "shard",                 srv_parse_shard,                 1,  1,  1 }, /* Server shard (only in peers protocol context) */
+	{ "slowstart",             srv_parse_slowstart,             1,  1,  1 }, /* Set the warm-up timer for a previously failed server */
+	{ "source",                srv_parse_source,               -1,  1,  1 }, /* Set the source address to be used to connect to the server */
+	{ "stick",                 srv_parse_stick,                 0,  1,  0 }, /* Enable stick-table persistence */
+	{ "tfo",                   srv_parse_tfo,                   0,  1,  1 }, /* enable TCP Fast Open of server */
+	{ "track",                 srv_parse_track,                 1,  1,  1 }, /* Set the current state of the server, tracking another one */
+	{ "upstream-proxy-tunnel", srv_parse_upstream_proxy_tunnel, 1,  1,  0 }, /* Set the upstream proxy tunnel backend of the server*/
+	{ "socks4",                srv_parse_socks4,                1,  1,  0 }, /* Set the socks4 proxy of the server*/
+	{ "usesrc",                srv_parse_usesrc,                0,  1,  1 }, /* safe-guard against usesrc without preceding <source> keyword */
+	{ "weight",                srv_parse_weight,                1,  1,  1 }, /* Set the load-balancing weight */
 	{ NULL, NULL, 0 },
 }};
 
@@ -2813,10 +2846,11 @@ void srv_settings_cpy(struct server *srv, const struct server *src, int srv_tmpl
 	if (srv_tmpl)
 		srv->srvrq = src->srvrq;
 
-	srv->netns                    = src->netns;
-	srv->check.via_socks4         = src->check.via_socks4;
-	srv->socks4_addr              = src->socks4_addr;
-	srv->log_bufsize              = src->log_bufsize;
+	srv->netns                      = src->netns;
+	srv->check.via_socks4           = src->check.via_socks4;
+	srv->socks4_addr                = src->socks4_addr;
+	srv->log_bufsize                = src->log_bufsize;
+	srv->upstream_proxy_tunnel_addr = src->upstream_proxy_tunnel_addr;
 
 	LIST_INIT(&srv->pp_tlvs);
 
diff --git a/src/sock.c b/src/sock.c
index fba92bd7cf..8955af24dd 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -893,6 +893,9 @@ int sock_conn_check(struct connection *conn)
 	if ((conn->flags & CO_FL_SOCKS4) && obj_type(conn->target) == OBJ_TYPE_SERVER)
 		addr = &objt_server(conn->target)->socks4_addr;
 
+	if ((conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL) && obj_type(conn->target) == OBJ_TYPE_SERVER)
+		addr = &objt_server(conn->target)->upstream_proxy_tunnel_addr;
+
 	if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
 		if (errno == EALREADY || errno == EINPROGRESS)
 			goto wait;
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index b4f9590866..c98407baaa 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -1164,6 +1164,9 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
 		conn->flags |= CO_FL_SOCKS4;
 		TRACE_DEVEL("configure SOCKS4 proxy", CHK_EV_TCPCHK_CONN);
 	}
+
+	/* TODO: Add check via upstream http proxy */
+
 	else if ((connect->options & TCPCHK_OPT_DEFAULT_CONNECT) && s && s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
 		conn->send_proxy_ofs = 1;
 		conn->flags |= CO_FL_SOCKS4;
diff --git a/src/xprt_handshake.c b/src/xprt_handshake.c
index 33f775087a..8e14962bdb 100644
--- a/src/xprt_handshake.c
+++ b/src/xprt_handshake.c
@@ -56,6 +56,17 @@ struct task *xprt_handshake_io_cb(struct task *t, void *bctx, unsigned int state
 			goto out;
 		}
 
+	/* TODO: check flags to send optional headers like keep alive host etc */
+	if (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL) {
+		if (!conn_send_proxy_tunnel_request(conn)) {
+			ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_SEND,
+								 &ctx->wait_event);
+
+			goto out;
+		} else {
+		}
+	}
+
 	if (conn->flags & CO_FL_ACCEPT_CIP)
 		if (!conn_recv_netscaler_cip(conn, CO_FL_ACCEPT_CIP)) {
 			ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
-- 
2.34.1

Reply via email to