-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathprivoxyexports.c
415 lines (343 loc) · 10.7 KB
/
privoxyexports.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#include "config.h"
#include "privoxyexports.h"
#include "jcc.c"
#ifndef FEATURE_GRACEFUL_TERMINATION
#error "You must define FEATURE_GRACEFUL_TERMINATION macro!"
#endif /* FEATURE_GRACEFUL_TERMINATION */
static void listen_loop_with_cb(privoxy_cb cb, void *data);
int privoxy_main_entry(const char *conf_path, privoxy_cb cb, void *data) {
#ifndef HAVE_ARC4RANDOM
unsigned int random_seed;
#endif
configfile = conf_path;
/* Prepare mutexes if supported and necessary. */
initialize_mutexes();
/* Enable logging until further notice. */
init_log_module();
files->next = NULL;
clients->next = NULL;
#ifdef _WIN32
InitWin32();
#endif
#ifndef HAVE_ARC4RANDOM
random_seed = (unsigned int)time(NULL);
#ifdef HAVE_RANDOM
srandom(random_seed);
#else
srand(random_seed);
#endif /* ifdef HAVE_RANDOM */
#endif /* ifndef HAVE_ARC4RANDOM */
/* Initialize the CGI subsystem */
cgi_init_error_messages();
listen_loop_with_cb(cb, data);
release_mutexes();
return 0;
}
jb_socket bfds[MAX_LISTENING_SOCKETS] = { 0 };
static void listen_loop_with_cb(privoxy_cb cb, void *data)
{
jb_socket *listen_fd = NULL;
struct client_states *csp_list = NULL;
struct client_state *csp = NULL;
struct configuration_spec *config;
unsigned int active_threads = 0;
#if defined(FEATURE_PTHREAD)
pthread_attr_t attrs;
pthread_attr_init(&attrs);
pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
#endif
config = load_config();
#ifdef FEATURE_CONNECTION_SHARING
/*
* XXX: Should be relocated once it no
* longer needs to emit log messages.
*/
initialize_reusable_connections();
#endif /* def FEATURE_CONNECTION_SHARING */
bind_ports_helper(config, bfds);
g_terminate = 0;
listen_fd = &bfds[0];
if (cb) {
cb((int) *listen_fd, data);
}
#ifdef FEATURE_GRACEFUL_TERMINATION
while (!g_terminate)
#else
for (;;)
#endif
{
#if !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) && !defined(__OS2__)
while (waitpid(-1, NULL, WNOHANG) > 0)
{
/* zombie children */
}
#endif /* !defined(FEATURE_PTHREAD) && !defined(_WIN32) && !defined(__BEOS__) */
/*
* Free data that was used by died threads
*/
active_threads = sweep();
#if defined(unix)
/*
* Re-open the errlog after HUP signal
*/
if (received_hup_signal)
{
if (NULL != config->logfile)
{
init_error_log(Argv[0], config->logfile);
}
received_hup_signal = 0;
}
#endif
csp_list = (struct client_states *) zalloc_or_die(sizeof(*csp_list));
csp = &csp_list->csp;
log_error(LOG_LEVEL_CONNECT,
"Waiting for the next client connection. Currently active threads: %d",
active_threads);
/*
* This config may be outdated, but for accept_connection()
* it's fresh enough.
*/
csp->config = config;
if ((*listen_fd == JB_INVALID_SOCKET) || !accept_connection(csp, bfds))
{
log_error(LOG_LEVEL_CONNECT, "accept failed: %E");
freez(csp_list);
continue;
}
csp->flags |= CSP_FLAG_ACTIVE;
csp->server_connection.sfd = JB_INVALID_SOCKET;
csp->config = config = load_config();
if (config->need_bind)
{
/*
* Since we were listening to the "old port", we will not see
* a "listen" param change until the next request. So, at
* least 1 more request must be made for us to find the new
* setting. I am simply closing the old socket and binding the
* new one.
*
* Which-ever is correct, we will serve 1 more page via the
* old settings. This should probably be a "show-status"
* request. This should not be a so common of an operation
* that this will hurt people's feelings.
*/
close_ports_helper(bfds);
bind_ports_helper(config, bfds);
}
#ifdef FEATURE_TOGGLE
if (global_toggle_state)
#endif /* def FEATURE_TOGGLE */
{
csp->flags |= CSP_FLAG_TOGGLED_ON;
}
if (run_loader(csp))
{
log_error(LOG_LEVEL_FATAL, "a loader failed - must exit");
/* Never get here - LOG_LEVEL_FATAL causes program exit */
}
#ifdef FEATURE_ACL
if (block_acl(NULL,csp))
{
log_error(LOG_LEVEL_CONNECT,
"Connection from %s on %s (socket %d) dropped due to ACL",
csp->ip_addr_str, csp->listen_addr_str, csp->cfd);
close_socket(csp->cfd);
freez(csp->ip_addr_str);
freez(csp->listen_addr_str);
freez(csp_list);
continue;
}
#endif /* def FEATURE_ACL */
if ((0 != config->max_client_connections)
&& (active_threads >= config->max_client_connections))
{
log_error(LOG_LEVEL_CONNECT,
"Rejecting connection from %s. Maximum number of connections reached.",
csp->ip_addr_str);
write_socket_delayed(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE,
strlen(TOO_MANY_CONNECTIONS_RESPONSE), get_write_delay(csp));
close_socket(csp->cfd);
freez(csp->ip_addr_str);
freez(csp->listen_addr_str);
freez(csp_list);
continue;
}
/* add it to the list of clients */
csp_list->next = clients->next;
clients->next = csp_list;
if (config->multi_threaded)
{
int child_id;
/* this is a switch () statement in the C preprocessor - ugh */
#undef SELECTED_ONE_OPTION
/* Use Pthreads in preference to native code */
#if defined(FEATURE_PTHREAD) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
{
pthread_t the_thread;
errno = pthread_create(&the_thread, &attrs,
(void * (*)(void *))serve, csp);
child_id = errno ? -1 : 0;
}
#endif
#if defined(_WIN32) && !defined(_CYGWIN) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
child_id = _beginthread(
(void (*)(void *))serve,
64 * 1024,
csp);
#endif
#if defined(__OS2__) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
child_id = _beginthread(
(void(* _Optlink)(void*))serve,
NULL,
64 * 1024,
csp);
#endif
#if defined(__BEOS__) && !defined(SELECTED_ONE_OPTION)
#define SELECTED_ONE_OPTION
{
thread_id tid = spawn_thread
(server_thread, "server", B_NORMAL_PRIORITY, csp);
if ((tid >= 0) && (resume_thread(tid) == B_OK))
{
child_id = (int) tid;
}
else
{
child_id = -1;
}
}
#endif
#if !defined(SELECTED_ONE_OPTION)
child_id = fork();
/* This block is only needed when using fork().
* When using threads, the server thread was
* created and run by the call to _beginthread().
*/
if (child_id == 0) /* child */
{
int rc = 0;
#ifdef FEATURE_TOGGLE
int inherited_toggle_state = global_toggle_state;
#endif /* def FEATURE_TOGGLE */
serve(csp);
/*
* If we've been toggled or we've blocked the request, tell Mom
*/
#ifdef FEATURE_TOGGLE
if (inherited_toggle_state != global_toggle_state)
{
rc |= RC_FLAG_TOGGLED;
}
#endif /* def FEATURE_TOGGLE */
#ifdef FEATURE_STATISTICS
if (csp->flags & CSP_FLAG_REJECTED)
{
rc |= RC_FLAG_BLOCKED;
}
#endif /* ndef FEATURE_STATISTICS */
_exit(rc);
}
else if (child_id > 0) /* parent */
{
/* in a fork()'d environment, the parent's
* copy of the client socket and the CSP
* are not used.
*/
int child_status;
#if !defined(_WIN32) && !defined(__CYGWIN__)
wait(&child_status);
/*
* Evaluate child's return code: If the child has
* - been toggled, toggle ourselves
* - blocked its request, bump up the stats counter
*/
#ifdef FEATURE_TOGGLE
if (WIFEXITED(child_status) && (WEXITSTATUS(child_status) & RC_FLAG_TOGGLED))
{
global_toggle_state = !global_toggle_state;
}
#endif /* def FEATURE_TOGGLE */
#ifdef FEATURE_STATISTICS
urls_read++;
if (WIFEXITED(child_status) && (WEXITSTATUS(child_status) & RC_FLAG_BLOCKED))
{
urls_rejected++;
}
#endif /* def FEATURE_STATISTICS */
#endif /* !defined(_WIN32) && defined(__CYGWIN__) */
close_socket(csp->cfd);
csp->flags &= ~CSP_FLAG_ACTIVE;
}
#endif
#undef SELECTED_ONE_OPTION
/* end of cpp switch () */
if (child_id < 0)
{
/*
* Spawning the child failed, assume it's because
* there are too many children running already.
* XXX: If you assume ...
*/
log_error(LOG_LEVEL_ERROR,
"Unable to take any additional connections: %E. Active threads: %d",
active_threads);
write_socket_delayed(csp->cfd, TOO_MANY_CONNECTIONS_RESPONSE,
strlen(TOO_MANY_CONNECTIONS_RESPONSE), get_write_delay(csp));
close_socket(csp->cfd);
csp->flags &= ~CSP_FLAG_ACTIVE;
}
}
else
{
serve(csp);
}
}
if (*listen_fd != JB_INVALID_SOCKET) {
close_socket(*listen_fd);
}
#if defined(FEATURE_PTHREAD)
pthread_attr_destroy(&attrs);
#endif
/* NOTREACHED unless FEATURE_GRACEFUL_TERMINATION is defined */
/* Clean up. Aim: free all memory (no leaks) */
#ifdef FEATURE_GRACEFUL_TERMINATION
log_error(LOG_LEVEL_ERROR, "Graceful termination requested");
unload_current_config_file();
unload_current_actions_file();
unload_current_re_filterfile();
#ifdef FEATURE_TRUST
unload_current_trust_file();
#endif
if (config->multi_threaded)
{
int i = 60;
do
{
sleep(1);
sweep();
} while ((clients->next != NULL) && (--i > 0));
if (i <= 0)
{
log_error(LOG_LEVEL_ERROR, "Graceful termination failed - still some live clients after 1 minute wait.");
}
}
sweep();
sweep();
#if defined(unix)
freez(basedir);
#endif
#endif /* FEATURE_GRACEFUL_TERMINATION */
}
extern void privoxy_shutdown(void) {
jb_socket bfds_cache[MAX_LISTENING_SOCKETS];
#ifdef FEATURE_GRACEFUL_TERMINATION
g_terminate = 1;
#endif
memcpy(bfds_cache, bfds, sizeof(bfds_cache));
bfds[0] = JB_INVALID_SOCKET;
close_ports_helper(bfds_cache);
}