/* 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 "proto.h" REDIRECT_LIST *redirect_load(REDIRECT_LIST * redirect_list, XML_LIST * xml_list) { REDIRECT_LIST *tmp_list = redirect_list; struct REDIRECT_LIST_LIST *redirect = NULL; if (tmp_list == NULL) { tmp_list = xmalloc(sizeof(REDIRECT_LIST)); tmp_list->redirect_list = NULL; tmp_list->id = 0; tmp_list->enabled = TRUE; redirect_list = tmp_list; pthread_rwlock_init(&tmp_list->lock, NULL); } else redirect = tmp_list->redirect_list; while ((xml_list = xml_section(xml_list, ""))) { XML_LIST_LOOP(xml_list, "") { XML_LIST_CMP(xml_list, "") { redirect = redirect_list_new(redirect); redirect->id = redirect_list->id++; if (tmp_list->redirect_list == NULL) tmp_list->redirect_list = redirect; 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")) redirect->enabled = FALSE; else redirect->enabled = TRUE; } } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) redirect_list_insert(redirect, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) redirect_list_insert(redirect, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) redirect_list_insert(redirect, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) redirect_list_insert(redirect, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) redirect_list_insert(redirect, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) redirect_list_insert(redirect, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL); } XML_LIST_CMP(xml_list, "") { xml_list = xml_list->next; if (xml_list->type == XML_VALUE) redirect_list_insert(redirect, NULL, NULL, NULL, 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, "false")) tmp_list->enabled = FALSE; else tmp_list->enabled = TRUE; } } } } return tmp_list; } XML_LIST *redirect_xml(REDIRECT_LIST * redirect_list, XML_LIST * xml_list) { char *ptr, buf[128]; struct REDIRECT_LIST_LIST *rl; pthread_rwlock_rdlock(&redirect_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, (redirect_list->enabled == TRUE) ? "true" : "false", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); for (rl = redirect_list->redirect_list; rl; rl = rl->next) { 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, (rl->enabled == TRUE) ? "true" : "false", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); if (rl->comment != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(rl->comment); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (rl->url != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(rl->url); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (rl->redirect != NULL) { xml_list = xml_list_add(xml_list, "", XML_TAG); ptr = string_to_xml(rl->redirect); xml_list = xml_list_add(xml_list, ptr, XML_VALUE); xfree(ptr); xml_list = xml_list_add(xml_list, "", XML_TAG); } if (rl->port != -1) { xml_list = xml_list_add(xml_list, "", XML_TAG); snprintf(buf, sizeof(buf), "%d", rl->port); xml_list = xml_list_add(xml_list, buf, 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, (rl->which == REDIRECT_BOTH) ? "both" : (rl->which == REDIRECT_URL) ? "url" : "location", 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, (rl->send302 == TRUE) ? "yes" : "no", XML_VALUE); xml_list = xml_list_add(xml_list, "", XML_TAG); xml_list = xml_list_add(xml_list, "", XML_TAG); snprintf(buf, sizeof(buf), "%s,%s,%s", (rl->options & URL_ENCODE) ? "encode" : "", (rl->options & URL_DECODEBEFORE) ? "decodebefore" : "", (rl->options & URL_DECODEAFTER) ? "decodeafter" : ""); xml_list = xml_list_add(xml_list, buf, 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, "", XML_TAG); pthread_rwlock_unlock(&redirect_list->lock); return xml_list; } struct REDIRECT_LIST_LIST *redirect_list_new(struct REDIRECT_LIST_LIST *x) { if (x == NULL) { x = xmalloc(sizeof(struct REDIRECT_LIST_LIST)); x->prev = NULL; } else { while (x->next != NULL) x = x->next; x->next = xmalloc(sizeof(struct REDIRECT_LIST_LIST)); x->next->prev = x; x = x->next; } x->enabled = TRUE; x->comment = NULL; x->up = NULL; x->url = NULL; x->port = -1; x->redirect = NULL; x->which = REDIRECT_BOTH; x->send302 = FALSE; x->options = 0; x->next = NULL; return x; } void redirect_list_insert(struct REDIRECT_LIST_LIST *x, char *a, char *b, char *c, char *d, char *e, char *f, char *g) { int i; char **args; if (a != NULL) { FREE_AND_NULL(x->comment); if (strcmp(a, "")) x->comment = xstrdup(a); } if (b != NULL) { if (x->up != NULL) reg_sub_free(x->up); FREE_AND_NULL(x->url); if (strcmp(b, "")) { x->up = reg_sub_compile(b, PCREFLAGS); x->url = xstrdup(b); } else x->url = NULL; } if (c != NULL) { FREE_AND_NULL(x->redirect); if (strcmp(c, "")) x->redirect = xstrdup(c); } if (d != NULL) { x->port = -1; if (strcmp(d, "")) x->port = atoi(d); } if (e != NULL) { x->which = REDIRECT_BOTH; if (strcmp(e, "")) { if (!strcasecmp(e, "location")) x->which = REDIRECT_HEADER; else if (!strcasecmp(e, "url")) x->which = REDIRECT_URL; } } if (f != NULL) { x->send302 = FALSE; if (strcmp(f, "")) { if (!strcasecmp(f, "yes")) x->send302 = TRUE; else x->send302 = FALSE; } } if (g != NULL) { x->options = FALSE; if (strcmp(g, "")) { args = string_break(g, ','); for (i = 0; args[i]; i++) { if (!strcasecmp(args[i], "encode")) x->options |= URL_ENCODE; else if (!strcasecmp(args[i], "decodeafter")) x->options |= URL_DECODEAFTER; else if (!strcasecmp(args[i], "decodebefore")) x->options |= URL_DECODEBEFORE; xfree(args[i]); } xfree(args); } } } struct REDIRECT_LIST_LIST *redirect_list_delete(struct REDIRECT_LIST_LIST *x) { struct REDIRECT_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->up != NULL) reg_sub_free(x->up); FREE_AND_NULL(x->comment); FREE_AND_NULL(x->url); FREE_AND_NULL(x->redirect); xfree(x); return start; } void redirect_free(REDIRECT_LIST * redirect_list) { if (redirect_list == NULL) return; redirect_list_free(redirect_list->redirect_list); xfree(redirect_list); } void redirect_list_free(struct REDIRECT_LIST_LIST *rl) { struct REDIRECT_LIST_LIST *tmp; while (rl != NULL) { tmp = rl->next; if (rl->up != NULL) reg_sub_free(rl->up); FREE_AND_NULL(rl->comment); FREE_AND_NULL(rl->url); FREE_AND_NULL(rl->redirect); xfree(rl); rl = tmp; } } int redirect_do(REDIRECT_LIST * redirect_list, CONNECTION * connection, int type) { int x = FALSE; char url[1024], *ret, *ptr, *ptr2; struct REDIRECT_LIST_LIST *rl; regmatch_sub_t *rmatch; HEADER *header; if (redirect_list == NULL) return FALSE; if (connection->bypass & FEATURE_REDIRECT) return FALSE; pthread_rwlock_rdlock(&redirect_list->lock); if (redirect_list->enabled == FALSE) { pthread_rwlock_unlock(&redirect_list->lock); return FALSE; } if (type == REDIRECT_REQUEST) snprintf(url, sizeof(url), "%s%s", (connection->header->host != NULL) ? connection->header->host : "", connection->header->file); else { ptr = (!strncasecmp(connection->rheader->location, "http://", 7)) ? connection->rheader->location + 7 : connection->rheader->location; if (*ptr == '/') snprintf(url, sizeof(url), "%s%s", connection->rheader->host, connection->rheader->location); else snprintf(url, sizeof(url), "%s", ptr); } for (rl = redirect_list->redirect_list; rl; rl = rl->next) { if (rl->up != NULL && rl->enabled == TRUE && (rl->which == REDIRECT_BOTH || (rl->which == REDIRECT_URL && type == REDIRECT_REQUEST) || (rl->which == REDIRECT_LOCATION && type == REDIRECT_HEADER))) { if (rl->options & URL_DECODEBEFORE) { /* decode url before matching */ ret = url_decode(url, strlen(url)); rmatch = reg_sub_exec(rl->up, ret); xfree(ret); } else rmatch = reg_sub_exec(rl->up, url); if (rmatch != NULL) { /* make redirect rules with empty redirect url still match, but do nothing */ if (rl->redirect == NULL) break; ret = reg_sub_parse(rmatch, rl->redirect); ptr = strchr(ret, '/'); if (ptr != NULL && (rl->options & URL_ENCODE || rl->options & URL_DECODEAFTER)) { /* encode or decode file portion of url */ ptr2 = (rl->options & URL_ENCODE) ? url_encode(&ptr[1], strlen(&ptr[1])) : url_decode(&ptr[1], strlen(&ptr[1])); ret = xrealloc(ret, ptr - ret + strlen(ptr2) + 2); ptr = strchr(ret, '/'); strcpy(&ptr[1], ptr2); xfree(ptr2); } if (type == REDIRECT_REQUEST) { if (rl->send302 == TRUE) { putlog(MMLOG_REDIRECT, "sent 302 redirect for request for %s to %s", url, ret); /* send a 302 redirect */ header = header_new(); header->type = HTTP_RESP; header->code = 302; header->content_length = 0; if (*ret != '/') { if (connection->header->url_command != NULL) { ptr = url_command_create(connection->header->url_command); snprintf(url, sizeof(url), "http://%s%s", ptr, ret); xfree(ptr); } else snprintf(url, sizeof(url), "http://%s", ret); } else snprintf(url, sizeof(url), "%s", ret); header->location = xstrdup(url); header_send(header, connection, CLIENT, HEADER_RESP); http_header_free(header); x = TRUE; } else { /* just connect to a different host */ if (ptr == NULL || *ret != '/') { FREE_AND_NULL(connection->header->host); connection->header->host = xstrndup(ret, (ptr != NULL) ? ptr - ret : ~0); } FREE_AND_NULL(connection->header->file); connection->header->file = xstrdup((ptr != NULL) ? ptr : "/"); if (rl->port != -1) connection->header->port = rl->port; putlog(MMLOG_REDIRECT, "request for %s to %s%s", url, (connection->header->host != NULL) ? connection->header->host : "", connection->header->file); } } else { /* change the Location: header from a 302 redirect */ snprintf(url, sizeof(url), "%s%s", (*ret == '/') ? "" : "http://", ret); putlog(MMLOG_REDIRECT, "302 redirect for %s to %s", connection->rheader->location, url); xfree(connection->rheader->location); connection->rheader->location = xstrdup(url); } FREE_AND_NULL(ret); reg_sub_match_free(rmatch); break; } } } pthread_rwlock_unlock(&redirect_list->lock); return x; }