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