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