1 /* ====================================================================
2 * The Apache Software License, Version 1.1
4 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * 3. The end-user documentation included with the redistribution,
20 * if any, must include the following acknowledgment:
21 * "This product includes software developed by the
22 * Apache Software Foundation (http://www.apache.org/)."
23 * Alternately, this acknowledgment may appear in the software itself,
24 * if and wherever such third-party acknowledgments normally appear.
26 * 4. The names "Apache" and "Apache Software Foundation" must
27 * not be used to endorse or promote products derived from this
28 * software without prior written permission. For written
29 * permission, please contact apache@apache.org.
31 * 5. Products derived from this software may not be called "Apache",
32 * nor may "Apache" appear in their name, without prior written
33 * permission of the Apache Software Foundation.
35 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47 * ====================================================================
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Apache Software Foundation. For more
51 * information on the Apache Software Foundation, please see
52 * <http://www.apache.org/>.
54 * Portions of this software are based upon public domain software
55 * originally written at the National Center for Supercomputing Applications,
56 * University of Illinois, Urbana-Champaign.
61 #include "mod_proxy.h"
64 #include "apr_optional.h"
67 #define MAX(x,y) ((x) >= (y) ? (x) : (y))
71 * A Web proxy module. Stages:
73 * translate_name: set filename to proxy:<URL>
74 * map_to_storage: run proxy_walk (rather than directory_walk/file_walk)
75 * can't trust directory_walk/file_walk since these are
76 * not in our filesystem. Prevents mod_http from serving
77 * the TRACE request we will set aside to handle later.
78 * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy:
79 * fix_ups: convert the URL stored in the filename to the
81 * handler: handle proxy requests
84 /* -------------------------------------------------------------- */
85 /* Translate the URL into a 'filename' */
87 static int alias_match(const char *uri, const char *alias_fakename)
89 const char *end_fakename = alias_fakename + strlen(alias_fakename);
90 const char *aliasp = alias_fakename, *urip = uri;
92 while (aliasp < end_fakename) {
94 /* any number of '/' in the alias matches any number in
95 * the supplied URI, but there must be at least one...
100 while (*aliasp == '/')
106 /* Other characters are compared literally */
107 if (*urip++ != *aliasp++)
112 /* Check last alias path component matched all the way */
114 if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
117 /* Return number of characters from URI which matched (may be
118 * greater than length of alias, since we may have matched
125 /* Detect if an absoluteURI should be proxied or not. Note that we
126 * have to do this during this phase because later phases are
127 * "short-circuiting"... i.e. translate_names will end when the first
128 * module returns OK. So for example, if the request is something like:
130 * GET http://othervhost/cgi-bin/printenv HTTP/1.0
132 * mod_alias will notice the /cgi-bin part and ScriptAlias it and
133 * short-circuit the proxy... just because of the ordering in the
134 * configuration file.
136 static int proxy_detect(request_rec *r)
138 void *sconf = r->server->module_config;
139 proxy_server_conf *conf;
141 conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
143 /* Ick... msvc (perhaps others) promotes ternary short results to int */
145 if (conf->req && r->parsed_uri.scheme) {
146 /* but it might be something vhosted */
147 if (!(r->parsed_uri.hostname
148 && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
149 && ap_matches_request_vhost(r, r->parsed_uri.hostname,
150 (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port
151 : ap_default_port(r))))) {
152 r->proxyreq = PROXYREQ_PROXY;
153 r->uri = r->unparsed_uri;
154 r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
155 r->handler = "proxy-server";
158 /* We need special treatment for CONNECT proxying: it has no scheme part */
159 else if (conf->req && r->method_number == M_CONNECT
160 && r->parsed_uri.hostname
161 && r->parsed_uri.port_str) {
162 r->proxyreq = PROXYREQ_PROXY;
163 r->uri = r->unparsed_uri;
164 r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
165 r->handler = "proxy-server";
170 static int proxy_trans(request_rec *r)
172 void *sconf = r->server->module_config;
173 proxy_server_conf *conf =
174 (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
176 struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
179 /* someone has already set up the proxy, it was possibly ourselves
185 /* XXX: since r->uri has been manipulated already we're not really
186 * compliant with RFC1945 at this point. But this probably isn't
187 * an issue because this is a hybrid proxy/origin server.
190 for (i = 0; i < conf->aliases->nelts; i++) {
191 len = alias_match(r->uri, ent[i].fake);
194 if ((ent[i].real[0] == '!' ) && ( ent[i].real[1] == 0 )) {
198 r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
199 (r->uri + len ), NULL);
200 r->handler = "proxy-server";
201 r->proxyreq = PROXYREQ_REVERSE;
208 static int proxy_walk(request_rec *r)
210 proxy_server_conf *sconf = ap_get_module_config(r->server->module_config,
212 ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
213 ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts;
214 ap_conf_vector_t *entry_config;
215 proxy_dir_conf *entry_proxy;
216 int num_sec = sconf->sec_proxy->nelts;
217 /* XXX: shouldn't we use URI here? Canonicalize it first?
218 * Pass over "proxy:" prefix
220 const char *proxyname = r->filename + 6;
223 for (j = 0; j < num_sec; ++j)
225 entry_config = sec_proxy[j];
226 entry_proxy = ap_get_module_config(entry_config, &proxy_module);
228 /* XXX: What about case insensitive matching ???
229 * Compare regex, fnmatch or string as appropriate
230 * If the entry doesn't relate, then continue
233 ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0)
234 : (entry_proxy->p_is_fnmatch
235 ? apr_fnmatch(entry_proxy->p, proxyname, 0)
236 : strncmp(proxyname, entry_proxy->p,
237 strlen(entry_proxy->p)))) {
240 per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults,
244 r->per_dir_config = per_dir_defaults;
249 static int proxy_map_location(request_rec *r)
253 if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
256 /* Don't let the core or mod_http map_to_storage hooks handle this,
257 * We don't need directory/file_walk, and we want to TRACE on our own.
259 if ((access_status = proxy_walk(r))) {
260 ap_die(access_status, r);
261 return access_status;
267 /* -------------------------------------------------------------- */
268 /* Fixup the filename */
271 * Canonicalise the URL
273 static int proxy_fixup(request_rec *r)
278 if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
281 /* XXX: Shouldn't we try this before we run the proxy_walk? */
282 url = &r->filename[6];
284 /* canonicalise each specific scheme */
285 if ((access_status = proxy_run_canon_handler(r, url))) {
286 return access_status;
289 p = strchr(url, ':');
290 if (p == NULL || p == url)
291 return HTTP_BAD_REQUEST;
293 return OK; /* otherwise; we've done the best we can */
296 /* Send a redirection if the request contains a hostname which is not */
297 /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
298 /* servers like Netscape's allow this and access hosts from the local */
299 /* domain in this case. I think it is better to redirect to a FQDN, since */
300 /* these will later be found in the bookmarks files. */
301 /* The "ProxyDomain" directive determines what domain will be appended */
302 static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
307 /* We only want to worry about GETs */
308 if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
311 /* If host does contain a dot already, or it is "localhost", decline */
312 if (strchr(r->parsed_uri.hostname, '.') != NULL
313 || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
314 return DECLINED; /* host name has a dot already */
316 ref = apr_table_get(r->headers_in, "Referer");
318 /* Reassemble the request, but insert the domain after the host name */
319 /* Note that the domain name always starts with a dot */
320 r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname,
322 nuri = apr_uri_unparse(r->pool,
324 APR_URI_UNP_REVEALPASSWORD);
326 apr_table_set(r->headers_out, "Location", nuri);
327 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
328 "Domain missing: %s sent to %s%s%s", r->uri,
329 apr_uri_unparse(r->pool, &r->parsed_uri,
330 APR_URI_UNP_OMITUSERINFO),
331 ref ? " from " : "", ref ? ref : "");
333 return HTTP_MOVED_PERMANENTLY;
336 /* -------------------------------------------------------------- */
339 static int proxy_handler(request_rec *r)
341 char *url, *scheme, *p;
343 void *sconf = r->server->module_config;
344 proxy_server_conf *conf = (proxy_server_conf *)
345 ap_get_module_config(sconf, &proxy_module);
346 apr_array_header_t *proxies = conf->proxies;
347 struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
348 int i, rc, access_status;
349 int direct_connect = 0;
353 /* is this for us? */
354 if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
357 /* handle max-forwards / OPTIONS / TRACE */
358 if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) {
359 maxfwd = strtol(str, NULL, 10);
361 switch (r->method_number) {
364 r->proxyreq = PROXYREQ_NONE;
365 if ((access_status = ap_send_http_trace(r)))
366 ap_die(access_status, r);
368 ap_finalize_request_protocol(r);
373 r->proxyreq = PROXYREQ_NONE;
374 if ((access_status = ap_send_http_options(r)))
375 ap_die(access_status, r);
377 ap_finalize_request_protocol(r);
381 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
382 "Max-Forwards has reached zero - proxy loop?");
386 maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0;
389 /* set configured max-forwards */
390 maxfwd = conf->maxfwd;
392 apr_table_set(r->headers_in, "Max-Forwards",
393 apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0));
395 url = r->filename + 6;
396 p = strchr(url, ':');
398 return HTTP_BAD_REQUEST;
400 /* If the host doesn't have a domain name, add one and redirect. */
401 if (conf->domain != NULL) {
402 rc = proxy_needsdomain(r, url, conf->domain);
403 if (ap_is_HTTP_REDIRECT(rc))
404 return HTTP_MOVED_PERMANENTLY;
408 scheme = apr_pstrdup(r->pool, url);
411 /* Check URI's destination host against NoProxy hosts */
412 /* Bypass ProxyRemote server lookup if configured as NoProxy */
413 /* we only know how to handle communication to a proxy via http */
414 /*if (strcasecmp(scheme, "http") == 0) */
417 struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
419 for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
420 direct_connect = list[ii].matcher(&list[ii], r);
423 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
424 (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
429 /* firstly, try a proxy, unless a NoProxy directive is active */
430 if (!direct_connect) {
431 for (i = 0; i < proxies->nelts; i++) {
432 p2 = ap_strchr_c(ents[i].scheme, ':'); /* is it a partial URL? */
433 if (strcmp(ents[i].scheme, "*") == 0 ||
434 (ents[i].use_regex && ap_regexec(ents[i].regexp, url, 0,NULL, 0)) ||
435 (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
437 strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
439 /* handle the scheme */
440 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
441 "Trying to run scheme_handler against proxy");
442 access_status = proxy_run_scheme_handler(r, conf, url, ents[i].hostname, ents[i].port);
444 /* an error or success */
445 if (access_status != DECLINED && access_status != HTTP_BAD_GATEWAY) {
446 return access_status;
448 /* we failed to talk to the upstream proxy */
453 /* otherwise, try it direct */
454 /* N.B. what if we're behind a firewall, where we must use a proxy or
458 /* handle the scheme */
459 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
460 "Trying to run scheme_handler");
461 access_status = proxy_run_scheme_handler(r, conf, url, NULL, 0);
462 if (DECLINED == access_status) {
463 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
464 "proxy: No protocol handler was valid for the URL %s. "
465 "If you are using a DSO version of mod_proxy, make sure "
466 "the proxy submodules are included in the configuration "
467 "using LoadModule.", r->uri);
468 return HTTP_FORBIDDEN;
470 return access_status;
473 /* -------------------------------------------------------------- */
474 /* Setup configurable data */
476 static void * create_proxy_config(apr_pool_t *p, server_rec *s)
478 proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
480 ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *));
481 ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote));
482 ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
483 ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
484 ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry));
485 ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry));
486 ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int));
488 ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
489 ps->viaopt_set = 0; /* 0 means default */
492 ps->recv_buffer_size = 0; /* this default was left unset for some reason */
493 ps->recv_buffer_size_set = 0;
494 ps->io_buffer_size = AP_IOBUFSIZE;
495 ps->io_buffer_size_set = 0;
496 ps->maxfwd = DEFAULT_MAX_FORWARDS;
498 ps->error_override = 0;
499 ps->error_override_set = 0;
500 ps->preserve_host_set = 0;
501 ps->preserve_host = 0;
504 ps->badopt = bad_error;
509 static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
511 proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
512 proxy_server_conf *base = (proxy_server_conf *) basev;
513 proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
515 ps->proxies = apr_array_append(p, base->proxies, overrides->proxies);
516 ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
517 ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
518 ps->raliases = apr_array_append(p, base->raliases, overrides->raliases);
519 ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
520 ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
521 ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
523 ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
524 ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
525 ps->req = (overrides->req_set == 0) ? base->req : overrides->req;
526 ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size;
527 ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size;
528 ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd;
529 ps->error_override = (overrides->error_override_set == 0) ? base->error_override : overrides->error_override;
530 ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host;
531 ps->timeout= (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
532 ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
537 static void *create_proxy_dir_config(apr_pool_t *p, char *dummy)
539 proxy_dir_conf *new =
540 (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
542 /* Filled in by proxysection, when applicable */
547 static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
549 proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
550 proxy_dir_conf *add = (proxy_dir_conf *) addv;
553 new->p_is_fnmatch = add->p_is_fnmatch;
560 add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex)
562 server_rec *s = cmd->server;
563 proxy_server_conf *conf =
564 (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
565 struct proxy_remote *new;
567 char *r, *f, *scheme;
571 r = apr_pstrdup(cmd->pool, r1);
572 scheme = apr_pstrdup(cmd->pool, r1);
573 f = apr_pstrdup(cmd->pool, f1);
575 if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') {
577 return "ProxyRemoteMatch: Bad syntax for a remote proxy server";
579 return "ProxyRemote: Bad syntax for a remote proxy server";
584 q = strchr(p + 3, ':');
586 if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
588 return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)";
590 return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
598 reg = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
600 return "Regular expression for ProxyRemoteMatch could not be compiled.";
603 if (strchr(f, ':') == NULL)
604 ap_str_tolower(f); /* lowercase scheme */
605 ap_str_tolower(p + 3); /* lowercase hostname */
608 port = apr_uri_port_of_scheme(scheme);
611 new = apr_array_push(conf->proxies);
614 new->hostname = p + 3;
617 new->use_regex = regex;
622 add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
624 return add_proxy(cmd, dummy, f1, r1, 0);
628 add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
630 return add_proxy(cmd, dummy, f1, r1, 1);
634 add_pass(cmd_parms *cmd, void *dummy, const char *f, const char *r)
636 server_rec *s = cmd->server;
637 proxy_server_conf *conf =
638 (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
639 struct proxy_alias *new;
640 if (r!=NULL && cmd->path == NULL ) {
641 new = apr_array_push(conf->aliases);
644 } else if (r==NULL && cmd->path != NULL) {
645 new = apr_array_push(conf->aliases);
646 new->fake = cmd->path;
650 return "ProxyPass needs a path when not defined in a location";
652 return "ProxyPass can not have a path when defined in a location";
659 add_pass_reverse(cmd_parms *cmd, void *dummy, const char *f, const char *r)
661 server_rec *s = cmd->server;
662 proxy_server_conf *conf;
663 struct proxy_alias *new;
665 conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
667 if (r!=NULL && cmd->path == NULL ) {
668 new = apr_array_push(conf->raliases);
671 } else if (r==NULL && cmd->path != NULL) {
672 new = apr_array_push(conf->raliases);
673 new->fake = cmd->path;
677 return "ProxyPassReverse needs a path when not defined in a location";
679 return "ProxyPassReverse can not have a path when defined in a location";
686 set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg)
688 server_rec *s = parms->server;
689 proxy_server_conf *conf =
690 ap_get_module_config(s->module_config, &proxy_module);
691 struct noproxy_entry *new;
692 struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
693 struct apr_sockaddr_t *addr;
697 /* Don't duplicate entries */
698 for (i = 0; i < conf->noproxies->nelts; i++) {
699 if (apr_strnatcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */
705 new = apr_array_push(conf->noproxies);
707 if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) {
718 * Set the ports CONNECT can use
721 set_allowed_ports(cmd_parms *parms, void *dummy, const char *arg)
723 server_rec *s = parms->server;
724 proxy_server_conf *conf =
725 ap_get_module_config(s->module_config, &proxy_module);
728 if (!apr_isdigit(arg[0]))
729 return "AllowCONNECT: port number must be numeric";
731 New = apr_array_push(conf->allowed_connect_ports);
736 /* Similar to set_proxy_exclude(), but defining directly connected hosts,
737 * which should never be accessed via the configured ProxyRemote servers
740 set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg)
742 server_rec *s = parms->server;
743 proxy_server_conf *conf =
744 ap_get_module_config(s->module_config, &proxy_module);
745 struct dirconn_entry *New;
746 struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
750 /* Don't duplicate entries */
751 for (i = 0; i < conf->dirconn->nelts; i++) {
752 if (strcasecmp(arg, list[i].name) == 0)
757 New = apr_array_push(conf->dirconn);
758 New->name = apr_pstrdup(parms->pool, arg);
759 New->hostaddr = NULL;
761 if (ap_proxy_is_ipaddr(New, parms->pool)) {
763 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
764 "Parsed addr %s", inet_ntoa(New->addr));
765 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
766 "Parsed mask %s", inet_ntoa(New->mask));
769 else if (ap_proxy_is_domainname(New, parms->pool)) {
770 ap_str_tolower(New->name);
772 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
773 "Parsed domain %s", New->name);
776 else if (ap_proxy_is_hostname(New, parms->pool)) {
777 ap_str_tolower(New->name);
779 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
780 "Parsed host %s", New->name);
784 ap_proxy_is_word(New, parms->pool);
786 fprintf(stderr, "Parsed word %s\n", New->name);
794 set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg)
796 proxy_server_conf *psf =
797 ap_get_module_config(parms->server->module_config, &proxy_module);
800 return "ProxyDomain: domain name must start with a dot.";
807 set_proxy_req(cmd_parms *parms, void *dummy, int flag)
809 proxy_server_conf *psf =
810 ap_get_module_config(parms->server->module_config, &proxy_module);
817 set_proxy_error_override(cmd_parms *parms, void *dummy, int flag)
819 proxy_server_conf *psf =
820 ap_get_module_config(parms->server->module_config, &proxy_module);
822 psf->error_override = flag;
823 psf->error_override_set = 1;
827 set_preserve_host(cmd_parms *parms, void *dummy, int flag)
829 proxy_server_conf *psf =
830 ap_get_module_config(parms->server->module_config, &proxy_module);
832 psf->preserve_host = flag;
833 psf->preserve_host_set = 1;
838 set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
840 proxy_server_conf *psf =
841 ap_get_module_config(parms->server->module_config, &proxy_module);
843 if (s < 512 && s != 0) {
844 return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
847 psf->recv_buffer_size = s;
848 psf->recv_buffer_size_set = 1;
853 set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
855 proxy_server_conf *psf =
856 ap_get_module_config(parms->server->module_config, &proxy_module);
859 psf->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
860 psf->io_buffer_size_set = 1;
865 set_max_forwards(cmd_parms *parms, void *dummy, const char *arg)
867 proxy_server_conf *psf =
868 ap_get_module_config(parms->server->module_config, &proxy_module);
871 return "ProxyMaxForwards must be greater or equal to zero..";
879 set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg)
881 proxy_server_conf *psf =
882 ap_get_module_config(parms->server->module_config, &proxy_module);
887 return "Proxy Timeout must be at least 1 second.";
890 psf->timeout=apr_time_from_sec(timeout);
896 set_via_opt(cmd_parms *parms, void *dummy, const char *arg)
898 proxy_server_conf *psf =
899 ap_get_module_config(parms->server->module_config, &proxy_module);
901 if (strcasecmp(arg, "Off") == 0)
902 psf->viaopt = via_off;
903 else if (strcasecmp(arg, "On") == 0)
904 psf->viaopt = via_on;
905 else if (strcasecmp(arg, "Block") == 0)
906 psf->viaopt = via_block;
907 else if (strcasecmp(arg, "Full") == 0)
908 psf->viaopt = via_full;
910 return "ProxyVia must be one of: "
911 "off | on | full | block";
919 set_bad_opt(cmd_parms *parms, void *dummy, const char *arg)
921 proxy_server_conf *psf =
922 ap_get_module_config(parms->server->module_config, &proxy_module);
924 if (strcasecmp(arg, "IsError") == 0)
925 psf->badopt = bad_error;
926 else if (strcasecmp(arg, "Ignore") == 0)
927 psf->badopt = bad_ignore;
928 else if (strcasecmp(arg, "StartBody") == 0)
929 psf->badopt = bad_body;
931 return "ProxyBadHeader must be one of: "
932 "IsError | Ignore | StartBody";
939 static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config)
941 proxy_server_conf *sconf = ap_get_module_config(s->module_config,
943 void **new_space = (void **)apr_array_push(sconf->sec_proxy);
945 *new_space = dir_config;
948 static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
951 const char *endp = ap_strrchr_c(arg, '>');
952 int old_overrides = cmd->override;
953 char *old_path = cmd->path;
954 proxy_dir_conf *conf;
955 ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
957 const command_rec *thiscmd = cmd->cmd;
959 const char *err = ap_check_cmd_context(cmd,
960 NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
966 return apr_pstrcat(cmd->pool, cmd->cmd->name,
967 "> directive missing closing '>'", NULL);
970 arg=apr_pstrndup(cmd->pool, arg, endp-arg);
973 if (thiscmd->cmd_data)
974 return "<ProxyMatch > block must specify a path";
976 return "<Proxy > block must specify a path";
979 cmd->path = ap_getword_conf(cmd->pool, &arg);
980 cmd->override = OR_ALL|ACCESS_CONF;
982 if (!strncasecmp(cmd->path, "proxy:", 6))
985 /* XXX Ignore case? What if we proxy a case-insensitive server?!?
986 * While we are at it, shouldn't we also canonicalize the entire
987 * scheme? See proxy_fixup()
989 if (thiscmd->cmd_data) { /* <ProxyMatch> */
990 r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
992 else if (!strcmp(cmd->path, "~")) {
993 cmd->path = ap_getword_conf(cmd->pool, &arg);
995 return "<Proxy ~ > block must specify a path";
996 if (strncasecmp(cmd->path, "proxy:", 6))
998 r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
1001 /* initialize our config and fetch it */
1002 conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
1003 &proxy_module, cmd->pool);
1005 errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
1010 conf->p = cmd->path;
1011 conf->p_is_fnmatch = apr_fnmatch_test(conf->p);
1013 ap_add_per_proxy_conf(cmd->server, new_dir_conf);
1016 return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
1017 "> arguments not (yet) supported.", NULL);
1020 cmd->path = old_path;
1021 cmd->override = old_overrides;
1026 static const command_rec proxy_cmds[] =
1028 AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF,
1029 "Container for directives affecting resources located in the proxied "
1031 AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF,
1032 "Container for directives affecting resources located in the proxied "
1033 "location, in regular expression syntax"),
1034 AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF,
1035 "on if the true proxy requests should be accepted"),
1036 AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF,
1037 "a scheme, partial URL or '*' and a proxy server"),
1038 AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
1039 "a regex pattern and a proxy server"),
1040 AP_INIT_TAKE12("ProxyPass", add_pass, NULL, RSRC_CONF|ACCESS_CONF,
1041 "a virtual path and a URL"),
1042 AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
1043 "a virtual path and a URL for reverse proxy behaviour"),
1044 AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
1045 "A list of names, hosts or domains to which the proxy will not connect"),
1046 AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF,
1047 "Receive buffer size for outgoing HTTP and FTP connections in bytes"),
1048 AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF,
1049 "IO buffer size for outgoing HTTP and FTP connections in bytes"),
1050 AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF,
1051 "The maximum number of proxies a request may be forwarded through."),
1052 AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF,
1053 "A list of domains, hosts, or subnets to which the proxy will connect directly"),
1054 AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF,
1055 "The default intranet domain name (in absence of a domain in the URL)"),
1056 AP_INIT_ITERATE("AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF,
1057 "A list of ports which CONNECT may connect to"),
1058 AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF,
1059 "Configure Via: proxy header header to one of: on | off | block | full"),
1060 AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF,
1061 "use our error handling pages instead of the servers' we are proxying"),
1062 AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF,
1063 "on if we should preserve host header while proxying"),
1064 AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF,
1065 "Set the timeout (in seconds) for a proxied connection. "
1066 "This overrides the server timeout"),
1067 AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF,
1068 "How to handle bad header line in response: IsError | Ignore | StartBody"),
1073 APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
1074 APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
1076 static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL;
1077 static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL;
1079 PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
1082 * if c == NULL just check if the optional function was imported
1083 * else run the optional function so ssl filters are inserted
1085 if (proxy_ssl_enable) {
1086 return c ? proxy_ssl_enable(c) : 1;
1092 PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
1094 if (proxy_ssl_disable) {
1095 return proxy_ssl_disable(c);
1101 static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1102 apr_pool_t *ptemp, server_rec *s)
1104 proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
1105 proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
1110 static void register_hooks(apr_pool_t *p)
1113 ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
1114 /* filename-to-URI translation */
1115 ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST);
1116 /* walk <Proxy > entries and suppress default TRACE behavior */
1117 ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
1119 ap_hook_fixups(proxy_fixup, NULL, NULL, APR_HOOK_FIRST);
1120 /* post read_request handling */
1121 ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST);
1122 /* post config handling */
1123 ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1126 module AP_MODULE_DECLARE_DATA proxy_module =
1128 STANDARD20_MODULE_STUFF,
1129 create_proxy_dir_config, /* create per-directory config structure */
1130 merge_proxy_dir_config, /* merge per-directory config structures */
1131 create_proxy_config, /* create per-server config structure */
1132 merge_proxy_config, /* merge per-server config structures */
1133 proxy_cmds, /* command table */
1138 APR_HOOK_LINK(scheme_handler)
1139 APR_HOOK_LINK(canon_handler)
1142 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler,
1143 (request_rec *r, proxy_server_conf *conf,
1144 char *url, const char *proxyhost,
1145 apr_port_t proxyport),(r,conf,url,
1146 proxyhost,proxyport),DECLINED)
1147 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler,
1148 (request_rec *r, char *url),(r,
1150 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups,
1151 (request_rec *r), (r),