diff options
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/proxy')
23 files changed, 8856 insertions, 0 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/.deps b/rubbos/app/httpd-2.0.64/modules/proxy/.deps new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/.deps diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/.indent.pro b/rubbos/app/httpd-2.0.64/modules/proxy/.indent.pro new file mode 100644 index 00000000..e2cd357a --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/.indent.pro @@ -0,0 +1,58 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-Tapr_bucket_brigade +-Tapr_pool_t +-Tap_filter_t +-Tbuf_area +-Tbuff_struct +-Tbuffy +-Tcmd_how +-Tcmd_parms +-Tcommand_rec +-Tcommand_struct +-Tconn_rec +-Tcore_dir_config +-Tcore_server_config +-Tdir_maker_func +-Tevent +-Tglobals_s +-Thandler_func +-Thandler_rec +-Tjoblist_s +-Tlisten_rec +-Tmerger_func +-Tmode_t +-Tmodule +-Tmodule_struct +-Tmutex +-Tn_long +-Tother_child_rec +-Toverrides_t +-Tparent_score +-Tpid_t +-Tpiped_log +-Tpool +-Trequest_rec +-Trequire_line +-Trlim_t +-Tscoreboard +-Tsemaphore +-Tserver_addr_rec +-Tserver_rec +-Tserver_rec_chain +-Tshort_score +-Ttable +-Ttable_entry +-Tthread +-Tu_wide_int +-Tvtime_t +-Twide_int +-Tproxy_server_conf diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/CHANGES b/rubbos/app/httpd-2.0.64/modules/proxy/CHANGES new file mode 100644 index 00000000..73a9228d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/CHANGES @@ -0,0 +1,223 @@ +****************************************** +* PLEASE NOTE: Now that development for * +* mod_proxy has been folded back into * +* the httpd-2.0 tree, this file has * +* been depreciated. Proxy changes should * +* be noted in httpd-2.0's CHANGES file. * +* This file exists for historical * +* purposes. * +****************************************** + +mod_proxy changes for httpd 2.0.29-dev + *) don't do keepalives for sub-requests. [Ian Holsman] + + *) fix up proxypass handling [Ian Holsman] + + *) don't send If-Modified-Since, Cache-Control, or If-None-Match on + a subrequest [Ian Holsman] + +mod_proxy changes for httpd 2.0.26-dev + *) Add New option 'HTTPProxyOverrideReturnedErrors'. By Turning the + Flag on, you will mask the error pages returned by the proxied + server, and will it will be handled as if your server generated + the error. This change was put in so that a 404 on a included + r-proxied component will act in the same manner as a 404 on a + included file. [Ian Holsman <ianh@cnet.com>] + +mod_proxy changes for httpd 2.0.25-dev + + *) Split proxy: space using <Proxy[Match] > directive blocks from + the <Directory[Match] > and <Files[Match] > blocks. Mod_proxy + now bypasses the directory and files testing phase (and skips + the http TRACE default handler on it's own, as well). Note that + <Location > blocks continue to be processed for proxy: requests. + [William Rowe <wrowe@covalent.net>] + + *) apr_uri type/function namespace changes in apr_uri functions + [Doug MacEachern <dougm@covalent.net>] + +mod_proxy changes for httpd 2.0.23-dev + + *) break the proxy_http_handler into multiple smaller functions. + [John Barbee <barbee@veribox.net>] + + *) Fix the proxy when the origin server sends back a 100 + Continue response. [John Barbee <barbee@veribox.net>] + + *) Change 'readbytes' from apr_size_t to apr_off_t due to change + in ap_get_brigade's parameters [John Barbee <barbee@veribox.net>] + +mod_proxy changes for httpd 2.0.20-dev + *) Timeout added for backend connections. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Fix abort code path in proxy_http.c, similar to FTP fix. + [Chuck Murcko <chuck@topsail.org>] + + *) Fix FTP ABOR command execution path. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) FTP return code variable cleanup; fixed problem in login + [Chuck Murcko <chuck@topsail.org>] + + *) Get PORT working again in the ftp proxy. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Return result code check for FTP QUIT, after fixing + problems with passive connection handling. + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Reorganize ap_proxy_string_read() internally to not process eos + buckets. + [Chuck Murcko <chuck@topsail.org>] + [Victor Orlikowski <v.j.orlikowski@gte.net>] + + *) Remove result code check for FTP QUIT command. Some servers send + nothing at all back in response to QUIT. + [Chuck Murcko <chuck@topsail.org>] + [Victor Orlikowski <v.j.orlikowski@gte.net>] + +mod_proxy changes for httpd 2.0.19 + + *) Reverse previous patch since the core reverted. + [Chuck Murcko <chuck@topsail.org>] + + *) Remove indirection on number of bytes to read for input filters. + [Chuck Murcko <chuck@topsail.org>] + + *) Fixed a problem with directory listing corruption in the + PROXY_DIR filter. + [Graham Leggett <minfrin@sharp.fm>] + + *) mod_proxy and the proxy submodules now build properly as DSOs. + [Graham Leggett <minfrin@sharp.fm>] + + *) Stopped the HTTP proxy from trying to read entity bodies when there + wasn't one (response was 1xx, 204, 205 or 304). + [Graham Leggett <minfrin@sharp.fm>] + + *) Made sure dates were canonicalised correctly when passed to the client + browser through the HTTP proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Split each individual proxy protocol into separate modules. + [Graham Leggett <minfrin@sharp.fm>] + + *) Added Max-Forwards support for all request types so as to prevent + loops. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix warnings about byte count type on Darwin (connect handler). + [Chuck Murcko <chuck@topsail.org>] + +mod_proxy changes for httpd 2.0.18 + + *) IPV6 EPSV support for IPV6 in FTP proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) FTP directory filter works now. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed some thread-safety issues with the HTTP proxy in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) PASV FTP works now. + [Graham Leggett <minfrin@sharp.fm>] + + *) Reworked the line-at-a-time read from the control connection to + workaround a stray empty bucket returned by the HTTP_IN filter. + [Graham Leggett <minfrin@sharp.fm>] + + *) Stopped the CORE filter from sending off an HTTP response when a + CONNECT tunnel was closed. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed the poll() loop in proxy_connect.c -> it works now!!! + [Graham Leggett <minfrin@sharp.fm>] + + *) Converted send_dir() to ap_proxy_send_dir_filter() in proxy_ftp.c. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.17 + + *) Major rework of ap_proxy_ftp_handler() to use filters (begone foul + BUFF!!!). It compiles, but is untested, and the build environment needs + to be fixed to include proxy_ftp.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Cleanup of dead functions within proxy_util.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Reworked the storage of the client socket between keepalive connections + to fix some nasty problems with the socket lasting longer than the + memory pool it was allocated from. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed bug where a hostname without a "." in it (such as "localhost") + would not trigger an IP address check with ProxyBlock. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.16 + + *) Fixed ProxyBlock bugs with ap_proxy_http_handler() and + ap_proxy_connect_handler(). + [Graham Leggett <minfrin@sharp.fm>] + + *) Updated ap_proxy_connect_handler() to support APR, while + moving some common code between http_handler and connect_handler + to proxy_util.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Updated mod_proxy.html docs to include v2.0 configuration. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed problem where responses without entity bodies would cause + the directly following proxy keepalive request to fail. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.15 + + *) Added support for downstream keepalives in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Changed mod_proxy ap_proxy_http_handler() to support APR properly. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix problem where incoming response headers were not being returned + to the client in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Added X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Server to + reverse proxied request headers in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) replace INADDR_NONE with APR_INADDR_NONE [Ian Holsman <IanH@cnet.com>] + + *) Fix problem with proxy configuration where globally set + configuration options were overridden inside virtual hosts. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix ProxyReceiveBufferSize where default value was left + uninitialised. + [Graham Leggett <minfrin@sharp.fm>] + + *) Some small changes: + - Ensured hop-by-hop headers were stripped as per + RFC2616 13.5.1. + - Upgraded version code to HTTP/1.1. + - Added Connection: close until Keepalives come. + - Some cosmetic fixes and commenting. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for httpd 2.0.14 + + *) removed ProxyNoCache and ProxyCacheForceCompletion config directives, + since we no longer directly cache from this module + [Chuck Murcko <chuck@topsail.org>] + + *) removed cache + [Chuck Murcko <chuck@topsail.org>] + + *) initial rerebuild for 2.0 + [Chuck Murcko <chuck@topsail.org>] + diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/Makefile b/rubbos/app/httpd-2.0.64/modules/proxy/Makefile new file mode 100644 index 00000000..d1597bdf --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/Makefile @@ -0,0 +1,8 @@ +top_srcdir = /bottlenecks/rubbos/app/httpd-2.0.64 +top_builddir = /bottlenecks/rubbos/app/httpd-2.0.64 +srcdir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/proxy +builddir = /bottlenecks/rubbos/app/httpd-2.0.64/modules/proxy +VPATH = /bottlenecks/rubbos/app/httpd-2.0.64/modules/proxy +# a modules Makefile has no explicit targets -- they will be defined by +# whatever modules are enabled. just grab special.mk to deal with this. +include $(top_srcdir)/build/special.mk diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/Makefile.in b/rubbos/app/httpd-2.0.64/modules/proxy/Makefile.in new file mode 100644 index 00000000..7c5c149d --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/Makefile.in @@ -0,0 +1,3 @@ +# a modules Makefile has no explicit targets -- they will be defined by +# whatever modules are enabled. just grab special.mk to deal with this. +include $(top_srcdir)/build/special.mk diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUmakefile b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUmakefile new file mode 100644 index 00000000..61842f0a --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUmakefile @@ -0,0 +1,247 @@ +# +# Declare the sub-directories to be built here +# + +SUBDIRS = \ + $(EOLIST) + +# +# Get the 'head' of the build environment. This includes default targets and +# paths to tools +# + +include $(AP_WORK)\build\NWGNUhead.inc + +# +# build this level's files + +# +# Make sure all needed macro's are defined +# + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxy.nlm \ + $(OBJDIR)/proxycon.nlm \ + $(OBJDIR)/proxyftp.nlm \ + $(OBJDIR)/proxyhtp.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + copy $(OBJDIR)\*.nlm $(INSTALL)\Apache2\modules\*.* + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxy b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxy new file mode 100644 index 00000000..d6abf6b3 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxy @@ -0,0 +1,261 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)\build\NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxy + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxy.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/mod_proxy.o \ + $(OBJDIR)/proxy_util.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + @ws2nlm.imp \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_module \ + proxy_hook_scheme_handler \ + proxy_hook_canon_handler \ + ap_proxy_ssl_enable \ + ap_proxy_ssl_disable \ + proxy_run_fixups \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +$(OBJDIR)/%.o: ../arch/netware/%.c $(OBJDIR)\$(NLM_NAME)_cc.opt + @echo compiling $< + $(CC) $< -o=$(OBJDIR)\$(@F) @$(OBJDIR)\$(NLM_NAME)_cc.opt + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxycon b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxycon new file mode 100644 index 00000000..07c91f70 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxycon @@ -0,0 +1,254 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)\build\NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxycon + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy Connection Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy Conn Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxycon.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/proxy_connect.o \ + $(OBJDIR)/proxy_util.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + proxy_module \ + proxy_hook_scheme_handler \ + proxy_hook_canon_handler \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_connect_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxyftp b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxyftp new file mode 100644 index 00000000..bd5d527c --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxyftp @@ -0,0 +1,260 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)\build\NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyftp + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy FTP Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy FTP Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxyftp.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/proxy_ftp.o \ + $(OBJDIR)/proxy_util.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + @ws2nlm.imp \ + proxy_module \ + proxy_hook_scheme_handler \ + proxy_hook_canon_handler \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_ftp_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +$(OBJDIR)/%.o: ../arch/netware/%.c $(OBJDIR)\$(NLM_NAME)_cc.opt + @echo compiling $< + $(CC) $< -o=$(OBJDIR)\$(@F) @$(OBJDIR)\$(NLM_NAME)_cc.opt + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxyhtp b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxyhtp new file mode 100644 index 00000000..5fda2693 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/NWGNUproxyhtp @@ -0,0 +1,263 @@ +# +# Make sure all needed macro's are defined +# + +# +# Get the 'head' of the build environment if necessary. This includes default +# targets and paths to tools +# + +ifndef EnvironmentDefined +include $(AP_WORK)\build\NWGNUhead.inc +endif + +# +# These directories will be at the beginning of the include list, followed by +# INCDIRS +# +XINCDIRS += \ + $(APR)/include \ + $(APRUTIL)/include \ + $(AP_WORK)/include \ + $(AP_WORK)/modules/http \ + $(NWOS) \ + $(EOLIST) + +# +# These flags will come after CFLAGS +# +XCFLAGS += \ + $(EOLIST) + +# +# These defines will come after DEFINES +# +XDEFINES += \ + $(EOLIST) + +# +# These flags will be added to the link.opt file +# +XLFLAGS += \ + $(EOLIST) + +# +# These values will be appended to the correct variables based on the value of +# RELEASE +# +ifeq "$(RELEASE)" "debug" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "noopt" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +ifeq "$(RELEASE)" "release" +XINCDIRS += \ + $(EOLIST) + +XCFLAGS += \ + $(EOLIST) + +XDEFINES += \ + $(EOLIST) + +XLFLAGS += \ + $(EOLIST) +endif + +# +# These are used by the link target if an NLM is being generated +# This is used by the link 'name' directive to name the nlm. If left blank +# TARGET_nlm (see below) will be used. +# +NLM_NAME = proxyhtp + +# +# This is used by the link '-desc ' directive. +# If left blank, NLM_NAME will be used. +# +NLM_DESCRIPTION = Apache $(VERSION_STR) Proxy HTTP Sub-Module + +# +# This is used by the '-threadname' directive. If left blank, +# NLM_NAME Thread will be used. +# +NLM_THREAD_NAME = Proxy HTTP Module + +# +# If this is specified, it will override VERSION value in +# $(AP_WORK)\build\NWGNUenvironment.inc +# +NLM_VERSION = + +# +# If this is specified, it will override the default of 64K +# +NLM_STACK_SIZE = 8192 + + +# +# If this is specified it will be used by the link '-entry' directive +# +NLM_ENTRY_SYM = _LibCPrelude + +# +# If this is specified it will be used by the link '-exit' directive +# +NLM_EXIT_SYM = _LibCPostlude + +# +# If this is specified it will be used by the link '-check' directive +# +NLM_CHECK_SYM = + +# +# If these are specified it will be used by the link '-flags' directive +# +NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION + +# +# If this is specified it will be linked in with the XDCData option in the def +# file instead of the default of $(NWOS)/apache.xdc. XDCData can be disabled +# by setting APACHE_UNIPROC in the environment +# +XDCDATA = + +# +# If there is an NLM target, put it here +# +TARGET_nlm = \ + $(OBJDIR)/proxyhtp.nlm \ + $(EOLIST) + +# +# If there is an LIB target, put it here +# +TARGET_lib = \ + $(EOLIST) + +# +# These are the OBJ files needed to create the NLM target above. +# Paths must all use the '/' character +# +FILES_nlm_objs = \ + $(OBJDIR)/proxy_http.o \ + $(OBJDIR)/proxy_util.o \ + $(OBJDIR)/libprews.o \ + $(EOLIST) + +# +# These are the LIB files needed to create the NLM target above. +# These will be added as a library command in the link.opt file. +# +FILES_nlm_libs = \ + libcpre.o \ + $(EOLIST) + +# +# These are the modules that the above NLM target depends on to load. +# These will be added as a module command in the link.opt file. +# +FILES_nlm_modules = \ + aprlib \ + libc \ + proxy \ + $(EOLIST) + +# +# If the nlm has a msg file, put it's path here +# +FILE_nlm_msg = + +# +# If the nlm has a hlp file put it's path here +# +FILE_nlm_hlp = + +# +# If this is specified, it will override $(NWOS)\copyright.txt. +# +FILE_nlm_copyright = + +# +# Any additional imports go here +# +FILES_nlm_Ximports = \ + @$(APR)/aprlib.imp \ + @$(NWOS)/httpd.imp \ + @libc.imp \ + @ws2nlm.imp \ + proxy_module \ + proxy_hook_scheme_handler \ + proxy_hook_canon_handler \ + proxy_run_fixups \ + ap_proxy_ssl_enable \ + ap_proxy_ssl_disable \ + $(EOLIST) + +# +# Any symbols exported to here +# +FILES_nlm_exports = \ + proxy_http_module \ + $(EOLIST) + +# +# These are the OBJ files needed to create the LIB target above. +# Paths must all use the '/' character +# +FILES_lib_objs = \ + $(EOLIST) + +# +# implement targets and dependancies (leave this section alone) +# + +libs :: $(OBJDIR) $(TARGET_lib) + +nlms :: libs $(TARGET_nlm) + +# +# Updated this target to create necessary directories and copy files to the +# correct place. (See $(AP_WORK)\build\NWGNUhead.inc for examples) +# +install :: nlms FORCE + +# +# Any specialized rules here +# + +$(OBJDIR)/%.o: ../arch/netware/%.c $(OBJDIR)\$(NLM_NAME)_cc.opt + @echo compiling $< + $(CC) $< -o=$(OBJDIR)\$(@F) @$(OBJDIR)\$(NLM_NAME)_cc.opt + +# +# Include the 'tail' makefile that has targets that depend on variables defined +# in this makefile +# + +include $(AP_WORK)\build\NWGNUtail.inc + + diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/config.m4 b/rubbos/app/httpd-2.0.64/modules/proxy/config.m4 new file mode 100644 index 00000000..d33683e9 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/config.m4 @@ -0,0 +1,34 @@ +dnl modules enabled in this directory by default + +APACHE_MODPATH_INIT(proxy) + +if test "$enable_proxy" = "shared"; then + proxy_mods_enable=shared +elif test "$enable_proxy" = "yes"; then + proxy_mods_enable=yes +else + proxy_mods_enable=no +fi + +proxy_objs="mod_proxy.lo proxy_util.lo" +APACHE_MODULE(proxy, Apache proxy module, $proxy_objs, , $proxy_mods_enable) + +proxy_connect_objs="proxy_connect.lo" +proxy_ftp_objs="proxy_ftp.lo" +proxy_http_objs="proxy_http.lo" + +case "$host" in + *os2*) + # OS/2 DLLs must resolve all symbols at build time and + # these sub-modules need some from the main proxy module + proxy_connect_objs="$proxy_connect_objs mod_proxy.la" + proxy_ftp_objs="$proxy_ftp_objs mod_proxy.la" + proxy_http_objs="$proxy_http_objs mod_proxy.la" + ;; +esac + +APACHE_MODULE(proxy_connect, Apache proxy CONNECT module, $proxy_connect_objs, , $proxy_mods_enable) +APACHE_MODULE(proxy_ftp, Apache proxy FTP module, $proxy_ftp_objs, , $proxy_mods_enable) +APACHE_MODULE(proxy_http, Apache proxy HTTP module, $proxy_http_objs, , $proxy_mods_enable) + +APACHE_MODPATH_FINISH diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/libproxy.exp b/rubbos/app/httpd-2.0.64/modules/proxy/libproxy.exp new file mode 100644 index 00000000..a20f2378 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/libproxy.exp @@ -0,0 +1 @@ +proxy_module diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.c b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.c new file mode 100644 index 00000000..84d5fb10 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.c @@ -0,0 +1,1181 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +#define CORE_PRIVATE + +#include "mod_proxy.h" +#include "mod_core.h" + +#include "apr_optional.h" + +#ifndef MAX +#define MAX(x,y) ((x) >= (y) ? (x) : (y)) +#endif + +/* + * A Web proxy module. Stages: + * + * translate_name: set filename to proxy:<URL> + * map_to_storage: run proxy_walk (rather than directory_walk/file_walk) + * can't trust directory_walk/file_walk since these are + * not in our filesystem. Prevents mod_http from serving + * the TRACE request we will set aside to handle later. + * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy: + * fix_ups: convert the URL stored in the filename to the + * canonical form. + * handler: handle proxy requests + */ + +/* -------------------------------------------------------------- */ +/* Translate the URL into a 'filename' */ + +static int alias_match(const char *uri, const char *alias_fakename) +{ + const char *end_fakename = alias_fakename + strlen(alias_fakename); + const char *aliasp = alias_fakename, *urip = uri; + + while (aliasp < end_fakename) { + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') + return 0; + + while (*aliasp == '/') + ++aliasp; + while (*urip == '/') + ++urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) + return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +/* Detect if an absoluteURI should be proxied or not. Note that we + * have to do this during this phase because later phases are + * "short-circuiting"... i.e. translate_names will end when the first + * module returns OK. So for example, if the request is something like: + * + * GET http://othervhost/cgi-bin/printenv HTTP/1.0 + * + * mod_alias will notice the /cgi-bin part and ScriptAlias it and + * short-circuit the proxy... just because of the ordering in the + * configuration file. + */ +static int proxy_detect(request_rec *r) +{ + void *sconf = r->server->module_config; + proxy_server_conf *conf; + + conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + /* Ick... msvc (perhaps others) promotes ternary short results to int */ + + if (conf->req && r->parsed_uri.scheme) { + /* but it might be something vhosted */ + if (!(r->parsed_uri.hostname + && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r)) + && ap_matches_request_vhost(r, r->parsed_uri.hostname, + (apr_port_t)(r->parsed_uri.port_str ? r->parsed_uri.port + : ap_default_port(r))))) { + r->proxyreq = PROXYREQ_PROXY; + r->uri = r->unparsed_uri; + r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + } + } + /* We need special treatment for CONNECT proxying: it has no scheme part */ + else if (conf->req && r->method_number == M_CONNECT + && r->parsed_uri.hostname + && r->parsed_uri.port_str) { + r->proxyreq = PROXYREQ_PROXY; + r->uri = r->unparsed_uri; + r->filename = apr_pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + } + return DECLINED; +} + +static int proxy_trans(request_rec *r) +{ + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + int i, len; + struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts; + + if (r->proxyreq) { + /* someone has already set up the proxy, it was possibly ourselves + * in proxy_detect + */ + return OK; + } + + /* XXX: since r->uri has been manipulated already we're not really + * compliant with RFC1945 at this point. But this probably isn't + * an issue because this is a hybrid proxy/origin server. + */ + + for (i = 0; i < conf->aliases->nelts; i++) { + len = alias_match(r->uri, ent[i].fake); + + if (len > 0) { + if ((ent[i].real[0] == '!' ) && ( ent[i].real[1] == 0 )) { + return DECLINED; + } + + r->filename = apr_pstrcat(r->pool, "proxy:", ent[i].real, + (r->uri + len ), NULL); + r->handler = "proxy-server"; + r->proxyreq = PROXYREQ_REVERSE; + return OK; + } + } + return DECLINED; +} + +static int proxy_walk(request_rec *r) +{ + proxy_server_conf *sconf = ap_get_module_config(r->server->module_config, + &proxy_module); + ap_conf_vector_t *per_dir_defaults = r->server->lookup_defaults; + ap_conf_vector_t **sec_proxy = (ap_conf_vector_t **) sconf->sec_proxy->elts; + ap_conf_vector_t *entry_config; + proxy_dir_conf *entry_proxy; + int num_sec = sconf->sec_proxy->nelts; + /* XXX: shouldn't we use URI here? Canonicalize it first? + * Pass over "proxy:" prefix + */ + const char *proxyname = r->filename + 6; + int j; + + for (j = 0; j < num_sec; ++j) + { + entry_config = sec_proxy[j]; + entry_proxy = ap_get_module_config(entry_config, &proxy_module); + + /* XXX: What about case insensitive matching ??? + * Compare regex, fnmatch or string as appropriate + * If the entry doesn't relate, then continue + */ + if (entry_proxy->r + ? ap_regexec(entry_proxy->r, proxyname, 0, NULL, 0) + : (entry_proxy->p_is_fnmatch + ? apr_fnmatch(entry_proxy->p, proxyname, 0) + : strncmp(proxyname, entry_proxy->p, + strlen(entry_proxy->p)))) { + continue; + } + per_dir_defaults = ap_merge_per_dir_configs(r->pool, per_dir_defaults, + entry_config); + } + + r->per_dir_config = per_dir_defaults; + + return OK; +} + +static int proxy_map_location(request_rec *r) +{ + int access_status; + + if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) + return DECLINED; + + /* Don't let the core or mod_http map_to_storage hooks handle this, + * We don't need directory/file_walk, and we want to TRACE on our own. + */ + if ((access_status = proxy_walk(r))) { + ap_die(access_status, r); + return access_status; + } + + return OK; +} + +/* -------------------------------------------------------------- */ +/* Fixup the filename */ + +/* + * Canonicalise the URL + */ +static int proxy_fixup(request_rec *r) +{ + char *url, *p; + int access_status; + + if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) + return DECLINED; + + /* XXX: Shouldn't we try this before we run the proxy_walk? */ + url = &r->filename[6]; + + /* canonicalise each specific scheme */ + if ((access_status = proxy_run_canon_handler(r, url))) { + return access_status; + } + + p = strchr(url, ':'); + if (p == NULL || p == url) + return HTTP_BAD_REQUEST; + + return OK; /* otherwise; we've done the best we can */ +} + +/* Send a redirection if the request contains a hostname which is not */ +/* fully qualified, i.e. doesn't have a domain name appended. Some proxy */ +/* servers like Netscape's allow this and access hosts from the local */ +/* domain in this case. I think it is better to redirect to a FQDN, since */ +/* these will later be found in the bookmarks files. */ +/* The "ProxyDomain" directive determines what domain will be appended */ +static int proxy_needsdomain(request_rec *r, const char *url, const char *domain) +{ + char *nuri; + const char *ref; + + /* We only want to worry about GETs */ + if (!r->proxyreq || r->method_number != M_GET || !r->parsed_uri.hostname) + return DECLINED; + + /* If host does contain a dot already, or it is "localhost", decline */ + if (strchr(r->parsed_uri.hostname, '.') != NULL + || strcasecmp(r->parsed_uri.hostname, "localhost") == 0) + return DECLINED; /* host name has a dot already */ + + ref = apr_table_get(r->headers_in, "Referer"); + + /* Reassemble the request, but insert the domain after the host name */ + /* Note that the domain name always starts with a dot */ + r->parsed_uri.hostname = apr_pstrcat(r->pool, r->parsed_uri.hostname, + domain, NULL); + nuri = apr_uri_unparse(r->pool, + &r->parsed_uri, + APR_URI_UNP_REVEALPASSWORD); + + apr_table_set(r->headers_out, "Location", nuri); + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "Domain missing: %s sent to %s%s%s", r->uri, + apr_uri_unparse(r->pool, &r->parsed_uri, + APR_URI_UNP_OMITUSERINFO), + ref ? " from " : "", ref ? ref : ""); + + return HTTP_MOVED_PERMANENTLY; +} + +/* -------------------------------------------------------------- */ +/* Invoke handler */ + +static int proxy_handler(request_rec *r) +{ + char *url, *scheme, *p; + const char *p2; + void *sconf = r->server->module_config; + proxy_server_conf *conf = (proxy_server_conf *) + ap_get_module_config(sconf, &proxy_module); + apr_array_header_t *proxies = conf->proxies; + struct proxy_remote *ents = (struct proxy_remote *) proxies->elts; + int i, rc, access_status; + int direct_connect = 0; + const char *str; + long maxfwd; + + /* is this for us? */ + if (!r->proxyreq || !r->filename || strncmp(r->filename, "proxy:", 6) != 0) + return DECLINED; + + /* handle max-forwards / OPTIONS / TRACE */ + if ((str = apr_table_get(r->headers_in, "Max-Forwards"))) { + maxfwd = strtol(str, NULL, 10); + if (maxfwd < 1) { + switch (r->method_number) { + case M_TRACE: { + int access_status; + r->proxyreq = PROXYREQ_NONE; + if ((access_status = ap_send_http_trace(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return OK; + } + case M_OPTIONS: { + int access_status; + r->proxyreq = PROXYREQ_NONE; + if ((access_status = ap_send_http_options(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return OK; + } + default: { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Max-Forwards has reached zero - proxy loop?"); + } + } + } + maxfwd = (maxfwd > 0) ? maxfwd - 1 : 0; + } + else { + /* set configured max-forwards */ + maxfwd = conf->maxfwd; + } + apr_table_set(r->headers_in, "Max-Forwards", + apr_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd : 0)); + + if (r->method_number == M_TRACE) { + core_server_config *coreconf = (core_server_config *) + ap_get_module_config(sconf, &core_module); + + if (coreconf->trace_enable == AP_TRACE_DISABLE) + { + /* Allow "error-notes" string to be printed by ap_send_error_response() + * Note; this goes nowhere, canned error response need an overhaul. + */ + apr_table_setn(r->notes, "error-notes", + "TRACE forbidden by server configuration"); + apr_table_setn(r->notes, "verbose-error-to", "*"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "proxy: TRACE forbidden by server configuration"); + return HTTP_FORBIDDEN; + } + + /* Can't test ap_should_client_block, we aren't ready to send + * the client a 100 Continue response till the connection has + * been established + */ + if (coreconf->trace_enable != AP_TRACE_EXTENDED + && (r->read_length || r->read_chunked || r->remaining)) + { + /* Allow "error-notes" string to be printed by ap_send_error_response() + * Note; this goes nowhere, canned error response need an overhaul. + */ + apr_table_setn(r->notes, "error-notes", + "TRACE with request body is not allowed"); + apr_table_setn(r->notes, "verbose-error-to", "*"); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "proxy: TRACE with request body is not allowed"); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + } + + url = r->filename + 6; + p = strchr(url, ':'); + if (p == NULL) + return HTTP_BAD_REQUEST; + + /* If the host doesn't have a domain name, add one and redirect. */ + if (conf->domain != NULL) { + rc = proxy_needsdomain(r, url, conf->domain); + if (ap_is_HTTP_REDIRECT(rc)) + return HTTP_MOVED_PERMANENTLY; + } + + *p = '\0'; + scheme = apr_pstrdup(r->pool, url); + *p = ':'; + + /* Check URI's destination host against NoProxy hosts */ + /* Bypass ProxyRemote server lookup if configured as NoProxy */ + /* we only know how to handle communication to a proxy via http */ + /*if (strcasecmp(scheme, "http") == 0) */ + { + int ii; + struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; + + for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) { + direct_connect = list[ii].matcher(&list[ii], r); + } +#if DEBUGGING + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + (direct_connect) ? "NoProxy for %s" : "UseProxy for %s", + r->uri); +#endif + } + + /* firstly, try a proxy, unless a NoProxy directive is active */ + if (!direct_connect) { + for (i = 0; i < proxies->nelts; i++) { + p2 = ap_strchr_c(ents[i].scheme, ':'); /* is it a partial URL? */ + if (strcmp(ents[i].scheme, "*") == 0 || + (ents[i].use_regex && + ap_regexec(ents[i].regexp, url, 0,NULL, 0) == 0) || + (p2 == NULL && strcasecmp(scheme, ents[i].scheme) == 0) || + (p2 != NULL && + strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) { + + /* handle the scheme */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Trying to run scheme_handler against proxy"); + access_status = proxy_run_scheme_handler(r, conf, url, ents[i].hostname, ents[i].port); + + /* an error or success */ + if (access_status != DECLINED && access_status != HTTP_BAD_GATEWAY) { + return access_status; + } + /* we failed to talk to the upstream proxy */ + } + } + } + + /* otherwise, try it direct */ + /* N.B. what if we're behind a firewall, where we must use a proxy or + * give up?? + */ + + /* handle the scheme */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Trying to run scheme_handler"); + access_status = proxy_run_scheme_handler(r, conf, url, NULL, 0); + if (DECLINED == access_status) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: No protocol handler was valid for the URL %s. " + "If you are using a DSO version of mod_proxy, make sure " + "the proxy submodules are included in the configuration " + "using LoadModule.", r->uri); + return HTTP_FORBIDDEN; + } + return access_status; +} + +/* -------------------------------------------------------------- */ +/* Setup configurable data */ + +static void * create_proxy_config(apr_pool_t *p, server_rec *s) +{ + proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf)); + + ps->sec_proxy = apr_array_make(p, 10, sizeof(ap_conf_vector_t *)); + ps->proxies = apr_array_make(p, 10, sizeof(struct proxy_remote)); + ps->aliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->raliases = apr_array_make(p, 10, sizeof(struct proxy_alias)); + ps->noproxies = apr_array_make(p, 10, sizeof(struct noproxy_entry)); + ps->dirconn = apr_array_make(p, 10, sizeof(struct dirconn_entry)); + ps->allowed_connect_ports = apr_array_make(p, 10, sizeof(int)); + ps->domain = NULL; + ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */ + ps->viaopt_set = 0; /* 0 means default */ + ps->req = 0; + ps->req_set = 0; + ps->recv_buffer_size = 0; /* this default was left unset for some reason */ + ps->recv_buffer_size_set = 0; + ps->io_buffer_size = AP_IOBUFSIZE; + ps->io_buffer_size_set = 0; + ps->maxfwd = DEFAULT_MAX_FORWARDS; + ps->maxfwd_set = 0; + ps->error_override = 0; + ps->error_override_set = 0; + ps->preserve_host_set = 0; + ps->preserve_host = 0; + ps->timeout = 0; + ps->timeout_set = 0; + ps->badopt = bad_error; + ps->badopt_set = 0; + return ps; +} + +static void * merge_proxy_config(apr_pool_t *p, void *basev, void *overridesv) +{ + proxy_server_conf *ps = apr_pcalloc(p, sizeof(proxy_server_conf)); + proxy_server_conf *base = (proxy_server_conf *) basev; + proxy_server_conf *overrides = (proxy_server_conf *) overridesv; + + ps->proxies = apr_array_append(p, base->proxies, overrides->proxies); + ps->sec_proxy = apr_array_append(p, base->sec_proxy, overrides->sec_proxy); + ps->aliases = apr_array_append(p, base->aliases, overrides->aliases); + ps->raliases = apr_array_append(p, base->raliases, overrides->raliases); + ps->noproxies = apr_array_append(p, base->noproxies, overrides->noproxies); + ps->dirconn = apr_array_append(p, base->dirconn, overrides->dirconn); + ps->allowed_connect_ports = apr_array_append(p, base->allowed_connect_ports, overrides->allowed_connect_ports); + + ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain; + ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt; + ps->viaopt_set = overrides->viaopt_set || base->viaopt_set; + ps->req = (overrides->req_set == 0) ? base->req : overrides->req; + ps->req_set = overrides->req_set || base->req_set; + ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size; + ps->recv_buffer_size_set = overrides->recv_buffer_size_set || base->recv_buffer_size_set; + ps->io_buffer_size = (overrides->io_buffer_size_set == 0) ? base->io_buffer_size : overrides->io_buffer_size; + ps->io_buffer_size_set = overrides->io_buffer_size_set || base->io_buffer_size_set; + ps->maxfwd = (overrides->maxfwd_set == 0) ? base->maxfwd : overrides->maxfwd; + ps->maxfwd_set = overrides->maxfwd_set || base->maxfwd_set; + ps->error_override = (overrides->error_override_set == 0) ? base->error_override : overrides->error_override; + ps->error_override_set = overrides->error_override_set || base->error_override_set; + ps->preserve_host = (overrides->preserve_host_set == 0) ? base->preserve_host : overrides->preserve_host; + ps->preserve_host_set = overrides->preserve_host_set || base->preserve_host_set; + ps->timeout= (overrides->timeout_set == 0) ? base->timeout : overrides->timeout; + ps->timeout_set = overrides->timeout_set || base->timeout_set; + ps->badopt = (overrides->badopt_set == 0) ? base->badopt : overrides->badopt; + ps->badopt_set = overrides->badopt_set || base->badopt_set; + + return ps; +} + +static void *create_proxy_dir_config(apr_pool_t *p, char *dummy) +{ + proxy_dir_conf *new = + (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf)); + + /* Filled in by proxysection, when applicable */ + + return (void *) new; +} + +static void *merge_proxy_dir_config(apr_pool_t *p, void *basev, void *addv) +{ + proxy_dir_conf *new = (proxy_dir_conf *) apr_pcalloc(p, sizeof(proxy_dir_conf)); + proxy_dir_conf *add = (proxy_dir_conf *) addv; + proxy_dir_conf *base = (proxy_dir_conf *) basev; + + new->p = add->p; + new->p_is_fnmatch = add->p_is_fnmatch; + new->r = add->r; + new->ftp_directory_charset = add->ftp_directory_charset ? + add->ftp_directory_charset : + base->ftp_directory_charset; + return new; +} + + +static const char * + add_proxy(cmd_parms *cmd, void *dummy, const char *f1, const char *r1, int regex) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + struct proxy_remote *new; + char *p, *q; + char *r, *f, *scheme; + regex_t *reg = NULL; + int port; + + r = apr_pstrdup(cmd->pool, r1); + scheme = apr_pstrdup(cmd->pool, r1); + f = apr_pstrdup(cmd->pool, f1); + p = strchr(r, ':'); + if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') { + if (regex) + return "ProxyRemoteMatch: Bad syntax for a remote proxy server"; + else + return "ProxyRemote: Bad syntax for a remote proxy server"; + } + else { + scheme[p-r] = 0; + } + q = strchr(p + 3, ':'); + if (q != NULL) { + if (sscanf(q + 1, "%u", &port) != 1 || port > 65535) { + if (regex) + return "ProxyRemoteMatch: Bad syntax for a remote proxy server (bad port number)"; + else + return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)"; + } + *q = '\0'; + } + else + port = -1; + *p = '\0'; + if (regex) { + reg = ap_pregcomp(cmd->pool, f, REG_EXTENDED); + if (!reg) + return "Regular expression for ProxyRemoteMatch could not be compiled."; + } + else + if (strchr(f, ':') == NULL) + ap_str_tolower(f); /* lowercase scheme */ + ap_str_tolower(p + 3); /* lowercase hostname */ + + if (port == -1) { + port = apr_uri_port_of_scheme(scheme); + } + + new = apr_array_push(conf->proxies); + new->scheme = f; + new->protocol = r; + new->hostname = p + 3; + new->port = port; + new->regexp = reg; + new->use_regex = regex; + return NULL; +} + +static const char * + add_proxy_noregex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1) +{ + return add_proxy(cmd, dummy, f1, r1, 0); +} + +static const char * + add_proxy_regex(cmd_parms *cmd, void *dummy, const char *f1, const char *r1) +{ + return add_proxy(cmd, dummy, f1, r1, 1); +} + +static const char * + add_pass(cmd_parms *cmd, void *dummy, const char *f, const char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module); + struct proxy_alias *new; + if (r!=NULL && cmd->path == NULL ) { + new = apr_array_push(conf->aliases); + new->fake = f; + new->real = r; + } else if (r==NULL && cmd->path != NULL) { + new = apr_array_push(conf->aliases); + new->fake = cmd->path; + new->real = f; + } else { + if ( r== NULL) + return "ProxyPass needs a path when not defined in a location"; + else + return "ProxyPass can not have a path when defined in a location"; + } + + return NULL; +} + +static const char * + add_pass_reverse(cmd_parms *cmd, void *dummy, const char *f, const char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf; + struct proxy_alias *new; + + conf = (proxy_server_conf *)ap_get_module_config(s->module_config, + &proxy_module); + if (r!=NULL && cmd->path == NULL ) { + new = apr_array_push(conf->raliases); + new->fake = f; + new->real = r; + } else if (r==NULL && cmd->path != NULL) { + new = apr_array_push(conf->raliases); + new->fake = cmd->path; + new->real = f; + } else { + if ( r == NULL) + return "ProxyPassReverse needs a path when not defined in a location"; + else + return "ProxyPassReverse can not have a path when defined in a location"; + } + + return NULL; +} + +static const char * + set_proxy_exclude(cmd_parms *parms, void *dummy, const char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + struct noproxy_entry *new; + struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts; + struct apr_sockaddr_t *addr; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i = 0; i < conf->noproxies->nelts; i++) { + if (apr_strnatcasecmp(arg, list[i].name) == 0) { /* ignore case for host names */ + found = 1; + } + } + + if (!found) { + new = apr_array_push(conf->noproxies); + new->name = arg; + if (APR_SUCCESS == apr_sockaddr_info_get(&addr, new->name, APR_UNSPEC, 0, 0, parms->pool)) { + new->addr = addr; + } + else { + new->addr = NULL; + } + } + return NULL; +} + +/* + * Set the ports CONNECT can use + */ +static const char * + set_allowed_ports(cmd_parms *parms, void *dummy, const char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + int *New; + + if (!apr_isdigit(arg[0])) + return "AllowCONNECT: port number must be numeric"; + + New = apr_array_push(conf->allowed_connect_ports); + *New = atoi(arg); + return NULL; +} + +/* Similar to set_proxy_exclude(), but defining directly connected hosts, + * which should never be accessed via the configured ProxyRemote servers + */ +static const char * + set_proxy_dirconn(cmd_parms *parms, void *dummy, const char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + ap_get_module_config(s->module_config, &proxy_module); + struct dirconn_entry *New; + struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i = 0; i < conf->dirconn->nelts; i++) { + if (strcasecmp(arg, list[i].name) == 0) + found = 1; + } + + if (!found) { + New = apr_array_push(conf->dirconn); + New->name = apr_pstrdup(parms->pool, arg); + New->hostaddr = NULL; + + if (ap_proxy_is_ipaddr(New, parms->pool)) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "Parsed addr %s", inet_ntoa(New->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "Parsed mask %s", inet_ntoa(New->mask)); +#endif + } + else if (ap_proxy_is_domainname(New, parms->pool)) { + ap_str_tolower(New->name); +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "Parsed domain %s", New->name); +#endif + } + else if (ap_proxy_is_hostname(New, parms->pool)) { + ap_str_tolower(New->name); +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "Parsed host %s", New->name); +#endif + } + else { + ap_proxy_is_word(New, parms->pool); +#if DEBUGGING + fprintf(stderr, "Parsed word %s\n", New->name); +#endif + } + } + return NULL; +} + +static const char * + set_proxy_domain(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (arg[0] != '.') + return "ProxyDomain: domain name must start with a dot."; + + psf->domain = arg; + return NULL; +} + +static const char * + set_proxy_req(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->req = flag; + psf->req_set = 1; + return NULL; +} +static const char * + set_proxy_error_override(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->error_override = flag; + psf->error_override_set = 1; + return NULL; +} +static const char * + set_preserve_host(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + psf->preserve_host = flag; + psf->preserve_host_set = 1; + return NULL; +} + +static const char * + set_recv_buffer_size(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int s = atoi(arg); + if (s < 512 && s != 0) { + return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default."; + } + + psf->recv_buffer_size = s; + psf->recv_buffer_size_set = 1; + return NULL; +} + +static const char * + set_io_buffer_size(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + long s = atol(arg); + + psf->io_buffer_size = ((s > AP_IOBUFSIZE) ? s : AP_IOBUFSIZE); + psf->io_buffer_size_set = 1; + return NULL; +} + +static const char * + set_max_forwards(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + long s = atol(arg); + if (s < 0) { + return "ProxyMaxForwards must be greater or equal to zero.."; + } + + psf->maxfwd = s; + psf->maxfwd_set = 1; + return NULL; +} +static const char* + set_proxy_timeout(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + int timeout; + + timeout=atoi(arg); + if (timeout<1) { + return "Proxy Timeout must be at least 1 second."; + } + psf->timeout_set=1; + psf->timeout=apr_time_from_sec(timeout); + + return NULL; +} + +static const char* + set_via_opt(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (strcasecmp(arg, "Off") == 0) + psf->viaopt = via_off; + else if (strcasecmp(arg, "On") == 0) + psf->viaopt = via_on; + else if (strcasecmp(arg, "Block") == 0) + psf->viaopt = via_block; + else if (strcasecmp(arg, "Full") == 0) + psf->viaopt = via_full; + else { + return "ProxyVia must be one of: " + "off | on | full | block"; + } + + psf->viaopt_set = 1; + return NULL; +} + +static const char* + set_bad_opt(cmd_parms *parms, void *dummy, const char *arg) +{ + proxy_server_conf *psf = + ap_get_module_config(parms->server->module_config, &proxy_module); + + if (strcasecmp(arg, "IsError") == 0) + psf->badopt = bad_error; + else if (strcasecmp(arg, "Ignore") == 0) + psf->badopt = bad_ignore; + else if (strcasecmp(arg, "StartBody") == 0) + psf->badopt = bad_body; + else { + return "ProxyBadHeader must be one of: " + "IsError | Ignore | StartBody"; + } + + psf->badopt_set = 1; + return NULL; +} + +static const char* set_ftp_directory_charset(cmd_parms *cmd, void *dconf, + const char *arg) +{ + proxy_dir_conf *conf = dconf; + + conf->ftp_directory_charset = arg; + return NULL; +} + +static void ap_add_per_proxy_conf(server_rec *s, ap_conf_vector_t *dir_config) +{ + proxy_server_conf *sconf = ap_get_module_config(s->module_config, + &proxy_module); + void **new_space = (void **)apr_array_push(sconf->sec_proxy); + + *new_space = dir_config; +} + +static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg) +{ + const char *errmsg; + const char *endp = ap_strrchr_c(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + proxy_dir_conf *conf; + ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool); + regex_t *r = NULL; + const command_rec *thiscmd = cmd->cmd; + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, + "> directive missing closing '>'", NULL); + } + + arg=apr_pstrndup(cmd->pool, arg, endp-arg); + + if (!arg) { + if (thiscmd->cmd_data) + return "<ProxyMatch > block must specify a path"; + else + return "<Proxy > block must specify a path"; + } + + cmd->path = ap_getword_conf(cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + if (!strncasecmp(cmd->path, "proxy:", 6)) + cmd->path += 6; + + /* XXX Ignore case? What if we proxy a case-insensitive server?!? + * While we are at it, shouldn't we also canonicalize the entire + * scheme? See proxy_fixup() + */ + if (thiscmd->cmd_data) { /* <ProxyMatch> */ + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + if (!r) { + return "Regex could not be compiled"; + } + } + else if (!strcmp(cmd->path, "~")) { + cmd->path = ap_getword_conf(cmd->pool, &arg); + if (!cmd->path) + return "<Proxy ~ > block must specify a path"; + if (strncasecmp(cmd->path, "proxy:", 6)) + cmd->path += 6; + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + if (!r) { + return "Regex could not be compiled"; + } + } + + /* initialize our config and fetch it */ + conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path, + &proxy_module, cmd->pool); + + errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf); + if (errmsg != NULL) + return errmsg; + + conf->r = r; + conf->p = cmd->path; + conf->p_is_fnmatch = apr_fnmatch_test(conf->p); + + ap_add_per_proxy_conf(cmd->server, new_dir_conf); + + if (*arg != '\0') { + return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name, + "> arguments not (yet) supported.", NULL); + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static const command_rec proxy_cmds[] = +{ + AP_INIT_RAW_ARGS("<Proxy", proxysection, NULL, RSRC_CONF, + "Container for directives affecting resources located in the proxied " + "location"), + AP_INIT_RAW_ARGS("<ProxyMatch", proxysection, (void*)1, RSRC_CONF, + "Container for directives affecting resources located in the proxied " + "location, in regular expression syntax"), + AP_INIT_FLAG("ProxyRequests", set_proxy_req, NULL, RSRC_CONF, + "on if the true proxy requests should be accepted"), + AP_INIT_TAKE2("ProxyRemote", add_proxy_noregex, NULL, RSRC_CONF, + "a scheme, partial URL or '*' and a proxy server"), + AP_INIT_TAKE2("ProxyRemoteMatch", add_proxy_regex, NULL, RSRC_CONF, + "a regex pattern and a proxy server"), + AP_INIT_TAKE12("ProxyPass", add_pass, NULL, RSRC_CONF|ACCESS_CONF, + "a virtual path and a URL"), + AP_INIT_TAKE12("ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF|ACCESS_CONF, + "a virtual path and a URL for reverse proxy behaviour"), + AP_INIT_ITERATE("ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, + "A list of names, hosts or domains to which the proxy will not connect"), + AP_INIT_TAKE1("ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, + "Receive buffer size for outgoing HTTP and FTP connections in bytes"), + AP_INIT_TAKE1("ProxyIOBufferSize", set_io_buffer_size, NULL, RSRC_CONF, + "IO buffer size for outgoing HTTP and FTP connections in bytes"), + AP_INIT_TAKE1("ProxyMaxForwards", set_max_forwards, NULL, RSRC_CONF, + "The maximum number of proxies a request may be forwarded through."), + AP_INIT_ITERATE("NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, + "A list of domains, hosts, or subnets to which the proxy will connect directly"), + AP_INIT_TAKE1("ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, + "The default intranet domain name (in absence of a domain in the URL)"), + AP_INIT_ITERATE("AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, + "A list of ports which CONNECT may connect to"), + AP_INIT_TAKE1("ProxyVia", set_via_opt, NULL, RSRC_CONF, + "Configure Via: proxy header header to one of: on | off | block | full"), + AP_INIT_FLAG("ProxyErrorOverride", set_proxy_error_override, NULL, RSRC_CONF, + "use our error handling pages instead of the servers' we are proxying"), + AP_INIT_FLAG("ProxyPreserveHost", set_preserve_host, NULL, RSRC_CONF, + "on if we should preserve host header while proxying"), + AP_INIT_TAKE1("ProxyTimeout", set_proxy_timeout, NULL, RSRC_CONF, + "Set the timeout (in seconds) for a proxied connection. " + "This overrides the server timeout"), + AP_INIT_TAKE1("ProxyBadHeader", set_bad_opt, NULL, RSRC_CONF, + "How to handle bad header line in response: IsError | Ignore | StartBody"), + AP_INIT_TAKE1("ProxyFtpDirCharset", set_ftp_directory_charset, NULL, + RSRC_CONF|ACCESS_CONF, "Define the character set for proxied FTP listings"), + {NULL} +}; + +APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *)); +APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *)); + +static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *proxy_ssl_enable = NULL; +static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *proxy_ssl_disable = NULL; + +PROXY_DECLARE(int) ap_proxy_ssl_enable(conn_rec *c) +{ + /* + * if c == NULL just check if the optional function was imported + * else run the optional function so ssl filters are inserted + */ + if (proxy_ssl_enable) { + return c ? proxy_ssl_enable(c) : 1; + } + + return 0; +} + +PROXY_DECLARE(int) ap_proxy_ssl_disable(conn_rec *c) +{ + if (proxy_ssl_disable) { + return proxy_ssl_disable(c); + } + + return 0; +} + +static int proxy_post_config(apr_pool_t *pconf, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + proxy_ssl_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable); + proxy_ssl_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable); + + return OK; +} + +static void register_hooks(apr_pool_t *p) +{ + /* fixup before mod_rewrite, so that the proxied url will not + * escaped accidentally by our fixup. + */ + static const char * const aszSucc[]={ "mod_rewrite.c", NULL }; + + /* handler */ + ap_hook_handler(proxy_handler, NULL, NULL, APR_HOOK_FIRST); + /* filename-to-URI translation */ + ap_hook_translate_name(proxy_trans, NULL, NULL, APR_HOOK_FIRST); + /* walk <Proxy > entries and suppress default TRACE behavior */ + ap_hook_map_to_storage(proxy_map_location, NULL,NULL, APR_HOOK_FIRST); + /* fixups */ + ap_hook_fixups(proxy_fixup, NULL, aszSucc, APR_HOOK_FIRST); + /* post read_request handling */ + ap_hook_post_read_request(proxy_detect, NULL, NULL, APR_HOOK_FIRST); + /* post config handling */ + ap_hook_post_config(proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE); +} + +module AP_MODULE_DECLARE_DATA proxy_module = +{ + STANDARD20_MODULE_STUFF, + create_proxy_dir_config, /* create per-directory config structure */ + merge_proxy_dir_config, /* merge per-directory config structures */ + create_proxy_config, /* create per-server config structure */ + merge_proxy_config, /* merge per-server config structures */ + proxy_cmds, /* command table */ + register_hooks +}; + +APR_HOOK_STRUCT( + APR_HOOK_LINK(scheme_handler) + APR_HOOK_LINK(canon_handler) +) + +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, scheme_handler, + (request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyhost, + apr_port_t proxyport),(r,conf,url, + proxyhost,proxyport),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(proxy, PROXY, int, canon_handler, + (request_rec *r, char *url),(r, + url),DECLINED) +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(proxy, PROXY, int, fixups, + (request_rec *r), (r), + OK, DECLINED) diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.dsp b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.dsp new file mode 100644 index 00000000..9fa9feb0 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.dsp @@ -0,0 +1,140 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy.mak" CFG="mod_proxy - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fd"Release\mod_proxy_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:"Release/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "PROXY_DECLARE_EXPORT" /Fd"Debug\mod_proxy_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy.so + +!ENDIF + +# Begin Target + +# Name "mod_proxy - Win32 Release" +# Name "mod_proxy - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\mod_proxy.c +# End Source File +# Begin Source File + +SOURCE=.\proxy_util.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_proxy - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy.so "proxy_module for Apache" ../../include/ap_release.h > .\mod_proxy.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_proxy - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy.so "proxy_module for Apache" ../../include/ap_release.h > .\mod_proxy.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.h b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.h new file mode 100644 index 00000000..d1ed7d46 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy.h @@ -0,0 +1,255 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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_fnmatch.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 { + 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; + 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; + apr_interval_time_t timeout_set; + enum { + bad_error, + bad_ignore, + bad_body + } badopt; /* how to deal with bad headers */ + char badopt_set; + +} 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? */ + const char *ftp_directory_charset; +} proxy_dir_conf; + +typedef struct { + conn_rec *connection; + char *hostname; + apr_port_t port; + int is_ssl; +} proxy_conn_rec; + +typedef struct { + float cache_completion; /* completion percentage */ + int content_length; /* length of the content */ +} proxy_completion; + + +/* 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_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)) + +/* 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(apr_table_t *)ap_proxy_read_headers(request_rec *r, request_rec *rp, char *buffer, int size, conn_rec *c); +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); +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); + +/* For proxy_util */ +extern module AP_MODULE_DECLARE_DATA proxy_module; + +#endif /*MOD_PROXY_H*/ diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_connect.dsp b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_connect.dsp new file mode 100644 index 00000000..0c2a12b0 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_connect.dsp @@ -0,0 +1,136 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_connect" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_connect - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_connect.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_connect.mak" CFG="mod_proxy_connect - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_connect - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_connect - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_connect_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:"Release/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_connect_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy_connect.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_connect.so + +!ENDIF + +# Begin Target + +# Name "mod_proxy_connect - Win32 Release" +# Name "mod_proxy_connect - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\proxy_connect.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_proxy_connect - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy_connect.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy_connect.so "proxy_connect_module for Apache" ../../include/ap_release.h > .\mod_proxy_connect.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_proxy_connect - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy_connect.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy_connect.so "proxy_connect_module for Apache" ../../include/ap_release.h > .\mod_proxy_connect.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_ftp.dsp b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_ftp.dsp new file mode 100644 index 00000000..3dfe0b7e --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_ftp.dsp @@ -0,0 +1,136 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_ftp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_ftp - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ftp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_ftp.mak" CFG="mod_proxy_ftp - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_ftp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_ftp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_ftp_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:"Release/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_ftp_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy_ftp.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_ftp.so + +!ENDIF + +# Begin Target + +# Name "mod_proxy_ftp - Win32 Release" +# Name "mod_proxy_ftp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\proxy_ftp.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_proxy_ftp - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy_ftp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy_ftp.so "proxy_ftp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ftp.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_proxy_ftp - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy_ftp.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy_ftp.so "proxy_ftp_module for Apache" ../../include/ap_release.h > .\mod_proxy_ftp.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_http.dsp b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_http.dsp new file mode 100644 index 00000000..d8f29006 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/mod_proxy_http.dsp @@ -0,0 +1,136 @@ +# Microsoft Developer Studio Project File - Name="mod_proxy_http" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_proxy_http - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_http.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_proxy_http.mak" CFG="mod_proxy_http - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_proxy_http - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_proxy_http - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_proxy_http_src" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /out:"Release/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Release/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so /opt:ref + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_proxy_http_src" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so +# ADD LINK32 kernel32.lib ws2_32.lib mswsock.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:"Debug/mod_proxy_http.so" /base:@..\..\os\win32\BaseAddr.ref,mod_proxy_http.so + +!ENDIF + +# Begin Target + +# Name "mod_proxy_http - Win32 Release" +# Name "mod_proxy_http - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\proxy_http.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter ".h" +# Begin Source File + +SOURCE=.\mod_proxy.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_proxy_http - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy_http.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy_http.so "proxy_http_module for Apache" ../../include/ap_release.h > .\mod_proxy_http.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_proxy_http - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_proxy_http.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_proxy_http.so "proxy_http_module for Apache" ../../include/ap_release.h > .\mod_proxy_http.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/modules.mk b/rubbos/app/httpd-2.0.64/modules/proxy/modules.mk new file mode 100644 index 00000000..ceb52a1b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/modules.mk @@ -0,0 +1,3 @@ +DISTCLEAN_TARGETS = modules.mk +static = +shared = diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/proxy_connect.c b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_connect.c new file mode 100644 index 00000000..20e40ebb --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_connect.c @@ -0,0 +1,377 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* CONNECT method for Apache proxy */ + +#define CORE_PRIVATE + +#include "mod_proxy.h" +#include "apr_poll.h" + +module AP_MODULE_DECLARE_DATA proxy_connect_module; + +int ap_proxy_connect_canon(request_rec *r, char *url); +int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport); + +/* + * This handles Netscape CONNECT method secure proxy requests. + * A connection is opened to the specified host and data is + * passed through between the WWW site and the browser. + * + * This code is based on the INTERNET-DRAFT document + * "Tunneling SSL Through a WWW Proxy" currently at + * http://www.mcom.com/newsref/std/tunneling_ssl.html. + * + * If proxyhost and proxyport are set, we send a CONNECT to + * the specified proxy.. + * + * FIXME: this doesn't log the number of bytes sent, but + * that may be okay, since the data is supposed to + * be transparent. In fact, this doesn't log at all + * yet. 8^) + * FIXME: doesn't check any headers initally sent from the + * client. + * FIXME: should allow authentication, but hopefully the + * generic proxy authentication is good enough. + * FIXME: no check for r->assbackwards, whatever that is. + */ + +static int +allowed_port(proxy_server_conf *conf, int port) +{ + int i; + int *list = (int *) conf->allowed_connect_ports->elts; + + for(i = 0; i < conf->allowed_connect_ports->nelts; i++) { + if(port == list[i]) + return 1; + } + return 0; +} + +/* canonicalise CONNECT URLs. */ +int ap_proxy_connect_canon(request_rec *r, char *url) +{ + + if (r->method_number != M_CONNECT) { + return DECLINED; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: canonicalising URL %s", url); + + return OK; +} + +/* CONNECT handler */ +int ap_proxy_connect_handler(request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + apr_pool_t *p = r->pool; + apr_socket_t *sock; + apr_status_t err, rv; + apr_size_t i, o, nbytes; + char buffer[HUGE_STRING_LEN]; + apr_socket_t *client_socket = ap_get_module_config(r->connection->conn_config, &core_module); + int failed; + apr_pollfd_t *pollfd; + apr_int32_t pollcnt; + apr_int16_t pollevent; + apr_sockaddr_t *uri_addr, *connect_addr; + + apr_uri_t uri; + const char *connectname; + int connectport = 0; + + /* is this for us? */ + if (r->method_number != M_CONNECT) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: declining URL %s", url); + return DECLINED; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: serving URL %s", url); + + + /* + * Step One: Determine Who To Connect To + * + * 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_hostinfo(p, url, &uri)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_pstrcat(p, "URI cannot be parsed: ", url, NULL)); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: connecting %s to %s:%d", url, uri.hostname, uri.port); + + /* do a DNS lookup for the destination host */ + err = apr_sockaddr_info_get(&uri_addr, uri.hostname, APR_UNSPEC, uri.port, 0, p); + + /* are we connecting directly, or via a proxy? */ + if (proxyname) { + connectname = proxyname; + connectport = proxyport; + err = apr_sockaddr_info_get(&connect_addr, proxyname, APR_UNSPEC, proxyport, 0, p); + } + else { + connectname = uri.hostname; + connectport = uri.port; + connect_addr = uri_addr; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: connecting to remote proxy %s on port %d", connectname, connectport); + + /* check if ProxyBlock directive on this host */ + if (OK != ap_proxy_checkproxyblock(r, conf, uri_addr)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + /* Check if it is an allowed port */ + if (conf->allowed_connect_ports->nelts == 0) { + /* Default setting if not overridden by AllowCONNECT */ + switch (uri.port) { + case APR_URI_HTTPS_DEFAULT_PORT: + case APR_URI_SNEWS_DEFAULT_PORT: + break; + default: + /* XXX can we call ap_proxyerror() here to get a nice log message? */ + return HTTP_FORBIDDEN; + } + } else if(!allowed_port(conf, uri.port)) { + /* XXX can we call ap_proxyerror() here to get a nice log message? */ + return HTTP_FORBIDDEN; + } + + /* + * Step Two: Make the Connection + * + * We have determined who to connect to. Now make the connection. + */ + + /* get all the possible IP addresses for the destname and loop through them + * until we get a successful connection + */ + if (APR_SUCCESS != err) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, + "DNS lookup failure for: ", + connectname, NULL)); + } + + /* + * At this point we have a list of one or more IP addresses of + * the machine to connect to. If configured, reorder this + * list so that the "best candidate" is first try. "best + * candidate" could mean the least loaded server, the fastest + * responding server, whatever. + * + * For now we do nothing, ie we get DNS round robin. + * XXX FIXME + */ + failed = ap_proxy_connect_to_backend(&sock, "CONNECT", connect_addr, + connectname, conf, r->server, + r->pool); + + /* handle a permanent error from the above loop */ + if (failed) { + if (proxyname) { + return DECLINED; + } + else { + return HTTP_BAD_GATEWAY; + } + } + + /* + * Step Three: Send the Request + * + * Send the HTTP/1.1 CONNECT request to the remote server + */ + + /* we are acting as a tunnel - the output filter stack should + * be completely empty, because when we are done here we are done completely. + * We add the NULL filter to the stack to do this... + */ + r->output_filters = NULL; + r->connection->output_filters = NULL; + + + /* If we are connecting through a remote proxy, we need to pass + * the CONNECT request on to it. + */ + if (proxyport) { + /* FIXME: Error checking ignored. + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: sending the CONNECT request to the remote proxy"); + nbytes = apr_snprintf(buffer, sizeof(buffer), + "CONNECT %s HTTP/1.0" CRLF, r->uri); + apr_send(sock, buffer, &nbytes); + nbytes = apr_snprintf(buffer, sizeof(buffer), + "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + apr_send(sock, buffer, &nbytes); + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: Returning 200 OK Status"); + nbytes = apr_snprintf(buffer, sizeof(buffer), + "HTTP/1.0 200 Connection Established" CRLF); + ap_xlate_proto_to_ascii(buffer, nbytes); + apr_send(client_socket, buffer, &nbytes); + nbytes = apr_snprintf(buffer, sizeof(buffer), + "Proxy-agent: %s" CRLF CRLF, ap_get_server_version()); + ap_xlate_proto_to_ascii(buffer, nbytes); + apr_send(client_socket, buffer, &nbytes); +#if 0 + /* This is safer code, but it doesn't work yet. I'm leaving it + * here so that I can fix it later. + */ + r->status = HTTP_OK; + r->header_only = 1; + apr_table_set(r->headers_out, "Proxy-agent: %s", ap_get_server_version()); + ap_rflush(r); +#endif + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: setting up poll()"); + + /* + * Step Four: Handle Data Transfer + * + * Handle two way transfer of data over the socket (this is a tunnel). + */ + +/* r->sent_bodyct = 1;*/ + + if((rv = apr_poll_setup(&pollfd, 2, r->pool)) != APR_SUCCESS) + { + apr_socket_close(sock); + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: CONNECT: error apr_poll_setup()"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Add client side to the poll */ + apr_poll_socket_add(pollfd, client_socket, APR_POLLIN); + + /* Add the server side to the poll */ + apr_poll_socket_add(pollfd, sock, APR_POLLIN); + + while (1) { /* Infinite loop until error (one side closes the connection) */ +/* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: CONNECT: going to sleep (poll)");*/ + if ((rv = apr_poll(pollfd, 2, &pollcnt, -1)) != APR_SUCCESS) + { + apr_socket_close(sock); + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "proxy: CONNECT: error apr_poll()"); + return HTTP_INTERNAL_SERVER_ERROR; + } +/* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: woke from select(), i=%d", pollcnt);*/ + + if (pollcnt) { + apr_poll_revents_get(&pollevent, sock, pollfd); + if (pollevent & APR_POLLIN) { +/* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: sock was set");*/ + nbytes = sizeof(buffer); + if (apr_recv(sock, buffer, &nbytes) == APR_SUCCESS) { + o = 0; + i = nbytes; + while(i > 0) + { + nbytes = i; + /* This is just plain wrong. No module should ever write directly + * to the client. For now, this works, but this is high on my list of + * things to fix. The correct line is: + * if ((nbytes = ap_rwrite(buffer + o, nbytes, r)) < 0) + * rbb + */ + if (apr_send(client_socket, buffer + o, &nbytes) != APR_SUCCESS) + break; + o += nbytes; + i -= nbytes; + } + } + else + break; + } + else if ((pollevent & APR_POLLERR) || (pollevent & APR_POLLHUP)) + break; + + + apr_poll_revents_get(&pollevent, client_socket, pollfd); + if (pollevent & APR_POLLIN) { +/* ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: client was set");*/ + nbytes = sizeof(buffer); + if (apr_recv(client_socket, buffer, &nbytes) == APR_SUCCESS) { + o = 0; + i = nbytes; + while(i > 0) + { + nbytes = i; + if (apr_send(sock, buffer + o, &nbytes) != APR_SUCCESS) + break; + o += nbytes; + i -= nbytes; + } + } + else + break; + } + else if ((pollevent & APR_POLLERR) || (pollevent & APR_POLLHUP)) + break; + } + else + break; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: CONNECT: finished with poll() - cleaning up"); + + /* + * Step Five: Clean Up + * + * Close the socket and clean up + */ + + apr_socket_close(sock); + + return OK; +} + +static void ap_proxy_connect_register_hook(apr_pool_t *p) +{ + proxy_hook_scheme_handler(ap_proxy_connect_handler, NULL, NULL, APR_HOOK_MIDDLE); + proxy_hook_canon_handler(ap_proxy_connect_canon, NULL, NULL, APR_HOOK_MIDDLE); +} + +module AP_MODULE_DECLARE_DATA proxy_connect_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_connect_register_hook /* register hooks */ +}; diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/proxy_ftp.c b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_ftp.c new file mode 100644 index 00000000..cbbf23c9 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_ftp.c @@ -0,0 +1,1936 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* FTP routines for Apache proxy */ + +#include "mod_proxy.h" +#if APR_HAVE_TIME_H +#include <time.h> +#endif + +#define AUTODETECT_PWD +/* Automatic timestamping (Last-Modified header) based on MDTM is used if: + * 1) the FTP server supports the MDTM command and + * 2) HAVE_TIMEGM (preferred) or HAVE_GMTOFF is available at compile time + */ +#define USE_MDTM + + +module AP_MODULE_DECLARE_DATA proxy_ftp_module; + +int ap_proxy_ftp_canon(request_rec *r, char *url); +int ap_proxy_ftp_handler(request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyhost, + apr_port_t proxyport); +apr_status_t ap_proxy_send_dir_filter(ap_filter_t * f, + apr_bucket_brigade *bb); + + +/* + * Decodes a '%' escaped string, and returns the number of characters + */ +static int decodeenc(char *x) +{ + int i, j, ch; + + if (x[0] == '\0') + return 0; /* special case for no characters */ + for (i = 0, j = 0; x[i] != '\0'; i++, j++) { + /* decode it if not already done */ + ch = x[i]; + if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } + x[j] = ch; + } + x[j] = '\0'; + return j; +} + +/* + * Escape the globbing characters in a path used as argument to + * the FTP commands (SIZE, CWD, RETR, MDTM, ...). + * ftpd assumes '\\' as a quoting character to escape special characters. + * Returns: escaped string + */ +#define FTP_GLOBBING_CHARS "*?[{~" +static char *ftp_escape_globbingchars(apr_pool_t *p, const char *path) +{ + char *ret = apr_palloc(p, 2*strlen(path)+sizeof("")); + char *d; + for (d = ret; *path; ++path) { + if (strchr(FTP_GLOBBING_CHARS, *path) != NULL) + *d++ = '\\'; + *d++ = *path; + } + *d = '\0'; + return ret; +} + +/* + * Check for globbing characters in a path used as argument to + * the FTP commands (SIZE, CWD, RETR, MDTM, ...). + * ftpd assumes '\\' as a quoting character to escape special characters. + * Returns: 0 (no globbing chars, or all globbing chars escaped), 1 (globbing chars) + */ +static int ftp_check_globbingchars(const char *path) +{ + for ( ; *path; ++path) { + if (*path == '\\') + ++path; + if (*path != '\0' && strchr(FTP_GLOBBING_CHARS, *path) != NULL) + return TRUE; + } + return FALSE; +} + +/* + * checks an encoded ftp string for bad characters, namely, CR, LF or + * non-ascii character + */ +static int ftp_check_string(const char *x) +{ + int i, ch = 0; +#if APR_CHARSET_EBCDIC + char buf[1]; +#endif + + for (i = 0; x[i] != '\0'; i++) { + ch = x[i]; + if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) { + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + } +#if !APR_CHARSET_EBCDIC + if (ch == '\015' || ch == '\012' || (ch & 0x80)) +#else /* APR_CHARSET_EBCDIC */ + if (ch == '\r' || ch == '\n') + return 0; + buf[0] = ch; + ap_xlate_proto_to_ascii(buf, 1); + if (buf[0] & 0x80) +#endif /* APR_CHARSET_EBCDIC */ + return 0; + } + return 1; +} + +/* + * Canonicalise ftp URLs. + */ +int ap_proxy_ftp_canon(request_rec *r, char *url) +{ + char *user, *password, *host, *path, *parms, *strp, sport[7]; + apr_pool_t *p = r->pool; + const char *err; + apr_port_t port, def_port; + + /* */ + if (strncasecmp(url, "ftp:", 4) == 0) { + url += 4; + } + else { + return DECLINED; + } + def_port = apr_uri_port_of_scheme("ftp"); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: canonicalising URL %s", url); + + port = def_port; + err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port); + if (err) + return HTTP_BAD_REQUEST; + if (user != NULL && !ftp_check_string(user)) + return HTTP_BAD_REQUEST; + if (password != NULL && !ftp_check_string(password)) + return HTTP_BAD_REQUEST; + + /* now parse path/parameters args, according to rfc1738 */ + /* + * N.B. if this isn't a true proxy request, then the URL path (but not + * query args) has already been decoded. This gives rise to the problem + * of a ; being decoded into the path. + */ + strp = strchr(url, ';'); + if (strp != NULL) { + *(strp++) = '\0'; + parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, + r->proxyreq); + if (parms == NULL) + return HTTP_BAD_REQUEST; + } + else + parms = ""; + + path = ap_proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) + return HTTP_BAD_REQUEST; + if (!ftp_check_string(path)) + return HTTP_BAD_REQUEST; + + if (r->proxyreq && r->args != NULL) { + if (strp != NULL) { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1); + if (strp == NULL) + return HTTP_BAD_REQUEST; + parms = apr_pstrcat(p, parms, "?", strp, NULL); + } + else { + strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1); + if (strp == NULL) + return HTTP_BAD_REQUEST; + path = apr_pstrcat(p, path, "?", strp, NULL); + } + r->args = NULL; + } + +/* now, rebuild URL */ + + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ + host = apr_pstrcat(p, "[", host, "]", NULL); + } + r->filename = apr_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "", + (password != NULL) ? ":" : "", + (password != NULL) ? password : "", + (user != NULL) ? "@" : "", host, sport, "/", path, + (parms[0] != '\0') ? ";" : "", parms, NULL); + + return OK; +} + +/* we chop lines longer than 80 characters */ +#define MAX_LINE_LEN 80 + +/* + * Reads response lines, returns both the ftp status code and + * remembers the response message in the supplied buffer + */ +static int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbuf, int msglen) +{ + int status; + char response[MAX_LINE_LEN]; + char buff[5]; + char *mb = msgbuf, *me = &msgbuf[msglen]; + apr_status_t rv; + int eos; + + if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { + return -1; + } +/* + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "proxy: <FTP: %s", response); +*/ + if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) || + !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-')) + status = 0; + else + status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0'; + + mb = apr_cpystrn(mb, response + 4, me - mb); + + if (response[3] == '-') { + memcpy(buff, response, 3); + buff[3] = ' '; + do { + if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) { + return -1; + } + mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb); + } while (memcmp(response, buff, 4) != 0); + } + + return status; +} + +/* this is a filter that turns a raw ASCII directory listing into pretty HTML */ + +/* ideally, mod_proxy should simply send the raw directory list up the filter + * stack to mod_autoindex, which in theory should turn the raw ascii into + * pretty html along with all the bells and whistles it provides... + * + * all in good time...! :) + */ + +typedef struct { + apr_bucket_brigade *in; + char buffer[MAX_STRING_LEN]; + enum { + HEADER, BODY, FOOTER + } state; +} proxy_dir_ctx_t; + +/* fallback regex for ls -s1; ($0..$2) == 3 */ +#define LS_REG_PATTERN "^ *([0-9]+) +([^ ]+)$" +#define LS_REG_MATCH 3 + +apr_status_t ap_proxy_send_dir_filter(ap_filter_t *f, apr_bucket_brigade *in) +{ + request_rec *r = f->r; + conn_rec *c = r->connection; + apr_pool_t *p = r->pool; + apr_bucket_brigade *out = apr_brigade_create(p, c->bucket_alloc); + apr_status_t rv; + + register int n; + char *dir, *path, *reldir, *site, *str, *type; + + const char *pwd = apr_table_get(r->notes, "Directory-PWD"); + const char *readme = apr_table_get(r->notes, "Directory-README"); + + proxy_dir_ctx_t *ctx = f->ctx; + + if (!ctx) { + f->ctx = ctx = apr_pcalloc(p, sizeof(*ctx)); + ctx->in = apr_brigade_create(p, c->bucket_alloc); + ctx->buffer[0] = 0; + ctx->state = HEADER; + } + + /* combine the stored and the new */ + APR_BRIGADE_CONCAT(ctx->in, in); + + if (HEADER == ctx->state) { + + /* basedir is either "", or "/%2f" for the "squid %2f hack" */ + const char *basedir = ""; /* By default, path is relative to the $HOME dir */ + char *wildcard = NULL; + + /* Save "scheme://site" prefix without password */ + site = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO); + /* ... and path without query args */ + path = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITSITEPART | APR_URI_UNP_OMITQUERY); + + /* If path began with /%2f, change the basedir */ + if (strncasecmp(path, "/%2f", 4) == 0) { + basedir = "/%2f"; + } + + /* Strip off a type qualifier. It is ignored for dir listings */ + if ((type = strstr(path, ";type=")) != NULL) + *type++ = '\0'; + + (void)decodeenc(path); + + while (path[1] == '/') /* collapse multiple leading slashes to one */ + ++path; + + reldir = strrchr(path, '/'); + if (reldir != NULL && ftp_check_globbingchars(reldir)) { + wildcard = &reldir[1]; + reldir[0] = '\0'; /* strip off the wildcard suffix */ + } + + /* Copy path, strip (all except the last) trailing slashes */ + /* (the trailing slash is needed for the dir component loop below) */ + path = dir = apr_pstrcat(p, path, "/", NULL); + for (n = strlen(path); n > 1 && path[n - 1] == '/' && path[n - 2] == '/'; --n) + path[n - 1] = '\0'; + + /* Add a link to the root directory (if %2f hack was used) */ + str = (basedir[0] != '\0') ? "<a href=\"/%2f/\">%2f</a>/" : ""; + + /* print "ftp://host/" */ + str = apr_psprintf(p, DOCTYPE_HTML_3_2 + "<html>\n <head>\n <title>%s%s%s</title>\n" + " </head>\n" + " <body>\n <h2>Directory of " + "<a href=\"/\">%s</a>/%s", + site, basedir, ap_escape_html(p, path), + site, str); + + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), + p, c->bucket_alloc)); + + for (dir = path+1; (dir = strchr(dir, '/')) != NULL; ) + { + *dir = '\0'; + if ((reldir = strrchr(path+1, '/'))==NULL) { + reldir = path+1; + } + else + ++reldir; + /* print "path/" component */ + str = apr_psprintf(p, "<a href=\"%s%s/\">%s</a>/", basedir, + ap_escape_uri(p, path), + ap_escape_html(p, reldir)); + *dir = '/'; + while (*dir == '/') + ++dir; + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, + strlen(str), p, + c->bucket_alloc)); + } + if (wildcard != NULL) { + wildcard = ap_escape_html(p, wildcard); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(wildcard, + strlen(wildcard), p, + c->bucket_alloc)); + } + + /* If the caller has determined the current directory, and it differs */ + /* from what the client requested, then show the real name */ + if (pwd == NULL || strncmp(pwd, path, strlen(pwd)) == 0) { + str = apr_psprintf(p, "</h2>\n\n <hr />\n\n<pre>"); + } + else { + str = apr_psprintf(p, "</h2>\n\n(%s)\n\n <hr />\n\n<pre>", + ap_escape_html(p, pwd)); + } + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), + p, c->bucket_alloc)); + + /* print README */ + if (readme) { + str = apr_psprintf(p, "%s\n</pre>\n\n<hr />\n\n<pre>\n", + ap_escape_html(p, readme)); + + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, + strlen(str), p, + c->bucket_alloc)); + } + + /* make sure page intro gets sent out */ + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc)); + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_cleanup(out); + + ctx->state = BODY; + } + + /* loop through each line of directory */ + while (BODY == ctx->state) { + char *filename; + int found = 0; + int eos = 0; + + regex_t *re = NULL; + regmatch_t re_result[LS_REG_MATCH]; + + /* Compile the output format of "ls -s1" as a fallback for non-unix ftp listings */ + re = ap_pregcomp(p, LS_REG_PATTERN, REG_EXTENDED); + ap_assert(re != NULL); + + /* get a complete line */ + /* if the buffer overruns - throw data away */ + while (!found && !APR_BRIGADE_EMPTY(ctx->in)) { + char *pos, *response; + apr_size_t len, max; + apr_bucket *e; + + e = APR_BRIGADE_FIRST(ctx->in); + if (APR_BUCKET_IS_EOS(e)) { + eos = 1; + break; + } + if (APR_SUCCESS != (rv = apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ))) { + return rv; + } + pos = memchr(response, APR_ASCII_LF, len); + if (pos != NULL) { + if ((response + len) != (pos + 1)) { + len = pos - response + 1; + apr_bucket_split(e, pos - response + 1); + } + found = 1; + } + max = sizeof(ctx->buffer) - strlen(ctx->buffer) - 1; + if (len > max) { + len = max; + } + + /* len+1 to leave space for the trailing nil char */ + apr_cpystrn(ctx->buffer+strlen(ctx->buffer), response, len+1); + + APR_BUCKET_REMOVE(e); + apr_bucket_destroy(e); + } + + /* EOS? jump to footer */ + if (eos) { + ctx->state = FOOTER; + break; + } + + /* not complete? leave and try get some more */ + if (!found) { + return APR_SUCCESS; + } + + { + apr_size_t n = strlen(ctx->buffer); + if (ctx->buffer[n-1] == CRLF[1]) /* strip trailing '\n' */ + ctx->buffer[--n] = '\0'; + if (ctx->buffer[n-1] == CRLF[0]) /* strip trailing '\r' if present */ + ctx->buffer[--n] = '\0'; + } + + /* a symlink? */ + if (ctx->buffer[0] == 'l' && (filename = strstr(ctx->buffer, " -> ")) != NULL) { + char *link_ptr = filename; + + do { + filename--; + } while (filename[0] != ' ' && filename > ctx->buffer); + if (filename > ctx->buffer) + *(filename++) = '\0'; + *(link_ptr++) = '\0'; + str = apr_psprintf(p, "%s <a href=\"%s\">%s %s</a>\n", + ap_escape_html(p, ctx->buffer), + ap_escape_uri(p, filename), + ap_escape_html(p, filename), + ap_escape_html(p, link_ptr)); + } + + /* a directory/file? */ + else if (ctx->buffer[0] == 'd' || ctx->buffer[0] == '-' || ctx->buffer[0] == 'l' || apr_isdigit(ctx->buffer[0])) { + int searchidx = 0; + char *searchptr = NULL; + int firstfile = 1; + if (apr_isdigit(ctx->buffer[0])) { /* handle DOS dir */ + searchptr = strchr(ctx->buffer, '<'); + if (searchptr != NULL) + *searchptr = '['; + searchptr = strchr(ctx->buffer, '>'); + if (searchptr != NULL) + *searchptr = ']'; + } + + filename = strrchr(ctx->buffer, ' '); + *(filename++) = '\0'; + + /* handle filenames with spaces in 'em */ + if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) { + firstfile = 0; + searchidx = filename - ctx->buffer; + } + else if (searchidx != 0 && ctx->buffer[searchidx] != 0) { + *(--filename) = ' '; + ctx->buffer[searchidx - 1] = '\0'; + filename = &ctx->buffer[searchidx]; + } + + /* Append a slash to the HREF link for directories */ + if (!strcmp(filename, ".") || !strcmp(filename, "..") || ctx->buffer[0] == 'd') { + str = apr_psprintf(p, "%s <a href=\"%s/\">%s</a>\n", + ap_escape_html(p, ctx->buffer), + ap_escape_uri(p, filename), + ap_escape_html(p, filename)); + } + else { + str = apr_psprintf(p, "%s <a href=\"%s\">%s</a>\n", + ap_escape_html(p, ctx->buffer), + ap_escape_uri(p, filename), + ap_escape_html(p, filename)); + } + } + /* Try a fallback for listings in the format of "ls -s1" */ + else if (0 == ap_regexec(re, ctx->buffer, LS_REG_MATCH, re_result, 0)) { + + filename = apr_pstrndup(p, &ctx->buffer[re_result[2].rm_so], re_result[2].rm_eo - re_result[2].rm_so); + + str = apr_pstrcat(p, ap_escape_html(p, apr_pstrndup(p, ctx->buffer, re_result[2].rm_so)), + "<a href=\"", ap_escape_uri(p, filename), "\">", + ap_escape_html(p, filename), "</a>\n", NULL); + } + else { + strcat(ctx->buffer, "\n"); /* re-append the newline */ + str = ap_escape_html(p, ctx->buffer); + } + + /* erase buffer for next time around */ + ctx->buffer[0] = 0; + + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p, + c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc)); + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_cleanup(out); + + } + + if (FOOTER == ctx->state) { + str = apr_psprintf(p, "</pre>\n\n <hr />\n\n %s\n\n </body>\n</html>\n", ap_psignature("", r)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p, + c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(out, apr_bucket_eos_create(c->bucket_alloc)); + if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) { + return rv; + } + apr_brigade_destroy(out); + } + + return APR_SUCCESS; +} + +/* Parse EPSV reply and return port, or zero on error. */ +static apr_port_t parse_epsv_reply(const char *reply) +{ + const char *p; + char *ep; + long port; + + /* Reply syntax per RFC 2428: "229 blah blah (|||port|)" where '|' + * can be any character in ASCII from 33-126, obscurely. Verify + * the syntax. */ + p = ap_strchr_c(reply, '('); + if (p == NULL || !p[1] || p[1] != p[2] || p[1] != p[3] + || p[4] == p[1]) { + return 0; + } + + errno = 0; + port = strtol(p + 4, &ep, 10); + if (errno || port < 1 || port > 65535 || ep[0] != p[1] || ep[1] != ')') { + return 0; + } + + return (apr_port_t)port; +} + +/* + * Generic "send FTP command to server" routine, using the control socket. + * Returns the FTP returncode (3 digit code) + * Allows for tracing the FTP protocol (in LogLevel debug) + */ +static int +proxy_ftp_command(const char *cmd, request_rec *r, conn_rec *ftp_ctrl, + apr_bucket_brigade *bb, char **pmessage) +{ + char *crlf; + int rc; + char message[HUGE_STRING_LEN]; + + /* If cmd == NULL, we retrieve the next ftp response line */ + if (cmd != NULL) { + conn_rec *c = r->connection; + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(cmd, strlen(cmd), r->pool, c->bucket_alloc)); + APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(c->bucket_alloc)); + ap_pass_brigade(ftp_ctrl->output_filters, bb); + + /* strip off the CRLF for logging */ + apr_cpystrn(message, cmd, sizeof(message)); + if ((crlf = strchr(message, '\r')) != NULL || + (crlf = strchr(message, '\n')) != NULL) + *crlf = '\0'; + if (strncmp(message,"PASS ", 5) == 0) + strcpy(&message[5], "****"); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy:>FTP: %s", message); + } + + rc = ftp_getrc_msg(ftp_ctrl, bb, message, sizeof message); + if (rc == -1 || rc == 421) + strcpy(message,"<unable to read result>"); + if ((crlf = strchr(message, '\r')) != NULL || + (crlf = strchr(message, '\n')) != NULL) + *crlf = '\0'; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy:<FTP: %3.3u %s", rc, message); + + if (pmessage != NULL) + *pmessage = apr_pstrdup(r->pool, message); + + return rc; +} + +/* Set ftp server to TYPE {A,I,E} before transfer of a directory or file */ +static int ftp_set_TYPE(char xfer_type, request_rec *r, conn_rec *ftp_ctrl, + apr_bucket_brigade *bb, char **pmessage) +{ + char old_type[2] = { 'A', '\0' }; /* After logon, mode is ASCII */ + int ret = HTTP_OK; + int rc; + + /* set desired type */ + old_type[0] = xfer_type; + + rc = proxy_ftp_command(apr_pstrcat(r->pool, "TYPE ", old_type, CRLF, NULL), + r, ftp_ctrl, bb, pmessage); +/* responses: 200, 421, 500, 501, 504, 530 */ + /* 200 Command okay. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 504 Command not implemented for that parameter. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + ret = ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + else if (rc != 200 && rc != 504) { + ret = ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Unable to set transfer type"); + } +/* Allow not implemented */ + else if (rc == 504) + /* ignore it silently */; + + return ret; +} + + +/* Return the current directory which we have selected on the FTP server, or NULL */ +static char *ftp_get_PWD(request_rec *r, conn_rec *ftp_ctrl, apr_bucket_brigade *bb) +{ + char *cwd = NULL; + char *ftpmessage = NULL; + + /* responses: 257, 500, 501, 502, 421, 550 */ + /* 257 "<directory-name>" <commentary> */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 550 Requested action not taken. */ + switch (proxy_ftp_command("PWD" CRLF, r, ftp_ctrl, bb, &ftpmessage)) { + case -1: + case 421: + case 550: + ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Failed to read PWD on ftp server"); + break; + + case 257: { + const char *dirp = ftpmessage; + cwd = ap_getword_conf(r->pool, &dirp); + } + } + return cwd; +} + + +/* Common routine for failed authorization (i.e., missing or wrong password) + * to an ftp service. This causes most browsers to retry the request + * with username and password (which was presumably queried from the user) + * supplied in the Authorization: header. + * Note that we "invent" a realm name which consists of the + * ftp://user@host part of the reqest (sans password -if supplied but invalid-) + */ +static int ftp_unauthorized(request_rec *r, int log_it) +{ + r->proxyreq = PROXYREQ_NONE; + /* + * Log failed requests if they supplied a password (log username/password + * guessing attempts) + */ + if (log_it) + ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, + "proxy: missing or failed auth to %s", + apr_uri_unparse(r->pool, + &r->parsed_uri, APR_URI_UNP_OMITPATHINFO)); + + apr_table_setn(r->err_headers_out, "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", + apr_uri_unparse(r->pool, &r->parsed_uri, + APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO), + "\"", NULL)); + + return HTTP_UNAUTHORIZED; +} + + +/* + * Handles direct access of ftp:// URLs + * Original (Non-PASV) version from + * Troy Morrison <spiffnet@zoom.com> + * PASV added by Chuck + * Filters by [Graham Leggett <minfrin@sharp.fm>] + */ +int ap_proxy_ftp_handler(request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyhost, + apr_port_t proxyport) +{ + apr_pool_t *p = r->pool; + conn_rec *c = r->connection; + proxy_conn_rec *backend; + apr_socket_t *sock, *local_sock, *data_sock = NULL; + apr_sockaddr_t *connect_addr; + apr_status_t rv; + conn_rec *origin, *data = NULL; + int err; + apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc); + char *buf, *connectname; + apr_port_t connectport; + char buffer[MAX_STRING_LEN]; + char *ftpmessage = NULL; + char *path, *strp, *type_suffix, *cwd = NULL; + apr_uri_t uri; + char *user = NULL; +/* char *account = NULL; how to supply an account in a URL? */ + const char *password = NULL; + int len, rc; + int one = 1; + char *size = NULL; + apr_socket_t *origin_sock = NULL; + char xfer_type = 'A'; /* after ftp login, the default is ASCII */ + int dirlisting = 0; +#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) + apr_time_t mtime = 0L; +#endif + + /* stuff for PASV mode */ + int connect = 0, use_port = 0; + char dates[APR_RFC822_DATE_LEN]; + + /* is this for us? */ + if (proxyhost) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: declining URL %s - proxyhost %s specified:", url, proxyhost); + return DECLINED; /* proxy connections are via HTTP */ + } + if (strncasecmp(url, "ftp:", 4)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: declining URL %s - not ftp:", url); + return DECLINED; /* only interested in FTP */ + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: serving URL %s", url); + + /* create space for state information */ + backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, &proxy_ftp_module); + if (!backend) { + backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec)); + backend->connection = NULL; + backend->hostname = NULL; + backend->port = 0; + ap_set_module_config(c->conn_config, &proxy_ftp_module, backend); + } + if (backend->connection) + origin_sock = ap_get_module_config(backend->connection->conn_config, &core_module); + + + /* + * I: Who Do I Connect To? ----------------------- + * + * Break up the URL to determine the host to connect to + */ + + /* we only support GET and HEAD */ + if (r->method_number != M_GET) + return HTTP_NOT_IMPLEMENTED; + + /* We break the URL into host, port, path-search */ + if (r->parsed_uri.hostname == NULL) { + if (APR_SUCCESS != apr_uri_parse(p, url, &uri)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + apr_psprintf(p, "URI cannot be parsed: %s", url)); + } + connectname = uri.hostname; + connectport = uri.port; + path = apr_pstrdup(p, uri.path); + } + else { + connectname = r->parsed_uri.hostname; + connectport = r->parsed_uri.port; + path = apr_pstrdup(p, r->parsed_uri.path); + } + if (connectport == 0) { + connectport = apr_uri_port_of_scheme("ftp"); + } + path = (path != NULL && path[0] != '\0') ? &path[1] : ""; + + type_suffix = strchr(path, ';'); + if (type_suffix != NULL) + *(type_suffix++) = '\0'; + + if (type_suffix != NULL && strncmp(type_suffix, "type=", 5) == 0 + && apr_isalpha(type_suffix[5])) { + /* "type=d" forces a dir listing. + * The other types (i|a|e) are directly used for the ftp TYPE command + */ + if ( ! (dirlisting = (apr_tolower(type_suffix[5]) == 'd'))) + xfer_type = apr_toupper(type_suffix[5]); + + /* Check valid types, rather than ignoring invalid types silently: */ + if (strchr("AEI", xfer_type) == NULL) + return ap_proxyerror(r, HTTP_BAD_REQUEST, apr_pstrcat(r->pool, + "ftp proxy supports only types 'a', 'i', or 'e': \"", + type_suffix, "\" is invalid.", NULL)); + } + else { + /* make binary transfers the default */ + xfer_type = 'I'; + } + + + /* + * The "Authorization:" header must be checked first. We allow the user + * to "override" the URL-coded user [ & password ] in the Browsers' + * User&Password Dialog. NOTE that this is only marginally more secure + * than having the password travel in plain as part of the URL, because + * Basic Auth simply uuencodes the plain text password. But chances are + * still smaller that the URL is logged regularly. + */ + if ((password = apr_table_get(r->headers_in, "Authorization")) != NULL + && strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0 + && (password = ap_pbase64decode(r->pool, password))[0] != ':') { + /* Check the decoded string for special characters. */ + if (!ftp_check_string(password)) { + return ap_proxyerror(r, HTTP_BAD_REQUEST, + "user credentials contained invalid character"); + } + /* + * Note that this allocation has to be made from r->connection->pool + * because it has the lifetime of the connection. The other + * allocations are temporary and can be tossed away any time. + */ + user = ap_getword_nulls(r->connection->pool, &password, ':'); + r->ap_auth_type = "Basic"; + r->user = r->parsed_uri.user = user; + } + else if ((user = r->parsed_uri.user) != NULL) { + user = apr_pstrdup(p, user); + decodeenc(user); + if ((password = r->parsed_uri.password) != NULL) { + char *tmp = apr_pstrdup(p, password); + decodeenc(tmp); + password = tmp; + } + } + else { + user = "anonymous"; + password = "apache-proxy@"; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: connecting %s to %s:%d", url, connectname, connectport); + + /* do a DNS lookup for the destination host */ + err = apr_sockaddr_info_get(&connect_addr, connectname, APR_UNSPEC, connectport, 0, p); + + /* check if ProxyBlock directive on this host */ + if (OK != ap_proxy_checkproxyblock(r, conf, connect_addr)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + + + /* + * II: Make the Connection ----------------------- + * + * We have determined who to connect to. Now make the connection. + */ + + /* + * get all the possible IP addresses for the destname and loop through + * them until we get a successful connection + */ + if (APR_SUCCESS != err) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_pstrcat(p, + "DNS lookup failure for: ", + connectname, NULL)); + } + + /* + * At this point we have a list of one or more IP addresses of the + * machine to connect to. If configured, reorder this list so that the + * "best candidate" is first try. "best candidate" could mean the least + * loaded server, the fastest responding server, whatever. + * + * For now we do nothing, ie we get DNS round robin. XXX FIXME + */ + + + /* try each IP address until we connect successfully */ + { + int failed = 1; + while (connect_addr) { + + if ((rv = apr_socket_create(&sock, connect_addr->family, SOCK_STREAM, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: error creating socket"); + connect_addr = connect_addr->next; + continue; + } + +#if !defined(TPF) && !defined(BEOS) + if (conf->recv_buffer_size > 0 + && (rv = apr_socket_opt_set(sock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "apr_socket_opt_set(APR_SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } +#endif + + if (APR_SUCCESS != (rv = apr_socket_opt_set(sock, APR_SO_REUSEADDR, one))) { + apr_socket_close(sock); +#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: error setting reuseaddr option: apr_socket_opt_set(APR_SO_REUSEADDR)"); + connect_addr = connect_addr->next; + continue; +#endif /* _OSD_POSIX */ + } + + /* Set a timeout on the socket */ + if (conf->timeout_set == 1) { + apr_socket_timeout_set(sock, conf->timeout); + } + else { + apr_socket_timeout_set(sock, r->server->timeout); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: fam %d socket created, trying to connect to %pI (%s)...", + connect_addr->family, connect_addr, connectname); + + /* make the connection out of the socket */ + rv = apr_connect(sock, connect_addr); + + /* if an error occurred, loop round and try again */ + if (rv != APR_SUCCESS) { + apr_socket_close(sock); + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: FTP: attempt to connect to %pI (%s) failed", connect_addr, connectname); + connect_addr = connect_addr->next; + continue; + } + + /* if we get here, all is well */ + failed = 0; + break; + } + + /* handle a permanent error from the above loop */ + if (failed) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, + "Could not connect to remote machine: %s port %d", + connectname, connectport)); + } + } + + /* the socket is now open, create a new connection */ + origin = ap_run_create_connection(p, r->server, sock, r->connection->id, + r->connection->sbh, c->bucket_alloc); + if (!origin) { + /* + * the peer reset the connection already; ap_run_create_connection() closed + * the socket + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: an error occurred creating a new connection to %pI (%s)", connect_addr, connectname); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* if a keepalive connection is floating around, close it first! */ + /* we might support ftp keepalives later, but not now... */ + if (backend->connection) { + apr_socket_close(origin_sock); + backend->connection = NULL; + origin_sock = NULL; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: control connection complete"); + + + /* + * III: Send Control Request ------------------------- + * + * Log into the ftp server, send the username & password, change to the + * correct directory... + */ + + /* set up the connection filters */ + rc = ap_run_pre_connection(origin, sock); + if (rc != OK && rc != DONE) { + origin->aborted = 1; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: pre_connection setup failed (%d)", + rc); + return rc; + } + + /* possible results: */ + /* 120 Service ready in nnn minutes. */ + /* 220 Service ready for new user. */ + /* 421 Service not available, closing control connection. */ + rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage); + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); + } + if (rc == 120) { + /* + * RFC2616 states: 14.37 Retry-After + * + * The Retry-After response-header field can be used with a 503 (Service + * Unavailable) response to indicate how long the service is expected + * to be unavailable to the requesting client. [...] The value of + * this field can be either an HTTP-date or an integer number of + * seconds (in decimal) after the time of the response. Retry-After + * = "Retry-After" ":" ( HTTP-date | delta-seconds ) + */ + char *secs_str = ftpmessage; + time_t secs; + + /* Look for a number, preceded by whitespace */ + while (*secs_str) + if ((secs_str==ftpmessage || apr_isspace(secs_str[-1])) && + apr_isdigit(secs_str[0])) + break; + if (*secs_str != '\0') { + secs = atol(secs_str); + apr_table_add(r->headers_out, "Retry-After", + apr_psprintf(p, "%lu", (unsigned long)(60 * secs))); + } + return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, ftpmessage); + } + if (rc != 220) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + + rc = proxy_ftp_command(apr_pstrcat(p, "USER ", user, CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results; 230, 331, 332, 421, 500, 501, 530 */ + /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ + /* 230 User logged in, proceed. */ + /* 331 User name okay, need password. */ + /* 332 Need account for login. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* (This may include errors such as command line too long.) */ + /* 501 Syntax error in parameters or arguments. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, "Error reading from remote server"); + } + if (rc == 530) { + return ftp_unauthorized(r, 1); /* log it: user name guessing + * attempt? */ + } + if (rc != 230 && rc != 331) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + + if (rc == 331) { /* send password */ + if (password == NULL) { + return ftp_unauthorized(r, 0); + } + + rc = proxy_ftp_command(apr_pstrcat(p, "PASS ", password, CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ + /* 230 User logged in, proceed. */ + /* 332 Need account for login. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 503 Bad sequence of commands. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 332) { + return ap_proxyerror(r, HTTP_UNAUTHORIZED, + apr_pstrcat(p, "Need account for login: ", ftpmessage, NULL)); + } + /* @@@ questionable -- we might as well return a 403 Forbidden here */ + if (rc == 530) { + return ftp_unauthorized(r, 1); /* log it: passwd guessing + * attempt? */ + } + if (rc != 230 && rc != 202) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + } + apr_table_set(r->notes, "Directory-README", ftpmessage); + + + /* Special handling for leading "%2f": this enforces a "cwd /" + * out of the $HOME directory which was the starting point after login + */ + if (strncasecmp(path, "%2f", 3) == 0) { + path += 3; + while (*path == '/') /* skip leading '/' (after root %2f) */ + ++path; + + rc = proxy_ftp_command("CWD /" CRLF, r, origin, bb, &ftpmessage); + if (rc == -1 || rc == 421) + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + + /* + * set the directory (walk directory component by component): this is + * what we must do if we don't know the OS type of the remote machine + */ + for (;;) { + strp = strchr(path, '/'); + if (strp == NULL) + break; + *strp = '\0'; + + len = decodeenc(path); /* Note! This decodes a %2f -> "/" */ + + if (strchr(path, '/')) { /* are there now any '/' characters? */ + return ap_proxyerror(r, HTTP_BAD_REQUEST, + "Use of /%2f is only allowed at the base directory"); + } + + /* NOTE: FTP servers do globbing on the path. + * So we need to escape the URI metacharacters. + * We use a special glob-escaping routine to escape globbing chars. + * We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH + */ + rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", + ftp_escape_globbingchars(p, path), CRLF, NULL), + r, origin, bb, &ftpmessage); + *strp = '/'; + /* responses: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + return ap_proxyerror(r, HTTP_NOT_FOUND, ftpmessage); + } + if (rc != 250) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + + path = strp + 1; + } + + /* + * IV: Make Data Connection? ------------------------- + * + * Try EPSV, if that fails... try PASV, if that fails... try PORT. + */ +/* this temporarily switches off EPSV/PASV */ +/*goto bypass;*/ + + /* set up data connection - EPSV */ + { + apr_sockaddr_t *data_addr; + char *data_ip; + apr_port_t data_port; + + /* + * The EPSV command replaces PASV where both IPV4 and IPV6 is + * supported. Only the port is returned, the IP address is always the + * same as that on the control connection. Example: Entering Extended + * Passive Mode (|||6446|) + */ + rc = proxy_ftp_command("EPSV" CRLF, + r, origin, bb, &ftpmessage); + /* possible results: 227, 421, 500, 501, 502, 530 */ + /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 229 && rc != 500 && rc != 501 && rc != 502) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + else if (rc == 229) { + /* Parse the port out of the EPSV reply. */ + data_port = parse_epsv_reply(ftpmessage); + + if (data_port) { + apr_sockaddr_t *epsv_addr; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: EPSV contacting remote host on port %d", + data_port); + + if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: error creating EPSV socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } + +#if !defined (TPF) && !defined(BEOS) + if (conf->recv_buffer_size > 0 + && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } +#endif + + /* make the connection */ + apr_socket_addr_get(&data_addr, APR_REMOTE, sock); + apr_sockaddr_ip_get(&data_ip, data_addr); + apr_sockaddr_info_get(&epsv_addr, data_ip, connect_addr->family, data_port, 0, p); + rv = apr_connect(data_sock, epsv_addr); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: FTP: EPSV attempt to connect to %pI failed - Firewall/NAT?", epsv_addr); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, + "EPSV attempt to connect to %pI failed - firewall/NAT?", epsv_addr)); + } + else { + connect = 1; + } + } + } + } + + /* set up data connection - PASV */ + if (!connect) { + rc = proxy_ftp_command("PASV" CRLF, + r, origin, bb, &ftpmessage); + /* possible results: 227, 421, 500, 501, 502, 530 */ + /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 227 && rc != 502) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + else if (rc == 227) { + unsigned int h0, h1, h2, h3, p0, p1; + char *pstr; + char *tok_cntx; + +/* FIXME: Check PASV against RFC1123 */ + + pstr = ftpmessage; + pstr = apr_strtok(pstr, " ", &tok_cntx); /* separate result code */ + if (pstr != NULL) { + if (*(pstr + strlen(pstr) + 1) == '=') { + pstr += strlen(pstr) + 2; + } + else { + pstr = apr_strtok(NULL, "(", &tok_cntx); /* separate address & + * port params */ + if (pstr != NULL) + pstr = apr_strtok(NULL, ")", &tok_cntx); + } + } + +/* FIXME: Only supports IPV4 - fix in RFC2428 */ + + if (pstr != NULL && (sscanf(pstr, + "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) { + + apr_sockaddr_t *pasv_addr; + apr_port_t pasvport = (p1 << 8) + p0; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: PASV contacting host %d.%d.%d.%d:%d", + h3, h2, h1, h0, pasvport); + + if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: error creating PASV socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } + +#if !defined (TPF) && !defined(BEOS) + if (conf->recv_buffer_size > 0 + && (rv = apr_socket_opt_set(data_sock, APR_SO_RCVBUF, + conf->recv_buffer_size))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: apr_socket_opt_set(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default"); + } +#endif + + /* make the connection */ + apr_sockaddr_info_get(&pasv_addr, apr_psprintf(p, "%d.%d.%d.%d", h3, h2, h1, h0), connect_addr->family, pasvport, 0, p); + rv = apr_connect(data_sock, pasv_addr); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, + "proxy: FTP: PASV attempt to connect to %pI failed - Firewall/NAT?", pasv_addr); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, apr_psprintf(r->pool, + "PASV attempt to connect to %pI failed - firewall/NAT?", pasv_addr)); + } + else { + connect = 1; + } + } + } + } +/*bypass:*/ + + /* set up data connection - PORT */ + if (!connect) { + apr_sockaddr_t *local_addr; + char *local_ip; + apr_port_t local_port; + unsigned int h0, h1, h2, h3, p0, p1; + + if ((rv = apr_socket_create(&local_sock, connect_addr->family, SOCK_STREAM, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: error creating local socket"); + return HTTP_INTERNAL_SERVER_ERROR; + } + apr_socket_addr_get(&local_addr, APR_LOCAL, sock); + apr_sockaddr_port_get(&local_port, local_addr); + apr_sockaddr_ip_get(&local_ip, local_addr); + + if ((rv = apr_socket_opt_set(local_sock, APR_SO_REUSEADDR, one)) + != APR_SUCCESS) { +#ifndef _OSD_POSIX /* BS2000 has this option "always on" */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: error setting reuseaddr option"); + return HTTP_INTERNAL_SERVER_ERROR; +#endif /* _OSD_POSIX */ + } + + apr_sockaddr_info_get(&local_addr, local_ip, APR_UNSPEC, local_port, 0, r->pool); + + if ((rv = apr_bind(local_sock, local_addr)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: error binding to ftp data socket %pI", local_addr); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* only need a short queue */ + if ((rv = apr_listen(local_sock, 2)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: error listening to ftp data socket %pI", local_addr); + return HTTP_INTERNAL_SERVER_ERROR; + } + +/* FIXME: Sent PORT here */ + + if (local_ip && (sscanf(local_ip, + "%d.%d.%d.%d", &h3, &h2, &h1, &h0) == 4)) { + p1 = (local_port >> 8); + p0 = (local_port & 0xFF); + + rc = proxy_ftp_command(apr_psprintf(p, "PORT %d,%d,%d,%d,%d,%d" CRLF, h3, h2, h1, h0, p1, p0), + r, origin, bb, &ftpmessage); + /* possible results: 200, 421, 500, 501, 502, 530 */ + /* 200 Command okay. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 200) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, buffer); + } + + /* signal that we must use the EPRT/PORT loop */ + use_port = 1; + } + else { +/* IPV6 FIXME: + * The EPRT command replaces PORT where both IPV4 and IPV6 is supported. The first + * number (1,2) indicates the protocol type. Examples: + * EPRT |1|132.235.1.2|6275| + * EPRT |2|1080::8:800:200C:417A|5282| + */ + return ap_proxyerror(r, HTTP_NOT_IMPLEMENTED, "Connect to IPV6 ftp server using EPRT not supported. Enable EPSV."); + } + } + + + /* + * V: Set The Headers ------------------- + * + * Get the size of the request, set up the environment for HTTP. + */ + + /* set request; "path" holds last path component */ + len = decodeenc(path); + + if (strchr(path, '/')) { /* are there now any '/' characters? */ + return ap_proxyerror(r, HTTP_BAD_REQUEST, + "Use of /%2f is only allowed at the base directory"); + } + + /* If len == 0 then it must be a directory (you can't RETR nothing) + * Also, don't allow to RETR by wildcard. Instead, create a dirlisting + */ + if (len == 0 || ftp_check_globbingchars(path)) { + dirlisting = 1; + } + else { + /* (from FreeBSD ftpd): + * SIZE is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return size of file in a format suitable for + * using with RESTART (we just count bytes). + */ + /* from draft-ietf-ftpext-mlst-14.txt: + * This value will + * change depending on the current STRUcture, MODE and TYPE of the data + * connection, or a data connection which would be created were one + * created now. Thus, the result of the SIZE command is dependent on + * the currently established STRU, MODE and TYPE parameters. + */ + /* Therefore: switch to binary if the user did not specify ";type=a" */ + ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage); + rc = proxy_ftp_command(apr_pstrcat(p, "SIZE ", + ftp_escape_globbingchars(p, path), CRLF, NULL), + r, origin, bb, &ftpmessage); + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + else if (rc == 213) {/* Size command ok */ + int j; + for (j = 0; apr_isdigit(ftpmessage[j]); j++) + ; + ftpmessage[j] = '\0'; + if (ftpmessage[0] != '\0') + size = ftpmessage; /* already pstrdup'ed: no copy necessary */ + } + else if (rc == 550) { /* Not a regular file */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: SIZE shows this is a directory"); + dirlisting = 1; + rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", + ftp_escape_globbingchars(p, path), CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + return ap_proxyerror(r, HTTP_NOT_FOUND, ftpmessage); + } + if (rc != 250) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + path = ""; + len = 0; + } + } + + cwd = ftp_get_PWD(r, origin, bb); + if (cwd != NULL) { + apr_table_set(r->notes, "Directory-PWD", cwd); + } + + if (dirlisting) { + ftp_set_TYPE('A', r, origin, bb, NULL); + /* If the current directory contains no slash, we are talking to + * a non-unix ftp system. Try LIST instead of "LIST -lag", it + * should return a long listing anyway (unlike NLST). + * Some exotic FTP servers might choke on the "-lag" switch. + */ + /* Note that we do not escape the path here, to allow for + * queries like: ftp://user@host/apache/src/server/http_*.c + */ + if (len != 0) + buf = apr_pstrcat(p, "LIST ", path, CRLF, NULL); + else if (cwd == NULL || strchr(cwd, '/') != NULL) + buf = apr_pstrcat(p, "LIST -lag", CRLF, NULL); + else + buf = "LIST" CRLF; + } + else { + /* switch to binary if the user did not specify ";type=a" */ + ftp_set_TYPE(xfer_type, r, origin, bb, &ftpmessage); +#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) + /* from draft-ietf-ftpext-mlst-14.txt: + * The FTP command, MODIFICATION TIME (MDTM), can be used to determine + * when a file in the server NVFS was last modified. <..> + * The syntax of a time value is: + * time-val = 14DIGIT [ "." 1*DIGIT ] <..> + * Symbolically, a time-val may be viewed as + * YYYYMMDDHHMMSS.sss + * The "." and subsequent digits ("sss") are optional. <..> + * Time values are always represented in UTC (GMT) + */ + rc = proxy_ftp_command(apr_pstrcat(p, "MDTM ", ftp_escape_globbingchars(p, path), CRLF, NULL), + r, origin, bb, &ftpmessage); + /* then extract the Last-Modified time from it (YYYYMMDDhhmmss or YYYYMMDDhhmmss.xxx GMT). */ + if (rc == 213) { + struct { + char YYYY[4+1]; + char MM[2+1]; + char DD[2+1]; + char hh[2+1]; + char mm[2+1]; + char ss[2+1]; + } time_val; + if (6 == sscanf(ftpmessage, "%4[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]%2[0-9]", + time_val.YYYY, time_val.MM, time_val.DD, time_val.hh, time_val.mm, time_val.ss)) { + struct tm tms; + memset (&tms, '\0', sizeof tms); + tms.tm_year = atoi(time_val.YYYY) - 1900; + tms.tm_mon = atoi(time_val.MM) - 1; + tms.tm_mday = atoi(time_val.DD); + tms.tm_hour = atoi(time_val.hh); + tms.tm_min = atoi(time_val.mm); + tms.tm_sec = atoi(time_val.ss); +#ifdef HAVE_TIMEGM /* Does system have timegm()? */ + mtime = timegm(&tms); + mtime *= APR_USEC_PER_SEC; +#elif HAVE_GMTOFF /* does struct tm have a member tm_gmtoff? */ + /* mktime will subtract the local timezone, which is not what we want. + * Add it again because the MDTM string is GMT + */ + mtime = mktime(&tms); + mtime += tms.tm_gmtoff; + mtime *= APR_USEC_PER_SEC; +#else + mtime = 0L; +#endif + } + } +#endif /* USE_MDTM */ +/* FIXME: Handle range requests - send REST */ + buf = apr_pstrcat(p, "RETR ", ftp_escape_globbingchars(p, path), CRLF, NULL); + } + rc = proxy_ftp_command(buf, r, origin, bb, &ftpmessage); + /* rc is an intermediate response for the LIST or RETR commands */ + + /* + * RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, + * 550 NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, + * 530 + */ + /* 110 Restart marker reply. */ + /* 125 Data connection already open; transfer starting. */ + /* 150 File status okay; about to open data connection. */ + /* 226 Closing data connection. */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 425 Can't open data connection. */ + /* 426 Connection closed; transfer aborted. */ + /* 450 Requested file action not taken. */ + /* 451 Requested action aborted. Local error in processing. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: RETR failed, trying LIST instead"); + + /* Directory Listings should always be fetched in ASCII mode */ + dirlisting = 1; + ftp_set_TYPE('A', r, origin, bb, NULL); + + rc = proxy_ftp_command(apr_pstrcat(p, "CWD ", + ftp_escape_globbingchars(p, path), CRLF, NULL), + r, origin, bb, &ftpmessage); + /* possible results: 250, 421, 500, 501, 502, 530, 550 */ + /* 250 Requested file action okay, completed. */ + /* 421 Service not available, closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + /* 501 Syntax error in parameters or arguments. */ + /* 502 Command not implemented. */ + /* 530 Not logged in. */ + /* 550 Requested action not taken. */ + if (rc == -1 || rc == 421) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc == 550) { + return ap_proxyerror(r, HTTP_NOT_FOUND, ftpmessage); + } + if (rc != 250) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + + /* Update current directory after CWD */ + cwd = ftp_get_PWD(r, origin, bb); + if (cwd != NULL) { + apr_table_set(r->notes, "Directory-PWD", cwd); + } + + /* See above for the "LIST" vs. "LIST -lag" discussion. */ + rc = proxy_ftp_command((cwd == NULL || strchr(cwd, '/') != NULL) + ? "LIST -lag" CRLF : "LIST" CRLF, + r, origin, bb, &ftpmessage); + + /* rc is an intermediate response for the LIST command (125 transfer starting, 150 opening data connection) */ + if (rc == -1 || rc == 421) + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + if (rc != 125 && rc != 150 && rc != 226 && rc != 250) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, ftpmessage); + } + + r->status = HTTP_OK; + r->status_line = "200 OK"; + + apr_rfc822_date(dates, r->request_time); + apr_table_setn(r->headers_out, "Date", dates); + apr_table_setn(r->headers_out, "Server", ap_get_server_version()); + + /* set content-type */ + if (dirlisting) { + ap_set_content_type(r, "text/html; charset=ISO-8859-1"); + } + else { + if (r->content_type) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: Content-Type set to %s", r->content_type); + } + else { + ap_set_content_type(r, ap_default_type(r)); + } + if (xfer_type != 'A' && size != NULL) { + /* We "trust" the ftp server to really serve (size) bytes... */ + apr_table_setn(r->headers_out, "Content-Length", size); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: Content-Length set to %s", size); + } + } + apr_table_setn(r->headers_out, "Content-Type", r->content_type); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: Content-Type set to %s", r->content_type); + +#if defined(USE_MDTM) && (defined(HAVE_TIMEGM) || defined(HAVE_GMTOFF)) + if (mtime != 0L) { + char datestr[APR_RFC822_DATE_LEN]; + apr_rfc822_date(datestr, mtime); + apr_table_set(r->headers_out, "Last-Modified", datestr); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: Last-Modified set to %s", datestr); + } +#endif /* USE_MDTM */ + + /* If an encoding has been set by mistake, delete it. + * @@@ FIXME (e.g., for ftp://user@host/file*.tar.gz, + * @@@ the encoding is currently set to x-gzip) + */ + if (dirlisting && r->content_encoding != NULL) + r->content_encoding = NULL; + + /* set content-encoding (not for dir listings, they are uncompressed)*/ + if (r->content_encoding != NULL && r->content_encoding[0] != '\0') { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: Content-Encoding set to %s", r->content_encoding); + apr_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); + } + + /* wait for connection */ + if (use_port) { + for (;;) { + rv = apr_accept(&data_sock, local_sock, r->pool); + if (rv == APR_EINTR) { + continue; + } + else if (rv == APR_SUCCESS) { + break; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "proxy: FTP: failed to accept data connection"); + return HTTP_BAD_GATEWAY; + } + } + } + + /* the transfer socket is now open, create a new connection */ + data = ap_run_create_connection(p, r->server, data_sock, r->connection->id, + r->connection->sbh, c->bucket_alloc); + if (!data) { + /* + * the peer reset the connection already; ap_run_create_connection() closed + * the socket + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: an error occurred creating the transfer connection"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* set up the connection filters */ + rc = ap_run_pre_connection(data, data_sock); + if (rc != OK && rc != DONE) { + data->aborted = 1; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: pre_connection setup failed (%d)", + rc); + return rc; + } + + /* + * VI: Receive the Response ------------------------ + * + * Get response from the remote ftp socket, and pass it up the filter chain. + */ + + /* send response */ + r->sent_bodyct = 1; + + if (dirlisting) { + /* insert directory filter */ + ap_add_output_filter("PROXY_SEND_DIR", NULL, r, r->connection); + } + + /* send body */ + if (!r->header_only) { + apr_bucket *e; + int finish = FALSE; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: start body send"); + + /* read the body, pass it to the output filters */ + while (ap_get_brigade(data->input_filters, + bb, + AP_MODE_READBYTES, + APR_BLOCK_READ, + conf->io_buffer_size) == APR_SUCCESS) { +#if DEBUGGING + { + apr_off_t readbytes; + apr_brigade_length(bb, 0, &readbytes); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, "proxy (PID %d): readbytes: %#x", + getpid(), readbytes); + } +#endif + /* sanity check */ + if (APR_BRIGADE_EMPTY(bb)) { + apr_brigade_cleanup(bb); + break; + } + + /* found the last brigade? */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + /* if this is the last brigade, cleanup the + * backend connection first to prevent the + * backend server from hanging around waiting + * for a slow client to eat these bytes + */ + ap_flush_conn(data); + if (data_sock) { + apr_socket_close(data_sock); + } + data_sock = NULL; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: data connection closed"); + /* signal that we must leave */ + finish = TRUE; + } + + /* if no EOS yet, then we must flush */ + if (FALSE == finish) { + e = apr_bucket_flush_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + + /* try send what we read */ + if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS + || c->aborted) { + /* Ack! Phbtt! Die! User aborted! */ + finish = TRUE; + } + + /* make sure we always clean up after ourselves */ + apr_brigade_cleanup(bb); + + /* if we are done, leave */ + if (TRUE == finish) { + break; + } + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: end body send"); + + } + if (data_sock) { + ap_flush_conn(data); + apr_socket_close(data_sock); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: FTP: data connection closed"); + } + + /* Retrieve the final response for the RETR or LIST commands */ + rc = proxy_ftp_command(NULL, r, origin, bb, &ftpmessage); + apr_brigade_cleanup(bb); + + /* + * VII: Clean Up ------------- + * + * If there are no KeepAlives, or if the connection has been signalled to + * close, close the socket and clean up + */ + + /* finish */ + rc = proxy_ftp_command("QUIT" CRLF, + r, origin, bb, &ftpmessage); + /* responses: 221, 500 */ + /* 221 Service closing control connection. */ + /* 500 Syntax error, command unrecognized. */ + ap_flush_conn(origin); + if (origin_sock) { + apr_socket_close(origin_sock); + origin_sock = NULL; + } + apr_brigade_destroy(bb); + return OK; +} + +static void ap_proxy_ftp_register_hook(apr_pool_t *p) +{ + /* hooks */ + proxy_hook_scheme_handler(ap_proxy_ftp_handler, NULL, NULL, APR_HOOK_MIDDLE); + proxy_hook_canon_handler(ap_proxy_ftp_canon, NULL, NULL, APR_HOOK_MIDDLE); + /* filters */ + ap_register_output_filter("PROXY_SEND_DIR", ap_proxy_send_dir_filter, + NULL, AP_FTYPE_RESOURCE); +} + +module AP_MODULE_DECLARE_DATA proxy_ftp_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_ftp_register_hook /* register hooks */ +}; diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/proxy_http.c b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_http.c new file mode 100644 index 00000000..ca5f038b --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_http.c @@ -0,0 +1,1824 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* HTTP routines for Apache proxy */ + +#include "mod_proxy.h" + +module AP_MODULE_DECLARE_DATA proxy_http_module; + +int ap_proxy_http_canon(request_rec *r, char *url); +int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport); + +typedef struct { + const char *name; + apr_port_t port; + apr_sockaddr_t *addr; + apr_socket_t *sock; + int close; +} proxy_http_conn_t; + +static apr_status_t ap_proxy_http_cleanup(request_rec *r, + proxy_http_conn_t *p_conn, + proxy_conn_rec *backend); + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +int ap_proxy_http_canon(request_rec *r, char *url) +{ + char *host, *path, *search, sport[7]; + const char *err; + const char *scheme; + apr_port_t port, def_port; + + /* ap_port_of_scheme() */ + if (strncasecmp(url, "http:", 5) == 0) { + url += 5; + scheme = "http"; + } + else if (strncasecmp(url, "https:", 6) == 0) { + url += 6; + scheme = "https"; + } + else { + return DECLINED; + } + def_port = apr_uri_port_of_scheme(scheme); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: HTTP: canonicalising URL %s", url); + + /* do syntatic check. + * We break the URL into host, port, path, search + */ + port = def_port; + err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "error parsing URL %s: %s", + url, err); + return HTTP_BAD_REQUEST; + } + + /* now parse path/search args, according to rfc1738 */ + /* N.B. if this isn't a true proxy request, then the URL _path_ + * has already been decoded. True proxy requests have r->uri + * == r->unparsed_uri, and no others have that property. + */ + if (r->uri == r->unparsed_uri) { + search = strchr(url, '?'); + if (search != NULL) + *(search++) = '\0'; + } + else + search = r->args; + + /* process path */ + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) + return HTTP_BAD_REQUEST; + + if (port != def_port) + apr_snprintf(sport, sizeof(sport), ":%d", port); + else + sport[0] = '\0'; + + if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */ + host = apr_pstrcat(r->pool, "[", host, "]", NULL); + } + r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, + "/", path, (search) ? "?" : "", (search) ? search : "", NULL); + return OK; +} + +static const char *ap_proxy_location_reverse_map(request_rec *r, proxy_server_conf *conf, const char *url) +{ + struct proxy_alias *ent; + int i, l1, l2; + char *u; + + /* XXX FIXME: Make sure this handled the ambiguous case of the :80 + * after the hostname */ + + l1 = strlen(url); + ent = (struct proxy_alias *)conf->raliases->elts; + for (i = 0; i < conf->raliases->nelts; i++) { + l2 = strlen(ent[i].real); + if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) { + u = apr_pstrcat(r->pool, ent[i].fake, &url[l2], NULL); + return ap_construct_url(r->pool, u, r); + } + } + return url; +} + +/* Clear all connection-based headers from the incoming headers table */ +static void ap_proxy_clear_connection(apr_pool_t *p, apr_table_t *headers) +{ + const char *name; + char *next = apr_pstrdup(p, apr_table_get(headers, "Connection")); + + apr_table_unset(headers, "Proxy-Connection"); + if (!next) + return; + + while (*next) { + name = next; + while (*next && !apr_isspace(*next) && (*next != ',')) { + ++next; + } + while (*next && (apr_isspace(*next) || (*next == ','))) { + *next = '\0'; + ++next; + } + apr_table_unset(headers, name); + } + apr_table_unset(headers, "Connection"); +} + +static +apr_status_t ap_proxy_http_determine_connection(apr_pool_t *p, request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *c, + proxy_server_conf *conf, + 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_sockaddr_t *uri_addr; + /* + * 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: HTTP connecting %s to %s:%d", *url, uri->hostname, + uri->port); + + /* do a DNS lookup for the destination host */ + /* see memory note above */ + err = apr_sockaddr_info_get(&uri_addr, apr_pstrdup(c->pool, uri->hostname), + APR_UNSPEC, uri->port, 0, c->pool); + + /* allocate these out of the connection pool - the check on + * r->connection->id makes sure that this string does not get accessed + * past the connection lifetime */ + /* are we connecting directly, or via a proxy? */ + if (proxyname) { + p_conn->name = apr_pstrdup(c->pool, proxyname); + p_conn->port = proxyport; + /* see memory note above */ + err = apr_sockaddr_info_get(&p_conn->addr, p_conn->name, APR_UNSPEC, + p_conn->port, 0, c->pool); + } else { + p_conn->name = apr_pstrdup(c->pool, uri->hostname); + p_conn->port = uri->port; + p_conn->addr = uri_addr; + *url = apr_pstrcat(p, uri->path, uri->query ? "?" : "", + uri->query ? uri->query : "", + uri->fragment ? "#" : "", + uri->fragment ? uri->fragment : "", NULL); + } + + if (err != APR_SUCCESS) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "DNS lookup failure for: ", + p_conn->name, 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, uri_addr)) { + return ap_proxyerror(r, HTTP_FORBIDDEN, + "Connect to remote machine blocked"); + } + return OK; +} + +static +apr_status_t ap_proxy_http_create_connection(apr_pool_t *p, request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *c, conn_rec **origin, + proxy_conn_rec *backend, + proxy_server_conf *conf, + const char *proxyname) { + int failed=0, new=0; + apr_socket_t *client_socket = NULL; + + /* We have determined who to connect to. Now make the connection, supporting + * a KeepAlive connection. + */ + + /* get all the possible IP addresses for the destname and loop through them + * until we get a successful connection + */ + + /* if a keepalive socket is already open, check whether it must stay + * open, or whether it should be closed and a new socket created. + */ + /* see memory note above */ + if (backend->connection) { + client_socket = ap_get_module_config(backend->connection->conn_config, &core_module); + if ((backend->connection->id == c->id) && + (backend->port == p_conn->port) && + (backend->hostname) && + (!apr_strnatcasecmp(backend->hostname, p_conn->name))) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: keepalive address match (keep original socket)"); + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: keepalive address mismatch / connection has" + " changed (close old socket (%s/%s, %d/%d))", + p_conn->name, backend->hostname, p_conn->port, + backend->port); + apr_socket_close(client_socket); + backend->connection = NULL; + } + } + + /* get a socket - either a keepalive one, or a new one */ + new = 1; + if ((backend->connection) && (backend->connection->id == c->id)) { + apr_size_t buffer_len = 1; + char test_buffer[1]; + apr_status_t socket_status; + apr_interval_time_t current_timeout; + + /* use previous keepalive socket */ + *origin = backend->connection; + p_conn->sock = client_socket; + new = 0; + + /* save timeout */ + apr_socket_timeout_get(p_conn->sock, ¤t_timeout); + /* set no timeout */ + apr_socket_timeout_set(p_conn->sock, 0); + socket_status = apr_recv(p_conn->sock, test_buffer, &buffer_len); + /* put back old timeout */ + apr_socket_timeout_set(p_conn->sock, current_timeout); + if ( APR_STATUS_IS_EOF(socket_status) ) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, + "proxy: previous connection is closed, creating a new connection."); + new = 1; + } + } + if (new) { + int rc; + + /* create a new socket */ + backend->connection = NULL; + + /* + * At this point we have a list of one or more IP addresses of + * the machine to connect to. If configured, reorder this + * list so that the "best candidate" is first try. "best + * candidate" could mean the least loaded server, the fastest + * responding server, whatever. + * + * For now we do nothing, ie we get DNS round robin. + * XXX FIXME + */ + failed = ap_proxy_connect_to_backend(&p_conn->sock, "HTTP", + p_conn->addr, p_conn->name, + conf, r->server, c->pool); + + /* handle a permanent error on the connect */ + if (failed) { + if (proxyname) { + return DECLINED; + } else { + return HTTP_BAD_GATEWAY; + } + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: socket is connected"); + + /* the socket is now open, create a new backend server connection */ + *origin = ap_run_create_connection(c->pool, r->server, p_conn->sock, + r->connection->id, + r->connection->sbh, c->bucket_alloc); + if (!*origin) { + /* the peer reset the connection already; ap_run_create_connection() + * closed the socket + */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, "proxy: an error occurred creating a " + "new connection to %pI (%s)", p_conn->addr, + p_conn->name); + apr_socket_close(p_conn->sock); + return HTTP_INTERNAL_SERVER_ERROR; + } + backend->connection = *origin; + backend->hostname = apr_pstrdup(c->pool, p_conn->name); + backend->port = p_conn->port; + + if (backend->is_ssl) { + if (!ap_proxy_ssl_enable(backend->connection)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, + r->server, "proxy: failed to enable ssl support " + "for %pI (%s)", p_conn->addr, p_conn->name); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + else { + ap_proxy_ssl_disable(backend->connection); + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: connection complete to %pI (%s)", + p_conn->addr, p_conn->name); + + /* set up the connection filters */ + rc = ap_run_pre_connection(*origin, p_conn->sock); + if (rc != OK && rc != DONE) { + (*origin)->aborted = 1; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: HTTP: pre_connection setup failed (%d)", + rc); + return rc; + } + } + return OK; +} + +static void add_te_chunked(apr_pool_t *p, + apr_bucket_alloc_t *bucket_alloc, + apr_bucket_brigade *header_brigade) +{ + apr_bucket *e; + char *buf; + const char te_hdr[] = "Transfer-Encoding: chunked" CRLF; + + buf = apr_pmemdup(p, te_hdr, sizeof(te_hdr)-1); + ap_xlate_proto_to_ascii(buf, sizeof(te_hdr)-1); + + e = apr_bucket_pool_create(buf, sizeof(te_hdr)-1, p, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); +} + +static void add_cl(apr_pool_t *p, + apr_bucket_alloc_t *bucket_alloc, + apr_bucket_brigade *header_brigade, + const char *cl_val) +{ + apr_bucket *e; + char *buf; + + buf = apr_pstrcat(p, "Content-Length: ", + cl_val, + CRLF, + NULL); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); +} + +#define ASCII_CRLF "\015\012" +#define ASCII_ZERO "\060" + +static void terminate_headers(apr_bucket_alloc_t *bucket_alloc, + apr_bucket_brigade *header_brigade) +{ + apr_bucket *e; + + /* add empty line at the end of the headers */ + e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); +} + +static apr_status_t pass_brigade(apr_bucket_alloc_t *bucket_alloc, + request_rec *r, proxy_http_conn_t *p_conn, + conn_rec *origin, apr_bucket_brigade *bb, + int flush) +{ + apr_status_t status; + + if (flush) { + apr_bucket *e = apr_bucket_flush_create(bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + status = ap_pass_brigade(origin->output_filters, bb); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: pass request body failed to %pI (%s)", + p_conn->addr, p_conn->name); + return status; + } + apr_brigade_cleanup(bb); + return APR_SUCCESS; +} + +static apr_status_t stream_reqbody_chunked(apr_pool_t *p, + request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *origin, + apr_bucket_brigade *header_brigade, + apr_bucket_brigade *input_brigade) +{ + int seen_eos = 0; + apr_size_t hdr_len; + apr_off_t bytes; + apr_status_t status; + apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc; + apr_bucket_brigade *bb; + apr_bucket *e; + + add_te_chunked(p, bucket_alloc, header_brigade); + terminate_headers(bucket_alloc, header_brigade); + + while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) + { + char chunk_hdr[20]; /* must be here due to transient bucket. */ + + /* If this brigade contains EOS, either stop or remove it. */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + seen_eos = 1; + + /* We can't pass this EOS to the output_filters. */ + e = APR_BRIGADE_LAST(input_brigade); + apr_bucket_delete(e); + } + + apr_brigade_length(input_brigade, 1, &bytes); + + hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr), + "%" APR_UINT64_T_HEX_FMT CRLF, + (apr_uint64_t)bytes); + + ap_xlate_proto_to_ascii(chunk_hdr, hdr_len); + e = apr_bucket_transient_create(chunk_hdr, hdr_len, + bucket_alloc); + APR_BRIGADE_INSERT_HEAD(input_brigade, e); + + /* + * Append the end-of-chunk CRLF + */ + e = apr_bucket_immortal_create(ASCII_CRLF, 2, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + + if (header_brigade) { + /* we never sent the header brigade, so go ahead and + * take care of that now + */ + bb = header_brigade; + + /* + * Save input_brigade in bb brigade. (At least) in the SSL case + * input_brigade contains transient buckets whose data would get + * overwritten during the next call of ap_get_brigade in the loop. + * ap_save_brigade ensures these buckets to be set aside. + * Calling ap_save_brigade with NULL as filter is OK, because + * bb brigade already has been created and does not need to get + * created by ap_save_brigade. + */ + status = ap_save_brigade(NULL, &bb, &input_brigade, p); + if (status != APR_SUCCESS) { + return status; + } + + header_brigade = NULL; + } + else { + bb = input_brigade; + } + + /* The request is flushed below this loop with chunk EOS header */ + status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 0); + if (status != APR_SUCCESS) { + return status; + } + + if (seen_eos) { + break; + } + + status = ap_get_brigade(r->input_filters, input_brigade, + AP_MODE_READBYTES, APR_BLOCK_READ, + HUGE_STRING_LEN); + + if (status != APR_SUCCESS) { + return status; + } + } + + if (header_brigade) { + /* we never sent the header brigade because there was no request body; + * send it now + */ + bb = header_brigade; + } + else { + if (!APR_BRIGADE_EMPTY(input_brigade)) { + /* input brigade still has an EOS which we can't pass to the output_filters. */ + e = APR_BRIGADE_LAST(input_brigade); + AP_DEBUG_ASSERT(APR_BUCKET_IS_EOS(e)); + apr_bucket_delete(e); + } + bb = input_brigade; + } + + e = apr_bucket_immortal_create(ASCII_ZERO ASCII_CRLF + /* <trailers> */ + ASCII_CRLF, + 5, bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + + /* Now we have headers-only, or the chunk EOS mark; flush it */ + status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1); + return status; +} + +static apr_status_t stream_reqbody_cl(apr_pool_t *p, + request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *origin, + apr_bucket_brigade *header_brigade, + apr_bucket_brigade *input_brigade, + const char *old_cl_val) +{ + int seen_eos = 0; + apr_status_t status = APR_SUCCESS; + apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc; + apr_bucket_brigade *bb; + apr_bucket *e; + apr_off_t cl_val = 0; + apr_off_t bytes; + apr_off_t bytes_streamed = 0; + + if (old_cl_val) { + add_cl(p, bucket_alloc, header_brigade, old_cl_val); + cl_val = atol(old_cl_val); + } + terminate_headers(bucket_alloc, header_brigade); + + while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) + { + apr_brigade_length(input_brigade, 1, &bytes); + bytes_streamed += bytes; + + /* If this brigade contains EOS, either stop or remove it. */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + seen_eos = 1; + + /* We can't pass this EOS to the output_filters. */ + e = APR_BRIGADE_LAST(input_brigade); + apr_bucket_delete(e); + } + + /* C-L < bytes streamed?!? + * We will error out after the body is completely + * consumed, but we can't stream more bytes at the + * back end since they would in part be interpreted + * as another request! If nothing is sent, then + * just send nothing. + * + * Prevents HTTP Response Splitting. + */ + if (bytes_streamed > cl_val) + continue; + + if (header_brigade) { + /* we never sent the header brigade, so go ahead and + * take care of that now + */ + bb = header_brigade; + + /* + * Save input_brigade in bb brigade. (At least) in the SSL case + * input_brigade contains transient buckets whose data would get + * overwritten during the next call of ap_get_brigade in the loop. + * ap_save_brigade ensures these buckets to be set aside. + * Calling ap_save_brigade with NULL as filter is OK, because + * bb brigade already has been created and does not need to get + * created by ap_save_brigade. + */ + status = ap_save_brigade(NULL, &bb, &input_brigade, p); + if (status != APR_SUCCESS) { + return status; + } + + header_brigade = NULL; + } + else { + bb = input_brigade; + } + + /* Once we hit EOS, we are ready to flush. */ + status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, seen_eos); + if (status != APR_SUCCESS) { + return status; + } + + if (seen_eos) { + break; + } + + status = ap_get_brigade(r->input_filters, input_brigade, + AP_MODE_READBYTES, APR_BLOCK_READ, + HUGE_STRING_LEN); + + if (status != APR_SUCCESS) { + return status; + } + } + + if (bytes_streamed != cl_val) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "proxy: client %s given Content-Length did not match" + " number of body bytes read", r->connection->remote_ip); + return APR_EOF; + } + + if (header_brigade) { + /* we never sent the header brigade since there was no request + * body; send it now with the flush flag + */ + bb = header_brigade; + status = pass_brigade(bucket_alloc, r, p_conn, origin, bb, 1); + } + return status; +} + +#define MAX_MEM_SPOOL 16384 + +static apr_status_t spool_reqbody_cl(apr_pool_t *p, + request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *origin, + apr_bucket_brigade *header_brigade, + apr_bucket_brigade *input_brigade, + int force_cl) +{ + int seen_eos = 0; + apr_status_t status; + apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc; + apr_bucket_brigade *body_brigade; + apr_bucket *e; + apr_off_t bytes, bytes_spooled = 0, fsize = 0; + apr_file_t *tmpfile = NULL; + + body_brigade = apr_brigade_create(p, bucket_alloc); + + while (!APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(input_brigade))) + { + /* If this brigade contains EOS, either stop or remove it. */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + seen_eos = 1; + + /* We can't pass this EOS to the output_filters. */ + e = APR_BRIGADE_LAST(input_brigade); + apr_bucket_delete(e); + } + + apr_brigade_length(input_brigade, 1, &bytes); + + if (bytes_spooled + bytes > MAX_MEM_SPOOL) { + /* can't spool any more in memory; write latest brigade to disk */ + if (tmpfile == NULL) { + const char *temp_dir; + char *template; + + status = apr_temp_dir_get(&temp_dir, p); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: search for temporary directory failed"); + return status; + } + apr_filepath_merge(&template, temp_dir, + "modproxy.tmp.XXXXXX", + APR_FILEPATH_NATIVE, p); + status = apr_file_mktemp(&tmpfile, template, 0, p); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: creation of temporary file in directory %s failed", + temp_dir); + return status; + } + } + for (e = APR_BRIGADE_FIRST(input_brigade); + e != APR_BRIGADE_SENTINEL(input_brigade); + e = APR_BUCKET_NEXT(e)) { + const char *data; + apr_size_t bytes_read, bytes_written; + + apr_bucket_read(e, &data, &bytes_read, APR_BLOCK_READ); + status = apr_file_write_full(tmpfile, data, bytes_read, &bytes_written); + if (status != APR_SUCCESS) { + const char *tmpfile_name; + + if (apr_file_name_get(&tmpfile_name, tmpfile) != APR_SUCCESS) { + tmpfile_name = "(unknown)"; + } + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: write to temporary file %s failed", + tmpfile_name); + return status; + } + AP_DEBUG_ASSERT(bytes_read == bytes_written); + fsize += bytes_written; + } + apr_brigade_cleanup(input_brigade); + } + else { + + /* + * Save input_brigade in body_brigade. (At least) in the SSL case + * input_brigade contains transient buckets whose data would get + * overwritten during the next call of ap_get_brigade in the loop. + * ap_save_brigade ensures these buckets to be set aside. + * Calling ap_save_brigade with NULL as filter is OK, because + * body_brigade already has been created and does not need to get + * created by ap_save_brigade. + */ + status = ap_save_brigade(NULL, &body_brigade, &input_brigade, p); + if (status != APR_SUCCESS) { + return status; + } + + } + + bytes_spooled += bytes; + + if (seen_eos) { + break; + } + + status = ap_get_brigade(r->input_filters, input_brigade, + AP_MODE_READBYTES, APR_BLOCK_READ, + HUGE_STRING_LEN); + + if (status != APR_SUCCESS) { + return status; + } + } + + if (bytes_spooled || force_cl) { + add_cl(p, bucket_alloc, header_brigade, apr_off_t_toa(p, bytes_spooled)); + } + terminate_headers(bucket_alloc, header_brigade); + APR_BRIGADE_CONCAT(header_brigade, body_brigade); + if (tmpfile) { + /* For platforms where the size of the file may be larger than + * that which can be stored in a single bucket (where the + * length field is an apr_size_t), split it into several + * buckets: */ + if (sizeof(apr_off_t) > sizeof(apr_size_t) + && fsize > AP_MAX_SENDFILE) { + e = apr_bucket_file_create(tmpfile, 0, AP_MAX_SENDFILE, p, + bucket_alloc); + while (fsize > AP_MAX_SENDFILE) { + apr_bucket *ce; + apr_bucket_copy(e, &ce); + APR_BRIGADE_INSERT_TAIL(header_brigade, ce); + e->start += AP_MAX_SENDFILE; + fsize -= AP_MAX_SENDFILE; + } + e->length = (apr_size_t)fsize; /* Resize just the last bucket */ + } + else { + e = apr_bucket_file_create(tmpfile, 0, (apr_size_t)fsize, p, + bucket_alloc); + } + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + } + /* This is all a single brigade, pass with flush flagged */ + status = pass_brigade(bucket_alloc, r, p_conn, origin, header_brigade, 1); + return status; +} + +static +apr_status_t ap_proxy_http_request(apr_pool_t *p, request_rec *r, + proxy_http_conn_t *p_conn, conn_rec *origin, + proxy_server_conf *conf, + apr_uri_t *uri, + char *url, + apr_bucket_brigade *header_brigade, + char *server_portstr) +{ + conn_rec *c = r->connection; + apr_bucket_alloc_t *bucket_alloc = c->bucket_alloc; + apr_bucket_brigade *input_brigade; + apr_bucket_brigade *temp_brigade; + apr_bucket *e; + char *buf; + const apr_array_header_t *headers_in_array; + const apr_table_entry_t *headers_in; + int counter; + apr_status_t status; + enum rb_methods {RB_INIT, RB_STREAM_CL, RB_STREAM_CHUNKED, RB_SPOOL_CL}; + enum rb_methods rb_method = RB_INIT; + const char *old_cl_val = NULL; + const char *old_te_val = NULL; + apr_off_t bytes_read = 0; + apr_off_t bytes; + int force10; + + /* + * Send the HTTP/1.1 request to the remote server + */ + + /* strip connection listed hop-by-hop headers from the request */ + /* even though in theory a connection: close coming from the client + * should not affect the connection to the server, it's unlikely + * that subsequent client requests will hit this thread/process, + * so we cancel server keepalive if the client does. + */ + if (ap_proxy_liststr(apr_table_get(r->headers_in, + "Connection"), "close")) { + p_conn->close++; + /* XXX: we are abusing r->headers_in rather than a copy, + * give the core output handler a clue the client would + * rather just close. + */ + c->keepalive = AP_CONN_CLOSE; + } + ap_proxy_clear_connection(p, r->headers_in); + + if (apr_table_get(r->subprocess_env, "force-proxy-request-1.0")) { + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.0" CRLF, NULL); + force10 = 1; + p_conn->close++; + } else { + buf = apr_pstrcat(p, r->method, " ", url, " HTTP/1.1" CRLF, NULL); + force10 = 0; + } + if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { + origin->keepalive = AP_CONN_CLOSE; + p_conn->close++; + } + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + if (conf->preserve_host == 0) { + if (uri->port_str && uri->port != DEFAULT_HTTP_PORT) { + buf = apr_pstrcat(p, "Host: ", uri->hostname, ":", uri->port_str, + CRLF, NULL); + } else { + buf = apr_pstrcat(p, "Host: ", uri->hostname, CRLF, NULL); + } + } + else { + /* don't want to use r->hostname, as the incoming header might have a + * port attached + */ + const char* hostname = apr_table_get(r->headers_in,"Host"); + if (!hostname) { + hostname = r->server->server_hostname; + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "proxy: no HTTP 0.9 request (with no host line) " + "on incoming request and preserve host set " + "forcing hostname to be %s for uri %s", + hostname, + r->uri ); + } + buf = apr_pstrcat(p, "Host: ", hostname, CRLF, NULL); + } + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + + /* handle Via */ + if (conf->viaopt == via_block) { + /* Block all outgoing Via: headers */ + apr_table_unset(r->headers_in, "Via"); + } else if (conf->viaopt != via_off) { + const char *server_name = ap_get_server_name(r); + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, + * then the server name returned by ap_get_server_name() is the + * origin server name (which does make too much sense with Via: headers) + * so we use the proxy vhost's name instead. + */ + if (server_name == r->hostname) + server_name = r->server->server_hostname; + /* Create a "Via:" request header entry and merge it */ + /* Generate outgoing Via: header with/without server comment: */ + apr_table_mergen(r->headers_in, "Via", + (conf->viaopt == via_full) + ? apr_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, server_portstr) + ); + } + + /* X-Forwarded-*: handling + * + * XXX Privacy Note: + * ----------------- + * + * These request headers are only really useful when the mod_proxy + * is used in a reverse proxy configuration, so that useful info + * about the client can be passed through the reverse proxy and on + * to the backend server, which may require the information to + * function properly. + * + * In a forward proxy situation, these options are a potential + * privacy violation, as information about clients behind the proxy + * are revealed to arbitrary servers out there on the internet. + * + * The HTTP/1.1 Via: header is designed for passing client + * information through proxies to a server, and should be used in + * a forward proxy configuation instead of X-Forwarded-*. See the + * ProxyVia option for details. + */ + + if (PROXYREQ_REVERSE == r->proxyreq) { + const char *buf; + + /* Add X-Forwarded-For: so that the upstream has a chance to + * determine, where the original request came from. + */ + apr_table_mergen(r->headers_in, "X-Forwarded-For", + r->connection->remote_ip); + + /* Add X-Forwarded-Host: so that upstream knows what the + * original request hostname was. + */ + if ((buf = apr_table_get(r->headers_in, "Host"))) { + apr_table_mergen(r->headers_in, "X-Forwarded-Host", buf); + } + + /* Add X-Forwarded-Server: so that upstream knows what the + * name of this proxy server is (if there are more than one) + * XXX: This duplicates Via: - do we strictly need it? + */ + apr_table_mergen(r->headers_in, "X-Forwarded-Server", + r->server->server_hostname); + } + + /* send request headers */ + proxy_run_fixups(r); + headers_in_array = apr_table_elts(r->headers_in); + headers_in = (const apr_table_entry_t *) headers_in_array->elts; + for (counter = 0; counter < headers_in_array->nelts; counter++) { + if (headers_in[counter].key == NULL + || headers_in[counter].val == NULL + + /* Already sent */ + || !strcasecmp(headers_in[counter].key, "Host") + + /* Clear out hop-by-hop request headers not to send + * RFC2616 13.5.1 says we should strip these headers + */ + || !strcasecmp(headers_in[counter].key, "Keep-Alive") + || !strcasecmp(headers_in[counter].key, "TE") + || !strcasecmp(headers_in[counter].key, "Trailer") + || !strcasecmp(headers_in[counter].key, "Upgrade") + + /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be + * suppressed if THIS server requested the authentication, + * not when a frontend proxy requested it! + * + * The solution to this problem is probably to strip out + * the Proxy-Authorisation header in the authorisation + * code itself, not here. This saves us having to signal + * somehow whether this request was authenticated or not. + */ + || !strcasecmp(headers_in[counter].key,"Proxy-Authorization") + || !strcasecmp(headers_in[counter].key,"Proxy-Authenticate")) { + continue; + } + + /* Skip Transfer-Encoding and Content-Length for now. + */ + if (!strcasecmp(headers_in[counter].key, "Transfer-Encoding")) { + old_te_val = headers_in[counter].val; + continue; + } + if (!strcasecmp(headers_in[counter].key, "Content-Length")) { + old_cl_val = headers_in[counter].val; + continue; + } + + /* for sub-requests, ignore freshness/expiry headers */ + if (r->main) { + if ( !strcasecmp(headers_in[counter].key, "If-Match") + || !strcasecmp(headers_in[counter].key, "If-Modified-Since") + || !strcasecmp(headers_in[counter].key, "If-Range") + || !strcasecmp(headers_in[counter].key, "If-Unmodified-Since") + || !strcasecmp(headers_in[counter].key, "If-None-Match")) { + continue; + } + } + + buf = apr_pstrcat(p, headers_in[counter].key, ": ", + headers_in[counter].val, CRLF, + NULL); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + } + + /* We have headers, let's figure out our request body... */ + input_brigade = apr_brigade_create(p, bucket_alloc); + + /* sub-requests never use keepalives, and mustn't pass request bodies. + * Because the new logic looks at input_brigade, we will self-terminate + * input_brigade and jump past all of the request body logic... + * Reading anything with ap_get_brigade is likely to consume the + * main request's body or read beyond EOS - which would be unplesant. + */ + if (r->main) { + p_conn->close++; + if (old_cl_val) { + old_cl_val = NULL; + apr_table_unset(r->headers_in, "Content-Length"); + } + if (old_te_val) { + old_te_val = NULL; + apr_table_unset(r->headers_in, "Transfer-Encoding"); + } + rb_method = RB_STREAM_CL; + e = apr_bucket_eos_create(input_brigade->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(input_brigade, e); + goto skip_body; + } + + /* WE only understand chunked. Other modules might inject + * (and therefore, decode) other flavors but we don't know + * that the can and have done so unless they they remove + * their decoding from the headers_in T-E list. + * XXX: Make this extensible, but in doing so, presume the + * encoding has been done by the extensions' handler, and + * do not modify add_te_chunked's logic + */ + if (old_te_val && strcmp(old_te_val, "chunked") != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "proxy: %s Transfer-Encoding is not supported", + old_te_val); + return APR_EINVAL; + } + + if (old_cl_val && old_te_val) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_ENOTIMPL, r->server, + "proxy: client %s (%s) requested Transfer-Encoding body" + " with Content-Length (C-L ignored)", + c->remote_ip, c->remote_host ? c->remote_host: ""); + apr_table_unset(r->headers_in, "Content-Length"); + old_cl_val = NULL; + origin->keepalive = AP_CONN_CLOSE; + p_conn->close++; + } + + /* Prefetch MAX_MEM_SPOOL bytes + * + * This helps us avoid any election of C-L v.s. T-E + * request bodies, since we are willing to keep in + * memory this much data, in any case. This gives + * us an instant C-L election if the body is of some + * reasonable size. + */ + temp_brigade = apr_brigade_create(p, bucket_alloc); + do { + status = ap_get_brigade(r->input_filters, temp_brigade, + AP_MODE_READBYTES, APR_BLOCK_READ, + MAX_MEM_SPOOL - bytes_read); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: prefetch request body failed to %s" + " from %s (%s)", + p_conn->name ? p_conn->name: "", + c->remote_ip, c->remote_host ? c->remote_host: ""); + return status; + } + + apr_brigade_length(temp_brigade, 1, &bytes); + bytes_read += bytes; + + /* + * Save temp_brigade in input_brigade. (At least) in the SSL case + * temp_brigade contains transient buckets whose data would get + * overwritten during the next call of ap_get_brigade in the loop. + * ap_save_brigade ensures these buckets to be set aside. + * Calling ap_save_brigade with NULL as filter is OK, because + * input_brigade already has been created and does not need to get + * created by ap_save_brigade. + */ + status = ap_save_brigade(NULL, &input_brigade, &temp_brigade, p); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: processing prefetched request body failed" + " to %s from %s (%s)", + p_conn->name ? p_conn->name: "", + c->remote_ip, c->remote_host ? c->remote_host: ""); + return status; + } + + /* Ensure we don't hit a wall where we have a buffer too small + * for ap_get_brigade's filters to fetch us another bucket, + * surrender once we hit 80 bytes less than MAX_MEM_SPOOL + * (an arbitrary value.) + */ + } while ((bytes_read < MAX_MEM_SPOOL - 80) + && !APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))); + + /* Use chunked request body encoding or send a content-length body? + * + * Prefer C-L when: + * + * We have no request body (handled by RB_STREAM_CL) + * + * We have a request body length <= MAX_MEM_SPOOL + * + * The administrator has setenv force-proxy-request-1.0 + * + * The client sent a C-L body, and the administrator has + * not setenv proxy-sendchunked or has set setenv proxy-sendcl + * + * The client sent a T-E body, and the administrator has + * setenv proxy-sendcl, and not setenv proxy-sendchunked + * + * If both proxy-sendcl and proxy-sendchunked are set, the + * behavior is the same as if neither were set, large bodies + * that can't be read will be forwarded in their original + * form of C-L, or T-E. + * + * To ensure maximum compatibility, setenv proxy-sendcl + * To reduce server resource use, setenv proxy-sendchunked + * + * Then address specific servers with conditional setenv + * options to restore the default behavior where desireable. + * + * We have to compute content length by reading the entire request + * body; if request body is not small, we'll spool the remaining + * input to a temporary file. Chunked is always preferable. + * + * We can only trust the client-provided C-L if the T-E header + * is absent, and the filters are unchanged (the body won't + * be resized by another content filter). + */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(input_brigade))) { + /* The whole thing fit, so our decision is trivial, use + * the filtered bytes read from the client for the request + * body Content-Length. + * + * If we expected no body, and read no body, do not set + * the Content-Length. + */ + if (old_cl_val || old_te_val || bytes_read) { + old_cl_val = apr_off_t_toa(r->pool, bytes_read); + } + rb_method = RB_STREAM_CL; + } + else if (old_te_val) { + if (force10 + || (apr_table_get(r->subprocess_env, "proxy-sendcl") + && !apr_table_get(r->subprocess_env, "proxy-sendchunks") + && !apr_table_get(r->subprocess_env, "proxy-sendchunked"))) { + rb_method = RB_SPOOL_CL; + } + else { + rb_method = RB_STREAM_CHUNKED; + } + } + else if (old_cl_val) { + if (r->input_filters == r->proto_input_filters) { + rb_method = RB_STREAM_CL; + } + else if (!force10 + && (apr_table_get(r->subprocess_env, "proxy-sendchunks") + || apr_table_get(r->subprocess_env, "proxy-sendchunked")) + && !apr_table_get(r->subprocess_env, "proxy-sendcl")) { + rb_method = RB_STREAM_CHUNKED; + } + else { + rb_method = RB_SPOOL_CL; + } + } + else { + /* This is an appropriate default; very efficient for no-body + * requests, and has the behavior that it will not add any C-L + * when the old_cl_val is NULL. + */ + rb_method = RB_SPOOL_CL; + } + +/* Yes I hate gotos. This is the subrequest shortcut */ +skip_body: + /* Handle Connection: header */ + if (!force10 && p_conn->close) { + buf = apr_pstrdup(p, "Connection: close" CRLF); + ap_xlate_proto_to_ascii(buf, strlen(buf)); + e = apr_bucket_pool_create(buf, strlen(buf), p, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(header_brigade, e); + } + + /* send the request body, if any. */ + switch(rb_method) { + case RB_STREAM_CHUNKED: + status = stream_reqbody_chunked(p, r, p_conn, origin, header_brigade, + input_brigade); + break; + case RB_STREAM_CL: + status = stream_reqbody_cl(p, r, p_conn, origin, header_brigade, + input_brigade, old_cl_val); + break; + case RB_SPOOL_CL: + status = spool_reqbody_cl(p, r, p_conn, origin, header_brigade, + input_brigade, (old_cl_val != NULL) + || (old_te_val != NULL) + || (bytes_read > 0)); + break; + default: + ap_assert(1 != 1); + break; + } + + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: pass request body failed to %pI (%s)" + " from %s (%s)", + p_conn->addr, p_conn->name ? p_conn->name: "", + c->remote_ip, c->remote_host ? c->remote_host: ""); + return status; + } + + return APR_SUCCESS; +} + +static int addit_dammit(void *v, const char *key, const char *val) +{ + apr_table_addn(v, key, val); + return 1; +} + +/* + * Limit the number of interim respones we sent back to the client. Otherwise + * we suffer from a memory build up. Besides there is NO sense in sending back + * an unlimited number of interim responses to the client. Thus if we cross + * this limit send back a 502 (Bad Gateway). + */ +#ifndef AP_MAX_INTERIM_RESPONSES +#define AP_MAX_INTERIM_RESPONSES 10 +#endif + +static +apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, + proxy_http_conn_t *p_conn, + conn_rec *origin, + proxy_conn_rec *backend, + proxy_server_conf *conf, + apr_bucket_brigade *bb, + char *server_portstr) { + conn_rec *c = r->connection; + char buffer[HUGE_STRING_LEN]; + const char *buf; + char keepchar; + request_rec *rp; + apr_bucket *e; + apr_table_t *save_table; + int len, backasswards; + int received_continue = 1; /* flag to indicate if we should + * loop over response parsing logic + * in the case that the origin told us + * to HTTP_CONTINUE + */ + + /* Get response from the remote server, and pass it up the + * filter chain + */ + + rp = ap_proxy_make_fake_req(origin, r); + /* In case anyone needs to know, this is a fake request that is really a + * response. + */ + rp->proxyreq = PROXYREQ_RESPONSE; + + while (received_continue && (received_continue <= AP_MAX_INTERIM_RESPONSES)) { + apr_brigade_cleanup(bb); + + len = ap_getline(buffer, sizeof(buffer), rp, 0); + if (len == 0) { + /* handle one potential stray CRLF */ + len = ap_getline(buffer, sizeof(buffer), rp, 0); + } + if (len <= 0) { + apr_socket_close(p_conn->sock); + backend->connection = NULL; + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "proxy: error reading status line from remote " + "server %s", p_conn->name); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + + /* Is it an HTTP/1 response? + * This is buggy if we ever see an HTTP/1.10 + */ + if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + int major, minor; + + if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) { + major = 1; + minor = 1; + } + /* If not an HTTP/1 message or + * if the status line was > 8192 bytes + */ + else if ((buffer[5] != '1') || (len >= sizeof(buffer)-1)) { + apr_socket_close(p_conn->sock); + backend->connection = NULL; + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_pstrcat(p, "Corrupt status line returned by remote " + "server: ", buffer, NULL)); + } + backasswards = 0; + + keepchar = buffer[12]; + buffer[12] = '\0'; + r->status = atoi(&buffer[9]); + + if (keepchar != '\0') { + buffer[12] = keepchar; + } else { + /* 2616 requires the space in Status-Line; the origin + * server may have sent one but ap_rgetline_core will + * have stripped it. */ + buffer[12] = ' '; + buffer[13] = '\0'; + } + r->status_line = apr_pstrdup(p, &buffer[9]); + + /* read the headers. */ + /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers*/ + /* Also, take care with headers with multiple occurences. */ + + /* First, tuck away all already existing cookies */ + save_table = apr_table_make(r->pool, 2); + apr_table_do(addit_dammit, save_table, r->headers_out, + "Set-Cookie", NULL); + + r->headers_out = ap_proxy_read_headers(r, rp, buffer, + sizeof(buffer), origin); + if (r->headers_out == NULL) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, + r->server, "proxy: bad HTTP/%d.%d header " + "returned by %s (%s)", major, minor, r->uri, + r->method); + p_conn->close += 1; + /* + * ap_send_error relies on a headers_out to be present. we + * are in a bad position here.. so force everything we send out + * to have nothing to do with the incoming packet + */ + r->headers_out = apr_table_make(r->pool,1); + r->status = HTTP_BAD_GATEWAY; + r->status_line = "bad gateway"; + return r->status; + } + + /* Now, add in the just read cookies */ + apr_table_do(addit_dammit, save_table, r->headers_out, + "Set-Cookie", NULL); + + /* and now load 'em all in */ + if (!apr_is_empty_table(save_table)) { + apr_table_unset(r->headers_out, "Set-Cookie"); + r->headers_out = apr_table_overlay(r->pool, + r->headers_out, + save_table); + } + + /* can't have both Content-Length and Transfer-Encoding */ + if (apr_table_get(r->headers_out, "Transfer-Encoding") + && apr_table_get(r->headers_out, "Content-Length")) { + /* 2616 section 4.4, point 3: "if both Transfer-Encoding + * and Content-Length are received, the latter MUST be + * ignored"; so unset it here to prevent any confusion + * later. */ + apr_table_unset(r->headers_out, "Content-Length"); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, + "proxy: server %s returned Transfer-Encoding and Content-Length", + p_conn->name); + p_conn->close += 1; + } + + /* strip connection listed hop-by-hop headers from response */ + p_conn->close += ap_proxy_liststr(apr_table_get(r->headers_out, + "Connection"), + "close"); + ap_proxy_clear_connection(p, r->headers_out); + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { + ap_set_content_type(r, apr_pstrdup(p, buf)); + } + if (!ap_is_HTTP_INFO(r->status)) { + ap_proxy_pre_http_request(origin, rp); + } + + /* handle Via header in response */ + if (conf->viaopt != via_off && conf->viaopt != via_block) { + const char *server_name = ap_get_server_name(r); + /* If USE_CANONICAL_NAME_OFF was configured for the proxy virtual host, + * then the server name returned by ap_get_server_name() is the + * origin server name (which does make too much sense with Via: headers) + * so we use the proxy vhost's name instead. + */ + if (server_name == r->hostname) + server_name = r->server->server_hostname; + + /* create a "Via:" response header entry and merge it */ + apr_table_mergen(r->headers_out, "Via", + (conf->viaopt == via_full) + ? apr_psprintf(p, "%d.%d %s%s (%s)", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, + server_portstr, + AP_SERVER_BASEVERSION) + : apr_psprintf(p, "%d.%d %s%s", + HTTP_VERSION_MAJOR(r->proto_num), + HTTP_VERSION_MINOR(r->proto_num), + server_name, + server_portstr) + ); + } + + /* cancel keepalive if HTTP/1.0 or less */ + if ((major < 1) || (minor < 1)) { + p_conn->close += 1; + origin->keepalive = AP_CONN_CLOSE; + } + } else { + /* an http/0.9 response */ + backasswards = 1; + r->status = 200; + r->status_line = "200 OK"; + p_conn->close += 1; + } + + if ( r->status != HTTP_CONTINUE ) { + received_continue = 0; + } else { + received_continue++; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "proxy: HTTP: received 100 CONTINUE"); + } + + /* we must accept 3 kinds of date, but generate only 1 kind of date */ + if ((buf = apr_table_get(r->headers_out, "Date")) != NULL) { + apr_table_set(r->headers_out, "Date", + ap_proxy_date_canon(p, buf)); + } + if ((buf = apr_table_get(r->headers_out, "Expires")) != NULL) { + apr_table_set(r->headers_out, "Expires", + ap_proxy_date_canon(p, buf)); + } + if ((buf = apr_table_get(r->headers_out, "Last-Modified")) != NULL) { + apr_table_set(r->headers_out, "Last-Modified", + ap_proxy_date_canon(p, buf)); + } + + /* munge the Location and URI response headers according to + * ProxyPassReverse + */ + if ((buf = apr_table_get(r->headers_out, "Location")) != NULL) { + apr_table_set(r->headers_out, "Location", + ap_proxy_location_reverse_map(r, conf, buf)); + } + if ((buf = apr_table_get(r->headers_out, "Content-Location")) != NULL) { + apr_table_set(r->headers_out, "Content-Location", + ap_proxy_location_reverse_map(r, conf, buf)); + } + if ((buf = apr_table_get(r->headers_out, "URI")) != NULL) { + apr_table_set(r->headers_out, "URI", + ap_proxy_location_reverse_map(r, conf, buf)); + } + + if ((r->status == 401) && (conf->error_override != 0)) { + const char *wa = "WWW-Authenticate"; + if ((buf = apr_table_get(r->headers_out, wa))) { + apr_table_set(r->err_headers_out, wa, buf); + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: origin server sent 401 without WWW-Authenticate header"); + } + } + + r->sent_bodyct = 1; + /* Is it an HTTP/0.9 response? If so, send the extra data */ + if (backasswards) { + apr_ssize_t cntr = len; + e = apr_bucket_heap_create(buffer, cntr, NULL, c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + + /* send body - but only if a body is expected */ + if ((!r->header_only) && /* not HEAD request */ + (r->status > 199) && /* not any 1xx response */ + (r->status != HTTP_NO_CONTENT) && /* not 204 */ + (r->status != HTTP_RESET_CONTENT) && /* not 205 */ + (r->status != HTTP_NOT_MODIFIED)) { /* not 304 */ + + /* We need to copy the output headers and treat them as input + * headers as well. BUT, we need to do this before we remove + * TE, so that they are preserved accordingly for + * ap_http_filter to know where to end. + */ + rp->headers_in = apr_table_copy(r->pool, r->headers_out); + + apr_table_unset(r->headers_out,"Transfer-Encoding"); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: start body send"); + + /* + * if we are overriding the errors, we can't put the content + * of the page into the brigade + */ + if ( (conf->error_override ==0) || r->status < 400 ) { + + /* read the body, pass it to the output filters */ + int finish = FALSE; + while (ap_get_brigade(rp->input_filters, + bb, + AP_MODE_READBYTES, + APR_BLOCK_READ, + conf->io_buffer_size) == APR_SUCCESS) { +#if DEBUGGING + { + apr_off_t readbytes; + apr_brigade_length(bb, 0, &readbytes); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, "proxy (PID %d): readbytes: %#x", + getpid(), readbytes); + } +#endif + /* sanity check */ + if (APR_BRIGADE_EMPTY(bb)) { + apr_brigade_cleanup(bb); + break; + } + + /* found the last brigade? */ + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + /* if this is the last brigade, cleanup the + * backend connection first to prevent the + * backend server from hanging around waiting + * for a slow client to eat these bytes + */ + ap_proxy_http_cleanup(r, p_conn, backend); + /* signal that we must leave */ + finish = TRUE; + } + + /* try send what we read */ + if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS + || c->aborted) { + /* Ack! Phbtt! Die! User aborted! */ + p_conn->close = 1; /* this causes socket close below */ + finish = TRUE; + } + + /* make sure we always clean up after ourselves */ + apr_brigade_cleanup(bb); + + /* if we are done, leave */ + if (TRUE == finish) { + break; + } + } + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: end body send"); + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: header only"); + } + } + + /* See define of AP_MAX_INTERIM_RESPONSES for why */ + if (received_continue > AP_MAX_INTERIM_RESPONSES) { + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + apr_psprintf(p, + "Too many (%d) interim responses from origin server", + received_continue)); + } + + if ( conf->error_override ) { + /* the code above this checks for 'OK' which is what the hook expects */ + if ( r->status == HTTP_OK ) + return OK; + else { + /* clear r->status for override error, otherwise ErrorDocument + * thinks that this is a recursive error, and doesn't find the + * custom error page + */ + int status = r->status; + r->status = HTTP_OK; + /* Discard body, if one is expected */ + if ((status > 199) && /* not any 1xx response */ + (status != HTTP_NO_CONTENT) && /* not 204 */ + (status != HTTP_RESET_CONTENT) && /* not 205 */ + (status != HTTP_NOT_MODIFIED)) { /* not 304 */ + ap_discard_request_body(rp); + } + return status; + } + } else + return OK; +} + +static +apr_status_t ap_proxy_http_cleanup(request_rec *r, proxy_http_conn_t *p_conn, + proxy_conn_rec *backend) { + /* If there are no KeepAlives, or if the connection has been signalled + * to close, close the socket and clean up + */ + + /* if the connection is < HTTP/1.1, or Connection: close, + * we close the socket, otherwise we leave it open for KeepAlive support + */ + if (p_conn->close || (r->proto_num < HTTP_VERSION(1,1))) { + if (p_conn->sock) { + apr_socket_close(p_conn->sock); + p_conn->sock = NULL; + backend->connection = NULL; + } + } + return OK; +} + +/* + * This handles http:// URLs, and other URLs using a remote proxy over http + * If proxyhost is NULL, then contact the server directly, otherwise + * go via the proxy. + * Note that if a proxy is used, then URLs other than http: can be accessed, + * also, if we have trouble which is clearly specific to the proxy, then + * we return DECLINED so that we can try another proxy. (Or the direct + * route.) + */ +int ap_proxy_http_handler(request_rec *r, proxy_server_conf *conf, + char *url, const char *proxyname, + apr_port_t proxyport) +{ + int status; + char server_portstr[32]; + conn_rec *origin = NULL; + proxy_conn_rec *backend = NULL; + int is_ssl = 0; + + /* Note: Memory pool allocation. + * A downstream keepalive connection is always connected to the existence + * (or not) of an upstream keepalive connection. If this is not done then + * load balancing against multiple backend servers breaks (one backend + * server ends up taking 100% of the load), and the risk is run of + * downstream keepalive connections being kept open unnecessarily. This + * keeps webservers busy and ties up resources. + * + * As a result, we allocate all sockets out of the upstream connection + * pool, and when we want to reuse a socket, we check first whether the + * connection ID of the current upstream connection is the same as that + * of the connection when the socket was opened. + */ + apr_pool_t *p = r->connection->pool; + conn_rec *c = r->connection; + apr_bucket_brigade *bb = apr_brigade_create(p, c->bucket_alloc); + apr_uri_t *uri = apr_palloc(r->connection->pool, sizeof(*uri)); + proxy_http_conn_t *p_conn = apr_pcalloc(r->connection->pool, + sizeof(*p_conn)); + + /* is it for us? */ + if (strncasecmp(url, "https:", 6) == 0) { + if (!ap_proxy_ssl_enable(NULL)) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: HTTPS: declining URL %s" + " (mod_ssl not configured?)", url); + return DECLINED; + } + is_ssl = 1; + } + else if (!(strncasecmp(url, "http:", 5)==0 || (strncasecmp(url, "ftp:", 4)==0 && proxyname))) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: HTTP: declining URL %s", url); + return DECLINED; /* only interested in HTTP, or FTP via proxy */ + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: HTTP: serving URL %s", url); + + + /* only use stored info for top-level pages. Sub requests don't share + * in keepalives + */ + if (!r->main) { + backend = (proxy_conn_rec *) ap_get_module_config(c->conn_config, + &proxy_http_module); + } + /* create space for state information */ + if (!backend) { + backend = apr_pcalloc(c->pool, sizeof(proxy_conn_rec)); + backend->connection = NULL; + backend->hostname = NULL; + backend->port = 0; + if (!r->main) { + ap_set_module_config(c->conn_config, &proxy_http_module, backend); + } + } + + backend->is_ssl = is_ssl; + + /* Step One: Determine Who To Connect To */ + status = ap_proxy_http_determine_connection(p, r, p_conn, c, conf, uri, + &url, proxyname, proxyport, + server_portstr, + sizeof(server_portstr)); + if ( status != OK ) { + return status; + } + + /* Step Two: Make the Connection */ + status = ap_proxy_http_create_connection(p, r, p_conn, c, &origin, backend, + conf, proxyname); + if ( status != OK ) { + return status; + } + + /* Step Three: Send the Request */ + status = ap_proxy_http_request(p, r, p_conn, origin, conf, uri, url, bb, + server_portstr); + if ( status != OK ) { + return status; + } + + /* Step Four: Receive the Response */ + status = ap_proxy_http_process_response(p, r, p_conn, origin, backend, conf, + bb, server_portstr); + if ( status != OK ) { + /* clean up even if there is an error */ + ap_proxy_http_cleanup(r, p_conn, backend); + return status; + } + + /* Step Five: Clean Up */ + status = ap_proxy_http_cleanup(r, p_conn, backend); + if ( status != OK ) { + return status; + } + + return OK; +} + +static void ap_proxy_http_register_hook(apr_pool_t *p) +{ + proxy_hook_scheme_handler(ap_proxy_http_handler, NULL, NULL, APR_HOOK_FIRST); + proxy_hook_canon_handler(ap_proxy_http_canon, NULL, NULL, APR_HOOK_FIRST); +} + +module AP_MODULE_DECLARE_DATA proxy_http_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command apr_table_t */ + ap_proxy_http_register_hook/* register hooks */ +}; + diff --git a/rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c new file mode 100644 index 00000000..bab945f5 --- /dev/null +++ b/rubbos/app/httpd-2.0.64/modules/proxy/proxy_util.c @@ -0,0 +1,1120 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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" + + +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 && (isenc != PROXYREQ_REVERSE) && (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; +} + +/* + * If the date is a valid RFC 850 date or asctime() date, then it + * is converted to the RFC 1123 format. + */ +PROXY_DECLARE(const char *) + ap_proxy_date_canon(apr_pool_t *p, const char *date) +{ + apr_status_t rv; + char* ndate; + + apr_time_t time = apr_date_parse_http(date); + if (!time) { + return date; + } + + ndate = apr_palloc(p, APR_RFC822_DATE_LEN); + rv = apr_rfc822_date(ndate, time); + if (rv != APR_SUCCESS) { + return date; + } + + return ndate; +} + +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; +} + +/* + * Reads headers from a buffer and returns an array of headers. + * Returns NULL on file error + * This routine tries to deal with too long lines and continuation lines. + * + * Note: Currently the headers are passed through unmerged. This has to be + * done so that headers which react badly to merging (such as Set-Cookie + * headers, which contain commas within the date field) do not get stuffed + * up. + */ +PROXY_DECLARE(apr_table_t *)ap_proxy_read_headers(request_rec *r, request_rec *rr, char *buffer, int size, conn_rec *c) +{ + apr_table_t *headers_out; + int len; + char *value, *end; + char field[MAX_STRING_LEN]; + int saw_headers = 0; + void *sconf = r->server->module_config; + proxy_server_conf *psc; + + psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); + + headers_out = apr_table_make(r->pool, 20); + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), or we timeout. + */ + while ((len = ap_getline(buffer, size, rr, 1)) > 0) { + + if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ + + /* We may encounter invalid headers, usually from buggy + * MS IIS servers, so we need to determine just how to handle + * them. We can either ignore them, assume that they mark the + * start-of-body (eg: a missing CRLF) or (the default) mark + * the headers as totally bogus and return a 500. The sole + * exception is an extra "HTTP/1.0 200, OK" line sprinkled + * in between the usual MIME headers, which is a favorite + * IIS bug. + */ + /* XXX: The mask check is buggy if we ever see an HTTP/1.10 */ + + if (!apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + if (psc->badopt == bad_error) { + /* Nope, it wasn't even an extra HTTP header. Give up. */ + return NULL; + } + else if (psc->badopt == bad_body) { + /* if we've already started loading headers_out, then + * return what we've accumulated so far, in the hopes + * that they are useful. Otherwise, we completely bail. + */ + /* FIXME: We've already scarfed the supposed 1st line of + * the body, so the actual content may end up being bogus + * as well. If the content is HTML, we may be lucky. + */ + if (saw_headers) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: Starting body due to bogus non-header in headers " + "returned by %s (%s)", r->uri, r->method); + return headers_out; + } else { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: No HTTP headers " + "returned by %s (%s)", r->uri, r->method); + return NULL; + } + } + } + /* this is the psc->badopt == bad_ignore case */ + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: Ignoring bogus HTTP header " + "returned by %s (%s)", r->uri, r->method); + continue; + } + + *value = '\0'; + ++value; + /* XXX: RFC2068 defines only SP and HT as whitespace, this test is + * wrong... and so are many others probably. + */ + while (apr_isspace(*value)) + ++value; /* Skip to start of value */ + + /* should strip trailing whitespace as well */ + for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end) + *end = '\0'; + + /* make sure we add so as not to destroy duplicated headers */ + apr_table_add(headers_out, buffer, value); + saw_headers = 1; + + /* the header was too long; at the least we should skip extra data */ + if (len >= size - 1) { + while ((len = ap_getline(field, MAX_STRING_LEN, rr, 1)) + >= MAX_STRING_LEN - 1) { + /* soak up the extra data */ + } + if (len == 0) /* time to exit the larger loop as well */ + break; + } + } + return headers_out; +} + + +/* + * 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", + inet_ntoa(This->addr), bits); + } + + This->mask.s_addr = htonl(APR_INADDR_NONE << (32 - bits)); + + if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "Warning: NetMask and IP-Addr disagree in %s/%ld", + inet_ntoa(This->addr), bits); + This->addr.s_addr &= This->mask.s_addr; + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + " Set to %s/%ld", + inet_ntoa(This->addr), bits); + } + + if (*addr == '\0') { + This->matcher = proxy_match_ipaddr; + return 1; + } + else + return (*addr == '\0'); /* okay iff we've parsed the whole string */ +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) +{ + int i, ip_addr[4]; + struct in_addr addr, *ip; + const char *host = proxy_get_host_of_request(r); + + if (host == NULL) /* oops! */ + return 0; + + memset(&addr, '\0', sizeof addr); + memset(ip_addr, '\0', sizeof ip_addr); + + if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { + for (addr.s_addr = 0, i = 0; i < 4; ++i) + addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + + if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s", inet_ntoa(This->mask)); + } +#endif + } + else { + struct apr_sockaddr_t *reqaddr; + + if (apr_sockaddr_info_get(&reqaddr, host, APR_UNSPEC, 0, 0, r->pool) + != APR_SUCCESS) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "2)IP-NoMatch: hostname=%s msg=Host not found", + host); +#endif + return 0; + } + + /* Try to deal with multiple IP addr's for a host */ + /* FIXME: This needs to be able to deal with IPv6 */ + while (reqaddr) { + ip = (struct in_addr *) reqaddr->ipaddr_ptr; + if (This->addr.s_addr == (ip->s_addr & This->mask.s_addr)) { +#if DEBUGGING + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "3)IP-Match: %s[%s] <-> ", host, + inet_ntoa(*ip)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "3)IP-NoMatch: %s[%s] <-> ", host, + inet_ntoa(*ip)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s/", inet_ntoa(This->addr)); + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "%s", inet_ntoa(This->mask)); + } +#endif + reqaddr = reqaddr->next; + } + } + + return 0; +} + +/* Return TRUE if addr represents a domain name */ +PROXY_DECLARE(int) ap_proxy_is_domainname(struct dirconn_entry *This, apr_pool_t *p) +{ + char *addr = This->name; + int i; + + /* Domain name must start with a '.' */ + if (addr[0] != '.') + return 0; + + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; apr_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) + continue; + +#if 0 + if (addr[i] == ':') { + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "@@@@ handle optional port in proxy_is_domainname()"); + /* @@@@ handle optional port */ + } +#endif + + if (addr[i] != '\0') + return 0; + + /* Strip trailing dots */ + for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) + addr[i] = '\0'; + + This->matcher = proxy_match_domainname; + return 1; +} + +/* Return TRUE if host "host" is in domain "domain" */ +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + int d_len = strlen(This->name), h_len; + + if (host == NULL) /* some error was logged already */ + return 0; + + h_len = strlen(host); + + /* @@@ do this within the setup? */ + /* Ignore trailing dots in domain comparison: */ + while (d_len > 0 && This->name[d_len - 1] == '.') + --d_len; + while (h_len > 0 && host[h_len - 1] == '.') + --h_len; + return h_len > d_len + && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; +} + +/* Return TRUE if host represents a host name */ +PROXY_DECLARE(int) ap_proxy_is_hostname(struct dirconn_entry *This, apr_pool_t *p) +{ + struct apr_sockaddr_t *addr; + char *host = This->name; + int i; + + /* Host names must not start with a '.' */ + if (host[0] == '.') + return 0; + + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; apr_isalnum(host[i]) || host[i] == '-' || host[i] == '.'; ++i); + + if (host[i] != '\0' || apr_sockaddr_info_get(&addr, host, APR_UNSPEC, 0, 0, p) != APR_SUCCESS) + return 0; + + This->hostaddr = addr; + + /* Strip trailing dots */ + for (i = strlen(host) - 1; i > 0 && host[i] == '.'; --i) + host[i] = '\0'; + + This->matcher = proxy_match_hostname; + return 1; +} + +/* Return TRUE if host "host" is equal to host2 "host2" */ +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) +{ + char *host = This->name; + const char *host2 = proxy_get_host_of_request(r); + int h2_len; + int h1_len; + + if (host == NULL || host2 == NULL) + return 0; /* oops! */ + + h2_len = strlen(host2); + h1_len = strlen(host); + +#if 0 + struct apr_sockaddr_t *addr = *This->hostaddr; + + /* Try to deal with multiple IP addr's for a host */ + while (addr) { + if (addr->ipaddr_ptr == ? ? ? ? ? ? ? ? ? ? ? ? ?) + return 1; + addr = addr->next; + } +#endif + + /* Ignore trailing dots in host2 comparison: */ + while (h2_len > 0 && host2[h2_len - 1] == '.') + --h2_len; + while (h1_len > 0 && host[h1_len - 1] == '.') + --h1_len; + return h1_len == h2_len + && strncasecmp(host, host2, h1_len) == 0; +} + +/* Return TRUE if addr is to be matched as a word */ +PROXY_DECLARE(int) ap_proxy_is_word(struct dirconn_entry *This, apr_pool_t *p) +{ + This->matcher = proxy_match_word; + return 1; +} + +/* Return TRUE if string "str2" occurs literally in "str1" */ +static int proxy_match_word(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + return host != NULL && ap_strstr_c(host, This->name) != NULL; +} + +/* checks whether a host in uri_addr matches proxyblock */ +PROXY_DECLARE(int) ap_proxy_checkproxyblock(request_rec *r, proxy_server_conf *conf, + apr_sockaddr_t *uri_addr) +{ + int j; + apr_sockaddr_t * src_uri_addr = uri_addr; + /* XXX FIXME: conf->noproxies->elts is part of an opaque structure */ + for (j = 0; j < conf->noproxies->nelts; j++) { + struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts; + struct apr_sockaddr_t *conf_addr = npent[j].addr; + uri_addr = src_uri_addr; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: checking remote machine [%s] against [%s]", uri_addr->hostname, npent[j].name); + if ((npent[j].name && ap_strstr_c(uri_addr->hostname, npent[j].name)) + || npent[j].name[0] == '*') { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: connect to remote machine %s blocked: name %s matched", uri_addr->hostname, npent[j].name); + return HTTP_FORBIDDEN; + } + while (conf_addr) { + while (uri_addr) { + char *conf_ip; + char *uri_ip; + apr_sockaddr_ip_get(&conf_ip, conf_addr); + apr_sockaddr_ip_get(&uri_ip, uri_addr); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: ProxyBlock comparing %s and %s", conf_ip, uri_ip); + if (!apr_strnatcasecmp(conf_ip, uri_ip)) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server, + "proxy: connect to remote machine %s blocked: IP %s matched", uri_addr->hostname, conf_ip); + return HTTP_FORBIDDEN; + } + uri_addr = uri_addr->next; + } + conf_addr = conf_addr->next; + } + } + return OK; +} + +/* set up the minimal filter set */ +PROXY_DECLARE(int) ap_proxy_pre_http_request(conn_rec *c, request_rec *r) +{ + ap_add_input_filter("HTTP_IN", NULL, r, c); + return OK; +} + +/* converts a series of buckets into a string + * XXX: BillS says this function performs essentially the same function as + * ap_rgetline() in protocol.c. Deprecate this function and use ap_rgetline() + * instead? I think ap_proxy_string_read() will not work properly on non ASCII + * (EBCDIC) machines either. + */ +PROXY_DECLARE(apr_status_t) ap_proxy_string_read(conn_rec *c, apr_bucket_brigade *bb, + char *buff, apr_size_t bufflen, int *eos) +{ + apr_bucket *e; + apr_status_t rv; + char *pos = buff; + char *response; + int found = 0; + apr_size_t len; + + /* start with an empty string */ + buff[0] = 0; + *eos = 0; + + /* loop through each brigade */ + while (!found) { + /* get brigade from network one line at a time */ + if (APR_SUCCESS != (rv = ap_get_brigade(c->input_filters, bb, + AP_MODE_GETLINE, + APR_BLOCK_READ, + 0))) { + return rv; + } + /* loop through each bucket */ + while (!found) { + if (*eos || APR_BRIGADE_EMPTY(bb)) { + /* The connection aborted or timed out */ + return APR_ECONNABORTED; + } + e = APR_BRIGADE_FIRST(bb); + if (APR_BUCKET_IS_EOS(e)) { + *eos = 1; + } + else { + if (APR_SUCCESS != apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ)) { + return rv; + } + /* is string LF terminated? + * XXX: This check can be made more efficient by simply checking + * if the last character in the 'response' buffer is an ASCII_LF. + * See ap_rgetline() for an example. + */ + if (memchr(response, APR_ASCII_LF, len)) { + found = 1; + } + /* concat strings until buff is full - then throw the data away */ + if (len > ((bufflen-1)-(pos-buff))) { + len = (bufflen-1)-(pos-buff); + } + if (len > 0) { + pos = apr_cpystrn(pos, response, len); + } + } + APR_BUCKET_REMOVE(e); + apr_bucket_destroy(e); + } + } + + return APR_SUCCESS; +} + +/* unmerge an element in the table */ +PROXY_DECLARE(void) ap_proxy_table_unmerge(apr_pool_t *p, apr_table_t *t, char *key) +{ + apr_off_t offset = 0; + apr_off_t count = 0; + char *value = NULL; + + /* get the value to unmerge */ + const char *initial = apr_table_get(t, key); + if (!initial) { + return; + } + value = apr_pstrdup(p, initial); + + /* remove the value from the headers */ + apr_table_unset(t, key); + + /* find each comma */ + while (value[count]) { + if (value[count] == ',') { + value[count] = 0; + apr_table_add(t, key, value + offset); + offset = count + 1; + } + count++; + } + apr_table_add(t, key, value + offset); +} + +PROXY_DECLARE(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, 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_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; +} + |