proxy_util.c revision 5d392744e2077f71f34ce098ab49d2c0ddcf4ea3
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * applicable.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * Licensed under the Apache License, Version 2.0 (the "License");
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * you may not use this file except in compliance with the License.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * You may obtain a copy of the License at
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * http://www.apache.org/licenses/LICENSE-2.0
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * Unless required by applicable law or agreed to in writing, software
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * distributed under the License is distributed on an "AS IS" BASIS,
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * See the License for the specific language governing permissions and
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * limitations under the License.
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder */
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder/* Utility routines for Apache proxy */
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder#include "mod_proxy.h"
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder#include "ap_mpm.h"
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder#include "scoreboard.h"
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder#include "apr_version.h"
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder#if APR_HAVE_UNISTD_H
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#include <unistd.h> /* for getpid() */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#endif
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#if (APR_MAJOR_VERSION < 1)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#undef apr_socket_create
10b02b2343246df6773585636fe3ddbefa3b6a1bChristian Maeder#define apr_socket_create apr_socket_create_ex
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#endif
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/* Global balancer counter */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maederint PROXY_DECLARE_DATA proxy_lb_workers = 0;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maederstatic int lb_workers_limit = 0;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maederstatic int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maederstatic int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maederstatic int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maederstatic int proxy_match_word(struct dirconn_entry *This, request_rec *r);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian MaederAPR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder (request_rec *r, request_rec *pr), (r, pr),
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder OK, DECLINED)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/* already called in the knowledge that the characters are hex digits */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian MaederPROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder{
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder int i, ch;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#if !APR_CHARSET_EBCDIC
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder ch = x[0];
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (apr_isdigit(ch))
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder i = ch - '0';
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else if (apr_isupper(ch))
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder i = ch - ('A' - 10);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder i = ch - ('a' - 10);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder i <<= 4;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder ch = x[1];
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder if (apr_isdigit(ch))
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder i += ch - '0';
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder else if (apr_isupper(ch))
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder i += ch - ('A' - 10);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder i += ch - ('a' - 10);
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder return i;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#else /*APR_CHARSET_EBCDIC*/
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder /*
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * we assume that the hex value refers to an ASCII character
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * so convert to EBCDIC so that it makes sense locally;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * example:
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * client specifies %20 in URL to refer to a space char;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * at this point we're called with EBCDIC "20"; after turning
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * EBCDIC "20" into binary 0x20, we then need to assume that 0x20
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * represents an ASCII char and convert 0x20 to EBCDIC, yielding
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * 0x40
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder */
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder char buf[1];
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder if (1 == sscanf(x, "%2x", &i)) {
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder buf[0] = i & 0xFF;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder ap_xlate_proto_from_ascii(buf, 1);
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder return buf[0];
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder }
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else {
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return 0;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder }
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#endif /*APR_CHARSET_EBCDIC*/
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder}
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian MaederPROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder{
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#if !APR_CHARSET_EBCDIC
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder int i;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder x[0] = '%';
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder i = (ch & 0xF0) >> 4;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (i >= 10)
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder x[1] = ('A' - 10) + i;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder x[1] = '0' + i;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder i = ch & 0x0F;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (i >= 10)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder x[2] = ('A' - 10) + i;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder x[2] = '0' + i;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder#else /*APR_CHARSET_EBCDIC*/
f52a4838c101d52bbbd689f6b51f2c1c9202f0a8Christian Maeder static const char ntoa[] = { "0123456789ABCDEF" };
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder char buf[1];
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder ch &= 0xFF;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder buf[0] = ch;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder ap_xlate_proto_to_ascii(buf, 1);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder x[0] = '%';
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder x[1] = ntoa[(buf[0] >> 4) & 0x0F];
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder x[2] = ntoa[buf[0] & 0x0F];
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder x[3] = '\0';
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder#endif /*APR_CHARSET_EBCDIC*/
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder}
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/*
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * canonicalise a URL-encoded string
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/*
adda0e6252b14215228e4071b347c49b808894f8Christian Maeder * Convert a URL-encoded string to canonical form.
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * It decodes characters which need not be encoded,
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * and encodes those which must be encoded, and does not touch
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * those which must not be touched.
adda0e6252b14215228e4071b347c49b808894f8Christian Maeder */
adda0e6252b14215228e4071b347c49b808894f8Christian MaederPROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
adda0e6252b14215228e4071b347c49b808894f8Christian Maeder int forcedec, int proxyreq)
adda0e6252b14215228e4071b347c49b808894f8Christian Maeder{
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder int i, j, ch;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder char *y;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder char *allowed; /* characters which should not be encoded */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder char *reserved; /* characters which much not be en/de-coded */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/*
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * N.B. in addition to :@&=, this allows ';' in an http path
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * and '?' in an ftp path -- this may be revised
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * Also, it makes a '+' character in a search string reserved, as
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * it may be form-encoded. (Although RFC 1738 doesn't allow this -
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * it only permits ; / ? : @ = & as reserved chars.)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (t == enc_path)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder allowed = "$-_.+!*'(),;:@&=";
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else if (t == enc_search)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder allowed = "$-_.!*'(),;:@&=";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder else if (t == enc_user)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder allowed = "$-_.+!*'(),;@&=";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder else if (t == enc_fpath)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder allowed = "$-_.+!*'(),?:@&=";
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else /* if (t == enc_parm) */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder allowed = "$-_.+!*'(),?/:@&=";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (t == enc_path)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder reserved = "/";
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else if (t == enc_search)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder reserved = "+";
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder reserved = "";
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder y = apr_palloc(p, 3 * len + 1);
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder for (i = 0, j = 0; i < len; i++, j++) {
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder/* always handle '/' first */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder ch = x[i];
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (strchr(reserved, ch)) {
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder y[j] = ch;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder continue;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder }
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/*
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * decode it if not already done. do not decode reverse proxied URLs
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * unless specifically forced
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder return NULL;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder ch = ap_proxy_hex2c(&x[i + 1]);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder i += 2;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder ap_proxy_c2hex(ch, &y[j]);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder j += 2;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder continue;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder }
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder }
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/* recode it, if necessary */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder ap_proxy_c2hex(ch, &y[j]);
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder j += 2;
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder }
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder else
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder y[j] = ch;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder }
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder y[j] = '\0';
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return y;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder}
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/*
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * Parses network-location.
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * urlp on input the URL; on output the path, after the leading /
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * user NULL if no user/password permitted
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * password holder for password
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * host holder for host
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * port port number; only set if one is supplied.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * Returns an error string.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian MaederPROXY_DECLARE(char *)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder char **passwordp, char **hostp, apr_port_t *port)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder{
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder char *addr, *scope_id, *strp, *host, *url = *urlp;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder char *user = NULL, *password = NULL;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder apr_port_t tmp_port;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder apr_status_t rv;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (url[0] != '/' || url[1] != '/')
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return "Malformed URL";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder host = url + 2;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder url = strchr(host, '/');
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (url == NULL)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder url = "";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder else
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder *(url++) = '\0'; /* skip seperating '/' */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder /* find _last_ '@' since it might occur in user/password part */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder strp = strrchr(host, '@');
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (strp != NULL) {
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder *strp = '\0';
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder user = host;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder host = strp + 1;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
cf39e23ee25b89496d451fcafc70ece1cf760891Christian Maeder/* find password */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder strp = strchr(user, ':');
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (strp != NULL) {
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder *strp = '\0';
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (password == NULL)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return "Bad %-escape in URL (password)";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder }
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (user == NULL)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return "Bad %-escape in URL (username)";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder }
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (userp != NULL) {
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder *userp = user;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder }
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (passwordp != NULL) {
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *passwordp = password;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder }
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
10b02b2343246df6773585636fe3ddbefa3b6a1bChristian Maeder /*
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * Parse the host string to separate host portion from optional port.
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * Perform range checking on port.
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder */
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return "Invalid host/port";
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder }
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder if (tmp_port != 0) { /* only update caller's port if port was specified */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *port = tmp_port;
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder }
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder ap_str_tolower(addr); /* DNS names are case-insensitive */
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *urlp = url;
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder *hostp = addr;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder return NULL;
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder}
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maederstatic const char * const lwday[7] =
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/*
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * If the date is a valid RFC 850 date or asctime() date, then it
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * is converted to the RFC 1123 format, otherwise it is not modified.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * This routine is not very fast at doing conversions, as it uses
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * sscanf and sprintf. However, if the date is already correctly
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * formatted, then it exits very quickly.
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian MaederPROXY_DECLARE(const char *)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder ap_proxy_date_canon(apr_pool_t *p, const char *x1)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder{
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder char *x = apr_pstrdup(p, x1);
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder int wk, mday, year, hour, min, sec, mon;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder char *q, month[4], zone[4], week[4];
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder q = strchr(x, ',');
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder /* check for RFC 850 date */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (q != NULL && q - x > 3 && q[1] == ' ') {
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder *q = '\0';
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder for (wk = 0; wk < 7; wk++)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (strcmp(x, lwday[wk]) == 0)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder break;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder *q = ',';
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (wk == 7)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder return x; /* not a valid date */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder q[17] != ':' || strcmp(&q[20], " GMT") != 0)
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder return x;
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder &hour, &min, &sec, zone) != 7)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return x;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (year < 70)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder year += 2000;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder year += 1900;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else {
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder/* check for acstime() date */
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder x[16] != ':' || x[19] != ' ' || x[24] != '\0')
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder return x;
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder &min, &sec, &year) != 7)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return x;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder for (wk = 0; wk < 7; wk++)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (strcmp(week, apr_day_snames[wk]) == 0)
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder break;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (wk == 7)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return x;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder/* check date */
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder for (mon = 0; mon < 12; mon++)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (strcmp(month, apr_month_snames[mon]) == 0)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder break;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (mon == 12)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return x;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder q = apr_palloc(p, 30);
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk],
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder mday, apr_month_snames[mon], year, hour, min, sec);
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder return q;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder}
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian MaederPROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder{
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->pool = c->pool;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->status = HTTP_OK;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->headers_in = apr_table_make(c->pool, 50);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->subprocess_env = apr_table_make(c->pool, 50);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->headers_out = apr_table_make(c->pool, 12);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder rp->err_headers_out = apr_table_make(c->pool, 5);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->notes = apr_table_make(c->pool, 5);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->server = r->server;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->proxyreq = r->proxyreq;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->request_time = r->request_time;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->connection = c;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->output_filters = c->output_filters;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder rp->input_filters = c->input_filters;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->proto_output_filters = c->output_filters;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->proto_input_filters = c->input_filters;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->request_config = ap_create_request_config(c->pool);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder proxy_run_create_req(r, rp);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return rp;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder}
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder/*
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * list is a comma-separated list of case-insensitive tokens, with
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * optional whitespace around the tokens.
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * The return returns 1 if the token val is found in the list, or 0
b7413fd7a18b060775364d271c3e706c07227b13Christian Maeder * otherwise.
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder */
5c37a9e2ecfe62f615c383db85582a67e3511e10Christian MaederPROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
5c37a9e2ecfe62f615c383db85582a67e3511e10Christian Maeder{
5c37a9e2ecfe62f615c383db85582a67e3511e10Christian Maeder int len, i;
5c37a9e2ecfe62f615c383db85582a67e3511e10Christian Maeder const char *p;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder len = strlen(val);
5c37a9e2ecfe62f615c383db85582a67e3511e10Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder while (list != NULL) {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder p = ap_strchr_c(list, ',');
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (p != NULL) {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder i = p - list;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder do
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder p++;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder while (apr_isspace(*p));
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else
5c37a9e2ecfe62f615c383db85582a67e3511e10Christian Maeder i = strlen(list);
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder while (i > 0 && apr_isspace(list[i - 1]))
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder i--;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (i == len && strncasecmp(list, val, len) == 0)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return 1;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder list = p;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return 0;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder}
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder/*
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * list is a comma-separated list of case-insensitive tokens, with
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * optional whitespace around the tokens.
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * if val appears on the list of tokens, it is removed from the list,
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * and the new list is returned.
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder */
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian MaederPROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder{
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder int len, i;
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder const char *p;
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder char *new = NULL;
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder len = strlen(val);
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder while (list != NULL) {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder p = ap_strchr_c(list, ',');
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (p != NULL) {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder i = p - list;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder do
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder p++;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder while (apr_isspace(*p));
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder i = strlen(list);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder while (i > 0 && apr_isspace(list[i - 1]))
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder i--;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (i == len && strncasecmp(list, val, len) == 0) {
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder /* do nothing */
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (new)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder new = apr_pstrndup(pool, list, i);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder list = p;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder }
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return new;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder}
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder/*
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * Converts 8 hex digits to a time integer
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian MaederPROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder{
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder int i, ch;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder unsigned int j;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder for (i = 0, j = 0; i < 8; i++) {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder ch = x[i];
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder j <<= 4;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (apr_isdigit(ch))
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder j |= ch - '0';
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else if (apr_isupper(ch))
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder j |= ch - ('A' - 10);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder else
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder j |= ch - ('a' - 10);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder }
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (j == 0xffffffff)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return -1; /* so that it works with 8-byte ints */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder else
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return j;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder}
/*
* 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(const char *) ap_proxy_location_reverse_map(request_rec *r,
proxy_dir_conf *conf, const char *url)
{
struct proxy_alias *ent;
int i, l1, l2;
char *u;
/*
* XXX FIXME: Make sure this handled the ambiguous case of the :<PORT>
* after the hostname
*/
l1 = strlen(url);
ent = (struct proxy_alias *)conf->raliases->elts;
for (i = 0; i < conf->raliases->nelts; i++) {
l2 = strlen(ent[i].real);
if (l1 >= l2 && strncasecmp(ent[i].real, url, l2) == 0) {
u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
return ap_construct_url(r->pool, u, r);
}
}
return url;
}
/*
* Cookies are a bit trickier to match: we've got two substrings to worry
* about, and we can't just find them with strstr 'cos of case. Regexp
* matching would be an easy fix, but for better consistency with all the
* other matches we'll refrain and use apr_strmatch to find path=/domain=
* and stick to plain strings for the config values.
*/
PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
proxy_dir_conf *conf, const char *str)
{
struct proxy_alias *ent;
size_t len = strlen(str);
const char *newpath = NULL;
const char *newdomain = NULL;
const char *pathp;
const char *domainp;
const char *pathe = NULL;
const char *domaine = NULL;
size_t l1, l2, poffs = 0, doffs = 0;
int i;
int ddiff = 0;
int pdiff = 0;
char *ret;
/*
* Find the match and replacement, but save replacing until we've done
* both path and domain so we know the new strlen
*/
if ((pathp = apr_strmatch(conf->cookie_path_str, str, len)) != NULL) {
pathp += 5;
poffs = pathp - str;
pathe = ap_strchr_c(pathp, ';');
l1 = pathe ? (pathe - pathp) : strlen(pathp);
pathe = pathp + l1 ;
ent = (struct proxy_alias *)conf->cookie_paths->elts;
for (i = 0; i < conf->cookie_paths->nelts; i++) {
l2 = strlen(ent[i].fake);
if (l1 >= l2 && strncmp(ent[i].fake, pathp, l2) == 0) {
newpath = ent[i].real;
pdiff = strlen(newpath) - l1;
break;
}
}
}
if ((domainp = apr_strmatch(conf->cookie_domain_str, str, len)) != NULL) {
domainp += 7;
doffs = domainp - str;
domaine = ap_strchr_c(domainp, ';');
l1 = domaine ? (domaine - domainp) : strlen(domainp);
domaine = domainp + l1;
ent = (struct proxy_alias *)conf->cookie_domains->elts;
for (i = 0; i < conf->cookie_domains->nelts; i++) {
l2 = strlen(ent[i].fake);
if (l1 >= l2 && strncasecmp(ent[i].fake, domainp, l2) == 0) {
newdomain = ent[i].real;
ddiff = strlen(newdomain) - l1;
break;
}
}
}
if (newpath) {
ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
l1 = strlen(newpath);
if (newdomain) {
l2 = strlen(newdomain);
if (doffs > poffs) {
memcpy(ret, str, poffs);
memcpy(ret + poffs, newpath, l1);
memcpy(ret + poffs + l1, pathe, domainp - pathe);
memcpy(ret + doffs + pdiff, newdomain, l2);
strcpy(ret + doffs + pdiff + l2, domaine);
}
else {
memcpy(ret, str, doffs) ;
memcpy(ret + doffs, newdomain, l2);
memcpy(ret + doffs + l2, domaine, pathp - domaine);
memcpy(ret + poffs + ddiff, newpath, l1);
strcpy(ret + poffs + ddiff + l1, pathe);
}
}
else {
memcpy(ret, str, poffs);
memcpy(ret + poffs, newpath, l1);
strcpy(ret + poffs + l1, pathe);
}
}
else {
if (newdomain) {
ret = apr_palloc(r->pool, len + pdiff + ddiff + 1);
l2 = strlen(newdomain);
memcpy(ret, str, doffs);
memcpy(ret + doffs, newdomain, l2);
strcpy(ret + doffs+l2, domaine);
}
else {
ret = (char *)str; /* no change */
}
}
return ret;
}
PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,
proxy_server_conf *conf,
const char *url)
{
proxy_balancer *balancer;
char *c, *uri = apr_pstrdup(p, url);
int i;
c = strchr(uri, ':');
if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
return NULL;
/* remove path from uri */
if ((c = strchr(c + 3, '/')))
*c = '\0';
balancer = (proxy_balancer *)conf->balancers->elts;
for (i = 0; i < conf->balancers->nelts; i++) {
if (strcasecmp(balancer->name, uri) == 0)
return balancer;
balancer++;
}
return NULL;
}
PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,
apr_pool_t *p,
proxy_server_conf *conf,
const char *url)
{
char *c, *q, *uri = apr_pstrdup(p, url);
proxy_balancer_method *lbmethod;
c = strchr(uri, ':');
if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
return "Bad syntax for a balancer name";
/* remove path from uri */
if ((q = strchr(c + 3, '/')))
*q = '\0';
ap_str_tolower(uri);
*balancer = apr_array_push(conf->balancers);
memset(*balancer, 0, sizeof(proxy_balancer));
/*
* NOTE: The default method is byrequests, which we assume
* exists!
*/
lbmethod = ap_lookup_provider(PROXY_LBMETHOD, "byrequests", "0");
if (!lbmethod) {
return "Can't find 'byrequests' lb method";
}
(*balancer)->name = uri;
(*balancer)->lbmethod = lbmethod;
(*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker));
/* XXX Is this a right place to create mutex */
#if APR_HAS_THREADS
if (apr_thread_mutex_create(&((*balancer)->mutex),
APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
/* XXX: Do we need to log something here */
return "can not create thread mutex";
}
#endif
return NULL;
}
PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,
proxy_server_conf *conf,
const char *url)
{
proxy_worker *worker;
proxy_worker *max_worker = NULL;
int max_match = 0;
int url_length;
int worker_name_length;
const char *c;
char *url_copy;
int i;
c = ap_strchr_c(url, ':');
if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')
return NULL;
url_copy = apr_pstrdup(p, url);
url_length = strlen(url);
/*
* We need to find the start of the path and
* therefore we know the length of the scheme://hostname/
* part to we can force-lowercase everything up to
* the start of the path.
*/
c = ap_strchr_c(c+3, '/');
if (c) {
char *pathstart;
pathstart = url_copy + (c - url);
*pathstart = '\0';
ap_str_tolower(url_copy);
*pathstart = '/';
} else {
ap_str_tolower(url_copy);
}
worker = (proxy_worker *)conf->workers->elts;
/*
* Do a "longest match" on the worker name to find the worker that
* fits best to the URL.
*/
for (i = 0; i < conf->workers->nelts; i++) {
if ( ((worker_name_length = strlen(worker->name)) <= url_length)
&& (worker_name_length > max_match)
&& (strncmp(url_copy, worker->name, worker_name_length) == 0) ) {
max_worker = worker;
max_match = worker_name_length;
}
worker++;
}
return max_worker;
}
#if APR_HAS_THREADS
static apr_status_t conn_pool_cleanup(void *theworker)
{
proxy_worker *worker = (proxy_worker *)theworker;
if (worker->cp->res) {
worker->cp->pool = NULL;
apr_reslist_destroy(worker->cp->res);
}
return APR_SUCCESS;
}
#endif
static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)
{
apr_pool_t *pool;
proxy_conn_pool *cp;
/*
* Create a connection pool's subpool.
* This pool is used for connection recycling.
* Once the worker is added it is never removed but
* it can be disabled.
*/
apr_pool_create(&pool, p);
/*
* Alloc from the same pool as worker.
* proxy_conn_pool is permanently attached to the worker.
*/
cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));
cp->pool = pool;
worker->cp = cp;
}
PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,
apr_pool_t *p,
proxy_server_conf *conf,
const char *url)
{
int rv;
apr_uri_t uri;
rv = apr_uri_parse(p, url, &uri);
if (rv != APR_SUCCESS) {
return "Unable to parse URL";
}
ap_str_tolower(uri.hostname);
ap_str_tolower(uri.scheme);
*worker = apr_array_push(conf->workers);
memset(*worker, 0, sizeof(proxy_worker));
(*worker)->name = apr_uri_unparse(p, &uri, APR_URI_UNP_REVEALPASSWORD);
(*worker)->scheme = uri.scheme;
(*worker)->hostname = uri.hostname;
(*worker)->port = uri.port;
(*worker)->id = proxy_lb_workers;
(*worker)->ajp_flush_packets = ajp_flush_off;
(*worker)->ajp_flush_wait = AJP_FLUSH_WAIT;
/* Increase the total worker count */
proxy_lb_workers++;
init_conn_pool(p, *worker);
#if APR_HAS_THREADS
if (apr_thread_mutex_create(&((*worker)->mutex),
APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {
/* XXX: Do we need to log something here */
return "can not create thread mutex";
}
#endif
return NULL;
}
PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)
{
proxy_worker *worker;
worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));
worker->id = proxy_lb_workers;
/* Increase the total worker count */
proxy_lb_workers++;
init_conn_pool(p, worker);
return worker;
}
PROXY_DECLARE(void)
ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer,
proxy_worker *worker)
{
proxy_worker *runtime;
runtime = apr_array_push(balancer->workers);
memcpy(runtime, worker, sizeof(proxy_worker));
runtime->id = proxy_lb_workers;
/* Increase the total runtime count */
proxy_lb_workers++;
}
PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,
proxy_balancer **balancer,
request_rec *r,
proxy_server_conf *conf, char **url)
{
int access_status;
access_status = proxy_run_pre_request(worker, balancer, r, conf, url);
if (access_status == DECLINED && *balancer == NULL) {
*worker = ap_proxy_get_worker(r->pool, conf, *url);
if (*worker) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"proxy: %s: found worker %s for %s",
(*worker)->scheme, (*worker)->name, *url);
*balancer = NULL;
access_status = OK;
}
else if (r->proxyreq == PROXYREQ_PROXY) {
if (conf->forward) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"proxy: *: found forward proxy worker for %s",
*url);
*balancer = NULL;
*worker = conf->forward;
access_status = OK;
}
}
else if (r->proxyreq == PROXYREQ_REVERSE) {
if (conf->reverse) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"proxy: *: found reverse proxy worker for %s",
*url);
*balancer = NULL;
*worker = conf->reverse;
access_status = OK;
}
}
}
else if (access_status == DECLINED && *balancer != NULL) {
/* All the workers are busy */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"proxy: all workers are busy. Unable to serve %s",
*url);
access_status = HTTP_SERVICE_UNAVAILABLE;
}
return access_status;
}
PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,
proxy_balancer *balancer,
request_rec *r,
proxy_server_conf *conf)
{
int access_status;
if (balancer)
access_status = proxy_run_post_request(worker, balancer, r, conf);
else {
access_status = OK;
}
return access_status;
}
/* DEPRECATED */
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, 0, 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_socket_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;
}
static apr_status_t connection_cleanup(void *theconn)
{
proxy_conn_rec *conn = (proxy_conn_rec *)theconn;
proxy_worker *worker = conn->worker;
/*
* If the connection pool is NULL the worker
* cleanup has been run. Just return.
*/
if (!worker->cp)
return APR_SUCCESS;
/* deterimine if the connection need to be closed */
if (conn->close_on_recycle || conn->close) {
apr_pool_t *p = conn->pool;
apr_pool_clear(conn->pool);
memset(conn, 0, sizeof(proxy_conn_rec));
conn->pool = p;
conn->worker = worker;
}
#if APR_HAS_THREADS
if (worker->hmax && worker->cp->res) {
apr_reslist_release(worker->cp->res, (void *)conn);
}
else
#endif
{
worker->cp->conn = conn;
}
/* Allways return the SUCCESS */
return APR_SUCCESS;
}
/* reslist constructor */
static apr_status_t connection_constructor(void **resource, void *params,
apr_pool_t *pool)
{
apr_pool_t *ctx;
proxy_conn_rec *conn;
proxy_worker *worker = (proxy_worker *)params;
/*
* Create the subpool for each connection
* This keeps the memory consumption constant
* when disconnecting from backend.
*/
apr_pool_create(&ctx, pool);
conn = apr_pcalloc(pool, sizeof(proxy_conn_rec));
conn->pool = ctx;
conn->worker = worker;
*resource = conn;
return APR_SUCCESS;
}
#if APR_HAS_THREADS /* only needed when threads are used */
/* reslist destructor */
static apr_status_t connection_destructor(void *resource, void *params,
apr_pool_t *pool)
{
proxy_conn_rec *conn = (proxy_conn_rec *)resource;
/* Destroy the pool only if not called from reslist_destroy */
if (conn->worker->cp->pool)
apr_pool_destroy(conn->pool);
return APR_SUCCESS;
}
#endif
/*
* ap_proxy_initialize_worker_share() concerns itself
* with initializing those parts of worker which
* are, or could be, shared. Basically worker->s
*/
PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,
proxy_worker *worker,
server_rec *s)
{
#if PROXY_HAS_SCOREBOARD
lb_score *score = NULL;
#else
void *score = NULL;
#endif
if (worker->s && (worker->s->status & PROXY_WORKER_INITIALIZED)) {
/* The worker share is already initialized */
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: worker %s already initialized",
worker->name);
return;
}
#if PROXY_HAS_SCOREBOARD
/* Get scoreboard slot */
if (ap_scoreboard_image) {
score = ap_get_scoreboard_lb(worker->id);
if (!score) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"proxy: ap_get_scoreboard_lb(%d) failed in child %" APR_PID_T_FMT " for worker %s",
worker->id, getpid(), worker->name);
}
else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: grabbed scoreboard slot %d in child %" APR_PID_T_FMT " for worker %s",
worker->id, getpid(), worker->name);
}
}
#endif
if (!score) {
score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat));
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: initialized plain memory in child %" APR_PID_T_FMT " for worker %s",
getpid(), worker->name);
}
worker->s = (proxy_worker_stat *)score;
/*
* recheck to see if we've already been here. Possible
* if proxy is using scoreboard to hold shared stats
*/
if (worker->s->status & PROXY_WORKER_INITIALIZED) {
/* The worker share is already initialized */
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: worker %s already initialized",
worker->name);
return;
}
if (worker->route)
strcpy(worker->s->route, worker->route);
else
*worker->s->route = '\0';
if (worker->redirect)
strcpy(worker->s->redirect, worker->redirect);
else
*worker->s->redirect = '\0';
worker->s->status |= (worker->status | PROXY_WORKER_INITIALIZED);
}
PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)
{
apr_status_t rv;
#if APR_HAS_THREADS
int mpm_threads;
#endif
if (worker->status & PROXY_WORKER_INITIALIZED) {
/* The worker is already initialized */
return APR_SUCCESS;
}
/* Set default parameters */
if (!worker->retry)
worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);
/* By default address is reusable */
worker->is_address_reusable = 1;
#if APR_HAS_THREADS
ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
if (mpm_threads > 1) {
/* Set hard max to no more then mpm_threads */
if (worker->hmax == 0 || worker->hmax > mpm_threads)
worker->hmax = mpm_threads;
if (worker->smax == 0 || worker->smax > worker->hmax)
worker->smax = worker->hmax;
/* Set min to be lower then smax */
if (worker->min > worker->smax)
worker->min = worker->smax;
}
else {
/* This will supress the apr_reslist creation */
worker->min = worker->smax = worker->hmax = 0;
}
if (worker->hmax) {
rv = apr_reslist_create(&(worker->cp->res),
worker->min, worker->smax,
worker->hmax, worker->ttl,
connection_constructor, connection_destructor,
worker, worker->cp->pool);
apr_pool_cleanup_register(worker->cp->pool, (void *)worker,
conn_pool_cleanup,
apr_pool_cleanup_null);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: initialized worker %d in child %" APR_PID_T_FMT " for (%s) min=%d max=%d smax=%d",
worker->id, getpid(), worker->hostname, worker->min,
worker->hmax, worker->smax);
#if (APR_MAJOR_VERSION > 0)
/* Set the acquire timeout */
if (rv == APR_SUCCESS && worker->acquire_set)
apr_reslist_timeout_set(worker->cp->res, worker->acquire);
#endif
}
else
#endif
{
rv = connection_constructor((void **)&(worker->cp->conn), worker, worker->cp->pool);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: initialized single connection worker %d in child %" APR_PID_T_FMT " for (%s)",
worker->id, getpid(), worker->hostname);
}
if (rv == APR_SUCCESS) {
worker->status |= (PROXY_WORKER_INITIALIZED);
}
return rv;
}
PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,
proxy_worker *worker,
server_rec *s)
{
if (worker->s->status & PROXY_WORKER_IN_ERROR) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: retrying the worker for (%s)",
proxy_function, worker->hostname);
if (apr_time_now() > worker->s->error_time + worker->retry) {
++worker->s->retries;
worker->s->status &= ~PROXY_WORKER_IN_ERROR;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: worker for (%s) has been marked for retry",
proxy_function, worker->hostname);
return OK;
}
else
return DECLINED;
}
else
return OK;
}
PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,
proxy_conn_rec **conn,
proxy_worker *worker,
server_rec *s)
{
apr_status_t rv;
if (!PROXY_WORKER_IS_USABLE(worker)) {
/* Retry the worker */
ap_proxy_retry_worker(proxy_function, worker, s);
if (!PROXY_WORKER_IS_USABLE(worker)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"proxy: %s: disabled connection for (%s)",
proxy_function, worker->hostname);
return HTTP_SERVICE_UNAVAILABLE;
}
}
#if APR_HAS_THREADS
if (worker->hmax && worker->cp->res) {
rv = apr_reslist_acquire(worker->cp->res, (void **)conn);
}
else
#endif
{
/* create the new connection if the previous was destroyed */
if (!worker->cp->conn)
connection_constructor((void **)conn, worker, worker->cp->pool);
else {
*conn = worker->cp->conn;
worker->cp->conn = NULL;
}
rv = APR_SUCCESS;
}
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"proxy: %s: failed to acquire connection for (%s)",
proxy_function, worker->hostname);
return HTTP_SERVICE_UNAVAILABLE;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: has acquired connection for (%s)",
proxy_function, worker->hostname);
(*conn)->worker = worker;
(*conn)->close = 0;
(*conn)->close_on_recycle = 0;
return OK;
}
PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,
proxy_conn_rec *conn,
server_rec *s)
{
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: has released connection for (%s)",
proxy_function, conn->worker->hostname);
/* If there is a connection kill it's cleanup */
if (conn->connection) {
apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
conn->connection = NULL;
}
connection_cleanup(conn);
return OK;
}
PROXY_DECLARE(int)
ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,
proxy_server_conf *conf,
proxy_worker *worker,
proxy_conn_rec *conn,
apr_uri_t *uri,
char **url,
const char *proxyname,
apr_port_t proxyport,
char *server_portstr,
int server_portstr_size)
{
int server_port;
apr_status_t err = APR_SUCCESS;
/*
* Break up the URL to determine the host to connect to
*/
/* we break the URL into host, port, uri */
if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {
return ap_proxyerror(r, HTTP_BAD_REQUEST,
apr_pstrcat(p,"URI cannot be parsed: ", *url,
NULL));
}
if (!uri->port) {
uri->port = apr_uri_port_of_scheme(uri->scheme);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: connecting %s to %s:%d", *url, uri->hostname,
uri->port);
/*
* allocate these out of the specified connection pool
* The scheme handler decides if this is permanent or
* short living pool.
*/
/* are we connecting directly, or via a proxy? */
if (!proxyname) {
*url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",
uri->query ? uri->query : "",
uri->fragment ? "#" : "",
uri->fragment ? uri->fragment : "", NULL);
}
/*
* Make sure that we pick the the correct and valid worker.
* If a single keepalive connection triggers different workers,
* then we have a problem (we don't select the correct one).
* Do an expensive check in this case, where we compare the
* the hostnames associated between the two.
*
* TODO: Handle this much better...
*/
if (!conn->hostname || !worker->is_address_reusable ||
(r->connection->keepalives &&
(r->proxyreq == PROXYREQ_PROXY || r->proxyreq == PROXYREQ_REVERSE) &&
(strcasecmp(conn->hostname, uri->hostname) != 0) ) ) {
if (proxyname) {
conn->hostname = apr_pstrdup(conn->pool, proxyname);
conn->port = proxyport;
} else {
conn->hostname = apr_pstrdup(conn->pool, uri->hostname);
conn->port = uri->port;
}
if (conn->connection) {
if (conn->sock) {
apr_socket_close(conn->sock);
conn->sock = NULL;
}
apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);
conn->connection = NULL;
}
err = apr_sockaddr_info_get(&(conn->addr),
conn->hostname, APR_UNSPEC,
conn->port, 0,
conn->pool);
}
else if (!worker->cp->addr) {
if ((err = PROXY_THREAD_LOCK(worker)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server,
"proxy: lock");
return HTTP_INTERNAL_SERVER_ERROR;
}
/*
* Worker can have the single constant backend adress.
* The single DNS lookup is used once per worker.
* If dynamic change is needed then set the addr to NULL
* inside dynamic config to force the lookup.
*/
err = apr_sockaddr_info_get(&(worker->cp->addr),
conn->hostname, APR_UNSPEC,
conn->port, 0,
worker->cp->pool);
conn->addr = worker->cp->addr;
PROXY_THREAD_UNLOCK(worker);
}
else
conn->addr = worker->cp->addr;
if (err != APR_SUCCESS) {
return ap_proxyerror(r, HTTP_BAD_GATEWAY,
apr_pstrcat(p, "DNS lookup failure for: ",
conn->hostname, NULL));
}
/* Get the server port for the Via headers */
{
server_port = ap_get_server_port(r);
if (ap_is_default_port(server_port, r)) {
strcpy(server_portstr,"");
} else {
apr_snprintf(server_portstr, server_portstr_size, ":%d",
server_port);
}
}
/* check if ProxyBlock directive on this host */
if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {
return ap_proxyerror(r, HTTP_FORBIDDEN,
"Connect to remote machine blocked");
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: connected %s to %s:%d", *url, conn->hostname,
conn->port);
return OK;
}
static int is_socket_connected(apr_socket_t *sock)
{
apr_size_t buffer_len = 1;
char test_buffer[1];
apr_status_t socket_status;
apr_interval_time_t current_timeout;
/* save timeout */
apr_socket_timeout_get(sock, &current_timeout);
/* set no timeout */
apr_socket_timeout_set(sock, 0);
socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);
/* put back old timeout */
apr_socket_timeout_set(sock, current_timeout);
if (APR_STATUS_IS_EOF(socket_status))
return 0;
else
return 1;
}
PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
proxy_conn_rec *conn,
proxy_worker *worker,
server_rec *s)
{
apr_status_t rv;
int connected = 0;
int loglevel;
apr_sockaddr_t *backend_addr = conn->addr;
apr_socket_t *newsock;
if (conn->sock) {
/*
* This increases the connection pool size
* but the number of dropped connections is
* relatively small compared to connection lifetime
*/
if (!(connected = is_socket_connected(conn->sock))) {
apr_socket_close(conn->sock);
conn->sock = NULL;
}
}
while (backend_addr && !connected) {
if ((rv = apr_socket_create(&newsock, backend_addr->family,
SOCK_STREAM, APR_PROTO_TCP,
conn->pool)) != 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,
worker->hostname);
/*
* 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 (worker->recv_buffer_size > 0 &&
(rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,
worker->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 (worker->timeout_set == 1) {
apr_socket_timeout_set(newsock, worker->timeout);
}
else {
apr_socket_timeout_set(newsock, s->timeout);
}
/* Set a keepalive option */
if (worker->keepalive) {
if ((rv = apr_socket_opt_set(newsock,
APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
"apr_socket_opt_set(SO_KEEPALIVE): Failed to set"
" Keepalive");
}
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: fam %d socket created to connect to %s",
proxy_function, backend_addr->family, worker->hostname);
/* make the connection out of the socket */
rv = apr_socket_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,
worker->hostname);
backend_addr = backend_addr->next;
continue;
}
conn->sock = newsock;
connected = 1;
}
/*
* Put the entire worker to error state if
* the PROXY_WORKER_IGNORE_ERRORS flag is not set.
* Altrough some connections may be alive
* no further connections to the worker could be made
*/
if (!connected && PROXY_WORKER_IS_USABLE(worker) &&
!(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
worker->s->status |= PROXY_WORKER_IN_ERROR;
worker->s->error_time = apr_time_now();
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
"ap_proxy_connect_backend disabling worker for (%s)",
worker->hostname);
}
else {
worker->s->error_time = 0;
worker->s->retries = 0;
}
return connected ? OK : DECLINED;
}
PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,
proxy_conn_rec *conn,
conn_rec *c,
server_rec *s)
{
apr_sockaddr_t *backend_addr = conn->addr;
int rc;
/*
* The socket is now open, create a new backend server connection
*/
conn->connection = ap_run_create_connection(c->pool, s, conn->sock,
c->id, c->sbh,
c->bucket_alloc);
if (!conn->connection) {
/*
* the peer reset the connection already; ap_run_create_connection()
* closed the socket
*/
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
s, "proxy: %s: an error occurred creating a "
"new connection to %pI (%s)", proxy_function,
backend_addr, conn->hostname);
/* XXX: Will be closed when proxy_conn is closed */
apr_socket_close(conn->sock);
conn->sock = NULL;
return HTTP_INTERNAL_SERVER_ERROR;
}
/*
* register the connection cleanup to client connection
* so that the connection can be closed or reused
*/
apr_pool_cleanup_register(c->pool, (void *)conn,
connection_cleanup,
apr_pool_cleanup_null);
/* For ssl connection to backend */
if (conn->is_ssl) {
if (!ap_proxy_ssl_enable(conn->connection)) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0,
s, "proxy: %s: failed to enable ssl support "
"for %pI (%s)", proxy_function,
backend_addr, conn->hostname);
return HTTP_INTERNAL_SERVER_ERROR;
}
}
else {
/* TODO: See if this will break FTP */
ap_proxy_ssl_disable(conn->connection);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: connection complete to %pI (%s)",
proxy_function, backend_addr, conn->hostname);
/* set up the connection filters */
rc = ap_run_pre_connection(conn->connection, conn->sock);
if (rc != OK && rc != DONE) {
conn->connection->aborted = 1;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"proxy: %s: pre_connection setup failed (%d)",
proxy_function, rc);
return rc;
}
return OK;
}
int ap_proxy_lb_workers(void)
{
/*
* Since we can't resize the scoreboard when reconfiguring, we
* have to impose a limit on the number of workers, we are
* able to reconfigure to.
*/
if (!lb_workers_limit)
lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
return lb_workers_limit;
}
PROXY_DECLARE(void) ap_proxy_backend_broke(request_rec *r,
apr_bucket_brigade *brigade)
{
apr_bucket *e;
conn_rec *c = r->connection;
r->no_cache = 1;
/*
* If this is a subrequest, then prevent also caching of the main
* request.
*/
if (r->main)
r->main->no_cache = 1;
e = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, c->pool,
c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(brigade, e);
e = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(brigade, e);
}