-/* 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 balancers that can be added */
-#define PROXY_DYNAMIC_BALANCER_LIMIT 16
-int ap_proxy_lb_workers(void);
-
-/* For proxy_util */
-extern module PROXY_DECLARE_DATA proxy_module;
-
-#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
+#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.
- */
-
-/* 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 */
-static int 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 = lb_workers;
- /* Increase the total worker count */
- ++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 = lb_workers;
- /* Increase the total worker count */
- ++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;
-
-#if PROXY_HAS_SCOREBOARD
- int mpm_daemons;
-
- ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &mpm_daemons);
- /* Check if we are prefork or single child */
- if (worker->hmax && mpm_daemons > 1) {
- /* Check only if workers_limit is set */
- if (lb_workers_limit && (lb_workers + 1) > lb_workers_limit) {
- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
- "proxy: Can not add worker (%s) to balancer (%s)."
- " Dynamic limit reached.",
- worker->name, balancer->name);
- return;
- }
- }
-#endif
- runtime = apr_array_push(balancer->workers);
- memcpy(runtime, worker, sizeof(proxy_worker));
- runtime->id = lb_workers;
- /* Increase the total runtime count */
- ++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);
-#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);
- if (worker->redirect)
- strcpy(worker->s->redirect, worker->redirect);
-}
-
-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 for (%s) min=%d max=%d smax=%d",
- 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 for (%s)",
- 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)
-{
- /* Set the dynamic #workers limit */
- lb_workers_limit = lb_workers + PROXY_DYNAMIC_BALANCER_LIMIT;
- return lb_workers_limit;
-}
+/* 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