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