~ubuntu-branches/debian/lenny/polipo/lenny

« back to all changes in this revision

Viewing changes to forbidden.c

  • Committer: Bazaar Package Importer
  • Author(s): Denis V. Sirotkin
  • Date: 2008-01-11 23:13:27 UTC
  • mfrom: (1.2.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20080111231327-zttt3l53yc5h5cqn
Tags: 1.0.4-1
* New upstream release
* Create /var/cache/polipo, /var/log/polipo and /var/run/polipo
  directories by postinst script. /var/log/polipo now has proxy:adm
  owners and 2755 rights, log files are proxy:adm 640 (closes: #291822)
* Remove /var/cache/polipo, /var/log/polipo and /var/run/polipo
  directories by postrm script, make full cleanup on purging package
* debian/control: change Standards-Version to 3.7.3
* debian/control: move Homepage to pseudo-header
* init.d: make sure /var/run/polipo exists
* init.d: remove the S runlevel from Default-Stop

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
Copyright (c) 2003 by Juliusz Chroboczek
 
2
Copyright (c) 2003-2006 by Juliusz Chroboczek
3
3
 
4
4
Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
of this software and associated documentation files (the "Software"), to deal
22
22
 
23
23
#include "polipo.h"
24
24
 
25
 
typedef struct _ForbiddenDomain {
 
25
#ifndef NO_FORBIDDEN
 
26
 
 
27
#include <regex.h>
 
28
 
 
29
typedef struct _Domain {
26
30
    int length;
27
31
    char domain[1];
28
 
} ForbiddenDomainRec, *ForbiddenDomainPtr;
 
32
} DomainRec, *DomainPtr;
29
33
 
30
34
AtomPtr forbiddenFile = NULL;
31
 
 
32
 
ForbiddenDomainPtr *forbiddenDomains;
33
 
int have_forbiddenDomains = 0;
34
 
int have_forbiddenRegex = 0;
35
 
static regex_t forbiddenRegex;
36
 
 
37
 
static char *regex;
 
35
AtomPtr forbiddenUrl = NULL;
 
36
int forbiddenRedirectCode = 302;
 
37
 
 
38
AtomPtr redirector = NULL;
 
39
int redirectorRedirectCode = 302;
 
40
 
 
41
DomainPtr *forbiddenDomains = NULL;
 
42
regex_t *forbiddenRegex = NULL;
 
43
 
 
44
AtomPtr uncachableFile = NULL;
 
45
DomainPtr *uncachableDomains = NULL;
 
46
regex_t *uncachableRegex = NULL;
 
47
 
 
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;
38
52
static int rlen, rsize, dlen, dsize;
39
53
 
 
54
#ifndef NO_REDIRECTOR
 
55
static pid_t redirector_pid = 0;
 
56
static int redirector_read_fd = -1, redirector_write_fd = -1;
 
57
#define REDIRECTOR_BUFFER_SIZE 1024
 
58
static char *redirector_buffer = NULL;
 
59
RedirectRequestPtr redirector_request_first = NULL,
 
60
    redirector_request_last = NULL;
 
61
#endif
 
62
 
 
63
static int atomSetterForbidden(ConfigVariablePtr, void*);
 
64
 
40
65
void
41
66
preinitForbidden(void)
42
67
{
43
 
    CONFIG_VARIABLE(forbiddenFile, CONFIG_ATOM,
44
 
                    "File specifying forbidden URLs.");
 
68
    CONFIG_VARIABLE_SETTABLE(forbiddenUrl, CONFIG_ATOM, configAtomSetter,
 
69
                             "URL to which forbidden requests "
 
70
                             "should be redirected.");
 
71
    CONFIG_VARIABLE_SETTABLE(forbiddenRedirectCode, CONFIG_INT,
 
72
                             configIntSetter,
 
73
                             "Redirect code, 301 or 302.");
 
74
    CONFIG_VARIABLE_SETTABLE(forbiddenFile, CONFIG_ATOM, atomSetterForbidden,
 
75
                             "File specifying forbidden URLs.");
 
76
#ifndef NO_REDIRECTOR
 
77
    CONFIG_VARIABLE_SETTABLE(redirector, CONFIG_ATOM, atomSetterForbidden,
 
78
                             "Squid-style redirector.");
 
79
    CONFIG_VARIABLE_SETTABLE(redirectorRedirectCode, CONFIG_INT,
 
80
                             configIntSetter,
 
81
                             "Redirect code to use with redirector.");
 
82
#endif
 
83
    CONFIG_VARIABLE_SETTABLE(uncachableFile, CONFIG_ATOM, atomSetterForbidden,
 
84
                             "File specifying uncachable URLs.");
 
85
}
 
86
 
 
87
static int
 
88
atomSetterForbidden(ConfigVariablePtr var, void *value)
 
89
{
 
90
    initForbidden();
 
91
    return configAtomSetter(var, value);
45
92
}
46
93
 
47
94
int
48
 
readForbiddenFile(char *filename)
 
95
readDomainFile(char *filename)
49
96
{
50
97
    FILE *in;
51
98
    char buf[512];
55
102
    in = fopen(filename, "r");
56
103
    if(in == NULL) {
57
104
        if(errno != ENOENT)
58
 
            do_log_error(L_ERROR, errno, "Couldn't open forbidden file");
 
105
            do_log_error(L_ERROR, errno, "Couldn't open file %s", filename);
59
106
        return -1;
60
107
    }
61
108
 
93
140
 
94
141
        if(is_regex) {
95
142
            while(rlen + i - start + 8 >= rsize) {
96
 
                char *new_regex;
97
 
                new_regex = realloc(regex, rsize * 2 + 1);
98
 
                if(new_regex == NULL) {
99
 
                    do_log(L_ERROR, "Couldn't allocate forbidden regex.\n");
 
143
                char *new_regexbuf;
 
144
                new_regexbuf = realloc(regexbuf, rsize * 2 + 1);
 
145
                if(new_regexbuf == NULL) {
 
146
                    do_log(L_ERROR, "Couldn't reallocate regex.\n");
100
147
                    fclose(in);
101
148
                    return -1;
102
149
                }
103
 
                regex = new_regex;
 
150
                regexbuf = new_regexbuf;
104
151
                rsize = rsize * 2 + 1;
105
152
            }
106
153
            if(rlen != 0)
107
 
                rlen = snnprintf(regex, rlen, rsize, "|");
108
 
            rlen = snnprintf(regex, rlen, rsize, "(");
109
 
            rlen = snnprint_n(regex, rlen, rsize, buf + start, i - start);
110
 
            rlen = snnprintf(regex, rlen, rsize, ")");
 
154
                rlen = snnprintf(regexbuf, rlen, rsize, "|");
 
155
            rlen = snnprintf(regexbuf, rlen, rsize, "(");
 
156
            rlen = snnprint_n(regexbuf, rlen, rsize, buf + start, i - start);
 
157
            rlen = snnprintf(regexbuf, rlen, rsize, ")");
111
158
        } else {
112
 
            ForbiddenDomainPtr new_domain;
 
159
            DomainPtr new_domain;
113
160
            if(dlen >= dsize - 1) {
114
 
                ForbiddenDomainPtr *new_domains;
115
 
                new_domains = realloc(forbiddenDomains, (dsize * 2 + 1) * 
116
 
                                      sizeof(ForbiddenDomainPtr));
 
161
                DomainPtr *new_domains;
 
162
                new_domains = realloc(domains, (dsize * 2 + 1) *
 
163
                                      sizeof(DomainPtr));
117
164
                if(new_domains == NULL) {
118
 
                    do_log(L_ERROR, 
119
 
                           "Couldn't reallocate forbidden domains.\n");
 
165
                    do_log(L_ERROR,
 
166
                           "Couldn't reallocate domain list.\n");
120
167
                    fclose(in);
121
168
                    return -1;
122
169
                }
123
 
                forbiddenDomains = new_domains;
 
170
                domains = new_domains;
124
171
                dsize = dsize * 2 + 1;
125
172
            }
126
 
            new_domain = malloc(sizeof(ForbiddenDomainRec) - 1 + i - start);
 
173
            new_domain = malloc(sizeof(DomainRec) - 1 + i - start);
127
174
            if(new_domain == NULL) {
128
 
                do_log(L_ERROR, "Couldn't allocate forbidden domain.\n");
 
175
                do_log(L_ERROR, "Couldn't allocate domain.\n");
129
176
                fclose(in);
130
177
                return -1;
131
178
            }
132
179
            new_domain->length = i - start;
133
180
            memcpy(new_domain->domain, buf + start, i - start);
134
 
            forbiddenDomains[dlen++] = new_domain;
 
181
            domains[dlen++] = new_domain;
135
182
        }
136
183
    }
137
184
    fclose(in);
139
186
}
140
187
 
141
188
void
142
 
initForbidden(void)
 
189
parseDomainFile(AtomPtr file,
 
190
                DomainPtr **domains_return, regex_t **regex_return)
143
191
{
144
 
    int rc;
145
192
    struct stat ss;
146
 
 
147
 
    if(forbiddenFile)
148
 
        forbiddenFile = expandTilde(forbiddenFile);
149
 
 
150
 
    if(forbiddenFile == NULL) {
151
 
        forbiddenFile = expandTilde(internAtom("~/.polipo-forbidden"));
152
 
        if(forbiddenFile) {
153
 
            if(access(forbiddenFile->string, F_OK) < 0) {
154
 
                releaseAtom(forbiddenFile);
155
 
                forbiddenFile = NULL;
156
 
            }
157
 
        }
158
 
    }
159
 
 
160
 
    if(forbiddenFile == NULL) {
161
 
        if(access("/etc/polipo/forbidden", F_OK) >= 0)
162
 
            forbiddenFile = internAtom("/etc/polipo/forbidden");
163
 
    }
164
 
 
165
 
    if(have_forbiddenDomains) {
166
 
        ForbiddenDomainPtr *domain = forbiddenDomains;
 
193
    int rc;
 
194
 
 
195
    if(*domains_return) {
 
196
        DomainPtr *domain = *domains_return;
167
197
        while(*domain) {
168
198
            free(*domain);
169
199
            domain++;
170
200
        }
171
 
        free(forbiddenDomains);
172
 
        have_forbiddenDomains = 0;
173
 
    }
174
 
 
175
 
    if(have_forbiddenRegex) {
176
 
        regfree(&forbiddenRegex);
177
 
        have_forbiddenRegex = 0;
178
 
    }
179
 
 
180
 
    if(!forbiddenFile || forbiddenFile->length == 0)
 
201
        free(*domains_return);
 
202
        *domains_return = NULL;
 
203
    }
 
204
 
 
205
    if(*regex_return) {
 
206
        regfree(*regex_return);
 
207
        *regex_return = NULL;
 
208
    }
 
209
 
 
210
    if(!file || file->length == 0)
181
211
        return;
182
212
 
183
 
    forbiddenDomains = malloc(64 * sizeof(ForbiddenDomainPtr));
184
 
    if(forbiddenDomains == NULL) {
185
 
        do_log(L_ERROR, "Couldn't allocate forbidden domains.\n");
 
213
    domains = malloc(64 * sizeof(DomainPtr));
 
214
    if(domains == NULL) {
 
215
        do_log(L_ERROR, "Couldn't allocate domain list.\n");
186
216
        return;
187
217
    }
188
218
    dlen = 0;
189
219
    dsize = 64;
190
220
 
191
 
    regex = malloc(512);
192
 
    if(regex == NULL) {
193
 
        do_log(L_ERROR, "Couldn't allocate forbidden regex.\n");
194
 
        free(forbiddenDomains);
195
 
        forbiddenDomains = NULL;
 
221
    regexbuf = malloc(512);
 
222
    if(regexbuf == NULL) {
 
223
        do_log(L_ERROR, "Couldn't allocate regex.\n");
 
224
        free(domains);
196
225
        return;
197
226
    }
198
227
    rlen = 0;
199
228
    rsize = 512;
200
229
 
201
 
    rc = stat(forbiddenFile->string, &ss);
 
230
    rc = stat(file->string, &ss);
202
231
    if(rc < 0) {
203
232
        if(errno != ENOENT)
204
 
            do_log_error(L_WARN, errno, "Couldn't stat forbidden file");
 
233
            do_log_error(L_WARN, errno, "Couldn't stat file %s", file->string);
205
234
    } else {
206
235
        if(!S_ISDIR(ss.st_mode))
207
 
            readForbiddenFile(forbiddenFile->string);
 
236
            readDomainFile(file->string);
208
237
        else {
209
238
            char *fts_argv[2];
210
239
            FTS *fts;
211
240
            FTSENT *fe;
212
 
            fts_argv[0] = forbiddenFile->string;
 
241
            fts_argv[0] = file->string;
213
242
            fts_argv[1] = NULL;
214
243
            fts = fts_open(fts_argv, FTS_LOGICAL, NULL);
215
244
            if(fts) {
218
247
                    if(!fe) break;
219
248
                    if(fe->fts_info != FTS_D && fe->fts_info != FTS_DP &&
220
249
                       fe->fts_info != FTS_DC && fe->fts_info != FTS_DNR)
221
 
                        readForbiddenFile(fe->fts_accpath);
 
250
                        readDomainFile(fe->fts_accpath);
222
251
                }
223
252
                fts_close(fts);
224
253
            } else {
225
254
                do_log_error(L_ERROR, errno,
226
 
                             "Couldn't scan forbidden directory");
 
255
                             "Couldn't scan directory %s", file->string);
227
256
            }
228
257
        }
229
258
    }
230
259
 
231
260
    if(dlen > 0) {
232
 
        forbiddenDomains[dlen] = NULL;
233
 
        have_forbiddenDomains = 1;
 
261
        domains[dlen] = NULL;
234
262
    } else {
235
 
        free(forbiddenDomains);
236
 
        forbiddenDomains = NULL;
 
263
        free(domains);
 
264
        domains = NULL;
237
265
    }
238
266
 
 
267
    regex_t *regex;
 
268
 
239
269
    if(rlen > 0) {
240
 
        rc = regcomp(&forbiddenRegex, regex, REG_EXTENDED | REG_NOSUB);
 
270
        regex = malloc(sizeof(regex_t));
 
271
        rc = regcomp(regex, regexbuf, REG_EXTENDED | REG_NOSUB);
241
272
        if(rc != 0) {
242
 
            do_log(L_ERROR, "Couldn't compile forbidden regex: %d.\n", rc);
243
 
        } else {
244
 
            have_forbiddenRegex = 1;
245
 
        }
246
 
    }
247
 
    free(regex);
 
273
            do_log(L_ERROR, "Couldn't compile regex: %d.\n", rc);
 
274
            free(regex);
 
275
            regex = NULL;
 
276
        }
 
277
    } else {
 
278
        regex = NULL;
 
279
    }
 
280
    free(regexbuf);
 
281
 
 
282
    *domains_return = domains;
 
283
    *regex_return = regex;
 
284
 
 
285
    return;
 
286
}
 
287
 
 
288
void
 
289
initForbidden(void)
 
290
{
 
291
    redirectorKill();
 
292
 
 
293
    if(forbiddenFile)
 
294
        forbiddenFile = expandTilde(forbiddenFile);
 
295
 
 
296
    if(forbiddenFile == NULL) {
 
297
        forbiddenFile = expandTilde(internAtom("~/.polipo-forbidden"));
 
298
        if(forbiddenFile) {
 
299
            if(access(forbiddenFile->string, F_OK) < 0) {
 
300
                releaseAtom(forbiddenFile);
 
301
                forbiddenFile = NULL;
 
302
            }
 
303
        }
 
304
    }
 
305
 
 
306
    if(forbiddenFile == NULL) {
 
307
        if(access("/etc/polipo/forbidden", F_OK) >= 0)
 
308
            forbiddenFile = internAtom("/etc/polipo/forbidden");
 
309
    }
 
310
 
 
311
    parseDomainFile(forbiddenFile, &forbiddenDomains, &forbiddenRegex);
 
312
 
 
313
 
 
314
    if(uncachableFile)
 
315
        uncachableFile = expandTilde(uncachableFile);
 
316
 
 
317
    if(uncachableFile == NULL) {
 
318
        uncachableFile = expandTilde(internAtom("~/.polipo-uncachable"));
 
319
        if(uncachableFile) {
 
320
            if(access(uncachableFile->string, F_OK) < 0) {
 
321
                releaseAtom(uncachableFile);
 
322
                uncachableFile = NULL;
 
323
            }
 
324
        }
 
325
    }
 
326
 
 
327
    if(uncachableFile == NULL) {
 
328
        if(access("/etc/polipo/uncachable", F_OK) >= 0)
 
329
            uncachableFile = internAtom("/etc/polipo/uncachable");
 
330
    }
 
331
 
 
332
    parseDomainFile(uncachableFile, &uncachableDomains, &uncachableRegex);
 
333
 
248
334
    return;
249
335
}
250
336
 
251
337
int
252
 
urlForbidden(char *url, int url_size)
 
338
urlIsMatched(char *url, int length, DomainPtr *domains, regex_t *regex)
253
339
{
254
 
    char url_copy[1024];
 
340
    if(length < 8)
 
341
        return 0;
255
342
 
256
 
    if(url_size >= 1024) return 1;
257
 
    if(url_size < 9) return 0;
258
343
    if(memcmp(url, "http://", 7) != 0)
259
344
        return 0;
260
345
 
261
 
    if(have_forbiddenDomains) {
 
346
    if(domains) {
262
347
        int i;
263
 
        ForbiddenDomainPtr *domain;
264
 
        for(i = 8; i < url_size; i++) {
 
348
        DomainPtr *domain;
 
349
        for(i = 8; i < length; i++) {
265
350
            if(url[i] == '/')
266
351
                break;
267
352
        }
268
 
        domain = forbiddenDomains;
 
353
        domain = domains;
269
354
        while(*domain) {
270
355
            if((*domain)->length <= (i - 7) &&
271
356
               (url[i - (*domain)->length - 1] == '.' ||
272
357
                url[i - (*domain)->length - 1] == '/') &&
273
358
               memcmp(url + i - (*domain)->length,
274
 
                      (*domain)->domain, 
 
359
                      (*domain)->domain,
275
360
                      (*domain)->length) == 0)
276
361
                return 1;
277
362
            domain++;
278
363
        }
279
364
    }
280
 
    if(have_forbiddenRegex) {
281
 
        memcpy(url_copy, url, url_size);
282
 
        url_copy[url_size] = '\0';
283
 
        if(!regexec(&forbiddenRegex, url_copy, 0, NULL, 0))
284
 
            return 1;
285
 
    }
286
 
    return 0;
287
 
}
 
365
 
 
366
    if(regex) {
 
367
        /* url is not necessarily 0-terminated */
 
368
        char smallcopy[50];
 
369
        char *urlcopy;
 
370
        int rc;
 
371
 
 
372
        if(length < 50) {
 
373
            urlcopy = smallcopy;
 
374
        } else {
 
375
            urlcopy = malloc(length + 1);
 
376
            if(urlcopy == NULL)
 
377
                return 0;
 
378
        }
 
379
        memcpy(urlcopy, url, length);
 
380
        urlcopy[length] = '\0';
 
381
 
 
382
        rc = regexec(regex, urlcopy, 0, NULL, 0);
 
383
 
 
384
        if(urlcopy != smallcopy)
 
385
            free(urlcopy);
 
386
 
 
387
        return !rc;
 
388
    }
 
389
    return 0;
 
390
}
 
391
 
 
392
int
 
393
urlIsUncachable(char *url, int length)
 
394
{
 
395
    return urlIsMatched(url, length, uncachableDomains, uncachableRegex);
 
396
}
 
397
 
 
398
int
 
399
urlForbidden(AtomPtr url,
 
400
             int (*handler)(int, AtomPtr, AtomPtr, AtomPtr, void*),
 
401
             void *closure)
 
402
{
 
403
    int forbidden = urlIsMatched(url->string, url->length,
 
404
                                 forbiddenDomains, forbiddenRegex);
 
405
    int code = 0;
 
406
    AtomPtr message = NULL, headers = NULL;
 
407
 
 
408
 
 
409
    if(forbidden) {
 
410
        message = internAtomF("Forbidden URL %s", url->string);
 
411
        if(forbiddenUrl) {
 
412
            code = forbiddenRedirectCode;
 
413
            headers = internAtomF("\r\nLocation: %s", forbiddenUrl->string);
 
414
        } else {
 
415
            code = 403;
 
416
        }
 
417
    }
 
418
 
 
419
#ifndef NO_REDIRECTOR
 
420
    if(code == 0 && redirector) {
 
421
        RedirectRequestPtr request;
 
422
        request = malloc(sizeof(RedirectRequestRec));
 
423
        if(request == NULL) {
 
424
            do_log(L_ERROR, "Couldn't allocate redirect request.\n");
 
425
            goto done;
 
426
        }
 
427
        request->url = url;
 
428
        request->handler = handler;
 
429
        request->data = closure;
 
430
        if(redirector_request_first == NULL)
 
431
            redirector_request_first = request;
 
432
        else
 
433
            redirector_request_last->next = request;
 
434
        redirector_request_last = request;
 
435
        request->next = NULL;
 
436
        if(request == redirector_request_first)
 
437
            redirectorTrigger();
 
438
        return 1;
 
439
    }
 
440
 
 
441
#endif
 
442
 
 
443
 done:
 
444
    handler(code, url, message, headers, closure);
 
445
    return 1;
 
446
}
 
447
 
 
448
#ifndef NO_REDIRECTOR
 
449
static void
 
450
logExitStatus(int status)
 
451
{
 
452
    if(WIFEXITED(status) && WEXITSTATUS(status) == 142)
 
453
        /* See child code in runRedirector */
 
454
        do_log(L_ERROR, "Couldn't start redirector.\n");
 
455
    else {
 
456
        char *reason =
 
457
            WIFEXITED(status) ? "with status" :
 
458
            WIFSIGNALED(status) ? "on signal" :
 
459
            "with unknown status";
 
460
        int value =
 
461
            WIFEXITED(status) ? WEXITSTATUS(status) :
 
462
            WIFSIGNALED(status) ? WTERMSIG(status) :
 
463
            status;
 
464
        do_log(L_ERROR,
 
465
               "Redirector exited %s %d.\n", reason, value);
 
466
    }
 
467
}
 
468
 
 
469
void
 
470
redirectorKill(void)
 
471
{
 
472
    int rc, status, dead;
 
473
 
 
474
    if(redirector_read_fd >= 0) {
 
475
        rc = waitpid(redirector_pid, &status, WNOHANG);
 
476
        dead = (rc > 0);
 
477
        close(redirector_read_fd);
 
478
        redirector_read_fd = -1;
 
479
        close(redirector_write_fd);
 
480
        redirector_write_fd = -1;
 
481
        if(!dead) {
 
482
            rc = kill(redirector_pid, SIGTERM);
 
483
            if(rc < 0 && errno != ESRCH) {
 
484
                do_log_error(L_ERROR, errno, "Couldn't kill redirector");
 
485
                redirector_pid = -1;
 
486
                return;
 
487
            }
 
488
            do {
 
489
                rc = waitpid(redirector_pid, &status, 0);
 
490
            } while(rc < 0 && errno == EINTR);
 
491
            if(rc < 0)
 
492
                do_log_error(L_ERROR, errno,
 
493
                             "Couldn't wait for redirector's death");
 
494
        } else
 
495
            logExitStatus(status);
 
496
        redirector_pid = -1;
 
497
    }
 
498
}
 
499
 
 
500
static void
 
501
redirectorDestroyRequest(RedirectRequestPtr request)
 
502
{
 
503
    assert(redirector_request_first == request);
 
504
    redirector_request_first = request->next;
 
505
    if(redirector_request_first == NULL)
 
506
        redirector_request_last = NULL;
 
507
    free(request);
 
508
}
 
509
 
 
510
void
 
511
redirectorTrigger(void)
 
512
{
 
513
    RedirectRequestPtr request = redirector_request_first;
 
514
    int rc;
 
515
 
 
516
    if(!request)
 
517
        return;
 
518
 
 
519
    if(redirector_read_fd < 0) {
 
520
        rc = runRedirector(&redirector_pid,
 
521
                           &redirector_read_fd, &redirector_write_fd);
 
522
        if(rc < 0) {
 
523
            request->handler(rc, request->url, NULL, NULL, request->data);
 
524
            redirectorDestroyRequest(request);
 
525
            return;
 
526
        }
 
527
    }
 
528
    do_stream_2(IO_WRITE, redirector_write_fd, 0,
 
529
                request->url->string, request->url->length,
 
530
                "\n", 1,
 
531
                redirectorStreamHandler1, request);
 
532
}
 
533
 
 
534
int
 
535
redirectorStreamHandler1(int status,
 
536
                         FdEventHandlerPtr event,
 
537
                         StreamRequestPtr srequest)
 
538
{
 
539
    RedirectRequestPtr request = (RedirectRequestPtr)srequest->data;
 
540
 
 
541
    if(status) {
 
542
        if(status >= 0)
 
543
            status = -EPIPE;
 
544
        do_log_error(L_ERROR, -status, "Write to redirector failed");
 
545
        goto fail;
 
546
    }
 
547
 
 
548
    if(!streamRequestDone(srequest))
 
549
        return 0;
 
550
 
 
551
    do_stream(IO_READ, redirector_read_fd, 0,
 
552
              redirector_buffer, REDIRECTOR_BUFFER_SIZE,
 
553
              redirectorStreamHandler2, request);
 
554
    return 1;
 
555
 
 
556
 fail:
 
557
    request->handler(status < 0 ? status : -EPIPE,
 
558
                     request->url, NULL, NULL, request->data);
 
559
    redirectorDestroyRequest(request);
 
560
    redirectorKill();
 
561
    return 1;
 
562
}
 
563
 
 
564
int
 
565
redirectorStreamHandler2(int status,
 
566
                         FdEventHandlerPtr event,
 
567
                         StreamRequestPtr srequest)
 
568
{
 
569
    RedirectRequestPtr request = (RedirectRequestPtr)srequest->data;
 
570
    char *c;
 
571
    AtomPtr message;
 
572
    AtomPtr headers;
 
573
    int code;
 
574
 
 
575
    if(status < 0) {
 
576
        do_log_error(L_ERROR, -status, "Read from redirector failed");
 
577
        request->handler(status, request->url, NULL, NULL, request->data);
 
578
        goto kill;
 
579
    }
 
580
    c = memchr(redirector_buffer, '\n', srequest->offset);
 
581
    if(!c) {
 
582
        if(!status && srequest->offset < REDIRECTOR_BUFFER_SIZE)
 
583
            return 0;
 
584
        do_log(L_ERROR, "Redirector returned incomplete reply.\n");
 
585
        request->handler(-EREDIRECTOR, request->url, NULL, NULL, request->data);
 
586
        goto kill;
 
587
    }
 
588
    *c = '\0';
 
589
 
 
590
    if(srequest->offset > c + 1 - redirector_buffer)
 
591
        do_log(L_WARN, "Stray bytes in redirector output.\n");
 
592
 
 
593
    if(c > redirector_buffer + 1 &&
 
594
       (c - redirector_buffer != request->url->length ||
 
595
        memcmp(redirector_buffer, request->url->string,
 
596
               request->url->length) != 0)) {
 
597
        code = redirectorRedirectCode;
 
598
        message = internAtom("Redirected by external redirector");
 
599
        if(message == NULL) {
 
600
            request->handler(-ENOMEM, request->url, NULL, NULL, request->data);
 
601
            goto kill;
 
602
        }
 
603
 
 
604
        headers = internAtomF("\r\nLocation: %s", redirector_buffer);
 
605
        if(headers == NULL) {
 
606
            releaseAtom(message);
 
607
            request->handler(-ENOMEM, request->url, NULL, NULL, request->data);
 
608
            goto kill;
 
609
        }
 
610
    } else {
 
611
        code = 0;
 
612
        message = NULL;
 
613
        headers = NULL;
 
614
    }
 
615
    request->handler(code, request->url,
 
616
                     message, headers, request->data);
 
617
    goto cont;
 
618
 
 
619
 cont:
 
620
    redirectorDestroyRequest(request);
 
621
    redirectorTrigger();
 
622
    return 1;
 
623
 
 
624
 kill:
 
625
    redirectorKill();
 
626
    goto cont;
 
627
}
 
628
 
 
629
int
 
630
runRedirector(pid_t *pid_return, int *read_fd_return, int *write_fd_return)
 
631
{
 
632
    int rc, rc2, status;
 
633
    pid_t pid;
 
634
    int filedes1[2], filedes2[2];
 
635
    sigset_t ss, old_mask;
 
636
 
 
637
    assert(redirector);
 
638
 
 
639
    if(redirector_buffer == NULL) {
 
640
        redirector_buffer = malloc(REDIRECTOR_BUFFER_SIZE);
 
641
        if(redirector_buffer == NULL)
 
642
            return -errno;
 
643
    }
 
644
 
 
645
    rc = pipe(filedes1);
 
646
    if(rc < 0) {
 
647
        rc = -errno;
 
648
        goto fail1;
 
649
    }
 
650
 
 
651
 
 
652
    rc = pipe(filedes2);
 
653
    if(rc < 0) {
 
654
        rc = -errno;
 
655
        goto fail2;
 
656
    }
 
657
 
 
658
    fflush(stdout);
 
659
    fflush(stderr);
 
660
    flushLog();
 
661
 
 
662
    interestingSignals(&ss);
 
663
    do {
 
664
        rc = sigprocmask(SIG_BLOCK, &ss, &old_mask);
 
665
    } while (rc < 0 && errno == EINTR);
 
666
    if(rc < 0) {
 
667
        rc = -errno;
 
668
        goto fail3;
 
669
    }
 
670
 
 
671
    pid = fork();
 
672
    if(pid < 0) {
 
673
        rc = -errno;
 
674
        goto fail4;
 
675
    }
 
676
 
 
677
    if(pid > 0) {
 
678
        do {
 
679
            rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
 
680
        } while(rc < 0 && errno == EINTR);
 
681
 
 
682
        if(rc < 0) {
 
683
            rc = -errno;
 
684
            goto fail4;
 
685
        }
 
686
 
 
687
        rc = setNonblocking(filedes1[1], 1);
 
688
        if(rc >= 0)
 
689
            rc = setNonblocking(filedes2[0], 1);
 
690
        if(rc < 0) {
 
691
            rc = -errno;
 
692
            goto fail4;
 
693
        }
 
694
 
 
695
        /* This is completely unnecesary -- if the redirector cannot be
 
696
           started, redirectorStreamHandler1 will get EPIPE straight away --,
 
697
           but it improves error messages somewhat. */
 
698
        rc = waitpid(pid, &status, WNOHANG);
 
699
        if(rc > 0) {
 
700
            logExitStatus(status);
 
701
            rc = -EREDIRECTOR;
 
702
            goto fail4;
 
703
        } else if(rc < 0) {
 
704
            rc = -errno;
 
705
            goto fail4;
 
706
        }
 
707
 
 
708
        *read_fd_return = filedes2[0];
 
709
        *write_fd_return = filedes1[1];
 
710
 
 
711
        *pid_return = pid;
 
712
        /* This comes at the end so that the fail* labels can work */
 
713
        close(filedes1[0]);
 
714
        close(filedes2[1]);
 
715
    } else {
 
716
        close(filedes1[1]);
 
717
        close(filedes2[0]);
 
718
        uninitEvents();
 
719
        do {
 
720
            rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
 
721
        } while (rc < 0 && errno == EINTR);
 
722
        if(rc < 0)
 
723
            exit(142);
 
724
 
 
725
        if(filedes1[0] != 0)
 
726
            dup2(filedes1[0], 0);
 
727
        if(filedes2[1] != 1)
 
728
            dup2(filedes2[1], 1);
 
729
 
 
730
        execlp(redirector->string, redirector->string, NULL);
 
731
        exit(142);
 
732
        /* NOTREACHED */
 
733
    }
 
734
    return 1;
 
735
 
 
736
 fail4:
 
737
    do {
 
738
        rc2 = sigprocmask(SIG_SETMASK, &old_mask, NULL);
 
739
    } while(rc2 < 0 && errno == EINTR);
 
740
 fail3:
 
741
    close(filedes2[0]);
 
742
    close(filedes2[1]);
 
743
 fail2:
 
744
    close(filedes1[0]);
 
745
    close(filedes1[1]);
 
746
 fail1:
 
747
    free(redirector_buffer);
 
748
    redirector_buffer = NULL;
 
749
    return rc;
 
750
}
 
751
 
 
752
#else
 
753
 
 
754
void
 
755
redirectorKill(void)
 
756
{
 
757
    return;
 
758
}
 
759
 
 
760
#endif
 
761
 
 
762
#else
 
763
 
 
764
void
 
765
preinitForbidden()
 
766
{
 
767
    return;
 
768
}
 
769
 
 
770
void
 
771
initForbidden()
 
772
{
 
773
    return;
 
774
}
 
775
 
 
776
int
 
777
urlIsUncachable(char *url, int length)
 
778
{
 
779
    return 0;
 
780
}
 
781
 
 
782
int
 
783
urlForbidden(AtomPtr url,
 
784
             int (*handler)(int, AtomPtr, AtomPtr, AtomPtr, void*),
 
785
             void *closure)
 
786
{
 
787
    handler(0, url, NULL, NULL, closure);
 
788
    return 1;
 
789
}
 
790
 
 
791
#endif