Files
apache-http-server/modules/proxy/proxy_util.c
Ryan Bloom 94c63c92a8 We need to setup protocol filters for the fake request. If we don't,
then when any request filters are added, the connection filters will be
lost.  This fixes the proxy.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@93752 13f79535-47bb-0310-9956-ffa450edef68
2002-03-07 06:07:25 +00:00

1106 lines
33 KiB
C

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" 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 name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
/* Utility routines for Apache proxy */
#include "mod_proxy.h"
static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
(request_rec *r, request_rec *pr), (r, pr),
OK, DECLINED)
/* already called in the knowledge that the characters are hex digits */
PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
{
int i, ch;
#if !APR_CHARSET_EBCDIC
ch = x[0];
if (apr_isdigit(ch))
i = ch - '0';
else if (apr_isupper(ch))
i = ch - ('A' - 10);
else
i = ch - ('a' - 10);
i <<= 4;
ch = x[1];
if (apr_isdigit(ch))
i += ch - '0';
else if (apr_isupper(ch))
i += ch - ('A' - 10);
else
i += ch - ('a' - 10);
return i;
#else /*APR_CHARSET_EBCDIC*/
return (1 == sscanf(x, "%2x", &i)) ? os_toebcdic[i&0xFF] : 0;
#endif /*APR_CHARSET_EBCDIC*/
}
PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
{
#if !APR_CHARSET_EBCDIC
int i;
x[0] = '%';
i = (ch & 0xF0) >> 4;
if (i >= 10)
x[1] = ('A' - 10) + i;
else
x[1] = '0' + i;
i = ch & 0x0F;
if (i >= 10)
x[2] = ('A' - 10) + i;
else
x[2] = '0' + i;
#else /*APR_CHARSET_EBCDIC*/
static const char ntoa[] = { "0123456789ABCDEF" };
ch &= 0xFF;
x[0] = '%';
x[1] = ntoa[(os_toascii[ch]>>4)&0x0F];
x[2] = ntoa[os_toascii[ch]&0x0F];
x[3] = '\0';
#endif /*APR_CHARSET_EBCDIC*/
}
/*
* canonicalise a URL-encoded string
*/
/*
* Convert a URL-encoded string to canonical form.
* It decodes characters which need not be encoded,
* and encodes those which must be encoded, and does not touch
* those which must not be touched.
*/
PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
int isenc)
{
int i, j, ch;
char *y;
char *allowed; /* characters which should not be encoded */
char *reserved; /* characters which much not be en/de-coded */
/* N.B. in addition to :@&=, this allows ';' in an http path
* and '?' in an ftp path -- this may be revised
*
* Also, it makes a '+' character in a search string reserved, as
* it may be form-encoded. (Although RFC 1738 doesn't allow this -
* it only permits ; / ? : @ = & as reserved chars.)
*/
if (t == enc_path)
allowed = "$-_.+!*'(),;:@&=";
else if (t == enc_search)
allowed = "$-_.!*'(),;:@&=";
else if (t == enc_user)
allowed = "$-_.+!*'(),;@&=";
else if (t == enc_fpath)
allowed = "$-_.+!*'(),?:@&=";
else /* if (t == enc_parm) */
allowed = "$-_.+!*'(),?/:@&=";
if (t == enc_path)
reserved = "/";
else if (t == enc_search)
reserved = "+";
else
reserved = "";
y = apr_palloc(p, 3 * len + 1);
for (i = 0, j = 0; i < len; i++, j++) {
/* always handle '/' first */
ch = x[i];
if (strchr(reserved, ch)) {
y[j] = ch;
continue;
}
/* decode it if not already done */
if (isenc && ch == '%') {
if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2]))
return NULL;
ch = ap_proxy_hex2c(&x[i + 1]);
i += 2;
if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */
ap_proxy_c2hex(ch, &y[j]);
j += 2;
continue;
}
}
/* recode it, if necessary */
if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
ap_proxy_c2hex(ch, &y[j]);
j += 2;
}
else
y[j] = ch;
}
y[j] = '\0';
return y;
}
/*
* Parses network-location.
* urlp on input the URL; on output the path, after the leading /
* user NULL if no user/password permitted
* password holder for password
* host holder for host
* port port number; only set if one is supplied.
*
* Returns an error string.
*/
PROXY_DECLARE(char *)
ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
char **passwordp, char **hostp, apr_port_t *port)
{
apr_port_t i;
char *strp, *host, *url = *urlp;
char *user = NULL, *password = NULL;
if (url[0] != '/' || url[1] != '/')
return "Malformed URL";
host = url + 2;
url = strchr(host, '/');
if (url == NULL)
url = "";
else
*(url++) = '\0'; /* skip seperating '/' */
/* find _last_ '@' since it might occur in user/password part */
strp = strrchr(host, '@');
if (strp != NULL) {
*strp = '\0';
user = host;
host = strp + 1;
/* find password */
strp = strchr(user, ':');
if (strp != NULL) {
*strp = '\0';
password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1);
if (password == NULL)
return "Bad %-escape in URL (password)";
}
user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1);
if (user == NULL)
return "Bad %-escape in URL (username)";
}
if (userp != NULL) {
*userp = user;
}
if (passwordp != NULL) {
*passwordp = password;
}
strp = strrchr(host, ':');
if (strp != NULL) {
*(strp++) = '\0';
for (i = 0; strp[i] != '\0'; i++)
if (!apr_isdigit(strp[i]))
break;
/* if (i == 0) the no port was given; keep default */
if (strp[i] != '\0') {
return "Bad port number in URL";
} else if (i > 0) {
*port = atoi(strp);
if (*port > 65535)
return "Port number in URL > 65535";
}
}
ap_str_tolower(host); /* DNS names are case-insensitive */
if (*host == '\0')
return "Missing host in URL";
/* check hostname syntax */
for (i = 0; host[i] != '\0'; i++)
if (!apr_isdigit(host[i]) && host[i] != '.')
break;
/* must be an IP address */
if (host[i] == '\0' && (apr_inet_addr(host) == -1))
{
return "Bad IP address in URL";
}
/* if (strchr(host,'.') == NULL && domain != NULL)
host = pstrcat(p, host, domain, NULL);
*/
*urlp = url;
*hostp = host;
return NULL;
}
static const char * const lwday[7] =
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
/*
* If the date is a valid RFC 850 date or asctime() date, then it
* is converted to the RFC 1123 format, otherwise it is not modified.
* This routine is not very fast at doing conversions, as it uses
* sscanf and sprintf. However, if the date is already correctly
* formatted, then it exits very quickly.
*/
PROXY_DECLARE(const char *)
ap_proxy_date_canon(apr_pool_t *p, const char *x1)
{
char *x = apr_pstrdup(p, x1);
int wk, mday, year, hour, min, sec, mon;
char *q, month[4], zone[4], week[4];
q = strchr(x, ',');
/* check for RFC 850 date */
if (q != NULL && q - x > 3 && q[1] == ' ') {
*q = '\0';
for (wk = 0; wk < 7; wk++)
if (strcmp(x, lwday[wk]) == 0)
break;
*q = ',';
if (wk == 7)
return x; /* not a valid date */
if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
q[17] != ':' || strcmp(&q[20], " GMT") != 0)
return x;
if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
&hour, &min, &sec, zone) != 7)
return x;
if (year < 70)
year += 2000;
else
year += 1900;
}
else {
/* check for acstime() date */
if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
x[16] != ':' || x[19] != ' ' || x[24] != '\0')
return x;
if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
&min, &sec, &year) != 7)
return x;
for (wk = 0; wk < 7; wk++)
if (strcmp(week, ap_day_snames[wk]) == 0)
break;
if (wk == 7)
return x;
}
/* check date */
for (mon = 0; mon < 12; mon++)
if (strcmp(month, ap_month_snames[mon]) == 0)
break;
if (mon == 12)
return x;
q = apr_palloc(p, 30);
apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[wk],
mday, ap_month_snames[mon], year, hour, min, sec);
return q;
}
PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
{
request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
rp->pool = c->pool;
rp->status = HTTP_OK;
rp->headers_in = apr_table_make(c->pool, 50);
rp->subprocess_env = apr_table_make(c->pool, 50);
rp->headers_out = apr_table_make(c->pool, 12);
rp->err_headers_out = apr_table_make(c->pool, 5);
rp->notes = apr_table_make(c->pool, 5);
rp->server = r->server;
rp->proxyreq = r->proxyreq;
rp->request_time = r->request_time;
rp->connection = c;
rp->output_filters = c->output_filters;
rp->input_filters = c->input_filters;
rp->proto_output_filters = c->output_filters;
rp->proto_input_filters = c->input_filters;
rp->request_config = ap_create_request_config(c->pool);
proxy_run_create_req(r, rp);
return rp;
}
/*
* Reads headers from a buffer and returns an array of headers.
* Returns NULL on file error
* This routine tries to deal with too long lines and continuation lines.
* @@@: XXX: FIXME: currently the headers are passed thru un-merged.
* Is that okay, or should they be collapsed where possible?
*/
PROXY_DECLARE(apr_table_t *)ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c)
{
apr_table_t *headers_out;
int len;
char *value, *end;
char field[MAX_STRING_LEN];
headers_out = ap_make_table(r->pool, 20);
/*
* Read header lines until we get the empty separator line, a read error,
* the connection closes (EOF), or we timeout.
*/
while ((len = ap_getline(buffer, size, rr, 1)) > 0) {
if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */
/* Buggy MS IIS servers sometimes return invalid headers
* (an extra "HTTP/1.0 200, OK" line sprinkled in between
* the usual MIME headers). Try to deal with it in a sensible
* way, but log the fact.
* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
/* Nope, it wasn't even an extra HTTP header. Give up. */
return NULL;
}
ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server,
"proxy: Ignoring duplicate HTTP header "
"returned by %s (%s)", r->uri, r->method);
continue;
}
*value = '\0';
++value;
/* XXX: RFC2068 defines only SP and HT as whitespace, this test is
* wrong... and so are many others probably.
*/
while (apr_isspace(*value))
++value; /* Skip to start of value */
/* should strip trailing whitespace as well */
for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end)
*end = '\0';
/* make sure we merge so as not to destroy duplicated headers */
apr_table_merge(headers_out, buffer, value);
/* the header was too long; at the least we should skip extra data */
if (len >= size - 1) {
while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1))
>= MAX_STRING_LEN - 1) {
/* soak up the extra data */
}
if (len == 0) /* time to exit the larger loop as well */
break;
}
}
return headers_out;
}
/*
* list is a comma-separated list of case-insensitive tokens, with
* optional whitespace around the tokens.
* The return returns 1 if the token val is found in the list, or 0
* otherwise.
*/
PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
{
int len, i;
const char *p;
len = strlen(val);
while (list != NULL) {
p = ap_strchr_c(list, ',');
if (p != NULL) {
i = p - list;
do
p++;
while (apr_isspace(*p));
}
else
i = strlen(list);
while (i > 0 && apr_isspace(list[i - 1]))
i--;
if (i == len && strncasecmp(list, val, len) == 0)
return 1;
list = p;
}
return 0;
}
/*
* list is a comma-separated list of case-insensitive tokens, with
* optional whitespace around the tokens.
* The return returns 1 if the token val is found in the list, or 0
* otherwise.
*/
PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
{
int len, i;
const char *p;
char *new = NULL;
len = strlen(val);
while (list != NULL) {
p = ap_strchr_c(list, ',');
if (p != NULL) {
i = p - list;
do
p++;
while (apr_isspace(*p));
}
else
i = strlen(list);
while (i > 0 && apr_isspace(list[i - 1]))
i--;
if (i == len && strncasecmp(list, val, len) == 0) {
/* do nothing */
}
else {
if (new)
new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
else
new = apr_pstrndup(pool, list, i);
}
list = p;
}
return new;
}
/*
* Converts 8 hex digits to a time integer
*/
PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
{
int i, ch;
unsigned int j;
for (i = 0, j = 0; i < 8; i++) {
ch = x[i];
j <<= 4;
if (apr_isdigit(ch))
j |= ch - '0';
else if (apr_isupper(ch))
j |= ch - ('A' - 10);
else
j |= ch - ('a' - 10);
}
if (j == 0xffffffff)
return -1; /* so that it works with 8-byte ints */
else
return j;
}
/*
* Converts a time integer to 8 hex digits
*/
PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)
{
int i, ch;
unsigned int j = t;
for (i = 7; i >= 0; i--) {
ch = j & 0xF;
j >>= 4;
if (ch >= 10)
y[i] = ch + ('A' - 10);
else
y[i] = ch + '0';
}
y[8] = '\0';
}
PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)
{
apr_table_setn(r->notes, "error-notes",
apr_pstrcat(r->pool,
"The proxy server could not handle the request "
"<EM><A HREF=\"", ap_escape_uri(r->pool, r->uri),
"\">", ap_escape_html(r->pool, r->method),
"&nbsp;",
ap_escape_html(r->pool, r->uri), "</A></EM>.<P>\n"
"Reason: <STRONG>",
ap_escape_html(r->pool, message),
"</STRONG>", NULL));
/* Allow "error-notes" string to be printed by ap_send_error_response() */
apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
"proxy: %s returned by %s", message, r->uri);
return statuscode;
}
static const char *
proxy_get_host_of_request(request_rec *r)
{
char *url, *user = NULL, *password = NULL, *err, *host;
apr_port_t port = -1;
if (r->hostname != NULL)
return r->hostname;
/* Set url to the first char after "scheme://" */
if ((url = strchr(r->uri, ':')) == NULL
|| url[1] != '/' || url[2] != '/')
return NULL;
url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */
err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
if (err != NULL)
ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
"%s", err);
r->hostname = host;
return host; /* ought to return the port, too */
}
/* Return TRUE if addr represents an IP address (or an IP network address) */
PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
{
const char *addr = This->name;
long ip_addr[4];
int i, quads;
long bits;
/* if the address is given with an explicit netmask, use that */
/* Due to a deficiency in apr_inet_addr(), it is impossible to parse */
/* "partial" addresses (with less than 4 quads) correctly, i.e. */
/* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
/* I therefore have to parse the IP address manually: */
/*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */
/* addr and mask were set by proxy_readmask() */
/*return 1; */
/* Parse IP addr manually, optionally allowing */
/* abbreviated net addresses like 192.168. */
/* Iterate over up to 4 (dotted) quads. */
for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
char *tmp;
if (*addr == '/' && quads > 0) /* netmask starts here. */
break;
if (!apr_isdigit(*addr))
return 0; /* no digit at start of quad */
ip_addr[quads] = strtol(addr, &tmp, 0);
if (tmp == addr) /* expected a digit, found something else */
return 0;
if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
/* invalid octet */
return 0;
}
addr = tmp;
if (*addr == '.' && quads != 3)
++addr; /* after the 4th quad, a dot would be illegal */
}
for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */
char *tmp;
++addr;
bits = strtol(addr, &tmp, 0);
if (tmp == addr) /* expected a digit, found something else */
return 0;
addr = tmp;
if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */
return 0;
}
else {
/* Determine (i.e., "guess") netmask by counting the */
/* number of trailing .0's; reduce #quads appropriately */
/* (so that 192.168.0.0 is equivalent to 192.168.) */
while (quads > 0 && ip_addr[quads - 1] == 0)
--quads;
/* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
if (quads < 1)
return 0;
/* every zero-byte counts as 8 zero-bits */
bits = 8 * quads;
if (bits != 32) /* no warning for fully qualified IP address */
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
inet_ntoa(This->addr), bits);
}
This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));
if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"Warning: NetMask and IP-Addr disagree in %s/%ld\n",
inet_ntoa(This->addr), bits);
This->addr.s_addr &= This->mask.s_addr;
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
" Set to %s/%ld\n",
inet_ntoa(This->addr), bits);
}
if (*addr == '\0') {
This->matcher = proxy_match_ipaddr;
return 1;
}
else
return (*addr == '\0'); /* okay iff we've parsed the whole string */
}
/* Return TRUE if addr represents an IP address (or an IP network address) */
static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
{
int i, ip_addr[4];
struct in_addr addr, *ip;
const char *host = proxy_get_host_of_request(r);
if (host == NULL) /* oops! */
return 0;
memset(&addr, '\0', sizeof addr);
memset(ip_addr, '\0', sizeof ip_addr);
if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
for (addr.s_addr = 0, i = 0; i < 4; ++i)
addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
#if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s/", inet_ntoa(This->addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s", inet_ntoa(This->mask));
#endif
return 1;
}
#if DEBUGGING
else {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s/", inet_ntoa(This->addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s", inet_ntoa(This->mask));
}
#endif
}
else {
struct apr_sockaddr_t *reqaddr;
if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)
!= APR_SUCCESS) {
#if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"2)IP-NoMatch: hostname=%s msg=Host not found",
host);
#endif
return 0;
}
/* Try to deal with multiple IP addr's for a host */
/* FIXME: This needs to be able to deal with IPv6 */
while (reqaddr) {
ip = (struct in_addr *) reqaddr->ipaddr_ptr;
if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {
#if DEBUGGING
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"3)IP-Match: %s[%s] <-> ", host,
inet_ntoa(*ip));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s/", inet_ntoa(This->addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s", inet_ntoa(This->mask));
#endif
return 1;
}
#if DEBUGGING
else {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"3)IP-NoMatch: %s[%s] <-> ", host,
inet_ntoa(*ip));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s/", inet_ntoa(This->addr));
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"%s", inet_ntoa(This->mask));
}
#endif
reqaddr = reqaddr->next;
}
}
return 0;
}
/* Return TRUE if addr represents a domain name */
PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)
{
char *addr = This->name;
int i;
/* Domain name must start with a '.' */
if (addr[0] != '.')
return 0;
/* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
continue;
#if 0
if (addr[i] == ':') {
ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
"@@@@ handle optional port in proxy_is_domainname()");
/* @@@@ handle optional port */
}
#endif
if (addr[i] != '\0')
return 0;
/* Strip trailing dots */
for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
addr[i] = '\0';
This->matcher = proxy_match_domainname;
return 1;
}
/* Return TRUE if host "host" is in domain "domain" */
static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
{
const char *host = proxy_get_host_of_request(r);
int d_len = strlen(This->name), h_len;
if (host == NULL) /* some error was logged already */
return 0;
h_len = strlen(host);
/* @@@ do this within the setup? */
/* Ignore trailing dots in domain comparison: */
while (d_len > 0 && This->name[d_len - 1] == '.')
--d_len;
while (h_len > 0 && host[h_len - 1] == '.')
--h_len;
return h_len > d_len
&& strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
}
/* Return TRUE if host represents a host name */
PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)
{
struct apr_sockaddr_t *addr;
char *host = This->name;
int i;
/* Host names must not start with a '.' */
if (host[0] == '.')
return 0;
/* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);
if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS)
return 0;
This->hostaddr = addr;
/* Strip trailing dots */
for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i)
host[i] = '\0';
This->matcher = proxy_match_hostname;
return 1;
}
/* Return TRUE if host "host" is equal to host2 "host2" */
static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
{
char *host = This->name;
const char *host2 = proxy_get_host_of_request(r);
int h2_len;
int h1_len;
if (host == NULL || host2 == NULL)
return 0; /* oops! */
h2_len = strlen(host2);
h1_len = strlen(host);
#if 0
struct apr_sockaddr_t *addr = *This->hostaddr;
/* Try to deal with multiple IP addr's for a host */
while (addr) {
if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)
return 1;
addr = addr->next;
}
#endif
/* Ignore trailing dots in host2 comparison: */
while (h2_len > 0 && host2[h2_len - 1] == '.')
--h2_len;
while (h1_len > 0 && host[h1_len - 1] == '.')
--h1_len;
return h1_len == h2_len
&& strncasecmp(host, host2, h1_len) == 0;
}
/* Return TRUE if addr is to be matched as a word */
PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)
{
This->matcher = proxy_match_word;
return 1;
}
/* Return TRUE if string "str2" occurs literally in "str1" */
static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
{
const char *host = proxy_get_host_of_request(r);
return host != NULL && ap_strstr_c(host, This->name) != NULL;
}
/* checks whether a host in uri_addr matches proxyblock */
PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf,
apr_sockaddr_t *uri_addr)
{
int j;
/* XXX FIXME: conf->noproxies->elts is part of an opaque structure */
for (j = 0; j < conf->noproxies->nelts; j++) {
struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
struct apr_sockaddr_t *conf_addr = npent[j].addr;
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
"proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);
if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))
|| npent[j].name[0] == '*') {
ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server,
"proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);
return HTTP_FORBIDDEN;
}
while (conf_addr) {
while (uri_addr) {
char *conf_ip;
char *uri_ip;
apr_sockaddr_ip_get(&conf_ip, conf_addr);
apr_sockaddr_ip_get(&uri_ip, uri_addr);
ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r->server,
"proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);
if (!apr_strnatcasecmp(conf_ip, uri_ip)) {
ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r->server,
"proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);
return HTTP_FORBIDDEN;
}
uri_addr = uri_addr->next;
}
conf_addr = conf_addr->next;
}
}
return OK;
}
/* set up the minimal filter set */
PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)
{
ap_add_input_filter("HTTP_IN", NULL, r, c);
return OK;
}
/* converts a series of buckets into a string
* XXX: BillS says this function performs essentially the same function as
* ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline()
* instead? I think ap_proxy_string_read() will not work properly on non ASCII
* (EBCDIC) machines either.
*/
PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,
char *buff, size_t bufflen, int *eos)
{
apr_bucket *e;
apr_status_t rv;
char *pos = buff;
char *response;
int found = 0;
apr_size_t len;
/* start with an empty string */
buff[0] = 0;
*eos = 0;
/* loop through each brigade */
while (!found) {
/* get brigade from network one line at a time */
if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb,
AP_MODE_GETLINE,
APR_BLOCK_READ,
0))) {
return rv;
}
/* loop through each bucket */
while (!found) {
if (*eos || APR_BRIGADE_EMPTY(bb)) {
/* The connection aborted or timed out */
return APR_ECONNABORTED;
}
e = APR_BRIGADE_FIRST(bb);
if (APR_BUCKET_IS_EOS(e)) {
*eos = 1;
}
else {
if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) {
return rv;
}
/* is string LF terminated?
* XXX: This check can be made more efficient by simply checking
* if the last character in the 'response' buffer is an ASCII_LF.
* See ap_rgetline() for an example.
*/
if (memchr(response, APR_ASCII_LF, len)) {
found = 1;
}
/* concat strings until buff is full - then throw the data away */
if (len > ((bufflen-1)-(pos-buff))) {
len = (bufflen-1)-(pos-buff);
}
if (len > 0) {
pos = apr_cpystrn(pos, response, len);
}
}
APR_BUCKET_REMOVE(e);
apr_bucket_destroy(e);
}
}
return APR_SUCCESS;
}
/* remove other filters (like DECHUNK) from filter stack */
PROXY_DECLARE(void) ap_proxy_reset_output_filters(conn_rec *c)
{
ap_filter_t *f = c->output_filters;
while (f) {
if (!strcasecmp(f->frec->name, "CORE") ||
!strcasecmp(f->frec->name, "CONTENT_LENGTH") ||
!strcasecmp(f->frec->name, "HTTP_HEADER")) {
f = f->next;
continue;
}
else {
ap_remove_output_filter(f);
f = f->next;
}
}
}
/* unmerge an element in the table */
PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)
{
apr_off_t offset = 0;
apr_off_t count = 0;
char *value = NULL;
/* get the value to unmerge */
const char *initial = apr_table_get(t, key);
if (!initial) {
return;
}
value = apr_pstrdup(p, initial);
/* remove the value from the headers */
apr_table_unset(t, key);
/* find each comma */
while (value[count]) {
if (value[count] == ',') {
value[count] = 0;
apr_table_add(t, key, value + offset);
offset = count + 1;
}
count++;
}
apr_table_add(t, key, value + offset);
}