📄 mod_proxy.c
字号:
/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#define CORE_PRIVATE#include "mod_proxy.h"#include "mod_core.h"#include "apr_optional.h"#ifndef MAX#define MAX(x,y) ((x) >= (y) ? (x) : (y))#endif/* * A Web proxy module. Stages: * * translate_name: set filename to proxy:<URL> * map_to_storage: run proxy_walk (rather than directory_walk/file_walk) * can't trust directory_walk/file_walk since these are * not in our filesystem. Prevents mod_http from serving * the TRACE request we will set aside to handle later. * 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' */static int alias_match(const char *uri, const char *alias_fakename){ const char *end_fakename = alias_fakename + strlen(alias_fakename); const char *aliasp = alias_fakename, *urip = uri; 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 */ if (*urip++ != *aliasp++) return 0; } } /* Check last alias path component matched all the way */ if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') return 0; /* Return number of characters from URI which matched (may be * greater than length of alias, since we may have matched * doubled slashes) */ return urip - uri;}/* 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){ void *sconf = r->server->module_config; proxy_server_conf *conf; conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); /* Ick... msvc (perhaps others) promotes ternary short results to int */ if (conf->req && r->parsed_uri.scheme) { /* but it might be something vhosted */ if (!(r->parsed_uri.hostname && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r)) && ap_matches_request_vhost(r, r->parsed_uri.hostname, (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r))))) { r->proxyreq = PROXYREQ_PROXY; r->uri = r->unparsed_uri; r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); r->handler = "proxy-server"; } } /* We need special treatment for CONNECT proxying: it has no scheme part */ else if (conf->req && r->method_number == M_CONNECT && r->parsed_uri.hostname && r->parsed_uri.port_str) { r->proxyreq = PROXYREQ_PROXY; r->uri = r->unparsed_uri; r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); r->handler = "proxy-server"; } return DECLINED;}static int proxy_trans(request_rec *r){ void *sconf = r->server->module_config; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); int i, len; struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts; 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 * an issue because this is a hybrid proxy/origin server. */ for (i = 0; i < conf->aliases->nelts; i++) { len = alias_match(r->uri, ent[i].fake); if (len > 0) { if ((ent[i].real[0] == '!' ) && ( ent[i].real[1] == 0 )) { return DECLINED; } r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real, (r->uri + len ), NULL); r->handler = "proxy-server"; r->proxyreq = PROXYREQ_REVERSE; return OK; } } return DECLINED;}static int proxy_walk(request_rec *r){ proxy_server_conf *sconf = ap_get_module_config(r->server->module_config, &proxy_module); ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults; ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts; ap_conf_vector_t *entry_config; proxy_dir_conf *entry_proxy; int num_sec = sconf->sec_proxy->nelts; /* XXX: shouldn't we use URI here? Canonicalize it first? * Pass over "proxy:" prefix */ const char *proxyname = r->filename + 6; int j; for (j = 0; j < num_sec; ++j) { entry_config = sec_proxy[j]; entry_proxy = ap_get_module_config(entry_config, &proxy_module); /* XXX: What about case insensitive matching ??? * Compare regex, fnmatch or string as appropriate * If the entry doesn't relate, then continue */ if (entry_proxy->r ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0) : (entry_proxy->p_is_fnmatch ? apr_fnmatch(entry_proxy->p, proxyname, 0) : strncmp(proxyname, entry_proxy->p, strlen(entry_proxy->p)))) { continue; } per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults, entry_config); } r->per_dir_config = per_dir_defaults; return OK;}static int proxy_map_location(request_rec *r){ int access_status; if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; /* Don't let the core or mod_http map_to_storage hooks handle this, * We don't need directory/file_walk, and we want to TRACE on our own. */ if ((access_status = proxy_walk(r))) { ap_die(access_status, r); return access_status; } return OK;}/* -------------------------------------------------------------- *//* Fixup the filename *//* * Canonicalise the URL */static int proxy_fixup(request_rec *r){ char *url, *p; int access_status; if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; /* XXX: Shouldn't we try this before we run the proxy_walk? */ url = &r->filename[6]; /* canonicalise each specific scheme */ if ((access_status = proxy_run_canon_handler(r, url))) { return access_status; } p = strchr(url, ':'); if (p == NULL || p == url) return HTTP_BAD_REQUEST; return OK; /* otherwise; we've done the best we can */}/* 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 */static int proxy_needsdomain(request_rec *r, const char *url, const char *domain){ char *nuri; const char *ref; /* We only want to worry about GETs */ if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname) return DECLINED; /* If host does contain a dot already, or it is "localhost", decline */ if (strchr(r->parsed_uri.hostname, '.') != NULL || strcasecmp(r->parsed_uri.hostname, "localhost") == 0) return DECLINED; /* host name has a dot already */ ref = apr_table_get(r->headers_in, "Referer"); /* Reassemble the request, but insert the domain after the host name */ /* Note that the domain name always starts with a dot */ r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname, domain, NULL); nuri = apr_uri_unparse(r->pool, &r->parsed_uri, APR_URI_UNP_REVEALPASSWORD); apr_table_set(r->headers_out, "Location", nuri); ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Domain missing: %s sent to %s%s%s", r->uri, apr_uri_unparse(r->pool, &r->parsed_uri, APR_URI_UNP_OMITUSERINFO), ref ? " from " : "", ref ? ref : ""); return HTTP_MOVED_PERMANENTLY;}/* -------------------------------------------------------------- *//* Invoke handler */static int proxy_handler(request_rec *r){ char *url, *scheme, *p; const char *p2; void *sconf = r->server->module_config; proxy_server_conf *conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); apr_array_header_t *proxies = conf->proxies; struct proxy_remote *ents = (struct proxy_remote *) proxies->elts; int i, rc, access_status; int direct_connect = 0; const char *str; long maxfwd; /* is this for us? */ if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; /* handle max-forwards / OPTIONS / TRACE */ if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) { maxfwd = strtol(str, NULL, 10); if (maxfwd < 1) { switch (r->method_number) { case M_TRACE: { int access_status; r->proxyreq = PROXYREQ_NONE; if ((access_status = ap_send_http_trace(r))) ap_die(access_status, r); else ap_finalize_request_protocol(r); return OK; } case M_OPTIONS: { int access_status; r->proxyreq = PROXYREQ_NONE; if ((access_status = ap_send_http_options(r))) ap_die(access_status, r); else ap_finalize_request_protocol(r); return OK; } default: { return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Max-Forwards has reached zero - proxy loop?"); } } } maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0; } else { /* set configured max-forwards */ maxfwd = conf->maxfwd; } apr_table_set(r->headers_in, "Max-Forwards", apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0)); url = r->filename + 6; p = strchr(url, ':'); if (p == NULL) return HTTP_BAD_REQUEST; /* If the host doesn't have a domain name, add one and redirect. */ if (conf->domain != NULL) { rc = proxy_needsdomain(r, url, conf->domain); if (ap_is_HTTP_REDIRECT(rc)) return HTTP_MOVED_PERMANENTLY; } *p = '\0'; scheme = apr_pstrdup(r->pool, url); *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; struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -