]> git.ipfire.org Git - thirdparty/squid.git/blame - src/access_log.cc
Sync store meta assignments with Squid-2.
[thirdparty/squid.git] / src / access_log.cc
CommitLineData
f892c2bf 1
2/*
ed013b6c 3 * $Id: access_log.cc,v 1.128 2007/08/13 18:25:14 hno 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"
985c86bc 48#include "SquidTime.h"
780745e5 49#include "CacheManager.h"
7684c4b1 50
51static void accessLogSquid(AccessLogEntry * al, Logfile * logfile);
52static void accessLogCommon(AccessLogEntry * al, Logfile * logfile);
53static void accessLogCustom(AccessLogEntry * al, customlog * log);
c3609322 54#if HEADERS_LOG
55static Logfile *headerslog = NULL;
56#endif
a7c05555 57
e66d7923 58#if MULTICAST_MISS_STREAM
59static int mcast_miss_fd = -1;
62e76326 60
e66d7923 61static struct sockaddr_in mcast_miss_to;
9bea1d5b 62static void mcast_encode(unsigned int *, size_t, const unsigned int *);
e66d7923 63#endif
64
7a2f978b 65const char *log_tags[] =
62e76326 66 {
67 "NONE",
68 "TCP_HIT",
69 "TCP_MISS",
1d7ab0f4 70 "TCP_REFRESH_UNMODIFIED",
71 "TCP_REFRESH_FAIL",
72 "TCP_REFRESH_MODIFIED",
62e76326 73 "TCP_CLIENT_REFRESH_MISS",
74 "TCP_IMS_HIT",
75 "TCP_SWAPFAIL_MISS",
76 "TCP_NEGATIVE_HIT",
77 "TCP_MEM_HIT",
78 "TCP_DENIED",
f1f78347 79 "TCP_DENIED_REPLY",
62e76326 80 "TCP_OFFLINE_HIT",
efd900cb 81#if LOG_TCP_REDIRECTS
62e76326 82 "TCP_REDIRECT",
efd900cb 83#endif
62e76326 84 "UDP_HIT",
85 "UDP_MISS",
86 "UDP_DENIED",
87 "UDP_INVALID",
88 "UDP_MISS_NOFETCH",
89 "ICP_QUERY",
90 "LOG_TYPE_MAX"
91 };
7a2f978b 92
d21f1c54 93#if FORW_VIA_DB
62e76326 94
95typedef struct
96{
6c40d272 97 hash_link hash;
1afe05c5 98 int n;
62e76326 99}
100
101fvdb_entry;
d21f1c54 102static hash_table *via_table = NULL;
103static hash_table *forw_table = NULL;
780745e5 104static void fvdbInit();
9bea1d5b 105static void fvdbDumpTable(StoreEntry * e, hash_table * hash);
106static void fvdbCount(hash_table * hash, const char *key);
d21f1c54 107static OBJH fvdbDumpVia;
108static OBJH fvdbDumpForw;
109static FREE fvdbFreeEntry;
9bea1d5b 110static void fvdbClear(void);
780745e5 111static void fvdbRegisterWithCacheManager(CacheManager & manager);
d21f1c54 112#endif
f892c2bf 113
114static int LogfileStatus = LOG_DISABLE;
f892c2bf 115#define LOG_BUF_SZ (MAX_URL<<2)
f892c2bf 116
117static const char c2x[] =
62e76326 118 "000102030405060708090a0b0c0d0e0f"
119 "101112131415161718191a1b1c1d1e1f"
120 "202122232425262728292a2b2c2d2e2f"
121 "303132333435363738393a3b3c3d3e3f"
122 "404142434445464748494a4b4c4d4e4f"
123 "505152535455565758595a5b5c5d5e5f"
124 "606162636465666768696a6b6c6d6e6f"
125 "707172737475767778797a7b7c7d7e7f"
126 "808182838485868788898a8b8c8d8e8f"
127 "909192939495969798999a9b9c9d9e9f"
128 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
129 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
130 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
131 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
132 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
133 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
f892c2bf 134
135/* log_quote -- URL-style encoding on MIME headers. */
136
592da4ec 137char *
9bea1d5b 138log_quote(const char *header)
f892c2bf 139{
9bea1d5b 140 int c;
141 int i;
142 char *buf;
143 char *buf_cursor;
62e76326 144
9bea1d5b 145 if (header == NULL) {
62e76326 146 buf = static_cast<char *>(xcalloc(1, 1));
147 *buf = '\0';
148 return buf;
f892c2bf 149 }
62e76326 150
e6ccf245 151 buf = static_cast<char *>(xcalloc(1, (strlen(header) * 3) + 1));
9bea1d5b 152 buf_cursor = buf;
153 /*
154 * We escape: \x00-\x1F"#%;<>?{}|\\\\^~`\[\]\x7F-\xFF
155 * which is the default escape list for the CPAN Perl5 URI module
156 * modulo the inclusion of space (x40) to make the raw logs a bit
157 * more readable.
158 */
62e76326 159
9bea1d5b 160 while ((c = *(const unsigned char *) header++) != '\0') {
7e3ce7b9 161#if !OLD_LOG_MIME
62e76326 162
163 if (c == '\r') {
164 *buf_cursor++ = '\\';
165 *buf_cursor++ = 'r';
166 } else if (c == '\n') {
167 *buf_cursor++ = '\\';
168 *buf_cursor++ = 'n';
169 } else
7e3ce7b9 170#endif
62e76326 171 if (c <= 0x1F
172 || c >= 0x7F
7c013081 173 || c == '%'
7e3ce7b9 174#if OLD_LOG_MIME
62e76326 175 || c == '"'
176 || c == '#'
62e76326 177 || c == ';'
178 || c == '<'
179 || c == '>'
180 || c == '?'
181 || c == '{'
182 || c == '}'
183 || c == '|'
184 || c == '\\'
185 || c == '^'
186 || c == '~'
187 || c == '`'
7e3ce7b9 188#endif
62e76326 189 || c == '['
190 || c == ']') {
191 *buf_cursor++ = '%';
192 i = c * 2;
193 *buf_cursor++ = c2x[i];
194 *buf_cursor++ = c2x[i + 1];
7e3ce7b9 195#if !OLD_LOG_MIME
62e76326 196
197 } else if (c == '\\') {
198 *buf_cursor++ = '\\';
199 *buf_cursor++ = '\\';
7e3ce7b9 200#endif
62e76326 201
202 } else {
203 *buf_cursor++ = (char) c;
204 }
f892c2bf 205 }
62e76326 206
9bea1d5b 207 *buf_cursor = '\0';
208 return buf;
f892c2bf 209}
210
2d72d4fd 211static char *
9bea1d5b 212username_quote(const char *header)
94439e4e 213/* copy of log_quote. Bugs there will be found here */
214{
9bea1d5b 215 int c;
216 int i;
217 char *buf;
218 char *buf_cursor;
62e76326 219
9bea1d5b 220 if (header == NULL) {
62e76326 221 buf = static_cast<char *>(xcalloc(1, 1));
222 *buf = '\0';
223 return buf;
94439e4e 224 }
62e76326 225
e6ccf245 226 buf = static_cast<char *>(xcalloc(1, (strlen(header) * 3) + 1));
9bea1d5b 227 buf_cursor = buf;
228 /*
229 * We escape: space \x00-\x1F and space (0x40) and \x7F-\xFF
230 * to prevent garbage in the logs. CR and LF are also there just in case.
231 */
62e76326 232
9bea1d5b 233 while ((c = *(const unsigned char *) header++) != '\0') {
62e76326 234 if (c == '\r') {
235 *buf_cursor++ = '\\';
236 *buf_cursor++ = 'r';
237 } else if (c == '\n') {
238 *buf_cursor++ = '\\';
239 *buf_cursor++ = 'n';
240 } else if (c <= 0x1F
241 || c >= 0x7F
7c013081 242 || c == '%'
62e76326 243 || c == ' ') {
244 *buf_cursor++ = '%';
245 i = c * 2;
246 *buf_cursor++ = c2x[i];
247 *buf_cursor++ = c2x[i + 1];
248 } else {
249 *buf_cursor++ = (char) c;
250 }
94439e4e 251 }
62e76326 252
9bea1d5b 253 *buf_cursor = '\0';
254 return buf;
94439e4e 255}
256
2d72d4fd 257static char *
9bea1d5b 258accessLogFormatName(const char *name)
94439e4e 259{
9bea1d5b 260 if (NULL == name)
62e76326 261 return NULL;
262
90c858f7 263 if (name[0] == '\0')
264 return NULL;
265
9bea1d5b 266 return username_quote(name);
94439e4e 267}
268
7684c4b1 269static char *
270log_quoted_string(const char *str)
271{
272 char *out = (char *)xmalloc(strlen(str) * 2 + 1);
273 char *p = out;
274
275 while (*str) {
276 int l = strcspn(str, "\"\\\r\n\t");
277 memcpy(p, str, l);
278 str += l;
279 p += l;
280
281 switch (*str) {
282
283 case '\0':
284 break;
285
286 case '\r':
287 *p++ = '\\';
288 *p++ = 'r';
289 str++;
290 break;
291
292 case '\n':
293 *p++ = '\\';
294 *p++ = 'n';
295 str++;
296 break;
297
298 case '\t':
299 *p++ = '\\';
300 *p++ = 't';
301 str++;
302 break;
303
304 default:
305 *p++ = '\\';
306 *p++ = *str;
307 str++;
308 break;
309 }
310 }
311
312 *p++ = '\0';
313 return out;
314}
315
316/*
317 * Bytecodes for the configureable logformat stuff
318 */
319typedef enum {
320 LFT_NONE, /* dummy */
321 LFT_STRING,
322
323 LFT_CLIENT_IP_ADDRESS,
324 LFT_CLIENT_FQDN,
44eb213b 325 LFT_CLIENT_PORT,
7684c4b1 326
327 /*LFT_SERVER_IP_ADDRESS, */
328 LFT_SERVER_IP_OR_PEER_NAME,
329 /*LFT_SERVER_PORT, */
330
331 LFT_LOCAL_IP,
332 LFT_LOCAL_PORT,
333 /*LFT_LOCAL_NAME, */
334
335 LFT_TIME_SECONDS_SINCE_EPOCH,
336 LFT_TIME_SUBSECOND,
337 LFT_TIME_LOCALTIME,
338 LFT_TIME_GMT,
339 LFT_TIME_TO_HANDLE_REQUEST,
340
341 LFT_REQUEST_HEADER,
342 LFT_REQUEST_HEADER_ELEM,
343 LFT_REQUEST_ALL_HEADERS,
344
345 LFT_REPLY_HEADER,
346 LFT_REPLY_HEADER_ELEM,
347 LFT_REPLY_ALL_HEADERS,
348
349 LFT_USER_NAME,
350 LFT_USER_LOGIN,
351 LFT_USER_IDENT,
352 /*LFT_USER_REALM, */
353 /*LFT_USER_SCHEME, */
4a972fa2 354 LFT_USER_EXTERNAL,
7684c4b1 355
356 LFT_HTTP_CODE,
357 /*LFT_HTTP_STATUS, */
358
359 LFT_SQUID_STATUS,
360 /*LFT_SQUID_ERROR, */
361 LFT_SQUID_HIERARCHY,
362
363 LFT_MIME_TYPE,
364
365 LFT_REQUEST_METHOD,
366 LFT_REQUEST_URI,
fef92cc1 367 LFT_REQUEST_URLPATH,
7684c4b1 368 /*LFT_REQUEST_QUERY, * // * this is not needed. see strip_query_terms */
369 LFT_REQUEST_VERSION,
370
371 /*LFT_REQUEST_SIZE_TOTAL, */
372 /*LFT_REQUEST_SIZE_LINE, */
373 /*LFT_REQUEST_SIZE_HEADERS, */
374 /*LFT_REQUEST_SIZE_BODY, */
375 /*LFT_REQUEST_SIZE_BODY_NO_TE, */
376
377 LFT_REPLY_SIZE_TOTAL,
0976f8db 378 LFT_REPLY_HIGHOFFSET,
379 LFT_REPLY_OBJECTSIZE,
7684c4b1 380 /*LFT_REPLY_SIZE_LINE, */
381 /*LFT_REPLY_SIZE_HEADERS, */
382 /*LFT_REPLY_SIZE_BODY, */
383 /*LFT_REPLY_SIZE_BODY_NO_TE, */
384
4a972fa2 385 LFT_TAG,
386 LFT_EXT_LOG,
387
7684c4b1 388 LFT_PERCENT /* special string cases for escaped chars */
389} logformat_bcode_t;
390
391enum log_quote {
392 LOG_QUOTE_NONE = 0,
393 LOG_QUOTE_QUOTES,
394 LOG_QUOTE_BRAKETS,
395 LOG_QUOTE_URL,
396 LOG_QUOTE_RAW
397};
398
399struct _logformat_token
400{
401 logformat_bcode_t type;
402 union {
403 char *string;
404
405 struct {
406 char *header;
407 char *element;
408 char separator;
409 }
410
411 header;
412 char *timespec;
413 } data;
414 unsigned char width;
415 unsigned char precision;
416
d394051a 417 enum log_quote quote;
7684c4b1 418
419unsigned int left:
420 1;
421
422unsigned int space:
423 1;
424
425unsigned int zero:
426 1;
427 int divisor;
428 logformat_token *next; /* todo: move from linked list to array */
429};
430
431struct logformat_token_table_entry
432{
433 const char *config;
434 logformat_bcode_t token_type;
435 int options;
436};
437
438struct logformat_token_table_entry logformat_token_table[] =
439 {
440
441 {">a", LFT_CLIENT_IP_ADDRESS},
442
44eb213b 443 { ">p", LFT_CLIENT_PORT},
7684c4b1 444 {">A", LFT_CLIENT_FQDN},
445
446 /*{ "<a", LFT_SERVER_IP_ADDRESS }, */
447 /*{ "<p", LFT_SERVER_PORT }, */
448 {"<A", LFT_SERVER_IP_OR_PEER_NAME},
449
6db223d9 450 /* {"oa", LFT_OUTGOING_IP}, */
451 /* {"ot", LFT_OUTGOING_TOS}, */
452
7684c4b1 453 {"la", LFT_LOCAL_IP},
454 {"lp", LFT_LOCAL_PORT},
455 /*{ "lA", LFT_LOCAL_NAME }, */
456
457 {"ts", LFT_TIME_SECONDS_SINCE_EPOCH},
458 {"tu", LFT_TIME_SUBSECOND},
459 {"tl", LFT_TIME_LOCALTIME},
460 {"tg", LFT_TIME_GMT},
461 {"tr", LFT_TIME_TO_HANDLE_REQUEST},
462
463 {">h", LFT_REQUEST_HEADER},
1e7a7497 464 {">h", LFT_REQUEST_ALL_HEADERS},
7684c4b1 465 {"<h", LFT_REPLY_HEADER},
1e7a7497 466 {"<h", LFT_REPLY_ALL_HEADERS},
7684c4b1 467
468 {"un", LFT_USER_NAME},
469 {"ul", LFT_USER_LOGIN},
470 /*{ "ur", LFT_USER_REALM }, */
471 /*{ "us", LFT_USER_SCHEME }, */
472 {"ui", LFT_USER_IDENT},
5407e452 473 {"ue", LFT_USER_EXTERNAL},
7684c4b1 474
475 {"Hs", LFT_HTTP_CODE},
476 /*{ "Ht", LFT_HTTP_STATUS }, */
477
478 {"Ss", LFT_SQUID_STATUS},
479 /*{ "Se", LFT_SQUID_ERROR }, */
480 {"Sh", LFT_SQUID_HIERARCHY},
481
482 {"mt", LFT_MIME_TYPE},
483
484 {"rm", LFT_REQUEST_METHOD},
485 {"ru", LFT_REQUEST_URI}, /* doesn't include the query-string */
fef92cc1 486 {"rp", LFT_REQUEST_URLPATH}, /* doesn't include the host */
7684c4b1 487 /* { "rq", LFT_REQUEST_QUERY }, * / / * the query-string, INCLUDING the leading ? */
488 {">v", LFT_REQUEST_VERSION},
489 {"rv", LFT_REQUEST_VERSION},
490
491 /*{ ">st", LFT_REQUEST_SIZE_TOTAL }, */
492 /*{ ">sl", LFT_REQUEST_SIZE_LINE }, * / / * the request line "GET ... " */
493 /*{ ">sh", LFT_REQUEST_SIZE_HEADERS }, */
494 /*{ ">sb", LFT_REQUEST_SIZE_BODY }, */
495 /*{ ">sB", LFT_REQUEST_SIZE_BODY_NO_TE }, */
496
497 {"<st", LFT_REPLY_SIZE_TOTAL},
0976f8db 498 {"<sH", LFT_REPLY_HIGHOFFSET},
499 {"<sS", LFT_REPLY_OBJECTSIZE},
7684c4b1 500 /*{ "<sl", LFT_REPLY_SIZE_LINE }, * / / * the reply line (protocol, code, text) */
501 /*{ "<sh", LFT_REPLY_SIZE_HEADERS }, */
502 /*{ "<sb", LFT_REPLY_SIZE_BODY }, */
503 /*{ "<sB", LFT_REPLY_SIZE_BODY_NO_TE }, */
504
4a972fa2 505 {"et", LFT_TAG},
506 {"ea", LFT_EXT_LOG},
507
7684c4b1 508 {"%", LFT_PERCENT},
509
510 {NULL, LFT_NONE} /* this must be last */
511 };
512
513static void
514accessLogCustom(AccessLogEntry * al, customlog * log)
515{
516 logformat *lf;
517 Logfile *logfile;
518 logformat_token *fmt;
032785bf 519 static MemBuf mb;
7684c4b1 520 char tmp[1024];
30abd221 521 String sb;
7684c4b1 522
2fe7eff9 523 mb.reset();
7684c4b1 524
525 lf = log->logFormat;
526 logfile = log->logfile;
527
528 for (fmt = lf->format; fmt != NULL; fmt = fmt->next) { /* for each token */
529 const char *out = NULL;
530 int quote = 0;
531 long int outint = 0;
532 int doint = 0;
533 int dofree = 0;
47f6e231 534 int64_t outoff = 0;
535 int dooff = 0;
7684c4b1 536
537 switch (fmt->type) {
538
539 case LFT_NONE:
540 out = "";
541 break;
542
543 case LFT_STRING:
544 out = fmt->data.string;
545 break;
546
547 case LFT_CLIENT_IP_ADDRESS:
548 out = inet_ntoa(al->cache.caddr);
549 break;
550
551 case LFT_CLIENT_FQDN:
552 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
553
554 if (!out)
555 out = inet_ntoa(al->cache.caddr);
556
557 break;
558
44eb213b 559 case LFT_CLIENT_PORT:
560 if (al->request) {
561 outint = al->request->client_port;
562 doint = 1;
563 }
564 break;
7684c4b1 565
566 /* case LFT_SERVER_IP_ADDRESS: */
567
568 case LFT_SERVER_IP_OR_PEER_NAME:
569 out = al->hier.host;
570
571 break;
572
573 /* case LFT_SERVER_PORT: */
574
575 case LFT_LOCAL_IP:
576 if (al->request)
577 out = inet_ntoa(al->request->my_addr);
578
579 break;
580
581 case LFT_LOCAL_PORT:
582 if (al->request) {
583 outint = al->request->my_port;
584 doint = 1;
585 }
586
587 break;
588
589 case LFT_TIME_SECONDS_SINCE_EPOCH:
590 outint = current_time.tv_sec;
591 doint = 1;
592 break;
593
594 case LFT_TIME_SUBSECOND:
595 outint = current_time.tv_usec / fmt->divisor;
596 doint = 1;
597 break;
598
599
600 case LFT_TIME_LOCALTIME:
601
602 case LFT_TIME_GMT: {
603 const char *spec;
604
605 struct tm *t;
606 spec = fmt->data.timespec;
607
608 if (!spec)
609 spec = "%d/%b/%Y %H:%M:%S";
610
611 if (fmt->type == LFT_TIME_LOCALTIME)
612 t = localtime(&squid_curtime);
613 else
614 t = gmtime(&squid_curtime);
615
616 strftime(tmp, sizeof(tmp), spec, t);
617
618 out = tmp;
619 }
620
621 break;
622
623 case LFT_TIME_TO_HANDLE_REQUEST:
624 outint = al->cache.msec;
625 doint = 1;
626 break;
627
628 case LFT_REQUEST_HEADER:
629
630 if (al->request)
a9925b40 631 sb = al->request->header.getByName(fmt->data.header.header);
7684c4b1 632
30abd221 633 out = sb.buf();
7684c4b1 634
635 quote = 1;
636
637 break;
638
639 case LFT_REPLY_HEADER:
640 if (al->reply)
9e8cf50b 641 sb = al->reply->header.getByName(fmt->data.header.header);
7684c4b1 642
30abd221 643 out = sb.buf();
7684c4b1 644
645 quote = 1;
646
647 break;
648
649 case LFT_REQUEST_HEADER_ELEM:
650 if (al->request)
a9925b40 651 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
7684c4b1 652
30abd221 653 out = sb.buf();
7684c4b1 654
655 quote = 1;
656
657 break;
658
659 case LFT_REPLY_HEADER_ELEM:
660 if (al->reply)
9e8cf50b 661 sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
7684c4b1 662
30abd221 663 out = sb.buf();
7684c4b1 664
665 quote = 1;
666
667 break;
668
669 case LFT_REQUEST_ALL_HEADERS:
670 out = al->headers.request;
671
672 quote = 1;
673
674 break;
675
676 case LFT_REPLY_ALL_HEADERS:
677 out = al->headers.reply;
678
679 quote = 1;
680
681 break;
682
683 case LFT_USER_NAME:
4a972fa2 684 out = accessLogFormatName(al->cache.authuser);
685
686 if (!out)
687 out = accessLogFormatName(al->cache.extuser);
688
689#if USE_SSL
690
691 if (!out)
692 out = accessLogFormatName(al->cache.ssluser);
693
694#endif
695
696 if (!out)
697 out = accessLogFormatName(al->cache.rfc931);
7684c4b1 698
699 dofree = 1;
700
701 break;
702
703 case LFT_USER_LOGIN:
704 out = accessLogFormatName(al->cache.authuser);
705
706 dofree = 1;
707
708 break;
709
710 case LFT_USER_IDENT:
711 out = accessLogFormatName(al->cache.rfc931);
712
713 dofree = 1;
714
715 break;
716
4a972fa2 717 case LFT_USER_EXTERNAL:
718 out = accessLogFormatName(al->cache.extuser);
719
720 dofree = 1;
721
722 break;
723
7684c4b1 724 /* case LFT_USER_REALM: */
725 /* case LFT_USER_SCHEME: */
726
727 case LFT_HTTP_CODE:
728 outint = al->http.code;
729
730 doint = 1;
731
732 break;
733
734 /* case LFT_HTTP_STATUS:
735 * out = statusline->text;
736 * quote = 1;
737 * break;
738 */
739
740 case LFT_SQUID_STATUS:
741 out = log_tags[al->cache.code];
742
743 break;
744
745 /* case LFT_SQUID_ERROR: */
746
747 case LFT_SQUID_HIERARCHY:
748 if (al->hier.ping.timedout)
2fe7eff9 749 mb.append("TIMEOUT_", 8);
7684c4b1 750
751 out = hier_strings[al->hier.code];
752
753 break;
754
755 case LFT_MIME_TYPE:
756 out = al->http.content_type;
757
758 break;
759
760 case LFT_REQUEST_METHOD:
761 out = al->_private.method_str;
762
763 break;
764
765 case LFT_REQUEST_URI:
766 out = al->url;
767
768 break;
769
fef92cc1 770 case LFT_REQUEST_URLPATH:
771 if (al->request) {
30abd221 772 out = al->request->urlpath.buf();
fef92cc1 773 quote = 1;
774 }
775 break;
776
7684c4b1 777 case LFT_REQUEST_VERSION:
778 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
779
780 out = tmp;
781
782 break;
783
784 /*case LFT_REQUEST_SIZE_TOTAL: */
785 /*case LFT_REQUEST_SIZE_LINE: */
786 /*case LFT_REQUEST_SIZE_HEADERS: */
787 /*case LFT_REQUEST_SIZE_BODY: */
788 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
789
790 case LFT_REPLY_SIZE_TOTAL:
47f6e231 791 outoff = al->cache.size;
7684c4b1 792
47f6e231 793 dooff = 1;
0976f8db 794
795 break;
796
797 case LFT_REPLY_HIGHOFFSET:
798 outint = static_cast<int>(al->cache.highOffset);
799
800 doint = 1;
801
802 break;
803
804 case LFT_REPLY_OBJECTSIZE:
805 outint = static_cast<int>(al->cache.objectSize);
806
807 doint = 1;
7684c4b1 808
809 break;
810
811 /*case LFT_REPLY_SIZE_LINE: */
812 /*case LFT_REPLY_SIZE_HEADERS: */
813 /*case LFT_REPLY_SIZE_BODY: */
814 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
815
4a972fa2 816 case LFT_TAG:
817 if (al->request)
30abd221 818 out = al->request->tag.buf();
4a972fa2 819
820 quote = 1;
821
822 break;
823
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
47f6e231 838 if (dooff) {
ed013b6c 839 snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero ? (int) fmt->width : 0, outoff);
47f6e231 840 out = tmp;
841
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) {
933 switch(*cur) {
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;
62e76326 1272
9bea1d5b 1273 if (Config.onoff.log_fqdn)
62e76326 1274 client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
1275
9bea1d5b 1276 if (client == NULL)
62e76326 1277 client = inet_ntoa(al->cache.caddr);
1278
a7ad6e4e 1279 user = accessLogFormatName(al->cache.authuser);
62e76326 1280
abb929f0 1281 if (!user)
1282 user = accessLogFormatName(al->cache.extuser);
1283
a7ad6e4e 1284#if USE_SSL
62e76326 1285
a7ad6e4e 1286 if (!user)
62e76326 1287 user = accessLogFormatName(al->cache.ssluser);
1288
a7ad6e4e 1289#endif
62e76326 1290
a7ad6e4e 1291 if (!user)
62e76326 1292 user = accessLogFormatName(al->cache.rfc931);
1293
a7ad6e4e 1294 if (user && !*user)
62e76326 1295 safe_free(user);
1296
e293bb20 1297 if (!Config.onoff.log_mime_hdrs) {
47f6e231 1298 logfilePrintf(logfile, "%9ld.%03d %6d %s %s/%03d %"PRId64" %s %s %s %s%s/%s %s",
e293bb20 1299 (long int) current_time.tv_sec,
1300 (int) current_time.tv_usec / 1000,
1301 al->cache.msec,
1302 client,
1303 log_tags[al->cache.code],
1304 al->http.code,
47f6e231 1305 al->cache.size,
e293bb20 1306 al->_private.method_str,
1307 al->url,
1308 user ? user : dash_str,
1309 al->hier.ping.timedout ? "TIMEOUT_" : "",
1310 hier_strings[al->hier.code],
1311 al->hier.host,
1312 al->http.content_type);
1313 } else {
7684c4b1 1314 char *ereq = log_quote(al->headers.request);
1315 char *erep = log_quote(al->headers.reply);
47f6e231 1316 logfilePrintf(logfile, "%9ld.%03d %6d %s %s/%03d %"PRId64" %s %s %s %s%s/%s %s [%s] [%s]",
e293bb20 1317 (long int) current_time.tv_sec,
1318 (int) current_time.tv_usec / 1000,
1319 al->cache.msec,
1320 client,
1321 log_tags[al->cache.code],
1322 al->http.code,
47f6e231 1323 al->cache.size,
e293bb20 1324 al->_private.method_str,
1325 al->url,
1326 user ? user : dash_str,
1327 al->hier.ping.timedout ? "TIMEOUT_" : "",
1328 hier_strings[al->hier.code],
1329 al->hier.host,
1330 al->http.content_type,
1331 ereq,
1332 erep);
7684c4b1 1333 safe_free(ereq);
1334 safe_free(erep);
7684c4b1 1335 }
949a9c0d 1336 logfilePrintf(logfile, "\n");
e293bb20 1337 safe_free(user);
f892c2bf 1338}
1339
137ee196 1340static void
7684c4b1 1341accessLogCommon(AccessLogEntry * al, Logfile * logfile)
f892c2bf 1342{
9bea1d5b 1343 const char *client = NULL;
1344 char *user1 = NULL, *user2 = NULL;
62e76326 1345
9bea1d5b 1346 if (Config.onoff.log_fqdn)
62e76326 1347 client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
1348
9bea1d5b 1349 if (client == NULL)
62e76326 1350 client = inet_ntoa(al->cache.caddr);
1351
9bea1d5b 1352 user1 = accessLogFormatName(al->cache.authuser);
62e76326 1353
9bea1d5b 1354 user2 = accessLogFormatName(al->cache.rfc931);
62e76326 1355
47f6e231 1356 logfilePrintf(logfile, "%s %s %s [%s] \"%s %s HTTP/%d.%d\" %d %"PRId64" %s:%s",
62e76326 1357 client,
1358 user2 ? user2 : dash_str,
1359 user1 ? user1 : dash_str,
1360 mkhttpdlogtime(&squid_curtime),
1361 al->_private.method_str,
1362 al->url,
1363 al->http.version.major, al->http.version.minor,
1364 al->http.code,
47f6e231 1365 al->cache.size,
62e76326 1366 log_tags[al->cache.code],
1367 hier_strings[al->hier.code]);
1368
9bea1d5b 1369 safe_free(user1);
62e76326 1370
9bea1d5b 1371 safe_free(user2);
7684c4b1 1372
1373 if (Config.onoff.log_mime_hdrs) {
1374 char *ereq = log_quote(al->headers.request);
1375 char *erep = log_quote(al->headers.reply);
1376 logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
1377 safe_free(ereq);
1378 safe_free(erep);
1379 } else {
1380 logfilePrintf(logfile, "\n");
1381 }
1382
f892c2bf 1383}
1384
1385void
7684c4b1 1386accessLogLog(AccessLogEntry * al, ACLChecklist * checklist)
f892c2bf 1387{
7684c4b1 1388 customlog *log;
1389
9bea1d5b 1390 if (LogfileStatus != LOG_ENABLE)
62e76326 1391 return;
1392
9bea1d5b 1393 if (al->url == NULL)
62e76326 1394 al->url = dash_str;
1395
9bea1d5b 1396 if (!al->http.content_type || *al->http.content_type == '\0')
62e76326 1397 al->http.content_type = dash_str;
1398
9bea1d5b 1399 if (al->icp.opcode)
62e76326 1400 al->_private.method_str = icp_opcode_str[al->icp.opcode];
9bea1d5b 1401 else
62e76326 1402 al->_private.method_str = RequestMethodStr[al->http.method];
1403
9bea1d5b 1404 if (al->hier.host[0] == '\0')
62e76326 1405 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
9bea1d5b 1406
7684c4b1 1407 for (log = Config.Log.accesslogs; log; log = log->next) {
dd9be8c8 1408 if(checklist && log->aclList && !checklist->matchAclListFast(log->aclList))
7684c4b1 1409 continue;
62e76326 1410
7684c4b1 1411 switch (log->type) {
1412
1413 case CLF_AUTO:
1414
1415 if (Config.onoff.common_log)
1416 accessLogCommon(al, log->logfile);
1417 else
1418 accessLogSquid(al, log->logfile);
1419
1420 break;
1421
1422 case CLF_SQUID:
1423 accessLogSquid(al, log->logfile);
1424
1425 break;
1426
1427 case CLF_COMMON:
1428 accessLogCommon(al, log->logfile);
1429
1430 break;
1431
1432 case CLF_CUSTOM:
1433 accessLogCustom(al, log);
1434
1435 break;
1436
1437 case CLF_NONE:
1438 goto last;
1439
1440 default:
1441 fatalf("Unknown log format %d\n", log->type);
1442
1443 break;
1444 }
1445
1446 logfileFlush(log->logfile);
1447
1448 if (!checklist)
1449 break;
f892c2bf 1450 }
62e76326 1451
7684c4b1 1452last:
1453 (void)0; /* NULL statement for label */
1454
e66d7923 1455#if MULTICAST_MISS_STREAM
62e76326 1456
9bea1d5b 1457 if (al->cache.code != LOG_TCP_MISS)
62e76326 1458 (void) 0;
9bea1d5b 1459 else if (al->http.method != METHOD_GET)
62e76326 1460 (void) 0;
9bea1d5b 1461 else if (mcast_miss_fd < 0)
62e76326 1462 (void) 0;
9bea1d5b 1463 else {
62e76326 1464 unsigned int ibuf[365];
1465 size_t isize;
1466 xstrncpy((char *) ibuf, al->url, 364 * sizeof(int));
1467 isize = ((strlen(al->url) + 8) / 8) * 2;
1468
1469 if (isize > 364)
1470 isize = 364;
1471
1472 mcast_encode((unsigned int *) ibuf, isize,
1473 (const unsigned int *) Config.mcast_miss.encode_key);
1474
1475 comm_udp_sendto(mcast_miss_fd,
1476 &mcast_miss_to, sizeof(mcast_miss_to),
1477 ibuf, isize * sizeof(int));
e66d7923 1478 }
62e76326 1479
e66d7923 1480#endif
f892c2bf 1481}
1482
1483void
9bea1d5b 1484accessLogRotate(void)
f892c2bf 1485{
7684c4b1 1486 customlog *log;
d21f1c54 1487#if FORW_VIA_DB
7684c4b1 1488
9bea1d5b 1489 fvdbClear();
d21f1c54 1490#endif
62e76326 1491
7684c4b1 1492 for (log = Config.Log.accesslogs; log; log = log->next) {
1493 if (log->logfile) {
1494 logfileRotate(log->logfile);
1495 }
1496 }
62e76326 1497
c3609322 1498#if HEADERS_LOG
62e76326 1499
9bea1d5b 1500 logfileRotate(headerslog);
62e76326 1501
c3609322 1502#endif
f892c2bf 1503}
1504
1505void
9bea1d5b 1506accessLogClose(void)
f892c2bf 1507{
7684c4b1 1508 customlog *log;
62e76326 1509
7684c4b1 1510 for (log = Config.Log.accesslogs; log; log = log->next) {
1511 if (log->logfile) {
1512 logfileClose(log->logfile);
1513 log->logfile = NULL;
1514 }
1515 }
62e76326 1516
c3609322 1517#if HEADERS_LOG
62e76326 1518
9bea1d5b 1519 logfileClose(headerslog);
62e76326 1520
9bea1d5b 1521 headerslog = NULL;
62e76326 1522
c3609322 1523#endif
f892c2bf 1524}
1525
b24880fe 1526HierarchyLogEntry::HierarchyLogEntry() :
1527 code(HIER_NONE),
1528 cd_lookup(LOOKUP_NONE),
1529 n_choices(0),
1530 n_ichoices(0)
1531{
1532 memset(host, '\0', SQUIDHOSTNAMELEN);
1533 memset(cd_host, '\0', SQUIDHOSTNAMELEN);
1534
1535 peer_select_start.tv_sec =0;
1536 peer_select_start.tv_usec =0;
1537
1538 store_complete_stop.tv_sec =0;
1539 store_complete_stop.tv_usec =0;
1540}
1541
f892c2bf 1542void
9bea1d5b 1543hierarchyNote(HierarchyLogEntry * hl,
62e76326 1544 hier_code code,
1545 const char *cache_peer)
f892c2bf 1546{
9bea1d5b 1547 assert(hl != NULL);
1548 hl->code = code;
1549 xstrncpy(hl->host, cache_peer, SQUIDHOSTNAMELEN);
f892c2bf 1550}
7a2f978b 1551
1552void
9bea1d5b 1553accessLogInit(void)
7a2f978b 1554{
7684c4b1 1555 customlog *log;
9bea1d5b 1556 assert(sizeof(log_tags) == (LOG_TYPE_MAX + 1) * sizeof(char *));
62e76326 1557
7684c4b1 1558 for (log = Config.Log.accesslogs; log; log = log->next) {
ed22613b 1559 if (log->type == CLF_NONE)
7684c4b1 1560 continue;
62e76326 1561
7684c4b1 1562 log->logfile = logfileOpen(log->filename, MAX_URL << 1, 1);
62e76326 1563
7684c4b1 1564 LogfileStatus = LOG_ENABLE;
1565 }
62e76326 1566
c3609322 1567#if HEADERS_LOG
62e76326 1568
9bea1d5b 1569 headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
62e76326 1570
9bea1d5b 1571 assert(NULL != headerslog);
62e76326 1572
d21f1c54 1573#endif
e66d7923 1574#if MULTICAST_MISS_STREAM
62e76326 1575
9bea1d5b 1576 if (Config.mcast_miss.addr.s_addr != no_addr.s_addr) {
62e76326 1577 memset(&mcast_miss_to, '\0', sizeof(mcast_miss_to));
1578 mcast_miss_to.sin_family = AF_INET;
1579 mcast_miss_to.sin_port = htons(Config.mcast_miss.port);
1580 mcast_miss_to.sin_addr.s_addr = Config.mcast_miss.addr.s_addr;
1581 mcast_miss_fd = comm_open(SOCK_DGRAM,
bdb741f4 1582 IPPROTO_UDP,
62e76326 1583 Config.Addrs.udp_incoming,
1584 Config.mcast_miss.port,
1585 COMM_NONBLOCKING,
1586 "Multicast Miss Stream");
1587
1588 if (mcast_miss_fd < 0)
1589 fatal("Cannot open Multicast Miss Stream Socket");
1590
bf8fe701 1591 debugs(46, 1, "Multicast Miss Stream Socket opened on FD " << mcast_miss_fd);
62e76326 1592
1593 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
1594
1595 if (strlen(Config.mcast_miss.encode_key) < 16)
1596 fatal("mcast_encode_key is too short, must be 16 characters");
e66d7923 1597 }
62e76326 1598
62ee09ca 1599#endif
1600#if FORW_VIA_DB
1601
1602 fvdbInit();
1603
1604#endif
1605}
1606
1607void
1608accessLogRegisterWithCacheManager(CacheManager & manager)
1609{
1610#if FORW_VIA_DB
1611
1612 fvdbRegisterWithCacheManager(manager);
1613
e66d7923 1614#endif
7a2f978b 1615}
c6e7cab0 1616
1617const char *
9bea1d5b 1618accessLogTime(time_t t)
c6e7cab0 1619{
62e76326 1620
9bea1d5b 1621 struct tm *tm;
1622 static char buf[128];
1623 static time_t last_t = 0;
62e76326 1624
9bea1d5b 1625 if (t != last_t) {
62e76326 1626 tm = localtime(&t);
1627 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
1628 last_t = t;
c6e7cab0 1629 }
62e76326 1630
9bea1d5b 1631 return buf;
c6e7cab0 1632}
d21f1c54 1633
1634
1635#if FORW_VIA_DB
1afe05c5 1636
d21f1c54 1637static void
9bea1d5b 1638fvdbInit(void)
d21f1c54 1639{
30abd221 1640 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
1641 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
62ee09ca 1642}
1643
1644static void
1645fvdbRegisterWithCacheManager(CacheManager & manager)
1646{
1647 manager.registerAction("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
1648 manager.registerAction("forw_headers", "X-Forwarded-For Request Headers",
1649 fvdbDumpForw, 0, 1);
d21f1c54 1650}
1651
1652static void
9bea1d5b 1653fvdbCount(hash_table * hash, const char *key)
1afe05c5 1654{
9bea1d5b 1655 fvdb_entry *fv;
62e76326 1656
9bea1d5b 1657 if (NULL == hash)
62e76326 1658 return;
1659
8021b4a6 1660 fv = (fvdb_entry *)hash_lookup(hash, key);
62e76326 1661
9bea1d5b 1662 if (NULL == fv) {
62e76326 1663 fv = static_cast <fvdb_entry *>(xcalloc(1, sizeof(fvdb_entry)));
1664 fv->hash.key = xstrdup(key);
1665 hash_join(hash, &fv->hash);
1afe05c5 1666 }
62e76326 1667
9bea1d5b 1668 fv->n++;
d21f1c54 1669}
1670
1671void
9bea1d5b 1672fvdbCountVia(const char *key)
d21f1c54 1673{
9bea1d5b 1674 fvdbCount(via_table, key);
d21f1c54 1675}
1676
1677void
9bea1d5b 1678fvdbCountForw(const char *key)
d21f1c54 1679{
9bea1d5b 1680 fvdbCount(forw_table, key);
d21f1c54 1681}
1682
1afe05c5 1683static void
9bea1d5b 1684fvdbDumpTable(StoreEntry * e, hash_table * hash)
d21f1c54 1685{
9bea1d5b 1686 hash_link *h;
1687 fvdb_entry *fv;
62e76326 1688
9bea1d5b 1689 if (hash == NULL)
62e76326 1690 return;
1691
9bea1d5b 1692 hash_first(hash);
62e76326 1693
9bea1d5b 1694 while ((h = hash_next(hash))) {
62e76326 1695 fv = (fvdb_entry *) h;
1696 storeAppendPrintf(e, "%9d %s\n", fv->n, hashKeyStr(&fv->hash));
1afe05c5 1697 }
d21f1c54 1698}
1699
1700static void
9bea1d5b 1701fvdbDumpVia(StoreEntry * e)
d21f1c54 1702{
9bea1d5b 1703 fvdbDumpTable(e, via_table);
d21f1c54 1704}
1afe05c5 1705
d21f1c54 1706static void
9bea1d5b 1707fvdbDumpForw(StoreEntry * e)
d21f1c54 1708{
9bea1d5b 1709 fvdbDumpTable(e, forw_table);
d21f1c54 1710}
1711
1712static
b644367b 1713void
9bea1d5b 1714fvdbFreeEntry(void *data)
d21f1c54 1715{
8021b4a6 1716 fvdb_entry *fv = static_cast <fvdb_entry *>(data);
9bea1d5b 1717 xfree(fv->hash.key);
1718 xfree(fv);
d21f1c54 1719}
1720
1721static void
9bea1d5b 1722fvdbClear(void)
d21f1c54 1723{
9bea1d5b 1724 hashFreeItems(via_table, fvdbFreeEntry);
1725 hashFreeMemory(via_table);
30abd221 1726 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
9bea1d5b 1727 hashFreeItems(forw_table, fvdbFreeEntry);
1728 hashFreeMemory(forw_table);
30abd221 1729 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 1730}
1731
1afe05c5 1732#endif
e66d7923 1733
1734#if MULTICAST_MISS_STREAM
1735/*
1736 * From http://www.io.com/~paulhart/game/algorithms/tea.html
1737 *
1738 * size of 'ibuf' must be a multiple of 2.
1739 * size of 'key' must be 4.
1740 * 'ibuf' is modified in place, encrypted data is written in
1741 * network byte order.
1742 */
1743static void
9bea1d5b 1744mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
e66d7923 1745{
9bea1d5b 1746 unsigned int y;
1747 unsigned int z;
1748 unsigned int sum;
1749 const unsigned int delta = 0x9e3779b9;
1750 unsigned int n = 32;
1751 const unsigned int k0 = htonl(key[0]);
1752 const unsigned int k1 = htonl(key[1]);
1753 const unsigned int k2 = htonl(key[2]);
1754 const unsigned int k3 = htonl(key[3]);
1755 int i;
62e76326 1756
9bea1d5b 1757 for (i = 0; i < isize; i += 2) {
62e76326 1758 y = htonl(ibuf[i]);
1759 z = htonl(ibuf[i + 1]);
1760 sum = 0;
1761
1762 for (n = 32; n; n--) {
1763 sum += delta;
1764 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
1765 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
1766 }
1767
1768 ibuf[i] = htonl(y);
1769 ibuf[i + 1] = htonl(z);
e66d7923 1770 }
1771}
1772
1773#endif
c3609322 1774
1775#if HEADERS_LOG
1776void
985c86bc 1777headersLog(int cs, int pq, method_t method, void *data)
c3609322 1778{
9bea1d5b 1779 HttpReply *rep;
190154cf 1780 HttpRequest *req;
9bea1d5b 1781 unsigned short magic = 0;
1782 unsigned char M = (unsigned char) m;
1783 unsigned short S;
1784 char *hmask;
1785 int ccmask = 0;
62e76326 1786
9bea1d5b 1787 if (0 == pq) {
62e76326 1788 /* reply */
1789 rep = data;
1790 req = NULL;
1791 magic = 0x0050;
1792 hmask = rep->header.mask;
1793
1794 if (rep->cache_control)
1795 ccmask = rep->cache_control->mask;
9bea1d5b 1796 } else {
62e76326 1797 /* request */
1798 req = data;
1799 rep = NULL;
1800 magic = 0x0051;
1801 hmask = req->header.mask;
1802
1803 if (req->cache_control)
1804 ccmask = req->cache_control->mask;
c3609322 1805 }
62e76326 1806
9bea1d5b 1807 if (0 == cs) {
62e76326 1808 /* client */
1809 magic |= 0x4300;
9bea1d5b 1810 } else {
62e76326 1811 /* server */
1812 magic |= 0x5300;
c3609322 1813 }
62e76326 1814
9bea1d5b 1815 magic = htons(magic);
1816 ccmask = htonl(ccmask);
62e76326 1817
9bea1d5b 1818 if (0 == pq)
62e76326 1819 S = (unsigned short) rep->sline.status;
9bea1d5b 1820 else
62e76326 1821 S = (unsigned short) HTTP_STATUS_NONE;
1822
9bea1d5b 1823 logfileWrite(headerslog, &magic, sizeof(magic));
62e76326 1824
9bea1d5b 1825 logfileWrite(headerslog, &M, sizeof(M));
62e76326 1826
9bea1d5b 1827 logfileWrite(headerslog, &S, sizeof(S));
62e76326 1828
9bea1d5b 1829 logfileWrite(headerslog, hmask, sizeof(HttpHeaderMask));
62e76326 1830
9bea1d5b 1831 logfileWrite(headerslog, &ccmask, sizeof(int));
62e76326 1832
9bea1d5b 1833 logfileFlush(headerslog);
c3609322 1834}
1835
1836#endif
c8be6d7b 1837
1838void
1839accessLogFreeMemory(AccessLogEntry * aLogEntry)
1840{
1841 safe_free(aLogEntry->headers.request);
1842 safe_free(aLogEntry->headers.reply);
1843 safe_free(aLogEntry->cache.authuser);
7684c4b1 1844
90a8964c 1845 HTTPMSGUNLOCK(aLogEntry->reply);
6dd9f4bd 1846 HTTPMSGUNLOCK(aLogEntry->request);
c8be6d7b 1847}
1848
1849int
1850logTypeIsATcpHit(log_type code)
1851{
1852 /* this should be a bitmap for better optimization */
62e76326 1853
c8be6d7b 1854 if (code == LOG_TCP_HIT)
62e76326 1855 return 1;
1856
c8be6d7b 1857 if (code == LOG_TCP_IMS_HIT)
62e76326 1858 return 1;
1859
1d7ab0f4 1860 if (code == LOG_TCP_REFRESH_FAIL)
62e76326 1861 return 1;
1862
1d7ab0f4 1863 if (code == LOG_TCP_REFRESH_UNMODIFIED)
62e76326 1864 return 1;
1865
c8be6d7b 1866 if (code == LOG_TCP_NEGATIVE_HIT)
62e76326 1867 return 1;
1868
c8be6d7b 1869 if (code == LOG_TCP_MEM_HIT)
62e76326 1870 return 1;
1871
c8be6d7b 1872 if (code == LOG_TCP_OFFLINE_HIT)
62e76326 1873 return 1;
1874
c8be6d7b 1875 return 0;
1876}
e6ccf245 1877