Token.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 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 "proxyp/Elements.h"
15 #include "sbuf/Stream.h"
16 #include "SquidConfig.h"
17 #include "Store.h"
18 
19 // Due to token overlaps between 1 and 2 letter tokens (Bug 3310)
20 // We split the token table into sets determined by the token length
21 namespace Format
22 {
23 
26 
30 
34 
39 
41 
43 
44  TokenTableEntry(NULL, LFT_NONE) /* this must be last */
45 };
46 
49 
54  /*TokenTableEntry( "lA", LFT_LOCAL_NAME ), */
55 
59 
66 
70 
73 
76  /*TokenTableEntry( "ur", LFT_USER_REALM ), */
77  /*TokenTableEntry( "us", LFT_USER_SCHEME ), */
80 
84  /*TokenTableEntry( "Ht", LFT_HTTP_STATUS ), */
86 
89 
91 
98  /*TokenTableEntry(">rq", LFT_CLIENT_REQ_QUERY),*/
100 
102  TokenTableEntry("ru", LFT_REQUEST_URI), /* doesn't include the query-string */
104  /* TokenTableEntry( "rq", LFT_REQUEST_QUERY ), * / / * the query-string, INCLUDING the leading ? */
107 
114  /*TokenTableEntry("<rq", LFT_SERVER_REQ_QUERY),*/
116 
119  /*TokenTableEntry( ">sb", LFT_REQUEST_SIZE_BODY ), */
120  /*TokenTableEntry( ">sB", LFT_REQUEST_SIZE_BODY_NO_TE ), */
121 
122  TokenTableEntry("<st", LFT_ADAPTED_REPLY_SIZE_TOTAL), // XXX: adapted should be code: <sta
125  TokenTableEntry("<sh", LFT_ADAPTED_REPLY_SIZE_HEADERS ), // XXX: adapted should be code: <sha
126  /*TokenTableEntry( "<sb", LFT_REPLY_SIZE_BODY ), */
127  /*TokenTableEntry( "<sB", LFT_REPLY_SIZE_BODY_NO_TE ), */
128 
129  TokenTableEntry("st", LFT_CLIENT_IO_SIZE_TOTAL), // XXX: total from client should be stC ??
130  /*TokenTableEntry("stP", LFT_SERVER_IO_SIZE_TOTAL),*/
131 
132  TokenTableEntry("et", LFT_TAG),
135 
136  TokenTableEntry(NULL, LFT_NONE) /* this must be last */
137 };
138 
146  TokenTableEntry(">handshake", LFT_CLIENT_HANDSHAKE),
147  TokenTableEntry("err_code", LFT_SQUID_ERROR ),
148  TokenTableEntry("err_detail", LFT_SQUID_ERROR_DETAIL ),
149  TokenTableEntry("note", LFT_NOTE ),
150  TokenTableEntry("credentials", LFT_CREDENTIALS),
151  /*
152  * Legacy external_acl_type format tokens
153  */
157  TokenTableEntry("EXT_LOG", LFT_EXT_LOG),
158  TokenTableEntry("EXT_TAG", LFT_TAG),
159  TokenTableEntry("EXT_USER", LFT_USER_EXTERNAL),
170  TokenTableEntry("SRCPORT", LFT_CLIENT_PORT),
171  TokenTableEntry("SRC", LFT_CLIENT_IP_ADDRESS), // keep after longer SRC* tokens
172  TokenTableEntry("TAG", LFT_TAG),
174 #if USE_OPENSSL
177 #endif
178  TokenTableEntry(NULL, LFT_NONE) /* this must be last */
179 };
180 
183 };
184 
185 #if USE_ADAPTATION
190  TokenTableEntry(NULL, LFT_NONE) /* this must be last */
191 };
192 #endif
193 
194 #if ICAP_CLIENT
195 static TokenTableEntry TokenTableIcap[] = {
198  TokenTableEntry("<last_h", LFT_ADAPTATION_LAST_HEADER), // deprecated
199 
201  TokenTableEntry("<service_name", LFT_ICAP_SERV_NAME),
207 
210 
215 
216  TokenTableEntry(NULL, LFT_NONE) /* this must be last */
217 };
218 #endif
219 
220 #if USE_OPENSSL
221 // TLS/SSL (tls:: or ssl::) tokens
223  TokenTableEntry("bump_mode", LFT_SSL_BUMP_MODE),
224  TokenTableEntry(">cert_subject", LFT_SSL_USER_CERT_SUBJECT),
225  TokenTableEntry(">cert_issuer", LFT_SSL_USER_CERT_ISSUER),
231  TokenTableEntry(">negotiated_version", LFT_TLS_CLIENT_NEGOTIATED_VERSION),
232  TokenTableEntry("<negotiated_version", LFT_TLS_SERVER_NEGOTIATED_VERSION),
233  TokenTableEntry(">negotiated_cipher", LFT_TLS_CLIENT_NEGOTIATED_CIPHER),
234  TokenTableEntry("<negotiated_cipher", LFT_TLS_SERVER_NEGOTIATED_CIPHER),
235  TokenTableEntry(">received_hello_version", LFT_TLS_CLIENT_RECEIVED_HELLO_VERSION),
236  TokenTableEntry("<received_hello_version", LFT_TLS_SERVER_RECEIVED_HELLO_VERSION),
237  TokenTableEntry(">received_supported_version", LFT_TLS_CLIENT_SUPPORTED_VERSION),
238  TokenTableEntry("<received_supported_version", LFT_TLS_SERVER_SUPPORTED_VERSION),
240 };
241 #endif
242 } // namespace Format
243 
245 void
247 {
248  // TODO standard log tokens
249 
250 #if USE_ADAPTATION
252 #endif
253 #if ICAP_CLIENT
255 #endif
256 #if USE_OPENSSL
259 #endif
261 }
262 
265 const char *
267 {
268  for (TokenTableEntry const *lte = table; lte->configTag != NULL; ++lte) {
269  debugs(46, 8, HERE << "compare tokens '" << lte->configTag << "' with '" << cur << "'");
270  if (strncmp(lte->configTag, cur, strlen(lte->configTag)) == 0) {
271  type = lte->tokenType;
272  label = lte->configTag;
273  debugs(46, 7, HERE << "Found token '" << label << "'");
274  return cur + strlen(lte->configTag);
275  }
276  }
277  return cur;
278 }
279 
280 /* parses a single token. Returns the token length in characters,
281  * and fills in the lt item with the token information.
282  * def is for sure null-terminated
283  */
284 int
285 Format::Token::parse(const char *def, Quoting *quoting)
286 {
287  const char *cur = def;
288 
289  int l;
290 
291  l = strcspn(cur, "%");
292 
293  if (l > 0) {
294  char *cp;
295  /* it's a string for sure, until \0 or the next % */
296  cp = (char *)xmalloc(l + 1);
297  xstrncpy(cp, cur, l + 1);
298  type = LFT_STRING;
299  data.string = cp;
300 
301  while (l > 0) {
302  switch (*cur) {
303 
304  case '"':
305 
306  if (*quoting == LOG_QUOTE_NONE)
307  *quoting = LOG_QUOTE_QUOTES;
308  else if (*quoting == LOG_QUOTE_QUOTES)
309  *quoting = LOG_QUOTE_NONE;
310 
311  break;
312 
313  case '[':
314  if (*quoting == LOG_QUOTE_NONE)
315  *quoting = LOG_QUOTE_MIMEBLOB;
316 
317  break;
318 
319  case ']':
320  if (*quoting == LOG_QUOTE_MIMEBLOB)
321  *quoting = LOG_QUOTE_NONE;
322 
323  break;
324  }
325 
326  ++cur;
327  --l;
328  }
329 
330  } else if (*cur) {
331 
332  ++cur;
333 
334  // select quoting style for his particular token
335  switch (*cur) {
336 
337  case '"':
339  ++cur;
340  break;
341 
342  case '\'':
344  ++cur;
345  break;
346 
347  case '[':
349  ++cur;
350  break;
351 
352  case '#':
354  ++cur;
355  break;
356 
357  case '/':
359  ++cur;
360  break;
361 
362  default:
363  quote = *quoting;
364  break;
365  }
366 
367  if (*cur == '-') {
368  left = true;
369  ++cur;
370  }
371 
372  if (*cur == '0') {
373  zero = true;
374  ++cur;
375  }
376 
377  char *endp;
378  if (xisdigit(*cur)) {
379  widthMin = strtol(cur, &endp, 10);
380  cur = endp;
381  }
382 
383  if (*cur == '.' && xisdigit(*(++cur))) {
384  widthMax = strtol(cur, &endp, 10);
385  cur = endp;
386  }
387 
388  // when {arg} field is before the token (old logformat syntax)
389  if (*cur == '{') {
390  char *cp;
391  ++cur;
392  l = strcspn(cur, "}");
393  cp = (char *)xmalloc(l + 1);
394  xstrncpy(cp, cur, l + 1);
395  data.string = cp;
396  cur += l;
397 
398  if (*cur == '}')
399  ++cur;
400  }
401 
402  type = LFT_NONE;
403 
404  // Scan each registered token namespace
405  debugs(46, 9, "check for token in " << TheConfig.tokens.size() << " namespaces.");
406  for (const auto &itr : TheConfig.tokens) {
407  debugs(46, 7, "check for possible " << itr.prefix << ":: token");
408  const size_t len = itr.prefix.length();
409  if (itr.prefix.cmp(cur, len) == 0 && cur[len] == ':' && cur[len+1] == ':') {
410  debugs(46, 5, "check for " << itr.prefix << ":: token in '" << cur << "'");
411  const char *old = cur;
412  cur = scanForToken(itr.tokenSet, cur+len+2);
413  if (old != cur) // found
414  break;
415  else // reset to start of namespace
416  cur = cur - len - 2;
417  }
418  }
419 
420  if (type == LFT_NONE) {
421  // For upward compatibility, assume "http::" prefix as default prefix
422  // for all log access formatting codes, except those starting with a
423  // "%" or a known namespace. (ie "icap::", "adapt::")
424  if (strncmp(cur,"http::", 6) == 0 && *(cur+6) != '%' )
425  cur += 6;
426 
427  // NP: scan the sets of tokens in decreasing size to guarantee no
428  // mistakes made with overlapping names. (Bug 3310)
429 
430  // Scan for various long tokens
431  debugs(46, 5, HERE << "scan for possible Misc token");
432  cur = scanForToken(TokenTableMisc, cur);
433  // scan for 2-char tokens
434  if (type == LFT_NONE) {
435  debugs(46, 5, HERE << "scan for possible 2C token");
436  cur = scanForToken(TokenTable2C, cur);
437  }
438  // finally scan for 1-char tokens.
439  if (type == LFT_NONE) {
440  debugs(46, 5, HERE << "scan for possible 1C token");
441  cur = scanForToken(TokenTable1C, cur);
442  }
443  }
444 
445  if (type == LFT_NONE)
446  throw TexcHere(ToSBuf("Unsupported %code: '", def, "'"));
447 
448  // when {arg} field is after the token (old external_acl_type token syntax)
449  // but accept only if there was none before the token
450  if (*cur == '{' && !data.string) {
451  char *cp;
452  ++cur;
453  l = strcspn(cur, "}");
454  cp = (char *)xmalloc(l + 1);
455  xstrncpy(cp, cur, l + 1);
456  data.string = cp;
457  cur += l;
458 
459  if (*cur == '}')
460  ++cur;
461  }
462 
463  if (*cur == ' ') {
464  space = true;
465  ++cur;
466  }
467  }
468 
469  switch (type) {
470 
471 #if USE_ADAPTATION
473 #endif
474 
475 #if ICAP_CLIENT
476  case LFT_ICAP_REQ_HEADER:
477 
478  case LFT_ICAP_REP_HEADER:
479 #endif
480 
482 
483  case LFT_REQUEST_HEADER:
484 
485  case LFT_REPLY_HEADER:
486 
487  case LFT_NOTE:
488 
490 
491  if (data.string) {
492  char *header = data.string;
493  const auto initialType = type;
494 
495  const auto pseudoHeader = header[0] == ':';
496  char *cp = strchr(pseudoHeader ? header+1 : header, ':');
497 
498  if (cp) {
499  *cp = '\0';
500  ++cp;
501 
502  if (*cp == ',' || *cp == ';' || *cp == ':') {
503  data.header.separator = *cp;
504  ++cp;
505  } else {
506  data.header.separator = ',';
507  }
508 
509  data.header.element = cp;
510 
511  switch (type) {
512  case LFT_REQUEST_HEADER:
514  break;
515 
518  break;
519 
520  case LFT_REPLY_HEADER:
522  break;
523 #if USE_ADAPTATION
526  break;
527 #endif
528 #if ICAP_CLIENT
529  case LFT_ICAP_REQ_HEADER:
531  break;
532  case LFT_ICAP_REP_HEADER:
534  break;
535 #endif
538  break;
539  default:
540  break;
541  }
542  }
543 
544  if (!*header)
545  throw TexcHere(ToSBuf("Can't parse configuration token: '", def, "': missing header name"));
546 
547  if (initialType == LFT_PROXY_PROTOCOL_RECEIVED_HEADER)
548  data.headerId = ProxyProtocol::FieldNameToFieldType(SBuf(header));
549  else if (pseudoHeader)
550  throw TexcHere(ToSBuf("Pseudo headers are not supported in this context; got: '", def, "'"));
551 
552  data.header.header = header;
553  } else {
554  switch (type) {
555  case LFT_REQUEST_HEADER:
557  break;
558 
561  break;
562 
563  case LFT_REPLY_HEADER:
565  break;
566 #if USE_ADAPTATION
569  break;
570 #endif
571 #if ICAP_CLIENT
572  case LFT_ICAP_REQ_HEADER:
574  break;
575  case LFT_ICAP_REP_HEADER:
577  break;
578 #endif
581  break;
582  default:
583  break;
584  }
586  }
587 
588  break;
589 
590  case LFT_CLIENT_FQDN:
591  Config.onoff.log_fqdn = 1;
592  break;
593 
597  case LFT_DNS_WAIT_TIME:
598 #if ICAP_CLIENT
600  case LFT_ICAP_IO_TIME:
601  case LFT_ICAP_TOTAL_TIME:
602 #endif
603  case LFT_TIME_START:
604  case LFT_TIME_SUBSECOND:
605  divisor = 1000;
606 
607  if (widthMax > 0) {
608  divisor = 1000000;
609 
610  for (int i = widthMax; i > 0; --i)
611  divisor /= 10;
612 
613  if (!divisor)
614  divisor = 1;
615  }
616  break;
617 
619  debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"Hs\" formatting code is deprecated. Use the \">Hs\" instead.");
621  break;
622 
624  debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"oa\" formatting code is deprecated. Use the \"<la\" instead.");
626  break;
627 
629  debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rp\" formatting code is deprecated. Use the \">rp\" instead.");
631  break;
632 
634  debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \">v\" formatting code is deprecated. Use the \">rv\" instead.");
636  break;
637 
638 #if !USE_SQUID_EUI
639  case LFT_CLIENT_EUI:
640  debugs(46, DBG_CRITICAL, "WARNING: The \">eui\" formatting code requires EUI features which are disabled in this Squid.");
641  break;
642 #endif
643 
644 #if USE_OPENSSL
649  break;
650 #endif
651 
653  debugs(46, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: The \"rG\" formatting code is deprecated. Use \"note{urlgroup}\" instead.");
654  type = LFT_NOTE;
655  data.header.header = xstrdup("urlgroup");
656  break;
657 
658  default:
659  break;
660  }
661 
662  return (cur - def);
663 }
664 
666  label(NULL),
667  widthMin(-1),
668  widthMax(-1),
670  left(false),
671  space(false),
672  zero(false),
673  divisor(1),
674  next(NULL)
675 {
676  data.string = NULL;
677  data.header.header = NULL;
678  data.header.element = NULL;
679  data.header.separator = ',';
681 }
682 
684 {
685  label = NULL; // drop reference to global static.
686  safe_free(data.string);
687  while (next) {
688  Token *tokens = next;
689  next = next->next;
690  tokens->next = NULL;
691  delete tokens;
692  }
693 }
694 
const char * scanForToken(TokenTableEntry const table[], const char *cur)
Definition: Token.cc:266
int type
Definition: errorpage.cc:152
Definition: SBuf.h:86
Token * next
Definition: Token.h:71
static TokenTableEntry TokenTable2C[]
2-char tokens
Definition: Token.cc:48
int i
Definition: membanger.c:49
#define xstrdup
static TokenTableEntry TokenTable1C[]
1-char tokens.
Definition: Token.cc:25
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:124
#define safe_free(x)
Definition: xalloc.h:73
int parse(const char *def, enum Quoting *quote)
Definition: Token.cc:285
Two::FieldType FieldNameToFieldType(const SBuf &nameOrId)
Definition: Elements.cc:102
One entry in a table of format tokens.
#define DBG_CRITICAL
Definition: Debug.h:45
std::list< TokenNamespace > tokens
list of token namespaces registered
Definition: Config.h:64
#define DBG_PARSE_NOTE(x)
Definition: Debug.h:50
static TokenTableEntry TokenTableIcap[]
ICAP (icap::) tokens.
Definition: Token.cc:196
Quoting
Quoting style for a format output.
Definition: ByteCode.h:256
static TokenTableEntry TokenTableAdapt[]
Definition: Token.cc:186
void const char HLPCB void * data
Definition: stub_helper.cc:16
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
#define DBG_IMPORTANT
Definition: Debug.h:46
static TokenTableEntry TokenTableProxyProtocol[]
Definition: Token.cc:181
static TokenTableEntry TokenTableMisc[]
Miscellaneous >2 byte tokens.
Definition: Token.cc:140
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
FmtConfig TheConfig
Definition: Config.cc:16
void registerTokens(const SBuf &nsName, TokenTableEntry const *tokenArray)
Definition: Config.cc:48
int cur
Definition: ModDevPoll.cc:76
static TokenTableEntry TokenTableSsl[]
Definition: Token.cc:222
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
int log_mime_hdrs
Definition: SquidConfig.h:286
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:153
struct SquidConfig::@112 onoff
const char * label
Definition: Token.h:51
#define xmalloc
static void Init()
Initialize the format token registrations.
Definition: Token.cc:246
#define xisdigit(x)
Definition: xis.h:20
const char * quote
Definition: html_quote.c:21
#define TexcHere(msg)
legacy convenience macro; it is not difficult to type Here() now
Definition: TextException.h:55
struct tok tokens[]
Definition: parse.c:168
struct Format::Token::@69 data
class SquidConfig Config
Definition: SquidConfig.cc:12
#define NULL
Definition: types.h:166
bool logTlsServerHelloDetails
Definition: SquidConfig.h:344
#define false
Definition: GnuRegex.c:233

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors