1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  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#include "mod_proxy.h"
18#include "mod_core.h"
19#include "apr_optional.h"
20#include "scoreboard.h"
21#include "mod_status.h"
22#include "proxy_util.h"
23
24#if (MODULE_MAGIC_NUMBER_MAJOR > 20020903)
25#include "mod_ssl.h"
26#else
27APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
28APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
29APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
30APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
31                        (apr_pool_t *, server_rec *,
32                         conn_rec *, request_rec *, char *));
33#endif
34
35#ifndef MAX
36#define MAX(x,y) ((x) >= (y) ? (x) : (y))
37#endif
38
39static const char * const proxy_id = "proxy";
40apr_global_mutex_t *proxy_mutex = NULL;
41
42/*
43 * A Web proxy module. Stages:
44 *
45 *  translate_name: set filename to proxy:<URL>
46 *  map_to_storage: run proxy_walk (rather than directory_walk/file_walk)
47 *                  can't trust directory_walk/file_walk since these are
48 *                  not in our filesystem.  Prevents mod_http from serving
49 *                  the TRACE request we will set aside to handle later.
50 *  fix_ups:        convert the URL stored in the filename to the
51 *                  canonical form.
52 *  handler:        handle proxy requests
53 */
54
55/* -------------------------------------------------------------- */
56/* Translate the URL into a 'filename' */
57
58static const char *set_worker_param(apr_pool_t *p,
59                                    proxy_worker *worker,
60                                    const char *key,
61                                    const char *val)
62{
63
64    int ival;
65    apr_interval_time_t timeout;
66
67    if (!strcasecmp(key, "loadfactor")) {
68        /* Normalized load factor. Used with BalancerMamber,
69         * it is a number between 1 and 100.
70         */
71        worker->s->lbfactor = atoi(val);
72        if (worker->s->lbfactor < 1 || worker->s->lbfactor > 100)
73            return "LoadFactor must be a number between 1..100";
74    }
75    else if (!strcasecmp(key, "retry")) {
76        /* If set it will give the retry timeout for the worker
77         * The default value is 60 seconds, meaning that if
78         * in error state, it will be retried after that timeout.
79         */
80        ival = atoi(val);
81        if (ival < 0)
82            return "Retry must be a positive value";
83        worker->s->retry = apr_time_from_sec(ival);
84        worker->s->retry_set = 1;
85    }
86    else if (!strcasecmp(key, "ttl")) {
87        /* Time in seconds that will destroy all the connections
88         * that exceed the smax
89         */
90        ival = atoi(val);
91        if (ival < 1)
92            return "TTL must be at least one second";
93        worker->s->ttl = apr_time_from_sec(ival);
94    }
95    else if (!strcasecmp(key, "min")) {
96        /* Initial number of connections to remote
97         */
98        ival = atoi(val);
99        if (ival < 0)
100            return "Min must be a positive number";
101        worker->s->min = ival;
102    }
103    else if (!strcasecmp(key, "max")) {
104        /* Maximum number of connections to remote
105         */
106        ival = atoi(val);
107        if (ival < 0)
108            return "Max must be a positive number";
109        worker->s->hmax = ival;
110    }
111    /* XXX: More inteligent naming needed */
112    else if (!strcasecmp(key, "smax")) {
113        /* Maximum number of connections to remote that
114         * will not be destroyed
115         */
116        ival = atoi(val);
117        if (ival < 0)
118            return "Smax must be a positive number";
119        worker->s->smax = ival;
120    }
121    else if (!strcasecmp(key, "acquire")) {
122        /* Acquire timeout in given unit (default is milliseconds).
123         * If set this will be the maximum time to
124         * wait for a free connection.
125         */
126        if (ap_timeout_parameter_parse(val, &timeout, "ms") != APR_SUCCESS)
127            return "Acquire timeout has wrong format";
128        if (timeout < 1000)
129            return "Acquire must be at least one millisecond";
130        worker->s->acquire = timeout;
131        worker->s->acquire_set = 1;
132    }
133    else if (!strcasecmp(key, "timeout")) {
134        /* Connection timeout in seconds.
135         * Defaults to server timeout.
136         */
137        ival = atoi(val);
138        if (ival < 1)
139            return "Timeout must be at least one second";
140        worker->s->timeout = apr_time_from_sec(ival);
141        worker->s->timeout_set = 1;
142    }
143    else if (!strcasecmp(key, "iobuffersize")) {
144        long s = atol(val);
145        if (s < 512 && s) {
146            return "IOBufferSize must be >= 512 bytes, or 0 for system default.";
147        }
148        worker->s->io_buffer_size = (s ? s : AP_IOBUFSIZE);
149        worker->s->io_buffer_size_set = 1;
150    }
151    else if (!strcasecmp(key, "receivebuffersize")) {
152        ival = atoi(val);
153        if (ival < 512 && ival != 0) {
154            return "ReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
155        }
156        worker->s->recv_buffer_size = ival;
157        worker->s->recv_buffer_size_set = 1;
158    }
159    else if (!strcasecmp(key, "keepalive")) {
160        if (!strcasecmp(val, "on"))
161            worker->s->keepalive = 1;
162        else if (!strcasecmp(val, "off"))
163            worker->s->keepalive = 0;
164        else
165            return "KeepAlive must be On|Off";
166        worker->s->keepalive_set = 1;
167    }
168    else if (!strcasecmp(key, "disablereuse")) {
169        if (!strcasecmp(val, "on"))
170            worker->s->disablereuse = 1;
171        else if (!strcasecmp(val, "off"))
172            worker->s->disablereuse = 0;
173        else
174            return "DisableReuse must be On|Off";
175        worker->s->disablereuse_set = 1;
176    }
177    else if (!strcasecmp(key, "route")) {
178        /* Worker route.
179         */
180        if (strlen(val) >= sizeof(worker->s->route))
181            return apr_psprintf(p, "Route length must be < %d characters",
182                    (int)sizeof(worker->s->route));
183        PROXY_STRNCPY(worker->s->route, val);
184    }
185    else if (!strcasecmp(key, "redirect")) {
186        /* Worker redirection route.
187         */
188        if (strlen(val) >= sizeof(worker->s->redirect))
189            return apr_psprintf(p, "Redirect length must be < %d characters",
190                    (int)sizeof(worker->s->redirect));
191        PROXY_STRNCPY(worker->s->redirect, val);
192    }
193    else if (!strcasecmp(key, "status")) {
194        const char *v;
195        int mode = 1;
196        apr_status_t rv;
197        /* Worker status.
198         */
199        for (v = val; *v; v++) {
200            if (*v == '+') {
201                mode = 1;
202                v++;
203            }
204            else if (*v == '-') {
205                mode = 0;
206                v++;
207            }
208            rv = ap_proxy_set_wstatus(*v, mode, worker);
209            if (rv != APR_SUCCESS)
210                return "Unknown status parameter option";
211        }
212    }
213    else if (!strcasecmp(key, "flushpackets")) {
214        if (!strcasecmp(val, "on"))
215            worker->s->flush_packets = flush_on;
216        else if (!strcasecmp(val, "off"))
217            worker->s->flush_packets = flush_off;
218        else if (!strcasecmp(val, "auto"))
219            worker->s->flush_packets = flush_auto;
220        else
221            return "flushpackets must be on|off|auto";
222    }
223    else if (!strcasecmp(key, "flushwait")) {
224        ival = atoi(val);
225        if (ival > 1000 || ival < 0) {
226            return "flushwait must be <= 1000, or 0 for system default of 10 millseconds.";
227        }
228        if (ival == 0)
229            worker->s->flush_wait = PROXY_FLUSH_WAIT;
230        else
231            worker->s->flush_wait = ival * 1000;    /* change to microseconds */
232    }
233    else if (!strcasecmp(key, "ping")) {
234        /* Ping/Pong timeout in given unit (default is second).
235         */
236        if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS)
237            return "Ping/Pong timeout has wrong format";
238        if (timeout < 1000)
239            return "Ping/Pong timeout must be at least one millisecond";
240        worker->s->ping_timeout = timeout;
241        worker->s->ping_timeout_set = 1;
242    }
243    else if (!strcasecmp(key, "lbset")) {
244        ival = atoi(val);
245        if (ival < 0 || ival > 99)
246            return "lbset must be between 0 and 99";
247        worker->s->lbset = ival;
248    }
249    else if (!strcasecmp(key, "connectiontimeout")) {
250        /* Request timeout in given unit (default is second).
251         * Defaults to connection timeout
252         */
253        if (ap_timeout_parameter_parse(val, &timeout, "s") != APR_SUCCESS)
254            return "Connectiontimeout has wrong format";
255        if (timeout < 1000)
256            return "Connectiontimeout must be at least one millisecond.";
257        worker->s->conn_timeout = timeout;
258        worker->s->conn_timeout_set = 1;
259    }
260    else if (!strcasecmp(key, "flusher")) {
261        if (strlen(val) >= sizeof(worker->s->flusher))
262            apr_psprintf(p, "flusher name length must be < %d characters",
263                    (int)sizeof(worker->s->flusher));
264        PROXY_STRNCPY(worker->s->flusher, val);
265    }
266    else {
267        return "unknown Worker parameter";
268    }
269    return NULL;
270}
271
272static const char *set_balancer_param(proxy_server_conf *conf,
273                                      apr_pool_t *p,
274                                      proxy_balancer *balancer,
275                                      const char *key,
276                                      const char *val)
277{
278
279    int ival;
280    if (!strcasecmp(key, "stickysession")) {
281        char *path;
282        /* Balancer sticky session name.
283         * Set to something like JSESSIONID or
284         * PHPSESSIONID, etc..,
285         */
286        if (strlen(val) >= sizeof(balancer->s->sticky_path))
287            apr_psprintf(p, "stickysession length must be < %d characters",
288                    (int)sizeof(balancer->s->sticky_path));
289        PROXY_STRNCPY(balancer->s->sticky_path, val);
290        PROXY_STRNCPY(balancer->s->sticky, val);
291
292        if ((path = strchr((char *)balancer->s->sticky, '|'))) {
293            *path++ = '\0';
294            PROXY_STRNCPY(balancer->s->sticky_path, path);
295        }
296    }
297    else if (!strcasecmp(key, "stickysessionsep")) {
298        /* separator/delimiter for sessionid and route,
299         * normally '.'
300         */
301        if (strlen(val) != 1) {
302            if (!strcasecmp(val, "off"))
303                balancer->s->sticky_separator = 0;
304            else
305                return "stickysessionsep must be a single character or Off";
306        }
307        else
308            balancer->s->sticky_separator = *val;
309    }
310    else if (!strcasecmp(key, "nofailover")) {
311        /* If set to 'on' the session will break
312         * if the worker is in error state or
313         * disabled.
314         */
315        if (!strcasecmp(val, "on"))
316            balancer->s->sticky_force = 1;
317        else if (!strcasecmp(val, "off"))
318            balancer->s->sticky_force = 0;
319        else
320            return "failover must be On|Off";
321    }
322    else if (!strcasecmp(key, "timeout")) {
323        /* Balancer timeout in seconds.
324         * If set this will be the maximum time to
325         * wait for a free worker.
326         * Default is not to wait.
327         */
328        ival = atoi(val);
329        if (ival < 1)
330            return "timeout must be at least one second";
331        balancer->s->timeout = apr_time_from_sec(ival);
332    }
333    else if (!strcasecmp(key, "maxattempts")) {
334        /* Maximum number of failover attempts before
335         * giving up.
336         */
337        ival = atoi(val);
338        if (ival < 0)
339            return "maximum number of attempts must be a positive number";
340        balancer->s->max_attempts = ival;
341        balancer->s->max_attempts_set = 1;
342    }
343    else if (!strcasecmp(key, "lbmethod")) {
344        proxy_balancer_method *provider;
345        if (strlen(val) > (sizeof(balancer->s->lbpname)-1))
346            return "unknown lbmethod";
347        provider = ap_lookup_provider(PROXY_LBMETHOD, val, "0");
348        if (provider) {
349            balancer->lbmethod = provider;
350            if (PROXY_STRNCPY(balancer->s->lbpname, val) == APR_SUCCESS) {
351                return NULL;
352            }
353            else {
354                return "lbmethod name too large";
355            }
356        }
357        return "unknown lbmethod";
358    }
359    else if (!strcasecmp(key, "scolonpathdelim")) {
360        /* If set to 'on' then ';' will also be
361         * used as a session path separator/delim (ala
362         * mod_jk)
363         */
364        if (!strcasecmp(val, "on"))
365            balancer->s->scolonsep = 1;
366        else if (!strcasecmp(val, "off"))
367            balancer->s->scolonsep = 0;
368        else
369            return "scolonpathdelim must be On|Off";
370    }
371    else if (!strcasecmp(key, "failonstatus")) {
372        char *val_split;
373        char *status;
374        char *tok_state;
375
376        val_split = apr_pstrdup(p, val);
377
378        balancer->errstatuses = apr_array_make(p, 1, sizeof(int));
379
380        status = apr_strtok(val_split, ", ", &tok_state);
381        while (status != NULL) {
382            ival = atoi(status);
383            if (ap_is_HTTP_VALID_RESPONSE(ival)) {
384                *(int *)apr_array_push(balancer->errstatuses) = ival;
385            }
386            else {
387                return "failonstatus must be one or more HTTP response codes";
388            }
389            status = apr_strtok(NULL, ", ", &tok_state);
390        }
391
392    }
393    else if (!strcasecmp(key, "failontimeout")) {
394        if (!strcasecmp(val, "on"))
395            balancer->failontimeout = 1;
396        else if (!strcasecmp(val, "off"))
397            balancer->failontimeout = 0;
398        else
399            return "failontimeout must be On|Off";
400    }
401    else if (!strcasecmp(key, "nonce")) {
402        if (!strcasecmp(val, "None")) {
403            *balancer->s->nonce = '\0';
404        }
405        else {
406            if (PROXY_STRNCPY(balancer->s->nonce, val) != APR_SUCCESS) {
407                return "Provided nonce is too large";
408            }
409        }
410    }
411    else if (!strcasecmp(key, "growth")) {
412        ival = atoi(val);
413        if (ival < 1 || ival > 100)   /* arbitrary limit here */
414            return "growth must be between 1 and 100";
415        balancer->growth = ival;
416    }
417    else if (!strcasecmp(key, "forcerecovery")) {
418        if (!strcasecmp(val, "on"))
419            balancer->s->forcerecovery = 1;
420        else if (!strcasecmp(val, "off"))
421            balancer->s->forcerecovery = 0;
422        else
423            return "forcerecovery must be On|Off";
424    }
425    else {
426        return "unknown Balancer parameter";
427    }
428    return NULL;
429}
430
431static int alias_match(const char *uri, const char *alias_fakename)
432{
433    const char *end_fakename = alias_fakename + strlen(alias_fakename);
434    const char *aliasp = alias_fakename, *urip = uri;
435    const char *end_uri = uri + strlen(uri);
436
437    while (aliasp < end_fakename && urip < end_uri) {
438        if (*aliasp == '/') {
439            /* any number of '/' in the alias matches any number in
440             * the supplied URI, but there must be at least one...
441             */
442            if (*urip != '/')
443                return 0;
444
445            while (*aliasp == '/')
446                ++aliasp;
447            while (*urip == '/')
448                ++urip;
449        }
450        else {
451            /* Other characters are compared literally */
452            if (*urip++ != *aliasp++)
453                return 0;
454        }
455    }
456
457    /* fixup badly encoded stuff (e.g. % as last character) */
458    if (aliasp > end_fakename) {
459        aliasp = end_fakename;
460    }
461    if (urip > end_uri) {
462        urip = end_uri;
463    }
464
465   /* We reach the end of the uri before the end of "alias_fakename"
466    * for example uri is "/" and alias_fakename "/examples"
467    */
468   if (urip == end_uri && aliasp != end_fakename) {
469       return 0;
470   }
471
472    /* Check last alias path component matched all the way */
473    if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
474        return 0;
475
476    /* Return number of characters from URI which matched (may be
477     * greater than length of alias, since we may have matched
478     * doubled slashes)
479     */
480
481    return urip - uri;
482}
483
484/* Detect if an absoluteURI should be proxied or not.  Note that we
485 * have to do this during this phase because later phases are
486 * "short-circuiting"... i.e. translate_names will end when the first
487 * module returns OK.  So for example, if the request is something like:
488 *
489 * GET http://othervhost/cgi-bin/printenv HTTP/1.0
490 *
491 * mod_alias will notice the /cgi-bin part and ScriptAlias it and
492 * short-circuit the proxy... just because of the ordering in the
493 * configuration file.
494 */
495static int proxy_detect(request_rec *r)
496{
497    void *sconf = r->server->module_config;
498    proxy_server_conf *conf =
499        (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
500
501    /* Ick... msvc (perhaps others) promotes ternary short results to int */
502
503    if (conf->req && r->parsed_uri.scheme) {
504        /* but it might be something vhosted */
505        if (!(r->parsed_uri.hostname
506              && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))
507              && ap_matches_request_vhost(r, r->parsed_uri.hostname,
508                                          (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port
509                                                       : ap_default_port(r))))) {
510            r->proxyreq = PROXYREQ_PROXY;
511            r->uri = r->unparsed_uri;
512            r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
513            r->handler = "proxy-server";
514        }
515    }
516    /* We need special treatment for CONNECT proxying: it has no scheme part */
517    else if (conf->req && r->method_number == M_CONNECT
518             && r->parsed_uri.hostname
519             && r->parsed_uri.port_str) {
520        r->proxyreq = PROXYREQ_PROXY;
521        r->uri = r->unparsed_uri;
522        r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL);
523        r->handler = "proxy-server";
524    }
525    return DECLINED;
526}
527
528static const char *proxy_interpolate(request_rec *r, const char *str)
529{
530    /* Interpolate an env str in a configuration string
531     * Syntax ${var} --> value_of(var)
532     * Method: replace one var, and recurse on remainder of string
533     * Nothing clever here, and crap like nested vars may do silly things
534     * but we'll at least avoid sending the unwary into a loop
535     */
536    const char *start;
537    const char *end;
538    const char *var;
539    const char *val;
540    const char *firstpart;
541
542    start = ap_strstr_c(str, "${");
543    if (start == NULL) {
544        return str;
545    }
546    end = ap_strchr_c(start+2, '}');
547    if (end == NULL) {
548        return str;
549    }
550    /* OK, this is syntax we want to interpolate.  Is there such a var ? */
551    var = apr_pstrndup(r->pool, start+2, end-(start+2));
552    val = apr_table_get(r->subprocess_env, var);
553    firstpart = apr_pstrndup(r->pool, str, (start-str));
554
555    if (val == NULL) {
556        return apr_pstrcat(r->pool, firstpart,
557                           proxy_interpolate(r, end+1), NULL);
558    }
559    else {
560        return apr_pstrcat(r->pool, firstpart, val,
561                           proxy_interpolate(r, end+1), NULL);
562    }
563}
564static apr_array_header_t *proxy_vars(request_rec *r,
565                                      apr_array_header_t *hdr)
566{
567    int i;
568    apr_array_header_t *ret = apr_array_make(r->pool, hdr->nelts,
569                                             sizeof (struct proxy_alias));
570    struct proxy_alias *old = (struct proxy_alias *) hdr->elts;
571
572    for (i = 0; i < hdr->nelts; ++i) {
573        struct proxy_alias *newcopy = apr_array_push(ret);
574        newcopy->fake = (old[i].flags & PROXYPASS_INTERPOLATE)
575                        ? proxy_interpolate(r, old[i].fake) : old[i].fake;
576        newcopy->real = (old[i].flags & PROXYPASS_INTERPOLATE)
577                        ? proxy_interpolate(r, old[i].real) : old[i].real;
578    }
579    return ret;
580}
581
582PROXY_DECLARE(int) ap_proxy_trans_match(request_rec *r, struct proxy_alias *ent,
583                                        proxy_dir_conf *dconf)
584{
585    int len;
586    const char *fake;
587    const char *real;
588    ap_regmatch_t regm[AP_MAX_REG_MATCH];
589    ap_regmatch_t reg1[AP_MAX_REG_MATCH];
590    char *found = NULL;
591    int mismatch = 0;
592    unsigned int nocanon = ent->flags & PROXYPASS_NOCANON;
593    const char *use_uri = nocanon ? r->unparsed_uri : r->uri;
594
595    if (dconf && (dconf->interpolate_env == 1) && (ent->flags & PROXYPASS_INTERPOLATE)) {
596        fake = proxy_interpolate(r, ent->fake);
597        real = proxy_interpolate(r, ent->real);
598    }
599    else {
600        fake = ent->fake;
601        real = ent->real;
602    }
603    if (ent->regex) {
604        if (!ap_regexec(ent->regex, r->uri, AP_MAX_REG_MATCH, regm, 0)) {
605            if ((real[0] == '!') && (real[1] == '\0')) {
606                return DECLINED;
607            }
608            /* test that we haven't reduced the URI */
609            if (nocanon && ap_regexec(ent->regex, r->unparsed_uri,
610                    AP_MAX_REG_MATCH, reg1, 0)) {
611                mismatch = 1;
612                use_uri = r->uri;
613            }
614            found = ap_pregsub(r->pool, real, use_uri, AP_MAX_REG_MATCH,
615                    (use_uri == r->uri) ? regm : reg1);
616            if (!found) {
617                ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01135)
618                              "Substitution in regular expression failed. "
619                              "Replacement too long?");
620                return HTTP_INTERNAL_SERVER_ERROR;
621            }
622
623            /* Note: The strcmp() below catches cases where there
624             * was no regex substitution. This is so cases like:
625             *
626             *    ProxyPassMatch \.gif balancer://foo
627             *
628             * will work "as expected". The upshot is that the 2
629             * directives below act the exact same way (ie: $1 is implied):
630             *
631             *    ProxyPassMatch ^(/.*\.gif)$ balancer://foo
632             *    ProxyPassMatch ^(/.*\.gif)$ balancer://foo$1
633             *
634             * which may be confusing.
635             */
636            if (strcmp(found, real) != 0) {
637                found = apr_pstrcat(r->pool, "proxy:", found, NULL);
638            }
639            else {
640                found = apr_pstrcat(r->pool, "proxy:", real, use_uri, NULL);
641            }
642        }
643    }
644    else {
645        len = alias_match(r->uri, fake);
646
647        if (len != 0) {
648            if ((real[0] == '!') && (real[1] == '\0')) {
649                return DECLINED;
650            }
651            if (nocanon && len != alias_match(r->unparsed_uri, ent->fake)) {
652                mismatch = 1;
653                use_uri = r->uri;
654            }
655            found = apr_pstrcat(r->pool, "proxy:", real, use_uri + len, NULL);
656        }
657    }
658    if (mismatch) {
659        /* We made a reducing transformation, so we can't safely use
660         * unparsed_uri.  Safe fallback is to ignore nocanon.
661         */
662        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01136)
663                "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon");
664    }
665
666    if (found) {
667        r->filename = found;
668        r->handler = "proxy-server";
669        r->proxyreq = PROXYREQ_REVERSE;
670        if (nocanon && !mismatch) {
671            /* mod_proxy_http needs to be told.  Different module. */
672            apr_table_setn(r->notes, "proxy-nocanon", "1");
673        }
674        if (ent->flags & PROXYPASS_NOQUERY) {
675            apr_table_setn(r->notes, "proxy-noquery", "1");
676        }
677        return OK;
678    }
679
680    return DONE;
681}
682
683static int proxy_trans(request_rec *r)
684{
685    int i;
686    struct proxy_alias *ent;
687    proxy_dir_conf *dconf;
688    proxy_server_conf *conf;
689
690    if (r->proxyreq) {
691        /* someone has already set up the proxy, it was possibly ourselves
692         * in proxy_detect
693         */
694        return OK;
695    }
696
697    if ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0')
698        || !r->uri || r->uri[0] != '/') {
699        return DECLINED;
700    }
701
702    /* XXX: since r->uri has been manipulated already we're not really
703     * compliant with RFC1945 at this point.  But this probably isn't
704     * an issue because this is a hybrid proxy/origin server.
705     */
706
707    dconf = ap_get_module_config(r->per_dir_config, &proxy_module);
708
709    /* short way - this location is reverse proxied? */
710    if (dconf->alias) {
711        int rv = ap_proxy_trans_match(r, dconf->alias, dconf);
712        if (DONE != rv) {
713            return rv;
714        }
715    }
716
717    conf = (proxy_server_conf *) ap_get_module_config(r->server->module_config,
718                                                      &proxy_module);
719
720    /* long way - walk the list of aliases, find a match */
721    if (conf->aliases->nelts) {
722        ent = (struct proxy_alias *) conf->aliases->elts;
723        for (i = 0; i < conf->aliases->nelts; i++) {
724            int rv = ap_proxy_trans_match(r, &ent[i], dconf);
725            if (DONE != rv) {
726                return rv;
727            }
728        }
729    }
730    return DECLINED;
731}
732
733static int proxy_walk(request_rec *r)
734{
735    proxy_server_conf *sconf = ap_get_module_config(r->server->module_config,
736                                                    &proxy_module);
737    ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults;
738    ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts;
739    ap_conf_vector_t *entry_config;
740    proxy_dir_conf *entry_proxy;
741    int num_sec = sconf->sec_proxy->nelts;
742    /* XXX: shouldn't we use URI here?  Canonicalize it first?
743     * Pass over "proxy:" prefix
744     */
745    const char *proxyname = r->filename + 6;
746    int j;
747    apr_pool_t *rxpool = NULL;
748
749    for (j = 0; j < num_sec; ++j)
750    {
751        int nmatch = 0;
752        int i;
753        ap_regmatch_t *pmatch = NULL;
754
755        entry_config = sec_proxy[j];
756        entry_proxy = ap_get_module_config(entry_config, &proxy_module);
757
758        if (entry_proxy->r) {
759
760            if (entry_proxy->refs && entry_proxy->refs->nelts) {
761                if (!rxpool) {
762                    apr_pool_create(&rxpool, r->pool);
763                }
764                nmatch = entry_proxy->refs->nelts;
765                pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
766            }
767
768            if (ap_regexec(entry_proxy->r, proxyname, nmatch, pmatch, 0)) {
769                continue;
770            }
771
772            for (i = 0; i < nmatch; i++) {
773                if (pmatch[i].rm_so >= 0 && pmatch[i].rm_eo >= 0 &&
774                        ((const char **)entry_proxy->refs->elts)[i]) {
775                    apr_table_setn(r->subprocess_env,
776                            ((const char **)entry_proxy->refs->elts)[i],
777                            apr_pstrndup(r->pool,
778                                    proxyname + pmatch[i].rm_so,
779                                    pmatch[i].rm_eo - pmatch[i].rm_so));
780                }
781            }
782        }
783
784        else if (
785            /* XXX: What about case insensitive matching ???
786             * Compare regex, fnmatch or string as appropriate
787             * If the entry doesn't relate, then continue
788             */
789            entry_proxy->p_is_fnmatch ? apr_fnmatch(entry_proxy->p,
790                    proxyname, 0) :
791                    strncmp(proxyname, entry_proxy->p,
792                            strlen(entry_proxy->p))) {
793            continue;
794        }
795        per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults,
796                                                             entry_config);
797    }
798
799    r->per_dir_config = per_dir_defaults;
800
801    if (rxpool) {
802        apr_pool_destroy(rxpool);
803    }
804
805    return OK;
806}
807
808static int proxy_map_location(request_rec *r)
809{
810    int access_status;
811
812    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
813        return DECLINED;
814
815    /* Don't let the core or mod_http map_to_storage hooks handle this,
816     * We don't need directory/file_walk, and we want to TRACE on our own.
817     */
818    if ((access_status = proxy_walk(r))) {
819        ap_die(access_status, r);
820        return access_status;
821    }
822
823    return OK;
824}
825
826/* -------------------------------------------------------------- */
827/* Fixup the filename */
828
829/*
830 * Canonicalise the URL
831 */
832static int proxy_fixup(request_rec *r)
833{
834    char *url, *p;
835    int access_status;
836    proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config,
837                                                 &proxy_module);
838
839    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
840        return DECLINED;
841
842    /* XXX: Shouldn't we try this before we run the proxy_walk? */
843    url = &r->filename[6];
844
845    if ((dconf->interpolate_env == 1) && (r->proxyreq == PROXYREQ_REVERSE)) {
846        /* create per-request copy of reverse proxy conf,
847         * and interpolate vars in it
848         */
849        proxy_req_conf *rconf = apr_palloc(r->pool, sizeof(proxy_req_conf));
850        ap_set_module_config(r->request_config, &proxy_module, rconf);
851        rconf->raliases = proxy_vars(r, dconf->raliases);
852        rconf->cookie_paths = proxy_vars(r, dconf->cookie_paths);
853        rconf->cookie_domains = proxy_vars(r, dconf->cookie_domains);
854    }
855
856    /* canonicalise each specific scheme */
857    if ((access_status = proxy_run_canon_handler(r, url))) {
858        return access_status;
859    }
860
861    p = strchr(url, ':');
862    if (p == NULL || p == url)
863        return HTTP_BAD_REQUEST;
864
865    return OK;      /* otherwise; we've done the best we can */
866}
867/* Send a redirection if the request contains a hostname which is not */
868/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
869/* servers like Netscape's allow this and access hosts from the local */
870/* domain in this case. I think it is better to redirect to a FQDN, since */
871/* these will later be found in the bookmarks files. */
872/* The "ProxyDomain" directive determines what domain will be appended */
873static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
874{
875    char *nuri;
876    const char *ref;
877
878    /* We only want to worry about GETs */
879    if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
880        return DECLINED;
881
882    /* If host does contain a dot already, or it is "localhost", decline */
883    if (strchr(r->parsed_uri.hostname, '.') != NULL /* has domain, or IPv4 literal */
884     || strchr(r->parsed_uri.hostname, ':') != NULL /* IPv6 literal */
885     || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
886        return DECLINED;    /* host name has a dot already */
887
888    ref = apr_table_get(r->headers_in, "Referer");
889
890    /* Reassemble the request, but insert the domain after the host name */
891    /* Note that the domain name always starts with a dot */
892    r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname,
893                                         domain, NULL);
894    nuri = apr_uri_unparse(r->pool,
895                           &r->parsed_uri,
896                           APR_URI_UNP_REVEALPASSWORD);
897
898    apr_table_setn(r->headers_out, "Location", nuri);
899    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(01138)
900                  "Domain missing: %s sent to %s%s%s", r->uri,
901                  apr_uri_unparse(r->pool, &r->parsed_uri,
902                                  APR_URI_UNP_OMITUSERINFO),
903                  ref ? " from " : "", ref ? ref : "");
904
905    return HTTP_MOVED_PERMANENTLY;
906}
907
908/* -------------------------------------------------------------- */
909/* Invoke handler */
910
911static int proxy_handler(request_rec *r)
912{
913    char *uri, *scheme, *p;
914    const char *p2;
915    void *sconf = r->server->module_config;
916    proxy_server_conf *conf = (proxy_server_conf *)
917        ap_get_module_config(sconf, &proxy_module);
918    apr_array_header_t *proxies = conf->proxies;
919    struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
920    int i, rc, access_status;
921    int direct_connect = 0;
922    const char *str;
923    apr_int64_t maxfwd;
924    proxy_balancer *balancer = NULL;
925    proxy_worker *worker = NULL;
926    int attempts = 0, max_attempts = 0;
927    struct dirconn_entry *list = (struct dirconn_entry *)conf->dirconn->elts;
928
929    /* is this for us? */
930    if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0)
931        return DECLINED;
932
933    /* handle max-forwards / OPTIONS / TRACE */
934    if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) {
935        char *end;
936        maxfwd = apr_strtoi64(str, &end, 10);
937        if (maxfwd < 0 || maxfwd == APR_INT64_MAX || *end) {
938            return ap_proxyerror(r, HTTP_BAD_REQUEST,
939                    apr_psprintf(r->pool,
940                            "Max-Forwards value '%s' could not be parsed", str));
941        }
942        else if (maxfwd == 0) {
943            switch (r->method_number) {
944            case M_TRACE: {
945                int access_status;
946                r->proxyreq = PROXYREQ_NONE;
947                if ((access_status = ap_send_http_trace(r)))
948                    ap_die(access_status, r);
949                else
950                    ap_finalize_request_protocol(r);
951                return OK;
952            }
953            case M_OPTIONS: {
954                int access_status;
955                r->proxyreq = PROXYREQ_NONE;
956                if ((access_status = ap_send_http_options(r)))
957                    ap_die(access_status, r);
958                else
959                    ap_finalize_request_protocol(r);
960                return OK;
961            }
962            default: {
963                return ap_proxyerror(r, HTTP_BAD_REQUEST,
964                                     "Max-Forwards has reached zero - proxy loop?");
965            }
966            }
967        }
968        maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0;
969    }
970    else {
971        /* set configured max-forwards */
972        maxfwd = conf->maxfwd;
973    }
974    if (maxfwd >= 0) {
975        apr_table_setn(r->headers_in, "Max-Forwards",
976                       apr_psprintf(r->pool, "%" APR_INT64_T_FMT, maxfwd));
977    }
978
979    if (r->method_number == M_TRACE) {
980        core_server_config *coreconf = (core_server_config *)
981                                       ap_get_core_module_config(sconf);
982
983        if (coreconf->trace_enable == AP_TRACE_DISABLE)
984        {
985            /* Allow "error-notes" string to be printed by ap_send_error_response()
986             * Note; this goes nowhere, canned error response need an overhaul.
987             */
988            apr_table_setn(r->notes, "error-notes",
989                           "TRACE forbidden by server configuration");
990            apr_table_setn(r->notes, "verbose-error-to", "*");
991            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01139)
992                          "TRACE forbidden by server configuration");
993            return HTTP_METHOD_NOT_ALLOWED;
994        }
995
996        /* Can't test ap_should_client_block, we aren't ready to send
997         * the client a 100 Continue response till the connection has
998         * been established
999         */
1000        if (coreconf->trace_enable != AP_TRACE_EXTENDED
1001            && (r->read_length || r->read_chunked || r->remaining))
1002        {
1003            /* Allow "error-notes" string to be printed by ap_send_error_response()
1004             * Note; this goes nowhere, canned error response need an overhaul.
1005             */
1006            apr_table_setn(r->notes, "error-notes",
1007                           "TRACE with request body is not allowed");
1008            apr_table_setn(r->notes, "verbose-error-to", "*");
1009            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01140)
1010                          "TRACE with request body is not allowed");
1011            return HTTP_REQUEST_ENTITY_TOO_LARGE;
1012        }
1013    }
1014
1015    uri = r->filename + 6;
1016    p = strchr(uri, ':');
1017    if (p == NULL) {
1018        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01141)
1019                      "proxy_handler no URL in %s", r->filename);
1020        return HTTP_BAD_REQUEST;
1021    }
1022
1023    /* If the host doesn't have a domain name, add one and redirect. */
1024    if (conf->domain != NULL) {
1025        rc = proxy_needsdomain(r, uri, conf->domain);
1026        if (ap_is_HTTP_REDIRECT(rc))
1027            return HTTP_MOVED_PERMANENTLY;
1028    }
1029
1030    scheme = apr_pstrndup(r->pool, uri, p - uri);
1031    /* Check URI's destination host against NoProxy hosts */
1032    /* Bypass ProxyRemote server lookup if configured as NoProxy */
1033    for (direct_connect = i = 0; i < conf->dirconn->nelts &&
1034                                        !direct_connect; i++) {
1035        direct_connect = list[i].matcher(&list[i], r);
1036    }
1037#if DEBUGGING
1038    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1039                (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
1040                r->uri);
1041#endif
1042
1043    do {
1044        char *url = uri;
1045        /* Try to obtain the most suitable worker */
1046        access_status = ap_proxy_pre_request(&worker, &balancer, r, conf, &url);
1047        if (access_status != OK) {
1048            /*
1049             * Only return if access_status is not HTTP_SERVICE_UNAVAILABLE
1050             * This gives other modules the chance to hook into the
1051             * request_status hook and decide what to do in this situation.
1052             */
1053            if (access_status != HTTP_SERVICE_UNAVAILABLE)
1054                return access_status;
1055            /*
1056             * Ensure that balancer is NULL if worker is NULL to prevent
1057             * potential problems in the post_request hook.
1058             */
1059            if (!worker)
1060                balancer = NULL;
1061            goto cleanup;
1062        }
1063
1064        /* Initialise worker if needed, note the shared area must be initialized by the balancer logic */
1065        if (balancer) {
1066            ap_proxy_initialize_worker(worker, r->server, conf->pool);
1067        }
1068
1069        if (balancer && balancer->s->max_attempts_set && !max_attempts)
1070            max_attempts = balancer->s->max_attempts;
1071        /* firstly, try a proxy, unless a NoProxy directive is active */
1072        if (!direct_connect) {
1073            for (i = 0; i < proxies->nelts; i++) {
1074                p2 = ap_strchr_c(ents[i].scheme, ':');  /* is it a partial URL? */
1075                if (strcmp(ents[i].scheme, "*") == 0 ||
1076                    (ents[i].use_regex &&
1077                     ap_regexec(ents[i].regexp, url, 0, NULL, 0) == 0) ||
1078                    (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
1079                    (p2 != NULL &&
1080                    strncasecmp(url, ents[i].scheme,
1081                                strlen(ents[i].scheme)) == 0)) {
1082
1083                    /* handle the scheme */
1084                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01142)
1085                                  "Trying to run scheme_handler against proxy");
1086                    access_status = proxy_run_scheme_handler(r, worker,
1087                                                             conf, url,
1088                                                             ents[i].hostname,
1089                                                             ents[i].port);
1090
1091                    /* Did the scheme handler process the request? */
1092                    if (access_status != DECLINED) {
1093                        const char *cl_a;
1094                        char *end;
1095                        apr_off_t cl;
1096
1097                        /*
1098                         * An fatal error or success, so no point in
1099                         * retrying with a direct connection.
1100                         */
1101                        if (access_status != HTTP_BAD_GATEWAY) {
1102                            goto cleanup;
1103                        }
1104                        cl_a = apr_table_get(r->headers_in, "Content-Length");
1105                        if (cl_a) {
1106                            apr_strtoff(&cl, cl_a, &end, 10);
1107                            /*
1108                             * The request body is of length > 0. We cannot
1109                             * retry with a direct connection since we already
1110                             * sent (parts of) the request body to the proxy
1111                             * and do not have any longer.
1112                             */
1113                            if (cl > 0) {
1114                                goto cleanup;
1115                            }
1116                        }
1117                        /*
1118                         * Transfer-Encoding was set as input header, so we had
1119                         * a request body. We cannot retry with a direct
1120                         * connection for the same reason as above.
1121                         */
1122                        if (apr_table_get(r->headers_in, "Transfer-Encoding")) {
1123                            goto cleanup;
1124                        }
1125                    }
1126                }
1127            }
1128        }
1129
1130        /* otherwise, try it direct */
1131        /* N.B. what if we're behind a firewall, where we must use a proxy or
1132        * give up??
1133        */
1134
1135        /* handle the scheme */
1136        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01143)
1137                      "Running scheme %s handler (attempt %d)",
1138                      scheme, attempts);
1139        AP_PROXY_RUN(r, worker, conf, url, attempts);
1140        access_status = proxy_run_scheme_handler(r, worker, conf,
1141                                                 url, NULL, 0);
1142        if (access_status == OK)
1143            break;
1144        else if (access_status == HTTP_INTERNAL_SERVER_ERROR) {
1145            /* Unrecoverable server error.
1146             * We can not failover to another worker.
1147             * Mark the worker as unusable if member of load balancer
1148             */
1149            if (balancer) {
1150                worker->s->status |= PROXY_WORKER_IN_ERROR;
1151                worker->s->error_time = apr_time_now();
1152            }
1153            break;
1154        }
1155        else if (access_status == HTTP_SERVICE_UNAVAILABLE) {
1156            /* Recoverable server error.
1157             * We can failover to another worker
1158             * Mark the worker as unusable if member of load balancer
1159             */
1160            if (balancer) {
1161                worker->s->status |= PROXY_WORKER_IN_ERROR;
1162                worker->s->error_time = apr_time_now();
1163            }
1164        }
1165        else {
1166            /* Unrecoverable error.
1167             * Return the origin status code to the client.
1168             */
1169            break;
1170        }
1171        /* Try again if the worker is unusable and the service is
1172         * unavailable.
1173         */
1174    } while (!PROXY_WORKER_IS_USABLE(worker) &&
1175             max_attempts > attempts++);
1176
1177    if (DECLINED == access_status) {
1178        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01144)
1179                      "No protocol handler was valid for the URL %s. "
1180                      "If you are using a DSO version of mod_proxy, make sure "
1181                      "the proxy submodules are included in the configuration "
1182                      "using LoadModule.", r->uri);
1183        access_status = HTTP_INTERNAL_SERVER_ERROR;
1184        goto cleanup;
1185    }
1186cleanup:
1187    ap_proxy_post_request(worker, balancer, r, conf);
1188
1189    proxy_run_request_status(&access_status, r);
1190    AP_PROXY_RUN_FINISHED(r, attempts, access_status);
1191
1192    return access_status;
1193}
1194
1195/* -------------------------------------------------------------- */
1196/* Setup configurable data */
1197
1198static void * create_proxy_config(apr_pool_t *p, server_rec *s)
1199{
1200    proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
1201
1202    ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *));
1203    ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote));
1204    ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
1205    ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry));
1206    ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry));
1207    ps->workers = apr_array_make(p, 10, sizeof(proxy_worker));
1208    ps->balancers = apr_array_make(p, 10, sizeof(proxy_balancer));
1209    ps->forward = NULL;
1210    ps->reverse = NULL;
1211    ps->domain = NULL;
1212    ps->id = apr_psprintf(p, "p%x", 1); /* simply for storage size */
1213    ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
1214    ps->viaopt_set = 0; /* 0 means default */
1215    ps->req = 0;
1216    ps->max_balancers = 0;
1217    ps->bal_persist = 0;
1218    ps->inherit = 1;
1219    ps->inherit_set = 0;
1220    ps->ppinherit = 1;
1221    ps->ppinherit_set = 0;
1222    ps->bgrowth = 5;
1223    ps->bgrowth_set = 0;
1224    ps->req_set = 0;
1225    ps->recv_buffer_size = 0; /* this default was left unset for some reason */
1226    ps->recv_buffer_size_set = 0;
1227    ps->io_buffer_size = AP_IOBUFSIZE;
1228    ps->io_buffer_size_set = 0;
1229    ps->maxfwd = DEFAULT_MAX_FORWARDS;
1230    ps->maxfwd_set = 0;
1231    ps->timeout = 0;
1232    ps->timeout_set = 0;
1233    ps->badopt = bad_error;
1234    ps->badopt_set = 0;
1235    ps->source_address = NULL;
1236    ps->source_address_set = 0;
1237    apr_pool_create_ex(&ps->pool, p, NULL, NULL);
1238
1239    return ps;
1240}
1241
1242static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv)
1243{
1244    proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf));
1245    proxy_server_conf *base = (proxy_server_conf *) basev;
1246    proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
1247
1248    ps->inherit = (overrides->inherit_set == 0) ? base->inherit : overrides->inherit;
1249    ps->inherit_set = overrides->inherit_set || base->inherit_set;
1250
1251    ps->ppinherit = (overrides->ppinherit_set == 0) ? base->ppinherit : overrides->ppinherit;
1252    ps->ppinherit_set = overrides->ppinherit_set || base->ppinherit_set;
1253
1254    if (ps->ppinherit) {
1255        ps->proxies = apr_array_append(p, base->proxies, overrides->proxies);
1256    }
1257    else {
1258        ps->proxies = overrides->proxies;
1259    }
1260    ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy);
1261    ps->aliases = apr_array_append(p, base->aliases, overrides->aliases);
1262    ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies);
1263    ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn);
1264    if (ps->inherit || ps->ppinherit) {
1265        ps->workers = apr_array_append(p, base->workers, overrides->workers);
1266        ps->balancers = apr_array_append(p, base->balancers, overrides->balancers);
1267    }
1268    else {
1269        ps->workers = overrides->workers;
1270        ps->balancers = overrides->balancers;
1271    }
1272    ps->forward = overrides->forward ? overrides->forward : base->forward;
1273    ps->reverse = overrides->reverse ? overrides->reverse : base->reverse;
1274
1275    ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
1276    ps->id = (overrides->id == NULL) ? base->id : overrides->id;
1277    ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
1278    ps->viaopt_set = overrides->viaopt_set || base->viaopt_set;
1279    ps->req = (overrides->req_set == 0) ? base->req : overrides->req;
1280    ps->req_set = overrides->req_set || base->req_set;
1281    ps->bgrowth = (overrides->bgrowth_set == 0) ? base->bgrowth : overrides->bgrowth;
1282    ps->bgrowth_set = overrides->bgrowth_set || base->bgrowth_set;
1283    ps->max_balancers = overrides->max_balancers || base->max_balancers;
1284    ps->bal_persist = overrides->bal_persist;
1285    ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size;
1286    ps->recv_buffer_size_set = overrides->recv_buffer_size_set || base->recv_buffer_size_set;
1287    ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size;
1288    ps->io_buffer_size_set = overrides->io_buffer_size_set || base->io_buffer_size_set;
1289    ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd;
1290    ps->maxfwd_set = overrides->maxfwd_set || base->maxfwd_set;
1291    ps->timeout = (overrides->timeout_set == 0) ? base->timeout : overrides->timeout;
1292    ps->timeout_set = overrides->timeout_set || base->timeout_set;
1293    ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt;
1294    ps->badopt_set = overrides->badopt_set || base->badopt_set;
1295    ps->proxy_status = (overrides->proxy_status_set == 0) ? base->proxy_status : overrides->proxy_status;
1296    ps->proxy_status_set = overrides->proxy_status_set || base->proxy_status_set;
1297    ps->source_address = (overrides->source_address_set == 0) ? base->source_address : overrides->source_address;
1298    ps->source_address_set = overrides->source_address_set || base->source_address_set;
1299    ps->pool = base->pool;
1300    return ps;
1301}
1302static const char *set_source_address(cmd_parms *parms, void *dummy,
1303                                      const char *arg)
1304{
1305    proxy_server_conf *psf =
1306        ap_get_module_config(parms->server->module_config, &proxy_module);
1307    struct apr_sockaddr_t *addr;
1308
1309    if (APR_SUCCESS == apr_sockaddr_info_get(&addr, arg, APR_UNSPEC, 0, 0,
1310                                             psf->pool)) {
1311        psf->source_address = addr;
1312        psf->source_address_set = 1;
1313    }
1314    else {
1315        return "ProxySourceAddress invalid value";
1316    }
1317
1318    return NULL;
1319}
1320
1321static void *create_proxy_dir_config(apr_pool_t *p, char *dummy)
1322{
1323    proxy_dir_conf *new =
1324        (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
1325
1326    /* Filled in by proxysection, when applicable */
1327
1328    /* Put these in the dir config so they work inside <Location> */
1329    new->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias));
1330    new->cookie_paths = apr_array_make(p, 10, sizeof(struct proxy_alias));
1331    new->cookie_domains = apr_array_make(p, 10, sizeof(struct proxy_alias));
1332    new->preserve_host_set = 0;
1333    new->preserve_host = 0;
1334    new->interpolate_env = -1; /* unset */
1335    new->error_override = 0;
1336    new->error_override_set = 0;
1337    new->add_forwarded_headers = 1;
1338
1339    return (void *) new;
1340}
1341
1342static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv)
1343{
1344    proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf));
1345    proxy_dir_conf *add = (proxy_dir_conf *) addv;
1346    proxy_dir_conf *base = (proxy_dir_conf *) basev;
1347
1348    new->p = add->p;
1349    new->p_is_fnmatch = add->p_is_fnmatch;
1350    new->r = add->r;
1351    new->refs = add->refs;
1352
1353    /* Put these in the dir config so they work inside <Location> */
1354    new->raliases = apr_array_append(p, base->raliases, add->raliases);
1355    new->cookie_paths
1356        = apr_array_append(p, base->cookie_paths, add->cookie_paths);
1357    new->cookie_domains
1358        = apr_array_append(p, base->cookie_domains, add->cookie_domains);
1359    new->interpolate_env = (add->interpolate_env == -1) ? base->interpolate_env
1360                                                        : add->interpolate_env;
1361    new->preserve_host = (add->preserve_host_set == 0) ? base->preserve_host
1362                                                        : add->preserve_host;
1363    new->preserve_host_set = add->preserve_host_set || base->preserve_host_set;
1364    new->error_override = (add->error_override_set == 0) ? base->error_override
1365                                                        : add->error_override;
1366    new->error_override_set = add->error_override_set || base->error_override_set;
1367    new->alias = (add->alias_set == 0) ? base->alias : add->alias;
1368    new->alias_set = add->alias_set || base->alias_set;
1369    new->add_forwarded_headers = add->add_forwarded_headers;
1370    return new;
1371}
1372
1373static const char *
1374    add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex)
1375{
1376    server_rec *s = cmd->server;
1377    proxy_server_conf *conf =
1378    (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
1379    struct proxy_remote *new;
1380    char *p, *q;
1381    char *r, *f, *scheme;
1382    ap_regex_t *reg = NULL;
1383    int port;
1384
1385    r = apr_pstrdup(cmd->pool, r1);
1386    scheme = apr_pstrdup(cmd->pool, r1);
1387    f = apr_pstrdup(cmd->pool, f1);
1388    p = strchr(r, ':');
1389    if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') {
1390        if (regex)
1391            return "ProxyRemoteMatch: Bad syntax for a remote proxy server";
1392        else
1393            return "ProxyRemote: Bad syntax for a remote proxy server";
1394    }
1395    else {
1396        scheme[p-r] = 0;
1397    }
1398    q = strchr(p + 3, ':');
1399    if (q != NULL) {
1400        if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {
1401            if (regex)
1402                return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)";
1403            else
1404                return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
1405        }
1406        *q = '\0';
1407    }
1408    else
1409        port = -1;
1410    *p = '\0';
1411    if (regex) {
1412        reg = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED);
1413        if (!reg)
1414            return "Regular expression for ProxyRemoteMatch could not be compiled.";
1415    }
1416    else
1417        if (strchr(f, ':') == NULL)
1418            ap_str_tolower(f);      /* lowercase scheme */
1419    ap_str_tolower(p + 3);      /* lowercase hostname */
1420
1421    if (port == -1) {
1422        port = apr_uri_port_of_scheme(scheme);
1423    }
1424
1425    new = apr_array_push(conf->proxies);
1426    new->scheme = f;
1427    new->protocol = r;
1428    new->hostname = p + 3;
1429    new->port = port;
1430    new->regexp = reg;
1431    new->use_regex = regex;
1432    return NULL;
1433}
1434
1435static const char *
1436    add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
1437{
1438    return add_proxy(cmd, dummy, f1, r1, 0);
1439}
1440
1441static const char *
1442    add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1)
1443{
1444    return add_proxy(cmd, dummy, f1, r1, 1);
1445}
1446
1447static char *de_socketfy(apr_pool_t *p, char *url)
1448{
1449    char *ptr;
1450    /*
1451     * We could be passed a URL during the config stage that contains
1452     * the UDS path... ignore it
1453     */
1454    if (!strncasecmp(url, "unix:", 5) &&
1455        ((ptr = ap_strchr(url, '|')) != NULL)) {
1456        /* move past the 'unix:...|' UDS path info */
1457        char *ret, *c;
1458
1459        ret = ptr + 1;
1460        /* special case: "unix:....|scheme:" is OK, expand
1461         * to "unix:....|scheme://localhost"
1462         * */
1463        c = ap_strchr(ret, ':');
1464        if (c == NULL) {
1465            return NULL;
1466        }
1467        if (c[1] == '\0') {
1468            return apr_pstrcat(p, ret, "//localhost", NULL);
1469        }
1470        else {
1471            return ret;
1472        }
1473    }
1474    return url;
1475}
1476
1477static const char *
1478    add_pass(cmd_parms *cmd, void *dummy, const char *arg, int is_regex)
1479{
1480    proxy_dir_conf *dconf = (proxy_dir_conf *)dummy;
1481    server_rec *s = cmd->server;
1482    proxy_server_conf *conf =
1483    (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
1484    struct proxy_alias *new;
1485    char *f = cmd->path;
1486    char *r = NULL;
1487    char *word;
1488    apr_table_t *params = apr_table_make(cmd->pool, 5);
1489    const apr_array_header_t *arr;
1490    const apr_table_entry_t *elts;
1491    int i;
1492    int use_regex = is_regex;
1493    unsigned int flags = 0;
1494    const char *err;
1495
1496    err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES);
1497    if (err) {
1498        return err;
1499    }
1500
1501    while (*arg) {
1502        word = ap_getword_conf(cmd->pool, &arg);
1503        if (!f) {
1504            if (!strcmp(word, "~")) {
1505                if (is_regex) {
1506                    return "ProxyPassMatch invalid syntax ('~' usage).";
1507                }
1508                use_regex = 1;
1509                continue;
1510            }
1511            f = word;
1512        }
1513        else if (!r) {
1514            r = word;
1515        }
1516        else if (!strcasecmp(word,"nocanon")) {
1517            flags |= PROXYPASS_NOCANON;
1518        }
1519        else if (!strcasecmp(word,"interpolate")) {
1520            flags |= PROXYPASS_INTERPOLATE;
1521        }
1522        else if (!strcasecmp(word,"noquery")) {
1523            flags |= PROXYPASS_NOQUERY;
1524        }
1525        else {
1526            char *val = strchr(word, '=');
1527            if (!val) {
1528                if (cmd->path) {
1529                    if (*r == '/') {
1530                        return "ProxyPass|ProxyPassMatch can not have a path when defined in "
1531                               "a location.";
1532                    }
1533                    else {
1534                        return "Invalid ProxyPass|ProxyPassMatch parameter. Parameter must "
1535                               "be in the form 'key=value'.";
1536                    }
1537                }
1538                else {
1539                    return "Invalid ProxyPass|ProxyPassMatch parameter. Parameter must be "
1540                           "in the form 'key=value'.";
1541                }
1542            }
1543            else
1544                *val++ = '\0';
1545            apr_table_setn(params, word, val);
1546        }
1547    };
1548
1549    if (r == NULL) {
1550        return "ProxyPass|ProxyPassMatch needs a path when not defined in a location";
1551    }
1552
1553    /* if per directory, save away the single alias */
1554    if (cmd->path) {
1555        dconf->alias = apr_pcalloc(cmd->pool, sizeof(struct proxy_alias));
1556        dconf->alias_set = 1;
1557        new = dconf->alias;
1558        if (apr_fnmatch_test(f)) {
1559            use_regex = 1;
1560        }
1561    }
1562    /* if per server, add to the alias array */
1563    else {
1564        new = apr_array_push(conf->aliases);
1565    }
1566
1567    new->fake = apr_pstrdup(cmd->pool, f);
1568    new->real = apr_pstrdup(cmd->pool, de_socketfy(cmd->pool, r));
1569    new->flags = flags;
1570    if (use_regex) {
1571        new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED);
1572        if (new->regex == NULL)
1573            return "Regular expression could not be compiled.";
1574    }
1575    else {
1576        new->regex = NULL;
1577    }
1578
1579    if (r[0] == '!' && r[1] == '\0')
1580        return NULL;
1581
1582    arr = apr_table_elts(params);
1583    elts = (const apr_table_entry_t *)arr->elts;
1584    /* Distinguish the balancer from worker */
1585    if (ap_proxy_valid_balancer_name(r, 9)) {
1586        proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r, 0);
1587        if (!balancer) {
1588            const char *err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, r, f, 0);
1589            if (err)
1590                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1591        }
1592        else {
1593            ap_proxy_update_balancer(cmd->pool, balancer, f);
1594        }
1595        for (i = 0; i < arr->nelts; i++) {
1596            const char *err = set_balancer_param(conf, cmd->pool, balancer, elts[i].key,
1597                                                 elts[i].val);
1598            if (err)
1599                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1600        }
1601        new->balancer = balancer;
1602    }
1603    else {
1604        proxy_worker *worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, de_socketfy(cmd->pool, r));
1605        int reuse = 0;
1606        if (!worker) {
1607            const char *err = ap_proxy_define_worker(cmd->pool, &worker, NULL, conf, r, 0);
1608            if (err)
1609                return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1610
1611            PROXY_COPY_CONF_PARAMS(worker, conf);
1612        } else {
1613            reuse = 1;
1614            ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, APLOGNO(01145)
1615                         "Sharing worker '%s' instead of creating new worker '%s'",
1616                         ap_proxy_worker_name(cmd->pool, worker), new->real);
1617        }
1618
1619        for (i = 0; i < arr->nelts; i++) {
1620            if (reuse) {
1621                ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(01146)
1622                             "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing",
1623                             elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker));
1624            } else {
1625                const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
1626                                                   elts[i].val);
1627                if (err)
1628                    return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
1629            }
1630        }
1631    }
1632    return NULL;
1633}
1634
1635static const char *
1636    add_pass_noregex(cmd_parms *cmd, void *dummy, const char *arg)
1637{
1638    return add_pass(cmd, dummy, arg, 0);
1639}
1640
1641static const char *
1642    add_pass_regex(cmd_parms *cmd, void *dummy, const char *arg)
1643{
1644    return add_pass(cmd, dummy, arg, 1);
1645}
1646
1647
1648static const char * add_pass_reverse(cmd_parms *cmd, void *dconf, const char *f,
1649                                     const char *r, const char *i)
1650{
1651    proxy_dir_conf *conf = dconf;
1652    struct proxy_alias *new;
1653    const char *fake;
1654    const char *real;
1655    const char *interp;
1656    const char *err;
1657
1658    err = ap_check_cmd_context(cmd, NOT_IN_DIRECTORY|NOT_IN_FILES);
1659    if (err) {
1660        return err;
1661    }
1662
1663    if (cmd->path == NULL) {
1664        if (r == NULL || !strcasecmp(r, "interpolate")) {
1665            return "ProxyPassReverse needs a path when not defined in a location";
1666        }
1667        fake = f;
1668        real = r;
1669        interp = i;
1670    }
1671    else {
1672        if (r && strcasecmp(r, "interpolate")) {
1673            return "ProxyPassReverse can not have a path when defined in a location";
1674        }
1675        fake = cmd->path;
1676        real = f;
1677        interp = r;
1678    }
1679
1680    new = apr_array_push(conf->raliases);
1681    new->fake = fake;
1682    new->real = real;
1683    new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
1684
1685    return NULL;
1686}
1687static const char* cookie_path(cmd_parms *cmd, void *dconf, const char *f,
1688                               const char *r, const char *interp)
1689{
1690    proxy_dir_conf *conf = dconf;
1691    struct proxy_alias *new;
1692
1693    new = apr_array_push(conf->cookie_paths);
1694    new->fake = f;
1695    new->real = r;
1696    new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
1697
1698    return NULL;
1699}
1700static const char* cookie_domain(cmd_parms *cmd, void *dconf, const char *f,
1701                                 const char *r, const char *interp)
1702{
1703    proxy_dir_conf *conf = dconf;
1704    struct proxy_alias *new;
1705
1706    new = apr_array_push(conf->cookie_domains);
1707    new->fake = f;
1708    new->real = r;
1709    new->flags = interp ? PROXYPASS_INTERPOLATE : 0;
1710    return NULL;
1711}
1712
1713static const char *
1714    set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg)
1715{
1716    server_rec *s = parms->server;
1717    proxy_server_conf *conf =
1718    ap_get_module_config(s->module_config, &proxy_module);
1719    struct noproxy_entry *new;
1720    struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
1721    struct apr_sockaddr_t *addr;
1722    int found = 0;
1723    int i;
1724
1725    /* Don't duplicate entries */
1726    for (i = 0; i < conf->noproxies->nelts; i++) {
1727        if (strcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */
1728            found = 1;
1729            break;
1730        }
1731    }
1732
1733    if (!found) {
1734        new = apr_array_push(conf->noproxies);
1735        new->name = arg;
1736        if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) {
1737            new->addr = addr;
1738        }
1739        else {
1740            new->addr = NULL;
1741        }
1742    }
1743    return NULL;
1744}
1745
1746
1747/* Similar to set_proxy_exclude(), but defining directly connected hosts,
1748 * which should never be accessed via the configured ProxyRemote servers
1749 */
1750static const char *
1751    set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg)
1752{
1753    server_rec *s = parms->server;
1754    proxy_server_conf *conf =
1755    ap_get_module_config(s->module_config, &proxy_module);
1756    struct dirconn_entry *New;
1757    struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
1758    int found = 0;
1759    int i;
1760
1761    /* Don't duplicate entries */
1762    for (i = 0; i < conf->dirconn->nelts; i++) {
1763        if (strcasecmp(arg, list[i].name) == 0) {
1764            found = 1;
1765            break;
1766        }
1767    }
1768
1769    if (!found) {
1770        New = apr_array_push(conf->dirconn);
1771        New->name = apr_pstrdup(parms->pool, arg);
1772        New->hostaddr = NULL;
1773
1774        if (ap_proxy_is_ipaddr(New, parms->pool)) {
1775#if DEBUGGING
1776            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1777                         "Parsed addr %s", inet_ntoa(New->addr));
1778            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1779                         "Parsed mask %s", inet_ntoa(New->mask));
1780#endif
1781        }
1782        else if (ap_proxy_is_domainname(New, parms->pool)) {
1783            ap_str_tolower(New->name);
1784#if DEBUGGING
1785            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1786                         "Parsed domain %s", New->name);
1787#endif
1788        }
1789        else if (ap_proxy_is_hostname(New, parms->pool)) {
1790            ap_str_tolower(New->name);
1791#if DEBUGGING
1792            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1793                         "Parsed host %s", New->name);
1794#endif
1795        }
1796        else {
1797            ap_proxy_is_word(New, parms->pool);
1798#if DEBUGGING
1799            fprintf(stderr, "Parsed word %s\n", New->name);
1800#endif
1801        }
1802    }
1803    return NULL;
1804}
1805
1806static const char *
1807    set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg)
1808{
1809    proxy_server_conf *psf =
1810    ap_get_module_config(parms->server->module_config, &proxy_module);
1811
1812    if (arg[0] != '.')
1813        return "ProxyDomain: domain name must start with a dot.";
1814
1815    psf->domain = arg;
1816    return NULL;
1817}
1818
1819static const char *
1820    set_proxy_req(cmd_parms *parms, void *dummy, int flag)
1821{
1822    proxy_server_conf *psf =
1823    ap_get_module_config(parms->server->module_config, &proxy_module);
1824
1825    psf->req = flag;
1826    psf->req_set = 1;
1827    return NULL;
1828}
1829
1830static const char *
1831    set_proxy_error_override(cmd_parms *parms, void *dconf, int flag)
1832{
1833    proxy_dir_conf *conf = dconf;
1834
1835    conf->error_override = flag;
1836    conf->error_override_set = 1;
1837    return NULL;
1838}
1839static const char *
1840   add_proxy_http_headers(cmd_parms *parms, void *dconf, int flag)
1841{
1842   proxy_dir_conf *conf = dconf;
1843   conf->add_forwarded_headers = flag;
1844   return NULL;
1845}
1846static const char *
1847    set_preserve_host(cmd_parms *parms, void *dconf, int flag)
1848{
1849    proxy_dir_conf *conf = dconf;
1850
1851    conf->preserve_host = flag;
1852    conf->preserve_host_set = 1;
1853    return NULL;
1854}
1855
1856static const char *
1857    set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1858{
1859    proxy_server_conf *psf =
1860    ap_get_module_config(parms->server->module_config, &proxy_module);
1861    int s = atoi(arg);
1862    if (s < 512 && s != 0) {
1863        return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
1864    }
1865
1866    psf->recv_buffer_size = s;
1867    psf->recv_buffer_size_set = 1;
1868    return NULL;
1869}
1870
1871static const char *
1872    set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg)
1873{
1874    proxy_server_conf *psf =
1875    ap_get_module_config(parms->server->module_config, &proxy_module);
1876    long s = atol(arg);
1877    if (s < 512 && s) {
1878        return "ProxyIOBufferSize must be >= 512 bytes, or 0 for system default.";
1879    }
1880    psf->io_buffer_size = (s ? s : AP_IOBUFSIZE);
1881    psf->io_buffer_size_set = 1;
1882    return NULL;
1883}
1884
1885static const char *
1886    set_max_forwards(cmd_parms *parms, void *dummy, const char *arg)
1887{
1888    proxy_server_conf *psf =
1889    ap_get_module_config(parms->server->module_config, &proxy_module);
1890    long s = atol(arg);
1891
1892    psf->maxfwd = s;
1893    psf->maxfwd_set = 1;
1894    return NULL;
1895}
1896static const char*
1897    set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg)
1898{
1899    proxy_server_conf *psf =
1900    ap_get_module_config(parms->server->module_config, &proxy_module);
1901    int timeout;
1902
1903    timeout = atoi(arg);
1904    if (timeout<1) {
1905        return "Proxy Timeout must be at least 1 second.";
1906    }
1907    psf->timeout_set = 1;
1908    psf->timeout = apr_time_from_sec(timeout);
1909
1910    return NULL;
1911}
1912
1913static const char*
1914    set_via_opt(cmd_parms *parms, void *dummy, const char *arg)
1915{
1916    proxy_server_conf *psf =
1917    ap_get_module_config(parms->server->module_config, &proxy_module);
1918
1919    if (strcasecmp(arg, "Off") == 0)
1920        psf->viaopt = via_off;
1921    else if (strcasecmp(arg, "On") == 0)
1922        psf->viaopt = via_on;
1923    else if (strcasecmp(arg, "Block") == 0)
1924        psf->viaopt = via_block;
1925    else if (strcasecmp(arg, "Full") == 0)
1926        psf->viaopt = via_full;
1927    else {
1928        return "ProxyVia must be one of: "
1929            "off | on | full | block";
1930    }
1931
1932    psf->viaopt_set = 1;
1933    return NULL;
1934}
1935
1936static const char*
1937    set_bad_opt(cmd_parms *parms, void *dummy, const char *arg)
1938{
1939    proxy_server_conf *psf =
1940    ap_get_module_config(parms->server->module_config, &proxy_module);
1941
1942    if (strcasecmp(arg, "IsError") == 0)
1943        psf->badopt = bad_error;
1944    else if (strcasecmp(arg, "Ignore") == 0)
1945        psf->badopt = bad_ignore;
1946    else if (strcasecmp(arg, "StartBody") == 0)
1947        psf->badopt = bad_body;
1948    else {
1949        return "ProxyBadHeader must be one of: "
1950            "IsError | Ignore | StartBody";
1951    }
1952
1953    psf->badopt_set = 1;
1954    return NULL;
1955}
1956
1957static const char*
1958    set_status_opt(cmd_parms *parms, void *dummy, const char *arg)
1959{
1960    proxy_server_conf *psf =
1961    ap_get_module_config(parms->server->module_config, &proxy_module);
1962
1963    if (strcasecmp(arg, "Off") == 0)
1964        psf->proxy_status = status_off;
1965    else if (strcasecmp(arg, "On") == 0)
1966        psf->proxy_status = status_on;
1967    else if (strcasecmp(arg, "Full") == 0)
1968        psf->proxy_status = status_full;
1969    else {
1970        return "ProxyStatus must be one of: "
1971            "off | on | full";
1972    }
1973
1974    psf->proxy_status_set = 1;
1975    return NULL;
1976}
1977
1978static const char *set_bgrowth(cmd_parms *parms, void *dummy, const char *arg)
1979{
1980    proxy_server_conf *psf =
1981    ap_get_module_config(parms->server->module_config, &proxy_module);
1982
1983    int growth = atoi(arg);
1984    if (growth < 0 || growth > 1000) {
1985        return "BalancerGrowth must be between 0 and 1000";
1986    }
1987    psf->bgrowth = growth;
1988    psf->bgrowth_set = 1;
1989
1990    return NULL;
1991}
1992
1993static const char *set_persist(cmd_parms *parms, void *dummy, int flag)
1994{
1995    proxy_server_conf *psf =
1996    ap_get_module_config(parms->server->module_config, &proxy_module);
1997
1998    psf->bal_persist = flag;
1999    return NULL;
2000}
2001
2002static const char *set_inherit(cmd_parms *parms, void *dummy, int flag)
2003{
2004    proxy_server_conf *psf =
2005    ap_get_module_config(parms->server->module_config, &proxy_module);
2006
2007    psf->inherit = flag;
2008    psf->inherit_set = 1;
2009    return NULL;
2010}
2011
2012static const char *set_ppinherit(cmd_parms *parms, void *dummy, int flag)
2013{
2014    proxy_server_conf *psf =
2015    ap_get_module_config(parms->server->module_config, &proxy_module);
2016
2017    psf->ppinherit = flag;
2018    psf->ppinherit_set = 1;
2019    return NULL;
2020}
2021
2022static const char *add_member(cmd_parms *cmd, void *dummy, const char *arg)
2023{
2024    server_rec *s = cmd->server;
2025    proxy_server_conf *conf =
2026    ap_get_module_config(s->module_config, &proxy_module);
2027    proxy_balancer *balancer;
2028    proxy_worker *worker;
2029    char *path = cmd->path;
2030    char *name = NULL;
2031    char *word;
2032    apr_table_t *params = apr_table_make(cmd->pool, 5);
2033    const apr_array_header_t *arr;
2034    const apr_table_entry_t *elts;
2035    int reuse = 0;
2036    int i;
2037    /* XXX: Should this be NOT_IN_DIRECTORY|NOT_IN_FILES? */
2038    const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
2039    if (err)
2040        return err;
2041
2042    if (cmd->path)
2043        path = apr_pstrdup(cmd->pool, cmd->path);
2044
2045    while (*arg) {
2046        char *val;
2047        word = ap_getword_conf(cmd->pool, &arg);
2048        val = strchr(word, '=');
2049
2050        if (!val) {
2051            if (!path)
2052                path = word;
2053            else if (!name)
2054                name = word;
2055            else {
2056                if (cmd->path)
2057                    return "BalancerMember can not have a balancer name when defined in a location";
2058                else
2059                    return "Invalid BalancerMember parameter. Parameter must "
2060                           "be in the form 'key=value'";
2061            }
2062        } else {
2063            *val++ = '\0';
2064            apr_table_setn(params, word, val);
2065        }
2066    }
2067    if (!path)
2068        return "BalancerMember must define balancer name when outside <Proxy > section";
2069    if (!name)
2070        return "BalancerMember must define remote proxy server";
2071
2072    ap_str_tolower(path);   /* lowercase scheme://hostname */
2073
2074    /* Try to find the balancer */
2075    balancer = ap_proxy_get_balancer(cmd->temp_pool, conf, path, 0);
2076    if (!balancer) {
2077        err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, path, "/", 0);
2078        if (err)
2079            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
2080    }
2081
2082    /* Try to find existing worker */
2083    worker = ap_proxy_get_worker(cmd->temp_pool, balancer, conf, de_socketfy(cmd->temp_pool, name));
2084    if (!worker) {
2085        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01147)
2086                     "Defining worker '%s' for balancer '%s'",
2087                     name, balancer->s->name);
2088        if ((err = ap_proxy_define_worker(cmd->pool, &worker, balancer, conf, name, 0)) != NULL)
2089            return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
2090        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, APLOGNO(01148)
2091                     "Defined worker '%s' for balancer '%s'",
2092                     ap_proxy_worker_name(cmd->pool, worker), balancer->s->name);
2093        PROXY_COPY_CONF_PARAMS(worker, conf);
2094    } else {
2095        reuse = 1;
2096        ap_log_error(APLOG_MARK, APLOG_INFO, 0, cmd->server, APLOGNO(01149)
2097                     "Sharing worker '%s' instead of creating new worker '%s'",
2098                     ap_proxy_worker_name(cmd->pool, worker), name);
2099    }
2100
2101    arr = apr_table_elts(params);
2102    elts = (const apr_table_entry_t *)arr->elts;
2103    for (i = 0; i < arr->nelts; i++) {
2104        if (reuse) {
2105            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(01150)
2106                         "Ignoring parameter '%s=%s' for worker '%s' because of worker sharing",
2107                         elts[i].key, elts[i].val, ap_proxy_worker_name(cmd->pool, worker));
2108        } else {
2109            err = set_worker_param(cmd->pool, worker, elts[i].key,
2110                                               elts[i].val);
2111            if (err)
2112                return apr_pstrcat(cmd->temp_pool, "BalancerMember ", err, NULL);
2113        }
2114    }
2115
2116    return NULL;
2117}
2118
2119static const char *
2120    set_proxy_param(cmd_parms *cmd, void *dummy, const char *arg)
2121{
2122    server_rec *s = cmd->server;
2123    proxy_server_conf *conf =
2124    (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
2125    char *name = NULL;
2126    char *word, *val;
2127    proxy_balancer *balancer = NULL;
2128    proxy_worker *worker = NULL;
2129    int in_proxy_section = 0;
2130    /* XXX: Should this be NOT_IN_DIRECTORY|NOT_IN_FILES? */
2131    const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
2132    if (err)
2133        return err;
2134
2135    if (cmd->directive->parent &&
2136        strncasecmp(cmd->directive->parent->directive,
2137                    "<Proxy", 6) == 0) {
2138        const char *pargs = cmd->directive->parent->args;
2139        /* Directive inside <Proxy section
2140         * Parent directive arg is the worker/balancer name.
2141         */
2142        name = ap_getword_conf(cmd->temp_pool, &pargs);
2143        if ((word = ap_strchr(name, '>')))
2144            *word = '\0';
2145        in_proxy_section = 1;
2146    }
2147    else {
2148        /* Standard set directive with worker/balancer
2149         * name as first param.
2150         */
2151        name = ap_getword_conf(cmd->temp_pool, &arg);
2152    }
2153
2154    if (ap_proxy_valid_balancer_name(name, 9)) {
2155        balancer = ap_proxy_get_balancer(cmd->pool, conf, name, 0);
2156        if (!balancer) {
2157            if (in_proxy_section) {
2158                err = ap_proxy_define_balancer(cmd->pool, &balancer, conf, name, "/", 0);
2159                if (err)
2160                    return apr_pstrcat(cmd->temp_pool, "ProxySet ",
2161                                       err, NULL);
2162            }
2163            else
2164                return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
2165                                   name, "' Balancer.", NULL);
2166        }
2167    }
2168    else {
2169        worker = ap_proxy_get_worker(cmd->temp_pool, NULL, conf, de_socketfy(cmd->temp_pool, name));
2170        if (!worker) {
2171            if (in_proxy_section) {
2172                err = ap_proxy_define_worker(cmd->pool, &worker, NULL,
2173                                             conf, name, 0);
2174                if (err)
2175                    return apr_pstrcat(cmd->temp_pool, "ProxySet ",
2176                                       err, NULL);
2177            }
2178            else
2179                return apr_pstrcat(cmd->temp_pool, "ProxySet can not find '",
2180                                   name, "' Worker.", NULL);
2181        }
2182    }
2183
2184    while (*arg) {
2185        word = ap_getword_conf(cmd->pool, &arg);
2186        val = strchr(word, '=');
2187        if (!val) {
2188            return "Invalid ProxySet parameter. Parameter must be "
2189                   "in the form 'key=value'";
2190        }
2191        else
2192            *val++ = '\0';
2193        if (worker)
2194            err = set_worker_param(cmd->pool, worker, word, val);
2195        else
2196            err = set_balancer_param(conf, cmd->pool, balancer, word, val);
2197
2198        if (err)
2199            return apr_pstrcat(cmd->temp_pool, "ProxySet: ", err, " ", word, "=", val, "; ", name, NULL);
2200    }
2201
2202    return NULL;
2203}
2204
2205static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config)
2206{
2207    proxy_server_conf *sconf = ap_get_module_config(s->module_config,
2208                                                    &proxy_module);
2209    void **new_space = (void **)apr_array_push(sconf->sec_proxy);
2210
2211    *new_space = dir_config;
2212}
2213
2214static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
2215{
2216    const char *errmsg;
2217    const char *endp = ap_strrchr_c(arg, '>');
2218    int old_overrides = cmd->override;
2219    char *old_path = cmd->path;
2220    proxy_dir_conf *conf;
2221    ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
2222    ap_regex_t *r = NULL;
2223    const command_rec *thiscmd = cmd->cmd;
2224    char *word, *val;
2225    proxy_balancer *balancer = NULL;
2226    proxy_worker *worker = NULL;
2227
2228    const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
2229    proxy_server_conf *sconf =
2230    (proxy_server_conf *) ap_get_module_config(cmd->server->module_config, &proxy_module);
2231
2232    if (err != NULL) {
2233        return err;
2234    }
2235
2236    if (endp == NULL) {
2237        return apr_pstrcat(cmd->pool, cmd->cmd->name,
2238                           "> directive missing closing '>'", NULL);
2239    }
2240
2241    arg = apr_pstrndup(cmd->pool, arg, endp-arg);
2242
2243    if (!arg) {
2244        if (thiscmd->cmd_data)
2245            return "<ProxyMatch > block must specify a path";
2246        else
2247            return "<Proxy > block must specify a path";
2248    }
2249
2250    cmd->path = ap_getword_conf(cmd->pool, &arg);
2251    cmd->override = OR_ALL|ACCESS_CONF;
2252
2253    if (!strncasecmp(cmd->path, "proxy:", 6))
2254        cmd->path += 6;
2255
2256    /* XXX Ignore case?  What if we proxy a case-insensitive server?!?
2257     * While we are at it, shouldn't we also canonicalize the entire
2258     * scheme?  See proxy_fixup()
2259     */
2260    if (thiscmd->cmd_data) { /* <ProxyMatch> */
2261        r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED);
2262        if (!r) {
2263            return "Regex could not be compiled";
2264        }
2265    }
2266
2267    /* initialize our config and fetch it */
2268    conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
2269                                 &proxy_module, cmd->pool);
2270
2271    errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
2272    if (errmsg != NULL)
2273        return errmsg;
2274
2275    conf->r = r;
2276    conf->p = cmd->path;
2277    conf->p_is_fnmatch = apr_fnmatch_test(conf->p);
2278
2279    if (r) {
2280        conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
2281        ap_regname(r, conf->refs, AP_REG_MATCH, 1);
2282    }
2283
2284    ap_add_per_proxy_conf(cmd->server, new_dir_conf);
2285
2286    if (*arg != '\0') {
2287        if (thiscmd->cmd_data)
2288            return "Multiple <ProxyMatch> arguments not (yet) supported.";
2289        if (conf->p_is_fnmatch)
2290            return apr_pstrcat(cmd->pool, thiscmd->name,
2291                               "> arguments are not supported for wildchar url.",
2292                               NULL);
2293        if (!ap_strchr_c(conf->p, ':'))
2294            return apr_pstrcat(cmd->pool, thiscmd->name,
2295                               "> arguments are not supported for non url.",
2296                               NULL);
2297        if (ap_proxy_valid_balancer_name((char *)conf->p, 9)) {
2298            balancer = ap_proxy_get_balancer(cmd->pool, sconf, conf->p, 0);
2299            if (!balancer) {
2300                err = ap_proxy_define_balancer(cmd->pool, &balancer,
2301                                               sconf, conf->p, "/", 0);
2302                if (err)
2303                    return apr_pstrcat(cmd->temp_pool, thiscmd->name,
2304                                       " ", err, NULL);
2305            }
2306        }
2307        else {
2308            worker = ap_proxy_get_worker(cmd->temp_pool, NULL, sconf,
2309                                         de_socketfy(cmd->temp_pool, (char*)conf->p));
2310            if (!worker) {
2311                err = ap_proxy_define_worker(cmd->pool, &worker, NULL,
2312                                          sconf, conf->p, 0);
2313                if (err)
2314                    return apr_pstrcat(cmd->temp_pool, thiscmd->name,
2315                                       " ", err, NULL);
2316            }
2317        }
2318        if (worker == NULL && balancer == NULL) {
2319            return apr_pstrcat(cmd->pool, thiscmd->name,
2320                               "> arguments are supported only for workers.",
2321                               NULL);
2322        }
2323        while (*arg) {
2324            word = ap_getword_conf(cmd->pool, &arg);
2325            val = strchr(word, '=');
2326            if (!val) {
2327                return "Invalid Proxy parameter. Parameter must be "
2328                       "in the form 'key=value'";
2329            }
2330            else
2331                *val++ = '\0';
2332            if (worker)
2333                err = set_worker_param(cmd->pool, worker, word, val);
2334            else
2335                err = set_balancer_param(sconf, cmd->pool, balancer,
2336                                         word, val);
2337            if (err)
2338                return apr_pstrcat(cmd->temp_pool, thiscmd->name, " ", err, " ",
2339                                   word, "=", val, "; ", conf->p, NULL);
2340        }
2341    }
2342
2343    cmd->path = old_path;
2344    cmd->override = old_overrides;
2345
2346    return NULL;
2347}
2348
2349static const command_rec proxy_cmds[] =
2350{
2351    AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF,
2352    "Container for directives affecting resources located in the proxied "
2353    "location"),
2354    AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF,
2355    "Container for directives affecting resources located in the proxied "
2356    "location, in regular expression syntax"),
2357    AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF,
2358     "on if the true proxy requests should be accepted"),
2359    AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF,
2360     "a scheme, partial URL or '*' and a proxy server"),
2361    AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF,
2362     "a regex pattern and a proxy server"),
2363    AP_INIT_FLAG("ProxyPassInterpolateEnv", ap_set_flag_slot_char,
2364        (void*)APR_OFFSETOF(proxy_dir_conf, interpolate_env),
2365        RSRC_CONF|ACCESS_CONF, "Interpolate Env Vars in reverse Proxy") ,
2366    AP_INIT_RAW_ARGS("ProxyPass", add_pass_noregex, NULL, RSRC_CONF|ACCESS_CONF,
2367     "a virtual path and a URL"),
2368    AP_INIT_RAW_ARGS("ProxyPassMatch", add_pass_regex, NULL, RSRC_CONF|ACCESS_CONF,
2369     "a virtual path and a URL"),
2370    AP_INIT_TAKE123("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF,
2371     "a virtual path and a URL for reverse proxy behaviour"),
2372    AP_INIT_TAKE23("ProxyPassReverseCookiePath", cookie_path, NULL,
2373       RSRC_CONF|ACCESS_CONF, "Path rewrite rule for proxying cookies"),
2374    AP_INIT_TAKE23("ProxyPassReverseCookieDomain", cookie_domain, NULL,
2375       RSRC_CONF|ACCESS_CONF, "Domain rewrite rule for proxying cookies"),
2376    AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF,
2377     "A list of names, hosts or domains to which the proxy will not connect"),
2378    AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF,
2379     "Receive buffer size for outgoing HTTP and FTP connections in bytes"),
2380    AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF,
2381     "IO buffer size for outgoing HTTP and FTP connections in bytes"),
2382    AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF,
2383     "The maximum number of proxies a request may be forwarded through."),
2384    AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF,
2385     "A list of domains, hosts, or subnets to which the proxy will connect directly"),
2386    AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF,
2387     "The default intranet domain name (in absence of a domain in the URL)"),
2388    AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF,
2389     "Configure Via: proxy header header to one of: on | off | block | full"),
2390    AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF|ACCESS_CONF,
2391     "use our error handling pages instead of the servers' we are proxying"),
2392    AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF|ACCESS_CONF,
2393     "on if we should preserve host header while proxying"),
2394    AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF,
2395     "Set the timeout (in seconds) for a proxied connection. "
2396     "This overrides the server timeout"),
2397    AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF,
2398     "How to handle bad header line in response: IsError | Ignore | StartBody"),
2399    AP_INIT_RAW_ARGS("BalancerMember", add_member, NULL, RSRC_CONF|ACCESS_CONF,
2400     "A balancer name and scheme with list of params"),
2401    AP_INIT_TAKE1("BalancerGrowth", set_bgrowth, NULL, RSRC_CONF,
2402     "Number of additional Balancers that can be added post-config"),
2403    AP_INIT_FLAG("BalancerPersist", set_persist, NULL, RSRC_CONF,
2404     "on if the balancer should persist changes on reboot/restart made via the Balancer Manager"),
2405    AP_INIT_FLAG("BalancerInherit", set_inherit, NULL, RSRC_CONF,
2406     "on if this server should inherit Balancers and Workers defined in the main server "
2407     "(Setting to off recommended if using the Balancer Manager)"),
2408    AP_INIT_FLAG("ProxyPassInherit", set_ppinherit, NULL, RSRC_CONF,
2409     "on if this server should inherit all ProxyPass directives defined in the main server "
2410     "(Setting to off recommended if using the Balancer Manager)"),
2411    AP_INIT_TAKE1("ProxyStatus", set_status_opt, NULL, RSRC_CONF,
2412     "Configure Status: proxy status to one of: on | off | full"),
2413    AP_INIT_RAW_ARGS("ProxySet", set_proxy_param, NULL, RSRC_CONF|ACCESS_CONF,
2414     "A balancer or worker name with list of params"),
2415    AP_INIT_TAKE1("ProxySourceAddress", set_source_address, NULL, RSRC_CONF,
2416     "Configure local source IP used for request forward"),
2417    AP_INIT_FLAG("ProxyAddHeaders", add_proxy_http_headers, NULL, RSRC_CONF|ACCESS_CONF,
2418     "on if X-Forwarded-* headers should be added or completed"),
2419    {NULL}
2420};
2421
2422static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL;
2423static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL;
2424static APR_OPTIONAL_FN_TYPE(ssl_is_https) *proxy_is_https = NULL;
2425static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *proxy_ssl_val = NULL;
2426
2427PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c)
2428{
2429    /*
2430     * if c == NULL just check if the optional function was imported
2431     * else run the optional function so ssl filters are inserted
2432     */
2433    if (proxy_ssl_enable) {
2434        return c ? proxy_ssl_enable(c) : 1;
2435    }
2436
2437    return 0;
2438}
2439
2440PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c)
2441{
2442    if (proxy_ssl_disable) {
2443        return proxy_ssl_disable(c);
2444    }
2445
2446    return 0;
2447}
2448
2449PROXY_DECLARE(int) ap_proxy_conn_is_https(conn_rec *c)
2450{
2451    if (proxy_is_https) {
2452        return proxy_is_https(c);
2453    }
2454    else
2455        return 0;
2456}
2457
2458PROXY_DECLARE(const char *) ap_proxy_ssl_val(apr_pool_t *p, server_rec *s,
2459                                             conn_rec *c, request_rec *r,
2460                                             const char *var)
2461{
2462    if (proxy_ssl_val) {
2463        /* XXX Perhaps the casting useless */
2464        return (const char *)proxy_ssl_val(p, s, c, r, (char *)var);
2465    }
2466    else
2467        return NULL;
2468}
2469
2470static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog,
2471                             apr_pool_t *ptemp, server_rec *s)
2472{
2473    apr_status_t rv = ap_global_mutex_create(&proxy_mutex, NULL,
2474            proxy_id, NULL, s, pconf, 0);
2475    if (rv != APR_SUCCESS) {
2476        ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02478)
2477        "failed to create %s mutex", proxy_id);
2478        return rv;
2479    }
2480
2481    proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
2482    proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
2483    proxy_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
2484    proxy_ssl_val = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
2485    ap_proxy_strmatch_path = apr_strmatch_precompile(pconf, "path=", 0);
2486    ap_proxy_strmatch_domain = apr_strmatch_precompile(pconf, "domain=", 0);
2487
2488    return OK;
2489}
2490
2491/*
2492 *  proxy Extension to mod_status
2493 */
2494static int proxy_status_hook(request_rec *r, int flags)
2495{
2496    int i, n;
2497    void *sconf = r->server->module_config;
2498    proxy_server_conf *conf = (proxy_server_conf *)
2499        ap_get_module_config(sconf, &proxy_module);
2500    proxy_balancer *balancer = NULL;
2501    proxy_worker **worker = NULL;
2502
2503    if ((flags & AP_STATUS_SHORT) || conf->balancers->nelts == 0 ||
2504        conf->proxy_status == status_off)
2505        return OK;
2506
2507    balancer = (proxy_balancer *)conf->balancers->elts;
2508    for (i = 0; i < conf->balancers->nelts; i++) {
2509        ap_rputs("<hr />\n<h1>Proxy LoadBalancer Status for ", r);
2510        ap_rvputs(r, balancer->s->name, "</h1>\n\n", NULL);
2511        ap_rputs("\n\n<table border=\"0\"><tr>"
2512                 "<th>SSes</th><th>Timeout</th><th>Method</th>"
2513                 "</tr>\n<tr>", r);
2514        if (*balancer->s->sticky) {
2515            if (strcmp(balancer->s->sticky, balancer->s->sticky_path)) {
2516                ap_rvputs(r, "<td>", balancer->s->sticky, " | ",
2517                          balancer->s->sticky_path, NULL);
2518            }
2519            else {
2520                ap_rvputs(r, "<td>", balancer->s->sticky, NULL);
2521            }
2522        }
2523        else {
2524            ap_rputs("<td> - ", r);
2525        }
2526        ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
2527                   apr_time_sec(balancer->s->timeout));
2528        ap_rprintf(r, "<td>%s</td>\n",
2529                   balancer->lbmethod->name);
2530        ap_rputs("</table>\n", r);
2531        ap_rputs("\n\n<table border=\"0\"><tr>"
2532                 "<th>Sch</th><th>Host</th><th>Stat</th>"
2533                 "<th>Route</th><th>Redir</th>"
2534                 "<th>F</th><th>Set</th><th>Acc</th><th>Wr</th><th>Rd</th>"
2535                 "</tr>\n", r);
2536
2537        worker = (proxy_worker **)balancer->workers->elts;
2538        for (n = 0; n < balancer->workers->nelts; n++) {
2539            char fbuf[50];
2540            ap_rvputs(r, "<tr>\n<td>", (*worker)->s->scheme, "</td>", NULL);
2541            ap_rvputs(r, "<td>", (*worker)->s->hostname, "</td><td>", NULL);
2542            ap_rvputs(r, ap_proxy_parse_wstatus(r->pool, *worker), NULL);
2543            ap_rvputs(r, "</td><td>", (*worker)->s->route, NULL);
2544            ap_rvputs(r, "</td><td>", (*worker)->s->redirect, NULL);
2545            ap_rprintf(r, "</td><td>%d</td>", (*worker)->s->lbfactor);
2546            ap_rprintf(r, "<td>%d</td>", (*worker)->s->lbset);
2547            ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td><td>", (*worker)->s->elected);
2548            ap_rputs(apr_strfsize((*worker)->s->transferred, fbuf), r);
2549            ap_rputs("</td><td>", r);
2550            ap_rputs(apr_strfsize((*worker)->s->read, fbuf), r);
2551            ap_rputs("</td>\n", r);
2552
2553            /* TODO: Add the rest of dynamic worker data */
2554            ap_rputs("</tr>\n", r);
2555
2556            ++worker;
2557        }
2558        ap_rputs("</table>\n", r);
2559        ++balancer;
2560    }
2561    ap_rputs("<hr /><table>\n"
2562             "<tr><th>SSes</th><td>Sticky session name</td></tr>\n"
2563             "<tr><th>Timeout</th><td>Balancer Timeout</td></tr>\n"
2564             "<tr><th>Sch</th><td>Connection scheme</td></tr>\n"
2565             "<tr><th>Host</th><td>Backend Hostname</td></tr>\n"
2566             "<tr><th>Stat</th><td>Worker status</td></tr>\n"
2567             "<tr><th>Route</th><td>Session Route</td></tr>\n"
2568             "<tr><th>Redir</th><td>Session Route Redirection</td></tr>\n"
2569             "<tr><th>F</th><td>Load Balancer Factor</td></tr>\n"
2570             "<tr><th>Acc</th><td>Number of uses</td></tr>\n"
2571             "<tr><th>Wr</th><td>Number of bytes transferred</td></tr>\n"
2572             "<tr><th>Rd</th><td>Number of bytes read</td></tr>\n"
2573             "</table>", r);
2574
2575    return OK;
2576}
2577
2578static void child_init(apr_pool_t *p, server_rec *s)
2579{
2580    proxy_worker *reverse = NULL;
2581
2582    apr_status_t rv = apr_global_mutex_child_init(&proxy_mutex,
2583                                      apr_global_mutex_lockfile(proxy_mutex),
2584                                      p);
2585    if (rv != APR_SUCCESS) {
2586        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(02479)
2587                     "could not init proxy_mutex in child");
2588        exit(1); /* Ugly, but what else? */
2589    }
2590
2591    /* TODO */
2592    while (s) {
2593        void *sconf = s->module_config;
2594        proxy_server_conf *conf;
2595        proxy_worker *worker;
2596        int i;
2597
2598        conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
2599        /*
2600         * NOTE: non-balancer members don't use shm at all...
2601         *       after all, why should they?
2602         */
2603        worker = (proxy_worker *)conf->workers->elts;
2604        for (i = 0; i < conf->workers->nelts; i++, worker++) {
2605            ap_proxy_initialize_worker(worker, s, conf->pool);
2606        }
2607        /* Create and initialize forward worker if defined */
2608        if (conf->req_set && conf->req) {
2609            proxy_worker *forward;
2610            ap_proxy_define_worker(p, &forward, NULL, NULL, "http://www.apache.org", 0);
2611            conf->forward = forward;
2612            PROXY_STRNCPY(conf->forward->s->name,     "proxy:forward");
2613            PROXY_STRNCPY(conf->forward->s->hostname, "*");
2614            PROXY_STRNCPY(conf->forward->s->scheme,   "*");
2615            conf->forward->hash.def = conf->forward->s->hash.def =
2616                ap_proxy_hashfunc(conf->forward->s->name, PROXY_HASHFUNC_DEFAULT);
2617             conf->forward->hash.fnv = conf->forward->s->hash.fnv =
2618                ap_proxy_hashfunc(conf->forward->s->name, PROXY_HASHFUNC_FNV);
2619            /* Do not disable worker in case of errors */
2620            conf->forward->s->status |= PROXY_WORKER_IGNORE_ERRORS;
2621            ap_proxy_initialize_worker(conf->forward, s, conf->pool);
2622            /* Disable address cache for generic forward worker */
2623            conf->forward->s->is_address_reusable = 0;
2624        }
2625        if (!reverse) {
2626            ap_proxy_define_worker(p, &reverse, NULL, NULL, "http://www.apache.org", 0);
2627            PROXY_STRNCPY(reverse->s->name,     "proxy:reverse");
2628            PROXY_STRNCPY(reverse->s->hostname, "*");
2629            PROXY_STRNCPY(reverse->s->scheme,   "*");
2630            reverse->hash.def = reverse->s->hash.def =
2631                ap_proxy_hashfunc(reverse->s->name, PROXY_HASHFUNC_DEFAULT);
2632            reverse->hash.fnv = reverse->s->hash.fnv =
2633                ap_proxy_hashfunc(reverse->s->name, PROXY_HASHFUNC_FNV);
2634            /* Do not disable worker in case of errors */
2635            reverse->s->status |= PROXY_WORKER_IGNORE_ERRORS;
2636            conf->reverse = reverse;
2637            ap_proxy_initialize_worker(conf->reverse, s, conf->pool);
2638            /* Disable address cache for generic reverse worker */
2639            reverse->s->is_address_reusable = 0;
2640        }
2641        conf->reverse = reverse;
2642        s = s->next;
2643    }
2644}
2645
2646/*
2647 * This routine is called before the server processes the configuration
2648 * files.
2649 */
2650static int proxy_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
2651                            apr_pool_t *ptemp)
2652{
2653    apr_status_t rv = ap_mutex_register(pconf, proxy_id, NULL,
2654            APR_LOCK_DEFAULT, 0);
2655    if (rv != APR_SUCCESS) {
2656        ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(02480)
2657                "failed to register %s mutex", proxy_id);
2658        return 500; /* An HTTP status would be a misnomer! */
2659    }
2660
2661    APR_OPTIONAL_HOOK(ap, status_hook, proxy_status_hook, NULL, NULL,
2662                      APR_HOOK_MIDDLE);
2663    /* Reset workers count on gracefull restart */
2664    proxy_lb_workers = 0;
2665    return OK;
2666}
2667static void register_hooks(apr_pool_t *p)
2668{
2669    /* fixup before mod_rewrite, so that the proxied url will not
2670     * escaped accidentally by our fixup.
2671     */
2672    static const char * const aszSucc[] = { "mod_rewrite.c", NULL};
2673    /* Only the mpm_winnt has child init hook handler.
2674     * make sure that we are called after the mpm
2675     * initializes.
2676     */
2677    static const char *const aszPred[] = { "mpm_winnt.c", "mod_proxy_balancer.c", NULL};
2678
2679    /* handler */
2680    ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST);
2681    /* filename-to-URI translation */
2682    ap_hook_translate_name(proxy_trans, aszSucc, NULL, APR_HOOK_FIRST);
2683    /* walk <Proxy > entries and suppress default TRACE behavior */
2684    ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST);
2685    /* fixups */
2686    ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST);
2687    /* post read_request handling */
2688    ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST);
2689    /* pre config handling */
2690    ap_hook_pre_config(proxy_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
2691    /* post config handling */
2692    ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
2693    /* child init handling */
2694    ap_hook_child_init(child_init, aszPred, NULL, APR_HOOK_MIDDLE);
2695
2696    /* register optional functions within proxy_util.c */
2697    proxy_util_register_hooks(p);
2698}
2699
2700AP_DECLARE_MODULE(proxy) =
2701{
2702    STANDARD20_MODULE_STUFF,
2703    create_proxy_dir_config,    /* create per-directory config structure */
2704    merge_proxy_dir_config,     /* merge per-directory config structures */
2705    create_proxy_config,        /* create per-server config structure */
2706    merge_proxy_config,         /* merge per-server config structures */
2707    proxy_cmds,                 /* command table */
2708    register_hooks
2709};
2710
2711APR_HOOK_STRUCT(
2712    APR_HOOK_LINK(scheme_handler)
2713    APR_HOOK_LINK(canon_handler)
2714    APR_HOOK_LINK(pre_request)
2715    APR_HOOK_LINK(post_request)
2716    APR_HOOK_LINK(request_status)
2717)
2718
2719APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler,
2720                                     (request_rec *r, proxy_worker *worker,
2721                                      proxy_server_conf *conf,
2722                                      char *url, const char *proxyhost,
2723                                      apr_port_t proxyport),(r,worker,conf,
2724                                      url,proxyhost,proxyport),DECLINED)
2725APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler,
2726                                      (request_rec *r, char *url),(r,
2727                                      url),DECLINED)
2728APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, pre_request, (
2729                                      proxy_worker **worker,
2730                                      proxy_balancer **balancer,
2731                                      request_rec *r,
2732                                      proxy_server_conf *conf,
2733                                      char **url),(worker,balancer,
2734                                      r,conf,url),DECLINED)
2735APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, post_request,
2736                                      (proxy_worker *worker,
2737                                       proxy_balancer *balancer,
2738                                       request_rec *r,
2739                                       proxy_server_conf *conf),(worker,
2740                                       balancer,r,conf),DECLINED)
2741APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups,
2742                                    (request_rec *r), (r),
2743                                    OK, DECLINED)
2744APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, request_status,
2745                                    (int *status, request_rec *r),
2746                                    (status, r),
2747                                    OK, DECLINED)
2748