From 6f96ad52275b5b35226cdb2ce66b3832e9dfb605 Mon Sep 17 00:00:00 2001 From: "Roy T. Fielding" Date: Tue, 24 Aug 1999 06:46:03 +0000 Subject: [PATCH] Apache 1.3.9 baseline for the Apache 2.0 repository. Obtained from: Apache 1.3.9 (minus unused files), tag APACHE_1_3_9 Submitted by: Apache Group git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@83750 13f79535-47bb-0310-9956-ffa450edef68 --- docs/man/ab.8 | 210 + docs/man/apachectl.8 | 133 + docs/man/apxs.8 | 459 ++ docs/man/dbmmanage.1 | 171 + docs/man/htdigest.1 | 97 + docs/man/htpasswd.1 | 213 + docs/man/httpd.8 | 211 + docs/man/logresolve.8 | 87 + docs/man/rotatelogs.8 | 83 + docs/man/suexec.8 | 70 + modules/aaa/.indent.pro | 54 + modules/aaa/mod_auth_anon.dsp | 113 + modules/arch/win32/mod_isapi.c | 569 +++ modules/cache/.indent.pro | 54 + modules/echo/.indent.pro | 54 + modules/experimental/.indent.pro | 54 + modules/filters/.indent.pro | 54 + modules/generators/.indent.pro | 54 + modules/generators/mod_info.dsp | 112 + modules/generators/mod_status.dsp | 113 + modules/http/.indent.pro | 54 + modules/http/http_core.c | 3214 ++++++++++++++ modules/http/http_protocol.c | 2763 ++++++++++++ modules/http/http_request.c | 1374 ++++++ modules/loggers/.indent.pro | 54 + modules/mappers/.indent.pro | 54 + modules/mappers/mod_rewrite.dsp | 117 + modules/mappers/mod_speling.dsp | 113 + modules/metadata/.indent.pro | 54 + modules/metadata/mod_cern_meta.dsp | 113 + modules/metadata/mod_expires.dsp | 113 + modules/metadata/mod_headers.dsp | 113 + modules/metadata/mod_usertrack.dsp | 113 + modules/ssl/.indent.pro | 54 + os/.indent.pro | 54 + os/bs2000/.cvsignore | 1 + os/bs2000/bs2login.c | 297 ++ os/bs2000/ebcdic.c | 252 ++ os/bs2000/ebcdic.h | 8 + os/bs2000/os-inline.c | 31 + os/bs2000/os.c | 103 + os/bs2000/os.h | 51 + os/os2/.cvsignore | 1 + os/os2/os-inline.c | 34 + os/os2/os.h | 52 + os/os2/util_os2.c | 96 + os/tpf/TPFExport | 7 + os/tpf/ebcdic.c | 221 + os/tpf/ebcdic.h | 8 + os/tpf/os-inline.c | 31 + os/tpf/os.c | 414 ++ os/tpf/os.h | 108 + os/tpf/samples/linkdll.jcl | 121 + os/tpf/samples/loadset.jcl | 58 + os/unix/.cvsignore | 1 + os/unix/os-inline.c | 31 + os/unix/os.h | 147 + os/win32/.cvsignore | 27 + os/win32/MakeModuleMak.cpp | 59 + os/win32/Module.mak.tmpl | 230 + os/win32/mod_isapi.c | 569 +++ os/win32/modules.c | 72 + os/win32/os.h | 126 + os/win32/util_win32.c | 677 +++ server/.cvsignore | 13 + server/.indent.pro | 54 + server/config.c | 1641 +++++++ server/gen_test_char.c | 62 + server/gen_test_char.dsp | 105 + server/gen_uri_delims.c | 31 + server/gen_uri_delims.dsp | 105 + server/log.c | 777 ++++ server/main.c | 6669 ++++++++++++++++++++++++++++ server/mpm/winnt/registry.c | 471 ++ server/mpm/winnt/service.c | 431 ++ server/rfc1413.c | 248 ++ server/util.c | 2145 +++++++++ server/util_date.c | 321 ++ server/util_md5.c | 229 + server/util_script.c | 1138 +++++ server/util_uri.c | 599 +++ server/vhost.c | 917 ++++ srclib/.cvsignore | 1 + support/.cvsignore | 11 + support/.indent.pro | 54 + support/README | 62 + support/SHA1/README.sha1 | 34 + support/SHA1/convert-sha1.pl | 36 + support/SHA1/htpasswd-sha1.pl | 22 + support/SHA1/ldif-sha1.example | 19 + support/ab.c | 1100 +++++ support/apxs.in | 655 +++ support/dbmmanage | 189 + support/htdigest.c | 271 ++ support/htpasswd.c | 577 +++ support/httpd.exp | 412 ++ support/log_server_status | 118 + support/logresolve.c | 345 ++ support/logresolve.pl | 264 ++ support/phf_abuse_log.cgi | 21 + support/rotatelogs.c | 82 + support/split-logfile | 103 + support/suexec.c | 566 +++ support/suexec.h | 143 + test/.cvsignore | 11 + test/.indent.pro | 54 + test/README | 3 + test/check_chunked | 42 + test/cls.c | 166 + test/tcpdumpscii.txt | 50 + test/test-writev.c | 85 + test/test_date.c | 180 + test/test_find.c | 62 + test/test_limits.c | 200 + test/test_parser.c | 59 + test/test_select.c | 30 + test/time-sem.c | 572 +++ test/zb.c | 567 +++ 118 files changed, 38277 insertions(+) create mode 100644 docs/man/ab.8 create mode 100644 docs/man/apachectl.8 create mode 100644 docs/man/apxs.8 create mode 100644 docs/man/dbmmanage.1 create mode 100644 docs/man/htdigest.1 create mode 100644 docs/man/htpasswd.1 create mode 100644 docs/man/httpd.8 create mode 100644 docs/man/logresolve.8 create mode 100644 docs/man/rotatelogs.8 create mode 100644 docs/man/suexec.8 create mode 100644 modules/aaa/.indent.pro create mode 100644 modules/aaa/mod_auth_anon.dsp create mode 100644 modules/arch/win32/mod_isapi.c create mode 100644 modules/cache/.indent.pro create mode 100644 modules/echo/.indent.pro create mode 100644 modules/experimental/.indent.pro create mode 100644 modules/filters/.indent.pro create mode 100644 modules/generators/.indent.pro create mode 100644 modules/generators/mod_info.dsp create mode 100644 modules/generators/mod_status.dsp create mode 100644 modules/http/.indent.pro create mode 100644 modules/http/http_core.c create mode 100644 modules/http/http_protocol.c create mode 100644 modules/http/http_request.c create mode 100644 modules/loggers/.indent.pro create mode 100644 modules/mappers/.indent.pro create mode 100644 modules/mappers/mod_rewrite.dsp create mode 100644 modules/mappers/mod_speling.dsp create mode 100644 modules/metadata/.indent.pro create mode 100644 modules/metadata/mod_cern_meta.dsp create mode 100644 modules/metadata/mod_expires.dsp create mode 100644 modules/metadata/mod_headers.dsp create mode 100644 modules/metadata/mod_usertrack.dsp create mode 100644 modules/ssl/.indent.pro create mode 100644 os/.indent.pro create mode 100644 os/bs2000/.cvsignore create mode 100644 os/bs2000/bs2login.c create mode 100644 os/bs2000/ebcdic.c create mode 100644 os/bs2000/ebcdic.h create mode 100644 os/bs2000/os-inline.c create mode 100644 os/bs2000/os.c create mode 100644 os/bs2000/os.h create mode 100644 os/os2/.cvsignore create mode 100644 os/os2/os-inline.c create mode 100644 os/os2/os.h create mode 100644 os/os2/util_os2.c create mode 100644 os/tpf/TPFExport create mode 100644 os/tpf/ebcdic.c create mode 100644 os/tpf/ebcdic.h create mode 100644 os/tpf/os-inline.c create mode 100644 os/tpf/os.c create mode 100644 os/tpf/os.h create mode 100644 os/tpf/samples/linkdll.jcl create mode 100644 os/tpf/samples/loadset.jcl create mode 100644 os/unix/.cvsignore create mode 100644 os/unix/os-inline.c create mode 100644 os/unix/os.h create mode 100644 os/win32/.cvsignore create mode 100644 os/win32/MakeModuleMak.cpp create mode 100644 os/win32/Module.mak.tmpl create mode 100644 os/win32/mod_isapi.c create mode 100644 os/win32/modules.c create mode 100644 os/win32/os.h create mode 100644 os/win32/util_win32.c create mode 100644 server/.cvsignore create mode 100644 server/.indent.pro create mode 100644 server/config.c create mode 100644 server/gen_test_char.c create mode 100644 server/gen_test_char.dsp create mode 100644 server/gen_uri_delims.c create mode 100644 server/gen_uri_delims.dsp create mode 100644 server/log.c create mode 100644 server/main.c create mode 100644 server/mpm/winnt/registry.c create mode 100644 server/mpm/winnt/service.c create mode 100644 server/rfc1413.c create mode 100644 server/util.c create mode 100644 server/util_date.c create mode 100644 server/util_md5.c create mode 100644 server/util_script.c create mode 100644 server/util_uri.c create mode 100644 server/vhost.c create mode 100644 srclib/.cvsignore create mode 100644 support/.cvsignore create mode 100644 support/.indent.pro create mode 100644 support/README create mode 100644 support/SHA1/README.sha1 create mode 100644 support/SHA1/convert-sha1.pl create mode 100644 support/SHA1/htpasswd-sha1.pl create mode 100644 support/SHA1/ldif-sha1.example create mode 100644 support/ab.c create mode 100644 support/apxs.in create mode 100644 support/dbmmanage create mode 100644 support/htdigest.c create mode 100644 support/htpasswd.c create mode 100644 support/httpd.exp create mode 100644 support/log_server_status create mode 100644 support/logresolve.c create mode 100644 support/logresolve.pl create mode 100644 support/phf_abuse_log.cgi create mode 100644 support/rotatelogs.c create mode 100644 support/split-logfile create mode 100644 support/suexec.c create mode 100644 support/suexec.h create mode 100644 test/.cvsignore create mode 100644 test/.indent.pro create mode 100644 test/README create mode 100644 test/check_chunked create mode 100644 test/cls.c create mode 100644 test/tcpdumpscii.txt create mode 100644 test/test-writev.c create mode 100644 test/test_date.c create mode 100644 test/test_find.c create mode 100644 test/test_limits.c create mode 100644 test/test_parser.c create mode 100644 test/test_select.c create mode 100644 test/time-sem.c create mode 100644 test/zb.c diff --git a/docs/man/ab.8 b/docs/man/ab.8 new file mode 100644 index 0000000000..5a06f36055 --- /dev/null +++ b/docs/man/ab.8 @@ -0,0 +1,210 @@ +.TH ab 1 "March 1998" +.\" $Id: ab.8,v 1.1 1999/08/24 06:45:53 fielding Exp $ +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +ab \- Apache HTTP server benchmarking tool +.SH SYNOPSIS +.B ab +[ +.B \-k +] [ +.BI \-n " requests" +] [ +.BI \-t " timelimit" +] [ +.BI \-c " concurrency" +] [ +.BI \-p " POST file" +] [ +.BI \-A " Authenticate username:password" +] [ +.BI \-P " Proxy Authenticate username:password" +] [ +.BI \-H " Custom header" +] [ +.BI \-C " Cookie name=value" +] [ +.BI \-T " content-type" +] [ +.BI \-v " verbosity" +] +] [ +.BI \-w " output HTML" +] +] [ +.BI \-x " attributes" +] +] [ +.BI \-y " attributes" +] +] [ +.BI \-z "
attributes" +] +.I [http://]hostname[:port]/path + +.B ab +[ +.B \-V +] [ +.B \-h +] +.PP +.SH DESCRIPTION +.B ab +is a tool for benchmarking your Apache HyperText Transfer Protocol (HTTP) +server. It is designed to give you an impression on how performant is your +current Apache installation. This especially shows you how much requests per +time your Apache installation is capable to serve. +.PP +.SH OPTIONS +.TP 12 +.B \-k +Enable the HTTP KeepAlive feature, i.e. perform multiple requests within one +HTTP session instead. Default is no KeepAlive. +.TP 12 +.BI \-n " requests" +Number of requests to perform for the benchmarking session. The default is to +just perform one single request which usually leads to not very representative +benchmarking results. +.TP 12 +.BI \-t " timelimit" +Seconds to max. spend for benchmarking. This implies +a +.B \-n +.B 50000 +internally. Use this to benchmark the server within a fixed total amount of +time. Per default there is no timelimit. +.TP 12 +.BI \-c " concurrency" +Number of multiple requests per time to perform. +Default is one request per time. + +.TP 12 +.BI \-p " POST file" +File containing data to POST. + +.TP 12 +.BI \-A " Authorization username:password" +Supply BASIC Authentification credentials to the server. The username +and password are separated by a single ':' and send on the wire uuencoded. +The string is send regardless of wether the server needs it; (i.e. has +send an 401. Authentifcation needed). + +.TP 12 +.BI \-p " Proxy-Authorization username:password" +Supply BASIC Authentification credentials to a proxy en-route. The username +and password are separated by a single ':' and send on the wire uuencoded. +The string is send regardless of wether the proxy needs it; (i.e. has +send an 407 Proxy authentifcation needed). + +.TP 12 +.BI \-C " Cookie name=value" +Add a 'Cookie:' line to the request. The argument is typically in the form +of a 'name=value' pair. This field is repeatable. + +.TP 12 +.BI \-p " Header string" +Postfix extra headers to the request. The argument is typically in the form +of a valid header line; containing a colon separated field value pair. (i.e. +'Accept-Encoding: zip/zop;8bit'). + +.TP 12 +.BI \-T " content-type" +Content-type header to use for POST data. + +.TP 12 +.B \-v +Set verbosity level - 4 and above prints information on headers, 3 and +above prints response codes (404, 200, etc.), 2 and above prints +warnings and info. + +.TP 12 +.BI \-w +Print out results in HTML tables. Default table is two columns wide, +with a white background. +.TP 12 +.BI \-x " attributes" +String to use as attributes for . Attributes are inserted +
+.TP 12 +.BI \-y " attributes" +String to use as attributes for . +.TP 12 +.BI \-z " attributes" +String to use as attributes for
. +.TP 12 +.B \-V +Display version number and exit. +.TP 12 +.B \-h +Display usage information. +.PD +.SH BUGS +There are various statically declared buffers of fixed length. Combined +with the lazy parsing of the command line arguments, the response headers +from the server and other external inputs this might bite you. +.P +It does not implement HTTP/1.x fully; only accepts some 'expected' forms +of responses. The rather heavy use of +.BR strstr(3) +shows up top in profile, +which might indicate a performance problem; i.e. you would measure the +.BR ab +performance rather than the server's. + +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/apachectl.8 b/docs/man/apachectl.8 new file mode 100644 index 0000000000..72300eb98b --- /dev/null +++ b/docs/man/apachectl.8 @@ -0,0 +1,133 @@ +.TH apachectl 1 "September 1997" +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +apachectl \- Apache HTTP server control interface +.SH SYNOPSIS +.B apachectl +\fIcommand\fP [...] +.SH DESCRIPTION +.B apachectl +is a front end to the Apache HyperText Transfer Protocol (HTTP) +server. It is designed to help the administrator control the +functioning of the Apache +.B httpd +daemon. +.PP +.B NOTE: +If your Apache installation uses non-standard paths, you will need to +edit the +.B apachectl +script to set the appropriate paths to your PID file and your +.B httpd +binary. See the comments in the script for details. +.PP +The +.B apachectl +script returns a 0 exit value on success, and >0 if an error +occurs. For more details, view the comments in the script. +.PP +Full documentation for Apache is available at +.B http://www.apache.org/ +. +.SH OPTIONS +The \fIcommand\fP can be any one or more of the following options: +.TP 12 +.BI start +Start the Apache daemon. Gives an error if it is already running. +.TP +.BI stop +Stops the Apache daemon. +.TP +.BI restart +Restarts the Apache daemon by sending it a SIGHUP. If the daemon +is not running, it is started. +This command automatically checks the configuration files via +.BI configtest +before initiating the restart to make sure Apache doesn't die. +.TP +.BI fullstatus +Displays a full status report from +.B mod_status. +For this to work, you need to have mod_status enabled on your server +and a text-based browser such as \fIlynx\fP available on your system. The +URL used to access the status report can be set by editing the +.B STATUSURL +variable in the script. +.TP +.BI status +Displays a brief status report. Similar to the fullstatus option, +except that the list of requests currently being served is omitted. +.TP +.BI graceful +Gracefully restarts the Apache daemon by sending it a SIGUSR1. If +the daemon is not running, it is started. This differs from a +normal restart in that currently open connections are not aborted. +A side effect is that old log files will not be closed immediately. +This means that if used in a log rotation script, a substantial delay may be +necessary to ensure that the old log files are closed before processing them. +This command automatically checks the configuration files via +.BI configtest +before initiating the restart to make sure Apache doesn't die. +.TP +.BI configtest +Run a configuration file syntax test. It parses the configuration +files and either reports +.B "Syntax Ok" +or detailed information about the particular syntax error. +.TP +.BI help +Displays a short help message. +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/apxs.8 b/docs/man/apxs.8 new file mode 100644 index 0000000000..464766c75f --- /dev/null +++ b/docs/man/apxs.8 @@ -0,0 +1,459 @@ +.TH apxs 8 "April 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +apxs \- APache eXtenSion tool +.SH SYNOPSIS +.B apxs +.B \-g +[ +.BI \-S " name=value +] +.BI \-n " modname" + +.B apxs +.B \-q +[ +.BI \-S " name=value +] +.IR query " ..." + +.B apxs +.B \-c +[ +.BI \-S " name=value +] +[ +.BI \-o " dsofile" +] +[ +.BI \-I " incdir" +] +[ +.BI \-D " name=value" +] +[ +.BI \-L " libdir" +] +[ +.BI \-l " libname" +] +[ +.BI \-Wc, "compiler-flags" +] +[ +.BI \-Wl, "linker-flags" +] +.IR files " ..." + +.B apxs +.B \-i +[ +.BI \-S " name=value +] +[ +.BI \-n " modname" +] +[ +.B \-a +] +[ +.B \-A +] +.IR dsofile " ..." + +.B apxs +.B \-e +[ +.BI \-S " name=value +] +[ +.BI \-n " modname" +] +[ +.B \-a +] +[ +.B \-A +] +.IR dsofile " ..." +.PP +.SH DESCRIPTION +.B apxs +is a tool for building and installing extension modules for the Apache +HyperText Transfer Protocol (HTTP) server. This is achieved by building a +dynamic shared object (DSO) from one or more source or object +.I files +which then can be loaded into +the Apache server under runtime via the +.B LoadModule +directive from +.BR mod_so. + +So to use this extension mechanism your platform has +to support the DSO feature and your +Apache +.B httpd +binary has to be built with the +.B mod_so +module. +The +.B apxs +tool automatically complains if this is not the case. +You can check this yourself by manually running the command + +.nf + $ httpd -l +.fi + +The module +.B mod_so +should be part of the displayed list. +If these requirements are fulfilled you can easily extend +your Apache server's functionality by installing your own +modules with the DSO mechanism by the help of this +.B apxs +tool: + +.nf + $ apxs -i -a -c mod_foo.c + gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c + ld -Bshareable -o mod_foo.so mod_foo.o + cp mod_foo.so /path/to/apache/libexec/mod_foo.so + chmod 755 /path/to/apache/libexec/mod_foo.so + [activating module `foo' in /path/to/apache/etc/httpd.conf] + $ apachectl restart + /path/to/apache/sbin/apachectl restart: httpd not running, trying to start + [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module + /path/to/apache/sbin/apachectl restart: httpd started + $ _ +.fi + +The arguments +.I files +can be any C source file (.c), a object file (.o) or +even a library archive (.a). The +.B apxs +tool automatically recognizes these extensions and automtaically used the C +source files for compilation while just using the object and archive files for +the linking phase. But when using such pre-compiled objects make sure they are +compiled for position independend code (PIC) to be able to use them for a +dynamically loaded shared object. +For instance with GCC you always just have to use +.BR -fpic . +For other +C compilers consult its manual +page or at watch for the flags +.B apxs +uses to compile the object files. + +For more details about DSO support in Apache read the documentation +of +.B mod_so +or perhaps even read the +.B src/modules/standard/mod_so.c +source file. + +.PP +.SH OPTIONS +Common options: +.TP 12 +.BI \-n " modname" +This explicitly sets the module name for the +.B \-i +(install) +and +.B \-g +(template generation) option. Use this to explicitly specify the module name. +For option +.B \-g +this is required, for option +.B \-i +the +.B apxs +tool tries to determine the name from the source or (as a fallback) at least +by guessing it from the filename. +.PP +Query options: +.TP 12 +.B \-q +Performs a query for +.BR apxs 's +knowledge about certain settings. The +.I query +parameters can be one or more of the following strings: +.nf + CC TARGET + CFLAGS SBINDIR + CFLAGS_SHLIB INCLUDEDIR + LD_SHLIB LIBEXECDIR + LDFLAGS_SHLIB SYSCONFDIR + LIBS_SHLIB +.fi +Use this for manually determining settings. For instance use +.nf + INC=-I`apxs -q INCLUDEDIR` +.fi +inside your own Makefiles if you need manual access +to Apache's C header files. +.PP +Configuration options: +.TP 12 +.BI \-S " name=value" +This option changes the apxs settings described above. +.PP +Template Generation options: +.TP 12 +.B \-g +This generates a subdirectory +.I name +(see option +.BR \-n ")" +and there two files: A sample module source file named +.BI mod_ name.c +which can be used as a template for creating your own modules or +as a quick start for playing with the APXS mechanism. +And a corresponding +.B Makefile +for even easier build and installing of this module. +.PP +DSO compilation options: +.TP 12 +.B \-c +This indicates the compilation operation. It first compiles the C source +files (.c) of +.I files +into corresponding object files (.o) and then builds a dynamically shared object in +.I dsofile +by linking these object files plus the remaining +object files (.o and .a) of +.I files +If no +.B \-o +option is specified +the output file is guessed from the first filename in +.I files +and thus usually defaults to +.BI mod_ name.so +.TP 12 +.BI \-o " dsofile" +Explicitly specifies the filename of the created dynamically shared object. If +not specified and the name cannot be guessed from the +.I files +list, the fallback name +.B mod_unknown.so +is used. +.TP 12 +.BI \-D " name=value" +This option is directly passed through to the compilation command(s). +Use this to add your own defines to the build process. +.TP 12 +.BI \-I " incdir" +This option is directly passed through to the compilation command(s). +Use this to add your own include directories to search to the build process. +.TP 12 +.BI \-L " libdir" +This option is directly passed through to the linker command. +Use this to add your own library directories to search to the build process. +.TP 12 +.BI \-l " libname" +This option is directly passed through to the linker command. +Use this to add your own libraries to search to the build process. +.TP 12 +.BI \-Wc, "compiler-flags" +This option passes +.I compiler-flags +as additional flags to the compiler command. +Use this to add local compiler-specific options. +.TP 12 +.BI \-Wl, "linker-flags" +This option passes +.I linker-flags +as additional flags to the linker command. +Use this to add local linker-specific options. +.PP +DSO installation and configuration options: +.TP 12 +.B \-i +This indicates the installation operation and installs one or more +dynamically shared objects into the +server's +.I libexec +directory. +.TP 12 +.B \-a +This activates the module by automatically adding a corresponding +.B LoadModule +line to Apache's +.B httpd.conf +configuration file, or by enabling it if it already exists. +.TP 12 +.B \-A +Same as option +.B \-a +but the created +.B LoadModule +directive is prefixed with a hash sign (#), i.e. the module is +just prepared for later activation but initially disabled. +.TP 12 +.B \-e +This indicates the editing operation, which can be used with the +.B \-a +and +.B \-A +options similarly to the +.B \-i +operation to edit Apache's +.B httpd.conf +configuration file without attempting to install the module. +.PD +.SH EXAMPLES +Assume you have an Apache module named mod_foo.c available which should extend +Apache's server functionality. To accomplish this you first have to compile +the C source into a shared object suitable for loading into the Apache server +under runtime via the following command: + +.nf + $ apxs -c mod_foo.c + gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c + ld -Bshareable -o mod_foo.so mod_foo.o + $ _ +.fi + +Then you have to update the Apache configuration by making sure a +.B LoadModule +directive is present to load this shared object. To simplify this +step +.B apxs +provides an automatic way to install the shared object in its +"libexec" directory and updating the +.B httpd.conf +file accordingly. This can be achieved by running: + +.nf + $ apxs -i -a mod_foo.c + cp mod_foo.so /path/to/apache/libexec/mod_foo.so + chmod 755 /path/to/apache/libexec/mod_foo.so + [activating module `foo' in /path/to/apache/etc/httpd.conf] + $ _ +.fi + +This way a line named + +.nf + LoadModule foo_module libexec/mod_foo.so +.fi + +is added to the configuration file if still not present. +If you want to have this this disabled per default use the +.B \-A +option, i.e. + +.nf + $ apxs -i -A mod_foo.c +.fi + +For a quick test of the APXS mechanism you can create a sample Apache module +template plus a corresponding Makefile via: + +.nf + $ apxs -g -n foo + Creating [DIR] foo + Creating [FILE] foo/Makefile + Creating [FILE] foo/mod_foo.c + $ _ +.fi + +Then you can immediately compile this sample module into a shared object and +load it into the Apache server: + +.nf + $ cd foo + $ make all reload + apxs -c mod_foo.c + gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c + ld -Bshareable -o mod_foo.so mod_foo.o + apxs -i -a -n "foo" mod_foo.so + cp mod_foo.so /path/to/apache/libexec/mod_foo.so + chmod 755 /path/to/apache/libexec/mod_foo.so + [activating module `foo' in /path/to/apache/etc/httpd.conf] + apachectl restart + /path/to/apache/sbin/apachectl restart: httpd not running, trying to start + [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module + /path/to/apache/sbin/apachectl restart: httpd started + $ _ +.fi + +You can even use +.B apxs +to compile complex modules outside the Apache source tree, like PHP3: + +.nf + $ cd php3 + $ ./configure --with-shared-apache=../apache-1.3 + $ apxs -c -o libphp3.so mod_php3.c libmodphp3-so.a + gcc -fpic -DSHARED_MODULE -I/tmp/apache/include -c mod_php3.c + ld -Bshareable -o libphp3.so mod_php3.o libmodphp3-so.a + $ _ +.fi + +because +.B apxs +automatically recognized C source files and object files. Only C source files +are compiled while remaining object files are used for the linking phase. + +.PD +.SH SEE ALSO +.BR apachectl(1), +.BR httpd(8). +. diff --git a/docs/man/dbmmanage.1 b/docs/man/dbmmanage.1 new file mode 100644 index 0000000000..06380ff0f5 --- /dev/null +++ b/docs/man/dbmmanage.1 @@ -0,0 +1,171 @@ +.TH dbmmanage 1 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +dbmmanage \- Create and update user authentication files in DBM format +.SH SYNOPSIS +.B dbmmanage +.I filename +[ +.I command +] [ +.I username +[ +.I encpasswd +] ] +.PP +.SH DESCRIPTION +.B dbmmanage +is used to create and update the DBM format files used to store +usernames and password for basic authentication of HTTP users. +Resources available from the +.B httpd +Apache web server can be restricted to just the users listed +in the files created by +.B dbmmanage. +This program can only be used +when the usernames are stored in a DBM file. To use a +flat-file database see +\fBhtpasswd\fP. +.PP +This manual page only lists the command line arguments. For details of +the directives necessary to configure user authentication in +.B httpd +see +the Apache manual, which is part of the Apache distribution or can be +found at http://www.apache.org/. +.SH OPTIONS +.IP \fB\fIfilename\fP +The filename of the DBM format file. Usually without the +extension .db, .pag, or .dir. +.IP \fB\fIcommand\fP +This selects the operation to perform: +.TP 12 +.B add +Adds an entry for \fIusername\fP to \fIfilename\fP using the encrypted +password \fIencpassword\fP. +.TP 12 +.B adduser +Asks for a password and then adds an entry for \fIusername\fP to +\fIfilename\fP . +.TP 12 +.B check +Asks for a password and then checks if +\fIusername\fP is in \fIfilename\fP and if it's password matches +the specified one. +.TP 12 +.B delete +Deletes the \fIusername\fP entry from \fIfilename\fP. +.TP 12 +.B import +Reads username:password entries (one per line) from STDIN and adds them to +\fIfilename\fP. The passwords already has to be crypted. +.TP 12 +.B update +Same as the "adduser" command, except that it makes sure \fIusername\fP +already exists in \fIfilename\fP. +.TP 12 +.B view +Just displays the complete contents of the DBM file. +.IP \fB\fIusername\fP +The user for which the update operation is performed. +.PD +.SH BUGS +.PP +One should be aware that there are a number of different DBM file +formats in existance, and with all likelihood, libraries for more than +one format may exist on your system. The three primary examples are +NDBM, the GNU project's GDBM, and Berkeley DB 2. Unfortunately, all +these libraries use different file formats, and you must make sure +that the file format used by +.I filename +is the same format that +.B dbmmanage +expects to see. +.B dbmmanage +currently has no way of determining what type of DBM file it is +looking at. If used against the wrong format, +.dbmmanage +will simply return nothing, or may create a different DBM file with a +different name, or at worst, it may corrupt the DBM file if you were +attempting to write to it. +.PP +.B dbmmanage +has a list of DBM format preferences, defined by the +.B @AnyDBM::ISA +array near the beginning of the program. Since we prefer the Berkeley +DB 2 file format, the order in which +.B dbmmanage +will look for system libraries is Berkeley DB 2, then NDBM, and then +GDBM. The first library found will be the library +.B dbmmanage +will attempt to use for all DBM file transactions. This ordering is +slightly different than the standard +.B @AnyDBM::ISA +ordering in perl, as well as the ordering used by the simple dbmopen() +call in Perl, so if you use any other utilities to manage your DBM +files, they must also follow this preference ordering. Similar care +must be taken if using programs in other languages, like C, to +access these files. +.PP +Apache's +.B mod_auth_db.c +module corresponds to Berkeley DB 2 library, while +.B mod_auth_dbm.c +corresponds to the NDBM library. Also, one can usually use the +.B file +program supplied with most Unix systems to see what format a DBM file is in. +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/htdigest.1 b/docs/man/htdigest.1 new file mode 100644 index 0000000000..c719e456ce --- /dev/null +++ b/docs/man/htdigest.1 @@ -0,0 +1,97 @@ +.TH htdigest 1 "March 1998" +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. For written permission, please contact +.\" apache@apache.org. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +htdigest \- Create and update user authentication files +.SH SYNOPSIS +.B htdigest +[ +.B \-c +] +.I passwdfile +.I realm +.I username +.SH DESCRIPTION +.B htdigest +is used to create and update the flat-files used to store +usernames, realm and password for digest authentication of HTTP users. +Resources available from the +.B httpd +Apache web server can be restricted to just the users listed +in the files created by +.B htdigest. +.PP +This manual page only lists the command line arguments. For details of +the directives necessary to configure digest authentication in +.B httpd +see +the Apache manual, which is part of the Apache distribution or can be +found at http://www.apache.org/. +.SH OPTIONS +.IP \-c +Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it +is deleted first. +.IP \fB\fIpasswdfile\fP +Name of the file to contain the username, realm and password. If \-c +is given, this file is created if it does not already exist, +or deleted and recreated if it does exist. +.IP \fB\fIrealm\fP +The realm name to which the user name belongs to. +.IP \fB\fIusername\fP +The user name to create or update in \fBpasswdfile\fP. If +\fIusername\fP does not exist is this file, an entry is added. If it +does exist, the password is changed. +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/htpasswd.1 b/docs/man/htpasswd.1 new file mode 100644 index 0000000000..1687cde41c --- /dev/null +++ b/docs/man/htpasswd.1 @@ -0,0 +1,213 @@ +.TH htpasswd 1 "February 1997" +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. For written permission, please contact +.\" apache@apache.org. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +htpasswd \- Create and update user authentication files +.SH SYNOPSIS +.B htpasswd +[ +.B \-c +] +[ +.B \-m +] +.I passwdfile +.I username +.br +.B htpasswd +.B \-b +[ +.B \-c +] +[ +.B \-m +.B \-d +.B \-p +.B \-s +] +.I passwdfile +.I username +.I password +.SH DESCRIPTION +.B htpasswd +is used to create and update the flat-files used to store +usernames and password for basic authentication of HTTP users. +If +.B htpasswd +cannot access a file, such as not being able to write to the output +file or not being able to read the file in order to update it, +it returns an error status and makes no changes. +.PP +Resources available from the +.B httpd +Apache web server can be restricted to just the users listed +in the files created by +.B htpasswd. +This program can only be used +when the usernames are stored in a flat-file. To use a +DBM database see +\fBdbmmanage\fP. +.PP +.B htpasswd +encrypts passwords using either a version of MD5 modified for Apache, +or the system's \fIcrypt()\fP routine. Files managed by +.B htpasswd +may contain both types of passwords; some user records may have +MD5-encrypted passwords while others in the same file may have passwords +encrypted with \fIcrypt()\fP. +.PP +This manual page only lists the command line arguments. For details of +the directives necessary to configure user authentication in +.B httpd +see +the Apache manual, which is part of the Apache distribution or can be +found at . +.SH OPTIONS +.IP \-b +Use batch mode; \fIi.e.\fP, get the password from the command line +rather than prompting for it. \fBThis option should be used with +extreme care, since the password is clearly visible on the command +line.\fP +.IP \-c +Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it +is rewritten and truncated. +.IP \-m +Use MD5 encryption for passwords. On Windows and TPF, this is the default. +.IP \-d +Use crypt() encryption for passwords. The default on all platforms but +Windows and TPF. Though possibly supported by +.B htpasswd +onm all platforms, it is not supported by the +.B httpd +server on Windows and TPF. +.IP \-s +Use SHA encryption for passwords. Faciliates migration from/to Netscape +servers using the LDAP Directory Interchange Format (ldif). +.IP \-p +Use plaintext passwords. Though +.B htpasswd +will support creation on all platofrms, the +.B httpd +deamon will only accept plain text passwords on Windows and TPF. +.IP \fB\fIpasswdfile\fP +Name of the file to contain the user name and password. If \-c +is given, this file is created if it does not already exist, +or rewritten and truncated if it does exist. +.IP \fB\fIusername\fP +The username to create or update in \fBpasswdfile\fP. If +\fIusername\fP does not exist in this file, an entry is added. If it +does exist, the password is changed. +.IP \fB\fIpassword\fP +The plaintext password to be encrypted and stored in the file. Only used +with the \fI-b\fP flag. +.SH EXIT STATUS +.B htpasswd +returns a zero status ("true") if the username and password have +been successfully added or updated in the \fIpasswdfile\fP. +.B htpasswd +returns 1 if it encounters some problem accessing files, 2 if there +was a syntax problem with the command line, 3 if the password was +entered interactively and the verification entry didn't match, 4 if +its operation was interrupted, 5 if a value is too long (username, +filename, password, or final computed record), and 6 if the username +contains illegal characters (see the \fBRESTRICTIONS\fP section). +.SH EXAMPLES +\fBhtpasswd /usr/local/etc/apache/.htpasswd-users jsmith\fP +.IP +Adds or modifies the password for user \fIjsmith\fP. +The user is prompted for the password. If executed +on a Windows system, the password will be encrypted using the +modified Apache MD5 algorithm; otherwise, the system's +\fIcrypt()\fP routine will be used. If the file does not +exist, +.B htpasswd +will do nothing except return an error. +.LP +\fBhtpasswd -c /home/doe/public_html/.htpasswd jane\fP +.IP +Creates a new file and stores a record in it for user \fIjane\fP. +The user is prompted for the password. +If the file exists and cannot be read, or cannot be written, +it is not altered and +.B htpasswd +will display a message and return an error status. +.LP +\fBhtpasswd -mb /usr/web/.htpasswd-all jones Pwd4Steve\fP +.IP +Encrypts the password from the command line (\fIPwd4Steve\fP) using +the MD5 algorithm, and stores it in the specified file. +.LP +.SH SECURITY CONSIDERATIONS +Web password files such as those managed by +.B htpasswd +should \fBnot\fP be within the Web server's URI space -- that is, +they should not be fetchable with a browser. +.PP +The use of the \fI-b\fP option is discouraged, since when it is +used the unencrypted password appears on the command line. +.SH RESTRICTIONS +On the Windows and MPE platforms, passwords encrypted with +.B htpasswd +are limited to no more than 255 characters in length. Longer +passwords will be truncated to 255 characters. +.PP +The MD5 algorithm used by +.B htpasswd +is specific to the Apache software; passwords encrypted using it will not be +usable with other Web servers. +.PP +Usernames are limited to 255 bytes and may not include the character ':'. +.SH SEE ALSO +.BR httpd(8) +and the scripts in support/SHA1 which come with the distribution. diff --git a/docs/man/httpd.8 b/docs/man/httpd.8 new file mode 100644 index 0000000000..ae320bad2a --- /dev/null +++ b/docs/man/httpd.8 @@ -0,0 +1,211 @@ +.TH httpd 8 "February 1997" +.\" Copyright (c) 1995-1997 David Robinson. All rights reserved. +.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. For written permission, please contact +.\" apache@apache.org. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +httpd \- Apache hypertext transfer protocol server +.SH SYNOPSIS +.B httpd +[ +.B \-X +] [ +.BI \-R " libexecdir" +] [ +.BI \-d " serverroot" +] [ +.BI \-f " config" +] [ +.BI \-C " directive" +] [ +.BI \-c " directive" +] [ +.BI \-D " parameter" +] + +.B httpd +[ +.B \-h +] +[ +.B \-l +] +[ +.B \-L +] +[ +.B \-v +] +[ +.B \-V +] +[ +.B \-S +] +[ +.B \-t +] +[ +.B \-T +] + +.SH DESCRIPTION +.B httpd +is the Apache HyperText Transfer Protocol (HTTP) server program. It is +designed to be run as a standalone daemon process. When used like this +it will create a pool of child processes to handle requests. To stop +it, send a TERM signal to the initial (parent) process. The PID of +this process is written to a file as given in the configuration file. +Alternatively +.B httpd +may be invoked by the Internet daemon inetd(8) each +time a connection to the HTTP service is made. +.PP +This manual page only lists the command line arguments. For details +of the directives necessary to configure +.B httpd +see the Apache manual, +which is part of the Apache distribution or can be found at +http://www.apache.org/. Paths in this manual may not reflect those +compiled into +.B httpd. +.SH OPTIONS +.TP 12 +.BI \-R " libexecdir" +This option is only available if Apache was built with +the +.I SHARED_CORE +rule enabled which forces the Apache core code to be +placed into a dynamic shared object (DSO) file. This file +is searched in a hardcoded path under ServerRoot per default. Use this +option if you want to override it. +.TP 12 +.BI \-d " serverroot" +Set the initial value for the ServerRoot directive to \fIserverroot\fP. This +can be overridden by the ServerRoot command in the configuration file. The +default is \fB/usr/local/apache\fP. +.TP +.BI \-f " config" +Execute the commands in the file \fIconfig\fP on startup. If \fIconfig\fP +does not begin with a /, then it is taken to be a path relative to +the ServerRoot. The default is \fBconf/httpd.conf\fP. +.TP +.BI \-C " directive" +Process the configuration \fIdirective\fP before reading config files. +.TP +.BI \-c " directive" +Process the configuration \fIdirective\fP after reading config files. +.TP +.BI \-D " parameter" +Sets a configuration \fIparameter\fP which can be used with +... sections in the configuration files +to conditionally skip or process commands. +.TP +.B \-h +Output a short summary of available command line options. +.TP +.B \-l +Output a list of modules compiled into the server. +.TP +.B \-L +Output a list of directives together with expected arguments and +places where the directive is valid. +.TP +.B \-S +Show the settings as parsed from the config file (currently only shows the +virtualhost settings). +.TP +.B \-t +Run syntax tests for configuration files only. The program immediately exits +after these syntax parsing with either a return code of 0 (Syntax OK) or +return code not equal to 0 (Syntax Error). +.TP +.B \-T +Same as option +.B \-t +but does not check the configured document roots. +.TP +.B \-X +Run in single-process mode, for internal debugging purposes only; the daemon +does not detach from the terminal or fork any children. Do NOT use this mode +to provide ordinary web service. +.TP +.B \-v +Print the version of +.B httpd +, and then exit. +.TP +.B \-V +Print the version and build parameters of +.B httpd +, and then exit. +.SH FILES +.PD 0 +.B /usr/local/apache/conf/httpd.conf +.br +.B /usr/local/apache/conf/srm.conf +.br +.B /usr/local/apache/conf/access.conf +.br +.B /usr/local/apache/conf/mime.types +.br +.B /usr/local/apache/conf/magic +.br +.B /usr/local/apache/logs/error_log +.br +.B /usr/local/apache/logs/access_log +.br +.B /usr/local/apache/logs/httpd.pid +.PD +.SH SEE ALSO +.BR inetd (8). diff --git a/docs/man/logresolve.8 b/docs/man/logresolve.8 new file mode 100644 index 0000000000..4851269ad6 --- /dev/null +++ b/docs/man/logresolve.8 @@ -0,0 +1,87 @@ +.TH logresolve 8 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +logresolve \- resolve hostnames for IP-adresses in Apache logfiles +.SH SYNOPSIS +.B logresolve +[ +.BI \-s " filename" +] [ +.B \-c +] < +.I access_log +> +.I access_log.new +.PP +.SH DESCRIPTION +.B logresolve +is a post-processing program to resolve IP-adresses in Apache's access +logfiles. To minimize impact on your nameserver, logresolve has its very own +internal hash-table cache. This means that each IP number will only be looked +up the first time it is found in the log file. +.SH OPTIONS +.TP 12 +.BI \-s " filename" +Specifies a filename to record statistics. +.TP 12 +.B \-c +This causes +.B logresolve +to apply some DNS checks: after finding the hostname from the IP address, it +looks up the IP addresses for the hostname and checks that one of these +matches the original address. +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/rotatelogs.8 b/docs/man/rotatelogs.8 new file mode 100644 index 0000000000..aee02e11ec --- /dev/null +++ b/docs/man/rotatelogs.8 @@ -0,0 +1,83 @@ +.TH rotatelogs 8 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +rotatelogs \- rotate Apache logs without having to kill the server +.SH SYNOPSIS +.B rotatelogs +.I logfile +.I rotationtime +.PP +.SH DESCRIPTION +.B rotatelogs +is a simple program for use in conjunction with Apache's piped logfile +feature which can be used like this: + +.fi + TransferLog "|rotatelogs /path/to/logs/access_log 86400" +.mf + +This creates the files /path/to/logs/access_log.nnnn where nnnn is the system +time at which the log nominally starts (this time will always be a multiple of +the rotation time, so you can synchronize cron scripts with it). At the end +of each rotation time (here after 24 hours) a new log is started. +.SH OPTIONS +.IP \fB\fIlogfile\fP +The path plus basename of the logfile. The suffix .nnnn is automatically +added. +.IP \fB\fIrotationtime\fP +The rotation time in seconds. +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/docs/man/suexec.8 b/docs/man/suexec.8 new file mode 100644 index 0000000000..801d7abce9 --- /dev/null +++ b/docs/man/suexec.8 @@ -0,0 +1,70 @@ +.TH suexec 8 "March 1998" +.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" 3. All advertising materials mentioning features or use of this +.\" software must display the following acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" 4. The names "Apache Server" and "Apache Group" must not be used to +.\" endorse or promote products derived from this software without +.\" prior written permission. +.\" +.\" 5. Products derived from this software may not be called "Apache" +.\" nor may "Apache" appear in their names without prior written +.\" permission of the Apache Group. +.\" +.\" 6. Redistributions of any form whatsoever must retain the following +.\" acknowledgment: +.\" "This product includes software developed by the Apache Group +.\" for use in the Apache HTTP server project (http://www.apache.org/)." +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +.\" OF THE POSSIBILITY OF SUCH DAMAGE. +.\" ==================================================================== +.\" +.\" This software consists of voluntary contributions made by many +.\" individuals on behalf of the Apache Group and was originally based +.\" on public domain software written at the National Center for +.\" Supercomputing Applications, University of Illinois, Urbana-Champaign. +.\" For more information on the Apache Group and the Apache HTTP server +.\" project, please see . +.SH NAME +suexec \- Switch User For Exec +.SH SYNOPSIS +No synopsis for usage, because this program +is used internally by Apache only. +.PP +.SH DESCRIPTION +.B suexec +is the "wrapper" support program for the suEXEC behaviour for Apache. +It is run from within Apache automatically to switch the user when +an external program has to be run under a different user. For more +information about suEXEC see the document `Apache suEXEC Support' +under http://www.apache.org/docs/suexec.html . +.PD +.SH SEE ALSO +.BR httpd(8) +. diff --git a/modules/aaa/.indent.pro b/modules/aaa/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/aaa/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/aaa/mod_auth_anon.dsp b/modules/aaa/mod_auth_anon.dsp new file mode 100644 index 0000000000..afe8cc7e2f --- /dev/null +++ b/modules/aaa/mod_auth_anon.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleAuthAnon" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleAuthAnon - 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 "ApacheModuleAuthAnon.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 "ApacheModuleAuthAnon.mak"\ + CFG="ApacheModuleAuthAnon - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleAuthAnon - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleAuthAnon - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleAuthAnon - 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 ".\ApacheModuleAuthAnonR" +# PROP Intermediate_Dir ".\ApacheModuleAuthAnonR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleAuthAnon - 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 ".\ApacheModuleAuthAnonD" +# PROP Intermediate_Dir ".\ApacheModuleAuthAnonD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleAuthAnon - Win32 Release" +# Name "ApacheModuleAuthAnon - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_auth_anon.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/arch/win32/mod_isapi.c b/modules/arch/win32/mod_isapi.c new file mode 100644 index 0000000000..fa002708ba --- /dev/null +++ b/modules/arch/win32/mod_isapi.c @@ -0,0 +1,569 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_isapi.c - Internet Server Application (ISA) module for Apache + * by Alexei Kosut + * + * This module implements Microsoft's ISAPI, allowing Apache (when running + * under Windows) to load Internet Server Applications (ISAPI extensions). + * It implements all of the ISAPI 2.0 specification, except for the + * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI + * extensions that use only synchronous I/O and are compatible with the + * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should + * function as well). + * + * To load, simply place the ISA in a location in the document tree. + * Then add an "AddHandler isapi-isa dll" into your config file. + * You should now be able to load ISAPI DLLs just be reffering to their + * URLs. Make sure the ExecCGI option is active in the directory + * the ISA is in. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_log.h" +#include "util_script.h" + +/* We use the exact same header file as the original */ +#include + +/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER, + define this to conform */ +#define RELAX_HEADER_RULE + +module isapi_module; + +/* Our "Connection ID" structure */ + +typedef struct { + LPEXTENSION_CONTROL_BLOCK ecb; + request_rec *r; + int status; +} isapi_cid; + +/* Declare the ISAPI functions */ + +BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, + LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer); +BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, + DWORD dwReserved); +BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize); +BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, + LPVOID lpvBuffer, LPDWORD lpdwSize, + LPDWORD lpdwDataType); + +/* + The optimiser blows it totally here. What happens is that autos are addressed relative to the + stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere + between setting isapi_entry and calling through it. We work around the problem by forcing it to + use frame pointers. +*/ +#pragma optimize("y",off) + +int isapi_handler (request_rec *r) { + LPEXTENSION_CONTROL_BLOCK ecb = + ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK)); + HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO)); + + HINSTANCE isapi_handle; + BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */ + DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */ + BOOL (*isapi_term)(DWORD); /* optional entry point 3 */ + + isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid)); + table *e = r->subprocess_env; + int retval; + + /* Use similar restrictions as CGIs */ + + if (!(ap_allow_options(r) & OPT_EXECCGI)) + return FORBIDDEN; + + if (r->finfo.st_mode == 0) + return NOT_FOUND; + + if (S_ISDIR(r->finfo.st_mode)) + return FORBIDDEN; + + /* Load the module */ + + if (!(isapi_handle = LoadLibraryEx(r->filename, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "Could not load DLL: %s", r->filename); + return SERVER_ERROR; + } + + if (!(isapi_version = + (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "DLL could not load GetExtensionVersion(): %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + if (!(isapi_entry = + (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "DLL could not load HttpExtensionProc(): %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension")); + + /* Run GetExtensionVersion() */ + + if ((*isapi_version)(pVer) != TRUE) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "ISAPI GetExtensionVersion() failed: %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + /* Set up variables */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + + /* Set up connection ID */ + ecb->ConnID = (HCONN)cid; + cid->ecb = ecb; + cid->r = r; + cid->status = 0; + + ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK); + ecb->dwVersion = MAKELONG(0, 2); + ecb->dwHttpStatusCode = 0; + strcpy(ecb->lpszLogData, ""); + ecb->lpszMethod = r->method; + ecb->lpszQueryString = ap_table_get(e, "QUERY_STRING"); + ecb->lpszPathInfo = ap_table_get(e, "PATH_INFO"); + ecb->lpszPathTranslated = ap_table_get(e, "PATH_TRANSLATED"); + ecb->lpszContentType = ap_table_get(e, "CONTENT_TYPE"); + + /* Set up client input */ + if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return retval; + } + + if (ap_should_client_block(r)) { + /* Unlike IIS, which limits this to 48k, we read the whole + * sucker in. I suppose this could be bad for memory if someone + * uploaded the complete works of Shakespeare. Well, WebSite + * does the same thing. + */ + long to_read = atol(ap_table_get(e, "CONTENT_LENGTH")); + long read; + + /* Actually, let's cap it at 48k, until we figure out what + * to do with this... we don't want a Content-Length: 1000000000 + * taking out the machine. + */ + + if (to_read > 49152) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read); + + if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + /* Although its not to spec, IIS seems to null-terminate + * its lpdData string. So we will too. To make sure + * cbAvailable matches cbTotalBytes, we'll up the latter + * and equalize them. + */ + ecb->cbAvailable = ecb->cbTotalBytes = read + 1; + ecb->lpbData[read] = '\0'; + } + else { + ecb->cbTotalBytes = 0; + ecb->cbAvailable = 0; + ecb->lpbData = NULL; + } + + /* Set up the callbacks */ + + ecb->GetServerVariable = &GetServerVariable; + ecb->WriteClient = &WriteClient; + ecb->ReadClient = &ReadClient; + ecb->ServerSupportFunction = &ServerSupportFunction; + + /* All right... try and load the sucker */ + retval = (*isapi_entry)(ecb); + + /* Set the status (for logging) */ + if (ecb->dwHttpStatusCode) + r->status = ecb->dwHttpStatusCode; + + /* Check for a log message - and log it */ + if (ecb->lpszLogData && strcmp(ecb->lpszLogData, "")) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "%s: %s", ecb->lpszLogData, r->filename); + + /* All done with the DLL... get rid of it */ + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + + switch(retval) { + case HSE_STATUS_SUCCESS: + case HSE_STATUS_SUCCESS_AND_KEEP_CONN: + /* Ignore the keepalive stuff; Apache handles it just fine without + * the ISA's "advice". + */ + + if (cid->status) /* We have a special status to return */ + return cid->status; + + return OK; + case HSE_STATUS_PENDING: /* We don't support this */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + case HSE_STATUS_ERROR: + default: + return SERVER_ERROR; + } + +} +#pragma optimize("",on) + +BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, + LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) { + request_rec *r = ((isapi_cid *)hConn)->r; + table *e = r->subprocess_env; + const char *result; + + /* Mostly, we just grab it from the environment, but there are + * a couple of special cases + */ + + if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) { + /* We don't support NT users, so this is always the same as + * REMOTE_USER + */ + result = ap_table_get(e, "REMOTE_USER"); + } + else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) { + /* Apache doesn't support secure requests inherently, so + * we have no way of knowing. We'll be conservative, and say + * all requests are insecure. + */ + result = "0"; + } + else if (!strcasecmp(lpszVariableName, "URL")) { + result = r->uri; + } + else { + result = ap_table_get(e, lpszVariableName); + } + + if (result) { + if (strlen(result) > *lpdwSizeofBuffer) { + *lpdwSizeofBuffer = strlen(result); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + strncpy(lpvBuffer, result, *lpdwSizeofBuffer); + return TRUE; + } + + /* Didn't find it */ + SetLastError(ERROR_INVALID_INDEX); + return FALSE; +} + +BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, + DWORD dwReserved) { + request_rec *r = ((isapi_cid *)ConnID)->r; + int writ; /* written, actually, but why shouldn't I make up words? */ + + /* We only support synchronous writing */ + if (dwReserved && dwReserved != HSE_IO_SYNC) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) { + SetLastError(ERROR); /* XXX: Find the right error code */ + return FALSE; + } + + *lpwdwBytes = writ; + return TRUE; +} + +BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) { + /* Doesn't need to do anything; we've read all the data already */ + return TRUE; +} + +/* XXX: There is an O(n^2) attack possible here. */ +BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, + LPVOID lpvBuffer, LPDWORD lpdwSize, + LPDWORD lpdwDataType) { + isapi_cid *cid = (isapi_cid *)hConn; + request_rec *subreq, *r = cid->r; + char *data; + + switch (dwHSERequest) { + case HSE_REQ_SEND_URL_REDIRECT_RESP: + /* Set the status to be returned when the HttpExtensionProc() + * is done. + */ + ap_table_set (r->headers_out, "Location", lpvBuffer); + cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT; + return TRUE; + + case HSE_REQ_SEND_URL: + /* Read any additional input */ + + if (r->remaining > 0) { + char argsbuffer[HUGE_STRING_LEN]; + + while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)); + } + + /* Reset the method to GET */ + r->method = ap_pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + /* Don't let anyone think there's still data */ + ap_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect((char *)lpvBuffer, r); + return TRUE; + + case HSE_REQ_SEND_RESPONSE_HEADER: + r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK"); + sscanf(r->status_line, "%d", &r->status); + cid->ecb->dwHttpStatusCode = r->status; + + /* Now fill in the HTTP headers, and the rest of it. Ick. + * lpdwDataType contains a string that has headers (in MIME + * format), a blank like, then (possibly) data. We need + * to parse it. + * + * Easy case first: + */ + if (!lpdwDataType) { + ap_send_http_header(r); + return TRUE; + } + + /* Make a copy - don't disturb the original */ + data = ap_pstrdup(r->pool, (char *)lpdwDataType); + + /* We *should* break before this while loop ends */ + while (*data) { + char *value, *lf = strchr(data, '\n'); + int p; + +#ifdef RELAX_HEADER_RULE + if (lf) + *lf = '\0'; +#else + if (!lf) { /* Huh? Invalid data, I think */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ISA sent invalid headers: %s", r->filename); + SetLastError(ERROR); /* XXX: Find right error */ + return FALSE; + } + + /* Get rid of \n and \r */ + *lf = '\0'; +#endif + p = strlen(data); + if (p > 0 && data[p-1] == '\r') data[p-1] = '\0'; + + /* End of headers */ + if (*data == '\0') { +#ifdef RELAX_HEADER_RULE + if (lf) +#endif + data = lf + 1; /* Reset data */ + break; + } + + if (!(value = strchr(data, ':'))) { + SetLastError(ERROR); /* XXX: Find right error */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ISA sent invalid headers", r->filename); + return FALSE; + } + + *value++ = '\0'; + while (*value && ap_isspace(*value)) ++value; + + /* Check all the special-case headers. Similar to what + * ap_scan_script_header_err() does (see that function for + * more detail) + */ + + if (!strcasecmp(data, "Content-Type")) { + char *tmp; + /* Nuke trailing whitespace */ + + char *endp = value + strlen(value) - 1; + while (endp > value && ap_isspace(*endp)) *endp-- = '\0'; + + tmp = ap_pstrdup (r->pool, value); + ap_str_tolower(tmp); + r->content_type = tmp; + } + else if (!strcasecmp(data, "Content-Length")) { + ap_table_set(r->headers_out, data, value); + } + else if (!strcasecmp(data, "Transfer-Encoding")) { + ap_table_set(r->headers_out, data, value); + } + else if (!strcasecmp(data, "Set-Cookie")) { + ap_table_add(r->err_headers_out, data, value); + } + else { + ap_table_merge(r->err_headers_out, data, value); + } + + /* Reset data */ +#ifdef RELAX_HEADER_RULE + if (!lf) { + data += p; + break; + } +#endif + data = lf + 1; + } + + /* All the headers should be set now */ + + ap_send_http_header(r); + + /* Any data left should now be sent directly */ + ap_rputs(data, r); + + return TRUE; + + case HSE_REQ_MAP_URL_TO_PATH: + /* Map a URL to a filename */ + subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer, + *lpdwSize), r); + + GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL); + + /* IIS puts a trailing slash on directories, Apache doesn't */ + + if (S_ISDIR (subreq->finfo.st_mode)) { + int l = strlen((char *)lpvBuffer); + + ((char *)lpvBuffer)[l] = '\\'; + ((char *)lpvBuffer)[l + 1] = '\0'; + } + + return TRUE; + + case HSE_REQ_DONE_WITH_SESSION: + /* Do nothing... since we don't support async I/O, they'll + * return from HttpExtensionProc soon + */ + return TRUE; + + /* We don't support all this async I/O, Microsoft-specific stuff */ + case HSE_REQ_IO_COMPLETION: + case HSE_REQ_TRANSMIT_FILE: + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } +} + +handler_rec isapi_handlers[] = { +{ "isapi-isa", isapi_handler }, +{ NULL} +}; + +module isapi_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + isapi_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/modules/cache/.indent.pro b/modules/cache/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/cache/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/echo/.indent.pro b/modules/echo/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/echo/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/experimental/.indent.pro b/modules/experimental/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/experimental/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/filters/.indent.pro b/modules/filters/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/filters/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/generators/.indent.pro b/modules/generators/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/generators/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/generators/mod_info.dsp b/modules/generators/mod_info.dsp new file mode 100644 index 0000000000..cf85646335 --- /dev/null +++ b/modules/generators/mod_info.dsp @@ -0,0 +1,112 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleInfo" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleInfo - 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 "ApacheModuleInfo.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 "ApacheModuleInfo.mak" CFG="ApacheModuleInfo - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleInfo - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleInfo - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleInfo - 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 ".\ApacheModuleInfoR" +# PROP Intermediate_Dir ".\ApacheModuleInfoR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleInfo - 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 ".\ApacheModuleInfoD" +# PROP Intermediate_Dir ".\ApacheModuleInfoD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleInfo - Win32 Release" +# Name "ApacheModuleInfo - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_info.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/generators/mod_status.dsp b/modules/generators/mod_status.dsp new file mode 100644 index 0000000000..8bc9271959 --- /dev/null +++ b/modules/generators/mod_status.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleStatus" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleStatus - 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 "ApacheModuleStatus.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 "ApacheModuleStatus.mak"\ + CFG="ApacheModuleStatus - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleStatus - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleStatus - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleStatus - 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 ".\ApacheModuleStatusR" +# PROP Intermediate_Dir ".\ApacheModuleStatusR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleStatus - 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 ".\ApacheModuleStatusD" +# PROP Intermediate_Dir ".\ApacheModuleStatusD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleStatus - Win32 Release" +# Name "ApacheModuleStatus - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_status.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/http/.indent.pro b/modules/http/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/http/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/http/http_core.c b/modules/http/http_core.c new file mode 100644 index 0000000000..e02d32ec5f --- /dev/null +++ b/modules/http/http_core.c @@ -0,0 +1,3214 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" /* For index_of_response(). Grump. */ +#include "http_request.h" +#include "http_conf_globals.h" +#include "http_vhost.h" +#include "http_main.h" /* For the default_handler below... */ +#include "http_log.h" +#include "rfc1413.h" +#include "util_md5.h" +#include "scoreboard.h" +#include "fnmatch.h" + +#ifdef USE_MMAP_FILES +#include + +/* mmap support for static files based on ideas from John Heidemann's + * patch against 1.0.5. See + * . + */ + +/* Files have to be at least this big before they're mmap()d. This is to deal + * with systems where the expense of doing an mmap() and an munmap() outweighs + * the benefit for small files. It shouldn't be set lower than 1. + */ +#ifndef MMAP_THRESHOLD +#ifdef SUNOS4 +#define MMAP_THRESHOLD (8*1024) +#else +#define MMAP_THRESHOLD 1 +#endif +#endif +#endif +#ifndef MMAP_LIMIT +#define MMAP_LIMIT (4*1024*1024) +#endif + +/* Server core module... This module provides support for really basic + * server operations, including options and commands which control the + * operation of other modules. Consider this the bureaucracy module. + * + * The core module also defines handlers, etc., do handle just enough + * to allow a server with the core module ONLY to actually serve documents + * (though it slaps DefaultType on all of 'em); this was useful in testing, + * but may not be worth preserving. + * + * This file could almost be mod_core.c, except for the stuff which affects + * the http_conf_globals. + */ + +static void *create_core_dir_config(pool *a, char *dir) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_pcalloc(a, sizeof(core_dir_config)); + if (!dir || dir[strlen(dir) - 1] == '/') { + conf->d = dir; + } + else if (strncmp(dir, "proxy:", 6) == 0) { + conf->d = ap_pstrdup(a, dir); + } + else { + conf->d = ap_pstrcat(a, dir, "/", NULL); + } + conf->d_is_fnmatch = conf->d ? (ap_is_fnmatch(conf->d) != 0) : 0; + conf->d_components = conf->d ? ap_count_dirs(conf->d) : 0; + + conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL; + conf->opts_add = conf->opts_remove = OPT_NONE; + conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL; + + conf->content_md5 = 2; + + conf->use_canonical_name = USE_CANONICAL_NAME_UNSET; + + conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET; + conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */ + conf->satisfy = SATISFY_NOSPEC; + +#ifdef RLIMIT_CPU + conf->limit_cpu = NULL; +#endif +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + conf->limit_mem = NULL; +#endif +#ifdef RLIMIT_NPROC + conf->limit_nproc = NULL; +#endif + + conf->limit_req_body = 0; + conf->sec = ap_make_array(a, 2, sizeof(void *)); +#ifdef WIN32 + conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET; +#endif + + conf->server_signature = srv_sig_unset; + + return (void *)conf; +} + +static void *merge_core_dir_configs(pool *a, void *basev, void *newv) +{ + core_dir_config *base = (core_dir_config *)basev; + core_dir_config *new = (core_dir_config *)newv; + core_dir_config *conf; + int i; + + conf = (core_dir_config *)ap_palloc(a, sizeof(core_dir_config)); + memcpy((char *)conf, (const char *)base, sizeof(core_dir_config)); + if (base->response_code_strings) { + conf->response_code_strings = + ap_palloc(a, sizeof(*conf->response_code_strings) + * RESPONSE_CODES); + memcpy(conf->response_code_strings, base->response_code_strings, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + } + + conf->d = new->d; + conf->d_is_fnmatch = new->d_is_fnmatch; + conf->d_components = new->d_components; + conf->r = new->r; + + if (new->opts & OPT_UNSET) { + /* there was no explicit setting of new->opts, so we merge + * preserve the invariant (opts_add & opts_remove) == 0 + */ + conf->opts_add = (conf->opts_add & ~new->opts_remove) | new->opts_add; + conf->opts_remove = (conf->opts_remove & ~new->opts_add) + | new->opts_remove; + conf->opts = (conf->opts & ~conf->opts_remove) | conf->opts_add; + if ((base->opts & OPT_INCNOEXEC) && (new->opts & OPT_INCLUDES)) { + conf->opts = (conf->opts & ~OPT_INCNOEXEC) | OPT_INCLUDES; + } + } + else { + /* otherwise we just copy, because an explicit opts setting + * overrides all earlier +/- modifiers + */ + conf->opts = new->opts; + conf->opts_add = new->opts_add; + conf->opts_remove = new->opts_remove; + } + + if (!(new->override & OR_UNSET)) { + conf->override = new->override; + } + if (new->ap_default_type) { + conf->ap_default_type = new->ap_default_type; + } + + if (new->ap_auth_type) { + conf->ap_auth_type = new->ap_auth_type; + } + if (new->ap_auth_name) { + conf->ap_auth_name = new->ap_auth_name; + } + if (new->ap_requires) { + conf->ap_requires = new->ap_requires; + } + + if (new->response_code_strings) { + if (conf->response_code_strings == NULL) { + conf->response_code_strings = ap_palloc(a, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + memcpy(conf->response_code_strings, new->response_code_strings, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + } + else { + for (i = 0; i < RESPONSE_CODES; ++i) { + if (new->response_code_strings[i] != NULL) { + conf->response_code_strings[i] + = new->response_code_strings[i]; + } + } + } + } + if (new->hostname_lookups != HOSTNAME_LOOKUP_UNSET) { + conf->hostname_lookups = new->hostname_lookups; + } + if ((new->do_rfc1413 & 2) == 0) { + conf->do_rfc1413 = new->do_rfc1413; + } + if ((new->content_md5 & 2) == 0) { + conf->content_md5 = new->content_md5; + } + if (new->use_canonical_name != USE_CANONICAL_NAME_UNSET) { + conf->use_canonical_name = new->use_canonical_name; + } + +#ifdef RLIMIT_CPU + if (new->limit_cpu) { + conf->limit_cpu = new->limit_cpu; + } +#endif +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) + if (new->limit_mem) { + conf->limit_mem = new->limit_mem; + } +#endif +#ifdef RLIMIT_NPROC + if (new->limit_nproc) { + conf->limit_nproc = new->limit_nproc; + } +#endif + + if (new->limit_req_body) { + conf->limit_req_body = new->limit_req_body; + } + conf->sec = ap_append_arrays(a, base->sec, new->sec); + + if (new->satisfy != SATISFY_NOSPEC) { + conf->satisfy = new->satisfy; + } + +#ifdef WIN32 + if (new->script_interpreter_source != INTERPRETER_SOURCE_UNSET) { + conf->script_interpreter_source = new->script_interpreter_source; + } +#endif + + if (new->server_signature != srv_sig_unset) { + conf->server_signature = new->server_signature; + } + + return (void*)conf; +} + +static void *create_core_server_config(pool *a, server_rec *s) +{ + core_server_config *conf; + int is_virtual = s->is_virtual; + + conf = (core_server_config *)ap_pcalloc(a, sizeof(core_server_config)); +#ifdef GPROF + conf->gprof_dir = NULL; +#endif + conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME; + conf->ap_document_root = is_virtual ? NULL : DOCUMENT_LOCATION; + conf->sec = ap_make_array(a, 40, sizeof(void *)); + conf->sec_url = ap_make_array(a, 40, sizeof(void *)); + + return (void *)conf; +} + +static void *merge_core_server_configs(pool *p, void *basev, void *virtv) +{ + core_server_config *base = (core_server_config *)basev; + core_server_config *virt = (core_server_config *)virtv; + core_server_config *conf; + + conf = (core_server_config *)ap_pcalloc(p, sizeof(core_server_config)); + *conf = *virt; + if (!conf->access_name) { + conf->access_name = base->access_name; + } + if (!conf->ap_document_root) { + conf->ap_document_root = base->ap_document_root; + } + conf->sec = ap_append_arrays(p, base->sec, virt->sec); + conf->sec_url = ap_append_arrays(p, base->sec_url, virt->sec_url); + + return conf; +} + +/* Add per-directory configuration entry (for section); + * these are part of the core server config. + */ + +CORE_EXPORT(void) ap_add_per_dir_conf(server_rec *s, void *dir_config) +{ + core_server_config *sconf = ap_get_module_config(s->module_config, + &core_module); + void **new_space = (void **)ap_push_array(sconf->sec); + + *new_space = dir_config; +} + +CORE_EXPORT(void) ap_add_per_url_conf(server_rec *s, void *url_config) +{ + core_server_config *sconf = ap_get_module_config(s->module_config, + &core_module); + void **new_space = (void **)ap_push_array(sconf->sec_url); + + *new_space = url_config; +} + +CORE_EXPORT(void) ap_add_file_conf(core_dir_config *conf, void *url_config) +{ + void **new_space = (void **)ap_push_array(conf->sec); + + *new_space = url_config; +} + +/* core_reorder_directories reorders the directory sections such that the + * 1-component sections come first, then the 2-component, and so on, finally + * followed by the "special" sections. A section is "special" if it's a regex, + * or if it doesn't start with / -- consider proxy: matching. All movements + * are in-order to preserve the ordering of the sections from the config files. + * See directory_walk(). + */ + +#ifdef HAVE_DRIVE_LETTERS +#define IS_SPECIAL(entry_core) \ + ((entry_core)->r != NULL \ + || ((entry_core)->d[0] != '/' && (entry_core)->d[1] != ':')) +#else +#define IS_SPECIAL(entry_core) \ + ((entry_core)->r != NULL || (entry_core)->d[0] != '/') +#endif + +/* We need to do a stable sort, qsort isn't stable. So to make it stable + * we'll be maintaining the original index into the list, and using it + * as the minor key during sorting. The major key is the number of + * components (where a "special" section has infinite components). + */ +struct reorder_sort_rec { + void *elt; + int orig_index; +}; + +static int reorder_sorter(const void *va, const void *vb) +{ + const struct reorder_sort_rec *a = va; + const struct reorder_sort_rec *b = vb; + core_dir_config *core_a; + core_dir_config *core_b; + + core_a = (core_dir_config *)ap_get_module_config(a->elt, &core_module); + core_b = (core_dir_config *)ap_get_module_config(b->elt, &core_module); + if (IS_SPECIAL(core_a)) { + if (!IS_SPECIAL(core_b)) { + return 1; + } + } + else if (IS_SPECIAL(core_b)) { + return -1; + } + else { + /* we know they're both not special */ + if (core_a->d_components < core_b->d_components) { + return -1; + } + else if (core_a->d_components > core_b->d_components) { + return 1; + } + } + /* Either they're both special, or they're both not special and have the + * same number of components. In any event, we now have to compare + * the minor key. */ + return a->orig_index - b->orig_index; +} + +void ap_core_reorder_directories(pool *p, server_rec *s) +{ + core_server_config *sconf; + array_header *sec; + struct reorder_sort_rec *sortbin; + int nelts; + void **elts; + int i; + pool *tmp; + + sconf = ap_get_module_config(s->module_config, &core_module); + sec = sconf->sec; + nelts = sec->nelts; + elts = (void **)sec->elts; + + /* we have to allocate tmp space to do a stable sort */ + tmp = ap_make_sub_pool(p); + sortbin = ap_palloc(tmp, sec->nelts * sizeof(*sortbin)); + for (i = 0; i < nelts; ++i) { + sortbin[i].orig_index = i; + sortbin[i].elt = elts[i]; + } + + qsort(sortbin, nelts, sizeof(*sortbin), reorder_sorter); + + /* and now copy back to the original array */ + for (i = 0; i < nelts; ++i) { + elts[i] = sortbin[i].elt; + } + + ap_destroy_pool(tmp); +} + +/***************************************************************** + * + * There are some elements of the core config structures in which + * other modules have a legitimate interest (this is ugly, but necessary + * to preserve NCSA back-compatibility). So, we have a bunch of accessors + * here... + */ + +API_EXPORT(int) ap_allow_options(request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + return conf->opts; +} + +API_EXPORT(int) ap_allow_overrides(request_rec *r) +{ + core_dir_config *conf; + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + return conf->override; +} + +API_EXPORT(const char *) ap_auth_type(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_auth_type; +} + +API_EXPORT(const char *) ap_auth_name(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_auth_name; +} + +API_EXPORT(const char *) ap_default_type(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_default_type + ? conf->ap_default_type + : DEFAULT_CONTENT_TYPE; +} + +API_EXPORT(const char *) ap_document_root(request_rec *r) /* Don't use this! */ +{ + core_server_config *conf; + + conf = (core_server_config *)ap_get_module_config(r->server->module_config, + &core_module); + return conf->ap_document_root; +} + +API_EXPORT(const array_header *) ap_requires(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + return conf->ap_requires; +} + +API_EXPORT(int) ap_satisfies(request_rec *r) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + return conf->satisfy; +} + +/* Should probably just get rid of this... the only code that cares is + * part of the core anyway (and in fact, it isn't publicised to other + * modules). + */ + +char *ap_response_code_string(request_rec *r, int error_index) +{ + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + if (conf->response_code_strings == NULL) { + return NULL; + } + return conf->response_code_strings[error_index]; +} + + +/* Code from Harald Hanche-Olsen */ +static ap_inline void do_double_reverse (conn_rec *conn) +{ + struct hostent *hptr; + + if (conn->double_reverse) { + /* already done */ + return; + } + if (conn->remote_host == NULL || conn->remote_host[0] == '\0') { + /* single reverse failed, so don't bother */ + conn->double_reverse = -1; + return; + } + hptr = gethostbyname(conn->remote_host); + if (hptr) { + char **haddr; + + for (haddr = hptr->h_addr_list; *haddr; haddr++) { + if (((struct in_addr *)(*haddr))->s_addr + == conn->remote_addr.sin_addr.s_addr) { + conn->double_reverse = 1; + return; + } + } + } + conn->double_reverse = -1; +} + +API_EXPORT(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config, + int type) +{ + struct in_addr *iaddr; + struct hostent *hptr; + int hostname_lookups; + int old_stat = SERVER_DEAD; /* we shouldn't ever be in this state */ + + /* If we haven't checked the host name, and we want to */ + if (dir_config) { + hostname_lookups = + ((core_dir_config *)ap_get_module_config(dir_config, &core_module)) + ->hostname_lookups; + if (hostname_lookups == HOSTNAME_LOOKUP_UNSET) { + hostname_lookups = HOSTNAME_LOOKUP_OFF; + } + } + else { + /* the default */ + hostname_lookups = HOSTNAME_LOOKUP_OFF; + } + + if (type != REMOTE_NOLOOKUP + && conn->remote_host == NULL + && (type == REMOTE_DOUBLE_REV + || hostname_lookups != HOSTNAME_LOOKUP_OFF)) { + old_stat = ap_update_child_status(conn->child_num, SERVER_BUSY_DNS, + (request_rec*)NULL); + iaddr = &(conn->remote_addr.sin_addr); + hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET); + if (hptr != NULL) { + conn->remote_host = ap_pstrdup(conn->pool, (void *)hptr->h_name); + ap_str_tolower(conn->remote_host); + + if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) { + do_double_reverse(conn); + if (conn->double_reverse != 1) { + conn->remote_host = NULL; + } + } + } + /* if failed, set it to the NULL string to indicate error */ + if (conn->remote_host == NULL) { + conn->remote_host = ""; + } + } + if (type == REMOTE_DOUBLE_REV) { + do_double_reverse(conn); + if (conn->double_reverse == -1) { + return NULL; + } + } + if (old_stat != SERVER_DEAD) { + (void)ap_update_child_status(conn->child_num, old_stat, + (request_rec*)NULL); + } + +/* + * Return the desired information; either the remote DNS name, if found, + * or either NULL (if the hostname was requested) or the IP address + * (if any identifier was requested). + */ + if (conn->remote_host != NULL && conn->remote_host[0] != '\0') { + return conn->remote_host; + } + else { + if (type == REMOTE_HOST || type == REMOTE_DOUBLE_REV) { + return NULL; + } + else { + return conn->remote_ip; + } + } +} + +API_EXPORT(const char *) ap_get_remote_logname(request_rec *r) +{ + core_dir_config *dir_conf; + + if (r->connection->remote_logname != NULL) { + return r->connection->remote_logname; + } + +/* If we haven't checked the identity, and we want to */ + dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + if (dir_conf->do_rfc1413 & 1) { + return ap_rfc1413(r->connection, r->server); + } + else { + return NULL; + } +} + +/* There are two options regarding what the "name" of a server is. The + * "canonical" name as defined by ServerName and Port, or the "client's + * name" as supplied by a possible Host: header or full URI. We never + * trust the port passed in the client's headers, we always use the + * port of the actual socket. + * + * The DNS option to UseCanonicalName causes this routine to do a + * reverse lookup on the local IP address of the connectiona and use + * that for the ServerName. This makes its value more reliable while + * at the same time allowing Demon's magic virtual hosting to work. + * The assumption is that DNS lookups are sufficiently quick... + * -- fanf 1998-10-03 + */ +API_EXPORT(const char *) ap_get_server_name(request_rec *r) +{ + conn_rec *conn = r->connection; + core_dir_config *d; + + d = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + if (d->use_canonical_name == USE_CANONICAL_NAME_OFF) { + return r->hostname ? r->hostname : r->server->server_hostname; + } + if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) { + if (conn->local_host == NULL) { + struct in_addr *iaddr; + struct hostent *hptr; + int old_stat; + old_stat = ap_update_child_status(conn->child_num, + SERVER_BUSY_DNS, r); + iaddr = &(conn->local_addr.sin_addr); + hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), + AF_INET); + if (hptr != NULL) { + conn->local_host = ap_pstrdup(conn->pool, + (void *)hptr->h_name); + ap_str_tolower(conn->local_host); + } + else { + conn->local_host = ap_pstrdup(conn->pool, + r->server->server_hostname); + } + (void) ap_update_child_status(conn->child_num, old_stat, r); + } + return conn->local_host; + } + /* default */ + return r->server->server_hostname; +} + +API_EXPORT(unsigned) ap_get_server_port(const request_rec *r) +{ + unsigned port; + core_dir_config *d = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + port = r->server->port ? r->server->port : ap_default_port(r); + + if (d->use_canonical_name == USE_CANONICAL_NAME_OFF + || d->use_canonical_name == USE_CANONICAL_NAME_DNS) { + return r->hostname ? ntohs(r->connection->local_addr.sin_port) + : port; + } + /* default */ + return port; +} + +API_EXPORT(char *) ap_construct_url(pool *p, const char *uri, + request_rec *r) +{ + unsigned port = ap_get_server_port(r); + const char *host = ap_get_server_name(r); + + if (ap_is_default_port(port, r)) { + return ap_pstrcat(p, ap_http_method(r), "://", host, uri, NULL); + } + return ap_psprintf(p, "%s://%s:%u%s", ap_http_method(r), host, port, uri); +} + +API_EXPORT(unsigned long) ap_get_limit_req_body(const request_rec *r) +{ + core_dir_config *d = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + + return d->limit_req_body; +} + +#ifdef WIN32 +static char* get_interpreter_from_win32_registry(pool *p, const char* ext) +{ + char extension_path[] = "SOFTWARE\\Classes\\"; + char executable_path[] = "\\SHELL\\OPEN\\COMMAND"; + + HKEY hkeyOpen; + DWORD type; + int size; + int result; + char *keyName; + char *buffer; + char *s; + + if (!ext) + return NULL; + /* + * Future optimization: + * When the registry is successfully searched, store the interpreter + * string in a table to make subsequent look-ups faster + */ + + /* Open the key associated with the script extension */ + keyName = ap_pstrcat(p, extension_path, ext, NULL); + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, + &hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* Read to NULL buffer to find value size */ + size = 0; + result = RegQueryValueEx(hkeyOpen, "", NULL, &type, NULL, &size); + + if (result == ERROR_SUCCESS) { + buffer = ap_palloc(p, size); + result = RegQueryValueEx(hkeyOpen, "", NULL, &type, buffer, &size); + } + + RegCloseKey(hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* Open the key associated with the interpreter path */ + keyName = ap_pstrcat(p, extension_path, buffer, executable_path, NULL); + + result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, + &hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* Read to NULL buffer to find value size */ + size = 0; + result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size); + + if (result == ERROR_SUCCESS) { + buffer = ap_palloc(p, size); + result = RegQueryValueEx(hkeyOpen, "", 0, &type, buffer, &size); + } + + RegCloseKey(hkeyOpen); + + if (result != ERROR_SUCCESS) + return NULL; + + /* + * The canonical way shell command entries are entered in the Win32 + * registry is as follows: + * shell [options] "%1" + * where + * shell - full path name to interpreter or shell to run. + * E.g., c:\usr\local\ntreskit\perl\bin\perl.exe + * options - optional switches + * E.g., \C + * "%1" - Place holder for file to run the shell against. + * Typically quoted. + * + * If we find a %1 or a quoted %1, lop it off. + */ + if (buffer && *buffer) { + if ((s = strstr(buffer, "\"%1"))) + *s = '\0'; + else if ((s = strstr(buffer, "%1"))) + *s = '\0'; + } + + return buffer; +} + +API_EXPORT (file_type_e) ap_get_win32_interpreter(const request_rec *r, + char** interpreter ) +{ + HANDLE hFile; + DWORD nBytesRead; + BOOLEAN bResult; + char buffer[1024]; + core_dir_config *d; + int i; + file_type_e fileType = eFileTypeUNKNOWN; + char *ext = NULL; + char *exename = NULL; + + d = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + + /* Find the file extension */ + exename = strrchr(r->filename, '/'); + if (!exename) { + exename = strrchr(r->filename, '\\'); + } + if (!exename) { + exename = r->filename; + } + else { + exename++; + } + ext = strrchr(exename, '.'); + + if (ext && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) { + return eFileTypeEXE32; + } + + /* If the file has an extension and it is not .com and not .exe and + * we've been instructed to search the registry, then do it! + */ + if (ext && strcasecmp(ext,".exe") && strcasecmp(ext,".com") && + d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY) { + /* Check the registry */ + *interpreter = get_interpreter_from_win32_registry(r->pool, ext); + if (*interpreter) + return eFileTypeSCRIPT; + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r->server, + "ScriptInterpreterSource config directive set to \"registry\".\n\t" + "Registry was searched but interpreter not found. Trying the shebang line."); + } + } + + /* Need to peek into the file figure out what it really is... */ + hFile = CreateFile(r->filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return eFileTypeUNKNOWN; + } + bResult = ReadFile(hFile, (void*) &buffer, sizeof(buffer) - 1, + &nBytesRead, NULL); + if (!bResult || (nBytesRead == 0)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ReadFile(%s) failed", r->filename); + CloseHandle(hFile); + return eFileTypeUNKNOWN; + } + CloseHandle(hFile); + buffer[nBytesRead] = '\0'; + + /* Script or executable, that is the question... */ + if ((buffer[0] == '#') && (buffer[1] == '!')) { + /* Assuming file is a script since it starts with a shebang */ + fileType = eFileTypeSCRIPT; + for (i = 2; i < sizeof(buffer); i++) { + if ((buffer[i] == '\r') + || (buffer[i] == '\n')) { + break; + } + } + buffer[i] = '\0'; + for (i = 2; buffer[i] == ' ' ; ++i) + ; + *interpreter = ap_pstrdup(r->pool, buffer + i ); + } + else { + /* Not a script, is it an executable? */ + IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer; + if ((nBytesRead >= sizeof(IMAGE_DOS_HEADER)) && (hdr->e_magic == IMAGE_DOS_SIGNATURE)) { + if (hdr->e_lfarlc < 0x40) + fileType = eFileTypeEXE16; + else + fileType = eFileTypeEXE32; + } + else + fileType = eFileTypeUNKNOWN; + } + + return fileType; +} +#endif + +/***************************************************************** + * + * Commands... this module handles almost all of the NCSA httpd.conf + * commands, but most of the old srm.conf is in the the modules. + */ + +static const char end_directory_section[] = ""; +static const char end_directorymatch_section[] = ""; +static const char end_location_section[] = ""; +static const char end_locationmatch_section[] = ""; +static const char end_files_section[] = ""; +static const char end_filesmatch_section[] = ""; +static const char end_virtualhost_section[] = ""; +static const char end_ifmodule_section[] = ""; +static const char end_ifdefine_section[] = ""; + + +API_EXPORT(const char *) ap_check_cmd_context(cmd_parms *cmd, + unsigned forbidden) +{ + const char *gt = (cmd->cmd->name[0] == '<' + && cmd->cmd->name[strlen(cmd->cmd->name)-1] != '>') + ? ">" : ""; + + if ((forbidden & NOT_IN_VIRTUALHOST) && cmd->server->is_virtual) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within section", NULL); + } + + if ((forbidden & NOT_IN_LIMIT) && cmd->limited != -1) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within section", NULL); + } + + if ((forbidden & NOT_IN_DIR_LOC_FILE) == NOT_IN_DIR_LOC_FILE + && cmd->path != NULL) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within " + "section", NULL); + } + + if (((forbidden & NOT_IN_DIRECTORY) + && (cmd->end_token == end_directory_section + || cmd->end_token == end_directorymatch_section)) + || ((forbidden & NOT_IN_LOCATION) + && (cmd->end_token == end_location_section + || cmd->end_token == end_locationmatch_section)) + || ((forbidden & NOT_IN_FILES) + && (cmd->end_token == end_files_section + || cmd->end_token == end_filesmatch_section))) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, gt, + " cannot occur within <", cmd->end_token+2, + " section", NULL); + } + + return NULL; +} + +static const char *set_access_name(cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + conf->access_name = ap_pstrdup(cmd->pool, arg); + return NULL; +} + +#ifdef GPROF +static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + conf->gprof_dir = ap_pstrdup(cmd->pool, arg); + return NULL; +} +#endif /*GPROF*/ + +static const char *set_document_root(cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + arg = ap_os_canonical_filename(cmd->pool, arg); + if (ap_configtestonly && ap_docrootcheck && !ap_is_directory(arg)) { + if (cmd->server->is_virtual) { + fprintf(stderr, "Warning: DocumentRoot [%s] does not exist\n", + arg); + } + else { + return "DocumentRoot must be a directory"; + } + } + + conf->ap_document_root = arg; + return NULL; +} + +API_EXPORT(void) ap_custom_response(request_rec *r, int status, char *string) +{ + core_dir_config *conf = + ap_get_module_config(r->per_dir_config, &core_module); + int idx; + + if(conf->response_code_strings == NULL) { + conf->response_code_strings = + ap_pcalloc(r->pool, + sizeof(*conf->response_code_strings) * + RESPONSE_CODES); + } + + idx = ap_index_of_response(status); + + conf->response_code_strings[idx] = + ((ap_is_url(string) || (*string == '/')) && (*string != '"')) ? + ap_pstrdup(r->pool, string) : ap_pstrcat(r->pool, "\"", string, NULL); +} + +static const char *set_error_document(cmd_parms *cmd, core_dir_config *conf, + char *line) +{ + int error_number, index_number, idx500; + char *w; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* 1st parameter should be a 3 digit number, which we recognize; + * convert it into an array index + */ + + w = ap_getword_conf_nc(cmd->pool, &line); + error_number = atoi(w); + + idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR); + + if (error_number == HTTP_INTERNAL_SERVER_ERROR) { + index_number = idx500; + } + else if ((index_number = ap_index_of_response(error_number)) == idx500) { + return ap_pstrcat(cmd->pool, "Unsupported HTTP response code ", + w, NULL); + } + + /* The entry should be ignored if it is a full URL for a 401 error */ + + if (error_number == 401 && + line[0] != '/' && line[0] != '"') { /* Ignore it... */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server, + "cannot use a full URL in a 401 ErrorDocument " + "directive --- ignoring!"); + } + else { /* Store it... */ + if (conf->response_code_strings == NULL) { + conf->response_code_strings = + ap_pcalloc(cmd->pool, + sizeof(*conf->response_code_strings) * RESPONSE_CODES); + } + conf->response_code_strings[index_number] = ap_pstrdup(cmd->pool, line); + } + + return NULL; +} + +/* access.conf commands... + * + * The *only* thing that can appear in access.conf at top level is a + * section. NB we need to have a way to cut the srm_command_loop + * invoked by dirsection (i.e., ) short when is seen. + * We do that by returning an error, which dirsection itself recognizes and + * discards as harmless. Cheesy, but it works. + */ + +static const char *set_override(cmd_parms *cmd, core_dir_config *d, + const char *l) +{ + char *w; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + d->override = OR_NONE; + while (l[0]) { + w = ap_getword_conf(cmd->pool, &l); + if (!strcasecmp(w, "Limit")) { + d->override |= OR_LIMIT; + } + else if (!strcasecmp(w, "Options")) { + d->override |= OR_OPTIONS; + } + else if (!strcasecmp(w, "FileInfo")) { + d->override |= OR_FILEINFO; + } + else if (!strcasecmp(w, "AuthConfig")) { + d->override |= OR_AUTHCFG; + } + else if (!strcasecmp(w, "Indexes")) { + d->override |= OR_INDEXES; + } + else if (!strcasecmp(w, "None")) { + d->override = OR_NONE; + } + else if (!strcasecmp(w, "All")) { + d->override = OR_ALL; + } + else { + return ap_pstrcat(cmd->pool, "Illegal override option ", w, NULL); + } + d->override &= ~OR_UNSET; + } + + return NULL; +} + +static const char *set_options(cmd_parms *cmd, core_dir_config *d, + const char *l) +{ + allow_options_t opt; + int first = 1; + char action; + + while (l[0]) { + char *w = ap_getword_conf(cmd->pool, &l); + action = '\0'; + + if (*w == '+' || *w == '-') { + action = *(w++); + } + else if (first) { + d->opts = OPT_NONE; + first = 0; + } + + if (!strcasecmp(w, "Indexes")) { + opt = OPT_INDEXES; + } + else if (!strcasecmp(w, "Includes")) { + opt = OPT_INCLUDES; + } + else if (!strcasecmp(w, "IncludesNOEXEC")) { + opt = (OPT_INCLUDES | OPT_INCNOEXEC); + } + else if (!strcasecmp(w, "FollowSymLinks")) { + opt = OPT_SYM_LINKS; + } + else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) { + opt = OPT_SYM_OWNER; + } + else if (!strcasecmp(w, "execCGI")) { + opt = OPT_EXECCGI; + } + else if (!strcasecmp(w, "MultiViews")) { + opt = OPT_MULTI; + } + else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */ + opt = OPT_MULTI|OPT_EXECCGI; + } + else if (!strcasecmp(w, "None")) { + opt = OPT_NONE; + } + else if (!strcasecmp(w, "All")) { + opt = OPT_ALL; + } + else { + return ap_pstrcat(cmd->pool, "Illegal option ", w, NULL); + } + + /* we ensure the invariant (d->opts_add & d->opts_remove) == 0 */ + if (action == '-') { + d->opts_remove |= opt; + d->opts_add &= ~opt; + d->opts &= ~opt; + } + else if (action == '+') { + d->opts_add |= opt; + d->opts_remove &= ~opt; + d->opts |= opt; + } + else { + d->opts |= opt; + } + } + + return NULL; +} + +static const char *satisfy(cmd_parms *cmd, core_dir_config *c, char *arg) +{ + if (!strcasecmp(arg, "all")) { + c->satisfy = SATISFY_ALL; + } + else if (!strcasecmp(arg, "any")) { + c->satisfy = SATISFY_ANY; + } + else { + return "Satisfy either 'any' or 'all'."; + } + return NULL; +} + +static const char *require(cmd_parms *cmd, core_dir_config *c, char *arg) +{ + require_line *r; + + if (!c->ap_requires) { + c->ap_requires = ap_make_array(cmd->pool, 2, sizeof(require_line)); + } + r = (require_line *)ap_push_array(c->ap_requires); + r->requirement = ap_pstrdup(cmd->pool, arg); + r->method_mask = cmd->limited; + return NULL; +} + +CORE_EXPORT_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *limited_methods = ap_getword(cmd->pool, &arg, '>'); + void *tog = cmd->cmd->cmd_data; + int limited = 0; + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* XXX: NB: Currently, we have no way of checking + * whether or sections are closed properly. + * (If we would add a srm_command_loop() here we might...) + */ + + while (limited_methods[0]) { + char *method = ap_getword_conf(cmd->pool, &limited_methods); + int methnum = ap_method_number_of(method); + + if (methnum == M_TRACE && !tog) { + return "TRACE cannot be controlled by "; + } + else if (methnum == M_INVALID) { + return ap_pstrcat(cmd->pool, "unknown method \"", method, + "\" in " : ">", NULL); + } + else { + limited |= (1 << methnum); + } + } + + /* Killing two features with one function, + * if (tog == NULL) , else + */ + cmd->limited = tog ? ~limited : limited; + return NULL; +} + +static const char *endlimit_section(cmd_parms *cmd, void *dummy, void *dummy2) +{ + void *tog = cmd->cmd->cmd_data; + + if (cmd->limited == -1) { + return tog ? " unexpected" : " unexpected"; + } + + cmd->limited = -1; + return NULL; +} + +/* + * When a section is not closed properly when end-of-file is reached, + * then an error message should be printed: + */ +static const char *missing_endsection(cmd_parms *cmd, int nest) +{ + if (nest < 2) { + return ap_psprintf(cmd->pool, "Missing %s directive at end-of-file", + cmd->end_token); + } + return ap_psprintf(cmd->pool, "%d missing %s directives at end-of-file", + nest, cmd->end_token); +} + +/* We use this in and , to ensure that + * people don't get bitten by wrong-cased regex matches + */ + +#ifdef WIN32 +#define USE_ICASE REG_ICASE +#else +#define USE_ICASE 0 +#endif + +static const char *end_nested_section(cmd_parms *cmd, void *dummy) +{ + if (cmd->end_token == NULL) { + return ap_pstrcat(cmd->pool, cmd->cmd->name, + " without matching <", cmd->cmd->name + 2, + " section", NULL); + } + /* + * This '!=' may look weird on a string comparison, but it's correct -- + * it's been set up so that checking for two pointers to the same datum + * is valid here. And faster. + */ + if (cmd->cmd->name != cmd->end_token) { + return ap_pstrcat(cmd->pool, "Expected ", cmd->end_token, " but saw ", + cmd->cmd->name, NULL); + } + return cmd->end_token; +} + +/* + * Report a missing-'>' syntax error. + */ +static char *unclosed_directive(cmd_parms *cmd) +{ + return ap_pstrcat(cmd->pool, cmd->cmd->name, + "> directive missing closing '>'", NULL); +} + +static const char *dirsection(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *errmsg; + char *endp = strrchr(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + core_dir_config *conf; + void *new_dir_conf = ap_create_per_dir_config(cmd->pool); + regex_t *r = NULL; + const char *old_end_token; + 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 unclosed_directive(cmd); + } + + *endp = '\0'; + + cmd->path = ap_getword_conf(cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + if (thiscmd->cmd_data) { /* */ + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else if (!strcmp(cmd->path, "~")) { + cmd->path = ap_getword_conf(cmd->pool, &arg); + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else { + /* Ensure that the pathname is canonical */ + cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path); + } + + old_end_token = cmd->end_token; + cmd->end_token = thiscmd->cmd_data ? end_directorymatch_section : end_directory_section; + errmsg = ap_srm_command_loop(cmd, new_dir_conf); + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + if (errmsg != (thiscmd->cmd_data + ? end_directorymatch_section + : end_directory_section)) { + return errmsg; + } + + conf = (core_dir_config *)ap_get_module_config(new_dir_conf, &core_module); + conf->r = r; + + ap_add_per_dir_conf(cmd->server, new_dir_conf); + + if (*arg != '\0') { + return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name, + "> arguments not (yet) supported.", NULL); + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static const char *urlsection(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *errmsg; + char *endp = strrchr(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + core_dir_config *conf; + regex_t *r = NULL; + const char *old_end_token; + const command_rec *thiscmd = cmd->cmd; + + void *new_url_conf = ap_create_per_dir_config(cmd->pool); + + 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 unclosed_directive(cmd); + } + + *endp = '\0'; + + cmd->path = ap_getword_conf(cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + if (thiscmd->cmd_data) { /* */ + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + } + else if (!strcmp(cmd->path, "~")) { + cmd->path = ap_getword_conf(cmd->pool, &arg); + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + } + + old_end_token = cmd->end_token; + cmd->end_token = thiscmd->cmd_data ? end_locationmatch_section + : end_location_section; + errmsg = ap_srm_command_loop(cmd, new_url_conf); + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + if (errmsg != (thiscmd->cmd_data + ? end_locationmatch_section + : end_location_section)) { + return errmsg; + } + + conf = (core_dir_config *)ap_get_module_config(new_url_conf, &core_module); + conf->d = ap_pstrdup(cmd->pool, cmd->path); /* No mangling, please */ + conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0; + conf->r = r; + + ap_add_per_url_conf(cmd->server, new_url_conf); + + if (*arg != '\0') { + return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name, + "> arguments not (yet) supported.", NULL); + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static const char *filesection(cmd_parms *cmd, core_dir_config *c, + const char *arg) +{ + const char *errmsg; + char *endp = strrchr(arg, '>'); + int old_overrides = cmd->override; + char *old_path = cmd->path; + core_dir_config *conf; + regex_t *r = NULL; + const char *old_end_token; + const command_rec *thiscmd = cmd->cmd; + + void *new_file_conf = ap_create_per_dir_config(cmd->pool); + + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_LOCATION); + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + cmd->path = ap_getword_conf(cmd->pool, &arg); + /* Only if not an .htaccess file */ + if (!old_path) { + cmd->override = OR_ALL|ACCESS_CONF; + } + + if (thiscmd->cmd_data) { /* */ + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else if (!strcmp(cmd->path, "~")) { + cmd->path = ap_getword_conf(cmd->pool, &arg); + r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE); + } + else { + /* Ensure that the pathname is canonical */ + cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path); + } + + old_end_token = cmd->end_token; + cmd->end_token = thiscmd->cmd_data ? end_filesmatch_section : end_files_section; + errmsg = ap_srm_command_loop(cmd, new_file_conf); + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + if (errmsg != (thiscmd->cmd_data + ? end_filesmatch_section + : end_files_section)) { + return errmsg; + } + + conf = (core_dir_config *)ap_get_module_config(new_file_conf, + &core_module); + conf->d = cmd->path; + conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0; + conf->r = r; + + ap_add_file_conf(c, new_file_conf); + + if (*arg != '\0') { + return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name, + "> arguments not (yet) supported.", NULL); + } + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +/* XXX: NB: Currently, we have no way of checking + * whether sections are closed properly. + * Extra (redundant, unpaired) directives are + * simply silently ignored. + */ +static const char *end_ifmod(cmd_parms *cmd, void *dummy) +{ + return NULL; +} + +static const char *start_ifmod(cmd_parms *cmd, void *dummy, char *arg) +{ + char *endp = strrchr(arg, '>'); + char l[MAX_STRING_LEN]; + int not = (arg[0] == '!'); + module *found; + int nest = 1; + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + if (not) { + arg++; + } + + found = ap_find_linked_module(arg); + + if ((!not && found) || (not && !found)) { + return NULL; + } + + while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) { + if (!strncasecmp(l, "")) { + nest--; + } + } + + if (nest) { + cmd->end_token = end_ifmodule_section; + return missing_endsection(cmd, nest); + } + return NULL; +} + +API_EXPORT(int) ap_exists_config_define(char *name) +{ + char **defines; + int i; + + defines = (char **)ap_server_config_defines->elts; + for (i = 0; i < ap_server_config_defines->nelts; i++) { + if (strcmp(defines[i], name) == 0) { + return 1; + } + } + return 0; +} + +static const char *end_ifdefine(cmd_parms *cmd, void *dummy) +{ + return NULL; +} + +static const char *start_ifdefine(cmd_parms *cmd, void *dummy, char *arg) +{ + char *endp; + char l[MAX_STRING_LEN]; + int defined; + int not = 0; + int nest = 1; + + endp = strrchr(arg, '>'); + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + if (arg[0] == '!') { + not = 1; + arg++; + } + + defined = ap_exists_config_define(arg); + + if ((!not && defined) || (not && !defined)) { + return NULL; + } + + while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) { + if (!strncasecmp(l, "")) { + nest--; + } + } + if (nest) { + cmd->end_token = end_ifdefine_section; + return missing_endsection(cmd, nest); + } + return NULL; +} + +/* httpd.conf commands... beginning with the business */ + +static const char *virtualhost_section(cmd_parms *cmd, void *dummy, char *arg) +{ + server_rec *main_server = cmd->server, *s; + const char *errmsg; + char *endp = strrchr(arg, '>'); + pool *p = cmd->pool, *ptemp = cmd->temp_pool; + const char *old_end_token; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (endp == NULL) { + return unclosed_directive(cmd); + } + + *endp = '\0'; + + /* FIXME: There's another feature waiting to happen here -- since you + can now put multiple addresses/names on a single + you might want to use it to group common definitions and then + define other "subhosts" with their individual differences. But + personally I'd rather just do it with a macro preprocessor. -djg */ + if (main_server->is_virtual) { + return " doesn't nest!"; + } + + errmsg = ap_init_virtual_host(p, arg, main_server, &s); + if (errmsg) { + return errmsg; + } + + s->next = main_server->next; + main_server->next = s; + + s->defn_name = cmd->config_file->name; + s->defn_line_number = cmd->config_file->line_number; + + old_end_token = cmd->end_token; + cmd->end_token = end_virtualhost_section; + cmd->server = s; + errmsg = ap_srm_command_loop(cmd, s->lookup_defaults); + cmd->server = main_server; + if (errmsg == NULL) { + errmsg = missing_endsection(cmd, 1); + } + cmd->end_token = old_end_token; + + if (s->srm_confname) { + ap_process_resource_config(s, s->srm_confname, p, ptemp); + } + + if (s->access_confname) { + ap_process_resource_config(s, s->access_confname, p, ptemp); + } + + if (errmsg == end_virtualhost_section) { + return NULL; + } + return errmsg; +} + +static const char *set_server_alias(cmd_parms *cmd, void *dummy, + const char *arg) +{ + if (!cmd->server->names) { + return "ServerAlias only used in "; + } + while (*arg) { + char **item, *name = ap_getword_conf(cmd->pool, &arg); + if (ap_is_matchexp(name)) { + item = (char **)ap_push_array(cmd->server->wild_names); + } + else { + item = (char **)ap_push_array(cmd->server->names); + } + *item = name; + } + return NULL; +} + +static const char *add_module_command(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (!ap_add_named_module(arg)) { + return ap_pstrcat(cmd->pool, "Cannot add module via name '", arg, + "': not in list of loaded modules", NULL); + } + return NULL; +} + +static const char *clear_module_list_command(cmd_parms *cmd, void *dummy) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_clear_module_list(); + return NULL; +} + +static const char *set_server_string_slot(cmd_parms *cmd, void *dummy, + char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int)(long)cmd->info; + char *struct_ptr = (char *)cmd->server; + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + *(char **)(struct_ptr + offset) = arg; + return NULL; +} + +static const char *server_type(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (!strcasecmp(arg, "inetd")) { + ap_standalone = 0; + } + else if (!strcasecmp(arg, "standalone")) { + ap_standalone = 1; + } + else { + return "ServerType must be either 'inetd' or 'standalone'"; + } + + return NULL; +} + +static const char *server_port(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int port; + + if (err != NULL) { + return err; + } + port = atoi(arg); + if (port <= 0 || port >= 65536) { /* 65536 == 1<<16 */ + return ap_pstrcat(cmd->temp_pool, "The port number \"", arg, + "\" is outside the appropriate range " + "(i.e., 1..65535).", NULL); + } + cmd->server->port = port; + return NULL; +} + +static const char *set_signature_flag(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (strcasecmp(arg, "On") == 0) { + d->server_signature = srv_sig_on; + } + else if (strcasecmp(arg, "Off") == 0) { + d->server_signature = srv_sig_off; + } + else if (strcasecmp(arg, "EMail") == 0) { + d->server_signature = srv_sig_withmail; + } + else { + return "ServerSignature: use one of: off | on | email"; + } + return NULL; +} + +static const char *set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg) +{ + int s = atoi(arg); + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (s < 512 && s != 0) { + return "SendBufferSize must be >= 512 bytes, or 0 for system default."; + } + cmd->server->send_buffer_size = s; + return NULL; +} + +static const char *set_user(cmd_parms *cmd, void *dummy, char *arg) +{ +#ifdef WIN32 + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server, + "User directive has no affect on Win32"); + cmd->server->server_uid = ap_user_id = 1; +#else + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (!cmd->server->is_virtual) { + ap_user_name = arg; + cmd->server->server_uid = ap_user_id = ap_uname2id(arg); + } + else { + if (ap_suexec_enabled) { + cmd->server->server_uid = ap_uname2id(arg); + } + else { + cmd->server->server_uid = ap_user_id; + fprintf(stderr, + "Warning: User directive in " + "requires SUEXEC wrapper.\n"); + } + } +#if !defined (BIG_SECURITY_HOLE) && !defined (OS2) + if (cmd->server->server_uid == 0) { + fprintf(stderr, + "Error:\tApache has not been designed to serve pages while\n" + "\trunning as root. There are known race conditions that\n" + "\twill allow any local user to read any file on the system.\n" + "\tIf you still desire to serve pages as root then\n" + "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n" + "\tsrc/Configuration file and rebuild the server. It is\n" + "\tstrongly suggested that you instead modify the User\n" + "\tdirective in your httpd.conf file to list a non-root\n" + "\tuser.\n"); + exit (1); + } +#endif +#endif /* WIN32 */ + + return NULL; +} + +static const char *set_group(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (!cmd->server->is_virtual) { + cmd->server->server_gid = ap_group_id = ap_gname2id(arg); + } + else { + if (ap_suexec_enabled) { + cmd->server->server_gid = ap_gname2id(arg); + } + else { + cmd->server->server_gid = ap_group_id; + fprintf(stderr, + "Warning: Group directive in requires " + "SUEXEC wrapper.\n"); + } + } + + return NULL; +} + +static const char *set_server_root(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + arg = ap_os_canonical_filename(cmd->pool, arg); + + if (!ap_is_directory(arg)) { + return "ServerRoot must be a valid directory"; + } + ap_cpystrn(ap_server_root, arg, + sizeof(ap_server_root)); + return NULL; +} + +static const char *set_timeout(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->timeout = atoi(arg); + return NULL; +} + +static const char *set_keep_alive_timeout(cmd_parms *cmd, void *dummy, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->keep_alive_timeout = atoi(arg); + return NULL; +} + +static const char *set_keep_alive(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* We've changed it to On/Off, but used to use numbers + * so we accept anything but "Off" or "0" as "On" + */ + if (!strcasecmp(arg, "off") || !strcmp(arg, "0")) { + cmd->server->keep_alive = 0; + } + else { + cmd->server->keep_alive = 1; + } + return NULL; +} + +static const char *set_keep_alive_max(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->keep_alive_max = atoi(arg); + return NULL; +} + +static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (cmd->server->is_virtual) { + return "PidFile directive not allowed in "; + } + ap_pid_fname = arg; + return NULL; +} + +static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_scoreboard_fname = arg; + return NULL; +} + +static const char *set_lockfile(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_lock_fname = arg; + return NULL; +} + +static const char *set_idcheck(cmd_parms *cmd, core_dir_config *d, int arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + d->do_rfc1413 = arg != 0; + return NULL; +} + +static const char *set_hostname_lookups(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (!strcasecmp(arg, "on")) { + d->hostname_lookups = HOSTNAME_LOOKUP_ON; + } + else if (!strcasecmp(arg, "off")) { + d->hostname_lookups = HOSTNAME_LOOKUP_OFF; + } + else if (!strcasecmp(arg, "double")) { + d->hostname_lookups = HOSTNAME_LOOKUP_DOUBLE; + } + else { + return "parameter must be 'on', 'off', or 'double'"; + } + return NULL; +} + +static const char *set_serverpath(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + cmd->server->path = arg; + cmd->server->pathlen = strlen(arg); + return NULL; +} + +static const char *set_content_md5(cmd_parms *cmd, core_dir_config *d, int arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + d->content_md5 = arg != 0; + return NULL; +} + +static const char *set_use_canonical_name(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if (strcasecmp(arg, "on") == 0) { + d->use_canonical_name = USE_CANONICAL_NAME_ON; + } + else if (strcasecmp(arg, "off") == 0) { + d->use_canonical_name = USE_CANONICAL_NAME_OFF; + } + else if (strcasecmp(arg, "dns") == 0) { + d->use_canonical_name = USE_CANONICAL_NAME_DNS; + } + else { + return "parameter must be 'on', 'off', or 'dns'"; + } + return NULL; +} + +static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg) +{ +#ifdef WIN32 + fprintf(stderr, "WARNING: StartServers has no effect on Win32\n"); +#else + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_to_start = atoi(arg); +#endif + return NULL; +} + +static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_min_free = atoi(arg); + if (ap_daemons_min_free <= 0) { + fprintf(stderr, "WARNING: detected MinSpareServers set to non-positive.\n"); + fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n"); + fprintf(stderr, "Please read the documentation.\n"); + ap_daemons_min_free = 1; + } + + return NULL; +} + +static const char *set_max_free_servers(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_max_free = atoi(arg); + return NULL; +} + +static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_limit = atoi(arg); + if (ap_daemons_limit > HARD_SERVER_LIMIT) { + fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit " + "of %d servers,\n", ap_daemons_limit, HARD_SERVER_LIMIT); + fprintf(stderr, " lowering MaxClients to %d. To increase, please " + "see the\n", HARD_SERVER_LIMIT); + fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n"); + ap_daemons_limit = HARD_SERVER_LIMIT; + } + else if (ap_daemons_limit < 1) { + fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n"); + ap_daemons_limit = 1; + } + return NULL; +} + +static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_max_requests_per_child = atoi(arg); + return NULL; +} + +static const char *set_threads(cmd_parms *cmd, void *dummy, char *arg) { + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_threads_per_child = atoi(arg); + if (ap_threads_per_child > HARD_SERVER_LIMIT) { + fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time limit " + "of %d threads,\n", ap_threads_per_child, HARD_SERVER_LIMIT); + fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please " + "see the\n", HARD_SERVER_LIMIT); + fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n"); + ap_threads_per_child = HARD_SERVER_LIMIT; + } + else if (ap_threads_per_child < 1) { + fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n"); + ap_threads_per_child = 1; + } + + return NULL; +} + +static const char *set_excess_requests(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_excess_requests_per_child = atoi(arg); + return NULL; +} + + +#if defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS) +static void set_rlimit(cmd_parms *cmd, struct rlimit **plimit, const char *arg, + const char * arg2, int type) +{ + char *str; + struct rlimit *limit; + /* If your platform doesn't define rlim_t then typedef it in ap_config.h */ + rlim_t cur = 0; + rlim_t max = 0; + + *plimit = (struct rlimit *)ap_pcalloc(cmd->pool, sizeof(**plimit)); + limit = *plimit; + if ((getrlimit(type, limit)) != 0) { + *plimit = NULL; + ap_log_error(APLOG_MARK, APLOG_ERR, cmd->server, + "%s: getrlimit failed", cmd->cmd->name); + return; + } + + if ((str = ap_getword_conf(cmd->pool, &arg))) { + if (!strcasecmp(str, "max")) { + cur = limit->rlim_max; + } + else { + cur = atol(str); + } + } + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server, + "Invalid parameters for %s", cmd->cmd->name); + return; + } + + if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) { + max = atol(str); + } + + /* if we aren't running as root, cannot increase max */ + if (geteuid()) { + limit->rlim_cur = cur; + if (max) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server, + "Must be uid 0 to raise maximum %s", cmd->cmd->name); + } + } + else { + if (cur) { + limit->rlim_cur = cur; + } + if (max) { + limit->rlim_max = max; + } + } +} +#endif + +#if !defined (RLIMIT_CPU) || !(defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)) || !defined (RLIMIT_NPROC) +static const char *no_set_limit(cmd_parms *cmd, core_dir_config *conf, + char *arg, char *arg2) +{ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server, + "%s not supported on this platform", cmd->cmd->name); + return NULL; +} +#endif + +#ifdef RLIMIT_CPU +static const char *set_limit_cpu(cmd_parms *cmd, core_dir_config *conf, + char *arg, char *arg2) +{ + set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU); + return NULL; +} +#endif + +#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS) +static const char *set_limit_mem(cmd_parms *cmd, core_dir_config *conf, + char *arg, char * arg2) +{ +#if defined(RLIMIT_AS) + set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS); +#elif defined(RLIMIT_DATA) + set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA); +#elif defined(RLIMIT_VMEM) + set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM); +#endif + return NULL; +} +#endif + +#ifdef RLIMIT_NPROC +static const char *set_limit_nproc(cmd_parms *cmd, core_dir_config *conf, + char *arg, char * arg2) +{ + set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC); + return NULL; +} +#endif + +static const char *set_bind_address(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_bind_address.s_addr = ap_get_virthost_addr(arg, NULL); + return NULL; +} + +static const char *set_listener(cmd_parms *cmd, void *dummy, char *ips) +{ + listen_rec *new; + char *ports; + unsigned short port; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ports = strchr(ips, ':'); + if (ports != NULL) { + if (ports == ips) { + return "Missing IP address"; + } + else if (ports[1] == '\0') { + return "Address must end in :"; + } + *(ports++) = '\0'; + } + else { + ports = ips; + } + + new=ap_pcalloc(cmd->pool, sizeof(listen_rec)); + new->local_addr.sin_family = AF_INET; + if (ports == ips) { /* no address */ + new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + else { + new->local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL); + } + port = atoi(ports); + if (!port) { + return "Port must be numeric"; + } + new->local_addr.sin_port = htons(port); + new->fd = -1; + new->used = 0; + new->next = ap_listeners; + ap_listeners = new; + return NULL; +} + +static const char *set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg) +{ + int b; + + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + b = atoi(arg); + if (b < 1) { + return "ListenBacklog must be > 0"; + } + ap_listenbacklog = b; + return NULL; +} + +static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg) +{ + struct stat finfo; + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + arg = ap_server_root_relative(cmd->pool, arg); + if ((stat(arg, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) { + return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", arg, + " does not exist or is not a directory", NULL); + } + ap_cpystrn(ap_coredump_dir, arg, sizeof(ap_coredump_dir)); + return NULL; +} + +static const char *include_config (cmd_parms *cmd, void *dummy, char *name) +{ + name = ap_server_root_relative(cmd->pool, name); + + ap_process_resource_config(cmd->server, name, cmd->pool, cmd->temp_pool); + + return NULL; +} + +static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg) +{ + char *str; + + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + if ((str = ap_getword_conf(cmd->pool, &arg))) { + if (!strcasecmp(str, "emerg")) { + cmd->server->loglevel = APLOG_EMERG; + } + else if (!strcasecmp(str, "alert")) { + cmd->server->loglevel = APLOG_ALERT; + } + else if (!strcasecmp(str, "crit")) { + cmd->server->loglevel = APLOG_CRIT; + } + else if (!strcasecmp(str, "error")) { + cmd->server->loglevel = APLOG_ERR; + } + else if (!strcasecmp(str, "warn")) { + cmd->server->loglevel = APLOG_WARNING; + } + else if (!strcasecmp(str, "notice")) { + cmd->server->loglevel = APLOG_NOTICE; + } + else if (!strcasecmp(str, "info")) { + cmd->server->loglevel = APLOG_INFO; + } + else if (!strcasecmp(str, "debug")) { + cmd->server->loglevel = APLOG_DEBUG; + } + else { + return "LogLevel requires level keyword: one of " + "emerg/alert/crit/error/warn/notice/info/debug"; + } + } + else { + return "LogLevel requires level keyword"; + } + + return NULL; +} + +API_EXPORT(const char *) ap_psignature(const char *prefix, request_rec *r) +{ + char sport[20]; + core_dir_config *conf; + + conf = (core_dir_config *)ap_get_module_config(r->per_dir_config, + &core_module); + if ((conf->server_signature == srv_sig_off) + || (conf->server_signature == srv_sig_unset)) { + return ""; + } + + ap_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r)); + + if (conf->server_signature == srv_sig_withmail) { + return ap_pstrcat(r->pool, prefix, "
" SERVER_BASEVERSION + " Server at server->server_admin, "\">", + ap_get_server_name(r), " Port ", sport, + "
\n", NULL); + } + return ap_pstrcat(r->pool, prefix, "
" SERVER_BASEVERSION + " Server at ", ap_get_server_name(r), " Port ", sport, + "
\n", NULL); +} + +/* + * Load an authorisation realm into our location configuration, applying the + * usual rules that apply to realms. + */ +static const char *set_authname(cmd_parms *cmd, void *mconfig, char *word1) +{ + core_dir_config *aconfig = (core_dir_config *)mconfig; + + aconfig->ap_auth_name = ap_escape_quotes(cmd->pool, word1); + return NULL; +} + +#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */ +static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + return os_set_account(cmd->pool, name); +} +#endif /*_OSD_POSIX*/ + +/* + * Handle a request to include the server's OS platform in the Server + * response header field (the ServerTokens directive). Unfortunately + * this requires a new global in order to communicate the setting back to + * http_main so it can insert the information in the right place in the + * string. + */ + +static const char *set_serv_tokens(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (!strcasecmp(arg, "OS")) { + ap_server_tokens = SrvTk_OS; + } + else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) { + ap_server_tokens = SrvTk_MIN; + } + else { + ap_server_tokens = SrvTk_FULL; + } + return NULL; +} + +static const char *set_limit_req_line(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int lim; + + if (err != NULL) { + return err; + } + lim = atoi(arg); + if (lim < 0) { + return ap_pstrcat(cmd->temp_pool, "LimitRequestLine \"", arg, + "\" must be a non-negative integer", NULL); + } + if (lim > DEFAULT_LIMIT_REQUEST_LINE) { + return ap_psprintf(cmd->temp_pool, "LimitRequestLine \"%s\" " + "must not exceed the precompiled maximum of %d", + arg, DEFAULT_LIMIT_REQUEST_LINE); + } + cmd->server->limit_req_line = lim; + return NULL; +} + +static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int lim; + + if (err != NULL) { + return err; + } + lim = atoi(arg); + if (lim < 0) { + return ap_pstrcat(cmd->temp_pool, "LimitRequestFieldsize \"", arg, + "\" must be a non-negative integer (0 = no limit)", + NULL); + } + if (lim > DEFAULT_LIMIT_REQUEST_FIELDSIZE) { + return ap_psprintf(cmd->temp_pool, "LimitRequestFieldsize \"%s\" " + "must not exceed the precompiled maximum of %d", + arg, DEFAULT_LIMIT_REQUEST_FIELDSIZE); + } + cmd->server->limit_req_fieldsize = lim; + return NULL; +} + +static const char *set_limit_req_fields(cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err = ap_check_cmd_context(cmd, + NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); + int lim; + + if (err != NULL) { + return err; + } + lim = atoi(arg); + if (lim < 0) { + return ap_pstrcat(cmd->temp_pool, "LimitRequestFields \"", arg, + "\" must be a non-negative integer (0 = no limit)", + NULL); + } + cmd->server->limit_req_fields = lim; + return NULL; +} + +static const char *set_limit_req_body(cmd_parms *cmd, core_dir_config *conf, + char *arg) +{ + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + if (err != NULL) { + return err; + } + + /* WTF: If strtoul is not portable, then write a replacement. + * Instead we have an idiotic define in httpd.h that prevents + * it from being used even when it is available. Sheesh. + */ + conf->limit_req_body = (unsigned long)strtol(arg, (char **)NULL, 10); + return NULL; +} + +#ifdef WIN32 +static const char *set_interpreter_source(cmd_parms *cmd, core_dir_config *d, + char *arg) +{ + if (!strcasecmp(arg, "registry")) { + d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY; + } else if (!strcasecmp(arg, "script")) { + d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG; + } else { + d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG; + } + return NULL; +} +#endif + +/* Note --- ErrorDocument will now work from .htaccess files. + * The AllowOverride of Fileinfo allows webmasters to turn it off + */ + +static const command_rec core_cmds[] = { + +/* Old access config file commands */ + +{ "" }, +{ "" }, +{ "" }, +{ "" }, +{ "", endlimit_section, NULL, OR_ALL, NO_ARGS, + "Marks end of " }, +{ "", endlimit_section, (void*)1, OR_ALL, NO_ARGS, + "Marks end of " }, +{ "" }, +{ "" }, +{ "" }, +{ "" }, +{ "" }, +{ "AuthType", ap_set_string_slot, + (void*)XtOffsetOf(core_dir_config, ap_auth_type), OR_AUTHCFG, TAKE1, + "An HTTP authorization type (e.g., \"Basic\")" }, +{ "AuthName", set_authname, NULL, OR_AUTHCFG, TAKE1, + "The authentication realm (e.g. \"Members Only\")" }, +{ "Require", require, NULL, OR_AUTHCFG, RAW_ARGS, + "Selects which authenticated users or groups may access a protected space" }, +{ "Satisfy", satisfy, NULL, OR_AUTHCFG, TAKE1, + "access policy if both allow and require used ('all' or 'any')" }, +#ifdef GPROF +{ "GprofDir", set_gprof_dir, NULL, RSRC_CONF, TAKE1, + "Directory to plop gmon.out files" }, +#endif + +/* Old resource config file commands */ + +{ "AccessFileName", set_access_name, NULL, RSRC_CONF, RAW_ARGS, + "Name(s) of per-directory config files (default: .htaccess)" }, +{ "DocumentRoot", set_document_root, NULL, RSRC_CONF, TAKE1, + "Root directory of the document tree" }, +{ "ErrorDocument", set_error_document, NULL, OR_FILEINFO, RAW_ARGS, + "Change responses for HTTP errors" }, +{ "AllowOverride", set_override, NULL, ACCESS_CONF, RAW_ARGS, + "Controls what groups of directives can be configured by per-directory " + "config files" }, +{ "Options", set_options, NULL, OR_OPTIONS, RAW_ARGS, + "Set a number of attributes for a given directory" }, +{ "DefaultType", ap_set_string_slot, + (void*)XtOffsetOf (core_dir_config, ap_default_type), + OR_FILEINFO, TAKE1, "the default MIME type for untypable files" }, + +/* Old server config file commands */ + +{ "ServerType", server_type, NULL, RSRC_CONF, TAKE1, + "'inetd' or 'standalone'"}, +{ "Port", server_port, NULL, RSRC_CONF, TAKE1, "A TCP port number"}, +{ "HostnameLookups", set_hostname_lookups, NULL, ACCESS_CONF|RSRC_CONF, TAKE1, + "\"on\" to enable, \"off\" to disable reverse DNS lookups, or \"double\" to " + "enable double-reverse DNS lookups" }, +{ "User", set_user, NULL, RSRC_CONF, TAKE1, + "Effective user id for this server"}, +{ "Group", set_group, NULL, RSRC_CONF, TAKE1, + "Effective group id for this server"}, +{ "ServerAdmin", set_server_string_slot, + (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF, TAKE1, + "The email address of the server administrator" }, +{ "ServerName", set_server_string_slot, + (void *)XtOffsetOf (server_rec, server_hostname), RSRC_CONF, TAKE1, + "The hostname of the server" }, +{ "ServerSignature", set_signature_flag, NULL, OR_ALL, TAKE1, + "En-/disable server signature (on|off|email)" }, +{ "ServerRoot", set_server_root, NULL, RSRC_CONF, TAKE1, + "Common directory of server-related files (logs, confs, etc.)" }, +{ "ErrorLog", set_server_string_slot, + (void *)XtOffsetOf (server_rec, error_fname), RSRC_CONF, TAKE1, + "The filename of the error log" }, +{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1, + "A file for logging the server process ID"}, +{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1, + "A file for Apache to maintain runtime process management information"}, +{ "LockFile", set_lockfile, NULL, RSRC_CONF, TAKE1, + "The lockfile used when Apache needs to lock the accept() call"}, +{ "AccessConfig", set_server_string_slot, + (void *)XtOffsetOf (server_rec, access_confname), RSRC_CONF, TAKE1, + "The filename of the access config file" }, +{ "ResourceConfig", set_server_string_slot, + (void *)XtOffsetOf (server_rec, srm_confname), RSRC_CONF, TAKE1, + "The filename of the resource config file" }, +{ "ServerAlias", set_server_alias, NULL, RSRC_CONF, RAW_ARGS, + "A name or names alternately used to access the server" }, +{ "ServerPath", set_serverpath, NULL, RSRC_CONF, TAKE1, + "The pathname the server can be reached at" }, +{ "Timeout", set_timeout, NULL, RSRC_CONF, TAKE1, "Timeout duration (sec)" }, +{ "KeepAliveTimeout", set_keep_alive_timeout, NULL, RSRC_CONF, TAKE1, + "Keep-Alive timeout duration (sec)"}, +{ "MaxKeepAliveRequests", set_keep_alive_max, NULL, RSRC_CONF, TAKE1, + "Maximum number of Keep-Alive requests per connection, or 0 for infinite" }, +{ "KeepAlive", set_keep_alive, NULL, RSRC_CONF, TAKE1, + "Whether persistent connections should be On or Off" }, +{ "IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, FLAG, + "Enable identd (RFC 1413) user lookups - SLOW" }, +{ "ContentDigest", set_content_md5, NULL, OR_OPTIONS, + FLAG, "whether or not to send a Content-MD5 header with each request" }, +{ "UseCanonicalName", set_use_canonical_name, NULL, + RSRC_CONF, TAKE1, + "How to work out the ServerName : Port when constructing URLs" }, +{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, + "Number of child processes launched at server startup" }, +{ "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1, + "Minimum number of idle children, to handle request spikes" }, +{ "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, + "Maximum number of idle children" }, +{ "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, + "Deprecated equivalent to MaxSpareServers" }, +{ "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1, + "Deprecated equivalent to MaxClients" }, +{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1, + "Maximum number of children alive at the same time" }, +{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1, + "Maximum number of requests a particular child serves before dying." }, +{ "RLimitCPU", +#ifdef RLIMIT_CPU + set_limit_cpu, (void*)XtOffsetOf(core_dir_config, limit_cpu), +#else + no_set_limit, NULL, +#endif + OR_ALL, TAKE12, "Soft/hard limits for max CPU usage in seconds" }, +{ "RLimitMEM", +#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined (RLIMIT_AS) + set_limit_mem, (void*)XtOffsetOf(core_dir_config, limit_mem), +#else + no_set_limit, NULL, +#endif + OR_ALL, TAKE12, "Soft/hard limits for max memory usage per process" }, +{ "RLimitNPROC", +#ifdef RLIMIT_NPROC + set_limit_nproc, (void*)XtOffsetOf(core_dir_config, limit_nproc), +#else + no_set_limit, NULL, +#endif + OR_ALL, TAKE12, "soft/hard limits for max number of processes per uid" }, +{ "BindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1, + "'*', a numeric IP address, or the name of a host with a unique IP address"}, +{ "Listen", set_listener, NULL, RSRC_CONF, TAKE1, + "A port number or a numeric IP address and a port number"}, +{ "SendBufferSize", set_send_buffer_size, NULL, RSRC_CONF, TAKE1, + "Send buffer size in bytes"}, +{ "AddModule", add_module_command, NULL, RSRC_CONF, ITERATE, + "The name of a module" }, +{ "ClearModuleList", clear_module_list_command, NULL, RSRC_CONF, NO_ARGS, + NULL }, +{ "ThreadsPerChild", set_threads, NULL, RSRC_CONF, TAKE1, + "Number of threads a child creates" }, +{ "ExcessRequestsPerChild", set_excess_requests, NULL, RSRC_CONF, TAKE1, + "Maximum number of requests a particular child serves after it is ready " + "to die." }, +{ "ListenBacklog", set_listenbacklog, NULL, RSRC_CONF, TAKE1, + "Maximum length of the queue of pending connections, as used by listen(2)" }, +{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1, + "The location of the directory Apache changes to before dumping core" }, +{ "Include", include_config, NULL, (RSRC_CONF | ACCESS_CONF), TAKE1, + "Name of the config file to be included" }, +{ "LogLevel", set_loglevel, NULL, RSRC_CONF, TAKE1, + "Level of verbosity in error logging" }, +{ "NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF, TAKE1, + "A numeric IP address:port, or the name of a host" }, +#ifdef _OSD_POSIX +{ "BS2000Account", set_bs2000_account, NULL, RSRC_CONF, TAKE1, + "Name of server User's bs2000 logon account name" }, +#endif +#ifdef WIN32 +{ "ScriptInterpreterSource", set_interpreter_source, NULL, OR_FILEINFO, TAKE1, + "Where to find interpreter to run Win32 scripts (Registry or script shebang line)" }, +#endif +{ "ServerTokens", set_serv_tokens, NULL, RSRC_CONF, TAKE1, + "Determine tokens displayed in the Server: header - Min(imal), OS or Full" }, +{ "LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF, TAKE1, + "Limit on maximum size of an HTTP request line"}, +{ "LimitRequestFieldsize", set_limit_req_fieldsize, NULL, RSRC_CONF, TAKE1, + "Limit on maximum size of an HTTP request header field"}, +{ "LimitRequestFields", set_limit_req_fields, NULL, RSRC_CONF, TAKE1, + "Limit (0 = unlimited) on max number of header fields in a request message"}, +{ "LimitRequestBody", set_limit_req_body, + (void*)XtOffsetOf(core_dir_config, limit_req_body), + OR_ALL, TAKE1, + "Limit (in bytes) on maximum size of request message body" }, +{ NULL } +}; + +/***************************************************************** + * + * Core handlers for various phases of server operation... + */ + +static int core_translate(request_rec *r) +{ + void *sconf = r->server->module_config; + core_server_config *conf = ap_get_module_config(sconf, &core_module); + + if (r->proxyreq) { + return HTTP_FORBIDDEN; + } + if ((r->uri[0] != '/') && strcmp(r->uri, "*")) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid URI in request %s", r->the_request); + return BAD_REQUEST; + } + + if (r->server->path + && !strncmp(r->uri, r->server->path, r->server->pathlen) + && (r->server->path[r->server->pathlen - 1] == '/' + || r->uri[r->server->pathlen] == '/' + || r->uri[r->server->pathlen] == '\0')) { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, + (r->uri + r->server->pathlen), NULL); + } + else { + /* + * Make sure that we do not mess up the translation by adding two + * /'s in a row. This happens under windows when the document + * root ends with a / + */ + if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] == '/') + && (*(r->uri) == '/')) { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri+1, + NULL); + } + else { + r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri, + NULL); + } + } + + return OK; +} + +static int do_nothing(request_rec *r) { return OK; } + +#ifdef USE_MMAP_FILES +struct mmap_rec { + void *mm; + size_t length; +}; + +static void mmap_cleanup(void *mmv) +{ + struct mmap_rec *mmd = mmv; + + if (munmap(mmd->mm, mmd->length) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, NULL, + "Failed to munmap memory of length %ld at 0x%lx", + (long) mmd->length, (long) mmd->mm); + } +} +#endif + +/* + * Default handler for MIME types without other handlers. Only GET + * and OPTIONS at this point... anyone who wants to write a generic + * handler for PUT or POST is free to do so, but it seems unwise to provide + * any defaults yet... So, for now, we assume that this will always be + * the last handler called and return 405 or 501. + */ + +static int default_handler(request_rec *r) +{ + core_dir_config *d = + (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); + int rangestatus, errstatus; + FILE *f; +#ifdef USE_MMAP_FILES + caddr_t mm; +#endif +#ifdef CHARSET_EBCDIC + int convert_flag; +#endif + + /* This handler has no use for a request body (yet), but we still + * need to read and discard it if the client sent one. + */ + if ((errstatus = ap_discard_request_body(r)) != OK) { + return errstatus; + } + + r->allowed |= (1 << M_GET) | (1 << M_OPTIONS); + + if (r->method_number == M_INVALID) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid method in request %s", r->the_request); + return NOT_IMPLEMENTED; + } + if (r->method_number == M_OPTIONS) { + return ap_send_http_options(r); + } + if (r->method_number == M_PUT) { + return METHOD_NOT_ALLOWED; + } + + if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) { + char *emsg; + + emsg = "File does not exist: "; + if (r->path_info == NULL) { + emsg = ap_pstrcat(r->pool, emsg, r->filename, NULL); + } + else { + emsg = ap_pstrcat(r->pool, emsg, r->filename, r->path_info, NULL); + } + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, "%s", emsg); + return HTTP_NOT_FOUND; + } + if (r->method_number != M_GET) { + return METHOD_NOT_ALLOWED; + } + +#if defined(OS2) || defined(WIN32) + /* Need binary mode for OS/2 */ + f = ap_pfopen(r->pool, r->filename, "rb"); +#else + f = ap_pfopen(r->pool, r->filename, "r"); +#endif + + if (f == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "file permissions deny server access: %s", r->filename); + return FORBIDDEN; + } + + ap_update_mtime(r, r->finfo.st_mtime); + ap_set_last_modified(r); + ap_set_etag(r); + ap_table_setn(r->headers_out, "Accept-Ranges", "bytes"); + if (((errstatus = ap_meets_conditions(r)) != OK) + || (errstatus = ap_set_content_length(r, r->finfo.st_size))) { + return errstatus; + } + +#ifdef USE_MMAP_FILES + ap_block_alarms(); + if ((r->finfo.st_size >= MMAP_THRESHOLD) + && (r->finfo.st_size < MMAP_LIMIT) + && (!r->header_only || (d->content_md5 & 1))) { + /* we need to protect ourselves in case we die while we've got the + * file mmapped */ + mm = mmap(NULL, r->finfo.st_size, PROT_READ, MAP_PRIVATE, + fileno(f), 0); + if (mm == (caddr_t)-1) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, r, + "default_handler: mmap failed: %s", r->filename); + } + } + else { + mm = (caddr_t)-1; + } + + if (mm == (caddr_t)-1) { + ap_unblock_alarms(); +#endif + +#ifdef CHARSET_EBCDIC + /* To make serving of "raw ASCII text" files easy (they serve faster + * since they don't have to be converted from EBCDIC), a new + * "magic" type prefix was invented: text/x-ascii-{plain,html,...} + * If we detect one of these content types here, we simply correct + * the type to the real text/{plain,html,...} type. Otherwise, we + * set a flag that translation is required later on. + */ + convert_flag = ap_checkconv(r); + if (d->content_md5 & 1) { + ap_table_setn(r->headers_out, "Content-MD5", + ap_md5digest(r->pool, f, convert_flag)); + } +#else + if (d->content_md5 & 1) { + ap_table_setn(r->headers_out, "Content-MD5", + ap_md5digest(r->pool, f)); + } +#endif /* CHARSET_EBCDIC */ + + rangestatus = ap_set_byterange(r); + + ap_send_http_header(r); + + if (!r->header_only) { + if (!rangestatus) { + ap_send_fd(f, r); + } + else { + long offset, length; + while (ap_each_byterange(r, &offset, &length)) { + /* + * Non zero returns are more portable than checking + * for a return of -1. + */ + if (fseek(f, offset, SEEK_SET)) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "Failed to fseek for byterange (%ld, %ld)", + offset, length); + } + else { + ap_send_fd_length(f, r, length); + } + } + } + } + +#ifdef USE_MMAP_FILES + } + else { + struct mmap_rec *mmd; + + mmd = ap_palloc(r->pool, sizeof(*mmd)); + mmd->mm = mm; + mmd->length = r->finfo.st_size; + ap_register_cleanup(r->pool, (void *)mmd, mmap_cleanup, mmap_cleanup); + ap_unblock_alarms(); + + if (d->content_md5 & 1) { + AP_MD5_CTX context; + + ap_MD5Init(&context); + ap_MD5Update(&context, (void *)mm, (unsigned int)r->finfo.st_size); + ap_table_setn(r->headers_out, "Content-MD5", + ap_md5contextTo64(r->pool, &context)); + } + + rangestatus = ap_set_byterange(r); + ap_send_http_header(r); + + if (!r->header_only) { + if (!rangestatus) { + ap_send_mmap(mm, r, 0, r->finfo.st_size); + } + else { + long offset, length; + while (ap_each_byterange(r, &offset, &length)) { + ap_send_mmap(mm, r, offset, length); + } + } + } + } +#endif + + ap_pfclose(r->pool, f); + return OK; +} + +static const handler_rec core_handlers[] = { +{ "*/*", default_handler }, +{ "default-handler", default_handler }, +{ NULL, NULL } +}; + +API_VAR_EXPORT module core_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_core_dir_config, /* create per-directory config structure */ + merge_core_dir_configs, /* merge per-directory config structures */ + create_core_server_config, /* create per-server config structure */ + merge_core_server_configs, /* merge per-server config structures */ + core_cmds, /* command table */ + core_handlers, /* handlers */ + core_translate, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + do_nothing, /* check access */ + do_nothing, /* type_checker */ + NULL, /* pre-run fixups */ + NULL, /* logger */ + NULL, /* header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* post_read_request */ +}; diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c new file mode 100644 index 0000000000..31779312c6 --- /dev/null +++ b/modules/http/http_protocol.c @@ -0,0 +1,2763 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_protocol.c --- routines which directly communicate with the client. + * + * Code originally by Rob McCool; much redone by Robert S. Thau + * and the Apache Group. + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_request.h" +#include "http_vhost.h" +#include "http_log.h" /* For errors detected in basic auth common + * support code... */ +#include "util_date.h" /* For parseHTTPdate and BAD_DATE */ +#include +#include "http_conf_globals.h" + +#define SET_BYTES_SENT(r) \ + do { if (r->sent_bodyct) \ + ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \ + } while (0) + + +static int parse_byterange(char *range, long clength, long *start, long *end) +{ + char *dash = strchr(range, '-'); + + if (!dash) + return 0; + + if ((dash == range)) { + /* In the form "-5" */ + *start = clength - atol(dash + 1); + *end = clength - 1; + } + else { + *dash = '\0'; + dash++; + *start = atol(range); + if (*dash) + *end = atol(dash); + else /* "5-" */ + *end = clength - 1; + } + + if (*start < 0) + *start = 0; + + if (*end >= clength) + *end = clength - 1; + + if (*start > *end) + return 0; + + return (*start > 0 || *end < clength - 1); +} + +static int internal_byterange(int, long *, request_rec *, const char **, long *, + long *); + +API_EXPORT(int) ap_set_byterange(request_rec *r) +{ + const char *range, *if_range, *match; + long range_start, range_end; + + if (!r->clength || r->assbackwards) + return 0; + + /* Check for Range request-header (HTTP/1.1) or Request-Range for + * backwards-compatibility with second-draft Luotonen/Franks + * byte-ranges (e.g. Netscape Navigator 2-3). + * + * We support this form, with Request-Range, and (farther down) we + * send multipart/x-byteranges instead of multipart/byteranges for + * Request-Range based requests to work around a bug in Netscape + * Navigator 2-3 and MSIE 3. + */ + + if (!(range = ap_table_get(r->headers_in, "Range"))) + range = ap_table_get(r->headers_in, "Request-Range"); + + if (!range || strncasecmp(range, "bytes=", 6)) { + return 0; + } + + /* Check the If-Range header for Etag or Date. + * Note that this check will return false (as required) if either + * of the two etags are weak. + */ + if ((if_range = ap_table_get(r->headers_in, "If-Range"))) { + if (if_range[0] == '"') { + if (!(match = ap_table_get(r->headers_out, "Etag")) || + (strcmp(if_range, match) != 0)) + return 0; + } + else if (!(match = ap_table_get(r->headers_out, "Last-Modified")) || + (strcmp(if_range, match) != 0)) + return 0; + } + + if (!strchr(range, ',')) { + /* A single range */ + if (!parse_byterange(ap_pstrdup(r->pool, range + 6), r->clength, + &range_start, &range_end)) + return 0; + + r->byterange = 1; + + ap_table_setn(r->headers_out, "Content-Range", + ap_psprintf(r->pool, "bytes %ld-%ld/%ld", + range_start, range_end, r->clength)); + ap_table_setn(r->headers_out, "Content-Length", + ap_psprintf(r->pool, "%ld", range_end - range_start + 1)); + } + else { + /* a multiple range */ + const char *r_range = ap_pstrdup(r->pool, range + 6); + long tlength = 0; + + r->byterange = 2; + r->boundary = ap_psprintf(r->pool, "%lx%lx", + r->request_time, (long) getpid()); + while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL)); + ap_table_setn(r->headers_out, "Content-Length", + ap_psprintf(r->pool, "%ld", tlength)); + } + + r->status = PARTIAL_CONTENT; + r->range = range + 6; + + return 1; +} + +API_EXPORT(int) ap_each_byterange(request_rec *r, long *offset, long *length) +{ + return internal_byterange(1, NULL, r, &r->range, offset, length); +} + +/* If this function is called with realreq=1, it will spit out + * the correct headers for a byterange chunk, and set offset and + * length to the positions they should be. + * + * If it is called with realreq=0, it will add to tlength the length + * it *would* have used with realreq=1. + * + * Either case will return 1 if it should be called again, and 0 + * when done. + */ +static int internal_byterange(int realreq, long *tlength, request_rec *r, + const char **r_range, long *offset, long *length) +{ + long range_start, range_end; + char *range; + + if (!**r_range) { + if (r->byterange > 1) { + if (realreq) + ap_rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL); + else + *tlength += 4 + strlen(r->boundary) + 4; + } + return 0; + } + + range = ap_getword(r->pool, r_range, ','); + if (!parse_byterange(range, r->clength, &range_start, &range_end)) + /* Skip this one */ + return internal_byterange(realreq, tlength, r, r_range, offset, + length); + + if (r->byterange > 1) { + const char *ct = r->content_type ? r->content_type : ap_default_type(r); + char ts[MAX_STRING_LEN]; + + ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end, + r->clength); + if (realreq) + ap_rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ", + ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012", + NULL); + else + *tlength += 4 + strlen(r->boundary) + 16 + strlen(ct) + 23 + + strlen(ts) + 4; + } + + if (realreq) { + *offset = range_start; + *length = range_end - range_start + 1; + } + else { + *tlength += range_end - range_start + 1; + } + return 1; +} + +API_EXPORT(int) ap_set_content_length(request_rec *r, long clength) +{ + r->clength = clength; + ap_table_setn(r->headers_out, "Content-Length", ap_psprintf(r->pool, "%ld", clength)); + return 0; +} + +API_EXPORT(int) ap_set_keepalive(request_rec *r) +{ + int ka_sent = 0; + int wimpy = ap_find_token(r->pool, + ap_table_get(r->headers_out, "Connection"), "close"); + const char *conn = ap_table_get(r->headers_in, "Connection"); + + /* The following convoluted conditional determines whether or not + * the current connection should remain persistent after this response + * (a.k.a. HTTP Keep-Alive) and whether or not the output message + * body should use the HTTP/1.1 chunked transfer-coding. In English, + * + * IF we have not marked this connection as errored; + * and the response body has a defined length due to the status code + * being 304 or 204, the request method being HEAD, already + * having defined Content-Length or Transfer-Encoding: chunked, or + * the request version being HTTP/1.1 and thus capable of being set + * as chunked [we know the (r->chunked = 1) side-effect is ugly]; + * and the server configuration enables keep-alive; + * and the server configuration has a reasonable inter-request timeout; + * and there is no maximum # requests or the max hasn't been reached; + * and the response status does not require a close; + * and the response generator has not already indicated close; + * and the client did not request non-persistence (Connection: close); + * and we haven't been configured to ignore the buggy twit + * or they're a buggy twit coming through a HTTP/1.1 proxy + * and the client is requesting an HTTP/1.0-style keep-alive + * or the client claims to be HTTP/1.1 compliant (perhaps a proxy); + * THEN we can be persistent, which requires more headers be output. + * + * Note that the condition evaluation order is extremely important. + */ + if ((r->connection->keepalive != -1) && + ((r->status == HTTP_NOT_MODIFIED) || + (r->status == HTTP_NO_CONTENT) || + r->header_only || + ap_table_get(r->headers_out, "Content-Length") || + ap_find_last_token(r->pool, + ap_table_get(r->headers_out, "Transfer-Encoding"), + "chunked") || + ((r->proto_num >= HTTP_VERSION(1,1)) && + (r->chunked = 1))) && /* THIS CODE IS CORRECT, see comment above. */ + r->server->keep_alive && + (r->server->keep_alive_timeout > 0) && + ((r->server->keep_alive_max == 0) || + (r->server->keep_alive_max > r->connection->keepalives)) && + !ap_status_drops_connection(r->status) && + !wimpy && + !ap_find_token(r->pool, conn, "close") && + (!ap_table_get(r->subprocess_env, "nokeepalive") || + ap_table_get(r->headers_in, "Via")) && + ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) || + (r->proto_num >= HTTP_VERSION(1,1))) + ) { + int left = r->server->keep_alive_max - r->connection->keepalives; + + r->connection->keepalive = 1; + r->connection->keepalives++; + + /* If they sent a Keep-Alive token, send one back */ + if (ka_sent) { + if (r->server->keep_alive_max) + ap_table_setn(r->headers_out, "Keep-Alive", + ap_psprintf(r->pool, "timeout=%d, max=%d", + r->server->keep_alive_timeout, left)); + else + ap_table_setn(r->headers_out, "Keep-Alive", + ap_psprintf(r->pool, "timeout=%d", + r->server->keep_alive_timeout)); + ap_table_mergen(r->headers_out, "Connection", "Keep-Alive"); + } + + return 1; + } + + /* Otherwise, we need to indicate that we will be closing this + * connection immediately after the current response. + * + * We only really need to send "close" to HTTP/1.1 clients, but we + * always send it anyway, because a broken proxy may identify itself + * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag + * to a HTTP/1.1 client. Better safe than sorry. + */ + if (!wimpy) + ap_table_mergen(r->headers_out, "Connection", "close"); + + r->connection->keepalive = 0; + + return 0; +} + +/* + * Return the latest rational time from a request/mtime (modification time) + * pair. We return the mtime unless it's in the future, in which case we + * return the current time. We use the request time as a reference in order + * to limit the number of calls to time(). We don't check for futurosity + * unless the mtime is at least as new as the reference. + */ +API_EXPORT(time_t) ap_rationalize_mtime(request_rec *r, time_t mtime) +{ + time_t now; + + /* For all static responses, it's almost certain that the file was + * last modified before the beginning of the request. So there's + * no reason to call time(NULL) again. But if the response has been + * created on demand, then it might be newer than the time the request + * started. In this event we really have to call time(NULL) again + * so that we can give the clients the most accurate Last-Modified. If we + * were given a time in the future, we return the current time - the + * Last-Modified can't be in the future. + */ + now = (mtime < r->request_time) ? r->request_time : time(NULL); + return (mtime > now) ? now : mtime; +} + +API_EXPORT(int) ap_meets_conditions(request_rec *r) +{ + const char *etag = ap_table_get(r->headers_out, "ETag"); + const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch; + time_t mtime; + + /* Check for conditional requests --- note that we only want to do + * this if we are successful so far and we are not processing a + * subrequest or an ErrorDocument. + * + * The order of the checks is important, since ETag checks are supposed + * to be more accurate than checks relative to the modification time. + * However, not all documents are guaranteed to *have* ETags, and some + * might have Last-Modified values w/o ETags, so this gets a little + * complicated. + */ + + if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) { + return OK; + } + + mtime = (r->mtime != 0) ? r->mtime : time(NULL); + + /* If an If-Match request-header field was given + * AND the field value is not "*" (meaning match anything) + * AND if our strong ETag does not match any entity tag in that field, + * respond with a status of 412 (Precondition Failed). + */ + if ((if_match = ap_table_get(r->headers_in, "If-Match")) != NULL) { + if (if_match[0] != '*' && + (etag == NULL || etag[0] == 'W' || + !ap_find_list_item(r->pool, if_match, etag))) { + return HTTP_PRECONDITION_FAILED; + } + } + else { + /* Else if a valid If-Unmodified-Since request-header field was given + * AND the requested resource has been modified since the time + * specified in this field, then the server MUST + * respond with a status of 412 (Precondition Failed). + */ + if_unmodified = ap_table_get(r->headers_in, "If-Unmodified-Since"); + if (if_unmodified != NULL) { + time_t ius = ap_parseHTTPdate(if_unmodified); + + if ((ius != BAD_DATE) && (mtime > ius)) { + return HTTP_PRECONDITION_FAILED; + } + } + } + + /* If an If-None-Match request-header field was given + * AND the field value is "*" (meaning match anything) + * OR our ETag matches any of the entity tags in that field, fail. + * + * If the request method was GET or HEAD, failure means the server + * SHOULD respond with a 304 (Not Modified) response. + * For all other request methods, failure means the server MUST + * respond with a status of 412 (Precondition Failed). + * + * GET or HEAD allow weak etag comparison, all other methods require + * strong comparison. We can only use weak if it's not a range request. + */ + if_nonematch = ap_table_get(r->headers_in, "If-None-Match"); + if (if_nonematch != NULL) { + if (r->method_number == M_GET) { + if (if_nonematch[0] == '*') + return HTTP_NOT_MODIFIED; + if (etag != NULL) { + if (ap_table_get(r->headers_in, "Range")) { + if (etag[0] != 'W' && + ap_find_list_item(r->pool, if_nonematch, etag)) { + return HTTP_NOT_MODIFIED; + } + } + else if (strstr(if_nonematch, etag)) { + return HTTP_NOT_MODIFIED; + } + } + } + else if (if_nonematch[0] == '*' || + (etag != NULL && + ap_find_list_item(r->pool, if_nonematch, etag))) { + return HTTP_PRECONDITION_FAILED; + } + } + /* Else if a valid If-Modified-Since request-header field was given + * AND it is a GET or HEAD request + * AND the requested resource has not been modified since the time + * specified in this field, then the server MUST + * respond with a status of 304 (Not Modified). + * A date later than the server's current request time is invalid. + */ + else if ((r->method_number == M_GET) + && ((if_modified_since = + ap_table_get(r->headers_in, "If-Modified-Since")) != NULL)) { + time_t ims = ap_parseHTTPdate(if_modified_since); + + if ((ims >= mtime) && (ims <= r->request_time)) { + return HTTP_NOT_MODIFIED; + } + } + return OK; +} + +/* + * Construct an entity tag (ETag) from resource information. If it's a real + * file, build in some of the file characteristics. If the modification time + * is newer than (request-time minus 1 second), mark the ETag as weak - it + * could be modified again in as short an interval. We rationalize the + * modification time we're given to keep it from being in the future. + */ +API_EXPORT(char *) ap_make_etag(request_rec *r, int force_weak) +{ + char *etag; + char *weak; + + /* + * Make an ETag header out of various pieces of information. We use + * the last-modified date and, if we have a real file, the + * length and inode number - note that this doesn't have to match + * the content-length (i.e. includes), it just has to be unique + * for the file. + * + * If the request was made within a second of the last-modified date, + * we send a weak tag instead of a strong one, since it could + * be modified again later in the second, and the validation + * would be incorrect. + */ + + weak = ((r->request_time - r->mtime > 1) && !force_weak) ? "" : "W/"; + + if (r->finfo.st_mode != 0) { + etag = ap_psprintf(r->pool, + "%s\"%lx-%lx-%lx\"", weak, + (unsigned long) r->finfo.st_ino, + (unsigned long) r->finfo.st_size, + (unsigned long) r->mtime); + } + else { + etag = ap_psprintf(r->pool, "%s\"%lx\"", weak, + (unsigned long) r->mtime); + } + + return etag; +} + +API_EXPORT(void) ap_set_etag(request_rec *r) +{ + char *etag; + char *variant_etag, *vlv; + int vlv_weak; + + if (!r->vlist_validator) { + etag = ap_make_etag(r, 0); + } + else { + /* If we have a variant list validator (vlv) due to the + * response being negotiated, then we create a structured + * entity tag which merges the variant etag with the variant + * list validator (vlv). This merging makes revalidation + * somewhat safer, ensures that caches which can deal with + * Vary will (eventually) be updated if the set of variants is + * changed, and is also a protocol requirement for transparent + * content negotiation. + */ + + /* if the variant list validator is weak, we make the whole + * structured etag weak. If we would not, then clients could + * have problems merging range responses if we have different + * variants with the same non-globally-unique strong etag. + */ + + vlv = r->vlist_validator; + vlv_weak = (vlv[0] == 'W'); + + variant_etag = ap_make_etag(r, vlv_weak); + + /* merge variant_etag and vlv into a structured etag */ + + variant_etag[strlen(variant_etag) - 1] = '\0'; + if (vlv_weak) + vlv += 3; + else + vlv++; + etag = ap_pstrcat(r->pool, variant_etag, ";", vlv, NULL); + } + + ap_table_setn(r->headers_out, "ETag", etag); +} + +/* + * This function sets the Last-Modified output header field to the value + * of the mtime field in the request structure - rationalized to keep it from + * being in the future. + */ +API_EXPORT(void) ap_set_last_modified(request_rec *r) +{ + time_t mod_time = ap_rationalize_mtime(r, r->mtime); + + ap_table_setn(r->headers_out, "Last-Modified", + ap_gm_timestr_822(r->pool, mod_time)); +} + +/* Get the method number associated with the given string, assumed to + * contain an HTTP method. Returns M_INVALID if not recognized. + * + * This is the first step toward placing method names in a configurable + * list. Hopefully it (and other routines) can eventually be moved to + * something like a mod_http_methods.c, complete with config stuff. + */ +API_EXPORT(int) ap_method_number_of(const char *method) +{ + switch (*method) { + case 'H': + if (strcmp(method, "HEAD") == 0) + return M_GET; /* see header_only in request_rec */ + break; + case 'G': + if (strcmp(method, "GET") == 0) + return M_GET; + break; + case 'P': + if (strcmp(method, "POST") == 0) + return M_POST; + if (strcmp(method, "PUT") == 0) + return M_PUT; + if (strcmp(method, "PATCH") == 0) + return M_PATCH; + if (strcmp(method, "PROPFIND") == 0) + return M_PROPFIND; + if (strcmp(method, "PROPPATCH") == 0) + return M_PROPPATCH; + break; + case 'D': + if (strcmp(method, "DELETE") == 0) + return M_DELETE; + break; + case 'C': + if (strcmp(method, "CONNECT") == 0) + return M_CONNECT; + if (strcmp(method, "COPY") == 0) + return M_COPY; + break; + case 'M': + if (strcmp(method, "MKCOL") == 0) + return M_MKCOL; + if (strcmp(method, "MOVE") == 0) + return M_MOVE; + break; + case 'O': + if (strcmp(method, "OPTIONS") == 0) + return M_OPTIONS; + break; + case 'T': + if (strcmp(method, "TRACE") == 0) + return M_TRACE; + break; + case 'L': + if (strcmp(method, "LOCK") == 0) + return M_LOCK; + break; + case 'U': + if (strcmp(method, "UNLOCK") == 0) + return M_UNLOCK; + break; + } + return M_INVALID; +} + +/* Get a line of protocol input, including any continuation lines + * caused by MIME folding (or broken clients) if fold != 0, and place it + * in the buffer s, of size n bytes, without the ending newline. + * + * Returns -1 on error, or the length of s. + * + * Note: Because bgets uses 1 char for newline and 1 char for NUL, + * the most we can get is (n - 2) actual characters if it + * was ended by a newline, or (n - 1) characters if the line + * length exceeded (n - 1). So, if the result == (n - 1), + * then the actual input line exceeded the buffer length, + * and it would be a good idea for the caller to puke 400 or 414. + */ +static int getline(char *s, int n, BUFF *in, int fold) +{ + char *pos, next; + int retval; + int total = 0; + + pos = s; + + do { + retval = ap_bgets(pos, n, in); /* retval == -1 if error, 0 if EOF */ + + if (retval <= 0) + return ((retval < 0) && (total == 0)) ? -1 : total; + + /* retval is the number of characters read, not including NUL */ + + n -= retval; /* Keep track of how much of s is full */ + pos += (retval - 1); /* and where s ends */ + total += retval; /* and how long s has become */ + + if (*pos == '\n') { /* Did we get a full line of input? */ + /* + * Trim any extra trailing spaces or tabs except for the first + * space or tab at the beginning of a blank string. This makes + * it much easier to check field values for exact matches, and + * saves memory as well. Terminate string at end of line. + */ + while (pos > (s + 1) && (*(pos - 1) == ' ' || *(pos - 1) == '\t')) { + --pos; /* trim extra trailing spaces or tabs */ + --total; /* but not one at the beginning of line */ + ++n; + } + *pos = '\0'; + --total; + ++n; + } + else + return total; /* if not, input line exceeded buffer size */ + + /* Continue appending if line folding is desired and + * the last line was not empty and we have room in the buffer and + * the next line begins with a continuation character. + */ + } while (fold && (retval != 1) && (n > 1) + && (ap_blookc(&next, in) == 1) + && ((next == ' ') || (next == '\t'))); + + return total; +} + +/* parse_uri: break apart the uri + * Side Effects: + * - sets r->args to rest after '?' (or NULL if no '?') + * - sets r->uri to request uri (without r->args part) + * - sets r->hostname (if not set already) from request (scheme://host:port) + */ +CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri) +{ + int status = HTTP_OK; + + r->unparsed_uri = ap_pstrdup(r->pool, uri); + + if (r->method_number == M_CONNECT) { + status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri); + } else { + /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */ + status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri); + } + + if (ap_is_HTTP_SUCCESS(status)) { + /* if it has a scheme we may need to do absoluteURI vhost stuff */ + if (r->parsed_uri.scheme + && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) { + r->hostname = r->parsed_uri.hostname; + } else if (r->method_number == M_CONNECT) { + r->hostname = r->parsed_uri.hostname; + } + r->args = r->parsed_uri.query; + r->uri = r->parsed_uri.path ? r->parsed_uri.path + : ap_pstrdup(r->pool, "/"); +#if defined(OS2) || defined(WIN32) + /* Handle path translations for OS/2 and plug security hole. + * This will prevent "http://www.wherever.com/..\..\/" from + * returning a directory for the root drive. + */ + { + char *x; + + for (x = r->uri; (x = strchr(x, '\\')) != NULL; ) + *x = '/'; + } +#endif /* OS2 || WIN32 */ + } + else { + r->args = NULL; + r->hostname = NULL; + r->status = status; /* set error status */ + r->uri = ap_pstrdup(r->pool, uri); + } +} + +static int read_request_line(request_rec *r) +{ + char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */ + const char *ll = l; + const char *uri; + conn_rec *conn = r->connection; + int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */ + int len; + + /* Read past empty lines until we get a real request line, + * a read error, the connection closes (EOF), or we timeout. + * + * We skip empty lines because browsers have to tack a CRLF on to the end + * of POSTs to support old CERN webservers. But note that we may not + * have flushed any previous response completely to the client yet. + * We delay the flush as long as possible so that we can improve + * performance for clients that are pipelining requests. If a request + * is pipelined then we won't block during the (implicit) read() below. + * If the requests aren't pipelined, then the client is still waiting + * for the final buffer flush from us, and we will block in the implicit + * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will + * have to block during a read. + */ + ap_bsetflag(conn->client, B_SAFEREAD, 1); + while ((len = getline(l, sizeof(l), conn->client, 0)) <= 0) { + if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) { + ap_bsetflag(conn->client, B_SAFEREAD, 0); + /* this is a hack to make sure that request time is set, + * it's not perfect, but it's better than nothing + */ + r->request_time = time(0); + return 0; + } + } + /* we've probably got something to do, ignore graceful restart requests */ +#ifdef SIGUSR1 + signal(SIGUSR1, SIG_IGN); +#endif + + ap_bsetflag(conn->client, B_SAFEREAD, 0); + + r->request_time = time(NULL); + r->the_request = ap_pstrdup(r->pool, l); + r->method = ap_getword_white(r->pool, &ll); + uri = ap_getword_white(r->pool, &ll); + + /* Provide quick information about the request method as soon as known */ + + r->method_number = ap_method_number_of(r->method); + if (r->method_number == M_GET && r->method[0] == 'H') { + r->header_only = 1; + } + + ap_parse_uri(r, uri); + + /* getline returns (size of max buffer - 1) if it fills up the + * buffer before finding the end-of-line. This is only going to + * happen if it exceeds the configured limit for a request-line. + */ + if (len > r->server->limit_req_line) { + r->status = HTTP_REQUEST_URI_TOO_LARGE; + r->proto_num = HTTP_VERSION(1,0); + r->protocol = ap_pstrdup(r->pool, "HTTP/1.0"); + return 0; + } + + r->assbackwards = (ll[0] == '\0'); + r->protocol = ap_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9"); + + if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor) + && minor < HTTP_VERSION(1,0)) /* don't allow HTTP/0.1000 */ + r->proto_num = HTTP_VERSION(major, minor); + else + r->proto_num = HTTP_VERSION(1,0); + + return 1; +} + +static void get_mime_headers(request_rec *r) +{ + char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */ + conn_rec *c = r->connection; + char *value; + char *copy; + int len; + unsigned int fields_read = 0; + table *tmp_headers; + + /* We'll use ap_overlap_tables later to merge these into r->headers_in. */ + tmp_headers = ap_make_table(r->pool, 50); + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), reach the server limit, or we timeout. + */ + while ((len = getline(field, sizeof(field), c->client, 1)) > 0) { + + if (r->server->limit_req_fields && + (++fields_read > r->server->limit_req_fields)) { + r->status = HTTP_BAD_REQUEST; + ap_table_setn(r->notes, "error-notes", + "The number of request header fields exceeds " + "this server's limit.

\n"); + return; + } + /* getline returns (size of max buffer - 1) if it fills up the + * buffer before finding the end-of-line. This is only going to + * happen if it exceeds the configured limit for a field size. + */ + if (len > r->server->limit_req_fieldsize) { + r->status = HTTP_BAD_REQUEST; + ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool, + "Size of a request header field exceeds server limit.

\n" + "

\n", field, "
\n", NULL)); + return; + } + copy = ap_palloc(r->pool, len + 1); + memcpy(copy, field, len + 1); + + if (!(value = strchr(copy, ':'))) { /* Find the colon separator */ + r->status = HTTP_BAD_REQUEST; /* or abort the bad request */ + ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool, + "Request header field is missing colon separator.

\n" + "

\n", copy, "
\n", NULL)); + return; + } + + *value = '\0'; + ++value; + while (*value == ' ' || *value == '\t') + ++value; /* Skip to start of value */ + + ap_table_addn(tmp_headers, copy, value); + } + + ap_overlap_tables(r->headers_in, tmp_headers, AP_OVERLAP_TABLES_MERGE); +} + +request_rec *ap_read_request(conn_rec *conn) +{ + request_rec *r; + pool *p; + const char *expect; + int access_status; + + p = ap_make_sub_pool(conn->pool); + r = ap_pcalloc(p, sizeof(request_rec)); + r->pool = p; + r->connection = conn; + conn->server = conn->base_server; + r->server = conn->server; + + conn->keptalive = conn->keepalive == 1; + conn->keepalive = 0; + + conn->user = NULL; + conn->ap_auth_type = NULL; + + r->headers_in = ap_make_table(r->pool, 50); + r->subprocess_env = ap_make_table(r->pool, 50); + r->headers_out = ap_make_table(r->pool, 12); + r->err_headers_out = ap_make_table(r->pool, 5); + r->notes = ap_make_table(r->pool, 5); + + r->request_config = ap_create_request_config(r->pool); + r->per_dir_config = r->server->lookup_defaults; + + r->sent_bodyct = 0; /* bytect isn't for body */ + + r->read_length = 0; + r->read_body = REQUEST_NO_BODY; + + r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */ + r->the_request = NULL; + +#ifdef CHARSET_EBCDIC + ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1); +#endif + + /* Get the request... */ + + ap_keepalive_timeout("read request line", r); + if (!read_request_line(r)) { + ap_kill_timeout(r); + if (r->status == HTTP_REQUEST_URI_TOO_LARGE) { + + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "request failed: URI too long"); + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + return NULL; + } + if (!r->assbackwards) { + ap_hard_timeout("read request headers", r); + get_mime_headers(r); + ap_kill_timeout(r); + if (r->status != HTTP_REQUEST_TIME_OUT) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "request failed: error reading the headers"); + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + } + else { + ap_kill_timeout(r); + + if (r->header_only) { + /* + * Client asked for headers only with HTTP/0.9, which doesn't send + * headers! Have to dink things just to make sure the error message + * comes through... + */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "client sent invalid HTTP/0.9 request: HEAD %s", + r->uri); + r->header_only = 0; + r->status = HTTP_BAD_REQUEST; + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + } + + r->status = HTTP_OK; /* Until further notice. */ + + /* update what we think the virtual host is based on the headers we've + * now read + */ + ap_update_vhost_from_headers(r); + + /* we may have switched to another server */ + r->per_dir_config = r->server->lookup_defaults; + + conn->keptalive = 0; /* We now have a request to play with */ + + if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1,1))) || + ((r->proto_num == HTTP_VERSION(1,1)) && + !ap_table_get(r->headers_in, "Host"))) { + /* + * Client sent us an HTTP/1.1 or later request without telling us the + * hostname, either with a full URL or a Host: header. We therefore + * need to (as per the 1.1 spec) send an error. As a special case, + * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain + * a Host: header, and the server MUST respond with 400 if it doesn't. + */ + r->status = HTTP_BAD_REQUEST; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "client sent HTTP/1.1 request without hostname " + "(see RFC2068 section 9, and 14.23): %s", r->uri); + ap_send_error_response(r, 0); + ap_log_transaction(r); + return r; + } + if (((expect = ap_table_get(r->headers_in, "Expect")) != NULL) && + (expect[0] != '\0')) { + /* + * The Expect header field was added to HTTP/1.1 after RFC 2068 + * as a means to signal when a 100 response is desired and, + * unfortunately, to signal a poor man's mandatory extension that + * the server must understand or return 417 Expectation Failed. + */ + if (strcasecmp(expect, "100-continue") == 0) { + r->expecting_100 = 1; + } + else { + r->status = HTTP_EXPECTATION_FAILED; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, + "client sent an unrecognized expectation value of " + "Expect: %s", expect); + ap_send_error_response(r, 0); + (void) ap_discard_request_body(r); + ap_log_transaction(r); + return r; + } + } + + if ((access_status = ap_run_post_read_request(r))) { + ap_die(access_status, r); + ap_log_transaction(r); + return NULL; + } + + return r; +} + +/* + * A couple of other functions which initialize some of the fields of + * a request structure, as appropriate for adjuncts of one kind or another + * to a request in progress. Best here, rather than elsewhere, since + * *someone* has to set the protocol-specific fields... + */ + +void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r) +{ + rnew->the_request = r->the_request; /* Keep original request-line */ + + rnew->assbackwards = 1; /* Don't send headers from this. */ + rnew->no_local_copy = 1; /* Don't try to send USE_LOCAL_COPY for a + * fragment. */ + rnew->method = "GET"; + rnew->method_number = M_GET; + rnew->protocol = "INCLUDED"; + + rnew->status = HTTP_OK; + + rnew->headers_in = r->headers_in; + rnew->subprocess_env = ap_copy_table(rnew->pool, r->subprocess_env); + rnew->headers_out = ap_make_table(rnew->pool, 5); + rnew->err_headers_out = ap_make_table(rnew->pool, 5); + rnew->notes = ap_make_table(rnew->pool, 5); + + rnew->expecting_100 = r->expecting_100; + rnew->read_length = r->read_length; + rnew->read_body = REQUEST_NO_BODY; + + rnew->main = (request_rec *) r; +} + +void ap_finalize_sub_req_protocol(request_rec *sub) +{ + SET_BYTES_SENT(sub->main); +} + +/* + * Support for the Basic authentication protocol, and a bit for Digest. + */ + +API_EXPORT(void) ap_note_auth_failure(request_rec *r) +{ + if (!strcasecmp(ap_auth_type(r), "Basic")) + ap_note_basic_auth_failure(r); + else if (!strcasecmp(ap_auth_type(r), "Digest")) + ap_note_digest_auth_failure(r); +} + +API_EXPORT(void) ap_note_basic_auth_failure(request_rec *r) +{ + if (strcasecmp(ap_auth_type(r), "Basic")) + ap_note_auth_failure(r); + else + ap_table_setn(r->err_headers_out, + r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate", + ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"", + NULL)); +} + +API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r) +{ + ap_table_setn(r->err_headers_out, + r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate", + ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"", + ap_auth_name(r), r->request_time)); +} + +API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw) +{ + const char *auth_line = ap_table_get(r->headers_in, + r->proxyreq ? "Proxy-Authorization" + : "Authorization"); + const char *t; + + if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic")) + return DECLINED; + + if (!ap_auth_name(r)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, + r, "need AuthName: %s", r->uri); + return SERVER_ERROR; + } + + if (!auth_line) { + ap_note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + + if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) { + /* Client tried to authenticate using wrong auth scheme */ + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "client used wrong authentication scheme: %s", r->uri); + ap_note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + + /* CHARSET_EBCDIC Issue's here ?!? Compare with 32/9 instead + * as we are operating on an octed stream ? + */ + while (*auth_line== ' ' || *auth_line== '\t') + auth_line++; + + t = ap_pbase64decode(r->pool, auth_line); + /* 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. + */ + r->connection->user = ap_getword_nulls (r->connection->pool, &t, ':'); + r->connection->ap_auth_type = "Basic"; + + *pw = t; + + return OK; +} + +/* New Apache routine to map status codes into array indicies + * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ... + * The number of status lines must equal the value of RESPONSE_CODES (httpd.h) + * and must be listed in order. + */ + +#ifdef UTS21 +/* The second const triggers an assembler bug on UTS 2.1. + * Another workaround is to move some code out of this file into another, + * but this is easier. Dave Dykstra, 3/31/99 + */ +static const char * status_lines[RESPONSE_CODES] = +#else +static const char * const status_lines[RESPONSE_CODES] = +#endif +{ + "100 Continue", + "101 Switching Protocols", + "102 Processing", +#define LEVEL_200 3 + "200 OK", + "201 Created", + "202 Accepted", + "203 Non-Authoritative Information", + "204 No Content", + "205 Reset Content", + "206 Partial Content", + "207 Multi-Status", +#define LEVEL_300 11 + "300 Multiple Choices", + "301 Moved Permanently", + "302 Found", + "303 See Other", + "304 Not Modified", + "305 Use Proxy", + "306 unused", + "307 Temporary Redirect", +#define LEVEL_400 19 + "400 Bad Request", + "401 Authorization Required", + "402 Payment Required", + "403 Forbidden", + "404 Not Found", + "405 Method Not Allowed", + "406 Not Acceptable", + "407 Proxy Authentication Required", + "408 Request Time-out", + "409 Conflict", + "410 Gone", + "411 Length Required", + "412 Precondition Failed", + "413 Request Entity Too Large", + "414 Request-URI Too Large", + "415 Unsupported Media Type", + "416 Requested Range Not Satisfiable", + "417 Expectation Failed", + "418 unused", + "419 unused", + "420 unused", + "421 unused", + "422 Unprocessable Entity", + "423 Locked", + "424 Failed Dependency", +#define LEVEL_500 44 + "500 Internal Server Error", + "501 Method Not Implemented", + "502 Bad Gateway", + "503 Service Temporarily Unavailable", + "504 Gateway Time-out", + "505 HTTP Version Not Supported", + "506 Variant Also Negotiates", + "507 Insufficient Storage", + "508 unused", + "509 unused", + "510 Not Extended" +}; + +/* The index is found by its offset from the x00 code of each level. + * Although this is fast, it will need to be replaced if some nutcase + * decides to define a high-numbered code before the lower numbers. + * If that sad event occurs, replace the code below with a linear search + * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1]; + */ +API_EXPORT(int) ap_index_of_response(int status) +{ + static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400, + LEVEL_500, RESPONSE_CODES}; + int i, pos; + + if (status < 100) /* Below 100 is illegal for HTTP status */ + return LEVEL_500; + + for (i = 0; i < 5; i++) { + status -= 100; + if (status < 100) { + pos = (status + shortcut[i]); + if (pos < shortcut[i + 1]) + return pos; + else + return LEVEL_500; /* status unknown (falls in gap) */ + } + } + return LEVEL_500; /* 600 or above is also illegal */ +} + +/* Send a single HTTP header field to the client. Note that this function + * is used in calls to table_do(), so their interfaces are co-dependent. + * In other words, don't change this one without checking table_do in alloc.c. + * It returns true unless there was a write error of some kind. + */ +API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r, + const char *fieldname, const char *fieldval) +{ + return (0 < ap_rvputs(r, fieldname, ": ", fieldval, "\015\012", NULL)); +} + +API_EXPORT(void) ap_basic_http_header(request_rec *r) +{ + char *protocol; +#ifdef CHARSET_EBCDIC + int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII); +#endif /*CHARSET_EBCDIC*/ + + if (r->assbackwards) + return; + + if (!r->status_line) + r->status_line = status_lines[ap_index_of_response(r->status)]; + + /* mod_proxy is only HTTP/1.0, so avoid sending HTTP/1.1 error response; + * kluge around broken browsers when indicated by force-response-1.0 + */ + if (r->proxyreq + || (r->proto_num == HTTP_VERSION(1,0) + && ap_table_get(r->subprocess_env, "force-response-1.0"))) { + + protocol = "HTTP/1.0"; + r->connection->keepalive = -1; + } + else + protocol = SERVER_PROTOCOL; + +#ifdef CHARSET_EBCDIC + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); +#endif /*CHARSET_EBCDIC*/ + + /* Output the HTTP/1.x Status-Line and the Date and Server fields */ + + ap_rvputs(r, protocol, " ", r->status_line, "\015\012", NULL); + + ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time)); + ap_send_header_field(r, "Server", ap_get_server_version()); + + ap_table_unset(r->headers_out, "Date"); /* Avoid bogosity */ + ap_table_unset(r->headers_out, "Server"); +#ifdef CHARSET_EBCDIC + if (!convert) + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert); +#endif /*CHARSET_EBCDIC*/ +} + +/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2 + * have a header parsing bug. If the terminating \r\n occur starting + * at offset 256, 257 or 258 of output then it will not properly parse + * the headers. Curiously it doesn't exhibit this problem at 512, 513. + * We are guessing that this is because their initial read of a new request + * uses a 256 byte buffer, and subsequent reads use a larger buffer. + * So the problem might exist at different offsets as well. + * + * This should also work on keepalive connections assuming they use the + * same small buffer for the first read of each new request. + * + * At any rate, we check the bytes written so far and, if we are about to + * tickle the bug, we instead insert a bogus padding header. Since the bug + * manifests as a broken image in Navigator, users blame the server. :( + * It is more expensive to check the User-Agent than it is to just add the + * bytes, so we haven't used the BrowserMatch feature here. + */ +static void terminate_header(BUFF *client) +{ + long int bs; + + ap_bgetopt(client, BO_BYTECT, &bs); + if (bs >= 255 && bs <= 257) + ap_bputs("X-Pad: avoid browser bug\015\012", client); + + ap_bputs("\015\012", client); /* Send the terminating empty line */ +} + +/* Build the Allow field-value from the request handler method mask. + * Note that we always allow TRACE, since it is handled below. + */ +static char *make_allow(request_rec *r) +{ + return 2 + ap_pstrcat(r->pool, + (r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "", + (r->allowed & (1 << M_POST)) ? ", POST" : "", + (r->allowed & (1 << M_PUT)) ? ", PUT" : "", + (r->allowed & (1 << M_DELETE)) ? ", DELETE" : "", + (r->allowed & (1 << M_CONNECT)) ? ", CONNECT" : "", + (r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "", + (r->allowed & (1 << M_PATCH)) ? ", PATCH" : "", + (r->allowed & (1 << M_PROPFIND)) ? ", PROPFIND" : "", + (r->allowed & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "", + (r->allowed & (1 << M_MKCOL)) ? ", MKCOL" : "", + (r->allowed & (1 << M_COPY)) ? ", COPY" : "", + (r->allowed & (1 << M_MOVE)) ? ", MOVE" : "", + (r->allowed & (1 << M_LOCK)) ? ", LOCK" : "", + (r->allowed & (1 << M_UNLOCK)) ? ", UNLOCK" : "", + ", TRACE", + NULL); +} + +API_EXPORT(int) ap_send_http_trace(request_rec *r) +{ + int rv; + + /* Get the original request */ + while (r->prev) + r = r->prev; + + if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) + return rv; + + ap_hard_timeout("send TRACE", r); + + r->content_type = "message/http"; + ap_send_http_header(r); + + /* Now we recreate the request, and echo it back */ + + ap_rvputs(r, r->the_request, "\015\012", NULL); + + ap_table_do((int (*) (void *, const char *, const char *)) + ap_send_header_field, (void *) r, r->headers_in, NULL); + ap_rputs("\015\012", r); + + ap_kill_timeout(r); + return OK; +} + +int ap_send_http_options(request_rec *r) +{ + const long int zero = 0L; + + if (r->assbackwards) + return DECLINED; + + ap_hard_timeout("send OPTIONS", r); + + ap_basic_http_header(r); + + ap_table_setn(r->headers_out, "Content-Length", "0"); + ap_table_setn(r->headers_out, "Allow", make_allow(r)); + ap_set_keepalive(r); + + ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field, + (void *) r, r->headers_out, NULL); + + terminate_header(r->connection->client); + + ap_kill_timeout(r); + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + + return OK; +} + +/* + * Here we try to be compatible with clients that want multipart/x-byteranges + * instead of multipart/byteranges (also see above), as per HTTP/1.1. We + * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication + * that the browser supports an older protocol. We also check User-Agent + * for Microsoft Internet Explorer 3, which needs this as well. + */ +static int use_range_x(request_rec *r) +{ + const char *ua; + return (ap_table_get(r->headers_in, "Request-Range") || + ((ua = ap_table_get(r->headers_in, "User-Agent")) + && strstr(ua, "MSIE 3"))); +} + +/* This routine is called by ap_table_do and merges all instances of + * the passed field values into a single array that will be further + * processed by some later routine. Originally intended to help split + * and recombine multiple Vary fields, though it is generic to any field + * consisting of comma/space-separated tokens. + */ +static int uniq_field_values(void *d, const char *key, const char *val) +{ + array_header *values; + char *start; + char *e; + char **strpp; + int i; + + values = (array_header *)d; + + e = ap_pstrdup(values->pool, val); + + do { + /* Find a non-empty fieldname */ + + while (*e == ',' || ap_isspace(*e)) { + ++e; + } + if (*e == '\0') { + break; + } + start = e; + while (*e != '\0' && *e != ',' && !ap_isspace(*e)) { + ++e; + } + if (*e != '\0') { + *e++ = '\0'; + } + + /* Now add it to values if it isn't already represented. + * Could be replaced by a ap_array_strcasecmp() if we had one. + */ + for (i = 0, strpp = (char **) values->elts; i < values->nelts; + ++i, ++strpp) { + if (*strpp && strcasecmp(*strpp, start) == 0) { + break; + } + } + if (i == values->nelts) { /* if not found */ + *(char **)ap_push_array(values) = start; + } + } while (*e != '\0'); + + return 1; +} + +/* + * Since some clients choke violently on multiple Vary fields, or + * Vary fields with duplicate tokens, combine any multiples and remove + * any duplicates. + */ +static void fixup_vary(request_rec *r) +{ + array_header *varies; + + varies = ap_make_array(r->pool, 5, sizeof(char *)); + + /* Extract all Vary fields from the headers_out, separate each into + * its comma-separated fieldname values, and then add them to varies + * if not already present in the array. + */ + ap_table_do((int (*)(void *, const char *, const char *))uniq_field_values, + (void *) varies, r->headers_out, "Vary", NULL); + + /* If we found any, replace old Vary fields with unique-ified value */ + + if (varies->nelts > 0) { + ap_table_setn(r->headers_out, "Vary", + ap_array_pstrcat(r->pool, varies, ',')); + } +} + +API_EXPORT(void) ap_send_http_header(request_rec *r) +{ + int i; + const long int zero = 0L; +#ifdef CHARSET_EBCDIC + int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII); +#endif /*CHARSET_EBCDIC*/ + + if (r->assbackwards) { + if (!r->main) + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; + return; + } + + /* + * Now that we are ready to send a response, we need to combine the two + * header field tables into a single table. If we don't do this, our + * later attempts to set or unset a given fieldname might be bypassed. + */ + if (!ap_is_empty_table(r->err_headers_out)) + r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out, + r->headers_out); + + /* + * Remove the 'Vary' header field if the client can't handle it. + * Since this will have nasty effects on HTTP/1.1 caches, force + * the response into HTTP/1.0 mode. + */ + if (ap_table_get(r->subprocess_env, "force-no-vary") != NULL) { + ap_table_unset(r->headers_out, "Vary"); + r->proto_num = HTTP_VERSION(1,0); + ap_table_set(r->subprocess_env, "force-response-1.0", "1"); + } + else { + fixup_vary(r); + } + + ap_hard_timeout("send headers", r); + + ap_basic_http_header(r); + +#ifdef CHARSET_EBCDIC + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1); +#endif /*CHARSET_EBCDIC*/ + + ap_set_keepalive(r); + + if (r->chunked) { + ap_table_mergen(r->headers_out, "Transfer-Encoding", "chunked"); + ap_table_unset(r->headers_out, "Content-Length"); + } + + if (r->byterange > 1) + ap_table_setn(r->headers_out, "Content-Type", + ap_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/", + "byteranges; boundary=", r->boundary, NULL)); + else if (r->content_type) + ap_table_setn(r->headers_out, "Content-Type", r->content_type); + else + ap_table_setn(r->headers_out, "Content-Type", ap_default_type(r)); + + if (r->content_encoding) + ap_table_setn(r->headers_out, "Content-Encoding", r->content_encoding); + + if (r->content_languages && r->content_languages->nelts) { + for (i = 0; i < r->content_languages->nelts; ++i) { + ap_table_mergen(r->headers_out, "Content-Language", + ((char **) (r->content_languages->elts))[i]); + } + } + else if (r->content_language) + ap_table_setn(r->headers_out, "Content-Language", r->content_language); + + /* + * Control cachability for non-cachable responses if not already set by + * some other part of the server configuration. + */ + if (r->no_cache && !ap_table_get(r->headers_out, "Expires")) + ap_table_addn(r->headers_out, "Expires", + ap_gm_timestr_822(r->pool, r->request_time)); + + /* Send the entire table of header fields, terminated by an empty line. */ + + ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field, + (void *) r, r->headers_out, NULL); + + terminate_header(r->connection->client); + + ap_kill_timeout(r); + + ap_bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; /* Whatever follows is real body stuff... */ + + /* Set buffer flags for the body */ + if (r->chunked) + ap_bsetflag(r->connection->client, B_CHUNK, 1); +#ifdef CHARSET_EBCDIC + if (!convert) + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert); +#endif /*CHARSET_EBCDIC*/ +} + +/* finalize_request_protocol is called at completion of sending the + * response. It's sole purpose is to send the terminating protocol + * information for any wrappers around the response message body + * (i.e., transfer encodings). It should have been named finalize_response. + */ +API_EXPORT(void) ap_finalize_request_protocol(request_rec *r) +{ + if (r->chunked && !r->connection->aborted) { + /* + * Turn off chunked encoding --- we can only do this once. + */ + r->chunked = 0; + ap_bsetflag(r->connection->client, B_CHUNK, 0); + + ap_soft_timeout("send ending chunk", r); + ap_rputs("0\015\012", r); + /* If we had footer "headers", we'd send them now */ + ap_rputs("\015\012", r); + ap_kill_timeout(r); + } +} + +/* Here we deal with getting the request message body from the client. + * Whether or not the request contains a body is signaled by the presence + * of a non-zero Content-Length or by a Transfer-Encoding: chunked. + * + * Note that this is more complicated than it was in Apache 1.1 and prior + * versions, because chunked support means that the module does less. + * + * The proper procedure is this: + * + * 1. Call setup_client_block() near the beginning of the request + * handler. This will set up all the necessary properties, and will + * return either OK, or an error code. If the latter, the module should + * return that error code. The second parameter selects the policy to + * apply if the request message indicates a body, and how a chunked + * transfer-coding should be interpreted. Choose one of + * + * REQUEST_NO_BODY Send 413 error if message has any body + * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length + * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me. + * REQUEST_CHUNKED_PASS Pass the chunks to me without removal. + * + * In order to use the last two options, the caller MUST provide a buffer + * large enough to hold a chunk-size line, including any extensions. + * + * 2. When you are ready to read a body (if any), call should_client_block(). + * This will tell the module whether or not to read input. If it is 0, + * the module should assume that there is no message body to read. + * This step also sends a 100 Continue response to HTTP/1.1 clients, + * so should not be called until the module is *definitely* ready to + * read content. (otherwise, the point of the 100 response is defeated). + * Never call this function more than once. + * + * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size. + * It will put data into the buffer (not necessarily a full buffer), and + * return the length of the input block. When it is done reading, it will + * return 0 if EOF, or -1 if there was an error. + * If an error occurs on input, we force an end to keepalive. + */ + +API_EXPORT(int) ap_setup_client_block(request_rec *r, int read_policy) +{ + const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding"); + const char *lenp = ap_table_get(r->headers_in, "Content-Length"); + unsigned long max_body; + + r->read_body = read_policy; + r->read_chunked = 0; + r->remaining = 0; + + if (tenc) { + if (strcasecmp(tenc, "chunked")) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Unknown Transfer-Encoding %s", tenc); + return HTTP_NOT_IMPLEMENTED; + } + if (r->read_body == REQUEST_CHUNKED_ERROR) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "chunked Transfer-Encoding forbidden: %s", r->uri); + return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED; + } + + r->read_chunked = 1; + } + else if (lenp) { + const char *pos = lenp; + + while (ap_isdigit(*pos) || ap_isspace(*pos)) + ++pos; + if (*pos != '\0') { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid Content-Length %s", lenp); + return HTTP_BAD_REQUEST; + } + + r->remaining = atol(lenp); + } + + if ((r->read_body == REQUEST_NO_BODY) && + (r->read_chunked || (r->remaining > 0))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "%s with body is not allowed for %s", r->method, r->uri); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + max_body = ap_get_limit_req_body(r); + if (max_body && (r->remaining > max_body)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Request content-length of %s is larger than the configured " + "limit of %lu", lenp, max_body); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + return OK; +} + +API_EXPORT(int) ap_should_client_block(request_rec *r) +{ + /* First check if we have already read the request body */ + + if (r->read_length || (!r->read_chunked && (r->remaining <= 0))) + return 0; + + if (r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)) { + /* sending 100 Continue interim response */ + ap_rvputs(r, SERVER_PROTOCOL, " ", status_lines[0], "\015\012\015\012", + NULL); + ap_rflush(r); + } + + return 1; +} + +static long get_chunk_size(char *b) +{ + long chunksize = 0; + + while (ap_isxdigit(*b)) { + int xvalue = 0; + + if (*b >= '0' && *b <= '9') + xvalue = *b - '0'; + else if (*b >= 'A' && *b <= 'F') + xvalue = *b - 'A' + 0xa; + else if (*b >= 'a' && *b <= 'f') + xvalue = *b - 'a' + 0xa; + + chunksize = (chunksize << 4) | xvalue; + ++b; + } + + return chunksize; +} + +/* get_client_block is called in a loop to get the request message body. + * This is quite simple if the client includes a content-length + * (the normal case), but gets messy if the body is chunked. Note that + * r->remaining is used to maintain state across calls and that + * r->read_length is the total number of bytes given to the caller + * across all invocations. It is messy because we have to be careful not + * to read past the data provided by the client, since these reads block. + * Returns 0 on End-of-body, -1 on error or premature chunk end. + * + * Reading the chunked encoding requires a buffer size large enough to + * hold a chunk-size line, including any extensions. For now, we'll leave + * that to the caller, at least until we can come up with a better solution. + */ +API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz) +{ + int c; + long len_read, len_to_read; + long chunk_start = 0; + unsigned long max_body; + + if (!r->read_chunked) { /* Content-length read */ + len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; + len_read = ap_bread(r->connection->client, buffer, len_to_read); + if (len_read <= 0) { + if (len_read < 0) + r->connection->keepalive = -1; + return len_read; + } + r->read_length += len_read; + r->remaining -= len_read; + return len_read; + } + + /* + * Handle chunked reading Note: we are careful to shorten the input + * bufsiz so that there will always be enough space for us to add a CRLF + * (if necessary). + */ + if (r->read_body == REQUEST_CHUNKED_PASS) + bufsiz -= 2; + if (bufsiz <= 0) + return -1; /* Cannot read chunked with a small buffer */ + + /* Check to see if we have already read too much request data. + * For efficiency reasons, we only check this at the top of each + * caller read pass, since the limit exists just to stop infinite + * length requests and nobody cares if it goes over by one buffer. + */ + max_body = ap_get_limit_req_body(r); + if (max_body && (r->read_length > max_body)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Chunked request body is larger than the configured limit of %lu", + max_body); + r->connection->keepalive = -1; + return -1; + } + + if (r->remaining == 0) { /* Start of new chunk */ + + chunk_start = getline(buffer, bufsiz, r->connection->client, 0); + if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1)) + || !ap_isxdigit(*buffer)) { + r->connection->keepalive = -1; + return -1; + } + + len_to_read = get_chunk_size(buffer); + + if (len_to_read == 0) { /* Last chunk indicated, get footers */ + if (r->read_body == REQUEST_CHUNKED_DECHUNK) { + get_mime_headers(r); + ap_snprintf(buffer, bufsiz, "%ld", r->read_length); + ap_table_unset(r->headers_in, "Transfer-Encoding"); + ap_table_setn(r->headers_in, "Content-Length", + ap_pstrdup(r->pool, buffer)); + return 0; + } + r->remaining = -1; /* Indicate footers in-progress */ + } + else { + r->remaining = len_to_read; + } + if (r->read_body == REQUEST_CHUNKED_PASS) { + buffer[chunk_start++] = CR; /* Restore chunk-size line end */ + buffer[chunk_start++] = LF; + buffer += chunk_start; /* and pass line on to caller */ + bufsiz -= chunk_start; + } + else { + /* REQUEST_CHUNKED_DECHUNK -- do not include the length of the + * header in the return value + */ + chunk_start = 0; + } + } + /* When REQUEST_CHUNKED_PASS, we are */ + if (r->remaining == -1) { /* reading footers until empty line */ + len_read = chunk_start; + + while ((bufsiz > 1) && ((len_read = + getline(buffer, bufsiz, r->connection->client, 1)) > 0)) { + + if (len_read != (bufsiz - 1)) { + buffer[len_read++] = CR; /* Restore footer line end */ + buffer[len_read++] = LF; + } + chunk_start += len_read; + buffer += len_read; + bufsiz -= len_read; + } + if (len_read < 0) { + r->connection->keepalive = -1; + return -1; + } + + if (len_read == 0) { /* Indicates an empty line */ + buffer[0] = CR; + buffer[1] = LF; + chunk_start += 2; + r->remaining = -2; + } + r->read_length += chunk_start; + return chunk_start; + } + /* When REQUEST_CHUNKED_PASS, we */ + if (r->remaining == -2) { /* finished footers when last called */ + r->remaining = 0; /* so now we must signal EOF */ + return 0; + } + + /* Otherwise, we are in the midst of reading a chunk of data */ + + len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; + + len_read = ap_bread(r->connection->client, buffer, len_to_read); + if (len_read <= 0) { + r->connection->keepalive = -1; + return -1; + } + + r->remaining -= len_read; + + if (r->remaining == 0) { /* End of chunk, get trailing CRLF */ + if ((c = ap_bgetc(r->connection->client)) == CR) { + c = ap_bgetc(r->connection->client); + } + if (c != LF) { + r->connection->keepalive = -1; + return -1; + } + if (r->read_body == REQUEST_CHUNKED_PASS) { + buffer[len_read++] = CR; + buffer[len_read++] = LF; + } + } + r->read_length += (chunk_start + len_read); + + return (chunk_start + len_read); +} + +/* In HTTP/1.1, any method can have a body. However, most GET handlers + * wouldn't know what to do with a request body if they received one. + * This helper routine tests for and reads any message body in the request, + * simply discarding whatever it receives. We need to do this because + * failing to read the request body would cause it to be interpreted + * as the next request on a persistent connection. + * + * Since we return an error status if the request is malformed, this + * routine should be called at the beginning of a no-body handler, e.g., + * + * if ((retval = ap_discard_request_body(r)) != OK) + * return retval; + */ +API_EXPORT(int) ap_discard_request_body(request_rec *r) +{ + int rv; + + if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_PASS))) + return rv; + + /* In order to avoid sending 100 Continue when we already know the + * final response status, and yet not kill the connection if there is + * no request body to be read, we need to duplicate the test from + * ap_should_client_block() here negated rather than call it directly. + */ + if ((r->read_length == 0) && (r->read_chunked || (r->remaining > 0))) { + char dumpbuf[HUGE_STRING_LEN]; + + if (r->expecting_100) { + r->connection->keepalive = -1; + return OK; + } + ap_hard_timeout("reading request body", r); + while ((rv = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0) + continue; + ap_kill_timeout(r); + + if (rv < 0) + return HTTP_BAD_REQUEST; + } + return OK; +} + +/* + * Send the body of a response to the client. + */ +API_EXPORT(long) ap_send_fd(FILE *f, request_rec *r) +{ + return ap_send_fd_length(f, r, -1); +} + +API_EXPORT(long) ap_send_fd_length(FILE *f, request_rec *r, long length) +{ + char buf[IOBUFSIZE]; + long total_bytes_sent = 0; + register int n, w, o, len; + + if (length == 0) + return 0; + + ap_soft_timeout("send body", r); + + while (!r->connection->aborted) { + if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length) + len = length - total_bytes_sent; + else + len = IOBUFSIZE; + + while ((n = fread(buf, sizeof(char), len, f)) < 1 + && ferror(f) && errno == EINTR && !r->connection->aborted) + continue; + + if (n < 1) { + break; + } + o = 0; + + while (n && !r->connection->aborted) { + w = ap_bwrite(r->connection->client, &buf[o], n); + if (w > 0) { + ap_reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n -= w; + o += w; + } + else if (w < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send body completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + break; + } + } + } + + ap_kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + +/* + * Send the body of a response to the client. + */ +API_EXPORT(long) ap_send_fb(BUFF *fb, request_rec *r) +{ + return ap_send_fb_length(fb, r, -1); +} + +API_EXPORT(long) ap_send_fb_length(BUFF *fb, request_rec *r, long length) +{ + char buf[IOBUFSIZE]; + long total_bytes_sent = 0; + register int n, w, o, len, fd; + fd_set fds; + + if (length == 0) + return 0; + + /* Make fb unbuffered and non-blocking */ + ap_bsetflag(fb, B_RD, 0); +#ifndef TPF + ap_bnonblock(fb, B_RD); +#endif + fd = ap_bfileno(fb, B_RD); +#ifdef CHECK_FD_SETSIZE + if (fd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "send body: filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", fd, FD_SETSIZE); + return 0; + } +#endif + + ap_soft_timeout("send body", r); + + FD_ZERO(&fds); + while (!r->connection->aborted) { +#ifdef NDELAY_PIPE_RETURNS_ZERO + /* Contributed by dwd@bell-labs.com for UTS 2.1.2, where the fcntl */ + /* O_NDELAY flag causes read to return 0 when there's nothing */ + /* available when reading from a pipe. That makes it tricky */ + /* to detect end-of-file :-(. This stupid bug is even documented */ + /* in the read(2) man page where it says that everything but */ + /* pipes return -1 and EAGAIN. That makes it a feature, right? */ + int afterselect = 0; +#endif + if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length) + len = length - total_bytes_sent; + else + len = IOBUFSIZE; + + do { + n = ap_bread(fb, buf, len); +#ifdef NDELAY_PIPE_RETURNS_ZERO + if ((n > 0) || (n == 0 && afterselect)) + break; +#else + if (n >= 0) + break; +#endif + if (r->connection->aborted) + break; + if (n < 0 && errno != EAGAIN) + break; + + /* we need to block, so flush the output first */ + if (ap_bflush(r->connection->client) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send body completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + break; + } + FD_SET(fd, &fds); + /* + * we don't care what select says, we might as well loop back + * around and try another read + */ + ap_select(fd + 1, &fds, NULL, NULL, NULL); +#ifdef NDELAY_PIPE_RETURNS_ZERO + afterselect = 1; +#endif + } while (!r->connection->aborted); + + if (n < 1 || r->connection->aborted) { + break; + } + o = 0; + + while (n && !r->connection->aborted) { + w = ap_bwrite(r->connection->client, &buf[o], n); + if (w > 0) { + ap_reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n -= w; + o += w; + } + else if (w < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send body completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + break; + } + } + } + + ap_kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + + + +/* The code writes MMAP_SEGMENT_SIZE bytes at a time. This is due to Apache's + * timeout model, which is a timeout per-write rather than a time for the + * entire transaction to complete. Essentially this should be small enough + * so that in one Timeout period, your slowest clients should be reasonably + * able to receive this many bytes. + * + * To take advantage of zero-copy TCP under Solaris 2.6 this should be a + * multiple of 16k. (And you need a SunATM2.0 network card.) + */ +#ifndef MMAP_SEGMENT_SIZE +#define MMAP_SEGMENT_SIZE 32768 +#endif + +/* send data from an in-memory buffer */ +API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset, + size_t length) +{ + size_t total_bytes_sent = 0; + int n, w; + + if (length == 0) + return 0; + + ap_soft_timeout("send mmap", r); + + length += offset; + while (!r->connection->aborted && offset < length) { + if (length - offset > MMAP_SEGMENT_SIZE) { + n = MMAP_SEGMENT_SIZE; + } + else { + n = length - offset; + } + + while (n && !r->connection->aborted) { + w = ap_bwrite(r->connection->client, (char *) mm + offset, n); + if (w > 0) { + ap_reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n -= w; + offset += w; + } + else if (w < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before send mmap completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + break; + } + } + } + + ap_kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + +API_EXPORT(int) ap_rputc(int c, request_rec *r) +{ + if (r->connection->aborted) + return EOF; + + if (ap_bputc(c, r->connection->client) < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rputc completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + SET_BYTES_SENT(r); + return c; +} + +API_EXPORT(int) ap_rputs(const char *str, request_rec *r) +{ + int rcode; + + if (r->connection->aborted) + return EOF; + + rcode = ap_bputs(str, r->connection->client); + if (rcode < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rputs completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + SET_BYTES_SENT(r); + return rcode; +} + +API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r) +{ + int n; + + if (r->connection->aborted) + return -1; + + n = ap_bwrite(r->connection->client, buf, nbyte); + if (n < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rwrite completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return -1; + } + SET_BYTES_SENT(r); + return n; +} + +API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list ap) +{ + int n; + + if (r->connection->aborted) + return -1; + + n = ap_vbprintf(r->connection->client, fmt, ap); + + if (n < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before vrprintf completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return -1; + } + SET_BYTES_SENT(r); + return n; +} + +API_EXPORT(int) ap_rprintf(request_rec *r, const char *fmt,...) +{ + va_list vlist; + int n; + + if (r->connection->aborted) + return -1; + + va_start(vlist, fmt); + n = ap_vbprintf(r->connection->client, fmt, vlist); + va_end(vlist); + + if (n < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rprintf completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return -1; + } + SET_BYTES_SENT(r); + return n; +} + +API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r,...) +{ + va_list args; + int i, j, k; + const char *x; + BUFF *fb = r->connection->client; + + if (r->connection->aborted) + return EOF; + + va_start(args, r); + for (k = 0;;) { + x = va_arg(args, const char *); + if (x == NULL) + break; + j = strlen(x); + i = ap_bwrite(fb, x, j); + if (i != j) { + va_end(args); + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rvputs completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + k += i; + } + va_end(args); + + SET_BYTES_SENT(r); + return k; +} + +API_EXPORT(int) ap_rflush(request_rec *r) +{ + if (ap_bflush(r->connection->client) < 0) { + if (!r->connection->aborted) { + ap_log_rerror(APLOG_MARK, APLOG_INFO, r, + "client stopped connection before rflush completed"); + ap_bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + } + return EOF; + } + return 0; +} + +/* We should have named this send_canned_response, since it is used for any + * response that can be generated by the server from the request record. + * This includes all 204 (no content), 3xx (redirect), 4xx (client error), + * and 5xx (server error) messages that have not been redirected to another + * handler via the ErrorDocument feature. + */ +void ap_send_error_response(request_rec *r, int recursive_error) +{ + int status = r->status; + int idx = ap_index_of_response(status); + char *custom_response; + const char *location = ap_table_get(r->headers_out, "Location"); + + /* + * It's possible that the Location field might be in r->err_headers_out + * instead of r->headers_out; use the latter if possible, else the + * former. + */ + if (location == NULL) { + location = ap_table_get(r->err_headers_out, "Location"); + } + /* We need to special-case the handling of 204 and 304 responses, + * since they have specific HTTP requirements and do not include a + * message body. Note that being assbackwards here is not an option. + */ + if (status == HTTP_NOT_MODIFIED) { + if (!ap_is_empty_table(r->err_headers_out)) + r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out, + r->headers_out); + ap_hard_timeout("send 304", r); + + ap_basic_http_header(r); + ap_set_keepalive(r); + + ap_table_do((int (*)(void *, const char *, const char *)) ap_send_header_field, + (void *) r, r->headers_out, + "Connection", + "Keep-Alive", + "ETag", + "Content-Location", + "Expires", + "Cache-Control", + "Vary", + "Warning", + "WWW-Authenticate", + "Proxy-Authenticate", + NULL); + + terminate_header(r->connection->client); + + ap_kill_timeout(r); + return; + } + + if (status == HTTP_NO_CONTENT) { + ap_send_http_header(r); + ap_finalize_request_protocol(r); + return; + } + + if (!r->assbackwards) { + table *tmp = r->headers_out; + + /* For all HTTP/1.x responses for which we generate the message, + * we need to avoid inheriting the "normal status" header fields + * that may have been set by the request handler before the + * error or redirect, except for Location on external redirects. + */ + r->headers_out = r->err_headers_out; + r->err_headers_out = tmp; + ap_clear_table(r->err_headers_out); + + if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) { + if ((location != NULL) && *location) { + ap_table_setn(r->headers_out, "Location", location); + } + else { + location = ""; /* avoids coredump when printing, below */ + } + } + + r->content_language = NULL; + r->content_languages = NULL; + r->content_encoding = NULL; + r->clength = 0; + r->content_type = "text/html"; + + if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED)) + ap_table_setn(r->headers_out, "Allow", make_allow(r)); + + ap_send_http_header(r); + + if (r->header_only) { + ap_finalize_request_protocol(r); + ap_rflush(r); + return; + } + } + + ap_hard_timeout("send error body", r); + + if ((custom_response = ap_response_code_string(r, idx))) { + /* + * We have a custom response output. This should only be + * a text-string to write back. But if the ErrorDocument + * was a local redirect and the requested resource failed + * for any reason, the custom_response will still hold the + * redirect URL. We don't really want to output this URL + * as a text message, so first check the custom response + * string to ensure that it is a text-string (using the + * same test used in ap_die(), i.e. does it start with a "). + * If it doesn't, we've got a recursive error, so find + * the original error and output that as well. + */ + if (custom_response[0] == '\"') { + ap_rputs(custom_response + 1, r); + ap_kill_timeout(r); + ap_finalize_request_protocol(r); + ap_rflush(r); + return; + } + /* + * Redirect failed, so get back the original error + */ + while (r->prev && (r->prev->status != HTTP_OK)) + r = r->prev; + } + { + const char *title = status_lines[idx]; + const char *h1; + const char *error_notes; + + /* Accept a status_line set by a module, but only if it begins + * with the 3 digit status code + */ + if (r->status_line != NULL + && strlen(r->status_line) > 4 /* long enough */ + && ap_isdigit(r->status_line[0]) + && ap_isdigit(r->status_line[1]) + && ap_isdigit(r->status_line[2]) + && ap_isspace(r->status_line[3]) + && ap_isalnum(r->status_line[4])) { + title = r->status_line; + } + + /* folks decided they didn't want the error code in the H1 text */ + h1 = &title[4]; + + ap_rvputs(r, + DOCTYPE_HTML_2_0 + "\n", title, + "\n\n

", h1, "

\n", + NULL); + + switch (status) { + case HTTP_MOVED_PERMANENTLY: + case HTTP_MOVED_TEMPORARILY: + case HTTP_TEMPORARY_REDIRECT: + ap_rvputs(r, "The document has moved pool, location), "\">here.

\n", + NULL); + break; + case HTTP_SEE_OTHER: + ap_rvputs(r, "The answer to your request is located pool, location), "\">here.

\n", + NULL); + break; + case HTTP_USE_PROXY: + ap_rvputs(r, "This resource is only accessible " + "through the proxy\n", + ap_escape_html(r->pool, location), + "
\nYou will need to ", + "configure your client to use that proxy.

\n", NULL); + break; + case HTTP_PROXY_AUTHENTICATION_REQUIRED: + case AUTH_REQUIRED: + ap_rputs("This server could not verify that you\n" + "are authorized to access the document\n" + "requested. Either you supplied the wrong\n" + "credentials (e.g., bad password), or your\n" + "browser doesn't understand how to supply\n" + "the credentials required.

\n", r); + break; + case BAD_REQUEST: + ap_rputs("Your browser sent a request that " + "this server could not understand.

\n", r); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case HTTP_FORBIDDEN: + ap_rvputs(r, "You don't have permission to access ", + ap_escape_html(r->pool, r->uri), + "\non this server.

\n", NULL); + break; + case NOT_FOUND: + ap_rvputs(r, "The requested URL ", + ap_escape_html(r->pool, r->uri), + " was not found on this server.

\n", NULL); + break; + case METHOD_NOT_ALLOWED: + ap_rvputs(r, "The requested method ", r->method, + " is not allowed " + "for the URL ", ap_escape_html(r->pool, r->uri), + ".

\n", NULL); + break; + case NOT_ACCEPTABLE: + ap_rvputs(r, + "An appropriate representation of the " + "requested resource ", + ap_escape_html(r->pool, r->uri), + " could not be found on this server.

\n", NULL); + /* fall through */ + case MULTIPLE_CHOICES: + { + const char *list; + if ((list = ap_table_get(r->notes, "variant-list"))) + ap_rputs(list, r); + } + break; + case LENGTH_REQUIRED: + ap_rvputs(r, "A request of the requested method ", r->method, + " requires a valid Content-length.

\n", NULL); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case PRECONDITION_FAILED: + ap_rvputs(r, "The precondition on the request for the URL ", + ap_escape_html(r->pool, r->uri), + " evaluated to false.

\n", NULL); + break; + case HTTP_NOT_IMPLEMENTED: + ap_rvputs(r, ap_escape_html(r->pool, r->method), " to ", + ap_escape_html(r->pool, r->uri), + " not supported.

\n", NULL); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case BAD_GATEWAY: + ap_rputs("The proxy server received an invalid\015\012" + "response from an upstream server.

\015\012", r); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case VARIANT_ALSO_VARIES: + ap_rvputs(r, "A variant for the requested resource\n

\n",
+		      ap_escape_html(r->pool, r->uri),
+		      "\n
\nis itself a negotiable resource. " + "This indicates a configuration error.

\n", NULL); + break; + case HTTP_REQUEST_TIME_OUT: + ap_rputs("I'm tired of waiting for your request.\n", r); + break; + case HTTP_GONE: + ap_rvputs(r, "The requested resource
", + ap_escape_html(r->pool, r->uri), + "
\nis no longer available on this server ", + "and there is no forwarding address.\n", + "Please remove all references to this resource.\n", + NULL); + break; + case HTTP_REQUEST_ENTITY_TOO_LARGE: + ap_rvputs(r, "The requested resource
", + ap_escape_html(r->pool, r->uri), "
\n", + "does not allow request data with ", r->method, + " requests, or the amount of data provided in\n", + "the request exceeds the capacity limit.\n", NULL); + break; + case HTTP_REQUEST_URI_TOO_LARGE: + ap_rputs("The requested URL's length exceeds the capacity\n" + "limit for this server.

\n", r); + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + break; + case HTTP_UNSUPPORTED_MEDIA_TYPE: + ap_rputs("The supplied request data is not in a format\n" + "acceptable for processing by this resource.\n", r); + break; + case HTTP_RANGE_NOT_SATISFIABLE: + ap_rputs("None of the range-specifier values in the Range\n" + "request-header field overlap the current extent\n" + "of the selected resource.\n", r); + break; + case HTTP_EXPECTATION_FAILED: + ap_rvputs(r, "The expectation given in the Expect request-header" + "\nfield could not be met by this server.

\n" + "The client sent

\n    Expect: ",
+	              ap_table_get(r->headers_in, "Expect"), "\n
\n" + "but we only allow the 100-continue expectation.\n", + NULL); + break; + case HTTP_UNPROCESSABLE_ENTITY: + ap_rputs("The server understands the media type of the\n" + "request entity, but was unable to process the\n" + "contained instructions.\n", r); + break; + case HTTP_LOCKED: + ap_rputs("The requested resource is currently locked.\n" + "The lock must be released or proper identification\n" + "given before the method can be applied.\n", r); + break; + case HTTP_FAILED_DEPENDENCY: + ap_rputs("The method could not be performed on the resource\n" + "because the requested action depended on another\n" + "action and that other action failed.\n", r); + break; + case HTTP_INSUFFICIENT_STORAGE: + ap_rputs("The method could not be performed on the resource\n" + "because the server is unable to store the\n" + "representation needed to successfully complete the\n" + "request. There is insufficient free space left in\n" + "your storage allocation.\n", r); + break; + case HTTP_SERVICE_UNAVAILABLE: + ap_rputs("The server is temporarily unable to service your\n" + "request due to maintenance downtime or capacity\n" + "problems. Please try again later.\n", r); + break; + case HTTP_GATEWAY_TIME_OUT: + ap_rputs("The proxy server did not receive a timely response\n" + "from the upstream server.\n", r); + break; + case HTTP_NOT_EXTENDED: + ap_rputs("A mandatory extension policy in the request is not\n" + "accepted by the server for this resource.\n", r); + break; + default: /* HTTP_INTERNAL_SERVER_ERROR */ + /* + * This comparison to expose error-notes could be modified to + * use a configuration directive and export based on that + * directive. For now "*" is used to designate an error-notes + * that is totally safe for any user to see (ie lacks paths, + * database passwords, etc.) + */ + if (((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) + && (h1 = ap_table_get(r->notes, "verbose-error-to")) != NULL + && (strcmp(h1, "*") == 0)) { + ap_rvputs(r, error_notes, "

\n", NULL); + } + else { + ap_rvputs(r, "The server encountered an internal error or\n" + "misconfiguration and was unable to complete\n" + "your request.

\n" + "Please contact the server administrator,\n ", + ap_escape_html(r->pool, r->server->server_admin), + " and inform them of the time the error occurred,\n" + "and anything you might have done that may have\n" + "caused the error.

\n" + "More information about this error may be available\n" + "in the server error log.

\n", NULL); + } + /* + * It would be nice to give the user the information they need to + * fix the problem directly since many users don't have access to + * the error_log (think University sites) even though they can easily + * get this error by misconfiguring an htaccess file. However, the + * error notes tend to include the real file pathname in this case, + * which some people consider to be a breach of privacy. Until we + * can figure out a way to remove the pathname, leave this commented. + * + * if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + * ap_rvputs(r, error_notes, "

\n", NULL); + * } + */ + break; + } + + if (recursive_error) { + ap_rvputs(r, "

Additionally, a ", + status_lines[ap_index_of_response(recursive_error)], + "\nerror was encountered while trying to use an " + "ErrorDocument to handle the request.\n", NULL); + } + ap_rputs(ap_psignature("


\n", r), r); + ap_rputs("\n", r); + } + ap_kill_timeout(r); + ap_finalize_request_protocol(r); + ap_rflush(r); +} diff --git a/modules/http/http_request.c b/modules/http/http_request.c new file mode 100644 index 0000000000..bc4d45f60f --- /dev/null +++ b/modules/http/http_request.c @@ -0,0 +1,1374 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_request.c: functions to get and process requests + * + * Rob McCool 3/21/93 + * + * Thoroughly revamped by rst for Apache. NB this file reads + * best from the bottom up. + * + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_conf_globals.h" /* for ap_extended_status */ +#include "http_log.h" +#include "http_main.h" +#include "scoreboard.h" +#include "fnmatch.h" + +/***************************************************************** + * + * Getting and checking directory configuration. Also checks the + * FollowSymlinks and FollowSymOwner stuff, since this is really the + * only place that can happen (barring a new mid_dir_walk callout). + * + * We can't do it as an access_checker module function which gets + * called with the final per_dir_config, since we could have a directory + * with FollowSymLinks disabled, which contains a symlink to another + * with a .htaccess file which turns FollowSymLinks back on --- and + * access in such a case must be denied. So, whatever it is that + * checks FollowSymLinks needs to know the state of the options as + * they change, all the way down. + */ + +/* + * We don't want people able to serve up pipes, or unix sockets, or other + * scary things. Note that symlink tests are performed later. + */ +static int check_safe_file(request_rec *r) +{ + if (r->finfo.st_mode == 0 /* doesn't exist */ + || S_ISDIR(r->finfo.st_mode) + || S_ISREG(r->finfo.st_mode) + || S_ISLNK(r->finfo.st_mode)) { + return OK; + } + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "object is not a file, directory or symlink: %s", + r->filename); + return HTTP_FORBIDDEN; +} + + +static int check_symlinks(char *d, int opts) +{ +#if defined(OS2) || defined(WIN32) + /* OS/2 doesn't have symlinks */ + return OK; +#else + struct stat lfi, fi; + char *lastp; + int res; + + if (opts & OPT_SYM_LINKS) + return OK; + + /* + * Strip trailing '/', if any, off what we're checking; trailing slashes + * make some systems follow symlinks to directories even in lstat(). + * After we've done the lstat, put it back. Also, don't bother checking + * '/' at all... + * + * Note that we don't have to worry about multiple slashes here because of + * no2slash() below... + */ + + lastp = d + strlen(d) - 1; + if (lastp == d) + return OK; /* Root directory, '/' */ + + if (*lastp == '/') + *lastp = '\0'; + else + lastp = NULL; + + res = lstat(d, &lfi); + + if (lastp) + *lastp = '/'; + + /* + * Note that we don't reject accesses to nonexistent files (multiviews or + * the like may cons up a way to run the transaction anyway)... + */ + + if (!(res >= 0) || !S_ISLNK(lfi.st_mode)) + return OK; + + /* OK, it's a symlink. May still be OK with OPT_SYM_OWNER */ + + if (!(opts & OPT_SYM_OWNER)) + return HTTP_FORBIDDEN; + + if (stat(d, &fi) < 0) + return HTTP_FORBIDDEN; + + return (fi.st_uid == lfi.st_uid) ? OK : HTTP_FORBIDDEN; + +#endif +} + +/* Dealing with the file system to get PATH_INFO + */ +static int get_path_info(request_rec *r) +{ + char *cp; + char *path = r->filename; + char *end = &path[strlen(path)]; + char *last_cp = NULL; + int rv; +#ifdef HAVE_DRIVE_LETTERS + char bStripSlash=1; +#endif + + if (r->finfo.st_mode) { + /* assume path_info already set */ + return OK; + } + +#ifdef HAVE_DRIVE_LETTERS + /* If the directory is x:\, then we don't want to strip + * the trailing slash since x: is not a valid directory. + */ + if (strlen(path) == 3 && path[1] == ':' && path[2] == '/') + bStripSlash = 0; + + + /* If UNC name == //machine/share/, do not + * advance over the trailing slash. Any other + * UNC name is OK to strip the slash. + */ + cp = end; + if (strlen(path) > 2 && path[0] == '/' && path[1] == '/' && + path[2] != '/' && cp[-1] == '/') { + char *p; + int iCount=0; + p = path; + while (p = strchr(p,'/')) { + p++; + iCount++; + } + + if (iCount == 4) + bStripSlash = 0; + } + + if (bStripSlash) +#endif + /* Advance over trailing slashes ... NOT part of filename + * if file is not a UNC name (Win32 only). + */ + for (cp = end; cp > path && cp[-1] == '/'; --cp) + continue; + + + while (cp > path) { + + /* See if the pathname ending here exists... */ + + *cp = '\0'; + + /* We must not stat() filenames that may cause os-specific system + * problems, such as "/file/aux" on DOS-abused filesystems. + * So pretend that they do not exist by returning an ENOENT error. + * This will force us to drop that part of the path and keep + * looking back for a "real" file that exists, while still allowing + * the "invalid" path parts within the PATH_INFO. + */ + if (!ap_os_is_filename_valid(path)) { + errno = ENOENT; + rv = -1; + } + else { + errno = 0; + rv = stat(path, &r->finfo); + } + + if (cp != end) + *cp = '/'; + + if (!rv) { + + /* + * Aha! Found something. If it was a directory, we will search + * contents of that directory for a multi_match, so the PATH_INFO + * argument starts with the component after that. + */ + + if (S_ISDIR(r->finfo.st_mode) && last_cp) { + r->finfo.st_mode = 0; /* No such file... */ + cp = last_cp; + } + + r->path_info = ap_pstrdup(r->pool, cp); + *cp = '\0'; + return OK; + } + /* must set this to zero, some stat()s may have corrupted it + * even if they returned an error. + */ + r->finfo.st_mode = 0; +#if defined(ENOENT) && defined(ENOTDIR) + if (errno == ENOENT || errno == ENOTDIR) { + last_cp = cp; + + while (--cp > path && *cp != '/') + continue; + + while (cp > path && cp[-1] == '/') + --cp; + } + else { +#if defined(EACCES) + if (errno != EACCES) +#endif + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "access to %s failed", r->uri); + return HTTP_FORBIDDEN; + } +#else +#error ENOENT || ENOTDIR not defined; please see the +#error comments at this line in the source for a workaround. + /* + * If ENOENT || ENOTDIR is not defined in one of the your OS's + * include files, Apache does not know how to check to see why the + * stat() of the index file failed; there are cases where it can fail + * even though the file exists. This means that it is possible for + * someone to get a directory listing of a directory even though + * there is an index (eg. index.html) file in it. If you do not have + * a problem with this, delete the above #error lines and start the + * compile again. If you need to do this, please submit a bug report + * from http://www.apache.org/bug_report.html letting us know that + * you needed to do this. Please be sure to include the operating + * system you are using. + */ + last_cp = cp; + + while (--cp > path && *cp != '/') + continue; + + while (cp > path && cp[-1] == '/') + --cp; +#endif /* ENOENT && ENOTDIR */ + } + return OK; +} + +static int directory_walk(request_rec *r) +{ + core_server_config *sconf = ap_get_module_config(r->server->module_config, + &core_module); + void *per_dir_defaults = r->server->lookup_defaults; + void **sec = (void **) sconf->sec->elts; + int num_sec = sconf->sec->nelts; + char *test_filename; + char *test_dirname; + int res; + unsigned i, num_dirs, iStart; + int j, test_filename_len; + + /* + * Are we dealing with a file? If not, we can (hopefuly) safely assume we + * have a handler that doesn't require one, but for safety's sake, and so + * we have something find_types() can get something out of, fake one. But + * don't run through the directory entries. + */ + + if (r->filename == NULL) { + r->filename = ap_pstrdup(r->pool, r->uri); + r->finfo.st_mode = 0; /* Not really a file... */ + r->per_dir_config = per_dir_defaults; + + return OK; + } + + /* + * Go down the directory hierarchy. Where we have to check for symlinks, + * do so. Where a .htaccess file has permission to override anything, + * try to find one. If either of these things fails, we could poke + * around, see why, and adjust the lookup_rec accordingly --- this might + * save us a call to get_path_info (with the attendant stat()s); however, + * for the moment, that's not worth the trouble. + * + * Fake filenames (i.e. proxy:) only match Directory sections. + */ + + if (!ap_os_is_path_absolute(r->filename)) + { + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_dir; + + for (j = 0; j < num_sec; ++j) { + + entry_config = sec[j]; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + this_conf = NULL; + if (entry_core->r) { + if (!ap_regexec(entry_core->r, r->filename, 0, NULL, 0)) + this_conf = entry_config; + } + else if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_dir, r->filename, 0)) + this_conf = entry_config; + } + else if (!strncmp(r->filename, entry_dir, strlen(entry_dir))) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + this_conf); + } + + r->per_dir_config = per_dir_defaults; + + return OK; + } + + r->filename = ap_os_case_canonical_filename(r->pool, r->filename); + + res = get_path_info(r); + if (res != OK) { + return res; + } + + r->filename = ap_os_canonical_filename(r->pool, r->filename); + + test_filename = ap_pstrdup(r->pool, r->filename); + + ap_no2slash(test_filename); + num_dirs = ap_count_dirs(test_filename); + + if (!ap_os_is_filename_valid(r->filename)) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Filename is not valid: %s", r->filename); + return HTTP_FORBIDDEN; + } + + if ((res = check_safe_file(r))) { + return res; + } + + test_filename_len = strlen(test_filename); + if (test_filename[test_filename_len - 1] == '/') + --num_dirs; + + if (S_ISDIR(r->finfo.st_mode)) + ++num_dirs; + + /* + * We will use test_dirname as scratch space while we build directory + * names during the walk. Profiling shows directory_walk to be a busy + * function so we try to avoid allocating lots of extra memory here. + * We need 2 extra bytes, one for trailing \0 and one because + * make_dirstr_prefix will add potentially one extra /. + */ + test_dirname = ap_palloc(r->pool, test_filename_len + 2); + + iStart = 1; +#ifdef WIN32 + /* If the name is a UNC name, then do not walk through the + * machine and share name (e.g. \\machine\share\) + */ + if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/') + iStart = 4; +#endif + + /* j keeps track of which section we're on, see core_reorder_directories */ + j = 0; + for (i = iStart; i <= num_dirs; ++i) { + int overrides_here; + core_dir_config *core_dir = (core_dir_config *) + ap_get_module_config(per_dir_defaults, &core_module); + + /* + * XXX: this could be made faster by only copying the next component + * rather than copying the entire thing all over. + */ + ap_make_dirstr_prefix(test_dirname, test_filename, i); + + /* + * Do symlink checks first, because they are done with the + * permissions appropriate to the *parent* directory... + */ + + if ((res = check_symlinks(test_dirname, core_dir->opts))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Symbolic link not allowed: %s", test_dirname); + return res; + } + + /* + * Begin *this* level by looking for matching sections + * from access.conf. + */ + + for (; j < num_sec; ++j) { + void *entry_config = sec[j]; + core_dir_config *entry_core; + char *entry_dir; + void *this_conf; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + if (entry_core->r + || !ap_os_is_path_absolute(entry_dir) + || entry_core->d_components > i) + break; + + this_conf = NULL; + if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) { + this_conf = entry_config; + } + } + else if (!strcmp(test_dirname, entry_dir)) + this_conf = entry_config; + + if (this_conf) { + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + this_conf); + core_dir = (core_dir_config *) + ap_get_module_config(per_dir_defaults, &core_module); + } + } + overrides_here = core_dir->override; + + /* If .htaccess files are enabled, check for one. */ + + if (overrides_here) { + void *htaccess_conf = NULL; + + res = ap_parse_htaccess(&htaccess_conf, r, overrides_here, + ap_pstrdup(r->pool, test_dirname), + sconf->access_name); + if (res) + return res; + + if (htaccess_conf) { + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + htaccess_conf); + r->per_dir_config = per_dir_defaults; + } + } + } + + /* + * There's two types of IS_SPECIAL sections (see http_core.c), and we've + * already handled the proxy:-style stuff. Now we'll deal with the + * regexes. + */ + for (; j < num_sec; ++j) { + void *entry_config = sec[j]; + core_dir_config *entry_core; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + + if (entry_core->r) { + if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) { + per_dir_defaults = + ap_merge_per_dir_configs(r->pool, per_dir_defaults, + entry_config); + } + } + } + r->per_dir_config = per_dir_defaults; + + /* + * Symlink permissions are determined by the parent. If the request is + * for a directory then applying the symlink test here would use the + * permissions of the directory as opposed to its parent. Consider a + * symlink pointing to a dir with a .htaccess disallowing symlinks. If + * you access /symlink (or /symlink/) you would get a 403 without this + * S_ISDIR test. But if you accessed /symlink/index.html, for example, + * you would *not* get the 403. + */ + if (!S_ISDIR(r->finfo.st_mode) + && (res = check_symlinks(r->filename, ap_allow_options(r)))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Symbolic link not allowed: %s", r->filename); + return res; + } + return OK; /* Can only "fail" if access denied by the + * symlink goop. */ +} + +static int location_walk(request_rec *r) +{ + core_server_config *sconf = ap_get_module_config(r->server->module_config, + &core_module); + void *per_dir_defaults = r->per_dir_config; + void **url = (void **) sconf->sec_url->elts; + int len, num_url = sconf->sec_url->nelts; + char *test_location; + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_url; + int j; + + if (!num_url) { + return OK; + } + + /* Location and LocationMatch differ on their behaviour w.r.t. multiple + * slashes. Location matches multiple slashes with a single slash, + * LocationMatch doesn't. An exception, for backwards brokenness is + * absoluteURIs... in which case neither match multiple slashes. + */ + if (r->uri[0] != '/') { + test_location = r->uri; + } + else { + test_location = ap_pstrdup(r->pool, r->uri); + ap_no2slash(test_location); + } + + /* Go through the location entries, and check for matches. */ + + /* we apply the directive sections in some order; + * should really try them with the most general first. + */ + for (j = 0; j < num_url; ++j) { + + entry_config = url[j]; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_url = entry_core->d; + + len = strlen(entry_url); + + this_conf = NULL; + + if (entry_core->r) { + if (!ap_regexec(entry_core->r, r->uri, 0, NULL, 0)) + this_conf = entry_config; + } + else if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_url, test_location, FNM_PATHNAME)) { + this_conf = entry_config; + } + } + else if (!strncmp(test_location, entry_url, len) && + (entry_url[len - 1] == '/' || + test_location[len] == '/' || test_location[len] == '\0')) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, this_conf); + } + r->per_dir_config = per_dir_defaults; + + return OK; +} + +static int file_walk(request_rec *r) +{ + core_dir_config *conf = ap_get_module_config(r->per_dir_config, &core_module); + void *per_dir_defaults = r->per_dir_config; + void **file = (void **) conf->sec->elts; + int num_files = conf->sec->nelts; + char *test_file; + + /* get the basename */ + test_file = strrchr(r->filename, '/'); + if (test_file == NULL) { + test_file = r->filename; + } + else { + ++test_file; + } + + /* Go through the file entries, and check for matches. */ + + if (num_files) { + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_file; + int j; + + /* we apply the directive sections in some order; + * should really try them with the most general first. + */ + for (j = 0; j < num_files; ++j) { + + entry_config = file[j]; + + entry_core = (core_dir_config *) + ap_get_module_config(entry_config, &core_module); + entry_file = entry_core->d; + + this_conf = NULL; + + if (entry_core->r) { + if (!ap_regexec(entry_core->r, test_file, 0, NULL, 0)) + this_conf = entry_config; + } + else if (entry_core->d_is_fnmatch) { + if (!ap_fnmatch(entry_file, test_file, FNM_PATHNAME)) { + this_conf = entry_config; + } + } + else if (!strcmp(test_file, entry_file)) { + this_conf = entry_config; + } + + if (this_conf) + per_dir_defaults = ap_merge_per_dir_configs(r->pool, + per_dir_defaults, + this_conf); + } + r->per_dir_config = per_dir_defaults; + } + return OK; +} + +/***************************************************************** + * + * The sub_request mechanism. + * + * Fns to look up a relative URI from, e.g., a map file or SSI document. + * These do all access checks, etc., but don't actually run the transaction + * ... use run_sub_req below for that. Also, be sure to use destroy_sub_req + * as appropriate if you're likely to be creating more than a few of these. + * (An early Apache version didn't destroy the sub_reqs used in directory + * indexing. The result, when indexing a directory with 800-odd files in + * it, was massively excessive storage allocation). + * + * Note more manipulation of protocol-specific vars in the request + * structure... + */ + +static request_rec *make_sub_request(const request_rec *r) +{ + pool *rrp = ap_make_sub_pool(r->pool); + request_rec *rr = ap_pcalloc(rrp, sizeof(request_rec)); + + rr->pool = rrp; + return rr; +} + +API_EXPORT(request_rec *) ap_sub_req_method_uri(const char *method, + const char *new_file, + const request_rec *r) +{ + request_rec *rnew; + int res; + char *udir; + + rnew = make_sub_request(r); + rnew->hostname = r->hostname; + rnew->request_time = r->request_time; + rnew->connection = r->connection; + rnew->server = r->server; + rnew->request_config = ap_create_request_config(rnew->pool); + rnew->htaccess = r->htaccess; + rnew->per_dir_config = r->server->lookup_defaults; + + ap_set_sub_req_protocol(rnew, r); + + /* would be nicer to pass "method" to ap_set_sub_req_protocol */ + rnew->method = method; + rnew->method_number = ap_method_number_of(method); + + if (new_file[0] == '/') + ap_parse_uri(rnew, new_file); + else { + udir = ap_make_dirstr_parent(rnew->pool, r->uri); + udir = ap_escape_uri(rnew->pool, udir); /* re-escape it */ + ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file)); + } + + res = ap_unescape_url(rnew->uri); + if (res) { + rnew->status = res; + return rnew; + } + + ap_getparents(rnew->uri); + + if ((res = location_walk(rnew))) { + rnew->status = res; + return rnew; + } + + res = ap_translate_name(rnew); + if (res) { + rnew->status = res; + return rnew; + } + + /* + * We could be clever at this point, and avoid calling directory_walk, + * etc. However, we'd need to test that the old and new filenames contain + * the same directory components, so it would require duplicating the + * start of translate_name. Instead we rely on the cache of .htaccess + * results. + * + * NB: directory_walk() clears the per_dir_config, so we don't inherit + * from location_walk() above + */ + + if ((res = directory_walk(rnew)) + || (res = file_walk(rnew)) + || (res = location_walk(rnew)) + || ((ap_satisfies(rnew) == SATISFY_ALL + || ap_satisfies(rnew) == SATISFY_NOSPEC) + ? ((res = ap_check_access(rnew)) + || (ap_some_auth_required(rnew) + && ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + : ((res = ap_check_access(rnew)) + && (!ap_some_auth_required(rnew) + || ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + ) + || (res = ap_find_types(rnew)) + || (res = ap_run_fixups(rnew)) + ) { + rnew->status = res; + } + return rnew; +} + +API_EXPORT(request_rec *) ap_sub_req_lookup_uri(const char *new_file, + const request_rec *r) +{ + return ap_sub_req_method_uri("GET", new_file, r); +} + +API_EXPORT(request_rec *) ap_sub_req_lookup_file(const char *new_file, + const request_rec *r) +{ + request_rec *rnew; + int res; + char *fdir; + + rnew = make_sub_request(r); + rnew->hostname = r->hostname; + rnew->request_time = r->request_time; + rnew->connection = r->connection; + rnew->server = r->server; + rnew->request_config = ap_create_request_config(rnew->pool); + rnew->htaccess = r->htaccess; + + ap_set_sub_req_protocol(rnew, r); + fdir = ap_make_dirstr_parent(rnew->pool, r->filename); + + /* + * Check for a special case... if there are no '/' characters in new_file + * at all, then we are looking at a relative lookup in the same + * directory. That means we won't have to redo directory_walk, and we may + * not even have to redo access checks. + */ + + if (strchr(new_file, '/') == NULL) { + char *udir = ap_make_dirstr_parent(rnew->pool, r->uri); + + rnew->uri = ap_make_full_path(rnew->pool, udir, new_file); + rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file); + ap_parse_uri(rnew, rnew->uri); /* fill in parsed_uri values */ + if (stat(rnew->filename, &rnew->finfo) < 0) { + rnew->finfo.st_mode = 0; + } + + if ((res = check_safe_file(rnew))) { + rnew->status = res; + return rnew; + } + + rnew->per_dir_config = r->per_dir_config; + + /* + * no matter what, if it's a subdirectory, we need to re-run + * directory_walk + */ + if (S_ISDIR(rnew->finfo.st_mode)) { + res = directory_walk(rnew); + if (!res) { + res = file_walk(rnew); + } + } + else { + if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew)))) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, rnew, + "Symbolic link not allowed: %s", rnew->filename); + rnew->status = res; + return rnew; + } + /* + * do a file_walk, if it doesn't change the per_dir_config then + * we know that we don't have to redo all the access checks + */ + if ((res = file_walk(rnew))) { + rnew->status = res; + return rnew; + } + if (rnew->per_dir_config == r->per_dir_config) { + if ((res = ap_find_types(rnew)) || (res = ap_run_fixups(rnew))) { + rnew->status = res; + } + return rnew; + } + } + } + else { + /* XXX: @@@: What should be done with the parsed_uri values? */ + ap_parse_uri(rnew, new_file); /* fill in parsed_uri values */ + /* + * XXX: this should be set properly like it is in the same-dir case + * but it's actually sometimes to impossible to do it... because the + * file may not have a uri associated with it -djg + */ + rnew->uri = "INTERNALLY GENERATED file-relative req"; + rnew->filename = ((ap_os_is_path_absolute(new_file)) ? + ap_pstrdup(rnew->pool, new_file) : + ap_make_full_path(rnew->pool, fdir, new_file)); + rnew->per_dir_config = r->server->lookup_defaults; + res = directory_walk(rnew); + if (!res) { + res = file_walk(rnew); + } + } + + if (res + || ((ap_satisfies(rnew) == SATISFY_ALL + || ap_satisfies(rnew) == SATISFY_NOSPEC) + ? ((res = ap_check_access(rnew)) + || (ap_some_auth_required(rnew) + && ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + : ((res = ap_check_access(rnew)) + && (!ap_some_auth_required(rnew) + || ((res = ap_check_user_id(rnew)) + || (res = ap_check_auth(rnew))))) + ) + || (res = ap_find_types(rnew)) + || (res = ap_run_fixups(rnew)) + ) { + rnew->status = res; + } + return rnew; +} + +API_EXPORT(int) ap_run_sub_req(request_rec *r) +{ +#ifndef CHARSET_EBCDIC + int retval = ap_invoke_handler(r); +#else /*CHARSET_EBCDIC*/ + /* Save the EBCDIC conversion setting of the caller across subrequests */ + int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII); + int retval = ap_invoke_handler(r); + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert); +#endif /*CHARSET_EBCDIC*/ + ap_finalize_sub_req_protocol(r); + return retval; +} + +API_EXPORT(void) ap_destroy_sub_req(request_rec *r) +{ + /* Reclaim the space */ + ap_destroy_pool(r->pool); +} + +/***************************************************************** + * + * Mainline request processing... + */ + +API_EXPORT(void) ap_die(int type, request_rec *r) +{ + int error_index = ap_index_of_response(type); + char *custom_response = ap_response_code_string(r, error_index); + int recursive_error = 0; + + if (type == DONE) { + ap_finalize_request_protocol(r); + return; + } + + /* + * The following takes care of Apache redirects to custom response URLs + * Note that if we are already dealing with the response to some other + * error condition, we just report on the original error, and give up on + * any attempt to handle the other thing "intelligently"... + */ + + if (r->status != HTTP_OK) { + recursive_error = type; + + while (r->prev && (r->prev->status != HTTP_OK)) + r = r->prev; /* Get back to original error */ + + type = r->status; + custom_response = NULL; /* Do NOT retry the custom thing! */ + } + + r->status = type; + + /* + * This test is done here so that none of the auth modules needs to know + * about proxy authentication. They treat it like normal auth, and then + * we tweak the status. + */ + if (r->status == AUTH_REQUIRED && r->proxyreq) { + r->status = HTTP_PROXY_AUTHENTICATION_REQUIRED; + } + + /* + * If we want to keep the connection, be sure that the request body + * (if any) has been read. + */ + if ((r->status != HTTP_NOT_MODIFIED) && (r->status != HTTP_NO_CONTENT) + && !ap_status_drops_connection(r->status) + && r->connection && (r->connection->keepalive != -1)) { + + (void) ap_discard_request_body(r); + } + + /* + * Two types of custom redirects --- plain text, and URLs. Plain text has + * a leading '"', so the URL code, here, is triggered on its absence + */ + + if (custom_response && custom_response[0] != '"') { + + if (ap_is_url(custom_response)) { + /* + * The URL isn't local, so lets drop through the rest of this + * apache code, and continue with the usual REDIRECT handler. + * But note that the client will ultimately see the wrong + * status... + */ + r->status = REDIRECT; + ap_table_setn(r->headers_out, "Location", custom_response); + } + else if (custom_response[0] == '/') { + const char *error_notes; + r->no_local_copy = 1; /* Do NOT send USE_LOCAL_COPY for + * error documents! */ + /* + * This redirect needs to be a GET no matter what the original + * method was. + */ + ap_table_setn(r->subprocess_env, "REQUEST_METHOD", r->method); + + /* + * Provide a special method for modules to communicate + * more informative (than the plain canned) messages to us. + * Propagate them to ErrorDocuments via the ERROR_NOTES variable: + */ + if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) { + ap_table_setn(r->subprocess_env, "ERROR_NOTES", error_notes); + } + r->method = ap_pstrdup(r->pool, "GET"); + r->method_number = M_GET; + ap_internal_redirect(custom_response, r); + return; + } + else { + /* + * Dumb user has given us a bad url to redirect to --- fake up + * dying with a recursive server error... + */ + recursive_error = SERVER_ERROR; + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Invalid error redirection directive: %s", + custom_response); + } + } + ap_send_error_response(r, recursive_error); +} + +static void decl_die(int status, char *phase, request_rec *r) +{ + if (status == DECLINED) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, r, + "configuration error: couldn't %s: %s", phase, r->uri); + ap_die(SERVER_ERROR, r); + } + else + ap_die(status, r); +} + +API_EXPORT(int) ap_some_auth_required(request_rec *r) +{ + /* Is there a require line configured for the type of *this* req? */ + + const array_header *reqs_arr = ap_requires(r); + require_line *reqs; + int i; + + if (!reqs_arr) + return 0; + + reqs = (require_line *) reqs_arr->elts; + + for (i = 0; i < reqs_arr->nelts; ++i) + if (reqs[i].method_mask & (1 << r->method_number)) + return 1; + + return 0; +} + +static void process_request_internal(request_rec *r) +{ + int access_status; + + /* Ignore embedded %2F's in path for proxy requests */ + if (!r->proxyreq && r->parsed_uri.path) { + access_status = ap_unescape_url(r->parsed_uri.path); + if (access_status) { + ap_die(access_status, r); + return; + } + } + + ap_getparents(r->uri); /* OK --- shrinking transformations... */ + + if ((access_status = location_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = ap_translate_name(r))) { + decl_die(access_status, "translate", r); + return; + } + + if (!r->proxyreq) { + /* + * We don't want TRACE to run through the normal handler set, we + * handle it specially. + */ + if (r->method_number == M_TRACE) { + if ((access_status = ap_send_http_trace(r))) + ap_die(access_status, r); + else + ap_finalize_request_protocol(r); + return; + } + } + + if (r->proto_num > HTTP_VERSION(1,0) && ap_table_get(r->subprocess_env, "downgrade-1.0")) { + r->proto_num = HTTP_VERSION(1,0); + } + + /* + * NB: directory_walk() clears the per_dir_config, so we don't inherit + * from location_walk() above + */ + + if ((access_status = directory_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = file_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = location_walk(r))) { + ap_die(access_status, r); + return; + } + + if ((access_status = ap_header_parse(r))) { + ap_die(access_status, r); + return; + } + + switch (ap_satisfies(r)) { + case SATISFY_ALL: + case SATISFY_NOSPEC: + if ((access_status = ap_check_access(r)) != 0) { + decl_die(access_status, "check access", r); + return; + } + if (ap_some_auth_required(r)) { + if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check user. No user file?" + : "perform authentication. AuthType not set!", r); + return; + } + if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check access. No groups file?" + : "perform authentication. AuthType not set!", r); + return; + } + } + break; + case SATISFY_ANY: + if (((access_status = ap_check_access(r)) != 0) || !ap_auth_type(r)) { + if (!ap_some_auth_required(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check access" + : "perform authentication. AuthType not set!", r); + return; + } + if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check user. No user file?" + : "perform authentication. AuthType not set!", r); + return; + } + if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) { + decl_die(access_status, ap_auth_type(r) + ? "check access. No groups file?" + : "perform authentication. AuthType not set!", r); + return; + } + } + break; + } + + if (! (r->proxyreq + && r->parsed_uri.scheme != NULL + && strcmp(r->parsed_uri.scheme, "http") == 0) ) { + if ((access_status = ap_find_types(r)) != 0) { + decl_die(access_status, "find types", r); + return; + } + } + + if ((access_status = ap_run_fixups(r)) != 0) { + ap_die(access_status, r); + return; + } + + if ((access_status = ap_invoke_handler(r)) != 0) { + ap_die(access_status, r); + return; + } + + /* Take care of little things that need to happen when we're done */ + ap_finalize_request_protocol(r); +} + +void ap_process_request(request_rec *r) +{ + int old_stat; + + if (ap_extended_status) + ap_time_process_request(r->connection->child_num, START_PREQUEST); + + process_request_internal(r); + + old_stat = ap_update_child_status(r->connection->child_num, + SERVER_BUSY_LOG, r); + + /* + * We want to flush the last packet if this isn't a pipelining connection + * *before* we start into logging. Suppose that the logging causes a DNS + * lookup to occur, which may have a high latency. If we hold off on + * this packet, then it'll appear like the link is stalled when really + * it's the application that's stalled. + */ + ap_bhalfduplex(r->connection->client); + ap_log_transaction(r); + + (void) ap_update_child_status(r->connection->child_num, old_stat, r); + if (ap_extended_status) + ap_time_process_request(r->connection->child_num, STOP_PREQUEST); +} + +static table *rename_original_env(pool *p, table *t) +{ + array_header *env_arr = ap_table_elts(t); + table_entry *elts = (table_entry *) env_arr->elts; + table *new = ap_make_table(p, env_arr->nalloc); + int i; + + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) + continue; + ap_table_setn(new, ap_pstrcat(p, "REDIRECT_", elts[i].key, NULL), + elts[i].val); + } + + return new; +} + +static request_rec *internal_internal_redirect(const char *new_uri, request_rec *r) +{ + int access_status; + request_rec *new = (request_rec *) ap_pcalloc(r->pool, sizeof(request_rec)); + + new->connection = r->connection; + new->server = r->server; + new->pool = r->pool; + + /* + * A whole lot of this really ought to be shared with http_protocol.c... + * another missing cleanup. It's particularly inappropriate to be + * setting header_only, etc., here. + */ + + new->method = r->method; + new->method_number = r->method_number; + ap_parse_uri(new, new_uri); + new->request_config = ap_create_request_config(r->pool); + new->per_dir_config = r->server->lookup_defaults; + + new->prev = r; + r->next = new; + + /* Inherit the rest of the protocol info... */ + + new->the_request = r->the_request; + + new->allowed = r->allowed; + + new->status = r->status; + new->assbackwards = r->assbackwards; + new->header_only = r->header_only; + new->protocol = r->protocol; + new->proto_num = r->proto_num; + new->hostname = r->hostname; + new->request_time = r->request_time; + new->main = r->main; + + new->headers_in = r->headers_in; + new->headers_out = ap_make_table(r->pool, 12); + new->err_headers_out = r->err_headers_out; + new->subprocess_env = rename_original_env(r->pool, r->subprocess_env); + new->notes = ap_make_table(r->pool, 5); + + new->htaccess = r->htaccess; + new->no_cache = r->no_cache; + new->expecting_100 = r->expecting_100; + new->no_local_copy = r->no_local_copy; + new->read_length = r->read_length; /* We can only read it once */ + new->vlist_validator = r->vlist_validator; + + ap_table_setn(new->subprocess_env, "REDIRECT_STATUS", + ap_psprintf(r->pool, "%d", r->status)); + + /* + * XXX: hmm. This is because mod_setenvif and mod_unique_id really need + * to do their thing on internal redirects as well. Perhaps this is a + * misnamed function. + */ + if ((access_status = ap_run_post_read_request(new))) { + ap_die(access_status, new); + return NULL; + } + + return new; +} + +API_EXPORT(void) ap_internal_redirect(const char *new_uri, request_rec *r) +{ + request_rec *new = internal_internal_redirect(new_uri, r); + process_request_internal(new); +} + +/* This function is designed for things like actions or CGI scripts, when + * using AddHandler, and you want to preserve the content type across + * an internal redirect. + */ +API_EXPORT(void) ap_internal_redirect_handler(const char *new_uri, request_rec *r) +{ + request_rec *new = internal_internal_redirect(new_uri, r); + if (r->handler) + new->content_type = r->content_type; + process_request_internal(new); +} + +/* + * Is it the initial main request, which we only get *once* per HTTP request? + */ +API_EXPORT(int) ap_is_initial_req(request_rec *r) +{ + return + (r->main == NULL) /* otherwise, this is a sub-request */ + && + (r->prev == NULL); /* otherwise, this is an internal redirect */ +} + +/* + * Function to set the r->mtime field to the specified value if it's later + * than what's already there. + */ +API_EXPORT(time_t) ap_update_mtime(request_rec *r, time_t dependency_mtime) +{ + if (r->mtime < dependency_mtime) { + r->mtime = dependency_mtime; + } + return r->mtime; +} diff --git a/modules/loggers/.indent.pro b/modules/loggers/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/loggers/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/mappers/.indent.pro b/modules/mappers/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/mappers/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/mappers/mod_rewrite.dsp b/modules/mappers/mod_rewrite.dsp new file mode 100644 index 0000000000..4a0ffd1bf6 --- /dev/null +++ b/modules/mappers/mod_rewrite.dsp @@ -0,0 +1,117 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleRewrite" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleRewrite - 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 "ApacheModuleRewrite.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 "ApacheModuleRewrite.mak"\ + CFG="ApacheModuleRewrite - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleRewrite - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleRewrite - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleRewrite - 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 ".\ApacheModuleRewriteR" +# PROP Intermediate_Dir ".\ApacheModuleRewriteR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleRewrite - 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 ".\ApacheModuleRewriteD" +# PROP Intermediate_Dir ".\ApacheModuleRewriteD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleRewrite - Win32 Release" +# Name "ApacheModuleRewrite - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_rewrite.c +# End Source File +# Begin Source File + +SOURCE=.\passwd.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/mappers/mod_speling.dsp b/modules/mappers/mod_speling.dsp new file mode 100644 index 0000000000..499a72f3f7 --- /dev/null +++ b/modules/mappers/mod_speling.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleSpeling" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleSpeling - 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 "ApacheModuleSpeling.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 "ApacheModuleSpeling.mak"\ + CFG="ApacheModuleSpeling - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleSpeling - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleSpeling - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleSpeling - 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 ".\ApacheModuleSpelingR" +# PROP Intermediate_Dir ".\ApacheModuleSpelingR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleSpeling - 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 ".\ApacheModuleSpelingD" +# PROP Intermediate_Dir ".\ApacheModuleSpelingD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleSpeling - Win32 Release" +# Name "ApacheModuleSpeling - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_speling.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/metadata/.indent.pro b/modules/metadata/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/metadata/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/modules/metadata/mod_cern_meta.dsp b/modules/metadata/mod_cern_meta.dsp new file mode 100644 index 0000000000..3564c0e2ef --- /dev/null +++ b/modules/metadata/mod_cern_meta.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleCERNMeta" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleCERNMeta - 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 "ApacheModuleCERNMeta.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 "ApacheModuleCERNMeta.mak"\ + CFG="ApacheModuleCERNMeta - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleCERNMeta - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleCERNMeta - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleCERNMeta - 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 ".\ApacheModuleCERNMetaR" +# PROP Intermediate_Dir ".\ApacheModuleCERNMetaR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleCERNMeta - 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 ".\ApacheModuleCERNMetaD" +# PROP Intermediate_Dir ".\ApacheModuleCERNMetaD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleCERNMeta - Win32 Release" +# Name "ApacheModuleCERNMeta - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_cern_meta.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/metadata/mod_expires.dsp b/modules/metadata/mod_expires.dsp new file mode 100644 index 0000000000..0b2fe902bb --- /dev/null +++ b/modules/metadata/mod_expires.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleExpires" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleExpires - 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 "ApacheModuleExpires.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 "ApacheModuleExpires.mak"\ + CFG="ApacheModuleExpires - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleExpires - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleExpires - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleExpires - 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 ".\ApacheModuleExpiresR" +# PROP Intermediate_Dir ".\ApacheModuleExpiresR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleExpires - 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 ".\ApacheModuleExpiresD" +# PROP Intermediate_Dir ".\ApacheModuleExpiresD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleExpires - Win32 Release" +# Name "ApacheModuleExpires - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_expires.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/metadata/mod_headers.dsp b/modules/metadata/mod_headers.dsp new file mode 100644 index 0000000000..1dc76027b3 --- /dev/null +++ b/modules/metadata/mod_headers.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleHeaders" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleHeaders - 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 "ApacheModuleHeaders.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 "ApacheModuleHeaders.mak"\ + CFG="ApacheModuleHeaders - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleHeaders - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleHeaders - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleHeaders - 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 ".\ApacheModuleHeadersR" +# PROP Intermediate_Dir ".\ApacheModuleHeadersR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleHeaders - 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 ".\ApacheModuleHeadersD" +# PROP Intermediate_Dir ".\ApacheModuleHeadersD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleHeaders - Win32 Release" +# Name "ApacheModuleHeaders - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_headers.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/metadata/mod_usertrack.dsp b/modules/metadata/mod_usertrack.dsp new file mode 100644 index 0000000000..e3aee558ce --- /dev/null +++ b/modules/metadata/mod_usertrack.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="ApacheModuleUserTrack" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ApacheModuleUserTrack - 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 "ApacheModuleUserTrack.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 "ApacheModuleUserTrack.mak"\ + CFG="ApacheModuleUserTrack - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheModuleUserTrack - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ApacheModuleUserTrack - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheModuleUserTrack - 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 ".\ApacheModuleUserTrackR" +# PROP Intermediate_Dir ".\ApacheModuleUserTrackR" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheModuleUserTrack - 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 ".\ApacheModuleUserTrackD" +# PROP Intermediate_Dir ".\ApacheModuleUserTrackD" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "ApacheModuleUserTrack - Win32 Release" +# Name "ApacheModuleUserTrack - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=..\..\modules\standard\mod_usertrack.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/ssl/.indent.pro b/modules/ssl/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/modules/ssl/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/os/.indent.pro b/os/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/os/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/os/bs2000/.cvsignore b/os/bs2000/.cvsignore new file mode 100644 index 0000000000..f3c7a7c5da --- /dev/null +++ b/os/bs2000/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/os/bs2000/bs2login.c b/os/bs2000/bs2login.c new file mode 100644 index 0000000000..32eb1aecb0 --- /dev/null +++ b/os/bs2000/bs2login.c @@ -0,0 +1,297 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifdef _OSD_POSIX +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include +#include + +#define ACCT_LEN 8 +#define USER_LEN 8 + +static const char *bs2000_account = NULL; +typedef enum +{ + bs2_unknown, /* not initialized yet. */ + bs2_noFORK, /* no fork() because -X flag was specified */ + bs2_FORK, /* only fork() because uid != 0 */ + bs2_FORK_RINI, /* prior to A17, regular fork() and _rini() was used. */ + bs2_RFORK_RINI, /* for A17, use of _rfork() and _rini() was required */ + bs2_UFORK /* As of A18, the new ufork() is used. */ +} bs2_ForkType; + +static bs2_ForkType forktype = bs2_unknown; + + +static void ap_pad(char *dest, size_t size, char ch) +{ + int i = strlen(dest); /* Leave space for trailing '\0' */ + + while (i < size-1) + dest[i++] = ch; + + dest[size-1] = '\0'; /* Guarantee for trailing '\0' */ +} + +static void ap_str_toupper(char *str) +{ + while (*str) { + *str = ap_toupper(*str); + ++str; + } +} + +/* Determine the method for forking off a child in such a way as to + * set both the POSIX and BS2000 user id's to the unprivileged user. + */ +static bs2_ForkType os_forktype(void) +{ + struct utsname os_version; + + /* have we checked the OS version before? If yes return the previous + * result - the OS release isn't going to change suddenly! + */ + if (forktype != bs2_unknown) { + return forktype; + } + + /* If the user is unprivileged, use the normal fork() only. */ + if (getuid() != 0) { + return forktype = bs2_FORK; + } + + if (uname(&os_version) < 0) + { + ap_log_error(APLOG_MARK, APLOG_ALERT, NULL, + "uname() failed - aborting."); + exit(APEXIT_CHILDFATAL); + } + + /* + * Old BS2000/OSD versions (before XPG4 SPEC1170) don't work with Apache. + * Anyway, simply return a fork(). + */ + if (strcmp(os_version.release, "01.0A") == 0 || + strcmp(os_version.release, "02.0A") == 0 || + strcmp(os_version.release, "02.1A") == 0) + { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, + "Error: unsupported OS version. " + "You may encounter problems."); + forktype = bs2_FORK; + } + + /* The following versions are special: + * OS versions before A17 needs regular fork() and _rini(). + * A17 requires _rfork() and _rini(), + * and later versions need ufork(). + */ + else if (strcmp(os_version.release, "01.1A") == 0 || + strcmp(os_version.release, "03.0A") == 0 || + strcmp(os_version.release, "03.1A") == 0 || + strcmp(os_version.release, "04.0A") == 0) + { + if (strcmp (os_version.version, "A18") >= 0) + forktype = bs2_UFORK; + + else if (strcmp (os_version.version, "A17") < 0) + forktype = bs2_FORK_RINI; + + else + forktype = bs2_RFORK_RINI; + } + + /* All later OS versions will hopefully use ufork() only ;-) */ + else + forktype = bs2_UFORK; + + return forktype; +} + + + +/* This routine is called by http_core for the BS2000Account directive */ +/* It stores the account name for later use */ +const char *os_set_account(pool *p, const char *account) +{ + char account_temp[ACCT_LEN+1]; + + ap_cpystrn(account_temp, account, sizeof account_temp); + + /* Make account all upper case */ + ap_str_toupper(account_temp); + + /* Pad to length 8 */ + ap_pad(account_temp, sizeof account_temp, ' '); + + bs2000_account = ap_pstrdup(p, account_temp); + return NULL; +} + +/* This routine complements the setuid() call: it causes the BS2000 job + * environment to be switched to the target user's user id. + * That is important if CGI scripts try to execute native BS2000 commands. + */ +int os_init_job_environment(server_rec *server, const char *user_name, int one_process) +{ + _rini_struct inittask; + char username[USER_LEN+1]; + int save_errno; + bs2_ForkType type = os_forktype(); + + /* We can be sure that no change to uid==0 is possible because of + * the checks in http_core.c:set_user() + */ + + /* The _rini() function works only after a prior _rfork(). + * In the case of one_process, it would fail. + */ + if (one_process) { + + type = forktype = bs2_noFORK; + + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server, + "The debug mode of Apache should only " + "be started by an unprivileged user!"); + return 0; + } + + /* If no _rini() is required, then return quickly. */ + if (type != bs2_RFORK_RINI && type != bs2_FORK_RINI) + return 0; + + /* An Account is required for _rini() */ + if (bs2000_account == NULL) + { + ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server, + "No BS2000Account configured - cannot switch to User %s", + user_name); + exit(APEXIT_CHILDFATAL); + } + + ap_cpystrn(username, user_name, sizeof username); + + /* Make user name all upper case */ + ap_str_toupper(username); + + /* Pad to length 8 */ + ap_pad(username, sizeof username, ' '); + + inittask.username = username; + inittask.account = bs2000_account; + inittask.processor_name = " "; + + /* Switch to the new logon user (setuid() and setgid() are done later) */ + /* Only the super user can switch identities. */ + if (_rini(&inittask) != 0) { + + ap_log_error(APLOG_MARK, APLOG_ALERT, server, + "_rini: BS2000 auth failed for user \"%s\" acct \"%s\"", + inittask.username, inittask.account); + + exit(APEXIT_CHILDFATAL); + } + + return 0; +} + +/* BS2000 requires a "special" version of fork() before a setuid()/_rini() call */ +pid_t os_fork(const char *user) +{ + pid_t pid; + char username[USER_LEN+1]; + + switch (os_forktype()) { + case bs2_FORK: + case bs2_FORK_RINI: + pid = fork(); + break; + + case bs2_RFORK_RINI: + pid = _rfork(); + break; + + case bs2_UFORK: + ap_cpystrn(username, user, sizeof username); + + /* Make user name all upper case - for some versions of ufork() */ + ap_str_toupper(username); + + pid = ufork(username); + if (pid == -1 && errno == EPERM) { + ap_log_error(APLOG_MARK, APLOG_EMERG, + NULL, "ufork: Possible mis-configuration " + "for user %s - Aborting.", user); + exit(1); + } + break; + + default: + pid = 0; + break; + } + + return pid; +} + +#else /* _OSD_POSIX */ +void bs2login_is_not_here() +{ +} +#endif /* _OSD_POSIX */ diff --git a/os/bs2000/ebcdic.c b/os/bs2000/ebcdic.c new file mode 100644 index 0000000000..0303fc6dbb --- /dev/null +++ b/os/bs2000/ebcdic.c @@ -0,0 +1,252 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + + +#ifdef CHARSET_EBCDIC +#include "ap_config.h" +#include "ebcdic.h" +/* + Initial Port for Apache-1.3 by + +"BS2000 OSD" is a POSIX on a main frame. It is made by Siemens AG, Germany. +Within the POSIX subsystem, the same character set was chosen as in +"native BS2000", namely EBCDIC. + +EBCDIC Table. (Yes, in EBCDIC, the letters 'a'..'z' are not contiguous!) +This table is bijective, i.e. there are no ambigous or duplicate characters +00 00 01 02 03 85 09 86 7f 87 8d 8e 0b 0c 0d 0e 0f *................* +10 10 11 12 13 8f 0a 08 97 18 19 9c 9d 1c 1d 1e 1f *................* +20 80 81 82 83 84 92 17 1b 88 89 8a 8b 8c 05 06 07 *................* +30 90 91 16 93 94 95 96 04 98 99 9a 9b 14 15 9e 1a *................* +40 20 a0 e2 e4 e0 e1 e3 e5 e7 f1 60 2e 3c 28 2b 7c * .........`.<(+|* +50 26 e9 ea eb e8 ed ee ef ec df 21 24 2a 29 3b 9f *&.........!$*);.* +60 2d 2f c2 c4 c0 c1 c3 c5 c7 d1 5e 2c 25 5f 3e 3f *-/........^,%_>?* +70 f8 c9 ca cb c8 cd ce cf cc a8 3a 23 40 27 3d 22 *..........:#@'="* +80 d8 61 62 63 64 65 66 67 68 69 ab bb f0 fd fe b1 *.abcdefghi......* +90 b0 6a 6b 6c 6d 6e 6f 70 71 72 aa ba e6 b8 c6 a4 *.jklmnopqr......* +a0 b5 af 73 74 75 76 77 78 79 7a a1 bf d0 dd de ae *..stuvwxyz......* +b0 a2 a3 a5 b7 a9 a7 b6 bc bd be ac 5b 5c 5d b4 d7 *...........[\]..* +c0 f9 41 42 43 44 45 46 47 48 49 ad f4 f6 f2 f3 f5 *.ABCDEFGHI......* +d0 a6 4a 4b 4c 4d 4e 4f 50 51 52 b9 fb fc db fa ff *.JKLMNOPQR......* +e0 d9 f7 53 54 55 56 57 58 59 5a b2 d4 d6 d2 d3 d5 *..STUVWXYZ......* +f0 30 31 32 33 34 35 36 37 38 39 b3 7b dc 7d da 7e *0123456789.{.}.~* +*/ + +/* The bijective ebcdic-to-ascii table: */ +const unsigned char os_toascii_strictly[256] = { +/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, + 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/ +/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/ +/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/ +/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/ +/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, + 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/ +/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/ +/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, + 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/ +/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, + 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/ +/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/ +/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/ +/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/ +/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, + 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/ +/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/ +/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/ +/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/ +/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~*/ +}; + +/* This table is (almost) identical to the previous one. The only difference + * is the fact that it maps every EBCDIC *except 0x0A* to its ASCII + * equivalent. The reason for this table is simple: Throughout the + * server, protocol strings are used in the form + * "Content-Type: text/plain\015\012". Now all the characters in the string + * are stored as EBCDIC, only the semantics of \012 is completely + * different from LF (look it up in the table above). \015 happens to be + * mapped to \015 anyway, so there's no special case for it. + * + * In THIS table, EBCDIC-\012 is mapped to ASCII-\012. + * This table is therefore used wherever an EBCDIC to ASCII conversion is + * needed in the server. + */ +/* ebcdic-to-ascii with \012 mapped to ASCII-\n */ +const unsigned char os_toascii[256] = { +/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, + 0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/ +/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/ +/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/ +/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/ +/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, + 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/ +/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/ +/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, + 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/ +/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, + 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/ +/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/ +/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/ +/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/ +/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, + 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/ +/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/ +/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/ +/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/ +/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~*/ +}; + +/* The ascii-to-ebcdic table: +00 00 01 02 03 37 2d 2e 2f 16 05 15 0b 0c 0d 0e 0f *................* +10 10 11 12 13 3c 3d 32 26 18 19 3f 27 1c 1d 1e 1f *................* +20 40 5a 7f 7b 5b 6c 50 7d 4d 5d 5c 4e 6b 60 4b 61 * !"#$%&'()*+,-./ +30 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 7a 5e 4c 7e 6e 6f *0123456789:;<=>?* +40 7c c1 c2 c3 c4 c5 c6 c7 c8 c9 d1 d2 d3 d4 d5 d6 *@ABCDEFGHIJKLMNO* +50 d7 d8 d9 e2 e3 e4 e5 e6 e7 e8 e9 bb bc bd 6a 6d *PQRSTUVWXYZ[\]^_* +60 4a 81 82 83 84 85 86 87 88 89 91 92 93 94 95 96 *`abcdefghijklmno* +70 97 98 99 a2 a3 a4 a5 a6 a7 a8 a9 fb 4f fd ff 07 *pqrstuvwxyz{|}~.* +80 20 21 22 23 24 04 06 08 28 29 2a 2b 2c 09 0a 14 *................* +90 30 31 25 33 34 35 36 17 38 39 3a 3b 1a 1b 3e 5f *................* +a0 41 aa b0 b1 9f b2 d0 b5 79 b4 9a 8a ba ca af a1 *................* +b0 90 8f ea fa be a0 b6 b3 9d da 9b 8b b7 b8 b9 ab *................* +c0 64 65 62 66 63 67 9e 68 74 71 72 73 78 75 76 77 *................* +d0 ac 69 ed ee eb ef ec bf 80 e0 fe dd fc ad ae 59 *................* +e0 44 45 42 46 43 47 9c 48 54 51 52 53 58 55 56 57 *................* +f0 8c 49 cd ce cb cf cc e1 70 c0 de db dc 8d 8e df *................* +*/ +const unsigned char os_toebcdic[256] = { +/*00*/ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, + 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/ +/*10*/ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, + 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/ +/*20*/ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, + 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */ +/*30*/ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /*0123456789:;<=>?*/ +/*40*/ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /*@ABCDEFGHIJKLMNO*/ +/*50*/ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xbb, 0xbc, 0xbd, 0x6a, 0x6d, /*PQRSTUVWXYZ[\]^_*/ +/*60*/ 0x4a, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /*`abcdefghijklmno*/ +/*70*/ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xfb, 0x4f, 0xfd, 0xff, 0x07, /*pqrstuvwxyz{|}~.*/ +/*80*/ 0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /*................*/ +/*90*/ 0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17, + 0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0x5f, /*................*/ +/*a0*/ 0x41, 0xaa, 0xb0, 0xb1, 0x9f, 0xb2, 0xd0, 0xb5, + 0x79, 0xb4, 0x9a, 0x8a, 0xba, 0xca, 0xaf, 0xa1, /*................*/ +/*b0*/ 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, + 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /*................*/ +/*c0*/ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, + 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /*................*/ +/*d0*/ 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, + 0x80, 0xe0, 0xfe, 0xdd, 0xfc, 0xad, 0xae, 0x59, /*................*/ +/*e0*/ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, + 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /*................*/ +/*f0*/ 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, + 0x70, 0xc0, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /*................*/ +}; + +/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset) + * dest and srce may be identical, or separate memory blocks, but + * should not overlap. + */ +void +ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toascii[*srce++]; + } +} +void +ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toascii_strictly[*srce++]; + } +} +void +ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toebcdic[*srce++]; + } +} +#endif /*CHARSET_EBCDIC*/ diff --git a/os/bs2000/ebcdic.h b/os/bs2000/ebcdic.h new file mode 100644 index 0000000000..267f9786df --- /dev/null +++ b/os/bs2000/ebcdic.h @@ -0,0 +1,8 @@ +#include + +extern const unsigned char os_toascii[256]; +extern const unsigned char os_toebcdic[256]; +void ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count); +void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count); +void ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count); + diff --git a/os/bs2000/os-inline.c b/os/bs2000/os-inline.c new file mode 100644 index 0000000000..e58917af36 --- /dev/null +++ b/os/bs2000/os-inline.c @@ -0,0 +1,31 @@ +/* + * This file contains functions which can be inlined if the compiler + * has an "inline" modifier. Because of this, this file is both a + * header file and a compilable module. + * + * Only inlineable functions should be defined in here. They must all + * include the INLINE modifier. + * + * If the compiler supports inline, this file will be #included as a + * header file from os.h to create all the inline function + * definitions. INLINE will be defined to whatever is required on + * function definitions to make them inline declarations. + * + * If the compiler does not support inline, this file will be compiled + * as a normal C file into libos.a (along with os.c). In this case + * INLINE will _not_ be set so we can use this to test if we are + * compiling this source file. + */ + +#ifndef INLINE +#define INLINE + +/* Anything required only when compiling */ +#include "ap_config.h" + +#endif + +INLINE int ap_os_is_path_absolute(const char *file) +{ + return (file && file[0] == '/' ? 1 : 0); +} diff --git a/os/bs2000/os.c b/os/bs2000/os.c new file mode 100644 index 0000000000..c33810a2f0 --- /dev/null +++ b/os/bs2000/os.c @@ -0,0 +1,103 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * This file will include OS specific functions which are not inlineable. + * Any inlineable functions should be defined in os-inline.c instead. + */ + +#include "httpd.h" +#include "http_core.h" +#include "os.h" + +/* Check the Content-Type to decide if conversion is needed */ +int ap_checkconv(struct request_rec *r) +{ + int convert_to_ascii; + const char *type; + + /* To make serving of "raw ASCII text" files easy (they serve faster + * since they don't have to be converted from EBCDIC), a new + * "magic" type prefix was invented: text/x-ascii-{plain,html,...} + * If we detect one of these content types here, we simply correct + * the type to the real text/{plain,html,...} type. Otherwise, we + * set a flag that translation is required later on. + */ + + type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type; + + /* If no content type is set then treat it as (ebcdic) text/plain */ + convert_to_ascii = (type == NULL); + + /* Conversion is applied to text/ files only, if ever. */ + if (type && (strncasecmp(type, "text/", 5) == 0 || + strncasecmp(type, "message/", 8) == 0)) { + if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX, + sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0) + r->content_type = ap_pstrcat(r->pool, "text/", + type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1, + NULL); + else + /* translate EBCDIC to ASCII */ + convert_to_ascii = 1; + } + /* Enable conversion if it's a text document */ + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii); + + return convert_to_ascii; +} + diff --git a/os/bs2000/os.h b/os/bs2000/os.h new file mode 100644 index 0000000000..1ede0813f2 --- /dev/null +++ b/os/bs2000/os.h @@ -0,0 +1,51 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "BS2000" + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#if !defined(INLINE) && defined(USE_GNU_INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern ap_inline + +INLINE int ap_os_is_path_absolute(const char *file); + +#include "os-inline.c" +#endif + +#ifndef INLINE +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *file); +#endif + +/* Other ap_os_ routines not used by this platform */ + +#define ap_os_is_filename_valid(f) (1) +#define ap_os_kill(pid, sig) kill(pid, sig) + +#if !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE) +typedef struct { + char *username; + char *account; + char *processor_name; +} _rini_struct; + +extern int _rini(_rini_struct *); +#endif /* !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE) */ + +/* Sorry if this is ugly, but the include order doesn't allow me + * to use request_rec here... */ +struct request_rec; +extern int ap_checkconv(struct request_rec *r); +extern pid_t os_fork(const char *user); + +#endif /*! APACHE_OS_H*/ diff --git a/os/os2/.cvsignore b/os/os2/.cvsignore new file mode 100644 index 0000000000..f3c7a7c5da --- /dev/null +++ b/os/os2/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/os/os2/os-inline.c b/os/os2/os-inline.c new file mode 100644 index 0000000000..e3ff9f2fb8 --- /dev/null +++ b/os/os2/os-inline.c @@ -0,0 +1,34 @@ +/* + * This file contains functions which can be inlined if the compiler + * has an "inline" modifier. Because of this, this file is both a + * header file and a compilable module. + * + * Only inlineable functions should be defined in here. They must all + * include the INLINE modifier. + * + * If the compiler supports inline, this file will be #included as a + * header file from os.h to create all the inline function + * definitions. INLINE will be defined to whatever is required on + * function definitions to make them inline declarations. + * + * If the compiler does not support inline, this file will be compiled + * as a normal C file into libos.a (along with os.c). In this case + * INLINE will _not_ be set so we can use this to test if we are + * compiling this source file. + */ + +#ifndef INLINE +#define INLINE + +/* Anything required only when compiling */ +#include "ap_config.h" + +#endif + +INLINE int ap_os_is_path_absolute(const char *file) +{ + /* For now, just do the same check that http_request.c and mod_alias.c + * do. + */ + return file && (file[0] == '/' || file[1] == ':'); +} diff --git a/os/os2/os.h b/os/os2/os.h new file mode 100644 index 0000000000..d717f1f8ca --- /dev/null +++ b/os/os2/os.h @@ -0,0 +1,52 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "OS/2" +#define HAVE_CANONICAL_FILENAME +#define HAVE_DRIVE_LETTERS + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#if defined(__GNUC__) && !defined(INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern __inline__ + +INLINE int ap_os_is_path_absolute(const char *file); + +#include "os-inline.c" +#endif + +#ifndef INLINE +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *file); +#endif + +/* FIXME: the following should be implemented on this platform */ +#define ap_os_is_filename_valid(f) (1) + +/* Use a specialized kill() function */ +int ap_os_kill(int pid, int sig); + +/* Maps an OS error code to an error message */ +char *ap_os_error_message(int err); + +/* OS/2 doesn't have symlinks so S_ISLNK is always false */ +#define S_ISLNK(m) 0 + +/* Dynamic loading functions */ +#define ap_os_dso_handle_t unsigned long +void ap_os_dso_init(void); +ap_os_dso_handle_t ap_os_dso_load(const char *); +void ap_os_dso_unload(ap_os_dso_handle_t); +void * ap_os_dso_sym(ap_os_dso_handle_t, const char *); +const char *ap_os_dso_error(void); + +#endif /* ! APACHE_OS_H */ diff --git a/os/os2/util_os2.c b/os/os2/util_os2.c new file mode 100644 index 0000000000..820ea61800 --- /dev/null +++ b/os/os2/util_os2.c @@ -0,0 +1,96 @@ +#define INCL_DOS +#define INCL_DOSERRORS +#include +#include "httpd.h" +#include "http_log.h" + + +API_EXPORT(char *)ap_os_canonical_filename(pool *pPool, const char *szFile) +{ + char buf[HUGE_STRING_LEN]; + char buf2[HUGE_STRING_LEN]; + int rc, len; + char *pos; + +/* Remove trailing slash unless it's a root directory */ + strcpy(buf, szFile); + len = strlen(buf); + + if (len > 3 && buf[len-1] == '/') + buf[--len] = 0; + + rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, HUGE_STRING_LEN); + + if (rc) { + if ( rc != ERROR_INVALID_NAME ) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, "OS/2 error %d for file %s", rc, szFile); + return ap_pstrdup(pPool, ""); + } else { + return ap_pstrdup(pPool, szFile); + } + } + + strlwr(buf2); + +/* Switch backslashes to forward */ + for (pos=buf2; *pos; pos++) + if (*pos == '\\') + *pos = '/'; + + return ap_pstrdup(pPool, buf2); +} + + + +int ap_os_kill(pid_t pid, int sig) +{ +/* SIGTERM's don't work too well in OS/2 (only affects other EMX programs). + CGIs may not be, esp. REXX scripts, so use a native call instead */ + + int rc; + + if ( sig == SIGTERM ) { + rc = DosSendSignalException( pid, XCPT_SIGNAL_BREAK ); + + if ( rc ) { + errno = ESRCH; + rc = -1; + } + } else { + rc = kill(pid, sig); + } + + return rc; +} + + + +char *ap_os_error_message(int err) +{ + static char result[200]; + char message[HUGE_STRING_LEN]; + ULONG len; + char *pos; + int c; + + if (DosGetMessage(NULL, 0, message, HUGE_STRING_LEN, err, "OSO001.MSG", &len) == 0) { + len--; + message[len] = 0; + pos = result; + + if (len >= sizeof(result)) + len = sizeof(result-1); + + for (c=0; c. + * + */ + + +#ifdef CHARSET_EBCDIC +#include "ap_config.h" +#include "ebcdic.h" +/* +This code does basic character mapping for IBM's TPF operating system. +It is a modified version of 's code for +the BS2000 (apache/src/os/bs2000/ebcdic.c). +*/ + +/* +Bijective EBCDIC (character set IBM-1047) to US-ASCII table: +This table is bijective - there are no ambigous or duplicate characters. +*/ +const unsigned char os_toascii_strictly[256] = { + 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f: */ + 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */ + 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f: */ + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f: */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */ + 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f: */ + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */ + 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f: */ + 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* ...........<(+| */ + 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f: */ + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */ + 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f: */ + 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */ + 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f: */ + 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */ + 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f: */ + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */ + 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f: */ + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */ + 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af: */ + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */ + 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf: */ + 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */ + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf: */ + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */ + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df: */ + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */ + 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef: */ + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff: */ + 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f /* 0123456789...... */ +}; + +/* +Server EBCDIC (character set IBM-1047) to US-ASCII table: +This table is a copy of the os_toascii_strictly bijective table above. +The only change is that hex 0a (\012 octal) is mapped to hex 0a +(ASCII's line feed) instead of hex 8e. This is done because throughout +Apache, protocol string definitions hardcode the linefeed as \012 (octal): +"Content-Type: text/plain\015\012". Without this kludge all protocol +string definitions would need to be changed from ...\012 to ...\025. +*/ +const unsigned char os_toascii[256] = { + 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f: */ + 0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */ + 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f: */ + 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f: */ + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */ + 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f: */ + 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */ + 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f: */ + 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* ...........<(+| */ + 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f: */ + 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */ + 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f: */ + 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */ + 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f: */ + 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */ + 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f: */ + 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */ + 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f: */ + 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */ + 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af: */ + 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */ + 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf: */ + 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */ + 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf: */ + 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */ + 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df: */ + 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */ + 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef: */ + 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff: */ + 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f /* 0123456789...... */ +}; + +/* +The US-ASCII to EBCDIC (character set IBM-1047) table: +This table is bijective (no ambiguous or duplicate characters) +*/ +const unsigned char os_toebcdic[256] = { + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, /* 00-0f: */ + 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */ + 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, /* 10-1f: */ + 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */ + 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* 20-2f: */ + 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */ + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 30-3f: */ + 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* 0123456789:;<=>? */ + 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 40-4f: */ + 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* @ABCDEFGHIJKLMNO */ + 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, /* 50-5f: */ + 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, /* PQRSTUVWXYZ[\]^_ */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 60-6f: */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* `abcdefghijklmno */ + 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* 70-7f: */ + 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, /* pqrstuvwxyz{|}~. */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08, /* 80-8f: */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /* ................ */ + 0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17, /* 90-9f: */ + 0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0xff, /* ................ */ + 0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5, /* a0-af: */ + 0xbb, 0xb4, 0x9a, 0x8a, 0xb0, 0xca, 0xaf, 0xbc, /* ................ */ + 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, /* b0-bf: */ + 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /* ................ */ + 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, /* c0-cf: */ + 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /* ................ */ + 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, /* d0-df: */ + 0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xba, 0xae, 0x59, /* ................ */ + 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, /* e0-ef: */ + 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /* ................ */ + 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, /* f0-ff: */ + 0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /* ................ */ +}; + +/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset) + * dest and srce may be identical, or separate memory blocks, but + * should not overlap. + */ +void +ebcdic2ascii(void *dest, const void *srce, size_t count) +{ + unsigned char *udest = dest; + const unsigned char *usrce = srce; + while (count-- != 0) { + *udest++ = os_toascii[*usrce++]; + } +} +void +ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count) +{ + while (count-- != 0) { + *dest++ = os_toascii_strictly[*srce++]; + } +} +void +ascii2ebcdic(void *dest, const void *srce, size_t count) +{ + unsigned char *udest = dest; + const unsigned char *usrce = srce; + + while (count-- != 0) { + *udest++ = os_toebcdic[*usrce++]; + } +} +#endif /*CHARSET_EBCDIC*/ + diff --git a/os/tpf/ebcdic.h b/os/tpf/ebcdic.h new file mode 100644 index 0000000000..e9c4120e1a --- /dev/null +++ b/os/tpf/ebcdic.h @@ -0,0 +1,8 @@ +#include + +extern const unsigned char os_toascii[256]; +extern const unsigned char os_toebcdic[256]; +void ebcdic2ascii(void *dest, const void *srce, size_t count); +void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count); +void ascii2ebcdic(void *dest, const void *srce, size_t count); + diff --git a/os/tpf/os-inline.c b/os/tpf/os-inline.c new file mode 100644 index 0000000000..e58917af36 --- /dev/null +++ b/os/tpf/os-inline.c @@ -0,0 +1,31 @@ +/* + * This file contains functions which can be inlined if the compiler + * has an "inline" modifier. Because of this, this file is both a + * header file and a compilable module. + * + * Only inlineable functions should be defined in here. They must all + * include the INLINE modifier. + * + * If the compiler supports inline, this file will be #included as a + * header file from os.h to create all the inline function + * definitions. INLINE will be defined to whatever is required on + * function definitions to make them inline declarations. + * + * If the compiler does not support inline, this file will be compiled + * as a normal C file into libos.a (along with os.c). In this case + * INLINE will _not_ be set so we can use this to test if we are + * compiling this source file. + */ + +#ifndef INLINE +#define INLINE + +/* Anything required only when compiling */ +#include "ap_config.h" + +#endif + +INLINE int ap_os_is_path_absolute(const char *file) +{ + return (file && file[0] == '/' ? 1 : 0); +} diff --git a/os/tpf/os.c b/os/tpf/os.c new file mode 100644 index 0000000000..e4682324cd --- /dev/null +++ b/os/tpf/os.c @@ -0,0 +1,414 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * This file will include OS specific functions which are not inlineable. + * Any inlineable functions should be defined in os-inline.c instead. + */ + +#include "httpd.h" +#include "http_core.h" +#include "os.h" +#include "scoreboard.h" +#include "http_log.h" +#include "http_conf_globals.h" + +static FILE *sock_fp; + +/* Check the Content-Type to decide if conversion is needed */ +int ap_checkconv(struct request_rec *r) +{ + int convert_to_ascii; + const char *type; + + /* To make serving of "raw ASCII text" files easy (they serve faster + * since they don't have to be converted from EBCDIC), a new + * "magic" type prefix was invented: text/x-ascii-{plain,html,...} + * If we detect one of these content types here, we simply correct + * the type to the real text/{plain,html,...} type. Otherwise, we + * set a flag that translation is required later on. + */ + + type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type; + + /* If no content type is set then treat it as (ebcdic) text/plain */ + convert_to_ascii = (type == NULL); + + /* Conversion is applied to text/ files only, if ever. */ + if (type && (strncasecmp(type, "text/", 5) == 0 || + strncasecmp(type, "message/", 8) == 0)) { + if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX, + sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0){ + r->content_type = ap_pstrcat(r->pool, "text/", + type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1, NULL); + if (r->method_number == M_PUT) + ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0); + } + + else + /* translate EBCDIC to ASCII */ + convert_to_ascii = 1; + } + else{ + if (r->method_number == M_PUT) + ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0); + /* don't translate non-text files to EBCDIC */ + } + /* Enable conversion if it's a text document */ + ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii); + + return convert_to_ascii; +} + +int tpf_select(int maxfds, fd_set *reads, fd_set *writes, fd_set *excepts, struct timeval *tv) +{ +/* We're going to force our way through select. We're only interested reads and TPF allows + 2billion+ socket descriptors for we don't want an fd_set that big. Just assume that maxfds-1 + contains the socket descriptor we're interested in. If it's 0, leave it alone. */ + + int sockets[1]; + int no_reads = 0; + int no_writes = 0; + int no_excepts = 0; + int timeout = 0; + int rv; + + if(maxfds) { + if(tv) + timeout = tv->tv_sec * 1000 + tv->tv_usec; + sockets[0] = maxfds-1; + no_reads++; + } + else + sockets[0] = 0; + + ap_check_signals(); + rv = select(sockets, no_reads, no_writes, no_excepts, timeout); + ap_check_signals(); + + return rv; + +} + +int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen) +{ + int socks[1]; + int rv; + + ap_check_signals(); + socks[0] = sockfd; + rv = select(socks, 1, 0, 0, 1000); + errno = sock_errno(); + if(rv>0) { + ap_check_signals(); + rv = accept(sockfd, peer, paddrlen); + errno = sock_errno(); + } + return rv; +} + +/* the getpass function is not usable on TPF */ +char *getpass(const char* prompt) +{ + errno = EIO; + return((char *)NULL); +} + +#ifndef __PIPE_ +int pipe(int fildes[2]) +{ + errno = ENOSYS; + return(-1); +} +#endif + +/* fork and exec functions are not defined on + TPF due to the implementation of tpf_fork() */ + +pid_t fork(void) +{ + errno = ENOSYS; + return(-1); +} + +int execl(const char *path, const char *arg0, ...) +{ + errno = ENOSYS; + return(-1); +} + +int execle(const char *path, const char *arg0, ...) +{ + errno = ENOSYS; + return(-1); +} + +int execve(const char *path, char *const argv[], char *const envp[]) +{ + errno = ENOSYS; + return(-1); +} + +int execvp(const char *file, char *const argv[]) +{ + errno = ENOSYS; + return(-1); +} + + + +int ap_tpf_spawn_child(pool *p, int (*func) (void *, child_info *), + void *data, enum kill_conditions kill_how, + int *pipe_in, int *pipe_out, int *pipe_err, + int out_fds[], int in_fds[], int err_fds[]) + +{ + + int i, temp_out, temp_in, temp_err, save_errno, pid, result=0; + int fd_flags_out, fd_flags_in, fd_flags_err; + struct tpf_fork_input fork_input; + TPF_FORK_CHILD *cld = (TPF_FORK_CHILD *) data; + array_header *env_arr = ap_table_elts ((array_header *) cld->subprocess_env); + table_entry *elts = (table_entry *) env_arr->elts; + + + + if (func) { + if (result=func(data, NULL)) { + return 0; /* error from child function */ + } + } + + if (pipe_out) { + fd_flags_out = fcntl(out_fds[0], F_GETFD); + fcntl(out_fds[0], F_SETFD, FD_CLOEXEC); + temp_out = dup(STDOUT_FILENO); + fcntl(temp_out, F_SETFD, FD_CLOEXEC); + dup2(out_fds[1], STDOUT_FILENO); + } + + + if (pipe_in) { + fd_flags_in = fcntl(in_fds[1], F_GETFD); + fcntl(in_fds[1], F_SETFD, FD_CLOEXEC); + temp_in = dup(STDIN_FILENO); + fcntl(temp_in, F_SETFD, FD_CLOEXEC); + dup2(in_fds[0], STDIN_FILENO); + } + + if (pipe_err) { + fd_flags_err = fcntl(err_fds[0], F_GETFD); + fcntl(err_fds[0], F_SETFD, FD_CLOEXEC); + temp_err = dup(STDERR_FILENO); + fcntl(temp_err, F_SETFD, FD_CLOEXEC); + dup2(err_fds[1], STDERR_FILENO); + } + + if (cld->subprocess_env) { + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) + continue; + setenv (elts[i].key, elts[i].val, 1); + } + } + + fork_input.program = (const char*) cld->filename; + fork_input.prog_type = cld->prog_type; + fork_input.istream = TPF_FORK_IS_BALANCE; + fork_input.ebw_data_length = 0; + fork_input.ebw_data = NULL; + fork_input.parm_data = NULL; + + + if ((pid = tpf_fork(&fork_input)) < 0) { + save_errno = errno; + if (pipe_out) { + close(out_fds[0]); + } + if (pipe_in) { + close(in_fds[1]); + } + if (pipe_err) { + close(err_fds[0]); + } + errno = save_errno; + pid = 0; + } + + if (cld->subprocess_env) { + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) + continue; + unsetenv (elts[i].key); + } + } + + if (pipe_out) { + close(out_fds[1]); + dup2(temp_out, STDOUT_FILENO); + close(temp_out); + fcntl(out_fds[0], F_SETFD, fd_flags_out); + } + + if (pipe_in) { + close(in_fds[0]); + dup2(temp_in, STDIN_FILENO); + close(temp_in); + fcntl(in_fds[1], F_SETFD, fd_flags_in); + } + + + if (pipe_err) { + close(err_fds[1]); + dup2(temp_err, STDERR_FILENO); + close(temp_err); + fcntl(err_fds[0], F_SETFD, fd_flags_err); + } + + + if (pid) { + + ap_note_subprocess(p, pid, kill_how); + + if (pipe_out) { + *pipe_out = out_fds[0]; + } + if (pipe_in) { + *pipe_in = in_fds[1]; + } + if (pipe_err) { + *pipe_err = err_fds[0]; + } + } + + return pid; + +} + +pid_t os_fork(server_rec *s, int slot) +{ + struct tpf_fork_input fork_input; + APACHE_TPF_INPUT input_parms; + int count; + listen_rec *lr; + + fflush(stdin); + if (dup2(fileno(sock_fp), STDIN_FILENO) == -1) + ap_log_error(APLOG_MARK, APLOG_CRIT, s, + "unable to replace stdin with sock device driver"); + fflush(stdout); + if (dup2(fileno(sock_fp), STDOUT_FILENO) == -1) + ap_log_error(APLOG_MARK, APLOG_CRIT, s, + "unable to replace stdout with sock device driver"); + input_parms.generation = ap_my_generation; +#ifdef SCOREBOARD_FILE + input_parms.scoreboard_fd = scoreboard_fd; +#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */ + input_parms.scoreboard_heap = ap_scoreboard_image; +#endif + + lr = ap_listeners; + count = 0; + do { + input_parms.listeners[count] = lr->fd; + lr = lr->next; + count++; + } while(lr != ap_listeners); + + input_parms.slot = slot; + input_parms.restart_time = ap_restart_time; + fork_input.ebw_data = &input_parms; + fork_input.program = ap_server_argv0; + fork_input.prog_type = TPF_FORK_NAME; + fork_input.istream = TPF_FORK_IS_BALANCE; + fork_input.ebw_data_length = sizeof(input_parms); + fork_input.parm_data = "-x"; + return tpf_fork(&fork_input); +} + +int os_check_server(char *server) { + #ifndef USE_TPF_DAEMON + int rv; + int *current_acn; + if((rv = inetd_getServerStatus(server)) == INETD_SERVER_STATUS_INACTIVE) + return 1; + else { + current_acn = (int *)cinfc_fast(CINFC_CMMACNUM); + if(ecbp2()->ce2acn != *current_acn) + return 1; + } + #endif + return 0; +} + +void os_note_additional_cleanups(pool *p, int sd) { + char sockfilename[50]; + /* write the socket to file so that TPF socket device driver will close socket in case + we happen to abend. */ + sprintf(sockfilename, "/dev/tpf.socket.file/%.8X", sd); + sock_fp = fopen(sockfilename, "r+"); + ap_note_cleanups_for_file(p, sock_fp); /* arrange to close on exec or restart */ + fcntl(sd,F_SETFD,FD_CLOEXEC); +} + +void os_tpf_child(APACHE_TPF_INPUT *input_parms) { + tpf_child = 1; + ap_my_generation = input_parms->generation; + ap_restart_time = input_parms->restart_time; +} + + diff --git a/os/tpf/os.h b/os/tpf/os.h new file mode 100644 index 0000000000..ff65322902 --- /dev/null +++ b/os/tpf/os.h @@ -0,0 +1,108 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "TPF" + +#ifdef errno +#undef errno +#endif + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#include "ap_config.h" + +#if !defined(INLINE) && defined(USE_GNU_INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern ap_inline +#include "os-inline.c" +#endif + +#ifndef INLINE +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *f); +#endif + +/* Other ap_os_ routines not used by this platform */ + +#define ap_os_is_filename_valid(f) (1) +#define ap_os_kill(pid, sig) kill(pid, sig) + +/* Sorry if this is ugly, but the include order doesn't allow me + * to use request_rec here... */ +struct request_rec; +extern int ap_checkconv(struct request_rec *r); + +#include +#ifndef __strings_h + +#define FD_SETSIZE 2048 + +typedef long fd_mask; + +#define NBBY 8 /* number of bits in a byte */ +#define NFDBITS (sizeof(fd_mask) * NBBY) +#define howmany(x, y) (((x)+((y)-1))/(y)) + +typedef struct fd_set { + fd_mask fds_bits [howmany(FD_SETSIZE, NFDBITS)]; +} fd_set; + +#define FD_CLR(n, p)((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p)((p)->fds_bits[(n)/NFDBITS] & (1 <<((n) % NFDBITS))) +#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p))) +#endif + +#ifdef FD_SET +#undef FD_SET +#define FD_SET(n, p) (0) +#endif + +#define RESOURCE_KEY ((void*) 0xC1C2C1C3) + +/* TPF doesn't have, or need, tzset (it is used in mod_expires.c) */ +#define tzset() + +#include +struct apache_input { + INETD_SERVER_INPUT inetd_server; + void *scoreboard_heap; /* scoreboard system heap address */ + int scoreboard_fd; /* scoreboard file descriptor */ + int slot; /* child number */ + int generation; /* server generation number */ + int listeners[10]; + time_t restart_time; +}; + +typedef struct apache_input APACHE_TPF_INPUT; + +typedef struct tpf_fork_child { + char *filename; + enum { FORK_NAME = 1, FORK_FILE = 2 } prog_type; + void *subprocess_env; +}TPF_FORK_CHILD; + +int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen); +extern int tpf_child; + +struct server_rec; +pid_t os_fork(struct server_rec *s, int slot); +int os_check_server(char *server); +char *getpass(const char *prompt); +extern char *ap_server_argv0; +extern int scoreboard_fd; +#include +#ifndef SIGPIPE +#define SIGPIPE 14 +#endif +#ifdef NSIG +#undef NSIG +#endif +#endif /*! APACHE_OS_H*/ diff --git a/os/tpf/samples/linkdll.jcl b/os/tpf/samples/linkdll.jcl new file mode 100644 index 0000000000..6bcd17c6b0 --- /dev/null +++ b/os/tpf/samples/linkdll.jcl @@ -0,0 +1,121 @@ +//APACH JOB MSGLEVEL=(1,1),CLASS=A,MSGCLASS=A +/*ROUTE PRINT XXXXXX.XXXXXX +/*ROUTE PUNCH XXXXXX.XXXXXX +/*NOTIFY XXXXXX.XXXXXX +//CCLE JCLLIB ORDER=(SYS1.CBC.SCBCPRC,SYS1.CEE.SCEEPROC) +//PRELINK EXEC EDCPL,COND.LKED=(0,NE), +// PPARM='OMVS,DLLNAME(pppp)', +// LREGSIZ='2048K', +// LPARM='AMODE=31,RMODE=ANY,LIST,XREF' +//PLKED.SYSLIB DD DISP=SHR,DSN=FSE0000.DEVP.STUB.OB +// DD DISP=SHR,DSN=FSE0000.DEVP.CLIB.OB +// DD DISP=SHR,DSN=ACP.CLIB.RLSE46.WEB +// DD DISP=SHR,DSN=ACP.STUB.RLSE46.WEB +// DD DISP=SHR,DSN=ACP.CLIB.RLSE40 +// DD DISP=SHR,DSN=ACP.STUB.RLSE40 +//PLKED.SYSDEFSD DD DSN=APA0000.DEVP.IMPORTS.DSD(ppppvv),DISP=SHR +//PLKED.DSD DD DSN=APA0000.DEVP.IMPORTS.DSD,DISP=SHR +//PLKED.OBJLIB DD DISP=SHR,DSN=FSE0000.DEVP.TEST.OB +// DD DISP=SHR,DSN=ACP.OBJ.RLSE46.WEB +// DD DISP=SHR,DSN=ACP.OBJ.INTG98.NBS +// DD DISP=SHR,DSN=ACP.MAIN.SYST.OBBSS +// DD DISP=SHR,DSN=ACP.DF.MAIN.SYST.OBBSS +// DD DISP=SHR,DSN=ACP.OBJ.RLSE40.BSS +//PLKED.OBJ1 DD PATH='/usr/local/apache/src/ap/ap_cpystrn.o' +//PLKED.OBJ2 DD PATH='/usr/local/apache/src/ap/ap_execve.o' +//PLKED.OBJ3 DD PATH='/usr/local/apache/src/ap/ap_signal.o' +//PLKED.OBJ4 DD PATH='/usr/local/apache/src/ap/ap_slack.o' +//PLKED.OBJ5 DD PATH='/usr/local/apache/src/ap/ap_snprintf.o' +//PLKED.OBJ6 DD PATH='/usr/local/apache/src/ap/ap_strings.o' +//PLKED.OBJ7 DD PATH='/usr/local/apache/src/os/tpf/ebcdic.o' +//PLKED.OBJ8 DD PATH='/usr/local/apache/src/os/tpf/os.o' +//PLKED.OBJ9 DD PATH='/usr/local/apache/src/os/tpf/os-inline.o' +//PLKED.OBJ10 DD PATH='/usr/local/apache/src/regex/regcomp.o' +//PLKED.OBJ11 DD PATH='/usr/local/apache/src/regex/regerror.o' +//PLKED.OBJ12 DD PATH='/usr/local/apache/src/regex/regexec.o' +//PLKED.OBJ13 DD PATH='/usr/local/apache/src/regex/regfree.o' +//PLKED.OBJ14 DD PATH='/usr/local/apache/src/main/alloc.o' +//PLKED.OBJ15 DD PATH='/usr/local/apache/src/main/buff.o' +//PLKED.OBJ16 DD PATH='/usr/local/apache/src/main/fnmatch.o' +//PLKED.OBJ17 DD PATH='/usr/local/apache/src/main/http_config.o' +//PLKED.OBJ18 DD PATH='/usr/local/apache/src/main/http_core.o' +//PLKED.OBJ19 DD PATH='/usr/local/apache/src/main/http_log.o' +//PLKED.OBJ20 DD PATH='/usr/local/apache/src/main/http_main.o' +//PLKED.OBJ21 DD PATH='/usr/local/apache/src/main/http_protocol.o' +//PLKED.OBJ22 DD PATH='/usr/local/apache/src/main/http_request.o' +//PLKED.OBJ23 DD PATH='/usr/local/apache/src/main/http_vhost.o' +//PLKED.OBJ24 DD PATH='/usr/local/apache/src/main/md5c.o' +//PLKED.OBJ25 DD PATH='/usr/local/apache/src/main/rfc1413.o' +//PLKED.OBJ26 DD PATH='/usr/local/apache/src/main/util.o' +//PLKED.OBJ27 DD PATH='/usr/local/apache/src/main/util_date.o' +//PLKED.OBJ28 DD PATH='/usr/local/apache/src/main/util_md5.o' +//PLKED.OBJ29 DD PATH='/usr/local/apache/src/main/util_script.o' +//PLKED.OBJ30 DD PATH='/usr/local/apache/src/main/util_uri.o' +//PLKED.OBJ31 DD PATH='/usr/local/apache/src/modules.o' +//PLKED.OBJ32 DD PATH='/usr/local/apache/src/buildmark.o' +//PLKED.OBJ33 DD PATH='/usr/local/apache/src/modules/standard/mod_auto\ +// index.o' +//PLKED.OBJ34 DD PATH='/usr/local/apache/src/modules/standard/mod_dir.\ +// o' +//PLKED.OBJ35 DD PATH='/usr/local/apache/src/modules/standard/mod_mime\ +// .o' +//PLKED.OBJ36 DD PATH='/usr/local/apache/src/modules/standard/mod_sete\ +// nvif.o' +//PLKED.OBJ37 DD PATH='/usr/local/apache/src/modules/standard/mod_alia\ +// s.o' +//PLKED.OBJ38 DD PATH='/usr/local/apache/src/modules/standard/mod_acce\ +// ss.o' +//PLKED.OBJ39 DD PATH='/usr/local/apache/src/modules/standard/mod_user\ +// dir.o' +//PLKED.OBJ40 DD PATH='/usr/local/apache/src/modules/standard/mod_spel\ +// ing.o' +//PLKED.OBJ41 DD PATH='/usr/local/apache/src/modules/standard/mod_nego\ +// tiation.o' +//PLKED.SYSIN DD * + ORDER @@DLMHDR + INCLUDE OBJLIB(CSTRTD40) + INCLUDE OBJ1 + INCLUDE OBJ2 + INCLUDE OBJ3 + INCLUDE OBJ4 + INCLUDE OBJ5 + INCLUDE OBJ6 + INCLUDE OBJ7 + INCLUDE OBJ8 + INCLUDE OBJ9 + INCLUDE OBJ10 + INCLUDE OBJ11 + INCLUDE OBJ12 + INCLUDE OBJ13 + INCLUDE OBJ14 + INCLUDE OBJ15 + INCLUDE OBJ16 + INCLUDE OBJ17 + INCLUDE OBJ18 + INCLUDE OBJ19 + INCLUDE OBJ20 + INCLUDE OBJ21 + INCLUDE OBJ22 + INCLUDE OBJ23 + INCLUDE OBJ24 + INCLUDE OBJ25 + INCLUDE OBJ26 + INCLUDE OBJ27 + INCLUDE OBJ28 + INCLUDE OBJ29 + INCLUDE OBJ30 + INCLUDE OBJ31 + INCLUDE OBJ32 + INCLUDE OBJ33 + INCLUDE OBJ34 + INCLUDE OBJ35 + INCLUDE OBJ36 + INCLUDE OBJ37 + INCLUDE OBJ38 + INCLUDE OBJ39 + INCLUDE OBJ40 + INCLUDE OBJ41 +/* +//*** WARNING *** NEVER change .LK to .OB in SYSLMOD!!! +//LKED.SYSLMOD DD DISP=OLD,DSN=xxxxxx.xxxx(ppppvv) +// diff --git a/os/tpf/samples/loadset.jcl b/os/tpf/samples/loadset.jcl new file mode 100644 index 0000000000..c0134d4cb8 --- /dev/null +++ b/os/tpf/samples/loadset.jcl @@ -0,0 +1,58 @@ +//OLDRWEB JOB MSGLEVEL=1,CLASS=A,MSGCLASS=S +//JOBCAT DD DSN=ICFCAT.ESAWK2,DISP=SHR +/*ROUTE PRINT xxxxxx.xxxxxxx +/*ROUTE PUNCH xxxxxx.xxxxxxx +//TLDR EXEC PGM=TPFLDRCA,REGION=8M, +// PARM='OLDR,SYS=ACP,CLMSIZE=8000000' +//STEPLIB DD DSN=ACP.LINK.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.LINK.RLSE40.BSS,DISP=SHR +// DD DSN=VIS0000.DEVP.TEST.LK,DISP=SHR +// DD DSN=SYS1.CEE.SCEERUN,DISP=SHR +//SALTB DD DSN=ACP.SALTBL.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.SALTBL.INTG46.WEB,DISP=SHR +//OBJLIB DD DSN=FSE0000.DEVP.TEST.OB,DISP=SHR +// DD DSN=APA0000.DEVP.TEST.OB,DISP=SHR +// DD DSN=ACP.DRVE.TEST.OB,DISP=SHR +// DD DSN=ACP.OBJ.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.OBJ.INTG36.DRV,DISP=SHR +// DD DSN=ACP.OBJ.INTG46.WEB,DISP=SHR +// DD DSN=ACP.OBJ.INTG40.BSS,DISP=SHR +//LOADMOD DD DSN=FSE0000.DEVP.TEST.LK,DISP=SHR +// DD DSN=APA0000.DEVP.TEST.LK,DISP=SHR +// DD DSN=CWEISS.LINK,DISP=SHR +// DD DSN=ACP.DRVE.TEST.LK,DISP=SHR +// DD DSN=ACP.LINK.RLSE46.WEB,DISP=SHR +// DD DSN=ACP.LINK.INTG98.NBS,DISP=SHR +// DD DSN=ACP.LINK.INTG46.WEB,DISP=SHR +// DD DSN=ACP.LINK.INTG36.DRV,DISP=SHR +// DD DSN=ACP.LINK.INTG40.BSS,DISP=SHR +//LOADSUM DD DSN=&&LOADSUM,DISP=(NEW,PASS),UNIT=SYSDA, +// LRECL=133,SPACE=(TRK,(10,10)),RECFM=FBA +//CPRTEMP DD UNIT=SYSDA, +// DSN=&&CPRTEMP,SPACE=(TRK,(100,20)), +// DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095), +// DISP=(NEW,DELETE) +//PROGTEMP DD UNIT=SYSDA, +// DSN=&&PRTEMP,SPACE=(TRK,(100,20)), +// DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095), +// DISP=(NEW,DELETE) +//OUTPUT DD DSN=&&VRDROUT,DISP=(NEW,PASS),UNIT=SYSDA, +// DCB=(RECFM=F,BLKSIZE=4095,LRECL=4095) +//SYSUDUMP DD DUMMY +//SYSABEND DD DUMMY +//SYSOUT DD SYSOUT=A +//SYSPRINT DD SYSOUT=A +//PRINTER DD SYSOUT=A +//CEEDUMP DD SYSOUT=A +//SYSIN DD * +SYSID=BSS +PATVERS=NONE +SALVERS=40 +LOADER LOADSET lllllll +LOADER CALL PROG ppppvv +/* +//TRANSMIT EXEC PGM=IKJEFT01, +// PARM='TRANSMIT xxxxxx.xxxxxx DDNAME(SYSTSIN) NOLOG NONOTIFY SEQ' +//SYSTSIN DD UNIT=SYSDA, +// DSN=&&VRDROUT,DISP=(OLD,DELETE) +//SYSTSPRT DD DUMMY diff --git a/os/unix/.cvsignore b/os/unix/.cvsignore new file mode 100644 index 0000000000..f3c7a7c5da --- /dev/null +++ b/os/unix/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/os/unix/os-inline.c b/os/unix/os-inline.c new file mode 100644 index 0000000000..54ff49a639 --- /dev/null +++ b/os/unix/os-inline.c @@ -0,0 +1,31 @@ +/* + * This file contains functions which can be inlined if the compiler + * has an "inline" modifier. Because of this, this file is both a + * header file and a compilable module. + * + * Only inlineable functions should be defined in here. They must all + * include the INLINE modifier. + * + * If the compiler supports inline, this file will be #included as a + * header file from os.h to create all the inline function + * definitions. INLINE will be defined to whatever is required on + * function definitions to make them inline declarations. + * + * If the compiler does not support inline, this file will be compiled + * as a normal C file into libos.a (along with os.c). In this case + * INLINE will _not_ be set so we can use this to test if we are + * compiling this source file. + */ + +#ifndef INLINE +#define INLINE + +/* Anything required only when compiling */ +#include "ap_config.h" + +#endif + +INLINE int ap_os_is_path_absolute(const char *file) +{ + return file[0] == '/'; +} diff --git a/os/unix/os.h b/os/unix/os.h new file mode 100644 index 0000000000..6689dfae5c --- /dev/null +++ b/os/unix/os.h @@ -0,0 +1,147 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#include "ap_config.h" + +#ifndef PLATFORM +#define PLATFORM "Unix" +#endif + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c or os-inline.c + */ + +#if !defined(INLINE) && defined(USE_GNU_INLINE) +/* Compiler supports inline, so include the inlineable functions as + * part of the header + */ +#define INLINE extern ap_inline + +INLINE int ap_os_is_path_absolute(const char *file); + +#include "os-inline.c" + +#else + +/* Compiler does not support inline, so prototype the inlineable functions + * as normal + */ +extern int ap_os_is_path_absolute(const char *file); +#endif + +/* Other ap_os_ routines not used by this platform */ + +#define ap_os_is_filename_valid(f) (1) +#define ap_os_kill(pid, sig) kill(pid, sig) + +/* + * Abstraction layer for loading + * Apache modules under run-time via + * dynamic shared object (DSO) mechanism + */ + +#ifdef HAVE_DL_H +#include +#endif + +/* + * Do not use native AIX DSO support + */ +#ifdef AIX +#undef HAVE_DLFCN_H +#endif + +#ifdef HAVE_DLFCN_H +#include +#else +void *dlopen(const char *, int); +int dlclose(void *); +void *dlsym(void *, const char *); +const char *dlerror(void); +#endif + +/* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY. + * The below define is a lie since we are really doing RTLD_LAZY since the + * system doesn't support RTLD_NOW. + */ +#ifndef RTLD_NOW +#define RTLD_NOW 1 +#endif + +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif + +#if (defined(__FreeBSD__) ||\ + defined(__OpenBSD__) ||\ + defined(__NetBSD__) ) && !defined(__ELF__) +#define DLSYM_NEEDS_UNDERSCORE +#endif + +#define ap_os_dso_handle_t void * +void ap_os_dso_init(void); +void * ap_os_dso_load(const char *); +void ap_os_dso_unload(void *); +void * ap_os_dso_sym(void *, const char *); +const char *ap_os_dso_error(void); + +#endif /* !APACHE_OS_H */ diff --git a/os/win32/.cvsignore b/os/win32/.cvsignore new file mode 100644 index 0000000000..17564450e9 --- /dev/null +++ b/os/win32/.cvsignore @@ -0,0 +1,27 @@ +*.mdp +*.ncb +*.opt +*.plg +*.dsw +ApacheModuleAuthAnonD +ApacheModuleAuthAnonR +ApacheModuleCERNMetaD +ApacheModuleCERNMetaR +ApacheModuleDigestD +ApacheModuleDigestR +ApacheModuleExpiresD +ApacheModuleExpiresR +ApacheModuleHeadersD +ApacheModuleHeadersR +ApacheModuleInfoD +ApacheModuleInfoR +ApacheModuleRewriteD +ApacheModuleRewriteR +ApacheModuleSpelingD +ApacheModuleSpelingR +ApacheModuleStatusD +ApacheModuleStatusR +ApacheModuleUserTrackD +ApacheModuleUserTrackR +ApacheOSR +ApacheOSD diff --git a/os/win32/MakeModuleMak.cpp b/os/win32/MakeModuleMak.cpp new file mode 100644 index 0000000000..9ab9f27221 --- /dev/null +++ b/os/win32/MakeModuleMak.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +void MakeMake(const char *szModule,const char *szSource) + { + ifstream ifs("Module.mak.tmpl",ios::nocreate); + assert(ifs.good()); + + char buf[1024]; + sprintf(buf,"%s.mak",szModule); + ofstream ofs(buf,ios::trunc); + for( ; ; ) + { + ifs.getline(buf,sizeof buf); + if(ifs.eof()) + break; + for(char *s=buf ; *s ; ) + { + char *p=strchr(s,'%'); + if(!p) + { + ofs << s << '\n'; + break; + } + if(!strncmp(p,"%Module%",8)) + { + ofs.write(s,p-s); + ofs << szModule; + s=p+8; + } + else if(!strncmp(p,"%Source%",8)) + { + ofs.write(s,p-s); + ofs << szSource; + s=p+8; + } + else + { + ofs.write(s,p-s+1); + s=p+1; + } + } + } + } + +void main(int argc,char **argv) + { + if(argc < 2 || (argc%2) != 1) + { + cerr << argv[0] << " [ ]+\n"; + exit(1); + } + for(int n=1 ; n < argc ; n+=2) + MakeMake(argv[n],argv[n+1]); + } + diff --git a/os/win32/Module.mak.tmpl b/os/win32/Module.mak.tmpl new file mode 100644 index 0000000000..162456f064 --- /dev/null +++ b/os/win32/Module.mak.tmpl @@ -0,0 +1,230 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +!IF "$(CFG)" == "" +CFG=%Module% - Win32 Debug +!MESSAGE No configuration specified. Defaulting to %Module% - Win32\ + Debug. +!ENDIF + +!IF "$(CFG)" != "%Module% - Win32 Release" && "$(CFG)" !=\ + "%Module% - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "%Module%.mak"\ + CFG="%Module% - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "%Module% - Win32 Release" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE "%Module% - Win32 Debug" (based on\ + "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "%Module% - Win32 Debug" +MTL=mktyplib.exe +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "%Module% - 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 "%Module%R" +# PROP Intermediate_Dir "%Module%R" +# PROP Target_Dir "" +OUTDIR=.\%Module%R +INTDIR=.\%Module%R + +ALL : "$(OUTDIR)\%Module%.dll" + +CLEAN : + -@erase "$(INTDIR)\%Source%.obj" + -@erase "$(OUTDIR)\%Module%.dll" + -@erase "$(OUTDIR)\%Module%.exp" + -@erase "$(OUTDIR)\%Module%.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\regex" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\regex" /D "WIN32" /D "NDEBUG" /D\ + "_WINDOWS" /Fp"$(INTDIR)/%Module%.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\%Module%R/ +CPP_SBRS=.\. +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/%Module%.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +LINK32_FLAGS=..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib\ + winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\ + uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll\ + /incremental:no /pdb:"$(OUTDIR)/%Module%.pdb" /machine:I386\ + /out:"$(OUTDIR)/%Module%.dll"\ + /implib:"$(OUTDIR)/%Module%.lib" +LINK32_OBJS= \ + "$(INTDIR)\%Source%.obj" + +"$(OUTDIR)\%Module%.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "%Module% - 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 "%Module%D" +# PROP Intermediate_Dir "%Module%D" +# PROP Target_Dir "" +OUTDIR=.\%Module%D +INTDIR=.\%Module%D + +ALL : "$(OUTDIR)\%Module%.dll" + +CLEAN : + -@erase "$(INTDIR)\%Source%.obj" + -@erase "$(INTDIR)\vc40.idb" + -@erase "$(INTDIR)\vc40.pdb" + -@erase "$(OUTDIR)\%Module%.dll" + -@erase "$(OUTDIR)\%Module%.exp" + -@erase "$(OUTDIR)\%Module%.ilk" + -@erase "$(OUTDIR)\%Module%.lib" + -@erase "$(OUTDIR)\%Module%.pdb" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\regex" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\regex" /D "WIN32" /D "_DEBUG"\ + /D "_WINDOWS" /Fp"$(INTDIR)/%Module%.pch" /YX /Fo"$(INTDIR)/"\ + /Fd"$(INTDIR)/" /c +CPP_OBJS=.\%Module%D/ +CPP_SBRS=.\. +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/%Module%.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 ..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +LINK32_FLAGS=..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib\ + winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\ + uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll\ + /incremental:yes /pdb:"$(OUTDIR)/%Module%.pdb" /debug /machine:I386\ + /out:"$(OUTDIR)/%Module%.dll"\ + /implib:"$(OUTDIR)/%Module%.lib" +LINK32_OBJS= \ + "$(INTDIR)\%Source%.obj" + +"$(OUTDIR)\%Module%.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Target + +# Name "%Module% - Win32 Release" +# Name "%Module% - Win32 Debug" + +!IF "$(CFG)" == "%Module% - Win32 Release" + +!ELSEIF "$(CFG)" == "%Module% - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=\work\apache\src\%Source%.c +DEP_CPP_MOD_A=\ + "..\alloc.h"\ + "..\buff.h"\ + "..\conf.h"\ + "..\http_config.h"\ + "..\http_core.h"\ + "..\http_log.h"\ + "..\http_request.h"\ + "..\httpd.h"\ + "..\regex\regex.h"\ + "..\ap_mmn.h"\ + ".\readdir.h"\ + {$(INCLUDE)}"\sys\stat.h"\ + {$(INCLUDE)}"\sys\types.h"\ + +NODEP_CPP_MOD_A=\ + "..\sfio.h"\ + + +"$(INTDIR)\%Source%.obj" : $(SOURCE) $(DEP_CPP_MOD_A) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/os/win32/mod_isapi.c b/os/win32/mod_isapi.c new file mode 100644 index 0000000000..fa002708ba --- /dev/null +++ b/os/win32/mod_isapi.c @@ -0,0 +1,569 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * mod_isapi.c - Internet Server Application (ISA) module for Apache + * by Alexei Kosut + * + * This module implements Microsoft's ISAPI, allowing Apache (when running + * under Windows) to load Internet Server Applications (ISAPI extensions). + * It implements all of the ISAPI 2.0 specification, except for the + * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI + * extensions that use only synchronous I/O and are compatible with the + * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should + * function as well). + * + * To load, simply place the ISA in a location in the document tree. + * Then add an "AddHandler isapi-isa dll" into your config file. + * You should now be able to load ISAPI DLLs just be reffering to their + * URLs. Make sure the ExecCGI option is active in the directory + * the ISA is in. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_log.h" +#include "util_script.h" + +/* We use the exact same header file as the original */ +#include + +/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER, + define this to conform */ +#define RELAX_HEADER_RULE + +module isapi_module; + +/* Our "Connection ID" structure */ + +typedef struct { + LPEXTENSION_CONTROL_BLOCK ecb; + request_rec *r; + int status; +} isapi_cid; + +/* Declare the ISAPI functions */ + +BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, + LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer); +BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, + DWORD dwReserved); +BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize); +BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, + LPVOID lpvBuffer, LPDWORD lpdwSize, + LPDWORD lpdwDataType); + +/* + The optimiser blows it totally here. What happens is that autos are addressed relative to the + stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere + between setting isapi_entry and calling through it. We work around the problem by forcing it to + use frame pointers. +*/ +#pragma optimize("y",off) + +int isapi_handler (request_rec *r) { + LPEXTENSION_CONTROL_BLOCK ecb = + ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK)); + HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO)); + + HINSTANCE isapi_handle; + BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */ + DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */ + BOOL (*isapi_term)(DWORD); /* optional entry point 3 */ + + isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid)); + table *e = r->subprocess_env; + int retval; + + /* Use similar restrictions as CGIs */ + + if (!(ap_allow_options(r) & OPT_EXECCGI)) + return FORBIDDEN; + + if (r->finfo.st_mode == 0) + return NOT_FOUND; + + if (S_ISDIR(r->finfo.st_mode)) + return FORBIDDEN; + + /* Load the module */ + + if (!(isapi_handle = LoadLibraryEx(r->filename, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "Could not load DLL: %s", r->filename); + return SERVER_ERROR; + } + + if (!(isapi_version = + (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "DLL could not load GetExtensionVersion(): %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + if (!(isapi_entry = + (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "DLL could not load HttpExtensionProc(): %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension")); + + /* Run GetExtensionVersion() */ + + if ((*isapi_version)(pVer) != TRUE) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT, r, + "ISAPI GetExtensionVersion() failed: %s", r->filename); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + /* Set up variables */ + ap_add_common_vars(r); + ap_add_cgi_vars(r); + + /* Set up connection ID */ + ecb->ConnID = (HCONN)cid; + cid->ecb = ecb; + cid->r = r; + cid->status = 0; + + ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK); + ecb->dwVersion = MAKELONG(0, 2); + ecb->dwHttpStatusCode = 0; + strcpy(ecb->lpszLogData, ""); + ecb->lpszMethod = r->method; + ecb->lpszQueryString = ap_table_get(e, "QUERY_STRING"); + ecb->lpszPathInfo = ap_table_get(e, "PATH_INFO"); + ecb->lpszPathTranslated = ap_table_get(e, "PATH_TRANSLATED"); + ecb->lpszContentType = ap_table_get(e, "CONTENT_TYPE"); + + /* Set up client input */ + if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return retval; + } + + if (ap_should_client_block(r)) { + /* Unlike IIS, which limits this to 48k, we read the whole + * sucker in. I suppose this could be bad for memory if someone + * uploaded the complete works of Shakespeare. Well, WebSite + * does the same thing. + */ + long to_read = atol(ap_table_get(e, "CONTENT_LENGTH")); + long read; + + /* Actually, let's cap it at 48k, until we figure out what + * to do with this... we don't want a Content-Length: 1000000000 + * taking out the machine. + */ + + if (to_read > 49152) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read); + + if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) { + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + return SERVER_ERROR; + } + + /* Although its not to spec, IIS seems to null-terminate + * its lpdData string. So we will too. To make sure + * cbAvailable matches cbTotalBytes, we'll up the latter + * and equalize them. + */ + ecb->cbAvailable = ecb->cbTotalBytes = read + 1; + ecb->lpbData[read] = '\0'; + } + else { + ecb->cbTotalBytes = 0; + ecb->cbAvailable = 0; + ecb->lpbData = NULL; + } + + /* Set up the callbacks */ + + ecb->GetServerVariable = &GetServerVariable; + ecb->WriteClient = &WriteClient; + ecb->ReadClient = &ReadClient; + ecb->ServerSupportFunction = &ServerSupportFunction; + + /* All right... try and load the sucker */ + retval = (*isapi_entry)(ecb); + + /* Set the status (for logging) */ + if (ecb->dwHttpStatusCode) + r->status = ecb->dwHttpStatusCode; + + /* Check for a log message - and log it */ + if (ecb->lpszLogData && strcmp(ecb->lpszLogData, "")) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "%s: %s", ecb->lpszLogData, r->filename); + + /* All done with the DLL... get rid of it */ + if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD); + FreeLibrary(isapi_handle); + + switch(retval) { + case HSE_STATUS_SUCCESS: + case HSE_STATUS_SUCCESS_AND_KEEP_CONN: + /* Ignore the keepalive stuff; Apache handles it just fine without + * the ISA's "advice". + */ + + if (cid->status) /* We have a special status to return */ + return cid->status; + + return OK; + case HSE_STATUS_PENDING: /* We don't support this */ + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + case HSE_STATUS_ERROR: + default: + return SERVER_ERROR; + } + +} +#pragma optimize("",on) + +BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName, + LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) { + request_rec *r = ((isapi_cid *)hConn)->r; + table *e = r->subprocess_env; + const char *result; + + /* Mostly, we just grab it from the environment, but there are + * a couple of special cases + */ + + if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) { + /* We don't support NT users, so this is always the same as + * REMOTE_USER + */ + result = ap_table_get(e, "REMOTE_USER"); + } + else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) { + /* Apache doesn't support secure requests inherently, so + * we have no way of knowing. We'll be conservative, and say + * all requests are insecure. + */ + result = "0"; + } + else if (!strcasecmp(lpszVariableName, "URL")) { + result = r->uri; + } + else { + result = ap_table_get(e, lpszVariableName); + } + + if (result) { + if (strlen(result) > *lpdwSizeofBuffer) { + *lpdwSizeofBuffer = strlen(result); + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + strncpy(lpvBuffer, result, *lpdwSizeofBuffer); + return TRUE; + } + + /* Didn't find it */ + SetLastError(ERROR_INVALID_INDEX); + return FALSE; +} + +BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes, + DWORD dwReserved) { + request_rec *r = ((isapi_cid *)ConnID)->r; + int writ; /* written, actually, but why shouldn't I make up words? */ + + /* We only support synchronous writing */ + if (dwReserved && dwReserved != HSE_IO_SYNC) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) { + SetLastError(ERROR); /* XXX: Find the right error code */ + return FALSE; + } + + *lpwdwBytes = writ; + return TRUE; +} + +BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) { + /* Doesn't need to do anything; we've read all the data already */ + return TRUE; +} + +/* XXX: There is an O(n^2) attack possible here. */ +BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest, + LPVOID lpvBuffer, LPDWORD lpdwSize, + LPDWORD lpdwDataType) { + isapi_cid *cid = (isapi_cid *)hConn; + request_rec *subreq, *r = cid->r; + char *data; + + switch (dwHSERequest) { + case HSE_REQ_SEND_URL_REDIRECT_RESP: + /* Set the status to be returned when the HttpExtensionProc() + * is done. + */ + ap_table_set (r->headers_out, "Location", lpvBuffer); + cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT; + return TRUE; + + case HSE_REQ_SEND_URL: + /* Read any additional input */ + + if (r->remaining > 0) { + char argsbuffer[HUGE_STRING_LEN]; + + while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)); + } + + /* Reset the method to GET */ + r->method = ap_pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + /* Don't let anyone think there's still data */ + ap_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect((char *)lpvBuffer, r); + return TRUE; + + case HSE_REQ_SEND_RESPONSE_HEADER: + r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK"); + sscanf(r->status_line, "%d", &r->status); + cid->ecb->dwHttpStatusCode = r->status; + + /* Now fill in the HTTP headers, and the rest of it. Ick. + * lpdwDataType contains a string that has headers (in MIME + * format), a blank like, then (possibly) data. We need + * to parse it. + * + * Easy case first: + */ + if (!lpdwDataType) { + ap_send_http_header(r); + return TRUE; + } + + /* Make a copy - don't disturb the original */ + data = ap_pstrdup(r->pool, (char *)lpdwDataType); + + /* We *should* break before this while loop ends */ + while (*data) { + char *value, *lf = strchr(data, '\n'); + int p; + +#ifdef RELAX_HEADER_RULE + if (lf) + *lf = '\0'; +#else + if (!lf) { /* Huh? Invalid data, I think */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ISA sent invalid headers: %s", r->filename); + SetLastError(ERROR); /* XXX: Find right error */ + return FALSE; + } + + /* Get rid of \n and \r */ + *lf = '\0'; +#endif + p = strlen(data); + if (p > 0 && data[p-1] == '\r') data[p-1] = '\0'; + + /* End of headers */ + if (*data == '\0') { +#ifdef RELAX_HEADER_RULE + if (lf) +#endif + data = lf + 1; /* Reset data */ + break; + } + + if (!(value = strchr(data, ':'))) { + SetLastError(ERROR); /* XXX: Find right error */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "ISA sent invalid headers", r->filename); + return FALSE; + } + + *value++ = '\0'; + while (*value && ap_isspace(*value)) ++value; + + /* Check all the special-case headers. Similar to what + * ap_scan_script_header_err() does (see that function for + * more detail) + */ + + if (!strcasecmp(data, "Content-Type")) { + char *tmp; + /* Nuke trailing whitespace */ + + char *endp = value + strlen(value) - 1; + while (endp > value && ap_isspace(*endp)) *endp-- = '\0'; + + tmp = ap_pstrdup (r->pool, value); + ap_str_tolower(tmp); + r->content_type = tmp; + } + else if (!strcasecmp(data, "Content-Length")) { + ap_table_set(r->headers_out, data, value); + } + else if (!strcasecmp(data, "Transfer-Encoding")) { + ap_table_set(r->headers_out, data, value); + } + else if (!strcasecmp(data, "Set-Cookie")) { + ap_table_add(r->err_headers_out, data, value); + } + else { + ap_table_merge(r->err_headers_out, data, value); + } + + /* Reset data */ +#ifdef RELAX_HEADER_RULE + if (!lf) { + data += p; + break; + } +#endif + data = lf + 1; + } + + /* All the headers should be set now */ + + ap_send_http_header(r); + + /* Any data left should now be sent directly */ + ap_rputs(data, r); + + return TRUE; + + case HSE_REQ_MAP_URL_TO_PATH: + /* Map a URL to a filename */ + subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer, + *lpdwSize), r); + + GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL); + + /* IIS puts a trailing slash on directories, Apache doesn't */ + + if (S_ISDIR (subreq->finfo.st_mode)) { + int l = strlen((char *)lpvBuffer); + + ((char *)lpvBuffer)[l] = '\\'; + ((char *)lpvBuffer)[l + 1] = '\0'; + } + + return TRUE; + + case HSE_REQ_DONE_WITH_SESSION: + /* Do nothing... since we don't support async I/O, they'll + * return from HttpExtensionProc soon + */ + return TRUE; + + /* We don't support all this async I/O, Microsoft-specific stuff */ + case HSE_REQ_IO_COMPLETION: + case HSE_REQ_TRANSMIT_FILE: + ap_log_rerror(APLOG_MARK, APLOG_WARNING, r, + "ISAPI asynchronous I/O not supported: %s", r->filename); + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } +} + +handler_rec isapi_handlers[] = { +{ "isapi-isa", isapi_handler }, +{ NULL} +}; + +module isapi_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + isapi_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/os/win32/modules.c b/os/win32/modules.c new file mode 100644 index 0000000000..d55d3d832c --- /dev/null +++ b/os/win32/modules.c @@ -0,0 +1,72 @@ +/* modules.c --- major modules compiled into Apache for Win32. + * Only insert an entry for a module if it must be compiled into + * the core server + */ + +#include "httpd.h" +#include "http_config.h" + +extern module core_module; +extern module so_module; +extern module mime_module; +extern module access_module; +extern module auth_module; +extern module negotiation_module; +extern module includes_module; +extern module autoindex_module; +extern module dir_module; +extern module cgi_module; +extern module userdir_module; +extern module alias_module; +extern module env_module; +extern module config_log_module; +extern module asis_module; +extern module imap_module; +extern module action_module; +extern module setenvif_module; +extern module isapi_module; + +module *ap_prelinked_modules[] = { + &core_module, + &so_module, + &mime_module, + &access_module, + &auth_module, + &negotiation_module, + &includes_module, + &autoindex_module, + &dir_module, + &cgi_module, + &userdir_module, + &alias_module, + &env_module, + &config_log_module, + &asis_module, + &imap_module, + &action_module, + &setenvif_module, + &isapi_module, + NULL +}; +module *ap_preloaded_modules[] = { + &core_module, + &so_module, + &mime_module, + &access_module, + &auth_module, + &negotiation_module, + &includes_module, + &autoindex_module, + &dir_module, + &cgi_module, + &userdir_module, + &alias_module, + &env_module, + &config_log_module, + &asis_module, + &imap_module, + &action_module, + &setenvif_module, + &isapi_module, + NULL +}; diff --git a/os/win32/os.h b/os/win32/os.h new file mode 100644 index 0000000000..80ffd7e65e --- /dev/null +++ b/os/win32/os.h @@ -0,0 +1,126 @@ +#ifndef APACHE_OS_H +#define APACHE_OS_H + +#define PLATFORM "Win32" + +/* + * This file in included in all Apache source code. It contains definitions + * of facilities available on _this_ operating system (HAVE_* macros), + * and prototypes of OS specific functions defined in os.c + */ + +/* temporarily replace crypt */ +/* char *crypt(const char *pw, const char *salt); */ +#define crypt(buf,salt) (buf) + +/* Although DIR_TYPE is dirent (see nt/readdir.h) we need direct.h for + chdir() */ +#include + +#define STATUS +/*#define WIN32_LEAN_AND_MEAN Now defined in project files */ +#ifndef STRICT + #define STRICT +#endif +#define CASE_BLIND_FILESYSTEM +#define NO_WRITEV +#define NO_SETSID +#define NO_USE_SIGACTION +#define NO_TIMES +#define NO_GETTIMEOFDAY +//#define NEED_PROCESS_H although we do, this is specially handled in ap_config.h +#define USE_LONGJMP +#define HAVE_MMAP +#define USE_MMAP_SCOREBOARD +#define MULTITHREAD +#define HAVE_CANONICAL_FILENAME +#define HAVE_DRIVE_LETTERS +typedef int uid_t; +typedef int gid_t; +typedef int pid_t; +typedef int mode_t; +typedef char * caddr_t; + +/* +Define export types. API_EXPORT_NONSTD is a nasty hack to avoid having to declare +every configuration function as __stdcall. +*/ + +#ifdef SHARED_MODULE +# define API_VAR_EXPORT __declspec(dllimport) +# define API_EXPORT(type) __declspec(dllimport) type __stdcall +# define API_EXPORT_NONSTD(type) __declspec(dllimport) type +#else +# define API_VAR_EXPORT __declspec(dllexport) +# define API_EXPORT(type) __declspec(dllexport) type __stdcall +# define API_EXPORT_NONSTD(type) __declspec(dllexport) type +#endif +#define MODULE_VAR_EXPORT __declspec(dllexport) + +#define strcasecmp(s1, s2) stricmp(s1, s2) +#define strncasecmp(s1, s2, n) strnicmp(s1, s2, n) +#define lstat(x, y) stat(x, y) +#define S_ISLNK(m) (0) +#define S_ISREG(m) ((m & _S_IFREG) == _S_IFREG) +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(m) (((m)&(S_IFREG)) == (S_IFREG)) +#endif +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define JMP_BUF jmp_buf +#define sleep(t) Sleep(t*1000) +#define O_CREAT _O_CREAT +#define O_RDWR _O_RDWR +#define SIGPIPE 17 +/* Seems Windows is not a subgenius */ +#define NO_SLACK +#include + +#define NO_OTHER_CHILD +#define NO_RELIABLE_PIPED_LOGS + +__inline int ap_os_is_path_absolute(const char *file) +{ + /* For now, just do the same check that http_request.c and mod_alias.c + * do. + */ + return file[0] == '/' || file[1] == ':'; +} + +#define stat(f,ps) os_stat(f,ps) +API_EXPORT(int) os_stat(const char *szPath,struct stat *pStat); + +API_EXPORT(int) os_strftime(char *s, size_t max, const char *format, const struct tm *tm); + +#define _spawnv(mode,cmdname,argv) os_spawnv(mode,cmdname,argv) +#define spawnv(mode,cmdname,argv) os_spawnv(mode,cmdname,argv) +API_EXPORT(int) os_spawnv(int mode,const char *cmdname,const char *const *argv); +#define _spawnve(mode,cmdname,argv,envp) os_spawnve(mode,cmdname,argv,envp) +#define spawnve(mode,cmdname,argv,envp) os_spawnve(mode,cmdname,argv,envp) +API_EXPORT(int) os_spawnve(int mode,const char *cmdname,const char *const *argv,const char *const *envp); +#define _spawnle os_spawnle +#define spawnle os_spawnle +API_EXPORT(int) os_spawnle(int mode,const char *cmdname,...); + +/* OS-dependent filename routines in util_win32.c */ + +API_EXPORT(int) ap_os_is_filename_valid(const char *file); + +/* Abstractions for dealing with shared object files (DLLs on Win32). + * These are used by mod_so.c + */ +#define ap_os_dso_handle_t HINSTANCE +#define ap_os_dso_init() +#define ap_os_dso_load(l) LoadLibraryEx(l, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) +#define ap_os_dso_unload(l) FreeLibrary(l) +#define ap_os_dso_sym(h,s) GetProcAddress(h,s) +#define ap_os_dso_error() "" /* for now */ + +/* Other ap_os_ routines not used by this platform */ +#define ap_os_kill(pid, sig) kill(pid, sig) + +#endif /* ! APACHE_OS_H */ diff --git a/os/win32/util_win32.c b/os/win32/util_win32.c new file mode 100644 index 0000000000..348e0f9e2a --- /dev/null +++ b/os/win32/util_win32.c @@ -0,0 +1,677 @@ +#include +#include +#include +#include +#include + +#include "httpd.h" +#include "http_log.h" + +/* Returns TRUE if the input string is a string + * of one or more '.' characters. + */ +static BOOL OnlyDots(char *pString) +{ + char *c; + + if (*pString == '\0') + return FALSE; + + for (c = pString;*c;c++) + if (*c != '.') + return FALSE; + + return TRUE; +} + +/* Accepts as input a pathname, and tries to match it to an + * existing path and return the pathname in the case that + * is present on the existing path. This routine also + * converts alias names to long names. + */ +API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool, + const char *szFile) +{ + char buf[HUGE_STRING_LEN]; + char *pInputName; + char *p, *q; + BOOL bDone = FALSE; + BOOL bFileExists = TRUE; + HANDLE hFind; + WIN32_FIND_DATA wfd; + + if (!szFile || strlen(szFile) == 0 || strlen(szFile) >= sizeof(buf)) + return ap_pstrdup(pPool, ""); + + buf[0] = '\0'; + pInputName = ap_pstrdup(pPool, szFile); + + /* First convert all slashes to \ so Win32 calls work OK */ + for (p = pInputName; *p; p++) { + if (*p == '/') + *p = '\\'; + } + + p = pInputName; + /* If there is drive information, copy it over. */ + if (pInputName[1] == ':') { + buf[0] = tolower(*p++); + buf[1] = *p++; + buf[2] = '\0'; + + /* If all we have is a drive letter, then we are done */ + if (strlen(pInputName) == 2) + bDone = TRUE; + } + + q = p; + if (*p == '\\') { + p++; + if (*p == '\\') /* Possible UNC name */ + { + p++; + /* Get past the machine name. FindFirstFile */ + /* will not find a machine name only */ + p = strchr(p, '\\'); + if (p) + { + p++; + /* Get past the share name. FindFirstFile */ + /* will not find a \\machine\share name only */ + p = strchr(p, '\\'); + if (p) { + strncat(buf,q,p-q); + q = p; + p++; + } + } + + if (!p) + p = q; + } + } + + p = strchr(p, '\\'); + + while (!bDone) { + if (p) + *p = '\0'; + + if (strchr(q, '*') || strchr(q, '?')) + bFileExists = FALSE; + + /* If the path exists so far, call FindFirstFile + * again. However, if this portion of the path contains + * only '.' charaters, skip the call to FindFirstFile + * since it will convert '.' and '..' to actual names. + * Note: in the call to OnlyDots, we may have to skip + * a leading slash. + */ + if (bFileExists && !OnlyDots((*q == '.' ? q : q+1))) { + hFind = FindFirstFile(pInputName, &wfd); + + if (hFind == INVALID_HANDLE_VALUE) { + bFileExists = FALSE; + } + else { + FindClose(hFind); + + if (*q == '\\') + strcat(buf,"\\"); + strcat(buf, wfd.cFileName); + } + } + + if (!bFileExists || OnlyDots((*q == '.' ? q : q+1))) { + strcat(buf, q); + } + + if (p) { + q = p; + *p++ = '\\'; + p = strchr(p, '\\'); + } + else { + bDone = TRUE; + } + } + + /* First convert all slashes to / so server code handles it ok */ + for (p = buf; *p; p++) { + if (*p == '\\') + *p = '/'; + } + + return ap_pstrdup(pPool, buf); +} + + +/* Perform canonicalization with the exception that the + * input case is preserved. + */ +API_EXPORT(char *) ap_os_case_canonical_filename(pool *pPool, + const char *szFile) +{ + char *pNewStr; + char *s; + char *p; + char *q; + + if (szFile == NULL || strlen(szFile) == 0) + return ap_pstrdup(pPool, ""); + + pNewStr = ap_pstrdup(pPool, szFile); + + /* Change all '\' characters to '/' characters. + * While doing this, remove any trailing '.'. + * Also, blow away any directories with 3 or + * more '.' + */ + for (p = pNewStr,s = pNewStr; *s; s++,p++) { + if (*s == '\\' || *s == '/') { + + q = p; + while (p > pNewStr && *(p-1) == '.') + p--; + + if (p == pNewStr && q-p <= 2 && *p == '.') + p = q; + else if (p > pNewStr && p < q && *(p-1) == '/') { + if (q-p > 2) + p--; + else + p = q; + } + + *p = '/'; + } + else { + *p = *s; + } + } + *p = '\0'; + + /* Blow away any final trailing '.' since on Win32 + * foo.bat == foo.bat. == foo.bat... etc. + * Also blow away any trailing spaces since + * "filename" == "filename " + */ + q = p; + while (p > pNewStr && (*(p-1) == '.' || *(p-1) == ' ')) + p--; + if ((p > pNewStr) || + (p == pNewStr && q-p > 2)) + *p = '\0'; + + + /* One more security issue to deal with. Win32 allows + * you to create long filenames. However, alias filenames + * are always created so that the filename will + * conform to 8.3 rules. According to the Microsoft + * Developer's network CD (1/98) + * "Automatically generated aliases are composed of the + * first six characters of the filename plus ~n + * (where n is a number) and the first three characters + * after the last period." + * Here, we attempt to detect and decode these names. + */ + p = strchr(pNewStr, '~'); + if (p != NULL) { + char *pConvertedName, *pQstr, *pPstr; + char buf[HUGE_STRING_LEN]; + /* We potentially have a short name. Call + * ap_os_systemcase_filename to examine the filesystem + * and possibly extract the long name. + */ + pConvertedName = ap_os_systemcase_filename(pPool, pNewStr); + + /* Since we want to preserve the incoming case as much + * as we can, compare for differences in the string and + * only substitute in the path names that changed. + */ + if (stricmp(pNewStr, pConvertedName)) { + buf[0] = '\0'; + + q = pQstr = pConvertedName; + p = pPstr = pNewStr; + do { + q = strchr(q,'/'); + p = strchr(p,'/'); + + if (p != NULL) { + *q = '\0'; + *p = '\0'; + } + + if (stricmp(pQstr, pPstr)) + strcat(buf, pQstr); /* Converted name */ + else + strcat(buf, pPstr); /* Original name */ + + + if (p != NULL) { + pQstr = q; + pPstr = p; + *q++ = '/'; + *p++ = '/'; + } + + } while (p != NULL); + + pNewStr = ap_pstrdup(pPool, buf); + } + } + + + return pNewStr; +} + +/* Perform complete canonicalization. + */ +API_EXPORT(char *) ap_os_canonical_filename(pool *pPool, const char *szFile) +{ + char *pNewName; + pNewName = ap_os_case_canonical_filename(pPool, szFile); + strlwr(pNewName); + return pNewName; +} + +/* Win95 doesn't like trailing /s. NT and Unix don't mind. This works + * around the problem. + * Errr... except if it is UNC and we are referring to the root of + * the UNC, we MUST have a trailing \ and we can't use /s. Jeez. + * Not sure if this refers to all UNCs or just roots, + * but I'm going to fix it for all cases for now. (Ben) + */ + +#undef stat +API_EXPORT(int) os_stat(const char *szPath, struct stat *pStat) +{ + int n; + + if (strlen(szPath) == 0) { + return -1; + } + + if (szPath[0] == '/' && szPath[1] == '/') { + char buf[_MAX_PATH]; + char *s; + int nSlashes = 0; + + ap_assert(strlen(szPath) < _MAX_PATH); + strcpy(buf, szPath); + for (s = buf; *s; ++s) { + if (*s == '/') { + *s = '\\'; + ++nSlashes; + } + } + /* then we need to add one more to get \\machine\share\ */ + if (nSlashes == 3) { + *s++ = '\\'; + } + *s = '\0'; + return stat(buf, pStat); + } + + /* + * Below removes the trailing /, however, do not remove + * it in the case of 'x:/' or stat will fail + */ + n = strlen(szPath); + if ((szPath[n - 1] == '\\' || szPath[n - 1] == '/') && + !(n == 3 && szPath[1] == ':')) { + char buf[_MAX_PATH]; + + ap_assert(n < _MAX_PATH); + strcpy(buf, szPath); + buf[n - 1] = '\0'; + + return stat(buf, pStat); + } + return stat(szPath, pStat); +} + +/* Fix two really crap problems with Win32 spawn[lv]e*: + * + * 1. Win32 doesn't deal with spaces in argv. + * 2. Win95 doesn't like / in cmdname. + */ + +#undef _spawnv +API_EXPORT(int) os_spawnv(int mode, const char *cmdname, + const char *const *argv) +{ + int n; + char **aszArgs; + const char *szArg; + char *szCmd; + char *s; + + szCmd = _alloca(strlen(cmdname)+1); + strcpy(szCmd, cmdname); + for (s = szCmd; *s; ++s) { + if (*s == '/') { + *s = '\\'; + } + } + + for (n = 0; argv[n]; ++n) + ; + + aszArgs = _alloca((n + 1) * sizeof(const char *)); + + for (n = 0; szArg = argv[n]; ++n) { + if (strchr(szArg, ' ')) { + int l = strlen(szArg); + + aszArgs[n] = _alloca(l + 2 + 1); + aszArgs[n][0] = '"'; + strcpy(&aszArgs[n][1], szArg); + aszArgs[n][l + 1] = '"'; + aszArgs[n][l + 2] = '\0'; + } + else { + aszArgs[n] = (char *)szArg; + } + } + + aszArgs[n] = NULL; + + return _spawnv(mode, szCmd, aszArgs); +} + +#undef _spawnve +API_EXPORT(int) os_spawnve(int mode, const char *cmdname, + const char *const *argv, const char *const *envp) +{ + int n; + char **aszArgs; + const char *szArg; + char *szCmd; + char *s; + + szCmd = _alloca(strlen(cmdname)+1); + strcpy(szCmd, cmdname); + for (s = szCmd; *s; ++s) { + if (*s == '/') { + *s = '\\'; + } + } + + for (n = 0; argv[n]; ++n) + ; + + aszArgs = _alloca((n + 1)*sizeof(const char *)); + + for (n = 0; szArg = argv[n]; ++n){ + if (strchr(szArg, ' ')) { + int l = strlen(szArg); + + aszArgs[n] = _alloca(l + 2 + 1); + aszArgs[n][0] = '"'; + strcpy(&aszArgs[n][1], szArg); + aszArgs[n][l + 1] = '"'; + aszArgs[n][l + 2] = '\0'; + } + else { + aszArgs[n] = (char *)szArg; + } + } + + aszArgs[n] = NULL; + + return _spawnve(mode, szCmd, aszArgs, envp); +} + +API_EXPORT(int) os_spawnle(int mode, const char *cmdname, ...) +{ + int n; + va_list vlist; + char **aszArgs; + const char *szArg; + const char *const *aszEnv; + char *szCmd; + char *s; + + szCmd = _alloca(strlen(cmdname)+1); + strcpy(szCmd, cmdname); + for (s = szCmd; *s; ++s) { + if (*s == '/') { + *s = '\\'; + } + } + + va_start(vlist, cmdname); + for (n = 0; va_arg(vlist, const char *); ++n) + ; + va_end(vlist); + + aszArgs = _alloca((n + 1) * sizeof(const char *)); + + va_start(vlist, cmdname); + for (n = 0; szArg = va_arg(vlist, const char *); ++n) { + if (strchr(szArg, ' ')) { + int l = strlen(szArg); + + aszArgs[n] = _alloca(l + 2 + 1); + aszArgs[n][0] = '"'; + strcpy(&aszArgs[n][1], szArg); + aszArgs[n][l + 1] = '"'; + aszArgs[n][l + 2] = '\0'; + } + else { + aszArgs[n] = (char *)szArg; + } + } + + aszArgs[n] = NULL; + + aszEnv = va_arg(vlist, const char *const *); + va_end(vlist); + + return _spawnve(mode, szCmd, aszArgs, aszEnv); +} + +#undef strftime + +/* Partial replacement for strftime. This adds certain expandos to the + * Windows version + */ + +API_EXPORT(int) os_strftime(char *s, size_t max, const char *format, + const struct tm *tm) { + /* If the new format string is bigger than max, the result string probably + * won't fit anyway. When %-expandos are added, made sure the padding below + * is enough. + */ + char *new_format = (char *) _alloca(max + 11); + size_t i, j, format_length = strlen(format); + int return_value; + int length_written; + + for (i = 0, j = 0; (i < format_length && j < max);) { + if (format[i] != '%') { + new_format[j++] = format[i++]; + continue; + } + switch (format[i+1]) { + case 'D': + /* Is this locale dependent? Shouldn't be... + Also note the year 2000 exposure here */ + memcpy(new_format + j, "%m/%d/%y", 8); + i += 2; + j += 8; + break; + case 'r': + memcpy(new_format + j, "%I:%M:%S %p", 11); + i += 2; + j += 11; + break; + case 'T': + memcpy(new_format + j, "%H:%M:%S", 8); + i += 2; + j += 8; + break; + case 'e': + length_written = ap_snprintf(new_format + j, max - j, "%2d", + tm->tm_mday); + j = (length_written == -1) ? max : (j + length_written); + i += 2; + break; + default: + /* We know we can advance two characters forward here. */ + new_format[j++] = format[i++]; + new_format[j++] = format[i++]; + } + } + if (j >= max) { + *s = '\0'; /* Defensive programming, okay since output is undefined */ + return_value = 0; + } else { + new_format[j] = '\0'; + return_value = strftime(s, max, new_format, tm); + } + return return_value; +} + +/* + * ap_os_is_filename_valid is given a filename, and returns 0 if the filename + * is not valid for use on this system. On Windows, this means it fails any + * of the tests below. Otherwise returns 1. + * + * Test for filename validity on Win32. This is of tests come in part from + * the MSDN article at "Technical Articles, Windows Platform, Base Services, + * Guidelines, Making Room for Long Filenames" although the information + * in MSDN about filename testing is incomplete or conflicting. There is a + * similar set of tests in "Technical Articles, Windows Platform, Base Services, + * Guidelines, Moving Unix Applications to Windows NT". + * + * The tests are: + * + * 1) total path length greater than MAX_PATH + * + * 2) anything using the octets 0-31 or characters " < > | : + * (these are reserved for Windows use in filenames. In addition + * each file system has its own additional characters that are + * invalid. See KB article Q100108 for more details). + * + * 3) anything ending in "." (no matter how many) + * (filename doc, doc. and doc... all refer to the same file) + * + * 4) any segment in which the basename (before first period) matches + * one of the DOS device names + * (the list comes from KB article Q100108 although some people + * reports that additional names such as "COM5" are also special + * devices). + * + * If the path fails ANY of these tests, the result must be to deny access. + */ + +API_EXPORT(int) ap_os_is_filename_valid(const char *file) +{ + const char *segstart; + unsigned int seglength; + const char *pos; + static const char * const invalid_characters = "?\"<>*|:"; + static const char * const invalid_filenames[] = { + "CON", "AUX", "COM1", "COM2", "COM3", + "COM4", "LPT1", "LPT2", "LPT3", "PRN", "NUL", NULL + }; + + /* Test 1 */ + if (strlen(file) > MAX_PATH) { + /* Path too long for Windows. Note that this test is not valid + * if the path starts with //?/ or \\?\. */ + return 0; + } + + pos = file; + + /* Skip any leading non-path components. This can be either a + * drive letter such as C:, or a UNC path such as \\SERVER\SHARE\. + * We continue and check the rest of the path based on the rules above. + * This means we could eliminate valid filenames from servers which + * are not running NT (such as Samba). + */ + + if (pos[0] && pos[1] == ':') { + /* Skip leading drive letter */ + pos += 2; + } + else { + if ((pos[0] == '\\' || pos[0] == '/') && + (pos[1] == '\\' || pos[1] == '/')) { + /* Is a UNC, so skip the server name and share name */ + pos += 2; + while (*pos && *pos != '/' && *pos != '\\') + pos++; + if (!*pos) { + /* No share name */ + return 0; + } + pos++; /* Move to start of share name */ + while (*pos && *pos != '/' && *pos != '\\') + pos++; + if (!*pos) { + /* No path information */ + return 0; + } + } + } + + while (*pos) { + unsigned int idx; + unsigned int baselength; + + while (*pos == '/' || *pos == '\\') { + pos++; + } + if (*pos == '\0') { + break; + } + segstart = pos; /* start of segment */ + while (*pos && *pos != '/' && *pos != '\\') { + pos++; + } + seglength = pos - segstart; + /* + * Now we have a segment of the path, starting at position "segstart" + * and length "seglength" + */ + + /* Test 2 */ + for (idx = 0; idx < seglength; idx++) { + if ((segstart[idx] > 0 && segstart[idx] < 32) || + strchr(invalid_characters, segstart[idx])) { + return 0; + } + } + + /* Test 3 */ + if (segstart[seglength-1] == '.') { + return 0; + } + + /* Test 4 */ + for (baselength = 0; baselength < seglength; baselength++) { + if (segstart[baselength] == '.') { + break; + } + } + + /* baselength is the number of characters in the base path of + * the segment (which could be the same as the whole segment length, + * if it does not include any dot characters). */ + if (baselength == 3 || baselength == 4) { + for (idx = 0; invalid_filenames[idx]; idx++) { + if (strlen(invalid_filenames[idx]) == baselength && + !strnicmp(invalid_filenames[idx], segstart, baselength)) { + return 0; + } + } + } + } + + return 1; +} diff --git a/server/.cvsignore b/server/.cvsignore new file mode 100644 index 0000000000..f8193cb91b --- /dev/null +++ b/server/.cvsignore @@ -0,0 +1,13 @@ +Makefile +uri_delims.h +gen_uri_delims +test_char.h +gen_test_char +gen_uri_delims_R +gen_test_char_R +gen_uri_delims_D +gen_uri_delims.ilk +gen_uri_delims.pdb +gen_test_char_D +gen_test_char.ilk +gen_test_char.pdb diff --git a/server/.indent.pro b/server/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/server/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/server/config.c b/server/config.c new file mode 100644 index 0000000000..8980f98ec4 --- /dev/null +++ b/server/config.c @@ -0,0 +1,1641 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_config.c: once was auxillary functions for reading httpd's config + * file and converting filenames into a namespace + * + * Rob McCool + * + * Wall-to-wall rewrite for Apache... commands which are part of the + * server core can now be found next door in "http_core.c". Now contains + * general command loop, and functions which do bookkeeping for the new + * Apache config stuff (modules and configuration vectors). + * + * rst + * + */ + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" /* for errors in parse_htaccess */ +#include "http_request.h" /* for default_handler (see invoke_handler) */ +#include "http_conf_globals.h" /* Sigh... */ +#include "http_vhost.h" +#include "explain.h" + +DEF_Explain + +/**************************************************************** + * + * We begin with the functions which deal with the linked list + * of modules which control just about all of the server operation. + */ + +/* total_modules is the number of modules that have been linked + * into the server. + */ +static int total_modules = 0; +/* dynamic_modules is the number of modules that have been added + * after the pre-loaded ones have been set up. It shouldn't be larger + * than DYNAMIC_MODULE_LIMIT. + */ +static int dynamic_modules = 0; +API_VAR_EXPORT module *top_module = NULL; +API_VAR_EXPORT module **ap_loaded_modules=NULL; + +typedef int (*handler_func) (request_rec *); +typedef void *(*dir_maker_func) (pool *, char *); +typedef void *(*merger_func) (pool *, void *, void *); + +/* Dealing with config vectors. These are associated with per-directory, + * per-server, and per-request configuration, and have a void* pointer for + * each modules. The nature of the structure pointed to is private to the + * module in question... the core doesn't (and can't) know. However, there + * are defined interfaces which allow it to create instances of its private + * per-directory and per-server structures, and to merge the per-directory + * structures of a directory and its subdirectory (producing a new one in + * which the defaults applying to the base directory have been properly + * overridden). + */ + +#ifndef ap_get_module_config +API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m) +{ + void **confv = (void **) conf_vector; + return confv[m->module_index]; +} +#endif + +#ifndef ap_set_module_config +API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val) +{ + void **confv = (void **) conf_vector; + confv[m->module_index] = val; +} +#endif + +static void *create_empty_config(pool *p) +{ + void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * + (total_modules + DYNAMIC_MODULE_LIMIT)); + return (void *) conf_vector; +} + +static void *create_default_per_dir_config(pool *p) +{ + void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT)); + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + dir_maker_func df = modp->create_dir_config; + + if (df) + conf_vector[modp->module_index] = (*df) (p, NULL); + } + + return (void *) conf_vector; +} + +void * + ap_merge_per_dir_configs(pool *p, void *base, void *new) +{ + void **conf_vector = (void **) ap_palloc(p, sizeof(void *) * total_modules); + void **base_vector = (void **) base; + void **new_vector = (void **) new; + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + merger_func df = modp->merge_dir_config; + int i = modp->module_index; + + if (df && new_vector[i]) + conf_vector[i] = (*df) (p, base_vector[i], new_vector[i]); + else + conf_vector[i] = new_vector[i] ? new_vector[i] : base_vector[i]; + } + + return (void *) conf_vector; +} + +static void *create_server_config(pool *p, server_rec *s) +{ + void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT)); + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + if (modp->create_server_config) + conf_vector[modp->module_index] = (*modp->create_server_config) (p, s); + } + + return (void *) conf_vector; +} + +static void merge_server_configs(pool *p, void *base, void *virt) +{ + /* Can reuse the 'virt' vector for the spine of it, since we don't + * have to deal with the moral equivalent of .htaccess files here... + */ + + void **base_vector = (void **) base; + void **virt_vector = (void **) virt; + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + merger_func df = modp->merge_server_config; + int i = modp->module_index; + + if (!virt_vector[i]) + virt_vector[i] = base_vector[i]; + else if (df) + virt_vector[i] = (*df) (p, base_vector[i], virt_vector[i]); + } +} + +void *ap_create_request_config(pool *p) +{ + return create_empty_config(p); +} + +CORE_EXPORT(void *) ap_create_per_dir_config(pool *p) +{ + return create_empty_config(p); +} + +#ifdef EXPLAIN + +struct { + int offset; + char *method; +} aMethods[] = + +{ +#define m(meth) { XtOffsetOf(module,meth),#meth } + m(translate_handler), + m(ap_check_user_id), + m(auth_checker), + m(type_checker), + m(fixer_upper), + m(logger), + { -1, "?" }, +#undef m +}; + +char *ShowMethod(module *modp, int offset) +{ + int n; + static char buf[200]; + + for (n = 0; aMethods[n].offset >= 0; ++n) + if (aMethods[n].offset == offset) + break; + ap_snprintf(buf, sizeof(buf), "%s:%s", modp->name, aMethods[n].method); + return buf; +} +#else +#define ShowMethod(modp,offset) +#endif + +/**************************************************************** + * + * Dispatch through the modules to find handlers for various phases + * of request handling. These are invoked by http_request.c to actually + * do the dirty work of slogging through the module structures. + */ + +/* + * Optimized run_method routines. The observation here is that many modules + * have NULL for most of the methods. So we build optimized lists of + * everything. If you think about it, this is really just like a sparse array + * implementation to avoid scanning the zero entries. + */ +static const int method_offsets[] = +{ + XtOffsetOf(module, translate_handler), + XtOffsetOf(module, ap_check_user_id), + XtOffsetOf(module, auth_checker), + XtOffsetOf(module, access_checker), + XtOffsetOf(module, type_checker), + XtOffsetOf(module, fixer_upper), + XtOffsetOf(module, logger), + XtOffsetOf(module, header_parser), + XtOffsetOf(module, post_read_request) +}; +#define NMETHODS (sizeof (method_offsets)/sizeof (method_offsets[0])) + +static struct { + int translate_handler; + int ap_check_user_id; + int auth_checker; + int access_checker; + int type_checker; + int fixer_upper; + int logger; + int header_parser; + int post_read_request; +} offsets_into_method_ptrs; + +/* + * This is just one big array of method_ptrs. It's constructed such that, + * for example, method_ptrs[ offsets_into_method_ptrs.logger ] is the first + * logger function. You go one-by-one from there until you hit a NULL. + * This structure was designed to hopefully maximize cache-coolness. + */ +static handler_func *method_ptrs; + +/* routine to reconstruct all these shortcuts... called after every + * add_module. + * XXX: this breaks if modules dink with their methods pointers + */ +static void build_method_shortcuts(void) +{ + module *modp; + int how_many_ptrs; + int i; + int next_ptr; + handler_func fp; + + if (method_ptrs) { + /* free up any previous set of method_ptrs */ + free(method_ptrs); + } + + /* first we count how many functions we have */ + how_many_ptrs = 0; + for (modp = top_module; modp; modp = modp->next) { + for (i = 0; i < NMETHODS; ++i) { + if (*(handler_func *) (method_offsets[i] + (char *) modp)) { + ++how_many_ptrs; + } + } + } + method_ptrs = malloc((how_many_ptrs + NMETHODS) * sizeof(handler_func)); + if (method_ptrs == NULL) { + fprintf(stderr, "Ouch! Out of memory in build_method_shortcuts()!\n"); + } + next_ptr = 0; + for (i = 0; i < NMETHODS; ++i) { + /* XXX: This is an itsy bit presumptuous about the alignment + * constraints on offsets_into_method_ptrs. I can't remember if + * ANSI says this has to be true... -djg */ + ((int *) &offsets_into_method_ptrs)[i] = next_ptr; + for (modp = top_module; modp; modp = modp->next) { + fp = *(handler_func *) (method_offsets[i] + (char *) modp); + if (fp) { + method_ptrs[next_ptr++] = fp; + } + } + method_ptrs[next_ptr++] = NULL; + } +} + + +static int run_method(request_rec *r, int offset, int run_all) +{ + int i; + + for (i = offset; method_ptrs[i]; ++i) { + handler_func mod_handler = method_ptrs[i]; + + if (mod_handler) { + int result; + + result = (*mod_handler) (r); + + if (result != DECLINED && (!run_all || result != OK)) + return result; + } + } + + return run_all ? OK : DECLINED; +} + +int ap_translate_name(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.translate_handler, 0); +} + +int ap_check_access(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.access_checker, 1); +} + +int ap_find_types(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.type_checker, 0); +} + +int ap_run_fixups(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.fixer_upper, 1); +} + +int ap_log_transaction(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.logger, 1); +} + +int ap_header_parse(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.header_parser, 1); +} + +int ap_run_post_read_request(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.post_read_request, 1); +} + +/* Auth stuff --- anything that defines one of these will presumably + * want to define something for the other. Note that check_auth is + * separate from check_access to make catching some config errors easier. + */ + +int ap_check_user_id(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.ap_check_user_id, 0); +} + +int ap_check_auth(request_rec *r) +{ + return run_method(r, offsets_into_method_ptrs.auth_checker, 0); +} + +/* + * For speed/efficiency we generate a compact list of all the handlers + * and wildcard handlers. This means we won't have to scan the entire + * module list looking for handlers... where we'll find a whole whack + * of NULLs. + */ +typedef struct { + handler_rec hr; + size_t len; +} fast_handler_rec; + +static fast_handler_rec *handlers; +static fast_handler_rec *wildhandlers; + +static void init_handlers(pool *p) +{ + module *modp; + int nhandlers = 0; + int nwildhandlers = 0; + const handler_rec *handp; + fast_handler_rec *ph, *pw; + char *starp; + + for (modp = top_module; modp; modp = modp->next) { + if (!modp->handlers) + continue; + for (handp = modp->handlers; handp->content_type; ++handp) { + if (strchr(handp->content_type, '*')) { + nwildhandlers ++; + } else { + nhandlers ++; + } + } + } + ph = handlers = ap_palloc(p, sizeof(*ph)*(nhandlers + 1)); + pw = wildhandlers = ap_palloc(p, sizeof(*pw)*(nwildhandlers + 1)); + for (modp = top_module; modp; modp = modp->next) { + if (!modp->handlers) + continue; + for (handp = modp->handlers; handp->content_type; ++handp) { + if ((starp = strchr(handp->content_type, '*'))) { + pw->hr.content_type = handp->content_type; + pw->hr.handler = handp->handler; + pw->len = starp - handp->content_type; + pw ++; + } else { + ph->hr.content_type = handp->content_type; + ph->hr.handler = handp->handler; + ph->len = strlen(handp->content_type); + ph ++; + } + } + } + pw->hr.content_type = NULL; + pw->hr.handler = NULL; + ph->hr.content_type = NULL; + ph->hr.handler = NULL; +} + +int ap_invoke_handler(request_rec *r) +{ + fast_handler_rec *handp; + const char *handler; + char *p; + size_t handler_len; + int result = HTTP_INTERNAL_SERVER_ERROR; + + if (r->handler) { + handler = r->handler; + handler_len = strlen(handler); + } + else { + handler = r->content_type ? r->content_type : ap_default_type(r); + if ((p = strchr(handler, ';')) != NULL) { /* MIME type arguments */ + while (p > handler && p[-1] == ' ') + --p; /* strip trailing spaces */ + handler_len = p - handler; + } + else { + handler_len = strlen(handler); + } + } + + /* Pass one --- direct matches */ + + for (handp = handlers; handp->hr.content_type; ++handp) { + if (handler_len == handp->len + && !strncmp(handler, handp->hr.content_type, handler_len)) { + result = (*handp->hr.handler) (r); + + if (result != DECLINED) + return result; + } + } + + if (result == HTTP_INTERNAL_SERVER_ERROR && r->handler) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r, + "handler \"%s\" not found for: %s", r->handler, r->filename); + } + + /* Pass two --- wildcard matches */ + + for (handp = wildhandlers; handp->hr.content_type; ++handp) { + if (handler_len >= handp->len + && !strncmp(handler, handp->hr.content_type, handp->len)) { + result = (*handp->hr.handler) (r); + + if (result != DECLINED) + return result; + } + } + + return HTTP_INTERNAL_SERVER_ERROR; +} + +/* One-time setup for precompiled modules --- NOT to be done on restart */ + +API_EXPORT(void) ap_add_module(module *m) +{ + /* This could be called from an AddModule httpd.conf command, + * after the file has been linked and the module structure within it + * teased out... + */ + + if (m->version != MODULE_MAGIC_NUMBER_MAJOR) { + fprintf(stderr, "%s: module \"%s\" is not compatible with this " + "version of Apache.\n", ap_server_argv0, m->name); + fprintf(stderr, "Please contact the vendor for the correct version.\n"); + exit(1); + } + + if (m->next == NULL) { + m->next = top_module; + top_module = m; + } + if (m->module_index == -1) { + m->module_index = total_modules++; + dynamic_modules++; + + if (dynamic_modules > DYNAMIC_MODULE_LIMIT) { + fprintf(stderr, "%s: module \"%s\" could not be loaded, because" + " the dynamic\n", ap_server_argv0, m->name); + fprintf(stderr, "module limit was reached. Please increase " + "DYNAMIC_MODULE_LIMIT and recompile.\n"); + exit(1); + } + } + + /* Some C compilers put a complete path into __FILE__, but we want + * only the filename (e.g. mod_includes.c). So check for path + * components (Unix and DOS), and remove them. + */ + + if (strrchr(m->name, '/')) + m->name = 1 + strrchr(m->name, '/'); + if (strrchr(m->name, '\\')) + m->name = 1 + strrchr(m->name, '\\'); + +#ifdef _OSD_POSIX /* __FILE__="*POSIX(/home/martin/apache/src/modules/standard/mod_info.c)" */ + /* We cannot fix the string in-place, because it's const */ + if (m->name[strlen(m->name)-1]==')') { + char *tmp = strdup(m->name); /* FIXME:memory leak, albeit a small one */ + tmp[strlen(tmp)-1] = '\0'; + m->name = tmp; + } +#endif /*_OSD_POSIX*/ +} + +/* + * remove_module undoes what add_module did. There are some caveats: + * when the module is removed, its slot is lost so all the current + * per-dir and per-server configurations are invalid. So we should + * only ever call this function when you are invalidating almost + * all our current data. I.e. when doing a restart. + */ + +API_EXPORT(void) ap_remove_module(module *m) +{ + module *modp; + + modp = top_module; + if (modp == m) { + /* We are the top module, special case */ + top_module = modp->next; + m->next = NULL; + } + else { + /* Not the top module, find use. When found modp will + * point to the module _before_ us in the list + */ + + while (modp && modp->next != m) { + modp = modp->next; + } + if (!modp) { + /* Uh-oh, this module doesn't exist */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, + "Cannot remove module %s: not found in module list", + m->name); + return; + } + /* Eliminate us from the module list */ + modp->next = modp->next->next; + } + + m->module_index = -1; /* simulate being unloaded, should + * be unnecessary */ + dynamic_modules--; + total_modules--; +} + +API_EXPORT(void) ap_add_loaded_module(module *mod) +{ + module **m; + + /* + * Add module pointer to top of chained module list + */ + ap_add_module(mod); + + /* + * And module pointer to list of loaded modules + * + * Notes: 1. ap_add_module() would already complain if no more space + * exists for adding a dynamically loaded module + * 2. ap_add_module() accepts double-inclusion, so we have + * to accept this, too. + */ + for (m = ap_loaded_modules; *m != NULL; m++) + ; + *m++ = mod; + *m = NULL; +} + +API_EXPORT(void) ap_remove_loaded_module(module *mod) +{ + module **m; + module **m2; + int done; + + /* + * Remove module pointer from chained module list + */ + ap_remove_module(mod); + + /* + * Remove module pointer from list of loaded modules + * + * Note: 1. We cannot determine if the module was successfully + * removed by ap_remove_module(). + * 2. We have not to complain explicity when the module + * is not found because ap_remove_module() did it + * for us already. + */ + for (m = m2 = ap_loaded_modules, done = 0; *m2 != NULL; m2++) { + if (*m2 == mod && done == 0) + done = 1; + else + *m++ = *m2; + } + *m = NULL; +} + +void ap_setup_prelinked_modules() +{ + module **m; + module **m2; + + /* + * Initialise total_modules variable and module indices + */ + total_modules = 0; + for (m = ap_preloaded_modules; *m != NULL; m++) + (*m)->module_index = total_modules++; + + /* + * Initialise list of loaded modules + */ + ap_loaded_modules = (module **)malloc( + sizeof(module *)*(total_modules+DYNAMIC_MODULE_LIMIT+1)); + if (ap_loaded_modules == NULL) { + fprintf(stderr, "Ouch! Out of memory in ap_setup_prelinked_modules()!\n"); + } + for (m = ap_preloaded_modules, m2 = ap_loaded_modules; *m != NULL; ) + *m2++ = *m++; + *m2 = NULL; + + /* + * Initialize chain of linked (=activate) modules + */ + for (m = ap_prelinked_modules; *m != NULL; m++) + ap_add_module(*m); +} + +API_EXPORT(const char *) ap_find_module_name(module *m) +{ + return m->name; +} + +API_EXPORT(module *) ap_find_linked_module(const char *name) +{ + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + if (strcmp(modp->name, name) == 0) + return modp; + } + return NULL; +} + +/* Add a named module. Returns 1 if module found, 0 otherwise. */ +API_EXPORT(int) ap_add_named_module(const char *name) +{ + module *modp; + int i = 0; + + for (modp = ap_loaded_modules[i]; modp; modp = ap_loaded_modules[++i]) { + if (strcmp(modp->name, name) == 0) { + /* Only add modules that are not already enabled. */ + if (modp->next == NULL) { + ap_add_module(modp); + } + return 1; + } + } + + return 0; +} + +/* Clear the internal list of modules, in preparation for starting over. */ +API_EXPORT(void) ap_clear_module_list() +{ + module **m = &top_module; + module **next_m; + + while (*m) { + next_m = &((*m)->next); + *m = NULL; + m = next_m; + } + + /* This is required; so we add it always. */ + ap_add_named_module("http_core.c"); +} + +/***************************************************************** + * + * Resource, access, and .htaccess config files now parsed by a common + * command loop. + * + * Let's begin with the basics; parsing the line and + * invoking the function... + */ + +static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, + void *mconfig, const char *args) +{ + char *w, *w2, *w3; + const char *errmsg; + + if ((parms->override & cmd->req_override) == 0) + return ap_pstrcat(parms->pool, cmd->name, " not allowed here", NULL); + + parms->info = cmd->cmd_data; + parms->cmd = cmd; + + switch (cmd->args_how) { + case RAW_ARGS: + return ((const char *(*)(cmd_parms *, void *, const char *)) + (cmd->func)) (parms, mconfig, args); + + case NO_ARGS: + if (*args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes no arguments", + NULL); + + return ((const char *(*)(cmd_parms *, void *)) + (cmd->func)) (parms, mconfig); + + case TAKE1: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes one argument", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *)) + (cmd->func)) (parms, mconfig, w); + + case TAKE2: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *)) (cmd->func)) (parms, mconfig, w, w2); + + case TAKE12: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *)) (cmd->func)) (parms, mconfig, w, + *w2 ? w2 : NULL); + + case TAKE3: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, " takes three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case TAKE23: + + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, + " takes two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case TAKE123: + + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, + " takes one, two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case TAKE13: + + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || (*w2 && !w3) || *args != 0) + return ap_pstrcat(parms->pool, cmd->name, + " takes one or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return ((const char *(*)(cmd_parms *, void *, const char *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2, w3); + + case ITERATE: + + while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') + if ((errmsg = ((const char *(*)(cmd_parms *, void *, + const char *)) (cmd->func)) (parms, mconfig, w))) + return errmsg; + + return NULL; + + case ITERATE2: + + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args == 0) + return ap_pstrcat(parms->pool, cmd->name, + " requires at least two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + + while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') + if ((errmsg = ((const char *(*)(cmd_parms *, void *, + const char *, const char *)) (cmd->func)) (parms, + mconfig, w, w2))) + return errmsg; + + return NULL; + + case FLAG: + + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off"))) + return ap_pstrcat(parms->pool, cmd->name, " must be On or Off", + NULL); + + return ((const char *(*)(cmd_parms *, void *, int)) + (cmd->func)) (parms, mconfig, strcasecmp(w, "off") != 0); + + default: + + return ap_pstrcat(parms->pool, cmd->name, + " is improperly configured internally (server bug)", + NULL); + } +} + +CORE_EXPORT(const command_rec *) ap_find_command(const char *name, const command_rec *cmds) +{ + while (cmds->name) + if (!strcasecmp(name, cmds->name)) + return cmds; + else + ++cmds; + + return NULL; +} + +CORE_EXPORT(const command_rec *) ap_find_command_in_modules(const char *cmd_name, module **mod) +{ + const command_rec *cmdp; + module *modp; + + for (modp = *mod; modp; modp = modp->next) + if (modp->cmds && (cmdp = ap_find_command(cmd_name, modp->cmds))) { + *mod = modp; + return cmdp; + } + + return NULL; +} + +CORE_EXPORT(void *) ap_set_config_vectors(cmd_parms *parms, void *config, module *mod) +{ + void *mconfig = ap_get_module_config(config, mod); + void *sconfig = ap_get_module_config(parms->server->module_config, mod); + + if (!mconfig && mod->create_dir_config) { + mconfig = (*mod->create_dir_config) (parms->pool, parms->path); + ap_set_module_config(config, mod, mconfig); + } + + if (!sconfig && mod->create_server_config) { + sconfig = (*mod->create_server_config) (parms->pool, parms->server); + ap_set_module_config(parms->server->module_config, mod, sconfig); + } + return mconfig; +} + +CORE_EXPORT(const char *) ap_handle_command(cmd_parms *parms, void *config, const char *l) +{ + void *oldconfig; + const char *args, *cmd_name, *retval; + const command_rec *cmd; + module *mod = top_module; + + if ((l[0] == '#') || (!l[0])) + return NULL; + + args = l; + cmd_name = ap_getword_conf(parms->temp_pool, &args); + if (*cmd_name == '\0') + return NULL; + + oldconfig = parms->context; + parms->context = config; + do { + if (!(cmd = ap_find_command_in_modules(cmd_name, &mod))) { + errno = EINVAL; + return ap_pstrcat(parms->pool, "Invalid command '", cmd_name, + "', perhaps mis-spelled or defined by a module " + "not included in the server configuration", NULL); + } + else { + void *mconfig = ap_set_config_vectors(parms,config, mod); + + retval = invoke_cmd(cmd, parms, mconfig, args); + mod = mod->next; /* Next time around, skip this one */ + } + } while (retval && !strcmp(retval, DECLINE_CMD)); + parms->context = oldconfig; + + return retval; +} + +API_EXPORT(const char *) ap_srm_command_loop(cmd_parms *parms, void *config) +{ + char l[MAX_STRING_LEN]; + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) { + const char *errmsg = ap_handle_command(parms, config, l); + if (errmsg) { + return errmsg; + } + } + + return NULL; +} + +/* + * Generic command functions... + */ + +API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *cmd, + char *struct_ptr, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int) (long) cmd->info; + *(char **) (struct_ptr + offset) = arg; + return NULL; +} + +API_EXPORT_NONSTD(const char *) ap_set_string_slot_lower(cmd_parms *cmd, + char *struct_ptr, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int) (long) cmd->info; + ap_str_tolower(arg); + *(char **) (struct_ptr + offset) = arg; + return NULL; +} + +API_EXPORT_NONSTD(const char *) ap_set_flag_slot(cmd_parms *cmd, + char *struct_ptr, int arg) +{ + /* This one's pretty generic too... */ + + int offset = (int) (long) cmd->info; + *(int *) (struct_ptr + offset) = arg ? 1 : 0; + return NULL; +} + +API_EXPORT_NONSTD(const char *) ap_set_file_slot(cmd_parms *cmd, char *struct_ptr, char *arg) +{ + /* Prepend server_root to relative arg. + This allows .htaccess to be independent of server_root, + so the server can be moved or mirrored with less pain. */ + char *p; + int offset = (int) (long) cmd->info; + if (ap_os_is_path_absolute(arg)) + p = arg; + else + p = ap_make_full_path(cmd->pool, ap_server_root, arg); + *(char **) (struct_ptr + offset) = p; + return NULL; +} + +/***************************************************************** + * + * Reading whole config files... + */ + +static cmd_parms default_parms = +{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +API_EXPORT(char *) ap_server_root_relative(pool *p, char *file) +{ + if(ap_os_is_path_absolute(file)) + return file; + return ap_make_full_path(p, ap_server_root, file); +} + + +/* This structure and the following functions are needed for the + * table-based config file reading. They are passed to the + * cfg_open_custom() routine. + */ + +/* Structure to be passed to cfg_open_custom(): it contains an + * index which is incremented from 0 to nelts on each call to + * cfg_getline() (which in turn calls arr_elts_getstr()) + * and an array_header pointer for the string array. + */ +typedef struct { + array_header *array; + int curr_idx; +} arr_elts_param_t; + + +/* arr_elts_getstr() returns the next line from the string array. */ +static void *arr_elts_getstr(void *buf, size_t bufsiz, void *param) +{ + arr_elts_param_t *arr_param = (arr_elts_param_t *) param; + + /* End of array reached? */ + if (++arr_param->curr_idx > arr_param->array->nelts) + return NULL; + + /* return the line */ + ap_cpystrn(buf, ((char **) arr_param->array->elts)[arr_param->curr_idx - 1], bufsiz); + + return buf; +} + + +/* arr_elts_close(): dummy close routine (makes sure no more lines can be read) */ +static int arr_elts_close(void *param) +{ + arr_elts_param_t *arr_param = (arr_elts_param_t *) param; + arr_param->curr_idx = arr_param->array->nelts; + return 0; +} + +static void process_command_config(server_rec *s, array_header *arr, pool *p, + pool *ptemp) +{ + const char *errmsg; + cmd_parms parms; + arr_elts_param_t arr_parms; + + arr_parms.curr_idx = 0; + arr_parms.array = arr; + + parms = default_parms; + parms.pool = p; + parms.temp_pool = ptemp; + parms.server = s; + parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); + parms.config_file = ap_pcfg_open_custom(p, "-c/-C directives", + &arr_parms, NULL, + arr_elts_getstr, arr_elts_close); + + errmsg = ap_srm_command_loop(&parms, s->lookup_defaults); + + if (errmsg) { + fprintf(stderr, "Syntax error in -C/-c directive:\n%s\n", errmsg); + exit(1); + } + + ap_cfg_closefile(parms.config_file); +} + +void ap_process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp) +{ + const char *errmsg; + cmd_parms parms; + struct stat finfo; + + fname = ap_server_root_relative(p, fname); + + if (!(strcmp(fname, ap_server_root_relative(p, RESOURCE_CONFIG_FILE))) || + !(strcmp(fname, ap_server_root_relative(p, ACCESS_CONFIG_FILE)))) { + if (stat(fname, &finfo) == -1) + return; + } + + /* don't require conf/httpd.conf if we have a -C or -c switch */ + if((ap_server_pre_read_config->nelts || ap_server_post_read_config->nelts) && + !(strcmp(fname, ap_server_root_relative(p, SERVER_CONFIG_FILE)))) { + if (stat(fname, &finfo) == -1) + return; + } + + /* GCC's initialization extensions are soooo nice here... */ + + parms = default_parms; + parms.pool = p; + parms.temp_pool = ptemp; + parms.server = s; + parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); + + if (!(parms.config_file = ap_pcfg_openfile(p,fname))) { + perror("fopen"); + fprintf(stderr, "%s: could not open document config file %s\n", + ap_server_argv0, fname); + exit(1); + } + + errmsg = ap_srm_command_loop(&parms, s->lookup_defaults); + + if (errmsg) { + fprintf(stderr, "Syntax error on line %d of %s:\n", + parms.config_file->line_number, parms.config_file->name); + fprintf(stderr, "%s\n", errmsg); + exit(1); + } + + ap_cfg_closefile(parms.config_file); +} + + +int ap_parse_htaccess(void **result, request_rec *r, int override, + const char *d, const char *access_name) +{ + configfile_t *f = NULL; + cmd_parms parms; + const char *errmsg; + char *filename = NULL; + const struct htaccess_result *cache; + struct htaccess_result *new; + void *dc = NULL; + +/* firstly, search cache */ + for (cache = r->htaccess; cache != NULL; cache = cache->next) + if (cache->override == override && strcmp(cache->dir, d) == 0) { + if (cache->htaccess != NULL) + *result = cache->htaccess; + return OK; + } + + parms = default_parms; + parms.override = override; + parms.pool = r->pool; + parms.temp_pool = r->pool; + parms.server = r->server; + parms.path = ap_pstrdup(r->pool, d); + + /* loop through the access names and find the first one */ + + while (access_name[0]) { + filename = ap_make_full_path(r->pool, d, + ap_getword_conf(r->pool, &access_name)); + + if ((f = ap_pcfg_openfile(r->pool, filename)) != NULL) { + + dc = ap_create_per_dir_config(r->pool); + + parms.config_file = f; + + errmsg = ap_srm_command_loop(&parms, dc); + + ap_cfg_closefile(f); + + if (errmsg) { + ap_log_rerror(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, r, + "%s: %s", filename, errmsg); + return HTTP_INTERNAL_SERVER_ERROR; + } + *result = dc; + break; + } + else if (errno != ENOENT && errno != ENOTDIR) { + ap_log_rerror(APLOG_MARK, APLOG_CRIT, r, + "%s pcfg_openfile: unable to check htaccess file, " + "ensure it is readable", + filename); + ap_table_setn(r->notes, "error-notes", + "Server unable to read htaccess file, denying " + "access to be safe"); + return HTTP_FORBIDDEN; + } + } + +/* cache it */ + new = ap_palloc(r->pool, sizeof(struct htaccess_result)); + new->dir = parms.path; + new->override = override; + new->htaccess = dc; +/* add to head of list */ + new->next = r->htaccess; + r->htaccess = new; + + return OK; +} + + +CORE_EXPORT(const char *) ap_init_virtual_host(pool *p, const char *hostname, + server_rec *main_server, server_rec **ps) +{ + server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec)); + +#ifdef RLIMIT_NOFILE + struct rlimit limits; + + getrlimit(RLIMIT_NOFILE, &limits); + if (limits.rlim_cur < limits.rlim_max) { + limits.rlim_cur += 2; + if (setrlimit(RLIMIT_NOFILE, &limits) < 0) { + perror("setrlimit(RLIMIT_NOFILE)"); + fprintf(stderr, "Cannot exceed hard limit for open files"); + } + } +#endif + + s->server_admin = NULL; + s->server_hostname = NULL; + s->error_fname = NULL; + s->srm_confname = NULL; + s->access_confname = NULL; + s->timeout = 0; + s->keep_alive_timeout = 0; + s->keep_alive = -1; + s->keep_alive_max = -1; + s->error_log = main_server->error_log; + s->loglevel = main_server->loglevel; + /* useful default, otherwise we get a port of 0 on redirects */ + s->port = main_server->port; + s->next = NULL; + + s->is_virtual = 1; + s->names = ap_make_array(p, 4, sizeof(char **)); + s->wild_names = ap_make_array(p, 4, sizeof(char **)); + + s->module_config = create_empty_config(p); + s->lookup_defaults = ap_create_per_dir_config(p); + + s->server_uid = ap_user_id; + s->server_gid = ap_group_id; + + s->limit_req_line = main_server->limit_req_line; + s->limit_req_fieldsize = main_server->limit_req_fieldsize; + s->limit_req_fields = main_server->limit_req_fields; + + *ps = s; + + return ap_parse_vhost_addrs(p, hostname, s); +} + + +static void fixup_virtual_hosts(pool *p, server_rec *main_server) +{ + server_rec *virt; + + for (virt = main_server->next; virt; virt = virt->next) { + merge_server_configs(p, main_server->module_config, + virt->module_config); + + virt->lookup_defaults = + ap_merge_per_dir_configs(p, main_server->lookup_defaults, + virt->lookup_defaults); + + if (virt->server_admin == NULL) + virt->server_admin = main_server->server_admin; + + if (virt->srm_confname == NULL) + virt->srm_confname = main_server->srm_confname; + + if (virt->access_confname == NULL) + virt->access_confname = main_server->access_confname; + + if (virt->timeout == 0) + virt->timeout = main_server->timeout; + + if (virt->keep_alive_timeout == 0) + virt->keep_alive_timeout = main_server->keep_alive_timeout; + + if (virt->keep_alive == -1) + virt->keep_alive = main_server->keep_alive; + + if (virt->keep_alive_max == -1) + virt->keep_alive_max = main_server->keep_alive_max; + + if (virt->send_buffer_size == 0) + virt->send_buffer_size = main_server->send_buffer_size; + + /* XXX: this is really something that should be dealt with by a + * post-config api phase */ + ap_core_reorder_directories(p, virt); + } + ap_core_reorder_directories(p, main_server); +} + +/***************************************************************** + * + * Getting *everything* configured... + */ + +static void init_config_globals(pool *p) +{ + /* ServerRoot, server_confname set in httpd.c */ + + ap_standalone = 1; + ap_user_name = DEFAULT_USER; + ap_user_id = ap_uname2id(DEFAULT_USER); + ap_group_id = ap_gname2id(DEFAULT_GROUP); + ap_daemons_to_start = DEFAULT_START_DAEMON; + ap_daemons_min_free = DEFAULT_MIN_FREE_DAEMON; + ap_daemons_max_free = DEFAULT_MAX_FREE_DAEMON; + ap_daemons_limit = HARD_SERVER_LIMIT; + ap_pid_fname = DEFAULT_PIDLOG; + ap_scoreboard_fname = DEFAULT_SCOREBOARD; + ap_lock_fname = DEFAULT_LOCKFILE; + ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + ap_bind_address.s_addr = htonl(INADDR_ANY); + ap_listeners = NULL; + ap_listenbacklog = DEFAULT_LISTENBACKLOG; + ap_extended_status = 0; + + /* Global virtual host hash bucket pointers. Init to null. */ + ap_init_vhost_config(p); + + ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); +} + +static server_rec *init_server_config(pool *p) +{ + server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec)); + + s->port = 0; + s->server_admin = DEFAULT_ADMIN; + s->server_hostname = NULL; + s->error_fname = DEFAULT_ERRORLOG; + s->error_log = stderr; + s->loglevel = DEFAULT_LOGLEVEL; + s->srm_confname = RESOURCE_CONFIG_FILE; + s->access_confname = ACCESS_CONFIG_FILE; + s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE; + s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE; + s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS; + s->timeout = DEFAULT_TIMEOUT; + s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT; + s->keep_alive_max = DEFAULT_KEEPALIVE; + s->keep_alive = 1; + s->next = NULL; + s->addrs = ap_pcalloc(p, sizeof(server_addr_rec)); + /* NOT virtual host; don't match any real network interface */ + s->addrs->host_addr.s_addr = htonl(INADDR_ANY); + s->addrs->host_port = 0; /* matches any port */ + s->addrs->virthost = ""; /* must be non-NULL */ + s->names = s->wild_names = NULL; + + s->module_config = create_server_config(p, s); + s->lookup_defaults = create_default_per_dir_config(p); + + return s; +} + + +static void default_listeners(pool *p, server_rec *s) +{ + listen_rec *new; + + if (ap_listeners != NULL) { + return; + } + /* allocate a default listener */ + new = ap_pcalloc(p, sizeof(listen_rec)); + new->local_addr.sin_family = AF_INET; + new->local_addr.sin_addr = ap_bind_address; + new->local_addr.sin_port = htons(s->port ? s->port : DEFAULT_HTTP_PORT); + new->fd = -1; + new->used = 0; + new->next = NULL; + ap_listeners = new; +} + + +server_rec *ap_read_config(pool *p, pool *ptemp, char *confname) +{ + server_rec *s = init_server_config(p); + + init_config_globals(p); + + /* All server-wide config files now have the SAME syntax... */ + + process_command_config(s, ap_server_pre_read_config, p, ptemp); + + ap_process_resource_config(s, confname, p, ptemp); + ap_process_resource_config(s, s->srm_confname, p, ptemp); + ap_process_resource_config(s, s->access_confname, p, ptemp); + + process_command_config(s, ap_server_post_read_config, p, ptemp); + + fixup_virtual_hosts(p, s); + default_listeners(p, s); + ap_fini_vhost_config(p, s); + + return s; +} + +void ap_single_module_configure(pool *p, server_rec *s, module *m) +{ + if (m->create_server_config) + ap_set_module_config(s->module_config, m, + (*m->create_server_config)(p, s)); + if (m->create_dir_config) + ap_set_module_config(s->lookup_defaults, m, + (*m->create_dir_config)(p, NULL)); +} + +void ap_init_modules(pool *p, server_rec *s) +{ + module *m; + + for (m = top_module; m; m = m->next) + if (m->init) + (*m->init) (s, p); + build_method_shortcuts(); + init_handlers(p); +} + +void ap_child_init_modules(pool *p, server_rec *s) +{ + module *m; + + for (m = top_module; m; m = m->next) + if (m->child_init) + (*m->child_init) (s, p); +} + +void ap_child_exit_modules(pool *p, server_rec *s) +{ + module *m; + +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif +#ifdef SIGUSR1 + signal(SIGUSR1, SIG_IGN); +#endif + + for (m = top_module; m; m = m->next) + if (m->child_exit) + (*m->child_exit) (s, p); + +} + +/******************************************************************** + * Configuration directives are restricted in terms of where they may + * appear in the main configuration files and/or .htaccess files according + * to the bitmask req_override in the command_rec structure. + * If any of the overrides set in req_override are also allowed in the + * context in which the command is read, then the command is allowed. + * The context is determined as follows: + * + * inside *.conf --> override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT); + * within or --> override = OR_ALL|ACCESS_CONF; + * within .htaccess --> override = AllowOverride for current directory; + * + * the result is, well, a rather confusing set of possibilities for when + * a particular directive is allowed to be used. This procedure prints + * in English where the given (pc) directive can be used. + */ +static void show_overrides(const command_rec *pc, module *pm) +{ + int n = 0; + + printf("\tAllowed in *.conf "); + if ((pc->req_override & (OR_OPTIONS | OR_FILEINFO | OR_INDEXES)) || + ((pc->req_override & RSRC_CONF) && + ((pc->req_override & (ACCESS_CONF | OR_AUTHCFG | OR_LIMIT))))) + printf("anywhere"); + else if (pc->req_override & RSRC_CONF) + printf("only outside , or "); + else + printf("only inside , or "); + + /* Warn if the directive is allowed inside or .htaccess + * but module doesn't support per-dir configuration */ + + if ((pc->req_override & (OR_ALL | ACCESS_CONF)) && !pm->create_dir_config) + printf(" [no per-dir config]"); + + if (pc->req_override & OR_ALL) { + printf(" and in .htaccess\n\twhen AllowOverride"); + + if ((pc->req_override & OR_ALL) == OR_ALL) + printf(" isn't None"); + else { + printf(" includes "); + + if (pc->req_override & OR_AUTHCFG) { + if (n++) + printf(" or "); + printf("AuthConfig"); + } + if (pc->req_override & OR_LIMIT) { + if (n++) + printf(" or "); + printf("Limit"); + } + if (pc->req_override & OR_OPTIONS) { + if (n++) + printf(" or "); + printf("Options"); + } + if (pc->req_override & OR_FILEINFO) { + if (n++) + printf(" or "); + printf("FileInfo"); + } + if (pc->req_override & OR_INDEXES) { + if (n++) + printf(" or "); + printf("Indexes"); + } + } + } + printf("\n"); +} + +/* Show the preloaded configuration directives, the help string explaining + * the directive arguments, in what module they are handled, and in + * what parts of the configuration they are allowed. Used for httpd -h. + */ +void ap_show_directives() +{ + const command_rec *pc; + int n; + + for (n = 0; ap_loaded_modules[n]; ++n) + for (pc = ap_loaded_modules[n]->cmds; pc && pc->name; ++pc) { + printf("%s (%s)\n", pc->name, ap_loaded_modules[n]->name); + if (pc->errmsg) + printf("\t%s\n", pc->errmsg); + show_overrides(pc, ap_loaded_modules[n]); + } +} + +/* Show the preloaded module names. Used for httpd -l. */ +void ap_show_modules() +{ + int n; + + printf("Compiled-in modules:\n"); + for (n = 0; ap_loaded_modules[n]; ++n) + printf(" %s\n", ap_loaded_modules[n]->name); +} diff --git a/server/gen_test_char.c b/server/gen_test_char.c new file mode 100644 index 0000000000..dc33a73d86 --- /dev/null +++ b/server/gen_test_char.c @@ -0,0 +1,62 @@ +/* we need some of the portability definitions... for strchr */ +#include "httpd.h" + +/* A bunch of functions in util.c scan strings looking for certain characters. + * To make that more efficient we encode a lookup table. + */ +#define T_ESCAPE_SHELL_CMD (0x01) +#define T_ESCAPE_PATH_SEGMENT (0x02) +#define T_OS_ESCAPE_PATH (0x04) +#define T_HTTP_TOKEN_STOP (0x08) + +int main(int argc, char *argv[]) +{ + unsigned c; + unsigned char flags; + + printf( +"/* this file is automatically generated by gen_test_char, do not edit */\n" +"#define T_ESCAPE_SHELL_CMD (%u)\n" +"#define T_ESCAPE_PATH_SEGMENT (%u)\n" +"#define T_OS_ESCAPE_PATH (%u)\n" +"#define T_HTTP_TOKEN_STOP (%u)\n" +"\n" +"static const unsigned char test_char_table[256] = {\n" +" 0,", + T_ESCAPE_SHELL_CMD, + T_ESCAPE_PATH_SEGMENT, + T_OS_ESCAPE_PATH, + T_HTTP_TOKEN_STOP); + + /* we explicitly dealt with NUL above + * in case some strchr() do bogosity with it */ + + for (c = 1; c < 256; ++c) { + flags = 0; + if (c % 20 == 0) + printf("\n "); + + /* escape_shell_cmd */ + if (strchr("&;`'\"|*?~<>^()[]{}$\\\n", c)) { + flags |= T_ESCAPE_SHELL_CMD; + } + + if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=~", c)) { + flags |= T_ESCAPE_PATH_SEGMENT; + } + + if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) { + flags |= T_OS_ESCAPE_PATH; + } + + /* these are the "tspecials" from RFC2068 */ + if (ap_iscntrl(c) || strchr(" \t()<>@,;:\\/[]?={}", c)) { + flags |= T_HTTP_TOKEN_STOP; + } + printf("%u%c", flags, (c < 255) ? ',' : ' '); + + } + printf("\n};\n"); + + return 0; +} diff --git a/server/gen_test_char.dsp b/server/gen_test_char.dsp new file mode 100644 index 0000000000..c2630ce9d1 --- /dev/null +++ b/server/gen_test_char.dsp @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Project File - Name="gen_test_char" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=gen_test_char - Win32 Debug +!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 "gen_test_char.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 "gen_test_char.mak" CFG="gen_test_char - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gen_test_char - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "gen_test_char - Win32 Debug" (based on\ + "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gen_test_char - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "gen_test" +# PROP BASE Intermediate_Dir "gen_test" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "gen_test_char_R" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# 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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE=$(InputPath) +PostBuild_Desc=Create test_char.h +PostBuild_Cmds=.\gen_test_char > test_char.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "gen_test_char - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "gen_tes0" +# PROP BASE Intermediate_Dir "gen_tes0" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "gen_test_char_D" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# 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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE=$(InputPath) +PostBuild_Desc=Create test_char.h +PostBuild_Cmds=.\gen_test_char > test_char.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "gen_test_char - Win32 Release" +# Name "gen_test_char - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\gen_test_char.c +# End Source File +# End Group +# End Target +# End Project diff --git a/server/gen_uri_delims.c b/server/gen_uri_delims.c new file mode 100644 index 0000000000..15a73fbc7a --- /dev/null +++ b/server/gen_uri_delims.c @@ -0,0 +1,31 @@ +#include + +/* generate a table of 256 values, where certain characters are + * marked "interesting"... for the uri parsing process. + */ + +int main(int argc, char *argv[]) +{ + int i; + char *value; + + printf("/* this file is automatically generated by " + "gen_uri_delims, do not edit */\n"); + printf("static const unsigned char uri_delims[256] = {"); + for (i = 0; i < 256; ++i) { + if (i % 20 == 0) + printf("\n "); + switch (i) { + case ':': value = "T_COLON"; break; + case '/': value = "T_SLASH"; break; + case '?': value = "T_QUESTION"; break; + case '#': value = "T_HASH"; break; + case '\0': value = "T_NUL"; break; + default: value = "0"; break; + } + printf("%s%c", value, (i < 255) ? ',' : ' '); + } + printf("\n};\n"); + + return 0; +} diff --git a/server/gen_uri_delims.dsp b/server/gen_uri_delims.dsp new file mode 100644 index 0000000000..566dd83e27 --- /dev/null +++ b/server/gen_uri_delims.dsp @@ -0,0 +1,105 @@ +# Microsoft Developer Studio Project File - Name="gen_uri_delims" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=gen_uri_delims - Win32 Debug +!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 "gen_uri_delims.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 "gen_uri_delims.mak" CFG="gen_uri_delims - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gen_uri_delims - Win32 Release" (based on\ + "Win32 (x86) Console Application") +!MESSAGE "gen_uri_delims - Win32 Debug" (based on\ + "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "gen_uri_delims - 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 "" +# PROP Intermediate_Dir "gen_uri_delims_R" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# 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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE=$(InputPath) +PostBuild_Desc=Create uri_delims.h +PostBuild_Cmds=.\gen_uri_delims > uri_delims.h +# End Special Build Tool + +!ELSEIF "$(CFG)" == "gen_uri_delims - 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 "" +# PROP Intermediate_Dir "gen_uri_delims_D" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# 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 user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE=$(InputPath) +PostBuild_Desc=Create uri_delims.h +PostBuild_Cmds=.\gen_uri_delims > uri_delims.h +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "gen_uri_delims - Win32 Release" +# Name "gen_uri_delims - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\gen_uri_delims.c +# End Source File +# End Group +# End Target +# End Project diff --git a/server/log.c b/server/log.c new file mode 100644 index 0000000000..7a1e0ea5d0 --- /dev/null +++ b/server/log.c @@ -0,0 +1,777 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_log.c: Dealing with the logs and errors + * + * Rob McCool + * + */ + + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_conf_globals.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" + +#include + +typedef struct { + char *t_name; + int t_val; +} TRANS; + +#ifdef HAVE_SYSLOG + +static const TRANS facilities[] = { + {"auth", LOG_AUTH}, +#ifdef LOG_AUTHPRIV + {"authpriv",LOG_AUTHPRIV}, +#endif +#ifdef LOG_CRON + {"cron", LOG_CRON}, +#endif +#ifdef LOG_DAEMON + {"daemon", LOG_DAEMON}, +#endif +#ifdef LOG_FTP + {"ftp", LOG_FTP}, +#endif +#ifdef LOG_KERN + {"kern", LOG_KERN}, +#endif +#ifdef LOG_LPR + {"lpr", LOG_LPR}, +#endif +#ifdef LOG_MAIL + {"mail", LOG_MAIL}, +#endif +#ifdef LOG_NEWS + {"news", LOG_NEWS}, +#endif +#ifdef LOG_SYSLOG + {"syslog", LOG_SYSLOG}, +#endif +#ifdef LOG_USER + {"user", LOG_USER}, +#endif +#ifdef LOG_UUCP + {"uucp", LOG_UUCP}, +#endif +#ifdef LOG_LOCAL0 + {"local0", LOG_LOCAL0}, +#endif +#ifdef LOG_LOCAL1 + {"local1", LOG_LOCAL1}, +#endif +#ifdef LOG_LOCAL2 + {"local2", LOG_LOCAL2}, +#endif +#ifdef LOG_LOCAL3 + {"local3", LOG_LOCAL3}, +#endif +#ifdef LOG_LOCAL4 + {"local4", LOG_LOCAL4}, +#endif +#ifdef LOG_LOCAL5 + {"local5", LOG_LOCAL5}, +#endif +#ifdef LOG_LOCAL6 + {"local6", LOG_LOCAL6}, +#endif +#ifdef LOG_LOCAL7 + {"local7", LOG_LOCAL7}, +#endif + {NULL, -1}, +}; +#endif + +static const TRANS priorities[] = { + {"emerg", APLOG_EMERG}, + {"alert", APLOG_ALERT}, + {"crit", APLOG_CRIT}, + {"error", APLOG_ERR}, + {"warn", APLOG_WARNING}, + {"notice", APLOG_NOTICE}, + {"info", APLOG_INFO}, + {"debug", APLOG_DEBUG}, + {NULL, -1}, +}; + +static int error_log_child(void *cmd, child_info *pinfo) +{ + /* Child process code for 'ErrorLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + int child_pid = 0; + + ap_cleanup_for_exec(); +#ifdef SIGHUP + /* No concept of a child process on Win32 */ + signal(SIGHUP, SIG_IGN); +#endif /* ndef SIGHUP */ +#if defined(WIN32) + child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#elif defined(OS2) + /* For OS/2 we need to use a '/' and spawn the child rather than exec as + * we haven't forked */ + child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#else + execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + exit(1); + /* NOT REACHED */ + return(child_pid); +} + +static void open_error_log(server_rec *s, pool *p) +{ + char *fname; + + if (*s->error_fname == '|') { + FILE *dummy; +#ifdef TPF + TPF_FORK_CHILD cld; + cld.filename = s->error_fname+1; + cld.subprocess_env = NULL; + cld.prog_type = FORK_NAME; + if (!ap_spawn_child(p, NULL, &cld, + kill_after_timeout, &dummy, NULL, NULL)) { +#else + if (!ap_spawn_child(p, error_log_child, (void *)(s->error_fname+1), + kill_after_timeout, &dummy, NULL, NULL)) { +#endif /* TPF */ + perror("ap_spawn_child"); + fprintf(stderr, "Couldn't fork child for ErrorLog process\n"); + exit(1); + } + + s->error_log = dummy; + } + +#ifdef HAVE_SYSLOG + else if (!strncasecmp(s->error_fname, "syslog", 6)) { + if ((fname = strchr(s->error_fname, ':'))) { + const TRANS *fac; + + fname++; + for (fac = facilities; fac->t_name; fac++) { + if (!strcasecmp(fname, fac->t_name)) { + openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, + fac->t_val); + s->error_log = NULL; + return; + } + } + } + else + openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, LOG_LOCAL7); + + s->error_log = NULL; + } +#endif + else { + fname = ap_server_root_relative(p, s->error_fname); + if (!(s->error_log = ap_pfopen(p, fname, "a"))) { + perror("fopen"); + fprintf(stderr, "%s: could not open error log file %s.\n", + ap_server_argv0, fname); + exit(1); + } + } +} + +void ap_open_logs(server_rec *s_main, pool *p) +{ + server_rec *virt, *q; + int replace_stderr; + + open_error_log(s_main, p); + + replace_stderr = 1; + if (s_main->error_log) { + /* replace stderr with this new log */ + fflush(stderr); + if (dup2(fileno(s_main->error_log), STDERR_FILENO) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, s_main, + "unable to replace stderr with error_log"); + } else { + replace_stderr = 0; + } + } + /* note that stderr may still need to be replaced with something + * because it points to the old error log, or back to the tty + * of the submitter. + */ + if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) { + ap_log_error(APLOG_MARK, APLOG_CRIT, s_main, + "unable to replace stderr with /dev/null"); + } + + for (virt = s_main->next; virt; virt = virt->next) { + if (virt->error_fname) + { + for (q=s_main; q != virt; q = q->next) + if (q->error_fname != NULL && + strcmp(q->error_fname, virt->error_fname) == 0) + break; + if (q == virt) + open_error_log(virt, p); + else + virt->error_log = q->error_log; + } + else + virt->error_log = s_main->error_log; + } +} + +API_EXPORT(void) ap_error_log2stderr(server_rec *s) { + if ( s->error_log != NULL + && fileno(s->error_log) != STDERR_FILENO) + dup2(fileno(s->error_log), STDERR_FILENO); +} + +static void log_error_core(const char *file, int line, int level, + const server_rec *s, const request_rec *r, + const char *fmt, va_list args) +{ + char errstr[MAX_STRING_LEN]; + size_t len; + int save_errno = errno; + FILE *logf; + + if (s == NULL) { + /* + * If we are doing stderr logging (startup), don't log messages that are + * above the default server log level unless it is a startup/shutdown + * notice + */ + if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) && + ((level & APLOG_LEVELMASK) > DEFAULT_LOGLEVEL)) + return; + logf = stderr; + } + else if (s->error_log) { + /* + * If we are doing normal logging, don't log messages that are + * above the server log level unless it is a startup/shutdown notice + */ + if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) && + ((level & APLOG_LEVELMASK) > s->loglevel)) + return; + logf = s->error_log; + } +#ifdef TPF + else if (tpf_child) { + /* + * If we are doing normal logging, don't log messages that are + * above the server log level unless it is a startup/shutdown notice + */ + if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) && + ((level & APLOG_LEVELMASK) > s->loglevel)) + return; + logf = stderr; + } +#endif /* TPF */ + else { + /* + * If we are doing syslog logging, don't log messages that are + * above the server log level (including a startup/shutdown notice) + */ + if ((level & APLOG_LEVELMASK) > s->loglevel) + return; + logf = NULL; + } + + if (logf) { + len = ap_snprintf(errstr, sizeof(errstr), "[%s] ", ap_get_time()); + } else { + len = 0; + } + + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "[%s] ", priorities[level & APLOG_LEVELMASK].t_name); + +#ifndef TPF + if (file && (level & APLOG_LEVELMASK) == APLOG_DEBUG) { +#ifdef _OSD_POSIX + char tmp[256]; + char *e = strrchr(file, '/'); + + /* In OSD/POSIX, the compiler returns for __FILE__ + * a string like: __FILE__="*POSIX(/usr/include/stdio.h)" + * (it even returns an absolute path for sources in + * the current directory). Here we try to strip this + * down to the basename. + */ + if (e != NULL && e[1] != '\0') { + ap_snprintf(tmp, sizeof(tmp), "%s", &e[1]); + e = &tmp[strlen(tmp)-1]; + if (*e == ')') + *e = '\0'; + file = tmp; + } +#endif /*_OSD_POSIX*/ + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "%s(%d): ", file, line); + } +#endif /* TPF */ + if (r) { + /* XXX: TODO: add a method of selecting whether logged client + * addresses are in dotted quad or resolved form... dotted + * quad is the most secure, which is why I'm implementing it + * first. -djg + */ + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "[client %s] ", r->connection->remote_ip); + } + if (!(level & APLOG_NOERRNO) + && (save_errno != 0) +#ifdef WIN32 + && !(level & APLOG_WIN32ERROR) +#endif + ) { + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "(%d)%s: ", save_errno, strerror(save_errno)); + } +#ifdef WIN32 + if (level & APLOG_WIN32ERROR) { + int nChars; + int nErrorCode; + + nErrorCode = GetLastError(); + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "(%d)", nErrorCode); + + nChars = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + nErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) errstr + len, + sizeof(errstr) - len, + NULL + ); + len += nChars; + if (nChars == 0) { + /* Um, error occurred, but we can't recurse to log it again + * (and it would probably only fail anyway), so lets just + * log the numeric value. + */ + nErrorCode = GetLastError(); + len += ap_snprintf(errstr + len, sizeof(errstr) - len, + "(FormatMessage failed with code %d): ", + nErrorCode); + } + else { + /* FormatMessage put the message in the buffer, but it may + * have appended a newline (\r\n). So remove it and use + * ": " instead like the Unix errors. The error may also + * end with a . before the return - if so, trash it. + */ + if (len > 1 && errstr[len-2] == '\r' && errstr[len-1] == '\n') { + if (len > 2 && errstr[len-3] == '.') + len--; + errstr[len-2] = ':'; + errstr[len-1] = ' '; + } + } + } +#endif + + len += ap_vsnprintf(errstr + len, sizeof(errstr) - len, fmt, args); + + /* NULL if we are logging to syslog */ + if (logf) { + fputs(errstr, logf); + fputc('\n', logf); + fflush(logf); + } +#ifdef HAVE_SYSLOG + else { + syslog(level & APLOG_LEVELMASK, "%s", errstr); + } +#endif +} + +API_EXPORT(void) ap_log_error(const char *file, int line, int level, + const server_rec *s, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + log_error_core(file, line, level, s, NULL, fmt, args); + va_end(args); +} + +API_EXPORT(void) ap_log_rerror(const char *file, int line, int level, + const request_rec *r, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + log_error_core(file, line, level, r->server, r, fmt, args); + /* + * IF the error level is 'warning' or more severe, + * AND there isn't already error text associated with this request, + * THEN make the message text available to ErrorDocument and + * other error processors. This can be disabled by stuffing + * something, even an empty string, into the "error-notes" cell + * before calling this routine. + */ + va_end(args); + va_start(args,fmt); + if (((level & APLOG_LEVELMASK) <= APLOG_WARNING) + && (ap_table_get(r->notes, "error-notes") == NULL)) { + ap_table_setn(r->notes, "error-notes", + ap_pvsprintf(r->pool, fmt, args)); + } + va_end(args); +} + +void ap_log_pid(pool *p, char *fname) +{ + FILE *pid_file; + struct stat finfo; + static pid_t saved_pid = -1; + pid_t mypid; + + if (!fname) + return; + + fname = ap_server_root_relative(p, fname); + mypid = getpid(); + if (mypid != saved_pid && stat(fname, &finfo) == 0) { + /* USR1 and HUP call this on each restart. + * Only warn on first time through for this pid. + * + * XXX: Could just write first time through too, although + * that may screw up scripts written to do something + * based on the last modification time of the pid file. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + ap_psprintf(p, + "pid file %s overwritten -- Unclean shutdown of previous Apache run?", + fname) + ); + } + + if(!(pid_file = fopen(fname, "w"))) { + perror("fopen"); + fprintf(stderr, "%s: could not log pid to file %s\n", + ap_server_argv0, fname); + exit(1); + } + fprintf(pid_file, "%ld\n", (long)mypid); + fclose(pid_file); + saved_pid = mypid; +} + +API_EXPORT(void) ap_log_error_old(const char *err, server_rec *s) +{ + ap_log_error(APLOG_MARK, APLOG_ERR, s, "%s", err); +} + +API_EXPORT(void) ap_log_unixerr(const char *routine, const char *file, + const char *msg, server_rec *s) +{ + ap_log_error(file, 0, APLOG_ERR, s, "%s", msg); +} + +API_EXPORT(void) ap_log_printf(const server_rec *s, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + log_error_core(APLOG_MARK, APLOG_ERR, s, NULL, fmt, args); + va_end(args); +} + +API_EXPORT(void) ap_log_reason(const char *reason, const char *file, request_rec *r) +{ + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "access to %s failed for %s, reason: %s", + file, + ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME), + reason); +} + +API_EXPORT(void) ap_log_assert(const char *szExp, const char *szFile, int nLine) +{ + fprintf(stderr, "[%s] file %s, line %d, assertion \"%s\" failed\n", + ap_get_time(), szFile, nLine, szExp); +#ifndef WIN32 + /* unix assert does an abort leading to a core dump */ + abort(); +#else + exit(1); +#endif +} + +/* piped log support */ + +#ifndef NO_RELIABLE_PIPED_LOGS +/* forward declaration */ +static void piped_log_maintenance(int reason, void *data, ap_wait_t status); + +static int piped_log_spawn(piped_log *pl) +{ + int pid; + + ap_block_alarms(); + pid = fork(); + if (pid == 0) { + /* XXX: this needs porting to OS2 and WIN32 */ + /* XXX: need to check what open fds the logger is actually passed, + * XXX: and CGIs for that matter ... cleanup_for_exec *should* + * XXX: close all the relevant stuff, but hey, it could be broken. */ + RAISE_SIGSTOP(PIPED_LOG_SPAWN); + /* we're now in the child */ + close(STDIN_FILENO); + dup2(pl->fds[0], STDIN_FILENO); + + ap_cleanup_for_exec(); + signal(SIGCHLD, SIG_DFL); /* for HPUX */ + signal(SIGHUP, SIG_IGN); + execl(SHELL_PATH, SHELL_PATH, "-c", pl->program, NULL); + fprintf(stderr, + "piped_log_spawn: unable to exec %s -c '%s': %s\n", + SHELL_PATH, pl->program, strerror (errno)); + exit(1); + } + if (pid == -1) { + fprintf(stderr, + "piped_log_spawn: unable to fork(): %s\n", strerror (errno)); + ap_unblock_alarms(); + return -1; + } + ap_unblock_alarms(); + pl->pid = pid; + ap_register_other_child(pid, piped_log_maintenance, pl, pl->fds[1]); + return 0; +} + + +static void piped_log_maintenance(int reason, void *data, ap_wait_t status) +{ + piped_log *pl = data; + + switch (reason) { + case OC_REASON_DEATH: + case OC_REASON_LOST: + pl->pid = -1; + ap_unregister_other_child(pl); + if (pl->program == NULL) { + /* during a restart */ + break; + } + if (piped_log_spawn(pl) == -1) { + /* what can we do? This could be the error log we're having + * problems opening up... */ + fprintf(stderr, + "piped_log_maintenance: unable to respawn '%s': %s\n", + pl->program, strerror(errno)); + } + break; + + case OC_REASON_UNWRITABLE: + if (pl->pid != -1) { + kill(pl->pid, SIGTERM); + } + break; + + case OC_REASON_RESTART: + pl->program = NULL; + if (pl->pid != -1) { + kill(pl->pid, SIGTERM); + } + break; + + case OC_REASON_UNREGISTER: + break; + } +} + + +static void piped_log_cleanup(void *data) +{ + piped_log *pl = data; + + if (pl->pid != -1) { + kill(pl->pid, SIGTERM); + } + ap_unregister_other_child(pl); + close(pl->fds[0]); + close(pl->fds[1]); +} + + +static void piped_log_cleanup_for_exec(void *data) +{ + piped_log *pl = data; + + close(pl->fds[0]); + close(pl->fds[1]); +} + + +API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program) +{ + piped_log *pl; + + pl = ap_palloc(p, sizeof (*pl)); + pl->p = p; + pl->program = ap_pstrdup(p, program); + pl->pid = -1; + ap_block_alarms (); + if (pipe(pl->fds) == -1) { + int save_errno = errno; + ap_unblock_alarms(); + errno = save_errno; + return NULL; + } + ap_register_cleanup(p, pl, piped_log_cleanup, piped_log_cleanup_for_exec); + if (piped_log_spawn(pl) == -1) { + int save_errno = errno; + ap_kill_cleanup(p, pl, piped_log_cleanup); + close(pl->fds[0]); + close(pl->fds[1]); + ap_unblock_alarms(); + errno = save_errno; + return NULL; + } + ap_unblock_alarms(); + return pl; +} + +API_EXPORT(void) ap_close_piped_log(piped_log *pl) +{ + ap_block_alarms(); + piped_log_cleanup(pl); + ap_kill_cleanup(pl->p, pl, piped_log_cleanup); + ap_unblock_alarms(); +} + +#else +static int piped_log_child(void *cmd, child_info *pinfo) +{ + /* Child process code for 'TransferLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + int child_pid = 1; + + ap_cleanup_for_exec(); +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif +#if defined(WIN32) + child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#elif defined(OS2) + /* For OS/2 we need to use a '/' and spawn the child rather than exec as + * we haven't forked */ + child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); + return(child_pid); +#else + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + perror("exec"); + fprintf(stderr, "Exec of shell for logging failed!!!\n"); + return(child_pid); +} + + +API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program) +{ + piped_log *pl; + FILE *dummy; +#ifdef TPF + TPF_FORK_CHILD cld; + cld.filename = (char *)program; + cld.subprocess_env = NULL; + cld.prog_type = FORK_NAME; + + if (!ap_spawn_child (p, NULL, &cld, + kill_after_timeout, &dummy, NULL, NULL)){ +#else + if (!ap_spawn_child(p, piped_log_child, (void *)program, + kill_after_timeout, &dummy, NULL, NULL)) { +#endif /* TPF */ + perror("ap_spawn_child"); + fprintf(stderr, "Couldn't fork child for piped log process\n"); + exit (1); + } + pl = ap_palloc(p, sizeof (*pl)); + pl->p = p; + pl->write_f = dummy; + + return pl; +} + + +API_EXPORT(void) ap_close_piped_log(piped_log *pl) +{ + ap_pfclose(pl->p, pl->write_f); +} +#endif diff --git a/server/main.c b/server/main.c new file mode 100644 index 0000000000..5bd372bbc4 --- /dev/null +++ b/server/main.c @@ -0,0 +1,6669 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * httpd.c: simple http daemon for answering WWW file requests + * + * + * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3) + * + * 03-06-95 blong + * changed server number for child-alone processes to 0 and changed name + * of processes + * + * 03-10-95 blong + * Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu) + * including set group before fork, and call gettime before to fork + * to set up libraries. + * + * 04-14-95 rst / rh + * Brandon's code snarfed from NCSA 1.4, but tinkered to work with the + * Apache server, and also to have child processes do accept() directly. + * + * April-July '95 rst + * Extensive rework for Apache. + */ + +#ifndef SHARED_CORE_BOOTSTRAP +#ifndef SHARED_CORE_TIESTATIC + +#ifdef SHARED_CORE +#define REALMAIN ap_main +int ap_main(int argc, char *argv[]); +#else +#define REALMAIN main +#endif + +#define CORE_PRIVATE + +#include "httpd.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" /* for read_config */ +#include "http_protocol.h" /* for read_request */ +#include "http_request.h" /* for process_request */ +#include "http_conf_globals.h" +#include "http_core.h" /* for get_remote_host */ +#include "http_vhost.h" +#include "util_script.h" /* to force util_script.c linking */ +#include "util_uri.h" +#include "scoreboard.h" +#include "multithread.h" +#include +#ifdef USE_SHMGET_SCOREBOARD +#include +#include +#include +#endif +#ifdef SecureWare +#include +#include +#include +#endif +#ifdef WIN32 +#include "../os/win32/getopt.h" +#elif !defined(BEOS) && !defined(TPF) +#include +#endif + +#ifdef HAVE_BSTRING_H +#include /* for IRIX, FD_SET calls bzero() */ +#endif + +#ifdef MULTITHREAD +/* special debug stuff -- PCS */ + +/* Set this non-zero if you are prepared to put up with more than one log entry per second */ +#define SEVERELY_VERBOSE 0 + + /* APD1() to APD5() are macros to help us debug. They can either + * log to the screen or the error_log file. In release builds, these + * macros do nothing. In debug builds, they send messages at priority + * "debug" to the error log file, or if DEBUG_TO_CONSOLE is defined, + * to the console. + */ + +# ifdef _DEBUG +# ifndef DEBUG_TO_CONSOLE +# define APD1(a) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a) +# define APD2(a,b) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b) +# define APD3(a,b,c) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c) +# define APD4(a,b,c,d) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d) +# define APD5(a,b,c,d,e) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d,e) +# else +# define APD1(a) printf("%s\n",a) +# define APD2(a,b) do { printf(a,b);putchar('\n'); } while(0); +# define APD3(a,b,c) do { printf(a,b,c);putchar('\n'); } while(0); +# define APD4(a,b,c,d) do { printf(a,b,c,d);putchar('\n'); } while(0); +# define APD5(a,b,c,d,e) do { printf(a,b,c,d,e);putchar('\n'); } while(0); +# endif +# else /* !_DEBUG */ +# define APD1(a) +# define APD2(a,b) +# define APD3(a,b,c) +# define APD4(a,b,c,d) +# define APD5(a,b,c,d,e) +# endif /* _DEBUG */ +#endif /* MULTITHREAD */ + +/* This next function is never used. It is here to ensure that if we + * make all the modules into shared libraries that core httpd still + * includes the full Apache API. Without this function the objects in + * main/util_script.c would not be linked into a minimal httpd. + * And the extra prototype is to make gcc -Wmissing-prototypes quiet. + */ +extern void ap_force_library_loading(void); +void ap_force_library_loading(void) { + ap_add_cgi_vars(NULL); +} + +#include "explain.h" + +#if !defined(max) +#define max(a,b) (a > b ? a : b) +#endif + +#ifdef WIN32 +#include "../os/win32/service.h" +#include "../os/win32/registry.h" +#define DEFAULTSERVICENAME "Apache" +#define PATHSEPARATOR '\\' +#else +#define PATHSEPARATOR '/' +#endif + + +#ifdef MINT +long _stksize = 32768; +#endif + +#ifdef USE_OS2_SCOREBOARD + /* Add MMAP style functionality to OS/2 */ +#define INCL_DOSMEMMGR +#define INCL_DOSEXCEPTIONS +#define INCL_DOSSEMAPHORES +#include +#include +#include +caddr_t create_shared_heap(const char *, size_t); +caddr_t get_shared_heap(const char *); +#endif + +DEF_Explain + +/* Defining GPROF when compiling uses the moncontrol() function to + * disable gprof profiling in the parent, and enable it only for + * request processing in children (or in one_process mode). It's + * absolutely required to get useful gprof results under linux + * because the profile itimers and such are disabled across a + * fork(). It's probably useful elsewhere as well. + */ +#ifdef GPROF +extern void moncontrol(int); +#define MONCONTROL(x) moncontrol(x) +#else +#define MONCONTROL(x) +#endif + +#ifndef MULTITHREAD +/* this just need to be anything non-NULL */ +void *ap_dummy_mutex = &ap_dummy_mutex; +#endif + +/* + * Actual definitions of config globals... here because this is + * for the most part the only code that acts on 'em. (Hmmm... mod_main.c?) + */ + +int ap_standalone=0; +int ap_configtestonly=0; +int ap_docrootcheck=1; +uid_t ap_user_id=0; +char *ap_user_name=NULL; +gid_t ap_group_id=0; +#ifdef MULTIPLE_GROUPS +gid_t group_id_list[NGROUPS_MAX]; +#endif +int ap_max_requests_per_child=0; +int ap_threads_per_child=0; +int ap_excess_requests_per_child=0; +char *ap_pid_fname=NULL; +char *ap_scoreboard_fname=NULL; +char *ap_lock_fname; +char *ap_server_argv0=NULL; +struct in_addr ap_bind_address; +int ap_daemons_to_start=0; +int ap_daemons_min_free=0; +int ap_daemons_max_free=0; +int ap_daemons_limit=0; +time_t ap_restart_time=0; +int ap_suexec_enabled = 0; +int ap_listenbacklog; +int ap_dump_settings = 0; +API_VAR_EXPORT int ap_extended_status = 0; + +/* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across SIGUSR1 restarts. We use this + * value to optimize routines that have to scan the entire scoreboard. + */ +static int max_daemons_limit = -1; + +/* + * During config time, listeners is treated as a NULL-terminated list. + * child_main previously would start at the beginning of the list each time + * through the loop, so a socket early on in the list could easily starve out + * sockets later on in the list. The solution is to start at the listener + * after the last one processed. But to do that fast/easily in child_main it's + * way more convenient for listeners to be a ring that loops back on itself. + * The routine setup_listeners() is called after config time to both open up + * the sockets and to turn the NULL-terminated list into a ring that loops back + * on itself. + * + * head_listener is used by each child to keep track of what they consider + * to be the "start" of the ring. It is also set by make_child to ensure + * that new children also don't starve any sockets. + * + * Note that listeners != NULL is ensured by read_config(). + */ +listen_rec *ap_listeners; +static listen_rec *head_listener; + +API_VAR_EXPORT char ap_server_root[MAX_STRING_LEN]=""; +char ap_server_confname[MAX_STRING_LEN]=""; +char ap_coredump_dir[MAX_STRING_LEN]; + +array_header *ap_server_pre_read_config; +array_header *ap_server_post_read_config; +array_header *ap_server_config_defines; + +/* *Non*-shared http_main globals... */ + +static server_rec *server_conf; +static JMP_BUF APACHE_TLS jmpbuffer; +static int sd; +static fd_set listenfds; +static int listenmaxfd; +static pid_t pgrp; + +/* one_process --- debugging mode variable; can be set from the command line + * with the -X flag. If set, this gets you the child_main loop running + * in the process which originally started up (no detach, no make_child), + * which is a pretty nice debugging environment. (You'll get a SIGHUP + * early in standalone_main; just continue through. This is the server + * trying to kill off any child processes which it might have lying + * around --- Apache doesn't keep track of their pids, it just sends + * SIGHUP to the process group, ignoring it in the root process. + * Continue through and you'll be fine.). + */ + +static int one_process = 0; + +/* set if timeouts are to be handled by the children and not by the parent. + * i.e. child_timeouts = !standalone || one_process. + */ +static int child_timeouts; + +#ifdef DEBUG_SIGSTOP +int raise_sigstop_flags; +#endif + +#ifndef NO_OTHER_CHILD +/* used to maintain list of children which aren't part of the scoreboard */ +typedef struct other_child_rec other_child_rec; +struct other_child_rec { + other_child_rec *next; + int pid; + void (*maintenance) (int, void *, ap_wait_t); + void *data; + int write_fd; +}; +static other_child_rec *other_children; +#endif + +static pool *pglobal; /* Global pool */ +static pool *pconf; /* Pool for config stuff */ +static pool *plog; /* Pool for error-logging files */ +static pool *ptrans; /* Pool for per-transaction stuff */ +static pool *pchild; /* Pool for httpd child stuff */ +static pool *pcommands; /* Pool for -C and -c switches */ + +static int APACHE_TLS my_pid; /* it seems silly to call getpid all the time */ +#ifndef MULTITHREAD +static int my_child_num; +#endif + +#ifdef TPF +int tpf_child = 0; +char tpf_server_name[INETD_SERVNAME_LENGTH+1]; +#endif /* TPF */ + +scoreboard *ap_scoreboard_image = NULL; + +/* + * Pieces for managing the contents of the Server response header + * field. + */ +static char *server_version = NULL; +static int version_locked = 0; + +/* Global, alas, so http_core can talk to us */ +enum server_token_type ap_server_tokens = SrvTk_FULL; + +/* + * This routine is called when the pconf pool is vacuumed. It resets the + * server version string to a known value and [re]enables modifications + * (which are disabled by configuration completion). + */ +static void reset_version(void *dummy) +{ + version_locked = 0; + ap_server_tokens = SrvTk_FULL; + server_version = NULL; +} + +API_EXPORT(const char *) ap_get_server_version(void) +{ + return (server_version ? server_version : SERVER_BASEVERSION); +} + +API_EXPORT(void) ap_add_version_component(const char *component) +{ + if (! version_locked) { + /* + * If the version string is null, register our cleanup to reset the + * pointer on pool destruction. We also know that, if NULL, + * we are adding the original SERVER_BASEVERSION string. + */ + if (server_version == NULL) { + ap_register_cleanup(pconf, NULL, (void (*)(void *))reset_version, + ap_null_cleanup); + server_version = ap_pstrdup(pconf, component); + } + else { + /* + * Tack the given component identifier to the end of + * the existing string. + */ + server_version = ap_pstrcat(pconf, server_version, " ", + component, NULL); + } + } +} + +/* + * This routine adds the real server base identity to the version string, + * and then locks out changes until the next reconfig. + */ +static void ap_set_version(void) +{ + if (ap_server_tokens == SrvTk_MIN) { + ap_add_version_component(SERVER_BASEVERSION); + } + else { + ap_add_version_component(SERVER_BASEVERSION " (" PLATFORM ")"); + } + /* + * Lock the server_version string if we're not displaying + * the full set of tokens + */ + if (ap_server_tokens != SrvTk_FULL) { + version_locked++; + } +} + +static APACHE_TLS int volatile exit_after_unblock = 0; + +#ifdef GPROF +/* + * change directory for gprof to plop the gmon.out file + * configure in httpd.conf: + * GprofDir logs/ -> $ServerRoot/logs/gmon.out + * GprofDir logs/% -> $ServerRoot/logs/gprof.$pid/gmon.out + */ +static void chdir_for_gprof(void) +{ + core_server_config *sconf = + ap_get_module_config(server_conf->module_config, &core_module); + char *dir = sconf->gprof_dir; + + if(dir) { + char buf[512]; + int len = strlen(sconf->gprof_dir) - 1; + if(*(dir + len) == '%') { + dir[len] = '\0'; + ap_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid()); + } + dir = ap_server_root_relative(pconf, buf[0] ? buf : dir); + if(mkdir(dir, 0755) < 0 && errno != EEXIST) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "gprof: error creating directory %s", dir); + } + } + else { + dir = ap_server_root_relative(pconf, "logs"); + } + + chdir(dir); +} +#else +#define chdir_for_gprof() +#endif + +/* a clean exit from a child with proper cleanup */ +static void clean_child_exit(int code) __attribute__ ((noreturn)); +static void clean_child_exit(int code) +{ + if (pchild) { + ap_child_exit_modules(pchild, server_conf); + ap_destroy_pool(pchild); + } + chdir_for_gprof(); + exit(code); +} + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT) +static void expand_lock_fname(pool *p) +{ + /* XXXX possibly bogus cast */ + ap_lock_fname = ap_psprintf(p, "%s.%lu", + ap_server_root_relative(p, ap_lock_fname), (unsigned long)getpid()); +} +#endif + +#if defined (USE_USLOCK_SERIALIZED_ACCEPT) + +#include + +static ulock_t uslock = NULL; + +#define accept_mutex_child_init(x) + +static void accept_mutex_init(pool *p) +{ + ptrdiff_t old; + usptr_t *us; + + + /* default is 8, allocate enough for all the children plus the parent */ + if ((old = usconfig(CONF_INITUSERS, HARD_SERVER_LIMIT + 1)) == -1) { + perror("usconfig(CONF_INITUSERS)"); + exit(-1); + } + + if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) { + perror("usconfig(CONF_LOCKTYPE)"); + exit(-1); + } + if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) { + perror("usconfig(CONF_ARENATYPE)"); + exit(-1); + } + if ((us = usinit("/dev/zero")) == NULL) { + perror("usinit"); + exit(-1); + } + + if ((uslock = usnewlock(us)) == NULL) { + perror("usnewlock"); + exit(-1); + } +} + +static void accept_mutex_on(void) +{ + switch (ussetlock(uslock)) { + case 1: + /* got lock */ + break; + case 0: + fprintf(stderr, "didn't get lock\n"); + clean_child_exit(APEXIT_CHILDFATAL); + case -1: + perror("ussetlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + if (usunsetlock(uslock) == -1) { + perror("usunsetlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT) + +/* This code probably only works on Solaris ... but it works really fast + * on Solaris. Note that pthread mutexes are *NOT* released when a task + * dies ... the task has to free it itself. So we block signals and + * try to be nice about releasing the mutex. + */ + +#include + +static pthread_mutex_t *accept_mutex = (void *)(caddr_t) -1; +static int have_accept_mutex; +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; + +static void accept_mutex_child_cleanup(void *foo) +{ + if (accept_mutex != (void *)(caddr_t)-1 + && have_accept_mutex) { + pthread_mutex_unlock(accept_mutex); + } +} + +static void accept_mutex_child_init(pool *p) +{ + ap_register_cleanup(p, NULL, accept_mutex_child_cleanup, ap_null_cleanup); +} + +static void accept_mutex_cleanup(void *foo) +{ + if (accept_mutex != (void *)(caddr_t)-1 + && munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) { + perror("munmap"); + } + accept_mutex = (void *)(caddr_t)-1; +} + +static void accept_mutex_init(pool *p) +{ + pthread_mutexattr_t mattr; + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + perror("open(/dev/zero)"); + exit(APEXIT_INIT); + } + accept_mutex = (pthread_mutex_t *) mmap((caddr_t) 0, sizeof(*accept_mutex), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (accept_mutex == (void *) (caddr_t) - 1) { + perror("mmap"); + exit(APEXIT_INIT); + } + close(fd); + if ((errno = pthread_mutexattr_init(&mattr))) { + perror("pthread_mutexattr_init"); + exit(APEXIT_INIT); + } + if ((errno = pthread_mutexattr_setpshared(&mattr, + PTHREAD_PROCESS_SHARED))) { + perror("pthread_mutexattr_setpshared"); + exit(APEXIT_INIT); + } + if ((errno = pthread_mutex_init(accept_mutex, &mattr))) { + perror("pthread_mutex_init"); + exit(APEXIT_INIT); + } + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int err; + + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + clean_child_exit(APEXIT_CHILDFATAL); + } + if ((err = pthread_mutex_lock(accept_mutex))) { + errno = err; + perror("pthread_mutex_lock"); + clean_child_exit(APEXIT_CHILDFATAL); + } + have_accept_mutex = 1; +} + +static void accept_mutex_off(void) +{ + int err; + + if ((err = pthread_mutex_unlock(accept_mutex))) { + errno = err; + perror("pthread_mutex_unlock"); + clean_child_exit(APEXIT_CHILDFATAL); + } + /* There is a slight race condition right here... if we were to die right + * now, we'd do another pthread_mutex_unlock. Now, doing that would let + * another process into the mutex. pthread mutexes are designed to be + * fast, as such they don't have protection for things like testing if the + * thread owning a mutex is actually unlocking it (or even any way of + * testing who owns the mutex). + * + * If we were to unset have_accept_mutex prior to releasing the mutex + * then the race could result in the server unable to serve hits. Doing + * it this way means that the server can continue, but an additional + * child might be in the critical section ... at least it's still serving + * hits. + */ + have_accept_mutex = 0; + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + clean_child_exit(1); + } +} + +#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT) + +#include +#include +#include + +#ifdef NEED_UNION_SEMUN +/* it makes no sense, but this isn't defined on solaris */ +union semun { + long val; + struct semid_ds *buf; + ushort *array; +}; + +#endif + +static int sem_id = -1; +static struct sembuf op_on; +static struct sembuf op_off; + +/* We get a random semaphore ... the lame sysv semaphore interface + * means we have to be sure to clean this up or else we'll leak + * semaphores. + */ +static void accept_mutex_cleanup(void *foo) +{ + union semun ick; + + if (sem_id < 0) + return; + /* this is ignored anyhow */ + ick.val = 0; + semctl(sem_id, 0, IPC_RMID, ick); +} + +#define accept_mutex_child_init(x) + +static void accept_mutex_init(pool *p) +{ + union semun ick; + struct semid_ds buf; + + /* acquire the semaphore */ + sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600); + if (sem_id < 0) { + perror("semget"); + exit(APEXIT_INIT); + } + ick.val = 1; + if (semctl(sem_id, 0, SETVAL, ick) < 0) { + perror("semctl(SETVAL)"); + exit(APEXIT_INIT); + } + if (!getuid()) { + /* restrict it to use only by the appropriate user_id ... not that this + * stops CGIs from acquiring it and dinking around with it. + */ + buf.sem_perm.uid = ap_user_id; + buf.sem_perm.gid = ap_group_id; + buf.sem_perm.mode = 0600; + ick.buf = &buf; + if (semctl(sem_id, 0, IPC_SET, ick) < 0) { + perror("semctl(IPC_SET)"); + exit(APEXIT_INIT); + } + } + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + + /* pre-initialize these */ + op_on.sem_num = 0; + op_on.sem_op = -1; + op_on.sem_flg = SEM_UNDO; + op_off.sem_num = 0; + op_off.sem_op = 1; + op_off.sem_flg = SEM_UNDO; +} + +static void accept_mutex_on(void) +{ + while (semop(sem_id, &op_on, 1) < 0) { + if (errno != EINTR) { + perror("accept_mutex_on"); + clean_child_exit(APEXIT_CHILDFATAL); + } + } +} + +static void accept_mutex_off(void) +{ + while (semop(sem_id, &op_off, 1) < 0) { + if (errno != EINTR) { + perror("accept_mutex_off"); + clean_child_exit(APEXIT_CHILDFATAL); + } + } +} + +#elif defined(USE_FCNTL_SERIALIZED_ACCEPT) +static struct flock lock_it; +static struct flock unlock_it; + +static int lock_fd = -1; + +#define accept_mutex_child_init(x) + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + + lock_it.l_whence = SEEK_SET; /* from current point */ + lock_it.l_start = 0; /* -"- */ + lock_it.l_len = 0; /* until end of file */ + lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ + lock_it.l_pid = 0; /* pid not actually interesting */ + unlock_it.l_whence = SEEK_SET; /* from current point */ + unlock_it.l_start = 0; /* -"- */ + unlock_it.l_len = 0; /* until end of file */ + unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + expand_lock_fname(p); + lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (lock_fd == -1) { + perror("open"); + fprintf(stderr, "Cannot open lock file: %s\n", ap_lock_fname); + exit(APEXIT_INIT); + } + unlink(ap_lock_fname); +} + +static void accept_mutex_on(void) +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) { + /* nop */ + } + + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "fcntl: F_SETLKW: Error getting accept lock, exiting! " + "Perhaps you need to use the LockFile directive to place " + "your lock file on a local disk!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == EINTR) { + /* nop */ + } + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "fcntl: F_SETLKW: Error freeing accept lock, exiting! " + "Perhaps you need to use the LockFile directive to place " + "your lock file on a local disk!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT) + +static int lock_fd = -1; + +static void accept_mutex_cleanup(void *foo) +{ + unlink(ap_lock_fname); +} + +/* + * Initialize mutex lock. + * Done by each child at it's birth + */ +static void accept_mutex_child_init(pool *p) +{ + + lock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600); + if (lock_fd == -1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "Child cannot open lock file: %s", ap_lock_fname); + clean_child_exit(APEXIT_CHILDINIT); + } +} + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + expand_lock_fname(p); + unlink(ap_lock_fname); + lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600); + if (lock_fd == -1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "Parent cannot open lock file: %s", ap_lock_fname); + exit(APEXIT_INIT); + } + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int ret; + + while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "flock: LOCK_EX: Error getting accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + if (flock(lock_fd, LOCK_UN) < 0) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "flock: LOCK_UN: Error freeing accept lock. Exiting!"); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_OS2SEM_SERIALIZED_ACCEPT) + +static HMTX lock_sem = -1; + +static void accept_mutex_cleanup(void *foo) +{ + DosReleaseMutexSem(lock_sem); + DosCloseMutexSem(lock_sem); +} + +/* + * Initialize mutex lock. + * Done by each child at it's birth + */ +static void accept_mutex_child_init(pool *p) +{ + int rc = DosOpenMutexSem(NULL, &lock_sem); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Child cannot open lock semaphore, rc=%d", rc); + clean_child_exit(APEXIT_CHILDINIT); + } else { + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + } +} + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +static void accept_mutex_init(pool *p) +{ + int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Parent cannot create lock semaphore, rc=%d", rc); + exit(APEXIT_INIT); + } + + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); +} + +static void accept_mutex_on(void) +{ + int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "OS2SEM: Error %d getting accept lock. Exiting!", rc); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +static void accept_mutex_off(void) +{ + int rc = DosReleaseMutexSem(lock_sem); + + if (rc != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "OS2SEM: Error %d freeing accept lock. Exiting!", rc); + clean_child_exit(APEXIT_CHILDFATAL); + } +} + +#elif defined(USE_TPF_CORE_SERIALIZED_ACCEPT) + +static int tpf_core_held; + +static void accept_mutex_cleanup(void *foo) +{ + if(tpf_core_held) + coruc(RESOURCE_KEY); +} + +#define accept_mutex_init(x) + +static void accept_mutex_child_init(pool *p) +{ + ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup); + tpf_core_held = 0; +} + +static void accept_mutex_on(void) +{ + corhc(RESOURCE_KEY); + tpf_core_held = 1; + ap_check_signals(); +} + +static void accept_mutex_off(void) +{ + coruc(RESOURCE_KEY); + tpf_core_held = 0; + ap_check_signals(); +} + +#else +/* Default --- no serialization. Other methods *could* go here, + * as #elifs... + */ +#if !defined(MULTITHREAD) +/* Multithreaded systems don't complete between processes for + * the sockets. */ +#define NO_SERIALIZED_ACCEPT +#define accept_mutex_child_init(x) +#define accept_mutex_init(x) +#define accept_mutex_on() +#define accept_mutex_off() +#endif +#endif + +/* On some architectures it's safe to do unserialized accept()s in the single + * Listen case. But it's never safe to do it in the case where there's + * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT + * when it's safe in the single Listen case. + */ +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define SAFE_ACCEPT(stmt) do {if(ap_listeners->next != ap_listeners) {stmt;}} while(0) +#else +#define SAFE_ACCEPT(stmt) do {stmt;} while(0) +#endif + +static void usage(char *bin) +{ + char pad[MAX_STRING_LEN]; + unsigned i; + + for (i = 0; i < strlen(bin); i++) + pad[i] = ' '; + pad[i] = '\0'; +#ifdef SHARED_CORE + fprintf(stderr, "Usage: %s [-R directory] [-D name] [-d directory] [-f file]\n", bin); +#else + fprintf(stderr, "Usage: %s [-D name] [-d directory] [-f file]\n", bin); +#endif + fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"]\n", pad); + fprintf(stderr, " %s [-v] [-V] [-h] [-l] [-L] [-S] [-t] [-T]\n", pad); +#ifdef WIN32 + fprintf(stderr, " %s [-n service] [-k signal] [-i] [-u]\n", pad); +#endif + fprintf(stderr, "Options:\n"); +#ifdef SHARED_CORE + fprintf(stderr, " -R directory : specify an alternate location for shared object files\n"); +#endif + fprintf(stderr, " -D name : define a name for use in directives\n"); + fprintf(stderr, " -d directory : specify an alternate initial ServerRoot\n"); + fprintf(stderr, " -f file : specify an alternate ServerConfigFile\n"); + fprintf(stderr, " -C \"directive\" : process directive before reading config files\n"); + fprintf(stderr, " -c \"directive\" : process directive after reading config files\n"); + fprintf(stderr, " -v : show version number\n"); + fprintf(stderr, " -V : show compile settings\n"); + fprintf(stderr, " -h : list available command line options (this page)\n"); + fprintf(stderr, " -l : list compiled-in modules\n"); + fprintf(stderr, " -L : list available configuration directives\n"); + fprintf(stderr, " -S : show parsed settings (currently only vhost settings)\n"); + fprintf(stderr, " -t : run syntax check for config files (with docroot check)\n"); + fprintf(stderr, " -T : run syntax check for config files (without docroot check)\n"); +#ifdef WIN32 + fprintf(stderr, " -n name : set service name and use its ServerConfigFile\n"); + fprintf(stderr, " -k shutdown : tell running Apache to shutdown\n"); + fprintf(stderr, " -k restart : tell running Apache to do a graceful restart\n"); + fprintf(stderr, " -k start : tell Apache to start\n"); + fprintf(stderr, " -i : install an Apache service\n"); + fprintf(stderr, " -u : uninstall an Apache service\n"); +#endif + exit(1); +} + +/***************************************************************** + * + * Timeout handling. DISTINCTLY not thread-safe, but all this stuff + * has to change for threads anyway. Note that this code allows only + * one timeout in progress at a time... + */ + +static APACHE_TLS conn_rec *volatile current_conn; +static APACHE_TLS request_rec *volatile timeout_req; +static APACHE_TLS const char *volatile timeout_name = NULL; +static APACHE_TLS int volatile alarms_blocked = 0; +static APACHE_TLS int volatile alarm_pending = 0; + +static void timeout(int sig) +{ + void *dirconf; + + if (alarms_blocked) { + alarm_pending = 1; + return; + } + if (exit_after_unblock) { + clean_child_exit(0); + } + + if (!current_conn) { + ap_longjmp(jmpbuffer, 1); + } + + if (timeout_req != NULL) + dirconf = timeout_req->per_dir_config; + else + dirconf = current_conn->server->lookup_defaults; + if (!current_conn->keptalive) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + current_conn->server, "[client %s] %s timed out", + current_conn->remote_ip, + timeout_name ? timeout_name : "request"); + } + + if (timeout_req) { + /* Someone has asked for this transaction to just be aborted + * if it times out... + */ + + request_rec *log_req = timeout_req; + request_rec *save_req = timeout_req; + + /* avoid looping... if ap_log_transaction started another + * timer (say via rfc1413.c) we could loop... + */ + timeout_req = NULL; + + while (log_req->main || log_req->prev) { + /* Get back to original request... */ + if (log_req->main) + log_req = log_req->main; + else + log_req = log_req->prev; + } + + if (!current_conn->keptalive) { + /* in some cases we come here before setting the time */ + if (log_req->request_time == 0) { + log_req->request_time = time(0); + } + ap_log_transaction(log_req); + } + + ap_bsetflag(save_req->connection->client, B_EOUT, 1); + ap_bclose(save_req->connection->client); + + if (!ap_standalone) + exit(0); + + ap_longjmp(jmpbuffer, 1); + } + else { /* abort the connection */ + ap_bsetflag(current_conn->client, B_EOUT, 1); + ap_bclose(current_conn->client); + current_conn->aborted = 1; + } +} + +#ifndef TPF +/* + * These two called from alloc.c to protect its critical sections... + * Note that they can nest (as when destroying the sub_pools of a pool + * which is itself being cleared); we have to support that here. + */ + +API_EXPORT(void) ap_block_alarms(void) +{ + ++alarms_blocked; +} + +API_EXPORT(void) ap_unblock_alarms(void) +{ + --alarms_blocked; + if (alarms_blocked == 0) { + if (exit_after_unblock) { + /* We have a couple race conditions to deal with here, we can't + * allow a timeout that comes in this small interval to allow + * the child to jump back to the main loop. Instead we block + * alarms again, and then note that exit_after_unblock is + * being dealt with. We choose this way to solve this so that + * the common path through unblock_alarms() is really short. + */ + ++alarms_blocked; + exit_after_unblock = 0; + clean_child_exit(0); + } + if (alarm_pending) { + alarm_pending = 0; + timeout(0); + } + } +} +#endif /* TPF */ + +static APACHE_TLS void (*volatile alarm_fn) (int) = NULL; +#ifdef WIN32 +static APACHE_TLS unsigned int alarm_expiry_time = 0; +#endif /* WIN32 */ + +#ifndef WIN32 +static void alrm_handler(int sig) +{ + if (alarm_fn) { + (*alarm_fn) (sig); + } +} +#endif + +unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x) +{ + unsigned int old; + +#ifdef WIN32 + old = alarm_expiry_time; + if (old) + old -= time(0); + if (x == 0) { + alarm_fn = NULL; + alarm_expiry_time = 0; + } + else { + alarm_fn = fn; + alarm_expiry_time = time(NULL) + x; + } +#else + if (alarm_fn && x && fn != alarm_fn) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL, + "ap_set_callback_and_alarm: possible nested timer!"); + } + alarm_fn = fn; +#ifndef OPTIMIZE_TIMEOUTS + old = alarm(x); +#else + if (child_timeouts) { + old = alarm(x); + } + else { + /* Just note the timeout in our scoreboard, no need to call the system. + * We also note that the virtual time has gone forward. + */ + ap_check_signals(); + old = ap_scoreboard_image->servers[my_child_num].timeout_len; + ap_scoreboard_image->servers[my_child_num].timeout_len = x; + ++ap_scoreboard_image->servers[my_child_num].cur_vtime; + } +#endif +#endif + return (old); +} + + +#ifdef WIN32 +API_EXPORT(int) ap_check_alarm(void) +{ + if (alarm_expiry_time) { + unsigned int t; + + t = time(NULL); + if (t >= alarm_expiry_time) { + alarm_expiry_time = 0; + (*alarm_fn) (0); + return (-1); + } + else { + return (alarm_expiry_time - t); + } + } + else + return (0); +} +#endif /* WIN32 */ + + + +/* reset_timeout (request_rec *) resets the timeout in effect, + * as long as it hasn't expired already. + */ + +API_EXPORT(void) ap_reset_timeout(request_rec *r) +{ + int i; + + if (timeout_name) { /* timeout has been set */ + i = ap_set_callback_and_alarm(alarm_fn, r->server->timeout); + if (i == 0) /* timeout already expired, so set it back to 0 */ + ap_set_callback_and_alarm(alarm_fn, 0); + } +} + + + + +void ap_keepalive_timeout(char *name, request_rec *r) +{ + unsigned int to; + + timeout_req = r; + timeout_name = name; + + if (r->connection->keptalive) + to = r->server->keep_alive_timeout; + else + to = r->server->timeout; + ap_set_callback_and_alarm(timeout, to); + +} + +API_EXPORT(void) ap_hard_timeout(char *name, request_rec *r) +{ + timeout_req = r; + timeout_name = name; + + ap_set_callback_and_alarm(timeout, r->server->timeout); + +} + +API_EXPORT(void) ap_soft_timeout(char *name, request_rec *r) +{ + timeout_name = name; + + ap_set_callback_and_alarm(timeout, r->server->timeout); + +} + +API_EXPORT(void) ap_kill_timeout(request_rec *dummy) +{ + ap_check_signals(); + ap_set_callback_and_alarm(NULL, 0); + timeout_req = NULL; + timeout_name = NULL; +} + + +/* + * More machine-dependent networking gooo... on some systems, + * you've got to be *really* sure that all the packets are acknowledged + * before closing the connection, since the client will not be able + * to see the last response if their TCP buffer is flushed by a RST + * packet from us, which is what the server's TCP stack will send + * if it receives any request data after closing the connection. + * + * In an ideal world, this function would be accomplished by simply + * setting the socket option SO_LINGER and handling it within the + * server's TCP stack while the process continues on to the next request. + * Unfortunately, it seems that most (if not all) operating systems + * block the server process on close() when SO_LINGER is used. + * For those that don't, see USE_SO_LINGER below. For the rest, + * we have created a home-brew lingering_close. + * + * Many operating systems tend to block, puke, or otherwise mishandle + * calls to shutdown only half of the connection. You should define + * NO_LINGCLOSE in ap_config.h if such is the case for your system. + */ +#ifndef MAX_SECS_TO_LINGER +#define MAX_SECS_TO_LINGER 30 +#endif + +#ifdef USE_SO_LINGER +#define NO_LINGCLOSE /* The two lingering options are exclusive */ + +static void sock_enable_linger(int s) +{ + struct linger li; + + li.l_onoff = 1; + li.l_linger = MAX_SECS_TO_LINGER; + + if (setsockopt(s, SOL_SOCKET, SO_LINGER, + (char *) &li, sizeof(struct linger)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (SO_LINGER)"); + /* not a fatal error */ + } +} + +#else +#define sock_enable_linger(s) /* NOOP */ +#endif /* USE_SO_LINGER */ + +#ifndef NO_LINGCLOSE + +/* Special version of timeout for lingering_close */ + +static void lingerout(int sig) +{ + if (alarms_blocked) { + alarm_pending = 1; + return; + } + + if (!current_conn) { + ap_longjmp(jmpbuffer, 1); + } + ap_bsetflag(current_conn->client, B_EOUT, 1); + current_conn->aborted = 1; +} + +static void linger_timeout(void) +{ + timeout_name = "lingering close"; + + ap_set_callback_and_alarm(lingerout, MAX_SECS_TO_LINGER); +} + +/* Since many clients will abort a connection instead of closing it, + * attempting to log an error message from this routine will only + * confuse the webmaster. There doesn't seem to be any portable way to + * distinguish between a dropped connection and something that might be + * worth logging. + */ +static void lingering_close(request_rec *r) +{ + char dummybuf[512]; + struct timeval tv; + fd_set lfds; + int select_rv; + int lsd; + + /* Prevent a slow-drip client from holding us here indefinitely */ + + linger_timeout(); + + /* Send any leftover data to the client, but never try to again */ + + if (ap_bflush(r->connection->client) == -1) { + ap_kill_timeout(r); + ap_bclose(r->connection->client); + return; + } + ap_bsetflag(r->connection->client, B_EOUT, 1); + + /* Close our half of the connection --- send the client a FIN */ + + lsd = r->connection->client->fd; + + if ((shutdown(lsd, 1) != 0) || r->connection->aborted) { + ap_kill_timeout(r); + ap_bclose(r->connection->client); + return; + } + + /* Set up to wait for readable data on socket... */ + + FD_ZERO(&lfds); + + /* Wait for readable data or error condition on socket; + * slurp up any data that arrives... We exit when we go for an + * interval of tv length without getting any more data, get an error + * from select(), get an error or EOF on a read, or the timer expires. + */ + + do { + /* We use a 2 second timeout because current (Feb 97) browsers + * fail to close a connection after the server closes it. Thus, + * to avoid keeping the child busy, we are only lingering long enough + * for a client that is actively sending data on a connection. + * This should be sufficient unless the connection is massively + * losing packets, in which case we might have missed the RST anyway. + * These parameters are reset on each pass, since they might be + * changed by select. + */ + FD_SET(lsd, &lfds); + tv.tv_sec = 2; + tv.tv_usec = 0; + + select_rv = ap_select(lsd + 1, &lfds, NULL, NULL, &tv); + + } while ((select_rv > 0) && + (read(lsd, dummybuf, sizeof dummybuf) > 0)); + + /* Should now have seen final ack. Safe to finally kill socket */ + + ap_bclose(r->connection->client); + + ap_kill_timeout(r); +} +#endif /* ndef NO_LINGCLOSE */ + +/***************************************************************** + * dealing with other children + */ + +#ifndef NO_OTHER_CHILD +API_EXPORT(void) ap_register_other_child(int pid, + void (*maintenance) (int reason, void *, ap_wait_t status), + void *data, int write_fd) +{ + other_child_rec *ocr; + + ocr = ap_palloc(pconf, sizeof(*ocr)); + ocr->pid = pid; + ocr->maintenance = maintenance; + ocr->data = data; + ocr->write_fd = write_fd; + ocr->next = other_children; + other_children = ocr; +} + +/* note that since this can be called by a maintenance function while we're + * scanning the other_children list, all scanners should protect themself + * by loading ocr->next before calling any maintenance function. + */ +API_EXPORT(void) ap_unregister_other_child(void *data) +{ + other_child_rec **pocr, *nocr; + + for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) { + if ((*pocr)->data == data) { + nocr = (*pocr)->next; + (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1); + *pocr = nocr; + /* XXX: um, well we've just wasted some space in pconf ? */ + return; + } + } +} + +/* test to ensure that the write_fds are all still writable, otherwise + * invoke the maintenance functions as appropriate */ +static void probe_writable_fds(void) +{ + fd_set writable_fds; + int fd_max; + other_child_rec *ocr, *nocr; + struct timeval tv; + int rc; + + if (other_children == NULL) + return; + + fd_max = 0; + FD_ZERO(&writable_fds); + do { + for (ocr = other_children; ocr; ocr = ocr->next) { + if (ocr->write_fd == -1) + continue; + FD_SET(ocr->write_fd, &writable_fds); + if (ocr->write_fd > fd_max) { + fd_max = ocr->write_fd; + } + } + if (fd_max == 0) + return; + + tv.tv_sec = 0; + tv.tv_usec = 0; + rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv); + } while (rc == -1 && errno == EINTR); + + if (rc == -1) { + /* XXX: uhh this could be really bad, we could have a bad file + * descriptor due to a bug in one of the maintenance routines */ + ap_log_unixerr("probe_writable_fds", "select", + "could not probe writable fds", server_conf); + return; + } + if (rc == 0) + return; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->write_fd == -1) + continue; + if (FD_ISSET(ocr->write_fd, &writable_fds)) + continue; + (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1); + } +} + +/* possibly reap an other_child, return 0 if yes, -1 if not */ +static int reap_other_child(int pid, ap_wait_t status) +{ + other_child_rec *ocr, *nocr; + + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid != pid) + continue; + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + return 0; + } + return -1; +} +#endif + +/***************************************************************** + * + * Dealing with the scoreboard... a lot of these variables are global + * only to avoid getting clobbered by the longjmp() that happens when + * a hard timeout expires... + * + * We begin with routines which deal with the file itself... + */ + +#ifdef MULTITHREAD +/* + * In the multithreaded mode, have multiple threads - not multiple + * processes that need to talk to each other. Just use a simple + * malloc. But let the routines that follow, think that you have + * shared memory (so they use memcpy etc.) + */ + +static void reinit_scoreboard(pool *p) +{ + ap_assert(!ap_scoreboard_image); + ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE); + if (ap_scoreboard_image == NULL) { + fprintf(stderr, "Ouch! Out of memory reiniting scoreboard!\n"); + } + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); +} + +void cleanup_scoreboard(void) +{ + ap_assert(ap_scoreboard_image); + free(ap_scoreboard_image); + ap_scoreboard_image = NULL; +} + +API_EXPORT(void) ap_sync_scoreboard_image(void) +{ +} + + +#else /* MULTITHREAD */ +#if defined(USE_OS2_SCOREBOARD) + +/* The next two routines are used to access shared memory under OS/2. */ +/* This requires EMX v09c to be installed. */ + +caddr_t create_shared_heap(const char *name, size_t size) +{ + ULONG rc; + void *mem; + Heap_t h; + + rc = DosAllocSharedMem(&mem, name, size, + PAG_COMMIT | PAG_READ | PAG_WRITE); + if (rc != 0) + return NULL; + h = _ucreate(mem, size, !_BLOCK_CLEAN, _HEAP_REGULAR | _HEAP_SHARED, + NULL, NULL); + if (h == NULL) + DosFreeMem(mem); + return (caddr_t) h; +} + +caddr_t get_shared_heap(const char *Name) +{ + + PVOID BaseAddress; /* Pointer to the base address of + the shared memory object */ + ULONG AttributeFlags; /* Flags describing characteristics + of the shared memory object */ + APIRET rc; /* Return code */ + + /* Request read and write access to */ + /* the shared memory object */ + AttributeFlags = PAG_WRITE | PAG_READ; + + rc = DosGetNamedSharedMem(&BaseAddress, Name, AttributeFlags); + + if (rc != 0) { + printf("DosGetNamedSharedMem error: return code = %ld", rc); + return 0; + } + + return BaseAddress; +} + +static void setup_shared_mem(pool *p) +{ + caddr_t m; + + int rc; + + m = (caddr_t) create_shared_heap("\\SHAREMEM\\SCOREBOARD", SCOREBOARD_SIZE); + if (m == 0) { + fprintf(stderr, "%s: Could not create OS/2 Shared memory pool.\n", + ap_server_argv0); + exit(APEXIT_INIT); + } + + rc = _uopen((Heap_t) m); + if (rc != 0) { + fprintf(stderr, + "%s: Could not uopen() newly created OS/2 Shared memory pool.\n", + ap_server_argv0); + } + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ + caddr_t m; + int rc; + + m = (caddr_t) get_shared_heap("\\SHAREMEM\\SCOREBOARD"); + if (m == 0) { + fprintf(stderr, "%s: Could not find existing OS/2 Shared memory pool.\n", + ap_server_argv0); + exit(APEXIT_INIT); + } + + rc = _uopen((Heap_t) m); + ap_scoreboard_image = (scoreboard *) m; +} + +#elif defined(USE_POSIX_SCOREBOARD) +#include +/* + * POSIX 1003.4 style + * + * Note 1: + * As of version 4.23A, shared memory in QNX must reside under /dev/shmem, + * where no subdirectories allowed. + * + * POSIX shm_open() and shm_unlink() will take care about this issue, + * but to avoid confusion, I suggest to redefine scoreboard file name + * in httpd.conf to cut "logs/" from it. With default setup actual name + * will be "/dev/shmem/logs.apache_status". + * + * If something went wrong and Apache did not unlinked this object upon + * exit, you can remove it manually, using "rm -f" command. + * + * Note 2: + * in QNX defines MAP_ANON, but current implementation + * does NOT support BSD style anonymous mapping. So, the order of + * conditional compilation is important: + * this #ifdef section must be ABOVE the next one (BSD style). + * + * I tested this stuff and it works fine for me, but if it provides + * trouble for you, just comment out USE_MMAP_SCOREBOARD in QNX section + * of ap_config.h + * + * June 5, 1997, + * Igor N. Kovalenko -- infoh@mail.wplus.net + */ + +static void cleanup_shared_mem(void *d) +{ + shm_unlink(ap_scoreboard_fname); +} + +static void setup_shared_mem(pool *p) +{ + char buf[512]; + caddr_t m; + int fd; + + fd = shm_open(ap_scoreboard_fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + ap_snprintf(buf, sizeof(buf), "%s: could not open(create) scoreboard", + ap_server_argv0); + perror(buf); + exit(APEXIT_INIT); + } + if (ltrunc(fd, (off_t) SCOREBOARD_SIZE, SEEK_SET) == -1) { + ap_snprintf(buf, sizeof(buf), "%s: could not ltrunc scoreboard", + ap_server_argv0); + perror(buf); + shm_unlink(ap_scoreboard_fname); + exit(APEXIT_INIT); + } + if ((m = (caddr_t) mmap((caddr_t) 0, + (size_t) SCOREBOARD_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, (off_t) 0)) == (caddr_t) - 1) { + ap_snprintf(buf, sizeof(buf), "%s: cannot mmap scoreboard", + ap_server_argv0); + perror(buf); + shm_unlink(ap_scoreboard_fname); + exit(APEXIT_INIT); + } + close(fd); + ap_register_cleanup(p, NULL, cleanup_shared_mem, ap_null_cleanup); + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_MMAP_SCOREBOARD) + +static void setup_shared_mem(pool *p) +{ + caddr_t m; + +#if defined(MAP_ANON) +/* BSD style */ +#ifdef CONVEXOS11 + /* + * 9-Aug-97 - Jeff Venters (venters@convex.hp.com) + * ConvexOS maps address space as follows: + * 0x00000000 - 0x7fffffff : Kernel + * 0x80000000 - 0xffffffff : User + * Start mmapped area 1GB above start of text. + * + * Also, the length requires a pointer as the actual length is + * returned (rounded up to a page boundary). + */ + { + unsigned len = SCOREBOARD_SIZE; + + m = mmap((caddr_t) 0xC0000000, &len, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0); + } +#elif defined(MAP_TMPFILE) + { + char mfile[] = "/tmp/apache_shmem_XXXX"; + int fd = mkstemp(mfile); + if (fd == -1) { + perror("open"); + fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile); + exit(APEXIT_INIT); + } + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile); + exit(APEXIT_INIT); + } + close(fd); + unlink(mfile); + } +#else + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); +#endif + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0); + exit(APEXIT_INIT); + } +#else +/* Sun style */ + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) { + perror("open"); + fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0); + exit(APEXIT_INIT); + } + m = mmap((caddr_t) 0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t) - 1) { + perror("mmap"); + fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0); + exit(APEXIT_INIT); + } + close(fd); +#endif + ap_scoreboard_image = (scoreboard *) m; + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_SHMGET_SCOREBOARD) +static key_t shmkey = IPC_PRIVATE; +static int shmid = -1; + +static void setup_shared_mem(pool *p) +{ + struct shmid_ds shmbuf; +#ifdef MOVEBREAK + char *obrk; +#endif + + if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) { +#ifdef LINUX + if (errno == ENOSYS) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf, + "Your kernel was built without CONFIG_SYSVIPC\n" + "%s: Please consult the Apache FAQ for details", + ap_server_argv0); + } +#endif + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "could not call shmget"); + exit(APEXIT_INIT); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "created shared memory segment #%d", shmid); + +#ifdef MOVEBREAK + /* + * Some SysV systems place the shared segment WAY too close + * to the dynamic memory break point (sbrk(0)). This severely + * limits the use of malloc/sbrk in the program since sbrk will + * refuse to move past that point. + * + * To get around this, we move the break point "way up there", + * attach the segment and then move break back down. Ugly + */ + if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not move break"); + } +#endif + +#define BADSHMAT ((scoreboard *)(-1)) + if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) { + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error"); + /* + * We exit below, after we try to remove the segment + */ + } + else { /* only worry about permissions if we attached the segment */ + if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl() could not stat segment #%d", shmid); + } + else { + shmbuf.shm_perm.uid = ap_user_id; + shmbuf.shm_perm.gid = ap_group_id; + if (shmctl(shmid, IPC_SET, &shmbuf) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "shmctl() could not set segment #%d", shmid); + } + } + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "shmctl: IPC_RMID: could not remove shared memory segment #%d", + shmid); + } + if (ap_scoreboard_image == BADSHMAT) /* now bailout */ + exit(APEXIT_INIT); + +#ifdef MOVEBREAK + if (obrk == (char *) -1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *) -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "sbrk() could not move break back"); + } +#endif + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ +} + +#elif defined(USE_TPF_SCOREBOARD) + +static void cleanup_scoreboard_heap() +{ + int rv; + rv = rsysc(ap_scoreboard_image, SCOREBOARD_FRAMES, SCOREBOARD_NAME); + if(rv == RSYSC_ERROR) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "rsysc() could not release scoreboard system heap"); + } +} + +static void setup_shared_mem(pool *p) +{ + cinfc(CINFC_WRITE, CINFC_CMMCTK2); + ap_scoreboard_image = (scoreboard *) gsysc(SCOREBOARD_FRAMES, SCOREBOARD_NAME); + + if (!ap_scoreboard_image) { + fprintf(stderr, "httpd: Could not create scoreboard system heap storage.\n"); + exit(APEXIT_INIT); + } + + ap_register_cleanup(p, NULL, cleanup_scoreboard_heap, ap_null_cleanup); + ap_scoreboard_image->global.running_generation = 0; +} + +static void reopen_scoreboard(pool *p) +{ + cinfc(CINFC_WRITE, CINFC_CMMCTK2); +} + +#else +#define SCOREBOARD_FILE +static scoreboard _scoreboard_image; +static int scoreboard_fd = -1; + +/* XXX: things are seriously screwed if we ever have to do a partial + * read or write ... we could get a corrupted scoreboard + */ +static int force_write(int fd, void *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = write(fd, buffer, bufsz); + if (rv > 0) { + buffer = (char *) buffer + rv; + bufsz -= rv; + } + } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR)); + + return rv < 0 ? rv : orig_sz - bufsz; +} + +static int force_read(int fd, void *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = read(fd, buffer, bufsz); + if (rv > 0) { + buffer = (char *) buffer + rv; + bufsz -= rv; + } + } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR)); + + return rv < 0 ? rv : orig_sz - bufsz; +} + +static void cleanup_scoreboard_file(void *foo) +{ + unlink(ap_scoreboard_fname); +} + +void reopen_scoreboard(pool *p) +{ + if (scoreboard_fd != -1) + ap_pclosef(p, scoreboard_fd); + +#ifdef TPF + ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); +#endif /* TPF */ + scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0666); + if (scoreboard_fd == -1) { + perror(ap_scoreboard_fname); + fprintf(stderr, "Cannot open scoreboard file:\n"); + clean_child_exit(1); + } +} +#endif + +/* Called by parent process */ +static void reinit_scoreboard(pool *p) +{ + int running_gen = 0; + if (ap_scoreboard_image) + running_gen = ap_scoreboard_image->global.running_generation; + +#ifndef SCOREBOARD_FILE + if (ap_scoreboard_image == NULL) { + setup_shared_mem(p); + } + memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE); + ap_scoreboard_image->global.running_generation = running_gen; +#else + ap_scoreboard_image = &_scoreboard_image; + ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname); + + scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644); + if (scoreboard_fd == -1) { + perror(ap_scoreboard_fname); + fprintf(stderr, "Cannot open scoreboard file:\n"); + exit(APEXIT_INIT); + } + ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup); + + memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image)); + ap_scoreboard_image->global.running_generation = running_gen; + force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); +#endif +} + +/* Routines called to deal with the scoreboard image + * --- note that we do *not* need write locks, since update_child_status + * only updates a *single* record in place, and only one process writes to + * a given scoreboard slot at a time (either the child process owning that + * slot, or the parent, noting that the child has died). + * + * As a final note --- setting the score entry to getpid() is always safe, + * since when the parent is writing an entry, it's only noting SERVER_DEAD + * anyway. + */ + +ap_inline void ap_sync_scoreboard_image(void) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, 0L, 0); + force_read(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image)); +#endif +} + +#endif /* MULTITHREAD */ + +API_EXPORT(int) ap_exists_scoreboard_image(void) +{ + return (ap_scoreboard_image ? 1 : 0); +} + +static ap_inline void put_scoreboard_info(int child_num, + short_score *new_score_rec) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, (long) child_num * sizeof(short_score), 0); + force_write(scoreboard_fd, new_score_rec, sizeof(short_score)); +#endif +} + +/* a clean exit from the parent with proper cleanup */ +static void clean_parent_exit(int code) __attribute__((noreturn)); +static void clean_parent_exit(int code) +{ + /* Clear the pool - including any registered cleanups */ + ap_destroy_pool(pglobal); + exit(code); +} + +int ap_update_child_status(int child_num, int status, request_rec *r) +{ + int old_status; + short_score *ss; + + if (child_num < 0) + return -1; + + ap_check_signals(); + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + old_status = ss->status; + ss->status = status; +#ifdef OPTIMIZE_TIMEOUTS + ++ss->cur_vtime; +#endif + + if (ap_extended_status) { +#ifndef OPTIMIZE_TIMEOUTS + ss->last_used = time(NULL); +#endif + if (status == SERVER_READY || status == SERVER_DEAD) { + /* + * Reset individual counters + */ + if (status == SERVER_DEAD) { + ss->my_access_count = 0L; + ss->my_bytes_served = 0L; + } + ss->conn_count = (unsigned short) 0; + ss->conn_bytes = (unsigned long) 0; + } + if (r) { + conn_rec *c = r->connection; + ap_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config, + REMOTE_NOLOOKUP), sizeof(ss->client)); + if (r->the_request == NULL) { + ap_cpystrn(ss->request, "NULL", sizeof(ss->request)); + } else if (r->parsed_uri.password == NULL) { + ap_cpystrn(ss->request, r->the_request, sizeof(ss->request)); + } else { + /* Don't reveal the password in the server-status view */ + ap_cpystrn(ss->request, ap_pstrcat(r->pool, r->method, " ", + ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD), + r->assbackwards ? NULL : " ", r->protocol, NULL), + sizeof(ss->request)); + } + ss->vhostrec = r->server; + } + } + if (status == SERVER_STARTING && r == NULL) { + /* clean up the slot's vhostrec pointer (maybe re-used) + * and mark the slot as belonging to a new generation. + */ + ss->vhostrec = NULL; + ap_scoreboard_image->parent[child_num].generation = ap_my_generation; +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[child_num]), 0); + force_write(scoreboard_fd, &ap_scoreboard_image->parent[child_num], + sizeof(parent_score)); +#endif + } + put_scoreboard_info(child_num, ss); + + return old_status; +} + +static void update_scoreboard_global(void) +{ +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, + (char *) &ap_scoreboard_image->global -(char *) ap_scoreboard_image, 0); + force_write(scoreboard_fd, &ap_scoreboard_image->global, + sizeof ap_scoreboard_image->global); +#endif +} + +void ap_time_process_request(int child_num, int status) +{ + short_score *ss; +#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES) + struct tms tms_blk; +#endif + + if (child_num < 0) + return; + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + + if (status == START_PREQUEST) { +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if ((ss->start_time = times(&tms_blk)) == -1) +#endif /* NO_TIMES */ + ss->start_time = (clock_t) 0; +#else + if (gettimeofday(&ss->start_time, (struct timezone *) 0) < 0) + ss->start_time.tv_sec = + ss->start_time.tv_usec = 0L; +#endif + } + else if (status == STOP_PREQUEST) { +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if ((ss->stop_time = times(&tms_blk)) == -1) +#endif + ss->stop_time = ss->start_time = (clock_t) 0; +#else + if (gettimeofday(&ss->stop_time, (struct timezone *) 0) < 0) + ss->stop_time.tv_sec = + ss->stop_time.tv_usec = + ss->start_time.tv_sec = + ss->start_time.tv_usec = 0L; +#endif + + } + + put_scoreboard_info(child_num, ss); +} + +static void increment_counts(int child_num, request_rec *r) +{ + long int bs = 0; + short_score *ss; + + ap_sync_scoreboard_image(); + ss = &ap_scoreboard_image->servers[child_num]; + + if (r->sent_bodyct) + ap_bgetopt(r->connection->client, BO_BYTECT, &bs); + +#ifndef NO_TIMES + times(&ss->times); +#endif + ss->access_count++; + ss->my_access_count++; + ss->conn_count++; + ss->bytes_served += (unsigned long) bs; + ss->my_bytes_served += (unsigned long) bs; + ss->conn_bytes += (unsigned long) bs; + + put_scoreboard_info(child_num, ss); +} + +static int find_child_by_pid(int pid) +{ + int i; + + for (i = 0; i < max_daemons_limit; ++i) + if (ap_scoreboard_image->parent[i].pid == pid) + return i; + + return -1; +} + +static void reclaim_child_processes(int terminate) +{ +#ifndef MULTITHREAD + int i, status; + long int waittime = 1024 * 16; /* in usecs */ + struct timeval tv; + int waitret, tries; + int not_dead_yet; +#ifndef NO_OTHER_CHILD + other_child_rec *ocr, *nocr; +#endif + + ap_sync_scoreboard_image(); + + for (tries = terminate ? 4 : 1; tries <= 9; ++tries) { + /* don't want to hold up progress any more than + * necessary, but we need to allow children a few moments to exit. + * Set delay with an exponential backoff. + */ + tv.tv_sec = waittime / 1000000; + tv.tv_usec = waittime % 1000000; + waittime = waittime * 4; + ap_select(0, NULL, NULL, NULL, &tv); + + /* now see who is done */ + not_dead_yet = 0; + for (i = 0; i < max_daemons_limit; ++i) { + int pid = ap_scoreboard_image->parent[i].pid; + + if (pid == my_pid || pid == 0) + continue; + + waitret = waitpid(pid, &status, WNOHANG); + if (waitret == pid || waitret == -1) { + ap_scoreboard_image->parent[i].pid = 0; + continue; + } + ++not_dead_yet; + switch (tries) { + case 1: /* 16ms */ + case 2: /* 82ms */ + break; + case 3: /* 344ms */ + /* perhaps it missed the SIGHUP, lets try again */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, + server_conf, + "child process %d did not exit, sending another SIGHUP", + pid); + kill(pid, SIGHUP); + waittime = 1024 * 16; + break; + case 4: /* 16ms */ + case 5: /* 82ms */ + case 6: /* 344ms */ + break; + case 7: /* 1.4sec */ + /* ok, now it's being annoying */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, + server_conf, + "child process %d still did not exit, sending a SIGTERM", + pid); + kill(pid, SIGTERM); + break; + case 8: /* 6 sec */ + /* die child scum */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "child process %d still did not exit, sending a SIGKILL", + pid); + kill(pid, SIGKILL); + break; + case 9: /* 14 sec */ + /* gave it our best shot, but alas... If this really + * is a child we are trying to kill and it really hasn't + * exited, we will likely fail to bind to the port + * after the restart. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "could not make child process %d exit, " + "attempting to continue anyway", pid); + break; + } + } +#ifndef NO_OTHER_CHILD + for (ocr = other_children; ocr; ocr = nocr) { + nocr = ocr->next; + if (ocr->pid == -1) + continue; + + waitret = waitpid(ocr->pid, &status, WNOHANG); + if (waitret == ocr->pid) { + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status); + } + else if (waitret == 0) { + (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1); + ++not_dead_yet; + } + else if (waitret == -1) { + /* uh what the heck? they didn't call unregister? */ + ocr->pid = -1; + (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1); + } + } +#endif + if (!not_dead_yet) { + /* nothing left to wait for */ + break; + } + } +#endif /* ndef MULTITHREAD */ +} + + +#if defined(NEED_WAITPID) +/* + Systems without a real waitpid sometimes lose a child's exit while waiting + for another. Search through the scoreboard for missing children. + */ +int reap_children(ap_wait_t *status) +{ + int n, pid; + + for (n = 0; n < max_daemons_limit; ++n) { + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->servers[n].status != SERVER_DEAD && + kill((pid = ap_scoreboard_image->parent[n].pid), 0) == -1) { + ap_update_child_status(n, SERVER_DEAD, NULL); + /* just mark it as having a successful exit status */ + bzero((char *) status, sizeof(ap_wait_t)); + return(pid); + } + } + return 0; +} +#endif + +/* Finally, this routine is used by the caretaker process to wait for + * a while... + */ + +/* number of calls to wait_or_timeout between writable probes */ +#ifndef INTERVAL_OF_WRITABLE_PROBES +#define INTERVAL_OF_WRITABLE_PROBES 10 +#endif +static int wait_or_timeout_counter; + +static int wait_or_timeout(ap_wait_t *status) +{ +#ifdef WIN32 +#define MAXWAITOBJ MAXIMUM_WAIT_OBJECTS + HANDLE h[MAXWAITOBJ]; + int e[MAXWAITOBJ]; + int round, pi, hi, rv, err; + for (round = 0; round <= (HARD_SERVER_LIMIT - 1) / MAXWAITOBJ + 1; round++) { + hi = 0; + for (pi = round * MAXWAITOBJ; + (pi < (round + 1) * MAXWAITOBJ) && (pi < HARD_SERVER_LIMIT); + pi++) { + if (ap_scoreboard_image->servers[pi].status != SERVER_DEAD) { + e[hi] = pi; + h[hi++] = (HANDLE) ap_scoreboard_image->parent[pi].pid; + } + + } + if (hi > 0) { + rv = WaitForMultipleObjects(hi, h, FALSE, 10000); + if (rv == -1) + err = GetLastError(); + if ((WAIT_OBJECT_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_OBJECT_0 + hi))) + return (ap_scoreboard_image->parent[e[rv - WAIT_OBJECT_0]].pid); + else if ((WAIT_ABANDONED_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_ABANDONED_0 + hi))) + return (ap_scoreboard_image->parent[e[rv - WAIT_ABANDONED_0]].pid); + + } + } + return (-1); + +#else /* WIN32 */ + struct timeval tv; + int ret; + + ++wait_or_timeout_counter; + if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) { + wait_or_timeout_counter = 0; +#ifndef NO_OTHER_CHILD + probe_writable_fds(); +#endif + } + ret = waitpid(-1, status, WNOHANG); + if (ret == -1 && errno == EINTR) { + return -1; + } + if (ret > 0) { + return ret; + } +#ifdef NEED_WAITPID + if ((ret = reap_children(status)) > 0) { + return ret; + } +#endif + tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000; + tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000; + ap_select(0, NULL, NULL, NULL, &tv); + return -1; +#endif /* WIN32 */ +} + + +#if defined(NSIG) +#define NumSIG NSIG +#elif defined(_NSIG) +#define NumSIG _NSIG +#elif defined(__NSIG) +#define NumSIG __NSIG +#else +#define NumSIG 32 /* for 1998's unixes, this is still a good assumption */ +#endif + +#ifdef SYS_SIGLIST /* platform has sys_siglist[] */ +#define INIT_SIGLIST() /*nothing*/ +#else /* platform has no sys_siglist[], define our own */ +#define SYS_SIGLIST ap_sys_siglist +#define INIT_SIGLIST() siglist_init(); + +const char *ap_sys_siglist[NumSIG]; + +static void siglist_init(void) +{ + int sig; + + ap_sys_siglist[0] = "Signal 0"; +#ifdef SIGHUP + ap_sys_siglist[SIGHUP] = "Hangup"; +#endif +#ifdef SIGINT + ap_sys_siglist[SIGINT] = "Interrupt"; +#endif +#ifdef SIGQUIT + ap_sys_siglist[SIGQUIT] = "Quit"; +#endif +#ifdef SIGILL + ap_sys_siglist[SIGILL] = "Illegal instruction"; +#endif +#ifdef SIGTRAP + ap_sys_siglist[SIGTRAP] = "Trace/BPT trap"; +#endif +#ifdef SIGIOT + ap_sys_siglist[SIGIOT] = "IOT instruction"; +#endif +#ifdef SIGABRT + ap_sys_siglist[SIGABRT] = "Abort"; +#endif +#ifdef SIGEMT + ap_sys_siglist[SIGEMT] = "Emulator trap"; +#endif +#ifdef SIGFPE + ap_sys_siglist[SIGFPE] = "Arithmetic exception"; +#endif +#ifdef SIGKILL + ap_sys_siglist[SIGKILL] = "Killed"; +#endif +#ifdef SIGBUS + ap_sys_siglist[SIGBUS] = "Bus error"; +#endif +#ifdef SIGSEGV + ap_sys_siglist[SIGSEGV] = "Segmentation fault"; +#endif +#ifdef SIGSYS + ap_sys_siglist[SIGSYS] = "Bad system call"; +#endif +#ifdef SIGPIPE + ap_sys_siglist[SIGPIPE] = "Broken pipe"; +#endif +#ifdef SIGALRM + ap_sys_siglist[SIGALRM] = "Alarm clock"; +#endif +#ifdef SIGTERM + ap_sys_siglist[SIGTERM] = "Terminated"; +#endif +#ifdef SIGUSR1 + ap_sys_siglist[SIGUSR1] = "User defined signal 1"; +#endif +#ifdef SIGUSR2 + ap_sys_siglist[SIGUSR2] = "User defined signal 2"; +#endif +#ifdef SIGCLD + ap_sys_siglist[SIGCLD] = "Child status change"; +#endif +#ifdef SIGCHLD + ap_sys_siglist[SIGCHLD] = "Child status change"; +#endif +#ifdef SIGPWR + ap_sys_siglist[SIGPWR] = "Power-fail restart"; +#endif +#ifdef SIGWINCH + ap_sys_siglist[SIGWINCH] = "Window changed"; +#endif +#ifdef SIGURG + ap_sys_siglist[SIGURG] = "urgent socket condition"; +#endif +#ifdef SIGPOLL + ap_sys_siglist[SIGPOLL] = "Pollable event occurred"; +#endif +#ifdef SIGIO + ap_sys_siglist[SIGIO] = "socket I/O possible"; +#endif +#ifdef SIGSTOP + ap_sys_siglist[SIGSTOP] = "Stopped (signal)"; +#endif +#ifdef SIGTSTP + ap_sys_siglist[SIGTSTP] = "Stopped"; +#endif +#ifdef SIGCONT + ap_sys_siglist[SIGCONT] = "Continued"; +#endif +#ifdef SIGTTIN + ap_sys_siglist[SIGTTIN] = "Stopped (tty input)"; +#endif +#ifdef SIGTTOU + ap_sys_siglist[SIGTTOU] = "Stopped (tty output)"; +#endif +#ifdef SIGVTALRM + ap_sys_siglist[SIGVTALRM] = "virtual timer expired"; +#endif +#ifdef SIGPROF + ap_sys_siglist[SIGPROF] = "profiling timer expired"; +#endif +#ifdef SIGXCPU + ap_sys_siglist[SIGXCPU] = "exceeded cpu limit"; +#endif +#ifdef SIGXFSZ + ap_sys_siglist[SIGXFSZ] = "exceeded file size limit"; +#endif + for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig) + if (ap_sys_siglist[sig] == NULL) + ap_sys_siglist[sig] = ""; +} +#endif /* platform has sys_siglist[] */ + + +/* handle all varieties of core dumping signals */ +static void sig_coredump(int sig) +{ + chdir(ap_coredump_dir); + signal(sig, SIG_DFL); +#ifndef WIN32 + kill(getpid(), sig); +#else + raise(sig); +#endif + /* At this point we've got sig blocked, because we're still inside + * the signal handler. When we leave the signal handler it will + * be unblocked, and we'll take the signal... and coredump or whatever + * is appropriate for this particular Unix. In addition the parent + * will see the real signal we received -- whereas if we called + * abort() here, the parent would only see SIGABRT. + */ +} + +/***************************************************************** + * Connection structures and accounting... + */ + +static void just_die(int sig) +{ /* SIGHUP to child process??? */ + /* if alarms are blocked we have to wait to die otherwise we might + * end up with corruption in alloc.c's internal structures */ + if (alarms_blocked) { + exit_after_unblock = 1; + } + else { + clean_child_exit(0); + } +} + +static int volatile usr1_just_die = 1; +static int volatile deferred_die; + +static void usr1_handler(int sig) +{ + if (usr1_just_die) { + just_die(sig); + } + deferred_die = 1; +} + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +ap_generation_t volatile ap_my_generation=0; + +#ifdef WIN32 +/* + * Signalling Apache on NT. + * + * Under Unix, Apache can be told to shutdown or restart by sending various + * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so + * we use "events" instead. The parent apache process goes into a loop + * where it waits forever for a set of events. Two of those events are + * called + * + * apPID_shutdown + * apPID_restart + * + * (where PID is the PID of the apache parent process). When one of these + * is signalled, the Apache parent performs the appropriate action. The events + * can become signalled through internal Apache methods (e.g. if the child + * finds a fatal error and needs to kill its parent), via the service + * control manager (the control thread will signal the shutdown event when + * requested to stop the Apache service), from the -k Apache command line, + * or from any external program which finds the Apache PID from the + * httpd.pid file. + * + * The signal_parent() function, below, is used to signal one of these events. + * It can be called by any child or parent process, since it does not + * rely on global variables. + * + * On entry, type gives the event to signal. 0 means shutdown, 1 means + * graceful restart. + */ + +static void signal_parent(int type) +{ + HANDLE e; + char *signal_name; + extern char signal_shutdown_name[]; + extern char signal_restart_name[]; + + /* after updating the shutdown_pending or restart flags, we need + * to wake up the parent process so it can see the changes. The + * parent will normally be waiting for either a child process + * to die, or for a signal on the "spache-signal" event. So set the + * "apache-signal" event here. + */ + + if (one_process) { + return; + } + + switch(type) { + case 0: signal_name = signal_shutdown_name; break; + case 1: signal_name = signal_restart_name; break; + default: return; + } + + APD2("signal_parent signalling event \"%s\"", signal_name); + + e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name); + if (!e) { + /* Um, problem, can't signal the parent, which means we can't + * signal ourselves to die. Ignore for now... + */ + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "OpenEvent on %s event", signal_name); + return; + } + if (SetEvent(e) == 0) { + /* Same problem as above */ + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "SetEvent on %s event", signal_name); + CloseHandle(e); + return; + } + CloseHandle(e); +} +#endif + +/* + * ap_start_shutdown() and ap_start_restart(), below, are a first stab at + * functions to initiate shutdown or restart without relying on signals. + * Previously this was initiated in sig_term() and restart() signal handlers, + * but we want to be able to start a shutdown/restart from other sources -- + * e.g. on Win32, from the service manager. Now the service manager can + * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that + * these functions can also be called by the child processes, since global + * variables are no longer used to pass on the required action to the parent. + */ + +void ap_start_shutdown(void) +{ +#ifndef WIN32 + if (shutdown_pending == 1) { + /* Um, is this _probably_ not an error, if the user has + * tried to do a shutdown twice quickly, so we won't + * worry about reporting it. + */ + return; + } + shutdown_pending = 1; +#else + signal_parent(0); /* get the parent process to wake up */ +#endif +} + +/* do a graceful restart if graceful == 1 */ +void ap_start_restart(int graceful) +{ +#ifndef WIN32 + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; + is_graceful = graceful; +#else + signal_parent(1); /* get the parent process to wake up */ +#endif /* WIN32 */ +} + +static void sig_term(int sig) +{ + ap_start_shutdown(); +} + +static void restart(int sig) +{ +#ifndef WIN32 + ap_start_restart(sig == SIGUSR1); +#else + ap_start_restart(1); +#endif +} + +static void set_signals(void) +{ +#ifndef NO_USE_SIGACTION + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (!one_process) { + sa.sa_handler = sig_coredump; +#if defined(SA_ONESHOT) + sa.sa_flags = SA_ONESHOT; +#elif defined(SA_RESETHAND) + sa.sa_flags = SA_RESETHAND; +#endif + if (sigaction(SIGSEGV, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)"); +#ifdef SIGBUS + if (sigaction(SIGBUS, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)"); +#endif +#ifdef SIGABORT + if (sigaction(SIGABORT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABORT)"); +#endif +#ifdef SIGABRT + if (sigaction(SIGABRT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)"); +#endif +#ifdef SIGILL + if (sigaction(SIGILL, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)"); +#endif + sa.sa_flags = 0; + } + sa.sa_handler = sig_term; + if (sigaction(SIGTERM, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGTERM)"); +#ifdef SIGINT + if (sigaction(SIGINT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)"); +#endif +#ifdef SIGXCPU + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXCPU, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXCPU)"); +#endif +#ifdef SIGXFSZ + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXFSZ, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXFSZ)"); +#endif +#ifdef SIGPIPE + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)"); +#endif + + /* we want to ignore HUPs and USR1 while we're busy processing one */ + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGUSR1); + sa.sa_handler = restart; + if (sigaction(SIGHUP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)"); + if (sigaction(SIGUSR1, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR1)"); +#else + if (!one_process) { + signal(SIGSEGV, sig_coredump); +#ifdef SIGBUS + signal(SIGBUS, sig_coredump); +#endif /* SIGBUS */ +#ifdef SIGABORT + signal(SIGABORT, sig_coredump); +#endif /* SIGABORT */ +#ifdef SIGABRT + signal(SIGABRT, sig_coredump); +#endif /* SIGABRT */ +#ifdef SIGILL + signal(SIGILL, sig_coredump); +#endif /* SIGILL */ +#ifdef SIGXCPU + signal(SIGXCPU, SIG_DFL); +#endif /* SIGXCPU */ +#ifdef SIGXFSZ + signal(SIGXFSZ, SIG_DFL); +#endif /* SIGXFSZ */ + } + + signal(SIGTERM, sig_term); +#ifdef SIGHUP + signal(SIGHUP, restart); +#endif /* SIGHUP */ +#ifdef SIGUSR1 + signal(SIGUSR1, restart); +#endif /* SIGUSR1 */ +#ifdef SIGPIPE + signal(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + +#endif +} + + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +static void detach(void) +{ +#if !defined(WIN32) + int x; + + chdir("/"); +#if !defined(MPE) && !defined(OS2) && !defined(TPF) +/* Don't detach for MPE because child processes can't survive the death of + the parent. */ + if ((x = fork()) > 0) + exit(0); + else if (x == -1) { + perror("fork"); + fprintf(stderr, "%s: unable to fork new process\n", ap_server_argv0); + exit(1); + } + RAISE_SIGSTOP(DETACH); +#endif +#ifndef NO_SETSID + if ((pgrp = setsid()) == -1) { + perror("setsid"); + fprintf(stderr, "%s: setsid failed\n", ap_server_argv0); + exit(1); + } +#elif defined(NEXT) || defined(NEWSOS) + if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp or getpgrp failed\n", ap_server_argv0); + exit(1); + } +#elif defined(OS2) || defined(TPF) + /* OS/2 and TPF don't support process group IDs */ + pgrp = getpid(); +#elif defined(MPE) + /* MPE uses negative pid for process group */ + pgrp = -getpid(); +#else + if ((pgrp = setpgrp(getpid(), 0)) == -1) { + perror("setpgrp"); + fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0); + exit(1); + } +#endif + + /* close out the standard file descriptors */ + if (freopen("/dev/null", "r", stdin) == NULL) { + fprintf(stderr, "%s: unable to replace stdin with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + /* continue anyhow -- note we can't close out descriptor 0 because we + * have nothing to replace it with, and if we didn't have a descriptor + * 0 the next file would be created with that value ... leading to + * havoc. + */ + } + if (freopen("/dev/null", "w", stdout) == NULL) { + fprintf(stderr, "%s: unable to replace stdout with /dev/null: %s\n", + ap_server_argv0, strerror(errno)); + } + /* stderr is a tricky one, we really want it to be the error_log, + * but we haven't opened that yet. So leave it alone for now and it'll + * be reopened moments later. + */ +#endif /* ndef WIN32 */ +} + +/* Set group privileges. + * + * Note that we use the username as set in the config files, rather than + * the lookup of to uid --- the same uid may have multiple passwd entries, + * with different sets of groups for each. + */ + +static void set_group_privs(void) +{ +#ifndef WIN32 + if (!geteuid()) { + char *name; + + /* Get username if passed as a uid */ + + if (ap_user_name[0] == '#') { + struct passwd *ent; + uid_t uid = atoi(&ap_user_name[1]); + + if ((ent = getpwuid(uid)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "getpwuid: couldn't determine user name from uid %u, " + "you probably need to modify the User directive", + (unsigned)uid); + clean_child_exit(APEXIT_CHILDFATAL); + } + + name = ent->pw_name; + } + else + name = ap_user_name; + +#if !defined(OS2) && !defined(TPF) + /* OS/2 and TPF don't support groups. */ + + /* + * Set the GID before initgroups(), since on some platforms + * setgid() is known to zap the group list. + */ + if (setgid(ap_group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setgid: unable to set group id to Group %u", + (unsigned)ap_group_id); + clean_child_exit(APEXIT_CHILDFATAL); + } + + /* Reset `groups' attributes. */ + + if (initgroups(name, ap_group_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "initgroups: unable to set groups for User %s " + "and Group %u", name, (unsigned)ap_group_id); + clean_child_exit(APEXIT_CHILDFATAL); + } +#ifdef MULTIPLE_GROUPS + if (getgroups(NGROUPS_MAX, group_id_list) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "getgroups: unable to get group list"); + clean_child_exit(APEXIT_CHILDFATAL); + } +#endif /* MULTIPLE_GROUPS */ +#endif /* !defined(OS2) && !defined(TPF) */ + } +#endif /* ndef WIN32 */ +} + +/* check to see if we have the 'suexec' setuid wrapper installed */ +static int init_suexec(void) +{ +#ifndef WIN32 + struct stat wrapper; + + if ((stat(SUEXEC_BIN, &wrapper)) != 0) + return (ap_suexec_enabled); + + if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) { + ap_suexec_enabled = 1; + } +#endif /* ndef WIN32 */ + return (ap_suexec_enabled); +} + +/***************************************************************** + * Connection structures and accounting... + */ + + +static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout, + const struct sockaddr_in *remaddr, + const struct sockaddr_in *saddr, + int child_num) +{ + conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec)); + + /* Got a connection structure, so initialize what fields we can + * (the rest are zeroed out by pcalloc). + */ + + conn->child_num = child_num; + + conn->pool = p; + conn->local_addr = *saddr; + conn->local_ip = ap_pstrdup(conn->pool, + inet_ntoa(conn->local_addr.sin_addr)); + conn->server = server; /* just a guess for now */ + ap_update_vhost_given_ip(conn); + conn->base_server = conn->server; + conn->client = inout; + + conn->remote_addr = *remaddr; + conn->remote_ip = ap_pstrdup(conn->pool, + inet_ntoa(conn->remote_addr.sin_addr)); + + return conn; +} + +#if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF) +static void sock_disable_nagle(int s) +{ + /* The Nagle algorithm says that we should delay sending partial + * packets in hopes of getting more data. We don't want to do + * this; we are not telnet. There are bad interactions between + * persistent connections and Nagle's algorithm that have very severe + * performance penalties. (Failing to disable Nagle is not much of a + * problem with simple HTTP.) + * + * In spite of these problems, failure here is not a shooting offense. + */ + int just_say_no = 1; + + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no, + sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "setsockopt: (TCP_NODELAY)"); + } +} + +#else +#define sock_disable_nagle(s) /* NOOP */ +#endif + + +static int make_sock(pool *p, const struct sockaddr_in *server) +{ + int s; + int one = 1; + char addr[512]; + + if (server->sin_addr.s_addr != htonl(INADDR_ANY)) + ap_snprintf(addr, sizeof(addr), "address %s port %d", + inet_ntoa(server->sin_addr), ntohs(server->sin_port)); + else + ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port)); + + /* note that because we're about to slack we don't use psocket */ + ap_block_alarms(); + if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: failed to get a socket for %s", addr); + ap_unblock_alarms(); + exit(1); + } + + /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels + * of tcp patches) has some really weird bugs where if you dup the + * socket now it breaks things across SIGHUP restarts. It'll either + * be unable to bind, or it won't respond. + */ +#if defined (SOLARIS2) && SOLARIS2 < 260 +#define WORKAROUND_SOLARIS_BUG +#endif + + /* PR#1282 Unixware 1.x appears to have the same problem as solaris */ +#if defined (UW) && UW < 200 +#define WORKAROUND_SOLARIS_BUG +#endif + + /* PR#1973 NCR SVR4 systems appear to have the same problem */ +#if defined (MPRAS) +#define WORKAROUND_SOLARIS_BUG +#endif + +#ifndef WORKAROUND_SOLARIS_BUG +#ifndef BEOS /* this won't work for BeOS sockets!! */ + s = ap_slack(s, AP_SLACK_HIGH); +#endif + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#ifdef TPF + os_note_additional_cleanups(p, s); +#endif /* TPF */ +#endif + +#ifndef MPE +/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */ +#ifndef _OSD_POSIX + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + ap_unblock_alarms(); + return -1; + } +#endif /*_OSD_POSIX*/ + one = 1; +#ifdef SO_KEEPALIVE + if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + + ap_unblock_alarms(); + return -1; + } +#endif +#endif + + sock_disable_nagle(s); + sock_enable_linger(s); + + /* + * To send data over high bandwidth-delay connections at full + * speed we must force the TCP window to open wide enough to keep the + * pipe full. The default window size on many systems + * is only 4kB. Cross-country WAN connections of 100ms + * at 1Mb/s are not impossible for well connected sites. + * If we assume 100ms cross-country latency, + * a 4kB buffer limits throughput to 40kB/s. + * + * To avoid this problem I've added the SendBufferSize directive + * to allow the web master to configure send buffer size. + * + * The trade-off of larger buffers is that more kernel memory + * is consumed. YMMV, know your customers and your network! + * + * -John Heidemann 25-Oct-96 + * + * If no size is specified, use the kernel default. + */ +#ifndef BEOS /* BeOS does not support SO_SNDBUF */ + if (server_conf->send_buffer_size) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, + "make_sock: failed to set SendBufferSize for %s, " + "using default", addr); + /* not a fatal error */ + } + } +#endif + +#ifdef MPE +/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */ + if (ntohs(server->sin_port) < 1024) + GETPRIVMODE(); +#endif + + if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) { + ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf, + "make_sock: could not bind to %s", addr); +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif + +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + ap_unblock_alarms(); + exit(1); + } +#ifdef MPE + if (ntohs(server->sin_port) < 1024) + GETUSERMODE(); +#endif + + if (listen(s, ap_listenbacklog) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "make_sock: unable to listen for connections on %s", addr); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + ap_unblock_alarms(); + exit(1); + } + +#ifdef WORKAROUND_SOLARIS_BUG + s = ap_slack(s, AP_SLACK_HIGH); + + ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */ +#endif + ap_unblock_alarms(); + +#ifdef CHECK_FD_SETSIZE + /* protect various fd_sets */ + if (s >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "make_sock: problem listening on %s, filedescriptor (%u) " + "larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", addr, s, FD_SETSIZE); +#ifdef BEOS + closesocket(s); +#else + close(s); +#endif + return -1; + } +#endif + + return s; +} + + +/* + * During a restart we keep track of the old listeners here, so that we + * can re-use the sockets. We have to do this because we won't be able + * to re-open the sockets ("Address already in use"). + * + * Unlike the listeners ring, old_listeners is a NULL terminated list. + * + * copy_listeners() makes the copy, find_listener() finds an old listener + * and close_unused_listener() cleans up whatever wasn't used. + */ +static listen_rec *old_listeners; + +/* unfortunately copy_listeners may be called before listeners is a ring */ +static void copy_listeners(pool *p) +{ + listen_rec *lr; + + ap_assert(old_listeners == NULL); + if (ap_listeners == NULL) { + return; + } + lr = ap_listeners; + do { + listen_rec *nr = malloc(sizeof *nr); + if (nr == NULL) { + fprintf(stderr, "Ouch! malloc failed in copy_listeners()\n"); + exit(1); + } + *nr = *lr; + ap_kill_cleanups_for_socket(p, nr->fd); + nr->next = old_listeners; + ap_assert(!nr->used); + old_listeners = nr; + lr = lr->next; + } while (lr && lr != ap_listeners); +} + + +static int find_listener(listen_rec *lr) +{ + listen_rec *or; + + for (or = old_listeners; or; or = or->next) { + if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) { + or->used = 1; + return or->fd; + } + } + return -1; +} + + +static void close_unused_listeners(void) +{ + listen_rec *or, *next; + + for (or = old_listeners; or; or = next) { + next = or->next; + if (!or->used) + closesocket(or->fd); + free(or); + } + old_listeners = NULL; +} + + +/* open sockets, and turn the listeners list into a singly linked ring */ +static void setup_listeners(pool *p) +{ + listen_rec *lr; + int fd; + + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + for (;;) { + fd = find_listener(lr); + if (fd < 0) { + fd = make_sock(p, &lr->local_addr); + } + else { + ap_note_cleanups_for_socket(p, fd); + } + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } + lr->fd = fd; + if (lr->next == NULL) + break; + lr = lr->next; + } + /* turn the list into a ring */ + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); + +#ifdef NO_SERIALIZED_ACCEPT + /* warn them about the starvation problem if they're using multiple + * sockets + */ + if (ap_listeners->next != ap_listeners) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, NULL, + "You cannot use multiple Listens safely on your system, " + "proceeding anyway. See src/PORTING, search for " + "SERIALIZED_ACCEPT."); + } +#endif +} + + +/* + * Find a listener which is ready for accept(). This advances the + * head_listener global. + */ +static ap_inline listen_rec *find_ready_listener(fd_set * main_fds) +{ + listen_rec *lr; + + lr = head_listener; + do { + if (FD_ISSET(lr->fd, main_fds)) { + head_listener = lr->next; + return (lr); + } + lr = lr->next; + } while (lr != head_listener); + return NULL; +} + + +#ifdef WIN32 +static int s_iInitCount = 0; + +static int AMCSocketInitialize(void) +{ + int iVersionRequested; + WSADATA wsaData; + int err; + + if (s_iInitCount > 0) { + s_iInitCount++; + return (0); + } + else if (s_iInitCount < 0) + return (s_iInitCount); + + /* s_iInitCount == 0. Do the initailization */ + iVersionRequested = MAKEWORD(1, 1); + err = WSAStartup((WORD) iVersionRequested, &wsaData); + if (err) { + s_iInitCount = -1; + return (s_iInitCount); + } + if (LOBYTE(wsaData.wVersion) != 1 || + HIBYTE(wsaData.wVersion) != 1) { + s_iInitCount = -2; + WSACleanup(); + return (s_iInitCount); + } + + s_iInitCount++; + return (s_iInitCount); + +} + + +static void AMCSocketCleanup(void) +{ + if (--s_iInitCount == 0) + WSACleanup(); + return; +} +#endif + +static void show_compile_settings(void) +{ + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); + printf("Server's Module Magic Number: %u:%u\n", + MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR); + printf("Server compiled with....\n"); +#ifdef BIG_SECURITY_HOLE + printf(" -D BIG_SECURITY_HOLE\n"); +#endif +#ifdef SECURITY_HOLE_PASS_AUTHORIZATION + printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n"); +#endif +#ifdef HAVE_MMAP + printf(" -D HAVE_MMAP\n"); +#endif +#ifdef HAVE_SHMGET + printf(" -D HAVE_SHMGET\n"); +#endif +#ifdef USE_MMAP_SCOREBOARD + printf(" -D USE_MMAP_SCOREBOARD\n"); +#endif +#ifdef USE_SHMGET_SCOREBOARD + printf(" -D USE_SHMGET_SCOREBOARD\n"); +#endif +#ifdef USE_OS2_SCOREBOARD + printf(" -D USE_OS2_SCOREBOARD\n"); +#endif +#ifdef USE_POSIX_SCOREBOARD + printf(" -D USE_POSIX_SCOREBOARD\n"); +#endif +#ifdef USE_MMAP_FILES + printf(" -D USE_MMAP_FILES\n"); +#ifdef MMAP_SEGMENT_SIZE + printf(" -D MMAP_SEGMENT_SIZE=%ld\n",(long)MMAP_SEGMENT_SIZE); +#endif +#endif /*USE_MMAP_FILES*/ +#ifdef NO_WRITEV + printf(" -D NO_WRITEV\n"); +#endif +#ifdef NO_LINGCLOSE + printf(" -D NO_LINGCLOSE\n"); +#endif +#ifdef USE_FCNTL_SERIALIZED_ACCEPT + printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_FLOCK_SERIALIZED_ACCEPT + printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_USLOCK_SERIALIZED_ACCEPT + printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT + printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n"); +#endif +#ifdef USE_PTHREAD_SERIALIZED_ACCEPT + printf(" -D USE_PTHREAD_SERIALIZED_ACCEPT\n"); +#endif +#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT + printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n"); +#endif +#ifdef NO_OTHER_CHILD + printf(" -D NO_OTHER_CHILD\n"); +#endif +#ifdef NO_RELIABLE_PIPED_LOGS + printf(" -D NO_RELIABLE_PIPED_LOGS\n"); +#endif +#ifdef BUFFERED_LOGS + printf(" -D BUFFERED_LOGS\n"); +#ifdef PIPE_BUF + printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF); +#endif +#endif +#ifdef MULTITHREAD + printf(" -D MULTITHREAD\n"); +#endif +#ifdef CHARSET_EBCDIC + printf(" -D CHARSET_EBCDIC\n"); +#endif +#ifdef NEED_HASHBANG_EMUL + printf(" -D NEED_HASHBANG_EMUL\n"); +#endif +#ifdef SHARED_CORE + printf(" -D SHARED_CORE\n"); +#endif + +/* This list displays the compiled-in default paths: */ +#ifdef HTTPD_ROOT + printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n"); +#endif +#ifdef SUEXEC_BIN + printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n"); +#endif +#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR) + printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n"); +#endif +#ifdef DEFAULT_PIDLOG + printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n"); +#endif +#ifdef DEFAULT_SCOREBOARD + printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n"); +#endif +#ifdef DEFAULT_LOCKFILE + printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n"); +#endif +#ifdef DEFAULT_XFERLOG + printf(" -D DEFAULT_XFERLOG=\"" DEFAULT_XFERLOG "\"\n"); +#endif +#ifdef DEFAULT_ERRORLOG + printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n"); +#endif +#ifdef TYPES_CONFIG_FILE + printf(" -D TYPES_CONFIG_FILE=\"" TYPES_CONFIG_FILE "\"\n"); +#endif +#ifdef SERVER_CONFIG_FILE + printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n"); +#endif +#ifdef ACCESS_CONFIG_FILE + printf(" -D ACCESS_CONFIG_FILE=\"" ACCESS_CONFIG_FILE "\"\n"); +#endif +#ifdef RESOURCE_CONFIG_FILE + printf(" -D RESOURCE_CONFIG_FILE=\"" RESOURCE_CONFIG_FILE "\"\n"); +#endif +} + + +/* Some init code that's common between win32 and unix... well actually + * some of it is #ifdef'd but was duplicated before anyhow. This stuff + * is still a mess. + */ +static void common_init(void) +{ + INIT_SIGLIST() +#ifdef AUX3 + (void) set42sig(); +#endif + +#ifdef WIN32 + /* Initialize the stupid sockets */ + AMCSocketInitialize(); +#endif /* WIN32 */ + + pglobal = ap_init_alloc(); + pconf = ap_make_sub_pool(pglobal); + plog = ap_make_sub_pool(pglobal); + ptrans = ap_make_sub_pool(pconf); + + ap_util_init(); + ap_util_uri_init(); + + pcommands = ap_make_sub_pool(NULL); + ap_server_pre_read_config = ap_make_array(pcommands, 1, sizeof(char *)); + ap_server_post_read_config = ap_make_array(pcommands, 1, sizeof(char *)); + ap_server_config_defines = ap_make_array(pcommands, 1, sizeof(char *)); +} + +#ifndef MULTITHREAD +/***************************************************************** + * Child process main loop. + * The following vars are static to avoid getting clobbered by longjmp(); + * they are really private to child_main. + */ + +static int srv; +static int csd; +static int dupped_csd; +static int requests_this_child; +static fd_set main_fds; + +API_EXPORT(void) ap_child_terminate(request_rec *r) +{ + r->connection->keepalive = 0; + requests_this_child = ap_max_requests_per_child = 1; +} + +static void child_main(int child_num_arg) +{ + NET_SIZE_T clen; + struct sockaddr sa_server; + struct sockaddr sa_client; + listen_rec *lr; + + /* All of initialization is a critical section, we don't care if we're + * told to HUP or USR1 before we're done initializing. For example, + * we could be half way through child_init_modules() when a restart + * signal arrives, and we'd have no real way to recover gracefully + * and exit properly. + * + * I suppose a module could take forever to initialize, but that would + * be either a broken module, or a broken configuration (i.e. network + * problems, file locking problems, whatever). -djg + */ + ap_block_alarms(); + + my_pid = getpid(); + csd = -1; + dupped_csd = -1; + my_child_num = child_num_arg; + requests_this_child = 0; + + /* Get a sub pool for global allocations in this child, so that + * we can have cleanups occur when the child exits. + */ + pchild = ap_make_sub_pool(pconf); + + /* needs to be done before we switch UIDs so we have permissions */ + reopen_scoreboard(pchild); + SAFE_ACCEPT(accept_mutex_child_init(pchild)); + + set_group_privs(); +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && ap_user_id > 1) { + GETPRIVMODE(); + if (setuid(ap_user_id) == -1) { + GETUSERMODE(); + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %d", ap_user_id); + exit(1); + } + GETUSERMODE(); + } +#else + /* Only try to switch if we're running as root */ + if (!geteuid() && ( +#ifdef _OSD_POSIX + os_init_job_environment(server_conf, ap_user_name, one_process) != 0 || +#endif + setuid(ap_user_id) == -1)) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %ld", (long) ap_user_id); + clean_child_exit(APEXIT_CHILDFATAL); + } +#endif + + ap_child_init_modules(pchild, server_conf); + + /* done with the initialization critical section */ + ap_unblock_alarms(); + + (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Setup the jump buffers so that we can return here after a timeout + */ + ap_setjmp(jmpbuffer); +#ifndef OS2 +#ifdef SIGURG + signal(SIGURG, timeout); +#endif +#endif + signal(SIGALRM, alrm_handler); +#ifdef TPF + signal(SIGHUP, just_die); + signal(SIGTERM, just_die); + signal(SIGUSR1, just_die); +#endif /* TPF */ + +#ifdef OS2 +/* Stop Ctrl-C/Ctrl-Break signals going to child processes */ + { + unsigned long ulTimes; + DosSetSignalExceptionFocus(0, &ulTimes); + } +#endif + + while (1) { + BUFF *conn_io; + request_rec *r; + + /* Prepare to receive a SIGUSR1 due to graceful restart so that + * we can exit cleanly. Since we're between connections right + * now it's the right time to exit, but we might be blocked in a + * system call when the graceful restart request is made. */ + usr1_just_die = 1; + signal(SIGUSR1, usr1_handler); + + /* + * (Re)initialize this child to a pre-connection state. + */ + + ap_kill_timeout(0); /* Cancel any outstanding alarms. */ + current_conn = NULL; + + ap_clear_pool(ptrans); + + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + clean_child_exit(0); + } + +#ifndef WIN32 + if ((ap_max_requests_per_child > 0 + && requests_this_child++ >= ap_max_requests_per_child)) { + clean_child_exit(0); + } +#else + ++requests_this_child; +#endif + + (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Wait for an acceptable connection to arrive. + */ + + /* Lock around "accept", if necessary */ + SAFE_ACCEPT(accept_mutex_on()); + + for (;;) { + if (ap_listeners->next != ap_listeners) { + /* more than one socket */ + memcpy(&main_fds, &listenfds, sizeof(fd_set)); + srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL); + + if (srv < 0 && errno != EINTR) { + /* Single Unix documents select as returning errnos + * EBADF, EINTR, and EINVAL... and in none of those + * cases does it make sense to continue. In fact + * on Linux 2.0.x we seem to end up with EFAULT + * occasionally, and we'd loop forever due to it. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + clean_child_exit(1); + } + + if (srv <= 0) + continue; + + lr = find_ready_listener(&main_fds); + if (lr == NULL) + continue; + sd = lr->fd; + } + else { + /* only one socket, just pretend we did the other stuff */ + sd = ap_listeners->fd; + } + + /* if we accept() something we don't want to die, so we have to + * defer the exit + */ + deferred_die = 0; + usr1_just_die = 0; + for (;;) { + clen = sizeof(sa_client); + csd = ap_accept(sd, &sa_client, &clen); + if (csd >= 0 || errno != EINTR) + break; + if (deferred_die) { + /* we didn't get a socket, and we were told to die */ + clean_child_exit(0); + } + } + + if (csd >= 0) + break; /* We have a socket ready for reading */ + else { + + /* Our old behaviour here was to continue after accept() + * errors. But this leads us into lots of troubles + * because most of the errors are quite fatal. For + * example, EMFILE can be caused by slow descriptor + * leaks (say in a 3rd party module, or libc). It's + * foolish for us to continue after an EMFILE. We also + * seem to tickle kernel bugs on some platforms which + * lead to never-ending loops here. So it seems best + * to just exit in most cases. + */ + switch (errno) { +#ifdef EPROTO + /* EPROTO on certain older kernels really means + * ECONNABORTED, so we need to ignore it for them. + * See discussion in new-httpd archives nh.9701 + * search for EPROTO. + * + * Also see nh.9603, search for EPROTO: + * There is potentially a bug in Solaris 2.x x<6, + * and other boxes that implement tcp sockets in + * userland (i.e. on top of STREAMS). On these + * systems, EPROTO can actually result in a fatal + * loop. See PR#981 for example. It's hard to + * handle both uses of EPROTO. + */ + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + /* Linux generates the rest of these, other tcp + * stacks (i.e. bsd) tend to hide them behind + * getsockopt() interfaces. They occur when + * the net goes sour or the client disconnects + * after the three-way handshake has been done + * in the kernel but before userland has picked + * up the socket. + */ +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + break; +#ifdef ENETDOWN + case ENETDOWN: + /* + * When the network layer has been shut down, there + * is not much use in simply exiting: the parent + * would simply re-create us (and we'd fail again). + * Use the CHILDFATAL code to tear the server down. + * @@@ Martin's idea for possible improvement: + * A different approach would be to define + * a new APEXIT_NETDOWN exit code, the reception + * of which would make the parent shutdown all + * children, then idle-loop until it detected that + * the network is up again, and restart the children. + * Ben Hyde noted that temporary ENETDOWN situations + * occur in mobile IP. + */ + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "accept: giving up."); + clean_child_exit(APEXIT_CHILDFATAL); +#endif /*ENETDOWN*/ + +#ifdef TPF + case EINACT: + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "offload device inactive"); + clean_child_exit(APEXIT_CHILDFATAL); + break; + default: + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "select/accept error (%u)", errno); + clean_child_exit(APEXIT_CHILDFATAL); +#else + default: + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + clean_child_exit(1); +#endif + } + } + + /* go around again, safe to die */ + usr1_just_die = 1; + if (deferred_die) { + /* ok maybe not, see ya later */ + clean_child_exit(0); + } + /* or maybe we missed a signal, you never know on systems + * without reliable signals + */ + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + clean_child_exit(0); + } + } + + SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ + +#ifdef TPF + if (csd == 0) /* 0 is invalid socket for TPF */ + continue; +#endif + + /* We've got a socket, let's at least process one request off the + * socket before we accept a graceful restart request. + */ + signal(SIGUSR1, SIG_IGN); + + ap_note_cleanups_for_fd(ptrans, csd); + + /* protect various fd_sets */ +#ifdef CHECK_FD_SETSIZE + if (csd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "[csd] filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", csd, FD_SETSIZE); + continue; + } +#endif + + /* + * We now have a connection, so set it up with the appropriate + * socket options, file descriptors, and read/write buffers. + */ + + clen = sizeof(sa_server); + if (getsockname(csd, &sa_server, &clen) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname"); + continue; + } + + sock_disable_nagle(csd); + + (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ, + (request_rec *) NULL); + + conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET); + +#ifdef B_SFIO + (void) sfdisc(conn_io->sf_in, SF_POPDISC); + sfdisc(conn_io->sf_in, bsfio_new(conn_io->pool, conn_io)); + sfsetbuf(conn_io->sf_in, NULL, 0); + + (void) sfdisc(conn_io->sf_out, SF_POPDISC); + sfdisc(conn_io->sf_out, bsfio_new(conn_io->pool, conn_io)); + sfsetbuf(conn_io->sf_out, NULL, 0); +#endif + + dupped_csd = csd; +#if defined(NEED_DUPPED_CSD) + if ((dupped_csd = dup(csd)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "dup: couldn't duplicate csd"); + dupped_csd = csd; /* Oh well... */ + } + ap_note_cleanups_for_fd(ptrans, dupped_csd); + + /* protect various fd_sets */ +#ifdef CHECK_FD_SETSIZE + if (dupped_csd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL, + "[dupped_csd] filedescriptor (%u) larger than FD_SETSIZE (%u) " + "found, you probably need to rebuild Apache with a " + "larger FD_SETSIZE", dupped_csd, FD_SETSIZE); + continue; + } +#endif +#endif + ap_bpushfd(conn_io, csd, dupped_csd); + + current_conn = new_connection(ptrans, server_conf, conn_io, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, + my_child_num); + + /* + * Read and process each request found on our connection + * until no requests are left or we decide to close. + */ + + while ((r = ap_read_request(current_conn)) != NULL) { + + /* read_request_line has already done a + * signal (SIGUSR1, SIG_IGN); + */ + + (void) ap_update_child_status(my_child_num, SERVER_BUSY_WRITE, r); + + /* process the request if it was read without error */ + + if (r->status == HTTP_OK) + ap_process_request(r); + + if(ap_extended_status) + increment_counts(my_child_num, r); + + if (!current_conn->keepalive || current_conn->aborted) + break; + + ap_destroy_pool(r->pool); + (void) ap_update_child_status(my_child_num, SERVER_BUSY_KEEPALIVE, + (request_rec *) NULL); + + ap_sync_scoreboard_image(); + if (ap_scoreboard_image->global.running_generation != ap_my_generation) { + ap_bclose(conn_io); + clean_child_exit(0); + } + + /* In case we get a graceful restart while we're blocked + * waiting for the request. + * + * XXX: This isn't perfect, we might actually read the + * request and then just die without saying anything to + * the client. This can be fixed by using deferred_die + * but you have to teach buff.c about it so that it can handle + * the EINTR properly. + * + * In practice though browsers (have to) expect keepalive + * connections to close before receiving a response because + * of network latencies and server timeouts. + */ + usr1_just_die = 1; + signal(SIGUSR1, usr1_handler); + } + + /* + * Close the connection, being careful to send out whatever is still + * in our buffers. If possible, try to avoid a hard close until the + * client has ACKed our FIN and/or has stopped sending us data. + */ + +#ifdef NO_LINGCLOSE + ap_bclose(conn_io); /* just close it */ +#else + if (r && r->connection + && !r->connection->aborted + && r->connection->client + && (r->connection->client->fd >= 0)) { + + lingering_close(r); + } + else { + ap_bsetflag(conn_io, B_EOUT, 1); + ap_bclose(conn_io); + } +#endif + } +} + +#ifdef TPF +static void reset_tpf_listeners(APACHE_TPF_INPUT *input_parms) +{ + int count; + listen_rec *lr; + + count = 0; + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + + for(;;) { + lr->fd = input_parms->listeners[count]; + if(lr->fd >= 0) { + FD_SET(lr->fd, &listenfds); + if(lr->fd > listenmaxfd) + listenmaxfd = lr->fd; + } + if(lr->next == NULL) + break; + lr = lr->next; + count++; + } + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); +} + +#endif /* TPF */ + +static int make_child(server_rec *s, int slot, time_t now) +{ + int pid; + + if (slot + 1 > max_daemons_limit) { + max_daemons_limit = slot + 1; + } + + if (one_process) { + signal(SIGHUP, just_die); + signal(SIGINT, just_die); +#ifdef SIGQUIT + signal(SIGQUIT, SIG_DFL); +#endif + signal(SIGTERM, just_die); + child_main(slot); + } + + /* avoid starvation */ + head_listener = head_listener->next; + + Explain1("Starting new child in slot %d", slot); + (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL); + + +#ifdef _OSD_POSIX + /* BS2000 requires a "special" version of fork() before a setuid() call */ + if ((pid = os_fork(ap_user_name)) == -1) { +#elif defined(TPF) + if ((pid = os_fork(s, slot)) == -1) { +#else + if ((pid = fork()) == -1) { +#endif + ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process"); + + /* fork didn't succeed. Fix the scoreboard or else + * it will say SERVER_STARTING forever and ever + */ + (void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL); + + /* In case system resources are maxxed out, we don't want + Apache running away with the CPU trying to fork over and + over and over again. */ + sleep(10); + + return -1; + } + + if (!pid) { +#ifdef AIX_BIND_PROCESSOR +/* by default AIX binds to a single processor + * this bit unbinds children which will then bind to another cpu + */ +#include + int status = bindprocessor(BINDPROCESS, (int)getpid(), + PROCESSOR_CLASS_ANY); + if (status != OK) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, + "processor unbind failed %d", status); + } +#endif + RAISE_SIGSTOP(MAKE_CHILD); + MONCONTROL(1); + /* Disable the restart signal handlers and enable the just_die stuff. + * Note that since restart() just notes that a restart has been + * requested there's no race condition here. + */ + signal(SIGHUP, just_die); + signal(SIGUSR1, just_die); + signal(SIGTERM, just_die); + child_main(slot); + } + +#ifdef OPTIMIZE_TIMEOUTS + ap_scoreboard_image->parent[slot].last_rtime = now; +#endif + ap_scoreboard_image->parent[slot].pid = pid; +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0); + force_write(scoreboard_fd, &ap_scoreboard_image->parent[slot], + sizeof(parent_score)); +#endif + + return 0; +} + + +/* start up a bunch of children */ +static void startup_children(int number_to_start) +{ + int i; + time_t now = time(0); + + for (i = 0; number_to_start && i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) { + continue; + } + if (make_child(server_conf, i, now) < 0) { + break; + } + --number_to_start; + } +} + + +/* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ +static int idle_spawn_rate = 1; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif +static int hold_off_on_exponential_spawning; + +static void perform_idle_server_maintenance(void) +{ + int i; + int to_kill; + int idle_count; + short_score *ss; + time_t now = time(0); + int free_length; + int free_slots[MAX_SPAWN_RATE]; + int last_non_dead; + int total_non_dead; + + /* initialize the free_list */ + free_length = 0; + + to_kill = -1; + idle_count = 0; + last_non_dead = -1; + total_non_dead = 0; + + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + int status; + + if (i >= max_daemons_limit && free_length == idle_spawn_rate) + break; + ss = &ap_scoreboard_image->servers[i]; + status = ss->status; + if (status == SERVER_DEAD) { + /* try to keep children numbers as low as possible */ + if (free_length < idle_spawn_rate) { + free_slots[free_length] = i; + ++free_length; + } + } + else { + /* We consider a starting server as idle because we started it + * at least a cycle ago, and if it still hasn't finished starting + * then we're just going to swamp things worse by forking more. + * So we hopefully won't need to fork more if we count it. + * This depends on the ordering of SERVER_READY and SERVER_STARTING. + */ + if (status <= SERVER_READY) { + ++ idle_count; + /* always kill the highest numbered child if we have to... + * no really well thought out reason ... other than observing + * the server behaviour under linux where lower numbered children + * tend to service more hits (and hence are more likely to have + * their data in cpu caches). + */ + to_kill = i; + } + + ++total_non_dead; + last_non_dead = i; +#ifdef OPTIMIZE_TIMEOUTS + if (ss->timeout_len) { + /* if it's a live server, with a live timeout then + * start checking its timeout */ + parent_score *ps = &ap_scoreboard_image->parent[i]; + if (ss->cur_vtime != ps->last_vtime) { + /* it has made progress, so update its last_rtime, + * last_vtime */ + ps->last_rtime = now; + ps->last_vtime = ss->cur_vtime; + } + else if (ps->last_rtime + ss->timeout_len < now) { + /* no progress, and the timeout length has been exceeded */ + ss->timeout_len = 0; + kill(ps->pid, SIGALRM); + } + } +#endif + } + } + max_daemons_limit = last_non_dead + 1; + if (idle_count > ap_daemons_max_free) { + /* kill off one child... we use SIGUSR1 because that'll cause it to + * shut down gracefully, in case it happened to pick up a request + * while we were counting + */ + kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1); + idle_spawn_rate = 1; + } + else if (idle_count < ap_daemons_min_free) { + /* terminate the free list */ + if (free_length == 0) { + /* only report this condition once */ + static int reported = 0; + + if (!reported) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf, + "server reached MaxClients setting, consider" + " raising the MaxClients setting"); + reported = 1; + } + idle_spawn_rate = 1; + } + else { + if (idle_spawn_rate >= 8) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "server seems busy, (you may need " + "to increase StartServers, or Min/MaxSpareServers), " + "spawning %d children, there are %d idle, and " + "%d total children", idle_spawn_rate, + idle_count, total_non_dead); + } + for (i = 0; i < free_length; ++i) { +#ifdef TPF + if(make_child(server_conf, free_slots[i], now) == -1) { + if(free_length == 1) { + shutdown_pending = 1; + ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, + "No active child processes: shutting down"); + } + } +#else + make_child(server_conf, free_slots[i], now); +#endif /* TPF */ + } + /* the next time around we want to spawn twice as many if this + * wasn't good enough, but not if we've just done a graceful + */ + if (hold_off_on_exponential_spawning) { + --hold_off_on_exponential_spawning; + } + else if (idle_spawn_rate < MAX_SPAWN_RATE) { + idle_spawn_rate *= 2; + } + } + } + else { + idle_spawn_rate = 1; + } +} + + +static void process_child_status(int pid, ap_wait_t status) +{ + /* Child died... if it died due to a fatal error, + * we should simply bail out. + */ + if ((WIFEXITED(status)) && + WEXITSTATUS(status) == APEXIT_CHILDFATAL) { + ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf, + "Child %d returned a Fatal error... \n" + "Apache is exiting!", + pid); + exit(APEXIT_CHILDFATAL); + } + if (WIFSIGNALED(status)) { + switch (WTERMSIG(status)) { + case SIGTERM: + case SIGHUP: + case SIGUSR1: + case SIGKILL: + break; + default: +#ifdef SYS_SIGLIST +#ifdef WCOREDUMP + if (WCOREDUMP(status)) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d), " + "possible coredump in %s", + pid, (WTERMSIG(status) >= NumSIG) ? "" : + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status), + ap_coredump_dir); + } + else { +#endif + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %s (%d)", pid, + SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status)); +#ifdef WCOREDUMP + } +#endif +#else + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, + server_conf, + "child pid %d exit signal %d", + pid, WTERMSIG(status)); +#endif + } + } +} + + +/***************************************************************** + * Executive routines. + */ + +#ifndef STANDALONE_MAIN +#define STANDALONE_MAIN standalone_main + +static void standalone_main(int argc, char **argv) +{ + int remaining_children_to_start; + +#ifdef OS2 + printf("%s \n", ap_get_server_version()); +#endif + + ap_standalone = 1; + + is_graceful = 0; + + if (!one_process) { + detach(); + } + else { + MONCONTROL(1); + } + + my_pid = getpid(); + + do { + copy_listeners(pconf); + if (!is_graceful) { + ap_restart_time = time(NULL); + } +#ifdef SCOREBOARD_FILE + else if (scoreboard_fd != -1) { + ap_kill_cleanup(pconf, NULL, cleanup_scoreboard_file); + ap_kill_cleanups_for_fd(pconf, scoreboard_fd); + } +#endif + ap_clear_pool(pconf); + ptrans = ap_make_sub_pool(pconf); + + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + setup_listeners(pconf); + ap_clear_pool(plog); + ap_open_logs(server_conf, plog); + ap_log_pid(pconf, ap_pid_fname); + ap_set_version(); /* create our server_version string */ + ap_init_modules(pconf, server_conf); + version_locked++; /* no more changes to server_version */ + SAFE_ACCEPT(accept_mutex_init(pconf)); + if (!is_graceful) { + reinit_scoreboard(pconf); + } +#ifdef SCOREBOARD_FILE + else { + ap_scoreboard_fname = ap_server_root_relative(pconf, ap_scoreboard_fname); + ap_note_cleanups_for_fd(pconf, scoreboard_fd); + } +#endif + + set_signals(); + + if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */ + ap_daemons_max_free = ap_daemons_min_free + 1; + + /* If we're doing a graceful_restart then we're going to see a lot + * of children exiting immediately when we get into the main loop + * below (because we just sent them SIGUSR1). This happens pretty + * rapidly... and for each one that exits we'll start a new one until + * we reach at least daemons_min_free. But we may be permitted to + * start more than that, so we'll just keep track of how many we're + * supposed to start up without the 1 second penalty between each fork. + */ + remaining_children_to_start = ap_daemons_to_start; + if (remaining_children_to_start > ap_daemons_limit) { + remaining_children_to_start = ap_daemons_limit; + } + if (!is_graceful) { + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + } + else { + /* give the system some time to recover before kicking into + * exponential mode */ + hold_off_on_exponential_spawning = 10; + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "%s configured -- resuming normal operations", + ap_get_server_version()); + if (ap_suexec_enabled) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "suEXEC mechanism enabled (wrapper: %s)", SUEXEC_BIN); + } + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "Server built: %s", ap_get_server_built()); + restart_pending = shutdown_pending = 0; + + while (!restart_pending && !shutdown_pending) { + int child_slot; + ap_wait_t status; + int pid = wait_or_timeout(&status); + + /* XXX: if it takes longer than 1 second for all our children + * to start up and get into IDLE state then we may spawn an + * extra child + */ + if (pid >= 0) { + process_child_status(pid, status); + /* non-fatal death... note that it's gone in the scoreboard. */ + ap_sync_scoreboard_image(); + child_slot = find_child_by_pid(pid); + Explain2("Reaping child %d slot %d", pid, child_slot); + if (child_slot >= 0) { + (void) ap_update_child_status(child_slot, SERVER_DEAD, + (request_rec *) NULL); + if (remaining_children_to_start + && child_slot < ap_daemons_limit) { + /* we're still doing a 1-for-1 replacement of dead + * children with new children + */ + make_child(server_conf, child_slot, time(0)); + --remaining_children_to_start; + } +#ifndef NO_OTHER_CHILD + } + else if (reap_other_child(pid, status) == 0) { + /* handled */ +#endif + } + else if (is_graceful) { + /* Great, we've probably just lost a slot in the + * scoreboard. Somehow we don't know about this + * child. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf, + "long lost child came home! (pid %d)", pid); + } + /* Don't perform idle maintenance when a child dies, + * only do it when there's a timeout. Remember only a + * finite number of children can die, and it's pretty + * pathological for a lot to die suddenly. + */ + continue; + } + else if (remaining_children_to_start) { + /* we hit a 1 second timeout in which none of the previous + * generation of children needed to be reaped... so assume + * they're all done, and pick up the slack if any is left. + */ + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + /* In any event we really shouldn't do the code below because + * few of the servers we just started are in the IDLE state + * yet, so we'd mistakenly create an extra server. + */ + continue; + } + + perform_idle_server_maintenance(); +#ifdef TPF + shutdown_pending = os_check_server(tpf_server_name); + ap_check_signals(); + sleep(1); +#endif /*TPF */ + } + + if (shutdown_pending) { + /* Time to gracefully shut down: + * Kill child processes, tell them to call child_exit, etc... + */ + if (ap_killpg(pgrp, SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM"); + } + reclaim_child_processes(1); /* Start with SIGTERM */ + + /* cleanup pid file on normal shutdown */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "caught SIGTERM, shutting down"); + clean_parent_exit(0); + } + + /* we've been told to restart */ + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + + if (one_process) { + /* not worth thinking about */ + clean_parent_exit(0); + } + + /* advance to the next generation */ + /* XXX: we really need to make sure this new generation number isn't in + * use by any of the children. + */ + ++ap_my_generation; + ap_scoreboard_image->global.running_generation = ap_my_generation; + update_scoreboard_global(); + + if (is_graceful) { +#ifndef SCOREBOARD_FILE + int i; +#endif + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGUSR1 received. Doing graceful restart"); + + /* kill off the idle ones */ + if (ap_killpg(pgrp, SIGUSR1) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGUSR1"); + } +#ifndef SCOREBOARD_FILE + /* This is mostly for debugging... so that we know what is still + * gracefully dealing with existing request. But we can't really + * do it if we're in a SCOREBOARD_FILE because it'll cause + * corruption too easily. + */ + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) { + ap_scoreboard_image->servers[i].status = SERVER_GRACEFUL; + } + } +#endif + } + else { + /* Kill 'em off */ + if (ap_killpg(pgrp, SIGHUP) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP"); + } + reclaim_child_processes(0); /* Not when just starting up */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf, + "SIGHUP received. Attempting to restart"); + } + } while (restart_pending); + + /*add_common_vars(NULL);*/ +} /* standalone_main */ +#else +/* prototype */ +void STANDALONE_MAIN(int argc, char **argv); +#endif /* STANDALONE_MAIN */ + +extern char *optarg; +extern int optind; + +int REALMAIN(int argc, char *argv[]) +{ + int c; + int sock_in; + int sock_out; + char *s; + +#ifdef SecureWare + if (set_auth_parameters(argc, argv) < 0) + perror("set_auth_parameters"); + if (getluid() < 0) + if (setluid(getuid()) < 0) + perror("setluid"); + if (setreuid(0, 0) < 0) + perror("setreuid"); +#endif + +#ifdef SOCKS + SOCKSinit(argv[0]); +#endif + +#ifdef TPF + APACHE_TPF_INPUT input_parms; + ecbptr()->ebrout = PRIMECRAS; + input_parms = * (APACHE_TPF_INPUT *)(&(ecbptr()->ebw000)); +#endif + + MONCONTROL(0); + + common_init(); + + if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) { + ap_server_argv0 = ++s; + } + else { + ap_server_argv0 = argv[0]; + } + + ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root)); + ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname)); + + ap_setup_prelinked_modules(); + + while ((c = getopt(argc, argv, + "D:C:c:xXd:f:vVlLR:StTh" +#ifdef DEBUG_SIGSTOP + "Z:" +#endif + )) != -1) { + char **new; + switch (c) { + case 'c': + new = (char **)ap_push_array(ap_server_post_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'C': + new = (char **)ap_push_array(ap_server_pre_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'D': + new = (char **)ap_push_array(ap_server_config_defines); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'd': + ap_cpystrn(ap_server_root, optarg, sizeof(ap_server_root)); + break; + case 'f': + ap_cpystrn(ap_server_confname, optarg, sizeof(ap_server_confname)); + break; + case 'v': + ap_set_version(); + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); + exit(0); + case 'V': + ap_set_version(); + show_compile_settings(); + exit(0); + case 'l': + ap_show_modules(); + exit(0); + case 'L': + ap_show_directives(); + exit(0); + case 'X': + ++one_process; /* Weird debugging mode. */ + break; +#ifdef TPF + case 'x': + os_tpf_child(&input_parms); + set_signals(); + break; +#endif +#ifdef DEBUG_SIGSTOP + case 'Z': + raise_sigstop_flags = atoi(optarg); + break; +#endif +#ifdef SHARED_CORE + case 'R': + /* just ignore this option here, because it has only + * effect when SHARED_CORE is used and then it was + * already handled in the Shared Core Bootstrap + * program. + */ + break; +#endif + case 'S': + ap_dump_settings = 1; + break; + case 't': + ap_configtestonly = 1; + ap_docrootcheck = 1; + break; + case 'T': + ap_configtestonly = 1; + ap_docrootcheck = 0; + break; + case 'h': + usage(argv[0]); + case '?': + usage(argv[0]); + } + } + + ap_suexec_enabled = init_suexec(); + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + + if (ap_configtestonly) { + fprintf(stderr, "Syntax OK\n"); + exit(0); + } + if (ap_dump_settings) { + exit(0); + } + + child_timeouts = !ap_standalone || one_process; + +#ifndef TPF + if (ap_standalone) { + ap_open_logs(server_conf, plog); + ap_set_version(); + ap_init_modules(pconf, server_conf); + version_locked++; + STANDALONE_MAIN(argc, argv); + } +#else + if (ap_standalone) { + if(!tpf_child) { + memcpy(tpf_server_name, input_parms.inetd_server.servname, INETD_SERVNAME_LENGTH); + tpf_server_name[INETD_SERVNAME_LENGTH+1] = '\0'; + ap_open_logs(server_conf, pconf); + } + ap_set_version(); + ap_init_modules(pconf, server_conf); + version_locked++; + if(tpf_child) { + copy_listeners(pconf); + reset_tpf_listeners(&input_parms); + server_conf->error_log = NULL; +#ifdef SCOREBOARD_FILE + scoreboard_fd = input_parms.scoreboard_fd; + ap_scoreboard_image = &_scoreboard_image; +#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */ + ap_scoreboard_image = (scoreboard *)input_parms.scoreboard_heap; +#endif + child_main(input_parms.slot); + } + else + STANDALONE_MAIN(argc, argv); + } +#endif + else { + conn_rec *conn; + request_rec *r; + struct sockaddr sa_server, sa_client; + BUFF *cio; + NET_SIZE_T l; + + ap_set_version(); + /* Yes this is called twice. */ + ap_init_modules(pconf, server_conf); + version_locked++; + ap_open_logs(server_conf, plog); + ap_init_modules(pconf, server_conf); + set_group_privs(); + +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && ap_user_id > 1) { + GETPRIVMODE(); + if (setuid(ap_user_id) == -1) { + GETUSERMODE(); + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %d", ap_user_id); + exit(1); + } + GETUSERMODE(); + } +#else + /* Only try to switch if we're running as root */ + if (!geteuid() && setuid(ap_user_id) == -1) { + ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf, + "setuid: unable to change to uid: %ld", + (long) ap_user_id); + exit(1); + } +#endif + if (ap_setjmp(jmpbuffer)) { + exit(0); + } + +#ifdef TPF +/* TPF only passes the incoming socket number from the internet daemon + in ebw000 */ + sock_in = * (int*)(&(ecbptr()->ebw000)); + sock_out = * (int*)(&(ecbptr()->ebw000)); +/* TPF also needs a signal set for alarm in inetd mode */ + signal(SIGALRM, alrm_handler); +#elif defined(MPE) +/* HP MPE 5.5 inetd only passes the incoming socket as stdin (fd 0), whereas + HPUX inetd passes the incoming socket as stdin (fd 0) and stdout (fd 1). + Go figure. SR 5003355016 has been submitted to request that the existing + functionality be documented, and then to enhance the functionality to be + like HPUX. */ + sock_in = fileno(stdin); + sock_out = fileno(stdin); +#else + sock_in = fileno(stdin); + sock_out = fileno(stdout); +#endif + + l = sizeof(sa_client); + if ((getpeername(sock_in, &sa_client, &l)) < 0) { +/* get peername will fail if the input isn't a socket */ + perror("getpeername"); + memset(&sa_client, '\0', sizeof(sa_client)); + } + + l = sizeof(sa_server); + if (getsockname(sock_in, &sa_server, &l) < 0) { + perror("getsockname"); + fprintf(stderr, "Error getting local address\n"); + exit(1); + } + server_conf->port = ntohs(((struct sockaddr_in *) &sa_server)->sin_port); + cio = ap_bcreate(ptrans, B_RDWR | B_SOCKET); + cio->fd = sock_out; + cio->fd_in = sock_in; + conn = new_connection(ptrans, server_conf, cio, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, -1); + + while ((r = ap_read_request(conn)) != NULL) { + + if (r->status == HTTP_OK) + ap_process_request(r); + + if (!conn->keepalive || conn->aborted) + break; + + ap_destroy_pool(r->pool); + } + + ap_bclose(cio); + } + exit(0); +} + +#else /* ndef MULTITHREAD */ + + +/********************************************************************** + * Multithreaded implementation + * + * This code is fairly specific to Win32. + * + * The model used to handle requests is a set of threads. One "main" + * thread listens for new requests. When something becomes + * available, it does a select and places the newly available socket + * onto a list of "jobs" (add_job()). Then any one of a fixed number + * of "worker" threads takes the top job off the job list with + * remove_job() and handles that connection to completion. After + * the connection has finished the thread is free to take another + * job from the job list. + * + * In the code, the "main" thread is running within the worker_main() + * function. The first thing this function does is create the + * worker threads, which operate in the child_sub_main() function. The + * main thread then goes into a loop within worker_main() where they + * do a select() on the listening sockets. The select times out once + * per second so that the thread can check for an "exit" signal + * from the parent process (see below). If this signal is set, the + * thread can exit, but only after it has accepted all incoming + * connections already in the listen queue (since Win32 appears + * to through away listened but unaccepted connections when a + * process dies). + * + * Because the main and worker threads exist within a single process + * they are vulnerable to crashes or memory leaks (crashes can also + * be caused within modules, of course). There also needs to be a + * mechanism to perform restarts and shutdowns. This is done by + * creating the main & worker threads within a subprocess. A + * main process (the "parent process") creates one (or more) + * processes to do the work, then the parent sits around waiting + * for the working process to die, in which case it starts a new + * one. The parent process also handles restarts (by creating + * a new working process then signalling the previous working process + * exit ) and shutdowns (by signalling the working process to exit). + * The parent process operates within the master_main() function. This + * process also handles requests from the service manager (NT only). + * + * Signalling between the parent and working process uses a Win32 + * event. Each child has a unique name for the event, which is + * passed to it with the -Z argument when the child is spawned. The + * parent sets (signals) this event to tell the child to die. + * At present all children do a graceful die - they finish all + * current jobs _and_ empty the listen queue before they exit. + * A non-graceful die would need a second event. The -Z argument in + * the child is also used to create the shutdown and restart events, + * since the prefix (apPID) contains the parent process PID. + * + * The code below starts with functions at the lowest level - + * worker threads, and works up to the top level - the main() + * function of the parent process. + * + * The scoreboard (in process memory) contains details of the worker + * threads (within the active working process). There is no shared + * "scoreboard" between processes, since only one is ever active + * at once (or at most, two, when one has been told to shutdown but + * is processes outstanding requests, and a new one has been started). + * This is controlled by a "start_mutex" which ensures only one working + * process is active at once. + **********************************************************************/ + +/* The code protected by #ifdef UNGRACEFUL_RESTARTS/#endif sections + * could implement a sort-of ungraceful restart for Win32. instead of + * graceful restarts. + * + * However it does not work too well because it does not intercept a + * connection already in progress (in child_sub_main()). We'd have to + * get that to poll on the exit event. + */ + +/* + * Definition of jobs, shared by main and worker threads. + */ + +typedef struct joblist_s { + struct joblist_s *next; + int sock; +} joblist; + +/* + * Globals common to main and worker threads. This structure is not + * used by the parent process. + */ + +typedef struct globals_s { +#ifdef UNGRACEFUL_RESTART + HANDLE thread_exit_event; +#else + int exit_now; +#endif + semaphore *jobsemaphore; + joblist *jobhead; + joblist *jobtail; + mutex *jobmutex; + int jobcount; +} globals; + +globals allowed_globals = +{0, NULL, NULL, NULL, NULL, 0}; + +/* + * add_job()/remove_job() - add or remove an accepted socket from the + * list of sockets connected to clients. allowed_globals.jobmutex protects + * against multiple concurrent access to the linked list of jobs. + */ + +void add_job(int sock) +{ + joblist *new_job; + + ap_assert(allowed_globals.jobmutex); + /* TODO: If too many jobs in queue, sleep, check for problems */ + ap_acquire_mutex(allowed_globals.jobmutex); + new_job = (joblist *) malloc(sizeof(joblist)); + if (new_job == NULL) { + fprintf(stderr, "Ouch! Out of memory in add_job()!\n"); + } + new_job->next = NULL; + new_job->sock = sock; + if (allowed_globals.jobtail != NULL) + allowed_globals.jobtail->next = new_job; + allowed_globals.jobtail = new_job; + if (!allowed_globals.jobhead) + allowed_globals.jobhead = new_job; + allowed_globals.jobcount++; + release_semaphore(allowed_globals.jobsemaphore); + ap_release_mutex(allowed_globals.jobmutex); +} + +int remove_job(void) +{ + joblist *job; + int sock; + +#ifdef UNGRACEFUL_RESTART + HANDLE hObjects[2]; + int rv; + + hObjects[0] = allowed_globals.jobsemaphore; + hObjects[1] = allowed_globals.thread_exit_event; + + rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); + ap_assert(rv != WAIT_FAILED); + if (rv == WAIT_OBJECT_0 + 1) { + /* thread_exit_now */ + APD1("thread got exit now event"); + return -1; + } + /* must be semaphore */ +#else + acquire_semaphore(allowed_globals.jobsemaphore); +#endif + ap_assert(allowed_globals.jobmutex); + +#ifdef UNGRACEFUL_RESTART + if (!allowed_globals.jobhead) { +#else + ap_acquire_mutex(allowed_globals.jobmutex); + if (allowed_globals.exit_now && !allowed_globals.jobhead) { +#endif + ap_release_mutex(allowed_globals.jobmutex); + return (-1); + } + job = allowed_globals.jobhead; + ap_assert(job); + allowed_globals.jobhead = job->next; + if (allowed_globals.jobhead == NULL) + allowed_globals.jobtail = NULL; + ap_release_mutex(allowed_globals.jobmutex); + sock = job->sock; + free(job); + return (sock); +} + +/* + * child_sub_main() - this is the main loop for the worker threads + * + * Each thread runs within this function. They wait within remove_job() + * for a job to become available, then handle all the requests on that + * connection until it is closed, then return to remove_job(). + * + * The worker thread will exit when it removes a job which contains + * socket number -1. This provides a graceful thread exit, since + * it will never exit during a connection. + * + * This code in this function is basically equivalent to the child_main() + * from the multi-process (Unix) environment, except that we + * + * - do not call child_init_modules (child init API phase) + * - block in remove_job, and when unblocked we have an already + * accepted socket, instead of blocking on a mutex or select(). + */ + +static void child_sub_main(int child_num) +{ + NET_SIZE_T clen; + struct sockaddr sa_server; + struct sockaddr sa_client; + pool *ptrans; + int requests_this_child = 0; + int csd = -1; + int dupped_csd = -1; + int srv = 0; + + ptrans = ap_make_sub_pool(pconf); + + (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL); + + /* + * Setup the jump buffers so that we can return here after a timeout. + */ +#if defined(USE_LONGJMP) + setjmp(jmpbuffer); +#else + sigsetjmp(jmpbuffer, 1); +#endif +#ifdef SIGURG + signal(SIGURG, timeout); +#endif + + while (1) { + BUFF *conn_io; + request_rec *r; + + /* + * (Re)initialize this child to a pre-connection state. + */ + + ap_set_callback_and_alarm(NULL, 0); /* Cancel any outstanding alarms */ + timeout_req = NULL; /* No request in progress */ + current_conn = NULL; + + ap_clear_pool(ptrans); + + (void) ap_update_child_status(child_num, SERVER_READY, + (request_rec *) NULL); + + /* Get job from the job list. This will block until a job is ready. + * If -1 is returned then the main thread wants us to exit. + */ + csd = remove_job(); + if (csd == -1) + break; /* time to exit */ + requests_this_child++; + + ap_note_cleanups_for_socket(ptrans, csd); + + /* + * We now have a connection, so set it up with the appropriate + * socket options, file descriptors, and read/write buffers. + */ + + clen = sizeof(sa_server); + if (getsockname(csd, &sa_server, &clen) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "getsockname"); + continue; + } + clen = sizeof(sa_client); + if ((getpeername(csd, &sa_client, &clen)) < 0) { + /* get peername will fail if the input isn't a socket */ + perror("getpeername"); + memset(&sa_client, '\0', sizeof(sa_client)); + } + + sock_disable_nagle(csd); + + (void) ap_update_child_status(child_num, SERVER_BUSY_READ, + (request_rec *) NULL); + + conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET); + dupped_csd = csd; +#if defined(NEED_DUPPED_CSD) + if ((dupped_csd = dup(csd)) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "dup: couldn't duplicate csd"); + dupped_csd = csd; /* Oh well... */ + } + ap_note_cleanups_for_socket(ptrans, dupped_csd); +#endif + ap_bpushfd(conn_io, csd, dupped_csd); + + current_conn = new_connection(ptrans, server_conf, conn_io, + (struct sockaddr_in *) &sa_client, + (struct sockaddr_in *) &sa_server, + child_num); + + /* + * Read and process each request found on our connection + * until no requests are left or we decide to close. + */ + + while ((r = ap_read_request(current_conn)) != NULL) { + (void) ap_update_child_status(child_num, SERVER_BUSY_WRITE, r); + + if (r->status == HTTP_OK) + ap_process_request(r); + + if (ap_extended_status) + increment_counts(child_num, r); + + if (!current_conn->keepalive || current_conn->aborted) + break; + + ap_destroy_pool(r->pool); + (void) ap_update_child_status(child_num, SERVER_BUSY_KEEPALIVE, + (request_rec *) NULL); + + ap_sync_scoreboard_image(); + } + + /* + * Close the connection, being careful to send out whatever is still + * in our buffers. If possible, try to avoid a hard close until the + * client has ACKed our FIN and/or has stopped sending us data. + */ + ap_kill_cleanups_for_socket(ptrans, csd); + +#ifdef NO_LINGCLOSE + ap_bclose(conn_io); /* just close it */ +#else + if (r && r->connection + && !r->connection->aborted + && r->connection->client + && (r->connection->client->fd >= 0)) { + + lingering_close(r); + } + else { + ap_bsetflag(conn_io, B_EOUT, 1); + ap_bclose(conn_io); + } +#endif + } + ap_destroy_pool(ptrans); + (void) ap_update_child_status(child_num, SERVER_DEAD, NULL); +} + + +void child_main(int child_num_arg) +{ + /* + * Only reason for this function, is to pass in + * arguments to child_sub_main() on its stack so + * that longjump doesn't try to corrupt its local + * variables and I don't need to make those + * damn variables static/global + */ + child_sub_main(child_num_arg); +} + + + +void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean) +{ + int i; + + free_thread(handles[thread_to_clean]); + for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++) + handles[i] = handles[i + 1]; + (*thread_cnt)--; +} +#ifdef WIN32 +/* + * The Win32 call WaitForMultipleObjects will only allow you to wait for + * a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading + * model in the multithreaded version of apache wants to use this call, + * we are restricted to a maximum of 64 threads. This is a simplistic + * routine that will increase this size. + */ +static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles, + DWORD dwSeconds) +{ + time_t tStopTime; + DWORD dwRet = WAIT_TIMEOUT; + DWORD dwIndex=0; + BOOL bFirst = TRUE; + + tStopTime = time(NULL) + dwSeconds; + + do { + if (!bFirst) + Sleep(1000); + else + bFirst = FALSE; + + for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) { + dwRet = WaitForMultipleObjects( + min(MAXIMUM_WAIT_OBJECTS, + nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)), + lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS), + 0, 0); + + if (dwRet != WAIT_TIMEOUT) { + break; + } + } + } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT)); + + return dwRet; +} +#endif +/***************************************************************** + * Executive routines. + */ + +extern void main_control_server(void *); /* in hellop.c */ + +event *exit_event; +mutex *start_mutex; + +#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */ +char signal_name_prefix[MAX_SIGNAL_NAME]; +char signal_restart_name[MAX_SIGNAL_NAME]; +char signal_shutdown_name[MAX_SIGNAL_NAME]; + +#define MAX_SELECT_ERRORS 100 + +/* + * Initialise the signal names, in the global variables signal_name_prefix, + * signal_restart_name and signal_shutdown_name. + */ + +void setup_signal_names(char *prefix) +{ + ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix); + ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name), + "%s_shutdown", signal_name_prefix); + ap_snprintf(signal_restart_name, sizeof(signal_restart_name), + "%s_restart", signal_name_prefix); + + APD2("signal prefix %s", signal_name_prefix); +} + +static void setup_inherited_listeners(pool *p) +{ + HANDLE pipe; + listen_rec *lr; + int fd; + WSAPROTOCOL_INFO WSAProtocolInfo; + DWORD BytesRead; + + /* Open the pipe to the parent process to receive the inherited socket + * data. The sockets have been set to listening in the parent process. + */ + pipe = GetStdHandle(STD_INPUT_HANDLE); + + /* Setup the listeners */ + listenmaxfd = -1; + FD_ZERO(&listenfds); + lr = ap_listeners; + + FD_ZERO(&listenfds); + + for (;;) { + fd = find_listener(lr); + if (fd < 0) { + if (!ReadFile(pipe, + &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO), + &BytesRead, + (LPOVERLAPPED) NULL)){ + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: Unable to read socket data from parent"); + exit(1); + } + fd = WSASocket(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &WSAProtocolInfo, + 0, + 0); + if (fd == INVALID_SOCKET) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf, + "setup_inherited_listeners: WSASocket failed to get inherit the socket."); + exit(1); + } + APD2("setup_inherited_listeners: WSASocket() returned socket %d", fd); + } + else { + ap_note_cleanups_for_socket(p, fd); + } + if (fd >= 0) { + FD_SET(fd, &listenfds); + if (fd > listenmaxfd) + listenmaxfd = fd; + } + lr->fd = fd; + if (lr->next == NULL) + break; + lr = lr->next; + } + /* turn the list into a ring */ + lr->next = ap_listeners; + head_listener = ap_listeners; + close_unused_listeners(); + CloseHandle(pipe); + return; +} + +/* + * worker_main() is main loop for the child process. The loop in + * this function becomes the controlling thread for the actually working + * threads (which run in a loop in child_sub_main()). + */ + +void worker_main(void) +{ + int nthreads; + fd_set main_fds; + int srv; + int clen; + int csd; + struct sockaddr_in sa_client; + int total_jobs = 0; + thread **child_handles; + int rv; + time_t end_time; + int i; + struct timeval tv; + int wait_time = 1; + int max_jobs_per_exe; + int max_jobs_after_exit_request; + HANDLE hObjects[2]; + int count_select_errors = 0; + pool *pchild; + + pchild = ap_make_sub_pool(pconf); + + ap_standalone = 1; + sd = -1; + nthreads = ap_threads_per_child; + max_jobs_after_exit_request = ap_excess_requests_per_child; + max_jobs_per_exe = ap_max_requests_per_child; + if (nthreads <= 0) + nthreads = 40; + if (max_jobs_per_exe <= 0) + max_jobs_per_exe = 0; + if (max_jobs_after_exit_request <= 0) + max_jobs_after_exit_request = max_jobs_per_exe / 10; + + if (!one_process) + detach(); + + my_pid = getpid(); + + ++ap_my_generation; + + copy_listeners(pconf); + ap_restart_time = time(NULL); + + reinit_scoreboard(pconf); + + /* + * Wait until we have permission to start accepting connections. + * start_mutex is used to ensure that only one child ever + * goes into the listen/accept loop at once. Also wait on exit_event, + * in case we (this child) is told to die before we get a chance to + * serve any requests. + */ + hObjects[0] = (HANDLE)start_mutex; + hObjects[1] = (HANDLE)exit_event; + rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE); + if (rv == WAIT_FAILED) { + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "Waiting for start_mutex or exit_event -- process will exit"); + + ap_destroy_pool(pchild); + cleanup_scoreboard(); + exit(0); + } + if (rv == WAIT_OBJECT_0 + 1) { + /* exit event signalled - exit now */ + ap_destroy_pool(pchild); + cleanup_scoreboard(); + exit(0); + } + /* start_mutex obtained, continue into the select() loop */ + if (one_process) { + setup_listeners(pconf); + } else { + /* Get listeners from the parent process */ + setup_inherited_listeners(pconf); + } + + if (listenmaxfd == -1) { + /* Help, no sockets were made, better log something and exit */ + ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL, + "No sockets were created for listening"); + + signal_parent(0); /* tell parent to die */ + + ap_destroy_pool(pchild); + cleanup_scoreboard(); + exit(0); + } + set_signals(); + + /* + * - Initialize allowed_globals + * - Create the thread table + * - Spawn off threads + * - Create listen socket set (done above) + * - loop { + * wait for request + * create new job + * } while (!time to exit) + * - Close all listeners + * - Wait for all threads to complete + * - Exit + */ + + ap_child_init_modules(pconf, server_conf); + + allowed_globals.jobsemaphore = create_semaphore(0); + allowed_globals.jobmutex = ap_create_mutex(NULL); + + /* spawn off the threads */ + child_handles = (thread *) alloca(nthreads * sizeof(int)); + for (i = 0; i < nthreads; i++) { + child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i); + } + if (nthreads > max_daemons_limit) { + max_daemons_limit = nthreads; + } + + while (1) { + if (max_jobs_per_exe && (total_jobs > max_jobs_per_exe)) { + /* MaxRequestsPerChild hit... + */ + break; + } + /* Always check for the exit event being signaled. + */ + rv = WaitForSingleObject(exit_event, 0); + ap_assert((rv == WAIT_TIMEOUT) || (rv == WAIT_OBJECT_0)); + if (rv == WAIT_OBJECT_0) { + APD1("child: exit event signalled, exiting"); + break; + } + + tv.tv_sec = wait_time; + tv.tv_usec = 0; + + memcpy(&main_fds, &listenfds, sizeof(fd_set)); + srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv); +#ifdef WIN32 + if (srv == SOCKET_ERROR) { + /* Map the Win32 error into a standard Unix error condition */ + errno = WSAGetLastError(); + srv = -1; + } +#endif /* WIN32 */ + + if (srv < 0) { + /* Error occurred - if EINTR, loop around with problem */ + if (errno != EINTR) { + /* A "real" error occurred, log it and increment the count of + * select errors. This count is used to ensure we don't go into + * a busy loop of continuous errors. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + count_select_errors++; + if (count_select_errors > MAX_SELECT_ERRORS) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf, + "Too many errors in select loop. Child process exiting."); + break; + } + } + continue; + } + count_select_errors = 0; /* reset count of errors */ + if (srv == 0) { + continue; + } + + { + listen_rec *lr; + + lr = find_ready_listener(&main_fds); + if (lr != NULL) { + sd = lr->fd; + } + } + do { + clen = sizeof(sa_client); + csd = accept(sd, (struct sockaddr *) &sa_client, &clen); +#ifdef WIN32 + if (csd == INVALID_SOCKET) { + csd = -1; + errno = WSAGetLastError(); + } +#endif /* WIN32 */ + } while (csd < 0 && errno == EINTR); + + if (csd < 0) { +#if defined(EPROTO) && defined(ECONNABORTED) + if ((errno != EPROTO) && (errno != ECONNABORTED)) +#elif defined(EPROTO) + if (errno != EPROTO) +#elif defined(ECONNABORTED) + if (errno != ECONNABORTED) +#endif + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + } + else { + add_job(csd); + total_jobs++; + } + } + + APD2("process PID %d exiting", my_pid); + + /* Get ready to shutdown and exit */ + allowed_globals.exit_now = 1; + ap_release_mutex(start_mutex); + +#ifdef UNGRACEFUL_RESTART + SetEvent(allowed_globals.thread_exit_event); +#else + for (i = 0; i < nthreads; i++) { + add_job(-1); + } +#endif + + APD2("process PID %d waiting for worker threads to exit", my_pid); + /* Wait for all your children */ + end_time = time(NULL) + 180; + while (nthreads) { + rv = wait_for_many_objects(nthreads, child_handles, + end_time - time(NULL)); + if (rv != WAIT_TIMEOUT) { + rv = rv - WAIT_OBJECT_0; + ap_assert((rv >= 0) && (rv < nthreads)); + cleanup_thread(child_handles, &nthreads, rv); + continue; + } + break; + } + + APD2("process PID %d killing remaining worker threads", my_pid); + for (i = 0; i < nthreads; i++) { + kill_thread(child_handles[i]); + free_thread(child_handles[i]); + } +#ifdef UNGRACEFUL_RESTART + ap_assert(CloseHandle(allowed_globals.thread_exit_event)); +#endif + destroy_semaphore(allowed_globals.jobsemaphore); + ap_destroy_mutex(allowed_globals.jobmutex); + + ap_child_exit_modules(pconf, server_conf); + ap_destroy_pool(pchild); + + cleanup_scoreboard(); + + APD2("process PID %d exited", my_pid); + clean_parent_exit(0); +} /* standalone_main */ + +/* + * Spawn a child Apache process. The child process has the command line arguments from + * argc and argv[], plus a -Z argument giving the name of an event. The child should + * open and poll or wait on this event. When it is signalled, the child should die. + * prefix is a prefix string for the event name. + * + * The child_num argument on entry contains a serial number for this child (used to create + * a unique event name). On exit, this number will have been incremented by one, ready + * for the next call. + * + * On exit, the value pointed to be *ev will contain the event created + * to signal the new child process. + * + * The return value is the handle to the child process if successful, else -1. If -1 is + * returned the error will already have been logged by ap_log_error(). + */ + +/********************************************************************** + * master_main - this is the parent (main) process. We create a + * child process to do the work, then sit around waiting for either + * the child to exit, or a restart or exit signal. If the child dies, + * we just respawn a new one. If we have a shutdown or graceful restart, + * tell the child to die when it is ready. If it is a non-graceful + * restart, force the child to die immediately. + **********************************************************************/ + +#define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */ + +static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes) +{ + int i; + int handle = 0; + + CloseHandle(handles[position]); + CloseHandle(events[position]); + + handle = (int)handles[position]; + + for (i = position; i < (*processes)-1; i++) { + handles[i] = handles[i + 1]; + events[i] = events[i + 1]; + } + (*processes)--; + + APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes); +} + +static int create_process(pool *p, HANDLE *handles, HANDLE *events, + int *processes, int *child_num, char *kill_event_name, int argc, char **argv) +{ + + int rv, i; + HANDLE kill_event; + char buf[1024]; + char exit_event_name[40]; /* apPID_C# */ + char *pCommand; + + STARTUPINFO si; /* Filled in prior to call to CreateProcess */ + PROCESS_INFORMATION pi; /* filled in on call to CreateProces */ + LPWSAPROTOCOL_INFO lpWSAProtocolInfo; + listen_rec *lr; + DWORD BytesWritten; + HANDLE hPipeRead = NULL; + HANDLE hPipeWrite = NULL; + SECURITY_ATTRIBUTES sa = {0}; + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + /* Build the command line. Should look something like this: + * C:/apache/bin/apache.exe -Z exit_event -f ap_server_confname + * First, get the path to the executable... + */ + rv = GetModuleFileName(NULL, buf, sizeof(buf)); + if (rv == sizeof(buf)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Path to Apache process too long"); + return -1; + } else if (rv == 0) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: GetModuleFileName() returned NULL for current process."); + return -1; + } + + /* Create the exit event (apPID_C#). Parent signals this event to tell the + * child to exit + */ + ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num)); + kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name); + if (!kill_event) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Could not create exit event for child process"); + return -1; + } + + pCommand = ap_psprintf(p, "\"%s\" -Z %s -f \"%s\"", buf, exit_event_name, ap_server_confname); + + for (i = 1; i < argc; i++) { + pCommand = ap_pstrcat(p, pCommand, " ", argv[i], NULL); + } + + /* Create a pipe to send socket info to the child */ + if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to create pipe to child process.\n"); + return -1; + } + + /* Give the read in of teh pipe (hPipeRead) to the child as stdin. The + * parent will write the socket data to the child on this pipe. + */ + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = hPipeRead; + + if (!CreateProcess(NULL, pCommand, NULL, NULL, + TRUE, /* Inherit handles */ + 0, /* Creation flags */ + NULL, NULL, + &si, &pi)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Not able to create the child process."); + /* + * We must close the handles to the new process and its main thread + * to prevent handle and memory leaks. + */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return -1; + } + else { + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, + "Parent: Created child process %d", pi.dwProcessId); + + /* Assume the child process lives. Update the process and event tables */ + handles[*processes] = pi.hProcess; + events[*processes] = kill_event; + (*processes)++; + + /* We never store the thread's handle, so close it now. */ + CloseHandle(pi.hThread); + + /* Run the chain of open sockets. For each socket, duplicate it + * for the target process then send the WSAPROTOCOL_INFO + * (returned by dup socket) to the child */ + lr = ap_listeners; + while (lr != NULL) { + lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO)); + ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf, + "Parent: Duplicating socket %d and sending it to child process %d", lr->fd, pi.dwProcessId); + if (WSADuplicateSocket(lr->fd, + pi.dwProcessId, + lpWSAProtocolInfo) == SOCKET_ERROR) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: WSADuplicateSocket failed for socket %d.", lr->fd ); + return -1; + } + + if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO), + &BytesWritten, + (LPOVERLAPPED) NULL)) { + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf, + "Parent: Unable to write duplicated socket %d to the child.", lr->fd ); + return -1; + } + + lr = lr->next; + if (lr == ap_listeners) + break; + } + } + CloseHandle(hPipeRead); + CloseHandle(hPipeWrite); + + return 0; +} + +/* To share the semaphores with other processes, we need a NULL ACL + * Code from MS KB Q106387 + */ + +static PSECURITY_ATTRIBUTES GetNullACL() +{ + PSECURITY_DESCRIPTOR pSD; + PSECURITY_ATTRIBUTES sa; + + sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES)); + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, + SECURITY_DESCRIPTOR_MIN_LENGTH); + if (pSD == NULL || sa == NULL) { + return NULL; + } + /* + * We can safely use GetLastError() here without presetting it; + * {Initialize,Set}SecurityDescriptor() have been verified as clearing it + * on successful completion. + */ + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) + || GetLastError()) { + LocalFree( pSD ); + LocalFree( sa ); + return NULL; + } + if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE) + || GetLastError()) { + LocalFree( pSD ); + LocalFree( sa ); + return NULL; + } + sa->nLength = sizeof(sa); + sa->lpSecurityDescriptor = pSD; + sa->bInheritHandle = TRUE; + return sa; +} + + +static void CleanNullACL( void *sa ) { + if( sa ) { + LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor); + LocalFree( sa ); + } +} + +int master_main(int argc, char **argv) +{ + /* returns NULL if invalid (Win95?) */ + PSECURITY_ATTRIBUTES sa = GetNullACL(); + int nchild = ap_daemons_to_start; + int child_num = 0; + int rv, cld; + char signal_prefix_string[100]; + int i; + time_t tmstart; + HANDLE signal_shutdown_event; /* used to signal shutdown to parent */ + HANDLE signal_restart_event; /* used to signal a restart to parent */ + HANDLE process_handles[MAX_PROCESSES]; + HANDLE process_kill_events[MAX_PROCESSES]; + int current_live_processes = 0; /* number of child process we know about */ + int processes_to_create = 0; /* number of child processes to create */ + pool *pparent = NULL; /* pool for the parent process. Cleaned on each restart */ + + nchild = 1; /* only allowed one child process for current generation */ + processes_to_create = nchild; + + is_graceful = 0; + + ap_snprintf(signal_prefix_string, sizeof(signal_prefix_string), + "ap%d", getpid()); + setup_signal_names(signal_prefix_string); + + /* Create shutdown event, apPID_shutdown, where PID is the parent + * Apache process ID. Shutdown is signaled by 'apache -k shutdown'. + */ + signal_shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name); + if (!signal_shutdown_event) { + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "master_main: Cannot create shutdown event %s", signal_shutdown_name); + CleanNullACL((void *)sa); + exit(1); + } + + /* Create restart event, apPID_restart, where PID is the parent + * Apache process ID. Restart is signaled by 'apache -k restart'. + */ + signal_restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name); + if (!signal_restart_event) { + CloseHandle(signal_shutdown_event); + ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf, + "master_main: Cannot create restart event %s", signal_restart_name); + CleanNullACL((void *)sa); + exit(1); + } + CleanNullACL((void *)sa); + + /* Create the start mutex, apPID, where PID is the parent Apache process ID. + * Ths start mutex is used during a restart to prevent more than one + * child process from entering the accept loop at once. + */ + start_mutex = ap_create_mutex(signal_prefix_string); + restart_pending = shutdown_pending = 0; + + do { /* restart-pending */ + if (!is_graceful) { + ap_restart_time = time(NULL); + } + copy_listeners(pconf); + ap_clear_pool(pconf); + pparent = ap_make_sub_pool(pconf); + + server_conf = ap_read_config(pconf, pparent, ap_server_confname); + setup_listeners(pconf); + ap_clear_pool(plog); + ap_open_logs(server_conf, plog); + ap_set_version(); + ap_init_modules(pconf, server_conf); + version_locked++; + service_set_status(SERVICE_START_PENDING); + /* Create child processes */ + while (processes_to_create--) { + if (create_process(pconf, process_handles, process_kill_events, + ¤t_live_processes, &child_num, signal_prefix_string, argc, argv) < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "master_main: create child process failed. Exiting."); + goto die_now; + } + } + service_set_status(SERVICE_RUNNING); + restart_pending = shutdown_pending = 0; + + /* Wait for either the shutdown or restart events to be signaled */ + process_handles[current_live_processes] = signal_shutdown_event; + process_handles[current_live_processes+1] = signal_restart_event; + rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles, + FALSE, INFINITE); + if (rv == WAIT_FAILED) { + /* Something serious is wrong */ + ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf, + "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown"); + shutdown_pending = 1; + break; + } + if (rv == WAIT_TIMEOUT) { + /* Hey, this cannot happen */ + ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, + "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT"); + shutdown_pending = 1; + } + + cld = rv - WAIT_OBJECT_0; + APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld, process_handles[cld], current_live_processes); + if (cld == current_live_processes) { + /* apPID_shutdown event signalled, we should exit now */ + shutdown_pending = 1; + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "master_main: Shutdown event signaled. Shutting the server down."); + if (ResetEvent(signal_shutdown_event) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "ResetEvent(signal_shutdown_event)"); + } + /* Signal each child processes to die */ + for (i = 0; i < current_live_processes; i++) { + APD3("master_main: signalling child %d, handle %d to die", i, process_handles[i]); + if (SetEvent(process_kill_events[i]) == 0) + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "master_main: SetEvent for child process in slot #%d failed", i); + } + break; + } else if (cld == current_live_processes+1) { + /* apPID_restart event signalled, restart the child process */ + int children_to_kill = current_live_processes; + restart_pending = 1; + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "master_main: Restart event signaled. Doing a graceful restart."); + if (ResetEvent(signal_restart_event) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "master_main: ResetEvent(signal_restart_event) failed."); + } + /* Signal each child process to die */ + for (i = 0; i < children_to_kill; i++) { + APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]); + if (SetEvent(process_kill_events[i]) == 0) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf, + "master_main: SetEvent for child process in slot #%d failed", i); + /* Remove the process (and event) from the process table */ + cleanup_process(process_handles, process_kill_events, i, ¤t_live_processes); + } + processes_to_create = nchild; + ++ap_my_generation; + continue; + } else { + /* A child process must have exited because of MaxRequestPerChild being hit + * or a fatal error condition (seg fault, etc.). Remove the dead process + * from the process_handles and process_kill_events table and create a new + * child process. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf, + "master_main: Child processed exited (due to MaxRequestsPerChild?). Restarting the child process."); + ap_assert(cld < current_live_processes); + cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); + APD2("main_process: child in slot %d died", rv); + processes_to_create = 1; + continue; + } + + } while (1); + + /* If we dropped out of the loop we definitly want to die completely. We need to + * make sure we wait for all the child process to exit first. + */ + + APD2("*** main process shutdown, processes=%d ***", current_live_processes); + +die_now: + + tmstart = time(NULL); + while (current_live_processes && ((tmstart+60) > time(NULL))) { + service_set_status(SERVICE_STOP_PENDING); + rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000); + if (rv == WAIT_TIMEOUT) + continue; + ap_assert(rv != WAIT_FAILED); + cld = rv - WAIT_OBJECT_0; + ap_assert(rv < current_live_processes); + APD4("main_process: child in #%d handle %d died, left=%d", + rv, process_handles[rv], current_live_processes); + cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes); + } + for (i = 0; i < current_live_processes; i++) { + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf, + "forcing termination of child #%d (handle %d)", i, process_handles[i]); + TerminateProcess((HANDLE) process_handles[i], 1); + } + + CloseHandle(signal_restart_event); + CloseHandle(signal_shutdown_event); + + /* cleanup pid file on normal shutdown */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pparent, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, + server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + if (pparent) { + ap_destroy_pool(pparent); + } + + ap_destroy_mutex(start_mutex); + + service_set_status(SERVICE_STOPPED); + return (0); +} + +/* + * Send signal to a running Apache. On entry signal should contain + * either "shutdown" or "restart" + */ + +int send_signal(pool *p, char *signal) +{ + char prefix[20]; + FILE *fp; + int nread; + char *fname; + int end; + + fname = ap_server_root_relative (p, ap_pid_fname); + + fp = fopen(fname, "r"); + if (!fp) { + printf("Cannot read apache PID file %s\n", fname); + return FALSE; + } + prefix[0] = 'a'; + prefix[1] = 'p'; + + nread = fread(prefix+2, 1, sizeof(prefix)-3, fp); + if (nread == 0) { + fclose(fp); + printf("PID file %s was empty\n", fname); + return FALSE; + } + fclose(fp); + + /* Terminate the prefix string */ + end = 2 + nread - 1; + while (end > 0 && (prefix[end] == '\r' || prefix[end] == '\n')) + end--; + prefix[end + 1] = '\0'; + + setup_signal_names(prefix); + + if (!strcasecmp(signal, "shutdown")) + ap_start_shutdown(); + else if (!strcasecmp(signal, "restart")) + ap_start_restart(1); + else { + printf("Unknown signal name \"%s\". Use either shutdown or restart.\n", + signal); + return FALSE; + } + return TRUE; +} + +void post_parse_init() +{ + ap_set_version(); + ap_init_modules(pconf, server_conf); + ap_suexec_enabled = init_suexec(); + version_locked++; + ap_open_logs(server_conf, plog); + set_group_privs(); +} + +int service_init() +{ + common_init(); + + ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root)); + if (ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname), + ap_server_argv0)) + return FALSE; + + ap_setup_prelinked_modules(); + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + ap_log_pid(pconf, ap_pid_fname); + post_parse_init(); + return TRUE; +} + +#ifdef WIN32 +__declspec(dllexport) + int apache_main(int argc, char *argv[]) +#else +int REALMAIN(int argc, char *argv[]) +#endif +{ + int c; + int child = 0; + char *cp; + char *s; + char *service_name = NULL; + int install = 0; + int conf_specified = 0; + char *signal_to_send = NULL; + char cwd[MAX_STRING_LEN]; + + /* Service application + * Configuration file in registry at: + * HKLM\System\CurrentControlSet\Services\[Svc name]\Parameters\ConfPath + */ + if (isProcessService()) { + service_main(master_main, argc, argv); + clean_parent_exit(0); + } + + /* Console application or a child process. */ + + if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) { + ap_server_argv0 = ++s; + } + else { + ap_server_argv0 = argv[0]; + } + + common_init(); + ap_setup_prelinked_modules(); + + if(!GetCurrentDirectory(sizeof(cwd),cwd)) { + ap_log_error(APLOG_MARK,APLOG_WIN32ERROR, NULL, + "GetCurrentDirectory() failure"); + return -1; + } + + ap_cpystrn(cwd, ap_os_canonical_filename(pcommands, cwd), sizeof(cwd)); + ap_cpystrn(ap_server_root, cwd, sizeof(ap_server_root)); + + while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLZ:iusStThk:n:")) != -1) { + char **new; + switch (c) { + case 'c': + new = (char **)ap_push_array(ap_server_post_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'C': + new = (char **)ap_push_array(ap_server_pre_read_config); + *new = ap_pstrdup(pcommands, optarg); + break; + case 'D': + new = (char **)ap_push_array(ap_server_config_defines); + *new = ap_pstrdup(pcommands, optarg); + break; +#ifdef WIN32 + case 'Z': + exit_event = open_event(optarg); + APD2("child: opened process event %s", optarg); + cp = strchr(optarg, '_'); + ap_assert(cp); + *cp = 0; + setup_signal_names(optarg); + start_mutex = ap_open_mutex(signal_name_prefix); + ap_assert(start_mutex); + child = 1; + break; + case 'n': + service_name = ap_pstrdup(pcommands, optarg); + if (isValidService(optarg)) { + ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname), + optarg); + conf_specified = 1; + } + break; + case 'i': + install = 1; + break; + case 'u': + install = -1; + break; + case 'S': + ap_dump_settings = 1; + break; + case 'k': + signal_to_send = optarg; + break; +#endif /* WIN32 */ + case 'd': + optarg = ap_os_canonical_filename(pcommands, optarg); + if (!ap_os_is_path_absolute(optarg)) { + optarg = ap_pstrcat(pcommands, cwd, optarg, NULL); + ap_getparents(optarg); + } + if (optarg[strlen(optarg)-1] != '/') + optarg = ap_pstrcat(pcommands, optarg, "/", NULL); + ap_cpystrn(ap_server_root, + optarg, + sizeof(ap_server_root)); + break; + case 'f': + ap_cpystrn(ap_server_confname, + ap_os_canonical_filename(pcommands, optarg), + sizeof(ap_server_confname)); + conf_specified = 1; + break; + case 'v': + ap_set_version(); + printf("Server version: %s\n", ap_get_server_version()); + printf("Server built: %s\n", ap_get_server_built()); + exit(0); + case 'V': + ap_set_version(); + show_compile_settings(); + exit(0); + case 'l': + ap_show_modules(); + exit(0); + case 'L': + ap_show_directives(); + exit(0); + case 'X': + ++one_process; /* Weird debugging mode. */ + break; + case 't': + ap_configtestonly = 1; + ap_docrootcheck = 1; + break; + case 'T': + ap_configtestonly = 1; + ap_docrootcheck = 0; + break; + case 'h': + usage(ap_server_argv0); + case '?': + usage(ap_server_argv0); + } /* switch */ + } /* while */ + + /* ServerConfFile is found in this order: + * (1) -f or -n + * (2) [-d]/SERVER_CONFIG_FILE + * (3) ./SERVER_CONFIG_FILE + * (4) [Registry: HKLM\Software\[product]\ServerRoot]/SERVER_CONFIG_FILE + * (5) /HTTPD_ROOT/SERVER_CONFIG_FILE + */ + + if (!conf_specified) { + ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname)); + if (access(ap_server_root_relative(pcommands, ap_server_confname), 0)) { + ap_registry_get_server_root(pconf, ap_server_root, sizeof(ap_server_root)); + if (!*ap_server_root) + ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root)); + ap_cpystrn(ap_server_root, ap_os_canonical_filename(pcommands, ap_server_root), + sizeof(ap_server_root)); + } + } + + if (!ap_os_is_path_absolute(ap_server_confname)) { + char *full_conf_path; + + full_conf_path = ap_pstrcat(pcommands, ap_server_root, "/", ap_server_confname, NULL); + full_conf_path = ap_os_canonical_filename(pcommands, full_conf_path); + ap_cpystrn(ap_server_confname, full_conf_path, sizeof(ap_server_confname)); + } + ap_getparents(ap_server_confname); + ap_no2slash(ap_server_confname); + +#ifdef WIN32 + if (install) { + if (!service_name) + service_name = ap_pstrdup(pconf, DEFAULTSERVICENAME); + if (install > 0) + InstallService(service_name, ap_server_root_relative(pcommands, ap_server_confname)); + else + RemoveService(service_name); + clean_parent_exit(0); + } + + if (service_name && signal_to_send) { + send_signal_to_service(service_name, signal_to_send); + clean_parent_exit(0); + } + + if (service_name && !conf_specified) { + printf("Unknown service: %s\n", service_name); + clean_parent_exit(0); + } +#endif + server_conf = ap_read_config(pconf, ptrans, ap_server_confname); + + if (ap_configtestonly) { + fprintf(stderr, "%s: Syntax OK\n", ap_server_root_relative(pcommands, ap_server_confname)); + clean_parent_exit(0); + } + + if (ap_dump_settings) { + clean_parent_exit(0); + } + + /* Treat -k start confpath as just -f confpath */ + if (signal_to_send && strcasecmp(signal_to_send, "start")) { + send_signal(pconf, signal_to_send); + clean_parent_exit(0); + } + + if (!child && !ap_dump_settings) { + ap_log_pid(pconf, ap_pid_fname); + } + + post_parse_init(); + +#ifdef OS2 + printf("%s running...\n", ap_get_server_version()); +#endif +#ifdef WIN32 + if (!child) { + printf("%s running...\n", ap_get_server_version()); + } +#endif + if (one_process && !exit_event) + exit_event = create_event(0, 0, NULL); + if (one_process && !start_mutex) + start_mutex = ap_create_mutex(NULL); + /* + * In the future, the main will spawn off a couple + * of children and monitor them. As soon as a child + * exits, it spawns off a new one + */ + if (child || one_process) { + if (!exit_event || !start_mutex) + exit(-1); + worker_main(); + ap_destroy_mutex(start_mutex); + destroy_event(exit_event); + } + else + master_main(argc, argv); + + clean_parent_exit(0); + return 0; /* purely to avoid a warning */ +} + +#endif /* ndef MULTITHREAD */ + +#else /* ndef SHARED_CORE_TIESTATIC */ + +/* +** Standalone Tie Program for Shared Core support +** +** It's purpose is to tie the static libraries and +** the shared core library under link-time and +** passing execution control to the real main function +** in the shared core library under run-time. +*/ + +extern int ap_main(int argc, char *argv[]); + +int main(int argc, char *argv[]) +{ + return ap_main(argc, argv); +} + +#endif /* ndef SHARED_CORE_TIESTATIC */ +#else /* ndef SHARED_CORE_BOOTSTRAP */ + +#ifdef OS2 +/* Shared core loader for OS/2 */ + +int ap_main(int argc, char *argv[]); /* Load time linked from libhttpd.dll */ + +int main(int argc, char *argv[]) +{ + return ap_main(argc, argv); +} + +#else + +/* +** Standalone Bootstrap Program for Shared Core support +** +** It's purpose is to initialise the LD_LIBRARY_PATH +** environment variable therewith the Unix loader is able +** to start the Standalone Tie Program (see above) +** and then replacing itself with this program by +** immediately passing execution to it. +*/ + +#include +#include +#include + +#include "ap_config.h" +#include "httpd.h" + +#if defined(HPUX) || defined(HPUX10) || defined(HPUX11) +#define VARNAME "SHLIB_PATH" +#else +#define VARNAME "LD_LIBRARY_PATH" +#endif + +#ifndef SHARED_CORE_DIR +#define SHARED_CORE_DIR HTTPD_ROOT "/libexec" +#endif + +#ifndef SHARED_CORE_EXECUTABLE_PROGRAM +#define SHARED_CORE_EXECUTABLE_PROGRAM "lib" TARGET ".ep" +#endif + +extern char *optarg; +extern int optind; + +int main(int argc, char *argv[], char *envp[]) +{ + char prog[MAX_STRING_LEN]; + char llp_buf[MAX_STRING_LEN]; + char **llp_slot; + char *llp_existing; + char *llp_dir; + char **envpnew; + int c, i, l; + + /* + * parse argument line, + * but only handle the -L option + */ + llp_dir = SHARED_CORE_DIR; + while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLR:SZ:tTh")) != -1) { + switch (c) { + case 'D': + case 'C': + case 'c': + case 'X': + case 'd': + case 'f': + case 'v': + case 'V': + case 'l': + case 'L': + case 'S': + case 'Z': + case 't': + case 'T': + case 'h': + case '?': + break; + case 'R': + llp_dir = strdup(optarg); + break; + } + } + + /* + * create path to SHARED_CORE_EXECUTABLE_PROGRAM + */ + ap_snprintf(prog, sizeof(prog), "%s/%s", llp_dir, SHARED_CORE_EXECUTABLE_PROGRAM); + + /* + * adjust process environment therewith the Unix loader + * is able to start the SHARED_CORE_EXECUTABLE_PROGRAM. + */ + llp_slot = NULL; + llp_existing = NULL; + l = strlen(VARNAME); + for (i = 0; envp[i] != NULL; i++) { + if (strncmp(envp[i], VARNAME "=", l+1) == 0) { + llp_slot = &envp[i]; + llp_existing = strchr(envp[i], '=') + 1; + } + } + if (llp_slot == NULL) { + envpnew = (char **)malloc(sizeof(char *)*(i + 2)); + if (envpnew == NULL) { + fprintf(stderr, "Ouch! Out of memory generating envpnew!\n"); + } + memcpy(envpnew, envp, sizeof(char *)*i); + envp = envpnew; + llp_slot = &envp[i++]; + envp[i] = NULL; + } + if (llp_existing != NULL) + ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s:%s", VARNAME, llp_dir, llp_existing); + else + ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s", VARNAME, llp_dir); + *llp_slot = strdup(llp_buf); + + /* + * finally replace our process with + * the SHARED_CORE_EXECUTABLE_PROGRAM + */ + if (execve(prog, argv, envp) == -1) { + fprintf(stderr, + "%s: Unable to exec Shared Core Executable Program `%s'\n", + argv[0], prog); + return 1; + } + else + return 0; +} + +#endif /* def OS2 */ +#endif /* ndef SHARED_CORE_BOOTSTRAP */ + +#ifndef SHARED_CORE_BOOTSTRAP +/* + * Force ap_validate_password() into the image so that modules like + * mod_auth can use it even if they're dynamically loaded. + */ +void suck_in_ap_validate_password(void); +void suck_in_ap_validate_password(void) +{ + ap_validate_password("a", "b"); +} +#endif + +/* force Expat to be linked into the server executable */ +#if defined(USE_EXPAT) && !defined(SHARED_CORE_BOOTSTRAP) +#include "xmlparse.h" +const XML_LChar *suck_in_expat(void); +const XML_LChar *suck_in_expat(void) +{ + return XML_ErrorString(XML_ERROR_NONE); +} +#endif /* USE_EXPAT */ + diff --git a/server/mpm/winnt/registry.c b/server/mpm/winnt/registry.c new file mode 100644 index 0000000000..23eeb5e449 --- /dev/null +++ b/server/mpm/winnt/registry.c @@ -0,0 +1,471 @@ +/* + * Functions to handle interacting with the Win32 registry + */ + +/* + * Apache registry key structure + * + * Apache's registry information is stored in the HKEY_LOCAL_MACHINE + * key, under + * + * HKLM\SOFTWARE\Apache Group\Apache\version + * + * These keys are defined in this file. The definition of the "version" part + * will need updating each time Apache moves from beta to non-beta or from a + * release to a development or beta version. + */ + +/* To allow for multiple services, store the configuration file's full path + * under each service entry: + * + * HKLM\System\CurrentControlSet\Services\[service name]\Parameters\ConfPath + * + * The default configuration path (for console apache) is still stored: + * + * HKLM\Software\[Vendor]\[Software]\[Version]\ServerRoot + */ + +#include +#include + +#include "httpd.h" +#include "http_log.h" + +/* Define where the Apache values are stored in the registry. In general + * VERSION will be the same across all beta releases for a particular + * major release, but will change when the final release is made. + */ + +#define VENDOR "Apache Group" +#define SOFTWARE "Apache" +#define VERSION "1.3.9" + +#define REGKEY "SOFTWARE\\" VENDOR "\\" SOFTWARE "\\" VERSION + +#define SERVICEKEYPRE "System\\CurrentControlSet\\Services\\" +#define SERVICEKEYPOST "\\Parameters" + +/* + * The Windows API registry key functions don't set the last error + * value (the windows equivalent of errno). So we need to set it + * with SetLastError() before calling the aplog_error() function. + * Because this is common, let's have a macro. + */ +#define do_error(rv,fmt,arg) do { \ + SetLastError(rv); \ + ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_ERR, NULL, fmt,arg); \ + } while (0); + +/* + * Get the data for registry key value. This is a generic function that + * can either get a value into a caller-supplied buffer, or it can + * allocate space for the value from the pass-in pool. It will normally + * be used by other functions within this file to get specific key values + * (e.g. registry_get_server_root()). This function returns a number of + * different error statuses, allowing the caller to differentiate + * between a key or value not existing and other kinds of errors. Depending + * on the type of data being obtained the caller can then either ignore + * the key-not-existing error, or treat it as a real error. + * + * If ppValue is NULL, allocate space for the value and return it in + * *pValue. The return value is the number of bytes in the value. + * The first argument is the pool to use to allocate space for the value. + * + * If pValue is not NULL, assume it is a buffer of nSizeValue bytes, + * and write the value into the buffer. The return value is the number + * of bytes in the value (so if the return value is greater than + * the supplied nSizeValue, the caller knows that *pValue is truncated). + * The pool argument is ignored. + * + * The return value is the number of bytes in the successfully retreived + * key if everything worked, or: + * + * -1 the key does not exists + * -2 if out of memory during the function + * -3 if the buffer specified by *pValue/nSizeValue was not large enough + * for the value. + * -4 if an error occurred + * + * If the return value is negative a message will be logged to the error + * log (aplog_error) function. If the return value is -2, -3 or -4 the message + * will be logged at priority "error", while if the return value is -1 the + * message will be logged at priority "warning". + */ + +static int ap_registry_get_key_int(pool *p, char *key, char *name, char *pBuffer, int nSizeBuffer, char **ppValue) +{ + long rv; + HKEY hKey; + char *pValue; + int nSize; + int retval; + + rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + key, + 0, + KEY_READ, + &hKey); + + if (rv == ERROR_FILE_NOT_FOUND) { + ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL, + "Registry does not contain key %s",key); + return -1; + } + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegOpenKeyEx HKLM\\%s",key); + return -4; + } + + if (pBuffer == NULL) { + /* Find the size required for the data by passing NULL as the buffer + * pointer. On return nSize will contain the size required for the + * buffer if the return value is ERROR_SUCCESS. + */ + rv = RegQueryValueEx(hKey, + name, /* key name */ + NULL, /* reserved */ + NULL, /* type */ + NULL, /* for value */ + &nSize); /* for size of "value" */ + + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegQueryValueEx(key %s)", key); + return -1; + } + + pValue = ap_palloc(p, nSize); + *ppValue = pValue; + if (!pValue) { + /* Eek, out of memory, probably not worth trying to carry on, + * but let's give it a go + */ + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,NULL, + "Error getting registry key: out of memory"); + return -2; + } + } + else { + /* Get the value into the existing buffer of length nSizeBuffer */ + pValue = pBuffer; + nSize = nSizeBuffer; + } + + rv = RegQueryValueEx(hKey, + name, /* key name */ + NULL, /* reserved */ + NULL, /* type */ + pValue, /* for value */ + &nSize); /* for size of "value" */ + + retval = 0; /* Return value */ + + if (rv == ERROR_FILE_NOT_FOUND) { + ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL, + "Registry does not contain value %s\\%s", key, name); + retval = -1; + } + else if (rv == ERROR_MORE_DATA) { + /* This should only happen if we got passed a pre-existing buffer + * (pBuffer, nSizeBuffer). But I suppose it could also happen if we + * allocate a buffer if another process changed the length of the + * value since we found out its length above. Umm. + */ + ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,NULL, + "Error getting registry value %s: buffer not big enough", key); + retval = -3; + } + else if (rv != ERROR_SUCCESS) { + do_error(rv, "RegQueryValueEx(key %s)", key); + retval = -4; + } + + rv = RegCloseKey(hKey); + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegCloseKey HKLM\\%s", key); + if (retval == 0) { + /* Keep error status from RegQueryValueEx, if any */ + retval = -4; + } + } + + return retval < 0 ? retval : nSize; +} + +/* + * Get the server root from the registry into 'dir' which is + * size bytes long. Returns 0 if the server root was found + * or if the serverroot key does not exist (in which case + * dir will contain an empty string), or -1 if there was + * an error getting the key. + */ + +int ap_registry_get_server_root(pool *p, char *dir, int size) +{ + int rv; + + rv = ap_registry_get_key_int(p, REGKEY, "ServerRoot", dir, size, NULL); + if (rv < 0) { + dir[0] = '\0'; + } + + return (rv < -1) ? -1 : 0; +} + +char *ap_get_service_key(char *service_name) +{ + char *key = malloc(strlen(SERVICEKEYPRE) + + strlen(service_name) + + strlen(SERVICEKEYPOST) + 1); + + sprintf(key,"%s%s%s", SERVICEKEYPRE, service_name, SERVICEKEYPOST); + + return(key); +} + +int ap_registry_get_service_conf(pool *p, char *dir, int size, char *service_name) +{ + int rv; + char *key = ap_get_service_key(service_name); + + rv = ap_registry_get_key_int(p, key, "ConfPath", dir, size, NULL); + if (rv < 0) { + dir[0] = '\0'; + } + + free(key); + return (rv < -1) ? -1 : 0; +} + +/********************************************************************** + * The rest of this file deals with storing keys or values in the registry + */ + +char *ap_registry_parse_key(int index, char *key) +{ + char *head = key, *skey; + int i; + + if(!key) + return(NULL); + + for(i = 0; i <= index; i++) + { + if(key && key[0] == '\\') + key++; + if (!key) + return(NULL); + head = key; + key = strchr(head, '\\'); + } + + if(!key) + return(strdup(head)); + *key = '\0'; + skey = strdup(head); + *key = '\\'; + return(skey); +} + +/* + * ap_registry_create_apache_key() creates the Apache registry key + * (HLKM\SOFTWARE\Apache Group\Apache\version, as defined at the start + * of this file), if it does not already exist. It will be called by + * ap_registry_store_key_int() if it cannot open this key. This + * function is intended to be called by ap_registry_store_key_int() if + * the Apache key does not exist when it comes to store a data item. + * + * Returns 0 on success or -1 on error. If -1 is returned, the error will + * already have been logged. + */ + +static int ap_registry_create_key(char *longkey) +{ + int index; + HKEY hKey; + HKEY hKeyNext; + int retval; + int rv; + char *key; + + hKey = HKEY_LOCAL_MACHINE; + index = 0; + retval = 0; + + /* Walk the tree, creating at each stage if necessary */ + while (key=ap_registry_parse_key(index,longkey)) { + int result; + + rv = RegCreateKeyEx(hKey, + key, /* subkey */ + 0, /* reserved */ + NULL, /* class */ + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + NULL, + &hKeyNext, + &result); + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegCreateKeyEx(%s)", longkey); + retval = -4; + } + + /* Close the old key */ + rv = RegCloseKey(hKey); + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegCloseKey", NULL); + if (retval == 0) { + /* Keep error status from RegCreateKeyEx, if any */ + retval = -4; + } + } + + if (retval) { + break; + } + + free(key); + hKey = hKeyNext; + index++; + } + + if (!key) { + /* Close the final key we opened, if we walked the entire + * tree + */ + rv = RegCloseKey(hKey); + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegCloseKey", NULL); + if (retval == 0) { + /* Keep error status from RegCreateKeyEx, if any */ + retval = -4; + } + } + } + else + free(key); + + return retval; +} + +/* + * ap_registry_store_key_int() stores a value name and value under the + * Apache registry key. If the Apache key does not exist it is created + * first. This function is intended to be called from a wrapper function + * in this file to set particular data values, such as + * ap_registry_set_server_root() below. + * + * Returns 0 if the value name and data was stored successfully, or + * returns -1 if the Apache key does not exist (since we try to create + * this key, this should never happen), or -4 if any other error occurred + * (these values are consistent with ap_registry_get_key_int()). + * If the return value is negative then the error will already have been + * logged via aplog_error(). + */ + +static int ap_registry_store_key_int(char *key, char *name, DWORD type, void *value, int value_size) +{ + long rv; + HKEY hKey; + int retval; + + rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + key, + 0, + KEY_WRITE, + &hKey); + + if (rv == ERROR_FILE_NOT_FOUND) { + /* Key could not be opened -- try to create it + */ + if (ap_registry_create_key(key) < 0) { + /* Creation failed (error already reported) */ + return -4; + } + + /* Now it has been created we should be able to open it + */ + rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + key, + 0, + KEY_WRITE, + &hKey); + + if (rv == ERROR_FILE_NOT_FOUND) { + ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL, + "Registry does not contain key %s after creation",key); + return -1; + } + } + + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegOpenKeyEx HKLM\\%s", key); + return -4; + } + + /* Now set the value and data */ + rv = RegSetValueEx(hKey, + name, /* value key name */ + 0, /* reserved */ + type, /* type */ + value, /* value data */ + (DWORD)value_size); /* for size of "value" */ + + retval = 0; /* Return value */ + + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegQueryValueEx(key %s)", key); + retval = -4; + } + else { + ap_log_error(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,NULL, + "Registry stored HKLM\\" REGKEY "\\%s value %s", key, + type == REG_SZ ? value : "(not displayable)"); + } + + /* Make sure we close the key even if there was an error storing + * the data + */ + rv = RegCloseKey(hKey); + if (rv != ERROR_SUCCESS) { + do_error(rv, "RegCloseKey HKLM\\%s", key); + if (retval == 0) { + /* Keep error status from RegQueryValueEx, if any */ + retval = -4; + } + } + + return retval; +} + +/* + * Sets the service confpath value within the registry. Returns 0 on success + * or -1 on error. If -1 is return the error will already have been + * logged via aplog_error(). + */ + +int ap_registry_set_service_conf(char *conf, char *service_name) +{ + int rv; + char *key = ap_get_service_key(service_name); + + rv = ap_registry_store_key_int(key, "ConfPath", REG_SZ, conf, strlen(conf)+1); + free(key); + + return rv < 0 ? -1: 0; +} + +/* + * Sets the serverroot value within the registry. Returns 0 on success + * or -1 on error. If -1 is return the error will already have been + * logged via aplog_error(). + */ + +int ap_registry_set_server_root(char *dir) +{ + int rv; + + rv = ap_registry_store_key_int(REGKEY, "ServerRoot", REG_SZ, dir, strlen(dir)+1); + + return rv < 0 ? -1 : 0; +} + diff --git a/server/mpm/winnt/service.c b/server/mpm/winnt/service.c new file mode 100644 index 0000000000..8108ae1ad9 --- /dev/null +++ b/server/mpm/winnt/service.c @@ -0,0 +1,431 @@ +#ifdef WIN32 + +#include +#include +#include +#include +#include + +#include "httpd.h" +#include "http_conf_globals.h" +#include "http_log.h" +#include "http_main.h" +#include "multithread.h" +#include "service.h" +#include "registry.h" + +static struct +{ + int (*main_fn)(int, char **); + event *stop_event; + int connected; + SERVICE_STATUS_HANDLE hServiceStatus; + char *name; + int exit_status; + SERVICE_STATUS ssStatus; + FILE *logFile; +} globdat; + +static void WINAPI service_main_fn(DWORD, LPTSTR *); +static void WINAPI service_ctrl(DWORD ctrlCode); +static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint); +static int ap_start_service(SC_HANDLE); +static int ap_stop_service(SC_HANDLE); + +int service_main(int (*main_fn)(int, char **), int argc, char **argv ) +{ + SERVICE_TABLE_ENTRY dispatchTable[] = + { + { "", service_main_fn }, + { NULL, NULL } + }; + + globdat.main_fn = main_fn; + globdat.stop_event = create_event(0, 0, "apache-signal"); + globdat.connected = 1; + + if(!StartServiceCtrlDispatcher(dispatchTable)) + { + /* This is a genuine failure of the SCM. */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "Error starting service control dispatcher"); + return(globdat.exit_status); + } + else + { + return(globdat.exit_status); + } +} + +void service_cd() +{ + /* change to the drive with the executable */ + char buf[300]; + GetModuleFileName(NULL, buf, 300); + buf[2] = 0; + chdir(buf); +} + +void __stdcall service_main_fn(DWORD argc, LPTSTR *argv) +{ + ap_server_argv0 = globdat.name = argv[0]; + + if(!(globdat.hServiceStatus = RegisterServiceCtrlHandler( globdat.name, service_ctrl))) + { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "Failure registering service handler"); + return; + } + + ReportStatusToSCMgr( + SERVICE_START_PENDING, // service state + NO_ERROR, // exit code + 3000); // wait hint + + service_cd(); + if( service_init() ) + /* Arguments are ok except for \! */ + globdat.exit_status = (*globdat.main_fn)( argc, argv ); + + ReportStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 0); + + return; +} + +void service_set_status(int status) +{ + ReportStatusToSCMgr(status, NO_ERROR, 3000); +} + + + +// +// FUNCTION: service_ctrl +// +// PURPOSE: This function is called by the SCM whenever +// ControlService() is called on this service. +// +// PARAMETERS: +// dwCtrlCode - type of control requested +// +// RETURN VALUE: +// none +// +// COMMENTS: +// +VOID WINAPI service_ctrl(DWORD dwCtrlCode) +{ + int state; + + + state = globdat.ssStatus.dwCurrentState; + switch(dwCtrlCode) + { + // Stop the service. + // + case SERVICE_CONTROL_STOP: + state = SERVICE_STOP_PENDING; + ap_start_shutdown(); + break; + + // Update the service status. + // + case SERVICE_CONTROL_INTERROGATE: + break; + + // invalid control code + // + default: + break; + + } + + ReportStatusToSCMgr(state, NO_ERROR, 0); +} + + +int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint) +{ + static int firstTime = 1; + static int checkPoint = 1; + int rv; + + if(firstTime) + { + firstTime = 0; + globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + globdat.ssStatus.dwServiceSpecificExitCode = 0; + globdat.ssStatus.dwCheckPoint = 1; + } + + if(globdat.connected) + { + if (currentState == SERVICE_START_PENDING) + globdat.ssStatus.dwControlsAccepted = 0; + else + globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + globdat.ssStatus.dwCurrentState = currentState; + globdat.ssStatus.dwWin32ExitCode = exitCode; + if(waitHint) + globdat.ssStatus.dwWaitHint = waitHint; + + if ( ( currentState == SERVICE_RUNNING ) || + ( currentState == SERVICE_STOPPED ) ) + { + globdat.ssStatus.dwWaitHint = 0; + globdat.ssStatus.dwCheckPoint = 0; + } + else + globdat.ssStatus.dwCheckPoint = ++checkPoint; + + rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus); + + } + return(1); +} + +void InstallService(char *service_name, char *conf) +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + TCHAR szPath[512]; + TCHAR szQuotedPath[512]; + + printf("Installing the %s service to use %s\n", service_name, conf); + + if (GetModuleFileName( NULL, szPath, 512 ) == 0) + { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "GetModuleFileName failed"); + return; + } + + ap_snprintf(szQuotedPath, 512, "\"%s\"", szPath); + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if (!schSCManager) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + } + else { + schService = CreateService( + schSCManager, // SCManager database + service_name, // name of service + service_name, // name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_AUTO_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szQuotedPath, // service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // dependencies + NULL, // LocalSystem account + NULL); // no password + + if (schService) { + CloseServiceHandle(schService); + + /* Now store the server_root in the registry */ + if(!ap_registry_set_service_conf(conf, service_name)) + printf("The %s service has been installed successfully.\n", service_name ); + } + else { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "CreateService failed"); + } + + CloseServiceHandle(schSCManager); + } +} + + +void RemoveService(char *service_name) +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + printf("Removing the %s service\n", service_name); + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if (!schSCManager) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + } + else { + schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS); + + if (schService == NULL) { + /* Could not open the service */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenService failed"); + } + else { + /* try to stop the service */ + ap_stop_service(schService); + + // now remove the service + if (DeleteService(schService) == 0) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "DeleteService failed"); + else + printf("The %s service has been removed successfully.\n", service_name ); + CloseServiceHandle(schService); + } + /* SCM removes registry parameters */ + CloseServiceHandle(schSCManager); + } + +} + +/* A hack to determine if we're running as a service without waiting for + * the SCM to fail; if AllocConsole succeeds, we're a service. + */ + +BOOL isProcessService() { + if( !AllocConsole() ) + return FALSE; + FreeConsole(); + return TRUE; +} + +/* Determine is service_name is a valid service + */ + +BOOL isValidService(char *service_name) { + SC_HANDLE schSCM, schSVC; + int Err; + + if (!(schSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + return FALSE; + } + + if ((schSVC = OpenService(schSCM, service_name, SERVICE_ALL_ACCESS))) { + CloseServiceHandle(schSVC); + CloseServiceHandle(schSCM); + return TRUE; + } + + Err = GetLastError(); + if (Err != ERROR_SERVICE_DOES_NOT_EXIST && Err != ERROR_INVALID_NAME) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenService failed"); + + return FALSE; +} + +int send_signal_to_service(char *service_name, char *sig) { + SC_HANDLE schService; + SC_HANDLE schSCManager; + int success = FALSE; + + enum { start, restart, stop, unknown } action; + static char *param[] = { "start", "restart", "shutdown" }; + static char *participle[] = { "starting", "restarting", "stopping" }; + static char *past[] = { "started", "restarted", "stopped" }; + + for (action = start; action < unknown; action++) + if (!strcasecmp(sig, param[action])) + break; + + if (action == unknown) { + printf("signal must be start, restart, or shutdown\n"); + return FALSE; + } + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if (!schSCManager) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenSCManager failed"); + } + else { + schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS); + + if (schService == NULL) { + /* Could not open the service */ + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "OpenService failed"); + } + else { + if (!QueryServiceStatus(schService, &globdat.ssStatus)) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, + "QueryService failed"); + else { + if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED && action == stop) + printf("The %s service is not started.\n", service_name); + else if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING && action == start) + printf("The %s service has already been started.\n", service_name); + else { + printf("The %s service is %s.\n", service_name, participle[action]); + + if (action == stop || action == restart) + success = ap_stop_service(schService); + if (action == start || action == restart) + success = ap_start_service(schService); + + if( success ) + printf("The %s service has %s.\n", service_name, past[action]); + else + printf("Failed to %s the %s service.\n", sig, service_name ); + } + + CloseServiceHandle(schService); + } + } + /* SCM removes registry parameters */ + CloseServiceHandle(schSCManager); + } + return success; +} + +int ap_stop_service(SC_HANDLE schService) +{ + if (ControlService(schService, SERVICE_CONTROL_STOP, &globdat.ssStatus)) { + Sleep(1000); + while (QueryServiceStatus(schService, &globdat.ssStatus)) { + if (globdat.ssStatus.dwCurrentState == SERVICE_STOP_PENDING) + Sleep(1000); + else + break; + } + } + if (QueryServiceStatus(schService, &globdat.ssStatus)) + if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) + return TRUE; + return FALSE; +} + +int ap_start_service(SC_HANDLE schService) { + if (StartService(schService, 0, NULL)) { + Sleep(1000); + while(QueryServiceStatus(schService, &globdat.ssStatus)) { + if(globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING) + Sleep(1000); + else + break; + } + } + if (QueryServiceStatus(schService, &globdat.ssStatus)) + if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING) + return TRUE; + return FALSE; +} + +#endif /* WIN32 */ + diff --git a/server/rfc1413.c b/server/rfc1413.c new file mode 100644 index 0000000000..65bc6ef4d0 --- /dev/null +++ b/server/rfc1413.c @@ -0,0 +1,248 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * rfc1413() speaks a common subset of the RFC 1413, AUTH, TAP and IDENT + * protocols. The code queries an RFC 1413 etc. compatible daemon on a remote + * host to look up the owner of a connection. The information should not be + * used for authentication purposes. This routine intercepts alarm signals. + * + * Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, + * The Netherlands. + */ + +/* Some small additions for Apache --- ditch the "sccsid" var if + * compiling with gcc (it *has* changed), include ap_config.h for the + * prototypes it defines on at least one system (SunlOSs) which has + * them missing from the standard header files, and one minor change + * below (extra parens around assign "if (foo = bar) ..." to shut up + * gcc -Wall). + */ + +/* Rewritten by David Robinson */ + +#include "httpd.h" /* for server_rec, conn_rec, ap_longjmp, etc. */ +#include "http_log.h" /* for aplog_error */ +#include "rfc1413.h" +#include "http_main.h" /* set_callback_and_alarm */ + +/* Local stuff. */ +/* Semi-well-known port */ +#define RFC1413_PORT 113 +/* maximum allowed length of userid */ +#define RFC1413_USERLEN 512 +/* rough limit on the amount of data we accept. */ +#define RFC1413_MAXDATA 1000 + +#ifndef RFC1413_TIMEOUT +#define RFC1413_TIMEOUT 30 +#endif +#define ANY_PORT 0 /* Any old port will do */ +#define FROM_UNKNOWN "unknown" + +int ap_rfc1413_timeout = RFC1413_TIMEOUT; /* Global so it can be changed */ + +static JMP_BUF timebuf; + +/* bind_connect - bind both ends of a socket */ +/* Ambarish fix this. Very broken */ +static int get_rfc1413(int sock, const struct sockaddr_in *our_sin, + const struct sockaddr_in *rmt_sin, + char user[RFC1413_USERLEN+1], server_rec *srv) +{ + struct sockaddr_in rmt_query_sin, our_query_sin; + unsigned int rmt_port, our_port; + int i; + char *cp; + char buffer[RFC1413_MAXDATA + 1]; + int buflen; + + /* + * Bind the local and remote ends of the query socket to the same + * IP addresses as the connection under investigation. We go + * through all this trouble because the local or remote system + * might have more than one network address. The RFC1413 etc. + * client sends only port numbers; the server takes the IP + * addresses from the query socket. + */ + + our_query_sin = *our_sin; + our_query_sin.sin_port = htons(ANY_PORT); + rmt_query_sin = *rmt_sin; + rmt_query_sin.sin_port = htons(RFC1413_PORT); + + if (bind(sock, (struct sockaddr *) &our_query_sin, + sizeof(struct sockaddr_in)) < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, srv, + "bind: rfc1413: Error binding to local port"); + return -1; + } + +/* + * errors from connect usually imply the remote machine doesn't support + * the service + */ + if (connect(sock, (struct sockaddr *) &rmt_query_sin, + sizeof(struct sockaddr_in)) < 0) + return -1; + +/* send the data */ + buflen = ap_snprintf(buffer, sizeof(buffer), "%u,%u\r\n", ntohs(rmt_sin->sin_port), + ntohs(our_sin->sin_port)); + + /* send query to server. Handle short write. */ +#ifdef CHARSET_EBCDIC + ebcdic2ascii(&buffer, &buffer, buflen); +#endif + i = 0; + while(i < strlen(buffer)) { + int j; + j = write(sock, buffer+i, (strlen(buffer+i))); + if (j < 0 && errno != EINTR) { + ap_log_error(APLOG_MARK, APLOG_CRIT, srv, + "write: rfc1413: error sending request"); + return -1; + } + else if (j > 0) { + i+=j; + } + } + + /* + * Read response from server. - the response should be newline + * terminated according to rfc - make sure it doesn't stomp it's + * way out of the buffer. + */ + + i = 0; + memset(buffer, '\0', sizeof(buffer)); + /* + * Note that the strchr function below checks for 10 instead of '\n' + * this allows it to work on both ASCII and EBCDIC machines. + */ + while((cp = strchr(buffer, '\012')) == NULL && i < sizeof(buffer) - 1) { + int j; + j = read(sock, buffer+i, (sizeof(buffer) - 1) - i); + if (j < 0 && errno != EINTR) { + ap_log_error(APLOG_MARK, APLOG_CRIT, srv, + "read: rfc1413: error reading response"); + return -1; + } + else if (j > 0) { + i+=j; + } + } + +/* RFC1413_USERLEN = 512 */ +#ifdef CHARSET_EBCDIC + ascii2ebcdic(&buffer, &buffer, (size_t)i); +#endif + if (sscanf(buffer, "%u , %u : USERID :%*[^:]:%512s", &rmt_port, &our_port, + user) != 3 || ntohs(rmt_sin->sin_port) != rmt_port + || ntohs(our_sin->sin_port) != our_port) + return -1; + + /* + * Strip trailing carriage return. It is part of the + * protocol, not part of the data. + */ + + if ((cp = strchr(user, '\r'))) + *cp = '\0'; + + return 0; +} + +/* ident_timeout - handle timeouts */ +static void ident_timeout(int sig) +{ + ap_longjmp(timebuf, sig); +} + +/* rfc1413 - return remote user name, given socket structures */ +char *ap_rfc1413(conn_rec *conn, server_rec *srv) +{ + static char user[RFC1413_USERLEN + 1]; /* XXX */ + static char *result; + static int sock; + + result = FROM_UNKNOWN; + + sock = ap_psocket(conn->pool, AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) { + ap_log_error(APLOG_MARK, APLOG_CRIT, srv, + "socket: rfc1413: error creating socket"); + conn->remote_logname = result; + } + + /* + * Set up a timer so we won't get stuck while waiting for the server. + */ + if (ap_setjmp(timebuf) == 0) { + ap_set_callback_and_alarm(ident_timeout, ap_rfc1413_timeout); + + if (get_rfc1413(sock, &conn->local_addr, &conn->remote_addr, user, srv) >= 0) + result = user; + } + ap_set_callback_and_alarm(NULL, 0); + ap_pclosesocket(conn->pool, sock); + conn->remote_logname = result; + + return conn->remote_logname; +} diff --git a/server/util.c b/server/util.c new file mode 100644 index 0000000000..b358501c07 --- /dev/null +++ b/server/util.c @@ -0,0 +1,2145 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * util.c: string utility things + * + * 3/21/93 Rob McCool + * 1995-96 Many changes by the Apache Group + * + */ + +/* Debugging aid: + * #define DEBUG to trace all cfg_open*()/cfg_closefile() calls + * #define DEBUG_CFG_LINES to trace every line read from the config files + */ + +#include "httpd.h" +#include "http_conf_globals.h" /* for user_id & group_id */ +#include "http_log.h" +#if defined(SUNOS4) +/* stdio.h has been read in ap_config.h already. Add missing prototypes here: */ +extern int fgetc(FILE *); +extern char *fgets(char *s, int, FILE*); +extern int fclose(FILE *); +#endif + +/* A bunch of functions in util.c scan strings looking for certain characters. + * To make that more efficient we encode a lookup table. The test_char_table + * is generated automatically by gen_test_char.c. + */ +#include "test_char.h" + +/* we assume the folks using this ensure 0 <= c < 256... which means + * you need a cast to (unsigned char) first, you can't just plug a + * char in here and get it to work, because if char is signed then it + * will first be sign extended. + */ +#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f)) + +void ap_util_init(void) +{ + /* nothing to do... previously there was run-time initialization of + * test_char_table here + */ +} + + +API_VAR_EXPORT const char ap_month_snames[12][4] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +API_VAR_EXPORT const char ap_day_snames[7][4] = +{ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +API_EXPORT(char *) ap_get_time() +{ + time_t t; + char *time_string; + + t = time(NULL); + time_string = ctime(&t); + time_string[strlen(time_string) - 1] = '\0'; + return (time_string); +} + +/* + * Examine a field value (such as a media-/content-type) string and return + * it sans any parameters; e.g., strip off any ';charset=foo' and the like. + */ +API_EXPORT(char *) ap_field_noparam(pool *p, const char *intype) +{ + const char *semi; + + semi = strchr(intype, ';'); + if (semi == NULL) { + return ap_pstrdup(p, intype); + } + else { + while ((semi > intype) && ap_isspace(semi[-1])) { + semi--; + } + return ap_pstrndup(p, intype, semi - intype); + } +} + +API_EXPORT(char *) ap_ht_time(pool *p, time_t t, const char *fmt, int gmt) +{ + char ts[MAX_STRING_LEN]; + char tf[MAX_STRING_LEN]; + struct tm *tms; + + tms = (gmt ? gmtime(&t) : localtime(&t)); + if(gmt) { + /* Convert %Z to "GMT" and %z to "+0000"; + * on hosts that do not have a time zone string in struct tm, + * strftime must assume its argument is local time. + */ + const char *f; + char *strp; + for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f) + ; f++, strp++) { + if (*f != '%') continue; + switch (f[1]) { + case '%': + *++strp = *++f; + break; + case 'Z': + *strp++ = 'G'; + *strp++ = 'M'; + *strp = 'T'; + f++; + break; + case 'z': /* common extension */ + *strp++ = '+'; + *strp++ = '0'; + *strp++ = '0'; + *strp++ = '0'; + *strp = '0'; + f++; + break; + } + } + *strp = '\0'; + fmt = tf; + } + + /* check return code? */ + strftime(ts, MAX_STRING_LEN, fmt, tms); + ts[MAX_STRING_LEN - 1] = '\0'; + return ap_pstrdup(p, ts); +} + +API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t sec) +{ + struct tm *tms; + + tms = gmtime(&sec); + + /* RFC date format; as strftime '%a, %d %b %Y %T GMT' */ + return ap_psprintf(p, + "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[tms->tm_wday], + tms->tm_mday, ap_month_snames[tms->tm_mon], tms->tm_year + 1900, + tms->tm_hour, tms->tm_min, tms->tm_sec); +} + +/* What a pain in the ass. */ +#if defined(HAVE_GMTOFF) +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz) +{ + time_t tt = time(NULL); + struct tm *t; + + t = localtime(&tt); + *tz = (int) (t->tm_gmtoff / 60); + return t; +} +#else +API_EXPORT(struct tm *) ap_get_gmtoff(int *tz) +{ + time_t tt = time(NULL); + struct tm gmt; + struct tm *t; + int days, hours, minutes; + + /* Assume we are never more than 24 hours away. */ + gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */ + t = localtime(&tt); /* buffer... so be careful */ + days = t->tm_yday - gmt.tm_yday; + hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + + t->tm_hour - gmt.tm_hour); + minutes = hours * 60 + t->tm_min - gmt.tm_min; + *tz = minutes; + return t; +} +#endif + +/* Roy owes Rob beer. */ +/* Rob owes Roy dinner. */ + +/* These legacy comments would make a lot more sense if Roy hadn't + * replaced the old later_than() routine with util_date.c. + * + * Well, okay, they still wouldn't make any sense. + */ + +/* Match = 0, NoMatch = 1, Abort = -1 + * Based loosely on sections of wildmat.c by Rich Salz + * Hmmm... shouldn't this really go component by component? + */ +API_EXPORT(int) ap_strcmp_match(const char *str, const char *exp) +{ + int x, y; + + for (x = 0, y = 0; exp[y]; ++y, ++x) { + if ((!str[x]) && (exp[y] != '*')) + return -1; + if (exp[y] == '*') { + while (exp[++y] == '*'); + if (!exp[y]) + return 0; + while (str[x]) { + int ret; + if ((ret = ap_strcmp_match(&str[x++], &exp[y])) != 1) + return ret; + } + return -1; + } + else if ((exp[y] != '?') && (str[x] != exp[y])) + return 1; + } + return (str[x] != '\0'); +} + +API_EXPORT(int) ap_strcasecmp_match(const char *str, const char *exp) +{ + int x, y; + + for (x = 0, y = 0; exp[y]; ++y, ++x) { + if ((!str[x]) && (exp[y] != '*')) + return -1; + if (exp[y] == '*') { + while (exp[++y] == '*'); + if (!exp[y]) + return 0; + while (str[x]) { + int ret; + if ((ret = ap_strcasecmp_match(&str[x++], &exp[y])) != 1) + return ret; + } + return -1; + } + else if ((exp[y] != '?') && (ap_tolower(str[x]) != ap_tolower(exp[y]))) + return 1; + } + return (str[x] != '\0'); +} + +API_EXPORT(int) ap_is_matchexp(const char *str) +{ + register int x; + + for (x = 0; str[x]; x++) + if ((str[x] == '*') || (str[x] == '?')) + return 1; + return 0; +} + +/* + * Apache stub function for the regex libraries regexec() to make sure the + * whole regex(3) API is available through the Apache (exported) namespace. + * This is especially important for the DSO situations of modules. + * DO NOT MAKE A MACRO OUT OF THIS FUNCTION! + */ +API_EXPORT(int) ap_regexec(const regex_t *preg, const char *string, + size_t nmatch, regmatch_t pmatch[], int eflags) +{ + return regexec(preg, string, nmatch, pmatch, eflags); +} + +API_EXPORT(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) +{ + return regerror(errcode, preg, errbuf, errbuf_size); +} + + +/* This function substitutes for $0-$9, filling in regular expression + * submatches. Pass it the same nmatch and pmatch arguments that you + * passed ap_regexec(). pmatch should not be greater than the maximum number + * of subexpressions - i.e. one more than the re_nsub member of regex_t. + * + * input should be the string with the $-expressions, source should be the + * string that was matched against. + * + * It returns the substituted string, or NULL on error. + * + * Parts of this code are based on Henry Spencer's regsub(), from his + * AT&T V8 regexp package. + */ + +API_EXPORT(char *) ap_pregsub(pool *p, const char *input, const char *source, + size_t nmatch, regmatch_t pmatch[]) +{ + const char *src = input; + char *dest, *dst; + char c; + size_t no; + int len; + + if (!source) + return NULL; + if (!nmatch) + return ap_pstrdup(p, src); + + /* First pass, find the size */ + + len = 0; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && ap_isdigit(*src)) + no = *src++ - '0'; + else + no = 10; + + if (no > 9) { /* Ordinary character. */ + if (c == '\\' && (*src == '$' || *src == '&')) + c = *src++; + len++; + } + else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { + len += pmatch[no].rm_eo - pmatch[no].rm_so; + } + + } + + dest = dst = ap_pcalloc(p, len + 1); + + /* Now actually fill in the string */ + + src = input; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && ap_isdigit(*src)) + no = *src++ - '0'; + else + no = 10; + + if (no > 9) { /* Ordinary character. */ + if (c == '\\' && (*src == '$' || *src == '&')) + c = *src++; + *dst++ = c; + } + else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { + len = pmatch[no].rm_eo - pmatch[no].rm_so; + memcpy(dst, source + pmatch[no].rm_so, len); + dst += len; + } + + } + *dst = '\0'; + + return dest; +} + +/* + * Parse .. so we don't compromise security + */ +API_EXPORT(void) ap_getparents(char *name) +{ + int l, w; + + /* Four paseses, as per RFC 1808 */ + /* a) remove ./ path segments */ + + for (l = 0, w = 0; name[l] != '\0';) { + if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/')) + l += 2; + else + name[w++] = name[l++]; + } + + /* b) remove trailing . path, segment */ + if (w == 1 && name[0] == '.') + w--; + else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/') + w--; + name[w] = '\0'; + + /* c) remove all xx/../ segments. (including leading ../ and /../) */ + l = 0; + + while (name[l] != '\0') { + if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' && + (l == 0 || name[l - 1] == '/')) { + register int m = l + 3, n; + + l = l - 2; + if (l >= 0) { + while (l >= 0 && name[l] != '/') + l--; + l++; + } + else + l = 0; + n = l; + while ((name[n] = name[m])) + (++n, ++m); + } + else + ++l; + } + + /* d) remove trailing xx/.. segment. */ + if (l == 2 && name[0] == '.' && name[1] == '.') + name[0] = '\0'; + else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') { + l = l - 4; + if (l >= 0) { + while (l >= 0 && name[l] != '/') + l--; + l++; + } + else + l = 0; + name[l] = '\0'; + } +} + +API_EXPORT(void) ap_no2slash(char *name) +{ + char *d, *s; + + s = d = name; + +#ifdef WIN32 + /* Check for UNC names. Leave leading two slashes. */ + if (s[0] == '/' && s[1] == '/') + *d++ = *s++; +#endif + + while (*s) { + if ((*d++ = *s) == '/') { + do { + ++s; + } while (*s == '/'); + } + else { + ++s; + } + } + *d = '\0'; +} + + +/* + * copy at most n leading directories of s into d + * d should be at least as large as s plus 1 extra byte + * assumes n > 0 + * the return value is the ever useful pointer to the trailing \0 of d + * + * examples: + * /a/b, 1 ==> / + * /a/b, 2 ==> /a/ + * /a/b, 3 ==> /a/b/ + * /a/b, 4 ==> /a/b/ + */ +API_EXPORT(char *) ap_make_dirstr_prefix(char *d, const char *s, int n) +{ + for (;;) { + *d = *s; + if (*d == '\0') { + *d = '/'; + break; + } + if (*d == '/' && (--n) == 0) + break; + ++d; + ++s; + } + *++d = 0; + return (d); +} + + +/* + * return the parent directory name including trailing / of the file s + */ +API_EXPORT(char *) ap_make_dirstr_parent(pool *p, const char *s) +{ + char *last_slash = strrchr(s, '/'); + char *d; + int l; + + if (last_slash == NULL) { + /* XXX: well this is really broken if this happens */ + return (ap_pstrdup(p, "/")); + } + l = (last_slash - s) + 1; + d = ap_palloc(p, l + 1); + memcpy(d, s, l); + d[l] = 0; + return (d); +} + + +/* + * This function is deprecated. Use one of the preceeding two functions + * which are faster. + */ +API_EXPORT(char *) ap_make_dirstr(pool *p, const char *s, int n) +{ + register int x, f; + char *res; + + for (x = 0, f = 0; s[x]; x++) { + if (s[x] == '/') + if ((++f) == n) { + res = ap_palloc(p, x + 2); + memcpy(res, s, x); + res[x] = '/'; + res[x + 1] = '\0'; + return res; + } + } + + if (s[strlen(s) - 1] == '/') + return ap_pstrdup(p, s); + else + return ap_pstrcat(p, s, "/", NULL); +} + +API_EXPORT(int) ap_count_dirs(const char *path) +{ + register int x, n; + + for (x = 0, n = 0; path[x]; x++) + if (path[x] == '/') + n++; + return n; +} + + +API_EXPORT(void) ap_chdir_file(const char *file) +{ + const char *x; + char buf[HUGE_STRING_LEN]; + + x = strrchr(file, '/'); + if (x == NULL) { + chdir(file); + } + else if (x - file < sizeof(buf) - 1) { + memcpy(buf, file, x - file); + buf[x - file] = '\0'; + chdir(buf); + } + /* XXX: well, this is a silly function, no method of reporting an + * error... ah well. */ +} + +API_EXPORT(char *) ap_getword_nc(pool *atrans, char **line, char stop) +{ + return ap_getword(atrans, (const char **) line, stop); +} + +API_EXPORT(char *) ap_getword(pool *atrans, const char **line, char stop) +{ + char *pos = strchr(*line, stop); + char *res; + + if (!pos) { + res = ap_pstrdup(atrans, *line); + *line += strlen(*line); + return res; + } + + res = ap_pstrndup(atrans, *line, pos - *line); + + while (*pos == stop) { + ++pos; + } + + *line = pos; + + return res; +} + +API_EXPORT(char *) ap_getword_white_nc(pool *atrans, char **line) +{ + return ap_getword_white(atrans, (const char **) line); +} + +API_EXPORT(char *) ap_getword_white(pool *atrans, const char **line) +{ + int pos = -1, x; + char *res; + + for (x = 0; (*line)[x]; x++) { + if (ap_isspace((*line)[x])) { + pos = x; + break; + } + } + + if (pos == -1) { + res = ap_pstrdup(atrans, *line); + *line += strlen(*line); + return res; + } + + res = ap_palloc(atrans, pos + 1); + ap_cpystrn(res, *line, pos + 1); + + while (ap_isspace((*line)[pos])) + ++pos; + + *line += pos; + + return res; +} + +API_EXPORT(char *) ap_getword_nulls_nc(pool *atrans, char **line, char stop) +{ + return ap_getword_nulls(atrans, (const char **) line, stop); +} + +API_EXPORT(char *) ap_getword_nulls(pool *atrans, const char **line, char stop) +{ + char *pos = strchr(*line, stop); + char *res; + + if (!pos) { + res = ap_pstrdup(atrans, *line); + *line += strlen(*line); + return res; + } + + res = ap_pstrndup(atrans, *line, pos - *line); + + ++pos; + + *line = pos; + + return res; +} + +/* Get a word, (new) config-file style --- quoted strings and backslashes + * all honored + */ + +static char *substring_conf(pool *p, const char *start, int len, char quote) +{ + char *result = ap_palloc(p, len + 2); + char *resp = result; + int i; + + for (i = 0; i < len; ++i) { + if (start[i] == '\\' && (start[i + 1] == '\\' + || (quote && start[i + 1] == quote))) + *resp++ = start[++i]; + else + *resp++ = start[i]; + } + + *resp++ = '\0'; + return result; +} + +API_EXPORT(char *) ap_getword_conf_nc(pool *p, char **line) +{ + return ap_getword_conf(p, (const char **) line); +} + +API_EXPORT(char *) ap_getword_conf(pool *p, const char **line) +{ + const char *str = *line, *strend; + char *res; + char quote; + + while (*str && ap_isspace(*str)) + ++str; + + if (!*str) { + *line = str; + return ""; + } + + if ((quote = *str) == '"' || quote == '\'') { + strend = str + 1; + while (*strend && *strend != quote) { + if (*strend == '\\' && strend[1] && strend[1] == quote) + strend += 2; + else + ++strend; + } + res = substring_conf(p, str + 1, strend - str - 1, quote); + + if (*strend == quote) + ++strend; + } + else { + strend = str; + while (*strend && !ap_isspace(*strend)) + ++strend; + + res = substring_conf(p, str, strend - str, 0); + } + + while (*strend && ap_isspace(*strend)) + ++strend; + *line = strend; + return res; +} + +API_EXPORT(int) ap_cfg_closefile(configfile_t *cfp) +{ +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, + "Done with config file %s", cfp->name); +#endif + return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); +} + +/* Common structure that holds the file and pool for ap_pcfg_openfile */ +typedef struct { + struct pool *pool; + FILE *file; +} poolfile_t; + +static int cfg_close(void *param) +{ + poolfile_t *cfp = (poolfile_t *) param; + return (ap_pfclose(cfp->pool, cfp->file)); +} + +static int cfg_getch(void *param) +{ + poolfile_t *cfp = (poolfile_t *) param; + return (fgetc(cfp->file)); +} + +static void *cfg_getstr(void *buf, size_t bufsiz, void *param) +{ + poolfile_t *cfp = (poolfile_t *) param; + return (fgets(buf, bufsiz, cfp->file)); +} + +/* Open a configfile_t as FILE, return open configfile_t struct pointer */ +API_EXPORT(configfile_t *) ap_pcfg_openfile(pool *p, const char *name) +{ + configfile_t *new_cfg; + poolfile_t *new_pfile; + FILE *file; + struct stat stbuf; + int saved_errno; + + if (name == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "Internal error: pcfg_openfile() called with NULL filename"); + return NULL; + } + + if (!ap_os_is_filename_valid(name)) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "Access to config file %s denied: not a valid filename", + name); + errno = EACCES; + return NULL; + } + + file = ap_pfopen(p, name, "r"); +#ifdef DEBUG + saved_errno = errno; + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL, + "Opening config file %s (%s)", + name, (file == NULL) ? strerror(errno) : "successful"); + errno = saved_errno; +#endif + if (file == NULL) + return NULL; + + if (fstat(fileno(file), &stbuf) == 0 && + !S_ISREG(stbuf.st_mode) && +#if defined(WIN32) || defined(OS2) + !(strcasecmp(name, "nul") == 0 || + (strlen(name) >= 4 && + strcasecmp(name + strlen(name) - 4, "/nul") == 0))) { +#else + strcmp(name, "/dev/null") != 0) { +#endif /* WIN32 || OS2 */ + saved_errno = errno; + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "Access to file %s denied by server: not a regular file", + name); + ap_pfclose(p, file); + errno = saved_errno; + return NULL; + } + + new_cfg = ap_palloc(p, sizeof(*new_cfg)); + new_pfile = ap_palloc(p, sizeof(*new_pfile)); + new_pfile->file = file; + new_pfile->pool = p; + new_cfg->param = new_pfile; + new_cfg->name = ap_pstrdup(p, name); + new_cfg->getch = (int (*)(void *)) cfg_getch; + new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr; + new_cfg->close = (int (*)(void *)) cfg_close; + new_cfg->line_number = 0; + return new_cfg; +} + + +/* Allocate a configfile_t handle with user defined functions and params */ +API_EXPORT(configfile_t *) ap_pcfg_open_custom(pool *p, const char *descr, + void *param, + int(*getch)(void *param), + void *(*getstr) (void *buf, size_t bufsiz, void *param), + int(*close_func)(void *param)) +{ + configfile_t *new_cfg = ap_palloc(p, sizeof(*new_cfg)); +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL, "Opening config handler %s", descr); +#endif + new_cfg->param = param; + new_cfg->name = descr; + new_cfg->getch = getch; + new_cfg->getstr = getstr; + new_cfg->close = close_func; + new_cfg->line_number = 0; + return new_cfg; +} + + +/* Read one character from a configfile_t */ +API_EXPORT(int) ap_cfg_getc(configfile_t *cfp) +{ + register int ch = cfp->getch(cfp->param); + if (ch == LF) + ++cfp->line_number; + return ch; +} + + +/* Read one line from open configfile_t, strip LF, increase line number */ +/* If custom handler does not define a getstr() function, read char by char */ +API_EXPORT(int) ap_cfg_getline(char *buf, size_t bufsize, configfile_t *cfp) +{ + /* If a "get string" function is defined, use it */ + if (cfp->getstr != NULL) { + char *src, *dst; + char *cp; + char *cbuf = buf; + size_t cbufsize = bufsize; + + while (1) { + ++cfp->line_number; + if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL) + return 1; + + /* + * check for line continuation, + * i.e. match [^\\]\\[\r]\n only + */ + cp = cbuf; + while (cp < cbuf+cbufsize && *cp != '\0') + cp++; + if (cp > cbuf && cp[-1] == LF) { + cp--; + if (cp > cbuf && cp[-1] == CR) + cp--; + if (cp > cbuf && cp[-1] == '\\') { + cp--; + if (!(cp > cbuf && cp[-1] == '\\')) { + /* + * line continuation requested - + * then remove backslash and continue + */ + cbufsize -= (cp-cbuf); + cbuf = cp; + continue; + } + else { + /* + * no real continuation because escaped - + * then just remove escape character + */ + for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++) + cp[0] = cp[1]; + } + } + } + break; + } + + /* + * Leading and trailing white space is eliminated completely + */ + src = buf; + while (ap_isspace(*src)) + ++src; + /* blast trailing whitespace */ + dst = &src[strlen(src)]; + while (--dst >= src && ap_isspace(*dst)) + *dst = '\0'; + /* Zap leading whitespace by shifting */ + if (src != buf) + for (dst = buf; (*dst++ = *src++) != '\0'; ) + ; + +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf); +#endif + return 0; + } else { + /* No "get string" function defined; read character by character */ + register int c; + register size_t i = 0; + + buf[0] = '\0'; + /* skip leading whitespace */ + do { + c = cfp->getch(cfp->param); + } while (c == '\t' || c == ' '); + + if (c == EOF) + return 1; + + if(bufsize < 2) { + /* too small, assume caller is crazy */ + return 1; + } + + while (1) { + if ((c == '\t') || (c == ' ')) { + buf[i++] = ' '; + while ((c == '\t') || (c == ' ')) + c = cfp->getch(cfp->param); + } + if (c == CR) { + /* silently ignore CR (_assume_ that a LF follows) */ + c = cfp->getch(cfp->param); + } + if (c == LF) { + /* increase line number and return on LF */ + ++cfp->line_number; + } + if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) { + /* + * check for line continuation + */ + if (i > 0 && buf[i-1] == '\\') { + i--; + if (!(i > 0 && buf[i-1] == '\\')) { + /* line is continued */ + c = cfp->getch(cfp->param); + continue; + } + /* else nothing needs be done because + * then the backslash is escaped and + * we just strip to a single one + */ + } + /* blast trailing whitespace */ + while (i > 0 && ap_isspace(buf[i - 1])) + --i; + buf[i] = '\0'; +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf); +#endif + return 0; + } + buf[i] = c; + ++i; + c = cfp->getch(cfp->param); + } + } +} + +/* Size an HTTP header field list item, as separated by a comma. + * The return value is a pointer to the beginning of the non-empty list item + * within the original string (or NULL if there is none) and the address + * of field is shifted to the next non-comma, non-whitespace character. + * len is the length of the item excluding any beginning whitespace. + */ +API_EXPORT(const char *) ap_size_list_item(const char **field, int *len) +{ + const unsigned char *ptr = (const unsigned char *)*field; + const unsigned char *token; + int in_qpair, in_qstr, in_com; + + /* Find first non-comma, non-whitespace byte */ + + while (*ptr == ',' || ap_isspace(*ptr)) + ++ptr; + + token = ptr; + + /* Find the end of this item, skipping over dead bits */ + + for (in_qpair = in_qstr = in_com = 0; + *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); + ++ptr) { + + if (in_qpair) { + in_qpair = 0; + } + else { + switch (*ptr) { + case '\\': in_qpair = 1; /* quoted-pair */ + break; + case '"' : if (!in_com) /* quoted string delim */ + in_qstr = !in_qstr; + break; + case '(' : if (!in_qstr) /* comment (may nest) */ + ++in_com; + break; + case ')' : if (in_com) /* end comment */ + --in_com; + break; + default : break; + } + } + } + + if ((*len = (ptr - token)) == 0) { + *field = (const char *)ptr; + return NULL; + } + + /* Advance field pointer to the next non-comma, non-white byte */ + + while (*ptr == ',' || ap_isspace(*ptr)) + ++ptr; + + *field = (const char *)ptr; + return (const char *)token; +} + +/* Retrieve an HTTP header field list item, as separated by a comma, + * while stripping insignificant whitespace and lowercasing anything not in + * a quoted string or comment. The return value is a new string containing + * the converted list item (or NULL if none) and the address pointed to by + * field is shifted to the next non-comma, non-whitespace. + */ +API_EXPORT(char *) ap_get_list_item(pool *p, const char **field) +{ + const char *tok_start; + const unsigned char *ptr; + unsigned char *pos; + char *token; + int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0; + + /* Find the beginning and maximum length of the list item so that + * we can allocate a buffer for the new string and reset the field. + */ + if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) { + return NULL; + } + token = ap_palloc(p, tok_len + 1); + + /* Scan the token again, but this time copy only the good bytes. + * We skip extra whitespace and any whitespace around a '=', '/', + * or ';' and lowercase normal characters not within a comment, + * quoted-string or quoted-pair. + */ + for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token; + *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); + ++ptr) { + + if (in_qpair) { + in_qpair = 0; + *pos++ = *ptr; + } + else { + switch (*ptr) { + case '\\': in_qpair = 1; + if (addspace == 1) + *pos++ = ' '; + *pos++ = *ptr; + addspace = 0; + break; + case '"' : if (!in_com) + in_qstr = !in_qstr; + if (addspace == 1) + *pos++ = ' '; + *pos++ = *ptr; + addspace = 0; + break; + case '(' : if (!in_qstr) + ++in_com; + if (addspace == 1) + *pos++ = ' '; + *pos++ = *ptr; + addspace = 0; + break; + case ')' : if (in_com) + --in_com; + *pos++ = *ptr; + addspace = 0; + break; + case ' ' : + case '\t': if (addspace) + break; + if (in_com || in_qstr) + *pos++ = *ptr; + else + addspace = 1; + break; + case '=' : + case '/' : + case ';' : if (!(in_com || in_qstr)) + addspace = -1; + *pos++ = *ptr; + break; + default : if (addspace == 1) + *pos++ = ' '; + *pos++ = (in_com || in_qstr) ? *ptr + : ap_tolower(*ptr); + addspace = 0; + break; + } + } + } + *pos = '\0'; + + return token; +} + +/* Find an item in canonical form (lowercase, no extra spaces) within + * an HTTP field value list. Returns 1 if found, 0 if not found. + * This would be much more efficient if we stored header fields as + * an array of list items as they are received instead of a plain string. + */ +API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok) +{ + const unsigned char *pos; + const unsigned char *ptr = (const unsigned char *)line; + int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0; + + if (!line || !tok) + return 0; + + do { /* loop for each item in line's list */ + + /* Find first non-comma, non-whitespace byte */ + + while (*ptr == ',' || ap_isspace(*ptr)) + ++ptr; + + if (*ptr) + good = 1; /* until proven otherwise for this item */ + else + break; /* no items left and nothing good found */ + + /* We skip extra whitespace and any whitespace around a '=', '/', + * or ';' and lowercase normal characters not within a comment, + * quoted-string or quoted-pair. + */ + for (pos = (const unsigned char *)tok; + *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); + ++ptr) { + + if (in_qpair) { + in_qpair = 0; + if (good) + good = (*pos++ == *ptr); + } + else { + switch (*ptr) { + case '\\': in_qpair = 1; + if (addspace == 1) + good = good && (*pos++ == ' '); + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case '"' : if (!in_com) + in_qstr = !in_qstr; + if (addspace == 1) + good = good && (*pos++ == ' '); + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case '(' : if (!in_qstr) + ++in_com; + if (addspace == 1) + good = good && (*pos++ == ' '); + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case ')' : if (in_com) + --in_com; + good = good && (*pos++ == *ptr); + addspace = 0; + break; + case ' ' : + case '\t': if (addspace || !good) + break; + if (in_com || in_qstr) + good = (*pos++ == *ptr); + else + addspace = 1; + break; + case '=' : + case '/' : + case ';' : if (!(in_com || in_qstr)) + addspace = -1; + good = good && (*pos++ == *ptr); + break; + default : if (!good) + break; + if (addspace == 1) + good = (*pos++ == ' '); + if (in_com || in_qstr) + good = good && (*pos++ == *ptr); + else + good = good && (*pos++ == ap_tolower(*ptr)); + addspace = 0; + break; + } + } + } + if (good && *pos) + good = 0; /* not good if only a prefix was matched */ + + } while (*ptr && !good); + + return good; +} + + +/* Retrieve a token, spacing over it and returning a pointer to + * the first non-white byte afterwards. Note that these tokens + * are delimited by semis and commas; and can also be delimited + * by whitespace at the caller's option. + */ + +API_EXPORT(char *) ap_get_token(pool *p, const char **accept_line, int accept_white) +{ + const char *ptr = *accept_line; + const char *tok_start; + char *token; + int tok_len; + + /* Find first non-white byte */ + + while (*ptr && ap_isspace(*ptr)) + ++ptr; + + tok_start = ptr; + + /* find token end, skipping over quoted strings. + * (comments are already gone). + */ + + while (*ptr && (accept_white || !ap_isspace(*ptr)) + && *ptr != ';' && *ptr != ',') { + if (*ptr++ == '"') + while (*ptr) + if (*ptr++ == '"') + break; + } + + tok_len = ptr - tok_start; + token = ap_pstrndup(p, tok_start, tok_len); + + /* Advance accept_line pointer to the next non-white byte */ + + while (*ptr && ap_isspace(*ptr)) + ++ptr; + + *accept_line = ptr; + return token; +} + + +/* find http tokens, see the definition of token from RFC2068 */ +API_EXPORT(int) ap_find_token(pool *p, const char *line, const char *tok) +{ + const unsigned char *start_token; + const unsigned char *s; + + if (!line) + return 0; + + s = (const unsigned char *)line; + for (;;) { + /* find start of token, skip all stop characters, note NUL + * isn't a token stop, so we don't need to test for it + */ + while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { + ++s; + } + if (!*s) { + return 0; + } + start_token = s; + /* find end of the token */ + while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { + ++s; + } + if (!strncasecmp((const char *)start_token, (const char *)tok, s - start_token)) { + return 1; + } + if (!*s) { + return 0; + } + } +} + + +API_EXPORT(int) ap_find_last_token(pool *p, const char *line, const char *tok) +{ + int llen, tlen, lidx; + + if (!line) + return 0; + + llen = strlen(line); + tlen = strlen(tok); + lidx = llen - tlen; + + if ((lidx < 0) || + ((lidx > 0) && !(ap_isspace(line[lidx - 1]) || line[lidx - 1] == ','))) + return 0; + + return (strncasecmp(&line[lidx], tok, tlen) == 0); +} + +API_EXPORT(char *) ap_escape_shell_cmd(pool *p, const char *str) +{ + char *cmd; + unsigned char *d; + const unsigned char *s; + + cmd = ap_palloc(p, 2 * strlen(str) + 1); /* Be safe */ + d = (unsigned char *)cmd; + s = (const unsigned char *)str; + for (; *s; ++s) { + +#if defined(OS2) || defined(WIN32) + /* Don't allow '&' in parameters under OS/2. */ + /* This can be used to send commands to the shell. */ + if (*s == '&') { + *d++ = ' '; + continue; + } +#endif + + if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) { + *d++ = '\\'; + } + *d++ = *s; + } + *d = '\0'; + + return cmd; +} + +static char x2c(const char *what) +{ + register char digit; + +#ifndef CHARSET_EBCDIC + digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); +#else /*CHARSET_EBCDIC*/ + char xstr[5]; + xstr[0]='0'; + xstr[1]='x'; + xstr[2]=what[0]; + xstr[3]=what[1]; + xstr[4]='\0'; + digit = os_toebcdic[0xFF & strtol(xstr, NULL, 16)]; +#endif /*CHARSET_EBCDIC*/ + return (digit); +} + +/* + * Unescapes a URL. + * Returns 0 on success, non-zero on error + * Failure is due to + * bad % escape returns BAD_REQUEST + * + * decoding %00 -> \0 + * decoding %2f -> / (a special character) + * returns NOT_FOUND + */ +API_EXPORT(int) ap_unescape_url(char *url) +{ + register int x, y, badesc, badpath; + + badesc = 0; + badpath = 0; + for (x = 0, y = 0; url[y]; ++x, ++y) { + if (url[y] != '%') + url[x] = url[y]; + else { + if (!ap_isxdigit(url[y + 1]) || !ap_isxdigit(url[y + 2])) { + badesc = 1; + url[x] = '%'; + } + else { + url[x] = x2c(&url[y + 1]); + y += 2; + if (url[x] == '/' || url[x] == '\0') + badpath = 1; + } + } + } + url[x] = '\0'; + if (badesc) + return BAD_REQUEST; + else if (badpath) + return NOT_FOUND; + else + return OK; +} + +API_EXPORT(char *) ap_construct_server(pool *p, const char *hostname, + unsigned port, const request_rec *r) +{ + if (ap_is_default_port(port, r)) + return ap_pstrdup(p, hostname); + else { + return ap_psprintf(p, "%s:%u", hostname, port); + } +} + +/* c2x takes an unsigned, and expects the caller has guaranteed that + * 0 <= what < 256... which usually means that you have to cast to + * unsigned char first, because (unsigned)(char)(x) fist goes through + * signed extension to an int before the unsigned cast. + * + * The reason for this assumption is to assist gcc code generation -- + * the unsigned char -> unsigned extension is already done earlier in + * both uses of this code, so there's no need to waste time doing it + * again. + */ +static const char c2x_table[] = "0123456789abcdef"; + +static ap_inline unsigned char *c2x(unsigned what, unsigned char *where) +{ + *where++ = '%'; + *where++ = c2x_table[what >> 4]; + *where++ = c2x_table[what & 0xf]; + return where; +} + +/* + * escape_path_segment() escapes a path segment, as defined in RFC 1808. This + * routine is (should be) OS independent. + * + * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all + * cases if a ':' occurs before the first '/' in the URL, the URL should be + * prefixed with "./" (or the ':' escaped). In the case of Unix, this means + * leaving '/' alone, but otherwise doing what escape_path_segment() does. For + * efficiency reasons, we don't use escape_path_segment(), which is provided for + * reference. Again, RFC 1808 is where this stuff is defined. + * + * If partial is set, os_escape_path() assumes that the path will be appended to + * something with a '/' in it (and thus does not prefix "./"). + */ + +API_EXPORT(char *) ap_escape_path_segment(pool *p, const char *segment) +{ + char *copy = ap_palloc(p, 3 * strlen(segment) + 1); + const unsigned char *s = (const unsigned char *)segment; + unsigned char *d = (unsigned char *)copy; + unsigned c; + + while ((c = *s)) { + if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) { + d = c2x(c, d); + } + else { + *d++ = c; + } + ++s; + } + *d = '\0'; + return copy; +} + +API_EXPORT(char *) ap_os_escape_path(pool *p, const char *path, int partial) +{ + char *copy = ap_palloc(p, 3 * strlen(path) + 3); + const unsigned char *s = (const unsigned char *)path; + unsigned char *d = (unsigned char *)copy; + unsigned c; + + if (!partial) { + char *colon = strchr(path, ':'); + char *slash = strchr(path, '/'); + + if (colon && (!slash || colon < slash)) { + *d++ = '.'; + *d++ = '/'; + } + } + while ((c = *s)) { + if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) { + d = c2x(c, d); + } + else { + *d++ = c; + } + ++s; + } + *d = '\0'; + return copy; +} + +/* ap_escape_uri is now a macro for os_escape_path */ + +API_EXPORT(char *) ap_escape_html(pool *p, const char *s) +{ + int i, j; + char *x; + + /* first, count the number of extra characters */ + for (i = 0, j = 0; s[i] != '\0'; i++) + if (s[i] == '<' || s[i] == '>') + j += 3; + else if (s[i] == '&') + j += 4; + + if (j == 0) + return ap_pstrndup(p, s, i); + + x = ap_palloc(p, i + j + 1); + for (i = 0, j = 0; s[i] != '\0'; i++, j++) + if (s[i] == '<') { + memcpy(&x[j], "<", 4); + j += 3; + } + else if (s[i] == '>') { + memcpy(&x[j], ">", 4); + j += 3; + } + else if (s[i] == '&') { + memcpy(&x[j], "&", 5); + j += 4; + } + else + x[j] = s[i]; + + x[j] = '\0'; + return x; +} + +API_EXPORT(int) ap_is_directory(const char *path) +{ + struct stat finfo; + + if (stat(path, &finfo) == -1) + return 0; /* in error condition, just return no */ + + return (S_ISDIR(finfo.st_mode)); +} + +API_EXPORT(char *) ap_make_full_path(pool *a, const char *src1, + const char *src2) +{ + register int x; + + x = strlen(src1); + if (x == 0) + return ap_pstrcat(a, "/", src2, NULL); + + if (src1[x - 1] != '/') + return ap_pstrcat(a, src1, "/", src2, NULL); + else + return ap_pstrcat(a, src1, src2, NULL); +} + +/* + * Check for an absoluteURI syntax (see section 3.2 in RFC2068). + */ +API_EXPORT(int) ap_is_url(const char *u) +{ + register int x; + + for (x = 0; u[x] != ':'; x++) { + if ((!u[x]) || + ((!ap_isalpha(u[x])) && (!ap_isdigit(u[x])) && + (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) { + return 0; + } + } + + return (x ? 1 : 0); /* If the first character is ':', it's broken, too */ +} + +API_EXPORT(int) ap_can_exec(const struct stat *finfo) +{ +#ifdef MULTIPLE_GROUPS + int cnt; +#endif +#if defined(OS2) || defined(WIN32) + /* OS/2 dosen't have Users and Groups */ + return 1; +#else + if (ap_user_id == finfo->st_uid) + if (finfo->st_mode & S_IXUSR) + return 1; + if (ap_group_id == finfo->st_gid) + if (finfo->st_mode & S_IXGRP) + return 1; +#ifdef MULTIPLE_GROUPS + for (cnt = 0; cnt < NGROUPS_MAX; cnt++) { + if (group_id_list[cnt] == finfo->st_gid) + if (finfo->st_mode & S_IXGRP) + return 1; + } +#endif + return (finfo->st_mode & S_IXOTH); +#endif +} + +#ifdef NEED_STRDUP +char *strdup(const char *str) +{ + char *sdup; + + if (!(sdup = (char *) malloc(strlen(str) + 1))) { + fprintf(stderr, "Ouch! Out of memory in our strdup()!\n"); + return NULL; + } + sdup = strcpy(sdup, str); + + return sdup; +} +#endif + +/* The following two routines were donated for SVR4 by Andreas Vogel */ +#ifdef NEED_STRCASECMP +int strcasecmp(const char *a, const char *b) +{ + const char *p = a; + const char *q = b; + for (p = a, q = b; *p && *q; p++, q++) { + int diff = ap_tolower(*p) - ap_tolower(*q); + if (diff) + return diff; + } + if (*p) + return 1; /* p was longer than q */ + if (*q) + return -1; /* p was shorter than q */ + return 0; /* Exact match */ +} + +#endif + +#ifdef NEED_STRNCASECMP +int strncasecmp(const char *a, const char *b, int n) +{ + const char *p = a; + const char *q = b; + + for (p = a, q = b; /*NOTHING */ ; p++, q++) { + int diff; + if (p == a + n) + return 0; /* Match up to n characters */ + if (!(*p && *q)) + return *p - *q; + diff = ap_tolower(*p) - ap_tolower(*q); + if (diff) + return diff; + } + /*NOTREACHED */ +} +#endif + +/* The following routine was donated for UTS21 by dwd@bell-labs.com */ +#ifdef NEED_STRSTR +char *strstr(char *s1, char *s2) +{ + char *p1, *p2; + if (*s2 == '\0') { + /* an empty s2 */ + return(s1); + } + while((s1 = strchr(s1, *s2)) != NULL) { + /* found first character of s2, see if the rest matches */ + p1 = s1; + p2 = s2; + while (*++p1 == *++p2) { + if (*p1 == '\0') { + /* both strings ended together */ + return(s1); + } + } + if (*p2 == '\0') { + /* second string ended, a match */ + break; + } + /* didn't find a match here, try starting at next character in s1 */ + s1++; + } + return(s1); +} +#endif + +#ifdef NEED_INITGROUPS +int initgroups(const char *name, gid_t basegid) +{ +#if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM) +/* QNX, MPE and BeOS do not appear to support supplementary groups. */ + return 0; +#else /* ndef QNX */ + gid_t groups[NGROUPS_MAX]; + struct group *g; + int index = 0; + + setgrent(); + + groups[index++] = basegid; + + while (index < NGROUPS_MAX && ((g = getgrent()) != NULL)) + if (g->gr_gid != basegid) { + char **names; + + for (names = g->gr_mem; *names != NULL; ++names) + if (!strcmp(*names, name)) + groups[index++] = g->gr_gid; + } + + endgrent(); + + return setgroups(index, groups); +#endif /* def QNX */ +} +#endif /* def NEED_INITGROUPS */ + +#ifdef NEED_WAITPID +/* From ikluft@amdahl.com + * this is not ideal but it works for SVR3 variants + * Modified by dwd@bell-labs.com to call wait3 instead of wait because + * apache started to use the WNOHANG option. + */ +int waitpid(pid_t pid, int *statusp, int options) +{ + int tmp_pid; + if (kill(pid, 0) == -1) { + errno = ECHILD; + return -1; + } + while (((tmp_pid = wait3(statusp, options, 0)) != pid) && + (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1)) + ; + return tmp_pid; +} +#endif + +API_EXPORT(int) ap_ind(const char *s, char c) +{ + register int x; + + for (x = 0; s[x]; x++) + if (s[x] == c) + return x; + + return -1; +} + +API_EXPORT(int) ap_rind(const char *s, char c) +{ + register int x; + + for (x = strlen(s) - 1; x != -1; x--) + if (s[x] == c) + return x; + + return -1; +} + +API_EXPORT(void) ap_str_tolower(char *str) +{ + while (*str) { + *str = ap_tolower(*str); + ++str; + } +} + +API_EXPORT(uid_t) ap_uname2id(const char *name) +{ +#ifdef WIN32 + return (1); +#else + struct passwd *ent; + + if (name[0] == '#') + return (atoi(&name[1])); + + if (!(ent = getpwnam(name))) { + fprintf(stderr, "%s: bad user name %s\n", ap_server_argv0, name); + exit(1); + } + return (ent->pw_uid); +#endif +} + +API_EXPORT(gid_t) ap_gname2id(const char *name) +{ +#ifdef WIN32 + return (1); +#else + struct group *ent; + + if (name[0] == '#') + return (atoi(&name[1])); + + if (!(ent = getgrnam(name))) { + fprintf(stderr, "%s: bad group name %s\n", ap_server_argv0, name); + exit(1); + } + return (ent->gr_gid); +#endif +} + + +/* + * Parses a host of the form
[:port] + * :port is permitted if 'port' is not NULL + */ +unsigned long ap_get_virthost_addr(char *w, unsigned short *ports) +{ + struct hostent *hep; + unsigned long my_addr; + char *p; + + p = strchr(w, ':'); + if (ports != NULL) { + *ports = 0; + if (p != NULL && strcmp(p + 1, "*") != 0) + *ports = atoi(p + 1); + } + + if (p != NULL) + *p = '\0'; + if (strcmp(w, "*") == 0) { + if (p != NULL) + *p = ':'; + return htonl(INADDR_ANY); + } + + my_addr = ap_inet_addr((char *)w); + if (my_addr != INADDR_NONE) { + if (p != NULL) + *p = ':'; + return my_addr; + } + + hep = gethostbyname(w); + + if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w); + exit(1); + } + + if (hep->h_addr_list[1]) { + fprintf(stderr, "Host %s has multiple addresses ---\n", w); + fprintf(stderr, "you must choose one explicitly for use as\n"); + fprintf(stderr, "a virtual host. Exiting!!!\n"); + exit(1); + } + + if (p != NULL) + *p = ':'; + + return ((struct in_addr *) (hep->h_addr))->s_addr; +} + + +static char *find_fqdn(pool *a, struct hostent *p) +{ + int x; + + if (!strchr(p->h_name, '.')) { + for (x = 0; p->h_aliases[x]; ++x) { + if (strchr(p->h_aliases[x], '.') && + (!strncasecmp(p->h_aliases[x], p->h_name, strlen(p->h_name)))) + return ap_pstrdup(a, p->h_aliases[x]); + } + return NULL; + } + return ap_pstrdup(a, (void *) p->h_name); +} + +char *ap_get_local_host(pool *a) +{ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + char str[MAXHOSTNAMELEN + 1]; + char *server_hostname; + struct hostent *p; + +#ifdef BEOS /* BeOS returns zero as an error for gethostname */ + if (gethostname(str, sizeof(str) - 1) == 0) { +#else + if (gethostname(str, sizeof(str) - 1) != 0) { +#endif /* BeOS */ + perror("Unable to gethostname"); + exit(1); + } + str[MAXHOSTNAMELEN] = '\0'; + if ((!(p = gethostbyname(str))) || (!(server_hostname = find_fqdn(a, p)))) { + fprintf(stderr, "%s: cannot determine local host name.\n", + ap_server_argv0); + fprintf(stderr, "Use the ServerName directive to set it manually.\n"); + exit(1); + } + + return server_hostname; +} + +/* simple 'pool' alloc()ing glue to ap_base64.c + */ +API_EXPORT(char *) ap_pbase64decode(pool *p, const char *bufcoded) +{ + char *decoded; + int l; + + decoded = (char *) ap_palloc(p, 1 + ap_base64decode_len(bufcoded)); + l = ap_base64decode(decoded, bufcoded); + decoded[l] = '\0'; /* make binary sequence into string */ + + return decoded; +} + +API_EXPORT(char *) ap_pbase64encode(pool *p, char *string) +{ + char *encoded; + int l = strlen(string); + + encoded = (char *) ap_palloc(p, 1 + ap_base64encode_len(l)); + l = ap_base64encode(encoded, string, l); + encoded[l] = '\0'; /* make binary sequence into string */ + + return encoded; +} + +/* deprecated names for the above two functions, here for compatibility + */ +API_EXPORT(char *) ap_uudecode(pool *p, const char *bufcoded) +{ + return ap_pbase64decode(p, bufcoded); +} + +API_EXPORT(char *) ap_uuencode(pool *p, char *string) +{ + return ap_pbase64encode(p, string); +} + +#ifdef OS2 +void os2pathname(char *path) +{ + char newpath[MAX_STRING_LEN]; + int loop; + int offset; + + offset = 0; + for (loop = 0; loop < (strlen(path) + 1) && loop < sizeof(newpath) - 1; loop++) { + if (path[loop] == '/') { + newpath[offset] = '\\'; + /* + offset = offset + 1; + newpath[offset] = '\\'; + */ + } + else + newpath[offset] = path[loop]; + offset = offset + 1; + }; + /* Debugging code */ + /* fprintf(stderr, "%s \n", newpath); */ + + strcpy(path, newpath); +}; + +/* quotes in the string are doubled up. + * Used to escape quotes in args passed to OS/2's cmd.exe + */ +char *ap_double_quotes(pool *p, char *str) +{ + int num_quotes = 0; + int len = 0; + char *quote_doubled_str, *dest; + + while (str[len]) { + num_quotes += str[len++] == '\"'; + } + + quote_doubled_str = ap_palloc(p, len + num_quotes + 1); + dest = quote_doubled_str; + + while (*str) { + if (*str == '\"') + *(dest++) = '\"'; + *(dest++) = *(str++); + } + + *dest = 0; + return quote_doubled_str; +} +#endif + + +#ifdef NEED_STRERROR +char * + strerror(int err) +{ + + char *p; + extern char *const sys_errlist[]; + + p = sys_errlist[err]; + return (p); +} +#endif + +#if defined(NEED_DIFFTIME) +double difftime(time_t time1, time_t time0) +{ + return (time1 - time0); +} +#endif + +/* we want to downcase the type/subtype for comparison purposes + * but nothing else because ;parameter=foo values are case sensitive. + * XXX: in truth we want to downcase parameter names... but really, + * apache has never handled parameters and such correctly. You + * also need to compress spaces and such to be able to compare + * properly. -djg + */ +API_EXPORT(void) ap_content_type_tolower(char *str) +{ + char *semi; + + semi = strchr(str, ';'); + if (semi) { + *semi = '\0'; + } + while (*str) { + *str = ap_tolower(*str); + ++str; + } + if (semi) { + *semi = ';'; + } +} + +/* + * Given a string, replace any bare " with \" . + */ +API_EXPORT(char *) ap_escape_quotes (pool *p, const char *instring) +{ + int newlen = 0; + const char *inchr = instring; + char *outchr, *outstring; + + /* + * Look through the input string, jogging the length of the output + * string up by an extra byte each time we find an unescaped ". + */ + while (*inchr != '\0') { + newlen++; + if (*inchr == '"') { + newlen++; + } + /* + * If we find a slosh, and it's not the last byte in the string, + * it's escaping something - advance past both bytes. + */ + if ((*inchr == '\\') && (inchr[1] != '\0')) { + inchr++; + newlen++; + } + inchr++; + } + outstring = ap_palloc(p, newlen + 1); + inchr = instring; + outchr = outstring; + /* + * Now copy the input string to the output string, inserting a slosh + * in front of every " that doesn't already have one. + */ + while (*inchr != '\0') { + if ((*inchr == '\\') && (inchr[1] != '\0')) { + *outchr++ = *inchr++; + *outchr++ = *inchr++; + } + if (*inchr == '"') { + *outchr++ = '\\'; + } + if (*inchr != '\0') { + *outchr++ = *inchr++; + } + } + *outchr = '\0'; + return outstring; +} diff --git a/server/util_date.c b/server/util_date.c new file mode 100644 index 0000000000..5111a600f0 --- /dev/null +++ b/server/util_date.c @@ -0,0 +1,321 @@ +/* ==================================================================== + * Copyright (c) 1996-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * util_date.c: date parsing utility routines + * These routines are (hopefully) platform-independent. + * + * 27 Oct 1996 Roy Fielding + * Extracted (with many modifications) from mod_proxy.c and + * tested with over 50,000 randomly chosen valid date strings + * and several hundred variations of invalid date strings. + * + */ + +#include "ap_config.h" +#include "util_date.h" +#include +#include + +/* + * Compare a string to a mask + * Mask characters (arbitrary maximum is 256 characters, just in case): + * @ - uppercase letter + * $ - lowercase letter + * & - hex digit + * # - digit + * ~ - digit or space + * * - swallow remaining characters + * - exact match for any other character + */ +API_EXPORT(int) ap_checkmask(const char *data, const char *mask) +{ + int i; + char d; + + for (i = 0; i < 256; i++) { + d = data[i]; + switch (mask[i]) { + case '\0': + return (d == '\0'); + + case '*': + return 1; + + case '@': + if (!ap_isupper(d)) + return 0; + break; + case '$': + if (!ap_islower(d)) + return 0; + break; + case '#': + if (!ap_isdigit(d)) + return 0; + break; + case '&': + if (!ap_isxdigit(d)) + return 0; + break; + case '~': + if ((d != ' ') && !ap_isdigit(d)) + return 0; + break; + default: + if (mask[i] != d) + return 0; + break; + } + } + return 0; /* We only get here if mask is corrupted (exceeds 256) */ +} + +/* + * tm2sec converts a GMT tm structure into the number of seconds since + * 1st January 1970 UT. Note that we ignore tm_wday, tm_yday, and tm_dst. + * + * The return value is always a valid time_t value -- (time_t)0 is returned + * if the input date is outside that capable of being represented by time(), + * i.e., before Thu, 01 Jan 1970 00:00:00 for all systems and + * beyond 2038 for 32bit systems. + * + * This routine is intended to be very fast, much faster than mktime(). + */ +API_EXPORT(time_t) ap_tm2sec(const struct tm * t) +{ + int year; + time_t days; + static const int dayoffset[12] = + {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275}; + + year = t->tm_year; + + if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138))) + return BAD_DATE; + + /* shift new year to 1st March in order to make leap year calc easy */ + + if (t->tm_mon < 2) + year--; + + /* Find number of days since 1st March 1900 (in the Gregorian calendar). */ + + days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4; + days += dayoffset[t->tm_mon] + t->tm_mday - 1; + days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */ + + days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec; + + if (days < 0) + return BAD_DATE; /* must have overflowed */ + else + return days; /* must be a valid time */ +} + +/* + * Parses an HTTP date in one of three standard forms: + * + * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + * + * and returns the time_t number of seconds since 1 Jan 1970 GMT, or + * 0 if this would be out of range or if the date is invalid. + * + * The restricted HTTP syntax is + * + * HTTP-date = rfc1123-date | rfc850-date | asctime-date + * + * rfc1123-date = wkday "," SP date1 SP time SP "GMT" + * rfc850-date = weekday "," SP date2 SP time SP "GMT" + * asctime-date = wkday SP date3 SP time SP 4DIGIT + * + * date1 = 2DIGIT SP month SP 4DIGIT + * ; day month year (e.g., 02 Jun 1982) + * date2 = 2DIGIT "-" month "-" 2DIGIT + * ; day-month-year (e.g., 02-Jun-82) + * date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) + * ; month day (e.g., Jun 2) + * + * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT + * ; 00:00:00 - 23:59:59 + * + * wkday = "Mon" | "Tue" | "Wed" + * | "Thu" | "Fri" | "Sat" | "Sun" + * + * weekday = "Monday" | "Tuesday" | "Wednesday" + * | "Thursday" | "Friday" | "Saturday" | "Sunday" + * + * month = "Jan" | "Feb" | "Mar" | "Apr" + * | "May" | "Jun" | "Jul" | "Aug" + * | "Sep" | "Oct" | "Nov" | "Dec" + * + * However, for the sake of robustness (and Netscapeness), we ignore the + * weekday and anything after the time field (including the timezone). + * + * This routine is intended to be very fast; 10x faster than using sscanf. + * + * Originally from Andrew Daviel , 29 Jul 96 + * but many changes since then. + * + */ +API_EXPORT(time_t) ap_parseHTTPdate(const char *date) +{ + struct tm ds; + int mint, mon; + const char *monstr, *timstr; + static const int months[12] = + { + ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b', + ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r', + ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n', + ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g', + ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't', + ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c'}; + + if (!date) + return BAD_DATE; + + while (*date && ap_isspace(*date)) /* Find first non-whitespace char */ + ++date; + + if (*date == '\0') + return BAD_DATE; + + if ((date = strchr(date, ' ')) == NULL) /* Find space after weekday */ + return BAD_DATE; + + ++date; /* Now pointing to first char after space, which should be */ + /* start of the actual date information for all 3 formats. */ + + if (ap_checkmask(date, "## @$$ #### ##:##:## *")) { /* RFC 1123 format */ + ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100; + if (ds.tm_year < 0) + return BAD_DATE; + + ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0'); + + ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); + + monstr = date + 3; + timstr = date + 12; + } + else if (ap_checkmask(date, "##-@$$-## ##:##:## *")) { /* RFC 850 format */ + ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0'); + if (ds.tm_year < 70) + ds.tm_year += 100; + + ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); + + monstr = date + 3; + timstr = date + 10; + } + else if (ap_checkmask(date, "@$$ ~# ##:##:## ####*")) { /* asctime format */ + ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100; + if (ds.tm_year < 0) + return BAD_DATE; + + ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0'); + + if (date[4] == ' ') + ds.tm_mday = 0; + else + ds.tm_mday = (date[4] - '0') * 10; + + ds.tm_mday += (date[5] - '0'); + + monstr = date; + timstr = date + 7; + } + else + return BAD_DATE; + + if (ds.tm_mday <= 0 || ds.tm_mday > 31) + return BAD_DATE; + + ds.tm_hour = ((timstr[0] - '0') * 10) + (timstr[1] - '0'); + ds.tm_min = ((timstr[3] - '0') * 10) + (timstr[4] - '0'); + ds.tm_sec = ((timstr[6] - '0') * 10) + (timstr[7] - '0'); + + if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61)) + return BAD_DATE; + + mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2]; + for (mon = 0; mon < 12; mon++) + if (mint == months[mon]) + break; + if (mon == 12) + return BAD_DATE; + + if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10)) + return BAD_DATE; + + /* February gets special check for leapyear */ + + if ((mon == 1) && + ((ds.tm_mday > 29) + || ((ds.tm_mday == 29) + && ((ds.tm_year & 3) + || (((ds.tm_year % 100) == 0) + && (((ds.tm_year % 400) != 100))))))) + return BAD_DATE; + + ds.tm_mon = mon; + + return ap_tm2sec(&ds); +} diff --git a/server/util_md5.c b/server/util_md5.c new file mode 100644 index 0000000000..368ddf63b2 --- /dev/null +++ b/server/util_md5.c @@ -0,0 +1,229 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/************************************************************************ + * NCSA HTTPd Server + * Software Development Group + * National Center for Supercomputing Applications + * University of Illinois at Urbana-Champaign + * 605 E. Springfield, Champaign, IL 61820 + * httpd@ncsa.uiuc.edu + * + * Copyright (C) 1995, Board of Trustees of the University of Illinois + * + ************************************************************************ + * + * md5.c: NCSA HTTPd code which uses the md5c.c RSA Code + * + * Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc. + * Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon + * University (see Copyright below). + * Portions of Content-MD5 code Copyright (C) 1991 Bell Communications + * Research, Inc. (Bellcore) (see Copyright below). + * Portions extracted from mpack, John G. Myers - jgm+@cmu.edu + * Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk) + * + */ + + + +/* md5.c --Module Interface to MD5. */ +/* Jeff Hostetler, Spyglass, Inc., 1994. */ + +#include "httpd.h" +#include "util_md5.h" + +API_EXPORT(char *) ap_md5_binary(pool *p, const unsigned char *buf, int length) +{ + const char *hex = "0123456789abcdef"; + AP_MD5_CTX my_md5; + unsigned char hash[16]; + char *r, result[33]; + int i; + + /* + * Take the MD5 hash of the string argument. + */ + + ap_MD5Init(&my_md5); + ap_MD5Update(&my_md5, buf, (unsigned int)length); + ap_MD5Final(hash, &my_md5); + + for (i = 0, r = result; i < 16; i++) { + *r++ = hex[hash[i] >> 4]; + *r++ = hex[hash[i] & 0xF]; + } + *r = '\0'; + + return ap_pstrdup(p, result); +} + +API_EXPORT(char *) ap_md5(pool *p, const unsigned char *string) +{ + return ap_md5_binary(p, string, (int) strlen((char *)string)); +} + +/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */ + +/* (C) Copyright 1993,1994 by Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Carnegie + * Mellon University not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. Carnegie Mellon University makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore) + * + * Permission to use, copy, modify, and distribute this material + * for any purpose and without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies, and that the name of Bellcore not be + * used in advertising or publicity pertaining to this + * material without the specific, prior written permission + * of an authorized representative of Bellcore. BELLCORE + * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY + * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS", + * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. + */ + +static char basis_64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +API_EXPORT(char *) ap_md5contextTo64(pool *a, AP_MD5_CTX * context) +{ + unsigned char digest[18]; + char *encodedDigest; + int i; + char *p; + + encodedDigest = (char *) ap_pcalloc(a, 25 * sizeof(char)); + + ap_MD5Final(digest, context); + digest[sizeof(digest) - 1] = digest[sizeof(digest) - 2] = 0; + + p = encodedDigest; + for (i = 0; i < sizeof(digest); i += 3) { + *p++ = basis_64[digest[i] >> 2]; + *p++ = basis_64[((digest[i] & 0x3) << 4) | ((int) (digest[i + 1] & 0xF0) >> 4)]; + *p++ = basis_64[((digest[i + 1] & 0xF) << 2) | ((int) (digest[i + 2] & 0xC0) >> 6)]; + *p++ = basis_64[digest[i + 2] & 0x3F]; + } + *p-- = '\0'; + *p-- = '='; + *p-- = '='; + return encodedDigest; +} + +#ifdef CHARSET_EBCDIC + +API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile, int convert) +{ + AP_MD5_CTX context; + unsigned char buf[1000]; + long length = 0; + int nbytes; + + ap_MD5Init(&context); + while ((nbytes = fread(buf, 1, sizeof(buf), infile))) { + length += nbytes; + if (!convert) { + ascii2ebcdic(buf, buf, nbytes); + } + ap_MD5Update(&context, buf, nbytes); + } + rewind(infile); + return ap_md5contextTo64(p, &context); +} + +#else + +API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile) +{ + AP_MD5_CTX context; + unsigned char buf[1000]; + long length = 0; + unsigned int nbytes; + + ap_MD5Init(&context); + while ((nbytes = fread(buf, 1, sizeof(buf), infile))) { + length += nbytes; + ap_MD5Update(&context, buf, nbytes); + } + rewind(infile); + return ap_md5contextTo64(p, &context); +} + +#endif /* CHARSET_EBCDIC */ diff --git a/server/util_script.c b/server/util_script.c new file mode 100644 index 0000000000..467dd2964c --- /dev/null +++ b/server/util_script.c @@ -0,0 +1,1138 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_main.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_core.h" /* For document_root. Sigh... */ +#include "http_request.h" /* for sub_req_lookup_uri() */ +#include "util_script.h" +#include "util_date.h" /* For parseHTTPdate() */ + +#ifdef OS2 +#define INCL_DOS +#include +#endif + +/* + * Various utility functions which are common to a whole lot of + * script-type extensions mechanisms, and might as well be gathered + * in one place (if only to avoid creating inter-module dependancies + * where there don't have to be). + */ + +#define MALFORMED_MESSAGE "malformed header from script. Bad header=" +#define MALFORMED_HEADER_LENGTH_TO_SHOW 30 + +/* If a request includes query info in the URL (stuff after "?"), and + * the query info does not contain "=" (indicative of a FORM submission), + * then this routine is called to create the argument list to be passed + * to the CGI script. When suexec is enabled, the suexec path, user, and + * group are the first three arguments to be passed; if not, all three + * must be NULL. The query info is split into separate arguments, where + * "+" is the separator between keyword arguments. + * + * XXXX: note that the WIN32 code uses one of the suexec strings + * to pass an interpreter name. Remember this if changing the way they + * are handled in create_argv. + * + */ +static char **create_argv(pool *p, char *path, char *user, char *group, + char *av0, const char *args) +{ + int x, numwords; + char **av; + char *w; + int idx = 0; + + /* count the number of keywords */ + + for (x = 0, numwords = 1; args[x]; x++) { + if (args[x] == '+') { + ++numwords; + } + } + + if (numwords > APACHE_ARG_MAX - 5) { + numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */ + } + av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *)); + + if (path) { + av[idx++] = path; + } + if (user) { + av[idx++] = user; + } + if (group) { + av[idx++] = group; + } + + av[idx++] = av0; + + for (x = 1; x <= numwords; x++) { + w = ap_getword_nulls(p, &args, '+'); + ap_unescape_url(w); + av[idx++] = ap_escape_shell_cmd(p, w); + } + av[idx] = NULL; + return av; +} + + +static char *http2env(pool *a, char *w) +{ + char *res = ap_pstrcat(a, "HTTP_", w, NULL); + char *cp = res; + + while (*++cp) { + if (!ap_isalnum(*cp) && *cp != '_') { + *cp = '_'; + } + else { + *cp = ap_toupper(*cp); + } + } + + return res; +} + +API_EXPORT(char **) ap_create_environment(pool *p, table *t) +{ + array_header *env_arr = ap_table_elts(t); + table_entry *elts = (table_entry *) env_arr->elts; + char **env = (char **) ap_palloc(p, (env_arr->nelts + 2) * sizeof(char *)); + int i, j; + char *tz; + char *whack; + + j = 0; + if (!ap_table_get(t, "TZ")) { + tz = getenv("TZ"); + if (tz != NULL) { + env[j++] = ap_pstrcat(p, "TZ=", tz, NULL); + } + } + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) { + continue; + } + env[j] = ap_pstrcat(p, elts[i].key, "=", elts[i].val, NULL); + whack = env[j]; + if (ap_isdigit(*whack)) { + *whack++ = '_'; + } + while (*whack != '=') { + if (!ap_isalnum(*whack) && *whack != '_') { + *whack = '_'; + } + ++whack; + } + ++j; + } + + env[j] = NULL; + return env; +} + +API_EXPORT(void) ap_add_common_vars(request_rec *r) +{ + table *e; + server_rec *s = r->server; + conn_rec *c = r->connection; + const char *rem_logname; + char *env_path; +#ifdef WIN32 + char *env_temp; +#endif + const char *host; + array_header *hdrs_arr = ap_table_elts(r->headers_in); + table_entry *hdrs = (table_entry *) hdrs_arr->elts; + int i; + + /* use a temporary table which we'll overlap onto + * r->subprocess_env later + */ + e = ap_make_table(r->pool, 25 + hdrs_arr->nelts); + + /* First, add environment vars from headers... this is as per + * CGI specs, though other sorts of scripting interfaces see + * the same vars... + */ + + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) { + continue; + } + + /* A few headers are special cased --- Authorization to prevent + * rogue scripts from capturing passwords; content-type and -length + * for no particular reason. + */ + + if (!strcasecmp(hdrs[i].key, "Content-type")) { + ap_table_addn(e, "CONTENT_TYPE", hdrs[i].val); + } + else if (!strcasecmp(hdrs[i].key, "Content-length")) { + ap_table_addn(e, "CONTENT_LENGTH", hdrs[i].val); + } + /* + * You really don't want to disable this check, since it leaves you + * wide open to CGIs stealing passwords and people viewing them + * in the environment with "ps -e". But, if you must... + */ +#ifndef SECURITY_HOLE_PASS_AUTHORIZATION + else if (!strcasecmp(hdrs[i].key, "Authorization") + || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) { + continue; + } +#endif + else { + ap_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val); + } + } + + if (!(env_path = getenv("PATH"))) { + env_path = DEFAULT_PATH; + } + +#ifdef WIN32 + if (env_temp = getenv("SystemRoot")) { + ap_table_addn(e, "SystemRoot", env_temp); + } + if (env_temp = getenv("COMSPEC")) { + ap_table_addn(e, "COMSPEC", env_temp); + } + if (env_temp = getenv("WINDIR")) { + ap_table_addn(e, "WINDIR", env_temp); + } +#endif + + ap_table_addn(e, "PATH", env_path); + ap_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r)); + ap_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version()); + ap_table_addn(e, "SERVER_NAME", ap_get_server_name(r)); + ap_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */ + ap_table_addn(e, "SERVER_PORT", + ap_psprintf(r->pool, "%u", ap_get_server_port(r))); + host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST); + if (host) { + ap_table_addn(e, "REMOTE_HOST", host); + } + ap_table_addn(e, "REMOTE_ADDR", c->remote_ip); + ap_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */ + ap_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */ + ap_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ + + ap_table_addn(e, "REMOTE_PORT", + ap_psprintf(r->pool, "%d", ntohs(c->remote_addr.sin_port))); + + if (c->user) { + ap_table_addn(e, "REMOTE_USER", c->user); + } + if (c->ap_auth_type) { + ap_table_addn(e, "AUTH_TYPE", c->ap_auth_type); + } + rem_logname = ap_get_remote_logname(r); + if (rem_logname) { + ap_table_addn(e, "REMOTE_IDENT", ap_pstrdup(r->pool, rem_logname)); + } + + /* Apache custom error responses. If we have redirected set two new vars */ + + if (r->prev) { + if (r->prev->args) { + ap_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args); + } + if (r->prev->uri) { + ap_table_addn(e, "REDIRECT_URL", r->prev->uri); + } + } + + ap_overlap_tables(r->subprocess_env, e, AP_OVERLAP_TABLES_SET); +} + +/* This "cute" little function comes about because the path info on + * filenames and URLs aren't always the same. So we take the two, + * and find as much of the two that match as possible. + */ + +API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info) +{ + int lu = strlen(uri); + int lp = strlen(path_info); + + while (lu-- && lp-- && uri[lu] == path_info[lp]); + + if (lu == -1) { + lu = 0; + } + + while (uri[lu] != '\0' && uri[lu] != '/') { + lu++; + } + return lu; +} + +/* Obtain the Request-URI from the original request-line, returning + * a new string from the request pool containing the URI or "". + */ +static char *original_uri(request_rec *r) +{ + char *first, *last; + + if (r->the_request == NULL) { + return (char *) ap_pcalloc(r->pool, 1); + } + + first = r->the_request; /* use the request-line */ + + while (*first && !ap_isspace(*first)) { + ++first; /* skip over the method */ + } + while (ap_isspace(*first)) { + ++first; /* and the space(s) */ + } + + last = first; + while (*last && !ap_isspace(*last)) { + ++last; /* end at next whitespace */ + } + + return ap_pstrndup(r->pool, first, last - first); +} + +API_EXPORT(void) ap_add_cgi_vars(request_rec *r) +{ + table *e = r->subprocess_env; + + ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1"); + ap_table_setn(e, "SERVER_PROTOCOL", r->protocol); + ap_table_setn(e, "REQUEST_METHOD", r->method); + ap_table_setn(e, "QUERY_STRING", r->args ? r->args : ""); + ap_table_setn(e, "REQUEST_URI", original_uri(r)); + + /* Note that the code below special-cases scripts run from includes, + * because it "knows" that the sub_request has been hacked to have the + * args and path_info of the original request, and not any that may have + * come with the script URI in the include command. Ugh. + */ + + if (!strcmp(r->protocol, "INCLUDED")) { + ap_table_setn(e, "SCRIPT_NAME", r->uri); + if (r->path_info && *r->path_info) { + ap_table_setn(e, "PATH_INFO", r->path_info); + } + } + else if (!r->path_info || !*r->path_info) { + ap_table_setn(e, "SCRIPT_NAME", r->uri); + } + else { + int path_info_start = ap_find_path_info(r->uri, r->path_info); + + ap_table_setn(e, "SCRIPT_NAME", + ap_pstrndup(r->pool, r->uri, path_info_start)); + + ap_table_setn(e, "PATH_INFO", r->path_info); + } + + if (r->path_info && r->path_info[0]) { + /* + * To get PATH_TRANSLATED, treat PATH_INFO as a URI path. + * Need to re-escape it for this, since the entire URI was + * un-escaped before we determined where the PATH_INFO began. + */ + request_rec *pa_req; + + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r); + + if (pa_req->filename) { +#ifdef WIN32 + char buffer[HUGE_STRING_LEN]; +#endif + char *pt = ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info, + NULL); +#ifdef WIN32 + /* We need to make this a real Windows path name */ + GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL); + ap_table_setn(e, "PATH_TRANSLATED", ap_pstrdup(r->pool, buffer)); +#else + ap_table_setn(e, "PATH_TRANSLATED", pt); +#endif + } + ap_destroy_sub_req(pa_req); + } +} + + +static int set_cookie_doo_doo(void *v, const char *key, const char *val) +{ + ap_table_addn(v, key, val); + return 1; +} + +API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, + int (*getsfunc) (char *, int, void *), + void *getsfunc_data) +{ + char x[MAX_STRING_LEN]; + char *w, *l; + int p; + int cgi_status = HTTP_OK; + table *merge; + table *cookie_table; + + if (buffer) { + *buffer = '\0'; + } + w = buffer ? buffer : x; + + ap_hard_timeout("read script header", r); + + /* temporary place to hold headers to merge in later */ + merge = ap_make_table(r->pool, 10); + + /* The HTTP specification says that it is legal to merge duplicate + * headers into one. Some browsers that support Cookies don't like + * merged headers and prefer that each Set-Cookie header is sent + * separately. Lets humour those browsers by not merging. + * Oh what a pain it is. + */ + cookie_table = ap_make_table(r->pool, 2); + ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL); + + while (1) { + + if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) { + ap_kill_timeout(r); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "Premature end of script headers: %s", r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Delete terminal (CR?)LF */ + + p = strlen(w); + if (p > 0 && w[p - 1] == '\n') { + if (p > 1 && w[p - 2] == '\015') { + w[p - 2] = '\0'; + } + else { + w[p - 1] = '\0'; + } + } + + /* + * If we've finished reading the headers, check to make sure any + * HTTP/1.1 conditions are met. If so, we're done; normal processing + * will handle the script's output. If not, just return the error. + * The appropriate thing to do would be to send the script process a + * SIGPIPE to let it know we're ignoring it, close the channel to the + * script process, and *then* return the failed-to-meet-condition + * error. Otherwise we'd be waiting for the script to finish + * blithering before telling the client the output was no good. + * However, we don't have the information to do that, so we have to + * leave it to an upper layer. + */ + if (w[0] == '\0') { + int cond_status = OK; + + ap_kill_timeout(r); + if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) { + cond_status = ap_meets_conditions(r); + } + ap_overlap_tables(r->err_headers_out, merge, + AP_OVERLAP_TABLES_MERGE); + if (!ap_is_empty_table(cookie_table)) { + /* the cookies have already been copied to the cookie_table */ + ap_table_unset(r->err_headers_out, "Set-Cookie"); + r->err_headers_out = ap_overlay_tables(r->pool, + r->err_headers_out, cookie_table); + } + return cond_status; + } + + /* if we see a bogus header don't ignore it. Shout and scream */ + +#ifdef CHARSET_EBCDIC + /* Chances are that we received an ASCII header text instead of + * the expected EBCDIC header lines. Try to auto-detect: + */ + if (!(l = strchr(w, ':'))) { + int maybeASCII = 0, maybeEBCDIC = 0; + char *cp; + + for (cp = w; *cp != '\0'; ++cp) { + if (isprint(*cp) && !isprint(os_toebcdic[*cp])) + ++maybeEBCDIC; + if (!isprint(*cp) && isprint(os_toebcdic[*cp])) + ++maybeASCII; + } + if (maybeASCII > maybeEBCDIC) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, + "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", r->filename); + ascii2ebcdic(w, w, cp - w); + } + } +#endif + if (!(l = strchr(w, ':'))) { + char malformed[(sizeof MALFORMED_MESSAGE) + 1 + + MALFORMED_HEADER_LENGTH_TO_SHOW]; + + strcpy(malformed, MALFORMED_MESSAGE); + strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW); + + if (!buffer) { + /* Soak up all the script output - may save an outright kill */ + while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) { + continue; + } + } + + ap_kill_timeout(r); + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, + "%s: %s", malformed, r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + *l++ = '\0'; + while (*l && ap_isspace(*l)) { + ++l; + } + + if (!strcasecmp(w, "Content-type")) { + char *tmp; + + /* Nuke trailing whitespace */ + + char *endp = l + strlen(l) - 1; + while (endp > l && ap_isspace(*endp)) { + *endp-- = '\0'; + } + + tmp = ap_pstrdup(r->pool, l); + ap_content_type_tolower(tmp); + r->content_type = tmp; + } + /* + * If the script returned a specific status, that's what + * we'll use - otherwise we assume 200 OK. + */ + else if (!strcasecmp(w, "Status")) { + r->status = cgi_status = atoi(l); + r->status_line = ap_pstrdup(r->pool, l); + } + else if (!strcasecmp(w, "Location")) { + ap_table_set(r->headers_out, w, l); + } + else if (!strcasecmp(w, "Content-Length")) { + ap_table_set(r->headers_out, w, l); + } + else if (!strcasecmp(w, "Transfer-Encoding")) { + ap_table_set(r->headers_out, w, l); + } + /* + * If the script gave us a Last-Modified header, we can't just + * pass it on blindly because of restrictions on future values. + */ + else if (!strcasecmp(w, "Last-Modified")) { + time_t mtime = ap_parseHTTPdate(l); + + ap_update_mtime(r, mtime); + ap_set_last_modified(r); + } + else if (!strcasecmp(w, "Set-Cookie")) { + ap_table_add(cookie_table, w, l); + } + else { + ap_table_add(merge, w, l); + } + } +} + +static int getsfunc_FILE(char *buf, int len, void *f) +{ + return fgets(buf, len, (FILE *) f) != NULL; +} + +API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f, + char *buffer) +{ + return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f); +} + +static int getsfunc_BUFF(char *w, int len, void *fb) +{ + return ap_bgets(w, len, (BUFF *) fb) > 0; +} + +API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb, + char *buffer) +{ + return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb); +} + + +API_EXPORT(void) ap_send_size(size_t size, request_rec *r) +{ + /* XXX: this -1 thing is a gross hack */ + if (size == (size_t)-1) { + ap_rputs(" -", r); + } + else if (!size) { + ap_rputs(" 0k", r); + } + else if (size < 1024) { + ap_rputs(" 1k", r); + } + else if (size < 1048576) { + ap_rprintf(r, "%4dk", (size + 512) / 1024); + } + else if (size < 103809024) { + ap_rprintf(r, "%4.1fM", size / 1048576.0); + } + else { + ap_rprintf(r, "%4dM", (size + 524288) / 1048576); + } +} + +#if defined(OS2) || defined(WIN32) +static char **create_argv_cmd(pool *p, char *av0, const char *args, char *path) +{ + register int x, n; + char **av; + char *w; + + for (x = 0, n = 2; args[x]; x++) { + if (args[x] == '+') { + ++n; + } + } + + /* Add extra strings to array. */ + n = n + 2; + + av = (char **) ap_palloc(p, (n + 1) * sizeof(char *)); + av[0] = av0; + + /* Now insert the extra strings we made room for above. */ + av[1] = strdup("/C"); + av[2] = strdup(path); + + for (x = (1 + 2); x < n; x++) { + w = ap_getword(p, &args, '+'); + ap_unescape_url(w); + av[x] = ap_escape_shell_cmd(p, w); + } + av[n] = NULL; + return av; +} +#endif + + +API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0, + char **env, int shellcmd) +{ + int pid = 0; +#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ + defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) + + core_dir_config *conf; + conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, + &core_module); + +#endif + +#if !defined(WIN32) && !defined(OS2) + /* the fd on r->server->error_log is closed, but we need somewhere to + * put the error messages from the log_* functions. So, we use stderr, + * since that is better than allowing errors to go unnoticed. Don't do + * this on Win32, though, since we haven't fork()'d. + */ + r->server->error_log = stderr; +#endif + +#ifdef RLIMIT_CPU + if (conf->limit_cpu != NULL) { + if ((setrlimit(RLIMIT_CPU, conf->limit_cpu)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit: failed to set CPU usage limit"); + } + } +#endif +#ifdef RLIMIT_NPROC + if (conf->limit_nproc != NULL) { + if ((setrlimit(RLIMIT_NPROC, conf->limit_nproc)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit: failed to set process limit"); + } + } +#endif +#if defined(RLIMIT_AS) + if (conf->limit_mem != NULL) { + if ((setrlimit(RLIMIT_AS, conf->limit_mem)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit(RLIMIT_AS): failed to set memory " + "usage limit"); + } + } +#elif defined(RLIMIT_DATA) + if (conf->limit_mem != NULL) { + if ((setrlimit(RLIMIT_DATA, conf->limit_mem)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit(RLIMIT_DATA): failed to set memory " + "usage limit"); + } + } +#elif defined(RLIMIT_VMEM) + if (conf->limit_mem != NULL) { + if ((setrlimit(RLIMIT_VMEM, conf->limit_mem)) != 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, r->server, + "setrlimit(RLIMIT_VMEM): failed to set memory " + "usage limit"); + } + } +#endif + +#ifdef OS2 + { + /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */ + int is_script = 0; + char interpreter[2048]; /* hope it's enough for the interpreter path */ + char error_object[260]; + FILE *program; + char *cmdline = r->filename, *cmdline_pos; + int cmdlen; + char *args = "", *args_end; + ULONG rc; + RESULTCODES rescodes; + int env_len, e; + char *env_block, *env_block_pos; + + if (r->args && r->args[0] && !strchr(r->args, '=')) + args = r->args; + + program = fopen(r->filename, "rt"); + + if (!program) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "fopen(%s) failed", + r->filename); + return (pid); + } + + fgets(interpreter, sizeof(interpreter), program); + fclose(program); + + if (!strncmp(interpreter, "#!", 2)) { + is_script = 1; + interpreter[strlen(interpreter) - 1] = '\0'; + if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') { + char buffer[300]; + if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { + strcpy(interpreter+2, buffer); + } else { + strcat(interpreter, ".exe"); + if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { + strcpy(interpreter+2, buffer); + } + } + } + } + + if (is_script) { + cmdline = ap_pstrcat(r->pool, interpreter+2, " ", r->filename, NULL); + } + else if (strstr(strupr(r->filename), ".CMD") > 0) { + /* Special case to allow use of REXX commands as scripts. */ + os2pathname(r->filename); + cmdline = ap_pstrcat(r->pool, SHELL_PATH, " /C ", r->filename, NULL); + } + else { + cmdline = r->filename; + } + + args = ap_pstrdup(r->pool, args); + ap_unescape_url(args); + args = ap_double_quotes(r->pool, args); + args_end = args + strlen(args); + + if (args_end - args > 4000) { /* cmd.exe won't handle lines longer than 4k */ + args_end = args + 4000; + *args_end = 0; + } + + /* +4 = 1 space between progname and args, 2 for double null at end, 2 for possible quote on first arg */ + cmdlen = strlen(cmdline) + strlen(args) + 4; + cmdline_pos = cmdline; + + while (*cmdline_pos) { + cmdlen += 2 * (*cmdline_pos == '+'); /* Allow space for each arg to be quoted */ + cmdline_pos++; + } + + cmdline = ap_pstrndup(r->pool, cmdline, cmdlen); + cmdline_pos = cmdline + strlen(cmdline); + + while (args < args_end) { + char *arg; + + arg = ap_getword_nc(r->pool, &args, '+'); + + if (strpbrk(arg, "&|<> ")) + arg = ap_pstrcat(r->pool, "\"", arg, "\"", NULL); + + *(cmdline_pos++) = ' '; + strcpy(cmdline_pos, arg); + cmdline_pos += strlen(cmdline_pos); + } + + *(++cmdline_pos) = 0; /* Add required second terminator */ + args = strchr(cmdline, ' '); + + if (args) { + *args = 0; + args++; + } + + /* Create environment block from list of envariables */ + for (env_len=1, e=0; env[e]; e++) + env_len += strlen(env[e]) + 1; + + env_block = ap_palloc(r->pool, env_len); + env_block_pos = env_block; + + for (e=0; env[e]; e++) { + strcpy(env_block_pos, env[e]); + env_block_pos += strlen(env_block_pos) + 1; + } + + *env_block_pos = 0; /* environment block is terminated by a double null */ + + rc = DosExecPgm(error_object, sizeof(error_object), EXEC_ASYNC, cmdline, env_block, &rescodes, cmdline); + + if (rc) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "DosExecPgm(%s %s) failed, %s - %s", + cmdline, args ? args : "", ap_os_error_message(rc), error_object ); + return -1; + } + + return rescodes.codeTerminate; + } +#elif defined(WIN32) + { + /* Adapted from Alec Kloss' work for OS/2 */ + char *interpreter = NULL; + char *arguments = NULL; + char *ext = NULL; + char *exename = NULL; + char *s = NULL; + char *quoted_filename; + char *pCommand; + char *pEnvBlock, *pNext; + + int i; + int iEnvBlockLen; + + file_type_e fileType; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + pid = -1; + + if (!shellcmd) { + + fileType = ap_get_win32_interpreter(r, &interpreter); + + if (fileType == eFileTypeUNKNOWN) { + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, + "%s is not executable; ensure interpreted scripts have " + "\"#!\" first line", + r->filename); + return (pid); + } + + /* + * Look at the arguments... + */ + arguments = ""; + if ((r->args) && (r->args[0]) && !strchr(r->args, '=')) { + /* If we are in this leg, there are some other arguments + * that we must include in the execution of the CGI. + * Because CreateProcess is the way it is, we have to + * create a command line like format for the execution + * of the CGI. This means we need to create on long + * string with the executable and arguments. + * + * The arguments string comes in the request structure, + * and each argument is separated by a '+'. We'll replace + * these pluses with spaces. + */ + + int iStringSize = 0; + int x; + + /* + * Duplicate the request structure string so we don't change it. + */ + arguments = ap_pstrdup(r->pool, r->args); + + /* + * Change the '+' to ' ' + */ + for (x=0; arguments[x]; x++) { + if ('+' == arguments[x]) { + arguments[x] = ' '; + } + } + + /* + * We need to unescape any characters that are + * in the arguments list. + */ + ap_unescape_url(arguments); + arguments = ap_escape_shell_cmd(r->pool, arguments); + } + + /* + * We have the interpreter (if there is one) and we have + * the arguments (if there are any). + * Build the command string to pass to CreateProcess. + */ + quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL); + if (interpreter && *interpreter) { + pCommand = ap_pstrcat(r->pool, interpreter, " ", + quoted_filename, " ", arguments, NULL); + } + else { + pCommand = ap_pstrcat(r->pool, quoted_filename, " ", arguments, NULL); + } + + } else { + + char *shell_cmd = "CMD.EXE /C "; + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + /* + * Use CMD.EXE for NT, COMMAND.COM for WIN95 + */ + if (GetVersionEx(&osver)) { + if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) { + shell_cmd = "COMMAND.COM /C "; + } + } + pCommand = ap_pstrcat(r->pool, shell_cmd, argv0, NULL); + } + + /* + * Make child process use hPipeOutputWrite as standard out, + * and make sure it does not show on screen. + */ + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = pinfo->hPipeInputRead; + si.hStdOutput = pinfo->hPipeOutputWrite; + si.hStdError = pinfo->hPipeErrorWrite; + + /* + * Win32's CreateProcess call requires that the environment + * be passed in an environment block, a null terminated block of + * null terminated strings. + */ + i = 0; + iEnvBlockLen = 1; + while (env[i]) { + iEnvBlockLen += strlen(env[i]) + 1; + i++; + } + + pEnvBlock = (char *)ap_pcalloc(r->pool,iEnvBlockLen); + + i = 0; + pNext = pEnvBlock; + while (env[i]) { + strcpy(pNext, env[i]); + pNext = pNext + strlen(pNext) + 1; + i++; + } + + if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, DETACHED_PROCESS, pEnvBlock, + ap_make_dirstr_parent(r->pool, r->filename), + &si, &pi)) { + if (fileType == eFileTypeEXE16) { + /* Hack to get 16-bit CGI's working. It works for all the + * standard modules shipped with Apache. pi.dwProcessId is 0 + * for 16-bit CGIs and all the Unix specific code that calls + * ap_call_exec interprets this as a failure case. And we can't + * use -1 either because it is mapped to 0 by the caller. + */ + pid = -2; + } + else { + pid = pi.dwProcessId; + /* + * We must close the handles to the new process and its main thread + * to prevent handle and memory leaks. + */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + } + return (pid); + } + +#else + if (ap_suexec_enabled + && ((r->server->server_uid != ap_user_id) + || (r->server->server_gid != ap_group_id) + || (!strncmp("/~", r->uri, 2)))) { + + char *execuser, *grpname; + struct passwd *pw; + struct group *gr; + + if (!strncmp("/~", r->uri, 2)) { + gid_t user_gid; + char *username = ap_pstrdup(r->pool, r->uri + 2); + char *pos = strchr(username, '/'); + + if (pos) { + *pos = '\0'; + } + + if ((pw = getpwnam(username)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getpwnam: invalid username %s", username); + return (pid); + } + execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL); + user_gid = pw->pw_gid; + + if ((gr = getgrgid(user_gid)) == NULL) { + if ((grpname = ap_palloc(r->pool, 16)) == NULL) { + return (pid); + } + else { + ap_snprintf(grpname, 16, "%ld", (long) user_gid); + } + } + else { + grpname = gr->gr_name; + } + } + else { + if ((pw = getpwuid(r->server->server_uid)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getpwuid: invalid userid %ld", + (long) r->server->server_uid); + return (pid); + } + execuser = ap_pstrdup(r->pool, pw->pw_name); + + if ((gr = getgrgid(r->server->server_gid)) == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "getgrgid: invalid groupid %ld", + (long) r->server->server_gid); + return (pid); + } + grpname = gr->gr_name; + } + + if (shellcmd) { + execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, + NULL, env); + } + + else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { + execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, + NULL, env); + } + + else { + execve(SUEXEC_BIN, + create_argv(r->pool, SUEXEC_BIN, execuser, grpname, + argv0, r->args), + env); + } + } + else { + if (shellcmd) { + execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env); + } + + else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { + execle(r->filename, argv0, NULL, env); + } + + else { + execve(r->filename, + create_argv(r->pool, NULL, NULL, NULL, argv0, r->args), + env); + } + } + return (pid); +#endif +} diff --git a/server/util_uri.c b/server/util_uri.c new file mode 100644 index 0000000000..10eb5dc0d0 --- /dev/null +++ b/server/util_uri.c @@ -0,0 +1,599 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * util_uri.c: URI related utility things + * + */ + +#include "httpd.h" +#include "http_log.h" +#include "http_conf_globals.h" /* for user_id & group_id */ +#include "util_uri.h" + +/* Some WWW schemes and their default ports; this is basically /etc/services */ +/* This will become global when the protocol abstraction comes */ +/* As the schemes are searched by a linear search, */ +/* they are sorted by their expected frequency */ +static schemes_t schemes[] = +{ + {"http", DEFAULT_HTTP_PORT}, + {"ftp", DEFAULT_FTP_PORT}, + {"https", DEFAULT_HTTPS_PORT}, + {"gopher", DEFAULT_GOPHER_PORT}, + {"wais", DEFAULT_WAIS_PORT}, + {"nntp", DEFAULT_NNTP_PORT}, + {"snews", DEFAULT_SNEWS_PORT}, + {"prospero", DEFAULT_PROSPERO_PORT}, + { NULL, 0xFFFF } /* unknown port */ +}; + + +API_EXPORT(unsigned short) ap_default_port_for_scheme(const char *scheme_str) +{ + schemes_t *scheme; + + for (scheme = schemes; scheme->name != NULL; ++scheme) + if (strcasecmp(scheme_str, scheme->name) == 0) + return scheme->default_port; + + return 0; +} + +API_EXPORT(unsigned short) ap_default_port_for_request(const request_rec *r) +{ + return (r->parsed_uri.scheme) + ? ap_default_port_for_scheme(r->parsed_uri.scheme) + : 0; +} + +/* Create a copy of a "struct hostent" record; it was presumably returned + * from a call to gethostbyname() and lives in static storage. + * By creating a copy we can tuck it away for later use. + */ +API_EXPORT(struct hostent *) ap_pduphostent(pool *p, const struct hostent *hp) +{ + struct hostent *newent; + char **ptrs; + char **aliases; + struct in_addr *addrs; + int i = 0, j = 0; + + if (hp == NULL) + return NULL; + + /* Count number of alias entries */ + if (hp->h_aliases != NULL) + for (; hp->h_aliases[j] != NULL; ++j) + continue; + + /* Count number of in_addr entries */ + if (hp->h_addr_list != NULL) + for (; hp->h_addr_list[i] != NULL; ++i) + continue; + + /* Allocate hostent structure, alias ptrs, addr ptrs, addrs */ + newent = (struct hostent *) ap_palloc(p, sizeof(*hp)); + aliases = (char **) ap_palloc(p, (j+1) * sizeof(char*)); + ptrs = (char **) ap_palloc(p, (i+1) * sizeof(char*)); + addrs = (struct in_addr *) ap_palloc(p, (i+1) * sizeof(struct in_addr)); + + *newent = *hp; + newent->h_name = ap_pstrdup(p, hp->h_name); + newent->h_aliases = aliases; + newent->h_addr_list = (char**) ptrs; + + /* Copy Alias Names: */ + for (j = 0; hp->h_aliases[j] != NULL; ++j) { + aliases[j] = ap_pstrdup(p, hp->h_aliases[j]); + } + aliases[j] = NULL; + + /* Copy address entries */ + for (i = 0; hp->h_addr_list[i] != NULL; ++i) { + ptrs[i] = (char*) &addrs[i]; + addrs[i] = *(struct in_addr *) hp->h_addr_list[i]; + } + ptrs[i] = NULL; + + return newent; +} + + +/* pgethostbyname(): resolve hostname, if successful return an ALLOCATED + * COPY OF the hostent structure, intended to be stored and used later. + * (gethostbyname() uses static storage that would be overwritten on each call) + */ +API_EXPORT(struct hostent *) ap_pgethostbyname(pool *p, const char *hostname) +{ + struct hostent *hp = gethostbyname(hostname); + return (hp == NULL) ? NULL : ap_pduphostent(p, hp); +} + + +/* Unparse a uri_components structure to an URI string. + * Optionally suppress the password for security reasons. + */ +API_EXPORT(char *) ap_unparse_uri_components(pool *p, const uri_components *uptr, unsigned flags) +{ + char *ret = ""; + + /* If suppressing the site part, omit both user name & scheme://hostname */ + if (!(flags & UNP_OMITSITEPART)) { + + /* Construct a "user:password@" string, honoring the passed UNP_ flags: */ + if (uptr->user||uptr->password) + ret = ap_pstrcat (p, + (uptr->user && !(flags & UNP_OMITUSER)) ? uptr->user : "", + (uptr->password && !(flags & UNP_OMITPASSWORD)) ? ":" : "", + (uptr->password && !(flags & UNP_OMITPASSWORD)) + ? ((flags & UNP_REVEALPASSWORD) ? uptr->password : "XXXXXXXX") + : "", + "@", NULL); + + /* Construct scheme://site string */ + if (uptr->hostname) { + int is_default_port; + + is_default_port = + (uptr->port_str == NULL || + uptr->port == 0 || + uptr->port == ap_default_port_for_scheme(uptr->scheme)); + + ret = ap_pstrcat (p, + uptr->scheme, "://", ret, + uptr->hostname ? uptr->hostname : "", + is_default_port ? "" : ":", + is_default_port ? "" : uptr->port_str, + NULL); + } + } + + /* Should we suppress all path info? */ + if (!(flags & UNP_OMITPATHINFO)) { + /* Append path, query and fragment strings: */ + ret = ap_pstrcat (p, + ret, + uptr->path ? uptr->path : "", + (uptr->query && !(flags & UNP_OMITQUERY)) ? "?" : "", + (uptr->query && !(flags & UNP_OMITQUERY)) ? uptr->query : "", + (uptr->fragment && !(flags & UNP_OMITQUERY)) ? "#" : NULL, + (uptr->fragment && !(flags & UNP_OMITQUERY)) ? uptr->fragment : NULL, + NULL); + } + return ret; +} + +/* The regex version of parse_uri_components has the advantage that it is + * relatively easy to understand and extend. But it has the disadvantage + * that the regexes are complex enough that regex libraries really + * don't do a great job with them performancewise. + * + * The default is a hand coded scanner that is two orders of magnitude + * faster. + */ +#ifdef UTIL_URI_REGEX + +static regex_t re_uri; +static regex_t re_hostpart; + +void ap_util_uri_init(void) +{ + int ret; + const char *re_str; + + /* This is a modified version of the regex that appeared in + * draft-fielding-uri-syntax-01. It doesnt allow the uri to contain a + * scheme but no hostinfo or vice versa. + * + * draft-fielding-uri-syntax-01.txt, section 4.4 tells us: + * + * Although the BNF defines what is allowed in each component, it is + * ambiguous in terms of differentiating between a site component and + * a path component that begins with two slash characters. + * + * RFC2068 disambiguates this for the Request-URI, which may only ever be + * the "abs_path" portion of the URI. So a request "GET //foo/bar + * HTTP/1.1" is really referring to the path //foo/bar, not the host foo, + * path /bar. Nowhere in RFC2068 is it possible to have a scheme but no + * hostinfo or a hostinfo but no scheme. (Unless you're proxying a + * protocol other than HTTP, but this parsing engine probably won't work + * for other protocols.) + * + * 12 3 4 5 6 7 8 */ + re_str = "^(([^:/?#]+)://([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"; + /* ^scheme--^ ^site---^ ^path--^ ^query^ ^frag */ + if ((ret = regcomp(&re_uri, re_str, REG_EXTENDED)) != 0) { + char line[1024]; + + /* Make a readable error message */ + ret = regerror(ret, &re_uri, line, sizeof line); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "Internal error: regcomp(\"%s\") returned non-zero (%s) - " + "possibly due to broken regex lib! " + "Did you define WANTHSREGEX=yes?", + re_str, line); + + exit(1); + } + + /* This is a sub-RE which will break down the hostinfo part, + * i.e., user, password, hostname and port. + * $ 12 3 4 5 6 7 */ + re_str = "^(([^:]*)(:(.*))?@)?([^@:]*)(:([0-9]*))?$"; + /* ^^user^ :pw ^host^ ^:[port]^ */ + if ((ret = regcomp(&re_hostpart, re_str, REG_EXTENDED)) != 0) { + char line[1024]; + + /* Make a readable error message */ + ret = regerror(ret, &re_hostpart, line, sizeof line); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "Internal error: regcomp(\"%s\") returned non-zero (%s) - " + "possibly due to broken regex lib! " + "Did you define WANTHSREGEX=yes?", + re_str, line); + + exit(1); + } +} + + +/* parse_uri_components(): + * Parse a given URI, fill in all supplied fields of a uri_components + * structure. This eliminates the necessity of extracting host, port, + * path, query info repeatedly in the modules. + * Side effects: + * - fills in fields of uri_components *uptr + * - none on any of the r->* fields + */ +API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr) +{ + int ret; + regmatch_t match[10]; /* This must have at least as much elements + * as there are braces in the re_strings */ + + ap_assert (uptr != NULL); + + /* Initialize the structure. parse_uri() and parse_uri_components() + * can be called more than once per request. + */ + memset (uptr, '\0', sizeof(*uptr)); + uptr->is_initialized = 1; + + ret = ap_regexec(&re_uri, uri, re_uri.re_nsub + 1, match, 0); + + if (ret != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "ap_regexec() could not parse uri (\"%s\")", + uri); + + return HTTP_BAD_REQUEST; + } + + if (match[2].rm_so != match[2].rm_eo) + uptr->scheme = ap_pstrndup (p, uri+match[2].rm_so, match[2].rm_eo - match[2].rm_so); + + /* empty hostinfo is valid, that's why we test $1 but use $3 */ + if (match[1].rm_so != match[1].rm_eo) + uptr->hostinfo = ap_pstrndup (p, uri+match[3].rm_so, match[3].rm_eo - match[3].rm_so); + + if (match[4].rm_so != match[4].rm_eo) + uptr->path = ap_pstrndup (p, uri+match[4].rm_so, match[4].rm_eo - match[4].rm_so); + + /* empty query string is valid, that's why we test $5 but use $6 */ + if (match[5].rm_so != match[5].rm_eo) + uptr->query = ap_pstrndup (p, uri+match[6].rm_so, match[6].rm_eo - match[6].rm_so); + + /* empty fragment is valid, test $7 use $8 */ + if (match[7].rm_so != match[7].rm_eo) + uptr->fragment = ap_pstrndup (p, uri+match[8].rm_so, match[8].rm_eo - match[8].rm_so); + + if (uptr->hostinfo) { + /* Parse the hostinfo part to extract user, password, host, and port */ + ret = ap_regexec(&re_hostpart, uptr->hostinfo, re_hostpart.re_nsub + 1, match, 0); + if (ret != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "ap_regexec() could not parse (\"%s\") as host part", + uptr->hostinfo); + + return HTTP_BAD_REQUEST; + } + + /* $ 12 3 4 5 6 7 */ + /* "^(([^:]*)(:(.*))?@)?([^@:]*)(:([0-9]*))?$" */ + /* ^^user^ :pw ^host^ ^:[port]^ */ + + /* empty user is valid, that's why we test $1 but use $2 */ + if (match[1].rm_so != match[1].rm_eo) + uptr->user = ap_pstrndup (p, uptr->hostinfo+match[2].rm_so, match[2].rm_eo - match[2].rm_so); + + /* empty password is valid, test $3 but use $4 */ + if (match[3].rm_so != match[3].rm_eo) + uptr->password = ap_pstrndup (p, uptr->hostinfo+match[4].rm_so, match[4].rm_eo - match[4].rm_so); + + /* empty hostname is valid, and implied by the existence of hostinfo */ + uptr->hostname = ap_pstrndup (p, uptr->hostinfo+match[5].rm_so, match[5].rm_eo - match[5].rm_so); + + if (match[6].rm_so != match[6].rm_eo) { + /* Note that the port string can be empty. + * If it is, we use the default port associated with the scheme + */ + uptr->port_str = ap_pstrndup (p, uptr->hostinfo+match[7].rm_so, match[7].rm_eo - match[7].rm_so); + if (uptr->port_str[0] != '\0') { + char *endstr; + int port; + + port = strtol(uptr->port_str, &endstr, 10); + uptr->port = port; + if (*endstr != '\0') { + /* Invalid characters after ':' found */ + return HTTP_BAD_REQUEST; + } + } + else { + uptr->port = uptr->scheme ? ap_default_port_for_scheme(uptr->scheme) : DEFAULT_HTTP_PORT; + } + } + } + + if (ret == 0) + ret = HTTP_OK; + return ret; +} +#else + +/* Here is the hand-optimized parse_uri_components(). There are some wild + * tricks we could pull in assembly language that we don't pull here... like we + * can do word-at-time scans for delimiter characters using the same technique + * that fast memchr()s use. But that would be way non-portable. -djg + */ + +/* We have a table that we can index by character and it tells us if the + * character is one of the interesting delimiters. Note that we even get + * compares for NUL for free -- it's just another delimiter. + */ + +#define T_COLON 0x01 /* ':' */ +#define T_SLASH 0x02 /* '/' */ +#define T_QUESTION 0x04 /* '?' */ +#define T_HASH 0x08 /* '#' */ +#define T_NUL 0x80 /* '\0' */ + +/* the uri_delims.h file is autogenerated by gen_uri_delims.c */ +#include "uri_delims.h" + +/* it works like this: + if (uri_delims[ch] & NOTEND_foobar) { + then we're not at a delimiter for foobar + } +*/ + +/* Note that we optimize the scheme scanning here, we cheat and let the + * compiler know that it doesn't have to do the & masking. + */ +#define NOTEND_SCHEME (0xff) +#define NOTEND_HOSTINFO (T_SLASH | T_QUESTION | T_HASH | T_NUL) +#define NOTEND_PATH (T_QUESTION | T_HASH | T_NUL) + +void ap_util_uri_init(void) +{ + /* nothing to do */ +} + +/* parse_uri_components(): + * Parse a given URI, fill in all supplied fields of a uri_components + * structure. This eliminates the necessity of extracting host, port, + * path, query info repeatedly in the modules. + * Side effects: + * - fills in fields of uri_components *uptr + * - none on any of the r->* fields + */ +API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr) +{ + const char *s; + const char *s1; + const char *hostinfo; + char *endstr; + int port; + + /* Initialize the structure. parse_uri() and parse_uri_components() + * can be called more than once per request. + */ + memset (uptr, '\0', sizeof(*uptr)); + uptr->is_initialized = 1; + + /* We assume the processor has a branch predictor like most -- + * it assumes forward branches are untaken and backwards are taken. That's + * the reason for the gotos. -djg + */ + if (uri[0] == '/') { +deal_with_path: + /* we expect uri to point to first character of path ... remember + * that the path could be empty -- http://foobar?query for example + */ + s = uri; + while ((uri_delims[*(unsigned char *)s] & NOTEND_PATH) == 0) { + ++s; + } + if (s != uri) { + uptr->path = ap_pstrndup(p, uri, s - uri); + } + if (*s == 0) { + return HTTP_OK; + } + if (*s == '?') { + ++s; + s1 = strchr(s, '#'); + if (s1) { + uptr->fragment = ap_pstrdup(p, s1 + 1); + uptr->query = ap_pstrndup(p, s, s1 - s); + } + else { + uptr->query = ap_pstrdup(p, s); + } + return HTTP_OK; + } + /* otherwise it's a fragment */ + uptr->fragment = ap_pstrdup(p, s + 1); + return HTTP_OK; + } + + /* find the scheme: */ + s = uri; + while ((uri_delims[*(unsigned char *)s] & NOTEND_SCHEME) == 0) { + ++s; + } + /* scheme must be non-empty and followed by :// */ + if (s == uri || s[0] != ':' || s[1] != '/' || s[2] != '/') { + goto deal_with_path; /* backwards predicted taken! */ + } + + uptr->scheme = ap_pstrndup(p, uri, s - uri); + s += 3; + hostinfo = s; + while ((uri_delims[*(unsigned char *)s] & NOTEND_HOSTINFO) == 0) { + ++s; + } + uri = s; /* whatever follows hostinfo is start of uri */ + uptr->hostinfo = ap_pstrndup(p, hostinfo, uri - hostinfo); + + /* If there's a username:password@host:port, the @ we want is the last @... + * too bad there's no memrchr()... For the C purists, note that hostinfo + * is definately not the first character of the original uri so therefore + * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C. + */ + do { + --s; + } while (s >= hostinfo && *s != '@'); + if (s < hostinfo) { + /* again we want the common case to be fall through */ +deal_with_host: + /* We expect hostinfo to point to the first character of + * the hostname. If there's a port it is the first colon. + */ + s = memchr(hostinfo, ':', uri - hostinfo); + if (s == NULL) { + /* we expect the common case to have no port */ + uptr->hostname = ap_pstrndup(p, hostinfo, uri - hostinfo); + goto deal_with_path; + } + uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo); + ++s; + uptr->port_str = ap_pstrndup(p, s, uri - s); + if (uri != s) { + port = strtol(uptr->port_str, &endstr, 10); + uptr->port = port; + if (*endstr == '\0') { + goto deal_with_path; + } + /* Invalid characters after ':' found */ + return HTTP_BAD_REQUEST; + } + uptr->port = ap_default_port_for_scheme(uptr->scheme); + goto deal_with_path; + } + + /* first colon delimits username:password */ + s1 = memchr(hostinfo, ':', s - hostinfo); + if (s1) { + uptr->user = ap_pstrndup(p, hostinfo, s1 - hostinfo); + ++s1; + uptr->password = ap_pstrndup(p, s1, s - s1); + } + else { + uptr->user = ap_pstrndup(p, hostinfo, s - hostinfo); + } + hostinfo = s + 1; + goto deal_with_host; +} + +/* Special case for CONNECT parsing: it comes with the hostinfo part only */ +/* See the INTERNET-DRAFT document "Tunneling SSL Through a WWW Proxy" + * currently at http://www.mcom.com/newsref/std/tunneling_ssl.html + * for the format of the "CONNECT host:port HTTP/1.0" request + */ +API_EXPORT(int) ap_parse_hostinfo_components(pool *p, const char *hostinfo, uri_components *uptr) +{ + const char *s; + char *endstr; + + /* Initialize the structure. parse_uri() and parse_uri_components() + * can be called more than once per request. + */ + memset (uptr, '\0', sizeof(*uptr)); + uptr->is_initialized = 1; + uptr->hostinfo = ap_pstrdup(p, hostinfo); + + /* We expect hostinfo to point to the first character of + * the hostname. There must be a port, separated by a colon + */ + s = strchr(hostinfo, ':'); + if (s == NULL) { + return HTTP_BAD_REQUEST; + } + uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo); + ++s; + uptr->port_str = ap_pstrdup(p, s); + if (*s != '\0') { + uptr->port = strtol(uptr->port_str, &endstr, 10); + if (*endstr == '\0') { + return HTTP_OK; + } + /* Invalid characters after ':' found */ + } + return HTTP_BAD_REQUEST; +} +#endif diff --git a/server/vhost.c b/server/vhost.c new file mode 100644 index 0000000000..793048f947 --- /dev/null +++ b/server/vhost.c @@ -0,0 +1,917 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * http_vhost.c: functions pertaining to virtual host addresses + * (configuration and run-time) + */ + +#define CORE_PRIVATE +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_log.h" +#include "http_vhost.h" +#include "http_protocol.h" + +/* + * After all the definitions there's an explanation of how it's all put + * together. + */ + +/* meta-list of name-vhosts. Each server_rec can be in possibly multiple + * lists of name-vhosts. + */ +typedef struct name_chain name_chain; +struct name_chain { + name_chain *next; + server_addr_rec *sar; /* the record causing it to be in + * this chain (needed for port comparisons) */ + server_rec *server; /* the server to use on a match */ +}; + +/* meta-list of ip addresses. Each server_rec can be in possibly multiple + * hash chains since it can have multiple ips. + */ +typedef struct ipaddr_chain ipaddr_chain; +struct ipaddr_chain { + ipaddr_chain *next; + server_addr_rec *sar; /* the record causing it to be in + * this chain (need for both ip addr and port + * comparisons) */ + server_rec *server; /* the server to use if this matches */ + name_chain *names; /* if non-NULL then a list of name-vhosts + * sharing this address */ +}; + +/* This defines the size of the hash table used for hashing ip addresses + * of virtual hosts. It must be a power of two. + */ +#ifndef IPHASH_TABLE_SIZE +#define IPHASH_TABLE_SIZE 256 +#endif + +/* A (n) bucket hash table, each entry has a pointer to a server rec and + * a pointer to the other entries in that bucket. Each individual address, + * even for virtualhosts with multiple addresses, has an entry in this hash + * table. There are extra buckets for _default_, and name-vhost entries. + * + * Note that after config time this is constant, so it is thread-safe. + */ +static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE]; + +/* dump out statistics about the hash function */ +/* #define IPHASH_STATISTICS */ + +/* list of the _default_ servers */ +static ipaddr_chain *default_list; + +/* list of the NameVirtualHost addresses */ +static server_addr_rec *name_vhost_list; +static server_addr_rec **name_vhost_list_tail; + +/* + * How it's used: + * + * The ip address determines which chain in iphash_table is interesting, then + * a comparison is done down that chain to find the first ipaddr_chain whose + * sar matches the address:port pair. + * + * If that ipaddr_chain has names == NULL then you're done, it's an ip-vhost. + * + * Otherwise it's a name-vhost list, and the default is the server in the + * ipaddr_chain record. We tuck away the ipaddr_chain record in the + * conn_rec field vhost_lookup_data. Later on after the headers we get a + * second chance, and we use the name_chain to figure out what name-vhost + * matches the headers. + * + * If there was no ip address match in the iphash_table then do a lookup + * in the default_list. + * + * How it's put together ... well you should be able to figure that out + * from how it's used. Or something like that. + */ + + +/* called at the beginning of the config */ +void ap_init_vhost_config(pool *p) +{ + memset(iphash_table, 0, sizeof(iphash_table)); + default_list = NULL; + name_vhost_list = NULL; + name_vhost_list_tail = &name_vhost_list; +} + + +/* + * Parses a host of the form
[:port] + * paddr is used to create a list in the order of input + * **paddr is the ->next pointer of the last entry (or s->addrs) + * *paddr is the variable used to keep track of **paddr between calls + * port is the default port to assume + */ +static const char *get_addresses(pool *p, char *w, server_addr_rec ***paddr, + unsigned port) +{ + struct hostent *hep; + unsigned long my_addr; + server_addr_rec *sar; + char *t; + int i, is_an_ip_addr; + + if (*w == 0) + return NULL; + + t = strchr(w, ':'); + if (t) { + if (strcmp(t + 1, "*") == 0) { + port = 0; + } + else if ((i = atoi(t + 1))) { + port = i; + } + else { + return ":port must be numeric"; + } + *t = 0; + } + + is_an_ip_addr = 0; + if (strcmp(w, "*") == 0) { + my_addr = htonl(INADDR_ANY); + is_an_ip_addr = 1; + } + else if (strcasecmp(w, "_default_") == 0 + || strcmp(w, "255.255.255.255") == 0) { + my_addr = DEFAULT_VHOST_ADDR; + is_an_ip_addr = 1; + } + else if ((my_addr = ap_inet_addr(w)) != INADDR_NONE) { + is_an_ip_addr = 1; + } + if (is_an_ip_addr) { + sar = ap_pcalloc(p, sizeof(server_addr_rec)); + **paddr = sar; + *paddr = &sar->next; + sar->host_addr.s_addr = my_addr; + sar->host_port = port; + sar->virthost = ap_pstrdup(p, w); + if (t != NULL) + *t = ':'; + return NULL; + } + + hep = gethostbyname(w); + + if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL, + "Cannot resolve host name %s --- ignoring!", w); + if (t != NULL) + *t = ':'; + return NULL; + } + + for (i = 0; hep->h_addr_list[i]; ++i) { + sar = ap_pcalloc(p, sizeof(server_addr_rec)); + **paddr = sar; + *paddr = &sar->next; + sar->host_addr = *(struct in_addr *) hep->h_addr_list[i]; + sar->host_port = port; + sar->virthost = ap_pstrdup(p, w); + } + + if (t != NULL) + *t = ':'; + return NULL; +} + + +/* parse the addresses */ +const char *ap_parse_vhost_addrs(pool *p, const char *hostname, server_rec *s) +{ + server_addr_rec **addrs; + const char *err; + + /* start the list of addreses */ + addrs = &s->addrs; + while (hostname[0]) { + err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port); + if (err) { + *addrs = NULL; + return err; + } + } + /* terminate the list */ + *addrs = NULL; + if (s->addrs) { + if (s->addrs->host_port) { + /* override the default port which is inherited from main_server */ + s->port = s->addrs->host_port; + } + } + return NULL; +} + + +const char *ap_set_name_virtual_host (cmd_parms *cmd, void *dummy, char *arg) +{ + /* use whatever port the main server has at this point */ + return get_addresses(cmd->pool, arg, &name_vhost_list_tail, + cmd->server->port); +} + + +/* hash table statistics, keep this in here for the beta period so + * we can find out if the hash function is ok + */ +#ifdef IPHASH_STATISTICS +static int iphash_compare(const void *a, const void *b) +{ + return (*(const int *) b - *(const int *) a); +} + + +static void dump_iphash_statistics(server_rec *main_s) +{ + unsigned count[IPHASH_TABLE_SIZE]; + int i; + ipaddr_chain *src; + unsigned total; + char buf[HUGE_STRING_LEN]; + char *p; + + total = 0; + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + count[i] = 0; + for (src = iphash_table[i]; src; src = src->next) { + ++count[i]; + if (i < IPHASH_TABLE_SIZE) { + /* don't count the slop buckets in the total */ + ++total; + } + } + } + qsort(count, IPHASH_TABLE_SIZE, sizeof(count[0]), iphash_compare); + p = buf + ap_snprintf(buf, sizeof(buf), + "iphash: total hashed = %u, avg chain = %u, " + "chain lengths (count x len):", + total, total / IPHASH_TABLE_SIZE); + total = 1; + for (i = 1; i < IPHASH_TABLE_SIZE; ++i) { + if (count[i - 1] != count[i]) { + p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u", + total, count[i - 1]); + total = 1; + } + else { + ++total; + } + } + p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u", + total, count[IPHASH_TABLE_SIZE - 1]); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, main_s, buf); +} +#endif + + +/* This hashing function is designed to get good distribution in the cases + * where the server is handling entire "networks" of servers. i.e. a + * whack of /24s. This is probably the most common configuration for + * ISPs with large virtual servers. + * + * NOTE: This function is symmetric (i.e. collapses all 4 octets + * into one), so machine byte order (big/little endianness) does not matter. + * + * Hash function provided by David Hankins. + */ +static ap_inline unsigned hash_inaddr(unsigned key) +{ + key ^= (key >> 16); + return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE; +} + + + +static ipaddr_chain *new_ipaddr_chain(pool *p, + server_rec *s, server_addr_rec *sar) +{ + ipaddr_chain *new; + + new = ap_palloc(p, sizeof(*new)); + new->names = NULL; + new->server = s; + new->sar = sar; + new->next = NULL; + return new; +} + + +static name_chain *new_name_chain(pool *p, server_rec *s, server_addr_rec *sar) +{ + name_chain *new; + + new = ap_palloc(p, sizeof(*new)); + new->server = s; + new->sar = sar; + new->next = NULL; + return new; +} + + +static ap_inline ipaddr_chain *find_ipaddr(struct in_addr *server_ip, + unsigned port) +{ + unsigned bucket; + ipaddr_chain *trav; + unsigned addr; + + /* scan the hash table for an exact match first */ + addr = server_ip->s_addr; + bucket = hash_inaddr(addr); + for (trav = iphash_table[bucket]; trav; trav = trav->next) { + server_addr_rec *sar = trav->sar; + if ((sar->host_addr.s_addr == addr) + && (sar->host_port == 0 || sar->host_port == port + || port == 0)) { + return trav; + } + } + return NULL; +} + + +static ipaddr_chain *find_default_server(unsigned port) +{ + server_addr_rec *sar; + ipaddr_chain *trav; + + for (trav = default_list; trav; trav = trav->next) { + sar = trav->sar; + if (sar->host_port == 0 || sar->host_port == port) { + /* match! */ + return trav; + } + } + return NULL; +} + + +static void dump_vhost_config(FILE *f) +{ + int i; + ipaddr_chain *ic; + name_chain *nc; + char buf[MAX_STRING_LEN]; + + fprintf(f, "VirtualHost configuration:\n"); + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + for (ic = iphash_table[i]; ic; ic = ic->next) { + if (ic->sar->host_port == 0) { + ap_snprintf(buf, sizeof(buf), "%pA:*", &ic->sar->host_addr); + } + else { + ap_snprintf(buf, sizeof(buf), "%pA:%u", &ic->sar->host_addr, + ic->sar->host_port); + } + if (ic->names == NULL) { + fprintf(f, "%-22s %s (%s:%u)\n", buf, + ic->server->server_hostname, ic->server->defn_name, + ic->server->defn_line_number); + continue; + } + fprintf(f, "%-22s is a NameVirtualHost\n" + "%22s default server %s (%s:%u)\n", + buf, "", ic->server->server_hostname, + ic->server->defn_name, ic->server->defn_line_number); + for (nc = ic->names; nc; nc = nc->next) { + if (nc->sar->host_port) { + fprintf(f, "%22s port %u ", "", nc->sar->host_port); + } + else { + fprintf(f, "%22s port * ", ""); + } + fprintf(f, "namevhost %s (%s:%u)\n", + nc->server->server_hostname, + nc->server->defn_name, + nc->server->defn_line_number); + } + } + } + if (default_list) { + fprintf(f, "_default_ servers:\n"); + for (ic = default_list; ic; ic = ic->next) { + if (ic->sar->host_port == 0) { + fprintf(f, "port * "); + } + else { + fprintf(f, "port %u ", ic->sar->host_port); + } + fprintf(f, "server %s (%s:%u)\n", + ic->server->server_hostname, ic->server->defn_name, + ic->server->defn_line_number); + } + } +} + +/* compile the tables and such we need to do the run-time vhost lookups */ +void ap_fini_vhost_config(pool *p, server_rec *main_s) +{ + server_addr_rec *sar; + int has_default_vhost_addr; + server_rec *s; + int i; + ipaddr_chain **iphash_table_tail[IPHASH_TABLE_SIZE]; + + /* terminate the name_vhost list */ + *name_vhost_list_tail = NULL; + + /* Main host first */ + s = main_s; + + if (!s->server_hostname) { + s->server_hostname = ap_get_local_host(p); + } + + /* initialize the tails */ + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + iphash_table_tail[i] = &iphash_table[i]; + } + + /* The first things to go into the hash table are the NameVirtualHosts + * Since name_vhost_list is in the same order that the directives + * occured in the config file, we'll copy it in that order. + */ + for (sar = name_vhost_list; sar; sar = sar->next) { + unsigned bucket = hash_inaddr(sar->host_addr.s_addr); + ipaddr_chain *new = new_ipaddr_chain(p, NULL, sar); + + *iphash_table_tail[bucket] = new; + iphash_table_tail[bucket] = &new->next; + + /* Notice that what we've done is insert an ipaddr_chain with + * both server and names NULL. Remember that. + */ + } + + /* The next things to go into the hash table are the virtual hosts + * themselves. They're listed off of main_s->next in the reverse + * order they occured in the config file, so we insert them at + * the iphash_table_tail but don't advance the tail. + */ + + for (s = main_s->next; s; s = s->next) { + has_default_vhost_addr = 0; + for (sar = s->addrs; sar; sar = sar->next) { + ipaddr_chain *ic; + + if (sar->host_addr.s_addr == DEFAULT_VHOST_ADDR + || sar->host_addr.s_addr == INADDR_ANY) { + /* add it to default bucket for each appropriate sar + * since we need to do a port test + */ + ipaddr_chain *other; + + other = find_default_server(sar->host_port); + if (other && other->sar->host_port != 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s, + "_default_ VirtualHost overlap on port %u," + " the first has precedence", sar->host_port); + } + has_default_vhost_addr = 1; + ic = new_ipaddr_chain(p, s, sar); + ic->next = default_list; + default_list = ic; + } + else { + /* see if it matches something we've already got */ + ic = find_ipaddr(&sar->host_addr, sar->host_port); + + /* the first time we encounter a NameVirtualHost address + * ic->server will be NULL, on subsequent encounters + * ic->names will be non-NULL. + */ + if (ic && (ic->names || ic->server == NULL)) { + name_chain *nc = new_name_chain(p, s, sar); + nc->next = ic->names; + ic->names = nc; + ic->server = s; + if (sar->host_port != ic->sar->host_port) { + /* one of the two is a * port, the other isn't */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s, + "VirtualHost %s:%u -- mixing * " + "ports and non-* ports with " + "a NameVirtualHost address is not supported," + " proceeding with undefined results", + sar->virthost, sar->host_port); + } + } + else if (ic) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s, + "VirtualHost %s:%u overlaps with " + "VirtualHost %s:%u, the first has precedence, " + "perhaps you need a NameVirtualHost directive", + sar->virthost, sar->host_port, + ic->sar->virthost, ic->sar->host_port); + ic->sar = sar; + ic->server = s; + } + else { + unsigned bucket = hash_inaddr(sar->host_addr.s_addr); + + ic = new_ipaddr_chain(p, s, sar); + ic->next = *iphash_table_tail[bucket]; + *iphash_table_tail[bucket] = ic; + } + } + } + + /* Ok now we want to set up a server_hostname if the user was + * silly enough to forget one. + * XXX: This is silly we should just crash and burn. + */ + if (!s->server_hostname) { + if (has_default_vhost_addr) { + s->server_hostname = main_s->server_hostname; + } + else if (!s->addrs) { + /* what else can we do? at this point this vhost has + no configured name, probably because they used + DNS in the VirtualHost statement. It's disabled + anyhow by the host matching code. -djg */ + s->server_hostname = + ap_pstrdup(p, "bogus_host_without_forward_dns"); + } + else { + struct hostent *h; + + if ((h = gethostbyaddr((char *) &(s->addrs->host_addr), + sizeof(struct in_addr), AF_INET))) { + s->server_hostname = ap_pstrdup(p, (char *) h->h_name); + } + else { + /* again, what can we do? They didn't specify a + ServerName, and their DNS isn't working. -djg */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s, + "Failed to resolve server name " + "for %s (check DNS) -- or specify an explicit " + "ServerName", + inet_ntoa(s->addrs->host_addr)); + s->server_hostname = + ap_pstrdup(p, "bogus_host_without_reverse_dns"); + } + } + } + } + + /* now go through and delete any NameVirtualHosts that didn't have any + * hosts associated with them. Lamers. + */ + for (i = 0; i < IPHASH_TABLE_SIZE; ++i) { + ipaddr_chain **pic = &iphash_table[i]; + + while (*pic) { + ipaddr_chain *ic = *pic; + + if (ic->server == NULL) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s, + "NameVirtualHost %s:%u has no VirtualHosts", + ic->sar->virthost, ic->sar->host_port); + *pic = ic->next; + } + else if (ic->names == NULL) { + /* if server != NULL and names == NULL then we're done + * looking at NameVirtualHosts + */ + break; + } + else { + pic = &ic->next; + } + } + } + +#ifdef IPHASH_STATISTICS + dump_iphash_statistics(main_s); +#endif + if (ap_dump_settings) { + dump_vhost_config(stderr); + } +} + + +/***************************************************************************** + * run-time vhost matching functions + */ + +/* Remove :port and optionally a single trailing . from the hostname, this + * canonicalizes it somewhat. + */ +static void fix_hostname(request_rec *r) +{ + const char *hostname = r->hostname; + char *host = ap_getword(r->pool, &hostname, ':'); /* get rid of port */ + size_t l; + + /* trim a trailing . */ + l = strlen(host); + if (l > 0 && host[l-1] == '.') { + host[l-1] = '\0'; + } + + r->hostname = host; +} + + +/* return 1 if host matches ServerName or ServerAliases */ +static int matches_aliases(server_rec *s, const char *host) +{ + int i; + array_header *names; + + /* match ServerName */ + if (!strcasecmp(host, s->server_hostname)) { + return 1; + } + + /* search all the aliases from ServerAlias directive */ + names = s->names; + if (names) { + char **name = (char **) names->elts; + for (i = 0; i < names->nelts; ++i) { + if(!name[i]) continue; + if (!strcasecmp(host, name[i])) + return 1; + } + } + names = s->wild_names; + if (names) { + char **name = (char **) names->elts; + for (i = 0; i < names->nelts; ++i) { + if(!name[i]) continue; + if (!ap_strcasecmp_match(host, name[i])) + return 1; + } + } + return 0; +} + + +/* Suppose a request came in on the same socket as this r, and included + * a header "Host: host:port", would it map to r->server? It's more + * than just that though. When we do the normal matches for each request + * we don't even bother considering Host: etc on non-namevirtualhosts, + * we just call it a match. But here we require the host:port to match + * the ServerName and/or ServerAliases. + */ +API_EXPORT(int) ap_matches_request_vhost(request_rec *r, const char *host, + unsigned port) +{ + server_rec *s; + server_addr_rec *sar; + + s = r->server; + + /* search all the values */ + /* XXX: If this is a NameVirtualHost then we may not be doing the Right Thing + * consider: + * + * NameVirtualHost 10.1.1.1 + * + * ServerName v1 + * + * + * ServerName v2 + * + * + * Suppose r->server is v2, and we're asked to match "10.1.1.1". We'll say + * "yup it's v2", when really it isn't... if a request came in for 10.1.1.1 + * it would really go to v1. + */ + for (sar = s->addrs; sar; sar = sar->next) { + if ((sar->host_port == 0 || port == sar->host_port) + && !strcasecmp(host, sar->virthost)) { + return 1; + } + } + + /* the Port has to match now, because the rest don't have ports associated + * with them. */ + if (port != s->port) { + return 0; + } + + return matches_aliases(s, host); +} + + +static void check_hostalias(request_rec *r) +{ + /* + * Even if the request has a Host: header containing a port we ignore + * that port. We always use the physical port of the socket. There + * are a few reasons for this: + * + * - the default of 80 or 443 for SSL is easier to handle this way + * - there is less of a possibility of a security problem + * - it simplifies the data structure + * - the client may have no idea that a proxy somewhere along the way + * translated the request to another ip:port + * - except for the addresses from the VirtualHost line, none of the other + * names we'll match have ports associated with them + */ + const char *host = r->hostname; + unsigned port = ntohs(r->connection->local_addr.sin_port); + server_rec *s; + server_rec *last_s; + name_chain *src; + + last_s = NULL; + + /* Recall that the name_chain is a list of server_addr_recs, some of + * whose ports may not match. Also each server may appear more than + * once in the chain -- specifically, it will appear once for each + * address from its VirtualHost line which matched. We only want to + * do the full ServerName/ServerAlias comparisons once for each + * server, fortunately we know that all the VirtualHost addresses for + * a single server are adjacent to each other. + */ + + for (src = r->connection->vhost_lookup_data; src; src = src->next) { + server_addr_rec *sar; + + /* We only consider addresses on the name_chain which have a matching + * port + */ + sar = src->sar; + if (sar->host_port != 0 && port != sar->host_port) { + continue; + } + + s = src->server; + + /* does it match the virthost from the sar? */ + if (!strcasecmp(host, sar->virthost)) { + goto found; + } + + if (s == last_s) { + /* we've already done ServerName and ServerAlias checks for this + * vhost + */ + continue; + } + last_s = s; + + if (matches_aliases(s, host)) { + goto found; + } + } + return; + +found: + /* s is the first matching server, we're done */ + r->server = r->connection->server = s; +} + + +static void check_serverpath(request_rec *r) +{ + server_rec *s; + server_rec *last_s; + name_chain *src; + unsigned port = ntohs(r->connection->local_addr.sin_port); + + /* + * This is in conjunction with the ServerPath code in http_core, so we + * get the right host attached to a non- Host-sending request. + * + * See the comment in check_hostalias about how each vhost can be + * listed multiple times. + */ + + last_s = NULL; + for (src = r->connection->vhost_lookup_data; src; src = src->next) { + /* We only consider addresses on the name_chain which have a matching + * port + */ + if (src->sar->host_port != 0 && port != src->sar->host_port) { + continue; + } + + s = src->server; + if (s == last_s) { + continue; + } + last_s = s; + + if (s->path && !strncmp(r->uri, s->path, s->pathlen) && + (s->path[s->pathlen - 1] == '/' || + r->uri[s->pathlen] == '/' || + r->uri[s->pathlen] == '\0')) { + r->server = r->connection->server = s; + return; + } + } +} + + +void ap_update_vhost_from_headers(request_rec *r) +{ + /* must set this for HTTP/1.1 support */ + if (r->hostname || (r->hostname = ap_table_get(r->headers_in, "Host"))) { + fix_hostname(r); + } + /* check if we tucked away a name_chain */ + if (r->connection->vhost_lookup_data) { + if (r->hostname) + check_hostalias(r); + else + check_serverpath(r); + } +} + + +/* Called for a new connection which has a known local_addr. Note that the + * new connection is assumed to have conn->server == main server. + */ +void ap_update_vhost_given_ip(conn_rec *conn) +{ + ipaddr_chain *trav; + unsigned port = ntohs(conn->local_addr.sin_port); + + /* scan the hash table for an exact match first */ + trav = find_ipaddr(&conn->local_addr.sin_addr, port); + if (trav) { + /* save the name_chain for later in case this is a name-vhost */ + conn->vhost_lookup_data = trav->names; + conn->server = trav->server; + return; + } + + /* There's certainly no name-vhosts with this address, they would have + * been matched above. + */ + conn->vhost_lookup_data = NULL; + + /* maybe there's a default server matching this port */ + trav = find_default_server(port); + if (trav) { + conn->server = trav->server; + } + + /* otherwise we're stuck with just the main server */ +} diff --git a/srclib/.cvsignore b/srclib/.cvsignore new file mode 100644 index 0000000000..f3c7a7c5da --- /dev/null +++ b/srclib/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/support/.cvsignore b/support/.cvsignore new file mode 100644 index 0000000000..399f114e93 --- /dev/null +++ b/support/.cvsignore @@ -0,0 +1,11 @@ +Makefile +rotatelogs +htpasswd +htdigest +unescape +inc2shtml +httpd_monitor +suexec +logresolve +ab +apxs diff --git a/support/.indent.pro b/support/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/support/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/support/README b/support/README new file mode 100644 index 0000000000..80e9cafde0 --- /dev/null +++ b/support/README @@ -0,0 +1,62 @@ +Support files: + +ab + ABuse your server with this benchmarker. Rudimentary + command line testing tool. + +apachectl + Apache run-time Control script. To facilitate the + administrator and/or your rc.d scripts to control the + functioning of the Apache httpd daemon. + +apxs + APache eXtenSion tool. Eases building and installing + DSO style modules. + +dbmmanage + Create and update user authentication files in the faster + DBM format used by mod_auth_db. + +htdigest + Create and update user authentication files used in + DIGEST authentification. See mod_auth_digest. + +htpasswd + Create and update user authentication files used in + BASIC authentification. I.e. the htpasswd files. + See mod_auth. + +httpd.8 + General apache man page. + +log_server_status + This script is designed to be run at a frequent interval by something + like cron. It connects to the server and downloads the status + information. It reformats the information to a single line and logs + it to a file. + +logresolve + resolve hostnames for IP-adresses in Apache logfiles + +phf_abuse_log.cgi + This script can be used to detect people trying to abuse an ancient + and long plugged security hole which existed in a CGI script distributed + with Apache 1.0.3 and earlier versions. + +rotatelogs + rotate Apache logs without having to kill the server. + +split-logfile + This script will take a combined virtual hosts access + log file and break its contents into separate files. + +suexec + Switch User For Exec. Used internally by apache, + see the document `Apache suEXEC Support' + under http://www.apache.org/docs/suexec.html . + +SHA1 + This directory includes some utilities to allow Apache 1.3.6 to + recognize passwords in SHA1 format, as used by Netscape web + servers. It is not installed by default. + diff --git a/support/SHA1/README.sha1 b/support/SHA1/README.sha1 new file mode 100644 index 0000000000..3998e1fdd9 --- /dev/null +++ b/support/SHA1/README.sha1 @@ -0,0 +1,34 @@ +This directory includes some utilities to allow Apache 1.3.6 to +recognize passwords in SHA1 format, as used by Netscape web servers. + +From Netscape's admin interface, export the password database to an +ldif file and then use convert.pl in this distribution to generate +apache style password files. + +Note: SHA1 support is useful for migration purposes, but is less + secure than Apache's password format, since Apache's (MD5) + password format uses a random eight character salt to generate + one of many possible hashes for the same password. Netscape + uses plain SHA1 without a salt, so the same password + will always generate the same hash, making it easier + to break since the search space is smaller. + +This code was contributed by Clinton Wong . + +README.sha1 + this file + +convert-sha1.pl + takes an ldif dump from Netscape's web server on + standard in, outputs apache htpasswd format on standard out. + + Usage: convert.pl < ldif > passwords + +htpasswd-sha1.pl + perl script to generate entries in apache htpasswd format. + + Usage: htpasswd-sha1.pl some_user some_password + +ldif-sha1.example + sample ldif dump with one sha1 password and one crypt password. + diff --git a/support/SHA1/convert-sha1.pl b/support/SHA1/convert-sha1.pl new file mode 100644 index 0000000000..35228022a0 --- /dev/null +++ b/support/SHA1/convert-sha1.pl @@ -0,0 +1,36 @@ +#!/usr/bin/perl -w +use strict; + +# This is public domain code. Do whatever you want with it. +# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif +# patch distribution as sample code for converting accounts from +# ldif format (as used by Netscape web servers) to Apache password format. + +my $uid=''; +my $passwd=''; + +while (my $line = <>) { + chomp $line; + if ( $line =~ /uid:\s*(.+)/) { $uid = $1 } + if ( $line =~ /userpassword:\s*(\{\w+\}.+)/) { + $passwd = $1; + $passwd =~ s/^\{crypt\}//i; # Apache stores crypt without a magic string + } + + if (length($line)==0) { + + if (length $uid and length $passwd) { + print $uid, ':', $passwd, "\n"; + } # output if we have something to print + + $uid = ''; + $passwd = ''; + + } # if newline +} # while something to read + +# handle last entry if there isn't a newline before EOF + if (length $uid and length $passwd) { + print $uid, ':', $passwd, "\n"; +} + diff --git a/support/SHA1/htpasswd-sha1.pl b/support/SHA1/htpasswd-sha1.pl new file mode 100644 index 0000000000..ad624d1101 --- /dev/null +++ b/support/SHA1/htpasswd-sha1.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl -w +use strict; +# +# Utility which takes a username and password +# on the command line and generates a username +# sha1-encrytped password on the stdout. +# +# Typical useage: +# ./htpasswd-sha1.pl dirkx MySecret >> sha1-passwd +# +# This is public domain code. Do whatever you want with it. +# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif +# patch distribution as sample code for generating entries for +# Apache password files using SHA1. + +use MIME::Base64; # http://www.cpan.org/modules/by-module/MIME/ +use Digest::SHA1; # http://www.cpan.org/modules/by-module/MD5/ + +if ($#ARGV!=1) { die "Usage $0: user password\n" } + +print $ARGV[0], ':{SHA}', encode_base64( Digest::SHA1::sha1($ARGV[1]) ); + diff --git a/support/SHA1/ldif-sha1.example b/support/SHA1/ldif-sha1.example new file mode 100644 index 0000000000..b8fe917eaf --- /dev/null +++ b/support/SHA1/ldif-sha1.example @@ -0,0 +1,19 @@ +dn: cn=someuser +cn: someuser +sn: someuser +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: someuser +userpassword: {SHA}GvF+c3IdvgxAARuC7Uuxp9vjzik= + +dn: cn=anotheruser +cn: anotheruser +sn: anotheruser +objectclass: top +objectclass: person +objectclass: organizationalPerson +objectclass: inetOrgPerson +uid: anotheruser +userpassword: {crypt}eFnp.4sz5XnH6 diff --git a/support/ab.c b/support/ab.c new file mode 100644 index 0000000000..0d968b247a --- /dev/null +++ b/support/ab.c @@ -0,0 +1,1100 @@ +/* ==================================================================== + * Copyright (c) 1998-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + ** This program is based on ZeusBench V1.0 written by Adam Twiss + ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/ + ** + ** This software is provided "as is" and any express or implied waranties, + ** including but not limited to, the implied warranties of merchantability and + ** fitness for a particular purpose are disclaimed. In no event shall + ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, + ** exemplary, or consequential damaged (including, but not limited to, + ** procurement of substitute good or services; loss of use, data, or profits; + ** or business interruption) however caused and on theory of liability. Whether + ** in contract, strict liability or tort (including negligence or otherwise) + ** arising in any way out of the use of this software, even if advised of the + ** possibility of such damage. + ** + */ + +/* + ** HISTORY: + ** - Originally written by Adam Twiss , March 1996 + ** with input from Mike Belshe and + ** Michael Campanella + ** - Enhanced by Dean Gaudet , November 1997 + ** - Cleaned up by Ralf S. Engelschall , March 1998 + ** - POST and verbosity by Kurt Sussman , August 1998 + ** - HTML table output added by David N. Welton , January 1999 + ** - Added Cookie, Arbitrary header and auth support. , April 199 + ** + */ + +/* + * BUGS: + * + * - uses strcpy/etc. + * - has various other poor buffer attacks related to the lazy parsing of + * response headers from the server + * - doesn't implement much of HTTP/1.x, only accepts certain forms of + * responses + * - (performance problem) heavy use of strstr shows up top in profile + * only an issue for loopback usage + */ + +#define VERSION "1.3a" + +/* -------------------------------------------------------------------- */ + +/* affects include files on Solaris */ +#define BSD_COMP + +/* allow compilation outside an Apache build tree */ +#ifdef NO_APACHE_INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ap_select select +#else /* (!)NO_APACHE_INCLUDES */ +#include "ap_config.h" +#include "ap.h" +#ifdef CHARSET_EBCDIC +#include "ebcdic.h" +#endif +#include +#include + +#ifndef NO_WRITEV +#include +#include +#endif + +#endif /* NO_APACHE_INCLUDES */ +/* ------------------- DEFINITIONS -------------------------- */ + +/* maximum number of requests on a time limited test */ +#define MAX_REQUESTS 50000 + +/* good old state hostname */ +#define STATE_UNCONNECTED 0 +#define STATE_CONNECTING 1 +#define STATE_READ 2 + +#define CBUFFSIZE 512 + +struct connection { + int fd; + int state; + int read; /* amount of bytes read */ + int bread; /* amount of body read */ + int length; /* Content-Length value used for keep-alive */ + char cbuff[CBUFFSIZE]; /* a buffer to store server response header */ + int cbx; /* offset in cbuffer */ + int keepalive; /* non-zero if a keep-alive request */ + int gotheader; /* non-zero if we have the entire header in + * cbuff */ + struct timeval start, connect, done; +}; + +struct data { + int read; /* number of bytes read */ + int ctime; /* time in ms to connect */ + int time; /* time in ms for connection */ +}; + +#define ap_min(a,b) ((a)<(b))?(a):(b) +#define ap_max(a,b) ((a)>(b))?(a):(b) + +/* --------------------- GLOBALS ---------------------------- */ + +int verbosity = 0; /* no verbosity by default */ +int posting = 0; /* GET by default */ +int requests = 1; /* Number of requests to make */ +int concurrency = 1; /* Number of multiple requests to make */ +int tlimit = 0; /* time limit in cs */ +int keepalive = 0; /* try and do keepalive connections */ +char servername[1024]; /* name that server reports */ +char hostname[1024]; /* host name */ +char path[1024]; /* path name */ +char postfile[1024]; /* name of file containing post data */ +char *postdata; /* *buffer containing data from postfile */ +int postlen = 0; /* length of data to be POSTed */ +char content_type[1024]; /* content type to put in POST header */ +char cookie[1024], /* optional cookie line */ + auth[1024], /* optional (basic/uuencoded) + * authentification */ + hdrs[4096]; /* optional arbitrary headers */ +int port = 80; /* port number */ + +int use_html = 0; /* use html in the report */ +char *tablestring; +char *trstring; +char *tdstring; + +int doclen = 0; /* the length the document should be */ +int totalread = 0; /* total number of bytes read */ +int totalbread = 0; /* totoal amount of entity body read */ +int totalposted = 0; /* total number of bytes posted, inc. headers */ +int done = 0; /* number of requests we have done */ +int doneka = 0; /* number of keep alive connections done */ +int good = 0, bad = 0; /* number of good and bad requests */ + +/* store error cases */ +int err_length = 0, err_conn = 0, err_except = 0; +int err_response = 0; + +struct timeval start, endtime; + +/* global request (and its length) */ +char request[512]; +int reqlen; + +/* one global throw-away buffer to read stuff into */ +char buffer[8192]; + +struct connection *con; /* connection array */ +struct data *stats; /* date for each request */ + +fd_set readbits, writebits; /* bits for select */ +struct sockaddr_in server; /* server addr structure */ + +/* --------------------------------------------------------- */ + +/* simple little function to perror and exit */ + +static void err(char *s) +{ + if (errno) { + perror(s); + } + else { + printf("%s", s); + } + exit(errno); +} + +/* --------------------------------------------------------- */ + +/* write out request to a connection - assumes we can write + (small) request out in one go into our new socket buffer */ + +static void write_request(struct connection * c) +{ +#ifndef NO_WRITEV + struct iovec out[2]; int outcnt = 1; +#endif + gettimeofday(&c->connect, 0); +#ifndef NO_WRITEV + out[0].iov_base = request; + out[0].iov_len = reqlen; + + if (posting) { + out[1].iov_base = postdata; + out[1].iov_len = postlen; + outcnt = 2; + totalposted += (reqlen + postlen); + } + writev(c->fd,out, outcnt); +#else + write(c->fd,request,reqlen); + if (posting) { + write(c->fd,postdata,postlen); + totalposted += (reqlen + postlen); + } +#endif + + c->state = STATE_READ; + FD_SET(c->fd, &readbits); + FD_CLR(c->fd, &writebits); +} + +/* --------------------------------------------------------- */ + +/* make an fd non blocking */ + +static void nonblock(int fd) +{ + int i = 1; + ioctl(fd, FIONBIO, &i); +} + +/* --------------------------------------------------------- */ + +/* returns the time in ms between two timevals */ + +static int timedif(struct timeval a, struct timeval b) +{ + register int us, s; + + us = a.tv_usec - b.tv_usec; + us /= 1000; + s = a.tv_sec - b.tv_sec; + s *= 1000; + return s + us; +} + +/* --------------------------------------------------------- */ + +/* calculate and output results */ + +static void output_results(void) +{ + int timetaken; + + gettimeofday(&endtime, 0); + timetaken = timedif(endtime, start); + + printf("\r \r"); + printf("Server Software: %s\n", servername); + printf("Server Hostname: %s\n", hostname); + printf("Server Port: %d\n", port); + printf("\n"); + printf("Document Path: %s\n", path); + printf("Document Length: %d bytes\n", doclen); + printf("\n"); + printf("Concurrency Level: %d\n", concurrency); + printf("Time taken for tests: %d.%03d seconds\n", + timetaken / 1000, timetaken % 1000); + printf("Complete requests: %d\n", done); + printf("Failed requests: %d\n", bad); + if (bad) + printf(" (Connect: %d, Length: %d, Exceptions: %d)\n", + err_conn, err_length, err_except); + if (err_response) + printf("Non-2xx responses: %d\n", err_response); + if (keepalive) + printf("Keep-Alive requests: %d\n", doneka); + printf("Total transferred: %d bytes\n", totalread); + if (posting) + printf("Total POSTed: %d\n", totalposted); + printf("HTML transferred: %d bytes\n", totalbread); + + /* avoid divide by zero */ + if (timetaken) { + printf("Requests per second: %.2f\n", 1000 * (float) (done) / timetaken); + printf("Transfer rate: %.2f kb/s received\n", + (float) (totalread) / timetaken); + if (posting) { + printf(" %.2f kb/s sent\n", + (float) (totalposted) / timetaken); + printf(" %.2f kb/s total\n", + (float) (totalread + totalposted) / timetaken); + } + } + + { + /* work out connection times */ + int i; + int totalcon = 0, total = 0; + int mincon = 9999999, mintot = 999999; + int maxcon = 0, maxtot = 0; + + for (i = 0; i < requests; i++) { + struct data s = stats[i]; + mincon = ap_min(mincon, s.ctime); + mintot = ap_min(mintot, s.time); + maxcon = ap_max(maxcon, s.ctime); + maxtot = ap_max(maxtot, s.time); + totalcon += s.ctime; + total += s.time; + } + if (requests > 0) { /* avoid division by zero (if 0 requests) */ + printf("\nConnnection Times (ms)\n"); + printf(" min avg max\n"); + printf("Connect: %5d %5d %5d\n", mincon, totalcon / requests, maxcon); + printf("Processing: %5d %5d %5d\n", + mintot - mincon, (total / requests) - (totalcon / requests), + maxtot - maxcon); + printf("Total: %5d %5d %5d\n", mintot, total / requests, maxtot); + } + } +} + +/* --------------------------------------------------------- */ + +/* calculate and output results in HTML */ + +static void output_html_results(void) +{ + int timetaken; + + gettimeofday(&endtime, 0); + timetaken = timedif(endtime, start); + + printf("\n\n\n", tablestring); + printf("" + "\n", + trstring, tdstring, tdstring, servername); + printf("" + "\n", + trstring, tdstring, tdstring, hostname); + printf("" + "\n", + trstring, tdstring, tdstring, port); + printf("" + "\n", + trstring, tdstring, tdstring, path); + printf("" + "\n", + trstring, tdstring, tdstring, doclen); + printf("" + "\n", + trstring, tdstring, tdstring, concurrency); + printf("" + "\n", + trstring, tdstring, tdstring, timetaken / 1000, timetaken % 1000); + printf("" + "\n", + trstring, tdstring, tdstring, done); + printf("" + "\n", + trstring, tdstring, tdstring, bad); + if (bad) + printf("\n", + trstring, tdstring, err_conn, err_length, err_except); + if (err_response) + printf("" + "\n", + trstring, tdstring, tdstring, err_response); + if (keepalive) + printf("" + "\n", + trstring, tdstring, tdstring, doneka); + printf("" + "\n", + trstring, tdstring, tdstring, totalread); + if (posting) + printf("" + "\n", + trstring, tdstring, tdstring, totalposted); + printf("" + "\n", + trstring, tdstring, tdstring, totalbread); + + /* avoid divide by zero */ + if (timetaken) { + printf("" + "\n", + trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken); + printf("" + "\n", + trstring, tdstring, tdstring, (float) (totalread) / timetaken); + if (posting) { + printf("" + "\n", + trstring, tdstring, tdstring, + (float) (totalposted) / timetaken); + printf("" + "\n", + trstring, tdstring, tdstring, + (float) (totalread + totalposted) / timetaken); + } + } + + { + /* work out connection times */ + int i; + int totalcon = 0, total = 0; + int mincon = 9999999, mintot = 999999; + int maxcon = 0, maxtot = 0; + + for (i = 0; i < requests; i++) { + struct data s = stats[i]; + mincon = ap_min(mincon, s.ctime); + mintot = ap_min(mintot, s.time); + maxcon = ap_max(maxcon, s.ctime); + maxtot = ap_max(maxtot, s.time); + totalcon += s.ctime; + total += s.time; + } + + if (requests > 0) { /* avoid division by zero (if 0 requests) */ + printf("\n", + trstring, tdstring); + printf("\n", + trstring, tdstring, tdstring, tdstring, tdstring); + printf("" + "" + "" + "\n", + trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon); + printf("" + "" + "" + "\n", + trstring, tdstring, tdstring, mintot - mincon, tdstring, + (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon); + printf("" + "" + "" + "\n", + trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot); + } + printf("
Server Software:%s
Server Hostname:%s
Server Port:%d
Document Path:%s
Document Length:%d bytes
Concurrency Level:%d
Time taken for tests:%d.%03d seconds
Complete requests:%d
Failed requests:%d
(Connect: %d, Length: %d, Exceptions: %d)
Non-2xx responses:%d
Keep-Alive requests:%d
Total transferred:%d bytes
Total POSTed:%d
HTML transferred:%d bytes
Requests per second:%.2f
Transfer rate:%.2f kb/s received
 %.2f kb/s sent
 %.2f kb/s total
Connnection Times (ms)
  min avg max
Connect:%5d%5d%5d
Processing:%5d%5d%5d
Total:%5d%5d%5d
\n"); + } +} + +/* --------------------------------------------------------- */ + +/* start asnchronous non-blocking connection */ + +static void start_connect(struct connection * c) +{ + c->read = 0; + c->bread = 0; + c->keepalive = 0; + c->cbx = 0; + c->gotheader = 0; + + c->fd = socket(AF_INET, SOCK_STREAM, 0); + if (c->fd < 0) + err("socket"); + + nonblock(c->fd); + gettimeofday(&c->start, 0); + + if (connect(c->fd, (struct sockaddr *) & server, sizeof(server)) < 0) { + if (errno == EINPROGRESS) { + c->state = STATE_CONNECTING; + FD_SET(c->fd, &writebits); + return; + } + else { + close(c->fd); + err_conn++; + if (bad++ > 10) { + err("\nTest aborted after 10 failures\n\n"); + } + start_connect(c); + } + } + + /* connected first time */ + write_request(c); +} + +/* --------------------------------------------------------- */ + +/* close down connection and save stats */ + +static void close_connection(struct connection * c) +{ + if (c->read == 0 && c->keepalive) { + /* server has legitimately shut down an idle keep alive request */ + good--; /* connection never happend */ + } + else { + if (good == 1) { + /* first time here */ + doclen = c->bread; + } + else if (c->bread != doclen) { + bad++; + err_length++; + } + + /* save out time */ + if (done < requests) { + struct data s; + gettimeofday(&c->done, 0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + } + + close(c->fd); + FD_CLR(c->fd, &readbits); + FD_CLR(c->fd, &writebits); + + /* connect again */ + start_connect(c); + return; +} + +/* --------------------------------------------------------- */ + +/* read data from connection */ + +static void read_connection(struct connection * c) +{ + int r; + char *part; + char respcode[4]; /* 3 digits and null */ + + r = read(c->fd, buffer, sizeof(buffer)); + if (r == 0 || (r < 0 && errno != EAGAIN)) { + good++; + close_connection(c); + return; + } + + if (r < 0 && errno == EAGAIN) + return; + + c->read += r; + totalread += r; + + if (!c->gotheader) { + char *s; + int l = 4; + int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 + * terminator */ + int tocopy = (space < r) ? space : r; +#ifndef CHARSET_EBCDIC + memcpy(c->cbuff + c->cbx, buffer, tocopy); +#else /* CHARSET_EBCDIC */ + ascii2ebcdic(c->cbuff + c->cbx, buffer, tocopy); +#endif /* CHARSET_EBCDIC */ + c->cbx += tocopy; + space -= tocopy; + c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */ + if (verbosity >= 4) { + printf("LOG: header received:\n%s\n", c->cbuff); + } + s = strstr(c->cbuff, "\r\n\r\n"); + /* + * this next line is so that we talk to NCSA 1.5 which blatantly + * breaks the http specification + */ + if (!s) { + s = strstr(c->cbuff, "\n\n"); + l = 2; + } + + if (!s) { + /* read rest next time */ + if (space) + return; + else { + /* header is in invalid or too big - close connection */ + close(c->fd); + if (bad++ > 10) { + err("\nTest aborted after 10 failures\n\n"); + } + FD_CLR(c->fd, &writebits); + start_connect(c); + } + } + else { + /* have full header */ + if (!good) { + /* this is first time, extract some interesting info */ + char *p, *q; + p = strstr(c->cbuff, "Server:"); + q = servername; + if (p) { + p += 8; + while (*p > 32) + *q++ = *p++; + } + *q = 0; + } + + /* + * XXX: this parsing isn't even remotely HTTP compliant... but in + * the interest of speed it doesn't totally have to be, it just + * needs to be extended to handle whatever servers folks want to + * test against. -djg + */ + + /* check response code */ + part = strstr(c->cbuff, "HTTP"); /* really HTTP/1.x_ */ + strncpy(respcode, (part + strlen("HTTP/1.x_")), 3); + respcode[3] = '\0'; + if (respcode[0] != '2') { + err_response++; + if (verbosity >= 2) + printf("WARNING: Response code not 2xx (%s)\n", respcode); + } + else if (verbosity >= 3) { + printf("LOG: Response code = %s\n", respcode); + } + + c->gotheader = 1; + *s = 0; /* terminate at end of header */ + if (keepalive && + (strstr(c->cbuff, "Keep-Alive") + || strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */ + char *cl; + cl = strstr(c->cbuff, "Content-Length:"); + /* handle NCSA, which sends Content-length: */ + if (!cl) + cl = strstr(c->cbuff, "Content-length:"); + if (cl) { + c->keepalive = 1; + c->length = atoi(cl + 16); + } + } + c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy; + totalbread += c->bread; + } + } + else { + /* outside header, everything we have read is entity body */ + c->bread += r; + totalbread += r; + } + + if (c->keepalive && (c->bread >= c->length)) { + /* finished a keep-alive connection */ + good++; + doneka++; + /* save out time */ + if (good == 1) { + /* first time here */ + doclen = c->bread; + } + else if (c->bread != doclen) { + bad++; + err_length++; + } + if (done < requests) { + struct data s; + gettimeofday(&c->done, 0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + c->keepalive = 0; + c->length = 0; + c->gotheader = 0; + c->cbx = 0; + c->read = c->bread = 0; + write_request(c); + c->start = c->connect; /* zero connect time with keep-alive */ + } +} + +/* --------------------------------------------------------- */ + +/* run the tests */ + +static void test(void) +{ + struct timeval timeout, now; + fd_set sel_read, sel_except, sel_write; + int i; + + if (!use_html) { + printf("Benchmarking %s (be patient)...", hostname); + fflush(stdout); + } + + { + /* get server information */ + struct hostent *he; + he = gethostbyname(hostname); + if (!he) + err("bad hostname"); + server.sin_family = he->h_addrtype; + server.sin_port = htons(port); + server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0]; + } + + con = malloc(concurrency * sizeof(struct connection)); + memset(con, 0, concurrency * sizeof(struct connection)); + + stats = malloc(requests * sizeof(struct data)); + + FD_ZERO(&readbits); + FD_ZERO(&writebits); + + /* setup request */ + if (!posting) { + sprintf(request, "GET %s HTTP/1.0\r\n" + "User-Agent: ApacheBench/%s\r\n" + "%s" "%s" "%s" + "Host: %s\r\n" + "Accept: */*\r\n" + "\r\n" "%s", + path, + VERSION, + keepalive ? "Connection: Keep-Alive\r\n" : "", + cookie, auth, hostname, hdrs); + } + else { + sprintf(request, "POST %s HTTP/1.0\r\n" + "User-Agent: ApacheBench/%s\r\n" + "%s" "%s" "%s" + "Host: %s\r\n" + "Accept: */*\r\n" + "Content-length: %d\r\n" + "Content-type: %s\r\n" + "%s" + "\r\n", + path, + VERSION, + keepalive ? "Connection: Keep-Alive\r\n" : "", + cookie, auth, + hostname, postlen, + (content_type[0]) ? content_type : "text/plain", hdrs); + } + + if (verbosity >= 2) + printf("INFO: POST header == \n---\n%s\n---\n", request); + + reqlen = strlen(request); + +#ifdef CHARSET_EBCDIC + ebcdic2ascii(request, request, reqlen); +#endif /* CHARSET_EBCDIC */ + + /* ok - lets start */ + gettimeofday(&start, 0); + + /* initialise lots of requests */ + for (i = 0; i < concurrency; i++) + start_connect(&con[i]); + + while (done < requests) { + int n; + /* setup bit arrays */ + memcpy(&sel_except, &readbits, sizeof(readbits)); + memcpy(&sel_read, &readbits, sizeof(readbits)); + memcpy(&sel_write, &writebits, sizeof(readbits)); + + /* check for time limit expiry */ + gettimeofday(&now, 0); + if (tlimit && timedif(now, start) > (tlimit * 1000)) { + requests = done; /* so stats are correct */ + } + + /* Timeout of 30 seconds. */ + timeout.tv_sec = 30; + timeout.tv_usec = 0; + n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout); + if (!n) { + err("\nServer timed out\n\n"); + } + if (n < 1) + err("select"); + + for (i = 0; i < concurrency; i++) { + int s = con[i].fd; + if (FD_ISSET(s, &sel_except)) { + bad++; + err_except++; + start_connect(&con[i]); + continue; + } + if (FD_ISSET(s, &sel_read)) + read_connection(&con[i]); + if (FD_ISSET(s, &sel_write)) + write_request(&con[i]); + } + } + if (use_html) + output_html_results(); + else + output_results(); +} + +/* ------------------------------------------------------- */ + +/* display copyright information */ +static void copyright(void) +{ + if (!use_html) { + printf("This is ApacheBench, Version %s\n", VERSION); + printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n"); + printf("Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/\n"); + printf("\n"); + } + else { + printf("

\n"); + printf(" This is ApacheBench, Version %s
\n", VERSION); + printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
\n"); + printf(" Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/
\n"); + printf("

\n

\n"); + } +} + +/* display usage information */ +static void usage(char *progname) +{ + fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname); + fprintf(stderr, "Options are:\n"); + fprintf(stderr, " -n requests Number of requests to perform\n"); + fprintf(stderr, " -c concurrency Number of multiple requests to make\n"); + fprintf(stderr, " -t timelimit Seconds to max. wait for responses\n"); + fprintf(stderr, " -p postfile File containg data to POST\n"); + fprintf(stderr, " -T content-type Content-type header for POSTing\n"); + fprintf(stderr, " -v verbosity How much troubleshooting info to print\n"); + fprintf(stderr, " -w Print out results in HTML tables\n"); + fprintf(stderr, " -x attributes String to insert as table attributes\n"); + fprintf(stderr, " -y attributes String to insert as tr attributes\n"); + fprintf(stderr, " -z attributes String to insert as td or th attributes\n"); + fprintf(stderr, " -C attribute Add cookie, eg. 'Apache=1234. (repeatable)\n"); + fprintf(stderr, " -H attribute Add Arbitrary header line, eg. 'Accept-Encoding: zop'\n"); + fprintf(stderr, " Inserted after all normal header lines. (repeatable)\n"); + fprintf(stderr, " -A attribute Add Basic WWW Authentication, the attributes\n"); + fprintf(stderr, " are a colon separated username and password.\n"); + fprintf(stderr, " -p attribute Add Basic Proxy Authentication, the attributes\n"); + fprintf(stderr, " are a colon separated username and password.\n"); + fprintf(stderr, " -V Print version number and exit\n"); + fprintf(stderr, " -k Use HTTP KeepAlive feature\n"); + fprintf(stderr, " -h Display usage information (this message)\n"); + exit(EINVAL); +} + +/* ------------------------------------------------------- */ + +/* split URL into parts */ + +static int parse_url(char *url) +{ + char *cp; + char *h; + char *p = NULL; + + if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0) + url += 7; + h = url; + if ((cp = strchr(url, ':')) != NULL) { + *cp++ = '\0'; + p = cp; + url = cp; + } + if ((cp = strchr(url, '/')) == NULL) + return 1; + strcpy(path, cp); + *cp = '\0'; + strcpy(hostname, h); + if (p != NULL) + port = atoi(p); + return 0; +} + +/* ------------------------------------------------------- */ + +/* read data to POST from file, save contents and length */ + +static int open_postfile(char *pfile) +{ + int postfd, status; + struct stat postfilestat; + + if ((postfd = open(pfile, O_RDONLY)) == -1) { + printf("Invalid postfile name (%s)\n", pfile); + return errno; + } + if ((status = fstat(postfd, &postfilestat)) == -1) { + perror("Can\'t stat postfile\n"); + return status; + } + postdata = malloc(postfilestat.st_size); + if (!postdata) { + printf("Can\'t alloc postfile buffer\n"); + return ENOMEM; + } + if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) { + printf("error reading postfilen"); + return EIO; + } + postlen = postfilestat.st_size; + return 0; +} + +/* ------------------------------------------------------- */ + +extern char *optarg; +extern int optind, opterr, optopt; + +/* sort out command-line args and call test */ +int main(int argc, char **argv) +{ + int c, r,l; + char tmp[1024]; + + /* table defaults */ + tablestring = ""; + trstring = ""; + tdstring = "bgcolor=white"; + cookie[0] = '\0'; + auth[0] = '\0'; + hdrs[0] = '\0'; + optind = 1; + while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVhwx:y:z:C:H:P:A:")) > 0) { + switch (c) { + case 'n': + requests = atoi(optarg); + if (!requests) { + err("Invalid number of requests\n"); + } + break; + case 'k': + keepalive = 1; + break; + case 'c': + concurrency = atoi(optarg); + break; + case 'p': + if (0 == (r = open_postfile(optarg))) { + posting = 1; + } + else if (postdata) { + exit(r); + } + break; + case 'v': + verbosity = atoi(optarg); + break; + case 't': + tlimit = atoi(optarg); + requests = MAX_REQUESTS; /* need to size data array on + * something */ + break; + case 'T': + strcpy(content_type, optarg); + break; + case 'C': + strncat(cookie, "Cookie: ", sizeof(cookie)); + strncat(cookie, optarg, sizeof(cookie)); + strncat(cookie, "\r\n", sizeof(cookie)); + break; + case 'A': + /* assume username passwd already to be in colon separated form. Ready + * to be uu-encoded. + */ + while(isspace(*optarg)) + optarg++; + l=ap_base64encode(tmp,optarg,strlen(optarg)); + tmp[l]='\0'; + + strncat(auth, "Authorization: basic ", sizeof(auth)); + strncat(auth, tmp, sizeof(auth)); + strncat(auth, "\r\n", sizeof(auth)); + break; + case 'P': + /* + * assume username passwd already to be in colon separated form. + */ + while(isspace(*optarg)) + optarg++; + l=ap_base64encode(tmp,optarg,strlen(optarg)); + tmp[l]='\0'; + + strncat(auth, "Proxy-Authorization: basic ", sizeof(auth)); + strncat(auth, tmp, sizeof(auth)); + strncat(auth, "\r\n", sizeof(auth)); + break; + case 'H': + strncat(hdrs, optarg, sizeof(hdrs)); + strncat(hdrs, "\r\n", sizeof(hdrs)); + break; + case 'V': + copyright(); + exit(0); + break; + case 'w': + use_html = 1; + break; + /* + * if any of the following three are used, turn on html output + * automatically + */ + case 'x': + use_html = 1; + tablestring = optarg; + break; + case 'y': + use_html = 1; + trstring = optarg; + break; + case 'z': + use_html = 1; + tdstring = optarg; + break; + case 'h': + usage(argv[0]); + break; + default: + fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c); + usage(argv[0]); + break; + } + } + if (optind != argc - 1) { + fprintf(stderr, "%s: wrong number of arguments\n", argv[0]); + usage(argv[0]); + } + + if (parse_url(argv[optind++])) { + fprintf(stderr, "%s: invalid URL\n", argv[0]); + usage(argv[0]); + } + + copyright(); + test(); + + exit(0); +} diff --git a/support/apxs.in b/support/apxs.in new file mode 100644 index 0000000000..3bdd35d2b9 --- /dev/null +++ b/support/apxs.in @@ -0,0 +1,655 @@ +#!/usr/local/bin/perl +## ==================================================================== +## Copyright (c) 1998-1999 The Apache Group. All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## +## 3. All advertising materials mentioning features or use of this +## software must display the following acknowledgment: +## "This product includes software developed by the Apache Group +## for use in the Apache HTTP server project (http://www.apache.org/)." +## +## 4. The names "Apache Server" and "Apache Group" must not be used to +## endorse or promote products derived from this software without +## prior written permission. For written permission, please contact +## apache@apache.org. +## +## 5. Products derived from this software may not be called "Apache" +## nor may "Apache" appear in their names without prior written +## permission of the Apache Group. +## +## 6. Redistributions of any form whatsoever must retain the following +## acknowledgment: +## "This product includes software developed by the Apache Group +## for use in the Apache HTTP server project (http://www.apache.org/)." +## +## THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +## EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +## ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +## OF THE POSSIBILITY OF SUCH DAMAGE. +## ==================================================================== +## +## This software consists of voluntary contributions made by many +## individuals on behalf of the Apache Group and was originally based +## on public domain software written at the National Center for +## Supercomputing Applications, University of Illinois, Urbana-Champaign. +## For more information on the Apache Group and the Apache HTTP server +## project, please see . +## + +## +## apxs -- APache eXtenSion tool +## Written by Ralf S. Engelschall +## + +require 5.003; +use strict; +package apxs; + +## +## Configuration +## + +my $CFG_TARGET = '@TARGET@'; # substituted via Makefile.tmpl +my $CFG_CC = '@CC@'; # substituted via Makefile.tmpl +my $CFG_CFLAGS = '@CFLAGS@'; # substituted via Makefile.tmpl +my $CFG_CFLAGS_SHLIB = '@CFLAGS_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_LD_SHLIB = '@LD_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_LDFLAGS_SHLIB = '@LDFLAGS_MOD_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_LIBS_SHLIB = '@LIBS_SHLIB@'; # substituted via Makefile.tmpl +my $CFG_PREFIX = '@prefix@'; # substituted via APACI install +my $CFG_SBINDIR = '@sbindir@'; # substituted via APACI install +my $CFG_INCLUDEDIR = '@includedir@'; # substituted via APACI install +my $CFG_LIBEXECDIR = '@libexecdir@'; # substituted via APACI install +my $CFG_SYSCONFDIR = '@sysconfdir@'; # substituted via APACI install + +## +## Cleanup the above stuff +## +$CFG_CFLAGS =~ s|^\s+||; +$CFG_CFLAGS =~ s|\s+$||; +$CFG_CFLAGS =~ s|\s+`.+apaci`||; + +## +## Initial shared object support check +## +if (not -x "$CFG_SBINDIR/$CFG_TARGET") { + print STDERR "apxs:Error: $CFG_SBINDIR/$CFG_TARGET not found or not executable\n"; + exit(1); +} +if (not grep(/mod_so/, `$CFG_SBINDIR/$CFG_TARGET -l`)) { + print STDERR "apxs:Error: Sorry, no shared object support for Apache\n"; + print STDERR "apxs:Error: available under your platform. Make sure\n"; + print STDERR "apxs:Error: the Apache module mod_so is compiled into\n"; + print STDERR "apxs:Error: your server binary `$CFG_SBINDIR/$CFG_TARGET'.\n"; + exit(1); +} + +## +## parse argument line +## + +# defaults for parameters +my $opt_n = ''; +my $opt_g = ''; +my $opt_c = 0; +my $opt_o = ''; +my @opt_D = (); +my @opt_I = (); +my @opt_L = (); +my @opt_l = (); +my @opt_W = (); +my @opt_S = (); +my $opt_e = 0; +my $opt_i = 0; +my $opt_a = 0; +my $opt_A = 0; +my $opt_q = 0; + +# this subroutine is derived from Perl's getopts.pl with the enhancement of +# the "+" metacharater at the format string to allow a list to be build by +# subsequent occurance of the same option. +sub Getopts { + my ($argumentative, @ARGV) = @_; + my (@args, $first, $rest, $pos); + my ($errs) = 0; + local ($_); + local ($[) = 0; + + @args = split( / */, $argumentative); + while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) { + ($first, $rest) = ($1,$2); + if ($_ =~ m|^--$|) { + shift(@ARGV); + last; + } + $pos = index($argumentative,$first); + if($pos >= $[) { + if($args[$pos+1] eq ':') { + shift(@ARGV); + if($rest eq '') { + unless (@ARGV) { + print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n"; + ++$errs; + } + $rest = shift(@ARGV); + } + eval "\$opt_$first = \$rest;"; + } + elsif ($args[$pos+1] eq '+') { + shift(@ARGV); + if($rest eq '') { + unless (@ARGV) { + print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n"; + ++$errs; + } + $rest = shift(@ARGV); + } + eval "push(\@opt_$first, \$rest);"; + } + else { + eval "\$opt_$first = 1"; + if($rest eq '') { + shift(@ARGV); + } + else { + $ARGV[0] = "-$rest"; + } + } + } + else { + print STDERR "apxs:Error: Unknown option: $first\n"; + ++$errs; + if($rest ne '') { + $ARGV[0] = "-$rest"; + } + else { + shift(@ARGV); + } + } + } + return ($errs == 0, @ARGV); +} + +sub usage { + print STDERR "Usage: apxs -g [-S =] -n \n"; + print STDERR " apxs -q [-S =] ...\n"; + print STDERR " apxs -c [-S =] [-o ] [-D [=]]\n"; + print STDERR " [-I ] [-L ] [-l ] [-Wc,]\n"; + print STDERR " [-Wl,] ...\n"; + print STDERR " apxs -i [-S =] [-a] [-A] [-n ] ...\n"; + print STDERR " apxs -e [-S =] [-a] [-A] [-n ] ...\n"; + exit(1); +} + +# option handling +my $rc; +($rc, @ARGV) = &Getopts("qn:gco:I+D+L+l+W+S+eiaA", @ARGV); +&usage if ($rc == 0); +&usage if ($#ARGV == -1 and not $opt_g); +&usage if (not $opt_q and not ($opt_g and $opt_n) and not $opt_i and not $opt_c and not $opt_e); + +# argument handling +my @args = @ARGV; +my $name = 'unknown'; +$name = $opt_n if ($opt_n ne ''); + +if (@opt_S) { + my ($opt_S); + foreach $opt_S (@opt_S) { + if ($opt_S =~ m/^([^=]+)=(.*)$/) { + my ($var) = $1; + my ($val) = $2; + my $oldval = eval "\$CFG_$var"; + + unless ($var and $oldval) { + print STDERR "apxs:Error: no config variable $var\n"; + &usage; + } + + eval "\$CFG_${var}=\"${val}\""; + } else { + print STDERR "apxs:Error: malformatted -S option\n"; + &usage; + } + } +} + +## +## Operation +## + +# helper function for executing a list of +# system command with return code checks +sub execute_cmds { + my (@cmds) = @_; + my ($cmd, $rc); + + foreach $cmd (@cmds) { + print STDERR "$cmd\n"; + $rc = system("$cmd"); + if ($rc != 0) { + printf(STDERR "apxs:Break: Command failed with rc=%d\n", $rc << 8); + exit(1); + } + } +} + +if ($opt_g) { + ## + ## SAMPLE MODULE SOURCE GENERATION + ## + + if (-d $name) { + print STDERR "apxs:Error: Directory `$name' already exists. Remove first\n"; + exit(1); + } + + my $data = join('', ); + $data =~ s|%NAME%|$name|sg; + $data =~ s|%TARGET%|$CFG_TARGET|sg; + + my ($mkf, $src) = ($data =~ m|^(.+)-=#=-\n(.+)|s); + + print STDERR "Creating [DIR] $name\n"; + system("mkdir $name"); + print STDERR "Creating [FILE] $name/Makefile\n"; + open(FP, ">${name}/Makefile") || die; + print FP $mkf; + close(FP); + print STDERR "Creating [FILE] $name/mod_$name.c\n"; + open(FP, ">${name}/mod_${name}.c") || die; + print FP $src; + close(FP); + + exit(0); +} + + +if ($opt_q) { + ## + ## QUERY INFORMATION + ## + + my $result = ''; + my $arg; + foreach $arg (@args) { + my $ok = 0; + my $name; + foreach $name (qw( + TARGET CC CFLAGS CFLAGS_SHLIB LD_SHLIB LDFLAGS_SHLIB LIBS_SHLIB + PREFIX SBINDIR INCLUDEDIR LIBEXECDIR SYSCONFDIR + )) { + if ($arg eq $name or $arg eq lc($name)) { + my $val = eval "\$CFG_$name"; + $result .= "${val}::"; + $ok = 1; + } + } + if (not $ok) { + printf(STDERR "apxs:Error: Invalid query string `%s'\n", $arg); + exit(1); + } + } + $result =~ s|::$||; + $result =~ s|::| |; + print $result; +} + +if ($opt_c) { + ## + ## SHARED OBJECT COMPILATION + ## + + # split files into sources and objects + my @srcs = (); + my @objs = (); + my $f; + foreach $f (@args) { + if ($f =~ m|\.c$|) { + push(@srcs, $f); + } + else { + push(@objs, $f); + } + } + + # determine output file + my $dso_file; + if ($opt_o eq '') { + if ($#srcs > -1) { + $dso_file = $srcs[0]; + $dso_file =~ s|\.[^.]+$|.so|; + } + elsif ($#objs > -1) { + $dso_file = $objs[0]; + $dso_file =~ s|\.[^.]+$|.so|; + } + else { + $dso_file = "mod_unknown.so"; + } + } + else { + $dso_file = $opt_o; + } + + # create compilation commands + my @cmds = (); + my $opt = ''; + my ($opt_Wc, $opt_I, $opt_D); + foreach $opt_Wc (@opt_W) { + $opt .= "$1 " if ($opt_Wc =~ m|^\s*c,(.*)$|); + } + foreach $opt_I (@opt_I) { + $opt .= "-I$opt_I "; + } + foreach $opt_D (@opt_D) { + $opt .= "-D$opt_D "; + } + my $cflags = "$CFG_CFLAGS $CFG_CFLAGS_SHLIB"; + my $s; + foreach $s (@srcs) { + my $o = $s; + $o =~ s|\.c$|.o|; + push(@cmds, "$CFG_CC $cflags -I$CFG_INCLUDEDIR $opt -c $s"); + unshift(@objs, $o); + } + + # create link command + my $cmd = "$CFG_LD_SHLIB $CFG_LDFLAGS_SHLIB -o $dso_file"; + my $o; + foreach $o (@objs) { + $cmd .= " $o"; + } + $opt = ''; + my ($opt_Wl, $opt_L, $opt_l); + foreach $opt_Wl (@opt_W) { + if($CFG_LD_SHLIB ne "gcc") { + $opt .= " $1" if ($opt_Wl =~ m|^\s*l,(.*)$|); + } else { + $opt .= " -W$opt_Wl"; + } + } + foreach $opt_L (@opt_L) { + $opt .= " -L$opt_L"; + } + foreach $opt_l (@opt_l) { + $opt .= " -l$opt_l"; + } + $cmd .= $opt; + $cmd .= " $CFG_LIBS_SHLIB"; + push(@cmds, $cmd); + + # execute the commands + &execute_cmds(@cmds); + + # allow one-step compilation and installation + if ($opt_i or $opt_e) { + @args = ( $dso_file ); + } +} + +if ($opt_i or $opt_e) { + ## + ## SHARED OBJECT INSTALLATION + ## + + # determine installation commands + # and corresponding LoadModule/AddModule directives + my @lmd = (); + my @amd = (); + my @cmds = (); + my $f; + foreach $f (@args) { + if ($f !~ m|\.so$|) { + print STDERR "apxs:Error: file $f is not a shared object\n"; + exit(1); + } + my $t = $f; + $t =~ s|^.+/([^/]+)$|$1|; + if ($opt_i) { + push(@cmds, "cp $f $CFG_LIBEXECDIR/$t"); + push(@cmds, "chmod 755 $CFG_LIBEXECDIR/$t"); + } + + # determine module symbolname and filename + my $filename = ''; + if ($name eq 'unknown') { + $name = ''; + my $base = $f; + $base =~ s|\.[^.]+$||; + if (-f "$base.c") { + open(FP, "<$base.c"); + my $content = join('', ); + close(FP); + if ($content =~ m|.*module\s+(?:MODULE_VAR_EXPORT\s+)?([a-zA-Z0-9_]+)_module\s*=\s*.*|s) { + $name = "$1"; + $filename = "$base.c"; + $filename =~ s|^[^/]+/||; + } + } + if ($name eq '') { + if ($base =~ m|.*mod_([a-zA-Z0-9_]+)\..+|) { + $name = "$1"; + $filename = $base; + $filename =~ s|^[^/]+/||; + } + } + if ($name eq '') { + print "apxs:Error: Sorry, cannot determine bootstrap symbol name\n"; + print "apxs:Error: Please specify one with option `-n'\n"; + exit(1); + } + } + if ($filename eq '') { + $filename = "mod_${name}.c"; + } + my $dir = $CFG_LIBEXECDIR; + $dir =~ s|^$CFG_PREFIX/?||; + $dir =~ s|(.)$|$1/|; + push(@lmd, sprintf("LoadModule %-18s %s", "${name}_module", "$dir$t")); + push(@amd, sprintf("AddModule %s", $filename)); + } + + # execute the commands + &execute_cmds(@cmds); + + # activate module via LoadModule/AddModule directive + if ($opt_a or $opt_A) { + if (not -f "$CFG_SYSCONFDIR/$CFG_TARGET.conf") { + print "apxs:Error: Config file $CFG_SYSCONFDIR/$CFG_TARGET.conf not found\n"; + exit(1); + } + + open(FP, "<$CFG_SYSCONFDIR/$CFG_TARGET.conf") || die; + my $content = join('', ); + close(FP); + + if ($content !~ m|\n#?\s*LoadModule\s+|) { + print STDERR "apxs:Error: Activation failed for custom $CFG_SYSCONFDIR/$CFG_TARGET.conf file.\n"; + print STDERR "apxs:Error: At least one `LoadModule' directive already has to exist.\n"; + exit(1); + } + + my $lmd; + my $c = ''; + $c = '#' if ($opt_A); + foreach $lmd (@lmd) { + my $what = $opt_A ? "preparing" : "activating"; + if ($content !~ m|\n#?\s*$lmd|) { + $content =~ s|^(.*\n#?\s*LoadModule\s+[^\n]+\n)|$1$c$lmd\n|sg; + } else { + $content =~ s|^(.*\n)#?\s*$lmd[^\n]*\n|$1$c$lmd\n|sg; + } + $lmd =~ m|LoadModule\s+(.+?)_module.*|; + print STDERR "[$what module `$1' in $CFG_SYSCONFDIR/$CFG_TARGET.conf]\n"; + } + my $amd; + foreach $amd (@amd) { + if ($content !~ m|\n#?\s*$amd|) { + $content =~ s|^(.*\n#?\s*AddModule\s+[^\n]+\n)|$1$c$amd\n|sg; + } else { + $content =~ s|^(.*\n)#?\s*$amd[^\n]*\n|$1$c$amd\n|sg; + } + } + if (@lmd or @amd) { + open(FP, ">$CFG_SYSCONFDIR/$CFG_TARGET.conf.new") || die; + print FP $content; + close(FP); + system("cp $CFG_SYSCONFDIR/$CFG_TARGET.conf $CFG_SYSCONFDIR/$CFG_TARGET.conf.bak && " . + "cp $CFG_SYSCONFDIR/$CFG_TARGET.conf.new $CFG_SYSCONFDIR/$CFG_TARGET.conf && " . + "rm $CFG_SYSCONFDIR/$CFG_TARGET.conf.new"); + } + } +} + +##EOF## +__DATA__ +## +## Makefile -- Build procedure for sample %NAME% Apache module +## Autogenerated via ``apxs -n %NAME% -g''. +## + +# the used tools +APXS=apxs +APACHECTL=apachectl + +# additional defines, includes and libraries +#DEF=-Dmy_define=my_value +#INC=-Imy/include/dir +#LIB=-Lmy/lib/dir -lmylib + +# the default target +all: mod_%NAME%.so + +# compile the shared object file +mod_%NAME%.so: mod_%NAME%.c + $(APXS) -c $(DEF) $(INC) $(LIB) mod_%NAME%.c + +# install the shared object file into Apache +install: all + $(APXS) -i -a -n '%NAME%' mod_%NAME%.so + +# cleanup +clean: + -rm -f mod_%NAME%.o mod_%NAME%.so + +# simple test +test: reload + lynx -mime_header http://localhost/%NAME% + +# install and activate shared object by reloading Apache to +# force a reload of the shared object file +reload: install restart + +# the general Apache start/restart/stop +# procedures +start: + $(APACHECTL) start +restart: + $(APACHECTL) restart +stop: + $(APACHECTL) stop + +-=#=- +/* +** mod_%NAME%.c -- Apache sample %NAME% module +** [Autogenerated via ``apxs -n %NAME% -g''] +** +** To play with this sample module first compile it into a +** DSO file and install it into Apache's libexec directory +** by running: +** +** $ apxs -c -i mod_%NAME%.c +** +** Then activate it in Apache's %TARGET%.conf file for instance +** for the URL /%NAME% in as follows: +** +** # %TARGET%.conf +** LoadModule %NAME%_module libexec/mod_%NAME%.so +** +** SetHandler %NAME% +** +** +** Then after restarting Apache via +** +** $ apachectl restart +** +** you immediately can request the URL /%NAME and watch for the +** output of this module. This can be achieved for instance via: +** +** $ lynx -mime_header http://localhost/%NAME% +** +** The output should be similar to the following one: +** +** HTTP/1.1 200 OK +** Date: Tue, 31 Mar 1998 14:42:22 GMT +** Server: Apache/1.3.4 (Unix) +** Connection: close +** Content-Type: text/html +** +** The sample page from mod_%NAME%.c +*/ + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "ap_config.h" + +/* The sample content handler */ +static int %NAME%_handler(request_rec *r) +{ + r->content_type = "text/html"; + ap_send_http_header(r); + if (!r->header_only) + ap_rputs("The sample page from mod_%NAME%.c\n", r); + return OK; +} + +/* Dispatch list of content handlers */ +static const handler_rec %NAME%_handlers[] = { + { "%NAME%", %NAME%_handler }, + { NULL, NULL } +}; + +/* Dispatch list for API hooks */ +module MODULE_VAR_EXPORT %NAME%_module = { + STANDARD_MODULE_STUFF, + NULL, /* module initializer */ + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + %NAME%_handlers, /* [#8] MIME-typed-dispatched handlers */ + NULL, /* [#1] URI to filename translation */ + NULL, /* [#4] validate user id from request */ + NULL, /* [#5] check if the user is ok _here_ */ + NULL, /* [#3] check access by host address */ + NULL, /* [#6] determine MIME type */ + NULL, /* [#7] pre-run fixups */ + NULL, /* [#9] log a transaction */ + NULL, /* [#2] header parser */ + NULL, /* child_init */ + NULL, /* child_exit */ + NULL /* [#0] post read-request */ +}; + diff --git a/support/dbmmanage b/support/dbmmanage new file mode 100644 index 0000000000..721bdd1eb3 --- /dev/null +++ b/support/dbmmanage @@ -0,0 +1,189 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995-1999 The Apache Group. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. All advertising materials mentioning features or use of this +# software must display the following acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# 4. The names "Apache Server" and "Apache Group" must not be used to +# endorse or promote products derived from this software without +# prior written permission. For written permission, please contact +# apache@apache.org. +# +# 5. Products derived from this software may not be called "Apache" +# nor may "Apache" appear in their names without prior written +# permission of the Apache Group. +# +# 6. Redistributions of any form whatsoever must retain the following +# acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# ==================================================================== +# +# This software consists of voluntary contributions made by many +# individuals on behalf of the Apache Group and was originally based +# on public domain software written at the National Center for +# Supercomputing Applications, University of Illinois, Urbana-Champaign. +# For more information on the Apache Group and the Apache HTTP server +# project, please see . + +#for more functionality see the HTTPD::UserAdmin module: +# http://www.perl.com/CPAN/modules/by-module/HTTPD/HTTPD-Tools-x.xx.tar.gz +# +# usage: dbmmanage + +package dbmmanage; +# -ldb -lndbm -lgdbm +BEGIN { @AnyDBM_File::ISA = qw(DB_File NDBM_File GDBM_File) } +use strict; +use Fcntl; +use AnyDBM_File (); + +my($file,$command,$key,$crypted_pwd) = @ARGV; + +usage() unless $file and $command and defined &{$dbmc::{$command}}; + +# if your osname is in $newstyle_salt, then use new style salt (starts with '_' and contains +# four bytes of iteration count and four bytes of salt). Otherwise, just use +# the traditional two-byte salt. +# see the man page on your system to decide if you have a newer crypt() lib. +# I believe that 4.4BSD derived systems do (at least BSD/OS 2.0 does). +# The new style crypt() allows up to 20 characters of the password to be +# significant rather than only 8. +my $newstyle_salt = join '|', qw{bsdos}; #others? + +# remove extension if any +my $chop = join '|', qw{db.? pag dir}; +$file =~ s/\.($chop)$//; + +my $is_update = $command eq "update"; +my $Is_Win32 = $^O eq "MSWin32"; +my %DB = (); +my @range = (); +my($mode, $flags) = $command =~ + /^(?:view|check)$/ ? (0644, O_RDONLY) : (0644, O_RDWR|O_CREAT); + +tie %DB, "AnyDBM_File", $file, $flags, $mode || die "Can't tie $file: $!"; +dbmc->$command(); +untie %DB; + +sub usage { + my $cmds = join "|", sort keys %dbmc::; + die "usage: $0 filename [$cmds] [username]\n"; +} + +my $x; +sub genseed { + my $psf; + for (qw(-xlwwa -le)) { + `ps $_ 2>/dev/null`; + $psf = $_, last unless $?; + } + srand (time ^ $$ ^ unpack("%L*", `ps $psf | gzip -f`)); + @range = (qw(. /), '0'..'9','a'..'z','A'..'Z'); + $x = int scalar @range; +} + +sub randchar { + join '', map $range[rand $x], 1..shift||1; +} + +sub salt { + my $newstyle = $^O =~ /(?:$newstyle_salt)/; + genseed() unless @range; + return $newstyle ? + join '', "_", randchar, "a..", randchar(4) : + randchar(2); +} + +sub getpass { + my $prompt = shift || "Enter password:"; + + unless($Is_Win32) { + open STDIN, "/dev/tty" or warn "couldn't open /dev/tty $!\n"; + system "stty -echo;"; + } + + my($c,$pwd); + print STDERR $prompt; + while (($c = getc(STDIN)) ne '' and $c ne "\n" and $c ne "\r") { + $pwd .= $c; + } + + system "stty echo" unless $Is_Win32; + print STDERR "\n"; + die "Can't use empty password!\n" unless length $pwd; + return $pwd; +} + +sub dbmc::update { + die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key}; + dbmc->adduser; +} + +sub dbmc::add { + die "Can't use empty password!\n" unless $crypted_pwd; + unless($is_update) { + die "Sorry, user `$key' already exists!\n" if $DB{$key}; + } + $DB{$key} = $crypted_pwd; + my $action = $is_update ? "updated" : "added"; + print "User $key $action with password encrypted to $DB{$key}\n"; +} + +sub dbmc::adduser { + my $value = getpass "New password:"; + die "They don't match, sorry.\n" unless getpass("Re-type new password:") eq $value; + $crypted_pwd = crypt $value, caller->salt; + dbmc->add; +} + +sub dbmc::delete { + die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key}; + delete $DB{$key}, print "`$key' deleted\n"; +} + +sub dbmc::view { + print $key ? "$key:$DB{$key}\n" : map { "$_:$DB{$_}\n" if $DB{$_} } keys %DB; +} + +sub dbmc::check { + die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key}; + print crypt(getpass(), $DB{$key}) eq $DB{$key} ? "password ok\n" : "password mismatch\n"; +} + +sub dbmc::import { + while(defined($_ = ) and chomp) { + ($key,$crypted_pwd) = split /:/, $_, 2; + dbmc->add; + } +} + diff --git a/support/htdigest.c b/support/htdigest.c new file mode 100644 index 0000000000..d1fc41800f --- /dev/null +++ b/support/htdigest.c @@ -0,0 +1,271 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ +/****************************************************************************** + ****************************************************************************** + * NOTE! This program is not safe as a setuid executable! Do not make it + * setuid! + ****************************************************************************** + *****************************************************************************/ +/* + * htdigest.c: simple program for manipulating digest passwd file for Apache + * + * by Alexei Kosut, based on htpasswd.c, by Rob McCool + */ + +#include "ap_config.h" +#include +#include "ap.h" +#include "ap_md5.h" +#if defined(MPE) || defined(QNX) || defined(WIN32) || defined(__TANDEM) +#include +#else +#include +#endif + +#ifdef WIN32 +#include +#define unlink _unlink +#endif + +#ifdef CHARSET_EBCDIC +#define LF '\n' +#define CR '\r' +#else +#define LF 10 +#define CR 13 +#endif /* CHARSET_EBCDIC */ + +#define MAX_STRING_LEN 256 + +char *tn; + +static void getword(char *word, char *line, char stop) +{ + int x = 0, y; + + for (x = 0; ((line[x]) && (line[x] != stop)); x++) + word[x] = line[x]; + + word[x] = '\0'; + if (line[x]) + ++x; + y = 0; + + while ((line[y++] = line[x++])); +} + +static int getline(char *s, int n, FILE *f) +{ + register int i = 0; + + while (1) { + s[i] = (char) fgetc(f); + + if (s[i] == CR) + s[i] = fgetc(f); + + if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) { + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + ++i; + } +} + +static void putline(FILE *f, char *l) +{ + int x; + + for (x = 0; l[x]; x++) + fputc(l[x], f); + fputc('\n', f); +} + + +static void add_password(char *user, char *realm, FILE *f) +{ + char *pw; + AP_MD5_CTX context; + unsigned char digest[16]; + char string[MAX_STRING_LEN]; + char pwin[MAX_STRING_LEN]; + char pwv[MAX_STRING_LEN]; + unsigned int i; + + if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) { + fprintf(stderr, "password too long"); + exit(5); + } + ap_getpass("Re-type new password: ", pwv, sizeof(pwv)); + if (strcmp(pwin, pwv) != 0) { + fprintf(stderr, "They don't match, sorry.\n"); + if (tn) { + unlink(tn); + } + exit(1); + } + pw = pwin; + fprintf(f, "%s:%s:", user, realm); + + /* Do MD5 stuff */ + sprintf(string, "%s:%s:%s", user, realm, pw); + + ap_MD5Init(&context); + ap_MD5Update(&context, (unsigned char *) string, strlen(string)); + ap_MD5Final(digest, &context); + + for (i = 0; i < 16; i++) + fprintf(f, "%02x", digest[i]); + + fprintf(f, "\n"); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: htdigest [-c] passwordfile realm username\n"); + fprintf(stderr, "The -c flag creates a new file.\n"); + exit(1); +} + +static void interrupted(void) +{ + fprintf(stderr, "Interrupted.\n"); + if (tn) + unlink(tn); + exit(1); +} + +int main(int argc, char *argv[]) +{ + FILE *tfp, *f; + char user[MAX_STRING_LEN]; + char realm[MAX_STRING_LEN]; + char line[MAX_STRING_LEN]; + char l[MAX_STRING_LEN]; + char w[MAX_STRING_LEN]; + char x[MAX_STRING_LEN]; + char command[MAX_STRING_LEN]; + int found; + + tn = NULL; + signal(SIGINT, (void (*)(int)) interrupted); + if (argc == 5) { + if (strcmp(argv[1], "-c")) + usage(); + if (!(tfp = fopen(argv[2], "w"))) { + fprintf(stderr, "Could not open passwd file %s for writing.\n", + argv[2]); + perror("fopen"); + exit(1); + } + printf("Adding password for %s in realm %s.\n", argv[4], argv[3]); + add_password(argv[4], argv[3], tfp); + fclose(tfp); + exit(0); + } + else if (argc != 4) + usage(); + + tn = tmpnam(NULL); + if (!(tfp = fopen(tn, "w"))) { + fprintf(stderr, "Could not open temp file.\n"); + exit(1); + } + + if (!(f = fopen(argv[1], "r"))) { + fprintf(stderr, + "Could not open passwd file %s for reading.\n", argv[1]); + fprintf(stderr, "Use -c option to create new one.\n"); + exit(1); + } + strcpy(user, argv[3]); + strcpy(realm, argv[2]); + + found = 0; + while (!(getline(line, MAX_STRING_LEN, f))) { + if (found || (line[0] == '#') || (!line[0])) { + putline(tfp, line); + continue; + } + strcpy(l, line); + getword(w, l, ':'); + getword(x, l, ':'); + if (strcmp(user, w) || strcmp(realm, x)) { + putline(tfp, line); + continue; + } + else { + printf("Changing password for user %s in realm %s\n", user, realm); + add_password(user, realm, tfp); + found = 1; + } + } + if (!found) { + printf("Adding user %s in realm %s\n", user, realm); + add_password(user, realm, tfp); + } + fclose(f); + fclose(tfp); +#if defined(OS2) || defined(WIN32) + sprintf(command, "copy \"%s\" \"%s\"", tn, argv[1]); +#else + sprintf(command, "cp %s %s", tn, argv[1]); +#endif + system(command); + unlink(tn); + return 0; +} diff --git a/support/htpasswd.c b/support/htpasswd.c new file mode 100644 index 0000000000..21a24e0c0a --- /dev/null +++ b/support/htpasswd.c @@ -0,0 +1,577 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/****************************************************************************** + ****************************************************************************** + * NOTE! This program is not safe as a setuid executable! Do not make it + * setuid! + ****************************************************************************** + *****************************************************************************/ +/* + * htpasswd.c: simple program for manipulating password file for + * the Apache HTTP server + * + * Originally by Rob McCool + * + * Exit values: + * 0: Success + * 1: Failure; file access/permission problem + * 2: Failure; command line syntax problem (usage message issued) + * 3: Failure; password verification failure + * 4: Failure; operation interrupted (such as with CTRL/C) + * 5: Failure; buffer would overflow (username, filename, or computed + * record too long) + * 6: Failure; username contains illegal or reserved characters + */ + +#include "ap_config.h" +#include +#include +#include +#include "ap.h" +#include "ap_md5.h" +#include "ap_sha1.h" + +#ifdef WIN32 +#include +#include "../os/win32/getopt.h" +#define unlink _unlink +#endif + +#ifndef CHARSET_EBCDIC +#define LF 10 +#define CR 13 +#else /*CHARSET_EBCDIC*/ +#define LF '\n' +#define CR '\r' +#endif /*CHARSET_EBCDIC*/ + +#define MAX_STRING_LEN 256 +#define ALG_PLAIN 0 +#define ALG_CRYPT 1 +#define ALG_APMD5 2 +#define ALG_APSHA 3 + +#define ERR_FILEPERM 1 +#define ERR_SYNTAX 2 +#define ERR_PWMISMATCH 3 +#define ERR_INTERRUPTED 4 +#define ERR_OVERFLOW 5 +#define ERR_BADUSER 6 + +/* + * This needs to be declared statically so the signal handler can + * access it. + */ +static char *tempfilename; + +/* + * Get a line of input from the user, not including any terminating + * newline. + */ +static int getline(char *s, int n, FILE *f) +{ + register int i = 0; + + while (1) { + s[i] = (char) fgetc(f); + + if (s[i] == CR) { + s[i] = fgetc(f); + } + + if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) { + s[i] = '\0'; + return (feof(f) ? 1 : 0); + } + ++i; + } +} + +static void putline(FILE *f, char *l) +{ + int x; + + for (x = 0; l[x]; x++) { + fputc(l[x], f); + } + fputc('\n', f); +} + +/* + * Make a password record from the given information. A zero return + * indicates success; failure means that the output buffer contains an + * error message instead. + */ +static int mkrecord(char *user, char *record, size_t rlen, char *passwd, + int alg) +{ + char *pw; + char cpw[120]; + char pwin[MAX_STRING_LEN]; + char pwv[MAX_STRING_LEN]; + char salt[9]; + + if (passwd != NULL) { + pw = passwd; + } + else { + if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) { + ap_snprintf(record, (rlen - 1), "password too long (>%d)", + sizeof(pwin) - 1); + return ERR_OVERFLOW; + } + ap_getpass("Re-type new password: ", pwv, sizeof(pwv)); + if (strcmp(pwin, pwv) != 0) { + ap_cpystrn(record, "password verification error", (rlen - 1)); + return ERR_PWMISMATCH; + } + pw = pwin; + memset(pwv, '\0', sizeof(pwin)); + } + switch (alg) { + + case ALG_APSHA: + /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */ + ap_sha1_base64(pw,strlen(pw),cpw); + break; + + case ALG_APMD5: + (void) srand((int) time((time_t *) NULL)); + ap_to64(&salt[0], rand(), 8); + salt[8] = '\0'; + + ap_MD5Encode((const unsigned char *)pw, (const unsigned char *)salt, + cpw, sizeof(cpw)); + break; + + case ALG_PLAIN: + /* XXX this len limitation is not in sync with any HTTPd len. */ + ap_cpystrn(cpw,pw,sizeof(cpw)); + break; + + case ALG_CRYPT: + default: + (void) srand((int) time((time_t *) NULL)); + ap_to64(&salt[0], rand(), 8); + salt[8] = '\0'; + + ap_cpystrn(cpw, (char *)crypt(pw, salt), sizeof(cpw) - 1); + break; + } + memset(pw, '\0', strlen(pw)); + + /* + * Check to see if the buffer is large enough to hold the username, + * hash, and delimiters. + */ + if ((strlen(user) + 1 + strlen(cpw)) > (rlen - 1)) { + ap_cpystrn(record, "resultant record too long", (rlen - 1)); + return ERR_OVERFLOW; + } + strcpy(record, user); + strcat(record, ":"); + strcat(record, cpw); + return 0; +} + +static int usage(void) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "\thtpasswd [-cmdps] passwordfile username\n"); + fprintf(stderr, "\thtpasswd -b[cmdps] passwordfile username password\n\n"); + fprintf(stderr, " -c Create a new file.\n"); + fprintf(stderr, " -m Force MD5 encryption of the password" +#if defined(WIN32) || defined(TPF) + " (default)" +#endif + ".\n"); + fprintf(stderr, " -d Force CRYPT encryption of the password" +#if (!(defined(WIN32) || defined(TPF))) + " (default)" +#endif + ".\n"); + fprintf(stderr, " -p Do not encrypt the password (plaintext).\n"); + fprintf(stderr, " -s Force SHA encryption of the password.\n"); + fprintf(stderr, " -b Use the password from the command line rather " + "than prompting for it.\n"); + fprintf(stderr, + "On Windows and TPF systems the '-m' flag is used by default.\n"); + fprintf(stderr, + "On all other systems, the '-p' flag will probably not work.\n"); + return ERR_SYNTAX; +} + +static void interrupted(void) +{ + fprintf(stderr, "Interrupted.\n"); + if (tempfilename != NULL) { + unlink(tempfilename); + } + exit(ERR_INTERRUPTED); +} + +/* + * Check to see if the specified file can be opened for the given + * access. + */ +static int accessible(char *fname, char *mode) +{ + FILE *s; + + s = fopen(fname, mode); + if (s == NULL) { + return 0; + } + fclose(s); + return 1; +} + +/* + * Return true if a file is readable. + */ +static int readable(char *fname) +{ + return accessible(fname, "r"); +} + +/* + * Return true if the specified file can be opened for write access. + */ +static int writable(char *fname) +{ + return accessible(fname, "a"); +} + +/* + * Return true if the named file exists, regardless of permissions. + */ +static int exists(char *fname) +{ +#ifdef WIN32 + struct _stat sbuf; +#else + struct stat sbuf; +#endif + int check; + +#ifdef WIN32 + check = _stat(fname, &sbuf); +#else + check = stat(fname, &sbuf); +#endif + return ((check == -1) && (errno == ENOENT)) ? 0 : 1; +} + +/* + * Copy from the current position of one file to the current position + * of another. + */ +static void copy_file(FILE *target, FILE *source) +{ + static char line[MAX_STRING_LEN]; + + while (fgets(line, sizeof(line), source) != NULL) { + fputs(line, target); + } +} + +/* + * Let's do it. We end up doing a lot of file opening and closing, + * but what do we care? This application isn't run constantly. + */ +int main(int argc, char *argv[]) +{ + FILE *ftemp = NULL; + FILE *fpw = NULL; + char user[MAX_STRING_LEN]; + char password[MAX_STRING_LEN]; + char record[MAX_STRING_LEN]; + char line[MAX_STRING_LEN]; + char pwfilename[MAX_STRING_LEN]; + char *arg; + int found = 0; + int alg = ALG_CRYPT; + int newfile = 0; + int noninteractive = 0; + int i; + int args_left = 2; + + tempfilename = NULL; + signal(SIGINT, (void (*)(int)) interrupted); + + /* + * Preliminary check to make sure they provided at least + * three arguments, we'll do better argument checking as + * we parse the command line. + */ + if (argc < 3) { + return usage(); + } + + /* + * Go through the argument list and pick out any options. They + * have to precede any other arguments. + */ + for (i = 1; i < argc; i++) { + arg = argv[i]; + if (*arg != '-') { + break; + } + while (*++arg != '\0') { + if (*arg == 'c') { + newfile++; + } + else if (*arg == 'm') { + alg = ALG_APMD5; + } + else if (*arg == 's') { + alg = ALG_APSHA; + } + else if (*arg == 'p') { + alg = ALG_PLAIN; + } + else if (*arg == 'd') { + alg = ALG_CRYPT; + } + else if (*arg == 'b') { + noninteractive++; + args_left++; + } + else { + return usage(); + } + } + } + + /* + * Make sure we still have exactly the right number of arguments left + * (the filename, the username, and possibly the password if -b was + * specified). + */ + if ((argc - i) != args_left) { + return usage(); + } + if (strlen(argv[i]) > (sizeof(pwfilename) - 1)) { + fprintf(stderr, "%s: filename too long\n", argv[0]); + return ERR_OVERFLOW; + } + strcpy(pwfilename, argv[i]); + if (strlen(argv[i + 1]) > (sizeof(user) - 1)) { + fprintf(stderr, "%s: username too long (>%d)\n", argv[0], + sizeof(user) - 1); + return ERR_OVERFLOW; + } + strcpy(user, argv[i + 1]); + if ((arg = strchr(user, ':')) != NULL) { + fprintf(stderr, "%s: username contains illegal character '%c'\n", + argv[0], *arg); + return ERR_BADUSER; + } + if (noninteractive) { + if (strlen(argv[i + 2]) > (sizeof(password) - 1)) { + fprintf(stderr, "%s: password too long (>%d)\n", argv[0], + sizeof(password) - 1); + return ERR_OVERFLOW; + } + strcpy(password, argv[i + 2]); + } + +#ifdef WIN32 + if (alg == ALG_CRYPT) { + alg = ALG_APMD5; + fprintf(stderr, "Automatically using MD5 format on Windows.\n"); + } +#endif + +#if (!(defined(WIN32) || defined(TPF))) + if (alg == ALG_PLAIN) { + fprintf(stderr,"Warning: storing passwords as plain text might " + "just not work on this platform.\n"); + } +#endif + /* + * Verify that the file exists if -c was omitted. We give a special + * message if it doesn't. + */ + if ((! newfile) && (! exists(pwfilename))) { + fprintf(stderr, "%s: cannot modify file %s; use '-c' to create it\n", + argv[0], pwfilename); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * Verify that we can read the existing file in the case of an update + * to it (rather than creation of a new one). + */ + if ((! newfile) && (! readable(pwfilename))) { + fprintf(stderr, "%s: cannot open file %s for read access\n", + argv[0], pwfilename); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * Now check to see if we can preserve an existing file in case + * of password verification errors on a -c operation. + */ + if (newfile && exists(pwfilename) && (! readable(pwfilename))) { + fprintf(stderr, "%s: cannot open file %s for read access\n" + "%s: existing auth data would be lost on password mismatch", + argv[0], pwfilename, argv[0]); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * Now verify that the file is writable! + */ + if (! writable(pwfilename)) { + fprintf(stderr, "%s: cannot open file %s for write access\n", + argv[0], pwfilename); + perror("fopen"); + exit(ERR_FILEPERM); + } + + /* + * All the file access checks have been made. Time to go to work; + * try to create the record for the username in question. If that + * fails, there's no need to waste any time on file manipulations. + * Any error message text is returned in the record buffer, since + * the mkrecord() routine doesn't have access to argv[]. + */ + i = mkrecord(user, record, sizeof(record) - 1, + noninteractive ? password : NULL, + alg); + if (i != 0) { + fprintf(stderr, "%s: %s\n", argv[0], record); + exit(i); + } + + /* + * We can access the files the right way, and we have a record + * to add or update. Let's do it.. + */ + tempfilename = tmpnam(NULL); + ftemp = fopen(tempfilename, "w+"); + if (ftemp == NULL) { + fprintf(stderr, "%s: unable to create temporary file\n", argv[0]); + perror("fopen"); + exit(ERR_FILEPERM); + } + /* + * If we're not creating a new file, copy records from the existing + * one to the temporary file until we find the specified user. + */ + if (! newfile) { + char scratch[MAX_STRING_LEN]; + + fpw = fopen(pwfilename, "r"); + while (! (getline(line, sizeof(line), fpw))) { + char *colon; + + if ((line[0] == '#') || (line[0] == '\0')) { + putline(ftemp, line); + continue; + } + strcpy(scratch, line); + /* + * See if this is our user. + */ + colon = strchr(scratch, ':'); + if (colon != NULL) { + *colon = '\0'; + } + if (strcmp(user, scratch) != 0) { + putline(ftemp, line); + continue; + } + found++; + break; + } + } + if (found) { + fprintf(stderr, "Updating "); + } + else { + fprintf(stderr, "Adding "); + } + fprintf(stderr, "password for user %s\n", user); + /* + * Now add the user record we created. + */ + putline(ftemp, record); + /* + * If we're updating an existing file, there may be additional + * records beyond the one we're updating, so copy them. + */ + if (! newfile) { + copy_file(ftemp, fpw); + fclose(fpw); + } + /* + * The temporary file now contains the information that should be + * in the actual password file. Close the open files, re-open them + * in the appropriate mode, and copy them file to the real one. + */ + fclose(ftemp); + fpw = fopen(pwfilename, "w+"); + ftemp = fopen(tempfilename, "r"); + copy_file(fpw, ftemp); + fclose(fpw); + fclose(ftemp); + unlink(tempfilename); + return 0; +} diff --git a/support/httpd.exp b/support/httpd.exp new file mode 100644 index 0000000000..31164b59b6 --- /dev/null +++ b/support/httpd.exp @@ -0,0 +1,412 @@ +#! +ap_MD5Encode +ap_MD5Final +ap_MD5Init +ap_MD5Update +ap_SHA1Final +ap_SHA1Init +ap_SHA1Update_binary +ap_SHA1Update +ap_add_cgi_vars +ap_add_common_vars +ap_add_module +ap_add_named_module +ap_add_per_dir_conf +ap_add_per_url_conf +ap_add_version_component +ap_allow_options +ap_allow_overrides +ap_append_arrays +ap_array_cat +ap_array_pstrcat +ap_auth_name +ap_auth_type +ap_base64encode +ap_base64encode_binary +ap_base64encode_len +ap_base64decode +ap_base64decode_binary +ap_base64decode_len +ap_basic_http_header +ap_bclose +ap_bcreate +ap_bfilbuf +ap_bfileno +ap_bflsbuf +ap_bflush +ap_bgetopt +ap_bgets +ap_bhalfduplex +ap_bind_address +ap_block_alarms +ap_blookc +ap_bnonblock +ap_bonerror +ap_bprintf +ap_bpushfd +ap_bputs +ap_bread +ap_bsetflag +ap_bsetopt +ap_bskiplf +ap_bspawn_child +ap_bvputs +ap_bwrite +ap_bytes_in_free_blocks +ap_bytes_in_pool +ap_call_exec +ap_can_exec +ap_cfg_closefile +ap_cfg_getc +ap_cfg_getline +ap_chdir_file +ap_check_access +ap_check_auth +ap_check_cmd_context +ap_check_user_id +ap_checkmask +ap_child_exit_modules +ap_child_init_modules +ap_child_terminate +ap_cleanup_for_exec +ap_clear_module_list +ap_clear_pool +ap_clear_table +ap_close_piped_log +ap_construct_server +ap_construct_url +ap_content_type_tolower +ap_copy_array +ap_copy_array_hdr +ap_copy_table +ap_core_reorder_directories +ap_coredump_dir +ap_count_dirs +ap_cpystrn +ap_create_environment +ap_create_per_dir_config +ap_create_request_config +ap_daemons_limit +ap_daemons_max_free +ap_daemons_min_free +ap_daemons_to_start +ap_day_snames +ap_default_port_for_request +ap_default_port_for_scheme +ap_default_type +ap_destroy_pool +ap_destroy_sub_req +ap_die +ap_discard_request_body +ap_document_root +ap_dummy_mutex +ap_each_byterange +ap_error_log2stderr +ap_escape_html +ap_escape_path_segment +ap_escape_quotes +ap_escape_shell_cmd +ap_excess_requests_per_child +ap_exists_config_define +ap_exists_scoreboard_image +ap_extended_status +ap_field_noparam +ap_finalize_request_protocol +ap_finalize_sub_req_protocol +ap_find_command +ap_find_command_in_modules +ap_find_last_token +ap_find_linked_module +ap_find_list_item +ap_find_module_name +ap_find_path_info +ap_find_token +ap_find_types +ap_fini_vhost_config +ap_fnmatch +ap_force_library_loading +ap_get_basic_auth_pw +ap_get_client_block +ap_get_gmtoff +ap_get_list_item +ap_get_local_host +ap_get_remote_host +ap_get_remote_logname +ap_get_server_built +ap_get_server_name +ap_get_server_port +ap_get_server_version +ap_get_time +ap_get_token +ap_get_virthost_addr +ap_getparents +ap_getword +ap_getword_conf +ap_getword_conf_nc +ap_getword_nc +ap_getword_nulls +ap_getword_nulls_nc +ap_getword_white +ap_getword_white_nc +ap_gm_timestr_822 +ap_gname2id +ap_group_id +ap_handle_command +ap_hard_timeout +ap_header_parse +ap_ht_time +ap_ind +ap_index_of_response +ap_init_alloc +ap_init_modules +ap_init_vhost_config +ap_init_virtual_host +ap_internal_redirect +ap_internal_redirect_handler +ap_invoke_handler +ap_is_directory +ap_is_fnmatch +ap_is_initial_req +ap_is_matchexp +ap_is_url +ap_keepalive_timeout +ap_kill_cleanup +ap_kill_cleanups_for_fd +ap_kill_cleanups_for_socket +ap_kill_timeout +ap_limit_section +ap_listenbacklog +ap_listeners +ap_lock_fname +ap_log_assert +ap_log_error +ap_log_error_old +ap_log_pid +ap_log_printf +ap_log_reason +ap_log_rerror +ap_log_transaction +ap_log_unixerr +ap_make_array +ap_make_dirstr +ap_make_dirstr_parent +ap_make_dirstr_prefix +ap_make_etag +ap_make_full_path +ap_make_sub_pool +ap_make_table +ap_matches_request_vhost +ap_max_requests_per_child +ap_md5 +ap_md5contextTo64 +ap_md5digest +ap_meets_conditions +ap_merge_per_dir_configs +ap_method_number_of +ap_month_snames +ap_my_generation +ap_no2slash +ap_note_auth_failure +ap_note_basic_auth_failure +ap_note_cleanups_for_fd +ap_note_cleanups_for_file +ap_note_cleanups_for_socket +ap_note_digest_auth_failure +ap_note_subprocess +ap_null_cleanup +ap_open_logs +ap_open_piped_log +ap_os_escape_path +ap_os_is_path_absolute +ap_overlay_tables +ap_overlap_tables +ap_palloc +ap_parseHTTPdate +ap_parse_hostinfo_components +ap_parse_htaccess +ap_parse_uri +ap_parse_uri_components +ap_parse_vhost_addrs +ap_pbase64decode +ap_pbase64encode +ap_pcalloc +ap_pcfg_open_custom +ap_pcfg_openfile +ap_pclosedir +ap_pclosef +ap_pclosesocket +ap_pduphostent +ap_pfclose +ap_pfdopen +ap_pfopen +ap_pgethostbyname +ap_pid_fname +ap_popendir +ap_popenf +ap_pregcomp +ap_pregfree +ap_pregsub +ap_prelinked_modules +ap_preloaded_modules +ap_process_request +ap_process_resource_config +ap_psignature +ap_psocket +ap_psprintf +ap_pstrcat +ap_pstrdup +ap_pstrndup +ap_push_array +ap_pvsprintf +ap_rationalize_mtime +ap_read_config +ap_read_request +ap_regerror +ap_regexec +ap_register_cleanup +ap_register_other_child +ap_remove_module +ap_requires +ap_reset_timeout +ap_response_code_string +ap_restart_time +ap_rfc1413 +ap_rfc1413_timeout +ap_rflush +ap_rind +ap_rprintf +ap_rputc +ap_rputs +ap_run_cleanup +ap_run_fixups +ap_run_post_read_request +ap_run_sub_req +ap_rvputs +ap_rwrite +ap_satisfies +ap_scan_script_header_err +ap_scan_script_header_err_buff +ap_scoreboard_fname +ap_scoreboard_image +ap_send_error_response +ap_send_fb +ap_send_fb_length +ap_send_fd +ap_send_fd_length +ap_send_header_field +ap_send_http_header +ap_send_http_options +ap_send_http_trace +ap_send_mmap +ap_send_size +ap_server_argv0 +ap_server_config_defines +ap_server_confname +ap_server_post_read_config +ap_server_pre_read_config +ap_server_root +ap_server_root_relative +ap_set_byterange +ap_set_callback_and_alarm +ap_set_content_length +ap_set_etag +ap_set_file_slot +ap_set_flag_slot +ap_set_keepalive +ap_set_last_modified +ap_set_name_virtual_host +ap_set_string_slot +ap_set_string_slot_lower +ap_set_sub_req_protocol +ap_setup_client_block +ap_setup_prelinked_modules +ap_sha1_base64 +ap_should_client_block +ap_show_directives +ap_show_modules +ap_signal +ap_single_module_configure +ap_size_list_item +ap_slack +ap_snprintf +ap_soft_timeout +ap_some_auth_required +ap_spawn_child +ap_srm_command_loop +ap_standalone +ap_start_restart +ap_start_shutdown +ap_str_tolower +ap_strcasecmp_match +ap_strcmp_match +ap_sub_req_lookup_file +ap_sub_req_lookup_uri +ap_sub_req_method_uri +ap_suexec_enabled +ap_sync_scoreboard_image +ap_table_add +ap_table_addn +ap_table_do +ap_table_get +ap_table_merge +ap_table_mergen +ap_table_set +ap_table_setn +ap_table_unset +ap_threads_per_child +ap_tm2sec +ap_translate_name +ap_uname2id +ap_unblock_alarms +ap_unescape_url +ap_unparse_uri_components +ap_unregister_other_child +ap_update_child_status +ap_update_mtime +ap_update_vhost_from_headers +ap_update_vhost_given_ip +ap_user_id +ap_user_name +ap_util_init +ap_util_uri_init +ap_uudecode +ap_validate_password +ap_vbprintf +ap_vformatter +ap_vsnprintf +core_module +top_module +XML_DefaultCurrent +XML_ErrorString +XML_ExternalEntityParserCreate +XML_GetBase +XML_GetBuffer +XML_GetCurrentByteCount +XML_GetCurrentByteIndex +XML_GetCurrentColumnNumber +XML_GetCurrentLineNumber +XML_GetErrorCode +XML_GetSpecifiedAttributeCount +XML_Parse +XML_ParseBuffer +XML_ParserCreate +XML_ParserCreateNS +XML_ParserFree +XML_SetBase +XML_SetCdataSectionHandler +XML_SetCharacterDataHandler +XML_SetCommentHandler +XML_SetDefaultHandler +XML_SetDefaultHandlerExpand +XML_SetElementHandler +XML_SetEncoding +XML_SetExternalEntityRefHandler +XML_SetExternalEntityRefHandlerArg +XML_SetNamespaceDeclHandler +XML_SetNotStandaloneHandler +XML_SetNotationDeclHandler +XML_SetProcessingInstructionHandler +XML_SetUnknownEncodingHandler +XML_SetUnparsedEntityDeclHandler +XML_SetUserData +XML_UseParserAsHandlerArg diff --git a/support/log_server_status b/support/log_server_status new file mode 100644 index 0000000000..1eb2893edc --- /dev/null +++ b/support/log_server_status @@ -0,0 +1,118 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995-1999 The Apache Group. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. All advertising materials mentioning features or use of this +# software must display the following acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# 4. The names "Apache Server" and "Apache Group" must not be used to +# endorse or promote products derived from this software without +# prior written permission. For written permission, please contact +# apache@apache.org. +# +# 5. Products derived from this software may not be called "Apache" +# nor may "Apache" appear in their names without prior written +# permission of the Apache Group. +# +# 6. Redistributions of any form whatsoever must retain the following +# acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# ==================================================================== +# +# This software consists of voluntary contributions made by many +# individuals on behalf of the Apache Group and was originally based +# on public domain software written at the National Center for +# Supercomputing Applications, University of Illinois, Urbana-Champaign. +# For more information on the Apache Group and the Apache HTTP server +# project, please see . + + +# Log Server Status +# Mark J Cox, UK Web Ltd 1996, mark@ukweb.com +# +# This script is designed to be run at a frequent interval by something +# like cron. It connects to the server and downloads the status +# information. It reformats the information to a single line and logs +# it to a file. Make sure the directory $wherelog is writable by the +# user who runs this script. +# +require 'sys/socket.ph'; + +$wherelog = "/var/log/graph/"; # Logs will be like "/var/log/graph/19960312" +$server = "localhost"; # Name of server, could be "www.foo.com" +$port = "80"; # Port on server +$request = "/status/?auto"; # Request to send + +sub tcp_connect +{ + local($host,$port) =@_; + $sockaddr='S n a4 x8'; + chop($hostname=`hostname`); + $port=(getservbyname($port, 'tcp'))[2] unless $port =~ /^\d+$/; + $me=pack($sockaddr,&AF_INET,0,(gethostbyname($hostname))[4]); + $them=pack($sockaddr,&AF_INET,$port,(gethostbyname($host))[4]); + socket(S,&PF_INET,&SOCK_STREAM,(getprotobyname('tcp'))[2]) || + die "socket: $!"; + bind(S,$me) || return "bind: $!"; + connect(S,$them) || return "connect: $!"; + select(S); + $| = 1; + select(stdout); + return ""; +} + +### Main + +{ + $year=`date +%y`; + chomp($year); + $year += ($year < 70) ? 2000 : 1900; + $date = $year . `date +%m%d:%H%M%S`; + chomp($date); + ($day,$time)=split(/:/,$date); + $res=&tcp_connect($server,$port); + open(OUT,">>$wherelog$day"); + if ($res) { + print OUT "$time:-1:-1:-1:-1:$res\n"; + exit 1; + } + print S "GET $request\n"; + while () { + $requests=$1 if ( m|^BusyServers:\ (\S+)|); + $idle=$1 if ( m|^IdleServers:\ (\S+)|); + $number=$1 if ( m|sses:\ (\S+)|); + $cpu=$1 if (m|^CPULoad:\ (\S+)|); + } + print OUT "$time:$requests:$idle:$number:$cpu\n"; +} + + diff --git a/support/logresolve.c b/support/logresolve.c new file mode 100644 index 0000000000..bcdc6e51b5 --- /dev/null +++ b/support/logresolve.c @@ -0,0 +1,345 @@ +/* + * logresolve 1.1 + * + * Tom Rathborne - tomr@uunet.ca - http://www.uunet.ca/~tomr/ + * UUNET Canada, April 16, 1995 + * + * Rewritten by David Robinson. (drtr@ast.cam.ac.uk) + * + * Usage: logresolve [-s filename] [-c] < access_log > new_log + * + * Arguments: + * -s filename name of a file to record statistics + * -c check the DNS for a matching A record for the host. + * + * Notes: + * + * To generate meaningful statistics from an HTTPD log file, it's good + * to have the domain name of each machine that accessed your site, but + * doing this on the fly can slow HTTPD down. + * + * Compiling NCSA HTTPD with the -DMINIMAL_DNS flag turns IP#->hostname + * resolution off. Before running your stats program, just run your log + * file through this program (logresolve) and all of your IP numbers will + * be resolved into hostnames (where possible). + * + * logresolve takes an HTTPD access log (in the COMMON log file format, + * or any other format that has the IP number/domain name as the first + * field for that matter), and outputs the same file with all of the + * domain names looked up. Where no domain name can be found, the IP + * number is left in. + * + * To minimize impact on your nameserver, logresolve has its very own + * internal hash-table cache. This means that each IP number will only + * be looked up the first time it is found in the log file. + * + * The -c option causes logresolve to apply the same check as httpd + * compiled with -DMAXIMUM_DNS; after finding the hostname from the IP + * address, it looks up the IP addresses for the hostname and checks + * that one of these matches the original address. + */ + +#include "ap_config.h" +#include + +#include + +#ifndef MPE +#include +#endif + +static void cgethost(struct in_addr ipnum, char *string, int check); +static int getline(char *s, int n); +static void stats(FILE *output); + + +/* maximum line length */ +#define MAXLINE 1024 + +/* maximum length of a domain name */ +#ifndef MAXDNAME +#define MAXDNAME 256 +#endif + +/* number of buckets in cache hash table */ +#define BUCKETS 256 + +#if defined(NEED_STRDUP) +char *strdup (const char *str) +{ + char *dup; + + if (!(dup = (char *) malloc(strlen(str) + 1))) + return NULL; + dup = strcpy(dup, str); + + return dup; +} +#endif + +/* + * struct nsrec - record of nameservice for cache linked list + * + * ipnum - IP number hostname - hostname noname - nonzero if IP number has no + * hostname, i.e. hostname=IP number + */ + +struct nsrec { + struct in_addr ipnum; + char *hostname; + int noname; + struct nsrec *next; +} *nscache[BUCKETS]; + +/* + * statistics - obvious + */ + +#ifndef h_errno +extern int h_errno; /* some machines don't have this in their headers */ +#endif + +/* largeste value for h_errno */ +#define MAX_ERR (NO_ADDRESS) +#define UNKNOWN_ERR (MAX_ERR+1) +#define NO_REVERSE (MAX_ERR+2) + +static int cachehits = 0; +static int cachesize = 0; +static int entries = 0; +static int resolves = 0; +static int withname = 0; +static int errors[MAX_ERR + 3]; + +/* + * cgethost - gets hostname by IP address, caching, and adding unresolvable + * IP numbers with their IP number as hostname, setting noname flag + */ + +static void cgethost (struct in_addr ipnum, char *string, int check) +{ + struct nsrec **current, *new; + struct hostent *hostdata; + char *name; + + current = &nscache[((ipnum.s_addr + (ipnum.s_addr >> 8) + + (ipnum.s_addr >> 16) + (ipnum.s_addr >> 24)) % BUCKETS)]; + + while (*current != NULL && ipnum.s_addr != (*current)->ipnum.s_addr) + current = &(*current)->next; + + if (*current == NULL) { + cachesize++; + new = (struct nsrec *) malloc(sizeof(struct nsrec)); + if (new == NULL) { + perror("malloc"); + fprintf(stderr, "Insufficient memory\n"); + exit(1); + } + *current = new; + new->next = NULL; + + new->ipnum = ipnum; + + hostdata = gethostbyaddr((const char *) &ipnum, sizeof(struct in_addr), + AF_INET); + if (hostdata == NULL) { + if (h_errno > MAX_ERR) + errors[UNKNOWN_ERR]++; + else + errors[h_errno]++; + new->noname = h_errno; + name = strdup(inet_ntoa(ipnum)); + } + else { + new->noname = 0; + name = strdup(hostdata->h_name); + if (check) { + if (name == NULL) { + perror("strdup"); + fprintf(stderr, "Insufficient memory\n"); + exit(1); + } + hostdata = gethostbyname(name); + if (hostdata != NULL) { + char **hptr; + + for (hptr = hostdata->h_addr_list; *hptr != NULL; hptr++) + if (((struct in_addr *) (*hptr))->s_addr == ipnum.s_addr) + break; + if (*hptr == NULL) + hostdata = NULL; + } + if (hostdata == NULL) { + fprintf(stderr, "Bad host: %s != %s\n", name, + inet_ntoa(ipnum)); + new->noname = NO_REVERSE; + free(name); + name = strdup(inet_ntoa(ipnum)); + errors[NO_REVERSE]++; + } + } + } + new->hostname = name; + if (new->hostname == NULL) { + perror("strdup"); + fprintf(stderr, "Insufficient memory\n"); + exit(1); + } + } + else + cachehits++; + + /* size of string == MAXDNAME +1 */ + strncpy(string, (*current)->hostname, MAXDNAME); + string[MAXDNAME] = '\0'; +} + +/* + * prints various statistics to output + */ + +static void stats (FILE *output) +{ + int i; + char *ipstring; + struct nsrec *current; + char *errstring[MAX_ERR + 3]; + + for (i = 0; i < MAX_ERR + 3; i++) + errstring[i] = "Unknown error"; + errstring[HOST_NOT_FOUND] = "Host not found"; + errstring[TRY_AGAIN] = "Try again"; + errstring[NO_RECOVERY] = "Non recoverable error"; + errstring[NO_DATA] = "No data record"; + errstring[NO_ADDRESS] = "No address"; + errstring[NO_REVERSE] = "No reverse entry"; + + fprintf(output, "logresolve Statistics:\n"); + + fprintf(output, "Entries: %d\n", entries); + fprintf(output, " With name : %d\n", withname); + fprintf(output, " Resolves : %d\n", resolves); + if (errors[HOST_NOT_FOUND]) + fprintf(output, " - Not found : %d\n", errors[HOST_NOT_FOUND]); + if (errors[TRY_AGAIN]) + fprintf(output, " - Try again : %d\n", errors[TRY_AGAIN]); + if (errors[NO_DATA]) + fprintf(output, " - No data : %d\n", errors[NO_DATA]); + if (errors[NO_ADDRESS]) + fprintf(output, " - No address: %d\n", errors[NO_ADDRESS]); + if (errors[NO_REVERSE]) + fprintf(output, " - No reverse: %d\n", errors[NO_REVERSE]); + fprintf(output, "Cache hits : %d\n", cachehits); + fprintf(output, "Cache size : %d\n", cachesize); + fprintf(output, "Cache buckets : IP number * hostname\n"); + + for (i = 0; i < BUCKETS; i++) + for (current = nscache[i]; current != NULL; current = current->next) { + ipstring = inet_ntoa(current->ipnum); + if (current->noname == 0) + fprintf(output, " %3d %15s - %s\n", i, ipstring, + current->hostname); + else { + if (current->noname > MAX_ERR + 2) + fprintf(output, " %3d %15s : Unknown error\n", i, + ipstring); + else + fprintf(output, " %3d %15s : %s\n", i, ipstring, + errstring[current->noname]); + } + } +} + + +/* + * gets a line from stdin + */ + +static int getline (char *s, int n) +{ + char *cp; + + if (!fgets(s, n, stdin)) + return (0); + cp = strchr(s, '\n'); + if (cp) + *cp = '\0'; + return (1); +} + +int main (int argc, char *argv[]) +{ + struct in_addr ipnum; + char *bar, hoststring[MAXDNAME + 1], line[MAXLINE], *statfile; + int i, check; + + check = 0; + statfile = NULL; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "-c") == 0) + check = 1; + else if (strcmp(argv[i], "-s") == 0) { + if (i == argc - 1) { + fprintf(stderr, "logresolve: missing filename to -s\n"); + exit(1); + } + i++; + statfile = argv[i]; + } + else { + fprintf(stderr, "Usage: logresolve [-s statfile] [-c] < input > output\n"); + exit(0); + } + } + + + for (i = 0; i < BUCKETS; i++) + nscache[i] = NULL; + for (i = 0; i < MAX_ERR + 2; i++) + errors[i] = 0; + + while (getline(line, MAXLINE)) { + if (line[0] == '\0') + continue; + entries++; + if (!isdigit(line[0])) { /* short cut */ + puts(line); + withname++; + continue; + } + bar = strchr(line, ' '); + if (bar != NULL) + *bar = '\0'; + ipnum.s_addr = inet_addr(line); + if (ipnum.s_addr == 0xffffffffu) { + if (bar != NULL) + *bar = ' '; + puts(line); + withname++; + continue; + } + + resolves++; + + cgethost(ipnum, hoststring, check); + if (bar != NULL) + printf("%s %s\n", hoststring, bar + 1); + else + puts(hoststring); + } + + if (statfile != NULL) { + FILE *fp; + fp = fopen(statfile, "w"); + if (fp == NULL) { + fprintf(stderr, "logresolve: could not open statistics file '%s'\n" + ,statfile); + exit(1); + } + stats(fp); + fclose(fp); + } + + return (0); +} diff --git a/support/logresolve.pl b/support/logresolve.pl new file mode 100644 index 0000000000..59ab9ebaf2 --- /dev/null +++ b/support/logresolve.pl @@ -0,0 +1,264 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995-1999 The Apache Group. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. All advertising materials mentioning features or use of this +# software must display the following acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# 4. The names "Apache Server" and "Apache Group" must not be used to +# endorse or promote products derived from this software without +# prior written permission. For written permission, please contact +# apache@apache.org. +# +# 5. Products derived from this software may not be called "Apache" +# nor may "Apache" appear in their names without prior written +# permission of the Apache Group. +# +# 6. Redistributions of any form whatsoever must retain the following +# acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# ==================================================================== +# +# This software consists of voluntary contributions made by many +# individuals on behalf of the Apache Group and was originally based +# on public domain software written at the National Center for +# Supercomputing Applications, University of Illinois, Urbana-Champaign. +# For more information on the Apache Group and the Apache HTTP server +# project, please see . + +# logresolve.pl +# +# v 1.2 by robh @ imdb.com +# +# usage: logresolve.pl outfile +# +# input = Apache/NCSA/.. logfile with IP numbers at start of lines +# output = same logfile with IP addresses resolved to hostnames where +# name lookups succeeded. +# +# this differs from the C based 'logresolve' in that this script +# spawns a number ($CHILDREN) of subprocesses to resolve addresses +# concurrently and sets a short timeout ($TIMEOUT) for each lookup in +# order to keep things moving quickly. +# +# the parent process handles caching of IP->hostnames using a Perl hash +# it also avoids sending the same IP to multiple child processes to be +# resolved multiple times concurrently. +# +# Depending on the settings of $CHILDREN and $TIMEOUT you should see +# significant reductions in the overall time taken to resolve your +# logfiles. With $CHILDREN=40 and $TIMEOUT=5 I've seen 200,000 - 300,000 +# logfile lines processed per hour compared to ~45,000 per hour +# with 'logresolve'. +# +# I haven't yet seen any noticable reduction in the percentage of IPs +# that fail to get resolved. Your mileage will no doubt vary. 5s is long +# enough to wait IMO. +# +# Known to work with FreeBSD 2.2 +# Known to have problems with Solaris +# +# 980417 - use 'sockaddr_un' for bind/connect to make the script work +# with linux. Fix from Luuk de Boer + +require 5.004; + +$|=1; + +use FileHandle; +use Socket; + +use strict; +no strict 'refs'; + +use vars qw($PROTOCOL); +$PROTOCOL = 0; + +my $CHILDREN = 40; +my $TIMEOUT = 5; + +my $filename; +my %hash = (); +my $parent = $$; + +my @children = (); +for (my $child = 1; $child <=$CHILDREN; $child++) { + my $f = fork(); + if (!$f) { + $filename = "./.socket.$parent.$child"; + if (-e $filename) { unlink($filename) || warn "$filename .. $!\n";} + &child($child); + exit(0); + } + push(@children, $f); +} + +&parent; +&cleanup; + +## remove all temporary files before shutting down +sub cleanup { + # die kiddies, die + kill(15, @children); + for (my $child = 1; $child <=$CHILDREN; $child++) { + if (-e "./.socket.$parent.$child") { + unlink("./.socket.$parent.$child") + || warn ".socket.$parent.$child $!"; + } + } +} + +sub parent { + # Trap some possible signals to trigger temp file cleanup + $SIG{'KILL'} = $SIG{'INT'} = $SIG{'PIPE'} = \&cleanup; + + my %CHILDSOCK; + my $filename; + + ## fork child processes. Each child will create a socket connection + ## to this parent and use an unique temp filename to do so. + for (my $child = 1; $child <=$CHILDREN; $child++) { + $CHILDSOCK{$child}= FileHandle->new; + + if (!socket($CHILDSOCK{$child}, AF_UNIX, SOCK_STREAM, $PROTOCOL)) { + warn "parent socket to child failed $!"; + } + $filename = "./.socket.$parent.$child"; + my $response; + do { + $response = connect($CHILDSOCK{$child}, sockaddr_un($filename)); + if ($response != 1) { + sleep(1); + } + } while ($response != 1); + $CHILDSOCK{$child}->autoflush; + } + ## All child processes should now be ready or at worst warming up + + my (@buffer, $child, $ip, $rest, $hostname, $response); + ## read the logfile lines from STDIN + while() { + @buffer = (); # empty the logfile line buffer array. + $child = 1; # children are numbered 1..N, start with #1 + + # while we have a child to talk to and data to give it.. + do { + push(@buffer, $_); # buffer the line + ($ip, $rest) = split(/ /, $_, 2); # separate IP form rest + + unless ($hash{$ip}) { # resolve if unseen IP + $CHILDSOCK{$child}->print("$ip\n"); # pass IP to next child + $hash{$ip} = $ip; # don't look it up again. + $child++; + } + } while (($child < ($CHILDREN-1)) and ($_ = )); + + ## now poll each child for a response + while (--$child > 0) { + $response = $CHILDSOCK{$child}->getline; + chomp($response); + # child sends us back both the IP and HOSTNAME, no need for us + # to remember what child received any given IP, and no worries + # what order we talk to the children + ($ip, $hostname) = split(/\|/, $response, 2); + $hash{$ip} = $hostname; + } + + # resolve all the logfiles lines held in the log buffer array.. + for (my $line = 0; $line <=$#buffer; $line++) { + # get next buffered line + ($ip, $rest) = split(/ /, $buffer[$line], 2); + # separate IP from rest and replace with cached hostname + printf STDOUT ("%s %s", $hash{$ip}, $rest); + } + } +} + +######################################## + +sub child { + # arg = numeric ID - how the parent refers to me + my $me = shift; + + # add trap for alarm signals. + $SIG{'ALRM'} = sub { die "alarmed"; }; + + # create a socket to communicate with parent + socket(INBOUND, AF_UNIX, SOCK_STREAM, $PROTOCOL) + || die "Error with Socket: !$\n"; + $filename = "./.socket.$parent.$me"; + bind(INBOUND, sockaddr_un($filename)) + || die "Error Binding $filename: $!\n"; + listen(INBOUND, 5) || die "Error Listening: $!\n"; + + my ($ip, $send_back); + my $talk = FileHandle->new; + + # accept a connection from the parent process. We only ever have + # have one connection where we exchange 1 line of info with the + # parent.. 1 line in (IP address), 1 line out (IP + hostname). + accept($talk, INBOUND) || die "Error Accepting: $!\n"; + # disable I/O buffering just in case + $talk->autoflush; + # while the parent keeps sending data, we keep responding.. + while(($ip = $talk->getline)) { + chomp($ip); + # resolve the IP if time permits and send back what we found.. + $send_back = sprintf("%s|%s", $ip, &nslookup($ip)); + $talk->print($send_back."\n"); + } +} + +# perform a time restricted hostname lookup. +sub nslookup { + # get the IP as an arg + my $ip = shift; + my $hostname = undef; + + # do the hostname lookup inside an eval. The eval will use the + # already configured SIGnal handler and drop out of the {} block + # regardless of whether the alarm occured or not. + eval { + alarm($TIMEOUT); + $hostname = gethostbyaddr(gethostbyname($ip), AF_INET); + alarm(0); + }; + if ($@ =~ /alarm/) { + # useful for debugging perhaps.. + # print "alarming, isn't it? ($ip)"; + } + + # return the hostname or the IP address itself if there is no hostname + $hostname ne "" ? $hostname : $ip; +} + + diff --git a/support/phf_abuse_log.cgi b/support/phf_abuse_log.cgi new file mode 100644 index 0000000000..9ce2749c57 --- /dev/null +++ b/support/phf_abuse_log.cgi @@ -0,0 +1,21 @@ +#!/usr/local/bin/perl + +# This script can be used to detect people trying to abuse the security hole which +# existed in A CGI script direstributed with Apache 1.0.3 and earlier versions. +# You can redirect them to here using the "" suggestion in +# httpd.conf. +# +# The format logged to is "[date] remote_addr remote_host [date] referrer user_agent". + +$LOG = "/var/log/phf_log"; + +require "ctime.pl"; +$when = &ctime(time); +$when =~ s/\n//go; +$ENV{HTTP_USER_AGENT} .= " via $ENV{HTTP_VIA}" if($ENV{HTTP_VIA}); + +open(LOG, ">>$LOG") || die "boo hoo, phf_log $!"; +print LOG "[$when] $ENV{REMOTE_ADDR} $ENV{REMOTE_HOST} $ENV{$HTTP_REFERER} $ENV{HTTP_USER_AGENT}\n"; +close(LOG); + +print "Content-type: text/html\r\n\r\nSmile, you're on Candid Camera.\n"; diff --git a/support/rotatelogs.c b/support/rotatelogs.c new file mode 100644 index 0000000000..dc6bd97729 --- /dev/null +++ b/support/rotatelogs.c @@ -0,0 +1,82 @@ +/* + * Simple program to rotate Apache logs without having to kill the server. + * + * Contributed by Ben Laurie + * + * 12 Mar 1996 + */ + + +#define BUFSIZE 65536 +#define MAX_PATH 1024 + +#include "ap_config.h" +#include +#include +#include + +int main (int argc, char **argv) +{ + char buf[BUFSIZE], buf2[MAX_PATH]; + time_t tLogEnd = 0; + time_t tRotation; + int nLogFD = -1; + int nRead; + char *szLogRoot; + + if (argc != 3) { + fprintf(stderr, + "%s \n\n", + argv[0]); +#ifdef OS2 + fprintf(stderr, + "Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n", + argv[0]); +#else + fprintf(stderr, + "Add this:\n\nTransferLog \"|%s /some/where 86400\"\n\n", + argv[0]); +#endif + fprintf(stderr, + "to httpd.conf. The generated name will be /some/where.nnnn " + "where nnnn is the\nsystem time at which the log nominally " + "starts (N.B. this time will always be a\nmultiple of the " + "rotation time, so you can synchronize cron scripts with it).\n" + "At the end of each rotation time a new log is started.\n"); + exit(1); + } + + szLogRoot = argv[1]; + tRotation = atoi(argv[2]); + if (tRotation <= 0) { + fprintf(stderr, "Rotation time must be > 0\n"); + exit(6); + } + + for (;;) { + nRead = read(0, buf, sizeof buf); + if (nRead == 0) + exit(3); + if (nRead < 0) + if (errno != EINTR) + exit(4); + if (nLogFD >= 0 && (time(NULL) >= tLogEnd || nRead < 0)) { + close(nLogFD); + nLogFD = -1; + } + if (nLogFD < 0) { + time_t tLogStart = (time(NULL) / tRotation) * tRotation; + sprintf(buf2, "%s.%010d", szLogRoot, (int) tLogStart); + tLogEnd = tLogStart + tRotation; + nLogFD = open(buf2, O_WRONLY | O_CREAT | O_APPEND, 0666); + if (nLogFD < 0) { + perror(buf2); + exit(2); + } + } + if (write(nLogFD, buf, nRead) != nRead) { + perror(buf2); + exit(5); + } + } +} diff --git a/support/split-logfile b/support/split-logfile new file mode 100644 index 0000000000..7cc4652da8 --- /dev/null +++ b/support/split-logfile @@ -0,0 +1,103 @@ +#!/usr/local/bin/perl +# +# ==================================================================== +# Copyright (c) 1995-1999 The Apache Group. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. All advertising materials mentioning features or use of this +# software must display the following acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# 4. The names "Apache Server" and "Apache Group" must not be used to +# endorse or promote products derived from this software without +# prior written permission. For permission please contact +# Apache@Apache.Org. +# +# 5. Products derived from this software may not be called "Apache" +# nor may "Apache" appear in their names without prior written +# permission of the Apache Group. +# +# 6. Redistributions of any form whatsoever must retain the following +# acknowledgment: +# "This product includes software developed by the Apache Group +# for use in the Apache HTTP server project (http://www.apache.org/)." +# +# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR +# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. +# ==================================================================== +# +# This software consists of voluntary contributions made by many +# individuals on behalf of the Apache Group and was originally based +# on public domain software written at the National Center for +# Supercomputing Applications, University of Illinois, Urbana-Champaign. +# For more information on the Apache Group and the Apache HTTP server +# project, please see . +# + +# +# This script will take a combined Web server access +# log file and break its contents into separate files. +# It assumes that the first field of each line is the +# virtual host identity (put there by "%v"), and that +# the logfiles should be named that+".log" in the current +# directory. +# +# The combined log file is read from stdin. Records read +# will be appended to any existing log files. +# +%is_open = (); + +while ($log_line = ) { + # + # Get the first token from the log record; it's the + # identity of the virtual host to which the record + # applies. + # + ($vhost) = split (/\s/, $log_line); + # + # Normalize the virtual host name to all lowercase. + # If it's blank, the request was handled by the default + # server, so supply a default name. This shouldn't + # happen, but caution rocks. + # + $vhost = lc ($vhost) or "access"; + # + # If the log file for this virtual host isn't opened + # yet, do it now. + # + if (! $is_open{$vhost}) { + open $vhost, ">>${vhost}.log" + or die ("Can't open ${vhost}.log"); + $is_open{$vhost} = 1; + } + # + # Strip off the first token (which may be null in the + # case of the default server), and write the edited + # record to the current log file. + # + $log_line =~ s/^\S*\s+//; + printf $vhost "%s", $log_line; +} +exit 0; diff --git a/support/suexec.c b/support/suexec.c new file mode 100644 index 0000000000..13f3fcc134 --- /dev/null +++ b/support/suexec.c @@ -0,0 +1,566 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * suexec.c -- "Wrapper" support program for suEXEC behaviour for Apache + * + *********************************************************************** + * + * NOTE! : DO NOT edit this code!!! Unless you know what you are doing, + * editing this code might open up your system in unexpected + * ways to would-be crackers. Every precaution has been taken + * to make this code as safe as possible; alter it at your own + * risk. + * + *********************************************************************** + * + * + */ + +#include "ap_config.h" +#include +#include +#include + +#include + +#include "suexec.h" + +/* + *********************************************************************** + * There is no initgroups() in QNX, so I believe this is safe :-) + * Use cc -osuexec -3 -O -mf -DQNX suexec.c to compile. + * + * May 17, 1997. + * Igor N. Kovalenko -- infoh@mail.wplus.net + *********************************************************************** + */ + +#if defined(NEED_INITGROUPS) +int initgroups(const char *name, gid_t basegid) +{ +/* QNX and MPE do not appear to support supplementary groups. */ + return 0; +} +#endif + +#if defined(PATH_MAX) +#define AP_MAXPATH PATH_MAX +#elif defined(MAXPATHLEN) +#define AP_MAXPATH MAXPATHLEN +#else +#define AP_MAXPATH 8192 +#endif + +#define AP_ENVBUF 256 + +extern char **environ; +static FILE *log = NULL; + +char *safe_env_lst[] = +{ + "AUTH_TYPE", + "CONTENT_LENGTH", + "CONTENT_TYPE", + "DATE_GMT", + "DATE_LOCAL", + "DOCUMENT_NAME", + "DOCUMENT_PATH_INFO", + "DOCUMENT_ROOT", + "DOCUMENT_URI", + "FILEPATH_INFO", + "GATEWAY_INTERFACE", + "LAST_MODIFIED", + "PATH_INFO", + "PATH_TRANSLATED", + "QUERY_STRING", + "QUERY_STRING_UNESCAPED", + "REMOTE_ADDR", + "REMOTE_HOST", + "REMOTE_IDENT", + "REMOTE_PORT", + "REMOTE_USER", + "REDIRECT_QUERY_STRING", + "REDIRECT_STATUS", + "REDIRECT_URL", + "REQUEST_METHOD", + "REQUEST_URI", + "SCRIPT_FILENAME", + "SCRIPT_NAME", + "SCRIPT_URI", + "SCRIPT_URL", + "SERVER_ADMIN", + "SERVER_NAME", + "SERVER_ADDR", + "SERVER_PORT", + "SERVER_PROTOCOL", + "SERVER_SOFTWARE", + "UNIQUE_ID", + "USER_NAME", + "TZ", + NULL +}; + + +static void err_output(const char *fmt, va_list ap) +{ +#ifdef LOG_EXEC + time_t timevar; + struct tm *lt; + + if (!log) { + if ((log = fopen(LOG_EXEC, "a")) == NULL) { + fprintf(stderr, "failed to open log file\n"); + perror("fopen"); + exit(1); + } + } + + time(&timevar); + lt = localtime(&timevar); + + fprintf(log, "[%d-%.2d-%.2d %.2d:%.2d:%.2d]: ", + lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, + lt->tm_hour, lt->tm_min, lt->tm_sec); + + vfprintf(log, fmt, ap); + + fflush(log); +#endif /* LOG_EXEC */ + return; +} + +static void log_err(const char *fmt,...) +{ +#ifdef LOG_EXEC + va_list ap; + + va_start(ap, fmt); + err_output(fmt, ap); + va_end(ap); +#endif /* LOG_EXEC */ + return; +} + +static void clean_env(void) +{ + char pathbuf[512]; + char **cleanenv; + char **ep; + int cidx = 0; + int idx; + + + if ((cleanenv = (char **) calloc(AP_ENVBUF, sizeof(char *))) == NULL) { + log_err("failed to malloc memory for environment\n"); + exit(120); + } + + sprintf(pathbuf, "PATH=%s", SAFE_PATH); + cleanenv[cidx] = strdup(pathbuf); + cidx++; + + for (ep = environ; *ep && cidx < AP_ENVBUF-1; ep++) { + if (!strncmp(*ep, "HTTP_", 5)) { + cleanenv[cidx] = *ep; + cidx++; + } + else { + for (idx = 0; safe_env_lst[idx]; idx++) { + if (!strncmp(*ep, safe_env_lst[idx], + strlen(safe_env_lst[idx]))) { + cleanenv[cidx] = *ep; + cidx++; + break; + } + } + } + } + + cleanenv[cidx] = NULL; + + environ = cleanenv; +} + +int main(int argc, char *argv[]) +{ + int userdir = 0; /* ~userdir flag */ + uid_t uid; /* user information */ + gid_t gid; /* target group placeholder */ + char *target_uname; /* target user name */ + char *target_gname; /* target group name */ + char *target_homedir; /* target home directory */ + char *actual_uname; /* actual user name */ + char *actual_gname; /* actual group name */ + char *prog; /* name of this program */ + char *cmd; /* command to be executed */ + char cwd[AP_MAXPATH]; /* current working directory */ + char dwd[AP_MAXPATH]; /* docroot working directory */ + struct passwd *pw; /* password entry holder */ + struct group *gr; /* group entry holder */ + struct stat dir_info; /* directory info holder */ + struct stat prg_info; /* program info holder */ + + /* + * If there are a proper number of arguments, set + * all of them to variables. Otherwise, error out. + */ + prog = argv[0]; + if (argc < 4) { + log_err("too few arguments\n"); + exit(101); + } + target_uname = argv[1]; + target_gname = argv[2]; + cmd = argv[3]; + + /* + * Check existence/validity of the UID of the user + * running this program. Error out if invalid. + */ + uid = getuid(); + if ((pw = getpwuid(uid)) == NULL) { + log_err("invalid uid: (%ld)\n", uid); + exit(102); + } + + /* + * Check to see if the user running this program + * is the user allowed to do so as defined in + * suexec.h. If not the allowed user, error out. + */ +#ifdef _OSD_POSIX + /* User name comparisons are case insensitive on BS2000/OSD */ + if (strcasecmp(HTTPD_USER, pw->pw_name)) { + log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); + exit(103); + } +#else /*_OSD_POSIX*/ + if (strcmp(HTTPD_USER, pw->pw_name)) { + log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER); + exit(103); + } +#endif /*_OSD_POSIX*/ + + /* + * Check for a leading '/' (absolute path) in the command to be executed, + * or attempts to back up out of the current directory, + * to protect against attacks. If any are + * found, error out. Naughty naughty crackers. + */ + if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3)) + || (strstr(cmd, "/../") != NULL)) { + log_err("invalid command (%s)\n", cmd); + exit(104); + } + + /* + * Check to see if this is a ~userdir request. If + * so, set the flag, and remove the '~' from the + * target username. + */ + if (!strncmp("~", target_uname, 1)) { + target_uname++; + userdir = 1; + } + + /* + * Error out if the target username is invalid. + */ + if ((pw = getpwnam(target_uname)) == NULL) { + log_err("invalid target user name: (%s)\n", target_uname); + exit(105); + } + + /* + * Error out if the target group name is invalid. + */ + if (strspn(target_gname, "1234567890") != strlen(target_gname)) { + if ((gr = getgrnam(target_gname)) == NULL) { + log_err("invalid target group name: (%s)\n", target_gname); + exit(106); + } + gid = gr->gr_gid; + actual_gname = strdup(gr->gr_name); + } + else { + gid = atoi(target_gname); + actual_gname = strdup(target_gname); + } + +#ifdef _OSD_POSIX + /* + * Initialize BS2000 user environment + */ + { + pid_t pid; + int status; + + switch (pid = ufork(target_uname)) + { + case -1: /* Error */ + log_err("failed to setup bs2000 environment for user %s: %s\n", + target_uname, strerror(errno)); + exit(150); + case 0: /* Child */ + break; + default: /* Father */ + while (pid != waitpid(pid, &status, 0)) + ; + /* @@@ FIXME: should we deal with STOP signals as well? */ + if (WIFSIGNALED(status)) + kill (getpid(), WTERMSIG(status)); + exit(WEXITSTATUS(status)); + } + } +#endif /*_OSD_POSIX*/ + + /* + * Save these for later since initgroups will hose the struct + */ + uid = pw->pw_uid; + actual_uname = strdup(pw->pw_name); + target_homedir = strdup(pw->pw_dir); + + /* + * Log the transaction here to be sure we have an open log + * before we setuid(). + */ + log_err("uid: (%s/%s) gid: (%s/%s) cmd: %s\n", + target_uname, actual_uname, + target_gname, actual_gname, + cmd); + + /* + * Error out if attempt is made to execute as root or as + * a UID less than UID_MIN. Tsk tsk. + */ + if ((uid == 0) || (uid < UID_MIN)) { + log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd); + exit(107); + } + + /* + * Error out if attempt is made to execute as root group + * or as a GID less than GID_MIN. Tsk tsk. + */ + if ((gid == 0) || (gid < GID_MIN)) { + log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd); + exit(108); + } + + /* + * Change UID/GID here so that the following tests work over NFS. + * + * Initialize the group access list for the target user, + * and setgid() to the target group. If unsuccessful, error out. + */ + if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) { + log_err("failed to setgid (%ld: %s)\n", gid, cmd); + exit(109); + } + + /* + * setuid() to the target user. Error out on fail. + */ + if ((setuid(uid)) != 0) { + log_err("failed to setuid (%ld: %s)\n", uid, cmd); + exit(110); + } + + /* + * Get the current working directory, as well as the proper + * document root (dependant upon whether or not it is a + * ~userdir request). Error out if we cannot get either one, + * or if the current working directory is not in the docroot. + * Use chdir()s and getcwd()s to avoid problems with symlinked + * directories. Yuck. + */ + if (getcwd(cwd, AP_MAXPATH) == NULL) { + log_err("cannot get current working directory\n"); + exit(111); + } + + if (userdir) { + if (((chdir(target_homedir)) != 0) || + ((chdir(USERDIR_SUFFIX)) != 0) || + ((getcwd(dwd, AP_MAXPATH)) == NULL) || + ((chdir(cwd)) != 0)) { + log_err("cannot get docroot information (%s)\n", target_homedir); + exit(112); + } + } + else { + if (((chdir(DOC_ROOT)) != 0) || + ((getcwd(dwd, AP_MAXPATH)) == NULL) || + ((chdir(cwd)) != 0)) { + log_err("cannot get docroot information (%s)\n", DOC_ROOT); + exit(113); + } + } + + if ((strncmp(cwd, dwd, strlen(dwd))) != 0) { + log_err("command not in docroot (%s/%s)\n", cwd, cmd); + exit(114); + } + + /* + * Stat the cwd and verify it is a directory, or error out. + */ + if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) { + log_err("cannot stat directory: (%s)\n", cwd); + exit(115); + } + + /* + * Error out if cwd is writable by others. + */ + if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) { + log_err("directory is writable by others: (%s)\n", cwd); + exit(116); + } + + /* + * Error out if we cannot stat the program. + */ + if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) { + log_err("cannot stat program: (%s)\n", cmd); + exit(117); + } + + /* + * Error out if the program is writable by others. + */ + if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) { + log_err("file is writable by others: (%s/%s)\n", cwd, cmd); + exit(118); + } + + /* + * Error out if the file is setuid or setgid. + */ + if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) { + log_err("file is either setuid or setgid: (%s/%s)\n", cwd, cmd); + exit(119); + } + + /* + * Error out if the target name/group is different from + * the name/group of the cwd or the program. + */ + if ((uid != dir_info.st_uid) || + (gid != dir_info.st_gid) || + (uid != prg_info.st_uid) || + (gid != prg_info.st_gid)) { + log_err("target uid/gid (%ld/%ld) mismatch " + "with directory (%ld/%ld) or program (%ld/%ld)\n", + uid, gid, + dir_info.st_uid, dir_info.st_gid, + prg_info.st_uid, prg_info.st_gid); + exit(120); + } + /* + * Error out if the program is not executable for the user. + * Otherwise, she won't find any error in the logs except for + * "[error] Premature end of script headers: ..." + */ + if (!(prg_info.st_mode & S_IXUSR)) { + log_err("file has no execute permission: (%s/%s)\n", cwd, cmd); + exit(121); + } + + clean_env(); + + /* + * Be sure to close the log file so the CGI can't + * mess with it. If the exec fails, it will be reopened + * automatically when log_err is called. Note that the log + * might not actually be open if LOG_EXEC isn't defined. + * However, the "log" cell isn't ifdef'd so let's be defensive + * and assume someone might have done something with it + * outside an ifdef'd LOG_EXEC block. + */ + if (log != NULL) { + fclose(log); + log = NULL; + } + + /* + * Execute the command, replacing our image with its own. + */ +#ifdef NEED_HASHBANG_EMUL + /* We need the #! emulation when we want to execute scripts */ + { + extern char **environ; + + ap_execve(cmd, &argv[3], environ); + } +#else /*NEED_HASHBANG_EMUL*/ + execv(cmd, &argv[3]); +#endif /*NEED_HASHBANG_EMUL*/ + + /* + * (I can't help myself...sorry.) + * + * Uh oh. Still here. Where's the kaboom? There was supposed to be an + * EARTH-shattering kaboom! + * + * Oh well, log the failure and error out. + */ + log_err("(%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd); + exit(255); +} diff --git a/support/suexec.h b/support/suexec.h new file mode 100644 index 0000000000..892badd1e0 --- /dev/null +++ b/support/suexec.h @@ -0,0 +1,143 @@ +/* ==================================================================== + * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * 4. The names "Apache Server" and "Apache Group" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the Apache Group + * for use in the Apache HTTP server project (http://www.apache.org/)." + * + * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Group and was originally based + * on public domain software written at the National Center for + * Supercomputing Applications, University of Illinois, Urbana-Champaign. + * For more information on the Apache Group and the Apache HTTP server + * project, please see . + * + */ + +/* + * suexec.h -- user-definable variables for the suexec wrapper code. + * (See README.configure on how to customize these variables.) + */ + + +#ifndef _SUEXEC_H +#define _SUEXEC_H + +/* + * HTTPD_USER -- Define as the username under which Apache normally + * runs. This is the only user allowed to execute + * this program. + */ +#ifndef HTTPD_USER +#define HTTPD_USER "www" +#endif + +/* + * UID_MIN -- Define this as the lowest UID allowed to be a target user + * for suEXEC. For most systems, 500 or 100 is common. + */ +#ifndef UID_MIN +#define UID_MIN 100 +#endif + +/* + * GID_MIN -- Define this as the lowest GID allowed to be a target group + * for suEXEC. For most systems, 100 is common. + */ +#ifndef GID_MIN +#define GID_MIN 100 +#endif + +/* + * USERDIR_SUFFIX -- Define to be the subdirectory under users' + * home directories where suEXEC access should + * be allowed. All executables under this directory + * will be executable by suEXEC as the user so + * they should be "safe" programs. If you are + * using a "simple" UserDir directive (ie. one + * without a "*" in it) this should be set to + * the same value. suEXEC will not work properly + * in cases where the UserDir directive points to + * a location that is not the same as the user's + * home directory as referenced in the passwd file. + * + * If you have VirtualHosts with a different + * UserDir for each, you will need to define them to + * all reside in one parent directory; then name that + * parent directory here. IF THIS IS NOT DEFINED + * PROPERLY, ~USERDIR CGI REQUESTS WILL NOT WORK! + * See the suEXEC documentation for more detailed + * information. + */ +#ifndef USERDIR_SUFFIX +#define USERDIR_SUFFIX "public_html" +#endif + +/* + * LOG_EXEC -- Define this as a filename if you want all suEXEC + * transactions and errors logged for auditing and + * debugging purposes. + */ +#ifndef LOG_EXEC +#define LOG_EXEC "/usr/local/apache/logs/cgi.log" /* Need me? */ +#endif + +/* + * DOC_ROOT -- Define as the DocumentRoot set for Apache. This + * will be the only hierarchy (aside from UserDirs) + * that can be used for suEXEC behavior. + */ +#ifndef DOC_ROOT +#define DOC_ROOT "/usr/local/apache/htdocs" +#endif + +/* + * SAFE_PATH -- Define a safe PATH environment to pass to CGI executables. + * + */ +#ifndef SAFE_PATH +#define SAFE_PATH "/usr/local/bin:/usr/bin:/bin" +#endif + +#endif /* _SUEXEC_H */ diff --git a/test/.cvsignore b/test/.cvsignore new file mode 100644 index 0000000000..e2244a438e --- /dev/null +++ b/test/.cvsignore @@ -0,0 +1,11 @@ +a.out +time-FCNTL +time-FLOCK +time-SYSVSEM +time-SYSVSEM2 +time-PTHREAD +time-USLOCK +zb +test-writev +test_date +test_select diff --git a/test/.indent.pro b/test/.indent.pro new file mode 100644 index 0000000000..a9fbe9f9a1 --- /dev/null +++ b/test/.indent.pro @@ -0,0 +1,54 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1 +-TBUFF +-TFILE +-TTRANS +-TUINT4 +-T_trans +-Tallow_options_t +-Tapache_sfio +-Tarray_header +-Tbool_int +-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 diff --git a/test/README b/test/README new file mode 100644 index 0000000000..9f8be502b8 --- /dev/null +++ b/test/README @@ -0,0 +1,3 @@ +This directory contains useful test code for testing various bits +of Apache functionality. This stuff is for the developers only, +so we might remove it on public releases. diff --git a/test/check_chunked b/test/check_chunked new file mode 100644 index 0000000000..6a12167211 --- /dev/null +++ b/test/check_chunked @@ -0,0 +1,42 @@ +#!/usr/bin/perl -w + +# This is meant to be used on the raw output of an HTTP/1.1 connection +# to check that the chunks are all correctly laid out. It's easiest +# to use a tool like netcat to generate the output. This script +# *insists* that \r exist in the output. +# +# You can find netcat at avian.org:/src/hacks/nc110.tgz. + +use strict; + +my $is_chunked = 0; + +# must toss headers +while(<>) { + if (/^Transfer-Encoding:\s+chunked/i) { + $is_chunked = 1; + } + last if ($_ eq "\r\n"); +} + +$is_chunked || die "wasn't chunked\n"; + +for(;;) { + $_ = <> || die "unexpected end of file!\n"; + + m#^([0-9a-f]+) *\r$#i || die "bogus chunklen: $_"; + + my $chunklen = hex($1); + + exit 0 if ($chunklen == 0); + + chop; chop; + print "$_ "; + + my $data = ''; + read(ARGV, $data, $chunklen) == $chunklen || die "short read!\n"; + + $_ = <> || die "unexpected end of file!\n"; + + $_ eq "\r\n" || die "missing chunk trailer!\n"; +} diff --git a/test/cls.c b/test/cls.c new file mode 100644 index 0000000000..91114a784c --- /dev/null +++ b/test/cls.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include + +/* + * Compare a string to a mask + * Mask characters: + * @ - uppercase letter + * # - lowercase letter + * & - hex digit + * # - digit + * * - swallow remaining characters + * - exact match for any other character + */ +static int checkmask(const char *data, const char *mask) +{ + int i, ch, d; + + for (i = 0; mask[i] != '\0' && mask[i] != '*'; i++) { + ch = mask[i]; + d = data[i]; + if (ch == '@') { + if (!isupper(d)) + return 0; + } + else if (ch == '$') { + if (!islower(d)) + return 0; + } + else if (ch == '#') { + if (!isdigit(d)) + return 0; + } + else if (ch == '&') { + if (!isxdigit(d)) + return 0; + } + else if (ch != d) + return 0; + } + + if (mask[i] == '*') + return 1; + else + return (data[i] == '\0'); +} + +/* + * Converts 8 hex digits to a time integer + */ +static int hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i = 0, j = 0; i < 8; i++) { + ch = x[i]; + j <<= 4; + if (isdigit(ch)) + j |= ch - '0'; + else if (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; +} + +int main(int argc, char **argv) +{ + int i, ver; + DIR *d; + struct dirent *e; + const char *s; + FILE *fp; + char path[FILENAME_MAX + 1]; + char line[1035]; + time_t date, lmod, expire; + unsigned int len; + struct tm ts; + char sdate[30], slmod[30], sexpire[30]; + const char time_format[] = "%e %b %Y %R"; + + if (argc != 2) { + printf("Usage: cls directory\n"); + exit(0); + } + + d = opendir(argv[1]); + if (d == NULL) { + perror("opendir"); + exit(1); + } + + for (;;) { + e = readdir(d); + if (e == NULL) + break; + s = e->d_name; + if (s[0] == '.' || s[0] == '#') + continue; + sprintf(path, "%s/%s", argv[1], s); + fp = fopen(path, "r"); + if (fp == NULL) { + perror("fopen"); + continue; + } + if (fgets(line, 1034, fp) == NULL) { + perror("fgets"); + fclose(fp); + continue; + } + if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n")) { + fprintf(stderr, "Bad cache file\n"); + fclose(fp); + continue; + } + date = hex2sec(line); + lmod = hex2sec(line + 9); + expire = hex2sec(line + 18); + ver = hex2sec(line + 27); + len = hex2sec(line + 35); + if (fgets(line, 1034, fp) == NULL) { + perror("fgets"); + fclose(fp); + continue; + } + fclose(fp); + i = strlen(line); + if (strncmp(line, "X-URL: ", 7) != 0 || line[i - 1] != '\n') { + fprintf(stderr, "Bad cache file\n"); + continue; + } + line[i - 1] = '\0'; + if (date != -1) { + ts = *gmtime(&date); + strftime(sdate, 30, time_format, &ts); + } + else + strcpy(sdate, "-"); + + if (lmod != -1) { + ts = *gmtime(&lmod); + strftime(slmod, 30, time_format, &ts); + } + else + strcpy(slmod, "-"); + + if (expire != -1) { + ts = *gmtime(&expire); + strftime(sexpire, 30, time_format, &ts); + } + else + strcpy(sexpire, "-"); + + printf("%s: %d; %s %s %s\n", line + 7, ver, sdate, slmod, sexpire); + } + + closedir(d); + return 0; +} diff --git a/test/tcpdumpscii.txt b/test/tcpdumpscii.txt new file mode 100644 index 0000000000..9c1060edab --- /dev/null +++ b/test/tcpdumpscii.txt @@ -0,0 +1,50 @@ + +From marcs@znep.com Fri Apr 17 15:16:16 1998 +Date: Sat, 22 Nov 1997 20:44:10 -0700 (MST) +From: Marc Slemko +To: TLOSAP +Subject: Re: Getting ethernet packets content under FreeBSD? (fwd) +Reply-To: new-httpd@apache.org + +Anyone too lazy to hack tcpdump (eg. my tcpdump has a -X option to display +the data in ASCII) can use something like the below to grab HTTP headers +when debugging broken clients. + +Nothing complicated, but handy. + +---------- Forwarded message ---------- +Date: Sat, 22 Nov 1997 14:35:23 PST +From: Bill Fenner +To: Nate Williams +Cc: bmah@ca.sandia.gov, hackers@FreeBSD.ORG +Subject: Re: Getting ethernet packets content under FreeBSD? + +I usually just use this perl script, which I call "tcpdumpscii". +Then run "tcpdumpscii -s 1500 -x [other tcpdump args]". + + Bill + +#!/import/misc/bin/perl +# +# +open(TCPDUMP,"tcpdump -l @ARGV|"); +while () { + if (/^\s+(\S\S)+/) { + $sav = $_; + $asc = ""; + while (s/\s*(\S\S)\s*//) { + $i = hex($1); + if ($i < 32 || $i > 126) { + $asc .= "."; + } else { + $asc .= pack(C,hex($1)); + } + } + $foo = "." x length($asc); + $_ = $sav; + s/\t/ /g; + s/^$foo/$asc/; + } + print; +} + diff --git a/test/test-writev.c b/test/test-writev.c new file mode 100644 index 0000000000..852c701766 --- /dev/null +++ b/test/test-writev.c @@ -0,0 +1,85 @@ +/* + test-writev: use this to figure out if your writev() does intelligent + things on the network. Some writev()s when given multiple buffers + will break them up into multiple packets, which is a waste. + + Linux prior to 2.0.31 has this problem. + + Solaris 2.5, 2.5.1 doesn't appear to, 2.6 hasn't been tested. + + IRIX 5.3 doesn't have this problem. + + To use this you want to snoop the wire with tcpdump, and then run + "test-writev a.b.c.d port#" ... against some TCP service on another + box. For example you can run it against port 80 on another server. + You want to look to see how many data packets are sent, you're hoping + only one of size 300 is sent. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INADDR_NONE +#define INADDR_NONE (-1ul) +#endif + +void main( int argc, char **argv ) +{ + struct sockaddr_in server_addr; + int s; + struct iovec vector[3]; + char buf[100]; + int i; + const int just_say_no = 1; + + if( argc != 3 ) { +usage: + fprintf( stderr, "usage: test-writev a.b.c.d port#\n" ); + exit( 1 ); + } + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = inet_addr( argv[1] ); + if( server_addr.sin_addr.s_addr == INADDR_NONE ) { + fprintf( stderr, "bogus address\n" ); + goto usage; + } + server_addr.sin_port = htons( atoi( argv[2] ) ); + + s = socket( AF_INET, SOCK_STREAM, 0 ); + if( s < 0 ) { + perror("socket"); + exit(1); + } + if( connect( s, (struct sockaddr *)&server_addr, sizeof( server_addr ) ) + != 0 ) { + perror("connect"); + exit(1); + } + + if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no, + sizeof(just_say_no)) != 0 ) { + perror( "TCP_NODELAY" ); + exit(1); + } + /* now build up a two part writev and write it out */ + for( i = 0; i < sizeof( buf ); ++i ) { + buf[i] = 'x'; + } + vector[0].iov_base = buf; + vector[0].iov_len = sizeof(buf); + vector[1].iov_base = buf; + vector[1].iov_len = sizeof(buf); + vector[2].iov_base = buf; + vector[2].iov_len = sizeof(buf); + + i = writev( s, &vector[0], 3 ); + fprintf( stdout, "i=%d, errno=%d\n", i, errno ); + exit(0); +} diff --git a/test/test_date.c b/test/test_date.c new file mode 100644 index 0000000000..4beaf7943b --- /dev/null +++ b/test/test_date.c @@ -0,0 +1,180 @@ +/* This program tests the parseHTTPdate routine in ../main/util_date.c. + * + * It is only semiautomated in that I would run it, modify the code to + * use a different algorithm or seed, recompile and run again, etc. + * Obviously it should use an argument for that, but I never got around + * to changing the implementation. + * + * gcc -g -O2 -I../main -o test_date ../main/util_date.o test_date.c + * test_date | egrep '^No ' + * + * Roy Fielding, 1996 + */ +#define API_EXPORT(x) x + +#include +#include +#include "util_date.h" + +static const long year2secs[] = { + 0L, /* 1970 */ + 31536000L, /* 1971 */ + 63072000L, /* 1972 */ + 94694400L, /* 1973 */ + 126230400L, /* 1974 */ + 157766400L, /* 1975 */ + 189302400L, /* 1976 */ + 220924800L, /* 1977 */ + 252460800L, /* 1978 */ + 283996800L, /* 1979 */ + 315532800L, /* 1980 */ + 347155200L, /* 1981 */ + 378691200L, /* 1982 */ + 410227200L, /* 1983 */ + 441763200L, /* 1984 */ + 473385600L, /* 1985 */ + 504921600L, /* 1986 */ + 536457600L, /* 1987 */ + 567993600L, /* 1988 */ + 599616000L, /* 1989 */ + 631152000L, /* 1990 */ + 662688000L, /* 1991 */ + 694224000L, /* 1992 */ + 725846400L, /* 1993 */ + 757382400L, /* 1994 */ + 788918400L, /* 1995 */ + 820454400L, /* 1996 */ + 852076800L, /* 1997 */ + 883612800L, /* 1998 */ + 915148800L, /* 1999 */ + 946684800L, /* 2000 */ + 978307200L, /* 2001 */ + 1009843200L, /* 2002 */ + 1041379200L, /* 2003 */ + 1072915200L, /* 2004 */ + 1104537600L, /* 2005 */ + 1136073600L, /* 2006 */ + 1167609600L, /* 2007 */ + 1199145600L, /* 2008 */ + 1230768000L, /* 2009 */ + 1262304000L, /* 2010 */ + 1293840000L, /* 2011 */ + 1325376000L, /* 2012 */ + 1356998400L, /* 2013 */ + 1388534400L, /* 2014 */ + 1420070400L, /* 2015 */ + 1451606400L, /* 2016 */ + 1483228800L, /* 2017 */ + 1514764800L, /* 2018 */ + 1546300800L, /* 2019 */ + 1577836800L, /* 2020 */ + 1609459200L, /* 2021 */ + 1640995200L, /* 2022 */ + 1672531200L, /* 2023 */ + 1704067200L, /* 2024 */ + 1735689600L, /* 2025 */ + 1767225600L, /* 2026 */ + 1798761600L, /* 2027 */ + 1830297600L, /* 2028 */ + 1861920000L, /* 2029 */ + 1893456000L, /* 2030 */ + 1924992000L, /* 2031 */ + 1956528000L, /* 2032 */ + 1988150400L, /* 2033 */ + 2019686400L, /* 2034 */ + 2051222400L, /* 2035 */ + 2082758400L, /* 2036 */ + 2114380800L, /* 2037 */ + 2145916800L /* 2038 */ +}; + +const char month_snames[12][4] = { + "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +void gm_timestr_822(char *ts, time_t sec) +{ + static const char *const days[7]= + {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + struct tm *tms; + + tms = gmtime(&sec); + + sprintf(ts, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", days[tms->tm_wday], + tms->tm_mday, month_snames[tms->tm_mon], tms->tm_year + 1900, + tms->tm_hour, tms->tm_min, tms->tm_sec); +} + +void gm_timestr_850(char *ts, time_t sec) +{ + static const char *const days[7]= + {"Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + struct tm *tms; + int year; + + tms = gmtime(&sec); + + year = tms->tm_year; + if (year >= 100) year -= 100; + + sprintf(ts, "%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT", days[tms->tm_wday], + tms->tm_mday, month_snames[tms->tm_mon], year, + tms->tm_hour, tms->tm_min, tms->tm_sec); +} + +void gm_timestr_ccc(char *ts, time_t sec) +{ + static const char *const days[7]= + {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + struct tm *tms; + + tms = gmtime(&sec); + + sprintf(ts, "%s %s %2d %.2d:%.2d:%.2d %d", days[tms->tm_wday], + month_snames[tms->tm_mon], tms->tm_mday, + tms->tm_hour, tms->tm_min, tms->tm_sec, tms->tm_year + 1900); +} + +int main (void) +{ + int year, i; + time_t guess; + time_t offset = 0; + /* time_t offset = 0; */ + /* time_t offset = ((31 + 28) * 24 * 3600) - 1; */ + time_t secstodate, newsecs; + char datestr[50]; + + for (year = 1970; year < 2038; ++year) { + secstodate = (time_t)year2secs[year - 1970] + offset; + gm_timestr_822(datestr, secstodate); + newsecs = parseHTTPdate(datestr); + if (secstodate == newsecs) + printf("Yes %4d %11ld %s\n", year, (long)secstodate, datestr); + else if (newsecs == BAD_DATE) + printf("No %4d %11ld %11ld %s\n", year, (long)secstodate, + (long)newsecs, datestr); + else + printf("No* %4d %11ld %11ld %s\n", year, (long)secstodate, + (long)newsecs, datestr); + } + + srand48(978245L); + + for (i = 0; i < 10000; ++i) { + guess = (time_t)mrand48(); + if (guess < 0) guess *= -1; + secstodate = guess + offset; + gm_timestr_822(datestr, secstodate); + newsecs = parseHTTPdate(datestr); + if (secstodate == newsecs) + printf("Yes %11ld %s\n", (long)secstodate, datestr); + else if (newsecs == BAD_DATE) + printf("No %11ld %11ld %s\n", (long)secstodate, + (long)newsecs, datestr); + else + printf("No* %11ld %11ld %s\n", (long)secstodate, + (long)newsecs, datestr); + } + exit(0); +} diff --git a/test/test_find.c b/test/test_find.c new file mode 100644 index 0000000000..6f887f5007 --- /dev/null +++ b/test/test_find.c @@ -0,0 +1,62 @@ +/* This program tests the ap_find_list_item routine in ../main/util.c. + * + * The defines in this sample compile line are specific to Roy's system. + * They should match whatever was used to compile Apache first. + * + gcc -g -O2 -I../os/unix -I../include -o test_find \ + -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \ + ../main/alloc.o ../main/buff.o ../main/util.o \ + ../ap/libap.a -lsocket -lnsl test_find.c + * + * Roy Fielding, 1999 + */ +#include +#include +#include "httpd.h" +#include "alloc.h" + +/* + * Dummy a bunch of stuff just to get a compile + */ +uid_t ap_user_id; +gid_t ap_group_id; +void *ap_dummy_mutex = &ap_dummy_mutex; +char *ap_server_argv0; + +API_EXPORT(void) ap_block_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_unblock_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_log_error(const char *file, int line, int level, + const request_rec *r, const char *fmt, ...) +{ + ; +} + +int main (void) +{ + ap_pool *p; + char line[512]; + char tok[512]; + + p = ap_init_alloc(); + + printf("Enter field value to find items within:\n"); + if (!gets(line)) + exit(0); + + printf("Enter search item:\n"); + while (gets(tok)) { + printf(" [%s] == %s\n", tok, ap_find_list_item(p, line, tok) + ? "Yes" : "No"); + printf("Enter search item:\n"); + } + + exit(0); +} diff --git a/test/test_limits.c b/test/test_limits.c new file mode 100644 index 0000000000..8d76ce2e08 --- /dev/null +++ b/test/test_limits.c @@ -0,0 +1,200 @@ +/************************************************************** + * test_limits.c + * + * A simple program for sending abusive requests to a server, based + * on the sioux.c exploit code that this nimrod posted (see below). + * Roy added options for testing long header fieldsize (-t h), long + * request-lines (-t r), and a long request body (-t b). + * + * FreeBSD 2.2.x, FreeBSD 3.0, IRIX 5.3, IRIX 6.2: + * gcc -o test_limits test_limits.c + * + * Solaris 2.5.1: + * gcc -o test_limits test_limits.c -lsocket -lnsl + * + * + * Message-ID: <861zqspvtw.fsf@niobe.ewox.org> + * Date: Fri, 7 Aug 1998 19:04:27 +0200 + * Sender: Bugtraq List + * From: Dag-Erling Coidan =?ISO-8859-1?Q?Sm=F8rgrav?= + * Subject: YA Apache DoS attack + * + * Copyright (c) 1998 Dag-Erling Codan Smrgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * Kudos to Mark Huizer who originally suggested this on freebsd-current + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define TEST_LONG_REQUEST_LINE 1 +#define TEST_LONG_REQUEST_FIELDS 2 +#define TEST_LONG_REQUEST_FIELDSIZE 3 +#define TEST_LONG_REQUEST_BODY 4 + +void +usage(void) +{ + fprintf(stderr, + "usage: test_limits [-t (r|n|h|b)] [-a address] [-p port] [-n num]\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + struct hostent *he; + FILE *f; + int o, sd; + + /* default parameters */ + char *addr = "localhost"; + int port = 80; + int num = 1000; + int testtype = TEST_LONG_REQUEST_FIELDS; + + /* get options */ + while ((o = getopt(argc, argv, "t:a:p:n:")) != EOF) + switch (o) { + case 't': + if (*optarg == 'r') + testtype = TEST_LONG_REQUEST_LINE; + else if (*optarg == 'n') + testtype = TEST_LONG_REQUEST_FIELDS; + else if (*optarg == 'h') + testtype = TEST_LONG_REQUEST_FIELDSIZE; + else if (*optarg == 'b') + testtype = TEST_LONG_REQUEST_BODY; + break; + case 'a': + addr = optarg; + break; + case 'p': + port = atoi(optarg); + break; + case 'n': + num = atoi(optarg); + break; + default: + usage(); + } + + if (argc != optind) + usage(); + + /* connect */ + if ((he = gethostbyname(addr)) == NULL) { + perror("gethostbyname"); + exit(1); + } + bzero(&sin, sizeof(sin)); + bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length); + sin.sin_family = he->h_addrtype; + sin.sin_port = htons(port); + + if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + perror("socket"); + exit(1); + } + + if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + perror("connect"); + exit(1); + } + + if ((f = fdopen(sd, "r+")) == NULL) { + perror("fdopen"); + exit(1); + } + + /* attack! */ + fprintf(stderr, "Testing like a plague of locusts on %s\n", addr); + + if (testtype == TEST_LONG_REQUEST_LINE) { + fprintf(f, "GET "); + while (num-- && !ferror(f)) { + fprintf(f, "/123456789"); + fflush(f); + } + fprintf(f, " HTTP/1.0\r\n\r\n"); + } + else { + fprintf(f, "GET /fred/foo HTTP/1.0\r\n"); + + if (testtype == TEST_LONG_REQUEST_FIELDSIZE) { + while (num-- && !ferror(f)) { + fprintf(f, "User-Agent: sioux"); + fflush(f); + } + fprintf(f, "\r\n"); + } + else if (testtype == TEST_LONG_REQUEST_FIELDS) { + while (num-- && !ferror(f)) + fprintf(f, "User-Agent: sioux\r\n"); + fprintf(f, "\r\n"); + } + else if (testtype == TEST_LONG_REQUEST_BODY) { + fprintf(f, "User-Agent: sioux\r\n"); + fprintf(f, "Content-Length: 33554433\r\n"); + fprintf(f, "\r\n"); + while (num-- && !ferror(f)) + fprintf(f, "User-Agent: sioux\r\n"); + } + else { + fprintf(f, "\r\n"); + } + } + fflush(f); + + { + ssize_t len; + char buff[512]; + + while ((len = read(sd, buff, 512)) > 0) + len = write(1, buff, len); + } + if (ferror(f)) { + perror("fprintf"); + exit(1); + } + + fclose(f); + exit(0); +} diff --git a/test/test_parser.c b/test/test_parser.c new file mode 100644 index 0000000000..23e2c21c50 --- /dev/null +++ b/test/test_parser.c @@ -0,0 +1,59 @@ +/* This program tests the ap_get_list_item routine in ../main/util.c. + * + * The defines in this sample compile line are specific to Roy's system. + * They should match whatever was used to compile Apache first. + * + gcc -g -O2 -I../os/unix -I../include -o test_parser \ + -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \ + ../main/alloc.o ../main/buff.o ../main/util.o \ + ../ap/libap.a -lsocket -lnsl test_parser.c + * + * Roy Fielding, 1999 + */ +#include +#include +#include "httpd.h" +#include "alloc.h" + +/* + * Dummy a bunch of stuff just to get a compile + */ +uid_t ap_user_id; +gid_t ap_group_id; +void *ap_dummy_mutex = &ap_dummy_mutex; +char *ap_server_argv0; + +API_EXPORT(void) ap_block_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_unblock_alarms(void) +{ + ; +} + +API_EXPORT(void) ap_log_error(const char *file, int line, int level, + const request_rec *r, const char *fmt, ...) +{ + ; +} + +int main (void) +{ + ap_pool *p; + const char *field; + char *newstr; + char instr[512]; + + p = ap_init_alloc(); + + while (gets(instr)) { + printf(" [%s] ==\n", instr); + field = instr; + while ((newstr = ap_get_list_item(p, &field)) != NULL) + printf(" <%s> ..\n", newstr); + } + + exit(0); +} diff --git a/test/test_select.c b/test/test_select.c new file mode 100644 index 0000000000..8c0a6a4363 --- /dev/null +++ b/test/test_select.c @@ -0,0 +1,30 @@ +/* This is just a quick test program to see how long a wait is + * produced by a select loop with an exponential backoff. + * + * gcc -g -O2 -o test_select test_select.c + * test_select + * + * Roy Fielding, 1996 + */ + +#include +#include +#include + +int main (void) +{ + int srv; + long waittime = 4096; + struct timeval tv; + + printf("Start\n"); + while ((waittime > 0) && (waittime < 3000000)) { + printf("%d\n", waittime); + tv.tv_sec = waittime/1000000; + tv.tv_usec = waittime%1000000; + waittime <<= 1; + srv = select(0, NULL, NULL, NULL, &tv); + } + printf("End\n"); + exit(0); +} diff --git a/test/time-sem.c b/test/time-sem.c new file mode 100644 index 0000000000..8bf67c7254 --- /dev/null +++ b/test/time-sem.c @@ -0,0 +1,572 @@ +/* +time-sem.c has the basics of the semaphores we use in http_main.c. It's +intended for timing differences between various methods on an +architecture. In practice we've found many things affect which semaphore +to be used: + + - NFS filesystems absolutely suck for fcntl() and flock() + + - uslock absolutely sucks on single-processor IRIX boxes, but + absolutely rocks on multi-processor boxes. The converse + is true for fcntl. sysvsem seems a moderate balance. + + - Under Solaris you can't have too many processes use SEM_UNDO, there + might be a tuneable somewhere that increases the limit from 29. + We're not sure what the tunable is, so there's a define + NO_SEM_UNDO which can be used to simulate us trapping/blocking + signals to be able to properly release the semaphore on a clean + child death. You'll also need to define NEED_UNION_SEMUN + under solaris. + +You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap() +doesn't work on your system (i.e. linux). + +argv[1] is the #children, argv[2] is the #iterations per child + +You should run each over many different #children inputs, and choose +#iter such that the program runs for at least a second or so... or even +longer depending on your patience. + +compile with: + +gcc -o time-FCNTL -Wall -O time-sem.c -DUSE_FCNTL_SERIALIZED_ACCEPT +gcc -o time-FLOCK -Wall -O time-sem.c -DUSE_FLOCK_SERIALIZED_ACCEPT +gcc -o time-SYSVSEM -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT +gcc -o time-SYSVSEM2 -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT -DNO_SEM_UNDO +gcc -o time-PTHREAD -Wall -O time-sem.c -DUSE_PTHREAD_SERIALIZED_ACCEPT -lpthread +gcc -o time-USLOCK -Wall -O time-sem.c -DUSE_USLOCK_SERIALIZED_ACCEPT + +not all versions work on all systems. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) + +static struct flock lock_it; +static struct flock unlock_it; + +static int fcntl_fd=-1; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void +accept_mutex_init(void) +{ + + lock_it.l_whence = SEEK_SET; /* from current point */ + lock_it.l_start = 0; /* -"- */ + lock_it.l_len = 0; /* until end of file */ + lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ + lock_it.l_pid = 0; /* pid not actually interesting */ + unlock_it.l_whence = SEEK_SET; /* from current point */ + unlock_it.l_start = 0; /* -"- */ + unlock_it.l_len = 0; /* until end of file */ + unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + printf("opening test-lock-thing in current directory\n"); + fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644); + if (fcntl_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing"); + exit (1); + } + unlink("test-lock-thing"); +} + +void accept_mutex_on(void) +{ + int ret; + + while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror ("fcntl lock_it"); + exit(1); + } +} + +void accept_mutex_off(void) +{ + if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0) + { + perror ("fcntl unlock_it"); + exit(1); + } +} + +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT) + +#include + +static int flock_fd=-1; + +#define FNAME "test-lock-thing" + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void accept_mutex_init(void) +{ + + printf("opening " FNAME " in current directory\n"); + flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (flock_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing"); + exit (1); + } +} + +void accept_mutex_child_init(void) +{ + flock_fd = open(FNAME, O_WRONLY, 0600); + if (flock_fd == -1) { + perror("open"); + exit(1); + } +} + +void accept_mutex_cleanup(void) +{ + unlink(FNAME); +} + +void accept_mutex_on(void) +{ + int ret; + + while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror ("flock(LOCK_EX)"); + exit(1); + } +} + +void accept_mutex_off(void) +{ + if (flock (flock_fd, LOCK_UN) < 0) + { + perror ("flock(LOCK_UN)"); + exit(1); + } +} + +#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT) + +#include +#include +#include + +static int sem_id = -1; +#ifdef NO_SEM_UNDO +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; +#endif + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ +#ifdef NEED_UNION_SEMUN + /* believe it or not, you need to define this under solaris */ + union semun { + int val; + struct semid_ds *buf; + ushort *array; + }; +#endif + + union semun ick; + + sem_id = semget(999, 1, IPC_CREAT | 0666); + if (sem_id < 0) { + perror ("semget"); + exit (1); + } + ick.val = 1; + if (semctl(sem_id, 0, SETVAL, ick) < 0) { + perror ("semctl"); + exit(1); + } +#ifdef NO_SEM_UNDO + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); +#endif +} + +void accept_mutex_on() +{ + struct sembuf op; + +#ifdef NO_SEM_UNDO + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + exit (1); + } + op.sem_flg = 0; +#else + op.sem_flg = SEM_UNDO; +#endif + op.sem_num = 0; + op.sem_op = -1; + if (semop(sem_id, &op, 1) < 0) { + perror ("accept_mutex_on"); + exit (1); + } +} + +void accept_mutex_off() +{ + struct sembuf op; + + op.sem_num = 0; + op.sem_op = 1; +#ifdef NO_SEM_UNDO + op.sem_flg = 0; +#else + op.sem_flg = SEM_UNDO; +#endif + if (semop(sem_id, &op, 1) < 0) { + perror ("accept_mutex_off"); + exit (1); + } +#ifdef NO_SEM_UNDO + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + exit (1); + } +#endif +} + +#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT) + +/* note: pthread mutexes aren't released on child death, hence the + * signal goop ... in a real implementation we'd do special things + * during hup, term, usr1. + */ + +#include + +static pthread_mutex_t *mutex; +static sigset_t accept_block_mask; +static sigset_t accept_previous_mask; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ + pthread_mutexattr_t mattr; + int fd; + + fd = open ("/dev/zero", O_RDWR); + if (fd == -1) { + perror ("open(/dev/zero)"); + exit (1); + } + mutex = (pthread_mutex_t *)mmap ((caddr_t)0, sizeof (*mutex), + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (mutex == (void *)(caddr_t)-1) { + perror ("mmap"); + exit (1); + } + close (fd); + if (pthread_mutexattr_init(&mattr)) { + perror ("pthread_mutexattr_init"); + exit (1); + } + if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) { + perror ("pthread_mutexattr_setpshared"); + exit (1); + } + if (pthread_mutex_init(mutex, &mattr)) { + perror ("pthread_mutex_init"); + exit (1); + } + sigfillset(&accept_block_mask); + sigdelset(&accept_block_mask, SIGHUP); + sigdelset(&accept_block_mask, SIGTERM); + sigdelset(&accept_block_mask, SIGUSR1); +} + +void accept_mutex_on() +{ + if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) { + perror("sigprocmask(SIG_BLOCK)"); + exit (1); + } + if (pthread_mutex_lock (mutex)) { + perror ("pthread_mutex_lock"); + exit (1); + } +} + +void accept_mutex_off() +{ + if (pthread_mutex_unlock (mutex)) { + perror ("pthread_mutex_unlock"); + exit (1); + } + if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) { + perror("sigprocmask(SIG_SETMASK)"); + exit (1); + } +} + +#elif defined (USE_USLOCK_SERIALIZED_ACCEPT) + +#include + +static usptr_t *us = NULL; +static ulock_t uslock = NULL; + +#define accept_mutex_child_init() +#define accept_mutex_cleanup() + +void accept_mutex_init(void) +{ + ptrdiff_t old; + /* default is 8 */ +#define CONF_INITUSERS_MAX 15 + if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) { + perror("usconfig"); + exit(-1); + } + if ((us = usinit("/dev/zero")) == NULL) { + perror("usinit"); + exit(-1); + } + if ((uslock = usnewlock(us)) == NULL) { + perror("usnewlock"); + exit(-1); + } +} +void accept_mutex_on() +{ + switch(ussetlock(uslock)) { + case 1: + /* got lock */ + break; + case 0: + fprintf(stderr, "didn't get lock\n"); + exit(-1); + case -1: + perror("ussetlock"); + exit(-1); + } +} +void accept_mutex_off() +{ + if (usunsetlock(uslock) == -1) { + perror("usunsetlock"); + exit(-1); + } +} +#endif + + +#ifndef USE_SHMGET_SCOREBOARD +static void *get_shared_mem(size_t size) +{ + void *result; + + /* allocate shared memory for the shared_counter */ + result = (unsigned long *)mmap ((caddr_t)0, size, + PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); + if (result == (void *)(caddr_t)-1) { + perror ("mmap"); + exit (1); + } + return result; +} +#else +#include +#include +#include + +static void *get_shared_mem(size_t size) +{ + key_t shmkey = IPC_PRIVATE; + int shmid = -1; + void *result; +#ifdef MOVEBREAK + char *obrk; +#endif + + if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) { + perror("shmget"); + exit(1); + } + +#ifdef MOVEBREAK + /* + * Some SysV systems place the shared segment WAY too close + * to the dynamic memory break point (sbrk(0)). This severely + * limits the use of malloc/sbrk in the program since sbrk will + * refuse to move past that point. + * + * To get around this, we move the break point "way up there", + * attach the segment and then move break back down. Ugly + */ + if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) { + perror("sbrk"); + } +#endif + +#define BADSHMAT ((void *)(-1)) + if ((result = shmat(shmid, 0, 0)) == BADSHMAT) { + perror("shmat"); + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + perror("shmctl(IPC_RMID)"); + } + if (result == BADSHMAT) /* now bailout */ + exit(1); + +#ifdef MOVEBREAK + if (obrk == (char *) -1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *) -1) { + perror("sbrk 2"); + } +#endif + return result; +} +#endif + +#ifdef _POSIX_PRIORITY_SCHEDULING +/* don't ask */ +#define _P __P +#include +#define YIELD sched_yield() +#else +#define YIELD do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0) +#endif + +void main (int argc, char **argv) +{ + int num_iter; + int num_child; + int i; + struct timeval first; + struct timeval last; + long ms; + int pid; + unsigned long *shared_counter; + + if (argc != 3) { + fprintf (stderr, "Usage: time-sem num-child num-iter\n"); + exit (1); + } + + num_child = atoi (argv[1]); + num_iter = atoi (argv[2]); + + /* allocate shared memory for the shared_counter */ + shared_counter = get_shared_mem(sizeof(*shared_counter)); + + /* initialize counter to 0 */ + *shared_counter = 0; + + accept_mutex_init (); + + /* parent grabs mutex until done spawning children */ + accept_mutex_on (); + + for (i = 0; i < num_child; ++i) { + pid = fork(); + if (pid == 0) { + /* child, do our thing */ + accept_mutex_child_init(); + for (i = 0; i < num_iter; ++i) { + unsigned long tmp; + + accept_mutex_on (); + tmp = *shared_counter; + YIELD; + *shared_counter = tmp + 1; + accept_mutex_off (); + } + exit (0); + } else if (pid == -1) { + perror ("fork"); + exit (1); + } + } + + /* a quick test to see that nothing is screwed up */ + if (*shared_counter != 0) { + puts ("WTF! shared_counter != 0 before the children have been started!"); + exit (1); + } + + gettimeofday (&first, NULL); + /* launch children into action */ + accept_mutex_off (); + for (i = 0; i < num_child; ++i) { + if (wait(NULL) == -1) { + perror ("wait"); + } + } + gettimeofday (&last, NULL); + + if (*shared_counter != num_child * num_iter) { + printf ("WTF! shared_counter != num_child * num_iter!\n" + "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n", + *shared_counter, + num_child, num_iter); + } + + last.tv_sec -= first.tv_sec; + ms = last.tv_usec - first.tv_usec; + if (ms < 0) { + --last.tv_sec; + ms += 1000000; + } + last.tv_usec = ms; + printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec); + + accept_mutex_cleanup(); + + exit(0); +} + diff --git a/test/zb.c b/test/zb.c new file mode 100644 index 0000000000..0a6666eb25 --- /dev/null +++ b/test/zb.c @@ -0,0 +1,567 @@ + +/* ZeusBench V1.01 + =============== + +This program is Copyright (C) Zeus Technology Limited 1996. + +This program may be used and copied freely providing this copyright notice +is not removed. + +This software is provided "as is" and any express or implied waranties, +including but not limited to, the implied warranties of merchantability and +fitness for a particular purpose are disclaimed. In no event shall +Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, +exemplary, or consequential damaged (including, but not limited to, +procurement of substitute good or services; loss of use, data, or profits; +or business interruption) however caused and on theory of liability. Whether +in contract, strict liability or tort (including negligence or otherwise) +arising in any way out of the use of this software, even if advised of the +possibility of such damage. + + Written by Adam Twiss (adam@zeus.co.uk). March 1996 + +Thanks to the following people for their input: + Mike Belshe (mbelshe@netscape.com) + Michael Campanella (campanella@stevms.enet.dec.com) + +*/ + +/* -------------------- Notes on compiling ------------------------------ + +This should compile unmodified using gcc on HP-UX, FreeBSD, Linux, +IRIX, Solaris, AIX and Digital Unix (OSF). On Solaris 2.x you will +need to compile with "-lnsl -lsocket" options. If you have any +difficulties compiling then let me know. + +On SunOS 4.x.x you may need to compile with -DSUNOS4 to add the following +two lines of code which appear not to exist in my SunOS headers */ + +#ifdef SUNOS4 +extern char *optarg; +extern int optind, opterr, optopt; +#endif + +/* -------------------------------------------------------------------- */ + +/* affects include files on Solaris */ +#define BSD_COMP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ------------------- DEFINITIONS -------------------------- */ + +/* maximum number of requests on a time limited test */ +#define MAX_REQUESTS 50000 + +/* good old state machine */ +#define STATE_UNCONNECTED 0 +#define STATE_CONNECTING 1 +#define STATE_READ 2 + +#define CBUFFSIZE 512 + +struct connection +{ + int fd; + int state; + int read; /* amount of bytes read */ + int bread; /* amount of body read */ + int length; /* Content-Length value used for keep-alive */ + char cbuff[CBUFFSIZE]; /* a buffer to store server response header */ + int cbx; /* offset in cbuffer */ + int keepalive; /* non-zero if a keep-alive request */ + int gotheader; /* non-zero if we have the entire header in cbuff */ + struct timeval start, connect, done; +}; + +struct data +{ + int read; /* number of bytes read */ + int ctime; /* time in ms to connect */ + int time; /* time in ms for connection */ +}; + +#define min(a,b) ((a)<(b))?(a):(b) +#define max(a,b) ((a)>(b))?(a):(b) + +/* --------------------- GLOBALS ---------------------------- */ + +int requests = 1; /* Number of requests to make */ +int concurrency = 1; /* Number of multiple requests to make */ +int tlimit = 0; /* time limit in cs */ +int keepalive = 0; /* try and do keepalive connections */ +char *machine; /* Machine name */ +char *file; /* file name to use */ +char server_name[80]; /* name that server reports */ +int port = 80; /* port to use */ + +int doclen = 0; /* the length the document should be */ +int totalread = 0; /* total number of bytes read */ +int totalbread = 0; /* totoal amount of entity body read */ +int done=0; /* number of requests we have done */ +int doneka=0; /* number of keep alive connections done */ +int good=0, bad=0; /* number of good and bad requests */ + +/* store error cases */ +int err_length = 0, err_conn = 0, err_except = 0; + +struct timeval start, endtime; + +/* global request (and its length) */ +char request[512]; +int reqlen; + +/* one global throw-away buffer to read stuff into */ +char buffer[4096]; + +struct connection *con; /* connection array */ +struct data *stats; /* date for each request */ + +fd_set readbits, writebits; /* bits for select */ +struct sockaddr_in server; /* server addr structure */ + +/* --------------------------------------------------------- */ + +/* simple little function to perror and exit */ + +static void err(char *s) +{ + perror(s); + exit(errno); +} + +/* --------------------------------------------------------- */ + +/* write out request to a connection - assumes we can write + (small) request out in one go into our new socket buffer */ + +void write_request(struct connection *c) +{ + gettimeofday(&c->connect,0); + write(c->fd,request, reqlen); + c->state = STATE_READ; + FD_SET(c->fd, &readbits); + FD_CLR(c->fd, &writebits); +} + +/* --------------------------------------------------------- */ + +/* make an fd non blocking */ + +void nonblock(int fd) +{ + int i=1; + ioctl(fd, FIONBIO, &i); +} + +/* --------------------------------------------------------- */ + +/* returns the time in ms between two timevals */ + +int timedif(struct timeval a, struct timeval b) +{ + register int us,s; + + us = a.tv_usec - b.tv_usec; + us /= 1000; + s = a.tv_sec - b.tv_sec; + s *= 1000; + return s+us; +} + +/* --------------------------------------------------------- */ + +/* calculate and output results and exit */ + +void output_results() +{ + int timetaken; + + gettimeofday(&endtime,0); + timetaken = timedif(endtime, start); + + printf("\n---\n"); + printf("Server: %s\n", server_name); + printf("Document Length: %d\n", doclen); + printf("Concurency Level: %d\n", concurrency); + printf("Time taken for tests: %d.%03d seconds\n", + timetaken/1000, timetaken%1000); + printf("Complete requests: %d\n", done); + printf("Failed requests: %d\n", bad); + if(bad) printf(" (Connect: %d, Length: %d, Exceptions: %d)\n", + err_conn, err_length, err_except); + if(keepalive) printf("Keep-Alive requests: %d\n", doneka); + printf("Bytes transfered: %d\n", totalread); + printf("HTML transfered: %d\n", totalbread); + + /* avoid divide by zero */ + if(timetaken) { + printf("Requests per seconds: %.2f\n", 1000*(float)(done)/timetaken); + printf("Transfer rate: %.2f kb/s\n", + (float)(totalread)/timetaken); + } + + { + /* work out connection times */ + int i; + int totalcon=0, total=0; + int mincon=9999999, mintot=999999; + int maxcon=0, maxtot=0; + + for(i=0; iread = 0; + c->bread = 0; + c->keepalive = 0; + c->cbx = 0; + c->gotheader = 0; + + c->fd = socket(AF_INET, SOCK_STREAM, 0); + if(c->fd<0) err("socket"); + + nonblock(c->fd); + gettimeofday(&c->start,0); + + if(connect(c->fd, (struct sockaddr *) &server, sizeof(server))<0) { + if(errno==EINPROGRESS) { + c->state = STATE_CONNECTING; + FD_SET(c->fd, &writebits); + return; + } + else { + close(c->fd); + err_conn++; + if(bad++>10) { + printf("\nTest aborted after 10 failures\n\n"); + exit(1); + } + start_connect(c); + } + } + + /* connected first time */ + write_request(c); +} + +/* --------------------------------------------------------- */ + +/* close down connection and save stats */ + +void close_connection(struct connection *c) +{ + if(c->read == 0 && c->keepalive) { + /* server has legitiamately shut down an idle keep alive request */ + good--; /* connection never happend */ + } + else { + if(good==1) { + /* first time here */ + doclen = c->bread; + } else if (c->bread!=doclen) { + bad++; + err_length++; + } + + /* save out time */ + if(done < requests) { + struct data s; + gettimeofday(&c->done,0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + } + + close(c->fd); + FD_CLR(c->fd, &readbits); + FD_CLR(c->fd, &writebits); + + /* connect again */ + start_connect(c); + return; +} + +/* --------------------------------------------------------- */ + +/* read data from connection */ + +void read_connection(struct connection *c) +{ + int r; + + r=read(c->fd,buffer,sizeof(buffer)); + if(r==0 || (r<0 && errno!=EAGAIN)) { + good++; + close_connection(c); + return; + } + + if(r<0 && errno==EAGAIN) return; + + c->read += r; + totalread += r; + + if(!c->gotheader) { + char *s; + int l=4; + int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 terminator */ + int tocopy = (spacecbuff+c->cbx, buffer, space); + c->cbx += tocopy; space -= tocopy; + c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */ + s = strstr(c->cbuff, "\r\n\r\n"); + /* this next line is so that we talk to NCSA 1.5 which blatantly breaks + the http specifaction */ + if(!s) { s = strstr(c->cbuff,"\n\n"); l=2; } + + if(!s) { + /* read rest next time */ + if(space) + return; + else { + /* header is in invalid or too big - close connection */ + close(c->fd); + if(bad++>10) { + printf("\nTest aborted after 10 failures\n\n"); + exit(1); + } + FD_CLR(c->fd, &writebits); + start_connect(c); + } + } + else { + /* have full header */ + if(!good) { + /* this is first time, extract some interesting info */ + char *p, *q; + p = strstr(c->cbuff, "Server:"); + q = server_name; + if(p) { p+=8; while(*p>32) *q++ = *p++; } + *q = 0; + } + + c->gotheader = 1; + *s = 0; /* terminate at end of header */ + if(keepalive && + (strstr(c->cbuff, "Keep-Alive") + || strstr(c->cbuff, "keep-alive"))) /* for benefit of MSIIS */ + { + char *cl; + cl = strstr(c->cbuff, "Content-Length:"); + /* for cacky servers like NCSA which break the spec and send a + lower case 'l' */ + if(!cl) cl = strstr(c->cbuff, "Content-length:"); + if(cl) { + c->keepalive=1; + c->length = atoi(cl+16); + } + } + c->bread += c->cbx - (s+l-c->cbuff) + r-tocopy; + totalbread += c->bread; + } + } + else { + /* outside header, everything we have read is entity body */ + c->bread += r; + totalbread += r; + } + + if(c->keepalive && (c->bread >= c->length)) { + /* finished a keep-alive connection */ + good++; doneka++; + /* save out time */ + if(good==1) { + /* first time here */ + doclen = c->bread; + } else if(c->bread!=doclen) { bad++; err_length++; } + if(done < requests) { + struct data s; + gettimeofday(&c->done,0); + s.read = c->read; + s.ctime = timedif(c->connect, c->start); + s.time = timedif(c->done, c->start); + stats[done++] = s; + } + c->keepalive = 0; c->length = 0; c->gotheader=0; c->cbx = 0; + c->read = c->bread = 0; + write_request(c); + c->start = c->connect; /* zero connect time with keep-alive */ + } +} + +/* --------------------------------------------------------- */ + +/* run the tests */ + +int test() +{ + struct timeval timeout, now; + fd_set sel_read, sel_except, sel_write; + int i; + + { + /* get server information */ + struct hostent *he; + he = gethostbyname(machine); + if (!he) err("gethostbyname"); + server.sin_family = he->h_addrtype; + server.sin_port = htons(port); + server.sin_addr.s_addr = ((unsigned long *)(he->h_addr_list[0]))[0]; + } + + con = malloc(concurrency*sizeof(struct connection)); + memset(con,0,concurrency*sizeof(struct connection)); + + stats = malloc(requests * sizeof(struct data)); + + FD_ZERO(&readbits); + FD_ZERO(&writebits); + + /* setup request */ + sprintf(request,"GET %s HTTP/1.0\r\nUser-Agent: ZeusBench/1.0\r\n" + "%sHost: %s\r\nAccept: */*\r\n\r\n", file, + keepalive?"Connection: Keep-Alive\r\n":"", machine ); + + reqlen = strlen(request); + + /* ok - lets start */ + gettimeofday(&start,0); + + /* initialise lots of requests */ + for(i=0; i (tlimit*1000)) { + requests=done; /* so stats are correct */ + output_results(); + } + + /* Timeout of 30 seconds. */ + timeout.tv_sec=30; timeout.tv_usec=0; + n=select(256, &sel_read, &sel_write, &sel_except, &timeout); + if(!n) { + printf("\nServer timed out\n\n"); + exit(1); + } + if(n<1) err("select"); + + for(i=0; i=requests) output_results(); + } + return 0; +} + +/* ------------------------------------------------------- */ + +/* display usage information */ + +void usage(char *progname) { + printf("\nZeusBench v1.0\n\n"); + printf("Usage: %s [-k] [-n requests | -t timelimit (sec)]" + "\n\t\t[-c concurrency] [-p port] \n",progname); + printf("Filename should start with a '/' e.g. /index.html\n\n"); + exit(EINVAL); +} + +/* ------------------------------------------------------- */ + +/* sort out command-line args and call test */ + +int main(int argc, char **argv) { + int c; + if (argc < 3) usage(argv[0]); + + machine = argv[1]; + file = argv[2]; + optind = 3; + while ((c = getopt(argc,argv,"p:n:c:d:t:d:k"))>0) { + switch(c) { + case 'd': + break; + case 'n': + requests = atoi(optarg); + if(!requests) { + printf("Invalid number of requests\n"); + exit(1); + } + break; + case 'k': + keepalive=1; + break; + case 'c': + concurrency = atoi(optarg); + break; + case 'p': + port = atoi(optarg); + break; + case 't': + tlimit = atoi(optarg); + requests = MAX_REQUESTS; /* need to size data array on something */ + break; + default: + usage(argv[0]); + break; + } + } + test(); + return 0; +} + + + + + + -- 2.50.1