ContentLengthInterpreter.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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 "base/Raw.h"
14#include "debug/Stream.h"
16#include "http/one/Parser.h"
17#include "HttpHeaderTools.h"
18#include "SquidConfig.h"
19#include "SquidString.h"
20#include "StrList.h"
21
23 value(-1),
24 headerWideProblem(nullptr),
25 debugLevel(Config.onoff.relaxed_header_parser <= 0 ? DBG_IMPORTANT : 2),
26 sawBad(false),
27 needsSanitizing(false),
28 sawGood(false),
29 prohibitedAndIgnored_(nullptr)
30{
31}
32
35const char *
36Http::ContentLengthInterpreter::findDigits(const char *prefix, const char * const valueEnd) const
37{
38 // skip leading OWS in RFC 7230's `OWS field-value OWS`
40 while (prefix < valueEnd) {
41 const auto ch = *prefix;
42 if (CharacterSet::DIGIT[ch])
43 return prefix; // common case: a pre-trimmed field value
44 if (!whitespace[ch])
45 return nullptr; // (trimmed) length does not start with a digit
46 ++prefix;
47 }
48 return nullptr; // empty or whitespace-only value
49}
50
52bool
53Http::ContentLengthInterpreter::goodSuffix(const char *suffix, const char * const end) const
54{
55 // optimize for the common case that does not need delimiters
56 if (suffix == end)
57 return true;
58
60 suffix < end; ++suffix) {
61 if (!delimiters[*suffix])
62 return false;
63 }
64 // needsSanitizing = true; // TODO: Always remove trailing whitespace?
65 return true; // including empty suffix
66}
67
70bool
71Http::ContentLengthInterpreter::checkValue(const char *rawValue, const int valueSize)
72{
73 Must(!sawBad);
74
75 const auto valueEnd = rawValue + valueSize;
76
77 const auto digits = findDigits(rawValue, valueEnd);
78 if (!digits) {
79 debugs(55, debugLevel, "WARNING: Leading garbage or empty value in" << Raw("Content-Length", rawValue, valueSize));
80 sawBad = true;
81 return false;
82 }
83
84 int64_t latestValue = -1;
85 char *suffix = nullptr;
86
87 if (!httpHeaderParseOffset(digits, &latestValue, &suffix)) {
88 debugs(55, DBG_IMPORTANT, "WARNING: Malformed" << Raw("Content-Length", rawValue, valueSize));
89 sawBad = true;
90 return false;
91 }
92
93 if (latestValue < 0) {
94 debugs(55, debugLevel, "WARNING: Negative" << Raw("Content-Length", rawValue, valueSize));
95 sawBad = true;
96 return false;
97 }
98
99 // check for garbage after the number
100 if (!goodSuffix(suffix, valueEnd)) {
101 debugs(55, debugLevel, "WARNING: Trailing garbage in" << Raw("Content-Length", rawValue, valueSize));
102 sawBad = true;
103 return false;
104 }
105
106 if (sawGood) {
107 /* we have found at least two, possibly identical values */
108
109 needsSanitizing = true; // replace identical values with a single value
110
111 const bool conflicting = value != latestValue;
112 if (conflicting)
113 headerWideProblem = "Conflicting"; // overwrite any lesser problem
114 else if (!headerWideProblem) // preserve a possibly worse problem
115 headerWideProblem = "Duplicate";
116
117 // with relaxed_header_parser, identical values are permitted
118 sawBad = !Config.onoff.relaxed_header_parser || conflicting;
119 return false; // conflicting or duplicate
120 }
121
122 sawGood = true;
123 value = latestValue;
124 return true;
125}
126
128bool
130{
131 Must(!sawBad);
132
134 debugs(55, debugLevel, "WARNING: List-like" << Raw("Content-Length", list.rawBuf(), list.size()));
135 sawBad = true;
136 return false;
137 }
138
139 needsSanitizing = true; // remove extra commas (at least)
140
141 const char *pos = nullptr;
142 const char *item = nullptr;;
143 int ilen = -1;
144 while (strListGetItem(&list, ',', &item, &ilen, &pos)) {
145 if (!checkValue(item, ilen) && sawBad)
146 break;
147 // keep going after a duplicate value to find conflicting ones
148 }
149 return false; // no need to keep this list field; it will be sanitized away
150}
151
152bool
154{
155 if (sawBad)
156 return false; // one rotten apple is enough to spoil all of them
157
158 // TODO: Optimize by always parsing the first integer first.
159 return rawValue.pos(',') ?
160 checkList(rawValue) :
161 checkValue(rawValue.rawBuf(), rawValue.size());
162}
163
bool httpHeaderParseOffset(const char *start, int64_t *value, char **endPtr)
class SquidConfig Config
Definition: SquidConfig.cc:12
int strListGetItem(const String *str, char del, const char **item, int *ilen, const char **pos)
Definition: StrList.cc:86
#define Must(condition)
Definition: TextException.h:75
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:18
static const CharacterSet DIGIT
Definition: CharacterSet.h:84
const char * findDigits(const char *prefix, const char *valueEnd) const
bool goodSuffix(const char *suffix, const char *const end) const
checks whether all characters after the Content-Length are allowed
bool checkList(const String &list)
handles Content-Length: a, b, c
bool checkValue(const char *start, const int size)
static const CharacterSet & WhitespaceCharacters()
Definition: Parser.cc:52
static const CharacterSet & DelimiterCharacters()
Definition: Parser.cc:59
Definition: Raw.h:21
struct SquidConfig::@106 onoff
int relaxed_header_parser
Definition: SquidConfig.h:315
const char * pos(char const *aString) const
Definition: String.cc:405
char const * rawBuf() const
Definition: SquidString.h:86
size_type size() const
Definition: SquidString.h:73
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
const CharacterSet whitespace("whitespace"," \r\n")

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors