From e234b5883cf9f1791af5ef8047f25ab6fcae7a56 Mon Sep 17 00:00:00 2001 From: Lawrence Matthews Date: Thu, 1 Dec 2016 04:05:04 -0800 Subject: [PATCH 1/6] Adds support for the HAProxy PROXY Protocol. --- include/curl/curl.h | 3 +++ lib/http.c | 38 ++++++++++++++++++++++++++ lib/setopt.c | 7 +++++ lib/urldata.h | 2 ++ src/tool_cfgable.h | 1 + src/tool_getparam.c | 4 +++ src/tool_help.c | 2 ++ src/tool_operate.c | 3 +++ tests/data/Makefile.inc | 2 +- tests/data/test1455 | 56 ++++++++++++++++++++++++++++++++++++++ tests/data/test1456 | 59 +++++++++++++++++++++++++++++++++++++++++ 11 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 tests/data/test1455 create mode 100644 tests/data/test1456 diff --git a/include/curl/curl.h b/include/curl/curl.h index fa019eca9dc887..43d5e031f8f2d7 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1841,6 +1841,9 @@ typedef enum { /* User data to pass to the resolver start callback. */ CINIT(RESOLVER_START_DATA, OBJECTPOINT, 273), + /* send HAProxy PROXY protocol header? */ + CINIT(HAPROXYPROTOCOL, LONG, 274), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/http.c b/lib/http.c index c1c7b39085d871..df2fbc01cd204b 100644 --- a/lib/http.c +++ b/lib/http.c @@ -92,6 +92,8 @@ static int http_getsock_do(struct connectdata *conn, int numsocks); static int http_should_fail(struct connectdata *conn); +static void add_haproxy_protocol_header(struct connectdata *conn, bool *done); + #ifdef USE_SSL static CURLcode https_connecting(struct connectdata *conn, bool *done); static int https_getsock(struct connectdata *conn, @@ -1357,6 +1359,11 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) /* nothing else to do except wait right now - we're not done here. */ return CURLE_OK; + if(conn->data->set.haproxyprotocol) { + /* add HAProxy PROXY protocol header */ + add_haproxy_protocol_header(conn, done); + } + if(conn->given->protocol & CURLPROTO_HTTPS) { /* perform SSL initialization */ result = https_connecting(conn, done); @@ -1382,6 +1389,37 @@ static int http_getsock_do(struct connectdata *conn, return GETSOCK_WRITESOCK(0); } +static void add_haproxy_protocol_header(struct connectdata *conn, bool *done) +{ + char proxy_header[128]; + Curl_send_buffer *req_buffer; + CURLcode result; + char tcp_version[5]; + + /* Emit the correct prefix for IPv6 */ + if (conn->bits.ipv6) { + strcpy(tcp_version, "TCP6"); + } else { + strcpy(tcp_version, "TCP4"); + } + + snprintf(proxy_header, + sizeof proxy_header, + "PROXY %s %s %s %i %i\r\n", + tcp_version, + conn->data->info.conn_local_ip, + conn->data->info.conn_primary_ip, + conn->data->info.conn_local_port, + conn->data->info.conn_primary_port); + req_buffer = Curl_add_buffer_init(); + Curl_add_bufferf(req_buffer, proxy_header); + result = Curl_add_buffer_send(req_buffer, + conn, + &conn->data->info.request_size, + 0, + FIRSTSOCKET); +} + #ifdef USE_SSL static CURLcode https_connecting(struct connectdata *conn, bool *done) { diff --git a/lib/setopt.c b/lib/setopt.c index 9c96eb3586b872..737a60f8569351 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1603,6 +1603,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE; break; + case CURLOPT_HAPROXYPROTOCOL: + /* + * Set to send the HAProxy Proxy Protocol header + */ + data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE; + break; + case CURLOPT_INTERFACE: /* * Set what interface or address/hostname to bind the socket to when diff --git a/lib/urldata.h b/lib/urldata.h index 3d7b9e5a53881a..32c3a05f123f46 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1678,6 +1678,8 @@ struct UserDefined { bool stream_depends_e; /* set or don't set the Exclusive bit */ int stream_weight; + bool haproxyprotocol; /* whether to send HAProxy PROXY protocol header */ + struct Curl_http2_dep *stream_dependents; bool abstract_unix_socket; diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 743ce725d29c69..9abaa9d394feda 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -252,6 +252,7 @@ struct OperationConfig { bool ssh_compression; /* enable/disable SSH compression */ long happy_eyeballs_timeout_ms; /* happy eyeballs timeout in milliseconds. 0 is valid. default: CURL_HET_DEFAULT. */ + bool haproxy_protocol; /* whether to send HAProxy PROXY protocol */ struct GlobalConfig *global; struct OperationConfig *prev; struct OperationConfig *next; /* Always last in the struct */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index c6b1a0d6f5adea..13d25d7fd10d1a 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -112,6 +112,7 @@ static const struct LongShort aliases[]= { {"*x", "krb", ARG_STRING}, {"*x", "krb4", ARG_STRING}, /* 'krb4' is the previous name */ + {"*X", "haproxy-protocol", ARG_BOOL}, {"*y", "max-filesize", ARG_STRING}, {"*z", "disable-eprt", ARG_BOOL}, {"*Z", "eprt", ARG_BOOL}, @@ -778,6 +779,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ else return PARAM_LIBCURL_DOESNT_SUPPORT; break; + case 'X': /* --haproxy-protocol */ + config->haproxy_protocol = toggle; + break; case 'y': /* --max-filesize */ { curl_off_t value; diff --git a/src/tool_help.c b/src/tool_help.c index 9796b7e87356b5..4bd65269a975b0 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -164,6 +164,8 @@ static const struct helptxt helptext[] = { "How long to wait in milliseconds for IPv6 before trying IPv4"}, {"-I, --head", "Show document info only"}, + {" --haproxy-protocol", + "Send HAProxy PROXY protocol header"}, {"-H, --header
", "Pass custom header(s) to server"}, {"-h, --help", diff --git a/src/tool_operate.c b/src/tool_operate.c index e8b434a534f6e6..a1bfce74f263b9 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1297,6 +1297,9 @@ static CURLcode operate_do(struct GlobalConfig *global, (long)config->localportrange); } + my_setopt(curl, CURLOPT_HAPROXYPROTOCOL, + config->haproxy_protocol?1L:0L); + /* curl 7.15.5 */ my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, config->ftp_alternative_to_user); diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 55abec873816a6..dc2170caae8dcc 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -161,7 +161,7 @@ test1424 test1425 test1426 test1427 \ test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 \ test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 \ test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 \ -test1452 test1453 test1454 \ +test1452 test1453 test1454 test1455 test1456 \ test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ test1516 test1517 \ diff --git a/tests/data/test1455 b/tests/data/test1455 new file mode 100644 index 00000000000000..7768a1f899405c --- /dev/null +++ b/tests/data/test1455 @@ -0,0 +1,56 @@ + + + +HTTP +HTTP GET + + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Thu, 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 + + +http://%HOSTIP:%HTTPPORT/1455 --haproxy-protocol --local-port 37756 + + + +# +# Verify data after the test has been "shot" + + +^User-Agent:.* + + +PROXY TCP4 %CLIENTIP %HOSTIP 37756 %HTTPPORT +GET /1455 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + diff --git a/tests/data/test1456 b/tests/data/test1456 new file mode 100644 index 00000000000000..07a6e7c03a81fb --- /dev/null +++ b/tests/data/test1456 @@ -0,0 +1,59 @@ + + + +HTTP +HTTP GET +IPv6 + + +# +# Server-side + + +HTTP/1.1 200 OK +Date: Thu, 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: yesyes + +-foo- + + + +# +# Client-side + + +ipv6 + + +http-ipv6 + + +HTTP-IPv6 GET with PROXY protocol + + +-g "http://%HOST6IP:%HTTP6PORT/1456" --local-port 44444 --haproxy-protocol + + + +# +# Verify data after the test has been "shot" + + +^User-Agent: + + +PROXY TCP6 ::1 ::1 44444 %HTTP6PORT +GET /1456 HTTP/1.1 +Host: %HOST6IP:%HTTP6PORT +Accept: */* + + + + From 27ba650804d296c26195f27d405cfa137205e02d Mon Sep 17 00:00:00 2001 From: Lawrence Matthews Date: Mon, 20 Feb 2017 08:34:49 -0800 Subject: [PATCH 2/6] Fixes source issues per PR comments --- lib/http.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/http.c b/lib/http.c index df2fbc01cd204b..7bda243c5847f8 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1389,7 +1389,8 @@ static int http_getsock_do(struct connectdata *conn, return GETSOCK_WRITESOCK(0); } -static void add_haproxy_protocol_header(struct connectdata *conn, bool *done) +static CURLcode add_haproxy_protocol_header(struct connectdata *conn, + bool *done) { char proxy_header[128]; Curl_send_buffer *req_buffer; @@ -1397,9 +1398,10 @@ static void add_haproxy_protocol_header(struct connectdata *conn, bool *done) char tcp_version[5]; /* Emit the correct prefix for IPv6 */ - if (conn->bits.ipv6) { + if(conn->bits.ipv6) { strcpy(tcp_version, "TCP6"); - } else { + } + else { strcpy(tcp_version, "TCP4"); } @@ -1418,6 +1420,8 @@ static void add_haproxy_protocol_header(struct connectdata *conn, bool *done) &conn->data->info.request_size, 0, FIRSTSOCKET); + + return result; } #ifdef USE_SSL From 2d932e4959d38fda41755a35e7c392f8d69bbdc4 Mon Sep 17 00:00:00 2001 From: Eli Young Date: Wed, 6 Dec 2017 12:39:22 -0800 Subject: [PATCH 3/6] lib: add missing error checks to HAProxy protocol --- lib/http.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/http.c b/lib/http.c index 7bda243c5847f8..cc2ad410b5a46f 100644 --- a/lib/http.c +++ b/lib/http.c @@ -92,7 +92,8 @@ static int http_getsock_do(struct connectdata *conn, int numsocks); static int http_should_fail(struct connectdata *conn); -static void add_haproxy_protocol_header(struct connectdata *conn, bool *done); +static CURLcode add_haproxy_protocol_header(struct connectdata *conn, + bool *done); #ifdef USE_SSL static CURLcode https_connecting(struct connectdata *conn, bool *done); @@ -1361,7 +1362,9 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) if(conn->data->set.haproxyprotocol) { /* add HAProxy PROXY protocol header */ - add_haproxy_protocol_header(conn, done); + result = add_haproxy_protocol_header(conn, done); + if(result) + return result; } if(conn->given->protocol & CURLPROTO_HTTPS) { @@ -1413,8 +1416,15 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn, conn->data->info.conn_primary_ip, conn->data->info.conn_local_port, conn->data->info.conn_primary_port); + req_buffer = Curl_add_buffer_init(); - Curl_add_bufferf(req_buffer, proxy_header); + if(!req_buffer) + return CURLE_OUT_OF_MEMORY; + + result = Curl_add_bufferf(req_buffer, proxy_header); + if(result) + return result; + result = Curl_add_buffer_send(req_buffer, conn, &conn->data->info.request_size, From a5543df0e808ba6c3d9ee687761b4472b9a82571 Mon Sep 17 00:00:00 2001 From: Eli Young Date: Wed, 6 Dec 2017 13:03:22 -0800 Subject: [PATCH 4/6] lib: remove done arg from HAProxy protocol handler This argument is unnecessary here. --- lib/http.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/http.c b/lib/http.c index cc2ad410b5a46f..c1b78b5837c0bb 100644 --- a/lib/http.c +++ b/lib/http.c @@ -92,8 +92,7 @@ static int http_getsock_do(struct connectdata *conn, int numsocks); static int http_should_fail(struct connectdata *conn); -static CURLcode add_haproxy_protocol_header(struct connectdata *conn, - bool *done); +static CURLcode add_haproxy_protocol_header(struct connectdata *conn); #ifdef USE_SSL static CURLcode https_connecting(struct connectdata *conn, bool *done); @@ -1362,7 +1361,7 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) if(conn->data->set.haproxyprotocol) { /* add HAProxy PROXY protocol header */ - result = add_haproxy_protocol_header(conn, done); + result = add_haproxy_protocol_header(conn); if(result) return result; } @@ -1392,8 +1391,7 @@ static int http_getsock_do(struct connectdata *conn, return GETSOCK_WRITESOCK(0); } -static CURLcode add_haproxy_protocol_header(struct connectdata *conn, - bool *done) +static CURLcode add_haproxy_protocol_header(struct connectdata *conn) { char proxy_header[128]; Curl_send_buffer *req_buffer; From ac178572e984bbeb94ae52aad16b4b8ae4d7d4e7 Mon Sep 17 00:00:00 2001 From: Eli Young Date: Wed, 6 Dec 2017 14:39:01 -0800 Subject: [PATCH 5/6] curl: only set CURLOPT_HAPROXYPROTOCOL if enabled --- src/tool_operate.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tool_operate.c b/src/tool_operate.c index a1bfce74f263b9..500b37bee31f5d 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1297,9 +1297,6 @@ static CURLcode operate_do(struct GlobalConfig *global, (long)config->localportrange); } - my_setopt(curl, CURLOPT_HAPROXYPROTOCOL, - config->haproxy_protocol?1L:0L); - /* curl 7.15.5 */ my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, config->ftp_alternative_to_user); @@ -1448,6 +1445,10 @@ static CURLcode operate_do(struct GlobalConfig *global, my_setopt(curl, CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, config->happy_eyeballs_timeout_ms); + /* new in 7.60.0 */ + if(config->haproxy_protocol) + my_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L); + /* initialize retry vars for loop below */ retry_sleep_default = (config->retry_delay) ? config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ From 1e4edfdb58d143a01c6b8ba45d14132d691df735 Mon Sep 17 00:00:00 2001 From: Eli Young Date: Mon, 5 Feb 2018 14:45:13 -0800 Subject: [PATCH 6/6] Document HAProxy protocol symbols and CLI flag --- docs/cmdline-opts/haproxy-protocol.d | 11 ++++ docs/libcurl/curl_easy_setopt.3 | 2 + docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3 | 57 +++++++++++++++++++++ docs/libcurl/opts/Makefile.inc | 1 + docs/libcurl/symbols-in-versions | 1 + 5 files changed, 72 insertions(+) create mode 100644 docs/cmdline-opts/haproxy-protocol.d create mode 100644 docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3 diff --git a/docs/cmdline-opts/haproxy-protocol.d b/docs/cmdline-opts/haproxy-protocol.d new file mode 100644 index 00000000000000..52e156058e288b --- /dev/null +++ b/docs/cmdline-opts/haproxy-protocol.d @@ -0,0 +1,11 @@ +Long: haproxy-protocol +Help: Send HAProxy PROXY protocol header +Protocols: HTTP +Added: 7.60.0 +--- +Send a HAProxy PROXY protocol header at the beginning of the connection. This +is used by some load balancers and reverse proxies to indicate the client's +true IP address and port. + +This option is primarily useful when sending test requests to a service that +expects this header. diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 1efb467e6424ce..b7d67f360196d5 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -185,6 +185,8 @@ Socks5 GSSAPI service name. \fICURLOPT_SOCKS5_GSSAPI_SERVICE(3)\fP Socks5 GSSAPI NEC mode. See \fICURLOPT_SOCKS5_GSSAPI_NEC(3)\fP .IP CURLOPT_PROXY_SERVICE_NAME Proxy authentication service name. \fICURLOPT_PROXY_SERVICE_NAME(3)\fP +.IP CURLOPT_HAPROXYPROTOCOL +Send an HAProxy PROXY protocol header. See \fICURLOPT_HAPROXYPROTOCOL(3)\fP .IP CURLOPT_SERVICE_NAME Authentication service name. \fICURLOPT_SERVICE_NAME(3)\fP .IP CURLOPT_INTERFACE diff --git a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3 b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3 new file mode 100644 index 00000000000000..01e667d163c46b --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.3 @@ -0,0 +1,57 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at https://curl.haxx.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" ************************************************************************** +.\" +.TH CURLOPT_HAPROXYPROTOCOL 3 "5 Feb 2018" "libcurl 7.60.0" "curl_easy_setopt options" +.SH NAME +CURLOPT_HAPROXYPROTOCOL \- send HAProxy PROXY protocol header +.SH SYNOPSIS +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HAPROXYPROTOCOL, + long haproxy_protocol); +.SH DESCRIPTION +A long parameter set to 1 tells the library to send an HAProxy PROXY +protocol header at beginning of the connection. The default action is not to +send this header. + +This option is primarily useful when sending test requests to a service that +expects this header. + +Most applications do not need this option. +.SH DEFAULT +0, do not send HAProxy PROXY protocol header +.SH PROTOCOLS +HTTP +.SH EXAMPLE +.nf +CURL *curl = curl_easy_init(); +if(curl) { + CURLcode ret; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); + curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L); + ret = curl_easy_perform(curl); +} +.fi +.SH AVAILABILITY +Along with HTTP. Added in 7.60.0. +.SH RETURN VALUE +Returns CURLE_OK if HTTP is enabled, and CURLE_UNKNOWN_OPTION if not. diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 2aa1acf33f77b7..b370082d609563 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -137,6 +137,7 @@ man_MANS = \ CURLOPT_FTP_USE_PRET.3 \ CURLOPT_GSSAPI_DELEGATION.3 \ CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 \ + CURLOPT_HAPROXYPROTOCOL.3 \ CURLOPT_HEADER.3 \ CURLOPT_HEADERDATA.3 \ CURLOPT_HEADERFUNCTION.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index c58086fb7ad0a0..2877de7f1d5ab8 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -404,6 +404,7 @@ CURLOPT_FTP_USE_EPSV 7.9.2 CURLOPT_FTP_USE_PRET 7.20.0 CURLOPT_GSSAPI_DELEGATION 7.22.0 CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS 7.59.0 +CURLOPT_HAPROXYPROTOCOL 7.60.0 CURLOPT_HEADER 7.1 CURLOPT_HEADERDATA 7.10 CURLOPT_HEADERFUNCTION 7.7.2