/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
\n" "Reason: ", ap_escape_html(r->pool, message), "", NULL)); /* Allow "error-notes" string to be printed by ap_send_error_response() */ apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*")); r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode); ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, "proxy: %s returned by %s", message, r->uri); return statuscode; } static const char * proxy_get_host_of_request(request_rec *r) { char *url, *user = NULL, *password = NULL, *err, *host; apr_port_t port = -1; if (r->hostname != NULL) return r->hostname; /* Set url to the first char after "scheme://" */ if ((url = strchr(r->uri, ':')) == NULL || url[1] != '/' || url[2] != '/') return NULL; url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); if (err != NULL) ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r, "%s", err); r->hostname = host; return host; /* ought to return the port, too */ } /* Return TRUE if addr represents an IP address (or an IP network address) */ PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p) { const char *addr = This->name; long ip_addr[4]; int i, quads; long bits; /* if the address is given with an explicit netmask, use that */ /* Due to a deficiency in apr_inet_addr(), it is impossible to parse */ /* "partial" addresses (with less than 4 quads) correctly, i.e. */ /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */ /* I therefore have to parse the IP address manually: */ /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */ /* addr and mask were set by proxy_readmask() */ /*return 1; */ /* Parse IP addr manually, optionally allowing */ /* abbreviated net addresses like 192.168. */ /* Iterate over up to 4 (dotted) quads. */ for (quads = 0; quads < 4 && *addr != '\0'; ++quads) { char *tmp; if (*addr == '/' && quads > 0) /* netmask starts here. */ break; if (!apr_isdigit(*addr)) return 0; /* no digit at start of quad */ ip_addr[quads] = strtol(addr, &tmp, 0); if (tmp == addr) /* expected a digit, found something else */ return 0; if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { /* invalid octet */ return 0; } addr = tmp; if (*addr == '.' && quads != 3) ++addr; /* after the 4th quad, a dot would be illegal */ } for (This->addr.s_addr = 0, i = 0; i < quads; ++i) This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */ char *tmp; ++addr; bits = strtol(addr, &tmp, 0); if (tmp == addr) /* expected a digit, found something else */ return 0; addr = tmp; if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */ return 0; } else { /* Determine (i.e., "guess") netmask by counting the */ /* number of trailing .0's; reduce #quads appropriately */ /* (so that 192.168.0.0 is equivalent to 192.168.) */ while (quads > 0 && ip_addr[quads - 1] == 0) --quads; /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */ if (quads < 1) return 0; /* every zero-byte counts as 8 zero-bits */ bits = 8 * quads; if (bits != 32) /* no warning for fully qualified IP address */ ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n", inet_ntoa(This->addr), bits); } This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits)); if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "Warning: NetMask and IP-Addr disagree in %s/%ld\n", inet_ntoa(This->addr), bits); This->addr.s_addr &= This->mask.s_addr; ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, " Set to %s/%ld\n", inet_ntoa(This->addr), bits); } if (*addr == '\0') { This->matcher = proxy_match_ipaddr; return 1; } else return (*addr == '\0'); /* okay iff we've parsed the whole string */ } /* Return TRUE if addr represents an IP address (or an IP network address) */ static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) { int i, ip_addr[4]; struct in_addr addr, *ip; const char *host = proxy_get_host_of_request(r); if (host == NULL) /* oops! */ return 0; memset(&addr, '\0', sizeof addr); memset(ip_addr, '\0', sizeof ip_addr); if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { for (addr.s_addr = 0, i = 0; i < 4; ++i) addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { #if DEBUGGING ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s/", inet_ntoa(This->addr)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s", inet_ntoa(This->mask)); #endif return 1; } #if DEBUGGING else { ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s/", inet_ntoa(This->addr)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s", inet_ntoa(This->mask)); } #endif } else { struct apr_sockaddr_t *reqaddr; if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool) != APR_SUCCESS) { #if DEBUGGING ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "2)IP-NoMatch: hostname=%s msg=Host not found", host); #endif return 0; } /* Try to deal with multiple IP addr's for a host */ /* FIXME: This needs to be able to deal with IPv6 */ while (reqaddr) { ip = (struct in_addr *) reqaddr->ipaddr_ptr; if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) { #if DEBUGGING ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "3)IP-Match: %s[%s] <-> ", host, inet_ntoa(*ip)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s/", inet_ntoa(This->addr)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s", inet_ntoa(This->mask)); #endif return 1; } #if DEBUGGING else { ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "3)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(*ip)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s/", inet_ntoa(This->addr)); ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "%s", inet_ntoa(This->mask)); } #endif reqaddr = reqaddr->next; } } return 0; } /* Return TRUE if addr represents a domain name */ PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p) { char *addr = This->name; int i; /* Domain name must start with a '.' */ if (addr[0] != '.') return 0; /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) continue; #if 0 if (addr[i] == ':') { ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "@@@@ handle optional port in proxy_is_domainname()"); /* @@@@ handle optional port */ } #endif if (addr[i] != '\0') return 0; /* Strip trailing dots */ for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) addr[i] = '\0'; This->matcher = proxy_match_domainname; return 1; } /* Return TRUE if host "host" is in domain "domain" */ static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) { const char *host = proxy_get_host_of_request(r); int d_len = strlen(This->name), h_len; if (host == NULL) /* some error was logged already */ return 0; h_len = strlen(host); /* @@@ do this within the setup? */ /* Ignore trailing dots in domain comparison: */ while (d_len > 0 && This->name[d_len - 1] == '.') --d_len; while (h_len > 0 && host[h_len - 1] == '.') --h_len; return h_len > d_len && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; } /* Return TRUE if host represents a host name */ PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p) { struct apr_sockaddr_t *addr; char *host = This->name; int i; /* Host names must not start with a '.' */ if (host[0] == '.') return 0; /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i); if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) return 0; This->hostaddr = addr; /* Strip trailing dots */ for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) host[i] = '\0'; This->matcher = proxy_match_hostname; return 1; } /* Return TRUE if host "host" is equal to host2 "host2" */ static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) { char *host = This->name; const char *host2 = proxy_get_host_of_request(r); int h2_len; int h1_len; if (host == NULL || host2 == NULL) return 0; /* oops! */ h2_len = strlen(host2); h1_len = strlen(host); #if 0 struct apr_sockaddr_t *addr = *This->hostaddr; /* Try to deal with multiple IP addr's for a host */ while (addr) { if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?) return 1; addr = addr->next; } #endif /* Ignore trailing dots in host2 comparison: */ while (h2_len > 0 && host2[h2_len - 1] == '.') --h2_len; while (h1_len > 0 && host[h1_len - 1] == '.') --h1_len; return h1_len == h2_len && strncasecmp(host, host2, h1_len) == 0; } /* Return TRUE if addr is to be matched as a word */ PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p) { This->matcher = proxy_match_word; return 1; } /* Return TRUE if string "str2" occurs literally in "str1" */ static int proxy_match_word(struct dirconn_entry *This, request_rec *r) { const char *host = proxy_get_host_of_request(r); return host != NULL && ap_strstr_c(host, This->name) != NULL; } /* checks whether a host in uri_addr matches proxyblock */ PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr) { int j; /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ for (j = 0; j < conf->noproxies->nelts; j++) { struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; struct apr_sockaddr_t *conf_addr = npent[j].addr; ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name); if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name)) || npent[j].name[0] == '*') { ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server, "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name); return HTTP_FORBIDDEN; } while (conf_addr) { while (uri_addr) { char *conf_ip; char *uri_ip; apr_sockaddr_ip_get(&conf_ip, conf_addr); apr_sockaddr_ip_get(&uri_ip, uri_addr); ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server, "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip); if (!apr_strnatcasecmp(conf_ip, uri_ip)) { ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server, "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip); return HTTP_FORBIDDEN; } uri_addr = uri_addr->next; } conf_addr = conf_addr->next; } } return OK; } /* set up the minimal filter set */ PROXY_DECLARE(int) ap_proxy_pre_http_connection(conn_rec *c) { ap_add_input_filter("CORE_IN", NULL, NULL, c); ap_add_output_filter("CORE", NULL, NULL, c); return OK; } PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r) { ap_add_input_filter("HTTP_IN", NULL, r, c); return OK; } /* converts a series of buckets into a string * XXX: BillS says this function performs essentially the same function as * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() * instead? I think ap_proxy_string_read() will not work properly on non ASCII * (EBCDIC) machines either. */ PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos) { apr_bucket *e; apr_status_t rv; char *pos = buff; char *response; int found = 0; apr_size_t len; /* start with an empty string */ buff[0] = 0; *eos = 0; /* loop through each brigade */ while (!found) { /* get brigade from network one line at a time */ if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, AP_MODE_GETLINE, APR_BLOCK_READ, 0))) { return rv; } /* loop through each bucket */ while (!found) { if (*eos || APR_BRIGADE_EMPTY(bb)) { /* The connection aborted or timed out */ return APR_ECONNABORTED; } e = APR_BRIGADE_FIRST(bb); if (APR_BUCKET_IS_EOS(e)) { *eos = 1; } else { if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) { return rv; } /* is string LF terminated? * XXX: This check can be made more efficient by simply checking * if the last character in the 'response' buffer is an ASCII_LF. * See ap_rgetline() for an example. */ if (memchr(response, APR_ASCII_LF, len)) { found = 1; } /* concat strings until buff is full - then throw the data away */ if (len > ((bufflen-1)-(pos-buff))) { len = (bufflen-1)-(pos-buff); } if (len > 0) { pos = apr_cpystrn(pos, response, len); } } APR_BUCKET_REMOVE(e); apr_bucket_destroy(e); } } return APR_SUCCESS; } /* remove other filters (like DECHUNK) from filter stack */ PROXY_DECLARE(void) ap_proxy_reset_output_filters(conn_rec *c) { ap_filter_t *f = c->output_filters; while (f) { if (!strcasecmp(f->frec->name, "CORE") || !strcasecmp(f->frec->name, "CONTENT_LENGTH") || !strcasecmp(f->frec->name, "HTTP_HEADER")) { f = f->next; continue; } else { ap_remove_output_filter(f); f = f->next; } } }