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