1 /*
2 * Pound - the reverse-proxy load-balancer
3 * Copyright (C) 2002-2010 Apsis GmbH
4 *
5 * This file is part of Pound.
6 *
7 * Pound is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Pound is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Contact information:
21 * Apsis GmbH
22 * P.O.Box
23 * 8707 Uetikon am See
24 * Switzerland
25 * EMail: roseg@apsis.ch
26 */
27
28 #include "pound.h"
29
30 /* common variables */
31 char *user, /* user to run as */
32 *group, /* group to run as */
33 *root_jail, /* directory to chroot to */
34 *pid_name, /* file to record pid in */
35 *ctrl_name; /* control socket name */
36
37 int alive_to, /* check interval for resurrection */
38 anonymise, /* anonymise client address */
39 daemonize, /* run as daemon */
40 log_facility, /* log facility to use */
41 print_log, /* print log messages to stdout/stderr */
42 grace, /* grace period before shutdown */
43 control_sock; /* control socket */
44
45 SERVICE *services; /* global services (if any) */
46
47 LISTENER *listeners; /* all available listeners */
48
49 regex_t HEADER, /* Allowed header */
50 CONN_UPGRD, /* upgrade in connection header */
51 CHUNK_HEAD, /* chunk header line */
52 RESP_SKIP, /* responses for which we skip response */
53 RESP_IGN, /* responses for which we ignore content */
54 LOCATION, /* the host we are redirected to */
55 AUTHORIZATION; /* the Authorisation header */
56
57 static int shut_down = 0;
58
59 #ifndef SOL_TCP
60 /* for systems without the definition */
61 int SOL_TCP;
62 #endif
63
64 /* worker pid */
65 static pid_t son = 0;
66
67 /*
68 * OpenSSL thread support stuff
69 */
70 static pthread_mutex_t *l_array;
71
72 static void
l_init(void)73 l_init(void)
74 {
75 int i, n_locks;
76
77 n_locks = CRYPTO_num_locks();
78 if((l_array = (pthread_mutex_t *)calloc(n_locks, sizeof(pthread_mutex_t))) == NULL) {
79 logmsg(LOG_ERR, "lock init: out of memory - aborted...");
80 exit(1);
81 }
82 for(i = 0; i < n_locks; i++)
83 /* pthread_mutex_init() always returns 0 */
84 pthread_mutex_init(&l_array[i], NULL);
85 return;
86 }
87
88 static void
l_lock(const int mode,const int n,const char * file,int line)89 l_lock(const int mode, const int n, /* unused */ const char *file, /* unused */ int line)
90 {
91 int ret_val;
92
93 if(mode & CRYPTO_LOCK) {
94 if(ret_val = pthread_mutex_lock(&l_array[n]))
95 logmsg(LOG_ERR, "l_lock lock(): %s", strerror(ret_val));
96 } else {
97 if(ret_val = pthread_mutex_unlock(&l_array[n]))
98 logmsg(LOG_ERR, "l_lock unlock(): %s", strerror(ret_val));
99 }
100 return;
101 }
102
103 static unsigned long
l_id(void)104 l_id(void)
105 {
106 return (unsigned long)pthread_self();
107 }
108
109 /*
110 * work queue stuff
111 */
112 static thr_arg *first = NULL, *last = NULL;
113 static pthread_cond_t arg_cond;
114 static pthread_mutex_t arg_mut;
115 int numthreads;
116
117 static void
init_thr_arg(void)118 init_thr_arg(void)
119 {
120 pthread_cond_init(&arg_cond, NULL);
121 pthread_mutex_init(&arg_mut, NULL);
122 return;
123 }
124
125 /*
126 * add a request to the queue
127 */
128 int
put_thr_arg(thr_arg * arg)129 put_thr_arg(thr_arg *arg)
130 {
131 thr_arg *res;
132
133 if((res = malloc(sizeof(thr_arg))) == NULL) {
134 logmsg(LOG_WARNING, "thr_arg malloc");
135 return -1;
136 }
137 memcpy(res, arg, sizeof(thr_arg));
138 res->next = NULL;
139 (void)pthread_mutex_lock(&arg_mut);
140 if(last == NULL)
141 first = last = res;
142 else {
143 last->next = res;
144 last = last->next;
145 }
146 (void)pthread_mutex_unlock(&arg_mut);
147 pthread_cond_signal(&arg_cond);
148 return 0;
149 }
150
151 /*
152 * get a request from the queue
153 */
154 thr_arg *
get_thr_arg(void)155 get_thr_arg(void)
156 {
157 thr_arg *res;
158
159 (void)pthread_mutex_lock(&arg_mut);
160 if(first == NULL)
161 (void)pthread_cond_wait(&arg_cond, &arg_mut);
162 if((res = first) != NULL)
163 if((first = first->next) == NULL)
164 last = NULL;
165 (void)pthread_mutex_unlock(&arg_mut);
166 if(first != NULL)
167 pthread_cond_signal(&arg_cond);
168 return res;
169 }
170
171 /*
172 * get the current queue length
173 */
174 int
get_thr_qlen(void)175 get_thr_qlen(void)
176 {
177 int res;
178 thr_arg *tap;
179
180 (void)pthread_mutex_lock(&arg_mut);
181 for(res = 0, tap = first; tap != NULL; tap = tap->next, res++)
182 ;
183 (void)pthread_mutex_unlock(&arg_mut);
184 return res;
185 }
186
187 /*
188 * handle SIGTERM/SIGQUIT - exit
189 */
190 static RETSIGTYPE
h_term(const int sig)191 h_term(const int sig)
192 {
193 logmsg(LOG_NOTICE, "received signal %d - exiting...", sig);
194 if(son > 0)
195 kill(son, sig);
196 if(ctrl_name != NULL)
197 (void)unlink(ctrl_name);
198 exit(0);
199 }
200
201 /*
202 * handle SIGHUP/SIGINT - exit after grace period
203 */
204 static RETSIGTYPE
h_shut(const int sig)205 h_shut(const int sig)
206 {
207 int status;
208 LISTENER *lstn;
209
210 logmsg(LOG_NOTICE, "received signal %d - shutting down...", sig);
211 if(son > 0) {
212 for(lstn = listeners; lstn; lstn = lstn->next)
213 close(lstn->sock);
214 kill(son, sig);
215 (void)wait(&status);
216 if(ctrl_name != NULL)
217 (void)unlink(ctrl_name);
218 exit(0);
219 } else
220 shut_down = 1;
221 }
222
223 /*
224 * Pound: the reverse-proxy/load-balancer
225 *
226 * Arguments:
227 * -f config_file configuration file - exclusive of other flags
228 */
229
230 int
main(const int argc,char ** argv)231 main(const int argc, char **argv)
232 {
233 int n_listeners, i, clnt_length, clnt;
234 struct pollfd *polls;
235 LISTENER *lstn;
236 pthread_t thr;
237 pthread_attr_t attr;
238 struct sched_param sp;
239 uid_t user_id;
240 gid_t group_id;
241 FILE *fpid;
242 struct sockaddr_storage clnt_addr;
243 char tmp[MAXBUF];
244 #ifndef SOL_TCP
245 struct protoent *pe;
246 #endif
247
248 print_log = 0;
249 (void)umask(077);
250 control_sock = -1;
251 log_facility = -1;
252 logmsg(LOG_NOTICE, "starting...");
253
254 signal(SIGHUP, h_shut);
255 signal(SIGINT, h_shut);
256 signal(SIGTERM, h_term);
257 signal(SIGQUIT, h_term);
258 signal(SIGPIPE, SIG_IGN);
259
260 srandom(getpid());
261
262 /* SSL stuff */
263 SSL_load_error_strings();
264 SSL_library_init();
265 OpenSSL_add_all_algorithms();
266 l_init();
267 init_thr_arg();
268 CRYPTO_set_id_callback(l_id);
269 CRYPTO_set_locking_callback(l_lock);
270 init_timer();
271
272 /* Disable SSL Compression for OpenSSL pre-1.0. 1.0 is handled with an option in config.c */
273 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
274 #ifndef SSL_OP_NO_COMPRESSION
275 {
276 int i,n;
277 STACK_OF(SSL_COMP) *ssl_comp_methods;
278
279 ssl_comp_methods = SSL_COMP_get_compression_methods();
280 n = sk_SSL_COMP_num(ssl_comp_methods);
281
282 for(i=n-1; i>=0; i--) {
283 sk_SSL_COMP_delete(ssl_comp_methods, i);
284 }
285 }
286 #endif
287 #endif
288
289 /* prepare regular expressions */
290 if(regcomp(&HEADER, "^([a-z0-9!#$%&'*+.^_`|~-]+):[ \t]*(.*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
291 || regcomp(&CONN_UPGRD, "(^|[ \t,])upgrade([ \t,]|$)", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
292 || regcomp(&CHUNK_HEAD, "^([0-9a-f]+).*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
293 || regcomp(&RESP_SKIP, "^HTTP/1.1 100.*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
294 || regcomp(&RESP_IGN, "^HTTP/1.[01] (10[1-9]|1[1-9][0-9]|204|30[456]).*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
295 || regcomp(&LOCATION, "(http|https)://([^/]+)(.*)", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
296 || regcomp(&AUTHORIZATION, "Authorization:[ \t]*Basic[ \t]*\"?([^ \t]*)\"?[ \t]*", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
297 ) {
298 logmsg(LOG_ERR, "bad essential Regex - aborted");
299 exit(1);
300 }
301
302 #ifndef SOL_TCP
303 /* for systems without the definition */
304 if((pe = getprotobyname("tcp")) == NULL) {
305 logmsg(LOG_ERR, "missing TCP protocol");
306 exit(1);
307 }
308 SOL_TCP = pe->p_proto;
309 #endif
310
311 /* read config */
312 config_parse(argc, argv);
313
314 if(log_facility != -1)
315 openlog("pound", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
316 if(ctrl_name != NULL) {
317 struct sockaddr_un ctrl;
318
319 memset(&ctrl, 0, sizeof(ctrl));
320 ctrl.sun_family = AF_UNIX;
321 strncpy(ctrl.sun_path, ctrl_name, sizeof(ctrl.sun_path) - 1);
322 (void)unlink(ctrl.sun_path);
323 if((control_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
324 logmsg(LOG_ERR, "Control \"%s\" create: %s", ctrl.sun_path, strerror(errno));
325 exit(1);
326 }
327 if(bind(control_sock, (struct sockaddr *)&ctrl, (socklen_t)sizeof(ctrl)) < 0) {
328 logmsg(LOG_ERR, "Control \"%s\" bind: %s", ctrl.sun_path, strerror(errno));
329 exit(1);
330 }
331 listen(control_sock, 512);
332 }
333
334 /* open listeners */
335 for(lstn = listeners, n_listeners = 0; lstn; lstn = lstn->next, n_listeners++) {
336 int opt;
337
338 /* prepare the socket */
339 if((lstn->sock = socket(lstn->addr.ai_family == AF_INET? PF_INET: PF_INET6, SOCK_STREAM, 0)) < 0) {
340 addr2str(tmp, MAXBUF - 1, &lstn->addr, 0);
341 logmsg(LOG_ERR, "HTTP socket %s create: %s - aborted", tmp, strerror(errno));
342 exit(1);
343 }
344 opt = 1;
345 setsockopt(lstn->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
346 if(bind(lstn->sock, lstn->addr.ai_addr, (socklen_t)lstn->addr.ai_addrlen) < 0) {
347 addr2str(tmp, MAXBUF - 1, &lstn->addr, 0);
348 logmsg(LOG_ERR, "HTTP socket bind %s: %s - aborted", tmp, strerror(errno));
349 exit(1);
350 }
351 listen(lstn->sock, 512);
352 }
353
354 /* alloc the poll structures */
355 if((polls = (struct pollfd *)calloc(n_listeners, sizeof(struct pollfd))) == NULL) {
356 logmsg(LOG_ERR, "Out of memory for poll - aborted");
357 exit(1);
358 }
359 for(lstn = listeners, i = 0; lstn; lstn = lstn->next, i++)
360 polls[i].fd = lstn->sock;
361
362 /* set uid if necessary */
363 if(user) {
364 struct passwd *pw;
365
366 if((pw = getpwnam(user)) == NULL) {
367 logmsg(LOG_ERR, "no such user %s - aborted", user);
368 exit(1);
369 }
370 user_id = pw->pw_uid;
371 }
372
373 /* set gid if necessary */
374 if(group) {
375 struct group *gr;
376
377 if((gr = getgrnam(group)) == NULL) {
378 logmsg(LOG_ERR, "no such group %s - aborted", group);
379 exit(1);
380 }
381 group_id = gr->gr_gid;
382 }
383
384 /* Turn off verbose messages (if necessary) */
385 print_log = 0;
386
387 if(daemonize) {
388 /* daemonize - make ourselves a subprocess. */
389 switch (fork()) {
390 case 0:
391 if(log_facility != -1) {
392 close(0);
393 close(1);
394 close(2);
395 }
396 break;
397 case -1:
398 logmsg(LOG_ERR, "fork: %s - aborted", strerror(errno));
399 exit(1);
400 default:
401 exit(0);
402 }
403 #ifdef HAVE_SETSID
404 (void) setsid();
405 #endif
406 }
407
408 /* record pid in file */
409 if((fpid = fopen(pid_name, "wt")) != NULL) {
410 fprintf(fpid, "%d\n", getpid());
411 fclose(fpid);
412 } else
413 logmsg(LOG_NOTICE, "Create \"%s\": %s", pid_name, strerror(errno));
414
415 /* chroot if necessary */
416 if(root_jail) {
417 if(chroot(root_jail)) {
418 logmsg(LOG_ERR, "chroot: %s - aborted", strerror(errno));
419 exit(1);
420 }
421 if(chdir("/")) {
422 logmsg(LOG_ERR, "chroot/chdir: %s - aborted", strerror(errno));
423 exit(1);
424 }
425 }
426
427 if(group)
428 if(setgid(group_id) || setegid(group_id)) {
429 logmsg(LOG_ERR, "setgid: %s - aborted", strerror(errno));
430 exit(1);
431 }
432 if(user)
433 if(setuid(user_id) || seteuid(user_id)) {
434 logmsg(LOG_ERR, "setuid: %s - aborted", strerror(errno));
435 exit(1);
436 }
437
438 /* split off into monitor and working process if necessary */
439 for(;;) {
440 #ifdef UPER
441 if((son = fork()) > 0) {
442 int status;
443
444 (void)wait(&status);
445 if(WIFEXITED(status))
446 logmsg(LOG_ERR, "MONITOR: worker exited normally %d, restarting...", WEXITSTATUS(status));
447 else if(WIFSIGNALED(status))
448 logmsg(LOG_ERR, "MONITOR: worker exited on signal %d, restarting...", WTERMSIG(status));
449 else
450 logmsg(LOG_ERR, "MONITOR: worker exited (stopped?) %d, restarting...", status);
451 } else if (son == 0) {
452 #endif
453
454 /* thread stuff */
455 pthread_attr_init(&attr);
456 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
457
458 #ifdef NEED_STACK
459 /* set new stack size - necessary for OpenBSD/FreeBSD and Linux NPTL */
460 if(pthread_attr_setstacksize(&attr, 1 << 18)) {
461 logmsg(LOG_ERR, "can't set stack size - aborted");
462 exit(1);
463 }
464 #endif
465 /* start timer */
466 if(pthread_create(&thr, &attr, thr_timer, NULL)) {
467 logmsg(LOG_ERR, "create thr_resurect: %s - aborted", strerror(errno));
468 exit(1);
469 }
470
471 /* start the controlling thread (if needed) */
472 if(control_sock >= 0 && pthread_create(&thr, &attr, thr_control, NULL)) {
473 logmsg(LOG_ERR, "create thr_control: %s - aborted", strerror(errno));
474 exit(1);
475 }
476
477 /* pause to make sure the service threads were started */
478 sleep(1);
479
480 /* create the worker threads */
481 for(i = 0; i < numthreads; i++)
482 if(pthread_create(&thr, &attr, thr_http, NULL)) {
483 logmsg(LOG_ERR, "create thr_http: %s - aborted", strerror(errno));
484 exit(1);
485 }
486
487 /* pause to make sure at least some of the worker threads were started */
488 sleep(1);
489
490 /* and start working */
491 for(;;) {
492 if(shut_down) {
493 logmsg(LOG_NOTICE, "shutting down...");
494 for(lstn = listeners; lstn; lstn = lstn->next)
495 close(lstn->sock);
496 if(grace > 0) {
497 sleep(grace);
498 logmsg(LOG_NOTICE, "grace period expired - exiting...");
499 }
500 if(ctrl_name != NULL)
501 (void)unlink(ctrl_name);
502 exit(0);
503 }
504 for(lstn = listeners, i = 0; i < n_listeners; lstn = lstn->next, i++) {
505 polls[i].events = POLLIN | POLLPRI;
506 polls[i].revents = 0;
507 }
508 if(poll(polls, n_listeners, -1) < 0) {
509 logmsg(LOG_WARNING, "poll: %s", strerror(errno));
510 } else {
511 for(lstn = listeners, i = 0; lstn; lstn = lstn->next, i++) {
512 if(polls[i].revents & (POLLIN | POLLPRI)) {
513 memset(&clnt_addr, 0, sizeof(clnt_addr));
514 clnt_length = sizeof(clnt_addr);
515 if((clnt = accept(lstn->sock, (struct sockaddr *)&clnt_addr,
516 (socklen_t *)&clnt_length)) < 0) {
517 logmsg(LOG_WARNING, "HTTP accept: %s", strerror(errno));
518 } else if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET
519 || ((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET6) {
520 thr_arg arg;
521
522 if(lstn->disabled) {
523 /*
524 addr2str(tmp, MAXBUF - 1, &clnt_addr, 1);
525 logmsg(LOG_WARNING, "HTTP disabled listener from %s", tmp);
526 */
527 close(clnt);
528 }
529 arg.sock = clnt;
530 arg.lstn = lstn;
531 if((arg.from_host.ai_addr = (struct sockaddr *)malloc(clnt_length)) == NULL) {
532 logmsg(LOG_WARNING, "HTTP arg address: malloc");
533 close(clnt);
534 continue;
535 }
536 memcpy(arg.from_host.ai_addr, &clnt_addr, clnt_length);
537 arg.from_host.ai_addrlen = clnt_length;
538 if(((struct sockaddr_in *)&clnt_addr)->sin_family == AF_INET)
539 arg.from_host.ai_family = AF_INET;
540 else
541 arg.from_host.ai_family = AF_INET6;
542 if(put_thr_arg(&arg))
543 close(clnt);
544 } else {
545 /* may happen on FreeBSD, I am told */
546 logmsg(LOG_WARNING, "HTTP connection prematurely closed by peer");
547 close(clnt);
548 }
549 }
550 }
551 }
552 }
553 #ifdef UPER
554 } else {
555 /* failed to spawn son */
556 logmsg(LOG_ERR, "Can't fork worker (%s) - aborted", strerror(errno));
557 exit(1);
558 }
559 #endif
560 }
561 }
562