2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
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.
10 #include "format/Config.h"
11 #include "format/Token.h"
12 #include "format/TokenTableEntry.h"
14 #include "SquidConfig.h"
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
23 static TokenTableEntry TokenTable1C
[] = {
25 {">a", LFT_CLIENT_IP_ADDRESS
},
26 {">p", LFT_CLIENT_PORT
},
27 {">A", LFT_CLIENT_FQDN
},
29 {"<a", LFT_SERVER_IP_ADDRESS
},
30 {"<p", LFT_SERVER_PORT
},
31 {"<A", LFT_SERVER_FQDN_OR_PEER_NAME
},
33 {">h", LFT_REQUEST_HEADER
},
34 {">h", LFT_REQUEST_ALL_HEADERS
},
35 {"<h", LFT_REPLY_HEADER
},
36 {"<h", LFT_REPLY_ALL_HEADERS
},
38 {">v", LFT_REQUEST_VERSION_OLD_2X
},
42 {NULL
, LFT_NONE
} /* this must be last */
46 static TokenTableEntry TokenTable2C
[] = {
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 }, */
54 {"<la", LFT_SERVER_LOCAL_IP
},
55 {"oa", LFT_SERVER_LOCAL_IP_OLD_27
},
56 {"<lp", LFT_SERVER_LOCAL_PORT
},
58 {"ts", LFT_TIME_SECONDS_SINCE_EPOCH
},
59 {"tu", LFT_TIME_SUBSECOND
},
60 {"tl", LFT_TIME_LOCALTIME
},
62 {"tS", LFT_TIME_START
},
63 {"tr", LFT_TIME_TO_HANDLE_REQUEST
},
65 {"<pt", LFT_PEER_RESPONSE_TIME
},
66 {"<tt", LFT_TOTAL_SERVER_SIDE_RESPONSE_TIME
},
67 {"dt", LFT_DNS_WAIT_TIME
},
69 {">ha", LFT_ADAPTED_REQUEST_HEADER
},
70 {">ha", LFT_ADAPTED_REQUEST_ALL_HEADERS
},
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
},
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
},
85 {"Ss", LFT_SQUID_STATUS
},
86 {"Sh", LFT_SQUID_HIERARCHY
},
88 {"mt", LFT_MIME_TYPE
},
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
},
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
},
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
},
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 }, */
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 }, */
127 {"st", LFT_CLIENT_IO_SIZE_TOTAL
}, // XXX: total from client should be stC ??
128 /*{"stP", LFT_SERVER_IO_SIZE_TOTAL},*/
132 {"sn", LFT_SEQUENCE_NUMBER
},
134 {NULL
, LFT_NONE
} /* this must be last */
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
},
147 {"credentials", LFT_CREDENTIALS
},
148 {NULL
, LFT_NONE
} /* this must be last */
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 */
161 /// ICAP (icap::) tokens
162 static TokenTableEntry TokenTableIcap
[] = {
163 {"tt", LFT_ICAP_TOTAL_TIME
},
164 {"<last_h", LFT_ADAPTATION_LAST_HEADER
}, // deprecated
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
},
174 {">h", LFT_ICAP_REQ_HEADER
},
175 {"<h", LFT_ICAP_REP_HEADER
},
177 {"tr", LFT_ICAP_TR_RESPONSE_TIME
},
178 {"tio", LFT_ICAP_IO_TIME
},
179 {"to", LFT_ICAP_OUTCOME
},
180 {"Hs", LFT_ICAP_STATUS_CODE
},
182 {NULL
, LFT_NONE
} /* this must be last */
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}, */
198 } // namespace Format
200 /// Register all components custom format tokens
202 Format::Token::Init()
204 // TODO standard log tokens
205 // TODO external ACL fmt tokens
208 TheConfig
.registerTokens(String("adapt"),::Format::TokenTableAdapt
);
211 TheConfig
.registerTokens(String("icap"),::Format::TokenTableIcap
);
214 TheConfig
.registerTokens(String("ssl"),::Format::TokenTableSsl
);
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
221 Format::Token::scanForToken(TokenTableEntry
const table
[], const char *cur
)
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
);
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
240 Format::Token::parse(const char *def
, Quoting
*quoting
)
242 const char *cur
= def
;
246 l
= strcspn(cur
, "%");
250 /* it's a string for sure, until \0 or the next % */
251 cp
= (char *)xmalloc(l
+ 1);
252 xstrncpy(cp
, cur
, l
+ 1);
261 if (*quoting
== LOG_QUOTE_NONE
)
262 *quoting
= LOG_QUOTE_QUOTES
;
263 else if (*quoting
== LOG_QUOTE_QUOTES
)
264 *quoting
= LOG_QUOTE_NONE
;
269 if (*quoting
== LOG_QUOTE_NONE
)
270 *quoting
= LOG_QUOTE_MIMEBLOB
;
275 if (*quoting
== LOG_QUOTE_MIMEBLOB
)
276 *quoting
= LOG_QUOTE_NONE
;
289 // select quoting style for his particular token
293 quote
= LOG_QUOTE_QUOTES
;
298 quote
= LOG_QUOTE_RAW
;
303 quote
= LOG_QUOTE_MIMEBLOB
;
308 quote
= LOG_QUOTE_URL
;
328 if (xisdigit(*cur
)) {
329 widthMin
= strtol(cur
, &endp
, 10);
333 if (*cur
== '.' && xisdigit(*(++cur
))) {
334 widthMax
= strtol(cur
, &endp
, 10);
341 l
= strcspn(cur
, "}");
342 cp
= (char *)xmalloc(l
+ 1);
343 xstrncpy(cp
, cur
, l
+ 1);
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
364 else // reset to start of namespace
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) != '%' )
376 // NP: scan the sets of tokens in decreasing size to guarantee no
377 // mistakes made with overlapping names. (Bug 3310)
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
);
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
);
394 if (type
== LFT_NONE
) {
395 fatalf("Can't parse configuration token: '%s'\n", def
);
407 case LFT_ADAPTATION_LAST_HEADER
:
411 case LFT_ICAP_REQ_HEADER
:
413 case LFT_ICAP_REP_HEADER
:
416 case LFT_ADAPTED_REQUEST_HEADER
:
418 case LFT_REQUEST_HEADER
:
420 case LFT_REPLY_HEADER
:
425 char *header
= data
.string
;
426 char *cp
= strchr(header
, ':');
432 if (*cp
== ',' || *cp
== ';' || *cp
== ':') {
433 data
.header
.separator
= *cp
;
436 data
.header
.separator
= ',';
439 data
.header
.element
= cp
;
442 case LFT_REQUEST_HEADER
:
443 type
= LFT_REQUEST_HEADER_ELEM
;
446 case LFT_ADAPTED_REQUEST_HEADER
:
447 type
= LFT_ADAPTED_REQUEST_HEADER_ELEM
;
450 case LFT_REPLY_HEADER
:
451 type
= LFT_REPLY_HEADER_ELEM
;
454 case LFT_ADAPTATION_LAST_HEADER
:
455 type
= LFT_ADAPTATION_LAST_HEADER_ELEM
;
459 case LFT_ICAP_REQ_HEADER
:
460 type
= LFT_ICAP_REQ_HEADER_ELEM
;
462 case LFT_ICAP_REP_HEADER
:
463 type
= LFT_ICAP_REP_HEADER_ELEM
;
471 data
.header
.header
= header
;
474 case LFT_REQUEST_HEADER
:
475 type
= LFT_REQUEST_ALL_HEADERS
;
478 case LFT_ADAPTED_REQUEST_HEADER
:
479 type
= LFT_ADAPTED_REQUEST_ALL_HEADERS
;
482 case LFT_REPLY_HEADER
:
483 type
= LFT_REPLY_ALL_HEADERS
;
486 case LFT_ADAPTATION_LAST_HEADER
:
487 type
= LFT_ADAPTATION_LAST_ALL_HEADERS
;
491 case LFT_ICAP_REQ_HEADER
:
492 type
= LFT_ICAP_REQ_ALL_HEADERS
;
494 case LFT_ICAP_REP_HEADER
:
495 type
= LFT_ICAP_REP_ALL_HEADERS
;
501 Config
.onoff
.log_mime_hdrs
= 1;
506 case LFT_CLIENT_FQDN
:
507 Config
.onoff
.log_fqdn
= 1;
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
:
515 case LFT_ICAP_TR_RESPONSE_TIME
:
516 case LFT_ICAP_IO_TIME
:
517 case LFT_ICAP_TOTAL_TIME
:
520 case LFT_TIME_SUBSECOND
:
526 for (int i
= widthMax
; i
> 0; --i
)
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
;
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
;
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
;
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
;
556 debugs(46, DBG_CRITICAL
, "WARNING: The \">eui\" formatting code requires EUI features which are disabled in this Squid.");
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.");
563 data
.header
.header
= xstrdup("urlgroup");
573 Format::Token::Token() : type(LFT_NONE
),
577 quote(LOG_QUOTE_NONE
),
585 data
.header
.header
= NULL
;
586 data
.header
.element
= NULL
;
587 data
.header
.separator
= ',';
590 Format::Token::~Token()
592 label
= NULL
; // drop reference to global static.
593 safe_free(data
.string
);
595 Token
*tokens
= next
;