/* ====================================================================
 * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */

/*  mod_ip_forwarding:http://dev.w3.org/cgi-bin/cvsweb/apache-modules
 *                         /mod_ip_forwarding/
 *  Copyright © 1995-1999 World Wide Web Consortium, (Massachusetts Institute
 *  of Technology, Institut National de Recherche en Informatique et en
 *  Automatique, Keio University). All Rights Reserved. This program is
 *  distributed under the W3C's Software  Intellectual Property License.
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See W3C License
 *   http://www.w3.org/Consortium/Legal/ for more details.
 */

/*
 * mod_ip_forwarding: IP client@ forwarding between proxies
 *
 * Author: Jose Kahan <kahan@w3.org>
 *
 *     23-August   1998 : Wrote the module from scratch
 *     28-August   1998 : ported to the 1.3.x API
 *     4-November  1998 : added a protection against internal redirects. This
 *                        was giving problems with mod_rewrite
 *
 * Features:
 * Forwards the IP@ address of a client inside a customizable HTTP
 * header. Main application: proxies.
 *
 * If the client send such a header, it'll substitute the value of
 * r->connection->remote_ip with the value given in the header (only in
 * this ASCII temp buffer). This way, CGI scripts can work with the
 * correct IP@ without having to be modified. When received, the header
 *  won't be cleared, so that it's possible to have cascading proxies.
 * The administrator must specify which clients can forward this header. If
 * an untrusted client sends such a header, it'll be removed from the
 * headers and an error message will be logged.
 *
 * Configuration:
 *    ForwardClientIPAddress [on/off (default)]
 *          "Controls sending of the X_Client_Address header"
 *
 *    AcceptForwardedClientIPAddress [on/off (default)]
 *           "Authorizes accepting an X_Client_Address header"
 *
 *    X_ClientIPAddrHeader string
 *        "Customizable header string for sending the client ip _addr"
 *
 *    AuthorizedProxies space separated list
 *     "List of authorized proxies who can send an X_Client_Address header"
 *     *
 * Notes:
 * - Give this module highest priority among the access control modules
 * - To read the user-defined header in another module (in another server!),
 * you just need :
 *
 * ip_forwarding_rec *conf =
        get_module_config (cmd->server->module_config,
                           &w3c_ip_forwarding_module);
 * char *ptr = table_get(r->headers_in, conf->ClientIPAddress_header);
 * Easy :-) except that we need to know the value of w3c_ip_forwarding_rec
 * or, another way, to use the name of the header and use a table_get, or
 * write a public function in this module, ... not so easy after all
 */

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_log.h"

/*------------------------------------------------*/
module MODULE_VAR_EXPORT w3c_ip_forwarding_module;

/*------------------------------------------------
** Private date structures
------------------------------------------------*/
typedef struct w3c_ip_forwarding_struct {
  char *clientIPAddress_header;
  array_header *authorizedProxies;
  int forwardClientIPAddress_flag;
  int acceptForwardedClientIPAddress_flag;
} w3c_ip_forwarding_rec;

typedef struct authorizedProxy_entry_struct {
  char *addr;
} authorizedProxy_entry;

#define DEFAULT_HEADER "X_Fwd_IP_Addr";

/* from the proxy module copy here if we aren't linking it*/
extern const char *
proxy_host2addr(const char *host, struct hostent *reqhp);

/* -------------------------------------------------------------- */
/* Setup configurable data */
/* -------------------------------------------------------------- */

static void *
create_ip_forwarding_config (pool *p, server_rec *d)
{
  w3c_ip_forwarding_rec *conf =
    (w3c_ip_forwarding_rec *) ap_pcalloc (p, sizeof (w3c_ip_forwarding_rec));

  conf->forwardClientIPAddress_flag = 0; /* turned off by default */
  conf->acceptForwardedClientIPAddress_flag = 0; /* turned off by default */
  conf->clientIPAddress_header = DEFAULT_HEADER;
  conf->authorizedProxies =  ap_make_array(p, 10,
                                           sizeof(authorizedProxy_entry));

  return conf;
}

static const char *
set_forwardClientIPAddress_flag (cmd_parms *cmd, void *d, int flag)
{
  w3c_ip_forwarding_rec *conf =
    ap_get_module_config (cmd->server->module_config,
                          &w3c_ip_forwarding_module);

  conf->forwardClientIPAddress_flag = flag;

  return NULL;
}

static const char *
set_acceptForwardedClientIPAddress_flag (cmd_parms *cmd, void *d, int flag)
{
  w3c_ip_forwarding_rec *conf =
    ap_get_module_config (cmd->server->module_config,
                          &w3c_ip_forwarding_module);

  conf->acceptForwardedClientIPAddress_flag = flag;

  return NULL;
}

static const char *
set_clientIPAddress_header (cmd_parms *cmd, void *d, char *header)
{
    w3c_ip_forwarding_rec *conf =
      ap_get_module_config (cmd->server->module_config,
                            &w3c_ip_forwarding_module);

    conf->clientIPAddress_header = header;

  return NULL;
}

static const char *
set_authorizedProxy (cmd_parms *cmd, void *d, char *arg)
{
  server_rec *s = cmd->server;
  w3c_ip_forwarding_rec *conf =
    ap_get_module_config (s->module_config,
                          &w3c_ip_forwarding_module);

  authorizedProxy_entry *new;
  authorizedProxy_entry *proxy_list= (authorizedProxy_entry *)
    conf->authorizedProxies->elts;

  int found = 0;
  int i;

  /* Don't duplicate entries */
  for (i=0; i < conf->authorizedProxies->nelts; i++)
    {
      if (strcmp(arg, proxy_list[i].addr) == 0) {
        found = 1;
        break;
      }

    }

  if (!found)
    {
      if (isdigit (arg[0])) {
        /* the address is already in IP */
        /* I'm lazy and probably could have programmed some call to the DNS */
        new = ap_push_array (conf->authorizedProxies);
        new->addr = arg;
      } else
        return ap_pstrcat(cmd->pool, "Invalid proxy IP address: ",  arg, NULL);
    }

  return NULL;
}

/*
 * List of directives specific to our module.
 */
command_rec w3c_ip_forwarding_cmds[] = {
  {
    "ForwardClientIPAddress",   /* directive name */
    set_forwardClientIPAddress_flag,    /* action routine for directive */
    NULL,                               /* argument to include in call */
    RSRC_CONF,                          /* where available */
    FLAG,                               /* arguments */
    "Controls forwarding of the X_Client_Address header"
    /* directive description */
  },
  {
    "AcceptForwardedClientIPAddress", /* directive name */
    set_acceptForwardedClientIPAddress_flag,
    /* action routine for directive */
    NULL,                                  /* argument to include in call */
    RSRC_CONF,                             /* where available */
    FLAG,                                  /* arguments */
    "Authorizes accepting a forwarded X_Client_Address header"
    /* directive description */
  },
  {
    "X_ClientIPAddrHeader",       /* directive name */
    set_clientIPAddress_header,       /* action routine for directive */
    NULL,                             /* argument to include in call */
    RSRC_CONF,                        /* where available */
    TAKE1,                            /* arguments */
    "Customizable header string for sending the client ip _addr"
    /* directive description */
  },
  {
    "AuthorizedProxies",        /* directive name */
    set_authorizedProxy,        /* action routine for directive */
    NULL,                               /* argument to include in call */
    RSRC_CONF,                  /* where available */
    ITERATE,                            /* arguments */
    "List of authorized proxies who can send an X_Client_Address header"
    /* directive description */
  },
  { NULL }
};

/*------------------------------------------------*/

/*
 * Internal handlers
 */

/*
** does all the ip_forwarding handling
*/

static int
w3c_ip_forwarding_handler (request_rec *r)
{
  server_rec *s = r->server;
  w3c_ip_forwarding_rec *conf =
    ap_get_module_config (s->module_config, &w3c_ip_forwarding_module);
  authorizedProxy_entry *proxy_list= (authorizedProxy_entry *)
    conf->authorizedProxies->elts;
  char *x_client_ip_header = conf->clientIPAddress_header;
  const char *x_ip_addr;
  int i, found;

  /* decline if we're being called inside a subrequest */
  if (r->main || r->prev)
     return DECLINED;

  /* did the client send an x_ip_addr? If yes, verify if this client
     is an authorized proxy. Otherwise, add the header
     */
  x_ip_addr = ap_table_get(r->headers_in, x_client_ip_header);

  if (x_ip_addr) { /* an ip_forward header was sent */
    /* check if the forwarding proxy is authorized to send the client_addr */
    found = 0;
    for (i=0; i < conf->authorizedProxies->nelts; i++)
      {
        if (!strcmp (r->connection->remote_ip, proxy_list[i].addr)) {
          found = 1;
          break;
        }
      }
    if (!found
        && strcmp (r->connection->remote_ip, r->server->addrs->virthost)) {
      /* ip_fwd header didn't come from an authorized proxy or from the
         server, so we remove the header */
      ap_log_error (APLOG_MARK, APLOG_ERR, r->server,
                    "Unauthorized Proxy (%s) tried "
                    "to forward a client IP address (%s)",
                    r->connection->remote_ip, x_ip_addr);
      ap_table_unset (r->headers_in, x_client_ip_header);
    } else {
      /* according to the configuration, we substitute the client's
         IP@ in this step or reforward it. We don't free
         the memory... it'll be done later by apache... but if
         I wanted to do so, what function could I use?
         */
      if (conf->acceptForwardedClientIPAddress_flag)
        r->connection->remote_ip = ap_pstrdup (r->pool,  x_ip_addr);

      if (!conf->forwardClientIPAddress_flag)
        ap_table_unset (r->headers_in, x_client_ip_header);
    }
  } else { /* no ip_forward header was sent */
    /* we didn't find any x_ip_addr header, so we add one, if the
     configurations requests it*/
    if (conf->forwardClientIPAddress_flag )
      {
        x_ip_addr = r->connection->remote_ip;
        ap_table_set  (r->headers_in, x_client_ip_header, x_ip_addr);
      }
  }

  /* other modules may be called after this one */
  return DECLINED;
}

/*------------------------------------------------*/

module MODULE_VAR_EXPORT w3c_ip_forwarding_module = {
  STANDARD_MODULE_STUFF,
  NULL,                        /* initializer */
  NULL,                        /* create per-directory config structure */
  NULL,                        /* merge per-directory config structures */
  create_ip_forwarding_config, /* create per-server config structure */
  NULL,                        /* merge per-server config structures */
  w3c_ip_forwarding_cmds,      /* command table */
  NULL,                        /* handlers */
  NULL,                        /* translate_handler */
  NULL,                        /* check_user_id */
  NULL,                        /* check auth */
  NULL,                        /* check access */
  NULL,                        /* type_checker */
  NULL,                        /* pre-run fixups */
  NULL,                        /* logger */
  NULL,                        /* header parser */
  NULL,                        /* child_init */
  NULL,                        /* child_exit */
  w3c_ip_forwarding_handler    /* post read-request */
};
