-/* Copyright 1999-2004 The Apache Software Foundation\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-#ifndef MOD_PROXY_H\r
-#define MOD_PROXY_H \r
-\r
-/*\r
- * Main include file for the Apache proxy\r
- */\r
-\r
-/*\r
-\r
- Also note numerous FIXMEs and CHECKMEs which should be eliminated.\r
-\r
- This code is once again experimental!\r
-\r
- Things to do:\r
-\r
- 1. Make it completely work (for FTP too)\r
-\r
- 2. HTTP/1.1\r
-\r
- Chuck Murcko <chuck@topsail.org> 02-06-01\r
-\r
- */\r
-\r
-#define CORE_PRIVATE\r
-\r
-#include "apr_hooks.h"\r
-#include "apr.h"\r
-#include "apr_lib.h"\r
-#include "apr_strings.h"\r
-#include "apr_buckets.h"\r
-#include "apr_md5.h"\r
-#include "apr_network_io.h"\r
-#include "apr_pools.h"\r
-#include "apr_strings.h"\r
-#include "apr_uri.h"\r
-#include "apr_date.h"\r
-#include "apr_strmatch.h"\r
-#include "apr_fnmatch.h"\r
-#include "apr_reslist.h"\r
-#define APR_WANT_STRFUNC\r
-#include "apr_want.h"\r
-\r
-#include "httpd.h"\r
-#include "http_config.h"\r
-#include "ap_config.h"\r
-#include "http_core.h"\r
-#include "http_protocol.h"\r
-#include "http_request.h"\r
-#include "http_vhost.h"\r
-#include "http_main.h"\r
-#include "http_log.h"\r
-#include "http_connection.h"\r
-#include "util_filter.h"\r
-#include "util_ebcdic.h"\r
-\r
-#if APR_HAVE_NETINET_IN_H\r
-#include <netinet/in.h>\r
-#endif\r
-#if APR_HAVE_ARPA_INET_H\r
-#include <arpa/inet.h>\r
-#endif\r
-\r
-/* for proxy_canonenc() */\r
-enum enctype {\r
- enc_path, enc_search, enc_user, enc_fpath, enc_parm\r
-};\r
-\r
-#if APR_CHARSET_EBCDIC\r
-#define CRLF "\r\n"\r
-#else /*APR_CHARSET_EBCDIC*/\r
-#define CRLF "\015\012"\r
-#endif /*APR_CHARSET_EBCDIC*/\r
-\r
-/* default Max-Forwards header setting */\r
-#define DEFAULT_MAX_FORWARDS 10\r
-\r
-/* static information about a remote proxy */\r
-struct proxy_remote {\r
- const char *scheme; /* the schemes handled by this proxy, or '*' */\r
- const char *protocol; /* the scheme used to talk to this proxy */\r
- const char *hostname; /* the hostname of this proxy */\r
- apr_port_t port; /* the port for this proxy */\r
- regex_t *regexp; /* compiled regex (if any) for the remote */\r
- int use_regex; /* simple boolean. True if we have a regex pattern */\r
-};\r
-\r
-struct proxy_alias {\r
- const char *real;\r
- const char *fake;\r
-};\r
-\r
-struct dirconn_entry {\r
- char *name;\r
- struct in_addr addr, mask;\r
- struct apr_sockaddr_t *hostaddr;\r
- int (*matcher) (struct dirconn_entry * This, request_rec *r);\r
-};\r
-\r
-struct noproxy_entry {\r
- const char *name;\r
- struct apr_sockaddr_t *addr;\r
-};\r
-\r
-typedef struct proxy_balancer proxy_balancer;\r
-typedef struct proxy_worker proxy_worker;\r
-typedef struct proxy_conn_pool proxy_conn_pool;\r
-\r
-typedef struct {\r
- apr_array_header_t *proxies;\r
- apr_array_header_t *sec_proxy;\r
- apr_array_header_t *aliases;\r
- apr_array_header_t *raliases;\r
- apr_array_header_t *noproxies;\r
- apr_array_header_t *dirconn;\r
- apr_array_header_t *allowed_connect_ports;\r
- apr_array_header_t *workers;\r
- apr_array_header_t *balancers;\r
- proxy_worker *forward; /* forward proxy worker */\r
- const char *domain; /* domain name to use in absence of a domain name in the request */\r
- int req; /* true if proxy requests are enabled */\r
- char req_set;\r
- enum {\r
- via_off,\r
- via_on,\r
- via_block,\r
- via_full\r
- } viaopt; /* how to deal with proxy Via: headers */\r
- char viaopt_set;\r
- apr_size_t recv_buffer_size;\r
- char recv_buffer_size_set;\r
- apr_size_t io_buffer_size;\r
- char io_buffer_size_set;\r
- long maxfwd;\r
- char maxfwd_set;\r
- /** \r
- * the following setting masks the error page\r
- * returned from the 'proxied server' and just \r
- * forwards the status code upwards.\r
- * This allows the main server (us) to generate\r
- * the error page, (so it will look like a error\r
- * returned from the rest of the system \r
- */\r
- int error_override;\r
- int error_override_set;\r
- int preserve_host;\r
- int preserve_host_set;\r
- apr_interval_time_t timeout;\r
- char timeout_set;\r
- enum {\r
- bad_error,\r
- bad_ignore,\r
- bad_body\r
- } badopt; /* how to deal with bad headers */\r
- char badopt_set;\r
-/* putting new stuff on the end maximises binary back-compatibility.\r
- * the strmatch_patterns are really a const just to have a\r
- * case-independent strstr.\r
- */\r
- apr_array_header_t* cookie_paths;\r
- apr_array_header_t* cookie_domains;\r
- const apr_strmatch_pattern* cookie_path_str;\r
- const apr_strmatch_pattern* cookie_domain_str;\r
- enum {\r
- status_off,\r
- status_on,\r
- status_full\r
- } proxy_status; /* Status display options */\r
- char proxy_status_set;\r
- apr_pool_t *pool; /* Pool used for allocating this struct */\r
-} proxy_server_conf;\r
-\r
-\r
-typedef struct {\r
- const char *p; /* The path */\r
- int p_is_fnmatch; /* Is this path an fnmatch candidate? */\r
- regex_t *r; /* Is this a regex? */\r
-} proxy_dir_conf;\r
-\r
-typedef struct {\r
- conn_rec *connection;\r
- const char *hostname;\r
- apr_port_t port;\r
- int is_ssl;\r
- apr_pool_t *pool; /* Subpool used for creating socket */\r
- apr_socket_t *sock; /* Connection socket */\r
- apr_sockaddr_t *addr; /* Preparsed remote address info */\r
- apr_uint32_t flags; /* Conection flags */\r
- int close; /* Close 'this' connection */\r
- int close_on_recycle; /* Close the connection when returning to pool */\r
- proxy_worker *worker; /* Connection pool this connection belogns to */\r
- void *data; /* per scheme connection data */\r
-} proxy_conn_rec;\r
-\r
-typedef struct {\r
- float cache_completion; /* completion percentage */\r
- int content_length; /* length of the content */\r
-} proxy_completion;\r
-\r
-/* Connection pool */\r
-struct proxy_conn_pool {\r
- apr_pool_t *pool; /* The pool used in constructor and destructor calls */\r
- apr_sockaddr_t *addr; /* Preparsed remote address info */\r
-#if APR_HAS_THREADS\r
- apr_reslist_t *res; /* Connection resource list */\r
-#endif\r
- proxy_conn_rec *conn; /* Single connection for prefork mpm's */\r
-};\r
-\r
-/* woker status flags */\r
-#define PROXY_WORKER_INITIALIZED 0x0001\r
-#define PROXY_WORKER_IGNORE_ERRORS 0x0002\r
-#define PROXY_WORKER_IN_SHUTDOWN 0x0010\r
-#define PROXY_WORKER_DISABLED 0x0020\r
-#define PROXY_WORKER_IN_ERROR 0x0040\r
-\r
-#define PROXY_WORKER_IS_USABLE(f) (!((f)->s->status & 0x00F0))\r
-\r
-/* default worker retry timeout in seconds */\r
-#define PROXY_WORKER_DEFAULT_RETRY 60\r
-#define PROXY_WORKER_MAX_ROUTE_SIZ 63\r
-\r
-/* Runtime worker status informations. Shared in scoreboard */\r
-typedef struct {\r
- int status;\r
- apr_time_t error_time; /* time of the last error */\r
- int retries; /* number of retries on this worker */\r
- int lbstatus; /* Current lbstatus */\r
- int lbfactor; /* dynamic lbfactor */\r
- apr_off_t transfered; /* Number of bytes transfered to remote */\r
- apr_off_t readed; /* Number of bytes readed from remote */\r
- apr_size_t elected; /* Number of times the worker was elected */\r
- char route[PROXY_WORKER_MAX_ROUTE_SIZ+1];\r
- char redirect[PROXY_WORKER_MAX_ROUTE_SIZ+1];\r
-} proxy_worker_stat;\r
-\r
-/* Worker configuration */\r
-struct proxy_worker {\r
- int id; /* scoreboard id */\r
- apr_interval_time_t retry; /* retry interval */\r
- int lbfactor; /* initial load balancing factor */\r
- const char *name;\r
- const char *scheme; /* scheme to use ajp|http|https */\r
- const char *hostname; /* remote backend address */\r
- const char *route; /* balancing route */\r
- const char *redirect; /* temporary balancing redirection route */\r
- apr_port_t port;\r
- int min; /* Desired minimum number of available connections */\r
- int smax; /* Soft maximum on the total number of connections */\r
- int hmax; /* Hard maximum on the total number of connections */\r
- apr_interval_time_t ttl; /* maximum amount of time in seconds a connection\r
- * may be available while exceeding the soft limit */\r
- apr_interval_time_t timeout; /* connection timeout */\r
- char timeout_set;\r
- apr_interval_time_t acquire; /* acquire timeout when the maximum number of connections is exceeded */\r
- char acquire_set;\r
- apr_size_t recv_buffer_size;\r
- char recv_buffer_size_set;\r
- apr_size_t io_buffer_size;\r
- char io_buffer_size_set;\r
- char keepalive;\r
- char keepalive_set;\r
- proxy_conn_pool *cp; /* Connection pool to use */\r
- proxy_worker_stat *s; /* Shared data */\r
- void *opaque; /* per scheme worker data */\r
-};\r
-\r
-struct proxy_balancer {\r
- apr_array_header_t *workers; /* array of proxy_workers */\r
- const char *name; /* name of the load balancer */\r
- const char *sticky; /* sticky session identifier */\r
- int sticky_force; /* Disable failover for sticky sessions */\r
- apr_interval_time_t timeout; /* Timeout for waiting on free connection */\r
- int max_attempts; /* Number of attempts before failing */\r
- char max_attempts_set;\r
-\r
- /* XXX: Perhaps we will need the proc mutex too.\r
- * Altrough we are only using arithmetic operations\r
- * it may lead to a incorrect calculations.\r
- * For now use only the thread mutex.\r
- */\r
-#if APR_HAS_THREADS\r
- apr_thread_mutex_t *mutex; /* Thread lock for updating lb params */\r
-#endif\r
-};\r
-\r
-/* hooks */\r
-\r
-/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and \r
- * PROXY_DECLARE_DATA with appropriate export and import tags for the platform\r
- */\r
-#if !defined(WIN32)\r
-#define PROXY_DECLARE(type) type\r
-#define PROXY_DECLARE_NONSTD(type) type\r
-#define PROXY_DECLARE_DATA\r
-#elif defined(PROXY_DECLARE_STATIC)\r
-#define PROXY_DECLARE(type) type __stdcall\r
-#define PROXY_DECLARE_NONSTD(type) type\r
-#define PROXY_DECLARE_DATA\r
-#elif defined(PROXY_DECLARE_EXPORT)\r
-#define PROXY_DECLARE(type) __declspec(dllexport) type __stdcall\r
-#define PROXY_DECLARE_NONSTD(type) __declspec(dllexport) type\r
-#define PROXY_DECLARE_DATA __declspec(dllexport)\r
-#else\r
-#define PROXY_DECLARE(type) __declspec(dllimport) type __stdcall\r
-#define PROXY_DECLARE_NONSTD(type) __declspec(dllimport) type\r
-#define PROXY_DECLARE_DATA __declspec(dllimport)\r
-#endif\r
-\r
-/**\r
- * Hook an optional proxy hook. Unlike static hooks, this uses a macro\r
- * instead of a function.\r
- */\r
-#define PROXY_OPTIONAL_HOOK(name,fn,pre,succ,order) \\r
- APR_OPTIONAL_HOOK(proxy,name,fn,pre,succ,order)\r
-\r
-APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, scheme_handler, (request_rec *r, \r
- proxy_worker *worker, proxy_server_conf *conf, char *url, \r
- const char *proxyhost, apr_port_t proxyport))\r
-APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, canon_handler, (request_rec *r, \r
- char *url))\r
-\r
-APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, create_req, (request_rec *r, request_rec *pr))\r
-APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, fixups, (request_rec *r)) \r
-\r
-/**\r
- * pre request hook.\r
- * It will return the most suitable worker at the moment\r
- * and coresponding balancer.\r
- * The url is rewritten from balancer://cluster/uri to scheme://host:port/uri\r
- * and then the scheme_handler is called.\r
- *\r
- */\r
-APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, pre_request, (proxy_worker **worker,\r
- proxy_balancer **balancer,\r
- request_rec *r,\r
- proxy_server_conf *conf, char **url)) \r
-/**\r
- * post request hook.\r
- * It is called after request for updating runtime balancer status.\r
- */\r
-APR_DECLARE_EXTERNAL_HOOK(proxy, PROXY, int, post_request, (proxy_worker *worker,\r
- proxy_balancer *balancer, request_rec *r,\r
- proxy_server_conf *conf))\r
-\r
-\r
-/* proxy_util.c */\r
-\r
-PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r);\r
-PROXY_DECLARE(int) ap_proxy_hex2c(const char *x);\r
-PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x);\r
-PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,\r
- int isenc);\r
-PROXY_DECLARE(char *)ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,\r
- char **passwordp, char **hostp, apr_port_t *port);\r
-PROXY_DECLARE(const char *)ap_proxy_date_canon(apr_pool_t *p, const char *x);\r
-PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val);\r
-PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val);\r
-PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x);\r
-PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y);\r
-PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message);\r
-PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p);\r
-PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p);\r
-PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p);\r
-PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p);\r
-PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, apr_sockaddr_t *uri_addr);\r
-PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r);\r
-PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, char *buff, size_t bufflen, int *eos);\r
-PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key);\r
-/* DEPRECATED (will be replaced with ap_proxy_connect_backend */\r
-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 *);\r
-PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c);\r
-PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c);\r
-\r
-/* Connection pool API */\r
-/**\r
- * Get the worker from proxy configuration\r
- * @param p memory pool used for finding worker\r
- * @param conf current proxy server configuration\r
- * @param url url to find the worker from\r
- * @return proxy_worker or NULL if not found\r
- */\r
-PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url);\r
-/**\r
- * Add the worker to proxy configuration\r
- * @param worker the new worker\r
- * @param p memory pool to allocate worker from \r
- * @param conf current proxy server configuration\r
- * @param url url containing worker name\r
- * @return error message or NULL if successfull\r
- */\r
-PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,\r
- apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url);\r
-\r
-/**\r
- * Create new worker\r
- * @param p memory pool to allocate worker from \r
- * @return new worker\r
- */\r
-PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p);\r
-\r
-/**\r
- * Initize the worker's shared data\r
- * @param conf current proxy server configuration\r
- * @param s current server record\r
- * @param worker worker to initialize\r
- */\r
-PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,\r
- proxy_worker *worker);\r
-\r
-\r
-/**\r
- * Initize the worker\r
- * @param worker worker to initialize\r
- * @param p memory pool to allocate worker from \r
- * @param s current server record\r
- * @return APR_SUCCESS or error code\r
- */\r
-PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker,\r
- server_rec *s);\r
-/**\r
- * Get the balancer from proxy configuration\r
- * @param p memory pool used for finding balancer\r
- * @param conf current proxy server configuration\r
- * @param url url to find the worker from. Has to have balancer:// prefix\r
- * @return proxy_balancer or NULL if not found\r
- */\r
-PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url);\r
-/**\r
- * Add the balancer to proxy configuration\r
- * @param balancer the new balancer\r
- * @param p memory pool to allocate balancer from \r
- * @param conf current proxy server configuration\r
- * @param url url containing balancer name\r
- * @return error message or NULL if successfull\r
- */\r
-PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,\r
- apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url);\r
-\r
-/**\r
- * Add the worker to the balancer\r
- * @param pool memory pool for adding worker \r
- * @param balancer balancer to add to\r
- * @param balancer worker to add\r
- * @note Single worker can be added to multiple balancers.\r
- */\r
-PROXY_DECLARE(void) ap_proxy_add_worker_to_balancer(apr_pool_t *pool,\r
- proxy_balancer *balancer,\r
- proxy_worker *worker);\r
-/**\r
- * Get the most suitable worker and(or) balancer for the request\r
- * @param worker worker used for processing request\r
- * @param balancer balancer used for processing request\r
- * @param r current request\r
- * @param conf current proxy server configuration\r
- * @param url request url that balancer can rewrite.\r
- * @return OK or HTTP_XXX error \r
- * @note It calls balancer pre_request hook if the url starts with balancer://\r
- * The balancer then rewrites the url to particular worker, like http://host:port\r
- */\r
-PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,\r
- proxy_balancer **balancer,\r
- request_rec *r,\r
- proxy_server_conf *conf,\r
- char **url);\r
-/**\r
- * Post request worker and balancer cleanup\r
- * @param worker worker used for processing request\r
- * @param balancer balancer used for processing request\r
- * @param r current request\r
- * @param conf current proxy server configuration\r
- * @return OK or HTTP_XXX error\r
- * @note When ever the pre_request is called, the post_request has to be\r
- * called too. \r
- */\r
-PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,\r
- proxy_balancer *balancer,\r
- request_rec *r,\r
- proxy_server_conf *conf);\r
-/**\r
- * Deternime backend hostname and port\r
- * @param p memory pool used for processing\r
- * @param r current request\r
- * @param conf current proxy server configuration\r
- * @param worker worker used for processing request\r
- * @param conn proxy connection struct\r
- * @param uri processed uri\r
- * @param url request url\r
- * @param proxyname are we connecting directly or via s proxy\r
- * @param proxyport proxy host port\r
- * @param server_portstr Via headers server port\r
- * @param server_portstr_size size of the server_portstr buffer\r
- * @return OK or HTTP_XXX error\r
- */ \r
-PROXY_DECLARE(int) ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,\r
- proxy_server_conf *conf,\r
- proxy_worker *worker,\r
- proxy_conn_rec *conn,\r
- apr_uri_t *uri,\r
- char **url,\r
- const char *proxyname,\r
- apr_port_t proxyport,\r
- char *server_portstr,\r
- int server_portstr_size);\r
-/**\r
- * Mark a worker for retry\r
- * @param proxy_function calling proxy scheme (http, ajp, ...)\r
- * @param conf current proxy server configuration\r
- * @param worker worker used for retrying\r
- * @param s current server record\r
- * @return OK if marked for retry, DECLINED otherwise\r
- * @note Worker will be marker for retry if the time of the last retry\r
- * has been ellapsed. In case there is no retry option set, defaults to\r
- * number_of_retries seconds.\r
- */ \r
-PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,\r
- proxy_worker *worker,\r
- server_rec *s);\r
-/**\r
- * Acquire a connection from workers connection pool\r
- * @param proxy_function calling proxy scheme (http, ajp, ...)\r
- * @param conn acquired connection\r
- * @param worker worker used for obtaining connection\r
- * @param s current server record\r
- * @return OK or HTTP_XXX error\r
- * @note If the number of connections is exhaused the function will\r
- * block untill the timeout is reached.\r
- */ \r
-PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,\r
- proxy_conn_rec **conn,\r
- proxy_worker *worker,\r
- server_rec *s);\r
-/**\r
- * Release a connection back to worker connection pool\r
- * @param proxy_function calling proxy scheme (http, ajp, ...)\r
- * @param conn acquired connection\r
- * @param s current server record\r
- * @return OK or HTTP_XXX error\r
- * @note The connection will be closed if conn->close_on_release is set\r
- */ \r
-PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,\r
- proxy_conn_rec *conn,\r
- server_rec *s);\r
-/**\r
- * Make a connection to the backend\r
- * @param proxy_function calling proxy scheme (http, ajp, ...)\r
- * @param conn acquired connection\r
- * @param worker connection worker\r
- * @param s current server record\r
- * @return OK or HTTP_XXX error\r
- * @note In case the socket already exists for conn, just check the link\r
- * status.\r
- */ \r
-PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,\r
- proxy_conn_rec *conn,\r
- proxy_worker *worker,\r
- server_rec *s);\r
-/**\r
- * Make a connection record for backend connection\r
- * @param proxy_function calling proxy scheme (http, ajp, ...)\r
- * @param conn acquired connection\r
- * @param c client connection record\r
- * @param s current server record\r
- * @return OK or HTTP_XXX error\r
- */ \r
-PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,\r
- proxy_conn_rec *conn,\r
- conn_rec *c, server_rec *s);\r
-\r
-/* Scoreboard */\r
-#if MODULE_MAGIC_NUMBER_MAJOR > 20020903\r
-#define PROXY_HAS_SCOREBOARD 1\r
-#else\r
-#define PROXY_HAS_SCOREBOARD 0\r
-#endif\r
-\r
-/* The number of dynamic workers that can be added when reconfiguring.\r
- * If this limit is reached you must stop and restart the server.\r
- */\r
-#define PROXY_DYNAMIC_BALANCER_LIMIT 16\r
-/**\r
- * Calculate number of maximum number of workers in scoreboard.\r
- * @return number of workers to allocate in the scoreboard\r
- */\r
-int ap_proxy_lb_workers(void);\r
-\r
-/* For proxy_util */\r
-extern module PROXY_DECLARE_DATA proxy_module;\r
-\r
-extern int PROXY_DECLARE_DATA proxy_lb_workers;\r
-\r
-#endif /*MOD_PROXY_H*/\r
+/* 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 <chuck@topsail.org> 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 <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#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\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/* Utility routines for Apache proxy */\r
-#include "mod_proxy.h"\r
-#include "ap_mpm.h"\r
-#include "scoreboard.h"\r
-#include "apr_version.h"\r
-\r
-#if APR_HAVE_UNISTD_H\r
-#include <unistd.h> /* for getpid() */\r
-#endif\r
-\r
-#if (APR_MAJOR_VERSION < 1)\r
-#undef apr_socket_create\r
-#define apr_socket_create apr_socket_create_ex\r
-#endif\r
-\r
-/* Global balancer counter */\r
-int PROXY_DECLARE_DATA proxy_lb_workers = 0;\r
-static int lb_workers_limit = 0;\r
-\r
-static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);\r
-static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);\r
-static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);\r
-static int proxy_match_word(struct dirconn_entry *This, request_rec *r);\r
-\r
-APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, create_req, \r
- (request_rec *r, request_rec *pr), (r, pr),\r
- OK, DECLINED)\r
-\r
-/* already called in the knowledge that the characters are hex digits */\r
-PROXY_DECLARE(int) ap_proxy_hex2c(const char *x)\r
-{\r
- int i, ch;\r
-\r
-#if !APR_CHARSET_EBCDIC\r
- ch = x[0];\r
- if (apr_isdigit(ch))\r
- i = ch - '0';\r
- else if (apr_isupper(ch))\r
- i = ch - ('A' - 10);\r
- else\r
- i = ch - ('a' - 10);\r
- i <<= 4;\r
-\r
- ch = x[1];\r
- if (apr_isdigit(ch))\r
- i += ch - '0';\r
- else if (apr_isupper(ch))\r
- i += ch - ('A' - 10);\r
- else\r
- i += ch - ('a' - 10);\r
- return i;\r
-#else /*APR_CHARSET_EBCDIC*/\r
- /* we assume that the hex value refers to an ASCII character\r
- * so convert to EBCDIC so that it makes sense locally;\r
- *\r
- * example:\r
- *\r
- * client specifies %20 in URL to refer to a space char;\r
- * at this point we're called with EBCDIC "20"; after turning\r
- * EBCDIC "20" into binary 0x20, we then need to assume that 0x20\r
- * represents an ASCII char and convert 0x20 to EBCDIC, yielding\r
- * 0x40\r
- */\r
- char buf[1];\r
-\r
- if (1 == sscanf(x, "%2x", &i)) {\r
- buf[0] = i & 0xFF;\r
- ap_xlate_proto_from_ascii(buf, 1);\r
- return buf[0];\r
- }\r
- else {\r
- return 0;\r
- }\r
-#endif /*APR_CHARSET_EBCDIC*/\r
-}\r
-\r
-PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x)\r
-{\r
-#if !APR_CHARSET_EBCDIC\r
- int i;\r
-\r
- x[0] = '%';\r
- i = (ch & 0xF0) >> 4;\r
- if (i >= 10)\r
- x[1] = ('A' - 10) + i;\r
- else\r
- x[1] = '0' + i;\r
-\r
- i = ch & 0x0F;\r
- if (i >= 10)\r
- x[2] = ('A' - 10) + i;\r
- else\r
- x[2] = '0' + i;\r
-#else /*APR_CHARSET_EBCDIC*/\r
- static const char ntoa[] = { "0123456789ABCDEF" };\r
- char buf[1];\r
-\r
- ch &= 0xFF;\r
-\r
- buf[0] = ch;\r
- ap_xlate_proto_to_ascii(buf, 1);\r
-\r
- x[0] = '%';\r
- x[1] = ntoa[(buf[0] >> 4) & 0x0F];\r
- x[2] = ntoa[buf[0] & 0x0F];\r
- x[3] = '\0';\r
-#endif /*APR_CHARSET_EBCDIC*/\r
-}\r
-\r
-/*\r
- * canonicalise a URL-encoded string\r
- */\r
-\r
-/*\r
- * Convert a URL-encoded string to canonical form.\r
- * It decodes characters which need not be encoded,\r
- * and encodes those which must be encoded, and does not touch\r
- * those which must not be touched.\r
- */\r
-PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t,\r
- int isenc)\r
-{\r
- int i, j, ch;\r
- char *y;\r
- char *allowed; /* characters which should not be encoded */\r
- char *reserved; /* characters which much not be en/de-coded */\r
-\r
-/* N.B. in addition to :@&=, this allows ';' in an http path\r
- * and '?' in an ftp path -- this may be revised\r
- * \r
- * Also, it makes a '+' character in a search string reserved, as\r
- * it may be form-encoded. (Although RFC 1738 doesn't allow this -\r
- * it only permits ; / ? : @ = & as reserved chars.)\r
- */\r
- if (t == enc_path)\r
- allowed = "$-_.+!*'(),;:@&=";\r
- else if (t == enc_search)\r
- allowed = "$-_.!*'(),;:@&=";\r
- else if (t == enc_user)\r
- allowed = "$-_.+!*'(),;@&=";\r
- else if (t == enc_fpath)\r
- allowed = "$-_.+!*'(),?:@&=";\r
- else /* if (t == enc_parm) */\r
- allowed = "$-_.+!*'(),?/:@&=";\r
-\r
- if (t == enc_path)\r
- reserved = "/";\r
- else if (t == enc_search)\r
- reserved = "+";\r
- else\r
- reserved = "";\r
-\r
- y = apr_palloc(p, 3 * len + 1);\r
-\r
- for (i = 0, j = 0; i < len; i++, j++) {\r
-/* always handle '/' first */\r
- ch = x[i];\r
- if (strchr(reserved, ch)) {\r
- y[j] = ch;\r
- continue;\r
- }\r
-/* decode it if not already done */\r
- if (isenc && ch == '%') {\r
- if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2]))\r
- return NULL;\r
- ch = ap_proxy_hex2c(&x[i + 1]);\r
- i += 2;\r
- if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */\r
- ap_proxy_c2hex(ch, &y[j]);\r
- j += 2;\r
- continue;\r
- }\r
- }\r
-/* recode it, if necessary */\r
- if (!apr_isalnum(ch) && !strchr(allowed, ch)) {\r
- ap_proxy_c2hex(ch, &y[j]);\r
- j += 2;\r
- }\r
- else\r
- y[j] = ch;\r
- }\r
- y[j] = '\0';\r
- return y;\r
-}\r
-\r
-/*\r
- * Parses network-location.\r
- * urlp on input the URL; on output the path, after the leading /\r
- * user NULL if no user/password permitted\r
- * password holder for password\r
- * host holder for host\r
- * port port number; only set if one is supplied.\r
- *\r
- * Returns an error string.\r
- */\r
-PROXY_DECLARE(char *)\r
- ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp,\r
- char **passwordp, char **hostp, apr_port_t *port)\r
-{\r
- char *addr, *scope_id, *strp, *host, *url = *urlp;\r
- char *user = NULL, *password = NULL;\r
- apr_port_t tmp_port;\r
- apr_status_t rv;\r
-\r
- if (url[0] != '/' || url[1] != '/')\r
- return "Malformed URL";\r
- host = url + 2;\r
- url = strchr(host, '/');\r
- if (url == NULL)\r
- url = "";\r
- else\r
- *(url++) = '\0'; /* skip seperating '/' */\r
-\r
- /* find _last_ '@' since it might occur in user/password part */\r
- strp = strrchr(host, '@');\r
-\r
- if (strp != NULL) {\r
- *strp = '\0';\r
- user = host;\r
- host = strp + 1;\r
-\r
-/* find password */\r
- strp = strchr(user, ':');\r
- if (strp != NULL) {\r
- *strp = '\0';\r
- password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, 1);\r
- if (password == NULL)\r
- return "Bad %-escape in URL (password)";\r
- }\r
-\r
- user = ap_proxy_canonenc(p, user, strlen(user), enc_user, 1);\r
- if (user == NULL)\r
- return "Bad %-escape in URL (username)";\r
- }\r
- if (userp != NULL) {\r
- *userp = user;\r
- }\r
- if (passwordp != NULL) {\r
- *passwordp = password;\r
- }\r
-\r
- /* Parse the host string to separate host portion from optional port.\r
- * Perform range checking on port.\r
- */\r
- rv = apr_parse_addr_port(&addr, &scope_id, &tmp_port, host, p);\r
- if (rv != APR_SUCCESS || addr == NULL || scope_id != NULL) {\r
- return "Invalid host/port";\r
- }\r
- if (tmp_port != 0) { /* only update caller's port if port was specified */\r
- *port = tmp_port;\r
- }\r
-\r
- ap_str_tolower(addr); /* DNS names are case-insensitive */\r
-\r
- *urlp = url;\r
- *hostp = addr;\r
-\r
- return NULL;\r
-}\r
-\r
-static const char * const lwday[7] =\r
-{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};\r
-\r
-/*\r
- * If the date is a valid RFC 850 date or asctime() date, then it\r
- * is converted to the RFC 1123 format, otherwise it is not modified.\r
- * This routine is not very fast at doing conversions, as it uses\r
- * sscanf and sprintf. However, if the date is already correctly\r
- * formatted, then it exits very quickly.\r
- */\r
-PROXY_DECLARE(const char *)\r
- ap_proxy_date_canon(apr_pool_t *p, const char *x1)\r
-{\r
- char *x = apr_pstrdup(p, x1);\r
- int wk, mday, year, hour, min, sec, mon;\r
- char *q, month[4], zone[4], week[4];\r
-\r
- q = strchr(x, ',');\r
- /* check for RFC 850 date */\r
- if (q != NULL && q - x > 3 && q[1] == ' ') {\r
- *q = '\0';\r
- for (wk = 0; wk < 7; wk++)\r
- if (strcmp(x, lwday[wk]) == 0)\r
- break;\r
- *q = ',';\r
- if (wk == 7)\r
- return x; /* not a valid date */\r
- if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||\r
- q[17] != ':' || strcmp(&q[20], " GMT") != 0)\r
- return x;\r
- if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,\r
- &hour, &min, &sec, zone) != 7)\r
- return x;\r
- if (year < 70)\r
- year += 2000;\r
- else\r
- year += 1900;\r
- }\r
- else {\r
-/* check for acstime() date */\r
- if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||\r
- x[16] != ':' || x[19] != ' ' || x[24] != '\0')\r
- return x;\r
- if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,\r
- &min, &sec, &year) != 7)\r
- return x;\r
- for (wk = 0; wk < 7; wk++)\r
- if (strcmp(week, apr_day_snames[wk]) == 0)\r
- break;\r
- if (wk == 7)\r
- return x;\r
- }\r
-\r
-/* check date */\r
- for (mon = 0; mon < 12; mon++)\r
- if (strcmp(month, apr_month_snames[mon]) == 0)\r
- break;\r
- if (mon == 12)\r
- return x;\r
-\r
- q = apr_palloc(p, 30);\r
- apr_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", apr_day_snames[wk],\r
- mday, apr_month_snames[mon], year, hour, min, sec);\r
- return q;\r
-}\r
-\r
-PROXY_DECLARE(request_rec *)ap_proxy_make_fake_req(conn_rec *c, request_rec *r)\r
-{\r
- request_rec *rp = apr_pcalloc(c->pool, sizeof(*r));\r
-\r
- rp->pool = c->pool;\r
- rp->status = HTTP_OK;\r
-\r
- rp->headers_in = apr_table_make(c->pool, 50);\r
- rp->subprocess_env = apr_table_make(c->pool, 50);\r
- rp->headers_out = apr_table_make(c->pool, 12);\r
- rp->err_headers_out = apr_table_make(c->pool, 5);\r
- rp->notes = apr_table_make(c->pool, 5);\r
-\r
- rp->server = r->server;\r
- rp->proxyreq = r->proxyreq;\r
- rp->request_time = r->request_time;\r
- rp->connection = c;\r
- rp->output_filters = c->output_filters;\r
- rp->input_filters = c->input_filters;\r
- rp->proto_output_filters = c->output_filters;\r
- rp->proto_input_filters = c->input_filters;\r
-\r
- rp->request_config = ap_create_request_config(c->pool);\r
- proxy_run_create_req(r, rp);\r
-\r
- return rp;\r
-}\r
-\r
-\r
-/*\r
- * list is a comma-separated list of case-insensitive tokens, with\r
- * optional whitespace around the tokens.\r
- * The return returns 1 if the token val is found in the list, or 0\r
- * otherwise.\r
- */\r
-PROXY_DECLARE(int) ap_proxy_liststr(const char *list, const char *val)\r
-{\r
- int len, i;\r
- const char *p;\r
-\r
- len = strlen(val);\r
-\r
- while (list != NULL) {\r
- p = ap_strchr_c(list, ',');\r
- if (p != NULL) {\r
- i = p - list;\r
- do\r
- p++;\r
- while (apr_isspace(*p));\r
- }\r
- else\r
- i = strlen(list);\r
-\r
- while (i > 0 && apr_isspace(list[i - 1]))\r
- i--;\r
- if (i == len && strncasecmp(list, val, len) == 0)\r
- return 1;\r
- list = p;\r
- }\r
- return 0;\r
-}\r
-\r
-/*\r
- * list is a comma-separated list of case-insensitive tokens, with\r
- * optional whitespace around the tokens.\r
- * if val appears on the list of tokens, it is removed from the list,\r
- * and the new list is returned.\r
- */\r
-PROXY_DECLARE(char *)ap_proxy_removestr(apr_pool_t *pool, const char *list, const char *val)\r
-{\r
- int len, i;\r
- const char *p;\r
- char *new = NULL;\r
-\r
- len = strlen(val);\r
-\r
- while (list != NULL) {\r
- p = ap_strchr_c(list, ',');\r
- if (p != NULL) {\r
- i = p - list;\r
- do\r
- p++;\r
- while (apr_isspace(*p));\r
- }\r
- else\r
- i = strlen(list);\r
-\r
- while (i > 0 && apr_isspace(list[i - 1]))\r
- i--;\r
- if (i == len && strncasecmp(list, val, len) == 0) {\r
- /* do nothing */\r
- }\r
- else {\r
- if (new)\r
- new = apr_pstrcat(pool, new, ",", apr_pstrndup(pool, list, i), NULL);\r
- else\r
- new = apr_pstrndup(pool, list, i);\r
- }\r
- list = p;\r
- }\r
- return new;\r
-}\r
-\r
-/*\r
- * Converts 8 hex digits to a time integer\r
- */\r
-PROXY_DECLARE(int) ap_proxy_hex2sec(const char *x)\r
-{\r
- int i, ch;\r
- unsigned int j;\r
-\r
- for (i = 0, j = 0; i < 8; i++) {\r
- ch = x[i];\r
- j <<= 4;\r
- if (apr_isdigit(ch))\r
- j |= ch - '0';\r
- else if (apr_isupper(ch))\r
- j |= ch - ('A' - 10);\r
- else\r
- j |= ch - ('a' - 10);\r
- }\r
- if (j == 0xffffffff)\r
- return -1; /* so that it works with 8-byte ints */\r
- else\r
- return j;\r
-}\r
-\r
-/*\r
- * Converts a time integer to 8 hex digits\r
- */\r
-PROXY_DECLARE(void) ap_proxy_sec2hex(int t, char *y)\r
-{\r
- int i, ch;\r
- unsigned int j = t;\r
-\r
- for (i = 7; i >= 0; i--) {\r
- ch = j & 0xF;\r
- j >>= 4;\r
- if (ch >= 10)\r
- y[i] = ch + ('A' - 10);\r
- else\r
- y[i] = ch + '0';\r
- }\r
- y[8] = '\0';\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxyerror(request_rec *r, int statuscode, const char *message)\r
-{\r
- apr_table_setn(r->notes, "error-notes",\r
- apr_pstrcat(r->pool, \r
- "The proxy server could not handle the request "\r
- "<em><a href=\"", ap_escape_uri(r->pool, r->uri),\r
- "\">", ap_escape_html(r->pool, r->method),\r
- " ", \r
- ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"\r
- "Reason: <strong>",\r
- ap_escape_html(r->pool, message), \r
- "</strong></p>", NULL));\r
-\r
- /* Allow "error-notes" string to be printed by ap_send_error_response() */\r
- apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));\r
-\r
- r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);\r
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,\r
- "proxy: %s returned by %s", message, r->uri);\r
- return statuscode;\r
-}\r
-\r
-static const char *\r
- proxy_get_host_of_request(request_rec *r)\r
-{\r
- char *url, *user = NULL, *password = NULL, *err, *host;\r
- apr_port_t port;\r
-\r
- if (r->hostname != NULL)\r
- return r->hostname;\r
-\r
- /* Set url to the first char after "scheme://" */\r
- if ((url = strchr(r->uri, ':')) == NULL\r
- || url[1] != '/' || url[2] != '/')\r
- return NULL;\r
-\r
- url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */\r
-\r
- err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);\r
-\r
- if (err != NULL)\r
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,\r
- "%s", err);\r
-\r
- r->hostname = host;\r
-\r
- return host; /* ought to return the port, too */\r
-}\r
-\r
-/* Return TRUE if addr represents an IP address (or an IP network address) */\r
-PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)\r
-{\r
- const char *addr = This->name;\r
- long ip_addr[4];\r
- int i, quads;\r
- long bits;\r
-\r
- /* if the address is given with an explicit netmask, use that */\r
- /* Due to a deficiency in apr_inet_addr(), it is impossible to parse */\r
- /* "partial" addresses (with less than 4 quads) correctly, i.e. */\r
- /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */\r
- /* I therefore have to parse the IP address manually: */\r
- /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */\r
- /* addr and mask were set by proxy_readmask() */\r
- /*return 1; */\r
-\r
- /* Parse IP addr manually, optionally allowing */\r
- /* abbreviated net addresses like 192.168. */\r
-\r
- /* Iterate over up to 4 (dotted) quads. */\r
- for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {\r
- char *tmp;\r
-\r
- if (*addr == '/' && quads > 0) /* netmask starts here. */\r
- break;\r
-\r
- if (!apr_isdigit(*addr))\r
- return 0; /* no digit at start of quad */\r
-\r
- ip_addr[quads] = strtol(addr, &tmp, 0);\r
-\r
- if (tmp == addr) /* expected a digit, found something else */\r
- return 0;\r
-\r
- if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {\r
- /* invalid octet */\r
- return 0;\r
- }\r
-\r
- addr = tmp;\r
-\r
- if (*addr == '.' && quads != 3)\r
- ++addr; /* after the 4th quad, a dot would be illegal */\r
- }\r
-\r
- for (This->addr.s_addr = 0, i = 0; i < quads; ++i)\r
- This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));\r
-\r
- if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */\r
- char *tmp;\r
-\r
- ++addr;\r
-\r
- bits = strtol(addr, &tmp, 0);\r
-\r
- if (tmp == addr) /* expected a digit, found something else */\r
- return 0;\r
-\r
- addr = tmp;\r
-\r
- if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */\r
- return 0;\r
-\r
- }\r
- else {\r
- /* Determine (i.e., "guess") netmask by counting the */\r
- /* number of trailing .0's; reduce #quads appropriately */\r
- /* (so that 192.168.0.0 is equivalent to 192.168.) */\r
- while (quads > 0 && ip_addr[quads - 1] == 0)\r
- --quads;\r
-\r
- /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */\r
- if (quads < 1)\r
- return 0;\r
-\r
- /* every zero-byte counts as 8 zero-bits */\r
- bits = 8 * quads;\r
-\r
- if (bits != 32) /* no warning for fully qualified IP address */\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",\r
- inet_ntoa(This->addr), bits);\r
- }\r
-\r
- This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits));\r
-\r
- if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "Warning: NetMask and IP-Addr disagree in %s/%ld\n",\r
- inet_ntoa(This->addr), bits);\r
- This->addr.s_addr &= This->mask.s_addr;\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- " Set to %s/%ld\n",\r
- inet_ntoa(This->addr), bits);\r
- }\r
-\r
- if (*addr == '\0') {\r
- This->matcher = proxy_match_ipaddr;\r
- return 1;\r
- }\r
- else\r
- return (*addr == '\0'); /* okay iff we've parsed the whole string */\r
-}\r
-\r
-/* Return TRUE if addr represents an IP address (or an IP network address) */\r
-static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)\r
-{\r
- int i, ip_addr[4];\r
- struct in_addr addr, *ip;\r
- const char *host = proxy_get_host_of_request(r);\r
-\r
- if (host == NULL) /* oops! */\r
- return 0;\r
-\r
- memset(&addr, '\0', sizeof addr);\r
- memset(ip_addr, '\0', sizeof ip_addr);\r
-\r
- if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {\r
- for (addr.s_addr = 0, i = 0; i < 4; ++i)\r
- addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));\r
-\r
- if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {\r
-#if DEBUGGING\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s/", inet_ntoa(This->addr));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s", inet_ntoa(This->mask));\r
-#endif\r
- return 1;\r
- }\r
-#if DEBUGGING\r
- else {\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s/", inet_ntoa(This->addr));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s", inet_ntoa(This->mask));\r
- }\r
-#endif\r
- }\r
- else {\r
- struct apr_sockaddr_t *reqaddr;\r
-\r
- if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool)\r
- != APR_SUCCESS) {\r
-#if DEBUGGING\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "2)IP-NoMatch: hostname=%s msg=Host not found", \r
- host);\r
-#endif\r
- return 0;\r
- }\r
-\r
- /* Try to deal with multiple IP addr's for a host */\r
- /* FIXME: This needs to be able to deal with IPv6 */\r
- while (reqaddr) {\r
- ip = (struct in_addr *) reqaddr->ipaddr_ptr;\r
- if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) {\r
-#if DEBUGGING\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "3)IP-Match: %s[%s] <-> ", host, \r
- inet_ntoa(*ip));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s/", inet_ntoa(This->addr));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s", inet_ntoa(This->mask));\r
-#endif\r
- return 1;\r
- }\r
-#if DEBUGGING\r
- else {\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "3)IP-NoMatch: %s[%s] <-> ", host, \r
- inet_ntoa(*ip));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s/", inet_ntoa(This->addr));\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "%s", inet_ntoa(This->mask));\r
- }\r
-#endif\r
- reqaddr = reqaddr->next;\r
- }\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-/* Return TRUE if addr represents a domain name */\r
-PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p)\r
-{\r
- char *addr = This->name;\r
- int i;\r
-\r
- /* Domain name must start with a '.' */\r
- if (addr[0] != '.')\r
- return 0;\r
-\r
- /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */\r
- for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)\r
- continue;\r
-\r
-#if 0\r
- if (addr[i] == ':') {\r
- ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,\r
- "@@@@ handle optional port in proxy_is_domainname()");\r
- /* @@@@ handle optional port */\r
- }\r
-#endif\r
-\r
- if (addr[i] != '\0')\r
- return 0;\r
-\r
- /* Strip trailing dots */\r
- for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)\r
- addr[i] = '\0';\r
-\r
- This->matcher = proxy_match_domainname;\r
- return 1;\r
-}\r
-\r
-/* Return TRUE if host "host" is in domain "domain" */\r
-static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)\r
-{\r
- const char *host = proxy_get_host_of_request(r);\r
- int d_len = strlen(This->name), h_len;\r
-\r
- if (host == NULL) /* some error was logged already */\r
- return 0;\r
-\r
- h_len = strlen(host);\r
-\r
- /* @@@ do this within the setup? */\r
- /* Ignore trailing dots in domain comparison: */\r
- while (d_len > 0 && This->name[d_len - 1] == '.')\r
- --d_len;\r
- while (h_len > 0 && host[h_len - 1] == '.')\r
- --h_len;\r
- return h_len > d_len\r
- && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;\r
-}\r
-\r
-/* Return TRUE if host represents a host name */\r
-PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p)\r
-{\r
- struct apr_sockaddr_t *addr;\r
- char *host = This->name;\r
- int i;\r
-\r
- /* Host names must not start with a '.' */\r
- if (host[0] == '.')\r
- return 0;\r
-\r
- /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */\r
- for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i);\r
-\r
- if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS)\r
- return 0;\r
- \r
- This->hostaddr = addr;\r
-\r
- /* Strip trailing dots */\r
- for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i)\r
- host[i] = '\0';\r
-\r
- This->matcher = proxy_match_hostname;\r
- return 1;\r
-}\r
-\r
-/* Return TRUE if host "host" is equal to host2 "host2" */\r
-static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)\r
-{\r
- char *host = This->name;\r
- const char *host2 = proxy_get_host_of_request(r);\r
- int h2_len;\r
- int h1_len;\r
-\r
- if (host == NULL || host2 == NULL)\r
- return 0; /* oops! */\r
-\r
- h2_len = strlen(host2);\r
- h1_len = strlen(host);\r
-\r
-#if 0\r
- struct apr_sockaddr_t *addr = *This->hostaddr;\r
-\r
- /* Try to deal with multiple IP addr's for a host */\r
- while (addr) {\r
- if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?)\r
- return 1;\r
- addr = addr->next;\r
- }\r
-#endif\r
-\r
- /* Ignore trailing dots in host2 comparison: */\r
- while (h2_len > 0 && host2[h2_len - 1] == '.')\r
- --h2_len;\r
- while (h1_len > 0 && host[h1_len - 1] == '.')\r
- --h1_len;\r
- return h1_len == h2_len\r
- && strncasecmp(host, host2, h1_len) == 0;\r
-}\r
-\r
-/* Return TRUE if addr is to be matched as a word */\r
-PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p)\r
-{\r
- This->matcher = proxy_match_word;\r
- return 1;\r
-}\r
-\r
-/* Return TRUE if string "str2" occurs literally in "str1" */\r
-static int proxy_match_word(struct dirconn_entry *This, request_rec *r)\r
-{\r
- const char *host = proxy_get_host_of_request(r);\r
- return host != NULL && ap_strstr_c(host, This->name) != NULL;\r
-}\r
-\r
-/* checks whether a host in uri_addr matches proxyblock */\r
-PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, \r
- apr_sockaddr_t *uri_addr)\r
-{\r
- int j;\r
- apr_sockaddr_t * src_uri_addr = uri_addr;\r
- /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */\r
- for (j = 0; j < conf->noproxies->nelts; j++) {\r
- struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;\r
- struct apr_sockaddr_t *conf_addr = npent[j].addr;\r
- uri_addr = src_uri_addr;\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,\r
- "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name);\r
- if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name))\r
- || npent[j].name[0] == '*') {\r
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,\r
- "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name);\r
- return HTTP_FORBIDDEN;\r
- }\r
- while (conf_addr) {\r
- while (uri_addr) {\r
- char *conf_ip;\r
- char *uri_ip;\r
- apr_sockaddr_ip_get(&conf_ip, conf_addr);\r
- apr_sockaddr_ip_get(&uri_ip, uri_addr);\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,\r
- "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip);\r
- if (!apr_strnatcasecmp(conf_ip, uri_ip)) {\r
- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,\r
- "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip);\r
- return HTTP_FORBIDDEN;\r
- }\r
- uri_addr = uri_addr->next;\r
- }\r
- conf_addr = conf_addr->next;\r
- }\r
- }\r
- return OK;\r
-}\r
-\r
-/* set up the minimal filter set */\r
-PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r)\r
-{\r
- ap_add_input_filter("HTTP_IN", NULL, r, c);\r
- return OK;\r
-}\r
-\r
-/* converts a series of buckets into a string \r
- * XXX: BillS says this function performs essentially the same function as \r
- * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() \r
- * instead? I think ap_proxy_string_read() will not work properly on non ASCII\r
- * (EBCDIC) machines either.\r
- */\r
-PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb,\r
- char *buff, apr_size_t bufflen, int *eos)\r
-{\r
- apr_bucket *e;\r
- apr_status_t rv;\r
- char *pos = buff;\r
- char *response;\r
- int found = 0;\r
- apr_size_t len;\r
-\r
- /* start with an empty string */\r
- buff[0] = 0;\r
- *eos = 0;\r
-\r
- /* loop through each brigade */\r
- while (!found) {\r
- /* get brigade from network one line at a time */\r
- if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, \r
- AP_MODE_GETLINE,\r
- APR_BLOCK_READ,\r
- 0))) {\r
- return rv;\r
- }\r
- /* loop through each bucket */\r
- while (!found) {\r
- if (*eos || APR_BRIGADE_EMPTY(bb)) {\r
- /* The connection aborted or timed out */\r
- return APR_ECONNABORTED;\r
- }\r
- e = APR_BRIGADE_FIRST(bb);\r
- if (APR_BUCKET_IS_EOS(e)) {\r
- *eos = 1;\r
- }\r
- else {\r
- if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) {\r
- return rv;\r
- }\r
- /* is string LF terminated? \r
- * XXX: This check can be made more efficient by simply checking \r
- * if the last character in the 'response' buffer is an ASCII_LF.\r
- * See ap_rgetline() for an example.\r
- */\r
- if (memchr(response, APR_ASCII_LF, len)) {\r
- found = 1;\r
- }\r
- /* concat strings until buff is full - then throw the data away */\r
- if (len > ((bufflen-1)-(pos-buff))) {\r
- len = (bufflen-1)-(pos-buff);\r
- }\r
- if (len > 0) {\r
- pos = apr_cpystrn(pos, response, len);\r
- }\r
- }\r
- APR_BUCKET_REMOVE(e);\r
- apr_bucket_destroy(e);\r
- }\r
- }\r
-\r
- return APR_SUCCESS;\r
-}\r
-\r
-/* unmerge an element in the table */\r
-PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key)\r
-{\r
- apr_off_t offset = 0;\r
- apr_off_t count = 0;\r
- char *value = NULL;\r
-\r
- /* get the value to unmerge */\r
- const char *initial = apr_table_get(t, key);\r
- if (!initial) {\r
- return;\r
- }\r
- value = apr_pstrdup(p, initial);\r
-\r
- /* remove the value from the headers */\r
- apr_table_unset(t, key);\r
-\r
- /* find each comma */\r
- while (value[count]) {\r
- if (value[count] == ',') {\r
- value[count] = 0;\r
- apr_table_add(t, key, value + offset);\r
- offset = count + 1;\r
- }\r
- count++;\r
- }\r
- apr_table_add(t, key, value + offset);\r
-}\r
-\r
-PROXY_DECLARE(proxy_balancer *) ap_proxy_get_balancer(apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url)\r
-{\r
- proxy_balancer *balancer;\r
- char *c, *uri = apr_pstrdup(p, url);\r
- int i;\r
- \r
- c = strchr(uri, ':'); \r
- if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')\r
- return NULL;\r
- /* remove path from uri */\r
- if ((c = strchr(c + 3, '/')))\r
- *c = '\0';\r
- balancer = (proxy_balancer *)conf->balancers->elts;\r
- for (i = 0; i < conf->balancers->nelts; i++) {\r
- if (strcasecmp(balancer->name, uri) == 0)\r
- return balancer;\r
- balancer++;\r
- }\r
- return NULL;\r
-}\r
-\r
-PROXY_DECLARE(const char *) ap_proxy_add_balancer(proxy_balancer **balancer,\r
- apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url)\r
-{\r
- char *c, *q, *uri = apr_pstrdup(p, url);\r
-\r
- c = strchr(uri, ':'); \r
- if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')\r
- return "Bad syntax for a balancer name";\r
- /* remove path from uri */\r
- if ((q = strchr(c + 3, '/')))\r
- *q = '\0';\r
-\r
- ap_str_tolower(uri);\r
- *balancer = apr_array_push(conf->balancers);\r
- memset(*balancer, 0, sizeof(proxy_balancer));\r
-\r
- (*balancer)->name = uri;\r
- (*balancer)->workers = apr_array_make(p, 5, sizeof(proxy_worker));\r
- /* XXX Is this a right place to create mutex */\r
-#if APR_HAS_THREADS\r
- if (apr_thread_mutex_create(&((*balancer)->mutex),\r
- APR_THREAD_MUTEX_DEFAULT, p) != APR_SUCCESS) {\r
- /* XXX: Do we need to log something here */\r
- return "can not create thread mutex";\r
- }\r
-#endif\r
-\r
- return NULL;\r
-}\r
-\r
-PROXY_DECLARE(proxy_worker *) ap_proxy_get_worker(apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url)\r
-{\r
- proxy_worker *worker;\r
- char *c, *uri = apr_pstrdup(p, url);\r
- int i;\r
- \r
- c = strchr(uri, ':'); \r
- if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')\r
- return NULL;\r
- /* remove path from uri */\r
- if ((c = strchr(c + 3, '/')))\r
- *c = '\0';\r
-\r
- worker = (proxy_worker *)conf->workers->elts;\r
- for (i = 0; i < conf->workers->nelts; i++) {\r
- if (strcasecmp(worker->name, uri) == 0) {\r
- return worker;\r
- }\r
- worker++;\r
- }\r
- return NULL;\r
-}\r
-\r
-#if APR_HAS_THREADS\r
-static apr_status_t conn_pool_cleanup(void *theworker)\r
-{\r
- proxy_worker *worker = (proxy_worker *)theworker;\r
- if (worker->cp->res) {\r
- worker->cp->pool = NULL;\r
- apr_reslist_destroy(worker->cp->res);\r
- }\r
- return APR_SUCCESS;\r
-}\r
-#endif\r
-\r
-static void init_conn_pool(apr_pool_t *p, proxy_worker *worker)\r
-{\r
- apr_pool_t *pool;\r
- proxy_conn_pool *cp;\r
- \r
- /* Create a connection pool's subpool. \r
- * This pool is used for connection recycling.\r
- * Once the worker is added it is never removed but\r
- * it can be disabled.\r
- */\r
- apr_pool_create(&pool, p);\r
- /* Alloc from the same pool as worker.\r
- * proxy_conn_pool is permanently attached to the worker. \r
- */\r
- cp = (proxy_conn_pool *)apr_pcalloc(p, sizeof(proxy_conn_pool));\r
- cp->pool = pool; \r
- worker->cp = cp;\r
-}\r
-\r
-PROXY_DECLARE(const char *) ap_proxy_add_worker(proxy_worker **worker,\r
- apr_pool_t *p,\r
- proxy_server_conf *conf,\r
- const char *url)\r
-{\r
- char *c, *q, *uri = apr_pstrdup(p, url);\r
- int port;\r
- \r
- c = strchr(uri, ':'); \r
- if (c == NULL || c[1] != '/' || c[2] != '/' || c[3] == '\0')\r
- return "Bad syntax for a remote proxy server";\r
- /* remove path from uri */\r
- if ((q = strchr(c + 3, '/')))\r
- *q = '\0';\r
-\r
- q = strchr(c + 3, ':');\r
- if (q != NULL) {\r
- if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) {\r
- return "Bad syntax for a remote proxy server (bad port number)";\r
- }\r
- }\r
- else\r
- port = -1;\r
- ap_str_tolower(uri);\r
- *worker = apr_array_push(conf->workers);\r
- memset(*worker, 0, sizeof(proxy_worker));\r
- (*worker)->name = apr_pstrdup(p, uri);\r
- *c = '\0';\r
- (*worker)->scheme = uri;\r
- (*worker)->hostname = c + 3;\r
-\r
- if (port == -1)\r
- port = apr_uri_port_of_scheme((*worker)->scheme);\r
- (*worker)->port = port;\r
- (*worker)->id = proxy_lb_workers;\r
- /* Increase the total worker count */\r
- proxy_lb_workers++;\r
- init_conn_pool(p, *worker);\r
-\r
- return NULL;\r
-}\r
-\r
-PROXY_DECLARE(proxy_worker *) ap_proxy_create_worker(apr_pool_t *p)\r
-{\r
-\r
- proxy_worker *worker;\r
- worker = (proxy_worker *)apr_pcalloc(p, sizeof(proxy_worker));\r
- worker->id = proxy_lb_workers;\r
- /* Increase the total worker count */\r
- proxy_lb_workers++;\r
- init_conn_pool(p, worker);\r
-\r
- return worker;\r
-}\r
-\r
-PROXY_DECLARE(void) \r
-ap_proxy_add_worker_to_balancer(apr_pool_t *pool, proxy_balancer *balancer,\r
- proxy_worker *worker)\r
-{\r
- proxy_worker *runtime;\r
-\r
- runtime = apr_array_push(balancer->workers);\r
- memcpy(runtime, worker, sizeof(proxy_worker));\r
- runtime->id = proxy_lb_workers;\r
- /* Increase the total runtime count */\r
- proxy_lb_workers++;\r
-\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker,\r
- proxy_balancer **balancer,\r
- request_rec *r,\r
- proxy_server_conf *conf, char **url)\r
-{\r
- int access_status;\r
- \r
- access_status = proxy_run_pre_request(worker, balancer, r, conf, url);\r
- if (access_status == DECLINED && *balancer == NULL) {\r
- *worker = ap_proxy_get_worker(r->pool, conf, *url);\r
- if (*worker) {\r
- *balancer = NULL;\r
- access_status = OK;\r
- }\r
- else if (r->proxyreq == PROXYREQ_PROXY) {\r
- if (conf->forward) {\r
- *balancer = NULL;\r
- *worker = conf->forward;\r
- access_status = OK;\r
- }\r
- }\r
- }\r
- else if (access_status == DECLINED && balancer != NULL) {\r
- /* All the workers are busy */\r
- access_status = HTTP_SERVICE_UNAVAILABLE;\r
- }\r
- return access_status;\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxy_post_request(proxy_worker *worker,\r
- proxy_balancer *balancer,\r
- request_rec *r,\r
- proxy_server_conf *conf)\r
-{\r
- int access_status;\r
- if (balancer)\r
- access_status = proxy_run_post_request(worker, balancer, r, conf);\r
- else { \r
- \r
-\r
- access_status = OK;\r
- }\r
-\r
- return access_status;\r
-}\r
-\r
-/* DEPRECATED */\r
-PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,\r
- const char *proxy_function,\r
- apr_sockaddr_t *backend_addr,\r
- const char *backend_name,\r
- proxy_server_conf *conf,\r
- server_rec *s,\r
- apr_pool_t *p)\r
-{\r
- apr_status_t rv;\r
- int connected = 0;\r
- int loglevel;\r
- \r
- while (backend_addr && !connected) {\r
- if ((rv = apr_socket_create(newsock, backend_addr->family,\r
- SOCK_STREAM, 0, p)) != APR_SUCCESS) {\r
- loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;\r
- ap_log_error(APLOG_MARK, loglevel, rv, s,\r
- "proxy: %s: error creating fam %d socket for target %s",\r
- proxy_function,\r
- backend_addr->family,\r
- backend_name);\r
- /* this could be an IPv6 address from the DNS but the\r
- * local machine won't give us an IPv6 socket; hopefully the\r
- * DNS returned an additional address to try\r
- */\r
- backend_addr = backend_addr->next;\r
- continue;\r
- }\r
-\r
-#if !defined(TPF) && !defined(BEOS)\r
- if (conf->recv_buffer_size > 0 &&\r
- (rv = apr_socket_opt_set(*newsock, APR_SO_RCVBUF,\r
- conf->recv_buffer_size))) {\r
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,\r
- "apr_socket_opt_set(SO_RCVBUF): Failed to set "\r
- "ProxyReceiveBufferSize, using default");\r
- }\r
-#endif\r
-\r
- /* Set a timeout on the socket */\r
- if (conf->timeout_set == 1) {\r
- apr_socket_timeout_set(*newsock, conf->timeout);\r
- }\r
- else {\r
- apr_socket_timeout_set(*newsock, s->timeout);\r
- }\r
-\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: %s: fam %d socket created to connect to %s",\r
- proxy_function, backend_addr->family, backend_name);\r
-\r
- /* make the connection out of the socket */\r
- rv = apr_socket_connect(*newsock, backend_addr);\r
-\r
- /* if an error occurred, loop round and try again */\r
- if (rv != APR_SUCCESS) {\r
- apr_socket_close(*newsock);\r
- loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;\r
- ap_log_error(APLOG_MARK, loglevel, rv, s,\r
- "proxy: %s: attempt to connect to %pI (%s) failed",\r
- proxy_function,\r
- backend_addr,\r
- backend_name);\r
- backend_addr = backend_addr->next;\r
- continue;\r
- }\r
- connected = 1;\r
- }\r
- return connected ? 0 : 1;\r
-}\r
-\r
-static apr_status_t connection_cleanup(void *theconn)\r
-{\r
- proxy_conn_rec *conn = (proxy_conn_rec *)theconn;\r
- proxy_worker *worker = conn->worker;\r
- \r
- /* If the connection pool is NULL the worker\r
- * cleanup has been run. Just return.\r
- */\r
- if (!worker->cp)\r
- return APR_SUCCESS;\r
-\r
- /* deterimine if the connection need to be closed */\r
- if (conn->close_on_recycle || conn->close) {\r
- apr_pool_t *p = conn->pool;\r
- apr_pool_clear(conn->pool);\r
- memset(conn, 0, sizeof(proxy_conn_rec));\r
- conn->pool = p;\r
- conn->worker = worker;\r
- }\r
-#if APR_HAS_THREADS\r
- if (worker->hmax && worker->cp->res) {\r
- apr_reslist_release(worker->cp->res, (void *)conn);\r
- }\r
- else\r
-#endif\r
- {\r
- worker->cp->conn = conn;\r
- }\r
-\r
- /* Allways return the SUCCESS */\r
- return APR_SUCCESS;\r
-}\r
-\r
-/* reslist constructor */\r
-static apr_status_t connection_constructor(void **resource, void *params,\r
- apr_pool_t *pool)\r
-{\r
- apr_pool_t *ctx;\r
- proxy_conn_rec *conn;\r
- proxy_worker *worker = (proxy_worker *)params;\r
- \r
- /* Create the subpool for each connection\r
- * This keeps the memory consumption constant\r
- * when disconnecting from backend.\r
- */\r
- apr_pool_create(&ctx, pool);\r
- conn = apr_pcalloc(pool, sizeof(proxy_conn_rec));\r
-\r
- conn->pool = ctx;\r
- conn->worker = worker;\r
- *resource = conn;\r
-\r
- return APR_SUCCESS;\r
-}\r
-\r
-/* reslist destructor */\r
-static apr_status_t connection_destructor(void *resource, void *params,\r
- apr_pool_t *pool)\r
-{\r
- proxy_conn_rec *conn = (proxy_conn_rec *)resource;\r
-\r
- /* Destroy the pool only if not called from reslist_destroy */ \r
- if (conn->worker->cp->pool)\r
- apr_pool_destroy(conn->pool);\r
-\r
- return APR_SUCCESS;\r
-}\r
-\r
-PROXY_DECLARE(void) ap_proxy_initialize_worker_share(proxy_server_conf *conf,\r
- proxy_worker *worker)\r
-{\r
-#if PROXY_HAS_SCOREBOARD\r
- lb_score *score = NULL;\r
-#else\r
- void *score = NULL;\r
-#endif\r
-#if PROXY_HAS_SCOREBOARD\r
- /* Get scoreboard slot */\r
- if (ap_scoreboard_image) {\r
- score = ap_get_scoreboard_lb(worker->id);\r
- if (!score)\r
- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, conf->pool,\r
- "proxy: ap_get_scoreboard_lb(%d) failed for worker %s",\r
- worker->id, worker->name);\r
- }\r
-#endif\r
- if (!score)\r
- score = apr_pcalloc(conf->pool, sizeof(proxy_worker_stat));\r
- worker->s = (proxy_worker_stat *)score;\r
- if (worker->route)\r
- strcpy(worker->s->route, worker->route);\r
- else\r
- *worker->s->route = '\0';\r
- if (worker->redirect)\r
- strcpy(worker->s->redirect, worker->redirect);\r
- else\r
- *worker->s->redirect = '\0';\r
-}\r
-\r
-PROXY_DECLARE(apr_status_t) ap_proxy_initialize_worker(proxy_worker *worker, server_rec *s)\r
-{\r
- apr_status_t rv;\r
-\r
-#if APR_HAS_THREADS\r
- int mpm_threads;\r
-\r
- ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);\r
- if (mpm_threads > 1) {\r
- /* Set hard max to no more then mpm_threads */\r
- if (worker->hmax == 0 || worker->hmax > mpm_threads)\r
- worker->hmax = mpm_threads;\r
- if (worker->smax == 0 || worker->smax > worker->hmax)\r
- worker->smax = worker->hmax;\r
- /* Set min to be lower then smax */\r
- if (worker->min > worker->smax)\r
- worker->min = worker->smax; \r
- }\r
- else {\r
- /* This will supress the apr_reslist creation */\r
- worker->min = worker->smax = worker->hmax = 0;\r
- }\r
- if (worker->hmax) {\r
- rv = apr_reslist_create(&(worker->cp->res),\r
- worker->min, worker->smax,\r
- worker->hmax, worker->ttl,\r
- connection_constructor, connection_destructor,\r
- worker, worker->cp->pool);\r
-\r
- apr_pool_cleanup_register(worker->cp->pool, (void *)worker,\r
- conn_pool_cleanup,\r
- apr_pool_cleanup_null);\r
-\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: initialized worker %d for (%s) min=%d max=%d smax=%d",\r
- worker->id, worker->hostname, worker->min, worker->hmax, worker->smax);\r
-\r
-#if (APR_MAJOR_VERSION > 0)\r
- /* Set the acquire timeout */\r
- if (rv == APR_SUCCESS && worker->acquire_set)\r
- apr_reslist_timeout_set(worker->cp->res, worker->acquire);\r
-#endif\r
- }\r
- else\r
-#endif\r
- {\r
- \r
- rv = connection_constructor((void **)&(worker->cp->conn), worker, worker->cp->pool);\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: initialized single connection worker %d for (%s)",\r
- worker->id, worker->hostname);\r
- }\r
- if (rv == APR_SUCCESS)\r
- worker->s->status |= PROXY_WORKER_INITIALIZED;\r
- /* Set default parameters */\r
- if (!worker->retry)\r
- worker->retry = apr_time_from_sec(PROXY_WORKER_DEFAULT_RETRY);\r
- return rv;\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxy_retry_worker(const char *proxy_function,\r
- proxy_worker *worker,\r
- server_rec *s)\r
-{\r
- if (worker->s->status & PROXY_WORKER_IN_ERROR) {\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: %s: retrying the worker for (%s)",\r
- proxy_function, worker->hostname);\r
- if (apr_time_now() > worker->s->error_time + worker->retry) {\r
- ++worker->s->retries;\r
- worker->s->status &= ~PROXY_WORKER_IN_ERROR;\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: %s: worker for (%s) has been marked for retry",\r
- proxy_function, worker->hostname);\r
- return OK;\r
- }\r
- else\r
- return DECLINED;\r
- }\r
- else\r
- return OK;\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxy_acquire_connection(const char *proxy_function,\r
- proxy_conn_rec **conn,\r
- proxy_worker *worker,\r
- server_rec *s)\r
-{\r
- apr_status_t rv;\r
-\r
- if (!PROXY_WORKER_IS_USABLE(worker)) {\r
- /* Retry the worker */\r
- ap_proxy_retry_worker(proxy_function, worker, s);\r
- \r
- if (!PROXY_WORKER_IS_USABLE(worker)) {\r
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,\r
- "proxy: %s: disabled connection for (%s)",\r
- proxy_function, worker->hostname);\r
- return HTTP_SERVICE_UNAVAILABLE;\r
- }\r
- }\r
-#if APR_HAS_THREADS\r
- if (worker->hmax) {\r
- rv = apr_reslist_acquire(worker->cp->res, (void **)conn);\r
- }\r
- else\r
-#endif\r
- {\r
- /* create the new connection if the previous was destroyed */\r
- if (!worker->cp->conn)\r
- connection_constructor((void **)conn, worker, worker->cp->pool);\r
- else {\r
- *conn = worker->cp->conn;\r
- worker->cp->conn = NULL;\r
- }\r
- rv = APR_SUCCESS;\r
- }\r
-\r
- if (rv != APR_SUCCESS) {\r
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,\r
- "proxy: %s: failed to acquire connection for (%s)",\r
- proxy_function, worker->hostname);\r
- return HTTP_SERVICE_UNAVAILABLE;\r
- }\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: %s: has acquired connection for (%s)",\r
- proxy_function, worker->hostname);\r
-\r
- (*conn)->worker = worker;\r
- (*conn)->close = 0;\r
- (*conn)->close_on_recycle = 0;\r
-\r
- return OK;\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxy_release_connection(const char *proxy_function,\r
- proxy_conn_rec *conn,\r
- server_rec *s)\r
-{\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: %s: has released connection for (%s)",\r
- proxy_function, conn->worker->hostname);\r
- /* If there is a connection kill it's cleanup */\r
- if (conn->connection) {\r
- apr_pool_cleanup_kill(conn->connection->pool, conn, connection_cleanup);\r
- conn->connection = NULL;\r
- }\r
- connection_cleanup(conn);\r
-\r
- return OK;\r
-}\r
-\r
-PROXY_DECLARE(int)\r
-ap_proxy_determine_connection(apr_pool_t *p, request_rec *r,\r
- proxy_server_conf *conf,\r
- proxy_worker *worker,\r
- proxy_conn_rec *conn,\r
- apr_uri_t *uri,\r
- char **url,\r
- const char *proxyname,\r
- apr_port_t proxyport,\r
- char *server_portstr,\r
- int server_portstr_size)\r
-{\r
- int server_port;\r
- apr_status_t err = APR_SUCCESS;\r
- /*\r
- * Break up the URL to determine the host to connect to\r
- */\r
-\r
- /* we break the URL into host, port, uri */\r
- if (APR_SUCCESS != apr_uri_parse(p, *url, uri)) {\r
- return ap_proxyerror(r, HTTP_BAD_REQUEST,\r
- apr_pstrcat(p,"URI cannot be parsed: ", *url,\r
- NULL));\r
- }\r
- if (!uri->port) {\r
- uri->port = apr_uri_port_of_scheme(uri->scheme);\r
- }\r
-\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,\r
- "proxy: connecting %s to %s:%d", *url, uri->hostname,\r
- uri->port);\r
-\r
- /* allocate these out of the specified connection pool \r
- * The scheme handler decides if this is permanent or\r
- * short living pool.\r
- */\r
- /* are we connecting directly, or via a proxy? */\r
- if (!proxyname) {\r
- *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "",\r
- uri->query ? uri->query : "",\r
- uri->fragment ? "#" : "",\r
- uri->fragment ? uri->fragment : "", NULL);\r
- }\r
- if (!conn->hostname) {\r
- if (proxyname) {\r
- conn->hostname = apr_pstrdup(conn->pool, proxyname);\r
- conn->port = proxyport;\r
- } else {\r
- conn->hostname = apr_pstrdup(conn->pool, uri->hostname);\r
- conn->port = uri->port;\r
- }\r
- }\r
- /* TODO: add address cache for forward proxies */\r
- conn->addr = worker->cp->addr;\r
- if (r->proxyreq == PROXYREQ_PROXY) {\r
- err = apr_sockaddr_info_get(&(conn->addr),\r
- conn->hostname, APR_UNSPEC,\r
- conn->port, 0,\r
- conn->pool);\r
- }\r
- else if (!worker->cp->addr) {\r
- /* Worker can have the single constant backend adress.\r
- * The single DNS lookup is used once per worker.\r
- * If dynamic change is needed then set the addr to NULL\r
- * inside dynamic config to force the lookup.\r
- */\r
- err = apr_sockaddr_info_get(&(worker->cp->addr),\r
- conn->hostname, APR_UNSPEC,\r
- conn->port, 0,\r
- worker->cp->pool);\r
- conn->addr = worker->cp->addr;\r
- }\r
- if (err != APR_SUCCESS) {\r
- return ap_proxyerror(r, HTTP_BAD_GATEWAY,\r
- apr_pstrcat(p, "DNS lookup failure for: ",\r
- conn->hostname, NULL));\r
- }\r
-\r
- /* Get the server port for the Via headers */\r
- {\r
- server_port = ap_get_server_port(r);\r
- if (ap_is_default_port(server_port, r)) {\r
- strcpy(server_portstr,"");\r
- } else {\r
- apr_snprintf(server_portstr, server_portstr_size, ":%d",\r
- server_port);\r
- }\r
- }\r
-\r
- /* check if ProxyBlock directive on this host */\r
- if (OK != ap_proxy_checkproxyblock(r, conf, conn->addr)) {\r
- return ap_proxyerror(r, HTTP_FORBIDDEN,\r
- "Connect to remote machine blocked");\r
- }\r
- return OK;\r
-}\r
-\r
-static int is_socket_connected(apr_socket_t *sock)\r
-\r
-{\r
- apr_size_t buffer_len = 1;\r
- char test_buffer[1]; \r
- apr_status_t socket_status;\r
- apr_interval_time_t current_timeout;\r
- \r
- /* save timeout */\r
- apr_socket_timeout_get(sock, ¤t_timeout);\r
- /* set no timeout */\r
- apr_socket_timeout_set(sock, 0);\r
- socket_status = apr_socket_recv(sock, test_buffer, &buffer_len);\r
- /* put back old timeout */\r
- apr_socket_timeout_set(sock, current_timeout);\r
- if (APR_STATUS_IS_EOF(socket_status))\r
- return 0;\r
- else\r
- return 1;\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,\r
- proxy_conn_rec *conn,\r
- proxy_worker *worker,\r
- server_rec *s)\r
-{\r
- apr_status_t rv;\r
- int connected = 0;\r
- int loglevel;\r
- apr_sockaddr_t *backend_addr = conn->addr;\r
- apr_socket_t *newsock;\r
- \r
- if (conn->sock) {\r
- /* This increases the connection pool size\r
- * but the number of dropped connections is\r
- * relatively small compared to connection lifetime\r
- */\r
- if (!(connected = is_socket_connected(conn->sock))) { \r
- apr_socket_close(conn->sock);\r
- conn->sock = NULL;\r
- }\r
- }\r
- while (backend_addr && !connected) {\r
- if ((rv = apr_socket_create(&newsock, backend_addr->family,\r
- SOCK_STREAM, APR_PROTO_TCP,\r
- conn->pool)) != APR_SUCCESS) {\r
- loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;\r
- ap_log_error(APLOG_MARK, loglevel, rv, s,\r
- "proxy: %s: error creating fam %d socket for target %s",\r
- proxy_function,\r
- backend_addr->family,\r
- worker->hostname);\r
- /* this could be an IPv6 address from the DNS but the\r
- * local machine won't give us an IPv6 socket; hopefully the\r
- * DNS returned an additional address to try\r
- */\r
- backend_addr = backend_addr->next;\r
- continue;\r
- }\r
-\r
-#if !defined(TPF) && !defined(BEOS)\r
- if (worker->recv_buffer_size > 0 &&\r
- (rv = apr_socket_opt_set(newsock, APR_SO_RCVBUF,\r
- worker->recv_buffer_size))) {\r
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,\r
- "apr_socket_opt_set(SO_RCVBUF): Failed to set "\r
- "ProxyReceiveBufferSize, using default");\r
- }\r
-#endif\r
-\r
- /* Set a timeout on the socket */\r
- if (worker->timeout_set == 1) {\r
- apr_socket_timeout_set(newsock, worker->timeout);\r
- }\r
- else {\r
- apr_socket_timeout_set(newsock, s->timeout);\r
- }\r
- /* Set a keepalive option */\r
- if (worker->keepalive) {\r
- if ((rv = apr_socket_opt_set(newsock, \r
- APR_SO_KEEPALIVE, 1)) != APR_SUCCESS) {\r
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,\r
- "apr_socket_opt_set(SO_KEEPALIVE): Failed to set"\r
- " Keepalive");\r
- }\r
- }\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: %s: fam %d socket created to connect to %s",\r
- proxy_function, backend_addr->family, worker->hostname);\r
-\r
- /* make the connection out of the socket */\r
- rv = apr_socket_connect(newsock, backend_addr);\r
-\r
- /* if an error occurred, loop round and try again */\r
- if (rv != APR_SUCCESS) {\r
- apr_socket_close(newsock);\r
- loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;\r
- ap_log_error(APLOG_MARK, loglevel, rv, s,\r
- "proxy: %s: attempt to connect to %pI (%s) failed",\r
- proxy_function,\r
- backend_addr,\r
- worker->hostname);\r
- backend_addr = backend_addr->next;\r
- continue;\r
- }\r
- \r
- conn->sock = newsock;\r
- connected = 1;\r
- }\r
- /* Put the entire worker to error state if\r
- * the PROXY_WORKER_IGNORE_ERRORS flag is not set.\r
- * Altrough some connections may be alive\r
- * no further connections to the worker could be made\r
- */\r
- if (!connected && PROXY_WORKER_IS_USABLE(worker) &&\r
- !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {\r
- worker->s->status |= PROXY_WORKER_IN_ERROR;\r
- worker->s->error_time = apr_time_now();\r
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,\r
- "ap_proxy_connect_backend disabling worker for (%s)",\r
- worker->hostname);\r
- }\r
- else {\r
- worker->s->error_time = 0;\r
- worker->s->retries = 0;\r
- }\r
- return connected ? OK : DECLINED;\r
-}\r
-\r
-PROXY_DECLARE(int) ap_proxy_connection_create(const char *proxy_function,\r
- proxy_conn_rec *conn,\r
- conn_rec *c,\r
- server_rec *s)\r
-{\r
- apr_sockaddr_t *backend_addr = conn->addr;\r
-\r
- /* The socket is now open, create a new backend server connection \r
- * \r
- */\r
- conn->connection = ap_run_create_connection(c->pool, s, conn->sock,\r
- c->id, c->sbh,\r
- c->bucket_alloc);\r
-\r
- if (!conn->connection) {\r
- /* the peer reset the connection already; ap_run_create_connection() \r
- * closed the socket\r
- */\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,\r
- s, "proxy: %s: an error occurred creating a "\r
- "new connection to %pI (%s)", proxy_function,\r
- backend_addr, conn->hostname);\r
- /* XXX: Will be closed when proxy_conn is closed */\r
- apr_socket_close(conn->sock);\r
- conn->sock = NULL;\r
- return HTTP_INTERNAL_SERVER_ERROR;\r
- }\r
- /* register the connection cleanup to client connection\r
- * so that the connection can be closed or reused\r
- */\r
- apr_pool_cleanup_register(c->pool, (void *)conn,\r
- connection_cleanup,\r
- apr_pool_cleanup_null); \r
-\r
- /* For ssl connection to backend */\r
- if (conn->is_ssl) {\r
- if (!ap_proxy_ssl_enable(conn->connection)) {\r
- ap_log_error(APLOG_MARK, APLOG_ERR, 0,\r
- s, "proxy: %s: failed to enable ssl support "\r
- "for %pI (%s)", proxy_function, \r
- backend_addr, conn->hostname);\r
- return HTTP_INTERNAL_SERVER_ERROR;\r
- }\r
- }\r
- else {\r
- /* TODO: See if this will break FTP */\r
- ap_proxy_ssl_disable(conn->connection);\r
- }\r
-\r
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,\r
- "proxy: %s: connection complete to %pI (%s)",\r
- proxy_function, backend_addr, conn->hostname);\r
-\r
- /* set up the connection filters */\r
- ap_run_pre_connection(conn->connection, conn->sock);\r
-\r
- return OK;\r
-}\r
-\r
-int ap_proxy_lb_workers(void)\r
-{\r
- /* Since we can't resize the scoreboard when reconfiguring, we\r
- * have to impose a limit on the number of workers, we are\r
- * able to reconfigure to.\r
- */\r
- if (!lb_workers_limit)\r
- lb_workers_limit = proxy_lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;\r
- return lb_workers_limit;\r
-}\r
+/* 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 <unistd.h> /* 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 "
+ "<em><a href=\"", ap_escape_uri(r->pool, r->uri),
+ "\">", ap_escape_html(r->pool, r->method),
+ " ",
+ ap_escape_html(r->pool, r->uri), "</a></em>.<p>\n"
+ "Reason: <strong>",
+ ap_escape_html(r->pool, message),
+ "</strong></p>", NULL));
+
+ /* Allow "error-notes" string to be printed by ap_send_error_response() */
+ apr_table_setn(r->notes, "verbose-error-to", apr_pstrdup(r->pool, "*"));
+
+ r->status_line = apr_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "proxy: %s returned by %s", message, r->uri);
+ return statuscode;
+}
+
+static const char *
+ proxy_get_host_of_request(request_rec *r)
+{
+ char *url, *user = NULL, *password = NULL, *err, *host;
+ apr_port_t port;
+
+ if (r->hostname != NULL)
+ return r->hostname;
+
+ /* Set url to the first char after "scheme://" */
+ if ((url = strchr(r->uri, ':')) == NULL
+ || url[1] != '/' || url[2] != '/')
+ return NULL;
+
+ url = apr_pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */
+
+ err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
+
+ if (err != NULL)
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "%s", err);
+
+ r->hostname = host;
+
+ return host; /* ought to return the port, too */
+}
+
+/* Return TRUE if addr represents an IP address (or an IP network address) */
+PROXY_DECLARE(int) ap_proxy_is_ipaddr(struct dirconn_entry *This, apr_pool_t *p)
+{
+ const char *addr = This->name;
+ long ip_addr[4];
+ int i, quads;
+ long bits;
+
+ /* if the address is given with an explicit netmask, use that */
+ /* Due to a deficiency in apr_inet_addr(), it is impossible to parse */
+ /* "partial" addresses (with less than 4 quads) correctly, i.e. */
+ /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
+ /* I therefore have to parse the IP address manually: */
+ /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */
+ /* addr and mask were set by proxy_readmask() */
+ /*return 1; */
+
+ /* Parse IP addr manually, optionally allowing */
+ /* abbreviated net addresses like 192.168. */
+
+ /* Iterate over up to 4 (dotted) quads. */
+ for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
+ char *tmp;
+
+ if (*addr == '/' && quads > 0) /* netmask starts here. */
+ break;
+
+ if (!apr_isdigit(*addr))
+ return 0; /* no digit at start of quad */
+
+ ip_addr[quads] = strtol(addr, &tmp, 0);
+
+ if (tmp == addr) /* expected a digit, found something else */
+ return 0;
+
+ if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
+ /* invalid octet */
+ return 0;
+ }
+
+ addr = tmp;
+
+ if (*addr == '.' && quads != 3)
+ ++addr; /* after the 4th quad, a dot would be illegal */
+ }
+
+ for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
+ This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
+
+ if (addr[0] == '/' && apr_isdigit(addr[1])) { /* net mask follows: */
+ char *tmp;
+
+ ++addr;
+
+ bits = strtol(addr, &tmp, 0);
+
+ if (tmp == addr) /* expected a digit, found something else */
+ return 0;
+
+ addr = tmp;
+
+ if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */
+ return 0;
+
+ }
+ else {
+ /* Determine (i.e., "guess") netmask by counting the */
+ /* number of trailing .0's; reduce #quads appropriately */
+ /* (so that 192.168.0.0 is equivalent to 192.168.) */
+ while (quads > 0 && ip_addr[quads - 1] == 0)
+ --quads;
+
+ /* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
+ if (quads < 1)
+ return 0;
+
+ /* every zero-byte counts as 8 zero-bits */
+ bits = 8 * quads;
+
+ if (bits != 32) /* no warning for fully qualified IP address */
+ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
+ "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\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;
+}