mod_proxy.c revision ab2c1c1c83ec91415565da5a71fbc15d9685caa6
/* ====================================================================
* 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
* 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
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#include "mod_proxy.h"
#define CORE_PRIVATE
#include "http_log.h"
#include "http_vhost.h"
#include "http_request.h"
/* This will become global when the protocol abstraction comes */
static struct proxy_services defports[] =
{
{"http", DEFAULT_HTTP_PORT},
{"ftp", DEFAULT_FTP_PORT},
{"https", DEFAULT_HTTPS_PORT},
{"gopher", DEFAULT_GOPHER_PORT},
{"nntp", DEFAULT_NNTP_PORT},
{"wais", DEFAULT_WAIS_PORT},
{"snews", DEFAULT_SNEWS_PORT},
{"prospero", DEFAULT_PROSPERO_PORT},
};
/*
* A Web proxy module. Stages:
*
* translate_name: set filename to proxy:<URL>
* type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy:
* fix_ups: convert the URL stored in the filename to the
* canonical form.
* handler: handle proxy requests
*/
/* -------------------------------------------------------------- */
/* Translate the URL into a 'filename' */
{
while (aliasp < end_fakename) {
if (*aliasp == '/') {
/* any number of '/' in the alias matches any number in
* the supplied URI, but there must be at least one...
*/
if (*urip != '/')
return 0;
while (*aliasp == '/')
++aliasp;
while (*urip == '/')
++urip;
}
else {
/* Other characters are compared literally */
return 0;
}
}
/* Check last alias path component matched all the way */
return 0;
/* Return number of characters from URI which matched (may be
* greater than length of alias, since we may have matched
* doubled slashes)
*/
}
/* Detect if an absoluteURI should be proxied or not. Note that we
* have to do this during this phase because later phases are
* "short-circuiting"... i.e. translate_names will end when the first
* module returns OK. So for example, if the request is something like:
*
* GET http://othervhost/cgi-bin/printenv HTTP/1.0
*
* mod_alias will notice the /cgi-bin part and ScriptAlias it and
* short-circuit the proxy... just because of the ordering in the
* configuration file.
*/
static int proxy_detect(request_rec *r)
{
/* but it might be something vhosted */
if (!(r->parsed_uri.hostname
r->proxyreq = 1;
r->uri = r->unparsed_uri;
r->handler = "proxy-server";
}
}
/* We need special treatment for CONNECT proxying: it has no scheme part */
&& r->parsed_uri.hostname
&& r->parsed_uri.port_str) {
r->proxyreq = 1;
r->uri = r->unparsed_uri;
r->handler = "proxy-server";
}
return DECLINED;
}
static int proxy_trans(request_rec *r)
{
int i, len;
if (r->proxyreq) {
/* someone has already set up the proxy, it was possibly ourselves
* in proxy_detect
*/
return OK;
}
/* XXX: since r->uri has been manipulated already we're not really
* compliant with RFC1945 at this point. But this probably isn't
*/
if (len > 0) {
r->handler = "proxy-server";
r->proxyreq = 1;
return OK;
}
}
return DECLINED;
}
/* -------------------------------------------------------------- */
/* Fixup the filename */
/*
* Canonicalise the URL
*/
static int proxy_fixup(request_rec *r)
{
char *url, *p;
return DECLINED;
/* canonicalise each specific scheme */
return HTTP_BAD_REQUEST;
return OK; /* otherwise; we've done the best we can */
}
{
ap_proxy_garbage_init(r, p);
}
/* Send a redirection if the request contains a hostname which is not */
/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
/* servers like Netscape's allow this and access hosts from the local */
/* domain in this case. I think it is better to redirect to a FQDN, since */
/* these will later be found in the bookmarks files. */
/* The "ProxyDomain" directive determines what domain will be appended */
{
char *nuri;
const char *ref;
/* We only want to worry about GETs */
return DECLINED;
/* If host does contain a dot already, or it is "localhost", decline */
return DECLINED; /* host name has a dot already */
/* Reassemble the request, but insert the domain after the host name */
/* Note that the domain name always starts with a dot */
&r->parsed_uri,
"Domain missing: %s sent to %s%s%s", r->uri,
return HTTP_MOVED_PERMANENTLY;
}
/* -------------------------------------------------------------- */
/* Invoke handler */
static int proxy_handler(request_rec *r)
{
int i, rc;
int direct_connect = 0;
const char *maxfwd_str;
return DECLINED;
if (r->method_number == M_TRACE &&
if (maxfwd < 1) {
int access_status;
r->proxyreq = 0;
if ((access_status = ap_send_http_trace(r)))
ap_die(access_status, r);
else
return OK;
}
}
return rc;
if (p == NULL)
return HTTP_BAD_REQUEST;
return rc;
/* If the host doesn't have a domain name, add one and redirect. */
if (ap_is_HTTP_REDIRECT(rc))
return HTTP_MOVED_PERMANENTLY;
}
*p = '\0';
*p = ':';
/* Check URI's destination host against NoProxy hosts */
/* Bypass ProxyRemote server lookup if configured as NoProxy */
/* we only know how to handle communication to a proxy via http */
/*if (strcasecmp(scheme, "http") == 0) */
{
int ii;
}
#if DEBUGGING
r->uri);
#endif
}
/* firstly, try a proxy, unless a NoProxy directive is active */
if (!direct_connect)
(p != NULL &&
/* CONNECT is a special method that bypasses the normal
* proxy code.
*/
if (r->method_number == M_CONNECT)
/* we only know how to handle communication to a proxy via http */
else
/* an error or success */
return rc;
/* we failed to talk to the upstream proxy */
}
}
/* otherwise, try it direct */
/* N.B. what if we're behind a firewall, where we must use a proxy or
* give up??
*/
/* handle the scheme */
if (r->method_number == M_CONNECT)
else
return HTTP_FORBIDDEN;
}
/* -------------------------------------------------------------- */
/* Setup configurable data */
static void *
{
/* at these levels, the cache can have 2^18 directories (256,000) */
return ps;
}
static const char *
{
struct proxy_remote *new;
char *p, *q;
int port;
p = strchr(r, ':');
return "ProxyRemote: Bad syntax for a remote proxy server";
if (q != NULL) {
return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
*q = '\0';
}
else
port = -1;
*p = '\0';
ap_str_tolower(f); /* lowercase scheme */
if (port == -1) {
int i;
break;
}
return NULL;
}
static const char *
{
struct proxy_alias *new;
return NULL;
}
static const char *
{
struct proxy_alias *new;
&proxy_module);
return NULL;
}
static const char *
{
struct noproxy_entry *new;
int found = 0;
int i;
/* Don't duplicate entries */
found = 1;
}
if (!found) {
/* Don't do name lookups on things that aren't dotted */
/*@@@FIXME: This copies only the first of (possibly many) IP addrs */
else
}
return NULL;
}
/*
* Set the ports CONNECT can use
*/
static const char *
{
int *New;
if (!ap_isdigit(arg[0]))
return "AllowCONNECT: port number must be numeric";
return NULL;
}
/* Similar to set_proxy_exclude(), but defining directly connected hosts,
* which should never be accessed via the configured ProxyRemote servers
*/
static const char *
{
struct dirconn_entry *New;
int found = 0;
int i;
/* Don't duplicate entries */
found = 1;
}
if (!found) {
#if DEBUGGING
#endif
}
#if DEBUGGING
#endif
}
#if DEBUGGING
#endif
}
else {
#if DEBUGGING
#endif
}
}
return NULL;
}
static const char *
{
if (arg[0] != '.')
return "ProxyDomain: domain name must start with a dot.";
return NULL;
}
static const char *
{
return NULL;
}
static const char *
{
int val;
return "CacheSize value must be an integer (kBytes)";
return NULL;
}
static const char *
{
return NULL;
}
static const char *
{
double val;
return "CacheLastModifiedFactor value must be a float";
return NULL;
}
static const char *
{
double val;
return "CacheMaxExpire value must be a float";
return NULL;
}
static const char *
{
double val;
return "CacheDefaultExpire value must be a float";
return NULL;
}
static const char *
{
double val;
return "CacheGcInterval value must be a float";
return NULL;
}
static const char *
{
int val;
if (val < 1)
return "CacheDirLevels value must be an integer greater than 0";
return "CacheDirLevels*CacheDirLength value must not be higher than 20";
return NULL;
}
static const char *
{
int val;
if (val < 1)
return "CacheDirLength value must be an integer greater than 0";
return "CacheDirLevels*CacheDirLength value must not be higher than 20";
return NULL;
}
static const char *
{
struct nocache_entry *new;
int found = 0;
int i;
/* Don't duplicate entries */
found = 1;
}
if (!found) {
/* Don't do name lookups on things that aren't dotted */
/*@@@FIXME: This copies only the first of (possibly many) IP addrs */
else
}
return NULL;
}
static const char *
{
if (s < 512 && s != 0) {
return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
}
psf->recv_buffer_size = s;
return NULL;
}
static const char*
{
if (s > 100 || s < 0) {
return "CacheForceCompletion must be <= 100 percent, "
"or 0 for system default.";
}
if (s > 0)
return NULL;
}
static const char*
{
else {
return "ProxyVia must be one of: "
"off | on | full | block";
}
return NULL;
}
static const handler_rec proxy_handlers[] =
{
{"proxy-server", proxy_handler},
{NULL}
};
static const command_rec proxy_cmds[] =
{
"on if the true proxy requests should be accepted"},
"a scheme, partial URL or '*' and a proxy server"},
"a virtual path and a URL"},
"a virtual path and a URL for reverse proxy behaviour"},
"A list of names, hosts or domains to which the proxy will not connect"},
"Receive buffer size for outgoing HTTP and FTP connections in bytes"},
"A list of domains, hosts, or subnets to which the proxy will connect directly"},
"The default intranet domain name (in absence of a domain in the URL)"},
"A list of ports which CONNECT may connect to"},
"The directory to store cache files"},
"The maximum disk space used by the cache in Kb"},
"The maximum time in hours to cache a document"},
"The default time in hours to cache a document"},
"The factor used to estimate Expires date from LastModified date"},
"The interval between garbage collections, in hours"},
"The number of levels of subdirectories in the cache"},
"The number of characters in subdirectory names"},
"A list of names, hosts or domains for which caching is *not* provided"},
"Force a http cache completion after this percentage is loaded"},
"Configure Via: proxy header header to one of: on | off | block | full"},
{NULL}
};
{
proxy_init, /* initializer */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
create_proxy_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
proxy_cmds, /* command ap_table_t */
proxy_handlers, /* handlers */
proxy_trans, /* translate_handler */
NULL, /* check_user_id */
NULL, /* check auth */
NULL, /* check access */
NULL, /* type_checker */
proxy_fixup, /* pre-run fixups */
NULL, /* logger */
NULL, /* header parser */
NULL, /* child_init */
NULL, /* child_exit */
proxy_detect /* post read-request */
};