:pserver:anonymous@cvs.middle-man.sourceforge.net:/cvsroot/middle-man middleman
[middleman.git] / src / header.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 <string.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include "proto.h"
27
28 extern COOKIE_LIST *cookie_list;
29
30 /*
31 load <header> section of xml_list into the header_list structure
32 */
33 HEADER_LIST *header_load(HEADER_LIST * header_list, XML_LIST * xml_list)
34 {
35         HEADER_LIST *tmp_list = header_list;
36         struct HEADER_LIST_LIST *allow = NULL, *deny = NULL, *insert = NULL;
37
38         if (tmp_list == NULL) {
39                 tmp_list = xmalloc(sizeof(HEADER_LIST));
40                 tmp_list->allow = NULL;
41                 tmp_list->deny = NULL;
42                 tmp_list->insert = NULL;
43                 tmp_list->policy = POLICY_ALLOW;
44                 tmp_list->id = 0;
45                 tmp_list->enabled = TRUE;
46
47                 header_list = tmp_list;
48
49                 pthread_rwlock_init(&tmp_list->lock, NULL);
50         } else {
51                 allow = tmp_list->allow;
52                 deny = tmp_list->deny;
53                 insert = tmp_list->insert;
54         }
55
56         while ((xml_list = xml_section(xml_list, "<header>"))) {
57                 XML_LIST_LOOP(xml_list, "<header>") {
58                         XML_LIST_CMP(xml_list, "<allow>") {
59                                 allow = header_ll_new(allow);
60                                 allow->id = header_list->id++;
61
62                                 if (tmp_list->allow == NULL)
63                                         tmp_list->allow = allow;
64                                 XML_LIST_LOOP(xml_list, "<allow>") {
65                                         XML_LIST_CMP(xml_list, "<enabled>") {
66                                                 xml_list = xml_list->next;
67                                                 if (xml_list->type == XML_VALUE) {
68                                                         if (!strcasecmp(xml_list->item, "false"))
69                                                                 allow->enabled = FALSE;
70                                                         else
71                                                                 allow->enabled = TRUE;
72                                                 }
73                                         }
74                                         XML_LIST_CMP(xml_list, "<comment>") {
75                                                 xml_list = xml_list->next;
76                                                 if (xml_list->type == XML_VALUE)
77                                                         header_ll_insert(allow, xml_list->item, NULL, NULL, NULL);
78                                         }
79                                         XML_LIST_CMP(xml_list, "<type>") {
80                                                 xml_list = xml_list->next;
81                                                 if (xml_list->type == XML_VALUE)
82                                                         header_ll_insert(allow, NULL, xml_list->item, NULL, NULL);
83                                         }
84                                         XML_LIST_CMP(xml_list, "<value>") {
85                                                 xml_list = xml_list->next;
86                                                 if (xml_list->type == XML_VALUE)
87                                                         header_ll_insert(allow, NULL, NULL, xml_list->item, NULL);
88                                         }
89                                         XML_LIST_CMP(xml_list, "<host>") {
90                                                 xml_list = xml_list->next;
91                                                 if (xml_list->type == XML_VALUE)
92                                                         header_ll_insert(allow, NULL, NULL, NULL, xml_list->item);
93                                         }
94                                 }
95                         }
96
97                         XML_LIST_CMP(xml_list, "<deny>") {
98                                 deny = header_ll_new(deny);
99                                 deny->id = header_list->id++;
100
101                                 if (tmp_list->deny == NULL)
102                                         tmp_list->deny = deny;
103                                 XML_LIST_LOOP(xml_list, "<deny>") {
104                                         XML_LIST_CMP(xml_list, "<enabled>") {
105                                                 xml_list = xml_list->next;
106                                                 if (xml_list->type == XML_VALUE) {
107                                                         if (!strcasecmp(xml_list->item, "false"))
108                                                                 deny->enabled = FALSE;
109                                                         else
110                                                                 deny->enabled = TRUE;
111                                                 }
112                                         }
113                                         XML_LIST_CMP(xml_list, "<comment>") {
114                                                 xml_list = xml_list->next;
115                                                 if (xml_list->type == XML_VALUE)
116                                                         header_ll_insert(deny, xml_list->item, NULL, NULL, NULL);
117                                         }
118                                         XML_LIST_CMP(xml_list, "<type>") {
119                                                 xml_list = xml_list->next;
120                                                 if (xml_list->type == XML_VALUE)
121                                                         header_ll_insert(deny, NULL, xml_list->item, NULL, NULL);
122                                         }
123                                         XML_LIST_CMP(xml_list, "<value>") {
124                                                 xml_list = xml_list->next;
125                                                 if (xml_list->type == XML_VALUE)
126                                                         header_ll_insert(deny, NULL, NULL, xml_list->item, NULL);
127                                         }
128                                         XML_LIST_CMP(xml_list, "<host>") {
129                                                 xml_list = xml_list->next;
130                                                 if (xml_list->type == XML_VALUE)
131                                                         header_ll_insert(deny, NULL, NULL, NULL, xml_list->item);
132                                         }
133                                 }
134                         }
135                         XML_LIST_CMP(xml_list, "<insert>") {
136                                 insert = header_ll_new(insert);
137                                 insert->id = header_list->id++;
138
139                                 if (tmp_list->insert == NULL)
140                                         tmp_list->insert = insert;
141                                 XML_LIST_LOOP(xml_list, "<insert>") {
142                                         XML_LIST_CMP(xml_list, "<enabled>") {
143                                                 xml_list = xml_list->next;
144                                                 if (xml_list->type == XML_VALUE) {
145                                                         if (!strcasecmp(xml_list->item, "false"))
146                                                                 insert->enabled = FALSE;
147                                                         else
148                                                                 insert->enabled = TRUE;
149                                                 }
150                                         }
151                                         XML_LIST_CMP(xml_list, "<comment>") {
152                                                 xml_list = xml_list->next;
153                                                 if (xml_list->type == XML_VALUE)
154                                                         header_ll_insert(insert, xml_list->item, NULL, NULL, NULL);
155                                         }
156                                         XML_LIST_CMP(xml_list, "<type>") {
157                                                 xml_list = xml_list->next;
158                                                 if (xml_list->type == XML_VALUE)
159                                                         header_ll_insert(insert, NULL, xml_list->item, NULL, NULL);
160                                         }
161                                         XML_LIST_CMP(xml_list, "<value>") {
162                                                 xml_list = xml_list->next;
163                                                 if (xml_list->type == XML_VALUE)
164                                                         header_ll_insert(insert, NULL, NULL, xml_list->item, NULL);
165                                         }
166                                         XML_LIST_CMP(xml_list, "<host>") {
167                                                 xml_list = xml_list->next;
168                                                 if (xml_list->type == XML_VALUE)
169                                                         header_ll_insert(insert, NULL, NULL, NULL, xml_list->item);
170                                         }
171                                 }
172                         }
173                         XML_LIST_CMP(xml_list, "<policy>") {
174                                 xml_list = xml_list->next;
175                                 if (xml_list->type == XML_VALUE) {
176                                         if (!strcasecmp(xml_list->item, "allow"))
177                                                 tmp_list->policy = POLICY_ALLOW;
178                                         else if (!strcasecmp(xml_list->item, "deny"))
179                                                 tmp_list->policy = POLICY_DENY;
180                                 }
181                         }
182                         XML_LIST_CMP(xml_list, "<enabled>") {
183                                 xml_list = xml_list->next;
184                                 if (xml_list->type == XML_VALUE) {
185                                         if (!strcasecmp(xml_list->item, "false"))
186                                                 tmp_list->enabled = FALSE;
187                                         else
188                                                 tmp_list->enabled = TRUE;
189                                 }
190                         }
191                 }
192         }
193
194         return header_list;
195 }
196
197 XML_LIST *header_xml(HEADER_LIST * header_list, XML_LIST * xml_list)
198 {
199         int i;
200         char *ptr;
201         struct HEADER_LIST_LIST *hl = NULL;
202
203         if (header_list == NULL)
204                 return xml_list;
205
206         pthread_rwlock_rdlock(&header_list->lock);
207
208         xml_list = xml_list_add(xml_list, "<header>", XML_TAG);
209
210         xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
211         xml_list = xml_list_add(xml_list, (header_list->enabled == TRUE) ? "true" : "false", XML_VALUE);
212         xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
213
214         xml_list = xml_list_add(xml_list, "<policy>", XML_TAG);
215         xml_list = xml_list_add(xml_list, (header_list->policy == POLICY_ALLOW) ? "allow" : "deny", XML_VALUE);
216         xml_list = xml_list_add(xml_list, "</policy>", XML_TAG);
217
218         for (i = 0; i < 3; i++) {
219                 switch (i) {
220                 case 0:
221                         hl = header_list->allow;
222                         break;
223                 case 1:
224                         hl = header_list->deny;
225                         break;
226                 case 2:
227                         hl = header_list->insert;
228                         break;
229                 }
230
231                 for (; hl; hl = hl->next) {
232                         xml_list = xml_list_add(xml_list, (i == 0) ? "<allow>" : (i == 1) ? "<deny>" : "<insert>", XML_TAG);
233
234                         xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
235                         xml_list = xml_list_add(xml_list, (hl->enabled == TRUE) ? "true" : "false", XML_VALUE);
236                         xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
237
238                         if (hl->comment != NULL) {
239                                 xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
240                                 ptr = string_to_xml(hl->comment);
241                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
242                                 xfree(ptr);
243                                 xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
244                         }
245
246                         if (hl->type != NULL) {
247                                 xml_list = xml_list_add(xml_list, "<type>", XML_TAG);
248                                 ptr = string_to_xml(hl->type);
249                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
250                                 xfree(ptr);
251                                 xml_list = xml_list_add(xml_list, "</type>", XML_TAG);
252                         }
253
254                         if (hl->value != NULL) {
255                                 xml_list = xml_list_add(xml_list, "<value>", XML_TAG);
256                                 ptr = string_to_xml(hl->value);
257                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
258                                 xfree(ptr);
259                                 xml_list = xml_list_add(xml_list, "</value>", XML_TAG);
260                         }
261
262                         if (hl->host != NULL) {
263                                 xml_list = xml_list_add(xml_list, "<host>", XML_TAG);
264                                 ptr = string_to_xml(hl->host);
265                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
266                                 xfree(ptr);
267                                 xml_list = xml_list_add(xml_list, "</host>", XML_TAG);
268                         }
269
270                         xml_list = xml_list_add(xml_list, (i == 0) ? "</allow>" : (i == 1) ? "</deny>" : "</insert>", XML_TAG);
271                 }
272         }
273
274         xml_list = xml_list_add(xml_list, "</header>", XML_TAG);
275
276         pthread_rwlock_unlock(&header_list->lock);
277
278         return xml_list;
279 }
280
281 /*
282 free memory used by HEADER_LIST linked list
283 */
284 void header_free(HEADER_LIST * header_list)
285 {
286         if (!header_list)
287                 return;
288
289         header_ll_free(header_list->allow);
290         header_ll_free(header_list->deny);
291         header_ll_free(header_list->insert);
292
293         pthread_rwlock_destroy(&header_list->lock);
294
295         xfree(header_list);
296 }
297
298 void header_ll_free(struct HEADER_LIST_LIST *hl)
299 {
300         struct HEADER_LIST_LIST *tmp;
301
302         while (hl != NULL) {
303                 tmp = hl->next;
304
305                 if (hl->te != NULL)
306                         reg_free(hl->te);
307                 if (hl->ve != NULL)
308                         reg_free(hl->ve);
309                 if (hl->he != NULL)
310                         reg_free(hl->he);
311                 FREE_AND_NULL(hl->comment);
312                 FREE_AND_NULL(hl->type);
313                 FREE_AND_NULL(hl->value);
314                 FREE_AND_NULL(hl->host);
315
316                 xfree(hl);
317                 hl = tmp;
318         }
319 }
320
321 void header_ll_insert(struct HEADER_LIST_LIST *x, char *a, char *b, char *c, char *d)
322 {
323         if (a != NULL) {
324                 FREE_AND_NULL(x->comment);
325
326                 if (strcmp(a, ""))
327                         x->comment = xstrdup(a);
328         }
329         if (b != NULL) {
330                 if (x->te != NULL)
331                         reg_free(x->te);
332                 FREE_AND_NULL(x->type);
333
334                 if (strcmp(b, "")) {
335                         x->type = xstrdup(b);
336                         x->te = reg_compile(b, REGFLAGS);
337                 } else
338                         x->te = NULL;
339         }
340         if (c != NULL) {
341                 if (x->ve != NULL)
342                         reg_free(x->ve);
343                 FREE_AND_NULL(x->value);
344
345                 if (strcmp(c, "")) {
346                         x->value = xstrdup(c);
347                         x->ve = reg_compile(c, REGFLAGS);
348                 } else
349                         x->ve = NULL;
350         }
351         if (d != NULL) {
352                 if (x->he != NULL)
353                         reg_free(x->he);
354                 FREE_AND_NULL(x->host);
355
356                 if (strcmp(d, "")) {
357                         x->host = xstrdup(d);
358                         x->he = reg_compile(d, REGFLAGS);
359                 } else
360                         x->he = NULL;
361         }
362 }
363
364
365 struct HEADER_LIST_LIST *header_ll_new(struct HEADER_LIST_LIST *x)
366 {
367         if (x == NULL) {
368                 x = xmalloc(sizeof(struct HEADER_LIST_LIST));
369                 x->prev = NULL;
370         } else {
371                 while (x->next != NULL)
372                         x = x->next;
373                 x->next = xmalloc(sizeof(struct HEADER_LIST_LIST));
374                 x->next->prev = x;
375                 x = x->next;
376         }
377
378         x->enabled = TRUE;
379         x->comment = NULL;
380         x->te = NULL;
381         x->ve = NULL;
382         x->he = NULL;
383         x->type = NULL;
384         x->value = NULL;
385         x->host = NULL;
386         x->next = NULL;
387
388         return x;
389 }
390
391 struct HEADER_LIST_LIST *header_ll_delete(struct HEADER_LIST_LIST *x)
392 {
393         struct HEADER_LIST_LIST *start = x;
394
395         while (start->prev != NULL)
396                 start = start->prev;
397
398         if (x->next != NULL)
399                 x->next->prev = x->prev;
400         if (x->prev != NULL)
401                 x->prev->next = x->next;
402         else
403                 start = start->next;
404
405         if (x->te != NULL)
406                 reg_free(x->te);
407         if (x->ve != NULL)
408                 reg_free(x->ve);
409         FREE_AND_NULL(x->comment);
410         FREE_AND_NULL(x->type);
411         FREE_AND_NULL(x->value);
412         FREE_AND_NULL(x->host);
413
414         xfree(x);
415
416         return start;
417 }
418
419 struct HTTP_HEADER_LIST *header_list_insert(struct HTTP_HEADER_LIST *x, char *a, char *b)
420 {
421         if (x == NULL)
422                 x = xmalloc(sizeof(struct HTTP_HEADER_LIST));
423         else {
424                 while (x->next != NULL)
425                         x = x->next;
426                 x->next = xmalloc(sizeof(struct HTTP_HEADER_LIST));
427                 x = x->next;
428         }
429         x->next = NULL;
430         x->type = xstrdup(a);
431         x->value = xstrdup(b);
432
433         return x;
434 }
435
436 HEADER *http_header_parse_response(char *s)
437 {
438         int x;
439         char version[10], code[10];
440         HEADER *header;
441
442         header = header_new();
443
444         header->type = HTTP_RESP;
445
446         x = sscanf(s, "%10s %10s", version, code);
447         if (x != 2)
448                 goto error;
449
450         if (!strcasecmp(version, "HTTP/1.1"))
451                 header->version = HTTP_HTTP11;
452         else
453                 header->version = HTTP_HTTP10;
454
455         header->code = atoi(code);
456         if (header->code <= 0) goto error;
457
458         http_header_list_parse(header, s);
459
460         return header;
461
462       error:
463         http_header_free(header);
464         return NULL;
465 }
466
467 HEADER *http_header_parse_request(char *s)
468 {
469         int x;
470         char *ptr, *ptr2;
471         char buf[8096], method[24], version[10];
472         HEADER *header;
473         struct url_command_t **url_command;
474
475         /* parse first line */
476         /* version can be left out, i.e. "GET /" */
477         x = sscanf(s, "%24s %4096s %10s", method, buf, version);
478         if (x < 2)
479                 return NULL;
480
481         header = header_new();
482
483         if (!strcasecmp(method, "CONNECT"))
484                 header->type = HTTP_CONNECT;
485         else if (buf[0] == '/')
486                 header->type = HTTP_REQUEST;
487         else
488                 header->type = HTTP_PROXY;
489
490         if (x == 3 && !strcasecmp(version, "HTTP/1.1"))
491                 header->version = HTTP_HTTP11;
492         else
493                 header->version = HTTP_HTTP10;
494
495
496         if (header->type != HTTP_REQUEST) {
497                 /* proxy request */
498
499                 /* find : in http:// */
500                 if (header->type != HTTP_CONNECT) {
501                         ptr = strchr(buf, ':');
502                         if (ptr == NULL) goto error;
503
504                         *(ptr++) = '\0';
505
506                         header->proto = xstrdup(buf);
507
508                         if (ptr[1] == '\0')
509                                 goto error;
510
511                         strcpy(buf, &ptr[2]);
512                 }
513
514                 /* find beginning of file */
515                 ptr = strchr(buf, '/');
516                 if (ptr != NULL) {
517                         header->file = xstrdup(ptr);
518                         *ptr = '\0';
519                 }
520
521                 /* strip out username:password if it exists, we won't need it */
522                 ptr = strchr(buf, '@');
523                 if (ptr != NULL)
524                         strcpy(buf, &ptr[1]);
525
526                 /* grab the port */
527                 ptr = strchr(buf, ':');
528                 if (ptr != NULL) {
529                         header->port = atoi(&ptr[1]);
530                         *ptr = '\0';
531                 } else
532                         header->port = (header->type == HTTP_CONNECT) ? 443 : 80;
533
534                 header->host = url_decode(buf, strlen(buf));
535         } else {
536                 /* http request */
537
538                 header->file = xstrdup(buf);
539         }
540
541         if (header->file == NULL) header->file = xstrdup("/");
542         header->method = xstrdup(method);
543
544         http_header_list_parse(header, s);
545
546         if (header->type != HTTP_REQUEST)
547                 header->url_command = url_command_parse(header->host);
548         else {
549                 ptr2 = strchr(&header->file[1], '/');
550                 if (ptr2 != NULL) {
551                         /* take url command from beginning of file if this is an http request */
552                         ptr = url_decode(&header->file[1], ptr2 - header->file - 1);
553                         header->url_command = url_command_parse(ptr);
554                         xfree(ptr);
555
556                         if (header->url_command != NULL) {
557                                 strcpy(header->file, ptr2);
558
559                                 header->file = xrealloc(header->file, strlen(header->file) + 1);
560                         }
561                 }
562         }
563
564         if (header->referer != NULL) {
565                 /* get url command from Referer header sent by browser */
566                 ptr2 = strstr(header->referer, "://");
567                 if (ptr2 != NULL)
568                         ptr2 += 3;
569                 else
570                         ptr2 = header->referer;
571
572                 ptr = url_decode(ptr2, strlen(ptr2));
573
574                 url_command = url_command_parse(ptr);
575
576                 if (url_command != NULL) {
577                         /* get rid of URL command from Referer string */
578                         /* yes.. I know, it's slightly broken... I doubt many sites are THAT pedantic though */
579                         xfree(header->referer);
580
581                         ptr2 = url_encode(ptr, strlen(ptr));
582                         snprintf(buf, sizeof(buf), "http://%s", ptr2);
583                         xfree(ptr2);
584
585                         header->referer = xstrdup(buf);
586
587                         /* this all needs to be done even if we've already got a url command from the URL
588                            to get rid of the URL command from the Referer URL */
589                         if (header->url_command == NULL)
590                                 header->url_command = url_command;
591                         else
592                                 url_command_free(url_command);
593                 }
594
595                 xfree(ptr);
596         }                                                       
597
598         return header;
599
600         error:
601         http_header_free(header);
602         return NULL;
603 }
604
605 void http_header_list_parse(HEADER * header, char *s)
606 {
607         char *ptr, *lptr, *eolptr, buf[8096];
608         struct HTTP_HEADER_LIST *header_list = NULL;
609
610         /* iterate through each header, grab useful header values and put them all into a linked list */
611         lptr = strchr(s, '\n');
612         while (lptr != NULL) {
613                 lptr++;
614
615                 eolptr = strchr(lptr, '\n');
616
617                 s_strncpy(buf, lptr, sizeof(buf));
618
619                 STRIP_CRLF(buf);
620
621                 ptr = strchr(buf, ':');
622
623                 if (ptr != NULL && ptr[1] != '\0') {
624                         *ptr = '\0';
625                         ptr += 2;
626
627                         if (header->content_length == -1 && !strcasecmp(buf, "Content-length"))
628                                 header->content_length = atoi(ptr);
629                         else if (!header->chunked && !strcasecmp(buf, "Transfer-Encoding") && !strcasecmp(ptr, "chunked"))
630                                 header->chunked = TRUE;
631                         else if (!header->content_type && !strcasecmp(buf, "Content-Type"))
632                                 header->content_type = xstrdup(ptr);
633                         else if (!header->content_encoding && !strcasecmp(buf, "Content-Encoding"))
634                                 header->content_encoding = xstrdup(ptr);
635                         else if (!header->accept_encoding && !strcasecmp(buf, "Accept-Encoding"))
636                                 header->accept_encoding = xstrdup(ptr);
637                         else if (!header->location && !strcasecmp(buf, "Location"))
638                                 header->location = xstrdup(ptr);
639                         else if (!header->host_header && !strcasecmp(buf, "Host"))
640                                 header->host_header = xstrdup(ptr);
641                         else if (!header->proxy_authenticate && !strcasecmp(buf, "Proxy-Authenticate"))
642                                 header->proxy_authenticate = xstrdup(ptr);
643                         else if (!header->proxy_authorization && !strcasecmp(buf, "Proxy-Authorization"))
644                                 header->proxy_authorization = xstrdup(ptr);
645                         else if (!header->referer && !strcasecmp(buf, "Referer"))
646                                 header->referer = xstrdup(ptr);
647                         else if (header->keepalive == -1 && !strcasecmp(buf, "Connection")) {
648                                 /* some browsers send stuff after the keep-alive or close */
649                                 if (!strncasecmp(ptr, "keep-alive", 10))
650                                         header->keepalive = TRUE;
651                                 else if (!strncasecmp(ptr, "close", 5))
652                                         header->keepalive = FALSE;
653                         } else if (header->proxy_keepalive == -1 && !strcasecmp(buf, "Proxy-Connection")) {
654                                 if (!strncasecmp(ptr, "keep-alive", 10))
655                                         header->proxy_keepalive = TRUE;
656                                 else if (!strncasecmp(ptr, "close", 5))
657                                         header->proxy_keepalive = FALSE;
658                         }
659
660                         header_list = header_list_insert(header_list, buf, ptr);
661
662                         if (header->header == NULL)
663                                 header->header = header_list;
664                 }
665
666                 if (eolptr != NULL && (eolptr[1] == '\r' || eolptr[1] == '\n'))
667                         break;
668                 lptr = eolptr;
669         }
670 }
671
672
673 /*
674 free memory used by HEADER structure 
675 */
676 void http_header_free(HEADER * header)
677 {
678         if (!header)
679                 return;
680
681         http_header_list_free(header->header);
682         http_header_list_free(header->header_filtered);
683
684         FREE_AND_NULL(header->method);
685         FREE_AND_NULL(header->proto);
686         FREE_AND_NULL(header->host);
687         FREE_AND_NULL(header->file);
688         FREE_AND_NULL(header->content_type);
689         FREE_AND_NULL(header->content_encoding);
690         FREE_AND_NULL(header->accept_encoding);
691         FREE_AND_NULL(header->referer);
692         FREE_AND_NULL(header->location);
693         FREE_AND_NULL(header->host_header);
694         FREE_AND_NULL(header->proxy_authenticate);
695         FREE_AND_NULL(header->proxy_authorization);
696
697         if (header->url_command != NULL)
698                 url_command_free(header->url_command);
699
700         xfree(header);
701 }
702
703 void http_header_list_free(struct HTTP_HEADER_LIST *header_list)
704 {
705         struct HTTP_HEADER_LIST *tmp_list;
706
707         while (header_list != NULL) {
708                 tmp_list = header_list->next;
709
710                 if (header_list->type != NULL)
711                         xfree(header_list->type);
712                 if (header_list->value != NULL)
713                         xfree(header_list->value);
714
715                 xfree(header_list);
716
717                 header_list = tmp_list;
718         }
719 }
720
721 struct HTTP_HEADER_LIST *http_header_list_dup(struct HTTP_HEADER_LIST *header_list)
722 {
723         struct HTTP_HEADER_LIST *ret = NULL;
724
725         for (; header_list; header_list = header_list->next)
726                 ret = header_list_insert(ret, header_list->type, header_list->value);
727
728         return ret;
729 }
730
731 /*
732 filter header list according to rules set in the XML config, return a new list
733 containing only headers for which no deny rule matched
734 */
735 struct HTTP_HEADER_LIST *header_filter(HEADER_LIST * header_list, CONNECTION * connection)
736 {
737         int action = FALSE, result = TRUE, i, ret;
738         struct HEADER_LIST_LIST *current;
739         struct HTTP_HEADER_LIST *http_header_list, *new_list = NULL, *start = NULL;
740
741         if (connection->bypass & FEATURE_HEADER)
742                 return NULL;
743
744         pthread_rwlock_rdlock(&header_list->lock);
745
746         if (header_list->enabled == FALSE) {
747                 pthread_rwlock_unlock(&header_list->lock);
748
749                 return NULL;
750         }
751
752         http_header_list = connection->header->header;
753
754         while (http_header_list != NULL) {
755                 for (i = 0; i < 2; i++) {
756                         if (i == 0) {
757                                 if (header_list->policy == POLICY_ALLOW) {
758                                         current = header_list->deny;
759                                         action = FALSE;
760                                         result = TRUE;
761                                 } else {
762                                         current = header_list->allow;
763                                         action = TRUE;
764                                         result = FALSE;
765                                 }
766                         } else if (result == action) {
767                                 if (header_list->policy == POLICY_ALLOW) {
768                                         current = header_list->allow;
769                                         action = TRUE;
770                                 } else {
771                                         current = header_list->deny;
772                                         action = FALSE;
773                                 }
774                         } else
775                                 break;
776
777                         for (; current != NULL; current = current->next) {
778                                 if (current->enabled == FALSE)
779                                         continue;
780
781                                 if (current->te != NULL) {
782                                         ret = reg_exec(current->te, http_header_list->type);
783                                         if (ret)
784                                                 continue;
785                                 }
786
787                                 if (current->ve != NULL) {
788                                         ret = reg_exec(current->ve, http_header_list->value);
789                                         if (ret)
790                                                 continue;
791                                 }
792
793                                 if (current->he != NULL) {
794                                         ret = reg_exec(current->he, connection->header->host);
795                                         if (ret)
796                                                 continue;
797                                 }
798
799                                 result = action;
800                                 break;
801                         }
802                 }
803
804                 if (result == TRUE) {
805                         new_list = header_list_insert(new_list, http_header_list->type, http_header_list->value);
806                         if (start == NULL)
807                                 start = new_list;
808                 } else
809                         putlog(MMLOG_HEADER, "removed %s: %s", http_header_list->type, http_header_list->value);
810
811                 http_header_list = http_header_list->next;
812         }
813
814         current = header_list->insert;
815
816         for (; current != NULL; current = current->next) {
817                 if (current->enabled == FALSE)
818                         continue;
819
820                 if (current->he != NULL) {
821                         ret = reg_exec(current->he, connection->header->host);
822                         if (ret)
823                                 continue;
824                 }
825
826                 if (current->type != NULL && current->value != NULL) {
827                         new_list = header_list_insert(new_list, current->type, current->value);
828                         putlog(MMLOG_HEADER, "added %s: %s", current->type, current->value);
829                 }
830         }
831
832         pthread_rwlock_unlock(&header_list->lock);
833
834         return start;
835 }
836
837 /*
838 retrieve header from browser or server
839 */
840 char *header_get(CONNECTION * connection, int flags, int timeout)
841 {
842         int x, pos = 0, first = FALSE;
843         char buf[8096];
844         struct pollfd pfd[2];
845         SOCKET *sock1, *sock2;
846         time_t start = time(NULL), now;
847
848         sock1 = (flags == SERVER) ? connection->server : connection->client;
849         sock2 = (flags == SERVER) ? connection->client : connection->server;
850
851         pfd[0].fd = sock1->fd;
852         pfd[0].events = POLLIN;
853
854         if (sock2 != NULL) {
855                 pfd[1].fd = sock2->fd;
856                 pfd[1].events = POLLIN;
857         } else
858                 pfd[1].revents = 0;
859
860         while (1) {
861                 now = time(NULL);
862
863                 if (now - start >= timeout)
864                         break;
865
866                 x = p_poll(pfd, (sock2 != NULL) ? 2 : 1, (sock1->inbuf_len != 0) ? 0 : (timeout - (now - start)) * 1000);
867
868                 if (sock1->inbuf_len != 0 || pfd[0].revents & POLLIN) {
869                         x = sock_getline(sock1, &buf[pos], sizeof(buf) - pos - 1);
870                         if (x <= 0)
871                                 break;
872
873                         if (buf[pos] == '\n' || buf[pos] == '\r' || pos == sizeof(buf) - 1) {
874                                 /* blank line or buffer size limit reached */
875
876                                 /* ignore leading blanklines, this will resolve some issues
877                                    with non-compliant browsers */
878                                 if (first == TRUE || pos == sizeof(buf) - 1) {
879                                         buf[pos] = '\0';
880                                         return xstrdup(buf);
881                                 }
882                         } else {
883                                 first = TRUE;
884                                 pos += x;
885                         }
886                 } else if (pfd[0].revents & (POLLHUP | POLLERR))
887                         break;
888
889                 if (pfd[1].revents & POLLIN) {
890                         x = sock_read(sock2, NULL, -1);
891
892                         if (x <= 0) {
893                                 /* the server may disconnect (or already be disconnected)
894                                    while reading client header */
895                                 if (flags == CLIENT) {
896                                         sock_close(connection->server);
897                                         connection->server = NULL;
898                                         sock2 = NULL;
899                                         pfd[1].revents = 0;
900                                 } else
901                                         break;
902                         }
903                 } else if (pfd[1].revents & (POLLHUP | POLLERR))
904                         break;
905         }
906
907         return NULL;
908 }
909
910 /*
911 send formatted HTTP header to browser or website without unwanted headers and blocked cookies
912 */
913 void header_send(HEADER * header, CONNECTION * connection, int which, int flags)
914 {
915         int send = TRUE;
916         struct HTTP_HEADER_LIST *http_header_list;
917         SOCKET *sock;
918
919         sock = (which == SERVER) ? connection->server : connection->client;
920         /* have sock_write buffer everything and write it all at once */
921         sock->frozen = TRUE;
922
923         http_header_list = (header->header_filtered != NULL) ? header->header_filtered : header->header;
924
925         if (header->type == HTTP_RESP)
926                 putsock(sock, "HTTP/1.1 %d %s\r\n", header->code, code_to_error(header->code));
927         else {
928                 if (flags == HEADER_DIRECT)
929                         putsock(sock, "%s %s HTTP/1.1\r\n", header->method, header->file);
930                 else {
931                         /* this is going to another proxy */
932                         if (header->type == HTTP_CONNECT)
933                                 putsock(sock, "CONNECT %s:%d HTTP/1.1\r\n", header->host, header->port);
934                         else
935                                 putsock(sock, "%s http://%s:%d%s HTTP/1.1\r\n", header->method, header->host, header->port, header->file);
936                 }
937         }
938
939         while (http_header_list != NULL) {
940                 if (!strncasecmp(http_header_list->type, "Set-Cookie", 10) || !strncasecmp(http_header_list->type, "Cookie", 6)) {
941                         send = cookie_check(cookie_list, (header->type == HTTP_RESP) ? COOKIE_IN : COOKIE_OUT, connection);
942                         if (send == FALSE) {
943                                 if (header->type == HTTP_RESP)
944                                         putlog(MMLOG_COOKIE, "blocked incoming from %s", header->host);
945                                 else
946                                         putlog(MMLOG_COOKIE, "blocked outgoing to %s", header->host);
947                         }
948                 } else if (bad_header(http_header_list->type, flags))
949                         send = FALSE;
950
951                 if (send)
952                         putsock(sock, "%s: %s\r\n", http_header_list->type, http_header_list->value);
953                 else
954                         send = TRUE;
955
956                 http_header_list = http_header_list->next;
957         }
958
959         /* content type could have been changed if the content was parsed with an external program,
960            therefore we need to send our own content-type header, not the one the site sent */
961         if (header->content_type != NULL && strcmp(header->content_type, ""))
962                 putsock(sock, "Content-type: %s\r\n", header->content_type);
963
964         if (header->content_length != -1)
965                 putsock(sock, "Content-Length: %d\r\n", header->content_length);
966
967         if (header->chunked)
968                 putsock(sock, "Transfer-Encoding: chunked\r\n");
969
970
971         switch (flags) {
972         case HEADER_FORWARD:
973                 putsock(sock, "Proxy-Connection: keep-alive\r\n");
974
975                 /* this doesn't really need to be sent with every request.. but what's the harm? :) */
976                 if (connection->proxy_auth != NULL)
977                         putsock(sock, "Proxy-Authorization: %s\r\n", connection->proxy_auth);
978         case HEADER_DIRECT:
979                 /* if gzip encoding is not supported, not sending an Accept-Encoding header at all
980                    fixes some issues with buggy versions of PHP */
981 #ifdef HAVE_ZLIB
982                 putsock(sock, "Accept-Encoding: %s\r\n", header->accept_encoding);
983 #endif /* HAVE_ZLIB */
984                 /* we need to pass our own Host: header, since the one the browser sent would be wrong
985                    if we're doing a redirect 
986                    note: some servers have problems interpreting the port field, omit it if it's 80 */
987                 if (header->port == 80)
988                         putsock(sock, "Host: %s\r\n", header->host);
989                 else
990                         putsock(sock, "Host: %s:%d\r\n", header->host, header->port);
991
992                 putsock(sock, "Connection: keep-alive\r\n");
993                 break;
994         case HEADER_RESP:
995                 if (header->location != NULL)
996                         putsock(sock, "Location: %s\r\n", header->location);
997
998                 if (header->proxy_authenticate != NULL)
999                         putsock(sock, "Proxy-Authenticate: %s\r\n", header->proxy_authenticate);
1000
1001                 if (header->content_encoding != NULL)
1002                         putsock(sock, "Content-Encoding: %s\r\n", header->content_encoding);
1003
1004                 putsock(sock, "Connection: %s\r\n", (connection->keepalive_client) ? "keep-alive" : "close");
1005                 putsock(sock, "Proxy-Connection: %s\r\n", (connection->keepalive_client) ? "keep-alive" : "close");
1006                 break;
1007         }
1008
1009         putsock(sock, "\r\n");
1010
1011         sock_flush(sock);
1012 }
1013
1014 /*
1015 return TRUE if header type isn't allowed to be passed, otherwise return FALSE
1016 */
1017 int bad_header(char *type, int flags)
1018 {
1019         switch (flags) {
1020         case HEADER_RESP:
1021                 if (!strcasecmp(type, "Connection") || !strncasecmp(type, "Proxy-", 6) || !strcasecmp(type, "Location") || !strncasecmp(type, "Content-", 8) || !strcasecmp(type, "Transfer-Encoding"))
1022                         return TRUE;
1023                 break;
1024         case HEADER_DIRECT:
1025         case HEADER_FORWARD:
1026                 if (!strcasecmp(type, "Host") || !strncasecmp(type, "Proxy-", 6) || !strncasecmp(type, "Content-", 8) || !strcasecmp(type, "Accept-Encoding") || !strcasecmp(type, "connection") || !strcasecmp(type, "keep-alive") || !strcasecmp(type, "TE") || !strcasecmp(type, "expect"))
1027                         return TRUE;
1028         }
1029
1030         return FALSE;
1031 }
1032
1033 /*
1034 return an emtpy HEADER * struct
1035 */
1036 HEADER *header_new()
1037 {
1038         HEADER *header;
1039
1040         header = xmalloc(sizeof(HEADER));
1041
1042         header->proto = NULL;
1043         header->host = NULL;
1044         header->file = NULL;
1045         header->method = NULL;
1046         header->version = 0;
1047         header->code = 0;
1048         header->type = 0;
1049         header->port = 0;
1050         header->header = NULL;
1051         header->header_filtered = NULL;
1052         header->content_length = -1;
1053         header->referer = NULL;
1054         header->chunked = FALSE;
1055         /* these need to be initialized as -1, since what we do when the client or server doesn't send a Connection:
1056            header is determined by the protocol version */
1057         header->keepalive = -1;
1058         header->proxy_keepalive = -1;
1059         header->content_type = NULL;
1060         header->content_encoding = NULL;
1061         header->accept_encoding = NULL;
1062         header->location = NULL;
1063         header->url_command = NULL;
1064         header->host_header = NULL;
1065         header->proxy_authenticate = NULL;
1066         header->proxy_authorization = NULL;
1067         return header;
1068 }
1069
1070 HEADER *http_header_dup(HEADER * h)
1071 {
1072         HEADER *header;
1073
1074         header = xmalloc(sizeof(HEADER));
1075
1076         header->proto = (h->proto != NULL) ? xstrdup(h->proto) : NULL;
1077         header->host = (h->host != NULL) ? xstrdup(h->host) : NULL;
1078         header->file = (h->file != NULL) ? xstrdup(h->file) : NULL;
1079         header->method = (h->method != NULL) ? xstrdup(h->method) : NULL;
1080         header->version = h->version;
1081         header->code = h->code;
1082         header->type = h->type;
1083         header->port = h->port;
1084         header->header = http_header_list_dup(h->header);
1085         header->header_filtered = http_header_list_dup(h->header_filtered);
1086         header->content_length = h->content_length;
1087         header->chunked = h->chunked;
1088         header->keepalive = h->keepalive;
1089         header->proxy_keepalive = h->proxy_keepalive;
1090         header->content_type = (h->content_type != NULL) ? xstrdup(h->content_type) : NULL;
1091         header->content_encoding = (h->content_encoding != NULL) ? xstrdup(h->content_encoding) : NULL;
1092         header->accept_encoding = (h->accept_encoding != NULL) ? xstrdup(h->accept_encoding) : NULL;
1093         header->location = (h->location != NULL) ? xstrdup(h->location) : NULL;
1094         header->url_command = NULL;
1095         header->host_header = (h->host_header != NULL) ? xstrdup(h->host_header) : NULL;
1096         header->proxy_authenticate = (h->proxy_authenticate != NULL) ? xstrdup(h->proxy_authenticate) : NULL;
1097         header->proxy_authorization = (h->proxy_authorization != NULL) ? xstrdup(h->proxy_authorization) : NULL;
1098         header->referer = (h->referer != NULL) ? xstrdup(h->referer) : NULL;
1099
1100         return header;
1101 }
1102
1103 /*
1104 parse options prefixed to a url, these can be stacked.. i.e. debug..bypass[r]..host.com
1105 */
1106 struct url_command_t **url_command_parse(char *url)
1107 {
1108         int count = 0;
1109         char *end, *ptr, *ptr2;
1110         struct url_command_t **ret = NULL;
1111
1112         while ((end = strstr(url, ".."))) {
1113                 ret = xrealloc(ret, (count + 2) * sizeof(struct url_command_t *));
1114                 ret[count] = xmalloc(sizeof(struct url_command_t));
1115
1116                 ret[count + 1] = NULL;
1117
1118                 ptr = memchr(url, '[', end - url);
1119                 ptr2 = NULL;
1120
1121                 if (ptr != NULL) {
1122                         ptr2 = memchr(ptr, ']', end - url);
1123                         if (ptr2 != NULL)
1124                                 ret[count]->options = xstrndup(&ptr[1], ptr2 - ptr - 1);
1125                 } else
1126                         ret[count]->options = NULL;
1127
1128                 ret[count]->command = xstrndup(url, (ptr != NULL) ? ptr - url : end - url);
1129
1130                 strcpy(url, (ptr2 != NULL) ? &ptr2[3] : &end[2]);
1131
1132                 count++;
1133         }
1134
1135         return ret;
1136 }
1137
1138 char *url_command_create(struct url_command_t **url_command) {
1139         char *ret;
1140         FILEBUF *filebuf;
1141
1142         filebuf = filebuf_new();
1143
1144         for (; *url_command; url_command++) {
1145                 if ((*url_command)->options != NULL)
1146                         filebuf_addf(filebuf, "%s[%s]..", (*url_command)->command, (*url_command)->options);
1147                 else
1148                         filebuf_addf(filebuf, "%s..", (*url_command)->command);
1149         }
1150
1151         filebuf_add(filebuf, "", 1);
1152
1153         filebuf_shorten(filebuf);
1154         ret = filebuf->data;
1155         filebuf->data = NULL;
1156
1157         filebuf_free(filebuf);
1158
1159         return ret;
1160 }
1161
1162 void url_command_free(struct url_command_t **url_command)
1163 {
1164         struct url_command_t **tmp = url_command;
1165
1166         while (*tmp != NULL) {
1167                 FREE_AND_NULL((*tmp)->command);
1168                 FREE_AND_NULL((*tmp)->options);
1169
1170                 xfree(*tmp);
1171
1172                 tmp++;
1173         }
1174
1175         xfree(url_command);
1176 }
1177
1178 /* prefix URL command to a URL */
1179 char *url_command_add(CONNECTION *connection, char *url) {
1180         char *uc, *ptr, buf[8096];
1181
1182         uc = url_command_create(connection->header->url_command);
1183
1184         if (*url == '/')
1185                 snprintf(buf, sizeof(buf), "http://%s%s:%d%s", uc, connection->header->host, connection->header->port, url);
1186         else {
1187                 ptr = strstr(url, "://");
1188                 if ((ptr != NULL && !strncasecmp(url, "http", 4)) || ptr == NULL) {
1189                         if (ptr != NULL)
1190                                 ptr += 3;
1191                         else
1192                                 ptr = url;
1193
1194                         snprintf(buf, sizeof(buf), "http://%s%s", uc, ptr);
1195                 }
1196         }
1197
1198         xfree(uc);
1199
1200         return xstrdup(buf);
1201 }