]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/format/Format.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / format / Format.cc
index 7ca4f53036ae9e32abcd974ced56c68750b54b9b..ce8d59701bf070b38edd4ead3a04618ea2b9473c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -8,24 +8,27 @@
 
 #include "squid.h"
 #include "AccessLogEntry.h"
+#include "base64.h"
 #include "client_side.h"
 #include "comm/Connection.h"
-#include "err_detail_type.h"
+#include "error/Detail.h"
 #include "errorpage.h"
 #include "fde.h"
 #include "format/Format.h"
 #include "format/Quoting.h"
 #include "format/Token.h"
-#include "fqdncache.h"
 #include "http/Stream.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
+#include "proxyp/Header.h"
 #include "rfc1738.h"
+#include "sbuf/Stream.h"
+#include "sbuf/StringConvert.h"
+#include "security/CertError.h"
+#include "security/Certificate.h"
 #include "security/NegotiationHistory.h"
-#include "SquidTime.h"
 #include "Store.h"
 #include "tools.h"
-#include "URL.h"
 #if USE_OPENSSL
 #include "ssl/ErrorDetail.h"
 #include "ssl/ServerBump.h"
@@ -37,8 +40,8 @@
 const SBuf Format::Dash("-");
 
 Format::Format::Format(const char *n) :
-    format(NULL),
-    next(NULL)
+    format(nullptr),
+    next(nullptr)
 {
     name = xstrdup(n);
 }
@@ -50,7 +53,7 @@ Format::Format::~Format()
         // unlink the next entry for deletion
         Format *temp = next;
         next = temp->next;
-        temp->next = NULL;
+        temp->next = nullptr;
         delete temp;
     }
 
@@ -66,14 +69,14 @@ Format::Format::parse(const char *def)
     Token *new_lt, *last_lt;
     enum Quoting quote = LOG_QUOTE_NONE;
 
-    debugs(46, 2, HERE << "got definition '" << def << "'");
+    debugs(46, 2, "got definition '" << def << "'");
 
     if (format) {
         debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'");
         return false;
     }
 
-    /* very inefficent parser, but who cares, this needs to be simple */
+    /* very inefficient parser, but who cares, this needs to be simple */
     /* First off, let's tokenize, we'll optimize in a second pass.
      * A token can either be a %-prefixed sequence (usually a dynamic
      * token but it can be an escaped sequence), or a string. */
@@ -92,14 +95,32 @@ Format::Format::parse(const char *def)
     return true;
 }
 
+size_t
+Format::AssembleOne(const char *token, MemBuf &mb, const AccessLogEntryPointer &ale)
+{
+    Token tkn;
+    enum Quoting quote = LOG_QUOTE_NONE;
+    const auto tokenSize = tkn.parse(token, &quote);
+    assert(tokenSize > 0);
+    if (ale != nullptr) {
+        Format fmt("SimpleToken");
+        fmt.format = &tkn;
+        fmt.assemble(mb, ale, 0);
+        fmt.format = nullptr;
+    } else {
+        mb.append("-", 1);
+    }
+    return static_cast<size_t>(tokenSize);
+}
+
 void
 Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) const
 {
-    debugs(46, 4, HERE);
+    debugs(46, 4, MYNAME);
 
     // loop rather than recursing to conserve stack space.
     for (const Format *fmt = this; fmt; fmt = fmt->next) {
-        debugs(46, 3, HERE << "Dumping format definition for " << fmt->name);
+        debugs(46, 3, "Dumping format definition for " << fmt->name);
         if (directiveName)
             storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
 
@@ -108,7 +129,7 @@ Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) co
                 storeAppendPrintf(entry, "%s", t->data.string);
             else {
                 char argbuf[256];
-                char *arg = NULL;
+                char *arg = nullptr;
                 ByteCode_t type = t->type;
 
                 switch (type) {
@@ -320,32 +341,57 @@ log_quoted_string(const char *str, char *out)
     *p = '\0';
 }
 
-#if USE_OPENSSL
-static char *
-sslErrorName(Ssl::ssl_error_t err, char *buf, size_t size)
+/// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
+/// headers from sent headers rather than failing to distinguish requests from responses.
+/// \retval HttpReply sent to the HTTP client (access.log and default context).
+/// \retval HttpReply received (encapsulated) from the ICAP server (icap.log context).
+/// \retval HttpRequest received (encapsulated) from the ICAP server (icap.log context).
+static const Http::Message *
+actualReplyHeader(const AccessLogEntry::Pointer &al)
 {
-    snprintf(buf, size, "SSL_ERR=%d", err);
-    return buf;
+    const Http::Message *msg = al->reply.getRaw();
+#if ICAP_CLIENT
+    // al->icap.reqMethod is methodNone in access.log context
+    if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
+        msg = al->adapted_request;
+#endif
+    return msg;
 }
+
+/// XXX: Misnamed. See actualReplyHeader().
+/// \return HttpRequest or HttpReply for %http::>h.
+static const Http::Message *
+actualRequestHeader(const AccessLogEntry::Pointer &al)
+{
+#if ICAP_CLIENT
+    // al->icap.reqMethod is methodNone in access.log context
+    if (al->icap.reqMethod == Adaptation::methodRespmod) {
+        // XXX: for now AccessLogEntry lacks virgin response headers
+        return nullptr;
+    }
 #endif
+    return al->request;
+}
 
 void
 Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
 {
-    char tmp[1024];
-    String sb;
+    static char tmp[1024];
+    SBuf sb;
 
-    for (Token *fmt = format; fmt != NULL; fmt = fmt->next) {   /* for each token */
-        const char *out = NULL;
+    for (Token *fmt = format; fmt; fmt = fmt->next) {   /* for each token */
+        const char *out = nullptr;
         int quote = 0;
         long int outint = 0;
         int doint = 0;
         int dofree = 0;
         int64_t outoff = 0;
         int dooff = 0;
-        struct timeval outtv = {0, 0};
+        struct timeval outtv = {};
         int doMsec = 0;
         int doSec = 0;
+        bool doUint64 = false;
+        uint64_t outUint64 = 0;
 
         switch (fmt->type) {
 
@@ -363,31 +409,29 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 
         case LFT_CLIENT_FQDN:
-            if (al->cache.caddr.isAnyAddr()) // e.g., ICAP OPTIONS lack client
-                out = "-";
-            else
-                out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
-            if (!out) {
-                out = al->cache.caddr.toStr(tmp,1024);
-            }
-
+            out = al->getLogClientFqdn(tmp, sizeof(tmp));
             break;
 
         case LFT_CLIENT_PORT:
             if (al->request) {
                 outint = al->request->client_addr.port();
                 doint = 1;
+            } else if (al->tcpClient) {
+                outint = al->tcpClient->remote.port();
+                doint = 1;
             }
             break;
 
         case LFT_CLIENT_EUI:
 #if USE_SQUID_EUI
             // TODO make the ACL checklist have a direct link to any TCP details.
-            if (al->request && al->request->clientConnectionManager.valid() && al->request->clientConnectionManager->clientConnection != NULL) {
-                if (al->request->clientConnectionManager->clientConnection->remote.isIPv4())
-                    al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
+            if (al->request && al->request->clientConnectionManager.valid() &&
+                    al->request->clientConnectionManager->clientConnection) {
+                const auto &conn = al->request->clientConnectionManager->clientConnection;
+                if (conn->remote.isIPv4())
+                    conn->remoteEui48.encode(tmp, sizeof(tmp));
                 else
-                    al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
+                    conn->remoteEui64.encode(tmp, sizeof(tmp));
                 out = tmp;
             }
 #endif
@@ -396,9 +440,9 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         case LFT_EXT_ACL_CLIENT_EUI48:
 #if USE_SQUID_EUI
             if (al->request && al->request->clientConnectionManager.valid() &&
-                    al->request->clientConnectionManager->clientConnection != NULL &&
+                    al->request->clientConnectionManager->clientConnection &&
                     al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
-                al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, 1024);
+                al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, sizeof(tmp));
                 out = tmp;
             }
 #endif
@@ -407,18 +451,17 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         case LFT_EXT_ACL_CLIENT_EUI64:
 #if USE_SQUID_EUI
             if (al->request && al->request->clientConnectionManager.valid() &&
-                    al->request->clientConnectionManager->clientConnection != NULL &&
+                    al->request->clientConnectionManager->clientConnection &&
                     !al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
-                al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, 1024);
+                al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, sizeof(tmp));
                 out = tmp;
             }
 #endif
             break;
 
         case LFT_SERVER_IP_ADDRESS:
-            if (al->hier.tcpServer != NULL) {
-                out = al->hier.tcpServer->remote.toStr(tmp,sizeof(tmp));
-            }
+            if (al->hier.tcpServer)
+                out = al->hier.tcpServer->remote.toStr(tmp, sizeof(tmp));
             break;
 
         case LFT_SERVER_FQDN_OR_PEER_NAME:
@@ -426,59 +469,52 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 
         case LFT_SERVER_PORT:
-            if (al->hier.tcpServer != NULL) {
+            if (al->hier.tcpServer) {
                 outint = al->hier.tcpServer->remote.port();
                 doint = 1;
             }
             break;
 
-        case LFT_LOCAL_LISTENING_IP: {
-            // avoid logging a dash if we have reliable info
-            const bool interceptedAtKnownPort = al->request ?
-                                                (al->request->flags.interceptTproxy ||
-                                                 al->request->flags.intercepted) && al->cache.port != NULL :
-                                                false;
-            if (interceptedAtKnownPort) {
-                const bool portAddressConfigured = !al->cache.port->s.isAnyAddr();
-                if (portAddressConfigured)
-                    out = al->cache.port->s.toStr(tmp, sizeof(tmp));
-            } else if (al->tcpClient != NULL)
-                out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
-        }
-        break;
+        case LFT_LOCAL_LISTENING_IP:
+            if (const auto addr = FindListeningPortAddress(nullptr, al.getRaw()))
+                out = addr->toStr(tmp, sizeof(tmp));
+            break;
 
         case LFT_CLIENT_LOCAL_IP:
-            if (al->tcpClient != NULL) {
-                out = al->tcpClient->local.toStr(tmp,sizeof(tmp));
-            }
+            if (al->tcpClient)
+                out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
             break;
 
         case LFT_CLIENT_LOCAL_TOS:
-            if (al->tcpClient != NULL) {
-                snprintf(tmp, sizeof(tmp), "0x%x", (uint32_t)al->tcpClient->tos);
-                out = tmp;
+            if (al->tcpClient) {
+                sb.appendf("0x%x", static_cast<uint32_t>(al->tcpClient->tos));
+                out = sb.c_str();
+            }
+            break;
+
+        case LFT_TRANSPORT_CLIENT_CONNECTION_ID:
+            if (al->tcpClient) {
+                outUint64 = al->tcpClient->id.value;
+                doUint64 = true;
             }
             break;
 
         case LFT_CLIENT_LOCAL_NFMARK:
-            if (al->tcpClient != NULL) {
-                snprintf(tmp, sizeof(tmp), "0x%x", al->tcpClient->nfmark);
-                out = tmp;
+            if (al->tcpClient) {
+                sb.appendf("0x%x", al->tcpClient->nfmark);
+                out = sb.c_str();
             }
             break;
 
         case LFT_LOCAL_LISTENING_PORT:
-            if (al->cache.port != NULL) {
-                outint = al->cache.port->s.port();
-                doint = 1;
-            } else if (al->request) {
-                outint = al->request->my_addr.port();
+            if (const auto port = FindListeningPortNumber(nullptr, al.getRaw())) {
+                outint = port;
                 doint = 1;
             }
             break;
 
         case LFT_CLIENT_LOCAL_PORT:
-            if (al->tcpClient != NULL) {
+            if (al->tcpClient) {
                 outint = al->tcpClient->local.port();
                 doint = 1;
             }
@@ -486,30 +522,46 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
 
         case LFT_SERVER_LOCAL_IP_OLD_27:
         case LFT_SERVER_LOCAL_IP:
-            if (al->hier.tcpServer != NULL) {
-                out = al->hier.tcpServer->local.toStr(tmp,sizeof(tmp));
-            }
+            if (al->hier.tcpServer)
+                out = al->hier.tcpServer->local.toStr(tmp, sizeof(tmp));
             break;
 
         case LFT_SERVER_LOCAL_PORT:
-            if (al->hier.tcpServer != NULL) {
+            if (al->hier.tcpServer) {
                 outint = al->hier.tcpServer->local.port();
                 doint = 1;
             }
-
             break;
 
         case LFT_SERVER_LOCAL_TOS:
-            if (al->hier.tcpServer != NULL) {
-                snprintf(tmp, sizeof(tmp), "0x%x", (uint32_t)al->hier.tcpServer->tos);
-                out = tmp;
+            if (al->hier.tcpServer) {
+                sb.appendf("0x%x", static_cast<uint32_t>(al->hier.tcpServer->tos));
+                out = sb.c_str();
             }
             break;
 
         case LFT_SERVER_LOCAL_NFMARK:
-            if (al->hier.tcpServer != NULL) {
-                snprintf(tmp, sizeof(tmp), "0x%x", al->hier.tcpServer->nfmark);
-                out = tmp;
+            if (al->hier.tcpServer) {
+                sb.appendf("0x%x", al->hier.tcpServer->nfmark);
+                out = sb.c_str();
+            }
+            break;
+
+        case LFT_CLIENT_HANDSHAKE:
+            if (al->request && al->request->clientConnectionManager.valid()) {
+                const auto &handshake = al->request->clientConnectionManager->preservedClientData;
+                if (const auto rawLength = handshake.length()) {
+                    // add 1 byte to optimize the c_str() conversion below
+                    char *buf = sb.rawAppendStart(base64_encode_len(rawLength) + 1);
+
+                    struct base64_encode_ctx ctx;
+                    base64_encode_init(&ctx);
+                    auto encLength = base64_encode_update(&ctx, buf, rawLength, reinterpret_cast<const uint8_t*>(handshake.rawContent()));
+                    encLength += base64_encode_final(&ctx, buf + encLength);
+
+                    sb.rawAppendFinish(buf, encLength);
+                    out = sb.c_str();
+                }
             }
             break;
 
@@ -525,10 +577,8 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 
         case LFT_TIME_LOCALTIME:
-
         case LFT_TIME_GMT: {
             const char *spec;
-
             struct tm *t;
             spec = fmt->data.string;
 
@@ -544,10 +594,8 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             }
 
             strftime(tmp, sizeof(tmp), spec, t);
-
             out = tmp;
         }
-
         break;
 
         case LFT_TIME_START:
@@ -555,27 +603,35 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             doSec = 1;
             break;
 
+        case LFT_BUSY_TIME: {
+            const auto &stopwatch = al->busyTime;
+            if (stopwatch.ran()) {
+                // make sure total() returns nanoseconds compatible with outoff
+                using nanos = std::chrono::duration<decltype(outoff), std::nano>;
+                const nanos n = stopwatch.total();
+                outoff = n.count();
+                dooff = true;
+            }
+        }
+        break;
+
         case LFT_TIME_TO_HANDLE_REQUEST:
             outtv = al->cache.trTime;
             doMsec = 1;
             break;
 
         case LFT_PEER_RESPONSE_TIME:
-            if (al->hier.peer_response_time.tv_sec ==  -1) {
-                out = "-";
-            } else {
-                outtv = al->hier.peer_response_time;
+            struct timeval peerResponseTime;
+            if (al->hier.peerResponseTime(peerResponseTime)) {
+                outtv = peerResponseTime;
                 doMsec = 1;
             }
             break;
 
         case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME: {
-            timeval total_response_time;
-            al->hier.totalResponseTime(total_response_time);
-            if (total_response_time.tv_sec == -1) {
-                out = "-";
-            } else {
-                outtv = total_response_time;
+            struct timeval totalResponseTime;
+            if (al->hier.totalResponseTime(totalResponseTime)) {
+                outtv = totalResponseTime;
                 doMsec = 1;
             }
         }
@@ -584,7 +640,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         case LFT_DNS_WAIT_TIME:
             if (al->request && al->request->dnsWait >= 0) {
                 // TODO: microsecond precision for dns wait time.
-                // Convert miliseconds to timeval struct:
+                // Convert milliseconds to timeval struct:
                 outtv.tv_sec = al->request->dnsWait / 1000;
                 outtv.tv_usec = (al->request->dnsWait % 1000) * 1000;
                 doMsec = 1;
@@ -592,95 +648,81 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 
         case LFT_REQUEST_HEADER:
-
-            if (al->request)
-                sb = al->request->header.getByName(fmt->data.header.header);
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
+            if (const Http::Message *msg = actualRequestHeader(al)) {
+                sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
         case LFT_ADAPTED_REQUEST_HEADER:
-
-            if (al->adapted_request)
-                sb = al->adapted_request->header.getByName(fmt->data.header.header);
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
+            if (al->adapted_request) {
+                sb = StringToSBuf(al->adapted_request->header.getByName(fmt->data.header.header));
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
         case LFT_REPLY_HEADER:
-            if (al->reply)
-                sb = al->reply->header.getByName(fmt->data.header.header);
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
+            if (const Http::Message *msg = actualReplyHeader(al)) {
+                sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
 #if USE_ADAPTATION
         case LFT_ADAPTATION_SUM_XACT_TIMES:
             if (al->request) {
                 Adaptation::History::Pointer ah = al->request->adaptHistory();
-                if (ah != NULL)
+                if (ah) {
                     ah->sumLogString(fmt->data.string, sb);
-                out = sb.termedBuf();
+                    out = sb.c_str();
+                }
             }
             break;
 
         case LFT_ADAPTATION_ALL_XACT_TIMES:
             if (al->request) {
                 Adaptation::History::Pointer ah = al->request->adaptHistory();
-                if (ah != NULL)
+                if (ah) {
                     ah->allLogString(fmt->data.string, sb);
-                out = sb.termedBuf();
+                    out = sb.c_str();
+                }
             }
             break;
 
         case LFT_ADAPTATION_LAST_HEADER:
             if (al->request) {
                 const Adaptation::History::Pointer ah = al->request->adaptHistory();
-                if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
-                    sb = ah->allMeta.getByName(fmt->data.header.header);
+                if (ah) { // XXX: add adapt::<all_h but use lastMeta here
+                    sb = StringToSBuf(ah->allMeta.getByName(fmt->data.header.header));
+                    out = sb.c_str();
+                    quote = 1;
+                }
             }
-
-            // XXX: here and elsewhere: move such code inside the if guard
-            out = sb.termedBuf();
-
-            quote = 1;
-
             break;
 
         case LFT_ADAPTATION_LAST_HEADER_ELEM:
             if (al->request) {
                 const Adaptation::History::Pointer ah = al->request->adaptHistory();
-                if (ah != NULL) // XXX: add adapt::<all_h but use lastMeta here
+                if (ah) { // XXX: add adapt::<all_h but use lastMeta here
                     sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+                    out = sb.c_str();
+                    quote = 1;
+                }
             }
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
             break;
 
         case LFT_ADAPTATION_LAST_ALL_HEADERS:
             out = al->adapt.last_meta;
-
             quote = 1;
-
             break;
 #endif
 
 #if ICAP_CLIENT
         case LFT_ICAP_ADDR:
-            if (!out)
-                out = al->icap.hostAddr.toStr(tmp,1024);
+            out = al->icap.hostAddr.toStr(tmp, sizeof(tmp));
             break;
 
         case LFT_ICAP_SERV_NAME:
@@ -715,21 +757,19 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 
         case LFT_ICAP_REQ_HEADER:
-            if (NULL != al->icap.request) {
-                sb = al->icap.request->header.getByName(fmt->data.header.header);
-                out = sb.termedBuf();
+            if (al->icap.request) {
+                sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REQ_HEADER_ELEM:
-            if (al->icap.request)
+            if (al->icap.request) {
                 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
         case LFT_ICAP_REQ_ALL_HEADERS:
@@ -738,30 +778,28 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
                 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
                     sb.append(e->name);
                     sb.append(": ");
-                    sb.append(e->value);
+                    sb.append(StringToSBuf(e->value));
                     sb.append("\r\n");
                 }
-                out = sb.termedBuf();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REP_HEADER:
-            if (NULL != al->icap.reply) {
-                sb = al->icap.reply->header.getByName(fmt->data.header.header);
-                out = sb.termedBuf();
+            if (al->icap.reply) {
+                sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_ICAP_REP_HEADER_ELEM:
-            if (NULL != al->icap.reply)
+            if (al->icap.reply) {
                 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
         case LFT_ICAP_REP_ALL_HEADERS:
@@ -770,10 +808,10 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
                 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
                     sb.append(e->name);
                     sb.append(": ");
-                    sb.append(e->value);
+                    sb.append(StringToSBuf(e->value));
                     sb.append("\r\n");
                 }
-                out = sb.termedBuf();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
@@ -803,115 +841,139 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 #endif
         case LFT_REQUEST_HEADER_ELEM:
-            if (al->request)
-                sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+            if (const Http::Message *msg = actualRequestHeader(al)) {
+                sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+                out = sb.c_str();
+                quote = 1;
+            }
+            break;
 
-            out = sb.termedBuf();
+        case LFT_PROXY_PROTOCOL_RECEIVED_HEADER:
+            if (al->proxyProtocolHeader) {
+                sb = al->proxyProtocolHeader->getValues(fmt->data.headerId, fmt->data.header.separator);
+                out = sb.c_str();
+                quote = 1;
+            }
+            break;
 
-            quote = 1;
+        case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS:
+            if (al->proxyProtocolHeader) {
+                sb = al->proxyProtocolHeader->toMime();
+                out = sb.c_str();
+                quote = 1;
+            }
+            break;
 
+        case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM:
+            if (al->proxyProtocolHeader) {
+                sb = al->proxyProtocolHeader->getElem(fmt->data.headerId, fmt->data.header.element, fmt->data.header.separator);
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
         case LFT_ADAPTED_REQUEST_HEADER_ELEM:
-            if (al->adapted_request)
+            if (al->adapted_request) {
                 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
         case LFT_REPLY_HEADER_ELEM:
-            if (al->reply)
-                sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
-
-            out = sb.termedBuf();
-
-            quote = 1;
-
+            if (const Http::Message *msg = actualReplyHeader(al)) {
+                sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
+                out = sb.c_str();
+                quote = 1;
+            }
             break;
 
         case LFT_REQUEST_ALL_HEADERS:
-            out = al->headers.request;
-
-            quote = 1;
-
+#if ICAP_CLIENT
+            if (al->icap.reqMethod == Adaptation::methodRespmod) {
+                // XXX: since AccessLogEntry::Headers lacks virgin response
+                // headers, do nothing for now
+                out = nullptr;
+            } else
+#endif
+            {
+                // just headers without start-line and CRLF
+                // XXX: reconcile with '<h'
+                out = al->headers.request;
+                quote = 1;
+            }
             break;
 
         case LFT_ADAPTED_REQUEST_ALL_HEADERS:
+            // just headers without start-line and CRLF
+            // XXX: reconcile with '<h'
             out = al->headers.adapted_request;
-
             quote = 1;
-
             break;
 
-        case LFT_REPLY_ALL_HEADERS:
-            out = al->headers.reply;
-
+        case LFT_REPLY_ALL_HEADERS: {
+            MemBuf allHeaders;
+            allHeaders.init();
+            // status-line + headers + CRLF
+            // XXX: reconcile with '>h' and '>ha'
+            al->packReplyHeaders(allHeaders);
+            sb.assign(allHeaders.content(), allHeaders.contentSize());
+            out = sb.c_str();
+#if ICAP_CLIENT
+            if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
+                out = al->headers.adapted_request;
+#endif
             quote = 1;
-
-            break;
+        }
+        break;
 
         case LFT_USER_NAME:
 #if USE_AUTH
-            if (al->request && al->request->auth_user_request != NULL)
+            if (al->request && al->request->auth_user_request)
                 out = strOrNull(al->request->auth_user_request->username());
 #endif
             if (!out && al->request && al->request->extacl_user.size()) {
                 if (const char *t = al->request->extacl_user.termedBuf())
                     out = t;
             }
-
             if (!out)
-                out = strOrNull(al->cache.extuser);
-
+                out = strOrNull(al->getExtUser());
 #if USE_OPENSSL
             if (!out)
                 out = strOrNull(al->cache.ssluser);
 #endif
             if (!out)
-                out = strOrNull(al->cache.rfc931);
+                out = strOrNull(al->getClientIdent());
             break;
 
         case LFT_USER_LOGIN:
 #if USE_AUTH
-            if (al->request && al->request->auth_user_request != NULL)
+            if (al->request && al->request->auth_user_request)
                 out = strOrNull(al->request->auth_user_request->username());
 #endif
             break;
 
         case LFT_USER_IDENT:
-            out = strOrNull(al->cache.rfc931);
+            out = strOrNull(al->getClientIdent());
             break;
 
         case LFT_USER_EXTERNAL:
-            if (al->request && al->request->extacl_user.size()) {
-                if (const char *t = al->request->extacl_user.termedBuf())
-                    out = t;
-            }
-
-            if (!out)
-                out = strOrNull(al->cache.extuser);
+            out = strOrNull(al->getExtUser());
             break;
 
         /* case LFT_USER_REALM: */
         /* case LFT_USER_SCHEME: */
 
         // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
-        // but compiler complains if ommited
+        // but compiler complains if omitted
         case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
         case LFT_HTTP_SENT_STATUS_CODE:
             outint = al->http.code;
-
             doint = 1;
-
             break;
 
         case LFT_HTTP_RECEIVED_STATUS_CODE:
-            if (al->hier.peer_reply_status == Http::scNone) {
-                out = "-";
-            } else {
+            if (al->hier.peer_reply_status != Http::scNone) {
                 outint = al->hier.peer_reply_status;
                 doint = 1;
             }
@@ -936,67 +998,54 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             break;
 
         case LFT_SQUID_ERROR:
-            if (al->request && al->request->errType != ERR_NONE)
-                out = errorPageName(al->request->errType);
+            if (const auto error = al->error())
+                out = errorPageName(error->category);
             break;
 
         case LFT_SQUID_ERROR_DETAIL:
-#if USE_OPENSSL
-            if (al->request && al->request->errType == ERR_SECURE_CONNECT_FAIL) {
-                if (! (out = Ssl::GetErrorName(al->request->errDetail)))
-                    out = sslErrorName(al->request->errDetail, tmp, sizeof(tmp));
-            } else
-#endif
-                if (al->request && al->request->errDetail != ERR_DETAIL_NONE) {
-                    if (al->request->errDetail > ERR_DETAIL_START && al->request->errDetail < ERR_DETAIL_MAX)
-                        out = errorDetailName(al->request->errDetail);
-                    else {
-                        if (al->request->errDetail >= ERR_DETAIL_EXCEPTION_START)
-                            snprintf(tmp, sizeof(tmp), "%s=0x%X",
-                                     errorDetailName(al->request->errDetail), (uint32_t) al->request->errDetail);
-                        else
-                            snprintf(tmp, sizeof(tmp), "%s=%d",
-                                     errorDetailName(al->request->errDetail), al->request->errDetail);
-                        out = tmp;
-                    }
+            if (const auto error = al->error()) {
+                if (const auto detail = error->detail) {
+                    sb = detail->brief();
+                    out = sb.c_str();
                 }
+            }
             break;
 
         case LFT_SQUID_HIERARCHY:
             if (al->hier.ping.timedout)
                 mb.append("TIMEOUT_", 8);
-
             out = hier_code_str[al->hier.code];
+            break;
 
+        case LFT_SQUID_REQUEST_ATTEMPTS:
+            outint = al->requestAttempts;
+            doint = 1;
             break;
 
         case LFT_MIME_TYPE:
             out = al->http.content_type;
-
             break;
 
         case LFT_CLIENT_REQ_METHOD:
             if (al->request) {
-                const SBuf &s = al->request->method.image();
-                sb.append(s.rawContent(), s.length());
-                out = sb.termedBuf();
+                sb = al->request->method.image();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_CLIENT_REQ_URI:
-            // original client URI
-            if (al->request) {
-                const SBuf &s = al->request->effectiveRequestUri();
-                sb.append(s.rawContent(), s.length());
-                out = sb.termedBuf();
+            if (const auto uri = al->effectiveVirginUrl()) {
+                sb = *uri;
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_CLIENT_REQ_URLSCHEME:
             if (al->request) {
-                out = al->request->url.getScheme().c_str();
+                sb = al->request->url.getScheme().image();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
@@ -1018,47 +1067,44 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         case LFT_REQUEST_URLPATH_OLD_31:
         case LFT_CLIENT_REQ_URLPATH:
             if (al->request) {
-                SBuf s = al->request->url.path();
-                out = s.c_str();
+                sb = al->request->url.path();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_CLIENT_REQ_VERSION:
             if (al->request) {
-                snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->request->http_ver.major, (int) al->request->http_ver.minor);
-                out = tmp;
+                sb.appendf("%u.%u", al->request->http_ver.major, al->request->http_ver.minor);
+                out = sb.c_str();
             }
             break;
 
         case LFT_REQUEST_METHOD:
-        {
-            const SBuf s(al->getLogMethod());
-            sb.append(s.rawContent(), s.length());
-            out = sb.termedBuf();
-            quote = 1;
-        }
-        break;
+            if (al->hasLogMethod()) {
+                sb = al->getLogMethod();
+                out = sb.c_str();
+                quote = 1;
+            }
+            break;
 
         case LFT_REQUEST_URI:
             if (!al->url.isEmpty()) {
-                const SBuf &s = al->url;
-                sb.append(s.rawContent(), s.length());
-                out = sb.termedBuf();
+                sb = al->url;
+                out = sb.c_str();
             }
             break;
 
         case LFT_REQUEST_VERSION_OLD_2X:
         case LFT_REQUEST_VERSION:
-            snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
-            out = tmp;
+            sb.appendf("%u.%u", al->http.version.major, al->http.version.minor);
+            out = sb.c_str();
             break;
 
         case LFT_SERVER_REQ_METHOD:
             if (al->adapted_request) {
-                const SBuf &s = al->adapted_request->method.image();
-                sb.append(s.rawContent(), s.length());
-                out = sb.termedBuf();
+                sb = al->adapted_request->method.image();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
@@ -1066,16 +1112,16 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         case LFT_SERVER_REQ_URI:
             // adapted request URI sent to server/peer
             if (al->adapted_request) {
-                const SBuf &s = al->adapted_request->effectiveRequestUri();
-                sb.append(s.rawContent(), s.length());
-                out = sb.termedBuf();
+                sb = al->adapted_request->effectiveRequestUri();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_SERVER_REQ_URLSCHEME:
             if (al->adapted_request) {
-                out = al->adapted_request->url.getScheme().c_str();
+                sb = al->adapted_request->url.getScheme().image();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
@@ -1096,17 +1142,17 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
 
         case LFT_SERVER_REQ_URLPATH:
             if (al->adapted_request) {
-                SBuf s = al->adapted_request->url.path();
-                out = s.c_str();
+                sb = al->adapted_request->url.path();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_SERVER_REQ_VERSION:
             if (al->adapted_request) {
-                snprintf(tmp, sizeof(tmp), "%d.%d",
-                         (int) al->adapted_request->http_ver.major,
-                         (int) al->adapted_request->http_ver.minor);
+                sb.appendf("%u.%u",
+                           al->adapted_request->http_ver.major,
+                           al->adapted_request->http_ver.minor);
                 out = tmp;
             }
             break;
@@ -1131,16 +1177,12 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
 
         case LFT_REPLY_HIGHOFFSET:
             outoff = al->cache.highOffset;
-
             dooff = 1;
-
             break;
 
         case LFT_REPLY_OBJECTSIZE:
             outoff = al->cache.objectSize;
-
             dooff = 1;
-
             break;
 
         case LFT_ADAPTED_REPLY_SIZE_HEADERS:
@@ -1158,19 +1200,17 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         /*case LFT_SERVER_IO_SIZE_TOTAL: */
 
         case LFT_TAG:
-            if (al->request)
+            if (al->request) {
                 out = al->request->tag.termedBuf();
-
-            quote = 1;
-
+                quote = 1;
+            }
             break;
 
         case LFT_EXT_LOG:
-            if (al->request)
+            if (al->request) {
                 out = al->request->extacl_log.termedBuf();
-
-            quote = 1;
-
+                quote = 1;
+            }
             break;
 
         case LFT_SEQUENCE_NUMBER:
@@ -1190,8 +1230,10 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             if (al->request) {
                 ConnStateData *conn = al->request->clientConnectionManager.get();
                 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
-                    if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
-                        out = sslGetUserCertificatePEM(ssl);
+                    if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
+                        sb = sslGetUserCertificatePEM(ssl);
+                        out = sb.c_str();
+                    }
                 }
             }
             break;
@@ -1200,8 +1242,10 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             if (al->request) {
                 ConnStateData *conn = al->request->clientConnectionManager.get();
                 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
-                    if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
-                        out = sslGetUserCertificatePEM(ssl);
+                    if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
+                        sb = sslGetUserCertificatePEM(ssl);
+                        out = sb.c_str();
+                    }
                 }
             }
             break;
@@ -1211,7 +1255,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
                 ConnStateData *conn = al->request->clientConnectionManager.get();
                 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
                     if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
-                        out = sslGetUserAttribute(ssl, format->data.header.header);
+                        out = sslGetUserAttribute(ssl, fmt->data.header.header);
                 }
             }
             break;
@@ -1221,34 +1265,32 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
                 ConnStateData *conn = al->request->clientConnectionManager.get();
                 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
                     if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
-                        out = sslGetCAAttribute(ssl, format->data.header.header);
+                        out = sslGetCAAttribute(ssl, fmt->data.header.header);
                 }
             }
             break;
 
         case LFT_SSL_USER_CERT_SUBJECT:
-            if (X509 *cert = al->cache.sslClientCert.get()) {
-                if (X509_NAME *subject = X509_get_subject_name(cert)) {
-                    X509_NAME_oneline(subject, tmp, sizeof(tmp));
-                    out = tmp;
-                }
+            if (const auto &cert = al->cache.sslClientCert) {
+                sb = Security::SubjectName(*cert);
+                out = sb.c_str();
             }
             break;
 
         case LFT_SSL_USER_CERT_ISSUER:
-            if (X509 *cert = al->cache.sslClientCert.get()) {
-                if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
-                    X509_NAME_oneline(issuer, tmp, sizeof(tmp));
-                    out = tmp;
-                }
+            if (const auto &cert = al->cache.sslClientCert) {
+                sb = Security::IssuerName(*cert);
+                out = sb.c_str();
             }
             break;
 
         case LFT_SSL_CLIENT_SNI:
             if (al->request && al->request->clientConnectionManager.valid()) {
-                if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
-                    if (!srvBump->clientSni.isEmpty())
-                        out = srvBump->clientSni.c_str();
+                if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
+                    if (!conn->tlsClientSni().isEmpty()) {
+                        sb = conn->tlsClientSni();
+                        out = sb.c_str();
+                    }
                 }
             }
             break;
@@ -1257,125 +1299,128 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             if (al->request && al->request->clientConnectionManager.valid()) {
                 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
                     const char *separator = fmt->data.string ? fmt->data.string : ":";
-                    for (Ssl::CertErrors const *sslError = srvBump->sslErrors(); sslError != NULL;  sslError = sslError->next) {
-                        if (sb.size())
+                    for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
+                        if (!sb.isEmpty())
                             sb.append(separator);
-                        if (const char *errorName = Ssl::GetErrorName(sslError->element.code))
-                            sb.append(errorName);
-                        else
-                            sb.append(sslErrorName(sslError->element.code, tmp, sizeof(tmp)));
-                        if (sslError->element.depth >= 0) {
-                            snprintf(tmp, sizeof(tmp), "@depth=%d", sslError->element.depth);
-                            sb.append(tmp);
-                        }
+                        sb.append(Ssl::GetErrorName(sslError->element.code, true));
+                        if (sslError->element.depth >= 0)
+                            sb.appendf("@depth=%d", sslError->element.depth);
                     }
-                    if (sb.size())
-                        out = sb.termedBuf();
+                    if (!sb.isEmpty())
+                        out = sb.c_str();
                 }
             }
             break;
 
         case LFT_SSL_SERVER_CERT_ISSUER:
         case LFT_SSL_SERVER_CERT_SUBJECT:
+        case LFT_SSL_SERVER_CERT_WHOLE:
             if (al->request && al->request->clientConnectionManager.valid()) {
                 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
                     if (X509 *serverCert = srvBump->serverCert.get()) {
                         if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
                             out = Ssl::GetX509UserAttribute(serverCert, "DN");
-                        else
+                        else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
                             out = Ssl::GetX509CAAttribute(serverCert, "DN");
+                        else {
+                            assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
+                            sb = Ssl::GetX509PEM(serverCert);
+                            out = sb.c_str();
+                            quote = 1;
+                        }
                     }
                 }
             }
             break;
 
         case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
-            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+            if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
                 out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
             break;
 
         case LFT_TLS_SERVER_NEGOTIATED_VERSION:
-            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+            if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
                 out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
             break;
 
         case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
-            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+            if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
                 out = al->tcpClient->hasTlsNegotiations()->helloVersion();
             break;
 
         case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
-            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+            if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
                 out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
             break;
 
         case LFT_TLS_CLIENT_SUPPORTED_VERSION:
-            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+            if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
                 out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
             break;
 
         case LFT_TLS_SERVER_SUPPORTED_VERSION:
-            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+            if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
                 out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
             break;
 
         case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
-            if (al->tcpClient != nullptr && al->tcpClient->hasTlsNegotiations())
+            if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
                 out = al->tcpClient->hasTlsNegotiations()->cipherName();
             break;
 
         case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
-            if (al->hier.tcpServer != nullptr && al->hier.tcpServer->hasTlsNegotiations())
+            if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
                 out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
             break;
 #endif
 
         case LFT_REQUEST_URLGROUP_OLD_2X:
             assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
+            break;
 
         case LFT_NOTE:
             tmp[0] = fmt->data.header.separator;
             tmp[1] = '\0';
             if (fmt->data.header.header && *fmt->data.header.header) {
                 const char *separator = tmp;
+                static SBuf note;
 #if USE_ADAPTATION
                 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
-                if (ah != NULL && ah->metaHeaders != NULL) {
-                    if (const char *meta = ah->metaHeaders->find(fmt->data.header.header, separator))
-                        sb.append(meta);
+                if (ah && ah->metaHeaders) {
+                    if (ah->metaHeaders->find(note, fmt->data.header.header, separator))
+                        sb.append(note);
                 }
 #endif
-                if (al->notes != NULL) {
-                    if (const char *note = al->notes->find(fmt->data.header.header, separator)) {
-                        if (sb.size())
+                if (al->notes) {
+                    if (al->notes->find(note, fmt->data.header.header, separator)) {
+                        if (!sb.isEmpty())
                             sb.append(separator);
                         sb.append(note);
                     }
                 }
-                out = sb.termedBuf();
+                out = sb.c_str();
                 quote = 1;
             } else {
                 // if no argument given use default "\r\n" as notes separator
                 const char *separator = fmt->data.string ? tmp : "\r\n";
 #if USE_ADAPTATION
                 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
-                if (ah != NULL && ah->metaHeaders != NULL && !ah->metaHeaders->empty())
+                if (ah && ah->metaHeaders && !ah->metaHeaders->empty())
                     sb.append(ah->metaHeaders->toString(separator));
 #endif
-                if (al->notes != NULL && !al->notes->empty())
+                if (al->notes && !al->notes->empty())
                     sb.append(al->notes->toString(separator));
 
-                out = sb.termedBuf();
+                out = sb.c_str();
                 quote = 1;
             }
             break;
 
         case LFT_CREDENTIALS:
 #if USE_AUTH
-            if (al->request && al->request->auth_user_request != NULL)
+            if (al->request && al->request->auth_user_request)
                 out = strOrNull(al->request->auth_user_request->credentialsStr());
 #endif
-
             break;
 
         case LFT_PERCENT:
@@ -1390,32 +1435,47 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             if (!al->lastAclData.isEmpty())
                 out = al->lastAclData.c_str();
             break;
+
+        case LFT_MASTER_XACTION:
+            if (al->request) {
+                doUint64 = true;
+                outUint64 = static_cast<uint64_t>(al->request->masterXaction->id.value);
+                break;
+            }
         }
 
         if (dooff) {
-            snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
-            out = tmp;
+            sb.appendf("%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
+            out = sb.c_str();
 
         } else if (doint) {
-            snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
-            out = tmp;
+            sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
+            out = sb.c_str();
+        } else if (doUint64) {
+            sb.appendf("%0*" PRIu64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outUint64);
+            out = sb.c_str();
         } else if (doMsec) {
             if (fmt->widthMax < 0) {
-                snprintf(tmp, sizeof(tmp), "%0*ld", fmt->widthMin , tvToMsec(outtv));
+                sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, tvToMsec(outtv));
             } else {
                 int precision = fmt->widthMax;
-                snprintf(tmp, sizeof(tmp), "%0*" PRId64 ".%0*" PRId64 "", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(outtv.tv_sec * 1000 + outtv.tv_usec / 1000), precision, static_cast<int64_t>((outtv.tv_usec % 1000 )* (1000 / fmt->divisor)));
+                sb.appendf("%0*" PRId64 ".%0*" PRId64 "", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(outtv.tv_sec * 1000 + outtv.tv_usec / 1000), precision, static_cast<int64_t>((outtv.tv_usec % 1000 )* (1000 / fmt->divisor)));
             }
-            out = tmp;
+            out = sb.c_str();
         } else if (doSec) {
             int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
-            snprintf(tmp, sizeof(tmp), "%0*" PRId64 ".%0*d", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(outtv.tv_sec), precision, (int)(outtv.tv_usec / fmt->divisor));
-            out = tmp;
+            sb.appendf("%0*" PRId64 ".%0*d", fmt->zero && (fmt->widthMin - precision - 1 >= 0) ? fmt->widthMin - precision - 1 : 0, static_cast<int64_t>(outtv.tv_sec), precision, (int)(outtv.tv_usec / fmt->divisor));
+            out = sb.c_str();
         }
 
         if (out && *out) {
             if (quote || fmt->quote != LOG_QUOTE_NONE) {
-                char *newout = NULL;
+                // Do not write to the tmp buffer because it may contain the to-be-quoted value.
+                static char quotedOut[2 * sizeof(tmp)];
+                static_assert(sizeof(quotedOut) > 0, "quotedOut has zero length");
+                quotedOut[0] = '\0';
+
+                char *newout = nullptr;
                 int newfree = 0;
 
                 switch (fmt->quote) {
@@ -1430,7 +1490,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
                         newout = (char *)xmalloc(out_len);
                         newfree = 1;
                     } else
-                        newout = tmp;
+                        newout = quotedOut;
                     log_quoted_string(out, newout);
                 }
                 break;
@@ -1469,7 +1529,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             }
 
             // enforce width limits if configured
-            const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec;
+            const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec && !doUint64;
             if (haveMaxWidth || fmt->widthMin) {
                 const int minWidth = fmt->widthMin >= 0 ?
                                      fmt->widthMin :0;
@@ -1489,7 +1549,7 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
         if (fmt->space)
             mb.append(" ", 1);
 
-        sb.clean();
+        sb.clear();
 
         if (dofree)
             safe_free(out);