]> git.ipfire.org Git - thirdparty/squid.git/blame - src/access_log.cc
s/MEM_ACL_PROXY_AUTH_DATA/MEM_ACL_USER_DATA/
[thirdparty/squid.git] / src / access_log.cc
CommitLineData
f892c2bf 1
2/*
f0debecb 3 * $Id: access_log.cc,v 1.64 2001/01/02 00:09:55 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 {
6c40d272 81 hash_link hash;
1afe05c5 82 int n;
d21f1c54 83} fvdb_entry;
84static hash_table *via_table = NULL;
85static hash_table *forw_table = NULL;
86static void fvdbInit(void);
1afe05c5 87static void fvdbDumpTable(StoreEntry * e, hash_table * hash);
88static void fvdbCount(hash_table * hash, const char *key);
d21f1c54 89static OBJH fvdbDumpVia;
90static OBJH fvdbDumpForw;
91static FREE fvdbFreeEntry;
92static void fvdbClear(void);
93#endif
f892c2bf 94
95static int LogfileStatus = LOG_DISABLE;
f892c2bf 96#define LOG_BUF_SZ (MAX_URL<<2)
f892c2bf 97
98static 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";
115
116/* log_quote -- URL-style encoding on MIME headers. */
117
592da4ec 118char *
f892c2bf 119log_quote(const char *header)
120{
718644ff 121 int c;
122 int i;
123 char *buf;
124 char *buf_cursor;
f892c2bf 125 if (header == NULL) {
126 buf = xcalloc(1, 1);
127 *buf = '\0';
128 return buf;
129 }
130 buf = xcalloc((strlen(header) * 3) + 1, 1);
131 buf_cursor = buf;
132 /*
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
136 * more readable.
137 */
79d39a72 138 while ((c = *(const unsigned char *) header++) != '\0') {
7e3ce7b9 139#if !OLD_LOG_MIME
140 if (c == '\r') {
141 *buf_cursor++ = '\\';
142 *buf_cursor++ = 'r';
143 } else if (c == '\n') {
144 *buf_cursor++ = '\\';
145 *buf_cursor++ = 'n';
146 } else
147#endif
148 if (c <= 0x1F
149 || c >= 0x7F
150#if OLD_LOG_MIME
151 || c == '"'
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#endif
166 || c == '['
f892c2bf 167 || c == ']') {
168 *buf_cursor++ = '%';
169 i = c * 2;
170 *buf_cursor++ = c2x[i];
171 *buf_cursor++ = c2x[i + 1];
7e3ce7b9 172#if !OLD_LOG_MIME
173 } else if (c == '\\') {
174 *buf_cursor++ = '\\';
175 *buf_cursor++ = '\\';
176#endif
f892c2bf 177 } else {
79d39a72 178 *buf_cursor++ = (char) c;
f892c2bf 179 }
180 }
181 *buf_cursor = '\0';
182 return buf;
183}
184
137ee196 185static void
5673c2e2 186accessLogSquid(AccessLogEntry * al)
f892c2bf 187{
e7b53d5d 188 const char *client = NULL;
17a0a4ee 189 if (Config.onoff.log_fqdn)
b22b7951 190 client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
f892c2bf 191 if (client == NULL)
192 client = inet_ntoa(al->cache.caddr);
5673c2e2 193 logfilePrintf(logfile, "%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s",
f892c2bf 194 (int) current_time.tv_sec,
195 (int) current_time.tv_usec / 1000,
196 al->cache.msec,
197 client,
198 log_tags[al->cache.code],
199 al->http.code,
200 al->cache.size,
201 al->private.method_str,
202 al->url,
203 al->cache.ident,
b4e7f82d 204 al->hier.ping.timedout ? "TIMEOUT_" : "",
f892c2bf 205 hier_strings[al->hier.code],
206 al->hier.host,
207 al->http.content_type);
208}
209
137ee196 210static void
5673c2e2 211accessLogCommon(AccessLogEntry * al)
f892c2bf 212{
3a0bd885 213 const char *client = NULL;
17a0a4ee 214 if (Config.onoff.log_fqdn)
f892c2bf 215 client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
216 if (client == NULL)
217 client = inet_ntoa(al->cache.caddr);
ccf44862 218 logfilePrintf(logfile, "%s %s - [%s] \"%s %s HTTP/%d.%d\" %d %d %s:%s",
f892c2bf 219 client,
220 al->cache.ident,
221 mkhttpdlogtime(&squid_curtime),
222 al->private.method_str,
223 al->url,
ccf44862 224 al->http.version.major, al->http.version.minor,
f892c2bf 225 al->http.code,
226 al->cache.size,
227 log_tags[al->cache.code],
228 hier_strings[al->hier.code]);
229}
230
231void
232accessLogLog(AccessLogEntry * al)
233{
3a459d9d 234 LOCAL_ARRAY(char, ident_buf, USER_IDENT_SZ);
137ee196 235
f892c2bf 236 if (LogfileStatus != LOG_ENABLE)
237 return;
238 if (al->url == NULL)
239 al->url = dash_str;
240 if (!al->http.content_type || *al->http.content_type == '\0')
241 al->http.content_type = dash_str;
3a0bd885 242 if (!al->cache.ident || *al->cache.ident == '\0') {
3a459d9d 243 al->cache.ident = dash_str;
244 } else {
245 xstrncpy(ident_buf, rfc1738_escape(al->cache.ident), USER_IDENT_SZ);
246 al->cache.ident = ident_buf;
3a0bd885 247 }
f892c2bf 248 if (al->icp.opcode)
27cd7235 249 al->private.method_str = icp_opcode_str[al->icp.opcode];
f892c2bf 250 else
251 al->private.method_str = RequestMethodStr[al->http.method];
4ea8f861 252 if (al->hier.host[0] == '\0')
253 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
137ee196 254
17a0a4ee 255 if (Config.onoff.common_log)
5673c2e2 256 accessLogCommon(al);
f892c2bf 257 else
5673c2e2 258 accessLogSquid(al);
17a0a4ee 259 if (Config.onoff.log_mime_hdrs) {
f892c2bf 260 char *ereq = log_quote(al->headers.request);
261 char *erep = log_quote(al->headers.reply);
5673c2e2 262 logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
f892c2bf 263 safe_free(ereq);
264 safe_free(erep);
137ee196 265 } else {
5673c2e2 266 logfilePrintf(logfile, "\n");
f892c2bf 267 }
5673c2e2 268 logfileFlush(logfile);
e66d7923 269#if MULTICAST_MISS_STREAM
270 if (al->cache.code != LOG_TCP_MISS)
271 (void) 0;
272 else if (al->http.method != METHOD_GET)
273 (void) 0;
274 else if (mcast_miss_fd < 0)
275 (void) 0;
276 else {
277 unsigned int ibuf[365];
278 size_t isize;
279 xstrncpy((char *) ibuf, al->url, 364 * sizeof(int));
280 isize = ((strlen(al->url) + 8) / 8) * 2;
6355b0f5 281 if (isize > 364)
282 isize = 364;
e66d7923 283 mcast_encode((unsigned int *) ibuf, isize,
0bdf2621 284 (const unsigned int *) Config.mcast_miss.encode_key);
e66d7923 285 comm_udp_sendto(mcast_miss_fd,
286 &mcast_miss_to, sizeof(mcast_miss_to),
287 ibuf, isize * sizeof(int));
288 }
289#endif
f892c2bf 290}
291
292void
293accessLogRotate(void)
294{
d21f1c54 295#if FORW_VIA_DB
296 fvdbClear();
297#endif
5673c2e2 298 if (NULL == logfile)
f892c2bf 299 return;
5673c2e2 300 logfileRotate(logfile);
c3609322 301#if HEADERS_LOG
302 logfileRotate(headerslog);
303#endif
f892c2bf 304}
305
306void
307accessLogClose(void)
308{
5673c2e2 309 logfileClose(logfile);
310 logfile = NULL;
c3609322 311#if HEADERS_LOG
312 logfileClose(headerslog);
313 headerslog = NULL;
314#endif
f892c2bf 315}
316
f892c2bf 317void
365e5b34 318hierarchyNote(HierarchyLogEntry * hl,
f892c2bf 319 hier_code code,
43c3424b 320 const char *cache_peer)
f892c2bf 321{
322 assert(hl != NULL);
323 hl->code = code;
43c3424b 324 xstrncpy(hl->host, cache_peer, SQUIDHOSTNAMELEN);
f892c2bf 325}
7a2f978b 326
327void
328accessLogInit(void)
329{
79d39a72 330 assert(sizeof(log_tags) == (LOG_TYPE_MAX + 1) * sizeof(char *));
f0debecb 331 if (strcasecmp(Config.Log.access, "none") == 0)
cce7e679 332 return;
08e8e020 333 logfile = logfileOpen(Config.Log.access, MAX_URL << 1, 1);
5673c2e2 334 LogfileStatus = LOG_ENABLE;
c3609322 335#if HEADERS_LOG
336 headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
337 assert(NULL != headerslog);
338#endif
d21f1c54 339#if FORW_VIA_DB
340 fvdbInit();
341#endif
e66d7923 342#if MULTICAST_MISS_STREAM
343 if (Config.mcast_miss.addr.s_addr != no_addr.s_addr) {
344 memset(&mcast_miss_to, '\0', sizeof(mcast_miss_to));
345 mcast_miss_to.sin_family = AF_INET;
346 mcast_miss_to.sin_port = htons(Config.mcast_miss.port);
347 mcast_miss_to.sin_addr.s_addr = Config.mcast_miss.addr.s_addr;
348 mcast_miss_fd = comm_open(SOCK_DGRAM,
349 0,
350 Config.Addrs.udp_incoming,
351 Config.mcast_miss.port,
352 COMM_NONBLOCKING,
353 "Multicast Miss Stream");
354 if (mcast_miss_fd < 0)
355 fatal("Cannot open Multicast Miss Stream Socket");
356 debug(46, 1) ("Multicast Miss Stream Socket opened on FD %d\n",
357 mcast_miss_fd);
7e3ce7b9 358 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
e66d7923 359 if (strlen(Config.mcast_miss.encode_key) < 16)
360 fatal("mcast_encode_key is too short, must be 16 characters");
361 }
362#endif
7a2f978b 363}
c6e7cab0 364
365const char *
366accessLogTime(time_t t)
367{
368 struct tm *tm;
369 static char buf[128];
370 static time_t last_t = 0;
371 if (t != last_t) {
372 tm = localtime(&t);
373 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
374 last_t = t;
375 }
376 return buf;
377}
d21f1c54 378
379
380#if FORW_VIA_DB
1afe05c5 381
d21f1c54 382static void
383fvdbInit(void)
384{
385 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
386 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
749e40bf 387 cachemgrRegister("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
d21f1c54 388 cachemgrRegister("forw_headers", "X-Forwarded-For Request Headers",
1da3b90b 389 fvdbDumpForw, 0, 1);
d21f1c54 390}
391
392static void
1afe05c5 393fvdbCount(hash_table * hash, const char *key)
394{
395 fvdb_entry *fv;
396 if (NULL == hash)
397 return;
398 fv = hash_lookup(hash, key);
399 if (NULL == fv) {
400 fv = xcalloc(1, sizeof(fvdb_entry));
186477c1 401 fv->hash.key = xstrdup(key);
402 hash_join(hash, &fv->hash);
1afe05c5 403 }
404 fv->n++;
d21f1c54 405}
406
407void
408fvdbCountVia(const char *key)
409{
1afe05c5 410 fvdbCount(via_table, key);
d21f1c54 411}
412
413void
414fvdbCountForw(const char *key)
415{
1afe05c5 416 fvdbCount(forw_table, key);
d21f1c54 417}
418
1afe05c5 419static void
420fvdbDumpTable(StoreEntry * e, hash_table * hash)
d21f1c54 421{
1afe05c5 422 hash_link *h;
423 fvdb_entry *fv;
424 if (hash == NULL)
425 return;
0f6bebac 426 hash_first(hash);
b68ee087 427 while ((h = hash_next(hash))) {
1afe05c5 428 fv = (fvdb_entry *) h;
186477c1 429 storeAppendPrintf(e, "%9d %s\n", fv->n, hashKeyStr(&fv->hash));
1afe05c5 430 }
d21f1c54 431}
432
433static void
1afe05c5 434fvdbDumpVia(StoreEntry * e)
d21f1c54 435{
1afe05c5 436 fvdbDumpTable(e, via_table);
d21f1c54 437}
1afe05c5 438
d21f1c54 439static void
1afe05c5 440fvdbDumpForw(StoreEntry * e)
d21f1c54 441{
1afe05c5 442 fvdbDumpTable(e, forw_table);
d21f1c54 443}
444
445static
b644367b 446void
1afe05c5 447fvdbFreeEntry(void *data)
d21f1c54 448{
1afe05c5 449 fvdb_entry *fv = data;
186477c1 450 xfree(fv->hash.key);
1afe05c5 451 xfree(fv);
d21f1c54 452}
453
454static void
455fvdbClear(void)
456{
457 hashFreeItems(via_table, fvdbFreeEntry);
f6cb924d 458 hashFreeMemory(via_table);
459 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 460 hashFreeItems(forw_table, fvdbFreeEntry);
f6cb924d 461 hashFreeMemory(forw_table);
462 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 463}
464
1afe05c5 465#endif
e66d7923 466
467#if MULTICAST_MISS_STREAM
468/*
469 * From http://www.io.com/~paulhart/game/algorithms/tea.html
470 *
471 * size of 'ibuf' must be a multiple of 2.
472 * size of 'key' must be 4.
473 * 'ibuf' is modified in place, encrypted data is written in
474 * network byte order.
475 */
476static void
477mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
478{
479 unsigned int y;
480 unsigned int z;
481 unsigned int sum;
482 const unsigned int delta = 0x9e3779b9;
483 unsigned int n = 32;
484 const unsigned int k0 = htonl(key[0]);
485 const unsigned int k1 = htonl(key[1]);
486 const unsigned int k2 = htonl(key[2]);
487 const unsigned int k3 = htonl(key[3]);
488 int i;
489 for (i = 0; i < isize; i += 2) {
490 y = htonl(ibuf[i]);
491 z = htonl(ibuf[i + 1]);
492 sum = 0;
493 for (n = 32; n; n--) {
494 sum += delta;
495 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
496 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
497 }
498 ibuf[i] = htonl(y);
499 ibuf[i + 1] = htonl(z);
500 }
501}
502
503#endif
c3609322 504
505#if HEADERS_LOG
506void
507headersLog(int cs, int pq, method_t m, void *data)
508{
509 HttpReply *rep;
510 request_t *req;
511 unsigned short magic = 0;
512 unsigned char M = (unsigned char) m;
513 unsigned short S;
514 char *hmask;
515 int ccmask = 0;
516 if (0 == pq) {
517 /* reply */
518 rep = data;
519 req = NULL;
520 magic = 0x0050;
521 hmask = rep->header.mask;
522 if (rep->cache_control)
523 ccmask = rep->cache_control->mask;
524 } else {
525 /* request */
526 req = data;
527 rep = NULL;
528 magic = 0x0051;
529 hmask = req->header.mask;
530 if (req->cache_control)
531 ccmask = req->cache_control->mask;
532 }
533 if (0 == cs) {
534 /* client */
535 magic |= 0x4300;
536 } else {
537 /* server */
538 magic |= 0x5300;
539 }
540 magic = htons(magic);
541 ccmask = htonl(ccmask);
542 if (0 == pq)
543 S = (unsigned short) rep->sline.status;
544 else
545 S = (unsigned short) HTTP_STATUS_NONE;
546 logfileWrite(headerslog, &magic, sizeof(magic));
547 logfileWrite(headerslog, &M, sizeof(M));
548 logfileWrite(headerslog, &S, sizeof(S));
549 logfileWrite(headerslog, hmask, sizeof(HttpHeaderMask));
550 logfileWrite(headerslog, &ccmask, sizeof(int));
551 logfileFlush(headerslog);
552}
553
554#endif