2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29 typedef struct _Domain
{
32 } DomainRec
, *DomainPtr
;
34 AtomPtr forbiddenFile
= NULL
;
35 AtomPtr forbiddenUrl
= NULL
;
36 int forbiddenRedirectCode
= 302;
38 AtomPtr redirector
= NULL
;
39 int redirectorRedirectCode
= 302;
41 DomainPtr
*forbiddenDomains
= NULL
;
42 regex_t
*forbiddenRegex
= NULL
;
44 AtomPtr uncachableFile
= NULL
;
45 DomainPtr
*uncachableDomains
= NULL
;
46 regex_t
*uncachableRegex
= NULL
;
48 /* these three are only used internally by {parse,read}DomainFile */
49 /* to avoid having to pass it all as parameters */
50 static DomainPtr
*domains
;
51 static char *regexbuf
;
52 static int rlen
, rsize
, dlen
, dsize
;
54 static pid_t redirector_pid
= 0;
55 static int redirector_read_fd
= -1, redirector_write_fd
= -1;
56 static char redirector_buffer
[512];
57 RedirectRequestPtr redirector_request_first
= NULL
,
58 redirector_request_last
= NULL
;
60 static int atomSetterForbidden(ConfigVariablePtr
, void*);
63 preinitForbidden(void)
65 CONFIG_VARIABLE_SETTABLE(forbiddenUrl
, CONFIG_ATOM
, configAtomSetter
,
66 "URL to which forbidden requests "
67 "should be redirected.");
68 CONFIG_VARIABLE_SETTABLE(forbiddenRedirectCode
, CONFIG_INT
,
70 "Redirect code, 301 or 302.");
71 CONFIG_VARIABLE_SETTABLE(forbiddenFile
, CONFIG_ATOM
, atomSetterForbidden
,
72 "File specifying forbidden URLs.");
74 CONFIG_VARIABLE_SETTABLE(redirector
, CONFIG_ATOM
, atomSetterForbidden
,
75 "Squid-style redirector.");
76 CONFIG_VARIABLE_SETTABLE(redirectorRedirectCode
, CONFIG_INT
,
78 "Redirect code to use with redirector.");
80 CONFIG_VARIABLE_SETTABLE(uncachableFile
, CONFIG_ATOM
, atomSetterForbidden
,
81 "File specifying uncachable URLs.");
85 atomSetterForbidden(ConfigVariablePtr var
, void *value
)
88 return configAtomSetter(var
, value
);
92 readDomainFile(char *filename
)
97 int i
, j
, is_regex
, start
;
99 in
= fopen(filename
, "r");
102 do_log_error(L_ERROR
, errno
, "Couldn't open file %s", filename
);
107 rs
= fgets(buf
, 512, in
);
110 for(i
= 0; i
< 512; i
++) {
111 if(buf
[i
] != ' ' && buf
[i
] != '\t')
115 for(i
= start
; i
< 512; i
++) {
116 if(buf
[i
] == '#' || buf
[i
] == '\r' || buf
[i
] == '\n')
120 if(buf
[i
- 1] != ' ' && buf
[i
- 1] != '\t')
128 /* The significant part of the line is now between start and i */
131 for(j
= start
; j
< i
; j
++) {
132 if(buf
[j
] == '\\' || buf
[j
] == '*' || buf
[j
] == '/') {
139 while(rlen
+ i
- start
+ 8 >= rsize
) {
141 new_regexbuf
= realloc(regexbuf
, rsize
* 2 + 1);
142 if(new_regexbuf
== NULL
) {
143 do_log(L_ERROR
, "Couldn't reallocate regex.\n");
147 regexbuf
= new_regexbuf
;
148 rsize
= rsize
* 2 + 1;
151 rlen
= snnprintf(regexbuf
, rlen
, rsize
, "|");
152 rlen
= snnprintf(regexbuf
, rlen
, rsize
, "(");
153 rlen
= snnprint_n(regexbuf
, rlen
, rsize
, buf
+ start
, i
- start
);
154 rlen
= snnprintf(regexbuf
, rlen
, rsize
, ")");
156 DomainPtr new_domain
;
157 if(dlen
>= dsize
- 1) {
158 DomainPtr
*new_domains
;
159 new_domains
= realloc(domains
, (dsize
* 2 + 1) *
161 if(new_domains
== NULL
) {
163 "Couldn't reallocate domain list.\n");
167 domains
= new_domains
;
168 dsize
= dsize
* 2 + 1;
170 new_domain
= malloc(sizeof(DomainRec
) - 1 + i
- start
);
171 if(new_domain
== NULL
) {
172 do_log(L_ERROR
, "Couldn't allocate domain.\n");
176 new_domain
->length
= i
- start
;
177 memcpy(new_domain
->domain
, buf
+ start
, i
- start
);
178 domains
[dlen
++] = new_domain
;
186 parseDomainFile(AtomPtr file
,
187 DomainPtr
**domains_return
, regex_t
**regex_return
)
192 if(*domains_return
) {
193 DomainPtr
*domain
= *domains_return
;
198 free(*domains_return
);
199 *domains_return
= NULL
;
203 regfree(*regex_return
);
204 *regex_return
= NULL
;
207 if(!file
|| file
->length
== 0)
210 domains
= malloc(64 * sizeof(DomainPtr
));
211 if(domains
== NULL
) {
212 do_log(L_ERROR
, "Couldn't allocate domain list.\n");
218 regexbuf
= malloc(512);
219 if(regexbuf
== NULL
) {
220 do_log(L_ERROR
, "Couldn't allocate regex.\n");
227 rc
= stat(file
->string
, &ss
);
230 do_log_error(L_WARN
, errno
, "Couldn't stat file %s", file
->string
);
232 if(!S_ISDIR(ss
.st_mode
))
233 readDomainFile(file
->string
);
238 fts_argv
[0] = file
->string
;
240 fts
= fts_open(fts_argv
, FTS_LOGICAL
, NULL
);
245 if(fe
->fts_info
!= FTS_D
&& fe
->fts_info
!= FTS_DP
&&
246 fe
->fts_info
!= FTS_DC
&& fe
->fts_info
!= FTS_DNR
)
247 readDomainFile(fe
->fts_accpath
);
251 do_log_error(L_ERROR
, errno
,
252 "Couldn't scan directory %s", file
->string
);
258 domains
[dlen
] = NULL
;
267 regex
= malloc(sizeof(regex_t
));
268 rc
= regcomp(regex
, regexbuf
, REG_EXTENDED
| REG_NOSUB
);
270 do_log(L_ERROR
, "Couldn't compile regex: %d.\n", rc
);
279 *domains_return
= domains
;
280 *regex_return
= regex
;
291 forbiddenFile
= expandTilde(forbiddenFile
);
293 if(forbiddenFile
== NULL
) {
294 forbiddenFile
= expandTilde(internAtom("~/.polipo-forbidden"));
296 if(access(forbiddenFile
->string
, F_OK
) < 0) {
297 releaseAtom(forbiddenFile
);
298 forbiddenFile
= NULL
;
303 if(forbiddenFile
== NULL
) {
304 if(access("/etc/polipo/forbidden", F_OK
) >= 0)
305 forbiddenFile
= internAtom("/etc/polipo/forbidden");
308 parseDomainFile(forbiddenFile
, &forbiddenDomains
, &forbiddenRegex
);
312 uncachableFile
= expandTilde(uncachableFile
);
314 if(uncachableFile
== NULL
) {
315 uncachableFile
= expandTilde(internAtom("~/.polipo-uncachable"));
317 if(access(uncachableFile
->string
, F_OK
) < 0) {
318 releaseAtom(uncachableFile
);
319 uncachableFile
= NULL
;
324 if(uncachableFile
== NULL
) {
325 if(access("/etc/polipo/uncachable", F_OK
) >= 0)
326 uncachableFile
= internAtom("/etc/polipo/uncachable");
329 parseDomainFile(uncachableFile
, &uncachableDomains
, &uncachableRegex
);
335 urlIsMatched(char *url
, int length
, DomainPtr
*domains
, regex_t
*regex
)
340 if(memcmp(url
, "http://", 7) != 0)
346 for(i
= 8; i
< length
; i
++) {
352 if((*domain
)->length
<= (i
- 7) &&
353 (url
[i
- (*domain
)->length
- 1] == '.' ||
354 url
[i
- (*domain
)->length
- 1] == '/') &&
355 memcmp(url
+ i
- (*domain
)->length
,
357 (*domain
)->length
) == 0)
363 if(!regexec(regex
, url
, 0, NULL
, 0))
370 urlIsUncachable(char *url
, int length
)
372 return urlIsMatched(url
, length
, uncachableDomains
, uncachableRegex
);
375 static char lf
[1] = "\n";
378 urlForbidden(AtomPtr url
,
379 int (*handler
)(int, AtomPtr
, AtomPtr
, AtomPtr
, void*),
382 int forbidden
= urlIsMatched(url
->string
, url
->length
,
383 forbiddenDomains
, forbiddenRegex
);
385 AtomPtr message
= NULL
, headers
= NULL
;
389 message
= internAtomF("Forbidden URL %s", url
->string
);
391 code
= forbiddenRedirectCode
;
392 headers
= internAtomF("\r\nLocation: %s", forbiddenUrl
->string
);
398 #ifndef NO_REDIRECTOR
399 if(code
== 0 && redirector
) {
400 RedirectRequestPtr request
;
401 request
= malloc(sizeof(RedirectRequestRec
));
402 if(request
== NULL
) {
403 do_log(L_ERROR
, "Couldn't allocate redirect request.\n");
407 request
->handler
= handler
;
408 request
->data
= closure
;
409 if(redirector_request_first
== NULL
)
410 redirector_request_first
= request
;
412 redirector_request_last
->next
= request
;
413 redirector_request_last
= request
;
414 request
->next
= NULL
;
415 if(request
== redirector_request_first
)
422 handler(code
, url
, message
, headers
, closure
);
426 #ifndef NO_REDIRECTOR
432 if(redirector_read_fd
>= 0) {
433 close(redirector_read_fd
);
434 redirector_read_fd
= -1;
435 close(redirector_write_fd
);
436 redirector_write_fd
= -1;
437 kill(redirector_pid
, SIGTERM
);
439 rc
= waitpid(redirector_pid
, &status
, 0);
440 } while(rc
< 0 && errno
== EINTR
);
442 do_log_error(L_ERROR
, errno
, "Couldn't wait for redirector");
449 redirectorDestroyRequest(RedirectRequestPtr request
)
451 assert(redirector_request_first
== request
);
452 redirector_request_first
= request
->next
;
453 if(redirector_request_first
== NULL
)
454 redirector_request_last
= NULL
;
459 redirectorTrigger(void)
461 RedirectRequestPtr request
= redirector_request_first
;
467 if(redirector_read_fd
< 0) {
468 rc
= runRedirector(&redirector_pid
,
469 &redirector_read_fd
, &redirector_write_fd
);
471 do_log_error(L_ERROR
, -rc
, "Couldn't run redirector");
472 request
->handler(rc
, request
->url
, NULL
, NULL
, request
->data
);
473 redirectorDestroyRequest(request
);
477 do_stream_2(IO_WRITE
, redirector_write_fd
, 0,
478 request
->url
->string
, request
->url
->length
,
480 redirectorStreamHandler1
, request
);
484 redirectorStreamHandler1(int status
,
485 FdEventHandlerPtr event
,
486 StreamRequestPtr srequest
)
488 RedirectRequestPtr request
= (RedirectRequestPtr
)srequest
->data
;
493 do_log_error(L_ERROR
, -status
, "Write to redirector failed");
494 request
->handler(status
< 0 ? status
: -EPIPE
,
495 request
->url
, NULL
, NULL
, request
->data
);
496 redirectorDestroyRequest(request
);
501 if(!streamRequestDone(srequest
))
504 do_stream(IO_READ
, redirector_read_fd
, 0,
505 redirector_buffer
, 512,
506 redirectorStreamHandler2
, request
);
511 redirectorStreamHandler2(int status
,
512 FdEventHandlerPtr event
,
513 StreamRequestPtr srequest
)
515 RedirectRequestPtr request
= (RedirectRequestPtr
)srequest
->data
;
522 do_log_error(L_ERROR
, -status
, "Read from redirector failed");
523 request
->handler(status
, request
->url
, NULL
, NULL
, request
->data
);
526 c
= memchr(redirector_buffer
, '\n', srequest
->offset
);
528 if(!status
&& c
< redirector_buffer
+ 512)
530 do_log(L_ERROR
, "Redirector returned incomplete reply.\n");
531 request
->handler(-EUNKNOWN
, request
->url
, NULL
, NULL
, request
->data
);
536 if(srequest
->offset
> c
+ 1 - redirector_buffer
)
537 do_log(L_WARN
, "Stray bytes in redirector output.\n");
539 if(c
> redirector_buffer
+ 1 &&
540 (c
- redirector_buffer
!= request
->url
->length
||
541 memcmp(redirector_buffer
, request
->url
->string
,
542 request
->url
->length
) != 0)) {
543 code
= redirectorRedirectCode
;
544 message
= internAtom("Redirected by external redirector");
545 if(message
== NULL
) {
546 request
->handler(-ENOMEM
, request
->url
, NULL
, NULL
, request
->data
);
550 headers
= internAtomF("\r\nLocation: %s", redirector_buffer
);
551 if(headers
== NULL
) {
552 releaseAtom(message
);
553 request
->handler(-ENOMEM
, request
->url
, NULL
, NULL
, request
->data
);
561 request
->handler(code
, request
->url
,
562 message
, headers
, request
->data
);
566 redirectorDestroyRequest(request
);
576 runRedirector(pid_t
*pid_return
, int *read_fd_return
, int *write_fd_return
)
580 int filedes1
[2], filedes2
[2];
581 sigset_t ss
, old_mask
;
600 interestingSignals(&ss
);
602 rc
= sigprocmask(SIG_BLOCK
, &ss
, &old_mask
);
603 } while (rc
< 0 && errno
== EINTR
);
615 rc
= sigprocmask(SIG_SETMASK
, &old_mask
, NULL
);
616 } while(rc
< 0 && errno
== EINTR
);
624 rc
= setNonblocking(filedes1
[1], 1);
626 rc
= setNonblocking(filedes2
[0], 1);
633 *read_fd_return
= filedes2
[0];
634 *write_fd_return
= filedes1
[1];
641 rc
= sigprocmask(SIG_SETMASK
, &old_mask
, NULL
);
642 } while (rc
< 0 && errno
== EINTR
);
647 dup2(filedes1
[0], 0);
649 dup2(filedes2
[1], 1);
651 execlp(redirector
->string
, redirector
->string
, NULL
);
683 urlIsUncachable(char *url
, int length
)
689 urlForbidden(AtomPtr url
,
690 int (*handler
)(int, AtomPtr
, AtomPtr
, AtomPtr
, void*),
693 handler(0, url
, NULL
, NULL
, closure
);