Hi.
Attached a new version with updated upstream-proxy.cfg.
This Patch have also the feature `upstream-proxy-target` to get rid of the
dependency for the srv->hostname.
```
tcp-request content upstream-proxy-target www.test1.com
```
Now have I tested the setup with `0.0.0.0` as server.
```
server https_Via_Proxy1 0.0.0.0:0 upstream-proxy-tunnel 127.0.0.1:3128 init-addr
127.0.0.1
```
@Dave: Can you use a name for the upstream-proxy-tunnel instead of IP?
@ALL: I need some help to implement fetch and conv feature into the patch, could
anyone help me for that.
Regards
Alex
On 2024-06-12 (Mi.) 12:57, Aleksandar Lazic wrote:
Hi Dave.
On 2024-06-12 (Mi.) 12:45, Aleksandar Lazic wrote:
On 2024-06-12 (Mi.) 12:26, Dave Cottlehuber wrote:
On Tue, 11 Jun 2024, at 22:57, Aleksandar Lazic wrote:
Hi Dave.
Thank you for your test and feedback.
When you put this line into backend, will this be better?
```
tcp-request connection upstream-proxy-header HOST www.httpbun.com
```
Regards
Alex
Hi Alex,
Sorry I forgot to mention that. This is not allowed by the backend:
[ALERT] (76213) : config : parsing
[/usr/local/etc/haproxy/haproxy.conf:228] : tcp-request connection is not
allowed because backend stream_be is not a frontend
So there is likely a simple solution to allow these in either end.
Not yet, afaik.
Looks like the "tcp-request content ..." is the solution.
```
frontend stream_fe
bind :::8443 v4v6
mode tcp
option tcplog
default_backend stream_be
backend stream_be
mode tcp
log global
tcp-request content upstream-proxy-header HOST www.httpbun.com
tcp-request content set-dst-port int(4433)
#tcp-request content set-hostname %[str(www.test1.com)]
tcp-request content upstream-proxy-header Host www.test1.com
tcp-request content upstream-proxy-header Proxy-Authorization "basic
%[env(MYPASS),base64]"
#server stream www.httpbun.com:443 upstream-proxy-tunnel 123.45.67.89:8000
server https_Via_Proxy1 www.test1.com:4433 upstream-proxy-tunnel
127.0.0.1:3128 init-addr 127.0.0.1
```
Looks like there is still a lot of work to do.
I work on that, stay tuned for updates.
From my point of view are the *upstream-proxy* settings only useful in the
backend section.
A+
Dave
Regards
Alex
From aad7b0afbbb37c988fc61801f2d4f56a6d1d8240 Mon Sep 17 00:00:00 2001
From: Aleksandar Lazic <al-hapr...@none.at>
Date: Wed, 12 Jun 2024 14:53:07 +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
---
doc/configuration.txt | 42 ++++++
examples/upstream-proxy-squid.conf | 60 +++++++++
examples/upstream-proxy.cfg | 91 +++++++++++++
include/haproxy/action-t.h | 7 +
include/haproxy/connection-t.h | 29 ++++-
include/haproxy/connection.h | 4 +
include/haproxy/proxy-t.h | 2 +
include/haproxy/server-t.h | 8 +-
include/haproxy/tcpcheck-t.h | 1 +
src/backend.c | 5 +
src/connection.c | 197 +++++++++++++++++++++++++++++
src/proto_quic.c | 4 +
src/proto_tcp.c | 2 +
src/proxy.c | 4 +
src/server.c | 140 ++++++++++++--------
src/sock.c | 3 +
src/tcp_rules.c | 112 +++++++++++++++-
src/tcpcheck.c | 3 +
src/xprt_handshake.c | 18 +++
19 files changed, 669 insertions(+), 63 deletions(-)
create mode 100644 examples/upstream-proxy-squid.conf
create mode 100644 examples/upstream-proxy.cfg
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 971c54d28..772e7a3cb 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -14269,6 +14269,8 @@ tarpit - - - - X - -
track-sc1 X X X - X X -
track-sc2 X X X - X X -
unset-var X X X X X X X
+upstream-proxy-header - - X - - - -
+upstream-proxy-target - - X - - - -
use-service - - X - X - -
wait-for-body - - - - X X -
wait-for-handshake - - - - X - -
@@ -15708,6 +15710,37 @@ unset-var(<var-name>)
Example:
http-request unset-var(req.my_var)
+upstream-proxy-header <header> <fmt>
+ Usable in: TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
+ X | - | - | - | - | - | -
+
+ This rule add headers to the upstream proxy connection.
+
+ Arguments :
+ <header> the Header name which should be added to the upstream proxy
+ call.
+ <fmt> the sample expression for the value
+
+ Example:
+ tcp-request connection upstream-proxy-header Host www.test1.com
+ tcp-request connection upstream-proxy-header Proxy-Authorization "basic %[env(MYPASS),base64]"
+
+ See also : server upstream-proxy-tunnel keyword
+
+upstream-proxy-target <target>
+ Usable in: TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
+ - | - | X | - | - | - | -
+
+ This rule sets the Target for the upstream proxy connection.
+
+ Arguments :
+ <header> the Target which should be used for the upstream proxy.
+
+ Example:
+ tcp-request connection upstream-proxy-header Host www.test1.com
+ tcp-request connection upstream-proxy-header Proxy-Authorization "basic %[env(MYPASS),base64]"
+
+ See also : server upstream-proxy-tunnel keyword
use-service <service-name>
Usable in: TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
@@ -18076,6 +18109,13 @@ tls-tickets
It may also be used as "default-server" setting to reset any previous
"default-server" "no-tls-tickets" setting.
+upstream-proxy-tunnel <addr>:<port>
+ May be used in the following contexts: tcp
+
+ This option enables upstream http proxy tunnel for outgoing connections to
+ the server. Using this option won't force the health check to go via upstream
+ http proxy by default.
+
verify [none|required]
May be used in the following contexts: tcp, http, log, peers, ring
@@ -21990,6 +22030,8 @@ fc_err_str : string
| 41 | "SOCKS4 Proxy deny the request" |
| 42 | "SOCKS4 Proxy handshake aborted by server" |
| 43 | "SSL fatal error" |
+ | 44 | "Error during reverse connect" |
+ | 45 | "Upstream http proxy write error during handshake" |
+----+---------------------------------------------------------------------------+
fc_fackets : integer
diff --git a/examples/upstream-proxy-squid.conf b/examples/upstream-proxy-squid.conf
new file mode 100644
index 000000000..7410f0969
--- /dev/null
+++ b/examples/upstream-proxy-squid.conf
@@ -0,0 +1,60 @@
+#
+# ATTETNTION: This squid config is only for test purpose!
+# ATTETNTION: http_access allow all
+# ATTETNTION: http_access allow CONNECT all
+#
+
+#debug_options all,9
+
+acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
+acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
+acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
+acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
+acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN)
+acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
+acl localnet src fc00::/7 # RFC 4193 local private network range
+acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
+
+acl SSL_ports port 443
+acl Safe_ports port 80 # http
+acl Safe_ports port 21 # ftp
+acl Safe_ports port 443 # https
+acl Safe_ports port 70 # gopher
+acl Safe_ports port 210 # wais
+acl Safe_ports port 1025-65535 # unregistered ports
+acl Safe_ports port 280 # http-mgmt
+acl Safe_ports port 488 # gss-http
+acl Safe_ports port 591 # filemaker
+acl Safe_ports port 777 # multiling http
+
+http_access allow all
+
+http_access allow CONNECT all
+
+# http_access allow localhost manager
+# http_access deny manager
+
+# http_access allow localhost
+
+
+# http_access deny to_localhost
+
+# http_access deny to_linklocal
+
+include /etc/squid/conf.d/*.conf
+
+
+#http_access deny all
+
+http_port 3128
+
+coredump_dir /var/spool/squid
+
+refresh_pattern ^ftp: 1440 20% 10080
+refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
+refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
+refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
+refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
+refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
+refresh_pattern . 0 20% 4320
+
diff --git a/examples/upstream-proxy.cfg b/examples/upstream-proxy.cfg
new file mode 100644
index 000000000..29ad2eda0
--- /dev/null
+++ b/examples/upstream-proxy.cfg
@@ -0,0 +1,91 @@
+# Test setup.
+# shell1: curl -vk --connect-to www.test1.com:4433:127.0.0.1:8080 -H "Host: www.test1.com" https://www.test1.com:4433
+# shell2: ./haproxy -d -f examples/upstream-proxy.cfg
+# shell3: sudo podman run --rm -it --name squid -e TZ=UTC -p 3128:3128 --network host -v /datadisk/git-repos/haproxy/examples/upstream-proxy-squid.conf:/etc/squid/squid.conf ubuntu/squid > squid_debug_all_allow_all.log 2>&1
+# shell4: openssl s_server -trace -www -bugs -debug -cert reg-tests/ssl/common.pem
+
+global
+ log stdout format raw daemon debug
+ stats timeout 30s
+
+ # Should be set outsite of haproxy config
+ setenv PROXY_AUTH USERNAME:MY-PASS
+
+ #tune.pt.zero-copy-forwarding off
+
+ # turn on stats unix socket
+ stats socket /datadisk/git-repos/haproxy/stats mode 660 level admin
+
+ expose-experimental-directives
+ #trace h1 sink stdout
+ #trace h1 level developer
+ #trace h1 verbosity complete
+ #trace h1 start now
+
+ #trace stream sink stdout
+ #trace stream level developer
+ #trace stream verbosity complete
+ #trace stream start now
+
+ #trace pt sink stdout
+ #trace pt level developer
+ #trace pt verbosity complete
+ #trace pt start now
+
+defaults
+
+ log global
+ maxconn 2000
+ timeout connect 5s
+ timeout client 50s
+ timeout server 50s
+
+resolvers mydns
+ nameserver local 127.0.0.53:53
+ timeout retry 1s
+ hold valid 10s
+ hold nx 3s
+ hold other 3s
+ hold obsolete 0s
+ accepted_payload_size 8192
+
+listen proxy-forward-8080
+ bind 0.0.0.0:8080
+ mode tcp
+ option tcplog
+
+ #tcp-request content do-resolve(txn.dstip,mydns,ipv4) str(www.test1.com)
+ #tcp-request content set-dst var(txn.dstip)
+ tcp-request content set-dst str(127.0.0.1)
+ tcp-request content set-dst-port int(4433)
+ tcp-request content upstream-proxy-target www.test1.com
+ tcp-request content upstream-proxy-header Host www.test1.com
+ tcp-request content upstream-proxy-header Proxy-Authorization "basic %[env(PROXY_AUTH),base64]"
+
+ #server stream www.httpbun.com:443 upstream-proxy-tunnel 123.45.67.89:8000
+ server https_Via_Proxy1 0.0.0.0:0 upstream-proxy-tunnel 127.0.0.1:3128 init-addr 127.0.0.1
+ #server https_Via_Proxy1 www.test1.com:4433 upstream-proxy-tunnel 127.0.0.1:3128 init-addr 127.0.0.1
+
+frontend stream_fe
+ bind :::8443 v4v6
+ mode tcp
+
+ default_backend stream_be
+
+backend stream_be
+
+ mode tcp
+
+ #tcp-request content upstream-proxy-header HOST www.httpbun.com
+ #tcp-request content do-resolve(txn.dstip,mydns,ipv4) str(www.test1.com)
+ #tcp-request content set-dst var(txn.dstip)
+ tcp-request content set-dst str(127.0.0.1)
+ tcp-request content set-dst-port int(4433)
+ tcp-request content upstream-proxy-target www.test1.com
+ tcp-request content upstream-proxy-header Host www.test1.com
+ tcp-request content upstream-proxy-header Proxy-Authorization "basic %[env(PROXY_AUTH),base64]"
+
+ #server stream www.httpbun.com:443 upstream-proxy-tunnel 123.45.67.89:8000
+ server https_Via_Proxy1 0.0.0.0:0 upstream-proxy-tunnel 127.0.0.1:3128 init-addr 127.0.0.1
+ #server https_Via_Proxy1 www.test1.com:4433 upstream-proxy-tunnel 127.0.0.1:3128 init-addr 127.0.0.1
+
diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h
index eee16a312..51e1fba18 100644
--- a/include/haproxy/action-t.h
+++ b/include/haproxy/action-t.h
@@ -93,6 +93,13 @@ enum act_name {
/* tcp actions */
ACT_TCP_EXPECT_PX,
ACT_TCP_EXPECT_CIP,
+
+ /* Add Upstream hostname */
+ ACT_TCP_UPSTREAM_HOSTNAME,
+
+ /* Add Upstream header */
+ ACT_TCP_UPSTREAM_HEADER,
+
ACT_TCP_CLOSE, /* close at the sender's */
};
diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h
index 83969da06..beea75dce 100644
--- a/include/haproxy/connection-t.h
+++ b/include/haproxy/connection-t.h
@@ -86,7 +86,9 @@ enum {
CO_FL_OPT_TOS = 0x00000020, /* connection has a special sockopt tos */
- /* unused : 0x00000040, 0x00000080 */
+ /* Upstream http proxy */
+ 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 */
/* These flags indicate whether the Control and Transport layers are initialized */
CO_FL_CTRL_READY = 0x00000100, /* FD was registered, fd_delete() needed */
@@ -133,7 +135,7 @@ enum {
CO_FL_ACCEPT_CIP = 0x04000000, /* receive a valid NetScaler Client IP header */
/* 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_UPSTREAM_PROXY_TUNNEL_RECV,
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 +157,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
@@ -174,8 +180,8 @@ static forceinline char *conn_show_flags(char *buf, size_t len, const char *deli
_(CO_FL_SOCK_WR_SH, _(CO_FL_ERROR, _(CO_FL_FDLESS, _(CO_FL_WAIT_L4_CONN,
_(CO_FL_WAIT_L6_CONN, _(CO_FL_SEND_PROXY, _(CO_FL_ACCEPT_PROXY, _(CO_FL_ACCEPT_CIP,
_(CO_FL_SSL_WAIT_HS, _(CO_FL_PRIVATE, _(CO_FL_RCVD_PROXY, _(CO_FL_SESS_IDLE,
- _(CO_FL_XPRT_TRACKED
- ))))))))))))))))))))))))))));
+ _(CO_FL_XPRT_TRACKED, _(CO_FL_UPSTREAM_PROXY_TUNNEL_SEND ,_(CO_FL_UPSTREAM_PROXY_TUNNEL_RECV
+ ))))))))))))))))))))))))))))));
/* epilogue */
_(~0U);
return buf;
@@ -241,6 +247,9 @@ 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 */
+ CO_ER_PROXY_CONNECT_RECV, /* Upstream http proxy read error during handshake */
};
/* error return codes for accept_conn() */
@@ -356,6 +365,16 @@ struct socks4_request {
char user_id[8]; /* the user ID string, variable length, terminated with a null (0x00); Using "HAProxy\0" */
};
+/*
+ * Upstream proxy header list
+ */
+
+struct uph_list {
+ struct list list;
+ struct buffer name;
+ struct buffer value;
+};
+
/* A connection handle is how we differentiate two connections on the lower
* layers. It usually is a file descriptor but can be a connection id. The
* CO_FL_FDLESS flag indicates which one is relevant.
@@ -526,7 +545,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 aa61cc71a..03e6cea0d 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -74,6 +74,10 @@ 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, int flag);
+int conn_recv_upstream_proxy_tunnel_response(struct connection *conn, int flag);
+
/* 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/proxy-t.h b/include/haproxy/proxy-t.h
index f6ed211c1..98fab13db 100644
--- a/include/haproxy/proxy-t.h
+++ b/include/haproxy/proxy-t.h
@@ -301,6 +301,8 @@ struct proxy {
struct list inspect_rules; /* inspection rules */
struct list l4_rules; /* layer4 rules */
struct list l5_rules; /* layer5 rules */
+ struct list uph_rules; /* upstream rules */
+ struct buffer upt; /* upstream proxy target */
} tcp_req;
struct { /* TCP request processing */
unsigned int inspect_delay; /* inspection delay */
diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h
index af58a5609..75f03e023 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 */
@@ -469,9 +470,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 22310eee0..ee861aaea 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 d74ae4082..d0dc6057a 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1716,6 +1716,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 3fedad9a5..0c420ba77 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -758,6 +758,8 @@ const char *conn_err_code_str(struct connection *c)
case CO_ERR_SSL_FATAL: return "SSL fatal error";
case CO_ER_REVERSE: return "Reverse connect failure";
+
+ case CO_ER_PROXY_CONNECT_SEND: return "Upstream http proxy write error during handshake";
}
return NULL;
}
@@ -1783,6 +1785,201 @@ int conn_recv_socks4_proxy_response(struct connection *conn)
return 0;
}
+/* TODO: use *TRACE* features from HAProxy instead of DPRINTF */
+int conn_send_upstream_proxy_tunnel_request(struct connection *conn, int flag) {
+
+ struct server *srv = NULL;
+ struct proxy *prx = NULL;
+ struct uph_list *my_headers;
+
+ DPRINTF(stderr, "HTTP TUNNEL SEND start\n");
+
+ if (!conn_ctrl_ready(conn))
+ goto out_error;
+
+ if (!conn_get_dst(conn))
+ goto out_error;
+
+ /* srv must be set */
+ srv = objt_server(conn->target);
+ BUG_ON(!srv);
+
+ prx = srv->proxy;
+
+ if (!prx){
+ DPRINTF(stderr," NO PROXYS\n");
+ goto out_error;
+ }
+
+ DPRINTF(stderr,"proxy->id :%s:\n", prx->id);
+ DPRINTF(stderr,"upstream-proxy-target: %s\n", ist0(ist2(b_orig(&prx->tcp_req.upt), b_data(&prx->tcp_req.upt))));
+
+ chunk_reset(&trash);
+ chunk_memcat(&trash,"CONNECT ",8);
+ chunk_memcat(&trash,b_orig(&prx->tcp_req.upt), b_data(&prx->tcp_req.upt));
+
+ /* TODO use buffer for get_net_port? */
+ chunk_appendf(&trash,":%u HTTP/1.1\r\n",
+ ntohs(get_net_port(conn->dst)));
+
+ list_for_each_entry(my_headers, &prx->tcp_req.uph_rules, list) {
+ DPRINTF(stderr,"list each name :%s:\n",ist0(ist2(b_orig(&my_headers->name), b_data(&my_headers->name))));
+ DPRINTF(stderr,"list each value :%s:\n",ist0(ist2(b_orig(&my_headers->value), b_data(&my_headers->value))));
+ chunk_memcat(&trash,b_orig(&my_headers->name), b_data(&my_headers->name));
+ chunk_memcat(&trash,": ",2);
+ chunk_memcat(&trash,b_orig(&my_headers->value), b_data(&my_headers->value));
+ chunk_memcat(&trash, "\r\n", 2);
+ }
+
+ /* Send last line to proxy*/
+ chunk_memcat(&trash, "\r\n", 2);
+
+ DPRINTF(stderr,"trash->data :%d:\n", (unsigned int)b_data(&trash));
+ DPRINTF(stderr,"What is send to Proxy >>:%s:<<\n",ist0(ist2(b_orig(&trash), b_data(&trash))));
+ DPRINTF(stderr,"send_proxy_ofs: %d\n", conn->send_proxy_ofs);
+
+ if (conn->send_proxy_ofs > 0) {
+ /*
+ * This is the first call to send the request
+ */
+ conn->send_proxy_ofs = -(int) b_data(&trash);
+ }
+
+ 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);
+ */
+
+ ret = conn_ctrl_send(
+ conn,
+ b_orig(&trash),
+ -conn->send_proxy_ofs,
+ (conn->subs && conn->subs->events & SUB_RETRY_SEND) ? CO_SFL_MSG_MORE : 0);
+
+ DPRINTF(stderr, "HTTP TUNNEL send_proxy_ofs FD[%04X]: Before send remain is [%d], sent [%d]\n",
+ conn->handle.fd, conn->send_proxy_ofs, ret);
+
+ if (ret < 0) {
+ DPRINTF(stderr, "HTTP TUNNEL FD[%04X]: Before send remain is [%d], sent [%d]\n",
+ conn->handle.fd, conn->send_proxy_ofs, ret);
+
+ 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;
+ conn->flags &= ~flag;
+
+
+ /* The connection is ready now, simply return and let the connection
+ * handler notify upper layers if needed.
+ */
+ conn->flags &= ~CO_FL_WAIT_L4_CONN;
+
+ DPRINTF(stderr,"HTTP TUNNEL SEND end: okay\n");
+ DPRINTF(stderr,"send_proxy_ofs: %d\n", conn->send_proxy_ofs);
+
+ 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;
+ }
+ DPRINTF(stderr, "HTTP TUNNEL SEND end: out_error\n");
+ return 0;
+
+ out_wait:
+ DPRINTF(stderr, "HTTP TUNNEL SEND end: out_wait\n");
+ return 0;
+}
+
+
+int conn_recv_upstream_proxy_tunnel_response(struct connection *conn, int flag) {
+
+ struct ist upstream_proxy_response = IST_NULL;
+ struct ist upstream_proxy_successful = ist("HTTP/1.1 200 Connection established");
+ int ret;
+
+ //DPRINTF(stderr, "HTTP TUNNEL RECV start\n");
+
+ if (!conn_ctrl_ready(conn))
+ goto fail;
+
+ BUG_ON(conn->flags & CO_FL_FDLESS);
+
+ if (!fd_recv_ready(conn->handle.fd)){
+ DPRINTF(stderr, "HTTP TUNNEL RECV fd_recv_ready\n");
+ goto not_ready;
+ }
+
+ chunk_reset(&trash);
+
+ while (1) {
+ ret = recv(conn->handle.fd, b_orig(&trash), b_size(&trash), MSG_DONTWAIT);
+
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ goto not_ready;
+ }
+
+ b_set_data(&trash, ret);
+ upstream_proxy_response = ist2(b_orig(&trash), b_data(&trash));
+
+ /* get the first line from proxy response*/
+ upstream_proxy_response = iststop(upstream_proxy_response,'\r');
+ DPRINTF(stderr,"upstream_proxy_response len :%ld:\n",istlen(upstream_proxy_response));
+ DPRINTF(stderr,"upstream_proxy_response :%s:\n",ist0(upstream_proxy_response));
+ DPRINTF(stderr,"Test isteq :%d:\n",isteq(upstream_proxy_response,upstream_proxy_successful));
+
+ /* check for HTTP/1.1 200 Connection established */
+ if(!isteq(upstream_proxy_response,upstream_proxy_successful)){
+ DPRINTF(stderr,"HTTP TUNNEL no 200 ret :%d: errno :%d: strerror :%s:\n", ret, errno ,strerror(errno));
+ goto recv_abort;
+ }
+ break;
+ }
+
+ DPRINTF(stderr, "HTTP TUNNEL RECV end\n");
+ conn->flags &= ~flag;
+ conn->flags &= ~CO_FL_WAIT_L4_CONN;
+
+ return 1;
+
+ not_ready:
+ //DPRINTF(stderr, "HTTP TUNNEL RECV not_ready out\n");
+ conn->flags |= CO_FL_WAIT_L4_CONN;
+ return 0;
+
+ recv_abort:
+ DPRINTF(stderr, "HTTP TUNNEL RECV recv_abort out\n");
+ if (conn->err_code == CO_ER_NONE) {
+ conn->err_code = CO_ER_PROXY_CONNECT_RECV;
+ }
+ conn->flags |= (CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH);
+ goto fail;
+
+ fail:
+ DPRINTF(stderr, "HTTP TUNNEL RECV fail out\n");
+ conn->flags |= CO_FL_ERROR;
+ 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 93a24af4b..cbf8c2c31 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 63be77508..10bdb9b0d 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/proxy.c b/src/proxy.c
index f1d9d7ab0..8ca5c366c 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -274,6 +274,7 @@ void free_proxy(struct proxy *p)
free_act_rules(&p->tcp_rep.inspect_rules);
free_act_rules(&p->tcp_req.l4_rules);
free_act_rules(&p->tcp_req.l5_rules);
+ free_act_rules(&p->tcp_req.uph_rules);
free_act_rules(&p->http_req_rules);
free_act_rules(&p->http_res_rules);
free_act_rules(&p->http_after_res_rules);
@@ -1355,6 +1356,7 @@ void init_new_proxy(struct proxy *p)
LIST_INIT(&p->tcp_rep.inspect_rules);
LIST_INIT(&p->tcp_req.l4_rules);
LIST_INIT(&p->tcp_req.l5_rules);
+ LIST_INIT(&p->tcp_req.uph_rules);
MT_LIST_INIT(&p->listener_queue);
LIST_INIT(&p->loggers);
LIST_INIT(&p->conf.bind);
@@ -1461,6 +1463,8 @@ void proxy_free_defaults(struct proxy *defproxy)
free_act_rules(&defproxy->tcp_rep.inspect_rules);
free_act_rules(&defproxy->tcp_req.l4_rules);
free_act_rules(&defproxy->tcp_req.l5_rules);
+ free_act_rules(&defproxy->tcp_req.uph_rules);
+ chunk_destroy(&defproxy->tcp_req.upt);
free_act_rules(&defproxy->http_req_rules);
free_act_rules(&defproxy->http_res_rules);
free_act_rules(&defproxy->http_after_res_rules);
diff --git a/src/server.c b/src/server.c
index caf2f40bb..d3128dcb2 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1935,6 +1935,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)
@@ -2283,55 +2315,56 @@ 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-conn-name", srv_parse_pool_conn_name, 1, 1, 1 }, /* Define expression to identify connections in idle pool */
- { "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-conn-name", srv_parse_pool_conn_name, 1, 1, 1 }, /* Define expression to identify connections in idle pool */
+ { "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 },
}};
@@ -2850,10 +2883,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 df82c6ea7..5c6f4e005 100644
--- a/src/sock.c
+++ b/src/sock.c
@@ -897,6 +897,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/tcp_rules.c b/src/tcp_rules.c
index 9ce6c9037..5247538e4 100644
--- a/src/tcp_rules.c
+++ b/src/tcp_rules.c
@@ -1018,6 +1018,114 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
arg += 2;
rule->action = ACT_TCP_EXPECT_CIP;
}
+ else if (strcmp(args[arg], "upstream-proxy-header") == 0) {
+ struct sample_expr *expr;
+ struct uph_list *my_headers;
+ int ret;
+ int kw = arg;
+
+ /*
+ * args[0] = tcp-request
+ * args[1] = connection
+ */
+
+ /* skip over keyword "upstream-proxy-header" */
+ arg++;
+
+ /* TODO: What's the best place to free this? */
+ my_headers = calloc(1,sizeof(*my_headers));
+
+ if (!my_headers)
+ {
+ DPRINTF(stderr,"Can't calloc my_headers\n");
+ }
+
+ LIST_INIT(&my_headers->list);
+
+ chunk_reset(&trash);
+
+ ret = chunk_printf(&trash, "%s",args[arg]);
+ chunk_dup(&my_headers->name,&trash);
+
+ DPRINTF(stderr,"b_size :%ld:\n",b_size(&my_headers->name));
+ DPRINTF(stderr,"b_data :%ld:\n",b_data(&my_headers->name));
+ DPRINTF(stderr,"Header name :%s:\n",args[arg]);
+
+ arg++;
+
+ chunk_reset(&trash);
+
+ ret = chunk_printf(&trash, "%s",args[arg]);
+ chunk_dup(&my_headers->value,&trash);
+
+ DPRINTF(stderr,"Header value :%s:\n",args[arg]);
+ ret = chunk_printf(&my_headers->value, "%s",args[arg]);
+ arg++;
+
+ LIST_APPEND(&curpx->tcp_req.uph_rules, &my_headers->list);
+
+ DPRINTF(stderr,"name :%s:\n",ist0(ist2(b_orig(&my_headers->name), b_data(&my_headers->name))));
+ DPRINTF(stderr,"value :%s:\n",ist0(ist2(b_orig(&my_headers->value), b_data(&my_headers->value))));
+
+/*
+ curpx->conf.args.ctx = ARGC_TCO;
+ expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args, NULL);
+ if (!expr) {
+ memprintf(err,
+ "'%s %s %s' : %s",
+ args[0], args[1], args[kw], *err);
+ return -1;
+ }
+ */
+ /*
+ * args[arg] => Header name
+ * args[arg+1] => Header value
+ */
+ //DPRINTF(stderr,"'%s %s' is in '%s %s' rules in %s '%s'\n",
+ // args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
+ //arg += 2;
+ rule->action = ACT_TCP_UPSTREAM_HEADER;
+ }
+ else if (strcmp(args[arg], "upstream-proxy-target") == 0) {
+ struct sample_expr *expr;
+ int ret;
+ int kw = arg;
+
+ /*
+ * args[0] = tcp-request
+ * args[1] = connection
+ */
+
+ /* skip over keyword "upstream-proxy-target" */
+ arg++;
+
+ chunk_reset(&trash);
+
+ ret = chunk_printf(&trash, "%s",args[arg]);
+ chunk_dup(&curproxy->tcp_req.upt,&trash);
+
+ /*
+ curpx->conf.args.ctx = ARGC_TCO;
+ expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args, NULL);
+ if (!expr) {
+ memprintf(err,
+ "'%s %s %s' : %s",
+ args[0], args[1], args[kw], *err);
+ return -1;
+ }
+ */
+
+ /*
+ * args[arg] => Header name
+ * args[arg+1] => Header value
+ * /
+ DPRINTF(stderr,"<<<<<<<<<<<<< upstream-proxy-target '%s %s' is in '%s %s' rules in %s '%s'\n",
+ args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
+ */
+ DPRINTF(stderr,"upstream-proxy-target :%s:\n",ist0(ist2(b_orig(&curproxy->tcp_req.upt), b_data(&curproxy->tcp_req.upt))));
+ arg += 2;
+ rule->action = ACT_TCP_UPSTREAM_HOSTNAME;
+ }
else {
struct action_kw *kw;
if (where & SMP_VAL_FE_CON_ACC) {
@@ -1038,7 +1146,7 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
return -1;
} else {
- const char *extra[] = { "accept", "reject", "capture", "track-sc", "expect-proxy", "expect-netscaler-cip", NULL };
+ const char *extra[] = { "accept", "reject", "capture", "track-sc", "expect-proxy", "expect-netscaler-cip","upstream-proxy-header","upstream-proxy-target", NULL };
const char *best = NULL;
@@ -1056,7 +1164,7 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
}
memprintf(err,
- "'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-cip', 'track-sc0' ... 'track-sc%d', %s "
+ "'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-cip', 'upstream-proxy-header', 'upstream-proxy-target', 'track-sc0' ... 'track-sc%d', %s "
"in %s '%s' (got '%s').%s%s%s\n",
args[0], args[1], global.tune.nb_stk_ctr-1,
trash.area, proxy_type_str(curpx),
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index e820fdc2e..b516b2ac0 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 33f775087..bd6c6cf51 100644
--- a/src/xprt_handshake.c
+++ b/src/xprt_handshake.c
@@ -56,6 +56,24 @@ 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_SEND) {
+ if (!conn_send_upstream_proxy_tunnel_request(conn,CO_FL_UPSTREAM_PROXY_TUNNEL_SEND)) {
+ ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_SEND,
+ &ctx->wait_event);
+ goto out;
+ }
+ }
+
+ /* TODO: check flags to send optional headers like keep alive host etc */
+ if (conn->flags & CO_FL_UPSTREAM_PROXY_TUNNEL_RECV) {
+ if (!conn_recv_upstream_proxy_tunnel_response(conn, CO_FL_UPSTREAM_PROXY_TUNNEL_RECV)) {
+ ctx->xprt->subscribe(conn, ctx->xprt_ctx, SUB_RETRY_RECV,
+ &ctx->wait_event);
+ goto out;
+ }
+ }
+
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