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