From 70281e39becfa6fd37dbbd24d6fc73b96ed9fea6 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 16 Apr 2026 13:44:13 +0200 Subject: [PATCH] haproxy: use correct ip version on client supplied address When a user supplies an IP address to use for the HAPROXY protocol, the IP version reported must be deduced from the address and has no relation to the IP version used for the upstream connection. Add test3220 to verify. Fixes #21340 Reported-by: Fiona Klute Closes #21341 --- .../libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md | 3 + lib/cf-haproxy.c | 26 +++++--- tests/data/Makefile.am | 2 +- tests/data/test3201 | 2 +- tests/data/test3202 | 2 +- tests/data/test3220 | 61 +++++++++++++++++++ 6 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 tests/data/test3220 diff --git a/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md b/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md index f26bda7cf1..f9fdba66b5 100644 --- a/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md +++ b/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md @@ -31,6 +31,9 @@ When this parameter is set to a valid IPv4 or IPv6 numerical address in its printable ASCII string version, the library sends this as the client address in the HAProxy PROXY protocol v1 header at beginning of the connection. +The client address is reported upstream as the source *and* destination address +of the non-existing client connection (since 8.20.0). + This option is an alternative to CURLOPT_HAPROXYPROTOCOL(3) as that one cannot use a specified address. diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 46d0541055..9ee5e790eb 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -28,6 +28,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" @@ -61,9 +62,16 @@ static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, struct Curl_easy *data) { + /* We fake a client connection report to the upstream server + * with the HAProxy protocol, reporting the client's source + * and destination IP addresses and ports. + * addresses: either the ones used to talk to the upstream + * OR the value supplied by the user + * ports: the ports used in the upstream connection */ + const char *client_source_ip; + const char *client_dest_ip; struct cf_haproxy_ctx *ctx = cf->ctx; CURLcode result; - const char *client_ip; struct ip_quadruple ipquad; bool is_ipv6; @@ -79,15 +87,19 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, if(result) return result; - /* Emit the correct prefix for IPv6 */ - if(data->set.str[STRING_HAPROXY_CLIENT_IP]) - client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; - else - client_ip = ipquad.local_ip; + if(data->set.str[STRING_HAPROXY_CLIENT_IP]) { + client_source_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; + client_dest_ip = client_source_ip; + is_ipv6 = !Curl_is_ipv4addr(client_source_ip); + } + else { + client_source_ip = ipquad.local_ip; + client_dest_ip = ipquad.remote_ip; + } result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", is_ipv6 ? "TCP6" : "TCP4", - client_ip, ipquad.remote_ip, + client_source_ip, client_dest_ip, ipquad.local_port, ipquad.remote_port); #ifdef USE_UNIX_SOCKETS diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index e955c8383f..15724a4b53 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -283,7 +283,7 @@ test3100 test3101 test3102 test3103 test3104 test3105 \ \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ -test3216 test3217 test3218 test3219 \ +test3216 test3217 test3218 test3219 test3220 \ \ test3300 test3301 \ \ diff --git a/tests/data/test3201 b/tests/data/test3201 index 23fcec12f8..7e051a2167 100644 --- a/tests/data/test3201 +++ b/tests/data/test3201 @@ -46,7 +46,7 @@ proxy # Verify data after the test has been "shot" -s/^PROXY TCP4 192.168.1.1 %HOSTIP (\d*) %HTTPPORT/proxy-line/ +s/^PROXY TCP4 192.168.1.1 192.168.1.1 (\d*) %HTTPPORT/proxy-line/ proxy-line diff --git a/tests/data/test3202 b/tests/data/test3202 index ffa0557438..499b7dba56 100644 --- a/tests/data/test3202 +++ b/tests/data/test3202 @@ -51,7 +51,7 @@ proxy # Strip off the (random) local port number. This test used to use a fixed # local port number that frequently causes the test to fail -s/^PROXY TCP6 2001:db8:: ::1 (\d*) %HTTP6PORT/proxy-line/ +s/^PROXY TCP6 2001:db8:: 2001:db8:: (\d*) %HTTP6PORT/proxy-line/ proxy-line diff --git a/tests/data/test3220 b/tests/data/test3220 new file mode 100644 index 0000000000..ba8dec8f92 --- /dev/null +++ b/tests/data/test3220 @@ -0,0 +1,61 @@ + + + + +HTTP +HTTP GET +proxy +haproxy + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: barkbark + +-foo- + + + +# Client-side + + +http + + +HTTP GET when PROXY Protocol enabled and spoofed client IP + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --haproxy-clientip "2a04:4e42::347" -H "Testno: %TESTNUMBER" + + +proxy + + + +# Verify data after the test has been "shot" + + +s/^PROXY TCP6 2a04:4e42::347 2a04:4e42::347 (\d*) %HTTPPORT/proxy-line/ + + +proxy-line +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Testno: %TESTNUMBER + + + +