]> git.ipfire.org Git - thirdparty/squid.git/blame - src/format/Format.cc
Bug 5428: Warn if pkg-config is not found (#1902)
[thirdparty/squid.git] / src / format / Format.cc
CommitLineData
bbc27441 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
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.
7 */
8
f7f3304a 9#include "squid.h"
38e16f92 10#include "AccessLogEntry.h"
6131103b 11#include "base64.h"
582c2af2 12#include "client_side.h"
38e16f92 13#include "comm/Connection.h"
83b053a0 14#include "error/Detail.h"
38e16f92 15#include "errorpage.h"
f4698e0b 16#include "fde.h"
38e16f92
AJ
17#include "format/Format.h"
18#include "format/Quoting.h"
31971e6a 19#include "format/Token.h"
d3dddfb5 20#include "http/Stream.h"
38e16f92
AJ
21#include "HttpRequest.h"
22#include "MemBuf.h"
36c774f7 23#include "proxyp/Header.h"
38e16f92 24#include "rfc1738.h"
7cfd3a41 25#include "sbuf/Stream.h"
6d19fc4d 26#include "sbuf/StringConvert.h"
92e3827b 27#include "security/CertError.h"
907831e6 28#include "security/Certificate.h"
4b307ad4 29#include "security/NegotiationHistory.h"
38e16f92 30#include "Store.h"
95d78f10 31#include "tools.h"
cb4f4424 32#if USE_OPENSSL
2f3e52b5 33#include "ssl/ErrorDetail.h"
cedca6e7 34#include "ssl/ServerBump.h"
2f3e52b5
CT
35#endif
36
bd85ea1f
AJ
37/// Convert a string to NULL pointer if it is ""
38#define strOrNull(s) ((s)==NULL||(s)[0]=='\0'?NULL:(s))
38e16f92 39
f57ae909
NH
40const SBuf Format::Dash("-");
41
38e16f92 42Format::Format::Format(const char *n) :
aee3523a
AR
43 format(nullptr),
44 next(nullptr)
38e16f92
AJ
45{
46 name = xstrdup(n);
47}
48
49Format::Format::~Format()
50{
51 // erase the list without consuming stack space
52 while (next) {
53 // unlink the next entry for deletion
54 Format *temp = next;
55 next = temp->next;
aee3523a 56 temp->next = nullptr;
38e16f92
AJ
57 delete temp;
58 }
59
60 // remove locals
61 xfree(name);
62 delete format;
63}
64
65bool
f4698e0b 66Format::Format::parse(const char *def)
38e16f92 67{
f4698e0b 68 const char *cur, *eos;
38e16f92
AJ
69 Token *new_lt, *last_lt;
70 enum Quoting quote = LOG_QUOTE_NONE;
71
bf95c10a 72 debugs(46, 2, "got definition '" << def << "'");
38e16f92
AJ
73
74 if (format) {
75 debugs(46, DBG_IMPORTANT, "WARNING: existing format for '" << name << " " << def << "'");
76 return false;
77 }
78
2f8abb64 79 /* very inefficient parser, but who cares, this needs to be simple */
38e16f92
AJ
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. */
83 cur = def;
84 eos = def + strlen(def);
85 format = new_lt = last_lt = new Token;
86 cur += new_lt->parse(cur, &quote);
87
88 while (cur < eos) {
89 new_lt = new Token;
90 last_lt->next = new_lt;
91 last_lt = new_lt;
92 cur += new_lt->parse(cur, &quote);
93 }
94
95 return true;
96}
97
7e6eabbc
CT
98size_t
99Format::AssembleOne(const char *token, MemBuf &mb, const AccessLogEntryPointer &ale)
100{
101 Token tkn;
102 enum Quoting quote = LOG_QUOTE_NONE;
103 const auto tokenSize = tkn.parse(token, &quote);
104 assert(tokenSize > 0);
105 if (ale != nullptr) {
106 Format fmt("SimpleToken");
107 fmt.format = &tkn;
108 fmt.assemble(mb, ale, 0);
109 fmt.format = nullptr;
8b082ed9 110 } else {
7e6eabbc 111 mb.append("-", 1);
8b082ed9 112 }
7e6eabbc
CT
113 return static_cast<size_t>(tokenSize);
114}
115
38e16f92 116void
4e56d7f6 117Format::Format::dump(StoreEntry * entry, const char *directiveName, bool eol) const
38e16f92 118{
bf95c10a 119 debugs(46, 4, MYNAME);
38e16f92
AJ
120
121 // loop rather than recursing to conserve stack space.
4e56d7f6 122 for (const Format *fmt = this; fmt; fmt = fmt->next) {
bf95c10a 123 debugs(46, 3, "Dumping format definition for " << fmt->name);
4e56d7f6
AJ
124 if (directiveName)
125 storeAppendPrintf(entry, "%s %s ", directiveName, fmt->name);
38e16f92 126
5f621cd0 127 for (Token *t = fmt->format; t; t = t->next) {
38e16f92
AJ
128 if (t->type == LFT_STRING)
129 storeAppendPrintf(entry, "%s", t->data.string);
130 else {
131 char argbuf[256];
aee3523a 132 char *arg = nullptr;
38e16f92
AJ
133 ByteCode_t type = t->type;
134
135 switch (type) {
f53969cc 136 /* special cases */
38e16f92
AJ
137
138 case LFT_STRING:
139 break;
140#if USE_ADAPTATION
141 case LFT_ADAPTATION_LAST_HEADER_ELEM:
142#endif
143#if ICAP_CLIENT
144 case LFT_ICAP_REQ_HEADER_ELEM:
145 case LFT_ICAP_REP_HEADER_ELEM:
146#endif
147 case LFT_REQUEST_HEADER_ELEM:
148 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
149 case LFT_REPLY_HEADER_ELEM:
150
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);
153 else
154 snprintf(argbuf, sizeof(argbuf), "%s:%s", t->data.header.header, t->data.header.element);
155
156 arg = argbuf;
157
158 switch (type) {
159 case LFT_REQUEST_HEADER_ELEM:
160 type = LFT_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
161 break;
162 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
163 type = LFT_ADAPTED_REQUEST_HEADER_ELEM; // XXX: remove _ELEM?
164 break;
165 case LFT_REPLY_HEADER_ELEM:
166 type = LFT_REPLY_HEADER_ELEM; // XXX: remove _ELEM?
167 break;
168#if USE_ADAPTATION
169 case LFT_ADAPTATION_LAST_HEADER_ELEM:
170 type = LFT_ADAPTATION_LAST_HEADER;
171 break;
172#endif
173#if ICAP_CLIENT
174 case LFT_ICAP_REQ_HEADER_ELEM:
175 type = LFT_ICAP_REQ_HEADER;
176 break;
177 case LFT_ICAP_REP_HEADER_ELEM:
178 type = LFT_ICAP_REP_HEADER;
179 break;
180#endif
181 default:
182 break;
183 }
184
185 break;
186
187 case LFT_REQUEST_ALL_HEADERS:
188 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
189 case LFT_REPLY_ALL_HEADERS:
190
191#if USE_ADAPTATION
192 case LFT_ADAPTATION_LAST_ALL_HEADERS:
193#endif
194#if ICAP_CLIENT
195 case LFT_ICAP_REQ_ALL_HEADERS:
196 case LFT_ICAP_REP_ALL_HEADERS:
197#endif
198
199 switch (type) {
200 case LFT_REQUEST_ALL_HEADERS:
201 type = LFT_REQUEST_HEADER;
202 break;
203 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
204 type = LFT_ADAPTED_REQUEST_HEADER;
205 break;
206 case LFT_REPLY_ALL_HEADERS:
207 type = LFT_REPLY_HEADER;
208 break;
209#if USE_ADAPTATION
210 case LFT_ADAPTATION_LAST_ALL_HEADERS:
211 type = LFT_ADAPTATION_LAST_HEADER;
212 break;
213#endif
214#if ICAP_CLIENT
215 case LFT_ICAP_REQ_ALL_HEADERS:
216 type = LFT_ICAP_REQ_HEADER;
217 break;
218 case LFT_ICAP_REP_ALL_HEADERS:
219 type = LFT_ICAP_REP_HEADER;
220 break;
221#endif
222 default:
223 break;
224 }
225
226 break;
227
228 default:
229 if (t->data.string)
230 arg = t->data.string;
231
232 break;
233 }
234
235 entry->append("%", 1);
236
237 switch (t->quote) {
238
239 case LOG_QUOTE_QUOTES:
240 entry->append("\"", 1);
241 break;
242
243 case LOG_QUOTE_MIMEBLOB:
244 entry->append("[", 1);
245 break;
246
247 case LOG_QUOTE_URL:
248 entry->append("#", 1);
249 break;
250
251 case LOG_QUOTE_RAW:
252 entry->append("'", 1);
253 break;
254
95d78f10
AJ
255 case LOG_QUOTE_SHELL:
256 entry->append("/", 1);
257 break;
258
38e16f92
AJ
259 case LOG_QUOTE_NONE:
260 break;
261 }
262
263 if (t->left)
264 entry->append("-", 1);
265
266 if (t->zero)
267 entry->append("0", 1);
268
8846a2b4
CT
269 if (t->widthMin >= 0)
270 storeAppendPrintf(entry, "%d", t->widthMin);
38e16f92 271
8846a2b4
CT
272 if (t->widthMax >= 0)
273 storeAppendPrintf(entry, ".%d", t->widthMax);
38e16f92
AJ
274
275 if (arg)
276 storeAppendPrintf(entry, "{%s}", arg);
277
aa99e35e 278 storeAppendPrintf(entry, "%s", t->label);
38e16f92
AJ
279
280 if (t->space)
281 entry->append(" ", 1);
282 }
283 }
284
4e56d7f6
AJ
285 if (eol)
286 entry->append("\n", 1);
38e16f92
AJ
287 }
288
289}
290
291static void
292log_quoted_string(const char *str, char *out)
293{
294 char *p = out;
295
296 while (*str) {
297 int l = strcspn(str, "\"\\\r\n\t");
298 memcpy(p, str, l);
299 str += l;
300 p += l;
301
302 switch (*str) {
303
304 case '\0':
305 break;
306
307 case '\r':
a38ec4b1
FC
308 *p = '\\';
309 ++p;
310 *p = 'r';
311 ++p;
cb4185f1 312 ++str;
38e16f92
AJ
313 break;
314
315 case '\n':
a38ec4b1
FC
316 *p = '\\';
317 ++p;
318 *p = 'n';
319 ++p;
cb4185f1 320 ++str;
38e16f92
AJ
321 break;
322
323 case '\t':
a38ec4b1
FC
324 *p = '\\';
325 ++p;
326 *p = 't';
327 ++p;
cb4185f1 328 ++str;
38e16f92
AJ
329 break;
330
331 default:
a38ec4b1
FC
332 *p = '\\';
333 ++p;
334 *p = *str;
335 ++p;
cb4185f1 336 ++str;
38e16f92
AJ
337 break;
338 }
339 }
340
cb4185f1 341 *p = '\0';
38e16f92
AJ
342}
343
9e8d0e18 344/// XXX: Misnamed. TODO: Split <h (and this function) to distinguish received
501b5d24
AR
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).
63df1d28 349static const Http::Message *
bd59d61c
EB
350actualReplyHeader(const AccessLogEntry::Pointer &al)
351{
49f57088 352 const Http::Message *msg = al->reply.getRaw();
c529dfc7 353#if ICAP_CLIENT
501b5d24 354 // al->icap.reqMethod is methodNone in access.log context
bd59d61c
EB
355 if (!msg && al->icap.reqMethod == Adaptation::methodReqmod)
356 msg = al->adapted_request;
25dadb98 357#endif
bd59d61c
EB
358 return msg;
359}
360
501b5d24
AR
361/// XXX: Misnamed. See actualReplyHeader().
362/// \return HttpRequest or HttpReply for %http::>h.
63df1d28 363static const Http::Message *
bd59d61c
EB
364actualRequestHeader(const AccessLogEntry::Pointer &al)
365{
c529dfc7 366#if ICAP_CLIENT
501b5d24 367 // al->icap.reqMethod is methodNone in access.log context
bd59d61c
EB
368 if (al->icap.reqMethod == Adaptation::methodRespmod) {
369 // XXX: for now AccessLogEntry lacks virgin response headers
370 return nullptr;
371 }
25dadb98 372#endif
bd59d61c
EB
373 return al->request;
374}
375
38e16f92 376void
f4698e0b 377Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logSequenceNumber) const
38e16f92 378{
6d19fc4d
AJ
379 static char tmp[1024];
380 SBuf sb;
38e16f92 381
6d19fc4d
AJ
382 for (Token *fmt = format; fmt; fmt = fmt->next) { /* for each token */
383 const char *out = nullptr;
38e16f92
AJ
384 int quote = 0;
385 long int outint = 0;
386 int doint = 0;
387 int dofree = 0;
388 int64_t outoff = 0;
389 int dooff = 0;
8b082ed9 390 struct timeval outtv = {};
01bd87d8
CT
391 int doMsec = 0;
392 int doSec = 0;
7cfd3a41
EB
393 bool doUint64 = false;
394 uint64_t outUint64 = 0;
38e16f92
AJ
395
396 switch (fmt->type) {
397
398 case LFT_NONE:
399 out = "";
400 break;
401
007cd686
EB
402 case LFT_BYTE:
403 tmp[0] = static_cast<char>(fmt->data.byteValue);
404 tmp[1] = '\0';
405 out = tmp;
406 break;
407
38e16f92
AJ
408 case LFT_STRING:
409 out = fmt->data.string;
410 break;
411
412 case LFT_CLIENT_IP_ADDRESS:
d4204018
AJ
413 al->getLogClientIp(tmp, sizeof(tmp));
414 out = tmp;
38e16f92
AJ
415 break;
416
417 case LFT_CLIENT_FQDN:
a8c7a110 418 out = al->getLogClientFqdn(tmp, sizeof(tmp));
38e16f92
AJ
419 break;
420
421 case LFT_CLIENT_PORT:
422 if (al->request) {
4dd643d5 423 outint = al->request->client_addr.port();
38e16f92 424 doint = 1;
57462605
AR
425 } else if (al->tcpClient) {
426 outint = al->tcpClient->remote.port();
427 doint = 1;
38e16f92
AJ
428 }
429 break;
430
38e16f92 431 case LFT_CLIENT_EUI:
8652f8e7 432#if USE_SQUID_EUI
38e16f92 433 // TODO make the ACL checklist have a direct link to any TCP details.
6d19fc4d
AJ
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));
38e16f92 439 else
6d19fc4d 440 conn->remoteEui64.encode(tmp, sizeof(tmp));
38e16f92
AJ
441 out = tmp;
442 }
38e16f92 443#endif
8652f8e7 444 break;
38e16f92 445
4e56d7f6 446 case LFT_EXT_ACL_CLIENT_EUI48:
38d7fa02 447#if USE_SQUID_EUI
4e56d7f6 448 if (al->request && al->request->clientConnectionManager.valid() &&
6d19fc4d 449 al->request->clientConnectionManager->clientConnection &&
4e56d7f6 450 al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
6d19fc4d 451 al->request->clientConnectionManager->clientConnection->remoteEui48.encode(tmp, sizeof(tmp));
4e56d7f6
AJ
452 out = tmp;
453 }
38d7fa02 454#endif
4e56d7f6
AJ
455 break;
456
457 case LFT_EXT_ACL_CLIENT_EUI64:
38d7fa02 458#if USE_SQUID_EUI
4e56d7f6 459 if (al->request && al->request->clientConnectionManager.valid() &&
6d19fc4d 460 al->request->clientConnectionManager->clientConnection &&
4e56d7f6 461 !al->request->clientConnectionManager->clientConnection->remote.isIPv4()) {
6d19fc4d 462 al->request->clientConnectionManager->clientConnection->remoteEui64.encode(tmp, sizeof(tmp));
4e56d7f6
AJ
463 out = tmp;
464 }
4e56d7f6 465#endif
38d7fa02 466 break;
4e56d7f6 467
8652f8e7 468 case LFT_SERVER_IP_ADDRESS:
6d19fc4d
AJ
469 if (al->hier.tcpServer)
470 out = al->hier.tcpServer->remote.toStr(tmp, sizeof(tmp));
8652f8e7 471 break;
38e16f92 472
8652f8e7 473 case LFT_SERVER_FQDN_OR_PEER_NAME:
38e16f92 474 out = al->hier.host;
38e16f92
AJ
475 break;
476
8652f8e7 477 case LFT_SERVER_PORT:
6d19fc4d 478 if (al->hier.tcpServer) {
4dd643d5 479 outint = al->hier.tcpServer->remote.port();
8652f8e7
AJ
480 doint = 1;
481 }
482 break;
38e16f92 483
ea35939b
A
484 case LFT_LOCAL_LISTENING_IP:
485 if (const auto addr = FindListeningPortAddress(nullptr, al.getRaw()))
486 out = addr->toStr(tmp, sizeof(tmp));
487 break;
28417506 488
8652f8e7 489 case LFT_CLIENT_LOCAL_IP:
6d19fc4d
AJ
490 if (al->tcpClient)
491 out = al->tcpClient->local.toStr(tmp, sizeof(tmp));
38e16f92
AJ
492 break;
493
f123f5e9 494 case LFT_CLIENT_LOCAL_TOS:
6d19fc4d
AJ
495 if (al->tcpClient) {
496 sb.appendf("0x%x", static_cast<uint32_t>(al->tcpClient->tos));
497 out = sb.c_str();
f123f5e9
CT
498 }
499 break;
500
a120b7a8
CT
501 case LFT_TRANSPORT_CLIENT_CONNECTION_ID:
502 if (al->tcpClient) {
503 outUint64 = al->tcpClient->id.value;
504 doUint64 = true;
505 }
506 break;
507
f123f5e9 508 case LFT_CLIENT_LOCAL_NFMARK:
6d19fc4d
AJ
509 if (al->tcpClient) {
510 sb.appendf("0x%x", al->tcpClient->nfmark);
511 out = sb.c_str();
f123f5e9
CT
512 }
513 break;
514
28417506 515 case LFT_LOCAL_LISTENING_PORT:
b6c0f0dc 516 if (const auto port = FindListeningPortNumber(nullptr, al.getRaw())) {
380b09ae 517 outint = *port;
fbbea662 518 doint = 1;
28417506
CT
519 }
520 break;
521
8652f8e7 522 case LFT_CLIENT_LOCAL_PORT:
6d19fc4d 523 if (al->tcpClient) {
4dd643d5 524 outint = al->tcpClient->local.port();
38e16f92
AJ
525 doint = 1;
526 }
38e16f92
AJ
527 break;
528
8652f8e7
AJ
529 case LFT_SERVER_LOCAL_IP_OLD_27:
530 case LFT_SERVER_LOCAL_IP:
6d19fc4d
AJ
531 if (al->hier.tcpServer)
532 out = al->hier.tcpServer->local.toStr(tmp, sizeof(tmp));
38e16f92
AJ
533 break;
534
8652f8e7 535 case LFT_SERVER_LOCAL_PORT:
6d19fc4d 536 if (al->hier.tcpServer) {
4dd643d5 537 outint = al->hier.tcpServer->local.port();
38e16f92
AJ
538 doint = 1;
539 }
38e16f92
AJ
540 break;
541
f123f5e9 542 case LFT_SERVER_LOCAL_TOS:
6d19fc4d
AJ
543 if (al->hier.tcpServer) {
544 sb.appendf("0x%x", static_cast<uint32_t>(al->hier.tcpServer->tos));
545 out = sb.c_str();
f123f5e9
CT
546 }
547 break;
548
549 case LFT_SERVER_LOCAL_NFMARK:
6d19fc4d
AJ
550 if (al->hier.tcpServer) {
551 sb.appendf("0x%x", al->hier.tcpServer->nfmark);
552 out = sb.c_str();
f123f5e9
CT
553 }
554 break;
555
6131103b
CT
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);
562
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);
567
568 sb.rawAppendFinish(buf, encLength);
569 out = sb.c_str();
570 }
571 }
572 break;
573
38e16f92
AJ
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);
577 dooff = 1;
578 break;
579
580 case LFT_TIME_SUBSECOND:
581 outint = current_time.tv_usec / fmt->divisor;
582 doint = 1;
583 break;
584
38e16f92 585 case LFT_TIME_LOCALTIME:
38e16f92
AJ
586 case LFT_TIME_GMT: {
587 const char *spec;
38e16f92 588 struct tm *t;
b22c1ad3 589 spec = fmt->data.string;
38e16f92
AJ
590
591 if (fmt->type == LFT_TIME_LOCALTIME) {
592 if (!spec)
593 spec = "%d/%b/%Y:%H:%M:%S %z";
594 t = localtime(&squid_curtime);
595 } else {
596 if (!spec)
597 spec = "%d/%b/%Y:%H:%M:%S";
598
599 t = gmtime(&squid_curtime);
600 }
601
602 strftime(tmp, sizeof(tmp), spec, t);
38e16f92
AJ
603 out = tmp;
604 }
38e16f92
AJ
605 break;
606
01bd87d8
CT
607 case LFT_TIME_START:
608 outtv = al->cache.start_time;
609 doSec = 1;
f53969cc 610 break;
af0ded40 611
0cd2a1b6
AR
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();
618 outoff = n.count();
619 dooff = true;
620 }
621 }
622 break;
623
38e16f92 624 case LFT_TIME_TO_HANDLE_REQUEST:
01bd87d8
CT
625 outtv = al->cache.trTime;
626 doMsec = 1;
38e16f92
AJ
627 break;
628
629 case LFT_PEER_RESPONSE_TIME:
d8165775
AR
630 struct timeval peerResponseTime;
631 if (al->hier.peerResponseTime(peerResponseTime)) {
632 outtv = peerResponseTime;
01bd87d8 633 doMsec = 1;
38e16f92
AJ
634 }
635 break;
636
16b70e2a 637 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME: {
5673f482
AR
638 // XXX: al->hier.totalPeeringTime is not updated until prepareLogWithRequestDetails().
639 // TODO: Avoid the need for updates by keeping totalPeeringTime (or even ALE::hier) in one place.
640 const auto &timer = (!al->hier.totalPeeringTime.ran() && al->request) ?
641 al->request->hier.totalPeeringTime : al->hier.totalPeeringTime;
642 if (timer.ran()) {
643 using namespace std::chrono_literals;
644 const auto duration = timer.total();
645 outtv.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
646 const auto totalUsec = std::chrono::duration_cast<std::chrono::microseconds>(duration);
647 outtv.tv_usec = (totalUsec % std::chrono::microseconds(1s)).count();
01bd87d8 648 doMsec = 1;
38e16f92 649 }
16b70e2a
CT
650 }
651 break;
38e16f92
AJ
652
653 case LFT_DNS_WAIT_TIME:
654 if (al->request && al->request->dnsWait >= 0) {
01bd87d8 655 // TODO: microsecond precision for dns wait time.
2f8abb64 656 // Convert milliseconds to timeval struct:
01bd87d8
CT
657 outtv.tv_sec = al->request->dnsWait / 1000;
658 outtv.tv_usec = (al->request->dnsWait % 1000) * 1000;
659 doMsec = 1;
38e16f92
AJ
660 }
661 break;
662
663 case LFT_REQUEST_HEADER:
6d19fc4d
AJ
664 if (const Http::Message *msg = actualRequestHeader(al)) {
665 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
666 out = sb.c_str();
667 quote = 1;
668 }
38e16f92
AJ
669 break;
670
671 case LFT_ADAPTED_REQUEST_HEADER:
6d19fc4d
AJ
672 if (al->adapted_request) {
673 sb = StringToSBuf(al->adapted_request->header.getByName(fmt->data.header.header));
674 out = sb.c_str();
675 quote = 1;
676 }
38e16f92
AJ
677 break;
678
6d19fc4d
AJ
679 case LFT_REPLY_HEADER:
680 if (const Http::Message *msg = actualReplyHeader(al)) {
681 sb = StringToSBuf(msg->header.getByName(fmt->data.header.header));
682 out = sb.c_str();
683 quote = 1;
684 }
685 break;
38e16f92
AJ
686
687#if USE_ADAPTATION
31971e6a 688 case LFT_ADAPTATION_SUM_XACT_TIMES:
38e16f92
AJ
689 if (al->request) {
690 Adaptation::History::Pointer ah = al->request->adaptHistory();
6d19fc4d 691 if (ah) {
38e16f92 692 ah->sumLogString(fmt->data.string, sb);
6d19fc4d
AJ
693 out = sb.c_str();
694 }
38e16f92
AJ
695 }
696 break;
697
31971e6a 698 case LFT_ADAPTATION_ALL_XACT_TIMES:
38e16f92
AJ
699 if (al->request) {
700 Adaptation::History::Pointer ah = al->request->adaptHistory();
6d19fc4d 701 if (ah) {
38e16f92 702 ah->allLogString(fmt->data.string, sb);
6d19fc4d
AJ
703 out = sb.c_str();
704 }
38e16f92
AJ
705 }
706 break;
707
708 case LFT_ADAPTATION_LAST_HEADER:
709 if (al->request) {
710 const Adaptation::History::Pointer ah = al->request->adaptHistory();
6d19fc4d
AJ
711 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
712 sb = StringToSBuf(ah->allMeta.getByName(fmt->data.header.header));
713 out = sb.c_str();
714 quote = 1;
715 }
38e16f92 716 }
38e16f92
AJ
717 break;
718
719 case LFT_ADAPTATION_LAST_HEADER_ELEM:
720 if (al->request) {
721 const Adaptation::History::Pointer ah = al->request->adaptHistory();
6d19fc4d 722 if (ah) { // XXX: add adapt::<all_h but use lastMeta here
36c774f7 723 sb = ah->allMeta.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
6d19fc4d
AJ
724 out = sb.c_str();
725 quote = 1;
726 }
38e16f92 727 }
38e16f92
AJ
728 break;
729
730 case LFT_ADAPTATION_LAST_ALL_HEADERS:
731 out = al->adapt.last_meta;
38e16f92 732 quote = 1;
38e16f92
AJ
733 break;
734#endif
735
736#if ICAP_CLIENT
737 case LFT_ICAP_ADDR:
6d19fc4d 738 out = al->icap.hostAddr.toStr(tmp, sizeof(tmp));
38e16f92
AJ
739 break;
740
741 case LFT_ICAP_SERV_NAME:
742 out = al->icap.serviceName.termedBuf();
743 break;
744
745 case LFT_ICAP_REQUEST_URI:
746 out = al->icap.reqUri.termedBuf();
747 break;
748
749 case LFT_ICAP_REQUEST_METHOD:
750 out = Adaptation::Icap::ICAP::methodStr(al->icap.reqMethod);
751 break;
752
753 case LFT_ICAP_BYTES_SENT:
754 outoff = al->icap.bytesSent;
755 dooff = 1;
756 break;
757
758 case LFT_ICAP_BYTES_READ:
759 outoff = al->icap.bytesRead;
760 dooff = 1;
761 break;
762
763 case LFT_ICAP_BODY_BYTES_READ:
764 if (al->icap.bodyBytesRead >= 0) {
765 outoff = al->icap.bodyBytesRead;
766 dooff = 1;
767 }
768 // else if icap.bodyBytesRead < 0, we do not have any http data,
769 // so just print a "-" (204 responses etc)
770 break;
771
772 case LFT_ICAP_REQ_HEADER:
6d19fc4d
AJ
773 if (al->icap.request) {
774 sb = StringToSBuf(al->icap.request->header.getByName(fmt->data.header.header));
775 out = sb.c_str();
38e16f92
AJ
776 quote = 1;
777 }
778 break;
779
780 case LFT_ICAP_REQ_HEADER_ELEM:
6d19fc4d 781 if (al->icap.request) {
36c774f7 782 sb = al->icap.request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
6d19fc4d
AJ
783 out = sb.c_str();
784 quote = 1;
785 }
38e16f92
AJ
786 break;
787
788 case LFT_ICAP_REQ_ALL_HEADERS:
789 if (al->icap.request) {
790 HttpHeaderPos pos = HttpHeaderInitPos;
791 while (const HttpHeaderEntry *e = al->icap.request->header.getEntry(&pos)) {
d5f18517 792 sb.append(e->name);
38e16f92 793 sb.append(": ");
6d19fc4d 794 sb.append(StringToSBuf(e->value));
38e16f92
AJ
795 sb.append("\r\n");
796 }
6d19fc4d 797 out = sb.c_str();
38e16f92
AJ
798 quote = 1;
799 }
800 break;
801
802 case LFT_ICAP_REP_HEADER:
6d19fc4d
AJ
803 if (al->icap.reply) {
804 sb = StringToSBuf(al->icap.reply->header.getByName(fmt->data.header.header));
805 out = sb.c_str();
38e16f92
AJ
806 quote = 1;
807 }
808 break;
809
810 case LFT_ICAP_REP_HEADER_ELEM:
6d19fc4d 811 if (al->icap.reply) {
36c774f7 812 sb = al->icap.reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
6d19fc4d
AJ
813 out = sb.c_str();
814 quote = 1;
815 }
38e16f92
AJ
816 break;
817
818 case LFT_ICAP_REP_ALL_HEADERS:
819 if (al->icap.reply) {
820 HttpHeaderPos pos = HttpHeaderInitPos;
821 while (const HttpHeaderEntry *e = al->icap.reply->header.getEntry(&pos)) {
d5f18517 822 sb.append(e->name);
38e16f92 823 sb.append(": ");
6d19fc4d 824 sb.append(StringToSBuf(e->value));
38e16f92
AJ
825 sb.append("\r\n");
826 }
6d19fc4d 827 out = sb.c_str();
38e16f92
AJ
828 quote = 1;
829 }
830 break;
831
832 case LFT_ICAP_TR_RESPONSE_TIME:
01bd87d8
CT
833 outtv = al->icap.trTime;
834 doMsec = 1;
38e16f92
AJ
835 break;
836
837 case LFT_ICAP_IO_TIME:
01bd87d8
CT
838 outtv = al->icap.ioTime;
839 doMsec = 1;
38e16f92
AJ
840 break;
841
842 case LFT_ICAP_STATUS_CODE:
843 outint = al->icap.resStatus;
844 doint = 1;
845 break;
846
847 case LFT_ICAP_OUTCOME:
848 out = al->icap.outcome;
849 break;
850
851 case LFT_ICAP_TOTAL_TIME:
01bd87d8
CT
852 outtv = al->icap.processingTime;
853 doMsec = 1;
38e16f92
AJ
854 break;
855#endif
856 case LFT_REQUEST_HEADER_ELEM:
6d19fc4d 857 if (const Http::Message *msg = actualRequestHeader(al)) {
36c774f7
EB
858 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
859 out = sb.c_str();
860 quote = 1;
861 }
862 break;
863
864 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER:
865 if (al->proxyProtocolHeader) {
866 sb = al->proxyProtocolHeader->getValues(fmt->data.headerId, fmt->data.header.separator);
867 out = sb.c_str();
868 quote = 1;
869 }
870 break;
871
872 case LFT_PROXY_PROTOCOL_RECEIVED_ALL_HEADERS:
873 if (al->proxyProtocolHeader) {
874 sb = al->proxyProtocolHeader->toMime();
875 out = sb.c_str();
876 quote = 1;
877 }
878 break;
879
880 case LFT_PROXY_PROTOCOL_RECEIVED_HEADER_ELEM:
881 if (al->proxyProtocolHeader) {
882 sb = al->proxyProtocolHeader->getElem(fmt->data.headerId, fmt->data.header.element, fmt->data.header.separator);
6d19fc4d
AJ
883 out = sb.c_str();
884 quote = 1;
885 }
38e16f92
AJ
886 break;
887
888 case LFT_ADAPTED_REQUEST_HEADER_ELEM:
6d19fc4d 889 if (al->adapted_request) {
36c774f7 890 sb = al->adapted_request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
6d19fc4d
AJ
891 out = sb.c_str();
892 quote = 1;
893 }
38e16f92
AJ
894 break;
895
6d19fc4d
AJ
896 case LFT_REPLY_HEADER_ELEM:
897 if (const Http::Message *msg = actualReplyHeader(al)) {
36c774f7 898 sb = msg->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
6d19fc4d
AJ
899 out = sb.c_str();
900 quote = 1;
901 }
902 break;
38e16f92
AJ
903
904 case LFT_REQUEST_ALL_HEADERS:
c529dfc7 905#if ICAP_CLIENT
bd59d61c
EB
906 if (al->icap.reqMethod == Adaptation::methodRespmod) {
907 // XXX: since AccessLogEntry::Headers lacks virgin response
908 // headers, do nothing for now
909 out = nullptr;
25dadb98
AR
910 } else
911#endif
912 {
49f57088
EB
913 // just headers without start-line and CRLF
914 // XXX: reconcile with '<h'
bd59d61c 915 out = al->headers.request;
6d19fc4d 916 quote = 1;
bd59d61c 917 }
38e16f92
AJ
918 break;
919
920 case LFT_ADAPTED_REQUEST_ALL_HEADERS:
49f57088
EB
921 // just headers without start-line and CRLF
922 // XXX: reconcile with '<h'
38e16f92 923 out = al->headers.adapted_request;
38e16f92 924 quote = 1;
38e16f92
AJ
925 break;
926
49f57088
EB
927 case LFT_REPLY_ALL_HEADERS: {
928 MemBuf allHeaders;
929 allHeaders.init();
930 // status-line + headers + CRLF
931 // XXX: reconcile with '>h' and '>ha'
932 al->packReplyHeaders(allHeaders);
933 sb.assign(allHeaders.content(), allHeaders.contentSize());
934 out = sb.c_str();
c529dfc7 935#if ICAP_CLIENT
bd59d61c
EB
936 if (!out && al->icap.reqMethod == Adaptation::methodReqmod)
937 out = al->headers.adapted_request;
25dadb98 938#endif
38e16f92 939 quote = 1;
49f57088
EB
940 }
941 break;
38e16f92
AJ
942
943 case LFT_USER_NAME:
c0e8c76f 944#if USE_AUTH
6d19fc4d 945 if (al->request && al->request->auth_user_request)
c0e8c76f
AJ
946 out = strOrNull(al->request->auth_user_request->username());
947#endif
93cc83e7
AJ
948 if (!out && al->request && al->request->extacl_user.size()) {
949 if (const char *t = al->request->extacl_user.termedBuf())
950 out = t;
951 }
38e16f92 952 if (!out)
e3bf07f5 953 out = strOrNull(al->getExtUser());
cb4f4424 954#if USE_OPENSSL
38e16f92 955 if (!out)
bd85ea1f 956 out = strOrNull(al->cache.ssluser);
38e16f92 957#endif
38e16f92
AJ
958 break;
959
960 case LFT_USER_LOGIN:
c0e8c76f 961#if USE_AUTH
6d19fc4d 962 if (al->request && al->request->auth_user_request)
c0e8c76f
AJ
963 out = strOrNull(al->request->auth_user_request->username());
964#endif
38e16f92
AJ
965 break;
966
38e16f92 967 case LFT_USER_EXTERNAL:
e3bf07f5 968 out = strOrNull(al->getExtUser());
38e16f92
AJ
969 break;
970
f53969cc
SM
971 /* case LFT_USER_REALM: */
972 /* case LFT_USER_SCHEME: */
38e16f92 973
f53969cc 974 // the fmt->type can not be LFT_HTTP_SENT_STATUS_CODE_OLD_30
2f8abb64 975 // but compiler complains if omitted
38e16f92
AJ
976 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
977 case LFT_HTTP_SENT_STATUS_CODE:
978 outint = al->http.code;
38e16f92 979 doint = 1;
38e16f92
AJ
980 break;
981
982 case LFT_HTTP_RECEIVED_STATUS_CODE:
6d19fc4d 983 if (al->hier.peer_reply_status != Http::scNone) {
38e16f92
AJ
984 outint = al->hier.peer_reply_status;
985 doint = 1;
986 }
987 break;
f53969cc
SM
988 /* case LFT_HTTP_STATUS:
989 * out = statusline->text;
990 * quote = 1;
991 * break;
992 */
38e16f92
AJ
993 case LFT_HTTP_BODY_BYTES_READ:
994 if (al->hier.bodyBytesRead >= 0) {
995 outoff = al->hier.bodyBytesRead;
996 dooff = 1;
997 }
998 // else if hier.bodyBytesRead < 0 we did not have any data exchange with
999 // a peer server so just print a "-" (eg requests served from cache,
1000 // or internal error messages).
1001 break;
1002
1003 case LFT_SQUID_STATUS:
a981b360 1004 out = al->cache.code.c_str();
38e16f92
AJ
1005 break;
1006
1007 case LFT_SQUID_ERROR:
83b053a0
CT
1008 if (const auto error = al->error())
1009 out = errorPageName(error->category);
38e16f92
AJ
1010 break;
1011
1012 case LFT_SQUID_ERROR_DETAIL:
83b053a0 1013 if (const auto error = al->error()) {
ea3f56e2
AR
1014 if (!error->details.empty()) {
1015 sb = ToSBuf(error->details);
83b053a0 1016 out = sb.c_str();
38e16f92 1017 }
83b053a0 1018 }
38e16f92
AJ
1019 break;
1020
1021 case LFT_SQUID_HIERARCHY:
1022 if (al->hier.ping.timedout)
1023 mb.append("TIMEOUT_", 8);
38e16f92 1024 out = hier_code_str[al->hier.code];
38e16f92
AJ
1025 break;
1026
e7a9b7a8
EB
1027 case LFT_SQUID_REQUEST_ATTEMPTS:
1028 outint = al->requestAttempts;
1029 doint = 1;
1030 break;
1031
38e16f92
AJ
1032 case LFT_MIME_TYPE:
1033 out = al->http.content_type;
38e16f92
AJ
1034 break;
1035
1036 case LFT_CLIENT_REQ_METHOD:
1037 if (al->request) {
6d19fc4d
AJ
1038 sb = al->request->method.image();
1039 out = sb.c_str();
38e16f92
AJ
1040 quote = 1;
1041 }
1042 break;
1043
1044 case LFT_CLIENT_REQ_URI:
bec110e4
EB
1045 if (const auto uri = al->effectiveVirginUrl()) {
1046 sb = *uri;
6d19fc4d 1047 out = sb.c_str();
38e16f92
AJ
1048 quote = 1;
1049 }
1050 break;
1051
5aca9cf2
AJ
1052 case LFT_CLIENT_REQ_URLSCHEME:
1053 if (al->request) {
6d19fc4d
AJ
1054 sb = al->request->url.getScheme().image();
1055 out = sb.c_str();
5aca9cf2
AJ
1056 quote = 1;
1057 }
1058 break;
1059
fa450988
AJ
1060 case LFT_CLIENT_REQ_URLDOMAIN:
1061 if (al->request) {
5c51bffb 1062 out = al->request->url.host();
fa450988
AJ
1063 quote = 1;
1064 }
1065 break;
1066
5aca9cf2 1067 case LFT_CLIENT_REQ_URLPORT:
380b09ae
AR
1068 if (al->request && al->request->url.port()) {
1069 outint = *al->request->url.port();
5aca9cf2
AJ
1070 doint = 1;
1071 }
1072 break;
1073
38e16f92
AJ
1074 case LFT_REQUEST_URLPATH_OLD_31:
1075 case LFT_CLIENT_REQ_URLPATH:
1076 if (al->request) {
6d19fc4d
AJ
1077 sb = al->request->url.path();
1078 out = sb.c_str();
38e16f92
AJ
1079 quote = 1;
1080 }
1081 break;
1082
1083 case LFT_CLIENT_REQ_VERSION:
1084 if (al->request) {
6d19fc4d
AJ
1085 sb.appendf("%u.%u", al->request->http_ver.major, al->request->http_ver.minor);
1086 out = sb.c_str();
38e16f92
AJ
1087 }
1088 break;
1089
1090 case LFT_REQUEST_METHOD:
ec2c4acf
AR
1091 if (al->hasLogMethod()) {
1092 sb = al->getLogMethod();
1093 out = sb.c_str();
1094 quote = 1;
1095 }
6d19fc4d 1096 break;
38e16f92
AJ
1097
1098 case LFT_REQUEST_URI:
f57ae909 1099 if (!al->url.isEmpty()) {
6d19fc4d
AJ
1100 sb = al->url;
1101 out = sb.c_str();
f57ae909 1102 }
38e16f92
AJ
1103 break;
1104
1105 case LFT_REQUEST_VERSION_OLD_2X:
1106 case LFT_REQUEST_VERSION:
6d19fc4d
AJ
1107 sb.appendf("%u.%u", al->http.version.major, al->http.version.minor);
1108 out = sb.c_str();
38e16f92
AJ
1109 break;
1110
1111 case LFT_SERVER_REQ_METHOD:
1112 if (al->adapted_request) {
6d19fc4d
AJ
1113 sb = al->adapted_request->method.image();
1114 out = sb.c_str();
38e16f92
AJ
1115 quote = 1;
1116 }
1117 break;
1118
1119 case LFT_SERVER_REQ_URI:
1120 // adapted request URI sent to server/peer
1121 if (al->adapted_request) {
6d19fc4d
AJ
1122 sb = al->adapted_request->effectiveRequestUri();
1123 out = sb.c_str();
38e16f92
AJ
1124 quote = 1;
1125 }
1126 break;
1127
5aca9cf2
AJ
1128 case LFT_SERVER_REQ_URLSCHEME:
1129 if (al->adapted_request) {
6d19fc4d
AJ
1130 sb = al->adapted_request->url.getScheme().image();
1131 out = sb.c_str();
5aca9cf2
AJ
1132 quote = 1;
1133 }
1134 break;
1135
1136 case LFT_SERVER_REQ_URLDOMAIN:
1137 if (al->adapted_request) {
5c51bffb 1138 out = al->adapted_request->url.host();
5aca9cf2
AJ
1139 quote = 1;
1140 }
1141 break;
1142
1143 case LFT_SERVER_REQ_URLPORT:
380b09ae
AR
1144 if (al->adapted_request && al->adapted_request->url.port()) {
1145 outint = *al->adapted_request->url.port();
5aca9cf2
AJ
1146 doint = 1;
1147 }
1148 break;
1149
38e16f92
AJ
1150 case LFT_SERVER_REQ_URLPATH:
1151 if (al->adapted_request) {
6d19fc4d
AJ
1152 sb = al->adapted_request->url.path();
1153 out = sb.c_str();
38e16f92
AJ
1154 quote = 1;
1155 }
1156 break;
1157
1158 case LFT_SERVER_REQ_VERSION:
1159 if (al->adapted_request) {
6d19fc4d 1160 sb.appendf("%u.%u",
2761b12a
SM
1161 al->adapted_request->http_ver.major,
1162 al->adapted_request->http_ver.minor);
38e16f92
AJ
1163 out = tmp;
1164 }
1165 break;
1166
d6df21d2 1167 case LFT_CLIENT_REQUEST_SIZE_TOTAL:
cc0ca3b9 1168 outoff = al->http.clientRequestSz.messageTotal();
38e16f92
AJ
1169 dooff = 1;
1170 break;
1171
d6df21d2 1172 case LFT_CLIENT_REQUEST_SIZE_HEADERS:
cc0ca3b9 1173 outoff = al->http.clientRequestSz.header;
38e16f92
AJ
1174 dooff =1;
1175 break;
d6df21d2 1176
f53969cc
SM
1177 /*case LFT_REQUEST_SIZE_BODY: */
1178 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
38e16f92 1179
d6df21d2 1180 case LFT_ADAPTED_REPLY_SIZE_TOTAL:
cc0ca3b9 1181 outoff = al->http.clientReplySz.messageTotal();
38e16f92
AJ
1182 dooff = 1;
1183 break;
1184
1185 case LFT_REPLY_HIGHOFFSET:
1186 outoff = al->cache.highOffset;
38e16f92 1187 dooff = 1;
38e16f92
AJ
1188 break;
1189
1190 case LFT_REPLY_OBJECTSIZE:
1191 outoff = al->cache.objectSize;
38e16f92 1192 dooff = 1;
38e16f92
AJ
1193 break;
1194
d6df21d2 1195 case LFT_ADAPTED_REPLY_SIZE_HEADERS:
cc0ca3b9 1196 outint = al->http.clientReplySz.header;
38e16f92
AJ
1197 doint = 1;
1198 break;
d6df21d2 1199
f53969cc
SM
1200 /*case LFT_REPLY_SIZE_BODY: */
1201 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
38e16f92 1202
d6df21d2 1203 case LFT_CLIENT_IO_SIZE_TOTAL:
cc0ca3b9 1204 outint = al->http.clientRequestSz.messageTotal() + al->http.clientReplySz.messageTotal();
d6df21d2
AJ
1205 doint = 1;
1206 break;
f53969cc 1207 /*case LFT_SERVER_IO_SIZE_TOTAL: */
d6df21d2 1208
38e16f92 1209 case LFT_TAG:
6d19fc4d 1210 if (al->request) {
38e16f92 1211 out = al->request->tag.termedBuf();
6d19fc4d
AJ
1212 quote = 1;
1213 }
38e16f92
AJ
1214 break;
1215
38e16f92 1216 case LFT_EXT_LOG:
6d19fc4d 1217 if (al->request) {
38e16f92 1218 out = al->request->extacl_log.termedBuf();
6d19fc4d
AJ
1219 quote = 1;
1220 }
38e16f92
AJ
1221 break;
1222
1223 case LFT_SEQUENCE_NUMBER:
1224 outoff = logSequenceNumber;
1225 dooff = 1;
1226 break;
1227
cb4f4424 1228#if USE_OPENSSL
08097970
AR
1229 case LFT_SSL_BUMP_MODE: {
1230 const Ssl::BumpMode mode = static_cast<Ssl::BumpMode>(al->ssl.bumpMode);
1231 // for Ssl::bumpEnd, Ssl::bumpMode() returns NULL and we log '-'
1232 out = Ssl::bumpMode(mode);
08097970 1233 }
3736fdd6 1234 break;
4e56d7f6
AJ
1235
1236 case LFT_EXT_ACL_USER_CERT_RAW:
1237 if (al->request) {
1238 ConnStateData *conn = al->request->clientConnectionManager.get();
33cc0629 1239 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
12b5040f
DN
1240 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1241 sb = sslGetUserCertificatePEM(ssl);
1242 out = sb.c_str();
1243 }
4e56d7f6
AJ
1244 }
1245 }
1246 break;
1247
1248 case LFT_EXT_ACL_USER_CERTCHAIN_RAW:
1249 if (al->request) {
1250 ConnStateData *conn = al->request->clientConnectionManager.get();
33cc0629 1251 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
12b5040f
DN
1252 if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
1253 sb = sslGetUserCertificatePEM(ssl);
1254 out = sb.c_str();
1255 }
4e56d7f6
AJ
1256 }
1257 }
1258 break;
1259
1260 case LFT_EXT_ACL_USER_CERT:
1261 if (al->request) {
1262 ConnStateData *conn = al->request->clientConnectionManager.get();
33cc0629
AJ
1263 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1264 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
c56abcbe 1265 out = sslGetUserAttribute(ssl, fmt->data.header.header);
4e56d7f6
AJ
1266 }
1267 }
1268 break;
1269
1270 case LFT_EXT_ACL_USER_CA_CERT:
1271 if (al->request) {
1272 ConnStateData *conn = al->request->clientConnectionManager.get();
33cc0629
AJ
1273 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
1274 if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
c56abcbe 1275 out = sslGetCAAttribute(ssl, fmt->data.header.header);
4e56d7f6
AJ
1276 }
1277 }
1278 break;
71cae389 1279
f4698e0b 1280 case LFT_SSL_USER_CERT_SUBJECT:
907831e6
AR
1281 if (const auto &cert = al->cache.sslClientCert) {
1282 sb = Security::SubjectName(*cert);
1283 out = sb.c_str();
f4698e0b
CT
1284 }
1285 break;
1286
1287 case LFT_SSL_USER_CERT_ISSUER:
907831e6
AR
1288 if (const auto &cert = al->cache.sslClientCert) {
1289 sb = Security::IssuerName(*cert);
1290 out = sb.c_str();
f4698e0b
CT
1291 }
1292 break;
4e56d7f6 1293
cedca6e7
CT
1294 case LFT_SSL_CLIENT_SNI:
1295 if (al->request && al->request->clientConnectionManager.valid()) {
4f6990ec
CT
1296 if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
1297 if (!conn->tlsClientSni().isEmpty()) {
1298 sb = conn->tlsClientSni();
1299 out = sb.c_str();
1300 }
cedca6e7
CT
1301 }
1302 }
1303 break;
789dda8d 1304
7598eb63
CT
1305 case LFT_SSL_SERVER_CERT_ERRORS:
1306 if (al->request && al->request->clientConnectionManager.valid()) {
1307 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1308 const char *separator = fmt->data.string ? fmt->data.string : ":";
6d19fc4d
AJ
1309 for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
1310 if (!sb.isEmpty())
7598eb63 1311 sb.append(separator);
1dcb1d49 1312 sb.append(Ssl::GetErrorName(sslError->element.code, true));
6d19fc4d
AJ
1313 if (sslError->element.depth >= 0)
1314 sb.appendf("@depth=%d", sslError->element.depth);
7598eb63 1315 }
6d19fc4d
AJ
1316 if (!sb.isEmpty())
1317 out = sb.c_str();
7598eb63
CT
1318 }
1319 }
cedca6e7 1320 break;
789dda8d
CT
1321
1322 case LFT_SSL_SERVER_CERT_ISSUER:
1323 case LFT_SSL_SERVER_CERT_SUBJECT:
12b5040f 1324 case LFT_SSL_SERVER_CERT_WHOLE:
d4ddb3e6
CT
1325 if (al->request && al->request->clientConnectionManager.valid()) {
1326 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
1327 if (X509 *serverCert = srvBump->serverCert.get()) {
1328 if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
1329 out = Ssl::GetX509UserAttribute(serverCert, "DN");
12b5040f 1330 else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
d4ddb3e6 1331 out = Ssl::GetX509CAAttribute(serverCert, "DN");
12b5040f
DN
1332 else {
1333 assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
1334 sb = Ssl::GetX509PEM(serverCert);
1335 out = sb.c_str();
1336 quote = 1;
1337 }
d4ddb3e6
CT
1338 }
1339 }
1340 }
789dda8d 1341 break;
2bcab852
CT
1342
1343 case LFT_TLS_CLIENT_NEGOTIATED_VERSION:
6d19fc4d 1344 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
2bcab852
CT
1345 out = al->tcpClient->hasTlsNegotiations()->negotiatedVersion();
1346 break;
1347
1348 case LFT_TLS_SERVER_NEGOTIATED_VERSION:
6d19fc4d 1349 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
2bcab852
CT
1350 out = al->hier.tcpServer->hasTlsNegotiations()->negotiatedVersion();
1351 break;
1352
1353 case LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION:
6d19fc4d 1354 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
2bcab852
CT
1355 out = al->tcpClient->hasTlsNegotiations()->helloVersion();
1356 break;
1357
1358 case LFT_TLS_SERVER_RECEIVED_HELLO_VERSION:
6d19fc4d 1359 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
2bcab852
CT
1360 out = al->hier.tcpServer->hasTlsNegotiations()->helloVersion();
1361 break;
1362
1363 case LFT_TLS_CLIENT_SUPPORTED_VERSION:
6d19fc4d 1364 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
2bcab852
CT
1365 out = al->tcpClient->hasTlsNegotiations()->supportedVersion();
1366 break;
1367
1368 case LFT_TLS_SERVER_SUPPORTED_VERSION:
6d19fc4d 1369 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
2bcab852
CT
1370 out = al->hier.tcpServer->hasTlsNegotiations()->supportedVersion();
1371 break;
1372
1373 case LFT_TLS_CLIENT_NEGOTIATED_CIPHER:
6d19fc4d 1374 if (al->tcpClient && al->tcpClient->hasTlsNegotiations())
2bcab852
CT
1375 out = al->tcpClient->hasTlsNegotiations()->cipherName();
1376 break;
1377
1378 case LFT_TLS_SERVER_NEGOTIATED_CIPHER:
6d19fc4d 1379 if (al->hier.tcpServer && al->hier.tcpServer->hasTlsNegotiations())
2bcab852
CT
1380 out = al->hier.tcpServer->hasTlsNegotiations()->cipherName();
1381 break;
08097970 1382#endif
f123f5e9 1383
d074f918
TT
1384 case LFT_REQUEST_URLGROUP_OLD_2X:
1385 assert(LFT_REQUEST_URLGROUP_OLD_2X == 0); // should never happen.
8b082ed9 1386 break;
d074f918 1387
d7f4a0b7 1388 case LFT_NOTE:
c7bcf010
CT
1389 tmp[0] = fmt->data.header.separator;
1390 tmp[1] = '\0';
1391 if (fmt->data.header.header && *fmt->data.header.header) {
1392 const char *separator = tmp;
75d47340 1393 static SBuf note;
cf9f0261 1394#if USE_ADAPTATION
7f0ceafc 1395 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
6d19fc4d 1396 if (ah && ah->metaHeaders) {
75d47340 1397 if (ah->metaHeaders->find(note, fmt->data.header.header, separator))
6d19fc4d 1398 sb.append(note);
cf9f0261
CT
1399 }
1400#endif
6d19fc4d 1401 if (al->notes) {
75d47340 1402 if (al->notes->find(note, fmt->data.header.header, separator)) {
6d19fc4d 1403 if (!sb.isEmpty())
c7bcf010 1404 sb.append(separator);
6d19fc4d 1405 sb.append(note);
cf9f0261
CT
1406 }
1407 }
6d19fc4d 1408 out = sb.c_str();
d7f4a0b7
CT
1409 quote = 1;
1410 } else {
e67eab3a
FC
1411 // No specific annotation requested. Report all annotations.
1412
c7bcf010
CT
1413 // if no argument given use default "\r\n" as notes separator
1414 const char *separator = fmt->data.string ? tmp : "\r\n";
e67eab3a 1415 SBufStream os;
cf9f0261 1416#if USE_ADAPTATION
7f0ceafc 1417 Adaptation::History::Pointer ah = al->request ? al->request->adaptHistory() : Adaptation::History::Pointer();
e67eab3a
FC
1418 if (ah && ah->metaHeaders)
1419 ah->metaHeaders->print(os, ": ", separator);
cf9f0261 1420#endif
e67eab3a
FC
1421 if (al->notes)
1422 al->notes->print(os, ": ", separator);
f4f55a21 1423
e67eab3a 1424 sb = os.buf();
6d19fc4d 1425 out = sb.c_str();
d7f4a0b7
CT
1426 quote = 1;
1427 }
1428 break;
08097970 1429
d4806c91
CT
1430 case LFT_CREDENTIALS:
1431#if USE_AUTH
6d19fc4d 1432 if (al->request && al->request->auth_user_request)
d4806c91
CT
1433 out = strOrNull(al->request->auth_user_request->credentialsStr());
1434#endif
d4806c91
CT
1435 break;
1436
38e16f92
AJ
1437 case LFT_PERCENT:
1438 out = "%";
5aca9cf2 1439 break;
38e16f92 1440
5aca9cf2 1441 case LFT_EXT_ACL_NAME:
25aa6c9a
AR
1442 if (!al->lastAclName.isEmpty())
1443 out = al->lastAclName.c_str();
4e56d7f6
AJ
1444 break;
1445
5aca9cf2 1446 case LFT_EXT_ACL_DATA:
b0e14ce2
NH
1447 if (!al->lastAclData.isEmpty())
1448 out = al->lastAclData.c_str();
38e16f92 1449 break;
7cfd3a41
EB
1450
1451 case LFT_MASTER_XACTION:
1452 if (al->request) {
1453 doUint64 = true;
1454 outUint64 = static_cast<uint64_t>(al->request->masterXaction->id.value);
1455 break;
1456 }
38e16f92
AJ
1457 }
1458
1459 if (dooff) {
6d19fc4d
AJ
1460 sb.appendf("%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
1461 out = sb.c_str();
38e16f92
AJ
1462
1463 } else if (doint) {
6d19fc4d
AJ
1464 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
1465 out = sb.c_str();
7cfd3a41
EB
1466 } else if (doUint64) {
1467 sb.appendf("%0*" PRIu64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outUint64);
1468 out = sb.c_str();
01bd87d8
CT
1469 } else if (doMsec) {
1470 if (fmt->widthMax < 0) {
f1162d09 1471 sb.appendf("%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, tvToMsec(outtv));
01bd87d8
CT
1472 } else {
1473 int precision = fmt->widthMax;
6d19fc4d 1474 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)));
01bd87d8 1475 }
6d19fc4d 1476 out = sb.c_str();
01bd87d8
CT
1477 } else if (doSec) {
1478 int precision = fmt->widthMax >=0 ? fmt->widthMax :3;
6d19fc4d
AJ
1479 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));
1480 out = sb.c_str();
38e16f92
AJ
1481 }
1482
1483 if (out && *out) {
1484 if (quote || fmt->quote != LOG_QUOTE_NONE) {
051b132b
EB
1485 // Do not write to the tmp buffer because it may contain the to-be-quoted value.
1486 static char quotedOut[2 * sizeof(tmp)];
1487 static_assert(sizeof(quotedOut) > 0, "quotedOut has zero length");
1488 quotedOut[0] = '\0';
1489
aee3523a 1490 char *newout = nullptr;
38e16f92
AJ
1491 int newfree = 0;
1492
1493 switch (fmt->quote) {
1494
1495 case LOG_QUOTE_NONE:
1496 newout = rfc1738_escape_unescaped(out);
1497 break;
1498
1499 case LOG_QUOTE_QUOTES: {
1500 size_t out_len = static_cast<size_t>(strlen(out)) * 2 + 1;
1501 if (out_len >= sizeof(tmp)) {
1502 newout = (char *)xmalloc(out_len);
1503 newfree = 1;
1504 } else
051b132b 1505 newout = quotedOut;
38e16f92
AJ
1506 log_quoted_string(out, newout);
1507 }
1508 break;
1509
1510 case LOG_QUOTE_MIMEBLOB:
1511 newout = QuoteMimeBlob(out);
1512 newfree = 1;
1513 break;
1514
1515 case LOG_QUOTE_URL:
1516 newout = rfc1738_escape(out);
1517 break;
1518
95d78f10
AJ
1519 case LOG_QUOTE_SHELL: {
1520 MemBuf mbq;
1521 mbq.init();
1522 strwordquote(&mbq, out);
1523 newout = mbq.content();
1524 mbq.stolen = 1;
1525 newfree = 1;
3736fdd6
SM
1526 }
1527 break;
95d78f10 1528
38e16f92
AJ
1529 case LOG_QUOTE_RAW:
1530 break;
1531 }
1532
1533 if (newout) {
1534 if (dofree)
1535 safe_free(out);
1536
1537 out = newout;
1538
1539 dofree = newfree;
1540 }
1541 }
1542
c32c6db7 1543 // enforce width limits if configured
7cfd3a41 1544 const bool haveMaxWidth = fmt->widthMax >=0 && !doint && !dooff && !doMsec && !doSec && !doUint64;
8846a2b4
CT
1545 if (haveMaxWidth || fmt->widthMin) {
1546 const int minWidth = fmt->widthMin >= 0 ?
b8ad91f5 1547 fmt->widthMin :0;
c32c6db7 1548 const int maxWidth = haveMaxWidth ?
8846a2b4 1549 fmt->widthMax : strlen(out);
c32c6db7 1550
38e16f92 1551 if (fmt->left)
4391cd15 1552 mb.appendf("%-*.*s", minWidth, maxWidth, out);
38e16f92 1553 else
4391cd15 1554 mb.appendf("%*.*s", minWidth, maxWidth, out);
38e16f92
AJ
1555 } else
1556 mb.append(out, strlen(out));
1557 } else {
1558 mb.append("-", 1);
1559 }
1560
1561 if (fmt->space)
1562 mb.append(" ", 1);
1563
6d19fc4d 1564 sb.clear();
38e16f92
AJ
1565
1566 if (dofree)
1567 safe_free(out);
1568 }
1569}
f53969cc 1570