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