Example:
server srv1 192.168.1.1:80 send-proxy-v2 set-proxy-v2-tlv-fmt(0x20) %[fc_pp_tlv(0x20)]
- In this case, we fetch the TLV with the type 0x20 as a string and set as the value
+ In this case, we fetch the raw TLV with the type 0x20 and set it as the value
of a newly created TLV that also has the type 0x20.
proxy-v2-options <option>[,<option>]*
fc_lost integer
fc_nb_streams integer
fc_pp_authority string
-fc_pp_tlv(<id>) string
+fc_pp_tlv(<id>) binary
fc_pp_unique_id string
fc_rcvd_proxy boolean
fc_reordering integer
fc_pp_authority : string
Returns the first authority TLV sent by the client in the PROXY protocol
- header, if any.
+ header, if any. If the value contains a NUL byte, it is considered malformed
+ and no data is returned.
-fc_pp_tlv(<id>) : string
- Returns the TLV value for the given TLV ID. The ID must either be a numeric
+fc_pp_tlv(<id>) : binary
+ Returns the raw TLV value for the given TLV ID, which may contain NUL
+ bytes. Use "-m str" or a converter to match it as a string, in which case
+ the value ends at the first NUL byte. The ID must either be a numeric
value between 0 and 255 or one of the following supported symbolic names
that correspond to the TLV constant suffixes in the PPv2 spec:
"ALPN": PP2_TYPE_ALPN, "AUTHORITY": PP2_TYPE_AUTHORITY,
fc_pp_unique_id : string
Returns the first unique ID TLV sent by the client in the PROXY protocol
- header, if any.
+ header, if any. If the value contains a NUL byte, it is considered malformed
+ and no data is returned.
fc_rcvd_proxy : boolean
Returns true if the client initiated the connection with a PROXY protocol
--- /dev/null
+varnishtest "fc_pp_tlv on the SSL TLV sent by send-proxy-v2-ssl"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
+feature ignore_unknown_macro
+
+# The SSL TLV (0x20) sent by send-proxy-v2-ssl is binary: its <client> and
+# <verify> fields hold \0 bytes when the client connection is not TLS.
+# fc_pp_tlv(0x20) must return all its bytes, so that it can be converted
+# with hex or relayed.
+
+haproxy h1 -conf {
+ global
+ .if feature(THREAD)
+ thread-groups 1
+ .endif
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ listen sender
+ bind "fd@${feS}"
+ server r ${h1_feR_addr}:${h1_feR_port} send-proxy-v2-ssl set-proxy-v2-tlv-fmt(0xE1) %[str("text")]
+
+ listen receiver
+ bind "fd@${feR}" accept-proxy
+ http-request return status 200 hdr ssl-tlv "%[fc_pp_tlv(0x20),hex]" hdr custom-tlv "%[fc_pp_tlv(0xE1)]"
+} -start
+
+client c1 -connect ${h1_feS_sock} {
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.custom-tlv == "text"
+ # <client> = 0x00 (no TLS), <verify> = 0x00000000
+ expect resp.http.ssl-tlv == "0000000000"
+} -run
--- /dev/null
+varnishtest "PROXY v2 TLV fetches and values holding a \0 byte"
+feature ignore_unknown_macro
+
+# A string sample cannot contain a \0 byte. TLV values are copied as is
+# from the PROXY v2 header, so the sender can put a \0 in them. The text
+# fetches fc_pp_authority and fc_pp_unique_id must return nothing for such
+# a value instead of being matched by "-m str" on its prefix. fc_pp_tlv
+# returns the raw bytes and must keep the \0: the SSL TLV (0x20) sent by
+# send-proxy-v2-ssl holds \0 bytes in its <client> and <verify> fields, and
+# must still be readable.
+
+haproxy h1 -conf {
+ global
+ .if feature(THREAD)
+ thread-groups 1
+ .endif
+
+ defaults
+ mode http
+ timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout client "${HAPROXY_TEST_TIMEOUT-5s}"
+ timeout server "${HAPROXY_TEST_TIMEOUT-5s}"
+
+ frontend fe1
+ bind "fd@${fe1}" accept-proxy
+ http-request set-var-fmt(txn.auth) match if { fc_pp_authority -m str public }
+ http-request set-var-fmt(txn.uid) match if { fc_pp_unique_id -m str public }
+ http-request return status 200 hdr authority %[var(txn.auth,nomatch)] hdr unique-id %[var(txn.uid,nomatch)] hdr tlv %[fc_pp_tlv(0xE1),hex] hdr ssl-tlv %[fc_pp_tlv(0x20),hex]
+} -start
+
+# Header layout:
+# 0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a PROXY v2 signature
+# 21 11 version 2, PROXY, TCP4
+# 00 LL length
+# 7f000001 7f000001 002a 0539 127.0.0.1:42 -> 127.0.0.1:1337
+# 02 00 LL <value> PP2_TYPE_AUTHORITY
+# 05 00 LL <value> PP2_TYPE_UNIQUE_ID
+# e1 00 LL <value> custom TLV
+# 20 00 05 00 00000000 PP2_TYPE_SSL: <client> = 0, <verify> = 0
+
+# all values "public", plus the SSL TLV
+client c1 -connect ${h1_fe1_sock} {
+ sendhex "0d0a0d0a000d0a515549540a 2111 002f 7f0000017f000001002a0539 020006 7075626c6963 050006 7075626c6963 e10006 7075626c6963 200005 0000000000"
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.authority == "match"
+ expect resp.http.unique-id == "match"
+ expect resp.http.tlv == "7075626C6963"
+ # a binary TLV keeps its \0 bytes
+ expect resp.http.ssl-tlv == "0000000000"
+} -run
+
+# all values "public\0x"
+client c2 -connect ${h1_fe1_sock} {
+ sendhex "0d0a0d0a000d0a515549540a 2111 002d 7f0000017f000001002a0539 020008 7075626c69630078 050008 7075626c69630078 e10008 7075626c69630078"
+ txreq -url "/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.authority == "nomatch"
+ expect resp.http.unique-id == "nomatch"
+ # a raw TLV keeps its \0 byte
+ expect resp.http.tlv == "7075626C69630078"
+} -run
list_for_each_entry_from(conn_tlv, &conn->tlv_list, list) {
if (conn_tlv->type == idx) {
smp->flags |= SMP_F_NOT_LAST;
- smp->data.type = SMP_T_STR;
+ /* a TLV value is a raw byte sequence */
+ smp->data.type = SMP_T_BIN;
smp->data.u.str.area = conn_tlv->value;
smp->data.u.str.data = conn_tlv->len;
smp->ctx.p = conn_tlv;
return 0;
}
-/* fetch the authority TLV from a PROXY protocol header */
-int smp_fetch_fc_pp_authority(const struct arg *args, struct sample *smp, const char *kw, void *private)
+/* Returns the first TLV of type <type> as a string. Fails if the value
+ * holds a NUL byte, since a string sample cannot contain one.
+ */
+static int smp_fetch_fc_pp_tlv_str(int type, struct sample *smp, const char *kw, void *private)
{
struct arg tlv_arg;
int ret;
- set_tlv_arg(PP2_TYPE_AUTHORITY, &tlv_arg);
+ set_tlv_arg(type, &tlv_arg);
ret = smp_fetch_fc_pp_tlv(&tlv_arg, smp, kw, private);
- smp->flags &= ~SMP_F_NOT_LAST; // return only the first authority
- return ret;
+ smp->flags &= ~SMP_F_NOT_LAST; // return only the first one
+ if (!ret)
+ return 0;
+
+ if (memchr(smp->data.u.str.area, 0, smp->data.u.str.data))
+ return 0;
+
+ smp->data.type = SMP_T_STR;
+ return 1;
+}
+
+/* fetch the authority TLV from a PROXY protocol header */
+int smp_fetch_fc_pp_authority(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+ return smp_fetch_fc_pp_tlv_str(PP2_TYPE_AUTHORITY, smp, kw, private);
}
/* fetch the unique ID TLV from a PROXY protocol header */
int smp_fetch_fc_pp_unique_id(const struct arg *args, struct sample *smp, const char *kw, void *private)
{
- struct arg tlv_arg;
- int ret;
-
- set_tlv_arg(PP2_TYPE_UNIQUE_ID, &tlv_arg);
- ret = smp_fetch_fc_pp_tlv(&tlv_arg, smp, kw, private);
- smp->flags &= ~SMP_F_NOT_LAST; // return only the first unique ID
- return ret;
+ return smp_fetch_fc_pp_tlv_str(PP2_TYPE_UNIQUE_ID, smp, kw, private);
}
/* fetch the error code of a connection */
{ "fc_nb_streams", smp_fetch_fc_nb_streams, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
{ "fc_pp_authority", smp_fetch_fc_pp_authority, 0, NULL, SMP_T_STR, SMP_USE_L4CLI },
{ "fc_pp_unique_id", smp_fetch_fc_pp_unique_id, 0, NULL, SMP_T_STR, SMP_USE_L4CLI },
- { "fc_pp_tlv", smp_fetch_fc_pp_tlv, ARG1(1, STR), smp_check_tlv_type, SMP_T_STR, SMP_USE_L5CLI },
+ { "fc_pp_tlv", smp_fetch_fc_pp_tlv, ARG1(1, STR), smp_check_tlv_type, SMP_T_BIN, SMP_USE_L5CLI },
{ "fc_settings_streams_limit", smp_fetch_fc_streams_limit, 0, NULL, SMP_T_SINT, SMP_USE_L5CLI },
{ /* END */ },
}};