proxy_util.c revision c332befc1519a1016d8de07608f0b859e6fab580
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * applicable.
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 * http://www.apache.org/licenses/LICENSE-2.0
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/* Utility routines for Apache proxy */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#define apr_socket_create apr_socket_create_ex
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/* Global balancer counter */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maederint PROXY_DECLARE_DATA proxy_lb_workers = 0;
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 MaederAPR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req,
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder (request_rec *r, request_rec *pr), (r, pr),
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/* already called in the knowledge that the characters are hex digits */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian MaederPROXY_DECLARE(int) ap_proxy_hex2c(const char *x)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#else /*APR_CHARSET_EBCDIC*/
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 * 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
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * represents an ASCII char and convert 0x20 to EBCDIC, yielding
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder#endif /*APR_CHARSET_EBCDIC*/
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian MaederPROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder#else /*APR_CHARSET_EBCDIC*/
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder static const char ntoa[] = { "0123456789ABCDEF" };
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder#endif /*APR_CHARSET_EBCDIC*/
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * canonicalise a URL-encoded string
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * Convert a URL-encoded string to canonical form.
adda0e6252b14215228e4071b347c49b808894f8Christian 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 MaederPROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder char *allowed; /* characters which should not be encoded */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder char *reserved; /* characters which much not be en/de-coded */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/* N.B. in addition to :@&=, this allows ';' in an http path
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * and '?' in an ftp path -- this may be revised
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.)
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder else if (t == enc_user)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder else /* if (t == enc_parm) */
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder for (i = 0, j = 0; i < len; i++, j++) {
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder/* always handle '/' first */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * decode it if not already done. do not decode reverse proxied URLs
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder * unless specifically forced
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') {
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder/* recode it, if necessary */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (!apr_isalnum(ch) && !strchr(allowed, ch)) {
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * Parses network-location.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * urlp on input the URL; on output the path, after the leading /
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * user NULL if no user/password permitted
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder * password holder for password
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * host holder for host
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * port port number; only set if one is supplied.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * Returns an error string.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian 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 char *addr, *scope_id, *strp, *host, *url = *urlp;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return "Malformed URL";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder /* find _last_ '@' since it might occur in user/password part */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/* find password */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1, 0);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return "Bad %-escape in URL (password)";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1, 0);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder return "Bad %-escape in URL (username)";
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder /* Parse the host string to separate host portion from optional port.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * Perform range checking on port.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder return "Invalid host/port";
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (tmp_port != 0) { /* only update caller's port if port was specified */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder ap_str_tolower(addr); /* DNS names are case-insensitive */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * If the date is a valid RFC 850 date or asctime() date, then it
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder * is converted to the RFC 1123 format, otherwise it is not modified.
1fac054baed931dc57f0e41dd0ade39adac28c49Christian Maeder * This routine is not very fast at doing conversions, as it uses
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder * sscanf and sprintf. However, if the date is already correctly
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * formatted, then it exits very quickly.
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder ap_proxy_date_canon(apr_pool_t *p, const char *x1)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder /* check for RFC 850 date */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (q != NULL && q - x > 3 && q[1] == ' ') {
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder return x; /* not a valid date */
e2ca90217abd35b3d5f98bfe73ecffb34badd837Christian Maeder if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder q[17] != ':' || strcmp(&q[20], " GMT") != 0)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder/* check for acstime() date */
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder x[16] != ':' || x[19] != ' ' || x[24] != '\0')
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder/* check date */
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder if (strcmp(month, apr_month_snames[mon]) == 0)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian 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);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian MaederPROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->headers_in = apr_table_make(c->pool, 50);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder rp->subprocess_env = apr_table_make(c->pool, 50);
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder rp->headers_out = apr_table_make(c->pool, 12);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->err_headers_out = apr_table_make(c->pool, 5);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->proto_output_filters = c->output_filters;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->proto_input_filters = c->input_filters;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder rp->request_config = ap_create_request_config(c->pool);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian 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
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian MaederPROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder const char *p;
45ad02e03fb913ba373d8fdcfe50244be3df31eaChristian Maeder if (i == len && strncasecmp(list, val, len) == 0)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * list is a comma-separated list of case-insensitive tokens, with
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * optional whitespace around the tokens.
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * if val appears on the list of tokens, it is removed from the list,
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * and the new list is returned.
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian MaederPROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder const char *p;
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder if (i == len && strncasecmp(list, val, len) == 0) {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder /* do nothing */
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * Converts 8 hex digits to a time integer
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian MaederPROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)
ee31a8a5f5d786472f2b5dfb271b38e6d401fa35Christian Maeder unsigned int j;
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder for (i = 0, j = 0; i < 8; i++) {
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder if (j == 0xffffffff)
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder return -1; /* so that it works with 8-byte ints */
7bffb8b0e6cae4bb7ecb59b99327add6106c06b9Christian Maeder * Converts a time integer to 8 hex digits
int i, ch;
return statuscode;
return r->hostname;
return NULL;
url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */
int i, quads;
long bits;
char *tmp;
char *tmp;
++addr;
--quads;
/* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
#if DEBUGGING
#if DEBUGGING
!= APR_SUCCESS) {
#if DEBUGGING
host);
while (reqaddr) {
#if DEBUGGING
#if DEBUGGING
--d_len;
--h_len;
int h2_len;
int h1_len;
while (addr) {
--h2_len;
--h1_len;
return HTTP_FORBIDDEN;
while (conf_addr) {
while (uri_addr) {
char *conf_ip;
char *uri_ip;
return HTTP_FORBIDDEN;
return OK;
return OK;
* ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline()
apr_bucket *e;
char *response;
int found = 0;
buff[0] = 0;
*eos = 0;
while (!found) {
return rv;
while (!found) {
return APR_ECONNABORTED;
if (APR_BUCKET_IS_EOS(e)) {
return rv;
if (len > 0) {
return APR_SUCCESS;
if (!initial) {
count++;
return url;
const char *pathp;
const char *domainp;
int ddiff = 0;
int pdiff = 0;
char *ret;
if (newpath) {
if (newdomain) {
if (newdomain) {
return ret;
const char *url)
return NULL;
return balancer;
balancer++;
return NULL;
apr_pool_t *p,
const char *url)
if (!lbmethod) {
#if APR_HAS_THREADS
return NULL;
const char *url)
return NULL;
return worker;
worker++;
return NULL;
#if APR_HAS_THREADS
return APR_SUCCESS;
apr_pool_t *p,
const char *url)
int rv;
#if APR_HAS_THREADS
return NULL;
return worker;
PROXY_DECLARE(void)
request_rec *r,
int access_status;
if (*worker) {
*url);
*url);
*url);
return access_status;
request_rec *r,
int access_status;
if (balancer)
return access_status;
const char *proxy_function,
const char *backend_name,
server_rec *s,
apr_pool_t *p)
int connected = 0;
int loglevel;
return APR_SUCCESS;
#if APR_HAS_THREADS
return APR_SUCCESS;
return APR_SUCCESS;
return APR_SUCCESS;
server_rec *s)
if (ap_scoreboard_image) {
if (!score)
if (!score) {
#if APR_HAS_THREADS
int mpm_threads;
return APR_SUCCESS;
#if APR_HAS_THREADS
#if (APR_MAJOR_VERSION > 0)
return rv;
server_rec *s)
return OK;
return DECLINED;
return OK;
server_rec *s)
return HTTP_SERVICE_UNAVAILABLE;
#if APR_HAS_THREADS
return HTTP_SERVICE_UNAVAILABLE;
return OK;
server_rec *s)
return OK;
PROXY_DECLARE(int)
char **url,
const char *proxyname,
char *server_portstr,
int server_portstr_size)
int server_port;
NULL));
if (!proxyname) {
if (proxyname) {
if (proxyname) {
return HTTP_INTERNAL_SERVER_ERROR;
return OK;
server_rec *s)
int connected = 0;
int loglevel;
conn_rec *c,
server_rec *s)
int rc;
c->bucket_alloc);
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
return rc;
return OK;
int ap_proxy_lb_workers(void)
if (!lb_workers_limit)
return lb_workers_limit;