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