Add new sample fetches to get the ciphers, supported groups, key shares and 
signature algorithms
that the client supports during a TLS handshake as part of the contents of a 
TLS ClientHello.
Currently we can get the following contents of the ClientHello message: 
SNI(req_ssl_sni) and
TLS protocol version(req_ssl_ver). The following new sample fetches will be 
added to get the
following contents of the ClientHello message exchanged during the TLS 
handshake (supported by
the client):
- req.ssl_cipherlist: Returns the binary form of the list of symmetric cipher 
options
- req.ssl_sigalgs: Returns the binary form of the list of signature algorithms
- req.ssl_keyshare_groups: Return the binary format of the list of key share 
group
- req.ssl_supported_groups: Returns the binary form of the list of supported 
groups used in key exchange

This added functionality would allow routing with fine granularity pending the 
capabilities the client
indicates in the ClientHello. For example, this would allow the ability to 
enable TLS passthrough or
TLS termination based on the supported groups detected in the ClientHello 
message. Another usage is to
take client key shares into consideration when deciding which of the client 
supported groups should be
used for groups considered to have 'equal security level' as well as enabling 
fine grain selection of
certificate types(beyond the RSA vs ECC distinction). All of that is relevant 
in the context of rapidly
upcoming PQC operation modes.

Fixes: #2532
---
 doc/configuration.txt                       |  66 ++
 reg-tests/checks/tcp-check-client-hello.vtc |  82 +++
 src/payload.c                               | 697 ++++++++++----------
 3 files changed, 479 insertions(+), 366 deletions(-)
 create mode 100644 reg-tests/checks/tcp-check-client-hello.vtc

diff --git a/doc/configuration.txt b/doc/configuration.txt
index da9d8b540..ab39da283 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -25057,6 +25057,10 @@ req_ssl_sni                                           
string
 req.ssl_st_ext                                        integer
 req.ssl_ver                                           integer
 req_ssl_ver                                           integer
+req.ssl_cipherlist                                    binary
+req.ssl_sigalgs                                       binary
+req.ssl_keyshare_groups                               binary
+req.ssl_supported_groups                              binary
 res.len                                               integer
 res.payload(<offset>,<length>)                        binary
 res.payload_lv(<offset1>,<length>[,<offset2>])        binary
@@ -25261,6 +25265,68 @@ req_ssl_sni : string (deprecated)
      use_backend bk_allow if { req.ssl_sni -f allowed_sites }
      default_backend bk_sorry_page
 
+req.ssl_cipherlist binary
+  Returns the binary form of the list of symmetric cipher options supported by
+  the client as reported in the contents of a TLS ClientHello. Note that this
+  only applies to raw contents found in the request buffer and not to contents
+  deciphered via an SSL data layer, so this will not work with "bind" lines
+  having the "ssl" option. Refer to "ssl_fc_cipherlist_bin" which is the SSL
+  bind equivalent that can be used when the "ssl" option is specified.
+
+  Examples :
+     # Wait for a client hello for at most 5 seconds
+     tcp-request inspect-delay 5s
+     tcp-request content accept if { req.ssl_hello_type 1 }
+     use-server fe3 if { req.ssl_cipherlist,be2hex(:,2),lower -m sub 1302:009f 
}
+     server fe3  ${htst_fe3_addr}:${htst_fe3_port}
+
+req.ssl_sigalgs binary
+  Returns the binary form of the list of signature algorithms supported by the
+  client as reported in the TLS ClientHello. This is available as a client 
hello
+  extension. Note that this only applies to raw contents found in the request
+  buffer and not to contents deciphered via an SSL data layer, so this will not
+  work with "bind" lines having the "ssl" option. Refer to "ssl_fc_sigalgs_bin"
+  which is the SSL bind equivalent that can be used when the "ssl" option is
+  specified.
+
+  Examples :
+     # Wait for a client hello for at most 5 seconds
+     tcp-request inspect-delay 5s
+     tcp-request content accept if { req.ssl_hello_type 1 }
+     use-server fe4 if { req.ssl_sigalgs,be2hex(:,2),lower -m sub 0403:0805 }
+     server fe4  ${htst_fe4_addr}:${htst_fe4_port}
+
+req.ssl_keyshare_groups binary
+  Return the binary format of the list of cryptographic parameters for key 
exchange
+  supported by the client as reported in the TLS ClientHello. In TLS v1.3, 
keyshare
+  is part of the ClientHello message and is the final client hello extension. 
Note
+  that this only applies to raw contents found in the request buffer and not to
+  contents deciphered via an  SSL data layer, so this will not work with "bind"
+  lines having the "ssl" option.
+
+  Examples :
+     # Wait for a client hello for at most 5 seconds
+     tcp-request inspect-delay 5s
+     tcp-request content accept if { req.ssl_hello_type 1 }
+     use-server fe3 if { req.ssl_keyshare_groups,be2hex(:,2),lower -m sub 001d 
 }
+     server fe3  ${htst_fe3_addr}:${htst_fe3_port}
+
+req.ssl_supported_groups binary
+  Returns the binary form of the list of supported groups supported by the 
client
+  as reported in the TLS ClientHello and used for key exchange which can 
include
+  both elliptic curve and non-EC key exchange. Note that this only applies to 
raw
+  contents found in the request buffer and not to contents deciphered via an 
SSL
+  data layer, so this will not  work with "bind" lines having the "ssl" option.
+  Refer to "ssl_fc_eclist_bin" hich is the SSL bind equivalent that can be used
+  when the "ssl" option is specified.
+
+  Examples :
+     # Wait for a client hello for at most 5 seconds
+     tcp-request inspect-delay 5s
+     tcp-request content accept if { req.ssl_hello_type 1 }
+     use-server fe3 if { req.ssl_supported_groups, be2hex(:,2),lower -m sub 
0017 }
+     server fe3  ${htst_fe3_addr}:${htst_fe3_port}
+
 req.ssl_st_ext : integer
   Returns 0 if the client didn't send a SessionTicket TLS Extension (RFC5077)
   Returns 1 if the client sent SessionTicket TLS Extension
diff --git a/reg-tests/checks/tcp-check-client-hello.vtc 
b/reg-tests/checks/tcp-check-client-hello.vtc
new file mode 100644
index 000000000..137361c65
--- /dev/null
+++ b/reg-tests/checks/tcp-check-client-hello.vtc
@@ -0,0 +1,82 @@
+#REQUIRE_OPTION=OPENSSL
+#REGTEST_TYPE=slow
+#EXCLUDE_TARGETS=osx,generic
+
+varnishtest "Health checks: test enhanced observability of TLS ClientHello"
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL) && 
!ssllib_name_startswith(wolfSSL) && !ssllib_name_startswith(LibreSSL) && 
openssl_version_atleast(1.1.1)'"
+feature ignore_unknown_macro
+
+syslog S_ok -level notice {
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv 
succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 
1/1 UP."
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv 
succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 
1/1 UP."
+    recv
+    expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be[0-9]+/srv 
succeeded, reason: Layer6 check passed.+check duration: [[:digit:]]+ms, status: 
1/1 UP."
+} -start
+
+haproxy htst -conf {
+    global
+        ssl-default-bind-options ssl-min-ver TLSv1.2 ssl-max-ver TLSv1.3
+
+    defaults
+        log global
+        option tcplog
+        timeout  client "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout  server "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout  connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    listen li1
+       mode tcp
+       bind "fd@${li1}"
+       tcp-request inspect-delay 100ms
+       
+       acl check_sig_algs req.ssl_sigalgs,be2hex(:,2),lower -m found
+       acl check_key_shares req.ssl_keyshare_groups,be2hex(:,2),lower -m found
+       tcp-request content accept if check_sig_algs
+       tcp-request content accept if check_key_shares
+       
+       # Refer to https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8 
&& https://tls13.xargs.org/#client-hello/annotated to get the binary values
+       use-server fe3 if { req.ssl_cipherlist,be2hex(:,2),lower -m sub 
1302:1303:1301:009f } || { req.ssl_supported_groups, be2hex(:,2),lower -m sub 
001d }
+       server fe3  ${htst_fe3_addr}:${htst_fe3_port}
+
+       use-server fe1 if { req.ssl_supported_groups, be2hex(:,2),lower -m sub 
0017 }
+       server fe1 ${htst_fe1_addr}:${htst_fe1_port}
+
+    frontend fe1
+        bind "fd@${fe1}" ssl crt ${testdir}/common.pem curves P-256:P-384
+
+    frontend fe3
+        bind "fd@${fe3}" ssl crt ${testdir}/common.pem
+} -start
+
+haproxy h1 -conf {
+    defaults
+        mode tcp
+        timeout  client "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout  server "${HAPROXY_TEST_TIMEOUT-5s}"
+        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+
+    backend be1
+        mode tcp
+        log ${S_ok_addr}:${S_ok_port} daemon
+        option log-health-checks
+        option tcp-check
+        server srv ${htst_li1_addr}:${htst_li1_port}  check inter 1s rise 1 
fall 1 check-ssl verify none curves X25519
+
+    backend be2
+        mode tcp
+        log ${S_ok_addr}:${S_ok_port} daemon
+        option log-health-checks
+        option tcp-check
+        server srv ${htst_li1_addr}:${htst_li1_port}  check inter 1s rise 1 
fall 1 check-ssl verify none curves P-256:P-384
+
+    backend be3
+        mode tcp
+        log ${S_ok_addr}:${S_ok_port} daemon
+        option log-health-checks
+        option tcp-check
+        server srv ${htst_li1_addr}:${htst_li1_port}  check inter 1s rise 1 
fall 1 check-ssl verify none ciphers DHE-RSA-AES256-GCM-SHA384
+} -start
+
+syslog S_ok -wait
diff --git a/src/payload.c b/src/payload.c
index 6a536d719..bb1970c8b 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -31,6 +31,19 @@
 /*       All supported sample fetch functions must be declared here     */
 /************************************************************************/
 
+int NOT_SSL_HELLO = 1;
+int TOO_SHORT = 2;
+
+/**
+ * struct to hold variables used during generic client hello parsing
+ */
+struct clnt_hello_proc {
+                 int hs_len;
+                 int ext_len;
+                 int status;
+          unsigned char *data;
+};
+
 /* wait for more data as long as possible, then return TRUE. This should be
  * used with content inspection.
  */
@@ -75,18 +88,44 @@ smp_fetch_len(const struct arg *args, struct sample *smp, 
const char *kw, void *
        return 1;
 }
 
-/* Returns 0 if the client didn't send a SessionTicket Extension
- * Returns 1 if the client sent SessionTicket Extension
- * Returns 2 if the client also sent non-zero length SessionTicket
- * Returns SMP_T_SINT data type
+/* Extract information presented in a TLS client hello handshake message.
+ * The format of the message is the following (cf RFC5246 + RFC6066) :
+ * TLS frame :
+ *   - uint8  type                            = 0x16   (Handshake)
+ *   - uint16 version                        >= 0x0301 (TLSv1)
+ *   - uint16 length                                   (frame length)
+ *   - TLS handshake :
+ *     - uint8  msg_type                      = 0x01   (ClientHello)
+ *     - uint24 length                                 (handshake message 
length)
+ *     - ClientHello :
+ *       - uint16 client_version             >= 0x0301 (TLSv1)
+ *       - uint8 Random[32]                  (4 first ones are timestamp)
+ *       - SessionID :
+ *         - uint8 session_id_len (0..32)              (SessionID len in bytes)
+ *         - uint8 session_id[session_id_len]
+ *       - CipherSuite :
+ *         - uint16 cipher_len               >= 2      (Cipher length in bytes)
+ *         - uint16 ciphers[cipher_len/2]
+ *       - CompressionMethod :
+ *         - uint8 compression_len           >= 1      (# of supported methods)
+ *         - uint8 compression_methods[compression_len]
+ *       - optional client_extension_len               (in bytes)
+ *       - optional sequence of ClientHelloExtensions  (as many bytes as 
above):
+ *         - uint16 extension_type            = 0 for server_name
+ *         - uint16 extension_len
+ *         - opaque extension_data[extension_len]
+ *           - uint16 server_name_list_len             (# of bytes here)
+ *           - opaque server_names[server_name_list_len bytes]
+ *             - uint8 name_type              = 0 for host_name
+ *             - uint16 name_len
+ *             - opaque hostname[name_len bytes]
  */
-static int
-smp_fetch_req_ssl_st_ext(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
-{
-       int hs_len, ext_len, bleft;
+struct clnt_hello_proc smp_client_hello_parse( struct sample *smp, int 
processCompressionMethods ) {
+    int hs_len, ext_len, bleft, status=0;
        struct channel *chn;
+       struct clnt_hello_proc cp;
        unsigned char *data;
-
+ 
        if (!smp->strm)
                goto not_ssl_hello;
 
@@ -94,8 +133,7 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
        if (IS_HTX_STRM(smp->strm))
                goto not_ssl_hello;
 
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-
+    chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
 
        bleft = ci_data(chn);
        data = (unsigned char *)ci_head(chn);
@@ -103,7 +141,7 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
        /* Check for SSL/TLS Handshake */
        if (!bleft)
                goto too_short;
-       if (*data != 0x16)
+       if(*data != 0x16)
                goto not_ssl_hello;
 
        /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
@@ -114,10 +152,11 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
 
        if (bleft < 5)
                goto too_short;
+
        hs_len = (data[3] << 8) + data[4];
        if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
                goto not_ssl_hello; /* too short to have an extension */
-
+               
        data += 5; /* enter TLS handshake */
        bleft -= 5;
 
@@ -130,6 +169,7 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
        /* Check the Hello's length */
        if (bleft < 4)
                goto too_short;
+
        hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
        if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
                goto not_ssl_hello; /* too short to have an extension */
@@ -156,32 +196,64 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
            ext_len > hs_len)
                goto not_ssl_hello;
 
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
+       /* Jump to the compression methods. For fetching cipher list this 
processing is not required. */
+       if (processCompressionMethods == 1) {
+               hs_len -= 2 + ext_len;
+               data   += 2 + ext_len;
 
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
+               if (hs_len < 2 ||                       /* minimum one 
compression method */
            data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
+                       goto not_ssl_hello;
 
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
+               /* Jump to the extensions */
+               hs_len -= 1 + data[0];
+               data   += 1 + data[0];
 
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
+               if (hs_len < 2 ||                       /* minimum one 
extension list length */
            (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
+                       goto not_ssl_hello;
 
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2;
+               hs_len = ext_len; /* limit ourselves to the extension length */
+               data += 2;
+    }
+       cp.hs_len = hs_len;
+       cp.ext_len = ext_len;
+       cp.data = data;
+       cp.status = status;
+       return cp;
+    
+       not_ssl_hello:
+               cp.status = NOT_SSL_HELLO;
+       
+       too_short:
+               cp.status = TOO_SHORT;
+
+       return cp;
+}
 
-       while (hs_len >= 4) {
+/* Returns 0 if the client didn't send a SessionTicket Extension
+ * Returns 1 if the client sent SessionTicket Extension
+ * Returns 2 if the client also sent non-zero length SessionTicket
+ * Returns SMP_T_SINT data type
+ */
+static int
+smp_fetch_req_ssl_st_ext(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
+{
+       struct clnt_hello_proc cp;
+
+    cp = smp_client_hello_parse(smp, 1);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
+               goto too_short;
+
+       while (cp.hs_len >= 4) {
                int ext_type, ext_len;
 
-               ext_type = (data[0] << 8) + data[1];
-               ext_len  = (data[2] << 8) + data[3];
+               ext_type = (cp.data[0] << 8) + cp.data[1];
+               ext_len  = (cp.data[2] << 8) + cp.data[3];
 
-               if (ext_len > hs_len - 4) /* Extension too long */
+               if (ext_len > cp.hs_len - 4) /* Extension too long */
                        goto not_ssl_hello;
 
                /* SesstionTicket extension */
@@ -197,8 +269,8 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
                        return 1;
                }
 
-               hs_len -= 4 + ext_len;
-               data   += 4 + ext_len;
+               cp.hs_len -= 4 + ext_len;
+               cp.data   += 4 + ext_len;
        }
        /* SessionTicket Extension not found */
        smp->data.type = SMP_T_SINT;
@@ -219,103 +291,21 @@ smp_fetch_req_ssl_st_ext(const struct arg *args, struct 
sample *smp, const char
 static int
 smp_fetch_req_ssl_ec_ext(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
 {
-       int hs_len, ext_len, bleft;
-       struct channel *chn;
-       unsigned char *data;
+       struct clnt_hello_proc cp;
 
-       if (!smp->strm)
-               goto not_ssl_hello;
-
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
-
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
-
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
-
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
+    cp = smp_client_hello_parse(smp, 1);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
                goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
 
-       if (bleft < 5)
-               goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
-
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
-               goto not_ssl_hello;
-
-       /* Check the Hello's length */
-       if (bleft < 4)
-               goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       /* We want the full handshake here */
-       if (bleft < hs_len)
-               goto too_short;
-
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
-
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
-
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
-
-       if (hs_len < 4 ||                               /* minimum one cipher */
-           (ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for 
a cipher */
-           ext_len > hs_len)
-               goto not_ssl_hello;
-
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
-
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-           data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
-
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
-
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
-
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2;
-
-       while (hs_len >= 4) {
+       while (cp.hs_len >= 4) {
                int ext_type, ext_len;
 
-               ext_type = (data[0] << 8) + data[1];
-               ext_len  = (data[2] << 8) + data[3];
+               ext_type = (cp.data[0] << 8) + cp.data[1];
+               ext_len  = (cp.data[2] << 8) + cp.data[3];
 
-               if (ext_len > hs_len - 4) /* Extension too long */
+               if (ext_len > cp.hs_len - 4) /* Extension too long */
                        goto not_ssl_hello;
 
                /* Elliptic curves extension */
@@ -326,8 +316,8 @@ smp_fetch_req_ssl_ec_ext(const struct arg *args, struct 
sample *smp, const char
                        return 1;
                }
 
-               hs_len -= 4 + ext_len;
-               data   += 4 + ext_len;
+               cp.hs_len -= 4 + ext_len;
+               cp.data   += 4 + ext_len;
        }
        /* server name not found */
        goto not_ssl_hello;
@@ -336,9 +326,9 @@ smp_fetch_req_ssl_ec_ext(const struct arg *args, struct 
sample *smp, const char
        smp->flags = SMP_F_MAY_CHANGE;
 
  not_ssl_hello:
-
        return 0;
 }
+
 /* returns the type of SSL hello message (mainly used to detect an SSL hello) 
*/
 static int
 smp_fetch_ssl_hello_type(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
@@ -522,163 +512,246 @@ smp_fetch_req_ssl_ver(const struct arg *args, struct 
sample *smp, const char *kw
        return 0;
 }
 
-/* Try to extract the Server Name Indication that may be presented in a TLS
- * client hello handshake message. The format of the message is the following
- * (cf RFC5246 + RFC6066) :
- * TLS frame :
- *   - uint8  type                            = 0x16   (Handshake)
- *   - uint16 version                        >= 0x0301 (TLSv1)
- *   - uint16 length                                   (frame length)
- *   - TLS handshake :
- *     - uint8  msg_type                      = 0x01   (ClientHello)
- *     - uint24 length                                 (handshake message 
length)
- *     - ClientHello :
- *       - uint16 client_version             >= 0x0301 (TLSv1)
- *       - uint8 Random[32]                  (4 first ones are timestamp)
- *       - SessionID :
- *         - uint8 session_id_len (0..32)              (SessionID len in bytes)
- *         - uint8 session_id[session_id_len]
- *       - CipherSuite :
- *         - uint16 cipher_len               >= 2      (Cipher length in bytes)
- *         - uint16 ciphers[cipher_len/2]
- *       - CompressionMethod :
- *         - uint8 compression_len           >= 1      (# of supported methods)
- *         - uint8 compression_methods[compression_len]
- *       - optional client_extension_len               (in bytes)
- *       - optional sequence of ClientHelloExtensions  (as many bytes as 
above):
- *         - uint16 extension_type            = 0 for server_name
- *         - uint16 extension_len
- *         - opaque extension_data[extension_len]
- *           - uint16 server_name_list_len             (# of bytes here)
- *           - opaque server_names[server_name_list_len bytes]
- *             - uint8 name_type              = 0 for host_name
- *             - uint16 name_len
- *             - opaque hostname[name_len bytes]
+/*
+ * Extract the ciphers that may be presented in a TLS client hello handshake 
message.
  */
 static int
-smp_fetch_ssl_hello_sni(const struct arg *args, struct sample *smp, const char 
*kw, void *private)
+smp_fetch_ssl_cipherlist(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
 {
-       int hs_len, ext_len, bleft;
-       struct channel *chn;
-       unsigned char *data;
+    struct clnt_hello_proc cp;
 
-       if (!smp->strm)
-               goto not_ssl_hello;
+    cp = smp_client_hello_parse(smp, 0);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
+               goto too_short;
 
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
+       smp->data.type = SMP_T_BIN;
+       smp->data.u.str.area = (char *)cp.data + 2;
+       smp->data.u.str.data = cp.ext_len;
+       smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
 
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
+       return 1;
 
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
+       too_short:
+       smp->flags = SMP_F_MAY_CHANGE;
 
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
-               goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
+       not_ssl_hello:
+       return 0;
+}
 
-       if (bleft < 5)
+/* 
+ * Extract the supported group that may be presented in a TLS client hello 
handshake
+ */
+static int
+smp_fetch_ssl_supported_groups(const struct arg *args, struct sample *smp, 
const char *kw, void *private)
+{
+       struct clnt_hello_proc cp;
+    cp = smp_client_hello_parse(smp, 1);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
                goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
 
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
+    while (cp.hs_len >= 4) {
+               int ext_type, ext_len, grp_len;
 
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
-               goto not_ssl_hello;
+        ext_type = (cp.data[0] << 8) + cp.data[1]; /* Extension type */
+        ext_len  = (cp.data[2] << 8) + cp.data[3]; /* Extension length */
 
-       /* Check the Hello's length */
-       if (bleft < 4)
-               goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
+        if (ext_len > cp.hs_len - 4) /* Extension too long */
+            goto not_ssl_hello;
 
-       /* We want the full handshake here */
-       if (bleft < hs_len)
+        if (ext_type == 10) { /* Supported groups extension type ID is 10dec */
+            if (ext_len < 2) /* need at least one entry of 2 bytes in the list 
length */
+                goto not_ssl_hello;
+
+            grp_len = (cp.data[4] << 8) + cp.data[5]; /* Supported group list 
length */
+            if (grp_len < 2 || grp_len > cp.hs_len - 6)
+                goto not_ssl_hello; /* at least 2 bytes per supported group */
+
+            smp->data.type = SMP_T_BIN;
+            smp->data.u.str.area = (char *)cp.data + 6;
+            smp->data.u.str.data = grp_len;
+            smp->flags = SMP_F_VOL_SESS | SMP_F_CONST;
+
+            return 1;
+        }
+        cp.hs_len -= 4 + ext_len;
+        cp.data   += 4 + ext_len;
+    }
+    /* supported groups not found */
+    goto not_ssl_hello;
+
+    too_short:
+       smp->flags = SMP_F_MAY_CHANGE;
+
+       not_ssl_hello:
+       return 0;
+}
+
+/*
+ * Extract the signature algorithms that may be presented in a TLS client hello
+ * handshake message.
+ */
+static int
+smp_fetch_ssl_sigalgs(const struct arg *args, struct sample *smp, const char 
*kw, void *private)
+{
+    struct clnt_hello_proc cp;
+    cp = smp_client_hello_parse(smp, 1);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
                goto too_short;
 
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
+    while (cp.hs_len >= 4) {
+         int ext_type, ext_len, sigalg_len;
 
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
+         ext_type = (cp.data[0] << 8) + cp.data[1]; /* Extension type */
+         ext_len  = (cp.data[2] << 8) + cp.data[3]; /* Extension length */
 
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
+         if (ext_len > cp.hs_len - 4) /* Extension too long */
+                 goto not_ssl_hello;
 
-       if (hs_len < 4 ||                               /* minimum one cipher */
-           (ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for 
a cipher */
-           ext_len > hs_len)
-               goto not_ssl_hello;
+         if (ext_type == 13) { /* Sigalgs extension type ID is 13dec */
+                 if (ext_len < 2) /* need at least one entry of 2 bytes in the 
list length */
+                         goto not_ssl_hello;
 
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
+                 sigalg_len = (cp.data[4] << 8) + cp.data[5]; /* Sigalgs list 
length */
+                 if (sigalg_len < 2 || sigalg_len > cp.hs_len - 6)
+                         goto not_ssl_hello; /* at least 2 bytes per sigalg */
 
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-           data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
+                 smp->data.type = SMP_T_BIN;
+                 smp->data.u.str.area = (char *)cp.data + 6;
+                 smp->data.u.str.data = sigalg_len;
+                 smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
 
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
+                 return 1;
 
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
+        }
+        cp.hs_len -= 4 + ext_len;
+        cp.data   += 4 + ext_len;
+    }
+    /* sigalgs not found */
+    goto not_ssl_hello;
 
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2;
+    too_short:
+       smp->flags = SMP_F_MAY_CHANGE;
 
-       while (hs_len >= 4) {
-               int ext_type, name_type, srv_len, name_len;
+    not_ssl_hello:
+       return 0;
+}
 
-               ext_type = (data[0] << 8) + data[1];
-               ext_len  = (data[2] << 8) + data[3];
+/*
+ * Extract the key shares that may be presented in a TLS client hello 
handshake message.
+ */
+static int
+smp_fetch_ssl_keyshare_groups(const struct arg *args, struct sample *smp, 
const char *kw, void *private)
+{
+    int readPosition, numberOfKeyshares;
+    struct buffer *smp_trash = NULL;
+    unsigned char *dataPointer;
+       struct clnt_hello_proc cp;
+
+    cp = smp_client_hello_parse(smp, 1);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
+               goto too_short;
 
-               if (ext_len > hs_len - 4) /* Extension too long */
+    while (cp.hs_len >= 4) {
+         int ext_type, ext_len, keyshare_len;
+
+         ext_type = (cp.data[0] << 8) + cp.data[1]; /* Extension type */
+         ext_len  = (cp.data[2] << 8) + cp.data[3]; /* Extension length */
+
+         if (ext_len > cp.hs_len - 4) /* Extension too long */
+                 goto not_ssl_hello;
+
+         if (ext_type == 51) { /* Keyshare extension type ID is 51dec */
+                 if (ext_len < 2) /* need at least one entry of 2 bytes in the 
list length */
+                    goto not_ssl_hello;
+
+          keyshare_len = (cp.data[4] << 8) + cp.data[5]; /* Client keyshare 
length */
+          if (keyshare_len < 2 || keyshare_len > cp.hs_len - 6)
+                goto not_ssl_hello; /* at least 2 bytes per keyshare */
+
+           dataPointer = cp.data + 6; /* start of keyshare entries */
+           readPosition = 0;
+           numberOfKeyshares = 0;
+           smp_trash = get_trash_chunk();
+           while (readPosition < keyshare_len) {
+                /* Get the binary value of the keyshare group and move the 
offset to the end of the related keyshare */
+                memmove(b_orig(smp_trash) + (2*numberOfKeyshares), 
&dataPointer[readPosition], 2);
+                numberOfKeyshares++;
+                readPosition += ((int)dataPointer[readPosition+2] << 8) + 
(int)dataPointer[readPosition+3] + 4;
+           }
+           smp->data.type = SMP_T_BIN;
+           /*smp->data.u.str.area = (char *)&smp->data.u.str.bin;*/
+           smp->data.u.str.area = smp_trash->area;
+           smp->data.u.str.data = 2*numberOfKeyshares;
+           smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
+
+           return 1;
+         }
+        cp.hs_len -= 4 + ext_len;
+        cp.data   += 4 + ext_len;
+    }
+    /* keyshare groups not found */
+    goto not_ssl_hello;
+
+       too_short:
+       smp->flags = SMP_F_MAY_CHANGE;
+       not_ssl_hello:
+       return 0;
+}
+
+/*
+ * Try to extract the Server Name Indication that may be presented in a TLS
+ * client hello handshake message.
+ */
+static int
+smp_fetch_ssl_hello_sni(const struct arg *args, struct sample *smp, const char 
*kw, void *private)
+{
+       struct clnt_hello_proc cp;
+
+    cp = smp_client_hello_parse(smp, 1);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
+               goto too_short;
+
+       while (cp.hs_len >= 4) {
+               int ext_len, ext_type, name_type, srv_len, name_len;
+
+               ext_type = (cp.data[0] << 8) + cp.data[1];
+               ext_len  = (cp.data[2] << 8) + cp.data[3];
+
+               if (ext_len > cp.hs_len - 4) /* Extension too long */
                        goto not_ssl_hello;
 
                if (ext_type == 0) { /* Server name */
                        if (ext_len < 2) /* need one list length */
                                goto not_ssl_hello;
 
-                       srv_len = (data[4] << 8) + data[5];
-                       if (srv_len < 4 || srv_len > hs_len - 6)
+                       srv_len = (cp.data[4] << 8) + cp.data[5];
+                       if (srv_len < 4 || srv_len > cp.hs_len - 6)
                                goto not_ssl_hello; /* at least 4 bytes per 
server name */
 
-                       name_type = data[6];
-                       name_len = (data[7] << 8) + data[8];
+                       name_type = cp.data[6];
+                       name_len = (cp.data[7] << 8) + cp.data[8];
 
                        if (name_type == 0) { /* hostname */
                                smp->data.type = SMP_T_STR;
-                               smp->data.u.str.area = (char *)data + 9;
+                               smp->data.u.str.area = (char *)cp.data + 9;
                                smp->data.u.str.data = name_len;
                                smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
                                return 1;
                        }
                }
 
-               hs_len -= 4 + ext_len;
-               data   += 4 + ext_len;
+               cp.hs_len -= 4 + ext_len;
+               cp.data   += 4 + ext_len;
        }
        /* server name not found */
        goto not_ssl_hello;
@@ -687,144 +760,32 @@ smp_fetch_ssl_hello_sni(const struct arg *args, struct 
sample *smp, const char *
        smp->flags = SMP_F_MAY_CHANGE;
 
  not_ssl_hello:
-
        return 0;
 }
 
 /* Try to extract the Application-Layer Protocol Negotiation (ALPN) protocol
  * names that may be presented in a TLS client hello handshake message. As the
  * message presents a list of protocol names in descending order of preference,
- * it may return iteratively. The format of the message is the following
- * (cf RFC5246 + RFC7301) :
- * TLS frame :
- *   - uint8  type                            = 0x16   (Handshake)
- *   - uint16 version                        >= 0x0301 (TLSv1)
- *   - uint16 length                                   (frame length)
- *   - TLS handshake :
- *     - uint8  msg_type                      = 0x01   (ClientHello)
- *     - uint24 length                                 (handshake message 
length)
- *     - ClientHello :
- *       - uint16 client_version             >= 0x0301 (TLSv1)
- *       - uint8 Random[32]                  (4 first ones are timestamp)
- *       - SessionID :
- *         - uint8 session_id_len (0..32)              (SessionID len in bytes)
- *         - uint8 session_id[session_id_len]
- *       - CipherSuite :
- *         - uint16 cipher_len               >= 2      (Cipher length in bytes)
- *         - uint16 ciphers[cipher_len/2]
- *       - CompressionMethod :
- *         - uint8 compression_len           >= 1      (# of supported methods)
- *         - uint8 compression_methods[compression_len]
- *       - optional client_extension_len               (in bytes)
- *       - optional sequence of ClientHelloExtensions  (as many bytes as 
above):
- *         - uint16 extension_type            = 16 for 
application_layer_protocol_negotiation
- *         - uint16 extension_len
- *         - opaque extension_data[extension_len]
- *           - uint16 protocol_names_len               (# of bytes here)
- *           - opaque protocol_names[protocol_names_len bytes]
- *             - uint8 name_len
- *             - opaque protocol_name[name_len bytes]
+ * it may return iteratively.
  */
 static int
 smp_fetch_ssl_hello_alpn(const struct arg *args, struct sample *smp, const 
char *kw, void *private)
 {
-       int hs_len, ext_len, bleft;
-       struct channel *chn;
-       unsigned char *data;
-
-       if (!smp->strm)
-               goto not_ssl_hello;
-
-       /* meaningless for HTX buffers */
-       if (IS_HTX_STRM(smp->strm))
-               goto not_ssl_hello;
+       struct clnt_hello_proc cp;
 
-       chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : 
&smp->strm->req;
-       bleft = ci_data(chn);
-       data = (unsigned char *)ci_head(chn);
-
-       /* Check for SSL/TLS Handshake */
-       if (!bleft)
-               goto too_short;
-       if (*data != 0x16)
-               goto not_ssl_hello;
-
-       /* Check for SSLv3 or later (SSL version >= 3.0) in the record layer*/
-       if (bleft < 3)
+    cp = smp_client_hello_parse(smp, 1);
+       if (cp.status == NOT_SSL_HELLO)
+               goto not_ssl_hello;
+       else if (cp.status == TOO_SHORT)
                goto too_short;
-       if (data[1] < 0x03)
-               goto not_ssl_hello;
-
-       if (bleft < 5)
-               goto too_short;
-       hs_len = (data[3] << 8) + data[4];
-       if (hs_len < 1 + 3 + 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       data += 5; /* enter TLS handshake */
-       bleft -= 5;
-
-       /* Check for a complete client hello starting at <data> */
-       if (bleft < 1)
-               goto too_short;
-       if (data[0] != 0x01) /* msg_type = Client Hello */
-               goto not_ssl_hello;
-
-       /* Check the Hello's length */
-       if (bleft < 4)
-               goto too_short;
-       hs_len = (data[1] << 16) + (data[2] << 8) + data[3];
-       if (hs_len < 2 + 32 + 1 + 2 + 2 + 1 + 1 + 2 + 2)
-               goto not_ssl_hello; /* too short to have an extension */
-
-       /* We want the full handshake here */
-       if (bleft < hs_len)
-               goto too_short;
-
-       data += 4;
-       /* Start of the ClientHello message */
-       if (data[0] < 0x03 || data[1] < 0x01) /* TLSv1 minimum */
-               goto not_ssl_hello;
-
-       ext_len = data[34]; /* session_id_len */
-       if (ext_len > 32 || ext_len > (hs_len - 35)) /* check for correct 
session_id len */
-               goto not_ssl_hello;
-
-       /* Jump to cipher suite */
-       hs_len -= 35 + ext_len;
-       data   += 35 + ext_len;
-
-       if (hs_len < 4 ||                               /* minimum one cipher */
-           (ext_len = (data[0] << 8) + data[1]) < 2 || /* minimum 2 bytes for 
a cipher */
-           ext_len > hs_len)
-               goto not_ssl_hello;
-
-       /* Jump to the compression methods */
-       hs_len -= 2 + ext_len;
-       data   += 2 + ext_len;
-
-       if (hs_len < 2 ||                       /* minimum one compression 
method */
-           data[0] < 1 || data[0] > hs_len)    /* minimum 1 bytes for a method 
*/
-               goto not_ssl_hello;
-
-       /* Jump to the extensions */
-       hs_len -= 1 + data[0];
-       data   += 1 + data[0];
-
-       if (hs_len < 2 ||                       /* minimum one extension list 
length */
-           (ext_len = (data[0] << 8) + data[1]) > hs_len - 2) /* list too long 
*/
-               goto not_ssl_hello;
-
-       hs_len = ext_len; /* limit ourselves to the extension length */
-       data += 2;
 
-       while (hs_len >= 4) {
-               int ext_type, name_len, name_offset;
+       while (cp.hs_len >= 4) {
+               int ext_len, ext_type, name_len, name_offset;
 
-               ext_type = (data[0] << 8) + data[1];
-               ext_len  = (data[2] << 8) + data[3];
+               ext_type = (cp.data[0] << 8) + cp.data[1];
+               ext_len  = (cp.data[2] << 8) + cp.data[3];
 
-               if (ext_len > hs_len - 4) /* Extension too long */
+               if (ext_len > cp.hs_len - 4) /* Extension too long */
                        goto not_ssl_hello;
 
                if (ext_type == 16) { /* ALPN */
@@ -833,13 +794,13 @@ smp_fetch_ssl_hello_alpn(const struct arg *args, struct 
sample *smp, const char
 
                        /* Name cursor in ctx, must begin after 
protocol_names_len */
                        name_offset = smp->ctx.i < 6 ? 6 : smp->ctx.i;
-                       name_len = data[name_offset];
+                       name_len = cp.data[name_offset];
 
                        if (name_len + name_offset - 3 > ext_len)
                                goto not_ssl_hello;
 
                        smp->data.type = SMP_T_STR;
-                       smp->data.u.str.area = (char *)data + name_offset + 1; 
/* +1 to skip name_len */
+                       smp->data.u.str.area = (char *)cp.data + name_offset + 
1; /* +1 to skip name_len */
                        smp->data.u.str.data = name_len;
                        smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
 
@@ -852,8 +813,8 @@ smp_fetch_ssl_hello_alpn(const struct arg *args, struct 
sample *smp, const char
                        return 1;
                }
 
-               hs_len -= 4 + ext_len;
-               data   += 4 + ext_len;
+               cp.hs_len -= 4 + ext_len;
+               cp.data   += 4 + ext_len;
        }
        /* alpn not found */
        goto not_ssl_hello;
@@ -1412,7 +1373,11 @@ static struct sample_fetch_kw_list smp_kws = {ILH, {
        { "req.ssl_st_ext",      smp_fetch_req_ssl_st_ext, 0,                   
   NULL,           SMP_T_SINT, SMP_USE_L6REQ },
        { "req.ssl_hello_type",  smp_fetch_ssl_hello_type, 0,                   
   NULL,           SMP_T_SINT, SMP_USE_L6REQ },
        { "req.ssl_sni",         smp_fetch_ssl_hello_sni,  0,                   
   NULL,           SMP_T_STR,  SMP_USE_L6REQ },
-       { "req.ssl_alpn",        smp_fetch_ssl_hello_alpn, 0,                   
   NULL,           SMP_T_STR,  SMP_USE_L6REQ },
+       { "req.ssl_cipherlist",        smp_fetch_ssl_cipherlist,       0,       
   NULL,           SMP_T_BIN,  
SMP_USE_L6REQ|SMP_USE_L4CLI|SMP_USE_L5CLI|SMP_USE_FTEND },
+    { "req.ssl_supported_groups",  smp_fetch_ssl_supported_groups, 0,          
NULL,           SMP_T_BIN,  
SMP_USE_L6REQ|SMP_USE_L4CLI|SMP_USE_L5CLI|SMP_USE_FTEND },
+    { "req.ssl_sigalgs",           smp_fetch_ssl_sigalgs,          0,          
NULL,           SMP_T_BIN,  
SMP_USE_L6REQ|SMP_USE_L4CLI|SMP_USE_L5CLI|SMP_USE_FTEND },
+    { "req.ssl_keyshare_groups",   smp_fetch_ssl_keyshare_groups,  0,          
NULL,           SMP_T_BIN,  
SMP_USE_L6REQ|SMP_USE_L4CLI|SMP_USE_L5CLI|SMP_USE_FTEND },
+    { "req.ssl_alpn",        smp_fetch_ssl_hello_alpn, 0,                      
NULL,           SMP_T_STR,  SMP_USE_L6REQ },
        { "req.ssl_ver",         smp_fetch_req_ssl_ver,    0,                   
   NULL,           SMP_T_SINT, SMP_USE_L6REQ },
        { "res.len",             smp_fetch_len,            0,                   
   NULL,           SMP_T_SINT, SMP_USE_L6RES },
        { "res.payload",         smp_fetch_payload,        ARG2(2,SINT,SINT),   
   NULL,           SMP_T_BIN,  SMP_USE_L6RES },
-- 
2.39.3 (Apple Git-145)



Reply via email to