Prevent: Unused 'thread_id' variable
[middleman.git] / src / main.c
1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <signal.h>
27 #include <fcntl.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <sys/wait.h>
31 #include "proto.h"
32
33 #ifdef USE_SYSLOG
34 #include <syslog.h>
35 #endif                          /* USE_SYSLOG */
36
37 int loglevel = 2047;
38 char configfile[256] = "", logfile[256] = "", username[256] = "", group[256] = "";
39 TEMPLATES *templates = NULL;
40 ACCESS_LIST *access_list = NULL;
41 HEADER_LIST *header_list = NULL;
42 FILTER_LIST *filter_list = NULL;
43 COOKIE_LIST *cookie_list = NULL;
44 REWRITE_LIST *rewrite_list = NULL;
45 MIME_LIST *mime_list = NULL;
46 REDIRECT_LIST *redirect_list = NULL;
47 KEYWORD_LIST *keyword_list = NULL;
48 FORWARD_LIST *forward_list = NULL;
49 EXTERNAL *external = NULL;
50 HASH_TABLE *dns_cache;
51 THREADLIST threads[MAXTHREADS];
52 NETWORK *network = NULL;
53 LOGBUFFER *logbuffer = NULL;
54
55 int main(int argc, char **argv)
56 {
57         int fpid, x;
58         char pidfile[256] = "";
59         struct stat fileinfo;
60         struct passwd *pwd = NULL;
61         struct group *grp = NULL;
62
63         if (argc < 2) {
64                 show_help(argv);
65                 exit(EXIT_SUCCESS);
66         }
67
68         while ((x = getopt(argc, argv, "hp:c:l:d:u:g:")) != EOF) {
69                 switch (x) {
70                 case 'c':
71                         s_strncpy(configfile, optarg, sizeof(configfile));
72                         break;
73                 case 'l':
74                         s_strncpy(logfile, optarg, sizeof(logfile));
75                         break;
76                 case 'p':
77                         s_strncpy(pidfile, optarg, sizeof(pidfile));
78                         break;
79                 case 'd':
80                         loglevel = atoi(optarg);
81                         break;
82                 case 'u':
83                         s_strncpy(username, optarg, sizeof(username));
84                         break;
85                 case 'g':
86                         s_strncpy(group, optarg, sizeof(group));
87                         break;
88                 case 'h':
89                         show_help(argv);
90                         exit(EXIT_SUCCESS);
91                 }
92         }
93
94         while (--argc > 0)
95                 memset(argv[argc], 0, strlen(argv[argc]));
96
97         if (*configfile) {
98                 if (stat(configfile, &fileinfo) == -1) {
99                         fprintf(stderr, "couldn't stat %s\n", configfile);
100                         exit(EXIT_FAILURE);
101                 }
102         } else {
103                 fprintf(stderr, "config file option missing\n");
104                 exit(EXIT_FAILURE);
105         }
106
107         if (strcmp(group, "")) {
108                 grp = getgrnam(group);
109                 if (grp == NULL) {
110                         fprintf(stderr, "getgrnam: unknown group\n");
111                         exit(EXIT_FAILURE);
112                 }
113
114                 x = setgid(grp->gr_gid);
115                 if (x == -1) {
116                         perror("setgid");
117                         exit(EXIT_FAILURE);
118                 }
119         } else {
120                 grp = getgrgid(getgid());
121                 if (grp == NULL) {
122                         fprintf(stderr, "setgrgid: unknown group");
123                         exit(EXIT_FAILURE);
124                 }
125
126                 s_strncpy(group, grp->gr_name, sizeof(group));
127         }
128
129         if (strcmp(username, "")) {
130                 pwd = getpwnam(username);
131                 if (pwd == NULL) {
132                         fprintf(stderr, "getpwnam: unknown user\n");
133                         exit(EXIT_FAILURE);
134                 }
135
136                 x = setuid(pwd->pw_uid);
137                 if (x == -1) {
138                         perror("setuid");
139                         exit(EXIT_FAILURE);
140                 }
141         } else {
142                 pwd = getpwuid(getuid());
143                 if (pwd == NULL) {
144                         fprintf(stderr, "getpwuid: unknown user");
145                         exit(EXIT_FAILURE);
146                 }
147
148                 s_strncpy(username, pwd->pw_name, sizeof(username));
149         }
150
151         fpid = fork();
152
153         switch (fpid) {
154         case -1:
155                 fprintf(stderr, "failed to fork daemon\n");
156                 exit(EXIT_FAILURE);
157                 break;
158         case 0:
159                 if (*pidfile) {
160                         x = pid_check(pidfile);
161
162                         switch (x) {
163                         case 0:
164                                 break;
165                         case -1:
166                                 exit(EXIT_FAILURE);
167                         default:
168                                 exit(EXIT_FAILURE);
169                         }
170                 }
171
172                 close(STDIN_FILENO);
173                 close(STDOUT_FILENO);
174                 close(STDERR_FILENO);
175
176                 mainloop();
177         default:
178                 setpgid(fpid, fpid);
179         }
180
181         exit(EXIT_SUCCESS);
182 }
183
184 void show_help(char **argv)
185 {
186         fprintf(stderr, "MiddleMan filtering proxy server v%s (c)2002 Jason McLaughlin\n\n", MMAN_VERSION);
187         fprintf(stderr, "Usage: %s [options]\n\n", argv[0]);
188         fprintf(stderr, " -c <file>  : location of config file\n");
189 #ifndef USE_SYSLOG
190         fprintf(stderr, " -l <file>  : file to log actvity to\n");
191 #endif
192         fprintf(stderr, " -p <file>  : PID file\n");
193         fprintf(stderr, " -u <username> : run as alternate user\n");
194         fprintf(stderr, " -g <groupname> : run in altername group\n");
195         fprintf(stderr, " -d <level> : set log level (default: %d)\n", loglevel);
196         fprintf(stderr, " -h : help\n\n");
197         fprintf(stderr, " Add any of the following to specify logging detail:\n");
198         fprintf(stderr, " 1   = requests\n");
199         fprintf(stderr, " 2   = network\n");
200         fprintf(stderr, " 4   = url filtering\n");
201         fprintf(stderr, " 8   = header filtering\n");
202         fprintf(stderr, " 16  = mime filtering\n");
203         fprintf(stderr, " 32  = cookie filtering\n");
204         fprintf(stderr, " 64  = redirections\n");
205         fprintf(stderr, " 128 = templates\n");
206         fprintf(stderr, " 256 = keyword filtering\n");
207         fprintf(stderr, " 512 = warnings\n");
208         fprintf(stderr, " 1024 = errors\n");
209         fprintf(stderr, " 2048 = debug\n");
210 }
211
212 /*
213 check if pidfile exists and whether or not the pid inside is active, otherwise
214 write current pid.
215 */
216 int pid_check(char *pidfile)
217 {
218         int i = 0, x;
219         FILE *fptr;
220
221         fptr = fopen(pidfile, "r");
222         if (fptr == NULL)
223                 goto makepidfile;
224
225         x = fscanf(fptr, "%d", &i);
226         if (x == 0) {
227                 fclose(fptr);
228                 goto makepidfile;
229         }
230
231         x = kill(i, SIGCHLD);
232         if (x == 0) {
233                 fclose(fptr);
234                 return i;
235         }
236
237       makepidfile:
238         unlink(pidfile);
239         fptr = fopen(pidfile, "w");
240         if (fptr == NULL)
241                 return TRUE;
242
243         fprintf(fptr, "%u\n", (unsigned int) getpid());
244
245         fclose(fptr);
246         return FALSE;
247 }
248
249 /*
250 things that only need to be done once at startup
251 */
252 void config()
253 {
254         int i;
255
256 #ifdef USE_SYSLOG
257         openlog("mman", LOG_PID, LOG_DAEMON);
258 #endif
259
260         logbuffer = xmalloc(sizeof(LOGBUFFER));
261         logbuffer->entries = 0;
262         logbuffer->size = LOGBUFFERSIZE;
263         logbuffer->head = logbuffer->tail = NULL;
264         pthread_rwlock_init(&logbuffer->lock, NULL);
265
266         signal_setup();
267         net_init();
268
269         for (i = 0; i < MAXTHREADS; i++) {
270                 threads[i].flags = THREAD_UNUSED;
271                 pthread_mutex_init(&threads[i].lock, NULL);
272         }
273
274         pcre_free = xfree;
275         pcre_malloc = (void *) xmalloc;
276
277         config_load(3, configfile);
278
279         dns_cache = hash_create(DNS_HASH_SIZE);
280 }
281
282 int config_load(int overwrite, char *file)
283 {
284         XML_LIST *xml_list;
285
286         xml_list = xml_load(NULL, file);
287
288         if (xml_list == NULL)
289                 return FALSE;
290
291         if (access_list != NULL) {
292                 pthread_rwlock_wrlock(&access_list->lock);
293
294                 if (overwrite) {
295                         access_list->id = 0;
296                         access_ll_free(access_list->allow);
297                         access_ll_free(access_list->deny);
298                         access_list->allow = access_list->deny = NULL;
299                 }
300
301                 access_load(access_list, xml_list);
302
303                 pthread_rwlock_unlock(&access_list->lock);
304         } else
305                 access_list = access_load(NULL, xml_list);
306
307         if (filter_list != NULL) {
308                 pthread_rwlock_wrlock(&filter_list->lock);
309
310                 if (overwrite) {
311                         filter_list->id = 0;
312                         filter_ll_free(filter_list->allow);
313                         filter_ll_free(filter_list->deny);
314                         filter_list->allow = filter_list->deny = NULL;
315                         FREE_AND_NULL(filter_list->dtemplate);
316                 }
317
318                 filter_load(filter_list, xml_list);
319
320                 pthread_rwlock_unlock(&filter_list->lock);
321         } else
322                 filter_list = filter_load(NULL, xml_list);
323
324         if (header_list != NULL) {
325                 pthread_rwlock_wrlock(&header_list->lock);
326
327                 if (overwrite) {
328                         header_list->id = 0;
329                         header_ll_free(header_list->allow);
330                         header_ll_free(header_list->deny);
331                         header_ll_free(header_list->insert);
332                         header_list->allow = header_list->deny = header_list->insert = NULL;
333                 }
334
335                 header_load(header_list, xml_list);
336
337                 pthread_rwlock_unlock(&header_list->lock);
338         } else
339                 header_list = header_load(NULL, xml_list);
340
341         if (cookie_list != NULL) {
342                 pthread_rwlock_wrlock(&cookie_list->lock);
343
344                 if (overwrite) {
345                         cookie_list->id = 0;
346                         cookie_ll_free(cookie_list->allow);
347                         cookie_ll_free(cookie_list->deny);
348                         cookie_list->allow = cookie_list->deny = NULL;
349                 }
350
351                 cookie_load(cookie_list, xml_list);
352
353                 pthread_rwlock_unlock(&cookie_list->lock);
354         } else
355                 cookie_list = cookie_load(NULL, xml_list);
356
357         if (rewrite_list != NULL) {
358                 pthread_rwlock_wrlock(&rewrite_list->lock);
359
360                 if (overwrite) {
361                         rewrite_list->id = 0;
362                         rewrite_list_free(rewrite_list->rewrite);
363                         rewrite_list->rewrite = NULL;
364                 }
365
366                 rewrite_load(rewrite_list, xml_list);
367
368                 pthread_rwlock_unlock(&rewrite_list->lock);
369         } else
370                 rewrite_list = rewrite_load(NULL, xml_list);
371
372         if (mime_list != NULL) {
373                 pthread_rwlock_wrlock(&mime_list->lock);
374
375                 if (overwrite) {
376                         mime_list->id = 0;
377                         mime_ll_free(mime_list->allow);
378                         mime_ll_free(mime_list->deny);
379                         mime_list->allow = mime_list->deny = NULL;
380                         FREE_AND_NULL(mime_list->dtemplate);
381                 }
382
383                 mime_load(mime_list, xml_list);
384
385                 pthread_rwlock_unlock(&mime_list->lock);
386         } else
387                 mime_list = mime_load(NULL, xml_list);
388
389         if (redirect_list != NULL) {
390                 pthread_rwlock_wrlock(&redirect_list->lock);
391
392                 if (overwrite) {
393                         redirect_list->id = 0;
394                         redirect_list_free(redirect_list->redirect_list);
395                         redirect_list->redirect_list = NULL;
396                 }
397
398                 redirect_load(redirect_list, xml_list);
399
400                 pthread_rwlock_unlock(&redirect_list->lock);
401         } else
402                 redirect_list = redirect_load(NULL, xml_list);
403
404         if (keyword_list != NULL) {
405                 pthread_rwlock_wrlock(&keyword_list->lock);
406
407                 if (overwrite) {
408                         keyword_list->id = 0;
409                         keyword_list_free(keyword_list->keyword_list);
410                         keyword_list->keyword_list = NULL;
411                 }
412
413                 keyword_load(keyword_list, xml_list);
414
415                 pthread_rwlock_unlock(&keyword_list->lock);
416         } else
417                 keyword_list = keyword_load(NULL, xml_list);
418
419         if (forward_list != NULL) {
420                 pthread_rwlock_wrlock(&forward_list->lock);
421
422                 if (overwrite) {
423                         forward_list->id = 0;
424                         forward_list_free(forward_list->forward_list);
425                         forward_list->forward_list = NULL;
426                 }
427
428                 forward_load(forward_list, xml_list);
429
430                 pthread_rwlock_unlock(&forward_list->lock);
431         } else
432                 forward_list = forward_load(NULL, xml_list);
433
434         if (templates != NULL) {
435                 pthread_rwlock_wrlock(&templates->lock);
436
437                 if (overwrite) {
438                         templates->id = 0;
439                         templates_list_free(templates->template_list);
440                         templates->template_list = NULL;
441                         FREE_AND_NULL(templates->path);
442                 }
443
444                 templates_load(templates, xml_list);
445
446                 pthread_rwlock_unlock(&templates->lock);
447         } else
448                 templates = templates_load(NULL, xml_list);
449
450         if (external != NULL) {
451                 pthread_rwlock_wrlock(&external->lock);
452
453                 if (overwrite) {
454                         external->id = 0;
455                         external_list_free(external->external_list);
456                         external->external_list = NULL;
457                 }
458
459                 external_load(external, xml_list);
460
461                 pthread_rwlock_unlock(&external->lock);
462         } else
463                 external = external_load(NULL, xml_list);
464
465         /* only do this at startup */
466         if (overwrite == 3) {
467                 if (network != NULL) {
468                         pthread_rwlock_wrlock(&network->lock);
469
470                         network = network_load(network, xml_list);
471                         network_check(network);
472
473                         pthread_rwlock_unlock(&network->lock);
474                 } else {
475                         network = network_load(NULL, xml_list);
476                         network_check(network);
477                 }
478         }
479
480         xml_list_free(xml_list);
481
482         return TRUE;
483 }
484
485 /* 
486 main event loop; accept connections, check access list, then create thread to
487 continue connection.
488 */
489 void mainloop()
490 {
491         int x;
492         CONNECTION *connection;
493
494         config();
495
496         while (1) {
497                 connection = net_accept(-1);
498
499                 if (connection != NULL) {
500                         if (access_check(access_list, connection, NULL, NULL)) {
501                                 x = process_new(connection);
502                                 if (x != 0) {
503                                         putlog(MMLOG_ERROR, "failed to create thread for %s", connection->ip);
504
505                                         net_close(connection);
506                                 }
507                         } else {
508                                 putlog(MMLOG_NETWORK, "refused connect from %s on port %d", connection->ip, connection->port);
509
510                                 net_close(connection);
511                         }
512                 }
513         }
514 }
515
516 static void sigchld(int signo)
517 {
518         while (waitpid(-1,NULL,WNOHANG)>0);
519 }
520
521 /*
522 create new thread
523 */
524 int process_new(CONNECTION * connection)
525 {
526         int perr, thread = -1, i;
527         pthread_attr_t thread_attr;
528
529         for (i = 0; i < MAXTHREADS && thread == -1; i++) {
530                 pthread_mutex_lock(&threads[i].lock);
531                 if (threads[i].flags & THREAD_UNUSED) {
532                         thread = i;
533
534                         threads[i].flags = THREAD_IDLE;
535                         threads[i].host = NULL;
536                         threads[i].file = NULL;
537                         threads[i].method = NULL;
538                         threads[i].port = 0;
539                         threads[i].requests = 0;
540
541                         threads[i].ip = xstrdup(connection->ip);
542                 }
543                 pthread_mutex_unlock(&threads[i].lock);
544         }
545
546         if (thread == -1)
547                 return -1;
548
549         connection->thread = thread;
550
551         pthread_attr_init(&thread_attr);
552         pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
553         if (getuid() == 0)
554                 pthread_attr_setschedpolicy(&thread_attr, SCHED_FIFO);
555
556         signal(SIGCHLD,sigchld);
557         if (0==(perr=fork())) {
558                 process_entry(connection);
559                 _exit(0);
560                 }
561         perr=close(connection->client->fd)
562
563         pthread_attr_destroy(&thread_attr);
564
565         return perr;
566 }
567
568 /*
569 entry function for new threads
570 */
571 void process_entry(CONNECTION * connection)
572 {
573         int x, ret;
574         struct HTTP_HEADER_LIST *http_header_list;
575         struct FILTER_LIST_LIST *filter_match;
576         char *headbuf = NULL, *ptr, buf[4096];
577         struct url_command_t **url_command;
578         FILEBUF *filebuf;
579         HEADER *header;
580
581         /* write log message here so the pid matches */
582         putlog(MMLOG_NETWORK, "allowed connect from %s on port %d", connection->ip, connection->port);
583
584         pthread_mutex_lock(&threads[connection->thread].lock);
585         threads[connection->thread].flags = THREAD_CHEADERWAIT;
586         threads[connection->thread].pid = (unsigned int) getpid();
587         pthread_mutex_unlock(&threads[connection->thread].lock);
588
589         while (1) {
590                 /* reset bypass mask to the one provided by the access rule */
591                 connection->bypass = connection->obypass;
592
593                 headbuf = header_get(connection, CLIENT, (connection->request) ? KEEPTIMEOUT : TIMEOUT);
594                 if (headbuf == NULL) {
595                         if (!connection->request) {
596                                 putlog(MMLOG_WARN, "timeout waiting for header from %s", connection->ip);
597
598                                 template_send(templates, "badrequest", connection, 400);
599                         }
600
601                         break;
602                 }
603
604                 /* pass the client header through the rewrite rules before parsing */
605                 /* note: can't bypass this with a url command */
606                 filebuf = filebuf_new();
607                 filebuf->data = headbuf;
608                 filebuf->size = strlen(headbuf) + 1;
609
610                 rewrite_do(rewrite_list, connection, filebuf, REWRITE_CLIENT, TRUE);
611
612                 headbuf = filebuf->data;
613                 filebuf->data = NULL;
614
615                 filebuf_free(filebuf);
616
617                 connection->header = http_header_parse_request(headbuf);
618                 xfree(headbuf);
619
620                 if (connection->header == NULL) {
621                         if (!connection->request) {
622                                 putlog(MMLOG_WARN, "invalid header reveived from %s", connection->ip);
623
624                                 template_send(templates, "badrequest", connection, 400);
625                         }
626
627                         break;
628                 }
629
630                 /* determine if the connection should be kept alive with the information gathered
631                    so far, this is incase a template or web interface is used */
632                 if (connection->header->type == HTTP_CONNECT)
633                         connection->keepalive_client = FALSE;
634                 else if (connection->header->keepalive == FALSE || connection->header->proxy_keepalive == FALSE)
635                         connection->keepalive_client = FALSE;
636                 else if (connection->header->proxy_keepalive == TRUE || connection->header->keepalive == TRUE)
637                         connection->keepalive_client = TRUE;
638                 else if (connection->header->version == HTTP_HTTP11)
639                         connection->keepalive_client = TRUE;
640                 else
641                         connection->keepalive_client = FALSE;
642
643                 if ((connection->access & ACCESS_BYPASS) && connection->header->url_command != NULL) {
644                         for (url_command = connection->header->url_command; *url_command; url_command++) {
645                                 if (!strcasecmp((*url_command)->command, "bypass")) {
646                                         ptr = (*url_command)->options;
647                                         if (ptr != NULL) {
648                                                 x = TRUE;
649
650                                                 for (; *ptr; ptr++) {
651                                                         switch (*ptr) {
652                                                         case '+':
653                                                                 x = TRUE;
654                                                                 break;
655                                                         case '-':
656                                                                 x = FALSE;
657                                                                 break;
658                                                         case 'f':
659                                                                 if (x == TRUE)
660                                                                         connection->bypass |= FEATURE_FILTER;
661                                                                 else
662                                                                         connection->bypass &= ~FEATURE_FILTER;
663                                                                 break;
664                                                         case 'h':
665                                                                 if (x == TRUE)
666                                                                         connection->bypass |= FEATURE_HEADER;
667                                                                 else
668                                                                         connection->bypass &= ~FEATURE_HEADER;
669                                                                 break;
670                                                         case 'm':
671                                                                 if (x == TRUE)
672                                                                         connection->bypass |= FEATURE_MIME;
673                                                                 else
674                                                                         connection->bypass &= ~FEATURE_MIME;
675                                                                 break;
676                                                         case 'r':
677                                                                 if (x == TRUE)
678                                                                         connection->bypass |= FEATURE_REDIRECT;
679                                                                 else
680                                                                         connection->bypass &= ~FEATURE_REDIRECT;
681                                                                 break;
682                                                         case 'c':
683                                                                 if (x == TRUE)
684                                                                         connection->bypass |= FEATURE_COOKIES;
685                                                                 else
686                                                                         connection->bypass &= ~FEATURE_COOKIES;
687                                                                 break;
688                                                         case 'w':
689                                                                 if (x == TRUE)
690                                                                         connection->bypass |= FEATURE_REWRITE;
691                                                                 else
692                                                                         connection->bypass &= ~FEATURE_REWRITE;
693                                                                 break;
694                                                         case 'e':
695                                                                 if (x == TRUE)
696                                                                         connection->bypass |= FEATURE_EXTERNAL;
697                                                                 else
698                                                                         connection->bypass &= ~FEATURE_EXTERNAL;
699                                                                 break;
700                                                         case 'p':
701                                                                 if (x == TRUE)
702                                                                         connection->bypass |= FEATURE_FORWARD;
703                                                                 else
704                                                                         connection->bypass &= ~FEATURE_FORWARD;
705                                                                 break;
706                                                         case 'k':
707                                                                 if (x == TRUE)
708                                                                         connection->bypass |= FEATURE_KEYWORDS;
709                                                                 else
710                                                                         connection->bypass &= ~FEATURE_KEYWORDS;
711                                                                 break;
712                                                         }
713                                                 }
714                                         } else
715                                                 connection->bypass = ~0;
716                                 }
717                         }
718                 }
719
720                 if (connection->authenticate == TRUE && connection->header->proxy_authorization == NULL) {
721                         header = header_new();
722                         header->type = HTTP_RESP;
723                         header->code = 407;
724                         header->content_length = 0;
725                         header->proxy_authenticate = xstrdup("Basic");
726
727                         header_send(header, connection, CLIENT, HEADER_RESP);
728
729                         http_header_free(header);
730
731                         goto skip;
732                 } else if (connection->header->proxy_authorization != NULL) {
733                         ptr = strchr(connection->header->proxy_authorization, ' ');
734                         if (ptr != NULL) {
735                                 ptr++;
736
737                                 ret = from64tobits(buf, ptr);
738                                 buf[ret] = '\0';
739
740                                 putlog(MMLOG_DEBUG, "%s", buf);
741
742                                 ptr = strchr(buf, ':');
743                                 if (ptr != NULL) {
744                                         *ptr = '\0';
745
746                                         ret = access_check(access_list, connection, buf, ++ptr);
747                                         if (ret)
748                                                 connection->authenticate = FALSE;
749                                 }
750                         }
751
752                         if (connection->authenticate == TRUE) {
753                                 header = header_new();
754                                 header->type = HTTP_RESP;
755                                 header->code = 407;
756                                 header->content_length = 0;
757                                 header->proxy_authenticate = xstrdup("Basic");
758
759                                 header_send(header, connection, CLIENT, HEADER_RESP);
760
761                                 http_header_free(header);
762
763                                 goto skip;
764                         }
765                 }
766
767                 /* redirect the request if any matching rules found (redirect_do will fill in the host, file and port
768                    members of the connection struct if any are found) */
769                 x = redirect_do(redirect_list, connection, REDIRECT_REQUEST);
770                 if (x == TRUE) {
771                         /* 302 redirect sent, no need to continue */
772                         goto skip;
773                 }
774
775                 pthread_mutex_lock(&threads[connection->thread].lock);
776                 FREE_AND_STRDUP(threads[connection->thread].host, connection->header->host);
777                 FREE_AND_STRDUP(threads[connection->thread].file, connection->header->file);
778                 FREE_AND_STRDUP(threads[connection->thread].method, connection->header->method);
779                 threads[connection->thread].port = connection->header->port;
780                 pthread_mutex_unlock(&threads[connection->thread].lock);
781
782                 http_header_list = header_filter(header_list, connection);
783                 connection->header->header_filtered = http_header_list;
784
785                 if (connection->header->type != HTTP_CONNECT) {
786                         if (connection->header->type != HTTP_REQUEST && strcasecmp(connection->header->proto, "http")) {
787                                 /* only http protocol is supported */
788                                 template_send(templates, "badprotocol", connection, 501);
789
790                                 goto skip;
791                         }
792
793                         if ((connection->header->type != HTTP_REQUEST && !strcasecmp(connection->header->host, INTERFACEURL)) || (connection->header->type == HTTP_REQUEST && !strncasecmp(&connection->header->file[1], INTERFACEURL, strlen(INTERFACEURL)))) {
794                                 /* request for web interface */
795                                 putlog(MMLOG_REQUEST, "request for web interface from %s", connection->ip);
796
797                                 interface_handle_request(connection);
798
799                                 goto skip;
800                         }
801
802                         if ((connection->header->type == HTTP_REQUEST && !(connection->access & ACCESS_HTTP)) || (connection->header->type == HTTP_PROXY && !(connection->access & ACCESS_PROXY))) {
803                                 template_send(templates, "noaccess", connection, 404);
804
805                                 goto skip;
806                         }
807                 } else if (!(connection->access & ACCESS_CONNECT)) {
808                         template_send(templates, "noaccess", connection, 404);
809
810                         goto skip;
811                 }
812
813                 if (connection->header->type == HTTP_REQUEST && connection->header->host == NULL) {
814                         if ((connection->access & ACCESS_TRANSPARENT) && connection->header->host_header != NULL) {
815                                 /* use Host: header if it's there */
816                                 ptr = strchr(connection->header->host_header, ':');
817                                 connection->header->host = xstrndup(connection->header->host_header, (ptr != NULL) ? ptr - connection->header->host_header : strlen(connection->header->host_header));
818                                 if (ptr != NULL)
819                                         connection->header->port = atoi(&ptr[1]);
820
821                                 /* this feature causes a recursion where the proxy keeps
822                                    connecting to itself if an HTTP request is made to the proxy
823                                    which doesn't match a redirect rule and isn't a request for the web interface. 
824                                    There's no reliable way to detect this except to forbid connections
825                                    to websites on the same port as the proxy
826                                  */
827                                 if (connection->header->port == connection->port) {
828                                         template_send(templates, "nofile", connection, 404);
829
830                                         goto skip;
831                                 }
832                         } else {
833                                 /* not a request for web interface, no host header, and no matching redirect rule */
834                                 template_send(templates, "nofile", connection, 404);
835                                 goto skip;
836                         }
837                 }
838
839                 for (url_command = connection->header->url_command; url_command && *url_command; url_command++) {
840                         if (!strcasecmp((*url_command)->command, "filter")) {
841                                 filter_check_show(connection);
842
843                                 goto skip;
844                         }
845                 }
846
847                 filter_match = filter_check(filter_list, connection);
848                 if (filter_match != NULL) {
849                         putlog(MMLOG_FILTER, "blocked %s%s", connection->header->host, connection->header->file);
850
851                         template_send(templates, (filter_match->template != NULL) ? filter_match->template : (filter_list->dtemplate != NULL) ? filter_list->dtemplate : "blocked", connection, (connection->header->type == HTTP_CONNECT) ? 404 : 200);
852
853                         pthread_rwlock_unlock(&filter_list->lock);
854
855                         goto skip;
856                 }
857                 pthread_rwlock_unlock(&filter_list->lock);
858
859                 if (connection->site_host != NULL && (strcasecmp(connection->header->host, connection->site_host) || connection->header->port != connection->site_port)) {
860                         /* not a request for same host/port as previous request */
861                         if (connection->server != NULL) {
862                                 sock_close(connection->server);
863                                 connection->server = NULL;
864                         }
865                 }
866
867                 if (connection->server == NULL) {
868                         /* check if this request should be forwarded through another proxy */
869                         /* forward_do will fill in all the necessary members of the connection struct */
870                         connection->proxy_type = PROXY_DIRECT;
871                         forward_do(forward_list, connection);
872
873                         x = protocol_start(connection);
874                         if (x == -1)
875                                 goto skip;
876
877                         FREE_AND_NULL(connection->site_host);
878                         connection->site_host = xstrdup(connection->header->host);
879                         connection->site_port = connection->header->port;
880                 }
881
882                 if (connection->header->type != HTTP_CONNECT)
883                         x = protocol_http(connection);
884                 else
885                         x = protocol_connect(connection);
886
887                 if (x < 0)
888                         putlog(MMLOG_HEADER, "error reading header from %s", connection->header->host);
889
890               skip:
891                 sock_flush(connection->client);
892
893                 connection->request++;
894
895                 http_header_free(connection->header);
896
897                 putlog(MMLOG_DEBUG, "keepalive_client = %d", connection->keepalive_client);
898
899                 if (!connection->keepalive_client) {
900                         if (connection->server != NULL) {
901                                 sock_close(connection->server);
902                                 connection->server = NULL;
903                         }
904
905                         break;
906                 } else if (!connection->keepalive_server && connection->server != NULL) {
907                         sock_close(connection->server);
908                         connection->server = NULL;
909                 }
910
911
912                 pthread_mutex_lock(&threads[connection->thread].lock);
913                 threads[connection->thread].flags = THREAD_CHEADERWAIT;
914                 FREE_AND_NULL(threads[connection->thread].host);
915                 FREE_AND_NULL(threads[connection->thread].file);
916                 FREE_AND_NULL(threads[connection->thread].method);
917                 threads[connection->thread].port = 0;
918                 threads[connection->thread].requests++;
919                 pthread_mutex_unlock(&threads[connection->thread].lock);
920
921                 connection->header = NULL;
922                 connection->rheader = NULL;
923
924                 FREE_AND_NULL(connection->proxy_host);
925         }
926
927         if (connection->server != NULL)
928                 sock_close(connection->server);
929
930         putlog(MMLOG_NETWORK, "%s disconnected after making %d requests", connection->ip, connection->request);
931
932         pthread_mutex_lock(&threads[connection->thread].lock);
933         threads[connection->thread].flags = THREAD_UNUSED;
934         FREE_AND_NULL(threads[connection->thread].ip);
935         FREE_AND_NULL(threads[connection->thread].host);
936         FREE_AND_NULL(threads[connection->thread].file);
937         FREE_AND_NULL(threads[connection->thread].method);
938         pthread_mutex_unlock(&threads[connection->thread].lock);
939
940         net_close(connection);
941
942         return;
943 }
944
945 /*
946 save all config settings to a file
947 */
948 int config_save(char *filename)
949 {
950         int ret;
951         XML_LIST *xml_list = NULL;
952
953         /* reconstruct config file for all sections */
954         xml_list = network_xml(network, xml_list);
955         xml_list = templates_xml(templates, xml_list);
956         xml_list = external_xml(external, xml_list);
957         xml_list = access_xml(access_list, xml_list);
958         xml_list = header_xml(header_list, xml_list);
959         xml_list = cookie_xml(cookie_list, xml_list);
960         xml_list = redirect_xml(redirect_list, xml_list);
961         xml_list = keyword_xml(keyword_list, xml_list);
962         xml_list = forward_xml(forward_list, xml_list);
963         xml_list = filter_xml(filter_list, xml_list);
964         xml_list = mime_xml(mime_list, xml_list);
965         xml_list = rewrite_xml(rewrite_list, xml_list);
966
967         if (xml_list == NULL)
968                 return FALSE;
969
970         while (xml_list->prev != NULL)
971                 xml_list = xml_list->prev;
972
973         ret = xml_save(xml_list, filename);
974
975         xml_list_free(xml_list);
976
977         return ret;
978 }