2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
10 #include "AccessLogEntry.h"
12 #include "client_side.h"
13 #include "comm/Connection.h"
14 #include "error/Detail.h"
15 #include "errorpage.h"
17 #include "format/Format.h"
18 #include "format/Quoting.h"
19 #include "format/Token.h"
20 #include "http/Stream.h"
21 #include "HttpRequest.h"
23 #include "proxyp/Header.h"
25 #include "sbuf/Stream.h"
26 #include "sbuf/StringConvert.h"
27 #include "security/CertError.h"
28 #include "security/Certificate.h"
29 #include "security/NegotiationHistory.h"
33 #include "ssl/ErrorDetail.h"
34 #include "ssl/ServerBump.h"
37 /// Convert a string to NULL pointer if it is ""
38 #define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
40 const SBuf
Format::Dash("-");
42 Format::Format::Format(const char *n
) :
49 Format::Format::~Format()
51 // erase the list without consuming stack space
53 // unlink the next entry for deletion
66 Format::Format::parse(const char *def
)
68 const char *cur
, *eos
;
69 Token
*new_lt
, *last_lt
;
70 enum Quoting quote
= LOG_QUOTE_NONE
;
72 debugs(46, 2, "got definition '" << def
<< "'");
75 debugs(46, DBG_IMPORTANT
, "WARNING: existing format for '" << name
<< " " << def
<< "'");
79 /* very inefficient parser, but who cares, this needs to be simple */
80 /* First off, let's tokenize, we'll optimize in a second pass.
81 * A token can either be a %-prefixed sequence (usually a dynamic
82 * token but it can be an escaped sequence), or a string. */
84 eos
= def
+ strlen(def
);
85 format
= new_lt
= last_lt
= new Token
;
86 cur
+= new_lt
->parse(cur
, "e
);
90 last_lt
->next
= new_lt
;
92 cur
+= new_lt
->parse(cur
, "e
);
99 Format::AssembleOne(const char *token
, MemBuf
&mb
, const AccessLogEntryPointer
&ale
)
102 enum Quoting quote
= LOG_QUOTE_NONE
;
103 const auto tokenSize
= tkn
.parse(token
, "e
);
104 assert(tokenSize
> 0);
105 if (ale
!= nullptr) {
106 Format
fmt("SimpleToken");
108 fmt
.assemble(mb
, ale
, 0);
109 fmt
.format
= nullptr;
113 return static_cast<size_t>(tokenSize
);
117 Format::Format::dump(StoreEntry
* entry
, const char *directiveName
, bool eol
) const
119 debugs(46, 4, MYNAME
);
121 // loop rather than recursing to conserve stack space.
122 for (const Format
*fmt
= this; fmt
; fmt
= fmt
->next
) {
123 debugs(46, 3, "Dumping format definition for " << fmt
->name
);
125 storeAppendPrintf(entry
, "%s %s ", directiveName
, fmt
->name
);
127 for (Token
*t
= fmt
->format
; t
; t
= t
->next
) {
128 if (t
->type
== LFT_STRING
)
129 storeAppendPrintf(entry
, "%s", t
->data
.string
);
133 ByteCode_t type
= t
->type
;
141 case LFT_ADAPTATION_LAST_HEADER_ELEM
:
144 case LFT_ICAP_REQ_HEADER_ELEM
:
145 case LFT_ICAP_REP_HEADER_ELEM
:
147 case LFT_REQUEST_HEADER_ELEM
:
148 case LFT_ADAPTED_REQUEST_HEADER_ELEM
:
149 case LFT_REPLY_HEADER_ELEM
:
151 if (t
->data
.header
.separator
!= ',')
152 snprintf(argbuf
, sizeof(argbuf
), "%s:%c%s", t
->data
.header
.header
, t
->data
.header
.separator
, t
->data
.header
.element
);
154 snprintf(argbuf
, sizeof(argbuf
), "%s:%s", t
->data
.header
.header
, t
->data
.header
.element
);
159 case LFT_REQUEST_HEADER_ELEM
:
160 type
= LFT_REQUEST_HEADER_ELEM
; // XXX: remove _ELEM?
162 case LFT_ADAPTED_REQUEST_HEADER_ELEM
:
163 type
= LFT_ADAPTED_REQUEST_HEADER_ELEM
; // XXX: remove _ELEM?
165 case LFT_REPLY_HEADER_ELEM
:
166 type
= LFT_REPLY_HEADER_ELEM
; // XXX: remove _ELEM?
169 case LFT_ADAPTATION_LAST_HEADER_ELEM
:
170 type
= LFT_ADAPTATION_LAST_HEADER
;
174 case LFT_ICAP_REQ_HEADER_ELEM
:
175 type
= LFT_ICAP_REQ_HEADER
;
177 case LFT_ICAP_REP_HEADER_ELEM
:
178 type
= LFT_ICAP_REP_HEADER
;
187 case LFT_REQUEST_ALL_HEADERS
:
188 case LFT_ADAPTED_REQUEST_ALL_HEADERS
:
189 case LFT_REPLY_ALL_HEADERS
:
192 case LFT_ADAPTATION_LAST_ALL_HEADERS
:
195 case LFT_ICAP_REQ_ALL_HEADERS
:
196 case LFT_ICAP_REP_ALL_HEADERS
:
200 case LFT_REQUEST_ALL_HEADERS
:
201 type
= LFT_REQUEST_HEADER
;
203 case LFT_ADAPTED_REQUEST_ALL_HEADERS
:
204 type
= LFT_ADAPTED_REQUEST_HEADER
;
206 case LFT_REPLY_ALL_HEADERS
:
207 type
= LFT_REPLY_HEADER
;
210 case LFT_ADAPTATION_LAST_ALL_HEADERS
:
211 type
= LFT_ADAPTATION_LAST_HEADER
;
215 case LFT_ICAP_REQ_ALL_HEADERS
:
216 type
= LFT_ICAP_REQ_HEADER
;
218 case LFT_ICAP_REP_ALL_HEADERS
:
219 type
= LFT_ICAP_REP_HEADER
;
230 arg
= t
->data
.string
;
235 entry
->append("%", 1);
239 case LOG_QUOTE_QUOTES
:
240 entry
->append("\"", 1);
243 case LOG_QUOTE_MIMEBLOB
:
244 entry
->append("[", 1);
248 entry
->append("#", 1);
252 entry
->append("'", 1);
255 case LOG_QUOTE_SHELL
:
256 entry
->append("/", 1);
264 entry
->append("-", 1);
267 entry
->append("0", 1);
269 if (t
->widthMin
>= 0)
270 storeAppendPrintf(entry
, "%d", t
->widthMin
);
272 if (t
->widthMax
>= 0)
273 storeAppendPrintf(entry
, ".%d", t
->widthMax
);
276 storeAppendPrintf(entry
, "{%s}", arg
);
278 storeAppendPrintf(entry
, "%s", t
->label
);
281 entry
->append(" ", 1);
286 entry
->append("\n", 1);
292 log_quoted_string(const char *str
, char *out
)
297 int l
= strcspn(str
, "\"\\\r\n\t");
344 /// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
345 /// headers from sent headers rather than failing to distinguish requests from responses.
346 /// \retval HttpReply sent to the HTTP client (access.log and default context).
347 /// \retval HttpReply received (encapsulated) from the ICAP server (icap.log context).
348 /// \retval HttpRequest received (encapsulated) from the ICAP server (icap.log context).
349 static const Http::Message
*
350 actualReplyHeader(const AccessLogEntry::Pointer
&al
)
352 const Http::Message
*msg
= al
->reply
.getRaw();
354 // al->icap.reqMethod is methodNone in access.log context
355 if (!msg
&& al
->icap
.reqMethod
== Adaptation::methodReqmod
)
356 msg
= al
->adapted_request
;
361 /// XXX: Misnamed. See actualReplyHeader().
362 /// \return HttpRequest or HttpReply for %http::>h.
363 static const Http::Message
*
364 actualRequestHeader(const AccessLogEntry::Pointer
&al
)
367 // al->icap.reqMethod is methodNone in access.log context
368 if (al
->icap
.reqMethod
== Adaptation::methodRespmod
) {
369 // XXX: for now AccessLogEntry lacks virgin response headers
377 Format::Format::assemble(MemBuf
&mb
, const AccessLogEntry::Pointer
&al
, int logSequenceNumber
) const
379 static char tmp
[1024];
382 for (Token
*fmt
= format
; fmt
; fmt
= fmt
->next
) { /* for each token */
383 const char *out
= nullptr;
390 struct timeval outtv
= {};
393 bool doUint64
= false;
394 uint64_t outUint64
= 0;
403 tmp
[0] = static_cast<char>(fmt
->data
.byteValue
);
409 out
= fmt
->data
.string
;
412 case LFT_CLIENT_IP_ADDRESS
:
413 al
->getLogClientIp(tmp
, sizeof(tmp
));
417 case LFT_CLIENT_FQDN
:
418 out
= al
->getLogClientFqdn(tmp
, sizeof(tmp
));
421 case LFT_CLIENT_PORT
:
423 outint
= al
->request
->client_addr
.port();
425 } else if (al
->tcpClient
) {
426 outint
= al
->tcpClient
->remote
.port();
433 // TODO make the ACL checklist have a direct link to any TCP details.
434 if (al
->request
&& al
->request
->clientConnectionManager
.valid() &&
435 al
->request
->clientConnectionManager
->clientConnection
) {
436 const auto &conn
= al
->request
->clientConnectionManager
->clientConnection
;
437 if (conn
->remote
.isIPv4())
438 conn
->remoteEui48
.encode(tmp
, sizeof(tmp
));
440 conn
->remoteEui64
.encode(tmp
, sizeof(tmp
));
446 case LFT_EXT_ACL_CLIENT_EUI48
:
448 if (al
->request
&& al
->request
->clientConnectionManager
.valid() &&
449 al
->request
->clientConnectionManager
->clientConnection
&&
450 al
->request
->clientConnectionManager
->clientConnection
->remote
.isIPv4()) {
451 al
->request
->clientConnectionManager
->clientConnection
->remoteEui48
.encode(tmp
, sizeof(tmp
));
457 case LFT_EXT_ACL_CLIENT_EUI64
:
459 if (al
->request
&& al
->request
->clientConnectionManager
.valid() &&
460 al
->request
->clientConnectionManager
->clientConnection
&&
461 !al
->request
->clientConnectionManager
->clientConnection
->remote
.isIPv4()) {
462 al
->request
->clientConnectionManager
->clientConnection
->remoteEui64
.encode(tmp
, sizeof(tmp
));
468 case LFT_SERVER_IP_ADDRESS
:
469 if (al
->hier
.tcpServer
)
470 out
= al
->hier
.tcpServer
->remote
.toStr(tmp
, sizeof(tmp
));
473 case LFT_SERVER_FQDN_OR_PEER_NAME
:
477 case LFT_SERVER_PORT
:
478 if (al
->hier
.tcpServer
) {
479 outint
= al
->hier
.tcpServer
->remote
.port();
484 case LFT_LOCAL_LISTENING_IP
:
485 if (const auto addr
= FindListeningPortAddress(nullptr, al
.getRaw()))
486 out
= addr
->toStr(tmp
, sizeof(tmp
));
489 case LFT_CLIENT_LOCAL_IP
:
491 out
= al
->tcpClient
->local
.toStr(tmp
, sizeof(tmp
));
494 case LFT_CLIENT_LOCAL_TOS
:
496 sb
.appendf("0x%x", static_cast<uint32_t>(al
->tcpClient
->tos
));
501 case LFT_TRANSPORT_CLIENT_CONNECTION_ID
:
503 outUint64
= al
->tcpClient
->id
.value
;
508 case LFT_CLIENT_LOCAL_NFMARK
:
510 sb
.appendf("0x%x", al
->tcpClient
->nfmark
);
515 case LFT_LOCAL_LISTENING_PORT
:
516 if (const auto port
= FindListeningPortNumber(nullptr, al
.getRaw())) {
522 case LFT_CLIENT_LOCAL_PORT
:
524 outint
= al
->tcpClient
->local
.port();
529 case LFT_SERVER_LOCAL_IP_OLD_27
:
530 case LFT_SERVER_LOCAL_IP
:
531 if (al
->hier
.tcpServer
)
532 out
= al
->hier
.tcpServer
->local
.toStr(tmp
, sizeof(tmp
));
535 case LFT_SERVER_LOCAL_PORT
:
536 if (al
->hier
.tcpServer
) {
537 outint
= al
->hier
.tcpServer
->local
.port();
542 case LFT_SERVER_LOCAL_TOS
:
543 if (al
->hier
.tcpServer
) {
544 sb
.appendf("0x%x", static_cast<uint32_t>(al
->hier
.tcpServer
->tos
));
549 case LFT_SERVER_LOCAL_NFMARK
:
550 if (al
->hier
.tcpServer
) {
551 sb
.appendf("0x%x", al
->hier
.tcpServer
->nfmark
);
556 case LFT_CLIENT_HANDSHAKE
:
557 if (al
->request
&& al
->request
->clientConnectionManager
.valid()) {
558 const auto &handshake
= al
->request
->clientConnectionManager
->preservedClientData
;
559 if (const auto rawLength
= handshake
.length()) {
560 // add 1 byte to optimize the c_str() conversion below
561 char *buf
= sb
.rawAppendStart(base64_encode_len(rawLength
) + 1);
563 struct base64_encode_ctx ctx
;
564 base64_encode_init(&ctx
);
565 auto encLength
= base64_encode_update(&ctx
, buf
, rawLength
, reinterpret_cast<const uint8_t*>(handshake
.rawContent()));
566 encLength
+= base64_encode_final(&ctx
, buf
+ encLength
);
568 sb
.rawAppendFinish(buf
, encLength
);
574 case LFT_TIME_SECONDS_SINCE_EPOCH
:
575 // some platforms store time in 32-bit, some 64-bit...
576 outoff
= static_cast<int64_t>(current_time
.tv_sec
);
580 case LFT_TIME_SUBSECOND
:
581 outint
= current_time
.tv_usec
/ fmt
->divisor
;
585 case LFT_TIME_LOCALTIME
:
589 spec
= fmt
->data
.string
;
591 if (fmt
->type
== LFT_TIME_LOCALTIME
) {
593 spec
= "%d/%b/%Y:%H:%M:%S %z";
594 t
= localtime(&squid_curtime
);
597 spec
= "%d/%b/%Y:%H:%M:%S";
599 t
= gmtime(&squid_curtime
);
602 strftime(tmp
, sizeof(tmp
), spec
, t
);
608 outtv
= al
->cache
.start_time
;
612 case LFT_BUSY_TIME
: {
613 const auto &stopwatch
= al
->busyTime
;
614 if (stopwatch
.ran()) {
615 // make sure total() returns nanoseconds compatible with outoff
616 using nanos
= std::chrono::duration
<decltype(outoff
), std::nano
>;
617 const nanos n
= stopwatch
.total();
624 case LFT_TIME_TO_HANDLE_REQUEST
:
625 outtv
= al
->cache
.trTime
;
629 case LFT_PEER_RESPONSE_TIME
:
630 struct timeval peerResponseTime
;
631 if (al
->hier
.peerResponseTime(peerResponseTime
)) {
632 outtv
= peerResponseTime
;
637 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME
: {
638 struct timeval totalResponseTime
;
639 if (al
->hier
.totalResponseTime(totalResponseTime
)) {
640 outtv
= totalResponseTime
;
646 case LFT_DNS_WAIT_TIME
:
647 if (al
->request
&& al
->request
->dnsWait
>= 0) {
648 // TODO: microsecond precision for dns wait time.
649 // Convert milliseconds to timeval struct:
650 outtv
.tv_sec
= al
->request
->dnsWait
/ 1000;
651 outtv
.tv_usec
= (al
->request
->dnsWait
% 1000) * 1000;
656 case LFT_REQUEST_HEADER
:
657 if (const Http::Message
*msg
= actualRequestHeader(al
)) {
658 sb
= StringToSBuf(msg
->header
.getByName(fmt
->data
.header
.header
));
664 case LFT_ADAPTED_REQUEST_HEADER
:
665 if (al
->adapted_request
) {
666 sb
= StringToSBuf(al
->adapted_request
->header
.getByName(fmt
->data
.header
.header
));
672 case LFT_REPLY_HEADER
:
673 if (const Http::Message
*msg
= actualReplyHeader(al
)) {
674 sb
= StringToSBuf(msg
->header
.getByName(fmt
->data
.header
.header
));
681 case LFT_ADAPTATION_SUM_XACT_TIMES
:
683 Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
685 ah
->sumLogString(fmt
->data
.string
, sb
);
691 case LFT_ADAPTATION_ALL_XACT_TIMES
:
693 Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
695 ah
->allLogString(fmt
->data
.string
, sb
);
701 case LFT_ADAPTATION_LAST_HEADER
:
703 const Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
704 if (ah
) { // XXX: add adapt::<all_h but use lastMeta here
705 sb
= StringToSBuf(ah
->allMeta
.getByName(fmt
->data
.header
.header
));
712 case LFT_ADAPTATION_LAST_HEADER_ELEM
:
714 const Adaptation::History::Pointer ah
= al
->request
->adaptHistory();
715 if (ah
) { // XXX: add adapt::<all_h but use lastMeta here
716 sb
= ah
->allMeta
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
723 case LFT_ADAPTATION_LAST_ALL_HEADERS
:
724 out
= al
->adapt
.last_meta
;
731 out
= al
->icap
.hostAddr
.toStr(tmp
, sizeof(tmp
));
734 case LFT_ICAP_SERV_NAME
:
735 out
= al
->icap
.serviceName
.termedBuf();
738 case LFT_ICAP_REQUEST_URI
:
739 out
= al
->icap
.reqUri
.termedBuf();
742 case LFT_ICAP_REQUEST_METHOD
:
743 out
= Adaptation::Icap::ICAP::methodStr(al
->icap
.reqMethod
);
746 case LFT_ICAP_BYTES_SENT
:
747 outoff
= al
->icap
.bytesSent
;
751 case LFT_ICAP_BYTES_READ
:
752 outoff
= al
->icap
.bytesRead
;
756 case LFT_ICAP_BODY_BYTES_READ
:
757 if (al
->icap
.bodyBytesRead
>= 0) {
758 outoff
= al
->icap
.bodyBytesRead
;
761 // else if icap.bodyBytesRead < 0, we do not have any http data,
762 // so just print a "-" (204 responses etc)
765 case LFT_ICAP_REQ_HEADER
:
766 if (al
->icap
.request
) {
767 sb
= StringToSBuf(al
->icap
.request
->header
.getByName(fmt
->data
.header
.header
));
773 case LFT_ICAP_REQ_HEADER_ELEM
:
774 if (al
->icap
.request
) {
775 sb
= al
->icap
.request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
781 case LFT_ICAP_REQ_ALL_HEADERS
:
782 if (al
->icap
.request
) {
783 HttpHeaderPos pos
= HttpHeaderInitPos
;
784 while (const HttpHeaderEntry
*e
= al
->icap
.request
->header
.getEntry(&pos
)) {
787 sb
.append(StringToSBuf(e
->value
));
795 case LFT_ICAP_REP_HEADER
:
796 if (al
->icap
.reply
) {
797 sb
= StringToSBuf(al
->icap
.reply
->header
.getByName(fmt
->data
.header
.header
));
803 case LFT_ICAP_REP_HEADER_ELEM
:
804 if (al
->icap
.reply
) {
805 sb
= al
->icap
.reply
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
811 case LFT_ICAP_REP_ALL_HEADERS
:
812 if (al
->icap
.reply
) {
813 HttpHeaderPos pos
= HttpHeaderInitPos
;
814 while (const HttpHeaderEntry
*e
= al
->icap
.reply
->header
.getEntry(&pos
)) {
817 sb
.append(StringToSBuf(e
->value
));
825 case LFT_ICAP_TR_RESPONSE_TIME
:
826 outtv
= al
->icap
.trTime
;
830 case LFT_ICAP_IO_TIME
:
831 outtv
= al
->icap
.ioTime
;
835 case LFT_ICAP_STATUS_CODE
:
836 outint
= al
->icap
.resStatus
;
840 case LFT_ICAP_OUTCOME
:
841 out
= al
->icap
.outcome
;
844 case LFT_ICAP_TOTAL_TIME
:
845 outtv
= al
->icap
.processingTime
;
849 case LFT_REQUEST_HEADER_ELEM
:
850 if (const Http::Message
*msg
= actualRequestHeader(al
)) {
851 sb
= msg
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
857 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER
:
858 if (al
->proxyProtocolHeader
) {
859 sb
= al
->proxyProtocolHeader
->getValues(fmt
->data
.headerId
, fmt
->data
.header
.separator
);
865 case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS
:
866 if (al
->proxyProtocolHeader
) {
867 sb
= al
->proxyProtocolHeader
->toMime();
873 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM
:
874 if (al
->proxyProtocolHeader
) {
875 sb
= al
->proxyProtocolHeader
->getElem(fmt
->data
.headerId
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
881 case LFT_ADAPTED_REQUEST_HEADER_ELEM
:
882 if (al
->adapted_request
) {
883 sb
= al
->adapted_request
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
889 case LFT_REPLY_HEADER_ELEM
:
890 if (const Http::Message
*msg
= actualReplyHeader(al
)) {
891 sb
= msg
->header
.getByNameListMember(fmt
->data
.header
.header
, fmt
->data
.header
.element
, fmt
->data
.header
.separator
);
897 case LFT_REQUEST_ALL_HEADERS
:
899 if (al
->icap
.reqMethod
== Adaptation::methodRespmod
) {
900 // XXX: since AccessLogEntry::Headers lacks virgin response
901 // headers, do nothing for now
906 // just headers without start-line and CRLF
907 // XXX: reconcile with '<h'
908 out
= al
->headers
.request
;
913 case LFT_ADAPTED_REQUEST_ALL_HEADERS
:
914 // just headers without start-line and CRLF
915 // XXX: reconcile with '<h'
916 out
= al
->headers
.adapted_request
;
920 case LFT_REPLY_ALL_HEADERS
: {
923 // status-line + headers + CRLF
924 // XXX: reconcile with '>h' and '>ha'
925 al
->packReplyHeaders(allHeaders
);
926 sb
.assign(allHeaders
.content(), allHeaders
.contentSize());
929 if (!out
&& al
->icap
.reqMethod
== Adaptation::methodReqmod
)
930 out
= al
->headers
.adapted_request
;
938 if (al
->request
&& al
->request
->auth_user_request
)
939 out
= strOrNull(al
->request
->auth_user_request
->username());
941 if (!out
&& al
->request
&& al
->request
->extacl_user
.size()) {
942 if (const char *t
= al
->request
->extacl_user
.termedBuf())
946 out
= strOrNull(al
->getExtUser());
949 out
= strOrNull(al
->cache
.ssluser
);
952 out
= strOrNull(al
->getClientIdent());
957 if (al
->request
&& al
->request
->auth_user_request
)
958 out
= strOrNull(al
->request
->auth_user_request
->username());
963 out
= strOrNull(al
->getClientIdent());
966 case LFT_USER_EXTERNAL
:
967 out
= strOrNull(al
->getExtUser());
970 /* case LFT_USER_REALM: */
971 /* case LFT_USER_SCHEME: */
973 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
974 // but compiler complains if omitted
975 case LFT_HTTP_SENT_STATUS_CODE_OLD_30
:
976 case LFT_HTTP_SENT_STATUS_CODE
:
977 outint
= al
->http
.code
;
981 case LFT_HTTP_RECEIVED_STATUS_CODE
:
982 if (al
->hier
.peer_reply_status
!= Http::scNone
) {
983 outint
= al
->hier
.peer_reply_status
;
987 /* case LFT_HTTP_STATUS:
988 * out = statusline->text;
992 case LFT_HTTP_BODY_BYTES_READ
:
993 if (al
->hier
.bodyBytesRead
>= 0) {
994 outoff
= al
->hier
.bodyBytesRead
;
997 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
998 // a peer server so just print a "-" (eg requests served from cache,
999 // or internal error messages).
1002 case LFT_SQUID_STATUS
:
1003 out
= al
->cache
.code
.c_str();
1006 case LFT_SQUID_ERROR
:
1007 if (const auto error
= al
->error())
1008 out
= errorPageName(error
->category
);
1011 case LFT_SQUID_ERROR_DETAIL
:
1012 if (const auto error
= al
->error()) {
1013 if (!error
->details
.empty()) {
1014 sb
= ToSBuf(error
->details
);
1020 case LFT_SQUID_HIERARCHY
:
1021 if (al
->hier
.ping
.timedout
)
1022 mb
.append("TIMEOUT_", 8);
1023 out
= hier_code_str
[al
->hier
.code
];
1026 case LFT_SQUID_REQUEST_ATTEMPTS
:
1027 outint
= al
->requestAttempts
;
1032 out
= al
->http
.content_type
;
1035 case LFT_CLIENT_REQ_METHOD
:
1037 sb
= al
->request
->method
.image();
1043 case LFT_CLIENT_REQ_URI
:
1044 if (const auto uri
= al
->effectiveVirginUrl()) {
1051 case LFT_CLIENT_REQ_URLSCHEME
:
1053 sb
= al
->request
->url
.getScheme().image();
1059 case LFT_CLIENT_REQ_URLDOMAIN
:
1061 out
= al
->request
->url
.host();
1066 case LFT_CLIENT_REQ_URLPORT
:
1067 if (al
->request
&& al
->request
->url
.port()) {
1068 outint
= *al
->request
->url
.port();
1073 case LFT_REQUEST_URLPATH_OLD_31
:
1074 case LFT_CLIENT_REQ_URLPATH
:
1076 sb
= al
->request
->url
.path();
1082 case LFT_CLIENT_REQ_VERSION
:
1084 sb
.appendf("%u.%u", al
->request
->http_ver
.major
, al
->request
->http_ver
.minor
);
1089 case LFT_REQUEST_METHOD
:
1090 if (al
->hasLogMethod()) {
1091 sb
= al
->getLogMethod();
1097 case LFT_REQUEST_URI
:
1098 if (!al
->url
.isEmpty()) {
1104 case LFT_REQUEST_VERSION_OLD_2X
:
1105 case LFT_REQUEST_VERSION
:
1106 sb
.appendf("%u.%u", al
->http
.version
.major
, al
->http
.version
.minor
);
1110 case LFT_SERVER_REQ_METHOD
:
1111 if (al
->adapted_request
) {
1112 sb
= al
->adapted_request
->method
.image();
1118 case LFT_SERVER_REQ_URI
:
1119 // adapted request URI sent to server/peer
1120 if (al
->adapted_request
) {
1121 sb
= al
->adapted_request
->effectiveRequestUri();
1127 case LFT_SERVER_REQ_URLSCHEME
:
1128 if (al
->adapted_request
) {
1129 sb
= al
->adapted_request
->url
.getScheme().image();
1135 case LFT_SERVER_REQ_URLDOMAIN
:
1136 if (al
->adapted_request
) {
1137 out
= al
->adapted_request
->url
.host();
1142 case LFT_SERVER_REQ_URLPORT
:
1143 if (al
->adapted_request
&& al
->adapted_request
->url
.port()) {
1144 outint
= *al
->adapted_request
->url
.port();
1149 case LFT_SERVER_REQ_URLPATH
:
1150 if (al
->adapted_request
) {
1151 sb
= al
->adapted_request
->url
.path();
1157 case LFT_SERVER_REQ_VERSION
:
1158 if (al
->adapted_request
) {
1160 al
->adapted_request
->http_ver
.major
,
1161 al
->adapted_request
->http_ver
.minor
);
1166 case LFT_CLIENT_REQUEST_SIZE_TOTAL
:
1167 outoff
= al
->http
.clientRequestSz
.messageTotal();
1171 case LFT_CLIENT_REQUEST_SIZE_HEADERS
:
1172 outoff
= al
->http
.clientRequestSz
.header
;
1176 /*case LFT_REQUEST_SIZE_BODY: */
1177 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
1179 case LFT_ADAPTED_REPLY_SIZE_TOTAL
:
1180 outoff
= al
->http
.clientReplySz
.messageTotal();
1184 case LFT_REPLY_HIGHOFFSET
:
1185 outoff
= al
->cache
.highOffset
;
1189 case LFT_REPLY_OBJECTSIZE
:
1190 outoff
= al
->cache
.objectSize
;
1194 case LFT_ADAPTED_REPLY_SIZE_HEADERS
:
1195 outint
= al
->http
.clientReplySz
.header
;
1199 /*case LFT_REPLY_SIZE_BODY: */
1200 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
1202 case LFT_CLIENT_IO_SIZE_TOTAL
:
1203 outint
= al
->http
.clientRequestSz
.messageTotal() + al
->http
.clientReplySz
.messageTotal();
1206 /*case LFT_SERVER_IO_SIZE_TOTAL: */
1210 out
= al
->request
->tag
.termedBuf();
1217 out
= al
->request
->extacl_log
.termedBuf();
1222 case LFT_SEQUENCE_NUMBER
:
1223 outoff
= logSequenceNumber
;
1228 case LFT_SSL_BUMP_MODE
: {
1229 const Ssl::BumpMode mode
= static_cast<Ssl::BumpMode
>(al
->ssl
.bumpMode
);
1230 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1231 out
= Ssl::bumpMode(mode
);
1235 case LFT_EXT_ACL_USER_CERT_RAW
:
1237 ConnStateData
*conn
= al
->request
->clientConnectionManager
.get();
1238 if (conn
&& Comm::IsConnOpen(conn
->clientConnection
)) {
1239 if (const auto ssl
= fd_table
[conn
->clientConnection
->fd
].ssl
.get()) {
1240 sb
= sslGetUserCertificatePEM(ssl
);
1247 case LFT_EXT_ACL_USER_CERTCHAIN_RAW
:
1249 ConnStateData
*conn
= al
->request
->clientConnectionManager
.get();
1250 if (conn
&& Comm::IsConnOpen(conn
->clientConnection
)) {
1251 if (const auto ssl
= fd_table
[conn
->clientConnection
->fd
].ssl
.get()) {
1252 sb
= sslGetUserCertificatePEM(ssl
);
1259 case LFT_EXT_ACL_USER_CERT
:
1261 ConnStateData
*conn
= al
->request
->clientConnectionManager
.get();
1262 if (conn
&& Comm::IsConnOpen(conn
->clientConnection
)) {
1263 if (auto ssl
= fd_table
[conn
->clientConnection
->fd
].ssl
.get())
1264 out
= sslGetUserAttribute(ssl
, fmt
->data
.header
.header
);
1269 case LFT_EXT_ACL_USER_CA_CERT
:
1271 ConnStateData
*conn
= al
->request
->clientConnectionManager
.get();
1272 if (conn
&& Comm::IsConnOpen(conn
->clientConnection
)) {
1273 if (auto ssl
= fd_table
[conn
->clientConnection
->fd
].ssl
.get())
1274 out
= sslGetCAAttribute(ssl
, fmt
->data
.header
.header
);
1279 case LFT_SSL_USER_CERT_SUBJECT
:
1280 if (const auto &cert
= al
->cache
.sslClientCert
) {
1281 sb
= Security::SubjectName(*cert
);
1286 case LFT_SSL_USER_CERT_ISSUER
:
1287 if (const auto &cert
= al
->cache
.sslClientCert
) {
1288 sb
= Security::IssuerName(*cert
);
1293 case LFT_SSL_CLIENT_SNI
:
1294 if (al
->request
&& al
->request
->clientConnectionManager
.valid()) {
1295 if (const ConnStateData
*conn
= al
->request
->clientConnectionManager
.get()) {
1296 if (!conn
->tlsClientSni().isEmpty()) {
1297 sb
= conn
->tlsClientSni();
1304 case LFT_SSL_SERVER_CERT_ERRORS
:
1305 if (al
->request
&& al
->request
->clientConnectionManager
.valid()) {
1306 if (Ssl::ServerBump
* srvBump
= al
->request
->clientConnectionManager
->serverBump()) {
1307 const char *separator
= fmt
->data
.string
? fmt
->data
.string
: ":";
1308 for (const Security::CertErrors
*sslError
= srvBump
->sslErrors(); sslError
; sslError
= sslError
->next
) {
1310 sb
.append(separator
);
1311 sb
.append(Ssl::GetErrorName(sslError
->element
.code
, true));
1312 if (sslError
->element
.depth
>= 0)
1313 sb
.appendf("@depth=%d", sslError
->element
.depth
);
1321 case LFT_SSL_SERVER_CERT_ISSUER
:
1322 case LFT_SSL_SERVER_CERT_SUBJECT
:
1323 case LFT_SSL_SERVER_CERT_WHOLE
:
1324 if (al
->request
&& al
->request
->clientConnectionManager
.valid()) {
1325 if (Ssl::ServerBump
* srvBump
= al
->request
->clientConnectionManager
->serverBump()) {
1326 if (X509
*serverCert
= srvBump
->serverCert
.get()) {
1327 if (fmt
->type
== LFT_SSL_SERVER_CERT_SUBJECT
)
1328 out
= Ssl::GetX509UserAttribute(serverCert
, "DN");
1329 else if (fmt
->type
== LFT_SSL_SERVER_CERT_ISSUER
)
1330 out
= Ssl::GetX509CAAttribute(serverCert
, "DN");
1332 assert(fmt
->type
== LFT_SSL_SERVER_CERT_WHOLE
);
1333 sb
= Ssl::GetX509PEM(serverCert
);
1342 case LFT_TLS_CLIENT_NEGOTIATED_VERSION
:
1343 if (al
->tcpClient
&& al
->tcpClient
->hasTlsNegotiations())
1344 out
= al
->tcpClient
->hasTlsNegotiations()->negotiatedVersion();
1347 case LFT_TLS_SERVER_NEGOTIATED_VERSION
:
1348 if (al
->hier
.tcpServer
&& al
->hier
.tcpServer
->hasTlsNegotiations())
1349 out
= al
->hier
.tcpServer
->hasTlsNegotiations()->negotiatedVersion();
1352 case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION
:
1353 if (al
->tcpClient
&& al
->tcpClient
->hasTlsNegotiations())
1354 out
= al
->tcpClient
->hasTlsNegotiations()->helloVersion();
1357 case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION
:
1358 if (al
->hier
.tcpServer
&& al
->hier
.tcpServer
->hasTlsNegotiations())
1359 out
= al
->hier
.tcpServer
->hasTlsNegotiations()->helloVersion();
1362 case LFT_TLS_CLIENT_SUPPORTED_VERSION
:
1363 if (al
->tcpClient
&& al
->tcpClient
->hasTlsNegotiations())
1364 out
= al
->tcpClient
->hasTlsNegotiations()->supportedVersion();
1367 case LFT_TLS_SERVER_SUPPORTED_VERSION
:
1368 if (al
->hier
.tcpServer
&& al
->hier
.tcpServer
->hasTlsNegotiations())
1369 out
= al
->hier
.tcpServer
->hasTlsNegotiations()->supportedVersion();
1372 case LFT_TLS_CLIENT_NEGOTIATED_CIPHER
:
1373 if (al
->tcpClient
&& al
->tcpClient
->hasTlsNegotiations())
1374 out
= al
->tcpClient
->hasTlsNegotiations()->cipherName();
1377 case LFT_TLS_SERVER_NEGOTIATED_CIPHER
:
1378 if (al
->hier
.tcpServer
&& al
->hier
.tcpServer
->hasTlsNegotiations())
1379 out
= al
->hier
.tcpServer
->hasTlsNegotiations()->cipherName();
1383 case LFT_REQUEST_URLGROUP_OLD_2X
:
1384 assert(LFT_REQUEST_URLGROUP_OLD_2X
== 0); // should never happen.
1388 tmp
[0] = fmt
->data
.header
.separator
;
1390 if (fmt
->data
.header
.header
&& *fmt
->data
.header
.header
) {
1391 const char *separator
= tmp
;
1394 Adaptation::History::Pointer ah
= al
->request
? al
->request
->adaptHistory() : Adaptation::History::Pointer();
1395 if (ah
&& ah
->metaHeaders
) {
1396 if (ah
->metaHeaders
->find(note
, fmt
->data
.header
.header
, separator
))
1401 if (al
->notes
->find(note
, fmt
->data
.header
.header
, separator
)) {
1403 sb
.append(separator
);
1410 // No specific annotation requested. Report all annotations.
1412 // if no argument given use default "\r\n" as notes separator
1413 const char *separator
= fmt
->data
.string
? tmp
: "\r\n";
1416 Adaptation::History::Pointer ah
= al
->request
? al
->request
->adaptHistory() : Adaptation::History::Pointer();
1417 if (ah
&& ah
->metaHeaders
)
1418 ah
->metaHeaders
->print(os
, ": ", separator
);
1421 al
->notes
->print(os
, ": ", separator
);
1429 case LFT_CREDENTIALS
:
1431 if (al
->request
&& al
->request
->auth_user_request
)
1432 out
= strOrNull(al
->request
->auth_user_request
->credentialsStr());
1440 case LFT_EXT_ACL_NAME
:
1441 out
= al
->lastAclName
;
1444 case LFT_EXT_ACL_DATA
:
1445 if (!al
->lastAclData
.isEmpty())
1446 out
= al
->lastAclData
.c_str();
1449 case LFT_MASTER_XACTION
:
1452 outUint64
= static_cast<uint64_t>(al
->request
->masterXaction
->id
.value
);
1458 sb
.appendf("%0*" PRId64
, fmt
->zero
&& fmt
->widthMin
>= 0 ? fmt
->widthMin
: 0, outoff
);
1462 sb
.appendf("%0*ld", fmt
->zero
&& fmt
->widthMin
>= 0 ? fmt
->widthMin
: 0, outint
);
1464 } else if (doUint64
) {
1465 sb
.appendf("%0*" PRIu64
, fmt
->zero
&& fmt
->widthMin
>= 0 ? fmt
->widthMin
: 0, outUint64
);
1467 } else if (doMsec
) {
1468 if (fmt
->widthMax
< 0) {
1469 sb
.appendf("%0*ld", fmt
->zero
&& fmt
->widthMin
>= 0 ? fmt
->widthMin
: 0, tvToMsec(outtv
));
1471 int precision
= fmt
->widthMax
;
1472 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
)));
1476 int precision
= fmt
->widthMax
>=0 ? fmt
->widthMax
:3;
1477 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
));
1482 if (quote
|| fmt
->quote
!= LOG_QUOTE_NONE
) {
1483 // Do not write to the tmp buffer because it may contain the to-be-quoted value.
1484 static char quotedOut
[2 * sizeof(tmp
)];
1485 static_assert(sizeof(quotedOut
) > 0, "quotedOut has zero length");
1486 quotedOut
[0] = '\0';
1488 char *newout
= nullptr;
1491 switch (fmt
->quote
) {
1493 case LOG_QUOTE_NONE
:
1494 newout
= rfc1738_escape_unescaped(out
);
1497 case LOG_QUOTE_QUOTES
: {
1498 size_t out_len
= static_cast<size_t>(strlen(out
)) * 2 + 1;
1499 if (out_len
>= sizeof(tmp
)) {
1500 newout
= (char *)xmalloc(out_len
);
1504 log_quoted_string(out
, newout
);
1508 case LOG_QUOTE_MIMEBLOB
:
1509 newout
= QuoteMimeBlob(out
);
1514 newout
= rfc1738_escape(out
);
1517 case LOG_QUOTE_SHELL
: {
1520 strwordquote(&mbq
, out
);
1521 newout
= mbq
.content();
1541 // enforce width limits if configured
1542 const bool haveMaxWidth
= fmt
->widthMax
>=0 && !doint
&& !dooff
&& !doMsec
&& !doSec
&& !doUint64
;
1543 if (haveMaxWidth
|| fmt
->widthMin
) {
1544 const int minWidth
= fmt
->widthMin
>= 0 ?
1546 const int maxWidth
= haveMaxWidth
?
1547 fmt
->widthMax
: strlen(out
);
1550 mb
.appendf("%-*.*s", minWidth
, maxWidth
, out
);
1552 mb
.appendf("%*.*s", minWidth
, maxWidth
, out
);
1554 mb
.append(out
, strlen(out
));