]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/http/ContentLengthInterpreter.cc
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 55 HTTP Header */
12 #include "base/CharacterSet.h"
14 #include "http/ContentLengthInterpreter.h"
15 #include "http/one/Parser.h"
16 #include "HttpHeaderTools.h"
17 #include "SquidConfig.h"
18 #include "SquidString.h"
21 Http::ContentLengthInterpreter::ContentLengthInterpreter():
23 headerWideProblem(nullptr),
24 debugLevel(Config
.onoff
.relaxed_header_parser
<= 0 ? DBG_IMPORTANT
: 2),
26 needsSanitizing(false),
28 prohibitedAndIgnored_(nullptr)
32 /// checks whether all characters before the Content-Length number are allowed
33 /// \returns the start of the digit sequence (or nil on errors)
35 Http::ContentLengthInterpreter::findDigits(const char *prefix
, const char * const valueEnd
) const
37 // skip leading OWS in RFC 7230's `OWS field-value OWS`
38 const CharacterSet
&whitespace
= Http::One::Parser::WhitespaceCharacters();
39 while (prefix
< valueEnd
) {
40 const auto ch
= *prefix
;
41 if (CharacterSet::DIGIT
[ch
])
42 return prefix
; // common case: a pre-trimmed field value
44 return nullptr; // (trimmed) length does not start with a digit
47 return nullptr; // empty or whitespace-only value
50 /// checks whether all characters after the Content-Length are allowed
52 Http::ContentLengthInterpreter::goodSuffix(const char *suffix
, const char * const end
) const
54 // optimize for the common case that does not need delimiters
58 for (const CharacterSet
&delimiters
= Http::One::Parser::DelimiterCharacters();
59 suffix
< end
; ++suffix
) {
60 if (!delimiters
[*suffix
])
63 // needsSanitizing = true; // TODO: Always remove trailing whitespace?
64 return true; // including empty suffix
67 /// handles a single-token Content-Length value
68 /// rawValue null-termination requirements are those of httpHeaderParseOffset()
70 Http::ContentLengthInterpreter::checkValue(const char *rawValue
, const int valueSize
)
74 const auto valueEnd
= rawValue
+ valueSize
;
76 const auto digits
= findDigits(rawValue
, valueEnd
);
78 debugs(55, debugLevel
, "WARNING: Leading garbage or empty value in" << Raw("Content-Length", rawValue
, valueSize
));
83 int64_t latestValue
= -1;
84 char *suffix
= nullptr;
86 if (!httpHeaderParseOffset(digits
, &latestValue
, &suffix
)) {
87 debugs(55, DBG_IMPORTANT
, "WARNING: Malformed" << Raw("Content-Length", rawValue
, valueSize
));
92 if (latestValue
< 0) {
93 debugs(55, debugLevel
, "WARNING: Negative" << Raw("Content-Length", rawValue
, valueSize
));
98 // check for garbage after the number
99 if (!goodSuffix(suffix
, valueEnd
)) {
100 debugs(55, debugLevel
, "WARNING: Trailing garbage in" << Raw("Content-Length", rawValue
, valueSize
));
106 /* we have found at least two, possibly identical values */
108 needsSanitizing
= true; // replace identical values with a single value
110 const bool conflicting
= value
!= latestValue
;
112 headerWideProblem
= "Conflicting"; // overwrite any lesser problem
113 else if (!headerWideProblem
) // preserve a possibly worse problem
114 headerWideProblem
= "Duplicate";
116 // with relaxed_header_parser, identical values are permitted
117 sawBad
= !Config
.onoff
.relaxed_header_parser
|| conflicting
;
118 return false; // conflicting or duplicate
126 /// handles Content-Length: a, b, c
128 Http::ContentLengthInterpreter::checkList(const String
&list
)
132 if (!Config
.onoff
.relaxed_header_parser
) {
133 debugs(55, debugLevel
, "WARNING: List-like" << Raw("Content-Length", list
.rawBuf(), list
.size()));
138 needsSanitizing
= true; // remove extra commas (at least)
140 const char *pos
= nullptr;
141 const char *item
= nullptr;;
143 while (strListGetItem(&list
, ',', &item
, &ilen
, &pos
)) {
144 if (!checkValue(item
, ilen
) && sawBad
)
146 // keep going after a duplicate value to find conflicting ones
148 return false; // no need to keep this list field; it will be sanitized away
152 Http::ContentLengthInterpreter::checkField(const String
&rawValue
)
155 return false; // one rotten apple is enough to spoil all of them
157 // TODO: Optimize by always parsing the first integer first.
158 return rawValue
.pos(',') ?
159 checkList(rawValue
) :
160 checkValue(rawValue
.rawBuf(), rawValue
.size());