]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHeaderTools.cc
Merge from trunk
[thirdparty/squid.git] / src / HttpHeaderTools.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 66 HTTP Header Tools
6 * AUTHOR: Alex Rousskov
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37 #include "HttpHeader.h"
38 #include "HttpHdrContRange.h"
39 #include "acl/FilledChecklist.h"
40 #include "MemBuf.h"
41
42 static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs);
43
44
45 HttpHeaderFieldInfo *
46 httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count)
47 {
48 int i;
49 HttpHeaderFieldInfo *table = NULL;
50 assert(attrs && count);
51
52 /* allocate space */
53 table = new HttpHeaderFieldInfo[count];
54
55 for (i = 0; i < count; ++i) {
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);
61 assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */
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());
67 }
68
69 return table;
70 }
71
72 void
73 httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count)
74 {
75 int i;
76
77 for (i = 0; i < count; ++i)
78 table[i].name.clean();
79
80 delete [] table;
81 }
82
83 void
84 httpHeaderMaskInit(HttpHeaderMask * mask, int value)
85 {
86 memset(mask, value, sizeof(*mask));
87 }
88
89 /** calculates a bit mask of a given array; does not reset mask! */
90 void
91 httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count)
92 {
93 size_t i;
94 const int * enums = (const int *) http_hdr_type_enums;
95 assert(mask && enums);
96 assert(count < sizeof(*mask) * 8); /* check for overflow */
97
98 for (i = 0; i < count; ++i) {
99 assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */
100 CBIT_SET(*mask, enums[i]);
101 }
102 }
103
104 /* same as httpHeaderPutStr, but formats the string using snprintf first */
105 void
106 httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...)
107 {
108 va_list args;
109 va_start(args, fmt);
110
111 httpHeaderPutStrvf(hdr, id, fmt, args);
112 va_end(args);
113 }
114
115 /* used by httpHeaderPutStrf */
116 static void
117 httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs)
118 {
119 MemBuf mb;
120 mb.init();
121 mb.vPrintf(fmt, vargs);
122 hdr->putStr(id, mb.buf);
123 mb.clean();
124 }
125
126
127 /** wrapper arrounf PutContRange */
128 void
129 httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len)
130 {
131 HttpHdrContRange *cr = httpHdrContRangeCreate();
132 assert(hdr && ent_len >= 0);
133 httpHdrContRangeSet(cr, spec, ent_len);
134 hdr->putContRange(cr);
135 httpHdrContRangeDestroy(cr);
136 }
137
138
139 /**
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.
143 */
144 int
145 httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
146 {
147 String list;
148 http_hdr_type ht;
149 int res;
150 /* what type of header do we have? */
151
152 if (hdr->has(HDR_PROXY_CONNECTION))
153 ht = HDR_PROXY_CONNECTION;
154 else if (hdr->has(HDR_CONNECTION))
155 ht = HDR_CONNECTION;
156 else
157 return 0;
158
159 list = hdr->getList(ht);
160
161 res = strListIsMember(&list, directive, ',');
162
163 list.clean();
164
165 return res;
166 }
167
168 /** returns true iff "m" is a member of the list */
169 int
170 strListIsMember(const String * list, const char *m, char del)
171 {
172 const char *pos = NULL;
173 const char *item;
174 int ilen = 0;
175 int mlen;
176 assert(list && m);
177 mlen = strlen(m);
178
179 while (strListGetItem(list, del, &item, &ilen, &pos)) {
180 if (mlen == ilen && !strncasecmp(item, m, ilen))
181 return 1;
182 }
183
184 return 0;
185 }
186
187 /** returns true iff "s" is a substring of a member of the list */
188 int
189 strListIsSubstr(const String * list, const char *s, char del)
190 {
191 assert(list && del);
192 return (list->find(s) != String::npos);
193
194 /** \note
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 */
201 }
202
203 /** appends an item to the list */
204 void
205 strListAdd(String * str, const char *item, char del)
206 {
207 assert(str && item);
208
209 if (str->size()) {
210 char buf[3];
211 buf[0] = del;
212 buf[1] = ' ';
213 buf[2] = '\0';
214 str->append(buf, 2);
215 }
216
217 str->append(item, strlen(item));
218 }
219
220 /**
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 */
228 int
229 strListGetItem(const String * str, char del, const char **item, int *ilen, const char **pos)
230 {
231 size_t len;
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 */
236 static char delim[3][8] = {
237 "\"?,",
238 "\"\\",
239 " ?,\t\r\n"
240 };
241 int quoted = 0;
242 assert(str && item && pos);
243
244 delim[0][1] = del;
245 delim[2][1] = del;
246
247 if (!*pos) {
248 *pos = str->termedBuf();
249
250 if (!*pos)
251 return 0;
252 }
253
254 /* skip leading whitespace and delimiters */
255 *pos += strspn(*pos, delim[2]);
256
257 *item = *pos; /* remember item's start */
258
259 /* find next delimiter */
260 do {
261 *pos += strcspn(*pos, delim[quoted]);
262 if (**pos == '"') {
263 quoted = !quoted;
264 *pos += 1;
265 } else if (quoted && **pos == '\\') {
266 *pos += 1;
267 if (**pos)
268 *pos += 1;
269 } else {
270 break; /* Delimiter found, marking the end of this value */
271 }
272 } while (**pos);
273
274 len = *pos - *item; /* *pos points to del or '\0' */
275
276 /* rtrim */
277 while (len > 0 && xisspace((*item)[len - 1]))
278 len--;
279
280 if (ilen)
281 *ilen = len;
282
283 return len > 0;
284 }
285
286 /** handy to printf prefixes of potentially very long buffers */
287 const char *
288 getStringPrefix(const char *str, const char *end)
289 {
290 #define SHORT_PREFIX_SIZE 512
291 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
292 const int sz = 1 + (end ? end - str : strlen(str));
293 xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz);
294 return buf;
295 }
296
297 /**
298 * parses an int field, complains if soemthing went wrong, returns true on
299 * success
300 */
301 int
302 httpHeaderParseInt(const char *start, int *value)
303 {
304 assert(value);
305 *value = atoi(start);
306
307 if (!*value && !xisdigit(*start)) {
308 debugs(66, 2, "failed to parse an int header field near '" << start << "'");
309 return 0;
310 }
311
312 return 1;
313 }
314
315 int
316 httpHeaderParseOffset(const char *start, int64_t * value)
317 {
318 errno = 0;
319 int64_t res = strtoll(start, NULL, 10);
320 if (!res && EINVAL == errno) /* maybe not portable? */
321 return 0;
322 *value = res;
323 return 1;
324 }
325
326
327 /**
328 * Parses a quoted-string field (RFC 2616 section 2.2), complains if
329 * something went wrong, returns non-zero on success.
330 * start should point at the first ".
331 * RC TODO: This is too looose. We should honour the BNF and exclude CTL's
332 */
333 int
334 httpHeaderParseQuotedString (const char *start, String *val)
335 {
336 const char *end, *pos;
337 val->clean();
338 assert (*start == '"');
339 pos = start + 1;
340
341 while (1) {
342 if (!(end = index (pos,'"'))) {
343 debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'");
344 return 0;
345 }
346
347 /* check for quoted-chars */
348 if (*(end - 1) != '\\') {
349 /* done */
350 val->append(start + 1, end-start-1);
351 return 1;
352 }
353
354 /* try for the end again */
355 pos = end + 1;
356 }
357 }
358
359 /**
360 * Checks the anonymizer (header_access) configuration.
361 *
362 * \retval 0 Header is explicitly blocked for removal
363 * \retval 1 Header is explicitly allowed
364 * \retval 1 Header has been replaced, the current version can be used.
365 * \retval 1 Header has no access controls to test
366 */
367 static int
368 httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
369 {
370 int retval;
371
372 /* check with anonymizer tables */
373 header_mangler *hm;
374 assert(e);
375
376 if (ROR_REQUEST == req_or_rep) {
377 hm = &Config.request_header_access[e->id];
378 } else if (ROR_REPLY == req_or_rep) {
379 hm = &Config.reply_header_access[e->id];
380 } else {
381 /* error. But let's call it "request". */
382 hm = &Config.request_header_access[e->id];
383 }
384
385 /* mangler or checklist went away. default allow */
386 if (!hm || !hm->access_list) {
387 return 1;
388 }
389
390 ACLFilledChecklist checklist(hm->access_list, request, NULL);
391
392 if (checklist.fastCheck()) {
393 /* aclCheckFast returns true for allow. */
394 retval = 1;
395 } else if (NULL == hm->replacement) {
396 /* It was denied, and we don't have any replacement */
397 retval = 0;
398 } else {
399 /* It was denied, but we have a replacement. Replace the
400 * header on the fly, and return that the new header
401 * is allowed.
402 */
403 e->value = hm->replacement;
404 retval = 1;
405 }
406
407 return retval;
408 }
409
410 /** Mangles headers for a list of headers. */
411 void
412 httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
413 {
414 HttpHeaderEntry *e;
415 HttpHeaderPos p = HttpHeaderInitPos;
416
417 int headers_deleted = 0;
418 while ((e = l->getEntry(&p)))
419 if (0 == httpHdrMangle(e, request, req_or_rep))
420 l->delAt(p, headers_deleted);
421
422 if (headers_deleted)
423 l->refreshMask();
424 }
425
426 /**
427 * return 1 if manglers are configured. Used to set a flag
428 * for optimization during request forwarding.
429 */
430 int
431 httpReqHdrManglersConfigured()
432 {
433 for (int i = 0; i < HDR_ENUM_END; i++) {
434 if (NULL != Config.request_header_access[i].access_list)
435 return 1;
436 }
437
438 return 0;
439 }