Hi everyone,

My colleague Christian Norbert Menges (in CC) has written a patch that I now 
polished and made ready for submission.
It adds a converter function to convert bytes, that are assumed to be saved in 
little endian, into decimal representation, similarly to the existing be2dec 
sample fetch.
This is necessary since Azure saves their PPv2 TLVs in little endian, see 
https://learn.microsoft.com/en-us/azure/private-link/private-link-service-overview.
This way, we can write something like `fc_pp_tlv(0xee),bytes(1,4),le2dec(,4)` 
to read the TLV.
There are some formatting issues with commas, but the found lines are 
consistent with existing code, so it should be fine.

Please let me know what you think. We’re not aware of any other way of 
achieving this.

Best regards,
Alexander


From 8a81f15041dc90e2c7f171d32236a95c9d092b97 Mon Sep 17 00:00:00 2001
From: Alexander Stephan 
alexander.step...@sap.com<mailto:alexander.step...@sap.com>
Date: Fri, 14 Mar 2025 11:17:09 +0000
Subject: [PATCH] MINOR: sample: Add le2dec (little endian to decimal) sample
fetch

This commit introduces a sample fetch, `le2dec`, to convert
little-endian binary input samples into their decimal representations.
The function converts the input into a string containing unsigned
integer numbers, with each number derived from a specified number of
input bytes. The numbers are separated using a user-defined separator.

This new sample is achieved by adding a parametrized sample_conv_2dec
function, unifying the logic for be2dec and le2dec converters.

Co-authored-by: Christian Norbert Menges 
christian.norbert.men...@sap.com<mailto:christian.norbert.men...@sap.com>
---
doc/configuration.txt          | 14 +++++++++
reg-tests/converter/le2dec.vtc | 56 ++++++++++++++++++++++++++++++++++
src/sample.c                   | 41 ++++++++++++++++++++-----
3 files changed, 104 insertions(+), 7 deletions(-)
create mode 100644 reg-tests/converter/le2dec.vtc

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 2404544c3..cf8639818 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -20264,6 +20264,7 @@ and(value)                                         
integer      integer
b64dec                                             string       binary
base64                                             binary       string
be2dec(separator,chunk_size[,truncate])            binary       string
+le2dec(separator,chunk_size[,truncate])            binary       string
be2hex([separator[,chunk_size[,truncate]]])        binary       string
bool                                               integer      boolean
bytes(offset[,length])                             binary       binary
@@ -20504,6 +20505,19 @@ be2dec(<separator>,<chunk_size>[,<truncate>])
       bin(01020304050607),be2dec(,2,1)  # 2587721286
       bin(7f000001),be2dec(.,1)         # 127.0.0.1

+le2dec(<separator>,<chunk_size>[,<truncate>])
+  Converts little-endian binary input sample to a string containing an unsigned
+  integer number per <chunk_size> input bytes. <separator> is inserted every
+  <chunk_size> binary input bytes if specified. The <truncate> flag indicates
+  whether the binary input is truncated at <chunk_size> boundaries. The maximum
+  value for <chunk_size> is limited by the size of long long int (8 bytes).
+
+  Example:
+      bin(01020304050607),le2dec(:,2)   # 513:1284:2055:7
+      bin(01020304050607),le2dec(-,2,1) # 513-1284-2055
+      bin(01020304050607),le2dec(,2,1)  # 51312842055
+      bin(7f000001),le2dec(.,1)         # 127.0.0.1
+
be2hex([<separator>[,<chunk_size>[,<truncate>]]])
   Converts big-endian binary input sample to a hex string containing two hex
   digits per input byte. It is used to log or transfer hex dumps of some
diff --git a/reg-tests/converter/le2dec.vtc b/reg-tests/converter/le2dec.vtc
new file mode 100644
index 000000000..327b91253
--- /dev/null
+++ b/reg-tests/converter/le2dec.vtc
@@ -0,0 +1,56 @@
+varnishtest "le2dec converter Test"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(3.0-dev0)'"
+feature ignore_unknown_macro
+
+server s1 {
+       rxreq
+       txresp -hdr "Connection: close"
+} -repeat 3 -start
+
+haproxy h1 -conf {
+    defaults
+       mode http
+       timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+       timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+       timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    frontend fe
+       bind "fd@${fe}"
+
+       #### requests
+       http-request  set-var(txn.input) req.hdr(input)
+
+       http-response set-header le2dec-1   "%[var(txn.input),le2dec(:,1)]"
+       http-response set-header le2dec-2   "%[var(txn.input),le2dec(-,3)]"
+       http-response set-header le2dec-3   "%[var(txn.input),le2dec(::,3,1)]"
+
+       default_backend be
+
+    backend be
+       server s1 ${s1_addr}:${s1_port}
+} -start
+
+client c1 -connect ${h1_fe_sock} {
+       txreq -url "/" \
+         -hdr "input:"
+       rxresp
+       expect resp.status == 200
+       expect resp.http.le2dec-1 == ""
+       expect resp.http.le2dec-2 == ""
+       expect resp.http.le2dec-3 == ""
+       txreq -url "/" \
+         -hdr "input: 0123456789"
+       rxresp
+       expect resp.status == 200
+       expect resp.http.le2dec-1 == "48:49:50:51:52:53:54:55:56:57"
+       expect resp.http.le2dec-2 == "3289392-3486771-3684150-57"
+       expect resp.http.le2dec-3 == "3289392::3486771::3684150"
+       txreq -url "/" \
+         -hdr "input: abcdefghijklmnopqrstuvwxyz"
+       rxresp
+       expect resp.status == 200
+       expect resp.http.le2dec-1 == 
"97:98:99:100:101:102:103:104:105:106:107:108:109:110:111:112:113:114:115:116:117:118:119:120:121:122"
+       expect resp.http.le2dec-2 == 
"6513249-6710628-6908007-7105386-7302765-7500144-7697523-7894902-31353"
+       expect resp.http.le2dec-3 == 
"6513249::6710628::6908007::7105386::7302765::7500144::7697523::7894902"
+} -run
diff --git a/src/sample.c b/src/sample.c
index ab44bfecc..f4522769a 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1983,7 +1983,7 @@ int sample_conv_var2smp_str(const struct arg *arg, struct 
sample *smp)
        }
}

-static int sample_conv_be2dec_check(struct arg *args, struct sample_conv *conv,
+static int sample_conv_2dec_check(struct arg *args, struct sample_conv *conv,
                                     const char *file, int line, char **err)
{
        if (args[1].data.sint <= 0 || args[1].data.sint > sizeof(unsigned long 
long)) {
@@ -1999,13 +1999,13 @@ static int sample_conv_be2dec_check(struct arg *args, 
struct sample_conv *conv,
        return 1;
}

-/* Converts big-endian binary input sample to a string containing an unsigned
+/* Converts big-endian/little-endian binary input sample to a string 
containing an unsigned
  * integer number per <chunk_size> input bytes separated with <separator>.
  * Optional <truncate> flag indicates if input is truncated at <chunk_size>
  * boundaries.
- * Arguments: separator (string), chunk_size (integer), truncate (0,1)
+ * Arguments: separator (string), chunk_size (integer), truncate (0,1), big 
endian (0,1)
  */
-static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void 
*private)
+static int sample_conv_2dec(const struct arg *args, struct sample *smp, void 
*private, int be)
{
        struct buffer *trash = get_trash_chunk();
        const int last = args[2].data.sint ? smp->data.u.str.data - 
args[1].data.sint + 1 : smp->data.u.str.data;
@@ -2029,8 +2029,12 @@ static int sample_conv_be2dec(const struct arg *args, 
struct sample *smp, void *
                        max_size -= args[0].data.str.data;

                /* Add integer */
-               for (number = 0, i = 0; i < args[1].data.sint && ptr < 
smp->data.u.str.data; i++)
-                       number = (number << 8) + (unsigned 
char)smp->data.u.str.area[ptr++];
+               for (number = 0, i = 0; i < args[1].data.sint && ptr < 
smp->data.u.str.data; i++) {
+                       if (be)
+                               number = (number << 8) + (unsigned 
char)smp->data.u.str.area[ptr++];
+                       else
+                               number |= (unsigned 
char)smp->data.u.str.area[ptr++] << (i*8);
+               }

                pos = ulltoa(number, trash->area + trash->data, trash->size - 
trash->data);
                if (pos)
@@ -2047,6 +2051,28 @@ static int sample_conv_be2dec(const struct arg *args, 
struct sample *smp, void *
        return 1;
}

+/* Converts big-endian binary input sample to a string containing an unsigned
+ * integer number per <chunk_size> input bytes separated with <separator>.
+ * Optional <truncate> flag indicates if input is truncated at <chunk_size>
+ * boundaries.
+ * Arguments: separator (string), chunk_size (integer), truncate (0,1)
+ */
+static int sample_conv_be2dec(const struct arg *args, struct sample *smp, void 
*private)
+{
+       return sample_conv_2dec(args, smp, private, 1);
+}
+
+/* Converts little-endian binary input sample to a string containing an 
unsigned
+ * integer number per <chunk_size> input bytes separated with <separator>.
+ * Optional <truncate> flag indicates if input is truncated at <chunk_size>
+ * boundaries.
+ * Arguments: separator (string), chunk_size (integer), truncate (0,1)
+ */
+static int sample_conv_le2dec(const struct arg *args, struct sample *smp, void 
*private)
+{
+       return sample_conv_2dec(args, smp, private, 0);
+}
+
static int sample_conv_be2hex_check(struct arg *args, struct sample_conv *conv,
                                     const char *file, int line, char **err)
{
@@ -5372,7 +5398,8 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, 
{
        { "upper",   sample_conv_str2upper,    0,                     NULL,     
                SMP_T_STR,  SMP_T_STR  },
        { "lower",   sample_conv_str2lower,    0,                     NULL,     
                SMP_T_STR,  SMP_T_STR  },
        { "length",  sample_conv_length,       0,                     NULL,     
                SMP_T_STR,  SMP_T_SINT },
-       { "be2dec",  sample_conv_be2dec,       ARG3(1,STR,SINT,SINT), 
sample_conv_be2dec_check, SMP_T_BIN,  SMP_T_STR  },
+       { "be2dec",  sample_conv_be2dec,       ARG3(1,STR,SINT,SINT), 
sample_conv_2dec_check,   SMP_T_BIN,  SMP_T_STR  },
+       { "le2dec",  sample_conv_le2dec,       ARG3(1,STR,SINT,SINT), 
sample_conv_2dec_check,   SMP_T_BIN,  SMP_T_STR  },
        { "be2hex",  sample_conv_be2hex,       ARG3(1,STR,SINT,SINT), 
sample_conv_be2hex_check, SMP_T_BIN,  SMP_T_STR  },
        { "hex",     sample_conv_bin2hex,      0,                     NULL,     
                SMP_T_BIN,  SMP_T_STR  },
        { "hex2i",   sample_conv_hex2int,      0,                     NULL,     
                SMP_T_STR,  SMP_T_SINT },
--
2.35.3


Reply via email to