2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 46 Access Log */
12 #include "AccessLogEntry.h"
13 #include "acl/Checklist.h"
15 #include "adaptation/Config.h"
17 #include "CachePeer.h"
18 #include "err_detail_type.h"
19 #include "errorpage.h"
20 #include "errorpage.h"
21 #include "errorpage.h"
22 #include "format/Token.h"
24 #include "hier_code.h"
25 #include "HttpReply.h"
26 #include "HttpRequest.h"
27 #include "log/access_log.h"
28 #include "log/Config.h"
29 #include "log/CustomLog.h"
31 #include "log/Formats.h"
33 #include "mgr/Registration.h"
35 #include "SquidConfig.h"
36 #include "SquidTime.h"
40 #include "eui/Eui48.h"
41 #include "eui/Eui64.h"
45 static Logfile
*headerslog
= NULL
;
48 #if MULTICAST_MISS_STREAM
49 static int mcast_miss_fd
= -1;
51 static struct sockaddr_in mcast_miss_to
;
52 static void mcast_encode(unsigned int *, size_t, const unsigned int *);
61 static hash_table
*via_table
= NULL
;
62 static hash_table
*forw_table
= NULL
;
63 static void fvdbInit();
64 static void fvdbDumpTable(StoreEntry
* e
, hash_table
* hash
);
65 static void fvdbCount(hash_table
* hash
, const char *key
);
66 static OBJH fvdbDumpVia
;
67 static OBJH fvdbDumpForw
;
68 static FREE fvdbFreeEntry
;
69 static void fvdbClear(void);
70 static void fvdbRegisterWithCacheManager();
73 int LogfileStatus
= LOG_DISABLE
;
76 accessLogLogTo(CustomLog
* log
, AccessLogEntry::Pointer
&al
, ACLChecklist
* checklist
)
82 if (!al
->http
.content_type
|| *al
->http
.content_type
== '\0')
83 al
->http
.content_type
= dash_str
;
85 if (al
->hier
.host
[0] == '\0')
86 xstrncpy(al
->hier
.host
, dash_str
, SQUIDHOSTNAMELEN
);
88 for (; log
; log
= log
->next
) {
89 if (log
->aclList
&& checklist
&& checklist
->fastCheck(log
->aclList
) != ACCESS_ALLOWED
)
92 // The special-case "none" type has no logfile object set
93 if (log
->type
== Log::Format::CLF_NONE
)
97 logfileLineStart(log
->logfile
);
101 case Log::Format::CLF_SQUID
:
102 Log::Format::SquidNative(al
, log
->logfile
);
105 case Log::Format::CLF_COMBINED
:
106 Log::Format::HttpdCombined(al
, log
->logfile
);
109 case Log::Format::CLF_COMMON
:
110 Log::Format::HttpdCommon(al
, log
->logfile
);
113 case Log::Format::CLF_REFERER
:
114 Log::Format::SquidReferer(al
, log
->logfile
);
117 case Log::Format::CLF_USERAGENT
:
118 Log::Format::SquidUserAgent(al
, log
->logfile
);
121 case Log::Format::CLF_CUSTOM
:
122 Log::Format::SquidCustom(al
, log
);
126 case Log::Format::CLF_ICAP_SQUID
:
127 Log::Format::SquidIcap(al
, log
->logfile
);
132 fatalf("Unknown log format %d\n", log
->type
);
136 logfileLineEnd(log
->logfile
);
139 // NP: WTF? if _any_ log line has no checklist ignore the following ones?
146 accessLogLog(AccessLogEntry::Pointer
&al
, ACLChecklist
* checklist
)
148 if (LogfileStatus
!= LOG_ENABLE
)
151 accessLogLogTo(Config
.Log
.accesslogs
, al
, checklist
);
152 #if MULTICAST_MISS_STREAM
154 if (al
->cache
.code
!= LOG_TCP_MISS
)
156 else if (al
->http
.method
!= METHOD_GET
)
158 else if (mcast_miss_fd
< 0)
161 unsigned int ibuf
[365];
163 xstrncpy((char *) ibuf
, al
->url
, 364 * sizeof(int));
164 isize
= ((strlen(al
->url
) + 8) / 8) * 2;
169 mcast_encode((unsigned int *) ibuf
, isize
,
170 (const unsigned int *) Config
.mcast_miss
.encode_key
);
172 comm_udp_sendto(mcast_miss_fd
,
173 &mcast_miss_to
, sizeof(mcast_miss_to
),
174 ibuf
, isize
* sizeof(int));
181 accessLogRotate(void)
189 for (log
= Config
.Log
.accesslogs
; log
; log
= log
->next
) {
191 int16_t rc
= (log
->rotateCount
>= 0 ? log
->rotateCount
: Config
.Log
.rotateNumber
);
192 logfileRotate(log
->logfile
, rc
);
198 logfileRotate(headerslog
, Config
.Log
.rotateNumber
);
208 for (log
= Config
.Log
.accesslogs
; log
; log
= log
->next
) {
210 logfileClose(log
->logfile
);
217 logfileClose(headerslog
);
224 HierarchyLogEntry::HierarchyLogEntry() :
226 cd_lookup(LOOKUP_NONE
),
229 peer_reply_status(Http::scNone
),
233 memset(host
, '\0', SQUIDHOSTNAMELEN
);
234 memset(cd_host
, '\0', SQUIDHOSTNAMELEN
);
236 peer_select_start
.tv_sec
=0;
237 peer_select_start
.tv_usec
=0;
239 store_complete_stop
.tv_sec
=0;
240 store_complete_stop
.tv_usec
=0;
242 peer_http_request_sent
.tv_sec
= 0;
243 peer_http_request_sent
.tv_usec
= 0;
245 peer_response_time
.tv_sec
= -1;
246 peer_response_time
.tv_usec
= 0;
248 totalResponseTime_
.tv_sec
= -1;
249 totalResponseTime_
.tv_usec
= 0;
251 firstConnStart_
.tv_sec
= 0;
252 firstConnStart_
.tv_usec
= 0;
256 HierarchyLogEntry::note(const Comm::ConnectionPointer
&server
, const char *requestedHost
)
259 if (tcpServer
== NULL
) {
261 xstrncpy(host
, requestedHost
, sizeof(host
));
263 code
= tcpServer
->peerType
;
265 if (tcpServer
->getPeer()) {
266 // went to peer, log peer host name
267 xstrncpy(host
, tcpServer
->getPeer()->name
, sizeof(host
));
269 xstrncpy(host
, requestedHost
, sizeof(host
));
275 HierarchyLogEntry::startPeerClock()
277 if (!firstConnStart_
.tv_sec
)
278 firstConnStart_
= current_time
;
282 HierarchyLogEntry::stopPeerClock(const bool force
)
284 debugs(46, 5, "First connection started: " << firstConnStart_
.tv_sec
<< "." <<
285 std::setfill('0') << std::setw(6) << firstConnStart_
.tv_usec
<<
286 ", current total response time value: " << (totalResponseTime_
.tv_sec
* 1000 + totalResponseTime_
.tv_usec
/1000) <<
287 (force
? ", force fixing" : ""));
288 if (!force
&& totalResponseTime_
.tv_sec
!= -1)
291 if (firstConnStart_
.tv_sec
)
292 tvSub(totalResponseTime_
, firstConnStart_
, current_time
);
296 HierarchyLogEntry::totalResponseTime(struct timeval
&responseTime
)
298 // This should not really happen, but there may be rare code
299 // paths that lead to FwdState discarded (or transaction logged)
300 // without (or before) a stopPeerClock() call.
301 if (firstConnStart_
.tv_sec
&& totalResponseTime_
.tv_sec
== -1)
302 stopPeerClock(false);
304 responseTime
= totalResponseTime_
;
308 accessLogRegisterWithCacheManager(void)
311 fvdbRegisterWithCacheManager();
320 accessLogRegisterWithCacheManager();
323 Log::TheConfig
.hasAdaptToken
= false;
326 Log::TheConfig
.hasIcapToken
= false;
329 for (log
= Config
.Log
.accesslogs
; log
; log
= log
->next
) {
330 if (log
->type
== Log::Format::CLF_NONE
)
333 log
->logfile
= logfileOpen(log
->filename
, log
->bufferSize
, log
->fatal
);
335 LogfileStatus
= LOG_ENABLE
;
338 for (Format::Token
* curr_token
= (log
->logFormat
?log
->logFormat
->format
:NULL
); curr_token
; curr_token
= curr_token
->next
) {
339 if (curr_token
->type
== Format::LFT_ADAPTATION_SUM_XACT_TIMES
||
340 curr_token
->type
== Format::LFT_ADAPTATION_ALL_XACT_TIMES
||
341 curr_token
->type
== Format::LFT_ADAPTATION_LAST_HEADER
||
342 curr_token
->type
== Format::LFT_ADAPTATION_LAST_HEADER_ELEM
||
343 curr_token
->type
== Format::LFT_ADAPTATION_LAST_ALL_HEADERS
||
344 (curr_token
->type
== Format::LFT_NOTE
&& !Adaptation::Config::metaHeaders
.empty())) {
345 Log::TheConfig
.hasAdaptToken
= true;
348 if (curr_token
->type
== Format::LFT_ICAP_TOTAL_TIME
) {
349 Log::TheConfig
.hasIcapToken
= true;
358 headerslog
= logfileOpen("/usr/local/squid/logs/headers.log", 512);
360 assert(NULL
!= headerslog
);
363 #if MULTICAST_MISS_STREAM
365 if (Config
.mcast_miss
.addr
.s_addr
!= no_addr
.s_addr
) {
366 memset(&mcast_miss_to
, '\0', sizeof(mcast_miss_to
));
367 mcast_miss_to
.sin_family
= AF_INET
;
368 mcast_miss_to
.sin_port
= htons(Config
.mcast_miss
.port
);
369 mcast_miss_to
.sin_addr
.s_addr
= Config
.mcast_miss
.addr
.s_addr
;
370 mcast_miss_fd
= comm_open(SOCK_DGRAM
,
372 Config
.Addrs
.udp_incoming
,
373 Config
.mcast_miss
.port
,
375 "Multicast Miss Stream");
377 if (mcast_miss_fd
< 0)
378 fatal("Cannot open Multicast Miss Stream Socket");
380 debugs(46, DBG_IMPORTANT
, "Multicast Miss Stream Socket opened on FD " << mcast_miss_fd
);
382 mcastSetTtl(mcast_miss_fd
, Config
.mcast_miss
.ttl
);
384 if (strlen(Config
.mcast_miss
.encode_key
) < 16)
385 fatal("mcast_encode_key is too short, must be 16 characters");
401 via_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
402 forw_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
406 fvdbRegisterWithCacheManager(void)
408 Mgr::RegisterAction("via_headers", "Via Request Headers", fvdbDumpVia
, 0, 1);
409 Mgr::RegisterAction("forw_headers", "X-Forwarded-For Request Headers",
414 fvdbCount(hash_table
* hash
, const char *key
)
421 fv
= (fvdb_entry
*)hash_lookup(hash
, key
);
424 fv
= static_cast <fvdb_entry
*>(xcalloc(1, sizeof(fvdb_entry
)));
425 fv
->hash
.key
= xstrdup(key
);
426 hash_join(hash
, &fv
->hash
);
433 fvdbCountVia(const char *key
)
435 fvdbCount(via_table
, key
);
439 fvdbCountForw(const char *key
)
441 fvdbCount(forw_table
, key
);
445 fvdbDumpTable(StoreEntry
* e
, hash_table
* hash
)
455 while ((h
= hash_next(hash
))) {
456 fv
= (fvdb_entry
*) h
;
457 storeAppendPrintf(e
, "%9d %s\n", fv
->n
, hashKeyStr(&fv
->hash
));
462 fvdbDumpVia(StoreEntry
* e
)
464 fvdbDumpTable(e
, via_table
);
468 fvdbDumpForw(StoreEntry
* e
)
470 fvdbDumpTable(e
, forw_table
);
475 fvdbFreeEntry(void *data
)
477 fvdb_entry
*fv
= static_cast <fvdb_entry
*>(data
);
485 hashFreeItems(via_table
, fvdbFreeEntry
);
486 hashFreeMemory(via_table
);
487 via_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
488 hashFreeItems(forw_table
, fvdbFreeEntry
);
489 hashFreeMemory(forw_table
);
490 forw_table
= hash_create((HASHCMP
*) strcmp
, 977, hash4
);
495 #if MULTICAST_MISS_STREAM
497 * From http://www.io.com/~paulhart/game/algorithms/tea.html
499 * size of 'ibuf' must be a multiple of 2.
500 * size of 'key' must be 4.
501 * 'ibuf' is modified in place, encrypted data is written in
502 * network byte order.
505 mcast_encode(unsigned int *ibuf
, size_t isize
, const unsigned int *key
)
510 const unsigned int delta
= 0x9e3779b9;
512 const unsigned int k0
= htonl(key
[0]);
513 const unsigned int k1
= htonl(key
[1]);
514 const unsigned int k2
= htonl(key
[2]);
515 const unsigned int k3
= htonl(key
[3]);
518 for (i
= 0; i
< isize
; i
+= 2) {
520 z
= htonl(ibuf
[i
+ 1]);
523 for (n
= 32; n
; --n
) {
525 y
+= (z
<< 4) + (k0
^ z
) + (sum
^ (z
>> 5)) + k1
;
526 z
+= (y
<< 4) + (k2
^ y
) + (sum
^ (y
>> 5)) + k3
;
530 ibuf
[i
+ 1] = htonl(z
);
538 headersLog(int cs
, int pq
, const HttpRequestMethod
& method
, void *data
)
542 unsigned short magic
= 0;
543 unsigned char M
= (unsigned char) m
;
552 hmask
= rep
->header
.mask
;
554 if (rep
->cache_control
)
555 ccmask
= rep
->cache_control
->mask
;
561 hmask
= req
->header
.mask
;
563 if (req
->cache_control
)
564 ccmask
= req
->cache_control
->mask
;
575 magic
= htons(magic
);
576 ccmask
= htonl(ccmask
);
578 unsigned short S
= 0;
580 S
= static_cast<unsigned short>(rep
->sline
.status());
582 logfileWrite(headerslog
, &magic
, sizeof(magic
));
583 logfileWrite(headerslog
, &M
, sizeof(M
));
584 logfileWrite(headerslog
, &S
, sizeof(S
));
585 logfileWrite(headerslog
, hmask
, sizeof(HttpHeaderMask
));
586 logfileWrite(headerslog
, &ccmask
, sizeof(int));