]> git.ipfire.org Git - thirdparty/squid.git/blame - src/access_log.cc
At the sprint the issue about our current 10ms main loop timeout came
[thirdparty/squid.git] / src / access_log.cc
CommitLineData
f892c2bf 1
2/*
60745f24 3 * $Id: access_log.cc,v 1.130 2008/01/20 08:54:28 amosjeffries 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:
cc192b50 548 if (!out) {
549 out = al->cache.caddr.NtoA(tmp,1024);
550 }
7684c4b1 551 break;
552
553 case LFT_CLIENT_FQDN:
554 out = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
cc192b50 555 if (!out) {
556 out = al->cache.caddr.NtoA(tmp,1024);
557 }
7684c4b1 558
559 break;
560
44eb213b 561 case LFT_CLIENT_PORT:
562 if (al->request) {
cc192b50 563 outint = al->request->client_addr.GetPort();
44eb213b 564 doint = 1;
565 }
566 break;
7684c4b1 567
568 /* case LFT_SERVER_IP_ADDRESS: */
569
570 case LFT_SERVER_IP_OR_PEER_NAME:
571 out = al->hier.host;
572
573 break;
574
575 /* case LFT_SERVER_PORT: */
576
577 case LFT_LOCAL_IP:
cc192b50 578 if (al->request) {
579 out = al->request->my_addr.NtoA(tmp,1024);
580 }
7684c4b1 581
582 break;
583
584 case LFT_LOCAL_PORT:
585 if (al->request) {
cc192b50 586 outint = al->request->my_addr.GetPort();
7684c4b1 587 doint = 1;
588 }
589
590 break;
591
592 case LFT_TIME_SECONDS_SINCE_EPOCH:
00a69875
HN
593 // some platforms store time in 32-bit, some 64-bit...
594 outoff = static_cast<int64_t>(current_time.tv_sec);
595 dooff = 1;
7684c4b1 596 break;
597
598 case LFT_TIME_SUBSECOND:
599 outint = current_time.tv_usec / fmt->divisor;
600 doint = 1;
601 break;
602
603
604 case LFT_TIME_LOCALTIME:
605
606 case LFT_TIME_GMT: {
607 const char *spec;
608
609 struct tm *t;
610 spec = fmt->data.timespec;
611
612 if (!spec)
613 spec = "%d/%b/%Y %H:%M:%S";
614
615 if (fmt->type == LFT_TIME_LOCALTIME)
616 t = localtime(&squid_curtime);
617 else
618 t = gmtime(&squid_curtime);
619
620 strftime(tmp, sizeof(tmp), spec, t);
621
622 out = tmp;
623 }
624
625 break;
626
627 case LFT_TIME_TO_HANDLE_REQUEST:
628 outint = al->cache.msec;
629 doint = 1;
630 break;
631
632 case LFT_REQUEST_HEADER:
633
634 if (al->request)
a9925b40 635 sb = al->request->header.getByName(fmt->data.header.header);
7684c4b1 636
30abd221 637 out = sb.buf();
7684c4b1 638
639 quote = 1;
640
641 break;
642
643 case LFT_REPLY_HEADER:
644 if (al->reply)
9e8cf50b 645 sb = al->reply->header.getByName(fmt->data.header.header);
7684c4b1 646
30abd221 647 out = sb.buf();
7684c4b1 648
649 quote = 1;
650
651 break;
652
653 case LFT_REQUEST_HEADER_ELEM:
654 if (al->request)
a9925b40 655 sb = al->request->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
7684c4b1 656
30abd221 657 out = sb.buf();
7684c4b1 658
659 quote = 1;
660
661 break;
662
663 case LFT_REPLY_HEADER_ELEM:
664 if (al->reply)
9e8cf50b 665 sb = al->reply->header.getByNameListMember(fmt->data.header.header, fmt->data.header.element, fmt->data.header.separator);
7684c4b1 666
30abd221 667 out = sb.buf();
7684c4b1 668
669 quote = 1;
670
671 break;
672
673 case LFT_REQUEST_ALL_HEADERS:
674 out = al->headers.request;
675
676 quote = 1;
677
678 break;
679
680 case LFT_REPLY_ALL_HEADERS:
681 out = al->headers.reply;
682
683 quote = 1;
684
685 break;
686
687 case LFT_USER_NAME:
4a972fa2 688 out = accessLogFormatName(al->cache.authuser);
689
690 if (!out)
691 out = accessLogFormatName(al->cache.extuser);
692
693#if USE_SSL
694
695 if (!out)
696 out = accessLogFormatName(al->cache.ssluser);
697
698#endif
699
700 if (!out)
701 out = accessLogFormatName(al->cache.rfc931);
7684c4b1 702
703 dofree = 1;
704
705 break;
706
707 case LFT_USER_LOGIN:
708 out = accessLogFormatName(al->cache.authuser);
709
710 dofree = 1;
711
712 break;
713
714 case LFT_USER_IDENT:
715 out = accessLogFormatName(al->cache.rfc931);
716
717 dofree = 1;
718
719 break;
720
4a972fa2 721 case LFT_USER_EXTERNAL:
722 out = accessLogFormatName(al->cache.extuser);
723
724 dofree = 1;
725
726 break;
727
7684c4b1 728 /* case LFT_USER_REALM: */
729 /* case LFT_USER_SCHEME: */
730
731 case LFT_HTTP_CODE:
732 outint = al->http.code;
733
734 doint = 1;
735
736 break;
737
738 /* case LFT_HTTP_STATUS:
739 * out = statusline->text;
740 * quote = 1;
741 * break;
742 */
743
744 case LFT_SQUID_STATUS:
745 out = log_tags[al->cache.code];
746
747 break;
748
749 /* case LFT_SQUID_ERROR: */
750
751 case LFT_SQUID_HIERARCHY:
752 if (al->hier.ping.timedout)
2fe7eff9 753 mb.append("TIMEOUT_", 8);
7684c4b1 754
755 out = hier_strings[al->hier.code];
756
757 break;
758
759 case LFT_MIME_TYPE:
760 out = al->http.content_type;
761
762 break;
763
764 case LFT_REQUEST_METHOD:
765 out = al->_private.method_str;
766
767 break;
768
769 case LFT_REQUEST_URI:
770 out = al->url;
771
772 break;
773
fef92cc1 774 case LFT_REQUEST_URLPATH:
775 if (al->request) {
30abd221 776 out = al->request->urlpath.buf();
fef92cc1 777 quote = 1;
778 }
779 break;
780
7684c4b1 781 case LFT_REQUEST_VERSION:
782 snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->http.version.major, (int) al->http.version.minor);
783
784 out = tmp;
785
786 break;
787
788 /*case LFT_REQUEST_SIZE_TOTAL: */
789 /*case LFT_REQUEST_SIZE_LINE: */
790 /*case LFT_REQUEST_SIZE_HEADERS: */
791 /*case LFT_REQUEST_SIZE_BODY: */
792 /*case LFT_REQUEST_SIZE_BODY_NO_TE: */
793
794 case LFT_REPLY_SIZE_TOTAL:
47f6e231 795 outoff = al->cache.size;
7684c4b1 796
47f6e231 797 dooff = 1;
0976f8db 798
799 break;
800
801 case LFT_REPLY_HIGHOFFSET:
00a69875 802 outoff = al->cache.highOffset;
0976f8db 803
00a69875 804 dooff = 1;
0976f8db 805
806 break;
807
808 case LFT_REPLY_OBJECTSIZE:
00a69875 809 outoff = al->cache.objectSize;
0976f8db 810
00a69875 811 dooff = 1;
7684c4b1 812
813 break;
814
815 /*case LFT_REPLY_SIZE_LINE: */
816 /*case LFT_REPLY_SIZE_HEADERS: */
817 /*case LFT_REPLY_SIZE_BODY: */
818 /*case LFT_REPLY_SIZE_BODY_NO_TE: */
819
4a972fa2 820 case LFT_TAG:
821 if (al->request)
30abd221 822 out = al->request->tag.buf();
4a972fa2 823
824 quote = 1;
825
826 break;
827
828 case LFT_EXT_LOG:
829 if (al->request)
30abd221 830 out = al->request->extacl_log.buf();
4a972fa2 831
832 quote = 1;
833
834 break;
835
7684c4b1 836 case LFT_PERCENT:
837 out = "%";
838
839 break;
840 }
841
47f6e231 842 if (dooff) {
ed013b6c 843 snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero ? (int) fmt->width : 0, outoff);
47f6e231 844 out = tmp;
845
846 } else if (doint) {
7684c4b1 847 snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero ? (int) fmt->width : 0, outint);
848 out = tmp;
849 }
850
851 if (out && *out) {
f8144161 852 if (quote || fmt->quote != LOG_QUOTE_NONE) {
7684c4b1 853 char *newout = NULL;
854 int newfree = 0;
855
856 switch (fmt->quote) {
857
858 case LOG_QUOTE_NONE:
859 newout = rfc1738_escape_unescaped(out);
7684c4b1 860 break;
861
862 case LOG_QUOTE_QUOTES:
863 newout = log_quoted_string(out);
864 newfree = 1;
865 break;
866
867 case LOG_QUOTE_BRAKETS:
868 newout = log_quote(out);
869 newfree = 1;
870 break;
871
872 case LOG_QUOTE_URL:
873 newout = rfc1738_escape(out);
874 break;
875
876 case LOG_QUOTE_RAW:
877 break;
878 }
879
880 if (newout) {
881 if (dofree)
882 safe_free(out);
883
884 out = newout;
885
886 dofree = newfree;
887 }
888 }
889
890 if (fmt->width) {
891 if (fmt->left)
2fe7eff9 892 mb.Printf("%-*s", (int) fmt->width, out);
7684c4b1 893 else
2fe7eff9 894 mb.Printf("%*s", (int) fmt->width, out);
7684c4b1 895 } else
2fe7eff9 896 mb.append(out, strlen(out));
7684c4b1 897 } else {
2fe7eff9 898 mb.append("-", 1);
7684c4b1 899 }
900
901 if (fmt->space)
2fe7eff9 902 mb.append(" ", 1);
7684c4b1 903
30abd221 904 sb.clean();
7684c4b1 905
906 if (dofree)
907 safe_free(out);
908 }
909
910 logfilePrintf(logfile, "%s\n", mb.buf);
911}
912
913/* parses a single token. Returns the token length in characters,
914 * and fills in the lt item with the token information.
915 * def is for sure null-terminated
916 */
917static int
fa38076e 918accessLogGetNewLogFormatToken(logformat_token * lt, char *def, enum log_quote *quote)
7684c4b1 919{
920 char *cur = def;
921
922 struct logformat_token_table_entry *lte;
923 int l;
924
925 memset(lt, 0, sizeof(*lt));
926 l = strcspn(cur, "%");
927
928 if (l > 0) {
929 char *cp;
930 /* it's a string for sure, until \0 or the next % */
931 cp = (char *)xmalloc(l + 1);
932 xstrncpy(cp, cur, l + 1);
933 lt->type = LFT_STRING;
934 lt->data.string = cp;
fa38076e 935
936 while (l > 0) {
937 switch(*cur) {
938
939 case '"':
940
941 if (*quote == LOG_QUOTE_NONE)
942 *quote = LOG_QUOTE_QUOTES;
943 else if (*quote == LOG_QUOTE_QUOTES)
944 *quote = LOG_QUOTE_NONE;
945
946 break;
947
948 case '[':
949 if (*quote == LOG_QUOTE_NONE)
950 *quote = LOG_QUOTE_BRAKETS;
951
952 break;
953
954 case ']':
955 if (*quote == LOG_QUOTE_BRAKETS)
956 *quote = LOG_QUOTE_NONE;
957
958 break;
959 }
960
961 cur++;
962 l--;
963 }
964
7684c4b1 965 goto done;
966 }
967
968 if (!*cur)
969 goto done;
970
971 cur++;
972
973 switch (*cur) {
974
975 case '"':
976 lt->quote = LOG_QUOTE_QUOTES;
977 cur++;
978 break;
979
980 case '\'':
981 lt->quote = LOG_QUOTE_RAW;
982 cur++;
983 break;
984
985 case '[':
986 lt->quote = LOG_QUOTE_BRAKETS;
987 cur++;
988 break;
989
990 case '#':
991 lt->quote = LOG_QUOTE_URL;
992 cur++;
993 break;
fa38076e 994
995 default:
996 lt->quote = *quote;
997 break;
7684c4b1 998 }
999
1000 if (*cur == '-') {
1001 lt->left = 1;
1002 cur++;
1003 }
1004
1005 if (*cur == '0') {
1006 lt->zero = 1;
1007 cur++;
1008 }
1009
ec451a0f 1010 if (xisdigit(*cur))
7684c4b1 1011 lt->width = strtol(cur, &cur, 10);
1012
1013 if (*cur == '.')
1014 lt->precision = strtol(cur + 1, &cur, 10);
1015
1016 if (*cur == '{') {
1017 char *cp;
1018 cur++;
1019 l = strcspn(cur, "}");
1020 cp = (char *)xmalloc(l + 1);
1021 xstrncpy(cp, cur, l + 1);
1022 lt->data.string = cp;
1023 cur += l;
1024
1025 if (*cur == '}')
1026 cur++;
1027 }
1028
1029 lt->type = LFT_NONE;
1030
1031 for (lte = logformat_token_table; lte->config != NULL; lte++) {
1032 if (strncmp(lte->config, cur, strlen(lte->config)) == 0) {
1033 lt->type = lte->token_type;
1034 cur += strlen(lte->config);
1035 break;
1036 }
1037 }
1038
1039 if (lt->type == LFT_NONE) {
1040 fatalf("Can't parse configuration token: '%s'\n",
1041 def);
1042 }
1043
7684c4b1 1044 if (*cur == ' ') {
1045 lt->space = 1;
1046 cur++;
1047 }
1048
1049done:
1050
1051 switch (lt->type) {
1052
1053 case LFT_REQUEST_HEADER:
1054
1055 case LFT_REPLY_HEADER:
1056
1057 if (lt->data.string) {
1058 char *header = lt->data.string;
1059 char *cp = strchr(header, ':');
1060
1061 if (cp) {
1062 *cp++ = '\0';
1063
1064 if (*cp == ',' || *cp == ';' || *cp == ':')
1065 lt->data.header.separator = *cp++;
1066 else
1067 lt->data.header.separator = ',';
1068
1069 lt->data.header.element = cp;
1070
1071 lt->type = (lt->type == LFT_REQUEST_HEADER) ?
1072 LFT_REQUEST_HEADER_ELEM :
1073 LFT_REPLY_HEADER_ELEM;
1074 }
1075
1076 lt->data.header.header = header;
1077 } else {
1078 lt->type = (lt->type == LFT_REQUEST_HEADER) ?
1079 LFT_REQUEST_ALL_HEADERS :
1080 LFT_REPLY_ALL_HEADERS;
1081 Config.onoff.log_mime_hdrs = 1;
1082 }
1083
1084 break;
1085
1086 case LFT_CLIENT_FQDN:
1087 Config.onoff.log_fqdn = 1;
1088 break;
1089
1090 case LFT_TIME_SUBSECOND:
1091 lt->divisor = 1000;
1092
1093 if (lt->precision) {
1094 int i;
1095 lt->divisor = 1000000;
1096
1097 for (i = lt->precision; i > 1; i--)
1098 lt->divisor /= 10;
1099
1100 if (!lt->divisor)
1101 lt->divisor = 0;
1102 }
1103
1104 break;
1105
1106 default:
1107 break;
1108 }
1109
1110 return (cur - def);
1111}
1112
1113int
1114accessLogParseLogFormat(logformat_token ** fmt, char *def)
1115{
1116 char *cur, *eos;
1117 logformat_token *new_lt, *last_lt;
fa38076e 1118 enum log_quote quote = LOG_QUOTE_NONE;
7684c4b1 1119
bf8fe701 1120 debugs(46, 2, "accessLogParseLogFormat: got definition '" << def << "'");
7684c4b1 1121
1122 /* very inefficent parser, but who cares, this needs to be simple */
1123 /* First off, let's tokenize, we'll optimize in a second pass.
1124 * A token can either be a %-prefixed sequence (usually a dynamic
1125 * token but it can be an escaped sequence), or a string. */
1126 cur = def;
1127 eos = def + strlen(def);
1128 *fmt = new_lt = last_lt = (logformat_token *)xmalloc(sizeof(logformat_token));
fa38076e 1129 cur += accessLogGetNewLogFormatToken(new_lt, cur, &quote);
7684c4b1 1130
1131 while (cur < eos) {
1132 new_lt = (logformat_token *)xmalloc(sizeof(logformat_token));
1133 last_lt->next = new_lt;
1134 last_lt = new_lt;
fa38076e 1135 cur += accessLogGetNewLogFormatToken(new_lt, cur, &quote);
7684c4b1 1136 }
1137
1138 return 1;
1139}
1140
1141void
1142accessLogDumpLogFormat(StoreEntry * entry, const char *name, logformat * definitions)
1143{
1144 logformat_token *t;
1145 logformat *format;
1146
1147 struct logformat_token_table_entry *te;
bf8fe701 1148 debugs(46, 0, "accessLogDumpLogFormat called");
7684c4b1 1149
1150 for (format = definitions; format; format = format->next) {
bf8fe701 1151 debugs(46, 0, "Dumping logformat definition for " << format->name);
7684c4b1 1152 storeAppendPrintf(entry, "logformat %s ", format->name);
7684c4b1 1153
3c52bc04 1154 for (t = format->format; t; t = t->next) {
7684c4b1 1155 if (t->type == LFT_STRING)
1156 storeAppendPrintf(entry, "%s", t->data.string);
1157 else {
3c52bc04 1158 char argbuf[256];
1159 char *arg = NULL;
1160 logformat_bcode_t type = t->type;
7684c4b1 1161
3c52bc04 1162 switch (type) {
7684c4b1 1163 /* special cases */
1164
1165 case LFT_STRING:
1166 break;
1167
1168 case LFT_REQUEST_HEADER_ELEM:
1169
1170 case LFT_REPLY_HEADER_ELEM:
1171
1172 if (t->data.header.separator != ',')
3c52bc04 1173 snprintf(argbuf, sizeof(argbuf), "%s:%c%s", t->data.header.header, t->data.header.separator, t->data.header.element);
7684c4b1 1174 else
3c52bc04 1175 snprintf(argbuf, sizeof(argbuf), "%s:%s", t->data.header.header, t->data.header.element);
1176
1177 arg = argbuf;
1178
1179 type = (type == LFT_REQUEST_HEADER_ELEM) ?
1180 LFT_REQUEST_HEADER :
1181 LFT_REPLY_HEADER;
1182
1183 break;
1184
1185 case LFT_REQUEST_ALL_HEADERS:
1186
1187 case LFT_REPLY_ALL_HEADERS:
1188
1189 type = (type == LFT_REQUEST_ALL_HEADERS) ?
1190 LFT_REQUEST_HEADER :
1191 LFT_REPLY_HEADER;
1192
1193 break;
7684c4b1 1194
1195 default:
1196 if (t->data.string)
3c52bc04 1197 arg = t->data.string;
7684c4b1 1198
1199 break;
1200 }
1201
3900307b 1202 entry->append("%", 1);
7684c4b1 1203
1204 switch (t->quote) {
1205
1206 case LOG_QUOTE_QUOTES:
3900307b 1207 entry->append("\"", 1);
7684c4b1 1208 break;
1209
1210 case LOG_QUOTE_BRAKETS:
3900307b 1211 entry->append("[", 1);
7684c4b1 1212 break;
1213
1214 case LOG_QUOTE_URL:
3900307b 1215 entry->append("#", 1);
7684c4b1 1216 break;
1217
1218 case LOG_QUOTE_RAW:
3900307b 1219 entry->append("'", 1);
7684c4b1 1220 break;
1221
1222 case LOG_QUOTE_NONE:
1223 break;
1224 }
1225
1226 if (t->left)
3900307b 1227 entry->append("-", 1);
7684c4b1 1228
3c52bc04 1229 if (t->zero)
3900307b 1230 entry->append("0", 1);
3c52bc04 1231
7684c4b1 1232 if (t->width)
1233 storeAppendPrintf(entry, "%d", (int) t->width);
1234
1235 if (t->precision)
1236 storeAppendPrintf(entry, ".%d", (int) t->precision);
1237
3c52bc04 1238 if (arg)
7684c4b1 1239 storeAppendPrintf(entry, "{%s}", arg);
1240
1241 for (te = logformat_token_table; te->config != NULL; te++) {
1242 if (te->token_type == t->type) {
1243 storeAppendPrintf(entry, "%s", te->config);
1244 break;
1245 }
1246 }
1247
3c52bc04 1248 if (t->space)
3900307b 1249 entry->append(" ", 1);
3c52bc04 1250
7684c4b1 1251 assert(te->config != NULL);
7684c4b1 1252 }
1253 }
1e7a7497 1254
3900307b 1255 entry->append("\n", 1);
7684c4b1 1256 }
3c52bc04 1257
7684c4b1 1258}
1259
1260void
1261accessLogFreeLogFormat(logformat_token ** tokens)
1262{
1263 while (*tokens) {
1264 logformat_token *token = *tokens;
1265 *tokens = token->next;
1266 safe_free(token->data.string);
1267 xfree(token);
1268 }
1269}
1270
137ee196 1271static void
7684c4b1 1272accessLogSquid(AccessLogEntry * al, Logfile * logfile)
f892c2bf 1273{
9bea1d5b 1274 const char *client = NULL;
a7ad6e4e 1275 const char *user = NULL;
cc192b50 1276 char buf[MAX_IPSTRLEN];
62e76326 1277
cc192b50 1278 if (Config.onoff.log_fqdn) {
62e76326 1279 client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
cc192b50 1280 }
62e76326 1281
cc192b50 1282 if (client == NULL) {
1283 client = al->cache.caddr.NtoA(buf,MAX_IPSTRLEN);
1284 }
62e76326 1285
a7ad6e4e 1286 user = accessLogFormatName(al->cache.authuser);
62e76326 1287
abb929f0 1288 if (!user)
1289 user = accessLogFormatName(al->cache.extuser);
1290
a7ad6e4e 1291#if USE_SSL
62e76326 1292
a7ad6e4e 1293 if (!user)
62e76326 1294 user = accessLogFormatName(al->cache.ssluser);
1295
a7ad6e4e 1296#endif
62e76326 1297
a7ad6e4e 1298 if (!user)
62e76326 1299 user = accessLogFormatName(al->cache.rfc931);
1300
a7ad6e4e 1301 if (user && !*user)
62e76326 1302 safe_free(user);
1303
e293bb20 1304 if (!Config.onoff.log_mime_hdrs) {
47f6e231 1305 logfilePrintf(logfile, "%9ld.%03d %6d %s %s/%03d %"PRId64" %s %s %s %s%s/%s %s",
e293bb20 1306 (long int) current_time.tv_sec,
1307 (int) current_time.tv_usec / 1000,
1308 al->cache.msec,
1309 client,
1310 log_tags[al->cache.code],
1311 al->http.code,
47f6e231 1312 al->cache.size,
e293bb20 1313 al->_private.method_str,
1314 al->url,
1315 user ? user : dash_str,
1316 al->hier.ping.timedout ? "TIMEOUT_" : "",
1317 hier_strings[al->hier.code],
1318 al->hier.host,
1319 al->http.content_type);
1320 } else {
7684c4b1 1321 char *ereq = log_quote(al->headers.request);
1322 char *erep = log_quote(al->headers.reply);
47f6e231 1323 logfilePrintf(logfile, "%9ld.%03d %6d %s %s/%03d %"PRId64" %s %s %s %s%s/%s %s [%s] [%s]",
e293bb20 1324 (long int) current_time.tv_sec,
1325 (int) current_time.tv_usec / 1000,
1326 al->cache.msec,
1327 client,
1328 log_tags[al->cache.code],
1329 al->http.code,
47f6e231 1330 al->cache.size,
e293bb20 1331 al->_private.method_str,
1332 al->url,
1333 user ? user : dash_str,
1334 al->hier.ping.timedout ? "TIMEOUT_" : "",
1335 hier_strings[al->hier.code],
1336 al->hier.host,
1337 al->http.content_type,
1338 ereq,
1339 erep);
7684c4b1 1340 safe_free(ereq);
1341 safe_free(erep);
7684c4b1 1342 }
949a9c0d 1343 logfilePrintf(logfile, "\n");
e293bb20 1344 safe_free(user);
f892c2bf 1345}
1346
137ee196 1347static void
7684c4b1 1348accessLogCommon(AccessLogEntry * al, Logfile * logfile)
f892c2bf 1349{
9bea1d5b 1350 const char *client = NULL;
1351 char *user1 = NULL, *user2 = NULL;
cc192b50 1352 char buf[MAX_IPSTRLEN];
62e76326 1353
cc192b50 1354 if (Config.onoff.log_fqdn) {
62e76326 1355 client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
cc192b50 1356 }
62e76326 1357
cc192b50 1358 if (client == NULL) {
1359 client = al->cache.caddr.NtoA(buf,MAX_IPSTRLEN);
1360 }
62e76326 1361
9bea1d5b 1362 user1 = accessLogFormatName(al->cache.authuser);
62e76326 1363
9bea1d5b 1364 user2 = accessLogFormatName(al->cache.rfc931);
62e76326 1365
47f6e231 1366 logfilePrintf(logfile, "%s %s %s [%s] \"%s %s HTTP/%d.%d\" %d %"PRId64" %s:%s",
62e76326 1367 client,
1368 user2 ? user2 : dash_str,
1369 user1 ? user1 : dash_str,
1370 mkhttpdlogtime(&squid_curtime),
1371 al->_private.method_str,
1372 al->url,
1373 al->http.version.major, al->http.version.minor,
1374 al->http.code,
47f6e231 1375 al->cache.size,
62e76326 1376 log_tags[al->cache.code],
1377 hier_strings[al->hier.code]);
1378
9bea1d5b 1379 safe_free(user1);
62e76326 1380
9bea1d5b 1381 safe_free(user2);
7684c4b1 1382
1383 if (Config.onoff.log_mime_hdrs) {
1384 char *ereq = log_quote(al->headers.request);
1385 char *erep = log_quote(al->headers.reply);
1386 logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
1387 safe_free(ereq);
1388 safe_free(erep);
1389 } else {
1390 logfilePrintf(logfile, "\n");
1391 }
1392
f892c2bf 1393}
1394
1395void
7684c4b1 1396accessLogLog(AccessLogEntry * al, ACLChecklist * checklist)
f892c2bf 1397{
7684c4b1 1398 customlog *log;
1399
9bea1d5b 1400 if (LogfileStatus != LOG_ENABLE)
62e76326 1401 return;
1402
9bea1d5b 1403 if (al->url == NULL)
62e76326 1404 al->url = dash_str;
1405
9bea1d5b 1406 if (!al->http.content_type || *al->http.content_type == '\0')
62e76326 1407 al->http.content_type = dash_str;
1408
9bea1d5b 1409 if (al->icp.opcode)
62e76326 1410 al->_private.method_str = icp_opcode_str[al->icp.opcode];
9bea1d5b 1411 else
60745f24 1412 al->_private.method_str = RequestMethodStr(al->http.method);
62e76326 1413
9bea1d5b 1414 if (al->hier.host[0] == '\0')
62e76326 1415 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
9bea1d5b 1416
7684c4b1 1417 for (log = Config.Log.accesslogs; log; log = log->next) {
dd9be8c8 1418 if(checklist && log->aclList && !checklist->matchAclListFast(log->aclList))
7684c4b1 1419 continue;
62e76326 1420
7684c4b1 1421 switch (log->type) {
1422
1423 case CLF_AUTO:
1424
1425 if (Config.onoff.common_log)
1426 accessLogCommon(al, log->logfile);
1427 else
1428 accessLogSquid(al, log->logfile);
1429
1430 break;
1431
1432 case CLF_SQUID:
1433 accessLogSquid(al, log->logfile);
1434
1435 break;
1436
1437 case CLF_COMMON:
1438 accessLogCommon(al, log->logfile);
1439
1440 break;
1441
1442 case CLF_CUSTOM:
1443 accessLogCustom(al, log);
1444
1445 break;
1446
1447 case CLF_NONE:
1448 goto last;
1449
1450 default:
1451 fatalf("Unknown log format %d\n", log->type);
1452
1453 break;
1454 }
1455
1456 logfileFlush(log->logfile);
1457
1458 if (!checklist)
1459 break;
f892c2bf 1460 }
62e76326 1461
7684c4b1 1462last:
1463 (void)0; /* NULL statement for label */
1464
e66d7923 1465#if MULTICAST_MISS_STREAM
62e76326 1466
9bea1d5b 1467 if (al->cache.code != LOG_TCP_MISS)
62e76326 1468 (void) 0;
9bea1d5b 1469 else if (al->http.method != METHOD_GET)
62e76326 1470 (void) 0;
9bea1d5b 1471 else if (mcast_miss_fd < 0)
62e76326 1472 (void) 0;
9bea1d5b 1473 else {
62e76326 1474 unsigned int ibuf[365];
1475 size_t isize;
1476 xstrncpy((char *) ibuf, al->url, 364 * sizeof(int));
1477 isize = ((strlen(al->url) + 8) / 8) * 2;
1478
1479 if (isize > 364)
1480 isize = 364;
1481
1482 mcast_encode((unsigned int *) ibuf, isize,
1483 (const unsigned int *) Config.mcast_miss.encode_key);
1484
1485 comm_udp_sendto(mcast_miss_fd,
1486 &mcast_miss_to, sizeof(mcast_miss_to),
1487 ibuf, isize * sizeof(int));
e66d7923 1488 }
62e76326 1489
e66d7923 1490#endif
f892c2bf 1491}
1492
1493void
9bea1d5b 1494accessLogRotate(void)
f892c2bf 1495{
7684c4b1 1496 customlog *log;
d21f1c54 1497#if FORW_VIA_DB
7684c4b1 1498
9bea1d5b 1499 fvdbClear();
d21f1c54 1500#endif
62e76326 1501
7684c4b1 1502 for (log = Config.Log.accesslogs; log; log = log->next) {
1503 if (log->logfile) {
1504 logfileRotate(log->logfile);
1505 }
1506 }
62e76326 1507
c3609322 1508#if HEADERS_LOG
62e76326 1509
9bea1d5b 1510 logfileRotate(headerslog);
62e76326 1511
c3609322 1512#endif
f892c2bf 1513}
1514
1515void
9bea1d5b 1516accessLogClose(void)
f892c2bf 1517{
7684c4b1 1518 customlog *log;
62e76326 1519
7684c4b1 1520 for (log = Config.Log.accesslogs; log; log = log->next) {
1521 if (log->logfile) {
1522 logfileClose(log->logfile);
1523 log->logfile = NULL;
1524 }
1525 }
62e76326 1526
c3609322 1527#if HEADERS_LOG
62e76326 1528
9bea1d5b 1529 logfileClose(headerslog);
62e76326 1530
9bea1d5b 1531 headerslog = NULL;
62e76326 1532
c3609322 1533#endif
f892c2bf 1534}
1535
b24880fe 1536HierarchyLogEntry::HierarchyLogEntry() :
1537 code(HIER_NONE),
1538 cd_lookup(LOOKUP_NONE),
1539 n_choices(0),
1540 n_ichoices(0)
1541{
1542 memset(host, '\0', SQUIDHOSTNAMELEN);
1543 memset(cd_host, '\0', SQUIDHOSTNAMELEN);
1544
1545 peer_select_start.tv_sec =0;
1546 peer_select_start.tv_usec =0;
1547
1548 store_complete_stop.tv_sec =0;
1549 store_complete_stop.tv_usec =0;
1550}
1551
f892c2bf 1552void
9bea1d5b 1553hierarchyNote(HierarchyLogEntry * hl,
62e76326 1554 hier_code code,
1555 const char *cache_peer)
f892c2bf 1556{
9bea1d5b 1557 assert(hl != NULL);
1558 hl->code = code;
1559 xstrncpy(hl->host, cache_peer, SQUIDHOSTNAMELEN);
f892c2bf 1560}
7a2f978b 1561
1562void
9bea1d5b 1563accessLogInit(void)
7a2f978b 1564{
7684c4b1 1565 customlog *log;
9bea1d5b 1566 assert(sizeof(log_tags) == (LOG_TYPE_MAX + 1) * sizeof(char *));
62e76326 1567
7684c4b1 1568 for (log = Config.Log.accesslogs; log; log = log->next) {
ed22613b 1569 if (log->type == CLF_NONE)
7684c4b1 1570 continue;
62e76326 1571
7684c4b1 1572 log->logfile = logfileOpen(log->filename, MAX_URL << 1, 1);
62e76326 1573
7684c4b1 1574 LogfileStatus = LOG_ENABLE;
1575 }
62e76326 1576
c3609322 1577#if HEADERS_LOG
62e76326 1578
9bea1d5b 1579 headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
62e76326 1580
9bea1d5b 1581 assert(NULL != headerslog);
62e76326 1582
d21f1c54 1583#endif
e66d7923 1584#if MULTICAST_MISS_STREAM
62e76326 1585
9bea1d5b 1586 if (Config.mcast_miss.addr.s_addr != no_addr.s_addr) {
62e76326 1587 memset(&mcast_miss_to, '\0', sizeof(mcast_miss_to));
1588 mcast_miss_to.sin_family = AF_INET;
1589 mcast_miss_to.sin_port = htons(Config.mcast_miss.port);
1590 mcast_miss_to.sin_addr.s_addr = Config.mcast_miss.addr.s_addr;
1591 mcast_miss_fd = comm_open(SOCK_DGRAM,
bdb741f4 1592 IPPROTO_UDP,
62e76326 1593 Config.Addrs.udp_incoming,
1594 Config.mcast_miss.port,
1595 COMM_NONBLOCKING,
1596 "Multicast Miss Stream");
1597
1598 if (mcast_miss_fd < 0)
1599 fatal("Cannot open Multicast Miss Stream Socket");
1600
bf8fe701 1601 debugs(46, 1, "Multicast Miss Stream Socket opened on FD " << mcast_miss_fd);
62e76326 1602
1603 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
1604
1605 if (strlen(Config.mcast_miss.encode_key) < 16)
1606 fatal("mcast_encode_key is too short, must be 16 characters");
e66d7923 1607 }
62e76326 1608
62ee09ca 1609#endif
1610#if FORW_VIA_DB
1611
1612 fvdbInit();
1613
1614#endif
1615}
1616
1617void
1618accessLogRegisterWithCacheManager(CacheManager & manager)
1619{
1620#if FORW_VIA_DB
1621
1622 fvdbRegisterWithCacheManager(manager);
1623
e66d7923 1624#endif
7a2f978b 1625}
c6e7cab0 1626
1627const char *
9bea1d5b 1628accessLogTime(time_t t)
c6e7cab0 1629{
62e76326 1630
9bea1d5b 1631 struct tm *tm;
1632 static char buf[128];
1633 static time_t last_t = 0;
62e76326 1634
9bea1d5b 1635 if (t != last_t) {
62e76326 1636 tm = localtime(&t);
1637 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
1638 last_t = t;
c6e7cab0 1639 }
62e76326 1640
9bea1d5b 1641 return buf;
c6e7cab0 1642}
d21f1c54 1643
1644
1645#if FORW_VIA_DB
1afe05c5 1646
d21f1c54 1647static void
9bea1d5b 1648fvdbInit(void)
d21f1c54 1649{
30abd221 1650 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
1651 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
62ee09ca 1652}
1653
1654static void
1655fvdbRegisterWithCacheManager(CacheManager & manager)
1656{
1657 manager.registerAction("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
1658 manager.registerAction("forw_headers", "X-Forwarded-For Request Headers",
1659 fvdbDumpForw, 0, 1);
d21f1c54 1660}
1661
1662static void
9bea1d5b 1663fvdbCount(hash_table * hash, const char *key)
1afe05c5 1664{
9bea1d5b 1665 fvdb_entry *fv;
62e76326 1666
9bea1d5b 1667 if (NULL == hash)
62e76326 1668 return;
1669
8021b4a6 1670 fv = (fvdb_entry *)hash_lookup(hash, key);
62e76326 1671
9bea1d5b 1672 if (NULL == fv) {
62e76326 1673 fv = static_cast <fvdb_entry *>(xcalloc(1, sizeof(fvdb_entry)));
1674 fv->hash.key = xstrdup(key);
1675 hash_join(hash, &fv->hash);
1afe05c5 1676 }
62e76326 1677
9bea1d5b 1678 fv->n++;
d21f1c54 1679}
1680
1681void
9bea1d5b 1682fvdbCountVia(const char *key)
d21f1c54 1683{
9bea1d5b 1684 fvdbCount(via_table, key);
d21f1c54 1685}
1686
1687void
9bea1d5b 1688fvdbCountForw(const char *key)
d21f1c54 1689{
9bea1d5b 1690 fvdbCount(forw_table, key);
d21f1c54 1691}
1692
1afe05c5 1693static void
9bea1d5b 1694fvdbDumpTable(StoreEntry * e, hash_table * hash)
d21f1c54 1695{
9bea1d5b 1696 hash_link *h;
1697 fvdb_entry *fv;
62e76326 1698
9bea1d5b 1699 if (hash == NULL)
62e76326 1700 return;
1701
9bea1d5b 1702 hash_first(hash);
62e76326 1703
9bea1d5b 1704 while ((h = hash_next(hash))) {
62e76326 1705 fv = (fvdb_entry *) h;
1706 storeAppendPrintf(e, "%9d %s\n", fv->n, hashKeyStr(&fv->hash));
1afe05c5 1707 }
d21f1c54 1708}
1709
1710static void
9bea1d5b 1711fvdbDumpVia(StoreEntry * e)
d21f1c54 1712{
9bea1d5b 1713 fvdbDumpTable(e, via_table);
d21f1c54 1714}
1afe05c5 1715
d21f1c54 1716static void
9bea1d5b 1717fvdbDumpForw(StoreEntry * e)
d21f1c54 1718{
9bea1d5b 1719 fvdbDumpTable(e, forw_table);
d21f1c54 1720}
1721
1722static
b644367b 1723void
9bea1d5b 1724fvdbFreeEntry(void *data)
d21f1c54 1725{
8021b4a6 1726 fvdb_entry *fv = static_cast <fvdb_entry *>(data);
9bea1d5b 1727 xfree(fv->hash.key);
1728 xfree(fv);
d21f1c54 1729}
1730
1731static void
9bea1d5b 1732fvdbClear(void)
d21f1c54 1733{
9bea1d5b 1734 hashFreeItems(via_table, fvdbFreeEntry);
1735 hashFreeMemory(via_table);
30abd221 1736 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
9bea1d5b 1737 hashFreeItems(forw_table, fvdbFreeEntry);
1738 hashFreeMemory(forw_table);
30abd221 1739 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 1740}
1741
1afe05c5 1742#endif
e66d7923 1743
1744#if MULTICAST_MISS_STREAM
1745/*
1746 * From http://www.io.com/~paulhart/game/algorithms/tea.html
1747 *
1748 * size of 'ibuf' must be a multiple of 2.
1749 * size of 'key' must be 4.
1750 * 'ibuf' is modified in place, encrypted data is written in
1751 * network byte order.
1752 */
1753static void
9bea1d5b 1754mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
e66d7923 1755{
9bea1d5b 1756 unsigned int y;
1757 unsigned int z;
1758 unsigned int sum;
1759 const unsigned int delta = 0x9e3779b9;
1760 unsigned int n = 32;
1761 const unsigned int k0 = htonl(key[0]);
1762 const unsigned int k1 = htonl(key[1]);
1763 const unsigned int k2 = htonl(key[2]);
1764 const unsigned int k3 = htonl(key[3]);
1765 int i;
62e76326 1766
9bea1d5b 1767 for (i = 0; i < isize; i += 2) {
62e76326 1768 y = htonl(ibuf[i]);
1769 z = htonl(ibuf[i + 1]);
1770 sum = 0;
1771
1772 for (n = 32; n; n--) {
1773 sum += delta;
1774 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
1775 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
1776 }
1777
1778 ibuf[i] = htonl(y);
1779 ibuf[i + 1] = htonl(z);
e66d7923 1780 }
1781}
1782
1783#endif
c3609322 1784
1785#if HEADERS_LOG
1786void
60745f24 1787headersLog(int cs, int pq, const HttpRequestMethod& method, void *data)
c3609322 1788{
9bea1d5b 1789 HttpReply *rep;
190154cf 1790 HttpRequest *req;
9bea1d5b 1791 unsigned short magic = 0;
1792 unsigned char M = (unsigned char) m;
1793 unsigned short S;
1794 char *hmask;
1795 int ccmask = 0;
62e76326 1796
9bea1d5b 1797 if (0 == pq) {
62e76326 1798 /* reply */
1799 rep = data;
1800 req = NULL;
1801 magic = 0x0050;
1802 hmask = rep->header.mask;
1803
1804 if (rep->cache_control)
1805 ccmask = rep->cache_control->mask;
9bea1d5b 1806 } else {
62e76326 1807 /* request */
1808 req = data;
1809 rep = NULL;
1810 magic = 0x0051;
1811 hmask = req->header.mask;
1812
1813 if (req->cache_control)
1814 ccmask = req->cache_control->mask;
c3609322 1815 }
62e76326 1816
9bea1d5b 1817 if (0 == cs) {
62e76326 1818 /* client */
1819 magic |= 0x4300;
9bea1d5b 1820 } else {
62e76326 1821 /* server */
1822 magic |= 0x5300;
c3609322 1823 }
62e76326 1824
9bea1d5b 1825 magic = htons(magic);
1826 ccmask = htonl(ccmask);
62e76326 1827
9bea1d5b 1828 if (0 == pq)
62e76326 1829 S = (unsigned short) rep->sline.status;
9bea1d5b 1830 else
62e76326 1831 S = (unsigned short) HTTP_STATUS_NONE;
1832
9bea1d5b 1833 logfileWrite(headerslog, &magic, sizeof(magic));
62e76326 1834
9bea1d5b 1835 logfileWrite(headerslog, &M, sizeof(M));
62e76326 1836
9bea1d5b 1837 logfileWrite(headerslog, &S, sizeof(S));
62e76326 1838
9bea1d5b 1839 logfileWrite(headerslog, hmask, sizeof(HttpHeaderMask));
62e76326 1840
9bea1d5b 1841 logfileWrite(headerslog, &ccmask, sizeof(int));
62e76326 1842
9bea1d5b 1843 logfileFlush(headerslog);
c3609322 1844}
1845
1846#endif
c8be6d7b 1847
1848void
1849accessLogFreeMemory(AccessLogEntry * aLogEntry)
1850{
1851 safe_free(aLogEntry->headers.request);
1852 safe_free(aLogEntry->headers.reply);
1853 safe_free(aLogEntry->cache.authuser);
7684c4b1 1854
90a8964c 1855 HTTPMSGUNLOCK(aLogEntry->reply);
6dd9f4bd 1856 HTTPMSGUNLOCK(aLogEntry->request);
c8be6d7b 1857}
1858
1859int
1860logTypeIsATcpHit(log_type code)
1861{
1862 /* this should be a bitmap for better optimization */
62e76326 1863
c8be6d7b 1864 if (code == LOG_TCP_HIT)
62e76326 1865 return 1;
1866
c8be6d7b 1867 if (code == LOG_TCP_IMS_HIT)
62e76326 1868 return 1;
1869
1d7ab0f4 1870 if (code == LOG_TCP_REFRESH_FAIL)
62e76326 1871 return 1;
1872
1d7ab0f4 1873 if (code == LOG_TCP_REFRESH_UNMODIFIED)
62e76326 1874 return 1;
1875
c8be6d7b 1876 if (code == LOG_TCP_NEGATIVE_HIT)
62e76326 1877 return 1;
1878
c8be6d7b 1879 if (code == LOG_TCP_MEM_HIT)
62e76326 1880 return 1;
1881
c8be6d7b 1882 if (code == LOG_TCP_OFFLINE_HIT)
62e76326 1883 return 1;
1884
c8be6d7b 1885 return 0;
1886}
e6ccf245 1887