]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
haproxy: use correct ip version on client supplied address
authorStefan Eissing <stefan@eissing.org>
Thu, 16 Apr 2026 11:44:13 +0000 (13:44 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 17 Apr 2026 08:01:49 +0000 (10:01 +0200)
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

docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md
lib/cf-haproxy.c
tests/data/Makefile.am
tests/data/test3201
tests/data/test3202
tests/data/test3220 [new file with mode: 0644]

index f26bda7cf1a80b5b75aea9a81f4071d23a51f4b9..f9fdba66b5cc917f88cb2ad709599eaf3e224231 100644 (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.
 
index 46d0541055d203f252ae4e0fc8b3368abeeb8009..9ee5e790ebb5d5cd7c9c0d07c4df05d65b1463f4 100644 (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
index e955c8383fbd27d1ad8d0fbcfd5f23be74418552..15724a4b538e803a3f7090aa9b743b9f31934048 100644 (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 \
 \
index 23fcec12f8ed6c6dd6a0468fbd0906333eac6fd9..7e051a216729ec10fe2c49271e716d4a825407a7 100644 (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
index ffa0557438e89a3f8563f8b76a7c61f5fca13740..499b7dba567653229c1f811ba97bd86ac118c5ab 100644 (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
diff --git a/tests/data/test3220 b/tests/data/test3220
new file mode 100644 (file)
index 0000000..ba8dec8
--- /dev/null
@@ -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>