Line data Source code
1 : /*********************************************************************
2 : *
3 : * File : $Source: /cvsroot/ijbswa/current/filters.c,v $
4 : *
5 : * Purpose : Declares functions to parse/crunch headers and pages.
6 : *
7 : * Copyright : Written by and Copyright (C) 2001-2020 the
8 : * Privoxy team. https://www.privoxy.org/
9 : *
10 : * Based on the Internet Junkbuster originally written
11 : * by and Copyright (C) 1997 Anonymous Coders and
12 : * Junkbusters Corporation. http://www.junkbusters.com
13 : *
14 : * This program is free software; you can redistribute it
15 : * and/or modify it under the terms of the GNU General
16 : * Public License as published by the Free Software
17 : * Foundation; either version 2 of the License, or (at
18 : * your option) any later version.
19 : *
20 : * This program is distributed in the hope that it will
21 : * be useful, but WITHOUT ANY WARRANTY; without even the
22 : * implied warranty of MERCHANTABILITY or FITNESS FOR A
23 : * PARTICULAR PURPOSE. See the GNU General Public
24 : * License for more details.
25 : *
26 : * The GNU General Public License should be included with
27 : * this file. If not, you can view it at
28 : * http://www.gnu.org/copyleft/gpl.html
29 : * or write to the Free Software Foundation, Inc., 59
30 : * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 : *
32 : *********************************************************************/
33 :
34 :
35 : #include "config.h"
36 :
37 : #include <stdio.h>
38 : #include <sys/types.h>
39 : #include <stdlib.h>
40 : #include <ctype.h>
41 : #include <string.h>
42 : #include <assert.h>
43 :
44 : #ifndef _WIN32
45 : #include <unistd.h>
46 : #include <netinet/in.h>
47 : #else
48 : #include <winsock2.h>
49 : #endif /* ndef _WIN32 */
50 :
51 : #include "project.h"
52 : #include "filters.h"
53 : #include "encode.h"
54 : #include "parsers.h"
55 : #include "ssplit.h"
56 : #include "errlog.h"
57 : #include "jbsockets.h"
58 : #include "miscutil.h"
59 : #include "actions.h"
60 : #include "cgi.h"
61 : #include "jcc.h"
62 : #include "list.h"
63 : #include "deanimate.h"
64 : #include "urlmatch.h"
65 : #include "loaders.h"
66 : #ifdef FEATURE_CLIENT_TAGS
67 : #include "client-tags.h"
68 : #endif
69 : #ifdef FEATURE_HTTPS_INSPECTION
70 : #include "ssl.h"
71 : #endif
72 :
73 : #ifdef _WIN32
74 : #include "win32.h"
75 : #endif
76 :
77 : typedef char *(*filter_function_ptr)();
78 : static filter_function_ptr get_filter_function(const struct client_state *csp);
79 : static jb_err prepare_for_filtering(struct client_state *csp);
80 : static void apply_url_actions(struct current_action_spec *action,
81 : struct http_request *http,
82 : #ifdef FEATURE_CLIENT_TAGS
83 : const struct list *client_tags,
84 : #endif
85 : struct url_actions *b);
86 :
87 : #ifdef FEATURE_EXTENDED_STATISTICS
88 : static void increment_block_reason_counter(const char *block_reason);
89 : #endif
90 :
91 : #ifdef FEATURE_ACL
92 : #ifdef HAVE_RFC2553
93 : /*********************************************************************
94 : *
95 : * Function : sockaddr_storage_to_ip
96 : *
97 : * Description : Access internal structure of sockaddr_storage
98 : *
99 : * Parameters :
100 : * 1 : addr = socket address
101 : * 2 : ip = IP address as array of octets in network order
102 : * (it points into addr)
103 : * 3 : len = length of IP address in octets
104 : * 4 : port = port number in network order;
105 : *
106 : * Returns : void
107 : *
108 : *********************************************************************/
109 3098 : static void sockaddr_storage_to_ip(const struct sockaddr_storage *addr,
110 : uint8_t **ip, unsigned int *len,
111 : in_port_t **port)
112 : {
113 3098 : assert(NULL != addr);
114 3098 : assert(addr->ss_family == AF_INET || addr->ss_family == AF_INET6);
115 :
116 3098 : switch (addr->ss_family)
117 : {
118 3098 : case AF_INET:
119 3098 : if (NULL != len)
120 : {
121 3098 : *len = 4;
122 : }
123 3098 : if (NULL != ip)
124 : {
125 3098 : *ip = (uint8_t *)
126 3098 : &(((struct sockaddr_in *)addr)->sin_addr.s_addr);
127 : }
128 3098 : if (NULL != port)
129 : {
130 3098 : *port = &((struct sockaddr_in *)addr)->sin_port;
131 : }
132 3098 : break;
133 :
134 0 : case AF_INET6:
135 0 : if (NULL != len)
136 : {
137 0 : *len = 16;
138 : }
139 0 : if (NULL != ip)
140 : {
141 0 : *ip = ((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr;
142 : }
143 0 : if (NULL != port)
144 : {
145 0 : *port = &((struct sockaddr_in6 *)addr)->sin6_port;
146 : }
147 0 : break;
148 :
149 : }
150 3098 : }
151 :
152 :
153 : /*********************************************************************
154 : *
155 : * Function : match_sockaddr
156 : *
157 : * Description : Check whether address matches network (IP address and port)
158 : *
159 : * Parameters :
160 : * 1 : network = socket address of subnework
161 : * 2 : netmask = network mask as socket address
162 : * 3 : address = checked socket address against given network
163 : *
164 : * Returns : 0 = doesn't match; 1 = does match
165 : *
166 : *********************************************************************/
167 0 : static int match_sockaddr(const struct sockaddr_storage *network,
168 : const struct sockaddr_storage *netmask,
169 : const struct sockaddr_storage *address)
170 : {
171 : uint8_t *network_addr, *netmask_addr, *address_addr;
172 : unsigned int addr_len;
173 : in_port_t *network_port, *netmask_port, *address_port;
174 : int i;
175 :
176 0 : if (network->ss_family != netmask->ss_family)
177 : {
178 : /* This should never happen */
179 0 : assert(network->ss_family == netmask->ss_family);
180 0 : log_error(LOG_LEVEL_FATAL, "Network and netmask differ in family.");
181 : }
182 :
183 0 : sockaddr_storage_to_ip(network, &network_addr, &addr_len, &network_port);
184 0 : sockaddr_storage_to_ip(netmask, &netmask_addr, NULL, &netmask_port);
185 0 : sockaddr_storage_to_ip(address, &address_addr, NULL, &address_port);
186 :
187 : /* Check for family */
188 0 : if ((network->ss_family == AF_INET) && (address->ss_family == AF_INET6)
189 0 : && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)address_addr))
190 : {
191 : /* Map AF_INET6 V4MAPPED address into AF_INET */
192 0 : address_addr += 12;
193 0 : addr_len = 4;
194 : }
195 0 : else if ((network->ss_family == AF_INET6) && (address->ss_family == AF_INET)
196 0 : && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)network_addr))
197 : {
198 : /* Map AF_INET6 V4MAPPED network into AF_INET */
199 0 : network_addr += 12;
200 0 : netmask_addr += 12;
201 0 : addr_len = 4;
202 : }
203 :
204 : /* XXX: Port check is signaled in netmask */
205 0 : if (*netmask_port && *network_port != *address_port)
206 : {
207 0 : return 0;
208 : }
209 :
210 : /* TODO: Optimize by checking by words instead of octets */
211 0 : for (i = 0; (i < addr_len) && netmask_addr[i]; i++)
212 : {
213 0 : if ((network_addr[i] & netmask_addr[i]) !=
214 0 : (address_addr[i] & netmask_addr[i]))
215 : {
216 0 : return 0;
217 : }
218 : }
219 :
220 0 : return 1;
221 : }
222 : #endif /* def HAVE_RFC2553 */
223 :
224 :
225 : /*********************************************************************
226 : *
227 : * Function : block_acl
228 : *
229 : * Description : Block this request?
230 : * Decide yes or no based on ACL file.
231 : *
232 : * Parameters :
233 : * 1 : dst = The proxy or gateway address this is going to.
234 : * Or NULL to check all possible targets.
235 : * 2 : csp = Current client state (buffers, headers, etc...)
236 : * Also includes the client IP address.
237 : *
238 : * Returns : 0 = FALSE (don't block) and 1 = TRUE (do block)
239 : *
240 : *********************************************************************/
241 0 : int block_acl(const struct access_control_addr *dst, const struct client_state *csp)
242 : {
243 0 : struct access_control_list *acl = csp->config->acl;
244 :
245 : /* if not using an access control list, then permit the connection */
246 0 : if (acl == NULL)
247 : {
248 0 : return(0);
249 : }
250 :
251 : /* search the list */
252 0 : while (acl != NULL)
253 : {
254 0 : if (
255 : #ifdef HAVE_RFC2553
256 0 : match_sockaddr(&acl->src->addr, &acl->src->mask, &csp->tcp_addr)
257 : #else
258 : (csp->ip_addr_long & acl->src->mask) == acl->src->addr
259 : #endif
260 : )
261 : {
262 0 : if (dst == NULL)
263 : {
264 : /* Just want to check if they have any access */
265 0 : if (acl->action == ACL_PERMIT)
266 : {
267 0 : return(0);
268 : }
269 : else
270 : {
271 0 : return(1);
272 : }
273 : }
274 0 : else if (
275 : #ifdef HAVE_RFC2553
276 : /*
277 : * XXX: An undefined acl->dst is full of zeros and should be
278 : * considered a wildcard address. sockaddr_storage_to_ip()
279 : * fails on such destinations because of unknown sa_familly
280 : * (glibc only?). However this test is not portable.
281 : *
282 : * So, we signal the acl->dst is wildcard in wildcard_dst.
283 : */
284 0 : acl->wildcard_dst ||
285 0 : match_sockaddr(&acl->dst->addr, &acl->dst->mask, &dst->addr)
286 : #else
287 : ((dst->addr & acl->dst->mask) == acl->dst->addr)
288 : && ((dst->port == acl->dst->port) || (acl->dst->port == 0))
289 : #endif
290 : )
291 : {
292 0 : if (acl->action == ACL_PERMIT)
293 : {
294 0 : return(0);
295 : }
296 : else
297 : {
298 0 : return(1);
299 : }
300 : }
301 : }
302 0 : acl = acl->next;
303 : }
304 :
305 0 : return(1);
306 :
307 : }
308 :
309 :
310 : /*********************************************************************
311 : *
312 : * Function : acl_addr
313 : *
314 : * Description : Called from `load_config' to parse an ACL address.
315 : *
316 : * Parameters :
317 : * 1 : aspec = String specifying ACL address.
318 : * 2 : aca = struct access_control_addr to fill in.
319 : *
320 : * Returns : 0 => Ok, everything else is an error.
321 : *
322 : *********************************************************************/
323 3098 : int acl_addr(const char *aspec, struct access_control_addr *aca)
324 : {
325 : int i, masklength;
326 : #ifdef HAVE_RFC2553
327 : struct addrinfo hints, *result;
328 : uint8_t *mask_data;
329 : in_port_t *mask_port;
330 : unsigned int addr_len;
331 : #else
332 : long port;
333 : #endif /* def HAVE_RFC2553 */
334 : char *p;
335 3098 : char *acl_spec = NULL;
336 :
337 : #ifdef HAVE_RFC2553
338 : /* XXX: Depend on ai_family */
339 3098 : masklength = 128;
340 : #else
341 : masklength = 32;
342 : port = 0;
343 : #endif
344 :
345 : /*
346 : * Use a temporary acl spec copy so we can log
347 : * the unmodified original in case of parse errors.
348 : */
349 3098 : acl_spec = strdup_or_die(aspec);
350 :
351 3098 : if ((p = strchr(acl_spec, '/')) != NULL)
352 : {
353 3098 : *p++ = '\0';
354 3098 : if (privoxy_isdigit(*p) == 0)
355 : {
356 0 : freez(acl_spec);
357 0 : return(-1);
358 : }
359 3098 : masklength = atoi(p);
360 : }
361 :
362 3098 : if ((masklength < 0) ||
363 : #ifdef HAVE_RFC2553
364 : (masklength > 128)
365 : #else
366 : (masklength > 32)
367 : #endif
368 : )
369 : {
370 0 : freez(acl_spec);
371 0 : return(-1);
372 : }
373 :
374 3098 : if ((*acl_spec == '[') && (NULL != (p = strchr(acl_spec, ']'))))
375 : {
376 0 : *p = '\0';
377 0 : memmove(acl_spec, acl_spec + 1, (size_t)(p - acl_spec));
378 :
379 0 : if (*++p != ':')
380 : {
381 0 : p = NULL;
382 : }
383 : }
384 : else
385 : {
386 3098 : p = strchr(acl_spec, ':');
387 : }
388 3098 : if (p != NULL)
389 : {
390 0 : assert(*p == ':');
391 0 : *p = '\0';
392 0 : p++;
393 : }
394 :
395 : #ifdef HAVE_RFC2553
396 3098 : memset(&hints, 0, sizeof(struct addrinfo));
397 3098 : hints.ai_family = AF_UNSPEC;
398 3098 : hints.ai_socktype = SOCK_STREAM;
399 :
400 3098 : i = getaddrinfo(acl_spec, p, &hints, &result);
401 :
402 3098 : if (i != 0)
403 : {
404 0 : log_error(LOG_LEVEL_ERROR, "Can not resolve [%s]:%s: %s",
405 : acl_spec, p, gai_strerror(i));
406 0 : freez(acl_spec);
407 0 : return(-1);
408 : }
409 3098 : freez(acl_spec);
410 :
411 : /* TODO: Allow multihomed hostnames */
412 3098 : memcpy(&(aca->addr), result->ai_addr, result->ai_addrlen);
413 3098 : freeaddrinfo(result);
414 : #else
415 : if (p != NULL)
416 : {
417 : char *endptr;
418 :
419 : port = strtol(p, &endptr, 10);
420 :
421 : if (port <= 0 || port > 65535 || *endptr != '\0')
422 : {
423 : freez(acl_spec);
424 : return(-1);
425 : }
426 : }
427 :
428 : aca->port = (unsigned long)port;
429 :
430 : aca->addr = ntohl(resolve_hostname_to_ip(acl_spec));
431 : freez(acl_spec);
432 :
433 : if (aca->addr == INADDR_NONE)
434 : {
435 : /* XXX: This will be logged as parse error. */
436 : return(-1);
437 : }
438 : #endif /* def HAVE_RFC2553 */
439 :
440 : /* build the netmask */
441 : #ifdef HAVE_RFC2553
442 : /* Clip masklength according to current family. */
443 3098 : if ((aca->addr.ss_family == AF_INET) && (masklength > 32))
444 : {
445 0 : masklength = 32;
446 : }
447 :
448 3098 : aca->mask.ss_family = aca->addr.ss_family;
449 3098 : sockaddr_storage_to_ip(&aca->mask, &mask_data, &addr_len, &mask_port);
450 :
451 3098 : if (p)
452 : {
453 : /* ACL contains a port number, check ports in the future. */
454 0 : *mask_port = 1;
455 : }
456 :
457 : /*
458 : * XXX: This could be optimized to operate on whole words instead
459 : * of octets (128-bit CPU could do it in one iteration).
460 : */
461 : /*
462 : * Octets after prefix can be omitted because of
463 : * previous initialization to zeros.
464 : */
465 3098 : for (i = 0; (i < addr_len) && masklength; i++)
466 : {
467 0 : if (masklength >= 8)
468 : {
469 0 : mask_data[i] = 0xFF;
470 0 : masklength -= 8;
471 : }
472 : else
473 : {
474 : /*
475 : * XXX: This assumes MSB of octet is on the left side.
476 : * This should be true for all architectures or solved
477 : * by the link layer.
478 : */
479 0 : mask_data[i] = (uint8_t)~((1 << (8 - masklength)) - 1);
480 0 : masklength = 0;
481 : }
482 : }
483 :
484 : #else
485 : aca->mask = 0;
486 : for (i=1; i <= masklength ; i++)
487 : {
488 : aca->mask |= (1U << (32 - i));
489 : }
490 :
491 : /* now mask off the host portion of the ip address
492 : * (i.e. save on the network portion of the address).
493 : */
494 : aca->addr = aca->addr & aca->mask;
495 : #endif /* def HAVE_RFC2553 */
496 :
497 3098 : return(0);
498 :
499 : }
500 : #endif /* def FEATURE_ACL */
501 :
502 :
503 : /*********************************************************************
504 : *
505 : * Function : connect_port_is_forbidden
506 : *
507 : * Description : Check to see if CONNECT requests to the destination
508 : * port of this request are forbidden. The check is
509 : * independent of the actual request method.
510 : *
511 : * Parameters :
512 : * 1 : csp = Current client state (buffers, headers, etc...)
513 : *
514 : * Returns : True if yes, false otherwise.
515 : *
516 : *********************************************************************/
517 550 : int connect_port_is_forbidden(const struct client_state *csp)
518 : {
519 1061 : return ((csp->action->flags & ACTION_LIMIT_CONNECT) &&
520 511 : !match_portlist(csp->action->string[ACTION_STRING_LIMIT_CONNECT],
521 : csp->http->port));
522 : }
523 :
524 :
525 : /*********************************************************************
526 : *
527 : * Function : block_url
528 : *
529 : * Description : Called from `chat'. Check to see if we need to block this.
530 : *
531 : * Parameters :
532 : * 1 : csp = Current client state (buffers, headers, etc...)
533 : *
534 : * Returns : NULL => unblocked, else HTTP block response
535 : *
536 : *********************************************************************/
537 14576 : struct http_response *block_url(struct client_state *csp)
538 : {
539 : struct http_response *rsp;
540 14576 : const char *new_content_type = NULL;
541 :
542 : /*
543 : * If it's not blocked, don't block it ;-)
544 : */
545 14576 : if ((csp->action->flags & ACTION_BLOCK) == 0)
546 : {
547 12267 : return NULL;
548 : }
549 2309 : if (csp->action->flags & ACTION_REDIRECT)
550 : {
551 1 : log_error(LOG_LEVEL_ERROR, "redirect{} overruled by block.");
552 : }
553 : /*
554 : * Else, prepare a response
555 : */
556 2309 : if (NULL == (rsp = alloc_http_response()))
557 : {
558 0 : return cgi_error_memory();
559 : }
560 :
561 : #ifdef FEATURE_EXTENDED_STATISTICS
562 2309 : if (csp->action->string[ACTION_STRING_BLOCK] != NULL)
563 : {
564 2212 : increment_block_reason_counter(csp->action->string[ACTION_STRING_BLOCK]);
565 : }
566 : #endif
567 :
568 : /*
569 : * If it's an image-url, send back an image or redirect
570 : * as specified by the relevant +image action
571 : */
572 : #ifdef FEATURE_IMAGE_BLOCKING
573 2309 : if (((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
574 49 : && is_imageurl(csp))
575 49 : {
576 : char *p;
577 : /* determine HOW images should be blocked */
578 49 : p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
579 :
580 49 : if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
581 : {
582 0 : log_error(LOG_LEVEL_ERROR, "handle-as-empty-document overruled by handle-as-image.");
583 : }
584 :
585 : /* and handle accordingly: */
586 49 : if ((p == NULL) || (0 == strcmpic(p, "pattern")))
587 : {
588 0 : rsp->status = strdup_or_die("403 Request blocked by Privoxy");
589 0 : rsp->body = bindup(image_pattern_data, image_pattern_length);
590 0 : if (rsp->body == NULL)
591 : {
592 0 : free_http_response(rsp);
593 0 : return cgi_error_memory();
594 : }
595 0 : rsp->content_length = image_pattern_length;
596 :
597 0 : if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
598 : {
599 0 : free_http_response(rsp);
600 0 : return cgi_error_memory();
601 : }
602 : }
603 49 : else if (0 == strcmpic(p, "blank"))
604 : {
605 49 : rsp->status = strdup_or_die("403 Request blocked by Privoxy");
606 49 : rsp->body = bindup(image_blank_data, image_blank_length);
607 49 : if (rsp->body == NULL)
608 : {
609 0 : free_http_response(rsp);
610 0 : return cgi_error_memory();
611 : }
612 49 : rsp->content_length = image_blank_length;
613 :
614 49 : if (enlist_unique_header(rsp->headers, "Content-Type", BUILTIN_IMAGE_MIMETYPE))
615 : {
616 0 : free_http_response(rsp);
617 0 : return cgi_error_memory();
618 : }
619 : }
620 : else
621 : {
622 0 : rsp->status = strdup_or_die("302 Local Redirect from Privoxy");
623 :
624 0 : if (enlist_unique_header(rsp->headers, "Location", p))
625 : {
626 0 : free_http_response(rsp);
627 0 : return cgi_error_memory();
628 : }
629 : }
630 :
631 : }
632 : else
633 : #endif /* def FEATURE_IMAGE_BLOCKING */
634 2260 : if (csp->action->flags & ACTION_HANDLE_AS_EMPTY_DOCUMENT)
635 : {
636 : /*
637 : * Send empty document.
638 : */
639 1138 : new_content_type = csp->action->string[ACTION_STRING_CONTENT_TYPE];
640 :
641 1138 : freez(rsp->body);
642 1138 : rsp->body = strdup_or_die(" ");
643 1138 : rsp->content_length = 1;
644 :
645 1138 : if (csp->config->feature_flags & RUNTIME_FEATURE_EMPTY_DOC_RETURNS_OK)
646 : {
647 : /*
648 : * Workaround for firefox bug 492459
649 : * https://bugzilla.mozilla.org/show_bug.cgi?id=492459
650 : * Return a 200 OK status for pages blocked with +handle-as-empty-document
651 : * if the "handle-as-empty-doc-returns-ok" runtime config option is set.
652 : */
653 0 : rsp->status = strdup_or_die("200 Request blocked by Privoxy");
654 : }
655 : else
656 : {
657 1138 : rsp->status = strdup_or_die("403 Request blocked by Privoxy");
658 : }
659 :
660 1138 : if (new_content_type != 0)
661 : {
662 0 : log_error(LOG_LEVEL_HEADER, "Overwriting Content-Type with %s", new_content_type);
663 0 : if (enlist_unique_header(rsp->headers, "Content-Type", new_content_type))
664 : {
665 0 : free_http_response(rsp);
666 0 : return cgi_error_memory();
667 : }
668 : }
669 : }
670 : else
671 :
672 : /*
673 : * Else, generate an HTML "blocked" message:
674 : */
675 : {
676 : jb_err err;
677 : struct map * exports;
678 :
679 1122 : rsp->status = strdup_or_die("403 Request blocked by Privoxy");
680 :
681 1122 : exports = default_exports(csp, NULL);
682 1122 : if (exports == NULL)
683 : {
684 0 : free_http_response(rsp);
685 0 : return cgi_error_memory();
686 : }
687 :
688 : #ifdef FEATURE_FORCE_LOAD
689 1122 : err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
690 : /*
691 : * Export the force conditional block killer if
692 : *
693 : * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
694 : * - Privoxy is configured to enforce blocks, or
695 : * - it's a CONNECT request and enforcing wouldn't work anyway.
696 : */
697 1122 : if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
698 1122 : || (0 == strcmpic(csp->http->gpc, "connect")))
699 : #endif /* ndef FEATURE_FORCE_LOAD */
700 : {
701 202 : err = map_block_killer(exports, "force-support");
702 : }
703 :
704 1122 : if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
705 1122 : if (!err) err = map(exports, "hostport", 1, html_encode(csp->http->hostport), 0);
706 1122 : if (!err) err = map(exports, "path", 1, html_encode(csp->http->path), 0);
707 1122 : if (!err) err = map(exports, "path-ue", 1, url_encode(csp->http->path), 0);
708 1122 : if (!err)
709 : {
710 : const char *block_reason;
711 1122 : if (csp->action->string[ACTION_STRING_BLOCK] != NULL)
712 : {
713 1025 : block_reason = csp->action->string[ACTION_STRING_BLOCK];
714 : }
715 : else
716 : {
717 97 : assert(connect_port_is_forbidden(csp));
718 97 : block_reason = "Forbidden CONNECT port.";
719 : }
720 1122 : err = map(exports, "block-reason", 1, html_encode(block_reason), 0);
721 : }
722 1122 : if (err)
723 : {
724 0 : free_map(exports);
725 0 : free_http_response(rsp);
726 0 : return cgi_error_memory();
727 : }
728 :
729 1122 : err = template_fill_for_cgi(csp, "blocked", exports, rsp);
730 1122 : if (err)
731 : {
732 0 : free_http_response(rsp);
733 0 : return cgi_error_memory();
734 : }
735 : }
736 2309 : rsp->crunch_reason = BLOCKED;
737 :
738 2309 : return finish_http_response(csp, rsp);
739 :
740 : }
741 :
742 :
743 : #ifdef FEATURE_TRUST
744 : /*********************************************************************
745 : *
746 : * Function : trust_url FIXME: I should be called distrust_url
747 : *
748 : * Description : Calls is_untrusted_url to determine if the URL is trusted
749 : * and if not, returns a HTTP 403 response with a reject message.
750 : *
751 : * Parameters :
752 : * 1 : csp = Current client state (buffers, headers, etc...)
753 : *
754 : * Returns : NULL => trusted, else http_response.
755 : *
756 : *********************************************************************/
757 6841 : struct http_response *trust_url(struct client_state *csp)
758 : {
759 : struct http_response *rsp;
760 : struct map * exports;
761 : char buf[BUFFER_SIZE];
762 : char *p;
763 : struct pattern_spec **tl;
764 : struct pattern_spec *t;
765 : jb_err err;
766 :
767 : /*
768 : * Don't bother to work on trusted URLs
769 : */
770 6841 : if (!is_untrusted_url(csp))
771 : {
772 6841 : return NULL;
773 : }
774 :
775 : /*
776 : * Else, prepare a response:
777 : */
778 0 : if (NULL == (rsp = alloc_http_response()))
779 : {
780 0 : return cgi_error_memory();
781 : }
782 :
783 0 : rsp->status = strdup_or_die("403 Request blocked by Privoxy");
784 0 : exports = default_exports(csp, NULL);
785 0 : if (exports == NULL)
786 : {
787 0 : free_http_response(rsp);
788 0 : return cgi_error_memory();
789 : }
790 :
791 : /*
792 : * Export the protocol, host, port, and referrer information
793 : */
794 0 : err = map(exports, "hostport", 1, csp->http->hostport, 1);
795 0 : if (!err) err = map(exports, "protocol", 1, csp->http->ssl ? "https://" : "http://", 1);
796 0 : if (!err) err = map(exports, "path", 1, csp->http->path, 1);
797 :
798 0 : if (NULL != (p = get_header_value(csp->headers, "Referer:")))
799 : {
800 0 : if (!err) err = map(exports, "referrer", 1, html_encode(p), 0);
801 : }
802 : else
803 : {
804 0 : if (!err) err = map(exports, "referrer", 1, "none set", 1);
805 : }
806 :
807 0 : if (err)
808 : {
809 0 : free_map(exports);
810 0 : free_http_response(rsp);
811 0 : return cgi_error_memory();
812 : }
813 :
814 : /*
815 : * Export the trust list
816 : */
817 0 : p = strdup_or_die("");
818 0 : for (tl = csp->config->trust_list; (t = *tl) != NULL ; tl++)
819 : {
820 0 : snprintf(buf, sizeof(buf), "<li>%s</li>\n", t->spec);
821 0 : string_append(&p, buf);
822 : }
823 0 : err = map(exports, "trusted-referrers", 1, p, 0);
824 :
825 0 : if (err)
826 : {
827 0 : free_map(exports);
828 0 : free_http_response(rsp);
829 0 : return cgi_error_memory();
830 : }
831 :
832 : /*
833 : * Export the trust info, if available
834 : */
835 0 : if (csp->config->trust_info->first)
836 : {
837 : struct list_entry *l;
838 :
839 0 : p = strdup_or_die("");
840 0 : for (l = csp->config->trust_info->first; l ; l = l->next)
841 : {
842 0 : snprintf(buf, sizeof(buf), "<li> <a href=\"%s\">%s</a><br>\n", l->str, l->str);
843 0 : string_append(&p, buf);
844 : }
845 0 : err = map(exports, "trust-info", 1, p, 0);
846 : }
847 : else
848 : {
849 0 : err = map_block_killer(exports, "have-trust-info");
850 : }
851 :
852 0 : if (err)
853 : {
854 0 : free_map(exports);
855 0 : free_http_response(rsp);
856 0 : return cgi_error_memory();
857 : }
858 :
859 : /*
860 : * Export the force conditional block killer if
861 : *
862 : * - Privoxy was compiled without FEATURE_FORCE_LOAD, or
863 : * - Privoxy is configured to enforce blocks, or
864 : * - it's a CONNECT request and enforcing wouldn't work anyway.
865 : */
866 : #ifdef FEATURE_FORCE_LOAD
867 0 : if ((csp->config->feature_flags & RUNTIME_FEATURE_ENFORCE_BLOCKS)
868 0 : || (0 == strcmpic(csp->http->gpc, "connect")))
869 : {
870 0 : err = map_block_killer(exports, "force-support");
871 : }
872 : else
873 : {
874 0 : err = map(exports, "force-prefix", 1, FORCE_PREFIX, 1);
875 : }
876 : #else /* ifndef FEATURE_FORCE_LOAD */
877 : err = map_block_killer(exports, "force-support");
878 : #endif /* ndef FEATURE_FORCE_LOAD */
879 :
880 0 : if (err)
881 : {
882 0 : free_map(exports);
883 0 : free_http_response(rsp);
884 0 : return cgi_error_memory();
885 : }
886 :
887 : /*
888 : * Build the response
889 : */
890 0 : err = template_fill_for_cgi(csp, "untrusted", exports, rsp);
891 0 : if (err)
892 : {
893 0 : free_http_response(rsp);
894 0 : return cgi_error_memory();
895 : }
896 0 : rsp->crunch_reason = UNTRUSTED;
897 :
898 0 : return finish_http_response(csp, rsp);
899 : }
900 : #endif /* def FEATURE_TRUST */
901 :
902 :
903 : /*********************************************************************
904 : *
905 : * Function : compile_dynamic_pcrs_job_list
906 : *
907 : * Description : Compiles a dynamic pcrs job list (one with variables
908 : * resolved at request time)
909 : *
910 : * Parameters :
911 : * 1 : csp = Current client state (buffers, headers, etc...)
912 : * 2 : b = The filter list to compile
913 : *
914 : * Returns : NULL in case of errors, otherwise the
915 : * pcrs job list.
916 : *
917 : *********************************************************************/
918 0 : pcrs_job *compile_dynamic_pcrs_job_list(const struct client_state *csp, const struct re_filterfile_spec *b)
919 : {
920 : struct list_entry *pattern;
921 0 : pcrs_job *job_list = NULL;
922 0 : pcrs_job *dummy = NULL;
923 0 : pcrs_job *lastjob = NULL;
924 0 : int error = 0;
925 :
926 0 : const struct pcrs_variable variables[] =
927 : {
928 0 : {"url", csp->http->url, 1},
929 0 : {"path", csp->http->path, 1},
930 0 : {"host", csp->http->host, 1},
931 0 : {"origin", csp->ip_addr_str, 1},
932 0 : {"listen-address", csp->listen_addr_str, 1},
933 : {NULL, NULL, 1}
934 : };
935 :
936 0 : for (pattern = b->patterns->first; pattern != NULL; pattern = pattern->next)
937 : {
938 0 : assert(pattern->str != NULL);
939 :
940 0 : dummy = pcrs_compile_dynamic_command(pattern->str, variables, &error);
941 0 : if (NULL == dummy)
942 : {
943 0 : log_error(LOG_LEVEL_ERROR,
944 : "Compiling dynamic pcrs job '%s' for '%s' failed with error code %d: %s",
945 : pattern->str, b->name, error, pcrs_strerror(error));
946 0 : continue;
947 : }
948 : else
949 : {
950 0 : if (error == PCRS_WARN_TRUNCATION)
951 : {
952 0 : log_error(LOG_LEVEL_ERROR,
953 : "At least one of the variables in \'%s\' had to "
954 : "be truncated before compilation", pattern->str);
955 : }
956 0 : if (job_list == NULL)
957 : {
958 0 : job_list = dummy;
959 : }
960 : else
961 : {
962 0 : lastjob->next = dummy;
963 : }
964 0 : lastjob = dummy;
965 : }
966 : }
967 :
968 0 : return job_list;
969 : }
970 :
971 :
972 : /*********************************************************************
973 : *
974 : * Function : rewrite_url
975 : *
976 : * Description : Rewrites a URL with a single pcrs command
977 : * and returns the result if it differs from the
978 : * original and isn't obviously invalid.
979 : *
980 : * Parameters :
981 : * 1 : old_url = URL to rewrite.
982 : * 2 : pcrs_command = pcrs command formatted as string (s@foo@bar@)
983 : *
984 : *
985 : * Returns : NULL if the pcrs_command didn't change the url, or
986 : * the result of the modification.
987 : *
988 : *********************************************************************/
989 270 : char *rewrite_url(char *old_url, const char *pcrs_command)
990 : {
991 270 : char *new_url = NULL;
992 : int hits;
993 :
994 270 : assert(old_url);
995 270 : assert(pcrs_command);
996 :
997 270 : new_url = pcrs_execute_single_command(old_url, pcrs_command, &hits);
998 :
999 270 : if (hits == 0)
1000 : {
1001 1 : log_error(LOG_LEVEL_REDIRECTS,
1002 : "pcrs command \"%s\" didn't change \"%s\".",
1003 : pcrs_command, old_url);
1004 1 : freez(new_url);
1005 : }
1006 269 : else if (hits < 0)
1007 : {
1008 0 : log_error(LOG_LEVEL_REDIRECTS,
1009 : "executing pcrs command \"%s\" to rewrite %s failed: %s",
1010 : pcrs_command, old_url, pcrs_strerror(hits));
1011 0 : freez(new_url);
1012 : }
1013 269 : else if (strncmpic(new_url, "http://", 7) && strncmpic(new_url, "https://", 8))
1014 : {
1015 0 : log_error(LOG_LEVEL_ERROR,
1016 : "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s), "
1017 : "but the result doesn't look like a valid URL and will be ignored.",
1018 0 : pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
1019 0 : freez(new_url);
1020 : }
1021 : else
1022 : {
1023 269 : log_error(LOG_LEVEL_REDIRECTS,
1024 : "pcrs command \"%s\" changed \"%s\" to \"%s\" (%u hi%s).",
1025 269 : pcrs_command, old_url, new_url, hits, (hits == 1) ? "t" : "ts");
1026 : }
1027 :
1028 270 : return new_url;
1029 :
1030 : }
1031 :
1032 :
1033 : #ifdef FEATURE_FAST_REDIRECTS
1034 : /*********************************************************************
1035 : *
1036 : * Function : get_last_url
1037 : *
1038 : * Description : Search for the last URL inside a string.
1039 : * If the string already is a URL, it will
1040 : * be the first URL found.
1041 : *
1042 : * Parameters :
1043 : * 1 : subject = the string to check
1044 : * 2 : redirect_mode = +fast-redirect{} mode
1045 : *
1046 : * Returns : NULL if no URL was found, or
1047 : * the last URL found.
1048 : *
1049 : *********************************************************************/
1050 603 : static char *get_last_url(char *subject, const char *redirect_mode)
1051 : {
1052 603 : char *new_url = NULL;
1053 : char *tmp;
1054 :
1055 603 : assert(subject);
1056 603 : assert(redirect_mode);
1057 :
1058 603 : subject = strdup(subject);
1059 603 : if (subject == NULL)
1060 : {
1061 0 : log_error(LOG_LEVEL_ERROR, "Out of memory while searching for redirects.");
1062 0 : return NULL;
1063 : }
1064 :
1065 603 : if (0 == strcmpic(redirect_mode, "check-decoded-url") && strchr(subject, '%'))
1066 303 : {
1067 308 : char *url_segment = NULL;
1068 : char **url_segments;
1069 : size_t max_segments;
1070 : int segments;
1071 :
1072 308 : log_error(LOG_LEVEL_REDIRECTS,
1073 : "Checking \"%s\" for encoded redirects.", subject);
1074 :
1075 : /*
1076 : * Check each parameter in the URL separately.
1077 : * Sectionize the URL at "?" and "&",
1078 : * go backwards through the segments, URL-decode them
1079 : * and look for a URL in the decoded result.
1080 : * Stop the search after the first match.
1081 : *
1082 : * XXX: This estimate is guaranteed to be high enough as we
1083 : * let ssplit() ignore empty fields, but also a bit wasteful.
1084 : */
1085 308 : max_segments = strlen(subject) / 2;
1086 308 : url_segments = malloc(max_segments * sizeof(char *));
1087 :
1088 308 : if (NULL == url_segments)
1089 : {
1090 0 : log_error(LOG_LEVEL_ERROR,
1091 : "Out of memory while decoding URL: %s", subject);
1092 0 : freez(subject);
1093 0 : return NULL;
1094 : }
1095 :
1096 308 : segments = ssplit(subject, "?&", url_segments, max_segments);
1097 :
1098 1321 : while (segments-- > 0)
1099 : {
1100 1316 : char *dtoken = url_decode(url_segments[segments]);
1101 1316 : if (NULL == dtoken)
1102 : {
1103 0 : log_error(LOG_LEVEL_ERROR, "Unable to decode \"%s\".", url_segments[segments]);
1104 0 : continue;
1105 : }
1106 1316 : url_segment = strstr(dtoken, "http://");
1107 1316 : if (NULL == url_segment)
1108 : {
1109 1102 : url_segment = strstr(dtoken, "https://");
1110 : }
1111 1316 : if (NULL != url_segment)
1112 : {
1113 303 : url_segment = strdup_or_die(url_segment);
1114 303 : freez(dtoken);
1115 303 : break;
1116 : }
1117 1013 : freez(dtoken);
1118 : }
1119 308 : freez(subject);
1120 308 : freez(url_segments);
1121 :
1122 308 : if (url_segment == NULL)
1123 : {
1124 5 : return NULL;
1125 : }
1126 303 : subject = url_segment;
1127 : }
1128 : else
1129 : {
1130 : /* Look for a URL inside this one, without decoding anything. */
1131 295 : log_error(LOG_LEVEL_REDIRECTS,
1132 : "Checking \"%s\" for unencoded redirects.", subject);
1133 : }
1134 :
1135 : /*
1136 : * Find the last URL encoded in the request
1137 : */
1138 598 : tmp = subject;
1139 1124 : while ((tmp = strstr(tmp, "http://")) != NULL)
1140 : {
1141 526 : new_url = tmp++;
1142 : }
1143 598 : tmp = (new_url != NULL) ? new_url : subject;
1144 689 : while ((tmp = strstr(tmp, "https://")) != NULL)
1145 : {
1146 91 : new_url = tmp++;
1147 : }
1148 :
1149 598 : if ((new_url != NULL)
1150 593 : && ( (new_url != subject)
1151 286 : || (0 == strncmpic(subject, "http://", 7))
1152 89 : || (0 == strncmpic(subject, "https://", 8))
1153 : ))
1154 : {
1155 : /*
1156 : * Return new URL if we found a redirect
1157 : * or if the subject already was a URL.
1158 : *
1159 : * The second case makes sure that we can
1160 : * chain get_last_url after another redirection check
1161 : * (like rewrite_url) without losing earlier redirects.
1162 : */
1163 593 : new_url = strdup(new_url);
1164 593 : freez(subject);
1165 593 : return new_url;
1166 : }
1167 :
1168 5 : freez(subject);
1169 5 : return NULL;
1170 :
1171 : }
1172 : #endif /* def FEATURE_FAST_REDIRECTS */
1173 :
1174 :
1175 : /*********************************************************************
1176 : *
1177 : * Function : redirect_url
1178 : *
1179 : * Description : Checks if Privoxy should answer the request with
1180 : * a HTTP redirect and generates the redirect if
1181 : * necessary.
1182 : *
1183 : * Parameters :
1184 : * 1 : csp = Current client state (buffers, headers, etc...)
1185 : *
1186 : * Returns : NULL if the request can pass, HTTP redirect otherwise.
1187 : *
1188 : *********************************************************************/
1189 12267 : struct http_response *redirect_url(struct client_state *csp)
1190 : {
1191 : struct http_response *rsp;
1192 : #ifdef FEATURE_FAST_REDIRECTS
1193 : /*
1194 : * XXX: Do we still need FEATURE_FAST_REDIRECTS
1195 : * as compile-time option? The user can easily disable
1196 : * it in his action file.
1197 : */
1198 : char * redirect_mode;
1199 : #endif /* def FEATURE_FAST_REDIRECTS */
1200 12267 : char *new_url = NULL;
1201 : char *redirection_string;
1202 :
1203 12267 : if ((csp->action->flags & ACTION_REDIRECT))
1204 : {
1205 280 : redirection_string = csp->action->string[ACTION_STRING_REDIRECT];
1206 :
1207 : /*
1208 : * If the redirection string begins with 's',
1209 : * assume it's a pcrs command, otherwise treat it as
1210 : * properly formatted URL and use it for the redirection
1211 : * directly.
1212 : *
1213 : * According to (the now obsolete) RFC 2616 section 14.30
1214 : * the URL has to be absolute and if the user tries:
1215 : * +redirect{sadly/this/will/be/parsed/as/pcrs_command.html}
1216 : * she would get undefined results anyway.
1217 : *
1218 : * RFC 7231 7.1.2 actually allows relative references,
1219 : * but those start with a leading slash (RFC 3986 4.2) and
1220 : * thus can't be mistaken for pcrs commands either.
1221 : */
1222 :
1223 280 : if (*redirection_string == 's')
1224 : {
1225 : char *requested_url;
1226 :
1227 : #ifdef FEATURE_HTTPS_INSPECTION
1228 270 : if (client_use_ssl(csp))
1229 : {
1230 : jb_err err;
1231 :
1232 0 : requested_url = strdup_or_die("https://");
1233 0 : err = string_append(&requested_url, csp->http->hostport);
1234 0 : if (!err) err = string_append(&requested_url, csp->http->path);
1235 0 : if (err)
1236 : {
1237 0 : log_error(LOG_LEVEL_FATAL,
1238 : "Failed to rebuild URL 'https://%s%s'",
1239 : csp->http->hostport, csp->http->path);
1240 : }
1241 : }
1242 : else
1243 : #endif
1244 : {
1245 270 : requested_url = csp->http->url;
1246 : }
1247 270 : new_url = rewrite_url(requested_url, redirection_string);
1248 : #ifdef FEATURE_HTTPS_INSPECTION
1249 270 : if (requested_url != csp->http->url)
1250 : {
1251 0 : assert(client_use_ssl(csp));
1252 0 : freez(requested_url);
1253 : }
1254 : #endif
1255 : }
1256 : else
1257 : {
1258 10 : log_error(LOG_LEVEL_REDIRECTS,
1259 : "No pcrs command recognized, assuming that \"%s\" is already properly formatted.",
1260 : redirection_string);
1261 10 : new_url = strdup(redirection_string);
1262 : }
1263 : }
1264 :
1265 : #ifdef FEATURE_FAST_REDIRECTS
1266 12267 : if ((csp->action->flags & ACTION_FAST_REDIRECTS))
1267 : {
1268 :
1269 : char *old_url;
1270 :
1271 603 : redirect_mode = csp->action->string[ACTION_STRING_FAST_REDIRECTS];
1272 :
1273 : /*
1274 : * If it exists, use the previously rewritten URL as input
1275 : * otherwise just use the old path.
1276 : */
1277 603 : old_url = (new_url != NULL) ? new_url : strdup(csp->http->path);
1278 603 : new_url = get_last_url(old_url, redirect_mode);
1279 603 : freez(old_url);
1280 : }
1281 :
1282 : /*
1283 : * Disable redirect checkers, so that they
1284 : * will be only run more than once if the user
1285 : * also enables them through tags.
1286 : *
1287 : * From a performance point of view
1288 : * it doesn't matter, but the duplicated
1289 : * log messages are annoying.
1290 : */
1291 12267 : csp->action->flags &= ~ACTION_FAST_REDIRECTS;
1292 : #endif /* def FEATURE_FAST_REDIRECTS */
1293 12267 : csp->action->flags &= ~ACTION_REDIRECT;
1294 :
1295 : /* Did any redirect action trigger? */
1296 12267 : if (new_url)
1297 : {
1298 872 : if (url_requires_percent_encoding(new_url))
1299 : {
1300 : char *encoded_url;
1301 314 : log_error(LOG_LEVEL_REDIRECTS, "Percent-encoding redirect URL: %N",
1302 : strlen(new_url), new_url);
1303 314 : encoded_url = percent_encode_url(new_url);
1304 314 : freez(new_url);
1305 314 : if (encoded_url == NULL)
1306 : {
1307 0 : return cgi_error_memory();
1308 : }
1309 314 : new_url = encoded_url;
1310 314 : assert(FALSE == url_requires_percent_encoding(new_url));
1311 : }
1312 :
1313 872 : if (0 == strcmpic(new_url, csp->http->url))
1314 : {
1315 0 : log_error(LOG_LEVEL_ERROR,
1316 : "New URL \"%s\" and old URL \"%s\" are the same. Redirection loop prevented.",
1317 : csp->http->url, new_url);
1318 0 : freez(new_url);
1319 : }
1320 : else
1321 : {
1322 872 : log_error(LOG_LEVEL_REDIRECTS, "New URL is: %s", new_url);
1323 :
1324 872 : if (NULL == (rsp = alloc_http_response()))
1325 : {
1326 0 : freez(new_url);
1327 0 : return cgi_error_memory();
1328 : }
1329 :
1330 872 : rsp->status = strdup_or_die("302 Local Redirect from Privoxy");
1331 872 : if (enlist_unique_header(rsp->headers, "Location", new_url))
1332 : {
1333 0 : freez(new_url);
1334 0 : free_http_response(rsp);
1335 0 : return cgi_error_memory();
1336 : }
1337 872 : rsp->crunch_reason = REDIRECTED;
1338 872 : freez(new_url);
1339 :
1340 872 : return finish_http_response(csp, rsp);
1341 : }
1342 : }
1343 :
1344 : /* Only reached if no redirect is required */
1345 11395 : return NULL;
1346 :
1347 : }
1348 :
1349 :
1350 : #ifdef FEATURE_IMAGE_BLOCKING
1351 : /*********************************************************************
1352 : *
1353 : * Function : is_imageurl
1354 : *
1355 : * Description : Given a URL, decide whether it should be treated
1356 : * as image URL or not.
1357 : *
1358 : * Parameters :
1359 : * 1 : csp = Current client state (buffers, headers, etc...)
1360 : *
1361 : * Returns : True (nonzero) if URL is an image URL, false (0)
1362 : * otherwise
1363 : *
1364 : *********************************************************************/
1365 49 : int is_imageurl(const struct client_state *csp)
1366 : {
1367 49 : return ((csp->action->flags & ACTION_IMAGE) != 0);
1368 :
1369 : }
1370 : #endif /* def FEATURE_IMAGE_BLOCKING */
1371 :
1372 :
1373 : #ifdef FEATURE_TRUST
1374 : /*********************************************************************
1375 : *
1376 : * Function : is_untrusted_url
1377 : *
1378 : * Description : Should we "distrust" this URL (and block it)?
1379 : *
1380 : * Yes if it matches a line in the trustfile, or if the
1381 : * referrer matches a line starting with "+" in the
1382 : * trustfile.
1383 : * No otherwise.
1384 : *
1385 : * Parameters :
1386 : * 1 : csp = Current client state (buffers, headers, etc...)
1387 : *
1388 : * Returns : 0 => trusted, 1 => untrusted
1389 : *
1390 : *********************************************************************/
1391 6841 : int is_untrusted_url(const struct client_state *csp)
1392 : {
1393 : struct file_list *fl;
1394 : struct block_spec *b;
1395 : struct pattern_spec **trusted_url;
1396 : struct http_request rhttp[1];
1397 : const char * referer;
1398 : jb_err err;
1399 :
1400 : /*
1401 : * If we don't have a trustlist, we trust everybody
1402 : */
1403 6841 : if (((fl = csp->tlist) == NULL) || ((b = fl->f) == NULL))
1404 : {
1405 6841 : return 0;
1406 : }
1407 :
1408 0 : memset(rhttp, '\0', sizeof(*rhttp));
1409 :
1410 : /*
1411 : * Do we trust the request URL itself?
1412 : */
1413 0 : for (b = b->next; b ; b = b->next)
1414 : {
1415 0 : if (url_match(b->url, csp->http))
1416 : {
1417 0 : return b->reject;
1418 : }
1419 : }
1420 :
1421 0 : if (NULL == (referer = get_header_value(csp->headers, "Referer:")))
1422 : {
1423 : /* no referrer was supplied */
1424 0 : return 1;
1425 : }
1426 :
1427 :
1428 : /*
1429 : * If not, do we maybe trust its referrer?
1430 : */
1431 0 : err = parse_http_url(referer, rhttp, REQUIRE_PROTOCOL);
1432 0 : if (err)
1433 : {
1434 0 : return 1;
1435 : }
1436 :
1437 0 : for (trusted_url = csp->config->trust_list; *trusted_url != NULL; trusted_url++)
1438 : {
1439 0 : if (url_match(*trusted_url, rhttp))
1440 : {
1441 : /* if the URL's referrer is from a trusted referrer, then
1442 : * add the target spec to the trustfile as an unblocked
1443 : * domain and return 0 (which means it's OK).
1444 : */
1445 :
1446 : FILE *fp;
1447 :
1448 0 : if (NULL != (fp = fopen(csp->config->trustfile, "a")))
1449 : {
1450 : char * path;
1451 : char * path_end;
1452 0 : char * new_entry = strdup_or_die("~");
1453 :
1454 0 : string_append(&new_entry, csp->http->hostport);
1455 :
1456 0 : path = csp->http->path;
1457 0 : if ( (path[0] == '/')
1458 0 : && (path[1] == '~')
1459 0 : && ((path_end = strchr(path + 2, '/')) != NULL))
1460 : {
1461 : /* since this path points into a user's home space
1462 : * be sure to include this spec in the trustfile.
1463 : */
1464 0 : long path_len = path_end - path; /* save offset */
1465 0 : path = strdup(path); /* Copy string */
1466 0 : if (path != NULL)
1467 : {
1468 0 : path_end = path + path_len; /* regenerate ptr to new buffer */
1469 0 : *(path_end + 1) = '\0'; /* Truncate path after '/' */
1470 : }
1471 0 : string_join(&new_entry, path);
1472 : }
1473 :
1474 : /*
1475 : * Give a reason for generating this entry.
1476 : */
1477 0 : string_append(&new_entry, " # Trusted referrer was: ");
1478 0 : string_append(&new_entry, referer);
1479 :
1480 0 : if (new_entry != NULL)
1481 : {
1482 0 : if (-1 == fprintf(fp, "%s\n", new_entry))
1483 : {
1484 0 : log_error(LOG_LEVEL_ERROR, "Failed to append \'%s\' to trustfile \'%s\': %E",
1485 0 : new_entry, csp->config->trustfile);
1486 : }
1487 0 : freez(new_entry);
1488 : }
1489 : else
1490 : {
1491 : /* FIXME: No way to handle out-of memory, so mostly ignoring it */
1492 0 : log_error(LOG_LEVEL_ERROR, "Out of memory adding pattern to trust file");
1493 : }
1494 :
1495 0 : fclose(fp);
1496 : }
1497 : else
1498 : {
1499 0 : log_error(LOG_LEVEL_ERROR, "Failed to append new entry for \'%s\' to trustfile \'%s\': %E",
1500 0 : csp->http->hostport, csp->config->trustfile);
1501 : }
1502 0 : return 0;
1503 : }
1504 : }
1505 :
1506 0 : return 1;
1507 : }
1508 : #endif /* def FEATURE_TRUST */
1509 :
1510 :
1511 : /*********************************************************************
1512 : *
1513 : * Function : get_filter
1514 : *
1515 : * Description : Get a filter with a given name and type.
1516 : * Note that taggers are filters, too.
1517 : *
1518 : * Parameters :
1519 : * 1 : csp = Current client state (buffers, headers, etc...)
1520 : * 2 : requested_name = Name of the content filter to get
1521 : * 3 : requested_type = Type of the filter to tagger to lookup
1522 : *
1523 : * Returns : A pointer to the requested filter
1524 : * or NULL if the filter wasn't found
1525 : *
1526 : *********************************************************************/
1527 1716941 : struct re_filterfile_spec *get_filter(const struct client_state *csp,
1528 : const char *requested_name,
1529 : enum filter_type requested_type)
1530 : {
1531 : int i;
1532 : struct re_filterfile_spec *b;
1533 : struct file_list *fl;
1534 :
1535 1716941 : for (i = 0; i < MAX_AF_FILES; i++)
1536 : {
1537 1716941 : fl = csp->rlist[i];
1538 1716941 : if ((NULL == fl) || (NULL == fl->f))
1539 : {
1540 : /*
1541 : * Either there are no filter files left or this
1542 : * filter file just contains no valid filters.
1543 : *
1544 : * Continue to be sure we don't miss valid filter
1545 : * files that are chained after empty or invalid ones.
1546 : */
1547 0 : continue;
1548 : }
1549 :
1550 13715416 : for (b = fl->f; b != NULL; b = b->next)
1551 : {
1552 13715416 : if (b->type != requested_type)
1553 : {
1554 : /* The callers isn't interested in this filter type. */
1555 10136519 : continue;
1556 : }
1557 3578897 : if (strcmp(b->name, requested_name) == 0)
1558 : {
1559 : /* The requested filter has been found. Abort search. */
1560 1716941 : return b;
1561 : }
1562 : }
1563 : }
1564 :
1565 : /* No filter with the given name and type exists. */
1566 0 : return NULL;
1567 :
1568 : }
1569 :
1570 :
1571 : /*********************************************************************
1572 : *
1573 : * Function : pcrs_filter_impl
1574 : *
1575 : * Description : Execute all text substitutions from all applying
1576 : * (based on filter_response_body value) +filter
1577 : * or +client_body_filter actions on the given buffer.
1578 : *
1579 : * Parameters :
1580 : * 1 : csp = Current client state (buffers, headers, etc...)
1581 : * 2 : filter_response_body = when TRUE execute +filter
1582 : * actions; execute +client_body_filter actions otherwise
1583 : * 3 : data = Target data
1584 : * 4 : data_len = Target data len
1585 : *
1586 : * Returns : a pointer to the (newly allocated) modified buffer.
1587 : * or NULL if there were no hits or something went wrong
1588 : *
1589 : *********************************************************************/
1590 2041 : static char *pcrs_filter_impl(const struct client_state *csp, int filter_response_body,
1591 : const char *data, size_t *data_len)
1592 : {
1593 2041 : int hits = 0;
1594 : size_t size, prev_size;
1595 2041 : const int filters_idx =
1596 2041 : filter_response_body ? ACTION_MULTI_FILTER : ACTION_MULTI_CLIENT_BODY_FILTER;
1597 2041 : const enum filter_type filter_type =
1598 2041 : filter_response_body ? FT_CONTENT_FILTER : FT_CLIENT_BODY_FILTER;
1599 :
1600 2041 : const char *old = NULL;
1601 2041 : char *new = NULL;
1602 : pcrs_job *job;
1603 :
1604 : struct re_filterfile_spec *b;
1605 : struct list_entry *filtername;
1606 :
1607 : /*
1608 : * Sanity first
1609 : */
1610 2041 : if (*data_len == 0)
1611 : {
1612 0 : return(NULL);
1613 : }
1614 :
1615 2041 : if (filters_available(csp) == FALSE)
1616 : {
1617 0 : log_error(LOG_LEVEL_ERROR, "Inconsistent configuration: "
1618 : "content filtering enabled, but no content filters available.");
1619 0 : return(NULL);
1620 : }
1621 :
1622 2041 : size = *data_len;
1623 2041 : old = data;
1624 :
1625 : /*
1626 : * For all applying actions, look if a filter by that
1627 : * name exists and if yes, execute it's pcrs_joblist on the
1628 : * buffer.
1629 : */
1630 5262 : for (filtername = csp->action->multi[filters_idx]->first;
1631 3221 : filtername != NULL; filtername = filtername->next)
1632 : {
1633 3221 : int current_hits = 0; /* Number of hits caused by this filter */
1634 3221 : int job_number = 0; /* Which job we're currently executing */
1635 3221 : int job_hits = 0; /* How many hits the current job caused */
1636 : pcrs_job *joblist;
1637 :
1638 3221 : b = get_filter(csp, filtername->str, filter_type);
1639 3221 : if (b == NULL)
1640 : {
1641 0 : continue;
1642 : }
1643 :
1644 3221 : joblist = b->joblist;
1645 :
1646 3221 : if (b->dynamic) joblist = compile_dynamic_pcrs_job_list(csp, b);
1647 :
1648 3221 : if (NULL == joblist)
1649 : {
1650 0 : log_error(LOG_LEVEL_RE_FILTER, "Filter %s has empty joblist. Nothing to do.", b->name);
1651 0 : continue;
1652 : }
1653 :
1654 3221 : prev_size = size;
1655 : /* Apply all jobs from the joblist */
1656 13520 : for (job = joblist; NULL != job; job = job->next)
1657 : {
1658 10300 : job_number++;
1659 10300 : job_hits = pcrs_execute(job, old, size, &new, &size);
1660 :
1661 10300 : if (job_hits >= 0)
1662 : {
1663 : /*
1664 : * That went well. Continue filtering
1665 : * and use the result of this job as
1666 : * input for the next one.
1667 : */
1668 10299 : current_hits += job_hits;
1669 10299 : if (old != data)
1670 : {
1671 8258 : freez(old);
1672 : }
1673 10299 : old = new;
1674 : }
1675 : else
1676 : {
1677 : /*
1678 : * This job caused an unexpected error. Inform the user
1679 : * and skip the rest of the jobs in this filter. We could
1680 : * continue with the next job, but usually the jobs
1681 : * depend on each other or are similar enough to
1682 : * fail for the same reason.
1683 : *
1684 : * At the moment our pcrs expects the error codes of pcre 3.4,
1685 : * but newer pcre versions can return additional error codes.
1686 : * As a result pcrs_strerror()'s error message might be
1687 : * "Unknown error ...", therefore we print the numerical value
1688 : * as well.
1689 : *
1690 : * XXX: Is this important enough for LOG_LEVEL_ERROR or
1691 : * should we use LOG_LEVEL_RE_FILTER instead?
1692 : */
1693 1 : log_error(LOG_LEVEL_ERROR, "Skipped filter \'%s\' after job number %u: %s (%d)",
1694 : b->name, job_number, pcrs_strerror(job_hits), job_hits);
1695 1 : break;
1696 : }
1697 : }
1698 :
1699 3221 : if (b->dynamic) pcrs_free_joblist(joblist);
1700 :
1701 3221 : if (filter_response_body)
1702 : {
1703 2360 : log_error(LOG_LEVEL_RE_FILTER,
1704 : "filtering %s%s (size %lu) with \'%s\' produced %d hits (new size %lu).",
1705 : csp->http->hostport, csp->http->path, prev_size, b->name, current_hits, size);
1706 : }
1707 : else
1708 : {
1709 861 : log_error(LOG_LEVEL_RE_FILTER, "filtering request body from client %s "
1710 : "(size %lu) with \'%s\' produced %d hits (new size %lu).",
1711 : csp->ip_addr_str, prev_size, b->name, current_hits, size);
1712 : }
1713 : #ifdef FEATURE_EXTENDED_STATISTICS
1714 3221 : update_filter_statistics(b->name, current_hits);
1715 : #endif
1716 3221 : hits += current_hits;
1717 : }
1718 :
1719 : /*
1720 : * If there were no hits, destroy our copy and let
1721 : * chat() use the original content
1722 : */
1723 2041 : if (!hits)
1724 : {
1725 1776 : if (old != data && old != new)
1726 : {
1727 0 : freez(old);
1728 : }
1729 1776 : freez(new);
1730 1776 : return(NULL);
1731 : }
1732 :
1733 265 : *data_len = size;
1734 265 : return(new);
1735 : }
1736 :
1737 :
1738 : /*********************************************************************
1739 : *
1740 : * Function : pcrs_filter_response_body
1741 : *
1742 : * Description : Execute all text substitutions from all applying
1743 : * +filter actions on the text buffer that's been
1744 : * accumulated in csp->iob->buf.
1745 : *
1746 : * Parameters :
1747 : * 1 : csp = Current client state (buffers, headers, etc...)
1748 : *
1749 : * Returns : a pointer to the (newly allocated) modified buffer.
1750 : * or NULL if there were no hits or something went wrong
1751 : *
1752 : *********************************************************************/
1753 1180 : static char *pcrs_filter_response_body(struct client_state *csp)
1754 : {
1755 1180 : size_t size = (size_t)(csp->iob->eod - csp->iob->cur);
1756 :
1757 1180 : char *new = NULL;
1758 :
1759 : /*
1760 : * Sanity first
1761 : */
1762 1180 : if (csp->iob->cur >= csp->iob->eod)
1763 : {
1764 0 : return NULL;
1765 : }
1766 :
1767 1180 : new = pcrs_filter_impl(csp, TRUE, csp->iob->cur, &size);
1768 :
1769 1180 : if (new != NULL)
1770 : {
1771 17 : csp->flags |= CSP_FLAG_MODIFIED;
1772 17 : csp->content_length = size;
1773 17 : clear_iob(csp->iob);
1774 : }
1775 :
1776 1180 : return new;
1777 : }
1778 :
1779 :
1780 : #ifdef FEATURE_EXTERNAL_FILTERS
1781 : /*********************************************************************
1782 : *
1783 : * Function : get_external_filter
1784 : *
1785 : * Description : Lookup the code to execute for an external filter.
1786 : * Masks the misuse of the re_filterfile_spec.
1787 : *
1788 : * Parameters :
1789 : * 1 : csp = Current client state (buffers, headers, etc...)
1790 : * 2 : name = Name of the content filter to get
1791 : *
1792 : * Returns : A pointer to the requested code
1793 : * or NULL if the filter wasn't found
1794 : *
1795 : *********************************************************************/
1796 0 : static const char *get_external_filter(const struct client_state *csp,
1797 : const char *name)
1798 : {
1799 : struct re_filterfile_spec *external_filter;
1800 :
1801 0 : external_filter = get_filter(csp, name, FT_EXTERNAL_CONTENT_FILTER);
1802 0 : if (external_filter == NULL)
1803 : {
1804 0 : log_error(LOG_LEVEL_FATAL,
1805 : "Didn't find stuff to execute for external filter: %s",
1806 : name);
1807 : }
1808 :
1809 0 : return external_filter->patterns->first->str;
1810 :
1811 : }
1812 :
1813 :
1814 : /*********************************************************************
1815 : *
1816 : * Function : set_privoxy_variables
1817 : *
1818 : * Description : Sets a couple of privoxy-specific environment variables
1819 : *
1820 : * Parameters :
1821 : * 1 : csp = Current client state (buffers, headers, etc...)
1822 : *
1823 : * Returns : N/A
1824 : *
1825 : *********************************************************************/
1826 0 : static void set_privoxy_variables(const struct client_state *csp)
1827 : {
1828 : int i;
1829 : struct {
1830 : const char *name;
1831 : const char *value;
1832 0 : } env[] = {
1833 0 : { "PRIVOXY_URL", csp->http->url },
1834 0 : { "PRIVOXY_PATH", csp->http->path },
1835 0 : { "PRIVOXY_HOST", csp->http->host },
1836 0 : { "PRIVOXY_ORIGIN", csp->ip_addr_str },
1837 0 : { "PRIVOXY_LISTEN_ADDRESS", csp->listen_addr_str },
1838 : };
1839 :
1840 0 : for (i = 0; i < SZ(env); i++)
1841 : {
1842 0 : if (setenv(env[i].name, env[i].value, 1))
1843 : {
1844 0 : log_error(LOG_LEVEL_ERROR, "Failed to set %s=%s: %E",
1845 : env[i].name, env[i].value);
1846 : }
1847 : }
1848 0 : }
1849 :
1850 :
1851 : /*********************************************************************
1852 : *
1853 : * Function : execute_external_filter
1854 : *
1855 : * Description : Pipe content into external filter and return the output
1856 : *
1857 : * Parameters :
1858 : * 1 : csp = Current client state (buffers, headers, etc...)
1859 : * 2 : name = Name of the external filter to execute
1860 : * 3 : content = The original content to filter
1861 : * 4 : size = The size of the content buffer
1862 : *
1863 : * Returns : a pointer to the (newly allocated) modified buffer.
1864 : * or NULL if there were no hits or something went wrong
1865 : *
1866 : *********************************************************************/
1867 0 : static char *execute_external_filter(const struct client_state *csp,
1868 : const char *name, char *content, size_t *size)
1869 : {
1870 : char cmd[200];
1871 : char file_name[FILENAME_MAX];
1872 : FILE *fp;
1873 : char *filter_output;
1874 : int fd;
1875 : int ret;
1876 : size_t new_size;
1877 : const char *external_filter;
1878 :
1879 0 : if (csp->config->temporary_directory == NULL)
1880 : {
1881 0 : log_error(LOG_LEVEL_ERROR,
1882 : "No temporary-directory configured. Can't execute filter: %s",
1883 : name);
1884 0 : return NULL;
1885 : }
1886 :
1887 0 : external_filter = get_external_filter(csp, name);
1888 :
1889 0 : if (sizeof(file_name) < snprintf(file_name, sizeof(file_name),
1890 0 : "%s/privoxy-XXXXXXXX", csp->config->temporary_directory))
1891 : {
1892 0 : log_error(LOG_LEVEL_ERROR, "temporary-directory path too long");
1893 0 : return NULL;
1894 : }
1895 :
1896 0 : fd = mkstemp(file_name);
1897 0 : if (fd == -1)
1898 : {
1899 0 : log_error(LOG_LEVEL_ERROR, "mkstemp() failed to create %s: %E", file_name);
1900 0 : return NULL;
1901 : }
1902 :
1903 0 : fp = fdopen(fd, "w");
1904 0 : if (fp == NULL)
1905 : {
1906 0 : log_error(LOG_LEVEL_ERROR, "fdopen() failed: %E");
1907 0 : unlink(file_name);
1908 0 : return NULL;
1909 : }
1910 :
1911 : /*
1912 : * The size may be zero if a previous filter discarded everything.
1913 : *
1914 : * This isn't necessary unintentional, so we just don't try
1915 : * to fwrite() nothing and let the user deal with the rest.
1916 : */
1917 0 : if ((*size != 0) && fwrite(content, *size, 1, fp) != 1)
1918 : {
1919 0 : log_error(LOG_LEVEL_ERROR, "fwrite(..., %lu, 1, ..) failed: %E", *size);
1920 0 : unlink(file_name);
1921 0 : fclose(fp);
1922 0 : return NULL;
1923 : }
1924 0 : fclose(fp);
1925 :
1926 0 : if (sizeof(cmd) < snprintf(cmd, sizeof(cmd), "%s < %s", external_filter, file_name))
1927 : {
1928 0 : log_error(LOG_LEVEL_ERROR,
1929 : "temporary-directory or external filter path too long");
1930 0 : unlink(file_name);
1931 0 : return NULL;
1932 : }
1933 :
1934 0 : log_error(LOG_LEVEL_RE_FILTER, "Executing '%s': %s", name, cmd);
1935 :
1936 : /*
1937 : * The locking is necessary to prevent other threads
1938 : * from overwriting the environment variables before
1939 : * the popen fork. Afterwards this no longer matters.
1940 : */
1941 0 : privoxy_mutex_lock(&external_filter_mutex);
1942 0 : set_privoxy_variables(csp);
1943 0 : fp = popen(cmd, "r");
1944 0 : privoxy_mutex_unlock(&external_filter_mutex);
1945 0 : if (fp == NULL)
1946 : {
1947 0 : log_error(LOG_LEVEL_ERROR, "popen(\"%s\", \"r\") failed: %E", cmd);
1948 0 : unlink(file_name);
1949 0 : return NULL;
1950 : }
1951 :
1952 : /* Allocate at least one byte */
1953 0 : filter_output = malloc_or_die(*size + 1);
1954 :
1955 0 : new_size = 0;
1956 0 : while (!feof(fp) && !ferror(fp))
1957 : {
1958 : size_t len;
1959 : /* Could be bigger ... */
1960 : enum { READ_LENGTH = 2048 };
1961 :
1962 0 : if (new_size + READ_LENGTH >= *size)
1963 : {
1964 : char *p;
1965 :
1966 : /* Could be considered wasteful if the content is 'large'. */
1967 0 : *size += (*size >= READ_LENGTH) ? *size : READ_LENGTH;
1968 :
1969 0 : p = realloc(filter_output, *size);
1970 0 : if (p == NULL)
1971 : {
1972 0 : log_error(LOG_LEVEL_ERROR, "Out of memory while reading "
1973 : "external filter output. Using what we got so far.");
1974 0 : break;
1975 : }
1976 0 : filter_output = p;
1977 : }
1978 0 : assert(new_size + READ_LENGTH < *size);
1979 0 : len = fread(&filter_output[new_size], 1, READ_LENGTH, fp);
1980 0 : if (len > 0)
1981 : {
1982 0 : new_size += len;
1983 : }
1984 : }
1985 :
1986 0 : ret = pclose(fp);
1987 0 : if (ret == -1)
1988 : {
1989 0 : log_error(LOG_LEVEL_ERROR, "Executing %s failed: %E", cmd);
1990 : }
1991 : else
1992 : {
1993 0 : log_error(LOG_LEVEL_RE_FILTER,
1994 : "Executing '%s' resulted in return value %d. "
1995 : "Read %lu of up to %lu bytes.", name, (ret >> 8), new_size, *size);
1996 : }
1997 :
1998 0 : unlink(file_name);
1999 0 : *size = new_size;
2000 :
2001 0 : return filter_output;
2002 :
2003 : }
2004 : #endif /* def FEATURE_EXTERNAL_FILTERS */
2005 :
2006 :
2007 : /*********************************************************************
2008 : *
2009 : * Function : pcrs_filter_request_body
2010 : *
2011 : * Description : Execute all text substitutions from all applying
2012 : * +client_body_filter actions on the given text buffer.
2013 : *
2014 : * Parameters :
2015 : * 1 : csp = Current client state (buffers, headers, etc...)
2016 : * 2 : data = Target data
2017 : * 3 : data_len = Target data len
2018 : *
2019 : * Returns : a pointer to the (newly allocated) modified buffer.
2020 : * or NULL if there were no hits or something went wrong
2021 : *
2022 : *********************************************************************/
2023 861 : static char *pcrs_filter_request_body(const struct client_state *csp, const char *data, size_t *data_len)
2024 : {
2025 861 : return pcrs_filter_impl(csp, FALSE, data, data_len);
2026 : }
2027 :
2028 :
2029 : /*********************************************************************
2030 : *
2031 : * Function : gif_deanimate_response
2032 : *
2033 : * Description : Deanimate the GIF image that has been accumulated in
2034 : * csp->iob->buf, set csp->content_length to the modified
2035 : * size and raise the CSP_FLAG_MODIFIED flag.
2036 : *
2037 : * Parameters :
2038 : * 1 : csp = Current client state (buffers, headers, etc...)
2039 : *
2040 : * Returns : a pointer to the (newly allocated) modified buffer.
2041 : * or NULL in case something went wrong.
2042 : *
2043 : *********************************************************************/
2044 : #ifdef FUZZ
2045 566 : char *gif_deanimate_response(struct client_state *csp)
2046 : #else
2047 : static char *gif_deanimate_response(struct client_state *csp)
2048 : #endif
2049 : {
2050 : struct binbuffer *in, *out;
2051 : char *p;
2052 : size_t size;
2053 :
2054 566 : size = (size_t)(csp->iob->eod - csp->iob->cur);
2055 :
2056 566 : in = zalloc_or_die(sizeof(*in));
2057 566 : out = zalloc_or_die(sizeof(*out));
2058 :
2059 566 : in->buffer = csp->iob->cur;
2060 566 : in->size = size;
2061 :
2062 566 : if (gif_deanimate(in, out, strncmp("last", csp->action->string[ACTION_STRING_DEANIMATE], 4)))
2063 : {
2064 564 : log_error(LOG_LEVEL_DEANIMATE, "failed! (gif parsing)");
2065 564 : freez(in);
2066 564 : buf_free(out);
2067 564 : return(NULL);
2068 : }
2069 : else
2070 : {
2071 2 : if ((int)size == out->offset)
2072 : {
2073 0 : log_error(LOG_LEVEL_DEANIMATE, "GIF not changed.");
2074 : }
2075 : else
2076 : {
2077 2 : log_error(LOG_LEVEL_DEANIMATE,
2078 : "Success! GIF shrunk from %lu bytes to %lu.", size, out->offset);
2079 : }
2080 2 : csp->content_length = out->offset;
2081 2 : csp->flags |= CSP_FLAG_MODIFIED;
2082 2 : p = out->buffer;
2083 2 : freez(in);
2084 2 : freez(out);
2085 2 : return(p);
2086 : }
2087 :
2088 : }
2089 :
2090 :
2091 : /*********************************************************************
2092 : *
2093 : * Function : get_filter_function
2094 : *
2095 : * Description : Decides which content filter function has
2096 : * to be applied (if any). Only considers functions
2097 : * for internal filters which are mutually-exclusive.
2098 : *
2099 : * Parameters :
2100 : * 1 : csp = Current client state (buffers, headers, etc...)
2101 : *
2102 : * Returns : The content filter function to run, or
2103 : * NULL if no content filter is active
2104 : *
2105 : *********************************************************************/
2106 1746 : static filter_function_ptr get_filter_function(const struct client_state *csp)
2107 : {
2108 1746 : filter_function_ptr filter_function = NULL;
2109 :
2110 : /*
2111 : * Choose the applying filter function based on
2112 : * the content type and action settings.
2113 : */
2114 2926 : if ((csp->content_type & CT_TEXT) &&
2115 1180 : (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER])))
2116 : {
2117 1180 : filter_function = pcrs_filter_response_body;
2118 : }
2119 566 : else if ((csp->content_type & CT_GIF) &&
2120 566 : (csp->action->flags & ACTION_DEANIMATE))
2121 : {
2122 566 : filter_function = gif_deanimate_response;
2123 : }
2124 :
2125 1746 : return filter_function;
2126 : }
2127 :
2128 :
2129 : /*********************************************************************
2130 : *
2131 : * Function : remove_chunked_transfer_coding
2132 : *
2133 : * Description : In-situ remove the "chunked" transfer coding as defined
2134 : * in RFC 7230 4.1 from a buffer. XXX: The implementation
2135 : * is neither complete nor compliant (TODO #129).
2136 : *
2137 : * Parameters :
2138 : * 1 : buffer = Pointer to the text buffer
2139 : * 2 : size = In: Number of bytes to be processed,
2140 : * Out: Number of bytes after de-chunking.
2141 : * (undefined in case of errors)
2142 : *
2143 : * Returns : JB_ERR_OK for success,
2144 : * JB_ERR_PARSE otherwise
2145 : *
2146 : *********************************************************************/
2147 : #ifdef FUZZ
2148 2187 : extern jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
2149 : #else
2150 : static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
2151 : #endif
2152 : {
2153 2187 : size_t newsize = 0;
2154 2187 : unsigned int chunksize = 0;
2155 : char *from_p, *to_p;
2156 2187 : const char *end_of_buffer = buffer + *size;
2157 :
2158 2187 : if (*size == 0)
2159 : {
2160 0 : log_error(LOG_LEVEL_FATAL, "Invalid chunked input. Buffer is empty.");
2161 0 : return JB_ERR_PARSE;
2162 : }
2163 :
2164 2187 : assert(buffer);
2165 2187 : from_p = to_p = buffer;
2166 :
2167 2187 : if (sscanf(buffer, "%x", &chunksize) != 1)
2168 : {
2169 313 : log_error(LOG_LEVEL_ERROR, "Invalid first chunksize while stripping \"chunked\" transfer coding");
2170 313 : return JB_ERR_PARSE;
2171 : }
2172 :
2173 2854 : while (chunksize > 0U)
2174 : {
2175 : /*
2176 : * If the chunk-size is valid, we should have at least
2177 : * chunk-size bytes of chunk-data and five bytes of
2178 : * meta data (chunk-size, CRLF, CRLF) left in the buffer.
2179 : */
2180 1813 : if (chunksize + 5 >= *size - newsize)
2181 : {
2182 11 : log_error(LOG_LEVEL_ERROR,
2183 : "Chunk size %u exceeds buffered data left. "
2184 : "Already digested %lu of %lu buffered bytes.",
2185 : chunksize, newsize, *size);
2186 11 : return JB_ERR_PARSE;
2187 : }
2188 :
2189 : /*
2190 : * Skip the chunk-size, the optional chunk-ext and the CRLF
2191 : * that is supposed to be located directly before the start
2192 : * of chunk-data.
2193 : */
2194 1802 : if (NULL == (from_p = strstr(from_p, "\r\n")))
2195 : {
2196 652 : log_error(LOG_LEVEL_ERROR, "Parse error while stripping \"chunked\" transfer coding");
2197 652 : return JB_ERR_PARSE;
2198 : }
2199 1150 : from_p += 2;
2200 :
2201 : /*
2202 : * The previous strstr() does not enforce chunk-validity
2203 : * and is sattisfied as long a CRLF is left in the buffer.
2204 : *
2205 : * Make sure the bytes we consider chunk-data are within
2206 : * the valid range.
2207 : */
2208 1150 : if (from_p + chunksize >= end_of_buffer)
2209 : {
2210 37 : log_error(LOG_LEVEL_ERROR,
2211 : "End of chunk is beyond the end of the buffer.");
2212 37 : return JB_ERR_PARSE;
2213 : }
2214 :
2215 1113 : memmove(to_p, from_p, (size_t) chunksize);
2216 1113 : newsize += chunksize;
2217 1113 : to_p = buffer + newsize;
2218 1113 : from_p += chunksize;
2219 :
2220 : /*
2221 : * Not merging this check with the previous one allows us
2222 : * to keep chunks without trailing CRLF. It's not clear
2223 : * if we actually have to care about those, though.
2224 : */
2225 1113 : if (from_p + 2 >= end_of_buffer)
2226 : {
2227 10 : log_error(LOG_LEVEL_ERROR, "Not enough room for trailing CRLF.");
2228 10 : return JB_ERR_PARSE;
2229 : }
2230 1103 : from_p += 2;
2231 1103 : if (sscanf(from_p, "%x", &chunksize) != 1)
2232 : {
2233 123 : log_error(LOG_LEVEL_INFO, "Invalid \"chunked\" transfer encoding detected and ignored.");
2234 123 : break;
2235 : }
2236 : }
2237 :
2238 : /* XXX: Should get its own loglevel. */
2239 1164 : log_error(LOG_LEVEL_RE_FILTER,
2240 : "De-chunking successful. Shrunk from %lu to %lu", *size, newsize);
2241 :
2242 1164 : *size = newsize;
2243 :
2244 1164 : return JB_ERR_OK;
2245 :
2246 : }
2247 :
2248 :
2249 : /*********************************************************************
2250 : *
2251 : * Function : prepare_for_filtering
2252 : *
2253 : * Description : If necessary, de-chunks and decompresses
2254 : * the content so it can get filterd.
2255 : *
2256 : * Parameters :
2257 : * 1 : csp = Current client state (buffers, headers, etc...)
2258 : *
2259 : * Returns : JB_ERR_OK for success,
2260 : * JB_ERR_PARSE otherwise
2261 : *
2262 : *********************************************************************/
2263 3765 : static jb_err prepare_for_filtering(struct client_state *csp)
2264 : {
2265 3765 : jb_err err = JB_ERR_OK;
2266 :
2267 : /*
2268 : * If the body has a "chunked" transfer-encoding,
2269 : * get rid of it, adjusting size and iob->eod
2270 : */
2271 3765 : if (csp->flags & CSP_FLAG_CHUNKED)
2272 : {
2273 2187 : size_t size = (size_t)(csp->iob->eod - csp->iob->cur);
2274 :
2275 2187 : log_error(LOG_LEVEL_RE_FILTER, "Need to de-chunk first");
2276 2187 : err = remove_chunked_transfer_coding(csp->iob->cur, &size);
2277 2187 : if (JB_ERR_OK == err)
2278 : {
2279 1164 : csp->iob->eod = csp->iob->cur + size;
2280 1164 : csp->flags |= CSP_FLAG_MODIFIED;
2281 : }
2282 : else
2283 : {
2284 1023 : return JB_ERR_PARSE;
2285 : }
2286 : }
2287 :
2288 : #ifdef FEATURE_ZLIB
2289 : /*
2290 : * If the body has a supported transfer-encoding,
2291 : * decompress it, adjusting size and iob->eod.
2292 : */
2293 2742 : if ((csp->content_type & (CT_GZIP|CT_DEFLATE))
2294 : #ifdef FEATURE_BROTLI
2295 2405 : || (csp->content_type & CT_BROTLI)
2296 : #endif
2297 : )
2298 : {
2299 367 : if (0 == csp->iob->eod - csp->iob->cur)
2300 : {
2301 : /* Nothing left after de-chunking. */
2302 11 : return JB_ERR_OK;
2303 : }
2304 :
2305 356 : err = decompress_iob(csp);
2306 :
2307 356 : if (JB_ERR_OK == err)
2308 : {
2309 128 : csp->flags |= CSP_FLAG_MODIFIED;
2310 128 : csp->content_type &= ~CT_TABOO;
2311 : }
2312 : else
2313 : {
2314 : /*
2315 : * Unset content types to remember not to
2316 : * modify the Content-Encoding header later.
2317 : */
2318 228 : csp->content_type &= ~CT_GZIP;
2319 228 : csp->content_type &= ~CT_DEFLATE;
2320 : #ifdef FEATURE_BROTLI
2321 228 : csp->content_type &= ~CT_BROTLI;
2322 : #endif
2323 : }
2324 : }
2325 : #endif
2326 :
2327 2731 : return err;
2328 : }
2329 :
2330 :
2331 : /*********************************************************************
2332 : *
2333 : * Function : execute_content_filters
2334 : *
2335 : * Description : Executes a given content filter.
2336 : *
2337 : * Parameters :
2338 : * 1 : csp = Current client state (buffers, headers, etc...)
2339 : *
2340 : * Returns : Pointer to the modified buffer, or
2341 : * NULL if filtering failed or wasn't necessary.
2342 : *
2343 : *********************************************************************/
2344 4492 : char *execute_content_filters(struct client_state *csp)
2345 : {
2346 : char *content;
2347 : filter_function_ptr content_filter;
2348 :
2349 4492 : assert(content_filters_enabled(csp->action));
2350 :
2351 4492 : if (0 == csp->iob->eod - csp->iob->cur)
2352 : {
2353 : /*
2354 : * No content (probably status code 301, 302 ...),
2355 : * no filtering necessary.
2356 : */
2357 727 : return NULL;
2358 : }
2359 :
2360 3765 : if (JB_ERR_OK != prepare_for_filtering(csp))
2361 : {
2362 : /*
2363 : * failed to de-chunk or decompress.
2364 : */
2365 1251 : return NULL;
2366 : }
2367 :
2368 2514 : if (0 == csp->iob->eod - csp->iob->cur)
2369 : {
2370 : /*
2371 : * Clown alarm: chunked and/or compressed nothing delivered.
2372 : */
2373 768 : return NULL;
2374 : }
2375 :
2376 1746 : content_filter = get_filter_function(csp);
2377 1746 : content = (content_filter != NULL) ? (*content_filter)(csp) : NULL;
2378 :
2379 : #ifdef FEATURE_EXTERNAL_FILTERS
2380 2926 : if ((csp->content_type & CT_TEXT) &&
2381 1180 : !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]))
2382 : {
2383 : struct list_entry *filtername;
2384 0 : size_t size = (size_t)csp->content_length;
2385 :
2386 0 : if (content == NULL)
2387 : {
2388 0 : content = csp->iob->cur;
2389 0 : size = (size_t)(csp->iob->eod - csp->iob->cur);
2390 : }
2391 :
2392 0 : for (filtername = csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER]->first;
2393 0 : filtername ; filtername = filtername->next)
2394 : {
2395 0 : char *result = execute_external_filter(csp, filtername->str, content, &size);
2396 0 : if (result != NULL)
2397 : {
2398 0 : if (content != csp->iob->cur)
2399 : {
2400 0 : free(content);
2401 : }
2402 0 : content = result;
2403 : }
2404 : }
2405 0 : csp->flags |= CSP_FLAG_MODIFIED;
2406 0 : csp->content_length = size;
2407 : }
2408 : #endif /* def FEATURE_EXTERNAL_FILTERS */
2409 :
2410 1746 : return content;
2411 :
2412 : }
2413 :
2414 :
2415 : /*********************************************************************
2416 : *
2417 : * Function : execute_client_body_filters
2418 : *
2419 : * Description : Executes client body filters for the request that is buffered
2420 : * in the client_iob. Upon success moves client_iob cur pointer
2421 : * to the end of the processed data.
2422 : *
2423 : * Parameters :
2424 : * 1 : csp = Current client state (buffers, headers, etc...)
2425 : * 2 : content_length = content length. Upon successful filtering
2426 : * the passed value is updated with the new content length.
2427 : *
2428 : * Returns : Pointer to the modified buffer, or
2429 : * NULL if filtering failed or wasn't necessary.
2430 : *
2431 : *********************************************************************/
2432 861 : char *execute_client_body_filters(struct client_state *csp, size_t *content_length)
2433 : {
2434 : char *ret;
2435 :
2436 861 : assert(client_body_filters_enabled(csp->action));
2437 :
2438 861 : if (content_length == 0)
2439 : {
2440 : /*
2441 : * No content, no filtering necessary.
2442 : */
2443 0 : return NULL;
2444 : }
2445 :
2446 861 : ret = pcrs_filter_request_body(csp, csp->client_iob->cur, content_length);
2447 861 : if (ret != NULL)
2448 : {
2449 248 : csp->client_iob->cur = csp->client_iob->eod;
2450 : }
2451 861 : return ret;
2452 : }
2453 :
2454 :
2455 : /*********************************************************************
2456 : *
2457 : * Function : get_url_actions
2458 : *
2459 : * Description : Gets the actions for this URL.
2460 : *
2461 : * Parameters :
2462 : * 1 : csp = Current client state (buffers, headers, etc...)
2463 : * 2 : http = http_request request for blocked URLs
2464 : *
2465 : * Returns : N/A
2466 : *
2467 : *********************************************************************/
2468 33380 : void get_url_actions(struct client_state *csp, struct http_request *http)
2469 : {
2470 : struct file_list *fl;
2471 : struct url_actions *b;
2472 : int i;
2473 :
2474 33380 : init_current_action(csp->action);
2475 :
2476 100140 : for (i = 0; i < MAX_AF_FILES; i++)
2477 : {
2478 100140 : if (((fl = csp->actions_list[i]) == NULL) || ((b = fl->f) == NULL))
2479 : {
2480 33380 : return;
2481 : }
2482 :
2483 : #ifdef FEATURE_CLIENT_TAGS
2484 66760 : apply_url_actions(csp->action, http, csp->client_tags, b);
2485 : #else
2486 : apply_url_actions(csp->action, http, b);
2487 : #endif
2488 : }
2489 :
2490 0 : return;
2491 : }
2492 :
2493 : /*********************************************************************
2494 : *
2495 : * Function : apply_url_actions
2496 : *
2497 : * Description : Applies a list of URL actions.
2498 : *
2499 : * Parameters :
2500 : * 1 : action = Destination.
2501 : * 2 : http = Current URL
2502 : * 3 : client_tags = list of client tags
2503 : * 4 : b = list of URL actions to apply
2504 : *
2505 : * Returns : N/A
2506 : *
2507 : *********************************************************************/
2508 66760 : static void apply_url_actions(struct current_action_spec *action,
2509 : struct http_request *http,
2510 : #ifdef FEATURE_CLIENT_TAGS
2511 : const struct list *client_tags,
2512 : #endif
2513 : struct url_actions *b)
2514 : {
2515 66760 : if (b == NULL)
2516 : {
2517 : /* Should never happen */
2518 0 : return;
2519 : }
2520 :
2521 767740 : for (b = b->next; NULL != b; b = b->next)
2522 : {
2523 700980 : if (url_match(b->url, http))
2524 : {
2525 76420 : merge_current_action(action, b->action);
2526 : }
2527 : #ifdef FEATURE_CLIENT_TAGS
2528 700980 : if (client_tag_match(b->url, client_tags))
2529 : {
2530 792 : merge_current_action(action, b->action);
2531 : }
2532 : #endif
2533 : }
2534 : }
2535 :
2536 :
2537 : /*********************************************************************
2538 : *
2539 : * Function : get_forward_override_settings
2540 : *
2541 : * Description : Returns forward settings as specified with the
2542 : * forward-override{} action. forward-override accepts
2543 : * forward lines similar to the one used in the
2544 : * configuration file, but without the URL pattern.
2545 : *
2546 : * For example:
2547 : *
2548 : * forward / .
2549 : *
2550 : * in the configuration file can be replaced with
2551 : * the action section:
2552 : *
2553 : * {+forward-override{forward .}}
2554 : * /
2555 : *
2556 : * Parameters :
2557 : * 1 : csp = Current client state (buffers, headers, etc...)
2558 : *
2559 : * Returns : Pointer to forwarding structure in case of success.
2560 : * Invalid syntax is fatal.
2561 : *
2562 : *********************************************************************/
2563 0 : static const struct forward_spec *get_forward_override_settings(struct client_state *csp)
2564 : {
2565 0 : const char *forward_override_line = csp->action->string[ACTION_STRING_FORWARD_OVERRIDE];
2566 : char forward_settings[BUFFER_SIZE];
2567 0 : char *http_parent = NULL;
2568 : /* variable names were chosen for consistency reasons. */
2569 0 : struct forward_spec *fwd = NULL;
2570 : int vec_count;
2571 : char *vec[3];
2572 :
2573 0 : assert(csp->action->flags & ACTION_FORWARD_OVERRIDE);
2574 : /* Should be enforced by load_one_actions_file() */
2575 0 : assert(strlen(forward_override_line) < sizeof(forward_settings) - 1);
2576 :
2577 : /* Create a copy ssplit can modify */
2578 0 : strlcpy(forward_settings, forward_override_line, sizeof(forward_settings));
2579 :
2580 0 : if (NULL != csp->fwd)
2581 : {
2582 : /*
2583 : * XXX: Currently necessary to prevent memory
2584 : * leaks when the show-url-info cgi page is visited.
2585 : */
2586 0 : unload_forward_spec(csp->fwd);
2587 : }
2588 :
2589 : /*
2590 : * allocate a new forward node, valid only for
2591 : * the lifetime of this request. Save its location
2592 : * in csp as well, so sweep() can free it later on.
2593 : */
2594 0 : fwd = csp->fwd = zalloc_or_die(sizeof(*fwd));
2595 :
2596 0 : vec_count = ssplit(forward_settings, " \t", vec, SZ(vec));
2597 0 : if ((vec_count == 2) && !strcasecmp(vec[0], "forward"))
2598 : {
2599 0 : fwd->type = SOCKS_NONE;
2600 :
2601 : /* Parse the parent HTTP proxy host:port */
2602 0 : http_parent = vec[1];
2603 :
2604 : }
2605 0 : else if ((vec_count == 2) && !strcasecmp(vec[0], "forward-webserver"))
2606 : {
2607 0 : fwd->type = FORWARD_WEBSERVER;
2608 :
2609 : /* Parse the parent HTTP server host:port */
2610 0 : http_parent = vec[1];
2611 :
2612 : }
2613 0 : else if (vec_count == 3)
2614 : {
2615 0 : char *socks_proxy = NULL;
2616 :
2617 0 : if (!strcasecmp(vec[0], "forward-socks4"))
2618 : {
2619 0 : fwd->type = SOCKS_4;
2620 0 : socks_proxy = vec[1];
2621 : }
2622 0 : else if (!strcasecmp(vec[0], "forward-socks4a"))
2623 : {
2624 0 : fwd->type = SOCKS_4A;
2625 0 : socks_proxy = vec[1];
2626 : }
2627 0 : else if (!strcasecmp(vec[0], "forward-socks5"))
2628 : {
2629 0 : fwd->type = SOCKS_5;
2630 0 : socks_proxy = vec[1];
2631 : }
2632 0 : else if (!strcasecmp(vec[0], "forward-socks5t"))
2633 : {
2634 0 : fwd->type = SOCKS_5T;
2635 0 : socks_proxy = vec[1];
2636 : }
2637 :
2638 0 : if (NULL != socks_proxy)
2639 : {
2640 : /* Parse the SOCKS proxy [user:pass@]host[:port] */
2641 0 : fwd->gateway_port = 1080;
2642 0 : parse_forwarder_address(socks_proxy,
2643 : &fwd->gateway_host, &fwd->gateway_port,
2644 : &fwd->auth_username, &fwd->auth_password);
2645 :
2646 0 : http_parent = vec[2];
2647 : }
2648 : }
2649 :
2650 0 : if (NULL == http_parent)
2651 : {
2652 0 : log_error(LOG_LEVEL_FATAL,
2653 : "Invalid forward-override syntax in: %s", forward_override_line);
2654 : /* Never get here - LOG_LEVEL_FATAL causes program exit */
2655 : }
2656 :
2657 : /* Parse http forwarding settings */
2658 0 : if (strcmp(http_parent, ".") != 0)
2659 : {
2660 0 : fwd->forward_port = 8000;
2661 0 : parse_forwarder_address(http_parent,
2662 : &fwd->forward_host, &fwd->forward_port,
2663 : NULL, NULL);
2664 : }
2665 :
2666 0 : assert (NULL != fwd);
2667 :
2668 0 : log_error(LOG_LEVEL_CONNECT,
2669 : "Overriding forwarding settings based on \'%s\'", forward_override_line);
2670 :
2671 0 : return fwd;
2672 : }
2673 :
2674 :
2675 : /*********************************************************************
2676 : *
2677 : * Function : forward_url
2678 : *
2679 : * Description : Should we forward this to another proxy?
2680 : *
2681 : * Parameters :
2682 : * 1 : csp = Current client state (buffers, headers, etc...)
2683 : * 2 : http = http_request request for current URL
2684 : *
2685 : * Returns : Pointer to forwarding information.
2686 : *
2687 : *********************************************************************/
2688 32420 : const struct forward_spec *forward_url(struct client_state *csp,
2689 : const struct http_request *http)
2690 : {
2691 : static const struct forward_spec fwd_default[1]; /* Zero'ed due to being static. */
2692 32420 : struct forward_spec *fwd = csp->config->forward;
2693 :
2694 32420 : if (csp->action->flags & ACTION_FORWARD_OVERRIDE)
2695 : {
2696 0 : return get_forward_override_settings(csp);
2697 : }
2698 :
2699 32420 : if (fwd == NULL)
2700 : {
2701 32420 : return fwd_default;
2702 : }
2703 :
2704 0 : while (fwd != NULL)
2705 : {
2706 0 : if (url_match(fwd->url, http))
2707 : {
2708 0 : return fwd;
2709 : }
2710 0 : fwd = fwd->next;
2711 : }
2712 :
2713 0 : return fwd_default;
2714 : }
2715 :
2716 :
2717 : /*********************************************************************
2718 : *
2719 : * Function : direct_response
2720 : *
2721 : * Description : Check if Max-Forwards == 0 for an OPTIONS or TRACE
2722 : * request and if so, return a HTTP 501 to the client.
2723 : *
2724 : * FIXME: I have a stupid name and I should handle the
2725 : * requests properly. Still, what we do here is rfc-
2726 : * compliant, whereas ignoring or forwarding are not.
2727 : *
2728 : * Parameters :
2729 : * 1 : csp = Current client state (buffers, headers, etc...)
2730 : *
2731 : * Returns : http_response if , NULL if nonmatch or handler fail
2732 : *
2733 : *********************************************************************/
2734 10692 : struct http_response *direct_response(struct client_state *csp)
2735 : {
2736 : struct http_response *rsp;
2737 : struct list_entry *p;
2738 :
2739 10692 : if ((0 == strcmpic(csp->http->gpc, "trace"))
2740 10123 : || (0 == strcmpic(csp->http->gpc, "options")))
2741 : {
2742 10736 : for (p = csp->headers->first; (p != NULL) ; p = p->next)
2743 : {
2744 10594 : if (!strncmpic(p->str, "Max-Forwards:", 13))
2745 : {
2746 : unsigned int max_forwards;
2747 :
2748 : /*
2749 : * If it's a Max-Forwards value of zero,
2750 : * we have to intercept the request.
2751 : */
2752 963 : if (1 == sscanf(p->str+12, ": %u", &max_forwards) && max_forwards == 0)
2753 : {
2754 : /*
2755 : * FIXME: We could handle at least TRACE here,
2756 : * but that would require a verbatim copy of
2757 : * the request which we don't have anymore
2758 : */
2759 928 : log_error(LOG_LEVEL_HEADER,
2760 : "Detected header \'%s\' in OPTIONS or TRACE request. Returning 501.",
2761 : p->str);
2762 :
2763 : /* Get mem for response or fail*/
2764 928 : if (NULL == (rsp = alloc_http_response()))
2765 : {
2766 928 : return cgi_error_memory();
2767 : }
2768 :
2769 928 : rsp->status = strdup_or_die("501 Not Implemented");
2770 928 : rsp->is_static = 1;
2771 928 : rsp->crunch_reason = UNSUPPORTED;
2772 :
2773 928 : return(finish_http_response(csp, rsp));
2774 : }
2775 : }
2776 : }
2777 : }
2778 9764 : return NULL;
2779 : }
2780 :
2781 :
2782 : /*********************************************************************
2783 : *
2784 : * Function : content_requires_filtering
2785 : *
2786 : * Description : Checks whether there are any content filters
2787 : * enabled for the current request and if they
2788 : * can actually be applied..
2789 : *
2790 : * Parameters :
2791 : * 1 : csp = Current client state (buffers, headers, etc...)
2792 : *
2793 : * Returns : TRUE for yes, FALSE otherwise
2794 : *
2795 : *********************************************************************/
2796 5984 : int content_requires_filtering(struct client_state *csp)
2797 : {
2798 5984 : if ((csp->content_type & CT_TABOO)
2799 76 : && !(csp->action->flags & ACTION_FORCE_TEXT_MODE))
2800 : {
2801 76 : return FALSE;
2802 : }
2803 :
2804 : /*
2805 : * Are we enabling text mode by force?
2806 : */
2807 5908 : if (csp->action->flags & ACTION_FORCE_TEXT_MODE)
2808 : {
2809 : /*
2810 : * Do we really have to?
2811 : */
2812 0 : if (csp->content_type & CT_TEXT)
2813 : {
2814 0 : log_error(LOG_LEVEL_HEADER, "Text mode is already enabled.");
2815 : }
2816 : else
2817 : {
2818 0 : csp->content_type |= CT_TEXT;
2819 0 : log_error(LOG_LEVEL_HEADER, "Text mode enabled by force. Take cover!");
2820 : }
2821 : }
2822 :
2823 5908 : if (!(csp->content_type & CT_DECLARED))
2824 : {
2825 : /*
2826 : * The server didn't bother to declare a MIME-Type.
2827 : * Assume it's text that can be filtered.
2828 : *
2829 : * This also regularly happens with 304 responses,
2830 : * therefore logging anything here would cause
2831 : * too much noise.
2832 : */
2833 4267 : csp->content_type |= CT_TEXT;
2834 : }
2835 :
2836 : /*
2837 : * Choose the applying filter function based on
2838 : * the content type and action settings.
2839 : */
2840 10467 : if ((csp->content_type & CT_TEXT) &&
2841 5061 : (!list_is_empty(csp->action->multi[ACTION_MULTI_FILTER]) ||
2842 502 : !list_is_empty(csp->action->multi[ACTION_MULTI_EXTERNAL_FILTER])))
2843 : {
2844 4057 : return TRUE;
2845 : }
2846 1851 : else if ((csp->content_type & CT_GIF) &&
2847 667 : (csp->action->flags & ACTION_DEANIMATE))
2848 : {
2849 575 : return TRUE;
2850 : }
2851 :
2852 1276 : return FALSE;
2853 :
2854 : }
2855 :
2856 :
2857 : /*********************************************************************
2858 : *
2859 : * Function : content_filters_enabled
2860 : *
2861 : * Description : Checks whether there are any content filters
2862 : * enabled for the current request.
2863 : *
2864 : * Parameters :
2865 : * 1 : action = Action spec to check.
2866 : *
2867 : * Returns : TRUE for yes, FALSE otherwise
2868 : *
2869 : *********************************************************************/
2870 6691 : int content_filters_enabled(const struct current_action_spec *action)
2871 : {
2872 7249 : return ((action->flags & ACTION_DEANIMATE) ||
2873 7249 : !list_is_empty(action->multi[ACTION_MULTI_FILTER]) ||
2874 558 : !list_is_empty(action->multi[ACTION_MULTI_EXTERNAL_FILTER]));
2875 : }
2876 :
2877 :
2878 : /*********************************************************************
2879 : *
2880 : * Function : client_body_filters_enabled
2881 : *
2882 : * Description : Checks whether there are any client body filters
2883 : * enabled for the current request.
2884 : *
2885 : * Parameters :
2886 : * 1 : action = Action spec to check.
2887 : *
2888 : * Returns : TRUE for yes, FALSE otherwise
2889 : *
2890 : *********************************************************************/
2891 2076 : int client_body_filters_enabled(const struct current_action_spec *action)
2892 : {
2893 2076 : return !list_is_empty(action->multi[ACTION_MULTI_CLIENT_BODY_FILTER]);
2894 : }
2895 :
2896 :
2897 : /*********************************************************************
2898 : *
2899 : * Function : filters_available
2900 : *
2901 : * Description : Checks whether there are any filters available.
2902 : *
2903 : * Parameters :
2904 : * 1 : csp = Current client state (buffers, headers, etc...)
2905 : *
2906 : * Returns : TRUE for yes, FALSE otherwise.
2907 : *
2908 : *********************************************************************/
2909 773792 : int filters_available(const struct client_state *csp)
2910 : {
2911 : int i;
2912 773792 : for (i = 0; i < MAX_AF_FILES; i++)
2913 : {
2914 773792 : const struct file_list *fl = csp->rlist[i];
2915 773792 : if ((NULL != fl) && (NULL != fl->f))
2916 : {
2917 773792 : return TRUE;
2918 : }
2919 : }
2920 0 : return FALSE;
2921 : }
2922 :
2923 : #ifdef FEATURE_EXTENDED_STATISTICS
2924 :
2925 : struct filter_statistics_entry
2926 : {
2927 : char *filter;
2928 : unsigned long long executions;
2929 : unsigned long long response_bodies_modified;
2930 : unsigned long long hits;
2931 :
2932 : struct filter_statistics_entry *next;
2933 : };
2934 :
2935 : static struct filter_statistics_entry *filter_statistics = NULL;
2936 :
2937 :
2938 : /*********************************************************************
2939 : *
2940 : * Function : register_filter_for_statistics
2941 : *
2942 : * Description : Registers a filter so we can gather statistics for
2943 : * it unless the filter has already been registered
2944 : * before.
2945 : *
2946 : * Parameters :
2947 : * 1 : filter = Name of the filter to register
2948 : *
2949 : * Returns : void
2950 : *
2951 : *********************************************************************/
2952 37176 : void register_filter_for_statistics(const char *filter)
2953 : {
2954 : struct filter_statistics_entry *entry;
2955 :
2956 37176 : privoxy_mutex_lock(&filter_statistics_mutex);
2957 :
2958 37176 : if (filter_statistics == NULL)
2959 : {
2960 3098 : filter_statistics = zalloc_or_die(sizeof(struct filter_statistics_entry));
2961 3098 : entry = filter_statistics;
2962 3098 : entry->filter = strdup_or_die(filter);
2963 3098 : privoxy_mutex_unlock(&filter_statistics_mutex);
2964 3098 : return;
2965 : }
2966 34078 : entry = filter_statistics;
2967 204468 : while (entry != NULL)
2968 : {
2969 204468 : if (!strcmp(entry->filter, filter))
2970 : {
2971 : /* Already registered, nothing to do. */
2972 0 : break;
2973 : }
2974 204468 : if (entry->next == NULL)
2975 : {
2976 34078 : entry->next = zalloc_or_die(sizeof(struct filter_statistics_entry));
2977 34078 : entry->next->filter = strdup_or_die(filter);
2978 34078 : break;
2979 : }
2980 170390 : entry = entry->next;
2981 : }
2982 :
2983 34078 : privoxy_mutex_unlock(&filter_statistics_mutex);
2984 :
2985 : }
2986 :
2987 :
2988 : /*********************************************************************
2989 : *
2990 : * Function : update_filter_statistics
2991 : *
2992 : * Description : Updates the statistics for a filter.
2993 : *
2994 : * Parameters :
2995 : * 1 : filter = Name of the filter to update
2996 : * 2 : hits = Hit count.
2997 : *
2998 : * Returns : void
2999 : *
3000 : *********************************************************************/
3001 3221 : void update_filter_statistics(const char *filter, int hits)
3002 : {
3003 : struct filter_statistics_entry *entry;
3004 :
3005 3221 : privoxy_mutex_lock(&filter_statistics_mutex);
3006 :
3007 3221 : entry = filter_statistics;
3008 18592 : while (entry != NULL)
3009 : {
3010 18592 : if (!strcmp(entry->filter, filter))
3011 : {
3012 3221 : entry->executions++;
3013 3221 : if (hits != 0)
3014 : {
3015 265 : entry->response_bodies_modified++;
3016 265 : entry->hits += (unsigned)hits;
3017 : }
3018 3221 : break;
3019 : }
3020 15371 : entry = entry->next;
3021 : }
3022 :
3023 3221 : privoxy_mutex_unlock(&filter_statistics_mutex);
3024 :
3025 3221 : }
3026 :
3027 :
3028 : /*********************************************************************
3029 : *
3030 : * Function : get_filter_statistics
3031 : *
3032 : * Description : Gets the statistics for a filter.
3033 : *
3034 : * Parameters :
3035 : * 1 : filter = Name of the filter to get statistics for.
3036 : * 2 : executions = Storage for the execution count.
3037 : * 3 : response_bodies_modified = Storage for the number
3038 : * of modified response bodies.
3039 : * 4 : hits = Storage for the number of hits.
3040 : *
3041 : * Returns : void
3042 : *
3043 : *********************************************************************/
3044 2000 : void get_filter_statistics(const char *filter, unsigned long long *executions,
3045 : unsigned long long *response_bodies_modified,
3046 : unsigned long long *hits)
3047 : {
3048 : struct filter_statistics_entry *entry;
3049 :
3050 2000 : privoxy_mutex_lock(&filter_statistics_mutex);
3051 :
3052 2000 : entry = filter_statistics;
3053 5000 : while (entry != NULL)
3054 : {
3055 5000 : if (!strcmp(entry->filter, filter))
3056 : {
3057 2000 : *executions = entry->executions;
3058 2000 : *response_bodies_modified = entry->response_bodies_modified;
3059 2000 : *hits = entry->hits;
3060 2000 : break;
3061 : }
3062 3000 : entry = entry->next;
3063 : }
3064 :
3065 2000 : privoxy_mutex_unlock(&filter_statistics_mutex);
3066 :
3067 2000 : }
3068 :
3069 :
3070 : struct block_statistics_entry
3071 : {
3072 : char *block_reason;
3073 : unsigned long long count;
3074 :
3075 : struct block_statistics_entry *next;
3076 : };
3077 :
3078 : static struct block_statistics_entry *block_statistics = NULL;
3079 :
3080 : /*********************************************************************
3081 : *
3082 : * Function : register_block_reason_for_statistics
3083 : *
3084 : * Description : Registers a block reason so we can gather statistics
3085 : * for it unless the block reason has already been
3086 : * registered before.
3087 : *
3088 : * Parameters :
3089 : * 1 : block_reason = Block reason to register
3090 : *
3091 : * Returns : void
3092 : *
3093 : *********************************************************************/
3094 14448 : void register_block_reason_for_statistics(const char *block_reason)
3095 : {
3096 : struct block_statistics_entry *entry;
3097 :
3098 14448 : privoxy_mutex_lock(&block_statistics_mutex);
3099 :
3100 14448 : if (block_statistics == NULL)
3101 : {
3102 3098 : block_statistics = zalloc_or_die(sizeof(struct block_statistics_entry));
3103 3098 : entry = block_statistics;
3104 3098 : entry->block_reason = strdup_or_die(block_reason);
3105 3098 : privoxy_mutex_unlock(&block_statistics_mutex);
3106 3098 : return;
3107 : }
3108 11350 : entry = block_statistics;
3109 19602 : while (entry != NULL)
3110 : {
3111 19602 : if (!strcmp(entry->block_reason, block_reason))
3112 : {
3113 : /* Already registered, nothing to do. */
3114 5154 : break;
3115 : }
3116 14448 : if (entry->next == NULL)
3117 : {
3118 6196 : entry->next = zalloc_or_die(sizeof(struct block_statistics_entry));
3119 6196 : entry->next->block_reason = strdup_or_die(block_reason);
3120 6196 : break;
3121 : }
3122 8252 : entry = entry->next;
3123 : }
3124 :
3125 11350 : privoxy_mutex_unlock(&block_statistics_mutex);
3126 :
3127 : }
3128 :
3129 :
3130 : /*********************************************************************
3131 : *
3132 : * Function : increment_block_reason_counter
3133 : *
3134 : * Description : Updates the counter for a block reason.
3135 : *
3136 : * Parameters :
3137 : * 1 : block_reason = Block reason to count
3138 : *
3139 : * Returns : void
3140 : *
3141 : *********************************************************************/
3142 2212 : static void increment_block_reason_counter(const char *block_reason)
3143 : {
3144 : struct block_statistics_entry *entry;
3145 :
3146 2212 : privoxy_mutex_lock(&block_statistics_mutex);
3147 :
3148 2212 : entry = block_statistics;
3149 5513 : while (entry != NULL)
3150 : {
3151 5513 : if (!strcmp(entry->block_reason, block_reason))
3152 : {
3153 2212 : entry->count++;
3154 2212 : break;
3155 : }
3156 3301 : entry = entry->next;
3157 : }
3158 :
3159 2212 : privoxy_mutex_unlock(&block_statistics_mutex);
3160 :
3161 2212 : }
3162 :
3163 :
3164 : /*********************************************************************
3165 : *
3166 : * Function : get_block_reason_count
3167 : *
3168 : * Description : Gets number of times a block reason was used.
3169 : *
3170 : * Parameters :
3171 : * 1 : block_reason = Block reason to get statistics for.
3172 : * 2 : count = Storage for the number of times the block
3173 : * reason was used.
3174 : *
3175 : * Returns : void
3176 : *
3177 : *********************************************************************/
3178 1500 : void get_block_reason_count(const char *block_reason, unsigned long long *count)
3179 : {
3180 : struct block_statistics_entry *entry;
3181 :
3182 1500 : privoxy_mutex_lock(&block_statistics_mutex);
3183 :
3184 1500 : entry = block_statistics;
3185 3000 : while (entry != NULL)
3186 : {
3187 3000 : if (!strcmp(entry->block_reason, block_reason))
3188 : {
3189 1500 : *count = entry->count;
3190 1500 : break;
3191 : }
3192 1500 : entry = entry->next;
3193 : }
3194 :
3195 1500 : privoxy_mutex_unlock(&block_statistics_mutex);
3196 :
3197 1500 : }
3198 :
3199 : #endif /* def FEATURE_EXTENDED_STATISTICS */
3200 :
3201 : /*
3202 : Local Variables:
3203 : tab-width: 3
3204 : end:
3205 : */
|