]> git.ipfire.org Git - thirdparty/squid.git/blame - src/HttpHeaderTools.cc
Bug 3178: bit more GCC compile errors
[thirdparty/squid.git] / src / HttpHeaderTools.cc
CommitLineData
7faf2bdb 1/*
262a0e14 2 * $Id$
7faf2bdb 3 *
4 * DEBUG: section 66 HTTP Header Tools
5 * AUTHOR: Alex Rousskov
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 8 * ----------------------------------------------------------
7faf2bdb 9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
7faf2bdb 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
26ac0430 23 *
7faf2bdb 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
26ac0430 28 *
7faf2bdb 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
7faf2bdb 33 */
34
35#include "squid.h"
c0941a6a 36#include "acl/FilledChecklist.h"
27bc2077
AJ
37#include "compat/strtoll.h"
38#include "HttpHdrContRange.h"
39#include "HttpHeader.h"
0eb49b6d 40#include "MemBuf.h"
7faf2bdb 41
2246b732 42static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs);
de336bbe 43
44
45HttpHeaderFieldInfo *
b644367b 46httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count)
7faf2bdb 47{
48 int i;
de336bbe 49 HttpHeaderFieldInfo *table = NULL;
50 assert(attrs && count);
51
52 /* allocate space */
0353e724 53 table = new HttpHeaderFieldInfo[count];
7faf2bdb 54
7faf2bdb 55 for (i = 0; i < count; ++i) {
62e76326 56 const http_hdr_type id = attrs[i].id;
57 HttpHeaderFieldInfo *info = table + id;
58 /* sanity checks */
59 assert(id >= 0 && id < count);
60 assert(attrs[i].name);
0353e724 61 assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */
62e76326 62 /* copy and init fields */
63 info->id = id;
64 info->type = attrs[i].type;
65 info->name = attrs[i].name;
66 assert(info->name.size());
7faf2bdb 67 }
62e76326 68
de336bbe 69 return table;
70}
71
72void
b644367b 73httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count)
de336bbe 74{
75 int i;
62e76326 76
de336bbe 77 for (i = 0; i < count; ++i)
30abd221 78 table[i].name.clean();
62e76326 79
7f4c8dec 80 delete [] table;
7faf2bdb 81}
82
d8b249ef 83void
97474590 84httpHeaderMaskInit(HttpHeaderMask * mask, int value)
d8b249ef 85{
97474590 86 memset(mask, value, sizeof(*mask));
d8b249ef 87}
88
b50e327b 89/** calculates a bit mask of a given array; does not reset mask! */
d8b249ef 90void
8abf232c 91httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count)
7faf2bdb 92{
e6ccf245 93 size_t i;
8abf232c 94 const int * enums = (const int *) http_hdr_type_enums;
d8b249ef 95 assert(mask && enums);
99edd1c3 96 assert(count < sizeof(*mask) * 8); /* check for overflow */
7faf2bdb 97
98 for (i = 0; i < count; ++i) {
62e76326 99 assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */
100 CBIT_SET(*mask, enums[i]);
7faf2bdb 101 }
7faf2bdb 102}
103
2246b732 104/* same as httpHeaderPutStr, but formats the string using snprintf first */
2246b732 105void
eeb423fb 106httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...)
2246b732 107{
9bc73deb 108 va_list args;
109 va_start(args, fmt);
62e76326 110
2246b732 111 httpHeaderPutStrvf(hdr, id, fmt, args);
112 va_end(args);
113}
114
115/* used by httpHeaderPutStrf */
116static void
117httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs)
118{
2246b732 119 MemBuf mb;
2fe7eff9 120 mb.init();
121 mb.vPrintf(fmt, vargs);
a9925b40 122 hdr->putStr(id, mb.buf);
2fe7eff9 123 mb.clean();
2246b732 124}
125
126
9035d1d5 127/** wrapper arrounf PutContRange */
d192d11f 128void
47f6e231 129httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len)
d192d11f 130{
131 HttpHdrContRange *cr = httpHdrContRangeCreate();
132 assert(hdr && ent_len >= 0);
133 httpHdrContRangeSet(cr, spec, ent_len);
a9925b40 134 hdr->putContRange(cr);
d192d11f 135 httpHdrContRangeDestroy(cr);
136}
137
a9771e51 138
9035d1d5 139/**
9bc73deb 140 * return true if a given directive is found in at least one of
141 * the "connection" header-fields note: if HDR_PROXY_CONNECTION is
142 * present we ignore HDR_CONNECTION.
99edd1c3 143 */
144int
5999b776 145httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
99edd1c3 146{
30abd221 147 String list;
9bc73deb 148 int res;
149 /* what type of header do we have? */
62e76326 150
95e78500 151#if USE_HTTP_VIOLATIONS
a9925b40 152 if (hdr->has(HDR_PROXY_CONNECTION))
95e78500
AJ
153 list = hdr->getList(HDR_PROXY_CONNECTION);
154 else
155#endif
2e881a6f
A
156 if (hdr->has(HDR_CONNECTION))
157 list = hdr->getList(HDR_CONNECTION);
158 else
159 return 0;
9bc73deb 160
9bc73deb 161 res = strListIsMember(&list, directive, ',');
62e76326 162
30abd221 163 list.clean();
164
9bc73deb 165 return res;
99edd1c3 166}
167
b50e327b 168/** returns true iff "m" is a member of the list */
99edd1c3 169int
30abd221 170strListIsMember(const String * list, const char *m, char del)
99edd1c3 171{
172 const char *pos = NULL;
173 const char *item;
9bc73deb 174 int ilen = 0;
175 int mlen;
99edd1c3 176 assert(list && m);
9bc73deb 177 mlen = strlen(m);
62e76326 178
9bc73deb 179 while (strListGetItem(list, del, &item, &ilen, &pos)) {
62e76326 180 if (mlen == ilen && !strncasecmp(item, m, ilen))
181 return 1;
99edd1c3 182 }
62e76326 183
99edd1c3 184 return 0;
185}
186
b50e327b 187/** returns true iff "s" is a substring of a member of the list */
edf3ffdc 188int
30abd221 189strListIsSubstr(const String * list, const char *s, char del)
edf3ffdc 190{
9bc73deb 191 assert(list && del);
b34dcde2 192 return (list->find(s) != String::npos);
9bc73deb 193
b50e327b 194 /** \note
9bc73deb 195 * Note: the original code with a loop is broken because it uses strstr()
196 * instead of strnstr(). If 's' contains a 'del', strListIsSubstr() may
197 * return true when it should not. If 's' does not contain a 'del', the
198 * implementaion is equavalent to strstr()! Thus, we replace the loop with
199 * strstr() above until strnstr() is available.
200 */
edf3ffdc 201}
202
b50e327b 203/** appends an item to the list */
99edd1c3 204void
30abd221 205strListAdd(String * str, const char *item, char del)
99edd1c3 206{
207 assert(str && item);
62e76326 208
528b2c61 209 if (str->size()) {
62e76326 210 char buf[3];
211 buf[0] = del;
212 buf[1] = ' ';
213 buf[2] = '\0';
214 str->append(buf, 2);
2246b732 215 }
62e76326 216
528b2c61 217 str->append(item, strlen(item));
99edd1c3 218}
219
b50e327b 220/**
7faf2bdb 221 * iterates through a 0-terminated string of items separated by 'del's.
222 * white space around 'del' is considered to be a part of 'del'
223 * like strtok, but preserves the source, and can iterate several strings at once
224 *
225 * returns true if next item is found.
226 * init pos with NULL to start iteration.
227 */
228int
30abd221 229strListGetItem(const String * str, char del, const char **item, int *ilen, const char **pos)
7faf2bdb 230{
231 size_t len;
f26664fd
HN
232 /* ',' is always enabled as field delimiter as this is required for
233 * processing merged header values properly, even if Cookie normally
234 * uses ';' as delimiter.
235 */
e1381638 236 static char delim[3][8] = {
26ac0430
AJ
237 "\"?,",
238 "\"\\",
239 " ?,\t\r\n"
b5c8ff59 240 };
f7573ac0 241 int quoted = 0;
7faf2bdb 242 assert(str && item && pos);
62e76326 243
f7573ac0 244 delim[0][1] = del;
b5c8ff59 245 delim[2][1] = del;
f7573ac0 246
7bd6a314 247 if (!*pos) {
9d7a89a5 248 *pos = str->termedBuf();
62e76326 249
250 if (!*pos)
251 return 0;
a3f9588e 252 }
7faf2bdb 253
f26664fd 254 /* skip leading whitespace and delimiters */
b5c8ff59 255 *pos += strspn(*pos, delim[2]);
5aff5fce 256
7faf2bdb 257 *item = *pos; /* remember item's start */
62e76326 258
7faf2bdb 259 /* find next delimiter */
f7573ac0 260 do {
261 *pos += strcspn(*pos, delim[quoted]);
f7573ac0 262 if (**pos == '"') {
263 quoted = !quoted;
264 *pos += 1;
e1381638 265 } else if (quoted && **pos == '\\') {
f7573ac0 266 *pos += 1;
f7573ac0 267 if (**pos)
268 *pos += 1;
e1381638
AJ
269 } else {
270 break; /* Delimiter found, marking the end of this value */
f7573ac0 271 }
272 } while (**pos);
62e76326 273
7faf2bdb 274 len = *pos - *item; /* *pos points to del or '\0' */
62e76326 275
7faf2bdb 276 /* rtrim */
b6a2f15e 277 while (len > 0 && xisspace((*item)[len - 1]))
62e76326 278 len--;
279
7faf2bdb 280 if (ilen)
62e76326 281 *ilen = len;
282
7faf2bdb 283 return len > 0;
284}
285
b50e327b 286/** handy to printf prefixes of potentially very long buffers */
7faf2bdb 287const char *
d8b249ef 288getStringPrefix(const char *str, const char *end)
7faf2bdb 289{
d8b249ef 290#define SHORT_PREFIX_SIZE 512
7faf2bdb 291 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
b644367b 292 const int sz = 1 + (end ? end - str : strlen(str));
d8b249ef 293 xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz);
7faf2bdb 294 return buf;
295}
b5107edb 296
b50e327b 297/**
b5107edb 298 * parses an int field, complains if soemthing went wrong, returns true on
299 * success
300 */
301int
302httpHeaderParseInt(const char *start, int *value)
303{
304 assert(value);
305 *value = atoi(start);
62e76326 306
b6a2f15e 307 if (!*value && !xisdigit(*start)) {
bf8fe701 308 debugs(66, 2, "failed to parse an int header field near '" << start << "'");
62e76326 309 return 0;
b5107edb 310 }
62e76326 311
b5107edb 312 return 1;
313}
314
47f6e231 315int
316httpHeaderParseOffset(const char *start, int64_t * value)
317{
66e255bc 318 errno = 0;
47f6e231 319 int64_t res = strtoll(start, NULL, 10);
320 if (!res && EINVAL == errno) /* maybe not portable? */
26ac0430 321 return 0;
47f6e231 322 *value = res;
323 return 1;
324}
325
de336bbe 326
b50e327b
AJ
327/**
328 * Parses a quoted-string field (RFC 2616 section 2.2), complains if
43ae1d95 329 * something went wrong, returns non-zero on success.
34460e19 330 * Un-escapes quoted-pair characters found within the string.
229af339 331 * start should point at the first double-quote.
43ae1d95 332 */
333int
34460e19 334httpHeaderParseQuotedString(const char *start, const int len, String *val)
43ae1d95 335{
336 const char *end, *pos;
30abd221 337 val->clean();
9abd1514 338 if (*start != '"') {
6d97f5f1
A
339 debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'");
340 return 0;
9abd1514 341 }
43ae1d95 342 pos = start + 1;
343
34460e19 344 while (*pos != '"' && len > (pos-start)) {
a0133f10
A
345 bool quoted = (*pos == '\\');
346 if (quoted)
347 pos++;
34460e19 348 if (!*pos || (pos-start) > len) {
6d97f5f1
A
349 debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'");
350 val->clean();
351 return 0;
352 }
34460e19 353 end = pos;
8236d34f 354 while (end <= (start+len) && *end != '\\' && *end != '\"' && *end > 0x1F && *end != 0x7F)
34460e19
AJ
355 end++;
356 if (*end <= 0x1F || *end == 0x7F) {
357 debugs(66, 2, "failed to parse a quoted-string header field with CTL octet " << (start-pos)
358 << " bytes into '" << start << "'");
359 val->clean();
360 return 0;
361 }
6d97f5f1
A
362 val->append(pos, end-pos);
363 pos = end;
43ae1d95 364 }
9abd1514
HN
365 /* Make sure it's defined even if empty "" */
366 if (!val->defined())
6d97f5f1 367 val->limitInit("", 0);
9abd1514 368 return 1;
43ae1d95 369}
370
b50e327b
AJ
371/**
372 * Checks the anonymizer (header_access) configuration.
4f4611bf 373 *
b50e327b
AJ
374 * \retval 0 Header is explicitly blocked for removal
375 * \retval 1 Header is explicitly allowed
376 * \retval 1 Header has been replaced, the current version can be used.
377 * \retval 1 Header has no access controls to test
6bccf575 378 */
2d72d4fd 379static int
8c01ada0 380httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
6bccf575 381{
e9b1e111 382 int retval;
383
6bccf575 384 /* check with anonymizer tables */
385 header_mangler *hm;
6bccf575 386 assert(e);
8c01ada0 387
388 if (ROR_REQUEST == req_or_rep) {
389 hm = &Config.request_header_access[e->id];
390 } else if (ROR_REPLY == req_or_rep) {
391 hm = &Config.reply_header_access[e->id];
392 } else {
393 /* error. But let's call it "request". */
394 hm = &Config.request_header_access[e->id];
395 }
396
b50e327b 397 /* mangler or checklist went away. default allow */
af6a12ee 398 if (!hm || !hm->access_list) {
b50e327b
AJ
399 return 1;
400 }
401
c0941a6a 402 ACLFilledChecklist checklist(hm->access_list, request, NULL);
62e76326 403
c0941a6a 404 if (checklist.fastCheck()) {
b50e327b 405 /* aclCheckFast returns true for allow. */
62e76326 406 retval = 1;
a453662f 407 } else if (NULL == hm->replacement) {
62e76326 408 /* It was denied, and we don't have any replacement */
409 retval = 0;
a453662f 410 } else {
62e76326 411 /* It was denied, but we have a replacement. Replace the
412 * header on the fly, and return that the new header
413 * is allowed.
414 */
415 e->value = hm->replacement;
416 retval = 1;
a453662f 417 }
e9b1e111 418
e9b1e111 419 return retval;
6bccf575 420}
421
b50e327b 422/** Mangles headers for a list of headers. */
6bccf575 423void
8c01ada0 424httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
6bccf575 425{
426 HttpHeaderEntry *e;
427 HttpHeaderPos p = HttpHeaderInitPos;
62e76326 428
ba9fb01d 429 int headers_deleted = 0;
a9925b40 430 while ((e = l->getEntry(&p)))
8c01ada0 431 if (0 == httpHdrMangle(e, request, req_or_rep))
ba9fb01d 432 l->delAt(p, headers_deleted);
433
434 if (headers_deleted)
435 l->refreshMask();
6bccf575 436}
5967c0bf 437
b50e327b 438/**
5967c0bf 439 * return 1 if manglers are configured. Used to set a flag
440 * for optimization during request forwarding.
441 */
442int
4f56514c 443httpReqHdrManglersConfigured()
5967c0bf 444{
445 for (int i = 0; i < HDR_ENUM_END; i++) {
446 if (NULL != Config.request_header_access[i].access_list)
447 return 1;
448 }
449
450 return 0;
451}