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