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