/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 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); return statuscode; } /* * This routine returns its own error message */ const char *ap_proxy_host2addr(const char *host, struct hostent *reqhp) { int i; struct hostent *hp; struct per_thread_data *ptd = get_per_thread_data(); for (i = 0; host[i] != '\0'; i++) if (!apr_isdigit(host[i]) && host[i] != '.') break; if (host[i] != '\0') { hp = gethostbyname(host); if (hp == NULL) return "Host not found"; } else { ptd->ipaddr = ap_inet_addr(host); hp = gethostbyaddr((char *) &ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET); if (hp == NULL) { memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf)); ptd->hpbuf.h_name = 0; ptd->hpbuf.h_addrtype = AF_INET; ptd->hpbuf.h_length = sizeof(ptd->ipaddr); ptd->hpbuf.h_addr_list = ptd->charpbuf; ptd->hpbuf.h_addr_list[0] = (char *) &ptd->ipaddr; ptd->hpbuf.h_addr_list[1] = 0; hp = &ptd->hpbuf; } } *reqhp = *hp; return NULL; } static const char * proxy_get_host_of_request(request_rec *r) { char *url, *user = NULL, *password = NULL, *err, *host; int 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) */ 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 ap_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(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; int ip_addr[4]; struct in_addr addr; struct in_addr *ip_list; char **ip_listptr; const char *found; 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 hostent the_host; memset(&the_host, '\0', sizeof the_host); found = ap_proxy_host2addr(host, &the_host); if (found != NULL) { #if DEBUGGING ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "2)IP-NoMatch: hostname=%s msg=%s", host, found); #endif return 0; } if (the_host.h_name != NULL) found = the_host.h_name; else found = host; /* Try to deal with multiple IP addr's for a host */ for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) { ip_list = (struct in_addr *) *ip_listptr; if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) { #if DEBUGGING ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list)); 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] <-> ", found, inet_ntoa(*ip_list)); 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 0; } /* Return TRUE if addr represents a domain name */ 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 addr represents a host name */ int ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p) { struct hostent host; char *addr = This->name; int i; /* Host names must not 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); #if 0 if (addr[i] == ':') { ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "@@@@ handle optional port in proxy_is_hostname()"); /* @@@@ handle optional port */ } #endif if (addr[i] != '\0' || ap_proxy_host2addr(addr, &host) != NULL) return 0; This->hostentry = ap_pduphostent (p, &host); /* Strip trailing dots */ for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) addr[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 unsigned long *ip_list; /* Try to deal with multiple IP addr's for a host */ for (ip_list = *This->hostentry->h_addr_list; *ip_list != 0UL; ++ip_list) if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?) return 1; #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 */ 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; } apr_status_t ap_proxy_doconnect(apr_socket_t *sock, char *host, apr_uint32_t port, request_rec *r) { apr_status_t rv; int i; for (i = 0; host[i] != '\0'; i++) if (!apr_isdigit(host[i]) && host[i] != '.') break; apr_set_port(sock, APR_REMOTE, port); if (host[i] == '\0') { apr_set_ipaddr(sock, APR_REMOTE, host); host = NULL; } do { rv = apr_connect(sock, host); } while (APR_STATUS_IS_EINTR(rv)); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "proxy connect to %s port %d failed", host, port); } return rv; } /* This function is called by ap_table_do() for all header lines */ /* (from proxy_http.c and proxy_ftp.c) */ /* It is passed a table_do_args struct pointer and a MIME field and value pair */ int ap_proxy_send_hdr_line(void *p, const char *key, const char *value) { struct request_rec *r = (struct request_rec *)p; if (key == NULL || value == NULL || value[0] == '\0') return 1; if (!r->assbackwards) ap_rvputs(r, key, ": ", value, CRLF, NULL); return 1; /* tell ap_table_do() to continue calling us for more headers */ } /* send a text line to one or two BUFF's; return line length */ unsigned ap_proxy_bputs2(const char *data, apr_socket_t *client, ap_cache_el *cache) { unsigned len = strlen(data); apr_file_t *cachefp = NULL; apr_send(client, data, &len); if (ap_cache_el_data(cache, &cachefp) == APR_SUCCESS) apr_puts(data, cachefp); return len; } #if defined WIN32 static DWORD tls_index; BOOL WINAPI DllMain (HINSTANCE dllhandle, DWORD reason, LPVOID reserved) { LPVOID memptr; switch (reason) { case DLL_PROCESS_ATTACH: tls_index = TlsAlloc(); case DLL_THREAD_ATTACH: /* intentional no break */ TlsSetValue (tls_index, malloc (sizeof (struct per_thread_data))); break; case DLL_THREAD_DETACH: memptr = TlsGetValue (tls_index); if (memptr) { free (memptr); TlsSetValue (tls_index, 0); } break; } return TRUE; } #endif static struct per_thread_data *get_per_thread_data(void) { #if 0 #if defined(WIN32) return (struct per_thread_data *) TlsGetValue (tls_index); #else static APACHE_TLS struct per_thread_data sptd; return &sptd; #endif #endif return NULL; } int ap_proxy_cache_send(request_rec *r, ap_cache_el *c) { apr_file_t *cachefp = NULL; apr_socket_t *fp = r->connection->client_socket; char buffer[500]; apr_size_t len; ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, "Sending cache file for %s", c->name); if(ap_cache_el_data(c, &cachefp) != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; /* send the response */ if(apr_fgets(buffer, sizeof(buffer), cachefp)) { len = strlen(buffer); apr_send(fp, buffer, &len); } /* send headers */ ap_cache_el_header_walk(c, ap_proxy_send_hdr_line, r, NULL); len = 2; apr_send(fp, CRLF, &len); /* send data */ /* XXX I changed the ap_proxy_send_fb call to use fp instead of cachefp. * this compiles cleanly, but it is probably the completely wrong * solution. We need to go through the proxy code, and remove all * of the BUFF's. rbb */ if(!r->header_only && !ap_proxy_send_fb(0, fp, r, NULL)) return HTTP_INTERNAL_SERVER_ERROR; return OK; } int ap_proxy_cache_should_cache(request_rec *r, apr_table_t *resp_hdrs, const int is_HTTP1) { const char *expire = apr_table_get(resp_hdrs, "Expires"); time_t expc; if (expire != NULL) expc = ap_parseHTTPdate(expire); else expc = BAD_DATE; if((r->status != HTTP_OK && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) || (r->status == HTTP_NOT_MODIFIED) || r->header_only || apr_table_get(r->headers_in, "Authorization") != NULL || (expire != NULL && expc == BAD_DATE) || (r->status == HTTP_OK && !apr_table_get(resp_hdrs, "Last-Modified") && is_HTTP1)) { ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, "proxy: Response is not cacheable: %s", r->unparsed_uri); return 0; } return 1; } /* * what responses should we not cache? * Unknown status responses and those known to be uncacheable * 304 HTTP_NOT_MODIFIED response when we have no valid cache file, or * 200 HTTP_OK response from HTTP/1.0 and up without a Last-Modified header, or * HEAD requests, or * requests with an Authorization header, or * protocol requests nocache (e.g. ftp with user/password) */ /* @@@ XXX FIXME: is the test "r->status != HTTP_MOVED_PERMANENTLY" correct? * or shouldn't it be "ap_is_HTTP_REDIRECT(r->status)" ? -MnKr */ int ap_proxy_cache_update(ap_cache_el *c) { ap_cache_handle_t *h = c ? c->cache : NULL; if(!h) return DECLINED; ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, NULL, "proxy: Cache finalized: %s", c->name); ap_cache_el_finalize(c); ap_cache_garbage_collect(h); return DECLINED; }