]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHeaderTools.cc
Merged from trunk
[thirdparty/squid.git] / src / HttpHeaderTools.cc
1 /*
2 * DEBUG: section 66 HTTP Header Tools
3 * AUTHOR: Alex Rousskov
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34 #include "acl/FilledChecklist.h"
35 #include "acl/Gadgets.h"
36 #include "client_side_request.h"
37 #include "client_side.h"
38 #include "comm/Connection.h"
39 #include "compat/strtoll.h"
40 #include "fde.h"
41 #include "HttpHdrContRange.h"
42 #include "HttpHeader.h"
43 #include "HttpHeaderFieldInfo.h"
44 #include "HttpHeaderTools.h"
45 #include "HttpRequest.h"
46 #include "MemBuf.h"
47 #include "SquidConfig.h"
48 #include "Store.h"
49 #include "StrList.h"
50
51 #if USE_SSL
52 #include "ssl/support.h"
53 #endif
54
55 #include <algorithm>
56 #include <string>
57 #if HAVE_ERRNO_H
58 #include <errno.h>
59 #endif
60
61 static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs);
62
63 HttpHeaderFieldInfo *
64 httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count)
65 {
66 int i;
67 HttpHeaderFieldInfo *table = NULL;
68 assert(attrs && count);
69
70 /* allocate space */
71 table = new HttpHeaderFieldInfo[count];
72
73 for (i = 0; i < count; ++i) {
74 const http_hdr_type id = attrs[i].id;
75 HttpHeaderFieldInfo *info = table + id;
76 /* sanity checks */
77 assert(id >= 0 && id < count);
78 assert(attrs[i].name);
79 assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */
80 /* copy and init fields */
81 info->id = id;
82 info->type = attrs[i].type;
83 info->name = attrs[i].name;
84 assert(info->name.size());
85 }
86
87 return table;
88 }
89
90 void
91 httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count)
92 {
93 int i;
94
95 for (i = 0; i < count; ++i)
96 table[i].name.clean();
97
98 delete [] table;
99 }
100
101 void
102 httpHeaderMaskInit(HttpHeaderMask * mask, int value)
103 {
104 memset(mask, value, sizeof(*mask));
105 }
106
107 /** calculates a bit mask of a given array; does not reset mask! */
108 void
109 httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count)
110 {
111 size_t i;
112 const int * enums = (const int *) http_hdr_type_enums;
113 assert(mask && enums);
114 assert(count < sizeof(*mask) * 8); /* check for overflow */
115
116 for (i = 0; i < count; ++i) {
117 assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */
118 CBIT_SET(*mask, enums[i]);
119 }
120 }
121
122 /* same as httpHeaderPutStr, but formats the string using snprintf first */
123 void
124 httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...)
125 {
126 va_list args;
127 va_start(args, fmt);
128
129 httpHeaderPutStrvf(hdr, id, fmt, args);
130 va_end(args);
131 }
132
133 /* used by httpHeaderPutStrf */
134 static void
135 httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs)
136 {
137 MemBuf mb;
138 mb.init();
139 mb.vPrintf(fmt, vargs);
140 hdr->putStr(id, mb.buf);
141 mb.clean();
142 }
143
144 /** wrapper arrounf PutContRange */
145 void
146 httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len)
147 {
148 HttpHdrContRange *cr = httpHdrContRangeCreate();
149 assert(hdr && ent_len >= 0);
150 httpHdrContRangeSet(cr, spec, ent_len);
151 hdr->putContRange(cr);
152 httpHdrContRangeDestroy(cr);
153 }
154
155 /**
156 * return true if a given directive is found in at least one of
157 * the "connection" header-fields note: if HDR_PROXY_CONNECTION is
158 * present we ignore HDR_CONNECTION.
159 */
160 int
161 httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
162 {
163 String list;
164 int res;
165 /* what type of header do we have? */
166
167 #if USE_HTTP_VIOLATIONS
168 if (hdr->has(HDR_PROXY_CONNECTION))
169 list = hdr->getList(HDR_PROXY_CONNECTION);
170 else
171 #endif
172 if (hdr->has(HDR_CONNECTION))
173 list = hdr->getList(HDR_CONNECTION);
174 else
175 return 0;
176
177 res = strListIsMember(&list, directive, ',');
178
179 list.clean();
180
181 return res;
182 }
183
184 /** handy to printf prefixes of potentially very long buffers */
185 const char *
186 getStringPrefix(const char *str, const char *end)
187 {
188 #define SHORT_PREFIX_SIZE 512
189 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
190 const int sz = 1 + (end ? end - str : strlen(str));
191 xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz);
192 return buf;
193 }
194
195 /**
196 * parses an int field, complains if soemthing went wrong, returns true on
197 * success
198 */
199 int
200 httpHeaderParseInt(const char *start, int *value)
201 {
202 assert(value);
203 *value = atoi(start);
204
205 if (!*value && !xisdigit(*start)) {
206 debugs(66, 2, "failed to parse an int header field near '" << start << "'");
207 return 0;
208 }
209
210 return 1;
211 }
212
213 int
214 httpHeaderParseOffset(const char *start, int64_t * value)
215 {
216 errno = 0;
217 int64_t res = strtoll(start, NULL, 10);
218 if (!res && EINVAL == errno) /* maybe not portable? */
219 return 0;
220 *value = res;
221 return 1;
222 }
223
224 /**
225 * Parses a quoted-string field (RFC 2616 section 2.2), complains if
226 * something went wrong, returns non-zero on success.
227 * Un-escapes quoted-pair characters found within the string.
228 * start should point at the first double-quote.
229 */
230 int
231 httpHeaderParseQuotedString(const char *start, const int len, String *val)
232 {
233 const char *end, *pos;
234 val->clean();
235 if (*start != '"') {
236 debugs(66, 2, HERE << "failed to parse a quoted-string header field near '" << start << "'");
237 return 0;
238 }
239 pos = start + 1;
240
241 while (*pos != '"' && len > (pos-start)) {
242
243 if (*pos =='\r') {
244 ++pos;
245 if ((pos-start) > len || *pos != '\n') {
246 debugs(66, 2, HERE << "failed to parse a quoted-string header field with '\\r' octet " << (start-pos)
247 << " bytes into '" << start << "'");
248 val->clean();
249 return 0;
250 }
251 }
252
253 if (*pos == '\n') {
254 ++pos;
255 if ( (pos-start) > len || (*pos != ' ' && *pos != '\t')) {
256 debugs(66, 2, HERE << "failed to parse multiline quoted-string header field '" << start << "'");
257 val->clean();
258 return 0;
259 }
260 // TODO: replace the entire LWS with a space
261 val->append(" ");
262 ++pos;
263 debugs(66, 2, HERE << "len < pos-start => " << len << " < " << (pos-start));
264 continue;
265 }
266
267 bool quoted = (*pos == '\\');
268 if (quoted) {
269 ++pos;
270 if (!*pos || (pos-start) > len) {
271 debugs(66, 2, HERE << "failed to parse a quoted-string header field near '" << start << "'");
272 val->clean();
273 return 0;
274 }
275 }
276 end = pos;
277 while (end < (start+len) && *end != '\\' && *end != '\"' && (unsigned char)*end > 0x1F && *end != 0x7F)
278 ++end;
279 if (((unsigned char)*end <= 0x1F && *end != '\r' && *end != '\n') || *end == 0x7F) {
280 debugs(66, 2, HERE << "failed to parse a quoted-string header field with CTL octet " << (start-pos)
281 << " bytes into '" << start << "'");
282 val->clean();
283 return 0;
284 }
285 val->append(pos, end-pos);
286 pos = end;
287 }
288
289 if (*pos != '\"') {
290 debugs(66, 2, HERE << "failed to parse a quoted-string header field which did not end with \" ");
291 val->clean();
292 return 0;
293 }
294 /* Make sure it's defined even if empty "" */
295 if (!val->defined())
296 val->limitInit("", 0);
297 return 1;
298 }
299
300 /**
301 * Checks the anonymizer (header_access) configuration.
302 *
303 * \retval 0 Header is explicitly blocked for removal
304 * \retval 1 Header is explicitly allowed
305 * \retval 1 Header has been replaced, the current version can be used.
306 * \retval 1 Header has no access controls to test
307 */
308 static int
309 httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
310 {
311 int retval;
312
313 /* check with anonymizer tables */
314 HeaderManglers *hms = NULL;
315 assert(e);
316
317 if (ROR_REQUEST == req_or_rep) {
318 hms = Config.request_header_access;
319 } else if (ROR_REPLY == req_or_rep) {
320 hms = Config.reply_header_access;
321 } else {
322 /* error. But let's call it "request". */
323 hms = Config.request_header_access;
324 }
325
326 /* manglers are not configured for this message kind */
327 if (!hms)
328 return 1;
329
330 const headerMangler *hm = hms->find(*e);
331
332 /* mangler or checklist went away. default allow */
333 if (!hm || !hm->access_list) {
334 return 1;
335 }
336
337 ACLFilledChecklist checklist(hm->access_list, request, NULL);
338
339 if (checklist.fastCheck() == ACCESS_ALLOWED) {
340 /* aclCheckFast returns true for allow. */
341 retval = 1;
342 } else if (NULL == hm->replacement) {
343 /* It was denied, and we don't have any replacement */
344 retval = 0;
345 } else {
346 /* It was denied, but we have a replacement. Replace the
347 * header on the fly, and return that the new header
348 * is allowed.
349 */
350 e->value = hm->replacement;
351 retval = 1;
352 }
353
354 return retval;
355 }
356
357 /** Mangles headers for a list of headers. */
358 void
359 httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
360 {
361 HttpHeaderEntry *e;
362 HttpHeaderPos p = HttpHeaderInitPos;
363
364 int headers_deleted = 0;
365 while ((e = l->getEntry(&p)))
366 if (0 == httpHdrMangle(e, request, req_or_rep))
367 l->delAt(p, headers_deleted);
368
369 if (headers_deleted)
370 l->refreshMask();
371 }
372
373 static
374 void header_mangler_clean(headerMangler &m)
375 {
376 aclDestroyAccessList(&m.access_list);
377 safe_free(m.replacement);
378 }
379
380 static
381 void header_mangler_dump_access(StoreEntry * entry, const char *option,
382 const headerMangler &m, const char *name)
383 {
384 if (m.access_list != NULL) {
385 storeAppendPrintf(entry, "%s ", option);
386 dump_acl_access(entry, name, m.access_list);
387 }
388 }
389
390 static
391 void header_mangler_dump_replacement(StoreEntry * entry, const char *option,
392 const headerMangler &m, const char *name)
393 {
394 if (m.replacement)
395 storeAppendPrintf(entry, "%s %s %s\n", option, name, m.replacement);
396 }
397
398 HeaderManglers::HeaderManglers()
399 {
400 memset(known, 0, sizeof(known));
401 memset(&all, 0, sizeof(all));
402 }
403
404 HeaderManglers::~HeaderManglers()
405 {
406 for (int i = 0; i < HDR_ENUM_END; ++i)
407 header_mangler_clean(known[i]);
408
409 typedef ManglersByName::iterator MBNI;
410 for (MBNI i = custom.begin(); i != custom.end(); ++i)
411 header_mangler_clean(i->second);
412
413 header_mangler_clean(all);
414 }
415
416 void
417 HeaderManglers::dumpAccess(StoreEntry * entry, const char *name) const
418 {
419 for (int i = 0; i < HDR_ENUM_END; ++i) {
420 header_mangler_dump_access(entry, name, known[i],
421 httpHeaderNameById(i));
422 }
423
424 typedef ManglersByName::const_iterator MBNCI;
425 for (MBNCI i = custom.begin(); i != custom.end(); ++i)
426 header_mangler_dump_access(entry, name, i->second, i->first.c_str());
427
428 header_mangler_dump_access(entry, name, all, "All");
429 }
430
431 void
432 HeaderManglers::dumpReplacement(StoreEntry * entry, const char *name) const
433 {
434 for (int i = 0; i < HDR_ENUM_END; ++i) {
435 header_mangler_dump_replacement(entry, name, known[i],
436 httpHeaderNameById(i));
437 }
438
439 typedef ManglersByName::const_iterator MBNCI;
440 for (MBNCI i = custom.begin(); i != custom.end(); ++i) {
441 header_mangler_dump_replacement(entry, name, i->second,
442 i->first.c_str());
443 }
444
445 header_mangler_dump_replacement(entry, name, all, "All");
446 }
447
448 headerMangler *
449 HeaderManglers::track(const char *name)
450 {
451 int id = httpHeaderIdByNameDef(name, strlen(name));
452
453 if (id == HDR_BAD_HDR) { // special keyword or a custom header
454 if (strcmp(name, "All") == 0)
455 id = HDR_ENUM_END;
456 else if (strcmp(name, "Other") == 0)
457 id = HDR_OTHER;
458 }
459
460 headerMangler *m = NULL;
461 if (id == HDR_ENUM_END) {
462 m = &all;
463 } else if (id == HDR_BAD_HDR) {
464 m = &custom[name];
465 } else {
466 m = &known[id]; // including HDR_OTHER
467 }
468
469 assert(m);
470 return m;
471 }
472
473 void
474 HeaderManglers::setReplacement(const char *name, const char *value)
475 {
476 // for backword compatibility, we allow replacements to be configured
477 // for headers w/o access rules, but such replacements are ignored
478 headerMangler *m = track(name);
479
480 safe_free(m->replacement); // overwrite old value if any
481 m->replacement = xstrdup(value);
482 }
483
484 const headerMangler *
485 HeaderManglers::find(const HttpHeaderEntry &e) const
486 {
487 // a known header with a configured ACL list
488 if (e.id != HDR_OTHER && 0 <= e.id && e.id < HDR_ENUM_END &&
489 known[e.id].access_list)
490 return &known[e.id];
491
492 // a custom header
493 if (e.id == HDR_OTHER) {
494 // does it have an ACL list configured?
495 // Optimize: use a name type that we do not need to convert to here
496 const ManglersByName::const_iterator i = custom.find(e.name.termedBuf());
497 if (i != custom.end())
498 return &i->second;
499 }
500
501 // Next-to-last resort: "Other" rules match any custom header
502 if (e.id == HDR_OTHER && known[HDR_OTHER].access_list)
503 return &known[HDR_OTHER];
504
505 // Last resort: "All" rules match any header
506 if (all.access_list)
507 return &all;
508
509 return NULL;
510 }
511
512 void
513 httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd)
514 {
515 ACLFilledChecklist checklist(NULL, request, NULL);
516
517 for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) {
518 if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) {
519 const char *fieldValue = NULL;
520 MemBuf mb;
521 if (hwa->quoted) {
522 if (al != NULL) {
523 mb.init();
524 hwa->valueFormat->assemble(mb, al, 0);
525 fieldValue = mb.content();
526 }
527 } else {
528 fieldValue = hwa->fieldValue.c_str();
529 }
530
531 if (!fieldValue || fieldValue[0] == '\0')
532 fieldValue = "-";
533
534 HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(),
535 fieldValue);
536 heads->addEntry(e);
537 }
538 }
539 }