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