From 0d9c89ae6fdc6bf0c9fcd8ff8d1da2dbc30b5d88 Mon Sep 17 00:00:00 2001 From: Mladen Turk Date: Tue, 28 Sep 2004 16:54:29 +0000 Subject: [PATCH] Reformat code to no tab and no CR-LF git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@105320 13f79535-47bb-0310-9956-ffa450edef68 --- modules/proxy/mod_proxy.h | 1228 +++++------ modules/proxy/proxy_ajp.c | 2 +- modules/proxy/proxy_balancer.c | 30 +- modules/proxy/proxy_connect.c | 92 +- modules/proxy/proxy_ftp.c | 54 +- modules/proxy/proxy_util.c | 3686 ++++++++++++++++---------------- 6 files changed, 2546 insertions(+), 2546 deletions(-) diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 20b26dad48..227d72e04f 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -1,614 +1,614 @@ -/* Copyright 1999-2004 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MOD_PROXY_H -#define MOD_PROXY_H - -/* - * Main include file for the Apache proxy - */ - -/* - - Also note numerous FIXMEs and CHECKMEs which should be eliminated. - - This code is once again experimental! - - Things to do: - - 1. Make it completely work (for FTP too) - - 2. HTTP/1.1 - - Chuck Murcko 02-06-01 - - */ - -#define CORE_PRIVATE - -#include "apr_hooks.h" -#include "apr.h" -#include "apr_lib.h" -#include "apr_strings.h" -#include "apr_buckets.h" -#include "apr_md5.h" -#include "apr_network_io.h" -#include "apr_pools.h" -#include "apr_strings.h" -#include "apr_uri.h" -#include "apr_date.h" -#include "apr_strmatch.h" -#include "apr_fnmatch.h" -#include "apr_reslist.h" -#define APR_WANT_STRFUNC -#include "apr_want.h" - -#include "httpd.h" -#include "http_config.h" -#include "ap_config.h" -#include "http_core.h" -#include "http_protocol.h" -#include "http_request.h" -#include "http_vhost.h" -#include "http_main.h" -#include "http_log.h" -#include "http_connection.h" -#include "util_filter.h" -#include "util_ebcdic.h" - -#if APR_HAVE_NETINET_IN_H -#include -#endif -#if APR_HAVE_ARPA_INET_H -#include -#endif - -/* for proxy_canonenc() */ -enum enctype { - enc_path, enc_search, enc_user, enc_fpath, enc_parm -}; - -#if APR_CHARSET_EBCDIC -#define CRLF "\r\n" -#else /*APR_CHARSET_EBCDIC*/ -#define CRLF "\015\012" -#endif /*APR_CHARSET_EBCDIC*/ - -/* default Max-Forwards header setting */ -#define DEFAULT_MAX_FORWARDS 10 - -/* static information about a remote proxy */ -struct proxy_remote { - const char *scheme; /* the schemes handled by this proxy, or '*' */ - const char *protocol; /* the scheme used to talk to this proxy */ - const char *hostname; /* the hostname of this proxy */ - apr_port_t port; /* the port for this proxy */ - regex_t *regexp; /* compiled regex (if any) for the remote */ - int use_regex; /* simple boolean. True if we have a regex pattern */ -}; - -struct proxy_alias { - const char *real; - const char *fake; -}; - -struct dirconn_entry { - char *name; - struct in_addr addr, mask; - struct apr_sockaddr_t *hostaddr; - int (*matcher) (struct dirconn_entry * This, request_rec *r); -}; - -struct noproxy_entry { - const char *name; - struct apr_sockaddr_t *addr; -}; - -typedef struct proxy_balancer proxy_balancer; -typedef struct proxy_worker proxy_worker; -typedef struct proxy_conn_pool proxy_conn_pool; - -typedef struct { - apr_array_header_t *proxies; - apr_array_header_t *sec_proxy; - apr_array_header_t *aliases; - apr_array_header_t *raliases; - apr_array_header_t *noproxies; - apr_array_header_t *dirconn; - apr_array_header_t *allowed_connect_ports; - apr_array_header_t *workers; - apr_array_header_t *balancers; - proxy_worker *forward; /* forward proxy worker */ - const char *domain; /* domain name to use in absence of a domain name in the request */ - int req; /* true if proxy requests are enabled */ - char req_set; - enum { - via_off, - via_on, - via_block, - via_full - } viaopt; /* how to deal with proxy Via: headers */ - char viaopt_set; - apr_size_t recv_buffer_size; - char recv_buffer_size_set; - apr_size_t io_buffer_size; - char io_buffer_size_set; - long maxfwd; - char maxfwd_set; - /** - * the following setting masks the error page - * returned from the 'proxied server' and just - * forwards the status code upwards. - * This allows the main server (us) to generate - * the error page, (so it will look like a error - * returned from the rest of the system - */ - int error_override; - int error_override_set; - int preserve_host; - int preserve_host_set; - apr_interval_time_t timeout; - char timeout_set; - enum { - bad_error, - bad_ignore, - bad_body - } badopt; /* how to deal with bad headers */ - char badopt_set; -/* putting new stuff on the end maximises binary back-compatibility. - * the strmatch_patterns are really a const just to have a - * case-independent strstr. - */ - apr_array_header_t* cookie_paths; - apr_array_header_t* cookie_domains; - const apr_strmatch_pattern* cookie_path_str; - const apr_strmatch_pattern* cookie_domain_str; - enum { - status_off, - status_on, - status_full - } proxy_status; /* Status display options */ - char proxy_status_set; - apr_pool_t *pool; /* Pool used for allocating this struct */ -} proxy_server_conf; - - -typedef struct { - const char *p; /* The path */ - int p_is_fnmatch; /* Is this path an fnmatch candidate? */ - regex_t *r; /* Is this a regex? */ -} proxy_dir_conf; - -typedef struct { - conn_rec *connection; - const char *hostname; - apr_port_t port; - int is_ssl; - apr_pool_t *pool; /* Subpool used for creating socket */ - apr_socket_t *sock; /* Connection socket */ - apr_sockaddr_t *addr; /* Preparsed remote address info */ - apr_uint32_t flags; /* Conection flags */ - int close; /* Close 'this' connection */ - int close_on_recycle; /* Close the connection when returning to pool */ - proxy_worker *worker; /* Connection pool this connection belogns to */ - void *data; /* per scheme connection data */ -} proxy_conn_rec; - -typedef struct { - float cache_completion; /* completion percentage */ - int content_length; /* length of the content */ -} proxy_completion; - -/* Connection pool */ -struct proxy_conn_pool { - apr_pool_t *pool; /* The pool used in constructor and destructor calls */ - apr_sockaddr_t *addr; /* Preparsed remote address info */ -#if APR_HAS_THREADS - apr_reslist_t *res; /* Connection resource list */ -#endif - proxy_conn_rec *conn; /* Single connection for prefork mpm's */ -}; - -/* woker status flags */ -#define PROXY_WORKER_INITIALIZED 0x0001 -#define PROXY_WORKER_IGNORE_ERRORS 0x0002 -#define PROXY_WORKER_IN_SHUTDOWN 0x0010 -#define PROXY_WORKER_DISABLED 0x0020 -#define PROXY_WORKER_IN_ERROR 0x0040 - -#define PROXY_WORKER_IS_USABLE(f) (!((f)->s->status & 0x00F0)) - -/* default worker retry timeout in seconds */ -#define PROXY_WORKER_DEFAULT_RETRY 60 -#define PROXY_WORKER_MAX_ROUTE_SIZ 63 - -/* Runtime worker status informations. Shared in scoreboard */ -typedef struct { - int status; - apr_time_t error_time; /* time of the last error */ - int retries; /* number of retries on this worker */ - int lbstatus; /* Current lbstatus */ - int lbfactor; /* dynamic lbfactor */ - apr_off_t transfered; /* Number of bytes transfered to remote */ - apr_off_t readed; /* Number of bytes readed from remote */ - apr_size_t elected; /* Number of times the worker was elected */ - char route[PROXY_WORKER_MAX_ROUTE_SIZ+1]; - char redirect[PROXY_WORKER_MAX_ROUTE_SIZ+1]; -} proxy_worker_stat; - -/* Worker configuration */ -struct proxy_worker { - int id; /* scoreboard id */ - apr_interval_time_t retry; /* retry interval */ - int lbfactor; /* initial load balancing factor */ - const char *name; - const char *scheme; /* scheme to use ajp|http|https */ - const char *hostname; /* remote backend address */ - const char *route; /* balancing route */ - const char *redirect; /* temporary balancing redirection route */ - apr_port_t port; - int min; /* Desired minimum number of available connections */ - int smax; /* Soft maximum on the total number of connections */ - int hmax; /* Hard maximum on the total number of connections */ - apr_interval_time_t ttl; /* maximum amount of time in seconds a connection - * may be available while exceeding the soft limit */ - apr_interval_time_t timeout; /* connection timeout */ - char timeout_set; - apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */ - char acquire_set; - apr_size_t recv_buffer_size; - char recv_buffer_size_set; - apr_size_t io_buffer_size; - char io_buffer_size_set; - char keepalive; - char keepalive_set; - proxy_conn_pool *cp; /* Connection pool to use */ - proxy_worker_stat *s; /* Shared data */ - void *opaque; /* per scheme worker data */ -}; - -struct proxy_balancer { - apr_array_header_t *workers; /* array of proxy_workers */ - const char *name; /* name of the load balancer */ - const char *sticky; /* sticky session identifier */ - int sticky_force; /* Disable failover for sticky sessions */ - apr_interval_time_t timeout; /* Timeout for waiting on free connection */ - int max_attempts; /* Number of attempts before failing */ - char max_attempts_set; - - /* XXX: Perhaps we will need the proc mutex too. - * Altrough we are only using arithmetic operations - * it may lead to a incorrect calculations. - * For now use only the thread mutex. - */ -#if APR_HAS_THREADS - apr_thread_mutex_t *mutex; /* Thread lock for updating lb params */ -#endif -}; - -/* hooks */ - -/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and - * PROXY_DECLARE_DATA with appropriate export and import tags for the platform - */ -#if !defined(WIN32) -#define PROXY_DECLARE(type) type -#define PROXY_DECLARE_NONSTD(type) type -#define PROXY_DECLARE_DATA -#elif defined(PROXY_DECLARE_STATIC) -#define PROXY_DECLARE(type) type __stdcall -#define PROXY_DECLARE_NONSTD(type) type -#define PROXY_DECLARE_DATA -#elif defined(PROXY_DECLARE_EXPORT) -#define PROXY_DECLARE(type) __declspec(dllexport) type __stdcall -#define PROXY_DECLARE_NONSTD(type) __declspec(dllexport) type -#define PROXY_DECLARE_DATA __declspec(dllexport) -#else -#define PROXY_DECLARE(type) __declspec(dllimport) type __stdcall -#define PROXY_DECLARE_NONSTD(type) __declspec(dllimport) type -#define PROXY_DECLARE_DATA __declspec(dllimport) -#endif - -/** - * Hook an optional proxy hook. Unlike static hooks, this uses a macro - * instead of a function. - */ -#define PROXY_OPTIONAL_HOOK(name,fn,pre,succ,order) \ - APR_OPTIONAL_HOOK(proxy,name,fn,pre,succ,order) - -APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, scheme_handler, (request_rec *r, - proxy_worker *worker, proxy_server_conf *conf, char *url, - const char *proxyhost, apr_port_t proxyport)) -APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, canon_handler, (request_rec *r, - char *url)) - -APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, create_req, (request_rec *r, request_rec *pr)) -APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, fixups, (request_rec *r)) - -/** - * pre request hook. - * It will return the most suitable worker at the moment - * and coresponding balancer. - * The url is rewritten from balancer://cluster/uri to scheme://host:port/uri - * and then the scheme_handler is called. - * - */ -APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, pre_request, (proxy_worker **worker, - proxy_balancer **balancer, - request_rec *r, - proxy_server_conf *conf, char **url)) -/** - * post request hook. - * It is called after request for updating runtime balancer status. - */ -APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, post_request, (proxy_worker *worker, - proxy_balancer *balancer, request_rec *r, - proxy_server_conf *conf)) - - -/* proxy_util.c */ - -PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r); -PROXY_DECLARE(int) ap_proxy_hex2c(const char *x); -PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x); -PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, - int isenc); -PROXY_DECLARE(char *)ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, - char **passwordp, char **hostp, apr_port_t *port); -PROXY_DECLARE(const char *)ap_proxy_date_canon(apr_pool_t *p, const char *x); -PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val); -PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val); -PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x); -PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y); -PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message); -PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p); -PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p); -PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p); -PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p); -PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr); -PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r); -PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos); -PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key); -/* DEPRECATED (will be replaced with ap_proxy_connect_backend */ -PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, server_rec *, apr_pool_t *); -PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c); -PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c); - -/* Connection pool API */ -/** - * Get the worker from proxy configuration - * @param p memory pool used for finding worker - * @param conf current proxy server configuration - * @param url url to find the worker from - * @return proxy_worker or NULL if not found - */ -PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, - proxy_server_conf *conf, - const char *url); -/** - * Add the worker to proxy configuration - * @param worker the new worker - * @param p memory pool to allocate worker from - * @param conf current proxy server configuration - * @param url url containing worker name - * @return error message or NULL if successfull - */ -PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker, - apr_pool_t *p, - proxy_server_conf *conf, - const char *url); - -/** - * Create new worker - * @param p memory pool to allocate worker from - * @return new worker - */ -PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p); - -/** - * Initize the worker's shared data - * @param conf current proxy server configuration - * @param s current server record - * @param worker worker to initialize - */ -PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf, - proxy_worker *worker); - - -/** - * Initize the worker - * @param worker worker to initialize - * @param p memory pool to allocate worker from - * @param s current server record - * @return APR_SUCCESS or error code - */ -PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, - server_rec *s); -/** - * Get the balancer from proxy configuration - * @param p memory pool used for finding balancer - * @param conf current proxy server configuration - * @param url url to find the worker from. Has to have balancer:// prefix - * @return proxy_balancer or NULL if not found - */ -PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p, - proxy_server_conf *conf, - const char *url); -/** - * Add the balancer to proxy configuration - * @param balancer the new balancer - * @param p memory pool to allocate balancer from - * @param conf current proxy server configuration - * @param url url containing balancer name - * @return error message or NULL if successfull - */ -PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer, - apr_pool_t *p, - proxy_server_conf *conf, - const char *url); - -/** - * Add the worker to the balancer - * @param pool memory pool for adding worker - * @param balancer balancer to add to - * @param balancer worker to add - * @note Single worker can be added to multiple balancers. - */ -PROXY_DECLARE(void) ap_proxy_add_worker_to_balancer(apr_pool_t *pool, - proxy_balancer *balancer, - proxy_worker *worker); -/** - * Get the most suitable worker and(or) balancer for the request - * @param worker worker used for processing request - * @param balancer balancer used for processing request - * @param r current request - * @param conf current proxy server configuration - * @param url request url that balancer can rewrite. - * @return OK or HTTP_XXX error - * @note It calls balancer pre_request hook if the url starts with balancer:// - * The balancer then rewrites the url to particular worker, like http://host:port - */ -PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, - proxy_balancer **balancer, - request_rec *r, - proxy_server_conf *conf, - char **url); -/** - * Post request worker and balancer cleanup - * @param worker worker used for processing request - * @param balancer balancer used for processing request - * @param r current request - * @param conf current proxy server configuration - * @return OK or HTTP_XXX error - * @note When ever the pre_request is called, the post_request has to be - * called too. - */ -PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker, - proxy_balancer *balancer, - request_rec *r, - proxy_server_conf *conf); -/** - * Deternime backend hostname and port - * @param p memory pool used for processing - * @param r current request - * @param conf current proxy server configuration - * @param worker worker used for processing request - * @param conn proxy connection struct - * @param uri processed uri - * @param url request url - * @param proxyname are we connecting directly or via s proxy - * @param proxyport proxy host port - * @param server_portstr Via headers server port - * @param server_portstr_size size of the server_portstr buffer - * @return OK or HTTP_XXX error - */ -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); -/** - * Mark a worker for retry - * @param proxy_function calling proxy scheme (http, ajp, ...) - * @param conf current proxy server configuration - * @param worker worker used for retrying - * @param s current server record - * @return OK if marked for retry, DECLINED otherwise - * @note Worker will be marker for retry if the time of the last retry - * has been ellapsed. In case there is no retry option set, defaults to - * number_of_retries seconds. - */ -PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function, - proxy_worker *worker, - server_rec *s); -/** - * Acquire a connection from workers connection pool - * @param proxy_function calling proxy scheme (http, ajp, ...) - * @param conn acquired connection - * @param worker worker used for obtaining connection - * @param s current server record - * @return OK or HTTP_XXX error - * @note If the number of connections is exhaused the function will - * block untill the timeout is reached. - */ -PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, - proxy_conn_rec **conn, - proxy_worker *worker, - server_rec *s); -/** - * Release a connection back to worker connection pool - * @param proxy_function calling proxy scheme (http, ajp, ...) - * @param conn acquired connection - * @param s current server record - * @return OK or HTTP_XXX error - * @note The connection will be closed if conn->close_on_release is set - */ -PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, - proxy_conn_rec *conn, - server_rec *s); -/** - * Make a connection to the backend - * @param proxy_function calling proxy scheme (http, ajp, ...) - * @param conn acquired connection - * @param worker connection worker - * @param s current server record - * @return OK or HTTP_XXX error - * @note In case the socket already exists for conn, just check the link - * status. - */ -PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, - proxy_conn_rec *conn, - proxy_worker *worker, - server_rec *s); -/** - * Make a connection record for backend connection - * @param proxy_function calling proxy scheme (http, ajp, ...) - * @param conn acquired connection - * @param c client connection record - * @param s current server record - * @return OK or HTTP_XXX error - */ -PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function, - proxy_conn_rec *conn, - conn_rec *c, server_rec *s); - -/* Scoreboard */ -#if MODULE_MAGIC_NUMBER_MAJOR > 20020903 -#define PROXY_HAS_SCOREBOARD 1 -#else -#define PROXY_HAS_SCOREBOARD 0 -#endif - -/* The number of dynamic workers that can be added when reconfiguring. - * If this limit is reached you must stop and restart the server. - */ -#define PROXY_DYNAMIC_BALANCER_LIMIT 16 -/** - * Calculate number of maximum number of workers in scoreboard. - * @return number of workers to allocate in the scoreboard - */ -int ap_proxy_lb_workers(void); - -/* For proxy_util */ -extern module PROXY_DECLARE_DATA proxy_module; - -extern int PROXY_DECLARE_DATA proxy_lb_workers; - -#endif /*MOD_PROXY_H*/ +/* Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOD_PROXY_H +#define MOD_PROXY_H + +/* + * Main include file for the Apache proxy + */ + +/* + + Also note numerous FIXMEs and CHECKMEs which should be eliminated. + + This code is once again experimental! + + Things to do: + + 1. Make it completely work (for FTP too) + + 2. HTTP/1.1 + + Chuck Murcko 02-06-01 + + */ + +#define CORE_PRIVATE + +#include "apr_hooks.h" +#include "apr.h" +#include "apr_lib.h" +#include "apr_strings.h" +#include "apr_buckets.h" +#include "apr_md5.h" +#include "apr_network_io.h" +#include "apr_pools.h" +#include "apr_strings.h" +#include "apr_uri.h" +#include "apr_date.h" +#include "apr_strmatch.h" +#include "apr_fnmatch.h" +#include "apr_reslist.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#include "httpd.h" +#include "http_config.h" +#include "ap_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_vhost.h" +#include "http_main.h" +#include "http_log.h" +#include "http_connection.h" +#include "util_filter.h" +#include "util_ebcdic.h" + +#if APR_HAVE_NETINET_IN_H +#include +#endif +#if APR_HAVE_ARPA_INET_H +#include +#endif + +/* for proxy_canonenc() */ +enum enctype { + enc_path, enc_search, enc_user, enc_fpath, enc_parm +}; + +#if APR_CHARSET_EBCDIC +#define CRLF "\r\n" +#else /*APR_CHARSET_EBCDIC*/ +#define CRLF "\015\012" +#endif /*APR_CHARSET_EBCDIC*/ + +/* default Max-Forwards header setting */ +#define DEFAULT_MAX_FORWARDS 10 + +/* static information about a remote proxy */ +struct proxy_remote { + const char *scheme; /* the schemes handled by this proxy, or '*' */ + const char *protocol; /* the scheme used to talk to this proxy */ + const char *hostname; /* the hostname of this proxy */ + apr_port_t port; /* the port for this proxy */ + regex_t *regexp; /* compiled regex (if any) for the remote */ + int use_regex; /* simple boolean. True if we have a regex pattern */ +}; + +struct proxy_alias { + const char *real; + const char *fake; +}; + +struct dirconn_entry { + char *name; + struct in_addr addr, mask; + struct apr_sockaddr_t *hostaddr; + int (*matcher) (struct dirconn_entry * This, request_rec *r); +}; + +struct noproxy_entry { + const char *name; + struct apr_sockaddr_t *addr; +}; + +typedef struct proxy_balancer proxy_balancer; +typedef struct proxy_worker proxy_worker; +typedef struct proxy_conn_pool proxy_conn_pool; + +typedef struct { + apr_array_header_t *proxies; + apr_array_header_t *sec_proxy; + apr_array_header_t *aliases; + apr_array_header_t *raliases; + apr_array_header_t *noproxies; + apr_array_header_t *dirconn; + apr_array_header_t *allowed_connect_ports; + apr_array_header_t *workers; + apr_array_header_t *balancers; + proxy_worker *forward; /* forward proxy worker */ + const char *domain; /* domain name to use in absence of a domain name in the request */ + int req; /* true if proxy requests are enabled */ + char req_set; + enum { + via_off, + via_on, + via_block, + via_full + } viaopt; /* how to deal with proxy Via: headers */ + char viaopt_set; + apr_size_t recv_buffer_size; + char recv_buffer_size_set; + apr_size_t io_buffer_size; + char io_buffer_size_set; + long maxfwd; + char maxfwd_set; + /** + * the following setting masks the error page + * returned from the 'proxied server' and just + * forwards the status code upwards. + * This allows the main server (us) to generate + * the error page, (so it will look like a error + * returned from the rest of the system + */ + int error_override; + int error_override_set; + int preserve_host; + int preserve_host_set; + apr_interval_time_t timeout; + char timeout_set; + enum { + bad_error, + bad_ignore, + bad_body + } badopt; /* how to deal with bad headers */ + char badopt_set; +/* putting new stuff on the end maximises binary back-compatibility. + * the strmatch_patterns are really a const just to have a + * case-independent strstr. + */ + apr_array_header_t* cookie_paths; + apr_array_header_t* cookie_domains; + const apr_strmatch_pattern* cookie_path_str; + const apr_strmatch_pattern* cookie_domain_str; + enum { + status_off, + status_on, + status_full + } proxy_status; /* Status display options */ + char proxy_status_set; + apr_pool_t *pool; /* Pool used for allocating this struct */ +} proxy_server_conf; + + +typedef struct { + const char *p; /* The path */ + int p_is_fnmatch; /* Is this path an fnmatch candidate? */ + regex_t *r; /* Is this a regex? */ +} proxy_dir_conf; + +typedef struct { + conn_rec *connection; + const char *hostname; + apr_port_t port; + int is_ssl; + apr_pool_t *pool; /* Subpool used for creating socket */ + apr_socket_t *sock; /* Connection socket */ + apr_sockaddr_t *addr; /* Preparsed remote address info */ + apr_uint32_t flags; /* Conection flags */ + int close; /* Close 'this' connection */ + int close_on_recycle; /* Close the connection when returning to pool */ + proxy_worker *worker; /* Connection pool this connection belogns to */ + void *data; /* per scheme connection data */ +} proxy_conn_rec; + +typedef struct { + float cache_completion; /* completion percentage */ + int content_length; /* length of the content */ +} proxy_completion; + +/* Connection pool */ +struct proxy_conn_pool { + apr_pool_t *pool; /* The pool used in constructor and destructor calls */ + apr_sockaddr_t *addr; /* Preparsed remote address info */ +#if APR_HAS_THREADS + apr_reslist_t *res; /* Connection resource list */ +#endif + proxy_conn_rec *conn; /* Single connection for prefork mpm's */ +}; + +/* woker status flags */ +#define PROXY_WORKER_INITIALIZED 0x0001 +#define PROXY_WORKER_IGNORE_ERRORS 0x0002 +#define PROXY_WORKER_IN_SHUTDOWN 0x0010 +#define PROXY_WORKER_DISABLED 0x0020 +#define PROXY_WORKER_IN_ERROR 0x0040 + +#define PROXY_WORKER_IS_USABLE(f) (!((f)->s->status & 0x00F0)) + +/* default worker retry timeout in seconds */ +#define PROXY_WORKER_DEFAULT_RETRY 60 +#define PROXY_WORKER_MAX_ROUTE_SIZ 63 + +/* Runtime worker status informations. Shared in scoreboard */ +typedef struct { + int status; + apr_time_t error_time; /* time of the last error */ + int retries; /* number of retries on this worker */ + int lbstatus; /* Current lbstatus */ + int lbfactor; /* dynamic lbfactor */ + apr_off_t transfered; /* Number of bytes transfered to remote */ + apr_off_t readed; /* Number of bytes readed from remote */ + apr_size_t elected; /* Number of times the worker was elected */ + char route[PROXY_WORKER_MAX_ROUTE_SIZ+1]; + char redirect[PROXY_WORKER_MAX_ROUTE_SIZ+1]; +} proxy_worker_stat; + +/* Worker configuration */ +struct proxy_worker { + int id; /* scoreboard id */ + apr_interval_time_t retry; /* retry interval */ + int lbfactor; /* initial load balancing factor */ + const char *name; + const char *scheme; /* scheme to use ajp|http|https */ + const char *hostname; /* remote backend address */ + const char *route; /* balancing route */ + const char *redirect; /* temporary balancing redirection route */ + apr_port_t port; + int min; /* Desired minimum number of available connections */ + int smax; /* Soft maximum on the total number of connections */ + int hmax; /* Hard maximum on the total number of connections */ + apr_interval_time_t ttl; /* maximum amount of time in seconds a connection + * may be available while exceeding the soft limit */ + apr_interval_time_t timeout; /* connection timeout */ + char timeout_set; + apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */ + char acquire_set; + apr_size_t recv_buffer_size; + char recv_buffer_size_set; + apr_size_t io_buffer_size; + char io_buffer_size_set; + char keepalive; + char keepalive_set; + proxy_conn_pool *cp; /* Connection pool to use */ + proxy_worker_stat *s; /* Shared data */ + void *opaque; /* per scheme worker data */ +}; + +struct proxy_balancer { + apr_array_header_t *workers; /* array of proxy_workers */ + const char *name; /* name of the load balancer */ + const char *sticky; /* sticky session identifier */ + int sticky_force; /* Disable failover for sticky sessions */ + apr_interval_time_t timeout; /* Timeout for waiting on free connection */ + int max_attempts; /* Number of attempts before failing */ + char max_attempts_set; + + /* XXX: Perhaps we will need the proc mutex too. + * Altrough we are only using arithmetic operations + * it may lead to a incorrect calculations. + * For now use only the thread mutex. + */ +#if APR_HAS_THREADS + apr_thread_mutex_t *mutex; /* Thread lock for updating lb params */ +#endif +}; + +/* hooks */ + +/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and + * PROXY_DECLARE_DATA with appropriate export and import tags for the platform + */ +#if !defined(WIN32) +#define PROXY_DECLARE(type) type +#define PROXY_DECLARE_NONSTD(type) type +#define PROXY_DECLARE_DATA +#elif defined(PROXY_DECLARE_STATIC) +#define PROXY_DECLARE(type) type __stdcall +#define PROXY_DECLARE_NONSTD(type) type +#define PROXY_DECLARE_DATA +#elif defined(PROXY_DECLARE_EXPORT) +#define PROXY_DECLARE(type) __declspec(dllexport) type __stdcall +#define PROXY_DECLARE_NONSTD(type) __declspec(dllexport) type +#define PROXY_DECLARE_DATA __declspec(dllexport) +#else +#define PROXY_DECLARE(type) __declspec(dllimport) type __stdcall +#define PROXY_DECLARE_NONSTD(type) __declspec(dllimport) type +#define PROXY_DECLARE_DATA __declspec(dllimport) +#endif + +/** + * Hook an optional proxy hook. Unlike static hooks, this uses a macro + * instead of a function. + */ +#define PROXY_OPTIONAL_HOOK(name,fn,pre,succ,order) \ + APR_OPTIONAL_HOOK(proxy,name,fn,pre,succ,order) + +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, scheme_handler, (request_rec *r, + proxy_worker *worker, proxy_server_conf *conf, char *url, + const char *proxyhost, apr_port_t proxyport)) +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, canon_handler, (request_rec *r, + char *url)) + +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, create_req, (request_rec *r, request_rec *pr)) +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, fixups, (request_rec *r)) + +/** + * pre request hook. + * It will return the most suitable worker at the moment + * and coresponding balancer. + * The url is rewritten from balancer://cluster/uri to scheme://host:port/uri + * and then the scheme_handler is called. + * + */ +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, pre_request, (proxy_worker **worker, + proxy_balancer **balancer, + request_rec *r, + proxy_server_conf *conf, char **url)) +/** + * post request hook. + * It is called after request for updating runtime balancer status. + */ +APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, post_request, (proxy_worker *worker, + proxy_balancer *balancer, request_rec *r, + proxy_server_conf *conf)) + + +/* proxy_util.c */ + +PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r); +PROXY_DECLARE(int) ap_proxy_hex2c(const char *x); +PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x); +PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, + int isenc); +PROXY_DECLARE(char *)ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, + char **passwordp, char **hostp, apr_port_t *port); +PROXY_DECLARE(const char *)ap_proxy_date_canon(apr_pool_t *p, const char *x); +PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val); +PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val); +PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x); +PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y); +PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message); +PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p); +PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p); +PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p); +PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p); +PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr); +PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r); +PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos); +PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key); +/* DEPRECATED (will be replaced with ap_proxy_connect_backend */ +PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **, const char *, apr_sockaddr_t *, const char *, proxy_server_conf *, server_rec *, apr_pool_t *); +PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c); +PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c); + +/* Connection pool API */ +/** + * Get the worker from proxy configuration + * @param p memory pool used for finding worker + * @param conf current proxy server configuration + * @param url url to find the worker from + * @return proxy_worker or NULL if not found + */ +PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p, + proxy_server_conf *conf, + const char *url); +/** + * Add the worker to proxy configuration + * @param worker the new worker + * @param p memory pool to allocate worker from + * @param conf current proxy server configuration + * @param url url containing worker name + * @return error message or NULL if successfull + */ +PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker, + apr_pool_t *p, + proxy_server_conf *conf, + const char *url); + +/** + * Create new worker + * @param p memory pool to allocate worker from + * @return new worker + */ +PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p); + +/** + * Initize the worker's shared data + * @param conf current proxy server configuration + * @param s current server record + * @param worker worker to initialize + */ +PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf, + proxy_worker *worker); + + +/** + * Initize the worker + * @param worker worker to initialize + * @param p memory pool to allocate worker from + * @param s current server record + * @return APR_SUCCESS or error code + */ +PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, + server_rec *s); +/** + * Get the balancer from proxy configuration + * @param p memory pool used for finding balancer + * @param conf current proxy server configuration + * @param url url to find the worker from. Has to have balancer:// prefix + * @return proxy_balancer or NULL if not found + */ +PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p, + proxy_server_conf *conf, + const char *url); +/** + * Add the balancer to proxy configuration + * @param balancer the new balancer + * @param p memory pool to allocate balancer from + * @param conf current proxy server configuration + * @param url url containing balancer name + * @return error message or NULL if successfull + */ +PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer, + apr_pool_t *p, + proxy_server_conf *conf, + const char *url); + +/** + * Add the worker to the balancer + * @param pool memory pool for adding worker + * @param balancer balancer to add to + * @param balancer worker to add + * @note Single worker can be added to multiple balancers. + */ +PROXY_DECLARE(void) ap_proxy_add_worker_to_balancer(apr_pool_t *pool, + proxy_balancer *balancer, + proxy_worker *worker); +/** + * Get the most suitable worker and(or) balancer for the request + * @param worker worker used for processing request + * @param balancer balancer used for processing request + * @param r current request + * @param conf current proxy server configuration + * @param url request url that balancer can rewrite. + * @return OK or HTTP_XXX error + * @note It calls balancer pre_request hook if the url starts with balancer:// + * The balancer then rewrites the url to particular worker, like http://host:port + */ +PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, + proxy_balancer **balancer, + request_rec *r, + proxy_server_conf *conf, + char **url); +/** + * Post request worker and balancer cleanup + * @param worker worker used for processing request + * @param balancer balancer used for processing request + * @param r current request + * @param conf current proxy server configuration + * @return OK or HTTP_XXX error + * @note When ever the pre_request is called, the post_request has to be + * called too. + */ +PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker, + proxy_balancer *balancer, + request_rec *r, + proxy_server_conf *conf); +/** + * Deternime backend hostname and port + * @param p memory pool used for processing + * @param r current request + * @param conf current proxy server configuration + * @param worker worker used for processing request + * @param conn proxy connection struct + * @param uri processed uri + * @param url request url + * @param proxyname are we connecting directly or via s proxy + * @param proxyport proxy host port + * @param server_portstr Via headers server port + * @param server_portstr_size size of the server_portstr buffer + * @return OK or HTTP_XXX error + */ +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); +/** + * Mark a worker for retry + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conf current proxy server configuration + * @param worker worker used for retrying + * @param s current server record + * @return OK if marked for retry, DECLINED otherwise + * @note Worker will be marker for retry if the time of the last retry + * has been ellapsed. In case there is no retry option set, defaults to + * number_of_retries seconds. + */ +PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function, + proxy_worker *worker, + server_rec *s); +/** + * Acquire a connection from workers connection pool + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param worker worker used for obtaining connection + * @param s current server record + * @return OK or HTTP_XXX error + * @note If the number of connections is exhaused the function will + * block untill the timeout is reached. + */ +PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function, + proxy_conn_rec **conn, + proxy_worker *worker, + server_rec *s); +/** + * Release a connection back to worker connection pool + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param s current server record + * @return OK or HTTP_XXX error + * @note The connection will be closed if conn->close_on_release is set + */ +PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function, + proxy_conn_rec *conn, + server_rec *s); +/** + * Make a connection to the backend + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param worker connection worker + * @param s current server record + * @return OK or HTTP_XXX error + * @note In case the socket already exists for conn, just check the link + * status. + */ +PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function, + proxy_conn_rec *conn, + proxy_worker *worker, + server_rec *s); +/** + * Make a connection record for backend connection + * @param proxy_function calling proxy scheme (http, ajp, ...) + * @param conn acquired connection + * @param c client connection record + * @param s current server record + * @return OK or HTTP_XXX error + */ +PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function, + proxy_conn_rec *conn, + conn_rec *c, server_rec *s); + +/* Scoreboard */ +#if MODULE_MAGIC_NUMBER_MAJOR > 20020903 +#define PROXY_HAS_SCOREBOARD 1 +#else +#define PROXY_HAS_SCOREBOARD 0 +#endif + +/* The number of dynamic workers that can be added when reconfiguring. + * If this limit is reached you must stop and restart the server. + */ +#define PROXY_DYNAMIC_BALANCER_LIMIT 16 +/** + * Calculate number of maximum number of workers in scoreboard. + * @return number of workers to allocate in the scoreboard + */ +int ap_proxy_lb_workers(void); + +/* For proxy_util */ +extern module PROXY_DECLARE_DATA proxy_module; + +extern int PROXY_DECLARE_DATA proxy_lb_workers; + +#endif /*MOD_PROXY_H*/ diff --git a/modules/proxy/proxy_ajp.c b/modules/proxy/proxy_ajp.c index a0d7f27900..13c9b102cf 100644 --- a/modules/proxy/proxy_ajp.c +++ b/modules/proxy/proxy_ajp.c @@ -303,7 +303,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, "ajp_read_header failed"); break; } - result = ajp_parse_type(r, conn->data); + result = ajp_parse_type(r, conn->data); } apr_brigade_destroy(input_brigade); diff --git a/modules/proxy/proxy_balancer.c b/modules/proxy/proxy_balancer.c index 1f38a0487c..4dadd59e1b 100644 --- a/modules/proxy/proxy_balancer.c +++ b/modules/proxy/proxy_balancer.c @@ -45,7 +45,7 @@ static int init_runtime_score(proxy_server_conf *conf, server_rec *s, proxy_bala for (i = 0; i < balancer->workers->nelts; i++) { ap_proxy_initialize_worker_share(conf, workers); - ap_proxy_initialize_worker(workers, s); + ap_proxy_initialize_worker(workers, s); workers->s->status = PROXY_WORKER_INITIALIZED; ++workers; } @@ -284,19 +284,19 @@ static proxy_worker *find_best_worker(proxy_balancer *balancer, * not in error state or not disabled. */ if (PROXY_WORKER_IS_USABLE(worker)) { - worker->s->lbstatus += worker->s->lbfactor; - total_factor += worker->s->lbfactor; - if (!candidate || worker->s->lbstatus > candidate->s->lbstatus) - candidate = worker; - } + worker->s->lbstatus += worker->s->lbfactor; + total_factor += worker->s->lbfactor; + if (!candidate || worker->s->lbstatus > candidate->s->lbstatus) + candidate = worker; + } worker++; } if (candidate) { - candidate->s->lbstatus -= total_factor; - candidate->s->elected++; + candidate->s->lbstatus -= total_factor; + candidate->s->elected++; PROXY_BALANCER_UNLOCK(balancer); - return candidate; + return candidate; } else { PROXY_BALANCER_UNLOCK(balancer); @@ -399,13 +399,13 @@ static int proxy_balancer_pre_request(proxy_worker **worker, * not in error state or not disabled. */ if (PROXY_WORKER_IS_USABLE(workers)) { - workers->s->lbstatus += workers->s->lbfactor; - total_factor += workers->s->lbfactor; - } + workers->s->lbstatus += workers->s->lbfactor; + total_factor += workers->s->lbfactor; + } workers++; } - runtime->s->lbstatus -= total_factor; - runtime->s->elected++; + runtime->s->lbstatus -= total_factor; + runtime->s->elected++; *worker = runtime; } @@ -787,7 +787,7 @@ static void child_init(apr_pool_t *p, server_rec *s) /* Initialize shared scoreboard data */ balancer = (proxy_balancer *)conf->balancers->elts; for (i = 0; i < conf->balancers->nelts; i++) { - init_runtime_score(conf, s, balancer); + init_runtime_score(conf, s, balancer); balancer++; } s = s->next; diff --git a/modules/proxy/proxy_connect.c b/modules/proxy/proxy_connect.c index 7dd16e34d3..fb672db192 100644 --- a/modules/proxy/proxy_connect.c +++ b/modules/proxy/proxy_connect.c @@ -58,8 +58,8 @@ allowed_port(proxy_server_conf *conf, int port) int *list = (int *) conf->allowed_connect_ports->elts; for(i = 0; i < conf->allowed_connect_ports->nelts; i++) { - if(port == list[i]) - return 1; + if(port == list[i]) + return 1; } return 0; } @@ -69,10 +69,10 @@ int ap_proxy_connect_canon(request_rec *r, char *url) { if (r->method_number != M_CONNECT) { - return DECLINED; + return DECLINED; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: canonicalising URL %s", url); + "proxy: CONNECT: canonicalising URL %s", url); return OK; } @@ -104,11 +104,11 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, /* is this for us? */ if (r->method_number != M_CONNECT) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: declining URL %s", url); - return DECLINED; + "proxy: CONNECT: declining URL %s", url); + return DECLINED; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: serving URL %s", url); + "proxy: CONNECT: serving URL %s", url); /* @@ -119,50 +119,50 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, /* we break the URL into host, port, uri */ if (APR_SUCCESS != apr_uri_parse_hostinfo(p, url, &uri)) { - return ap_proxyerror(r, HTTP_BAD_REQUEST, - apr_pstrcat(p, "URI cannot be parsed: ", url, NULL)); + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_pstrcat(p, "URI cannot be parsed: ", url, NULL)); } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: connecting %s to %s:%d", url, uri.hostname, uri.port); + "proxy: CONNECT: connecting %s to %s:%d", url, uri.hostname, uri.port); /* do a DNS lookup for the destination host */ err = apr_sockaddr_info_get(&uri_addr, uri.hostname, APR_UNSPEC, uri.port, 0, p); /* are we connecting directly, or via a proxy? */ if (proxyname) { - connectname = proxyname; - connectport = proxyport; + connectname = proxyname; + connectport = proxyport; err = apr_sockaddr_info_get(&connect_addr, proxyname, APR_UNSPEC, proxyport, 0, p); } else { - connectname = uri.hostname; - connectport = uri.port; - connect_addr = uri_addr; + connectname = uri.hostname; + connectport = uri.port; + connect_addr = uri_addr; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: connecting to remote proxy %s on port %d", connectname, connectport); + "proxy: CONNECT: connecting to remote proxy %s on port %d", connectname, connectport); /* check if ProxyBlock directive on this host */ if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) { - return ap_proxyerror(r, HTTP_FORBIDDEN, - "Connect to remote machine blocked"); + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); } /* Check if it is an allowed port */ if (conf->allowed_connect_ports->nelts == 0) { - /* Default setting if not overridden by AllowCONNECT */ - switch (uri.port) { - case APR_URI_HTTPS_DEFAULT_PORT: - case APR_URI_SNEWS_DEFAULT_PORT: - break; - default: + /* Default setting if not overridden by AllowCONNECT */ + switch (uri.port) { + case APR_URI_HTTPS_DEFAULT_PORT: + case APR_URI_SNEWS_DEFAULT_PORT: + break; + default: /* XXX can we call ap_proxyerror() here to get a nice log message? */ - return HTTP_FORBIDDEN; - } + return HTTP_FORBIDDEN; + } } else if(!allowed_port(conf, uri.port)) { /* XXX can we call ap_proxyerror() here to get a nice log message? */ - return HTTP_FORBIDDEN; + return HTTP_FORBIDDEN; } /* @@ -175,7 +175,7 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, * until we get a successful connection */ if (APR_SUCCESS != err) { - return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, "DNS lookup failure for: ", connectname, NULL)); } @@ -222,26 +222,26 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, * the CONNECT request on to it. */ if (proxyport) { - /* FIXME: Error checking ignored. - */ + /* FIXME: Error checking ignored. + */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: sending the CONNECT request to the remote proxy"); + "proxy: CONNECT: sending the CONNECT request to the remote proxy"); nbytes = apr_snprintf(buffer, sizeof(buffer), - "CONNECT %s HTTP/1.0" CRLF, r->uri); + "CONNECT %s HTTP/1.0" CRLF, r->uri); apr_socket_send(sock, buffer, &nbytes); nbytes = apr_snprintf(buffer, sizeof(buffer), - "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); apr_socket_send(sock, buffer, &nbytes); } else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: Returning 200 OK Status"); + "proxy: CONNECT: Returning 200 OK Status"); nbytes = apr_snprintf(buffer, sizeof(buffer), - "HTTP/1.0 200 Connection Established" CRLF); + "HTTP/1.0 200 Connection Established" CRLF); ap_xlate_proto_to_ascii(buffer, nbytes); apr_socket_send(client_socket, buffer, &nbytes); nbytes = apr_snprintf(buffer, sizeof(buffer), - "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); ap_xlate_proto_to_ascii(buffer, nbytes); apr_socket_send(client_socket, buffer, &nbytes); #if 0 @@ -256,7 +256,7 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: setting up poll()"); + "proxy: CONNECT: setting up poll()"); /* * Step Four: Handle Data Transfer @@ -268,7 +268,7 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, if ((rv = apr_pollset_create(&pollset, 2, r->pool, 0)) != APR_SUCCESS) { - apr_socket_close(sock); + apr_socket_close(sock); ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: CONNECT: error apr_pollset_create()"); return HTTP_INTERNAL_SERVER_ERROR; @@ -288,7 +288,7 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, while (1) { /* Infinite loop until error (one side closes the connection) */ if ((rv = apr_pollset_poll(pollset, -1, &pollcnt, &signalled)) != APR_SUCCESS) { - apr_socket_close(sock); + apr_socket_close(sock); ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: CONNECT: error apr_poll()"); return HTTP_INTERNAL_SERVER_ERROR; } @@ -375,7 +375,7 @@ int ap_proxy_connect_handler(request_rec *r, proxy_worker *worker, } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: CONNECT: finished with poll() - cleaning up"); + "proxy: CONNECT: finished with poll() - cleaning up"); /* * Step Five: Clean Up @@ -396,10 +396,10 @@ static void ap_proxy_connect_register_hook(apr_pool_t *p) module AP_MODULE_DECLARE_DATA proxy_connect_module = { STANDARD20_MODULE_STUFF, - NULL, /* create per-directory config structure */ - NULL, /* merge per-directory config structures */ - NULL, /* create per-server config structure */ - NULL, /* merge per-server config structures */ - NULL, /* command apr_table_t */ - ap_proxy_connect_register_hook /* register hooks */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_connect_register_hook /* register hooks */ }; diff --git a/modules/proxy/proxy_ftp.c b/modules/proxy/proxy_ftp.c index e82636d265..2efcda67bd 100644 --- a/modules/proxy/proxy_ftp.c +++ b/modules/proxy/proxy_ftp.c @@ -96,7 +96,7 @@ static int ftp_check_globbingchars(const char *path) { for ( ; *path; ++path) { if (*path == '\\') - ++path; + ++path; if (path != '\0' && strchr(FTP_GLOBBING_CHARS, *path) != NULL) return TRUE; } @@ -1531,39 +1531,39 @@ int ap_proxy_ftp_handler(request_rec *r, proxy_worker *worker, proxy_server_conf r, origin, bb, &ftpmessage); /* then extract the Last-Modified time from it (YYYYMMDDhhmmss or YYYYMMDDhhmmss.xxx GMT). */ if (rc == 213) { - struct { - char YYYY[4+1]; - char MM[2+1]; - char DD[2+1]; - char hh[2+1]; - char mm[2+1]; - char ss[2+1]; - } time_val; - if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]", - time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) { + struct { + char YYYY[4+1]; + char MM[2+1]; + char DD[2+1]; + char hh[2+1]; + char mm[2+1]; + char ss[2+1]; + } time_val; + if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]", + time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) { struct tm tms; - memset (&tms, '\0', sizeof tms); - tms.tm_year = atoi(time_val.YYYY) - 1900; - tms.tm_mon = atoi(time_val.MM) - 1; - tms.tm_mday = atoi(time_val.DD); - tms.tm_hour = atoi(time_val.hh); - tms.tm_min = atoi(time_val.mm); - tms.tm_sec = atoi(time_val.ss); + memset (&tms, '\0', sizeof tms); + tms.tm_year = atoi(time_val.YYYY) - 1900; + tms.tm_mon = atoi(time_val.MM) - 1; + tms.tm_mday = atoi(time_val.DD); + tms.tm_hour = atoi(time_val.hh); + tms.tm_min = atoi(time_val.mm); + tms.tm_sec = atoi(time_val.ss); #ifdef HAVE_TIMEGM /* Does system have timegm()? */ - mtime = timegm(&tms); - mtime *= APR_USEC_PER_SEC; + mtime = timegm(&tms); + mtime *= APR_USEC_PER_SEC; #elif HAVE_GMTOFF /* does struct tm have a member tm_gmtoff? */ /* mktime will subtract the local timezone, which is not what we want. - * Add it again because the MDTM string is GMT - */ - mtime = mktime(&tms); - mtime += tms.tm_gmtoff; - mtime *= APR_USEC_PER_SEC; + * Add it again because the MDTM string is GMT + */ + mtime = mktime(&tms); + mtime += tms.tm_gmtoff; + mtime *= APR_USEC_PER_SEC; #else - mtime = 0L; + mtime = 0L; #endif } - } + } #endif /* USE_MDTM */ /* FIXME: Handle range requests - send REST */ buf = apr_pstrcat(p, "RETR ", ftp_escape_globbingchars(p, path), CRLF, NULL); diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 2819ca8001..4293e1e1d2 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -1,1843 +1,1843 @@ -/* Copyright 1999-2004 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Utility routines for Apache proxy */ -#include "mod_proxy.h" -#include "ap_mpm.h" -#include "scoreboard.h" -#include "apr_version.h" - -#if APR_HAVE_UNISTD_H -#include /* for getpid() */ -#endif - -#if (APR_MAJOR_VERSION < 1) -#undef apr_socket_create -#define apr_socket_create apr_socket_create_ex -#endif - -/* Global balancer counter */ -int PROXY_DECLARE_DATA proxy_lb_workers = 0; -static int lb_workers_limit = 0; - -static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); -static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); -static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); -static int proxy_match_word(struct dirconn_entry *This, request_rec *r); - -APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, - (request_rec *r, request_rec *pr), (r, pr), - OK, DECLINED) - -/* already called in the knowledge that the characters are hex digits */ -PROXY_DECLARE(int) ap_proxy_hex2c(const char *x) -{ - int i, ch; - -#if !APR_CHARSET_EBCDIC - ch = x[0]; - if (apr_isdigit(ch)) - i = ch - '0'; - else if (apr_isupper(ch)) - i = ch - ('A' - 10); - else - i = ch - ('a' - 10); - i <<= 4; - - ch = x[1]; - if (apr_isdigit(ch)) - i += ch - '0'; - else if (apr_isupper(ch)) - i += ch - ('A' - 10); - else - i += ch - ('a' - 10); - return i; -#else /*APR_CHARSET_EBCDIC*/ - /* we assume that the hex value refers to an ASCII character - * so convert to EBCDIC so that it makes sense locally; - * - * example: - * - * client specifies %20 in URL to refer to a space char; - * at this point we're called with EBCDIC "20"; after turning - * EBCDIC "20" into binary 0x20, we then need to assume that 0x20 - * represents an ASCII char and convert 0x20 to EBCDIC, yielding - * 0x40 - */ - char buf[1]; - - if (1 == sscanf(x, "%2x", &i)) { - buf[0] = i & 0xFF; - ap_xlate_proto_from_ascii(buf, 1); - return buf[0]; - } - else { - return 0; - } -#endif /*APR_CHARSET_EBCDIC*/ -} - -PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x) -{ -#if !APR_CHARSET_EBCDIC - int i; - - x[0] = '%'; - i = (ch & 0xF0) >> 4; - if (i >= 10) - x[1] = ('A' - 10) + i; - else - x[1] = '0' + i; - - i = ch & 0x0F; - if (i >= 10) - x[2] = ('A' - 10) + i; - else - x[2] = '0' + i; -#else /*APR_CHARSET_EBCDIC*/ - static const char ntoa[] = { "0123456789ABCDEF" }; - char buf[1]; - - ch &= 0xFF; - - buf[0] = ch; - ap_xlate_proto_to_ascii(buf, 1); - - x[0] = '%'; - x[1] = ntoa[(buf[0] >> 4) & 0x0F]; - x[2] = ntoa[buf[0] & 0x0F]; - x[3] = '\0'; -#endif /*APR_CHARSET_EBCDIC*/ -} - -/* - * canonicalise a URL-encoded string - */ - -/* - * Convert a URL-encoded string to canonical form. - * It decodes characters which need not be encoded, - * and encodes those which must be encoded, and does not touch - * those which must not be touched. - */ -PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, - int isenc) -{ - int i, j, ch; - char *y; - char *allowed; /* characters which should not be encoded */ - char *reserved; /* characters which much not be en/de-coded */ - -/* N.B. in addition to :@&=, this allows ';' in an http path - * and '?' in an ftp path -- this may be revised - * - * Also, it makes a '+' character in a search string reserved, as - * it may be form-encoded. (Although RFC 1738 doesn't allow this - - * it only permits ; / ? : @ = & as reserved chars.) - */ - if (t == enc_path) - allowed = "$-_.+!*'(),;:@&="; - else if (t == enc_search) - allowed = "$-_.!*'(),;:@&="; - else if (t == enc_user) - allowed = "$-_.+!*'(),;@&="; - else if (t == enc_fpath) - allowed = "$-_.+!*'(),?:@&="; - else /* if (t == enc_parm) */ - allowed = "$-_.+!*'(),?/:@&="; - - if (t == enc_path) - reserved = "/"; - else if (t == enc_search) - reserved = "+"; - else - reserved = ""; - - y = apr_palloc(p, 3 * len + 1); - - for (i = 0, j = 0; i < len; i++, j++) { -/* always handle '/' first */ - ch = x[i]; - if (strchr(reserved, ch)) { - y[j] = ch; - continue; - } -/* decode it if not already done */ - if (isenc && ch == '%') { - if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) - return NULL; - ch = ap_proxy_hex2c(&x[i + 1]); - i += 2; - if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ - ap_proxy_c2hex(ch, &y[j]); - j += 2; - continue; - } - } -/* recode it, if necessary */ - if (!apr_isalnum(ch) && !strchr(allowed, ch)) { - ap_proxy_c2hex(ch, &y[j]); - j += 2; - } - else - y[j] = ch; - } - y[j] = '\0'; - return y; -} - -/* - * Parses network-location. - * urlp on input the URL; on output the path, after the leading / - * user NULL if no user/password permitted - * password holder for password - * host holder for host - * port port number; only set if one is supplied. - * - * Returns an error string. - */ -PROXY_DECLARE(char *) - ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, - char **passwordp, char **hostp, apr_port_t *port) -{ - char *addr, *scope_id, *strp, *host, *url = *urlp; - char *user = NULL, *password = NULL; - apr_port_t tmp_port; - apr_status_t rv; - - if (url[0] != '/' || url[1] != '/') - return "Malformed URL"; - host = url + 2; - url = strchr(host, '/'); - if (url == NULL) - url = ""; - else - *(url++) = '\0'; /* skip seperating '/' */ - - /* find _last_ '@' since it might occur in user/password part */ - strp = strrchr(host, '@'); - - if (strp != NULL) { - *strp = '\0'; - user = host; - host = strp + 1; - -/* find password */ - strp = strchr(user, ':'); - if (strp != NULL) { - *strp = '\0'; - password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1); - if (password == NULL) - return "Bad %-escape in URL (password)"; - } - - user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1); - if (user == NULL) - return "Bad %-escape in URL (username)"; - } - if (userp != NULL) { - *userp = user; - } - if (passwordp != NULL) { - *passwordp = password; - } - - /* Parse the host string to separate host portion from optional port. - * Perform range checking on port. - */ - rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p); - if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) { - return "Invalid host/port"; - } - if (tmp_port != 0) { /* only update caller's port if port was specified */ - *port = tmp_port; - } - - ap_str_tolower(addr); /* DNS names are case-insensitive */ - - *urlp = url; - *hostp = addr; - - return NULL; -} - -static const char * const lwday[7] = -{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; - -/* - * If the date is a valid RFC 850 date or asctime() date, then it - * is converted to the RFC 1123 format, otherwise it is not modified. - * This routine is not very fast at doing conversions, as it uses - * sscanf and sprintf. However, if the date is already correctly - * formatted, then it exits very quickly. - */ -PROXY_DECLARE(const char *) - ap_proxy_date_canon(apr_pool_t *p, const char *x1) -{ - char *x = apr_pstrdup(p, x1); - int wk, mday, year, hour, min, sec, mon; - char *q, month[4], zone[4], week[4]; - - q = strchr(x, ','); - /* check for RFC 850 date */ - if (q != NULL && q - x > 3 && q[1] == ' ') { - *q = '\0'; - for (wk = 0; wk < 7; wk++) - if (strcmp(x, lwday[wk]) == 0) - break; - *q = ','; - if (wk == 7) - return x; /* not a valid date */ - if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || - q[17] != ':' || strcmp(&q[20], " GMT") != 0) - return x; - if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, - &hour, &min, &sec, zone) != 7) - return x; - if (year < 70) - year += 2000; - else - year += 1900; - } - else { -/* check for acstime() date */ - if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || - x[16] != ':' || x[19] != ' ' || x[24] != '\0') - return x; - if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, - &min, &sec, &year) != 7) - return x; - for (wk = 0; wk < 7; wk++) - if (strcmp(week, apr_day_snames[wk]) == 0) - break; - if (wk == 7) - return x; - } - -/* check date */ - for (mon = 0; mon < 12; mon++) - if (strcmp(month, apr_month_snames[mon]) == 0) - break; - if (mon == 12) - return x; - - q = apr_palloc(p, 30); - apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk], - mday, apr_month_snames[mon], year, hour, min, sec); - return q; -} - -PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r) -{ - request_rec *rp = apr_pcalloc(c->pool, sizeof(*r)); - - rp->pool = c->pool; - rp->status = HTTP_OK; - - rp->headers_in = apr_table_make(c->pool, 50); - rp->subprocess_env = apr_table_make(c->pool, 50); - rp->headers_out = apr_table_make(c->pool, 12); - rp->err_headers_out = apr_table_make(c->pool, 5); - rp->notes = apr_table_make(c->pool, 5); - - rp->server = r->server; - rp->proxyreq = r->proxyreq; - rp->request_time = r->request_time; - rp->connection = c; - rp->output_filters = c->output_filters; - rp->input_filters = c->input_filters; - rp->proto_output_filters = c->output_filters; - rp->proto_input_filters = c->input_filters; - - rp->request_config = ap_create_request_config(c->pool); - proxy_run_create_req(r, rp); - - return rp; -} - - -/* - * list is a comma-separated list of case-insensitive tokens, with - * optional whitespace around the tokens. - * The return returns 1 if the token val is found in the list, or 0 - * otherwise. - */ -PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val) -{ - int len, i; - const char *p; - - len = strlen(val); - - while (list != NULL) { - p = ap_strchr_c(list, ','); - if (p != NULL) { - i = p - list; - do - p++; - while (apr_isspace(*p)); - } - else - i = strlen(list); - - while (i > 0 && apr_isspace(list[i - 1])) - i--; - if (i == len && strncasecmp(list, val, len) == 0) - return 1; - list = p; - } - return 0; -} - -/* - * list is a comma-separated list of case-insensitive tokens, with - * optional whitespace around the tokens. - * if val appears on the list of tokens, it is removed from the list, - * and the new list is returned. - */ -PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val) -{ - int len, i; - const char *p; - char *new = NULL; - - len = strlen(val); - - while (list != NULL) { - p = ap_strchr_c(list, ','); - if (p != NULL) { - i = p - list; - do - p++; - while (apr_isspace(*p)); - } - else - i = strlen(list); - - while (i > 0 && apr_isspace(list[i - 1])) - i--; - if (i == len && strncasecmp(list, val, len) == 0) { - /* do nothing */ - } - else { - if (new) - new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL); - else - new = apr_pstrndup(pool, list, i); - } - list = p; - } - return new; -} - -/* - * Converts 8 hex digits to a time integer - */ -PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x) -{ - int i, ch; - unsigned int j; - - for (i = 0, j = 0; i < 8; i++) { - ch = x[i]; - j <<= 4; - if (apr_isdigit(ch)) - j |= ch - '0'; - else if (apr_isupper(ch)) - j |= ch - ('A' - 10); - else - j |= ch - ('a' - 10); - } - if (j == 0xffffffff) - return -1; /* so that it works with 8-byte ints */ - else - return j; -} - -/* - * Converts a time integer to 8 hex digits - */ -PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y) -{ - int i, ch; - unsigned int j = t; - - for (i = 7; i >= 0; i--) { - ch = j & 0xF; - j >>= 4; - if (ch >= 10) - y[i] = ch + ('A' - 10); - else - y[i] = ch + '0'; - } - y[8] = '\0'; -} - -PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message) -{ - apr_table_setn(r->notes, "error-notes", - apr_pstrcat(r->pool, - "The proxy server could not handle the request " - "pool, r->uri), - "\">", ap_escape_html(r->pool, r->method), - " ", - ap_escape_html(r->pool, r->uri), ".

\n" - "Reason: ", - ap_escape_html(r->pool, message), - "

", 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\n", - inet_ntoa(This->addr), bits); - } - - This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits)); - - if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, - "Warning: NetMask and IP-Addr disagree in %s/%ld\n", - inet_ntoa(This->addr), bits); - This->addr.s_addr &= This->mask.s_addr; - ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, - " Set to %s/%ld\n", - inet_ntoa(This->addr), bits); - } - - if (*addr == '\0') { - This->matcher = proxy_match_ipaddr; - return 1; - } - else - return (*addr == '\0'); /* okay iff we've parsed the whole string */ -} - -/* Return TRUE if addr represents an IP address (or an IP network address) */ -static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) -{ - int i, ip_addr[4]; - struct in_addr addr, *ip; - const char *host = proxy_get_host_of_request(r); - - if (host == NULL) /* oops! */ - return 0; - - memset(&addr, '\0', sizeof addr); - memset(ip_addr, '\0', sizeof ip_addr); - - if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { - for (addr.s_addr = 0, i = 0; i < 4; ++i) - addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); - - if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { -#if DEBUGGING - ap_log_error(APLOG_MARK, APLOG_STARTUP, 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(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); - - 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)); - - (*balancer)->name = uri; - (*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; - 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'; - - worker = (proxy_worker *)conf->workers->elts; - for (i = 0; i < conf->workers->nelts; i++) { - if (strcasecmp(worker->name, uri) == 0) { - return worker; - } - worker++; - } - return NULL; -} - -#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) -{ - char *c, *q, *uri = apr_pstrdup(p, url); - int port; - - c = strchr(uri, ':'); - if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') - return "Bad syntax for a remote proxy server"; - /* remove path from uri */ - if ((q = strchr(c + 3, '/'))) - *q = '\0'; - - q = strchr(c + 3, ':'); - if (q != NULL) { - if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) { - return "Bad syntax for a remote proxy server (bad port number)"; - } - } - else - port = -1; - ap_str_tolower(uri); - *worker = apr_array_push(conf->workers); - memset(*worker, 0, sizeof(proxy_worker)); - (*worker)->name = apr_pstrdup(p, uri); - *c = '\0'; - (*worker)->scheme = uri; - (*worker)->hostname = c + 3; - - if (port == -1) - port = apr_uri_port_of_scheme((*worker)->scheme); - (*worker)->port = port; - (*worker)->id = proxy_lb_workers; - /* Increase the total worker count */ - proxy_lb_workers++; - init_conn_pool(p, *worker); - - 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) { - *balancer = NULL; - access_status = OK; - } - else if (r->proxyreq == PROXYREQ_PROXY) { - if (conf->forward) { - *balancer = NULL; - *worker = conf->forward; - access_status = OK; - } - } - } - else if (access_status == DECLINED && balancer != NULL) { - /* All the workers are busy */ - 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; -} - -/* 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; -} - -PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf, - proxy_worker *worker) -{ -#if PROXY_HAS_SCOREBOARD - lb_score *score = NULL; -#else - void *score = NULL; -#endif -#if PROXY_HAS_SCOREBOARD - /* Get scoreboard slot */ - if (ap_scoreboard_image) { - score = ap_get_scoreboard_lb(worker->id); - if (!score) - ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conf->pool, - "proxy: ap_get_scoreboard_lb(%d) failed for worker %s", - worker->id, worker->name); - } -#endif - if (!score) - score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat)); - worker->s = (proxy_worker_stat *)score; - 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'; -} - -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; - - 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 for (%s) min=%d max=%d smax=%d", - worker->id, 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 for (%s)", - worker->id, worker->hostname); - } - if (rv == APR_SUCCESS) - worker->s->status |= PROXY_WORKER_INITIALIZED; - /* Set default parameters */ - if (!worker->retry) - worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY); - 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) { - 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); - } - if (!conn->hostname) { - 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; - } - } - /* TODO: add address cache for forward proxies */ - conn->addr = worker->cp->addr; - if (r->proxyreq == PROXYREQ_PROXY) { - err = apr_sockaddr_info_get(&(conn->addr), - conn->hostname, APR_UNSPEC, - conn->port, 0, - conn->pool); - } - else if (!worker->cp->addr) { - /* 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; - } - 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"); - } - 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, ¤t_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; - - /* 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 */ - ap_run_pre_connection(conn->connection, conn->sock); - - 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; -} +/* Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Utility routines for Apache proxy */ +#include "mod_proxy.h" +#include "ap_mpm.h" +#include "scoreboard.h" +#include "apr_version.h" + +#if APR_HAVE_UNISTD_H +#include /* for getpid() */ +#endif + +#if (APR_MAJOR_VERSION < 1) +#undef apr_socket_create +#define apr_socket_create apr_socket_create_ex +#endif + +/* Global balancer counter */ +int PROXY_DECLARE_DATA proxy_lb_workers = 0; +static int lb_workers_limit = 0; + +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_word(struct dirconn_entry *This, request_rec *r); + +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, + (request_rec *r, request_rec *pr), (r, pr), + OK, DECLINED) + +/* already called in the knowledge that the characters are hex digits */ +PROXY_DECLARE(int) ap_proxy_hex2c(const char *x) +{ + int i, ch; + +#if !APR_CHARSET_EBCDIC + ch = x[0]; + if (apr_isdigit(ch)) + i = ch - '0'; + else if (apr_isupper(ch)) + i = ch - ('A' - 10); + else + i = ch - ('a' - 10); + i <<= 4; + + ch = x[1]; + if (apr_isdigit(ch)) + i += ch - '0'; + else if (apr_isupper(ch)) + i += ch - ('A' - 10); + else + i += ch - ('a' - 10); + return i; +#else /*APR_CHARSET_EBCDIC*/ + /* we assume that the hex value refers to an ASCII character + * so convert to EBCDIC so that it makes sense locally; + * + * example: + * + * client specifies %20 in URL to refer to a space char; + * at this point we're called with EBCDIC "20"; after turning + * EBCDIC "20" into binary 0x20, we then need to assume that 0x20 + * represents an ASCII char and convert 0x20 to EBCDIC, yielding + * 0x40 + */ + char buf[1]; + + if (1 == sscanf(x, "%2x", &i)) { + buf[0] = i & 0xFF; + ap_xlate_proto_from_ascii(buf, 1); + return buf[0]; + } + else { + return 0; + } +#endif /*APR_CHARSET_EBCDIC*/ +} + +PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x) +{ +#if !APR_CHARSET_EBCDIC + int i; + + x[0] = '%'; + i = (ch & 0xF0) >> 4; + if (i >= 10) + x[1] = ('A' - 10) + i; + else + x[1] = '0' + i; + + i = ch & 0x0F; + if (i >= 10) + x[2] = ('A' - 10) + i; + else + x[2] = '0' + i; +#else /*APR_CHARSET_EBCDIC*/ + static const char ntoa[] = { "0123456789ABCDEF" }; + char buf[1]; + + ch &= 0xFF; + + buf[0] = ch; + ap_xlate_proto_to_ascii(buf, 1); + + x[0] = '%'; + x[1] = ntoa[(buf[0] >> 4) & 0x0F]; + x[2] = ntoa[buf[0] & 0x0F]; + x[3] = '\0'; +#endif /*APR_CHARSET_EBCDIC*/ +} + +/* + * canonicalise a URL-encoded string + */ + +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, + int isenc) +{ + int i, j, ch; + char *y; + char *allowed; /* characters which should not be encoded */ + char *reserved; /* characters which much not be en/de-coded */ + +/* N.B. in addition to :@&=, this allows ';' in an http path + * and '?' in an ftp path -- this may be revised + * + * Also, it makes a '+' character in a search string reserved, as + * it may be form-encoded. (Although RFC 1738 doesn't allow this - + * it only permits ; / ? : @ = & as reserved chars.) + */ + if (t == enc_path) + allowed = "$-_.+!*'(),;:@&="; + else if (t == enc_search) + allowed = "$-_.!*'(),;:@&="; + else if (t == enc_user) + allowed = "$-_.+!*'(),;@&="; + else if (t == enc_fpath) + allowed = "$-_.+!*'(),?:@&="; + else /* if (t == enc_parm) */ + allowed = "$-_.+!*'(),?/:@&="; + + if (t == enc_path) + reserved = "/"; + else if (t == enc_search) + reserved = "+"; + else + reserved = ""; + + y = apr_palloc(p, 3 * len + 1); + + for (i = 0, j = 0; i < len; i++, j++) { +/* always handle '/' first */ + ch = x[i]; + if (strchr(reserved, ch)) { + y[j] = ch; + continue; + } +/* decode it if not already done */ + if (isenc && ch == '%') { + if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) + return NULL; + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ + ap_proxy_c2hex(ch, &y[j]); + j += 2; + continue; + } + } +/* recode it, if necessary */ + if (!apr_isalnum(ch) && !strchr(allowed, ch)) { + ap_proxy_c2hex(ch, &y[j]); + j += 2; + } + else + y[j] = ch; + } + y[j] = '\0'; + return y; +} + +/* + * Parses network-location. + * urlp on input the URL; on output the path, after the leading / + * user NULL if no user/password permitted + * password holder for password + * host holder for host + * port port number; only set if one is supplied. + * + * Returns an error string. + */ +PROXY_DECLARE(char *) + ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, + char **passwordp, char **hostp, apr_port_t *port) +{ + char *addr, *scope_id, *strp, *host, *url = *urlp; + char *user = NULL, *password = NULL; + apr_port_t tmp_port; + apr_status_t rv; + + if (url[0] != '/' || url[1] != '/') + return "Malformed URL"; + host = url + 2; + url = strchr(host, '/'); + if (url == NULL) + url = ""; + else + *(url++) = '\0'; /* skip seperating '/' */ + + /* find _last_ '@' since it might occur in user/password part */ + strp = strrchr(host, '@'); + + if (strp != NULL) { + *strp = '\0'; + user = host; + host = strp + 1; + +/* find password */ + strp = strchr(user, ':'); + if (strp != NULL) { + *strp = '\0'; + password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1); + if (password == NULL) + return "Bad %-escape in URL (password)"; + } + + user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1); + if (user == NULL) + return "Bad %-escape in URL (username)"; + } + if (userp != NULL) { + *userp = user; + } + if (passwordp != NULL) { + *passwordp = password; + } + + /* Parse the host string to separate host portion from optional port. + * Perform range checking on port. + */ + rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p); + if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) { + return "Invalid host/port"; + } + if (tmp_port != 0) { /* only update caller's port if port was specified */ + *port = tmp_port; + } + + ap_str_tolower(addr); /* DNS names are case-insensitive */ + + *urlp = url; + *hostp = addr; + + return NULL; +} + +static const char * const lwday[7] = +{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + +/* + * If the date is a valid RFC 850 date or asctime() date, then it + * is converted to the RFC 1123 format, otherwise it is not modified. + * This routine is not very fast at doing conversions, as it uses + * sscanf and sprintf. However, if the date is already correctly + * formatted, then it exits very quickly. + */ +PROXY_DECLARE(const char *) + ap_proxy_date_canon(apr_pool_t *p, const char *x1) +{ + char *x = apr_pstrdup(p, x1); + int wk, mday, year, hour, min, sec, mon; + char *q, month[4], zone[4], week[4]; + + q = strchr(x, ','); + /* check for RFC 850 date */ + if (q != NULL && q - x > 3 && q[1] == ' ') { + *q = '\0'; + for (wk = 0; wk < 7; wk++) + if (strcmp(x, lwday[wk]) == 0) + break; + *q = ','; + if (wk == 7) + return x; /* not a valid date */ + if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || + q[17] != ':' || strcmp(&q[20], " GMT") != 0) + return x; + if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, + &hour, &min, &sec, zone) != 7) + return x; + if (year < 70) + year += 2000; + else + year += 1900; + } + else { +/* check for acstime() date */ + if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || + x[16] != ':' || x[19] != ' ' || x[24] != '\0') + return x; + if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, + &min, &sec, &year) != 7) + return x; + for (wk = 0; wk < 7; wk++) + if (strcmp(week, apr_day_snames[wk]) == 0) + break; + if (wk == 7) + return x; + } + +/* check date */ + for (mon = 0; mon < 12; mon++) + if (strcmp(month, apr_month_snames[mon]) == 0) + break; + if (mon == 12) + return x; + + q = apr_palloc(p, 30); + apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk], + mday, apr_month_snames[mon], year, hour, min, sec); + return q; +} + +PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r) +{ + request_rec *rp = apr_pcalloc(c->pool, sizeof(*r)); + + rp->pool = c->pool; + rp->status = HTTP_OK; + + rp->headers_in = apr_table_make(c->pool, 50); + rp->subprocess_env = apr_table_make(c->pool, 50); + rp->headers_out = apr_table_make(c->pool, 12); + rp->err_headers_out = apr_table_make(c->pool, 5); + rp->notes = apr_table_make(c->pool, 5); + + rp->server = r->server; + rp->proxyreq = r->proxyreq; + rp->request_time = r->request_time; + rp->connection = c; + rp->output_filters = c->output_filters; + rp->input_filters = c->input_filters; + rp->proto_output_filters = c->output_filters; + rp->proto_input_filters = c->input_filters; + + rp->request_config = ap_create_request_config(c->pool); + proxy_run_create_req(r, rp); + + return rp; +} + + +/* + * list is a comma-separated list of case-insensitive tokens, with + * optional whitespace around the tokens. + * The return returns 1 if the token val is found in the list, or 0 + * otherwise. + */ +PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val) +{ + int len, i; + const char *p; + + len = strlen(val); + + while (list != NULL) { + p = ap_strchr_c(list, ','); + if (p != NULL) { + i = p - list; + do + p++; + while (apr_isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && apr_isspace(list[i - 1])) + i--; + if (i == len && strncasecmp(list, val, len) == 0) + return 1; + list = p; + } + return 0; +} + +/* + * list is a comma-separated list of case-insensitive tokens, with + * optional whitespace around the tokens. + * if val appears on the list of tokens, it is removed from the list, + * and the new list is returned. + */ +PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val) +{ + int len, i; + const char *p; + char *new = NULL; + + len = strlen(val); + + while (list != NULL) { + p = ap_strchr_c(list, ','); + if (p != NULL) { + i = p - list; + do + p++; + while (apr_isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && apr_isspace(list[i - 1])) + i--; + if (i == len && strncasecmp(list, val, len) == 0) { + /* do nothing */ + } + else { + if (new) + new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL); + else + new = apr_pstrndup(pool, list, i); + } + list = p; + } + return new; +} + +/* + * Converts 8 hex digits to a time integer + */ +PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i = 0, j = 0; i < 8; i++) { + ch = x[i]; + j <<= 4; + if (apr_isdigit(ch)) + j |= ch - '0'; + else if (apr_isupper(ch)) + j |= ch - ('A' - 10); + else + j |= ch - ('a' - 10); + } + if (j == 0xffffffff) + return -1; /* so that it works with 8-byte ints */ + else + return j; +} + +/* + * Converts a time integer to 8 hex digits + */ +PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y) +{ + int i, ch; + unsigned int j = t; + + for (i = 7; i >= 0; i--) { + ch = j & 0xF; + j >>= 4; + if (ch >= 10) + y[i] = ch + ('A' - 10); + else + y[i] = ch + '0'; + } + y[8] = '\0'; +} + +PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message) +{ + apr_table_setn(r->notes, "error-notes", + apr_pstrcat(r->pool, + "The proxy server could not handle the request " + "pool, r->uri), + "\">", ap_escape_html(r->pool, r->method), + " ", + ap_escape_html(r->pool, r->uri), ".

\n" + "Reason: ", + ap_escape_html(r->pool, message), + "

", 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\n", + inet_ntoa(This->addr), bits); + } + + This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits)); + + if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "Warning: NetMask and IP-Addr disagree in %s/%ld\n", + inet_ntoa(This->addr), bits); + This->addr.s_addr &= This->mask.s_addr; + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + " Set to %s/%ld\n", + inet_ntoa(This->addr), bits); + } + + if (*addr == '\0') { + This->matcher = proxy_match_ipaddr; + return 1; + } + else + return (*addr == '\0'); /* okay iff we've parsed the whole string */ +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) +{ + int i, ip_addr[4]; + struct in_addr addr, *ip; + const char *host = proxy_get_host_of_request(r); + + if (host == NULL) /* oops! */ + return 0; + + memset(&addr, '\0', sizeof addr); + memset(ip_addr, '\0', sizeof ip_addr); + + if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { + for (addr.s_addr = 0, i = 0; i < 4; ++i) + addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + + if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 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(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); + + 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)); + + (*balancer)->name = uri; + (*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; + 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'; + + worker = (proxy_worker *)conf->workers->elts; + for (i = 0; i < conf->workers->nelts; i++) { + if (strcasecmp(worker->name, uri) == 0) { + return worker; + } + worker++; + } + return NULL; +} + +#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) +{ + char *c, *q, *uri = apr_pstrdup(p, url); + int port; + + c = strchr(uri, ':'); + if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0') + return "Bad syntax for a remote proxy server"; + /* remove path from uri */ + if ((q = strchr(c + 3, '/'))) + *q = '\0'; + + q = strchr(c + 3, ':'); + if (q != NULL) { + if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) { + return "Bad syntax for a remote proxy server (bad port number)"; + } + } + else + port = -1; + ap_str_tolower(uri); + *worker = apr_array_push(conf->workers); + memset(*worker, 0, sizeof(proxy_worker)); + (*worker)->name = apr_pstrdup(p, uri); + *c = '\0'; + (*worker)->scheme = uri; + (*worker)->hostname = c + 3; + + if (port == -1) + port = apr_uri_port_of_scheme((*worker)->scheme); + (*worker)->port = port; + (*worker)->id = proxy_lb_workers; + /* Increase the total worker count */ + proxy_lb_workers++; + init_conn_pool(p, *worker); + + 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) { + *balancer = NULL; + access_status = OK; + } + else if (r->proxyreq == PROXYREQ_PROXY) { + if (conf->forward) { + *balancer = NULL; + *worker = conf->forward; + access_status = OK; + } + } + } + else if (access_status == DECLINED && balancer != NULL) { + /* All the workers are busy */ + 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; +} + +/* 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; +} + +PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf, + proxy_worker *worker) +{ +#if PROXY_HAS_SCOREBOARD + lb_score *score = NULL; +#else + void *score = NULL; +#endif +#if PROXY_HAS_SCOREBOARD + /* Get scoreboard slot */ + if (ap_scoreboard_image) { + score = ap_get_scoreboard_lb(worker->id); + if (!score) + ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conf->pool, + "proxy: ap_get_scoreboard_lb(%d) failed for worker %s", + worker->id, worker->name); + } +#endif + if (!score) + score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat)); + worker->s = (proxy_worker_stat *)score; + 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'; +} + +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; + + 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 for (%s) min=%d max=%d smax=%d", + worker->id, 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 for (%s)", + worker->id, worker->hostname); + } + if (rv == APR_SUCCESS) + worker->s->status |= PROXY_WORKER_INITIALIZED; + /* Set default parameters */ + if (!worker->retry) + worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY); + 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) { + 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); + } + if (!conn->hostname) { + 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; + } + } + /* TODO: add address cache for forward proxies */ + conn->addr = worker->cp->addr; + if (r->proxyreq == PROXYREQ_PROXY) { + err = apr_sockaddr_info_get(&(conn->addr), + conn->hostname, APR_UNSPEC, + conn->port, 0, + conn->pool); + } + else if (!worker->cp->addr) { + /* 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; + } + 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"); + } + 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, ¤t_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; + + /* 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 */ + ap_run_pre_connection(conn->connection, conn->sock); + + 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; +} -- 2.50.1