]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http/ContentLengthInterpreter.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / http / ContentLengthInterpreter.cc
CommitLineData
a1b9ec20 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
a1b9ec20
AR
3 *
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.
7 */
8
9/* DEBUG: section 55 HTTP Header */
10
11#include "squid.h"
12#include "base/CharacterSet.h"
13#include "Debug.h"
14#include "http/ContentLengthInterpreter.h"
15#include "http/one/Parser.h"
16#include "HttpHeaderTools.h"
17#include "SquidConfig.h"
18#include "SquidString.h"
19#include "StrList.h"
20
21Http::ContentLengthInterpreter::ContentLengthInterpreter(const int aDebugLevel):
22 value(-1),
23 headerWideProblem(nullptr),
24 debugLevel(aDebugLevel),
25 sawBad(false),
26 needsSanitizing(false),
27 sawGood(false)
28{
29}
30
31/// checks whether all characters after the Content-Length are allowed
32bool
33Http::ContentLengthInterpreter::goodSuffix(const char *suffix, const char * const end) const
34{
35 // optimize for the common case that does not need delimiters
36 if (suffix == end)
37 return true;
38
39 for (const CharacterSet &delimiters = Http::One::Parser::DelimiterCharacters();
40 suffix < end; ++suffix) {
41 if (!delimiters[*suffix])
42 return false;
43 }
44 // needsSanitizing = true; // TODO: Always remove trailing whitespace?
45 return true; // including empty suffix
46}
47
48/// handles a single-token Content-Length value
49/// rawValue null-termination requirements are those of httpHeaderParseOffset()
50bool
51Http::ContentLengthInterpreter::checkValue(const char *rawValue, const int valueSize)
52{
53 Must(!sawBad);
54
55 int64_t latestValue = -1;
56 char *suffix = nullptr;
57 // TODO: Handle malformed values with leading signs (e.g., "-0" or "+1").
58 if (!httpHeaderParseOffset(rawValue, &latestValue, &suffix)) {
59 debugs(55, DBG_IMPORTANT, "WARNING: Malformed" << Raw("Content-Length", rawValue, valueSize));
60 sawBad = true;
61 return false;
62 }
63
64 if (latestValue < 0) {
65 debugs(55, debugLevel, "WARNING: Negative" << Raw("Content-Length", rawValue, valueSize));
66 sawBad = true;
67 return false;
68 }
69
70 // check for garbage after the number
71 if (!goodSuffix(suffix, rawValue + valueSize)) {
72 debugs(55, debugLevel, "WARNING: Trailing garbage in" << Raw("Content-Length", rawValue, valueSize));
73 sawBad = true;
74 return false;
75 }
76
77 if (sawGood) {
78 /* we have found at least two, possibly identical values */
79
80 needsSanitizing = true; // replace identical values with a single value
81
82 const bool conflicting = value != latestValue;
83 if (conflicting)
84 headerWideProblem = "Conflicting"; // overwrite any lesser problem
85 else if (!headerWideProblem) // preserve a possibly worse problem
86 headerWideProblem = "Duplicate";
87
88 // with relaxed_header_parser, identical values are permitted
89 sawBad = !Config.onoff.relaxed_header_parser || conflicting;
90 return false; // conflicting or duplicate
91 }
92
93 sawGood = true;
94 value = latestValue;
95 return true;
96}
97
98/// handles Content-Length: a, b, c
99bool
100Http::ContentLengthInterpreter::checkList(const String &list)
101{
102 Must(!sawBad);
103
104 if (!Config.onoff.relaxed_header_parser) {
105 debugs(55, debugLevel, "WARNING: List-like" << Raw("Content-Length", list.rawBuf(), list.size()));
106 sawBad = true;
107 return false;
108 }
109
110 needsSanitizing = true; // remove extra commas (at least)
111
112 const char *pos = nullptr;
113 const char *item = nullptr;;
114 int ilen = -1;
115 while (strListGetItem(&list, ',', &item, &ilen, &pos)) {
116 if (!checkValue(item, ilen) && sawBad)
117 break;
118 // keep going after a duplicate value to find conflicting ones
119 }
120 return false; // no need to keep this list field; it will be sanitized away
121}
122
123bool
124Http::ContentLengthInterpreter::checkField(const String &rawValue)
125{
126 if (sawBad)
127 return false; // one rotten apple is enough to spoil all of them
128
129 // TODO: Optimize by always parsing the first integer first.
130 return rawValue.pos(',') ?
131 checkList(rawValue) :
132 checkValue(rawValue.rawBuf(), rawValue.size());
133}
134