home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / proxy / mod_proxy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  28.3 KB  |  899 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1996-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. #include "mod_proxy.h"
  59.  
  60. #define CORE_PRIVATE
  61.  
  62. #include "http_log.h"
  63. #include "http_vhost.h"
  64. #include "http_request.h"
  65.  
  66. /* Some WWW schemes and their default ports; this is basically /etc/services */
  67. /* This will become global when the protocol abstraction comes */
  68. static struct proxy_services defports[] =
  69. {
  70.     {"http", DEFAULT_HTTP_PORT},
  71.     {"ftp", DEFAULT_FTP_PORT},
  72.     {"https", DEFAULT_HTTPS_PORT},
  73.     {"gopher", DEFAULT_GOPHER_PORT},
  74.     {"nntp", DEFAULT_NNTP_PORT},
  75.     {"wais", DEFAULT_WAIS_PORT},
  76.     {"snews", DEFAULT_SNEWS_PORT},
  77.     {"prospero", DEFAULT_PROSPERO_PORT},
  78.     {NULL, -1}            /* unknown port */
  79. };
  80.  
  81. /*
  82.  * A Web proxy module. Stages:
  83.  *
  84.  *  translate_name: set filename to proxy:<URL>
  85.  *  type_checker:   set type to PROXY_MAGIC_TYPE if filename begins proxy:
  86.  *  fix_ups:        convert the URL stored in the filename to the
  87.  *                  canonical form.
  88.  *  handler:        handle proxy requests
  89.  */
  90.  
  91. /* -------------------------------------------------------------- */
  92. /* Translate the URL into a 'filename' */
  93.  
  94. static int alias_match(const char *uri, const char *alias_fakename)
  95. {
  96.     const char *end_fakename = alias_fakename + strlen(alias_fakename);
  97.     const char *aliasp = alias_fakename, *urip = uri;
  98.  
  99.     while (aliasp < end_fakename) {
  100.     if (*aliasp == '/') {
  101.         /* any number of '/' in the alias matches any number in
  102.          * the supplied URI, but there must be at least one...
  103.          */
  104.         if (*urip != '/')
  105.         return 0;
  106.  
  107.         while (*aliasp == '/')
  108.         ++aliasp;
  109.         while (*urip == '/')
  110.         ++urip;
  111.     }
  112.     else {
  113.         /* Other characters are compared literally */
  114.         if (*urip++ != *aliasp++)
  115.         return 0;
  116.     }
  117.     }
  118.  
  119.     /* Check last alias path component matched all the way */
  120.  
  121.     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
  122.     return 0;
  123.  
  124.     /* Return number of characters from URI which matched (may be
  125.      * greater than length of alias, since we may have matched
  126.      * doubled slashes)
  127.      */
  128.  
  129.     return urip - uri;
  130. }
  131.  
  132. /* Detect if an absoluteURI should be proxied or not.  Note that we
  133.  * have to do this during this phase because later phases are
  134.  * "short-circuiting"... i.e. translate_names will end when the first
  135.  * module returns OK.  So for example, if the request is something like:
  136.  *
  137.  * GET http://othervhost/cgi-bin/printenv HTTP/1.0
  138.  *
  139.  * mod_alias will notice the /cgi-bin part and ScriptAlias it and
  140.  * short-circuit the proxy... just because of the ordering in the
  141.  * configuration file.
  142.  */
  143. static int proxy_detect(request_rec *r)
  144. {
  145.     void *sconf = r->server->module_config;
  146.     proxy_server_conf *conf;
  147.  
  148.     conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
  149.  
  150.     if (conf->req && r->parsed_uri.scheme) {
  151.     /* but it might be something vhosted */
  152.        if (!(r->parsed_uri.hostname
  153.         && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
  154.         && ap_matches_request_vhost(r, r->parsed_uri.hostname,
  155.                r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
  156.         r->proxyreq = 1;
  157.         r->uri = r->unparsed_uri;
  158.         r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
  159.         r->handler = "proxy-server";
  160.         }
  161.     }
  162.     /* We need special treatment for CONNECT proxying: it has no scheme part */
  163.     else if (conf->req && r->method_number == M_CONNECT
  164.          && r->parsed_uri.hostname
  165.          && r->parsed_uri.port_str) {
  166.         r->proxyreq = 1;
  167.         r->uri = r->unparsed_uri;
  168.         r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
  169.         r->handler = "proxy-server";
  170.     }
  171.     return DECLINED;
  172. }
  173.  
  174. static int proxy_trans(request_rec *r)
  175. {
  176.     void *sconf = r->server->module_config;
  177.     proxy_server_conf *conf =
  178.     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
  179.     int i, len;
  180.     struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
  181.  
  182.     if (r->proxyreq) {
  183.     /* someone has already set up the proxy, it was possibly ourselves
  184.      * in proxy_detect
  185.      */
  186.     return OK;
  187.     }
  188.  
  189.     /* XXX: since r->uri has been manipulated already we're not really
  190.      * compliant with RFC1945 at this point.  But this probably isn't
  191.      * an issue because this is a hybrid proxy/origin server.
  192.      */
  193.  
  194.     for (i = 0; i < conf->aliases->nelts; i++) {
  195.         len = alias_match(r->uri, ent[i].fake);
  196.         
  197.        if (len > 0) {
  198.            r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real,
  199.                                  r->uri + len, NULL);
  200.            r->handler = "proxy-server";
  201.            r->proxyreq = 1;
  202.            return OK;
  203.     }
  204.     }
  205.     return DECLINED;
  206. }
  207.  
  208. /* -------------------------------------------------------------- */
  209. /* Fixup the filename */
  210.  
  211. /*
  212.  * Canonicalise the URL
  213.  */
  214. static int proxy_fixup(request_rec *r)
  215. {
  216.     char *url, *p;
  217.  
  218.     if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
  219.     return DECLINED;
  220.  
  221.     url = &r->filename[6];
  222.  
  223. /* canonicalise each specific scheme */
  224.     if (strncasecmp(url, "http:", 5) == 0)
  225.     return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT);
  226.     else if (strncasecmp(url, "ftp:", 4) == 0)
  227.     return ap_proxy_ftp_canon(r, url + 4);
  228.  
  229.     p = strchr(url, ':');
  230.     if (p == NULL || p == url)
  231.     return HTTP_BAD_REQUEST;
  232.  
  233.     return OK;        /* otherwise; we've done the best we can */
  234. }
  235.  
  236. static void proxy_init(server_rec *r, pool *p)
  237. {
  238.     ap_proxy_garbage_init(r, p);
  239. }
  240.  
  241.  
  242.  
  243. /* Send a redirection if the request contains a hostname which is not */
  244. /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
  245. /* servers like Netscape's allow this and access hosts from the local */
  246. /* domain in this case. I think it is better to redirect to a FQDN, since */
  247. /* these will later be found in the bookmarks files. */
  248. /* The "ProxyDomain" directive determines what domain will be appended */
  249. static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
  250. {
  251.     char *nuri;
  252.     const char *ref;
  253.  
  254.     /* We only want to worry about GETs */
  255.     if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname)
  256.     return DECLINED;
  257.  
  258.     /* If host does contain a dot already, or it is "localhost", decline */
  259.     if (strchr(r->parsed_uri.hostname, '.') != NULL
  260.      || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
  261.     return DECLINED;    /* host name has a dot already */
  262.  
  263.     ref = ap_table_get(r->headers_in, "Referer");
  264.  
  265.     /* Reassemble the request, but insert the domain after the host name */
  266.     /* Note that the domain name always starts with a dot */
  267.     r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname,
  268.                      domain, NULL);
  269.     nuri = ap_unparse_uri_components(r->pool,
  270.                   &r->parsed_uri,
  271.                   UNP_REVEALPASSWORD);
  272.  
  273.     ap_table_set(r->headers_out, "Location", nuri);
  274.     ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
  275.         "Domain missing: %s sent to %s%s%s", r->uri,
  276.         ap_unparse_uri_components(r->pool, &r->parsed_uri,
  277.               UNP_OMITUSERINFO),
  278.         ref ? " from " : "", ref ? ref : "");
  279.  
  280.     return HTTP_MOVED_PERMANENTLY;
  281. }
  282.  
  283. /* -------------------------------------------------------------- */
  284. /* Invoke handler */
  285.  
  286. static int proxy_handler(request_rec *r)
  287. {
  288.     char *url, *scheme, *p;
  289.     void *sconf = r->server->module_config;
  290.     proxy_server_conf *conf =
  291.     (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
  292.     array_header *proxies = conf->proxies;
  293.     struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
  294.     int i, rc;
  295.     cache_req *cr;
  296.     int direct_connect = 0;
  297.     const char *maxfwd_str;
  298.  
  299.     if (!r->proxyreq || strncmp(r->filename, "proxy:", 6) != 0)
  300.     return DECLINED;
  301.  
  302.     if (r->method_number == M_TRACE &&
  303.     (maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) {
  304.     int maxfwd = strtol(maxfwd_str, NULL, 10);
  305.     if (maxfwd < 1) {
  306.         int access_status;
  307.         r->proxyreq = 0;
  308.         if ((access_status = ap_send_http_trace(r)))
  309.         ap_die(access_status, r);
  310.         else
  311.         ap_finalize_request_protocol(r);
  312.         return OK;
  313.     }
  314.     ap_table_setn(r->headers_in, "Max-Forwards", 
  315.               ap_psprintf(r->pool, "%d", (maxfwd > 0) ? maxfwd-1 : 0));
  316.     }
  317.  
  318.     if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
  319.     return rc;
  320.  
  321.     url = r->filename + 6;
  322.     p = strchr(url, ':');
  323.     if (p == NULL)
  324.     return HTTP_BAD_REQUEST;
  325.  
  326.     rc = ap_proxy_cache_check(r, url, &conf->cache, &cr);
  327.     if (rc != DECLINED)
  328.     return rc;
  329.  
  330.     /* If the host doesn't have a domain name, add one and redirect. */
  331.     if (conf->domain != NULL) {
  332.     rc = proxy_needsdomain(r, url, conf->domain);
  333.     if (ap_is_HTTP_REDIRECT(rc))
  334.         return HTTP_MOVED_PERMANENTLY;
  335.     }
  336.  
  337.     *p = '\0';
  338.     scheme = ap_pstrdup(r->pool, url);
  339.     *p = ':';
  340.  
  341.     /* Check URI's destination host against NoProxy hosts */
  342.     /* Bypass ProxyRemote server lookup if configured as NoProxy */
  343.     /* we only know how to handle communication to a proxy via http */
  344.     /*if (strcasecmp(scheme, "http") == 0) */
  345.     {
  346.     int ii;
  347.     struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
  348.  
  349.     for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
  350.         direct_connect = list[ii].matcher(&list[ii], r);
  351.     }
  352. #if DEBUGGING
  353.     ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
  354.              (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
  355.              r->uri);
  356. #endif
  357.     }
  358.  
  359. /* firstly, try a proxy, unless a NoProxy directive is active */
  360.  
  361.     if (!direct_connect)
  362.     for (i = 0; i < proxies->nelts; i++) {
  363.         p = strchr(ents[i].scheme, ':');    /* is it a partial URL? */
  364.         if (strcmp(ents[i].scheme, "*") == 0 ||
  365.         (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
  366.         (p != NULL &&
  367.            strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
  368.         /* CONNECT is a special method that bypasses the normal
  369.          * proxy code.
  370.          */
  371.         if (r->method_number == M_CONNECT)
  372.             rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname,
  373.                            ents[i].port);
  374. /* we only know how to handle communication to a proxy via http */
  375.         else if (strcasecmp(ents[i].protocol, "http") == 0)
  376.             rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname,
  377.                         ents[i].port);
  378.         else
  379.             rc = DECLINED;
  380.  
  381.         /* an error or success */
  382.         if (rc != DECLINED && rc != HTTP_BAD_GATEWAY)
  383.             return rc;
  384.         /* we failed to talk to the upstream proxy */
  385.         }
  386.     }
  387.  
  388. /* otherwise, try it direct */
  389. /* N.B. what if we're behind a firewall, where we must use a proxy or
  390.  * give up??
  391.  */
  392.     /* handle the scheme */
  393.     if (r->method_number == M_CONNECT)
  394.     return ap_proxy_connect_handler(r, cr, url, NULL, 0);
  395.     if (strcasecmp(scheme, "http") == 0)
  396.     return ap_proxy_http_handler(r, cr, url, NULL, 0);
  397.     if (strcasecmp(scheme, "ftp") == 0)
  398.     return ap_proxy_ftp_handler(r, cr, url);
  399.     else
  400.     return HTTP_FORBIDDEN;
  401. }
  402.  
  403. /* -------------------------------------------------------------- */
  404. /* Setup configurable data */
  405.  
  406. static void *
  407.      create_proxy_config(pool *p, server_rec *s)
  408. {
  409.     proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf));
  410.  
  411.     ps->proxies = ap_make_array(p, 10, sizeof(struct proxy_remote));
  412.     ps->aliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
  413.     ps->raliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
  414.     ps->noproxies = ap_make_array(p, 10, sizeof(struct noproxy_entry));
  415.     ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry));
  416.     ps->nocaches = ap_make_array(p, 10, sizeof(struct nocache_entry));
  417.     ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int));
  418.     ps->domain = NULL;
  419.     ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
  420.     ps->req = 0;
  421.  
  422.     ps->cache.root = NULL;
  423.     ps->cache.space = DEFAULT_CACHE_SPACE;
  424.     ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE;
  425.     ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE;
  426.     ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR;
  427.     ps->cache.gcinterval = -1;
  428.     /* at these levels, the cache can have 2^18 directories (256,000)  */
  429.     ps->cache.dirlevels = 3;
  430.     ps->cache.dirlength = 1;
  431.     ps->cache.cache_completion = DEFAULT_CACHE_COMPLETION;
  432.  
  433.     return ps;
  434. }
  435.  
  436. static const char *
  437.      add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r)
  438. {
  439.     server_rec *s = cmd->server;
  440.     proxy_server_conf *conf =
  441.     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
  442.     struct proxy_remote *new;
  443.     char *p, *q;
  444.     int port;
  445.  
  446.     p = strchr(r, ':');
  447.     if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0')
  448.     return "ProxyRemote: Bad syntax for a remote proxy server";
  449.     q = strchr(p + 3, ':');
  450.     if (q != NULL) {
  451.     if (sscanf(q + 1, "%u", &port) != 1 || port > 65535)
  452.         return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
  453.     *q = '\0';
  454.     }
  455.     else
  456.     port = -1;
  457.     *p = '\0';
  458.     if (strchr(f, ':') == NULL)
  459.     ap_str_tolower(f);        /* lowercase scheme */
  460.     ap_str_tolower(p + 3);        /* lowercase hostname */
  461.  
  462.     if (port == -1) {
  463.     int i;
  464.     for (i = 0; defports[i].scheme != NULL; i++)
  465.         if (strcasecmp(defports[i].scheme, r) == 0)
  466.         break;
  467.     port = defports[i].port;
  468.     }
  469.  
  470.     new = ap_push_array(conf->proxies);
  471.     new->scheme = f;
  472.     new->protocol = r;
  473.     new->hostname = p + 3;
  474.     new->port = port;
  475.     return NULL;
  476. }
  477.  
  478. static const char *
  479.      add_pass(cmd_parms *cmd, void *dummy, char *f, char *r)
  480. {
  481.     server_rec *s = cmd->server;
  482.     proxy_server_conf *conf =
  483.     (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
  484.     struct proxy_alias *new;
  485.  
  486.     new = ap_push_array(conf->aliases);
  487.     new->fake = f;
  488.     new->real = r;
  489.     return NULL;
  490. }
  491.  
  492. static const char *
  493.     add_pass_reverse(cmd_parms *cmd, void *dummy, char *f, char *r)
  494. {
  495.     server_rec *s = cmd->server;
  496.     proxy_server_conf *conf;
  497.     struct proxy_alias *new;
  498.  
  499.     conf = (proxy_server_conf *)ap_get_module_config(s->module_config, 
  500.                                                   &proxy_module);
  501.     new = ap_push_array(conf->raliases);
  502.     new->fake = f;
  503.     new->real = r;
  504.     return NULL;
  505. }
  506.  
  507. static const char *
  508.      set_proxy_exclude(cmd_parms *parms, void *dummy, char *arg)
  509. {
  510.     server_rec *s = parms->server;
  511.     proxy_server_conf *conf =
  512.     ap_get_module_config(s->module_config, &proxy_module);
  513.     struct noproxy_entry *new;
  514.     struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
  515.     struct hostent hp;
  516.     int found = 0;
  517.     int i;
  518.  
  519.     /* Don't duplicate entries */
  520.     for (i = 0; i < conf->noproxies->nelts; i++) {
  521.     if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
  522.         found = 1;
  523.     }
  524.  
  525.     if (!found) {
  526.     new = ap_push_array(conf->noproxies);
  527.     new->name = arg;
  528.     /* Don't do name lookups on things that aren't dotted */
  529.     if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
  530.         /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
  531.         memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
  532.     else
  533.         new->addr.s_addr = 0;
  534.     }
  535.     return NULL;
  536. }
  537.  
  538. /*
  539.  * Set the ports CONNECT can use
  540.  */
  541. static const char *
  542.     set_allowed_ports(cmd_parms *parms, void *dummy, char *arg)
  543. {
  544.     server_rec *s = parms->server;
  545.     proxy_server_conf *conf =
  546.       ap_get_module_config(s->module_config, &proxy_module);
  547.     int *New;
  548.  
  549.     if (!isdigit(arg[0]))
  550.     return "AllowCONNECT: port number must be numeric";
  551.  
  552.     New = ap_push_array(conf->allowed_connect_ports);
  553.     *New = atoi(arg);
  554.     return NULL;
  555. }
  556.  
  557. /* Similar to set_proxy_exclude(), but defining directly connected hosts,
  558.  * which should never be accessed via the configured ProxyRemote servers
  559.  */
  560. static const char *
  561.      set_proxy_dirconn(cmd_parms *parms, void *dummy, char *arg)
  562. {
  563.     server_rec *s = parms->server;
  564.     proxy_server_conf *conf =
  565.     ap_get_module_config(s->module_config, &proxy_module);
  566.     struct dirconn_entry *New;
  567.     struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
  568.     int found = 0;
  569.     int i;
  570.  
  571.     /* Don't duplicate entries */
  572.     for (i = 0; i < conf->dirconn->nelts; i++) {
  573.     if (strcasecmp(arg, list[i].name) == 0)
  574.         found = 1;
  575.     }
  576.  
  577.     if (!found) {
  578.     New = ap_push_array(conf->dirconn);
  579.     New->name = arg;
  580.     New->hostentry = NULL;
  581.  
  582.     if (ap_proxy_is_ipaddr(New, parms->pool)) {
  583. #if DEBUGGING
  584.         fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr));
  585.         fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask));
  586. #endif
  587.     }
  588.     else if (ap_proxy_is_domainname(New, parms->pool)) {
  589.         ap_str_tolower(New->name);
  590. #if DEBUGGING
  591.         fprintf(stderr, "Parsed domain %s\n", New->name);
  592. #endif
  593.     }
  594.     else if (ap_proxy_is_hostname(New, parms->pool)) {
  595.         ap_str_tolower(New->name);
  596. #if DEBUGGING
  597.         fprintf(stderr, "Parsed host %s\n", New->name);
  598. #endif
  599.     }
  600.     else {
  601.         ap_proxy_is_word(New, parms->pool);
  602. #if DEBUGGING
  603.         fprintf(stderr, "Parsed word %s\n", New->name);
  604. #endif
  605.     }
  606.     }
  607.     return NULL;
  608. }
  609.  
  610. static const char *
  611.      set_proxy_domain(cmd_parms *parms, void *dummy, char *arg)
  612. {
  613.     proxy_server_conf *psf =
  614.     ap_get_module_config(parms->server->module_config, &proxy_module);
  615.  
  616.     if (arg[0] != '.')
  617.     return "ProxyDomain: domain name must start with a dot.";
  618.  
  619.     psf->domain = arg;
  620.     return NULL;
  621. }
  622.  
  623. static const char *
  624.      set_proxy_req(cmd_parms *parms, void *dummy, int flag)
  625. {
  626.     proxy_server_conf *psf =
  627.     ap_get_module_config(parms->server->module_config, &proxy_module);
  628.  
  629.     psf->req = flag;
  630.     return NULL;
  631. }
  632.  
  633.  
  634. static const char *
  635.      set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg)
  636. {
  637.     proxy_server_conf *psf =
  638.     ap_get_module_config(parms->server->module_config, &proxy_module);
  639.     int val;
  640.  
  641.     if (sscanf(arg, "%d", &val) != 1)
  642.     return "CacheSize value must be an integer (kBytes)";
  643.     psf->cache.space = val;
  644.     return NULL;
  645. }
  646.  
  647. static const char *
  648.      set_cache_root(cmd_parms *parms, void *dummy, char *arg)
  649. {
  650.     proxy_server_conf *psf =
  651.     ap_get_module_config(parms->server->module_config, &proxy_module);
  652.  
  653.     psf->cache.root = arg;
  654.  
  655.     return NULL;
  656. }
  657.  
  658. static const char *
  659.      set_cache_factor(cmd_parms *parms, void *dummy, char *arg)
  660. {
  661.     proxy_server_conf *psf =
  662.     ap_get_module_config(parms->server->module_config, &proxy_module);
  663.     double val;
  664.  
  665.     if (sscanf(arg, "%lg", &val) != 1)
  666.     return "CacheLastModifiedFactor value must be a float";
  667.     psf->cache.lmfactor = val;
  668.  
  669.     return NULL;
  670. }
  671.  
  672. static const char *
  673.      set_cache_maxex(cmd_parms *parms, void *dummy, char *arg)
  674. {
  675.     proxy_server_conf *psf =
  676.     ap_get_module_config(parms->server->module_config, &proxy_module);
  677.     double val;
  678.  
  679.     if (sscanf(arg, "%lg", &val) != 1)
  680.     return "CacheMaxExpire value must be a float";
  681.     psf->cache.maxexpire = (int) (val * (double) SEC_ONE_HR);
  682.     return NULL;
  683. }
  684.  
  685. static const char *
  686.      set_cache_defex(cmd_parms *parms, void *dummy, char *arg)
  687. {
  688.     proxy_server_conf *psf =
  689.     ap_get_module_config(parms->server->module_config, &proxy_module);
  690.     double val;
  691.  
  692.     if (sscanf(arg, "%lg", &val) != 1)
  693.     return "CacheDefaultExpire value must be a float";
  694.     psf->cache.defaultexpire = (int) (val * (double) SEC_ONE_HR);
  695.     return NULL;
  696. }
  697.  
  698. static const char *
  699.      set_cache_gcint(cmd_parms *parms, void *dummy, char *arg)
  700. {
  701.     proxy_server_conf *psf =
  702.     ap_get_module_config(parms->server->module_config, &proxy_module);
  703.     double val;
  704.  
  705.     if (sscanf(arg, "%lg", &val) != 1)
  706.     return "CacheGcInterval value must be a float";
  707.     psf->cache.gcinterval = (int) (val * (double) SEC_ONE_HR);
  708.     return NULL;
  709. }
  710.  
  711. static const char *
  712.      set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg)
  713. {
  714.     proxy_server_conf *psf =
  715.     ap_get_module_config(parms->server->module_config, &proxy_module);
  716.     int val;
  717.  
  718.     val = atoi(arg);
  719.     if (val < 1)
  720.     return "CacheDirLevels value must be an integer greater than 0";
  721.     if (val * psf->cache.dirlength > CACHEFILE_LEN)
  722.     return "CacheDirLevels*CacheDirLength value must not be higher than 20";
  723.     psf->cache.dirlevels = val;
  724.     return NULL;
  725. }
  726.  
  727. static const char *
  728.      set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg)
  729. {
  730.     proxy_server_conf *psf =
  731.     ap_get_module_config(parms->server->module_config, &proxy_module);
  732.     int val;
  733.  
  734.     val = atoi(arg);
  735.     if (val < 1)
  736.     return "CacheDirLength value must be an integer greater than 0";
  737.     if (val * psf->cache.dirlevels > CACHEFILE_LEN)
  738.     return "CacheDirLevels*CacheDirLength value must not be higher than 20";
  739.     psf->cache.dirlength = val;
  740.     return NULL;
  741. }
  742.  
  743. static const char *
  744.      set_cache_exclude(cmd_parms *parms, void *dummy, char *arg)
  745. {
  746.     server_rec *s = parms->server;
  747.     proxy_server_conf *conf =
  748.     ap_get_module_config(s->module_config, &proxy_module);
  749.     struct nocache_entry *new;
  750.     struct nocache_entry *list = (struct nocache_entry *) conf->nocaches->elts;
  751.     struct hostent hp;
  752.     int found = 0;
  753.     int i;
  754.  
  755.     /* Don't duplicate entries */
  756.     for (i = 0; i < conf->nocaches->nelts; i++) {
  757.     if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
  758.         found = 1;
  759.     }
  760.  
  761.     if (!found) {
  762.     new = ap_push_array(conf->nocaches);
  763.     new->name = arg;
  764.     /* Don't do name lookups on things that aren't dotted */
  765.     if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
  766.         /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
  767.         memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
  768.     else
  769.         new->addr.s_addr = 0;
  770.     }
  771.     return NULL;
  772. }
  773.  
  774. static const char *
  775.      set_recv_buffer_size(cmd_parms *parms, void *dummy, char *arg)
  776. {
  777.     proxy_server_conf *psf =
  778.     ap_get_module_config(parms->server->module_config, &proxy_module);
  779.     int s = atoi(arg);
  780.     if (s < 512 && s != 0) {
  781.     return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
  782.     }
  783.  
  784.     psf->recv_buffer_size = s;
  785.     return NULL;
  786. }
  787.  
  788. static const char*
  789.     set_cache_completion(cmd_parms *parms, void *dummy, char *arg)
  790. {
  791.     proxy_server_conf *psf =
  792.     ap_get_module_config(parms->server->module_config, &proxy_module);
  793.     int s = atoi(arg);
  794.     if (s > 100 || s < 0) {
  795.     return "CacheForceCompletion must be <= 100 percent, "
  796.                "or 0 for system default.";
  797.     }
  798.  
  799.     if (s > 0)
  800.       psf->cache.cache_completion = ((float)s / 100);
  801.     return NULL;    
  802. }
  803.  
  804. static const char*
  805.     set_via_opt(cmd_parms *parms, void *dummy, char *arg)
  806. {
  807.     proxy_server_conf *psf =
  808.     ap_get_module_config(parms->server->module_config, &proxy_module);
  809.  
  810.     if (strcasecmp(arg, "Off") == 0)
  811.         psf->viaopt = via_off;
  812.     else if (strcasecmp(arg, "On") == 0)
  813.         psf->viaopt = via_on;
  814.     else if (strcasecmp(arg, "Block") == 0)
  815.         psf->viaopt = via_block;
  816.     else if (strcasecmp(arg, "Full") == 0)
  817.         psf->viaopt = via_full;
  818.     else {
  819.     return "ProxyVia must be one of: "
  820.                "off | on | full | block";
  821.     }
  822.  
  823.     return NULL;    
  824. }
  825.  
  826. static const handler_rec proxy_handlers[] =
  827. {
  828.     {"proxy-server", proxy_handler},
  829.     {NULL}
  830. };
  831.  
  832. static const command_rec proxy_cmds[] =
  833. {
  834.     {"ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG,
  835.      "on if the true proxy requests should be accepted"},
  836.     {"ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2,
  837.      "a scheme, partial URL or '*' and a proxy server"},
  838.     {"ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2,
  839.      "a virtual path and a URL"},
  840.     {"ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF, TAKE2,
  841.      "a virtual path and a URL for reverse proxy behaviour"},
  842.     {"ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, ITERATE,
  843.      "A list of names, hosts or domains to which the proxy will not connect"},
  844.     {"ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, TAKE1,
  845.      "Receive buffer size for outgoing HTTP and FTP connections in bytes"},
  846.     {"NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, ITERATE,
  847.      "A list of domains, hosts, or subnets to which the proxy will connect directly"},
  848.     {"ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, TAKE1,
  849.      "The default intranet domain name (in absence of a domain in the URL)"},
  850.     {"AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, ITERATE,
  851.      "A list of ports which CONNECT may connect to"},
  852.     {"CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1,
  853.      "The directory to store cache files"},
  854.     {"CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1,
  855.      "The maximum disk space used by the cache in Kb"},
  856.     {"CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1,
  857.      "The maximum time in hours to cache a document"},
  858.     {"CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1,
  859.      "The default time in hours to cache a document"},
  860.     {"CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1,
  861.      "The factor used to estimate Expires date from LastModified date"},
  862.     {"CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1,
  863.      "The interval between garbage collections, in hours"},
  864.     {"CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1,
  865.      "The number of levels of subdirectories in the cache"},
  866.     {"CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1,
  867.      "The number of characters in subdirectory names"},
  868.     {"NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
  869.      "A list of names, hosts or domains for which caching is *not* provided"},
  870.     {"CacheForceCompletion", set_cache_completion, NULL, RSRC_CONF, TAKE1,
  871.      "Force a http cache completion after this percentage is loaded"},
  872.     {"ProxyVia", set_via_opt, NULL, RSRC_CONF, TAKE1,
  873.      "Configure Via: proxy header header to one of: on | off | block | full"},
  874.     {NULL}
  875. };
  876.  
  877. module MODULE_VAR_EXPORT proxy_module =
  878. {
  879.     STANDARD_MODULE_STUFF,
  880.     proxy_init,            /* initializer */
  881.     NULL,            /* create per-directory config structure */
  882.     NULL,            /* merge per-directory config structures */
  883.     create_proxy_config,    /* create per-server config structure */
  884.     NULL,            /* merge per-server config structures */
  885.     proxy_cmds,            /* command table */
  886.     proxy_handlers,        /* handlers */
  887.     proxy_trans,        /* translate_handler */
  888.     NULL,            /* check_user_id */
  889.     NULL,            /* check auth */
  890.     NULL,            /* check access */
  891.     NULL,            /* type_checker */
  892.     proxy_fixup,        /* pre-run fixups */
  893.     NULL,            /* logger */
  894.     NULL,            /* header parser */
  895.     NULL,            /* child_init */
  896.     NULL,            /* child_exit */
  897.     proxy_detect        /* post read-request */
  898. };
  899.