Copilot commented on code in PR #12683:
URL: https://github.com/apache/trafficserver/pull/12683#discussion_r2548115286


##########
plugins/lua/ts_lua_client_cert_helpers.h:
##########
@@ -0,0 +1,190 @@
+// 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 "";
+  }
+
+  X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  i2a_ASN1_INTEGER(bio, serial);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  ASN1_TIME_print(bio, time);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  PEM_write_bio_X509(bio, cert);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+  X509_get0_signature(&sig, nullptr, cert);
+#else
+  sig = cert->signature;
+#endif
+
+  if (!sig) {
+    return "";
+  }
+
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    return "";
+  }
+
+  for (int i = 0; i < sig->length; i++) {
+    BIO_printf(bio, "%02x", sig->data[i]);
+    if (i < sig->length - 1) {
+      BIO_printf(bio, ":");
+    }
+  }
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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
+          inet_ntop(AF_INET, data, ip_str, sizeof(ip_str));
+          results.emplace_back(ip_str);
+        } else if (len == 16) { // IPv6
+          inet_ntop(AF_INET6, data, ip_str, sizeof(ip_str));
+          results.emplace_back(ip_str);
+        }

Review Comment:
   Missing error handling for `inet_ntop` failures. The function `inet_ntop` 
can fail and return NULL (e.g., if the address family is invalid or the buffer 
is too small). When this happens, pushing the uninitialized `ip_str` buffer to 
the results vector could lead to undefined behavior.
   
   Add error checking:
   ```cpp
   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);
     }
   }
   ```
   ```suggestion
             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);
             }
   ```



##########
doc/admin-guide/plugins/lua.en.rst:
##########
@@ -1256,6 +1256,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.
+
+

Review Comment:
   [nitpick] Missing usage example for `client_cert_get_issuer`. Unlike other 
similar functions in the documentation (e.g., `client_cert_get_subject`, 
`client_cert_get_pem`), the `client_cert_get_issuer` function lacks a usage 
example. For consistency and completeness, consider adding an example similar 
to the other certificate functions.
   ```suggestion
   
   Here is an example:
   
   ::
   
       function do_global_read_request()
           issuer = ts.client_request.client_cert_get_issuer()
           if issuer then
               ts.debug('Client cert issuer: ' .. issuer)
           end
       end
   ```



##########
plugins/lua/ts_lua_client_cert_helpers.h:
##########
@@ -0,0 +1,190 @@
+// 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 "";
+  }
+
+  X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  i2a_ASN1_INTEGER(bio, serial);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  ASN1_TIME_print(bio, time);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  PEM_write_bio_X509(bio, cert);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+  X509_get0_signature(&sig, nullptr, cert);
+#else
+  sig = cert->signature;
+#endif
+
+  if (!sig) {
+    return "";
+  }
+
+  BIO *bio = BIO_new(BIO_s_mem());
+  if (!bio) {
+    return "";
+  }
+
+  for (int i = 0; i < sig->length; i++) {
+    BIO_printf(bio, "%02x", sig->data[i]);
+    if (i < sig->length - 1) {
+      BIO_printf(bio, ":");
+    }
+  }
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(data, length);
+
+  BIO_free(bio);
+  return result;

Review Comment:
   Potential null pointer dereference. The helper functions in this file do not 
check if `BIO_get_mem_data` returns a valid pointer before using it to 
construct a `std::string`. While `BIO_get_mem_data` typically returns a valid 
pointer when the BIO is successfully created, defensive programming would 
suggest checking the return value or at minimum verifying that `length > 0` 
before constructing the string.
   
   For example, in `get_x509_name_string`, `get_x509_serial_string`, 
`get_x509_time_string`, `get_x509_pem_string`, and `get_x509_signature_string`, 
consider adding a check:
   ```cpp
   if (data && length > 0) {
     std::string result(data, length);
   }
   ```



##########
plugins/lua/ts_lua_client_cert_helpers.h:
##########
@@ -0,0 +1,190 @@
+// 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 "";
+  }
+
+  X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  i2a_ASN1_INTEGER(bio, serial);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  ASN1_TIME_print(bio, time);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  PEM_write_bio_X509(bio, cert);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+  X509_get0_signature(&sig, nullptr, cert);
+#else

Review Comment:
   [nitpick] Direct access to OpenSSL struct members is deprecated. The code 
uses `cert->signature` for OpenSSL versions prior to 1.1.0, which directly 
accesses internal structure members. This approach is deprecated and may not 
work with newer OpenSSL versions that are built with opaque structures.
   
   While the `#if` directive handles this for OpenSSL 1.1.0+, consider adding a 
comment explaining that the `#else` branch is for legacy OpenSSL versions and 
may have compatibility issues with some builds.
   ```suggestion
   #else
     // Legacy OpenSSL versions (<1.1.0): direct access to struct members is 
deprecated and may not work
     // if OpenSSL is built with opaque structures. Use with caution.
   ```



##########
plugins/lua/ts_lua_client_cert_helpers.h:
##########
@@ -0,0 +1,190 @@
+// Helper functions for certificate data extraction

Review Comment:
   Missing Apache License header. All header files in the plugins/lua directory 
include the standard Apache Software Foundation license header. This new header 
file should include the same license header for consistency with the rest of 
the codebase.
   
   Add the Apache License header at the beginning of the file (before `#pragma 
once`), following the pattern seen in other files like 
`ts_lua_cached_response.h`.



##########
plugins/lua/ts_lua_client_cert_helpers.h:
##########
@@ -0,0 +1,190 @@
+// 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 "";
+  }
+
+  X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  i2a_ASN1_INTEGER(bio, serial);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  ASN1_TIME_print(bio, time);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(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 "";
+  }
+
+  PEM_write_bio_X509(bio, cert);
+
+  char  *data   = nullptr;
+  long   length = BIO_get_mem_data(bio, &data);
+  std::string result(data, length);
+
+  BIO_free(bio);
+  return result;
+}

Review Comment:
   Missing error checking for OpenSSL write operations. Functions like 
`X509_NAME_print_ex`, `i2a_ASN1_INTEGER`, `ASN1_TIME_print`, and 
`PEM_write_bio_X509` can fail and return error codes (typically 0 or negative 
values on failure). These failures are not checked, which could result in 
incomplete or invalid data being returned.
   
   For example, in `get_x509_name_string`:
   ```cpp
   if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) <= 0) {
     BIO_free(bio);
     return "";
   }
   ```
   
   Similar checks should be added for the other helper functions.



##########
plugins/lua/ts_lua_client_cert_helpers.h:
##########
@@ -0,0 +1,190 @@
+// Helper functions for certificate data extraction

Review Comment:
   Missing header guard. The header file lacks a header guard (either `#pragma 
once` or traditional include guards). Based on the codebase convention seen in 
other header files (e.g., `ts_lua_cached_response.h`, 
`ts_lua_client_request.h`), this file should use `#pragma once` at the 
beginning.
   
   Add at the top of the file:
   ```cpp
   #pragma once
   ```



##########
plugins/lua/ts_lua_client_request.cc:
##########
@@ -1139,3 +1174,924 @@ 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;
+}

Review Comment:
   The client and server certificate functions contain significant code 
duplication. Each pair of functions (e.g., `client_cert_get_pem` and 
`server_cert_get_pem`) follows nearly identical logic with only a difference in 
how the certificate is retrieved 
(`SSL_get1_peer_certificate`/`SSL_get_peer_certificate` vs 
`SSL_get_certificate`).
   
   Consider refactoring to reduce duplication by:
   1. Creating a helper function that accepts the certificate retrieval logic 
as a parameter
   2. Or creating a common implementation that takes a certificate pointer 
directly
   
   This would improve maintainability and reduce the risk of inconsistencies 
when making future changes.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to