]> git.ipfire.org Git - thirdparty/squid.git/blob - src/format/Token.cc
Merged from trunk
[thirdparty/squid.git] / src / format / Token.cc
1 /*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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
9 #include "squid.h"
10 #include "format/Config.h"
11 #include "format/Token.h"
12 #include "format/TokenTableEntry.h"
13 #include "globals.h"
14 #include "SquidConfig.h"
15 #include "Store.h"
16
17 // Due to token overlaps between 1 and 2 letter tokens (Bug 3310)
18 // We split the token table into sets determined by the token length
19 namespace Format
20 {
21
22 /// 1-char tokens.
23 static TokenTableEntry TokenTable1C[] = {
24
25 {">a", LFT_CLIENT_IP_ADDRESS},
26 {">p", LFT_CLIENT_PORT},
27 {">A", LFT_CLIENT_FQDN},
28
29 {"<a", LFT_SERVER_IP_ADDRESS},
30 {"<p", LFT_SERVER_PORT},
31 {"<A", LFT_SERVER_FQDN_OR_PEER_NAME},
32
33 {">h", LFT_REQUEST_HEADER},
34 {">h", LFT_REQUEST_ALL_HEADERS},
35 {"<h", LFT_REPLY_HEADER},
36 {"<h", LFT_REPLY_ALL_HEADERS},
37
38 {">v", LFT_REQUEST_VERSION_OLD_2X},
39
40 {"%", LFT_PERCENT},
41
42 {NULL, LFT_NONE} /* this must be last */
43 };
44
45 /// 2-char tokens
46 static TokenTableEntry TokenTable2C[] = {
47
48 {">la", LFT_CLIENT_LOCAL_IP},
49 {"la", LFT_LOCAL_LISTENING_IP},
50 {">lp", LFT_CLIENT_LOCAL_PORT},
51 {"lp", LFT_LOCAL_LISTENING_PORT},
52 /*{ "lA", LFT_LOCAL_NAME }, */
53
54 {"<la", LFT_SERVER_LOCAL_IP},
55 {"oa", LFT_SERVER_LOCAL_IP_OLD_27},
56 {"<lp", LFT_SERVER_LOCAL_PORT},
57
58 {"ts", LFT_TIME_SECONDS_SINCE_EPOCH},
59 {"tu", LFT_TIME_SUBSECOND},
60 {"tl", LFT_TIME_LOCALTIME},
61 {"tg", LFT_TIME_GMT},
62 {"tS", LFT_TIME_START},
63 {"tr", LFT_TIME_TO_HANDLE_REQUEST},
64
65 {"<pt", LFT_PEER_RESPONSE_TIME},
66 {"<tt", LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME},
67 {"dt", LFT_DNS_WAIT_TIME},
68
69 {">ha", LFT_ADAPTED_REQUEST_HEADER},
70 {">ha", LFT_ADAPTED_REQUEST_ALL_HEADERS},
71
72 {"un", LFT_USER_NAME},
73 {"ul", LFT_USER_LOGIN},
74 /*{ "ur", LFT_USER_REALM }, */
75 /*{ "us", LFT_USER_SCHEME }, */
76 {"ui", LFT_USER_IDENT},
77 {"ue", LFT_USER_EXTERNAL},
78
79 {"Hs", LFT_HTTP_SENT_STATUS_CODE_OLD_30},
80 {">Hs", LFT_HTTP_SENT_STATUS_CODE},
81 {"<Hs", LFT_HTTP_RECEIVED_STATUS_CODE},
82 /*{ "Ht", LFT_HTTP_STATUS }, */
83 {"<bs", LFT_HTTP_BODY_BYTES_READ},
84
85 {"Ss", LFT_SQUID_STATUS},
86 {"Sh", LFT_SQUID_HIERARCHY},
87
88 {"mt", LFT_MIME_TYPE},
89
90 {">rm", LFT_CLIENT_REQ_METHOD},
91 {">ru", LFT_CLIENT_REQ_URI},
92 {">rs", LFT_CLIENT_REQ_URLSCHEME},
93 {">rd", LFT_CLIENT_REQ_URLDOMAIN},
94 {">rP", LFT_CLIENT_REQ_URLPORT},
95 {">rp", LFT_CLIENT_REQ_URLPATH},
96 /*{">rq", LFT_CLIENT_REQ_QUERY},*/
97 {">rv", LFT_CLIENT_REQ_VERSION},
98
99 {"rm", LFT_REQUEST_METHOD},
100 {"ru", LFT_REQUEST_URI}, /* doesn't include the query-string */
101 {"rp", LFT_REQUEST_URLPATH_OLD_31},
102 /* { "rq", LFT_REQUEST_QUERY }, * / / * the query-string, INCLUDING the leading ? */
103 {"rv", LFT_REQUEST_VERSION},
104 {"rG", LFT_REQUEST_URLGROUP_OLD_2X},
105
106 {"<rm", LFT_SERVER_REQ_METHOD},
107 {"<ru", LFT_SERVER_REQ_URI},
108 {"<rs", LFT_SERVER_REQ_URLSCHEME},
109 {"<rd", LFT_SERVER_REQ_URLDOMAIN},
110 {"<rP", LFT_SERVER_REQ_URLPORT},
111 {"<rp", LFT_SERVER_REQ_URLPATH},
112 /*{"<rq", LFT_SERVER_REQ_QUERY},*/
113 {"<rv", LFT_SERVER_REQ_VERSION},
114
115 {">st", LFT_CLIENT_REQUEST_SIZE_TOTAL },
116 {">sh", LFT_CLIENT_REQUEST_SIZE_HEADERS },
117 /*{ ">sb", LFT_REQUEST_SIZE_BODY }, */
118 /*{ ">sB", LFT_REQUEST_SIZE_BODY_NO_TE }, */
119
120 {"<st", LFT_ADAPTED_REPLY_SIZE_TOTAL}, // XXX: adapted should be code: <sta
121 {"<sH", LFT_REPLY_HIGHOFFSET},
122 {"<sS", LFT_REPLY_OBJECTSIZE},
123 {"<sh", LFT_ADAPTED_REPLY_SIZE_HEADERS }, // XXX: adapted should be code: <sha
124 /*{ "<sb", LFT_REPLY_SIZE_BODY }, */
125 /*{ "<sB", LFT_REPLY_SIZE_BODY_NO_TE }, */
126
127 {"st", LFT_CLIENT_IO_SIZE_TOTAL}, // XXX: total from client should be stC ??
128 /*{"stP", LFT_SERVER_IO_SIZE_TOTAL},*/
129
130 {"et", LFT_TAG},
131 {"ea", LFT_EXT_LOG},
132 {"sn", LFT_SEQUENCE_NUMBER},
133
134 {NULL, LFT_NONE} /* this must be last */
135 };
136
137 /// Miscellaneous >2 byte tokens
138 static TokenTableEntry TokenTableMisc[] = {
139 {">eui", LFT_CLIENT_EUI},
140 {">qos", LFT_CLIENT_LOCAL_TOS},
141 {"<qos", LFT_SERVER_LOCAL_TOS},
142 {">nfmark", LFT_CLIENT_LOCAL_NFMARK},
143 {"<nfmark", LFT_SERVER_LOCAL_NFMARK},
144 {"err_code", LFT_SQUID_ERROR },
145 {"err_detail", LFT_SQUID_ERROR_DETAIL },
146 {"note", LFT_NOTE },
147 {"credentials", LFT_CREDENTIALS},
148 {NULL, LFT_NONE} /* this must be last */
149 };
150
151 #if USE_ADAPTATION
152 static TokenTableEntry TokenTableAdapt[] = {
153 {"all_trs", LFT_ADAPTATION_ALL_XACT_TIMES},
154 {"sum_trs", LFT_ADAPTATION_SUM_XACT_TIMES},
155 {"<last_h", LFT_ADAPTATION_LAST_HEADER},
156 {NULL, LFT_NONE} /* this must be last */
157 };
158 #endif
159
160 #if ICAP_CLIENT
161 /// ICAP (icap::) tokens
162 static TokenTableEntry TokenTableIcap[] = {
163 {"tt", LFT_ICAP_TOTAL_TIME},
164 {"<last_h", LFT_ADAPTATION_LAST_HEADER}, // deprecated
165
166 {"<A", LFT_ICAP_ADDR},
167 {"<service_name", LFT_ICAP_SERV_NAME},
168 {"ru", LFT_ICAP_REQUEST_URI},
169 {"rm", LFT_ICAP_REQUEST_METHOD},
170 {">st", LFT_ICAP_BYTES_SENT},
171 {"<st", LFT_ICAP_BYTES_READ},
172 {"<bs", LFT_ICAP_BODY_BYTES_READ},
173
174 {">h", LFT_ICAP_REQ_HEADER},
175 {"<h", LFT_ICAP_REP_HEADER},
176
177 {"tr", LFT_ICAP_TR_RESPONSE_TIME},
178 {"tio", LFT_ICAP_IO_TIME},
179 {"to", LFT_ICAP_OUTCOME},
180 {"Hs", LFT_ICAP_STATUS_CODE},
181
182 {NULL, LFT_NONE} /* this must be last */
183 };
184 #endif
185
186 #if USE_OPENSSL
187 // SSL (ssl::) tokens
188 static TokenTableEntry TokenTableSsl[] = {
189 {"bump_mode", LFT_SSL_BUMP_MODE},
190 {">cert_subject", LFT_SSL_USER_CERT_SUBJECT},
191 {">cert_issuer", LFT_SSL_USER_CERT_ISSUER},
192 {">sni", LFT_SSL_CLIENT_SNI},
193 /*{"<cert_subject", LFT_SSL_SERVER_CERT_SUBJECT}, */
194 /*{"<cert_issuer", LFT_SSL_SERVER_CERT_ISSUER}, */
195 {NULL, LFT_NONE}
196 };
197 #endif
198 } // namespace Format
199
200 /// Register all components custom format tokens
201 void
202 Format::Token::Init()
203 {
204 // TODO standard log tokens
205 // TODO external ACL fmt tokens
206
207 #if USE_ADAPTATION
208 TheConfig.registerTokens(String("adapt"),::Format::TokenTableAdapt);
209 #endif
210 #if ICAP_CLIENT
211 TheConfig.registerTokens(String("icap"),::Format::TokenTableIcap);
212 #endif
213 #if USE_OPENSSL
214 TheConfig.registerTokens(String("ssl"),::Format::TokenTableSsl);
215 #endif
216 }
217
218 /// Scans a token table to see if the next token exists there
219 /// returns a pointer to next unparsed byte and updates type member if found
220 const char *
221 Format::Token::scanForToken(TokenTableEntry const table[], const char *cur)
222 {
223 for (TokenTableEntry const *lte = table; lte->configTag != NULL; ++lte) {
224 debugs(46, 8, HERE << "compare tokens '" << lte->configTag << "' with '" << cur << "'");
225 if (strncmp(lte->configTag, cur, strlen(lte->configTag)) == 0) {
226 type = lte->tokenType;
227 label = lte->configTag;
228 debugs(46, 7, HERE << "Found token '" << label << "'");
229 return cur + strlen(lte->configTag);
230 }
231 }
232 return cur;
233 }
234
235 /* parses a single token. Returns the token length in characters,
236 * and fills in the lt item with the token information.
237 * def is for sure null-terminated
238 */
239 int
240 Format::Token::parse(const char *def, Quoting *quoting)
241 {
242 const char *cur = def;
243
244 int l;
245
246 l = strcspn(cur, "%");
247
248 if (l > 0) {
249 char *cp;
250 /* it's a string for sure, until \0 or the next % */
251 cp = (char *)xmalloc(l + 1);
252 xstrncpy(cp, cur, l + 1);
253 type = LFT_STRING;
254 data.string = cp;
255
256 while (l > 0) {
257 switch (*cur) {
258
259 case '"':
260
261 if (*quoting == LOG_QUOTE_NONE)
262 *quoting = LOG_QUOTE_QUOTES;
263 else if (*quoting == LOG_QUOTE_QUOTES)
264 *quoting = LOG_QUOTE_NONE;
265
266 break;
267
268 case '[':
269 if (*quoting == LOG_QUOTE_NONE)
270 *quoting = LOG_QUOTE_MIMEBLOB;
271
272 break;
273
274 case ']':
275 if (*quoting == LOG_QUOTE_MIMEBLOB)
276 *quoting = LOG_QUOTE_NONE;
277
278 break;
279 }
280
281 ++cur;
282 --l;
283 }
284
285 } else if (*cur) {
286
287 ++cur;
288
289 // select quoting style for his particular token
290 switch (*cur) {
291
292 case '"':
293 quote = LOG_QUOTE_QUOTES;
294 ++cur;
295 break;
296
297 case '\'':
298 quote = LOG_QUOTE_RAW;
299 ++cur;
300 break;
301
302 case '[':
303 quote = LOG_QUOTE_MIMEBLOB;
304 ++cur;
305 break;
306
307 case '#':
308 quote = LOG_QUOTE_URL;
309 ++cur;
310 break;
311
312 default:
313 quote = *quoting;
314 break;
315 }
316
317 if (*cur == '-') {
318 left = true;
319 ++cur;
320 }
321
322 if (*cur == '0') {
323 zero = true;
324 ++cur;
325 }
326
327 char *endp;
328 if (xisdigit(*cur)) {
329 widthMin = strtol(cur, &endp, 10);
330 cur = endp;
331 }
332
333 if (*cur == '.' && xisdigit(*(++cur))) {
334 widthMax = strtol(cur, &endp, 10);
335 cur = endp;
336 }
337
338 if (*cur == '{') {
339 char *cp;
340 ++cur;
341 l = strcspn(cur, "}");
342 cp = (char *)xmalloc(l + 1);
343 xstrncpy(cp, cur, l + 1);
344 data.string = cp;
345 cur += l;
346
347 if (*cur == '}')
348 ++cur;
349 }
350
351 type = LFT_NONE;
352
353 // Scan each registered token namespace
354 debugs(46, 9, HERE << "check for token in " << TheConfig.tokens.size() << " namespaces.");
355 for (std::list<TokenNamespace>::const_iterator itr = TheConfig.tokens.begin(); itr != TheConfig.tokens.end(); ++itr) {
356 debugs(46, 7, HERE << "check for possible " << itr->prefix << ":: token");
357 const size_t len = itr->prefix.size();
358 if (itr->prefix.cmp(cur, len) == 0 && cur[len] == ':' && cur[len+1] == ':') {
359 debugs(46, 5, HERE << "check for " << itr->prefix << ":: token in '" << cur << "'");
360 const char *old = cur;
361 cur = scanForToken(itr->tokenSet, cur+len+2);
362 if (old != cur) // found
363 break;
364 else // reset to start of namespace
365 cur = cur - len - 2;
366 }
367 }
368
369 if (type == LFT_NONE) {
370 // For upward compatibility, assume "http::" prefix as default prefix
371 // for all log access formatting codes, except those starting with a
372 // "%" or a known namespace. (ie "icap::", "adapt::")
373 if (strncmp(cur,"http::", 6) == 0 && *(cur+6) != '%' )
374 cur += 6;
375
376 // NP: scan the sets of tokens in decreasing size to guarantee no
377 // mistakes made with overlapping names. (Bug 3310)
378
379 // Scan for various long tokens
380 debugs(46, 5, HERE << "scan for possible Misc token");
381 cur = scanForToken(TokenTableMisc, cur);
382 // scan for 2-char tokens
383 if (type == LFT_NONE) {
384 debugs(46, 5, HERE << "scan for possible 2C token");
385 cur = scanForToken(TokenTable2C, cur);
386 }
387 // finally scan for 1-char tokens.
388 if (type == LFT_NONE) {
389 debugs(46, 5, HERE << "scan for possible 1C token");
390 cur = scanForToken(TokenTable1C, cur);
391 }
392 }
393
394 if (type == LFT_NONE) {
395 fatalf("Can't parse configuration token: '%s'\n", def);
396 }
397
398 if (*cur == ' ') {
399 space = true;
400 ++cur;
401 }
402 }
403
404 switch (type) {
405
406 #if USE_ADAPTATION
407 case LFT_ADAPTATION_LAST_HEADER:
408 #endif
409
410 #if ICAP_CLIENT
411 case LFT_ICAP_REQ_HEADER:
412
413 case LFT_ICAP_REP_HEADER:
414 #endif
415
416 case LFT_ADAPTED_REQUEST_HEADER:
417
418 case LFT_REQUEST_HEADER:
419
420 case LFT_REPLY_HEADER:
421
422 case LFT_NOTE:
423
424 if (data.string) {
425 char *header = data.string;
426 char *cp = strchr(header, ':');
427
428 if (cp) {
429 *cp = '\0';
430 ++cp;
431
432 if (*cp == ',' || *cp == ';' || *cp == ':') {
433 data.header.separator = *cp;
434 ++cp;
435 } else {
436 data.header.separator = ',';
437 }
438
439 data.header.element = cp;
440
441 switch (type) {
442 case LFT_REQUEST_HEADER:
443 type = LFT_REQUEST_HEADER_ELEM;
444 break;
445
446 case LFT_ADAPTED_REQUEST_HEADER:
447 type = LFT_ADAPTED_REQUEST_HEADER_ELEM;
448 break;
449
450 case LFT_REPLY_HEADER:
451 type = LFT_REPLY_HEADER_ELEM;
452 break;
453 #if USE_ADAPTATION
454 case LFT_ADAPTATION_LAST_HEADER:
455 type = LFT_ADAPTATION_LAST_HEADER_ELEM;
456 break;
457 #endif
458 #if ICAP_CLIENT
459 case LFT_ICAP_REQ_HEADER:
460 type = LFT_ICAP_REQ_HEADER_ELEM;
461 break;
462 case LFT_ICAP_REP_HEADER:
463 type = LFT_ICAP_REP_HEADER_ELEM;
464 break;
465 #endif
466 default:
467 break;
468 }
469 }
470
471 data.header.header = header;
472 } else {
473 switch (type) {
474 case LFT_REQUEST_HEADER:
475 type = LFT_REQUEST_ALL_HEADERS;
476 break;
477
478 case LFT_ADAPTED_REQUEST_HEADER:
479 type = LFT_ADAPTED_REQUEST_ALL_HEADERS;
480 break;
481
482 case LFT_REPLY_HEADER:
483 type = LFT_REPLY_ALL_HEADERS;
484 break;
485 #if USE_ADAPTATION
486 case LFT_ADAPTATION_LAST_HEADER:
487 type = LFT_ADAPTATION_LAST_ALL_HEADERS;
488 break;
489 #endif
490 #if ICAP_CLIENT
491 case LFT_ICAP_REQ_HEADER:
492 type = LFT_ICAP_REQ_ALL_HEADERS;
493 break;
494 case LFT_ICAP_REP_HEADER:
495 type = LFT_ICAP_REP_ALL_HEADERS;
496 break;
497 #endif
498 default:
499 break;
500 }
501 Config.onoff.log_mime_hdrs = 1;
502 }
503
504 break;
505
506 case LFT_CLIENT_FQDN:
507 Config.onoff.log_fqdn = 1;
508 break;
509
510 case LFT_TIME_TO_HANDLE_REQUEST:
511 case LFT_PEER_RESPONSE_TIME:
512 case LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME:
513 case LFT_DNS_WAIT_TIME:
514 #if ICAP_CLIENT
515 case LFT_ICAP_TR_RESPONSE_TIME:
516 case LFT_ICAP_IO_TIME:
517 case LFT_ICAP_TOTAL_TIME:
518 #endif
519 case LFT_TIME_START:
520 case LFT_TIME_SUBSECOND:
521 divisor = 1000;
522
523 if (widthMax > 0) {
524 divisor = 1000000;
525
526 for (int i = widthMax; i > 0; --i)
527 divisor /= 10;
528
529 if (!divisor)
530 divisor = 1;
531 }
532 break;
533
534 case LFT_HTTP_SENT_STATUS_CODE_OLD_30:
535 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"Hs\" formatting code is deprecated. Use the \">Hs\" instead.");
536 type = LFT_HTTP_SENT_STATUS_CODE;
537 break;
538
539 case LFT_SERVER_LOCAL_IP_OLD_27:
540 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"oa\" formatting code is deprecated. Use the \"<la\" instead.");
541 type = LFT_SERVER_LOCAL_IP;
542 break;
543
544 case LFT_REQUEST_URLPATH_OLD_31:
545 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rp\" formatting code is deprecated. Use the \">rp\" instead.");
546 type = LFT_CLIENT_REQ_URLPATH;
547 break;
548
549 case LFT_REQUEST_VERSION_OLD_2X:
550 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \">v\" formatting code is deprecated. Use the \">rv\" instead.");
551 type = LFT_REQUEST_VERSION;
552 break;
553
554 #if !USE_SQUID_EUI
555 case LFT_CLIENT_EUI:
556 debugs(46, DBG_CRITICAL, "WARNING: The \">eui\" formatting code requires EUI features which are disabled in this Squid.");
557 break;
558 #endif
559
560 case LFT_REQUEST_URLGROUP_OLD_2X:
561 debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rG\" formatting code is deprecated. Use \"note{urlgroup}\" instead.");
562 type = LFT_NOTE;
563 data.header.header = xstrdup("urlgroup");
564 break;
565
566 default:
567 break;
568 }
569
570 return (cur - def);
571 }
572
573 Format::Token::Token() : type(LFT_NONE),
574 label(NULL),
575 widthMin(-1),
576 widthMax(-1),
577 quote(LOG_QUOTE_NONE),
578 left(false),
579 space(false),
580 zero(false),
581 divisor(1),
582 next(NULL)
583 {
584 data.string = NULL;
585 data.header.header = NULL;
586 data.header.element = NULL;
587 data.header.separator = ',';
588 }
589
590 Format::Token::~Token()
591 {
592 label = NULL; // drop reference to global static.
593 safe_free(data.string);
594 while (next) {
595 Token *tokens = next;
596 next = next->next;
597 tokens->next = NULL;
598 delete tokens;
599 }
600 }
601