3 * $Id: access_log.cc,v 1.66 2001/01/12 00:37:14 wessels Exp $
5 * DEBUG: section 46 Access Log
6 * AUTHOR: Duane Wessels
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
39 static void accessLogSquid(AccessLogEntry
* al
);
40 static void accessLogCommon(AccessLogEntry
* al
);
41 static Logfile
*logfile
= NULL
;
43 static Logfile
*headerslog
= NULL
;
46 #if MULTICAST_MISS_STREAM
47 static int mcast_miss_fd
= -1;
48 static struct sockaddr_in mcast_miss_to
;
49 static void mcast_encode(unsigned int *, size_t, const unsigned int *);
52 const char *log_tags
[] =
60 "TCP_CLIENT_REFRESH_MISS",
84 static hash_table
*via_table
= NULL
;
85 static hash_table
*forw_table
= NULL
;
86 static void fvdbInit(void);
87 static void fvdbDumpTable(StoreEntry
* e
, hash_table
* hash
);
88 static void fvdbCount(hash_table
* hash
, const char *key
);
89 static OBJH fvdbDumpVia
;
90 static OBJH fvdbDumpForw
;
91 static FREE fvdbFreeEntry
;
92 static void fvdbClear(void);
95 static int LogfileStatus
= LOG_DISABLE
;
96 #define LOG_BUF_SZ (MAX_URL<<2)
98 static const char c2x
[] =
99 "000102030405060708090a0b0c0d0e0f"
100 "101112131415161718191a1b1c1d1e1f"
101 "202122232425262728292a2b2c2d2e2f"
102 "303132333435363738393a3b3c3d3e3f"
103 "404142434445464748494a4b4c4d4e4f"
104 "505152535455565758595a5b5c5d5e5f"
105 "606162636465666768696a6b6c6d6e6f"
106 "707172737475767778797a7b7c7d7e7f"
107 "808182838485868788898a8b8c8d8e8f"
108 "909192939495969798999a9b9c9d9e9f"
109 "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
110 "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
111 "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
112 "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
113 "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
114 "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
116 /* log_quote -- URL-style encoding on MIME headers. */
119 log_quote(const char *header
)
125 if (header
== NULL
) {
130 buf
= xcalloc((strlen(header
) * 3) + 1, 1);
133 * We escape: \x00-\x1F"#%;<>?{}|\\\\^~`\[\]\x7F-\xFF
134 * which is the default escape list for the CPAN Perl5 URI module
135 * modulo the inclusion of space (x40) to make the raw logs a bit
138 while ((c
= *(const unsigned char *) header
++) != '\0') {
141 *buf_cursor
++ = '\\';
143 } else if (c
== '\n') {
144 *buf_cursor
++ = '\\';
170 *buf_cursor
++ = c2x
[i
];
171 *buf_cursor
++ = c2x
[i
+ 1];
173 } else if (c
== '\\') {
174 *buf_cursor
++ = '\\';
175 *buf_cursor
++ = '\\';
178 *buf_cursor
++ = (char) c
;
186 username_quote(const char *header
)
187 /* copy of log_quote. Bugs there will be found here */
193 if (header
== NULL
) {
198 buf
= xcalloc((strlen(header
) * 3) + 1, 1);
201 * We escape: space \x00-\x1F and space (0x40) and \x7F-\xFF
202 * to prevent garbage in the logs. CR and LF are also there just in case.
204 while ((c
= *(const unsigned char *) header
++) != '\0') {
206 *buf_cursor
++ = '\\';
208 } else if (c
== '\n') {
209 *buf_cursor
++ = '\\';
216 *buf_cursor
++ = c2x
[i
];
217 *buf_cursor
++ = c2x
[i
+ 1];
219 *buf_cursor
++ = (char) c
;
227 accessLogFormatName(const char *name
)
230 return xcalloc(strlen(dash_str
) + 1, 1);
231 return username_quote(name
);
235 accessLogSquid(AccessLogEntry
* al
)
237 const char *client
= NULL
;
239 if (Config
.onoff
.log_fqdn
)
240 client
= fqdncache_gethostbyaddr(al
->cache
.caddr
, FQDN_LOOKUP_IF_MISS
);
242 client
= inet_ntoa(al
->cache
.caddr
);
243 logfilePrintf(logfile
, "%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s",
244 (int) current_time
.tv_sec
,
245 (int) current_time
.tv_usec
/ 1000,
248 log_tags
[al
->cache
.code
],
251 al
->private.method_str
,
253 (user
= accessLogFormatName(al
->cache
.authuser
?
254 al
->cache
.authuser
: al
->cache
.rfc931
)),
255 al
->hier
.ping
.timedout
? "TIMEOUT_" : "",
256 hier_strings
[al
->hier
.code
],
258 al
->http
.content_type
);
263 accessLogCommon(AccessLogEntry
* al
)
265 const char *client
= NULL
;
267 if (Config
.onoff
.log_fqdn
)
268 client
= fqdncache_gethostbyaddr(al
->cache
.caddr
, 0);
270 client
= inet_ntoa(al
->cache
.caddr
);
271 logfilePrintf(logfile
, "%s %s %s [%s] \"%s %s HTTP/%d.%d\" %d %d %s:%s",
273 accessLogFormatName(al
->cache
.rfc931
),
274 (user
= accessLogFormatName(al
->cache
.authuser
)),
275 mkhttpdlogtime(&squid_curtime
),
276 al
->private.method_str
,
278 al
->http
.version
.major
, al
->http
.version
.minor
,
281 log_tags
[al
->cache
.code
],
282 hier_strings
[al
->hier
.code
]);
287 accessLogLog(AccessLogEntry
* al
)
289 if (LogfileStatus
!= LOG_ENABLE
)
293 if (!al
->http
.content_type
|| *al
->http
.content_type
== '\0')
294 al
->http
.content_type
= dash_str
;
296 al
->private.method_str
= icp_opcode_str
[al
->icp
.opcode
];
298 al
->private.method_str
= RequestMethodStr
[al
->http
.method
];
299 if (al
->hier
.host
[0] == '\0')
300 xstrncpy(al
->hier
.host
, dash_str
, SQUIDHOSTNAMELEN
);
302 if (Config
.onoff
.common_log
)
306 if (Config
.onoff
.log_mime_hdrs
) {
307 char *ereq
= log_quote(al
->headers
.request
);
308 char *erep
= log_quote(al
->headers
.reply
);
309 logfilePrintf(logfile
, " [%s] [%s]\n", ereq
, erep
);
313 logfilePrintf(logfile
, "\n");
315 logfileFlush(logfile
);
316 #if MULTICAST_MISS_STREAM
317 if (al
->cache
.code
!= LOG_TCP_MISS
)
319 else if (al
->http
.method
!= METHOD_GET
)
321 else if (mcast_miss_fd
< 0)
324 unsigned int ibuf
[365];
326 xstrncpy((char *) ibuf
, al
->url
, 364 * sizeof(int));
327 isize
= ((strlen(al
->url
) + 8) / 8) * 2;
330 mcast_encode((unsigned int *) ibuf
, isize
,
331 (const unsigned int *) Config
.mcast_miss
.encode_key
);
332 comm_udp_sendto(mcast_miss_fd
,
333 &mcast_miss_to
, sizeof(mcast_miss_to
),
334 ibuf
, isize
* sizeof(int));
340 accessLogRotate(void)
347 logfileRotate(logfile
);
349 logfileRotate(headerslog
);
356 logfileClose(logfile
);
359 logfileClose(headerslog
);
365 hierarchyNote(HierarchyLogEntry
* hl
,
367 const char *cache_peer
)
371 xstrncpy(hl
->host
, cache_peer
, SQUIDHOSTNAMELEN
);
377 assert(sizeof(log_tags
) == (LOG_TYPE_MAX
+ 1) * sizeof(char *));
378 if (strcasecmp(Config
.Log
.access
, "none") == 0)
380 logfile
= logfileOpen(Config
.Log
.access
, MAX_URL
<< 1, 1);
381 LogfileStatus
= LOG_ENABLE
;
383 headerslog
= logfileOpen("/usr/local/squid/logs/headers.log", 512);
384 assert(NULL
!= headerslog
);
389 #if MULTICAST_MISS_STREAM
390 if (Config
.mcast_miss
.addr
.s_addr
!= no_addr
.s_addr
) {
391 memset(&mcast_miss_to
, '\0', sizeof(mcast_miss_to
));
392 mcast_miss_to
.sin_family
= AF_INET
;
393 mcast_miss_to
.sin_port
= htons(Config
.mcast_miss
.port
);
394 mcast_miss_to
.sin_addr
.s_addr
= Config
.mcast_miss
.addr
.s_addr
;
395 mcast_miss_fd
= comm_open(SOCK_DGRAM
,
397 Config
.Addrs
.udp_incoming
,
398 Config
.mcast_miss
.port
,
400 "Multicast Miss Stream");
401 if (mcast_miss_fd
< 0)
402 fatal("Cannot open Multicast Miss Stream Socket");
403 debug(46, 1) ("Multicast Miss Stream Socket opened on FD %d\n",
405 mcastSetTtl(mcast_miss_fd
, Config
.mcast_miss
.ttl
);
406 if (strlen(Config
.mcast_miss
.encode_key
) < 16)
407 fatal("mcast_encode_key is too short, must be 16 characters");
413 accessLogTime(time_t t
)
416 static char buf
[128];
417 static time_t last_t
= 0;
420 strftime(buf
, 127, "%Y/%m/%d %H:%M:%S", tm
);
432 via_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
433 forw_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
434 cachemgrRegister("via_headers", "Via Request Headers", fvdbDumpVia
, 0, 1);
435 cachemgrRegister("forw_headers", "X-Forwarded-For Request Headers",
440 fvdbCount(hash_table
* hash
, const char *key
)
445 fv
= hash_lookup(hash
, key
);
447 fv
= xcalloc(1, sizeof(fvdb_entry
));
448 fv
->hash
.key
= xstrdup(key
);
449 hash_join(hash
, &fv
->hash
);
455 fvdbCountVia(const char *key
)
457 fvdbCount(via_table
, key
);
461 fvdbCountForw(const char *key
)
463 fvdbCount(forw_table
, key
);
467 fvdbDumpTable(StoreEntry
* e
, hash_table
* hash
)
474 while ((h
= hash_next(hash
))) {
475 fv
= (fvdb_entry
*) h
;
476 storeAppendPrintf(e
, "%9d %s\n", fv
->n
, hashKeyStr(&fv
->hash
));
481 fvdbDumpVia(StoreEntry
* e
)
483 fvdbDumpTable(e
, via_table
);
487 fvdbDumpForw(StoreEntry
* e
)
489 fvdbDumpTable(e
, forw_table
);
494 fvdbFreeEntry(void *data
)
496 fvdb_entry
*fv
= data
;
504 hashFreeItems(via_table
, fvdbFreeEntry
);
505 hashFreeMemory(via_table
);
506 via_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
507 hashFreeItems(forw_table
, fvdbFreeEntry
);
508 hashFreeMemory(forw_table
);
509 forw_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
514 #if MULTICAST_MISS_STREAM
516 * From http://www.io.com/~paulhart/game/algorithms/tea.html
518 * size of 'ibuf' must be a multiple of 2.
519 * size of 'key' must be 4.
520 * 'ibuf' is modified in place, encrypted data is written in
521 * network byte order.
524 mcast_encode(unsigned int *ibuf
, size_t isize
, const unsigned int *key
)
529 const unsigned int delta
= 0x9e3779b9;
531 const unsigned int k0
= htonl(key
[0]);
532 const unsigned int k1
= htonl(key
[1]);
533 const unsigned int k2
= htonl(key
[2]);
534 const unsigned int k3
= htonl(key
[3]);
536 for (i
= 0; i
< isize
; i
+= 2) {
538 z
= htonl(ibuf
[i
+ 1]);
540 for (n
= 32; n
; n
--) {
542 y
+= (z
<< 4) + (k0
^ z
) + (sum
^ (z
>> 5)) + k1
;
543 z
+= (y
<< 4) + (k2
^ y
) + (sum
^ (y
>> 5)) + k3
;
546 ibuf
[i
+ 1] = htonl(z
);
554 headersLog(int cs
, int pq
, method_t m
, void *data
)
558 unsigned short magic
= 0;
559 unsigned char M
= (unsigned char) m
;
568 hmask
= rep
->header
.mask
;
569 if (rep
->cache_control
)
570 ccmask
= rep
->cache_control
->mask
;
576 hmask
= req
->header
.mask
;
577 if (req
->cache_control
)
578 ccmask
= req
->cache_control
->mask
;
587 magic
= htons(magic
);
588 ccmask
= htonl(ccmask
);
590 S
= (unsigned short) rep
->sline
.status
;
592 S
= (unsigned short) HTTP_STATUS_NONE
;
593 logfileWrite(headerslog
, &magic
, sizeof(magic
));
594 logfileWrite(headerslog
, &M
, sizeof(M
));
595 logfileWrite(headerslog
, &S
, sizeof(S
));
596 logfileWrite(headerslog
, hmask
, sizeof(HttpHeaderMask
));
597 logfileWrite(headerslog
, &ccmask
, sizeof(int));
598 logfileFlush(headerslog
);