Squid Web Cache master
Loading...
Searching...
No Matches
HttpHdrContRange.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2026 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 68 HTTP Content-Range Header */
10
11#include "squid.h"
12#include "base/Packable.h"
13#include "debug/Stream.h"
14#include "enums.h"
15#include "HttpHdrContRange.h"
16#include "HttpHeaderTools.h"
17
18#include <limits>
19
20/*
21 * Currently only byte ranges are supported
22 *
23 * Content-Range = "Content-Range" ":" content-range-spec
24 * content-range-spec = byte-content-range-spec
25 * byte-content-range-spec = bytes-unit SP
26 * ( byte-range-resp-spec | "*") "/"
27 * ( entity-length | "*" )
28 * byte-range-resp-spec = first-byte-pos "-" last-byte-pos
29 * entity-length = 1*DIGIT
30 */
31
32/* local constants */
33#define range_spec_unknown (-1)
34
35/* local routines */
36#define known_spec(s) ((s) != range_spec_unknown)
37#define size_min(a,b) ((a) <= (b) ? (a) : (b))
38#define size_diff(a,b) ((a) >= (b) ? ((a)-(b)) : 0)
39
40/* globals */
41
42/* parses range-resp-spec and inits spec, returns true on success */
43static int
44httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec * spec, const char *field, int flen)
45{
46 const char *p;
47 assert(spec);
48 spec->offset = spec->length = range_spec_unknown;
49
50 if (flen < 2)
51 return 0;
52
53 /* is spec given ? */
54 if (*field == '*')
55 return 1;
56
57 /* check format, must be %d-%d */
58 if (!((p = strchr(field, '-')) && (p - field < flen))) {
59 debugs(68, 2, "invalid (no '-') resp-range-spec near: '" << field << "'");
60 return 0;
61 }
62
63 /* parse offset */
64 if (!httpHeaderParseOffset(field, &spec->offset))
65 return 0;
66
67 /* Additional check for BUG2155 - there MUST BE first-byte-pos and it MUST be positive*/
68 if (spec->offset < 0) {
69 debugs(68, 2, "invalid (no first-byte-pos or it is negative) resp-range-spec near: '" << field << "'");
70 return 0;
71 }
72
73 ++p;
74
75 /* do we have last-pos ? */
76 if (p - field >= flen) {
77 debugs(68, 2, "invalid (no last-byte-pos) resp-range-spec near: '" << field << "'");
78 return 0;
79 }
80
81 int64_t last_pos;
82
83 if (!httpHeaderParseOffset(p, &last_pos))
84 return 0;
85
86 if (last_pos < spec->offset) {
87 debugs(68, 2, "invalid (negative last-byte-pos) resp-range-spec near: '" << field << "'");
88 return 0;
89 }
90
91 // prevent `last_pos + 1` overflows
92 if (last_pos == std::numeric_limits<decltype(last_pos)>::max()) {
93 debugs(68, 2, "unsupported (last-byte-pos too large) resp-range-spec near: '" << field << "'");
94 return 0;
95 }
96
97 spec->length = size_diff(last_pos + 1, spec->offset);
98
99 /* we managed to parse, check if the result makes sense */
100 if (spec->length <= 0) {
101 debugs(68, 2, "invalid range (" << spec->offset << " += " <<
102 (long int) spec->length << ") in resp-range-spec near: '" << field << "'");
103 return 0;
104 }
105
106 return 1;
107}
108
109static void
111{
112 /* Ensure typecast is safe */
113 assert (spec->length >= 0);
114
115 if (!known_spec(spec->offset) || !known_spec(spec->length))
116 p->append("*", 1);
117 else
118 p->appendf("bytes %" PRId64 "-%" PRId64, spec->offset, spec->offset + spec->length - 1);
119}
120
121/*
122 * Content Range
123 */
124
133
136{
138
139 if (!httpHdrContRangeParseInit(r, str)) {
140 delete r;
141 return nullptr;
142 }
143
144 return r;
145}
146
147/* returns true if ranges are valid; inits HttpHdrContRange */
148int
150{
151 const char *p;
152 assert(range && str);
153 debugs(68, 8, "parsing content-range field: '" << str << "'");
154 /* check range type */
155
156 if (strncasecmp(str, "bytes ", 6))
157 return 0;
158
159 str += 6;
160
161 /* split */
162 if (!(p = strchr(str, '/')))
163 return 0;
164
165 if (*str == '*')
166 range->spec.offset = range->spec.length = range_spec_unknown;
167 else if (!httpHdrRangeRespSpecParseInit(&range->spec, str, p - str))
168 return 0;
169
170 ++p;
171
172 if (*p == '*') {
173 if (!known_spec(range->spec.offset)) {
174 debugs(68, 2, "invalid (*/*) content-range-spec near: '" << str << "'");
175 return 0;
176 }
178 } else if (!httpHeaderParseOffset(p, &range->elength))
179 return 0;
180 else if (range->elength <= 0) {
181 /* Additional paranoidal check for BUG2155 - entity-length MUST be > 0 */
182 debugs(68, 2, "invalid (entity-length is negative) content-range-spec near: '" << str << "'");
183 return 0;
184 } else if (known_spec(range->spec.length) && range->elength < (range->spec.offset + range->spec.length)) {
185 debugs(68, 2, "invalid (range is outside entity-length) content-range-spec near: '" << str << "'");
186 return 0;
187 }
188
189 // reject unsatisfied-range and such; we only use well-defined ranges today
190 if (!known_spec(range->spec.offset) || !known_spec(range->spec.length)) {
191 debugs(68, 2, "unwanted content-range-spec near: '" << str << "'");
192 return 0;
193 }
194
195 debugs(68, 8, "parsed content-range field: " <<
196 (long int) range->spec.offset << "-" <<
197 (long int) range->spec.offset + range->spec.length - 1 << " / " <<
198 (long int) range->elength);
199
200 return 1;
201}
202
205{
206 HttpHdrContRange *dup;
207 assert(range);
209 *dup = *range;
210 return dup;
211}
212
213void
215{
216 assert(range && p);
218 /* Ensure typecast is safe */
219 assert (range->elength >= 0);
220
221 if (!known_spec(range->elength))
222 p->append("/*", 2);
223 else
224 p->appendf("/%" PRId64, range->elength);
225}
226
227void
229{
230 assert(cr && ent_len >= 0);
231 cr->spec = spec;
232 cr->elength = ent_len;
233}
234
#define size_diff(a, b)
HttpHdrContRange * httpHdrContRangeCreate(void)
static void httpHdrRangeRespSpecPackInto(const HttpHdrRangeSpec *spec, Packable *p)
static int httpHdrRangeRespSpecParseInit(HttpHdrRangeSpec *spec, const char *field, int flen)
HttpHdrContRange * httpHdrContRangeDup(const HttpHdrContRange *range)
#define known_spec(s)
void httpHdrContRangePackInto(const HttpHdrContRange *range, Packable *p)
#define range_spec_unknown
void httpHdrContRangeSet(HttpHdrContRange *cr, HttpHdrRangeSpec spec, int64_t ent_len)
int httpHdrContRangeParseInit(HttpHdrContRange *range, const char *str)
HttpHdrContRange * httpHdrContRangeParseCreate(const char *str)
bool httpHeaderParseOffset(const char *start, int64_t *value, char **endPtr)
#define assert(EX)
Definition assert.h:17
HttpHdrRangeSpec spec
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition Packable.h:61
virtual void append(const char *buf, int size)=0
Appends a c-string to existing packed data.
A const & max(A const &lhs, A const &rhs)
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define PRId64
Definition types.h:104