]> git.ipfire.org Git - thirdparty/squid.git/blame - src/access_log.cc
DW:
[thirdparty/squid.git] / src / access_log.cc
CommitLineData
f892c2bf 1
2/*
186477c1 3 * $Id: access_log.cc,v 1.60 2000/10/31 23:48:13 wessels Exp $
f892c2bf 4 *
5 * DEBUG: section 46 Access Log
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
f892c2bf 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
efd900cb 15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * 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
5673c2e2 39static void accessLogSquid(AccessLogEntry * al);
40static void accessLogCommon(AccessLogEntry * al);
41static Logfile *logfile = NULL;
c3609322 42#if HEADERS_LOG
43static Logfile *headerslog = NULL;
44#endif
a7c05555 45
e66d7923 46#if MULTICAST_MISS_STREAM
47static int mcast_miss_fd = -1;
48static struct sockaddr_in mcast_miss_to;
49static void mcast_encode(unsigned int *, size_t, const unsigned int *);
50#endif
51
7a2f978b 52const char *log_tags[] =
53{
54 "NONE",
55 "TCP_HIT",
56 "TCP_MISS",
57 "TCP_REFRESH_HIT",
58 "TCP_REF_FAIL_HIT",
59 "TCP_REFRESH_MISS",
ee1679df 60 "TCP_CLIENT_REFRESH_MISS",
7a2f978b 61 "TCP_IMS_HIT",
7a2f978b 62 "TCP_SWAPFAIL_MISS",
63 "TCP_NEGATIVE_HIT",
64 "TCP_MEM_HIT",
79a15e0a 65 "TCP_DENIED",
b540e168 66 "TCP_OFFLINE_HIT",
efd900cb 67#if LOG_TCP_REDIRECTS
68 "TCP_REDIRECT",
69#endif
7a2f978b 70 "UDP_HIT",
7a2f978b 71 "UDP_MISS",
72 "UDP_DENIED",
73 "UDP_INVALID",
74 "UDP_MISS_NOFETCH",
071a3ae7 75 "ICP_QUERY",
7a2f978b 76 "LOG_TYPE_MAX"
77};
78
d21f1c54 79#if FORW_VIA_DB
80typedef struct {
1afe05c5 81 char *key;
82 void *next;
83 int n;
d21f1c54 84} fvdb_entry;
85static hash_table *via_table = NULL;
86static hash_table *forw_table = NULL;
87static void fvdbInit(void);
1afe05c5 88static void fvdbDumpTable(StoreEntry * e, hash_table * hash);
89static void fvdbCount(hash_table * hash, const char *key);
d21f1c54 90static OBJH fvdbDumpVia;
91static OBJH fvdbDumpForw;
92static FREE fvdbFreeEntry;
93static void fvdbClear(void);
94#endif
f892c2bf 95
96static int LogfileStatus = LOG_DISABLE;
f892c2bf 97#define LOG_BUF_SZ (MAX_URL<<2)
f892c2bf 98
99static const char c2x[] =
100"000102030405060708090a0b0c0d0e0f"
101"101112131415161718191a1b1c1d1e1f"
102"202122232425262728292a2b2c2d2e2f"
103"303132333435363738393a3b3c3d3e3f"
104"404142434445464748494a4b4c4d4e4f"
105"505152535455565758595a5b5c5d5e5f"
106"606162636465666768696a6b6c6d6e6f"
107"707172737475767778797a7b7c7d7e7f"
108"808182838485868788898a8b8c8d8e8f"
109"909192939495969798999a9b9c9d9e9f"
110"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
111"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
112"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
113"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
114"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
115"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
116
117/* log_quote -- URL-style encoding on MIME headers. */
118
592da4ec 119char *
f892c2bf 120log_quote(const char *header)
121{
718644ff 122 int c;
123 int i;
124 char *buf;
125 char *buf_cursor;
f892c2bf 126 if (header == NULL) {
127 buf = xcalloc(1, 1);
128 *buf = '\0';
129 return buf;
130 }
131 buf = xcalloc((strlen(header) * 3) + 1, 1);
132 buf_cursor = buf;
133 /*
134 * We escape: \x00-\x1F"#%;<>?{}|\\\\^~`\[\]\x7F-\xFF
135 * which is the default escape list for the CPAN Perl5 URI module
136 * modulo the inclusion of space (x40) to make the raw logs a bit
137 * more readable.
138 */
79d39a72 139 while ((c = *(const unsigned char *) header++) != '\0') {
7e3ce7b9 140#if !OLD_LOG_MIME
141 if (c == '\r') {
142 *buf_cursor++ = '\\';
143 *buf_cursor++ = 'r';
144 } else if (c == '\n') {
145 *buf_cursor++ = '\\';
146 *buf_cursor++ = 'n';
147 } else
148#endif
149 if (c <= 0x1F
150 || c >= 0x7F
151#if OLD_LOG_MIME
152 || c == '"'
153 || c == '#'
154 || c == '%'
155 || c == ';'
156 || c == '<'
157 || c == '>'
158 || c == '?'
159 || c == '{'
160 || c == '}'
161 || c == '|'
162 || c == '\\'
163 || c == '^'
164 || c == '~'
165 || c == '`'
166#endif
167 || c == '['
f892c2bf 168 || c == ']') {
169 *buf_cursor++ = '%';
170 i = c * 2;
171 *buf_cursor++ = c2x[i];
172 *buf_cursor++ = c2x[i + 1];
7e3ce7b9 173#if !OLD_LOG_MIME
174 } else if (c == '\\') {
175 *buf_cursor++ = '\\';
176 *buf_cursor++ = '\\';
177#endif
f892c2bf 178 } else {
79d39a72 179 *buf_cursor++ = (char) c;
f892c2bf 180 }
181 }
182 *buf_cursor = '\0';
183 return buf;
184}
185
137ee196 186static void
5673c2e2 187accessLogSquid(AccessLogEntry * al)
f892c2bf 188{
e7b53d5d 189 const char *client = NULL;
17a0a4ee 190 if (Config.onoff.log_fqdn)
b22b7951 191 client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
f892c2bf 192 if (client == NULL)
193 client = inet_ntoa(al->cache.caddr);
5673c2e2 194 logfilePrintf(logfile, "%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s",
f892c2bf 195 (int) current_time.tv_sec,
196 (int) current_time.tv_usec / 1000,
197 al->cache.msec,
198 client,
199 log_tags[al->cache.code],
200 al->http.code,
201 al->cache.size,
202 al->private.method_str,
203 al->url,
204 al->cache.ident,
b4e7f82d 205 al->hier.ping.timedout ? "TIMEOUT_" : "",
f892c2bf 206 hier_strings[al->hier.code],
207 al->hier.host,
208 al->http.content_type);
209}
210
137ee196 211static void
5673c2e2 212accessLogCommon(AccessLogEntry * al)
f892c2bf 213{
3a0bd885 214 const char *client = NULL;
17a0a4ee 215 if (Config.onoff.log_fqdn)
f892c2bf 216 client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
217 if (client == NULL)
218 client = inet_ntoa(al->cache.caddr);
5673c2e2 219 logfilePrintf(logfile, "%s %s - [%s] \"%s %s HTTP/%.1f\" %d %d %s:%s",
f892c2bf 220 client,
221 al->cache.ident,
222 mkhttpdlogtime(&squid_curtime),
223 al->private.method_str,
224 al->url,
c68e9c6b 225 al->http.version,
f892c2bf 226 al->http.code,
227 al->cache.size,
228 log_tags[al->cache.code],
229 hier_strings[al->hier.code]);
230}
231
232void
233accessLogLog(AccessLogEntry * al)
234{
3a459d9d 235 LOCAL_ARRAY(char, ident_buf, USER_IDENT_SZ);
137ee196 236
f892c2bf 237 if (LogfileStatus != LOG_ENABLE)
238 return;
239 if (al->url == NULL)
240 al->url = dash_str;
241 if (!al->http.content_type || *al->http.content_type == '\0')
242 al->http.content_type = dash_str;
3a0bd885 243 if (!al->cache.ident || *al->cache.ident == '\0') {
3a459d9d 244 al->cache.ident = dash_str;
245 } else {
246 xstrncpy(ident_buf, rfc1738_escape(al->cache.ident), USER_IDENT_SZ);
247 al->cache.ident = ident_buf;
3a0bd885 248 }
f892c2bf 249 if (al->icp.opcode)
27cd7235 250 al->private.method_str = icp_opcode_str[al->icp.opcode];
f892c2bf 251 else
252 al->private.method_str = RequestMethodStr[al->http.method];
4ea8f861 253 if (al->hier.host[0] == '\0')
254 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
137ee196 255
17a0a4ee 256 if (Config.onoff.common_log)
5673c2e2 257 accessLogCommon(al);
f892c2bf 258 else
5673c2e2 259 accessLogSquid(al);
17a0a4ee 260 if (Config.onoff.log_mime_hdrs) {
f892c2bf 261 char *ereq = log_quote(al->headers.request);
262 char *erep = log_quote(al->headers.reply);
5673c2e2 263 logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
f892c2bf 264 safe_free(ereq);
265 safe_free(erep);
137ee196 266 } else {
5673c2e2 267 logfilePrintf(logfile, "\n");
f892c2bf 268 }
5673c2e2 269 logfileFlush(logfile);
e66d7923 270#if MULTICAST_MISS_STREAM
271 if (al->cache.code != LOG_TCP_MISS)
272 (void) 0;
273 else if (al->http.method != METHOD_GET)
274 (void) 0;
275 else if (mcast_miss_fd < 0)
276 (void) 0;
277 else {
278 unsigned int ibuf[365];
279 size_t isize;
280 xstrncpy((char *) ibuf, al->url, 364 * sizeof(int));
281 isize = ((strlen(al->url) + 8) / 8) * 2;
6355b0f5 282 if (isize > 364)
283 isize = 364;
e66d7923 284 mcast_encode((unsigned int *) ibuf, isize,
0bdf2621 285 (const unsigned int *) Config.mcast_miss.encode_key);
e66d7923 286 comm_udp_sendto(mcast_miss_fd,
287 &mcast_miss_to, sizeof(mcast_miss_to),
288 ibuf, isize * sizeof(int));
289 }
290#endif
f892c2bf 291}
292
293void
294accessLogRotate(void)
295{
d21f1c54 296#if FORW_VIA_DB
297 fvdbClear();
298#endif
5673c2e2 299 if (NULL == logfile)
f892c2bf 300 return;
5673c2e2 301 logfileRotate(logfile);
c3609322 302#if HEADERS_LOG
303 logfileRotate(headerslog);
304#endif
f892c2bf 305}
306
307void
308accessLogClose(void)
309{
5673c2e2 310 logfileClose(logfile);
311 logfile = NULL;
c3609322 312#if HEADERS_LOG
313 logfileClose(headerslog);
314 headerslog = NULL;
315#endif
f892c2bf 316}
317
f892c2bf 318void
365e5b34 319hierarchyNote(HierarchyLogEntry * hl,
f892c2bf 320 hier_code code,
43c3424b 321 const char *cache_peer)
f892c2bf 322{
323 assert(hl != NULL);
324 hl->code = code;
43c3424b 325 xstrncpy(hl->host, cache_peer, SQUIDHOSTNAMELEN);
f892c2bf 326}
7a2f978b 327
328void
329accessLogInit(void)
330{
79d39a72 331 assert(sizeof(log_tags) == (LOG_TYPE_MAX + 1) * sizeof(char *));
08e8e020 332 logfile = logfileOpen(Config.Log.access, MAX_URL << 1, 1);
5673c2e2 333 LogfileStatus = LOG_ENABLE;
c3609322 334#if HEADERS_LOG
335 headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
336 assert(NULL != headerslog);
337#endif
d21f1c54 338#if FORW_VIA_DB
339 fvdbInit();
340#endif
e66d7923 341#if MULTICAST_MISS_STREAM
342 if (Config.mcast_miss.addr.s_addr != no_addr.s_addr) {
343 memset(&mcast_miss_to, '\0', sizeof(mcast_miss_to));
344 mcast_miss_to.sin_family = AF_INET;
345 mcast_miss_to.sin_port = htons(Config.mcast_miss.port);
346 mcast_miss_to.sin_addr.s_addr = Config.mcast_miss.addr.s_addr;
347 mcast_miss_fd = comm_open(SOCK_DGRAM,
348 0,
349 Config.Addrs.udp_incoming,
350 Config.mcast_miss.port,
351 COMM_NONBLOCKING,
352 "Multicast Miss Stream");
353 if (mcast_miss_fd < 0)
354 fatal("Cannot open Multicast Miss Stream Socket");
355 debug(46, 1) ("Multicast Miss Stream Socket opened on FD %d\n",
356 mcast_miss_fd);
7e3ce7b9 357 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
e66d7923 358 if (strlen(Config.mcast_miss.encode_key) < 16)
359 fatal("mcast_encode_key is too short, must be 16 characters");
360 }
361#endif
7a2f978b 362}
c6e7cab0 363
364const char *
365accessLogTime(time_t t)
366{
367 struct tm *tm;
368 static char buf[128];
369 static time_t last_t = 0;
370 if (t != last_t) {
371 tm = localtime(&t);
372 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
373 last_t = t;
374 }
375 return buf;
376}
d21f1c54 377
378
379#if FORW_VIA_DB
1afe05c5 380
d21f1c54 381static void
382fvdbInit(void)
383{
384 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
385 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
749e40bf 386 cachemgrRegister("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
d21f1c54 387 cachemgrRegister("forw_headers", "X-Forwarded-For Request Headers",
1da3b90b 388 fvdbDumpForw, 0, 1);
d21f1c54 389}
390
391static void
1afe05c5 392fvdbCount(hash_table * hash, const char *key)
393{
394 fvdb_entry *fv;
395 if (NULL == hash)
396 return;
397 fv = hash_lookup(hash, key);
398 if (NULL == fv) {
399 fv = xcalloc(1, sizeof(fvdb_entry));
186477c1 400 fv->hash.key = xstrdup(key);
401 hash_join(hash, &fv->hash);
1afe05c5 402 }
403 fv->n++;
d21f1c54 404}
405
406void
407fvdbCountVia(const char *key)
408{
1afe05c5 409 fvdbCount(via_table, key);
d21f1c54 410}
411
412void
413fvdbCountForw(const char *key)
414{
1afe05c5 415 fvdbCount(forw_table, key);
d21f1c54 416}
417
1afe05c5 418static void
419fvdbDumpTable(StoreEntry * e, hash_table * hash)
d21f1c54 420{
1afe05c5 421 hash_link *h;
422 fvdb_entry *fv;
423 if (hash == NULL)
424 return;
0f6bebac 425 hash_first(hash);
b68ee087 426 while ((h = hash_next(hash))) {
1afe05c5 427 fv = (fvdb_entry *) h;
186477c1 428 storeAppendPrintf(e, "%9d %s\n", fv->n, hashKeyStr(&fv->hash));
1afe05c5 429 }
d21f1c54 430}
431
432static void
1afe05c5 433fvdbDumpVia(StoreEntry * e)
d21f1c54 434{
1afe05c5 435 fvdbDumpTable(e, via_table);
d21f1c54 436}
1afe05c5 437
d21f1c54 438static void
1afe05c5 439fvdbDumpForw(StoreEntry * e)
d21f1c54 440{
1afe05c5 441 fvdbDumpTable(e, forw_table);
d21f1c54 442}
443
444static
b644367b 445void
1afe05c5 446fvdbFreeEntry(void *data)
d21f1c54 447{
1afe05c5 448 fvdb_entry *fv = data;
186477c1 449 xfree(fv->hash.key);
1afe05c5 450 xfree(fv);
d21f1c54 451}
452
453static void
454fvdbClear(void)
455{
456 hashFreeItems(via_table, fvdbFreeEntry);
f6cb924d 457 hashFreeMemory(via_table);
458 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 459 hashFreeItems(forw_table, fvdbFreeEntry);
f6cb924d 460 hashFreeMemory(forw_table);
461 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 462}
463
1afe05c5 464#endif
e66d7923 465
466#if MULTICAST_MISS_STREAM
467/*
468 * From http://www.io.com/~paulhart/game/algorithms/tea.html
469 *
470 * size of 'ibuf' must be a multiple of 2.
471 * size of 'key' must be 4.
472 * 'ibuf' is modified in place, encrypted data is written in
473 * network byte order.
474 */
475static void
476mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
477{
478 unsigned int y;
479 unsigned int z;
480 unsigned int sum;
481 const unsigned int delta = 0x9e3779b9;
482 unsigned int n = 32;
483 const unsigned int k0 = htonl(key[0]);
484 const unsigned int k1 = htonl(key[1]);
485 const unsigned int k2 = htonl(key[2]);
486 const unsigned int k3 = htonl(key[3]);
487 int i;
488 for (i = 0; i < isize; i += 2) {
489 y = htonl(ibuf[i]);
490 z = htonl(ibuf[i + 1]);
491 sum = 0;
492 for (n = 32; n; n--) {
493 sum += delta;
494 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
495 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
496 }
497 ibuf[i] = htonl(y);
498 ibuf[i + 1] = htonl(z);
499 }
500}
501
502#endif
c3609322 503
504#if HEADERS_LOG
505void
506headersLog(int cs, int pq, method_t m, void *data)
507{
508 HttpReply *rep;
509 request_t *req;
510 unsigned short magic = 0;
511 unsigned char M = (unsigned char) m;
512 unsigned short S;
513 char *hmask;
514 int ccmask = 0;
515 if (0 == pq) {
516 /* reply */
517 rep = data;
518 req = NULL;
519 magic = 0x0050;
520 hmask = rep->header.mask;
521 if (rep->cache_control)
522 ccmask = rep->cache_control->mask;
523 } else {
524 /* request */
525 req = data;
526 rep = NULL;
527 magic = 0x0051;
528 hmask = req->header.mask;
529 if (req->cache_control)
530 ccmask = req->cache_control->mask;
531 }
532 if (0 == cs) {
533 /* client */
534 magic |= 0x4300;
535 } else {
536 /* server */
537 magic |= 0x5300;
538 }
539 magic = htons(magic);
540 ccmask = htonl(ccmask);
541 if (0 == pq)
542 S = (unsigned short) rep->sline.status;
543 else
544 S = (unsigned short) HTTP_STATUS_NONE;
545 logfileWrite(headerslog, &magic, sizeof(magic));
546 logfileWrite(headerslog, &M, sizeof(M));
547 logfileWrite(headerslog, &S, sizeof(S));
548 logfileWrite(headerslog, hmask, sizeof(HttpHeaderMask));
549 logfileWrite(headerslog, &ccmask, sizeof(int));
550 logfileFlush(headerslog);
551}
552
553#endif