]> git.ipfire.org Git - thirdparty/squid.git/blame - src/access_log.cc
consistent formatting
[thirdparty/squid.git] / src / access_log.cc
CommitLineData
f892c2bf 1
2/*
94439e4e 3 * $Id: access_log.cc,v 1.65 2001/01/07 23:36:37 hno 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
94439e4e 185char *
186username_quote(const char *header)
187/* copy of log_quote. Bugs there will be found here */
188{
189 int c;
190 int i;
191 char *buf;
192 char *buf_cursor;
193 if (header == NULL) {
194 buf = xcalloc(1, 1);
195 *buf = '\0';
196 return buf;
197 }
198 buf = xcalloc((strlen(header) * 3) + 1, 1);
199 buf_cursor = buf;
200 /*
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.
203 */
204 while ((c = *(const unsigned char *) header++) != '\0') {
205 if (c == '\r') {
206 *buf_cursor++ = '\\';
207 *buf_cursor++ = 'r';
208 } else if (c == '\n') {
209 *buf_cursor++ = '\\';
210 *buf_cursor++ = 'n';
211 } else if (c <= 0x1F
212 || c >= 0x7F
213 || c == ' ') {
214 *buf_cursor++ = '%';
215 i = c * 2;
216 *buf_cursor++ = c2x[i];
217 *buf_cursor++ = c2x[i + 1];
218 } else {
219 *buf_cursor++ = (char) c;
220 }
221 }
222 *buf_cursor = '\0';
223 return buf;
224}
225
226char *
227accessLogFormatName(const char *name)
228{
229 if (NULL == name)
230 return xcalloc(strlen(dash_str) + 1, 1);
231 return username_quote(name);
232}
233
137ee196 234static void
5673c2e2 235accessLogSquid(AccessLogEntry * al)
f892c2bf 236{
e7b53d5d 237 const char *client = NULL;
94439e4e 238 char *user = NULL;
17a0a4ee 239 if (Config.onoff.log_fqdn)
b22b7951 240 client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
f892c2bf 241 if (client == NULL)
242 client = inet_ntoa(al->cache.caddr);
5673c2e2 243 logfilePrintf(logfile, "%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s",
f892c2bf 244 (int) current_time.tv_sec,
245 (int) current_time.tv_usec / 1000,
246 al->cache.msec,
247 client,
248 log_tags[al->cache.code],
249 al->http.code,
250 al->cache.size,
251 al->private.method_str,
252 al->url,
94439e4e 253 (user = accessLogFormatName(al->cache.authuser ?
254 al->cache.authuser : al->cache.rfc931)),
b4e7f82d 255 al->hier.ping.timedout ? "TIMEOUT_" : "",
f892c2bf 256 hier_strings[al->hier.code],
257 al->hier.host,
258 al->http.content_type);
94439e4e 259 safe_free(user);
f892c2bf 260}
261
137ee196 262static void
5673c2e2 263accessLogCommon(AccessLogEntry * al)
f892c2bf 264{
3a0bd885 265 const char *client = NULL;
94439e4e 266 char *user = NULL;
17a0a4ee 267 if (Config.onoff.log_fqdn)
f892c2bf 268 client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
269 if (client == NULL)
270 client = inet_ntoa(al->cache.caddr);
94439e4e 271 logfilePrintf(logfile, "%s %s %s [%s] \"%s %s HTTP/%d.%d\" %d %d %s:%s",
f892c2bf 272 client,
94439e4e 273 accessLogFormatName(al->cache.rfc931),
274 (user = accessLogFormatName(al->cache.authuser)),
f892c2bf 275 mkhttpdlogtime(&squid_curtime),
276 al->private.method_str,
277 al->url,
ccf44862 278 al->http.version.major, al->http.version.minor,
f892c2bf 279 al->http.code,
280 al->cache.size,
281 log_tags[al->cache.code],
282 hier_strings[al->hier.code]);
94439e4e 283 safe_free(user);
f892c2bf 284}
285
286void
287accessLogLog(AccessLogEntry * al)
288{
f892c2bf 289 if (LogfileStatus != LOG_ENABLE)
290 return;
291 if (al->url == NULL)
292 al->url = dash_str;
293 if (!al->http.content_type || *al->http.content_type == '\0')
294 al->http.content_type = dash_str;
f892c2bf 295 if (al->icp.opcode)
27cd7235 296 al->private.method_str = icp_opcode_str[al->icp.opcode];
f892c2bf 297 else
298 al->private.method_str = RequestMethodStr[al->http.method];
4ea8f861 299 if (al->hier.host[0] == '\0')
300 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
137ee196 301
17a0a4ee 302 if (Config.onoff.common_log)
5673c2e2 303 accessLogCommon(al);
f892c2bf 304 else
5673c2e2 305 accessLogSquid(al);
17a0a4ee 306 if (Config.onoff.log_mime_hdrs) {
f892c2bf 307 char *ereq = log_quote(al->headers.request);
308 char *erep = log_quote(al->headers.reply);
5673c2e2 309 logfilePrintf(logfile, " [%s] [%s]\n", ereq, erep);
f892c2bf 310 safe_free(ereq);
311 safe_free(erep);
137ee196 312 } else {
5673c2e2 313 logfilePrintf(logfile, "\n");
f892c2bf 314 }
5673c2e2 315 logfileFlush(logfile);
e66d7923 316#if MULTICAST_MISS_STREAM
317 if (al->cache.code != LOG_TCP_MISS)
318 (void) 0;
319 else if (al->http.method != METHOD_GET)
320 (void) 0;
321 else if (mcast_miss_fd < 0)
322 (void) 0;
323 else {
324 unsigned int ibuf[365];
325 size_t isize;
326 xstrncpy((char *) ibuf, al->url, 364 * sizeof(int));
327 isize = ((strlen(al->url) + 8) / 8) * 2;
6355b0f5 328 if (isize > 364)
329 isize = 364;
e66d7923 330 mcast_encode((unsigned int *) ibuf, isize,
0bdf2621 331 (const unsigned int *) Config.mcast_miss.encode_key);
e66d7923 332 comm_udp_sendto(mcast_miss_fd,
333 &mcast_miss_to, sizeof(mcast_miss_to),
334 ibuf, isize * sizeof(int));
335 }
336#endif
f892c2bf 337}
338
339void
340accessLogRotate(void)
341{
d21f1c54 342#if FORW_VIA_DB
343 fvdbClear();
344#endif
5673c2e2 345 if (NULL == logfile)
f892c2bf 346 return;
5673c2e2 347 logfileRotate(logfile);
c3609322 348#if HEADERS_LOG
349 logfileRotate(headerslog);
350#endif
f892c2bf 351}
352
353void
354accessLogClose(void)
355{
5673c2e2 356 logfileClose(logfile);
357 logfile = NULL;
c3609322 358#if HEADERS_LOG
359 logfileClose(headerslog);
360 headerslog = NULL;
361#endif
f892c2bf 362}
363
f892c2bf 364void
365e5b34 365hierarchyNote(HierarchyLogEntry * hl,
f892c2bf 366 hier_code code,
43c3424b 367 const char *cache_peer)
f892c2bf 368{
369 assert(hl != NULL);
370 hl->code = code;
43c3424b 371 xstrncpy(hl->host, cache_peer, SQUIDHOSTNAMELEN);
f892c2bf 372}
7a2f978b 373
374void
375accessLogInit(void)
376{
79d39a72 377 assert(sizeof(log_tags) == (LOG_TYPE_MAX + 1) * sizeof(char *));
f0debecb 378 if (strcasecmp(Config.Log.access, "none") == 0)
cce7e679 379 return;
08e8e020 380 logfile = logfileOpen(Config.Log.access, MAX_URL << 1, 1);
5673c2e2 381 LogfileStatus = LOG_ENABLE;
c3609322 382#if HEADERS_LOG
383 headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
384 assert(NULL != headerslog);
385#endif
d21f1c54 386#if FORW_VIA_DB
387 fvdbInit();
388#endif
e66d7923 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,
396 0,
397 Config.Addrs.udp_incoming,
398 Config.mcast_miss.port,
399 COMM_NONBLOCKING,
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",
404 mcast_miss_fd);
7e3ce7b9 405 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
e66d7923 406 if (strlen(Config.mcast_miss.encode_key) < 16)
407 fatal("mcast_encode_key is too short, must be 16 characters");
408 }
409#endif
7a2f978b 410}
c6e7cab0 411
412const char *
413accessLogTime(time_t t)
414{
415 struct tm *tm;
416 static char buf[128];
417 static time_t last_t = 0;
418 if (t != last_t) {
419 tm = localtime(&t);
420 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
421 last_t = t;
422 }
423 return buf;
424}
d21f1c54 425
426
427#if FORW_VIA_DB
1afe05c5 428
d21f1c54 429static void
430fvdbInit(void)
431{
432 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
433 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
749e40bf 434 cachemgrRegister("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
d21f1c54 435 cachemgrRegister("forw_headers", "X-Forwarded-For Request Headers",
1da3b90b 436 fvdbDumpForw, 0, 1);
d21f1c54 437}
438
439static void
1afe05c5 440fvdbCount(hash_table * hash, const char *key)
441{
442 fvdb_entry *fv;
443 if (NULL == hash)
444 return;
445 fv = hash_lookup(hash, key);
446 if (NULL == fv) {
447 fv = xcalloc(1, sizeof(fvdb_entry));
186477c1 448 fv->hash.key = xstrdup(key);
449 hash_join(hash, &fv->hash);
1afe05c5 450 }
451 fv->n++;
d21f1c54 452}
453
454void
455fvdbCountVia(const char *key)
456{
1afe05c5 457 fvdbCount(via_table, key);
d21f1c54 458}
459
460void
461fvdbCountForw(const char *key)
462{
1afe05c5 463 fvdbCount(forw_table, key);
d21f1c54 464}
465
1afe05c5 466static void
467fvdbDumpTable(StoreEntry * e, hash_table * hash)
d21f1c54 468{
1afe05c5 469 hash_link *h;
470 fvdb_entry *fv;
471 if (hash == NULL)
472 return;
0f6bebac 473 hash_first(hash);
b68ee087 474 while ((h = hash_next(hash))) {
1afe05c5 475 fv = (fvdb_entry *) h;
186477c1 476 storeAppendPrintf(e, "%9d %s\n", fv->n, hashKeyStr(&fv->hash));
1afe05c5 477 }
d21f1c54 478}
479
480static void
1afe05c5 481fvdbDumpVia(StoreEntry * e)
d21f1c54 482{
1afe05c5 483 fvdbDumpTable(e, via_table);
d21f1c54 484}
1afe05c5 485
d21f1c54 486static void
1afe05c5 487fvdbDumpForw(StoreEntry * e)
d21f1c54 488{
1afe05c5 489 fvdbDumpTable(e, forw_table);
d21f1c54 490}
491
492static
b644367b 493void
1afe05c5 494fvdbFreeEntry(void *data)
d21f1c54 495{
1afe05c5 496 fvdb_entry *fv = data;
186477c1 497 xfree(fv->hash.key);
1afe05c5 498 xfree(fv);
d21f1c54 499}
500
501static void
502fvdbClear(void)
503{
504 hashFreeItems(via_table, fvdbFreeEntry);
f6cb924d 505 hashFreeMemory(via_table);
506 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 507 hashFreeItems(forw_table, fvdbFreeEntry);
f6cb924d 508 hashFreeMemory(forw_table);
509 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 510}
511
1afe05c5 512#endif
e66d7923 513
514#if MULTICAST_MISS_STREAM
515/*
516 * From http://www.io.com/~paulhart/game/algorithms/tea.html
517 *
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.
522 */
523static void
524mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
525{
526 unsigned int y;
527 unsigned int z;
528 unsigned int sum;
529 const unsigned int delta = 0x9e3779b9;
530 unsigned int n = 32;
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]);
535 int i;
536 for (i = 0; i < isize; i += 2) {
537 y = htonl(ibuf[i]);
538 z = htonl(ibuf[i + 1]);
539 sum = 0;
540 for (n = 32; n; n--) {
541 sum += delta;
542 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
543 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
544 }
545 ibuf[i] = htonl(y);
546 ibuf[i + 1] = htonl(z);
547 }
548}
549
550#endif
c3609322 551
552#if HEADERS_LOG
553void
554headersLog(int cs, int pq, method_t m, void *data)
555{
556 HttpReply *rep;
557 request_t *req;
558 unsigned short magic = 0;
559 unsigned char M = (unsigned char) m;
560 unsigned short S;
561 char *hmask;
562 int ccmask = 0;
563 if (0 == pq) {
564 /* reply */
565 rep = data;
566 req = NULL;
567 magic = 0x0050;
568 hmask = rep->header.mask;
569 if (rep->cache_control)
570 ccmask = rep->cache_control->mask;
571 } else {
572 /* request */
573 req = data;
574 rep = NULL;
575 magic = 0x0051;
576 hmask = req->header.mask;
577 if (req->cache_control)
578 ccmask = req->cache_control->mask;
579 }
580 if (0 == cs) {
581 /* client */
582 magic |= 0x4300;
583 } else {
584 /* server */
585 magic |= 0x5300;
586 }
587 magic = htons(magic);
588 ccmask = htonl(ccmask);
589 if (0 == pq)
590 S = (unsigned short) rep->sline.status;
591 else
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);
599}
600
601#endif