]> 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 "ACLChecklist.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->pos(s) != 0;
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 static char delim[3][8] = {
233 "\"?,",
234 "\"\\",
235 " ?,\t\r\n"
236 };
237 int quoted = 0;
238 assert(str && item && pos);
239
240 delim[0][1] = del;
241 delim[2][1] = del;
242
243 if (!*pos) {
244 *pos = str->termedBuf();
245
246 if (!*pos)
247 return 0;
248 }
249
250 /* skip leading ws and delimiters */
251 *pos += strspn(*pos, delim[2]);
252
253 *item = *pos; /* remember item's start */
254
255 /* find next delimiter */
256 do {
257 *pos += strcspn(*pos, delim[quoted]);
258
259 if (**pos == del)
260 break;
261
262 if (**pos == '"') {
263 quoted = !quoted;
264 *pos += 1;
265 }
266
267 if (quoted && **pos == '\\') {
268 *pos += 1;
269
270 if (**pos)
271 *pos += 1;
272 }
273 } while (**pos);
274
275 len = *pos - *item; /* *pos points to del or '\0' */
276
277 /* rtrim */
278 while (len > 0 && xisspace((*item)[len - 1]))
279 len--;
280
281 if (ilen)
282 *ilen = len;
283
284 return len > 0;
285 }
286
287 /** handy to printf prefixes of potentially very long buffers */
288 const char *
289 getStringPrefix(const char *str, const char *end)
290 {
291 #define SHORT_PREFIX_SIZE 512
292 LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE);
293 const int sz = 1 + (end ? end - str : strlen(str));
294 xstrncpy(buf, str, (sz > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz);
295 return buf;
296 }
297
298 /**
299 * parses an int field, complains if soemthing went wrong, returns true on
300 * success
301 */
302 int
303 httpHeaderParseInt(const char *start, int *value)
304 {
305 assert(value);
306 *value = atoi(start);
307
308 if (!*value && !xisdigit(*start)) {
309 debugs(66, 2, "failed to parse an int header field near '" << start << "'");
310 return 0;
311 }
312
313 return 1;
314 }
315
316 int
317 httpHeaderParseOffset(const char *start, int64_t * value)
318 {
319 errno = 0;
320 int64_t res = strtoll(start, NULL, 10);
321 if (!res && EINVAL == errno) /* maybe not portable? */
322 return 0;
323 *value = res;
324 return 1;
325 }
326
327
328 /**
329 * Parses a quoted-string field (RFC 2616 section 2.2), complains if
330 * something went wrong, returns non-zero on success.
331 * start should point at the first ".
332 * RC TODO: This is too looose. We should honour the BNF and exclude CTL's
333 */
334 int
335 httpHeaderParseQuotedString (const char *start, String *val)
336 {
337 const char *end, *pos;
338 val->clean();
339 assert (*start == '"');
340 pos = start + 1;
341
342 while (1) {
343 if (!(end = index (pos,'"'))) {
344 debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'");
345 return 0;
346 }
347
348 /* check for quoted-chars */
349 if (*(end - 1) != '\\') {
350 /* done */
351 val->append(start + 1, end-start-1);
352 return 1;
353 }
354
355 /* try for the end again */
356 pos = end + 1;
357 }
358 }
359
360 /**
361 * Checks the anonymizer (header_access) configuration.
362 *
363 * \retval 0 Header is explicitly blocked for removal
364 * \retval 1 Header is explicitly allowed
365 * \retval 1 Header has been replaced, the current version can be used.
366 * \retval 1 Header has no access controls to test
367 */
368 static int
369 httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
370 {
371 int retval;
372
373 /* check with anonymizer tables */
374 header_mangler *hm;
375 ACLChecklist *checklist;
376 assert(e);
377
378 if (ROR_REQUEST == req_or_rep) {
379 hm = &Config.request_header_access[e->id];
380 } else if (ROR_REPLY == req_or_rep) {
381 hm = &Config.reply_header_access[e->id];
382 } else {
383 /* error. But let's call it "request". */
384 hm = &Config.request_header_access[e->id];
385 }
386
387 /* mangler or checklist went away. default allow */
388 if(!hm || !hm->access_list) {
389 return 1;
390 }
391
392 checklist = aclChecklistCreate(hm->access_list, request, NULL);
393
394 if (checklist->fastCheck()) {
395 /* aclCheckFast returns true for allow. */
396 retval = 1;
397 } else if (NULL == hm->replacement) {
398 /* It was denied, and we don't have any replacement */
399 retval = 0;
400 } else {
401 /* It was denied, but we have a replacement. Replace the
402 * header on the fly, and return that the new header
403 * is allowed.
404 */
405 e->value = hm->replacement;
406 retval = 1;
407 }
408
409 delete checklist;
410 return retval;
411 }
412
413 /** Mangles headers for a list of headers. */
414 void
415 httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
416 {
417 HttpHeaderEntry *e;
418 HttpHeaderPos p = HttpHeaderInitPos;
419
420 int headers_deleted = 0;
421 while ((e = l->getEntry(&p)))
422 if (0 == httpHdrMangle(e, request, req_or_rep))
423 l->delAt(p, headers_deleted);
424
425 if (headers_deleted)
426 l->refreshMask();
427 }
428
429 /**
430 * return 1 if manglers are configured. Used to set a flag
431 * for optimization during request forwarding.
432 */
433 int
434 httpReqHdrManglersConfigured()
435 {
436 for (int i = 0; i < HDR_ENUM_END; i++) {
437 if (NULL != Config.request_header_access[i].access_list)
438 return 1;
439 }
440
441 return 0;
442 }