/* MiddleMan filtering proxy server Copyright (C) 2002 Jason McLaughlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include "proto.h" extern COOKIE_LIST *cookie_list; /* load
section of xml_list into the header_list structure */ HEADER_LIST *header_load(HEADER_LIST * header_list, XML_LIST * xml_list) { HEADER_LIST *tmp_list = header_list; struct HEADER_LIST_LIST *allow = NULL, *deny = NULL, *insert = NULL; if (tmp_list == NULL) { tmp_list = xmalloc(sizeof(HEADER_LIST)); tmp_list->allow = NULL; tmp_list->deny = NULL; tmp_list->insert = NULL; tmp_list->policy = POLICY_ALLOW; tmp_list->id = 0; tmp_list->enabled = TRUE; header_list = tmp_list; pthread_rwlock_init(&tmp_list->lock, NULL); } else { allow = tmp_list->allow; deny = tmp_list->deny; insert = tmp_list->insert; } while ((xml_list = xml_section(xml_list, "
"))) { XML_LIST_LOOP(xml_list, "
") { XML_LIST_CMP(xml_list, "") { allow = header_ll_new(allow); allow->id = header_list->id++; if (tmp_list->allow == NULL) tmp_list->allow = allow; XML_LIST_LOOP(xml_list, "") { XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "false")) allow->enabled = FALSE; else allow->enabled = TRUE; } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(allow, xml_list->item, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(allow, NULL, xml_list->item, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(allow, NULL, NULL, xml_list->item, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(allow, NULL, NULL, NULL, xml_list->item); } } } XML_LIST_CMP(xml_list, "") { deny = header_ll_new(deny); deny->id = header_list->id++; if (tmp_list->deny == NULL) tmp_list->deny = deny; XML_LIST_LOOP(xml_list, "") { XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "false")) deny->enabled = FALSE; else deny->enabled = TRUE; } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(deny, xml_list->item, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(deny, NULL, xml_list->item, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(deny, NULL, NULL, xml_list->item, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(deny, NULL, NULL, NULL, xml_list->item); } } } XML_LIST_CMP(xml_list, "") { insert = header_ll_new(insert); insert->id = header_list->id++; if (tmp_list->insert == NULL) tmp_list->insert = insert; XML_LIST_LOOP(xml_list, "") { XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "false")) insert->enabled = FALSE; else insert->enabled = TRUE; } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(insert, xml_list->item, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(insert, NULL, xml_list->item, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(insert, NULL, NULL, xml_list->item, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) header_ll_insert(insert, NULL, NULL, NULL, xml_list->item); } } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "allow")) tmp_list->policy = POLICY_ALLOW; else if (!strcasecmp(xml_list->item, "deny")) tmp_list->policy = POLICY_DENY; } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) { if (!strcasecmp(xml_list->item, "false")) tmp_list->enabled = FALSE; else tmp_list->enabled = TRUE; } } } } return header_list; } XML_LIST *header_xml(HEADER_LIST * header_list, XML_LIST * xml_list) { int i; char *ptr; struct HEADER_LIST_LIST *hl = NULL; if (header_list == NULL) return xml_list; pthread_rwlock_rdlock(&header_list->lock); xml_list = xml_list_add(xml_list, "
", XML_TAG); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, (header_list->enabled == TRUE) ? "true" : "false", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, (header_list->policy == POLICY_ALLOW) ? "allow" : "deny", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); for (i = 0; i < 3; i++) { switch (i) { case 0: hl = header_list->allow; break; case 1: hl = header_list->deny; break; case 2: hl = header_list->insert; break; } for (; hl; hl = hl->next) { xml_list = xml_list_add(xml_list, (i == 0) ? "" : (i == 1) ? "" : "", XML_TAG); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, (hl->enabled == TRUE) ? "true" : "false", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); if (hl->comment != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(hl->comment); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (hl->type != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(hl->type); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (hl->value != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(hl->value); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (hl->host != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(hl->host); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } xml_list = xml_list_add(xml_list, (i == 0) ? "" : (i == 1) ? "" : "", XML_TAG); } } xml_list = xml_list_add(xml_list, "
", XML_TAG); pthread_rwlock_unlock(&header_list->lock); return xml_list; } /* free memory used by HEADER_LIST linked list */ void header_free(HEADER_LIST * header_list) { if (!header_list) return; header_ll_free(header_list->allow); header_ll_free(header_list->deny); header_ll_free(header_list->insert); pthread_rwlock_destroy(&header_list->lock); xfree(header_list); } void header_ll_free(struct HEADER_LIST_LIST *hl) { struct HEADER_LIST_LIST *tmp; while (hl != NULL) { tmp = hl->next; if (hl->te != NULL) reg_free(hl->te); if (hl->ve != NULL) reg_free(hl->ve); if (hl->he != NULL) reg_free(hl->he); FREE_AND_NULL(hl->comment); FREE_AND_NULL(hl->type); FREE_AND_NULL(hl->value); FREE_AND_NULL(hl->host); xfree(hl); hl = tmp; } } void header_ll_insert(struct HEADER_LIST_LIST *x, char *a, char *b, char *c, char *d) { if (a != NULL) { FREE_AND_NULL(x->comment); if (strcmp(a, "")) x->comment = xstrdup(a); } if (b != NULL) { if (x->te != NULL) reg_free(x->te); FREE_AND_NULL(x->type); if (strcmp(b, "")) { x->type = xstrdup(b); x->te = reg_compile(b, REGFLAGS); } else x->te = NULL; } if (c != NULL) { if (x->ve != NULL) reg_free(x->ve); FREE_AND_NULL(x->value); if (strcmp(c, "")) { x->value = xstrdup(c); x->ve = reg_compile(c, REGFLAGS); } else x->ve = NULL; } if (d != NULL) { if (x->he != NULL) reg_free(x->he); FREE_AND_NULL(x->host); if (strcmp(d, "")) { x->host = xstrdup(d); x->he = reg_compile(d, REGFLAGS); } else x->he = NULL; } } struct HEADER_LIST_LIST *header_ll_new(struct HEADER_LIST_LIST *x) { if (x == NULL) { x = xmalloc(sizeof(struct HEADER_LIST_LIST)); x->prev = NULL; } else { while (x->next != NULL) x = x->next; x->next = xmalloc(sizeof(struct HEADER_LIST_LIST)); x->next->prev = x; x = x->next; } x->enabled = TRUE; x->comment = NULL; x->te = NULL; x->ve = NULL; x->he = NULL; x->type = NULL; x->value = NULL; x->host = NULL; x->next = NULL; return x; } struct HEADER_LIST_LIST *header_ll_delete(struct HEADER_LIST_LIST *x) { struct HEADER_LIST_LIST *start = x; while (start->prev != NULL) start = start->prev; if (x->next != NULL) x->next->prev = x->prev; if (x->prev != NULL) x->prev->next = x->next; else start = start->next; if (x->te != NULL) reg_free(x->te); if (x->ve != NULL) reg_free(x->ve); FREE_AND_NULL(x->comment); FREE_AND_NULL(x->type); FREE_AND_NULL(x->value); FREE_AND_NULL(x->host); xfree(x); return start; } struct HTTP_HEADER_LIST *header_list_insert(struct HTTP_HEADER_LIST *x, char *a, char *b) { if (x == NULL) x = xmalloc(sizeof(struct HTTP_HEADER_LIST)); else { while (x->next != NULL) x = x->next; x->next = xmalloc(sizeof(struct HTTP_HEADER_LIST)); x = x->next; } x->next = NULL; x->type = xstrdup(a); x->value = xstrdup(b); return x; } HEADER *http_header_parse_response(char *s) { int x; char version[10], code[10]; HEADER *header; header = header_new(); header->type = HTTP_RESP; x = sscanf(s, "%10s %10s", version, code); if (x != 2) goto error; if (!strcasecmp(version, "HTTP/1.1")) header->version = HTTP_HTTP11; else header->version = HTTP_HTTP10; header->code = atoi(code); if (header->code <= 0) goto error; http_header_list_parse(header, s); return header; error: http_header_free(header); return NULL; } HEADER *http_header_parse_request(char *s) { int x; char *ptr, *ptr2; char buf[8096], method[24], version[10]; HEADER *header; struct url_command_t **url_command; /* parse first line */ /* version can be left out, i.e. "GET /" */ x = sscanf(s, "%24s %4096s %10s", method, buf, version); if (x < 2) return NULL; header = header_new(); if (!strcasecmp(method, "CONNECT")) header->type = HTTP_CONNECT; else if (buf[0] == '/') header->type = HTTP_REQUEST; else header->type = HTTP_PROXY; if (x == 3 && !strcasecmp(version, "HTTP/1.1")) header->version = HTTP_HTTP11; else header->version = HTTP_HTTP10; if (header->type != HTTP_REQUEST) { /* proxy request */ /* find : in http:// */ if (header->type != HTTP_CONNECT) { ptr = strchr(buf, ':'); if (ptr == NULL) goto error; *(ptr++) = '\0'; header->proto = xstrdup(buf); if (ptr[1] == '\0') goto error; strcpy(buf, &ptr[2]); } /* find beginning of file */ ptr = strchr(buf, '/'); if (ptr != NULL) { header->file = xstrdup(ptr); *ptr = '\0'; } /* strip out username:password if it exists, we won't need it */ ptr = strchr(buf, '@'); if (ptr != NULL) strcpy(buf, &ptr[1]); /* grab the port */ ptr = strchr(buf, ':'); if (ptr != NULL) { header->port = atoi(&ptr[1]); *ptr = '\0'; } else header->port = (header->type == HTTP_CONNECT) ? 443 : 80; header->host = url_decode(buf, strlen(buf)); } else { /* http request */ header->file = xstrdup(buf); } if (header->file == NULL) header->file = xstrdup("/"); header->method = xstrdup(method); http_header_list_parse(header, s); if (header->type != HTTP_REQUEST) header->url_command = url_command_parse(header->host); else { ptr2 = strchr(&header->file[1], '/'); if (ptr2 != NULL) { /* take url command from beginning of file if this is an http request */ ptr = url_decode(&header->file[1], ptr2 - header->file - 1); header->url_command = url_command_parse(ptr); xfree(ptr); if (header->url_command != NULL) { strcpy(header->file, ptr2); header->file = xrealloc(header->file, strlen(header->file) + 1); } } } if (header->referer != NULL) { /* get url command from Referer header sent by browser */ ptr2 = strstr(header->referer, "://"); if (ptr2 != NULL) ptr2 += 3; else ptr2 = header->referer; ptr = url_decode(ptr2, strlen(ptr2)); url_command = url_command_parse(ptr); if (url_command != NULL) { /* get rid of URL command from Referer string */ /* yes.. I know, it's slightly broken... I doubt many sites are THAT pedantic though */ xfree(header->referer); ptr2 = url_encode(ptr, strlen(ptr)); snprintf(buf, sizeof(buf), "http://%s", ptr2); xfree(ptr2); header->referer = xstrdup(buf); /* this all needs to be done even if we've already got a url command from the URL to get rid of the URL command from the Referer URL */ if (header->url_command == NULL) header->url_command = url_command; else url_command_free(url_command); } xfree(ptr); } return header; error: http_header_free(header); return NULL; } void http_header_list_parse(HEADER * header, char *s) { char *ptr, *lptr, *eolptr, buf[8096]; struct HTTP_HEADER_LIST *header_list = NULL; /* iterate through each header, grab useful header values and put them all into a linked list */ lptr = strchr(s, '\n'); while (lptr != NULL) { lptr++; eolptr = strchr(lptr, '\n'); s_strncpy(buf, lptr, sizeof(buf)); STRIP_CRLF(buf); ptr = strchr(buf, ':'); if (ptr != NULL && ptr[1] != '\0') { *ptr = '\0'; ptr += 2; if (header->content_length == -1 && !strcasecmp(buf, "Content-length")) header->content_length = atoi(ptr); else if (!header->chunked && !strcasecmp(buf, "Transfer-Encoding") && !strcasecmp(ptr, "chunked")) header->chunked = TRUE; else if (!header->content_type && !strcasecmp(buf, "Content-Type")) header->content_type = xstrdup(ptr); else if (!header->content_encoding && !strcasecmp(buf, "Content-Encoding")) header->content_encoding = xstrdup(ptr); else if (!header->accept_encoding && !strcasecmp(buf, "Accept-Encoding")) header->accept_encoding = xstrdup(ptr); else if (!header->location && !strcasecmp(buf, "Location")) header->location = xstrdup(ptr); else if (!header->host_header && !strcasecmp(buf, "Host")) header->host_header = xstrdup(ptr); else if (!header->proxy_authenticate && !strcasecmp(buf, "Proxy-Authenticate")) header->proxy_authenticate = xstrdup(ptr); else if (!header->proxy_authorization && !strcasecmp(buf, "Proxy-Authorization")) header->proxy_authorization = xstrdup(ptr); else if (!header->referer && !strcasecmp(buf, "Referer")) header->referer = xstrdup(ptr); else if (header->keepalive == -1 && !strcasecmp(buf, "Connection")) { /* some browsers send stuff after the keep-alive or close */ if (!strncasecmp(ptr, "keep-alive", 10)) header->keepalive = TRUE; else if (!strncasecmp(ptr, "close", 5)) header->keepalive = FALSE; } else if (header->proxy_keepalive == -1 && !strcasecmp(buf, "Proxy-Connection")) { if (!strncasecmp(ptr, "keep-alive", 10)) header->proxy_keepalive = TRUE; else if (!strncasecmp(ptr, "close", 5)) header->proxy_keepalive = FALSE; } header_list = header_list_insert(header_list, buf, ptr); if (header->header == NULL) header->header = header_list; } if (eolptr != NULL && (eolptr[1] == '\r' || eolptr[1] == '\n')) break; lptr = eolptr; } } /* free memory used by HEADER structure */ void http_header_free(HEADER * header) { if (!header) return; http_header_list_free(header->header); http_header_list_free(header->header_filtered); FREE_AND_NULL(header->method); FREE_AND_NULL(header->proto); FREE_AND_NULL(header->host); FREE_AND_NULL(header->file); FREE_AND_NULL(header->content_type); FREE_AND_NULL(header->content_encoding); FREE_AND_NULL(header->accept_encoding); FREE_AND_NULL(header->referer); FREE_AND_NULL(header->location); FREE_AND_NULL(header->host_header); FREE_AND_NULL(header->proxy_authenticate); FREE_AND_NULL(header->proxy_authorization); if (header->url_command != NULL) url_command_free(header->url_command); xfree(header); } void http_header_list_free(struct HTTP_HEADER_LIST *header_list) { struct HTTP_HEADER_LIST *tmp_list; while (header_list != NULL) { tmp_list = header_list->next; if (header_list->type != NULL) xfree(header_list->type); if (header_list->value != NULL) xfree(header_list->value); xfree(header_list); header_list = tmp_list; } } struct HTTP_HEADER_LIST *http_header_list_dup(struct HTTP_HEADER_LIST *header_list) { struct HTTP_HEADER_LIST *ret = NULL; for (; header_list; header_list = header_list->next) ret = header_list_insert(ret, header_list->type, header_list->value); return ret; } /* filter header list according to rules set in the XML config, return a new list containing only headers for which no deny rule matched */ struct HTTP_HEADER_LIST *header_filter(HEADER_LIST * header_list, CONNECTION * connection) { int action = FALSE, result = TRUE, i, ret; struct HEADER_LIST_LIST *current; struct HTTP_HEADER_LIST *http_header_list, *new_list = NULL, *start = NULL; if (connection->bypass & FEATURE_HEADER) return NULL; pthread_rwlock_rdlock(&header_list->lock); if (header_list->enabled == FALSE) { pthread_rwlock_unlock(&header_list->lock); return NULL; } http_header_list = connection->header->header; while (http_header_list != NULL) { for (i = 0; i < 2; i++) { if (i == 0) { if (header_list->policy == POLICY_ALLOW) { current = header_list->deny; action = FALSE; result = TRUE; } else { current = header_list->allow; action = TRUE; result = FALSE; } } else if (result == action) { if (header_list->policy == POLICY_ALLOW) { current = header_list->allow; action = TRUE; } else { current = header_list->deny; action = FALSE; } } else break; for (; current != NULL; current = current->next) { if (current->enabled == FALSE) continue; if (current->te != NULL) { ret = reg_exec(current->te, http_header_list->type); if (ret) continue; } if (current->ve != NULL) { ret = reg_exec(current->ve, http_header_list->value); if (ret) continue; } if (current->he != NULL) { ret = reg_exec(current->he, connection->header->host); if (ret) continue; } result = action; break; } } if (result == TRUE) { new_list = header_list_insert(new_list, http_header_list->type, http_header_list->value); if (start == NULL) start = new_list; } else putlog(MMLOG_HEADER, "removed %s: %s", http_header_list->type, http_header_list->value); http_header_list = http_header_list->next; } current = header_list->insert; for (; current != NULL; current = current->next) { if (current->enabled == FALSE) continue; if (current->he != NULL) { ret = reg_exec(current->he, connection->header->host); if (ret) continue; } if (current->type != NULL && current->value != NULL) { new_list = header_list_insert(new_list, current->type, current->value); putlog(MMLOG_HEADER, "added %s: %s", current->type, current->value); } } pthread_rwlock_unlock(&header_list->lock); return start; } /* retrieve header from browser or server */ char *header_get(CONNECTION * connection, int flags, int timeout) { int x, pos = 0, first = FALSE; char buf[8096]; struct pollfd pfd[2]; SOCKET *sock1, *sock2; time_t start = time(NULL), now; sock1 = (flags == SERVER) ? connection->server : connection->client; sock2 = (flags == SERVER) ? connection->client : connection->server; pfd[0].fd = sock1->fd; pfd[0].events = POLLIN; if (sock2 != NULL) { pfd[1].fd = sock2->fd; pfd[1].events = POLLIN; } else pfd[1].revents = 0; while (1) { now = time(NULL); if (now - start >= timeout) break; x = p_poll(pfd, (sock2 != NULL) ? 2 : 1, (sock1->inbuf_len != 0) ? 0 : (timeout - (now - start)) * 1000); if (sock1->inbuf_len != 0 || pfd[0].revents & POLLIN) { x = sock_getline(sock1, &buf[pos], sizeof(buf) - pos - 1); if (x <= 0) break; if (buf[pos] == '\n' || buf[pos] == '\r' || pos == sizeof(buf) - 1) { /* blank line or buffer size limit reached */ /* ignore leading blanklines, this will resolve some issues with non-compliant browsers */ if (first == TRUE || pos == sizeof(buf) - 1) { buf[pos] = '\0'; return xstrdup(buf); } } else { first = TRUE; pos += x; } } else if (pfd[0].revents & (POLLHUP | POLLERR)) break; if (pfd[1].revents & POLLIN) { x = sock_read(sock2, NULL, -1); if (x <= 0) { /* the server may disconnect (or already be disconnected) while reading client header */ if (flags == CLIENT) { sock_close(connection->server); connection->server = NULL; sock2 = NULL; pfd[1].revents = 0; } else break; } } else if (pfd[1].revents & (POLLHUP | POLLERR)) break; } return NULL; } /* send formatted HTTP header to browser or website without unwanted headers and blocked cookies */ void header_send(HEADER * header, CONNECTION * connection, int which, int flags) { int send = TRUE; struct HTTP_HEADER_LIST *http_header_list; SOCKET *sock; sock = (which == SERVER) ? connection->server : connection->client; /* have sock_write buffer everything and write it all at once */ sock->frozen = TRUE; http_header_list = (header->header_filtered != NULL) ? header->header_filtered : header->header; if (header->type == HTTP_RESP) putsock(sock, "HTTP/1.1 %d %s\r\n", header->code, code_to_error(header->code)); else { if (flags == HEADER_DIRECT) putsock(sock, "%s %s HTTP/1.1\r\n", header->method, header->file); else { /* this is going to another proxy */ if (header->type == HTTP_CONNECT) putsock(sock, "CONNECT %s:%d HTTP/1.1\r\n", header->host, header->port); else putsock(sock, "%s http://%s:%d%s HTTP/1.1\r\n", header->method, header->host, header->port, header->file); } } while (http_header_list != NULL) { if (!strncasecmp(http_header_list->type, "Set-Cookie", 10) || !strncasecmp(http_header_list->type, "Cookie", 6)) { send = cookie_check(cookie_list, (header->type == HTTP_RESP) ? COOKIE_IN : COOKIE_OUT, connection); if (send == FALSE) { if (header->type == HTTP_RESP) putlog(MMLOG_COOKIE, "blocked incoming from %s", header->host); else putlog(MMLOG_COOKIE, "blocked outgoing to %s", header->host); } } else if (bad_header(http_header_list->type, flags)) send = FALSE; if (send) putsock(sock, "%s: %s\r\n", http_header_list->type, http_header_list->value); else send = TRUE; http_header_list = http_header_list->next; } /* content type could have been changed if the content was parsed with an external program, therefore we need to send our own content-type header, not the one the site sent */ if (header->content_type != NULL && strcmp(header->content_type, "")) putsock(sock, "Content-type: %s\r\n", header->content_type); if (header->content_length != -1) putsock(sock, "Content-Length: %d\r\n", header->content_length); if (header->chunked) putsock(sock, "Transfer-Encoding: chunked\r\n"); switch (flags) { case HEADER_FORWARD: putsock(sock, "Proxy-Connection: keep-alive\r\n"); /* this doesn't really need to be sent with every request.. but what's the harm? :) */ if (connection->proxy_auth != NULL) putsock(sock, "Proxy-Authorization: %s\r\n", connection->proxy_auth); case HEADER_DIRECT: /* if gzip encoding is not supported, not sending an Accept-Encoding header at all fixes some issues with buggy versions of PHP */ #ifdef HAVE_ZLIB putsock(sock, "Accept-Encoding: %s\r\n", header->accept_encoding); #endif /* HAVE_ZLIB */ /* we need to pass our own Host: header, since the one the browser sent would be wrong if we're doing a redirect note: some servers have problems interpreting the port field, omit it if it's 80 */ if (header->port == 80) putsock(sock, "Host: %s\r\n", header->host); else putsock(sock, "Host: %s:%d\r\n", header->host, header->port); putsock(sock, "Connection: keep-alive\r\n"); break; case HEADER_RESP: if (header->location != NULL) putsock(sock, "Location: %s\r\n", header->location); if (header->proxy_authenticate != NULL) putsock(sock, "Proxy-Authenticate: %s\r\n", header->proxy_authenticate); if (header->content_encoding != NULL) putsock(sock, "Content-Encoding: %s\r\n", header->content_encoding); putsock(sock, "Connection: %s\r\n", (connection->keepalive_client) ? "keep-alive" : "close"); putsock(sock, "Proxy-Connection: %s\r\n", (connection->keepalive_client) ? "keep-alive" : "close"); break; } putsock(sock, "\r\n"); sock_flush(sock); } /* return TRUE if header type isn't allowed to be passed, otherwise return FALSE */ int bad_header(char *type, int flags) { switch (flags) { case HEADER_RESP: if (!strcasecmp(type, "Connection") || !strncasecmp(type, "Proxy-", 6) || !strcasecmp(type, "Location") || !strncasecmp(type, "Content-", 8) || !strcasecmp(type, "Transfer-Encoding")) return TRUE; break; case HEADER_DIRECT: case HEADER_FORWARD: 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")) return TRUE; } return FALSE; } /* return an emtpy HEADER * struct */ HEADER *header_new() { HEADER *header; header = xmalloc(sizeof(HEADER)); header->proto = NULL; header->host = NULL; header->file = NULL; header->method = NULL; header->version = 0; header->code = 0; header->type = 0; header->port = 0; header->header = NULL; header->header_filtered = NULL; header->content_length = -1; header->referer = NULL; header->chunked = FALSE; /* these need to be initialized as -1, since what we do when the client or server doesn't send a Connection: header is determined by the protocol version */ header->keepalive = -1; header->proxy_keepalive = -1; header->content_type = NULL; header->content_encoding = NULL; header->accept_encoding = NULL; header->location = NULL; header->url_command = NULL; header->host_header = NULL; header->proxy_authenticate = NULL; header->proxy_authorization = NULL; return header; } HEADER *http_header_dup(HEADER * h) { HEADER *header; header = xmalloc(sizeof(HEADER)); header->proto = (h->proto != NULL) ? xstrdup(h->proto) : NULL; header->host = (h->host != NULL) ? xstrdup(h->host) : NULL; header->file = (h->file != NULL) ? xstrdup(h->file) : NULL; header->method = (h->method != NULL) ? xstrdup(h->method) : NULL; header->version = h->version; header->code = h->code; header->type = h->type; header->port = h->port; header->header = http_header_list_dup(h->header); header->header_filtered = http_header_list_dup(h->header_filtered); header->content_length = h->content_length; header->chunked = h->chunked; header->keepalive = h->keepalive; header->proxy_keepalive = h->proxy_keepalive; header->content_type = (h->content_type != NULL) ? xstrdup(h->content_type) : NULL; header->content_encoding = (h->content_encoding != NULL) ? xstrdup(h->content_encoding) : NULL; header->accept_encoding = (h->accept_encoding != NULL) ? xstrdup(h->accept_encoding) : NULL; header->location = (h->location != NULL) ? xstrdup(h->location) : NULL; header->url_command = NULL; header->host_header = (h->host_header != NULL) ? xstrdup(h->host_header) : NULL; header->proxy_authenticate = (h->proxy_authenticate != NULL) ? xstrdup(h->proxy_authenticate) : NULL; header->proxy_authorization = (h->proxy_authorization != NULL) ? xstrdup(h->proxy_authorization) : NULL; header->referer = (h->referer != NULL) ? xstrdup(h->referer) : NULL; return header; } /* parse options prefixed to a url, these can be stacked.. i.e. debug..bypass[r]..host.com */ struct url_command_t **url_command_parse(char *url) { int count = 0; char *end, *ptr, *ptr2; struct url_command_t **ret = NULL; while ((end = strstr(url, ".."))) { ret = xrealloc(ret, (count + 2) * sizeof(struct url_command_t *)); ret[count] = xmalloc(sizeof(struct url_command_t)); ret[count + 1] = NULL; ptr = memchr(url, '[', end - url); ptr2 = NULL; if (ptr != NULL) { ptr2 = memchr(ptr, ']', end - url); if (ptr2 != NULL) ret[count]->options = xstrndup(&ptr[1], ptr2 - ptr - 1); } else ret[count]->options = NULL; ret[count]->command = xstrndup(url, (ptr != NULL) ? ptr - url : end - url); strcpy(url, (ptr2 != NULL) ? &ptr2[3] : &end[2]); count++; } return ret; } char *url_command_create(struct url_command_t **url_command) { char *ret; FILEBUF *filebuf; filebuf = filebuf_new(); for (; *url_command; url_command++) { if ((*url_command)->options != NULL) filebuf_addf(filebuf, "%s[%s]..", (*url_command)->command, (*url_command)->options); else filebuf_addf(filebuf, "%s..", (*url_command)->command); } filebuf_add(filebuf, "", 1); filebuf_shorten(filebuf); ret = filebuf->data; filebuf->data = NULL; filebuf_free(filebuf); return ret; } void url_command_free(struct url_command_t **url_command) { struct url_command_t **tmp = url_command; while (*tmp != NULL) { FREE_AND_NULL((*tmp)->command); FREE_AND_NULL((*tmp)->options); xfree(*tmp); tmp++; } xfree(url_command); } /* prefix URL command to a URL */ char *url_command_add(CONNECTION *connection, char *url) { char *uc, *ptr, buf[8096]; uc = url_command_create(connection->header->url_command); if (*url == '/') snprintf(buf, sizeof(buf), "http://%s%s:%d%s", uc, connection->header->host, connection->header->port, url); else { ptr = strstr(url, "://"); if ((ptr != NULL && !strncasecmp(url, "http", 4)) || ptr == NULL) { if (ptr != NULL) ptr += 3; else ptr = url; snprintf(buf, sizeof(buf), "http://%s%s", uc, ptr); } } xfree(uc); return xstrdup(buf); }