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 
35 const char *
36 Http::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 
52 bool
53 Http::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 
59  for (const CharacterSet &delimiters = Http::One::Parser::DelimiterCharacters();
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 
70 bool
71 Http::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 
128 bool
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 
152 bool
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 
static const CharacterSet & DelimiterCharacters()
Definition: Parser.cc:59
int relaxed_header_parser
Definition: SquidConfig.h:315
const char * rawBuf() const
Definition: SquidString.h:86
bool goodSuffix(const char *suffix, const char *const end) const
checks whether all characters after the Content-Length are allowed
const char * findDigits(const char *prefix, const char *valueEnd) const
struct SquidConfig::@97 onoff
Definition: Raw.h:20
static const CharacterSet & WhitespaceCharacters()
Definition: Parser.cc:52
const CharacterSet whitespace("whitespace"," \r\n")
bool httpHeaderParseOffset(const char *start, int64_t *value, char **endPtr)
static const CharacterSet DIGIT
Definition: CharacterSet.h:84
const char * pos(char const *aString) const
Definition: String.cc:405
bool checkValue(const char *start, const int size)
bool checkList(const String &list)
handles Content-Length: a, b, c
size_type size() const
Definition: SquidString.h:73
#define Must(condition)
Definition: TextException.h:75
#define DBG_IMPORTANT
Definition: Stream.h:38
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
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:78

 

Introduction

Documentation

Support

Miscellaneous