This is an automated email from the ASF dual-hosted git repository.

kichan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new fcd0827045 Add support for retrieving cert info in lua plugin (#12683)
fcd0827045 is described below

commit fcd0827045dd3da6a2342715bb7e94d5c8322955
Author: Kit Chan <[email protected]>
AuthorDate: Tue Dec 16 18:41:05 2025 +0100

    Add support for retrieving cert info in lua plugin (#12683)
    
    * Add support for retrieving cert info in lua plugin
    
    * fix format error
    
    * fix format error
    
    * fix compile error
    
    * Update plugins/lua/ts_lua_client_cert_helpers.h
    
    Co-authored-by: Copilot <[email protected]>
    
    * fix error checking
    
    * fix error checking
    
    * fix error checking
    
    * fix error checking
    
    * fix error checking
    
    * Update plugins/lua/ts_lua_client_cert_helpers.h
    
    Co-authored-by: Copilot <[email protected]>
    
    * fix error checking
    
    * fix error checking
    
    * fix compile error
    
    ---------
    
    Co-authored-by: Copilot <[email protected]>
---
 doc/admin-guide/plugins/lua.en.rst       | 347 +++++++++++
 plugins/lua/ts_lua_client_cert_helpers.h | 243 ++++++++
 plugins/lua/ts_lua_client_request.cc     | 955 +++++++++++++++++++++++++++++++
 3 files changed, 1545 insertions(+)

diff --git a/doc/admin-guide/plugins/lua.en.rst 
b/doc/admin-guide/plugins/lua.en.rst
index b00999633e..08970e340e 100644
--- a/doc/admin-guide/plugins/lua.en.rst
+++ b/doc/admin-guide/plugins/lua.en.rst
@@ -1340,6 +1340,353 @@ Here is an example:
     end
 
 
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_pem
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_pem()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the PEM-encoded client certificate (for mTLS connections).
+
+Returns the client certificate in PEM format, or nil if no client certificate 
is present.
+
+Here is an example:
+
+::
+
+    function do_global_read_request()
+        pem = ts.client_request.client_cert_get_pem()
+        if pem then
+            ts.debug('Client cert PEM: ' .. pem)
+        end
+    end
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_subject
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_subject()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the subject DN from the client certificate.
+
+Returns the subject distinguished name in RFC2253 format, or nil if not 
available.
+
+Here is an example:
+
+::
+
+    function do_global_read_request()
+        subject = ts.client_request.client_cert_get_subject()
+        if subject then
+            ts.debug('Client cert subject: ' .. subject)
+        end
+    end
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_issuer
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_issuer()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the issuer DN from the client certificate.
+
+Returns the issuer distinguished name in RFC2253 format, or nil if not 
available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_serial
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_serial()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the serial number from the client certificate.
+
+Returns the certificate serial number as a string, or nil if not available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_signature
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_signature()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the signature from the client certificate.
+
+Returns the certificate signature as a colon-separated hex string, or nil if 
not available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_not_before
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_not_before()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the "not before" timestamp from the client certificate.
+
+Returns the certificate validity start date/time as a string, or nil if not 
available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_not_after
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_not_after()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the "not after" timestamp from the client certificate.
+
+Returns the certificate validity end date/time as a string, or nil if not 
available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_version
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_version()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the X.509 version from the client certificate.
+
+Returns the certificate version as an integer (typically 2 for v3 
certificates), or nil if not available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_san_dns
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_san_dns()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get DNS Subject Alternative Names from the client certificate.
+
+Returns a Lua table (array) of DNS names, or nil if none are present.
+
+Here is an example:
+
+::
+
+    function do_global_read_request()
+        dns_names = ts.client_request.client_cert_get_san_dns()
+        if dns_names then
+            for i, name in ipairs(dns_names) do
+                ts.debug('DNS SAN: ' .. name)
+            end
+        end
+    end
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_san_ip
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_san_ip()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get IP address Subject Alternative Names from the client 
certificate.
+
+Returns a Lua table (array) of IP addresses, or nil if none are present.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_san_email
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_san_email()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get email Subject Alternative Names from the client 
certificate.
+
+Returns a Lua table (array) of email addresses, or nil if none are present.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.client_cert_get_san_uri
+-----------------------------------------------
+**syntax:** *ts.client_request.client_cert_get_san_uri()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get URI Subject Alternative Names from the client certificate.
+
+Returns a Lua table (array) of URIs, or nil if none are present.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_pem
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_pem()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the PEM-encoded server certificate (the certificate ATS 
presented to the client).
+
+Returns the server certificate in PEM format, or nil if not available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_subject
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_subject()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the subject DN from the server certificate.
+
+Returns the subject distinguished name in RFC2253 format, or nil if not 
available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_issuer
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_issuer()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the issuer DN from the server certificate.
+
+Returns the issuer distinguished name in RFC2253 format, or nil if not 
available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_serial
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_serial()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the serial number from the server certificate.
+
+Returns the certificate serial number as a string, or nil if not available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_signature
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_signature()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the signature from the server certificate.
+
+Returns the certificate signature as a colon-separated hex string, or nil if 
not available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_not_before
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_not_before()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the "not before" timestamp from the server certificate.
+
+Returns the certificate validity start date/time as a string, or nil if not 
available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_not_after
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_not_after()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the "not after" timestamp from the server certificate.
+
+Returns the certificate validity end date/time as a string, or nil if not 
available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_version
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_version()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get the X.509 version from the server certificate.
+
+Returns the certificate version as an integer (typically 2 for v3 
certificates), or nil if not available.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_san_dns
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_san_dns()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get DNS Subject Alternative Names from the server certificate.
+
+Returns a Lua table (array) of DNS names, or nil if none are present.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_san_ip
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_san_ip()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get IP address Subject Alternative Names from the server 
certificate.
+
+Returns a Lua table (array) of IP addresses, or nil if none are present.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_san_email
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_san_email()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get email Subject Alternative Names from the server 
certificate.
+
+Returns a Lua table (array) of email addresses, or nil if none are present.
+
+
+:ref:`TOP <admin-plugins-ts-lua>`
+
+ts.client_request.server_cert_get_san_uri
+-----------------------------------------------
+**syntax:** *ts.client_request.server_cert_get_san_uri()*
+
+**context:** do_remap/do_os_response or do_global_* or later
+
+**description**: Get URI Subject Alternative Names from the server certificate.
+
+Returns a Lua table (array) of URIs, or nil if none are present.
+
+
 :ref:`TOP <admin-plugins-ts-lua>`
 
 ts.client_request.get_pp_info
diff --git a/plugins/lua/ts_lua_client_cert_helpers.h 
b/plugins/lua/ts_lua_client_cert_helpers.h
new file mode 100644
index 0000000000..c7f20b50eb
--- /dev/null
+++ b/plugins/lua/ts_lua_client_cert_helpers.h
@@ -0,0 +1,243 @@
+/*
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+*/
+
+// Helper functions for certificate data extraction
+static std::string
+get_x509_name_string(X509_NAME *name)
+{
+  if (!name) {
+    return "";
+  }
+
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    return "";
+  }
+
+  if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) <= 0) {
+    BIO_free(bio);
+    return "";
+  }
+
+  char       *data   = nullptr;
+  long        length = BIO_get_mem_data(bio, &data);
+  std::string result;
+
+  if (data && length > 0) {
+    result.assign(data, length);
+  }
+
+  BIO_free(bio);
+  return result;
+}
+
+static std::string
+get_x509_serial_string(X509 *cert)
+{
+  if (!cert) {
+    return "";
+  }
+
+  ASN1_INTEGER *serial = X509_get_serialNumber(cert);
+  if (!serial) {
+    return "";
+  }
+
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    return "";
+  }
+
+  if (i2a_ASN1_INTEGER(bio, serial) <= 0) {
+    BIO_free(bio);
+    return "";
+  }
+
+  char       *data   = nullptr;
+  long        length = BIO_get_mem_data(bio, &data);
+  std::string result;
+
+  if (data && length > 0) {
+    result.assign(data, length);
+  }
+
+  BIO_free(bio);
+  return result;
+}
+
+static std::string
+get_x509_time_string(ASN1_TIME *time)
+{
+  if (!time) {
+    return "";
+  }
+
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    return "";
+  }
+
+  if (ASN1_TIME_print(bio, time) <= 0) {
+    BIO_free(bio);
+    return "";
+  }
+
+  char       *data   = nullptr;
+  long        length = BIO_get_mem_data(bio, &data);
+  std::string result;
+
+  if (data && length > 0) {
+    result.assign(data, length);
+  }
+
+  BIO_free(bio);
+  return result;
+}
+
+static std::string
+get_x509_pem_string(X509 *cert)
+{
+  if (!cert) {
+    return "";
+  }
+
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    return "";
+  }
+
+  if (PEM_write_bio_X509(bio, cert) <= 0) {
+    BIO_free(bio);
+    return "";
+  }
+
+  char       *data   = nullptr;
+  long        length = BIO_get_mem_data(bio, &data);
+  std::string result;
+
+  if (data && length > 0) {
+    result.assign(data, length);
+  }
+
+  BIO_free(bio);
+  return result;
+}
+
+static std::string
+get_x509_signature_string(X509 *cert)
+{
+  if (!cert) {
+    return "";
+  }
+
+  const ASN1_BIT_STRING *sig = nullptr;
+  X509_get0_signature(&sig, nullptr, cert);
+
+  if (!sig) {
+    return "";
+  }
+
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    return "";
+  }
+
+  for (int i = 0; i < sig->length; i++) {
+    if (BIO_printf(bio, "%02x", sig->data[i]) <= 0) {
+      BIO_free(bio);
+      return "";
+    }
+    if (i < sig->length - 1) {
+      if (BIO_printf(bio, ":") <= 0) {
+        BIO_free(bio);
+        return "";
+      }
+    }
+  }
+
+  char       *data   = nullptr;
+  long        length = BIO_get_mem_data(bio, &data);
+  std::string result;
+
+  if (data && length > 0) {
+    result.assign(data, length);
+  }
+
+  BIO_free(bio);
+  return result;
+}
+
+static std::vector<std::string>
+get_x509_san_strings(X509 *cert, int san_type)
+{
+  std::vector<std::string> results;
+
+  if (!cert) {
+    return results;
+  }
+
+  GENERAL_NAMES *names = static_cast<GENERAL_NAMES *>(X509_get_ext_d2i(cert, 
NID_subject_alt_name, nullptr, nullptr));
+  if (!names) {
+    return results;
+  }
+
+  int num_names = sk_GENERAL_NAME_num(names);
+  for (int i = 0; i < num_names; i++) {
+    GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
+    if (!name || name->type != san_type) {
+      continue;
+    }
+
+    switch (san_type) {
+    case GEN_DNS:
+    case GEN_EMAIL:
+    case GEN_URI: {
+      ASN1_STRING *str = name->d.ia5;
+      if (str) {
+        const unsigned char *data = ASN1_STRING_get0_data(str);
+        int                  len  = ASN1_STRING_length(str);
+        if (data && len > 0) {
+          results.emplace_back(reinterpret_cast<const char *>(data), len);
+        }
+      }
+      break;
+    }
+    case GEN_IPADD: {
+      ASN1_OCTET_STRING *ip = name->d.iPAddress;
+      if (ip) {
+        const unsigned char *data = ASN1_STRING_get0_data(ip);
+        int                  len  = ASN1_STRING_length(ip);
+        char                 ip_str[INET6_ADDRSTRLEN];
+
+        if (len == 4) { // IPv4
+          if (inet_ntop(AF_INET, data, ip_str, sizeof(ip_str)) != nullptr) {
+            results.emplace_back(ip_str);
+          }
+        } else if (len == 16) { // IPv6
+          if (inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str)) != nullptr) {
+            results.emplace_back(ip_str);
+          }
+        }
+      }
+      break;
+    }
+    }
+  }
+  GENERAL_NAMES_free(names);
+  return results;
+}
diff --git a/plugins/lua/ts_lua_client_request.cc 
b/plugins/lua/ts_lua_client_request.cc
index a6beea92ab..4e675ffafa 100644
--- a/plugins/lua/ts_lua_client_request.cc
+++ b/plugins/lua/ts_lua_client_request.cc
@@ -18,7 +18,14 @@
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/bio.h>
+#include <vector>
+#include <string>
 #include "ts_lua_util.h"
+#include "ts_lua_client_cert_helpers.h"
 
 typedef enum {
   TS_LUA_PP_INFO_VERSION   = TS_PP_INFO_VERSION,
@@ -94,6 +101,32 @@ static int  
ts_lua_client_request_get_ssl_protocol(lua_State *L);
 static void ts_lua_inject_client_request_ssl_curve_api(lua_State *L);
 static int  ts_lua_client_request_get_ssl_curve(lua_State *L);
 
+static void ts_lua_inject_client_request_cert_api(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_pem(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_subject(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_issuer(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_serial(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_signature(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_not_before(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_not_after(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_version(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_san_dns(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_san_ip(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_san_email(lua_State *L);
+static int  ts_lua_client_request_client_cert_get_san_uri(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_pem(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_subject(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_issuer(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_serial(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_signature(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_not_before(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_not_after(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_version(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_san_dns(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_san_ip(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_san_email(lua_State *L);
+static int  ts_lua_client_request_server_cert_get_san_uri(lua_State *L);
+
 static void ts_lua_inject_client_request_pp_info_api(lua_State *L);
 static int  ts_lua_client_request_get_pp_info(lua_State *L);
 static int  ts_lua_client_request_get_pp_info_int(lua_State *L);
@@ -118,6 +151,7 @@ ts_lua_inject_client_request_api(lua_State *L)
   ts_lua_inject_client_request_ssl_cipher_api(L);
   ts_lua_inject_client_request_ssl_protocol_api(L);
   ts_lua_inject_client_request_ssl_curve_api(L);
+  ts_lua_inject_client_request_cert_api(L);
   ts_lua_inject_client_request_pp_info_api(L);
 
   lua_setfield(L, -2, "client_request");
@@ -1169,6 +1203,927 @@ ts_lua_client_request_get_ssl_curve(lua_State *L)
   return 1;
 }
 
+// Certificate API Functions
+static void
+ts_lua_inject_client_request_cert_api(lua_State *L)
+{
+  // Client certificate functions
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_pem);
+  lua_setfield(L, -2, "client_cert_get_pem");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_subject);
+  lua_setfield(L, -2, "client_cert_get_subject");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_issuer);
+  lua_setfield(L, -2, "client_cert_get_issuer");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_serial);
+  lua_setfield(L, -2, "client_cert_get_serial");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_signature);
+  lua_setfield(L, -2, "client_cert_get_signature");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_not_before);
+  lua_setfield(L, -2, "client_cert_get_not_before");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_not_after);
+  lua_setfield(L, -2, "client_cert_get_not_after");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_version);
+  lua_setfield(L, -2, "client_cert_get_version");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_san_dns);
+  lua_setfield(L, -2, "client_cert_get_san_dns");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_san_ip);
+  lua_setfield(L, -2, "client_cert_get_san_ip");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_san_email);
+  lua_setfield(L, -2, "client_cert_get_san_email");
+
+  lua_pushcfunction(L, ts_lua_client_request_client_cert_get_san_uri);
+  lua_setfield(L, -2, "client_cert_get_san_uri");
+
+  // Server certificate functions
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_pem);
+  lua_setfield(L, -2, "server_cert_get_pem");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_subject);
+  lua_setfield(L, -2, "server_cert_get_subject");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_issuer);
+  lua_setfield(L, -2, "server_cert_get_issuer");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_serial);
+  lua_setfield(L, -2, "server_cert_get_serial");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_signature);
+  lua_setfield(L, -2, "server_cert_get_signature");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_not_before);
+  lua_setfield(L, -2, "server_cert_get_not_before");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_not_after);
+  lua_setfield(L, -2, "server_cert_get_not_after");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_version);
+  lua_setfield(L, -2, "server_cert_get_version");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_san_dns);
+  lua_setfield(L, -2, "server_cert_get_san_dns");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_san_ip);
+  lua_setfield(L, -2, "server_cert_get_san_ip");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_san_email);
+  lua_setfield(L, -2, "server_cert_get_san_email");
+
+  lua_pushcfunction(L, ts_lua_client_request_server_cert_get_san_uri);
+  lua_setfield(L, -2, "server_cert_get_san_uri");
+}
+
+// Client Certificate Functions
+static int
+ts_lua_client_request_client_cert_get_pem(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::string pem = get_x509_pem_string(cert);
+        X509_free(cert);
+        if (!pem.empty()) {
+          lua_pushlstring(L, pem.c_str(), pem.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_subject(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::string subject = 
get_x509_name_string(X509_get_subject_name(cert));
+        X509_free(cert);
+        if (!subject.empty()) {
+          lua_pushlstring(L, subject.c_str(), subject.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_issuer(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::string issuer = get_x509_name_string(X509_get_issuer_name(cert));
+        X509_free(cert);
+        if (!issuer.empty()) {
+          lua_pushlstring(L, issuer.c_str(), issuer.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_serial(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::string serial = get_x509_serial_string(cert);
+        X509_free(cert);
+        if (!serial.empty()) {
+          lua_pushlstring(L, serial.c_str(), serial.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_signature(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::string sig = get_x509_signature_string(cert);
+        X509_free(cert);
+        if (!sig.empty()) {
+          lua_pushlstring(L, sig.c_str(), sig.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_not_before(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::string not_before = 
get_x509_time_string(X509_get_notBefore(cert));
+        X509_free(cert);
+        if (!not_before.empty()) {
+          lua_pushlstring(L, not_before.c_str(), not_before.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_not_after(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::string not_after = get_x509_time_string(X509_get_notAfter(cert));
+        X509_free(cert);
+        if (!not_after.empty()) {
+          lua_pushlstring(L, not_after.c_str(), not_after.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_version(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        long version = X509_get_version(cert);
+        X509_free(cert);
+        lua_pushinteger(L, version);
+        return 1;
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_san_dns(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::vector<std::string> dns_names = get_x509_san_strings(cert, 
GEN_DNS);
+        X509_free(cert);
+
+        if (!dns_names.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < dns_names.size(); i++) {
+            lua_pushlstring(L, dns_names[i].c_str(), dns_names[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_san_ip(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::vector<std::string> ip_addrs = get_x509_san_strings(cert, 
GEN_IPADD);
+        X509_free(cert);
+
+        if (!ip_addrs.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < ip_addrs.size(); i++) {
+            lua_pushlstring(L, ip_addrs[i].c_str(), ip_addrs[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_san_email(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::vector<std::string> emails = get_x509_san_strings(cert, 
GEN_EMAIL);
+        X509_free(cert);
+
+        if (!emails.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < emails.size(); i++) {
+            lua_pushlstring(L, emails[i].c_str(), emails[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_client_cert_get_san_uri(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL *ssl = reinterpret_cast<SSL *>(ssl_conn);
+#ifdef OPENSSL_IS_OPENSSL3
+      X509 *cert = SSL_get1_peer_certificate(ssl);
+#else
+      X509 *cert = SSL_get_peer_certificate(ssl);
+#endif
+      if (cert) {
+        std::vector<std::string> uris = get_x509_san_strings(cert, GEN_URI);
+        X509_free(cert);
+
+        if (!uris.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < uris.size(); i++) {
+            lua_pushlstring(L, uris[i].c_str(), uris[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+// Server Certificate Functions
+static int
+ts_lua_client_request_server_cert_get_pem(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::string pem = get_x509_pem_string(cert);
+        if (!pem.empty()) {
+          lua_pushlstring(L, pem.c_str(), pem.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_subject(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::string subject = 
get_x509_name_string(X509_get_subject_name(cert));
+        if (!subject.empty()) {
+          lua_pushlstring(L, subject.c_str(), subject.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_issuer(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::string issuer = get_x509_name_string(X509_get_issuer_name(cert));
+        if (!issuer.empty()) {
+          lua_pushlstring(L, issuer.c_str(), issuer.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_serial(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::string serial = get_x509_serial_string(cert);
+        if (!serial.empty()) {
+          lua_pushlstring(L, serial.c_str(), serial.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_signature(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::string sig = get_x509_signature_string(cert);
+        if (!sig.empty()) {
+          lua_pushlstring(L, sig.c_str(), sig.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_not_before(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::string not_before = 
get_x509_time_string(X509_get_notBefore(cert));
+        if (!not_before.empty()) {
+          lua_pushlstring(L, not_before.c_str(), not_before.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_not_after(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::string not_after = get_x509_time_string(X509_get_notAfter(cert));
+        if (!not_after.empty()) {
+          lua_pushlstring(L, not_after.c_str(), not_after.length());
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_version(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        long version = X509_get_version(cert);
+        lua_pushinteger(L, version);
+        return 1;
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_san_dns(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::vector<std::string> dns_names = get_x509_san_strings(cert, 
GEN_DNS);
+
+        if (!dns_names.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < dns_names.size(); i++) {
+            lua_pushlstring(L, dns_names[i].c_str(), dns_names[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_san_ip(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::vector<std::string> ip_addrs = get_x509_san_strings(cert, 
GEN_IPADD);
+
+        if (!ip_addrs.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < ip_addrs.size(); i++) {
+            lua_pushlstring(L, ip_addrs[i].c_str(), ip_addrs[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_san_email(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::vector<std::string> emails = get_x509_san_strings(cert, 
GEN_EMAIL);
+
+        if (!emails.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < emails.size(); i++) {
+            lua_pushlstring(L, emails[i].c_str(), emails[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
+static int
+ts_lua_client_request_server_cert_get_san_uri(lua_State *L)
+{
+  ts_lua_http_ctx *http_ctx;
+  TSHttpSsn        ssnp;
+  TSVConn          client_conn;
+
+  GET_HTTP_CONTEXT(http_ctx, L);
+
+  ssnp        = TSHttpTxnSsnGet(http_ctx->txnp);
+  client_conn = TSHttpSsnClientVConnGet(ssnp);
+
+  if (TSVConnIsSsl(client_conn)) {
+    TSSslConnection ssl_conn = TSVConnSslConnectionGet(client_conn);
+    if (ssl_conn) {
+      SSL  *ssl  = reinterpret_cast<SSL *>(ssl_conn);
+      X509 *cert = SSL_get_certificate(ssl);
+      if (cert) {
+        std::vector<std::string> uris = get_x509_san_strings(cert, GEN_URI);
+
+        if (!uris.empty()) {
+          lua_newtable(L);
+          for (size_t i = 0; i < uris.size(); i++) {
+            lua_pushlstring(L, uris[i].c_str(), uris[i].length());
+            lua_rawseti(L, -2, i + 1);
+          }
+          return 1;
+        }
+      }
+    }
+  }
+
+  lua_pushnil(L);
+  return 1;
+}
+
 static void
 ts_lua_inject_client_request_pp_info_api(lua_State *L)
 {


Reply via email to