]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy.c
Remove proxy_mdule_conf.
[apache] / modules / proxy / mod_proxy.c
1 #define FIX_15207
2 /* Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define CORE_PRIVATE
18
19 #include "mod_proxy.h"
20 #include "mod_core.h"
21
22 #include "apr_optional.h"
23
24 #if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
25 #include "mod_ssl.h"
26 #else
27 APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
28 APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
29 #endif
30
31 #ifndef MAX
32 #define MAX(x,y) ((x) >= (y) ? (x) : (y))
33 #endif
34
35 /*
36  * A Web proxy module. Stages:
37  *
38  *  translate_name: set filename to proxy:<URL>
39  *  map_to_storage: run proxy_walk (rather than directory_walk/file_walk)
40  *                  can't trust directory_walk/file_walk since these are
41  *                  not in our filesystem.  Prevents mod_http from serving
42  *                  the TRACE request we will set aside to handle later.
43  *  type_checker:   set type to PROXY_MAGIC_TYPE if filename begins proxy:
44  *  fix_ups:        convert the URL stored in the filename to the
45  *                  canonical form.
46  *  handler:        handle proxy requests
47  */
48
49 /* -------------------------------------------------------------- */
50 /* Translate the URL into a 'filename' */
51
52 #ifdef FIX_15207
53 /* XXX: EBCDIC safe? --nd */
54 #define x2c(x) (((x >= '0') && (x <= '9'))         \
55                    ? (x - '0')                     \
56                    : (((x >= 'a') && (x <= 'f'))   \
57                        ? (10 + x - 'a')            \
58                        : ((x >= 'A') && (x <='F')) \
59                            ? (10 + x - 'A')        \
60                            : 0                     \
61                      )                             \
62                )
63
64 static unsigned char hex2c(const char* p) {
65   const char c1 = p[1];
66   const char c2 = p[1] ? p[2]: '\0';
67   int i1 = c1 ? x2c(c1) : 0;
68   int i2 = c2 ? x2c(c2) : 0;
69   unsigned char ret = (i1 << 4) | i2;
70
71   return ret;
72 }
73 #endif
74
75 static const char *set_worker_param(proxy_worker *worker,
76                                     const char *key,
77                                     const char *val)
78 {
79
80     int ival;
81     if (!strcasecmp(key, "loadfactor")) {
82         worker->lbfactor = atoi(val);
83         if (worker->lbfactor < 1 || worker->lbfactor > 100)
84             return "loadfactor must be number between 1..100";
85     }
86     else if (!strcasecmp(key, "retry")) {
87         ival = atoi(val);
88         if (ival < 1)
89             return "retry must be al least one second";
90         worker->retry = apr_time_from_sec(ival);
91     }
92     else if (!strcasecmp(key, "ttl")) {
93         ival = atoi(val);
94         if (ival < 1)
95             return "ttl must be al least one second";
96         worker->ttl = apr_time_from_sec(ival);
97     }
98     else if (!strcasecmp(key, "min")) {
99         ival = atoi(val);
100         if (ival < 0)
101             return "min must be a positive number";
102         worker->min = ival;
103     }
104     else if (!strcasecmp(key, "max")) {
105         ival = atoi(val);
106         if (ival < 0)
107             return "max must be a positive number";
108         worker->hmax = ival;
109     }
110     /* XXX: More inteligent naming needed */
111     else if (!strcasecmp(key, "smax")) {
112         ival = atoi(val);
113         if (ival < 0)
114             return "smax must be a positive number";
115         worker->smax = ival;
116     }
117     else {
118         return "unknown parameter";
119     }
120     return NULL;
121 }
122
123 static const char *set_balancer_param(proxy_balancer *balancer,
124                                       const char *key,
125                                       const char *val)
126 {
127
128     int ival;
129     if (!strcasecmp(key, "stickysession")) {
130         balancer->sticky = val;
131     }
132     else if (!strcasecmp(key, "nofailover")) {
133         if (!strcasecmp(val, "on"))
134             balancer->sticky_force = 1;
135         else if (!strcasecmp(val, "off"))
136             balancer->sticky_force = 0;
137         else
138             return "failover must be On|Off";
139     }
140     else if (!strcasecmp(key, "timeout")) {
141         ival = atoi(val);
142         if (ival < 1)
143             return "timeout must be al least one second";
144         balancer->timeout = apr_time_from_sec(ival);
145     }
146     else {
147         return "unknown parameter";
148     }
149     return NULL;
150 }
151
152 static int alias_match(const char *uri, const char *alias_fakename)
153 {
154     const char *end_fakename = alias_fakename + strlen(alias_fakename);
155     const char *aliasp = alias_fakename, *urip = uri;
156     const char *end_uri = uri + strlen(uri);
157     unsigned char uric, aliasc;
158
159     while (aliasp < end_fakename && urip < end_uri) {
160         if (*aliasp == '/') {
161             /* any number of '/' in the alias matches any number in
162              * the supplied URI, but there must be at least one...
163              */
164             if (*urip != '/')
165                 return 0;
166
167             while (*aliasp == '/')
168                 ++aliasp;
169             while (*urip == '/')
170                 ++urip;
171         }
172         else {
173 #ifndef FIX_15207
174             /* Other characters are compared literally */
175             if (*urip++ != *aliasp++)
176                 return 0;
177 #else
178             /* Other characters are canonicalised and compared literally */
179             if (*urip == '%') {
180                 uric = hex2c(urip);
181                 urip += 3;
182             } else {
183                 uric = (unsigned char)*urip++;
184             }
185             if (*aliasp == '%') {
186                 aliasc = hex2c(aliasp);
187                 aliasp += 3;
188             } else {
189                 aliasc = (unsigned char)*aliasp++;
190             }
191             if (uric != aliasc) {
192                 return 0;
193             }
194 #endif
195         }
196     }
197
198     /* fixup badly encoded stuff (e.g. % as last character) */
199     if (aliasp > end_fakename) {
200         aliasp = end_fakename;
201     }
202     if (urip > end_uri) {
203         urip = end_uri;
204     }
205
206     /* Check last alias path component matched all the way */
207     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
208         return 0;
209
210     /* Return number of characters from URI which matched (may be
211      * greater than length of alias, since we may have matched
212      * doubled slashes)
213      */
214
215     return urip - uri;
216 }
217
218 /* Detect if an absoluteURI should be proxied or not.  Note that we
219  * have to do this during this phase because later phases are
220  * "short-circuiting"... i.e. translate_names will end when the first
221  * module returns OK.  So for example, if the request is something like:
222  *
223  * GET http://othervhost/cgi-bin/printenv HTTP/1.0
224  *
225  * mod_alias will notice the /cgi-bin part and ScriptAlias it and
226  * short-circuit the proxy... just because of the ordering in the
227  * configuration file.
228  */
229 static int proxy_detect(request_rec *r)
230 {
231     void *sconf = r->server->module_config;
232     proxy_server_conf *conf =
233         (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
234 #ifdef FIX_15207
235     int i, len;
236     struct proxy_alias *ent = (struct proxy_alias *)conf->aliases->elts;
237 #endif
238
239     /* Ick... msvc (perhaps others) promotes ternary short results to int */
240
241     if (conf->req && r->parsed_uri.scheme) {
242         /* but it might be something vhosted */
243         if (!(r->parsed_uri.hostname
244               && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
245               && ap_matches_request_vhost(r, r->parsed_uri.hostname,
246                                           (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port 
247                                                        : ap_default_port(r))))) {
248             r->proxyreq = PROXYREQ_PROXY;
249             r->uri = r->unparsed_uri;
250             r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
251             r->handler = "proxy-server";
252         }
253     }
254     /* We need special treatment for CONNECT proxying: it has no scheme part */
255     else if (conf->req && r->method_number == M_CONNECT
256              && r->parsed_uri.hostname
257              && r->parsed_uri.port_str) {
258         r->proxyreq = PROXYREQ_PROXY;
259         r->uri = r->unparsed_uri;
260         r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
261         r->handler = "proxy-server";
262 #ifdef FIX_15207
263     } else {
264         /* test for a ProxyPass */
265         for (i = 0; i < conf->aliases->nelts; i++) {
266             len = alias_match(r->unparsed_uri, ent[i].fake);
267             if (len > 0) {
268                 r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
269                                           r->unparsed_uri + len, NULL);
270                 r->handler = "proxy-server";
271                 r->proxyreq = PROXYREQ_REVERSE;
272                 r->uri = r->unparsed_uri;
273                 break;
274             }
275         }
276 #endif
277     }
278     return DECLINED;
279 }
280
281 static int proxy_trans(request_rec *r)
282 {
283 #ifndef FIX_15207
284     void *sconf = r->server->module_config;
285     proxy_server_conf *conf =
286     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
287     int i, len;
288     struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
289 #endif
290
291     if (r->proxyreq) {
292         /* someone has already set up the proxy, it was possibly ourselves
293          * in proxy_detect
294          */
295         return OK;
296     }
297
298 #ifndef FIX_15207
299     /* XXX: since r->uri has been manipulated already we're not really
300      * compliant with RFC1945 at this point.  But this probably isn't
301      * an issue because this is a hybrid proxy/origin server.
302      */
303
304     for (i = 0; i < conf->aliases->nelts; i++) {
305         len = alias_match(r->uri, ent[i].fake);
306
307        if (len > 0) {
308            if ((ent[i].real[0] == '!') && (ent[i].real[1] == 0)) {
309                return DECLINED;
310            }
311
312            r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real,
313                                      r->uri + len, NULL);
314            r->handler = "proxy-server";
315            r->proxyreq = PROXYREQ_REVERSE;
316            return OK;
317        }
318     }
319 #endif
320     return DECLINED;
321 }
322
323 static int proxy_walk(request_rec *r)
324 {
325     proxy_server_conf *sconf = ap_get_module_config(r->server->module_config,
326                                                     &proxy_module);
327     ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
328     ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts;
329     ap_conf_vector_t *entry_config;
330     proxy_dir_conf *entry_proxy;
331     int num_sec = sconf->sec_proxy->nelts;
332     /* XXX: shouldn't we use URI here?  Canonicalize it first?
333      * Pass over "proxy:" prefix 
334      */
335     const char *proxyname = r->filename + 6;
336     int j;
337
338     for (j = 0; j < num_sec; ++j) 
339     {
340         entry_config = sec_proxy[j];
341         entry_proxy = ap_get_module_config(entry_config, &proxy_module);
342
343         /* XXX: What about case insensitive matching ???
344          * Compare regex, fnmatch or string as appropriate
345          * If the entry doesn't relate, then continue 
346          */
347         if (entry_proxy->r 
348               ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0)
349               : (entry_proxy->p_is_fnmatch
350                    ? apr_fnmatch(entry_proxy->p, proxyname, 0)
351                    : strncmp(proxyname, entry_proxy->p, 
352                                         strlen(entry_proxy->p)))) {
353             continue;
354         }
355         per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults,
356                                                              entry_config);
357     }
358
359     r->per_dir_config = per_dir_defaults;
360
361     return OK;
362 }
363
364 static int proxy_map_location(request_rec *r)
365 {
366     int access_status;
367
368     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
369         return DECLINED;
370
371     /* Don't let the core or mod_http map_to_storage hooks handle this,
372      * We don't need directory/file_walk, and we want to TRACE on our own.
373      */
374     if ((access_status = proxy_walk(r))) {
375         ap_die(access_status, r);
376         return access_status;
377     }
378
379     return OK;
380 }
381 #ifndef FIX_15207
382 /* -------------------------------------------------------------- */
383 /* Fixup the filename */
384
385 /*
386  * Canonicalise the URL
387  */
388 static int proxy_fixup(request_rec *r)
389 {
390     char *url, *p;
391     int access_status;
392
393     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
394         return DECLINED;
395
396 #ifdef FIX_15207
397 /* We definitely shouldn't canonicalize a proxy_pass.
398  * But should we really canonicalize a STD_PROXY??? -- Fahree
399  */
400     if (r->proxyreq == PROXYREQ_REVERSE) {
401         return OK;
402     }
403 #endif
404
405     /* XXX: Shouldn't we try this before we run the proxy_walk? */
406     url = &r->filename[6];
407
408     /* canonicalise each specific scheme */
409     if ((access_status = proxy_run_canon_handler(r, url))) {
410         return access_status;
411     }
412
413     p = strchr(url, ':');
414     if (p == NULL || p == url)
415         return HTTP_BAD_REQUEST;
416
417     return OK;          /* otherwise; we've done the best we can */
418 }
419 #endif
420 /* Send a redirection if the request contains a hostname which is not */
421 /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
422 /* servers like Netscape's allow this and access hosts from the local */
423 /* domain in this case. I think it is better to redirect to a FQDN, since */
424 /* these will later be found in the bookmarks files. */
425 /* The "ProxyDomain" directive determines what domain will be appended */
426 static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
427 {
428     char *nuri;
429     const char *ref;
430
431     /* We only want to worry about GETs */
432     if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
433         return DECLINED;
434
435     /* If host does contain a dot already, or it is "localhost", decline */
436     if (strchr(r->parsed_uri.hostname, '.') != NULL
437      || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
438         return DECLINED;        /* host name has a dot already */
439
440     ref = apr_table_get(r->headers_in, "Referer");
441
442     /* Reassemble the request, but insert the domain after the host name */
443     /* Note that the domain name always starts with a dot */
444     r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname,
445                                          domain, NULL);
446     nuri = apr_uri_unparse(r->pool,
447                            &r->parsed_uri,
448                            APR_URI_UNP_REVEALPASSWORD);
449
450     apr_table_set(r->headers_out, "Location", nuri);
451     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
452                   "Domain missing: %s sent to %s%s%s", r->uri,
453                   apr_uri_unparse(r->pool, &r->parsed_uri,
454                                   APR_URI_UNP_OMITUSERINFO),
455                   ref ? " from " : "", ref ? ref : "");
456
457     return HTTP_MOVED_PERMANENTLY;
458 }
459
460 /* -------------------------------------------------------------- */
461 /* Invoke handler */
462
463 static int proxy_handler(request_rec *r)
464 {
465     char *url, *scheme, *p;
466     const char *p2;
467     void *sconf = r->server->module_config;
468     proxy_server_conf *conf = (proxy_server_conf *)
469         ap_get_module_config(sconf, &proxy_module);
470     apr_array_header_t *proxies = conf->proxies;
471     struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
472     int i, rc, access_status;
473     int direct_connect = 0;
474     const char *str;
475     long maxfwd;
476     proxy_balancer *balancer = NULL;
477     proxy_worker *worker = NULL;
478
479     /* is this for us? */
480     if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
481         return DECLINED;
482
483     /* handle max-forwards / OPTIONS / TRACE */
484     if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) {
485         maxfwd = strtol(str, NULL, 10);
486         if (maxfwd < 1) {
487             switch (r->method_number) {
488             case M_TRACE: {
489                 int access_status;
490                 r->proxyreq = PROXYREQ_NONE;
491                 if ((access_status = ap_send_http_trace(r)))
492                     ap_die(access_status, r);
493                 else
494                     ap_finalize_request_protocol(r);
495                 return OK;
496             }
497             case M_OPTIONS: {
498                 int access_status;
499                 r->proxyreq = PROXYREQ_NONE;
500                 if ((access_status = ap_send_http_options(r)))
501                     ap_die(access_status, r);
502                 else
503                     ap_finalize_request_protocol(r);
504                 return OK;
505             }
506             default: {
507                 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
508                                      "Max-Forwards has reached zero - proxy loop?");
509             }
510             }
511         }
512         maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0;
513     }
514     else {
515         /* set configured max-forwards */
516         maxfwd = conf->maxfwd;
517     }
518     apr_table_set(r->headers_in, "Max-Forwards", 
519                   apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0));
520
521     url = r->filename + 6;
522     p = strchr(url, ':');
523     if (p == NULL) {
524         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
525                       "proxy_handler no URL in %s", r->filename);
526         return HTTP_BAD_REQUEST;
527     }
528
529     /* If the host doesn't have a domain name, add one and redirect. */
530     if (conf->domain != NULL) {
531         rc = proxy_needsdomain(r, url, conf->domain);
532         if (ap_is_HTTP_REDIRECT(rc))
533             return HTTP_MOVED_PERMANENTLY;
534     }
535
536     *p = '\0';
537     scheme = apr_pstrdup(r->pool, url);
538     *p = ':';
539
540     /* Check URI's destination host against NoProxy hosts */
541     /* Bypass ProxyRemote server lookup if configured as NoProxy */
542     /* we only know how to handle communication to a proxy via http */
543     /*if (strcasecmp(scheme, "http") == 0) */
544     {
545         int ii;
546         struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
547
548         for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
549             direct_connect = list[ii].matcher(&list[ii], r);
550         }
551 #if DEBUGGING
552         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
553                       (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
554                       r->uri);
555 #endif
556     }
557     
558     /* Try to obtain the most suitable worker */
559     access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
560     if (access_status != OK)
561         return access_status;
562     
563     /* firstly, try a proxy, unless a NoProxy directive is active */
564     if (!direct_connect) {
565         for (i = 0; i < proxies->nelts; i++) {
566             p2 = ap_strchr_c(ents[i].scheme, ':');  /* is it a partial URL? */
567             if (strcmp(ents[i].scheme, "*") == 0 ||
568                 (ents[i].use_regex && ap_regexec(ents[i].regexp, url, 0,NULL, 0)) ||
569                 (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
570                 (p2 != NULL &&
571                  strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
572
573                 /* handle the scheme */
574                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
575                              "Trying to run scheme_handler against proxy");
576                 access_status = proxy_run_scheme_handler(r, conf, url, ents[i].hostname, ents[i].port);
577
578                 /* an error or success */
579                 if (access_status != DECLINED && access_status != HTTP_BAD_GATEWAY) {
580                     return access_status;
581                 }
582                 /* we failed to talk to the upstream proxy */
583             }
584         }
585     }
586
587     /* otherwise, try it direct */
588     /* N.B. what if we're behind a firewall, where we must use a proxy or
589      * give up??
590      */
591
592     /* handle the scheme */
593     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
594                  "Trying to run scheme_handler");
595     access_status = proxy_run_scheme_handler(r, conf, url, NULL, 0);
596     if (DECLINED == access_status) {
597         ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
598                     "proxy: No protocol handler was valid for the URL %s. "
599                     "If you are using a DSO version of mod_proxy, make sure "
600                     "the proxy submodules are included in the configuration "
601                     "using LoadModule.", r->uri);
602         return HTTP_FORBIDDEN;
603     }
604     if (balancer) {
605         access_status = proxy_run_post_request(worker, balancer, r, conf);
606         if (access_status == DECLINED) {
607             access_status = OK; /* no post_request handler available */
608             /* TODO: reclycle direct worker */
609         }
610     }
611     return access_status;
612 }
613
614 /* -------------------------------------------------------------- */
615 /* Setup configurable data */
616
617 static void * create_proxy_config(apr_pool_t *p, server_rec *s)
618 {
619     proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
620
621     ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *));
622     ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote));
623     ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
624     ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
625     ps->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias));
626     ps->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias));
627     ps->cookie_path_str = apr_strmatch_precompile(p, "path=", 0);
628     ps->cookie_domain_str = apr_strmatch_precompile(p, "domain=", 0);
629     ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry));
630     ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry));
631     ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int));
632     ps->workers = apr_array_make(p, 10, sizeof(proxy_worker));
633     ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer));
634     ps->domain = NULL;
635     ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
636     ps->viaopt_set = 0; /* 0 means default */
637     ps->req = 0;
638     ps->req_set = 0;
639     ps->recv_buffer_size = 0; /* this default was left unset for some reason */
640     ps->recv_buffer_size_set = 0;
641     ps->io_buffer_size = AP_IOBUFSIZE;
642     ps->io_buffer_size_set = 0;
643     ps->maxfwd = DEFAULT_MAX_FORWARDS;
644     ps->maxfwd_set = 0;
645     ps->error_override = 0; 
646     ps->error_override_set = 0; 
647     ps->preserve_host_set = 0;
648     ps->preserve_host = 0;    
649     ps->timeout = 0;
650     ps->timeout_set = 0;
651     ps->badopt = bad_error;
652     ps->badopt_set = 0;
653     return ps;
654 }
655
656 static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
657 {
658     proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
659     proxy_server_conf *base = (proxy_server_conf *) basev;
660     proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
661
662     ps->proxies = apr_array_append(p, base->proxies, overrides->proxies);
663     ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
664     ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
665     ps->raliases = apr_array_append(p, base->raliases, overrides->raliases);
666     ps->cookie_paths
667         = apr_array_append(p, base->cookie_paths, overrides->cookie_paths);
668     ps->cookie_domains
669         = apr_array_append(p, base->cookie_domains, overrides->cookie_domains);
670     ps->cookie_path_str = base->cookie_path_str;
671     ps->cookie_domain_str = base->cookie_domain_str;
672     ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
673     ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
674     ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
675     ps->workers = apr_array_append(p, base->workers, overrides->workers);
676     ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
677
678     ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
679     ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
680     ps->req = (overrides->req_set == 0) ? base->req : overrides->req;
681     ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size;
682     ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size;
683     ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd;
684     ps->error_override = (overrides->error_override_set == 0) ? base->error_override : overrides->error_override;
685     ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host;
686     ps->timeout= (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
687     ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
688
689     return ps;
690 }
691
692 static void *create_proxy_dir_config(apr_pool_t *p, char *dummy)
693 {
694     proxy_dir_conf *new =
695         (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
696
697     /* Filled in by proxysection, when applicable */
698
699     return (void *) new;
700 }
701
702 static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
703 {
704     proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
705     proxy_dir_conf *add = (proxy_dir_conf *) addv;
706
707     new->p = add->p;
708     new->p_is_fnmatch = add->p_is_fnmatch;
709     new->r = add->r;
710     return new;
711 }
712
713
714 static const char *
715     add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex)
716 {
717     server_rec *s = cmd->server;
718     proxy_server_conf *conf =
719     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
720     struct proxy_remote *new;
721     char *p, *q;
722     char *r, *f, *scheme;
723     regex_t *reg = NULL;
724     int port;
725
726     r = apr_pstrdup(cmd->pool, r1);
727     scheme = apr_pstrdup(cmd->pool, r1);
728     f = apr_pstrdup(cmd->pool, f1);
729     p = strchr(r, ':');
730     if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') {
731         if (regex)
732             return "ProxyRemoteMatch: Bad syntax for a remote proxy server";
733         else
734             return "ProxyRemote: Bad syntax for a remote proxy server";
735     }
736     else {
737         scheme[p-r] = 0;
738     }
739     q = strchr(p + 3, ':');
740     if (q != NULL) {
741         if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
742             if (regex)
743                 return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)";
744             else
745                 return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
746         }
747         *q = '\0';
748     }
749     else
750         port = -1;
751     *p = '\0';
752     if (regex) {
753         reg = ap_pregcomp(cmd->pool, f, REG_EXTENDED);
754         if (!reg)
755             return "Regular expression for ProxyRemoteMatch could not be compiled.";
756     }
757     else
758         if (strchr(f, ':') == NULL)
759             ap_str_tolower(f);          /* lowercase scheme */
760     ap_str_tolower(p + 3);              /* lowercase hostname */
761
762     if (port == -1) {
763         port = apr_uri_port_of_scheme(scheme);
764     }
765
766     new = apr_array_push(conf->proxies);
767     new->scheme = f;
768     new->protocol = r;
769     new->hostname = p + 3;
770     new->port = port;
771     new->regexp = reg;
772     new->use_regex = regex;
773     return NULL;
774 }
775
776 static const char *
777     add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
778 {
779     return add_proxy(cmd, dummy, f1, r1, 0);
780 }
781
782 static const char *
783     add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
784 {
785     return add_proxy(cmd, dummy, f1, r1, 1);
786 }
787
788 static const char *
789     add_pass(cmd_parms *cmd, void *dummy, const char *arg)
790 {
791     server_rec *s = cmd->server;
792     proxy_server_conf *conf =
793     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
794     struct proxy_alias *new;
795     char *f = cmd->path;
796     char *r = NULL;
797     char *word;
798     apr_table_t *params = apr_table_make(cmd->pool, 5);
799     const apr_array_header_t *arr;
800     const apr_table_entry_t *elts;
801     int i;
802     
803     while (*arg) {
804         word = ap_getword_conf(cmd->pool, &arg);
805         if (!f)
806             f = word;
807         else if (!r)
808             r = word;
809         else {
810             char *val = strchr(word, '=');
811             if (!val) {
812                 if (cmd->path)
813                     return "Invalid ProxyPass parameter. Paramet must be in the form key=value";
814                 else
815                     return "ProxyPass can not have a path when defined in a location"; 
816             }
817             else
818                 *val++ = '\0';
819             apr_table_setn(params, word, val);
820         }
821     };
822
823     if (r == NULL)
824         return "ProxyPass needs a path when not defined in a location";
825
826     new = apr_array_push(conf->aliases);
827     new->fake = apr_pstrdup(cmd->pool, f);
828     new->real = apr_pstrdup(cmd->pool, r);
829     
830     arr = apr_table_elts(params);
831     elts = (const apr_table_entry_t *)arr->elts;
832     /* Distinguish the balancer from woker */
833     if (strncasecmp(r, "balancer:", 9) == 0) {
834         proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
835         if (!balancer) {
836             const char *err = ap_proxy_add_balancer(&balancer,
837                                                     cmd->pool,
838                                                     conf, r);
839             if (err)
840                 return apr_pstrcat(cmd->temp_pool, "BalancerMember: ", err, NULL);
841         }        
842         for (i = 0; i < arr->nelts; i++) {
843             const char *err = set_balancer_param(balancer, elts[i].key, elts[i].val);
844             if (err)
845                 return apr_pstrcat(cmd->temp_pool, "ProxyPass: ", err, NULL);
846         }
847     }
848     else {
849         proxy_worker *worker = ap_proxy_get_worker(cmd->pool, conf, r);
850         if (!worker) {
851             const char *err = ap_proxy_add_worker(&worker, cmd->pool, conf, r);
852             if (err)
853                 return apr_pstrcat(cmd->temp_pool, "ProxyPass: ", err, NULL);
854         }
855         if (conf->timeout_set)
856             worker->timeout = conf->timeout;
857         for (i = 0; i < arr->nelts; i++) {
858             const char *err = set_worker_param(worker, elts[i].key, elts[i].val);
859             if (err)
860                 return apr_pstrcat(cmd->temp_pool, "ProxyPass: ", err, NULL);
861         }
862     }
863     return NULL;
864 }
865
866 static const char *
867     add_pass_reverse(cmd_parms *cmd, void *dummy, const char *f, const char *r)
868 {
869     server_rec *s = cmd->server;
870     proxy_server_conf *conf;
871     struct proxy_alias *new;
872
873     conf = (proxy_server_conf *)ap_get_module_config(s->module_config, 
874                                                      &proxy_module);
875     if (r!=NULL && cmd->path == NULL ) {
876         new = apr_array_push(conf->raliases);
877         new->fake = f;
878         new->real = r;
879     } else if (r==NULL && cmd->path != NULL) {
880         new = apr_array_push(conf->raliases);
881         new->fake = cmd->path;
882         new->real = f;
883     } else {
884         if ( r == NULL)
885             return "ProxyPassReverse needs a path when not defined in a location";
886         else 
887             return "ProxyPassReverse can not have a path when defined in a location";
888     }
889
890     return NULL;
891 }
892 static const char*
893     cookie_path(cmd_parms *cmd, void *dummy, const char *f, const char *r)
894 {
895     server_rec *s = cmd->server;
896     proxy_server_conf *conf;
897     struct proxy_alias *new;
898
899     conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
900                                                      &proxy_module);
901     new = apr_array_push(conf->cookie_paths);
902     new->fake = f;
903     new->real = r;
904
905     return NULL;
906 }
907 static const char*
908     cookie_domain(cmd_parms *cmd, void *dummy, const char *f, const char *r)
909 {
910     server_rec *s = cmd->server;
911     proxy_server_conf *conf;
912     struct proxy_alias *new;
913
914     conf = (proxy_server_conf *)ap_get_module_config(s->module_config,
915                                                      &proxy_module);
916     new = apr_array_push(conf->cookie_domains);
917     new->fake = f;
918     new->real = r;
919
920     return NULL;
921 }
922
923 static const char *
924     set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg)
925 {
926     server_rec *s = parms->server;
927     proxy_server_conf *conf =
928     ap_get_module_config(s->module_config, &proxy_module);
929     struct noproxy_entry *new;
930     struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
931     struct apr_sockaddr_t *addr;
932     int found = 0;
933     int i;
934
935     /* Don't duplicate entries */
936     for (i = 0; i < conf->noproxies->nelts; i++) {
937         if (apr_strnatcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */
938             found = 1;
939         }
940     }
941
942     if (!found) {
943         new = apr_array_push(conf->noproxies);
944         new->name = arg;
945         if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) {
946             new->addr = addr;
947         }
948         else {
949             new->addr = NULL;
950         }
951     }
952     return NULL;
953 }
954
955 /*
956  * Set the ports CONNECT can use
957  */
958 static const char *
959     set_allowed_ports(cmd_parms *parms, void *dummy, const char *arg)
960 {
961     server_rec *s = parms->server;
962     proxy_server_conf *conf =
963         ap_get_module_config(s->module_config, &proxy_module);
964     int *New;
965
966     if (!apr_isdigit(arg[0]))
967         return "AllowCONNECT: port number must be numeric";
968
969     New = apr_array_push(conf->allowed_connect_ports);
970     *New = atoi(arg);
971     return NULL;
972 }
973
974 /* Similar to set_proxy_exclude(), but defining directly connected hosts,
975  * which should never be accessed via the configured ProxyRemote servers
976  */
977 static const char *
978     set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg)
979 {
980     server_rec *s = parms->server;
981     proxy_server_conf *conf =
982     ap_get_module_config(s->module_config, &proxy_module);
983     struct dirconn_entry *New;
984     struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
985     int found = 0;
986     int i;
987
988     /* Don't duplicate entries */
989     for (i = 0; i < conf->dirconn->nelts; i++) {
990         if (strcasecmp(arg, list[i].name) == 0)
991             found = 1;
992     }
993
994     if (!found) {
995         New = apr_array_push(conf->dirconn);
996         New->name = apr_pstrdup(parms->pool, arg);
997         New->hostaddr = NULL;
998
999         if (ap_proxy_is_ipaddr(New, parms->pool)) {
1000 #if DEBUGGING
1001             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1002                          "Parsed addr %s", inet_ntoa(New->addr));
1003             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1004                          "Parsed mask %s", inet_ntoa(New->mask));
1005 #endif
1006         }
1007         else if (ap_proxy_is_domainname(New, parms->pool)) {
1008             ap_str_tolower(New->name);
1009 #if DEBUGGING
1010             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1011                          "Parsed domain %s", New->name);
1012 #endif
1013         }
1014         else if (ap_proxy_is_hostname(New, parms->pool)) {
1015             ap_str_tolower(New->name);
1016 #if DEBUGGING
1017             ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1018                          "Parsed host %s", New->name);
1019 #endif
1020         }
1021         else {
1022             ap_proxy_is_word(New, parms->pool);
1023 #if DEBUGGING
1024             fprintf(stderr, "Parsed word %s\n", New->name);
1025 #endif
1026         }
1027     }
1028     return NULL;
1029 }
1030
1031 static const char *
1032     set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg)
1033 {
1034     proxy_server_conf *psf =
1035     ap_get_module_config(parms->server->module_config, &proxy_module);
1036
1037     if (arg[0] != '.')
1038         return "ProxyDomain: domain name must start with a dot.";
1039
1040     psf->domain = arg;
1041     return NULL;
1042 }
1043
1044 static const char *
1045     set_proxy_req(cmd_parms *parms, void *dummy, int flag)
1046 {
1047     proxy_server_conf *psf =
1048     ap_get_module_config(parms->server->module_config, &proxy_module);
1049
1050     psf->req = flag;
1051     psf->req_set = 1;
1052     return NULL;
1053 }
1054 static const char *
1055     set_proxy_error_override(cmd_parms *parms, void *dummy, int flag)
1056 {
1057     proxy_server_conf *psf =
1058     ap_get_module_config(parms->server->module_config, &proxy_module);
1059
1060     psf->error_override = flag;
1061     psf->error_override_set = 1;
1062     return NULL;
1063 }
1064 static const char *
1065     set_preserve_host(cmd_parms *parms, void *dummy, int flag)
1066 {
1067     proxy_server_conf *psf =
1068     ap_get_module_config(parms->server->module_config, &proxy_module);
1069
1070     psf->preserve_host = flag;
1071     psf->preserve_host_set = 1;
1072     return NULL;
1073 }
1074
1075 static const char *
1076     set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1077 {
1078     proxy_server_conf *psf =
1079     ap_get_module_config(parms->server->module_config, &proxy_module);
1080     int s = atoi(arg);
1081     if (s < 512 && s != 0) {
1082         return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
1083     }
1084
1085     psf->recv_buffer_size = s;
1086     psf->recv_buffer_size_set = 1;
1087     return NULL;
1088 }
1089
1090 static const char *
1091     set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1092 {
1093     proxy_server_conf *psf =
1094     ap_get_module_config(parms->server->module_config, &proxy_module);
1095     long s = atol(arg);
1096
1097     psf->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE);
1098     psf->io_buffer_size_set = 1;
1099     return NULL;
1100 }
1101
1102 static const char *
1103     set_max_forwards(cmd_parms *parms, void *dummy, const char *arg)
1104 {
1105     proxy_server_conf *psf =
1106     ap_get_module_config(parms->server->module_config, &proxy_module);
1107     long s = atol(arg);
1108     if (s < 0) {
1109         return "ProxyMaxForwards must be greater or equal to zero..";
1110     }
1111
1112     psf->maxfwd = s;
1113     psf->maxfwd_set = 1;
1114     return NULL;
1115 }
1116 static const char*
1117     set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg)
1118 {
1119     proxy_server_conf *psf =
1120     ap_get_module_config(parms->server->module_config, &proxy_module);
1121     int timeout;
1122
1123     timeout=atoi(arg);
1124     if (timeout<1) {
1125         return "Proxy Timeout must be at least 1 second.";
1126     }
1127     psf->timeout_set=1;
1128     psf->timeout=apr_time_from_sec(timeout);
1129
1130     return NULL;    
1131 }
1132
1133 static const char*
1134     set_via_opt(cmd_parms *parms, void *dummy, const char *arg)
1135 {
1136     proxy_server_conf *psf =
1137     ap_get_module_config(parms->server->module_config, &proxy_module);
1138
1139     if (strcasecmp(arg, "Off") == 0)
1140         psf->viaopt = via_off;
1141     else if (strcasecmp(arg, "On") == 0)
1142         psf->viaopt = via_on;
1143     else if (strcasecmp(arg, "Block") == 0)
1144         psf->viaopt = via_block;
1145     else if (strcasecmp(arg, "Full") == 0)
1146         psf->viaopt = via_full;
1147     else {
1148         return "ProxyVia must be one of: "
1149             "off | on | full | block";
1150     }
1151
1152     psf->viaopt_set = 1;
1153     return NULL;    
1154 }
1155
1156 static const char*
1157     set_bad_opt(cmd_parms *parms, void *dummy, const char *arg)
1158 {
1159     proxy_server_conf *psf =
1160     ap_get_module_config(parms->server->module_config, &proxy_module);
1161
1162     if (strcasecmp(arg, "IsError") == 0)
1163         psf->badopt = bad_error;
1164     else if (strcasecmp(arg, "Ignore") == 0)
1165         psf->badopt = bad_ignore;
1166     else if (strcasecmp(arg, "StartBody") == 0)
1167         psf->badopt = bad_body;
1168     else {
1169         return "ProxyBadHeader must be one of: "
1170             "IsError | Ignore | StartBody";
1171     }
1172
1173     psf->badopt_set = 1;
1174     return NULL;    
1175 }
1176
1177 static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
1178 {
1179     server_rec *s = cmd->server;
1180     proxy_server_conf *conf =
1181     ap_get_module_config(s->module_config, &proxy_module);
1182     proxy_balancer *balancer;
1183     proxy_worker *worker;
1184     char *path = cmd->path;
1185     char *name = NULL;
1186     char *word;
1187     apr_table_t *params = apr_table_make(cmd->pool, 5);
1188     const apr_array_header_t *arr;
1189     const apr_table_entry_t *elts;
1190     int i;
1191     
1192     if (cmd->path)
1193         path = apr_pstrdup(cmd->pool, cmd->path);
1194     while (*arg) {
1195         word = ap_getword_conf(cmd->pool, &arg);
1196         if (!path)
1197             path = word;
1198         else if (!name)
1199             name = word;
1200         else {
1201             char *val = strchr(word, '=');
1202             if (!val)
1203                 if (cmd->path)
1204                     return "BalancerMember can not have a balancer name when defined in a location";
1205                 else
1206                     return "Invalid BalancerMember parameter. Paramet must be in the form key=value";
1207             else
1208                 *val++ = '\0';
1209             apr_table_setn(params, word, val);
1210         }
1211     }
1212     if (!path)
1213         return "BalancerMember must define balancer name when outside <Proxy > section";
1214     if (!name)
1215         return "BalancerMember must define remote proxy server";
1216     
1217     ap_str_tolower(path);       /* lowercase scheme://hostname */
1218     ap_str_tolower(name);       /* lowercase scheme://hostname */
1219
1220     /* Try to find existing worker */
1221     worker = ap_proxy_get_worker(cmd->temp_pool, conf, name);
1222     if (!worker) {
1223         const char *err;
1224         if ((err = ap_proxy_add_worker(&worker, cmd->pool, conf, name)) != NULL)
1225             return apr_pstrcat(cmd->temp_pool, "BalancerMember: ", err, NULL); 
1226     }
1227     if (conf->timeout_set)
1228         worker->timeout = conf->timeout;
1229
1230     arr = apr_table_elts(params);
1231     elts = (const apr_table_entry_t *)arr->elts;
1232     for (i = 0; i < arr->nelts; i++) {
1233         const char *err = set_worker_param(worker, elts[i].key, elts[i].val);
1234         if (err)
1235             return apr_pstrcat(cmd->temp_pool, "BalancerMember: ", err, NULL);
1236     }
1237     /* Try to find the balancer */
1238     balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, name); 
1239     if (!balancer) {
1240         const char *err = ap_proxy_add_balancer(&balancer,
1241                                                 cmd->pool,
1242                                                 conf, path);
1243         if (err)
1244             return apr_pstrcat(cmd->temp_pool, "BalancerMember: ", err, NULL);
1245     }
1246     /* Add the worker to the load balancer */
1247     ap_proxy_add_worker_to_balancer(balancer, worker);
1248
1249     return NULL;
1250 }
1251
1252 static const char *
1253     set_sticky_session(cmd_parms *cmd, void *dummy, const char *f, const char *r)
1254 {
1255     server_rec *s = cmd->server;
1256     proxy_server_conf *conf =
1257     ap_get_module_config(s->module_config, &proxy_module);
1258     proxy_balancer *balancer;
1259     const char *name, *sticky;
1260
1261     if (r != NULL && cmd->path == NULL ) {
1262         name = f;
1263         sticky = r;
1264     } else if (r == NULL && cmd->path != NULL) {
1265         name = cmd->path;
1266         sticky = f;
1267     } else {
1268         if (r == NULL)
1269             return "BalancerStickySession needs a path when not defined in a location";
1270         else 
1271             return "BalancerStickySession can not have a path when defined in a location";
1272     }
1273     /* Try to find the balancer */
1274     balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, name);
1275     if (!balancer)
1276         return apr_pstrcat(cmd->temp_pool, "BalancerStickySession: can not find a load balancer '",
1277                            name, "'", NULL);
1278     if (!strcasecmp(sticky, "nofailover"))
1279         balancer->sticky_force = 1;   
1280     else
1281         balancer->sticky = sticky;
1282     return NULL;
1283 }
1284
1285 static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config)
1286 {
1287     proxy_server_conf *sconf = ap_get_module_config(s->module_config,
1288                                                     &proxy_module);
1289     void **new_space = (void **)apr_array_push(sconf->sec_proxy);
1290     
1291     *new_space = dir_config;
1292 }
1293
1294 static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
1295 {
1296     const char *errmsg;
1297     const char *endp = ap_strrchr_c(arg, '>');
1298     int old_overrides = cmd->override;
1299     char *old_path = cmd->path;
1300     proxy_dir_conf *conf;
1301     ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
1302     regex_t *r = NULL;
1303     const command_rec *thiscmd = cmd->cmd;
1304
1305     const char *err = ap_check_cmd_context(cmd,
1306                                            NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
1307     if (err != NULL) {
1308         return err;
1309     }
1310
1311     if (endp == NULL) {
1312         return apr_pstrcat(cmd->pool, cmd->cmd->name,
1313                            "> directive missing closing '>'", NULL);
1314     }
1315
1316     arg=apr_pstrndup(cmd->pool, arg, endp-arg);
1317
1318     if (!arg) {
1319         if (thiscmd->cmd_data)
1320             return "<ProxyMatch > block must specify a path";
1321         else
1322             return "<Proxy > block must specify a path";
1323     }
1324
1325     cmd->path = ap_getword_conf(cmd->pool, &arg);
1326     cmd->override = OR_ALL|ACCESS_CONF;
1327
1328     if (!strncasecmp(cmd->path, "proxy:", 6))
1329         cmd->path += 6;
1330
1331     /* XXX Ignore case?  What if we proxy a case-insensitive server?!? 
1332      * While we are at it, shouldn't we also canonicalize the entire
1333      * scheme?  See proxy_fixup()
1334      */
1335     if (thiscmd->cmd_data) { /* <ProxyMatch> */
1336         r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
1337         if (!r) {
1338             return "Regex could not be compiled";
1339         }
1340     }
1341     else if (!strcmp(cmd->path, "~")) {
1342         cmd->path = ap_getword_conf(cmd->pool, &arg);
1343         if (!cmd->path)
1344             return "<Proxy ~ > block must specify a path";
1345         if (strncasecmp(cmd->path, "proxy:", 6))
1346             cmd->path += 6;
1347         r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
1348         if (!r) {
1349             return "Regex could not be compiled";
1350         }
1351     }
1352
1353     /* initialize our config and fetch it */
1354     conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
1355                                  &proxy_module, cmd->pool);
1356
1357     errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
1358     if (errmsg != NULL)
1359         return errmsg;
1360
1361     conf->r = r;
1362     conf->p = cmd->path;
1363     conf->p_is_fnmatch = apr_fnmatch_test(conf->p);
1364
1365     ap_add_per_proxy_conf(cmd->server, new_dir_conf);
1366
1367     if (*arg != '\0') {
1368         return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
1369                            "> arguments not (yet) supported.", NULL);
1370     }
1371
1372     cmd->path = old_path;
1373     cmd->override = old_overrides;
1374
1375     return NULL;
1376 }
1377
1378 static const command_rec proxy_cmds[] =
1379 {
1380     AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF, 
1381     "Container for directives affecting resources located in the proxied "
1382     "location"),
1383     AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF,
1384     "Container for directives affecting resources located in the proxied "
1385     "location, in regular expression syntax"),
1386     AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF,
1387      "on if the true proxy requests should be accepted"),
1388     AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF,
1389      "a scheme, partial URL or '*' and a proxy server"),
1390     AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
1391      "a regex pattern and a proxy server"),
1392     AP_INIT_RAW_ARGS("ProxyPass", add_pass, NULL, RSRC_CONF|ACCESS_CONF,
1393      "a virtual path and a URL"),
1394     AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
1395      "a virtual path and a URL for reverse proxy behaviour"),
1396     AP_INIT_TAKE2("ProxyPassReverseCookiePath", cookie_path, NULL,
1397        RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
1398     AP_INIT_TAKE2("ProxyPassReverseCookieDomain", cookie_domain, NULL,
1399        RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
1400     AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
1401      "A list of names, hosts or domains to which the proxy will not connect"),
1402     AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF,
1403      "Receive buffer size for outgoing HTTP and FTP connections in bytes"),
1404     AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF,
1405      "IO buffer size for outgoing HTTP and FTP connections in bytes"),
1406     AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF,
1407      "The maximum number of proxies a request may be forwarded through."),
1408     AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF,
1409      "A list of domains, hosts, or subnets to which the proxy will connect directly"),
1410     AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF,
1411      "The default intranet domain name (in absence of a domain in the URL)"),
1412     AP_INIT_ITERATE("AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF,
1413      "A list of ports which CONNECT may connect to"),
1414     AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF,
1415      "Configure Via: proxy header header to one of: on | off | block | full"),
1416     AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF,
1417      "use our error handling pages instead of the servers' we are proxying"),
1418     AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF,
1419      "on if we should preserve host header while proxying"),
1420     AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF,
1421      "Set the timeout (in seconds) for a proxied connection. "
1422      "This overrides the server timeout"),
1423     AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF,
1424      "How to handle bad header line in response: IsError | Ignore | StartBody"),
1425     AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF,
1426      "A balancer name and scheme with list of params"), 
1427     AP_INIT_TAKE12("BalancerStickySession", set_sticky_session, NULL, RSRC_CONF|ACCESS_CONF,
1428      "A balancer and sticky session name"),
1429     {NULL}
1430 };
1431
1432 static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL;
1433 static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL;
1434
1435 PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
1436 {
1437     /* 
1438      * if c == NULL just check if the optional function was imported
1439      * else run the optional function so ssl filters are inserted
1440      */
1441     if (proxy_ssl_enable) {
1442         return c ? proxy_ssl_enable(c) : 1;
1443     }
1444
1445     return 0;
1446 }
1447
1448 PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
1449 {
1450     if (proxy_ssl_disable) {
1451         return proxy_ssl_disable(c);
1452     }
1453
1454     return 0;
1455 }
1456
1457 static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
1458                              apr_pool_t *ptemp, server_rec *s)
1459 {
1460     proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
1461     proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
1462
1463     return OK;
1464 }
1465
1466 static void register_hooks(apr_pool_t *p)
1467 {
1468     /* fixup before mod_rewrite, so that the proxied url will not
1469      * escaped accidentally by our fixup.
1470      */
1471 #ifndef FIX_15207
1472     static const char * const aszSucc[]={ "mod_rewrite.c", NULL };
1473 #endif
1474
1475     /* handler */
1476     ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
1477     /* filename-to-URI translation */
1478     ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST);
1479     /* walk <Proxy > entries and suppress default TRACE behavior */
1480     ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
1481 #ifndef FIX_15207
1482     /* fixups */
1483     ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST);
1484 #endif
1485     /* post read_request handling */
1486     ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST);
1487     /* post config handling */
1488     ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1489 }
1490
1491 module AP_MODULE_DECLARE_DATA proxy_module =
1492 {
1493     STANDARD20_MODULE_STUFF,
1494     create_proxy_dir_config,    /* create per-directory config structure */
1495     merge_proxy_dir_config,     /* merge per-directory config structures */
1496     create_proxy_config,        /* create per-server config structure */
1497     merge_proxy_config,         /* merge per-server config structures */
1498     proxy_cmds,                 /* command table */
1499     register_hooks
1500 };
1501
1502 APR_HOOK_STRUCT(
1503         APR_HOOK_LINK(scheme_handler)
1504         APR_HOOK_LINK(canon_handler)
1505         APR_HOOK_LINK(pre_request)
1506         APR_HOOK_LINK(post_request)
1507 )
1508
1509 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, 
1510                                      (request_rec *r, proxy_server_conf *conf, 
1511                                      char *url, const char *proxyhost, 
1512                                      apr_port_t proxyport),(r,conf,url,
1513                                      proxyhost,proxyport),DECLINED)
1514 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler, 
1515                                      (request_rec *r, char *url),(r,
1516                                      url),DECLINED)
1517 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, pre_request, (
1518                                       proxy_worker **worker,
1519                                       proxy_balancer **balancer,
1520                                       request_rec *r, 
1521                                       proxy_server_conf *conf,
1522                                       char **url),(worker,balancer,
1523                                       r,conf,url),DECLINED)
1524 APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, post_request,
1525                                       (proxy_worker *worker,
1526                                        proxy_balancer *balancer,
1527                                        request_rec *r,
1528                                        proxy_server_conf *conf),(worker,
1529                                        balancer,r,conf),DECLINED)
1530 APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups,
1531                                     (request_rec *r), (r),
1532                                     OK, DECLINED)