]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeaderTools.cc
Moved HttpHeaderFieldStat to own header file.
[thirdparty/squid.git] / src / HttpHeaderTools.cc
CommitLineData
7faf2bdb 1/*
7faf2bdb 2 * DEBUG: section 66 HTTP Header Tools
3 * AUTHOR: Alex Rousskov
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
7faf2bdb 7 *
2b6662ba 8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
7faf2bdb 16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
26ac0430 21 *
7faf2bdb 22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26ac0430 26 *
7faf2bdb 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 30 *
7faf2bdb 31 */
32
582c2af2 33#include "squid.h"
c0941a6a 34#include "acl/FilledChecklist.h"
3b07476b 35#include "acl/Gadgets.h"
f4698e0b 36#include "client_side_request.h"
582c2af2 37#include "client_side.h"
f4698e0b 38#include "comm/Connection.h"
27bc2077 39#include "compat/strtoll.h"
f4698e0b 40#include "fde.h"
27bc2077
AJ
41#include "HttpHdrContRange.h"
42#include "HttpHeader.h"
3b07476b
CT
43#include "HttpHeaderTools.h"
44#include "HttpRequest.h"
0eb49b6d 45#include "MemBuf.h"
4d5904f7 46#include "SquidConfig.h"
582c2af2 47#include "Store.h"
28204b3b 48#include "StrList.h"
582c2af2 49
f4698e0b
CT
50#if USE_SSL
51#include "ssl/support.h"
52#endif
582c2af2 53
f4698e0b 54#include <algorithm>
f4698e0b 55#include <string>
21d845b1
FC
56#if HAVE_ERRNO_H
57#include <errno.h>
58#endif
7faf2bdb 59
2246b732 60static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs);
de336bbe 61
de336bbe 62HttpHeaderFieldInfo *
b644367b 63httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count)
7faf2bdb 64{
65 int i;
de336bbe 66 HttpHeaderFieldInfo *table = NULL;
67 assert(attrs && count);
68
69 /* allocate space */
0353e724 70 table = new HttpHeaderFieldInfo[count];
7faf2bdb 71
7faf2bdb 72 for (i = 0; i < count; ++i) {
62e76326 73 const http_hdr_type id = attrs[i].id;
74 HttpHeaderFieldInfo *info = table + id;
75 /* sanity checks */
76 assert(id >= 0 && id < count);
77 assert(attrs[i].name);
0353e724 78 assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */
62e76326 79 /* copy and init fields */
80 info->id = id;
81 info->type = attrs[i].type;
82 info->name = attrs[i].name;
83 assert(info->name.size());
7faf2bdb 84 }
62e76326 85
de336bbe 86 return table;
87}
88
89void
b644367b 90httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count)
de336bbe 91{
92 int i;
62e76326 93
de336bbe 94 for (i = 0; i < count; ++i)
30abd221 95 table[i].name.clean();
62e76326 96
7f4c8dec 97 delete [] table;
7faf2bdb 98}
99
d8b249ef 100void
97474590 101httpHeaderMaskInit(HttpHeaderMask * mask, int value)
d8b249ef 102{
97474590 103 memset(mask, value, sizeof(*mask));
d8b249ef 104}
105
b50e327b 106/** calculates a bit mask of a given array; does not reset mask! */
d8b249ef 107void
8abf232c 108httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count)
7faf2bdb 109{
e6ccf245 110 size_t i;
8abf232c 111 const int * enums = (const int *) http_hdr_type_enums;
d8b249ef 112 assert(mask && enums);
99edd1c3 113 assert(count < sizeof(*mask) * 8); /* check for overflow */
7faf2bdb 114
115 for (i = 0; i < count; ++i) {
62e76326 116 assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */
117 CBIT_SET(*mask, enums[i]);
7faf2bdb 118 }
7faf2bdb 119}
120
2246b732 121/* same as httpHeaderPutStr, but formats the string using snprintf first */
2246b732 122void
eeb423fb 123httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...)
2246b732 124{
9bc73deb 125 va_list args;
126 va_start(args, fmt);
62e76326 127
2246b732 128 httpHeaderPutStrvf(hdr, id, fmt, args);
129 va_end(args);
130}
131
132/* used by httpHeaderPutStrf */
133static void
134httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs)
135{
2246b732 136 MemBuf mb;
2fe7eff9 137 mb.init();
138 mb.vPrintf(fmt, vargs);
a9925b40 139 hdr->putStr(id, mb.buf);
2fe7eff9 140 mb.clean();
2246b732 141}
142
9035d1d5 143/** wrapper arrounf PutContRange */
d192d11f 144void
47f6e231 145httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len)
d192d11f 146{
147 HttpHdrContRange *cr = httpHdrContRangeCreate();
148 assert(hdr && ent_len >= 0);
149 httpHdrContRangeSet(cr, spec, ent_len);
a9925b40 150 hdr->putContRange(cr);
d192d11f 151 httpHdrContRangeDestroy(cr);
152}
153
9035d1d5 154/**
9bc73deb 155 * return true if a given directive is found in at least one of
156 * the "connection" header-fields note: if HDR_PROXY_CONNECTION is
157 * present we ignore HDR_CONNECTION.
99edd1c3 158 */
159int
5999b776 160httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
99edd1c3 161{
30abd221 162 String list;
9bc73deb 163 int res;
164 /* what type of header do we have? */
62e76326 165
95e78500 166#if USE_HTTP_VIOLATIONS
a9925b40 167 if (hdr->has(HDR_PROXY_CONNECTION))
95e78500
AJ
168 list = hdr->getList(HDR_PROXY_CONNECTION);
169 else
170#endif
2e881a6f
A
171 if (hdr->has(HDR_CONNECTION))
172 list = hdr->getList(HDR_CONNECTION);
173 else
174 return 0;
9bc73deb 175
9bc73deb 176 res = strListIsMember(&list, directive, ',');
62e76326 177
30abd221 178 list.clean();
179
9bc73deb 180 return res;
99edd1c3 181}
182
99edd1c3 183
7faf2bdb 184
b50e327b 185/** handy to printf prefixes of potentially very long buffers */
7faf2bdb 186const char *
d8b249ef 187getStringPrefix(const char *str, const char *end)
7faf2bdb 188{
d8b249ef 189#define SHORT_PREFIX_SIZE 512
7faf2bdb 190 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
b644367b 191 const int sz = 1 + (end ? end - str : strlen(str));
d8b249ef 192 xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz);
7faf2bdb 193 return buf;
194}
b5107edb 195
b50e327b 196/**
b5107edb 197 * parses an int field, complains if soemthing went wrong, returns true on
198 * success
199 */
200int
201httpHeaderParseInt(const char *start, int *value)
202{
203 assert(value);
204 *value = atoi(start);
62e76326 205
b6a2f15e 206 if (!*value && !xisdigit(*start)) {
bf8fe701 207 debugs(66, 2, "failed to parse an int header field near '" << start << "'");
62e76326 208 return 0;
b5107edb 209 }
62e76326 210
b5107edb 211 return 1;
212}
213
47f6e231 214int
215httpHeaderParseOffset(const char *start, int64_t * value)
216{
66e255bc 217 errno = 0;
47f6e231 218 int64_t res = strtoll(start, NULL, 10);
219 if (!res && EINVAL == errno) /* maybe not portable? */
26ac0430 220 return 0;
47f6e231 221 *value = res;
222 return 1;
223}
224
b50e327b
AJ
225/**
226 * Parses a quoted-string field (RFC 2616 section 2.2), complains if
43ae1d95 227 * something went wrong, returns non-zero on success.
34460e19 228 * Un-escapes quoted-pair characters found within the string.
229af339 229 * start should point at the first double-quote.
43ae1d95 230 */
231int
34460e19 232httpHeaderParseQuotedString(const char *start, const int len, String *val)
43ae1d95 233{
234 const char *end, *pos;
30abd221 235 val->clean();
9abd1514 236 if (*start != '"') {
5f5ddef6 237 debugs(66, 2, HERE << "failed to parse a quoted-string header field near '" << start << "'");
6d97f5f1 238 return 0;
9abd1514 239 }
43ae1d95 240 pos = start + 1;
241
34460e19 242 while (*pos != '"' && len > (pos-start)) {
5f5ddef6
CT
243
244 if (*pos =='\r') {
95dc7ff4 245 ++pos;
5f5ddef6
CT
246 if ((pos-start) > len || *pos != '\n') {
247 debugs(66, 2, HERE << "failed to parse a quoted-string header field with '\\r' octet " << (start-pos)
248 << " bytes into '" << start << "'");
249 val->clean();
250 return 0;
251 }
252 }
253
254 if (*pos == '\n') {
95dc7ff4 255 ++pos;
5f5ddef6
CT
256 if ( (pos-start) > len || (*pos != ' ' && *pos != '\t')) {
257 debugs(66, 2, HERE << "failed to parse multiline quoted-string header field '" << start << "'");
258 val->clean();
259 return 0;
260 }
261 // TODO: replace the entire LWS with a space
262 val->append(" ");
95dc7ff4 263 ++pos;
5f5ddef6
CT
264 debugs(66, 2, HERE << "len < pos-start => " << len << " < " << (pos-start));
265 continue;
266 }
267
a0133f10 268 bool quoted = (*pos == '\\');
5f5ddef6 269 if (quoted) {
95dc7ff4 270 ++pos;
5f5ddef6
CT
271 if (!*pos || (pos-start) > len) {
272 debugs(66, 2, HERE << "failed to parse a quoted-string header field near '" << start << "'");
273 val->clean();
274 return 0;
275 }
6d97f5f1 276 }
34460e19 277 end = pos;
c4b86597 278 while (end < (start+len) && *end != '\\' && *end != '\"' && (unsigned char)*end > 0x1F && *end != 0x7F)
95dc7ff4 279 ++end;
c4b86597 280 if (((unsigned char)*end <= 0x1F && *end != '\r' && *end != '\n') || *end == 0x7F) {
5f5ddef6 281 debugs(66, 2, HERE << "failed to parse a quoted-string header field with CTL octet " << (start-pos)
34460e19
AJ
282 << " bytes into '" << start << "'");
283 val->clean();
284 return 0;
285 }
6d97f5f1
A
286 val->append(pos, end-pos);
287 pos = end;
43ae1d95 288 }
5f5ddef6
CT
289
290 if (*pos != '\"') {
291 debugs(66, 2, HERE << "failed to parse a quoted-string header field which did not end with \" ");
292 val->clean();
293 return 0;
294 }
9abd1514
HN
295 /* Make sure it's defined even if empty "" */
296 if (!val->defined())
6d97f5f1 297 val->limitInit("", 0);
9abd1514 298 return 1;
43ae1d95 299}
300
b50e327b
AJ
301/**
302 * Checks the anonymizer (header_access) configuration.
4f4611bf 303 *
b50e327b
AJ
304 * \retval 0 Header is explicitly blocked for removal
305 * \retval 1 Header is explicitly allowed
306 * \retval 1 Header has been replaced, the current version can be used.
307 * \retval 1 Header has no access controls to test
6bccf575 308 */
2d72d4fd 309static int
8c01ada0 310httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
6bccf575 311{
e9b1e111 312 int retval;
313
6bccf575 314 /* check with anonymizer tables */
3b07476b 315 HeaderManglers *hms = NULL;
6bccf575 316 assert(e);
8c01ada0 317
318 if (ROR_REQUEST == req_or_rep) {
3b07476b 319 hms = Config.request_header_access;
8c01ada0 320 } else if (ROR_REPLY == req_or_rep) {
3b07476b 321 hms = Config.reply_header_access;
8c01ada0 322 } else {
323 /* error. But let's call it "request". */
3b07476b 324 hms = Config.request_header_access;
8c01ada0 325 }
326
3b07476b
CT
327 /* manglers are not configured for this message kind */
328 if (!hms)
329 return 1;
330
331 const header_mangler *hm = hms->find(*e);
332
b50e327b 333 /* mangler or checklist went away. default allow */
af6a12ee 334 if (!hm || !hm->access_list) {
b50e327b
AJ
335 return 1;
336 }
337
c0941a6a 338 ACLFilledChecklist checklist(hm->access_list, request, NULL);
62e76326 339
2efeb0b7 340 if (checklist.fastCheck() == ACCESS_ALLOWED) {
b50e327b 341 /* aclCheckFast returns true for allow. */
62e76326 342 retval = 1;
a453662f 343 } else if (NULL == hm->replacement) {
62e76326 344 /* It was denied, and we don't have any replacement */
345 retval = 0;
a453662f 346 } else {
62e76326 347 /* It was denied, but we have a replacement. Replace the
348 * header on the fly, and return that the new header
349 * is allowed.
350 */
351 e->value = hm->replacement;
352 retval = 1;
a453662f 353 }
e9b1e111 354
e9b1e111 355 return retval;
6bccf575 356}
357
b50e327b 358/** Mangles headers for a list of headers. */
6bccf575 359void
8c01ada0 360httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
6bccf575 361{
362 HttpHeaderEntry *e;
363 HttpHeaderPos p = HttpHeaderInitPos;
62e76326 364
ba9fb01d 365 int headers_deleted = 0;
a9925b40 366 while ((e = l->getEntry(&p)))
8c01ada0 367 if (0 == httpHdrMangle(e, request, req_or_rep))
ba9fb01d 368 l->delAt(p, headers_deleted);
369
370 if (headers_deleted)
371 l->refreshMask();
6bccf575 372}
5967c0bf 373
3b07476b
CT
374static
375void header_mangler_clean(header_mangler &m)
376{
377 aclDestroyAccessList(&m.access_list);
378 safe_free(m.replacement);
379}
380
381static
382void header_mangler_dump_access(StoreEntry * entry, const char *option,
d3abcb0d 383 const header_mangler &m, const char *name)
3b07476b
CT
384{
385 if (m.access_list != NULL) {
386 storeAppendPrintf(entry, "%s ", option);
387 dump_acl_access(entry, name, m.access_list);
388 }
389}
390
391static
392void header_mangler_dump_replacement(StoreEntry * entry, const char *option,
d3abcb0d 393 const header_mangler &m, const char *name)
3b07476b
CT
394{
395 if (m.replacement)
396 storeAppendPrintf(entry, "%s %s %s\n", option, name, m.replacement);
397}
398
399HeaderManglers::HeaderManglers()
400{
401 memset(known, 0, sizeof(known));
402 memset(&all, 0, sizeof(all));
403}
404
405HeaderManglers::~HeaderManglers()
406{
95dc7ff4 407 for (int i = 0; i < HDR_ENUM_END; ++i)
3b07476b
CT
408 header_mangler_clean(known[i]);
409
410 typedef ManglersByName::iterator MBNI;
411 for (MBNI i = custom.begin(); i != custom.end(); ++i)
412 header_mangler_clean(i->second);
413
414 header_mangler_clean(all);
415}
416
417void
418HeaderManglers::dumpAccess(StoreEntry * entry, const char *name) const
5967c0bf 419{
95dc7ff4 420 for (int i = 0; i < HDR_ENUM_END; ++i) {
3b07476b
CT
421 header_mangler_dump_access(entry, name, known[i],
422 httpHeaderNameById(i));
5967c0bf 423 }
424
3b07476b
CT
425 typedef ManglersByName::const_iterator MBNCI;
426 for (MBNCI i = custom.begin(); i != custom.end(); ++i)
427 header_mangler_dump_access(entry, name, i->second, i->first.c_str());
428
429 header_mangler_dump_access(entry, name, all, "All");
5967c0bf 430}
3b07476b
CT
431
432void
433HeaderManglers::dumpReplacement(StoreEntry * entry, const char *name) const
434{
95dc7ff4 435 for (int i = 0; i < HDR_ENUM_END; ++i) {
3b07476b
CT
436 header_mangler_dump_replacement(entry, name, known[i],
437 httpHeaderNameById(i));
438 }
439
440 typedef ManglersByName::const_iterator MBNCI;
441 for (MBNCI i = custom.begin(); i != custom.end(); ++i) {
442 header_mangler_dump_replacement(entry, name, i->second,
443 i->first.c_str());
444 }
445
446 header_mangler_dump_replacement(entry, name, all, "All");
447}
448
449header_mangler *
450HeaderManglers::track(const char *name)
451{
452 int id = httpHeaderIdByNameDef(name, strlen(name));
453
454 if (id == HDR_BAD_HDR) { // special keyword or a custom header
455 if (strcmp(name, "All") == 0)
456 id = HDR_ENUM_END;
457 else if (strcmp(name, "Other") == 0)
458 id = HDR_OTHER;
459 }
460
461 header_mangler *m = NULL;
462 if (id == HDR_ENUM_END) {
463 m = &all;
d3abcb0d 464 } else if (id == HDR_BAD_HDR) {
3b07476b
CT
465 m = &custom[name];
466 } else {
467 m = &known[id]; // including HDR_OTHER
468 }
469
470 assert(m);
471 return m;
472}
473
474void
475HeaderManglers::setReplacement(const char *name, const char *value)
476{
477 // for backword compatibility, we allow replacements to be configured
478 // for headers w/o access rules, but such replacements are ignored
479 header_mangler *m = track(name);
480
481 safe_free(m->replacement); // overwrite old value if any
482 m->replacement = xstrdup(value);
483}
484
485const header_mangler *
486HeaderManglers::find(const HttpHeaderEntry &e) const
487{
488 // a known header with a configured ACL list
489 if (e.id != HDR_OTHER && 0 <= e.id && e.id < HDR_ENUM_END &&
d3abcb0d 490 known[e.id].access_list)
3b07476b
CT
491 return &known[e.id];
492
493 // a custom header
494 if (e.id == HDR_OTHER) {
495 // does it have an ACL list configured?
496 // Optimize: use a name type that we do not need to convert to here
497 const ManglersByName::const_iterator i = custom.find(e.name.termedBuf());
498 if (i != custom.end())
499 return &i->second;
500 }
501
502 // Next-to-last resort: "Other" rules match any custom header
503 if (e.id == HDR_OTHER && known[HDR_OTHER].access_list)
504 return &known[HDR_OTHER];
505
506 // Last resort: "All" rules match any header
507 if (all.access_list)
508 return &all;
509
510 return NULL;
511}
512
f4698e0b 513void
4bf68cfa 514httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd)
f4698e0b
CT
515{
516 ACLFilledChecklist checklist(NULL, request, NULL);
517
518 for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) {
519 if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) {
520 const char *fieldValue = NULL;
521 MemBuf mb;
522 if (hwa->quoted) {
4bf68cfa 523 if (al != NULL) {
f4698e0b 524 mb.init();
4bf68cfa 525 hwa->valueFormat->assemble(mb, al, 0);
f4698e0b
CT
526 fieldValue = mb.content();
527 }
528 } else {
529 fieldValue = hwa->fieldValue.c_str();
530 }
531
532 if (!fieldValue || fieldValue[0] == '\0')
533 fieldValue = "-";
534
999aa5d2
A
535 HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(),
536 fieldValue);
f4698e0b
CT
537 heads->addEntry(e);
538 }
539 }
540}