]> git.ipfire.org Git - thirdparty/squid.git/blame - src/access_log.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / access_log.cc
CommitLineData
f892c2bf 1/*
262a0e14 2 * $Id$
f892c2bf 3 *
4 * DEBUG: section 46 Access Log
5 * AUTHOR: Duane Wessels
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 8 * ----------------------------------------------------------
f892c2bf 9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
f892c2bf 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
26ac0430 23 *
f892c2bf 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
26ac0430 28 *
f892c2bf 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
f892c2bf 33 */
34
35
36#include "squid.h"
450e0c10 37#include "AccessLogEntry.h"
f892c2bf 38
7684c4b1 39// Store.h Required by configuration directives parsing/dumping only
40#include "Store.h"
41
42#include "ACLChecklist.h"
43
44#include "HttpReply.h"
45#include "HttpRequest.h"
0eb49b6d 46#include "MemBuf.h"
985c86bc 47#include "SquidTime.h"
780745e5 48#include "CacheManager.h"
7684c4b1 49
50static void accessLogSquid(AccessLogEntry * al, Logfile * logfile);
51static void accessLogCommon(AccessLogEntry * al, Logfile * logfile);
52static void accessLogCustom(AccessLogEntry * al, customlog * log);
c3609322 53#if HEADERS_LOG
54static Logfile *headerslog = NULL;
55#endif
a7c05555 56
e66d7923 57#if MULTICAST_MISS_STREAM
58static int mcast_miss_fd = -1;
62e76326 59
e66d7923 60static struct sockaddr_in mcast_miss_to;
9bea1d5b 61static void mcast_encode(unsigned int *, size_t, const unsigned int *);
e66d7923 62#endif
63
26ac0430
AJ
64const char *log_tags[] = {
65 "NONE",
66 "TCP_HIT",
67 "TCP_MISS",
68 "TCP_REFRESH_UNMODIFIED",
69 "TCP_REFRESH_FAIL",
70 "TCP_REFRESH_MODIFIED",
71 "TCP_CLIENT_REFRESH_MISS",
72 "TCP_IMS_HIT",
73 "TCP_SWAPFAIL_MISS",
74 "TCP_NEGATIVE_HIT",
75 "TCP_MEM_HIT",
76 "TCP_DENIED",
77 "TCP_DENIED_REPLY",
78 "TCP_OFFLINE_HIT",
efd900cb 79#if LOG_TCP_REDIRECTS
26ac0430 80 "TCP_REDIRECT",
efd900cb 81#endif
26ac0430
AJ
82 "UDP_HIT",
83 "UDP_MISS",
84 "UDP_DENIED",
85 "UDP_INVALID",
86 "UDP_MISS_NOFETCH",
87 "ICP_QUERY",
88 "LOG_TYPE_MAX"
89};
7a2f978b 90
d21f1c54 91#if FORW_VIA_DB
62e76326 92
26ac0430 93typedef struct {
6c40d272 94 hash_link hash;
1afe05c5 95 int n;
2fadd50d 96} fvdb_entry;
d21f1c54 97static hash_table *via_table = NULL;
98static hash_table *forw_table = NULL;
780745e5 99static void fvdbInit();
9bea1d5b 100static void fvdbDumpTable(StoreEntry * e, hash_table * hash);
101static void fvdbCount(hash_table * hash, const char *key);
d21f1c54 102static OBJH fvdbDumpVia;
103static OBJH fvdbDumpForw;
104static FREE fvdbFreeEntry;
9bea1d5b 105static void fvdbClear(void);
d62a240c 106static void fvdbRegisterWithCacheManager();
d21f1c54 107#endif
f892c2bf 108
109static int LogfileStatus = LOG_DISABLE;
f892c2bf 110#define LOG_BUF_SZ (MAX_URL<<2)
f892c2bf 111
112static const char c2x[] =
62e76326 113 "000102030405060708090a0b0c0d0e0f"
114 "101112131415161718191a1b1c1d1e1f"
115 "202122232425262728292a2b2c2d2e2f"
116 "303132333435363738393a3b3c3d3e3f"
117 "404142434445464748494a4b4c4d4e4f"
118 "505152535455565758595a5b5c5d5e5f"
119 "606162636465666768696a6b6c6d6e6f"
120 "707172737475767778797a7b7c7d7e7f"
121 "808182838485868788898a8b8c8d8e8f"
122 "909192939495969798999a9b9c9d9e9f"
123 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
124 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
125 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
126 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
127 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
128 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
f892c2bf 129
130/* log_quote -- URL-style encoding on MIME headers. */
131
592da4ec 132char *
9bea1d5b 133log_quote(const char *header)
f892c2bf 134{
9bea1d5b 135 int c;
136 int i;
137 char *buf;
138 char *buf_cursor;
62e76326 139
9bea1d5b 140 if (header == NULL) {
62e76326 141 buf = static_cast<char *>(xcalloc(1, 1));
142 *buf = '\0';
143 return buf;
f892c2bf 144 }
62e76326 145
e6ccf245 146 buf = static_cast<char *>(xcalloc(1, (strlen(header) * 3) + 1));
9bea1d5b 147 buf_cursor = buf;
148 /*
26ac0430 149 * We escape: \x00-\x1F"#%;<>?{}|\\\\^~`\[\]\x7F-\xFF
9bea1d5b 150 * which is the default escape list for the CPAN Perl5 URI module
151 * modulo the inclusion of space (x40) to make the raw logs a bit
152 * more readable.
153 */
62e76326 154
9bea1d5b 155 while ((c = *(const unsigned char *) header++) != '\0') {
7e3ce7b9 156#if !OLD_LOG_MIME
62e76326 157
158 if (c == '\r') {
159 *buf_cursor++ = '\\';
160 *buf_cursor++ = 'r';
161 } else if (c == '\n') {
162 *buf_cursor++ = '\\';
163 *buf_cursor++ = 'n';
164 } else
7e3ce7b9 165#endif
62e76326 166 if (c <= 0x1F
167 || c >= 0x7F
7c013081 168 || c == '%'
7e3ce7b9 169#if OLD_LOG_MIME
62e76326 170 || c == '"'
171 || c == '#'
62e76326 172 || c == ';'
173 || c == '<'
174 || c == '>'
175 || c == '?'
176 || c == '{'
177 || c == '}'
178 || c == '|'
179 || c == '\\'
180 || c == '^'
181 || c == '~'
182 || c == '`'
7e3ce7b9 183#endif
62e76326 184 || c == '['
185 || c == ']') {
186 *buf_cursor++ = '%';
187 i = c * 2;
188 *buf_cursor++ = c2x[i];
189 *buf_cursor++ = c2x[i + 1];
7e3ce7b9 190#if !OLD_LOG_MIME
62e76326 191
192 } else if (c == '\\') {
193 *buf_cursor++ = '\\';
194 *buf_cursor++ = '\\';
7e3ce7b9 195#endif
62e76326 196
197 } else {
198 *buf_cursor++ = (char) c;
199 }
f892c2bf 200 }
62e76326 201
9bea1d5b 202 *buf_cursor = '\0';
203 return buf;
f892c2bf 204}
205
2d72d4fd 206static char *
9bea1d5b 207username_quote(const char *header)
94439e4e 208/* copy of log_quote. Bugs there will be found here */
209{
9bea1d5b 210 int c;
211 int i;
212 char *buf;
213 char *buf_cursor;
62e76326 214
9bea1d5b 215 if (header == NULL) {
62e76326 216 buf = static_cast<char *>(xcalloc(1, 1));
217 *buf = '\0';
218 return buf;
94439e4e 219 }
62e76326 220
e6ccf245 221 buf = static_cast<char *>(xcalloc(1, (strlen(header) * 3) + 1));
9bea1d5b 222 buf_cursor = buf;
223 /*
224 * We escape: space \x00-\x1F and space (0x40) and \x7F-\xFF
26ac0430 225 * to prevent garbage in the logs. CR and LF are also there just in case.
9bea1d5b 226 */
62e76326 227
9bea1d5b 228 while ((c = *(const unsigned char *) header++) != '\0') {
62e76326 229 if (c == '\r') {
230 *buf_cursor++ = '\\';
231 *buf_cursor++ = 'r';
232 } else if (c == '\n') {
233 *buf_cursor++ = '\\';
234 *buf_cursor++ = 'n';
235 } else if (c <= 0x1F
236 || c >= 0x7F
7c013081 237 || c == '%'
62e76326 238 || c == ' ') {
239 *buf_cursor++ = '%';
240 i = c * 2;
241 *buf_cursor++ = c2x[i];
242 *buf_cursor++ = c2x[i + 1];
243 } else {
244 *buf_cursor++ = (char) c;
245 }
94439e4e 246 }
62e76326 247
9bea1d5b 248 *buf_cursor = '\0';
249 return buf;
94439e4e 250}
251
2d72d4fd 252static char *
9bea1d5b 253accessLogFormatName(const char *name)
94439e4e 254{
9bea1d5b 255 if (NULL == name)
62e76326 256 return NULL;
257
90c858f7 258 if (name[0] == '\0')
259 return NULL;
260
9bea1d5b 261 return username_quote(name);
94439e4e 262}
263
7684c4b1 264static char *
265log_quoted_string(const char *str)
266{
267 char *out = (char *)xmalloc(strlen(str) * 2 + 1);
268 char *p = out;
269
270 while (*str) {
271 int l = strcspn(str, "\"\\\r\n\t");
272 memcpy(p, str, l);
273 str += l;
274 p += l;
275
276 switch (*str) {
277
278 case '\0':
279 break;
280
281 case '\r':
282 *p++ = '\\';
283 *p++ = 'r';
284 str++;
285 break;
286
287 case '\n':
288 *p++ = '\\';
289 *p++ = 'n';
290 str++;
291 break;
292
293 case '\t':
294 *p++ = '\\';
295 *p++ = 't';
296 str++;
297 break;
298
299 default:
300 *p++ = '\\';
301 *p++ = *str;
302 str++;
303 break;
304 }
305 }
306
307 *p++ = '\0';
308 return out;
309}
310
311/*
312 * Bytecodes for the configureable logformat stuff
313 */
314typedef enum {
315 LFT_NONE, /* dummy */
316 LFT_STRING,
317
318 LFT_CLIENT_IP_ADDRESS,
319 LFT_CLIENT_FQDN,
44eb213b 320 LFT_CLIENT_PORT,
7684c4b1 321
322 /*LFT_SERVER_IP_ADDRESS, */
323 LFT_SERVER_IP_OR_PEER_NAME,
324 /*LFT_SERVER_PORT, */
325
326 LFT_LOCAL_IP,
327 LFT_LOCAL_PORT,
328 /*LFT_LOCAL_NAME, */
329
330 LFT_TIME_SECONDS_SINCE_EPOCH,
331 LFT_TIME_SUBSECOND,
332 LFT_TIME_LOCALTIME,
333 LFT_TIME_GMT,
334 LFT_TIME_TO_HANDLE_REQUEST,
335
336 LFT_REQUEST_HEADER,
337 LFT_REQUEST_HEADER_ELEM,
338 LFT_REQUEST_ALL_HEADERS,
339
340 LFT_REPLY_HEADER,
341 LFT_REPLY_HEADER_ELEM,
342 LFT_REPLY_ALL_HEADERS,
343
344 LFT_USER_NAME,
345 LFT_USER_LOGIN,
346 LFT_USER_IDENT,
347 /*LFT_USER_REALM, */
348 /*LFT_USER_SCHEME, */
4a972fa2 349 LFT_USER_EXTERNAL,
7684c4b1 350
351 LFT_HTTP_CODE,
352 /*LFT_HTTP_STATUS, */
353
354 LFT_SQUID_STATUS,
355 /*LFT_SQUID_ERROR, */
356 LFT_SQUID_HIERARCHY,
357
358 LFT_MIME_TYPE,
359
360 LFT_REQUEST_METHOD,
361 LFT_REQUEST_URI,
fef92cc1 362 LFT_REQUEST_URLPATH,
7684c4b1 363 /*LFT_REQUEST_QUERY, * // * this is not needed. see strip_query_terms */
364 LFT_REQUEST_VERSION,
365
1a86db31 366 LFT_REQUEST_SIZE_TOTAL,
7684c4b1 367 /*LFT_REQUEST_SIZE_LINE, */
368 /*LFT_REQUEST_SIZE_HEADERS, */
369 /*LFT_REQUEST_SIZE_BODY, */
370 /*LFT_REQUEST_SIZE_BODY_NO_TE, */
371
372 LFT_REPLY_SIZE_TOTAL,
0976f8db 373 LFT_REPLY_HIGHOFFSET,
374 LFT_REPLY_OBJECTSIZE,
7684c4b1 375 /*LFT_REPLY_SIZE_LINE, */
376 /*LFT_REPLY_SIZE_HEADERS, */
377 /*LFT_REPLY_SIZE_BODY, */
378 /*LFT_REPLY_SIZE_BODY_NO_TE, */
379
4a972fa2 380 LFT_TAG,
1a86db31 381 LFT_IO_SIZE_TOTAL,
4a972fa2 382 LFT_EXT_LOG,
383
7684c4b1 384 LFT_PERCENT /* special string cases for escaped chars */
385} logformat_bcode_t;
386
387enum log_quote {
388 LOG_QUOTE_NONE = 0,
389 LOG_QUOTE_QUOTES,
390 LOG_QUOTE_BRAKETS,
391 LOG_QUOTE_URL,
392 LOG_QUOTE_RAW
393};
394
e1f7507e
AJ
395/* FIXME: public class so we can pre-define its type. */
396class logformat_token
7684c4b1 397{
e1f7507e 398public:
7684c4b1 399 logformat_bcode_t type;
400 union {
401 char *string;
402
403 struct {
404 char *header;
405 char *element;
406 char separator;
2fadd50d 407 } header;
7684c4b1 408 char *timespec;
409 } data;
410 unsigned char width;
411 unsigned char precision;
d394051a 412 enum log_quote quote;
3d0ac046
HN
413 unsigned int left:1;
414 unsigned int space:1;
415 unsigned int zero:1;
7684c4b1 416 int divisor;
417 logformat_token *next; /* todo: move from linked list to array */
418};
419
26ac0430 420struct logformat_token_table_entry {
7684c4b1 421 const char *config;
422 logformat_bcode_t token_type;
423 int options;
424};
425
26ac0430 426struct logformat_token_table_entry logformat_token_table[] = {
7684c4b1 427
26ac0430 428 {">a", LFT_CLIENT_IP_ADDRESS},
7684c4b1 429
26ac0430
AJ
430 { ">p", LFT_CLIENT_PORT},
431 {">A", LFT_CLIENT_FQDN},
7684c4b1 432
26ac0430
AJ
433 /*{ "<a", LFT_SERVER_IP_ADDRESS }, */
434 /*{ "<p", LFT_SERVER_PORT }, */
435 {"<A", LFT_SERVER_IP_OR_PEER_NAME},
7684c4b1 436
26ac0430
AJ
437 /* {"oa", LFT_OUTGOING_IP}, */
438 /* {"ot", LFT_OUTGOING_TOS}, */
6db223d9 439
26ac0430
AJ
440 {"la", LFT_LOCAL_IP},
441 {"lp", LFT_LOCAL_PORT},
442 /*{ "lA", LFT_LOCAL_NAME }, */
7684c4b1 443
26ac0430
AJ
444 {"ts", LFT_TIME_SECONDS_SINCE_EPOCH},
445 {"tu", LFT_TIME_SUBSECOND},
446 {"tl", LFT_TIME_LOCALTIME},
447 {"tg", LFT_TIME_GMT},
448 {"tr", LFT_TIME_TO_HANDLE_REQUEST},
7684c4b1 449
26ac0430
AJ
450 {">h", LFT_REQUEST_HEADER},
451 {">h", LFT_REQUEST_ALL_HEADERS},
452 {"<h", LFT_REPLY_HEADER},
453 {"<h", LFT_REPLY_ALL_HEADERS},
7684c4b1 454
26ac0430
AJ
455 {"un", LFT_USER_NAME},
456 {"ul", LFT_USER_LOGIN},
457 /*{ "ur", LFT_USER_REALM }, */
458 /*{ "us", LFT_USER_SCHEME }, */
459 {"ui", LFT_USER_IDENT},
460 {"ue", LFT_USER_EXTERNAL},
7684c4b1 461
26ac0430
AJ
462 {"Hs", LFT_HTTP_CODE},
463 /*{ "Ht", LFT_HTTP_STATUS }, */
7684c4b1 464
26ac0430
AJ
465 {"Ss", LFT_SQUID_STATUS},
466 /*{ "Se", LFT_SQUID_ERROR }, */
467 {"Sh", LFT_SQUID_HIERARCHY},
7684c4b1 468
26ac0430 469 {"mt", LFT_MIME_TYPE},
7684c4b1 470
26ac0430
AJ
471 {"rm", LFT_REQUEST_METHOD},
472 {"ru", LFT_REQUEST_URI}, /* doesn't include the query-string */
473 {"rp", LFT_REQUEST_URLPATH}, /* doesn't include the host */
474 /* { "rq", LFT_REQUEST_QUERY }, * / / * the query-string, INCLUDING the leading ? */
475 {">v", LFT_REQUEST_VERSION},
476 {"rv", LFT_REQUEST_VERSION},
7684c4b1 477
26ac0430
AJ
478 { ">st", LFT_REQUEST_SIZE_TOTAL },
479 /*{ ">sl", LFT_REQUEST_SIZE_LINE }, * / / * the request line "GET ... " */
480 /*{ ">sh", LFT_REQUEST_SIZE_HEADERS }, */
481 /*{ ">sb", LFT_REQUEST_SIZE_BODY }, */
482 /*{ ">sB", LFT_REQUEST_SIZE_BODY_NO_TE }, */
7684c4b1 483
26ac0430
AJ
484 {"<st", LFT_REPLY_SIZE_TOTAL},
485 {"<sH", LFT_REPLY_HIGHOFFSET},
486 {"<sS", LFT_REPLY_OBJECTSIZE},
487 /*{ "<sl", LFT_REPLY_SIZE_LINE }, * / / * the reply line (protocol, code, text) */
488 /*{ "<sh", LFT_REPLY_SIZE_HEADERS }, */
489 /*{ "<sb", LFT_REPLY_SIZE_BODY }, */
490 /*{ "<sB", LFT_REPLY_SIZE_BODY_NO_TE }, */
7684c4b1 491
26ac0430
AJ
492 {"et", LFT_TAG},
493 {"st", LFT_IO_SIZE_TOTAL},
494 {"ea", LFT_EXT_LOG},
4a972fa2 495
26ac0430 496 {"%", LFT_PERCENT},
7684c4b1 497
26ac0430
AJ
498 {NULL, LFT_NONE} /* this must be last */
499};
7684c4b1 500
501static void
502accessLogCustom(AccessLogEntry * al, customlog * log)
503{
504 logformat *lf;
505 Logfile *logfile;
506 logformat_token *fmt;
032785bf 507 static MemBuf mb;
7684c4b1 508 char tmp[1024];
30abd221 509 String sb;
7684c4b1 510
2fe7eff9 511 mb.reset();
7684c4b1 512
513 lf = log->logFormat;
514 logfile = log->logfile;
515
516 for (fmt = lf->format; fmt != NULL; fmt = fmt->next) { /* for each token */
517 const char *out = NULL;
518 int quote = 0;
519 long int outint = 0;
520 int doint = 0;
521 int dofree = 0;
47f6e231 522 int64_t outoff = 0;
523 int dooff = 0;
7684c4b1 524
525 switch (fmt->type) {
526
527 case LFT_NONE:
528 out = "";
529 break;
530
531 case LFT_STRING:
532 out = fmt->data.string;
533 break;
534
535 case LFT_CLIENT_IP_ADDRESS:
cc192b50 536 if (!out) {
537 out = al->cache.caddr.NtoA(tmp,1024);
538 }
7684c4b1 539 break;
540
541 case LFT_CLIENT_FQDN:
542 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
cc192b50 543 if (!out) {
544 out = al->cache.caddr.NtoA(tmp,1024);
545 }
7684c4b1 546
547 break;
548
44eb213b 549 case LFT_CLIENT_PORT:
26ac0430
AJ
550 if (al->request) {
551 outint = al->request->client_addr.GetPort();
552 doint = 1;
553 }
554 break;
7684c4b1 555
556 /* case LFT_SERVER_IP_ADDRESS: */
557
558 case LFT_SERVER_IP_OR_PEER_NAME:
559 out = al->hier.host;
560
561 break;
562
563 /* case LFT_SERVER_PORT: */
564
565 case LFT_LOCAL_IP:
cc192b50 566 if (al->request) {
567 out = al->request->my_addr.NtoA(tmp,1024);
568 }
7684c4b1 569
570 break;
571
572 case LFT_LOCAL_PORT:
573 if (al->request) {
cc192b50 574 outint = al->request->my_addr.GetPort();
7684c4b1 575 doint = 1;
576 }
577
578 break;
579
580 case LFT_TIME_SECONDS_SINCE_EPOCH:
26ac0430 581 // some platforms store time in 32-bit, some 64-bit...
00a69875
HN
582 outoff = static_cast<int64_t>(current_time.tv_sec);
583 dooff = 1;
7684c4b1 584 break;
585
586 case LFT_TIME_SUBSECOND:
587 outint = current_time.tv_usec / fmt->divisor;
588 doint = 1;
589 break;
590
591
592 case LFT_TIME_LOCALTIME:
593
594 case LFT_TIME_GMT: {
26ac0430 595 const char *spec;
7684c4b1 596
26ac0430
AJ
597 struct tm *t;
598 spec = fmt->data.timespec;
7684c4b1 599
137d8f08
AJ
600 if (fmt->type == LFT_TIME_LOCALTIME) {
601 if (!spec)
602 spec = "%d/%b/%Y:%H:%M:%S %z";
26ac0430 603 t = localtime(&squid_curtime);
137d8f08
AJ
604 } else {
605 if (!spec)
606 spec = "%d/%b/%Y:%H:%M:%S";
607
26ac0430 608 t = gmtime(&squid_curtime);
137d8f08 609 }
7684c4b1 610
26ac0430 611 strftime(tmp, sizeof(tmp), spec, t);
7684c4b1 612
26ac0430
AJ
613 out = tmp;
614 }
7684c4b1 615
26ac0430 616 break;
7684c4b1 617
618 case LFT_TIME_TO_HANDLE_REQUEST:
619 outint = al->cache.msec;
620 doint = 1;
621 break;
622
623 case LFT_REQUEST_HEADER:
624
625 if (al->request)
a9925b40 626 sb = al->request->header.getByName(fmt->data.header.header);
7684c4b1 627
30abd221 628 out = sb.buf();
7684c4b1 629
630 quote = 1;
631
632 break;
633
634 case LFT_REPLY_HEADER:
635 if (al->reply)
9e8cf50b 636 sb = al->reply->header.getByName(fmt->data.header.header);
7684c4b1 637
30abd221 638 out = sb.buf();
7684c4b1 639
640 quote = 1;
641
642 break;
643
644 case LFT_REQUEST_HEADER_ELEM:
645 if (al->request)
a9925b40 646 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
7684c4b1 647
30abd221 648 out = sb.buf();
7684c4b1 649
650 quote = 1;
651
652 break;
653
654 case LFT_REPLY_HEADER_ELEM:
655 if (al->reply)
9e8cf50b 656 sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
7684c4b1 657
30abd221 658 out = sb.buf();
7684c4b1 659
660 quote = 1;
661
662 break;
663
664 case LFT_REQUEST_ALL_HEADERS:
665 out = al->headers.request;
666
667 quote = 1;
668
669 break;
670
671 case LFT_REPLY_ALL_HEADERS:
672 out = al->headers.reply;
673
674 quote = 1;
675
676 break;
677
678 case LFT_USER_NAME:
4a972fa2 679 out = accessLogFormatName(al->cache.authuser);
680
681 if (!out)
682 out = accessLogFormatName(al->cache.extuser);
683
684#if USE_SSL
685
686 if (!out)
687 out = accessLogFormatName(al->cache.ssluser);
688
689#endif
690
691 if (!out)
692 out = accessLogFormatName(al->cache.rfc931);
7684c4b1 693
694 dofree = 1;
695
696 break;
697
698 case LFT_USER_LOGIN:
699 out = accessLogFormatName(al->cache.authuser);
700
701 dofree = 1;
702
703 break;
704
705 case LFT_USER_IDENT:
706 out = accessLogFormatName(al->cache.rfc931);
707
708 dofree = 1;
709
710 break;
711
4a972fa2 712 case LFT_USER_EXTERNAL:
713 out = accessLogFormatName(al->cache.extuser);
714
715 dofree = 1;
716
717 break;
718
7684c4b1 719 /* case LFT_USER_REALM: */
720 /* case LFT_USER_SCHEME: */
721
722 case LFT_HTTP_CODE:
723 outint = al->http.code;
724
725 doint = 1;
726
727 break;
728
729 /* case LFT_HTTP_STATUS:
730 * out = statusline->text;
731 * quote = 1;
732 * break;
733 */
734
735 case LFT_SQUID_STATUS:
736 out = log_tags[al->cache.code];
737
738 break;
739
740 /* case LFT_SQUID_ERROR: */
741
742 case LFT_SQUID_HIERARCHY:
743 if (al->hier.ping.timedout)
2fe7eff9 744 mb.append("TIMEOUT_", 8);
7684c4b1 745
746 out = hier_strings[al->hier.code];
747
748 break;
749
750 case LFT_MIME_TYPE:
751 out = al->http.content_type;
752
753 break;
754
755 case LFT_REQUEST_METHOD:
756 out = al->_private.method_str;
757
758 break;
759
760 case LFT_REQUEST_URI:
761 out = al->url;
762
763 break;
764
fef92cc1 765 case LFT_REQUEST_URLPATH:
26ac0430
AJ
766 if (al->request) {
767 out = al->request->urlpath.buf();
768 quote = 1;
769 }
fef92cc1 770 break;
771
7684c4b1 772 case LFT_REQUEST_VERSION:
773 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
7684c4b1 774 out = tmp;
1a86db31 775 break;
7684c4b1 776
1a86db31 777 case LFT_REQUEST_SIZE_TOTAL:
af5294a7 778 outoff = al->cache.requestSize;
1a86db31 779 dooff = 1;
7684c4b1 780 break;
781
7684c4b1 782 /*case LFT_REQUEST_SIZE_LINE: */
783 /*case LFT_REQUEST_SIZE_HEADERS: */
784 /*case LFT_REQUEST_SIZE_BODY: */
785 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
786
787 case LFT_REPLY_SIZE_TOTAL:
1a86db31 788 outoff = al->cache.replySize;
47f6e231 789 dooff = 1;
0976f8db 790 break;
791
792 case LFT_REPLY_HIGHOFFSET:
00a69875 793 outoff = al->cache.highOffset;
0976f8db 794
00a69875 795 dooff = 1;
0976f8db 796
797 break;
798
799 case LFT_REPLY_OBJECTSIZE:
00a69875 800 outoff = al->cache.objectSize;
0976f8db 801
00a69875 802 dooff = 1;
7684c4b1 803
804 break;
805
806 /*case LFT_REPLY_SIZE_LINE: */
807 /*case LFT_REPLY_SIZE_HEADERS: */
808 /*case LFT_REPLY_SIZE_BODY: */
809 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
810
4a972fa2 811 case LFT_TAG:
812 if (al->request)
30abd221 813 out = al->request->tag.buf();
4a972fa2 814
815 quote = 1;
816
817 break;
818
1a86db31
AJ
819 case LFT_IO_SIZE_TOTAL:
820 outint = al->cache.requestSize + al->cache.replySize;
821 doint = 1;
822 break;
823
4a972fa2 824 case LFT_EXT_LOG:
825 if (al->request)
30abd221 826 out = al->request->extacl_log.buf();
4a972fa2 827
828 quote = 1;
829
830 break;
831
7684c4b1 832 case LFT_PERCENT:
833 out = "%";
834
835 break;
836 }
837
26ac0430 838 if (dooff) {
ed013b6c 839 snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero ? (int) fmt->width : 0, outoff);
47f6e231 840 out = tmp;
26ac0430 841
47f6e231 842 } else if (doint) {
7684c4b1 843 snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero ? (int) fmt->width : 0, outint);
844 out = tmp;
845 }
846
847 if (out && *out) {
f8144161 848 if (quote || fmt->quote != LOG_QUOTE_NONE) {
7684c4b1 849 char *newout = NULL;
850 int newfree = 0;
851
852 switch (fmt->quote) {
853
854 case LOG_QUOTE_NONE:
855 newout = rfc1738_escape_unescaped(out);
7684c4b1 856 break;
857
858 case LOG_QUOTE_QUOTES:
859 newout = log_quoted_string(out);
860 newfree = 1;
861 break;
862
863 case LOG_QUOTE_BRAKETS:
864 newout = log_quote(out);
865 newfree = 1;
866 break;
867
868 case LOG_QUOTE_URL:
869 newout = rfc1738_escape(out);
870 break;
871
872 case LOG_QUOTE_RAW:
873 break;
874 }
875
876 if (newout) {
877 if (dofree)
878 safe_free(out);
879
880 out = newout;
881
882 dofree = newfree;
883 }
884 }
885
886 if (fmt->width) {
887 if (fmt->left)
2fe7eff9 888 mb.Printf("%-*s", (int) fmt->width, out);
7684c4b1 889 else
2fe7eff9 890 mb.Printf("%*s", (int) fmt->width, out);
7684c4b1 891 } else
2fe7eff9 892 mb.append(out, strlen(out));
7684c4b1 893 } else {
2fe7eff9 894 mb.append("-", 1);
7684c4b1 895 }
896
897 if (fmt->space)
2fe7eff9 898 mb.append(" ", 1);
7684c4b1 899
30abd221 900 sb.clean();
7684c4b1 901
902 if (dofree)
903 safe_free(out);
904 }
905
906 logfilePrintf(logfile, "%s\n", mb.buf);
907}
908
909/* parses a single token. Returns the token length in characters,
910 * and fills in the lt item with the token information.
911 * def is for sure null-terminated
912 */
913static int
fa38076e 914accessLogGetNewLogFormatToken(logformat_token * lt, char *def, enum log_quote *quote)
7684c4b1 915{
916 char *cur = def;
917
918 struct logformat_token_table_entry *lte;
919 int l;
920
921 memset(lt, 0, sizeof(*lt));
922 l = strcspn(cur, "%");
923
924 if (l > 0) {
925 char *cp;
926 /* it's a string for sure, until \0 or the next % */
927 cp = (char *)xmalloc(l + 1);
928 xstrncpy(cp, cur, l + 1);
929 lt->type = LFT_STRING;
930 lt->data.string = cp;
fa38076e 931
932 while (l > 0) {
26ac0430 933 switch (*cur) {
fa38076e 934
935 case '"':
936
937 if (*quote == LOG_QUOTE_NONE)
938 *quote = LOG_QUOTE_QUOTES;
939 else if (*quote == LOG_QUOTE_QUOTES)
940 *quote = LOG_QUOTE_NONE;
941
942 break;
943
944 case '[':
945 if (*quote == LOG_QUOTE_NONE)
946 *quote = LOG_QUOTE_BRAKETS;
947
948 break;
949
950 case ']':
951 if (*quote == LOG_QUOTE_BRAKETS)
952 *quote = LOG_QUOTE_NONE;
953
954 break;
955 }
956
957 cur++;
958 l--;
959 }
960
7684c4b1 961 goto done;
962 }
963
964 if (!*cur)
965 goto done;
966
967 cur++;
968
969 switch (*cur) {
970
971 case '"':
972 lt->quote = LOG_QUOTE_QUOTES;
973 cur++;
974 break;
975
976 case '\'':
977 lt->quote = LOG_QUOTE_RAW;
978 cur++;
979 break;
980
981 case '[':
982 lt->quote = LOG_QUOTE_BRAKETS;
983 cur++;
984 break;
985
986 case '#':
987 lt->quote = LOG_QUOTE_URL;
988 cur++;
989 break;
fa38076e 990
991 default:
992 lt->quote = *quote;
993 break;
7684c4b1 994 }
995
996 if (*cur == '-') {
997 lt->left = 1;
998 cur++;
999 }
1000
1001 if (*cur == '0') {
1002 lt->zero = 1;
1003 cur++;
1004 }
1005
ec451a0f 1006 if (xisdigit(*cur))
7684c4b1 1007 lt->width = strtol(cur, &cur, 10);
1008
1009 if (*cur == '.')
1010 lt->precision = strtol(cur + 1, &cur, 10);
1011
1012 if (*cur == '{') {
1013 char *cp;
1014 cur++;
1015 l = strcspn(cur, "}");
1016 cp = (char *)xmalloc(l + 1);
1017 xstrncpy(cp, cur, l + 1);
1018 lt->data.string = cp;
1019 cur += l;
1020
1021 if (*cur == '}')
1022 cur++;
1023 }
1024
1025 lt->type = LFT_NONE;
1026
1027 for (lte = logformat_token_table; lte->config != NULL; lte++) {
1028 if (strncmp(lte->config, cur, strlen(lte->config)) == 0) {
1029 lt->type = lte->token_type;
1030 cur += strlen(lte->config);
1031 break;
1032 }
1033 }
1034
1035 if (lt->type == LFT_NONE) {
1036 fatalf("Can't parse configuration token: '%s'\n",
1037 def);
1038 }
1039
7684c4b1 1040 if (*cur == ' ') {
1041 lt->space = 1;
1042 cur++;
1043 }
1044
1045done:
1046
1047 switch (lt->type) {
1048
1049 case LFT_REQUEST_HEADER:
1050
1051 case LFT_REPLY_HEADER:
1052
1053 if (lt->data.string) {
1054 char *header = lt->data.string;
1055 char *cp = strchr(header, ':');
1056
1057 if (cp) {
1058 *cp++ = '\0';
1059
1060 if (*cp == ',' || *cp == ';' || *cp == ':')
1061 lt->data.header.separator = *cp++;
1062 else
1063 lt->data.header.separator = ',';
1064
1065 lt->data.header.element = cp;
1066
1067 lt->type = (lt->type == LFT_REQUEST_HEADER) ?
1068 LFT_REQUEST_HEADER_ELEM :
1069 LFT_REPLY_HEADER_ELEM;
1070 }
1071
1072 lt->data.header.header = header;
1073 } else {
1074 lt->type = (lt->type == LFT_REQUEST_HEADER) ?
1075 LFT_REQUEST_ALL_HEADERS :
1076 LFT_REPLY_ALL_HEADERS;
1077 Config.onoff.log_mime_hdrs = 1;
1078 }
1079
1080 break;
1081
1082 case LFT_CLIENT_FQDN:
1083 Config.onoff.log_fqdn = 1;
1084 break;
1085
1086 case LFT_TIME_SUBSECOND:
1087 lt->divisor = 1000;
1088
1089 if (lt->precision) {
1090 int i;
1091 lt->divisor = 1000000;
1092
1093 for (i = lt->precision; i > 1; i--)
1094 lt->divisor /= 10;
1095
1096 if (!lt->divisor)
1097 lt->divisor = 0;
1098 }
1099
1100 break;
1101
1102 default:
1103 break;
1104 }
1105
1106 return (cur - def);
1107}
1108
1109int
1110accessLogParseLogFormat(logformat_token ** fmt, char *def)
1111{
1112 char *cur, *eos;
1113 logformat_token *new_lt, *last_lt;
fa38076e 1114 enum log_quote quote = LOG_QUOTE_NONE;
7684c4b1 1115
bf8fe701 1116 debugs(46, 2, "accessLogParseLogFormat: got definition '" << def << "'");
7684c4b1 1117
1118 /* very inefficent parser, but who cares, this needs to be simple */
1119 /* First off, let's tokenize, we'll optimize in a second pass.
1120 * A token can either be a %-prefixed sequence (usually a dynamic
1121 * token but it can be an escaped sequence), or a string. */
1122 cur = def;
1123 eos = def + strlen(def);
1124 *fmt = new_lt = last_lt = (logformat_token *)xmalloc(sizeof(logformat_token));
fa38076e 1125 cur += accessLogGetNewLogFormatToken(new_lt, cur, &quote);
7684c4b1 1126
1127 while (cur < eos) {
1128 new_lt = (logformat_token *)xmalloc(sizeof(logformat_token));
1129 last_lt->next = new_lt;
1130 last_lt = new_lt;
fa38076e 1131 cur += accessLogGetNewLogFormatToken(new_lt, cur, &quote);
7684c4b1 1132 }
1133
1134 return 1;
1135}
1136
1137void
1138accessLogDumpLogFormat(StoreEntry * entry, const char *name, logformat * definitions)
1139{
1140 logformat_token *t;
1141 logformat *format;
1142
1143 struct logformat_token_table_entry *te;
bf8fe701 1144 debugs(46, 0, "accessLogDumpLogFormat called");
7684c4b1 1145
1146 for (format = definitions; format; format = format->next) {
bf8fe701 1147 debugs(46, 0, "Dumping logformat definition for " << format->name);
7684c4b1 1148 storeAppendPrintf(entry, "logformat %s ", format->name);
7684c4b1 1149
3c52bc04 1150 for (t = format->format; t; t = t->next) {
7684c4b1 1151 if (t->type == LFT_STRING)
1152 storeAppendPrintf(entry, "%s", t->data.string);
1153 else {
3c52bc04 1154 char argbuf[256];
1155 char *arg = NULL;
1156 logformat_bcode_t type = t->type;
7684c4b1 1157
3c52bc04 1158 switch (type) {
7684c4b1 1159 /* special cases */
1160
1161 case LFT_STRING:
1162 break;
1163
1164 case LFT_REQUEST_HEADER_ELEM:
1165
1166 case LFT_REPLY_HEADER_ELEM:
1167
1168 if (t->data.header.separator != ',')
3c52bc04 1169 snprintf(argbuf, sizeof(argbuf), "%s:%c%s", t->data.header.header, t->data.header.separator, t->data.header.element);
7684c4b1 1170 else
3c52bc04 1171 snprintf(argbuf, sizeof(argbuf), "%s:%s", t->data.header.header, t->data.header.element);
1172
1173 arg = argbuf;
1174
1175 type = (type == LFT_REQUEST_HEADER_ELEM) ?
1176 LFT_REQUEST_HEADER :
1177 LFT_REPLY_HEADER;
1178
1179 break;
1180
1181 case LFT_REQUEST_ALL_HEADERS:
1182
1183 case LFT_REPLY_ALL_HEADERS:
1184
1185 type = (type == LFT_REQUEST_ALL_HEADERS) ?
1186 LFT_REQUEST_HEADER :
1187 LFT_REPLY_HEADER;
1188
1189 break;
7684c4b1 1190
1191 default:
1192 if (t->data.string)
3c52bc04 1193 arg = t->data.string;
7684c4b1 1194
1195 break;
1196 }
1197
3900307b 1198 entry->append("%", 1);
7684c4b1 1199
1200 switch (t->quote) {
1201
1202 case LOG_QUOTE_QUOTES:
3900307b 1203 entry->append("\"", 1);
7684c4b1 1204 break;
1205
1206 case LOG_QUOTE_BRAKETS:
3900307b 1207 entry->append("[", 1);
7684c4b1 1208 break;
1209
1210 case LOG_QUOTE_URL:
3900307b 1211 entry->append("#", 1);
7684c4b1 1212 break;
1213
1214 case LOG_QUOTE_RAW:
3900307b 1215 entry->append("'", 1);
7684c4b1 1216 break;
1217
1218 case LOG_QUOTE_NONE:
1219 break;
1220 }
1221
1222 if (t->left)
3900307b 1223 entry->append("-", 1);
7684c4b1 1224
3c52bc04 1225 if (t->zero)
3900307b 1226 entry->append("0", 1);
3c52bc04 1227
7684c4b1 1228 if (t->width)
1229 storeAppendPrintf(entry, "%d", (int) t->width);
1230
1231 if (t->precision)
1232 storeAppendPrintf(entry, ".%d", (int) t->precision);
1233
3c52bc04 1234 if (arg)
7684c4b1 1235 storeAppendPrintf(entry, "{%s}", arg);
1236
1237 for (te = logformat_token_table; te->config != NULL; te++) {
1238 if (te->token_type == t->type) {
1239 storeAppendPrintf(entry, "%s", te->config);
1240 break;
1241 }
1242 }
1243
3c52bc04 1244 if (t->space)
3900307b 1245 entry->append(" ", 1);
3c52bc04 1246
7684c4b1 1247 assert(te->config != NULL);
7684c4b1 1248 }
1249 }
1e7a7497 1250
3900307b 1251 entry->append("\n", 1);
7684c4b1 1252 }
3c52bc04 1253
7684c4b1 1254}
1255
1256void
1257accessLogFreeLogFormat(logformat_token ** tokens)
1258{
1259 while (*tokens) {
1260 logformat_token *token = *tokens;
1261 *tokens = token->next;
1262 safe_free(token->data.string);
1263 xfree(token);
1264 }
1265}
1266
137ee196 1267static void
7684c4b1 1268accessLogSquid(AccessLogEntry * al, Logfile * logfile)
f892c2bf 1269{
9bea1d5b 1270 const char *client = NULL;
a7ad6e4e 1271 const char *user = NULL;
cc192b50 1272 char buf[MAX_IPSTRLEN];
62e76326 1273
cc192b50 1274 if (Config.onoff.log_fqdn) {
62e76326 1275 client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
cc192b50 1276 }
62e76326 1277
cc192b50 1278 if (client == NULL) {
1279 client = al->cache.caddr.NtoA(buf,MAX_IPSTRLEN);
1280 }
62e76326 1281
a7ad6e4e 1282 user = accessLogFormatName(al->cache.authuser);
62e76326 1283
abb929f0 1284 if (!user)
1285 user = accessLogFormatName(al->cache.extuser);
1286
a7ad6e4e 1287#if USE_SSL
62e76326 1288
a7ad6e4e 1289 if (!user)
62e76326 1290 user = accessLogFormatName(al->cache.ssluser);
1291
a7ad6e4e 1292#endif
62e76326 1293
a7ad6e4e 1294 if (!user)
62e76326 1295 user = accessLogFormatName(al->cache.rfc931);
1296
a7ad6e4e 1297 if (user && !*user)
62e76326 1298 safe_free(user);
1299
e293bb20 1300 if (!Config.onoff.log_mime_hdrs) {
0a48349d 1301 logfilePrintf(logfile, "%9ld.%03d %6d %s %s/%03d %"PRId64" %s %s %s %s%s/%s %s\n",
e293bb20 1302 (long int) current_time.tv_sec,
1303 (int) current_time.tv_usec / 1000,
1304 al->cache.msec,
1305 client,
1306 log_tags[al->cache.code],
1307 al->http.code,
1a86db31 1308 al->cache.replySize,
e293bb20 1309 al->_private.method_str,
1310 al->url,
1311 user ? user : dash_str,
1312 al->hier.ping.timedout ? "TIMEOUT_" : "",
1313 hier_strings[al->hier.code],
1314 al->hier.host,
1315 al->http.content_type);
1316 } else {
7684c4b1 1317 char *ereq = log_quote(al->headers.request);
1318 char *erep = log_quote(al->headers.reply);
0a48349d 1319 logfilePrintf(logfile, "%9ld.%03d %6d %s %s/%03d %"PRId64" %s %s %s %s%s/%s %s [%s] [%s]\n",
e293bb20 1320 (long int) current_time.tv_sec,
1321 (int) current_time.tv_usec / 1000,
1322 al->cache.msec,
1323 client,
1324 log_tags[al->cache.code],
1325 al->http.code,
1a86db31 1326 al->cache.replySize,
e293bb20 1327 al->_private.method_str,
1328 al->url,
1329 user ? user : dash_str,
1330 al->hier.ping.timedout ? "TIMEOUT_" : "",
1331 hier_strings[al->hier.code],
1332 al->hier.host,
1333 al->http.content_type,
1334 ereq,
1335 erep);
7684c4b1 1336 safe_free(ereq);
1337 safe_free(erep);
7684c4b1 1338 }
e293bb20 1339 safe_free(user);
f892c2bf 1340}
1341
137ee196 1342static void
7684c4b1 1343accessLogCommon(AccessLogEntry * al, Logfile * logfile)
f892c2bf 1344{
9bea1d5b 1345 const char *client = NULL;
1346 char *user1 = NULL, *user2 = NULL;
cc192b50 1347 char buf[MAX_IPSTRLEN];
62e76326 1348
cc192b50 1349 if (Config.onoff.log_fqdn) {
62e76326 1350 client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
cc192b50 1351 }
62e76326 1352
cc192b50 1353 if (client == NULL) {
1354 client = al->cache.caddr.NtoA(buf,MAX_IPSTRLEN);
1355 }
62e76326 1356
9bea1d5b 1357 user1 = accessLogFormatName(al->cache.authuser);
62e76326 1358
9bea1d5b 1359 user2 = accessLogFormatName(al->cache.rfc931);
62e76326 1360
47f6e231 1361 logfilePrintf(logfile, "%s %s %s [%s] \"%s %s HTTP/%d.%d\" %d %"PRId64" %s:%s",
62e76326 1362 client,
1363 user2 ? user2 : dash_str,
1364 user1 ? user1 : dash_str,
1365 mkhttpdlogtime(&squid_curtime),
1366 al->_private.method_str,
1367 al->url,
1368 al->http.version.major, al->http.version.minor,
1369 al->http.code,
1a86db31 1370 al->cache.replySize,
62e76326 1371 log_tags[al->cache.code],
1372 hier_strings[al->hier.code]);
1373
9bea1d5b 1374 safe_free(user1);
62e76326 1375
9bea1d5b 1376 safe_free(user2);
7684c4b1 1377
1378 if (Config.onoff.log_mime_hdrs) {
1379 char *ereq = log_quote(al->headers.request);
1380 char *erep = log_quote(al->headers.reply);
1381 logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
1382 safe_free(ereq);
1383 safe_free(erep);
1384 } else {
1385 logfilePrintf(logfile, "\n");
1386 }
1387
f892c2bf 1388}
1389
1390void
7684c4b1 1391accessLogLog(AccessLogEntry * al, ACLChecklist * checklist)
f892c2bf 1392{
7684c4b1 1393 customlog *log;
1394
9bea1d5b 1395 if (LogfileStatus != LOG_ENABLE)
62e76326 1396 return;
1397
9bea1d5b 1398 if (al->url == NULL)
62e76326 1399 al->url = dash_str;
1400
9bea1d5b 1401 if (!al->http.content_type || *al->http.content_type == '\0')
62e76326 1402 al->http.content_type = dash_str;
1403
9bea1d5b 1404 if (al->icp.opcode)
62e76326 1405 al->_private.method_str = icp_opcode_str[al->icp.opcode];
9bea1d5b 1406 else
60745f24 1407 al->_private.method_str = RequestMethodStr(al->http.method);
62e76326 1408
9bea1d5b 1409 if (al->hier.host[0] == '\0')
62e76326 1410 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
9bea1d5b 1411
7684c4b1 1412 for (log = Config.Log.accesslogs; log; log = log->next) {
26ac0430 1413 if (checklist && log->aclList && !checklist->matchAclListFast(log->aclList))
7684c4b1 1414 continue;
62e76326 1415
7684c4b1 1416 switch (log->type) {
1417
1418 case CLF_AUTO:
1419
1420 if (Config.onoff.common_log)
1421 accessLogCommon(al, log->logfile);
1422 else
1423 accessLogSquid(al, log->logfile);
1424
1425 break;
1426
1427 case CLF_SQUID:
1428 accessLogSquid(al, log->logfile);
1429
1430 break;
1431
1432 case CLF_COMMON:
1433 accessLogCommon(al, log->logfile);
1434
1435 break;
1436
1437 case CLF_CUSTOM:
1438 accessLogCustom(al, log);
1439
1440 break;
1441
1442 case CLF_NONE:
1443 goto last;
1444
1445 default:
1446 fatalf("Unknown log format %d\n", log->type);
1447
1448 break;
1449 }
1450
1451 logfileFlush(log->logfile);
1452
1453 if (!checklist)
1454 break;
f892c2bf 1455 }
62e76326 1456
7684c4b1 1457last:
1458 (void)0; /* NULL statement for label */
1459
e66d7923 1460#if MULTICAST_MISS_STREAM
62e76326 1461
9bea1d5b 1462 if (al->cache.code != LOG_TCP_MISS)
62e76326 1463 (void) 0;
9bea1d5b 1464 else if (al->http.method != METHOD_GET)
62e76326 1465 (void) 0;
9bea1d5b 1466 else if (mcast_miss_fd < 0)
62e76326 1467 (void) 0;
9bea1d5b 1468 else {
62e76326 1469 unsigned int ibuf[365];
1470 size_t isize;
1471 xstrncpy((char *) ibuf, al->url, 364 * sizeof(int));
1472 isize = ((strlen(al->url) + 8) / 8) * 2;
1473
1474 if (isize > 364)
1475 isize = 364;
1476
1477 mcast_encode((unsigned int *) ibuf, isize,
1478 (const unsigned int *) Config.mcast_miss.encode_key);
1479
1480 comm_udp_sendto(mcast_miss_fd,
1481 &mcast_miss_to, sizeof(mcast_miss_to),
1482 ibuf, isize * sizeof(int));
e66d7923 1483 }
62e76326 1484
e66d7923 1485#endif
f892c2bf 1486}
1487
1488void
9bea1d5b 1489accessLogRotate(void)
f892c2bf 1490{
7684c4b1 1491 customlog *log;
d21f1c54 1492#if FORW_VIA_DB
7684c4b1 1493
9bea1d5b 1494 fvdbClear();
d21f1c54 1495#endif
62e76326 1496
7684c4b1 1497 for (log = Config.Log.accesslogs; log; log = log->next) {
1498 if (log->logfile) {
1499 logfileRotate(log->logfile);
1500 }
1501 }
62e76326 1502
c3609322 1503#if HEADERS_LOG
62e76326 1504
9bea1d5b 1505 logfileRotate(headerslog);
62e76326 1506
c3609322 1507#endif
f892c2bf 1508}
1509
1510void
9bea1d5b 1511accessLogClose(void)
f892c2bf 1512{
7684c4b1 1513 customlog *log;
62e76326 1514
7684c4b1 1515 for (log = Config.Log.accesslogs; log; log = log->next) {
1516 if (log->logfile) {
1517 logfileClose(log->logfile);
1518 log->logfile = NULL;
1519 }
1520 }
62e76326 1521
c3609322 1522#if HEADERS_LOG
62e76326 1523
9bea1d5b 1524 logfileClose(headerslog);
62e76326 1525
9bea1d5b 1526 headerslog = NULL;
62e76326 1527
c3609322 1528#endif
f892c2bf 1529}
1530
b24880fe 1531HierarchyLogEntry::HierarchyLogEntry() :
1532 code(HIER_NONE),
1533 cd_lookup(LOOKUP_NONE),
1534 n_choices(0),
1535 n_ichoices(0)
1536{
1537 memset(host, '\0', SQUIDHOSTNAMELEN);
1538 memset(cd_host, '\0', SQUIDHOSTNAMELEN);
1539
1540 peer_select_start.tv_sec =0;
1541 peer_select_start.tv_usec =0;
1542
1543 store_complete_stop.tv_sec =0;
1544 store_complete_stop.tv_usec =0;
1545}
1546
f892c2bf 1547void
9bea1d5b 1548hierarchyNote(HierarchyLogEntry * hl,
62e76326 1549 hier_code code,
1550 const char *cache_peer)
f892c2bf 1551{
9bea1d5b 1552 assert(hl != NULL);
1553 hl->code = code;
1554 xstrncpy(hl->host, cache_peer, SQUIDHOSTNAMELEN);
f892c2bf 1555}
7a2f978b 1556
5f5e883f
FC
1557static void
1558accessLogRegisterWithCacheManager(void)
1559{
1560#if FORW_VIA_DB
1561 fvdbRegisterWithCacheManager();
1562#endif
1563}
1564
7a2f978b 1565void
9bea1d5b 1566accessLogInit(void)
7a2f978b 1567{
7684c4b1 1568 customlog *log;
caf0eda4
FC
1569
1570 accessLogRegisterWithCacheManager();
1571
9bea1d5b 1572 assert(sizeof(log_tags) == (LOG_TYPE_MAX + 1) * sizeof(char *));
62e76326 1573
7684c4b1 1574 for (log = Config.Log.accesslogs; log; log = log->next) {
ed22613b 1575 if (log->type == CLF_NONE)
7684c4b1 1576 continue;
62e76326 1577
7684c4b1 1578 log->logfile = logfileOpen(log->filename, MAX_URL << 1, 1);
62e76326 1579
7684c4b1 1580 LogfileStatus = LOG_ENABLE;
1581 }
62e76326 1582
c3609322 1583#if HEADERS_LOG
62e76326 1584
9bea1d5b 1585 headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
62e76326 1586
9bea1d5b 1587 assert(NULL != headerslog);
62e76326 1588
d21f1c54 1589#endif
e66d7923 1590#if MULTICAST_MISS_STREAM
62e76326 1591
9bea1d5b 1592 if (Config.mcast_miss.addr.s_addr != no_addr.s_addr) {
62e76326 1593 memset(&mcast_miss_to, '\0', sizeof(mcast_miss_to));
1594 mcast_miss_to.sin_family = AF_INET;
1595 mcast_miss_to.sin_port = htons(Config.mcast_miss.port);
1596 mcast_miss_to.sin_addr.s_addr = Config.mcast_miss.addr.s_addr;
1597 mcast_miss_fd = comm_open(SOCK_DGRAM,
bdb741f4 1598 IPPROTO_UDP,
62e76326 1599 Config.Addrs.udp_incoming,
1600 Config.mcast_miss.port,
1601 COMM_NONBLOCKING,
1602 "Multicast Miss Stream");
1603
1604 if (mcast_miss_fd < 0)
1605 fatal("Cannot open Multicast Miss Stream Socket");
1606
bf8fe701 1607 debugs(46, 1, "Multicast Miss Stream Socket opened on FD " << mcast_miss_fd);
62e76326 1608
1609 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
1610
1611 if (strlen(Config.mcast_miss.encode_key) < 16)
1612 fatal("mcast_encode_key is too short, must be 16 characters");
e66d7923 1613 }
62e76326 1614
62ee09ca 1615#endif
1616#if FORW_VIA_DB
1617
1618 fvdbInit();
1619
1620#endif
1621}
1622
c6e7cab0 1623const char *
9bea1d5b 1624accessLogTime(time_t t)
c6e7cab0 1625{
62e76326 1626
9bea1d5b 1627 struct tm *tm;
1628 static char buf[128];
1629 static time_t last_t = 0;
62e76326 1630
9bea1d5b 1631 if (t != last_t) {
62e76326 1632 tm = localtime(&t);
1633 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
1634 last_t = t;
c6e7cab0 1635 }
62e76326 1636
9bea1d5b 1637 return buf;
c6e7cab0 1638}
d21f1c54 1639
1640
1641#if FORW_VIA_DB
1afe05c5 1642
d21f1c54 1643static void
9bea1d5b 1644fvdbInit(void)
d21f1c54 1645{
30abd221 1646 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
1647 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
62ee09ca 1648}
1649
1650static void
caf0eda4 1651fvdbRegisterWithCacheManager(void)
62ee09ca 1652{
d62a240c
FC
1653 CacheManager *manager=CacheManager::GetInstance();
1654 manager->registerAction("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
1655 manager->registerAction("forw_headers", "X-Forwarded-For Request Headers",
26ac0430 1656 fvdbDumpForw, 0, 1);
d21f1c54 1657}
1658
1659static void
9bea1d5b 1660fvdbCount(hash_table * hash, const char *key)
1afe05c5 1661{
9bea1d5b 1662 fvdb_entry *fv;
62e76326 1663
9bea1d5b 1664 if (NULL == hash)
62e76326 1665 return;
1666
8021b4a6 1667 fv = (fvdb_entry *)hash_lookup(hash, key);
62e76326 1668
9bea1d5b 1669 if (NULL == fv) {
62e76326 1670 fv = static_cast <fvdb_entry *>(xcalloc(1, sizeof(fvdb_entry)));
1671 fv->hash.key = xstrdup(key);
1672 hash_join(hash, &fv->hash);
1afe05c5 1673 }
62e76326 1674
9bea1d5b 1675 fv->n++;
d21f1c54 1676}
1677
1678void
9bea1d5b 1679fvdbCountVia(const char *key)
d21f1c54 1680{
9bea1d5b 1681 fvdbCount(via_table, key);
d21f1c54 1682}
1683
1684void
9bea1d5b 1685fvdbCountForw(const char *key)
d21f1c54 1686{
9bea1d5b 1687 fvdbCount(forw_table, key);
d21f1c54 1688}
1689
1afe05c5 1690static void
9bea1d5b 1691fvdbDumpTable(StoreEntry * e, hash_table * hash)
d21f1c54 1692{
9bea1d5b 1693 hash_link *h;
1694 fvdb_entry *fv;
62e76326 1695
9bea1d5b 1696 if (hash == NULL)
62e76326 1697 return;
1698
9bea1d5b 1699 hash_first(hash);
62e76326 1700
9bea1d5b 1701 while ((h = hash_next(hash))) {
62e76326 1702 fv = (fvdb_entry *) h;
1703 storeAppendPrintf(e, "%9d %s\n", fv->n, hashKeyStr(&fv->hash));
1afe05c5 1704 }
d21f1c54 1705}
1706
1707static void
9bea1d5b 1708fvdbDumpVia(StoreEntry * e)
d21f1c54 1709{
9bea1d5b 1710 fvdbDumpTable(e, via_table);
d21f1c54 1711}
1afe05c5 1712
d21f1c54 1713static void
9bea1d5b 1714fvdbDumpForw(StoreEntry * e)
d21f1c54 1715{
9bea1d5b 1716 fvdbDumpTable(e, forw_table);
d21f1c54 1717}
1718
1719static
b644367b 1720void
9bea1d5b 1721fvdbFreeEntry(void *data)
d21f1c54 1722{
8021b4a6 1723 fvdb_entry *fv = static_cast <fvdb_entry *>(data);
9bea1d5b 1724 xfree(fv->hash.key);
1725 xfree(fv);
d21f1c54 1726}
1727
1728static void
9bea1d5b 1729fvdbClear(void)
d21f1c54 1730{
9bea1d5b 1731 hashFreeItems(via_table, fvdbFreeEntry);
1732 hashFreeMemory(via_table);
30abd221 1733 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
9bea1d5b 1734 hashFreeItems(forw_table, fvdbFreeEntry);
1735 hashFreeMemory(forw_table);
30abd221 1736 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 1737}
1738
1afe05c5 1739#endif
e66d7923 1740
1741#if MULTICAST_MISS_STREAM
1742/*
1743 * From http://www.io.com/~paulhart/game/algorithms/tea.html
1744 *
1745 * size of 'ibuf' must be a multiple of 2.
1746 * size of 'key' must be 4.
1747 * 'ibuf' is modified in place, encrypted data is written in
1748 * network byte order.
1749 */
1750static void
9bea1d5b 1751mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
e66d7923 1752{
9bea1d5b 1753 unsigned int y;
1754 unsigned int z;
1755 unsigned int sum;
1756 const unsigned int delta = 0x9e3779b9;
1757 unsigned int n = 32;
1758 const unsigned int k0 = htonl(key[0]);
1759 const unsigned int k1 = htonl(key[1]);
1760 const unsigned int k2 = htonl(key[2]);
1761 const unsigned int k3 = htonl(key[3]);
1762 int i;
62e76326 1763
9bea1d5b 1764 for (i = 0; i < isize; i += 2) {
62e76326 1765 y = htonl(ibuf[i]);
1766 z = htonl(ibuf[i + 1]);
1767 sum = 0;
1768
1769 for (n = 32; n; n--) {
1770 sum += delta;
1771 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
1772 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
1773 }
1774
1775 ibuf[i] = htonl(y);
1776 ibuf[i + 1] = htonl(z);
e66d7923 1777 }
1778}
1779
1780#endif
c3609322 1781
1782#if HEADERS_LOG
1783void
60745f24 1784headersLog(int cs, int pq, const HttpRequestMethod& method, void *data)
c3609322 1785{
9bea1d5b 1786 HttpReply *rep;
190154cf 1787 HttpRequest *req;
9bea1d5b 1788 unsigned short magic = 0;
1789 unsigned char M = (unsigned char) m;
1790 unsigned short S;
1791 char *hmask;
1792 int ccmask = 0;
62e76326 1793
9bea1d5b 1794 if (0 == pq) {
62e76326 1795 /* reply */
1796 rep = data;
1797 req = NULL;
1798 magic = 0x0050;
1799 hmask = rep->header.mask;
1800
1801 if (rep->cache_control)
1802 ccmask = rep->cache_control->mask;
9bea1d5b 1803 } else {
62e76326 1804 /* request */
1805 req = data;
1806 rep = NULL;
1807 magic = 0x0051;
1808 hmask = req->header.mask;
1809
1810 if (req->cache_control)
1811 ccmask = req->cache_control->mask;
c3609322 1812 }
62e76326 1813
9bea1d5b 1814 if (0 == cs) {
62e76326 1815 /* client */
1816 magic |= 0x4300;
9bea1d5b 1817 } else {
62e76326 1818 /* server */
1819 magic |= 0x5300;
c3609322 1820 }
62e76326 1821
9bea1d5b 1822 magic = htons(magic);
1823 ccmask = htonl(ccmask);
62e76326 1824
9bea1d5b 1825 if (0 == pq)
62e76326 1826 S = (unsigned short) rep->sline.status;
9bea1d5b 1827 else
62e76326 1828 S = (unsigned short) HTTP_STATUS_NONE;
1829
9bea1d5b 1830 logfileWrite(headerslog, &magic, sizeof(magic));
62e76326 1831
9bea1d5b 1832 logfileWrite(headerslog, &M, sizeof(M));
62e76326 1833
9bea1d5b 1834 logfileWrite(headerslog, &S, sizeof(S));
62e76326 1835
9bea1d5b 1836 logfileWrite(headerslog, hmask, sizeof(HttpHeaderMask));
62e76326 1837
9bea1d5b 1838 logfileWrite(headerslog, &ccmask, sizeof(int));
62e76326 1839
9bea1d5b 1840 logfileFlush(headerslog);
c3609322 1841}
1842
1843#endif
c8be6d7b 1844
1845void
1846accessLogFreeMemory(AccessLogEntry * aLogEntry)
1847{
1848 safe_free(aLogEntry->headers.request);
1849 safe_free(aLogEntry->headers.reply);
1850 safe_free(aLogEntry->cache.authuser);
7684c4b1 1851
90a8964c 1852 HTTPMSGUNLOCK(aLogEntry->reply);
6dd9f4bd 1853 HTTPMSGUNLOCK(aLogEntry->request);
c8be6d7b 1854}
1855
1856int
1857logTypeIsATcpHit(log_type code)
1858{
1859 /* this should be a bitmap for better optimization */
62e76326 1860
c8be6d7b 1861 if (code == LOG_TCP_HIT)
62e76326 1862 return 1;
1863
c8be6d7b 1864 if (code == LOG_TCP_IMS_HIT)
62e76326 1865 return 1;
1866
1d7ab0f4 1867 if (code == LOG_TCP_REFRESH_FAIL)
62e76326 1868 return 1;
1869
1d7ab0f4 1870 if (code == LOG_TCP_REFRESH_UNMODIFIED)
62e76326 1871 return 1;
1872
c8be6d7b 1873 if (code == LOG_TCP_NEGATIVE_HIT)
62e76326 1874 return 1;
1875
c8be6d7b 1876 if (code == LOG_TCP_MEM_HIT)
62e76326 1877 return 1;
1878
c8be6d7b 1879 if (code == LOG_TCP_OFFLINE_HIT)
62e76326 1880 return 1;
1881
c8be6d7b 1882 return 0;
1883}
e6ccf245 1884