summaryrefslogtreecommitdiffstats
path: root/rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c
diff options
context:
space:
mode:
authorhongbotian <hongbo.tianhongbo@huawei.com>2015-11-30 01:45:08 -0500
committerhongbotian <hongbo.tianhongbo@huawei.com>2015-11-30 01:45:08 -0500
commite8ec7aa8e38a93f5b034ac74cebce5de23710317 (patch)
treeaa031937bf856c1f8d6ad7877b8d2cb0224da5ef /rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c
parentcc40af334e619bb549038238507407866f774f8f (diff)
upload http
JIRA: BOTTLENECK-10 Change-Id: I7598427ff904df438ce77c2819ee48ac75ffa8da Signed-off-by: hongbotian <hongbo.tianhongbo@huawei.com>
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c')
-rw-r--r--rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c1120
1 files changed, 1120 insertions, 0 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c
new file mode 100644
index 00000000..bab945f5
--- /dev/null
+++ b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c
@@ -0,0 +1,1120 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* 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*/
+ /* we assume that the hex value refers to an ASCII character
+ * so convert to EBCDIC so that it makes sense locally;
+ *
+ * example:
+ *
+ * client specifies %20 in URL to refer to a space char;
+ * at this point we're called with EBCDIC "20"; after turning
+ * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
+ * represents an ASCII char and convert 0x20 to EBCDIC, yielding
+ * 0x40
+ */
+ char buf[1];
+
+ if (1 == sscanf(x, "%2x", &i)) {
+ buf[0] = i & 0xFF;
+ ap_xlate_proto_from_ascii(buf, 1);
+ return buf[0];
+ }
+ else {
+ return 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" };
+ char buf[1];
+
+ ch &= 0xFF;
+
+ buf[0] = ch;
+ ap_xlate_proto_to_ascii(buf, 1);
+
+ x[0] = '%';
+ x[1] = ntoa[(buf[0] >> 4) & 0x0F];
+ x[2] = ntoa[buf[0] & 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 && (isenc != PROXYREQ_REVERSE) && (ch == '%')) {
+ if (!apr_isxdigit(x[i + 1]) || !apr_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)
+{
+ char *addr, *scope_id, *strp, *host, *url = *urlp;
+ char *user = NULL, *password = NULL;
+ apr_port_t tmp_port;
+ apr_status_t rv;
+
+ 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;
+ }
+
+ /* Parse the host string to separate host portion from optional port.
+ * Perform range checking on port.
+ */
+ rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
+ if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
+ return "Invalid host/port";
+ }
+ if (tmp_port != 0) { /* only update caller's port if port was specified */
+ *port = tmp_port;
+ }
+
+ ap_str_tolower(addr); /* DNS names are case-insensitive */
+
+ *urlp = url;
+ *hostp = addr;
+
+ return NULL;
+}
+
+/*
+ * If the date is a valid RFC 850 date or asctime() date, then it
+ * is converted to the RFC 1123 format.
+ */
+PROXY_DECLARE(const char *)
+ ap_proxy_date_canon(apr_pool_t *p, const char *date)
+{
+ apr_status_t rv;
+ char* ndate;
+
+ apr_time_t time = apr_date_parse_http(date);
+ if (!time) {
+ return date;
+ }
+
+ ndate = apr_palloc(p, APR_RFC822_DATE_LEN);
+ rv = apr_rfc822_date(ndate, time);
+ if (rv != APR_SUCCESS) {
+ return date;
+ }
+
+ return ndate;
+}
+
+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.
+ *
+ * Note: Currently the headers are passed through unmerged. This has to be
+ * done so that headers which react badly to merging (such as Set-Cookie
+ * headers, which contain commas within the date field) do not get stuffed
+ * up.
+ */
+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];
+ int saw_headers = 0;
+ void *sconf = r->server->module_config;
+ proxy_server_conf *psc;
+
+ psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+
+ headers_out = apr_table_make(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 */
+
+ /* We may encounter invalid headers, usually from buggy
+ * MS IIS servers, so we need to determine just how to handle
+ * them. We can either ignore them, assume that they mark the
+ * start-of-body (eg: a missing CRLF) or (the default) mark
+ * the headers as totally bogus and return a 500. The sole
+ * exception is an extra "HTTP/1.0 200, OK" line sprinkled
+ * in between the usual MIME headers, which is a favorite
+ * IIS bug.
+ */
+ /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */
+
+ if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
+ if (psc->badopt == bad_error) {
+ /* Nope, it wasn't even an extra HTTP header. Give up. */
+ return NULL;
+ }
+ else if (psc->badopt == bad_body) {
+ /* if we've already started loading headers_out, then
+ * return what we've accumulated so far, in the hopes
+ * that they are useful. Otherwise, we completely bail.
+ */
+ /* FIXME: We've already scarfed the supposed 1st line of
+ * the body, so the actual content may end up being bogus
+ * as well. If the content is HTML, we may be lucky.
+ */
+ if (saw_headers) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: Starting body due to bogus non-header in headers "
+ "returned by %s (%s)", r->uri, r->method);
+ return headers_out;
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: No HTTP headers "
+ "returned by %s (%s)", r->uri, r->method);
+ return NULL;
+ }
+ }
+ }
+ /* this is the psc->badopt == bad_ignore case */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "proxy: Ignoring bogus 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 add so as not to destroy duplicated headers */
+ apr_table_add(headers_out, buffer, value);
+ saw_headers = 1;
+
+ /* 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.
+ * if val appears on the list of tokens, it is removed from the list,
+ * and the new list is returned.
+ */
+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></p>", 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, 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;
+
+ 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, 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, 0, NULL,
+ "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld",
+ 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, 0, NULL,
+ "Warning: NetMask and IP-Addr disagree in %s/%ld",
+ inet_ntoa(This->addr), bits);
+ This->addr.s_addr &= This->mask.s_addr;
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ " Set to %s/%ld",
+ 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, 0, NULL,
+ "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s", inet_ntoa(This->mask));
+#endif
+ return 1;
+ }
+#if DEBUGGING
+ else {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 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, 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, 0, NULL,
+ "3)IP-Match: %s[%s] <-> ", host,
+ inet_ntoa(*ip));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s", inet_ntoa(This->mask));
+#endif
+ return 1;
+ }
+#if DEBUGGING
+ else {
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "3)IP-NoMatch: %s[%s] <-> ", host,
+ inet_ntoa(*ip));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "%s/", inet_ntoa(This->addr));
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 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, 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;
+ apr_sockaddr_t * src_uri_addr = uri_addr;
+ /* 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;
+ uri_addr = src_uri_addr;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 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, 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, 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, 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, apr_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;
+}
+
+/* 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);
+}
+
+PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,
+ const char *proxy_function,
+ apr_sockaddr_t *backend_addr,
+ const char *backend_name,
+ proxy_server_conf *conf,
+ server_rec *s,
+ apr_pool_t *p)
+{
+ apr_status_t rv;
+ int connected = 0;
+ int loglevel;
+
+ while (backend_addr && !connected) {
+ if ((rv = apr_socket_create(newsock, backend_addr->family,
+ SOCK_STREAM, p)) != APR_SUCCESS) {
+ loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+ ap_log_error(APLOG_MARK, loglevel, rv, s,
+ "proxy: %s: error creating fam %d socket for target %s",
+ proxy_function,
+ backend_addr->family,
+ backend_name);
+ /* this could be an IPv6 address from the DNS but the
+ * local machine won't give us an IPv6 socket; hopefully the
+ * DNS returned an additional address to try
+ */
+ backend_addr = backend_addr->next;
+ continue;
+ }
+
+#if !defined(TPF) && !defined(BEOS)
+ if (conf->recv_buffer_size > 0 &&
+ (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,
+ conf->recv_buffer_size))) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+ "apr_socket_opt_set(SO_RCVBUF): Failed to set "
+ "ProxyReceiveBufferSize, using default");
+ }
+#endif
+
+ /* Set a timeout on the socket */
+ if (conf->timeout_set == 1) {
+ apr_socket_timeout_set(*newsock, conf->timeout);
+ }
+ else {
+ apr_socket_timeout_set(*newsock, s->timeout);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "proxy: %s: fam %d socket created to connect to %s",
+ proxy_function, backend_addr->family, backend_name);
+
+ /* make the connection out of the socket */
+ rv = apr_connect(*newsock, backend_addr);
+
+ /* if an error occurred, loop round and try again */
+ if (rv != APR_SUCCESS) {
+ apr_socket_close(*newsock);
+ loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
+ ap_log_error(APLOG_MARK, loglevel, rv, s,
+ "proxy: %s: attempt to connect to %pI (%s) failed",
+ proxy_function,
+ backend_addr,
+ backend_name);
+ backend_addr = backend_addr->next;
+ continue;
+ }
+ connected = 1;
+ }
+ return connected ? 0 : 1;
+}
+