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
This commit is contained in:
Stefan Eissing 2026-04-16 13:44:13 +02:00 committed by Daniel Stenberg
parent 021a87cf81
commit 70281e39be
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
6 changed files with 86 additions and 10 deletions

View file

@ -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.

View file

@ -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

View file

@ -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 \
\

View file

@ -46,7 +46,7 @@ proxy
# Verify data after the test has been "shot"
<verify>
<strippart>
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/
</strippart>
<protocol crlf="yes">
proxy-line

View file

@ -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
<strippart>
s/^PROXY TCP6 2001:db8:: ::1 (\d*) %HTTP6PORT/proxy-line/
s/^PROXY TCP6 2001:db8:: 2001:db8:: (\d*) %HTTP6PORT/proxy-line/
</strippart>
<protocol crlf="yes">
proxy-line

61
tests/data/test3220 Normal file
View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="US-ASCII"?>
<testcase>
<info>
<keywords>
HTTP
HTTP GET
proxy
haproxy
</keywords>
</info>
# Server-side
<reply name="%TESTNUMBER">
<data nocheck="yes">
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-
</data>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP GET when PROXY Protocol enabled and spoofed client IP
</name>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER --haproxy-clientip "2a04:4e42::347" -H "Testno: %TESTNUMBER"
</command>
<features>
proxy
</features>
</client>
# Verify data after the test has been "shot"
<verify>
<strippart>
s/^PROXY TCP6 2a04:4e42::347 2a04:4e42::347 (\d*) %HTTPPORT/proxy-line/
</strippart>
<protocol crlf="yes">
proxy-line
GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
Testno: %TESTNUMBER
</protocol>
</verify>
</testcase>