diff options
Diffstat (limited to 'usr.sbin')
313 files changed, 77805 insertions, 0 deletions
diff --git a/usr.sbin/httpd/ABOUT_APACHE b/usr.sbin/httpd/ABOUT_APACHE new file mode 100644 index 00000000000..b2e2c6d61f6 --- /dev/null +++ b/usr.sbin/httpd/ABOUT_APACHE @@ -0,0 +1,222 @@ + + The Apache HTTP Server Project + + http://www.apache.org/ + + June 1997 + +The Apache Project is a collaborative software development effort aimed +at creating a robust, commercial-grade, featureful, and freely-available +source code implementation of an HTTP (Web) server. The project is +jointly managed by a group of volunteers located around the world, using +the Internet and the Web to communicate, plan, and develop the server and +its related documentation. These volunteers are known as the Apache Group. +In addition, hundreds of users have contributed ideas, code, and +documentation to the project. This file is intended to briefly describe +the history of the Apache Group, recognize the many contributors, and +explain how you can join the fun too. + +In February of 1995, the most popular server software on the Web was the +public domain HTTP daemon developed by Rob McCool at the National Center +for Supercomputing Applications, University of Illinois, Urbana-Champaign. +However, development of that httpd had stalled after Rob left NCSA in +mid-1994, and many webmasters had developed their own extensions and bug +fixes that were in need of a common distribution. A small group of these +webmasters, contacted via private e-mail, gathered together for the purpose +of coordinating their changes (in the form of "patches"). Brian Behlendorf +and Cliff Skolnick put together a mailing list, shared information space, +and logins for the core developers on a machine in the California Bay Area, +with bandwidth and diskspace donated by HotWired and Organic Online. +By the end of February, eight core contributors formed the foundation +of the original Apache Group: + + Brian Behlendorf Roy T. Fielding Rob Hartill + David Robinson Cliff Skolnick Randy Terbush + Robert S. Thau Andrew Wilson + +with additional contributions from + + Eric Hagberg Frank Peters Nicolas Pioch + +Using NCSA httpd 1.3 as a base, we added all of the published bug fixes +and worthwhile enhancements we could find, tested the result on our own +servers, and made the first official public release (0.6.2) of the Apache +server in April 1995. By coincidence, NCSA restarted their own development +during the same period, and Brandon Long and Beth Frank of the NCSA Server +Development Team joined the list in March as honorary members so that the +two projects could share ideas and fixes. + +The early Apache server was a big hit, but we all knew that the codebase +needed a general overhaul and redesign. During May-June 1995, while +Rob Hartill and the rest of the group focused on implementing new features +for 0.7.x (like pre-forked child processes) and supporting the rapidly growing +Apache user community, Robert Thau designed a new server architecture +(code-named Shambhala) which included a modular structure and API for better +extensibility, pool-based memory allocation, and an adaptive pre-forking +process model. The group switched to this new server base in July and added +the features from 0.7.x, resulting in Apache 0.8.8 (and its brethren) +in August. + +After extensive beta testing, many ports to obscure platforms, a new set +of documentation (by David Robinson), and the addition of many features +in the form of our standard modules, Apache 1.0 was released on +December 1, 1995. + +Less than a year after the group was formed, the Apache server passed +NCSA's httpd as the #1 server on the Internet. + +============================================================================ + +Current Apache Group in alphabetical order as of 1 August 1997: + + Brian Behlendorf Organic Online, California + Ken Coar Process Software Corporation, New England, USA + Mark J. Cox UKWeb, UK + Ralf S. Engelschall Munich, Germany. + Roy T. Fielding UC Irvine, California + Dean Gaudet Steam Tunnel Operations, California + Rob Hartill Internet Movie DB, UK + Jim Jagielski jaguNET ISP, Maryland + Alexei Kosut Stanford University, California + Ben Laurie Freelance Consultant, UK + Chuck Murcko The Topsail Group, Pennsylvania + Aram W. Mirzadeh Qosina Corporation, New York + Sameer Parekh C2Net, California + Paul Sutton UKWeb, UK + Marc Slemko Canada + Randy Terbush Zyzzyva ISP, Nebraska + Dirk-Willem van Gulik Freelance Consultant, Italy + Andrew Wilson Freelance Consultant, UK + +Apache Emeritae (old group members now off doing other things) + + Robert S. Thau MIT, Massachusetts + David Robinson Cambridge University, UK + +Other major contributors + + Rob McCool (original author of the NCSA httpd), + Brandon Long and Beth Frank (NCSA Server Development Team, post-1.3), + Paul Richards (convinced the group to use remote CVS after 1.0), + Kevin Hughes (creator of all those nifty icons), + Henry Spencer (author of the regex library), Garey Smiley (OS/2 port), + Howard Fear (mod_include), Florent Guillaume (language negotiation) + +Many 3rd-party modules, frequently used and recommended, are also +freely-available and linked from the related projects page: +<http://www.zyzzyva.com/module_registry/>, and their authors frequently +contribute ideas, patches, and testing. In particular, Doug MacEachern +(mod_perl) and Rasmus Lerdorf (mod_php). + +Hundreds of people have made individual contributions to the Apache +project. Patch contributors are listed in the src/CHANGES file. +Frequent contributors have included Petr Lampa, Tom Tromey, +James H. Cloos Jr., Ed Korthof, Nathan Neulinger, Jason S. Clary, +Jason A. Dour, Michael Douglass, Tony Sanders, Martin Kraemer, +Brian Tao, Michael Smith, Adam Sussman, Nathan Schrenk, Matthew Gray, +and John Heidemann. + +============================================================================ + +How to join the Apache Group + +There are several levels of contributing. If you just want to send +in an occasional suggestion/fix, then you can just use the bug reporting +form at <http://www.apache.org/bugdb.cgi>. You can also subscribe to the +announcements mailing list (apache-announce@apache.org) which we use to +broadcast information about new releases, bugfixes, and upcoming events. + +If you'd like to become an active member of the Apache Group (the group +of volunteers who vote on changes to the distributed server), then +you need to start by subscribing to the new-httpd@apache.org mailing list. +One warning though: traffic is high, 1000 to 1500 messages/month. +To subscribe to the list, send "subscribe new-httpd" in the body of +a message to <majordomo@apache.org>. We recommend reading the list for +a while before trying to jump in to development. + + NOTE: The developer mailing list (new-httpd@apache.org) is not + a user support forum; it is for people actively working on development + of the server code and documentation, and for planning future + directions. If you have user/configuration questions, send them + to the USENET newsgroup "comp.infosystems.www.servers.unix". + +The Apache Group is a meritocracy -- the more work you have done, the more +you are allowed to do. The group founders set the original rules, but +they can be changed by vote of the active members. There is a core group +of people who have logins on our server (hyperreal.com) and access to the +CVS repository. Everyone has access to the CVS snapshots. Changes to +the code are proposed on the mailing list and usually voted on by active +members -- three +1 (yes votes) and no -1 (no votes, or vetoes) are needed +to commit a code change during a release cycle; docs are usually committed +first and then changed as needed, with conflicts resolved by majority vote. + +Our primary method of communication is our mailing list. Approximately 40 +messages a day flow over the list, and are typically very conversational in +tone. We discuss new features to add, bug fixes, user problems, developments +in the web server community, release dates, etc. The actual code development +takes place on the developers' local machines, with proposed changes +communicated using a patch (output of a context "diff -c3 oldfile newfile" +command), and committed to the source repository by one of the core +developers using remote CVS. + +New members of the Apache Group are added when a frequent contributor is +nominated by one member and unanimously approved by the voting members. +In most cases, this "new" member has been actively contributing to the +group's work for over six months, so it's usually an easy decision. +Anyone on the mailing list can vote on a particular issue, but we only +count those made by active members or people who are known to be experts +on that part of the server. Vetoes must be accompanied by a convincing +explanation. + +The above describes our past and current (as of June 1997) guidelines, +which will probably change over time as the membership of the group +changes and our development/coordination tools improve. + +============================================================================ + +Why Apache Is Free + +Apache exists to provide a robust and commercial-grade reference +implementation of the HTTP protocol. It must remain a platform upon which +individuals and institutions can build reliable systems, both for +experimental purposes and for mission-critical purposes. We believe the +tools of online publishing should be in the hands of everyone, and +software companies should make their money providing value-added services +such as specialized modules and support, amongst other things. We realize +that it is often seen as an economic advantage for one company to "own" a +market - in the software industry that means to control tightly a +particular conduit such that all others must pay. This is typically done +by "owning" the protocols through which companies conduct business, at the +expense of all those other companies. To the extent that the protocols of +the World Wide Web remain "unowned" by a single company, the Web will +remain a level playing field for companies large and small. Thus, +"ownership" of the protocol must be prevented, and the existence of a +robust reference implementation of the protocol, available absolutely for +free to all companies, is a tremendously good thing. + +Furthermore, Apache is an organic entity; those who benefit from it +by using it often contribute back to it by providing feature enhancements, +bug fixes, and support for others in public newsgroups. The amount of +effort expended by any particular individual is usually fairly light, but +the resulting product is made very strong. This kind of community can +only happen with freeware -- when someone pays for software, they usually +aren't willing to fix its bugs. One can argue, then, that Apache's +strength comes from the fact that it's free, and if it were made "not +free" it would suffer tremendously, even if that money were spent on a +real development team. + +We want to see Apache used very widely -- by large companies, small +companies, research institutions, schools, individuals, in the intranet +environment, everywhere -- even though this may mean that companies who +could afford commercial software, and would pay for it without blinking, +might get a "free ride" by using Apache. We would even be happy if some +commercial software companies completely dropped their own HTTP server +development plans and used Apache as a base, with the proper attributions +as described in the LICENSE file. + +Thanks for using Apache! + +============================================================================ +Roy Fielding, June 1997 + +If you are interested in other WWW history, see <http://www.webhistory.org/> diff --git a/usr.sbin/httpd/CHANGES b/usr.sbin/httpd/CHANGES new file mode 100644 index 00000000000..4f36295a07b --- /dev/null +++ b/usr.sbin/httpd/CHANGES @@ -0,0 +1,128 @@ + OVERVIEW OF NEW FEATURES IN APACHE 1.2 + +New features with this release, as extensions of the Apache functionality +For more information, see the documentation included with this release +(htdocs/manual/) or http://www.apache.org/docs/ + +In addition to a number of bug fixes and internal performance +enhancements, Apache 1.2 has the following specific new user +features: + + + *) HTTP/1.1 Compliance + Aside from the optional proxy module (which operates as HTTP/1.0), + Apache is conditionally compliant with the HTTP/1.1 proposed standard, + as approved by the IESG and the IETF HTTP working group. + HTTP/1.1 provides a much-improved protocol, and should allow for + greater performance and efficiency when transferring files. Apache + does, however, still work great with HTTP/1.0 browsers. We are very + close to being unconditionally compliant; if you note any deviance + from the proposed standard, please report it as a bug. + + *) eXtended Server Side Includes (XSSI) + A new set of server-side include directives allows the user to + better create WWW pages. This includes number of powerful new + features, such as the ability to set variables and use conditional + HTML. + + *) File-based and Regex-enabled Directive Sections + The new <Files> section allows directives to be enabled based on + full filename, not just directory and URL. In addition, <Files> + sections can appear in .htaccess files. <Files>, along with + <Directory> and <Location>, can also now be based on regular + expressions, not just simple prefix matching. + + *) Browser-based Environment Variables + Environment variables can now be set based on the User-Agent + string of the browser. Combined with XSSI, this allows you to + write browser-based conditional HTML documents. + + *) SetUID CGI Execution + Apache now supports the execution of CGI scripts as users other + than the server user. A number of security checks are built in to + try and make this as safe as possible. + + *) URL Rewriting Module + The optional mod_rewrite module is now included. This module can + provide powerful URL mapping, using regular expressions. There's + nothing this module can't do! + + *) Enhanced, Configurable Logging + The optional mod_log_config included with earlier versions of + Apache is now standard, and has been enhanced to allow logging of + much more detail about the transaction, and can be used to open + more than one log at once (each of which can have a different log + format). + + *) User Tracking (Cookies) Revisions + The mod_cookies included with previous versions of Apache has been + renamed mod_usertrack, to more accurately reflect its function + (some people inadvertently thought it enabled cookie support in + Apache, which is not true - Apache supports the use of cookies + directly). It is also now possible to disable the generation of + cookies, even when the cookie module is compiled in. Also, an + expiry time can be set on the cookies. + + *) Multiple IPs in <VirtualHost> + The <VirtualHost> directive can now take more than one IP address + or hostname. This lets a single vhost handles requests for + multiple IPs or hostnames. + + *) CGI Debugging Environment + ScriptLog allows you to now set up a log that records all input + and output to failed CGI scripts. This includes environment + variables, input headers, POST data, output, and more. This makes + CGI scripts much easier to debug. + + *) Resource Limits for CGI Scripts + New directives allow the limiting of resources used by CGI scripts + (e.g. max CPU time). This is helpful in preventing 'runaway' CGI + processes. + + *) Redirect Directive Can Return Alternate Status + The Redirect directive can return permanent or temporary redirects, + "Gone" or "See Other" HTTP status. For NCSA-compatibility, + RedirectTemp and RedirectPermanent are also implemented. + + *) Graceful Restarts + Apache can re-read the config files and re-open log files without + terminating transactions in progress. + + *) Simplified Compilation + The process of configuring Apache for compilation has been + simplified. + + *) Add or Remove Options + The Options directive can now add or remove options from those + currently in force, rather than always replacing them. + + *) Command-line Help + The -h command-line option now lists all the available directives. + + *) Optional Headers Module to Set or Remove HTTP Headers + The optional mod_headers module can be used to set custom headers + in the HTTP response. It can append to existing headers, replace + them, or remove headers from the response. + + *) Conditional Config Directives + A new <IfModule> section allows directives to be enabled only if a + given module is loaded into the server. + + *) Authorization Directives Now Use NCSA-style Syntax + The AuthUserFile, AuthGroupFile and AuthDigestFile commands now + have a syntax compatible with the NCSA server. + + *) Optional Proxy Module + An improved FTP, HTTP, and CONNECT mode SSL proxy is included with + Apache 1.2. Some of the changes visible to users: + + - Improved FTP proxy supporting PASV mode + - CONNECT mode ports are configurable from a list + - NoCache * directive for disabling proxy caching + - Numerous bug fixes + + *) Optional Example Module + An example module that demonstrates many of the aspects of the + API is now included with Apache as of version 1.2. It can be + used as a base for those who wish to write their own Apache + modules. diff --git a/usr.sbin/httpd/KEYS b/usr.sbin/httpd/KEYS new file mode 100644 index 00000000000..0158466e091 --- /dev/null +++ b/usr.sbin/httpd/KEYS @@ -0,0 +1,187 @@ +This file contains the PGP keys of various Apache developers. +Please don't use them for email unless you have to. Their main +purpose is code signing. + +Apache users: pgp < KEYS +Apache developers: pgp -kxa <your name> and append it to this file. + + +Type Bits/KeyID Date User ID +pub 1024/2719AF35 1995/05/13 Ben Laurie <ben@algroup.co.uk> + Ben Laurie <ben@gonzo.ben.algroup.co.uk> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.3ia + +mQCNAi+0jQEAAAEEAK7oX0FeNncaHfa1v+V7SMUviAm8qB8orWG0zvja4ZtSrHVg +/PMwppUh44t5ERA9lltRBdHu30+YSh8a1dYt1XOD83nknzj9rhtpFAPqyywlLVhN +VY3PVLyMbULw27aEAGc+StFqrDoUQ0+j9QU/YH/IyVN9rBaJyhsIDEUnGa81AAUR +tB5CZW4gTGF1cmllIDxiZW5AYWxncm91cC5jby51az6JARUDBRAyb2Doc3AsNzyk +Yh0BARa6CACUBnsP9Vb+T/PvNYKVQBIODz+90tz5GozWwCVfPVSaRd8Dz+oF1sFs +YCz/KuxqBhL5PkiCuSMfOVlPA5nirjoktMF/af5saZqhPr5rvr67Z1OzZnVDvWe4 +DhFrn8EoLrY5YNJhUwfINnZqyKaQu8TW6p4caLkTCW0KM+4ztTe74xRG9NeE+K0+ +0RMpAF3jEY36LGRjq6miazt2bVZQDTl6CuWE+gAaFlX2ojV7e1xdxVvpBIEc34MP +g9ORJ0evx1QilMt1VyGcS/pe4IQgjdJqjU/4fzqFZkT2nntQMbV9kQyNe2+qfqP7 +giTryIanmBAfd3oOCTsRz2VKPfdhCqCRiQB1AwUQMRdzEEyr2GZv4ALJAQEuhAL6 +A8I84BR+87uNAHD0ZJkTM73WdyMEGvAKBvrZK/g0VLYj0NtgkSuRJfrXnGkuh27I +ZrjfL952Q/mXgMtHhJHJ9YfenGFWSEDHnolNzKOzTQJpE01IZ3nWv7ezA9N1LZVC +iQCVAgUQMROrdRsIDEUnGa81AQEUNgQAlvyjt534RDQd2AYGoZriaFzjaL7dTCRH +4b1zxuWBNWf3pI4W0iwU02Q5rEWEmY5DLl6/ie+vcQKOWSqXVgnM/s6EARdKEN56 +d6PzkwszgfEybDYrcAxReJcTCcV8ItJer/iqpBLgtaxyUpI77NvKcDGHp6BgYpnv +1lNkH0FISK+JAJUDBRAwtzlWdGx7qH+PTVkBARFWA/99NTCMihlOZS7LmHDVic/q +H1K1DVdMcv0iL39+7Pq4+AA/ET8dWIgcjaIreSqAZTpjwU1pMPaWgecDD1rEMCYX +R+JoofLJ24BLcSlpXJ/gWMifYNxqdDeMRkw/aW/kaXQJWIz+oDYNuOyi5VvB6faF +6Lm7P5cw1mX0I5rYc3woh7QoQmVuIExhdXJpZSA8YmVuQGdvbnpvLmJlbi5hbGdy +b3VwLmNvLnVrPokAlQIFEDEXgCUbCAxFJxmvNQEBiL8D/3MLjfHGvuByqP1VFQrF +QeMNd2aIQuC7ys3lkDvrLkkPJQANua0/MdDaZk6F5pCGcTmmmaJOjcOcCheD7FU5 +w9zxkQGR3Swr3opFHSr/CkEl83jRy3oq1MFydWoGajQjIr/c23X8zr+XntPyO6VX +q5He4RrTiXeAEFBzz+J+R+EQ +=zh1u +-----END PGP PUBLIC KEY BLOCK----- + + +Type bits/keyID Date User ID +pub 1024/A99F75DD 1997/01/24 Ken A L Coar <Coar@DECUS.Org> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.2 + +mQCNAzLpIyUAAAEEAN9KC8CxTeozPYJjsnhFpJ14d4Hhf2M6OTgqPQFRHOswM/3j +B7IW0s+HwVyQ5/SjIlo+8ur9X7yaj1FS2GQmKD1x9LKeHRAoosBIs33okRtoeDRy +ufTaTyQTwLklxClWm3JEef4xZioun1mtWbpz0yVEOCSZcRvtnJrNPMCpn3XdAAUR +tB1LZW4gQSBMIENvYXIgPENvYXJAREVDVVMuT3JnPokAlQMFEDNLrGCazTzAqZ91 +3QEBzwEEAMqamgAftJ8X39dH+slXVZhXAkUDfUNPkDyy7Yd8R+UTCdXXZMRrVSc3 +3nGsd7sycFot+TQ4RWK8g1o/eY0LzgT+hSxsI80BabdnX2hCP8Yq6aqBw9XfHRQU ++zUHA5h150dX9vEUp+Rb8UKPvkqTNz58Cv1HFAHboZ55KMJ+oeTk +=arlJ +-----END PGP PUBLIC KEY BLOCK----- + +Type Bits/KeyID Date User ID +pub 768/A0BB71C1 1997/06/03 Jim Jagielski <jim@jaguNET.com> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.3 + +mQBtAzOUkNMAAAEDANZdTUJQPwrFI9526Qf+DEWL8dXgfhWW8o6CzewdcCoHYEpu +9CiOMD3f9bgo1VozOPceGzCu/9FF2hMLUvVsTAZkzC3rre5TtPo/vOf5HJ+ac9M7 +aqxW+gRu2/90oLtxwQAFEbQfSmltIEphZ2llbHNraSA8amltQGphZ3VORVQuY29t +PokAdQMFEDOUkNRu2/90oLtxwQEB8iEC/i9Qo55TlT8bRpcqeM3lzNDqzU9cqKRf +9X8pGJIVE5m2JPm99qPLs8RPeepLChi8ZZ+2hSfb7ldQhvVLgNqQqLpsjGtJjJOU +C+MrKDeSk2WAicg6Uo0FWCsEHxrssw139A== +=pwim +-----END PGP PUBLIC KEY BLOCK----- + +Type Bits/KeyID Date User ID +pub 2048/DD919C31 1996/12/24 sameer@c2.net + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.3ia + +mQENAzK/QZIAAAEIALrsEjuGlt6wkHy8fx2wPSkH7paAqJHDCbO1W/GMVs41BsH1 +xpyBi9lOtUXHsDC8Obx/TES4/xVPSsFKPQLa9Q/OsxjXmEPBvQ5PZdOXJ5zmRMI1 +1cfUp2s8w6i+IS68IWRKdPMshGWFGar1YUPM1UpVME7U+uGD3wgdC4DrVJHzS5Eh +gEDyQ9FPb+8CpsRO3AvUPzsZGG8Iy/9GiLzmaJG34zZ5fv5X7sr89xiWJ21ehk+X +ePO9kvq+nzfOCCK6a3GZD4g3KJX/Pm3oKeaXeL8WSCCPzpNbtRJk3ofeN7Zm1K0L +yChPiyui+OO063/WASv52bxUIlmzbX82a92RnDEABRG0DXNhbWVlckBjMi5uZXSJ +ARUDBRAyv0GTbX82a92RnDEBAfqVB/9GSzADIVqY0faFOLN6+E3qqg3hPRLBvjgC +5cvTlwT7W64zI+aiSZuN+xAXq+3lnKtmzn45F3hD7gBxRPJbSKsObn2zU4UcqW/o +qoiYEnO9EhoBomwPUbVy8C00CWvDLfeF4L5r+2oXgilTsCojSaWJX0QoPCwRQao1 +YwZ6CqAA78vdbBNkmA0WrPsVqwd3ijgFapcX671AqiT+pDbvK646I6uGPXJzN3ZU +vFuDim9D2uNk9CfvPhKGscr4qqP40TnNn5fjSsmrFyFxYsdwo7I4TFpnsEPOw226 +GU+TR7zdwnByP72AxPEBJ/F22LwNyreuph+fRpWCnCf+9gVW9Heh +=jS5Z +-----END PGP PUBLIC KEY BLOCK----- + +=========================== +Rob Hartill <robh@imdb.com> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.2 + +mQCNAzG6VfMAAAEEAOvtvphFG/D02vGLENBl5OVPgEJgP9E1xhUgKTZnJstv30kD +h1IqeIBkEAy5bpKapCbvvxukyQErhB0efTi2v5yTAlz5pVjgWM5Sa8CyTXJmXPHH +EuOfy1DqaiQSmZ6KWX0ygw3gKDZMiNMf06UURLLYtRlGKSYY3WVj2u2UCmS9AAUR +tB5Sb2JlcnQgSGFydGlsbCA8cm9iaEBpbWRiLmNvbT6JAJUDBRAx5eIAZWPa7ZQK +ZL0BAU2XBACXfopMzC8kW3KEqq+N9W9fkGNgy//8XqQ77FmfPQPbO4X7Zn3cyO46 +MxvPP+92zSyN3dyj/xWZYoRLwll+ync9d4KUFwKw45DALAvz1CKHMOpQPD7dIWdE +9poJQrcbKeOqLcGZTu/hY90gWBUZ++9umR8X8lyh/WEgcUolfgYHew== +=upYh +-----END PGP PUBLIC KEY BLOCK----- + +Type Bits/KeyID Date User ID +pub 1024/631B5749 1996/06/21 Randy Terbush <randy@zyzzyva.com> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.3 + +mQCNAzHLBS8AAAEEANGFXb9o0NPVfVjSLvQh1j3fN6cMeVNA5BGUJ6HZGP/NDxTE +i8hwejJqakkU4ux/g6Kqckrx3h8WR7OXZZ+R8CsA0bg9Sr42ndEQCUISgArg+lXZ +gRUniARPPA7tamTSq8v1mnxqy9s26Ht2rAG2D6IiK/7v0JlezKirDeBjG1dJAAUR +tCFSYW5keSBUZXJidXNoIDxyYW5keUB6eXp6eXZhLmNvbT6JAJUDBRAxywUwqKsN +4GMbV0kBAegnA/sH63WyfwMFmn3nWe8T/5IXO/QkMYoMGLS1i7IxMY9O8BVvKQM+ +oxEcJdFAG7zPZkpgKzTBxmExz5hMZ9hwJ42XhrslWoP7JVvADJcdthrUAYW9W+jx +GcDYAW3qW5DpKsQchfvXq9QOBDxP+Kbbe2B8xGEyGUhLkacISFTrIhhQSg== +=8P8s +-----END PGP PUBLIC KEY BLOCK----- + +Type Bits/KeyID Date User ID +pub 1024/49A563D9 1997/02/24 Mark Cox <mark@ukweb.com> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.3ia + +mQCNAzMRY/IAAAEEAOloTOU0f4w7FDRMM6kA/6XazXxJ/HH8dsmb6E7RuYfVlXsd +kCwxUBOkyW+AYhkHbYUwnB5qBoFUyLrbLGuwKHW1KnAwgbeZLTH5nqQLpA0RLGVZ +v3tzImKUdyyxBphZWC4IeEgUbl9cc+piOsEJ8QzF7gnqwWo/Ku6tTP1JpWPZAAUR +tBlNYXJrIENveCA8bWFya0B1a3dlYi5jb20+iQCVAwUQMxFj8u6tTP1JpWPZAQHz +eQP+N0nQDbPzWeqLssQLyhFkjw5zZByN60j8p25+6JEq7RXgkN1cHtAdH5LMwRAG +fc258f7P9Syp64lH8s4XWYSX5GX8YA8MurOrJmoGFrJs/yxWng8xtxI9tFUnuoIb +HqnD7HCS9Oj1INdyyQuCxZYGHAgxHhpfNTZt+33tMSFIZTQ= +=uIkU +-----END PGP PUBLIC KEY BLOCK----- + +Type Bits/KeyID Date User ID +pub 1024/2F90A69D 1997/02/24 Paul Sutton <paul@ukweb.com> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.3ia + +mQCNAzMRsB0AAAEEAKj2XYYEGcZhT69x4gskQ3xz+KMTLn7gKSqqcyyeinJ0ZjLl +6AJjb1/68nGsF+IIY+IJS+5smq8do1qpC3UZcmw423Sg8F71GeqDO4HZXOAOieVy +rpVs6S5TaXlJOcrC7zZCx+iql97+xJFjUGkkS7j/jIkx1AajzMNkSr0vkKadAAUR +tBxQYXVsIFN1dHRvbiA8cGF1bEB1a3dlYi5jb20+iQCVAwUQMxGwHcNkSr0vkKad +AQGrigP9F43zbiOigYel+JCMiB0HK/UdqSrf3xWxHIKWKNhQNjhnyeF+jKQwFld6 +7KQYsqZIpHsWLWmSk0AmKQOUIw+DxclDxBL2dT4p+CjgTgIAcbvPpahWkBAw/E+c +EGTiYbe+Y3sHJhhP+d0TOLmsETG9tpi7gFZ6FfNcWPxFMdxGrf4= +=0jQW +-----END PGP PUBLIC KEY BLOCK----- + +Type bits/keyID Date User ID +pub 1024/BA20321D 1997/06/05 Chuck Murcko <chuck@topsail.org> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.2 + +mQCNAzOW7moAAAEEAMYZlNOxWCjLR/PosadbG+xsrB2unid2LiYoakTFiDIBaZjx +bu6hNmVZPYfKOXQcqrCu0EY3uVLP/L89bST5pfIZOzz8GTm33zrETgfzpXYyFdbX +eZ5vc6aa3+7zmI7h/aU567P9ruB2C/RBLl1A59wmPRRVvjEIAkI4bAO6IDIdAAUR +tCBDaHVjayBNdXJja28gPGNodWNrQHRvcHNhaWwub3JnPg== +=vUdL +-----END PGP PUBLIC KEY BLOCK----- + +Type Bits/KeyID Date User ID +pub 1024/26BB437D 1997/04/28 Ralf S. Engelschall <rse@engelschall.com> + +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: 2.6.3ia + +mQCNAzNko/QAAAEEANZ2kpN/oMkz4tqzxvKPZws/XwsD0Y+E5/y7P2DIw4uHS/4N +syQbgkdrZhPBlXDv68DQioHXWsb904qyr7iZB1LC5ItK9MgqlK+Z2mvPqsGbHM8J ++oYib8kf2zJ6HvrYrP7NYB0tN9YYum2ICtx+hIi6aKGXdB1ATA5erwYmu0N9AAUR +tClSYWxmIFMuIEVuZ2Vsc2NoYWxsIDxyc2VAZW5nZWxzY2hhbGwuY29tPokAlQMF +EDNko/QOXq8GJrtDfQEBKVoD/2K/+4pcwhxok+FkuLwC5Pnuh/1oeOYHiKYwx0Z3 +p09RLvDtNldr6VD+aL9JltxdPTARzZ8M50UqoF9jMr25GifheFYhilww41OVZA3e +cLXlLgda1+t0vWs3Eg/i2b0arQQDaIq7PeRdjdEDgwnG4xBaqaAqfgxwOXJ+LPWF +hiXZ +=K7lL +-----END PGP PUBLIC KEY BLOCK----- diff --git a/usr.sbin/httpd/LICENSE b/usr.sbin/httpd/LICENSE new file mode 100644 index 00000000000..ec09d302feb --- /dev/null +++ b/usr.sbin/httpd/LICENSE @@ -0,0 +1,54 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + + + diff --git a/usr.sbin/httpd/Makefile b/usr.sbin/httpd/Makefile new file mode 100644 index 00000000000..b2895c9fe25 --- /dev/null +++ b/usr.sbin/httpd/Makefile @@ -0,0 +1,113 @@ +SUBDIR+=src +WWWROOT=/var/www +CONFFILES= \ + conf/srm.conf-dist conf/access.conf-dist conf/httpd.conf-dist \ + conf/mime.types conf/access.conf conf/httpd.conf conf/srm.conf +HTDOCS= \ + htdocs/apache_pb.gif htdocs/index.html htdocs/openbsdpower.gif +CGIFILES= \ + cgi-bin/printenv cgi-bin/test-cgi +MANUALFILES= \ + manual/images/home.gif manual/images/index.gif \ + manual/images/sub.gif manual/misc/API.html \ + manual/misc/known_bugs.html manual/misc/FAQ.html \ + manual/misc/known_client_problems.html \ + manual/misc/client_block_api.html manual/misc/nopgp.html \ + manual/misc/compat_notes.html manual/misc/perf-bsd44.html \ + manual/misc/descriptors.html manual/misc/perf-dec.html \ + manual/misc/fin_wait_2.html manual/misc/perf.html \ + manual/misc/footer.html manual/misc/security_tips.html \ + manual/misc/header.html manual/misc/vif-info.html \ + manual/misc/howto.html manual/misc/windoz_keepalive.html \ + manual/LICENSE manual/host.html manual/multilogs.html \ + manual/TODO manual/new_features_1_0.html \ + manual/bind.html manual/index.html \ + manual/new_features_1_1.html manual/cgi_path.html \ + manual/install.html manual/new_features_1_2.html \ + manual/content-negotiation.html manual/install_1_1.html \ + manual/process-model.html manual/custom-error.html \ + manual/invoking.html manual/stopping.html \ + manual/dns-caveats.html manual/keepalive.html \ + manual/suexec.html manual/env.html manual/location.html \ + manual/unixware.html manual/footer.html \ + manual/man-template.html manual/vhosts-in-depth.html \ + manual/handler.html manual/virtual-host.html \ + manual/header.html manual/mod/core.html \ + manual/mod/mod_auth_msql.html manual/mod/mod_include.html \ + manual/mod/directives.html manual/mod/mod_browser.html \ + manual/mod/mod_info.html manual/mod/footer.html \ + manual/mod/mod_cern_meta.html manual/mod/mod_log_agent.html \ + manual/mod/header.html manual/mod/mod_cgi.html \ + manual/mod/mod_log_common.html manual/mod/index.html \ + manual/mod/mod_cookies.html manual/mod/mod_log_config.html \ + manual/mod/mod_access.html manual/mod/mod_digest.html \ + manual/mod/mod_log_referer.html manual/mod/mod_actions.html \ + manual/mod/mod_dir.html manual/mod/mod_mime.html \ + manual/mod/mod_alias.html manual/mod/mod_dld.html \ + manual/mod/mod_negotiation.html manual/mod/mod_asis.html \ + manual/mod/mod_env.html manual/mod/mod_proxy.html \ + manual/mod/mod_auth.html manual/mod/mod_example.html \ + manual/mod/mod_rewrite.html manual/mod/mod_auth_anon.html \ + manual/mod/mod_expires.html manual/mod/mod_status.html \ + manual/mod/mod_auth_db.html manual/mod/mod_headers.html \ + manual/mod/mod_userdir.html manual/mod/mod_auth_dbm.html \ + manual/mod/mod_imap.html manual/mod/mod_usertrack.html +ICONFILES= \ + icons/README icons/c.gif icons/hand.right.gif icons/pie2.gif \ + icons/sphere1.gif icons/a.gif icons/comp.blue.gif \ + icons/hand.up.gif icons/pie3.gif icons/sphere2.gif \ + icons/alert.black.gif icons/comp.gray.gif icons/icon.sheet.gif \ + icons/pie4.gif icons/tar.gif icons/alert.red.gif \ + icons/compressed.gif icons/image1.gif icons/pie5.gif \ + icons/tex.gif icons/apache_pb.gif icons/continued.gif \ + icons/image2.gif icons/pie6.gif icons/text.gif icons/back.gif \ + icons/dir.gif icons/image3.gif icons/pie7.gif \ + icons/transfer.gif icons/ball.gray.gif icons/down.gif \ + icons/index.gif icons/pie8.gif icons/unknown.gif \ + icons/ball.red.gif icons/dvi.gif icons/layout.gif \ + icons/portal.gif icons/up.gif icons/binary.gif icons/f.gif \ + icons/left.gif icons/ps.gif icons/uu.gif icons/binhex.gif \ + icons/folder.gif icons/link.gif icons/quill.gif \ + icons/uuencoded.gif icons/blank.gif icons/folder.open.gif \ + icons/movie.gif icons/right.gif icons/world1.gif \ + icons/bomb.gif icons/folder.sec.gif icons/p.gif \ + icons/screw1.gif icons/world2.gif icons/box1.gif \ + icons/forward.gif icons/patch.gif icons/screw2.gif \ + icons/box2.gif icons/generic.gif icons/pdf.gif \ + icons/script.gif icons/broken.gif icons/generic.red.gif \ + icons/pie0.gif icons/sound1.gif icons/burst.gif \ + icons/generic.sec.gif icons/pie1.gif icons/sound2.gif + +distribution: + @-for i in ${CONFFILES}; do \ + j=`dirname $$i`; \ + echo "Installing ${DESTDIR}${WWWROOT}/$$i"; \ + ${INSTALL} ${INSTALL_COPY} -g ${BINGRP} -m 444 \ + ${.CURDIR}/$$i ${DESTDIR}${WWWROOT}/$$j/; \ + done + @-for i in ${HTDOCS}; do \ + j=`dirname $$i`; \ + echo "Installing ${DESTDIR}${WWWROOT}/$$i"; \ + ${INSTALL} ${INSTALL_COPY} -g ${BINGRP} -m 444 \ + ${.CURDIR}/$$i ${DESTDIR}${WWWROOT}/$$j/; \ + done + @-for i in ${MANUALFILES}; do \ + j=`dirname $$i`; \ + echo "Installing ${DESTDIR}${WWWROOT}/$$i"; \ + ${INSTALL} ${INSTALL_COPY} -g ${BINGRP} -m 444 \ + ${.CURDIR}/htdocs/$$i ${DESTDIR}${WWWROOT}/htdocs/$$j/; \ + done + @-for i in ${CGIFILES}; do \ + j=`dirname $$i`; \ + echo "Installing ${DESTDIR}${WWWROOT}/$$i"; \ + ${INSTALL} ${INSTALL_COPY} -g ${BINGRP} -m 555 \ + ${.CURDIR}/$$i ${DESTDIR}${WWWROOT}/$$j/; \ + done + @-for i in ${ICONFILES}; do \ + j=`dirname $$i`; \ + echo "Installing ${DESTDIR}${WWWROOT}/$$i"; \ + ${INSTALL} ${INSTALL_COPY} -g ${BINGRP} -m 444 \ + ${.CURDIR}/$$i ${DESTDIR}${WWWROOT}/$$j/; \ + done + +.include<bsd.subdir.mk> diff --git a/usr.sbin/httpd/README b/usr.sbin/httpd/README new file mode 100644 index 00000000000..4757f026d0c --- /dev/null +++ b/usr.sbin/httpd/README @@ -0,0 +1,86 @@ + Apache + Version 1.2 (and up) + +What is it? +----------- + +Apache is an HTTP server designed as a plug-in replacement for the NCSA +server version 1.3 (or 1.4). It fixes numerous bugs in the NCSA server and +includes many frequently requested new features, and has an API which +allows it to be extended to meet users' needs more easily. + +Documentation +------------- + +The documentation available as of the date of this release is also +included, in HTML format, in the htdocs/manual/ directory. For the +most up-to-date documentation, visit us on the WWW, at +<URL:http://www.apache.org/>. + +Installation +------------ + +Unless you grabbed a binary distribution of Apache, you must compile +it for your specific platform. In order to compile it, you must set +compile-time options (in particular, system type) for your system by +editing a Configuration file, run a script which generates a Makefile +and a small piece of C code, and then compile it. + +For instructions on compilation, see the file 'INSTALL' in the src/ directory. + +After compilation, you will have a binary called "httpd" in the src/ +directory. If you received a binary distribution of apache, you +should have this file already. + +The next step is to edit the configuration files for the server. In +the subdirectory called "conf" you should find distribution versions +of the three configuration files: srm.conf-dist, access.conf-dist, and +httpd.conf-dist. Copy them to srm.conf, access.conf, httpd.conf +respectively. + +First edit httpd.conf. This sets up general attributes about the +server - the port number, the user it runs as, etc. Next edit the +srm.conf file - this sets up the root of the document tree, special +functions like server-parsed HTML or internal imagemap parsing, etc. +Finally, edit the access.conf file to at least set the base cases of +access. Documentation for all of these is located at +<URL:http://www.apache.org/docs/>. + +Finally, make a call to httpd, with a -f to the full path to the +httpd.conf file. I.e., the common case: + + /usr/local/etc/apache/src/httpd -f /usr/local/etc/apache/conf/httpd.conf + +And voila! The server should be running. + +By default the srm.conf and access.conf files are located by name - to +specifically call them by other names, use the AccessConfig and +ResourceConfig directives in httpd.conf. + +The Latest Version +------------------ + +Details of the latest version are in the apache project page (above). + +Licensing +--------- + +Please see the file called LICENSE. + +Acknowledgments +---------------- + +We wish to acknowledge the following copyrighted works that make up +portions of the Apache software: + +Portions of this software were developed at the National Center for +Supercomputing Applications at the University of Illinois at +Urbana-Champaign. + +This software contains code derived from the RSA Data Security Inc. MD5 +Message-Digest Algorithm, including various modifications by Spyglass Inc., +Carnegie Mellon University, and Bell Communications Research, Inc. +(Bellcore). + +This package contains software written and copyrighted by Henry Spencer. +Please see the file called src/regex/COPYRIGHT. diff --git a/usr.sbin/httpd/cgi-bin/printenv b/usr.sbin/httpd/cgi-bin/printenv new file mode 100644 index 00000000000..7d389e0ac56 --- /dev/null +++ b/usr.sbin/httpd/cgi-bin/printenv @@ -0,0 +1,7 @@ +#!/usr/local/bin/perl + +print "Content-type: text/html\n\n"; +while (($key, $val) = each %ENV) { + print "$key = $val<BR>\n"; +} + diff --git a/usr.sbin/httpd/cgi-bin/test-cgi b/usr.sbin/httpd/cgi-bin/test-cgi new file mode 100644 index 00000000000..a85631e3aa2 --- /dev/null +++ b/usr.sbin/httpd/cgi-bin/test-cgi @@ -0,0 +1,31 @@ +#!/bin/sh + +# disable filename globbing +set -f + +echo Content-type: text/plain +echo + +echo CGI/1.0 test script report: +echo + +echo argc is $#. argv is "$*". +echo + +echo SERVER_SOFTWARE = $SERVER_SOFTWARE +echo SERVER_NAME = $SERVER_NAME +echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE +echo SERVER_PROTOCOL = $SERVER_PROTOCOL +echo SERVER_PORT = $SERVER_PORT +echo REQUEST_METHOD = $REQUEST_METHOD +echo HTTP_ACCEPT = "$HTTP_ACCEPT" +echo PATH_INFO = "$PATH_INFO" +echo PATH_TRANSLATED = "$PATH_TRANSLATED" +echo SCRIPT_NAME = "$SCRIPT_NAME" +echo QUERY_STRING = "$QUERY_STRING" +echo REMOTE_HOST = $REMOTE_HOST +echo REMOTE_ADDR = $REMOTE_ADDR +echo REMOTE_USER = $REMOTE_USER +echo AUTH_TYPE = $AUTH_TYPE +echo CONTENT_TYPE = $CONTENT_TYPE +echo CONTENT_LENGTH = $CONTENT_LENGTH diff --git a/usr.sbin/httpd/conf/access.conf b/usr.sbin/httpd/conf/access.conf new file mode 100644 index 00000000000..94630fd115c --- /dev/null +++ b/usr.sbin/httpd/conf/access.conf @@ -0,0 +1,70 @@ +# access.conf: Global access configuration +# Online docs at http://www.apache.org/ + +# This file defines server settings which affect which types of services +# are allowed, and in what circumstances. + +# Each directory to which Apache has access, can be configured with respect +# to which services and features are allowed and/or disabled in that +# directory (and its subdirectories). + +# Originally by Rob McCool + +# This should be changed to whatever you set DocumentRoot to. + +<Directory /var/www/htdocs> + +# This may also be "None", "All", or any combination of "Indexes", +# "Includes", "FollowSymLinks", "ExecCGI", or "MultiViews". + +# Note that "MultiViews" must be named *explicitly* --- "Options All" +# doesn't give it to you (or at least, not yet). + +Options Indexes FollowSymLinks + +# This controls which options the .htaccess files in directories can +# override. Can also be "All", or any combination of "Options", "FileInfo", +# "AuthConfig", and "Limit" + +AllowOverride None + +# Controls who can get stuff from this server. + +order allow,deny +allow from all + +</Directory> + +# /usr/local/etc/httpd/cgi-bin should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. + +<Directory /var/www/cgi-bin> +AllowOverride None +Options None +</Directory> + +# Allow server status reports, with the URL of http://servername/server-status +# Change the ".your_domain.com" to match your domain to enable. + +#<Location /server-status> +#SetHandler server-status + +#order deny,allow +#deny from all +#allow from .your_domain.com +#</Location> + +# There have been reports of people trying to abuse an old bug from pre-1.1 +# days. This bug involved a CGI script distributed as a part of Apache. +# By uncommenting these lines you can redirect these attacks to a logging +# script on phf.apache.org. Or, you can record them yourself, using the script +# support/phf_abuse_log.cgi. + +#<Location /cgi-bin/phf*> +#deny from all +#ErrorDocument 403 http://phf.apache.org/phf_abuse_log.cgi +#</Location> + +# You may place any other directories or locations you wish to have +# access information for after this one. + diff --git a/usr.sbin/httpd/conf/access.conf-dist b/usr.sbin/httpd/conf/access.conf-dist new file mode 100644 index 00000000000..8e17b5360f1 --- /dev/null +++ b/usr.sbin/httpd/conf/access.conf-dist @@ -0,0 +1,70 @@ +# access.conf: Global access configuration +# Online docs at http://www.apache.org/ + +# This file defines server settings which affect which types of services +# are allowed, and in what circumstances. + +# Each directory to which Apache has access, can be configured with respect +# to which services and features are allowed and/or disabled in that +# directory (and its subdirectories). + +# Originally by Rob McCool + +# This should be changed to whatever you set DocumentRoot to. + +<Directory /usr/local/etc/httpd/htdocs> + +# This may also be "None", "All", or any combination of "Indexes", +# "Includes", "FollowSymLinks", "ExecCGI", or "MultiViews". + +# Note that "MultiViews" must be named *explicitly* --- "Options All" +# doesn't give it to you (or at least, not yet). + +Options Indexes FollowSymLinks + +# This controls which options the .htaccess files in directories can +# override. Can also be "All", or any combination of "Options", "FileInfo", +# "AuthConfig", and "Limit" + +AllowOverride None + +# Controls who can get stuff from this server. + +order allow,deny +allow from all + +</Directory> + +# /usr/local/etc/httpd/cgi-bin should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. + +<Directory /usr/local/etc/httpd/cgi-bin> +AllowOverride None +Options None +</Directory> + +# Allow server status reports, with the URL of http://servername/server-status +# Change the ".your_domain.com" to match your domain to enable. + +#<Location /server-status> +#SetHandler server-status + +#order deny,allow +#deny from all +#allow from .your_domain.com +#</Location> + +# There have been reports of people trying to abuse an old bug from pre-1.1 +# days. This bug involved a CGI script distributed as a part of Apache. +# By uncommenting these lines you can redirect these attacks to a logging +# script on phf.apache.org. Or, you can record them yourself, using the script +# support/phf_abuse_log.cgi. + +#<Location /cgi-bin/phf*> +#deny from all +#ErrorDocument 403 http://phf.apache.org/phf_abuse_log.cgi +#</Location> + +# You may place any other directories or locations you wish to have +# access information for after this one. + diff --git a/usr.sbin/httpd/conf/httpd.conf b/usr.sbin/httpd/conf/httpd.conf new file mode 100644 index 00000000000..21098ccbc7a --- /dev/null +++ b/usr.sbin/httpd/conf/httpd.conf @@ -0,0 +1,192 @@ +# This is the main server configuration file. See URL http://www.apache.org/ +# for instructions. + +# Do NOT simply read the instructions in here without understanding +# what they do, if you are unsure consult the online docs. You have been +# warned. + +# Originally by Rob McCool + +# ServerType is either inetd, or standalone. + +ServerType standalone + +# If you are running from inetd, go to "ServerAdmin". + +# Port: The port the standalone listens to. For ports < 1023, you will +# need httpd to be run as root initially. + +Port 80 + +# HostnameLookups: Log the names of clients or just their IP numbers +# e.g. www.apache.org (on) or 204.62.129.132 (off) +# You should probably turn this off unless you are going to actually +# use the information in your logs, or with a CGI. Leaving this on +# can slow down access to your site. +HostnameLookups on + +# If you wish httpd to run as a different user or group, you must run +# httpd as root initially and it will switch. + +# User/Group: The name (or #number) of the user/group to run httpd as. +# On SCO (ODT 3) use User nouser and Group nogroup +# On HPUX you may not be able to use shared memory as nobody, and the +# suggested workaround is to create a user www and use that user. +User nobody +Group #-1 + +# The following directive disables keepalives and HTTP header flushes for +# Netscape 2.x and browsers which spoof it. There are known problems with +# these + +BrowserMatch Mozilla/2 nokeepalive + +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. + +ServerAdmin you@your.address + +# ServerRoot: The directory the server's config, error, and log files +# are kept in +# NOTE! If you intend to place this on a NFS (or otherwise network) +# mounted filesystem then please read the LockFile documentation, +# you will save yourself a lot of trouble. + +ServerRoot /var/www + +# BindAddress: You can support virtual hosts with this option. This option +# is used to tell the server which IP address to listen to. It can either +# contain "*", an IP address, or a fully qualified Internet domain name. +# See also the VirtualHost directive. + +#BindAddress * + +# ErrorLog: The location of the error log file. If this does not start +# with /, ServerRoot is prepended to it. + +ErrorLog logs/error_log + +# TransferLog: The location of the transfer log file. If this does not +# start with /, ServerRoot is prepended to it. + +TransferLog logs/access_log + +# PidFile: The file the server should log its pid to +PidFile logs/httpd.pid + +# ScoreBoardFile: File used to store internal server process information. +# Not all architectures require this. But if yours does (you'll know because +# this file is created when you run Apache) then you *must* ensure that +# no two invocations of Apache share the same scoreboard file. +ScoreBoardFile logs/apache_status + +# ServerName allows you to set a host name which is sent back to clients for +# your server if it's different than the one the program would get (i.e. use +# "www" instead of the host's real name). +# +# Note: You cannot just invent host names and hope they work. The name you +# define here must be a valid DNS name for your host. If you don't understand +# this, ask your network administrator. + +#ServerName new.host.name + +# CacheNegotiatedDocs: By default, Apache sends Pragma: no-cache with each +# document that was negotiated on the basis of content. This asks proxy +# servers not to cache the document. Uncommenting the following line disables +# this behavior, and proxies will be allowed to cache the documents. + +#CacheNegotiatedDocs + +# Timeout: The number of seconds before receives and sends time out + +Timeout 300 + +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. + +KeepAlive On + +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We reccomend you leave this number high, for maximum performance. + +MaxKeepAliveRequests 100 + +# KeepAliveTimeout: Number of seconds to wait for the next request + +KeepAliveTimeout 15 + +# Server-pool size regulation. Rather than making you guess how many +# server processes you need, Apache dynamically adapts to the load it +# sees --- that is, it tries to maintain enough server processes to +# handle the current load, plus a few spare servers to handle transient +# load spikes (e.g., multiple simultaneous requests from a single +# Netscape browser). + +# It does this by periodically checking how many servers are waiting +# for a request. If there are fewer than MinSpareServers, it creates +# a new spare. If there are more than MaxSpareServers, some of the +# spares die off. These values are probably OK for most sites --- + +MinSpareServers 5 +MaxSpareServers 10 + +# Number of servers to start --- should be a reasonable ballpark figure. + +StartServers 5 + +# Limit on total number of servers running, i.e., limit on the number +# of clients who can simultaneously connect --- if this limit is ever +# reached, clients will be LOCKED OUT, so it should NOT BE SET TOO LOW. +# It is intended mainly as a brake to keep a runaway server from taking +# Unix with it as it spirals down... + +MaxClients 150 + +# MaxRequestsPerChild: the number of requests each child process is +# allowed to process before the child dies. +# The child will exit so as to avoid problems after prolonged use when +# Apache (and maybe the libraries it uses) leak. On most systems, this +# isn't really needed, but a few (such as Solaris) do have notable leaks +# in the libraries. + +MaxRequestsPerChild 30 + +# Proxy Server directives. Uncomment the following line to +# enable the proxy server: + +#ProxyRequests On + +# To enable the cache as well, edit and uncomment the following lines: + +#CacheRoot /usr/local/etc/httpd/proxy +#CacheSize 5 +#CacheGcInterval 4 +#CacheMaxExpire 24 +#CacheLastModifiedFactor 0.1 +#CacheDefaultExpire 1 +#NoCache a_domain.com another_domain.edu joes.garage_sale.com + +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, in addition to the default. See also the VirtualHost command + +#Listen 3000 +#Listen 12.34.56.78:80 + +# VirtualHost: Allows the daemon to respond to requests for more than one +# server address, if your server machine is configured to accept IP packets +# for multiple addresses. This can be accomplished with the ifconfig +# alias flag, or through kernel patches like VIF. + +# Any httpd.conf or srm.conf directive may go into a VirtualHost command. +# See also the BindAddress entry. + +#<VirtualHost host.some_domain.com> +#ServerAdmin webmaster@host.some_domain.com +#DocumentRoot /www/docs/host.some_domain.com +#ServerName host.some_domain.com +#ErrorLog logs/host.some_domain.com-error_log +#TransferLog logs/host.some_domain.com-access_log +#</VirtualHost> + + diff --git a/usr.sbin/httpd/conf/httpd.conf-dist b/usr.sbin/httpd/conf/httpd.conf-dist new file mode 100644 index 00000000000..fb0c28605ea --- /dev/null +++ b/usr.sbin/httpd/conf/httpd.conf-dist @@ -0,0 +1,192 @@ +# This is the main server configuration file. See URL http://www.apache.org/ +# for instructions. + +# Do NOT simply read the instructions in here without understanding +# what they do, if you are unsure consult the online docs. You have been +# warned. + +# Originally by Rob McCool + +# ServerType is either inetd, or standalone. + +ServerType standalone + +# If you are running from inetd, go to "ServerAdmin". + +# Port: The port the standalone listens to. For ports < 1023, you will +# need httpd to be run as root initially. + +Port 80 + +# HostnameLookups: Log the names of clients or just their IP numbers +# e.g. www.apache.org (on) or 204.62.129.132 (off) +# You should probably turn this off unless you are going to actually +# use the information in your logs, or with a CGI. Leaving this on +# can slow down access to your site. +HostnameLookups on + +# If you wish httpd to run as a different user or group, you must run +# httpd as root initially and it will switch. + +# User/Group: The name (or #number) of the user/group to run httpd as. +# On SCO (ODT 3) use User nouser and Group nogroup +# On HPUX you may not be able to use shared memory as nobody, and the +# suggested workaround is to create a user www and use that user. +User nobody +Group #-1 + +# The following directive disables keepalives and HTTP header flushes for +# Netscape 2.x and browsers which spoof it. There are known problems with +# these + +BrowserMatch Mozilla/2 nokeepalive + +# ServerAdmin: Your address, where problems with the server should be +# e-mailed. + +ServerAdmin you@your.address + +# ServerRoot: The directory the server's config, error, and log files +# are kept in +# NOTE! If you intend to place this on a NFS (or otherwise network) +# mounted filesystem then please read the LockFile documentation, +# you will save yourself a lot of trouble. + +ServerRoot /usr/local/etc/httpd + +# BindAddress: You can support virtual hosts with this option. This option +# is used to tell the server which IP address to listen to. It can either +# contain "*", an IP address, or a fully qualified Internet domain name. +# See also the VirtualHost directive. + +#BindAddress * + +# ErrorLog: The location of the error log file. If this does not start +# with /, ServerRoot is prepended to it. + +ErrorLog logs/error_log + +# TransferLog: The location of the transfer log file. If this does not +# start with /, ServerRoot is prepended to it. + +TransferLog logs/access_log + +# PidFile: The file the server should log its pid to +PidFile logs/httpd.pid + +# ScoreBoardFile: File used to store internal server process information. +# Not all architectures require this. But if yours does (you'll know because +# this file is created when you run Apache) then you *must* ensure that +# no two invocations of Apache share the same scoreboard file. +ScoreBoardFile logs/apache_status + +# ServerName allows you to set a host name which is sent back to clients for +# your server if it's different than the one the program would get (i.e. use +# "www" instead of the host's real name). +# +# Note: You cannot just invent host names and hope they work. The name you +# define here must be a valid DNS name for your host. If you don't understand +# this, ask your network administrator. + +#ServerName new.host.name + +# CacheNegotiatedDocs: By default, Apache sends Pragma: no-cache with each +# document that was negotiated on the basis of content. This asks proxy +# servers not to cache the document. Uncommenting the following line disables +# this behavior, and proxies will be allowed to cache the documents. + +#CacheNegotiatedDocs + +# Timeout: The number of seconds before receives and sends time out + +Timeout 300 + +# KeepAlive: Whether or not to allow persistent connections (more than +# one request per connection). Set to "Off" to deactivate. + +KeepAlive On + +# MaxKeepAliveRequests: The maximum number of requests to allow +# during a persistent connection. Set to 0 to allow an unlimited amount. +# We reccomend you leave this number high, for maximum performance. + +MaxKeepAliveRequests 100 + +# KeepAliveTimeout: Number of seconds to wait for the next request + +KeepAliveTimeout 15 + +# Server-pool size regulation. Rather than making you guess how many +# server processes you need, Apache dynamically adapts to the load it +# sees --- that is, it tries to maintain enough server processes to +# handle the current load, plus a few spare servers to handle transient +# load spikes (e.g., multiple simultaneous requests from a single +# Netscape browser). + +# It does this by periodically checking how many servers are waiting +# for a request. If there are fewer than MinSpareServers, it creates +# a new spare. If there are more than MaxSpareServers, some of the +# spares die off. These values are probably OK for most sites --- + +MinSpareServers 5 +MaxSpareServers 10 + +# Number of servers to start --- should be a reasonable ballpark figure. + +StartServers 5 + +# Limit on total number of servers running, i.e., limit on the number +# of clients who can simultaneously connect --- if this limit is ever +# reached, clients will be LOCKED OUT, so it should NOT BE SET TOO LOW. +# It is intended mainly as a brake to keep a runaway server from taking +# Unix with it as it spirals down... + +MaxClients 150 + +# MaxRequestsPerChild: the number of requests each child process is +# allowed to process before the child dies. +# The child will exit so as to avoid problems after prolonged use when +# Apache (and maybe the libraries it uses) leak. On most systems, this +# isn't really needed, but a few (such as Solaris) do have notable leaks +# in the libraries. + +MaxRequestsPerChild 30 + +# Proxy Server directives. Uncomment the following line to +# enable the proxy server: + +#ProxyRequests On + +# To enable the cache as well, edit and uncomment the following lines: + +#CacheRoot /usr/local/etc/httpd/proxy +#CacheSize 5 +#CacheGcInterval 4 +#CacheMaxExpire 24 +#CacheLastModifiedFactor 0.1 +#CacheDefaultExpire 1 +#NoCache a_domain.com another_domain.edu joes.garage_sale.com + +# Listen: Allows you to bind Apache to specific IP addresses and/or +# ports, in addition to the default. See also the VirtualHost command + +#Listen 3000 +#Listen 12.34.56.78:80 + +# VirtualHost: Allows the daemon to respond to requests for more than one +# server address, if your server machine is configured to accept IP packets +# for multiple addresses. This can be accomplished with the ifconfig +# alias flag, or through kernel patches like VIF. + +# Any httpd.conf or srm.conf directive may go into a VirtualHost command. +# See also the BindAddress entry. + +#<VirtualHost host.some_domain.com> +#ServerAdmin webmaster@host.some_domain.com +#DocumentRoot /www/docs/host.some_domain.com +#ServerName host.some_domain.com +#ErrorLog logs/host.some_domain.com-error_log +#TransferLog logs/host.some_domain.com-access_log +#</VirtualHost> + + diff --git a/usr.sbin/httpd/conf/mime.types b/usr.sbin/httpd/conf/mime.types new file mode 100644 index 00000000000..b662c16b67c --- /dev/null +++ b/usr.sbin/httpd/conf/mime.types @@ -0,0 +1,98 @@ +# This is a comment. I love comments. + +application/activemessage +application/andrew-inset +application/applefile +application/atomicmail +application/dca-rft +application/dec-dx +application/mac-binhex40 hqx +application/mac-compactpro cpt +application/macwriteii +application/msword doc +application/news-message-id +application/news-transmission +application/octet-stream bin dms lha lzh exe class +application/oda oda +application/pdf pdf +application/postscript ai eps ps +application/powerpoint ppt +application/remote-printing +application/rtf rtf +application/slate +application/wita +application/wordperfect5.1 +application/x-bcpio bcpio +application/x-cdlink vcd +application/x-compress +application/x-cpio cpio +application/x-csh csh +application/x-director dcr dir dxr +application/x-dvi dvi +application/x-gtar gtar +application/x-gzip +application/x-hdf hdf +application/x-koan skp skd skt skm +application/x-latex latex +application/x-mif mif +application/x-netcdf nc cdf +application/x-sh sh +application/x-shar shar +application/x-stuffit sit +application/x-sv4cpio sv4cpio +application/x-sv4crc sv4crc +application/x-tar tar +application/x-tcl tcl +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +application/x-troff-man man +application/x-troff-me me +application/x-troff-ms ms +application/x-ustar ustar +application/x-wais-source src +application/zip zip +audio/basic au snd +audio/midi mid midi kar +audio/mpeg mpga mp2 +audio/x-aiff aif aiff aifc +audio/x-pn-realaudio ram +audio/x-pn-realaudio-plugin rpm +audio/x-realaudio ra +audio/x-wav wav +chemical/x-pdb pdb xyz +image/gif gif +image/ief ief +image/jpeg jpeg jpg jpe +image/png png +image/tiff tiff tif +image/x-cmu-raster ras +image/x-portable-anymap pnm +image/x-portable-bitmap pbm +image/x-portable-graymap pgm +image/x-portable-pixmap ppm +image/x-rgb rgb +image/x-xbitmap xbm +image/x-xpixmap xpm +image/x-xwindowdump xwd +message/external-body +message/news +message/partial +message/rfc822 +multipart/alternative +multipart/appledouble +multipart/digest +multipart/mixed +multipart/parallel +text/html html htm +text/plain txt +text/richtext rtx +text/tab-separated-values tsv +text/x-setext etx +text/x-sgml sgml sgm +video/mpeg mpeg mpg mpe +video/quicktime qt mov +video/x-msvideo avi +video/x-sgi-movie movie +x-conference/x-cooltalk ice +x-world/x-vrml wrl vrml diff --git a/usr.sbin/httpd/conf/srm.conf b/usr.sbin/httpd/conf/srm.conf new file mode 100644 index 00000000000..42728f126cb --- /dev/null +++ b/usr.sbin/httpd/conf/srm.conf @@ -0,0 +1,206 @@ +# With this document, you define the name space that users see of your http +# server. This file also defines server settings which affect how requests are +# serviced, and how results should be formatted. + +# See the tutorials at http://www.apache.org/ for +# more information. + +# Originally by Rob McCool; Adapted for Apache + + +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. + +DocumentRoot /var/www/htdocs + +# UserDir: The name of the directory which is appended onto a user's home +# directory if a ~user request is recieved. + +UserDir public_html + +# DirectoryIndex: Name of the file or files to use as a pre-written HTML +# directory index. Separate multiple entries with spaces. + +DirectoryIndex index.html + +# FancyIndexing is whether you want fancy directory indexing or standard + +FancyIndexing on + +# AddIcon tells the server which icon to show for different files or filename +# extensions + +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. + +DefaultIcon /icons/unknown.gif + +# AddDescription allows you to place a short description after a file in +# server-generated indexes. +# Format: AddDescription "description" filename + +# ReadmeName is the name of the README file the server will look for by +# default. Format: ReadmeName name +# +# The server will first look for name.html, include it if found, and it will +# then look for name and include it as plaintext if found. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. + +ReadmeName README +HeaderName HEADER + +# IndexIgnore is a set of filenames which directory indexing should ignore +# Format: IndexIgnore name1 name2... + +IndexIgnore */.??* *~ *# */HEADER* */README* */RCS + +# AccessFileName: The name of the file to look for in each directory +# for access control information. + +AccessFileName .htaccess + +# DefaultType is the default MIME type for documents which the server +# cannot find the type of from filename extensions. + +DefaultType text/plain + +# AddEncoding allows you to have certain browsers (Mosaic/X 2.1+) uncompress +# information on the fly. Note: Not all browsers support this. + +AddEncoding x-compress Z +AddEncoding x-gzip gz + +# AddLanguage allows you to specify the language of a document. You can +# then use content negotiation to give a browser a file in a language +# it can understand. Note that the suffix does not have to be the same +# as the language keyword --- those with documents in Polish (whose +# net-standard language code is pl) may wish to use "AddLanguage pl .po" +# to avoid the ambiguity with the common suffix for perl scripts. + +AddLanguage en .en +AddLanguage fr .fr +AddLanguage de .de +AddLanguage da .da +AddLanguage el .el +AddLanguage it .it + +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# Just list the languages in decreasing order of preference. + +LanguagePriority en fr de + +# Redirect allows you to tell clients about documents which used to exist in +# your server's namespace, but do not anymore. This allows you to tell the +# clients where to look for the relocated document. +# Format: Redirect fakename url + + +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname + +# Note that if you include a trailing / on fakename then the server will +# require it to be present in the URL. So "/icons" isn't aliased in this +# example. + +#Alias /icons/ /var/www/icons/ + +# ScriptAlias: This controls which directories contain server scripts. +# Format: ScriptAlias fakename realname + +ScriptAlias /cgi-bin/ /var/www/cgi-bin/ + +# If you want to use server side includes, or CGI outside +# ScriptAliased directories, uncomment the following lines. + +# AddType allows you to tweak mime.types without actually editing it, or to +# make certain files to be certain types. +# Format: AddType type/subtype ext1 + +# AddHandler allows you to map certain file extensions to "handlers", +# actions unrelated to filetype. These can be either built into the server +# or added with the Action command (see below) +# Format: AddHandler action-name ext1 + +# To use CGI scripts: +#AddHandler cgi-script .cgi + +# To use server-parsed HTML files +#AddType text/html .shtml +#AddHandler server-parsed .shtml + +# Uncomment the following line to enable Apache's send-asis HTTP file +# feature +#AddHandler send-as-is asis + +# If you wish to use server-parsed imagemap files, use +#AddHandler imap-file map + +# To enable type maps, you might want to use +#AddHandler type-map var + +# Action lets you define media types that will execute a script whenever +# a matching file is called. This eliminates the need for repeated URL +# pathnames for oft-used CGI file processors. +# Format: Action media/type /cgi-script/location +# Format: Action handler-name /cgi-script/location + +# MetaDir: specifies the name of the directory in which Apache can find +# meta information files. These files contain additional HTTP headers +# to include when sending the document + +#MetaDir .web + +# MetaSuffix: specifies the file name suffix for the file containing the +# meta information. + +#MetaSuffix .meta + +# Customizable error response (Apache style) +# these come in three flavors +# +# 1) plain text +#ErrorDocument 500 "The server made a boo boo. +# n.b. the (") marks it as text, it does not get output +# +# 2) local redirects +#ErrorDocument 404 /missing.html +# to redirect to local url /missing.html +#ErrorDocument 404 /cgi-bin/missing_handler.pl +# n.b. can redirect to a script or a document using server-side-includes. +# +# 3) external redirects +#ErrorDocument 402 http://some.other_server.com/subscription_info.html +# diff --git a/usr.sbin/httpd/conf/srm.conf-dist b/usr.sbin/httpd/conf/srm.conf-dist new file mode 100644 index 00000000000..40102cd03ce --- /dev/null +++ b/usr.sbin/httpd/conf/srm.conf-dist @@ -0,0 +1,206 @@ +# With this document, you define the name space that users see of your http +# server. This file also defines server settings which affect how requests are +# serviced, and how results should be formatted. + +# See the tutorials at http://www.apache.org/ for +# more information. + +# Originally by Rob McCool; Adapted for Apache + + +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. + +DocumentRoot /usr/local/etc/httpd/htdocs + +# UserDir: The name of the directory which is appended onto a user's home +# directory if a ~user request is recieved. + +UserDir public_html + +# DirectoryIndex: Name of the file or files to use as a pre-written HTML +# directory index. Separate multiple entries with spaces. + +DirectoryIndex index.html + +# FancyIndexing is whether you want fancy directory indexing or standard + +FancyIndexing on + +# AddIcon tells the server which icon to show for different files or filename +# extensions + +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +# DefaultIcon is which icon to show for files which do not have an icon +# explicitly set. + +DefaultIcon /icons/unknown.gif + +# AddDescription allows you to place a short description after a file in +# server-generated indexes. +# Format: AddDescription "description" filename + +# ReadmeName is the name of the README file the server will look for by +# default. Format: ReadmeName name +# +# The server will first look for name.html, include it if found, and it will +# then look for name and include it as plaintext if found. +# +# HeaderName is the name of a file which should be prepended to +# directory indexes. + +ReadmeName README +HeaderName HEADER + +# IndexIgnore is a set of filenames which directory indexing should ignore +# Format: IndexIgnore name1 name2... + +IndexIgnore */.??* *~ *# */HEADER* */README* */RCS + +# AccessFileName: The name of the file to look for in each directory +# for access control information. + +AccessFileName .htaccess + +# DefaultType is the default MIME type for documents which the server +# cannot find the type of from filename extensions. + +DefaultType text/plain + +# AddEncoding allows you to have certain browsers (Mosaic/X 2.1+) uncompress +# information on the fly. Note: Not all browsers support this. + +AddEncoding x-compress Z +AddEncoding x-gzip gz + +# AddLanguage allows you to specify the language of a document. You can +# then use content negotiation to give a browser a file in a language +# it can understand. Note that the suffix does not have to be the same +# as the language keyword --- those with documents in Polish (whose +# net-standard language code is pl) may wish to use "AddLanguage pl .po" +# to avoid the ambiguity with the common suffix for perl scripts. + +AddLanguage en .en +AddLanguage fr .fr +AddLanguage de .de +AddLanguage da .da +AddLanguage el .el +AddLanguage it .it + +# LanguagePriority allows you to give precedence to some languages +# in case of a tie during content negotiation. +# Just list the languages in decreasing order of preference. + +LanguagePriority en fr de + +# Redirect allows you to tell clients about documents which used to exist in +# your server's namespace, but do not anymore. This allows you to tell the +# clients where to look for the relocated document. +# Format: Redirect fakename url + + +# Aliases: Add here as many aliases as you need (with no limit). The format is +# Alias fakename realname + +# Note that if you include a trailing / on fakename then the server will +# require it to be present in the URL. So "/icons" isn't aliased in this +# example. + +#Alias /icons/ /usr/local/etc/httpd/icons/ + +# ScriptAlias: This controls which directories contain server scripts. +# Format: ScriptAlias fakename realname + +#ScriptAlias /cgi-bin/ /usr/local/etc/httpd/cgi-bin/ + +# If you want to use server side includes, or CGI outside +# ScriptAliased directories, uncomment the following lines. + +# AddType allows you to tweak mime.types without actually editing it, or to +# make certain files to be certain types. +# Format: AddType type/subtype ext1 + +# AddHandler allows you to map certain file extensions to "handlers", +# actions unrelated to filetype. These can be either built into the server +# or added with the Action command (see below) +# Format: AddHandler action-name ext1 + +# To use CGI scripts: +#AddHandler cgi-script .cgi + +# To use server-parsed HTML files +#AddType text/html .shtml +#AddHandler server-parsed .shtml + +# Uncomment the following line to enable Apache's send-asis HTTP file +# feature +#AddHandler send-as-is asis + +# If you wish to use server-parsed imagemap files, use +#AddHandler imap-file map + +# To enable type maps, you might want to use +#AddHandler type-map var + +# Action lets you define media types that will execute a script whenever +# a matching file is called. This eliminates the need for repeated URL +# pathnames for oft-used CGI file processors. +# Format: Action media/type /cgi-script/location +# Format: Action handler-name /cgi-script/location + +# MetaDir: specifies the name of the directory in which Apache can find +# meta information files. These files contain additional HTTP headers +# to include when sending the document + +#MetaDir .web + +# MetaSuffix: specifies the file name suffix for the file containing the +# meta information. + +#MetaSuffix .meta + +# Customizable error response (Apache style) +# these come in three flavors +# +# 1) plain text +#ErrorDocument 500 "The server made a boo boo. +# n.b. the (") marks it as text, it does not get output +# +# 2) local redirects +#ErrorDocument 404 /missing.html +# to redirect to local url /missing.html +#ErrorDocument 404 /cgi-bin/missing_handler.pl +# n.b. can redirect to a script or a document using server-side-includes. +# +# 3) external redirects +#ErrorDocument 402 http://some.other_server.com/subscription_info.html +# diff --git a/usr.sbin/httpd/htdocs/apache_pb.gif b/usr.sbin/httpd/htdocs/apache_pb.gif Binary files differnew file mode 100644 index 00000000000..3a1c139fc42 --- /dev/null +++ b/usr.sbin/httpd/htdocs/apache_pb.gif diff --git a/usr.sbin/httpd/htdocs/index.html b/usr.sbin/httpd/htdocs/index.html new file mode 100644 index 00000000000..7671f8ddd82 --- /dev/null +++ b/usr.sbin/httpd/htdocs/index.html @@ -0,0 +1,54 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Test Page for Apache Installation</TITLE> + </HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <H1 ALIGN="CENTER">It Worked!</H1> + <P> + If you can see this, it means that the installation of the + <A + HREF="http://www.apache.org/" + >Apache</A> + software on this system was successful. You may now add content to + this directory and replace this page. + </P> + <HR WIDTH="50%" SIZE="8"> + <BLOCKQUOTE> + If you are seeing this instead of the content you expected, please + <STRONG>contact the administrator of the site involved.</STRONG> If + you send mail about this to the authors of the Apache software, who almost + certainly have nothing to do with this site, your message will be + <STRONG><BIG>ignored</BIG></STRONG>. + </BLOCKQUOTE> + <HR WIDTH="50%" SIZE="8"> + <P> + The Apache + <A + HREF="manual/index.html" + >documentation</A> + has been included with this distribution. + </P> + <P> + You are free to use the image below on an Apache-powered web + server. Thanks for using Apache! + </P> + <P ALIGN="CENTER"> + <IMG SRC="apache_pb.gif" ALT=""> + </P> + <P> + You can also use the image below on an OpenBSD-powered web + server. + </P> + <P ALIGN="CENTER"> + <IMG SRC="openbsdpower.gif" ALT="This Site OpenBSD Powered!"> + </P> + </BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/LICENSE b/usr.sbin/httpd/htdocs/manual/LICENSE new file mode 100644 index 00000000000..ec09d302feb --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/LICENSE @@ -0,0 +1,54 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + + + diff --git a/usr.sbin/httpd/htdocs/manual/TODO b/usr.sbin/httpd/htdocs/manual/TODO new file mode 100644 index 00000000000..975ac8e68ea --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/TODO @@ -0,0 +1,4 @@ +Documentation changes/enhancements needed: + +- Documentation for mod_expires +- Documentation for Satisfy diff --git a/usr.sbin/httpd/htdocs/manual/bind.html b/usr.sbin/httpd/htdocs/manual/bind.html new file mode 100644 index 00000000000..e5fd0362d46 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/bind.html @@ -0,0 +1,121 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>Setting which addresses and ports Apache uses</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Setting which addresses and ports Apache uses</h1> + +<hr> + +When Apache starts, it connects to some port and address on the +local machine and waits for incoming requests. By default, it +listens to all addresses on the machine, and to the port +as specified by the <tt>Port</tt> directive in the server configuration. +However, it can be told to listen to more the one port, or to listen +to only selected addresses, or a combination. This is often combined +with the Virtual Host feature which determines how Apache +responds to different IP addresses, hostnames and ports.<p> + +There are two directives used to restrict or specify which addresses +and ports Apache listens to. + +<ul> +<li><a href="#bindaddress">BindAddress</a> is used to restrict the server to listening to + a single address, and can be used to permit multiple Apache servers + on the same machine listening to different IP addresses. +<li><a href="#listen">Listen</a> can be used to make a single Apache server listen + to more than one address and/or port. +</ul> + +<h3><a name="bindaddress">BindAddress</a></h3> +<strong>Syntax:</strong> BindAddress <em>[ * | IP-address | hostname ]</em><br> +<strong>Default:</strong> <code>BindAddress *</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Core<p> + +Makes the server listen to just the specified address. If the argument +is *, the server listens to all addresses. The port listened to +is set with the <tt>Port</tt> directive. Only one BindAddress +should be used. + +<h3><a name="listen">Listen</a></h3> +<strong>Syntax:</strong> Listen <em>[ port | IP-address:port ]</em><br> +<strong>Default:</strong> <code>none</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Core<p> + +<tt>Listen</tt> can be used instead of <tt>BindAddress</tt> and +<tt>Port</tt>. It tells the server to accept incoming requests on the +specified port or address-and-port combination. If the first format is +used, with a port number only, the server listens to the given port on +all interfaces, instead of the port given by the <tt>Port</tt> +directive. If an IP address is given as well as a port, the server +will listen on the given port and interface. <p> Multiple Listen +directives may be used to specify a number of addresses and ports to +listen to. The server will respond to requests from any of the listed +addresses and ports.<p> + +For example, to make the server accept connections on both port +80 and port 8000, use: +<pre> + Listen 80 + Listen 8000 +</pre> + +To make the server accept connections on two specified +interfaces and port numbers, use +<pre> + Listen 192.170.2.1:80 + Listen 192.170.2.5:8000 +</pre> + +<h2>How this works with Virtual Hosts</h2> + +BindAddress and Listen do not implement Virtual Hosts. They tell the +main server what addresses and ports to listen to. If no +<VirtualHost> directives are used, the server will behave the +same for all accepted requests. However, <VirtualHost> can be +used to specify a different behavior for one or more of the addresses +and ports. To implement a VirtualHost, the server must first be told +to listen to the address and port to be used. Then a +<VirtualHost> section should be created for a specified address +and port to set the behavior of this virtual host. Note that if the +<VirtualHost> is set for an address and port that the server is +not listening to, it cannot be accessed. + +<h2>See also</h2> + +See also the documentation on +<a href="virtual-host.html">Virtual Hosts</a>, +<a href="host.html">Non-IP virtual hosts</a>, +<a href="mod/core.html#bindaddress">BindAddress directive</a>, +<a href="mod/core.html#port">Port directive</a>, +<a href="dns-caveats.html">DNS Issues</a> +and +<a href="mod/core.html#virtualhost"><VirtualHost> section</a>. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/cgi_path.html b/usr.sbin/httpd/htdocs/manual/cgi_path.html new file mode 100644 index 00000000000..f70aed3067c --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/cgi_path.html @@ -0,0 +1,105 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>PATH_INFO Changes in the CGI Environment</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">PATH_INFO Changes in the CGI Environment</h1> + +<hr> + +<h2><a name="over">Overview</a></h2> + +<p>As implemented in Apache 1.1.1 and earlier versions, the method +Apache used to create PATH_INFO in the CGI environment was +counterintuitive, and could result in crashes in certain cases. In +Apache 1.2 and beyond, this behavior has changed. Although this +results in some compatibility problems with certain legacy CGI +applications, the Apache 1.2 behavior is still compatible with the +CGI/1.1 specification, and CGI scripts can be easily modified (<a +href="#compat">see below</a>). + +<h2><a name="prob">The Problem</a></h2> + +<p>Apache 1.1.1 and earlier implemented the PATH_INFO and SCRIPT_NAME +environment variables by looking at the filename, not the URL. While +this resulted in the correct values in many cases, when the filesystem +path was overloaded to contain path information, it could result in +errant behavior. For example, if the following appeared in a config +file: +<pre> + Alias /cgi-ralph /usr/local/httpd/cgi-bin/user.cgi/ralph +</pre> +<p>In this case, <code>user.cgi</code> is the CGI script, the "/ralph" +is information to be passed onto the CGI. If this configuration was in +place, and a request came for "<code>/cgi-ralph/script/</code>", the +code would set PATH_INFO to "<code>/ralph/script</code>", and +SCRIPT_NAME to "<code>/cgi-</code>". Obviously, the latter is +incorrect. In certain cases, this could even cause the server to +crash.</p> + +<h2><a name="solution">The Solution</a></h2> + +<p>Apache 1.2 and later now determine SCRIPT_NAME and PATH_INFO by +looking directly at the URL, and determining how much of the URL is +client-modifiable, and setting PATH_INFO to it. To use the above +example, PATH_INFO would be set to "<code>/script</code>", and +SCRIPT_NAME to "<code>/cgi-ralph</code>". This makes sense and results +in no server behavior problems. It also permits the script to be +guaranteed that +"<code>http://$SERVER_NAME:$SERVER_PORT$SCRIPT_NAME$PATH_INFO</code>" +will always be an accessible URL that points to the current script, +something which was not necessarily true with previous versions of +Apache. + +<p>However, the "<code>/ralph</code>" +information from the <code>Alias</code> directive is lost. This is +unfortunate, but we feel that using the filesystem to pass along this +sort of information is not a recommended method, and a script making +use of it "deserves" not to work. Apache 1.2b3 and later, however, do +provide <a href="#compat">a workaround.</a> + +<h2><a name="compat">Compatibility with Previous Servers</a></h2> + +<p>It may be necessary for a script that was designed for earlier +versions of Apache or other servers to need the information that the +old PATH_INFO variable provided. For this purpose, Apache 1.2 (1.2b3 +and later) sets an additional variable, FILEPATH_INFO. This +environment variable contains the value that PATH_INFO would have had +with Apache 1.1.1.</p> + +<p>A script that wishes to work with both Apache 1.2 and earlier +versions can simply test for the existence of FILEPATH_INFO, and use +it if available. Otherwise, it can use PATH_INFO. For example, in +Perl, one might use: +<pre> + $path_info = $ENV{'FILEPATH_INFO'} || $ENV{'PATH_INFO'}; +</pre> + +<p>By doing this, a script can work with all servers supporting the +CGI/1.1 specification, including all versions of Apache.</p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/content-negotiation.html b/usr.sbin/httpd/htdocs/manual/content-negotiation.html new file mode 100644 index 00000000000..ad4eae9fa57 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/content-negotiation.html @@ -0,0 +1,438 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache Content Negotiation</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Content Negotiation</h1> + +Apache's support for content negotiation has been updated to meet the +HTTP/1.1 specification. It can choose the best representation of a +resource based on the browser-supplied preferences for media type, +languages, character set and encoding. It is also implements a +couple of features to give more intelligent handling of requests from +browsers which send incomplete negotiation information. <p> + +Content negotiation is provided by the +<a href="mod/mod_negotiation.html">mod_negotiation</a> module, +which is compiled in by default. + +<hr> + +<h2>About Content Negotiation</h2> + +A resource may be available in several different representations. For +example, it might be available in different languages or different +media types, or a combination. One way of selecting the most +appropriate choice is to give the user an index page, and let them +select. However it is often possible for the server to choose +automatically. This works because browsers can send as part of each +request information about what representations they prefer. For +example, a browser could indicate that it would like to see +information in French, if possible, else English will do. Browsers +indicate their preferences by headers in the request. To request only +French representations, the browser would send + +<pre> + Accept-Language: fr +</pre> + +Note that this preference will only be applied when there is a choice +of representations and they vary by language. +<p> + +As an example of a more complex request, this browser has been +configured to accept French and English, but prefer French, and to +accept various media types, preferring HTML over plain text or other +text types, and preferring GIF or JPEG over other media types, but also +allowing any other media type as a last resort: + +<pre> + Accept-Language: fr; q=1.0, en; q=0.5 + Accept: text/html; q=1.0, text/*; q=0.8, image/gif; q=0.6, + image/jpeg; q=0.6, image/*; q=0.5, */*; q=0.1 +</pre> + +Apache 1.2 supports 'server driven' content negotiation, as defined in +the HTTP/1.1 specification. It fully supports the Accept, +Accept-Language, Accept-Charset and Accept-Encoding request headers. +<p> + +The terms used in content negotiation are: a <b>resource</b> is an +item which can be requested of a server, which might be selected as +the result of a content negotiation algorithm. If a resource is +available in several formats, these are called <b>representations</b> +or <b>variants</b>. The ways in which the variants for a particular +resource vary are called the <b>dimensions</b> of negotiation. + +<h2>Negotiation in Apache</h2> + +In order to negotiate a resource, the server needs to be given +information about each of the variants. This is done in one of two +ways: + +<ul> + <li> Using a type map (i.e., a <code>*.var</code> file) which + names the files containing the variants explicitly + <li> Or using a 'MultiViews' search, where the server does an implicit + filename pattern match, and chooses from among the results. +</ul> + +<h3>Using a type-map file</h3> + +A type map is a document which is associated with the handler +named <code>type-map</code> (or, for backwards-compatibility with +older Apache configurations, the mime type +<code>application/x-type-map</code>). Note that to use this feature, +you've got to have a <code>SetHandler</code> some place which defines a +file suffix as <code>type-map</code>; this is best done with a +<pre> + + AddHandler type-map var + +</pre> +in <code>srm.conf</code>. See comments in the sample config files for +details. <p> + +Type map files have an entry for each available variant; these entries +consist of contiguous RFC822-format header lines. Entries for +different variants are separated by blank lines. Blank lines are +illegal within an entry. It is conventional to begin a map file with +an entry for the combined entity as a whole (although this +is not required, and if present will be ignored). An example +map file is: +<pre> + + URI: foo + + URI: foo.en.html + Content-type: text/html + Content-language: en + + URI: foo.fr.de.html + Content-type: text/html; charset=iso-8859-2 + Content-language: fr, de +</pre> + +If the variants have different source qualities, that may be indicated +by the "qs" parameter to the media type, as in this picture (available +as jpeg, gif, or ASCII-art): +<pre> + URI: foo + + URI: foo.jpeg + Content-type: image/jpeg; qs=0.8 + + URI: foo.gif + Content-type: image/gif; qs=0.5 + + URI: foo.txt + Content-type: text/plain; qs=0.01 + +</pre> +<p> + +qs values can vary between 0.000 and 1.000. Note that any variant with +a qs value of 0.000 will never be chosen. Variants with no 'qs' +parameter value are given a qs factor of 1.0. <p> + +The full list of headers recognized is: + +<dl> + <dt> <code>URI:</code> + <dd> uri of the file containing the variant (of the given media + type, encoded with the given content encoding). These are + interpreted as URLs relative to the map file; they must be on + the same server (!), and they must refer to files to which the + client would be granted access if they were to be requested + directly. + <dt> <code>Content-type:</code> + <dd> media type --- charset, level and "qs" parameters may be given. These + are often referred to as MIME types; typical media types are + <code>image/gif</code>, <code>text/plain</code>, or + <code>text/html; level=3</code>. + <dt> <code>Content-language:</code> + <dd> The languages of the variant, specified as an Internet standard + language code (e.g., <code>en</code> for English, + <code>kr</code> for Korean, etc.). + <dt> <code>Content-encoding:</code> + <dd> If the file is compressed, or otherwise encoded, rather than + containing the actual raw data, this says how that was done. + For compressed files (the only case where this generally comes + up), content encoding should be + <code>x-compress</code>, or <code>x-gzip</code>, as appropriate. + <dt> <code>Content-length:</code> + <dd> The size of the file. Clients can ask to receive a given media + type only if the variant isn't too big; specifying a content + length in the map allows the server to compare against these + thresholds without checking the actual file. +</dl> + +<h3>Multiviews</h3> + +This is a per-directory option, meaning it can be set with an +<code>Options</code> directive within a <code><Directory></code>, +<code><Location></code> or <code><Files></code> +section in <code>access.conf</code>, or (if <code>AllowOverride</code> +is properly set) in <code>.htaccess</code> files. Note that +<code>Options All</code> does not set <code>MultiViews</code>; you +have to ask for it by name. (Fixing this is a one-line change to +<code>http_core.h</code>). + +<p> + +The effect of <code>MultiViews</code> is as follows: if the server +receives a request for <code>/some/dir/foo</code>, if +<code>/some/dir</code> has <code>MultiViews</code> enabled, and +<code>/some/dir/foo</code> does <em>not</em> exist, then the server reads the +directory looking for files named foo.*, and effectively fakes up a +type map which names all those files, assigning them the same media +types and content-encodings it would have if the client had asked for +one of them by name. It then chooses the best match to the client's +requirements, and forwards them along. + +<p> + +This applies to searches for the file named by the +<code>DirectoryIndex</code> directive, if the server is trying to +index a directory; if the configuration files specify +<pre> + + DirectoryIndex index + +</pre> then the server will arbitrate between <code>index.html</code> +and <code>index.html3</code> if both are present. If neither are +present, and <code>index.cgi</code> is there, the server will run it. + +<p> + +If one of the files found when reading the directive is a CGI script, +it's not obvious what should happen. The code gives that case +special treatment --- if the request was a POST, or a GET with +QUERY_ARGS or PATH_INFO, the script is given an extremely high quality +rating, and generally invoked; otherwise it is given an extremely low +quality rating, which generally causes one of the other views (if any) +to be retrieved. + +<h2>The Negotiation Algorithm</h2> + +After Apache has obtained a list of the variants for a given resource, +either from a type-map file or from the filenames in the directory, it +applies a algorithm to decide on the 'best' variant to return, if +any. To do this it calculates a quality value for each variant in each +of the dimensions of variance. It is not necessary to know any of the +details of how negotiation actually takes place in order to use Apache's +content negotiation features. However the rest of this document +explains in detail the algorithm used for those interested. <p> + +In some circumstances, Apache can 'fiddle' the quality factor of a +particular dimension to achieve a better result. The ways Apache can +fiddle quality factors is explained in more detail below. + +<h3>Dimensions of Negotiation</h3> + +<table> +<tr><th>Dimension +<th>Notes +<tr><td>Media Type +<td>Browser indicates preferences on Accept: header. Each item +can have an associated quality factor. Variant description can also +have a quality factor. +<tr><td>Language +<td>Browser indicates preferences on Accept-Language: header. Each +item +can have a quality factor. Variants can be associated with none, one +or more languages. +<tr><td>Encoding +<td>Browser indicates preference with Accept-Encoding: header. +<tr><td>Charset +<td>Browser indicates preference with Accept-Charset: header. Variants +can indicate a charset as a parameter of the media type. +</table> + +<h3>Apache Negotiation Algorithm</h3> + +Apache uses an algorithm to select the 'best' variant (if any) to +return to the browser. This algorithm is not configurable. It operates +like this: +<p> + +<ol> +<li> +Firstly, for each dimension of the negotiation, the appropriate +Accept header is checked and a quality assigned to this each +variant. If the Accept header for any dimension means that this +variant is not acceptable, eliminate it. If no variants remain, go +to step 4. + +<li>Select the 'best' variant by a process of elimination. Each of +the following tests is applied in order. Any variants not selected at +each stage are eliminated. After each test, if only one variant +remains, it is selected as the best match. If more than one variant +remains, move onto the next test. + +<ol> +<li>Multiply the quality factor from the Accept header with the + quality-of-source factor for this variant's media type, and select + the variants with the highest value + +<li>Select the variants with the highest language quality factor + +<li>Select the variants with the best language match, using either the + order of languages on the <code>LanguagePriority</code> directive (if present), + else the order of languages on the Accept-Language header. + +<li>Select the variants with the highest 'level' media parameter + (used to give the version of text/html media types). + +<li>Select only unencoded variants, if there is a mix of encoded + and non-encoded variants. If either all variants are encoded + or all variants are not encoded, select all. + +<li>Select only variants with acceptable charset media parameters, + as given on the Accept-Charset header line. Charset ISO-8859-1 + is always acceptable. Variants not associated with a particular + charset are assumed to be in ISO-8859-1. + +<li>Select the variants with the smallest content length + +<li>Select the first variant of those remaining (this will be either the +first listed in the type-map file, or the first read from the directory) +and go to stage 3. + +</ol> + +<li>The algorithm has now selected one 'best' variant, so return + it as the response. The HTTP response header Vary is set to indicate the + dimensions of negotiation (browsers and caches can use this + information when caching the resource). End. + +<li>To get here means no variant was selected (because non are acceptable + to the browser). Return a 406 status (meaning "No acceptable representation") + with a response body consisting of an HTML document listing the + available variants. Also set the HTTP Vary header to indicate the + dimensions of variance. + +</ol> +<h2><a name="better">Fiddling with Quality Values</a></h2> + +Apache sometimes changes the quality values from what would be +expected by a strict interpretation of the algorithm above. This is to +get a better result from the algorithm for browsers which do not send +full or accurate information. Some of the most popular browsers send +Accept header information which would otherwise result in the +selection of the wrong variant in many cases. If a browser +sends full and correct information these fiddles will not +be applied. +<p> + +<h3>Media Types and Wildcards</h3> + +The Accept: request header indicates preferences for media types. It +can also include 'wildcard' media types, such as "image/*" or "*/*" +where the * matches any string. So a request including: +<pre> + Accept: image/*, */* +</pre> + +would indicate that any type starting "image/" is acceptable, +as is any other type (so the first "image/*" is redundant). Some +browsers routinely send wildcards in addition to explicit types they +can handle. For example: +<pre> + Accept: text/html, text/plain, image/gif, image/jpeg, */* +</pre> + +The intention of this is to indicate that the explicitly +listed types are preferred, but if a different representation is +available, that is ok too. However under the basic algorithm, as given +above, the */* wildcard has exactly equal preference to all the other +types, so they are not being preferred. The browser should really have +sent a request with a lower quality (preference) value for *.*, such +as: +<pre> + Accept: text/html, text/plain, image/gif, image/jpeg, */*; q=0.01 +</pre> + +The explicit types have no quality factor, so they default to a +preference of 1.0 (the highest). The wildcard */* is given +a low preference of 0.01, so other types will only be returned if +no variant matches an explicitly listed type. +<p> + +If the Accept: header contains <i>no</i> q factors at all, Apache sets +the q value of "*/*", if present, to 0.01 to emulate the desired +behavior. It also sets the q value of wildcards of the format +"type/*" to 0.02 (so these are preferred over matches against +"*/*". If any media type on the Accept: header contains a q factor, +these special values are <i>not</i> applied, so requests from browsers +which send the correct information to start with work as expected. + +<h3>Variants with no Language</h3> + +If some of the variants for a particular resource have a language +attribute, and some do not, those variants with no language +are given a very low language quality factor of 0.001.<p> + +The reason for setting this language quality factor for +variant with no language to a very low value is to allow +for a default variant which can be supplied if none of the +other variants match the browser's language preferences. + +For example, consider the situation with three variants: + +<ul> +<li>foo.en.html, language en +<li>foo.fr.html, language en +<li>foo.html, no language +</ul> + +The meaning of a variant with no language is that it is +always acceptable to the browser. If the request Accept-Language +header includes either en or fr (or both) one of foo.en.html +or foo.fr.html will be returned. If the browser does not list +either en or fr as acceptable, foo.html will be returned instead. + +<h2>Note on Caching</h2> + +When a cache stores a document, it associates it with the request URL. +The next time that URL is requested, the cache can use the stored +document, provided it is still within date. But if the resource is +subject to content negotiation at the server, this would result in +only the first requested variant being cached, and subsequent cache +hits could return the wrong response. To prevent this, +Apache normally marks all responses that are returned after content negotiation +as non-cacheable by HTTP/1.0 clients. Apache also supports the HTTP/1.1 +protocol features to allow caching of negotiated responses. <P> + +For requests which come from a HTTP/1.0 compliant client (either a +browser or a cache), the directive <tt>CacheNegotiatedDocs</tt> can be +used to allow caching of responses which were subject to negotiation. +This directive can be given in the server config or virtual host, and +takes no arguments. It has no effect on requests from HTTP/1.1 +clients. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/custom-error.html b/usr.sbin/httpd/htdocs/manual/custom-error.html new file mode 100644 index 00000000000..d4ec34ebbb6 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/custom-error.html @@ -0,0 +1,165 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Custom error responses</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Custom error responses</H1> + +<DL> + +<DT>Purpose + + <DD>Additional functionality. Allows webmasters to configure the response of + Apache to some error or problem. + + <P>Customizable responses can be defined to be activated in the + event of a server detected error or problem. + + <P>e.g. if a script crashes and produces a "500 Server Error" + response, then this response can be replaced with either some + friendlier text or by a redirection to another URL (local or + external). + + <P> + +<DT>Old behavior + + <DD>NCSA httpd 1.3 would return some boring old error/problem message + which would often be meaningless to the user, and would provide no + means of logging the symptoms which caused it.<BR> + + <P> + +<DT>New behavior + + <DD>The server can be asked to; + <OL> + <LI>Display some other text, instead of the NCSA hard coded messages, or + <LI>redirect to a local URL, or + <LI>redirect to an external URL. + </OL> + + <P>Redirecting to another URL can be useful, but only if some information + can be passed which can then be used to explain and/or log the error/problem + more clearly. + + <P>To achieve this, Apache will define new CGI-like environment + variables, e.g. + + <blockquote><code> +REDIRECT_HTTP_ACCEPT=*/*, image/gif, image/x-xbitmap, image/jpeg <br> +REDIRECT_HTTP_USER_AGENT=Mozilla/1.1b2 (X11; I; HP-UX A.09.05 9000/712) <br> +REDIRECT_PATH=.:/bin:/usr/local/bin:/etc <br> +REDIRECT_QUERY_STRING= <br> +REDIRECT_REMOTE_ADDR=121.345.78.123 <br> +REDIRECT_REMOTE_HOST=ooh.ahhh.com <br> +REDIRECT_SERVER_NAME=crash.bang.edu <br> +REDIRECT_SERVER_PORT=80 <br> +REDIRECT_SERVER_SOFTWARE=Apache/0.8.15 <br> +REDIRECT_URL=/cgi-bin/buggy.pl <br> + </code></blockquote> + + <P>note the <code>REDIRECT_</code> prefix. + + <P>At least <code>REDIRECT_URL</code> and <code>REDIRECT_QUERY_STRING</code> will + be passed to the new URL (assuming it's a cgi-script or a cgi-include). The + other variables will exist only if they existed prior to the error/problem. + <b>None</b> of these will be set if your ErrorDocument is an + <i>external</i> redirect (i.e. anything starting with a protocol name + like <code>http:</code>, even if it refers to the same host as the + server).<p> + +<DT>Configuration + + <DD> Use of "ErrorDocument" is enabled for .htaccess files when the + <A HREF="mod/core.html#allowoverride">"FileInfo" override</A> is allowed. + + <P>Here are some examples... + + <blockquote><code> +ErrorDocument 500 /cgi-bin/crash-recover <br> +ErrorDocument 500 "Sorry, our script crashed. Oh dear<br> +ErrorDocument 500 http://xxx/ <br> +ErrorDocument 404 /Lame_excuses/not_found.html <br> +ErrorDocument 401 /Subscription/how_to_subscribe.html + </code></blockquote> + + <P>The syntax is, + + <P><code><A HREF="mod/core.html#errordocument">ErrorDocument</A></code> +<3-digit-code> action + + <P>where the action can be, + + <OL> + <LI>Text to be displayed. Prefix the text with a quote ("). Whatever + follows the quote is displayed. <em>Note: the (") prefix isn't + displayed.</em> + + <LI>An external URL to redirect to. + + <LI>A local URL to redirect to. + + </OL> +</DL> + +<P><HR><P> + +<h2>Custom error responses and redirects</H2> + +<DL> + +<DT>Purpose + + <DD>Apache's behavior to redirected URLs has been modified so that additional + environment variables are available to a script/server-include.<p> + +<DT>Old behavior + + <DD>Standard CGI vars were made available to a script which has been + redirected to. No indication of where the redirection came from was provided. + + <p> + +<DT>New behavior + <DD> + +A new batch of environment variables will be initialized for use by a +script which has been redirected to. Each new variable will have the +prefix <code>REDIRECT_</code>. <code>REDIRECT_</code> environment +variables are created from the CGI environment variables which existed +prior to the redirect, they are renamed with a <code>REDIRECT_</code> +prefix, i.e. <code>HTTP_USER_AGENT</code> becomes +<code>REDIRECT_HTTP_USER_AGENT</code>. In addition to these new +variables, Apache will define <code>REDIRECT_URL</code> and +<code>REDIRECT_STATUS</code> to help the script trace its origin. +Both the original URL and the URL being redirected to can be logged in +the access log. + +</DL> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/dns-caveats.html b/usr.sbin/httpd/htdocs/manual/dns-caveats.html new file mode 100644 index 00000000000..a8ff0d8aac5 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/dns-caveats.html @@ -0,0 +1,213 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>Issues Regarding DNS and Apache</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Issues Regarding DNS and Apache</h1> + +<p>This page could be summarized with the statement: <i>don't require +Apache to use DNS for any parsing of the configuration files</i>. +If Apache has to use DNS to parse the configuration files then your +server may be subject to reliability problems (it might not boot), or +denial and theft of service attacks (including users able to steal hits +from other users). + +<h3>A Simple Example</h3> + +Consider this configuration snippet: + +<blockquote><pre> + <VirtualHost www.abc.dom> + ServerAdmin webgirl@abc.dom + DocumentRoot /www/abc + </VirtualHost> +</pre></blockquote> + +<p>In order for Apache to function properly it absolutely needs +to have two pieces of information about each virtual host: the +<a href="mod/core.html#servername"><code>ServerName</code></a> +and at least one IP address that the server +responds to. This example does not include the IP address, so Apache +must use DNS to find the address of <code>www.abc.dom</code>. If for +some reason DNS is not available at the time your server is parsing its +config file, then this virtual host <b>will not be configured</b>. It +won't be able to respond to any hits to this virtual host (prior to +Apache version 1.2 the server would not even boot). + +<p>Suppose that <code>www.abc.dom</code> has address 10.0.0.1. Then +consider this configuration snippet: + +<blockquote><pre> + <VirtualHost 10.0.0.1> + ServerAdmin webgirl@abc.dom + DocumentRoot /www/abc + </VirtualHost> +</pre></blockquote> + +<p>Now Apache needs to use reverse DNS to find the <code>ServerName</code> +for this virtualhost. If that reverse lookup fails then it will partially +disable the virtualhost (prior to Apache version 1.2 the server would not +even boot). If the virtual host is name-based then it will effectively +be totally disabled, but if it is IP-based then it will mostly work. +However if Apache should ever have to generate a full URL for the server +which includes the server name then it will fail to generate a valid URL. + +<p>Here is a snippet that avoids both of these problems. + +<blockquote><pre> + <VirtualHost 10.0.0.1> + ServerName www.abc.dom + ServerAdmin webgirl@abc.dom + DocumentRoot /www/abc + </VirtualHost> +</pre></blockquote> + +<h3>Denial of Service</h3> + +<p>There are (at least) two forms that denial of service can come in. +If you are running a version of Apache prior to version 1.2 then your +server will not even boot if one of the two DNS lookups mentioned above +fails for any of your virtual hosts. In some cases this DNS lookup may +not even be under your control. For example, if <code>abc.dom</code> +is one of your customers and they control their own DNS then they +can force your (pre-1.2) server to fail while booting simply by deleting the +<code>www.abc.dom</code> record. + +<p>Another form is far more insidious. Consider this configuration +snippet: + +<blockquote><pre> + <VirtualHost www.abc.dom> + ServerAdmin webgirl@abc.dom + DocumentRoot /www/abc + </VirtualHost> +</pre></blockquote> + +<blockquote><pre> + <VirtualHost www.def.dom> + ServerAdmin webguy@def.dom + DocumentRoot /www/def + </VirtualHost> +</pre></blockquote> + +<p>Suppose that you've assigned 10.0.0.1 to <code>www.abc.dom</code> and +10.0.0.2 to <code>www.def.dom</code>. Furthermore, suppose that +<code>def.com</code> has control of their own DNS. With this config +you have put <code>def.com</code> into a position where they can steal +all traffic destined to <code>abc.com</code>. To do so, all they have to +do is set <code>www.def.dom</code> to 10.0.0.1. +Since they control their own DNS you can't stop them from pointing the +<code>www.def.com</code> record wherever they wish. + +<p>Requests coming in to 10.0.0.1 (including all those where users typed +in URLs of the form <code>http://www.abc.dom/whatever</code>) will all be +served by the <code>def.com</code> virtual host. To better understand why +this happens requires a more in-depth discussion of how Apache matches +up incoming requests with the virtual host that will serve it. A rough +document describing this <a href="vhosts-in-depth.html"> is available</a>. + +<h3>The "main server" Address</h3> + +<p>The addition of <a href="host.html">non-IP-based virtual host +support</a> in Apache 1.1 requires Apache to know the IP address(es) of +the host that httpd is running on. To get this address it uses either +the global <code>ServerName</code> (if present) or calls the C function +<code>gethostname</code> (which should return the same as typing +"hostname" at the command prompt). Then it performs a DNS lookup on +this address. At present there is no way to avoid this lookup. + +<p>If you fear that this lookup might fail because your DNS server is down +then you can insert the hostname in <code>/etc/hosts</code> (where you +probably already have it so that the machine can boot properly). Then +ensure that your machine is configured to use <code>/etc/hosts</code> +in the event that DNS fails. Depending on what OS you are using this +might be accomplished by editing <code>/etc/resolv.conf</code>, or maybe +<code>/etc/nsswitch.conf</code>. + +<p>If your server doesn't have to perform DNS for any other reason +then you might be able to get away with running Apache with the +<code>HOSTRESORDER</code> environment variable set to "local". This all +depends on what OS and resolver libraries you are using. It also affects +CGIs unless you use <a href="mod/mod_env.html"><code>mod_env</code></a> +to control the environment. It's best to consult the man pages or FAQs +for your OS. + +<h3>The _default_ Address</h3> + +<p>Any address that happens to go to your webserver which doesn't match +the IP address of any of the webservers will be served from the "main" or +"default" server configurations. The "main" server configuration consists +of all those definitions appearing outside of any VirtualHost section. +You may want instead to define a <code><VirtualHost _default_:*></code> +which returns 403 or 404 for all hits. (The trailing <code>:*</code> +makes it apply to all ports, which is just a safety measure should you +begin using multiple <code><a href="mod/core.html#listen">Listen</a></code> +directives.) + +<h3><a name="tips">Tips to Avoid these problems</a></h3> + +<ul> +<li> use IP addresses in <code><VirtualHost></code> +<li> use IP addresses in <code>Listen</code> +<li> use IP addresses in <code>BindAddress</code> +<li> ensure all virtual hosts have an explicit <code>ServerName</code> +<li> create a <code><VirtualHost _default_:*></code> server that + has no pages to serve +</ul> + +<h3>Appendix: Future Directions</h3> + +<p>The situation regarding DNS is highly undesirable. For Apache +1.2 we've attempted to make the server at least continue booting +in the event of failed DNS, but it might not be the best we +can do. In any event requiring the use of explicit IP addresses in +configuration files is highly undesirable in today's Internet where <a +href="http://www.ietf.org/html.charters/pier-charter.html">renumbering +</a> is a necessity. + +<p>A possible work around to the theft of service attack described above +would be to perform a reverse DNS lookup on the ip address returned by +the forward lookup and compare the two names. In the event of a mismatch +the virtualhost would be disabled. This would require reverse DNS to be +configured properly (which is something that most admins are familiar with +because of the common use of "double-reverse" DNS lookups by FTP servers +and TCP wrappers). + +<p>In any event it doesn't seem possible to reliably boot a virtual-hosted +web server when DNS has failed unless IP addresses are used. Partial +solutions such as disabling portions of the configuration might be worse +than not booting at all depending on what the webserver is supposed +to accomplish. + +<p>As HTTP/1.1 is deployed and browsers and proxies start issuing the +<code>Host</code> header it will become possible to avoid the use of +IP-based virtual hosts entirely. In this event a webserver has no requirement +to do DNS lookups during configuration. But as of March 1997 these +features have not been deployed widely enough to be put into use on +critical webservers. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/env.html b/usr.sbin/httpd/htdocs/manual/env.html new file mode 100644 index 00000000000..5052befcafc --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/env.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Special Purpose Environment Variables</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Special Purpose Environment Variables</h1> +<P>Interoperability problems have led to the introduction of +mechanisms to modify the way Apache behaves when talking to particular +clients. To make these mechanisms as flexible as possible, they +are invoked by defining environment variables, typically with +<A HREF="mod/mod_browser.html#browsermatch">BrowserMatch</A>, though +<A HREF="mod/mod_env.html#setenv">SetEnv</A> and +<A HREF="mod/mod_env.html#passenv">PassEnv</A> could also be used, for +example.</P> + +<H2>nokeepalive</H2> +This disables <A HREF="mod/core.html#keepalive">KeepAlive</A> when set. Because +of problems with Netscape 2.x and KeepAlive, we recommend the following +directive be used: +<BLOCKQUOTE><CODE> +BrowserMatch Mozilla/2 nokeepalive +</CODE></BLOCKQUOTE> +<H2>force-response-1.0</H2> +This forces an HTTP/1.0 response when set. It was originally implemented as a +result of a problem with AOL's proxies. Some clients may not behave correctly +when given an HTTP/1.1 response, and this can be used to interoperate with +them. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/footer.html b/usr.sbin/httpd/htdocs/manual/footer.html new file mode 100644 index 00000000000..794bbad31fe --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/footer.html @@ -0,0 +1,6 @@ +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> diff --git a/usr.sbin/httpd/htdocs/manual/handler.html b/usr.sbin/httpd/htdocs/manual/handler.html new file mode 100644 index 00000000000..43a0047228c --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/handler.html @@ -0,0 +1,153 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache's Handler Use</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Apache's Handler Use</h1> + +<h2>What is a Handler</h2> + +<p>A "handler" is an internal Apache representation of the action to be +performed when a file is called. Generally, files have implicit +handlers, based on the file type. Normally, all files are simply +served by the server, but certain file typed are "handled" +separately. For example, you may use a type of +"application/x-httpd-cgi" to invoke CGI scripts.</p> + +<p>Apache 1.1 adds the additional ability to use handlers +explicitly. Either based on filename extensions or on location, these +handlers are unrelated to file type. This is advantageous both because +it is a more elegant solution, but it also allows for both a type +<strong>and</strong> a handler to be associated with a file.</p> + +<p>Handlers can either be built into the server or to a module, or +they can be added with the <a +href="mod/mod_actions.html#action">Action</a> directive. The built-in +handlers in the standard distribution are as follows:</p> + +<ul> +<li><strong>send-as-is</strong>: + Send file with HTTP headers as is. + (<a href="mod/mod_asis.html">mod_asis</a>) +<li><strong>cgi-script</strong>: + Treat the file as a CGI script. + (<a href="mod/mod_cgi.html">mod_cgi</a>) +<li><strong>imap-file</strong>: + Imagemap rule file. + (<a href="mod/mod_imap.html">mod_imap</a>) +<li><strong>server-info</strong>: + Get the server's configuration information + (<a href="mod/mod_info.html">mod_info</a>) +<li><strong>server-parsed</strong>: + Parse for server-side includes + (<a href="mod/mod_include.html">mod_include</a>) +<li><strong>server-status</strong>: + Get the server's status report + (<a href="mod/mod_status.html">mod_status</a>) +<li><strong>type-map</strong>: + Parse as a type map file for content negotiation + (<a href="mod/mod_negotiation.html">mod_negotiation</a>) +</ul> + +<p> + +<h2>Directives</h2> +<ul> +<li><A HREF="#addhandler">AddHandler</A> +<li><A HREF="#sethandler">SetHandler</A> +</ul> + +<hr> + +<h2><a name="addhandler">AddHandler</a></h2> + +<strong>Syntax:</strong> <AddHandler <em>handler-name extension</em>><br> +<strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime + +<p>AddHandler maps the filename extension <em>extension</em> to the +handler <em>handler-name</em>. For example, to activate CGI scripts +with the file extension "<code>.cgi</code>", you might use: +<pre> + AddHandler cgi-script cgi +</pre> + +<p>Once that has been put into your srm.conf or httpd.conf file, any +file ending with "<code>.cgi</code>" will be treated as a CGI +program.</p> + +<hr> + +<h2><a name="sethandler">SetHandler</a></h2> + +<strong>Syntax:</strong> <SetHandler <em>handler-name</em>><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime + +<p>When placed into an <code>.htaccess</code> file or a +<code><Directory></code> or <code><Location></code> section, +this directive forces all matching files to be parsed through the +handler given by <em>handler-name</em>. For example, if you had a +directory you wanted to be parsed entirely as imagemap rule files, +regardless of extension, you might put the following into an +<code>.htaccess</code> file in that directory: +<pre> + SetHandler imap-file +</pre> +<p>Another example: if you wanted to have the server display a status +report whenever a URL of <code>http://servername/status</code> was +called, you might put the following into access.conf: +<pre> + <Location /status> + SetHandler server-status + </Location> +</pre> + +<p><hr> + +<h2>Programmer's Note</h2> + +<p>In order to implement the handler features, an addition has been +made to the <a href="misc/API.html">Apache API</a> that you may wish to +make use of. Specifically, a new record has been added to the +<code>request_rec</code> structure:</p> +<pre> + char *handler +</pre> +<p>If you wish to have your module engage a handler, you need only to +set <code>r->handler</code> to the name of the handler at any time +prior to the <code>invoke_handler</code> stage of the +request. Handlers are implemented as they were before, albeit using +the handler name instead of a content type. While it is not +necessary, the naming convention for handlers is to use a +dash-separated word, with no slashes, so as to not invade the media +type name-space.</p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/header.html b/usr.sbin/httpd/htdocs/manual/header.html new file mode 100644 index 00000000000..6980228778e --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/header.html @@ -0,0 +1,6 @@ +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> diff --git a/usr.sbin/httpd/htdocs/manual/host.html b/usr.sbin/httpd/htdocs/manual/host.html new file mode 100644 index 00000000000..74639a5b5fb --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/host.html @@ -0,0 +1,185 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>Apache non-IP Virtual Hosts</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Apache non-IP Virtual Hosts</h1> + +<strong>See Also:</strong> +<a href="virtual-host.html">Virtual Host Support</a> + +<hr> + +<h2>What is a Virtual Host</h2> + +<p>The "Virtual Host" refers to the practice of maintaining more than +one server on one machine, as differentiated by their apparent +hostname. For example, it is often desirable for companies sharing a +web server to have their own domains, with web servers accessible as +<code>www.company1.com</code> and <code>www.company2.com</code>, +without requiring the user to know any extra path information.</p> + +<p>Apache was one of the first servers to support virtual hosts right +out of the box, but since the base <code>HTTP</code> (HyperText +Transport Protocol) standard does not allow any method for the server +to determine the hostname it is being addressed as, Apache's virtual +host support has required a separate IP address for each +server. Documentation on using this approach (which still works very +well) <a href="virtual-host.html">is available</a>. + +<p>While the approach described above works, with the available IP +address space growing smaller, and the number of domains increasing, +it is not the most elegant solution, and is hard to implement on some +machines. The <code>HTTP/1.1</code> protocol contains a method for the +server to identify what name it is being addressed as. Apache 1.1 and +later support this approach as well as the traditional +IP-address-per-hostname method.</p> + +<p>The benefits of using the new virtual host support is a practically +unlimited number of servers, ease of configuration and use, and +requires no additional hardware or software. The main disadvantage is +that the user's browser must support this part of the protocol. The +latest versions of many browsers (including Netscape Navigator 2.0 and +later) do, but many browsers, especially older ones, do not. This can +cause problems, although a possible solution is addressed below.</p> + +<h2>Using non-IP Virtual Hosts</h2> + +<p>Using the new virtual hosts is quite easy, and superficially looks +like the old method. You simply add to one of the Apache configuration +files (most likely <code>httpd.conf</code> or <code>srm.conf</code>) +code similar to the following:</p> +<pre> + <VirtualHost www.apache.org> + ServerName www.apache.org + DocumentRoot /usr/web/apache + </VirtualHost> +</pre> + +<p>Of course, any additional directives can (and should) be placed +into the <code><VirtualHost></code> section. To make this work, +all that is needed is to make sure that the <code>www.apache.org</code> +DNS entry points to the same IP address as the main +server. Optionally, you could simply use that IP address in the +<VirtualHost> entry.</p> + +<p>Additionally, many servers may wish to be accessible by more than +one name. For example, the Apache server might want to be accessible +as <code>apache.org</code>, or <code>ftp.apache.org</code>, assuming +the IP addresses pointed to the same server. In fact, one might want it +so that all addresses at <code>apache.org</code> were picked up by the +server. This is possible with the <code>ServerAlias</code> +directive, placed inside the <VirtualHost> section. For +example:</p> + +<pre> + ServerAlias apache.org *.apache.org +</pre> + +<p>Note that you can use <code>*</code> and <code>?</code> as wild-card +characters.</p> + +<p>You also might need ServerAlias if you are serving local users who +do not always include the domain name. For example, if local users are +familiar with typing "www" or "www.physics" then you will need to add +<code>ServerAlias www www.physics</code>. It isn't possible for the +server to know what domain the client uses for their name resolution +because the client doesn't provide that information in the request.</p> + +<h2>Security Considerations</h2> + +Apache allows all virtual hosts to be made accessible via the +<code>Host:</code> header through all IP interfaces, even those which +are configured to use different IP interfaces. For example, if the +configuration for <code>www.foo.com</code> contained a virtual host +section for <code>www.bar.com</code>, and <code>www.bar.com</code> was +a separate IP interface, such that +non-<code>Host:</code>-header-supporting browsers can use it, as +before with Apache 1.0. If a request is made to +<code>www.foo.com</code> and the request includes the header +<code>Host: www.bar.com</code>, a page from <code>www.bar.com</code> +will be sent. + +<P> + +This is a security concern if you are controlling access to a +particular server based on IP-layer controls, such as from within a +firewall or router. Let's say <code>www.bar.com</code> in the above +example was instead an intra-net server called +<code>private.foo.com</code>, and the router used by foo.com only let +internal users access <code>private.foo.com</code>. Obviously, +<code>Host:</code> header functionality now allows someone who has +access to <code>www.foo.com</code> to get +<code>private.foo.com</code>, if they send a <code>Host: +private.foo.com</code> header. It is important to note that this +condition exists only if you only implement this policy at the IP +layer - all security controls used by Apache (i.e., <A +HREF="mod/mod_access.html">allow, deny from,</A> etc.) are consistently +respected. + +<h2>Compatibility with Older Browsers</h2> + +<p>As mentioned earlier, a majority of browsers do not send the +required data for the new virtual hosts to work properly. These +browsers will always be sent to the main server's pages. There is a +workaround, albeit a slightly cumbersome one:</p> + +<p>To continue the <code>www.apache.org</code> example (Note: Apache's +web server does not actually function in this manner), we might use the +new <code>ServerPath</code> directive in the <code>www.apache.org</code> virtual host, +for example: + +<pre> + ServerPath /apache +</pre> +<p>What does this mean? It means that a request for any file beginning +with "<code>/apache</code>" will be looked for in the Apache +docs. This means that the pages can be accessed as +<code>http://www.apache.org/apache/</code> for all browsers, although +new browsers can also access it as +<code>http://www.apache.org/</code>.</p> + +<p>In order to make this work, put a link on your main server's page +to <code>http://www.apache.org/apache/</code> (Note: Do not use +<code>http://www.apache.org/</code> - this would create an endless +loop). Then, in the virtual host's pages, be sure to use either purely +relative links (e.g. "<code>file.html</code>" or +"<code>../icons/image.gif</code>" or links containing the prefacing +<code>/apache/</code> +(e.g. "<code>http://www.apache.org/apache/file.html</code>" or +"<code>/apache/docs/1.1/index.html</code>").</p> + +<p>This requires a bit of +discipline, but adherence to these guidelines will, for the most part, +ensure that your pages will work with all browsers, new and old. When +a new browser contacts <code>http://www.apache.org/</code>, they will +be directly taken to the Apache pages. Older browsers will be able to +click on the link from the main server, go to +<code>http://www.apache.org/apache/</code>, and then access the +pages.</p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/images/home.gif b/usr.sbin/httpd/htdocs/manual/images/home.gif Binary files differnew file mode 100644 index 00000000000..11299c1cb7e --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/images/home.gif diff --git a/usr.sbin/httpd/htdocs/manual/images/index.gif b/usr.sbin/httpd/htdocs/manual/images/index.gif Binary files differnew file mode 100644 index 00000000000..741c8939d77 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/images/index.gif diff --git a/usr.sbin/httpd/htdocs/manual/images/sub.gif b/usr.sbin/httpd/htdocs/manual/images/sub.gif Binary files differnew file mode 100644 index 00000000000..93061c5ad7f --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/images/sub.gif diff --git a/usr.sbin/httpd/htdocs/manual/index.html b/usr.sbin/httpd/htdocs/manual/index.html new file mode 100644 index 00000000000..dc967b83335 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/index.html @@ -0,0 +1,79 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache documentation</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Apache User's Guide</h1> + +<hr> + +<h3><a name="new">Release Notes</a></h3> +<ul> +<li><a href="new_features_1_2.html">New features in Apache 1.2</a> +<li><a href="new_features_1_1.html">New features in Apache 1.1</a> +<li><a href="new_features_1_0.html">New features in Apache 1.0</a> +</ul> +<ul> +<li><a href="misc/known_bugs.html">Known Bugs</a> +<li><a href="misc/compat_notes.html">Compatibility Notes with NCSA httpd</a> +<li><a href="LICENSE">Apache License</A> +</ul> + +<H3><a name="ref">Apache Reference Manual</a></h3> +<UL> + <LI><A + HREF="http://www.apache.org/manual-index/docs" + ><STRONG>Search</STRONG></A> + the master manual pages for key words + </LI> +</UL> +<ul> +<li><A HREF="install.html">Compiling and Installing Apache</A> +<li><A HREF="invoking.html">Starting Apache</A> +<li><A HREF="stopping.html">Stopping or Restarting Apache</A> +<li><A HREF="mod/directives.html">Apache run-time configuration directives</A> +<li><A HREF="mod/">Apache modules</A> +<li><A HREF="handler.html">Apache's handler use</A> +<li><A HREF="env.html">Special purpose environment variables</A> +<LI><A HREF="misc/API.html">Highly generalized API to server functionality</A> +<LI><A HREF="suexec.html">Using SetUserID Execution for CGI</A> +</ul> + +<h3><a name="oth">Other Notes</a></h3> +<ul> +<li><A HREF="misc/FAQ.html">Frequently Asked Questions list</a> +<li><A href="misc/howto.html">How do I? documentation</A> +<LI><A HREF="misc/security_tips.html">Security tips</A> +<LI><A HREF="misc/perf.html">Performance hints</a> for heavily loaded web servers. +<LI><A HREF="misc/fin_wait_2.html">Discussion of the FIN_WAIT_2 problem</A> +<LI><A + HREF="http://www.apache.org/info/jdk-102.html" + >Java's 1.0.2 virtual machine and HTTP/1.1</A> +</ul> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/install.html b/usr.sbin/httpd/htdocs/manual/install.html new file mode 100644 index 00000000000..0bdc45c87df --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/install.html @@ -0,0 +1,261 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Compiling and Installing Apache</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Compiling and Installing Apache 1.2</H1> + +<P>If you wish to download and install an earlier version of Apache please +read <A HREF="install_1_1.html">Compiling and Installing Apache 1.1</A>.</P> + +UnixWare users will want to consult <A HREF="unixware.html">build notes</A> +for various UnixWare versions before compiling. + +<H2>Downloading Apache</H2> + +Information on the latest version of Apache can be found on the Apache +web server at <A +HREF="http://www.apache.org/">http://www.apache.org/</A>. This will +list the current release, any more recent beta-test release, together +with details of mirror web and anonymous ftp sites. + +<P> + +If you downloaded a binary distribution, skip to <A +HREF="#install">Installing Apache</A>. Otherwise read the next section +for how to compile the server. + +<h2>Compiling Apache</h2> + +Compiling Apache consists of three steps: Firstly select which Apache +<b>modules</b> you want to include into the server. Secondly create a +configuration for your operating system. Thirdly compile the +executable. +<P> + +All configuration of Apache is performed in the <CODE>src</CODE> +directory of the Apache distribution. Change into this directory. + +<OL> + <LI> + Select modules to compile into Apache in the + <CODE>Configuration</CODE> file. Uncomment lines corresponding to + those optional modules you wish to include (among the Module lines + at the bottom of the file), or add new lines corresponding to + additional modules you have downloaded or written. (See <A + HREF="misc/API.html">API.html</A> for preliminary docs on how to + write Apache modules). Advanced users can comment out some of the + default modules if they are sure they will not need them (be careful + though, since many of the default modules are vital for the correct + operation and security of the server). + <P> + + You should also read the instructions in the <CODE>Configuration</CODE> + file to see if you need to set any of the <CODE>Rule</CODE> lines. + + + <LI> + Configure Apache for your operating system. Normally you can just + type run the <CODE>Configure</CODE> script as given below. However + if this fails or you have any special requirements (e.g. to include + an additional library required by an optional module) you might need + to edit one or more of the following options in the + <CODE>Configuration</CODE> file: + <CODE>EXTRA_CFLAGS, LIBS, LFLAGS, INCLUDES</CODE>. + <P> + + Run the <CODE>Configure</CODE> script: + <BLOCKQUOTE> + <PRE> + % Configure + Using 'Configuration' as config file + + configured for <whatever> platform + + setting C compiler to <whatever> * + + setting C compiler optimization-level to <whatever> * + % + </PRE> + </BLOCKQUOTE> + + (*: Depending on Configuration and your system, Configure + make not print these lines. That's OK).<P> + + This generates a Makefile for use in stage 3. It also creates a + Makefile in the support directory, for compilation of the optional + support programs. + <P> + + (If you want to maintain multiple configurations, you can give a + option to <CODE>Configure</CODE> to tell it to read an alternative + Configuration file, such as <CODE>Configure -file + Configuration.ai</CODE>). + <P> + + <LI> + Type <CODE>make</CODE>. +</OL> + +The modules we place in the Apache distribution are the ones we have +tested and are used regularly by various members of the Apache +development group. Additional modules contributed by members or third +parties with specific needs or functions are available at <A +HREF="http://www.apache.org/dist/contrib/modules/"><URL:http://www.apache.org/dist/contrib/modules/></a>. +There are instructions on that page for linking these modules into the +core Apache code. + +<h2><A NAME="install">Installing Apache</A></h2> + +You will have a binary file called <CODE>httpd</CODE> in the +<CODE>src</CODE> directory. A binary distribution of Apache will +supply this file. <P> + +The next step is to install the program and configure it. Apache is +designed to be configured and run from the same set of directories +where it is compiled. If you want to run it from somewhere else, make +a directory and copy the <CODE>conf</CODE>, <CODE>logs</CODE> and +<CODE>icons</CODE> directories into it. <P> + +The next step is to edit the configuration files for the server. This +consists of setting up various <B>directives</B> in up to three +central configuration files. By default, these files are located in +the <CODE>conf</CODE> directory and are called <CODE>srm.conf</CODE>, +<CODE>access.conf</CODE> and <CODE>httpd.conf</CODE>. To help you get +started there are same files in the <CODE>conf</CODE> directory of the +distribution, called <CODE>srm.conf-dist</CODE>, +<CODE>access.conf-dist</CODE> and <CODE>httpd.conf-dist</CODE>. Copy +or rename these files to the names without the <CODE>-dist</CODE>. +Then edit each of the files. Read the comments in each file carefully. +Failure to setup these files correctly could lead to your server not +working or being insecure. You should also have an additional file in +the <CODE>conf</CODE> directory called <CODE>mime.types</CODE>. This +file usually does not need editing. + +<P> + +First edit <CODE>httpd.conf</CODE>. This sets up general attributes +about the server: the port number, the user it runs as, etc. Next +edit the <CODE>srm.conf</CODE> file; this sets up the root of the +document tree, special functions like server-parsed HTML or internal +imagemap parsing, etc. Finally, edit the <CODE>access.conf</CODE> +file to at least set the base cases of access. + +<P> + +In addition to these three files, the server behavior can be configured +on a directory-by-directory basis by using <CODE>.htaccess</CODE> +files in directories accessed by the server. + +<H3>Starting and Stopping the Server</H3> + +To start the server, simply run <CODE>httpd</CODE>. This will look for +<CODE>httpd.conf</CODE> in the location compiled into the code (by +default <CODE>/usr/locale/etc/httpd/conf/httpd.conf</CODE>). If +this file is somewhere else, you can give the real +location with the -f argument. For example: + +<PRE> + /usr/local/etc/apache/src/httpd -f /usr/local/etc/apache/conf/httpd.conf +</PRE> + +If all goes well this will return to the command prompt almost +immediately. This indicates that the server is now up and running. If +anything goes wrong during the initialization of the server you will +see an error message on the screen. + +If the server started ok, you can now use your browser to +connect to the server and read the documentation. If you are running +the browser on the same machine as the server and using the default +port of 80, a suitable URL to enter into your browser is + +<PRE> + http://localhost/ +</PRE> + +<P> + +Note that when the server starts it will create a number of +<i>child</i> processes to handle the requests. If you started Apache +as the root user, the parent process will continue to run as root +while the children will change to the user as given in the httpd.conf +file. + +<P> + +If when you run <CODE>httpd</CODE> it complained about being unable to +"bind" to an address, then either some other process is already using +the port you have configured Apache to use, or you are running httpd +as a normal user but trying to use port below 1024 (such as the +default port 80). + +<P> + +If the server is not running, read the error message displayed +when you run httpd. You should also check the server +error_log for additional information (with the default configuration, +this will be located in the file <CODE>error_log</CODE> in the +<CODE>logs</CODE> directory). + +<P> + +If you want your server to continue running after a system reboot, you +should add a call to <CODE>httpd</CODE> to your system startup files +(typically <CODE>rc.local</CODE> or a file in an +<CODE>rc.<I>N</I></CODE> directory). This will start Apache as root. +Before doing this ensure that your server is properly configured +for security and access restrictions. + +<P> + +To stop Apache send the parent process a TERM signal. The PID of this +process is written to the file <CODE>httpd.pid</CODE> in the +<CODE>logs</CODE> directory (unless configured otherwise). Do not +attempt to kill the child processes because they will be renewed by +the parent. A typical command to stop the server is: + +<PRE> + kill -TERM `cat /usr/local/etc/apache/logs/httpd.pid` +</PRE> + +<P> + +For more information about Apache command line options, configuration +and log files, see <A HREF="invoking.html">Starting Apache</A>. For a +reference guide to all Apache directives supported by the distributed +modules, see the <A HREF="mod/directives.html">Apache directives</A>. + +<H2>Compiling Support Programs</H2> + +In addition to the main <CODE>httpd</CODE> server which is compiled +and configured as above, Apache includes a number of support programs. +These are not compiled by default. The support programs are in the +<CODE>support</CODE> directory of the distribution. To compile +the support programs, change into this directory and type +<PRE> + make +</PRE> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/install_1_1.html b/usr.sbin/httpd/htdocs/manual/install_1_1.html new file mode 100644 index 00000000000..f5f0f4d376c --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/install_1_1.html @@ -0,0 +1,124 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Compiling and Installing Apache</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Compiling and Installing Apache</h1> +<h2>Downloading Apache</h2> +<p>Information on the latest version of Apache can be found on the Apache +web server at http://www.apache.org/. This will list the current release, +any more recent beta-test release, together with details of mirror +web and anonymous ftp sites.</p> + +UnixWare users will want to consult <A HREF="unixware.html">build notes</A> +for various UnixWare versions before compiling. + +<h2>Compiling Apache</h2> +This release of Apache supports the notion of `optional modules'. +However, the server has to know which modules are compiled into it, in +order for those modules to be effective; this requires generation of a +short bit of code (`<code>modules.c</code>') which simply has a list of them. +<p> +If you are satisfied with our standard module set, and expect to +continue to be satisfied with it, then you can just edit the stock +<code>Makefile</code> and compile as you have been doing previously. If you +would +like to select optional modules, however, you need to run the +configuration script. +<p> +To do this: +<ol> +<li>Edit the file `<code>Configuration</code>'. This contains the per-machine +config settings of the Makefile, and also an additional section at +the bottom which lists the modules which have been compiled in, and +also names the files containing them. You will need to: +<ol> +<li> Select a compiler and compilation options as appropriate to +your machine. +<li> Uncomment lines corresponding to those optional modules you wish +to include (among the Module lines at the bottom of the file) +or add new lines corresponding to custom modules you have written. +<p> +Note that DBM auth has to be explicitly configured in, if you want +it; just uncomment the corresponding line. +</ol> +<li> Run the `Configure' script: +<blockquote><code> +% Configure<br> +Using 'Configuration' as config file<br> +%</code></blockquote> + +This generates new versions of the Makefile and of modules.c. If +you want to maintain multiple configurations, you can say, e.g., +<blockquote><code> +% Configure -file Configuration.ai<br> +Using alternate config file Configuration.ai<br> +%</code></blockquote> + +<li>Type `make'. +<p> +The modules we place in the Apache distribution are the ones we have +tested and are used regularly by various members of the Apache +development group. Additional modules contributed by members or third +parties with specific needs or functions are available at +<A HREF="http://www.apache.org/dist/contrib/modules/"><URL:http://www.apache.org/dist/contrib/modules/></A>. There are instructions on that page for +linking these modules into the core Apache code. +</ol> + +<h2>Installing Apache</h2> +After compilation, you will have a binary called `httpd' in the +<code>src/</code> directory. A binary distribution of Apache will supply this +file. +<p> +The next step is to edit the configuration files for the server. In +the subdirectory called `conf' you should find distribution versions +of the three configuration files: <code>srm.conf-dist</code>, +<code>access.conf-dist</code> and <code>httpd.conf-dist</code>. Copy them to +<code>srm.conf</code>, <code>access.conf</code> and <code>httpd.conf</code> +respectively. +<p> +First edit <code>httpd.conf</code>. This sets up general attributes about the +server; the port number, the user it runs as, etc. Next edit the +<code>srm.conf</code> file; this sets up the root of the document tree, +special functions like server-parsed HTML or internal imagemap parsing, etc. +Finally, edit the <code>access.conf</code> file to at least set the base cases +of access. +<p> +Finally, make a call to httpd, with a -f to the full path to the +httpd.conf file. I.e., the common case: +<blockquote><code> + /usr/local/etc/apache/src/httpd -f /usr/local/etc/apache/conf/httpd.conf +</code></blockquote> +The server should be now running. +<p> +By default the <code>srm.conf</code> and <code>access.conf</code> files are +located by name; to specifically call them by other names, use the +<A HREF="mod/core.html#accessconfig">AccessConfig</A> and +<A HREF="mod/core.html#resourceconfig">ResourceConfig</A> directives in +<code>httpd.conf</code>. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/invoking.html b/usr.sbin/httpd/htdocs/manual/invoking.html new file mode 100644 index 00000000000..b960916cfee --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/invoking.html @@ -0,0 +1,136 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Starting Apache</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Starting Apache</h1> + +<h2>Invoking Apache</h2> +The <code>httpd</code> program is usually run as a daemon which executes +continuously, handling requests. It is possible to invoke Apache by +the Internet daemon <code>inetd</code> each time a connection to the HTTP +service is made (use the +<A HREF="mod/core.html#servertype">ServerType</A> directive) +but this is not recommended. + +<h2>Command line options</h2> +The following options are recognized on the httpd command line: +<dl> +<dt><code>-d</code> <em>serverroot</em> +<dd>Set the initial value for the +<A HREF="mod/core.html#serverroot">ServerRoot</A> variable to +<em>serverroot</em>. This can be overridden by the ServerRoot command in the +configuration file. The default is <code>/usr/local/etc/httpd</code>. + +<dt><code>-f</code> <em>config</em> +<dd>Execute the commands in the file <em>config</em> on startup. If +<em>config</em> does not begin with a <code>/</code>, then it is taken to be a +path relative to the <A HREF="mod/core.html#serverroot">ServerRoot</A>. The +default is <code>conf/httpd.conf</code>. + +<dt><code>-X</code> +<dd>Run in single-process mode, for internal debugging purposes only; the +daemon does not detach from the terminal or fork any children. Do <em>NOT</em> +use this mode to provide ordinary web service. + +<dt><code>-v</code> +<dd>Print the version of httpd, and then exit. + +<dt><a name="help"><code>-h</code></a> +<dd>Give a list of directives together with expected arguments and +places where the directive is valid. (New in Apache 1.2) + +<dt><code>-l</code> +<dd>Give a list of all modules compiled into the server. + +<dt><code>-?</code> +<dd>Print a list of the httpd options, and then exit. +</dl> + +<h2>Configuration files</h2> +The server will read three files for configuration directives. Any directive +may appear in any of these files. The the names of these files are taken +to be relative to the server root; this is set by the +<A HREF="mod/core.html#serverroot">ServerRoot</A> directive, or the +<code>-d</code> command line flag. + +Conventionally, the files are: +<dl> +<dt><code>conf/httpd.conf</code> +<dd>Contains directives that control the operation of the server daemon. +The filename may be overridden with the <code>-f</code> command line flag. + +<dt><code>conf/srm.conf</code> +<dd>Contains directives that control the specification of documents that +the server can provide to clients. The filename may be overridden with +the <A HREF="mod/core.html#resourceconfig">ResourceConfig</A> directive. + +<dt><code>conf/access.conf</code> +<dd>Contains directives that control access to documents. +The filename may be overridden with the +<A HREF="mod/core.html#accessconfig">AccessConfig</A> directive. +</dl> +However, these conventions need not be adhered to. +<p> +The server also reads a file containing mime document types; the filename +is set by the <A HREF="mod/mod_mime.html#typesconfig">TypesConfig</A> directive, +and is <code>conf/mime.types</code> by default. + +<h2>Log files</h2> +<h3>security warning</h3> +Anyone who can write to the directory where Apache is writing a +log file can almost certainly gain access to the uid that the server is +started as, which is normally root. Do <EM>NOT</EM> give people write +access to the directory the logs are stored in without being aware of +the consequences; see the <A HREF="misc/security_tips.html">security tips</A> +document for details. +<h3>pid file</h3> +On daemon startup, it saves the process id of the parent httpd process to +the file <code>logs/httpd.pid</code>. This filename can be changed with the +<A HREF="mod/core.html#pidfile">PidFile</A> directive. The process-id is for +use by the administrator in restarting and terminating the daemon; +A HUP or USR1 signal causes the daemon to re-read its configuration files and +a TERM signal causes it to die gracefully. For more information +see the <a href="stopping.html">Stopping and Restarting</a> page. +<p> +If the process dies (or is killed) abnormally, then it will be necessary to +kill the children httpd processes. + +<h3>Error log</h3> +The server will log error messages to a log file, <code>logs/error_log</code> +by default. The filename can be set using the +<A HREF="mod/core.html#errorlog">ErrorLog</A> directive; different error logs can +be set for different <A HREF="mod/core.html#virtualhost">virtual hosts</A>. + +<h3>Transfer log</h3> +The server will typically log each request to a transfer file, +<code>logs/access_log</code> by default. The filename can be set using a +<A HREF="mod/mod_log_common.html#transferlog">TransferLog</A> directive; different +transfer logs can be set for different <A HREF="mod/core.html#virtualhost">virtual +hosts</A>. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/keepalive.html b/usr.sbin/httpd/htdocs/manual/keepalive.html new file mode 100644 index 00000000000..0e0255a6257 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/keepalive.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>Apache Keep-Alive Support</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Apache Keep-Alive Support</h1> + +<hr> + +<h2>What is Keep-Alive?</h2> + +The Keep-Alive extension to HTTP, as defined by the +<code>HTTP/1.1</code> draft, allows persistent connections. These +long-lived HTTP sessions allow multiple requests to be send over the +same TCP connection, and in some cases have been shown to result in an +almost 50% speedup in latency times for HTML documents with lots of +images. + +<h2>Enabling Keep-Alive Support</h2> + +Apache 1.1 comes with Keep-Alive support on by default, however there +are some directives you can use to modify Apache's behavior: + +<p><strong>Note</strong>: Apache 1.2 uses a different syntax for the <a +href="mod/core.html#keepalive">KeepAlive</a> directive.</p> + +<h3>KeepAlive</h3> +<strong>Syntax:</strong> KeepAlive <em>max-requests</em><br> +<strong>Default:</strong> <code>KeepAlive 5</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Core<p> + +This directive enables Keep-Alive support. Set <em>max-requests</em> +to the maximum number of requests you want Apache to entertain per +connection. A limit is imposed to prevent a client from hogging your +server resources. Set this to <code>0</code> to disable support. + +<h3>KeepAliveTimeout</h3> +<strong>Syntax:</strong> KeepAliveTimeout <em>seconds</em><br> +<strong>Default:</strong> <code>KeepAliveTimeout 15</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Core<p> + +The number of seconds Apache will wait for a subsequent request before +closing the connection. Once a request has been received, the timeout +value specified by the <a +href="mod/core.html#timeout"><code>Timeout</code></a> directive +applies. + +<h2>When Keep-Alive Is Used</h2> + +In order for Keep-Alive support to be used, first the browser must +support it. Many current browsers, including Netscape Navigator 2.0, +and Spyglass Mosaic-based browsers (including Microsoft Internet +Explorer) do. Note, however, that some Windows 95-based browsers misbehave +with Keep-Alive-supporting servers; they may occasionally hang on a +connect. This has been observed with several Windows browsers, and occurs +when connecting to any Keep-Alive server, not just Apache. Netscape 3.0b5 +and later versions are known to work around this problem. + +<p>However, Keep-Alive support only is active with files where the +length is known beforehand. This means that most CGI scripts, +server-side included files and directory listings will not use +the Keep-Alive protocol. While this should be completely transparent +to the end user, it is something the web-master may want to keep in mind.</p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/location.html b/usr.sbin/httpd/htdocs/manual/location.html new file mode 100644 index 00000000000..bdefc559d6c --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/location.html @@ -0,0 +1,71 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Access Control by URL</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Access Control by URL</H1> + +<h2><a name="location">The <code><Location></code> Directive</a></h2> + +<strong>Syntax:</strong> <Location <em>URL prefix</em>><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<br> + +<p>The <Location> directive provides for access control by +URL. It is comparable to the <a +href="mod/core.html#directory"><Directory></a> directive, and +should be matched with a </Location> directive. Directives that +apply to the URL given should be listen +within. <code><Location></code> sections are processed in the +order they appear in the configuration file, after the +<Directory> sections and <code>.htaccess</code> files are +read.</p> + +<p>Note that, due to the way HTTP functions, <em>URL prefix</em> +should, save for proxy requests, be of the form <code>/path/</code>, +and should not include the <code>http://servername</code>. It doesn't +necessarily have to protect a directory (it can be an individual +file, or a number of files), and can include wild-cards. In a wild-card +string, `?' matches any single character, and `*' matches any +sequences of characters. + +<p>This functionality is especially useful when combined with the +<code><a href="mod/mod_mime.html#sethandler">SetHandler</a></code> +directive. For example, to enable status requests, but allow them only +from browsers at foo.com, you might use: + +<pre> + <Location /status> + SetHandler server-status + order deny,allow + deny from all + allow from .foo.com + </Location> +</pre> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/man-template.html b/usr.sbin/httpd/htdocs/manual/man-template.html new file mode 100644 index 00000000000..6d2ff047993 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/man-template.html @@ -0,0 +1,81 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_foobar</TITLE> +</HEAD> + +<!-- read and delete all blockquotes, and this comment --> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_foobar</h1> + +<blockquote><em>Add this file as a link in modules.html</em></blockquote> + +This module is contained in the <code>mod_foobar.c</code> file, and +<strong>is/is not</strong> compiled in by default. It provides for +<strong>the foobar feature</strong>. Any document with the mime type +<code>foo/bar</code> will be processed by this module. + +<blockquote><em>Add the magic mime type to the list in +magic_types.html</em></blockquote> + +<h2>Summary</h2> +General module documentation here. + +<h2>Directives</h2> +<ul> +<li><A HREF="#adirective">ADirective</A> +</ul> + +<blockquote><em>Add these directives to the list in +directives.html</em></blockquote> + +<hr> + +<h2><A name="adirective">ADirective</A></h2> +<strong>Syntax:</strong> ADirective <em>some args</em><br> +<strong>Default:</strong> <code>ADirective default value</code><br> +<strong>Context:</strong> context-list<br> + +<blockquote><em>context-list is where this directive can appear; +allowed: server config, virtual host, directory, .htaccess</em></blockquote> + +<strong>Override:</strong> override<br> + +<blockquote><em>required if the directive is allowed in .htaccess files; +the AllowOverride option that allows the directive.</em></blockquote> + +<strong>Status:</strong> status<br> + +<blockquote><em>Core if in core apache, Base if in one of the standard +modules, Extension if in an extension module (not compiled in by default) +or Experimental</em></blockquote> + +<strong>Module:</strong> mod_foobar<p> + +The ADirective directive does something. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/misc/API.html b/usr.sbin/httpd/htdocs/manual/misc/API.html new file mode 100644 index 00000000000..1ad15723ed3 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/API.html @@ -0,0 +1,1017 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>Apache API notes</title> +</head> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Apache API notes</h1> + +These are some notes on the Apache API and the data structures you +have to deal with, etc. They are not yet nearly complete, but +hopefully, they will help you get your bearings. Keep in mind that +the API is still subject to change as we gain experience with it. +(See the TODO file for what <em>might</em> be coming). However, +it will be easy to adapt modules to any changes that are made. +(We have more modules to adapt than you do). +<p> + +A few notes on general pedagogical style here. In the interest of +conciseness, all structure declarations here are incomplete --- the +real ones have more slots that I'm not telling you about. For the +most part, these are reserved to one component of the server core or +another, and should be altered by modules with caution. However, in +some cases, they really are things I just haven't gotten around to +yet. Welcome to the bleeding edge.<p> + +Finally, here's an outline, to give you some bare idea of what's +coming up, and in what order: + +<ul> +<li> <a href="#basics">Basic concepts.</a> +<menu> + <li> <a href="#HMR">Handlers, Modules, and Requests</a> + <li> <a href="#moduletour">A brief tour of a module</a> +</menu> +<li> <a href="#handlers">How handlers work</a> +<menu> + <li> <a href="#req_tour">A brief tour of the <code>request_rec</code></a> + <li> <a href="#req_orig">Where request_rec structures come from</a> + <li> <a href="#req_return">Handling requests, declining, and returning error codes</a> + <li> <a href="#resp_handlers">Special considerations for response handlers</a> + <li> <a href="#auth_handlers">Special considerations for authentication handlers</a> + <li> <a href="#log_handlers">Special considerations for logging handlers</a> +</menu> +<li> <a href="#pools">Resource allocation and resource pools</a> +<li> <a href="#config">Configuration, commands and the like</a> +<menu> + <li> <a href="#per-dir">Per-directory configuration structures</a> + <li> <a href="#commands">Command handling</a> + <li> <a href="#servconf">Side notes --- per-server configuration, virtual servers, etc.</a> +</menu> +</ul> + +<h2><a name="basics">Basic concepts.</a></h2> + +We begin with an overview of the basic concepts behind the +API, and how they are manifested in the code. + +<h3><a name="HMR">Handlers, Modules, and Requests</a></h3> + +Apache breaks down request handling into a series of steps, more or +less the same way the Netscape server API does (although this API has +a few more stages than NetSite does, as hooks for stuff I thought +might be useful in the future). These are: + +<ul> + <li> URI -> Filename translation + <li> Auth ID checking [is the user who they say they are?] + <li> Auth access checking [is the user authorized <em>here</em>?] + <li> Access checking other than auth + <li> Determining MIME type of the object requested + <li> `Fixups' --- there aren't any of these yet, but the phase is + intended as a hook for possible extensions like + <code>SetEnv</code>, which don't really fit well elsewhere. + <li> Actually sending a response back to the client. + <li> Logging the request +</ul> + +These phases are handled by looking at each of a succession of +<em>modules</em>, looking to see if each of them has a handler for the +phase, and attempting invoking it if so. The handler can typically do +one of three things: + +<ul> + <li> <em>Handle</em> the request, and indicate that it has done so + by returning the magic constant <code>OK</code>. + <li> <em>Decline</em> to handle the request, by returning the magic + integer constant <code>DECLINED</code>. In this case, the + server behaves in all respects as if the handler simply hadn't + been there. + <li> Signal an error, by returning one of the HTTP error codes. + This terminates normal handling of the request, although an + ErrorDocument may be invoked to try to mop up, and it will be + logged in any case. +</ul> + +Most phases are terminated by the first module that handles them; +however, for logging, `fixups', and non-access authentication +checking, all handlers always run (barring an error). Also, the +response phase is unique in that modules may declare multiple handlers +for it, via a dispatch table keyed on the MIME type of the requested +object. Modules may declare a response-phase handler which can handle +<em>any</em> request, by giving it the key <code>*/*</code> (i.e., a +wildcard MIME type specification). However, wildcard handlers are +only invoked if the server has already tried and failed to find a more +specific response handler for the MIME type of the requested object +(either none existed, or they all declined).<p> + +The handlers themselves are functions of one argument (a +<code>request_rec</code> structure. vide infra), which returns an +integer, as above.<p> + +<h3><a name="moduletour">A brief tour of a module</a></h3> + +At this point, we need to explain the structure of a module. Our +candidate will be one of the messier ones, the CGI module --- this +handles both CGI scripts and the <code>ScriptAlias</code> config file +command. It's actually a great deal more complicated than most +modules, but if we're going to have only one example, it might as well +be the one with its fingers in every place.<p> + +Let's begin with handlers. In order to handle the CGI scripts, the +module declares a response handler for them. Because of +<code>ScriptAlias</code>, it also has handlers for the name +translation phase (to recognize <code>ScriptAlias</code>ed URIs), the +type-checking phase (any <code>ScriptAlias</code>ed request is typed +as a CGI script).<p> + +The module needs to maintain some per (virtual) +server information, namely, the <code>ScriptAlias</code>es in effect; +the module structure therefore contains pointers to a functions which +builds these structures, and to another which combines two of them (in +case the main server and a virtual server both have +<code>ScriptAlias</code>es declared).<p> + +Finally, this module contains code to handle the +<code>ScriptAlias</code> command itself. This particular module only +declares one command, but there could be more, so modules have +<em>command tables</em> which declare their commands, and describe +where they are permitted, and how they are to be invoked. <p> + +A final note on the declared types of the arguments of some of these +commands: a <code>pool</code> is a pointer to a <em>resource pool</em> +structure; these are used by the server to keep track of the memory +which has been allocated, files opened, etc., either to service a +particular request, or to handle the process of configuring itself. +That way, when the request is over (or, for the configuration pool, +when the server is restarting), the memory can be freed, and the files +closed, <i>en masse</i>, without anyone having to write explicit code to +track them all down and dispose of them. Also, a +<code>cmd_parms</code> structure contains various information about +the config file being read, and other status information, which is +sometimes of use to the function which processes a config-file command +(such as <code>ScriptAlias</code>). + +With no further ado, the module itself: + +<pre> +/* Declarations of handlers. */ + +int translate_scriptalias (request_rec *); +int type_scriptalias (request_rec *); +int cgi_handler (request_rec *); + +/* Subsidiary dispatch table for response-phase handlers, by MIME type */ + +handler_rec cgi_handlers[] = { +{ "application/x-httpd-cgi", cgi_handler }, +{ NULL } +}; + +/* Declarations of routines to manipulate the module's configuration + * info. Note that these are returned, and passed in, as void *'s; + * the server core keeps track of them, but it doesn't, and can't, + * know their internal structure. + */ + +void *make_cgi_server_config (pool *); +void *merge_cgi_server_config (pool *, void *, void *); + +/* Declarations of routines to handle config-file commands */ + +extern char *script_alias(cmd_parms *, void *per_dir_config, char *fake, + char *real); + +command_rec cgi_cmds[] = { +{ "ScriptAlias", script_alias, NULL, RSRC_CONF, TAKE2, + "a fakename and a realname"}, +{ NULL } +}; + +module cgi_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creator */ + NULL, /* dir merger --- default is to override */ + make_cgi_server_config, /* server config */ + merge_cgi_server_config, /* merge server config */ + cgi_cmds, /* command table */ + cgi_handlers, /* handlers */ + translate_scriptalias, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + type_scriptalias, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; +</pre> + +<h2><a name="handlers">How handlers work</a></h2> + +The sole argument to handlers is a <code>request_rec</code> structure. +This structure describes a particular request which has been made to +the server, on behalf of a client. In most cases, each connection to +the client generates only one <code>request_rec</code> structure.<p> + +<h3><a name="req_tour">A brief tour of the <code>request_rec</code></a></h3> + +The <code>request_rec</code> contains pointers to a resource pool +which will be cleared when the server is finished handling the +request; to structures containing per-server and per-connection +information, and most importantly, information on the request itself.<p> + +The most important such information is a small set of character +strings describing attributes of the object being requested, including +its URI, filename, content-type and content-encoding (these being filled +in by the translation and type-check handlers which handle the +request, respectively). <p> + +Other commonly used data items are tables giving the MIME headers on +the client's original request, MIME headers to be sent back with the +response (which modules can add to at will), and environment variables +for any subprocesses which are spawned off in the course of servicing +the request. These tables are manipulated using the +<code>table_get</code> and <code>table_set</code> routines. <p> +<BLOCKQUOTE> + Note that the <SAMP>Content-type</SAMP> header value <EM>cannot</EM> be + set by module content-handlers using the <SAMP>table_*()</SAMP> + routines. Rather, it is set by pointing the <SAMP>content_type</SAMP> + field in the <SAMP>request_rec</SAMP> structure to an appropriate + string. <EM>E.g.</EM>, + <PRE> + r->content_type = "text/html"; + </PRE> +</BLOCKQUOTE> +Finally, there are pointers to two data structures which, in turn, +point to per-module configuration structures. Specifically, these +hold pointers to the data structures which the module has built to +describe the way it has been configured to operate in a given +directory (via <code>.htaccess</code> files or +<code><Directory></code> sections), for private data it has +built in the course of servicing the request (so modules' handlers for +one phase can pass `notes' to their handlers for other phases). There +is another such configuration vector in the <code>server_rec</code> +data structure pointed to by the <code>request_rec</code>, which +contains per (virtual) server configuration data.<p> + +Here is an abridged declaration, giving the fields most commonly used:<p> + +<pre> +struct request_rec { + + pool *pool; + conn_rec *connection; + server_rec *server; + + /* What object is being requested */ + + char *uri; + char *filename; + char *path_info; + char *args; /* QUERY_ARGS, if any */ + struct stat finfo; /* Set by server core; + * st_mode set to zero if no such file */ + + char *content_type; + char *content_encoding; + + /* MIME header environments, in and out. Also, an array containing + * environment variables to be passed to subprocesses, so people can + * write modules to add to that environment. + * + * The difference between headers_out and err_headers_out is that + * the latter are printed even on error, and persist across internal + * redirects (so the headers printed for ErrorDocument handlers will + * have them). + */ + + table *headers_in; + table *headers_out; + table *err_headers_out; + table *subprocess_env; + + /* Info about the request itself... */ + + int header_only; /* HEAD request, as opposed to GET */ + char *protocol; /* Protocol, as given to us, or HTTP/0.9 */ + char *method; /* GET, HEAD, POST, etc. */ + int method_number; /* M_GET, M_POST, etc. */ + + /* Info for logging */ + + char *the_request; + int bytes_sent; + + /* A flag which modules can set, to indicate that the data being + * returned is volatile, and clients should be told not to cache it. + */ + + int no_cache; + + /* Various other config info which may change with .htaccess files + * These are config vectors, with one void* pointer for each module + * (the thing pointed to being the module's business). + */ + + void *per_dir_config; /* Options set in config files, etc. */ + void *request_config; /* Notes on *this* request */ + +}; + +</pre> + +<h3><a name="req_orig">Where request_rec structures come from</a></h3> + +Most <code>request_rec</code> structures are built by reading an HTTP +request from a client, and filling in the fields. However, there are +a few exceptions: + +<ul> + <li> If the request is to an imagemap, a type map (i.e., a + <code>*.var</code> file), or a CGI script which returned a + local `Location:', then the resource which the user requested + is going to be ultimately located by some URI other than what + the client originally supplied. In this case, the server does + an <em>internal redirect</em>, constructing a new + <code>request_rec</code> for the new URI, and processing it + almost exactly as if the client had requested the new URI + directly. <p> + + <li> If some handler signaled an error, and an + <code>ErrorDocument</code> is in scope, the same internal + redirect machinery comes into play.<p> + + <li> Finally, a handler occasionally needs to investigate `what + would happen if' some other request were run. For instance, + the directory indexing module needs to know what MIME type + would be assigned to a request for each directory entry, in + order to figure out what icon to use.<p> + + Such handlers can construct a <em>sub-request</em>, using the + functions <code>sub_req_lookup_file</code> and + <code>sub_req_lookup_uri</code>; this constructs a new + <code>request_rec</code> structure and processes it as you + would expect, up to but not including the point of actually + sending a response. (These functions skip over the access + checks if the sub-request is for a file in the same directory + as the original request).<p> + + (Server-side includes work by building sub-requests and then + actually invoking the response handler for them, via the + function <code>run_sub_request</code>). +</ul> + +<h3><a name="req_return">Handling requests, declining, and returning error codes</a></h3> + +As discussed above, each handler, when invoked to handle a particular +<code>request_rec</code>, has to return an <code>int</code> to +indicate what happened. That can either be + +<ul> + <li> OK --- the request was handled successfully. This may or may + not terminate the phase. + <li> DECLINED --- no erroneous condition exists, but the module + declines to handle the phase; the server tries to find another. + <li> an HTTP error code, which aborts handling of the request. +</ul> + +Note that if the error code returned is <code>REDIRECT</code>, then +the module should put a <code>Location</code> in the request's +<code>headers_out</code>, to indicate where the client should be +redirected <em>to</em>. <p> + +<h3><a name="resp_handlers">Special considerations for response handlers</a></h3> + +Handlers for most phases do their work by simply setting a few fields +in the <code>request_rec</code> structure (or, in the case of access +checkers, simply by returning the correct error code). However, +response handlers have to actually send a request back to the client. <p> + +They should begin by sending an HTTP response header, using the +function <code>send_http_header</code>. (You don't have to do +anything special to skip sending the header for HTTP/0.9 requests; the +function figures out on its own that it shouldn't do anything). If +the request is marked <code>header_only</code>, that's all they should +do; they should return after that, without attempting any further +output. <p> + +Otherwise, they should produce a request body which responds to the +client as appropriate. The primitives for this are <code>rputc</code> +and <code>rprintf</code>, for internally generated output, and +<code>send_fd</code>, to copy the contents of some <code>FILE *</code> +straight to the client. <p> + +At this point, you should more or less understand the following piece +of code, which is the handler which handles <code>GET</code> requests +which have no more specific handler; it also shows how conditional +<code>GET</code>s can be handled, if it's desirable to do so in a +particular response handler --- <code>set_last_modified</code> checks +against the <code>If-modified-since</code> value supplied by the +client, if any, and returns an appropriate code (which will, if +nonzero, be USE_LOCAL_COPY). No similar considerations apply for +<code>set_content_length</code>, but it returns an error code for +symmetry.<p> + +<pre> +int default_handler (request_rec *r) +{ + int errstatus; + FILE *f; + + if (r->method_number != M_GET) return DECLINED; + if (r->finfo.st_mode == 0) return NOT_FOUND; + + if ((errstatus = set_content_length (r, r->finfo.st_size)) + || (errstatus = set_last_modified (r, r->finfo.st_mtime))) + return errstatus; + + f = fopen (r->filename, "r"); + + if (f == NULL) { + log_reason("file permissions deny server access", + r->filename, r); + return FORBIDDEN; + } + + register_timeout ("send", r); + send_http_header (r); + + if (!r->header_only) send_fd (f, r); + pfclose (r->pool, f); + return OK; +} +</pre> + +Finally, if all of this is too much of a challenge, there are a few +ways out of it. First off, as shown above, a response handler which +has not yet produced any output can simply return an error code, in +which case the server will automatically produce an error response. +Secondly, it can punt to some other handler by invoking +<code>internal_redirect</code>, which is how the internal redirection +machinery discussed above is invoked. A response handler which has +internally redirected should always return <code>OK</code>. <p> + +(Invoking <code>internal_redirect</code> from handlers which are +<em>not</em> response handlers will lead to serious confusion). + +<h3><a name="auth_handlers">Special considerations for authentication handlers</a></h3> + +Stuff that should be discussed here in detail: + +<ul> + <li> Authentication-phase handlers not invoked unless auth is + configured for the directory. + <li> Common auth configuration stored in the core per-dir + configuration; it has accessors <code>auth_type</code>, + <code>auth_name</code>, and <code>requires</code>. + <li> Common routines, to handle the protocol end of things, at least + for HTTP basic authentication (<code>get_basic_auth_pw</code>, + which sets the <code>connection->user</code> structure field + automatically, and <code>note_basic_auth_failure</code>, which + arranges for the proper <code>WWW-Authenticate:</code> header + to be sent back). +</ul> + +<h3><a name="log_handlers">Special considerations for logging handlers</a></h3> + +When a request has internally redirected, there is the question of +what to log. Apache handles this by bundling the entire chain of +redirects into a list of <code>request_rec</code> structures which are +threaded through the <code>r->prev</code> and <code>r->next</code> +pointers. The <code>request_rec</code> which is passed to the logging +handlers in such cases is the one which was originally built for the +initial request from the client; note that the bytes_sent field will +only be correct in the last request in the chain (the one for which a +response was actually sent). + +<h2><a name="pools">Resource allocation and resource pools</a></h2> + +One of the problems of writing and designing a server-pool server is +that of preventing leakage, that is, allocating resources (memory, +open files, etc.), without subsequently releasing them. The resource +pool machinery is designed to make it easy to prevent this from +happening, by allowing resource to be allocated in such a way that +they are <em>automatically</em> released when the server is done with +them. <p> + +The way this works is as follows: the memory which is allocated, file +opened, etc., to deal with a particular request are tied to a +<em>resource pool</em> which is allocated for the request. The pool +is a data structure which itself tracks the resources in question. <p> + +When the request has been processed, the pool is <em>cleared</em>. At +that point, all the memory associated with it is released for reuse, +all files associated with it are closed, and any other clean-up +functions which are associated with the pool are run. When this is +over, we can be confident that all the resource tied to the pool have +been released, and that none of them have leaked. <p> + +Server restarts, and allocation of memory and resources for per-server +configuration, are handled in a similar way. There is a +<em>configuration pool</em>, which keeps track of resources which were +allocated while reading the server configuration files, and handling +the commands therein (for instance, the memory that was allocated for +per-server module configuration, log files and other files that were +opened, and so forth). When the server restarts, and has to reread +the configuration files, the configuration pool is cleared, and so the +memory and file descriptors which were taken up by reading them the +last time are made available for reuse. <p> + +It should be noted that use of the pool machinery isn't generally +obligatory, except for situations like logging handlers, where you +really need to register cleanups to make sure that the log file gets +closed when the server restarts (this is most easily done by using the +function <code><a href="#pool-files">pfopen</a></code>, which also +arranges for the underlying file descriptor to be closed before any +child processes, such as for CGI scripts, are <code>exec</code>ed), or +in case you are using the timeout machinery (which isn't yet even +documented here). However, there are two benefits to using it: +resources allocated to a pool never leak (even if you allocate a +scratch string, and just forget about it); also, for memory +allocation, <code>palloc</code> is generally faster than +<code>malloc</code>.<p> + +We begin here by describing how memory is allocated to pools, and then +discuss how other resources are tracked by the resource pool +machinery. + +<h3>Allocation of memory in pools</h3> + +Memory is allocated to pools by calling the function +<code>palloc</code>, which takes two arguments, one being a pointer to +a resource pool structure, and the other being the amount of memory to +allocate (in <code>char</code>s). Within handlers for handling +requests, the most common way of getting a resource pool structure is +by looking at the <code>pool</code> slot of the relevant +<code>request_rec</code>; hence the repeated appearance of the +following idiom in module code: + +<pre> +int my_handler(request_rec *r) +{ + struct my_structure *foo; + ... + + foo = (foo *)palloc (r->pool, sizeof(my_structure)); +} +</pre> + +Note that <em>there is no <code>pfree</code></em> --- +<code>palloc</code>ed memory is freed only when the associated +resource pool is cleared. This means that <code>palloc</code> does not +have to do as much accounting as <code>malloc()</code>; all it does in +the typical case is to round up the size, bump a pointer, and do a +range check.<p> + +(It also raises the possibility that heavy use of <code>palloc</code> +could cause a server process to grow excessively large. There are +two ways to deal with this, which are dealt with below; briefly, you +can use <code>malloc</code>, and try to be sure that all of the memory +gets explicitly <code>free</code>d, or you can allocate a sub-pool of +the main pool, allocate your memory in the sub-pool, and clear it out +periodically. The latter technique is discussed in the section on +sub-pools below, and is used in the directory-indexing code, in order +to avoid excessive storage allocation when listing directories with +thousands of files). + +<h3>Allocating initialized memory</h3> + +There are functions which allocate initialized memory, and are +frequently useful. The function <code>pcalloc</code> has the same +interface as <code>palloc</code>, but clears out the memory it +allocates before it returns it. The function <code>pstrdup</code> +takes a resource pool and a <code>char *</code> as arguments, and +allocates memory for a copy of the string the pointer points to, +returning a pointer to the copy. Finally <code>pstrcat</code> is a +varargs-style function, which takes a pointer to a resource pool, and +at least two <code>char *</code> arguments, the last of which must be +<code>NULL</code>. It allocates enough memory to fit copies of each +of the strings, as a unit; for instance: + +<pre> + pstrcat (r->pool, "foo", "/", "bar", NULL); +</pre> + +returns a pointer to 8 bytes worth of memory, initialized to +<code>"foo/bar"</code>. + +<h3><a name="pool-files">Tracking open files, etc.</a></h3> + +As indicated above, resource pools are also used to track other sorts +of resources besides memory. The most common are open files. The +routine which is typically used for this is <code>pfopen</code>, which +takes a resource pool and two strings as arguments; the strings are +the same as the typical arguments to <code>fopen</code>, e.g., + +<pre> + ... + FILE *f = pfopen (r->pool, r->filename, "r"); + + if (f == NULL) { ... } else { ... } +</pre> + +There is also a <code>popenf</code> routine, which parallels the +lower-level <code>open</code> system call. Both of these routines +arrange for the file to be closed when the resource pool in question +is cleared. <p> + +Unlike the case for memory, there <em>are</em> functions to close +files allocated with <code>pfopen</code>, and <code>popenf</code>, +namely <code>pfclose</code> and <code>pclosef</code>. (This is +because, on many systems, the number of files which a single process +can have open is quite limited). It is important to use these +functions to close files allocated with <code>pfopen</code> and +<code>popenf</code>, since to do otherwise could cause fatal errors on +systems such as Linux, which react badly if the same +<code>FILE*</code> is closed more than once. <p> + +(Using the <code>close</code> functions is not mandatory, since the +file will eventually be closed regardless, but you should consider it +in cases where your module is opening, or could open, a lot of files). + +<h3>Other sorts of resources --- cleanup functions</h3> + +More text goes here. Describe the the cleanup primitives in terms of +which the file stuff is implemented; also, <code>spawn_process</code>. + +<h3>Fine control --- creating and dealing with sub-pools, with a note +on sub-requests</h3> + +On rare occasions, too-free use of <code>palloc()</code> and the +associated primitives may result in undesirably profligate resource +allocation. You can deal with such a case by creating a +<em>sub-pool</em>, allocating within the sub-pool rather than the main +pool, and clearing or destroying the sub-pool, which releases the +resources which were associated with it. (This really <em>is</em> a +rare situation; the only case in which it comes up in the standard +module set is in case of listing directories, and then only with +<em>very</em> large directories. Unnecessary use of the primitives +discussed here can hair up your code quite a bit, with very little +gain). <p> + +The primitive for creating a sub-pool is <code>make_sub_pool</code>, +which takes another pool (the parent pool) as an argument. When the +main pool is cleared, the sub-pool will be destroyed. The sub-pool +may also be cleared or destroyed at any time, by calling the functions +<code>clear_pool</code> and <code>destroy_pool</code>, respectively. +(The difference is that <code>clear_pool</code> frees resources +associated with the pool, while <code>destroy_pool</code> also +deallocates the pool itself. In the former case, you can allocate new +resources within the pool, and clear it again, and so forth; in the +latter case, it is simply gone). <p> + +One final note --- sub-requests have their own resource pools, which +are sub-pools of the resource pool for the main request. The polite +way to reclaim the resources associated with a sub request which you +have allocated (using the <code>sub_req_lookup_...</code> functions) +is <code>destroy_sub_request</code>, which frees the resource pool. +Before calling this function, be sure to copy anything that you care +about which might be allocated in the sub-request's resource pool into +someplace a little less volatile (for instance, the filename in its +<code>request_rec</code> structure). <p> + +(Again, under most circumstances, you shouldn't feel obliged to call +this function; only 2K of memory or so are allocated for a typical sub +request, and it will be freed anyway when the main request pool is +cleared. It is only when you are allocating many, many sub-requests +for a single main request that you should seriously consider the +<code>destroy...</code> functions). + +<h2><a name="config">Configuration, commands and the like</a></h2> + +One of the design goals for this server was to maintain external +compatibility with the NCSA 1.3 server --- that is, to read the same +configuration files, to process all the directives therein correctly, +and in general to be a drop-in replacement for NCSA. On the other +hand, another design goal was to move as much of the server's +functionality into modules which have as little as possible to do with +the monolithic server core. The only way to reconcile these goals is +to move the handling of most commands from the central server into the +modules. <p> + +However, just giving the modules command tables is not enough to +divorce them completely from the server core. The server has to +remember the commands in order to act on them later. That involves +maintaining data which is private to the modules, and which can be +either per-server, or per-directory. Most things are per-directory, +including in particular access control and authorization information, +but also information on how to determine file types from suffixes, +which can be modified by <code>AddType</code> and +<code>DefaultType</code> directives, and so forth. In general, the +governing philosophy is that anything which <em>can</em> be made +configurable by directory should be; per-server information is +generally used in the standard set of modules for information like +<code>Alias</code>es and <code>Redirect</code>s which come into play +before the request is tied to a particular place in the underlying +file system. <p> + +Another requirement for emulating the NCSA server is being able to +handle the per-directory configuration files, generally called +<code>.htaccess</code> files, though even in the NCSA server they can +contain directives which have nothing at all to do with access +control. Accordingly, after URI -> filename translation, but before +performing any other phase, the server walks down the directory +hierarchy of the underlying filesystem, following the translated +pathname, to read any <code>.htaccess</code> files which might be +present. The information which is read in then has to be +<em>merged</em> with the applicable information from the server's own +config files (either from the <code><Directory></code> sections +in <code>access.conf</code>, or from defaults in +<code>srm.conf</code>, which actually behaves for most purposes almost +exactly like <code><Directory /></code>).<p> + +Finally, after having served a request which involved reading +<code>.htaccess</code> files, we need to discard the storage allocated +for handling them. That is solved the same way it is solved wherever +else similar problems come up, by tying those structures to the +per-transaction resource pool. <p> + +<h3><a name="per-dir">Per-directory configuration structures</a></h3> + +Let's look out how all of this plays out in <code>mod_mime.c</code>, +which defines the file typing handler which emulates the NCSA server's +behavior of determining file types from suffixes. What we'll be +looking at, here, is the code which implements the +<code>AddType</code> and <code>AddEncoding</code> commands. These +commands can appear in <code>.htaccess</code> files, so they must be +handled in the module's private per-directory data, which in fact, +consists of two separate <code>table</code>s for MIME types and +encoding information, and is declared as follows: + +<pre> +typedef struct { + table *forced_types; /* Additional AddTyped stuff */ + table *encoding_types; /* Added with AddEncoding... */ +} mime_dir_config; +</pre> + +When the server is reading a configuration file, or +<code><Directory></code> section, which includes one of the MIME +module's commands, it needs to create a <code>mime_dir_config</code> +structure, so those commands have something to act on. It does this +by invoking the function it finds in the module's `create per-dir +config slot', with two arguments: the name of the directory to which +this configuration information applies (or <code>NULL</code> for +<code>srm.conf</code>), and a pointer to a resource pool in which the +allocation should happen. <p> + +(If we are reading a <code>.htaccess</code> file, that resource pool +is the per-request resource pool for the request; otherwise it is a +resource pool which is used for configuration data, and cleared on +restarts. Either way, it is important for the structure being created +to vanish when the pool is cleared, by registering a cleanup on the +pool if necessary). <p> + +For the MIME module, the per-dir config creation function just +<code>palloc</code>s the structure above, and a creates a couple of +<code>table</code>s to fill it. That looks like this: + +<pre> +void *create_mime_dir_config (pool *p, char *dummy) +{ + mime_dir_config *new = + (mime_dir_config *) palloc (p, sizeof(mime_dir_config)); + + new->forced_types = make_table (p, 4); + new->encoding_types = make_table (p, 4); + + return new; +} +</pre> + +Now, suppose we've just read in a <code>.htaccess</code> file. We +already have the per-directory configuration structure for the next +directory up in the hierarchy. If the <code>.htaccess</code> file we +just read in didn't have any <code>AddType</code> or +<code>AddEncoding</code> commands, its per-directory config structure +for the MIME module is still valid, and we can just use it. +Otherwise, we need to merge the two structures somehow. <p> + +To do that, the server invokes the module's per-directory config merge +function, if one is present. That function takes three arguments: +the two structures being merged, and a resource pool in which to +allocate the result. For the MIME module, all that needs to be done +is overlay the tables from the new per-directory config structure with +those from the parent: + +<pre> +void *merge_mime_dir_configs (pool *p, void *parent_dirv, void *subdirv) +{ + mime_dir_config *parent_dir = (mime_dir_config *)parent_dirv; + mime_dir_config *subdir = (mime_dir_config *)subdirv; + mime_dir_config *new = + (mime_dir_config *)palloc (p, sizeof(mime_dir_config)); + + new->forced_types = overlay_tables (p, subdir->forced_types, + parent_dir->forced_types); + new->encoding_types = overlay_tables (p, subdir->encoding_types, + parent_dir->encoding_types); + + return new; +} +</pre> + +As a note --- if there is no per-directory merge function present, the +server will just use the subdirectory's configuration info, and ignore +the parent's. For some modules, that works just fine (e.g., for the +includes module, whose per-directory configuration information +consists solely of the state of the <code>XBITHACK</code>), and for +those modules, you can just not declare one, and leave the +corresponding structure slot in the module itself <code>NULL</code>.<p> + +<h3><a name="commands">Command handling</a></h3> + +Now that we have these structures, we need to be able to figure out +how to fill them. That involves processing the actual +<code>AddType</code> and <code>AddEncoding</code> commands. To find +commands, the server looks in the module's <code>command table</code>. +That table contains information on how many arguments the commands +take, and in what formats, where it is permitted, and so forth. That +information is sufficient to allow the server to invoke most +command-handling functions with pre-parsed arguments. Without further +ado, let's look at the <code>AddType</code> command handler, which +looks like this (the <code>AddEncoding</code> command looks basically +the same, and won't be shown here): + +<pre> +char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->forced_types, ext, ct); + return NULL; +} +</pre> + +This command handler is unusually simple. As you can see, it takes +four arguments, two of which are pre-parsed arguments, the third being +the per-directory configuration structure for the module in question, +and the fourth being a pointer to a <code>cmd_parms</code> structure. +That structure contains a bunch of arguments which are frequently of +use to some, but not all, commands, including a resource pool (from +which memory can be allocated, and to which cleanups should be tied), +and the (virtual) server being configured, from which the module's +per-server configuration data can be obtained if required.<p> + +Another way in which this particular command handler is unusually +simple is that there are no error conditions which it can encounter. +If there were, it could return an error message instead of +<code>NULL</code>; this causes an error to be printed out on the +server's <code>stderr</code>, followed by a quick exit, if it is in +the main config files; for a <code>.htaccess</code> file, the syntax +error is logged in the server error log (along with an indication of +where it came from), and the request is bounced with a server error +response (HTTP error status, code 500). <p> + +The MIME module's command table has entries for these commands, which +look like this: + +<pre> +command_rec mime_cmds[] = { +{ "AddType", add_type, NULL, OR_FILEINFO, TAKE2, + "a mime type followed by a file extension" }, +{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, TAKE2, + "an encoding (e.g., gzip), followed by a file extension" }, +{ NULL } +}; +</pre> + +The entries in these tables are: + +<ul> + <li> The name of the command + <li> The function which handles it + <li> a <code>(void *)</code> pointer, which is passed in the + <code>cmd_parms</code> structure to the command handler --- + this is useful in case many similar commands are handled by the + same function. + <li> A bit mask indicating where the command may appear. There are + mask bits corresponding to each <code>AllowOverride</code> + option, and an additional mask bit, <code>RSRC_CONF</code>, + indicating that the command may appear in the server's own + config files, but <em>not</em> in any <code>.htaccess</code> + file. + <li> A flag indicating how many arguments the command handler wants + pre-parsed, and how they should be passed in. + <code>TAKE2</code> indicates two pre-parsed arguments. Other + options are <code>TAKE1</code>, which indicates one pre-parsed + argument, <code>FLAG</code>, which indicates that the argument + should be <code>On</code> or <code>Off</code>, and is passed in + as a boolean flag, <code>RAW_ARGS</code>, which causes the + server to give the command the raw, unparsed arguments + (everything but the command name itself). There is also + <code>ITERATE</code>, which means that the handler looks the + same as <code>TAKE1</code>, but that if multiple arguments are + present, it should be called multiple times, and finally + <code>ITERATE2</code>, which indicates that the command handler + looks like a <code>TAKE2</code>, but if more arguments are + present, then it should be called multiple times, holding the + first argument constant. + <li> Finally, we have a string which describes the arguments that + should be present. If the arguments in the actual config file + are not as required, this string will be used to help give a + more specific error message. (You can safely leave this + <code>NULL</code>). +</ul> + +Finally, having set this all up, we have to use it. This is +ultimately done in the module's handlers, specifically for its +file-typing handler, which looks more or less like this; note that the +per-directory configuration structure is extracted from the +<code>request_rec</code>'s per-directory configuration vector by using +the <code>get_module_config</code> function. + +<pre> +int find_ct(request_rec *r) +{ + int i; + char *fn = pstrdup (r->pool, r->filename); + mime_dir_config *conf = (mime_dir_config *) + get_module_config(r->per_dir_config, &mime_module); + char *type; + + if (S_ISDIR(r->finfo.st_mode)) { + r->content_type = DIR_MAGIC_TYPE; + return OK; + } + + if((i=rind(fn,'.')) < 0) return DECLINED; + ++i; + + if ((type = table_get (conf->encoding_types, &fn[i]))) + { + r->content_encoding = type; + + /* go back to previous extension to try to use it as a type */ + + fn[i-1] = '\0'; + if((i=rind(fn,'.')) < 0) return OK; + ++i; + } + + if ((type = table_get (conf->forced_types, &fn[i]))) + { + r->content_type = type; + } + + return OK; +} + +</pre> + +<h3><a name="servconf">Side notes --- per-server configuration, virtual servers, etc.</a></h3> + +The basic ideas behind per-server module configuration are basically +the same as those for per-directory configuration; there is a creation +function and a merge function, the latter being invoked where a +virtual server has partially overridden the base server configuration, +and a combined structure must be computed. (As with per-directory +configuration, the default if no merge function is specified, and a +module is configured in some virtual server, is that the base +configuration is simply ignored). <p> + +The only substantial difference is that when a command needs to +configure the per-server private module data, it needs to go to the +<code>cmd_parms</code> data to get at it. Here's an example, from the +alias module, which also indicates how a syntax error can be returned +(note that the per-directory configuration argument to the command +handler is declared as a dummy, since the module doesn't actually have +per-directory config data): + +<pre> +char *add_redirect(cmd_parms *cmd, void *dummy, char *f, char *url) +{ + server_rec *s = cmd->server; + alias_server_conf *conf = (alias_server_conf *) + get_module_config(s->module_config,&alias_module); + alias_entry *new = push_array (conf->redirects); + + if (!is_url (url)) return "Redirect to non-URL"; + + new->fake = f; new->real = url; + return NULL; +} +</pre> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</body></html> diff --git a/usr.sbin/httpd/htdocs/manual/misc/FAQ.html b/usr.sbin/httpd/htdocs/manual/misc/FAQ.html new file mode 100644 index 00000000000..ec8c8a1b07b --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/FAQ.html @@ -0,0 +1,2287 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Apache Server Frequently Asked Questions</TITLE> + </HEAD> + + <!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + <H1 ALIGN="CENTER">Apache Server Frequently Asked Questions</H1> + <P> + $Revision: 1.1 $ ($Date: 1998/03/25 07:08:35 $) + </P> + <P> + The latest version of this FAQ is always available from the main + Apache web site, at + <<A + HREF="http://www.apache.org/docs/misc/FAQ.html" + REL="Help" + ><SAMP>http://www.apache.org/docs/misc/FAQ.html</SAMP></A>>. + </P> +<!-- Notes about changes: --> +<!-- - If adding a relative link to another part of the --> +<!-- documentation, *do* include the ".html" portion. There's a --> +<!-- good chance that the user will be reading the documentation --> +<!-- on his own system, which may not be configured for --> +<!-- multiviews. --> +<!-- - When adding items, make sure they're put in the right place --> +<!-- - verify that the numbering matches up. --> +<!-- - *Don't* use <PRE></PRE> blocks - they don't appear --> +<!-- correctly in a reliable way when this is converted to text --> +<!-- with Lynx. Use <DL><DD><CODE>xxx<BR>xx</CODE></DD></DL> --> +<!-- blocks inside a <P></P> instead. This is necessary to get --> +<!-- the horizontal and vertical indenting right. --> +<!-- - Don't forget to include an HR tag after the last /P tag --> +<!-- but before the /LI in an item. --> + <P> + If you are reading a text-only version of this FAQ, you may find numbers + enclosed in brackets (such as "[12]"). These refer to the list of + reference URLs to be found at the end of the document. These references + do not appear, and are not needed, for the hypertext version. + </P> + <H2>The Questions</H2> +<!-- Stuff to Add: --> +<!-- - can't bind to port 80 --> +<!-- - permission denied --> +<!-- - address already in use --> +<!-- - mod_auth & passwd lines "user:pw:.*" - ++1st colon onward is --> +<!-- treated as pw, not just ++1st to --2nd. --> +<!-- - SSL: --> +<!-- - Can I use Apache-SSL for free in Canada? --> +<!-- - Why can't I use Apache-SSL in the U.S.? --> +<!-- - How can I found out how many visitors my site gets? --> +<!-- - How do I add a counter? --> +<!-- - How do I configure Apache as a proxy? --> +<!-- - What browsers support HTTP/1.1? --> +<!-- - What's the point of vhosts-by-name is there aren't any --> +<!-- HTTP/1.1 browsers? --> +<!-- - Is there an Apache for W95/WNT? --> +<!-- - Why does Apache die when a vhost can't be DNS-resolved? --> +<!-- - Why do I get "send lost connection" messages in my error --> +<!-- log? --> +<!-- - specifically consider .pdf files which seem to cause this --> +<!-- a lot when accessed via the plugin ... and also mention --> +<!-- how range-requests can cause bytes served < file size --> +<!-- - Why do directory indexes appear as garbage? (A: -lucb) --> +<!-- - How do I add a footer to all pages offered by my server? --> +<!-- - Fix midi question; a bigger problem than midi vs. x-midi is --> +<!-- the simple fact that older versions of Apache (and new ones --> +<!-- that have been upgraded without upgrading the mime.types --> +<!-- file) don't have the type listed at all. --> +<!-- - Why is my .htaccess ignored? --> +<!-- - RewriteRule /~fraggle/* /cgi-bin/fraggle.pl does not work --> +<UL> + <LI><STRONG>Background</STRONG> + <OL START=1> + <LI><A HREF="#what">What is Apache?</A> + </LI> + <LI><A HREF="#why">Why was Apache created?</A> + </LI> + <LI><A HREF="#relate">How does The Apache Group's work relate to + other servers?</A> + </LI> + <LI><A HREF="#name">Why the name "Apache"?</A> + </LI> + <LI><A HREF="#compare">OK, so how does Apache compare to other servers?</A> + </LI> + <LI><A HREF="#tested">How thoroughly tested is Apache?</A> + </LI> + <LI><A HREF="#future">What are the future plans for Apache?</A> + </LI> + <LI><A HREF="#support">Whom do I contact for support?</A> + </LI> + <LI><A HREF="#more">Is there any more information on Apache?</A> + </LI> + <LI><A HREF="#where">Where can I get Apache?</A> + </LI> + </OL> + </LI> + <LI><STRONG>Technical Questions</STRONG> + <OL START=11> + <LI><A HREF="#what2do">"Why can't I ...? Why won't ... + work?" What to do in case of problems</A> + </LI> + <LI><A HREF="#compatible">How compatible is Apache with my existing + NCSA 1.3 setup?</A> + </LI> + <LI><A HREF="#CGIoutsideScriptAlias">How do I enable CGI execution + in directories other than the ScriptAlias?</A> + </LI> + <LI><A HREF="#premature-script-headers">What does it mean when my + CGIs fail with "<SAMP>Premature end of script + headers</SAMP>"?</A> + </LI> + <LI><A HREF="#ssi-part-i">How do I enable SSI (parsed HTML)?</A> + </LI> + <LI><A HREF="#ssi-part-ii">Why don't my parsed files get cached?</A> + </LI> + <LI><A HREF="#ssi-part-iii">How can I have my script output parsed?</A> + </LI> + <LI><A HREF="#proxy">Does or will Apache act as a Proxy server?</A> + </LI> + <LI><A HREF="#multiviews">What are "multiviews"?</A> + </LI> + <LI><A HREF="#fdlim">Why can't I run more than <<EM>n</EM>> + virtual hosts?</A> + </LI> + <LI><A HREF="#freebsd-setsize">Can I increase FD_SETSIZE on FreeBSD?</A> + </LI> + <LI><A HREF="#limitGET">Why do I keep getting "access denied" for + form POST requests?</A> + </LI> + <LI><A HREF="#passwdauth">Can I use my <SAMP>/etc/passwd</SAMP> file + for Web page authentication?</A> + </LI> + <LI><A HREF="#errordoc401">Why doesn't my <CODE>ErrorDocument + 401</CODE> work?</A> + </LI> + <LI><A HREF="#errordocssi">How can I use <CODE>ErrorDocument</CODE> + and SSI to simplify customized error messages?</A> + </LI> + <LI><A HREF="#setgid">Why do I get "<SAMP>setgid: Invalid + argument</SAMP>" at startup?</A> + </LI> + <LI><A HREF="#cookies1">Why does Apache send a cookie on every response?</A> + </LI> + <LI><A HREF="#cookies2">Why don't my cookies work, I even compiled in + <SAMP>mod_cookies</SAMP>?</A> + </LI> + <LI><A HREF="#jdk1-and-http1.1">Why do my Java app[let]s give me plain text + when I request an URL from an Apache server?</A> + </LI> + <LI><A HREF="#putsupport">Why can't I publish to my Apache server + using PUT on Netscape Gold and other programs?</A> + </LI> + <LI><A HREF="#fastcgi">Why isn't FastCGI included with Apache any + more?</A> + </LI> + <LI><A HREF="#nodelay">Why am I getting "<SAMP>httpd: could not + set socket option TCP_NODELAY</SAMP>" in my error log?</A> + </LI> + <LI><A HREF="#peerreset">Why am I getting "<SAMP>connection + reset by peer</SAMP>" in my error log?</A> + </LI> + <LI><A HREF="#nph-scripts">How can I get my script's output without + Apache buffering it? Why doesn't my server push work?</A> + </LI> + <LI><A HREF="#linuxiovec">Why do I get complaints about redefinition + of "<CODE>struct iovec</CODE>" when compiling under Linux?</A> + </LI> + <LI><A HREF="#wheres-the-dump">The errorlog says Apache dumped core, + but where's the dump file?</A> + </LI> + <LI><A HREF="#dnsauth">Why isn't restricting access by host or domain name + working correctly?</A> + </LI> + <LI><A HREF="#SSL-i">Why doesn't Apache include SSL?</A> + </LI> + <LI><A HREF="#HPUX-core">Why do I get core dumps under HPUX using + HP's ANSI C compiler?</A> + </LI> + <LI><A HREF="#midi">How do I get Apache to send a MIDI file so the + browser can play it?</A> + </LI> + <LI><A HREF="#cantbuild">Why won't Apache compile with my + system's <SAMP>cc</SAMP>?</A> + </LI> + <LI><A HREF="#addlog">How do I add browsers and referrers to my + logs?</A> + </LI> + <LI><A HREF="#bind8.1">Why do I get an error about an undefined + reference to "<SAMP>__inet_ntoa</SAMP>" or other + <SAMP>__inet_*</SAMP> symbols?</A> + </LI> + <LI><A HREF="#set-servername">Why does accessing directories only work + when I include the trailing "/" + (<EM>e.g.</EM>, <SAMP>http://foo.domain.com/~user/</SAMP>) but + not when I omit it + (<EM>e.g.</EM>, <SAMP>http://foo.domain.com/~user</SAMP>)?</A> + </LI> + <LI><A HREF="#user-authentication">How do I set up Apache to require + a username and password to access certain documents?</A> + </LI> + <LI><A HREF="#remote-user-var">Why is the environment variable + <SAMP>REMOTE_USER</SAMP> not set?</A> + </LI> + <LI><A HREF="#remote-auth-only">How do I set up Apache to allow access + to certain documents only if a site is either a local site + <EM>or</EM> the user supplies a password and username?</A> + </LI> + <LI><A HREF="#no-info-directives">Why doesn't mod_info list any + directives?</A> + <LI><A HREF="#linux-shmget">When I run it under Linux I get "shmget: + function not found", what should I do?</A> + </LI> + <LI><A HREF="#authauthoritative">Why does my authentication give + me a server error?</A> + <LI><A HREF="#auth-on-same-machine">Do I have to keep the (mSQL) + authentication information on the same machine?</A> + </LI> + <LI><A HREF="#msql-slow">Why is my mSQL authentication terribly slow?</A> + </LI> + <LI><A HREF="#rewrite-more-config">Where can I find mod_rewrite rulesets + which already solve particular URL-related problems?</A> + </LI> + <LI><A HREF="#rewrite-article">Where can I find any published information about + URL-manipulations and mod_rewrite?</A> + </LI> + <LI><A HREF="#rewrite-complexity">Why is mod_rewrite so difficult to learn + and seems so complicated?</A> + </LI> + <LI><A HREF="#rewrite-dontwork">What can I do if my RewriteRules don't work + as expected?</A> + </LI> + <LI><A HREF="#rewrite-prefixdocroot">Why don't some of my URLs get + prefixed with DocumentRoot when using mod_rewrite?</A> + </LI> + <LI><A HREF="#rewrite-nocase">How can I make all my URLs case-insensitive + with mod_rewrite?</A> + </LI> + <LI><A HREF="#rewrite-virthost">Why are RewriteRules in my VirtualHost + parts ignored?</A> + </LI> + <LI><A HREF="#rewrite-envwhitespace">How can I use strings with whitespaces + in RewriteRule's ENV flag?</A> + </LI> + <LI><A HREF="#cgi-spec">Where can I find the "CGI + specification"?</A> + </LI> + <LI><A HREF="#year2000">Is Apache Year 2000 compliant?</A> + </LI> + <LI><A HREF="#namevhost">I upgraded to Apache 1.3b and now my + virtual hosts don't work!</A> + </LI> + <LI><A HREF="#redhat">I'm using RedHat Linux and I have problems with httpd + dying randomly or not restarting properly</A> + </LI> + <li><a href="#stopping">I upgraded from an Apache version earlier + than 1.2.0 and suddenly I have problems with Apache dying randomly + or not restarting properly</a> + </li> + <li><a href="#redhat-htm">I'm using RedHat Linux and my .htm files are showing + up as html source rather than being formatted!</a> + </li> + <li><a href="#glibc-crypt">I'm using RedHat Linux 5.0, or some other glibc + based Linux system, and I get errors with the <code>crypt</code> function when + I attempt to build Apache 1.2.</a> + </li> + </OL> + </LI> +</UL> + +<HR> + + <H2>The Answers</H2> + <H3> + Background + </H3> +<OL START=1> + <LI><A NAME="what"> + <STRONG>What is Apache?</STRONG> + </A> + <P> + Apache was originally based on code and ideas found in the most + popular HTTP server of the time.. NCSA httpd 1.3 (early 1995). It has + since evolved into a far superior system which can rival (and probably + surpass) almost any other UNIX based HTTP server in terms of functionality, + efficiency and speed. + </P> + <P> + Since it began, it has been completely rewritten, and includes many new + features. Apache is, as of January 1997, the most popular WWW server on + the Internet, according to the + <A + HREF="http://www.netcraft.com/Survey/" + >Netcraft Survey</A>. + </P> + <HR> + </LI> + <LI><A NAME="why"> + <STRONG>Why was Apache created?</STRONG> + </A> + <P> + To address the concerns of a group of WWW providers and part-time httpd + programmers that httpd didn't behave as they wanted it to behave. + Apache is an entirely volunteer effort, completely funded by its + members, not by commercial sales. + </P> + <HR> + </LI> + <LI><A NAME="relate"> + <STRONG>How does The Apache Group's work relate to other + server efforts, such as NCSA's?</STRONG> + </A> + <P> + We, of course, owe a great debt to NCSA and their programmers for + making the server Apache was based on. We now, however, have our own + server, and our project is mostly our own. The Apache Project is an + entirely independent venture. + </P> + <HR> + </LI> + <LI><A NAME="name"> + <STRONG>Why the name "Apache"?</STRONG> + </A> + <P> + A cute name which stuck. Apache is "<STRONG>A + PA</STRONG>t<STRONG>CH</STRONG>y server". It was + based on some existing code and a series of "patch files". + </P> + <HR> + </LI> + <LI><A NAME="compare"> + <STRONG>OK, so how does Apache compare to other servers?</STRONG> + </A> + <P> + For an independent assessment, see + <A + HREF="http://webcompare.internet.com/chart.html" + >Web Compare</A>'s + comparison chart. + </P> + <P> + Apache has been shown to be substantially faster than many other + free servers. Although certain commercial servers have claimed to + surpass Apache's speed (it has not been demonstrated that any of these + "benchmarks" are a good way of measuring WWW server speed at any + rate), we feel that it is better to have a mostly-fast free server + than an extremely-fast server that costs thousands of dollars. Apache + is run on sites that get millions of hits per day, and they have + experienced no performance difficulties. + </P> + <HR> + </LI> + <LI><A NAME="tested"> + <STRONG>How thoroughly tested is Apache?</STRONG> + </A> + <P> + Apache is run on over 500,000 Internet servers (as of July 1997). It has + been tested thoroughly by both developers and users. The Apache Group + maintains rigorous standards before releasing new versions of their + server, and our server runs without a hitch on over one third of all + WWW servers available on the Internet. When bugs do show up, we + release patches and new versions as soon as they are available. + </P> + <P> + The Apache project's web site includes a page with a partial list of + <A + HREF="http://www.apache.org/info/apache_users.html" + >sites running Apache</A>. + </P> + <HR> + </LI> + <LI><A NAME="future"> + <STRONG>What are the future plans for Apache?</STRONG> + </A> + <P> + <UL> + <LI>to continue as a public domain HTTP server, + </LI> + <LI>to keep up with advances in HTTP protocol and web developments in + general, + </LI> + <LI>to collect suggestions for fixes/improvements from its users, + </LI> + <LI>to respond to needs of large volume providers as well as + occasional users. + </LI> + </UL> + </P> + <HR> + </LI> + <LI><A NAME="support"> + <STRONG>Whom do I contact for support?</STRONG> + </A> + <P> + There is no official support for Apache. None of the developers want to + be swamped by a flood of trivial questions that can be resolved elsewhere. + Bug reports and suggestions should be sent <EM>via</EM> + <A + HREF="http://www.apache.org/bug_report.html" + >the bug report page</A>. + Other questions should be directed to the + <A + HREF="news:comp.infosystems.www.servers.unix" + ><SAMP>comp.infosystems.www.servers.unix</SAMP></A> + newsgroup, where some of the Apache team lurk, + in the company of many other httpd gurus who should be able + to help. + </P> + <P> + Commercial support for Apache is, however, available from a number + of third parties. + </P> + <HR> + </LI> + <LI><A NAME="more"> + <STRONG>Is there any more information available on + Apache?</STRONG> + </A> + <P> + Indeed there is. See the main + <A + HREF="http://www.apache.org/" + >Apache web site</A>. + There is also a regular electronic publication called + <A + HREF="http://www.apacheweek.com/" + REL="Help" + ><CITE>Apache Week</CITE></A> + available. Links to relevant <CITE>Apache Week</CITE> articles are + included below where appropriate. There are also some + <A + HREF="http://www.apache.org/info/apache_books.html" + >Apache-specific books</A> available. + </P> + <HR> + </LI> + <LI><A NAME="where"> + <STRONG>Where can I get Apache?</STRONG> + </A> + <P> + You can find out how to download the source for Apache at the + project's + <A + HREF="http://www.apache.org/" + >main web page</A>. + </P> + <HR> + </LI> +</OL> + <H3> + Technical Questions + </H3> +<OL START=11> + <LI><A NAME="what2do"> + <STRONG>"Why can't I ...? Why won't ... work?" What to + do in case of problems</STRONG> + </A> + <P> + If you are having trouble with your Apache server software, you should + take the following steps: + </P> + <OL> + <LI><STRONG>Check the errorlog!</STRONG> + <P> + Apache tries to be helpful when it encounters a problem. In many + cases, it will provide some details by writing one or messages to + the server error log. Sometimes this is enough for you to diagnose + & fix the problem yourself (such as file permissions or the like). + The default location of the error log is + <SAMP>/usr/local/apache/logs/error_log</SAMP>, but see the + <A + HREF="../mod/core.html#errorlog" + ><SAMP>ErrorLog</SAMP></A> + directive in your config files for the location on your server. + </P> + </LI> + <LI><STRONG>Check the + <A + HREF="http://www.apache.org/docs/misc/FAQ.html" + >FAQ</A>!</STRONG> + <P> + The latest version of the Apache Frequently-Asked Questions list can + always be found at the main Apache web site. + </P> + </LI> + <LI><STRONG>Check the Apache bug database</STRONG> + <P> + Most problems that get reported to The Apache Group are recorded in + the + <A + HREF="http://bugs.apache.org/" + >bug database</A>. + <EM><STRONG>Please</STRONG> check the existing reports, open + <STRONG>and</STRONG> closed, before adding one.</EM> If you find + that your issue has already been reported, please <EM>don't</EM> add + a "me, too" report. If the original report isn't closed + yet, we suggest that you check it periodically. You might also + consider contacting the original submitter, because there may be an + email exchange going on about the issue that isn't getting recorded + in the database. + </P> + </LI> + <LI><STRONG>Ask in the <SAMP>comp.infosystems.www.servers.unix</SAMP> + USENET newsgroup</STRONG> + <P> + A lot of common problems never make it to the bug database because + there's already high Q&A traffic about them in the + <A + HREF="news:comp.infosystems.www.servers.unix" + ><SAMP>comp.infosystems.www.servers.unix</SAMP></A> + newsgroup. Many Apache users, and some of the developers, can be + found roaming its virtual halls, so it is suggested that you seek + wisdom there. The chances are good that you'll get a faster answer + there than from the bug database, even if you <EM>don't</EM> see + your question already posted. + </P> + </LI> + <LI><STRONG>If all else fails, report the problem in the bug + database</STRONG> + <P> + If you've gone through those steps above that are appropriate and + have obtained no relief, then please <EM>do</EM> let The Apache + Group know about the problem by + <A + HREF="http://www.apache.org/bug_report.html" + >logging a bug report</A>. + </P> + <P> + If your problem involves the server crashing and generating a core + dump, please include a backtrace (if possible). As an example, + </P> + <P> + <DL> + <DD><CODE># cd <EM>ServerRoot</EM><BR> + # dbx httpd core<BR> + (dbx) where</CODE> + </DD> + </DL> + </P> + <P> + (Substitute the appropriate locations for your + <SAMP>ServerRoot</SAMP> and your <SAMP>httpd</SAMP> and + <SAMP>core</SAMP> files. You may have to use <CODE>gdb</CODE> + instead of <CODE>dbx</CODE>.) + </P> + </LI> + </OL> + <HR> + </LI> + <LI><A NAME="compatible"> + <STRONG>How compatible is Apache with my existing NCSA 1.3 + setup?</STRONG> + </A> + <P> + Apache attempts to offer all the features and configuration options + of NCSA httpd 1.3, as well as many of the additional features found in + NCSA httpd 1.4 and NCSA httpd 1.5. + </P> + <P> + NCSA httpd appears to be moving toward adding experimental features + which are not generally required at the moment. Some of the experiments + will succeed while others will inevitably be dropped. The Apache + philosophy is to add what's needed as and when it is needed. + </P> + <P> + Friendly interaction between Apache and NCSA developers should ensure + that fundamental feature enhancements stay consistent between the two + servers for the foreseeable future. + </P> + <HR> + </LI> + <LI><A NAME="CGIoutsideScriptAlias"> + <STRONG>How do I enable CGI execution in directories other than + the ScriptAlias?</STRONG> + </A> + <P> + Apache recognizes all files in a directory named as a + <A + HREF="../mod/mod_alias.html#scriptalias" + ><SAMP>ScriptAlias</SAMP></A> + as being eligible for execution rather than processing as normal + documents. This applies regardless of the file name, so scripts in a + ScriptAlias directory don't need to be named + "<SAMP>*.cgi</SAMP>" or "<SAMP>*.pl</SAMP>" or + whatever. In other words, <EM>all</EM> files in a ScriptAlias + directory are scripts, as far as Apache is concerned. + </P> + <P> + To persuade Apache to execute scripts in other locations, such as in + directories where normal documents may also live, you must tell it how + to recognize them - and also that it's okay to execute them. For + this, you need to use something like the + <A + HREF="../mod/mod_mime.html#addhandler" + ><SAMP>AddHandler</SAMP></A> + directive. + </P> + <P> + <OL> + <LI>In an appropriate section of your server configuration files, add + a line such as + <P> + <DL> + <DD><CODE>AddHandler cgi-script .cgi</CODE> + </DD> + </DL> + </P> + <P> + The server will then recognize that all files in that location (and + its logical descendants) that end in "<SAMP>.cgi</SAMP>" + are script files, not documents. + </P> + </LI> + <LI>Make sure that the directory location is covered by an + <A + HREF="../mod/core.html#options" + ><SAMP>Options</SAMP></A> + declaration that includes the <SAMP>ExecCGI</SAMP> option. + </LI> + </OL> + </P> + <P> + In some situations it can be not conform to your local policy to actually + allow all files named "<SAMP>*.cgi</SAMP>" to be executable. + Perhaps all you want is to enable a particular file in a normal directory to + be executable. This can be alternatively accomplished + <EM>via</EM> + <A + HREF="../mod/mod_rewrite.html" + ><SAMP>mod_rewrite</SAMP></A> + and the following steps: + </P> + <P> + <OL> + <LI>Locally add to the corresponding <SAMP>.htaccess</SAMP> file a ruleset + similar to this one: + <P> + <DL> + <DD><CODE>RewriteEngine on + <BR> + RewriteBase /~foo/bar/ + <BR> + RewriteRule ^quux\.cgi$ - [T=application/x-httpd-cgi]</CODE> + </DD> + </DL> + </P> + </LI> + <LI>Make sure that the directory location is covered by an + <A + HREF="../mod/core.html#options" + ><SAMP>Options</SAMP></A> + declaration that includes the <SAMP>ExecCGI</SAMP> and + <SAMP>FollowSymLinks</SAMP> option. + </LI> + </OL> + </P> + <HR> + </LI> + <LI><A NAME="premature-script-headers"> + <STRONG>What does it mean when my CGIs fail with + "<SAMP>Premature end of script headers</SAMP>"?</STRONG> + </A> + <P> + It means just what it says: the server was expecting a complete set of + HTTP headers (one or more followed by a blank line), and didn't get + them. + </P> + <P> + The most common cause of this problem is the script dying before + sending the complete set of headers, or possibly any at all, to the + server. To see if this is the case, try running the script standalone + from an interactive session, rather than as a script under the server. + If you get error messages, this is almost certainly the cause of the + "premature end of script headers" message. + </P> + <P> + The second most common cause of this (aside from people not + outputting the required headers at all) is a result of an interaction + with Perl's output buffering. To make Perl flush its buffers + after each output statement, insert the following statements around + the <CODE>print</CODE> or <CODE>write</CODE> statements that send your + HTTP headers: + </P> + <P> + <DL> + <DD><CODE>{<BR> + local ($oldbar) = $|;<BR> + $cfh = select (STDOUT);<BR> + $| = 1;<BR> + #<BR> + # print your HTTP headers here<BR> + #<BR> + $| = $oldbar;<BR> + select ($cfh);<BR> + }</CODE> + </DD> + </DL> + </P> + <P> + This is generally only necessary when you are calling external + programs from your script that send output to stdout, or if there will + be a long delay between the time the headers are sent and the actual + content starts being emitted. To maximize performance, you should + turn buffer-flushing back <EM>off</EM> (with <CODE>$| = 0</CODE> or the + equivalent) after the statements that send the headers, as displayed + above. + </P> + <P> + If your script isn't written in Perl, do the equivalent thing for + whatever language you <EM>are</EM> using (<EM>e.g.</EM>, for C, call + <CODE>fflush()</CODE> after writing the headers). + </P> + <HR> + </LI> + <LI><A NAME="ssi-part-i"> + <STRONG>How do I enable SSI (parsed HTML)?</STRONG> + </A> + <P> + SSI (an acronym for Server-Side Include) directives allow static HTML + documents to be enhanced at run-time (<EM>e.g.</EM>, when delivered to + a client by Apache). The format of SSI directives is covered + in the <A HREF="../mod/mod_include.html">mod_include manual</A>; + suffice it to say that Apache supports not only SSI but + xSSI (eXtended SSI) directives. + </P> + <P> + Processing a document at run-time is called <EM>parsing</EM> it; hence + the term "parsed HTML" sometimes used for documents that + contain SSI instructions. Parsing tends to be <EM>extremely</EM> + resource-consumptive, and is not enabled by default. It can also + interfere with the cachability of your documents, which can put a + further load on your server. (see the + <A + HREF="#ssi-part-ii" + >next question</A> + for more information about this.) + </P> + <P> + To enable SSI processing, you need to + </P> + <UL> + <LI>Build your server with the + <A + HREF="../mod/mod_include.html" + ><SAMP>mod_include</SAMP></A> + module. This is normally compiled in by default. + </LI> + <LI>Make sure your server configuration files have an + <A + HREF="../mod/core.html#options" + ><SAMP>Options</SAMP></A> + directive which permits <SAMP>Includes</SAMP>. + </LI> + <LI>Make sure that the directory where you want the SSI documents to + live is covered by the "server-parsed" content handler, + either explicitly or in some ancestral location. That can be done + with the following + <A + HREF="../mod/mod_mime.html#addhandler" + ><SAMP>AddHandler</SAMP></A> + directive: + <P> + <DL> + <DD><CODE>AddHandler server-parsed .shtml</CODE> + </DD> + </DL> + </P> + This indicates that all files ending in ".shtml" in that + location (or its descendants) should be parsed. Note that using + ".html" will cause all normal HTML files to be parsed, + which may put an inordinate load on your server. + </LI> + </UL> + <P> + For additional information, see the <CITE>Apache Week</CITE> article + on + <A + HREF="http://www.apacheweek.com/features/ssi" + REL="Help" + ><CITE>Using Server Side Includes</CITE></A>. + </P> + <HR> + </LI> + <LI><A NAME="ssi-part-ii"> + <STRONG>Why don't my parsed files get cached?</STRONG> + </A> + <P> + Since the server is performing run-time processing of your SSI + directives, which may change the content shipped to the client, it + can't know at the time it starts parsing what the final size of the + result will be, or whether the parsed result will always be the same. + This means that it can't generate <SAMP>Content-Length</SAMP> or + <SAMP>Last-Modified</SAMP> headers. Caches commonly work by comparing + the <SAMP>Last-Modified</SAMP> of what's in the cache with that being + delivered by the server. Since the server isn't sending that header + for a parsed document, whatever's doing the caching can't tell whether + the document has changed or not - and so fetches it again to be on the + safe side. + </P> + <P> + You can work around this in some cases by causing an + <SAMP>Expires</SAMP> header to be generated. (See the + <A + HREF="../mod/mod_expires.html" + REL="Help" + ><SAMP>mod_expires</SAMP></A> + documentation for more details.) Another possibility is to use the + <A + HREF="../mod/mod_include.html#xbithack" + REL="Help" + ><SAMP>XBitHack Full</SAMP></A> + mechanism, which tells Apache to send (under certain circumstances + detailed in the XBitHack directive description) a + <SAMP>Last-Modified</SAMP> header based upon the last modification + time of the file being parsed. Note that this may actually be lying + to the client if the parsed file doesn't change but the SSI-inserted + content does; if the included content changes often, this can result + in stale copies being cached. + </P> + <HR> + </LI> + <LI><A NAME="ssi-part-iii"> + <STRONG>How can I have my script output parsed?</STRONG> + </A> + <P> + So you want to include SSI directives in the output from your CGI + script, but can't figure out how to do it? + The short answer is "you can't." This is potentially + a security liability and, more importantly, it can not be cleanly + implemented under the current server API. The best workaround + is for your script itself to do what the SSIs would be doing. + After all, it's generating the rest of the content. + </P> + <P> + This is a feature The Apache Group hopes to add in the next major + release after 1.3. + </P> + <HR> + </LI> + <LI><A NAME="proxy"> + <STRONG>Does or will Apache act as a Proxy server?</STRONG> + </A> + <P> + Apache version 1.1 and above comes with a + <A + HREF="../mod/mod_proxy.html" + >proxy module</A>. + If compiled + in, this will make Apache act as a caching-proxy server. + </P> + <HR> + </LI> + <LI><A NAME="multiviews"> + <STRONG>What are "multiviews"?</STRONG> + </A> + <P> + "Multiviews" is the general name given to the Apache + server's ability to provide language-specific document variants in + response to a request. This is documented quite thoroughly in the + <A + HREF="../content-negotiation.html" + REL="Help" + >content negotiation</A> + description page. In addition, <CITE>Apache Week</CITE> carried an + article on this subject entitled + "<A + HREF="http://www.apacheweek.com/features/negotiation" + REL="Help" + ><CITE>Content Negotiation Explained</CITE></A>". + </P> + <HR> + </LI> + <LI><A NAME="fdlim"> + <STRONG>Why can't I run more than <<EM>n</EM>> + virtual hosts?</STRONG> + </A> + <P> + You are probably running into resource limitations in your + operating system. The most common limitation is the + <EM>per</EM>-process limit on <STRONG>file descriptors</STRONG>, + which is almost always the cause of problems seen when adding + virtual hosts. Apache often does not give an intuitive error + message because it is normally some library routine (such as + <CODE>gethostbyname()</CODE>) which needs file descriptors and + doesn't complain intelligibly when it can't get them. + </P> + <P> + Each log file requires a file descriptor, which means that if you are + using separate access and error logs for each virtual host, each + virtual host needs two file descriptors. Each + <A + HREF="../mod/core.html#listen" + ><SAMP>Listen</SAMP></A> + directive also needs a file descriptor. + </P> + <P> + Typical values for <<EM>n</EM>> that we've seen are in + the neighborhood of 128 or 250. When the server bumps into the file + descriptor limit, it may dump core with a SIGSEGV, it might just + hang, or it may limp along and you'll see (possibly meaningful) errors + in the error log. One common problem that occurs when you run into + a file descriptor limit is that CGI scripts stop being executed + properly. + </P> + <P> + As to what you can do about this: + </P> + <OL> + <LI>Reduce the number of + <A + HREF="../mod/core.html#listen" + ><SAMP>Listen</SAMP></A> + directives. If there are no other servers running on the machine + on the same port then you normally don't + need any Listen directives at all. By default Apache listens to + all addresses on port 80. + </LI> + <LI>Reduce the number of log files. You can use + <A + HREF="../mod/mod_log_config.html" + ><SAMP>mod_log_config</SAMP></A> + to log all requests to a single log file while including the name + of the virtual host in the log file. You can then write a + script to split the logfile into separate files later if + necessary. + </LI> + <LI>Increase the number of file descriptors available to the server + (see your system's documentation on the <CODE>limit</CODE> or + <CODE>ulimit</CODE> commands). For some systems, information on + how to do this is available in the + <A + HREF="perf.html" + >performance hints</A> + page. There is a specific note for + <A HREF="#freebsd-setsize">FreeBSD</A> below. + </LI> + <LI>"Don't do that" - try to run with fewer virtual hosts + </LI> + <LI>Spread your operation across multiple server processes (using + <A + HREF="../mod/core.html#listen" + ><SAMP>Listen</SAMP></A> + for example, but see the first point) and/or ports. + </LI> + </OL> + <P> + Since this is an operating-system limitation, there's not much else + available in the way of solutions. + </P> + <P> + As of 1.2.1 we have made attempts to work around various limitations + involving running with many descriptors. + <A HREF="descriptors.html">More information is available.</A> + </P> + <HR> + </LI> + + <LI><A NAME="freebsd-setsize"> + <STRONG>Can I increase <SAMP>FD_SETSIZE</SAMP> on FreeBSD?</STRONG> + </A> + <P> + On versions of FreeBSD before 3.0, the <SAMP>FD_SETSIZE</SAMP> define + defaults to 256. This means that you will have trouble usefully using + more than 256 file descriptors in Apache. This can be increased, but + doing so can be tricky. + + If you are using a version prior to 2.2, you need to recompile your + kernel with a larger FD_SETSIZE. This can be done by adding a + line such as: + </P> + <DL> + <DD><CODE>options FD_SETSIZE <EM>nnn</EM></CODE> + </DD> + </DL> + <P> + To your kernel config file. Starting at version 2.2, this is no + longer necessary. + </P> + <P> + If you are using a version of 2.1-stable from after 1997/03/10 or + 2.2 or 3.0-current from before 1997/06/28, there is a limit in + the resolver library that prevents it from using more file descriptors + than what FD_SETSIZE is set to when libc is compiled. To increase + this, you have to recompile libc with a higher FD_SETSIZE. + </P> + <P> + In FreeBSD 3.0, the default FD_SETSIZE has been increased to + 1024 and the above limitation in the resolver library + has been removed. + </P> + <P> + After you deal with the appropriate changes above, you can increase + the setting of <SAMP>FD_SETSIZE</SAMP> at Apache compilation time + by adding "<SAMP>-DFD_SETSIZE=<EM>nnn</EM></SAMP>" to the + <SAMP>EXTRA_CFLAGS</SAMP> line in your <SAMP>Configuration</SAMP> + file. + </P> + <HR> + </LI> + + <LI><A NAME="limitGET"> + <STRONG>Why do I keep getting "access denied" for form POST + requests?</STRONG> + </A> + <P> + The most common cause of this is a <SAMP><Limit></SAMP> section + that only names the <SAMP>GET</SAMP> method. Look in your + configuration files for something that resembles the following and + would affect the location where the POST-handling script resides: + </P> + <P> + <DL> + <DD><CODE><Limit GET> + <BR> :</CODE> + </DD> + </DL> + </P> + <P> + Change that to <CODE><Limit GET POST></CODE> and the problem + will probably go away. Better yet, remove the + <CODE><Limit></CODE> and <CODE></Limit></CODE> lines + altogether unless you're <EM>specifically</EM> trying to limit by + method (<SAMP>GET</SAMP>, <SAMP>PUT</SAMP>, <EM>et cetera</EM>). If + you don't have a <CODE><Limit></CODE> container, the + restrictions apply equally to <EM>all</EM> methods. + </P> + <HR> + </LI> + <LI><A NAME="passwdauth"> + <STRONG>Can I use my <SAMP>/etc/passwd</SAMP> file + for Web page authentication?</STRONG> + </A> + <P> + Yes, you can - but it's a <STRONG>very bad idea</STRONG>. Here are + some of the reasons: + </P> + <UL> + <LI>The Web technology provides no governors on how often or how + rapidly password (authentication failure) retries can be made. That + means that someone can hammer away at your system's + <SAMP>root</SAMP> password using the Web, using a dictionary or + similar mass attack, just as fast as the wire and your server can + handle the requests. Most operating systems these days include + attack detection (such as <EM>n</EM> failed passwords for the same + account within <EM>m</EM> seconds) and evasion (breaking the + connection, disabling the account under attack, disabling + <EM>all</EM> logins from that source, <EM>et cetera</EM>), but the + Web does not. + </LI> + <LI>An account under attack isn't notified (unless the server is + heavily modified); there's no "You have 19483 login + failures" message when the legitimate owner logs in. + </LI> + <LI>Without an exhaustive and error-prone examination of the server + logs, you can't tell whether an account has been compromised. + Detecting that an attack has occurred, or is in progress, is fairly + obvious, though - <EM>if</EM> you look at the logs. + </LI> + <LI>Web authentication passwords (at least for Basic authentication) + generally fly across the wire, and through intermediate proxy + systems, in what amounts to plain text. "O'er the net we + go/Caching all the way;/O what fun it is to surf/Giving my password + away!" + </LI> + <LI>Since HTTP is stateless, information about the authentication is + transmitted <EM>each and every time</EM> a request is made to the + server. Essentially, the client caches it after the first + successful access, and transmits it without asking for all + subsequent requests to the same server. + </LI> + <LI>It's relatively trivial for someone on your system to put up a + page that will steal the cached password from a client's cache + without them knowing. Can you say "password grabber"? + </LI> + </UL> + <P> + If you still want to do this in light of the above disadvantages, the + method is left as an exercise for the reader. It'll void your Apache + warranty, though, and you'll lose all accumulated UNIX guru points. + </P> + <HR> + </LI> + <LI><A NAME="errordoc401"> + <STRONG>Why doesn't my <CODE>ErrorDocument 401</CODE> work?</STRONG> + </A> + <P> + You need to use it with a URL in the form + "<SAMP>/foo/bar</SAMP>" and not one with a method and + hostname such as "<SAMP>http://host/foo/bar</SAMP>". See the + <A + HREF="../mod/core.html#errordocument" + ><SAMP>ErrorDocument</SAMP></A> + documentation for details. This was incorrectly documented in the past. + </P> + <HR> + </LI> + <LI><A NAME="errordocssi"> + <STRONG>How can I use <CODE>ErrorDocument</CODE> + and SSI to simplify customized error messages?</STRONG> + </A> + <P> + Have a look at <A HREF="custom_errordocs.html">this document</A>. + It shows in example form how you can a combination of XSSI and + negotiation to tailor a set of <CODE>ErrorDocument</CODE>s to your + personal taste, and returning different internationalized error + responses based on the client's native language. + </P> + <HR> + </LI> + <LI><A NAME="setgid"> + <STRONG>Why do I get "<SAMP>setgid: Invalid + argument</SAMP>" at startup?</STRONG> + </A> + <P> + Your + <A + HREF="../mod/core.html#group" + ><SAMP>Group</SAMP></A> + directive (probably in <SAMP>conf/httpd.conf</SAMP>) needs to name a + group that actually exists in the <SAMP>/etc/group</SAMP> file (or + your system's equivalent). + </P> + <HR> + </LI> + <LI><A NAME="cookies1"> + <STRONG>Why does Apache send a cookie on every response?</STRONG> + </A> + <P> + Apache does <EM>not</EM> send automatically send a cookie on every + response, unless you have re-compiled it with the + <A + HREF="../mod/mod_cookies.html" + ><SAMP>mod_cookies</SAMP></A> + module. + This module was distributed with Apache prior to 1.2. + This module may help track users, and uses cookies to do this. If + you are not using the data generated by <SAMP>mod_cookies</SAMP>, do + not compile it into Apache. Note that in 1.2 this module was renamed + to the more correct name + <A + HREF="../mod/mod_usertrack.html" + ><SAMP>mod_usertrack</SAMP></A>, + and cookies + have to be specifically enabled with the + <A + HREF="../mod/mod_usertrack.html#cookietracking" + ><SAMP>CookieTracking</SAMP></A> + directive. + </P> + <HR> + </LI> + <LI><A NAME="cookies2"> + <STRONG>Why don't my cookies work, I even compiled in + <SAMP>mod_cookies</SAMP>? + </STRONG> + </A> + <P> + Firstly, you do <EM>not</EM> need to compile in + <SAMP>mod_cookies</SAMP> in order for your scripts to work (see the + <A + HREF="#cookies1" + >previous question</A> + for more about <SAMP>mod_cookies</SAMP>). Apache passes on your + <SAMP>Set-Cookie</SAMP> header fine, with or without this module. If + cookies do not work it will be because your script does not work + properly or your browser does not use cookies or is not set-up to + accept them. + </P> + <HR> + </LI> + <LI><A NAME="jdk1-and-http1.1"> + <STRONG>Why do my Java app[let]s give me plain text when I request + an URL from an Apache server?</STRONG> + </A> + <P> + As of version 1.2, Apache is an HTTP/1.1 (HyperText Transfer Protocol + version 1.1) server. This fact is reflected in the protocol version + that's included in the response headers sent to a client when + processing a request. Unfortunately, low-level Web access classes + included in the Java Development Kit (JDK) version 1.0.2 expect to see + the version string "HTTP/1.0" and do not correctly interpret + the "HTTP/1.1" value Apache is sending (this part of the + response is a declaration of what the server can do rather than a + declaration of the dialect of the response). The result + is that the JDK methods do not correctly parse the headers, and + include them with the document content by mistake. + </P> + <P> + This is definitely a bug in the JDK 1.0.2 foundation classes from Sun, + and it has been fixed in version 1.1. However, the classes in + question are part of the virtual machine environment, which means + they're part of the Web browser (if Java-enabled) or the Java + environment on the client system - so even if you develop + <EM>your</EM> classes with a recent JDK, the eventual users might + encounter the problem. + The classes involved are replaceable by vendors implementing the + Java virtual machine environment, and so even those that are based + upon the 1.0.2 version may not have this problem. + </P> + <P> + In the meantime, a workaround is to tell + Apache to "fake" an HTTP/1.0 response to requests that come + from the JDK methods; this can be done by including a line such as the + following in your server configuration files: + </P> + <P> + <DL> + <DD><CODE>BrowserMatch Java1.0 force-response-1.0 + <BR> + BrowserMatch JDK/1.0 force-response-1.0</CODE> + </DD> + </DL> + </P> + <P> + More information about this issue can be found in the + <A + HREF="http://www.apache.org/info/jdk-102.html" + ><CITE>Java and HTTP/1.1</CITE></A> + page at the Apache web site. + </P> + <HR> + </LI> + <LI><A NAME="putsupport"> + <STRONG>Why can't I publish to my Apache server using PUT on + Netscape Gold and other programs?</STRONG> + </A> + <P> + Because you need to install and configure a script to handle + the uploaded files. This script is often called a "PUT" handler. + There are several available, but they may have security problems. + Using FTP uploads may be easier and more secure, at least for now. + For more information, see the <CITE>Apache Week</CITE> article + <A + HREF="http://www.apacheweek.com/features/put" + ><CITE>Publishing Pages with PUT</CITE></A>. + </P> + <HR> + </LI> + <LI><A NAME="fastcgi"> + <STRONG>Why isn't FastCGI included with Apache any more?</STRONG> + </A> + <P> + The simple answer is that it was becoming too difficult to keep the + version being included with Apache synchronized with the master copy + at the + <A + HREF="http://www.fastcgi.com/servers/apache/" + >FastCGI web site</A>. When a new version of Apache was released, the + version of the FastCGI module included with it would soon be out of date. + </P> + <P> + You can still obtain the FastCGI module for Apache from the master + FastCGI web site. + </P> + <HR> + </LI> + <LI><A NAME="nodelay"> + <STRONG>Why am I getting "<SAMP>httpd: could not set socket + option TCP_NODELAY</SAMP>" in my error log?</STRONG> + </A> + <P> + This message almost always indicates that the client disconnected + before Apache reached the point of calling <CODE>setsockopt()</CODE> + for the connection. It shouldn't occur for more than about 1% of the + requests your server handles, and it's advisory only in any case. + </P> + <HR> + </LI> + <LI><A NAME="peerreset"> + <STRONG>Why am I getting "<SAMP>connection reset by + peer</SAMP>" in my error log?</STRONG> + </A> + <P> + This is a normal message and nothing about which to be alarmed. It simply + means that the client canceled the connection before it had been + completely set up - such as by the end-user pressing the "Stop" + button. People's patience being what it is, sites with response-time + problems or slow network links may experiences this more than + high-capacity ones or those with large pipes to the network. + </P> + <HR> + </LI> + <LI><A NAME="nph-scripts"> + <STRONG>How can I get my script's output without Apache buffering + it? Why doesn't my server push work?</STRONG> + </A> + <P> + In order to improve network performance, Apache buffers script output + into relatively large chunks. If you have a script that sends + information in bursts (eg. as partial-done messages in a multi-commit + database transaction or any type of server push), the client will + not necessarily get the output as the script is generating it. + </P> + <P> + To avoid this, Apache recognizes scripts whose names begin with + "<SAMP>nph-</SAMP>" as <EM>non-parsed-header</EM> scripts. + That is, Apache won't buffer their output, but connect it directly to + the socket going back to the client. + </P> + <P> + While this will probably do what you want, there <EM>are</EM> some + disadvantages to it: + </P> + <UL> + <LI><STRONG>YOU</STRONG> (the script) are responsible for generating + <STRONG>ALL</STRONG> of the HTTP headers, and no longer + <EM>just</EM> the "<SAMP>Content-type</SAMP>" or + "<SAMP>Location</SAMP>" headers + </LI> + <LI>Unless your script generates its output carefully, you will see a + performance penalty as excessive numbers of packets go back and forth + </LI> + </UL> + <P> + As an example how you might handle the former (in a Perl script): + </P> + <P> + <DL> + <DD><CODE>if ($0 =~ m:^(.*/)*nph-[^/]*$:) { + <BR> + + $HTTP_headers = + "HTTP/1.1 200 OK\015\012"; + <BR> + + $HTTP_headers .= + "Connection: close\015\012"; + <BR> + + print $HTTP_headers; + <BR> + }</CODE> + </DD> + </DL> + </P> + <P> + and then follow with your normal non-<SAMP>nph</SAMP> headers. + </P> + <P>Note that in version 1.3, all CGI scripts will be unbuffered + so the only difference between nph scripts and normal scripts is + that nph scripts require the full HTTP headers to be sent. + </P> + <HR> + </LI> + <LI><A NAME="linuxiovec"> + <STRONG>Why do I get complaints about redefinition + of "<CODE>struct iovec</CODE>" when + compiling under Linux?</STRONG> + </A> + <P> + This is a conflict between your C library includes and your kernel + includes. You need to make sure that the versions of both are matched + properly. There are two workarounds, either one will solve the problem: + </P> + <P> + <UL> + <LI>Remove the definition of <CODE>struct iovec</CODE> from your C + library includes. It is located in <CODE>/usr/include/sys/uio.h</CODE>. + <STRONG>Or,</STRONG> + </LI> + <LI>Add <CODE>-DNO_WRITEV</CODE> to the <CODE>EXTRA_CFLAGS</CODE> + line in your <SAMP>Configuration</SAMP> and reconfigure/rebuild. + This hurts performance and should only be used as a last resort. + </LI> + </UL> + </P> + <HR> + </LI> + <LI><A NAME="wheres-the-dump"> + <STRONG>The errorlog says Apache dumped core, but where's the dump + file?</STRONG> + </A> + <P> + In Apache version 1.2, the error log message + about dumped core includes the directory where the dump file should be + located. However, many Unixes do not allow a process that has + called <CODE>setuid()</CODE> to dump core for security reasons; + the typical Apache setup has the server started as root to bind to + port 80, after which it changes UIDs to a non-privileged user to + serve requests. + </P> + <P> + Dealing with this is extremely operating system-specific, and may + require rebuilding your system kernel. Consult your operating system + documentation or vendor for more information about whether your system + does this and how to bypass it. If there <EM>is</EM> a documented way + of bypassing it, it is recommended that you bypass it only for the + <SAMP>httpd</SAMP> server process if possible. + </P> + <P> + The canonical location for Apache's core-dump files is the + <A HREF="../mod/core.html#serverroot">ServerRoot</A> + directory. As of Apache version 1.3, the location can be set <EM>via</EM> + the + <A + HREF="../mod/core.html#coredumpdirectory" + ><SAMP>CoreDumpDirectory</SAMP></A> + directive to a different directory. Make sure that this directory is + writable by the user the server runs as (as opposed to the user the server + is <EM>started</EM> as). + </P> + <HR> + </LI> + <LI><A NAME="dnsauth"> + <STRONG>Why isn't restricting access by host or domain name + working correctly?</STRONG> + </A> + <P> + Two of the most common causes of this are: + </P> + <OL> + <LI><STRONG>An error, inconsistency, or unexpected mapping in the DNS + registration</STRONG> + <BR> + This happens frequently: your configuration restricts access to + <SAMP>Host.FooBar.Com</SAMP>, but you can't get in from that host. + The usual reason for this is that <SAMP>Host.FooBar.Com</SAMP> is + actually an alias for another name, and when Apache performs the + address-to-name lookup it's getting the <EM>real</EM> name, not + <SAMP>Host.FooBar.Com</SAMP>. You can verify this by checking the + reverse lookup yourself. The easiest way to work around it is to + specify the correct host name in your configuration. + </LI> + <LI><STRONG>Inadequate checking and verification in your + configuration of Apache</STRONG> + <BR> + If you intend to perform access checking and restriction based upon + the client's host or domain name, you really need to configure + Apache to double-check the origin information it's supplied. You do + this by adding the <SAMP>-DMAXIMUM_DNS</SAMP> clause to the + <SAMP>EXTRA_CFLAGS</SAMP> definition in your + <SAMP>Configuration</SAMP> file. For example: + <P> + <DL> + <DD><CODE>EXTRA_CFLAGS=-DMAXIMUM_DNS</CODE> + </DD> + </DL> + </P> + <P> + This will cause Apache to be very paranoid about making sure a + particular host address is <EM>really</EM> assigned to the name it + claims to be. Note that this <EM>can</EM> incur a significant + performance penalty, however, because of all the name resolution + requests being sent to a nameserver. + </P> + </LI> + </OL> + <HR> + </LI> + <LI><A NAME="SSL-i"> + <STRONG>Why doesn't Apache include SSL?</STRONG> + </A> + <P> + SSL (Secure Socket Layer) data transport requires encryption, and many + governments have restrictions upon the import, export, and use of + encryption technology. If Apache included SSL in the base package, + its distribution would involve all sorts of legal and bureaucratic + issues, and it would no longer be freely available. Also, some of + the technology required to talk to current clients using SSL is + patented by <A HREF="http://www.rsa.com/">RSA Data Security</A>, + who restricts its use without a license. + </P> + <P> + Some SSL implementations of Apache are available, however; see the + "<A + HREF="http://www.apache.org/related_projects.html" + >related projects</A>" + page at the main Apache web site. + </P> + <P> + You can find out more about this topic in the <CITE>Apache Week</CITE> + article about + <A + HREF="http://www.apacheweek.com/features/ssl" + REL="Help" + ><CITE>Apache and Secure Transactions</CITE></A>. + </P> + <HR> + </LI> + <LI><A NAME="HPUX-core"> + <STRONG>Why do I get core dumps under HPUX using HP's ANSI + C compiler?</STRONG> + </A> + <P> + We have had numerous reports of Apache dumping core when compiled + with HP's ANSI C compiler using optimization. Disabling the compiler + optimization has fixed these problems. + </P> + <HR> + </LI> + <LI><A NAME="midi"> + <STRONG>How do I get Apache to send a MIDI file so the browser can + play it?</STRONG> + </A> + <P> + Even though the registered MIME type for MIDI files is + <SAMP>audio/midi</SAMP>, some browsers are not set up to recognize it + as such; instead, they look for <SAMP>audio/x-midi</SAMP>. There are + two things you can do to address this: + </P> + <OL> + <LI>Configure your browser to treat documents of type + <SAMP>audio/midi</SAMP> correctly. This is the type that Apache + sends by default. This may not be workable, however, if you have + many client installations to change, or if some or many of the + clients are not under your control. + </LI> + <LI>Instruct Apache to send a different <SAMP>Content-type</SAMP> + header for these files by adding the following line to your server's + configuration files: + <P> + <DL> + <DD><CODE>AddType audio/x-midi .mid .midi .kar</CODE> + </DD> + </DL> + </P> + <P> + Note that this may break browsers that <EM>do</EM> recognize the + <SAMP>audio/midi</SAMP> MIME type unless they're prepared to also + handle <SAMP>audio/x-midi</SAMP> the same way. + </P> + </LI> + </OL> + <HR> + </LI> + <LI><A NAME="cantbuild"> + <STRONG>Why won't Apache compile with my system's + <SAMP>cc</SAMP>?</STRONG> + </A> + <P> + If the server won't compile on your system, it is probably due to one + of the following causes: + </P> + <UL> + <LI><STRONG>The <SAMP>Configure</SAMP> script doesn't recognize your system + environment.</STRONG> + <BR> + This might be either because it's completely unknown or because + the specific environment (include files, OS version, <EM>et + cetera</EM>) isn't explicitly handled. If this happens, you may + need to port the server to your OS yourself. + </LI> + <LI><STRONG>Your system's C compiler is garbage.</STRONG> + <BR> + Some operating systems include a default C compiler that is either + not ANSI C-compliant or suffers from other deficiencies. The usual + recommendation in cases like this is to acquire, install, and use + <SAMP>gcc</SAMP>. + </LI> + <LI><STRONG>Your <SAMP>include</SAMP> files may be confused.</STRONG> + <BR> + In some cases, we have found that a compiler installation or system + upgrade has left the C header files in an inconsistent state. Make + sure that your include directory tree is in sync with the compiler and + the operating system. + </LI> + <LI><STRONG>Your operating system or compiler may be out of + revision.</STRONG> + <BR> + Software vendors (including those that develop operating systems) + issue new releases for a reason; sometimes to add functionality, but + more often to fix bugs that have been discovered. Try upgrading + your compiler and/or your operating system. + </LI> + </UL> + <P> + The Apache Group tests the ability to build the server on many + different platforms. Unfortunately, we can't test all of the OS + platforms there are. If you have verified that none of the above + issues is the cause of your problem, and it hasn't been reported + before, please submit a + <A + HREF="http://www.apache.org/bug_report.html" + >problem report</A>. + Be sure to include <EM>complete</EM> details, such as the compiler + & OS versions and exact error messages. + </P> + <HR> + </LI> + <LI><A NAME="addlog"> + <STRONG>How do I add browsers and referrers to my logs?</STRONG> + </A> + <P> + Apache provides a couple of different ways of doing this. The + recommended method is to compile the + <A + HREF="../mod/mod_log_config.html" + ><SAMP>mod_log_config</SAMP></A> + module into your configuration and use the + <A + HREF="../mod/mod_log_config.html#customlog" + ><SAMP>CustomLog</SAMP></A> + directive. + </P> + <P> + You can either log the additional information in files other than your + normal transfer log, or you can add them to the records already being + written. For example: + </P> + <P> + <CODE> + CustomLog logs/access_log "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\"" + </CODE> + </P> + <P> + This will add the values of the <SAMP>User-agent:</SAMP> and + <SAMP>Referer:</SAMP> headers, which indicate the client and the + referring page, respectively, to the end of each line in the access + log. + </P> + <P> + You may want to check out the <CITE>Apache Week</CITE> article + entitled: + "<A + HREF="http://www.apacheweek.com/features/logfiles" + REL="Help" + ><CITE>Gathering Visitor Information: Customising Your + Logfiles</CITE></A>". + </P> + <HR> + </LI> + <LI><A NAME="bind8.1"> + <STRONG>Why do I get an error about an undefined reference to + "<SAMP>__inet_ntoa</SAMP>" or other + <SAMP>__inet_*</SAMP> symbols?</STRONG> + </A> + <P> + If you have installed <A HREF="http://www.isc.org/bind.html">BIND-8</A> + then this is normally due to a conflict between your include files + and your libraries. BIND-8 installs its include files and libraries + <CODE>/usr/local/include/</CODE> and <CODE>/usr/local/lib/</CODE>, while + the resolver that comes with your system is probably installed in + <CODE>/usr/include/</CODE> and <CODE>/usr/lib/</CODE>. If + your system uses the header files in <CODE>/usr/local/include/</CODE> + before those in <CODE>/usr/include/</CODE> but you do not use the new + resolver library, then the two versions will conflict. + </P> + <P> + To resolve this, you can either make sure you use the include files + and libraries that came with your system or make sure to use the + new include files and libraries. Adding <CODE>-lbind</CODE> to the + <CODE>EXTRA_LDFLAGS</CODE> line in your <SAMP>Configuration</SAMP> + file, then re-running <SAMP>Configure</SAMP>, should resolve the + problem. (Apache versions 1.2.* and earlier use + <CODE>EXTRA_LFLAGS</CODE> instead.) + </P> + <P> + <STRONG>Note:</STRONG>As of BIND 8.1.1, the bind libraries and files are + installed under <SAMP>/usr/local/bind</SAMP> by default, so you + should not run into this problem. Should you want to use the bind + resolvers you'll have to add the following to the respective lines: + </P> + <P> + <DL> + <DD><CODE>EXTRA_CFLAGS=-I/usr/local/bind/include + <BR> + EXTRA_LDFLAGS=-L/usr/local/bind/lib + <BR> + EXTRA_LIBS=-lbind</CODE> + </DD> + </DL> + </P> + <HR> + </LI> + <LI><A NAME="set-servername"> + <STRONG>Why does accessing directories only work when I include + the trailing "/" + (<EM>e.g.</EM>, <SAMP>http://foo.domain.com/~user/</SAMP>) + but not when I omit it + (<EM>e.g.</EM>, <SAMP>http://foo.domain.com/~user</SAMP>)?</STRONG> + </A> + <P> + When you access a directory without a trailing "/", Apache needs + to send what is called a redirect to the client to tell it to + add the trailing slash. If it did not do so, relative URLs would + not work properly. When it sends the redirect, it needs to know + the name of the server so that it can include it in the redirect. + There are two ways for Apache to find this out; either it can guess, + or you can tell it. If your DNS is configured correctly, it can + normally guess without any problems. If it is not, however, then + you need to tell it. + </P> + <P> + Add a <A HREF="../mod/core.html#servername">ServerName</A> directive + to the config file to tell it what the domain name of the server is. + </P> + <HR> + </LI> + <LI><A NAME="user-authentication"> + <STRONG>How do I set up Apache to require a username and + password to access certain documents?</STRONG> + </A> + <P> + There are several ways to do this; some of the more popular + ones are to use the <A HREF="../mod/mod_auth.html">mod_auth</A>, + <A HREF="../mod/mod_auth_db.html">mod_auth_db</A>, or + <A HREF="../mod/mod_auth_dbm.html">mod_auth_dbm</A> modules. + </P> + <P> + For an explanation on how to implement these restrictions, see + <A + HREF="http://www.apacheweek.com/" + ><CITE>Apache Week</CITE></A>'s + articles on + <A + HREF="http://www.apacheweek.com/features/userauth" + ><CITE>Using User Authentication</CITE></A> + or + <A + HREF="http://www.apacheweek.com/features/dbmauth" + ><CITE>DBM User Authentication</CITE></A>. + </P> + <HR> + </LI> + <LI><A NAME="remote-user-var"> + <STRONG>Why is the environment variable + <SAMP>REMOTE_USER</SAMP> not set?</STRONG> + </A> + <P> + This variable is set and thus available in SSI or CGI scripts <STRONG>if and + only if</STRONG> the requested document was protected by access + authentication. For an explanation on how to implement these restrictions, + see + <A + HREF="http://www.apacheweek.com/" + ><CITE>Apache Week</CITE></A>'s + articles on + <A + HREF="http://www.apacheweek.com/features/userauth" + ><CITE>Using User Authentication</CITE></A> + or + <A + HREF="http://www.apacheweek.com/features/dbmauth" + ><CITE>DBM User Authentication</CITE></A>. + </P> + <P> + Hint: When using a CGI script to receive the data of a HTML <SAMP>FORM</SAMP> + notice that protecting the document containing the <SAMP>FORM</SAMP> is not + sufficient to provide <SAMP>REMOTE_USER</SAMP> to the CGI script. You have + to protect the CGI script, too. Or alternatively only the CGI script (then + authentication happens only after filling out the form). + </P> + <HR> + </LI> + <LI><A NAME="remote-auth-only"> + <STRONG>How do I set up Apache to allow access to certain + documents only if a site is either a local site <EM>or</EM> + the user supplies a password and username?</STRONG> + </A> + <P> + Use the <A HREF="../mod/core.html#satisfy">Satisfy</A> directive, + in particular the <CODE>Satisfy Any</CODE> directive, to require + that only one of the access restrictions be met. For example, + adding the following configuration to a <SAMP>.htaccess</SAMP> + or server configuration file would restrict access to people who + either are accessing the site from a host under domain.com or + who can supply a valid username and password: + </P> + <P> + <DL> + <DD><CODE>deny from all + <BR> + allow from .domain.com + <BR> + AuthType Basic + <BR> + AuthUserFile /usr/local/apache/conf/htpasswd.users + <BR> + AuthName special directory + <BR> + require valid-user + <BR> + satisfy any</CODE> + </DD> + </DL> + </P> + <P> + See the <A HREF="#user-authentication">user authentication</A> + question and the <A HREF="../mod/mod_access.html">mod_access</A> + module for details on how the above directives work. + </P> + <HR> + </LI> + <LI><A NAME="no-info-directives"> + <STRONG>Why doesn't mod_info list any directives?</STRONG> + </A> + <P> + The + <A + HREF="../mod/mod_info.html" + ><SAMP>mod_info</SAMP></A> + module allows you to use a Web browser to see how your server is + configured. Among the information it displays is the list modules and + their configuration directives. The "current" values for + the directives are not necessarily those of the running server; they + are extracted from the configuration files themselves at the time of + the request. If the files have been changed since the server was last + reloaded, the display will will not match the values actively in use. + If the files and the path to the files are not readable by the user as + which the server is running (see the + <A + HREF="../mod/core.html#user" + ><SAMP>User</SAMP></A> + directive), then <SAMP>mod_info</SAMP> cannot read them in order to + list their values. An entry <EM>will</EM> be made in the error log in + this event, however. + </P> + <HR> + </LI> + <LI><A NAME="linux-shmget"> + <STRONG>When I run it under Linux I get "shmget: + function not found", what should I do?</STRONG> + </A> + <P> + Your kernel has been built without SysV IPC support. You will have to + rebuild the kernel with that support enabled (it's under the + "General Setup" submenu). Documentation for + kernel building is beyond the scope of this FAQ; you should consult + the + <A HREF="http://www.linuxhq.com/HOWTO/Kernel-HOWTO.html" + >Kernel HOWTO</A>, + or the documentation provided with your distribution, or a + <A HREF="http://www.linuxhq.com/HOWTO/META-FAQ.html" + >Linux newsgroup/mailing list</A>. + As a last-resort workaround, you can + comment out the <CODE>#define USE_SHMGET_SCOREBOARD</CODE> + definition in the + <SAMP>LINUX</SAMP> section of + <SAMP>src/conf.h</SAMP> and rebuild the server (prior to 1.3b4, simply + removing <CODE>#define HAVE_SHMGET</CODE> would have sufficed). + This will produce a server which is slower and less reliable. + </P> + <HR> + </LI> + <LI><A NAME="authauthoritative"> + <STRONG>Why does my authentication give me a server error?</STRONG> + </A> + <P> + Under normal circumstances, the Apache access control modules will + pass unrecognized user IDs on to the next access control module in + line. Only if the user ID is recognized and the password is validated + (or not) will it give the usual success or "authentication + failed" messages. + </P> + <P> + However, if the last access module in line 'declines' the validation + request (because it has never heard of the user ID or because it is not + configured), the <SAMP>http_request</SAMP> handler will give one of + the following, confusing, errors: + </P> + <UL> + <LI><SAMP>check access</SAMP> + </LI> + <LI><SAMP>check user. No user file?</SAMP> + </LI> + <LI><SAMP>check access. No groups file?</SAMP> + </LI> + </UL> + <P> + This does <EM>not</EM> mean that you have to add an + '<SAMP>AuthUserFile /dev/null</SAMP>' line as some magazines suggest! + </P> + <P> + The solution is to ensure that at least the last module is authoritative + and <STRONG>CONFIGURED</STRONG>. By default, <SAMP>mod_auth</SAMP> is + authoritative and will give an OK/Denied, but only if it is configured + with the proper <SAMP>AuthUserFile</SAMP>. Likewise, if a valid group + is required. (Remember that the modules are processed in the reverse + order from that in which they appear in your compile-time + <SAMP>Configuration</SAMP> file.) + </P> + <P> + A typical situation for this error is when you are using the + <SAMP>mod_auth_dbm</SAMP>, <SAMP>mod_auth_msql</SAMP>, + <SAMP>mod_auth_mysql</SAMP>, <SAMP>mod_auth_anon</SAMP> or + <SAMP>mod_auth_cookie</SAMP> modules on their own. These are by + default <STRONG>not</STRONG> authoritative, and this will pass the + buck on to the (non-existent) next authentication module when the + user ID is not in their respective database. Just add the appropriate + '<SAMP><EM>XXX</EM>Authoritative yes</SAMP>' line to the configuration. + </P> + <P> + In general it is a good idea (though not terribly efficient) to have the + file-based <SAMP>mod_auth</SAMP> a module of last resort. This allows + you to access the web server with a few special passwords even if the + databases are down or corrupted. This does cost a + file open/seek/close for each request in a protected area. + </P> + <HR> + </LI> + <LI><A NAME="auth-on-same-machine"> + <STRONG>Do I have to keep the (mSQL) authentication information + on the same machine?</STRONG> + </A> + <P> + Some organizations feel very strongly about keeping the authentication + information on a different machine than the webserver. With the + <SAMP>mod_auth_msql</SAMP>, <SAMP>mod_auth_mysql</SAMP>, and other SQL + modules connecting to (R)DBMses this is quite possible. Just configure + an explicit host to contact. + </P> + <P> + Be aware that with mSQL and Oracle, opening and closing these database + connections is very expensive and time consuming. You might want to + look at the code in the <SAMP>auth_*</SAMP> modules and play with the + compile time flags to alleviate this somewhat, if your RDBMS licences + allow for it. + </P> + <HR> + </LI> + <LI><A NAME="msql-slow"> + <STRONG>Why is my mSQL authentication terribly slow?</STRONG> + </A> + <P> + You have probably configured the Host by specifying a FQHN, + and thus the <SAMP>libmsql</SAMP> will use a full blown TCP/IP socket + to talk to the database, rather than a fast internal device. The + <SAMP>libmsql</SAMP>, the mSQL FAQ, and the <SAMP>mod_auth_msql</SAMP> + documentation warn you about this. If you have to use different + hosts, check out the <SAMP>mod_auth_msql</SAMP> code for + some compile time flags which might - or might not - suit you. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-more-config"> + <STRONG>Where can I find mod_rewrite rulesets which already solve + particular URL-related problems?</STRONG> + </A> + <P> + There is a collection of + <A + HREF="http://www.engelschall.com/pw/apache/rewriteguide/" + >Practical Solutions for URL-Manipulation</A> + where you can + find all typical solutions the author of + <A + HREF="../mod/mod_rewrite.html" + ><SAMP>mod_rewrite</SAMP></A> + currently knows of. If you have more + interesting rulesets which solve particular problems not currently covered in + this document, send it to + <A + HREF="mailto:rse@apache.org" + >Ralf S. Engelschall</A> + for inclusion. The + other webmasters will thank you for avoiding the reinvention of the wheel. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-article"> + <STRONG>Where can I find any published information about URL-manipulations and + mod_rewrite?</STRONG> + </A> + <P> + There is an article from + <A + HREF="mailto:rse@apache.org" + >Ralf S. Engelschall</A> + about URL-manipulations based on + <A + HREF="../mod/mod_rewrite.html" + ><SAMP>mod_rewrite</SAMP></A> + in the "iX Multiuser Multitasking Magazin" issue #12/96. The + german (original) version + can be read online at + <A + HREF="http://www.heise.de/ix/artikel/9612149/" + >http://www.heise.de/ix/artikel/9612149/</A>, + the English (translated) version can be found at + <A + HREF="http://www.heise.de/ix/artikel/E/9612149/" + >http://www.heise.de/ix/artikel/E/9612149/</A>. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-complexity"> + <STRONG>Why is mod_rewrite so difficult to learn and seems so + complicated?</STRONG> + </A> + <P> + Hmmm... there are a lot of reasons. First, mod_rewrite itself is a powerful + module which can help you in really <STRONG>all</STRONG> aspects of URL rewriting, so + it can be no trivial module per definition. To accomplish its hard job it + uses software leverage and makes use of a powerful regular expression + library by Henry Spencer which is an integral part of Apache since its + version 1.2. And regular expressions itself can be difficult to newbies, + while providing the most flexible power to the advanced hacker. + </P> + <P> + On the other hand mod_rewrite has to work inside the Apache API environment + and needs to do some tricks to fit there. For instance the Apache API as of + 1.x really was not designed for URL rewriting at the <TT>.htaccess</TT> + level of processing. Or the problem of multiple rewrites in sequence, which + is also not handled by the API per design. To provide this features + mod_rewrite has to do some special (but API compliant!) handling which leads + to difficult processing inside the Apache kernel. While the user usually + doesn't see anything of this processing, it can be difficult to find + problems when some of your RewriteRules seem not to work. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-dontwork"> + <STRONG>What can I do if my RewriteRules don't work as expected? + </STRONG> + </A> + <P> + Use "<SAMP>RewriteLog somefile</SAMP>" and + "<SAMP>RewriteLogLevel 9</SAMP>" and have a precise look at the + steps the rewriting engine performs. This is really the only one and best + way to debug your rewriting configuration. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-prefixdocroot"><STRONG>Why don't some of my URLs + get prefixed with DocumentRoot when using mod_rewrite?</STRONG> + </A> + <P> + If the rule starts with <SAMP>/somedir/...</SAMP> make sure that really no + <SAMP>/somedir</SAMP> exists on the filesystem if you don't want to lead the + URL to match this directory, i.e. there must be no root directory named + <SAMP>somedir</SAMP> on the filesystem. Because if there is such a + directory, the URL will not get prefixed with DocumentRoot. This behaviour + looks ugly, but is really important for some other aspects of URL + rewriting. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-nocase"> + <STRONG>How can I make all my URLs case-insensitive with mod_rewrite? + </STRONG> + </A> + <P> + You can't! The reason is: First, case translations for arbitrary length URLs + cannot be done <EM>via</EM> regex patterns and corresponding substitutions. + One need + a per-character pattern like sed/Perl <SAMP>tr|..|..|</SAMP> feature. + Second, just + making URLs always upper or lower case will not resolve the complete problem + of case-INSENSITIVE URLs, because actually the URLs had to be rewritten to + the correct case-variant residing on the filesystem because in later + processing Apache needs to access the file. And Unix filesystem is always + case-SENSITIVE. + </P> + <P> + But there is a module named <CODE>mod_speling.c</CODE> (yes, it is named + this way!) out there on the net. Try this one. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-virthost"> + <STRONG> Why are RewriteRules in my VirtualHost parts ignored? + </STRONG> + </A> + <P> + Because you have to enable the engine for every virtual host explicitly due + to security concerns. Just add a "RewriteEngine on" to your + virtual host configuration parts. + </P> + <HR> + </LI> + <LI><A NAME="rewrite-envwhitespace"> + <STRONG> How can I use strings with whitespaces in RewriteRule's ENV + flag?</STRONG> + </A> + <P> + There is only one ugly solution: You have to surround the complete flag + argument by quotation marks (<SAMP>"[E=...]"</SAMP>). Notice: The argument + to quote here is not the argument to the E-flag, it is the argument of the + Apache config file parser, i.e. the third argument of the RewriteRule here. + So you have to write <SAMP>"[E=any text with whitespaces]"</SAMP>. + </P> + <HR> + </LI> + <LI><A NAME="cgi-spec"><STRONG>Where can I find the "CGI + specification"?</STRONG></A> + <P> + The Common Gateway Interface (CGI) specification can be found at + the original NCSA site + <<A HREF="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"> + <SAMP>http://hoohoo.ncsa.uiuc.edu/cgi/interface.html</SAMP></A>>. + This version hasn't been updated since 1995, and there have been + some efforts to update it. + </P> + <HR> + </LI> + <LI><A NAME="year2000"> + <STRONG>Is Apache Year 2000 compliant?</STRONG> + </A> + <P> + Yes, Apache is Year 2000 compliant. + </P> + <P> + Apache internally never stores years as two digits. + On the HTTP protocol level RFC1123-style addresses are generated + which is the only format a HTTP/1.1-compliant server should + generate. To be compatible with older applications Apache + recognizes ANSI C's <CODE>asctime()</CODE> and + RFC850-/RFC1036-style date formats, too. + The <CODE>asctime()</CODE> format uses four-digit years, + but the RFC850 and RFC1036 date formats only define a two-digit year. + If Apache sees such a date with a value less than 70 it assumes that + the century is <SAMP>20</SAMP> rather than <SAMP>19</SAMP>. + </P> + <P> + Some aspects of Apache's output may use two-digit years, such as the + automatic listing of directory contents provided by + <A + HREF="../mod/mod_autoindex.html" + ><SAMP>mod_autoindex</SAMP></A> + with the + <A + HREF="../mod/mod_autoindex.html#indexoptions" + ><SAMP>FancyIndexing</SAMP></A> + option enabled, but it is improper to depend upon such displays for + specific syntax. And even that issue is being addressed by the + developers; a future version of Apache should allow you to format that + display as you like. + </P> + <P> + Although Apache is Year 2000 compliant, you may still get problems + if the underlying OS has problems with dates past year 2000 + (<EM>e.g.</EM>, OS calls which accept or return year numbers). + Most (UNIX) systems store dates internally as signed 32-bit integers + which contain the number of seconds since 1<SUP>st</SUP> January 1970, so + the magic boundary to worry about is the year 2038 and not 2000. + But modern operating systems shouldn't cause any trouble + at all. + </P> + <HR> + </LI> + <LI><A NAME="namevhost"> + <STRONG>I upgraded to Apache 1.3b and now my virtual hosts don't + work!</STRONG> + </A> + <P> + In versions of Apache prior to 1.3b2, there was a lot of confusion + regarding address-based virtual hosts and (HTTP/1.1) name-based + virtual hosts, and the rules concerning how the server processed + <SAMP><VirtualHost></SAMP> definitions were very complex and not + well documented. + </P> + <P> + Apache 1.3b2 introduced a new directive, + <A + HREF="http://www.apache.org/docs/mod/core.html#namevirtualhost" + ><SAMP>NameVirtualHost</SAMP></A>, + which simplifies the rules quite a bit. However, changing the rules + like this means that your existing name-based + <SAMP><VirtualHost></SAMP> containers probably won't work + correctly immediately following the upgrade. + </P> + <P> + To correct this problem, add the following line to the beginning of + your server configuration file, before defining any virtual hosts: + </P> + <DL> + <DD><CODE>NameVirtualHost <EM>n.n.n.n</EM></CODE> + </DD> + </DL> + <P> + Replace the "<SAMP>n.n.n.n</SAMP>" with the IP address to + which the name-based virtual host names resolve; if you have multiple + name-based hosts on multiple addresses, repeat the directive for each + address. + </P> + <P> + Make sure that your name-based <SAMP><VirtualHost></SAMP> blocks + contain <SAMP>ServerName</SAMP> and possibly <SAMP>ServerAlias</SAMP> + directives so Apache can be sure to tell them apart correctly. + </P> + <P> + Please see the + <A HREF="http://www.apache.org/docs/vhosts/">Apache + Virtual Host documentation</A> for further details about configuration. + </P> + <HR> + </LI> + + <li><a name="redhat"><strong>I'm using RedHat Linux and I have problems with httpd + dying randomly or not restarting properly</strong></a> + + <p>RedHat Linux versions 4.x (and possibly earlier) rpms contain + various nasty scripts which do not stop or restart Apache properly. + These can affect you even if you're not running the RedHat supplied + rpms. + + <p> If you're using the default install then you're probably running + Apache 1.1.3, which is outdated. From RedHat's ftp site you can + pick up a more recent RPM for Apache 1.2.x. This will solve one of + the problems. + + <p> If you're using a custom built Apache rather than the RedHat rpms + then you should <code>rpm -e apache</code>. In particular you want + the mildly broken <code>/etc/logrotate.d/apache</code> script to be + removed, and you want the broken <code>/etc/rc.d/init.d/httpd</code> + (or <code>httpd.init</code>) script to be removed. The latter is + actually fixed by the apache-1.2.5 rpms but if you're building your + own Apache then you probably don't want the RedHat files. + + <p>We can't stress enough how important it is for folks, <i>especially + vendors</i> to follow the <a href="../stopping.html">stopping Apache + directions</a> given in our documentation. In RedHat's defense, + the broken scripts were necessary with Apache 1.1.x because the + Linux support in 1.1.x was very poor, and there were various race + conditions on all platforms. None of this should be necessary with + Apache 1.2 and later. + </p> + <hr> + </li> + + <li><a name="stopping"><strong>I upgraded from an Apache version earlier + than 1.2.0 and suddenly I have problems with Apache dying randomly + or not restarting properly</strong></a> + + <p>You should read <a href="#redhat">the previous note</a> about + problems with RedHat installations. It is entirely likely that your + installation has start/stop/restart scripts which were built for + an earlier version of Apache. Versions earlier than 1.2.0 had + various race conditions that made it necessary to use + <code>kill -9</code> at times to take out all the httpd servers. + But that should not be necessary any longer. You should follow + the <a href="../stopping.html">directions on how to stop + and restart Apache</a>. + + <p>As of Apache 1.3 there is a script + <code>src/support/apachectl</code> which, after a bit of + customization, is suitable for starting, stopping, and restarting + your server. + </p> + <hr> + + </li> + + <li><a name="redhat-htm"><strong>I'm using RedHat Linux and my .htm files are showing + up as html source rather than being formatted!</strong></a> + + <p>RedHat messed up and forgot to put a content type for <code>.htm</code> + files into <code>/etc/mime.types</code>. Edit <code>/etc/mime.types</code>, + find the line containing <code>html</code> and add <code>htm</code> to it. + Then restart your httpd server: + <pre> + kill -HUP `cat /var/run/httpd.pid` + </pre> + Then <b>clear your browsers' caches</b>. (Many browsers won't re-examine + the content type after they've reloaded a page.) + </p> + <hr> + + <li><a name="glibc-crypt"><strong>I'm using RedHat Linux 5.0, or some other glibc + based Linux system, and I get errors with the <code>crypt</code> function when + I attempt to build Apache 1.2.</strong></a> + + <p>glibc puts the crypt function into a separate library. Edit your + <code>src/Configuration</code> file and set this: + <pre> + EXTRA_LIBS=-lcrypt + </pre> + </p> + <hr> + + <!-- Don't forget to add HR tags at the end of each list item.. --> + +</OL> + <HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/client_block_api.html b/usr.sbin/httpd/htdocs/manual/misc/client_block_api.html new file mode 100644 index 00000000000..1d416586a6b --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/client_block_api.html @@ -0,0 +1,99 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Reading Client Input in Apache 1.2</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Reading Client Input in Apache 1.2</h1> + +<hr> + +<p>Apache 1.1 and earlier let modules handle POST and PUT requests by +themselves. The module would, on its own, determine whether the +request had an entity, how many bytes it was, and then called a +function (<code>read_client_block</code>) to get the data. + +<p>However, HTTP/1.1 requires several things of POST and PUT request +handlers that did not fit into this module, and all existing modules +have to be rewritten. The API calls for handling this have been +further abstracted, so that future HTTP protocol changes can be +accomplished while remaining backwards-compatible.</p> + +<hr> + +<h3>The New API Functions</h3> + +<pre> + int setup_client_block (request_rec *, int read_policy); + int should_client_block (request_rec *); + long get_client_block (request_rec *, char *buffer, int buffer_size); +</pre> + +<ol> +<li>Call <code>setup_client_block()</code> 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 +<pre> + 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. +</pre> + 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. + + + +<li>When you are ready to possibly accept input, call + <code>should_client_block()</code>. + This will tell the module whether or not to read input. If it is 0, + the module should assume that the input is of a non-entity type + (e.g. a GET request). A nonzero response indicates that the module + should proceed (to step 3). + This step also sends a 100 Continue response + to HTTP/1.1 clients, so should not be called until the module + is <strong>*definitely*</strong> ready to read content. (otherwise, the point of the + 100 response is defeated). Never call this function more than once. + +<li>Finally, call <code>get_client_block</code> in a loop. Pass it a + buffer and its + size. It will put data into the buffer (not necessarily the full + buffer, in the case of chunked inputs), 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. + +</ol> + +<p>As an example, please look at the code in +<code>mod_cgi.c</code>. This is properly written to the new API +guidelines.</p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/compat_notes.html b/usr.sbin/httpd/htdocs/manual/misc/compat_notes.html new file mode 100644 index 00000000000..b6594d354e6 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/compat_notes.html @@ -0,0 +1,149 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML><HEAD> +<TITLE>Apache HTTP Server: Compatibility Notes with NCSA's Server</TITLE> +</HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Compatibility Notes with NCSA's Server</H1> + +<HR> + +While Apache 0.8.x and beyond are for the most part a drop-in +replacement for NCSA's httpd and earlier versions of Apache, there are +a couple gotcha's to watch out for. These are mostly due to the fact +that the parser for config and access control files was rewritten from +scratch, so certain liberties the earlier servers took may not be +available here. These are all easily fixable. If you know of other +non-fatal problems that belong here, <a +href="http://www.apache.org/bugdb.cgi">let us know.</a> + +<P>Please also check the <A HREF="known_bugs.html">known bugs</A> page. + + + +<OL> + +<LI>The basic mod_auth <CODE>AuthGroupFile</CODE>-specified group file + format allows commas between user names - Apache does not.<BR> + <I>- added 12/1/96</I> + <p> + + <LI><P>If you follow the NCSA guidelines for setting up access restrictions + based on client domain, you may well have added entries for, + <CODE>AuthType, AuthName, AuthUserFile</CODE> or <CODE>AuthGroupFile</CODE>. + <B>None</B> of these are needed (or appropriate) for restricting access + based on client domain. + + <P>When Apache sees <CODE>AuthType</CODE> it (reasonably) assumes you + are using some authorization type based on username and password. + + <P>Please remove <CODE>AuthType</CODE>, it's unnecessary even for NCSA. + + <P> + + <LI><CODE>AuthUserFile</CODE> requires a full pathname. In earlier + versions of NCSA httpd and Apache, you could use a filename + relative to the .htaccess file. This could be a major security hole, + as it made it trivially easy to make a ".htpass" file in the a + directory easily accessible by the world. We recommend you store + your passwords outside your document tree. + + <P> + + <LI><CODE>OldScriptAlias</CODE> is no longer supported. + + <P> + + <LI><CODE>exec cgi=""</CODE> produces reasonable <B>malformed header</B> + responses when used to invoke non-CGI scripts.<BR> + The NCSA code ignores the missing header. (bad idea)<BR> + Solution: write CGI to the CGI spec or use <CODE>exec cmd=""</CODE> instead. + <P>We might add <CODE>virtual</CODE> support to <CODE>exec cmd</CODE> to + make up for this difference. + + <P> + + <LI><Limit> silliness - in the old Apache 0.6.5, a + directive of <Limit GET> would also restrict POST methods - Apache 0.8.8's new + core is correct in not presuming a limit on a GET is the same limit on a POST, + so if you are relying on that behavior you need to change your access configurations + to reflect that. + + <P> + + <LI>Icons for FancyIndexing broken - well, no, they're not broken, + we've just upgraded the + icons from flat .xbm files to pretty and much smaller .gif files, courtesy of +<a href="mailto:kevinh@eit.com">Kevin Hughes</a> at +<a href="http://www.eit.com/">EIT</a>. + If you are using the same srm.conf from an old distribution, make sure + you add the new + <A + HREF="../mod/mod_dir.html#addicon" + >AddIcon</A>, + <A + HREF="../mod/mod_dir.html#addiconbytype" + >AddIconByType</A>, + and + <A + HREF="../mod/mod_dir.html#defaulticon" + >DefaultIcon</A> + directives. + + <P> + + <LI>Under IRIX, the "Group" directive in httpd.conf needs to be a + valid group name + (<EM>i.e.</EM>, "nogroup") not the numeric group ID. The distribution + httpd.conf, and earlier ones, had the default Group be "#-1", which + was causing silent exits at startup.<p> + +<li><code>.asis</code> files: Apache 0.6.5 did not require a Status header; +it added one automatically if the .asis file contained a Location header. +0.8.14 requires a Status header. <p> + + <P> + <LI>Apache versions before 1.2b1 will ignore the last line of configuration + files if the last line does not have a trailing newline. This affects + configuration files (httpd.conf, access.conf and srm.conf), and + htpasswd and htgroup files. + </LI> + + <LI>Apache does not permit commas delimiting the methods in <Limit>. + + <LI>Apache's <CODE><VirtualHost></CODE> treats all addresses as + "optional" (i.e. the server should continue booting if it can't resolve + the address). Whereas in NCSA the default is to fail booting unless + an added <code>optional</code> keyword is included. + + <LI>Apache does not implement <CODE>OnDeny</CODE> use + <a href="../mod/core.html#errordocument"><code>ErrorDocument</code></a> + instead. + +</OL> + +More to come when we notice them.... + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/descriptors.html b/usr.sbin/httpd/htdocs/manual/misc/descriptors.html new file mode 100644 index 00000000000..5207eee0fbd --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/descriptors.html @@ -0,0 +1,168 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Descriptors and Apache</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Descriptors and Apache</H1> + +<p>A <EM>descriptor</EM>, also commonly called a <EM>file handle</EM> is +an object that a program uses to read or write an open file, or open +network socket, or a variety of other devices. It is represented +by an integer, and you may be familiar with <code>stdin</code>, +<code>stdout</code>, and <code>stderr</code> which are descriptors 0, +1, and 2 respectively. +Apache needs a descriptor for each log file, plus one for each +network socket that it listens on, plus a handful of others. Libraries +that Apache uses may also require descriptors. Normal programs don't +open up many descriptors at all, and so there are some latent problems +that you may experience should you start running Apache with many +descriptors (i.e. with many virtual hosts). + +<p>The operating system enforces a limit on the number of descriptors +that a program can have open at a time. There are typically three limits +involved here. One is a kernel limitation, depending on your operating +system you will either be able to tune the number of descriptors available +to higher numbers (this is frequently called <EM>FD_SETSIZE</EM>). Or you +may be stuck with a (relatively) low amount. The second limit is called +the <EM>hard resource</EM> limit, and it is sometimes set by root in an +obscure operating system file, but frequently is the same as the kernel +limit. The third limit is called the <EM>soft +resource</EM> limit. The soft limit is always less than or equal to +the hard limit. For example, the hard limit may be 1024, but the soft +limit only 64. Any user can raise their soft limit up to the hard limit. +Root can raise the hard limit up to the system maximum limit. The soft +limit is the actual limit that is used when enforcing the maximum number +of files a process can have open. + +<p>To summarize: + +<center><pre> + #open files <= soft limit <= hard limit <= kernel limit +</pre></center> + +<p>You control the hard and soft limits using the <code>limit</code> (csh) +or <code>ulimit</code> (sh) directives. See the respective man pages +for more information. For example you can probably use +<code>ulimit -n unlimited</code> to raise your soft limit up to the +hard limit. You should include this command in a shell script which +starts your webserver. + +<p>Unfortunately, it's not always this simple. As mentioned above, +you will probably run into some system limitations that will need to be +worked around somehow. Work was done in version 1.2.1 to improve the +situation somewhat. Here is a partial list of systems and workarounds +(assuming you are using 1.2.1 or later): + +<dl> + + <dt><STRONG>BSDI 2.0</STRONG> + <dd>Under BSDI 2.0 you can build Apache to support more descriptors + by adding <code>-DFD_SETSIZE=nnn</code> to + <code>EXTRA_CFLAGS</code> (where nnn is the number of descriptors + you wish to support, keep it less than the hard limit). But it + will run into trouble if more than approximately 240 Listen + directives are used. This may be cured by rebuilding your kernel + with a higher FD_SETSIZE. + <p> + + <dt><STRONG>FreeBSD 2.2, BSDI 2.1+</STRONG> + <dd>Similar to the BSDI 2.0 case, you should define + <code>FD_SETSIZE</code> and rebuild. But the extra + Listen limitation doesn't exist. + <p> + + <dt><STRONG>Linux</STRONG> + <dd>By default Linux has a kernel maximum of 256 open descriptors + per process. There are several patches available for the + 2.0.x series which raise this to 1024 and beyond, and you + can find them in the "unofficial patches" section of <a + href="http://www.linuxhq.com/">the Linux Information HQ</a>. + None of these patches are perfect, and an entirely different + approach is likely to be taken during the 2.1.x development. + Applying these patches will raise the FD_SETSIZE used to compile + all programs, and unless you rebuild all your libraries you should + avoid running any other program with a soft descriptor limit above + 256. As of this writing the patches available for increasing + the number of descriptors do not take this into account. On a + dedicated webserver you probably won't run into trouble. + <p> + + <dt><STRONG>Solaris through 2.5.1</STRONG> + <dd>Solaris has a kernel hard limit of 1024 (may be lower in earlier + versions). But it has a limitation that files using + the stdio library cannot have a descriptor above 255. + Apache uses the stdio library for the ErrorLog directive. + When you have more than approximately 110 virtual hosts + (with an error log and an access log each) you will need to + build Apache with <code>-DHIGH_SLACK_LINE=256</code> added to + <code>EXTRA_CFLAGS</code>. You will be limited to approximately + 240 error logs if you do this. + <p> + + <dt><STRONG>AIX</STRONG> + <dd>AIX version 3.2?? appears to have a hard limit of 128 descriptors. + End of story. Version 4.1.5 has a hard limit of 2000. + <p> + + <dt><STRONG>Others</STRONG> + <dd>If you have details on another operating system, please submit + it through our <a href="http://www.apache.org/bug_report.html">Bug + Report Page</a>. + <p> + +</dl> + +<p>In addition to the problems described above there are problems with +many libraries that Apache uses. The most common example is the bind +DNS resolver library that is used by pretty much every unix, which +fails if it ends up with a descriptor above 256. We suspect there +are other libraries that similar limitations. So the code as of 1.2.1 +takes a defensive stance and tries to save descriptors less than 16 +for use while processing each request. This is called the <EM>low +slack line</EM>. + +<p>Note that this shouldn't waste descriptors. If you really are pushing +the limits and Apache can't get a descriptor above 16 when it wants +it, it will settle for one below 16. + +<p>In extreme situations you may want to lower the low slack line, +but you shouldn't ever need to. For example, lowering it can +increase the limits 240 described above under Solaris and BSDI 2.0. +But you'll play a delicate balancing game with the descriptors needed +to serve a request. Should you want to play this game, the compile +time parameter is <code>LOW_SLACK_LINE</code> and there's a tiny +bit of documentation in the header file <code>httpd.h</code>. + +<p>Finally, if you suspect that all this slack stuff is causing you +problems, you can disable it. Add <code>-DNO_SLACK</code> to +<code>EXTRA_CFLAGS</code> and rebuild. But please report it to +our <a href="http://www.apache.org/bug_report.html">Bug +Report Page</a> so that +we can investigate. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/fin_wait_2.html b/usr.sbin/httpd/htdocs/manual/misc/fin_wait_2.html new file mode 100644 index 00000000000..e4c4f5a1ad9 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/fin_wait_2.html @@ -0,0 +1,340 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Connections in FIN_WAIT_2 and Apache</TITLE> +<LINK REV="made" HREF="mailto:marc@apache.org"> + +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Connections in the FIN_WAIT_2 state and Apache</H1> +<OL> +<LI><H2>What is the FIN_WAIT_2 state?</H2> +Starting with the Apache 1.2 betas, people are reporting many more +connections in the FIN_WAIT_2 state (as reported by +<code>netstat</code>) than they saw using older versions. When the +server closes a TCP connection, it sends a packet with the FIN bit +sent to the client, which then responds with a packet with the ACK bit +set. The client then sends a packet with the FIN bit set to the +server, which responds with an ACK and the connection is closed. The +state that the connection is in during the period between when the +server gets the ACK from the client and the server gets the FIN from +the client is known as FIN_WAIT_2. See the <A +HREF="ftp://ds.internic.net/rfc/rfc793.txt">TCP RFC</A> for the +technical details of the state transitions.<P> + +The FIN_WAIT_2 state is somewhat unusual in that there is no timeout +defined in the standard for it. This means that on many operating +systems, a connection in the FIN_WAIT_2 state will stay around until +the system is rebooted. If the system does not have a timeout and +too many FIN_WAIT_2 connections build up, it can fill up the space +allocated for storing information about the connections and crash +the kernel. The connections in FIN_WAIT_2 do not tie up an httpd +process.<P> + +<LI><H2>But why does it happen?</H2> + +There are several reasons for it happening, and not all of them are +fully understood by the Apache team yet. What is known follows.<P> + +<H3>Buggy clients and persistent connections</H3> + +Several clients have a bug which pops up when dealing with +<A HREF="../keepalive.html">persistent connections</A> (aka keepalives). +When the connection is idle and the server closes the connection +(based on the <A HREF="../mod/core.html#keepalivetimeout"> +KeepAliveTimeout</A>), the client is programmed so that the client does +not send back a FIN and ACK to the server. This means that the +connection stays in the FIN_WAIT_2 state until one of the following +happens:<P> +<UL> + <LI>The client opens a new connection to the same or a different + site, which causes it to fully close the older connection on + that socket. + <LI>The user exits the client, which on some (most?) clients + causes the OS to fully shutdown the connection. + <LI>The FIN_WAIT_2 times out, on servers that have a timeout + for this state. +</UL><P> +If you are lucky, this means that the buggy client will fully close the +connection and release the resources on your server. However, there +are some cases where the socket is never fully closed, such as a dialup +client disconnecting from their provider before closing the client. +In addition, a client might sit idle for days without making another +connection, and thus may hold its end of the socket open for days +even though it has no further use for it. +<STRONG>This is a bug in the browser or in its operating system's +TCP implementation.</STRONG> <P> + +The clients on which this problem has been verified to exist:<P> +<UL> + <LI>Mozilla/3.01 (X11; I; FreeBSD 2.1.5-RELEASE i386) + <LI>Mozilla/2.02 (X11; I; FreeBSD 2.1.5-RELEASE i386) + <LI>Mozilla/3.01Gold (X11; I; SunOS 5.5 sun4m) + <LI>MSIE 3.01 on the Macintosh + <LI>MSIE 3.01 on Windows 95 +</UL><P> + +This does not appear to be a problem on: +<UL> + <LI>Mozilla/3.01 (Win95; I) +</UL> +<P> + +It is expected that many other clients have the same problem. What a +client <STRONG>should do</STRONG> is periodically check its open +socket(s) to see if they have been closed by the server, and close their +side of the connection if the server has closed. This check need only +occur once every few seconds, and may even be detected by a OS signal +on some systems (e.g., Win95 and NT clients have this capability, but +they seem to be ignoring it).<P> + +Apache <STRONG>cannot</STRONG> avoid these FIN_WAIT_2 states unless it +disables persistent connections for the buggy clients, just +like we recommend doing for Navigator 2.x clients due to other bugs. +However, non-persistent connections increase the total number of +connections needed per client and slow retrieval of an image-laden +web page. Since non-persistent connections have their own resource +consumptions and a short waiting period after each closure, a busy server +may need persistence in order to best serve its clients.<P> + +As far as we know, the client-caused FIN_WAIT_2 problem is present for +all servers that support persistent connections, including Apache 1.1.x +and 1.2.<P> + +<H3>Something in Apache may be broken</H3> + +While the above bug is a problem, it is not the whole problem. +Some users have observed no FIN_WAIT_2 problems with Apache 1.1.x, +but with 1.2b enough connections build up in the FIN_WAIT_2 state to +crash their server. We have not yet identified why this would occur +and welcome additional test input.<P> + +One possible (and most likely) source for additional FIN_WAIT_2 states +is a function called <CODE>lingering_close()</CODE> which was added +between 1.1 and 1.2. This function is necessary for the proper +handling of persistent connections and any request which includes +content in the message body (e.g., PUTs and POSTs). +What it does is read any data sent by the client for +a certain time after the server closes the connection. The exact +reasons for doing this are somewhat complicated, but involve what +happens if the client is making a request at the same time the +server sends a response and closes the connection. Without lingering, +the client might be forced to reset its TCP input buffer before it +has a chance to read the server's response, and thus understand why +the connection has closed. +See the <A HREF="#appendix">appendix</A> for more details.<P> + +We have not yet tracked down the exact reason why +<CODE>lingering_close()</CODE> causes problems. Its code has been +thoroughly reviewed and extensively updated in 1.2b6. It is possible +that there is some problem in the BSD TCP stack which is causing the +observed problems. It is also possible that we fixed it in 1.2b6. +Unfortunately, we have not been able to replicate the problem on our +test servers.<P> + +<H2><LI>What can I do about it?</H2> + +There are several possible workarounds to the problem, some of +which work better than others.<P> + +<H3>Add a timeout for FIN_WAIT_2</H3> + +The obvious workaround is to simply have a timeout for the FIN_WAIT_2 state. +This is not specified by the RFC, and could be claimed to be a +violation of the RFC, but it is widely recognized as being necessary. +The following systems are known to have a timeout: +<P> +<UL> + <LI><A HREF="http://www.freebsd.org/">FreeBSD</A> versions starting at 2.0 or possibly earlier. + <LI><A HREF="http://www.netbsd.org/">NetBSD</A> version 1.2(?) + <LI><A HREF="http://www.openbsd.org/">OpenBSD</A> all versions(?) + <LI><A HREF="http://www.bsdi.com/">BSD/OS</A> 2.1, with the + <A HREF="ftp://ftp.bsdi.com/bsdi/patches/patches-2.1/K210-027"> + K210-027</A> patch installed. + <LI><A HREF="http://www.sun.com/">Solaris</A> as of around version + 2.2. The timeout can be tuned by using <CODE>ndd</CODE> to + modify <CODE>tcp_fin_wait_2_flush_interval</CODE>, but the + default should be appropriate for most servers and improper + tuning can have negative impacts. + <LI><A HREF="http://www.sco.com/">SCO TCP/IP Release 1.2.1</A> + can be modified to have a timeout by following + <A HREF="http://www.sco.com/cgi-bin/waisgate?WAISdocID=2242622956+0+0+0&WAISaction=retrieve"> SCO's instructions</A>. + <LI><A HREF="http://www.linux.org/">Linux</A> 2.0.x and + earlier(?) + <LI><A HREF="http://www.hp.com/">HP-UX</A> 10.x defaults to + terminating connections in the FIN_WAIT_2 state after the + normal keepalive timeouts. This does not + refer to the persistent connection or HTTP keepalive + timeouts, but the <CODE>SO_LINGER</CODE> socket option + which is enabled by Apache. This parameter can be adjusted + by using <CODE>nettune</CODE> to modify parameters such as + <CODE>tcp_keepstart</CODE> and <CODE>tcp_keepstop</CODE>. + In later revisions, there is an explicit timer for + connections in FIN_WAIT_2 that can be modified; contact HP + support for details. + <LI><A HREF="http://www.sgi.com/">SGI IRIX</A> can be patched to + support a timeout. For IRIX 5.3, 6.2, and 6.3, + use patches 1654, 1703 and 1778 respectively. If you + have trouble locating these patches, please contact your + SGI support channel for help. + <LI><A HREF="http://www.ncr.com/">NCR's MP RAS Unix</A> 2.xx and + 3.xx both have FIN_WAIT_2 timeouts. In 2.xx it is non-tunable + at 600 seconds, while in 3.xx it defaults to 600 seconds and + is calculated based on the tunable "max keep alive probes" + (default of 8) multiplied by the "keep alive interval" (default + 75 seconds). + <LI><A HREF="http://www.sequent.com">Squent's ptx/TCP/IP for + DYNIX/ptx</A> has had a FIN_WAIT_2 timeout since around + release 4.1 in mid-1994. +</UL> +<P> +The following systems are known to not have a timeout: +<P> +<UL> + <LI><A HREF="http://www.sun.com/">SunOS 4.x</A> does not and + almost certainly never will have one because it as at the + very end of its development cycle for Sun. If you have kernel + source should be easy to patch. +</UL> +<P> +There is a +<A HREF="http://www.apache.org/dist/contrib/patches/1.2/fin_wait_2.patch"> +patch available</A> for adding a timeout to the FIN_WAIT_2 state; it +was originally intended for BSD/OS, but should be adaptable to most +systems using BSD networking code. You need kernel source code to be +able to use it. If you do adapt it to work for any other systems, +please drop me a note at <A HREF="mailto:marc@apache.org">marc@apache.org</A>. +<P> +<H3>Compile without using <CODE>lingering_close()</CODE></H3> + +It is possible to compile Apache 1.2 without using the +<CODE>lingering_close()</CODE> function. This will result in that +section of code being similar to that which was in 1.1. If you do +this, be aware that it can cause problems with PUTs, POSTs and +persistent connections, especially if the client uses pipelining. +That said, it is no worse than on 1.1, and we understand that keeping your +server running is quite important.<P> + +To compile without the <CODE>lingering_close()</CODE> function, add +<CODE>-DNO_LINGCLOSE</CODE> to the end of the +<CODE>EXTRA_CFLAGS</CODE> line in your <CODE>Configuration</CODE> file, +rerun <CODE>Configure</CODE> and rebuild the server. +<P> +<H3>Use <CODE>SO_LINGER</CODE> as an alternative to +<CODE>lingering_close()</CODE></H3> + +On most systems, there is an option called <CODE>SO_LINGER</CODE> that +can be set with <CODE>setsockopt(2)</CODE>. It does something very +similar to <CODE>lingering_close()</CODE>, except that it is broken +on many systems so that it causes far more problems than +<CODE>lingering_close</CODE>. On some systems, it could possibly work +better so it may be worth a try if you have no other alternatives. <P> + +To try it, add <CODE>-DUSE_SO_LINGER -DNO_LINGCLOSE</CODE> to the end of the +<CODE>EXTRA_CFLAGS</CODE> line in your <CODE>Configuration</CODE> +file, rerun <CODE>Configure</CODE> and rebuild the server. <P> + +<STRONG>NOTE:</STRONG> Attempting to use <CODE>SO_LINGER</CODE> and +<CODE>lingering_close()</CODE> at the same time is very likely to do +very bad things, so don't.<P> + +<H3>Increase the amount of memory used for storing connection state</H3> +<DL> +<DT>BSD based networking code: +<DD>BSD stores network data, such as connection states, +in something called an mbuf. When you get so many connections +that the kernel does not have enough mbufs to put them all in, your +kernel will likely crash. You can reduce the effects of the problem +by increasing the number of mbufs that are available; this will not +prevent the problem, it will just make the server go longer before +crashing.<P> + +The exact way to increase them may depend on your OS; look +for some reference to the number of "mbufs" or "mbuf clusters". On +many systems, this can be done by adding the line +<CODE>NMBCLUSTERS="n"</CODE>, where <CODE>n</CODE> is the number of +mbuf clusters you want to your kernel config file and rebuilding your +kernel.<P> +</DL> + +<H3>Disable KeepAlive</H3> +<P>If you are unable to do any of the above then you should, as a last +resort, disable KeepAlive. Edit your httpd.conf and change "KeepAlive On" +to "KeepAlive Off". + +<H2><LI>Feedback</H2> + +If you have any information to add to this page, please contact me at +<A HREF="mailto:marc@apache.org">marc@apache.org</A>.<P> + +<H2><A NAME="appendix"><LI>Appendix</A></H2> +<P> +Below is a message from Roy Fielding, one of the authors of HTTP/1.1. + +<H3>Why the lingering close functionality is necessary with HTTP</H3> + +The need for a server to linger on a socket after a close is noted a couple +times in the HTTP specs, but not explained. This explanation is based on +discussions between myself, Henrik Frystyk, Robert S. Thau, Dave Raggett, +and John C. Mallery in the hallways of MIT while I was at W3C.<P> + +If a server closes the input side of the connection while the client +is sending data (or is planning to send data), then the server's TCP +stack will signal an RST (reset) back to the client. Upon +receipt of the RST, the client will flush its own incoming TCP buffer +back to the un-ACKed packet indicated by the RST packet argument. +If the server has sent a message, usually an error response, to the +client just before the close, and the client receives the RST packet +before its application code has read the error message from its incoming +TCP buffer and before the server has received the ACK sent by the client +upon receipt of that buffer, then the RST will flush the error message +before the client application has a chance to see it. The result is +that the client is left thinking that the connection failed for no +apparent reason.<P> + +There are two conditions under which this is likely to occur: +<OL> +<LI>sending POST or PUT data without proper authorization +<LI>sending multiple requests before each response (pipelining) + and one of the middle requests resulting in an error or + other break-the-connection result. +</OL> +<P> +The solution in all cases is to send the response, close only the +write half of the connection (what shutdown is supposed to do), and +continue reading on the socket until it is either closed by the +client (signifying it has finally read the response) or a timeout occurs. +That is what the kernel is supposed to do if SO_LINGER is set. +Unfortunately, SO_LINGER has no effect on some systems; on some other +systems, it does not have its own timeout and thus the TCP memory +segments just pile-up until the next reboot (planned or not).<P> + +Please note that simply removing the linger code will not solve the +problem -- it only moves it to a different and much harder one to detect. +</OL> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/footer.html b/usr.sbin/httpd/htdocs/manual/misc/footer.html new file mode 100644 index 00000000000..cbbcf9a8e2c --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/footer.html @@ -0,0 +1,7 @@ +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> diff --git a/usr.sbin/httpd/htdocs/manual/misc/header.html b/usr.sbin/httpd/htdocs/manual/misc/header.html new file mode 100644 index 00000000000..3a472e66722 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/header.html @@ -0,0 +1,6 @@ +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> diff --git a/usr.sbin/httpd/htdocs/manual/misc/howto.html b/usr.sbin/httpd/htdocs/manual/misc/howto.html new file mode 100644 index 00000000000..c3ddf868066 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/howto.html @@ -0,0 +1,160 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<META NAME="description" CONTENT="Some 'how to' tips for the Apache httpd server"> +<META NAME="keywords" CONTENT="apache,redirect,robots,rotate,logfiles"> +<TITLE>Apache HOWTO documentation</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Apache HOWTO documentation</H1> + +How to: +<ul> +<li><A HREF="#redirect">redirect an entire server or directory to a single URL</A> +<li><A HREF="#logreset">reset your log files</A> +<li><A HREF="#stoprob">stop/restrict robots</A> +</ul> + +<HR> +<H2><A name="redirect">How to redirect an entire server or directory to a single URL</A></H2> + +<P>There are two chief ways to redirect all requests for an entire +server to a single location: one which requires the use of +<code>mod_rewrite</code>, and another which uses a CGI script. + +<P>First: if all you need to do is migrate a server from one name to +another, simply use the <code>Redirect</code> directive, as supplied +by <code>mod_alias</code>: + +<blockquote><pre> + Redirect / http://www.apache.org/ +</pre></blockquote> + +<P>Since <code>Redirect</code> will forward along the complete path, +however, it may not be appropriate - for example, when the directory +structure has changed after the move, and you simply want to direct people +to the home page. + +<P>The best option is to use the standard Apache module <code>mod_rewrite</code>. +If that module is compiled in, the following lines: + +<blockquote><pre>RewriteEngine On +RewriteRule /.* http://www.apache.org/ [R] +</pre></blockquote> + +This will send an HTTP 302 Redirect back to the client, and no matter +what they gave in the original URL, they'll be sent to +"http://www.apache.org". + +The second option is to set up a <CODE>ScriptAlias</Code> pointing to +a <B>cgi script</B> which outputs a 301 or 302 status and the location +of the other server.</P> + +<P>By using a <B>cgi-script</B> you can intercept various requests and +treat them specially, e.g. you might want to intercept <B>POST</B> +requests, so that the client isn't redirected to a script on the other +server which expects POST information (a redirect will lose the POST +information.) You might also want to use a CGI script if you don't +want to compile mod_rewrite into your server. + +<P>Here's how to redirect all requests to a script... In the server +configuration file, +<blockquote><pre>ScriptAlias / /usr/local/httpd/cgi-bin/redirect_script</pre></blockquote> + +and here's a simple perl script to redirect requests: + +<blockquote><pre> +#!/usr/local/bin/perl + +print "Status: 302 Moved Temporarily\r +Location: http://www.some.where.else.com/\r\n\r\n"; + +</pre></blockquote></P> + +<HR> + +<H2><A name="logreset">How to reset your log files</A></H2> + +<P>Sooner or later, you'll want to reset your log files (access_log and +error_log) because they are too big, or full of old information you don't +need.</P> + +<P><CODE>access.log</CODE> typically grows by 1Mb for each 10,000 requests.</P> + +<P>Most people's first attempt at replacing the logfile is to just move the +logfile or remove the logfile. This doesn't work.</P> + +<P>Apache will continue writing to the logfile at the same offset as before the +logfile moved. This results in a new logfile being created which is just +as big as the old one, but it now contains thousands (or millions) of null +characters.</P> + +<P>The correct procedure is to move the logfile, then signal Apache to tell it to reopen the logfiles.</P> + +<P>Apache is signaled using the <B>SIGHUP</B> (-1) signal. e.g. +<blockquote><code> +mv access_log access_log.old<BR> +kill -1 `cat httpd.pid` +</code></blockquote> +</P> + +<P>Note: <code>httpd.pid</code> is a file containing the <B>p</B>rocess <B>id</B> +of the Apache httpd daemon, Apache saves this in the same directory as the log +files.</P> + +<P>Many people use this method to replace (and backup) their logfiles on a +nightly or weekly basis.</P> +<HR> + +<H2><A name="stoprob">How to stop or restrict robots</A></H2> + +<P>Ever wondered why so many clients are interested in a file called +<code>robots.txt</code> which you don't have, and never did have?</P> + +<P>These clients are called <B>robots</B> (also known as crawlers, +spiders and other cute name) - special automated clients which +wander around the web looking for interesting resources.</P> + +<P>Most robots are used to generate some kind of <em>web index</em> which +is then used by a <em>search engine</em> to help locate information.</P> + +<P><code>robots.txt</code> provides a means to request that robots limit their +activities at the site, or more often than not, to leave the site alone.</P> + +<P>When the first robots were developed, they had a bad reputation for sending hundreds/thousands of requests to each site, often resulting in the site being overloaded. Things have improved dramatically since then, thanks to <A HREF="http://info.webcrawler.com/mak/projects/robots/guidelines.html"> Guidelines for Robot Writers</A>, but even so, some robots may <A HREF="http://www.zyzzyva.com/robots/alert/">exhibit unfriendly behavior</A> which the webmaster isn't willing to tolerate, and will want to stop.</P> + +<P>Another reason some webmasters want to block access to robots, is to +stop them indexing dynamic information. Many search engines will use the +data collected from your pages for months to come - not much use if your +serving stock quotes, news, weather reports or anything else that will be +stale by the time people find it in a search engine.</P> + +<P>If you decide to exclude robots completely, or just limit the areas +in which they can roam, create a <CODE>robots.txt</CODE> file; refer +to the <A HREF="http://info.webcrawler.com/mak/projects/robots/robots.html">robot information pages</A> provided by Martijn Koster for the syntax.</P> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/index.html b/usr.sbin/httpd/htdocs/manual/misc/index.html new file mode 100644 index 00000000000..d7469c16f07 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/index.html @@ -0,0 +1,132 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Apache Miscellaneous Documentation</TITLE> + </HEAD> + + <!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + <H1 ALIGN="CENTER">Apache Miscellaneous Documentation</h1> + + <P> + Below is a list of additional documentation pages that apply to the + Apache web server development project. + </P> + <DL> + <DT><A + HREF="API.html" + >API</A> + </DT> + <DD>Description of Apache's Application Programming Interface. + </DD> + <DT><A + HREF="FAQ.html" + >FAQ</A> + </DT> + <DD>Frequently-Asked Questions concerning the Apache project and server + </DD> + <DT><A + HREF="client_block_api.html" + >Reading Client Input in Apache 1.2</A> + </DT> + <DD>Describes differences between Apache 1.1 and 1.2 in how modules + read information from the client + </DD> + <DT><A + HREF="compat_notes.html" + >Compatibility with NCSA</A> + </DT> + <DD>Notes about Apache's compatibility with the NCSA server + </DD> + <DT><A + HREF="fin_wait_2.html" + ><SAMP>FIN_WAIT_2</SAMP></A> + </DT> + <DD>A description of the causes of Apache processes going into the + <SAMP>FIN_WAIT_2</SAMP> state, and what you can do about it + </DD> + <DT><A + HREF="howto.html" + >"How-To"</A> + </DT> + <DD>Instructions about how to accomplish some commonly-desired server + functionality changes + </DD> + <DT><A + HREF="known_bugs.html" + >Known Bugs</A> + </DT> + <DD>Just what it says - a list of known bugs in each of the Apache releases + </DD> + <DT><A + HREF="nopgp.html" + >No PGP</A> + </DT> + <DD>Why we took PEM and PGP support out of the base Apache distribution + </DD> + <DT><A + HREF="perf-bsd44.html" + >Performance Notes (BSD 4.4)</A> + </DT> + <DD>Some notes about ways to improve/optimize Apache performance on + BSD 4.4 systems + </DD> + <DT><A + HREF="perf-dec.html" + >Performance Notes (Digital UNIX)</A> + </DT> + <DD>Extracts of USENET postings describing how to optimize Apache + performance on Digital UNIX systems + </DD> + <DT><A + HREF="perf.html" + >Performance Notes (General)</A> + </DT> + <DD>Some generic notes about how to improve Apache performance + </DD> + <DT><A + HREF="security_tips.html" + >Security Tips</A> + </DT> + <DD>Some "do"s - and "don't"s - for keeping your + Apache web site secure + </DD> + <DT><A + HREF="vif-info.html" + >Virtual Hosts (IP-based)</A> + </DT> + <DD>Excerpts and notes about configuring and using Apache IP-based virtual + hosts + </DD> + <DT><A + HREF="windoz_keepalive.html" + >Windows Bug with Web Keepalive</A> + </DT> + <DD>A brief description of a known problem with Microsoft Windows and + web sites accessed using keepalive connections + </DD> + </DL> + + <HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + + </BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/known_bugs.html b/usr.sbin/httpd/htdocs/manual/misc/known_bugs.html new file mode 100644 index 00000000000..95bb37c705d --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/known_bugs.html @@ -0,0 +1,171 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache HTTP Server Project</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Known Bugs in Apache</H1> + +The most up-to-date resource for bug tracking and information is the +<A HREF="http://www.apache.org/bugdb.cgi">Apache bug database</A>. +Significant bugs at release time will also be noted there. +If you are running a 1.2 beta release or version 1.1.3 or earlier +and thing you have found a bug, please upgrade to 1.2. Many bugs +in early versions have been fixed in 1.2. + +<P>See Also: <A HREF="compat_notes.html">Compatibility notes</A></P> +<HR> + +<H2>Apache 1.2 Bugs</H2> + +<OL> + <LI><b>Exists in 1.2.1 only.</b> + On Solaris 2.x the server will stop running after receiving a + SIGHUP. Four workarounds exist (choose one):<p> + <ul> + <li>Retrieve <a href="http://www.apache.org/dist/patches/apply_to_1.2.1/solaris_hup.patch">this patch</a>. + <code>cd</code> to your <code>apache_1.2.1</code> directory, and + type <code>patch -s -p1 < /path/to/patchfile</code>. Then rebuild + Apache.<p> + <li>Use SIGUSR1 instead of SIGHUP, see <a href="../stopping.html"> + Stopping and Restarting Apache</a> for more details.<p> + <li>Add <code>-DNO_SLACK</code> to + <code>EXTRA_CFLAGS</code> in + your <code>Configuration</code> file, re-run <code>Configure</code> + and rebuild your server. This disables the + <a href="descriptors.html">descriptor slack workaround</a><p> + <li>(Least preferable) Use Apache 1.2.0 instead of 1.2.1.<p> + </ul> + This problem will be tracked as + <a href="http://www.apache.org/bugdb.cgi/full/832">PR#832</a>.<p> + </li> + + <LI>(Exists in 1.2.0 and in 1.2.1 after either of the + <code>NO_SLACK</code> or patch provided by the previous bug are applied.) + Solaris 2.5.1 (and probably other versions of Solaris) appear to have + a race condition completely unrelated to all the others. It is possible + during a SIGHUP that the server will fail to start because it will not + be able to re-open its sockets. To our knowledge this has only shown + up during testing when we pummel the server with as many SIGHUP requests + per second as we can. This appears unrelated to the similar sounding bug + described in <a href="http://www.apache.org/bugdb.cgi/full/832">PR#832</a>. + <p> + + <LI><a name="listenbug">On some architectures</A> + if your configuration uses multiple + <a href="../mod/core.html#listen">Listen</a> directives then it is possible + that the server will starve one of the sockets while serving hits on + another. The work-around is to add + <code>-DUSE_FLOCK_SERIALIZED_ACCEPT</code> to the + <code>EXTRA_CFLAGS</code> line in your Configuration and rebuild. + (If you encounter problems with that, you can also try + <code>-DUSE_FCNTL_SERIALIZED_ACCEPT</code>.) + This affects any architecture that doesn't use one of the + <code>USE_xxxxx_SERIALIZED_ACCEPT</code> definitions, see the + source file <code>conf.h</code> for your architecture. + This will be tracked as + <a href="http://www.apache.org/bugdb.cgi/full/467">PR#467</a>. + <P></LI> + + <LI><b>Fixed in 1.2.1.</b> + <a name="cnegbug">Apache's</A> <A HREF="../content-negotiation.html">Content + Negotiation</A> should pick the smallest variant if there + are several that are equally acceptable. A bug in 1.2 means it no + longer does this unless all the variants have character sets. + This <A HREF="../../dist/contrib/patches/1.2/conneg-bug.patch">patch</A> + fixes this problem. It also fixes the problem which makes Apache + pick the last equally acceptable variant instead of the first. + This will be tracked as + <a href="http://www.apache.org/bugdb.cgi/full/94">PR#94</a>. + <P></LI> + + <LI> + The PATH_INFO part of a request URI cannot include the sequence + <CODE>%2f</CODE>. This will be tracked as + <A HREF="http://www.apache.org/bugdb.cgi/full/543">PR#543</A>. + <P></LI> + + <LI>Users of early 1.2 betas reported problems with many + connections stuck in the FIN_WAIT_2 state due to server + timeouts. Several changes were made during the beta testing of 1.2 + to reduce this problem as much as possible, although you may still + see sockets in FIN_WAIT_2 state due to network or operating system + issues outside the control of Apache. See our <A + HREF="fin_wait_2.html">FIN_WAIT_2</A> page for more details. + + <P>SunOS4 has a kernel bug in the allocation of memory for the mbuf table. + When it fills up, the result is a Panic the next time any routine tries + to set something in an imaginary mbuf beyond the range of the table. + Due to buggy browser behavior and the lack of a FIN_WAIT_2 timeout + on SunOS4, "KeepAlive Off" is necessary to avoid filling up the mbuf + table on busy sites. + <P></LI> + + <LI>Compilation fails on SCO3 when using gcc instead of cc, complaining + with "<CODE>gcc: noinline: No such file or directory</CODE>". Fix + is given in <a href="http://www.apache.org/bugdb.cgi/full/695">PR#695</A>. + <P></LI> + + <LI>If compilation fails complaining about "unknown symbol __inet_ntoa()" + then you have probably installed version 8 of bind. You will need to + explicitly link with the bind library by adding <CODE>-lbind</CODE> + to <CODE>EXTRA_LDFLAGS</CODE> in <CODE>Configuration</CODE>. See + <A HREF="http://www.apache.org/bugdb.cgi/full/616">PR#616</A> + and the + <A HREF="FAQ.html#bind8.1">Apache FAQ</A>. + <P></LI> + + <LI>The message "<CODE>created shared memory segment #730499</CODE>" + in error_log is not an error and should be ignored. See + <a href="http://www.apache.org/bugdb.cgi/full/696">PR#696</A>. + <P></LI> + + <LI> + Compiling on Solaris 2 with SunSoft's C compiler gives the warning + <CODE>"mod_include.c", line 1123: warning: end-of-loop code not + reached</CODE>. This is a bogus warning and can be ignored. + See <A HREF="http://www.apache.org/bugdb.cgi/full/681">PR#681</A>. + <P></LI> + + <LI><a href="descriptors.html"><b>Workaround available in 1.2.1.</b></a> + There appears to be a problem on BSDI 2.1 with large numbers of + virtual hosts. This appears similar to a file-descriptor limit + but BSDI should not have this problem. This will be tracked as + <A HREF="http://www.apache.org/bugdb.cgi/full/611">PR#611</A>. + See also the <A HREF="FAQ.html#fdlim">Apache FAQ</A>. + <P></LI> + + <LI><a href="descriptors.html"><b>Workaround available in 1.2.1.</b></a> + Solaris 2 has problems with large numbers of virtual hosts. This is + because of an operating system limit of 256 file pointers, not due + to Apache. See also the <A HREF="FAQ.html#fdlim">Apache FAQ</A>. + <P></LI> + +</OL> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/misc/known_client_problems.html b/usr.sbin/httpd/htdocs/manual/misc/known_client_problems.html new file mode 100644 index 00000000000..4f64e06a2f4 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/known_client_problems.html @@ -0,0 +1,213 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache HTTP Server Project</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Known Problems in Clients</H1> + +<p>Over time the Apache Group has discovered or been notified of problems +with various clients which we have had to work around. This document +describes these problems and the workarounds available. It's not arranged +in any particular order. Some familiarity with the standards is assumed, +but not necessary. + +<p>For brevity, <i>Navigator</i> will refer to Netscape's Navigator +product, and <i>MSIE</i> will refer to Microsoft's Internet Explorer +product. All trademarks and copyrights belong to their respective +companies. We welcome input from the various client authors to correct +inconsistencies in this paper, or to provide us with exact version +numbers where things are broken/fixed. + +<p>For reference, +<a href="ftp://ds.internic.net/rfc/rfc1945.txt">RFC1945</a> +defines HTTP/1.0, and +<a href="ftp://ds.internic.net/rfc/rfc2068.txt">RFC2068</a> +defines HTTP/1.1. Apache as of version 1.2 is an HTTP/1.1 server (with an +optional HTTP/1.0 proxy). + +<p>Various of these workarounds are triggered by environment variables. +The admin typically controls which are set, and for which clients, by using +<a href="../mod/mod_browser.html">mod_browser</a>. Unless otherwise +noted all of these workarounds exist in versions 1.2 and later. + +<a name="trailing-crlf"><H3>Trailing CRLF on POSTs</H3></a> + +<p>This is a legacy issue. The CERN webserver required <code>POST</code> +data to have an extra <code>CRLF</code> following it. Thus many +clients send an extra <code>CRLF</code> that +is not included in the <code>Content-Length</code> of the request. +Apache works around this problem by eating any empty lines which +appear before a request. + +<a name="broken-keepalive"><h3>Broken keepalive</h3></a> + +<p>Various clients have had broken implementations of <i>keepalive</i> +(persistent connections). In particular the Windows versions of +Navigator 2.0 get very confused when the server times out an +idle connection. The workaround is present in the default config files: +<blockquote><code> +BrowserMatch Mozilla/2 nokeepalive +</code></blockquote> +Note that this matches some earlier versions of MSIE, which began the +practice of calling themselves <i>Mozilla</i> in their user-agent +strings just like Navigator. + +<p>MSIE 4.0b2, which claims to support HTTP/1.1, does not properly +support keepalive when it is used on 301 or 302 (redirect) +responses. Unfortunately Apache's <code>nokeepalive</code> code +prior to 1.2.2 would not work with HTTP/1.1 clients. You must apply +<a href="http://www.apache.org/dist/patches/apply_to_1.2.1/msie_4_0b2_fixes.patch">this +patch</a> to version 1.2.1. Then add this to your config: +<blockquote><code> +BrowserMatch "MSIE 4\.0b2;" nokeepalive +</code></blockquote> + +<a name="force-response-1.0"><h3>Incorrect interpretation of <code>HTTP/1.1</code> in response</h3></a> + +<p>To quote from section 3.1 of RFC1945: +<blockquote> +HTTP uses a "<major>.<minor>" numbering scheme to indicate versions +of the protocol. The protocol versioning policy is intended to allow +the sender to indicate the format of a message and its capacity for +understanding further HTTP communication, rather than the features +obtained via that communication. +</blockquote> +Since Apache is an HTTP/1.1 server, it indicates so as part of its +response. Many client authors mistakenly treat this part of the response +as an indication of the protocol that the response is in, and then refuse +to accept the response. + +<p>The first major indication of this problem was with AOL's proxy servers. +When Apache 1.2 went into beta it was the first wide-spread HTTP/1.1 +server. After some discussion, AOL fixed their proxies. In +anticipation of similar problems, the <code>force-response-1.0</code> +environment variable was added to Apache. When present Apache will +indicate "HTTP/1.0" in response to an HTTP/1.0 client, +but will not in any other way change the response. + +<p>The pre-1.1 Java Development Kit (JDK) that is used in many clients +(including Navigator 3.x and MSIE 3.x) exhibits this problem. As do some +of the early pre-releases of the 1.1 JDK. We think it is fixed in the +1.1 JDK release. In any event the workaround: +<blockquote><code> +BrowserMatch Java1.0 force-response-1.0 <br> +BrowserMatch JDK/1.0 force-response-1.0 +</code></blockquote> + +<p>RealPlayer 4.0 from Progressive Networks also exhibits this problem. +However they have fixed it in version 4.01 of the player, but version +4.01 uses the same <code>User-Agent</code> as version 4.0. The +workaround is still: +<blockquote><code> +BrowserMatch "RealPlayer 4.0" force-response-1.0 +</code></blockquote> + +<a name="msie4.0b2"><h3>Requests use HTTP/1.1 but responses must be in HTTP/1.0</h3></a> + +<p>MSIE 4.0b2 has this problem. Its Java VM makes requests in HTTP/1.1 +format but the responses must be in HTTP/1.0 format (in particular, it +does not understand <i>chunked</i> responses). The workaround +is to fool Apache into believing the request came in HTTP/1.0 format. +<blockquote><code> +BrowserMatch "MSIE 4\.0b2;" downgrade-1.0 force-response-1.0 +</code></blockquote> +This workaround is available in 1.2.2, and in a +<a href="http://www.apache.org/dist/patches/apply_to_1.2.1/msie_4_0b2_fixes.patch">patch +</a> against 1.2.1. + +<a name="257th-byte"><h3>Boundary problems with header parsing</h3></a> + +<p>All versions of Navigator from 2.0 through 4.0b2 (and possibly later) +have a problem if the trailing CRLF of the response header starts at +the 256th or 257th byte of the response. A BrowserMatch for this would +match on nearly every hit, so the workaround is enabled automatically +on all responses. The workaround is to detect when this condition would +occur in a response and add extra padding to the header to push the +trailing CRLF past the 257th byte of the response. + +<a name="boundary-string"><h3>Multipart responses and Quoted Boundary Strings</h3></a> + +<p>On multipart responses some clients will not accept quotes (") +around the boundary string. The MIME standard recommends that +such quotes be used. But the clients were probably written based +on one of the examples in RFC2068, which does not include quotes. +Apache does not include quotes on its boundary strings to workaround +this problem. + +<a name="byterange-requests"><h3>Byterange requests</h3></a> + +<p>A byterange request is used when the client wishes to retrieve a +portion of an object, not necessarily the entire object. There +was a very old draft which included these byteranges in the URL. +Old clients such as Navigator 2.0b1 and MSIE 3.0 for the MAC +exhibit this behaviour, and +it will appear in the servers' access logs as (failed) attempts to +retrieve a URL with a trailing ";xxx-yyy". Apache does not attempt +to implement this at all. + +<p>A subsequent draft of this standard defines a header +<code>Request-Range</code>, and a response type +<code>multipart/x-byteranges</code>. The HTTP/1.1 standard includes +this draft with a few fixes, and it defines the header +<code>Range</code> and type <code>multipart/byteranges</code>. + +<p>Navigator (versions 2 and 3) sends both <code>Range</code> and +<code>Request-Range</code> headers (with the same value), but does not +accept a <code>multipart/byteranges</code> response. The response must +be <code>multipart/x-byteranges</code>. As a workaround, if Apache +receives a <code>Request-Range</code> header it considers it "higher +priority" than a <code>Range</code> header and in response uses +<code>multipart/x-byteranges</code>. + +<p>The Adobe Acrobat Reader plugin makes extensive use of byteranges and +prior to version 3.01 supports only the <code>multipart/x-byterange</code> +response. Unfortunately there is no clue that it is the plugin +making the request. If the plugin is used with Navigator, the above +workaround works fine. But if the plugin is used with MSIE 3 (on +Windows) the workaround won't work because MSIE 3 doesn't give the +<code>Range-Request</code> clue that Navigator does. To workaround this, +Apache special cases "MSIE 3" in the <code>User-Agent</code> and serves +<code>multipart/x-byteranges</code>. Note that the necessity for this +with MSIE 3 is actually due to the Acrobat plugin, not due to the browser. + +<p>Netscape Communicator appears to not issue the non-standard +<code>Request-Range</code> header. When an Acrobat plugin prior to +version 3.01 is used with it, it will not properly understand byteranges. +The user must upgrade their Acrobat reader to 3.01. + +<a name="cookie-merge"><h3><code>Set-Cookie</code> header is unmergeable</h3></a> + +<p>The HTTP specifications say that it is legal to merge headers with +duplicate names into one (separated by semicolon). Some browsers +that support Cookies don't like merged headers and prefer that each +<code>Set-Cookie</code> header is sent separately. When parsing the +headers returned by a CGI, Apache will explicitly avoid merging any +<code>Set-Cookie</code> headers. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/misc/nopgp.html b/usr.sbin/httpd/htdocs/manual/misc/nopgp.html new file mode 100644 index 00000000000..9a3497b442a --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/nopgp.html @@ -0,0 +1,101 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Why We Took PEM Out of Apache</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Why We Took PEM Out of Apache</H1> + +On May 17th, 1995, we were asked by a representative of NCSA to remove +any copies of NCSA httpd prior to 1.4.1 from our web site. They +were mandated by the NSA to inform us that redistribution of pre-1.4.1 +code violated the same laws that make distributing Phill Zimmerman's +PGP package to other countries illegal. There was <B>no</B> +encryption in NCSA's httpd, only hooks to publicly available libraries +of PEM code. By the NSA's rules, even hooks to this type of +application is illegal. + +<P> + +Because Apache is based on NCSA code, and we had basically not touched +that part of the software, we were informed that Apache was also +illegal to distribute to foreign countries, and advised (not mandated) +by NCSA to remove it. So, we removed both the copies of the NCSA +httpd we had, and all versions of Apache previous to 0.6.5. + +<P> + +The Apache members are strong advocates of the right to digital +privacy, so the decision to submit to the NSA and remove the code was +not an easy one. Here are some elements in our rationale: + +<UL> + +<LI>The PEM code in httpd was not widely used. No major site relied +upon its use, so its loss is not a blow to encryption and security on +the world wide web. There are other efforts designed to give much +more flexible security - SSL and SHTTP - so this wasn't a function +whose absence would really be missed on a functional level. + +<LI>We didn't feel like being just a couple more martyrs in a fight +being fought very well by many other people. Rather than have the +machine that supports the project confiscated or relocated to South +Africa, etc., we think there are more efficient methods to address the +issue. + +</UL> + +It kind of sickens us that we had to do it, but so be it. + +<P> + +Patches that re-implement the PEM code may be available at a foreign +site soon. If it does show up, we'll point to it - that can't be illegal! + +<P> + +Finally, here is a compendium of pointers to sites related to +encryption and export law. We can't promise this list will be up to +date, so send us mail when you see a problem or want a link added. +Thanks. + +<UL> +<LI><A HREF="http://www.yahoo.com/Computers_and_Internet/Security_and_Encryption/"> +Yahoo - Science: Mathematics: Security and Encryption</A> +<LI><A HREF="http://www.eff.org/pub/EFF/Policy/Crypto/"> +EFF Crypto/Privacy/Security Archive</A> +<LI><A HREF="http://www.quadralay.com/www/Crypt/Crypt.html"> +Crypto page at Quadralay</A> +<LI><A HREF="ftp://ftp.cygnus.com/pub/export/export.html"> +Cryptography Export Control Archives (Cygnus)</A> +<LI><A HREF="http://www.law.indiana.edu/law/iclu.html"> +ICLU - Your Rights in Cyberspace</A> +</UL> + + <a href="http://www.behlendorf.com/~brian/">Brian</a>, <a href="mailto:brian@hyperreal.com">brian@hyperreal.com</a> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/perf-bsd44.html b/usr.sbin/httpd/htdocs/manual/misc/perf-bsd44.html new file mode 100644 index 00000000000..a65afb00304 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/perf-bsd44.html @@ -0,0 +1,249 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Running a High-Performance Web Server for BSD</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<A NAME="initial"> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +</A> +<H1 ALIGN="CENTER">Running a High-Performance Web Server for BSD</H1> + +Like other OS's, the listen queue is often the <b>first limit hit</b>. The +following are comments from "Aaron Gifford <agifford@InfoWest.COM>" +on how to fix this on BSDI 1.x, 2.x, and FreeBSD 2.0 (and earlier): + +<p> + +Edit the following two files: +<blockquote><code> /usr/include/sys/socket.h <br> + /usr/src/sys/sys/socket.h </code></blockquote> +In each file, look for the following: +<pre> + /* + * Maximum queue length specifiable by listen. + */ + #define SOMAXCONN 5 +</pre> + +Just change the "5" to whatever appears to work. I bumped the two +machines I was having problems with up to 32 and haven't noticed the +problem since. + +<p> + +After the edit, recompile the kernel and recompile the Apache server +then reboot. + +<P> + +FreeBSD 2.1 seems to be perfectly happy, with SOMAXCONN +set to 32 already. + +<p> + +<A NAME="detail"> +<b>Addendum for <i>very</i> heavily loaded BSD servers</b><br> +</A> +from Chuck Murcko <chuck@telebase.com> + +<p> + +If you're running a really busy BSD Apache server, the following are useful +things to do if the system is acting sluggish:<p> + +<ul> + +<li> Run vmstat to check memory usage, page/swap rates, etc. + +<li> Run netstat -m to check mbuf usage + +<li> Run fstat to check file descriptor usage + +</ul> + +These utilities give you an idea what you'll need to tune in your kernel, +and whether it'll help to buy more RAM. + +Here are some BSD kernel config parameters (actually BSDI, but pertinent to +FreeBSD and other 4.4-lite derivatives) from a system getting heavy usage. +The tools mentioned above were used, and the system memory was increased to +48 MB before these tuneups. Other system parameters remained unchanged. + +<p> + +<pre> +maxusers 256 +</pre> + +Maxusers drives a <i>lot</i> of other kernel parameters: + +<ul> + +<li> Maximum # of processes + +<li> Maximum # of processes per user + +<li> System wide open files limit + +<li> Per-process open files limit + +<li> Maximum # of mbuf clusters + +<li> Proc/pgrp hash table size + +</ul> + +The actual formulae for these derived parameters are in +<i>/usr/src/sys/conf/param.c</i>. +These calculated parameters can also be overridden (in part) by specifying +your own values in the kernel configuration file: + +<pre> +# Network options. NMBCLUSTERS defines the number of mbuf clusters and +# defaults to 256. This machine is a server that handles lots of traffic, +# so we crank that value. +options SOMAXCONN=256 # max pending connects +options NMBCLUSTERS=4096 # mbuf clusters at 4096 + +# +# Misc. options +# +options CHILD_MAX=512 # maximum number of child processes +options OPEN_MAX=512 # maximum fds (breaks RPC svcs) +</pre> + +SOMAXCONN is not derived from maxusers, so you'll always need to increase +that yourself. We used a value guaranteed to be larger than Apache's +default for the listen() of 128, currently. + +<p> + +In many cases, NMBCLUSTERS must be set much larger than would appear +necessary at first glance. The reason for this is that if the browser +disconnects in mid-transfer, the socket fd associated with that particular +connection ends up in the TIME_WAIT state for several minutes, during +which time its mbufs are not yet freed. Another reason is that, on server +timeouts, some connections end up in FIN_WAIT_2 state forever, because +this state doesn't time out on the server, and the browser never sent +a final FIN. For more details see the +<A HREF="fin_wait_2.html">FIN_WAIT_2</A> page. + +<p> + +Some more info on mbuf clusters (from sys/mbuf.h): +<pre> +/* + * Mbufs are of a single size, MSIZE (machine/machparam.h), which + * includes overhead. An mbuf may add a single "mbuf cluster" of size + * MCLBYTES (also in machine/machparam.h), which has no additional overhead + * and is used instead of the internal data area; this is done when + * at least MINCLSIZE of data must be stored. + */ +</pre> + +<p> + +CHILD_MAX and OPEN_MAX are set to allow up to 512 child processes (different +than the maximum value for processes per user ID) and file descriptors. +These values may change for your particular configuration (a higher OPEN_MAX +value if you've got modules or CGI scripts opening lots of connections or +files). If you've got a lot of other activity besides httpd on the same +machine, you'll have to set NPROC higher still. In this example, the NPROC +value derived from maxusers proved sufficient for our load. + +<p> + +<b>Caveats</b> + +<p> + +Be aware that your system may not boot with a kernel that is configured +to use more resources than you have available system RAM. <b>ALWAYS</b> +have a known bootable kernel available when tuning your system this way, +and use the system tools beforehand to learn if you need to buy more +memory before tuning. + +<p> + +RPC services will fail when the value of OPEN_MAX is larger than 256. +This is a function of the original implementations of the RPC library, +which used a byte value for holding file descriptors. BSDI has partially +addressed this limit in its 2.1 release, but a real fix may well await +the redesign of RPC itself. + +<p> + +Finally, there's the hard limit of child processes configured in Apache. + +<p> + +For versions of Apache later than 1.0.5 you'll need to change the +definition for <b>HARD_SERVER_LIMIT</b> in <i>httpd.h</i> and recompile +if you need to run more than the default 150 instances of httpd. + +<p> + +From conf/httpd.conf-dist: + +<pre> +# Limit on total number of servers running, i.e., limit on the number +# of clients who can simultaneously connect --- if this limit is ever +# reached, clients will be LOCKED OUT, so it should NOT BE SET TOO LOW. +# It is intended mainly as a brake to keep a runaway server from taking +# Unix with it as it spirals down... + +MaxClients 150 +</pre> + +Know what you're doing if you bump this value up, and make sure you've +done your system monitoring, RAM expansion, and kernel tuning beforehand. +Then you're ready to service some serious hits! + +<p> + +Thanks to <i>Tony Sanders</i> and <i>Chris Torek</i> at BSDI for their +helpful suggestions and information. + +<P> + +"M. Teterin" <mi@ALDAN.ziplink.net> writes:<P> +<blockquote>It really does help if your kernel and frequently used utilities +are fully optimized. Rebuilding the FreeBSD kernel on an AMD-133 +(486-class CPU) web-server with<BR> +<code> -m486 -fexpensive-optimizations -fomit-frame-pointer -O2</code><BR> +helped reduce the number of "unable" errors, because the CPU was +often maxed out.</blockquote> +<P> + +<HR> + +<H3>More welcome!</H3> + +If you have tips to contribute, send mail to <a +href="mailto:brian@organic.com">brian@organic.com</a> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</body></html> + diff --git a/usr.sbin/httpd/htdocs/manual/misc/perf-dec.html b/usr.sbin/httpd/htdocs/manual/misc/perf-dec.html new file mode 100644 index 00000000000..eb0551a1ecd --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/perf-dec.html @@ -0,0 +1,292 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Performance Tuning Tips for Digital Unix</TITLE> +</HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Performance Tuning Tips for Digital Unix</H1> + +Below is a set of newsgroup posts made by an engineer from DEC in +response to queries about how to modify DEC's Digital Unix OS for more +heavily loaded web sites. Copied with permission. + +<HR> + +<H2>Update</H2> +From: Jeffrey Mogul <mogul@pa.dec.com><BR> +Date: Fri, 28 Jun 96 16:07:56 MDT<BR> + +<OL> +<LI> The advice given in the README file regarding the + "tcbhashsize" variable is incorrect. The largest value + this should be set to is 1024. Setting it any higher + will have the perverse result of disabling the hashing + mechanism. + +<LI>Patch ID OSF350-146 has been superseded by +<blockquote> + Patch ID OSF350-195 for V3.2C<BR> + Patch ID OSF360-350195 for V3.2D +</blockquote> + Patch IDs for V3.2E and V3.2F should be available soon. + There is no known reason why the Patch ID OSF360-350195 + won't work on these releases, but such use is not officially + supported by Digital. This patch kit will not be needed for + V3.2G when it is released. +</OL> +<HR> + + +<PRE> +From mogul@pa.dec.com (Jeffrey Mogul) +Organization DEC Western Research +Date 30 May 1996 00:50:25 GMT +Newsgroups <A HREF="news:comp.unix.osf.osf1">comp.unix.osf.osf1</A> +Message-ID <A HREF="news:4oirch$bc8@usenet.pa.dec.com"><4oirch$bc8@usenet.pa.dec.com></A> +Subject Re: Web Site Performance +References 1 + + + +In article <skoogDs54BH.9pF@netcom.com> skoog@netcom.com (Jim Skoog) writes: +>Where are the performance bottlenecks for Alpha AXP running the +>Netscape Commerce Server 1.12 with high volume internet traffic? +>We are evaluating network performance for a variety of Alpha AXP +>runing DEC UNIX 3.2C, which run DEC's seal firewall and behind +>that Alpha 1000 and 2100 webservers. + +Our experience (running such Web servers as <A HREF="http://altavista.digital.com">altavista.digital.com</A> +and <A HREF="http://www.digital.com">www.digital.com</A>) is that there is one important kernel tuning +knob to adjust in order to get good performance on V3.2C. You +need to patch the kernel global variable "somaxconn" (use dbx -k +to do this) from its default value of 8 to something much larger. + +How much larger? Well, no larger than 32767 (decimal). And +probably no less than about 2048, if you have a really high volume +(millions of hits per day), like AltaVista does. + +This change allows the system to maintain more than 8 TCP +connections in the SYN_RCVD state for the HTTP server. (You +can use "netstat -An |grep SYN_RCVD" to see how many such +connections exist at any given instant). + +If you don't make this change, you might find that as the load gets +high, some connection attempts take a very long time. And if a lot +of your clients disconnect from the Internet during the process of +TCP connection establishment (this happens a lot with dialup +users), these "embryonic" connections might tie up your somaxconn +quota of SYN_RCVD-state connections. Until the kernel times out +these embryonic connections, no other connections will be accepted, +and it will appear as if the server has died. + +The default value for somaxconn in Digital UNIX V4.0 will be quite +a bit larger than it has been in previous versions (we inherited +this default from 4.3BSD). + +Digital UNIX V4.0 includes some other performance-related changes +that significantly improve its maximum HTTP connection rate. However, +we've been using V3.2C systems to front-end for altavista.digital.com +with no obvious performance bottlenecks at the millions-of-hits-per-day +level. + +We have some Webstone performance results available at + <A HREF="http://www.digital.com/info/alphaserver/news/webff.html">http://www.digital.com/info/alphaserver/news/webff.html</A> +I'm not sure if these were done using V4.0 or an earlier version +of Digital UNIX, although I suspect they were done using a test +version of V4.0. + +-Jeff + +<HR> + +---------------------------------------------------------------------------- + +From mogul@pa.dec.com (Jeffrey Mogul) +Organization DEC Western Research +Date 31 May 1996 21:01:01 GMT +Newsgroups <A HREF="news:comp.unix.osf.osf1">comp.unix.osf.osf1</A> +Message-ID <A HREF="news:4onmmd$mmd@usenet.pa.dec.com"><4onmmd$mmd@usenet.pa.dec.com></A> +Subject Digital UNIX V3.2C Internet tuning patch info + +---------------------------------------------------------------------------- + +Something that probably few people are aware of is that Digital +has a patch kit available for Digital UNIX V3.2C that may improve +Internet performance, especially for busy web servers. + +This patch kit is one way to increase the value of somaxconn, +which I discussed in a message here a day or two ago. + +I've included in this message the revised README file for this +patch kit below. Note that the original README file in the patch +kit itself may be an earlier version; I'm told that the version +below is the right one. + +Sorry, this patch kit is NOT available for other versions of Digital +UNIX. Most (but not quite all) of these changes also made it into V4.0, +so the description of the various tuning parameters in this README +file might be useful to people running V4.0 systems. + +This patch kit does not appear to be available (yet?) from + <A HREF="http://www.service.digital.com/html/patch_service.html">http://www.service.digital.com/html/patch_service.html</A> +so I guess you'll have to call Digital's Customer Support to get it. + +-Jeff + +DESCRIPTION: Digital UNIX Network tuning patch + + Patch ID: OSF350-146 + + SUPERSEDED PATCHES: OSF350-151, OSF350-158 + + This set of files improves the performance of the network + subsystem on a system being used as a web server. There are + additional tunable parameters included here, to be used + cautiously by an informed system administrator. + +TUNING + + To tune the web server, the number of simultaneous socket + connection requests are limited by: + + somaxconn Sets the maximum number of pending requests + allowed to wait on a listening socket. The + default value in Digital UNIX V3.2 is 8. + This patch kit increases the default to 1024, + which matches the value in Digital UNIX V4.0. + + sominconn Sets the minimum number of pending connections + allowed on a listening socket. When a user + process calls listen with a backlog less + than sominconn, the backlog will be set to + sominconn. sominconn overrides somaxconn. + The default value is 1. + + The effectiveness of tuning these parameters can be monitored by + the sobacklog variables available in the kernel: + + sobacklog_hiwat Tracks the maximum pending requests to any + socket. The initial value is 0. + + sobacklog_drops Tracks the number of drops exceeding the + socket set backlog limit. The initial + value is 0. + + somaxconn_drops Tracks the number of drops exceeding the + somaxconn limit. When sominconn is larger + than somaxconn, tracks the number of drops + exceeding sominconn. The initial value is 0. + + TCP timer parameters also affect performance. Tuning the following + require some knowledge of the characteristics of the network. + + tcp_msl Sets the tcp maximum segment lifetime. + This is the maximum lifetime in half + seconds that a packet can be in transit + on the network. This value, when doubled, + is the length of time a connection remains + in the TIME_WAIT state after a incoming + close request is processed. The unit is + specified in 1/2 seconds, the initial + value is 60. + + tcp_rexmit_interval_min + Sets the minimum TCP retransmit interval. + For some WAN networks the default value may + be too short, causing unnecessary duplicate + packets to be sent. The unit is specified + in 1/2 seconds, the initial value is 1. + + tcp_keepinit This is the amount of time a partially + established connection will sit on the listen + queue before timing out (e.g. if a client + sends a SYN but never answers our SYN/ACK). + Partially established connections tie up slots + on the listen queue. If the queue starts to + fill with connections in SYN_RCVD state, + tcp_keepinit can be decreased to make those + partial connects time out sooner. This should + be used with caution, since there might be + legitimate clients that are taking a while + to respond to SYN/ACK. The unit is specified + in 1/2 seconds, the default value is 150 + (ie. 75 seconds). + + The hashlist size for the TCP inpcb lookup table is regulated by: + + tcbhashsize The number of hash buckets used for the + TCP connection table used in the kernel. + The initial value is 32. For best results, + should be specified as a power of 2. For + busy Web servers, set this to 2048 or more. + + The hashlist size for the interface alias table is regulated by: + + inifaddr_hsize The number of hash buckets used for the + interface alias table used in the kernel. + The initial value is 32. For best results, + should be specified as a power of 2. + + ipport_userreserved The maximum number of concurrent non-reserved, + dynamically allocated ports. Default range + is 1025-5000. The maximum value is 65535. + This limits the numer of times you can + simultaneously telnet or ftp out to connect + to other systems. + + tcpnodelack Don't delay acknowledging TCP data; this + can sometimes improve performance of locally + run CAD packages. Default is value is 0, + the enabled value is 1. + + Digital UNIX version: + + V3.2C +Feature V3.2C patch V4.0 +======= ===== ===== ==== +somaxconn X X X +sominconn - X X +sobacklog_hiwat - X - +sobacklog_drops - X - +somaxconn_drops - X - +tcpnodelack X X X +tcp_keepidle X X X +tcp_keepintvl X X X +tcp_keepcnt - X X +tcp_keepinit - X X +TCP keepalive per-socket - - X +tcp_msl - X - +tcp_rexmit_interval_min - X - +TCP inpcb hashing - X X +tcbhashsize - X X +interface alias hashing - X X +inifaddr_hsize - X X +ipport_userreserved - X - +sysconfig -q inet - - X +sysconfig -q socket - - X + +</PRE> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/perf.html b/usr.sbin/httpd/htdocs/manual/misc/perf.html new file mode 100644 index 00000000000..bb7221419a7 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/perf.html @@ -0,0 +1,145 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Hints on Running a High-Performance Web Server</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Hints on Running a High-Performance Web Server</H1> + +Running Apache on a heavily loaded web server, one often encounters +problems related to the machine and OS configuration. "Heavy" is +relative, of course - but if you are seeing more than a couple hits +per second on a sustained basis you should consult the pointers on +this page. In general the suggestions involve how to tune your kernel +for the heavier TCP load, hardware/software conflicts that arise, etc. + +<UL> +<LI><A HREF="#AUX">A/UX (Apple's UNIX)</A> +<LI><A HREF="#BSD">BSD-based (BSDI, FreeBSD, etc)</A> +<LI><A HREF="#DEC">Digital UNIX</A> +<LI><A HREF="#Linux">Linux</A> +<LI><A HREF="#SGI">SGI</A> +<LI><A HREF="#Solaris">Solaris</A> +<LI><A HREF="#SunOS">SunOS 4.x</A> +</UL> + +<HR> + +<H3><A NAME="AUX"> +A/UX (Apple's UNIX) +</A></H3> + +If you are running Apache on A/UX, a page that gives some helpful +performance hints (concerning the <I>listen()</I> queue and using +virtual hosts) +<A HREF="http://www.jaguNET.com/apache.html">can be found here</A> + +<P><HR> + +<H3><A NAME="BSD"> +BSD-based (BSDI, FreeBSD, etc) +</A></H3> + +<A HREF="perf-bsd44.html#initial">Quick</A> and +<A HREF="perf-bsd44.html#detail">detailed</A> +performance tuning hints for BSD-derived systems. + +<P><HR> + +<H3><A NAME="DEC"> +Digital UNIX +</A></H3> + +<UL> + <LI><A HREF="http://www.digital.com/info/internet/document/ias/tuning.html">DIGITAL + UNIX Tuning Parameters for Web Servers</A> + <LI>We have some <A HREF="perf-dec.html">newsgroup postings</A> on how to tune + Digital UNIX 3.2 and 4.0. +</UL> + +<P><HR> + +<H3><A NAME="Linux"> +Linux +</A></H3> + +The most common problem on Linux shows up on heavily-loaded systems +where the whole server will appear to freeze for a couple of minutes +at a time, and then come back to life. This has been traced to a +listen() queue overload - certain Linux implementations have a low +value set for the incoming connection queue which can cause problems. +Please see our <a +href="http://www.qosina.com/~awm/apache/linux-tcp.html">Using Apache on +Linux</a> page for more info on how to fix this. + +<P><HR> + +<H3><A NAME="SGI"> +SGI +</A></H3> + +<UL> +<LI><A HREF="http://www.sgi.com/Products/WebFORCE/Resources/res_TuningGuide.html"> +WebFORCE Web Server Tuning Guidelines for IRIX 5.3, +<http://www.sgi.com/Products/WebFORCE/Resources/res_TuningGuide.html></A> +</UL> + +<P><HR> + +<H3><A NAME="Solaris"> +Solaris 2.4 +</A></H3> + +The Solaris 2.4 TCP implementation has a few inherent limitations that +only became apparent under heavy loads. This has been fixed to some +extent in 2.5 (and completely revamped in 2.6), but for now consult +the following URL for tips on how to expand the capabilities if you +are finding slowdowns and lags are hurting performance. + +<UL> + +<LI><A href="http://www.sun.com/sun-on-net/performance.html">Sun Performance Information</a> +</UL> + +<P><HR> + +<H3><A NAME="SunOS"> +SunOS 4.x +</A></H3> + +More information on tuning SOMAXCONN on SunOS can be found at +<A HREF="http://www.islandnet.com/~mark/somaxconn.html"> +http://www.islandnet.com/~mark/somaxconn.html</A>. + +<P><HR> + +<H3>More welcome!</H3> + +If you have tips to contribute, send mail to <a +href="mailto:brian@organic.com">brian@organic.com</a> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</body></html> + diff --git a/usr.sbin/httpd/htdocs/manual/misc/security_tips.html b/usr.sbin/httpd/htdocs/manual/misc/security_tips.html new file mode 100644 index 00000000000..20942181fa0 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/security_tips.html @@ -0,0 +1,198 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache HTTP Server: Security Tips</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Security Tips for Server Configuration</H1> + +<hr> + +<P>Some hints and tips on security issues in setting up a web server. Some of +the suggestions will be general, others specific to Apache. + +<HR> + +<H2>Permissions on Log File Directories</H2> +<P>When Apache starts, it opens the log files as the user who started the +server before switching to the user defined in the +<a href="../mod/core.html#user"><b>User</b></a> directive. Anyone who +has write permission for the directory where any log files are +being written to can append pseudo-arbitrary data to any file on the +system which is writable by the user who starts Apache. Since the +server is normally started by root, you should <EM>NOT</EM> give anyone +write permission to the directory where logs are stored unless you +want them to have root access. +<P> +<HR> +<H2>Server Side Includes</H2> +<P>Server side includes (SSI) can be configured so that users can execute +arbitrary programs on the server. That thought alone should send a shiver +down the spine of any sys-admin.<p> + +One solution is to disable that part of SSI. To do that you use the +IncludesNOEXEC option to the <A HREF="../mod/core.html#options">Options</A> +directive.<p> + +<HR> + +<H2>Non Script Aliased CGI</H2> +<P>Allowing users to execute <B>CGI</B> scripts in any directory should only +be considered if; +<OL> + <LI>You trust your users not to write scripts which will deliberately or +accidentally expose your system to an attack. + <LI>You consider security at your site to be so feeble in other areas, as to +make one more potential hole irrelevant. + <LI>You have no users, and nobody ever visits your server. +</OL><p> +<HR> + +<H2>Script Alias'ed CGI</H2> +<P>Limiting <B>CGI</B> to special directories gives the admin control over +what goes into those directories. This is inevitably more secure than +non script aliased CGI, but <strong>only if users with write access to the +directories are trusted</strong> or the admin is willing to test each new CGI +script/program for potential security holes.<P> + +Most sites choose this option over the non script aliased CGI approach.<p> + +<HR> +<H2>CGI in general</H2> +<P>Always remember that you must trust the writers of the CGI script/programs +or your ability to spot potential security holes in CGI, whether they were +deliberate or accidental.<p> + +All the CGI scripts will run as the same user, so they have potential to +conflict (accidentally or deliberately) with other scripts e.g. +User A hates User B, so he writes a script to trash User B's CGI +database. One program which can be used to allow scripts to run +as different users is <A HREF="../suexec.html">suEXEC</A> which is +included with Apache as of 1.2 and is called from special hooks in +the Apache server code. Another popular way of doing this is with +<A HREF="http://wwwcgi.umr.edu/~cgiwrap/">CGIWrap</A>. <P> + +<HR> + + +<H2>Stopping users overriding system wide settings...</H2> +<P>To run a really tight ship, you'll want to stop users from setting +up <CODE>.htaccess</CODE> files which can override security features +you've configured. Here's one way to do it...<p> + +In the server configuration file, put +<blockquote><code> +<Directory /> <br> +AllowOverride None <br> +Options None <br> +allow from all <br> +</Directory> <br> +</code></blockquote> + +Then setup for specific directories<P> + +This stops all overrides, Includes and accesses in all directories apart +from those named.<p> +<HR> +<H2> + Protect server files by default +</H2> +<P> +One aspect of Apache which is occasionally misunderstood is the feature +of default access. That is, unless you take steps to change it, if the +server can find its way to a file through normal URL mapping rules, it +can serve it to clients. +</P> +<P> +For instance, consider the following example: +</P> +<OL> + <LI><SAMP># cd /; ln -s / public_html</SAMP> + </LI> + <LI>Accessing <SAMP>http://localhost/~root/</SAMP> + </LI> +</OL> +<P> +This would allow clients to walk through the entire filesystem. To work +around this, add the following block to your server's configuration: +</P> +<PRE> + <Directory /> + Order deny,allow + Deny from all + </Directory> +</PRE> +<P> +This will forbid default access to filesystem locations. Add +appropriate +<A + HREF="../mod/core.html#directory" +><SAMP><Directory></SAMP></A> +blocks to allow access only +in those areas you wish. For example, +</P> +<PRE> + <Directory /usr/users/*/public_html> + Order deny,allow + Allow from all + </Directory> + <Directory /usr/local/httpd> + Order deny,allow + Allow from all + </Directory> +</PRE> +<P> +Pay particular attention to the interactions of +<A + HREF="../mod/core.html#location" +><SAMP><Location></SAMP></A> +and +<A + HREF="../mod/core.html#directory" +><SAMP><Directory></SAMP></A> +directives; for instance, even if <SAMP><Directory /></SAMP> +denies access, a <SAMP><Location /></SAMP> directive might +overturn it. +</P> +<P> +Also be wary of playing games with the +<A + HREF="../mod/mod_userdir.html#userdir" +>UserDir</A> +directive; setting it to something like <SAMP>"./"</SAMP> +would have the same effect, for root, as the first example above. + +<HR> +<P>Please send any other useful security tips to The Apache Group +by filling out a +<A HREF="http://www.apache.org/bugdb.cgi">problem report</A>, or by +sending mail to +<A HREF="mailto:apache-bugs@mail.apache.org">apache-bugs@mail.apache.org</A> +<p> +<HR> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/vif-info.html b/usr.sbin/httpd/htdocs/manual/misc/vif-info.html new file mode 100644 index 00000000000..f81190e2c35 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/vif-info.html @@ -0,0 +1,412 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML><HEAD> +<TITLE>Configuring Multiple IP Addresses</TITLE> +</HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Configuring Multiple IP Addresses</H1> + +<PRE> +This material is originally from John Ioannidis (ji@polaris.ctr.columbia.edu) +I have condensed it some and applied some corrections for SunOS 4.1.x +courtesy of Chuck Smoko (csmoko@relay.nswc.navy.mil). + +Bob Baggerman (bob@bizweb.com) +12 Jan 94 + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +John Ionnidis writes: + +This is a topic that comes up once in a while on comp.protocols.tcp-ip +and other newsgroups. The question is, how to get a machine with one +network interface to respond to more than one IP addresses. + +I have a solution than might suit you. For my doctoral work (there's +a paper about it in this year's ('91) SIGCOMM, also available for +anonymous FTP from cs.columbia.edu:/pub/ji/sigcomm*.ps.Z), I've +developed what I call the "Virtual Interface" (VIF). To the networking +code, it looks like an interface. It gets ifattach()ed when you open +the /dev/vif* device, and then you can ifconfig it as you like. It +does not have an if_input procedure; it only has an if_output. Packets +that it receives (from higher-level protocols) which have its +IP address, it simply loops back (like any well-behaved if driver). +Packets that it receives that are destined for some other address, it +encapsulates in an encapsulation protocol I call IPIP (IP-within-IP, +protocol number IPPROTO_IPIP == 94), and sends it to another machine +that groks that encapsulation protocol. This feature you won't need, +but here's how to have multiple IP addresses on a machine with a +single real interface: + +Let's say your primary interface's IP address is 198.3.2.1, and you +also want it to respond to addresses 198.4.3.2 and 198.5.4.3 (note +that these are three distinct class C addresses in three distinct +class C nets). Here are the ifconfigs: + + ifconfig le0 198.3.2.1 up -trailers # config primary interface + + ifconfig vif0 198.4.3.2 up # config first virtual interface + route delete net 198.4.3 198.4.3.2 # delete spurious route + route add host 198.4.3.2 198.4.3.2 0 # add route for this i/f + + ifconfig vif1 198.5.4.3 up # config second virtual interface + route delete net 198.5.4 198.5.4.3 # delete spurious route + route add host 198.5.4.3 198.5.4.3 0 # add route for this i/f + +The route deletes are needed because the ifconfig creates a default +route to the interface's network, which can cause problems; all that's +needed is the (host) route to the interface's address. + +Now, get le0's ethernet address (say, 8:0:20:3:2:1), and add the +following static ARP entries: + + arp -s 198.4.3.2 8:0:20:3:2:1 pub + arp -s 198.5.4.3 8:0:20:3:2:1 pub + +This will cause any ARP requests for the VIF addresses to be replied +with your machine's ethernet address. + +Now, make sure your default route is to your segment's gateway, +through the real interface. Finally, make sure your routers and/or +hosts on the same segment as yours know that 198.4.3.2 and 198.5.4.3 +are on that cable. + +Here's what you've accomplished. + +ARP requests for any of your host's addresses will be replied to with +the host's ethernet address (the real one, because that's what it is, +the virtual ones because of the public static arp entries). Packets +reaching your host with any of these addresses will be accepted by the +ip_input routine because they match the address of one of the host's +interfaces. Packets leaving your host can have any of its addresses +(real and virtual). + +The code for vif follows. To use it, put the stuff in netinet/if_vif.c +and netinet/if_vif.h, configure your kernel with the number of +virtual interfaces you want using a line like: + +pseudo-device vif4 # Virtual IP interface + +in your configuration file, and the line + +netinet/if_vif.c optional vif device-driver + +in the "files" file. Also, add the appropriate entries in conf.c, so +that you can access the if_attach() routine when you open the device: + + +-------------------------- conf.c------------------------------------------ + +add this in the appropriate place in the headers of conf.c: + +-------------------- +#include "vif.h" +#if NVIF > 0 +int vifopen(), vifclose(), vifread(), vifwrite(), vifselect(), vifioctl(); +#else +#define vifopen nodev +#define vifclose nodev +#define vifread nodev +#define vifwrite nodev +#define vifselect nodev +#define vifioctl nodev +#endif +-------------------- + +then, way down in the definition for cdevsw[]: + +-------------------- + vifopen, vifclose, vifread, vifwrite, /*14*/ + vifioctl, nodev, nodev, 0, + 0, nodev, +-------------------- + +Make sure you remember the correct major device number, 14 in this case! + +--------------------------------------------------------------------------- + +Finally, here's the code. It has the tunneling pieces removed (you +need more code to use that anyway), and it comes from a Mach 2.6 +kernel; it should compile on any Berkeley-derived unix with minor +changes (most likely only in the includes). + +---------------------netinet/if_vif.h-------------------------------------- +typedef struct +{ + struct ifnet vif_if; + struct ifnet *vif_sif; /* slave interface */ + int vif_flags; +} vif_softc_t; + +#define VIFMTU (1024+512) +--------------------------------------------------------------------------- + +and + +---------------------netinet/if_vif.c-------------------------------------- +/* + * Virtual IP interface module. + */ + +#include "param.h" +#include "../sys/systm.h" +#include "../sys/mbuf.h" +#include "../sys/socket.h" +#include "../sys/errno.h" +#include "../sys/ioctl.h" + +#include "../net/if.h" +#include "../net/netisr.h" +#include "../net/route.h" + +#ifdef INET +#include "../netinet/in.h" +#include "../netinet/in_systm.h" +#include "../netinet/in_var.h" +#include "../netinet/ip.h" +#endif + +#include "in_pcb.h" +#include "vif.h" + +typedef struct +{ + struct ifnet vif_if; + struct ifnet *vif_sif; /* slave interface */ + int vif_flags; +} vif_softc_t; + +#define VIFMTU (1024+512) + +vif_softc_t vif_softc[NVIF]; + +int vifs_inited = 0; + + +vifattach() +{ + register int i; + register struct ifnet *ifp; + int vifoutput(), vififioctl(); + + for (i=0; i<NVIF; i++) + { + ifp = &vif_softc[i].vif_if; + ifp->if_name = "vif"; + ifp->if_unit = i; + ifp->if_mtu = VIFMTU; + ifp->if_flags = IFF_LOOPBACK | IFF_NOARP; + ifp->if_ioctl = vififioctl; + ifp->if_output = vifoutput; + if_attach(ifp); + } +} + +vifopen(dev, flag) +int dev, flag; +{ + int unit; + + if (!vifs_inited) + { + vifattach(); + vifs_inited = 1; + printf("vif initialized\n"); + } + + unit = minor(dev); + if ((unit < 0) || (unit >= NVIF)) + { + return ENXIO; + } + + return 0; +} + +vifclose(dev, flag) +int dev, flag; +{ + return 0; +} + +vifread() +{ + return ENXIO; +} + +vifwrite() +{ + return ENXIO; +} + +vifselect() +{ + return ENXIO; +} + +vifoutput(ifp, m0, dst) + struct ifnet *ifp; + register struct mbuf *m0; + struct sockaddr *dst; +{ + int s; + register struct ifqueue *ifq; + struct mbuf *m; + struct sockaddr_in *din; + + if (dst->sa_family != AF_INET) + { + printf("%s%d: can't handle af%d\n", + ifp->if_name, ifp->if_unit, + dst->sa_family); + m_freem(m0); + return (EAFNOSUPPORT); + } + + din = (struct sockaddr_in *)dst; + + if (din->sin_addr.s_addr == IA_SIN(ifp->if_addrlist)->sin_addr.s_addr) + { + /* printf("%s%d: looping\n", ifp->if_name, ifp->if_unit); */ + + /* + * Place interface pointer before the data + * for the receiving protocol. + */ + if (m0->m_off <= MMAXOFF && + m0->m_off >= MMINOFF + sizeof(struct ifnet *)) { + m0->m_off -= sizeof(struct ifnet *); + m0->m_len += sizeof(struct ifnet *); + } else { + MGET(m, M_DONTWAIT, MT_HEADER); + if (m == (struct mbuf *)0) + return (ENOBUFS); + m->m_off = MMINOFF; + m->m_len = sizeof(struct ifnet *); + m->m_next = m0; + m0 = m; + } + *(mtod(m0, struct ifnet **)) = ifp; + s = splimp(); + ifp->if_opackets++; + ifq = &ipintrq; + if (IF_QFULL(ifq)) { + IF_DROP(ifq); + m_freem(m0); + splx(s); + return (ENOBUFS); + } + IF_ENQUEUE(ifq, m0); + schednetisr(NETISR_IP); + ifp->if_ipackets++; + splx(s); + return (0); + } + + return EHOSTUNREACH; +} + +/* + * Process an ioctl request. + */ +/* ARGSUSED */ +vififioctl(ifp, cmd, data) + register struct ifnet *ifp; + int cmd; + caddr_t data; +{ + int error = 0; + + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + /* + * Everything else is done at a higher level. + */ + break; + + default: + error = EINVAL; + } + return (error); +} + +vifioctl(dev, cmd, arg, mode) +dev_t dev; +int cmd; +caddr_t arg; +int mode; +{ + int unit; + + unit = minor(dev); + if ((unit < 0) || (unit >= NVIF)) + return ENXIO; + + return EINVAL; +} +---------------------------------------------------------------------------- + +To use it, compile your kernel, and reboot. Then create the vif +device: + +# mknod /dev/vif c 14 0 + +(or whatever major number it ended up being), and echo something into +it: + +# echo > /dev/vif + +This will cause the device to be opened, which will if_attach the +interfaces. If you feel like playing with the code, you may want to +kmem_alloc() the vif_softc structrure at open time, and use the minor +number of the device to tell it how many interfaces to create. + +Now you can go ahead and ifconfig etc. + +I'll be happy to answer minor questions, and hear about success and +failure stories, but I cannot help you if you don't already know how +to hack kernels. + +Good luck! + +/ji + +In-Real-Life: John "Heldenprogrammer" Ioannidis +E-Mail-To: ji@cs.columbia.edu +V-Mail-To: +1 212 854 8120 +P-Mail-To: 450 Computer Science \n Columbia University \n New York, NY 10027 +</PRE> + +<P> + +Note: there is also a <A +HREF="http://www.multihost.com/">commercial-product-turned-freeware +called "Col. Patch"</A> which does this as a loadable kernel module +for SunOS 4.1.3_U1. + +<P> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/misc/windoz_keepalive.html b/usr.sbin/httpd/htdocs/manual/misc/windoz_keepalive.html new file mode 100644 index 00000000000..a321aaf6098 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/misc/windoz_keepalive.html @@ -0,0 +1,63 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>MS Windows Netscape 3.0b4 KeepAlive problem solved</TITLE> +</HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">HTTP/1.1 KeepAlive problems with Netscape 3.0</H1> + +<PRE> +Date: Mon, 1 Jul 1996 16:03:06 -0700 (PDT) +From: Alexei Kosut <akosut@organic.com> +To: Apache Group +Subject: Re: keepalive and windoze + +Good news and good news (of a sort).. + +I was able to snag a Windows 95 machine here at Organic, and tried out +some things: + +1) On Netscape 3.0b4, I was able to reproduce the bug, each and every +time. It's really simple: go to the Network Setup panel. Set it to +"Connect Every Time" and only let it have 1 connection at once (this may +not be neccessary, but it's helpeful). Then load an image that's +kept-alive. Then wait until the connection times out (this depends on the +server - 10-30 seconds, except for MIIS, which never times out, near as I +can tell). Then hit reload. It will hang. (actually, once it crashed). + +2) This happens with all forms of server. Apache 1.1, Netscape 2.0, +Spyglass 1.2, NCSA 1.5 (although, as stated, I couldn't test MIIS). + +3) Netscape 3.0b5 does, indeed, *not* have this bug. At least, I couldn't +get it to perform such. Yipee. + +So, we just put up a note on the web page. Make sure we say that all the +servers have the bug, it's a Windows bug, and Netscape Navigator 3.0b5 +works around it. That way, no one can yell at us. Yes? + +-- Alexei Kosut <akosut@organic.com> The Apache HTTP Server + http://www.nueva.pvt.k12.ca.us/~akosut/ http://www.apache.org/ +</PRE> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY></HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/core.html b/usr.sbin/httpd/htdocs/manual/mod/core.html new file mode 100644 index 00000000000..27c2a6c00da --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/core.html @@ -0,0 +1,1462 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache Core Features</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Apache Core Features</h1> + +These configuration parameters control the core Apache features, and are +always available. + + +<ul> +<li><A HREF="#accessconfig">AccessConfig</A> +<li><A HREF="#accessfilename">AccessFileName</A> +<li><A HREF="#addmodule">AddModule</A> +<li><A HREF="#allowoverride">AllowOverride</A> +<li><A HREF="#authname">AuthName</A> +<li><A HREF="#authtype">AuthType</A> +<li><A HREF="#bindaddress">BindAddress</A> +<li><A HREF="#clearmodulelist">ClearModuleList</A> +<li><A HREF="#defaulttype">DefaultType</A> +<li><A HREF="#directory"><Directory></A> +<li><A HREF="#documentroot">DocumentRoot</A> +<li><A HREF="#errordocument">ErrorDocument</A> +<li><A HREF="#errorlog">ErrorLog</A> +<li><A HREF="#files"><Files></A> +<li><A HREF="#group">Group</A> +<li><A HREF="#hostnamelookups">HostNameLookups</A> +<li><A HREF="#identitycheck">IdentityCheck</A> +<li><A HREF="#ifmodule"><IfModule></A> +<li><A HREF="#keepalive">KeepAlive</A> +<li><A HREF="#keepalivetimeout">KeepAliveTimeout</A> +<li><A HREF="#limit"><Limit></A> +<li><A HREF="#listen">Listen</A> +<li><A HREF="#location"><Location></A> +<li><A HREF="#lockfile">LockFile</A> +<li><A HREF="#maxclients">MaxClients</A> +<li><A HREF="#maxkeepaliverequests">MaxKeepAliveRequests</a> +<li><A HREF="#maxrequestsperchild">MaxRequestsPerChild</A> +<li><A HREF="#maxspareservers">MaxSpareServers</A> +<li><A HREF="#minspareservers">MinSpareServers</A> +<li><A HREF="#options">Options</A> +<li><A HREF="#pidfile">PidFile</A> +<li><A HREF="#port">Port</A> +<li><A HREF="#require">require</A> +<li><A HREF="#resourceconfig">ResourceConfig</A> +<li><A HREF="#rlimitcpu">RLimitCPU</A> +<li><A HREF="#rlimitmem">RLimitMEM</A> +<li><A HREF="#rlimitnproc">RLimitNPROC</A> +<li><A HREF="#satisfy">Satisfy</A> +<li><A HREF="#scoreboardfile">ScoreBoardFile</A> +<li><A HREF="#sendbuffersize">SendBufferSize</A> +<li><A HREF="#serveradmin">ServerAdmin</A> +<li><A HREF="#serveralias">ServerAlias</A> +<li><A HREF="#servername">ServerName</A> +<li><A HREF="#serverpath">ServerPath</A> +<li><A HREF="#serverroot">ServerRoot</A> +<li><A HREF="#servertype">ServerType</A> +<li><A HREF="#startservers">StartServers</A> +<li><A HREF="#timeout">TimeOut</A> +<li><A HREF="#user">User</A> +<li><A HREF="#virtualhost"><VirtualHost></A> +</ul> +<hr> + +<A name="accessconfig"><h2>AccessConfig directive</h2></A> +<!--%plaintext <?INDEX {\tt AccessConfig} directive> --> +<strong>Syntax:</strong> AccessConfig <em>filename</em><br> +<strong>Default:</strong> <code>AccessConfig conf/access.conf</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +The server will read this file for more directives after reading the +<A HREF="#resourceconfig">ResourceConfig</A> file. <em>Filename</em> is +relative to the <A HREF="#serverroot">ServerRoot</A>. +This feature can be disabled using: +<blockquote><code>AccessConfig /dev/null</code></blockquote> +Historically, this file only contained +<A HREF="#directory"><Directory></A> sections; in fact it can now +contain any server directive allowed in the <em>server config</em> context. +<p><hr> + +<A name="accessfilename"><h2>AccessFileName directive</h2></A> +<!--%plaintext <?INDEX {\tt AccessFileName} directive> --> +<strong>Syntax:</strong> AccessFileName <em>filename</em><br> +<strong>Default:</strong> <code>AccessFileName .htaccess</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +When returning a document to the client the server looks for an +access control file with this name in every directory of the path to +the document, if access control files are enabled for that directory. + +For example: +<blockquote><code>AccessFileName .acl</code></blockquote> +before returning the document /usr/local/web/index.html, the +server will read /.acl, /usr/.acl, /usr/local/.acl and /usr/local/web/.acl +for directives, unless they have been disabled with +<blockquote><code> +<Directory /><br> +AllowOverride None<br> +</Directory></code></blockquote><p><hr> + +<A name="addmodule"><h2>AddModule directive</h2></A> +<!--%plaintext <?INDEX {\tt AddModule} directive> --> +<strong>Syntax:</strong> AddModule <em>module module ...</em><br> +<strong>Context:</strong> server config <br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> AddModule is only available in Apache 1.2 and later<p> + +The server can have modules compiled in which are not actively in use. +This directive can be used to enable the use of those modules. The +server comes with a pre-loaded list of active modules; this list can +be cleared with the <A HREF="#clearmodulelist">ClearModuleList</A> +directive.<p><hr> + +<A name="allowoverride"><h2>AllowOverride directive</h2></A> +<!--%plaintext <?INDEX {\tt AllowOverride} directive> --> +<strong>Syntax:</strong> AllowOverride <em>override override ...</em><br> +<strong>Default:</strong> <code>AllowOverride All</code><br> +<strong>Context:</strong> directory<br> +<strong>Status:</strong> core<p> + +When the server finds an .htaccess file (as specified by +<A HREF="#accessfilename">AccessFileName</A>) it needs to know which +directives declared in that file can override earlier access information.<p> + +<em>Override</em> can be set to <code>None</code>, in which case the server +will not read the file, <code>All</code> in which case the server will +allow all the directives, or one or more of the following: +<dl> +<dt>AuthConfig +<dd> +<!--%plaintext <?INDEX {\tt AuthConfig} override> --> +Allow use of the authorization directives +(<A HREF="mod_auth_dbm.html#authdbmgroupfile">AuthDBMGroupFile</A>, +<A HREF="mod_auth_dbm.html#authdbmuserfile">AuthDBMUserFile</A>, +<A HREF="mod_auth.html#authgroupfile">AuthGroupFile</A>, +<A HREF="#authname">AuthName</A>, <A HREF="#authtype">AuthType</A>, +<A HREF="mod_auth.html#authuserfile">AuthUserFile</A>, +<A HREF="#require">require</A>, etc.). +<dt>FileInfo +<dd> +<!--%plaintext <?INDEX {\tt FileInfo} override> --> +Allow use of the directives controlling document types +(<A HREF="mod_mime.html#addencoding">AddEncoding</A>, +<A HREF="mod_mime.html#addlanguage">AddLanguage</A>, +<A HREF="mod_mime.html#addtype">AddType</A>, +<A HREF="#defaulttype">DefaultType</A>, +<A HREF="#errordocument">ErrorDocument</A>, +<A HREF="mod_negotiation.html#languagepriority">LanguagePriority</A>, etc.). +<dt>Indexes +<dd> +<!--%plaintext <?INDEX {\tt Indexes} override> --> +Allow use of the directives controlling directory indexing +(<A HREF="mod_dir.html#adddescription">AddDescription</A>, +<A HREF="mod_dir.html#addicon">AddIcon</A>, +<A HREF="mod_dir.html#addiconbyencoding">AddIconByEncoding</A>, +<A HREF="mod_dir.html#addiconbytype">AddIconByType</A>, +<A HREF="mod_dir.html#defaulticon">DefaultIcon</A>, +<A HREF="mod_dir.html#directoryindex">DirectoryIndex</A>, +<A HREF="mod_dir.html#fancyindexing">FancyIndexing</A>, +<A HREF="mod_dir.html#headername">HeaderName</A>, +<A HREF="mod_dir.html#indexignore">IndexIgnore</A>, +<A HREF="mod_dir.html#indexoptions">IndexOptions</A>, +<A HREF="mod_dir.html#readmename">ReadmeName</A>, etc.). +<dt>Limit +<dd> +<!--%plaintext <?INDEX {\tt Limit} override> --> +Allow use of the directives controlling host access (allow, deny and order). +<dt>Options +<dd> +<!--%plaintext <?INDEX {\tt Options} override> --> +Allow use of the directives controlling specific directory features +(<A HREF="#options">Options</A> and +<A HREF="mod_include.html#xbithack">XBitHack</A>). +</dl><p><hr> + +<A name="authname"><h2>AuthName directive</h2></A> +<!--%plaintext <?INDEX {\tt AuthName} directive> --> +<strong>Syntax:</strong> AuthName <em>auth-domain</em><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> core<p> + +This directive sets the name of the authorization realm for a directory. +This realm is given to the client so that the user knows which username and +password to send. +It must be accompanied by <A HREF="#authtype">AuthType</A> and +<A HREF="#require">require</A> directives, and directives such as +<A HREF="mod_auth.html#authuserfile">AuthUserFile</A> and +<A HREF="mod_auth.html#authgroupfile">AuthGroupFile</A> to work.<p><hr> + +<A name="authtype"><h2>AuthType directive</h2></A> +<!--%plaintext <?INDEX {\tt AuthType} directive> --> +<strong>Syntax:</strong> AuthType <em>type</em><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> core<p> + +This directive selects the type of user authentication for a directory. +Only <code>Basic</code> is currently implemented. +<!--%plaintext <?INDEX {\tt Basic} authentication scheme> --> +It must be accompanied by <A HREF="#authname">AuthName</A> and +<A HREF="#require">require</A> directives, and directives such as +<A HREF="mod_auth.html#authuserfile">AuthUserFile</A> and +<A HREF="mod_auth.html#authgroupfile">AuthGroupFile</A> to work.<p><hr> + +<A name="bindaddress"><h2>BindAddress directive</h2></A> +<!--%plaintext <?INDEX {\tt BindAddress} directive> --> +<strong>Syntax:</strong> BindAddress <em>saddr</em><br> +<strong>Default:</strong> <code>BindAddress *</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +A Unix® http server can either listen for connections to every +IP address of the server machine, or just one IP address of the server +machine. <em>Saddr</em> can be + +<menu> +<li>* +<li>An IP address +<li>A fully-qualified Internet domain name +</menu> +If the value is *, then the server will listen for connections on +every IP address, otherwise it will only listen on the IP address +specified. <p> + +This option can be used as an alternative method for supporting +<A HREF="../virtual-host.html">virtual hosts</A> instead of using +<A HREF="#virtualhost"><VirtualHost></A> sections. + +<p><strong>See Also:</strong> +<a href="../dns-caveats.html">DNS Issues</a><br> +<strong>See Also:</strong> +<a href="../bind.html">Setting which addresses and ports Apache uses</a></p> + +<hr> + +<A name="clearmodulelist"><h2>ClearModuleList directive</h2></A> +<!--%plaintext <?INDEX {\tt ClearModuleList} directive> --> +<strong>Syntax:</strong> ClearModuleList<br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> ClearModuleList is only available in Apache 1.2 and later<p> + +The server comes with a built-in list of active modules. This +directive clears the list. It is assumed that the list will then be +re-populated using the <A HREF="#addmodule">AddModule</A> directive.<p><hr> + +<A name="defaulttype"><h2>DefaultType directive</h2></A> +<!--%plaintext <?INDEX {\tt DefaultType} directive> --> +<strong>Syntax:</strong> DefaultType <em>mime-type</em><br> +<strong>Default:</strong> <code>DefaultType text/html</code><br> +<strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Override:</strong> FileInfo<br> +<strong>Status:</strong> core<p> + +There will be times when the server is asked to provide a document +whose type cannot be determined by its MIME types mappings.<p> + +The server must inform the client of the content-type of the document, so in +the event of an unknown type it uses the <CODE>DefaultType</CODE>. For +example: +<blockquote><code>DefaultType image/gif</code></blockquote> +would be appropriate for a directory which contained many gif images +with filenames missing the .gif extension.<p><hr> + +<A name="directory"><h2><Directory> directive</h2></A> +<!--%plaintext <?INDEX {\tt Directory} section directive> --> +<strong>Syntax:</strong> <Directory <em>directory</em>> ... </Directory> <br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Core. <p> + +<Directory> and </Directory> are used to enclose a group of +directives which will apply only to the named directory and sub-directories +of that directory. Any directive which is allowed in a directory +context may be used. <em>Directory</em> is either the full path to a directory, +or a wild-card string. In a wild-card string, `?' matches any single character, +and `*' matches any sequences of characters. Example: +<pre> + <Directory /usr/local/httpd/htdocs> + Options Indexes FollowSymLinks + </Directory> +</pre> + +<p><strong>Apache 1.2 and above:</strong> +Extended regular expressions can also be used, with the addition of the +<code>~</code> character. For example:</p> + +<pre> + <Directory ~ "^/www/.*/[0-9]{3}"> +</pre> + +would match directories in /www/ that consisted of three numbers.<p> + +<p>If multiple directory sections match the directory (or its parents) containing +a document, then the directives are applied in the order of shortest match +first, interspersed with the directives from the +<A HREF="#accessfilename">.htaccess</A> files. For example, with +<blockquote><code> +<Directory /><br> +AllowOverride None<br> +</Directory><br><br> +<Directory /home/*><br> +AllowOverride FileInfo<br> +</Directory></code></blockquote> +for access to the document <code>/home/web/dir/doc.html</code> the +steps are: +<menu> +<li>Apply directive <code>AllowOverride None</code> (disabling +<code>.htaccess</code> files). +<li>Apply directive <code>AllowOverride FileInfo</code> (for directory +<code>/home/web</code>). +<li>Apply any FileInfo directives in <code>/home/web/.htaccess</code> +</menu> + +<P> +<STRONG> +Note that the default Apache access for <Directory /> is +<SAMP>Allow from All</SAMP>. This means that Apache will serve any file +mapped from an URL. It is recommended that you change this with a block +such as +</STRONG> +<PRE> + <Directory /> + Order Deny,Allow + Deny from All + </Directory> +</PRE> +<P> +<STRONG> +and then override this for directories you <EM>want</EM> accessible. +See the +<A + HREF="../misc/security_tips.html" +>Security Tips</A> +page for more details. +</STRONG> +</P> + +The directory sections typically occur in the access.conf file, but they +may appear in any configuration file. <Directory> directives cannot +nest, and cannot appear in a <A HREF="#limit"><Limit></A> section. +<p><hr> + +<A NAME="documentroot"><h2>DocumentRoot directive</h2></A> +<!--%plaintext <?INDEX {\tt DocumentRoot} directive> --> +<strong>Syntax:</strong> DocumentRoot <em>directory-filename</em><br> +<strong>Default:</strong> <code>DocumentRoot +/usr/local/etc/httpd/htdocs</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +This directive sets the directory from which httpd will serve files. +Unless matched by a directive like Alias, the server appends the path +from the requested URL to the document root to make the path to the +document. Example: +<blockquote><code>DocumentRoot /usr/web</code></blockquote> +then an access to <code>http://www.my.host.com/index.html</code> refers +to <code>/usr/web/index.html</code>. + +<P>There appears to be a bug in mod_dir which causes problems when the +DocumentRoot has a trailing slash (i.e. "DocumentRoot /usr/web/") so +please avoid that. + +<p><hr> + +<A name="errordocument"><h2>ErrorDocument directive</h2></A> +<!--%plaintext <?INDEX {\tt ErrorDocument} directive> --> +<strong>Syntax:</strong> ErrorDocument <em>error-code document</em><br> +<strong>Context</strong> server config, virtual host, directory, .htaccess<br> +<strong>Status:</strong> core<br> +<strong>Override:</strong> FileInfo<br> +<strong>Compatibility:</strong> The directory and .htaccess contexts +are only available in Apache 1.1 and later.<p> + +In the event of a problem or error, Apache can be configured to do +one of four things, + +<OL> +<LI>output a simple hardcoded error message +<LI>output a customized message +<LI>redirect to a local URL to handle the problem/error +<LI>redirect to an external URL to handle the problem/error +</OL> + +<P>The first option is the default, while options 2-4 are configured +using the <CODE>ErrorDocument</CODE> directive, which is followed by +the HTTP response code and a message or URL. + +<P><em>Messages</em> in this context begin with a single quote +(<code>"</code>), which does not form part of the message itself. +Apache will sometimes offer additional information regarding the +problem/error. + +<P>URLs can begin with a slash (/) for local URLs, or be a full +URL which the client can resolve. Examples: +<blockquote><code> +ErrorDocument 500 http://foo.example.com/cgi-bin/tester<br> +ErrorDocument 404 /cgi-bin/bad_urls.pl<br> +ErrorDocument 401 /subscription_info.html<br> +ErrorDocument 403 "Sorry can't allow you access today +</code></blockquote> + +<P>Note that when you specify an <CODE>ErrorDocument</CODE> that +points to a remote URL (ie. anything with a method such as "http" in +front of it) Apache will send a redirect to the client to tell it +where to find the document, even if the document ends up being +on the same server.. This has several implications, the +most important being that <STRONG>if you use an "ErrorDocument 401" +directive then it must refer to a local document.</STRONG> This results +from the nature of the HTTP basic authentication scheme. + +<P>See Also: <A HREF="../custom-error.html">documentation of customizable +responses.</A><p><hr> + +<A name="errorlog"><h2>ErrorLog directive</h2></A> +<!--%plaintext <?INDEX {\tt ErrorLog} directive> --> +<strong>Syntax:</strong> ErrorLog <em>filename</em><br> +<strong>Default:</strong> <code>ErrorLog logs/error_log</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +The error log directive sets the name of the file to which the server will log +any errors it encounters. If the filename does not begin with a slash (/) +then it is assumed to be relative to the <A HREF="#serverroot">ServerRoot</A>. +Example: +<blockquote><code>ErrorLog /dev/null</code></blockquote> +This effectively turns off error logging.<p> + +SECURITY: See the <A HREF="../misc/security_tips.html">security tips</A> +document for details on why your security could be compromised if +the directory where logfiles are stored is writable by anyone other +than the user that starts the server. + +<p><hr> + +<A name="files"><h2><Files></h2></A> +<strong>Syntax:</strong> <Files <em>filename</em>> +... </Files><br> +<strong>Context:</strong> server config, virtual host, htaccess<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> only available in Apache +1.2 and above.<p> + +<p>The <Files> directive provides for access control by +filename. It is comparable to the <a +href="#directory"><Directory></a> directive and +<a href="#location"><Location></a> directives. It +should be matched with a </Files> directive. Directives that +apply to the filename given should be listed +within. <code><Files></code> sections are processed in the +order they appear in the configuration file, after the +<Directory> sections and <code>.htaccess</code> files are +read, but before <Location> sections.</p> + +<p>The <em>filename</em> argument should include a filename, or a +wild-card string, where `?' matches any single character, and `*' matches any +sequences of characters. Extended regular expressions can also be used, with the addition of +the <code>~</code> character. For example:</p> + +<pre> + <Files ~ "\.(gif|jpe?g|png)$"> +</pre> + +would match most common Internet graphics formats. + +<p>Note that unlike <a +href="#directory"><code><Directory></code></a> and <a +href="#location"><code><Location></code></a> sections, +<code><Files></code> sections can be used inside .htaccess +files. This allows users to control access to their own files, at a +file-by-file level. When used in an .htaccess file, if the +<em>filename</em> does not begin with a <code>/</code> character, +the directory being applied will be prefixed automatically. + +<p> <hr> + +<A name="group"><h2>Group directive</h2></A> +<!--%plaintext <?INDEX {\tt Group} directive> --> +<strong>Syntax:</strong> Group <em>unix-group</em><br> +<strong>Default:</strong> <code>Group #-1</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +The Group directive sets the group under which the server will answer requests. +In order to use this directive, the stand-alone server must be run initially +as root. <em>Unix-group</em> is one of: +<dl> +<dt>A group name +<dd>Refers to the given group by name. +<dt># followed by a group number. +<dd>Refers to a group by its number. +</dl> + +It is recommended that you set up a new group specifically for running the +server. Some admins use user <code>nobody</code>, but this is not always +possible or desirable.<p> + +Note: if you start the server as a non-root user, it will fail to change +to the specified group, and will instead continue to run as the group of the +original user. <p> + +Special note: Use of this directive in <VirtualHost> requires a +properly configured <A HREF="../suexec.html">suEXEC wrapper</A>. +When used inside a <VirtualHost> in this manner, only the group +that CGIs are run as is affected. Non-CGI requests are still processed +as the group specified in the main Group directive.<p> + +SECURITY: See <A HREF="#user">User</A> for a discussion of the security +considerations.<p><hr> + +<A name="hostnamelookups"><h2>HostNameLookups directive</h2></A> +<!--%plaintext <?INDEX {\tt HostNameLookups} directive> --> +<strong>Syntax:</strong> HostNameLookups <em>boolean</em><br> +<strong>Default:</strong> <code>HostNameLookups on</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +This directive enables DNS lookups so that host names can be logged. +Having this directive set <code>on</code> also enables the use of names +in <Limit> blocks for access control.<p> + +Heavily loaded sites should set this directive <code>off</code>, since DNS +lookups can take considerable amounts of time. The utility <i>logresolve</i>, +provided in the <i>/support</i> directory, can be used to look up host names +from logged IP addresses offline.<p><hr> + +<A name="identitycheck"><h2>IdentityCheck directive</h2></A> +<!--%plaintext <?INDEX {\tt IdentityCheck} directive> --> +<strong>Syntax:</strong> IdentityCheck <em>boolean</em><br> +<strong>Default:</strong> <code>IdentityCheck off</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +This directive enables RFC1413-compliant logging of the remote user name +for each connection, where the client machine runs identd or something similar. +This information is logged in the access log. <em>Boolean</em> is either +<code>on</code> or <code>off</code>.<p> + +The information should not be trusted in any way except for rudimentary usage +tracking.<p> + +Note that this can cause serious latency problems accessing your server +since every request requires one of these lookups to be performed. When +firewalls are involved each lookup might possibly fail and add 30 seconds +of latency to each hit. So in general this is not very useful on public +servers accessible from the Internet. +<p><hr> + +<A NAME="ifmodule"><H2><IfModule></H2></A> +<b>Syntax:</b> <IfModule [!]<i>module-name</i>> <i>...</i> +</IfModule><br> +<b>Default:</b> None<br> +<b>Context:</b> all<br> +<b>Status:</b> Core +<strong>Compatibility:</strong> ScriptLog is only available in 1.2 and +later.<P> + +<p> + +The <IfModule <i>test</i>>...</IfModule> +section is used to mark directives that are conditional. The +directives within an IfModule section are only +processed if the <i>test</i> is true. If <i>test</i> +is false, everything between the start and end markers +is ignored.<p> + +The <i>test</i> in the <IfModule> section directive +can be one of two forms: + +<ul> +<li><i>module name</i> +<li>!<i>module name</i> +</ul> + +<p>In the former case, the directives between the start and end markers +are only processed if the module named <i>module name</i> is compiled +in to Apache. The second format reverses the test, and only processes +the directives if <i>module name</i> is <b>not</b> compiled in. + +<p>The <i>module name</i> argument is a module name as given as the file +name of the module, at the time it was compiled. For example, +<code>mod_rewrite.c</code>. + +<p><IfModule> sections are nest-able, which can be used to implement +simple multiple-module tests. + +<P> <hr> + +<h2><a name="keepalive">KeepAlive</a></h2> +<strong>Syntax: (Apache 1.1)</strong> KeepAlive <em>max-requests</em><br> +<strong>Default: (Apache 1.1)</strong> <code>KeepAlive 5</code><br> +<strong>Syntax: (Apache 1.2)</strong> KeepAlive <em>on/off</em><br> +<strong>Default: (Apache 1.2)</strong> <code>KeepAlive On</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Core<br> +<strong>Compatibility:</strong> KeepAlive is only available in Apache +1.1 and later.<p> + +This directive enables +<a href="../keepalive.html">Keep-Alive</a> +support. + +<p><strong>Apache 1.1</strong>: Set <em>max-requests</em> +to the maximum number of requests you want Apache to entertain per +request. A limit is imposed to prevent a client from hogging your +server resources. Set this to <code>0</code> to disable support. + +<p><strong>Apache 1.2 and later</strong>: Set to "On" to enable +persistent connections, "Off" to disable. See also the <a +href="#maxkeepaliverequests">MaxKeepAliveRequests</a> directive.</p> + +<h2><a name="keepalivetimeout">KeepAliveTimeout</a></h2> +<strong>Syntax:</strong> KeepAliveTimeout <em>seconds</em><br> +<strong>Default:</strong> <code>KeepAliveTimeout 15</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Core<br> +<strong>Compatibility:</strong> KeepAliveTimeout is only available in Apache +1.1 and later.<p> + +The number of seconds Apache will wait for a subsequent request before +closing the connection. Once a request has been received, the timeout +value specified by the <a +href="#timeout"><code>Timeout</code></a> directive +applies. +<hr> + +<A name="listen"><h2>Listen</h2></A> +<strong>Syntax:</strong> +Listen [<em>IP address</em>:]<em>port number</em><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> Listen is only available in Apache +1.1 and later.<p> + +<p>The Listen directive instructs Apache to listen to more than one IP +address or port; by default it responds to requests on all IP +interfaces, but only on the port given by the <a href="#port">Port</a> +directive.</p> + +<p><strong>See Also:</strong> +<a href="../dns-caveats.html">DNS Issues</a><br> +<strong>See Also:</strong> +<a href="../bind.html">Setting which addresses and ports Apache uses</a><br> +<strong>See Also:</strong> +<a href="../misc/known_bugs.html#listenbug">Known Bugs</a></p> +<hr> + +<A name="limit"><h2><Limit> directive</h2></A> +<!--%plaintext <?INDEX {\tt Limit} section directive> --> +<strong>Syntax:</strong> + <Limit <em>method method</em> ... > ... </Limit><br> +<strong>Context:</strong> any<br> +<strong>Status:</strong> core<p> + +<Limit> and </Limit> are used to enclose a group of +access control directives which will then apply only to the specified +access methods, where <em>method</em> is any valid HTTP method. +Any directive except another <Limit> or +<A HREF="#directory"><Directory></A> may be used; the majority will be +unaffected by the <Limit>. Example: +<blockquote><code> +<Limit GET POST><br> +require valid-user<br> +</Limit></code></blockquote> +If an access control directive appears outside a <Limit> directive, +then it applies to all access methods.<p><hr> + +<h2><a name="location"><Location></a></h2> + +<strong>Syntax:</strong> <Location <em>URL</em>> +... </Location><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> Location is only available in Apache +1.1 and later.<p> + +<p>The <Location> directive provides for access control by +URL. It is comparable to the <a +href="#directory"><Directory></a> directive, and +should be matched with a </Location> directive. Directives that +apply to the URL given should be listed +within. <code><Location></code> sections are processed in the +order they appear in the configuration file, after the +<Directory> sections and <code>.htaccess</code> files are +read.</p> + +<p>Note that, due to the way HTTP functions, <em>URL prefix</em> +should, save for proxy requests, be of the form <code>/path/</code>, +and should not include the <code>http://servername</code>. It doesn't +necessarily have to protect a directory (it can be an individual +file, or a number of files), and can include wild-cards. In a wild-card +string, `?' matches any single character, and `*' matches any +sequences of characters. + +<p><strong>Apache 1.2 and above:</strong> +Extended regular expressions can also be used, with the addition of +the +<code>~</code> character. For example:</p> + +<pre> + <Location ~ "/(extra|special)/data"> +</pre> + +<p>would match URLs that contained the substring "/extra/data" or +"/special/data".</p> + +<p>The <code>Location</code> functionality is especially useful when +combined with the <code><a +href="mod_mime.html#sethandler">SetHandler</a></code> directive. For example, to enable status requests, but allow them only +from browsers at foo.com, you might use: + +<pre> + <Location /status> + SetHandler server-status + order deny,allow + deny from all + allow from .foo.com + </Location> +</pre> +<hr> + +<A NAME="lockfile"><H2>LockFile</H2></A> +<strong>Syntax:</strong> LockFile <em>filename</em><BR> +<strong>Default:</strong> <code>LockFile logs/accept.lock</code><BR> +<strong>Context:</strong> server config<BR> +<strong>Status:</strong> core<P> + +The LockFile directive sets the path to the lockfile used when +Apache is compiled with either USE_FCNTL_SERIALIZED_ACCEPT or +USE_FLOCK_SERIALIZED_ACCEPT. This directive should normally be +left at its default value. The main reason for changing it is if +the <code>logs</code> directory is NFS mounted, since the lockfile +should be stored on a local disk if possible. The PID of the main +server process is automatically appended to the filename. + +<P><HR> + +<A name="maxclients"><h2>MaxClients</h2></A> +<!--%plaintext <?INDEX {\tt MaxClients} directive> --> +<strong>Syntax:</strong> MaxClients <em>number</em><br> +<strong>Default:</strong> <code>MaxClients 256</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The MaxClients directive sets the limit on the number of simultaneous +requests that can be supported; not more than this number of child server +processes will be created.<p><hr> + +<A name="maxkeepaliverequests"><h2>MaxKeepAliveRequests</h2></A> +<strong>Syntax:</strong> MaxKeepAliveRequests <em>number</em><br> +<strong>Default:</strong> <code>MaxKeepAliveRequests 100</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> Only available in Apache +1.2 and later. + +<p>The MaxKeepAliveRequests directive limits the number of requests +allowed per connection when <a href="#keepalive">KeepAlive</a> is +on. If it is set to "<code>0</code>", unlimited requests will be +allowed. We recommend that this setting be kept to a high value for +maximum server performance. + +<A name="maxrequestsperchild"><h2>MaxRequestsPerChild directive</h2></A> +<!--%plaintext <?INDEX {\tt MaxRequestsPerChild} directive> --> +<strong>Syntax:</strong> MaxRequestsPerChild <em>number</em><br> +<strong>Default:</strong> <code>MaxRequestsPerChild 0</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The MaxRequestsPerChild directive sets the limit on the number of requests +that an individual child server process will handle. After MaxRequestsPerChild +requests, the child process will die. If MaxRequestsPerChild is 0, then +the process will never expire.<p> + +Setting MaxRequestsPerChild to a non-zero limit has two beneficial effects: +<ul> +<li>it limits the amount of memory that process can consume by (accidental) +memory leakage; +<li> by giving processes a finite lifetime, it helps reduce the +number of processes when the server load reduces. +</ul><p><hr> + +<A name="maxspareservers"><h2>MaxSpareServers directive</h2></A> +<!--%plaintext <?INDEX {\tt MaxSpareServers} directive> --> +<strong>Syntax:</strong> MaxSpareServers <em>number</em><br> +<strong>Default:</strong> <code>MaxSpareServers 10</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The MaxSpareServers directive sets the desired maximum number of <em>idle</em> +child server processes. An idle process is one which is not handling +a request. If there are more than MaxSpareServers idle, then the parent +process will kill off the excess processes.<p> + +Tuning of this parameter should only be necessary on very busy sites. +Setting this parameter to a large number is almost always a bad idea.<p> + +See also <A HREF="#minspareservers">MinSpareServers</A> and +<A HREF="#startservers">StartServers</A>.<p><hr> + +<A name="minspareservers"><h2>MinSpareServers directive</h2></A> +<!--%plaintext <?INDEX {\tt MinSpareServers} directive> --> +<strong>Syntax:</strong> MinSpareServers <em>number</em><br> +<strong>Default:</strong> <code>MinSpareServers 5</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The MinSpareServers directive sets the desired minimum number of <em>idle</em> +child server processes. An idle process is one which is not handling +a request. If there are fewer than MinSpareServers idle, then the parent +process creates new children at a maximum rate of 1 per second.<p> + +Tuning of this parameter should only be necessary on very busy sites. +Setting this parameter to a large number is almost always a bad idea.<p> + +See also <A HREF="#maxspareservers">MaxSpareServers</A> and +<A HREF="#startservers">StartServers</A>.<p><hr> + +<A name="options"><h2>Options directive</h2></A> +<!--%plaintext <?INDEX {\tt Options} directive> --> +<strong>Syntax:</strong> Options <em>[+|-]option [+|-]option ...</em><br> +<strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Override:</strong> Options<br> +<strong>Status:</strong> core<p> + +The Options directive controls which server features are available in +a particular directory. +<p> +<em>option</em> can be set to <code>None</code>, in which case none of +the extra features are enabled, or one or more of the following: +<dl> +<dt>All +<dd>All options except for MultiViews. +<dt>ExecCGI +<dd> +<!--%plaintext <?INDEX {\tt ExecCGI} option> --> +Execution of CGI scripts is permitted. +<dt>FollowSymLinks +<dd> +<!--%plaintext <?INDEX {\tt FollowSymLinks} option> --> +The server will follow symbolic links in this directory. +<b>Note</b>: even though the server follows the symlink it does <i>not</i> +change the pathname used to match against <code><Directory></code> +sections. +<dt>Includes +<dd> +<!--%plaintext <?INDEX {\tt Includes} option> --> +Server-side includes are permitted. +<dt>IncludesNOEXEC +<dd> +<!--%plaintext <?INDEX {\tt IncludesNOEXEC} option> --> +Server-side includes are permitted, but the #exec command and +#include of CGI scripts are disabled. +<dt>Indexes +<dd> +<!--%plaintext <?INDEX {\tt Indexes} option> --> +If a URL which maps to a directory is requested, and the there is no +DirectoryIndex (e.g. index.html) in that directory, then the server will +return a formatted listing of the directory. +<dt>MultiViews +<dd> +<!--%plaintext <?INDEX {\tt MultiViews} option> --> +<A HREF="../content-negotiation.html">Content negotiated</A> MultiViews are +allowed. +<dt>SymLinksIfOwnerMatch +<dd> +<!--%plaintext <?INDEX {\tt SymLinksIfOwnerMatch} option> --> +The server will only follow symbolic links for which the target +file or directory is owned by the same user id as the link. +</dl> + +Normally, if multiple <code>Options</code> could apply to a directory, +then the most specific one is taken complete; the options are not +merged. However if <i>all</i> the options on the <code>Options</code> +directive are preceded by a + or - symbol, the options are +merged. Any options preceded by a + are added to the options +currently in force, and any options preceded by a - are removed from +the options currently in force. <P> + +For example, without any + and - symbols: + +<blockquote><code> +<Directory /web/docs> <br> +Options Indexes FollowSymLinks<br> +</Directory><br> +<Directory /web/docs/spec> <br> +Options Includes<br> +</Directory> +</code></blockquote> +then only <code>Includes</code> will be set for the /web/docs/spec +directory. However if the second <code>Options</code> directive uses the + +and - symbols:<p> + +<blockquote><code> +<Directory /web/docs> <br> +Options Indexes FollowSymLinks<br> +</Directory><br> +<Directory /web/docs/spec> <br> +Options +Includes -Indexes<br> +</Directory> +</code></blockquote> +then the options <code>FollowSymLinks</code> and <code>Includes</code> +are set for the /web/docs/spec directory. +<hr> + +<A name="pidfile"><h2>PidFile directive</h2></A> +<!--%plaintext <?INDEX {\tt PidFile} directive> --> +<strong>Syntax:</strong> PidFile <em>filename</em><br> +<strong>Default:</strong> <code>PidFile logs/httpd.pid</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The PidFile directive sets the file to which the server records the +process id of the daemon. If the filename does not begin with a slash (/) +then it is assumed to be relative to the <A HREF="#serverroot">ServerRoot</A>. +The PidFile is only used in <A HREF="#servertype">standalone</A> mode.<p> + +It is often useful to be able to send the server a signal, so that it closes +and then reopens its <A HREF="#errorlog">ErrorLog</A> and TransferLog, and +re-reads its configuration files. This is done by sending a SIGHUP (kill -1) +signal to the process id listed in the PidFile.<p> + +The PidFile is subject to the same warnings about log file placement and +<a href="../misc/security_tips.html">security</a>. + +<p><hr> + +<A name="port"><h2>Port directive</h2></A> +<!--%plaintext <?INDEX {\tt Port} directive> --> +<strong>Syntax:</strong> Port <em>number</em><br> +<strong>Default:</strong> <code>Port 80</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +<em>Number</em> is a number from 0 to 65535; some port numbers (especially below +1024) are reserved for particular protocols. See <code>/etc/services</code> +for a list of some defined ports; the standard port for the http protocol +is 80.<p> + +The Port directive has two behaviors, the first of which is necessary for +NCSA backwards compatibility (and which is confusing in the context of +Apache).<p> + +<ul> +<li> +In the absence of any <a href="#listen">Listen</a> or +<a href="#bindaddress">BindAddress</a> directives specifying a port number, +the Port directive sets the network port on which the server listens. +If there are any Listen or BindAddress directives specifying +<code>:number</code> then Port has no effect on what address the server +listens at. + +<li>The Port directive +sets the <code>SERVER_PORT</code> environment variable (for +<a href="mod_cgi.html">CGI</a> and <a href="mod_include.html">SSI</a>), +and is used when the server must generate a URL that refers to itself +(for example when creating an external redirect to itself). +</ul> + +In no event does a Port setting affect +what ports a <a href="#virtualhost">VirtualHost</a> responds on, the +VirtualHost directive itself is used for that.<p> + +The primary behaviour of Port should be considered to be similar to that of +the <a href="#servername">ServerName</a> directive. The ServerName +and Port together specify what you consider to be the <i>canonical</i> +address of the server.<p> + +Port 80 is one of Unix's special ports. All ports numbered +below 1024 are reserved for system use, i.e. regular (non-root) users cannot +make use of them; instead they can only use higher port numbers. +To use port 80, you must start the server from the root account. +After binding to the port and before accepting requests, Apache will change +to a low privileged user as set by the <A HREF="#user">User directive</A>.<p> + +If you cannot use port 80, choose any other unused port. Non-root users +will have to choose a port number higher than 1023, such as 8000.<p> + +SECURITY: if you do start the server as root, be sure +not to set <A HREF="#user">User</A> to root. If you run the server as +root whilst handling connections, your site may be open to a major security +attack.<p><hr> + +<A name="require"><h2>require directive</h2></A> +<!--%plaintext <?INDEX {\tt require} directive> --> +<strong>Syntax:</strong> require <em>entity-name entity entity...</em><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> core<p> + +This directive selects which authenticated users can access a directory. +The allowed syntaxes are: +<ul> +<li>require user <em>userid userid ...</em><p> +Only the named users can access the directory.<p> +<li>require group <em>group-name group-name ...</em><p> +Only users in the named groups can access the directory.<p> +<li>require valid-user<p> +All valid users can access the directory. +</ul> +<p> +If <code>require</code> appears in a <A HREF="#limit"><Limit></A> +section, then it restricts access to the named methods, otherwise +it restricts access for all methods. Example: +<blockquote><code> +AuthType Basic<br> +AuthName somedomain<br> +AuthUserFile /web/users<br> +AuthGroupFile /web/groups<br> +<Limit GET POST><br> +require group admin<br> +</Limit> +</code></blockquote> + +Require must be accompanied by <A HREF="#authname">AuthName</A> and +<A HREF="#authtype">AuthType</A> directives, and directives such as +<A HREF="mod_auth.html#authuserfile">AuthUserFile</A> and +<A HREF="mod_auth.html#authgroupfile">AuthGroupFile</A> (to define users and +groups) in order to work correctly.<p><hr> + +<A name="resourceconfig"><h2>ResourceConfig directive</h2></A> +<!--%plaintext <?INDEX {\tt ResourceConfig} directive> --> +<strong>Syntax:</strong> ResourceConfig <em>filename</em><br> +<strong>Default:</strong> <code>ResourceConfig conf/srm.conf</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +The server will read this file for more directives after reading the +httpd.conf file. <em>Filename</em> is relative to the +<A HREF="#serverroot">ServerRoot</A>. +This feature can be disabled using: +<blockquote><code>ResourceConfig /dev/null</code></blockquote> +Historically, this file contained most directives except for server +configuration directives and <A HREF="#directory"><Directory></A> +sections; in fact it can now contain any server directive allowed in the +<em>server config</em> context.<p> + +See also <A HREF="#accessconfig">AccessConfig</A>.<p><hr> + +<A name="rlimit"> </A> +<A name="rlimitcpu"><h2>RLimitCPU directive</h2></A> +<!--%plaintext <?INDEX {\tt RLimitCPU} directive> --> +<strong>Syntax:</strong> RLimitCPU <em># or 'max'</em> <em>[# or 'max']</em><br> +<strong>Default:</strong> <code>Unset uses operating system defaults</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> RLimitCPU is only available in Apache 1.2 and later<p> + +Takes 1 or 2 parameters. The first parameter sets the soft resource limit for all +processes and the second parameter sets the maximum resource limit. Either parameter +can be a number, or <em>max</em> to indicate to the server that the limit should +be set to the maximum allowed by the operating system configuration. Raising the +maximum resource limit requires that the server is running as root, or in the initial +startup phase.<p> + +CPU resource limits are expressed in seconds per process.<p> + +See also <A HREF="#rlimitmem">RLimitMEM</A> or <A HREF="#rlimitnproc">RLimitNPROC</A>.<p><hr> + +<A name="rlimitmem"><h2>RLimitMEM directive</h2></A> +<!--%plaintext <?INDEX {\tt RLimitMEM} directive> --> +<strong>Syntax:</strong> RLimitMEM <em># or 'max'</em> <em>[# or 'max']</em><br> +<strong>Default:</strong> <code>Unset uses operating system defaults</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> RLimitMEM is only available in Apache 1.2 and later<p> + +Takes 1 or 2 parameters. The first parameter sets the soft resource limit for all +processes and the second parameter sets the maximum resource limit. Either parameter +can be a number, or <em>max</em> to indicate to the server that the limit should +be set to the maximum allowed by the operating system configuration. Raising the +maximum resource limit requires that the server is running as root, or in the initial +startup phase.<p> + +Memory resource limits are expressed in bytes per process.<p> + +See also <A HREF="#rlimitcpu">RLimitCPU</A> or <A HREF="#rlimitnproc">RLimitNPROC</A>.<p><hr> + +<A name="rlimitnproc"><h2>RLimitNPROC directive</h2></A> +<!--%plaintext <?INDEX {\tt RLimitNPROC} directive> --> +<strong>Syntax:</strong> RLimitNPROC <em># or 'max'</em> <em>[# or 'max']</em><br> +<strong>Default:</strong> <code>Unset uses operating system defaults</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> RLimitNPROC is only available in Apache 1.2 and later<p> + +Takes 1 or 2 parameters. The first parameter sets the soft resource limit for all +processes and the second parameter sets the maximum resource limit. Either parameter +can be a number, or <em>max</em> to indicate to the server that the limit should +be set to the maximum allowed by the operating system configuration. Raising the +maximum resource limit requires that the server is running as root, or in the initial +startup phase.<p> + +Process limits control the number of processes per user.<p> + +Note: If CGI processes are <b>not</b> running under userids other than the +web server userid, this directive will limit the number of processes that the +server itself can create. Evidence of this situation will be indicated by +<b><em>cannot fork</em></b> messages in the error_log.<p> + +See also <A HREF="#rlimitmem">RLimitMEM</A> or <A HREF="#rlimitcpu">RLimitCPU</A>. + +<p><hr> + +<A name="satisfy"><h2>Satisfy</h2></A> +<!--%plaintext <?INDEX {\tt Satisfy} directive> --> +<strong>Syntax:</strong> Satisfy <em>'any' or 'all'</em><br> +<strong>Default:</strong> Satisfy all<br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> Satisfy is only available in Apache 1.2 and later<p> + +Access policy if both allow and require used. The parameter can be +either <em>'all'</em> or <em>'any'</em>. This directive is only useful +if access to a particular area is being restricted by both +username/password <i>and</i> client host address. In this case the +default behavior ("all") is to require that the client passes the +address access restriction <i>and</i> enters a valid username and +password. With the "any" option the client will be granted access if +they either pass the host restriction or enter a valid username and +password. This can be used to password restrict an area, but to let +clients from particular addresses in without prompting for a password. + + +<p><hr> + +<A name="scoreboardfile"><h2>ScoreBoardFile directive</h2></A> +<!--%plaintext <?INDEX {\tt ScoreBoardFile} directive> --> +<strong>Syntax:</strong> ScoreBoardFile <em>filename</em><br> +<strong>Default:</strong> <code>ScoreBoardFile logs/apache_status</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The ScoreBoardFile directive is required on some architectures to place +a file that the server will use to communicate between its children and +the parent. The easiest way to find out if your architecture requires +a scoreboard file is to run Apache and see if it creates the file named +by the directive. If your architecture requires it then you must ensure +that this file is not used at the same time by more than one invocation +of Apache.<p> + +If you have to use a ScoreBoardFile then you may see improved speed by +placing it on a RAM disk. But be careful that you heed the same warnings +about log file placement and +<a href="../misc/security_tips.html">security</a>.<p> + +Apache 1.2 and above:<p> + +Linux 1.x users might be able to add <code>-DHAVE_SHMGET</code> to +the <code>EXTRA_CFLAGS</code> in your <code>Configuration</code>. This +might work with some 1.x installations, but won't work with all of +them.<p> + +SVR4 users should consider adding <code>-DHAVE_SHMGET</code> to the +<code>EXTRA_CFLAGS</code> in your <code>Configuration</code>. This +is believed to work, but we were unable to test it in time for 1.2 +release.<p> + +<strong>See Also</strong>: +<a href="../stopping.html">Stopping and Restarting Apache</a></p> + +<p><hr> + +<A name="sendbuffersize"><h2>SendBufferSize directive</h2></A> +<!--%plaintext <?INDEX {\tt SendBufferSize} directive> --> +<strong>Syntax:</strong> SendBufferSize <em>bytes</em><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The server will set the TCP buffer size to the number of bytes +specified. Very useful to increase past standard OS defaults on high +speed high latency (i.e. 100ms or so, such as transcontinental +fast pipes) +<p><hr> + +<A name="serveradmin"><h2>ServerAdmin directive</h2></A> +<!--%plaintext <?INDEX {\tt ServerAdmin} directive> --> +<strong>Syntax:</strong> ServerAdmin <em>email-address</em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +The ServerAdmin sets the e-mail address that the server includes in any +error messages it returns to the client.<p> + +It may be worth setting up a dedicated address for this, e.g. +<blockquote><code>ServerAdmin www-admin@foo.bar.com</code></blockquote> +as users do not always mention that they are talking about the server!<p><hr> + +<A name="serveralias"><h2>ServerAlias directive</h2></A> + +<strong>Syntax:</strong> ServerAlias <em>host1 host2 ...</em><br> +<strong>Context:</strong> virtual host<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> ServerAlias is only available in Apache +1.1 and later.<p> + +The ServerAlias directive sets the alternate names for a host, for use +with +<a href="../host.html">Host-header based virtual hosts</a>. +<p><strong>See Also</strong>: +<a href="../vhosts-in-depth.html">In-depth description of Virtual Host matching</a></p> + +<hr> + +<A name="servername"><h2>ServerName directive</h2></A> +<!--%plaintext <?INDEX {\tt ServerName} directive> --> +<strong>Syntax:</strong> ServerName <em>fully-qualified domain name</em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +The ServerName directive sets the hostname of the server; this is only +used when creating redirection URLs. If it is not specified, then the +server attempts to deduce it from its own IP address; however this may +not work reliably, or may not return the preferred hostname. For example: +<blockquote><code>ServerName www.wibble.com</code></blockquote> +would be used if the canonical (main) name of the actual machine +were <code>monster.wibble.com</code>.<p> +<p><strong>See Also</strong>: +<a href="../dns-caveats.html">DNS Issues</a></p> +<hr> + +<A name="serverpath"><h2>ServerPath directive</h2></A> + +<strong>Syntax:</strong> ServerPath <em>pathname</em><br> +<strong>Context:</strong> virtual host<br> +<strong>Status:</strong> core<br> +<strong>Compatibility:</strong> ServerPath is only available in Apache +1.1 and later.<p> + +The ServerPath directive sets the legacy URL pathname for a host, for +use with <a href="../host.html">Host-header based virtual hosts</a>. +<p><strong>See Also</strong>: +<a href="../vhosts-in-depth.html">In-depth description of Virtual Host matching</a></p> +<hr> + +<A name="serverroot"><h2>ServerRoot directive</h2></A> +<!--%plaintext <?INDEX {\tt ServerRoot} directive> --> +<strong>Syntax:</strong> ServerRoot <em>directory-filename</em><br> +<strong>Default:</strong> <code>ServerRoot /usr/local/etc/httpd</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The ServerRoot directive sets the directory in which the server lives. +Typically it will contain the subdirectories <code>conf/</code> and +<code>logs/</code>. Relative paths for other configuration files are taken +as relative to this directory.<br> +See also <a href="../invoking.html">the <code>-d</code> option to httpd</a>.<p><hr> + +<A name="servertype"><h2>ServerType directive</h2></A> +<!--%plaintext <?INDEX {\tt ServerType} directive> --> +<strong>Syntax:</strong> ServerType <em>type</em><br> +<strong>Default:</strong> <code>ServerType standalone</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The ServerType directive sets how the server is executed by the system. +<em>Type</em> is one of +<dl> +<dt>inetd +<dd>The server will be run from the system process inetd; the command to start +the server is added to <code>/etc/inetd.conf</code> +<dt>standalone +<dd>The server will run as a daemon process; the command to start the server +is added to the system startup scripts. (<code>/etc/rc.local</code> or +<code>/etc/rc3.d/...</code>.) +</dl> + +Inetd is the lesser used of the two options. For each http +connection received, a new copy of the server is started from scratch; +after the connection is complete, this program exits. There is a high price to +pay per connection, but for security reasons, some admins prefer this option. +<p> + +Standalone is the most common setting for ServerType since +it is far more efficient. The server is started once, and services all +subsequent connections. If you intend running Apache to serve a busy site, +standalone will probably be your only option.<p> + +SECURITY: if you are paranoid about security, run in inetd mode. Security +cannot be guaranteed in either, but whilst most people are happy to use +standalone, inetd is probably least prone to attack.<p><hr> + +<A name="startservers"><h2>StartServers directive</h2></A> +<!--%plaintext <?INDEX {\tt StartServers} directive> --> +<strong>Syntax:</strong> StartServers <em>number</em><br> +<strong>Default:</strong> <code>StartServers 5</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The StartServers directive sets the number of child server processes created +on startup. As the number of processes is dynamically controlled depending +on the load, there is usually little reason to adjust this parameter.<p> + +See also <A HREF="#minspareservers">MinSpareServers</A> and +<A HREF="#maxspareservers">MaxSpareServers</A>.<p><hr> + +<A name="timeout"><h2>TimeOut directive</h2></A> +<!--%plaintext <?INDEX {\tt TimeOut} directive> --> +<strong>Syntax:</strong> TimeOut <em>number</em><br> +<strong>Default:</strong> <code>TimeOut 300</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> core<p> + +The TimeOut directive currently defines the amount of time Apache will +wait for three things: + +<OL> + <LI>The total amount of time it takes to receive a GET request. + <LI>The amount of time between receipt of TCP packets on a POST or + PUT request. + <LI>The amount of time between ACKs on transmissions of TCP packets + in responses. +</OL> + +We plan on making these separately configurable at some point down the +road. The timer used to default to 1200 before 1.2, but has been +lowered to 300 which is still far more than necessary in most +situations. It is not set any lower by default because there may +still be odd places in the code where the timer is not reset when +a packet is sent. + +<p><hr> + +<A name="user"><h2>User directive</h2></A> +<!--%plaintext <?INDEX {\tt User} directive> --> +<strong>Syntax:</strong> User <em>unix-userid</em><br> +<strong>Default:</strong> <code>User #-1</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> core<p> + +The User directive sets the userid as which the server will answer requests. +In order to use this directive, the standalone server must be run initially +as root. <em>Unix-userid</em> is one of: +<dl> +<dt>A username +<dd>Refers to the given user by name. +<dt># followed by a user number. +<dd>Refers to a user by their number. +</dl> + +The user should have no privileges which result in it being able to access +files which are not intended to be visible to the outside world, and +similarly, the user should not be able to execute code which is not +meant for httpd requests. It is recommended that you set up a new user and +group specifically for running the server. Some admins use user +<code>nobody</code>, but this is not always possible or desirable.<p> + +Notes: If you start the server as a non-root user, it will fail to change +to the lesser privileged user, and will instead continue to run as +that original user. If you do start the server as root, then it is normal +for the parent process to remain running as root.<p> + +Special note: Use of this directive in <VirtualHost> requires a +properly configured <A HREF="../suexec.html">suEXEC wrapper</A>. +When used inside a <VirtualHost> in this manner, only the user +that CGIs are run as is affected. Non-CGI requests are still processed +with the user specified in the main User directive.<p> + +SECURITY: Don't set User (or <A HREF="#group">Group</A>) to +<code>root</code> unless you know exactly what you are doing, and what the +dangers are.<p><hr> + +<A name="virtualhost"><h2><VirtualHost> directive</h2></A> +<!--%plaintext <?INDEX {\tt VirtualHost} section directive> --> +<strong>Syntax:</strong> <VirtualHost <em>addr</em>[:<em>port</em>] ...> ... +</VirtualHost> <br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Core.<br> +<strong>Compatibility:</strong> Non-IP address-based Virtual Hosting only +available in Apache 1.1 and later.<br> +<strong>Compatibility:</strong> Multiple address support only available in +Apache 1.2 and later.<p> + +<VirtualHost> and </VirtualHost> are used to enclose a group of +directives which will apply only to a particular virtual host. +Any directive which is allowed in a virtual host context may be used. +When the server receives a request for a document on a particular virtual +host, it uses the configuration directives enclosed in the <VirtualHost> +section. <em>Addr</em> can be +<menu> +<li>The IP address of the virtual host +<li>A fully qualified domain name for the IP address of the virtual host. +</menu> Example: +<blockquote> +<code> +<VirtualHost 10.1.2.3> <br> +ServerAdmin webmaster@host.foo.com <br> +DocumentRoot /www/docs/host.foo.com <br> +ServerName host.foo.com <br> +ErrorLog logs/host.foo.com-error_log <br> +TransferLog logs/host.foo.com-access_log <br> +</VirtualHost> +</code></blockquote> + +Each VirtualHost must correspond to a different IP address or a +different host name for the server, in the latter case the server +machine must be configured to accept IP packets for multiple +addresses. (If the machine does not have multiple network interfaces, +then this can be accomplished with the <code>ifconfig alias</code> +command (if your OS supports it), or with kernel patches like <A +HREF="../misc/vif-info.html">VIF</A> (for SunOS(TM) 4.1.x)).<p> + +The special name <code>_default_</code> can be specified in which case +this virtual host will match any IP address that is not explicitly listed +in another virtual host. In the absence of any _default_ virtual host +the "main" server config, consisting of all those definitions outside +any VirtualHost section, is used when no match occurs.<p> + +You can specify a <code>:port</code> to change the port that is matched. +If unspecified then it defaults to the same port as the most recent +<code><a href="#port">Port</a></code> statement of the main server. You +may also specify <code>:*</code> to match all ports on that address. +(This is recommended when used with <code>_default_</code>.)<p> + +<strong>SECURITY</strong>: See the +<A HREF="../misc/security_tips.html">security tips</A> +document for details on why your security could be compromised if +the directory where logfiles are stored is writable by anyone other +than the user that starts the server. + +<p><strong>See also:</strong> +<A HREF="../dns-caveats.html">Warnings about DNS and Apache</a><br> +<strong>See also:</strong> +<A HREF="../virtual-host.html">Information on Virtual Hosts. +(multihome)</A><br> +<strong>See also:</strong> +<a href="../host.html">Non-IP address-based Virtual Hosts</a><br> +<strong>See also:</strong> +<a href="../vhosts-in-depth.html">In-depth description of Virtual Host matching</a> +</p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/directives.html b/usr.sbin/httpd/htdocs/manual/mod/directives.html new file mode 100644 index 00000000000..e5d8fb839e1 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/directives.html @@ -0,0 +1,190 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache directives</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Apache directives</H1> + +<ul> +<li><A HREF="core.html#accessconfig">AccessConfig</A> +<li><A HREF="core.html#accessfilename">AccessFileName</A> +<li><A HREF="mod_actions.html#action">Action</A> +<li><A HREF="mod_dir.html#addalt">AddAlt</A> +<li><A HREF="mod_dir.html#addaltbyencoding">AddAltByEncoding</A> +<li><A HREF="mod_dir.html#addaltbytype">AddAltByType</A> +<li><A HREF="mod_dir.html#adddescription">AddDescription</A> +<li><A HREF="mod_mime.html#addencoding">AddEncoding</A> +<li><A HREF="mod_mime.html#addhandler">AddHandler</A> +<li><A HREF="mod_dir.html#addicon">AddIcon</A> +<li><A HREF="mod_dir.html#addiconbyencoding">AddIconByEncoding</A> +<li><A HREF="mod_dir.html#addiconbytype">AddIconByType</A> +<li><A HREF="mod_mime.html#addlanguage">AddLanguage</A> +<li><A HREF="core.html#addmodule">AddModule</A> +<li><A HREF="mod_mime.html#addtype">AddType</A> +<li><A HREF="mod_log_agent.html#agentlog">AgentLog</A> +<li><A HREF="mod_alias.html#alias">Alias</A> +<li><A HREF="mod_access.html#allow">allow</A> +<li><A HREF="core.html#allowoverride">AllowOverride</A> +<li><A HREF="mod_auth_anon.html#anonymous">Anonymous</A> +<li><A HREF="mod_auth_anon.html#Authoritative">Anonymous_Authoritative</A> +<li><A HREF="mod_auth_anon.html#LogEmail">Anonymous_LogEmail</A> +<li><A HREF="mod_auth_anon.html#MustGiveEmail">Anonymous_MustGiveEmail</A> +<li><A HREF="mod_auth_anon.html#NoUserID">Anonymous_NoUserID</A> +<li><A HREF="mod_auth_anon.html#VerifyEmail">Anonymous_VerifyEmail</A> +<li><A HREF="mod_auth.html#authauthoritative">AuthAuthoritative</A> +<li><A HREF="mod_auth_db.html#authdbauthoritative">AuthDBAuthoritative</A> +<li><A HREF="mod_auth_db.html#authdbgroupfile">AuthDBGroupFile</A> +<li><A HREF="mod_auth_dbm.html#authdbmauthoritative">AuthDBMAuthoritative</A> +<li><A HREF="mod_auth_dbm.html#authdbmgroupfile">AuthDBMGroupFile</A> +<li><A HREF="mod_auth_dbm.html#authdbmgroupfile">AuthDBMGroupFile</A> +<li><A HREF="mod_auth_db.html#authdbuserfile">AuthDBUserFile</A> +<li><A HREF="mod_auth_dbm.html#authdbmuserfile">AuthDBMUserFile</A> +<li><A HREF="mod_digest.html#authdigestfile">AuthDigestFile</A> +<li><A HREF="mod_auth.html#authgroupfile">AuthGroupFile</A> +<li><A HREF="core.html#authname">AuthName</A> +<li><A HREF="core.html#authtype">AuthType</A> +<li><A HREF="mod_auth.html#authuserfile">AuthUserFile</A> +<li><A HREF="core.html#bindaddress">BindAddress</A> +<li><A HREF="mod_browser.html#browsermatch">BrowserMatch</A> +<li><A HREF="mod_browser.html#browsermatchnocase">BrowserMatchNoCase</A> +<li><A HREF="mod_proxy.html#cachedefaultexpire">CacheDefaultExpire</A> +<li><A HREF="mod_proxy.html#cachedirlength">CacheDirLength</A> +<li><A HREF="mod_proxy.html#cachedirlevels">CacheDirLevels</A> +<li><A HREF="mod_proxy.html#cachegcinterval">CacheGcInterval</A> +<li><A HREF="mod_proxy.html#cachelastmodifiedfactor">CacheLastModifiedFactor</A> +<li><A HREF="mod_proxy.html#cachemaxexpire">CacheMaxExpire</A> +<li><A HREF="mod_negotiation.html#cachenegotiateddocs">CacheNegotiatedDocs</A> +<li><A HREF="mod_proxy.html#cacheroot">CacheRoot</A> +<li><A HREF="mod_proxy.html#cachesize">CacheSize</A> +<li><A HREF="core.html#clearmodulelist">ClearModuleList</A> +<li><A HREF="mod_usertrack.html#cookieexpires">CookieExpires</A> +<li><A HREF="mod_cookies.html#cookielog">CookieLog</A> (mod_cookies) +<li><A HREF="mod_log_config.html#cookielog">CookieLog</A> (mod_log_config) +<li><A HREF="mod_usertrack.html#cookietracking">CookieTracking</A> +<li><A HREF="mod_log_config.html#customlog">CustomLog</A> +<li><A HREF="mod_dir.html#defaulticon">DefaultIcon</A> +<li><A HREF="core.html#defaulttype">DefaultType</A> +<li><A HREF="mod_access.html#deny">deny</A> +<li><A HREF="core.html#directory"><Directory></A> +<li><A HREF="mod_dir.html#directoryindex">DirectoryIndex</A> +<li><A HREF="core.html#documentroot">DocumentRoot</A> +<li><A HREF="core.html#errordocument">ErrorDocument</A> +<li><A HREF="core.html#errorlog">ErrorLog</A> +<li><A HREF="mod_example.html#example">Example</A> +<li><A HREF="mod_expires.html#expiresactive">ExpiresActive</A> +<li><A HREF="mod_expires.html#expiresbytype">ExpiresByType</A> +<li><A HREF="mod_expires.html#expiresdefault">ExpiresDefault</A> +<li><A HREF="mod_dir.html#fancyindexing">FancyIndexing</A> +<li><A HREF="core.html#files"><Files></A> +<li><A HREF="mod_mime.html#forcetype">ForceType</A> +<li><A HREF="core.html#group">Group</A> +<li><A HREF="mod_headers.html#header">Header</A> +<li><A HREF="mod_dir.html#headername">HeaderName</A> +<li><A HREF="core.html#hostnamelookups">HostNameLookups</A> +<li><A HREF="core.html#identitycheck">IdentityCheck</A> +<li><A HREF="core.html#ifmodule"><IfModule></A> +<li><A HREF="mod_imap.html#imapbase">ImapBase</A> +<li><A HREF="mod_imap.html#imapdefault">ImapDefault</A> +<li><A HREF="mod_imap.html#imapmenu">ImapMenu</A> +<li><A HREF="mod_dir.html#indexignore">IndexIgnore</A> +<li><A HREF="mod_dir.html#indexoptions">IndexOptions</A> +<li><A HREF="core.html#keepalive">KeepAlive</A> +<li><A HREF="core.html#keepalivetimeout">KeepAliveTimeout</A> +<li><A HREF="mod_negotiation.html#languagepriority">LanguagePriority</A> +<li><A HREF="core.html#limit"><Limit></A> +<li><A HREF="core.html#listen">Listen</A> +<li><A HREF="mod_dld.html#loadfile">LoadFile</A> +<li><A HREF="mod_dld.html#loadmodule">LoadModule</A> +<li><A HREF="core.html#location"><Location></A> +<li><A HREF="core.html#lockfile">LockFile</A> +<li><A HREF="mod_log_config.html#logformat">LogFormat</A> +<li><A HREF="core.html#maxclients">MaxClients</A> +<li><A HREF="core.html#maxkeepaliverequests">MaxKeepAliveRequests</A> +<li><A HREF="core.html#maxrequestsperchild">MaxRequestsPerChild</A> +<li><A HREF="core.html#maxspareservers">MaxSpareServers</A> +<li><A HREF="mod_cern_meta.html#metadir">MetaDir</A> +<li><A HREF="mod_cern_meta.html#metasuffix">MetaSuffix</A> +<li><A HREF="core.html#minspareservers">MinSpareServers</A> +<li><A HREF="mod_proxy.html#nocache">NoCache</A> +<li><A HREF="core.html#options">Options</A> +<li><A HREF="mod_access.html#order">order</A> +<li><A HREF="mod_env.html#passenv">PassEnv</A> +<li><A HREF="core.html#pidfile">PidFile</A> +<li><A HREF="core.html#port">Port</A> +<li><A HREF="mod_proxy.html#proxyblock">ProxyBlock</A> +<li><A HREF="mod_proxy.html#proxypass">ProxyPass</A> +<li><A HREF="mod_proxy.html#proxyremote">ProxyRemote</A> +<li><A HREF="mod_proxy.html#proxyrequests">ProxyRequests</A> +<li><A HREF="mod_dir.html#readmename">ReadmeName</A> +<li><A HREF="mod_alias.html#redirect">Redirect</A> +<li><A HREF="mod_alias.html#redirectperm">RedirectPermanent</A> +<li><A HREF="mod_alias.html#redirecttemp">RedirectTemp</A> +<li><A HREF="mod_log_referer.html#refererignore">RefererIgnore</A> +<li><A HREF="mod_log_referer.html#refererlog">RefererLog</A> +<li><A HREF="core.html#require">require</A> +<li><A HREF="core.html#resourceconfig">ResourceConfig</A> +<li><A HREF="mod_rewrite.html#RewriteBase">RewriteBase</A> +<li><A HREF="mod_rewrite.html#RewriteCond">RewriteCond</A> +<li><A HREF="mod_rewrite.html#RewriteEngine">RewriteEngine</A> +<li><A HREF="mod_rewrite.html#RewriteLog">RewriteLog</A> +<li><A HREF="mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</A> +<li><A HREF="mod_rewrite.html#RewriteMap">RewriteMap</A> +<li><A HREF="mod_rewrite.html#RewriteOptions">RewriteOptions</A> +<li><A HREF="mod_rewrite.html#RewriteRule">RewriteRule</A> +<li><A HREF="core.html#rlimitcpu">RLimitCPU</A> +<li><A HREF="core.html#rlimitmem">RLimitMEM</A> +<li><A HREF="core.html#rlimitnproc">RLimitNPROC</A> +<li><A HREF="core.html#satisfy">Satisfy</A> +<li><A HREF="core.html#scoreboardfile">ScoreBoardFile</A> +<li><A HREF="mod_actions.html#script">Script</A> +<li><A HREF="mod_alias.html#scriptalias">ScriptAlias</A> +<li><A HREF="mod_cgi.html#scriptlog">ScriptLog</A> +<li><A HREF="mod_cgi.html#scriptlogbuffer">ScriptLogBuffer</A> +<li><A HREF="mod_cgi.html#scriptloglength">ScriptLogLength</A> +<li><A HREF="core.html#sendbuffersize">SendBufferSize</A> +<li><A HREF="core.html#serveradmin">ServerAdmin</A> +<li><A HREF="core.html#serveralias">ServerAlias</A> +<li><A HREF="core.html#servername">ServerName</A> +<li><A HREF="core.html#serverpath">ServerPath</A> +<li><A HREF="core.html#serverroot">ServerRoot</A> +<li><A HREF="core.html#servertype">ServerType</A> +<li><A HREF="mod_env.html#setenv">SetEnv</A> +<li><A HREF="mod_mime.html#sethandler">SetHandler</A> +<li><A HREF="core.html#startservers">StartServers</A> +<li><A HREF="core.html#timeout">TimeOut</A> +<li><A HREF="mod_log_common.html#transferlog">TransferLog</A> (mod_log_common) +<li><A HREF="mod_log_config.html#transferlog">TransferLog</A> (mod_log_config) +<li><A HREF="mod_mime.html#typesconfig">TypesConfig</A> +<li><A HREF="mod_env.html#unsetenv">UnsetEnv</A> +<li><A HREF="core.html#user">User</A> +<li><A HREF="mod_userdir.html#userdir">UserDir</A> +<li><A HREF="core.html#virtualhost"><VirtualHost></A> +<li><A HREF="mod_include.html#xbithack">XBitHack</A> +</ul> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/footer.html b/usr.sbin/httpd/htdocs/manual/mod/footer.html new file mode 100644 index 00000000000..cbbcf9a8e2c --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/footer.html @@ -0,0 +1,7 @@ +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> diff --git a/usr.sbin/httpd/htdocs/manual/mod/header.html b/usr.sbin/httpd/htdocs/manual/mod/header.html new file mode 100644 index 00000000000..3a472e66722 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/header.html @@ -0,0 +1,6 @@ +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> diff --git a/usr.sbin/httpd/htdocs/manual/mod/index.html b/usr.sbin/httpd/htdocs/manual/mod/index.html new file mode 100644 index 00000000000..2a2aecea5a8 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/index.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache modules</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Apache modules</h1> + +<P> +Below is a list of all of the modules that come as part of the +Apache distribution. See also the complete alphabetical list of +<A + HREF="directives.html" +>all Apache directives</A>. +</P> + +<dl> +<dt><A HREF="core.html">Core</A> +<dd>Core Apache features. +<dt><A HREF="mod_access.html">mod_access</A> +<dd>Host based access control. +<dt><A HREF="mod_actions.html">mod_actions</A> Apache 1.1 and later. +<dd>Filetype/method-based script execution +<dt><A HREF="mod_alias.html">mod_alias</A> +<dd>Aliases and redirects. +<dt><A HREF="mod_asis.html">mod_asis</A> +<dd>The .asis file handler. +<dt><A HREF="mod_auth.html">mod_auth</A> +<dd>User authentication using text files. +<dt><A HREF="mod_auth_anon.html">mod_auth_anon</A> +<dd>Anonymous user authentication, FTP-style. +<dt><A HREF="mod_auth_db.html">mod_auth_db</A> +<dd>User authentication using Berkeley DB files. +<dt><A HREF="mod_auth_dbm.html">mod_auth_dbm</A> +<dd>User authentication using DBM files. +<dt><A HREF="mod_auth_msql.html">mod_auth_msql</A> +<dd>User authentication using mSQL files. +<dt><A HREF="mod_browser.html">mod_browser</A> Apache 1.2 and up +<dd>Set environment variables based on User-Agent strings +<dt><A HREF="mod_cern_meta.html">mod_cern_meta</a> +<dd>Support for HTTP header metafiles. +<dt><A HREF="mod_cgi.html">mod_cgi</A> +<dd>Invoking CGI scripts. +<dt><A HREF="mod_cookies.html">mod_cookies</A> up to Apache 1.1.1 +<dd>Support for Netscape-like cookies. Replaced in Apache 1.2 by +mod_usertrack +<dt><A HREF="mod_digest.html">mod_digest</A> +<dd>MD5 authentication +<dt><A HREF="mod_dir.html">mod_dir</A> +<dd>Automatic directory listings. +<dt><A HREF="mod_dld.html">mod_dld</A> +<dd>Start-time linking with the GNU libdld. +<dt><A HREF="mod_env.html">mod_env</A> +<dd>Passing of environments to CGI scripts +<dt><A HREF="mod_example.html">mod_example</A> Apache 1.2 and up +<dd>Demonstrates Apache API +<dt><A HREF="mod_expires.html">mod_expires</A> Apache 1.2 and up +<dd>Apply Expires: headers to resources +<dt><A HREF="mod_headers.html">mod_headers</A> Apache 1.2 and up +<dd>Add arbitrary HTTP headers to resources +<dt><A HREF="mod_imap.html">mod_imap</A> +<dd>The imagemap file handler. +<dt><A HREF="mod_include.html">mod_include</A> +<dd>Server-parsed documents. +<dt><A HREF="mod_info.html">mod_info</a> +<dd>Server configuration information +<dt><A HREF="mod_log_agent.html">mod_log_agent</A> +<dd>Logging of User Agents. +<dt><A HREF="mod_log_common.html">mod_log_common</A> up to Apache 1.1.1 +<dd>Standard logging in the Common Logfile Format. Replaced by the +mod_log_config module in Apache 1.2 and up +<dt><A HREF="mod_log_config.html">mod_log_config</A> +<dd>User-configurable logging replacement for mod_log_common. +<dt><A HREF="mod_log_referer.html">mod_log_referer</A> +<dd>Logging of document references. +<dt><A HREF="mod_mime.html">mod_mime</A> +<dd>Determining document types. +<dt><A HREF="mod_negotiation.html">mod_negotiation</A> +<dd>Content negotiation. +<dt><A HREF="mod_rewrite.html">mod_rewrite</a> Apache 1.2 and up +<dd>Powerful URI-to-filename mapping using regular expressions +<dt><A HREF="mod_proxy.html">mod_proxy</A> +<dd>Caching proxy abilities +<dt><A HREF="mod_status.html">mod_status</a> +<dd>Server status display +<dt><A HREF="mod_userdir.html">mod_userdir</A> +<dd>User home directories. +<dt><A HREF="mod_usertrack.html">mod_usertrack</A> Apache 1.2 and up +<dd>User tracking using Cookies (replacement for mod_cookies.c) +</dl> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_access.html b/usr.sbin/httpd/htdocs/manual/mod/mod_access.html new file mode 100644 index 00000000000..0787282a9db --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_access.html @@ -0,0 +1,189 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_access</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Module mod_access</h1> + +This module is contained in the <code>mod_access.c</code> file, and +is compiled in by default. It provides access control based on client +hostname or IP address. + + +<menu> +<li><A HREF="#allow">allow</A> +<li><A HREF="#allowfromenv">allow from env=</A> +<li><A HREF="#deny">deny</A> +<li><A HREF="#denyfromenv">deny from env=</A> +<li><A HREF="#order">order</A> +</menu> +<hr> + + +<A name="allow"><h2>allow</h2></A> +<!--%plaintext <?INDEX {\tt allow} directive> --> +<strong>Syntax:</strong> allow from <em>host host ...</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> Limit<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_access<p> + +The allow directive affects which hosts can access a given directory. +<em>Host</em> is one of the following: +<dl> +<dt><code>all</code> +<dd>all hosts are allowed access +<dt>A (partial) domain-name +<dd>host whose name is, or ends in, this string are allowed access. +<dt>A full IP address +<dd>An IP address of a host allowed access +<dt>A partial IP address +<dd>The first 1 to 3 bytes of an IP address, for subnet restriction. +</dl> + +Example:<blockquote><code>allow from .ncsa.uiuc.edu</code></blockquote> +All hosts in the specified domain are allowed access.<p> + +Note that this compares whole components; <code>bar.edu</code> +would not match <code>foobar.edu</code>.<p> + +See also <A HREF="#deny">deny</A>, <A HREF="#order">order</A>, and +<a href="mod_browser.html#browsermatch">BrowserMatch</a>.<p> + +<a name="allowfromenv"><strong>Syntax:</strong> allow from env=<em>variablename</em></a><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> Limit<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_access<br> +<strong>Compatibility:</strong> Apache 1.2 and above<p> + +The allow from env directive controls access to a directory by the +existence (or non-existence) of an environment variable. + +Example:<blockquote><pre> +BrowserMatch ^KnockKnock/2.0 let_me_in +<Directory /docroot> +order allow,deny +allow from env=let_me_in +deny from all +</Directory> +</pre></blockquote> + +See also <A HREF="#denyfromenv">deny from env</A> +and <A HREF="#order">order</A>.<p><hr> + +<A name="deny"><h2>deny</h2></A> +<!--%plaintext <?INDEX {\tt deny} directive> --> +<strong>Syntax:</strong> deny from <em>host host ...</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> Limit<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_access<p> + +The deny directive affects which hosts can access a given directory. +<em>Host</em> is one of the following: +<dl> +<dt><code>all</code> +<dd>all hosts are denied access +<dt>A (partial) domain-name +<dd>host whose name is, or ends in, this string are denied access. +<dt>A full IP address +<dd>An IP address of a host denied access +<dt>A partial IP address +<dd>The first 1 to 3 bytes of an IP address, for subnet restriction. +</dl> + +Example:<blockquote><code>deny from 16</code></blockquote> +All hosts in the specified network are denied access.<p> + +Note that this compares whole components; <code>bar.edu</code> +would not match <code>foobar.edu</code>.<p> + +See also <A HREF="#allow">allow</A> and <A HREF="#order">order</A>.<p> + +<a name="denyfromenv"><strong>Syntax:</strong> deny from env=<em>variablename</em></a><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> Limit<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_access<br> +<strong>Compatibility:</strong> Apache 1.2 and above<p> + +The deny from env directive controls access to a directory by the +existence (or non-existence) of an environment variable. + +Example:<blockquote><pre> +BrowserMatch ^BadRobot/0.9 go_away +<Directory /docroot> +order deny,allow +deny from env=go_away +allow from all +</Directory> +</pre></blockquote> + +See also <A HREF="#allowfromenv">allow from env</A> +and <A HREF="#order">order</A>.<p><hr> + +<A name="order"><h2>order</h2></A> +<!--%plaintext <?INDEX {\tt order} directive> --> +<strong>Syntax:</strong> order <em>ordering</em><br> +<strong>Default:</strong> <code>order deny,allow</code><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> Limit<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_access<p> + +The order directive controls the order in which <A HREF="#allow">allow</A> and +<A HREF="#deny">deny</A> directives are evaluated. <em>Ordering</em> is one +of +<dl> +<dt>deny,allow +<dd>the deny directives are evaluated before the allow directives. (The +initial state is OK.) +<dt>allow,deny +<dd>the allow directives are evaluated before the deny directives. (The +initial state is FORBIDDEN.) +<dt>mutual-failure +<dd>Only those hosts which appear on the allow list and do not appear +on the deny list are granted access. (The initial state is irrelevant.) +</dl> + +Note that in all cases every <code>allow</code> and <code>deny</code> +statement is evaluated, there is no "short-circuiting". + +<p>Example: +<blockquote><code> +order deny,allow<br> +deny from all<br> +allow from .ncsa.uiuc.edu<br> +</code></blockquote> +Hosts in the ncsa.uiuc.edu domain are allowed access; all other hosts are +denied access. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_actions.html b/usr.sbin/httpd/htdocs/manual/mod/mod_actions.html new file mode 100644 index 00000000000..686760abf26 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_actions.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Module mod_actions</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_actions</h1> + +This module is contained in the <code>mod_actions.c</code> file, and +is compiled in by default. It provides for +executing CGI scripts based on media type or request method. It is not +present in versions prior to Apache 1.1. + +<h2>Summary</h2> + +This module lets you run CGI scripts whenever a file of a certain type +is requested. This makes it much easier to execute scripts that +process files. + +<h2>Directives</h2> +<ul> +<li><A HREF="#action">Action</A> +<li><A HREF="#script">Script</A> +</ul> + +<hr> + +<A name="action"><h2>Action</h2></A> +<strong>Syntax:</strong> Action <em>mime-type cgi-script</em><br> +<strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Override:</strong> FileInfo<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_actions<br> +<strong>Compatibility:</strong> Action is only available in Apache 1.1 +and later<p> + +This directive adds an action, which will activate <em>cgi-script</em> when +a file of content type <em>mime-type</em> is requested. It sends the +URL and file path of the requested document using the standard +CGI PATH_INFO and PATH_TRANSLATED environment variables. + +<hr> + +<A name="script"><h2>Script</h2></A> +<strong>Syntax:</strong> Script <em>method cgi-script</em><br> +<strong>Context:</strong> server config, virtual host, directory<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_actions<br> +<strong>Compatibility:</strong> Script is only available in Apache 1.1 +and later<p> + +<p>This directive adds an action, which will activate <em>cgi-script</em> when +a file is requested using the method of <em>method</em>, which can be +one of <code>GET</code>, <code>POST</code>, <code>PUT</code> or +<code>DELETE</code>. It sends the +URL and file path of the requested document using the standard +CGI PATH_INFO and PATH_TRANSLATED environment variables. + +<p>Note that the Script command defines default actions only. If a CGI +script is called, or some other resource that is capable of handling +the requested method internally, it will do so. Also note that script +with a method of <code>GET</code> will only be called if there are +query arguments present (e.g. foo.html?hi). Otherwise, the request +will proceed normally. + +<p>Examples: +<pre> + Script GET /cgi-bin/search #e.g. for <ISINDEX>-style searching + Script PUT /~bob/put.cgi + +</pre> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_alias.html b/usr.sbin/httpd/htdocs/manual/mod/mod_alias.html new file mode 100644 index 00000000000..247cdc35ade --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_alias.html @@ -0,0 +1,159 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_alias</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Module mod_alias</h1> + +This module is contained in the <code>mod_alias.c</code> file, and +is compiled in by default. It provides for mapping different parts of the +host filesystem in the the document tree, and for URL redirection. + + +<menu> +<li><A HREF="#alias">Alias</A> +<li><A HREF="#redirect">Redirect</A> +<li><A HREF="#redirecttemp">RedirectTemp</A> +<li><A HREF="#redirectperm">RedirectPermanent</A> +<li><A HREF="#scriptalias">ScriptAlias</A> +</menu> +<hr> + + +<A name="alias"><h2>Alias</h2></A> +<!--%plaintext <?INDEX {\tt Alias} directive> --> +<strong>Syntax:</strong> Alias <em>url-path directory-filename</em><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_alias<br> + +The Alias directive allows documents to be stored in the local filesystem +other than under the <A HREF="core.html#documentroot">DocumentRoot</A>. +URLs with a (%-decoded) path beginning with <em>url-path</em> will be +mapped to local files beginning with <em>directory-filename</em>. +Example: +<blockquote><code>Alias /image /ftp/pub/image</code></blockquote> +A request for http://myserver/image/foo.gif would cause the server to +return the file /ftp/pub/image/foo.gif.<p> + +Note that if you include a trailing / on the <em>url-path</em> then the +server will require a trailing / in order to expand the alias. That is, +if you use <code>Alias /icons/ /usr/local/etc/httpd/icons/</code> then +the url <code>/icons</code> will not be aliased.<p> + +See also <A HREF="#scriptalias">ScriptAlias</A>.<p><hr> + +<A name="redirect"><h2>Redirect</h2></A> +<!--%plaintext <?INDEX {\tt Redirect} directive> --> +<strong>Syntax:</strong> Redirect [ <em>status</em> ] <em>url-path url</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_alias<br> +<strong>Compatibility:</strong> The directory and .htaccess context's +are only available in versions 1.1 and later. The <em>status</em> argument is only available in Apache 1.2 or later.<p> + +The Redirect directive maps an old URL into a new one. The new URL is returned +to the client which attempts to fetch it again with the new address. +<em>Url-path</em> a (%-decoded) path; any requests for documents beginning with +this path will be returned a redirect error to a new (%-encoded) url +beginning with <em>url</em>. Example: +<blockquote><code>Redirect /service +http://foo2.bar.com/service</code></blockquote> +If the client requests http://myserver/service/foo.txt, it will be told to +access http://foo2.bar.com/service/foo.txt instead.<p> + +Note: Redirect directives take precedence over Alias and ScriptAlias +directives, irrespective of their ordering in the configuration file. Also, +<em>Url-path</em> must be an absolute path, not a relative path, even when used with +.htaccess files or inside of <Directory> sections.<p> + +If no <em>status</em> argument is given, the redirect will be +"temporary" (HTTP status 302). This indicates to the client that the +resources is has moved temporarily. The <em>status</em> +argument can be used to return other HTTP status codes: +<dl> +<dt>permanent<dd>Returns a permanent redirect status (301) indicating that +the resource has moved permanently. +<dt>temp<dd>Returns a temporary redirect status (302). This is the +default. +<dt>seeother<dd>Returns a "See Other" status (303) indicating that +the resource has been replaced. +<dt>gone<dd>Returns a "Gone" status (410) indicating that the resource +has been permanently removed. When this status is used the <em>url</em> +argument should be omitted. +</dl> + +Other status codes can be returned by giving the numeric status code +as the value of <em>status</em>. If the status is between 300 and 399, +the <em>url</em> argument must be present, otherwise it must be +omitted. Note that the status must be known to the Apache code (see +the function <code>send_error_response</code> in http_protocol.c). + +<A name="redirecttemp"><h2>RedirectTemp</h2></A> +<!--%plaintext <?INDEX {\tt Redirect} directive> --> +<strong>Syntax:</strong> RedirectTemp <em>url-path url</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_alias<br> +<strong>Compatibility:</strong> This directive is only available in 1.2<P> + +This directive makes the client know that the Redirect is only +temporary. (Status 302). Exactly equivalent to <code>Redirect temporary </code><P> + +<A name="redirectperm"><h2>RedirectPermanent</h2></A> +<!--%plaintext <?INDEX {\tt Redirect} directive> --> +<strong>Syntax:</strong> RedirectPermanent <em>url-path url</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_alias<br> +<strong>Compatibility:</strong> This directive is only available in 1.2<P> + +This directive makes the client know that the Redirect is permanent. +(Status 301). Exactly equivalent to <code>Redirect permanent</code><P> + +<hr> +<A name="scriptalias"><h2>ScriptAlias</h2></A> +<!--%plaintext <?INDEX {\tt ScriptAlias} directive> --> +<strong>Syntax:</strong> ScriptAlias <em>url-path directory-filename</em><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_alias<br> + +The ScriptAlias directive has the same behavior as the +<A HREF="#alias">Alias</A> directive, except that in addition it +marks the target directory as containing CGI scripts. +URLs with a (%-decoded) path beginning with <em>url-path</em> will be +mapped to scripts beginning with <em>directory-filename</em>. +Example: +<blockquote><code>ScriptAlias /cgi-bin/ /web/cgi-bin/</code></blockquote> +A request for http://myserver/cgi-bin/foo would cause the server to +run the script /web/cgi-bin/foo.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_asis.html b/usr.sbin/httpd/htdocs/manual/mod/mod_asis.html new file mode 100644 index 00000000000..59a164c7b4b --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_asis.html @@ -0,0 +1,81 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_asis</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Module mod_asis</h1> + +This module is contained in the <code>mod_asis.c</code> file, and +is compiled in by default. It provides for <code>.asis</code> files. Any +document with mime type <code>httpd/send-as-is</code> will be processed by +this module. +<!--%plaintext <?INDEX {\tt httpd/send-as-is} mime type> --> + +<h2>Purpose</h2> +To allow file types to be defined such that Apache sends them without +adding HTTP headers.<P> + +This can be used to send any kind of data from the server, including redirects +and other special HTTP responses, without requiring a cgi-script or an nph +script. +<h2>Usage</h2> +In the server configuration file, define a new mime type called +<code>httpd/send-as-is</code> e.g. +<blockquote><code>AddType httpd/send-as-is asis</code></blockquote> +this defines the <code>.asis</code> file extension as being of the new +<code>httpd/send-as-is</code> mime type. The contents of any file with a +<code>.asis</code> extension will then be sent by Apache to the client with +almost no changes. Clients will need HTTP headers to be attached, so do not +forget them. A Status: header is also required; the data should be the +3-digit HTTP response code, followed by a textual message.<p> + +Here's an example of a file whose contents are sent <em>as is</em> so as to +tell the client that a file has redirected. +<blockquote><code> +Status: 302 Now where did I leave that URL <br> +Location: http://xyz.abc.com/foo/bar.html <br> +Content-type: text/html <br> +<br> +<HTML> <br> +<HEAD> <br> +<TITLE>Lame excuses'R'us</TITLE> <br> +</HEAD> <br> +<BODY> <br> +<H1>Fred's exceptionally wonderful page has moved to <br> +<A HREF="http://xyz.abc.com/foo/bar.html">Joe's</A> site. <br> +</H1> <br> +</BODY> <br> +</HTML> +</code></blockquote> +Notes: the server always adds a Date: and Server: header to the data returned +to the client, so these should not be included in the file. +The server does <em>not</em> add a Last-Modified header; it probably should. +<P> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_auth.html b/usr.sbin/httpd/htdocs/manual/mod/mod_auth.html new file mode 100644 index 00000000000..9fa7c47159f --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_auth.html @@ -0,0 +1,158 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_auth</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Module mod_auth</h1> + +This module is contained in the <code>mod_auth.c</code> file, and +is compiled in by default. It provides for user authentication using +textual files. + + +<menu> +<li><A HREF="#authgroupfile">AuthGroupFile</A> +<li><A HREF="#authuserfile">AuthUserFile</A> +<li><A HREF="#authauthoritative">AuthAuthoritative</A> +</menu> +<hr> + + +<A name="authgroupfile"><h2>AuthGroupFile</h2></A> +<!--%plaintext <?INDEX {\tt AuthGroupFile} directive> --> +<strong>Syntax:</strong> AuthGroupFile <em>filename</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_auth<p> + +The AuthGroupFile directive sets the name of a textual file containing the list +of user groups for user authentication. <em>Filename</em> is the absolute path +to the group file.<p> +Each line of the group file contains a groupname followed by a colon, followed +by the member usernames separated by spaces. Example: +<blockquote><code>mygroup: bob joe anne</code></blockquote> +Note that searching large groups files is <em>very</em> inefficient; +<A HREF="mod_auth_dbm.html#authdbmgroupfile">AuthDBMGroupFile</A> should +be used instead.<p> + +Security: make sure that the AuthGroupFile is stored outside the +document tree of the web-server; do <em>not</em> put it in the directory that +it protects. Otherwise, clients will be able to download the AuthGroupFile.<p> + +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authuserfile">AuthUserFile</A>.<p><hr> + +<A name="authuserfile"><h2>AuthUserFile</h2></A> +<!--%plaintext <?INDEX {\tt AuthUserFile} directive> --> +<strong>Syntax:</strong> AuthUserFile <em>filename</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_auth<p> + +The AuthUserFile directive sets the name of a textual file containing +the list of users and passwords for user +authentication. <em>Filename</em> is the absolute path to the user +file.<p> Each line of the user file file contains a username followed +by a colon, followed by the crypt() encrypted password. The behavior +of multiple occurrences of the same user is undefined.<p> Note that +searching user groups files is inefficient; <A +HREF="mod_auth_dbm.html#authdbmuserfile">AuthDBMUserFile</A> should be +used instead.<p> + +Security: make sure that the AuthUserFile is stored outside the +document tree of the web-server; do <em>not</em> put it in the directory that +it protects. Otherwise, clients will be able to download the AuthUserFile.<p> + +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authgroupfile">AuthGroupFile</A>.<p> +<hr> +<A name="authauthoritative"><h2>AuthAuthoritative</h2></A> +<!--%plaintext <?INDEX {\tt AuthAuthoritative} directive> --> +<strong>Syntax:</strong> AuthAuthoritative < <strong> on</strong>(default) | off > <br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_auth<p> + +Setting the AuthAuthoritative directive explicitly to <b>'off'</b> +allows for both authentication and authorization to be passed on to +lower level modules (as defined in the <code>Configuration</code> and +<code>modules.c</code> files) if there is <b>no userID</b> or +<b>rule</b> matching the supplied userID. If there is a userID and/or +rule specified; the usual password and access checks will be applied +and a failure will give an Authorization Required reply. + +<p> + +So if a userID appears in the database of more than one module; or if +a valid require directive applies to more than one module; then the +first module will verify the credentials; and no access is passed on; +regardless of the AuthAuthoritative setting. + +<p> + +A common use for this is in conjunction with one of the database +modules; such as <a +href="mod_auth_db.html"><code>mod_auth_db.c</code></a>, <a +href="mod_auth_dbm.html"><code>mod_auth_dbm.c</code></a>, <a +href="mod_auth_msql.html"><code>mod_auth_msql.c</code></a> and <a +href="mod_auth_anon.html"><code>mod_auth_anon.c</code></a>. These modules +supply the bulk of the user credential checking; but a few +(administrator) related accesses fall through to a lower level with a +well protected AuthUserFile. + +<p> + +<b>Default:</b> By default; control is not passed on; and an unknown +userID or rule will result in an Authorization Required reply. Not +setting it thus keeps the system secure; and forces an NSCA compliant +behaviour. + +<p> + +Security: Do consider the implications of allowing a user to allow +fall-through in his .htaccess file; and verify that this is really +what you want; Generally it is easier to just secure a single +.htpasswd file, than it is to secure a database such as mSQL. Make +sure that the AuthUserFile is stored outside the document tree of the +web-server; do <em>not</em> put it in the directory that it +protects. Otherwise, clients will be able to download the +AuthUserFile. + +<p> +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authgroupfile">AuthGroupFile</A>.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_auth_anon.html b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_anon.html new file mode 100644 index 00000000000..b9ee13bcba6 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_anon.html @@ -0,0 +1,262 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_auth_anon.c</TITLE> +</HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_auth_anon</H1> + +This module is contained in the <code>mod_auth_anon.c</code> file and +is not compiled in by default. It is only available in Apache 1.1 and +later. It allows "anonymous" user access to authenticated areas. + +<h2>Summary</h2> + +It does access control in a manner similar to anonymous-ftp sites; i.e. +have a 'magic' user id 'anonymous' and the email address as a password. +These email addresses can be logged. +<p> +Combined with other (database) access control methods, this allows for +effective user tracking and customization according to a user profile +while still keeping the site open for 'unregistered' users. One advantage +of using Auth-based user tracking is that, unlike magic-cookies and +funny URL pre/postfixes, it is completely browser independent and it +allows users to share URLs. +<p> + +<a href="#Directives">Directives</a> / +<a href="#Example">Example</a> / +<a href="#CompileTimeOptions">Compile time options</a> / +<a href="#RevisionHistory">RevisionHistory</a> / +<a href="#Person">Person to blame</a> / +<a href="#Sourcecode">Sourcecode</a> +<p> + +<h2><a name="Directives">Directives</a></h2> +<ul> +<li><A HREF="#anonymous">Anonymous</A> +<li><A HREF="#Authoritative">Anonymous_Authoritative</A> +<li><A HREF="#LogEmail">Anonymous_LogEmail</A> +<li><A HREF="#MustGiveEmail">Anonymous_MustGiveEmail</A> +<li><A HREF="#NoUserID">Anonymous_NoUserID</A> +<li><A HREF="#VerifyEmail">Anonymous_VerifyEmail</A> +</ul> + +<hr> + +<A name="anonymous"><h2>Anonymous</h2></a> +<!--%plaintext <?INDEX {\tt Anonymous} directive> --> +<strong>Syntax:</strong> Anonymous <em>user user ...</em><br> +<strong>Default:</strong> none<br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_anon<p> + + A list of one or more 'magic' userIDs which are allowed access + without password verification. The userIDs are space separated. + It is possible to use the ' and " quotes to allow a space in + a userID as well as the \ escape character. + <p> + Please note that the comparison is <b>case-IN-sensitive</b>. + <br> + I strongly suggest that the magic username '<code>anonymous</code>' + is always one of the allowed userIDs. + <p> + Example:<br> + <code> + Anonymous: anonymous "Not Registered" 'I don\'t know' + </code><p> + This would allow the user to enter without password verification + by using the userId's 'anonymous', 'AnonyMous','Not Registered' and + 'I Don't Know'. +<HR> + +<A name="Authoritative"><h2>Anonymous_Authoritative</h2></A> +<strong>Syntax:</strong> Anonymous_Authoritative <em>on | off</em><br> +<strong>Default:</strong> <code>Anonymous_Authoritative off</code><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_anon<p> + + When set 'on', there is no + fall-through to other authorization methods. So if a + userID does not match the values specified in the + <code>Anonymous</code> directive, access is denied. + <p> + Be sure you know what you are doing when you decide to switch + it on. And remember that it is the linking order of the modules + (in the Configuration / Make file) which details the order + in which the Authorization modules are queried. +<hr> + +<A name="LogEmail"><h2>Anonymous_LogEmail</h2></A> +<strong>Syntax:</strong> Anonymous_LogEmail <em>on | off</em><br> +<strong>Default:</strong> <code>off</code><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_anon<p> + + When set 'on', the default, the 'password' entered (which hopefully + contains a sensible email address) is logged in the httpd-log file. +<hr> + +<A name="MustGiveEmail"><h2>Anonymous_MustGiveEmail</h2></a> +<!--%plaintext <?INDEX {\tt Anonymous_MustGiveEmail} directive> --> +<strong>Syntax:</strong> Anonymous_MustGiveEmail <em>on</em> | <em>off</em><br> +<strong>Default:</strong> off<br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_anon<p> + + Specifies whether the user must specify an email + address as the password. This prohibits blank passwords. +<HR> + +<A name="NoUserID"><h2>Anonymous_NoUserID</h2></A> +<strong>Syntax:</strong> Anonymous_NoUserID <em>on | off</em><br> +<strong>Default:</strong> <code>Anonymous_NoUserID off</code><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_anon<p> + + When set 'on', users can leave + the userID (and perhaps the password field) empty. This + can be very convenient for MS-Explorer users who can + just hit return or click directly on the OK button; which + seems a natural reaction. + +<hr> + +<A name="VerifyEmail"><h2>Anonymous_VerifyEmail</h2></A> +<strong>Syntax:</strong> Anonymous <em>on | off</em><br> +<strong>Default:</strong> <code>Anonymous_VerifyEmail off</code><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_anon<p> + + When set 'on' the 'password' entered is + checked for at least one '@' and a '.' to encourage users to enter + valid email addresses (see the above <code>Auth_LogEmail</code>). + +<hr><a name="Example"><h2>Example</h2></a> + +The example below (when combined with the Auth directives +of a htpasswd-file based (or GDM, mSQL etc) base access +control system allows users in as 'guests' with the +following properties: +<ul> +<li> +It insists that the user enters a userId. (<code>Anonymous_NoUserId</code>) +<li> +It insists that the user enters a password. (<code>Anonymous_MustGiveEmail</code>) +<li> +The password entered must be a valid email address, ie. contain at least one '@' and a '.'. +(<code>Anonymous_VerifyEmail</code>) +<li> +The userID must be one of <code>anonymous guest www test welcome</code> +and comparison is <b>not</b> case sensitive. +<code><directory /web/docs/public></code> +<li> +And the Email addresses entered in the passswd field are logged to +the httpd-log file +(<code>Anonymous_LogEmail</code>) +</ul> +<p> +Excerpt of access.conf: +<dl> +<dt><code> +Anonymous anonymous guest www test welcome<p> +Anonymous_MustGiveEmail on<br> +Anonymous_VerifyEmail on<br> +Anonymous_NoUserId off<br> +Anonymous_LogEmail on<br> +<p> +AuthName Use 'anonymous' & Email address for guest entry<br> +AuthType basic<p> + +</code></dt> +<dd> + Normal Apache/NCSA tokens for access control + <p> + <code><limit get post head></code><br> + <code>order deny,allow </code><br> + <code>allow from all </code><br> + <p> + <code>require valid-user </code><br> + <code><limit> </code><br> +</dd> +</dl> + + +<hr><h2><a name="CompileTimeOptions">Compile Time Options</a></h2> + +Currently there are no Compile options. + +<hr><h2><a name="RevisionHistory">Revision History</a></h2> + +This version: 23 Nov 1995, 24 Feb 1996, 16 May 1996. + +<dl> + +<dt>Version 0.4<br></dt> + <dd>First release + </dd> +<dt>Version 0.5<br></dt> + <dd>Added 'VerifyEmail' and 'LogEmail' options. Multiple + 'anonymous' tokens allowed. more docs. Added Authoritative + functionality. + </dd> +</dl> + + +<hr><h2><a name="Person">Contact/person to blame</a></h2> + +This module was written for the +<a href="http://ewse.ceo.org">European Wide Service Exchange</a> by +<<a href="mailto:Dirk.vanGulik@jrc.it"><code>Dirk.vanGulik@jrc.it</code></a>>. +Feel free to contact me if you have any problems, ice-creams or bugs. This +documentation, courtesy of Nick Himba, <a href="mailto:himba@cs.utwente.nl"> +<code><himba@cs.utwente.nl></code></a>. +<p> + + +<hr><h2><a NAME="Sourcecode">Sourcecode</a></h2> + +The source code can be found at <a href="http://www.apache.org"><code> +http://www.apache.org</code></a>. A snapshot of a development version +usually resides at <a href="http://me-www.jrc.it/~dirkx/mod_auth_anon.c"><code> +http://me-www.jrc.it/~dirkx/mod_auth_anon.c</code></a>. Please make sure +that you always quote the version you use when filing a bug report. +<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</body> +</html> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_auth_db.html b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_db.html new file mode 100644 index 00000000000..2140aa4bcc0 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_db.html @@ -0,0 +1,173 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_auth_db</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_auth_db</h1> + +This module is contained in the <code>mod_auth_db.c</code> file, and +is not compiled in by default. It provides for user authentication using +Berkeley DB files. It is an alternative to <A HREF="mod_auth_dbm.html">DBM</A> +files for those systems which support DB and not DBM. It is only +available in Apache 1.1 and later. + + +<menu> +<li><A HREF="#authdbgroupfile">AuthDBGroupFile</A> +<li><A HREF="#authdbuserfile">AuthDBUserFile</A> +<li><A HREF="#authdbauthoritative">AuthDBAuthoritative</A> +</menu> +<hr> + + +<A name="authdbgroupfile"><h2>AuthDBGroupFile</h2></A> +<!--%plaintext <?INDEX {\tt AuthDBGroupFile} directive> --> +<strong>Syntax:</strong> AuthDBGroupFile <em>filename</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_db<p> + +The AuthDBGroupFile directive sets the name of a DB file containing the list +of user groups for user authentication. <em>Filename</em> is the absolute path +to the group file.<p> + +The group file is keyed on the username. The value for a user is a +comma-separated list of the groups to which the users belongs. There must +be no whitespace within the value, and it must never contain any colons.<p> + +Security: make sure that the AuthDBGroupFile is stored outside the +document tree of the web-server; do <em>not</em> put it in the directory that +it protects. Otherwise, clients will be able to download the +AuthDBGroupFile unless otherwise protected.<p> + +Combining Group and Password DB files: In some cases it is easier to +manage a single database which contains both the password and group +details for each user. This simplifies any support programs that need +to be written: they now only have to deal with writing to and locking +a single DBM file. This can be accomplished by first setting the group +and password files to point to the same DB file:<p> + +<blockquote><code> +AuthDBGroupFile /www/userbase<br> +AuthDBUserFile /www/userbase +</code></blockquote> + +The key for the single DB record is the username. The value consists of <p> + +<blockquote><code> +Unix Crypt-ed Password : List of Groups [ : (ignored) ] +</code></blockquote> + +The password section contains the Unix crypt() password as before. This is +followed by a colon and the comma separated list of groups. Other data may +optionally be left in the DB file after another colon; it is ignored by the +authentication module. <p> + +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authdbuserfile">AuthDBUserFile</A>.<p><hr> + +<A name="authdbuserfile"><h2>AuthDBUserFile</h2></A> +<!--%plaintext <?INDEX {\tt AuthDBUserFile} directive> --> +<strong>Syntax:</strong> AuthDBUserFile <em>filename</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_db<p> + +The AuthDBUserFile directive sets the name of a DB file containing the list +of users and passwords for user authentication. <em>Filename</em> is the +absolute path to the user file.<p> + +The user file is keyed on the username. The value for a user is the +crypt() encrypted password, optionally followed by a colon and +arbitrary data. The colon and the data following it will be ignored +by the server.<p> + +Security: make sure that the AuthDBUserFile is stored outside the +document tree of the web-server; do <em>not</em> put it in the directory that +it protects. Otherwise, clients will be able to download the +AuthDBUserFile.<p> + +Important compatibility note: The implementation of "dbmopen" in the +apache modules reads the string length of the hashed values from the +DB data structures, rather than relying upon the string being +NULL-appended. Some applications, such as the Netscape web server, +rely upon the string being NULL-appended, so if you are having trouble +using DB files interchangeably between applications this may be a +part of the problem. <p> + +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authdbgroupfile">AuthDBGroupFile</A>.<p> +<hr> +<A name="authdbauthoritative"><h2>AuthDBAuthoritative</h2></A> +<!--%plaintext <?INDEX {\tt AuthDBAuthoritative} directive> --> +<strong>Syntax:</strong> AuthDBAuthoritative < <strong> on</strong>(default) | off > <br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_auth<p> + +Setting the AuthDBAuthoritative directive explicitly to <b>'off'</b> +allows for both authentication and authorization to be passed on +to lower level modules (as defined in the <code>Configuration</code> +and <code>modules.c</code> file if there is <b>no userID</b> or +<b>rule</b> matching the supplied userID. If there is a userID +and/or rule specified; the usual password and access checks will +be applied and a failure will give an Authorization Required reply. +<p> +So if a userID appears in the database of more than one module; or +if a valid require directive applies to more than one module; then +the first module will verify the credentials; and no access is +passed on; regardless of the AuthAuthoritative setting. <p> + +A common use for this is in conjunction with one of the basic auth +modules; such as <a href="mod_auth.html"><code>mod_auth.c</code></a>. +Whereas this DB module supplies the bulk of the user credential +checking; a few (administrator) related accesses fall through to +a lower level with a well protected .htpasswd file. <p> + +<b>Default:</b> By default; control is not passed on; and an unknown +userID or rule will result in an Authorization Required reply. Not +setting it thus keeps the system secure; and forces an NSCA compliant +behaviour. <p> +Security: Do consider the implications of allowing a user to allow +fall-through in his .htaccess file; and verify that this is really +what you want; Generally it is easier to just secure a single +.htpasswd file, than it is to secure a database which might have +more access interfaces. + +<p> +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authdbgroupfile">AuthDBGroupFile</A>.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_auth_dbm.html b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_dbm.html new file mode 100644 index 00000000000..7098d3ead3e --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_dbm.html @@ -0,0 +1,175 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_auth_dbm</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Module mod_auth_dbm</h1> + +This module is contained in the <code>mod_auth_dbm.c</code> file, and +is not compiled in by default. It provides for user authentication using +DBM files. + + +<menu> +<li><A HREF="#authdbmgroupfile">AuthDBMGroupFile</A> +<li><A HREF="#authdbmuserfile">AuthDBMUserFile</A> +<li><A HREF="#authdbmauthoritative">AuthDBMAuthoritative</A> +</menu> +<hr> + + +<A name="authdbmgroupfile"><h2>AuthDbmGroupFile</h2></A> +<!--%plaintext <?INDEX {\tt AuthDbmGroupFile} directive> --> +<strong>Syntax:</strong> AuthDBMGroupFile <em>filename</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_dbm<p> + +The AuthDBMGroupFile directive sets the name of a DBM file containing the list +of user groups for user authentication. <em>Filename</em> is the absolute path +to the group file.<p> + +The group file is keyed on the username. The value for a user is a +comma-separated list of the groups to which the users belongs. There must +be no whitespace within the value, and it must never contain any colons.<p> + +Security: make sure that the AuthDBMGroupFile is stored outside the +document tree of the web-server; do <em>not</em> put it in the directory that +it protects. Otherwise, clients will be able to download the +AuthDBMGroupFile unless otherwise protected.<p> + +Combining Group and Password DBM files: In some cases it is easier to +manage a single database which contains both the password and group +details for each user. This simplifies any support programs that need +to be written: they now only have to deal with writing to and locking +a single DBM file. This can be accomplished by first setting the group +and password files to point to the same DBM:<p> + +<blockquote><code> +AuthDBMGroupFile /www/userbase<br> +AuthDBMUserFile /www/userbase +</code></blockquote> + +The key for the single DBM is the username. The value consists of <p> + +<blockquote><code> +Unix Crypt-ed Password : List of Groups [ : (ignored) ] +</code></blockquote> + +The password section contains the Unix crypt() password as before. This is +followed by a colon and the comma separated list of groups. Other data may +optionally be left in the DBM file after another colon; it is ignored by the +authentication module. This is what www.telescope.org uses for its combined +password and group database. <p> + +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authdbmuserfile">AuthDBMUserFile</A>.<p><hr> + +<A name="authdbmuserfile"><h2>AuthDBMUserFile</h2></A> +<!--%plaintext <?INDEX {\tt AuthDBMUserFile} directive> --> +<strong>Syntax:</strong> AuthDBMUserFile <em>filename</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_auth_dbm<p> + +The AuthDBMUserFile directive sets the name of a DBM file containing the list +of users and passwords for user authentication. <em>Filename</em> is the +absolute path to the user file.<p> + +The user file is keyed on the username. The value for a user is the +crypt() encrypted password, optionally followed by a colon and +arbitrary data. The colon and the data following it will be ignored +by the server.<p> + +Security: make sure that the AuthDBMUserFile is stored outside the +document tree of the web-server; do <em>not</em> put it in the directory that +it protects. Otherwise, clients will be able to download the +AuthDBMUserFile.<p> + +Important compatibility note: The implementation of "dbmopen" in the +apache modules reads the string length of the hashed values from the +DBM data structures, rather than relying upon the string being +NULL-appended. Some applications, such as the Netscape web server, +rely upon the string being NULL-appended, so if you are having trouble +using DBM files interchangeably between applications this may be a +part of the problem. <p> + +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authdbmgroupfile">AuthDBMGroupFile</A>.<p> + +<hr> +<A name="authdbmauthoritative"><h2>AuthDBMAuthoritative</h2></A> +<!--%plaintext <?INDEX {\tt AuthDBMAuthoritative} directive> --> +<strong>Syntax:</strong> AuthDBMAuthoritative < <strong> on</strong>(default) | off > <br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_auth<p> + +Setting the AuthDBMAuthoritative directive explicitly to <b>'off'</b> +allows for both authentication and authorization to be passed on +to lower level modules (as defined in the <code>Configuration</code> +and <code>modules.c</code> file if there is <b>no userID</b> or +<b>rule</b> matching the supplied userID. If there is a userID +and/or rule specified; the usual password and access checks will +be applied and a failure will give an Authorization Required reply. +<p> +So if a userID appears in the database of more than one module; or +if a valid require directive applies to more than one module; then +the first module will verify the credentials; and no access is +passed on; regardless of the AuthAuthoritative setting. <p> + +A common use for this is in conjunction with one of the basic auth +modules; such as <a href="mod_auth.html"><code>mod_auth.c</code></a>. +Whereas this DBM module supplies the bulk of the user credential +checking; a few (administrator) related accesses fall through to +a lower level with a well protected .htpasswd file. <p> + +<b>Default:</b> By default; control is not passed on; and an unknown +userID or rule will result in an Authorization Required reply. Not +setting it thus keeps the system secure; and forces an NSCA compliant +behaviour. <p> + +Security: Do consider the implications of allowing a user to allow +fall-through in his .htaccess file; and verify that this is really +what you want; Generally it is easier to just secure a single +.htpasswd file, than it is to secure a database which might have +more access interfaces. + +<p> +See also <A HREF="core.html#authname">AuthName</A>, +<A HREF="core.html#authtype">AuthType</A> and +<A HREF="#authdbmgroupfile">AuthDBMGroupFile</A>.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_auth_msql.html b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_msql.html new file mode 100644 index 00000000000..9e85f5d2cdd --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_auth_msql.html @@ -0,0 +1,488 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Module mod_auth_msql</title> +</head> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_auth_msql</H1> + +This module is contained in the <code>mod_auth_msql.c</code> file and +is compiled in by default. It allows access control using the public +domain mSQL database <code><a +href="ftp://ftp.bond.edu.au/pub/Minerva/msql">ftp://ftp.bond.edu.au/pub/Minerva/msql</a></code>, +a fast but limited SQL engine which can be contacted over an internal +Unix domain protocol as well as over normal TCP/IP socket +communication. It is only available in Apache 1.1 and later. <p> + +<a href="#FullDescription">Full description</a> / +<a href="#Example">Example</a> / +<a href="#CompileTimeOptions">Compile time options</a> / +<a href="#RevisionHistory">RevisionHistory</a> / +<a href="#Person">Person to blame</a> / +<a href="#Sourcecode">Sourcecode</a> +<p> + +<hr><h2><a name="FullDescription">Full description of all tokens</a></h2> +<dl> + +<code><dt> +Auth_MSQLhost < FQHN | IP Address | localhost > +</dt></code><dd> + Hostname of the machine running the mSQL demon. The effective uid + of the server should be allowed access. If not given, or if it is + the magic name <code>localhost</code>, it is passed to the mSQL library as a null + pointer. This effectively forces it to use /dev/msql rather than the + (slower) socket communication. +</dd> + +<code><dt> +Auth_MSQLdatabase < mSQL database name > +</dt></code><dd> + Name of the database in which the following table(s) are contained (Quick check: use the + mSQL command <code>relshow [<hostname> dbase]</code> to verify the spelling of the + database name). +</dd> + +<code><dt> +Auth_MSQLpwd_table < mSQL table name > +</dt></code><dd> + Contains at least the fields with the username + and the (encrypted) password. Each uid should only occur once in this table and + for performance reasons should be a primary key. + Normally this table is compulsory, but it is + possible to use a fall-through to other methods + and use the mSQL module for group control only. + See the <a href="#Authoritative"><code>Auth_MSQL_Authoritative</code></a> + directive below. +</dd> + +<code><dt> +Auth_MSQLgrp_table < mSQL table name in the above database > +</dt></code><dd> + Contains at least the fields with the + username and the groupname. A user which + is in multiple groups has therefore + multiple entries. There might be some performance + problems associated with this and one + might consider to have separate tables for each + group (rather than all groups in one table) if + your directory structure allows for it. + One only need to specify this table when doing + group control. +</dd> + +<code><dt> +Auth_MSQLuid_field < mSQL field name > +</dt></code><dd> + Name of the field containing the username in the <code> + Auth_MSQLpwd_table</code> and optionally in the <code> + Auth_MSQLgrp_table</code> tables. +</dd> + +<code><dt> +Auth_MSQLpwd_field < mSQL field name > +</dt></code><dd> + Fieldname for the passwords in the <code> + Auth_MSQLpwd_table</code> table. +</dd> + +<code><dt> +Auth_MSQLgrp_field < mSQL field name > +</dt></code><dd> + Fieldname for the groupname<br> + Only the fields used need to be specified. When this + module is compiled with the + <a href="#VITEK"><code>BACKWARD_VITEK</code></a> option then + the uid and pwd field names default to 'user' and 'password'. + However you are strongly encouraged to always specify these values + explicitly given the security issues involved. +</dd> + +<code><dt> +Auth_MSQL_nopasswd < on | off > +</dt></code><dd> + Skip password comparison if passwd field is + empty, i.e. allow any password. This is 'off' + by default to ensure that an empty field + in the mSQL table does not allow people in by + default with a random password. +</dd> + +<code><dt> +<a name="Authoritative">Auth_MSQL_Authoritative < on | off ></a> +</dt></code><dd> + Default is 'on'. When set 'on', there is no + fall-through to other authorization methods. So if a + user is not in the mSQL dbase table (and perhaps + not in the right group) or has the password wrong, then + he or she is denied access. When this directive is set to + 'off', control is passed on to any other authorization + modules, such as the basic auth module with the htpasswd + file or the Unix-(g)dbm modules. The default is 'on' + to avoid nasty 'fall-through' surprises. Be sure you + know what you are doing when you decide to switch it off. +</dd> + +<code><dt> +Auth_MSQL_EncryptedPasswords < on | off > +</dt></code><dd> + Default is 'on'. When set on, the values in the + pwd_field are assumed to be crypt-ed using *your* + machines 'crypt()' function and the incoming password + is 'crypt'ed before comparison. When this function is + 'off', the comparison is done directly with the plaintext + entered password. (Yes, http-basic-auth does send the + password as plaintext over the wire :-( ). The default + is a sensible 'on', and I personally think that it is + a *very-bad-idea* to change this. However a multi + vendor or international environment (which sometimes + leads to different crypts functions) might force you to. +</dd> +</dl> + + +<hr><h2><a name="Example">Example</a></h2> + +An example mSQL table could be created with the following commands: +<pre> + % msqladmin create www <br> + % msql www <br> + -> create table user_records ( <br> + -> User_id char(32) primary key, <br> + -> Cpasswd char(32), <br> + -> Xgroup char(32) <br> + -> ) \g <br> + query OK <br> + -> \q <br> + % <br> +</pre><br> + +The <code>User_id</code> can be as long as desired. However some of the +popular web browsers truncate names at or stop the user from entering +names longer than 32 characters. Furthermore the 'crypt' function +on your platform might impose further limits. Also use of +the <code>require users uid [uid..]</code> directive in the +<code>access.conf</code> file where the uid's are separated by +spaces can possibly prohibit the use of spaces in your usernames. +Also, please note the <a href="#MAX_FIELD_LEN"><code>MAX_FIELD_LEN</code></a> +directive somewhere below. +<p> +To use the above, the following example could be in your +<code>access.conf</code> file. Also there is a more elaborate description +below this example. +<p> + +<code><directory /web/docs/private></code> +<p> + +<dl> +<dt><code> +Auth_MSQLhost localhost<br> +</code></dt> + <blockquote>or</blockquote> +<dt><code> +Auth_MSQLhost datab.machine.your.org +</code></dt><dd> + If this directive is omitted or set to <code>localhost</code>, + it is assumed that Apache and the mSQL + database run on the same (physical) machine and the faster + /dev/msql communication channel will be used. Otherwise, + it is the machine to contact by TCP/IP. Consult the mSQL + documentation for more information. +</dd> +<p> + +<dt><code> +Auth_MSQLdatabase www +</code></dt><dd> + The name of the database on the above machine, + which contains *both* the tables for group and + for user/passwords. Currently it is not possible + to have these split over two databases. Make + sure that the <code>msql.acl</code> (access control file) of + mSQL does indeed allow the effective uid of the + web server read access to this database. Check the + httpd.conf file for this uid. +</dd> + +<code><dt> +Auth_MSQLpwd_table user_records +</dt></code><dd> + This is the table which contain the uid/password combination + is specified. +</dd> + +<code><dt> +Auth_MSQLuid_field User_id <br> +Auth_MSQLpwd_field Cpasswd +</dt></code><dd> + These two directive specify the field names in the <code>user_record</code> + table. If this module is compiled with the <a href="#VITEK"><code>BACKWARD_VITEK</code></a> + compatibility switch, the defaults <code>user</code> and <code>password</code> are + assumed if you do not specify them. Currently the user_id field + *MUST* be a primary key or one must ensure that each user only + occurs <b>once</b> in the table. If a uid occurs twice access is + denied by default; but see the <code><a href="#ONLY_ONCE">ONLY_ONCE</a></code> + compiler directive for more information. +</dd> + +<code><dt> +Auth_MSQLgrp_table user_records <br> +Auth_MSQLgrp_field Xgroup <br> +</dt></code><dd> + Optionally one can also specify a table which contains the + user/group combinations. This can be the same table which + also contains the username/password combinations. However + if a user belongs to two or more groups, one will have to + use a different table with multiple entries. +</dd> + +<code><dt> +Auth_MSQL_nopasswd off <br> +Auth_MSQL_Authoritative on <br> +Auth_MSQL_EncryptedPasswords on <br> +</dt></code><dd> + These three optional fields (all set to the sensible defaults, + so you really do not have to enter them) are described in more + detail below. If you choose to set these to any other values then + the above, be very sure you understand the security implications and + do verify that Apache does what you expect it to do. +</dd> + +<code><dt> +AuthName example mSQL realm <br> +AuthType basic +</dt></code> +<dd> + Normal Apache/NCSA tokens for access control + <p> + <code><limit get post head></code><br> + <code>order deny,allow </code><br> + <code>allow from all </code><br> + <p> + <code>require valid-user </code><br> + <ul><li><code>valid-user</code>; allow in any user which has a valid uid/passwd + pair in the above pwd_table. + </ul> + or<br> + <code>require user smith jones </code><br> + <ul><li>Limit access to users who have a valid uid/passwd pair in the + above pwd_table *and* whose uid is 'smith' or 'jones'. Do note that + the uid's are separated by 'spaces' for historic (NCSA) reasons. + So allowing uids with spaces might cause problems. + </ul> + <code>require group has_paid</code><br> + <ul><li>Optionally also ensure that the uid has the value 'has_paid' in + the group field in the group table. + </ul> + <code><limit> </code><br> +</dd> +</dl> + + +<hr><h2><a name="CompileTimeOptions">Compile Time Options</a></h2> + +<dl> +<dt><code> +<a name="ONLY_ONCE">#define ONLY_ONCE 1</a> +</code></dt><dd> + If the mSQL table containing the uid/passwd combination does + not have the uid field as a primary key, it is possible for the + uid to occur more than once in the table with possibly different + passwords. When this module is compiled with the <code>ONLY_ONCE</code> + directive set, access is denied if the uid occurs more than once in the + uid/passwd table. If you choose not to set it, the software takes + the first pair returned and ignores any further pairs. The SQL + statement used for this is<br> + <p><code>"select password form pwd_table where user='UID'"</code><p> + this might lead to unpredictable results. For this reason as well + as for performance reasons you are strongly advised to make the + uid field a primary key. Use at your own peril :-) +</dd> + +<dt><code> +<a name="KEEP_MSQL_CONNECTION_OPEN">#define KEEP_MSQL_CONNECTION_OPEN</a> +</code></dt><dd> + Normally the (TCP/IP) connection with the database is opened and + closed for each SQL query. When the Apache web-server and the database + are on the same machine, and /dev/msql is used this does not + cause a serious overhead. However when your platform does not + support this (see the mSQL documentation) or when the web server + and the database are on different machines the overhead can be + considerable. When the above directive is set defined the server leaves + the connection open, i.e. no call to <code>msqlClose()</code>. + If an error occurs an attempt is made to reopen the connection for + the next http request. + <p> + This has a number of very serious drawbacks + <ul><li> It costs 2 already rare file-descriptors for each child. + <li> It costs msql-connections, typically one per child. The (compiled in) + number of connections mSQL can handle is low, typically 6 or 12. + which might prohibit access to the mSQL database for later + processes. + <li> When a child dies, it might not free that connection properly + or quick enough. + <li> When errors start to occur, connection/file-descriptor resources + might become exhausted very quickly. + </ul> + <p> + In short, use this at your own peril and only in a highly controlled and + monitored environment. +</dd> + +<dt><code> +<a name="VITEK"> +#define BACKWARD_VITEK<br></a> +#define VITEK_uid_name "user"<br> +#define VITEK_gid_name "passwd" +</code></dt><dd> + A second mSQL auth module for Apache has also been developed by Vivek Khera + <<a href="mailto:khera@kciLink.com"><code>khera@kciLink.com</code></a>> + and was subsequently distributed with some early versions of Apache. It + can be obtained from + <code><a href="ftp://ftp.kcilink.com/pub/">ftp://ftp.kcilink.com/pub/mod_auth_msql.c*</a></code>. + Older 'vitek' versions had the field/table names compiled in. Newer + versions, v.1.11 have more <code>access.conf</code> configuration + options. However these where chosen not to be in line the 'ewse' + version of this module. Also, the 'vitek' module does not give group + control or 'empty' password control. + <p> + To get things slightly more in line this version (0.9) should + be backward compatible with the 'vitek' module by: + <ul><li> Adding support for the <code>Auth_MSQL_EncryptedPasswords</code> on/off functionality + <li> Adding support for the different spelling of the 4 configuration + tokens for user-table-name, user/password-field-name and dbase-name. + <li> Setting some field names to a default which used to be hard + coded in in older 'vitek' modules. + </ul> + <p> + If this troubles you, remove the 'BACKWARD_VITEK' define. +</dd> + +<dt><code> +<a name="MAX_FIELD_LEN"> +#define MAX_FIELD_LEN (64)<br> +#define MAX_QUERY_LEN (32+24+MAX_FIELD_LEN*2+3*MSQL_FIELD_NAME_LEN+1*MSQL_TABLE_NAME_LEN)<br></a> +</code></dt><dd> + In order to avoid using the very large <code>HUGE_STRING_LENGTH</code>, the above two compile + time directives are supplies. The <code>MAX_FIELD_LEN</code> contains the maximum number of + characters in your user, password and group fields. The maximum query length is derived + from those values. + <p> + We only do the following two queries: + <ul><li>For the user/passwd combination + <p><code>"select PWDFIELD from PWDTABLE where USERFIELD='UID'"</code><br> + <li>Optionally for the user/group combination: + <p><code>"select GROUPFIELD from GROUPTABLE where USERFIELD='UID' and GROUPFIELD='GID'"</code><br> + </ul> + <p> + This leads to the above limit for the query string. We are ignoring escaping a wee bit here + assuming not more than 24 escapes.) +</dd> +</dl> + + +<hr><h2><a name="RevisionHistory">Revision History</a></h2> + +This version: 23 Nov 1995, 24 Feb 1996, 16 May 1996. + +<dl> + +<dt>Version 0.0<br></dt> + <dd>First release + </dd> +<dt>Version 0.1<br></dt> + <dd>Update to Apache 1.00 + </dd> +<dt>Version 0.2<br></dt> + <dd>Added lines which got missing God knows when + and which did the valid-user authentication no good at all ! + </dd> +<dt>Version 0.3<br></dt> + <dd>Added '<code>Auth_MSQL_nopasswd</code>' option + </dd> +<dt>Version 0.4<br></dt> + <dd>Cleaned out the error messages mess. + </dd> +<dt>Version 0.6<br></dt> + <dd>Inconsistency with gid/grp in comment/token/source + Make sure you really use '<code>Auth_MSQLgrp_field</code>' + as indicated above. + </dd> +<dt>Version 0.7<br></dt> + <dd><code>*host</code> to <code>host</code> fixed. Credits + go to Rob Stout, <stout@lava.et.tudelft.nl> for + spotting this one. + </dd> +<dt>Version 0.8<br></dt> + <dd>Authoritative directive added. See above. + </dd> +<dt>Version 0.9<br></dt> + <dd><code>palloc</code> return code check(s), should be + backward compatible with 1.11 version of Vivek Khera + <khera@kciLink.com> msql + module, fixed broken err msg in group control, changed + command table messages to make more sense when displayed + in that new module management tool. Added + <code>Auth_MSQL_EncryptedPasswords</code> on/off functionality. + msqlClose() statements added upon error. Support for + persistent connections with the mSQL database (riscy). + Escaping of ' and \. Replaced some + <code>MAX_STRING_LENGTH</code> claims. + </dd> +</dl> + + +<hr><h2><a name="Person">Contact/person to blame</a></h2> + +This module was written for the +<a href="http://ewse.ceo.org">European Wide Service Exchange</a> by +<<a href="mailto:Dirk.vanGulik@jrc.it"><code>Dirk.vanGulik@jrc.it</code></a>>. +Feel free to contact me if you have any problems, ice-creams or bugs. This +documentation, courtesy of Nick Himba, <a href="mailto:himba@cs.utwente.nl"> +<code><himba@cs.utwente.nl></code></a>. +<p> + + +<hr><h2><a NAME="Sourcecode">Sourcecode</a></h2> + +The source code can be found at <a href="http://www.apache.org"><code> +http://www.apache.org</code></a>. A snapshot of a development version +usually resides at <a href="http://me-www.jrc.it/~dirkx/mod_auth_msql.c"><code> +http://me-www.jrc.it/~dirkx/mod_auth_msql.c</code></a>. Please make sure +that you always quote the version you use when filing a bug report. +<p> +Furthermore a test/demonstration suite (which assumes that you have +both mSQL and Apache compiled and installed) is available at the contrib +section of <a href="ftp://ftp.apache.org/apache/dist/contrib"><code> +ftp://ftp.apache.org/apache/dist/contrib</code></a> or +<a href="http://me-www.jrc.it/~dirkx/apache-msql-demo.tar.gz"><code> +http://me-www.jrc.it/~dirkx/apache-msql-demo.tar.gz</code></a> and +its <a href="http://me-www.jrc.it/~dirkx/apache-msql-demo"><code> +README</code></a> file. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</body> +</html> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_browser.html b/usr.sbin/httpd/htdocs/manual/mod/mod_browser.html new file mode 100644 index 00000000000..47ec81b760a --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_browser.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_browser</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_browser</H1> + +This module is contained in the <code>mod_browser.c</code> file, and +is compiled in by default with Apache 1.2 and above. It provides for +setting environment variables based on the browser. + +<h2>Summary</h2> + +<p>This module allows you to set environment variables based on the name of +the browser accessing your document, based on the <code>User-Agent</code> +header field. This is especially useful when combined with a conditional +HTML language such as <a href="mod_include.html">XSSI</a> or PHP, and +can provide for simple browser-based negotiation of HTML features.</p> + +<h2>Directives</h2> +<ul> +<li><A HREF="#browsermatch">BrowserMatch</A> +<li><A HREF="#browsermatchnocase">BrowserMatchNoCase</A> +</ul> + +<hr> + +<h2><A name="browsermatch">BrowserMatch</A></h2> +<strong>Syntax:</strong> BrowserMatch <em>regex attr1 attr2...</em><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> base<br> +<strong>Module:</strong> mod_browser<br> +<strong>Compatibility:</strong> Apache 1.2 and above<p> + +The BrowserMatch directive defines environment variables based on the +User-Agent +header. The first argument should be a POSIX.2 extended regular +expression (similar to an egrep-style regex). The rest of the arguments +give names of variables to set. These take the form of either +"<code>varname</code>", "<code>!varname</code>" or +"<code>varname=value</code>". In the first form, the value will be set +to "1". The second will remove the given variable if already defined, +and the third will set the variable to the value given by <code>value</code>. If a User-Agent +string matches more than one entry, they will +be merged. Entries are processed in the order they appear, and later +entries can override earlier ones. + +<p>For example:</p> +<pre> + BrowserMatch ^Mozilla forms jpeg=yes browser=netscape + BrowserMatch "^Mozilla/[2-3]" tables agif frames javascript + BrowserMatch MSIE !javascript +</pre> + +<p><h2><A name="browsermatchnocase">BrowserMatchNoCase</A></h2> +<strong>Syntax:</strong> BrowserMatchNoCase <em>regex attr1 attr2...</em><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> base<br> +<strong>Module:</strong> mod_browser<br> +<strong>Compatibility:</strong> Apache 1.2 and above + +<p>The <code>BrowserMatchNoCase</code> directive is semantically identical to + the <a href="#browsermatch"><code>BrowserMatch</code></a> + directive. However, it provides for case-insensitive matching. For + example:</p> +<pre> + BrowserMatchNoCase mac platform=macintosh + BrowserMatchNoCase win platform=windows +</pre> + + +<p> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_cern_meta.html b/usr.sbin/httpd/htdocs/manual/mod/mod_cern_meta.html new file mode 100644 index 00000000000..3eb508f9997 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_cern_meta.html @@ -0,0 +1,94 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Module mod_cern_meta</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_cern_meta</H1> + +This module is contained in the <code>mod_cern_meta.c</code> file, and +is not compiled in by default. It provides for CERN httpd metafile +semantics. It is only available in Apache 1.1 and later. + +<h2>Summary</h2> + +Emulate the CERN HTTPD Meta file semantics. Meta files are HTTP +headers that can be output in addition to the normal range of headers +for each file accessed. They appear rather like the Apache +.asis files, and are able to provide a crude way of influencing +the Expires: header, as well as providing other curiosities. +There are many ways to manage meta information, this one was +chosen because there is already a large number of CERN users +who can exploit this module. + +<p>More information on the +<a href="http://www.w3.org/pub/WWW/Daemon/User/Config/General.html#MetaDir +">CERN metafile semantics</a> is available. + +<h2>Directives</h2> +<ul> +<li><A HREF="#metadir">MetaDir</A> +<li><A HREF="#metasuffix">MetaSuffix</A> +</ul> + +<hr> + +<h2><A name="metadir">MetaDir</A></h2> +<strong>Syntax:</strong> MetaDir <em>directory name</em><br> +<strong>Default:</strong> <code>MetaDir .web</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_cern_meta<br> +<strong>Compatibility:</strong> MetaDir is only available in Apache 1.1 +and later.<p> + +Specifies the name of the directory in which Apache can find +meta information files. The directory is usually a 'hidden' +subdirectory of the directory that contains the file being +accessed. Set to "<code>.</code>" to look in the same directory as the +file. + +<h2><A name="metasuffix">MetaSuffix</A></h2> +<strong>Syntax:</strong> MetaSuffix <em>suffix</em><br> +<strong>Default:</strong> <code>MetaSuffix .meta</code><br> +<strong>Context:</strong> server config<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_cern_meta<br> +<strong>Compatibility:</strong> MetaSuffix is only available in Apache 1.1 +and later.<p> + +Specifies the file name suffix for the file containing the +meta information. For example, the default values for the two +directives will cause a request to <code> +DOCUMENT_ROOT/somedir/index.html</code> to look in +<code>DOCUMENT_ROOT/somedir/.web/index.html.meta</code> and will use +its contents to generate additional MIME header information. + +<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_cgi.html b/usr.sbin/httpd/htdocs/manual/mod/mod_cgi.html new file mode 100644 index 00000000000..d1cfa745a5b --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_cgi.html @@ -0,0 +1,187 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Apache module mod_cgi</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<h1 ALIGN="CENTER">Module mod_cgi</h1> + +This module is contained in the <code>mod_cgi.c</code> file, and +is compiled in by default. It provides for execution of CGI scripts. +Any file with mime type <code>application/x-httpd-cgi</code> will be +processed by this module. +<!--%plaintext <?INDEX {\tt application/x-httpd-cgi} mime type> --> +<!--%plaintext <?INDEX CGI scripts> --> + +<h2>Summary</h2> +Any file that has the mime type <code>application/x-httpd-cgi</code> +or handler <code>cgi-script</code> (Apache 1.1 or later) +will be treated as a CGI script, and run by the server, with its output +being returned to the client. Files acquire this type either by +having a name ending in an extension defined by the +<A HREF="mod_mime.html#addtype">AddType</A> directive, or by being in +a <A HREF="mod_alias.html#scriptalias">ScriptAlias</A> directory. <p> + +When the server invokes a CGI script, it will add a variable called +<code>DOCUMENT_ROOT</code> to the environment. This variable will contain the +value of the <A HREF="core.html#documentroot">DocumentRoot</A> +configuration variable. + +<h2>CGI Environment variables</h2> +The server will set the CGI environment variables as described in the CGI +specification, with the following provisions: +<dl> +<dt>REMOTE_HOST +<dd>This will only be set if the server has not been compiled with +<code>MINIMAL_DNS</code>. +<dt>REMOTE_IDENT +<dd>This will only be set if +<A HREF="core.html#identitycheck">IdentityCheck</A> is set to <code>on</code>. +<dt>REMOTE_USER +<dd>This will only be set if the CGI script is subject to authentication. +</dl> +<P> + +<hr> + +<h2><a name="cgi_debug">CGI Debugging</a></h2> + +Debugging CGI scripts has traditionally been difficult, mainly because +it has +not +been possible to study the output (standard output and error) for +scripts +which are failing to run properly. These directives, included in +Apache 1.2 and later, provide +more detailed logging of errors when they occur. + +<hr> + +<h2>CGI Logfile Format</h2> + +When configured, the CGI error log logs any CGI which does not execute +properly. Each CGI script which fails to operate causes several lines +of information to be logged. The first two lines are always of the +format: + +<pre> + %% [<i>time</i>] <i>request-line</i> + %% <i>HTTP-status</i> <i>CGI-script-filename</i> +</pre> + +If the error is that CGI script cannot be run, the log file will +contain +an extra two lines: + +<pre> + %%error + <i>error-message</i> +</pre> + +Alternatively, if the error is the result of the script returning +incorrect header information (often due to a bug in the script), the +following information is logged: + +<pre> + %request + <i>All HTTP request headers received</i> + <i>POST or PUT entity (if any)</i> + %response + <i>All headers output by the CGI script</i> + %stdout + <i>CGI standard output</i> + %stderr + <i>CGI standard error</i> +</pre> + +(The %stdout and %stderr parts may be missing if the script did not +output +anything on standard output or standard error). + +<hr> + +<h2>Directives</h2> + +<h3><a name="scriptlog">ScriptLog</a></h3> + +<b>Syntax:</b> ScriptLog <i>filename</i><br> +<b>Default:</b> none<br> +<b>Context:</b> resource config<br> +<b>Status:</b> mod_cgi +<p> + +The <tt>ScriptLog</tt> directive sets the CGI script error logfile. +If no ScriptLog is given, no error log is created. If given, any +CGI errors are logged into the filename given as argument. If this +is a relative file or path it is taken relative to the server root. + +<P>This log will be opened as the user the child processes run as, +ie. the user specified in the main <A HREF="core.html#User">User</A> +directive. This means that either the directory the script log is +in needs to be writable by that user or the file needs to be manually +created and set to be writable by that user. If you place the +script log in your main logs directory, do <STRONG>NOT</STRONG> +change the directory permissions to make it writable by the user +the child processes run as.</P> + +<p>Note that script logging is meant to be a debugging feature when +writing CGI scripts, and is not meant to be activated continuously on +running servers. It is not optimized for speed or efficiency, and may +have security problems if used in a manner other than that for which +it was designed.</p> + +<h3><a name="scriptloglength">ScriptLogLength</a></h3> + +<b>Syntax:</b> ScriptLogLength <i>size</i><br> +<b>Default:</b> 10385760<br> +<b>Context:</b> resource config<br> +<b>Status:</b> mod_cgi +<p> + +<tt>ScriptLogLength</tt> can be used to limit the size of the CGI +script logfile. Since the logfile logs a lot of information per CGI +error (all request headers, all script output) it can grow to be a big +file. To prevent problems due to unbounded growth, this directive can +be used to set an maximum file-size for the CGI logfile. If the file +exceeds this size, no more information will be written to it. + +<h3><a name="scriptlogbuffer">ScriptLogBuffer</a></h3> + +<b>Syntax:</b> ScriptLogBuffer <i>size</i><br> +<b>Default:</b> 1024<br> +<b>Context:</b> resource config<br> +<b>Status:</b> mod_cgi +<p> + +The size of any PUT or POST entity body that is logged to the file is +limited, to prevent the log file growing too big too quickly if large +bodies are being received. By default, up to 1024 bytes are logged, +but this can be changed with this directive. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_cookies.html b/usr.sbin/httpd/htdocs/manual/mod/mod_cookies.html new file mode 100644 index 00000000000..5f76d8f736f --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_cookies.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_cookies</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_cookies</h1> + +This module is contained in the <code>mod_cookies.c</code> file, and +is not compiled in by default. It provides for Netscape(TM) cookies. +There is no documentation available for this module. + + +<menu> +<li><A HREF="#cookielog">CookieLog</A> +</menu> +<hr> + + +<h2><A name="cookielog">CookieLog</A></h2> +<!--%plaintext <?INDEX {\tt CookieLog} directive> --> +<strong>Syntax:</strong> CookieLog <em>filename</em><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Experimental<br> +<strong>Module:</strong> mod_cookies<p> + +The CookieLog directive sets the filename for logging of cookies. +The filename is relative to the <A HREF="core.html#serverroot">ServerRoot</A>. +<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_digest.html b/usr.sbin/httpd/htdocs/manual/mod/mod_digest.html new file mode 100644 index 00000000000..ff6c0a6e153 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_digest.html @@ -0,0 +1,78 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_digest</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_digest</h1> + +This module is contained in the <code>mod_digest.c</code> file, and is +not compiled in by default. It is only available in Apache 1.1 and +later. It provides for user authentication using MD5 Digest +Authentication. + + +<menu> +<li><A HREF="#authdigestfile">AuthDigestFile</A> +</menu> +<hr> + + +<h2><A name="authdigestfile">AuthDigestFile</A></h2> +<strong>Syntax:</strong> AuthDigestFile <em>filename</em><br> +<Strong>Context:</strong> directory, .htaccess<br> +<Strong>Override:</strong> AuthConfig<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_digest<p> + +<p>The AuthDigestFile directive sets the name of a textual file containing +the list +of users and encoded passwords for digest authentication. +<em>Filename</em> +is the absolute path to the user file.</p> +<p>The digest file uses a special format. Files in this format can be +created using the "htdigest" utility found in the support/ subdirectory of +the Apache distribution.</p> + +<hr> + +<h3>Using Digest Authentication</h3> + +<p>Using MD5 Digest authentication is very simple. Simply set up +authentication normally. However, use "AuthType Digest" and +"AuthDigestFile" instead of the normal "AuthType Basic" and +"AuthUserFile". Everything else should remain the same.</p> + +<p>MD5 authentication provides a more secure password system, but only +works with supporting browsers. As of this writing (July 1996), the +majority of browsers do not support digest authentication. Therefore, we +do not recommend using this feature on a large Internet site. However, for +personal and intra-net use, where browser users can be controlled, it is +ideal.</p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_dir.html b/usr.sbin/httpd/htdocs/manual/mod/mod_dir.html new file mode 100644 index 00000000000..43beb8f145c --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_dir.html @@ -0,0 +1,380 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_dir</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_dir</H1> + +This module is contained in the <code>mod_dir.c</code> file, and +is compiled in by default. It provides for directory indexing. + +<h2>Summary</h2> +This module controls the directory indexing. The index of a directory +can come from one of two sources: +<ul> +<li>A file written by the user, typically called <code>index.html</code>. +The <A HREF="#directoryindex">DirectoryIndex</A> directive sets the name +of this file. +<li>Otherwise, a listing generated by the server. The other directives +control the format of this listing. The <A HREF="#addicon">AddIcon</A>, +<A HREF="#addiconbyencoding">AddIconByEncoding</A> and +<A HREF="#addiconbytype">AddIconByType</A> are used to set a list of +icons to display for various file types; for each file listed, the +first icon listed that matches the file is displayed. +</ul> + + +<h2>Directives</h2> + +<menu> +<li><A HREF="#addalt">AddAlt</A> +<li><A HREF="#addaltbyencoding">AddAltByEncoding</A> +<li><A HREF="#addaltbytype">AddAltByType</A> +<li><A HREF="#adddescription">AddDescription</A> +<li><A HREF="#addicon">AddIcon</A> +<li><A HREF="#addiconbyencoding">AddIconByEncoding</A> +<li><A HREF="#addiconbytype">AddIconByType</A> +<li><A HREF="#defaulticon">DefaultIcon</A> +<li><A HREF="#directoryindex">DirectoryIndex</A> +<li><A HREF="#fancyindexing">FancyIndexing</A> +<li><A HREF="#headername">HeaderName</A> +<li><A HREF="#indexignore">IndexIgnore</A> +<li><A HREF="#indexoptions">IndexOptions</A> +<li><A HREF="#readmename">ReadmeName</A> +</menu> +<hr> + +<A name="addalt"><h2>AddAlt</h2></A> +<!--%plaintext <?INDEX {\tt AddAlt} directive> --> +<strong>Syntax:</strong> AddAlt <em>string file file...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +This sets the alternate text to display for a file, instead of an icon, for +<A HREF="#fancyindexing">FancyIndexing</A>. <em>File</em> is a file +extension, partial filename, wild-card expression or full filename for files +to describe. <em>String</em> is enclosed in double quotes +(<code>"</code>). This alternate text is displayed if the client is +image-incapable or has image loading disabled. + +<HR> +<A name="addaltbyencoding"><h2>AddAltByEncoding</h2></A> +<!--%plaintext <?INDEX {\tt AddAltByEncoding} directive> --> +<strong>Syntax:</strong> AddAltByEncoding <em>string MIME-encoding + MIME-encoding...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +This sets the alternate text to display for a file, instead of an icon, for +<A HREF="#fancyindexing">FancyIndexing</A>. <em>MIME-encoding</em> is a +valid content-encoding, such as <SAMP>x-compress</SAMP>. +<em>String</em> is enclosed in double quotes +(<code>"</code>). This alternate text is displayed if the client is +image-incapable or has image loading disabled. + +<HR> +<A name="addaltbytype"><h2>AddAltByType</h2></A> +<!--%plaintext <?INDEX {\tt AddAltByType} directive> --> +<strong>Syntax:</strong> AddAltByType <em>string MIME-type MIME-type...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +This sets the alternate text to display for a file, instead of an icon, for +<A HREF="#fancyindexing">FancyIndexing</A>. <em>MIME-type</em> is a +valid content-type, such as <SAMP>text/html</SAMP>. +<em>String</em> is enclosed in double quotes +(<code>"</code>). This alternate text is displayed if the client is +image-incapable or has image loading disabled. + +<HR> + +<A name="adddescription"><h2>AddDescription</h2></A> +<!--%plaintext <?INDEX {\tt AddDescription} directive> --> +<strong>Syntax:</strong> AddDescription <em>string file file...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +This sets the description to display for a file, for +<A HREF="#fancyindexing">FancyIndexing</A>. <em>File</em> is a file +extension, partial filename, wild-card expression or full filename for files +to describe. <em>String</em> is enclosed in double quotes +(<code>"</code>). Example: +<blockquote><code>AddDescription "The planet Mars" /web/pics/mars.gif +</code></blockquote><p><hr> + +<A name="addicon"><h2>AddIcon</h2></A> +<!--%plaintext <?INDEX {\tt AddIcon} directive> --> +<strong>Syntax:</strong> AddIcon <em>icon name name ...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +This sets the icon to display next to a file ending in <em>name</em> for +<A HREF="#fancyindexing">FancyIndexing</A>. <em>Icon</em> is either a +(%-escaped) relative URL to the icon, or of the format +(<em>alttext</em>,<em>url</em>) where <em>alttext</em> is the text tag given +for an icon for non-graphical browsers.<p> + +<em>Name</em> is either ^^DIRECTORY^^ for directories, ^^BLANKICON^^ for +blank lines (to format the list correctly), a file extension, a wildcard +expression, a partial filename or a complete filename. Examples: +<blockquote><code> +AddIcon (IMG,/icons/image.xbm) .gif .jpg .xbm <br> +AddIcon /icons/dir.xbm ^^DIRECTORY^^ <br> +AddIcon /icons/backup.xbm *~ +</code></blockquote> +<A HREF="#addiconbytype">AddIconByType</A> should be used in preference to +AddIcon, when possible.<p><hr> + +<A name="addiconbyencoding"><h2>AddIconByEncoding</h2></A> +<!--%plaintext <?INDEX {\tt AddIconByEncoding} directive> --> +<strong>Syntax:</strong> AddIconByEncoding <em>icon mime-encoding mime-encoding +...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +This sets the icon to display next to files with +<em>mime-encoding</em> for <A HREF="#fancyindexing">FancyIndexing</A>. +<em>Icon</em> is either a (%-escaped) relative URL to the icon, or of the +format (<em>alttext</em>,<em>url</em>) where <em>alttext</em> is the text tag +given for an icon for non-graphical browsers.<p> + +<em>Mime-encoding</em> is a wildcard expression matching required the +content-encoding. Examples: +<blockquote><code> +AddIconByEncoding /icons/compress.xbm x-compress +</code></blockquote><p><hr> + +<A name="addiconbytype"><h2>AddIconByType</h2></A> +<!--%plaintext <?INDEX {\tt AddIconByType} directive> --> +<strong>Syntax:</strong> AddIconByType <em>icon mime-type mime-type ...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +This sets the icon to display next to files of type <em>mime-type</em> for +<A HREF="#fancyindexing">FancyIndexing</A>. <em>Icon</em> is either a +(%-escaped) relative URL to the icon, or of the format +(<em>alttext</em>,<em>url</em>) where <em>alttext</em> is the text tag given +for an icon for non-graphical browsers.<p> +<em>Mime-type</em> is a wildcard expression matching required the mime types. +Examples: +<blockquote><code> +AddIconByType (IMG,/icons/image.xbm) image/* +</code></blockquote><p><hr> + +<A name="defaulticon"><h2>DefaultIcon</h2></A> +<!--%plaintext <?INDEX {\tt DefaultIcon} directive> --> +<strong>Syntax:</strong> DefaultIcon <em>url</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +The DefaultIcon directive sets the icon to display for files when no +specific icon is known, for <A HREF="#fancyindexing">FancyIndexing</A>. +<em>Url</em> is a (%-escaped) relative URL to the icon. Examples: +<blockquote><code> +DefaultIcon /icon/unknown.xbm +</code></blockquote><p><hr> + +<A name="directoryindex"><h2>DirectoryIndex</h2></A> +<!--%plaintext <?INDEX {\tt DirectoryIndex} directive> --> +<strong>Syntax:</strong> DirectoryIndex <em>local-url local-url ...</em><br> +<strong>Default:</strong> <code>DirectoryIndex index.html</code><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +The DirectoryIndex directive sets the list of resources to look for, +when the client requests an index of the directory by specifying a / +at the end of the a directory name. <em>Local-url</em> is the +(%-encoded) URL of a document on the server relative to the requested +directory; it is usually the name of a file in the directory. Several +URLs may be given, in which case the server will return the first one +that it finds. If none of the resources exist and the +<CODE>Indexes</CODE> option is set, the server will generate its own +listing of the directory. +<P> + +Example: +<blockquote><code> +DirectoryIndex index.html +</code></blockquote> +then a request for <code>http://myserver/docs/</code> would return +<code>http://myserver/docs/index.html</code> if it exists, or would list +the directory if it did not. <p> + +Note that the documents do not need to be relative to the directory; +<blockquote><code> +DirectoryIndex index.html index.txt /cgi-bin/index.pl</code></blockquote> +would cause the CGI script <code>/cgi-bin/index.pl</code> to be executed +if neither <code>index.html</code> or <code>index.txt</code> existed in +a directory.<p><hr> + +<A name="fancyindexing"><h2>FancyIndexing</h2></A> +<!--%plaintext <?INDEX {\tt FancyIndexing} directive> --> +<strong>Syntax:</strong> FancyIndexing <em>boolean</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +The FancyIndexing directive sets the FancyIndexing option for a directory. +<em>Boolean</em> can be <code>on</code> or <code>off</code>. The +<A HREF="#indexoptions">IndexOptions</A> directive should be used in +preference.<p><hr> + +<A name="headername"><h2>HeaderName</h2></A> +<!--%plaintext <?INDEX {\tt HeaderName} directive> --> +<strong>Syntax:</strong> HeaderName <em>filename</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +The HeaderName directive sets the name of the file that will be inserted +at the top of the index listing. <em>Filename</em> is the name of the file +to include, and is taken to be relative to the directory being indexed. +The server first attempts to include <em>filename</em><code>.html</code> +as an HTML document, otherwise it will include <em>filename</em> as plain +text. Example: +<blockquote><code>HeaderName HEADER</code></blockquote> +when indexing the directory <code>/web</code>, the server will first look for +the HTML file <code>/web/HEADER.html</code> and include it if found, otherwise +it will include the plain text file <code>/web/HEADER</code>, if it exists. + +<p>See also <A HREF="#readmename">ReadmeName</A>.<p><hr> + +<A name="indexignore"><h2>IndexIgnore</h2></A> +<!--%plaintext <?INDEX {\tt IndexIgnore} directive> --> +<strong>Syntax:</strong> IndexIgnore <em>file file ...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +The IndexIgnore directive adds to the list of files to hide when listing +a directory. <em>File</em> is a file extension, partial filename, +wildcard expression or full filename for files to ignore. Multiple +IndexIgnore directives add to the list, rather than the replacing the list +of ignored files. By default, the list contains `<code>.</code>'. Example: +<blockquote><code> +IndexIgnore README .htaccess *~ +</code></blockquote><p><hr> + +<A name="indexoptions"><h2>IndexOptions</h2></A> +<!--%plaintext <?INDEX {\tt IndexOptions} directive> --> +<strong>Syntax:</strong> IndexOptions <em>option option ...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +The IndexOptions directive specifies the behavior of the directory indexing. +<em>Option</em> can be one of +<dl> +<dt>FancyIndexing +<dd><!--%plaintext <?INDEX {\tt FancyIndexing} index option> --> +This turns on fancy indexing of directories. +<dt>IconsAreLinks +<dd> +<!--%plaintext <?INDEX {\tt IconsAreLinks} index option> --> +This makes the icons part of the anchor for the filename, for +fancy indexing. +<dt>ScanHTMLTitles +<dd><!--%plaintext <?INDEX {\tt ScanHTMLTitles} index option> --> +This enables the extraction of the title from HTML documents for fancy +indexing. If the file does not have a description given by +<A HREF="#adddescription">AddDescription</A> then httpd will read the +document for the value of the TITLE tag. This is CPU and disk intensive. +<dt>SuppressLastModified +<dd> +<!--%plaintext <?INDEX {\tt SuppressLastModified} index option> --> +This will suppress the display of the last modification date, in fancy +indexing listings. +<dt>SuppressSize +<dd> +<!--%plaintext <?INDEX {\tt SuppressSize} index option> --> +This will suppress the file size in fancy indexing listings. +<dt>SuppressDescription +<dd> +<!--%plaintext <?INDEX {\tt SuppressDescription} index option> --> +This will suppress the file description in fancy indexing listings. +</dl> +This default is that no options are enabled. If multiple IndexOptions +could apply to a directory, then the most specific one is taken complete; +the options are not merged. For example: +<blockquote><code> +<Directory /web/docs> <br> +IndexOptions FancyIndexing <br> +</Directory><br> +<Directory /web/docs/spec> <br> +IndexOptions ScanHTMLTitles <br> +</Directory> +</code></blockquote> +then only <code>ScanHTMLTitles</code> will be set for the /web/docs/spec +directory.<p><hr> + +<A name="readmename"><h2>ReadmeName</h2></A> +<!--%plaintext <?INDEX {\tt ReadmeName} directive> --> +<strong>Syntax:</strong> ReadmeName <em>filename</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_dir<p> + +The ReadmeName directive sets the name of the file that will be appended +to the end of the index listing. <em>Filename</em> is the name of the file +to include, and is taken to be relative to the directory being indexed. +The server first attempts to include <em>filename</em><code>.html</code> +as an HTML document, otherwise it will include <em>filename</em> as plain +text. Example: +<blockquote><code>ReadmeName README</code></blockquote> +when indexing the directory <code>/web</code>, the server will first look for +the HTML file <code>/web/README.html</code> and include it if found, otherwise +it will include the plain text file <code>/web/README</code>, if it exists. + +<p>See also <A HREF="#headername">HeaderName</A>.<p> + + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_dld.html b/usr.sbin/httpd/htdocs/manual/mod/mod_dld.html new file mode 100644 index 00000000000..55ee488f1f0 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_dld.html @@ -0,0 +1,93 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_dld</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_dld</h1> + +This module is contained in the <code>mod_dld.c</code> file, and is not +compiled in by default. It provides for loading of executable code +and modules into the server at start-up time, using the GNU dld library. + +<h2>Summary</h2> + +The optional dld module is a proof-of-concept piece of code which +loads other modules into the server as it is configuring itself (the +first time only; for now, rereading the config files cannot affect the +state of loaded modules), using the GNU dynamic linking library, DLD. +It isn't compiled into the server by default, since not everyone has +DLD, but it works when I try it. (Famous last words.) <p> + +Note that for some reason, <code>LoadFile /lib/libc.a</code> seems to be +required for just about everything.<p> + +Note: that DLD needs to read the symbol table out of the server binary +when starting up; these commands will fail if the server can't find +its own binary when it starts up, or if that binary is stripped.<p> + + +<h2>Directives</h2> +<ul> +<li><A HREF="#loadfile">LoadFile</A> +<li><A HREF="#loadmodule">LoadModule</A> +</ul> +<hr> + + +<h2><A name="loadfile">LoadFile</A></h2> +<!--%plaintext <?INDEX {\tt LoadFile} directive> --> +<strong>Syntax:</strong> LoadFile <em>filename filename ...</em><br> +<Strong>Context:</strong> server config<br> +<strong>Status:</strong> Experimental<br> +<strong>Module:</strong> mod_dld<p> + +The LoadFile directive links in the named object files or libraries when +the server is started; this is used to load additional code which +may be required for some module to work. <em>Filename</em> is relative +to <A HREF="core.html#serverroot">ServerRoot</A>.<p><hr> + +<h2><A name="loadmodule">LoadModule</A></h2> +<!--%plaintext <?INDEX {\tt LoadModule} directive> --> +<strong>Syntax:</strong> LoadModule <em>module filename</em><br> +<Strong>Context:</strong> server config<br> +<strong>Status:</strong> Experimental<br> +<strong>Module:</strong> mod_dld<p> + +The LoadModule directive links in the object file or library <em>filename</em> +and adds the module structure named <em>module</em> to the list of active +modules. <em>Module</em> is the name of the external variable of type +<code>module</code> in the file. Example: +<blockquote><code> +LoadModule ai_backcompat_module modules/mod_ai_backcompat.o<br> +LoadFile /lib/libc.a +</code></blockquote> +loads the module in the modules subdirectory of the ServerRoot.<p> + + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_env.html b/usr.sbin/httpd/htdocs/manual/mod/mod_env.html new file mode 100644 index 00000000000..f3b602fdd72 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_env.html @@ -0,0 +1,105 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_env</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_env</H1> + +This module is contained in the <code>mod_env.c</code> file, and +is not compiled in by default. It provides for +passing environment variables to CGI/SSI scripts. Is is only available +in Apache 1.1 and later. + +<h2>Summary</h2> + +This module allows Apache's CGI and SSI environment to inherit +environment variables from the shell which invoked the httpd process. +CERN web-servers are able to do this, so this module is especially +useful to web-admins who wish to migrate from CERN to Apache without +rewriting all their scripts + +<h2>Directives</h2> +<ul> +<li><A HREF="#passenv">PassEnv</A> +<li><A HREF="#setenv">SetEnv</A> +<li><A HREF="#unsetenv">UnsetEnv</A> +</ul> + +<hr> + +<h2><A name="passenv">PassEnv</A></h2> +<strong>Syntax:</strong> PassEnv <em>variable variable ...</em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_env<br> +<strong>Compatibility:</strong> PassEnv is only available in +Apache 1.1 and later.<p> + +Specifies one or more environment variables to pass to CGI scripts +from the server's own environment. Example: +<pre> + PassEnv LD_LIBRARY_PATH +</pre> + +<HR> + +<h2><A name="setenv">SetEnv</A></h2> +<strong>Syntax:</strong> SetEnv <em>variable value</em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_env<br> +<strong>Compatibility:</strong> SetEnv is only available in +Apache 1.1 and later.<p> + +Sets an environment variable, which is then passed on to CGI +scripts. Example: +<pre> + SetEnv SPECIAL_PATH /foo/bin +</pre> + +<hr> + +<h2><A name="unsetenv">UnsetEnv</A></h2> +<strong>Syntax:</strong> UnsetEnv <em>variable variable ...</em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_env<br> +<strong>Compatibility:</strong> UnsetEnv is only available in +Apache 1.1 and later.<p> + +Removes one or more environment variables from those passed on to +CGI scripts. Example: +<pre> + UnsetEnv LD_LIBRARY_PATH +</pre> + + + +<p> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_example.html b/usr.sbin/httpd/htdocs/manual/mod/mod_example.html new file mode 100644 index 00000000000..013966e9f92 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_example.html @@ -0,0 +1,153 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Apache module mod_example</TITLE> + </HEAD> + <!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + <H1 ALIGN="CENTER">Module mod_example</h1> + <P> + This module is contained in the <CODE>modules/mod_example.c</CODE> file, and + <STRONG>is not</STRONG> compiled in by default. It illustrates many of + the aspects of the + <A + HREF="../misc/API.html" + REL="Help" + >Apache 1.2 API</A> + and, when used, demonstrates the manner in which module callbacks are + triggered by the server. + </P> + <H2>Summary</H2> + <P> + The files in the <CODE>src/modules/example directory</CODE> under the + Apache distribution directory tree are provided as an example to those + that wish to write modules that use the Apache API. + </P> + <P> + The main file is <CODE>mod_example.c</CODE>, which illustrates all + the different callback mechanisms and call syntaces. By no means does + an add-on module need to include routines for all of the callbacks - + quite the contrary! + </P> + <P> + The example module is an actual working module. If you link it into + your server, enable the "example-handler" handler for a location, and + then browse to that location, you will see a display of + some of the tracing the example module did as the various callbacks + were made. + </P> + <P> + To include the example module in your server, follow the steps below: + </P> + <OL> + <LI>Uncomment the "Module example_module" line near the bottom of + the <CODE>src/Configuration</CODE> file. If there isn't one, add + it; it should look like this: + <PRE> + Module example_module modules/example/mod_example.o + </PRE> + </LI> + <LI>Run the <CODE>src/Configure</CODE> script + ("<SAMP>cd src; ./Configure</SAMP>"). This will + build the Makefile for the server itself, and update the + <CODE>src/modules/Makefile</CODE> for any additional modules you + have requested from beneath that subdirectory. + </LI> + <LI>Make the server (run "<SAMP>make</SAMP>" in the <CODE>src</CODE> + directory). + </LI> + </OL> + <P> + To add another module of your own: + </P> + <OL TYPE="A"> + <LI><SAMP>mkdir src/modules/<EM>mymodule</EM></SAMP> + </LI> + <LI><SAMP>cp src/modules/example/* src/modules/<EM>mymodule</EM></SAMP> + </LI> + <LI>Modify the files in the new directory. + </LI> + <LI>Follow steps [1] through [3] above, with appropriate changes. + </LI> + </OL> + <H3> + Using the <SAMP>mod_example</SAMP> Module + </H3> + <P> + To activate the example module, include a block similar to the + following in your <SAMP>srm.conf</SAMP> file: + </P> + <PRE> + <Location /example-info> + SetHandler example-handler + </Location> + </PRE> + <P> + As an alternative, you can put the following into a + <A + HREF="core.html#accessfilename" + ><SAMP>.htaccess</SAMP></A> + file and then request the file "test.example" from that + location: + </P> + <PRE> + AddHandler example-handler .example + </PRE> + <P> + After reloading/restarting your server, you should be able to browse + to this location and see the brief display mentioned earlier. + </P> + <H2>Directives</H2> + <P> + <UL> + <LI><A HREF="#example">Example</A> + </LI> + </UL> + </P> + <HR> + <H2><A NAME="example"> + Example + </A></H2> + <P> + <STRONG>Syntax:</STRONG> Example + <BR> + <STRONG>Default:</STRONG> None + <BR> + <STRONG>Context:</STRONG> server config, virtual host, directory, .htaccess + <BR> + <STRONG>Override:</STRONG> Options + <BR> + <STRONG>Status:</STRONG> Extension + <BR> + <STRONG>Module:</STRONG> mod_example + </P> + <P> + The Example directive activates the example module's content handler + for a particular location or file type. It takes no arguments. If + you browse to an URL to which the example content-handler applies, you + will get a display of the routines within the module and how and in + what order they were called to service the document request. + </P> + <HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + + </BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_expires.html b/usr.sbin/httpd/htdocs/manual/mod/mod_expires.html new file mode 100644 index 00000000000..af226b259fa --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_expires.html @@ -0,0 +1,198 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Apache module mod_expires</TITLE> + </HEAD> + <!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + <H1 ALIGN="CENTER">Module mod_expires</H1> + <P> + This module is contained in the <CODE>mod_expires.c</CODE> file, and + is <STRONG>not</STRONG> compiled in by default. It provides for the + generation of <CODE>Expires</CODE> headers according to user-specified + criteria. + </P> + <H2>Summary</H2> + <P> + This module controls the setting of the <CODE>Expires</CODE> HTTP + header in server responses. The expiration date can set to be + relative to either the time the source file was last modified, or to + the time of the client access. + </P> + <P> + The <CODE>Expires</CODE> HTTP header is an instruction to the client + about the document's validity and persistence. If cached, the document + may be fetched from the cache rather than from the source until this + time has passed. After that, the cache copy is considered + "expired" and invalid, and a new copy must be obtained from + the source. + </P> + <H2>Directives</H2> + <P> + <MENU> + <LI><A + HREF="#expiresactive" + >ExpiresActive</A> + </LI> + <LI><A + HREF="#expiresbytype" + >ExpiresByType</A> + </LI> + <LI><A + HREF="#expiresdefault" + >ExpiresDefault</A> + </LI> + </MENU> + <HR> + <H2><A NAME="expiresactive"> + ExpiresActive directive + </A></H2> + <!--%plaintext <?INDEX {\tt ExpiresActive} directive> --> + <P> + <STRONG>Syntax:</STRONG> ExpiresActive <EM>boolean</EM> + <BR> + <STRONG>Context:</STRONG> server config, virtual host, directory, .htaccess + <br> + <STRONG>Override:</STRONG> Indexes + <br> + <STRONG>Status:</STRONG> Extension + <br> + <STRONG>Module:</STRONG> mod_expires + </P> + <P> + This directive enables or disables the generation of the + <CODE>Expires</CODE> header for the document realm in question. (That + is, if found in an <CODE>.htaccess</CODE> file, for instance, it + applies only to documents generated from that directory.) If set to + <EM><CODE>Off</CODE></EM>, no <CODE>Expires</CODE> header will be + generated for any document in the realm (unless overridden at a lower + level, such as an <CODE>.htaccess</CODE> file overriding a server + config file). If set to <EM><CODE>On</CODE></EM>, the header will be + added to served documents according to the criteria defined by the + <A + HREF="#expiresbytype" + >ExpiresByType</A> + and + <A + HREF="#expiresdefault" + >ExpiresDefault</A> + directives (<EM>q.v.</EM>). + </P> + <P> + Note that this directive does not guarantee that an + <CODE>Expires</CODE> header will be generated. If the criteria aren't + met, no header will be sent, and the effect will be as though this + directive wasn't even specified. + </P> + <HR> + <H2><A NAME="expiresbytype"> + ExpiresByType directive + </A></H2> + <!--%plaintext <?INDEX {\tt ExpiresByType} directive> --> + <P> + <STRONG>Syntax:</STRONG> ExpiresByType <EM>mime-type <code>seconds</EM> + <BR> + <STRONG>Context:</STRONG> server config, virtual host, directory, .htaccess + <br> + <STRONG>Override:</STRONG> Indexes + <br> + <STRONG>Status:</STRONG> Extension + <br> + <STRONG>Module:</STRONG> mod_expires + </P> + <P> + This directive defines the value of the <CODE>Expires</CODE> header + generated for documents of the specified type (<EM>e.g.</EM>, + <CODE>text/html</CODE>). The second argument sets the number of + seconds that will be added to a base time to construct the expiration + date. + </P> + <P> + The base time is either the last modification time of the file, or the + time of the client's access to the document. Which should be used is + specified by the <CODE><EM><code></EM></CODE> field; + <STRONG>M</STRONG> means that the file's last modification time should + be used as the base time, and <STRONG>A</STRONG> means the client's + access time should be used. + </P> + <P> + The difference in effect is subtle. If <EM>M</EM> is used, all current + copies of the document in all caches will expire at the same time, + which can be good for something like a weekly notice that's always + found at the same URL. If <EM>A</EM> is used, the date of expiration + is different for each client; this can be good for image files that + don't change very often, particularly for a set of related documents + that all refer to the same images (<EM>i.e.</EM>, the images will be + accessed repeatedly within a relatively short timespan). + </P> + <P> + <STRONG>Example:</STRONG> + </P> + <P> + <PRE> + ExpiresActive On # enable expirations + ExpiresByType image/gif A2592000 # expire GIF images after a month + # in the client's cache + ExpiresByType text/html M604800 # HTML documents are good for a + # week from the time they were + # changed, period + </PRE> + </P> + <P> + Note that this directive only has effect if <CODE>ExpiresActive + On</CODE> has been specified. It overrides, for the specified MIME + type <EM>only</EM>, any expiration date set by the + <A + HREF="#expiresdefault" + >ExpiresDefault</A> + directive. + </P> + <HR> + <H2><A NAME="expiresdefault"> + ExpiresDefault directive + </A></H2> + <!--%plaintext <?INDEX {\tt ExpiresDefault} directive> --> + <P> + <STRONG>Syntax:</STRONG> ExpiresDefault <EM><code>seconds</EM> + <BR> + <STRONG>Context:</STRONG> server config, virtual host, directory, .htaccess + <br> + <STRONG>Override:</STRONG> Indexes + <br> + <STRONG>Status:</STRONG> Extension + <br> + <STRONG>Module:</STRONG> mod_expires + </P> + <P> + This directive sets the default algorithm for calculating the + expiration time for all documents in the affected realm. It can be + overridden on a type-by-type basis by the + <A + HREF="#expiresbytype" + >ExpiresByType</A> + directive. See the description of that directive for details about + the syntax of the argument. + </P> + <HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + + </BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_headers.html b/usr.sbin/httpd/htdocs/manual/mod/mod_headers.html new file mode 100644 index 00000000000..212c519bf7e --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_headers.html @@ -0,0 +1,117 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_headers</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Module mod_headers</h1> + +The optional headers module allows for the customization of HTTP +response headers. Headers can be merged, replaced or removed. The +directives described in this document are only available if Apache is +compiled with <b>mod_headers.c</b>. + +<hr> + +<h2>Directive</h2> +<ul> +<li><A HREF="#header">Header</A> +</ul> + +<hr> + +<h2><A name="header">Header</A></h2> +<strong>Syntax:</strong> Header [ set | append | add ] <em>header</em> <em>value</em><br> +<strong>Syntax:</strong> Header unset <em>header</em><br> +<strong>Context:</strong> server config, virtual host, access.conf, .htaccess<br> +<strong>Status:</strong> optional<br> +<strong>Module:</strong> mod_header<p> + +This directive can replace, merge or remove HTTP response headers. The +action it performs is determined by the first argument. This can be one +of the following values: + +<ul> +<li><b>set</b><br> + The response header is set, replacing any previous header with this name + +<li><b>append</b><br> + The response header is appended to any existing header of the same + name. When a new value is merged onto an existing header it is + separated from the existing header with a comma. This is the HTTP standard + way of giving a header multiple values. + +<li><b>add</b><br> + The response header is added to the existing set of headers, even if + this header already exists. This can result in two (or more) headers + having the same name. This can lead to unforeseen consequences, and in + general "append" should be used instead. + +<li><b>unset</b><br> + The response header of this name is removed, if it exists. If there are + multiple headers of the same name, only the first one set will be removed. +</ul> + +This argument is followed by a header name, which can include the +final colon, but it is not required. Case is ignored. For +add, append and set a value is given as the third argument. If this +value contains spaces, it should be surrounded by double quotes. +For unset, no value should be given. + +<h3>Order of Processing</h3> + +The Header directive can occur almost anywhere within the server +configuration. It is valid in the main server config and virtual host +sections, inside <Directory>, <Location> and <Files> +sections, and within .htaccess files. +<p> +The Header directives are processed in the following order: +<ol> +<li>main server +<li>virtual host +<li><Directory> sections and .htaccess +<li><Location> +<li><Files> +</ol> + +Order is important. These two headers have a different effect if reversed: +<pre> +Header append Author "John P. Doe" +Header unset Author +</pre> + +This way round, the Author header is not set. If reversed, the Author +header is set to "John P. Doe". +<p> + +The Header directives are processed just before the response is sent +by its handler. These means that some headers that are added just +before the response is sent cannot be unset or overridden. This +includes headers such as "Date" and "Server". +<P> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_imap.html b/usr.sbin/httpd/htdocs/manual/mod/mod_imap.html new file mode 100644 index 00000000000..25875b86002 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_imap.html @@ -0,0 +1,297 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Apache module mod_imap</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Module mod_imap</h1> + +This module is contained in the <code>mod_imap.c</code> file, and is +compiled in by default. It provides for <code>.map</code> files, +replacing the functionality of the <code>imagemap</code> CGI +program. Any directory or document type configured to use the handler +<code>imap-file</code> (using either <code><A +HREF="mod_mime.html#addhandler">AddHandler</A> </code> or <code><A +HREF="mod_mime.html#sethandler">SetHandler</A></code>) will be +processed by this module. + +<h2>Summary</h2> + +This module is in the default Apache distribution. The following directive will +activate files ending with <code>.map</code> as imagemap files: + +<blockquote><code>AddHandler imap-file map</code></blockquote> + +Note that the following is still supported: + + <blockquote><code>AddType application/x-httpd-imap map</code></blockquote> + +However, we are trying to phase out "magic MIME types" so we are deprecating +this method. + +<H2>New Features</H2> +The imagemap module adds some new features that were not +possible with previously distributed imagemap programs.<P> + +<ul> +<LI>URL references relative to the Referer: information. +<LI>Default <BASE> assignment through a new map directive +<code>base</code>. +<LI>No need for <code>imagemap.conf</code> file. +<LI>Point references. +<LI>Configurable generation of imagemap menus. +</ul> +<P> + +<h2>Configuration Directives</h2> +<ul> +<li><A HREF="#imapmenu">ImapMenu</A> +<li><A HREF="#imapdefault">ImapDefault</A> +<li><A HREF="#imapbase">ImapBase</A> +</ul> + + +<p> + +<h3><A name="imapmenu">ImapMenu</A></h3> +<strong>Syntax:</strong> ImapMenu <code>{none, formatted, semi-formatted, + unformatted}</code><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Module:</strong> mod_imap.c<br> +<strong>Compatibility:</strong> ImapMenu is only available in Apache +1.1 and later.<p> + +The ImapMenu directive determines the action taken if an imagemap file +is called without valid coordinates. +<dl> + <dt><code>none</code> + <dd>If ImapMenu is + <code>none</code>, no menu is generated, and the <code>default</code> + action is performed. + <dt><code>formatted</code> + <dd>A <code>formatted</code> menu is the simplest menu. Comments + in the imagemap file are ignored. A level one header is + printed, then an hrule, then the links each on a separate line. + The menu has a consistent, plain look close to that of + a directory listing. + <dt><code>semiformatted</code> + <dd>In the <code>semiformatted</code> menu, comments are printed + where they occur in the imagemap file. Blank lines are turned + into HTML breaks. No header or hrule is printed, but otherwise + the menu is the same as a <code>formatted</code> menu. + <dt><code>unformatted</code> + <dd>Comments are printed, blank lines are ignored. Nothing is + printed that does not appear in the imagemap file. All breaks + and headers must be included as comments in the imagemap file. + This gives you the most flexibility over the appearance of your + menus, but requires you to treat your map files as HTML instead + of plaintext. +</dl> + +<p> + +<h3><A name="imapdefault">ImapDefault</A></h3> +<strong>Syntax:</strong> ImapDefault <code>{error, nocontent, + map, referer, URL}</code><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Module:</strong> mod_imap.c<br> +<strong>Compatibility:</strong> ImapDefault is only available in Apache +1.1 and later.<p> + + +The ImapDefault directive sets the default <code>default</code> used in +the imagemap files. It's value is overridden by a <code>default</code> +directive within the imagemap file. If not present, the +<code>default</code> action is <code>nocontent</code>, which means +that a <code>204 No Content</code> is sent to the client. In this +case, the client should continue to display the original page. + +<p> + +<h3><A name="imapbase">ImapBase</A></h3> +<strong>Syntax:</strong> ImapBase <code>{map, referer, URL}</code><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Indexes<br> +<strong>Module:</strong> mod_imap.c<br> +<strong>Compatibility:</strong> ImapBase is only available in Apache +1.1 and later.<p> + +The ImapBase directive sets the default <code>base</code> used in +the imagemap files. It's value is overridden by a <code>base</code> +directive within the imagemap file. If not present, the +<code>base</code> defaults to <code>http://servername/</code>. + +<hr> +<p> + +<h2>Imagemap File</h2> +The lines in the imagemap files can have one of several formats: +<blockquote> +<code>directive value [x,y ...]</code><br> +<code>directive value "Menu text" [x,y ...]</code><br> +<code>directive value x,y ... "Menu text"</code><br> +</blockquote> +The directive is one of <code>base</code>, <code>default</code>, +<code>poly</code>, <code>circle</code>, <code>rect</code>, or +<code>point</code>. The value is an absolute or relative URL, or one +of the special values listed below. The coordinates are +<code>x,y</code> pairs separated by whitespace. The quoted text is +used as the text of the link if a imagemap menu is generated. Lines +beginning with '#' are comments. + +<h3>Imagemap File Directives</h3> +There are six directives allowed in the imagemap file. The directives +can come in any order, but are processed in the order they are found +in the imagemap file. +<dl> +<dt><code>base</code> Directive +<dd>Has the effect of <code><BASE href="value"></code>. The + non-absolute URLs of the map-file are taken relative to this value. + The <code>base</code> directive overrides ImapBase as set in a + .htaccess file or in the server configuration files. In the absence + of an ImapBase configuration directive, <code>base</code> defaults to + <code>http://server_name/</code>. <br> + <code>base_uri</code> is synonymous with <code>base</code>. Note that + a trailing slash on the URL is significant. +<p> +<dt><code>default</code> Directive +<dd>The action taken if the coordinates given do not fit any of the + <code>poly</code>, <code>circle</code> or <code>rect</code> + directives, and there are no <code>point</code> directives. Defaults + to <code>nocontent</code> in the absence of an ImapDefault + configuration setting, causing a status code of <code>204 No + Content</code> to be returned. The client should keep the same + page displayed. +<p> +<dt><code>poly</code> Directive +<dd>Takes three to one-hundred points, and is obeyed if the user selected + coordinates fall within the polygon defined by these points. +<p> +<dt><code>circle</code> +<dd>Takes the center coordinates of a circle and a point on the circle. Is + obeyed if the user selected point is with the circle. +<p> +<dt><code>rect</code> Directive +<dd>Takes the coordinates of two opposing corners of a rectangle. Obeyed + if the point selected is within this rectangle. +<p> +<dt><code>point</code> Directive +<dd>Takes a single point. The point directive closest to the user + selected point is obeyed if no other directives are satisfied. + Note that <code>default</code> will not be followed if a + <code>point</code> directive is present and valid coordinates are + given. +</dl> + + + +<h3>Values</h3> +The values for each of the directives can any of the following: +<dl> + <dt>a URL + <dd>The URL can be relative or absolute URL. Relative URLs can + contain '..' syntax and will be resolved relative to the + <code>base</code> value. <br> + <code>base</code> itself will not resolved according to the current + value. A statement <code>base mailto:</code> will work properly, though. +<p> + <dt><code>map</code> + <dd>Equivalent to the URL of the imagemap file itself. No + coordinates are sent with this, so a menu will be generated + unless ImapMenu is set to 'none'. +<p> + <dt><code>menu</code> + <dd>Synonymous with <code>map</code>. +<p> + <dt><code>referer</code> + <dd>Equivalent to the URL of the referring document. + Defaults to <code>http://servername/</code> if no Referer: + header was present. +<p> + <dt><code>nocontent</code> + <dd>Sends a status code of <code>204 No Content</code>, + telling the client to keep the same page displayed. Valid for + all but <code>base</code>. +<p> + <dt><code>error</code> + <dd>Fails with a <code>500 Server Error</code>. Valid for all but + <code>base</code>, but sort of silly for anything but + <code>default</code>. +</dl> + +<h3>Coordinates</h3> +<dl> + <dt><code>0,0 200,200</code> + <dd>A coordinate consists of an <tt>x</tt> and a <tt>y</tt> value + separated by a comma. The coordinates are separated from each other + by whitespace. To accommodate the way Lynx handles imagemaps, should a + user select the coordinate <code>0,0</code>, it is as if + no coordinate had been selected. +</dl> + +<h3>Quoted Text</h3> +<dl> + <dt><code>"Menu Text"</code> + <dd>After the value or after the coordinates, the line optionally may + contain text within double quotes. This string is used as the + text for the link if a menu is generated:<br> + <code><a href="http://foo.com/">Menu text</a></code><br> + If no quoted text is present, the name of the link will be used + as the text:<br> + <code><a href="http://foo.com/">http://foo.com</a></code><br> + It is impossible to escape double quotes within this text. +</dl> + +<hr> + +<h2>Example Mapfile</h2> +<blockquote><code> +#Comments are printed in a 'formatted' or 'semiformatted' menu. <br> +#And can contain html tags. <hr> <br> +base referer <br> +poly map "Could I have a menu, please?" 0,0 0,10 10,10 10,0 <br> +rect .. 0,0 77,27 "the directory of the referer"<br> +circle http://www.inetnebr.com/lincoln/feedback/ 195,0 305,27 <br> +rect another_file "in same directory as referer" 306,0 419,27 <br> +point http://www.zyzzyva.com/ 100,100 <br> +point http://www.tripod.com/ 200,200 <br> +rect mailto:nate@tripod.com 100,150 200,0 "Bugs?" <br> +</code></blockquote> +<P> + +<h2>Referencing your mapfile</h2> +<blockquote><code> +<A HREF="/maps/imagmap1.map"> <br> +<IMG ISMAP SRC="/images/imagemap1.gif"> <br> +</A> +</code></blockquote><p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_include.html b/usr.sbin/httpd/htdocs/manual/mod/mod_include.html new file mode 100644 index 00000000000..3ce515b26d8 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_include.html @@ -0,0 +1,398 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_include</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_include</H1> + +This module is contained in the <code>mod_include.c</code> file, and +is compiled in by default. It provides for server-parsed html +documents. Several directives beyond the original NCSA definition have been +included in Apache 1.2 - these are flagged below with the phrase +"Apache 1.2 and above". Of particular significance are the new flow +control directives documented at the bottom. + +<H2>Enabling Server-Side Includes</H2> + +Any document with handler of "server-parsed" will be parsed by this +module, if the <CODE>Includes</CODE> option is set. If documents +containing server-side include directives are given the extension +.shtml, the following directives will make Apache parse them and +assign the resulting document the mime type of <CODE>text/html</CODE>: + +<PRE> +AddType text/html .shtml +AddHandler server-parsed .shtml +</PRE> + +The following directive must be given for the directories containing +the shtml files (typically in a <CODE><Directory></CODE> section, +but this directive is also valid .htaccess files if <CODE>AllowOverride +Options</CODE> is set): + +<PRE> +Options +Includes +</PRE> + +Alternatively the <A HREF="#xbithack"><CODE>XBitHack</CODE></A> +directive can be used to parse normal (<CODE>text/html</CODE>) files, +based on file permissions. <P> + +For backwards compatibility, documents with mime type +<code>text/x-server-parsed-html</code> or +<code>text/x-server-parsed-html3</code> will also be parsed +(and the resulting output given the mime type <code>text/html</code>). + +<h2>Basic Elements</h2> + +The document is parsed as an HTML document, with special commands embedded +as SGML comments. A command has the syntax: + +<blockquote><code> +<!--#</code><em>element attribute=value attribute=value ...</em> +<code> --> +</code></blockquote> + +The value will often be enclosed in double quotes; many commands only allow +a single attribute-value pair. Note that the comment terminator +(<SAMP>--></SAMP>) should be preceded by whitespace to ensure that it +isn't considered part of an SSI token. +<p> +The allowed elements are:<p> + +<dl> + +<dt><strong>config</strong> +<dd> +This command controls various aspects of the parsing. The valid attributes +are: +<dl> +<dt>errmsg +<dd>The value is a message that is sent back to the client if an error occurs +whilst parsing the document. +<dt>sizefmt +<dd>The value sets the format to be used which displaying the size of a file. +Valid values are <code>bytes</code> for a count in bytes, or +<code>abbrev</code> for a count in Kb or Mb as appropriate. +<dt>timefmt +<dd>The value is a string to be used by the <code>strftime(3)</code> library +routine when printing dates. +</dl> + +<dt><strong>echo</strong> +<dd> +This command prints one of the include variables, defined below. +If the variable is unset, it is printed as <code>(none)</code>. +Any dates printed are subject to the currently configured <code>timefmt</code>. +Attributes: +<dl> +<dt>var +<dd>The value is the name of the variable to print. +</dl> + +<dt><strong>exec</strong> +<dd> +The exec command executes a given shell command or CGI script. +The IncludesNOEXEC <A HREF="core.html#options">Option</A> disables this command +completely. The valid attributes are: +<dl> +<dt>cgi +<dd> +The value specifies a (%-encoded) URL relative path to the CGI script. +If the path does not begin with a (/), then it is taken to be relative to +the current document. The document referenced by this path is invoked +as a CGI script, even if the server would not normally recognize it as +such. However, the directory containing the script must be enabled for +CGI scripts (with <A HREF="mod_alias.html#scriptalias">ScriptAlias</A> +or the ExecCGI <A HREF="core.html#options">Option</A>).<p> +The CGI script is given the PATH_INFO and query string (QUERY_STRING) of the +original request from the client; these cannot be specified in the URL path. +The include variables will be available to the script in addition to the +standard <A HREF="mod_cgi.html">CGI</A> environment.<p> +If the script returns a Location: header instead of output, then this +will be translated into an HTML anchor.<p> +The <code>include virtual</code> element should be used in preference to +<code>exec cgi</code>. +<dt>cmd +<dd>The server will execute the given string using <code>/bin/sh</code>. +The include variables are available to the command. +</dl> + +<dt><strong>fsize</strong> +<dd> +This command prints the size of the specified file, subject to the +<code>sizefmt</code> format specification. Attributes: +<dl> +<dt>file +<dd>The value is a path relative to the directory containing the current +document being parsed. +<dt>virtual +<dd>The value is a (%-encoded) URL-path relative to the current document being +parsed. If it does not begin with a slash (/) then it is taken to be relative +to the current document. +</dl> + +<dt><strong>flastmod</strong> +<dd> +This command prints the last modification date of the specified file, +subject to the <code>timefmt</code> format specification. The attributes are +the same as for the <code>fsize</code> command. + +<dt><strong>include</strong> +<dd> +This command inserts the text of another document or file into the parsed +file. Any included file is subject to the usual access control. If the +directory containing the parsed file has the +<A HREF="core.html#options">Option</A> +IncludesNOEXEC set, and the including the document would cause a program +to be executed, then it will not be included; this prevents the execution of +CGI scripts. Otherwise CGI scripts are invoked as normal using the complete +URL given in the command, including any query string. +<!--%plaintext <?INDEX CGI scripts, {\tt include} element and> --> +<p> + +An attribute defines the location of the document; the inclusion is done for +each attribute given to the include command. The valid attributes are: +<dl> +<dt>file +<dd>The value is a path relative to the directory containing the current +document being parsed. It cannot contain <code>../</code>, nor can it be an +absolute path. The <code>virtual</code> attribute should always be used +in preference to this one. +<dt>virtual +<dd>The value is a (%-encoded) URL relative to the current document being +parsed. The URL cannot contain a scheme or hostname, only a path and +an optional query string. If it does not begin with a slash (/) then it +is taken to be relative to the current document. +</dl> +A URL is constructed from the attribute, and the output the server +would return if the URL were accessed by the client is included in the parsed +output. Thus included files can be nested. + +<dt><strong>printenv</strong> +<dd>This prints out a listing of all existing variables and their values. + No attributes. +<dd>For example: <code><!--#printenv --></code> +<dd>Apache 1.2 and above. + +<dt><strong>set</strong> +<dd>This sets the value of a variable. Attributes: +<dl> +<dt>var +<dd>The name of the variable to set. +<dt>value +<dd>The value to give a variable. +</dl> +For example: + <CODE><!--#set var="category" value="help" --></CODE> +<dd>Apache 1.2 and above. + +</dl> + +<h2>Include Variables</h2> + +In addition to the variables in the standard CGI environment, these are +available for the <code>echo</code> command, for <code>if</code> and +<code>elif</code>, and to any program invoked by the document. + +<dl> +<dt>DATE_GMT +<dd>The current date in Greenwich Mean Time. +<dt>DATE_LOCAL +<dd>The current date in the local time zone. +<dt>DOCUMENT_NAME +<dd>The filename (excluding directories) of the document requested by the +user. +<dt>DOCUMENT_URI +<dd>The (%-decoded) URL path of the document requested by the user. Note that +in the case of nested include files, this is <em>not</em> then URL for the +current document. +<dt>LAST_MODIFIED +<dd>The last modification date of the document requested by the user. +</dl> +<p> + +<H2>Variable Substitution</H2> +<P> Variable substitution is done within quoted strings in most cases + where they may reasonably occur as an argument to an SSI directive. + This includes the + <SAMP>config</SAMP>, + <SAMP>exec</SAMP>, + <SAMP>flastmod</SAMP>, + <SAMP>fsize</SAMP>, + <SAMP>include</SAMP>, and + <SAMP>set</SAMP> + directives, as well as the arguments to conditional operators. + You can insert a literal dollar sign into the string using backslash + quoting: + +<PRE> + <!--#if expr="$a = \$test" --> +</PRE> + +<P> If a variable reference needs to be substituted in the middle of a + character sequence that might otherwise be considered a valid + identifier in its own right, it can be disambiguated by enclosing + the reference in braces, <EM>à la</EM> shell substitution: + +<PRE> + <!--#set var="Zed" value="${REMOTE_HOST}_${REQUEST_METHOD}" --> +</PRE> + +<P> This will result in the <SAMP>Zed</SAMP> variable being set to + "<SAMP>X_Y</SAMP>" if <SAMP>REMOTE_HOST</SAMP> is + "<SAMP>X</SAMP>" and <SAMP>REQUEST_METHOD</SAMP> is + "<SAMP>Y</SAMP>". + +<P> EXAMPLE: the below example will print "in foo" if the DOCUMENT_URI is +/foo/file.html, "in bar" if it is /bar/file.html and "in neither" +otherwise: +<PRE> + <!--#if expr="\"$DOCUMENT_URI\" = \"/foo/file.html\"" --> + in foo + <!--#elif expr="\"$DOCUMENT_URI\" = \"/bar/file.html\"" --> + in bar + <!--#else --> + in neither + <!--#endif --> +</PRE> + +<H2>Flow Control Elements</H2> + +These are available in Apache 1.2 and above. The basic flow control +elements are: + +<PRE> + <!--#if expr="<I>test_condition</I>" --> + <!--#elif expr="<I>test_condition</I>" --> + <!--#else --> + <!--#endif --> +</PRE> + +<P> The <B><CODE>if</CODE></B> element works like an + if statement in a programming language. The test condition + is evaluated and if the result is true, then the text until + the next <B><CODE>elif</CODE></B>, <B><CODE>else</CODE></B>. + or <B><CODE>endif</CODE></B> element is included in the + output stream. + +<P> The <B><CODE>elif</CODE></B> or <B><CODE>else</CODE></B> + statements are be used the put text into the output stream + if the original test_condition was false. These elements + are optional. + +<P> The <B><CODE>endif</CODE></B> element ends the + <B><CODE>if</CODE></B> element and is required. + +<P> <I>test_condition</I> is one of the following: + +<DL> + +<DT><I>string</I><DD>true if <I>string</I> is not empty + +<DT><I>string1</I> = <I>string2</I><BR> + <I>string1</I> != <I>string2</I> + +<DD>Compare string1 with string 2. If string2 has the form <I>/string/</I> + than it is compared as a regular expression. + Regular expressions have the same syntax as those found in the + Unix egrep command. + +<DT>( <I>test_condition</I> ) + <DD>true if <I>test_condition</I> is true +<DT>! <I>test_condition</I> + <DD>true if <I>test_condition</I> is false +<DT><I>test_condition1</I> && <I>test_condition2</I> + <DD>true if both <I>test_condition1</I> and + <I>test_condition2</I> are true +<DT><I>test_condition1</I> || <I>test_condition2</I> + <DD>true if either <I>test_condition1</I> or + <I>test_condition2</I> is true +</DL> + +<P> "<I>=</I>" and "<I>!=</I>" bind more tightly than "<I>&&</I>" and + "<I>||</I>". + "<I>!</I>" binds most tightly. Thus, the following are equivalent: + +<PRE> + <!--#if expr="$a = test1 && $b = test2" --> + <!--#if expr="($a = test1) && ($b = test2)" --> +</PRE> + +<P> Anything that's not recognized as a variable or an operator is + treated as a string. Strings can also be quoted: <I>'string'</I>. + Unquoted strings can't contain whitespace (blanks and tabs) + because it is used to separate tokens such as variables. If + multiple strings are found in a row, they are concatenated using + blanks. So, + +<PRE> + <I>string1 string2</I> results in <I>string1 string2</I> + <I>'string1 string2'</I> results in <I>string1 string2</I> +</PRE> + +<hr> +<h2>Directives</h2> +<ul> +<li><A HREF="#xbithack">XBitHack</A> +</ul> +<hr> + + +<h2><A name="xbithack">XBitHack</A></h2> +<!--%plaintext <?INDEX {\tt XBitHack} directive> --> +<strong>Syntax:</strong> XBitHack <em>status</em><br> +<strong>Default:</strong> <code>XBitHack off</code><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> Options<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_include<p> + +The XBitHack directives controls the parsing of ordinary html documents. +This directive only affects files associated with the MIME type +<CODE>text/html</CODE>. +<em>Status</em> can have the following values: +<dl> +<dt>off +<dd>No special treatment of executable files. +<dt>on +<dd>Any file that has the user-execute bit set will be treated as a +server-parsed html document. +<dt>full +<dd>As for <code>on</code> but also test the group-execute bit. If it +is set, then set the Last-modified date of the returned file to be the +last modified time of the file. If it is not set, then no last-modified date +is sent. Setting this bit allows clients and proxies to cache the result of +the request. +<p><b>Note:</b> you would not want to use this, for example, when you +<code>#include</code> a CGI that produces different output on each hit +(or potentially depends on the hit). +</dl> +<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_info.html b/usr.sbin/httpd/htdocs/manual/mod/mod_info.html new file mode 100644 index 00000000000..8bb49e28983 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_info.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Apache module mod_info</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Module mod_info</h1> + +This module is contained in the <code>mod_info.c</code> file. It +provides a comprehensive overview of the server configuration +including all installed modules and directives in the configuration +files. This module is not compiled into the +server by default. It is only available in Apache 1.1 and later. To +enable it, add the following line to the server build Configuration +file, and rebuild the server: + +<PRE> +Module info_module mod_info.o +</PRE> + +<HR> +<P> +To configure it, add the following to your <code>access.conf</code> file. + +<PRE> +<Location /server-info> +SetHandler server-info +</Location> +</PRE> + +You may wish to add a +<A + HREF="core.html#limit" +><Limit></A> +clause inside the +<A + HREF="core.html#location" +>location</A> +directive to limit access to your server configuration information.<p> +Once configured, the server information is obtained by accessing +<tt>http://your.host.dom/server-info</tt><p> +<BLOCKQUOTE> + <STRONG> + Note that the configuration files are read by the module at run-time, + and therefore the display may <EM>not</EM> reflect the running + server's active configuration if the files have been changed since the + server was last reloaded. Also, the configuration files must be + readable by the user as which the server is running (see the + <A + HREF="core.html#user" + ><SAMP>User</SAMP></A> + directive), or else the directive settings will not be listed. + <P> + It should also be noted that if <SAMP>mod_info</SAMP> is compiled into + the server, its handler capability is available in <EM>all</EM> + configuration files, including <EM>per</EM>-directory files + (<EM>e.g.</EM>, <SAMP>.htaccess</SAMP>). This may have + security-related ramifications for your site. + </P> + </STRONG> +</BLOCKQUOTE> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_log_agent.html b/usr.sbin/httpd/htdocs/manual/mod/mod_log_agent.html new file mode 100644 index 00000000000..a0913517dc0 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_log_agent.html @@ -0,0 +1,74 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Module mod_log_agent</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_log_agent</h1> + +This module is contained in the <code>mod_log_agent.c</code> file, and is not +compiled in by default. It provides for logging of the client user agents. + + +<ul> +<li><A HREF="#agentlog">AgentLog</A> +</ul> +<hr> + + +<h2><A name="agentlog">AgentLog</A></h2> +<!--%plaintext <?INDEX {\tt AgentLog} directive> --> +<strong>Syntax:</strong> AgentLog <em>file-pipe</em><br> +<strong>Default:</strong> <code>AgentLog logs/agent_log</code><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_log_agent<p> + +The AgentLog directive sets the name of the file to which the server will +log the UserAgent header of incoming requests. <em>File-pipe</em> is one +of +<dl><dt>A filename +<dd>A filename relative to the <A HREF="core.html#serverroot">ServerRoot</A>. +<dt> `|' followed by a command +<dd>A program to receive the agent log information on its standard input. +Note the a new program will not be started for a VirtualHost if it inherits +the AgentLog from the main server. +</dl> +<strong>Security:</strong> if a program is used, then it will be +run under the user who started httpd. This will be root if the server +was started by root; be sure that the program is secure.<p> + +<strong>Security:</strong> See the <A +HREF="../misc/security_tips.html">security tips</A> document for +details on why your security could be compromised if the directory +where logfiles are stored is writable by anyone other than the user +that starts the server.<P> + +This directive is provided for compatibility with NCSA 1.4.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_log_common.html b/usr.sbin/httpd/htdocs/manual/mod/mod_log_common.html new file mode 100644 index 00000000000..d4d4e5deec4 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_log_common.html @@ -0,0 +1,114 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_log_common</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_log_common</h1> + +This module is contained in the <code>mod_log_common.c</code> file, +and is compiled in by default. It provides for logging of the requests +made to the server using the Common Logfile Format. This module has +been replaced by mod_log_config in Apache 1.2 + +<h2>Log file format</h2> +The log file contains a separate line for each request. A line is composed +of several tokens separated by spaces: +<blockquote> +host ident authuser date request status bytes +</blockquote> +If a token does not have a value then it is represented by a hyphen (-). +The meanings and values of these tokens are as follows: +<dl> +<dt>host +<dd>The fully-qualified domain name of the client, or its IP number if the +name is not available. +<dt>ident +<dd>If <A HREF="core.html#identitycheck">IdentityCheck</A> is enabled and the +client machine runs identd, then this is the identity information reported +by the client. +<dt>authuser +<dd>If the request was for an password protected document, then this is +the userid used in the request. +<dt>date +<dd>The date and time of the request, in the following format: +<dl><dd><blockquote><code> date = [day/month/year:hour:minute:second zone] <br> +day = 2*digit<br> +month = 3*letter<br> +year = 4*digit<br> +hour = 2*digit<br> +minute = 2*digit<br> +second = 2*digit<br> +zone = (`+' | `-') 4*digit</code></blockquote></dl> +<dt>request +<dd>The request line from the client, enclosed in double quotes +(<code>"</code>). +<dt>status +<dd>The three digit status code returned to the client. +<dt>bytes +<dd>The number of bytes in the object returned to the client, not including +any headers. +</dl> + + +<h2>Directives</h2> +<ul> +<li><A HREF="#transferlog">TransferLog</A> +</ul> +<hr> + + +<h2><A name="transferlog">TransferLog</A></h2> +<!--%plaintext <?INDEX {\tt TransferLog} directive> --> +<strong>Syntax:</strong> TransferLog <em>file-pipe</em><br> +<strong>Default:</strong> <code>TransferLog logs/transfer_log</code><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_log_common<p> + +The TransferLog directive sets the name of the file to which the server will +log the incoming requests. <em>File-pipe</em> is one +of +<dl><dt>A filename +<dd>A filename relative to the <A HREF="core.html#serverroot">ServerRoot</A>. +<dt> `|' followed by a command +<dd>A program to receive the agent log information on its standard input. +Note the a new program will not be started for a VirtualHost if it inherits +the TransferLog from the main server. +</dl> +<strong>Security:</strong> if a program is used, then it will be +run under the user who started httpd. This will be root if the server +was started by root; be sure that the program is secure.<p> + +<strong>Security:</strong> See the <A +HREF="../misc/security_tips.html">security tips</A> document for +details on why your security could be compromised if the directory +where logfiles are stored is writable by anyone other than the user +that starts the server.<P> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_log_config.html b/usr.sbin/httpd/htdocs/manual/mod/mod_log_config.html new file mode 100644 index 00000000000..a670e824458 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_log_config.html @@ -0,0 +1,276 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_log_config</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_log_config</h1> + +This module is contained in the <code>mod_log_config.c</code> file, +and is compiled in by default in Apache 1.2. mod_log_config replaces +mod_log_common in Apache 1.2. Prior to version 1.2, mod_log_config was +an optional module. It provides for logging of the requests made to +the server, using the Common Log Format or a user-specified format. + +<h2>Summary</h2> + +Three directives are provided by this module: <code>TransferLog</code> +to create a log file, <code>LogFormat</code> to set a custom format, +and <code>CustomLog</code> to define a log file and format in one go. +The <code>TransferLog</code> and <code>CustomLog</code> directives can +be used multiple times in each server to cause each request to be +logged to multiple files. +<P> + +<h3>Compatibility notes</h3> + +<ul> +<li>This module is based on mod_log_config distributed with +previous Apache releases, now updated to handle multiple logs. +There is now no need to re-configure Apache to use configuration log +formats. + +<li>The module also implements the <code>CookieLog</code> directive, +used to log user-tracking information created by <a +href="mod_usertrack.html">mod_usertrack</a>. The use of +<code>CookieLog</code> is deprecated, and a <code>CustomLog</code> +should be defined to log user-tracking information instead. + +</ul> + +<h2>Log File Formats</h2> + +Unless told otherwise with <tt>LogFormat</tt> the log files created by +<tt>TransferLog</tt> will be in standard "Common Log Format" +(CLF). The contents of each line in a CLF file are explained +below. Alternatively, the log file can be customized (and if multiple +log files are used, each can have a different format). Custom formats +are set with <code>LogFormat</code> and <code>CustomLog</code>. + +<h3>Common Log Format</h3> + +The Common Log Format (CLF) file contains a separate line for each +request. A line is composed of several tokens separated by spaces: + +<blockquote> +host ident authuser date request status bytes +</blockquote> +If a token does not have a value then it is represented by a hyphen (-). +The meanings and values of these tokens are as follows: +<dl> +<dt>host +<dd>The fully-qualified domain name of the client, or its IP number if the +name is not available. +<dt>ident +<dd>If <A HREF="core.html#identitycheck">IdentityCheck</A> is enabled and the +client machine runs identd, then this is the identity information reported +by the client. +<dt>authuser +<dd>If the request was for an password protected document, then this is +the userid used in the request. +<dt>date +<dd>The date and time of the request, in the following format: +<dl><dd><blockquote><code> date = [day/month/year:hour:minute:second zone] <br> +day = 2*digit<br> +month = 3*letter<br> +year = 4*digit<br> +hour = 2*digit<br> +minute = 2*digit<br> +second = 2*digit<br> +zone = (`+' | `-') 4*digit</code></blockquote></dl> +<dt>request +<dd>The request line from the client, enclosed in double quotes +(<code>"</code>). +<dt>status +<dd>The three digit status code returned to the client. +<dt>bytes +<dd>The number of bytes in the object returned to the client, not including +any headers. +</dl> + +<h3><A NAME="formats">Custom Log Formats</A></h3> + +The format argument to the <code>LogFormat</code> and +<code>CustomLog</code> is a string. This string is logged to the log +file for each request. It can contain literal characters copied into +the log files, and `%' directives which are replaced in the log file +by the values as follows: + +<PRE> +%...b: Bytes sent, excluding HTTP headers. +%...f: Filename +%...{FOOBAR}e: The contents of the environment variable FOOBAR +%...h: Remote host +%...{Foobar}i: The contents of Foobar: header line(s) in the request + sent to the server. +%...l: Remote logname (from identd, if supplied) +%...{Foobar}n: The contents of note "Foobar" from another module. +%...{Foobar}o: The contents of Foobar: header line(s) in the reply. +%...p: The port the request was served to +%...P: The process ID of the child that serviced the request. +%...r: First line of request +%...s: Status. For requests that got internally redirected, this + is status of the *original* request --- %...>s for the last. +%...t: Time, in common log format time format +%...{format}t: The time, in the form given by format, which should + be in strftime(3) format. +%...T: The time taken to serve the request, in seconds. +%...u: Remote user (from auth; may be bogus if return status (%s) is 401) +%...U: The URL path requested. +%...v: The name of the server (i.e. which virtual host?) +</PRE> + +The `...' can be nothing at all (e.g. <code>"%h %u %r %s %b"</code>), or it can +indicate conditions for inclusion of the item (which will cause it +to be replaced with `-' if the condition is not met). Note that +there is no escaping performed on the strings from %r, %...i and +%...o; some with long memories may remember that I thought this was +a bad idea, once upon a time, and I'm still not comfortable with +it, but it is difficult to see how to `do the right thing' with all +of `%..i', unless we URL-escape everything and break with CLF. + +<P> + +The forms of condition are a list of HTTP status codes, which may +or may not be preceded by `!'. Thus, `%400,501{User-agent}i' logs +User-agent: on 400 errors and 501 errors (Bad Request, Not +Implemented) only; `%!200,304,302{Referer}i' logs Referer: on all +requests which did <b>not</b> return some sort of normal status. + +<P> + +Note that the common log format is defined by the string <code>"%h %l +%u %t \"%r\" %s %b"</code>, which can be used as the basis for +extending for format if desired (e.g. to add extra fields at the end). +NCSA's extended/combined log format would be <code>"%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\""</code>. + +<h2>Using Multiple Log Files</h2> + +The <code>TransferLog</code> and <code>CustomLog</code> directives can +be given more than once to log requests to multiple log files. Each +request will be logged to all the log files defined by either of these +directives. + +<h3>Use with Virtual Hosts</h3> + +If a <VirtualHost> section does not contain any +<tt>TransferLog</tt> or <tt>CustomLog</tt> directives, the +logs defined for the main server will be used. If it does +contain one or more of these directives, requests serviced by +this virtual host will only be logged in the log files defined +within its definition, not in any of the main server's log files. +See the examples below. +<p> + +<h2>Security Considerations</h2> + +See the <A HREF="../misc/security_tips.html">security tips</A> document +for details on why your security could be compromised if the directory +where logfiles are stored is writable by anyone other than the user +that starts the server. +<p> +<h2>Directives</h2> + +<ul> +<li><A HREF="#cookielog">CookieLog</A> +<LI><A HREF="#customlog">CustomLog</A> +<li><A HREF="#logformat">LogFormat</A> +<li><A HREF="#transferlog">TransferLog</A> +</ul> +<hr> + + +<h2><A name="cookielog">CookieLog</A></h2> +<!--%plaintext <?INDEX {\tt CookieLog} directive> --> +<strong>Syntax:</strong> CookieLog <em>filename</em><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Module:</strong> mod_cookies<br> +<strong>Compatibility:</strong> Only available in Apache 1.2 and above<p> + +The CookieLog directive sets the filename for logging of cookies. +The filename is relative to the <A +HREF="core.html#serverroot">ServerRoot</A>. This directive is included +only for compatibility with <a +href="mod_cookies.html">mod_cookies</a>, and is deprecated. +<p> + +<H2><A NAME="customlog">CustomLog</A></H2> +<STRONG>Syntax:</STRONG> CustomLog <em>file-pipe</em> <em>format</em><BR> +<STRONG>Context:</STRONG> server config, virtual host<BR> +<STRONG>Status:</STRONG> Base<BR> +<STRONG>Module:</STRONG> mod_log_config<P> + +The first argument is the filename to log to. This is used +exactly like the argument to <tt>TransferLog</tt>, that is, +it is either a full path, or relative to the current +server root. <p> + +The format argument specifies a format for each line of the log file. +The options available for the format are exactly the same as for +the argument of the <tt>LogFormat</tt> directive. If the format +includes any spaces (which it will do in almost all cases) it +should be enclosed in double quotes. + +<h2><A name="logformat">LogFormat</A></h2> +<!--%plaintext <?INDEX {\tt LogFormat} directive> --> +<strong>Syntax:</strong> LogFormat <em>string</em><br> +<strong>Default:</strong> <code>LogFormat "%h %l %u %t \"%r\" +%s %b"</code><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_log_config<p> + +This sets the format of the logfile. See <A HREF="#formats"> +Custom Log Formats</A> for details on the format arguments.<p><hr> + + +<h2><A name="transferlog">TransferLog</A></h2> +<!--%plaintext <?INDEX {\tt TransferLog} directive> --> +<strong>Syntax:</strong> TransferLog <em>file-pipe</em><br> +<strong>Default:</strong> none<br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_log_config<p> + +The TransferLog directive adds a log file in Common Log Format. +<em>File-pipe</em> is one +of +<dl><dt>A filename +<dd>A filename relative to the <A HREF="core.html#serverroot">ServerRoot</A>. +<dt> `|' followed by a command +<dd>A program to receive the agent log information on its standard input. +Note the a new program will not be started for a VirtualHost if it inherits +the TransferLog from the main server. +</dl> +<strong>Security:</strong> if a program is used, then it will be +run under the user who started httpd. This will be root if the server +was started by root; be sure that the program is secure.<p> + + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_log_referer.html b/usr.sbin/httpd/htdocs/manual/mod/mod_log_referer.html new file mode 100644 index 00000000000..ab86f2967a8 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_log_referer.html @@ -0,0 +1,101 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_log_referer</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_log_referer</h1> + +This module is contained in the <code>mod_log_referer.c</code> file, and is not +compiled in by default. It provides for logging of the documents which +reference documents on the server. + +<h2>Log file format</h2> +The log file contains a separate line for each refer. Each line has the +format +<blockquote><em>uri</em> <code>-></code> <em>document</em></blockquote> +where <em>uri</em> is the (%-escaped) URI for the document that references +the one requested by the client, and <em>document</em> is the (%-decoded) +local URL to the document being referred to. + + +<h2>Directives</h2> +<ul> +<li><A HREF="#refererignore">RefererIgnore</A> +<li><A HREF="#refererlog">RefererLog</A> +</ul> +<hr> + + +<h2><A name="refererignore">RefererIgnore</A></h2> +<!--%plaintext <?INDEX {\tt RefererIgnore} directive> --> +<strong>Syntax:</strong> RefererIgnore <em>string string ...</em><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_log_referer<p> + +The RefererIgnore directive adds to the list of strings to ignore in +Referer headers. If any of the strings in the list is contained in +the Referer header, then no referrer information will be logged for the +request. Example: +<blockquote><code>RefererIgnore www.ncsa.uiuc.edu</code></blockquote> +This avoids logging references from www.ncsa.uiuc.edu. +<p><hr> + + +<h2><A name="refererlog">RefererLog</A></h2> +<!--%plaintext <?INDEX {\tt RefererLog} directive> --> +<strong>Syntax:</strong> RefererLog <em>file-pipe</em><br> +<strong>Default:</strong> <code>RefererLog logs/referer_log</code><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Extension<br> +<strong>Module:</strong> mod_log_referer<p> + +The RefererLog directive sets the name of the file to which the server will +log the Referer header of incoming requests. <em>File-pipe</em> is one +of +<dl><dt>A filename +<dd>A filename relative to the <A HREF="core.html#serverroot">ServerRoot</A>. +<dt> `|' followed by a command +<dd>A program to receive the referrer log information on its standard input. +Note the a new program will not be started for a VirtualHost if it inherits +the RefererLog from the main server. +</dl> +<strong>Security:</strong> if a program is used, then it will be +run under the user who started httpd. This will be root if the server +was started by root; be sure that the program is secure.<p> + +<strong>Security:</strong> See the <A +HREF="../misc/security_tips.html">security tips</A> document for +details on why your security could be compromised if the directory +where logfiles are stored is writable by anyone other than the user +that starts the server.<P> + +This directive is provided for compatibility with NCSA 1.4.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_mime.html b/usr.sbin/httpd/htdocs/manual/mod/mod_mime.html new file mode 100644 index 00000000000..aaf392a3e4a --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_mime.html @@ -0,0 +1,247 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_mime</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_mime</h1> + +This module is contained in the <code>mod_mime.c</code> file, and is +compiled in by default. It provides for determining the types of files +from the filename. + +<h2>Summary</h2> + +This module is used to determine various bits of "meta information" +about documents. This information relates to the content of the +document and is returned to the browser or used in content-negotiation +within the server. In addition, a "handler" can be set for a document, +which determines how the document will be processed within the server. + +<P> + +The directives <A HREF="#addencoding">AddEncoding</A>, <A +HREF="#addhandler">AddHandler</A>, <A +HREF="#addlanguage">AddLanguage</A> and <A HREF="#addtype">AddType</A> +are all used to map file extensions onto the meta-information for that +file. Respectively they set the content-encoding, handler, +content-language and mime-type (content-type) of documents. The +directive <A HREF="#typesconfig">TypesConfig</A> is used to specify a +file which also maps extensions onto mime types. The directives <A +HREF="#forcetype">ForceType</A> and <A +HREF="#sethandler">SetHandler</A> are used to associated all the files +in a given location (e.g. a particular directory) onto a particular +mime type or handler. + +<P> + +Files can have more than one extension, and the order of the +extensions is normally irrelevant. For example, if the file +<CODE>welcome.html.fr</CODE> maps onto content type text/html and +language French then the file <CODE>welcome.fr.html</CODE> will map +onto exactly the same information. The only exception to this is if an +extension is given which Apache does not know how to handle. In this +case it will "forget" about any information it obtained from +extensions to the left of the unknown extension. So, for example, if +the extensions fr and html are mapped to the appropriate language and +type but extension xxx is not assigned to anything, then the file +<CODE>welcome.fr.xxx.html</CODE> will be associated with content-type +text/html but <i>no</i> language. + +<P> + +<h2> Directives</h2> +<ul> +<li><A HREF="#addencoding">AddEncoding</A> +<li><A HREF="#addhandler">AddHandler</A> +<li><A HREF="#addlanguage">AddLanguage</A> +<li><A HREF="#addtype">AddType</A> +<li><A HREF="#forcetype">ForceType</A> +<li><A HREF="#sethandler">SetHandler</A> +<li><A HREF="#typesconfig">TypesConfig</A> +</ul> +<hr> + + +<h2><A name="addencoding">AddEncoding</A></h2> +<!--%plaintext <?INDEX {\tt AddEncoding} directive> --> +<strong>Syntax:</strong> AddEncoding <em>mime-enc extension extension...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> FileInfo<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime<p> + +The AddEncoding directive adds to the list of filename extensions which +filenames may end in for the specified encoding type. <em>Mime-enc</em> +is the mime encoding to use for documents ending in <em>extension</em>. +Example: +<blockquote><code> +AddEncoding x-gzip gz<br> +AddEncoding x-compress Z +</code></blockquote> + +This will cause files ending in .gz to be marked as encoded using the x-gzip +encoding, and .Z files to be marked as encoded with x-compress.<p><hr> + +<h2><a name="addhandler">AddHandler</a></h2> + +<strong>Syntax:</strong> AddHandler <em>handler-name extension extension...</em><br> +<strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime<br> +<strong>Compatibility:</strong> AddHandler is only available in Apache +1.1 and later<p> + +<p>AddHandler maps the filename extensions <em>extension</em> to the +<a href="../handler.html">handler</a> +<em>handler-name</em>. For example, to activate CGI scripts +with the file extension "<code>.cgi</code>", you might use: +<pre> + AddHandler cgi-script cgi +</pre> + +<p>Once that has been put into your srm.conf or httpd.conf file, any +file ending with "<code>.cgi</code>" will be treated as a CGI +program.</p> +<HR> + +<h2><A name="addlanguage">AddLanguage</A></h2> +<!--%plaintext <?INDEX {\tt AddLanguage} directive> --> +<strong>Syntax:</strong> AddLanguage <em>mime-lang extension extension...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> FileInfo<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime<p> + +The AddLanguage directive adds to the list of filename extensions which +filenames may end in for the specified content language. <em>Mime-lang</em> +is the mime language of files with names ending <em>extension</em>, +after any content encoding extensions have been removed. Example: +<blockquote><code> +AddEncoding x-compress Z<br> +AddLanguage en .en<br> +AddLanguage fr .fr<br> +</code></blockquote> + +Then the document <code>xxxx.en.Z</code> will be treated as being a compressed +English document. Although the content language is reported to the client, +the browser is unlikely to use this information. The AddLanguage directive +is more useful for content negotiation, where the server returns one +from several documents based on the client's language preference.<p><hr> + +<h2><A name="addtype">AddType</A></h2> +<!--%plaintext <?INDEX {\tt AddType} directive> --> +<strong>Syntax:</strong> AddType <em>mime-type extension extension...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> FileInfo<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime<p> + +The AddType directive adds to the list of filename extensions which +filenames may end in for the specified content type. <em>Mime-enc</em> +is the mime type to use for documents ending in <em>extension</em>. +after content-encoding and language extensions have been removed. Example: +<blockquote><code> +AddType image/gif GIF +</code></blockquote> +It is recommended that new mime types be added using the AddType directive +rather than changing the <A HREF="#typesconfig">TypesConfig</A> file.<p> +Note that, unlike the NCSA httpd, this directive cannot be used to set the +type of particular files.<p><hr> + +<h2><a name="forcetype">ForceType</a></h2> + +<strong>Syntax:</strong> ForceType <em>media type</em><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime<br> +<strong>Compatibility:</strong> ForceType is only available in Apache +1.1 and later.<p> + +<p>When placed into an <code>.htaccess</code> file or a +<code><Directory></code> or <code><Location></code> section, +this directive forces all matching files to be served +as the content type given by <em>media type</em>. For example, if you +had a directory full of GIF files, but did not want to label them all with +".gif", you might want to use: +<pre> + ForceType image/gif +</pre> +<p>Note that this will override any filename extensions that might +media type.</p> + +<h2><a name="sethandler">SetHandler</a></h2> + +<strong>Syntax:</strong> SetHandler <em>handler-name</em><br> +<strong>Context:</strong> directory, .htaccess<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime<br> +<strong>Compatibility:</strong> SetHandler is only available in Apache +1.1 and later.<p> + +<p>When placed into an <code>.htaccess</code> file or a +<code><Directory></code> or <code><Location></code> section, +this directive forces all matching files to be parsed through the +<a href="../handler.html">handler</a> +given by <em>handler-name</em>. For example, if you had a +directory you wanted to be parsed entirely as imagemap rule files, +regardless of extension, you might put the following into an +<code>.htaccess</code> file in that directory: +<pre> + SetHandler imap-file +</pre> + +<p>Another example: if you wanted to have the server display a status +report whenever a URL of <code>http://servername/status</code> was +called, you might put the following into access.conf: +<pre> + <Location /status> + SetHandler server-status + </Location> +</pre> +<HR> + +<h2><A name="typesconfig">TypesConfig</A></h2> +<!--%plaintext <?INDEX {\tt TypesConfig} directive> --> +<strong>Syntax:</strong> TypesConfig <em>filename</em><br> +<strong>Default:</strong> <code>TypesConfig conf/mime.types</code><br> +<Strong>Context:</strong> server config<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_mime<p> + +The TypesConfig directive sets the location of the mime types configuration +file. <em>Filename</em> is relative to the +<A HREF="core.html#serverroot">ServerRoot</A>. This file sets the default list of +mappings from filename extensions to content types; changing this file is not +recommended. Use the <A HREF="#addtype">AddType</A> directive instead. The +file contains lines in the format of the arguments to an AddType command: +<blockquote><em>mime-type extension extension ...</em></blockquote> +The extensions are lower-cased. Blank lines, and lines beginning with a hash +character (`#') are ignored.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_negotiation.html b/usr.sbin/httpd/htdocs/manual/mod/mod_negotiation.html new file mode 100644 index 00000000000..94cef4e5ac3 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_negotiation.html @@ -0,0 +1,160 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_negotiation</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_negotiation</h1> + +This module is contained in the <code>mod_negotiation.c</code> file, +and is compiled in by default. It provides for <A +HREF="../content-negotiation.html">content negotiation</A>. + +<h2>Summary</h2> +Content negotiation, or more accurately content selection, is the +selection of the document that best matches the clients +capabilities, from one of several available documents. +There are two implementations of this. +<ul> +<li> A type map (a file with the handler <code>type-map</code>) +which explicitly lists the files containing the variants. +<li> A MultiViews search (enabled by the MultiViews +<A HREF="core.html#options">Option</A>, where the server does an implicit +filename pattern match, and choose from amongst the results. +</ul> + +<h3>Type maps</h3> +A type map has the same format as RFC822 mail headers. It contains document +descriptions separated by blank lines, with lines beginning with a hash +character ('#') treated as comments. A document description consists of +several header records; records may be continued on multiple lines if the +continuation lines start with spaces. The leading space will be deleted +and the lines concatenated. A header record consists of a keyword +name, which always ends in a colon, followed by a value. Whitespace is allowed +between the header name and value, and between the tokens of value. + +The headers allowed are: + +<dl> +<dt>Content-Encoding: +<dd>The encoding of the file. Currently only two encodings are recognized +by http; <code>x-compress</code> for compressed files, and <code>x-gzip</code> +for gzipped files. +<dt>Content-Language: +<dd>The language of the variant, as an Internet standard language code, such +as <code>en</code>. +<dt>Content-Length: +<dd>The length of the file, in bytes. If this header is not present, then +the actual length of the file is used. +<dt>Content-Type: +<dd>The MIME media type of the document, with optional parameters. +parameters are separated from the media type and from one another by +semi-colons. Parameter syntax is name=value; allowed parameters are: +<dl> +<dt>level +<dd>the value is an integer, which specifies the version of the media type. +For <code>text/html</code> this defaults to 2, otherwise 0. +<dt>qs +<dd>the value is a floating-point number with value between 0. and 1. +It indications the 'quality' of this variant. +</dl> +Example: +<blockquote><code>Content-Type: image/jpeg; qs=0.8</code></blockquote> +<dt>URI: +<dd>The path to the file containing this variant, relative to the map file. +</dl> + +<h3>MultiViews</h3> +A MultiViews search is enabled by the MultiViews +<A HREF="core.html#options">Option</A>. +If the server receives a request for <code>/some/dir/foo</code> and +<code>/some/dir/foo</code> does <em>not</em> exist, then the server reads the +directory looking for all files named <code>foo.*</code>, and effectively +fakes up a type map which names all those files, assigning them the same media +types and content-encodings it would have if the client had asked for +one of them by name. It then chooses the best match to the client's +requirements, and returns that document.<p> + + + +<h2>Directives</h2> +<ul> +<li><A href="#cachenegotiateddocs">CacheNegotiatedDocs</a> +<li><A HREF="#languagepriority">LanguagePriority</A> +</ul> +<hr> + + +<h2><A name="cachenegotiateddocs">CacheNegotiatedDocs</A></h2> +<strong>Syntax:</strong> CacheNegotiatedDocs<br> +<Strong>Context:</strong> server config<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_negotiation<br> +<strong>Compatibility:</strong> CacheNegotiatedDocs is only available +in Apache 1.1 and later.<p> + +<p>If set, this directive allows content-negotiated documents to be +cached by proxy servers. This could mean that clients behind those +proxys could retrieve versions of the documents that are not the best +match for their abilities, but it will make caching more +efficient. +<p> + +This directive only applies to requests which come from HTTP/1.0 browsers. +HTTP/1.1 provides much better control over the caching of negotiated +documents, and this directive has no effect in responses to +HTTP/1.1 requests. + + + +<h2><A name="languagepriority">LanguagePriority</A></h2> +<!--%plaintext <?INDEX {\tt LanguagePriority} directive> --> +<strong>Syntax:</strong> LanguagePriority <em>mime-lang mime-lang...</em><br> +<Strong>Context:</strong> server config, virtual host, directory, .htaccess<br> +<Strong>Override:</strong> FileInfo<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_negotiation<p> + +The LanguagePriority sets the precedence of language variants for the case +where the client does not express a preference, when handling a +MultiViews request. The list of <em>mime-lang</em> are in order of decreasing +preference. Example: + +<blockquote><code>LanguagePriority en fr de</code></blockquote> + +For a request for <code>foo.html</code>, where <code>foo.html.fr</code> +and <code>foo.html.de</code> both existed, but the browser did not express +a language preference, then <code>foo.html.fr</code> would be returned.<p> + +<P> + +Note that this directive only has an effect if a 'best' language +cannot be determined by other any other means. Correctly implemented +HTTP/1.1 requests will mean this directive has no effect. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_proxy.html b/usr.sbin/httpd/htdocs/manual/mod/mod_proxy.html new file mode 100644 index 00000000000..baf4c4ff65f --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_proxy.html @@ -0,0 +1,379 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_proxy</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_proxy</H1> + +This module is contained in the <code>mod_proxy.c</code> file for Apache 1.1.x, +or the <code>modules/proxy</code> subdirectory for Apache 1.2, and +is not compiled in by default. It provides for an <b>HTTP 1.0</b> caching proxy +server. It is only available in Apache 1.1 and later. Common configuration +questions are addressed <a href="#configs">here</a>. + +<h3>Note:</h3> +<p>This module was experimental in Apache 1.1.x. As of Apache 1.2, mod_proxy +stability is <i>greatly</i> improved.<p> + +<h2>Summary</h2> + +This module implements a proxy/cache for Apache. It implements +proxying capability for +<code>FTP</code>, +<code>CONNECT</code> (for SSL), +<code>HTTP/0.9</code>, and +<code>HTTP/1.0</code>. +The module can be configured to connect to other proxy modules for these +and other protocols. + +<h2>Directives</h2> +<ul> +<li><a href="#proxyrequests">ProxyRequests</a> +<li><a href="#proxyremote">ProxyRemote</a> +<li><a href="#proxypass">ProxyPass</a> +<li><a href="#proxyblock">ProxyBlock</a> +<li><a href="#cacheroot">CacheRoot</a> +<li><a href="#cachesize">CacheSize</a> +<li><a href="#cachemaxexpire">CacheMaxExpire</a> +<li><a href="#cachedefaultexpire">CacheDefaultExpire</a> +<li><a href="#cachelastmodifiedfactor">CacheLastModifiedFactor</a> +<li><a href="#cachegcinterval">CacheGcInterval</a> +<li><a href="#cachedirlevels">CacheDirLevels</a> +<li><a href="#cachedirlength">CacheDirLength</a> +<li><a href="#nocache">NoCache</a> +</ul> + +<hr> + +<A name="proxyrequests"><h2>ProxyRequests</h2></A> +<strong>Syntax:</strong> ProxyRequests <em>on/off</em><br> +<strong>Default:</strong> <code>ProxyRequests Off</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> ProxyRequest is only available in +Apache 1.1 and later.<p> + +This allows or prevents Apache from functioning as a proxy +server. Setting ProxyRequests to 'off' does not disable use of the <a +href="#proxypass">ProxyPass</a> directive. + +<A name="proxyremote"><h2>ProxyRemote</h2></A> +<strong>Syntax:</strong> ProxyRemote <em><match> <remote-server></em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> ProxyRemote is only available in +Apache 1.1 and later.<p> + +This defines remote proxies to this proxy. <match> is either the +name of a URL-scheme that the remote server supports, or a partial URL +for which the remote server should be used, or '*' to indicate the +server should be contacted for all requests. <remote-server> is a +partial URL for the remote server. Syntax: + +<pre> + <remote-server> = <protocol>://<hostname>[:port] +</pre> + +<protocol> is the protocol that should be used to communicate +with the remote server; only "http" is supported by this module. + +Example: +<pre> + ProxyRemote http://goodguys.com/ http://mirrorguys.com:8000 + ProxyRemote * http://cleversite.com + ProxyRemote ftp http://ftpproxy.mydomain.com:8080 +</pre> + +In the last example, the proxy will forward FTP requests, encapsulated +as yet another HTTP proxy request, to another proxy which can handle +them. + +<A name="proxypass"><h2>ProxyPass</h2></A> +<strong>Syntax:</strong> ProxyPass <em><path> <url></em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> ProxyPass is only available in +Apache 1.1 and later.<p> + +This directive allows remote servers to be mapped into the space of the local +server; the local server does not act as a proxy in the conventional sense, +but appears to be a mirror of the remote server. <path> is the name of +a local virtual path; <url> is a partial URL for the remote server. + +Suppose the local server has address http://wibble.org; then +<pre> + ProxyPass /mirror/foo http://foo.com +</pre> +Will cause a local request for the http://wibble.org/mirror/foo/bar to be +internally converted into a proxy request to http://foo.com/bar + +<A name="proxyblock"><h2>ProxyBlock</h2></A> +<strong>Syntax:</strong> ProxyBlock <em><word/host/domain list></em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> ProxyBlock is only available in +Apache 1.2 and later.<p> + +The ProxyBlock directive specifies a list of words, hosts and/or domains, +separated by spaces. HTTP, HTTPS, and FTP document requests to matched words, +hosts or domains are <em>blocked</em> by the proxy server. The proxy module +will also attempt to determine IP addresses of list items which may be +hostnames during startup, and cache them for match test as well. Example: + +<pre> + ProxyBlock joes_garage.com some_host.co.uk rocky.wotsamattau.edu +</pre> + +'rocky.wotsamattau.edu' would also be matched if referenced by IP address.<p> + +Note that 'wotsamattau' would also be sufficient to match 'wotsamattau.edu'.<p> + +Note also that + +<pre> +ProxyBlock * +</pre> + +blocks connections to all sites. + +<A name="cacheroot"><h2>CacheRoot</h2></A> +<strong>Syntax:</strong> CacheRoot <em><directory></em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheRoot is only available in +Apache 1.1 and later.<p> + +Sets the name of the directory to contain cache files; this must be +writable +by the httpd server. + +<A name="cachesize"><h2>CacheSize</h2></A> +<strong>Syntax:</strong> CacheSize <em><size></em><br> +<strong>Default:</strong> <code>CacheSize 5</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheSize is only available in +Apache 1.1 and later.<p> + +Sets the desired space usage of the cache, in Kb (1024 byte units). Although +usage may grow above this setting, the garbage collection will delete files +until the usage is at or below this setting. + +<A name="cachegcinterval"><h2>CacheGcInterval</h2></A> +<strong>Syntax:</strong> CacheGcInterval <em><time></em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheGcinterval is only available in +Apache 1.1 and later.<p> + +Check the cache every <time> hours, and delete files if the space +usage is greater than that set by CacheSize. + +<A name="cachemaxexpire"><h2>CacheMaxExpire</h2></A> +<strong>Syntax:</strong> CacheMaxExpire <em><time></em><br> +<strong>Default:</strong> <code>CacheMaxExpire 24</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheMaxExpire is only available in +Apache 1.1 and later.<p> + +Cachable HTTP documents will be retained for at most <time> hours without +checking the origin server. Thus documents can be at most <time> +hours out of date. This restriction is enforced even if an expiry date +was supplied with the document. + +<A name="cachelastmodifiedfactor"><h2>CacheLastModifiedFactor</h2></A> +<strong>Syntax:</strong> CacheLastModifiedFactor <em><factor></em><br> +<strong>Default:</strong> <code>CacheLastModifiedFactor 0.1</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheLastModifiedFactor is only available in +Apache 1.1 and later.<p> + +If the origin HTTP server did not supply an expiry date for the +document, then estimate one using the formula +<pre> + expiry-period = time-since-last-modification * <factor> +</pre> +For example, if the document was last modified 10 hours ago, and +<factor> is 0.1, then the expiry period will be set to 10*0.1 = 1 hour. + +<p>If the expiry-period would be longer than that set by CacheMaxExpire, +then the latter takes precedence. + +<A name="cachedirlevels"><h2>CacheDirLevels</h2></A> +<strong>Syntax:</strong> CacheDirLevels <em><levels></em><br> +<strong>Default:</strong> <code>CacheDirLevels 3</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheDirLevels is only available in +Apache 1.1 and later.<p> + +CacheDirLevels sets the number of levels of subdirectories in the cache. +Cached data will be saved this many directory levels below CacheRoot. + +<A name="cachedirlength"><h2>CacheDirLength</h2></A> +<strong>Syntax:</strong> CacheDirLength <em><length></em><br> +<strong>Default:</strong> <code>CacheDirLength 1</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheDirLength is only available in +Apache 1.1 and later.<p> + +CacheDirLength sets the number of characters in proxy cache subdirectory names. + +<A name="cachedefaultexpire"><h2>CacheDefaultExpire</h2></A> +<strong>Syntax:</strong> CacheDefaultExpire <em><time></em><br> +<strong>Default:</strong> <code>CacheDefaultExpire 1</code><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> CacheDefaultExpire is only available in +Apache 1.1 and later.<p> + +If the document is fetched via a protocol that does not support expiry times, +then use <time> hours as the expiry time. +<a href="#cachemaxexpire">CacheMaxExpire</a> does <strong>not</strong> +override. + +<A name="nocache"><h2>NoCache</h2></A> +<strong>Syntax:</strong> NoCache <em><word/host/domain list></em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_proxy<br> +<strong>Compatibility:</strong> NoCache is only available in +Apache 1.1 and later.<p> + +The NoCache directive specifies a list of words, hosts and/or domains, separated +by spaces. HTTP and non-passworded FTP documents from matched words, hosts or +domains are <em>not</em> cached by the proxy server. The proxy module will +also attempt to determine IP addresses of list items which may be hostnames +during startup, and cache them for match test as well. Example: + +<pre> + NoCache joes_garage.com some_host.co.uk bullwinkle.wotsamattau.edu +</pre> + +'bullwinkle.wotsamattau.edu' would also be matched if referenced by IP +address.<p> + +Note that 'wotsamattau' would also be sufficient to match 'wotsamattau.edu'.<p> + +Note also that + +<pre> +NoCache * +</pre> + +disables caching completely.<p> + +<hr> + +<a name="configs"><h2>Common configuration topics</h2></a> + +<ul> +<li><a href="#access">Controlling access to your proxy</a> +<li><a href="#shortname">Using Netscape hostname shortcuts</a> +<li><a href="#mimetypes">Why doesn't file type <i>xxx</i> download via FTP?</a> +<li><a href="#startup">Why does Apache start more slowly when using the + proxy module?</a> +<li><a href="#socks">Can I use the Apache proxy module with my SOCKS proxy?</a> +</ul> + +<h2><a name="access">Controlling access to your proxy</a></h2> + +You can control who can access your proxy via the normal <Directory> +control block using the following example:<p> + +<pre> +<Directory proxy:*> +<Limit GET PUT POST DELETE CONNECT OPTIONS> +order deny,allow +deny from [machines you'd like *not* to allow by IP address or name] +allow from [machines you'd like to allow by IP address or name] +</Limit> +</Directory> +</pre><p> + +A <Files> block will also work, and is the only method known to work +for all possible URLs in Apache versions earlier than 1.2b10.<p> + +<h2><a name="shortname">Using Netscape hostname shortcuts</a></h2> + +There is an optional patch to the proxy module to allow Netscape-like +hostname shortcuts to be used. It's available +<a href="http://www.apache.org/dist/contrib/patches/1.2/netscapehost.patch"> +here</a>.<p> + +<h2><a name="mimetypes">Why doesn't file type <i>xxx</i> download via FTP?</a></h2> + +You probably don't have that particular file type defined as +<i>application/octet-stream</i> in your proxy's mime.types configuration +file. A useful line can be<p> + +<pre> +application/octet-stream bin dms lha lzh exe class tgz taz +</pre> + +<h2><a name="startup">Why does Apache start more slowly when using the + proxy module?</a></h2> + +If you're using the <code>ProxyBlock</code> or <code>NoCache</code> +directives, hostnames' IP addresses are looked up and cached during +startup for later match test. This may take a few seconds (or more) +depending on the speed with which the hostname lookups occur.<p> + +<h2><a name="socks">Can I use the Apache proxy module with my SOCKS proxy?</a></h2> + +Yes. Just build Apache with the rule <code>SOCKS4=yes</code> in your +<i>Configuration</i> file, and follow the instructions there. SOCKS5 +capability can be added in a similar way (there's no <code>SOCKS5</code> +rule yet), so use the <code>EXTRA_LFLAGS</code> definition, or build Apache +normally and run it with the <i>runsocks</i> wrapper provided with SOCKS5, +if your OS supports dynamically linked libraries.<p> + +Some users have reported problems when using SOCKS version 4.2 on Solaris. +The problem was solved by upgrading to SOCKS 4.3.<p> + +Remember that you'll also have to grant access to your Apache proxy machine by +permitting connections on the appropriate ports in your SOCKS daemon's +configuration.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_rewrite.html b/usr.sbin/httpd/htdocs/manual/mod/mod_rewrite.html new file mode 100644 index 00000000000..44ed3239cf2 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_rewrite.html @@ -0,0 +1,1231 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<!--%hypertext --> +<!-- mod_rewrite.html --> +<!-- Documentation for the mod_rewrite Apache module --> +<html> +<head> +<title>Apache module mod_rewrite</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<h1 ALIGN="CENTER">Module mod_rewrite</h1> + +This module is contained in the <code>mod_rewrite.c</code> file, with Apache +1.2 and later. It provides a rule-based rewriting engine to rewrite requested +URLs on the fly. <code>mod_rewrite</code> is not compiled into the server by +default. To use <code>mod_rewrite</code> you have to enable the following line +in the server build Configuration file: +<pre> + Module rewrite_module mod_rewrite.o +</pre> + +<h2>Summary</h2> + +This module uses a rule-based rewriting engine (based on a +regular-expression parser) to rewrite requested URLs on the fly. + +<p> +It supports an unlimited number of additional rule conditions (which can +operate on a lot of variables, including HTTP headers) for granular +matching and external database lookups (either via plain text +tables, DBM hash files or external processes) for advanced URL +substitution. + +<p> +It operates on the full URLs (including the PATH_INFO part) both in per-server +context (httpd.conf) and per-dir context (.htaccess) and even can generate +QUERY_STRING parts on result. The rewritten result can lead to internal +sub-processing, external request redirection or to internal proxy throughput. + +<p> +This module was originally written in April 1996 and +gifted exclusively to the The Apache Group in July 1997 by +<p> +<blockquote> + <i>Ralf S. Engelschall</i><br> + <a href="mailto:rse@engelschall.com"><tt>rse@engelschall.com</tt></a><br> + <a href="http://www.engelschall.com/"><tt>www.engelschall.com</tt></a> +</blockquote> + +<!--%hypertext --> +<HR> +<!--/%hypertext --> + +<p> +<h2>Directives</h2> + +<ul> + <li><a href="#RewriteEngine">RewriteEngine</a> + <li><a href="#RewriteOptions">RewriteOptions</a> + <li><a href="#RewriteLog">RewriteLog</a> + <li><a href="#RewriteLogLevel">RewriteLogLevel</a> + <li><a href="#RewriteMap">RewriteMap</a> + <li><a href="#RewriteBase">RewriteBase</a> + <li><a href="#RewriteCond">RewriteCond</a> + <li><a href="#RewriteRule">RewriteRule</a> +</ul> + +<!--%hypertext --> +<hr> +<!--/%hypertext --> + + +<center> +<a name="Configuration"> +<h1>Configuration Directives</h1> +</a> +</center> + +<a name="RewriteEngine"><h3>RewriteEngine</h3></a> +<strong>Syntax:</strong> <code>RewriteEngine</code> {<code>on,off</code>}<br> +<strong>Default:</strong> <strong><code>RewriteEngine off</code></strong><br> +<strong>Context:</strong> server config, virtual host, per-directory config<br> +<p> + +The <tt>RewriteEngine</tt> directive enables or disables the +runtime rewriting engine. If it is set to <code>off</code> this module does +no runtime processing at all. It does not even update the <tt>SCRIPT_URx</tt> +environment variables. + +<p> +Use this directive to disable the module instead of commenting out +all <tt>RewriteRule</tt> directives! + +<p> +<hr noshade size=1> +<p> + +<a name="RewriteOptions"><h3>RewriteOptions</h3></a> +<strong>Syntax:</strong> <code>RewriteOptions</code> <em>Option</em> ...<br> +<strong>Default:</strong> -<em>None</em>-<br> +<strong>Context:</strong> server config, virtual host, per-directory config<br> +<p> + +The <tt>RewriteOption</tt> directive sets some special options for the +current per-server or per-directory configuration. The <em>Option</em> +strings can be one of the following: + +<ul> +<li>'<strong><code>inherit</code></strong>'<br> + This forces the current configuration to inherit the configuration of the + parent. In per-virtual-server context this means that the maps, + conditions and rules of the main server gets inherited. In per-directory + context this means that conditions and rules of the parent directory's + <tt>.htaccess</tt> configuration gets inherited. +</ul> + +<p> +<hr noshade size=1> +<p> + +<a name="RewriteLog"><h3>RewriteLog</h3></a> +<strong>Syntax:</strong> <code>RewriteLog</code> <em>Filename</em><br> +<strong>Default:</strong> -<em>None</em>-<br> +<strong>Context:</strong> server config, virtual host<br> +<p> + +The <tt>RewriteLog</tt> directive sets the name of the file to which the +server logs any rewriting actions it performs. If the name does not begin +with a slash ('<tt>/</tt>') then it is assumed to be relative to the +<em>Server Root</em>. The directive should occur only once per server +config. + +<p> +<table width="70%" border=0 bgcolor="#f0f0f0" cellspacing=0 cellpadding=10> +<tr><td> +To disable the logging of rewriting actions it is not recommended +to set <em>Filename</em> +to <code>/dev/null</code>, because although the rewriting engine does +not create output to a logfile it still creates the logfile +output internally. <b>This will slow down the server with no advantage to the +administrator!</b> +To disable logging either remove or comment out the +<tt>RewriteLog</tt> directive or use <tt>RewriteLogLevel 0</tt>! +</td></tr> +</table> + +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +SECURITY: See the <a +href="../misc/security_tips.html">Apache Security +Tips</a> document for details on why your security could be compromised if the +directory where logfiles are stored is writable by anyone other than the user +that starts the server. +</td></tr> +</table> + +<p> +<b>Example:</b> +<blockquote> +<pre> +RewriteLog "/usr/local/var/apache/logs/rewrite.log" +</pre> +</blockquote> + +<p> +<hr noshade size=1> +<p> + +<a name="RewriteLogLevel"><h3>RewriteLogLevel</h3></a> +<strong>Syntax:</strong> <code>RewriteLogLevel</code> <em>Level</em><br> +<strong>Default:</strong> <strong><code>RewriteLogLevel 0</code></strong><br> +<strong>Context:</strong> server config, virtual host<br> +<p> + +The <tt>RewriteLogLevel</tt> directive set the verbosity level of the rewriting +logfile. The default level 0 means no logging, while 9 or more means +that practically all actions are logged. + +<p> +To disable the logging of rewriting actions simply set <em>Level</em> to 0. +This disables all rewrite action logs. + +<p> +<table width="70%" border=0 bgcolor="#f0f0f0" cellspacing=0 cellpadding=10> +<tr><td> +<b>Notice:</b> Using a high value for <i>Level</i> will slow down your Apache +server dramatically! Use the rewriting logfile only for debugging or at least +at <em>Level</em> not greater than 2! +</td></tr> +</table> + + +<p> +<b>Example:</b> +<blockquote> +<pre> +RewriteLogLevel 3 +</pre> +</blockquote> + +<p> +<hr noshade size=1> +<p> + +<a name="RewriteMap"><h3>RewriteMap</h3></a> +<strong>Syntax:</strong> <code>RewriteMap</code> <em>Mapname</em> <code>{txt,dbm,prg}:</code><em>Filename</em><br> +<strong>Default:</strong> not used per default<br> +<strong>Context:</strong> server config, virtual host<br> +<p> + +The <tt>RewriteMap</tt> directive defines an external <em>Rewriting Map</em> +which can be used inside rule substitution strings by the mapping-functions +to insert/substitute fields through a key lookup. +<p> + +The <a name="mapfunc"><em>Mapname</em></a> is the name of the map and will +be used to specify a mapping-function for the substitution strings of a +rewriting rule via + +<blockquote><strong> +<code>${</code> <em>Mapname</em> <code>:</code> <em>LookupKey</em> +<code>|</code> <em>DefaultValue</em> <code>}</code> +</strong></blockquote> + +When such a directive occurs the map <em>Mapname</em> +is consulted and the key <em>LookupKey</em> is looked-up. If the key is +found, the map-function directive is substituted by <em>SubstValue</em>. If +the key is not found then it is substituted by <em>DefaultValue</em>. + +<p> +The <em>Filename</em> must be a valid Unix filepath, containing one +of the following formats: + +<ol> +<li><b>Plain Text Format</b> + <p> + This is a ASCII file which contains either blank lines, comment lines + (starting with a '#' character) or + + <blockquote><strong> + <em>MatchingKey</em> <em>SubstValue</em> + </strong></blockquote> + + pairs - one per line. You can create such files either manually, + using your favorite editor, or by using the programs + <tt>mapcollect</tt> and <tt>mapmerge</tt> from the <tt>support</tt> + directory of the <b>mod_rewrite</b> distribution. + <p> + To declare such a map prefix, <em>Filename</em> with a <code>txt:</code> + string as in the following example: + +<p> +<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0"> +<tr><td><pre> +# +# map.real-to-user -- maps realnames to usernames +# + +Ralf.S.Engelschall rse # Bastard Operator From Hell +Dr.Fred.Klabuster fred # Mr. DAU +</pre></td></tr> +</table> + +<p> +<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0"> +<tr><td><pre> +RewriteMap real-to-host txt:/path/to/file/map.real-to-user +</pre></td></tr> +</table> + +<p> +<li><b>DBM Hashfile Format</b> + <p> + This is a binary NDBM format file containing the + same contents as the <em>Plain Text Format</em> files. You can create + such a file with any NDBM tool or with the <tt>dbmmanage</tt> program + from the <tt>support</tt> directory of the Apache distribution. + <p> + To declare such a map prefix <em>Filename</em> with a <code>dbm:</code> + string. +<p> +<li><b>Program Format</b> + <p> + This is a Unix executable, not a lookup file. To create it you can use + the language of your choice, but the result has to be a run-able Unix + binary (i.e. either object-code or a script with the + magic cookie trick '<tt>#!/path/to/interpreter</tt>' as the first line). + <p> + This program gets started once at startup of the Apache servers and then + communicates with the rewriting engine over its <tt>stdin</tt> and + <tt>stdout</tt> file-handles. For each map-function lookup it will + receive the key to lookup as a newline-terminated string on + <tt>stdin</tt>. It then has to give back the looked-up value as a + newline-terminated string on <tt>stdout</tt> or the four-character string + ``<tt>NULL</tt>'' if it fails (i.e. there is no corresponding value + for the given key). A trivial program which will implement a 1:1 map + (i.e. key == value) could be: + <p> +<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0"> +<tr><td><pre> +#!/usr/bin/perl +$| = 1; +while (<STDIN>) { + # ...here any transformations + # or lookups should occur... + print $_; +} +</pre></td></tr> +</table> + <p> + <b>But be very careful:</b><br> + <ol> + <li>``<i>Keep the program simple, stupid</i>'' (KISS), because + if this program hangs it will lead to a hang of the Apache server + when the rule occurs. + <li>Avoid one common mistake: never do buffered I/O on <tt>stdout</tt>! + This will cause a deadloop! Hence the ``<tt>$|=1</tt>'' in the above + example... + </ol> + <p> + To declare such a map prefix <em>Filename</em> with a <code>prg:</code> + string. +</ol> + +The <tt>RewriteMap</tt> directive can occur more than once. For each +mapping-function use one <tt>RewriteMap</tt> directive to declare its +rewriting mapfile. While you cannot <b>declare</b> a map in per-directory +context it is of course possible to <b>use</b> this map in per-directory +context. + +<p> +<table width="70%" border=0 bgcolor="#f0f0f0" cellspacing=0 cellpadding=10> +<tr><td> +For plain text and DBM format files the looked-up keys are cached in-core +until the <tt>mtime</tt> of the mapfile changes or the server does a +restart. This way you can have map-functions in rules which are used +for <b>every</b> request. This is no problem, because the external lookup +only happens once! +</td></tr> +</table> + + +<p> +<hr noshade size=1> +<p> + +<a name="RewriteBase"><h3>RewriteBase</h3></a> +<strong>Syntax:</strong> <code>RewriteBase</code> <em>BaseURL</em><br> +<strong>Default:</strong> <em>default is the physical directory path</em><br> +<strong>Context:</strong> per-directory config<br> +<p> + +The <tt>RewriteBase</tt> directive explicitly sets the base URL for +per-directory rewrites. As you will see below, <tt>RewriteRule</tt> can be +used in per-directory config files (<tt>.htaccess</tt>). There it will act +locally, i.e. the local directory prefix is stripped at this stage of +processing and your rewriting rules act only on the remainder. At the end +it is automatically added. + +<p> +When a substitution occurs for a new URL, this module has to +re-inject the URL into the server processing. To be able to do this it needs +to know what the corresponding URL-prefix or URL-base is. By default this +prefix is the corresponding filepath itself. <b>But at most websites URLs are +<b>NOT</b> directly related to physical filename paths, so this assumption +will be usually be wrong!</b> There you have to use the <tt>RewriteBase</tt> +directive to specify the correct URL-prefix. + +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +So, if your webserver's URLs are <b>not</b> directly +related to physical file paths, you have to use <tt>RewriteBase</tt> in every +<tt>.htaccess</tt> files where you want to use <tt>RewriteRule</tt> +directives. +</td></tr> +</table> + +<p> +<b>Example:</b> + +<blockquote> + Assume the following per-directory config file: + +<p> +<table border=0 cellspacing=1 cellpadding=5 bgcolor="#f0f0f0"> +<tr><td><pre> +# +# /abc/def/.htaccess -- per-dir config file for directory /abc/def +# Remember: /abc/def is the physical path of /xyz, i.e. the server +# has a 'Alias /xyz /abc/def' directive e.g. +# + +RewriteEngine On + +# let the server know that we are reached via /xyz and not +# via the physical path prefix /abc/def +RewriteBase /xyz + +# now the rewriting rules +RewriteRule ^oldstuff\.html$ newstuff.html +</pre></td></tr> +</table> + +<p> +In the above example, a request to <tt>/xyz/oldstuff.html</tt> gets correctly +rewritten to the physical file <tt>/abc/def/newstuff.html</tt>. + +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +<font size=-1> +<b>For the Apache hackers:</b><br> +The following list gives detailed information about the internal +processing steps: + +<p> +<pre> +Request: + /xyz/oldstuff.html + +Internal Processing: + /xyz/oldstuff.html -> /abc/def/oldstuff.html (per-server Alias) + /abc/def/oldstuff.html -> /abc/def/newstuff.html (per-dir RewriteRule) + /abc/def/newstuff.html -> /xyz/newstuff.html (per-dir RewriteBase) + /xyz/newstuff.html -> /abc/def/newstuff.html (per-server Alias) + +Result: + /abc/def/newstuff.html +</pre> + +This seems very complicated but is the correct Apache internal processing, +because the per-directory rewriting comes too late in the process. So, +when it occurs the (rewritten) request has to be re-injected into the Apache +kernel! BUT: While this seems like a serious overhead, it really isn't, because +this re-injection happens fully internal to the Apache server and the same +procedure is used by many other operations inside Apache. So, you can be +sure the design and implementation is correct. +</font> +</td></tr> +</table> + +</blockquote> + + +<p> +<hr noshade size=1> +<p> + +<a name="RewriteCond"><h3>RewriteCond</h3></a> +<strong>Syntax:</strong> <code>RewriteCond</code> <em>TestString</em> <em>CondPattern</em><br> +<strong>Default:</strong> -<em>None</em>-<br> +<strong>Context:</strong> server config, virtual host, per-directory config<br> +<p> + +The <tt>RewriteCond</tt> directive defines a rule condition. Precede a +<tt>RewriteRule</tt> directive with one or more <tt>RewriteCond</tt> +directives. + +The following rewriting rule is only used if its pattern matches the current +state of the URI <b>AND</b> if these additional conditions apply, too. + +<p> +<em>TestString</em> is a string which contains server-variables of the form + +<blockquote><strong> +<tt>%{</tt> <em>NAME_OF_VARIABLE</em> <tt>}</tt> +</strong></blockquote> + +where <em>NAME_OF_VARIABLE</em> can be a string +of the following list: + +<p> +<table bgcolor="#f0f0f0" cellspacing=0 cellpadding=5> +<tr> +<td valign=top> +<b>HTTP headers:</b><p> +<font size=-1> +HTTP_USER_AGENT<br> +HTTP_REFERER<br> +HTTP_COOKIE<br> +HTTP_FORWARDED<br> +HTTP_HOST<br> +HTTP_PROXY_CONNECTION<br> +HTTP_ACCEPT<br> +</font> +</td> + +<td valign=top> +<b>connection & request:</b><p> +<font size=-1> +REMOTE_ADDR<br> +REMOTE_HOST<br> +REMOTE_USER<br> +REMOTE_IDENT<br> +REQUEST_METHOD<br> +SCRIPT_FILENAME<br> +PATH_INFO<br> +QUERY_STRING<br> +AUTH_TYPE<br> +</font> +</td> + +</tr> +<tr> + +<td valign=top> +<b>server internals:</b><p> +<font size=-1> +DOCUMENT_ROOT<br> +SERVER_ADMIN<br> +SERVER_NAME<br> +SERVER_PORT<br> +SERVER_PROTOCOL<br> +SERVER_SOFTWARE<br> +SERVER_VERSION<br> +</font> +</td> + +<td valign=top> +<b>system stuff:</b><p> +<font size=-1> +TIME_YEAR<br> +TIME_MON<br> +TIME_DAY<br> +TIME_HOUR<br> +TIME_MIN<br> +TIME_SEC<br> +TIME_WDAY<br> +TIME<br> +</font> +</td> + +<td valign=top> +<b>specials:</b><p> +<font size=-1> +API_VERSION<br> +THE_REQUEST<br> +REQUEST_URI<br> +REQUEST_FILENAME<br> +IS_SUBREQ<br> +</font> +</td> +</tr> +</table> + + +<p> +<table width="70%" border=0 bgcolor="#f0f0f0" cellspacing=0 cellpadding=10> +<tr><td> +These variables all correspond to the similar named HTTP MIME-headers, C +variables of the Apache server or <tt>struct tm</tt> fields of the Unix +system. +</td></tr> +</table> + +<p> +Special Notes: +<ol> +<li>The variables SCRIPT_FILENAME and REQUEST_FILENAME contain the same +value, i.e. the value of the <tt>filename</tt> field of the internal +<tt>request_rec</tt> structure of the Apache server. The first name is just the +commonly known CGI variable name while the second is the consistent +counterpart to REQUEST_URI (which contains the value of the <tt>uri</tt> +field of <tt>request_rec</tt>). + +<p> +<li>There is the special format: <tt>%{ENV:variable}</tt> where +<i>variable</i> can be any environment variable. This is looked-up via +internal Apache structures and (if not found there) via <tt>getenv()</tt> from +the Apache server process. + +<p> +<li>There is the special format: <tt>%{HTTP:header}</tt> where +<i>header</i> can be any HTTP MIME-header name. This is looked-up +from the HTTP request. Example: <tt>%{HTTP:Proxy-Connection}</tt> +is the value of the HTTP header ``<tt>Proxy-Connection:</tt>''. + +<p> +<li>There is the special format: <tt>%{LA-U:url}</tt> +for look-aheads like <tt>-U</tt>. This performs a internal sub-request to +look-ahead for the final value of <i>url</i>. + +<p> +<li>There is the special format: <tt>%{LA-F:file}</tt> +for look-aheads like <tt>-F</tt>. This performs a internal sub-request to +look-ahead for the final value of <i>file</i>. +</ol> + +<p> +<em>CondPattern</em> is the condition pattern, i.e. a regular expression +which gets applied to the current instance of the <em>TestString</em>, i.e. +<em>TestString</em> gets evaluated and then matched against +<em>CondPattern</em>. + +<p> +<b>Remember:</b> <em>CondPattern</em> is a standard +<em>Extended Regular Expression</em> with some additions: + +<ol> +<li>You can precede the pattern string with a '<tt>!</tt>' character +(exclamation mark) to specify a <b>non</b>-matching pattern. + +<p> +<li> +There are some special variants of <em>CondPatterns</em>. Instead of real +regular expression strings you can also use one of the following: +<p> +<ul> +<li>'<b><CondPattern</b>' (is lexicographically lower)<br> +Treats the <i>CondPattern</i> as a plain string and compares it +lexicographically to <i>TestString</i> and results in a true expression if +<i>TestString</i> is lexicographically lower then <i>CondPattern</i>. +<p> +<li>'<b>>CondPattern</b>' (is lexicographically greater)<br> +Treats the <i>CondPattern</i> as a plain string and compares it +lexicographically to <i>TestString</i> and results in a true expression if +<i>TestString</i> is lexicographically greater then <i>CondPattern</i>. +<p> +<li>'<b>=CondPattern</b>' (is lexicographically equal)<br> +Treats the <i>CondPattern</i> as a plain string and compares it +lexicographically to <i>TestString</i> and results in a true expression if +<i>TestString</i> is lexicographically equal to <i>CondPattern</i>, i.e the +two strings are exactly equal (character by character). +<p> +<li>'<b>-d</b>' (is <b>d</b>irectory)<br> +Treats the <i>TestString</i> as a pathname and +tests if it exists and is a directory. +<p> +<li>'<b>-f</b>' (is regular <b>f</b>ile)<br> +Treats the <i>TestString</i> as a pathname and +tests if it exists and is a regular file. +<p> +<li>'<b>-s</b>' (is regular file with <b>s</b>ize)<br> +Treats the <i>TestString</i> as a pathname and +tests if it exists and is a regular file with size greater then zero. +<p> +<li>'<b>-l</b>' (is symbolic <b>l</b>ink)<br> +Treats the <i>TestString</i> as a pathname and +tests if it exists and is a symbolic link. +<p> +<li>'<b>-F</b>' (is existing file via subrequest)<br> +Checks if <i>TestString</i> is a valid file and accessible via all the +server's currently-configured access controls for that path. This uses an +internal subrequest to determine the check, so use it with care because it +decreases your servers performance! +<p> +<li>'<b>-U</b>' (is existing URL via subrequest)<br> +Checks if <i>TestString</i> is a valid URL and accessible via all the server's +currently-configured access controls for that path. This uses an internal +subrequest to determine the check, so use it with care because it decreases +your servers performance! +</ul> +<p> +Notice: All of these tests can also be prefixed by a not ('!') character +to negate their meaning. +</ol> + +<p> +Additionally you can set special flags for <em>CondPattern</em> by appending + +<blockquote><strong> +<code>[</code><em>flags</em><code>]</code> +</strong></blockquote> + +as the third argument to the <tt>RewriteCond</tt> directive. <em>Flags</em> +is a comma-separated list of the following flags: + +<ul> +<li>'<strong><code>nocase|NC</code></strong>' (<b>n</b>o <b>c</b>ase)<br> + This makes the condition test case-insensitive, i.e. there is + no difference between 'A-Z' and 'a-z' both in the expanded + <em>TestString</em> and the <em>CondPattern</em>. +<p> +<li>'<strong><code>ornext|OR</code></strong>' (<b>or</b> next condition)<br> + Use this to combine rule conditions with a local OR instead of the + implicit AND. Typical example: + <p> +<blockquote><pre> +RewriteCond %{REMOTE_HOST} ^host1.* [OR] +RewriteCond %{REMOTE_HOST} ^host2.* [OR] +RewriteCond %{REMOTE_HOST} ^host3.* +RewriteRule ...some special stuff for any of these hosts... +</pre></blockquote> + Without this flag you had to write down the cond/rule three times. +</ul> + +<p> +<b>Example:</b> +<blockquote> + +To rewrite the Homepage of a site according to the ``<tt>User-Agent:</tt>'' +header of the request, you can use the following: + +<blockquote><pre> +RewriteCond %{HTTP_USER_AGENT} ^Mozilla.* +RewriteRule ^/$ /homepage.max.html [L] + +RewriteCond %{HTTP_USER_AGENT} ^Lynx.* +RewriteRule ^/$ /homepage.min.html [L] + +RewriteRule ^/$ /homepage.std.html [L] +</pre></blockquote> + +Interpretation: If you use Netscape Navigator as your browser (which identifies +itself as 'Mozilla'), then you get the max homepage, which includes +Frames, etc. If you use the Lynx browser (which is Terminal-based), then you +get the min homepage, which contains no images, no tables, etc. If you +use any other browser you get the standard homepage. +</blockquote> + +<p> +<hr noshade size=1> +<p> + +<a name="RewriteRule"><h3>RewriteRule</h3></a> +<strong>Syntax:</strong> <code>RewriteRule</code> <em>Pattern</em> <em>Substitution</em><br> +<strong>Default:</strong> -<em>None</em>-<br> +<strong>Context:</strong> server config, virtual host, per-directory config<br> + +<p> +The <tt>RewriteRule</tt> directive is the real rewriting workhorse. The +directive can occur more than once. Each directive then defines one single +rewriting rule. The <b>definition order</b> of these rules is +<b>important</b>, because this order is used when applying the rules at +run-time. + +<p> +<a name="patterns"><em>Pattern</em></a> can be (for Apache 1.1.x a System +V8 and for Apache 1.2.x a POSIX) <a name="regexp">regular expression</a> +which gets applied to the current URL. Here ``current'' means the value of the +URL when this rule gets applied. This may not be the original requested +URL, because there could be any number of rules before which already matched +and made alterations to it. + +<p> +Some hints about the syntax of regular expressions: + +<p> +<table bgcolor="#f0f0f0" cellspacing=0 cellpadding=5> +<tr> +<td valign=top> +<pre> +<strong><code>^</code></strong> Start of line +<strong><code>$</code></strong> End of line +<strong><code>.</code></strong> Any single character +<strong><code>[</code></strong>chars<strong><code>]</code></strong> One of chars +<strong><code>[^</code></strong>chars<strong><code>]</code></strong> None of chars + +<strong><code>?</code></strong> 0 or 1 of the preceding char +<strong><code>*</code></strong> 0 or N of the preceding char +<strong><code>+</code></strong> 1 or N of the preceding char + +<strong><code>\</code></strong>char escape that specific char + (e.g. for specifying the chars "<code>.[]()</code>" etc.) + +<strong><code>(</code></strong>string<strong><code>)</code></strong> Grouping of chars (the <b>N</b>th group can be used on the RHS with <code>$</code><b>N</b>) +</pre> +</td> +</tr> +</table> + +<p> +Additionally the NOT character ('<tt>!</tt>') is a possible pattern +prefix. This gives you the ability to negate a pattern; to say, for instance: ``<i>if +the current URL does <b>NOT</b> match to this pattern</i>''. This can be used +for special cases where it is better to match the negative pattern or as a +last default rule. + +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +<b>Notice!</b> When using the NOT character to negate a pattern you cannot +have grouped wildcard parts in the pattern. This is impossible because when +the pattern does NOT match, there are no contents for the groups. In +consequence, if negated patterns are used, you cannot use <tt>$N</tt> in the +substitution string! +</td></tr> +</table> + +<p> +<a name="rhs"><em>Substitution</em></a> of a rewriting rule is the string +which is substituted for (or replaces) the original URL for which +<em>Pattern</em> matched. Beside plain text you can use + +<ol> +<li>pattern-group back-references (<code>$N</code>) +<li>server-variables as in rule condition test-strings (<code>%{VARNAME}</code>) +<li><a href="#mapfunc">mapping-function</a> calls (<code>${mapname:key|default}</code>) +</ol> + +Back-references are <code>$</code><b>N</b> (<b>N</b>=1..9) identifiers which +will be replaced by the contents of the <b>N</b>th group of the matched +<em>Pattern</em>. The server-variables are the same as for the +<em>TestString</em> of a <tt>RewriteCond</tt> directive. The +mapping-functions come from the <tt>RewriteMap</tt> directive and are +explained there. These three types of variables are expanded in the order of +the above list. + +<p> +As already mentioned above, all the rewriting rules are applied to the +<em>Substitution</em> (in the order of definition in the config file). The +URL is <b>completely replaced</b> by the <em>Substitution</em> and the +rewriting process goes on until there are no more rules (unless explicitly +terminated by a <code><b>L</b></code> flag - see below). + +<p> +There is a special substitution string named '<tt>-</tt>' which means: +<b>NO substitution</b>! Sounds silly? No, it is useful to provide rewriting +rules which <b>only</b> match some URLs but do no substitution, e.g. in +conjunction with the <b>C</b> (chain) flag to be able to have more than one +pattern to be applied before a substitution occurs. + +<p> +One more note: You can even create URLs in the substitution string containing +a query string part. Just use a question mark inside the substitution string +to indicate that the following stuff should be re-injected into the +QUERY_STRING. When you want to erase an existing query string, end the +substitution string with just the question mark. + +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +<b>Notice</b>: There is a special feature. When you prefix a substitution +field with <tt>http://</tt><em>thishost</em>[<em>:thisport</em>] then +<b>mod_rewrite</b> automatically strips it out. This auto-reduction on +implicit external redirect URLs is a useful and important feature when +used in combination with a mapping-function which generates the hostname +part. Have a look at the first example in the example section below to +understand this. +<p> +<b>Remember:</b> An unconditional external redirect to your own server will +not work with the prefix <tt>http://thishost</tt> because of this feature. +To achieve such a self-redirect, you have to use the <b>R</b>-flag (see +below). +</td></tr> +</table> + +<p> +Additionally you can set special flags for <em>Substitution</em> by appending + +<blockquote><strong> +<code>[</code><em>flags</em><code>]</code> +</strong></blockquote> + +as the third argument to the <tt>RewriteRule</tt> directive. <em>Flags</em> is a +comma-separated list of the following flags: + +<ul> +<li>'<strong><code>redirect|R</code>[=<i>code</i>]</strong>' (force <a name="redirect"><b>r</b>edirect</a>)<br> + Prefix <em>Substitution</em> + with <code>http://thishost[:thisport]/</code> (which makes the new URL a URI) to + force a external redirection. If no <i>code</i> is given a HTTP response + of 302 (MOVED TEMPORARILY) is used. If you want to use other response + codes in the range 300-400 just specify them as a number or use + one of the following symbolic names: <tt>temp</tt> (default), <tt>permanent</tt>, + <tt>seeother</tt>. + Use it for rules which should + canonicalize the URL and gives it back to the client, e.g. translate + ``<code>/~</code>'' into ``<code>/u/</code>'' or always append a slash to + <code>/u/</code><em>user</em>, etc.<br> + <p> + <b>Notice:</b> When you use this flag, make sure that the + substitution field is a valid URL! If not, you are redirecting to an + invalid location! And remember that this flag itself only prefixes the + URL with <code>http://thishost[:thisport]/</code>, but rewriting goes on. + Usually you also want to stop and do the redirection immediately. To stop + the rewriting you also have to provide the 'L' flag. +<p> +<li>'<strong><code>forbidden|F</code></strong>' (force URL to be <b>f</b>orbidden)<br> + This forces the current URL to be forbidden, i.e. it immediately sends + back a HTTP response of 403 (FORBIDDEN). Use this flag in conjunction with + appropriate RewriteConds to conditionally block some URLs. +<p> +<li>'<strong><code>gone|G</code></strong>' (force URL to be <b>g</b>one)<br> + This forces the current URL to be gone, i.e. it immediately sends back a + HTTP response of 410 (GONE). Use this flag to mark no longer existing + pages as gone. +<p> +<li>'<strong><code>proxy|P</code></strong>' (force <b>p</b>roxy)<br> + This flag forces the substitution part to be internally forced as a proxy + request and immediately (i.e. rewriting rule processing stops here) put + through the proxy module. You have to make sure that the substitution + string is a valid URI (e.g. typically <tt>http://</tt>) which can + be handled by the Apache proxy module. If not you get an error from + the proxy module. Use this flag to achieve a more powerful implementation + of the <tt>mod_proxy</tt> directive <tt>ProxyPass</tt>, to map + some remote stuff into the namespace of the local server. + <p> + Notice: <b>You really have to put <tt>ProxyRequests On</tt> into your + server configuration to prevent proxy requests from leading to core-dumps + inside the Apache kernel. If you have not compiled in the proxy module, + then there is no core-dump problem, because mod_rewrite checks for + existence of the proxy module and if lost forbids proxy URLs. </b> +<p> +<li>'<strong><code>last|L</code></strong>' (<b>l</b>ast rule)<br> + Stop the rewriting process here and + don't apply any more rewriting rules. This corresponds to the Perl + <code>last</code> command or the <code>break</code> command from the C + language. Use this flag to prevent the currently rewritten URL from being + rewritten further by following rules which may be wrong. For + example, use it to rewrite the root-path URL ('<code>/</code>') to a real + one, e.g. '<code>/e/www/</code>'. +<p> +<li>'<strong><code>next|N</code></strong>' (<b>n</b>ext round)<br> + Re-run the rewriting process (starting again with the first rewriting + rule). Here the URL to match is again not the original URL but the URL + from the last rewriting rule. This corresponds to the Perl + <code>next</code> command or the <code>continue</code> command from the C + language. Use this flag to restart the rewriting process, i.e. to + immediately go to the top of the loop. <br> + <b>But be careful not to create a deadloop!</b> +<p> +<li>'<strong><code>chain|C</code></strong>' (<b>c</b>hained with next rule)<br> + This flag chains the current rule with the next rule (which itself can + also be chained with its following rule, etc.). This has the following + effect: if a rule matches, then processing continues as usual, i.e. the + flag has no effect. If the rule does <b>not</b> match, then all following + chained rules are skipped. For instance, use it to remove the + ``<tt>.www</tt>'' part inside a per-directory rule set when you let an + external redirect happen (where the ``<tt>.www</tt>'' part should not to + occur!). +<p> +<li>'<strong><code>type|T</code></strong>=<em>mime-type</em>' (force MIME <b>t</b>ype)<br> + Force the MIME-type of the target file to be <em>mime-type</em>. For + instance, this can be used to simulate the old <tt>mod_alias</tt> + directive <tt>ScriptAlias</tt> which internally forces all files inside + the mapped directory to have a MIME type of + ``<tt>application/x-httpd-cgi</tt>''. +<p> +<li>'<strong><code>nosubreq|NS</code></strong>' (used only if <b>n</b>o internal <b>s</b>ub-request)<br> + This flag forces the rewriting engine to skip a rewriting rule if the + current request is an internal sub-request. For instance, sub-requests + occur internally in Apache when <tt>mod_include</tt> tries to find out + information about possible directory default files (<tt>index.xxx</tt>). + On sub-requests it is not always useful and even sometimes causes a failure to + if the complete set of rules are applied. Use this flag to exclude some rules.<br> + <p> + Use the following rule for your decision: whenever you prefix some URLs + with CGI-scripts to force them to be processed by the CGI-script, the + chance is high that you will run into problems (or even overhead) on sub-requests. + In these cases, use this flag. +<p> +<li>'<strong><code>qsappend|QSA</code></strong>' (<b>q</b>uery <b>s</b>tring + <b>a</b>ppend)<br> + This flag forces the rewriting engine to append a query + string part in the substitution string to the existing one instead of + replacing it. Use this when you want to add more data to the query string + via a rewrite rule. +<p> +<li>'<strong><code>passthrough|PT</code></strong>' (<b>p</b>ass <b>t</b>hrough to next handler)<br> + This flag forces the rewriting engine to set the <code>uri</code> field + of the internal <code>request_rec</code> structure to the value + of the <code>filename</code> field. This flag is just a hack to be able + to post-process the output of <tt>RewriteRule</tt> directives by + <tt>Alias</tt>, <tt>ScriptAlias</tt>, <tt>Redirect</tt>, etc. directives + from other URI-to-filename translators. A trivial example to show the + semantics: + If you want to rewrite <tt>/abc</tt> to <tt>/def</tt> via the rewriting + engine of <tt>mod_rewrite</tt> and then <tt>/def</tt> to <tt>/ghi</tt> + with <tt>mod_alias</tt>: + <pre> + RewriteRule ^/abc(.*) /def$1 [PT] + Alias /def /ghi + </pre> + If you omit the <tt>PT</tt> flag then <tt>mod_rewrite</tt> + will do its job fine, i.e. it rewrites <tt>uri=/abc/...</tt> to + <tt>filename=/def/...</tt> as a full API-compliant URI-to-filename + translator should do. Then <tt>mod_alias</tt> comes and tries to do a + URI-to-filename transition which will not work. + <p> + Notice: <b>You have to use this flag if you want to intermix directives + of different modules which contain URL-to-filename translators</b>. The + typical example is the use of <tt>mod_alias</tt> and + <tt>mod_rewrite</tt>.. +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +<font size=-1> + <b>For the Apache hackers:</b><br> + If the current Apache API had a + filename-to-filename hook additionally to the URI-to-filename hook then + we wouldn't need this flag! But without such a hook this flag is the + only solution. The Apache Group has discussed this problem and will + add such hooks into Apache version 2.0. +</font> +</td></tr> +</table> +<p> +<li>'<strong><code>skip|S</code></strong>=<em>num</em>' (<b>s</b>kip next rule(s))<br> + This flag forces the rewriting engine to skip the next <em>num</em> rules + in sequence when the current rule matches. Use this to make pseudo + if-then-else constructs: The last rule of the then-clause becomes + a <tt>skip=N</tt> where N is the number of rules in the else-clause. + (This is <b>not</b> the same as the 'chain|C' flag!) +<p> +<li>'<strong><code>env|E=</code></strong><i>VAR</i>:<i>VAL</i>' (set <b>e</b>nvironment variable)<br> + This forces an environment variable named <i>VAR</i> to be set to the value + <i>VAL</i>, where <i>VAL</i> can contain regexp backreferences <tt>$N</tt> + which will be expanded. You can use this flag more than once to set more + than one variable. The variables can be later dereferenced at a lot of + situations, but the usual location will be from within XSSI (via + <tt><!--#echo var="VAR"--></tt>) or CGI (e.g. <tt>$ENV{'VAR'}</tt>). + But additionally you can also dereference it in a following RewriteCond + pattern via <tt>%{ENV:VAR}</tt>. Use this to strip but remember + information from URLs. +</ul> + +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +Remember: Never forget that <em>Pattern</em> gets applied to a complete URL +in per-server configuration files. <b>But in per-directory configuration +files, the per-directory prefix (which always is the same for a specific +directory!) gets automatically <em>removed</em> for the pattern matching and +automatically <em>added</em> after the substitution has been done.</b> This feature is +essential for many sorts of rewriting, because without this prefix stripping +you have to match the parent directory which is not always possible. +<p> +There is one exception: If a substitution string starts with +``<tt>http://</tt>'' then the directory prefix will be <b>not</b> added and a +external redirect or proxy throughput (if flag <b>P</b> is used!) is forced! +</td></tr> +</table> + +<p> +<table width="70%" border=0 bgcolor="#fff0f0" cellspacing=0 cellpadding=10> +<tr><td> +Notice! To enable the rewriting engine for per-directory configuration files +you need to set ``<tt>RewriteEngine On</tt>'' in these files <b>and</b> +``<tt>Option FollowSymLinks</tt>'' enabled. If your administrator has +disabled override of <tt>FollowSymLinks</tt> for a user's directory, then +you cannot use the rewriting engine. This restriction is needed for +security reasons. +</td></tr> +</table> + +<p> +Here are all possible substitution combinations and their meanings: + +<p> +<b>Inside per-server configuration (<tt>httpd.conf</tt>)<br> +for request ``<tt>GET /somepath/pathinfo</tt>'':</b><br> + +<p> +<table bgcolor="#f0f0f0" cellspacing=0 cellpadding=5> +<tr> +<td> +<pre> +<b>Given Rule</b> <b>Resulting Substitution</b> +---------------------------------------------- ---------------------------------- +^/somepath(.*) otherpath$1 not supported, because invalid! + +^/somepath(.*) otherpath$1 [R] not supported, because invalid! + +^/somepath(.*) otherpath$1 [P] not supported, because invalid! +---------------------------------------------- ---------------------------------- +^/somepath(.*) /otherpath$1 /otherpath/pathinfo + +^/somepath(.*) /otherpath$1 [R] http://thishost/otherpath/pathinfo + via external redirection + +^/somepath(.*) /otherpath$1 [P] not supported, because silly! +---------------------------------------------- ---------------------------------- +^/somepath(.*) http://thishost/otherpath$1 /otherpath/pathinfo + +^/somepath(.*) http://thishost/otherpath$1 [R] http://thishost/otherpath/pathinfo + via external redirection + +^/somepath(.*) http://thishost/otherpath$1 [P] not supported, because silly! +---------------------------------------------- ---------------------------------- +^/somepath(.*) http://otherhost/otherpath$1 http://otherhost/otherpath/pathinfo + via external redirection + +^/somepath(.*) http://otherhost/otherpath$1 [R] http://otherhost/otherpath/pathinfo + via external redirection + (the [R] flag is redundant) + +^/somepath(.*) http://otherhost/otherpath$1 [P] http://otherhost/otherpath/pathinfo + via internal proxy +</pre> +</td> +</tr> +</table> + +<p> +<b>Inside per-directory configuration for <tt>/somepath</tt><br> +(i.e. file <tt>.htaccess</tt> in dir <tt>/physical/path/to/somepath</tt> containing +<tt>RewriteBase /somepath</tt>)<br> for +request ``<tt>GET /somepath/localpath/pathinfo</tt>'':</b><br> + +<p> +<table bgcolor="#f0f0f0" cellspacing=0 cellpadding=5> +<tr> +<td> +<pre> +<b>Given Rule</b> <b>Resulting Substitution</b> +---------------------------------------------- ---------------------------------- +^localpath(.*) otherpath$1 /somepath/otherpath/pathinfo + +^localpath(.*) otherpath$1 [R] http://thishost/somepath/otherpath/pathinfo + via external redirection + +^localpath(.*) otherpath$1 [P] not supported, because silly! +---------------------------------------------- ---------------------------------- +^localpath(.*) /otherpath$1 /otherpath/pathinfo + +^localpath(.*) /otherpath$1 [R] http://thishost/otherpath/pathinfo + via external redirection + +^localpath(.*) /otherpath$1 [P] not supported, because silly! +---------------------------------------------- ---------------------------------- +^localpath(.*) http://thishost/otherpath$1 /otherpath/pathinfo + +^localpath(.*) http://thishost/otherpath$1 [R] http://thishost/otherpath/pathinfo + via external redirection + +^localpath(.*) http://thishost/otherpath$1 [P] not supported, because silly! +---------------------------------------------- ---------------------------------- +^localpath(.*) http://otherhost/otherpath$1 http://otherhost/otherpath/pathinfo + via external redirection + +^localpath(.*) http://otherhost/otherpath$1 [R] http://otherhost/otherpath/pathinfo + via external redirection + (the [R] flag is redundant) + +^localpath(.*) http://otherhost/otherpath$1 [P] http://otherhost/otherpath/pathinfo + via internal proxy +</pre> +</td> +</tr> +</table> + + +<p> +<b>Example:</b> +<p> +<blockquote> +We want to rewrite URLs of the form +<blockquote> +<code>/</code> <em>Language</em> +<code>/~</code> <em>Realname</em> +<code>/.../</code> <em>File</em> +</blockquote> +into +<blockquote> +<code>/u/</code> <em>Username</em> +<code>/.../</code> <em>File</em> +<code>.</code> <em>Language</em> +</blockquote> +<p> +We take the rewrite mapfile from above and save it under +<code>/anywhere/map.real-to-user</code>. Then we only have to add the +following lines to the Apache server configuration file: + +<blockquote> +<pre> +RewriteLog /anywhere/rewrite.log +RewriteMap real-to-user txt:/anywhere/map.real-to-host +RewriteRule ^/([^/]+)/~([^/]+)/(.*)$ /u/${real-to-user:$2|nobody}/$3.$1 +</pre> +</blockquote> +</blockquote> + + +<!--%hypertext --> +<hr> +<!--/%hypertext --> + +<center> +<a name="Additional"> +<h1>Additional Features</h1> +</a> +</center> + +<a name="EnvVar"> +<h2>Environment Variables</h2> +</a> + +This module keeps track of two additional (non-standard) CGI/SSI environment +variables named <tt>SCRIPT_URL</tt> and <tt>SCRIPT_URI</tt>. These contain +the <em>logical</em> Web-view to the current resource, while the standard CGI/SSI +variables <tt>SCRIPT_NAME</tt> and <tt>SCRIPT_FILENAME</tt> contain the +<em>physical</em> System-view. + +<p> +Notice: These variables hold the URI/URL <em>as they were initially +requested</em>, i.e. in a state <em>before</em> any rewriting. This is +important because the rewriting process is primarily used to rewrite logical +URLs to physical pathnames. + +<p> +<b>Example:</b> + +<blockquote> +<pre> +SCRIPT_NAME=/v/sw/free/lib/apache/global/u/rse/.www/index.html +SCRIPT_FILENAME=/u/rse/.www/index.html +SCRIPT_URL=/u/rse/ +SCRIPT_URI=http://en2.en.sdm.de/u/rse/ +</pre> +</blockquote> + + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> +<!--/%hypertext --> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_status.html b/usr.sbin/httpd/htdocs/manual/mod/mod_status.html new file mode 100644 index 00000000000..f88c38c00b8 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_status.html @@ -0,0 +1,120 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>Apache module mod_status</title> +</head><!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> + +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Module mod_status</h1> + +The Status Module is only available in Apache 1.1 and later.<p> + +<h2>Function</h2> + +The Status module allows a server administrator to find out how well +their server is performing. A HTML page is presented that gives +the current server statistics in an easily readable form. If required +this page can be made to automatically refresh (given a compatible +browser). Another page gives a simple machine-readable list of the current +server state. +<p> +The details given are: +<ul> +<li>The number of children serving requests +<li>The number of idle children +<li>The status of each child, the number of requests that child has +performed and the total number of bytes served by the child (*) +<li>A total number of accesses and byte count served (*) +<li>The time the server was started/restarted and the +time it has been running for +<li>Averages giving the number of requests per second, +the number of bytes served per second and the average number +of bytes per request (*) +<li>The current percentage CPU used by each child and in total by +Apache (*) +<li>The current hosts and requests being processed (*) +</ul> + +A compile-time option must be used to display the details marked "(*)" as +the instrumentation required for obtaining these statistics does not +exist within standard Apache. + +<h2>Enabling Status Support</h2> + +To enable status reports only for browsers from the foo.com +domain add this code to your <code>access.conf</code> configuration file +<pre> + <Location /server-status> + SetHandler server-status + + order deny,allow + deny from all + allow from .foo.com + </Location> +</pre> +<p> +You can now access server statistics by using a Web browser to access the +page <code>http://your.server.name/server-status</code> +<p> +Note that mod_status will only work when you are running Apache in +<A HREF="core.html#servertype">standalone</A> mode and not +<A HREF="core.html#servertype">inetd</A> mode. + +<h3>Automatic Updates</h3> +You can get the status page to update itself automatically if you have +a browser that supports "refresh". Access the page +<code>http://your.server.name/server-status?refresh=N</code> to refresh the page +every N seconds. +<h3>Machine Readable Status File</h3> +A machine-readable version of the status file is available by accessing the +page <code>http://your.server.name/server-status?auto</code>. This is useful +when automatically run, see the Perl program in the <code>/support</code> +directory of Apache, <code>log_server_status</code>. + +<h2>Full Instrumentation</h2> + +To obtain full statistics you must compile Apache with a special +directive. On some machines there may be a small performance loss +if you do this. Try full statistics and see if you notice any +difference. If you do please contact <a href="mailto:mark@ukweb.com"> +mark@ukweb.com</a> and tell me your configuration. + +<p> + +Do this by adding the following to the AUX_CFLAGS line in the +"Configuration" file and then recompiling as usual. +<pre> + AUX_CFLAGS= (something) -DSTATUS +</pre> + +<BLOCKQUOTE> + <STRONG> + It should be noted that if <SAMP>mod_status</SAMP> is compiled into + the server, its handler capability is available in <EM>all</EM> + configuration files, including <EM>per</EM>-directory files + (<EM>e.g.</EM>, <SAMP>.htaccess</SAMP>). This may have + security-related ramifications for your site. + </STRONG> +</BLOCKQUOTE> +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_userdir.html b/usr.sbin/httpd/htdocs/manual/mod/mod_userdir.html new file mode 100644 index 00000000000..946f8da6db2 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_userdir.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_userdir</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_userdir</h1> + +This module is contained in the <code>mod_userdir.c</code> file, and +is compiled in by default. It provides for user-specific directories. + + +<ul> +<li><A HREF="#userdir">UserDir</A> +</ul> +<hr> + + +<h2><A name="userdir">UserDir</A></h2> +<!--%plaintext <?INDEX {\tt UserDir} directive> --> +<strong>Syntax:</strong> UserDir <em>directory/filename</em><br> +<strong>Default:</strong> <code>UserDir public_html</code><br> +<Strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> Base<br> +<strong>Module:</strong> mod_userdir<br> +<strong>Compatibility:</strong> All forms except the <code>UserDir +public_html</code> form are only available in Apache 1.1 or above.<p> + +The UserDir directive sets the real directory in a user's home directory +to use when a request for a document for a user is received. +<em>Directory</em> is either <code>disabled</code>, to disable this feature, + or the name of a directory, following one of the following +patterns. If not disabled, then a request for +<code>http://www.foo.com/~bob/one/two.html</code> will be translated to: +<pre> +UserDir public_html -> ~bob/public_html/one/two.html +UserDir /usr/web -> /usr/web/bob/one/two.html +UserDir /home/*/www -> /home/bob/www/one/two.html +</pre> +The following directives will send redirects to the client: +<pre> +UserDir http://www.foo.com/users -> http//www.foo.com/users/bob/one/two.html +UserDir http://www.foo.com/*/usr -> http://www.foo.com/bob/usr/one/two.html +UserDir http://www.foo.com/~*/ -> http://www.foo.com/~bob/one/two.html +</pre> + +<P> +<STRONG> +Be careful when using this directive; for instance, <SAMP>"UserDir +./"</SAMP> would map <SAMP>"/~root"</SAMP> to +<SAMP>"/"</SAMP> - which is probably undesirable. See also +the +<A + HREF="core.html#directory" +><Directory></A> +directive and the +<A + HREF="../misc/security_tips.html" +>Security Tips</A> +page for more information. +</STRONG> +</P> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/mod/mod_usertrack.html b/usr.sbin/httpd/htdocs/manual/mod/mod_usertrack.html new file mode 100644 index 00000000000..5dc90165e3f --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/mod/mod_usertrack.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_usertrack</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="../images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Module mod_usertrack</h1> + +Previous releases of Apache have included a module which generates a +'clickstream' log of user activity on a site using cookies. This was +called the "cookies" module, mod_cookies. In Apache 1.2 and later this +module has been renamed the "user tracking" module, +mod_usertrack. This module has been simplified and new directives +added. + +<hr> + +<h2>Logging</h2> + +Previously, the cookies module (now the user tracking module) did its +own logging, using the <tt>CookieLog</tt> directive. In this release, +this module does no logging at all. Instead, a configurable log +format file should be used to log user click-streams. This is possible +because the logging module now allows <a +href="../multilogs.html">multiple log files</a>. The cookie itself is +logged by using the text <tt>%{cookie}n </tt> + +in the log file format. For example: +<pre> +CustomLog logs/clickstream "%{cookie}n %r %t" +</pre> + +For backward compatibility the configurable log module implements the +old <tt>CookieLog</tt> directive, but this should be upgraded to the +above <tt>CustomLog</tt> directive. + +<h2>Directives</h2> + +<ul> +<li><a href="#cookieexpires">CookieExpires</a> +<li><a href="#cookietracking">CookieTracking</a> +</ul> + +<hr> + +<h2><a name="cookieexpires">CookieExpires</A></h2> +<strong>Syntax:</strong> CookieExpires <em>expiry-period</em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> optional<br> +<strong>Module:</strong> mod_usertrack<p> + +When used, this directive sets an expiry time on the cookie generated +by the usertrack module. The <i>expiry-period</i> can be given either +as a number of seconds, or in the format such as "2 weeks 3 days 7 +hours". Valid denominations are: years, months, weeks, hours, minutes +and seconds. + +<p>If this directive is not used, cookies last only for the current +browser session.</p> + +<h2><a name="cookietracking">CookieTracking</A></h2> +<strong>Syntax:</strong> CookieTracking <em>on | off</em><br> +<strong>Context:</strong> server config, virtual host, directory, +.htaccess<br> +<strong>Override:</strong> FileInfo<br> +<strong>Status:</strong> optional<br> +<strong>Module:</strong> mod_usertrack<p> + +When the user track module is compiled in, and "CookieTracking on" is +set, Apache will start sending a user-tracking cookie for all new +requests. This directive can be used to turn this behavior on or off +on a per-server or per-directory basis. By default, compiling +mod_usertrack will not activate cookies. + + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/multilogs.html b/usr.sbin/httpd/htdocs/manual/multilogs.html new file mode 100644 index 00000000000..e777f0e9393 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/multilogs.html @@ -0,0 +1,134 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache Multiple Log Files</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Multiple Log Files</h1> + +It is now possible to specify multiple log files, each with a fully +customizable format. This is compatible with existing +configurations. Multiple log files are implemented as part of the <a +href="mod/mod_log_config.html">mod_log_config</a> module which as of +Apache 1.2 is the default log module. + +<hr> + +<h2>Using Multiple Log Files</h2> + +Multiple log files be created with either the <code>TransferLog</code> +or <code>CustomLog</code> directive. These directives can be +repeated to create more than one log file (in previous releases, +only one logfile could be given per server configuration). +The <code>TransferLog</code> directive creates a log file +in the standard "common log format", although this can be customized +with <code>LogFormat</code>. The syntax of these two +directives is the same as for the config log module in previous +Apache releases. +<p> + +The real power of multiple log files come from the ability to +create log files in different formats. For example, as well +as a CLF transfer log, the server could log the user agent of +each client, or the referrer information, or any other aspect of +the request, such as the language preferences of the user. +<p> + +The new <code>CustomLog</code> directive takes both a filename to log +to, and a log file format. + +<hr> + +<strong>Syntax:</strong> CustomLog <em>filename "format"</em><br> +<strong>Context:</strong> server config, virtual host<br> +<strong>Status:</strong> base<br> +<strong>Module:</strong> mod_log_config<p> + +The first argument is the filename to log to. This is used +exactly like the argument to <code>TransferLog</code>, that is, +it is either a file as a full path or relative to the current +server root, or |programname. Be aware that anyone who can write to +the directory where a log file is written can gain access to the uid +that starts the server. See the <A HREF="misc/security_tips.html"> +security tips</A> document for details.<p> + +The format argument specifies a format for each line of the log file. +The options available for the format are exactly the same as for +the argument of the <code>LogFormat</code> directive. If the format +includes any spaces (which it will do in almost all cases) it +should be enclosed in double quotes. +<p> + +<h3>Use with Virtual Hosts</h3> + +If a <VirtualHost> section does not contain any +<code>TransferLog</code> or <code>CustomLog</code> directives, the +logs defined for the main server will be used. If it does +contain one or more of these directives, requests serviced by +this virtual host will only be logged in the log files defined +within its definition, not in any of the main server's log files. +See the examples below. +<p> + +<hr> + +<h3>Examples</h3> + +To create a normal (CLF) format log file in logs/access_log, and a +log of user agents: + +<pre> +TransferLog logs/access_log +CustomLog logs/agents "%{user-agent}i" +</pre> + +To define a CLF transfer log and a referrer log which log +all accesses to both the main server and a virtual host: + +<pre> +TransferLog logs/access_log +CustomLog logs/referer "%{referer}i" + +<VirtualHost> + DocumentRoot /whatever + ServerName my.virtual.host +</VirtualHost> +</pre> + +Since no TransferLog or CustomLog directives appear inside the +<VirtualHost> section, any requests for this virtual host +will be logged in the main server's log files. If however the +directive + +<pre> +TransferLog logs/vhost_access_log +</pre> + +was added inside the virtual host definition, then accesses to this +virtual host will be logged in vhost_access_log file (in common +log format), and <i>not</i> in logs/access_log or logs/referer. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/new_features_1_0.html b/usr.sbin/httpd/htdocs/manual/new_features_1_0.html new file mode 100644 index 00000000000..812da8b2845 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/new_features_1_0.html @@ -0,0 +1,137 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache extra features</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Overview of new features</h1> + +<H2>New Features with Apache 1.0</H2> + +<P>New features with this release, as extensions of the Apache +functionality (see also more detailed <code>CHANGES</code> file) in +the source directory. Because the core code has changed so +significantly, there are certain liberties that earlier versions of +Apache (and the NCSA daemon) took that Apache 1.0 is pickier about - +please check the <a href="misc/compat_notes.html">compatibility notes</a> if you have any problems. + +<UL> + +<LI> API for server extensions --- see below for a brief sermon on +philosophy, or see <a href="misc/API.html">src/API.html</a> for an actual +overview. Most server functionality (including includes, CGI, and +most forms of access control) are actually implemented as +API-conformant modules; you can also do other neat stuff (we've +included a sample module, for instance, which one of us is using to +track click-trails using the <a +href="http://home.netscape.com/newsref/std/cookie_spec.html">Netscape +cookie mechanism</a>, for visitors who come in through Netscape +clients). <a href="mod/mod_dld.html">Modules</a> can also be loaded dynamically using GNU DLD. + +<P> +The API is not yet quite stable (see src/TODO for some possible +changes), but anything done now will be easily adapted for future +versions --- after all, we have more modules to adapt than you do. + +<P> + +<LI> <a href="process-model.html">New Process Model - much less forking, no fixed number of children.</a> + +We found that many people were using values for "MaxServers" either +too high or too low, and were hanging themselves on it. The model we +adopted is still based on long-lived minimal-forking processes, but +instead of specifying one number of persistent processes, the +web-master specifies a maximum and minimum number of processes to be +"spare" - every couple of seconds the parent checks the actual number +of spare servers and adjusts accordingly. This should keep the number +of servers concurrently running relatively low while still ensuring +minimal forking. + +<P> + +<LI> <a href="virtual-host.html"><VirtualHost> (the configuration directive for +multiple-homed servers)</a> is more general now. Just about any srm.conf or +httpd.conf command can go in a <Virtualhost> section, with the +following specific exceptions: ServerType, UserId, GroupId, +StartServers, MaxRequestsPerChild, BindAddress, PidFile, TypesConfig, +ServerRoot. + +<P> + +<LI> <a href="content-negotiation.html">Support for content negotiation of languages through MultiViews</a> +(*.fr, *.de, *.en suffixes), via the new AddLanguage and LanguagePriority +commands (code written by Florent Guillaume, guillaum@clipper.ens.fr). + +<P> + +<LI> Significant internal cleanups and rearrangements. The two externally + visible consequences of this are that just about all of the unchecked + fixed limits are gone, and that the server is somewhat pickier about + config file syntax (noting and complaining about extraneous command + arguments or other stuff at the end of command lines). + +<P> + +<LI> XBITHACK is a run-time option, and can be selectively enabled per + directory --- the -DXBITHACK compile-time option just changes the + default. The command which configures it is "XBitHack", which is + allowed everywhere "Options" is; this takes an argument --- + "XBitHack Off" turns it off; "XBitHack On" gets you the NCSA + -DXBITHACK behavior; and "XBitHack Full" gets you the Apache GXBIT + stuff on top of that. (-DXBITHACK makes "Full" the default; + otherwise, it defaults "Off"). + +<P> + +<LI> TransferLog can specify a program which gets the log entries piped to it, + a la 'TransferLog "| /var/www/my-perl-script -arg valu"' --- this should + give the same SIGTERM/pause/SIGKILL treatment to the logging process on + server restarts that a CGI script gets on an aborted request. NB the + server is counting on the logging process to work, and will probably hang + or worse if it dies. + +<P> + +<LI> <a href="mod/mod_log_config.html">Configurable logging module</a> --- this + is a replacement for the standard plane-jane Common Log Format code, which + supports a LogFormat directive which allows you to control the formatting of + entries in the TransferLog, and add some new items if you like (in + particular, Referer and User-Agent). EXPERIMENTAL. +</ul> +<p><hr> + + +<H2>Other features of Apache</h2> +<ul> +<LI><A HREF="mod/mod_dld.html">Dynamically loading modules using GNU DLD</A> +<LI><A HREF="mod/mod_imap.html">Imagemap Module</A> +<li><A HREF="mod/mod_dir.html#directoryindex">Multiple DirectoryIndex filenames</A> +<LI><A HREF="mod/mod_asis.html"> "Send as is" file types</A> +<li><A HREF="mod/mod_include.html#xbithack">XBITHACK last modified</A> +</ul> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/new_features_1_1.html b/usr.sbin/httpd/htdocs/manual/new_features_1_1.html new file mode 100644 index 00000000000..eb29707db1a --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/new_features_1_1.html @@ -0,0 +1,225 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>New features with Apache 1.1</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Overview of new features</h1> + +<h2>API Changes</h2> + +A few changes to the Apache API were made for 1.1. It is possible +that some third-party modules will no longer work with 1.1, though +we have made every effort to provide backwards-compatibility. If you +encounter a module that does not work with 1.1, please <a +href="http://www.apache.org/bug_report.html">let us know</a>. + + +<h2>New Features with Apache 1.1</h2> +<p>New features with this release, as extensions of the Apache +functionality (see also more detailed <code>CHANGES</code> file in +the source directory.) Because the core code has changed so +significantly, there are certain liberties that earlier versions of +Apache (and the NCSA daemon) took that recent Apache versions are +pickier about - please check the <a +href="misc/compat_notes.html">compatibility notes</a> if you have any +problems.</p> +<hr> + +<p>In addition to a number of bug fixes and internal performance +enhancements, <a href="http://www.apache.org/dist/">Apache +1.1</a> has the following specific new user features:</p> + +<ul> + +<li><b><a href="keepalive.html">Support for Keep-Alive Persistent +Connections</a></b><br> Apache now has (optional) support for persistent +connections, as defined by the HTTP/1.1 draft. This protocol, +supported by a number of current HTTP servers and browsers (including +Netscape Navigator 2.0) has been shown to increase speed of document +transfer by up to 50% in certain cases. + +<li><b><a href="host.html">New non-IP Intensive VirtualHost +Support</a></b><br> +Apache's support for virtual hosts has been enhanced to be able to +use information sent by some new Web browsers to determine the server +being accessed, without requiring an additional IP address for each +host. + +<li><b><a href="bind.html">Listen to Multiple Addresses and +Ports</a></b><br> +Using the new <code>Listen</code> directive, Apache can listen to more +than one port and IP address, using the same configuration set. + +<li><b><a href="mod/mod_status.html">Status +Module</a></b><br> +Apache now contains a module that gives the web-master accurate, +up-to-date information about the server's status and its resource +consumption. It also gives the current state of each server process +including the current URL being processed. For an example, check out +<A HREF="http://www.apache.org/status">the status of the +www.apache.org server</A>. + +<li><b><a href="mod/mod_info.html">Server Information Module</a></b></br> +This module gives a plethora of information about the other modules +installed, their directives, and their configurations. It is +extremely helpful in debugging configuration problems. For an +example, check out <A +HREF="http://www.apache.org/serv-info">information about the +www.apache.org server</A>. + +<li><b><a href="mod/mod_proxy.html">Experimental Caching Proxy Server</a></b><br> +Apache can now act as +an HTTP proxy server, allowing clients behind firewalls to use the +server to access the outside world. In addition, it can cache +documents it proxies, speeding up access to frequently requested +documents. + +<li><b><a href="location.html">URL-based Access Protection</a></b><br> +In addition to access checking and authorization by filename (with +<code><a href="mod/core.html#directory"><Directory></a></code>), +the new <code><Location></code> directive allows protection by +URL. + +<li><b><a href="mod/mod_actions.html">Filetype-based Script "Actions"</a></b><br> +You can now run CGI scripts whenever a file of a certain type is +requested. This makes it much easier to execute scripts that process +files. In addition, you can use the new <a +href="mod/mod_actions.html#script">Script</a> directive to enable scripts +for files called with HTTP methods Apache does not natively support. + +<li><b><a href="handler.html">New "Handler" Directives</a></b><br> +The new <code>AddHandler</code> and <code>SetHandler</code> directive +allows "handlers" to be defined for filename extensions or +directories. These handlers, which can either be built into Apache or +added with the <a href="mod/mod_actions.html">Action</a> directive, extend +Apache's range of usability, and almost entirely remove the "magic" +media types. + +<li><b><a href="mod/mod_env.html">Customizable CGI Environment +Variables</a></b><br> +New <code>PassEnv</code> and <code>SetEnv</code> directives allow you to +modify the environment variables passed to CGI scripts. + +<li><b><a href="mod/mod_cern_meta.html">CERN Metafile Support</a></b><br> +Now emulates the CERN httpd's support for metafiles containing additional +HTTP headers to be supplied with a document. + +<li><b><a href="mod/mod_imap.html">Improved Imagemap Support</a></b><br> +The internal imagemap handling code has been rewritten and +reorganized, adding new handling of default, base and relative URLs, +and support for creating non-graphical menus for use with clients that +do not support imagemaps. + +<li><b><a href="mod/mod_userdir.html">Improved UserDir Directive</a></b><br> +Now supports the ability to point user's files (as specified by URLs +beginning with the "<code>~</code>" character) at directories other +than those specified by the Unix password file. + +<li><b>Minimal DNS Now Runtime Option</b><br> +New <code>HostnameLookups</code> +server configuration directive can be used to turn <code>On</code> or +<code>Off</code> DNS lookups. This supersedes the -DMINIMAL_DNS +compile-time configuration option. This option can be set per-directory. + +<li><b>IdentityCheck Now Per-Directory Option</b><br> +The <code>IdentityCheck</code> directive, which controls the use of +ident to check the remote user name, can now be set per directory. The +ident support is also RFC 1413-compliant. + +<li><b>Redirect Now Usable in <code>.htaccess</code> Files</b><br> +The <a href="mod/mod_alias.html#redirect"><code>Redirect</code></a> +directive can now be used in <code>.htaccess</code> files when the +<code>FileInfo</code> directive has been set on. This allows users to +redirect parts of their directories without requiring CGI scripts + +<li><b>ErrorDocument Now Usable in <code>.htaccess</code> Files</b><br> +The <a href="custom-error.html"><code>ErrorDocument</code></a> +directive can now be used in <code>.htaccess</code> files when the +<code>FileInfo</code> directive has been set on. This allows users to +have different error messages for different sections of a site. + +<li><b><code>ForceType</code> Directive</b><br> +This new directive, in <code><Directory></code> sections or +.htaccess files, allows you to override the filename extensions and +force a single content type. (e.g. <code>ForceType +application/octet-stream</code>) + +<li><b>File Owner Available to Included CGI Scripts</b><br> +Server-side includes that call CGI scripts will now set a +<code>USER_NAME</code> environment variable that contains the owner of +the file which included it. + +<li><b>Improved Icons</b><br> +Thanks to <a href="mailto:kevinh@eit.com">Kevin +Hughes</a>, Apache's nifty color GIF icons for directory listings have +been updated. In addition, the <a +href="../apache_pb.gif">Powered by Apache</a> +(<code>apache_pb.gif</code>) logo has been included. + +</ul> + +<hr> + +<h3>New Authentication Modules</h3> + +<p><strong>Note:</strong> These modules are not +compiled into the server by default, as they require special support +on the host system. They must be enabled specifically in the +<code>Configuration</code> file.</p> + +<ul> + +<li><b><a href="mod/mod_auth_anon.html">Anonymous HTTP Logins</a></b><br> +New options allow you to allow, using Basic HTTP Authentication, +anonymous logins, like those of FTP. This allows you to collect email +addresses of people accessing your site. + +<li><b><a href="mod/mod_digest.html">Support for Digest +Authentication</a></b><br> +Apache now supports digest authentication using RSA MD5 +encryption. When used with a supporting web browser, this provides a +more secure alternative to Basic authentication. + +<li><strong><a href="mod/mod_auth_db.html">Support for Unix DB +Authentication</a></strong> - <code>mod_auth_db.c</code><br> In +addition to <a href="mod/mod_auth_dbm.html">DBM</a> support, Apache now +contains optional support for Berkeley DB databases. + +<li><strong><a href="mod/mod_auth_msql.html">mSQL Database +Authentication</a></strong> - <code>mod_auth_msql.html</code> <BR> +Support for the use of mSQL databases for user authentication via HTTP +is now supported. + +</ul> + +<hr> + +<h3>OS/2 Support</h3> + +<p>Apache now includes support for OS/2, thanks to <a href="http://www.slink.com/ApacheOS2/">Softlink Services.</a></p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/new_features_1_2.html b/usr.sbin/httpd/htdocs/manual/new_features_1_2.html new file mode 100644 index 00000000000..e46cf29c1f6 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/new_features_1_2.html @@ -0,0 +1,220 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>New features with Apache 1.2</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Overview of new features</h1> + +<h2>API Changes</h2> + +<p>Some non-compatible changes were made to the Apache API in order to +deal with HTTP/1.1 compatibility. It is possible that some modules +will no longer work (specifically, those that process input using the +POST or PUT methods). If you encounter a module that does not work, +please contact the author. A <a +href="misc/client_block_api.html">programmer's note</a> on the subject is +available.</p> + +<p>Additionally, some changes were made to the CGI environment that +may cause some CGI scripts to work incorrectly. If you are +experiencing trouble with a CGI that worked fine under Apache 1.1.1, +please see <a href="cgi_path.html">our explanation of the changes.</a></p> + +<h2>New Features with Apache 1.2</h2> +<p>New features with this release, as extensions of the Apache +functionality. Because the core code has changed so +significantly, there are certain liberties that earlier versions of +Apache (and the NCSA daemon) took that recent Apache versions are +pickier about - please check the <a +href="misc/compat_notes.html">compatibility notes</a> if you have any +problems.</p> +<hr> + +<p>In addition to a number of bug fixes and internal performance +enhancements, <a href="http://www.apache.org/dist/">Apache +1.2</a> has the following specific new user features:</p> + +<ul> + +<li><b><xa href="http11.html">HTTP/1.1 Compliance</a></b> +[Documentation to be written]<br> +Aside from the optional proxy module (which operates as HTTP/1.0), +Apache is conditionally compliant with the HTTP/1.1 proposed standard, +as approved by the IESG and the +<a href="http://www.ics.uci.edu/pub/ietf/http/">IETF HTTP working group</a>. +HTTP/1.1 provides a much-improved protocol, and +should allow for greater performance and efficiency when transferring +files. Apache does, however, still work great with HTTP/1.0 browsers. +We are very close to being unconditionally compliant; if you note any +deviance from the proposed standard, please report it as a bug. + +<li><b><a href="mod/mod_include.html">eXtended Server Side Includes +(XSSI)</a></b><br> +A new set of server-side include +directives allows the user to better create WWW pages. This includes +number of powerful new features, such as the ability to set variables +and use conditional HTML. + +<li><b><a href="mod/core.html#files">File-based and Regex-enabled +Directive Sections</a></b><br> +The new +<a href="mod/core.html#files"><code><Files></code></a> +section allows directives to be enabled based on full filename, not just directory and URL. In +addition, <code><Files></code> sections can appear in +<code>.htaccess</code> files. <code><Files></code>, along with +<a href="mod/core.html#directory"><code><Directory></code></a> + and <a href="mod/core.html#location"><code><Location></code></a>, can +also now be based on regular expressions, not just simple prefix +matching. + +<li><b><a href="mod/mod_browser.html">Browser-based Environment +Variables</a></b><br> +Environment variables can now be set based on the +<code>User-Agent</code> string of the browser. Combined with <a +href="mod/mod_include.html">XSSI</a>, this allows you to write browser-based +conditional HTML documents. + +<li><b><a href="suexec.html">SetUID CGI Execution</a></b><br> + Apache now + supports the execution of CGI scripts as users other + than the server user. A number of security checks are built in + to try and make this as safe as possible. + +<li><b><a href="mod/mod_rewrite.html">URL Rewriting Module</a></b><br> +The optional <code>mod_rewrite</code> module is now included. This +module can provide powerful URL mapping, using regular +expressions. There's nothing this module can't do! + +<li><b><a href="mod/mod_log_config.html">Enhanced, Configurable +Logging</a></b><br> +The optional <code>mod_log_config</code> included with earlier +versions of Apache is now standard, and has been enhanced to allow +logging of much more detail about the transaction, and can be used to +open <a href="multilogs.html">more than one log file</a> at once +(each of which can have a different log format). If you have Apache +write any logs to a directory which is writable by anyone other than +the user that starts the server, see the <A HREF="misc/security_tips.html"> +security tips</A> document to be sure you aren't putting the security +of your server at risk. + + +<li><b><a href="mod/mod_usertrack.html">User Tracking (Cookies) +Revisions</a></b><br> +The <code>mod_cookies</code> included with previous versions of Apache +has been renamed <code>mod_usertrack</code>, to more accurately +reflect its function (some people inadvertently thought it enabled +cookie support in Apache, which is not true - Apache supports the use +of cookies directly). It is also now possible to disable the +generation of cookies, even when + the cookie module is compiled in. Also, an expiry time can be set + on the cookies. + +<li><b><a href="mod/core.html#virtualhost"><VirtualHost> Enhancements</a></b><br> + The <VirtualHost> directive can now take more than one IP + address or hostname. This lets a single vhost handles requests + for multiple IPs or hostnames. Also the special section + <VirtualHost _default_> can be used to handle requests normally + left for the main server configuration. + +<li><b><a href="mod/mod_cgi.html#cgi_debug">CGI Debugging Environment</a></b><br> +<code>ScriptLog</code> allows you to now set up a log that records +all input and output to failed CGI scripts. This includes environment +variables, input headers, POST data, output, and more. This makes CGI +scripts much easier to debug. + +<li><b><a href="mod/core.html#rlimit">Resource Limits for CGI Scripts</a></b><br> +New directives allow the limiting of resources used by CGI scripts +(e.g. max CPU time). This is helpful in preventing 'runaway' CGI +processes. + +<li><b><a href="mod/mod_alias.html">Redirect Directive Can Return Alternate Status</a></b><br> + The Redirect directive can return permanent or temporary redirects, + "Gone" or "See Other" HTTP status. For NCSA-compatibility, + RedirectTemp and RedirectPermanent are also implemented. + +<li><b><a href="install.html">Simplified Compilation</a></b><br> + The process of configuring Apache for compilation has been + simplified. + +<li><b><a href="mod/core.html#options">Add or Remove Options</a></b><br> + The <code>Options</code> directive can now add or remove options from + those currently in force, rather than always replacing them. + +<li><b><a href="invoking.html#help">Command-line Help</a></b><br> +The <code>-h</code> command-line option now lists all the available +directives. + +<li><b><a href="mod/mod_headers.html">Optional Headers Module to Set or Remove HTTP Headers</a></b><br> +The optional <code>mod_headers</code> module can be used to set custom +headers in the HTTP response. It can append to existing headers, +replace them, or remove headers from the response. + +<li><b><a href="mod/core.html#ifmodule">Conditional Config Directives</a></b><br> +A new <code><IfModule></code> section allows directives to be +enabled only if a given module is loaded into the server. + +<li><b><xa href="ncsa_auth.html">Authorization Directives Now Use +NCSA-style Syntax</a></b><br> + +The <a href="mod/mod_auth.html#authuserfile">AuthUserFile</a>, <a +href="mod/mod_auth.html#authgroupfile">AuthGroupFile</a> and <a +href="mod/mod_digest.html#authdigestfile">AuthDigestFile</a> commands +now have a syntax compatible with the NCSA server. + +<li><b><a href="mod/core.html#satisfy">NCSA Satisfy authentication +directive now implemented</a></b><br> +<code>Satisfy</code> allows for more flexible access control +configurations. + +<li><b>Better NCSA Compatibility</b><br> +Apache directives are now more compatible with NCSA 1.5 to make +moving between servers easier. In particular, Apache now implements the +<a href="mod/core.html#satisfy"><code>Satisfy</code></a>, +<a href="mod/core.html#maxkeepaliverequests">MaxKeepAliveRequests</A>, +<a href="mod/mod_alias.html#redirectperm">RedirectPermanent</A> and +<a href="mod/mod_alias.html#redirecttemp">RedirectTemp</A>, +directives, and the following directives are now syntax-compatible with +NCSA: +<a href="mod/mod_auth.html#authuserfile">AuthUserFile</A>, +<a href="mod/mod_auth.html#authgroupfile">AuthGroupFile</A>, +<a href="mod/mod_digest.html#authdigestfile">AuthDigestFile</A>, +<a href="mod/core.html#keepalive">KeepAlive</A> and +<a href="mod/core.html#keepalivetimeout">KeepAliveTimeout</A>. + +<li><b><a href="mod/mod_proxy.html">Optional proxy module</a></b><br> +An improved FTP, HTTP, and CONNECT mode SSL proxy is included with +Apache 1.2. Some of the changes visible to users: + <dl><dl> + <dt>- Improved FTP proxy supporting PASV mode + <dt>- ProxyBlock directive for excluding sites to proxy + <dt>- NoCache * directive for disabling proxy caching + <dt>- Numerous bug fixes + </dl></dl> + +</ul> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> + diff --git a/usr.sbin/httpd/htdocs/manual/process-model.html b/usr.sbin/httpd/htdocs/manual/process-model.html new file mode 100644 index 00000000000..1671f5569f8 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/process-model.html @@ -0,0 +1,80 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML><HEAD> +<TITLE>Server Pool Management</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<H1 ALIGN="CENTER">Server Pool Management</H1> + +<HR> +<P> +We found that many people were using values for "MaxServers" either +too high or too low, and were hanging themselves on it. The model we +adopted is still based on long-lived minimal-forking processes, but +instead of specifying one number of persistent processes, the +web-master specifies a maximum and minimum number of processes to be +"spare" - every couple of seconds the parent checks the actual number +of spare servers and adjusts accordingly. This should keep the number +of servers concurrently running relatively low while still ensuring +minimal forking. + +<P> + +We renamed the current StartServers to MinSpareServers, created +separate StartServers parameter which means what it says, and renamed +MaxServers to MaxSpareServers (though the old name still works, for +NCSA 1.4 back-compatibility). The old names were generally regarded +as too confusing. + +<P> + +The defaults for each variable are: + +<PRE> +MinSpareServers 5 +MaxSpareServers 10 +StartServers 5 +</PRE> + +There is an absolute maximum number of simultaneous children defined +by a compile-time limit which defaults to 256 and a "MaxClients" +directive which specifies the number of simultaneous children that +will be allowed. MaxClients can be adjusted up to the compile-time +limit (HARD_SERVER_LIMIT, defined in httpd.h). If you need more +than 256 simultaneous children, you need to modify both HARD_SERVER_LIMIT +and MaxClients.<P> + +In versions before 1.2, HARD_SERVER_LIMIT defaulted to 150.<P> + +We do not recommend changing either of these values unless: + +<OL> +<LI>You know you have the server resources to handle more +<LI>You use the machine for other purposes and must limit the amount of memory +Apache uses +</OL> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</body></html> + + diff --git a/usr.sbin/httpd/htdocs/manual/stopping.html b/usr.sbin/httpd/htdocs/manual/stopping.html new file mode 100644 index 00000000000..1db5e588609 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/stopping.html @@ -0,0 +1,178 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Stopping and Restarting Apache</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Stopping and Restarting Apache</h1> + +<p>You will notice many <code>httpd</code> executables running on your system, +but you should not send signals to any of them except the parent, whose +pid is in the <a href="mod/core.html#pidfile">PidFile</a>. That is to +say you shouldn't ever need to send signals to any process except the +parent. There are three signals that you can send the parent: +<code>TERM</code>, <code>HUP</code>, and <code>USR1</code>, which will +be described in a moment. + +<p>To send a signal to the parent you should issue a command such as: +<blockquote><pre> + kill -TERM `cat /usr/local/etc/httpd/logs/httpd.pid` +</pre></blockquote> + +You can read about its progress by issuing: + +<blockquote><pre> + tail -f /usr/local/etc/httpd/logs/error_log +</pre></blockquote> + +Modify those examples to match your +<a href="mod/core.html#serverroot">ServerRoot</a> and +<a href="mod/core.html#pidfile">PidFile</a> settings. + +<h3>TERM Signal: stop now</h3> + +<p>Sending the <code>TERM</code> signal to the parent causes it to +immediately attempt to kill off all of its children. It may take it +several seconds to complete killing off its children. Then the +parent itself exits. Any requests in progress are terminated, and no +further requests are served. + +<h3>HUP Signal: restart now</h3> + +<p>Sending the <code>HUP</code> signal to the parent causes it to kill off +its children like in <code>TERM</code> but the parent doesn't exit. It +re-reads its configuration files, and re-opens any log files. +Then it spawns a new set of children and continues +serving hits. + +<p>Users of the +<a href="mod/mod_status.html">status module</a> +will notice that the server statistics are +set to zero when a <code>HUP</code> is sent. + +<p><b>Note:</b> If your configuration file has errors in it when you issue a +restart then your parent will not restart, it will exit with an error. +See below for a method of avoiding this. + +<h3>USR1 Signal: graceful restart</h3> + +<p><b>Note:</b> prior to release 1.2b9 this code is quite unstable and +shouldn't be used at all. + +<p>The <code>USR1</code> signal causes the parent process to <i>advise</i> +the children to exit after their current request (or to exit immediately +if they're not serving anything). The parent re-reads its configuration +files and re-opens its log files. As each child dies off the parent +replaces it with a child from the new <i>generation</i> of the +configuration, which begins serving new requests immediately. + +<p>This code is designed to always respect the +<a href="mod/core.html#maxclients">MaxClients</a>, +<a href="mod/core.html#minspareservers">MinSpareServers</a>, +and <a href="mod/core.html#maxspareservers">MaxSpareServers</a> settings. +Furthermore, it respects <a href="mod/core.html#startservers">StartServers</a> +in the following manner: if after one second at least StartServers new +children have not been created, then create enough to pick up the slack. +This is to say that the code tries to maintain both the number of children +appropriate for the current load on the server, and respect your wishes +with the StartServers parameter. + +<p>Users of the +<a href="mod/mod_status.html">status module</a> +will notice that the server statistics +are <b>not</b> set to zero when a <code>USR1</code> is sent. The code +was written to both minimize the time in which the server is unable to serve +new requests (they will be queued up by the operating system, so they're +not lost in any event) and to respect your tuning parameters. In order +to do this it has to keep the <i>scoreboard</i> used to keep track +of all children across generations. + +<p>The status module will also use a <code>G</code> to indicate those +children which are still serving requests started before the graceful +restart was given. + +<p>At present there is no way for a log rotation script using +<code>USR1</code> to know for certain that all children writing the +pre-restart log have finished. We suggest that you use a suitable delay +after sending the <code>USR1</code> signal before you do anything with the +old log. For example if most of your hits take less than 10 minutes to +complete for users on low bandwidth links then you could wait 15 minutes +before doing anything with the old log. + +<p><b>Note:</b> If your configuration file has errors in it when you issue a +restart then your parent will not restart, it will exit with an error. +In the case of graceful +restarts it will also leave children running when it exits. (These are +the children which are "gracefully exiting" by handling their last request.) +This will cause problems if you attempt to restart the server -- it will +not be able to bind to its listening ports. At present the only work +around is to check the syntax of your files before doing a restart. The +easiest way is to just run httpd as a non-root user. If there are no +errors it will attempt to open its sockets and logs and fail because it's +not root (or because the currently running httpd already has those ports +bound). If it fails for any other reason then it's probably a config file +error and the error should be fixed before issuing the graceful restart. + +<h3>Appendix: signals and race conditions</h3> + +<p>Prior to Apache 1.2b9 there were several <i>race conditions</i> +involving the restart and die signals (a simple description of race +condition is: a time-sensitive problem, as in if something happens at just +the wrong time it won't behave as expected). For those architectures that +have the "right" feature set we have eliminated as many as we can. +But it should be noted that there still do exist race conditions on +certain architectures. + +<p>Architectures that use an on disk +<a href="mod/core.html#scoreboardfile">ScoreBoardFile</a> +have the potential to corrupt their scoreboards. This can result in +the "bind: Address already in use" (after <code>HUP</code>) or +"long lost child came home!" (after <code>USR1</code>). The former is +a fatal error, while the latter just causes the server to lose a scoreboard +slot. So it might be advisable to use graceful restarts, with +an occasional hard restart. These problems are very difficult to work +around, but fortunately most architectures do not require a scoreboard file. +See the ScoreBoardFile documentation for a method to determine if your +architecture uses it. + +<p><code>NEXT</code> and <code>MACHTEN</code> (68k only) have small race +conditions +which can cause a restart/die signal to be lost, but should not cause the +server to do anything otherwise problematic. +<!-- they don't have sigaction, or we're not using it -djg --> + +<p>All architectures have a small race condition in each child involving +the second and subsequent requests on a persistent HTTP connection +(KeepAlive). It may exit after reading the request line but before +reading any of the request headers. There is a fix that was discovered +too late to make 1.2. In theory this isn't an issue because the KeepAlive +client has to expect these events because of network latencies and +server timeouts. In practice it doesn't seem to affect anything either +-- in a test case the server was restarted twenty times per second and +clients successfully browsed the site without getting broken images or +empty documents. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/suexec.html b/usr.sbin/httpd/htdocs/manual/suexec.html new file mode 100644 index 00000000000..fb77589b83b --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/suexec.html @@ -0,0 +1,518 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache suEXEC Support</TITLE> +</HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Apache suEXEC Support</H1> + +<P ALIGN="LEFT"> +<OL> + <LH><BIG><STRONG>CONTENTS</STRONG></BIG></LH> + <LI><A HREF="#what">What is suEXEC?</A></LI> + <LI><A HREF="#before">Before we begin.</A></LI> + <LI><A HREF="#model">suEXEC Security Model.</A></LI> + <LI><A HREF="#install">Configuring & Installing suEXEC</A></LI> + <LI><A HREF="#enable">Enabling & Disabling suEXEC</A></LI> + <LI><A HREF="#debug">Debugging suEXEC</A></LI> + <LI><A HREF="#jabberwock">Beware the Jabberwock: Warnings & + Examples</A></LI> +</OL> +</P> + +<H3><A NAME="what">What is suEXEC?</A></H3> +<P ALIGN="LEFT"> +The <STRONG>suEXEC</STRONG> feature -- introduced in Apache 1.2 -- provides +Apache users the ability to run <STRONG>CGI</STRONG> and <STRONG>SSI</STRONG> +programs under user IDs different from the user ID of the calling web-server. +Normally, when a CGI or SSI program executes, it runs as the same user who is +running the web server. +</P> + +<P ALIGN="LEFT"> +Used properly, this feature can reduce considerably the security risks involved +with allowing users to develop and run private CGI or SSI programs. However, +if suEXEC is improperly configured, it can cause any number of problems and +possibly create new holes in your computer's security. If you aren't familiar +with managing setuid root programs and the security issues they present, we +highly recommend that you not consider using suEXEC. +</P> + +<P ALIGN="CENTER"> +<STRONG><A HREF="suexec.html">BACK TO CONTENTS</A></STRONG> +</P> + +<H3><A NAME="before">Before we begin.</A></H3> +<P ALIGN="LEFT"> +Before jumping head-first into this document, you should be aware of the +assumptions made on the part of the Apache Group and this document. +</P> + +<P ALIGN="LEFT"> +First, it is assumed that you are using a UNIX derivate operating system that +is capable of <STRONG>setuid</STRONG> and <STRONG>setgid</STRONG> operations. +All command examples are given in this regard. Other platforms, if they are +capable of supporting suEXEC, may differ in their configuration. +</P> + +<P ALIGN="LEFT"> +Second, it is assumed you are familiar with some basic concepts of your +computer's security and its administration. This involves an understanding +of <STRONG>setuid/setgid</STRONG> operations and the various effects they +may have on your system and its level of security. +</P> + +<P ALIGN="LEFT"> +Third, it is assumed that you are using an <STRONG>unmodified</STRONG> +version of suEXEC code. All code for suEXEC has been carefully scrutinized and +tested by the developers as well as numerous beta testers. Every precaution has +been taken to ensure a simple yet solidly safe base of code. Altering this +code can cause unexpected problems and new security risks. It is +<STRONG>highly</STRONG> recommended you not alter the suEXEC code unless you +are well versed in the particulars of security programming and are willing to +share your work with the Apache Group for consideration. +</P> + +<P ALIGN="LEFT"> +Fourth, and last, it has been the decision of the Apache Group to +<STRONG>NOT</STRONG> make suEXEC part of the default installation of Apache. +To this end, suEXEC configuration is a manual process requiring of the +administrator careful attention to details. It is through this process +that the Apache Group hopes to limit suEXEC installation only to those +who are determined to use it. +</P> + +<P ALIGN="LEFT"> +Still with us? Yes? Good. Let's move on! +</P> + +<P ALIGN="CENTER"> +<STRONG><A HREF="suexec.html">BACK TO CONTENTS</A></STRONG> +</P> + +<H3><A NAME="model">suEXEC Security Model</A></H3> +<P ALIGN="LEFT"> +Before we begin configuring and installing suEXEC, we will first discuss +the security model you are about to implement. By doing so, you may +better understand what exactly is going on inside suEXEC and what precautions +are taken to ensure your system's security. +</P> + +<P ALIGN="LEFT"> +<STRONG>suEXEC</STRONG> is based on a setuid "wrapper" program that is +called by the main Apache web server. This wrapper is called when an HTTP +request is made for a CGI or SSI program that the administrator has designated +to run as a userid other than that of the main server. When such a request +is made, Apache provides the suEXEC wrapper with the program's name and the +user and group IDs under which the program is to execute. +</P> + +<P ALIGN="LEFT"> +The wrapper then employs the following process to determine success or +failure -- if any one of these conditions fail, the program logs the failure +and exits with an error, otherwise it will continue: + <OL> + <LI><STRONG>Was the wrapper called with the proper number of arguments?</STRONG> + <BLOCKQUOTE> + The wrapper will only execute if it is given the proper number of arguments. + The proper argument format is known to the Apache web server. If the wrapper + is not receiving the proper number of arguments, it is either being hacked, or + there is something wrong with the suEXEC portion of your Apache binary. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the user executing this wrapper a valid user of this system?</STRONG> + <BLOCKQUOTE> + This is to ensure that the user executing the wrapper is truly a user of the system. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is this valid user allowed to run the wrapper?</STRONG> + <BLOCKQUOTE> + Is this user the user allowed to run this wrapper? Only one user (the Apache + user) is allowed to execute this program. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Does the target program have an unsafe hierarchical reference?</STRONG> + <BLOCKQUOTE> + Does the target program contain a leading '/' or have a '..' backreference? These + are not allowed; the target program must reside within the Apache webspace. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target user name valid?</STRONG> + <BLOCKQUOTE> + Does the target user exist? + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target group name valid?</STRONG> + <BLOCKQUOTE> + Does the target group exist? + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target user <EM>NOT</EM> superuser?</STRONG> + <BLOCKQUOTE> + Presently, suEXEC does not allow 'root' to execute CGI/SSI programs. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target userid <EM>ABOVE</EM> the minimum ID number?</STRONG> + <BLOCKQUOTE> + The minimum user ID number is specified during configuration. This allows you + to set the lowest possible userid that will be allowed to execute CGI/SSI programs. + This is useful to block out "system" accounts. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target group <EM>NOT</EM> the superuser group?</STRONG> + <BLOCKQUOTE> + Presently, suEXEC does not allow the 'root' group to execute CGI/SSI programs. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target groupid <EM>ABOVE</EM> the minimum ID number?</STRONG> + <BLOCKQUOTE> + The minimum group ID number is specified during configuration. This allows you + to set the lowest possible groupid that will be allowed to execute CGI/SSI programs. + This is useful to block out "system" groups. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Can the wrapper successfully become the target user and group?</STRONG> + <BLOCKQUOTE> + Here is where the program becomes the target user and group via setuid and setgid + calls. The group access list is also initialized with all of the groups of which + the user is a member. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Does the directory in which the program resides exist?</STRONG> + <BLOCKQUOTE> + If it doesn't exist, it can't very well contain files. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the directory within the Apache webspace?</STRONG> + <BLOCKQUOTE> + If the request is for a regular portion of the server, is the requested directory + within the server's document root? If the request is for a UserDir, is the requested + directory within the user's document root? + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the directory <EM>NOT</EM> writable by anyone else?</STRONG> + <BLOCKQUOTE> + We don't want to open up the directory to others; only the owner user may be able + to alter this directories contents. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Does the target program exist?</STRONG> + <BLOCKQUOTE> + If it doesn't exists, it can't very well be executed. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target program <EM>NOT</EM> writable by anyone else?</STRONG> + <BLOCKQUOTE> + We don't want to give anyone other than the owner the ability to change the program. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target program <EM>NOT</EM> setuid or setgid?</STRONG> + <BLOCKQUOTE> + We do not want to execute programs that will then change our UID/GID again. + </BLOCKQUOTE> + </LI> + <LI><STRONG>Is the target user/group the same as the program's user/group?</STRONG> + <BLOCKQUOTE> + Is the user the owner of the file? + </BLOCKQUOTE> + </LI> + <LI><STRONG>Can we successfully clean the process environment to ensure safe operations?</STRONG> + <BLOCKQUOTE> + suEXEC cleans the process' environment by establishing a safe execution PATH (defined + during configuration), as well as only passing through those variables whose names + are listed in the safe environment list (also created during configuration). + </BLOCKQUOTE> + </LI> + <LI><STRONG>Can we successfully become the target program and execute?</STRONG> + <BLOCKQUOTE> + Here is where suEXEC ends and the target program begins. + </BLOCKQUOTE> + </LI> + </OL> +</P> + +<P ALIGN="LEFT"> +This is the standard operation of the the suEXEC wrapper's security model. +It is somewhat stringent and can impose new limitations and guidelines for +CGI/SSI design, but it was developed carefully step-by-step with security +in mind. +</P> + +<P ALIGN="LEFT"> +For more information as to how this security model can limit your possibilities +in regards to server configuration, as well as what security risks can be avoided +with a proper suEXEC setup, see the <A HREF="#beware">"Beware the Jabberwock"</A> +section of this document. +</P> + +<P ALIGN="CENTER"> +<STRONG><A HREF="suexec.html">BACK TO CONTENTS</A></STRONG> +</P> + +<H3><A NAME="install">Configuring & Installing suEXEC</A></H3> +<P ALIGN="LEFT"> +Here's where we begin the fun. The configuration and installation of suEXEC is +a four step process: edit the suEXEC header file, compile suEXEC, place the +suEXEC binary in its proper location, and configure Apache for use with suEXEC. +</P> + +<P ALIGN="LEFT"> +<STRONG>EDITING THE SUEXEC HEADER FILE</STRONG><BR> +- From the top-level of the Apache source tree, type: +<STRONG><code>cd support [ENTER]</code></STRONG> +</P> + +<P ALIGN="LEFT"> +Edit the <code>suexec.h</code> file and change the following macros to +match your local Apache installation. +</P> + +<P ALIGN="LEFT"> +<EM>From support/suexec.h</EM> +<PRE> + /* + * HTTPD_USER -- Define as the username under which Apache normally + * runs. This is the only user allowed to execute + * this program. + */ + #define HTTPD_USER "www" + + /* + * UID_MIN -- Define this as the lowest UID allowed to be a target user + * for suEXEC. For most systems, 500 or 100 is common. + */ + #define UID_MIN 100 + + /* + * GID_MIN -- Define this as the lowest GID allowed to be a target group + * for suEXEC. For most systems, 100 is common. + */ + #define GID_MIN 100 + + /* + * 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. + */ + #define USERDIR_SUFFIX "public_html" + + /* + * LOG_EXEC -- Define this as a filename if you want all suEXEC + * transactions and errors logged for auditing and + * debugging purposes. + */ + #define LOG_EXEC "/usr/local/etc/httpd/logs/cgi.log" /* Need me? */ + + /* + * 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. + */ + #define DOC_ROOT "/usr/local/etc/httpd/htdocs" + + /* + * SAFE_PATH -- Define a safe PATH environment to pass to CGI executables. + * + */ + #define SAFE_PATH "/usr/local/bin:/usr/bin:/bin" +</PRE> +</P> + +<P ALIGN="LEFT"> +<STRONG>COMPILING THE SUEXEC WRAPPER</STRONG><BR> +You now need to compile the suEXEC wrapper. At the shell command prompt, +type: <STRONG><CODE>cc suexec.c -o suexec [ENTER]</CODE></STRONG>. +This should create the <STRONG><em>suexec</em></STRONG> wrapper executable. +</P> + +<P ALIGN="LEFT"> +<STRONG>COMPILING APACHE FOR USE WITH SUEXEC</STRONG><BR> +By default, Apache is compiled to look for the suEXEC wrapper in the following +location. +</P> + +<P ALIGN="LEFT"> +<EM>From src/httpd.h</EM> +<PRE> + /* The path to the suEXEC wrapper */ + #define SUEXEC_BIN "/usr/local/etc/httpd/sbin/suexec" +</PRE> +</P> + +<P ALIGN="LEFT"> +If your installation requires location of the wrapper program in a different +directory, edit src/httpd.h and recompile your Apache server. +See <A HREF="install.html">Compiling and Installing Apache</A> for more +info on this process. +</P> + +<P ALIGN="LEFT"> +<STRONG>COPYING THE SUEXEC BINARY TO ITS PROPER LOCATION</STRONG><BR> +Copy the <STRONG><em>suexec</em></STRONG> executable created in the +exercise above to the defined location for <STRONG>SUEXEC_BIN</STRONG>. +</P> + +<P ALIGN="LEFT"> +<STRONG><CODE>cp suexec /usr/local/etc/httpd/sbin/suexec [ENTER]</CODE></STRONG> +</P> + +<P ALIGN="LEFT"> +In order for the wrapper to set the user ID, it must me installed as owner +<STRONG><em>root</em></STRONG> and must have the setuserid execution bit +set for file modes. If you are not running a <STRONG><em>root</em></STRONG> +user shell, do so now and execute the following commands. +</P> + +<P ALIGN="LEFT"> +<STRONG><CODE>chown root /usr/local/etc/httpd/sbin/suexec [ENTER]</CODE></STRONG><BR> +<STRONG><CODE>chmod 4711 /usr/local/etc/httpd/sbin/suexec [ENTER]</CODE></STRONG> +</P> + +<P ALIGN="CENTER"> +<STRONG><A HREF="suexec.html">BACK TO CONTENTS</A></STRONG> +</P> + +<H3><A NAME="enable">Enabling & Disabling suEXEC</A></H3> +<P ALIGN="LEFT"> +After properly installing the <STRONG>suexec</STRONG> wrapper +executable, you must kill and restart the Apache server. A simple +<STRONG><CODE>kill -1 `cat httpd.pid`</CODE></STRONG> will not be enough. +Upon startup of the web-server, if Apache finds a properly configured +<STRONG>suexec</STRONG> wrapper, it will print the following message to +the console: +</P> + +<P ALIGN="LEFT"> +<CODE>Configuring Apache for use with suexec wrapper.</CODE> +</P> + +<P ALIGN="LEFT"> +If you don't see this message at server startup, the server is most +likely not finding the wrapper program where it expects it, or the +executable is not installed <STRONG><EM>setuid root</EM></STRONG>. Check +your installation and try again. +</P> + +<P ALIGN="LEFT"> +One way to use <STRONG>suEXEC</STRONG> is through the +<a href="mod/core.html#user"><STRONG>User</STRONG></a> and +<a href="mod/core.html#group"><STRONG>Group</STRONG></a> directives in +<a href="mod/core.html#virtualhost"><STRONG>VirtualHost</STRONG></a> +definitions. By setting these directives to values different from the +main server user ID, all requests for CGI resources will be executed as +the <STRONG>User</STRONG> and <STRONG>Group</STRONG> defined for that +<STRONG><VirtualHost></STRONG>. If only one or +neither of these directives are specified for a +<STRONG><VirtualHost></STRONG> then the main +server userid is assumed.<p> + +<STRONG>suEXEC</STRONG> can also be used to to execute CGI programs as +the user to which the request is being directed. This is accomplished by +using the <STRONG>~</STRONG> character prefixing the user ID for whom +execution is desired. +The only requirement needed for this feature to work is for CGI +execution to be enabled for the user and that the script must meet the +scrutiny of the <a href="#model">security checks</a> above. + +<P ALIGN="CENTER"> +<STRONG><A HREF="suexec.html">BACK TO CONTENTS</A></STRONG> +</P> + +<H3><A NAME="debug">Debugging suEXEC</A></H3> +<P ALIGN="LEFT"> +The suEXEC wrapper will write log information to the location defined in +the <code>suexec.h</code> as indicated above. If you feel you have +configured and installed the wrapper properly, have a look at this log +and the error_log for the server to see where you may have gone astray. +</P> + +<P ALIGN="CENTER"> +<STRONG><A HREF="suexec.html">BACK TO CONTENTS</A></STRONG> +</P> + +<H3><A NAME="jabberwock">Beware the Jabberwock: Warnings & Examples</A></H3> +<P ALIGN="LEFT"> +<STRONG>NOTE!</STRONG> This section may not be complete. For the latest +revision of this section of the documentation, see the Apache Group's +<A HREF="http://www.apache.org/docs/suexec.html">Online Documentation</A> +version. +</P> + +<P ALIGN="LEFT"> +There are a few points of interest regarding the wrapper that can cause +limitations on server setup. Please review these before submitting any +"bugs" regarding suEXEC. +<UL> + <LH><STRONG>suEXEC Points Of Interest</STRONG></LH> + <LI>Hierarchy limitations + <BLOCKQUOTE> + For security and efficiency reasons, all suexec requests must + remain within either a top-level document root for virtual + host requests, or one top-level personal document root for + userdir requests. For example, if you have four VirtualHosts + configured, you would need to structure all of your VHosts' + document roots off of one main Apache document hierarchy to + take advantage of suEXEC for VirtualHosts. (Example forthcoming.) + </BLOCKQUOTE> + </LI> + <LI>suEXEC's PATH environment variable + <BLOCKQUOTE> + This can be a dangerous thing to change. Make certain every + path you include in this define is a <STRONG>trusted</STRONG> + directory. You don't want to open people up to having someone + from across the world running a trojan horse on them. + </BLOCKQUOTE> + </LI> + <LI>Altering the suEXEC code + <BLOCKQUOTE> + Again, this can cause <STRONG>Big Trouble</STRONG> if you try + this without knowing what you are doing. Stay away from it + if at all possible. + </BLOCKQUOTE> + </LI> +</UL> + +<P ALIGN="CENTER"> +<STRONG><A HREF="suexec.html">BACK TO CONTENTS</A></STRONG> +</P> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/unixware.html b/usr.sbin/httpd/htdocs/manual/unixware.html new file mode 100644 index 00000000000..4d8e1c53b99 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/unixware.html @@ -0,0 +1,73 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Compiling Apache under UnixWare</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + + +<H1 ALIGN="CENTER">Compiling Apache under UnixWare</H1> + +To compile a working copy of Apache under UnixWare, there are several other +steps you may need to take. These prevent such problems as zombie processes, +bind errors, and accept errors, to name a few. + +<H2>UnixWare 1.x</H2> + +Make sure that USE_FCNTL_SERIALIZE_ACCEPT is defined (if not +defined by Apache autoconfiguration). If using the UnixWare <i>cc</i> +compiler, and you still see accept() errors, don't use compiler optimization, +or get <i>gcc</i>. + +<H2>UnixWare 2.0.x</H2> + +SCO patch <a href="ftp://ftp.sco.com/UW20/tf2163.txt">tf2163</a> is required +in order for Apache to work correctly on UnixWare 2.0.x. See +<a href="http://www.sco.com">http://www.sco.com</a> +for UnixWare patch information.<p> + +In addition, make sure that USE_FCNTL_SERIALIZE_ACCEPT is defined (if not +defined by Apache autoconfiguration). To reduce instances of connections +in FIN_WAIT_2 state, you may also want to define NO_LINGCLOSE (Apache 1.2 +only). + +<H2>UnixWare 2.1.x</H2> + +SCO patch <a href="ftp://ftp.sco.com/UW21/ptf3123b.txt">ptf3123</a> is required +in order for Apache to work correctly on UnixWare 2.1.x. See +<a href="http://www.sco.com">http://www.sco.com</a> +for UnixWare patch information.<p> + +<b>NOTE:</b> Unixware 2.1.2 and later already have patch ptf3123 included<p> + +In addition, make sure that USE_FCNTL_SERIALIZE_ACCEPT is defined (if not +defined by Apache autoconfiguration). To reduce instances of connections +in FIN_WAIT_2 state, you may also want to define NO_LINGCLOSE (Apache 1.2 +only).<p> + +Thanks to Joe Doupnik <JRD@cc.usu.edu> and Rich Vaughn +<rvaughn@aad.com> for additional info for UnixWare builds.<p> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/vhosts-in-depth.html b/usr.sbin/httpd/htdocs/manual/vhosts-in-depth.html new file mode 100644 index 00000000000..0f87f775ed6 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/vhosts-in-depth.html @@ -0,0 +1,398 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html><head> +<title>An In-Depth Discussion of VirtualHost Matching</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">An In-Depth Discussion of VirtualHost Matching</h1> + +<p>This is a very rough document that was probably out of date the moment +it was written. It attempts to explain exactly what the code does when +deciding what virtual host to serve a hit from. It's provided on the +assumption that something is better than nothing. The server version +under discussion is Apache 1.2. + +<p>If you just want to "make it work" without understanding +how, there's a <a href="#whatworks">What Works</a> section at the bottom. + +<h3>Config File Parsing</h3> + +<p>There is a main_server which consists of all the definitions appearing +outside of <CODE>VirtualHost</CODE> sections. There are virtual servers, +called <EM>vhosts</EM>, which are defined by +<A + HREF="mod/core.html#virtualhost" +><SAMP>VirtualHost</SAMP></A> +sections. + +<p>The directives +<A + HREF="mod/core.html#port" +><SAMP>Port</SAMP></A>, +<A + HREF="mod/core.html#servername" +><SAMP>ServerName</SAMP></A>, +<A + HREF="mod/core.html#serverpath" +><SAMP>ServerPath</SAMP></A>, +and +<A + HREF="mod/core.html#serveralias" +><SAMP>ServerAlias</SAMP></A> +can appear anywhere within the definition of +a server. However, each appearance overrides the previous appearance +(within that server). + +<p>The default value of the <code>Port</code> field for main_server +is 80. The main_server has no default <code>ServerName</code>, +<code>ServerPath</code>, or <code>ServerAlias</code>. + +<p>In the absence of any +<A + HREF="mod/core.html#listen" +><SAMP>Listen</SAMP></A> +directives, the (final if there +are multiple) <code>Port</code> directive in the main_server indicates +which port httpd will listen on. + +<p> The <code>Port</code> and <code>ServerName</code> directives for +any server main or virtual are used when generating URLs such as during +redirects. + +<p> Each address appearing in the <code>VirtualHost</code> directive +can have an optional port. If the port is unspecified it defaults to +the value of the main_server's most recent <code>Port</code> statement. +The special port <SAMP>*</SAMP> indicates a wildcard that matches any port. +Collectively the entire set of addresses (including multiple +<SAMP>A</SAMP> record +results from DNS lookups) are called the vhost's <EM>address set</EM>. + +<p> The magic <code>_default_</code> address has significance during +the matching algorithm. It essentially matches any unspecified address. + +<p> After parsing the <code>VirtualHost</code> directive, the vhost server +is given a default <code>Port</code> equal to the port assigned to the +first name in its <code>VirtualHost</code> directive. The complete +list of names in the <code>VirtualHost</code> directive are treated +just like a <code>ServerAlias</code> (but are not overridden by any +<code>ServerAlias</code> statement). Note that subsequent <code>Port</code> +statements for this vhost will not affect the ports assigned in the +address set. + +<p> +All vhosts are stored in a list which is in the reverse order that +they appeared in the config file. For example, if the config file is: + +<blockquote><pre> + <VirtualHost A> + ... + </VirtualHost> + + <VirtualHost B> + ... + </VirtualHost> + + <VirtualHost C> + ... + </VirtualHost> +</pre></blockquote> + +Then the list will be ordered: main_server, C, B, A. Keep this in mind. + +<p> +After parsing has completed, the list of servers is scanned, and various +merges and default values are set. In particular: + +<ol> +<li>If a vhost has no + <A + HREF="mod/core.html#serveradmin" + ><code>ServerAdmin</code></A>, + <A + HREF="mod/core.html#resourceconfig" + ><code>ResourceConfig</code></A>, + <A + HREF="mod/core.html#accessconfig" + ><code>AccessConfig</code></A>, + <A + HREF="mod/core.html#timeout" + ><code>Timeout</code></A>, + <A + HREF="mod/core.html#keepalivetimeout" + ><code>KeepAliveTimeout</code></A>, + <A + HREF="mod/core.html#keepalive" + ><code>KeepAlive</code></A>, + <A + HREF="mod/core.html#maxkeepaliverequests" + ><code>MaxKeepAliveRequests</code></A>, + or + <A + HREF="mod/core.html#sendbuffersize" + ><code>SendBufferSize</code></A> + directive then the respective value is + inherited from the main_server. (That is, inherited from whatever + the final setting of that value is in the main_server.) + +<li>The "lookup defaults" that define the default directory + permissions + for a vhost are merged with those of the main server. This includes + any per-directory configuration information for any module. + +<li>The per-server configs for each module from the main_server are + merged into the vhost server. +</ol> + +Essentially, the main_server is treated as "defaults" or a +"base" on +which to build each vhost. But the positioning of these main_server +definitions in the config file is largely irrelevant -- the entire +config of the main_server has been parsed when this final merging occurs. +So even if a main_server definition appears after a vhost definition +it might affect the vhost definition. + +<p> If the main_server has no <code>ServerName</code> at this point, +then the hostname of the machine that httpd is running on is used +instead. We will call the <EM>main_server address set</EM> those IP +addresses returned by a DNS lookup on the <code>ServerName</code> of +the main_server. + +<p> Now a pass is made through the vhosts to fill in any missing +<code>ServerName</code> fields and to classify the vhost as either +an <EM>IP-based</EM> vhost or a <EM>name-based</EM> vhost. A vhost is +considered a name-based vhost if any of its address set overlaps the +main_server (the port associated with each address must match the +main_server's <code>Port</code>). Otherwise it is considered an IP-based +vhost. + +<p> For any undefined <code>ServerName</code> fields, a name-based vhost +defaults to the address given first in the <code>VirtualHost</code> +statement defining the vhost. Any vhost that includes the magic +<SAMP>_default_</SAMP> wildcard is given the same <code>ServerName</code> as +the main_server. Otherwise the vhost (which is necessarily an IP-based +vhost) is given a <code>ServerName</code> based on the result of a reverse +DNS lookup on the first address given in the <code>VirtualHost</code> +statement. + +<p> + +<h3>Vhost Matching</h3> + +<p> +The server determines which vhost to use for a request as follows: + +<p> <code>find_virtual_server</code>: When the connection is first made +by the client, the local IP address (the IP address to which the client +connected) is looked up in the server list. A vhost is matched if it +is an IP-based vhost, the IP address matches and the port matches +(taking into account wildcards). + +<p> If no vhosts are matched then the last occurrence, if it appears, +of a <SAMP>_default_</SAMP> address (which if you recall the ordering of the +server list mentioned above means that this would be the first occurrence +of <SAMP>_default_</SAMP> in the config file) is matched. + +<p> In any event, if nothing above has matched, then the main_server is +matched. + +<p> The vhost resulting from the above search is stored with data +about the connection. We'll call this the <EM>connection vhost</EM>. +The connection vhost is constant over all requests in a particular TCP/IP +session -- that is, over all requests in a KeepAlive/persistent session. + +<p> For each request made on the connection the following sequence of +events further determines the actual vhost that will be used to serve +the request. + +<p> <code>check_fulluri</code>: If the requestURI is an absoluteURI, that +is it includes <code>http://hostname/</code>, then an attempt is made to +determine if the hostname's address (and optional port) match that of +the connection vhost. If it does then the hostname portion of the URI +is saved as the <EM>request_hostname</EM>. If it does not match, then the +URI remains untouched. <STRONG>Note</STRONG>: to achieve this address +comparison, +the hostname supplied goes through a DNS lookup unless it matches the +<code>ServerName</code> or the local IP address of the client's socket. + +<p> <code>parse_uri</code>: If the URI begins with a protocol +(<EM>i.e.</EM>, <code>http:</code>, <code>ftp:</code>) then the request is +considered a proxy request. Note that even though we may have stripped +an <code>http://hostname/</code> in the previous step, this could still +be a proxy request. + +<p> <code>read_request</code>: If the request does not have a hostname +from the earlier step, then any <code>Host:</code> header sent by the +client is used as the request hostname. + +<p> <code>check_hostalias</code>: If the request now has a hostname, +then an attempt is made to match for this hostname. The first step +of this match is to compare any port, if one was given in the request, +against the <code>Port</code> field of the connection vhost. If there's +a mismatch then the vhost used for the request is the connection vhost. +(This is a bug, see observations.) + +<p> +If the port matches, then httpd scans the list of vhosts starting with +the next server <STRONG>after</STRONG> the connection vhost. This scan does not +stop if there are any matches, it goes through all possible vhosts, +and in the end uses the last match it found. The comparisons performed +are as follows: + +<ul> +<li>Compare the request hostname:port with the vhost + <code>ServerName</code> and <code>Port</code>. + +<li>Compare the request hostname against any and all addresses given in + the <code>VirtualHost</code> directive for this vhost. + +<li>Compare the request hostname against the <code>ServerAlias</code> + given for the vhost. +</ul> + +<p> +<code>check_serverpath</code>: If the request has no hostname +(back up a few paragraphs) then a scan similar to the one +in <code>check_hostalias</code> is performed to match any +<code>ServerPath</code> directives given in the vhosts. Note that the +<STRONG>last match</STRONG> is used regardless (again consider the ordering of +the virtual hosts). + +<h3>Observations</h3> + +<ul> + +<li>It is difficult to define an IP-based vhost for the machine's + "main IP address". You essentially have to create a bogus + <code>ServerName</code> for the main_server that does not match the + machine's IPs. + +<li>During the scans in both <code>check_hostalias</code> and + <code>check_serverpath</code> no check is made that the vhost being + scanned is actually a name-based vhost. This means, for example, that + it's possible to match an IP-based vhost through another address. But + because the scan starts in the vhost list at the first vhost that + matched the local IP address of the connection, not all IP-based vhosts + can be matched. + + <p>Consider the config file above with three vhosts A, B, C. Suppose + that B is a named-based vhost, and A and C are IP-based vhosts. If + a request comes in on B or C's address containing a header + "<SAMP>Host: A</SAMP>" then + it will be served from A's config. If a request comes in on A's + address then it will always be served from A's config regardless of + any Host: header. + </p> + +<li>Unless you have a <SAMP>_default_</SAMP> vhost, + it doesn't matter if you mix name-based vhosts in amongst IP-based + vhosts. During the <code>find_virtual_server</code> phase above no + named-based vhost will be matched, so the main_server will remain the + connection vhost. Then scans will cover all vhosts in the vhost list. + + <p>If you do have a <SAMP>_default_</SAMP> vhost, then you cannot place + named-based vhosts after it in the config. This is because on any + connection to the main server IPs the connection vhost will always be + the <SAMP>_default_</SAMP> vhost since none of the name-based are + considered during <code>find_virtual_server</code>. + </p> + +<li>You should never specify DNS names in <code>VirtualHost</code> + directives because it will force your server to rely on DNS to boot. + Furthermore it poses a security threat if you do not control the + DNS for all the domains listed. + <a href="dns-caveats.html"> + There's more information + available on this and the next two topics</a>.</p> + +<li><code>ServerName</code> should always be set for each vhost. Otherwise + A DNS lookup is required for each vhost.</p> + +<li>A DNS lookup is always required for the main_server's + <code>ServerName</code> (or to generate that if it isn't specified + in the config).</p> + +<li>If a <code>ServerPath</code> directive exists which is a prefix of + another <code>ServerPath</code> directive that appears later in + the configuration file, then the former will always be matched + and the latter will never be matched. (That is assuming that no + Host header was available to disambiguate the two.)</p> + +<li>If a vhost that would otherwise be a name-vhost includes a + <code>Port</code> statement that doesn't match the main_server + <code>Port</code> then it will be considered an IP-based vhost. + Then <code>find_virtual_server</code> will match it (because + the ports associated with each address in the address set default + to the port of the main_server) as the connection vhost. Then + <code>check_hostalias</code> will refuse to check any other name-based + vhost because of the port mismatch. The result is that the vhost + will steal all hits going to the main_server address.</p> + +<li>If two IP-based vhosts have an address in common, the vhost appearing + later in the file is always matched. Such a thing might happen + inadvertently. If the config has name-based vhosts and for some reason + the main_server <code>ServerName</code> resolves to the wrong address + then all the name-based vhosts will be parsed as ip-based vhosts. + Then the last of them will steal all the hits.</P> + +<li>The last name-based vhost in the config is always matched for any hit + which doesn't match one of the other name-based vhosts.</p> + +</ul> + +<h3><a name="whatworks">What Works</a></h3> + +<p>In addition to the tips on the <a href="dns-caveats.html#tips">DNS +Issues</a> page, here are some further tips: + +<ul> + +<li>Place all main_server definitions before any VirtualHost definitions. +(This is to aid the readability of the configuration -- the post-config +merging process makes it non-obvious that definitions mixed in around +virtualhosts might affect all virtualhosts.) +<p> + +<li>Arrange your VirtualHosts such +that all name-based virtual hosts come first, followed by IP-based +virtual hosts, followed by any <SAMP>_default_</SAMP> virtual host +<p> + +<li>Avoid <code>ServerPaths</code> which are prefixes of other +<code>ServerPaths</code>. If you cannot avoid this then you have to +ensure that the longer (more specific) prefix vhost appears earlier in +the configuration file than the shorter (less specific) prefix +(<EM>i.e.</EM>, "ServerPath /abc" should appear after +"ServerPath /abcdef"). +<p> + +<li>Do not use <i>port-based</i> vhosts in the same server as +name-based vhosts. A loose definition for port-based is a vhost which +is determined by the port on the server (<em>i.e.</em> one server with +ports 8000, 8080, and 80 all of which have different configurations). +<p> + +</ul> + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</BODY> +</HTML> diff --git a/usr.sbin/httpd/htdocs/manual/virtual-host.html b/usr.sbin/httpd/htdocs/manual/virtual-host.html new file mode 100644 index 00000000000..d21ac708fd8 --- /dev/null +++ b/usr.sbin/httpd/htdocs/manual/virtual-host.html @@ -0,0 +1,216 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Apache Server Virtual Host Support</title> +</head> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<DIV ALIGN="CENTER"> + <IMG SRC="images/sub.gif" ALT="[APACHE DOCUMENTATION]"> + <H3> + Apache HTTP Server Version 1.2 + </H3> +</DIV> + +<h1 ALIGN="CENTER">Virtual Host Support</h1> + +<strong>See Also:</strong> +<a href="host.html">Non-IP based virtual hosts</a> + +<h2>What are virtual hosts?</h2> +This is the ability of a single machine to be a web server for multiple +domains. For example, an Internet service provider might have a machine +called <code>www.serve.com</code> which provides Web space for several +organizations including, say, <em>smallco</em> and <em>baygroup</em>. +Ordinarily, these groups would be given parts of the Web tree on www.serve.com. +So smallco's home page would have the URL +<blockquote> +http://www.serve.com/smallco/ +</blockquote> +and baygroup's home page would have the URL +<blockquote> +http://www.serve.com/baygroup/ +</blockquote> +<p> +For esthetic reasons, however, both organizations would rather their home +pages appeared under their own names rather than that of the service +provider's; but they do not want to set up their own Internet links and +servers. +<p> +Virtual hosts are the solution to this problem. smallco and baygroup would +have their own Internet name registrations, <code>www.smallco.com</code> and +<code>www.baygroup.org</code> respectively. These hostnames would both +correspond to the service provider's machine (www.serve.com). Thus +smallco's home page would now have the URL +<blockquote> +http://www.smallco.com/ +</blockquote> +and baygroup's home page would would have the URL +<blockquote> +http://www.baygroup.org/ +</blockquote> + +<h2>System requirements</h2> +Due to limitations in the HTTP/1.0 protocol, the web server <strong>must have a +different IP address for each virtual host</strong>. This can be achieved +by the machine having several physical network connections, or by use +of a <a href="misc/vif-info.html">virtual interface</a> on some operating systems. + +<h2>How to set up Apache</h2> +There are two ways of configuring apache to support multiple hosts. +Either by running a separate httpd daemon for each hostname, or by running a +single daemon which supports all the virtual hosts. +<p> +Use multiple daemons when: +<ul> +<li>The different virtual hosts need very different httpd configurations, such + as different values for: <A HREF="mod/core.html#servertype">ServerType</A>, + <A HREF="mod/core.html#user">User</A>, + <A HREF="mod/core.html#group">Group</A>, + <A HREF="mod/mod_mime.html#typesconfig">TypesConfig</A> or + <A HREF="mod/core.html#serverroot">ServerRoot</A>. +<li>The machine does not process a very high request rate. +</ul> +Use a single daemon when: +<ul> +<li>Sharing of the httpd configuration between virtual hosts is acceptable. +<li>The machine services a large number of requests, and so the performance + loss in running separate daemons may be significant. +</ul> + +<h2>Setting up multiple daemons</h2> +Create a separate httpd installation for each virtual host. +For each installation, use the +<A HREF="mod/core.html#bindaddress">BindAddress</A> directive in the configuration +file to select which IP address (or virtual host) that daemon services. +e.g. +<blockquote><code>BindAddress www.smallco.com</code></blockquote> +This hostname can also be given as an IP address. + +<h2>Setting up a single daemon</h2> +For this case, a single httpd will service requests for all the virtual hosts. +The <A HREF="mod/core.html#virtualhost">VirtualHost</A> directive in the + configuration file is used to set the values of +<A HREF="mod/core.html#serveradmin">ServerAdmin</A>, +<A HREF="mod/core.html#servername">ServerName</A>, +<A HREF="mod/core.html#documentroot">DocumentRoot</A>, +<A HREF="mod/core.html#errorlog">ErrorLog</A> and +<A HREF="mod/mod_log_common.html#transferlog">TransferLog</A> configuration +directives to different values for each virtual host. +e.g. +<blockquote><code> +<VirtualHost www.smallco.com><br> +ServerAdmin webmaster@mail.smallco.com<br> +DocumentRoot /groups/smallco/www<br> +ServerName www.smallco.com<br> +ErrorLog /groups/smallco/logs/error_log<br> +TransferLog /groups/smallco/logs/access_log<br> +</VirtualHost><br> +<br> +<VirtualHost www.baygroup.org><br> +ServerAdmin webmaster@mail.baygroup.org<br> +DocumentRoot /groups/baygroup/www<br> +ServerName www.baygroup.org<br> +ErrorLog /groups/baygroup/logs/error_log<br> +TransferLog /groups/baygroup/logs/access_log<br> +</VirtualHost><br> +</code></blockquote> + +This VirtualHost hostnames can also be given as IP addresses. + +<P> + +Almost <strong>ANY</strong> configuration directive can be put +in the VirtualHost directive, with the exception of +<A HREF="mod/core.html#servertype">ServerType</A>, +<A HREF="mod/core.html#user">User</A>, +<A HREF="mod/core.html#group">Group</A>, +<A HREF="mod/core.html#startservers">StartServers</A>, +<A HREF="mod/core.html#maxspareservers">MaxSpareServers</A>, +<A HREF="mod/core.html#minspareservers">MinSpareServers</A>, +<A HREF="mod/core.html#maxrequestsperchild">MaxRequestsPerChild</A>, +<A HREF="mod/core.html#bindaddress">BindAddress</A>, +<A HREF="mod/core.html#pidfile">PidFile</A>, +<A HREF="mod/mod_mime.html#typesconfig">TypesConfig</A>, and +<A HREF="mod/core.html#serverroot">ServerRoot</A>. + +<P> + +<EM>SECURITY:</EM> When specifying where to write log files, be aware +of some security risks which are present if anyone other than the +user that starts Apache has write access to the directory where they +are written. See the <A HREF="misc/security_tips.html">security +tips</A> document for details. + +<P> + +<H2>File Handle/Resource Limits:</H2> +When using a large number of Virtual Hosts, Apache may run out of available +file descriptors if each Virtual Host specifies different log files. +The total number of file descriptors used by Apache is one for each distinct +error log file, one for every other log file directive, plus 10-20 for +internal use. Unix operating systems limit the number of file descriptors that +may be used by a process; the limit is typically 64, and may usually be +increased up to a large hard-limit. +<p> +Although Apache attempts to increase the limit as required, this +may not work if: +<ol> +<li>Your system does not provide the setrlimit() system call. +<li>The setrlimit(RLIMIT_NOFILE) call does not function on your system + (such as Solaris 2.3) +<li>The number of file descriptors required exceeds the hard limit. +<li>Your system imposes other limits on file descriptors, such as a limit +on stdio streams only using file descriptors below 256. (Solaris 2) +</ol> + +In the event of problems you can: +<ul> +<li>Reduce the number of log files; don't specify log files in the VirtualHost +sections, but only log to the main log files. +<li>If you system falls into 1 or 2 (above), then increase the file descriptor +limit before starting Apache, using a script like +<blockquote><code> +#!/bin/sh <br> +ulimit -S -n 100 <br> +exec httpd</code></blockquote> +</ul> + +The have been reports that Apache may start running out of resources allocated +for the root process. This will exhibit itself as errors in the error log like +"unable to fork". There are two ways you can bump this up: + +<OL> +<LI>Have a <code>csh</code> script wrapper around httpd which sets the +"rlimit" to some large number, like 512. +<LI>Edit http_main.c to add calls to setrlimit() from main(), along the lines of +<PRE> + struct rlimit rlp; + + rlp.rlim_cur = rlp.rlim_max = 512; + if (setrlimit(RLIMIT_NPROC, &rlp)) { + fprintf(stderr, "setrlimit(RLIMIT_NPROC) failed.\n"); + exit(1); + } +</PRE> +(thanks to "Aaron Gifford <agifford@InfoWest.COM>" for the patch) +</OL> + +The latter will probably manifest itself in a later version of Apache. + +<HR> +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 1.2 +</H3> + +<A HREF="./"><IMG SRC="images/index.gif" ALT="Index"></A> + +</body></html> + diff --git a/usr.sbin/httpd/htdocs/openbsdpower.gif b/usr.sbin/httpd/htdocs/openbsdpower.gif Binary files differnew file mode 100644 index 00000000000..9875138a68b --- /dev/null +++ b/usr.sbin/httpd/htdocs/openbsdpower.gif diff --git a/usr.sbin/httpd/icons/README b/usr.sbin/httpd/icons/README new file mode 100644 index 00000000000..a1fc5a5a9c7 --- /dev/null +++ b/usr.sbin/httpd/icons/README @@ -0,0 +1,161 @@ +Public Domain Icons + + These icons were originally made for Mosaic for X and have been + included in the NCSA httpd and Apache server distributions in the + past. They are in the public domain and may be freely included in any + application. The originals were done by Kevin Hughes (kevinh@eit.com). + + Many thanks to Andy Polyakov for tuning the icon colors and adding a + few new images. If you'd like to contribute additions or ideas to + this set, please let me know. + + The distribution site for these icons is at: + + http://www.eit.com/goodies/www.icons/ + + Kevin Hughes + September 11, 1995 + + +Suggested Uses + +The following are a few suggestions, to serve as a starting point for ideas. +Please feel free to tweak and rename the icons as you like. + + a.gif + This might be used to represent PostScript or text layout + languages. + + alert.black.gif, alert.red.gif + These can be used to highlight any important items, such as a + README file in a directory. + + back.gif, forward.gif + These can be used as links to go to previous and next areas. + + ball.gray.gif, ball.red.gif + These might be used as bullets. + + binary.gif + This can be used to represent binary files. + + binhex.gif + This can represent BinHex-encoded data. + + blank.gif + This can be used as a placeholder or a spacing element. + + bomb.gif + This can be used to repreesnt core files. + + box1.gif, box2.gif + These icons can be used to represent generic 3D applications and + related files. + + broken.gif + This can represent corrupted data. + + burst.gif + This can call attention to new and important items. + + c.gif + This might represent C source code. + + comp.blue.gif, comp.red.gif + These little computer icons can stand for telnet or FTP + sessions. + + compressed.gif + This may represent compressed data. + + continued.gif + This can be a link to a continued listing of a directory. + + down.gif, up.gif, left.gif, right.gif + These can be used to scroll up, down, left and right in a + listing or may be used to denote items in an outline. + + dvi.gif + This can represent DVI files. + + f.gif + This might represent FORTRAN or Forth source code. + + folder.gif, folder.open.gif, folder.sec.gif + The folder can represent directories. There is also a version + that can represent secure directories or directories that cannot + be viewed. + + generic.gif, generic.sec.gif, generic.red.gif + These can represent generic files, secure files, and important + files, respectively. + + hand.right.gif, hand.up.gif + These can point out important items (pun intended). + + image1.gif, image2.gif, image3.gif + These can represent image formats of various types. + + index.gif + This might represent a WAIS index or search facility. + + layout.gif + This might represent files and formats that contain graphics as + well as text layout, such as HTML and PDF files. + + link.gif + This might represent files that are symbolic links. + + movie.gif + This can represent various movie formats. + + p.gif + This may stand for Perl or Python source code. + + pie0.gif ... pie8.gif + These icons can be used in applications where a list of + documents is returned from a search. The little pie chart images + can denote how relevant the documents may be to your search + query. + + patch.gif + This may stand for patches and diff files. + + portal.gif + This might be a link to an online service or a 3D world. + + ps.gif, quill.gif + These may represent PostScript files. + + screw1.gif, screw2.gif + These may represent CAD or engineering data and formats. + + script.gif + This can represent any of various interpreted languages, such as + Perl, python, TCL, and shell scripts, as well as server + configuration files. + + sound1.gif, sound2.gif + These can represent sound files. + + sphere1.gif, sphere2.gif + These can represent 3D worlds or rendering applications and + formats. + + tex.gif + This can represent TeX files. + + text.gif + This can represent generic (plain) text files. + + transfer.gif + This can represent FTP transfers or uploads/downloads. + + unknown.gif + This may represent a file of an unknown type. + + uuencoded.gif + This can stand for uuencoded data. + + world1.gif, world2.gif + These can represent 3D worlds or other 3D formats. diff --git a/usr.sbin/httpd/icons/a.gif b/usr.sbin/httpd/icons/a.gif Binary files differnew file mode 100644 index 00000000000..bb23d971f4c --- /dev/null +++ b/usr.sbin/httpd/icons/a.gif diff --git a/usr.sbin/httpd/icons/alert.black.gif b/usr.sbin/httpd/icons/alert.black.gif Binary files differnew file mode 100644 index 00000000000..eaecd2172a0 --- /dev/null +++ b/usr.sbin/httpd/icons/alert.black.gif diff --git a/usr.sbin/httpd/icons/alert.red.gif b/usr.sbin/httpd/icons/alert.red.gif Binary files differnew file mode 100644 index 00000000000..a4238940433 --- /dev/null +++ b/usr.sbin/httpd/icons/alert.red.gif diff --git a/usr.sbin/httpd/icons/apache_pb.gif b/usr.sbin/httpd/icons/apache_pb.gif Binary files differnew file mode 100644 index 00000000000..3a1c139fc42 --- /dev/null +++ b/usr.sbin/httpd/icons/apache_pb.gif diff --git a/usr.sbin/httpd/icons/back.gif b/usr.sbin/httpd/icons/back.gif Binary files differnew file mode 100644 index 00000000000..a694ae1ec3f --- /dev/null +++ b/usr.sbin/httpd/icons/back.gif diff --git a/usr.sbin/httpd/icons/ball.gray.gif b/usr.sbin/httpd/icons/ball.gray.gif Binary files differnew file mode 100644 index 00000000000..eb84268c4cc --- /dev/null +++ b/usr.sbin/httpd/icons/ball.gray.gif diff --git a/usr.sbin/httpd/icons/ball.red.gif b/usr.sbin/httpd/icons/ball.red.gif Binary files differnew file mode 100644 index 00000000000..a8425cb574b --- /dev/null +++ b/usr.sbin/httpd/icons/ball.red.gif diff --git a/usr.sbin/httpd/icons/binary.gif b/usr.sbin/httpd/icons/binary.gif Binary files differnew file mode 100644 index 00000000000..9a15cbae04c --- /dev/null +++ b/usr.sbin/httpd/icons/binary.gif diff --git a/usr.sbin/httpd/icons/binhex.gif b/usr.sbin/httpd/icons/binhex.gif Binary files differnew file mode 100644 index 00000000000..62d0363108d --- /dev/null +++ b/usr.sbin/httpd/icons/binhex.gif diff --git a/usr.sbin/httpd/icons/blank.gif b/usr.sbin/httpd/icons/blank.gif Binary files differnew file mode 100644 index 00000000000..0ccf01e1983 --- /dev/null +++ b/usr.sbin/httpd/icons/blank.gif diff --git a/usr.sbin/httpd/icons/bomb.gif b/usr.sbin/httpd/icons/bomb.gif Binary files differnew file mode 100644 index 00000000000..270fdb1c064 --- /dev/null +++ b/usr.sbin/httpd/icons/bomb.gif diff --git a/usr.sbin/httpd/icons/box1.gif b/usr.sbin/httpd/icons/box1.gif Binary files differnew file mode 100644 index 00000000000..65dcd002eaf --- /dev/null +++ b/usr.sbin/httpd/icons/box1.gif diff --git a/usr.sbin/httpd/icons/box2.gif b/usr.sbin/httpd/icons/box2.gif Binary files differnew file mode 100644 index 00000000000..c43bc4faecf --- /dev/null +++ b/usr.sbin/httpd/icons/box2.gif diff --git a/usr.sbin/httpd/icons/broken.gif b/usr.sbin/httpd/icons/broken.gif Binary files differnew file mode 100644 index 00000000000..9f8cbe9f760 --- /dev/null +++ b/usr.sbin/httpd/icons/broken.gif diff --git a/usr.sbin/httpd/icons/burst.gif b/usr.sbin/httpd/icons/burst.gif Binary files differnew file mode 100644 index 00000000000..fbdcf575f78 --- /dev/null +++ b/usr.sbin/httpd/icons/burst.gif diff --git a/usr.sbin/httpd/icons/c.gif b/usr.sbin/httpd/icons/c.gif Binary files differnew file mode 100644 index 00000000000..7555b6c164f --- /dev/null +++ b/usr.sbin/httpd/icons/c.gif diff --git a/usr.sbin/httpd/icons/comp.blue.gif b/usr.sbin/httpd/icons/comp.blue.gif Binary files differnew file mode 100644 index 00000000000..f8d76a8c23f --- /dev/null +++ b/usr.sbin/httpd/icons/comp.blue.gif diff --git a/usr.sbin/httpd/icons/comp.gray.gif b/usr.sbin/httpd/icons/comp.gray.gif Binary files differnew file mode 100644 index 00000000000..7664cd03649 --- /dev/null +++ b/usr.sbin/httpd/icons/comp.gray.gif diff --git a/usr.sbin/httpd/icons/compressed.gif b/usr.sbin/httpd/icons/compressed.gif Binary files differnew file mode 100644 index 00000000000..39e732739f5 --- /dev/null +++ b/usr.sbin/httpd/icons/compressed.gif diff --git a/usr.sbin/httpd/icons/continued.gif b/usr.sbin/httpd/icons/continued.gif Binary files differnew file mode 100644 index 00000000000..b0ffb7e0cc0 --- /dev/null +++ b/usr.sbin/httpd/icons/continued.gif diff --git a/usr.sbin/httpd/icons/dir.gif b/usr.sbin/httpd/icons/dir.gif Binary files differnew file mode 100644 index 00000000000..48264601ae0 --- /dev/null +++ b/usr.sbin/httpd/icons/dir.gif diff --git a/usr.sbin/httpd/icons/down.gif b/usr.sbin/httpd/icons/down.gif Binary files differnew file mode 100644 index 00000000000..a354c871cd0 --- /dev/null +++ b/usr.sbin/httpd/icons/down.gif diff --git a/usr.sbin/httpd/icons/dvi.gif b/usr.sbin/httpd/icons/dvi.gif Binary files differnew file mode 100644 index 00000000000..791be33105d --- /dev/null +++ b/usr.sbin/httpd/icons/dvi.gif diff --git a/usr.sbin/httpd/icons/f.gif b/usr.sbin/httpd/icons/f.gif Binary files differnew file mode 100644 index 00000000000..fbe353c2822 --- /dev/null +++ b/usr.sbin/httpd/icons/f.gif diff --git a/usr.sbin/httpd/icons/folder.gif b/usr.sbin/httpd/icons/folder.gif Binary files differnew file mode 100644 index 00000000000..48264601ae0 --- /dev/null +++ b/usr.sbin/httpd/icons/folder.gif diff --git a/usr.sbin/httpd/icons/folder.open.gif b/usr.sbin/httpd/icons/folder.open.gif Binary files differnew file mode 100644 index 00000000000..30979cb5285 --- /dev/null +++ b/usr.sbin/httpd/icons/folder.open.gif diff --git a/usr.sbin/httpd/icons/folder.sec.gif b/usr.sbin/httpd/icons/folder.sec.gif Binary files differnew file mode 100644 index 00000000000..75332d9e59b --- /dev/null +++ b/usr.sbin/httpd/icons/folder.sec.gif diff --git a/usr.sbin/httpd/icons/forward.gif b/usr.sbin/httpd/icons/forward.gif Binary files differnew file mode 100644 index 00000000000..b2959b4c85c --- /dev/null +++ b/usr.sbin/httpd/icons/forward.gif diff --git a/usr.sbin/httpd/icons/generic.gif b/usr.sbin/httpd/icons/generic.gif Binary files differnew file mode 100644 index 00000000000..de60b2940f9 --- /dev/null +++ b/usr.sbin/httpd/icons/generic.gif diff --git a/usr.sbin/httpd/icons/generic.red.gif b/usr.sbin/httpd/icons/generic.red.gif Binary files differnew file mode 100644 index 00000000000..94743981d93 --- /dev/null +++ b/usr.sbin/httpd/icons/generic.red.gif diff --git a/usr.sbin/httpd/icons/generic.sec.gif b/usr.sbin/httpd/icons/generic.sec.gif Binary files differnew file mode 100644 index 00000000000..88d5240c3c3 --- /dev/null +++ b/usr.sbin/httpd/icons/generic.sec.gif diff --git a/usr.sbin/httpd/icons/hand.right.gif b/usr.sbin/httpd/icons/hand.right.gif Binary files differnew file mode 100644 index 00000000000..5cdbc7206da --- /dev/null +++ b/usr.sbin/httpd/icons/hand.right.gif diff --git a/usr.sbin/httpd/icons/hand.up.gif b/usr.sbin/httpd/icons/hand.up.gif Binary files differnew file mode 100644 index 00000000000..85a5d683177 --- /dev/null +++ b/usr.sbin/httpd/icons/hand.up.gif diff --git a/usr.sbin/httpd/icons/icon.sheet.gif b/usr.sbin/httpd/icons/icon.sheet.gif Binary files differnew file mode 100644 index 00000000000..ad1686e4480 --- /dev/null +++ b/usr.sbin/httpd/icons/icon.sheet.gif diff --git a/usr.sbin/httpd/icons/image1.gif b/usr.sbin/httpd/icons/image1.gif Binary files differnew file mode 100644 index 00000000000..01e442bfa92 --- /dev/null +++ b/usr.sbin/httpd/icons/image1.gif diff --git a/usr.sbin/httpd/icons/image2.gif b/usr.sbin/httpd/icons/image2.gif Binary files differnew file mode 100644 index 00000000000..751faeea364 --- /dev/null +++ b/usr.sbin/httpd/icons/image2.gif diff --git a/usr.sbin/httpd/icons/image3.gif b/usr.sbin/httpd/icons/image3.gif Binary files differnew file mode 100644 index 00000000000..4f30484ff64 --- /dev/null +++ b/usr.sbin/httpd/icons/image3.gif diff --git a/usr.sbin/httpd/icons/index.gif b/usr.sbin/httpd/icons/index.gif Binary files differnew file mode 100644 index 00000000000..162478fb3a7 --- /dev/null +++ b/usr.sbin/httpd/icons/index.gif diff --git a/usr.sbin/httpd/icons/layout.gif b/usr.sbin/httpd/icons/layout.gif Binary files differnew file mode 100644 index 00000000000..c96338a1522 --- /dev/null +++ b/usr.sbin/httpd/icons/layout.gif diff --git a/usr.sbin/httpd/icons/left.gif b/usr.sbin/httpd/icons/left.gif Binary files differnew file mode 100644 index 00000000000..279e6710d49 --- /dev/null +++ b/usr.sbin/httpd/icons/left.gif diff --git a/usr.sbin/httpd/icons/link.gif b/usr.sbin/httpd/icons/link.gif Binary files differnew file mode 100644 index 00000000000..c5b6889a76d --- /dev/null +++ b/usr.sbin/httpd/icons/link.gif diff --git a/usr.sbin/httpd/icons/movie.gif b/usr.sbin/httpd/icons/movie.gif Binary files differnew file mode 100644 index 00000000000..00351837741 --- /dev/null +++ b/usr.sbin/httpd/icons/movie.gif diff --git a/usr.sbin/httpd/icons/p.gif b/usr.sbin/httpd/icons/p.gif Binary files differnew file mode 100644 index 00000000000..7b917b4e91e --- /dev/null +++ b/usr.sbin/httpd/icons/p.gif diff --git a/usr.sbin/httpd/icons/patch.gif b/usr.sbin/httpd/icons/patch.gif Binary files differnew file mode 100644 index 00000000000..39bc90e7953 --- /dev/null +++ b/usr.sbin/httpd/icons/patch.gif diff --git a/usr.sbin/httpd/icons/pdf.gif b/usr.sbin/httpd/icons/pdf.gif Binary files differnew file mode 100644 index 00000000000..c88fd777c4b --- /dev/null +++ b/usr.sbin/httpd/icons/pdf.gif diff --git a/usr.sbin/httpd/icons/pie0.gif b/usr.sbin/httpd/icons/pie0.gif Binary files differnew file mode 100644 index 00000000000..6f7a0ae7a70 --- /dev/null +++ b/usr.sbin/httpd/icons/pie0.gif diff --git a/usr.sbin/httpd/icons/pie1.gif b/usr.sbin/httpd/icons/pie1.gif Binary files differnew file mode 100644 index 00000000000..03aa6be71eb --- /dev/null +++ b/usr.sbin/httpd/icons/pie1.gif diff --git a/usr.sbin/httpd/icons/pie2.gif b/usr.sbin/httpd/icons/pie2.gif Binary files differnew file mode 100644 index 00000000000..b04c5e09086 --- /dev/null +++ b/usr.sbin/httpd/icons/pie2.gif diff --git a/usr.sbin/httpd/icons/pie3.gif b/usr.sbin/httpd/icons/pie3.gif Binary files differnew file mode 100644 index 00000000000..4db9d023eda --- /dev/null +++ b/usr.sbin/httpd/icons/pie3.gif diff --git a/usr.sbin/httpd/icons/pie4.gif b/usr.sbin/httpd/icons/pie4.gif Binary files differnew file mode 100644 index 00000000000..93471fdd885 --- /dev/null +++ b/usr.sbin/httpd/icons/pie4.gif diff --git a/usr.sbin/httpd/icons/pie5.gif b/usr.sbin/httpd/icons/pie5.gif Binary files differnew file mode 100644 index 00000000000..57aee93f070 --- /dev/null +++ b/usr.sbin/httpd/icons/pie5.gif diff --git a/usr.sbin/httpd/icons/pie6.gif b/usr.sbin/httpd/icons/pie6.gif Binary files differnew file mode 100644 index 00000000000..0dc327b5697 --- /dev/null +++ b/usr.sbin/httpd/icons/pie6.gif diff --git a/usr.sbin/httpd/icons/pie7.gif b/usr.sbin/httpd/icons/pie7.gif Binary files differnew file mode 100644 index 00000000000..8661337f067 --- /dev/null +++ b/usr.sbin/httpd/icons/pie7.gif diff --git a/usr.sbin/httpd/icons/pie8.gif b/usr.sbin/httpd/icons/pie8.gif Binary files differnew file mode 100644 index 00000000000..59ddb34ce0f --- /dev/null +++ b/usr.sbin/httpd/icons/pie8.gif diff --git a/usr.sbin/httpd/icons/portal.gif b/usr.sbin/httpd/icons/portal.gif Binary files differnew file mode 100644 index 00000000000..0e6e506e004 --- /dev/null +++ b/usr.sbin/httpd/icons/portal.gif diff --git a/usr.sbin/httpd/icons/ps.gif b/usr.sbin/httpd/icons/ps.gif Binary files differnew file mode 100644 index 00000000000..0f565bc1db7 --- /dev/null +++ b/usr.sbin/httpd/icons/ps.gif diff --git a/usr.sbin/httpd/icons/quill.gif b/usr.sbin/httpd/icons/quill.gif Binary files differnew file mode 100644 index 00000000000..818a5cdc7e0 --- /dev/null +++ b/usr.sbin/httpd/icons/quill.gif diff --git a/usr.sbin/httpd/icons/right.gif b/usr.sbin/httpd/icons/right.gif Binary files differnew file mode 100644 index 00000000000..b256e5f75fb --- /dev/null +++ b/usr.sbin/httpd/icons/right.gif diff --git a/usr.sbin/httpd/icons/screw1.gif b/usr.sbin/httpd/icons/screw1.gif Binary files differnew file mode 100644 index 00000000000..af6ba2b097b --- /dev/null +++ b/usr.sbin/httpd/icons/screw1.gif diff --git a/usr.sbin/httpd/icons/screw2.gif b/usr.sbin/httpd/icons/screw2.gif Binary files differnew file mode 100644 index 00000000000..06dccb3e44c --- /dev/null +++ b/usr.sbin/httpd/icons/screw2.gif diff --git a/usr.sbin/httpd/icons/script.gif b/usr.sbin/httpd/icons/script.gif Binary files differnew file mode 100644 index 00000000000..d8a853bc582 --- /dev/null +++ b/usr.sbin/httpd/icons/script.gif diff --git a/usr.sbin/httpd/icons/sound1.gif b/usr.sbin/httpd/icons/sound1.gif Binary files differnew file mode 100644 index 00000000000..8efb49f55d6 --- /dev/null +++ b/usr.sbin/httpd/icons/sound1.gif diff --git a/usr.sbin/httpd/icons/sound2.gif b/usr.sbin/httpd/icons/sound2.gif Binary files differnew file mode 100644 index 00000000000..48e6a7fb2fa --- /dev/null +++ b/usr.sbin/httpd/icons/sound2.gif diff --git a/usr.sbin/httpd/icons/sphere1.gif b/usr.sbin/httpd/icons/sphere1.gif Binary files differnew file mode 100644 index 00000000000..7067070da27 --- /dev/null +++ b/usr.sbin/httpd/icons/sphere1.gif diff --git a/usr.sbin/httpd/icons/sphere2.gif b/usr.sbin/httpd/icons/sphere2.gif Binary files differnew file mode 100644 index 00000000000..a9e462a377c --- /dev/null +++ b/usr.sbin/httpd/icons/sphere2.gif diff --git a/usr.sbin/httpd/icons/tar.gif b/usr.sbin/httpd/icons/tar.gif Binary files differnew file mode 100644 index 00000000000..4032c1bd3d4 --- /dev/null +++ b/usr.sbin/httpd/icons/tar.gif diff --git a/usr.sbin/httpd/icons/tex.gif b/usr.sbin/httpd/icons/tex.gif Binary files differnew file mode 100644 index 00000000000..45e43233b84 --- /dev/null +++ b/usr.sbin/httpd/icons/tex.gif diff --git a/usr.sbin/httpd/icons/text.gif b/usr.sbin/httpd/icons/text.gif Binary files differnew file mode 100644 index 00000000000..4c623909fbf --- /dev/null +++ b/usr.sbin/httpd/icons/text.gif diff --git a/usr.sbin/httpd/icons/transfer.gif b/usr.sbin/httpd/icons/transfer.gif Binary files differnew file mode 100644 index 00000000000..33697dbb667 --- /dev/null +++ b/usr.sbin/httpd/icons/transfer.gif diff --git a/usr.sbin/httpd/icons/unknown.gif b/usr.sbin/httpd/icons/unknown.gif Binary files differnew file mode 100644 index 00000000000..32b1ea23fb6 --- /dev/null +++ b/usr.sbin/httpd/icons/unknown.gif diff --git a/usr.sbin/httpd/icons/up.gif b/usr.sbin/httpd/icons/up.gif Binary files differnew file mode 100644 index 00000000000..6d6d6d1ebf8 --- /dev/null +++ b/usr.sbin/httpd/icons/up.gif diff --git a/usr.sbin/httpd/icons/uu.gif b/usr.sbin/httpd/icons/uu.gif Binary files differnew file mode 100644 index 00000000000..4387d529f69 --- /dev/null +++ b/usr.sbin/httpd/icons/uu.gif diff --git a/usr.sbin/httpd/icons/uuencoded.gif b/usr.sbin/httpd/icons/uuencoded.gif Binary files differnew file mode 100644 index 00000000000..4387d529f69 --- /dev/null +++ b/usr.sbin/httpd/icons/uuencoded.gif diff --git a/usr.sbin/httpd/icons/world1.gif b/usr.sbin/httpd/icons/world1.gif Binary files differnew file mode 100644 index 00000000000..05b4ec20588 --- /dev/null +++ b/usr.sbin/httpd/icons/world1.gif diff --git a/usr.sbin/httpd/icons/world2.gif b/usr.sbin/httpd/icons/world2.gif Binary files differnew file mode 100644 index 00000000000..e3203f7a881 --- /dev/null +++ b/usr.sbin/httpd/icons/world2.gif diff --git a/usr.sbin/httpd/src/.indent.pro b/usr.sbin/httpd/src/.indent.pro new file mode 100644 index 00000000000..77b65f3d6a7 --- /dev/null +++ b/usr.sbin/httpd/src/.indent.pro @@ -0,0 +1 @@ +-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs diff --git a/usr.sbin/httpd/src/CHANGES b/usr.sbin/httpd/src/CHANGES new file mode 100644 index 00000000000..f7cb6411f45 --- /dev/null +++ b/usr.sbin/httpd/src/CHANGES @@ -0,0 +1,2167 @@ +Changes with Apache 1.2.6 + + *) Increase the robustness of the child_main loop. When unexpected + select() or accept() errors occur we exit() the child. This deals + with many reported problems where apache would fill the error_log + with messages. [Dean Gaudet] PR#1747, 1107, 588, 1787, 987, 588 + + *) PORT: Add -lm to LIBS for HPUX. [Dean Gaudet] PR#1639 + + *) SECURITY: "UserDir /abspath" without a * in the path would allow + remote users to access "/~.." and bypass access restrictions + (but note /~../.. was handled properly). + [Lauri Jesmin <jesmin@ut.ee>] PR#1701 + + *) mod_rewrite's RewriteLog should behave like mod_log_config, it + shouldn't force hostname lookups. [Dean Gaudet] PR#1684 + + *) mod_include when using XBitHack Full would send ETags in addition to + sending Last-Modifieds. This is incorrect HTTP/1.1 behaviour. + [Dean Gaudet] PR#1133 + + *) SECURITY: When a client connects to a particular port/addr, and + gives a Host: header ensure that the virtual host requested can + actually be reached via that port/addr. [Ed Korthof <ed@organic.com>] + + *) Support virtual hosts with wildcard port and/or multiple ports + properly. [Ed Korthof <ed@organic.com>] + + *) Fixed some case-sensitivity issues according to RFC2068. + [Dean Gaudet] + + *) Set r->allowed properly in mod_asis.c, mod_dir.c, mod_info.c, + and mod_include.c. [Dean Gaudet] + + *) Variable 'cwd' was being used pointlessly before being set. + [Ken Coar] PR#1738 + + *) SIGURG doesn't exist on all platforms. + [Mark Andrew Heinrich <heinrich@tinderbox.Stanford.EDU>] + + *) When an error occurs during a POST, or other operation with a + request body, the body has to be read from the net before allowing + a keepalive session to continue. [Roy Fielding] PR#1399 + + *) When an error occurs in fcntl() locking suggest the user look up + the docs for LockFile. [Dean Gaudet] + + *) table_set() and table_unset() did not deal correctly with + multiple occurrences of the same key. [Stephen Scheck + <sscheck@infonex.net>, Ben Laurie] PR#1604 + + *) send_fd_length() did not calculate total_bytes_sent properly in error + cases. [Ben Reser <breser@regnow.com>] PR#1366 + + *) r->connection->user was allocated in the wrong pool causing corruption + in some cases when used with mod_cern_meta. [Dean Gaudet] PR#1500 + + *) mod_proxy was sending HTTP/1.1 responses to ftp requests by mistake. + Also removed the auto-generated link to www.apache.org that was the + source of so many misdirected bug reports. [Roy Fielding, Marc Slemko] + + *) Multiple "close" tokens may have been set in the "Connection" + header, not an error, but a waste. + [Ronald.Tschalaer@psi.ch] PR#1683 + + *) "basic" and "digest" auth tokens should be tested case-insensitive. + [Ronald.Tschalaer@psi.ch] PR#1599, PR#1666 + + *) It appears the "257th byte" bug (see + htdocs/manual/misc/known_client_problems.html#257th-byte) can happen + at the 256th byte as well. Fixed. [Dean Gaudet] + + *) mod_rewrite would not handle %3f properly in some situations. + [Ralf Engelschall] + + *) Apache could generate improperly chunked HTTP/1.1 responses when + the bputc() or rputc() functions were used by modules (such as + mod_include). [Dean Gaudet] + + *) #ifdef wrap a few #defines in httpd.h to make life easier on + some ports. [Ralf Engelschall] + + *) Fix MPE compilation error in mod_usertrack.c. [Mark Bixby] + + *) Quote CC='$(CC)' to improve recurse make calls. [Martin Kraemer] + + *) Avoid B_ERROR redeclaration on sysvr4 systems. [Martin Kraemer] + +Changes with Apache 1.2.5 + + *) SECURITY: Fix a possible buffer overflow in logresolve. This is + only an issue on systems without a MAXDNAME define or where + the resolver returns domain names longer than MAXDNAME. [Marc Slemko] + + *) Fix an improper length in an ap_snprintf call in proxy_date_canon(). + [Marc Slemko] + + *) Fix core dump in the ftp proxy when reading incorrectly formatted + directory listings. [Marc Slemko] + + *) SECURITY: Fix possible minor buffer overflow in the proxy cache. + [Marc Slemko] + + *) SECURITY: Eliminate possible buffer overflow in cfg_getline, which + is used to read various types of files such as htaccess and + htpasswd files. [Marc Slemko] + + *) SECURITY: Ensure that the buffer returned by ht_time is always + properly null terminated. [Marc Slemko] + + *) SECURITY: General mod_include cleanup, including fixing several + possible buffer overflows and a possible infinite loop. This cleanup + was done against 1.3 code and then backported to 1.2, the result + is a large difference (due to indentation cleanup in 1.3 code). + Users interested in seeing a smaller set of relevant differences + should consider comparing against src/modules/standard/mod_include.c + from the 1.3b3 release. Non-indentation changes to mod_include + between 1.2 and 1.3 were minimal. [Dean Gaudet, Marc Slemko] + + *) SECURITY: Numerous changes to mod_imap in a general cleanup + including fixing a possible buffer overflow. This cleanup also + was done with 1.3 code as a basis, see the the previous note + about mod_include. [Dean Gaudet] + + *) SECURITY: If a htaccess file can not be read due to bad + permissions, deny access to the directory with a HTTP_FORBIDDEN. + The previous behavior was to ignore the htaccess file if it could not + be read. This change may make some setups with unreadable + htaccess files stop working. PR#817 [Marc Slemko] + + *) SECURITY: no2slash() was O(n^2) in the length of the input. + Make it O(n). This inefficiency could be used to mount a denial + of service attack against the Apache server. Thanks to + Michal Zalewski <lcamtuf@boss.staszic.waw.pl> for reporting + this. [Dean Gaudet] + + *) mod_include used uninitialized data for some uses of && and ||. + [Brian Slesinsky <bslesins@wired.com>] PR#1139 + + *) mod_imap should decline all non-GET methods. + [Jay Bloodworth <jay@pathways.sde.state.sc.us>] + + *) suexec.c wouldn't build without -DLOG_EXEC. [Jason A. Dour] + + *) mod_userdir was modifying r->finfo in cases where it wasn't setting + r->filename. Since those two are meant to be in sync with each other + this is a bug. ["Paul B. Henson" <henson@intranet.csupomona.edu>] + + *) mod_include did not properly handle all possible redirects from sub- + requests. [Ken Coar] + + *) Inetd mode (which is buggy) uses timeouts without having setup the + jmpbuffer. [Dean Gaudet] PR#1064 + + *) Work around problem under Linux where a child will start looping + reporting a select error over and over. + [Rick Franchuk <rickf@transpect.net>] PR#1107 + +Changes with Apache 1.2.4 + + *) The ProxyRemote change in 1.2.3 introduced a bug resulting in the proxy + always making requests with the full-URI instead of just the URI path. + [Marc Slemko, Roy Fielding] + + *) Add -lm for AIX versions >= 4.2 to allow Apache to link properly + on this platform. [Marc Slemko] + +Changes with Apache 1.2.3 + + *) The request to a remote proxy was mangled if it was generated as the + result of a ProxyPass directive. URL schemes other than http:// were not + supported when ProxyRemote was used. PR#260, PR#656, PR#699, PR#713, + PR#812 [Lars Eilebrecht] + + *) Fixed proxy-pass-through feature of mod_rewrite; Added error logging + information for case where proxy module is not available. [Marc Slemko] + + *) Force proxy to always respond as HTTP/1.0, which it was failing to + do for errors and cached responses. [Roy Fielding] + + *) PORT: Improved support for ConvexOS 11. [Jeff Venters] + +Changes with Apache 1.2.2 [not released] + + *) Fixed another long-standing bug in sub_req_lookup_file where it would + happily skip past access checks on subdirectories looked up with relative + paths. (It's used by mod_dir, mod_negotiation, and mod_include.) + [Dean Gaudet] + + *) Add lockfile name to error message printed out when + USE_FLOCK_SERIALIZED_ACCEPT is defined. + [Marc Slemko] + + *) Enhanced the chunking and error handling inside the buffer functions. + [Dean Gaudet, Roy Fielding] + + *) When merging the main server's <Directory> and <Location> sections into + a vhost, put the main server's first and the vhost's second. Otherwise + the vhost can't override the main server. [Dean Gaudet] PR#717 + + *) The <Directory> code would merge and re-merge the same section after + a match was found, possibly causing problems with some modules. + [Dean Gaudet] + + *) Fixed an infinite loop in mod_imap for references above the server root. + [Dean Gaudet] PR#748 + + *) mod_include cleanup showed that handle_else was being used to handle + endif. It didn't cause problems, but it was cleaned up too. + [Howard Fear] + + *) Last official synchonisation of mod_rewrite with author version (because + mod_rewrite is now directly developed by the author at the Apache Group): + o added diff between mod_rewrite 3.0.6+ and 3.0.9 + minus WIN32/NT stuff, but plus copyright removement. + In detail: + - workaround for detecting infinite rewriting loops + - fixed setting of env vars when "-" is used as subst string + - fixed forced response code on redirects (PR#777) + - fixed cases where r->args is "" + - kludge to disable locking on pipes under braindead SunOS + - fix for rewritelog in cases where remote hostname is unknown + - fixed totally damaged request_rec walk-back loop + o remove static from local data and add static to global ones. + o replaced ugly proxy finding stuff by simple + find_linked_module("mod_proxy") call. + o added missing negation char on rewritelog() + o fixed a few comment typos + [Ralf S. Engelschall] + + *) Anonymous_LogEmail was logging on each subrequest. + [Dean Gaudet] PR#421, PR#868 + + *) "force-response-1.0" now only applies to requests which are HTTP/1.0 to + begin with. "nokeepalive" now works for HTTP/1.1 clients. Added + "downgrade-1.0" which causes Apache to pretend it received a 1.0. + Additionally mod_browser now triggers during translate_name to workaround + a deficiency in the header_parse phase. + [Dean Gaudet] PR#875 + + *) get_client_block() returns wrong length if policy is + REQUEST_CHUNKED_DECHUNK. + [Kenichi Hori <ken@d2.bs1.fc.nec.co.jp>] PR#815 + + *) Properly treat <files> container like other containers in mod_info. + [Marc Slemko] PR#848 + + *) The proxy didn't treat the "Host:" keyword of the host header as case- + insensitive. The proxy would corrupt the first line of a response from + an HTTP/0.9 server. [Kenichi Hori <ken@d2.bs1.fc.nec.co.jp>] PR#813,814 + + *) mod_include would log some bogus values occasionally. + [Skip Montanaro <skip@calendar.com>, Marc Slemko] PR#797 + + *) PORT: The slack fd changes in 1.2.1 introduced a problem with SIGHUP + under Solaris 2.x (up through 2.5.1). It has been fixed. + [Dean Gaudet] PR#832 + + *) API: In HTTP/1.1, whether or not a request message contains a body + is independent of the request method and based solely on the presence + of a Content-Length or Transfer-Encoding. Therefore, our default + handlers need to be prepared to read a body even if they don't know + what to do with it; otherwise, the body would be mistaken for the + next request on a persistent connection. discard_request_body() + has been added to take care of that. [Roy Fielding] PR#378 + + *) API: Symbol APACHE_RELEASE provides a numeric form of the Apache + release version number, such that it always increases along the + same lines as our source code branching. [Roy Fielding] + + *) Minor oversight on multiple variants fixed. [Paul Sutton] PR#94 + +Changes with Apache 1.2.1 + + *) SECURITY: Don't serve file system objects unless they are plain files, + symlinks, or directories. This prevents local users from using pipes + or named sockets to invoke programs for an extremely crude form of + CGI. [Dean Gaudet] + + *) SECURITY: HeaderName and ReadmeName were settable in .htaccess and + could contain "../" allowing a local user to "publish" any file on + the system. No slashes are allowed now. [Dean Gaudet] + + *) SECURITY: It was possible to violate the symlink Options using mod_dir + (headers, readmes, titles), mod_negotiation (type maps), or + mod_cern_meta (meta files). [Dean Gaudet] + + *) SECURITY: Apache will refuse to run as "User root" unless + BIG_SECURITY_HOLE is defined at compile time. [Dean Gaudet] + + *) CONFIG: If a symlink pointed to a directory then it would be disallowed + if it contained a .htaccess disallowing symlinks. This is contrary + to the rule that symlink permissions are tested with the symlink + options of the parent directory. [Dean Gaudet] PR#353 + + *) CONFIG: The LockFile directive can be used to place the serializing + lockfile in any location. It previously defaulted to /usr/tmp/htlock. + [Somehow it took four of us: Randy Terbush, Jim Jagielski, Dean Gaudet, + Marc Slemko] + + *) Request processing now retains state of whether or not the request + body has been read, so that internal redirects and subrequests will + not try to read it twice (and block). [Roy Fielding] + + *) Add a placeholder in modules/Makefile to avoid errors with certain + makes. [Marc Slemko] + + *) QUERY_STRING was unescaped in mod_include, it shouldn't be. + [Dean Gaudet] PR#644 + + *) mod_include was not properly changing the current directory. + [Marc Slemko] PR#742 + + *) Attempt to work around problems with third party libraries that do not + handle high numbered descriptors (examples include bind, and + solaris libc). On all systems apache attempts to keep all permanent + descriptors above 15 (called the low slack line). Solaris users + can also benefit from adding -DHIGH_SLACK_LINE=256 to EXTRA_CFLAGS + which keeps all non-FILE * descriptors above 255. On all systems + this should make supporting large numbers of vhosts with many open + log files more feasible. If this causes trouble please report it, + you can disable this workaround by adding -DNO_SLACK to EXTRA_CFLAGS. + [Dean Gaudet] various PRs + + *) Related to the last entry, network sockets are now opened before + log files are opened. The only known case where this can cause + problems is under Solaris with many virtualhosts and many Listen + directives. But using -DHIGH_SLACK_LINE=256 described above will + work around this problem. [Dean Gaudet] + + *) USE_FLOCK_SERIALIZED_ACCEPT is now default for FreeBSD, A/UX, and + SunOS 4. + + *) Improved unix error response logging. [Marc Slemko] + + *) Update mod_rewrite from 3.0.5 to 3.0.6. New ruleflag + QSA=query_string_append. Also fixed a nasty bug in per-dir context: + when a URL http://... was used in concunction with a special + redirect flag, e.g. R=permanent, the permanent status was lost. + [Ronald Tschalaer <Ronald.Tschalaer@psi.ch>, Ralf S. Engelschall] + + *) If an object has multiple variants that are otherwise equal Apache + would prefer the last listed variant rather than the first. + [Paul Sutton] PR#94 + + *) "make clean" at the top level now removes *.o. [Dean Gaudet] PR#752 + + *) mod_status dumps core in inetd mode. [Marc Slemko and Roy Fielding] + PR#566 + + *) pregsub had an off-by-1 in its error checking code. [Alexei Kosut] + + *) PORT: fix rlim_t problems with AIX 4.2. [Marc Slemko] PR#333 + + *) PORT: Update Unixware support for 2.1.2. + [Lawrence Rosenman <ler@lerctr.org>] PR#511 + + *) PORT: NonStop-UX [Joachim Schmitz <schmitz_joachim@tandem.com>] PR#327 + + *) PORT: Update ConvexOS support for 11.5. + [David DeSimone <fox@convex.com>] PR#399 + + *) PORT: Support for dec cc compiler under ultrix. + ["P. Alejandro Lopez-Valencia" <alejolo@ideam.gov.co>] PR#388 + + *) PORT: Support for Maxion/OS SVR4.2 Real Time Unix. [no name given] PR#383 + + *) PORT: Workaround for AIX 3.x compiler bug in http_bprintf.c. + [Marc Slemko] PR#725 + + *) PORT: fix problem compiling http_bprintf.c with gcc under SCO + [Marc Slemko] PR#695 + +Changes with Apache 1.2 + +Changes with Apache 1.2b11 + + *) Fixed open timestamp fd in proxy_cache.c [Chuck Murcko] + + *) Added undocumented perl SSI mechanism for -DUSE_PERL_SSI and mod_perl. + [Rob Hartill] + + *) Proxy needs to use hard_timeout instead of soft_timeout when it is + reading from one buffer and writing to another, at least until it has + a custom timeout handler. [Roy Fielding and Petr Lampa] + + *) Fixed problem on Irix with servers hanging in IdentityCheck, + apparently due to a mismatch between sigaction and setjmp. + [Roy Fielding] PR#502 + + *) Log correct status code if we timeout before receiving a request (408) + or if we received a request-line that was too long to process (414). + [Ed Korthof and Roy Fielding] PR#601 + + *) Virtual hosts with the same ServerName, but on different ports, were + not being selected properly. [Ed Korthof] + + *) Added code to return the requested IP address from proxy_host2addr() + if gethostbyaddr() fails due to reverse DNS lookup problems. Original + change submitted by Jozsef Hollosi <hollosi@sbcm.com>. + [Chuck Murcko] PR#614 + + *) If multiple requests on a single connection are used to retrieve + data from different virtual hosts, the virtual host list would be + scanned starting with the most recently used VH instead of the first, + causing most virtual hosts to be ignored. + [Paul Sutton and Martin Mares] PR#610 + + *) The OS/2 handling of process group was broken by a porting patch for + MPE, so restored prior code for OS/2. [Roy Fielding and Garey Smiley] + + *) Inherit virtual server port from main server if none (or "*") is + given for VirtualHost. [Dean Gaudet] PR#576 + + *) If the lookup for a DirectoryIndex name with content negotiation + has found matching variants, but none are acceptable, return the + negotiation result if there are no more DirectoryIndex names to lookup. + [Petr Lampa and Roy Fielding] + + *) If a soft_timeout occurs after keepalive is set, then the main child + loop would try to read another request even though the connection + has been aborted. [Roy Fielding] + + *) Configure changes: Allow for whitespace at the start of a + Module declaration. Also, be more understanding about the + CC=/OPTIM= format in Configuration. Finally, fix compiler + flags if using HP-UX's cc compiler. [Jim Jagielski] + + *) Subrequests and internal redirects now inherit the_request from the + original request-line. [Roy Fielding] + + *) Test for error conditions before creating output header fields, since + we don't want the error message to include those fields. Likewise, + reset the content_language(s) and content_encoding of the response + before generating or redirecting to an error message, since the new + message will have its own Content-* definitions. [Dean Gaudet] + + *) Restored the semantics of headers_out (headers sent only with 200..299 + and 304 responses) and err_headers_out (headers sent with all responses). + Avoid the overhead of copying tables if err_headers_out is empty + (the usual case). [Roy Fielding] + + *) Fixed a couple places where a check for the default Content-Type was + not properly checking both the value configured by the DefaultType + directive and the DEFAULT_TYPE symbol in httpd.h. Changed the value + of DEFAULT_TYPE to match the documented default (text/plain). + [Dean Gaudet] PR#506 + + *) Escape the HTML-sensitive characters in the Request-URI that is + output for each child by mod_status. [Dean Gaudet and Ken Coar] PR#501 + + *) Properly initialize the flock structures used by the mutex locking + around accept() when USE_FCNTL_SERIALIZED_ACCEPT is defined. + [Marc Slemko] + + *) The method for determining PATH_INFO has been restored to the pre-1.2b + (and NCSA httpd) definition wherein it was the extra path info beyond + the CGI script filename. The environment variable FILEPATH_INFO has + been removed, and instead we supply the original REQUEST_URI to any + script that wants to be Apache-specific and needs the real URI path. + This solves a problem with existing scripts that use extra path info + in the ScriptAlias directive to pass options to the CGI script. + [Roy Fielding] + + *) The _default_ change in 1.2b10 will change the behaviour on configs + that use multiple Listen statements for listening on multiple ports. + But that change is necessary to make _default_ consistent with other + forms of <VirtualHost>. It requires such configs to be modified + to use <VirtualHost _default_:*>. The documentation has been + updated. [Dean Gaudet] PR#530 + + *) If an ErrorDocument CGI script is used to respond to an error + generated by another CGI script which has already read the message + body of the request, the server would block trying to read the + message body again. [Rob Hartill] + + *) signal() replacement conflicted with a define on QNX (and potentially + other platforms). Fixed. [Ben Laurie] PR#512 + +Changes with Apache 1.2b10 + + *) Allow HTTPD_ROOT, SERVER_CONFIG_FILE, DEFAULT_PATH, and SHELL_PATH + to be configured via -D in Configuration. [Dean Gaudet] PR#449 + + *) <VirtualHost _default_:portnum> didn't work properly. [Dean Gaudet] + + *) Added prototype for mktemp() for SUNOS4 [Marc Slemko] + + *) In mod_proxy.c, check return values for proxy_host2addr() when reading + config, in case the hostent struct returned is trash. + [Chuck Murcko] PR #491 + + *) Fixed the fix in 1.2b9 for parsing URL query info into args for CGI + scripts. [Dean Gaudet, Roy Fielding, Marc Slemko] + +Changes with Apache 1.2b9 [never announced] + + *) Reset the MODULE_MAGIC_NUMBER to account for the unsigned port + changes and in anticipation of 1.2 final release. [Roy Fielding] + + *) Fix problem with scripts not receiving a SIGPIPE when client drops + the connection (e.g., when user presses Stop). Apache will now stop + trying to send a message body immediately after an error from write. + [Roy Fielding and Nathan Kurz] PR#335 + + *) Rearrange Configuration.tmpl so that mod_rewrite has higher priority + than mod_alias, and mod_alias has higher priority than mod_proxy; + rearranged other modules to enhance understanding of their purpose + and relative order (and maybe even reduce some overhead). + [Roy Fielding and Sameer Parekh] + + *) Fix graceful restart. Eliminate many signal-related race + conditions in both forms of restart, and in SIGTERM. See + htdocs/manual/stopping.html for details on stopping and + restarting the parent. [Dean Gaudet] + + *) Fix memory leaks in mod_rewrite, mod_browser, mod_include. Tune + memory allocator to avoid a behaviour that required extra blocks to + be allocated. [Dean Gaudet] + + *) Allow suexec to access files relative to current directory but not + above. (Excluding leading / or any .. directory.) [Ken Coar] + PR#269, 319, 395 + + *) Fix suexec segfault when group doesn't exist. [Gregory Neil Shapiro] + PR#367, 368, 354, 453 + + *) Fix the above fix: if suexec is enabled, avoid destroying r->url + while obtaining the /~user and save the username in a separate data + area so that it won't be overwritten by the call to getgrgid(), and + fix some misuse of the pool string allocation functions. Also fixes + a general problem with parsing URL query info into args for CGI scripts. + [Roy Fielding] PR#339, 367, 354, 453 + + *) Fix IRIX warning about bzero undefined. [Marc Slemko] + + *) Fix problem with <Directory proxy:...>. [Martin Kraemer] PR#271 + + *) Corrected spelling of "authoritative". AuthDBAuthoratative became + AuthDBAuthoritative. [Marc Slemko] PR#420 + + *) MaxClients should be at least 1. [Lars Eilebrecht] PR#375 + + *) The default handler now logs invalid methods or URIs (i.e. PUT on an + object that can't be PUT, or FOOBAR for some method FOOBAR that + apache doesn't know about at all). Log 404s that occur in mod_include. + [Paul Sutton, John Van Essen] + + *) If a soft timeout (or lingerout) occurs while trying to flush a + buffer or write inside buff.c or fread'ing from a CGI's output, + then the timeout would be ignored. [Roy Fielding] PR#373 + + *) Work around a bug in Netscape Navigator versions 2.x, 3.x and 4.0b2's + parsing of headers. If the terminating empty-line CRLF occurs starting + at the 256th or 257th byte of output, then Navigator will think a normal + image is invalid. We are guessing that this is because their initial + read of a new request uses a 256 byte buffer. We check the bytes written + so far and, if we are about to tickle the bug, we instead insert a + padding header of eminent bogosity. [Roy Fielding and Dean Gaudet] PR#232 + + *) Fixed SIGSEGV problem when a DirectoryIndex file is also the source + of an external redirection. [Roy Fielding and Paul Sutton] + + *) Configure would create a broken Makefile if the configuration file + contained a commented-out Rule. [Roy Fielding] + + *) Promote per_dir_config and subprocess_env from the subrequest to the + main request in mod_negotiation. In particular this fixes a bug + where <Files> sections wouldn't properly apply to negotiated content. + [Dean Gaudet] + + *) Fix a potential deadlock in mod_cgi script_err handling. + [Ralf S. Engelschall] + + *) rotatelogs zero-pads the logfile names to improve alphabetic sorting. + [Mitchell Blank Jr] + + *) Updated mod_rewrite to 3.0.4: Fixes HTTP redirects from within + .htaccess files because the RewriteBase was not replaced correctly. + Updated mod_rewrite to 3.0.5: Fixes problem with rewriting inside + <Directory> sections missing a trailing /. [Ralf S. Engelschall] + + *) Clean up Linux settings in conf.h by detecting 2.x versus 1.x. For + 1.x the settings are those of pre-1.2b8. For 2.x we include + HAVE_SHMGET (scoreboard in shared memory rather than file) and + HAVE_SYS_RESOURCE_H (enable the RLimit commands). + [Dean Gaudet] PR#336, PR#340 + + *) Redirect did not preserve ?query_strings when present in the client's + request. [Dean Gaudet] + + *) Configure was finding non-modules on EXTRA_LIBS. [Frank Cringle] PR#380 + + *) Use /bin/sh5 on ultrix. [P. Alejandro Lopez-Valencia] PR#369 + + *) Add UnixWare compile/install instructions. [Chuck Murcko] + + *) Add mod_example (illustration of API techniques). [Ken Coar] + + *) Add macro for memmove to conf.h for SUNOS4. [Marc Slemko] + + *) Improve handling of directories when filenames have spaces in them. + [Chuck Murcko] + + *) For hosts with multiple IP addresses, try all additional addresses if + necessary to get a connect. Fail only if hostent address list is + exhausted. [Chuck Murcko] + + *) More signed/unsigned port fixes. [Dean Gaudet] + + *) HARD_SERVER_LIMIT can be defined in the Configuration file now. + [Dean Gaudet] + +Changes with Apache 1.2b8 + + *) suexec.c doesn't close the log file, allowing CGIs to continue writing + to it. [Marc Slemko] + + *) The addition of <Location> and <File> directives made the + sub_req_lookup_simple() function bogus, so we now handle + the special cases directly. [Dean Gaudet] + + *) We now try to log where the server is dumping core when a fatal + signal is received. [Ken Coar] + + *) Improved lingering_close by adding a special timeout, removing the + spurious log messages, removing the nonblocking settings (they + are not needed with the better timeout), and adding commentary + about the NO_LINGCLOSE and USE_SO_LINGER issues. NO_LINGCLOSE is + now the default for SunOS4, Unixware, NeXT, and Irix. [Roy Fielding] + + *) Send error messages about setsockopt failures to the server error + log instead of stderr. [Roy Fielding] + + *) Fix loopholes in proxy cache expiry vis a vis alarms. [Brian Moore] + + *) Stopgap solution for CGI 3-second delay with server-side includes: if + processing a subrequest, allocate memory from r->main->pool instead + of r->pool so that we can avoid waiting for free_proc_chain to cleanup + in the middle of an SSI request. [Dean Gaudet] PR #122 + + *) Fixed status of response when POST is received for a nonexistant URL + (was sending 405, now 404) and when any method is sent with a + full-URI that doesn't match the server and the server is not acting + as a proxy (was sending 501, now 403). [Roy Fielding] + + *) Host port changed to unsigned short. [Ken Coar] PR #276 + + *) Fix typo in command definition of AuthAuthoritative. [Ken Coar] PR #246 + + *) Defined HAVE_SHMGET for shared memory on Linux. [Dean Gaudet] + + *) Report extra info from errno with many errors that cause httpd to exit. + spawn_child, popenf, and pclosef now have valid errno returns in the + event of an error. Correct problems where errno was stomped on + before being reported. [Dean Gaudet] + + *) In the proxy, if the cache filesystem was full, garbage_coll() was + never called, and thus the filesystem would remain full indefinitely. + We now also remove incomplete cache files left if the origin server + didn't send a Content-Length header and either the client has aborted + transfer or bwrite() to client has failed. [Petr Lampa] + + *) Fixed the handling of module and script-added header fields. + Improved the interface for sending header fields and reduced + the duplication of code between sending okay responses and errors. + We now always send both headers_out and err_headers_out, and + ensure that the server-reserved fields are not being overridden, + while not overriding those that are not reserved. [Roy Fielding] + + *) Moved transparent content negotiation fields to err_headers_out + to reflect above changes. [Petr Lampa] + + *) Fixed the determination of whether or not we should make the + connection persistent for all of the cases where some other part + of the server has already indicated that we should not. Also + improved the ordering of the test so that chunked encoding will + be set whenever it is desired instead of only when KeepAlive + is enabled. Added persistent connection capability for most error + responses (those that do not indicate a bad input stream) when + accessed by an HTTP/1.1 client. [Roy Fielding] + + *) Added missing timeouts for sending header fields, error responses, + and the last chunk of chunked encoding, each of which could have + resulted in a process being stuck in write forever. Using soft_timeout + requires that the sender check for an aborted connection rather than + continuing after an EINTR. Timeouts that used to be initiated before + send_http_header (and never killed) are now initiated only within or + around the routines that actually do the sending, and not allowed to + propagate above the caller. [Roy Fielding] + + *) mod_auth_anon required an @ or a . in the email address, not both. + [Dirk vanGulik] + + *) per_dir_defaults weren't set correctly until directory_walk for + name-based vhosts. This fixes an obscure bug with the wrong config + info being used for vhosts that share the same ip as the server. + [Dean Gaudet] + + *) Improved generation of modules/Makefile to be more generic for + new module directories. [Ken Coar, Chuck Murcko, Roy Fielding] + + *) Generate makefile dependency for Configuration based on the actual + name given when running the Configure process. [Dean Gaudet] + + *) Fixed problem with vhost error log not being set prior to + initializing virtual hosts. [Dean Gaudet] + + *) Fixed infinite loop when a trailing slash is included after a type map + file URL (extra path info). [Petr Lampa] + + *) Fixed server status updating of per-connection counters. [Roy Fielding] + + *) Add documentation for DNS issues (reliability and security), and try + to explain the virtual host matching process. [Dean Gaudet] + + *) Try to continue gracefully by disabling the vhost if a DNS lookup + fails while parsing the configuration file. [Dean Gaudet] + + *) Improved calls to setsockopt. [Roy Fielding] + + *) Negotiation changes: Don't output empty content-type in variant list; + Output charset in variant list; Return sooner from handle_multi() if + no variants found; Add handling of '*' wildcard in Accept-Charset. + [Petr Lampa and Paul Sutton] + + *) Fixed overlaying of request/sub-request notes and headers in + mod_negotiation. [Dean Gaudet] + + *) If two variants' charset quality are equal and one is the default + charset (iso-8859-1), then prefer the variant that was specifically + listed in Accept-Charset instead of the default. [Petr Lampa] + + *) Memory allocation problem in push_array() -- it would corrupt memory + when nalloc==0. [Kai Risku <krisku@tf.hut.fi> and Roy Fielding] + + *) invoke_handler() doesn't handle mime arguments in content-type + [Petr Lampa] PR#160 + + *) Reduced IdentityCheck timeout to 30 seconds, as per RFC 1413 minimum. + [Ken Coar] + + *) Fixed problem with ErrorDocument not working for virtual hosts + due to one of the performance changes in 1.2b7. [Dean Gaudet] + + *) Log an error message if we get a request header that is too long, + since it may indicate a buffer overflow attack. [Marc Slemko] + + *) Made is_url() allow "[-.+a-zA-Z0-9]+:" as a valid scheme and + not reject URLs without a double-slash, as per RFC2068 section 3.2. + [Ken Coar] PR #146, #187 + + *) Added table entry placeholder for new header_parser callback + in all of the distributed modules. [Ken Coar] PR #191 + + *) Allow for cgi files without the .EXE extension on them under OS/2. + [Garey Smiley] PR #59 + + *) Fixed error message when resource is not found and URL contains + path info. [Petr Lampa and Dean Gaudet] PR #40 + + *) Fixed user and server confusion over what should be a virtual host + and what is the main server, resulting in access to something + other than the name defined in the virtualhost directive (but + with the same IP address) failing. [Dean Gaudet] + + *) Updated mod_rewrite to version 3.0.2, which: fixes compile error on + AIX; improves the redirection stuff to enable the users to generally + redirect to http, https, gopher and ftp; added TIME variable for + RewriteCond which expands to YYYYMMDDHHMMSS strings and added the + special patterns >STRING, <STRING and =STRING to RewriteCond, which + can be used in conjunction with %{TIME} or other variables to create + time-dependent rewriting rules. [Ralf S. Engelschall] + + *) bpushfd() no longer notes cleanups for the file descriptors it is handed. + Module authors may need to adjust their code for proper cleanup to take + place (that is, call note_cleanups_for_fd()). This change fixes problems + with file descriptors being erroneously closed when the proxy module was + in use. [Ben Laurie] + + *) Fix bug in suexec reintroduced by changes in 1.2b7 which allows + initgroups() to hose the group information needed for later + comparisons. [Randy Terbush] + + *) Remove unnecessary call to va_end() in create_argv() which + caused a SEGV on some systems. + + *) Use proper MAXHOSTNAMELEN symbol for limiting length of server name. + [Dean Gaudet] + + *) Clear memory allocated for listeners. [Randy Terbush] + + *) Improved handling of IP address as a virtualhost address and + introduced "_default_" as a synonym for the default vhost config. + [Dean Gaudet] PR #212 + +Changes with Apache 1.2b7 + + *) Port to UXP/DS(V20) [Toshiaki Nomura <nom@yk.fujitsu.co.jp>] + + *) unset Content-Length if chunked (RFC-2068) [Petr Lampa] + + *) mod_negotiation fixes [Petr Lampa] PR#157, PR#158, PR#159 + - replace protocol response numbers with symbols + - save variant-list into main request notes + - free allocated memory from subrequests + - merge notes, headers_out and err_headers_out + + *) changed status check mask in proxy_http.c from "HTTP/#.# ### *" to + "HTTP/#.# ###*" to be more lenient about what we accept. + [Chuck Murcko] + + *) more proxy FTP bug fixes: + - Changed send_dir() to remove user/passwd from displayed URL. + - Changed login error messages to be more descriptive. + - remove setting of SO_DEBUG socket option + - Make ftp_getrc() more lenient about multiline responses, + specifically, 230 responses which don't have continuation 230- + on each line). These seem to be all NT FTP servers, and while + perhaps questionable, they appear to be legal by RFC 959. + - Add missing kill_timeout() after transfer to user completes. + [Chuck Murcko] + + *) Fixed problem where a busy server could hang when restarting + after being sent a SIGHUP due to child processes not exiting. + [Marc Slemko] + + *) Modify mod_include escaping so a '\' only signifies an escaped + character if the next character is one that needs + escaping. [Ben Laurie] + + *) Eliminated possible infinite loop in mod_imap when relative URLs are + used with a 'base' directive that does not have a '/' in it. + [Marc Slemko, reported by Onno Witvliet <onno@tc.hsa.nl>] + + *) Reduced the default timeout from 1200 seconds to 300, and the + one in the sample configfile from 400 to 300. [Marc Slemko] + + *) Stop vbprintf from crashing if given a NULL string pointer; + print (null) instead. [Ken Coar] + + *) Don't disable Nagle algorithm if system doesn't have TCP_NODELAY. + [Marc Slemko and Roy Fielding] + + *) Fixed problem with mod_cgi-generated internal redirects trying to + read the request message-body twice. [Archie Cobbs and Roy Fielding] + + *) Reduced timeout on lingering close, removed possibility of a blocked + read causing the child to hang, and stopped logging of errors if + the socket is not connected (reset by client). [Roy Fielding] + + *) Rearranged main child loop to remove duplication of code in + select/accept and keep-alive requests, fixed several bugs regarding + checking scoreboard_image for exit indication and failure to + account for all success conditions and trap all error conditions, + prevented multiple flushes before closing the socket; close the entire + socket buffer instead of just one descriptor, prevent logging of + EPROTO and ECONNABORTED on platforms where supported, and generally + improved readability. [Roy Fielding] + + *) Extensive performance improvements. Cleaned up inefficient use of + auto initializers, multiple is_matchexp calls on a static string, + and excessive merging of response_code_strings. [Dean Gaudet] + + *) Added double-buffering to mod_include to improve performance on + server-side includes. [Marc Slemko] + + *) Several fixes for suexec wrapper. [Randy Terbush] + - Make wrapper work for files on NFS filesystem. + - Fix portability problem of MAXPATHLEN. + - Fix array overrun problem in clean_env(). + - Fix allocation of PATH environment variable + + *) Removed extraneous blank line is description of mod_status chars. + [Kurt Kohler] + + *) Logging of errors from the call_exec routine simply went nowhere, + since the logfile fd has been closed, so now we send them to stderr. + [Harald T. Alvestrand] + + *) Fixed core dump when DocumentRoot is a CGI. + [Ben Laurie, reported by geddis@tesserae.com] + + *) Fixed potential file descriptor leak in mod_asis; updated it and + http_core to use pfopen/pfclose instead of fopen/fclose. + [Randy Terbush and Roy Fielding] + + *) Fixed handling of unsigned ints in ap_snprintf() on some chips such + as the DEC Alpha which is 64-bit but uses 32-bit ints. + [Dean Gaudet and Ken Coar] + + *) Return a 302 response code to the client when sending a redirect + due to a missing trailing '/' on a directory instead of a 301; now + it is cacheable. [Markus Gyger] + + *) Fix condition where, if a bad directive occurs in .htaccess, and + sub_request() goes first to this directory, then log_reason() will + SIGSEGV because it doesn't have initialized r->per_dir_config. + [PR#162 from Petr Lampa, fix by Marc Slemko and Dean Gaudet] + + *) Fix handling of lang_index in is_variant_better(). This was + causing problems which resulted in the server sending the + wrong language document in some cases. [Petr Lampa] + + *) Remove free() from clean_env() in suexec wrapper. This was nuking + the clean environment on some systems. + + *) Tweak byteserving code (e.g. serving PDF files) to work around + bugs in Netscape Navigator and Microsoft Internet Explorer. + Emit Content-Length header when sending multipart/byteranges. + [Alexei Kosut] + + *) Port to HI-UX/WE2. [Nick Maclaren] + + *) Port to HP MPE operating system for HP 3000 machines + [Mark Bixby <markb@cccd.edu>] + + *) Fixed bug which caused a segmentation fault if only one argument + given to RLimit* directives. [Ed Korthof] + + *) Continue persistent connection after 204 or 304 response. [Dean Gaudet] + + *) Improved buffered output to the client by delaying the flush decision + until the BUFF code is actually about to read the next request. + This fixes a problem introduced in 1.2b5 with clients that send + an extra CRLF after a POST request. Also improved chunked output + performance by combining writes using writev() and removing as + many bflush() calls as possible. NOTE: Platforms without writev() + must add -DNO_WRITEV to the compiler CFLAGS, either in Configuration + or Configure, unless we have already done so. [Dean Gaudet] + + *) Fixed mod_rewrite bug which truncated the rewritten URL [Marc Slemko] + + *) Fixed mod_info output corruption bug introduced by buffer overflow + fixes. [Dean Gaudet] + + *) Fixed http_protocol to correctly output all HTTP/1.1 headers, including + for the special case of a 304 response. [Paul Sutton] + + *) Improved handling of TRACE method by bypassing normal method handling + and header parsing routines; fixed Allow response to always allow TRACE. + [Dean Gaudet] + + *) Fixed compiler warnings in the regex library. [Dean Gaudet] + + *) Cleaned-up some of the generated HTML. [Ken Coar] + +Changes with Apache 1.2b6 + + *) Allow whitespace in imagemap mapfile coordinates. [Marc Slemko] + + *) Fix typo introduced in fix for potential infinite loop around + accept() in child_main(). This change caused the rev to 1.2b6. + 1.2b5 was never a public beta. + +Changes with Apache 1.2b5 + + *) Change KeepAlive semantics (On|Off instead of a number), add + MaxKeepAliveRequests directive. [Alexei Kosut] + + *) Various NeXT compilation patches, as well as a change in + regex/regcomp.c since that file also used a NEXT define. + [Andreas Koenig] + + *) Allow * to terminate the end of a directory match in mod_dir. + Allows /~* to match for both /~joe and /~joe/. [David Bronder] + + *) Don't call can_exec() if suexec_enabled. Calling this requires + scripts executed by the suexec wrapper to be world executable, which + defeats one of the advantages of running the wrapper. [Randy Terbush] + + *) Portability Fix: IRIX complained with 'make clean' about *pure* (removed) + [Jim Jagielski] + + *) Migration from sprintf() to snprintf() to avoid buffer + overflows. [Marc Slemko] + + *) Provide portable snprintf() implementation (ap_snprintf) + as well as *cvt family. [Jim Jagielski] + + *) Portability Fix: NeXT lacks unistd.h so we wrap it's inclusion + [Jim Jagielski] + + *) Remove mod_fastcgi.c from the distribution. This module appears + to be maintained more through the Open Market channels and should + continue to be easily available at http://www.fastcgi.com/ + + *) Fixed bug in modules/Makefile that wouldn't allow building in more + than one subdirectory (or cleaning, either). [Jeremy Laidman] + + *) mod_info assumed that the config files were relative to ServerRoot. + [Ken the Rodent] + + *) CGI scripts called as an error document resulting from failed + CGI execution would hang waiting for POST'ed data. [Rob Hartill] + + *) Log reason when mod_dir returns access HTTP_FORBIDDEN + [Ken the Rodent] + + *) Properly check errno to prevent display of a directory index + when server receives a long enough URL to confuse stat(). + [Marc Slemko] + + *) Several security enhancements to suexec wrapper. It is _highly_ + recommended that previously installed versions of the wrapper + be replaced with this version. [Randy Terbush, Jason Dour] + + - ~user execution now properly restricted to ~user's home + directory and below. + - execution restricted to UID/GID > 100 + - restrict passed environment to known variables + - call setgid() before initgroups() (portability fix) + - remove use of setenv() (portability fix) + + *) Add HTTP/1.0 response forcing. [Ben Laurie] + + *) Add access control via environment variables. [Ben Laurie] + + *) Add rflush() function. [Alexei Kosut] + + *) remove duplicate pcalloc() call in new_connection(). + + *) Fix incorrect comparison which could allow number of children = + MaxClients + 1 if less than HARD_SERVER_LIMIT. Also fix potential + problem if StartServers > HARD_SERVER_LIMIT. [Ed Korthof] + + *) Updated support for OSes (MachTen, ULTRIX, Paragon, ISC, OpenBSD + AIX PS/2, CONVEXOS. [Jim Jagielski] + + *) Replace instances of inet_ntoa() with inet_addr() for ProxyBlock. + It's more portable. [Martin Kraemer] + + *) Replace references to make in Makefile.tmpl with $(MAKE). + [Chuck Murcko] + + *) Add ProxyBlock directive w/IP address caching. Add IP address + caching to NoCache directive as well. ProxyBlock works with all + handlers; NoCache now also works with FTP for anonymous logins. + Still more code cleanup. [Chuck Murcko] + + *) Add "header parse" API hook [Ben Laurie] + + *) Fix byte ordering problems for REMOTE_PORT [Chuck Murcko] + + *) suEXEC wrapper was freeing memory that had not been malloc'ed. + + *) Correctly allow access and auth directives in <Files> sections in + server config files. [Alexei Kosut] + + *) Fix bug with ServerPath that could cause certain files to be not + found by the server. [Alexei Kosut] + + *) Fix handling of ErrorDocument so that it doesn't remove a trailing + double-quote from text and so that it properly checks for unsupported + status codes using the new index_of_response interface. [Roy Fielding] + + *) Multiple fixes to the lingering_close code in order to avoid being + interrupted by a stray timeout, to avoid lingering on a connection + that has already been aborted or never really existed, to ensure that + we stop lingering as soon as any error condition is received, and to + prevent being stuck indefinitely if the read blocks. Also improves + reporting of error conditions. [Marc Slemko and Roy Fielding] + + *) Fixed initialization of parameter structure for sigaction. + [mgyger@itr.ch, Adrian Filipi-Martin] + + *) Fixed reinitializing the parameters before each call to accept and + select, and removed potential for infinite loop in accept. + [Roy Fielding, after useful PR from adrian@virginia.edu] + + *) Fixed condition where, if a child fails to fork, the scoreboard would + continue to say SERVER_STARTING forever. Eventually, the main process + would refuse to start new children because count_idle_servers() will + count those SERVER_STARTING entries and will always report that there + are enough idle servers. [Phillip Vandry] + + *) Fixed bug in bcwrite regarding failure to account for partial writes. + Avoided calling bflush() when the client is pipelining requests. + Removed unnecessary flushes from http_protocol. [Dean Gaudet] + + *) Added description of "." mode in server-status [Jim Jagielski] + + +Changes with Apache 1.2b4: + + *) Fix possible race condition in accept_mutex_init() that + could leave a small security hole open allowing files to be + overwritten in cases where the server UID has write permissions. + [Marc Slemko] + + *) Fix awk compatibilty problem in Configure. [Jim Jagielski] + + *) Fix portablity problem in util_script where ARG_MAX may not be + defined for some systems. + + *) Add changes to allow compilation on Machten 4.0.3 for PowerPC. + [Randal Schwartz] + + *) OS/2 changes to support an MMAP style scoreboard file and UNIX + style magic #! token for better script portability. [Garey Smiley] + + *) Fix bug in suexec wrapper introduced in b3 that would cause failed + execution for ~userdir CGI. [Jason Dour] + + *) Fix initgroups() business in suexec wrapper. [Jason Dour] + + *) Fix month off by one in suexec wrapper logging. + +Changes with Apache 1.2b3: + + *) Fix error in mod_cgi which could cause resources not to be properly + freed, or worse. [Dean Gaudet] + + *) Fix find_string() NULL pointer dereference. [Howard Fear] + + *) Add set_flag_slot() at the request of Dirk and others. + [Dirk vanGulik] + + *) Sync mod_rewrite with patch level 10. [Ralf Engelschall] + + *) Add changes to improve the error message given for invalid + ServerName parameters. [Dirk vanGulik] + + *) Add "Authoritative" directive for Auth modules that don't + currently have it. This gives admin control to assign authoritative + control to an authentication scheme and allow "fall through" for + those authentication modules that aren't "Authoritative" thereby + allowing multiple authentication mechanisms to be chained. + [Dirk vanGulik] + + *) Remove requirement for ResourceConfig/AccessConfig if not using + the three config file layout. [Randy Terbush] + + *) Add PASV mode to mod_proxy FTP handler. [Chuck Murcko] + + *) Changes to suexec wrapper to fix the following problems: + 1. symlinked homedirs will kill ~userdirs. + 2. initgroups() on Linux 2.0.x clobbers gr->grid. + 3. CGI command lines paramters problems + 4. pw-pwdir for "docroot check" still the httpd user's pw record. + [Randy Terbush, Jason Dour] + + *) Change create_argv() to accept variable arguments. This fixes + a problem where arguments were not getting passed to the CGI via + argv[] when the suexec wrapper was active. [Randy Terbush, Jake Buchholz] + + *) Collapse multiple slashes in path URLs to properly apply + handlers defined by <Location>. [Alexei Kosut] + + *) Define a sane set of DEFAULT_USER and DEFAULT_GROUP values for AIX. + + *) Improve the accuracy of request duration timings by setting + r->request_time in read_request_line() instead of read_request(). + [Dean Gaudet] + + *) Reset timeout while reading via get_client_block() in mod_cgi.c + Fixes problem with timed out transfers of large files. [Rasmus Lerdorf] + + *) Add the ability to pass different Makefile.tmpl files to Configure + using the -make flag. [Rob Hartill] + + *) Fix coredump triggered when sending a SIGHUP to the server caused + by an assertion failure, in turn caused by an uninitialised field in a + listen_rec. + [Ben Laurie] + + *) Add FILEPATH_INFO variable to CGI environment, which is equal to + PATH_INFO from previous versions of Apache (in certain situations, + Apache 1.2's PATH_INFO will be different than 1.1's). [Alexei Kosut] + [later removed in 1.2b11] + + *) Add rwrite() function to API to allow for sending strings of + arbitrary length. [Doug MacEachern] + + *) Remove rlim_t typedef for NetBSD. Do older versions need this? + + *) Defined rlim_t and WANTHSREGEX=yes and fixed waitpid() substitute for + NeXT. [Jim Jagielski] + + *) Removed recent modification to promote the status code on internal + redirects, since the correct fix was to change the default log format + in mod_log_config so that it outputs the original status. [Rob Hartill] + +Changes with Apache 1.2b2: + + *) Update set_signals() to use sigaction() for setting handlers. + This appears to fix a re-entrant problem in the seg_fault() + bus_error() handlers. [Randy Terbush] + + *) Changes to allow mod_status compile for OS/2 [Garey Smiley] + + *) changes for DEC AXP running OSF/1 v3.0. [Marc Evans] + + *) proxy_http.c bugfixes: [Chuck Murcko] + 1) fixes possible NULL pointer reference w/NoCache + 2) fixes NoCache behavior when using ProxyRemote (ProxyRemote + host would cache nothing if it was in the local domain, + and the local domain was in the NoCache list) + 3) Adds Host: header when not available + 4) Some code cleanup and clarification + + *) mod_include.c bugfixes: + 1) Fixed an ommission that caused include variables to not + be parsed in config errmsg directives [Howard Fear] + 2) Remove HAVE_POSIX_REGEX cruft [Alexei Kosut] + 3) Patch to fix compiler warnings [perrot@lal.in2p3.fr] + 4) Allow backslash-escaping to all quoted text + [Ben Yoshino <ben@wiliki.eng.hawaii.edu>] + 5) Pass variable to command line if not set in XSSI's env + [Howard Fear] + + *) Fix infinite loop when processing Content-language lines in + type-map files. [Alexei Kosut] + + *) Closed file-globbing hole in test-cgi script. [Brian Behlendorf] + + *) Fixed problem in set_[user|group] that prevented CGI execution + for non-virtualhosts when suEXEC was enabled. [Randy Terbush] + + *) Added PORTING information file. [Jim Jagielski] + + *) Added definitions for S_IWGRP and S_IWOTH to conf.h [Ben Laurie] + + *) Changed default group to "nogroup" instead of "nobody" [Randy Terbush] + + *) Fixed define typo of FCNTL_SERIALIZED_ACCEPT where + USE_FCNTL_SERIALIZED_ACCEPT was intended. + + *) Fixed additional uses of 0xffffffff where INADDR_NONE was intended, + which caused problems of systems where socket s_addr is >32bits. + + *) Added comment to explain (r->chunked = 1) side-effect in + http_protocol.c [Roy Fielding] + + *) Replaced use of index() in mod_expires.c with more appropriate + and portable isdigit() test. [Ben Laurie] + + *) Updated Configure for ... + OS/2 (DEF_WANTHSREGEX=yes, other code changes) + *-dg-dgux* (bad pattern match) + QNX (DEF_WANTHSREGEX=yes) + *-sunos4* (DEF_WANTHSREGEX=yes, -DUSEBCOPY) + *-ultrix (new) + *-unixware211 (new) + and added some user diagnostic info. [Ben Laurie] + + *) In helpers/CutRule, replaced "cut" invocation with "awk" invocation + for better portability. [Jim Jagielski] + + *) Updated helpers/GuessOS for ... + SCO 5 (recognize minor releases) + SCO Unixware (braindamaged uname, whatever-whatever-unixware2) + SCO UnixWare 2.1.1 (requires a separate set of #defines in conf.h) + IRIX64 (-sgi-irix64) + ULTRIX (-unknown-ultrix) + SINIX (-whatever-sysv4) + NCR Unix (-ncr-sysv4) + and fixed something in helpers/PrintPath [Ben Laurie] + +Changes with Apache 1.2b1: + + *) Not listed. See <http://www.apache.org/docs/new_features_1_2.html> + +Changes with Apache 1.1.1: + + *) Fixed bug where Cookie module would make two entries in the + logfile for each access [Mark Cox] + + *) Fixed bug where Redirect in .htaccess files would cause memory + leak. [Nathan Neulinger] + + *) MultiViews now works correctly with AddHandler [Alexei Kosut] + + *) Problems with mod_auth_msql fixed [Dirk vanGulik] + + *) Fix misspelling of "Anonymous_Authorative" directive in mod_auth_anon. + +Changes with Apache 1.1.0: + + *) Bring NeXT support up to date. [Takaaki Matsumoto] + + *) Bring QNX support up to date. [Ben Laurie] + + *) Make virtual hosts default to main server keepalive parameters. + [Alexei Kosut, Ben Laurie] + + *) Allow ScanHTMLTitles to work with lowercase <title> tags. [Alexei Kosut] + + *) Fix missing address family for connect, also remove unreachable statement + in mod_proxy. [Ben Laurie] + + *) mod_env now turned on by default in Configuration.tmpl. + + *) Bugs which were fixed: + a) yet more mod_proxy bugs [Ben Laurie] + b) CGI works again with inetd [Alexei Kosut] + c) Leading colons were stripped from passwords [osm@interguide.com] + d) Another fix to multi-method Limit problem [jk@tools.de] + +Changes with Apache 1.1b4: + + *) r->bytes_sent variable restored. [Robert Thau] + + *) Previously broken multi-method <Limit> parsing fixed. [Robert Thau] + + *) More possibly unsecure programs removed from the support directory. + + *) More mod_auth_msql authentication improvements. + + *) VirtualHosts based on Host: headers no longer conflict with the + Listen directive. + + *) OS/2 compatibility enhancements. [Gary Smiley] + + *) POST now allowed to directory index CGI scripts. + + *) Actions now work with files of the default type. + + *) Bugs which were fixed: + a) more mod_proxy bugs + b) early termination of inetd requests + c) compile warnings on several systems + d) problems when scripts stop reading output early + +Changes with Apache 1.1b3: + + *) Much of cgi-bin and all of cgi-src has been removed, due to + various security holes found and that we could no longer support + them. + + *) The "Set-Cookie" header is now special-cased to not merge multiple + instances, since certain popular browsers can not handle multiple + Set-Cookie instructions in a single header. [Paul Sutton] + + *) rprintf() added to buffer code, occurrences of sprintf removed. + [Ben Laurie] + + *) CONNECT method for proxy module, which means tunneling SSL should work. + (No crypto needed) Also a NoCache config directive. + + *) Several API additions: pstrndup(), table_unset() and get_token() + functions now available to modules. + + *) mod_imap fixups, in particular Location: headers are now complete + URL's. + + *) New "info" module which reports on installed module set through a + special URL, a la mod_status. + + *) "ServerPath" directive added - allows for graceful transition + for Host:-header-based virtual hosts. + + *) Anonymous authentication module improvements. + + *) MSQL authentication module improvements. + + *) Status module design improved - output now table-based. [Ben Laurie] + + *) htdigest utility included for use with digest authentication + module. + + *) mod_negotiation: Accept values with wildcards to be treated with + less priority than those without wildcards at the same quality + value. [Alexei Kosut] + + *) Bugs which were fixed: + a) numerous mod_proxy bugs + b) CGI early-termination bug [Ben Laurie] + c) Keepalives not working with virtual hosts + d) RefererIgnore problems + e) closing fd's twice in mod_include (causing core dumps on + Linux and elsewhere). + +Changes with Apache 1.1b2: + + *) Bugfixes: + a) core dumps in mod_digest + b) truncated hostnames/ip address in the logs + c) relative URL's in mod_imap map files + +Changes with Apache 1.1b1: + + *) Not listed. See <http://www.apache.org/docs/new_features_1_1.html> + +Changes with Apache 1.0.3: + + *) Internal redirects which occur in mod_dir.c now preserve the + query portion of a request (the bit after the question mark). + [Adam Sussman] + + *) Escape active characters '<', '>' and '&' in html output in + directory listings, error messages and redirection links. + [David Robinson] + + *) Apache will now work with LynxOS 2.3 and later [Steven Watt] + + *) Fix for POSIX compliance in waiting for processes in alloc.c. + [Nick Williams] + + *) setsockopt no longer takes a const declared argument [Martijn Koster] + + *) Reset timeout timer after each successful fwrite() to the network. + This patch adds a reset_timeout() procedure that is called by + send_fd() to reset the timeout ever time data is written to the net. + [Nathan Schrenk] + + *) timeout() signal handler now checks for SIGPIPE and reports + lost connections in a more user friendly way. [Rob Hartill] + + *) Location of the "scoreboard" file which used to live in /tmp is + now configurable (for OSes that can't use mmap) via ScoreBoardFile + which works similar to PidFile (in httpd.conf) [Rob Hartill] + + *) Include sys/resource.h in the correct place for SunOS4 [Sameer Parekh] + + *) the pstrcat call in mod_cookies.c didn't have an ending NULL, + which caused a SEGV with cookies enabled + + *) Output warning when MinSpareServers is set to <= 0 and change it to 1 + [Rob Hartill] + + *) Log the UNIX textual error returned by some system calls, in + particular errors from accept() [David Robinson] + + *) Add strerror function to util.c for SunOS4 [Randy Terbush] + +Changes with Apache 1.0.2 + + *) patch to get Apache compiled on UnixWare 2.x, recommended as + a temporary measure, pending rewrite of rfc931.c. [Chuck Murcko] + + *) Fix get_basic_auth_pw() to set the auth_type of the request. + [David Robinson] + + *) past changes to http_config.c to only use the + setrlimit function on systems defining RLIMIT_NOFILE + broke the feature on SUNOS4. Now defines HAVE_RESOURCE + for SUNOS and prototypes the needed functions. + + *) Remove uses of MAX_STRING_LEN/HUGE_STRING_LEN from several routines. + [David Robinson] + + *) Fix use of pointer to scratch memory. [Cliff Skolnick] + + *) Merge multiple headers from CGI scripts instead of taking last + one. [David Robinson] + + *) Add support for SCO 5. [Ben Laurie] + +Changes with Apache 1.0.1 + + *) Silence mod_log_referer and mod_log_agent if not configured + [Randy Terbush] + + *) Recursive includes can occur if the client supplies PATH_INFO data + and the server provider uses relative links; as file.html + relative to /doc.shtml/pathinfo is /doc.shtml/file.html. [David Robinson] + + *) The replacement for initgroups() did not call {set,end}grent(). This + had two implications: if anything else used getgrent(), then + initgroups() would fail, and it was consuming a file descriptor. + [Ben Laurie] + + *) On heavily loaded servers it was possible for the scoreboard to get + out of sync with reality, as a result of a race condition. + The observed symptoms are far more Apaches running than should + be, and heavy system loads, generally followed by catastrophic + system failure. [Ben Laurie] + + *) Fix typo in license. [David Robinson] + +Changes with Apache 1.0.0 + + *) Not listed. See <http://www.apache.org/docs/new_features_1_0.html> + +Changes with Apache 0.8.16 + + *) New man page for 'httpd' added to support directory [David Robinson] + + *) .htgroup files can have more than one line giving members for a + given group (each must have the group name in front), for NCSA + back-compatibility [Robert Thau] + + *) Mutual exclusion around accept() is on by default for SVR4 systems + generally, since they generally can't handle multiple processes in + accept() on the same socket. This should cure flaky behavior on + a lot of those systems. [David Robinson] + + *) AddType, AddEncoding, and AddLanguage directives take multiple + extensions on a single command line [David Robinson] + + *) UserDir can be disabled for a given virtual host by saying + "UserDir disabled" in the <VirtualHost> section --- it was a bug + that this didn't work. [David Robinson] + + *) Compiles on QNX [Ben Laurie] + + *) Corrected parsing of ctime time format [David Robinson] + + *) httpd does a perror() before exiting if it can't log its pid + to the PidFile, to make diagnosing the error a bit easier. + [David Robinson] + + *) <!--#include file="..."--> can no longer include files in the + parent directory, for NCSA back-compatibility. [David Robinson] + + *) '~' is *not* escaped in URIs generated for directory listings + [Roy Fielding] + + *) Eliminated compiler warning in the imagemap module [Randy Terbush] + + *) Fixed bug involving handling URIs with escaped %-characters + in redirects [David Robinson] + +Changes with Apache 0.8.15 + + *) Switched to new, simpler license + + *) Eliminated core dumps with improperly formatted DBM group files [Mark Cox] + + *) Don't allow requests for ordinary files to have PATH_INFO [Ben Laurie] + + *) Reject paths containing %-escaped '%' or null characters [David Robinson] + + *) Correctly handles internal redirects to files with names containing '%' + [David Robinson] + + *) Repunctuated some error messages [Aram Mirzadeh, Andrew Wilson] + + *) Use geteuid() rather than getuid() to see if we have root privilege, + so that server correctly resets privilege if run setuid root. [Andrew + Wilson] + + *) Handle ftp: and telnet: URLs correctly in imagemaps (built-in module) + [Randy Terbush] + + *) Fix relative URLs in imagemap files [Randy Terbush] + + *) Somewhat better fix for the old "Alias /foo/ /bar/" business + [David Robinson] + + *) Don't repeatedly open the ErrorLog if a bunch of <VirtualHost> + entries all name the same one. [David Robinson] + + *) Fix directory listings with filenames containing unusual characters + [David Robinson] + + *) Better URI-escaping for generated URIs in directories with filenames + containing unusual characters [Ben Laurie] + + *) Fixed potential FILE* leak in http_main.c [Ben Laurie] + + *) Unblock alarms on error return from spawn_child() [David Robinson] + + *) Sample Config files have extra note for SCO users [Ben Laurie] + + *) Configuration has note for HP-UX users [Rob Hartill] + + *) Eliminated some bogus Linux-only #defines in conf.h [Aram Mirzadeh] + + *) Nuked bogus #define in httpd.h [David Robinson] + + *) Better test for whether a system has setrlimit() [David Robinson] + + *) Calls update_child_status() after reopen_scoreboard() [David Robinson] + + *) Doesn't send itself SIGHUP on startup when run in the -X debug-only mode + [Ben Laurie] + +Changes with Apache 0.8.14 + + *) Compiles on SCO ODT 3.0 [Ben Laurie] + + *) AddDescription works (better) [Ben Laurie] + + *) Leaves an intelligible error diagnostic when it can't set group + privileges on standalone startup [Andrew Wilson] + + *) Compiles on NeXT again --- the 0.8.13 RLIMIT patch was failing on + that machine, which claims to be BSD but does not support RLIMIT. + [Randy Terbush] + + *) gcc -Wall no longer complains about an unused variable when util.c + is compiled with -DMINIMAL_DNS [Andrew Wilson] + + *) Nuked another compiler warning for -Wall on Linux [Aram Mirzadeh] + +Changes with Apache 0.8.13 + + *) Make IndexIgnore *work* (ooops) [Jarkko Torppa] + + *) Have built-in imagemap code recognize & honor Point directive [James + Cloos] + + *) Generate cleaner directory listings in directories with a mix of + long and short filenames [Rob Hartill] + + *) Properly initialize dynamically loaded modules [Royston Shufflebotham] + + *) Properly default ServerName for virtual servers [Robert Thau] + + *) Rationalize handling of BSD in conf.h and elsewhere [Randy Terbush, + Paul Richards and a cast of thousands...] + + *) On self-identified BSD systems (we don't try to guess any more), + allocate a few extra file descriptors per virtual host with setrlimit, + if we can, to avoid running out. [Randy Terbush] + + *) Write 22-character lock file name into buffer with enough space + on startup [Konstantin Olchanski] + + *) Use archaic setpgrp() interface on NeXT, which requires it [Brian + Pinkerton] + + *) Suppress -Wall warning by casting const away in util.c [Aram Mirzadeh] + + *) Suppress -Wall warning by initializing variable in negotiation code + [Tobias Weingartner] + +Changes with Apache 0.8.12 + + *) Doesn't pause three seconds after including a CGI script which is + too slow to die off (this is done by not even trying to kill off + subprocesses, including the SIGTERM/pause/SIGKILL routine, until + after the entire document has been processed). [Robert Thau] + + *) Doesn't do SSI if Options Includes is off. (Ooops). [David Robinson] + + *) Options IncludesNoExec allows inclusion of at least text/* [Roy Fielding] + + *) Allows .htaccess files to override <Directory> sections naming the + same directory [David Robinson] + + *) Removed an efficiency hack in sub_req_lookup_uri which was + causing certain extremely marginal cases (e.g., ScriptAlias of a + *particular* index.html file) to fail. [David Robinson] + + *) Doesn't log an error when the requested URI requires + authentication, but no auth header line was supplied by the + client; this is a normal condition (the client doesn't no auth is + needed here yet). [Robert Thau] + + *) Behaves more sanely when the name server loses its mind [Sean Welch] + + *) RFC931 code compiles cleanly on old BSDI releases [Randy Terbush] + + *) RFC931 code no longer passes out name of prior clients on current + requests if the current request came from a server that doesn't + do RFC931. [David Robinson] + + *) Configuration script accepts "Module" lines with trailing whitespace. + [Robert Thau] + + *) Cleaned up compiler warning from mod_access.c [Robert Thau] + + *) Cleaned up comments in mod_cgi.c [Robert Thau] + +Changes with Apache 0.8.11 + + *) Wildcard <Directory> specifications work. [Robert Thau] + + *) Doesn't loop for buggy CGI on Solaris [Cliff Skolnick] + + *) Symlink checks (FollowSymLinks off, or SymLinkIfOwnerMatch) always check + the file being requested itself, in addition to the directories leading + up to it. [Robert Thau] + + *) Logs access failures due to symlink checks or invalid client address + in the error log [Roy Fielding, Robert Thau] + + *) Symlink checks deal correctly with systems where lstat of + "/path/to/some/link/" follows the link. [Thau, Fielding] + + *) Doesn't reset DirectoryIndex to 'index.html' when + other directory options are set in a .htaccess file. [Robert Thau] + + *) Clarified init code and nuked bogus warning in mod_access.c + [Florent Guillaume] + + *) Corrected several directives in sample srm.conf + --- includes corrections to directory indexing icon-related directives + (using unknown.gif rather than unknown.xbm as the DefaultIcon, doing + icons for encodings right, and turning on AddEncoding by default). + [Roy Fielding] + + *) Corrected descriptions of args to AddIcon and AddAlt in command table + [James Cloos] + + *) INSTALL & README mention "contributed modules" directory [Brian + Behlendorf] + + *) Fixed English in the license language... "for for" --> "for". + [Roy Fielding] + + *) Fixed ScriptAlias/Alias interaction by moving ScriptAlias handling to + mod_alias.c, merging it almost completely with handling of Alias, and + adding a 'notes' field to the request_rec which allows the CGI module + to discover whether the Alias module has put this request through + ScriptAlias (which it needs to know for back-compatibility, as the old + NCSA code did not check Options ExecCGI in ScriptAlias directories). + [Robert Thau] + + +Changes with Apache 0.8.10 + + *) AllowOverride applies to the named directory, and not just + subdirectories. [David Robinson] + + *) Do locking for accept() exclusion (on systems that need it) + using a special file created for the purpose in /usr/tmp, and + not the error log; using the error log causes real problems + if it's NFS-mounted; this is known to be the cause of a whole + lot of "server hang" problems with Solaris. [David Robinson; + thanks to Merten Schumann for help diagnosing the problem]. + +Changes with Apache 0.8.9 + + *) Compiles with -DMAXIMUM_DNS ---- ooops! [Henrik Mortensen] + + *) Nested includes see environment variables of the including document, + for NCSA bug-compatibility (some sites have standard footer includes + which try to print out the last-modified date). [Eric Hagberg/Robert + Thau] + + *) <!--exec cgi="/some/uri/here"--> always treats the item named by the + URI as a CGI script, even if it would have been treated as something + else if requested directly, for NCSA back-compatibility. (Note that + this means that people who know the name of the script can see the + code just by asking for it). [Robert Thau] + + *) New version of dbmmanage script included in support directory as + dbmmanage.new. + + *) Check if scoreboard file couldn't be opened, and say so, rather + then going insane [David Robinson] + + *) POST to CGI works on A/UX [Jim Jagielski] + + *) AddIcon and AddAlt commands work properly [Rob Hartill] + + *) NCSA server push works properly --- the Arena bug compatibility + workaround, which broke it, is gone (use -DARENA_BUG_WORKAROUND + if you still want the workaround). [Rob Hartill] + + *) If client didn't submit any Accept-encodings, ignore encodings in + content negotiation. (NB this will all have to be reworked anyway + for the new HTTP draft). [Florent Guillaume] + + *) Don't dump core when trying to log timed-out requests [Jim Jagielski] + + *) Really honor CacheNegotiatedDocs [Florent Guillaume] + + *) Give Redirect priority over Alias, for NCSA bug compatibility + [David Robinson] + + *) Correctly set PATH_TRANSLATED in all cases from <!--#exec cmd=""-->, + paralleling earlier bug fix for CGI [David Robinson] + + *) If DBM auth is improperly configured, report a server error and don't + dump core. + + *) Deleted FCNTL_SERIALIZED_ACCEPTS from conf.h entry for A/UX; + it seems to work well enough without it (even in a 10 hits/sec + workout), and the overhead for the locking under A/UX is + alarmingly high (though it is very low on other systems). + [Eric Hagberg] + + *) Fixed portability problems with mod_cookies.c [Cliff Skolnick] + + *) Further de-Berklize mod_cookies.c; change the bogus #include. [Brian + Behlendorf/Eric Hagberg] + + *) More improvements to default Configuration for A/UX [Jim Jagielski] + + *) Compiles clean on NEXT [Rob Hartill] + + *) Compiles clean on SGI [Robert Thau] + +Changes with Apache 0.8.8 + + *) SunOS library prototypes now never included unless explicitly + requested in the configuration (via -DSUNOS_LIB_PROTOTYPES); + people using GNU libc on SunOS are screwed by prototypes for the + standard library. + + (Those who wish to compile clean with gcc -Wall on a standard + SunOS setup need the prototypes, and may obtain them using + -DSUNOS_LIB_PROTOTYPES. Those wishing to use -Wall on a system + with nonstandard libraries are presumably competent to make their + own arrangements). + + *) Strips trailing '/' characters off both args to the Alias command, + to make 'Alias /foo/ /bar/' work. + +Changes with Apache 0.8.7 + + *) Don't hang when restarting with a child from 'TransferLog "|..."' running + [reported by David Robinson] + + *) Compiles clean on OSF/1 [David Robinson] + + *) Added some of the more recent significant changes (AddLanguage stuff, + experimental LogFormat support) to CHANGES file in distribution root + directory + +Changes with Apache 0.8.6 + + *) Deleted Netscape reload workaround --- it's in violation of HTTP specs. + (If you actually wanted a conditional GET which bypassed the cache, you + couldn't get it). [Reported by Roy Fielding] + + *) Properly terminate headers on '304 Not Modified' replies to conditional + GETs --- no browser we can find cares much, but the CERN proxy chokes. + [Reported by Cliff Skolnick; fix discovered independently by Rob Hartill] + + *) httpd -v doesn't call itself "Shambhala". [Reported by Chuck Murcko] + + *) SunOS lib-function prototypes in conf.h conditionalized on __GNUC__, + not __SUNPRO_C (they're needed to quiet gcc -Wall, but acc chokes on 'em, + and older versions don't set the __SUNPRO_C preprocessor variable). On + all other systems, these are never used anyway. [Reported by Mark Cox]. + + *) Scoreboard file (/tmp/htstatus.*) no longer publically writable. + +Changes with Apache 0.8.5 + + *) Added last-minute configurable log experiment, as optional module + + *) Correctly set r->bytes_sent for HTTP/0.9 requests, so they get logged + properly. (One-line fix to http_protocol.c). + + *) Work around bogus behavior when reloading from Netscape. + It's Netscape's bug --- for some reason they expect a request with + If-modified-since: to not function as a conditional GET if it also + comes with Pragma: no-cache, which is way out of line with the HTTP + spec (according to Roy Fielding, the redactor). + + *) Added parameter to set maximum number of server processes. + + *) Added patches to make it work on A/UX. A/UX is *weird*. [Eric Hagberg] + + *) IdentityCheck bugfix [Chuck Murcko]. + + *) Corrected cgi-src/Makefile entry for new imagemap script. [Alexei Kosut] + + *) More sample config file corrections; add extension to AddType for + *.asis, move AddType generic description to its proper place, and + fix miscellaneous typos. [ Alexei Kosut ] + + *) Deleted the *other* reference to the regents from the Berkeley + legal disclaimer (everyplace). + + *) Nuked Shambhala name from src/README; had already cleaned it out + of everywhere else. + +Changes with Apache 0.8.4 + + *) Changes to server-pool management parms --- renamed current + StartServers to MinSpareServers, created separate StartServers + parameter which means what it says, and renamed MaxServers to + MaxSpareServers (though the old name still works, for NCSA 1.4 + back-compatibility). The old names were generally regarded as + too confusing. Also altered "docs" in sample config files. + + *) More improvements to default config files --- + sample directives (commented out) for XBitHack, BindAddress, + CacheNegotiatedDocs, VirtualHost; decent set of AddLanguage + defaults, AddTypes for send-as-is and imagemap magic types, and + improvements to samples for DirectoryIndex [Alexei Kosut] + + *) Yet more improvements to default config files --- changes to + Alexei's sample AddLanguage directives, and sample LanguagePriority + [ Florent Guillaume ] + + *) Set config file locations properly if not set in httpd.conf + [ David Robinson ] + + *) Don't escape URIs in internal redirects multiple times; don't + do that when translating PATH_INFO to PATH_TRANSLATED either. + [ David Robinson ] + + *) Corrected spelling of "Required" in 401 error reports [Andrew Wilson] + +Changes with Apache 0.8.3 + + *) Edited distribution README to *briefly* summarize installation + procedures, and give a pointer to the INSTALL file in the src/ + directory. + + *) Upgraded imagemap script in cgi-bin to 1.8 version from more + recent NCSA distributions. + + *) Bug fix to previous bug fix --- if .htaccess file and <Directory> + exist for the same directory, use both and don't segfault. [Reported + by David Robinson] + + *) Proper makefile dependencies [David Robinson] + + *) Note (re)starts in error log --- reported by Rob Hartill. + + *) Only call no2slash() after get_path_info() has been done, to + preserve multiple slashes in the PATH_INFO [NCSA compatibility, + reported by Andrew Wilson, though this one is probably a real bug] + + *) Fixed mod_imap.c --- relative paths with base_uri referer don't + dump core when Referer is not supplied. [Randy Terbush] + + *) Lightly edited sample config files to refer people to our documentation + instead of NCSA's, and to list Rob McCool as *original* author (also + deleted his old, and no doubt non-functional email address). Would be + nice to have examples of new features... + +Changes with Apache 0.8.2 + + *) Added AddLanuage code [Florent Guillaume] + + *) Don't say "access forbidden" when a CGI script is not found. [Mark Cox] + + *) All sorts of problems when MultiViews finds a directory. It would + be nice if mod_dir.c was robust enough to handle that, but for now, + just punt. [reported by Brian Behlendorf] + + *) Wait for all children on restart, to make sure that the old socket + is gone and we can reopen it. [reported by Randy Terbush] + + *) Imagemap module is enabled in default Configuration + + *) RefererLog and UserAgentLog modules properly default the logfile + [Randy Terbush] + + *) Mark Cox's mod_cookies added to the distribution as an optional + module (commented out in the default Configuration, and noted as + an experiment, along with mod_dld). [Mark Cox] + + *) Compiles on Ultrix (a continuing battle...). [Robert Thau] + + *) Fixed nasty bug in SIGTERM handling [reported by Randy Terbush] + + *) Changed "Shambhala" to "Apache" in API docs. [Robert Thau] + + *) Added new, toothier legal disclaimer. [Robert Thau; copied from BSD + license] + +Changes with Apache 0.8.1 + + *) New imagemap module [Randy Terbush] + + *) Replacement referer log module with NCSA-compatible RefererIgnore + [Matthew Gray again] + + *) Don't mung directory listings with very long filenames. + [Florent Guillaume] + +Changes with Apache 0.8.0 (nee Shambhala 0.6.2): + + *) New config script. See INSTALL for info. [Robert Thau] + + *) Scoreboard mechanism for regulating the number of extant server + processes. MaxServers and StartServers defaults are the same as + for NCSA, but the meanings are slightly different. (Actually, + I should probably lower the MaxServers default to 10). + + Before asking for a new connection, each server process checks + the number of other servers which are also waiting for a + connection. If there are more than MaxServers, it quietly dies + off. Conversely, every second, the root, or caretaker, process + looks to see how many servers are waiting for a new connection; + if there are fewer than StartServers, it starts a new one. This + does not depend on the number of server processes already extant. + The accounting is arranged through a "scoreboard" file, named + /tmp/htstatus.*, on which each process has an independent file + descriptor (they need to seek without interference). + + The end effect is that MaxServers is the maximum number of + servers on an *inactive* server machine, but more will be forked + off to handle unusually heavy loads (or unusually slow clients); + these will die off when they are no longer needed --- without + reverting to the overhead of full forking operation. There is a + hard maximum of 150 server processes compiled in, largely to + avoid forking out of control and dragging the machine down. + (This is arguably too high). + + In my server endurance tests, this mechanism did not appear to + impose any significant overhead, even after I forced it to put the + scoreboard file on a normal filesystem (which might have more + overhead than tmpfs). [Robert Thau] + + *) Set HTTP_FOO variables for SSI <!--#exec cmd-->s, not just CGI scripts. + [Cliff Skolnick] + + *) Read .htaccess files even in directory with <Directory> section. + (Former incompatibility noted on mailing list, now fixed). [Robert + Thau] + + *) "HEAD /" gives the client a "Bad Request" error message, rather + than trying to send no body *and* no headers. [Cliff Skolnick]. + + *) Don't produce double error reports for some very obscure cases + mainly involving auth configuration (the "all modules decline to + handle" case which is a sure sign of a server bug in most cases, + but also happens when authentication is badly misconfigured). + [Robert Thau] + + *) Moved FCNTL_SERIALIZED_ACCEPT defines into conf.h (that's what + it's *for*, and this sort of thing really shouldn't be cluttering + up the Makefile). [Robert Thau] + + *) Incidental code cleanups in http_main.c --- stop dragging + sa_client around; just declare it where used. [Robert Thau] + + *) Another acc-related fix. (It doesn't like const char + in some places...). [Mark Cox] + +Changes with 0.6.1 + + *) Fixed auth_name-related typos in http_core.c [Brian Behlendorf] + Also, fixed auth typo in http_protocol.c unmasked by this fix. + + *) Compiles clean with acc on SunOS [Paul Sutton] + + *) Reordered modules in modules.c so that Redirect takes priority + over ScriptAlias, for NCSA bug-compatibility [Rob Hartill] --- + believe it or not, he has an actual site with a ScriptAlias and + a Redirect declared for the *exact same directory*. Even *my* + compatibility fetish wouldn't motivate me to fix this if the fix + required any effort, but it doesn't, so what the hey. + + *) Fixed to properly default several server_rec fields for virtual + servers from the corresponding fields in the main server_rec. + [Cliff Skolnick --- 'port' was a particular irritant]. + + *) No longer kills off nph- child processes before they are + finished sending output. [Matthew Gray] + +Changes with 0.6.0 + + *) Two styles of timeout --- hard and soft. soft_timeout()s just put + the connection to the client in an "aborted" state, but otherwise + allow whatever handlers are running to clean up. hard_timeout()s + abort the request in progress completely; anything not tied to some + resource pool cleanup will leak. They're still around because I + haven't yet come up with a more elegant way of handling + timeouts when talking to something that isn't the client. The + default_handler and the dir_handler now use soft timeouts, largely + so I can test the feature. [Robert Thau] + + *) TransferLog "| my_postprocessor ..." seems to be there. Note that + the case of log handlers dying prematurely is probably handled VERY + gracelessly at this point, and if the logger stops reading input, + the server will hang. (It is known to correctly restart the + logging process on server restart; this is (should be!) going through + the same SIGTERM/pause/SIGKILL routine used to ding an errant CGI + script). [Robert Thau] + + *) asis files supported (new module). [Robert Thau] + + *) IdentityCheck code is compiled in, but has not been tested. (I + don't know anyone who runs identd). [Robert Thau] + + *) PATH_INFO and PATH_TRANSLATED are not set unless some real PATH_INFO + came in with the request, for NCSA bug-compatibility. [Robert Thau] + + *) Don't leak the DIR * on HEAD request for a directory. [Robert Thau] + + *) Deleted the block_alarms() stuff from dbm_auth; no longer necessary, + as timeouts are not in scope. [Robert Thau] + + *) quoted-string args in config files now handled correctly (doesn't drop + the last character). [Robert Thau; reported by Randy Terbush] + + *) Fixed silly typo in http_main.c which was suddenly fatal in HP-UX. + How the hell did it ever work? [Robert Thau; reported by Rob Hartill] + + *) mod_core.c --- default_type returns DEFAULT_TYPE (the compile-time + default default type); the former default default behavior when all + type-checkers defaulted had been a core dump. [Paul Sutton] + + *) Copy filenames out of the struct dirent when indexing + directories. (On Linux, readdir() returns a pointer to the same + memory area every time). Fix is in mod_dir.c. [Paul Sutton] + +Changes with 0.5.3 [not released] + + *) Default response handler notes "file not found" in the error log, + if the file was not found. [Cliff Skolnick]. + + *) Another Cliff bug --- "GET /~user" now properly redirects (the userdir + code no longer sets up bogus PATH_INFO which fakes out the directory + handler). [Cliff Skolnick] + +Changes with 0.5.2 + + *) Changes to http_main.c --- root server no longer plays silly + games with SIGCHLD, and so now detects and replaces dying + children. Child processes just die on SIGTERM, without taking + the whole process group with them. Potential problem --- if any + child process refuses to die, we hang in restart. + MaxRequestsPerChild may still not work, but it certainly works + better than it did before this! [Robert Thau] + + *) mod_dir.c bug fixes: ReadmeName and HeaderName + work (or work better, at least); over-long description lines + properly terminated. [Mark Cox] + + *) http_request.c now calls unescape_url() more places where it + should [Paul Sutton]. + + *) More directory handling bugs (reported by Cox) + Parent Directory link is now set correctly. [Robert Thau] + +Changes with 0.5.1: [Hopefully complete] 10 Apr 1995 + + *) Generalized cleanup interface in alloc.c --- any function can be + registered with alloc.c as a cleanup for a resource pool; + tracking of files and file descriptors has been reimplemented in + terms of this interface, so I can give it some sort of a test. + [Robert Thau] + + *) More changes in alloc.c --- new cleanup_for_exec() function, + which tracks down and closes all file descriptors which have been + registered with the alloc.c machinery before the server exec()s a + child process for CGI or <!--#exec-->. CGI children now get + started with exactly three file descriptors open. Hopefully, + this cures the problem Rob H. was having with overly persistent + CGI connections. [Robert Thau] + + *) Mutual exclusion around the accept() in child_main() --- this is + required on at least SGI, Solaris and Linux, and is #ifdef'ed in + by default on those systems only (-DFCNTL_SERIALIZED_ACCEPT). + This uses fcntl(F_SETLK,...) on the error log descriptor because + flock() on that descriptor won't work on systems which have BSD + flock() semantics, including (I think) Linux 1.3 and Solaris. + + This does work on SunOS (when the server is idle, only one + process in the pool is waiting on accept()); it *ought* to work + on the other systems. [Robert Thau] + + *) FreeBSD and BSDI portability tweaks [Chuck Murcko] + + *) sizeof(*sa_client) bugfix from [Rob Hartill] + + *) pstrdup(..., NULL) returns NULL, [Randy Terbush] + + *) block_alarms() to avoid leaking the DBM* in dbm auth (this should + be unnecessary if I go to the revised timeout-handling scheme). + [Robert Thau] + + *) For NCSA bug-compatibility, set QUERY_STRING env var (to a null + string) even if none came in with the request. [Robert Thau] + + *) CHANGES file added to distribution ;-). + +Changes with 0.4 02 Apr 1995 + + *) Patches by Brian Behlendorf, Andrew Wilson, Robert Thau, + and Rob Hartill. + +Changes with 0.3 24 Mar 1995 + + *) Patches by Robert Thau, David Robinson, Rob Hartill, and + Carlos Varela + +Changes with 0.2 18 Mar 1995 + + *) Based on NCSA httpd 1.3 by Rob McCool and patches by CERT, + Roy Fielding, Robert Thau, Nicolas Pioch, David Robinson, + Brian Behlendorf, Rob Hartill, and Cliff Skolnick diff --git a/usr.sbin/httpd/src/Configuration b/usr.sbin/httpd/src/Configuration new file mode 100644 index 00000000000..27baba12831 --- /dev/null +++ b/usr.sbin/httpd/src/Configuration @@ -0,0 +1,292 @@ +# Config file for the Apache httpd. + +# Configuration.tmpl is the template for Configuration. Configuration should +# be edited to select the modules to be included as well as various flags +# for Makefile. + +# The template should only be changed when a new system or module is added, +# or an existing one modified. This will also most likely require some minor +# changes to Configure to recognize those changes. + +# There are 5 types of lines here: + +# '#' comments, distinguished by having a '#' as the first non-blank character +# +# Makefile options, such as CC=gcc, etc... +# +# Rules, distinquished by having "Rule" at the front. These are used to +# control Configure's behavior as far as how to create Makefile. +# +# Module selection lines, distinguished by having 'Module' at the front. +# These list the configured modules, in reverse priority order (lowest +# priority first). They're down at the bottom. +# +# Optional module selection lines, distinguished by having `%Module' +# at the front. These specify a module that is to be compiled in (but +# not enabled). The AddModule directive can be used to enable such a +# module. By default no such modules are defined. + + +################################################################ +# Makefile configuration +# +# These are added to the general flags determined by Configure. +# Edit these to work around Configure if needed. The EXTRA_* family +# will be added to the regular Makefile flags. For example, if you +# want to compile with -Wall, then add that to EXTRA_CFLAGS. These +# will be added to whatever flags Configure determines as appropriate +# and needed for your platform. +# +# You can also set the compiler and Optimization used here as well. +# Settings here have priority; If not set, Configure will attempt to guess +# the C compiler, and set OPTIM to '-O2' +# +# REDHAT LINUX 5.0 USERS PLEASE NOTE! You have to add -lcrypt to +# EXTRA_LIBS. This is fixed in 1.3 but will not be fixed in 1.2. +# +EXTRA_CFLAGS= +EXTRA_LFLAGS= +EXTRA_LIBS= +EXTRA_INCLUDES= + +#CC= +#OPTIM=-O2 +#RANLIB= + +################################################################ +# Rules configuration +# +# These are used to let Configure know that we want certain +# functions. The format is: Rule RULE=value +# +# At present, only the following RULES are known: WANTHSREGEX, SOCKS4, +# STATUS, and IRIXNIS. +# +# For all Rules, if set to "yes", then Configure knows we want that +# capability and does what is required to add it in. If set to "default" +# then Configure makes a "best guess"; if set to anything else, or not +# present, then nothing is done. +# +# SOCKS4: +# If SOCKS4 is set to 'yes', be sure that you add the sock library +# location to EXTRA_LIBS, otherwise Configure will assume +# "-L/usr/local/lib -lsocks" +# +# STATUS: +# If Configure determines that you are using the status_module, +# it will automatically enable full status information if set +# to 'yes'. If the status module is not included, having STATUS +# set to 'yes' has no impact. +# +# IRIXNIS: +# Only takes effect if Configure determines that you are running +# SGI IRIX. If you are, and you are using NIS, you should set this +# to 'yes' +# + +Rule STATUS=yes +Rule SOCKS4=no +Rule IRIXNIS=no + +# The following rules should be set automatically by Configure. However, if +# they are not set by Configure (because we don't know the correct value for +# your platform), or are set incorrectly, you may override them here. +# If you have to do this, please let us know what you set and what your +# platform is, by filling out a problem report form at the Apache web site: +# <http://www.apache.org/bugdb.cgi>. If your browser is forms-incapable, +# you can get the information to us by sending mail to apache-bugs@apache.org. +# +# WANTHSREGEX: +# Apache requires a POSIX regex implementation. Henry Spencer's +# excellent regex package is included with Apache and can be used +# if desired. If your OS has a decent regex, you can elect to +# not use this one by setting WANTHSREGEX to 'no' or commenting +# out the Rule. The "default" action is "no" unless overruled +# by OS specifics + +Rule WANTHSREGEX=default + +################################################################ +# Module configuration +# +# Modules are listed in reverse priority order --- the ones that come +# later can override the behavior of those that come earlier. This +# can have visible effects; for instance, if UserDir followed Alias, +# you couldn't alias out a particular user's home directory. + +# The configuration below is what we consider a decent default +# configuration. If you want the functionality provided by a particular +# module, remove the "#" sign at the beginning of the line. But remember, +# the more modules you compile into the server, the larger the executable +# is and the more memory it will take, so if you are unlikely to use the +# functionality of a particular module you might wish to leave it out. + +## +## Config manipulation modules +## +## mod_env sets up additional or restricted environment variables to be +## passed to CGI/SSI scripts. It is listed first (lowest priority) since +## it does not do per-request stuff. + +Module env_module mod_env.o + +## mod_dld defines commands that allow other modules to be loaded +## dynamically (at runtime). This module is for experimental use only. + +# Module dld_module mod_dld.o + +## +## Request logging modules +## + +Module config_log_module mod_log_config.o + +## Optional modules for NCSA user-agent/referer logging compatibility +## We recommend, however, that you just use the configurable access_log. + +# Module agent_log_module mod_log_agent.o +# Module referer_log_module mod_log_referer.o + +## +## Type checking modules +## +## mod_mime maps filename extensions to content types, encodings, and +## magic type handlers (the latter is obsoleted by mod_actions). +## mod_negotiation allows content selection based on the Accept* headers. + +Module mime_module mod_mime.o +Module negotiation_module mod_negotiation.o + +## +## Content delivery modules +## +## The status module allows the server to display current details about +## how well it is performing and what it is doing. Consider also enabling +## STATUS=yes (see the Rules section near the start of this file) to allow +## full status information. Check conf/access.conf on how to enable this. + +# Module status_module mod_status.o + +## The Info module displays configuration information for the server and +## all included modules. It's very useful for debugging. + +# Module info_module mod_info.o + +## mod_include translates server-side include (SSI) statements in text files. +## mod_dir handles requests on directories and directory indexes. +## mod_cgi handles CGI scripts. + +Module includes_module mod_include.o +Module dir_module mod_dir.o +Module cgi_module mod_cgi.o + +## The asis module implemented ".asis" file types, which allow the embedding +## of HTTP headers at the beginning of the document. mod_imap handles internal +## imagemaps (no more cgi-bin/imagemap/!). mod_actions is used to specify +## CGI scripts which act as "handlers" for particular files, for example to +## automatically convert every GIF to another file type. + +Module asis_module mod_asis.o +Module imap_module mod_imap.o +Module action_module mod_actions.o + +## +## URL translation modules. +## +## The UserDir module for selecting resource directories by user name +## and a common prefix, e.g., /~<user> , /usr/web/<user> , etc. + +Module userdir_module mod_userdir.o + +## The proxy module enables the server to act as a proxy for outside +## http and ftp services. It's not as complete as it could be yet. +## NOTE: You do not want this module UNLESS you are running a proxy; +## it is not needed for normal (origin server) operation. + +#Module proxy_module modules/proxy/libproxy.a + +## The Alias module provides simple URL translation and redirection. + +Module alias_module mod_alias.o + +## mod_rewrite allows for powerful URI-to-URI and URI-to-filename mapping, +## using regular expressions. + +# Module rewrite_module mod_rewrite.o + +## +## Access control and authentication modules. +## +Module access_module mod_access.o +Module auth_module mod_auth.o + +## The anon_auth module allows for anonymous-FTP-style username/ +## password authentication. + +# Module anon_auth_module mod_auth_anon.o + +## db_auth and dbm_auth work with Berkeley DB files - make sure there +## is support for DBM files on your system. You may need to grab the GNU +## "gdbm" package if not and possibly adjust EXTRA_LIBS. (This may be +## done by Configure at a later date) + +Module db_auth_module mod_auth_db.o +Module dbm_auth_module mod_auth_dbm.o + +## msql_auth checks against an mSQL database. You must have mSQL installed +## and an "msql.h" available for this to even compile. Additionally, +## you may need to add a couple entries to the EXTRA_LIBS line, like +## +## -lmsql -L/usr/local/lib -L/usr/local/Minerva/lib +## +## This depends on your installation of mSQL. (This may be done by Configure +## at a later date) + +# Module msql_auth_module mod_auth_msql.o + +## "digest" implements HTTP Digest Authentication rather than the less +## secure Basic Auth used by the other modules. + +# Module digest_module mod_digest.o + +## Optional response header manipulation modules. +## +## cern_meta mimics the behavior of the CERN web server with regards to +## metainformation files. + +# Module cern_meta_module mod_cern_meta.o + +## The expires module can apply Expires: headers to resources, +## as a function of access time or modification time. + +# Module expires_module mod_expires.o + +## The headers module can set arbitrary HTTP response headers, +## as configured in server, vhost, access.conf or .htaccess configs + +# Module headers_module mod_headers.o + +## Miscellaneous modules +## +## mod_usertrack.c is the new name for mod_cookies.c. This module +## uses Netscape cookies to automatically construct and log +## click-trails from Netscape cookies, or compatible clients who +## aren't coming in via proxy. +## +## You do not need this, or any other module to allow your site +## to use Cookies. This module is for user tracking only + +# Module usertrack_module mod_usertrack.o + +## The example module, which demonstrates the use of the API. See +## the file modules/example/README for details. This module should +## only be used for testing -- DO NOT ENABLE IT on a production server. + +# Module example_module modules/example/mod_example.o + +## mod_browser lets you set environment variables based on the User-Agent +## string in the request; this is useful for conditional HTML, for example. +## Since it is also used to detect buggy browsers for workarounds, it +## should be the last (highest priority) module. + +Module browser_module mod_browser.o diff --git a/usr.sbin/httpd/src/Configuration.tmpl b/usr.sbin/httpd/src/Configuration.tmpl new file mode 100644 index 00000000000..178308124ed --- /dev/null +++ b/usr.sbin/httpd/src/Configuration.tmpl @@ -0,0 +1,292 @@ +# Config file for the Apache httpd. + +# Configuration.tmpl is the template for Configuration. Configuration should +# be edited to select the modules to be included as well as various flags +# for Makefile. + +# The template should only be changed when a new system or module is added, +# or an existing one modified. This will also most likely require some minor +# changes to Configure to recognize those changes. + +# There are 5 types of lines here: + +# '#' comments, distinguished by having a '#' as the first non-blank character +# +# Makefile options, such as CC=gcc, etc... +# +# Rules, distinquished by having "Rule" at the front. These are used to +# control Configure's behavior as far as how to create Makefile. +# +# Module selection lines, distinguished by having 'Module' at the front. +# These list the configured modules, in reverse priority order (lowest +# priority first). They're down at the bottom. +# +# Optional module selection lines, distinguished by having `%Module' +# at the front. These specify a module that is to be compiled in (but +# not enabled). The AddModule directive can be used to enable such a +# module. By default no such modules are defined. + + +################################################################ +# Makefile configuration +# +# These are added to the general flags determined by Configure. +# Edit these to work around Configure if needed. The EXTRA_* family +# will be added to the regular Makefile flags. For example, if you +# want to compile with -Wall, then add that to EXTRA_CFLAGS. These +# will be added to whatever flags Configure determines as appropriate +# and needed for your platform. +# +# You can also set the compiler and Optimization used here as well. +# Settings here have priority; If not set, Configure will attempt to guess +# the C compiler, and set OPTIM to '-O2' +# +# REDHAT LINUX 5.0 USERS PLEASE NOTE! You have to add -lcrypt to +# EXTRA_LIBS. This is fixed in 1.3 but will not be fixed in 1.2. +# +EXTRA_CFLAGS= +EXTRA_LFLAGS= +EXTRA_LIBS= +EXTRA_INCLUDES= + +#CC= +#OPTIM=-O2 +#RANLIB= + +################################################################ +# Rules configuration +# +# These are used to let Configure know that we want certain +# functions. The format is: Rule RULE=value +# +# At present, only the following RULES are known: WANTHSREGEX, SOCKS4, +# STATUS, and IRIXNIS. +# +# For all Rules, if set to "yes", then Configure knows we want that +# capability and does what is required to add it in. If set to "default" +# then Configure makes a "best guess"; if set to anything else, or not +# present, then nothing is done. +# +# SOCKS4: +# If SOCKS4 is set to 'yes', be sure that you add the sock library +# location to EXTRA_LIBS, otherwise Configure will assume +# "-L/usr/local/lib -lsocks" +# +# STATUS: +# If Configure determines that you are using the status_module, +# it will automatically enable full status information if set +# to 'yes'. If the status module is not included, having STATUS +# set to 'yes' has no impact. +# +# IRIXNIS: +# Only takes effect if Configure determines that you are running +# SGI IRIX. If you are, and you are using NIS, you should set this +# to 'yes' +# + +Rule STATUS=yes +Rule SOCKS4=no +Rule IRIXNIS=no + +# The following rules should be set automatically by Configure. However, if +# they are not set by Configure (because we don't know the correct value for +# your platform), or are set incorrectly, you may override them here. +# If you have to do this, please let us know what you set and what your +# platform is, by filling out a problem report form at the Apache web site: +# <http://www.apache.org/bugdb.cgi>. If your browser is forms-incapable, +# you can get the information to us by sending mail to apache-bugs@apache.org. +# +# WANTHSREGEX: +# Apache requires a POSIX regex implementation. Henry Spencer's +# excellent regex package is included with Apache and can be used +# if desired. If your OS has a decent regex, you can elect to +# not use this one by setting WANTHSREGEX to 'no' or commenting +# out the Rule. The "default" action is "no" unless overruled +# by OS specifics + +Rule WANTHSREGEX=default + +################################################################ +# Module configuration +# +# Modules are listed in reverse priority order --- the ones that come +# later can override the behavior of those that come earlier. This +# can have visible effects; for instance, if UserDir followed Alias, +# you couldn't alias out a particular user's home directory. + +# The configuration below is what we consider a decent default +# configuration. If you want the functionality provided by a particular +# module, remove the "#" sign at the beginning of the line. But remember, +# the more modules you compile into the server, the larger the executable +# is and the more memory it will take, so if you are unlikely to use the +# functionality of a particular module you might wish to leave it out. + +## +## Config manipulation modules +## +## mod_env sets up additional or restricted environment variables to be +## passed to CGI/SSI scripts. It is listed first (lowest priority) since +## it does not do per-request stuff. + +Module env_module mod_env.o + +## mod_dld defines commands that allow other modules to be loaded +## dynamically (at runtime). This module is for experimental use only. + +# Module dld_module mod_dld.o + +## +## Request logging modules +## + +Module config_log_module mod_log_config.o + +## Optional modules for NCSA user-agent/referer logging compatibility +## We recommend, however, that you just use the configurable access_log. + +# Module agent_log_module mod_log_agent.o +# Module referer_log_module mod_log_referer.o + +## +## Type checking modules +## +## mod_mime maps filename extensions to content types, encodings, and +## magic type handlers (the latter is obsoleted by mod_actions). +## mod_negotiation allows content selection based on the Accept* headers. + +Module mime_module mod_mime.o +Module negotiation_module mod_negotiation.o + +## +## Content delivery modules +## +## The status module allows the server to display current details about +## how well it is performing and what it is doing. Consider also enabling +## STATUS=yes (see the Rules section near the start of this file) to allow +## full status information. Check conf/access.conf on how to enable this. + +# Module status_module mod_status.o + +## The Info module displays configuration information for the server and +## all included modules. It's very useful for debugging. + +# Module info_module mod_info.o + +## mod_include translates server-side include (SSI) statements in text files. +## mod_dir handles requests on directories and directory indexes. +## mod_cgi handles CGI scripts. + +Module includes_module mod_include.o +Module dir_module mod_dir.o +Module cgi_module mod_cgi.o + +## The asis module implemented ".asis" file types, which allow the embedding +## of HTTP headers at the beginning of the document. mod_imap handles internal +## imagemaps (no more cgi-bin/imagemap/!). mod_actions is used to specify +## CGI scripts which act as "handlers" for particular files, for example to +## automatically convert every GIF to another file type. + +Module asis_module mod_asis.o +Module imap_module mod_imap.o +Module action_module mod_actions.o + +## +## URL translation modules. +## +## The UserDir module for selecting resource directories by user name +## and a common prefix, e.g., /~<user> , /usr/web/<user> , etc. + +Module userdir_module mod_userdir.o + +## The proxy module enables the server to act as a proxy for outside +## http and ftp services. It's not as complete as it could be yet. +## NOTE: You do not want this module UNLESS you are running a proxy; +## it is not needed for normal (origin server) operation. + +# Module proxy_module modules/proxy/libproxy.a + +## The Alias module provides simple URL translation and redirection. + +Module alias_module mod_alias.o + +## mod_rewrite allows for powerful URI-to-URI and URI-to-filename mapping, +## using regular expressions. + +# Module rewrite_module mod_rewrite.o + +## +## Access control and authentication modules. +## +Module access_module mod_access.o +Module auth_module mod_auth.o + +## The anon_auth module allows for anonymous-FTP-style username/ +## password authentication. + +# Module anon_auth_module mod_auth_anon.o + +## db_auth and dbm_auth work with Berkeley DB files - make sure there +## is support for DBM files on your system. You may need to grab the GNU +## "gdbm" package if not and possibly adjust EXTRA_LIBS. (This may be +## done by Configure at a later date) + +# Module db_auth_module mod_auth_db.o +# Module dbm_auth_module mod_auth_dbm.o + +## msql_auth checks against an mSQL database. You must have mSQL installed +## and an "msql.h" available for this to even compile. Additionally, +## you may need to add a couple entries to the EXTRA_LIBS line, like +## +## -lmsql -L/usr/local/lib -L/usr/local/Minerva/lib +## +## This depends on your installation of mSQL. (This may be done by Configure +## at a later date) + +# Module msql_auth_module mod_auth_msql.o + +## "digest" implements HTTP Digest Authentication rather than the less +## secure Basic Auth used by the other modules. + +# Module digest_module mod_digest.o + +## Optional response header manipulation modules. +## +## cern_meta mimics the behavior of the CERN web server with regards to +## metainformation files. + +# Module cern_meta_module mod_cern_meta.o + +## The expires module can apply Expires: headers to resources, +## as a function of access time or modification time. + +# Module expires_module mod_expires.o + +## The headers module can set arbitrary HTTP response headers, +## as configured in server, vhost, access.conf or .htaccess configs + +# Module headers_module mod_headers.o + +## Miscellaneous modules +## +## mod_usertrack.c is the new name for mod_cookies.c. This module +## uses Netscape cookies to automatically construct and log +## click-trails from Netscape cookies, or compatible clients who +## aren't coming in via proxy. +## +## You do not need this, or any other module to allow your site +## to use Cookies. This module is for user tracking only + +# Module usertrack_module mod_usertrack.o + +## The example module, which demonstrates the use of the API. See +## the file modules/example/README for details. This module should +## only be used for testing -- DO NOT ENABLE IT on a production server. + +# Module example_module modules/example/mod_example.o + +## mod_browser lets you set environment variables based on the User-Agent +## string in the request; this is useful for conditional HTML, for example. +## Since it is also used to detect buggy browsers for workarounds, it +## should be the last (highest priority) module. + +Module browser_module mod_browser.o diff --git a/usr.sbin/httpd/src/Configure b/usr.sbin/httpd/src/Configure new file mode 100644 index 00000000000..9b47e26d157 --- /dev/null +++ b/usr.sbin/httpd/src/Configure @@ -0,0 +1,688 @@ +#!/bin/sh +trap 'rm -f $tmpfile; exit' 0 1 2 3 15 + +# Apache configuration script, first cut --- rst. +# Dont like it? Inspired to do something better? Go for it. + +# second cut --- jmj +# At this point we change what Configuration contains. It maintain +# contains comments, specific compiler flags, a list of included +# modules and "rules". These rules are used to allow Configure to +# be totally configured from Configuration +# +# Uses 3 supplemental scripts located in ./helpers: CutRule, +# GuessOS and PrintPath +# + +file=Configuration +tmpfile=htconf.$$ +makefile_tmpl=Makefile.tmpl + +while [ "x$1" != "x" ]; do + if [ "x$1" = "x-file" ] ; then + shift 1; file=$1; shift 1 + if [ ! -r $file ]; then + echo "$file does not exist or is not readable." + exit 1 + fi + elif [ "x$1" = "x-make" ] ; then + shift 1; makefile_tmpl=$1; shift 1 + if [ ! -r $makefile_tmpl ]; then + echo "$makefile_tmpl does not exist or is not readable." + exit 1 + fi + else + echo "Ignoring command line option '$1'" + shift 1 + fi +done +echo "Using config file: $file" +echo "Using Makefile template file: $makefile_tmpl" + +if [ ! -r $file ]; then + echo "Can't see or read \"$file\"" + exit 1 +fi + +# First, strip comments and blank lines and then change Rules to comments +# and then remove whitespace before Module declarations + +sed 's/#.*//' $file | \ + sed '/^[ ]*$/d' | \ + sed 's/[ ]*$//' | \ + sed 's/^Rule[ ]*/##Rule:/' | \ + sed 's/^[ ]*Module/Module/' | \ + sed 's/^[ ]*%Module/%Module/' > $tmpfile + +# Check for syntax errors... + +if egrep -v '^%?Module[ ]+[A-Za-z0-9_]+[ ]+[^ ]+$' $tmpfile \ + | grep -v = > /dev/null +then + echo "Syntax error --- The configuration file is used only to" + echo "define the list of included modules or to set Makefile" + echo "options or Configure rules, and I don't see that at all:" + egrep -v '^Module[ ]+[A-Za-z0-9_]+[ ]+[^ ]+$' $tmpfile | \ + grep -v = + exit 1 +fi + +# File is OK --- make backup copies of things and then get the new ones: + +if [ -f Makefile ] ; then mv Makefile Makefile.bak; fi +if [ -f modules.c ] ; then mv modules.c modules.c.bak; fi + +sed -e 's/_module//' $tmpfile | awk >modules.c '\ + BEGIN { modules[n++] = "core" ; pmodules[pn++] = "core"} \ + /^Module/ { modules[n++] = $2 ; pmodules[pn++] = $2 } \ + /^%Module/ { pmodules[pn++] = $2 } \ + END { print "/* modules.c --- automatically generated by Apache"; \ + print " * configuration script. DO NOT HAND EDIT!!!!!"; \ + print " */"; \ + print ""; \ + print "#include \"httpd.h\""; \ + print "#include \"http_config.h\""; \ + print ""; \ + for (i = 0; i < pn; ++i) { \ + printf ("extern module %s_module;\n", pmodules[i]); \ + } \ + print ""; \ + print "module *prelinked_modules[] = {"; \ + for (i = 0; i < n; ++i) { \ + printf " &%s_module,\n", modules[i]; \ + } \ + print " NULL"; \ + print "};"; \ + print "module *preloaded_modules[] = {"; \ + for (i = 0; i < pn; ++i) { \ + printf " &%s_module,\n", pmodules[i]; \ + } \ + print " NULL"; \ + print "};"; \ + }' + +# +# Add module set only +# +echo "#" > Makefile +echo "# Makefile automatically generated from $makefile_tmpl" >> Makefile +echo "# and configuration file by Apache config script." >> Makefile +echo "# Hand-edited changes will be lost if the config script" >> Makefile +echo "# is re-run" >> Makefile +echo "#" >> Makefile + +awk >>Makefile <$tmpfile '\ + /^Module/ { modules[n++] = $3 } \ + /^%Module/ { modules[n++] = $3 } \ + END { print "MODULES=\\"; \ + for (i = 0; i < n; ++i) { \ + if (i < n-1) printf (" %s \\\n", modules[i]); \ + else printf (" %s\n", modules[i]); \ + } \ + print "" \ + }' +# +# Now add Makefile additions and Rules +# +awk >>Makefile <$tmpfile '\ + BEGIN { print "# Makefile options inherited from Configure"; \ + print "###############"; \ + } \ + /\=/ { print } \ + END { print "###############"; }' + +# +# Extract the rules. +# +RULE_WANTHSREGEX=`./helpers/CutRule WANTHSREGEX $file` +RULE_STATUS=`./helpers/CutRule STATUS $file` +RULE_SOCKS4=`./helpers/CutRule SOCKS4 $file` +RULE_IRIXNIS=`./helpers/CutRule IRIXNIS $file` + +# +# Now we determine the OS/Platform automagically, thanks to +# GuessOS, a home-brewed OS-determiner ala config.guess +# +# We adjust CFLAGS, LIBS, LFLAGS and INCLUDES (and other Makefile +# options) as required. Setting CC and OPTIM here has no effect +# if they were set in Configure. +# +# Also, we set DEF_WANTHSREGEX and to the appropriate +# value for each platform. +# +# As more PLATFORMs are added to Configuration.tmpl, be sure to +# add the required lines below. +# + +PLAT=`./helpers/GuessOS` + +# Preset DBM_LIB. Can be overridden on a per-platform basis. + +DBM_LIB="-ldbm" + +# +# Look for ranlib. Do it early in case we want to override it below +# +if ./helpers/PrintPath -s ranlib; then + RANLIB="ranlib" +else + RANLIB="true" +fi + +# +# We now look for popular compilers. As with ranlib, we +# do this early because some options may depend +# on which compiler we use/find +# +for compilers in "gcc" "cc" "acc" "c89" +do + lookedfor="$lookedfor $compilers" + if ./helpers/PrintPath -s $compilers; then + COMPILER="$compilers" + break + fi +done + +# +SHELL="/bin/sh" + +case "$PLAT" in + *MPE/iX*) + OS='MPE/iX' + CFLAGS="$CFLAGS -DMPE -D_POSIX_SOURCE -D_SOCKET_SOURCE" + LIBS="$LIBS -lsocket" + LFLAGS="$LFLAGS -Xlinker \"-WL,cap=ia,ba,ph,pm;nmstack=1024000\"" + ;; + *-apple-aux3*) + OS='A/UX 3.1.x' + CFLAGS="$CFLAGS -DAUX -D_POSIX_SOURCE" + LIBS="$LIBS -lposix -lbsd" + LFLAGS="$LFLAGS -s" + DEF_WANTHSREGEX=no + ;; + i386-ibm-aix*) + OS='IBM AIX PS/2' + CFLAGS="$CFLAGS -DAIX -U__STR__ -DUSEBCOPY" + DEF_WANTHSREGEX=no + ;; + *-ibm-aix[1-3].*|*-ibm-aix4.[0-1]) + OS='IBM AIX < v4.2' + CFLAGS="$CFLAGS -DAIX -DNEED_RLIM_T -U__STR__" + ;; + *-ibm-aix*) + OS='IBM AIX >= 4.2' + CFLAGS="$CFLAGS -DAIX -U__STR__" + LFLAGS="$LFLAGS -lm" + ;; + *-apollo-*) + OS='Apollo Domain' + CFLAGS="$CFLAGS -DAPOLLO" + ;; + *-dg-dgux*) + OS='DG/UX 5.4' + CFLAGS="$CFLAGS -DDGUX" + DEF_WANTHSREGEX=yes + ;; + *OS/2*) + DEF_WANTHSREGEX=yes + OS='EMX OS/2' + CFLAGS="$CFLAGS -Zbsd-signals -Zbin-files -DTCPIPV4 -g" + LIBS="$LIBS -lsocket -llibufc -lbsd" + DBM_LIB="-lgdbm" + ;; + *-hi-hiux) + OS='HI-UX' + CFLAGS="$CFLAGS -DHIUX" + # if we're using the HIUX compiler, add a few flags. + if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then + CFLAGS="$CFLAGS -Aa -D_HIUX_SOURCE" + OPTIM=" " + fi + ;; + *-hp-hpux10.*) + OS='HP-UX 10' + CFLAGS="$CFLAGS -DHPUX10" + # if we're using the HPUX compiler, add a few flags. + if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then + CFLAGS="$CFLAGS -Aa -D_HPUX_SOURCE" + OPTIM=" " + fi + ;; + *-hp-hpux*) + OS='HP-UX' + CFLAGS="$CFLAGS -DHPUX" + if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then + CFLAGS="$CFLAGS -Aa -D_HPUX_SOURCE" + OPTIM=" " + fi + LIBS="$LIBS -lm" + ;; + *-sgi-irix64) +# Note: We'd like to see patches to compile 64-bit, but for now... + echo "You are running 64-bit Irix. For now, we will compile 32-bit" + echo "but if you would care to port to 64-bit, send us the patches." + CFLAGS="$CFLAGS -n32" + LFLAGS="$LFLAGS -n32" + DEF_WANTHSREGEX=yes + DBM_LIB="" + if [ "$RULE_IRIXNIS" = "yes" ]; then + OS='SGI IRIX w/NIS' + CFLAGS="$CFLAGS -DIRIX" + LIBS="$LIBS -lsun" + else + OS='SGI IRIX' + CFLAGS="$CFLAGS -DIRIX" + fi + ;; + *-sgi-irix) + DEF_WANTHSREGEX=yes + DBM_LIB="" + if [ "$RULE_IRIXNIS" = "yes" ]; then + OS='SGI IRIX w/NIS' + CFLAGS="$CFLAGS -DIRIX" + LIBS="$LIBS -lsun" + else + OS='SGI IRIX' + CFLAGS="$CFLAGS -DIRIX" + fi + ;; + alpha-*-linux2) + DEF_WANTHSREGEX=yes + OS='Linux' + CFLAGS="$CFLAGS -DLINUX=2" + LIBS="$LIBS -lcrypt" + ;; + sparc-*-linux2) + DEF_WANTHSREGEX=yes + OS='Linux' + CFLAGS="$CFLAGS -DLINUX=2" + LIBS="$LIBS -lm" + ;; + *-linux2) + DEF_WANTHSREGEX=yes + OS='Linux' + CFLAGS="$CFLAGS -DLINUX=2" + ;; + *-linux1) + DEF_WANTHSREGEX=yes + OS='Linux' + CFLAGS="$CFLAGS -DLINUX=1" + ;; + *-lynx-lynxos*) + OS='LynxOS' + CFLAGS="$CFLAGS -DLYNXOS" + LIBS="$LIBS -lbsd -ldes -lc_p" + ;; + *486-*-bsdi*) + OS='BSDI w/486' + CFLAGS="$CFLAGS -m486" + DBM_LIB="" + ;; + *-bsdi*) + OS='BSDI' + DBM_LIB="" + ;; + *486-*-freebsd*|*486-*-netbsd*) + OS='FreeBSD/NETBSD on 486' + LIBS="$LIBS -lcrypt" + DBM_LIB="" + ;; + *-freebsd*|*-netbsd*) + OS='FreeBSD/NetBSD' + LIBS="$LIBS -lcrypt" + DBM_LIB="" + ;; + *-openbsd*) + OS='OpenBSD' + DBM_LIB="" + ;; + *-next-nextstep*) + OS='NeXT' + CFLAGS="$CFLAGS -DNEXT" + DEF_WANTHSREGEX=yes + RANLIB="sleep 5; /bin/ranlib" + # ranlib on most NeXTs sets the time wrong. 5 secs wait does much good + ;; + *-dec-osf*) + OS='DEC OSF/1' + CFLAGS="$CFLAGS -DOSF1" + LIBS="$LIBS -lm" + ;; + *-qnx) + OS='QNX' + CFLAGS="$CFLAGS -DQNX" + LIBS="$LIBS -N128k -lsocket" + DEF_WANTHSREGEX=yes + ;; + *-qnx32) + OS='QNX32' + CFLAGS="$CFLAGS -DQNX -mf -3" + LIBS="$LIBS -N128k -lsocket" + DEF_WANTHSREGEX=yes + ;; + *-isc4*) + OS='ISC 4' + CC='gcc' + CFLAGS="$CFLAGS -posix -DISC" + LFLAGS="$LFLAGS -posix" + LIBS="$LIBS -linet" + DEF_WANTHSREGEX=yes + ;; + *-sco3*) + OS='SCO 3' + CFLAGS="$CFLAGS -DSCO -Oacgiltz" + LIBS="$LIBS -lPW -lsocket -lmalloc -lcrypt_i" + DEF_WANTHSREGEX=yes + ;; + *-sco5*) + OS='SCO 5' + CFLAGS="$CFLAGS -DSCO5" + LIBS="$LIBS -lsocket -lmalloc -lprot" + if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then + OSBPRINTF="-K noinline" + fi + DEF_WANTHSREGEX=no + ;; + *-solaris2*) + OS='Solaris 2' + CFLAGS="$CFLAGS -DSOLARIS2" + LIBS="$LIBS -lsocket -lnsl" + DBM_LIB="" + DEF_WANTHSREGEX=yes + ;; + *-sunos4*) + OS='SunOS 4' + CFLAGS="$CFLAGS -DSUNOS4 -DUSEBCOPY" + DEF_WANTHSREGEX=yes + ;; + *-unixware1) + DEF_WANTHSREGEX=yes + OS='Unixware' + CFLAGS="$CFLAGS -DSVR4 -DNO_LINGCLOSE" + LIBS="$LIBS -lsocket -lnsl -lcrypt" + ;; + *-unixware2) + DEF_WANTHSREGEX=yes + OS='Unixware' + CFLAGS="$CFLAGS -DSVR4 -DNO_LINGCLOSE" + LIBS="$LIBS -lsocket -lnsl -lcrypt" + ;; + *-unixware211) + OS='Unixware 2.1.1' + CFLAGS="$CFLAGS -DUW" + LIBS="$LIBS -lsocket -lnsl -lcrypt" + ;; + *-unixware212) + OS='Unixware 2.1.2' + CFLAGS="$CFLAGS -DUW" + LIBS="$LIBS -lsocket -lnsl -lcrypt" + DBM_LIB="" + ;; + maxion-*-sysv4*) + OS='SVR4' + CFLAGS="$CFLAGS -DSVR4" + DEF_WANTHSREGEX=yes + LIBS="$LIBS -lsocket -lnsl -lc -lgen" + ;; + *-sni-sysv4*) + OS='SVR4' + CFLAGS="$CFLAGS -DSVR4 -D_XPG_IV -DUSE_MMAP_FILES" + DEF_WANTHSREGEX=yes + LIBS="$LIBS -lsocket -lnsl -lc" + ;; + DS/90\ 7000-*-sysv4*) + OS='UXP/DS' + CFLAGS="$CFLAGS -DUXPDS" + LIBS="$LIBS -lsocket -lnsl" + DEF_WANTHSREGEX=yes + ;; + *-tandem-sysv4*) + OS='SVR4' + CFLAGS="$CFLAGS -DSVR4" + LIBS="$LIBS -lsocket -lnsl" + DEF_WANTHSREGEX=yes + ;; + *-sysv4*) + OS='SVR4' + CFLAGS="$CFLAGS -DSVR4" + LIBS="$LIBS -lsocket -lnsl -lc" + ;; + *-uts*) + OS='Amdahl UTS' + CFLAGS="$CFLAGS -Xa -eft -DUTS21" + LIBS="$LIBS -lsocket -lbsd -la" + ;; + *-ultrix) + OS='ULTRIX' + CFLAGS="-DULTRIX" + DEF_WANTHSREGEX=yes + SHELL="/bin/sh5" + if [ "$CC" = "cc" ] || [ "$COMPILER" = "cc" ]; then + CFLAGS="$CFLAGS -std" + fi + ;; + *powerpc-tenon-machten*) + OS='MachTen PPC' + LFLAGS="$LFLAGS -Xlstack=0x14000 -Xldelcsect" + ;; + *-machten*) + OS='MachTen 68K' + LFLAGS="$LFLAGS -stack 0x14000" + DEF_WANTHSREGEX=yes + ;; + *convex-v11*) + OS='CONVEXOS11' + CFLAGS="$CFLAGS -ext -DCONVEXOS11" + OPTIM="-O1" # scalar optimization only + CC='cc' + DEF_WANTHSREGEX=yes + ;; + i860-intel-osf1) + DEF_WANTHSREGEX=yes + OS='Paragon OSF/1' + CFLAGS="$CFLAGS -DPARAGON" + ;; + *) # default: Catch systems we don't know about + echo Sorry, but we cannot grok \"$PLAT\" + echo uname -m + uname -m + echo uname -r + uname -r + echo uname -s + uname -s + echo uname -v + uname -v + echo uname -X + uname -X + echo Ideally, read the file PORTING, do what it says, and send the + echo resulting patches to The Apache Group by filling out a report + echo form at http://www.apache.org/bugdb.cgi - or, if your browser + echo isn\'t forms-capable, you can send them via email to + echo apache-bugs@apache.org. If you don\'t wish to do the port + echo yourself, please submit this output rather than the patches. + echo Thank you + exit 1 + ;; +esac + +# +# See if we need to override WANTHSREGEX +# +if [ "$RULE_WANTHSREGEX" = "default" ]; then + if [ "x$DEF_WANTHSREGEX" = "x" ]; then + RULE_WANTHSREGEX=no + else + RULE_WANTHSREGEX=$DEF_WANTHSREGEX + fi +fi + +# Show the final values of the rules + +echo "###############" > Makefile.config +echo "# Platform: $OS" >> Makefile.config +echo "# Final Rules:" >> Makefile.config +echo "# Rule WANTHSREGEX=$RULE_WANTHSREGEX" >> Makefile.config +echo "###############" >> Makefile.config + +# +# Now that _that's_ done, get on with it +# + +echo " + configured for $OS platform" + +# +# Now we determine the C-compiler and optimization level +# to use. Settings of CC and OPTIM in Configuration have +# the highest precedence; next comes any settings from +# the above "OS-specific" section. If still unset, +# then we use the "found" location of COMPILERS above +# and set a "safe" optimization level +# + +if egrep "^CC[ ]*=" Makefile > /dev/null; then + CC="" # clear it just in case +else + if [ "x$CC" = "x" ]; then + if [ "x$COMPILER" = "x" ]; then + echo "Error: could not find any of these C compilers" + echo " anywhere in your PATH: $lookedfor" + echo "Configure terminated" + exit 1 + fi + CC=$COMPILER + fi + echo " + setting C compiler to $CC" +fi + +# +# Ditto for optimization +# +if egrep "^OPTIM[ ]*=" Makefile > /dev/null; then + OPTIM="" # ditto +else + if [ "x$OPTIM" = "x" ]; then + OPTIM="-O2" + fi + echo " + setting C compiler optimization-level to $OPTIM" +fi + +# +# Are they using the status monitor module? If so, check +# for STATUS rule... +# +STAT_MOD="mod_status" +if grep "$STAT_MOD" Makefile > /dev/null; then + if [ "$RULE_STATUS" = "yes" ]; then + CFLAGS="$CFLAGS -DSTATUS" + fi +fi + +# +# Are they using dbm auth? If so, add DBM library. +# +if grep mod_auth_dbm Makefile > /dev/null; then + LIBS="$LIBS $DBM_LIB" +fi + +# +# Now HS's POSIX regex implementation if needed/wanted +# +if [ "$RULE_WANTHSREGEX" = "yes" ]; then + REGLIB="regex/libregex.a" + INCLUDES="$INCLUDES -Iregex" +fi + +# +# Now SOCKS4. +# NOTE: We assume that if they are using SOCKS4, then they've +# adjusted EXTRA_LIBS and/or EXTRA_LFLAGS as required, +# otherwise we assume "-L/usr/local/lib -lsocks" +# +if [ "$RULE_SOCKS4" = "yes" ]; then + # Set flag and check Makefile for -lsocks line + CFLAGS="$CFLAGS -Dconnect=Rconnect -Dselect=Rselect" + CFLAGS="$CFLAGS -Dgethostbyname=Rgethostbyname" + if [ "$OS" = "Solaris 2" ]; then + LIBS="$LIBS -lresolv" + fi + if grep "EXTRA_" Makefile | grep "\-lsocks" > /dev/null; then : ; + else + LIBS="$LIBS -L/usr/local/lib -lsocks" + fi +fi + +# +# Good enough +# +echo >> Makefile +if [ "x$CC" != "x" ]; then + echo "CC=$CC" >> Makefile.config +fi +if [ "x$OPTIM" != "x" ]; then + echo "OPTIM=$OPTIM" >> Makefile.config +fi +echo "CFLAGS1=$CFLAGS">> Makefile.config +echo "INCLUDES1=$INCLUDES">> Makefile.config +echo "LIBS1=$LIBS">> Makefile.config +echo "LFLAGS1=$LFLAGS">> Makefile.config +echo "BROKEN_BPRINTF_FLAGS=$OSBPRINTF">> Makefile.config +echo "REGLIB=$REGLIB">> Makefile.config +echo "RANLIB=$RANLIB">> Makefile.config +echo "SHELL=$SHELL">> Makefile.config +echo >> Makefile.config +echo "#### End of Configure created section ####">> Makefile.config + + +# Now (finish) creating the makefiles +cat Makefile.config >> Makefile +sed -e "s#@@Configuration@@#$file#" "$makefile_tmpl" >>Makefile +awk >>Makefile <$tmpfile \ + '($1 == "Module" && $3 ~ /modules\//) { printf "%s: modules/last-built ; @cat /dev/null\n\n", $3, $3}' +#cat Makefile.config ../support/Makefile.tmpl > ../support/Makefile + +cat << EOF > modules/Makefile +# +# Simple Makefile for modules in src/modules. +# Generated by src/Configure according to rules in src/Configuration; +# hand-edit at your own risk! +# + +SHELL=$SHELL +EOF + +if [ "$RULE_WANTHSREGEX" = "yes" ]; then + INCLUDES2="-I../../regex" +fi + +echo "INCLUDES2=$INCLUDES2">> modules/Makefile +echo "MOD_CFLAGS=\$(INCLUDES2) \$(AUX_CFLAGS)">> modules/Makefile + +awk >> modules/Makefile < $tmpfile '\ + BEGIN {printf "MODULES="} \ + ($1 == "Module" && $3 ~ /modules\//) {split ($3, pp, "/"); printf "%s ", pp[2]} \ + END {printf "\n"}' + +awk >> modules/Makefile < $tmpfile '\ + BEGIN {printf "CLEANERS="} \ + ($1 == "Module" && $3 ~ /modules\//) {split ($3, pp, "/"); printf "%s_clean ", pp[2]} \ + END {printf "\n"}' + +cat << EOF >> modules/Makefile + +default: \$(MODULES) + @echo "Done building module subdirectories" + +clean: \$(CLEANERS) + @echo "Done cleaning module subdirectories" + +placeholder \$(MODULES): ForceMe + (cd \$@; \$(MAKE) CC='\$(CC)' AUX_CFLAGS='\$(MOD_CFLAGS)' RANLIB='\$(RANLIB)') + +ForceMe: + +EOF + +awk >>modules/Makefile <$tmpfile \ + '($1 == "Module" && $3 ~ /modules\//) { split ($3, pp, "/"); \ + printf "%s_clean:\n\t(cd %s; $(MAKE) clean)\n\n", pp[2], pp[2]}' + diff --git a/usr.sbin/httpd/src/INSTALL b/usr.sbin/httpd/src/INSTALL new file mode 100644 index 00000000000..a83de509c29 --- /dev/null +++ b/usr.sbin/httpd/src/INSTALL @@ -0,0 +1,71 @@ +This release of Apache supports the notion of "optional modules". +However, the server has to know which modules are compiled into it, in +order for those modules to be effective; this requires generation of a +short bit of code ("modules.c") which simply has a list of them. + +It is also necessary to choose the correct options for your platform. + +To do this: + +1) Copy the file "Configuration.tmpl" to "Configuration" and then edit + "Configuration". This contains the list and settings of various + "Rules" and an additional section at the bottom which + lists the modules which have been compiled in, and also names the + files containing them. You will need to: + + a) Adjust the Rules and EXTRA_CFLAGS|LIBS|LFLAGS|INCLUDES if + you feel so inclined. + + b) Uncomment lines corresponding to those optional modules you wish + to include (among the Module lines at the bottom of the file), + or add new lines corresponding to custom modules you have written. + (See API.html for preliminary docs on how to do that). + + Note that DBM auth has to be explicitly configured in, if you want + it --- just uncomment the corresponding line. + +2) Run the "Configure" script: + + % ./Configure + Using config file: Configuration + Using Makefile template file: Makefile.tmpl + + configured for <whatever> platform + + setting C compiler to <whatever> * + + setting C compiler optimization-level to <whatever> * + % + + This generates new versions of the Makefile and of modules.c. (If + you want to maintain multiple configurations, you can say, e.g., + + % ./Configure -file Configuration.ai + Using config file: Configuration.ai + Using Makefile template file: Makefile.tmpl + + configured for <whatever> platform + + setting C compiler to <whatever> * + + setting C compiler optimization-level to <whatever> * + % + + (*: Depending on Configuration and your system, Configure + may not print these lines. That's OK.) + +3) Type "make". + +The modules we place in the Apache distribution are the ones we have +tested and are used regularly by various members of the Apache +development group. Additional modules contributed by members or third +parties with specific needs or functions are available at +<URL:http://www.apache.org/dist/contrib/modules/>. There are +instructions on that page for linking these modules into the +core Apache code. + +If during compilation you get a warning about a missing 'regex.h', set +WANTHSREGEX=yes in the 'Configuration', and let The Apache Group know +you needed to do this for your OS by filling out a problem report form +at <http://www.apache.org/bugdb.cgi>, or by sending a mail message to +apache-bugs@apache.org. Include the output of the command "uname -a". + + +-------------------------------------------------------------------- + +Now that you have compiled Apache, go back to the README file in the +top-level directory of this distribution to continue the installation. diff --git a/usr.sbin/httpd/src/Makefile.bsd-wrapper b/usr.sbin/httpd/src/Makefile.bsd-wrapper new file mode 100644 index 00000000000..bc836a9c85b --- /dev/null +++ b/usr.sbin/httpd/src/Makefile.bsd-wrapper @@ -0,0 +1,48 @@ + +.include <bsd.own.mk> + +PROG=httpd +BINDIR=/usr/sbin +BINOWN=root +BINGRP=daemon + +all: Makefile + ${MAKE} + +.FORCE: .IGNORE + +helpers/GuessOS: + lndir -e Makefile.bsd-wrapper -e obj ${.CURDIR} ${.OBJDIR} + +config: .FORCE + sh ${.CURDIR}/Configure -file ${.CURDIR}/Configuration -make ${.CURDIR}/Makefile.tmpl + +Makefile: helpers/GuessOS + sh ${.CURDIR}/Configure -file ${.CURDIR}/Configuration -make ${.CURDIR}/Makefile.tmpl + +# apache has no man pages in the dist + +maninistall: + @echo No man pages for apache + +install: maninistall + ${INSTALL} ${INSTALL_COPY} ${INSTALL_STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} ${PROG} ${BINDIR} + +clean cleandir: +.if exists(${.OBJDIR}/Makefile) + ${MAKE} clean +.endif + /bin/rm -f Makefile + /bin/rm -f modules/Makefile + /bin/rm -f Makefile.config + /bin/rm -f modules.c + +depend: + # Nothing here yet +lint: + #Nothing here yet +tags: + #Nothing here yet + +.include<bsd.obj.mk> +.include<bsd.subdir.mk> diff --git a/usr.sbin/httpd/src/Makefile.tmpl b/usr.sbin/httpd/src/Makefile.tmpl new file mode 100644 index 00000000000..e546fd11f69 --- /dev/null +++ b/usr.sbin/httpd/src/Makefile.tmpl @@ -0,0 +1,162 @@ +# Apache makefile template (well, suffix). + +# This is combined with the information in the "Configuration" file +# by the configure script to make the actual Makefile. + +CFLAGS=$(OPTIM) $(CFLAGS1) $(EXTRA_CFLAGS) +LIBS=$(EXTRA_LIBS) $(LIBS1) +INCLUDES=$(INCLUDES1) $(EXTRA_INCLUDES) +LFLAGS=$(LFLAGS1) $(EXTRA_LFLAGS) + +OBJS= alloc.o http_main.o http_core.o http_config.o http_request.o \ + http_log.o http_protocol.o rfc1413.o util.o util_script.o modules.o buff.o\ + md5c.o util_md5.o explain.o http_bprintf.o util_date.o util_snprintf.o\ + $(MODULES) + +.c.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $(SPACER) $< + +all: @@Configuration@@ httpd + +@@Configuration@@: Configuration.tmpl + @echo "@@Configuration@@ older than Configuration.tmpl, or doesn't exist." + @echo "Consider copying Configuration.tmpl to @@Configuration@@, editing and rerunning" + @echo "Configure." + @echo "If not, you will at least have to touch @@Configuration@@." + @false + +httpd: $(REGLIB) $(OBJS) + $(CC) $(LFLAGS) -o httpd $(OBJS) $(REGLIB) $(LIBS) + +regex/libregex.a: + (cd regex; $(MAKE) lib CC='$(CC)' AUX_CFLAGS='$(CFLAGS)' RANLIB='$(RANLIB)') + +modules/last-built: + (cd modules; \ + $(MAKE) CC='$(CC)' AUX_CFLAGS='$(CFLAGS)' RANLIB='$(RANLIB)') + +clean: + rm -f httpd *.o core + cd regex; $(MAKE) clean + cd modules; $(MAKE) clean + +dist.tar: + # Assure a semi-sensible configuration going out... + cp Makefile.orig Makefile + cp modules.c.orig modules.c + tar cvf dist.tar README INSTALL CHANGES TODO API.html \ + Configuration Configure Makefile.tmpl Makefile *.h *.c + +# Work around broken compilers +http_bprintf.o: http_bprintf.c + $(CC) -c $(INCLUDES) $(CFLAGS) $(BROKEN_BPRINTF_FLAGS) http_bprintf.c + +# We really don't expect end users to use this rule. It works only with +# gcc, and rebuilds Makefile.tmpl. You have to re-run Configure after +# using it. +depend: + sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \ + && $(CC) -MM $(INCLUDES) $(CFLAGS) *.c >> Makefile.new \ + && mv Makefile.tmpl Makefile.tmpl.bak \ + && mv Makefile.new Makefile.tmpl + +#Dependencies + +$(OBJS): Makefile + +# DO NOT REMOVE +alloc.o: alloc.c httpd.h conf.h alloc.h buff.h +buff.o: buff.c conf.h alloc.h buff.h +explain.o: explain.c explain.h +http_bprintf.o: http_bprintf.c conf.h alloc.h buff.h +http_config.o: http_config.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h http_log.h http_request.h \ + http_conf_globals.h explain.h +http_core.o: http_core.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_core.h http_protocol.h http_conf_globals.h http_main.h \ + http_log.h rfc1413.h util_md5.h md5.h scoreboard.h +http_log.o: http_log.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_core.h http_log.h +http_main.o: http_main.c httpd.h conf.h alloc.h buff.h http_main.h \ + http_log.h http_config.h http_protocol.h http_request.h \ + http_conf_globals.h http_core.h scoreboard.h explain.h +http_protocol.o: http_protocol.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h http_protocol.h http_main.h http_log.h \ + util_date.h +http_request.o: http_request.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_request.h http_core.h http_protocol.h http_log.h \ + http_main.h scoreboard.h +md5c.o: md5c.c md5.h +mod_access.o: mod_access.c httpd.h conf.h alloc.h buff.h http_core.h \ + http_config.h http_log.h http_request.h +mod_actions.o: mod_actions.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_request.h http_core.h http_protocol.h http_main.h \ + http_log.h util_script.h +mod_alias.o: mod_alias.c httpd.h conf.h alloc.h buff.h http_config.h +mod_asis.o: mod_asis.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_protocol.h http_log.h util_script.h http_main.h http_request.h +mod_auth.o: mod_auth.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_core.h http_log.h http_protocol.h +mod_auth_anon.o: mod_auth_anon.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h http_log.h http_protocol.h +mod_auth_db.o: mod_auth_db.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h http_log.h http_protocol.h +mod_auth_dbm.o: mod_auth_dbm.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h http_log.h http_protocol.h +mod_auth_msql.o: mod_auth_msql.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h http_log.h http_protocol.h +mod_browser.o: mod_browser.c httpd.h conf.h alloc.h buff.h \ + http_config.h +mod_cern_meta.o: mod_cern_meta.c httpd.h conf.h alloc.h buff.h \ + http_config.h util_script.h http_log.h http_request.h +mod_cgi.o: mod_cgi.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_request.h http_core.h http_protocol.h http_main.h http_log.h \ + util_script.h http_conf_globals.h +mod_digest.o: mod_digest.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_core.h http_log.h http_protocol.h util_md5.h md5.h +mod_dir.o: mod_dir.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_core.h http_request.h http_protocol.h http_log.h http_main.h \ + util_script.h +mod_dld.o: mod_dld.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_conf_globals.h +mod_env.o: mod_env.c httpd.h conf.h alloc.h buff.h http_config.h +mod_expires.o: mod_expires.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_log.h +mod_headers.o: mod_headers.c httpd.h conf.h alloc.h buff.h \ + http_config.h +mod_imap.o: mod_imap.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_request.h http_core.h http_protocol.h http_main.h http_log.h \ + util_script.h +mod_include.o: mod_include.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_request.h http_core.h http_protocol.h http_log.h \ + http_main.h util_script.h +mod_info.o: mod_info.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_core.h http_log.h http_main.h http_protocol.h util_script.h +mod_log_agent.o: mod_log_agent.c httpd.h conf.h alloc.h buff.h \ + http_config.h +mod_log_config.o: mod_log_config.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h +mod_log_referer.o: mod_log_referer.c httpd.h conf.h alloc.h buff.h \ + http_config.h +mod_mime.o: mod_mime.c httpd.h conf.h alloc.h buff.h http_config.h +mod_negotiation.o: mod_negotiation.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_request.h http_core.h http_log.h util_script.h +mod_rewrite.o: mod_rewrite.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_request.h http_core.h http_log.h mod_rewrite.h +mod_status.o: mod_status.c httpd.h conf.h alloc.h buff.h http_config.h \ + http_core.h http_protocol.h http_main.h util_script.h scoreboard.h \ + http_log.h +mod_userdir.o: mod_userdir.c httpd.h conf.h alloc.h buff.h \ + http_config.h +mod_usertrack.o: mod_usertrack.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_core.h +modules.o: modules.c httpd.h conf.h alloc.h buff.h http_config.h +rfc1413.o: rfc1413.c httpd.h conf.h alloc.h buff.h http_log.h \ + rfc1413.h +util.o: util.c httpd.h conf.h alloc.h buff.h http_conf_globals.h +util_date.o: util_date.c util_date.h +util_md5.o: util_md5.c httpd.h conf.h alloc.h buff.h util_md5.h md5.h +util_script.o: util_script.c httpd.h conf.h alloc.h buff.h \ + http_config.h http_conf_globals.h http_main.h http_log.h \ + http_protocol.h http_core.h http_request.h util_script.h +util_snprintf.o: util_snprintf.c conf.h diff --git a/usr.sbin/httpd/src/PORTING b/usr.sbin/httpd/src/PORTING new file mode 100644 index 00000000000..24c7c4f3771 --- /dev/null +++ b/usr.sbin/httpd/src/PORTING @@ -0,0 +1,257 @@ +The Semi-Official Guide to Porting Apache + +------------- +Introduction: +------------- +Apache has been ported to a wide variety of platforms, from multiple +UNIX varients to OS/2. Nonetheless, there are most likely a few +platforms out there that currently are not "officially" supported +under Apache. Porting Apache to these platforms can be quite simple +depending on the "genericness" of the OS. This doc will provide +some basic guidelines to help the potential porter. + +------------- +Requirements: +------------- +One of the basic requirements for a potential Apache platform is +a robust TCP/IP implementation. Just about any UNIX out there +nowadays, even some ancient ones, have a TCP/IP stack that will +work. In particular, the UNIX should provide for sockets and the +basic controlling functions for them (like accept(), bind(), etc). + +The source for Apache is written in ANSI-C, so an ANSI-C compiler +is required. However, Apache does not use or require ANSI-only +functions or options (eg: the "%n" parameter in the scanf() +family); the source basically uses ANSI function prototyping but +no other specific ANSIisms. Thus, an ANSI-to-K&R filter _may_ +work, although as far as I know it has not yet been tried. If you +attempt this, let the Apache team know (mailto: new-httpd@hyperreal.com). + +------------------- +The Starting Point: +------------------- +The first thing to look at is the output of the ./helpers/GuessOS +script. This is a simple script that attempts to determine the +platform and OS you are running on. The output of this script +is used by Configure to set some basic compilation parameters. + +The output of ./helpers/GuessOS was designed to be GNUconfig.guess +compatible (from GNU/autoconf). The format of the output string +is: + + machine-vendor-OS + +This string is returned to the main Configure script as the +shell variable $PLAT. If Configure is not "aware" of that platform +(or cannot correctly parse it), it will complain and die. + +---------------------- +Configure cannot Grok: +---------------------- +If this happens to you, then it means that Configure doesn't +know how to configure and compile Apache for your OS. The first +course of action is the easiest: Look in Configure and see if +there are any OSs which is similar to yours. + +For example, let's say that your OS is similar to HP-UX, but that +GuessOS returns "foobar-intel-hubble". You would then edit +Configure as follows: + + *-hp-hpux*|*-*-hubble) + OS='HP-UX' + CFLAGS="$CFLAGS -DHPUX" + ;; + +The '|*-*-hubble' was added to the switch statement for HP-UX. + +Another fix may involve editing the GuessOS helper script. Let's +say, for example, that your system is SysV4-based, but that +GuessOS does not return that info. You could then add a switch +to the script that does something like: + + *WeirdSystem*) + echo "${MACHINE}-whatever-sysv4"; exit 0 + ;; + +In this case, we force GuessOS to return a string that includes +the "sysv4" cookie for Configure to recognize. + +Unfortunately, unless you are running a very generic BSD or SysV +system, no "supported" OS will be close enough in all aspects to +allow for a clear (and possibly workable) build of Apache. If this +is the case, you will need to port Apache to your OS. + +------------------- +Porting for Apache: +------------------- +When all else fails, it's time to hack some code. The source itself +is generic enough that most ports are incredibly easy. No matter +what, however, there are 2 source files that need to be updated +for the port: + + Configure + conf.h + +Configure: +========== +Configure concerns itself with determining the OS-type for the +build and setting up a few Makefile variables for the build. The +most important is 'OS' and 'CFLAGS'. For example, when Configure +determines a build for A/UX, it runs the following lines: + + case "$PLAT" in + *-apple-aux3*) + OS='A/UX 3.1.x' + CFLAGS="$CFLAGS -DAUX -D_POSIX_SOURCE" + LIBS="$LIBS -lposix -lbsd" + LFLAGS="$LFLAGS -s" + DEF_WANTHSREGEX=no + ;; + +The 'OS' variable is used to define the system Apache is being built +for. You will also note that 'CFLAGS' defines "-DAUX". In this case, +'AUX' is a magic cookie used by the Apache code (mainly conf.h [see +below]) to handle OS-specific code. Each code that has and requires +such OS-specific code will require a unique "system cookie" defined +in 'CFLAGS'. You will also note that Configure also goes ahead and +predefines the LIBS and LFLAGS Makefile variables (DEF_WANTHSREGEX is +explained below). + +conf.h: +======= +The Apache code, specifically in conf.h, uses a variety of #defines to +control how the code is compiled and what options are available for each +supported OS. One of the hardest parts about the porting process is +determining which of the following are applicable for your system and +setup. This time using the example of AIX, we see: + + #elif defined(AIX) + #undef HAVE_GMTOFF + #undef NO_KILLPG + #undef NO_SETSID + #define HAVE_SYS_SELECT_H + #define JMP_BUF sigjmp_buf + #define HAVE_MMAP + typedef int rlim_t; + +The above lines describe which functions, capabilities and specifics +are required for Apache to build and run under IBM AIX (the #undefs +are not strictly required, but are a Good Idea anyway). + +The following several lines provide a list and short description +of these #defines. By correcting #defining the ones you need in conf.h +(wrapped by the above mentioned "system cookie"), you can fine tune the +build for your OS. + +-- + + NEED_*: + If the particular OS doesn't supply the specified function, we use the + Apache-supplied version (in util.c). + + NEED_STRERROR: + NEED_STRDUP: + NEED_STRCASECMP: + NEED_STRNCASECMP: + NEED_INITGROUPS: + NEED_WAITPID: + NEED_STRERROR: +-- + + HAVE_*: + Does this OS have/support this capability? + + HAVE_GMTOFF: + Define if the OS's tm struct has the tm_gmtoff element + + HAVE_RESOURCE: + Define if the OS supports the getrlimit()/setrlimit() functions + + HAVE_MMAP: + Define if the OS supports the BSD mmap() call. This is used by various + OSs to allow the scoreboard file to be held in shared mmapped-memory + instead of a real file. + + HAVE_SHMGET: + Define if the OS has the SysV-based shmget() family of shared-memory + functions. Used to allow the scoreboard to live in a shared-memory + slot instead of a real file. + + HAVE_CRYPT_H: + Define if the OS has the <crypt.h> header file. + + HAVE_SYS_SELECT_H: + Define if the OS has the <sys/select.h> header file. + + HAVE_SYS_RESOURCE_H: + Define if the OS has and supports the getrlimit/setrlimit + family. Apache uses this to determine if RLIMIT_CPU|VMEM|DATA|RLIMIT + is found and used. + + HAVE_SNPRINTF: + Apache makes extensive use of the snprintf() function. many + platforms do not provide this function. If your platform + does provide it _and_ it's reliable (most are not) then + define this to use the OS version. Otherwise, Apache will + use it's own. +-- + + USE_*: + These #defines are used for functions and ability that aren't exactly + required but should be used. + + USE_FCNTL_SERIALIZED_ACCEPT: + Define if the OS requires a mutex "lock" around the socket accept() + call. Use fcntl() locking. + + USE_FLOCK_SERIALIZED_ACCEPT: + Define if the OS requires a mutex "lock" around the socket accept() + call. Use flock() locking (fcntl() is expensive on some OSs, esp. + when using NFS). + + USE_LONGJMP: + use the longjmp() call instead of siglongjmp() + (as well as setjmp() instead of sigsetjmp()) + +-- + + NO_*: + These are defined if the OS does NOT have the specified function or if + we should not use it. + + NO_UNISTD_H: + NO_KILLPG: + NO_SETSID: + NO_USE_SIGACTION: + Do not use the sigaction() call, even if we have it. + NO_LINGCLOSE: + Do not use Apache's soft, "lingering" close feature to + terminate connections. + NO_SLACK: + Do not use the "slack" fd feature which requires a working fcntl + F_DUPFD. +-- + + MISC #DEFINES: + Various other #defines used in the code. + + JMP_BUF: + The variable-type for siglongjmp() or longjmp() call. + + MOVEBREAK: + Amount to move sbrk() breakpoint, if required, before attaching + shared-memory segment. + +----------- +Conclusion: +----------- +The above hints, and a good understanding of your OS and Apache, will +go a LONG way in helping you get Apache built and running on your +OS. If you have a port, PLEASE send Email to 'new-httpd@hyperreal.com' +with the patches so that we may add them to the official version. +If you hit a rough spot in the porting process, you can also try +sending Email to that address as well and, if you are lucky, someone +will respond. Another good source is the 'comp.infosystems.www.servers.unix' +Usenet group as well. + +Good luck and happy porting! diff --git a/usr.sbin/httpd/src/README b/usr.sbin/httpd/src/README new file mode 100644 index 00000000000..4abb59f4bb7 --- /dev/null +++ b/usr.sbin/httpd/src/README @@ -0,0 +1,147 @@ +The following document was written by Robert S. Thau (rst@ai.mit.edu) on the +release of Apache 1.0. Some details may have changed since then regarding the +functions and names of modules, but the basic ideas are still intact. +================================================= + +The basic idea of the new Apache release is to make a modular +"tinkertoy" server, to which people can easily add code which is +valuable to them (even if it isn't universally useful) without hairing +up a monolithic server. Applications for this idea include database +integration, support for experimental search and scripting extensions, +new authentication modes (digest authentication, for instance, could +be done entirely as a module), and so forth. All modules have the +same interface to the server core, and through it, to each other. + +In particular, the following are modules in the current code base: +common log format (other loggers can easily coexist with it), auth and +dbm auth (although both use common code in http_protocol.c to parse +the Authorization: line), directory handling (which can be added or +replaced), handling of aliases and access control, content +negotiation, CGI, includes, aliases, and so forth. (What's left in +the basic server? Not a whole lot). The configuration file commands +which configure these things are defined, for the most part, by the +modules themselves, and not by the server core (each module has, or +can have, a command dispatch table). + +Besides carving up the base code into modules, this release makes a +few other fairly pervasive changes. Most of the global variables are +gone; most of the MAX_STRING_LENGTH char arrays are gone (the few that +are left being sprintf() targets, or I/O buffers of various sorts), +and unmunge_name has vanished. The most drastic change is the use of +a "compool" strategy to manage resources allocated for a request --- +the code in alloc.c keeps track of it all and allows it to be freed en +bloc at the end of the request. This strategy seems to be effective +in stanching memory and descriptor leaks. + +Additional third-party modules can be found at +<URL:http://www.apache.org/dist/contrib/modules/>. + + +A brief code review: + +The code here can be divided into the server core (the http_* files, +along with alloc.c and the various utility files), and several modules +(the mod_* files). + +The core interfaces to modules through the "module" structure which +describes each one. There's a linked list of these things rooted at +top_module, through which http_config.c dispatches when necessary. The +module structures themselves are defined at the bottom of the mod_foo +files. (Loading new modules dynamically at runtime should be simple; +just push them onto the linked list. The only complication is what to +do with AddModule commands when the config files are reread, +particularly if you find a module has been taken out). + +In addition to the core itself (which does have a module structure to +hold its command tables, and the handlers for various phases of +request handling which make it *barely* a web server on its own), +the modules included here are the following: + +mod_mime.c --- deduction of MIME types and content-encodings from + filename extensions. This module defines the AddType, AddEncoding, + and TypesConfig config-file directives. This code is off in a + module by itself so that people who want to experiment with other + meta-information schemes can replace it, and still have content + negotiation work. + +mod_log_config.c --- logging in configurable or common log format. + +mod_auth.c --- HTTP authentication. Defines the AuthUserFile and + AuthGroupFile directives (other auth-related commands are handled by + the core itself, so it knows which requests require it to poll the + modules for authentication handlers). + +mod_auth_dbm.c --- DBM auth. Untested, and left out of the modules + list in modules.c because of that, but it does at least compile. + Grump. + +mod_access.c --- access checking by DNS name or IP address; defines + the "order", "allow" and "deny" config-file commands. (If this + module is compiled out, the server fails safe --- any attempt to + configure access control will die on a config file syntax error when + the relevant commands go unrecognized). + +mod_negotiation.c --- Content negotiation. Defines the + CacheNegotiatedDocs config-file command. Making this a module is + perhaps going overboard, but I wanted to see how far I could push + it. + +mod_alias.c --- Alias command and file translation. + +mod_userdir.c --- ditto for Userdir. + +mod_cgi.c --- Common Gateway Interface. Also defines ScriptAlias, + because scripts are treated slightly differently depending on + whether they are ScriptAliased or not (in particular, ExecCGI is not + required in the former case). + +mod_includes.c --- server-side includes. + +mod_dir.c --- defines a whole *raft* of commands; handles directories. + +mod_asis.c --- ASIS file handling. + +mod_dld.c --- the experimental runtime-code-loader described above. + You'll have to alter the makefile and modules.c to make this active + if you want it. + + + +As to the core, here's a brief review of what's where: + +http_protocol.c --- functions for dealing directly with the client. + Reading requests, writing replies of various sorts. I've tried to + route all data transfer between server and client through here, so + there's a single piece of code to change if we want to add, say, + HTTP-NG packetization. The major glaring exception is NPH- CGI + scripts; what *will* we do with those for HTTP-NG? + +http_request.c --- functions which direct the processing of requests, + including error handling. Generally responsible for making sure + that the right module handlers get invoked, in the right order. + (This includes the "sub-request" mechanism, which is used by + includes and other stuff to ask about the status of particular + subfiles). + +http_core.c --- + Contains the core module structure, its command table, and the + command handlers, also the filename translation routine, and the + like for the core. (Basically, this is all of the core module stuff + which looks more or less like the boilerplate from the other modules). + +http_config.c --- Functions to read config files and dispatch to the + command handlers; also, routines to manage configuration vectors, + and to dispatch to modules' handlers for the various phases of + handling a request. + +http_log.c --- just the error log. Error handling is split between + http_protocol.c (for generating the default error responses) and + http_request.c (for executive handling, including ErrorDocument + invocation); transaction logging is in the modules. + +http_main.c --- System startup, restart, and accepting connections; + also timeout handling (which is pretty grotesque right now; ideas?) + +alloc.c --- allocation of all resources which might have to be reclaimed + eventually, including memory, files, and child processes. + diff --git a/usr.sbin/httpd/src/alloc.c b/usr.sbin/httpd/src/alloc.c new file mode 100644 index 00000000000..d002a6f8f8c --- /dev/null +++ b/usr.sbin/httpd/src/alloc.c @@ -0,0 +1,1142 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + + +/* + * Resource allocation code... the code here is responsible for making + * sure that nothing leaks. + * + * rst --- 4/95 --- 6/95 + */ + +#include "httpd.h" + +#include <stdarg.h> + +/***************************************************************** + * + * Managing free storage blocks... + */ + +union align +{ + /* Types which are likely to have the longest RELEVANT alignment + * restrictions... + */ + + char *cp; + void (*f)(); + long l; + FILE *fp; + double d; +}; + +#define CLICK_SZ (sizeof(union align)) + +union block_hdr +{ + union align a; + + /* Actual header... */ + + struct { + char *endp; + union block_hdr *next; + char *first_avail; + } h; +}; + +union block_hdr *block_freelist = NULL; + + + +/* Get a completely new block from the system pool. Note that we rely on +malloc() to provide aligned memory. */ + +union block_hdr *malloc_block (int size) +{ + union block_hdr *blok = + (union block_hdr *)malloc(size + sizeof(union block_hdr)); + + if (blok == NULL) { + fprintf (stderr, "Ouch! malloc failed in malloc_block()\n"); + exit (1); + } + blok->h.next = NULL; + blok->h.first_avail = (char *)(blok + 1); + blok->h.endp = size + blok->h.first_avail; + + return blok; +} + + + +void chk_on_blk_list (union block_hdr *blok, union block_hdr *free_blk) +{ + /* Debugging code. Left in for the moment. */ + + while (free_blk) { + if (free_blk == blok) { + fprintf (stderr, "Ouch! Freeing free block\n"); + exit (1); + } + free_blk = free_blk->h.next; + } +} + +/* Free a chain of blocks --- must be called with alarms blocked. */ + +void free_blocks (union block_hdr *blok) +{ + /* First, put new blocks at the head of the free list --- + * we'll eventually bash the 'next' pointer of the last block + * in the chain to point to the free blocks we already had. + */ + + union block_hdr *old_free_list = block_freelist; + + if (blok == NULL) return; /* Sanity check --- freeing empty pool? */ + + block_freelist = blok; + + /* + * Next, adjust first_avail pointers of each block --- have to do it + * sooner or later, and it simplifies the search in new_block to do it + * now. + */ + + while (blok->h.next != NULL) { + chk_on_blk_list (blok, old_free_list); + blok->h.first_avail = (char *)(blok + 1); + blok = blok->h.next; + } + + chk_on_blk_list (blok, old_free_list); + blok->h.first_avail = (char *)(blok + 1); + + /* Finally, reset next pointer to get the old free blocks back */ + + blok->h.next = old_free_list; +} + + + + +/* Get a new block, from our own free list if possible, from the system + * if necessary. Must be called with alarms blocked. + */ + +union block_hdr *new_block (int min_size) +{ + union block_hdr **lastptr = &block_freelist; + union block_hdr *blok = block_freelist; + + /* First, see if we have anything of the required size + * on the free list... + */ + + while (blok != NULL) { + if (min_size + BLOCK_MINFREE <= blok->h.endp - blok->h.first_avail) { + *lastptr = blok->h.next; + blok->h.next = NULL; + return blok; + } + else { + lastptr = &blok->h.next; + blok = blok->h.next; + } + } + + /* Nope. */ + + min_size += BLOCK_MINFREE; + return malloc_block((min_size > BLOCK_MINALLOC) ? min_size : BLOCK_MINALLOC); +} + + + +/* Accounting */ + +long bytes_in_block_list (union block_hdr *blok) +{ + long size = 0; + + while (blok) { + size += blok->h.endp - (char *)(blok + 1); + blok = blok->h.next; + } + + return size; +} + + +/***************************************************************** + * + * Pool internals and management... + * NB that subprocesses are not handled by the generic cleanup code, + * basically because we don't want cleanups for multiple subprocesses + * to result in multiple three-second pauses. + */ + +struct process_chain; +struct cleanup; + +static void run_cleanups (struct cleanup *); +static void free_proc_chain (struct process_chain *); + +struct pool { + union block_hdr *first; + union block_hdr *last; + struct cleanup *cleanups; + struct process_chain *subprocesses; + struct pool *sub_pools; + struct pool *sub_next; + struct pool *sub_prev; + struct pool *parent; + char *free_first_avail; +}; + +pool *permanent_pool; + +/* Each pool structure is allocated in the start of its own first block, + * so we need to know how many bytes that is (once properly aligned...). + * This also means that when a pool's sub-pool is destroyed, the storage + * associated with it is *completely* gone, so we have to make sure it + * gets taken off the parent's sub-pool list... + */ + +#define POOL_HDR_CLICKS (1 + ((sizeof(struct pool) - 1) / CLICK_SZ)) +#define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ) + +struct pool *make_sub_pool (struct pool *p) +{ + union block_hdr *blok; + pool *new_pool; + + block_alarms(); + + blok = new_block (0); + new_pool = (pool *)blok->h.first_avail; + blok->h.first_avail += POOL_HDR_BYTES; + + memset ((char *)new_pool, '\0', sizeof (struct pool)); + new_pool->free_first_avail = blok->h.first_avail; + new_pool->first = new_pool->last = blok; + + if (p) { + new_pool->parent = p; + new_pool->sub_next = p->sub_pools; + if (new_pool->sub_next) new_pool->sub_next->sub_prev = new_pool; + p->sub_pools = new_pool; + } + + unblock_alarms(); + + return new_pool; +} + +void init_alloc() { permanent_pool = make_sub_pool (NULL); } + +void clear_pool (struct pool *a) +{ + block_alarms(); + + while (a->sub_pools) + destroy_pool (a->sub_pools); + + a->sub_pools = NULL; + + run_cleanups (a->cleanups); a->cleanups = NULL; + free_proc_chain (a->subprocesses); a->subprocesses = NULL; + free_blocks (a->first->h.next); a->first->h.next = NULL; + + a->last = a->first; + a->first->h.first_avail = a->free_first_avail; + + unblock_alarms(); +} + +void destroy_pool (pool *a) +{ + block_alarms(); + clear_pool (a); + + if (a->parent) { + if (a->parent->sub_pools == a) a->parent->sub_pools = a->sub_next; + if (a->sub_prev) a->sub_prev->sub_next = a->sub_next; + if (a->sub_next) a->sub_next->sub_prev = a->sub_prev; + } + + free_blocks (a->first); + unblock_alarms(); +} + +long bytes_in_pool (pool *p) { return bytes_in_block_list (p->first); } +long bytes_in_free_blocks () { return bytes_in_block_list (block_freelist); } + +/***************************************************************** + * + * Allocating stuff... + */ + + +void *palloc (struct pool *a, int reqsize) +{ + /* Round up requested size to an even number of alignment units (core clicks) + */ + + int nclicks = 1 + ((reqsize - 1) / CLICK_SZ); + int size = nclicks * CLICK_SZ; + + /* First, see if we have space in the block most recently + * allocated to this pool + */ + + union block_hdr *blok = a->last; + char *first_avail = blok->h.first_avail; + char *new_first_avail; + + if(reqsize <= 0) + return NULL; + + new_first_avail = first_avail + size; + + if (new_first_avail <= blok->h.endp) { + blok->h.first_avail = new_first_avail; + return (void *)first_avail; + } + + /* Nope --- get a new one that's guaranteed to be big enough */ + + block_alarms(); + blok = new_block (size); + a->last->h.next = blok; + a->last = blok; + unblock_alarms(); + + first_avail = blok->h.first_avail; + blok->h.first_avail += size; + + return (void *)first_avail; +} + +void *pcalloc(struct pool *a, int size) +{ + void *res = palloc (a, size); + memset (res, '\0', size); + return res; +} + +char *pstrdup(struct pool *a, const char *s) +{ + char *res; + if (s == NULL) return NULL; + res = palloc (a, strlen(s) + 1); + strcpy (res, s); + return res; +} + +char *pstrndup(struct pool *a, const char *s, int n) +{ + char *res; + if (s == NULL) return NULL; + res = palloc (a, n + 1); + strncpy (res, s, n); + res[n] = '\0'; + return res; +} + +char *pstrcat(pool *a, ...) +{ + char *cp, *argp, *res; + + /* Pass one --- find length of required string */ + + int len = 0; + va_list adummy; + + va_start (adummy, a); + + while ((cp = va_arg (adummy, char *)) != NULL) + len += strlen(cp); + + va_end (adummy); + + /* Allocate the required string */ + + res = (char *)palloc(a, len + 1); + cp = res; + + /* Pass two --- copy the argument strings into the result space */ + + va_start (adummy, a); + + while ((argp = va_arg (adummy, char *)) != NULL) { + strcpy (cp, argp); + cp += strlen(argp); + } + + va_end (adummy); + + /* Return the result string */ + + return res; +} + + +/***************************************************************** + * + * The 'array' functions... + */ + +array_header *make_array (pool *p, int nelts, int elt_size) +{ + array_header *res = (array_header *)palloc(p, sizeof(array_header)); + + if (nelts < 1) nelts = 1; /* Assure sanity if someone asks for + * array of zero elts. + */ + + res->elts = pcalloc (p, nelts * elt_size); + + res->pool = p; + res->elt_size = elt_size; + res->nelts = 0; /* No active elements yet... */ + res->nalloc = nelts; /* ...but this many allocated */ + + return res; +} + +void *push_array (array_header *arr) +{ + if (arr->nelts == arr->nalloc) { + int new_size = (arr->nalloc <= 0) ? 1 : arr->nalloc * 2; + char *new_data; + + new_data = pcalloc (arr->pool, arr->elt_size * new_size); + + memcpy (new_data, arr->elts, arr->nalloc * arr->elt_size); + arr->elts = new_data; + arr->nalloc = new_size; + } + + ++arr->nelts; + return arr->elts + (arr->elt_size * (arr->nelts - 1)); +} + +void array_cat (array_header *dst, const array_header *src) +{ + int elt_size = dst->elt_size; + + if (dst->nelts + src->nelts > dst->nalloc) { + int new_size = (dst->nalloc <= 0) ? 1 : dst->nalloc * 2; + char *new_data; + + while (dst->nelts + src->nelts > new_size) + new_size *= 2; + + new_data = pcalloc (dst->pool, elt_size * new_size); + memcpy (new_data, dst->elts, dst->nalloc * elt_size); + + dst->elts = new_data; + dst->nalloc = new_size; + } + + memcpy (dst->elts + dst->nelts * elt_size, src->elts, elt_size * src->nelts); + dst->nelts += src->nelts; +} + +array_header *copy_array (pool *p, const array_header *arr) +{ + array_header *res = make_array (p, arr->nalloc, arr->elt_size); + + memcpy (res->elts, arr->elts, arr->elt_size * arr->nelts); + res->nelts = arr->nelts; + return res; +} + +/* This cute function copies the array header *only*, but arranges + * for the data section to be copied on the first push or arraycat. + * It's useful when the elements of the array being copied are + * read only, but new stuff *might* get added on the end; we have the + * overhead of the full copy only where it is really needed. + */ + +array_header *copy_array_hdr (pool *p, const array_header *arr) +{ + array_header *res = (array_header *)palloc(p, sizeof(array_header)); + + res->elts = arr->elts; + + res->pool = p; + res->elt_size = arr->elt_size; + res->nelts = arr->nelts; + res->nalloc = arr->nelts; /* Force overflow on push */ + + return res; +} + +/* The above is used here to avoid consing multiple new array bodies... */ + +array_header *append_arrays (pool *p, + const array_header *first, + const array_header *second) +{ + array_header *res = copy_array_hdr (p, first); + + array_cat (res, second); + return res; +} + + +/***************************************************************** + * + * The "table" functions. + */ + +table *make_table (pool *p, int nelts) { + return make_array (p, nelts, sizeof (table_entry)); +} + +table *copy_table (pool *p, const table *t) { + return copy_array (p, t); +} + +void clear_table (table *t) +{ + t->nelts = 0; +} + +array_header *table_elts (table *t) { return t; } + +char *table_get (const table *t, const char *key) +{ + table_entry *elts = (table_entry *)t->elts; + int i; + + if (key == NULL) return NULL; + + for (i = 0; i < t->nelts; ++i) + if (!strcasecmp (elts[i].key, key)) + return elts[i].val; + + return NULL; +} + +void table_set (table *t, const char *key, const char *val) +{ + register int i, j, k; + table_entry *elts = (table_entry *)t->elts; + int done = 0; + + for (i = 0; i < t->nelts;) { + if (!strcasecmp (elts[i].key, key)) { + if (!done) { + elts[i].val = pstrdup(t->pool, val); + done = 1; + ++i; + } + else { /* delete an extraneous element */ + for (j = i, k = i + 1; k < t->nelts; ++j, ++k) { + elts[j].key = elts[k].key; + elts[j].val = elts[k].val; + } + --t->nelts; + } + } + else { + ++i; + } + } + + if (!done) { + elts = (table_entry *)push_array(t); + elts->key = pstrdup (t->pool, key); + elts->val = pstrdup (t->pool, val); + } +} + +void table_unset( table *t, const char *key ) +{ + register int i, j, k; + table_entry *elts = (table_entry *)t->elts; + + for (i = 0; i < t->nelts;) { + if (!strcasecmp (elts[i].key, key)) { + + /* found an element to skip over + * there are any number of ways to remove an element from + * a contiguous block of memory. I've chosen one that + * doesn't do a memcpy/bcopy/array_delete, *shrug*... + */ + for (j = i, k = i + 1; k < t->nelts; ++j, ++k) { + elts[j].key = elts[k].key; + elts[j].val = elts[k].val; + } + --t->nelts; + } + else { + ++i; + } + } +} + +void table_merge (table *t, const char *key, const char *val) +{ + table_entry *elts = (table_entry *)t->elts; + int i; + + for (i = 0; i < t->nelts; ++i) + if (!strcasecmp (elts[i].key, key)) { + elts[i].val = pstrcat (t->pool, elts[i].val, ", ", val, NULL); + return; + } + + elts = (table_entry *)push_array(t); + elts->key = pstrdup (t->pool, key); + elts->val = pstrdup (t->pool, val); +} + +void table_add (table *t, const char *key, const char *val) +{ + table_entry *elts = (table_entry *)t->elts; + + elts = (table_entry *)push_array(t); + elts->key = pstrdup (t->pool, key); + elts->val = pstrdup (t->pool, val); +} + +table* overlay_tables (pool *p, const table *overlay, const table *base) +{ + return append_arrays (p, overlay, base); +} + +/* And now for something completely abstract ... + * + * For each key value given as a vararg: + * run the function pointed to as + * int comp(void *r, char *key, char *value); + * on each valid key-value pair in the table t that matches the vararg key, + * or once for every valid key-value pair if the vararg list is empty, + * until the function returns false (0) or we finish the table. + * + * Note that we restart the traversal for each vararg, which means that + * duplicate varargs will result in multiple executions of the function + * for each matching key. Note also that if the vararg list is empty, + * only one traversal will be made and will cut short if comp returns 0. + * + * Note that the table_get and table_merge functions assume that each key in + * the table is unique (i.e., no multiple entries with the same key). This + * function does not make that assumption, since it (unfortunately) isn't + * true for some of Apache's tables. + * + * Note that rec is simply passed-on to the comp function, so that the + * caller can pass additional info for the task. + */ +void table_do (int (*comp)(void *, const char *, const char *), void *rec, + const table *t, ...) +{ + va_list vp; + char *argp; + table_entry *elts = (table_entry *)t->elts; + int rv, i; + + va_start(vp, t); + + argp = va_arg(vp, char *); + + do { + for (rv = 1, i = 0; rv && (i < t->nelts); ++i) { + if (elts[i].key && (!argp || !strcasecmp(elts[i].key, argp))) { + rv = (*comp)(rec, elts[i].key, elts[i].val); + } + } + } while (argp && ((argp = va_arg(vp, char *)) != NULL)); + + va_end(vp); +} + +/***************************************************************** + * + * Managing generic cleanups. + */ + +struct cleanup { + void *data; + void (*plain_cleanup)(void *); + void (*child_cleanup)(void *); + struct cleanup *next; +}; + +void register_cleanup (pool *p, void *data, void (*plain_cleanup)(void *), + void (*child_cleanup)(void *)) +{ + struct cleanup *c = (struct cleanup *)palloc(p, sizeof (struct cleanup)); + c->data = data; + c->plain_cleanup = plain_cleanup; + c->child_cleanup = child_cleanup; + c->next = p->cleanups; + p->cleanups = c; +} + +void kill_cleanup (pool *p, void *data, void (*cleanup)(void *)) +{ + struct cleanup *c = p->cleanups; + struct cleanup **lastp = &p->cleanups; + + while (c) { + if (c->data == data && c->plain_cleanup == cleanup) { + *lastp = c->next; + break; + } + + lastp = &c->next; + c = c->next; + } +} + +void run_cleanup (pool *p, void *data, void (*cleanup)(void *)) +{ + block_alarms(); /* Run cleanup only once! */ + (*cleanup)(data); + kill_cleanup (p, data, cleanup); + unblock_alarms(); +} + +static void run_cleanups (struct cleanup *c) +{ + while (c) { + (*c->plain_cleanup)(c->data); + c = c->next; + } +} + +static void run_child_cleanups (struct cleanup *c) +{ + while (c) { + (*c->child_cleanup)(c->data); + c = c->next; + } +} + +static void cleanup_pool_for_exec (pool *p) +{ + run_child_cleanups (p->cleanups); + p->cleanups = NULL; + + for (p = p->sub_pools; p; p = p->sub_next) + cleanup_pool_for_exec (p); +} + +void cleanup_for_exec() +{ + block_alarms(); + cleanup_pool_for_exec (permanent_pool); + unblock_alarms(); +} + +/***************************************************************** + * + * Files and file descriptors; these are just an application of the + * generic cleanup interface. + */ + +static void fd_cleanup (void *fdv) { close ((int)fdv); } + +void note_cleanups_for_fd (pool *p, int fd) { + register_cleanup (p, (void *)fd, fd_cleanup, fd_cleanup); +} + +void kill_cleanups_for_fd(pool *p,int fd) + { + kill_cleanup(p,(void *)fd,fd_cleanup); + } + +int popenf(pool *a, const char *name, int flg, int mode) +{ + int fd; + int save_errno; + + block_alarms(); + fd = open(name, flg, mode); + save_errno = errno; + if (fd >= 0) { + fd = ap_slack (fd, AP_SLACK_HIGH); + note_cleanups_for_fd (a, fd); + } + unblock_alarms(); + errno = save_errno; + return fd; +} + +int pclosef(pool *a, int fd) +{ + int res; + int save_errno; + + block_alarms(); + res = close(fd); + save_errno = errno; + kill_cleanup(a, (void *)fd, fd_cleanup); + unblock_alarms(); + errno = save_errno; + return res; +} + +/* Note that we have separate plain_ and child_ cleanups for FILE *s, + * since fclose() would flush I/O buffers, which is extremely undesirable; + * we just close the descriptor. + */ + +static void file_cleanup (void *fpv) { fclose ((FILE *)fpv); } +static void file_child_cleanup (void *fpv) { close (fileno ((FILE *)fpv)); } + +void note_cleanups_for_file (pool *p, FILE *fp) { + register_cleanup (p, (void *)fp, file_cleanup, file_child_cleanup); +} + +FILE *pfopen(pool *a, const char *name, const char *mode) +{ + FILE *fd = NULL; + int baseFlag, desc; + + block_alarms(); + + if (*mode == 'a') { + /* Work around faulty implementations of fopen */ + baseFlag = (*(mode+1) == '+') ? O_RDWR : O_WRONLY; + desc = open(name, baseFlag | O_APPEND | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (desc >= 0) { + desc = ap_slack(desc, AP_SLACK_LOW); + fd = fdopen(desc, mode); + } + } else { + fd = fopen(name, mode); + } + + if (fd != NULL) note_cleanups_for_file (a, fd); + unblock_alarms(); + return fd; +} + +FILE *pfdopen(pool *a,int fd, const char *mode) +{ + FILE *f; + + block_alarms(); + f=fdopen(fd,mode); + if(f != NULL) + note_cleanups_for_file(a,f); + unblock_alarms(); + return f; +} + + +int pfclose(pool *a, FILE *fd) +{ + int res; + + block_alarms(); + res = fclose(fd); + kill_cleanup(a, (void *)fd, file_cleanup); + unblock_alarms(); + return res; +} + +/* + * Here's a pool-based interface to POSIX regex's regcomp(). + * Note that we return regex_t instead of being passed one. + * The reason is that if you use an already-used regex_t structure, + * the memory that you've already allocated gets forgotten, and + * regfree() doesn't clear it. So we don't allow it. + */ + +static void regex_cleanup (void *preg) { regfree ((regex_t *)preg); } + +regex_t *pregcomp(pool *p, const char *pattern, int cflags) { + regex_t *preg = palloc(p, sizeof(regex_t)); + + if (regcomp(preg, pattern, cflags)) + return NULL; + + register_cleanup (p, (void *)preg, regex_cleanup, regex_cleanup); + + return preg; +} + + +void pregfree(pool *p, regex_t *reg) +{ + block_alarms(); + regfree (reg); + kill_cleanup (p, (void *)reg, regex_cleanup); + unblock_alarms(); +} + +/***************************************************************** + * + * More grotty system stuff... subprocesses. Frump. These don't use + * the generic cleanup interface because I don't want multiple + * subprocesses to result in multiple three-second pauses; the + * subprocesses have to be "freed" all at once. If someone comes + * along with another resource they want to allocate which has the + * same property, we might want to fold support for that into the + * generic interface, but for now, it's a special case + */ + +struct process_chain { + pid_t pid; + enum kill_conditions kill_how; + struct process_chain *next; +}; + +void note_subprocess (pool *a, int pid, enum kill_conditions how) +{ + struct process_chain *new = + (struct process_chain *)palloc(a, sizeof(struct process_chain)); + + new->pid = pid; + new->kill_how = how; + new->next = a->subprocesses; + a->subprocesses = new; +} + +int spawn_child_err (pool *p, void (*func)(void *), void *data, + enum kill_conditions kill_how, + FILE **pipe_in, FILE **pipe_out, FILE **pipe_err) +{ + int pid; + int in_fds[2]; + int out_fds[2]; + int err_fds[2]; + int save_errno; + + block_alarms(); + + if (pipe_in && pipe (in_fds) < 0) + { + save_errno = errno; + unblock_alarms(); + errno = save_errno; + return 0; + } + + if (pipe_out && pipe (out_fds) < 0) { + save_errno = errno; + if (pipe_in) { + close (in_fds[0]); close (in_fds[1]); + } + unblock_alarms(); + errno = save_errno; + return 0; + } + + if (pipe_err && pipe (err_fds) < 0) { + save_errno = errno; + if (pipe_in) { + close (in_fds[0]); close (in_fds[1]); + } + if (pipe_out) { + close (out_fds[0]); close (out_fds[1]); + } + unblock_alarms(); + errno = save_errno; + return 0; + } + + if ((pid = fork()) < 0) { + save_errno = errno; + if (pipe_in) { + close (in_fds[0]); close (in_fds[1]); + } + if (pipe_out) { + close (out_fds[0]); close (out_fds[1]); + } + if (pipe_err) { + close (err_fds[0]); close (err_fds[1]); + } + unblock_alarms(); + errno = save_errno; + return 0; + } + + if (!pid) { + /* Child process */ + + if (pipe_out) { + close (out_fds[0]); + dup2 (out_fds[1], STDOUT_FILENO); + close (out_fds[1]); + } + + if (pipe_in) { + close (in_fds[1]); + dup2 (in_fds[0], STDIN_FILENO); + close (in_fds[0]); + } + + if (pipe_err) { + close (err_fds[0]); + dup2 (err_fds[1], STDERR_FILENO); + close (err_fds[1]); + } + + /* HP-UX SIGCHLD fix goes here, if someone will remind me what it is... */ + signal (SIGCHLD, SIG_DFL); /* Was that it? */ + + func (data); + exit (0); /* Should never get here... */ + } + + /* Parent process */ + + note_subprocess (p, pid, kill_how); + + if (pipe_out) { + close (out_fds[1]); +#ifdef __EMX__ + /* Need binary mode set for OS/2. */ + *pipe_out = fdopen (out_fds[0], "rb"); +#else + *pipe_out = fdopen (out_fds[0], "r"); +#endif + + if (*pipe_out) note_cleanups_for_file (p, *pipe_out); + } + + if (pipe_in) { + close (in_fds[0]); +#ifdef __EMX__ + /* Need binary mode set for OS/2 */ + *pipe_in = fdopen (in_fds[1], "wb"); +#else + *pipe_in = fdopen (in_fds[1], "w"); +#endif + + if (*pipe_in) note_cleanups_for_file (p, *pipe_in); + } + + if (pipe_err) { + close (err_fds[1]); +#ifdef __EMX__ + /* Need binary mode set for OS/2. */ + *pipe_err = fdopen (err_fds[0], "rb"); +#else + *pipe_err = fdopen (err_fds[0], "r"); +#endif + + if (*pipe_err) note_cleanups_for_file (p, *pipe_err); + } + + unblock_alarms(); + return pid; +} + +static void free_proc_chain (struct process_chain *procs) +{ + /* Dispose of the subprocesses we've spawned off in the course of + * whatever it was we're cleaning up now. This may involve killing + * some of them off... + */ + + struct process_chain *p; + int need_timeout = 0; + int status; + + if (procs == NULL) return; /* No work. Whew! */ + + /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL + * dance with any of the processes we're cleaning up. If we've got + * any kill-on-sight subprocesses, ditch them now as well, so they + * don't waste any more cycles doing whatever it is that they shouldn't + * be doing anymore. + */ + +#ifndef NEED_WAITPID + /* Pick up all defunct processes */ + for (p = procs; p; p = p->next) { + if (waitpid (p->pid, (int *) 0, WNOHANG) > 0) { + p->kill_how = kill_never; + } + } +#endif + + for (p = procs; p; p = p->next) { + if (p->kill_how == kill_after_timeout) { + /* Subprocess may be dead already. Only need the timeout if not. */ + if (kill (p->pid, SIGTERM) != -1) + need_timeout = 1; + } else if (p->kill_how == kill_always) { + kill (p->pid, SIGKILL); + } + } + + /* Sleep only if we have to... */ + + if (need_timeout) sleep (3); + + /* OK, the scripts we just timed out for have had a chance to clean up + * --- now, just get rid of them, and also clean up the system accounting + * goop... + */ + + for (p = procs; p; p = p->next){ + + if (p->kill_how == kill_after_timeout) + kill (p->pid, SIGKILL); + + if (p->kill_how != kill_never) + waitpid (p->pid, &status, 0); + } +} + diff --git a/usr.sbin/httpd/src/alloc.h b/usr.sbin/httpd/src/alloc.h new file mode 100644 index 00000000000..858bf2cb549 --- /dev/null +++ b/usr.sbin/httpd/src/alloc.h @@ -0,0 +1,252 @@ + +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * Resource allocation routines... + * + * designed so that we don't have to keep track of EVERYTHING so that + * it can be explicitly freed later (a fundamentally unsound strategy --- + * particularly in the presence of die()). + * + * Instead, we maintain pools, and allocate items (both memory and I/O + * handlers) from the pools --- currently there are two, one for per + * transaction info, and one for config info. When a transaction is over, + * we can delete everything in the per-transaction pool without fear, and + * without thinking too hard about it either. + * + * rst + */ + +/* Arenas for configuration info and transaction info + * --- actual layout of the pool structure is private to + * alloc.c. + */ + +typedef struct pool pool; + +extern pool *permanent_pool; +void init_alloc(); /* Set up everything */ +pool *make_sub_pool (pool *); /* All pools are subpools of permanent_pool */ +void destroy_pool (pool *); + +/* Clearing out EVERYTHING in an pool... destroys any sub-pools */ + +void clear_pool (struct pool *); + +/* Preparing for exec() --- close files, etc., but *don't* flush I/O + * buffers, *don't* wait for subprocesses, and *don't* free any memory. + */ + +void cleanup_for_exec (); + +/* routines to allocate memory from an pool... */ + +void *palloc(struct pool *, int nbytes); +void *pcalloc(struct pool *, int nbytes); +extern char *pstrdup(struct pool *, const char *s); +extern char *pstrndup(struct pool *, const char *s, int n); +char *pstrcat(struct pool *, ...); /* all '...' must be char* */ + +/* array and alist management... keeping lists of things. + * Common enough to want common support code ... + */ + +typedef struct { + pool *pool; + int elt_size; + int nelts; + int nalloc; + char *elts; +} array_header; + +array_header *make_array (pool *p, int nelts, int elt_size); +void *push_array (array_header *); +void array_cat (array_header *dst, const array_header *src); +array_header *append_arrays (pool *, const array_header *, + const array_header *); + +/* copy_array copies the *entire* array. copy_array_hdr just copies + * the header, and arranges for the elements to be copied if (and only + * if) the code subsequently does a push or arraycat. + */ + +array_header *copy_array (pool *p, const array_header *src); +array_header *copy_array_hdr (pool *p, const array_header *src); + + +/* Tables. Implemented alist style, for now, though we try to keep + * it so that imposing a hash table structure on top in the future + * wouldn't be *too* hard... + * + * Note that key comparisons for these are case-insensitive, largely + * because that's what's appropriate and convenient everywhere they're + * currently being used... + */ + +typedef array_header table; + +typedef struct { + char *key; /* maybe NULL in future; + * check when iterating thru table_elts + */ + char *val; +} table_entry; + +table *make_table (pool *p, int nelts); +table *copy_table (pool *p, const table *); +void clear_table (table *); +char *table_get (const table *, const char *); +void table_set (table *, const char *name, const char *val); +void table_merge (table *, const char *name, const char *more_val); +void table_unset (table *, const char *key); +void table_add (table *, const char *name, const char *val); +void table_do (int (*comp)(void *, const char *, const char *), void *rec, + const table *t, ...); + +table *overlay_tables (pool *p, const table *overlay, const table *base); + +array_header *table_elts (table *); + +#define is_empty_table(t) (((t) == NULL)||((t)->nelts == 0)) + +/* routines to remember allocation of other sorts of things... + * generic interface first. Note that we want to have two separate + * cleanup functions in the general case, one for exec() preparation, + * to keep CGI scripts and the like from inheriting access to things + * they shouldn't be able to touch, and one for actually cleaning up, + * when the actual server process wants to get rid of the thing, + * whatever it is. + * + * kill_cleanup disarms a cleanup, presumably because the resource in + * question has been closed, freed, or whatever, and it's scarce + * enough to want to reclaim (e.g., descriptors). It arranges for the + * resource not to be cleaned up a second time (it might have been + * reallocated). run_cleanup does the same, but runs it first. + * + * Cleanups are identified for purposes of finding & running them off by the + * plain_cleanup and data, which should presumably be unique. + * + * NB any code which invokes register_cleanup or kill_cleanup directly + * is a critical section which should be guarded by block_alarms() and + * unblock_alarms() below... + */ + +void register_cleanup (pool *p, void *data, + void (*plain_cleanup)(void *), + void (*child_cleanup)(void *)); + +void kill_cleanup (pool *p, void *data, void (*plain_cleanup)(void *)); +void run_cleanup (pool *p, void *data, void (*cleanup)(void *)); + +/* The time between when a resource is actually allocated, and when it + * its cleanup is registered is a critical section, during which the + * resource could leak if we got interrupted or timed out. So, anything + * which registers cleanups should bracket resource allocation and the + * cleanup registry with these. (This is done internally by run_cleanup). + * + * NB they are actually implemented in http_main.c, since they are bound + * up with timeout handling in general... + */ + +extern void block_alarms(); +extern void unblock_alarms(); + +/* Common cases which want utility support.. + * the note_cleanups_for_foo routines are for + */ + +FILE *pfopen(struct pool *, const char *name, const char *fmode); +FILE *pfdopen(struct pool *, int fd, const char *fmode); +int popenf(struct pool *, const char *name, int flg, int mode); + +void note_cleanups_for_file (pool *, FILE *); +void note_cleanups_for_fd (pool *, int); +void kill_cleanups_for_fd (pool *p, int fd); + +regex_t *pregcomp (pool *p, const char *pattern, int cflags); +void pregfree (pool *p, regex_t *reg); + +/* routines to note closes... file descriptors are constrained enough + * on some systems that we want to support this. + */ + +int pfclose(struct pool *, FILE *); +int pclosef(struct pool *, int fd); + +/* ... even child processes (which we may want to wait for, + * or to kill outright, on unexpected termination). + * + * spawn_child is a utility routine which handles an awful lot of + * the rigamarole associated with spawning a child --- it arranges + * for pipes to the child's stdin and stdout, if desired (if not, + * set the associated args to NULL). It takes as args a function + * to call in the child, and an argument to be passed to the function. + */ + +enum kill_conditions { kill_never, kill_always, kill_after_timeout, just_wait}; + +int spawn_child_err (pool *, void (*)(void *), void *, + enum kill_conditions, FILE **pipe_in, FILE **pipe_out, + FILE **pipe_err); +#define spawn_child(p,f,v,k,in,out) spawn_child_err(p,f,v,k,in,out,NULL) + +/* magic numbers --- min free bytes to consider a free pool block useable, + * and the min amount to allocate if we have to go to malloc() */ + +#define BLOCK_MINFREE 4096 +#define BLOCK_MINALLOC 8192 + +/* Finally, some accounting */ + +long bytes_in_pool(pool *p); +long bytes_in_free_blocks(); diff --git a/usr.sbin/httpd/src/buff.c b/usr.sbin/httpd/src/buff.c new file mode 100644 index 00000000000..48872043359 --- /dev/null +++ b/usr.sbin/httpd/src/buff.c @@ -0,0 +1,1011 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +#include "conf.h" +#include "alloc.h" +#include "buff.h" + +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#ifndef NO_UNISTD_H +#include <unistd.h> +#endif +#ifndef NO_WRITEV +#include <sys/types.h> +#include <sys/uio.h> +#endif + +#ifdef HAVE_BSTRING_H +#include <bstring.h> /* for IRIX, FD_SET calls bzero() */ +#endif + +#define DEFAULT_BUFSIZE (4096) +/* This must be enough to represent (DEFAULT_BUFSIZE - 3) in hex, + * plus two extra characters. + */ +#define CHUNK_HEADER_SIZE (5) + +/* + * Buffered I/O routines. + * These are a replacement for the stdio routines. + * Advantages: + * Known semantics for handling of file-descriptors (on close etc.) + * No problems reading and writing simultanously to the same descriptor + * No limits on the number of open file handles. + * Only uses memory resources; no need to ensure the close routine + * is called. + * Extra code could be inserted between the buffered and un-buffered routines. + * Timeouts could be handled by using select or poll before read or write. + * Extra error handling could be introduced; e.g. + * keep an address to which we should longjump(), or + * keep a stack of routines to call on error. + */ + +/* Notes: + * On reading EOF, EOF will set in the flags and no further Input will + * be done. + * + * On an error except for EAGAIN, ERROR will be set in the flags and no + * futher I/O will be done + */ + +static void +doerror(BUFF *fb, int err) +{ + int errsave = errno; /* Save errno to prevent overwriting it below */ + + if (err == B_RD) + fb->flags |= B_RDERR; + else + fb->flags |= B_WRERR; + if (fb->error != NULL) (*fb->error)(fb, err, fb->error_data); + + errno = errsave; +} + +/* Buffering routines */ +/* + * Create a new buffered stream + */ +BUFF * +bcreate(pool *p, int flags) +{ + BUFF *fb; + + fb = palloc(p, sizeof(BUFF)); + fb->pool=p; + fb->bufsiz = DEFAULT_BUFSIZE; + fb->flags = flags & B_RDWR; + + if (flags & B_RD) fb->inbase = palloc(p, fb->bufsiz); + else fb->inbase = NULL; + + /* overallocate so that we can put a chunk trailer of CRLF into this + * buffer */ + if (flags & B_WR) fb->outbase = palloc(p, fb->bufsiz + 2); + else fb->outbase = NULL; + + fb->inptr = fb->inbase; + + fb->incnt = 0; + fb->outcnt = 0; + fb->outchunk = -1; + fb->error = NULL; + fb->bytes_sent = 0L; + + fb->fd = -1; + fb->fd_in = -1; + + return fb; +} + +/* + * Push some I/O file descriptors onto the stream + */ +void +bpushfd(BUFF *fb, int fd_in, int fd_out) +{ + fb->fd = fd_out; + fb->fd_in = fd_in; +} + +int +bsetopt(BUFF *fb, int optname, const void *optval) +{ + if (optname == BO_BYTECT) + { + fb->bytes_sent = *(const long int *)optval - (long int)fb->outcnt;; + return 0; + } else + { + errno = EINVAL; + return -1; + } +} + +int +bgetopt(BUFF *fb, int optname, void *optval) +{ + if (optname == BO_BYTECT) + { + long int bs=fb->bytes_sent + fb->outcnt; + if (bs < 0L) bs = 0L; + *(long int *)optval = bs; + return 0; + } else + { + errno = EINVAL; + return -1; + } +} + + +static int bflush_core(BUFF *fb); + +/* + * Start chunked encoding. + * + * Note that in order for bputc() to be an efficient macro we have to + * guarantee that start_chunk() has always been called on the buffer before we + * leave any routine in this file. Said another way, if a routine here uses + * end_chunk() and writes something on the wire, then it has to call + * start_chunk() or set an error condition before returning. + */ +static void +start_chunk( BUFF *fb ) +{ + if (fb->outchunk != -1) { + /* already chunking */ + return; + } + if (!(fb->flags & B_WR) || (fb->flags & (B_WRERR|B_EOUT))) { + /* unbuffered writes */ + return; + } + + /* we need at least the header_len + at least 1 data byte + * remember that we've overallocated fb->outbase so that we can always + * fit the two byte CRLF trailer + */ + if( fb->bufsiz - fb->outcnt < CHUNK_HEADER_SIZE + 1 ) { + bflush_core(fb); + } + /* assume there's enough space now */ + fb->outchunk = fb->outcnt; + fb->outcnt += CHUNK_HEADER_SIZE; +} + + +/* + * end a chunk -- tweak the chunk_header from start_chunk, and add a trailer + */ +static void +end_chunk( BUFF *fb ) +{ + int i; + char *strp; + + if( fb->outchunk == -1 ) { + /* not chunking */ + return; + } + + if( fb->outchunk + CHUNK_HEADER_SIZE == fb->outcnt ) { + /* nothing was written into this chunk, and we can't write a 0 size + * chunk because that signifies EOF, so just erase it + */ + fb->outcnt = fb->outchunk; + fb->outchunk = -1; + return; + } + + /* we know this will fit because of how we wrote it in start_chunk() */ + i = ap_snprintf( (char *)&fb->outbase[fb->outchunk], + CHUNK_HEADER_SIZE, + "%x", fb->outcnt - fb->outchunk - CHUNK_HEADER_SIZE ); + + /* we may have to tack some trailing spaces onto the number we just wrote + * in case it was smaller than our estimated size. We've also written + * a \0 into the buffer with ap_snprintf so we might have to put a + * \r back in. + */ + strp = &fb->outbase[fb->outchunk + i]; + while (i < CHUNK_HEADER_SIZE - 2) { + *strp++ = ' '; + ++i; + } + *strp++ = '\015'; + *strp = '\012'; + + /* tack on the trailing CRLF, we've reserved room for this */ + fb->outbase[fb->outcnt++] = '\015'; + fb->outbase[fb->outcnt++] = '\012'; + + fb->outchunk = -1; +} + + +/* + * Set a flag on (1) or off (0). + */ +int bsetflag(BUFF *fb, int flag, int value) +{ + if (value) { + fb->flags |= flag; + if( flag & B_CHUNK ) { + start_chunk(fb); + } + } else { + fb->flags &= ~flag; + if( flag & B_CHUNK ) { + end_chunk(fb); + } + } + return value; +} + + +/* + * This is called instead of read() everywhere in here. It implements + * the B_SAFEREAD functionality -- which is to force a flush() if a read() + * would block. It also deals with the EINTR errno result from read(). + * return code is like read() except EINTR is eliminated. + */ +static int +saferead( BUFF *fb, void *buf, int nbyte ) +{ + int rv; + + if( fb->flags & B_SAFEREAD ) { + fd_set fds; + struct timeval tv; + + /* test for a block */ + do { + FD_ZERO( &fds ); + FD_SET( fb->fd_in, &fds ); + tv.tv_sec = 0; + tv.tv_usec = 0; +#ifdef SELECT_NEEDS_CAST + rv = select( fb->fd_in + 1, (int *)&fds, NULL, NULL, &tv ); +#else + rv = select( fb->fd_in + 1, &fds, NULL, NULL, &tv ); +#endif + } while( rv < 0 && errno == EINTR ); + /* treat any error as if it would block as well */ + if( rv != 1 ) { + bflush(fb); + } + } + do { + rv = read( fb->fd_in, buf, nbyte ); + } while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT)); + return( rv ); +} + + +/* + * Read up to nbyte bytes into buf. + * If fewer than byte bytes are currently available, then return those. + * Returns 0 for EOF, -1 for error. + */ +int +bread(BUFF *fb, void *buf, int nbyte) +{ + int i, nrd; + + if (fb->flags & B_RDERR) return -1; + if (nbyte == 0) return 0; + + if (!(fb->flags & B_RD)) + { +/* Unbuffered reading */ + i = saferead( fb, buf, nbyte ); + if (i == -1 && errno != EAGAIN) doerror(fb, B_RD); + return i; + } + + nrd = fb->incnt; +/* can we fill the buffer */ + if (nrd >= nbyte) + { + memcpy(buf, fb->inptr, nbyte); + fb->incnt = nrd - nbyte; + fb->inptr += nbyte; + return nbyte; + } + + if (nrd > 0) + { + memcpy(buf, fb->inptr, nrd); + nbyte -= nrd; + buf = nrd + (char *)buf; + fb->incnt = 0; + } + if (fb->flags & B_EOF) return nrd; + +/* do a single read */ + if (nbyte >= fb->bufsiz) + { +/* read directly into buffer */ + i = saferead( fb, buf, nbyte ); + if (i == -1) + { + if (nrd == 0) + { + if (errno != EAGAIN) doerror(fb, B_RD); + return -1; + } + else return nrd; + } else if (i == 0) fb->flags |= B_EOF; + } else + { +/* read into hold buffer, then memcpy */ + fb->inptr = fb->inbase; + i = saferead( fb, fb->inptr, fb->bufsiz ); + if (i == -1) + { + if (nrd == 0) + { + if (errno != EAGAIN) doerror(fb, B_RD); + return -1; + } + else return nrd; + } else if (i == 0) fb->flags |= B_EOF; + fb->incnt = i; + if (i > nbyte) i = nbyte; + memcpy(buf, fb->inptr, i); + fb->incnt -= i; + fb->inptr += i; + } + return nrd + i; +} + + +/* + * Reads from the stream into the array pointed to by buff, until + * a (CR)LF sequence is read, or end-of-file condition is encountered + * or until n-1 bytes have been stored in buff. If a CRLF sequence is + * read, it is replaced by a newline character. The string is then + * terminated with a null character. + * + * Returns the number of bytes stored in buff, or zero on end of + * transmission, or -1 on an error. + * + * Notes: + * If null characters are exepected in the data stream, then + * buff should not be treated as a null terminated C string; instead + * the returned count should be used to determine the length of the + * string. + * CR characters in the byte stream not immediately followed by a LF + * will be preserved. + */ +int +bgets(char *buff, int n, BUFF *fb) +{ + int i, ch, ct; + +/* Can't do bgets on an unbuffered stream */ + if (!(fb->flags & B_RD)) + { + errno = EINVAL; + return -1; + } + if (fb->flags & B_RDERR) return -1; + + ct = 0; + i = 0; + for (;;) + { + if (i == fb->incnt) + { +/* no characters left */ + fb->inptr = fb->inbase; + fb->incnt = 0; + if (fb->flags & B_EOF) break; + i = saferead( fb, fb->inptr, fb->bufsiz ); + if (i == -1) + { + buff[ct] = '\0'; + if (ct == 0) + { + if (errno != EAGAIN) doerror(fb, B_RD); + return -1; + } + else return ct; + } + fb->incnt = i; + if (i == 0) + { + fb->flags |= B_EOF; + break; /* EOF */ + } + i = 0; + continue; /* restart with the new data */ + } + + ch = fb->inptr[i++]; + if (ch == '\012') /* got LF */ + { + if (ct == 0) buff[ct++] = '\n'; +/* if just preceeded by CR, replace CR with LF */ + else if (buff[ct-1] == '\015') buff[ct-1] = '\n'; + else if (ct < n-1) buff[ct++] = '\n'; + else i--; /* no room for LF */ + break; + } + if (ct == n-1) + { + i--; /* push back ch */ + break; + } + + buff[ct++] = ch; + } + fb->incnt -= i; + fb->inptr += i; + + buff[ct] = '\0'; + return ct; +} + +/* + * Looks at the stream fb and places the first character into buff + * without removing it from the stream buffer. + * + * Returns 1 on success, zero on end of transmission, or -1 on an error. + * + */ +int blookc(char *buff, BUFF *fb) +{ + int i; + + *buff = '\0'; + + if (!(fb->flags & B_RD)) { /* Can't do blookc on an unbuffered stream */ + errno = EINVAL; + return -1; + } + if (fb->flags & B_RDERR) return -1; + + if (fb->incnt == 0) { /* no characters left in stream buffer */ + fb->inptr = fb->inbase; + if (fb->flags & B_EOF) + return 0; + + i = saferead( fb, fb->inptr, fb->bufsiz ); + + if (i == -1) { + if (errno != EAGAIN) + doerror(fb, B_RD); + return -1; + } + if (i == 0) { + fb->flags |= B_EOF; + return 0; /* EOF */ + } + else fb->incnt = i; + } + + *buff = fb->inptr[0]; + return 1; +} + +/* + * Skip data until a linefeed character is read + * Returns 1 on success, 0 if no LF found, or -1 on error + */ +int +bskiplf(BUFF *fb) +{ + unsigned char *x; + int i; + +/* Can't do bskiplf on an unbuffered stream */ + if (!(fb->flags & B_RD)) + { + errno = EINVAL; + return -1; + } + if (fb->flags & B_RDERR) return -1; + + for (;;) + { + x = (unsigned char *)memchr(fb->inptr, '\012', fb->incnt); + if (x != NULL) + { + x++; + fb->incnt -= x - fb->inptr; + fb->inptr = x; + return 1; + } + + fb->inptr = fb->inbase; + fb->incnt = 0; + if (fb->flags & B_EOF) return 0; + i = saferead( fb, fb->inptr, fb->bufsiz ); + if (i == 0) fb->flags |= B_EOF; + if (i == -1 && errno != EAGAIN) doerror(fb, B_RD); + if (i == 0 || i == -1) return i; + fb->incnt = i; + } +} + +/* + * Emtpy the buffer after putting a single character in it + */ +int +bflsbuf(int c, BUFF *fb) +{ + char ss[1]; + + ss[0] = c; + return bwrite(fb, ss, 1); +} + +/* + * Fill the buffer and read a character from it + */ +int +bfilbuf(BUFF *fb) +{ + int i; + char buf[1]; + + i = bread(fb, buf, 1); + if (i == 0) errno = 0; /* no error; EOF */ + if (i != 1) return EOF; + else return buf[0]; +} + + +/* + * When doing chunked encodings we really have to write everything in the + * chunk before proceeding onto anything else. This routine either writes + * nbytes and returns 0 or returns -1 indicating a failure. + * + * This is *seriously broken* if used on a non-blocking fd. It will poll. + */ +static int +write_it_all(BUFF *fb, const void *buf, int nbyte) +{ + int i; + + if (fb->flags & (B_WRERR|B_EOUT)) + return -1; + + while (nbyte > 0) { + i = write(fb->fd, buf, nbyte); + if (i < 0) { + if (errno != EAGAIN && errno != EINTR) { + doerror (fb, B_WR); + return -1; + } + } + else { + nbyte -= i; + buf = i + (const char *)buf; + } + if (fb->flags & B_EOUT) + return -1; + } + return 0; +} + + +/* + * A hook to write() that deals with chunking. This is really a protocol- + * level issue, but we deal with it here because it's simpler; this is + * an interim solution pending a complete rewrite of all this stuff in + * 2.0, using something like sfio stacked disciplines or BSD's funopen(). + */ +static int +bcwrite(BUFF *fb, const void *buf, int nbyte) +{ + char chunksize[16]; /* Big enough for practically anything */ + int rv; +#ifndef NO_WRITEV + struct iovec vec[3]; + int i; +#endif + + if (fb->flags & (B_WRERR|B_EOUT)) + return -1; + + if (!(fb->flags & B_CHUNK)) { + do rv = write(fb->fd, buf, nbyte); + while (rv == -1 && errno == EINTR && !(fb->flags & B_EOUT)); + if (rv == -1) { + if (errno != EAGAIN) { + doerror (fb, B_WR); + } + return -1; + } else if (rv == 0) { + errno = EAGAIN; + } + return rv; + } + +#ifdef NO_WRITEV + /* without writev() this has poor performance, too bad */ + + ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", nbyte); + if (write_it_all(fb, chunksize, strlen(chunksize)) == -1) + return -1; + if (write_it_all(fb, buf, nbyte) == -1) + return -1; + if (write_it_all(fb, "\015\012", 2) == -1) + return -1; + return nbyte; +#else + +#define NVEC (sizeof(vec)/sizeof(vec[0])) + + vec[0].iov_base = chunksize; + vec[0].iov_len = ap_snprintf(chunksize, sizeof(chunksize), "%x\015\012", + nbyte); + vec[1].iov_base = (void *)buf; /* cast is to avoid const warning */ + vec[1].iov_len = nbyte; + vec[2].iov_base = "\r\n"; + vec[2].iov_len = 2; + /* while it's nice an easy to build the vector and crud, it's painful + * to deal with a partial writev() + */ + for( i = 0; i < NVEC; ) { + do { + rv = writev( fb->fd, &vec[i], NVEC - i ); + } while ((rv <= 0) + && !(fb->flags & B_EOUT) + && (errno == EINTR || errno == EAGAIN || rv == 0)); + if (rv == -1) { + doerror (fb, B_WR); + return -1; + } + /* recalculate vec to deal with partial writes */ + while (rv > 0) { + if( rv <= vec[i].iov_len ) { + vec[i].iov_base = (char *)vec[i].iov_base + rv; + vec[i].iov_len -= rv; + rv = 0; + if( vec[i].iov_len == 0 ) { + ++i; + } + } else { + rv -= vec[i].iov_len; + ++i; + } + } + if (fb->flags & B_EOUT) + return -1; + } + /* if we got here, we wrote it all */ + return nbyte; +#undef NVEC +#endif +} + + +/* + * Write nbyte bytes. + * Only returns fewer than nbyte if an error ocurred. + * Returns -1 if no bytes were written before the error ocurred. + * It is worth noting that if an error occurs, the buffer is in an unknown + * state. + */ +int +bwrite(BUFF *fb, const void *buf, int nbyte) +{ + int i, nwr, useable_bufsiz; + + if (fb->flags & (B_WRERR|B_EOUT)) return -1; + if (nbyte == 0) return 0; + + if (!(fb->flags & B_WR)) + { +/* unbuffered write -- have to use bcwrite since we aren't taking care + * of chunking any other way */ + i = bcwrite(fb, buf, nbyte); + if (i <= 0) { + return -1; + } + fb->bytes_sent += i; + if (fb->flags & B_EOUT) + return -1; + else + return i; + } + +/* + * Whilst there is data in the buffer, keep on adding to it and writing it + * out + */ + nwr = 0; + while (fb->outcnt > 0) + { +/* can we accept some data? */ + i = fb->bufsiz - fb->outcnt; + if (i > 0) + { + if (i > nbyte) i = nbyte; + memcpy(fb->outbase + fb->outcnt, buf, i); + fb->outcnt += i; + nbyte -= i; + buf = i + (const char *)buf; + nwr += i; + if (nbyte == 0) return nwr; /* return if none left */ + } + +/* the buffer must be full */ + if (fb->flags & B_CHUNK) { + end_chunk(fb); + /* it is just too painful to try to re-cram the buffer while + * chunking + */ + if (write_it_all(fb, fb->outbase, fb->outcnt) == -1) { + /* we cannot continue after a chunked error */ + return -1; + } + fb->bytes_sent += fb->outcnt; + fb->outcnt = 0; + break; + } + do { + i = write(fb->fd, fb->outbase, fb->outcnt); + } while (i == -1 && errno == EINTR && !(fb->flags & B_EOUT)); + if (i <= 0) { + if (i == 0) /* return of 0 means non-blocking */ + errno = EAGAIN; + if (nwr == 0) { + if (errno != EAGAIN) doerror(fb, B_WR); + return -1; + } + else return nwr; + } + fb->bytes_sent += i; + + /* deal with a partial write */ + if (i < fb->outcnt) + { + int j, n=fb->outcnt; + unsigned char *x=fb->outbase; + for (j=i; j < n; j++) x[j-i] = x[j]; + fb->outcnt -= i; + } + else + fb->outcnt = 0; + + if (fb->flags & B_EOUT) + return -1; + } +/* we have emptied the file buffer. Now try to write the data from the + * original buffer until there is less than bufsiz left. Note that we + * use bcwrite() to do this for us, it will do the chunking so that + * we don't have to dink around building a chunk in our own buffer. + * Remember we may not be able to use the entire buffer if we're + * chunking. + */ + useable_bufsiz = fb->bufsiz; + if (fb->flags & B_CHUNK) useable_bufsiz -= CHUNK_HEADER_SIZE; + while (nbyte >= useable_bufsiz) + { + i = bcwrite(fb, buf, nbyte); + if (i <= 0) { + if (nwr == 0) { + return -1; + } + else return nwr; + } + fb->bytes_sent += i; + + buf = i + (const char *)buf; + nwr += i; + nbyte -= i; + + if (fb->flags & B_EOUT) + return -1; + } +/* copy what's left to the file buffer */ + fb->outcnt = 0; + if( fb->flags & B_CHUNK ) start_chunk( fb ); + if (nbyte > 0) memcpy(fb->outbase + fb->outcnt, buf, nbyte); + fb->outcnt += nbyte; + nwr += nbyte; + return nwr; +} + +static int bflush_core(BUFF *fb) +{ + int i; + + while (fb->outcnt > 0) + { + do { + i = write(fb->fd, fb->outbase, fb->outcnt); + } while ((i <= 0) + && !(fb->flags & B_EOUT) + && (errno == EINTR || errno == EAGAIN || i == 0)); + + if (i == 0) { + errno = EAGAIN; + return -1; /* return of 0 means B_EOUT and non-blocking */ + } + else if (i < 0) { + if (errno != EAGAIN) doerror(fb, B_WR); + return -1; + } + fb->bytes_sent += i; + + /* + * We should have written all the data, but if the fd was in a + * strange (non-blocking) mode, then we might not have done so. + */ + if (i < fb->outcnt) + { + int j, n=fb->outcnt; + unsigned char *x=fb->outbase; + for (j=i; j < n; j++) x[j-i] = x[j]; + } + fb->outcnt -= i; + + /* If a soft timeout occurs while flushing, the handler should + * have set the buffer flag B_EOUT. + */ + if (fb->flags & B_EOUT) + return -1; + } + + return 0; +} + +/* + * Flushes the buffered stream. + * Returns 0 on success or -1 on error + */ +int bflush(BUFF *fb) +{ + int ret; + + if (!(fb->flags & B_WR) || (fb->flags & B_EOUT)) return 0; + + if (fb->flags & B_WRERR) return -1; + + if (fb->flags & B_CHUNK) end_chunk(fb); + + ret = bflush_core(fb); + + if (ret == 0 && (fb->flags & B_CHUNK)) { + start_chunk(fb); + } + + return ret; +} + +/* + * Flushes and closes the file, even if an error occurred. + * Discards an data that was not read, or not written by bflush() + * Sets the EOF flag to indicate no futher data can be read, + * and the EOUT flag to indicate no further data can be written. + */ +int +bclose(BUFF *fb) +{ + int rc1, rc2, rc3; + + if (fb->flags & B_WR) rc1 = bflush(fb); + else rc1 = 0; + rc2 = close(fb->fd); + if (fb->fd_in != fb->fd) rc3 = close(fb->fd_in); + else rc3 = 0; + + fb->inptr = fb->inbase; + fb->incnt = 0; + fb->outcnt = 0; + + fb->flags |= B_EOF | B_EOUT; + fb->fd = -1; + fb->fd_in = -1; + + if (rc1 != 0) return rc1; + else if (rc2 != 0) return rc2; + else return rc3; +} + +/* + * returns the number of bytes written or -1 on error + */ +int +bputs(const char *x, BUFF *fb) +{ + int i, j=strlen(x); + i = bwrite(fb, x, j); + if (i != j) return -1; + else return j; +} + +/* + * returns the number of bytes written or -1 on error + */ +int +bvputs(BUFF *fb, ...) +{ + int i, j, k; + va_list v; + const char *x; + + va_start(v, fb); + for (k=0;;) + { + x = va_arg(v, const char *); + if (x == NULL) break; + j = strlen(x); + i = bwrite(fb, x, j); + if (i != j) + { + va_end(v); + return -1; + } + k += i; + } + + va_end(v); + + return k; +} + +void +bonerror(BUFF *fb, void (*error)(BUFF *, int, void *), void *data) +{ + fb->error = error; + fb->error_data = data; +} diff --git a/usr.sbin/httpd/src/buff.h b/usr.sbin/httpd/src/buff.h new file mode 100644 index 00000000000..aa1926723d3 --- /dev/null +++ b/usr.sbin/httpd/src/buff.h @@ -0,0 +1,139 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +#include <stdarg.h> + +/* Reading is buffered */ +#define B_RD (1) +/* Writing is buffered */ +#define B_WR (2) +#define B_RDWR (3) +/* At end of file, or closed stream; no further input allowed */ +#define B_EOF (4) +/* No further output possible */ +#define B_EOUT (8) +/* A read error has occurred */ +#define B_RDERR (16) +/* A write error has occurred */ +#define B_WRERR (32) +#ifdef B_ERROR /* in SVR4: sometimes defined in /usr/include/sys/buf.h */ +#undef B_ERROR /* avoid "warning: `B_ERROR' redefined" */ +#endif +#define B_ERROR (48) +/* Use chunked writing */ +#define B_CHUNK (64) +/* bflush() if a read would block */ +#define B_SAFEREAD (128) + +typedef struct buff_struct BUFF; + +struct buff_struct +{ + int flags; /* flags */ + unsigned char *inptr; /* pointer to next location to read */ + int incnt; /* number of bytes left to read from input buffer; + * always 0 if had a read error */ + int outchunk; /* location of chunk header when chunking */ + int outcnt; /* number of byte put in output buffer */ + unsigned char *inbase; + unsigned char *outbase; + int bufsiz; + void (*error)(BUFF *fb, int op, void *data); + void *error_data; + long int bytes_sent; /* number of bytes actually written */ + + pool *pool; + +/* could also put pointers to the basic I/O routines here */ + int fd; /* the file descriptor */ + int fd_in; /* input file descriptor, if different */ +}; + +/* Options to bset/getopt */ +#define BO_BYTECT (1) + +/* Stream creation and modification */ +extern BUFF *bcreate(pool *p, int flags); +extern void bpushfd(BUFF *fb, int fd_in, int fd_out); +extern int bsetopt(BUFF *fb, int optname, const void *optval); +extern int bgetopt(BUFF *fb, int optname, void *optval); +extern int bsetflag(BUFF *fb, int flag, int value); +extern int bclose(BUFF *fb); + +#define bgetflag(fb, flag) ((fb)->flags & (flag)) + +/* Error handling */ +extern void bonerror(BUFF *fb, void (*error)(BUFF *, int, void *), + void *data); + +/* I/O */ +extern int bread(BUFF *fb, void *buf, int nbyte); +extern int bgets(char *s, int n, BUFF *fb); +extern int blookc(char *buff, BUFF *fb); +extern int bskiplf(BUFF *fb); +extern int bwrite(BUFF *fb, const void *buf, int nbyte); +extern int bflush(BUFF *fb); +extern int bputs(const char *x, BUFF *fb); +extern int bvputs(BUFF *fb, ...); +extern int bprintf(BUFF *fb,const char *fmt,...); +extern int vbprintf(BUFF *fb,const char *fmt,va_list vlist); + +/* Internal routines */ +extern int bflsbuf(int c, BUFF *fb); +extern int bfilbuf(BUFF *fb); + +#define bgetc(fb) ( ((fb)->incnt == 0) ? bfilbuf(fb) : \ + ((fb)->incnt--, *((fb)->inptr++)) ) + +#define bputc(c, fb) ((((fb)->flags & (B_EOUT|B_WRERR|B_WR)) != B_WR || \ + (fb)->outcnt == (fb)->bufsiz) ? bflsbuf(c, (fb)) : \ + ((fb)->outbase[(fb)->outcnt++] = (c), 0)) diff --git a/usr.sbin/httpd/src/conf.h b/usr.sbin/httpd/src/conf.h new file mode 100644 index 00000000000..03c90748dfe --- /dev/null +++ b/usr.sbin/httpd/src/conf.h @@ -0,0 +1,763 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * conf.h: system-dependant #defines and includes... + * See PORTING for a listing of what they mean + */ + +#if !defined(QNX) && !defined(MPE) +#include <sys/param.h> +#endif + +/* Define one of these according to your system. */ +#if defined(MPE) +#include <sys/times.h> +#define JMP_BUF sigjmp_buf +#define NO_SETSID +#define NO_KILLPG +#define NO_WRITEV +#define NEED_INITGROUPS +#define NEED_STRCASECMP +#define NEED_STRDUP +#define NEED_STRNCASECMP +extern void GETPRIVMODE(); +extern void GETUSERMODE(); +extern char *inet_ntoa(); +#define NO_SLACK + +#elif defined(SUNOS4) +#define HAVE_GMTOFF +#define HAVE_SYS_RESOURCE_H +#undef NO_KILLPG +#undef NO_SETSID +char *crypt(const char *pw, const char *salt); +char *mktemp(char *template); +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#include <sys/time.h> +#define NEED_STRERROR +typedef int rlim_t; +#define memmove(a,b,c) bcopy(b,a,c) +#define NO_LINGCLOSE +#define USE_FLOCK_SERIALIZED_ACCEPT + +#elif defined(SOLARIS2) +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#define HAVE_SYS_RESOURCE_H +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +#define USE_FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP +#define HAVE_CRYPT_H +int gethostname(char *name, int namelen); + +#elif defined(IRIX) +#undef HAVE_GMTOFF +/* IRIX has killpg, but it's only in _BSD_COMPAT, so don't use it in case + * there's some weird conflict with non-BSD signals */ +#define NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define USE_FCNTL_SERIALIZED_ACCEPT +#define HAVE_SHMGET +#define HAVE_CRYPT_H +#define NO_LONG_DOUBLE +#define HAVE_BSTRING_H +#define NO_LINGCLOSE + +#elif defined(HIUX) +#define HAVE_SYS_RESOURCE_H +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#ifndef _HIUX_SOURCE +#define _HIUX_SOURCE +#endif +#define JMP_BUF sigjmp_buf +#define HAVE_SHMGET +#define SELECT_NEEDS_CAST + +#elif defined(HPUX) || defined(HPUX10) +#define HAVE_SYS_RESOURCE_H +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#ifndef _HPUX_SOURCE +#define _HPUX_SOURCE +#endif +#define JMP_BUF sigjmp_buf +#define HAVE_SHMGET +#ifndef HPUX10 +#define SELECT_NEEDS_CAST +typedef int rlim_t; +#endif + +#elif defined(AIX) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define HAVE_SYS_SELECT_H +#define JMP_BUF sigjmp_buf +#ifndef __ps2__ +#define HAVE_MMAP +#define DEFAULT_GROUP "nobody" +#endif +#define DEFAULT_USER "nobody" +#ifdef NEED_RLIM_T +typedef int rlim_t; +#endif + +#elif defined(ULTRIX) +#define HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define ULTRIX_BRAIN_DEATH +#define NEED_STRDUP +/* If you have Ultrix 4.3, and are using cc, const is broken */ +#ifndef __ultrix__ /* Hack to check for pre-Ultrix 4.4 cc */ +#define const /* Not implemented */ +#endif +#define JMP_BUF sigjmp_buf + +#elif defined(OSF1) +#define HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#define HAVE_CRYPT_H +#define NO_LONG_DOUBLE + +#elif defined(PARAGON) +#define HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#define HAVE_CRYPT_H +#define NO_LONG_DOUBLE +typedef int rlim_t; + +#elif defined(SEQUENT) +#define HAVE_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_STRDUP +#define tolower(c) (isupper(c) ? tolower(c) : c) + +#elif defined(NEXT) +typedef unsigned short mode_t; +#define HAVE_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_STRDUP +#define NO_LINGCLOSE +#define NO_UNISTD_H +#undef _POSIX_SOURCE +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR)) +#endif +#ifndef S_ISREG +#define S_ISREG(m) (((m)&(S_IFMT)) == (S_IFREG)) +#endif +#ifndef S_IXUSR +#define S_IXUSR 00100 +#endif +#ifndef S_IRGRP +#define S_IRGRP 00040 +#endif +#ifndef S_IXGRP +#define S_IXGRP 00010 +#endif +#ifndef S_IROTH +#define S_IROTH 00004 +#endif +#ifndef S_IXOTH +#define S_IXOTH 00001 +#endif +#ifndef S_IRUSR +#define S_IRUSR S_IREAD +#endif +#ifndef S_IWUSR +#define S_IWUSR S_IWRITE +#endif +#ifndef S_IWGRP +#define S_IWGRP 000020 +#endif +#ifndef S_IWOTH +#define S_IWOTH 000002 +#ifndef rlim_t +typedef int rlim_t; +#endif +typedef u_long n_long; +#endif + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define waitpid(a,b,c) wait4((a) == -1 ? 0 : (a),(union wait *)(b),c,NULL) +typedef int pid_t; +#define JMP_BUF jmp_buf +#define USE_LONGJMP +#define NO_USE_SIGACTION + +#elif defined(LINUX) +#if LINUX > 1 +#define HAVE_SHMGET +#define HAVE_SYS_RESOURCE_H +typedef int rlim_t; +#endif +#define USE_FCNTL_SERIALIZED_ACCEPT +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define JMP_BUF sigjmp_buf +#include <sys/time.h> + +#elif defined(SCO) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define NO_WRITEV +#define JMP_BUF sigjmp_buf +#define SIGURG SIGUSR1 /* but note, this signal will be sent to a process group if enabled (for OOB data). It is not currently enabled. */ +#include <sys/time.h> + +#elif defined(SCO5) + +#define JMP_BUF sigjmp_buf +#define SIGURG SIGUSR1 +#define HAVE_SYS_SELECT_H +#define USE_FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP +#define HAVE_SYS_RESOURCE_H +#define SecureWare + +/* Although SCO 5 defines these in <strings.h> (note the "s") they don't have +consts. Sigh. */ +extern int strcasecmp(const char *,const char *); +extern int strncasecmp(const char *,const char *,unsigned); + +#elif defined(AUX) +/* These are to let -Wall compile more cleanly */ +extern int strcasecmp(const char *, const char *); +extern int strncasecmp(const char *,const char *,unsigned); +extern int set42sig(), getopt(), getpeername(), bzero(); +extern int listen(), bind(), socket(), getsockname(); +extern int accept(), gethostname(), connect(), lstat(); +extern int select(), killpg(), shutdown(); +extern int initgroups(), setsockopt(); +extern char *shmat(); +extern int shmctl(); +extern int shmget(); +extern char *sbrk(); +extern char *crypt(); +#include <sys/time.h> +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_STRDUP +#define JMP_BUF sigjmp_buf +/* fcntl() locking is expensive with NFS */ +#define USE_FLOCK_SERIALIZED_ACCEPT +#define HAVE_SHMGET +/* + * NOTE: If when you run Apache under A/UX and you get a warning + * that httpd couldn't move break, then the below value for + * MOVEBREAK (64megs) is too large for your setup. Try reducing + * to 0x2000000 which is still PLENTY of space. I doubt if + * even on heavy systems sbrk() would be called at all... + */ +#define MOVEBREAK 0x4000000 +#define NO_LINGCLOSE +#define NO_SLACK + +#elif defined(SVR4) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +/* A lot of SVR4 systems need this */ +#define USE_FCNTL_SERIALIZED_ACCEPT + +#elif defined(UW) +#define NO_LINGCLOSE +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +#define HAVE_RESOURCE +#define HAVE_MMAP +#define HAVE_SHMGET +#define HAVE_CRYPT_H +#define HAVE_SYS_SELECT_H +#define HAVE_SYS_RESOURCE_H +#include <sys/time.h> +#define _POSIX_SOURCE + +#elif defined(DGUX) +#define NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +/* A lot of SVR4 systems need this */ +#define USE_FCNTL_SERIALIZED_ACCEPT + +#elif defined(__NetBSD__) || defined(__OpenBSD__) +#define HAVE_SYS_RESOURCE_H +#define HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define DEFAULT_USER "nobody" +#define DEFAULT_GROUP "nogroup" +#define HAVE_MMAP +#define USE_FLOCK_SERIALIZED_ACCEPT + + +#elif defined(UTS21) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#define NO_SETSID +#define NEED_WAITPID +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +#define strftime(buf,bufsize,fmt,tm) ascftime(buf,fmt,tm) +#include <sys/types.h> + +#elif defined(APOLLO) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID + +#elif defined(__FreeBSD__) || defined(__bsdi__) +#if defined(__FreeBSD__) +#include <osreldate.h> +#endif +#define HAVE_SYS_RESOURCE_H +#define HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#define JMP_BUF sigjmp_buf +#define HAVE_MMAP +#define DEFAULT_USER "nobody" +#define DEFAULT_GROUP "nogroup" +#if defined(__bsdi__) || \ +(defined(__FreeBSD_version) && (__FreeBSD_version < 220000)) +typedef quad_t rlim_t; +#endif +#define USE_FLOCK_SERIALIZED_ACCEPT + +#elif defined(QNX) +#ifndef crypt +char *crypt(const char *pw, const char *salt); +#endif +#ifndef initgroups +int initgroups (char *, int); +#endif +#ifndef strncasecmp +#define strncasecmp strnicmp +#endif +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_INITGROUPS +#define NEED_SELECT_H +#define NEED_PROCESS_H +#define HAVE_SYS_SELECT_H +#include <unix.h> +#define JMP_BUF sigjmp_buf + +#elif defined(LYNXOS) +#undef NO_KILLPG +#undef NO_SETSID +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NEED_INITGROUPS +#define JMP_BUF jmp_buf + +#elif defined(UXPDS) +#undef NEED_STRCASECMP +#undef NEED_STRNCASECMP +#undef NEED_STRDUP +#undef HAVE_GMTOFF +#define NO_KILLPG +#undef NO_SETSID +#define HAVE_RESOURCE 1 +#define bzero(a,b) memset(a,0,b) +#define JMP_BUF sigjmp_buf +#define USE_FCNTL_SERIALIZED_ACCEPT +#define HAVE_MMAP +#define HAVE_CRYPT_H + +#elif defined(__EMX__) +/* Defines required for EMX OS/2 port. */ +#define JMP_BUF sigjmp_buf +#define NO_KILLPG +#define NEED_STRCASECMP +#define NEED_STRNCASECMP +#define NO_SETSID +/* Add some drive name support */ +#define chdir _chdir2 +#include <sys/time.h> +#define MAXSOCKETS 4096 +#define HAVE_MMAP + +#elif defined(__MACHTEN__) +typedef int rlim_t; +#define JMP_BUF sigjmp_buf +#undef NO_KILLPG +#define NO_SETSID +#define HAVE_GMTOFF +#ifndef __MACHTEN_PPC__ +#ifndef __MACHTEN_68K__ +#define __MACHTEN_68K__ +#endif +#define USE_FLOCK_SERIALIZED_ACCEPT +#define NO_USE_SIGACTION +#define USE_LONGJMP +#undef NEED_STRDUP +#else +#define HAVE_SHMGET +#define USE_FCNTL_SERIALIZED_ACCEPT +#endif + +/* Convex OS v11 */ +#elif defined(CONVEXOS11) +#undef HAVE_GMTOFF +#undef NO_KILLPG +#undef NO_SETSID +#undef NEED_STRDUP +#define HAVE_MMAP + +#define NO_TIMEZONE +#include <stdio.h> +#include <sys/types.h> +#define JMP_BUF jmp_buf +typedef int rlim_t; + +#elif defined(ISC) +#include <net/errno.h> +#define NO_KILLPG +#undef NO_SETSID +#define HAVE_SHMGET +#define SIGURG SIGUSR1 +#define JMP_BUF sigjmp_buf +#define USE_FCNTL_SERIALIZED_ACCEPT + +/* Unknown system - Edit these to match */ +#else +#ifdef BSD +#define HAVE_GMTOFF +#else +#undef HAVE_GMTOFF +#endif +/* NO_KILLPG is set on systems that don't have killpg */ +#undef NO_KILLPG +/* NO_SETSID is set on systems that don't have setsid */ +#undef NO_SETSID +/* NEED_STRDUP is set on stupid systems that don't have strdup. */ +#undef NEED_STRDUP +#endif + +/* Do we have sys/resource.h; assume that BSD does. */ +#ifndef HAVE_SYS_RESOURCE_H +#ifdef BSD +#define HAVE_SYS_RESOURCE_H +#endif +#endif /* HAVE_SYS_RESOURCE_H */ + +/* + * The particular directory style your system supports. If you have dirent.h + * in /usr/include (POSIX) or /usr/include/sys (SYSV), #include + * that file and define DIR_TYPE to be dirent. Otherwise, if you have + * /usr/include/sys/dir.h, define DIR_TYPE to be direct and include that + * file. If you have neither, I'm confused. + */ + +#include <sys/types.h> +#include <stdarg.h> +/* + * We use snprintf() to avoid overflows, but we include + * our own version (ap_snprintf). Allow for people to use their + * snprintf() if they want + */ +#ifdef HAVE_SNPRINTF +#define ap_snprintf snprintf +#define ap_vsnprintf vsnprintf +#else +int ap_snprintf(char *buf, size_t len, const char *format,...); +int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap); +#endif + +#if !defined(NEXT) +#include <dirent.h> +#define DIR_TYPE dirent +#else +#include <sys/dir.h> +#define DIR_TYPE direct +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#ifndef MPE +#include <sys/file.h> +#endif +#include <sys/socket.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#include <ctype.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/ioctl.h> +#ifndef MPE +#include <arpa/inet.h> /* for inet_ntoa */ +#endif +#include <time.h> /* for ctime */ +#include <signal.h> +#include <errno.h> +#include <sys/wait.h> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <limits.h> +#if !defined(QNX) && !defined(CONVEXOS11) && !defined(NEXT) +#include <memory.h> +#endif +#ifdef NEED_PROCESS_H +#include <process.h> +#endif + +#include <regex.h> + +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#ifdef SUNOS4 +int getrlimit( int, struct rlimit *); +int setrlimit( int, struct rlimit *); +#endif +#endif +#ifdef HAVE_MMAP +#ifndef __EMX__ +/* This file is not needed for OS/2 */ +#include <sys/mman.h> +#endif +#endif +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +#define MAP_ANON MAP_ANONYMOUS +#endif + +#if defined(HAVE_MMAP) && defined(NO_MMAP) +#undef HAVE_MMAP +#endif + +#ifndef LOGNAME_MAX +#define LOGNAME_MAX 25 +#endif + +#ifndef NEXT +#include <unistd.h> +#endif + +#ifdef ultrix +#define ULTRIX_BRAIN_DEATH +#endif + +#ifndef S_ISLNK +#ifndef __EMX__ +/* Don't define this for OS/2 */ +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) +#endif + +/* + * Replace signal function with sigaction equivalent + */ +#ifndef NO_USE_SIGACTION +typedef void Sigfunc(int); + +#if defined(SIG_IGN) && !defined(SIG_ERR) +#define SIG_ERR ((Sigfunc *)-1) +#endif + +/* + * For some strange reason, QNX defines signal to signal. Eliminate it. + */ +#ifdef signal +#undef signal +#endif +#define signal(s,f) ap_signal(s,f) +Sigfunc *signal(int signo, Sigfunc *func); +#endif + +#include <setjmp.h> + +#if defined(USE_LONGJMP) +#define ap_longjmp(x, y) longjmp((x), (y)) +#define ap_setjmp(x) setjmp(x) +#else +#define ap_longjmp(x, y) siglongjmp((x), (y)) +#define ap_setjmp(x) sigsetjmp((x), 1) +#endif + +/* Finding offsets of elements within structures. + * Taken from the X code... they've sweated portability of this stuff + * so we don't have to. Sigh... + */ + +#if defined(CRAY) || defined(__arm) +#if __STDC__ +#define XtOffset(p_type,field) _Offsetof(p_type,field) +#else +#ifdef CRAY2 +#define XtOffset(p_type,field) \ + (sizeof(int)*((unsigned int)&(((p_type)NULL)->field))) + +#else /* !CRAY2 */ + +#define XtOffset(p_type,field) ((unsigned int)&(((p_type)NULL)->field)) + +#endif /* !CRAY2 */ +#endif /* __STDC__ */ +#else /* ! (CRAY || __arm) */ + +#define XtOffset(p_type,field) \ + ((long) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) + +#endif /* !CRAY */ + +#ifdef offsetof +#define XtOffsetOf(s_type,field) offsetof(s_type,field) +#else +#define XtOffsetOf(s_type,field) XtOffset(s_type*,field) +#endif + +#ifdef SUNOS_LIB_PROTOTYPES +/* Prototypes needed to get a clean compile with gcc -Wall. + * Believe it or not, these do have to be declared, at least on SunOS, + * because they aren't mentioned in the relevant system headers. + * Sun Quality Software. Gotta love it. + */ + +int getopt (int, char **, char *); + +int strcasecmp (char *, char *); +int strncasecmp (char *, char *, int); +int toupper(int); +int tolower(int); + +int printf (char *, ...); +int fprintf (FILE *, char *, ...); +int fputs (char *, FILE *); +int fread (char *, int, int, FILE *); +int fwrite (char *, int, int, FILE *); +int fflush (FILE *); +int fclose (FILE *); +int ungetc (int, FILE *); +int _filbuf (FILE *); /* !!! */ +int _flsbuf (unsigned char, FILE *); /* !!! */ +int sscanf (char *, char *, ...); +void setbuf (FILE *, char *); +void perror (char *); + +time_t time (time_t *); +int strftime (char *, int, char *, struct tm *); + +int initgroups (char *, int); +int wait3 (int *, int, void*); /* Close enough for us... */ +int lstat (const char *, struct stat *); +int stat (const char *, struct stat *); +int flock (int, int); +#ifndef NO_KILLPG +int killpg(int, int); +#endif +int socket (int, int, int); +int setsockopt (int, int, int, const char*, int); +int listen (int, int); +int bind (int, struct sockaddr *, int); +int connect (int, struct sockaddr *, int); +int accept (int, struct sockaddr *, int *); +int shutdown (int, int); + +int getsockname (int s, struct sockaddr *name, int *namelen); +int getpeername (int s, struct sockaddr *name, int *namelen); +int gethostname (char *name, int namelen); +void syslog (int, char *, ...); +char *mktemp (char *); + +long vfprintf (FILE *, char *, va_list); + +#endif diff --git a/usr.sbin/httpd/src/explain.c b/usr.sbin/httpd/src/explain.c new file mode 100644 index 00000000000..6490c940522 --- /dev/null +++ b/usr.sbin/httpd/src/explain.c @@ -0,0 +1,14 @@ +#include <stdio.h> +#include <stdarg.h> +#include "explain.h" + +void _Explain(const char *szFile,int nLine,const char *szFmt,...) + { + va_list vlist; + + fprintf(stderr,"%s(%d): ",szFile,nLine); + va_start(vlist,szFmt); + vfprintf(stderr,szFmt,vlist); + va_end(vlist); + fputc('\n',stderr); + } diff --git a/usr.sbin/httpd/src/explain.h b/usr.sbin/httpd/src/explain.h new file mode 100644 index 00000000000..5912502585e --- /dev/null +++ b/usr.sbin/httpd/src/explain.h @@ -0,0 +1,23 @@ +#ifndef EXPLAIN +#define DEF_Explain +#define Explain0(f) +#define Explain1(f,a1) +#define Explain2(f,a1,a2) +#define Explain3(f,a1,a2,a3) +#define Explain4(f,a1,a2,a3,a4) +#define Explain5(f,a1,a2,a3,a4,a5) +#define Explain6(f,a1,a2,a3,a4,a5,a6) +#else +#define DEF_Explain static const char *__ExplainFile=__FILE__; +void _Explain(const char *szFile,int nLine,const char *szFmt,...); +#define Explain0(f) _Explain(__ExplainFile,__LINE__,f) +#define Explain1(f,a1) _Explain(__ExplainFile,__LINE__,f,a1) +#define Explain2(f,a1,a2) _Explain(__ExplainFile,__LINE__,f,a1,a2) +#define Explain3(f,a1,a2,a3) _Explain(__ExplainFile,__LINE__,f,a1,a2,a3) +#define Explain4(f,a1,a2,a3,a4) _Explain(__ExplainFile,__LINE__,f,a1,a2,a3,a4) +#define Explain5(f,a1,a2,a3,a4,a5) \ + _Explain(__ExplainFile,__LINE__,f,a1,a2,a3,a4,a5) +#define Explain6(f,a1,a2,a3,a4,a5,a6) \ + _Explain(__ExplainFile,__LINE__,f,a1,a2,a3,a4,a5,a6) + +#endif diff --git a/usr.sbin/httpd/src/helpers/CutRule b/usr.sbin/httpd/src/helpers/CutRule new file mode 100644 index 00000000000..740a3362c1b --- /dev/null +++ b/usr.sbin/httpd/src/helpers/CutRule @@ -0,0 +1,7 @@ +# Helper script for Configure - cut a rule from Configuration. +# note that there is a tab and a space in the character groups. +# Map to lowercase to make tests easier + +egrep "^[ ]*Rule[ ]+$1[ ]*=" $2 | \ +awk 'BEGIN {FS="="}{print $2}' | \ +sed 's/[ ]//g' | tr "A-Z" "a-z" diff --git a/usr.sbin/httpd/src/helpers/GuessOS b/usr.sbin/httpd/src/helpers/GuessOS new file mode 100644 index 00000000000..0029797bbc5 --- /dev/null +++ b/usr.sbin/httpd/src/helpers/GuessOS @@ -0,0 +1,234 @@ +#!/bin/sh +# +# Simple OS/Platform guesser. Similar to config.guess but +# much, much smaller. Since it was developed for use with +# Apache, it follows under Apache's regular licensing +# with one specific addition: Any changes or additions +# to this script should be Emailed to the Apache +# group (apache@apache.org) in general and to +# Jim Jagielski (jim@jaguNET.com) in specific. +# +# Be as similar to the output of config.guess/config.sub +# as possible. + +# First get uname entries that we use below + +MACHINE=`(uname -m) 2>/dev/null` || MACHINE="unknown" +RELEASE=`(uname -r) 2>/dev/null` || RELEASE="unknown" +SYSTEM=`(uname -s) 2>/dev/null` || SYSTEM="unknown" +VERSION=`(uname -v) 2>/dev/null` || VERSION="unknown" + + +# Now test for ISC and SCO, since it is has a braindamaged uname. +# +# We need to work around FreeBSD 1.1.5.1 +XREL=`uname -X 2>/dev/null | grep "^Release" | awk '{print $3}'` +if [ "x$XREL" != "x" ]; then + if [ -f /etc/kconfig ]; then + case "$XREL" in + 4.0|4.1) + echo "${MACHINE}-whatever-isc4"; exit 0 + ;; + esac + else + case "$XREL" in + 3.2v4.2) + echo "whatever-whatever-sco3"; exit 0 + ;; + 3.2v5.0*) + echo "whatever-whatever-sco5"; exit 0 + ;; + 4.2MP) + if [ "x$VERSION" = "x2.1.1" ]; then + echo "${MACHINE}-whatever-unixware211"; exit 0 + elif [ "x$VERSION" = "x2.1.2" ]; then + echo "${MACHINE}-whatever-unixware212"; exit 0 + else + echo "${MACHINE}-whatever-unixware2"; exit 0 + fi + ;; + 4.2) + echo "whatever-whatever-unixware1"; exit 0 + ;; + esac + fi +fi +# Now we simply scan though... In most cases, the SYSTEM info is enough +# +case "${SYSTEM}:${RELEASE}:${VERSION}:${MACHINE}" in + A/UX:*) + echo "m68k-apple-aux3"; exit 0 + ;; + + AIX:*) + echo "${MACHINE}-ibm-aix${VERSION}.${RELEASE}"; exit 0 + ;; + + dgux:*) + echo "${MACHINE}-dg-dgux"; exit 0 + ;; + + HI-UX:*) + echo "${MACHINE}-hi-hiux"; exit 0 + ;; + + HP-UX:*) + HPUXVER=`echo ${RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "$HPUXVER" in + 10.*) + echo "${MACHINE}-hp-hpux10."; exit 0 + ;; + *) + echo "${MACHINE}-hp-hpux"; exit 0 + ;; + esac + ;; + + IRIX:*) + echo "${MACHINE}-sgi-irix"; exit 0 + ;; + + IRIX64:*) + echo "${MACHINE}-sgi-irix64"; exit 0 + ;; + + Linux:[2-9].*) + echo "${MACHINE}-whatever-linux2"; exit 0 + ;; + + Linux:1.*) + echo "${MACHINE}-whatever-linux1"; exit 0 + ;; + + LynxOS:*) + echo "${MACHINE}-lynx-lynxos"; exit 0 + ;; + + BSD/386:*:*:*486*|BSD/OS:*:*:*:*486*) + echo "i486-whatever-bsdi"; exit 0 + ;; + + BSD/386:*|BSD/OS:*) + echo "${MACHINE}-whatever-bsdi"; exit 0 + ;; + + FreeBSD:*:*:*486*) + echo "i486-whatever-freebsd"; exit 0 + ;; + + FreeBSD:*) + echo "${MACHINE}-whatever-freebsd"; exit 0 + ;; + + NetBSD:*:*:*486*) + echo "i486-whatever-netbsd"; exit 0 + ;; + + NetBSD:*) + echo "${MACHINE}-whatever-netbsd"; exit 0 + ;; + + OpenBSD:*) + echo "${MACHINE}-whatever-openbsd"; exit 0 + ;; + + OSF1:*:*:*alpha*) + echo "${MACHINE}-dec-osf"; exit 0 + ;; + + QNX:*) + case "$VERSION" in + 423) + echo "${MACHINE}-qssl-qnx32" + ;; + *) + echo "${MACHINE}-qssl-qnx" + ;; + esac + exit 0 + ;; + + Paragon*:*:*:*) + echo "i860-intel-osf1"; exit 0 + ;; + + SunOS:5.*) + echo "${MACHINE}-sun-solaris2"; exit 0 + ;; + + SunOS:*) + echo "${MACHINE}-sun-sunos4"; exit 0 + ;; + + UNIX_System_V:4.*:*) + echo "${MACHINE}-whatever-sysv4"; exit 0 + ;; + + *:4*:R4*:m88k) + echo "${MACHINE}-whatever-sysv4"; exit 0 + ;; + + DYNIX/ptx:4*:*) + echo "${MACHINE}-whatever-sysv4"; exit 0 + ;; + + *:4.0:3.0:3[34]?? | *:4.0:3.0:3[34]??,*) + echo "i486-ncr-sysv4"; exit 0 + ;; + + ULTRIX:*) + echo "${MACHINE}-unknown-ultrix"; exit 0 + ;; + + SINIX-?:* | ReliantUNIX-?:*) + echo "${MACHINE}-sni-sysv4"; exit 0 + ;; + + machten:*) + echo "${MACHINE}-tenon-${SYSTEM}"; exit 0; + ;; + + library:*) + echo "${MACHINE}-ncr-sysv4"; exit 0 + ;; + + ConvexOS:*:11.*:*) + echo "${MACHINE}-v11-${SYSTEM}"; exit 0; + ;; + + UNIX_SV:*:*:maxion) + echo "${MACHINE}-ccur-sysv4"; exit 0; + ;; + + NonStop-UX:4.[02]*:[BC]*:*) + echo "${MACHINE}-tandem-sysv4"; exit 0; + ;; + +esac + +# +# Ugg. These are all we can determine by what we know about +# the output of uname. Be more creative: +# + +# Do the Apollo stuff first. Here, we just simply assume +# that the existance of the /usr/apollo directory is proof +# enough +if [ -d /usr/apollo ]; then + echo "whatever-apollo-whatever" + exit 0 +fi + +# Now NeXT +ISNEXT=`hostinfo 2>/dev/null` +case "$ISNEXT" in + *NeXT*) + echo "whatever-next-nextstep"; exit 0 + ;; +esac + +# At this point we gone through all the one's +# we know of: Punt + +echo "${MACHINE}-whatever-${SYSTEM}|${RELEASE}|${VERSION}" +exit 0 diff --git a/usr.sbin/httpd/src/helpers/PrintPath b/usr.sbin/httpd/src/helpers/PrintPath new file mode 100644 index 00000000000..9ec0e0c37f0 --- /dev/null +++ b/usr.sbin/httpd/src/helpers/PrintPath @@ -0,0 +1,46 @@ +#!/bin/sh +# Look for $1 somewhere in $PATH +# will print out the full pathname unless +# called with the '-s' option +# +# We do some funny stuff to check to see +# if test/[] knows about -x +# +testfile="pp.t.$$" + +cat > $testfile <<ENDTEST +#!/bin/sh +if [ -x / ] || [ -x /bin ] || [ -x /bin/ls ]; then + exit 0 +fi +exit 1 +ENDTEST + +if `/bin/sh $testfile 2>/dev/null`; then + test_exec_flag="-x" +else + test_exec_flag="-r" +fi +rm -f $testfile + +if [ "x$1" = "x-s" ]; then + shift +else + echo="yes" +fi + +for path in `echo $PATH | + sed 's/^:/.:/ + s/::/:.:/g + s/:$/:./ + s/:/ /g' ` +do + if [ $test_exec_flag $path/$1 ] && [ ! -d $path/$1 ]; then + if [ "$echo" = "yes" ]; then + echo $path/$1 + fi + exit 0 + fi +done +exit 1 + diff --git a/usr.sbin/httpd/src/htconf.634 b/usr.sbin/httpd/src/htconf.634 new file mode 100644 index 00000000000..e8e7e53fdb7 --- /dev/null +++ b/usr.sbin/httpd/src/htconf.634 @@ -0,0 +1,25 @@ +EXTRA_CFLAGS= +EXTRA_LFLAGS= +EXTRA_LIBS= +EXTRA_INCLUDES= +##Rule:STATUS=yes +##Rule:SOCKS4=no +##Rule:IRIXNIS=no +##Rule:WANTHSREGEX=default +Module env_module mod_env.o +Module config_log_module mod_log_config.o +Module mime_module mod_mime.o +Module negotiation_module mod_negotiation.o +Module includes_module mod_include.o +Module dir_module mod_dir.o +Module cgi_module mod_cgi.o +Module asis_module mod_asis.o +Module imap_module mod_imap.o +Module action_module mod_actions.o +Module userdir_module mod_userdir.o +Module alias_module mod_alias.o +Module access_module mod_access.o +Module auth_module mod_auth.o +Module db_auth_module mod_auth_db.o +Module dbm_auth_module mod_auth_dbm.o +Module browser_module mod_browser.o diff --git a/usr.sbin/httpd/src/http_bprintf.c b/usr.sbin/httpd/src/http_bprintf.c new file mode 100644 index 00000000000..3035b759ccf --- /dev/null +++ b/usr.sbin/httpd/src/http_bprintf.c @@ -0,0 +1,602 @@ +/* + * printf() style routines stolen from FastCGI + * Copyright (c) 1996 Open Market, Inc. + */ + +/* + * Modified to work with Apache buffering routines by Ben Laurie + * <ben@algroup.co.uk>. + * + * Modifications Copyright (C) 1996 Ben Laurie. + * + * History: + * 18 May 1996 Initial revision [Ben Laurie] + * + */ + +#include <assert.h> +#include <math.h> +#include "conf.h" +#include "alloc.h" +#include "buff.h" + +#if !defined(max) +#define max(a,b) (a > b ? a : b) +#endif + +#ifdef NO_LONG_DOUBLE +#define LONG_DOUBLE double +#else +#define LONG_DOUBLE long double +#endif + +#define FALSE 0 +#define TRUE 1 + +#define PRINTF_BUFFLEN 100 + /* + * More than sufficient space for all unmodified conversions + * except %s and %f. + */ +#define FMT_BUFFLEN 25 + /* + * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop + */ +#define NULL_STRING "(null)" + /* + * String displayed if given a NULL pointer. + */ + +/* + * Copy n characters from *srcPtr to *destPtr, then increment + * both *srcPtr and *destPtr by n. + */ +static void CopyAndAdvance(char **destPtr, const char **srcPtr, int n) + { + char *dest = *destPtr; + const char *src = *srcPtr; + int i; + + for (i = 0; i < n; i++) + *dest++ = *src++; + *destPtr = dest; + *srcPtr = src; + } + +int vbprintf(BUFF *bp, const char *format, va_list arg) + { + const char *f,*fStop,*percentPtr,*p; + char *fmtBuffPtr, *buffPtr; + int op, performedOp, sizeModifier, buffLen, specifierLength; + int fastPath, n, buffReqd, minWidth, precision, exp; + int buffCount = 0; + int auxBuffLen = 0; + char *auxBuffPtr = NULL; + int streamCount = 0; + char fmtBuff[FMT_BUFFLEN]; + char buff[PRINTF_BUFFLEN]; + + int intArg; + short shortArg; + long longArg; + unsigned unsignedArg; + unsigned long uLongArg; + unsigned short uShortArg; + char *charPtrArg = NULL; + void *voidPtrArg; + int *intPtrArg; + long *longPtrArg; + short *shortPtrArg; + double doubleArg = 0.0; + LONG_DOUBLE lDoubleArg = 0.0; + + fmtBuff[0] = '%'; + f=format; + fStop = f + strlen(f); + while (f != fStop) + { + percentPtr = memchr(f, '%', fStop - f); + if(percentPtr == NULL) percentPtr = fStop; + if(percentPtr != f) + { + if(bwrite(bp,f,percentPtr - f) < 0) + goto ErrorReturn; + streamCount += percentPtr - f; + f = percentPtr; + if(f == fStop) + break; + } + fastPath = TRUE; + /* + * The following loop always executes either once or twice. + */ + for (;;) + { + if(fastPath) + { + /* + * Fast path: Scan optimistically, hoping that no flags, + * minimum field width, or precision are specified. + * Use the preallocated buffer, which is large enough + * for all fast path cases. If the conversion specifier + * is really more complex, run the loop a second time + * using the slow path. + * Note that fast path execution of %s bypasses the buffer + * and %f is not attempted on the fast path due to + * its large buffering requirements. + */ + op = percentPtr[1]; + switch(op) + { + case 'l': + case 'L': + case 'h': + sizeModifier = op; + op = percentPtr[2]; + fmtBuff[1] = sizeModifier; + fmtBuff[2] = op; + fmtBuff[3] = '\0'; + specifierLength = 3; + break; + default: + sizeModifier = ' '; + fmtBuff[1] = op; + fmtBuff[2] = '\0'; + specifierLength = 2; + break; + } + buffPtr = buff; + buffLen = PRINTF_BUFFLEN; + } + else + { + /* + * Slow path: Scan the conversion specifier and construct + * a new format string, compute an upper bound on the + * amount of buffering that sprintf will require, + * and allocate a larger buffer if necessary. + */ + p = percentPtr + 1; + fmtBuffPtr = &fmtBuff[1]; + /* + * Scan flags + */ + n = strspn(p, "-0+ #"); + if(n > 5) + goto ErrorReturn; + CopyAndAdvance(&fmtBuffPtr, &p, n); + + /* Optimiser bug in SCO 5 - p is not advanced here under -O2. + * -K noinline fixes it. Ben. + */ + + /* + * Scan minimum field width + */ + n = strspn(p, "0123456789"); + if(n == 0) + { + if(*p == '*') + { + minWidth = va_arg(arg, int); + if(abs(minWidth) > 999999) goto ErrorReturn; + /* + * The following use of strlen rather than the + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ + sprintf(fmtBuffPtr, "%d", minWidth); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } + else + minWidth = 0; + } + else if(n <= 6) + { + minWidth = strtol(p, NULL, 10); + CopyAndAdvance(&fmtBuffPtr, &p, n); + } + else + goto ErrorReturn; + /* + * Scan precision + */ + if(*p == '.') + { + p++; + n = strspn(p, "0123456789"); + if(n == 0) + { + if(*p == '*') + { + precision = va_arg(arg, int); + if(precision < 0) precision = 0; + if(precision > 999999) goto ErrorReturn; + /* + * The following use of strlen rather than the + * value returned from sprintf is because SUNOS4 + * returns a char * instead of an int count. + */ + sprintf(fmtBuffPtr, ".%d", precision); + fmtBuffPtr += strlen(fmtBuffPtr); + p++; + } + else + precision = 0; + } + else if(n <= 6) + { + precision = strtol(p, NULL, 10); + *fmtBuffPtr++='.'; + CopyAndAdvance(&fmtBuffPtr, &p, n); + } + else + goto ErrorReturn; + } + else + precision = -1; + /* + * Scan size modifier and conversion operation + */ + switch(*p) + { + case 'l': + case 'L': + case 'h': + sizeModifier = *p; + CopyAndAdvance(&fmtBuffPtr, &p, 1); + break; + + default: + sizeModifier = ' '; + break; + } + op = *p; + CopyAndAdvance(&fmtBuffPtr, &p, 1); + assert(fmtBuffPtr - fmtBuff < FMT_BUFFLEN); + *fmtBuffPtr = '\0'; + /* + bwrite(bp,"[",1); + bwrite(bp,fmtBuff,strlen(fmtBuff)); + bwrite(bp,"]",1); + */ + specifierLength = p - percentPtr; + /* + * Bound the required buffer size. For s and f + * conversions this requires examining the argument. + */ + switch(op) + { + case 'd': + case 'i': + case 'u': + case 'o': + case 'x': + case 'X': + case 'c': + case 'p': + buffReqd = max(precision, 46); + break; + + case 's': + charPtrArg = va_arg(arg, char *); + if (charPtrArg == NULL) { + charPtrArg = NULL_STRING; + }; + if(precision == -1) + buffReqd = strlen(charPtrArg); + else + { + p = memchr(charPtrArg, '\0', precision); + if (p == NULL) + buffReqd = precision; + else + buffReqd = p - charPtrArg; + } + break; + + case 'f': + switch(sizeModifier) + { + case ' ': + doubleArg = va_arg(arg, double); + frexp(doubleArg, &exp); + break; + + case 'L': + lDoubleArg = va_arg(arg, LONG_DOUBLE); + frexp(lDoubleArg, &exp); + break; + + default: + goto ErrorReturn; + } + if(precision == -1) + precision = 6; + buffReqd = precision + 3 + ((exp > 0) ? exp/3 : 0); + break; + + case 'e': + case 'E': + case 'g': + case 'G': + if(precision == -1) + precision = 6; + buffReqd = precision + 8; + break; + + case 'n': + case '%': + default: + goto ErrorReturn; + } + buffReqd = max(buffReqd + 10, minWidth); + /* + * Allocate the buffer + */ + if(buffReqd <= PRINTF_BUFFLEN) + { + buffPtr = buff; + buffLen = PRINTF_BUFFLEN; + } + else + { + if(auxBuffPtr == NULL || buffReqd > auxBuffLen) + { + if(auxBuffPtr != NULL) free(auxBuffPtr); + auxBuffPtr = malloc(buffReqd); + auxBuffLen = buffReqd; + if(auxBuffPtr == NULL) + goto ErrorReturn; + } + buffPtr = auxBuffPtr; + buffLen = auxBuffLen; + } + } + /* + * This giant switch statement requires the following variables + * to be set up: op, sizeModifier, arg, buffPtr, fmtBuff. + * When fastPath == FALSE and op == 's' or 'f', the argument + * has been read into charPtrArg, doubleArg, or lDoubleArg. + * The statement produces the boolean performedOp, TRUE iff + * the op/sizeModifier were executed and argument consumed; + * if performedOp, the characters written into buffPtr[] + * and the character count buffCount (== EOF meaning error). + * + * The switch cases are arranged in the same order as in the + * description of fprintf in section 15.11 of Harbison and Steele. + */ + performedOp = TRUE; + switch(op) + { + case 'd': + case 'i': + switch(sizeModifier) + { + case ' ': + intArg = va_arg(arg, int); + sprintf(buffPtr, fmtBuff, intArg); + buffCount = strlen(buffPtr); + break; + + case 'l': + longArg = va_arg(arg, long); + sprintf(buffPtr, fmtBuff, longArg); + buffCount = strlen(buffPtr); + break; + + case 'h': + shortArg = va_arg(arg, short); + sprintf(buffPtr, fmtBuff, shortArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + + case 'u': + case 'o': + case 'x': + case 'X': + switch(sizeModifier) + { + case ' ': + unsignedArg = va_arg(arg, unsigned); + sprintf(buffPtr, fmtBuff, unsignedArg); + buffCount = strlen(buffPtr); + break; + + case 'l': + uLongArg = va_arg(arg, unsigned long); + sprintf(buffPtr, fmtBuff, uLongArg); + buffCount = strlen(buffPtr); + break; + + case 'h': + uShortArg = va_arg(arg, unsigned short); + sprintf(buffPtr, fmtBuff, uShortArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + + case 'c': + switch(sizeModifier) + { + case ' ': + intArg = va_arg(arg, int); + sprintf(buffPtr, fmtBuff, intArg); + buffCount = strlen(buffPtr); + break; + + case 'l': + /* + * XXX: Allowed by ISO C Amendment 1, but + * many platforms don't yet support wint_t + */ + goto ErrorReturn; + + default: + goto ErrorReturn; + } + break; + + case 's': + switch(sizeModifier) + { + case ' ': + if(fastPath) + { + buffPtr = va_arg(arg, char *); + if (buffPtr == NULL) { + buffPtr = NULL_STRING; + }; + buffCount = strlen(buffPtr); + buffLen = buffCount + 1; + } + else + { + sprintf(buffPtr, fmtBuff, charPtrArg); + buffCount = strlen(buffPtr); + } + break; + + case 'l': + /* + * XXX: Don't know how to convert a sequence + * of wide characters into a byte stream, or + * even how to predict the buffering required. + */ + goto ErrorReturn; + + default: + goto ErrorReturn; + } + break; + + case 'p': + if(sizeModifier != ' ') + goto ErrorReturn; + voidPtrArg = va_arg(arg, void *); + sprintf(buffPtr, fmtBuff, voidPtrArg); + buffCount = strlen(buffPtr); + break; + + case 'n': + switch(sizeModifier) + { + case ' ': + intPtrArg = va_arg(arg, int *); + *intPtrArg = streamCount; + break; + + case 'l': + longPtrArg = va_arg(arg, long *); + *longPtrArg = streamCount; + break; + + case 'h': + shortPtrArg = va_arg(arg, short *); + *shortPtrArg = streamCount; + break; + + default: + goto ErrorReturn; + } + buffCount = 0; + break; + + case 'f': + if(fastPath) + { + performedOp = FALSE; + break; + } + + switch(sizeModifier) + { + case ' ': + sprintf(buffPtr, fmtBuff, doubleArg); + buffCount = strlen(buffPtr); + break; + + case 'L': + sprintf(buffPtr, fmtBuff, lDoubleArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + /* FIXME: Used to flow through here? Should it? Ben */ + + case 'e': + case 'E': + case 'g': + case 'G': + switch(sizeModifier) + { + case ' ': + doubleArg = va_arg(arg, double); + sprintf(buffPtr, fmtBuff, doubleArg); + buffCount = strlen(buffPtr); + break; + + case 'L': + lDoubleArg = va_arg(arg, LONG_DOUBLE); + sprintf(buffPtr, fmtBuff, lDoubleArg); + buffCount = strlen(buffPtr); + break; + + default: + goto ErrorReturn; + } + break; + + case '%': + if(sizeModifier != ' ') + goto ErrorReturn; + buff[0] = '%'; + buffCount = 1; + break; + + case '\0': + goto ErrorReturn; + + default: + performedOp = FALSE; + break; + } /* switch(op) */ + + if(performedOp) + break; + if(!fastPath) + goto ErrorReturn; + fastPath = FALSE; + } /* for (;;) */ + assert(buffCount < buffLen); + if(buffCount > 0) + { + if(bwrite(bp,buffPtr,buffCount) < 0) + goto ErrorReturn; + streamCount += buffCount; + } + else if(buffCount < 0) + goto ErrorReturn; + f += specifierLength; + } /* while(f != fStop) */ + goto NormalReturn; +ErrorReturn: + streamCount = -1; +NormalReturn: + if(auxBuffPtr != NULL) + free(auxBuffPtr); + return streamCount; + } diff --git a/usr.sbin/httpd/src/http_conf_globals.h b/usr.sbin/httpd/src/http_conf_globals.h new file mode 100644 index 00000000000..3a60e7db77b --- /dev/null +++ b/usr.sbin/httpd/src/http_conf_globals.h @@ -0,0 +1,86 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * Process config --- what the process ITSELF is doing + */ + +extern int standalone; +extern uid_t user_id; +extern char *user_name; +extern gid_t group_id; +#ifdef MULTIPLE_GROUPS +extern gid_t group_id_list[NGROUPS_MAX]; +#endif +extern int max_requests_per_child; +extern struct in_addr bind_address; +extern listen_rec *listeners; +extern int daemons_to_start; +extern int daemons_min_free; +extern int daemons_max_free; +extern int daemons_limit; +extern int suexec_enabled; + +extern char *pid_fname; +extern char *scoreboard_fname; +extern char *lock_fname; +extern char *server_argv0; + +/* Trying to allocate these in the config pool gets us into some *nasty* + * chicken-and-egg problems in http_main.c --- where do you stick them + * when pconf gets cleared? Better to just allocate a little space + * statically... + */ + +extern char server_root[MAX_STRING_LEN]; +extern char server_confname[MAX_STRING_LEN]; + diff --git a/usr.sbin/httpd/src/http_config.c b/usr.sbin/httpd/src/http_config.c new file mode 100644 index 00000000000..d39af86994c --- /dev/null +++ b/usr.sbin/httpd/src/http_config.c @@ -0,0 +1,1213 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * 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 "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. + */ + +/* num_modules is the number of currently active modules. */ +static int num_modules = 0; +/* total_modules is the number of modules linked in. */ +static int total_modules = 0; +module *top_module = NULL; + +typedef int (*handler)(request_rec *); +typedef void *(*maker)(pool *); +typedef void *(*dir_maker)(pool *, char *); +typedef void *(*merger)(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). + */ + +void * +get_module_config (void *conf_vector, module *m) +{ + void **confv = (void**)conf_vector; + return confv[m->module_index]; +} + +void +set_module_config (void *conf_vector, module *m, void *val) +{ + void **confv = (void**)conf_vector; + confv[m->module_index] = val; +} + +void * +create_empty_config (pool *p) +{ + void **conf_vector = (void **)pcalloc(p, sizeof(void*) * total_modules); + return (void *)conf_vector; +} + +void * +create_default_per_dir_config (pool *p) +{ + void **conf_vector = (void **)pcalloc(p, sizeof(void*) * (total_modules+DYNAMIC_MODULE_LIMIT)); + module *modp; + + for (modp = top_module; modp; modp = modp->next) { + dir_maker df = modp->create_dir_config; + + if (df) conf_vector[modp->module_index] = (*df)(p, NULL); + } + + return (void*)conf_vector; +} + +void * +merge_per_dir_configs (pool *p, void *base, void *new) +{ + void **conf_vector = (void **)pcalloc(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 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; +} + +void * +create_server_config (pool *p, server_rec *s) +{ + void **conf_vector = (void **)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; +} + +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 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 *create_connection_config (pool *p) { + return create_empty_config (p); +} + +void *create_request_config (pool *p) { + return create_empty_config (p); +} + +void *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(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. + */ + +int +run_method (request_rec *r, int offset, int run_all) +{ + module *modp; + for (modp = top_module; modp; modp = modp->next) { + handler mod_handler = *(handler *)(offset + (char *)(modp)); + + if (mod_handler) { + int result; + + Explain1("Run %s",ShowMethod(modp,offset)); + result = (*mod_handler)(r); + + Explain2("%s returned %d",ShowMethod(modp,offset),result); + if (result != DECLINED && (!run_all || result != OK)) + return result; + } + } + + return run_all ? OK : DECLINED; +} + +int translate_name(request_rec *r) { + return run_method (r, XtOffsetOf (module, translate_handler), 0); +} + +int check_access(request_rec *r) { + return run_method (r, XtOffsetOf (module, access_checker), 1); +} + +int find_types (request_rec *r) { + return run_method (r, XtOffsetOf (module, type_checker), 0); +} + +int run_fixups (request_rec *r) { + return run_method (r, XtOffsetOf (module, fixer_upper), 1); +} + +int log_transaction (request_rec *r) { + return run_method (r, XtOffsetOf (module, logger), 1); +} + +int header_parse (request_rec *r) { + return run_method (r, XtOffsetOf (module, header_parser), 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 check_user_id (request_rec *r) { + return run_method (r, XtOffsetOf (module, check_user_id), 0); +} + +int check_auth (request_rec *r) { + return run_method (r, XtOffsetOf (module, auth_checker), 0); +} + +int invoke_handler (request_rec *r) +{ + module *modp; + handler_rec *handp; + char *content_type = r->content_type ? r->content_type : default_type (r); + char *handler, *p; + + if ((p = strchr(content_type, ';')) != NULL) { /* MIME type arguments */ + while (p > content_type && p[-1] == ' ') --p; /* strip trailing spaces */ + content_type = pstrndup(r->pool, content_type, p - content_type); + } + handler = r->handler ? r->handler : content_type; + + /* Pass one --- direct matches */ + + for (modp = top_module; modp; modp = modp->next) + { + if (!modp->handlers) continue; + + for (handp = modp->handlers; handp->content_type; ++handp) { + if (!strcasecmp (handler, handp->content_type)) { + int result = (*handp->handler)(r); + + if (result != DECLINED) return result; + } + } + } + + /* Pass two --- wildcard matches */ + + for (modp = top_module; modp; modp = modp->next) + { + if (!modp->handlers) continue; + + for (handp = modp->handlers; handp->content_type; ++handp) { + char *starp = strchr (handp->content_type, '*'); + int len; + + if (!starp) continue; + + len = starp - handp->content_type; + + if (!len || !strncasecmp (handler, handp->content_type, len)) + { + int result = (*handp->handler)(r); + + if (result != DECLINED) return result; + } + } + } + + return NOT_IMPLEMENTED; +} + +/* One-time setup for precompiled modules --- NOT to be done on restart */ + +void 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... + */ + + /* At some point, we may want to offer back-compatibility for + * loading modules that are for older versions of Apache. For now, + * though, we don't. + */ + + if (m->version != MODULE_MAGIC_NUMBER) { + fprintf(stderr, "httpd: module \"%s\" is not compatible with this " + "version of Apache.\n", m->name); + fprintf(stderr, "Please contact the author 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 = num_modules++; + } +} + +void setup_prelinked_modules () +{ + extern module *prelinked_modules[], *preloaded_modules[]; + module **m; + + /* First, set all module indices, and init total_modules. */ + total_modules = 0; + for (m = preloaded_modules; *m; ++m, ++total_modules) { + (*m)->module_index = total_modules; + } + + for (m = prelinked_modules; *m; ++m) { + add_module (*m); + } +} + +const char *find_module_name (module *m) +{ + return m->name; +} + +module *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. */ +int add_named_module (const char *name) +{ + extern module *preloaded_modules[]; + module *modp; + int i = 0; + + for (modp = preloaded_modules[i]; modp; modp = preloaded_modules[++i]) { + if (strcmp(modp->name, name) == 0) { + /* Only add modules that are not already enabled. */ + if (modp->next == NULL) { + add_module(modp); + } + return 1; + } + } + + return 0; +} + +/* Clear the internal list of modules, in preparation for starting over. */ +void clear_module_list () +{ + module **m = &top_module; + module **next_m; + + while (*m) { + next_m = &((*m)->next); + *m = NULL; + m = next_m; + } + + num_modules = 0; + + /* This is required; so we add it always. */ + 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... + */ + +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 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 (*cmd->func) (parms, mconfig, args); + + case NO_ARGS: + if (*args != 0) + return pstrcat (parms->pool, cmd->name, " takes no arguments", + NULL); + + return (*cmd->func) (parms, mconfig); + + case TAKE1: + w = getword_conf (parms->pool, &args); + + if (*w == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes one argument", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w); + + case TAKE2: + + w = getword_conf (parms->pool, &args); + w2 = getword_conf (parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w, w2); + + case TAKE12: + + w = getword_conf (parms->pool, &args); + w2 = getword_conf (parms->pool, &args); + + if (*w == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes 1-2 arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w, *w2 ? w2 : NULL); + + case TAKE3: + + w = getword_conf (parms->pool, &args); + w2 = getword_conf (parms->pool, &args); + w3 = getword_conf (parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w, w2, w3); + + case TAKE23: + + w = getword_conf (parms->pool, &args); + w2 = getword_conf (parms->pool, &args); + w3 = *args ? getword_conf (parms->pool, &args) : NULL; + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w, w2, w3); + + case TAKE123: + + w = getword_conf (parms->pool, &args); + w2 = *args ? getword_conf (parms->pool, &args) : NULL; + w3 = *args ? getword_conf (parms->pool, &args) : NULL; + + if (*w == '\0' || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes one, two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w, w2, w3); + + case TAKE13: + + w = getword_conf (parms->pool, &args); + w2 = *args ? getword_conf (parms->pool, &args) : NULL; + w3 = *args ? getword_conf (parms->pool, &args) : NULL; + + if (*w == '\0' || (*w2 && !w3) || *args != 0) + return pstrcat (parms->pool, cmd->name, " takes one or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return (*cmd->func) (parms, mconfig, w, w2, w3); + + case ITERATE: + + while (*(w = getword_conf (parms->pool, &args)) != '\0') + if ((errmsg = (*cmd->func) (parms, mconfig, w))) + return errmsg; + + return NULL; + + case ITERATE2: + + w = getword_conf (parms->pool, &args); + + if (*w == '\0' || *args == 0) + return pstrcat(parms->pool, cmd->name, + " requires at least two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + + while (*(w2 = getword_conf (parms->pool, &args)) != '\0') + if ((errmsg = (*cmd->func) (parms, mconfig, w, w2))) + return errmsg; + + return NULL; + + case FLAG: + + w = getword_conf (parms->pool, &args); + + if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp (w, "off"))) + return pstrcat (parms->pool, cmd->name, " must be On or Off", + NULL); + + return (*cmd->func) (parms, mconfig, strcasecmp (w, "off") != 0); + + default: + + return pstrcat (parms->pool, cmd->name, + " is improperly configured internally (server bug)", + NULL); + } +} + +const command_rec *find_command (const char *name, const command_rec *cmds) +{ + while (cmds->name) + if (!strcasecmp (name, cmds->name)) + return cmds; + else + ++cmds; + + return NULL; +} + +const command_rec *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 = find_command (cmd_name, modp->cmds))) { + *mod = modp; + return cmdp; + } + + return NULL; +} + +const char *handle_command (cmd_parms *parms, void *config, const char *l) +{ + const char *args, *cmd_name, *retval; + const command_rec *cmd; + module *mod = top_module; + + ++parms->config_line; + if((l[0] == '#') || (!l[0])) return NULL; + + args = l; + cmd_name = getword_conf (parms->temp_pool, &args); + if (*cmd_name == '\0') return NULL; + + do { + if (!(cmd = find_command_in_modules (cmd_name, &mod))) { + return pstrcat (parms->pool, "Invalid command ", cmd_name, NULL); + } + else { + void *mconfig = get_module_config (config, mod); + void *sconfig = + get_module_config (parms->server->module_config, mod); + + if (!mconfig && mod->create_dir_config) { + mconfig = (*mod->create_dir_config) (parms->pool, parms->path); + set_module_config (config, mod, mconfig); + } + + if (!sconfig && mod->create_server_config) { + sconfig = + (*mod->create_server_config)(parms->pool, parms->server); + set_module_config (parms->server->module_config, mod, sconfig); + } + + retval = invoke_cmd (cmd, parms, mconfig, args); + mod = mod->next; /* Next time around, skip this one */ + } + } while (retval && !strcmp(retval, DECLINE_CMD)); + + return retval; +} + +const char *srm_command_loop (cmd_parms *parms, void *config) +{ + char l[MAX_STRING_LEN]; + + while (!(cfg_getline (l, MAX_STRING_LEN, parms->infile))) { + const char *errmsg = handle_command (parms, config, l); + if (errmsg) return errmsg; + } + + return NULL; +} + +/* + * Generic command functions... + */ + +const char *set_string_slot (cmd_parms *cmd, char *struct_ptr, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int)cmd->info; + *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg); + return NULL; +} + +const char *set_flag_slot (cmd_parms *cmd, char *struct_ptr, int arg) +{ + /* This one's pretty generic too... */ + + int offset = (int)cmd->info; + *(int *)(struct_ptr + offset) = arg ? 1 : 0; + return NULL; +} + +/***************************************************************** + * + * Reading whole config files... + */ + +cmd_parms default_parms = { NULL, 0, -1, NULL, 0, NULL, NULL, NULL, NULL }; + +char *server_root_relative (pool *p, char *file) +{ +#ifdef __EMX__ + /* Add support for OS/2 drive names */ + if ((file[0] == '/') || (file[1] == ':')) return file; +#else + if (file[0] == '/') return file; +#endif + return make_full_path (p, server_root, file); +} + +void process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp) +{ + FILE *cfg; + const char *errmsg; + cmd_parms parms; + struct stat finfo; + + fname = server_root_relative (p, fname); + + if (!(strcmp(fname, server_root_relative(p, RESOURCE_CONFIG_FILE))) || + !(strcmp(fname, server_root_relative(p, ACCESS_CONFIG_FILE)))) { + if (stat(fname, &finfo) == -1) + return; + } + + /* GCC's initialization extensions are soooo nice here... */ + + parms = default_parms; + parms.config_file = fname; + parms.pool = p; + parms.temp_pool = ptemp; + parms.server = s; + parms.override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT); + + if(!(cfg = fopen(fname, "r"))) { + perror("fopen"); + fprintf(stderr,"httpd: could not open document config file %s\n", + fname); + exit(1); + } + + parms.infile = cfg; + + errmsg = srm_command_loop (&parms, s->lookup_defaults); + + if (errmsg) { + fprintf (stderr, "Syntax error on line %d of %s:\n", + parms.config_line, fname); + fprintf (stderr, "%s\n", errmsg); + exit(1); + } + + fclose(cfg); +} + + +int parse_htaccess(void **result, request_rec *r, int override, + char *d, char *filename) +{ + FILE *f; + cmd_parms parms; + const char *errmsg; + const struct htaccess_result *cache; + struct htaccess_result *new; + void *dc; + +/* 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 = d; + + if((f=pfopen(r->pool, filename, "r"))) { + dc = create_per_dir_config (r->pool); + + parms.infile = f; + parms.config_file = filename; + + errmsg = srm_command_loop (&parms, dc); + + pfclose(r->pool, f); + + if (errmsg) { + log_reason (errmsg, filename, r); + return SERVER_ERROR; + } + + *result = dc; + } else { + if (errno == ENOENT || errno == ENOTDIR) + dc = NULL; + else { + log_unixerr("pfopen", filename, + "unable to check htaccess file, ensure it is readable", + r->server); + return HTTP_FORBIDDEN; + } + } + +/* cache it */ + new = palloc(r->pool, sizeof(struct htaccess_result)); + new->dir = pstrdup(r->pool, d); + new->override = override; + new->htaccess = dc; +/* add to head of list */ + new->next = r->htaccess; + r->htaccess = new; + + return OK; +} + +/***************************************************************** + * + * Virtual host stuff; note that the commands that invoke this stuff + * are with the command table in http_core.c. + */ + +/* + * Parses a host of the form <address>[: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 void 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; + + t = strchr(w, ':'); + if (t) { + if( strcmp(t+1,"*") == 0 ) { + port = 0; + } else if( (i = atoi(t+1)) ) { + port = i; + } else { + fprintf( stderr, "Port must be numeric\n" ); + } + *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( +#ifdef DGUX + ( my_addr = inet_network(w) ) +#else + ( my_addr = inet_addr(w) ) +#endif + != INADDR_NONE ) { + is_an_ip_addr = 1; + } + if( is_an_ip_addr ) { + sar = pcalloc( p, sizeof( server_addr_rec ) ); + **paddr = sar; + *paddr = &sar->next; + sar->host_addr.s_addr = my_addr; + sar->host_port = port; + sar->virthost = pstrdup(p, w); + if (t != NULL) *t = ':'; + return; + } + + hep = gethostbyname(w); + + if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { + fprintf (stderr, "Cannot resolve host name %s --- ignoring!\n", w); + if (t != NULL) *t = ':'; + return; + } + + for( i = 0; hep->h_addr_list[i]; ++i ) { + sar = 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 = pstrdup(p, w); + } + + if (t != NULL) *t = ':'; +} + +server_rec *init_virtual_host (pool *p, const char *hostname, + server_rec *main_server) +{ + server_rec *s = (server_rec *)pcalloc (p, sizeof (server_rec)); + server_addr_rec **addrs; + +#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; + /* start the list of addreses */ + addrs = &s->addrs; + while( hostname[0] ) { + get_addresses( p, getword_conf( p, &hostname ), &addrs, + main_server->port ); + } + /* terminate the list */ + *addrs = NULL; + if( s->addrs ) { + if (s->addrs->host_port) { + s->port = s->addrs->host_port; /* set them the same, by default */ + } else { + /* otherwise we get a port of 0 on redirects */ + s->port = main_server->port; + } + } + s->next = NULL; + + s->is_virtual = 1; + s->names = NULL; + + s->module_config = create_empty_config (p); + s->lookup_defaults = create_per_dir_config (p); + + s->server_uid = user_id; + s->server_gid = group_id; + + return s; +} + +int is_virtual_server (server_rec *s) +{ + return s->is_virtual; +} + +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 = + 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; + } +} + +/***************************************************************** + * + * Getting *everything* configured... + */ + +void init_config_globals (pool *p) +{ + /* ServerRoot, server_confname set in httpd.c */ + + standalone = 1; + user_name = DEFAULT_USER; + user_id = uname2id(DEFAULT_USER); + group_id = gname2id(DEFAULT_GROUP); + daemons_to_start = DEFAULT_START_DAEMON; + daemons_min_free = DEFAULT_MIN_FREE_DAEMON; + daemons_max_free = DEFAULT_MAX_FREE_DAEMON; + daemons_limit = HARD_SERVER_LIMIT; + pid_fname = DEFAULT_PIDLOG; + scoreboard_fname = DEFAULT_SCOREBOARD; + lock_fname = DEFAULT_LOCKFILE; + max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + bind_address.s_addr = htonl(INADDR_ANY); + listeners = NULL; +} + +server_rec *init_server_config(pool *p) +{ + server_rec *s = (server_rec *)pcalloc (p, sizeof (server_rec)); + + s->port = DEFAULT_PORT; + s->server_admin = DEFAULT_ADMIN; + s->server_hostname = NULL; + s->error_fname = DEFAULT_ERRORLOG; + s->error_log = stderr; + s->srm_confname = RESOURCE_CONFIG_FILE; + s->access_confname = ACCESS_CONFIG_FILE; + 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 = pcalloc(p, sizeof (server_addr_rec)); + s->addrs->host_addr.s_addr = htonl (INADDR_ANY); /* NOT virtual host; + * don't match any real network + * interface. + */ + s->addrs->host_port = 0; /* matches any port */ + + s->module_config = create_server_config (p, s); + s->lookup_defaults = create_default_per_dir_config (p); + + return s; +} + +server_rec *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_resource_config (s, confname, p, ptemp); + process_resource_config (s, s->srm_confname, p, ptemp); + process_resource_config (s, s->access_confname, p, ptemp); + + fixup_virtual_hosts (p, s); + + return s; +} + + +void init_modules(pool *p, server_rec *s) +{ + module *m; + + for (m = top_module; m; m = m->next) + if (m->init) + (*m->init) (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 <Directory> or <Location> --> 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. + */ +void show_overrides(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 <Directory> or <Location>"); + else + printf("only inside <Directory> or <Location>"); + + /* Warn if the directive is allowed inside <Directory> 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 show_directives() +{ + extern module *preloaded_modules[]; + command_rec *pc; + int n; + + for (n = 0; preloaded_modules[n]; ++n) + for (pc = preloaded_modules[n]->cmds; pc && pc->name; ++pc) { + printf("%s\n", pc->name); + if (pc->errmsg) + printf("\t%s\n", pc->errmsg); + printf("\t%s\n", preloaded_modules[n]->name); + show_overrides(pc, preloaded_modules[n]); + } +} + +/* Show the preloaded module names. Used for httpd -l. */ +void show_modules() +{ + extern module *preloaded_modules[]; + int n; + + printf ("Compiled-in modules:\n"); + for (n = 0; preloaded_modules[n]; ++n) + printf (" %s\n", preloaded_modules[n]->name); +} + diff --git a/usr.sbin/httpd/src/http_config.h b/usr.sbin/httpd/src/http_config.h new file mode 100644 index 00000000000..02d041854d3 --- /dev/null +++ b/usr.sbin/httpd/src/http_config.h @@ -0,0 +1,298 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * The central data structures around here... + */ + +/* Command dispatch structures... */ + +enum cmd_how { + RAW_ARGS, /* cmd_func parses command line itself */ + TAKE1, /* one argument only */ + TAKE2, /* two arguments only */ + ITERATE, /* one argument, occuring multiple times + * (e.g., IndexIgnore) + */ + ITERATE2, /* two arguments, 2nd occurs multiple times + * (e.g., AddIcon) + */ + FLAG, /* One of 'On' or 'Off' */ + NO_ARGS, /* No args at all, e.g. </Directory> */ + TAKE12, /* one or two arguments */ + TAKE3, /* three arguments only */ + TAKE23, /* two or three arguments */ + TAKE123, /* one, two or three arguments */ + TAKE13 /* one or three arguments */ +}; + +typedef struct command_struct { + char *name; /* Name of this command */ + const char *(*func)(); /* Function invoked */ + void *cmd_data; /* Extra data, for functions which + * implement multiple commands... + */ + int req_override; /* What overrides need to be allowed to + * enable this command. + */ + enum cmd_how args_how; /* What the command expects as arguments */ + + char *errmsg; /* 'usage' message, in case of syntax errors */ +} command_rec; + +/* The allowed locations for a configuration directive are the union of + * those indicated by each set bit in the req_override mask. + * + * (req_override & RSRC_CONF) => *.conf outside <Directory> or <Location> + * (req_override & ACCESS_CONF) => *.conf inside <Directory> or <Location> + * (req_override & OR_AUTHCFG) => *.conf inside <Directory> or <Location> + * and .htaccess when AllowOverride AuthConfig + * (req_override & OR_LIMIT) => *.conf inside <Directory> or <Location> + * and .htaccess when AllowOverride Limit + * (req_override & OR_OPTIONS) => *.conf anywhere + * and .htaccess when AllowOverride Options + * (req_override & OR_FILEINFO) => *.conf anywhere + * and .htaccess when AllowOverride FileInfo + * (req_override & OR_INDEXES) => *.conf anywhere + * and .htaccess when AllowOverride Indexes + */ +#define OR_NONE 0 +#define OR_LIMIT 1 +#define OR_OPTIONS 2 +#define OR_FILEINFO 4 +#define OR_AUTHCFG 8 +#define OR_INDEXES 16 +#define OR_UNSET 32 +#define ACCESS_CONF 64 +#define RSRC_CONF 128 +#define OR_ALL (OR_LIMIT|OR_OPTIONS|OR_FILEINFO|OR_AUTHCFG|OR_INDEXES) + +/* This can be returned by a function if they don't wish to handle + * a command. Make it something not likely someone will actually use + * as an error code. + */ + +#define DECLINE_CMD "\a\b" + +/* + * This structure is passed to a command which is being invoked, + * to carry a large variety of miscellaneous data which is all of + * use to *somebody*... + */ + +typedef struct { + void *info; /* Argument to command from cmd_table */ + int override; /* Which allow-override bits are set */ + int limited; /* Which methods are <Limit>ed */ + + char *config_file; /* Filename cmd read from */ + int config_line; /* Line cmd read from */ + FILE *infile; /* fd for more lines (not currently used) */ + + pool *pool; /* Pool to allocate new storage in */ + pool *temp_pool; /* Pool for scratch memory; persists during + * configuration, but wiped before the first + * request is served... + */ + server_rec *server; /* Server_rec being configured for */ + char *path; /* If configuring for a directory, + * pathname of that directory. + */ + const command_rec *cmd; /* configuration command */ +} cmd_parms; + +/* This structure records the existence of handlers in a module... */ + +typedef struct { + char *content_type; + int (*handler) (request_rec *); +} handler_rec; + +/* + * Module structures. Just about everything is dispatched through + * these, directly or indirectly (through the command and handler + * tables). + */ + +typedef struct module_struct { + int version; /* API version, *not* module version; + * check that module is compatible with this + * version of the server. + */ + int module_index; /* Index to this modules structures in + * config vectors. + */ + + const char *name; + + struct module_struct *next; + +#ifdef ULTRIX_BRAIN_DEATH + void (*init)(); + void *(*create_dir_config)(); + void *(*merge_dir_config)(); + void *(*create_server_config)(); + void *(*merge_server_config)(); +#else + void (*init)(server_rec *, pool *); + void *(*create_dir_config)(pool *p, char *dir); + void *(*merge_dir_config)(pool *p, void *base_conf, void *new_conf); + void *(*create_server_config)(pool *p, server_rec *s); + void *(*merge_server_config)(pool *p, void *base_conf, void *new_conf); +#endif + + command_rec *cmds; + handler_rec *handlers; + + /* Hooks for getting into the middle of server ops... + * + * translate_handler --- translate URI to filename + * access_checker --- check access by host address, etc. All of these + * run; if all decline, that's still OK. + * check_user_id --- get and validate user id from the HTTP request + * auth_checker --- see if the user (from check_user_id) is OK *here*. + * If all of *these* decline, the request is rejected + * (as a SERVER_ERROR, since the module which was + * supposed to handle this was configured wrong). + * type_checker --- Determine MIME type of the requested entity; + * sets content_type, _encoding and _language fields. + * logger --- log a transaction. Not supported yet out of sheer + * laziness on my part. + */ + + int (*translate_handler)(request_rec *); + int (*check_user_id)(request_rec *); + int (*auth_checker)(request_rec *); + int (*access_checker)(request_rec *); + int (*type_checker)(request_rec *); + int (*fixer_upper)(request_rec *); + int (*logger)(request_rec *); + int (*header_parser)(request_rec *); +} module; + +/* Initializer for the first few module slots, which are only + * really set up once we start running. Note that the first word + * is a version check; this should allow us to deal with changes to + * the API (the server can detect an old-format module, and either + * handle it back-compatibly, or at least signal an error). + */ + +#define MODULE_MAGIC_NUMBER 19970622 +#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, -1, __FILE__, NULL + +/* Generic accessors for other modules to get at their own module-specific + * data + */ + +void *get_module_config (void *conf_vector, module *m); +void set_module_config (void *conf_vector, module *m, void *val); + +/* Generic command handling function... */ + +const char *set_string_slot (cmd_parms *, char *, char *); +const char *set_flag_slot (cmd_parms *, char *, int); + +/* For modules which need to read config files, open logs, etc. ... + * this returns the fname argument if it begins with '/'; otherwise + * it relativizes it wrt server_root. + */ + +char *server_root_relative (pool *p, char *fname); + +/* Finally, the hook for dynamically loading modules in... */ + +void add_module (module *m); +int add_named_module (const char *name); +void clear_module_list (); +const char *find_module_name (module *m); +module *find_linked_module (const char *name); + +#ifdef CORE_PRIVATE + +/* For http_main.c... */ + +server_rec *read_config (pool *conf_pool, pool *temp_pool, char *config_name); +void init_modules(pool *p, server_rec *s); +void setup_prelinked_modules(); +void show_directives(); +void show_modules(); + +/* For http_request.c... */ + +void *create_request_config (pool *p); +void *create_per_dir_config (pool *p); +void *merge_per_dir_configs (pool *p, void *base, void *new); + +/* For http_core.c... (<Directory> command and virtual hosts) */ + +int parse_htaccess(void **result, request_rec *r, int override, + char *path, char *file); +const char *srm_command_loop (cmd_parms *parms, void *config); + +server_rec *init_virtual_host (pool *p, const char *hostname, server_rec *main_server); +int is_virtual_server (server_rec *); +void process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp); + +/* Module-method dispatchers, also for http_request.c */ + +int translate_name (request_rec *); +int directory_walk (request_rec *); /* check symlinks, get per-dir config */ +int check_access (request_rec *); /* check access on non-auth basis */ +int check_user_id (request_rec *); /* obtain valid username from client auth */ +int check_auth (request_rec *); /* check (validated) user is authorized here */ +int find_types (request_rec *); /* identify MIME type */ +int run_fixups (request_rec *); /* poke around for other metainfo, etc.... */ +int invoke_handler (request_rec *); +int log_transaction (request_rec *r); +int header_parse (request_rec *); + +#endif diff --git a/usr.sbin/httpd/src/http_core.c b/usr.sbin/httpd/src/http_core.c new file mode 100644 index 00000000000..ce44d64eea2 --- /dev/null +++ b/usr.sbin/httpd/src/http_core.c @@ -0,0 +1,1413 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +#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_conf_globals.h" + +#include "http_main.h" /* For the default_handler below... */ +#include "http_log.h" +#include "rfc1413.h" +#include "util_md5.h" +#include "scoreboard.h" + +/* 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. + */ + +void *create_core_dir_config (pool *a, char *dir) +{ + core_dir_config *conf = + (core_dir_config *)pcalloc(a, sizeof(core_dir_config)); + + if (!dir || dir[strlen(dir) - 1] == '/') conf->d = dir; + else if (strncmp(dir,"proxy:",6)==0) conf->d = pstrdup (a, dir); + else conf->d = pstrcat (a, dir, "/", NULL); + conf->d_is_matchexp = conf->d ? is_matchexp( conf->d ) : 0; + + + conf->opts = dir ? OPT_UNSET : OPT_ALL; + conf->opts_add = conf->opts_remove = OPT_NONE; + conf->override = dir ? OR_UNSET : OR_ALL; + + conf->content_md5 = 2; + + conf->hostname_lookups = 2;/* binary, but will use 2 as an "unset = on" */ + 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) + conf->limit_mem = NULL; +#endif +#ifdef RLIMIT_NPROC + conf->limit_nproc = NULL; +#endif + + conf->sec = make_array (a, 2, sizeof(void *)); + + return (void *)conf; +} + +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 = + (core_dir_config *)pcalloc (a, sizeof(core_dir_config)); + int i; + + memcpy ((char *)conf, (const char *)base, sizeof(core_dir_config)); + if( base->response_code_strings ) { + conf->response_code_strings = 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_matchexp = new->d_is_matchexp; + conf->r = new->r; + + if (new->opts != OPT_UNSET) conf->opts = new->opts; + if (new->opts_add) conf->opts |= new->opts_add; + if (new->opts_remove) conf->opts &= ~(new->opts_remove); + + if (new->override != OR_UNSET) conf->override = new->override; + if (new->default_type) conf->default_type = new->default_type; + + if (new->auth_type) conf->auth_type = new->auth_type; + if (new->auth_name) conf->auth_name = new->auth_name; + if (new->requires) conf->requires = new->requires; + + if( new->response_code_strings ) { + if( conf->response_code_strings == NULL ) { + conf->response_code_strings = 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 != 2) + 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; + +#ifdef RLIMIT_CPU + if (new->limit_cpu) conf->limit_cpu = new->limit_cpu; +#endif +#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) + 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 + + conf->sec = append_arrays (a, base->sec, new->sec); + + if (new->satisfy != SATISFY_NOSPEC) conf->satisfy = new->satisfy; + return (void*)conf; +} + +void *create_core_server_config (pool *a, server_rec *s) +{ + core_server_config *conf = + (core_server_config *)pcalloc(a, sizeof(core_server_config)); + int is_virtual = s->is_virtual; + + conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME; + conf->document_root = is_virtual ? NULL : DOCUMENT_LOCATION; + conf->sec = make_array (a, 40, sizeof(void *)); + conf->sec_url = make_array (a, 40, sizeof(void *)); + + return (void *)conf; +} + +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 = + (core_server_config *)pcalloc(p, sizeof(core_server_config)); + + *conf = *virt; + if (!conf->access_name) conf->access_name = base->access_name; + if (!conf->document_root) conf->document_root = base->document_root; + conf->sec = append_arrays (p, base->sec, virt->sec); + conf->sec_url = append_arrays (p, base->sec_url, virt->sec_url); + + return conf; +} + +/* Add per-directory configuration entry (for <directory> section); + * these are part of the core server config. + */ + +void add_per_dir_conf (server_rec *s, void *dir_config) +{ + core_server_config *sconf = get_module_config (s->module_config, + &core_module); + void **new_space = (void **) push_array (sconf->sec); + + *new_space = dir_config; +} + +void add_per_url_conf (server_rec *s, void *url_config) +{ + core_server_config *sconf = get_module_config (s->module_config, + &core_module); + void **new_space = (void **) push_array (sconf->sec_url); + + *new_space = url_config; +} + +void add_file_conf (core_dir_config *conf, void *url_config) +{ + void **new_space = (void **) push_array (conf->sec); + + *new_space = url_config; +} + +/***************************************************************** + * + * 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... + */ + +int allow_options (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->opts; +} + +int allow_overrides (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->override; +} + +char *auth_type (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->auth_type; +} + +char *auth_name (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->auth_name; +} + +char *default_type (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->default_type ? conf->default_type : DEFAULT_TYPE; +} + +char *document_root (request_rec *r) /* Don't use this!!! */ +{ + core_server_config *conf = + (core_server_config *)get_module_config(r->server->module_config, + &core_module); + + return conf->document_root; +} + +array_header *requires (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + return conf->requires; +} + +int satisfies (request_rec *r) +{ + core_dir_config *conf = + (core_dir_config *)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 *response_code_string (request_rec *r, int error_index) +{ + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + + if( conf->response_code_strings == NULL ) { + return NULL; + } + return conf->response_code_strings[error_index]; +} + +const char * +get_remote_host(conn_rec *conn, void *dir_config, int type) +{ + struct in_addr *iaddr; + struct hostent *hptr; +#ifdef MAXIMUM_DNS + char **haddr; +#endif + core_dir_config *dir_conf = NULL; + +/* If we haven't checked the host name, and we want to */ + if (dir_config) + dir_conf = (core_dir_config *)get_module_config(dir_config, &core_module); + + if ((!dir_conf) || (type != REMOTE_NOLOOKUP && conn->remote_host == NULL && dir_conf->hostname_lookups)) + { +#ifdef STATUS + int old_stat = update_child_status(conn->child_num, + SERVER_BUSY_DNS, + (request_rec*)NULL); +#endif /* STATUS */ + iaddr = &(conn->remote_addr.sin_addr); + hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET); + if (hptr != NULL) + { + conn->remote_host = pstrdup(conn->pool, (void *)hptr->h_name); + str_tolower (conn->remote_host); + +#ifdef MAXIMUM_DNS + /* Grrr. Check THAT name to make sure it's really the name of the addr. */ + /* Code from Harald Hanche-Olsen <hanche@imf.unit.no> */ + + hptr = gethostbyname(conn->remote_host); + if (hptr) + { + for(haddr=hptr->h_addr_list; *haddr; haddr++) + if(((struct in_addr *)(*haddr))->s_addr == iaddr->s_addr) + break; + } + if((!hptr) || (!(*haddr))) + conn->remote_host = NULL; +#endif + } +/* if failed, set it to the NULL string to indicate error */ + if (conn->remote_host == NULL) conn->remote_host = ""; +#ifdef STATUS + (void)update_child_status(conn->child_num,old_stat,(request_rec*)NULL); +#endif /* STATUS */ + } + +/* + * 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) return NULL; + else return conn->remote_ip; + } +} + +const char * +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 *) + get_module_config(r->per_dir_config, &core_module); + + if (dir_conf->do_rfc1413 & 1) + return rfc1413(r->connection, r->server); + else + return NULL; +} + +/***************************************************************** + * + * Commands... this module handles almost all of the NCSA httpd.conf + * commands, but most of the old srm.conf is in the the modules. + */ + +const char *set_access_name (cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = get_module_config (sconf, &core_module); + + conf->access_name = arg; + return NULL; +} + +const char *set_document_root (cmd_parms *cmd, void *dummy, char *arg) +{ + void *sconf = cmd->server->module_config; + core_server_config *conf = get_module_config (sconf, &core_module); + + if (!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->document_root = arg; + return NULL; +} + +const char *set_error_document (cmd_parms *cmd, core_dir_config *conf, + char *line) +{ + int error_number, index_number, idx500; + char *w; + + /* 1st parameter should be a 3 digit number, which we recognize; + * convert it into an array index + */ + + w = getword_conf_nc (cmd->pool, &line); + error_number = atoi(w); + + idx500 = index_of_response(HTTP_INTERNAL_SERVER_ERROR); + + if (error_number == HTTP_INTERNAL_SERVER_ERROR) + index_number = idx500; + else if ((index_number = index_of_response(error_number)) == idx500) + return pstrcat(cmd->pool, "Unsupported HTTP response code ", w, NULL); + + /* Store it... */ + + if( conf->response_code_strings == NULL ) { + conf->response_code_strings = pcalloc(cmd->pool, + sizeof(*conf->response_code_strings) * RESPONSE_CODES ); + } + conf->response_code_strings[index_number] = pstrdup (cmd->pool, line); + + return NULL; +} + +/* access.conf commands... + * + * The *only* thing that can appear in access.conf at top level is a + * <Directory> section. NB we need to have a way to cut the srm_command_loop + * invoked by dirsection (i.e., <Directory>) short when </Directory> is seen. + * We do that by returning an error, which dirsection itself recognizes and + * discards as harmless. Cheesy, but it works. + */ + +const char *set_override (cmd_parms *cmd, core_dir_config *d, const char *l) +{ + char *w; + + d->override = OR_NONE; + while(l[0]) { + w = 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 pstrcat (cmd->pool, "Illegal override option ", w, NULL); + } + + return NULL; +} + +const char *set_options (cmd_parms *cmd, core_dir_config *d, const char *l) +{ + char opt; + int first = 1; + char action; + + while(l[0]) { + char *w = 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 pstrcat (cmd->pool, "Illegal option ", w, NULL); + + if (action == '-') + d->opts_remove |= opt; + else if (action == '+') + d->opts_add |= opt; + else + d->opts |= opt; + } + + return NULL; +} + +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; +} + +const char *require (cmd_parms *cmd, core_dir_config *c, char *arg) +{ + require_line *r; + + if (!c->requires) + c->requires = make_array (cmd->pool, 2, sizeof(require_line)); + + r = (require_line *)push_array (c->requires); + r->requirement = pstrdup (cmd->pool, arg); + r->method_mask = cmd->limited; + return NULL; +} + +const char *limit (cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *limited_methods = getword(cmd->pool,&arg,'>'); + int limited = 0; + + if (cmd->limited > 0) return "Can't nest <Limit> sections"; + + while(limited_methods[0]) { + char *method = getword_conf (cmd->pool, &limited_methods); + if(!strcasecmp(method,"GET")) limited |= (1 << M_GET); + else if(!strcasecmp(method,"PUT")) limited |= (1 << M_PUT); + else if(!strcasecmp(method,"POST")) limited |= (1 << M_POST); + else if(!strcasecmp(method,"DELETE")) limited |= (1 << M_DELETE); + else if(!strcasecmp(method,"CONNECT")) limited |= (1 << M_CONNECT); + else if(!strcasecmp(method,"OPTIONS")) limited |= (1 << M_OPTIONS); + else return "unknown method in <Limit>"; + } + + cmd->limited = limited; + return NULL; +} + +const char *endlimit (cmd_parms *cmd, void *dummy, void *dummy2) +{ + if (cmd->limited == -1) return "</Limit> unexpected"; + + cmd->limited = -1; + return NULL; +} + +static const char end_dir_magic[] = "</Directory> outside of any <Directory> section"; + +const char *end_dirsection (cmd_parms *cmd, void *dummy) { + return end_dir_magic; +} + +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 = create_per_dir_config (cmd->pool); + regex_t *r = NULL; + + if (endp) *endp = '\0'; + + if (cmd->path) return "<Directory> sections don't nest"; + if (cmd->limited != -1) return "Can't have <Directory> within <Limit>"; + + cmd->path = getword_conf (cmd->pool, &arg); +#ifdef __EMX__ + /* Fix OS/2 HPFS filename case problem. */ + cmd->path = strlwr(cmd->path); +#endif + cmd->override = OR_ALL|ACCESS_CONF; + + if (!strcmp(cmd->path, "~")) { + cmd->path = getword_conf (cmd->pool, &arg); + r = pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + } + + errmsg = srm_command_loop (cmd, new_dir_conf); + if (errmsg != end_dir_magic) return errmsg; + + conf = (core_dir_config *)get_module_config(new_dir_conf, &core_module); + conf->r = r; + + add_per_dir_conf (cmd->server, new_dir_conf); + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static const char end_url_magic[] = "</Location> outside of any <Location> section"; + +const char *end_urlsection (cmd_parms *cmd, void *dummy) { + return end_url_magic; +} + +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; + + void *new_url_conf = create_per_dir_config (cmd->pool); + + if (endp) *endp = '\0'; + + if (cmd->path) return "<Location> sections don't nest"; + if (cmd->limited != -1) return "Can't have <Location> within <Limit>"; + + cmd->path = getword_conf (cmd->pool, &arg); + cmd->override = OR_ALL|ACCESS_CONF; + + if (!strcmp(cmd->path, "~")) { + cmd->path = getword_conf (cmd->pool, &arg); + r = pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + } + + errmsg = srm_command_loop (cmd, new_url_conf); + if (errmsg != end_url_magic) return errmsg; + + conf = (core_dir_config *)get_module_config(new_url_conf, &core_module); + conf->d = pstrdup(cmd->pool, cmd->path); /* No mangling, please */ + conf->d_is_matchexp = is_matchexp( conf->d ); + conf->r = r; + + add_per_url_conf (cmd->server, new_url_conf); + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +static char *end_file_magic = "</Files> outside of any <Files> section"; + +const char *end_filesection (cmd_parms *cmd, void *dummy) { + return end_file_magic; +} + +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; + + void *new_file_conf = create_per_dir_config (cmd->pool); + + if (endp) *endp = '\0'; + + if (cmd->limited != -1) return "Can't have <Files> within <Limit>"; + + cmd->path = getword_conf (cmd->pool, &arg); + /* Only if not an .htaccess file */ + if (cmd->path) + cmd->override = OR_ALL|ACCESS_CONF; + + if (!strcmp(cmd->path, "~")) { + cmd->path = getword_conf (cmd->pool, &arg); + if (old_path && cmd->path[0] != '/' && cmd->path[0] != '^') + cmd->path = pstrcat(cmd->pool, "^", old_path, cmd->path, NULL); + r = pregcomp(cmd->pool, cmd->path, REG_EXTENDED); + } + else if (old_path && cmd->path[0] != '/') + cmd->path = pstrcat(cmd->pool, old_path, cmd->path, NULL); + + errmsg = srm_command_loop (cmd, new_file_conf); + if (errmsg != end_file_magic) return errmsg; + + conf = (core_dir_config *)get_module_config(new_file_conf, &core_module); + conf->d = pstrdup(cmd->pool, cmd->path); + conf->d_is_matchexp = is_matchexp( conf->d ); + conf->r = r; + + add_file_conf (c, new_file_conf); + + cmd->path = old_path; + cmd->override = old_overrides; + + return NULL; +} + +const char *end_ifmod (cmd_parms *cmd, void *dummy) { + return NULL; +} + +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) *endp = '\0'; + if (not) arg++; + + found = find_linked_module(arg); + + if ((!not && found) || (not && !found)) + return NULL; + + while (nest && !(cfg_getline (l, MAX_STRING_LEN, cmd->infile))) { + if (!strncasecmp(l, "<IfModule", 9)) + nest++; + if (!strcasecmp(l, "</IfModule>")) + nest--; + } + + return NULL; +} + +/* httpd.conf commands... beginning with the <VirtualHost> business */ + +const char end_virthost_magic[] = "</Virtualhost> out of place"; + +const char *end_virtualhost_section (cmd_parms *cmd, void *dummy) +{ + return end_virthost_magic; +} + +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; + + if (endp) *endp = '\0'; + + /* FIXME: There's another feature waiting to happen here -- since you + can now put multiple addresses/names on a single <VirtualHost> + 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 "<VirtualHost> doesn't nest!"; + + s = init_virtual_host (p, arg, main_server); + s->next = main_server->next; + main_server->next = s; + + cmd->server = s; + errmsg = srm_command_loop (cmd, s->lookup_defaults); + cmd->server = main_server; + + if (s->srm_confname) + process_resource_config (s, s->srm_confname, p, ptemp); + + if (s->access_confname) + process_resource_config (s, s->access_confname, p, ptemp); + + if (errmsg == end_virthost_magic) return NULL; + return errmsg; +} + +const char *add_module_command (cmd_parms *cmd, void *dummy, char *arg) +{ + if (add_named_module (arg)) + return NULL; + return "required module not found"; +} + +const char *clear_module_list_command (cmd_parms *cmd, void *dummy) +{ + clear_module_list (); + return NULL; +} + +const char *set_server_string_slot (cmd_parms *cmd, void *dummy, char *arg) +{ + /* This one's pretty generic... */ + + int offset = (int)cmd->info; + char *struct_ptr = (char *)cmd->server; + + *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg); + return NULL; +} + +const char *server_type (cmd_parms *cmd, void *dummy, char *arg) +{ + if (!strcasecmp (arg, "inetd")) standalone = 0; + else if (!strcasecmp (arg, "standalone")) standalone = 1; + else return "ServerType must be either 'inetd' or 'standalone'"; + + return NULL; +} + +const char *server_port (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->port = atoi (arg); + return NULL; +} + +const char *set_send_buffer_size (cmd_parms *cmd, void *dummy, char *arg) { + int s = atoi (arg); + if (s < 512 && s != 0) { + return "SendBufferSize must be >= 512 bytes, or 0 for system default."; + } + cmd->server->send_buffer_size = s; + return NULL; +} + +const char *set_user (cmd_parms *cmd, void *dummy, char *arg) +{ + if (!cmd->server->is_virtual) { + user_name = pstrdup (cmd->pool, arg); + cmd->server->server_uid = user_id = uname2id(arg); + } + else { + if (suexec_enabled) + cmd->server->server_uid = uname2id(arg); + else { + cmd->server->server_uid = user_id; + fprintf(stderr, + "Warning: User directive in <VirtualHost> " + "requires SUEXEC wrapper.\n"); + } + } +#if !defined (BIG_SECURITY_HOLE) + if (cmd->server->server_uid == 0) { + fprintf (stderr, +"Error:\tApache has not been designed to serve pages while running\n" +"\tas root. There are known race conditions that will allow any\n" +"\tlocal user to read any file on the system. Should you still\n" +"\tdesire to serve pages as root then add -DBIG_SECURITY_HOLE to\n" +"\tthe EXTRA_CFLAGS line in your src/Configuration file and rebuild\n" +"\tthe server. It is strongly suggested that you instead modify the\n" +"\tUser directive in your httpd.conf file to list a non-root user.\n"); + exit (1); + } +#endif + + return NULL; +} + +const char *set_group (cmd_parms *cmd, void *dummy, char *arg) +{ + if (!cmd->server->is_virtual) + cmd->server->server_gid = group_id = gname2id(arg); + else { + if (suexec_enabled) + cmd->server->server_gid = gname2id(arg); + else { + cmd->server->server_gid = group_id; + fprintf(stderr, + "Warning: Group directive in <VirtualHost> requires SUEXEC wrapper.\n"); + } + } + + return NULL; +} + +const char *set_server_root (cmd_parms *cmd, void *dummy, char *arg) { + if (!is_directory (arg)) return "ServerRoot must be a valid directory"; + strncpy (server_root, arg, sizeof(server_root)-1); + server_root[sizeof(server_root)-1] = '\0'; + return NULL; +} + +const char *set_timeout (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->timeout = atoi (arg); + return NULL; +} + +const char *set_keep_alive_timeout (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->keep_alive_timeout = atoi (arg); + return NULL; +} + +const char *set_keep_alive (cmd_parms *cmd, void *dummy, char *arg) { + /* 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; +} + +const char *set_keep_alive_max (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->keep_alive_max = atoi (arg); + return NULL; +} + +const char *set_pidfile (cmd_parms *cmd, void *dummy, char *arg) { + pid_fname = pstrdup (cmd->pool, arg); + return NULL; +} + +const char *set_scoreboard (cmd_parms *cmd, void *dummy, char *arg) { + scoreboard_fname = pstrdup (cmd->pool, arg); + return NULL; +} + +const char *set_lockfile (cmd_parms *cmd, void *dummy, char *arg) { + lock_fname = pstrdup (cmd->pool, arg); + return NULL; +} + +const char *set_idcheck (cmd_parms *cmd, core_dir_config *d, int arg) { + d->do_rfc1413 = arg; + return NULL; +} + +const char *set_hostname_lookups (cmd_parms *cmd, core_dir_config *d, int arg) +{ + d->hostname_lookups = arg; + return NULL; +} + +const char *set_serverpath (cmd_parms *cmd, void *dummy, char *arg) { + cmd->server->path = pstrdup (cmd->pool, arg); + cmd->server->pathlen = strlen (arg); + return NULL; +} + +const char *set_content_md5 (cmd_parms *cmd, core_dir_config *d, int arg) { + d->content_md5 = arg; + return NULL; +} + +const char *set_daemons_to_start (cmd_parms *cmd, void *dummy, char *arg) { + daemons_to_start = atoi (arg); + return NULL; +} + +const char *set_min_free_servers (cmd_parms *cmd, void *dummy, char *arg) { + daemons_min_free = atoi (arg); + if (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"); + daemons_min_free = 1; + } + + return NULL; +} + +const char *set_max_free_servers (cmd_parms *cmd, void *dummy, char *arg) { + daemons_max_free = atoi (arg); + return NULL; +} + +const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg) { + daemons_limit = atoi (arg); + if (daemons_limit > HARD_SERVER_LIMIT) { + fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit " + "of %d servers,\n", 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/httpd.h.\n"); + daemons_limit = HARD_SERVER_LIMIT; + } else if (daemons_limit < 1) { + fprintf (stderr, "WARNING: Require MaxClients > 0, setting to 1\n"); + daemons_limit = 1; + } + return NULL; +} + +const char *set_max_requests (cmd_parms *cmd, void *dummy, char *arg) { + max_requests_per_child = atoi (arg); + return NULL; +} + +#if defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) +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 conf.h */ + rlim_t cur = 0; + rlim_t max = 0; + + *plimit=(struct rlimit *)pcalloc(cmd->pool,sizeof **plimit); + limit=*plimit; + if ((getrlimit(type, limit)) != 0) + { + *plimit = NULL; + log_unixerr("getrlimit",cmd->cmd->name,"failed",cmd->server); + return; + } + + if ((str = getword_conf(cmd->pool, &arg))) + if (!strcasecmp(str, "max")) + cur = limit->rlim_max; + else + cur = atol(str); + else { + log_printf(cmd->server, "Invalid parameters for %s", cmd->cmd->name); + return; + } + + if (arg2 && (str = 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) + log_printf(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_NPROC) +static const char *no_set_limit (cmd_parms *cmd, core_dir_config *conf, + char *arg, char *arg2) +{ + log_printf(cmd->server, "%s not supported on this platform", + cmd->cmd->name); + return NULL; +} +#endif + +#ifdef RLIMIT_CPU +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) +const char *set_limit_mem (cmd_parms *cmd, core_dir_config *conf, char *arg, char * arg2) +{ +#ifdef RLIMIT_DATA + set_rlimit(cmd,&conf->limit_mem,arg,arg2,RLIMIT_DATA); +#else + set_rlimit(cmd,&conf->limit_mem,arg,arg2,RLIMIT_VMEM); +#endif + return NULL; +} +#endif + +#ifdef RLIMIT_NPROC +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 + +const char *set_bind_address (cmd_parms *cmd, void *dummy, char *arg) { + bind_address.s_addr = get_virthost_addr (arg, NULL); + return NULL; +} + +const char *set_listener(cmd_parms *cmd, void *dummy, char *ips) +{ + listen_rec *new; + char *ports; + unsigned port; + + if (cmd->server->is_virtual) return "Listen not allowed in <VirtualHost>"; + ports=strchr(ips, ':'); + if (ports != NULL) + { + if (ports == ips) return "Missing IP address"; + else if (ports[0] == '\0') + return "Address must end in :<port-number>"; + *(ports++) = '\0'; + } else + ports = ips; + + new=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 = 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 = listeners; + listeners = new; + return NULL; +} + +/* Note --- ErrorDocument will now work from .htaccess files. + * The AllowOverride of Fileinfo allows webmasters to turn it off + */ + +command_rec core_cmds[] = { + +/* Old access config file commands */ + +{ "<Directory", dirsection, NULL, RSRC_CONF, RAW_ARGS, "Container for directives affecting resources located in the specified directories" }, +{ "</Directory>", end_dirsection, NULL, ACCESS_CONF, NO_ARGS, NULL }, +{ "<Location", urlsection, NULL, RSRC_CONF, RAW_ARGS, "Container for directives affecting resources accessed through the specified URL paths" }, +{ "</Location>", end_urlsection, NULL, ACCESS_CONF, NO_ARGS, NULL }, +{ "<VirtualHost", virtualhost_section, NULL, RSRC_CONF, RAW_ARGS, "Container to map directives to a particular virtual host" }, +{ "</VirtualHost>", end_virtualhost_section, NULL, RSRC_CONF, NO_ARGS, NULL }, +{ "<Files", filesection, NULL, OR_ALL, RAW_ARGS, "Container for directives affecting files matching specified patterns" }, +{ "</Files>", end_filesection, NULL, OR_ALL, NO_ARGS, NULL }, +{ "<Limit", limit, NULL, OR_ALL, RAW_ARGS, "Container for authentication directives when accessed using specified HTTP methods" }, +{ "</Limit>", endlimit, NULL, OR_ALL, RAW_ARGS, NULL }, +{ "<IfModule", start_ifmod, NULL, OR_ALL, RAW_ARGS, "Container for directives based on existance of specified modules" }, +{ "</IfModule>", end_ifmod, NULL, OR_ALL, NO_ARGS, NULL }, +{ "AuthType", set_string_slot, (void*)XtOffsetOf(core_dir_config, auth_type), + OR_AUTHCFG, TAKE1, "An HTTP authorization type (e.g., \"Basic\")" }, +{ "AuthName", set_string_slot, (void*)XtOffsetOf(core_dir_config, auth_name), + OR_AUTHCFG, RAW_ARGS, "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')" }, + +/* Old resource config file commands */ + +{ "AccessFileName", set_access_name, NULL, RSRC_CONF, TAKE1, "Name 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", set_string_slot, + (void*)XtOffsetOf (core_dir_config, 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, FLAG, "\"on\" to enable or \"off\" to disable 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" }, +{ "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_string_slot, + (void *)XtOffsetOf (server_rec, names), 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, RSRC_CONF|ACCESS_CONF|OR_AUTHCFG, FLAG, "whether or not to send a Content-MD5 header with each request" }, +{ "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) + 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 }, +{ NULL }, +}; + +/***************************************************************** + * + * Core handlers for various phases of server operation... + */ + +int core_translate (request_rec *r) +{ + void *sconf = r->server->module_config; + core_server_config *conf = get_module_config (sconf, &core_module); + + if (r->proxyreq) return HTTP_FORBIDDEN; + if ((r->uri[0] != '/') && strcmp(r->uri, "*")) { + log_printf(r->server, "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 = pstrcat (r->pool, conf->document_root, + (r->uri + r->server->pathlen), NULL); + else + r->filename = pstrcat (r->pool, conf->document_root, r->uri, NULL); + + return OK; +} + +int do_nothing (request_rec *r) { return OK; } + +/* + * 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. + */ + +int default_handler (request_rec *r) +{ + core_dir_config *d = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + int rangestatus, errstatus; + FILE *f; + + /* 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 = discard_request_body(r)) != OK) + return errstatus; + + r->allowed |= (1 << M_GET) | (1 << M_OPTIONS); + + if (r->method_number == M_INVALID) { + log_printf(r->server, "Invalid method in request %s", r->the_request); + return NOT_IMPLEMENTED; + } + if (r->method_number == M_OPTIONS) return 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)) { + log_reason("File does not exist", + r->path_info ? pstrcat(r->pool, r->filename, r->path_info, NULL) + : r->filename, r); + return NOT_FOUND; + } + if (r->method_number != M_GET) return METHOD_NOT_ALLOWED; + +#ifdef __EMX__ + /* Need binary mode for OS/2 */ + f = pfopen (r->pool, r->filename, "rb"); +#else + f = pfopen (r->pool, r->filename, "r"); +#endif + + if (f == NULL) { + log_reason("file permissions deny server access", r->filename, r); + return FORBIDDEN; + } + + if ((errstatus = set_last_modified (r, r->finfo.st_mtime)) + || (errstatus = set_content_length (r, r->finfo.st_size))) + return errstatus; + + if (d->content_md5 & 1) { + table_set (r->headers_out, "Content-MD5", md5digest(r->pool, f)); + } + + rangestatus = set_byterange(r); + send_http_header (r); + + if (!r->header_only) { + if (!rangestatus) + send_fd (f, r); + else { + long offset, length; + while (each_byterange(r, &offset, &length)) { + fseek(f, offset, SEEK_SET); + send_fd_length(f, r, length); + } + } + } + + pfclose(r->pool, f); + return OK; +} + +handler_rec core_handlers[] = { +{ "*/*", default_handler }, +{ NULL } +}; + +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 */ +}; diff --git a/usr.sbin/httpd/src/http_core.h b/usr.sbin/httpd/src/http_core.h new file mode 100644 index 00000000000..8454b81ad11 --- /dev/null +++ b/usr.sbin/httpd/src/http_core.h @@ -0,0 +1,205 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/***************************************************************** + * + * The most basic server code is encapsulated in a single module + * known as the core, which is just *barely* functional enough to + * serve documents, though not terribly well. + * + * Largely for NCSA back-compatibility reasons, the core needs to + * make pieces of its config structures available to other modules. + * The accessors are declared here, along with the interpretation + * of one of them (allow_options). + */ + +#define OPT_NONE 0 +#define OPT_INDEXES 1 +#define OPT_INCLUDES 2 +#define OPT_SYM_LINKS 4 +#define OPT_EXECCGI 8 +#define OPT_UNSET 16 +#define OPT_INCNOEXEC 32 +#define OPT_SYM_OWNER 64 +#define OPT_MULTI 128 +#define OPT_ALL (OPT_INDEXES|OPT_INCLUDES|OPT_SYM_LINKS|OPT_EXECCGI) + +/* options for get_remote_host() */ +#define REMOTE_HOST (0) +#define REMOTE_NAME (1) +#define REMOTE_NOLOOKUP (2) + +#define SATISFY_ALL 0 +#define SATISFY_ANY 1 +#define SATISFY_NOSPEC 2 + +int allow_options (request_rec *); +int allow_overrides (request_rec *); +char *default_type (request_rec *); +char *document_root (request_rec *); /* Don't use this! If your request went + * through a Userdir, or something like + * that, it'll screw you. But it's + * back-compatible... + */ +extern const char *get_remote_host(conn_rec *conn, void *dir_config, int type); +extern const char *get_remote_logname(request_rec *r); + +/* Authentication stuff. This is one of the places where compatibility + * with the old config files *really* hurts; they don't discriminate at + * all between different authentication schemes, meaning that we need + * to maintain common state for all of them in the core, and make it + * available to the other modules through interfaces. + */ + +typedef struct { + int method_mask; + char *requirement; +} require_line; + +char *auth_type (request_rec *); +char *auth_name (request_rec *); +int satisfies (request_rec *r); +array_header *requires (request_rec *); + +#ifdef CORE_PRIVATE + +/* + * Core is also unlike other modules in being implemented in more than + * one file... so, data structures are declared here, even though most of + * the code that cares really is in http_core.c. Also, anothre accessor. + */ + +char *response_code_string (request_rec *r, int error_index); + +extern module core_module; + +/* Per-directory configuration */ + +typedef char allow_options_t; +typedef char overrides_t; + +typedef struct { + char *d; + /* since is_matchexp(conf->d) was being called so frequently in + * directory_walk() and its relatives, this field was created and + * is set to the result of that call. + */ + int d_is_matchexp; + + allow_options_t opts; + allow_options_t opts_add; + allow_options_t opts_remove; + overrides_t override; + + /* MIME typing --- the core doesn't do anything at all with this, + * but it does know what to slap on a request for a document which + * goes untyped by other mechanisms before it slips out the door... + */ + + char *default_type; + + /* Authentication stuff. Groan... */ + + int satisfy; + char *auth_type; + char *auth_name; + array_header *requires; + + int content_md5; + + /* Custom response config. These can contain text or a URL to redirect to. + * if response_code_strings is NULL then there are none in the config, + * if it's not null then it's allocated to sizeof(char*)*RESPONSE_CODES. + * This lets us do quick merges in merge_core_dir_configs(). + */ + + char **response_code_strings; + + /* Hostname resolution etc */ + int hostname_lookups; + int do_rfc1413; /* See if client is advertising a username? */ + + /* System Resource Control */ +#ifdef RLIMIT_CPU + struct rlimit *limit_cpu; +#endif +#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) + struct rlimit *limit_mem; +#endif +#ifdef RLIMIT_NPROC + struct rlimit *limit_nproc; +#endif + + /* Access control */ + array_header *sec; + regex_t *r; + +} core_dir_config; + +/* Per-server core configuration */ + +typedef struct { + + /* Name translations --- we want the core to be able to do *something* + * so it's at least a minimally functional web server on its own (and + * can be tested that way). But let's keep it to the bare minimum: + */ + char *document_root; + + /* Access control */ + + char *access_name; + array_header *sec; + array_header *sec_url; +} core_server_config; + +#endif diff --git a/usr.sbin/httpd/src/http_log.c b/usr.sbin/httpd/src/http_log.c new file mode 100644 index 00000000000..896c0a8dc59 --- /dev/null +++ b/usr.sbin/httpd/src/http_log.c @@ -0,0 +1,197 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_log.c: Dealing with the logs and errors + * + * Rob McCool + * + */ + + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" + +#include <stdarg.h> + +void error_log_child (void *cmd) +{ + /* 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... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); +#ifdef __EMX__ + /* For OS/2 we need to use a '/' */ + execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + exit (1); +} + +void open_error_log(server_rec *s, pool *p) +{ + char *fname; + + fname = server_root_relative (p, s->error_fname); + + if (*s->error_fname == '|') { + FILE *dummy; + + if (!spawn_child (p, error_log_child, (void *)(s->error_fname+1), + kill_after_timeout, &dummy, NULL)) { + perror ("spawn_child"); + fprintf (stderr, "Couldn't fork child for ErrorLog process\n"); + exit (1); + } + + s->error_log = dummy; + } else { + if(!(s->error_log = pfopen(p, fname, "a"))) { + perror("fopen"); + fprintf(stderr,"httpd: could not open error log file %s.\n", fname); + exit(1); + } + } +} + +void open_logs (server_rec *s_main, pool *p) +{ + server_rec *virt, *q; + + open_error_log (s_main, p); + + 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; + } +} + +void error_log2stderr(server_rec *s) { + if(fileno(s->error_log) != STDERR_FILENO) + dup2(fileno(s->error_log),STDERR_FILENO); +} + +void log_pid(pool *p, char *pid_fname) { + FILE *pid_file; + + if (!pid_fname) return; + pid_fname = server_root_relative (p, pid_fname); + if(!(pid_file = fopen(pid_fname,"w"))) { + perror("fopen"); + fprintf(stderr,"httpd: could not log pid to file %s\n", pid_fname); + exit(1); + } + fprintf(pid_file,"%ld\n",(long)getpid()); + fclose(pid_file); +} + +void log_error(char *err, server_rec *s) { + fprintf(s->error_log, "[%s] %s\n",get_time(),err); + fflush(s->error_log); +} + +void +log_unixerr(const char *routine, const char *file, const char *msg, + server_rec *s) +{ + const char *p, *q; + + p = strerror(errno); + q = get_time(); + + if (file != NULL) + fprintf(s->error_log, "[%s] %s: %s: %s\n", q, routine, file, p); + else + fprintf(s->error_log, "[%s] %s: %s\n", q, routine, p); + if (msg != NULL) fprintf(s->error_log, "[%s] - %s\n", q, msg); + + fflush(s->error_log); +} + +void +log_printf(const server_rec *s, const char *fmt, ...) +{ + va_list args; + + fprintf(s->error_log, "[%s] ", get_time()); + va_start (args, fmt); + vfprintf (s->error_log, fmt, args); + va_end (args); + + fputc('\n', s->error_log); + fflush(s->error_log); +} + +void log_reason(const char *reason, const char *file, request_rec *r) { + fprintf (r->server->error_log, + "[%s] access to %s failed for %s, reason: %s\n", + get_time(), file, + get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME), + reason); + fflush (r->server->error_log); +} + diff --git a/usr.sbin/httpd/src/http_log.h b/usr.sbin/httpd/src/http_log.h new file mode 100644 index 00000000000..e30324db61d --- /dev/null +++ b/usr.sbin/httpd/src/http_log.h @@ -0,0 +1,62 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +void open_logs (server_rec *, pool *p); +void error_log2stderr (server_rec *); + +void log_pid (pool *p, char *pid_fname); +void log_error(char *err, server_rec *s); +extern void log_unixerr(const char *routine, const char *file, + const char *msg, server_rec *s); +void log_printf(const server_rec *s, const char *fmt, ...); +void log_reason(const char *reason, const char *fname, request_rec *r); + diff --git a/usr.sbin/httpd/src/http_main.c b/usr.sbin/httpd/src/http_main.c new file mode 100644 index 00000000000..e553a2d3636 --- /dev/null +++ b/usr.sbin/httpd/src/http_main.c @@ -0,0 +1,2620 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * 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. + */ + +#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 "scoreboard.h" +#include <assert.h> +#include <sys/stat.h> +#ifdef HAVE_SHMGET +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#endif +#ifdef SecureWare +#include <sys/security.h> +#include <sys/audit.h> +#include <prot.h> +#endif +#include <netinet/tcp.h> + +#ifdef HAVE_BSTRING_H +#include <bstring.h> /* for IRIX, FD_SET calls bzero() */ +#endif + +#include "explain.h" + +#if !defined(max) +#define max(a,b) (a > b ? a : b) +#endif + +#ifdef __EMX__ + /* Add MMAP style functionality to OS/2 */ + #ifdef HAVE_MMAP + #define INCL_DOSMEMMGR + #include <os2.h> + #include <umalloc.h> + #include <stdio.h> + caddr_t create_shared_heap (const char *, size_t); + caddr_t get_shared_heap (const char *); + #endif +#endif + + +DEF_Explain + +/* + * 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 standalone; +uid_t user_id; +char *user_name; +gid_t group_id; +#ifdef MULTIPLE_GROUPS +gid_t group_id_list[NGROUPS_MAX]; +#endif +int max_requests_per_child; +char *pid_fname; +char *scoreboard_fname; +char *lock_fname; +char *server_argv0; +struct in_addr bind_address; +listen_rec *listeners; +int daemons_to_start; +int daemons_min_free; +int daemons_max_free; +int daemons_limit; +time_t restart_time; +int suexec_enabled = 0; + +char server_root[MAX_STRING_LEN]; +char server_confname[MAX_STRING_LEN]; + +/* *Non*-shared http_main globals... */ + +server_rec *server_conf; +JMP_BUF jmpbuffer; +int sd; +static fd_set listenfds; +static int listenmaxfd; +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.). + */ + +int one_process = 0; + +/* small utility macros to make things easier to read */ + +#ifdef NO_KILLPG +#define ap_killpg(x, y) (kill (-(x), (y))) +#else +#define ap_killpg(x, y) (killpg ((x), (y))) +#endif + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT) +static void expand_lock_fname(pool *p) +{ + char buf[20]; + + ap_snprintf( buf, sizeof(buf), ".%u", getpid() ); + lock_fname = pstrcat (p, server_root_relative (p, lock_fname), buf, NULL); +} +#endif + +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) +static struct flock lock_it; +static struct flock unlock_it; + +static int lock_fd=-1; + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +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 = popenf(p, lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (lock_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", lock_fname); + exit (1); + } + unlink(lock_fname); +} + +void accept_mutex_on() +{ + int ret; + + while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + log_unixerr("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!", server_conf); + exit(1); + } +} + +void accept_mutex_off() +{ + if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0) + { + log_unixerr("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!", server_conf); + exit(1); + } +} +#elif defined(USE_FLOCK_SERIALIZED_ACCEPT) + +static int lock_fd=-1; + +/* + * Initialize mutex lock. + * Must be safe to call this on a restart. + */ +void +accept_mutex_init(pool *p) +{ + + expand_lock_fname (p); + lock_fd = popenf(p, lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644); + if (lock_fd == -1) + { + perror ("open"); + fprintf (stderr, "Cannot open lock file: %s\n", lock_fname); + exit (1); + } + unlink(lock_fname); +} + +void accept_mutex_on() +{ + int ret; + + while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR) + continue; + + if (ret < 0) { + log_unixerr("flock", "LOCK_EX", "Error getting accept lock. Exiting!", + server_conf); + exit(1); + } +} + +void accept_mutex_off() +{ + if (flock (lock_fd, LOCK_UN) < 0) + { + log_unixerr("flock", "LOCK_UN", "Error freeing accept lock. Exiting!", + server_conf); + exit(1); + } +} +#else +/* Default --- no serialization. Other methods *could* go here, + * as #elifs... + */ +#define accept_mutex_init(x) +#define accept_mutex_on() +#define accept_mutex_off() +#endif + +void usage(char *bin) +{ + fprintf(stderr,"Usage: %s [-d directory] [-f file] [-v] [-h] [-l]\n",bin); + fprintf(stderr,"-d directory : specify an alternate initial ServerRoot\n"); + fprintf(stderr,"-f file : specify an alternate ServerConfigFile\n"); + fprintf(stderr,"-v : show version number\n"); + fprintf(stderr,"-h : list directives\n"); + fprintf(stderr,"-l : list modules\n"); + 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 conn_rec *current_conn; +static request_rec *timeout_req; +static char *timeout_name = NULL; +static int alarms_blocked = 0; +static int alarm_pending = 0; + +#ifndef NO_USE_SIGACTION +/* + * Replace standard signal() with the more reliable sigaction equivalent + * from W. Richard Stevens' "Advanced Programming in the UNIX Environment" + * (the version that does not automatically restart system calls). + */ +Sigfunc *signal(int signo, Sigfunc *func) +{ + struct sigaction act, oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +#ifdef SA_INTERRUPT /* SunOS */ + act.sa_flags |= SA_INTERRUPT; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +} +#endif + +void timeout(int sig) /* Also called on SIGPIPE */ +{ + char errstr[MAX_STRING_LEN]; + void *dirconf; + + signal(SIGPIPE, SIG_IGN); /* Block SIGPIPE */ + if (alarms_blocked) { + alarm_pending = 1; + return; + } + + 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 (sig == SIGPIPE) { + ap_snprintf(errstr, sizeof(errstr), "%s lost connection to client %s", + timeout_name ? timeout_name : "request", + get_remote_host(current_conn, dirconf, REMOTE_NAME)); + } else { + ap_snprintf(errstr, sizeof(errstr), "%s timed out for %s", + timeout_name ? timeout_name : "request", + get_remote_host(current_conn, dirconf, REMOTE_NAME)); + } + + if (!current_conn->keptalive) + log_error(errstr, current_conn->server); + + if (timeout_req) { + /* Someone has asked for this transaction to just be aborted + * if it times out... + */ + + request_rec *log_req = timeout_req; + + 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) + log_transaction(log_req); + + bsetflag(timeout_req->connection->client, B_EOUT, 1); + bclose(timeout_req->connection->client); + + if (!standalone) exit(0); + + ap_longjmp (jmpbuffer, 1); + } + else { /* abort the connection */ + bsetflag(current_conn->client, B_EOUT, 1); + current_conn->aborted = 1; + } +} + +/* + * 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. + */ + +void block_alarms() { + ++alarms_blocked; +} + +void unblock_alarms() { + --alarms_blocked; + if (alarms_blocked == 0 && alarm_pending) { + alarm_pending = 0; + timeout(0); + } +} + +void keepalive_timeout (char *name, request_rec *r) +{ + timeout_req = r; + timeout_name = name; + + signal(SIGALRM, timeout); + if (r->connection->keptalive) + alarm (r->server->keep_alive_timeout); + else + alarm (r->server->timeout); +} + +void hard_timeout (char *name, request_rec *r) +{ + timeout_req = r; + timeout_name = name; + + signal(SIGALRM, timeout); + alarm (r->server->timeout); +} + +void soft_timeout (char *name, request_rec *r) +{ + timeout_name = name; + + signal(SIGALRM, timeout); + alarm (r->server->timeout); +} + +void kill_timeout (request_rec *dummy) { + alarm (0); + timeout_req = NULL; + timeout_name = NULL; +} + +/* reset_timeout (request_rec *) resets the timeout in effect, + * as long as it hasn't expired already. + */ + +void reset_timeout (request_rec *r) { + int i; + + if (timeout_name) { /* timeout has been set */ + i = alarm(r->server->timeout); + if (i == 0) /* timeout already expired, so set it back to 0 */ + alarm(0); + } +} + +/* + * 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 conf.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) { + log_unixerr("setsockopt", "(SO_LINGER)", NULL, server_conf); + /* 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(sig) +int sig; +{ + if (alarms_blocked) { + alarm_pending = 1; + return; + } + + if (!current_conn) { + ap_longjmp (jmpbuffer, 1); + } + bsetflag(current_conn->client, B_EOUT, 1); + current_conn->aborted = 1; +} + +static void linger_timeout () +{ + timeout_name = "lingering close"; + + signal(SIGALRM, lingerout); + alarm(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) +{ + int dummybuf[512]; + struct timeval tv; + fd_set lfds, fds_read, fds_err; + int select_rv = 0, read_rv = 0; + 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 (bflush(r->connection->client) == -1) { + kill_timeout(r); + bclose(r->connection->client); + return; + } + 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) { + kill_timeout(r); + bclose(r->connection->client); + return; + } + + /* Set up to wait for readable data on socket... */ + + FD_ZERO(&lfds); + FD_SET(lsd, &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 exception on lsd, 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. + */ + tv.tv_sec = 2; + tv.tv_usec = 0; + read_rv = 0; + fds_read = lfds; + fds_err = lfds; + +#ifdef SELECT_NEEDS_CAST + select_rv = select(lsd+1, (int*)&fds_read, NULL, (int*)&fds_err, &tv); +#else + select_rv = select(lsd+1, &fds_read, NULL, &fds_err, &tv); +#endif + } while ((select_rv > 0) && /* Something to see on socket */ + !FD_ISSET(lsd, &fds_err) && /* that isn't an error condition */ + FD_ISSET(lsd, &fds_read) && /* and is worth trying to read */ + ((read_rv = read(lsd, dummybuf, sizeof dummybuf)) > 0)); + + /* Should now have seen final ack. Safe to finally kill socket */ + + bclose(r->connection->client); + + kill_timeout(r); +} +#endif /* ndef NO_LINGCLOSE */ + +/***************************************************************** + * + * 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... + */ + +#if defined(HAVE_MMAP) +static scoreboard *scoreboard_image=NULL; + +static void setup_shared_mem(void) +{ + caddr_t m; + +#ifdef __EMX__ + char errstr[MAX_STRING_LEN]; + int rc; + + m = (caddr_t)create_shared_heap("\\SHAREMEM\\SCOREBOARD", HARD_SERVER_LIMIT*sizeof(short_score)); + if(m == 0) { + fprintf(stderr, "httpd: Could not create OS/2 Shared memory pool.\n"); + exit(1); + } + + rc = _uopen((Heap_t)m); + if(rc != 0) { + fprintf(stderr, "httpd: Could not uopen() newly created OS/2 Shared memory pool.\n"); + } + +#elif defined(MAP_ANON) || defined(MAP_FILE) +/* 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); + } +#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, "httpd: Could not mmap memory\n"); + exit(1); + } +#else +/* Sun style */ + int fd; + + fd = open("/dev/zero", O_RDWR); + if (fd == -1) + { + perror("open"); + fprintf(stderr, "httpd: Could not open /dev/zero\n"); + exit(1); + } + m = mmap((caddr_t)0, SCOREBOARD_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (m == (caddr_t)-1) + { + perror("mmap"); + fprintf(stderr, "httpd: Could not mmap /dev/zero\n"); + exit(1); + } + close(fd); +#endif + scoreboard_image = (scoreboard *)m; + scoreboard_image->global.exit_generation=0; +} + +#elif defined(HAVE_SHMGET) +static scoreboard *scoreboard_image=NULL; +static key_t shmkey = IPC_PRIVATE; +static int shmid = -1; + +static void setup_shared_mem(void) +{ + char errstr[MAX_STRING_LEN]; + 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) { + fprintf(stderr, + "httpd: Your kernel was built without CONFIG_SYSVIPC\n" + "httpd: please consult the Apache FAQ for details\n"); + } +#endif + perror("shmget"); + fprintf(stderr, "httpd: Could not call shmget\n"); + exit(1); + } + + ap_snprintf(errstr, sizeof(errstr), "created shared memory segment #%d", shmid); + log_error(errstr, server_conf); + +#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"); + fprintf(stderr, "httpd: Could not move break\n"); + } +#endif + +#define BADSHMAT ((scoreboard *)(-1)) + if ((scoreboard_image = (scoreboard *)shmat(shmid, 0, 0)) == BADSHMAT) + { + perror("shmat"); + fprintf(stderr, "httpd: Could not call shmat\n"); + /* + * 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) { + perror("shmctl"); + fprintf(stderr, "httpd: Could not stat segment #%d\n", shmid); + } + else + { + shmbuf.shm_perm.uid = user_id; + shmbuf.shm_perm.gid = group_id; + if (shmctl(shmid, IPC_SET, &shmbuf) != 0) { + perror("shmctl"); + fprintf(stderr, "httpd: Could not set segment #%d\n", shmid); + } + } + } + /* + * We must avoid leaving segments in the kernel's + * (small) tables. + */ + if (shmctl(shmid, IPC_RMID, NULL) != 0) { + perror("shmctl"); + fprintf(stderr, "httpd: Could not delete segment #%d\n", shmid); + ap_snprintf(errstr, sizeof(errstr), "could not remove shared memory segment #%d", shmid); + log_unixerr("shmctl","IPC_RMID",errstr, server_conf); + } + if (scoreboard_image == BADSHMAT) /* now bailout */ + exit(1); + +#ifdef MOVEBREAK + if (obrk == (char *)-1) + return; /* nothing else to do */ + if (sbrk(-(MOVEBREAK)) == (char *)-1) + { + perror("sbrk"); + fprintf(stderr, "httpd: Could not move break back\n"); + } +#endif + scoreboard_image->global.exit_generation=0; +} + +#else +#define SCOREBOARD_FILE +static scoreboard _scoreboard_image; +static scoreboard *scoreboard_image=&_scoreboard_image; +static int scoreboard_fd; + +/* 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, char *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = write (fd, buffer, bufsz); + if (rv > 0) { + 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, char *buffer, int bufsz) +{ + int rv, orig_sz = bufsz; + + do { + rv = read (fd, buffer, bufsz); + if (rv > 0) { + buffer += rv; + bufsz -= rv; + } + } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR)); + + return rv < 0? rv : orig_sz - bufsz; +} +#endif + +/* Called by parent process */ +void reinit_scoreboard (pool *p) +{ + int exit_gen=0; + if(scoreboard_image) + exit_gen=scoreboard_image->global.exit_generation; + +#ifndef SCOREBOARD_FILE + if (scoreboard_image == NULL) + { + setup_shared_mem(); + } + memset(scoreboard_image, 0, SCOREBOARD_SIZE); + scoreboard_image->global.exit_generation=exit_gen; +#else + scoreboard_fname = server_root_relative (p, scoreboard_fname); + +#ifdef __EMX__ + /* OS/2 needs binary mode set. */ + scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_BINARY|O_RDWR, 0644); +#else + scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_RDWR, 0644); +#endif + if (scoreboard_fd == -1) + { + perror (scoreboard_fname); + fprintf (stderr, "Cannot open scoreboard file:\n"); + exit (1); + } + + memset ((char*)scoreboard_image, 0, sizeof(*scoreboard_image)); + scoreboard_image->global.exit_generation=exit_gen; + force_write (scoreboard_fd, (char*)scoreboard_image, + sizeof(*scoreboard_image)); +#endif +} + +/* called by child */ +void reopen_scoreboard (pool *p) +{ +#ifdef SCOREBOARD_FILE + if (scoreboard_fd != -1) pclosef (p, scoreboard_fd); + +#ifdef __EMX__ + /* OS/2 needs binary mode set. */ + scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_BINARY|O_RDWR, 0666); +#else + scoreboard_fd = popenf(p, scoreboard_fname, O_CREAT|O_RDWR, 0666); +#endif + if (scoreboard_fd == -1) + { + perror (scoreboard_fname); + fprintf (stderr, "Cannot open scoreboard file:\n"); + exit (1); + } +#else +#ifdef __EMX__ +#ifdef HAVE_MMAP + caddr_t m; + int rc; + + m = (caddr_t)get_shared_heap("\\SHAREMEM\\SCOREBOARD"); + if(m == 0) { + fprintf(stderr, "httpd: Could not find existing OS/2 Shared memory pool.\n"); + exit(1); + } + + rc = _uopen((Heap_t)m); + scoreboard_image = (scoreboard *)m; +#endif +#endif +#endif +} + +void cleanup_scoreboard () +{ +#ifdef SCOREBOARD_FILE + unlink (scoreboard_fname); +#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. + */ + +void sync_scoreboard_image () +{ +#ifdef SCOREBOARD_FILE + lseek (scoreboard_fd, 0L, 0); + force_read (scoreboard_fd, (char*)scoreboard_image, + sizeof(*scoreboard_image)); +#endif +} + +int exists_scoreboard_image () +{ + return (scoreboard_image ? 1 : 0); +} + +int update_child_status (int child_num, int status, request_rec *r) +{ + int old_status; + short_score new_score_rec; + + if (child_num < 0) + return -1; + + sync_scoreboard_image(); + new_score_rec = scoreboard_image->servers[child_num]; + new_score_rec.pid = getpid(); + old_status = new_score_rec.status; + new_score_rec.status = status; + +#if defined(STATUS) + new_score_rec.last_used=time(NULL); + if (status == SERVER_READY || status == SERVER_DEAD) { + /* + * Reset individual counters + */ + if (status == SERVER_DEAD) { + new_score_rec.my_access_count = 0L; + new_score_rec.my_bytes_served = 0L; + } + new_score_rec.conn_count = (unsigned short)0; + new_score_rec.conn_bytes = (unsigned long)0; + } + if (r) { + int slot_size; + conn_rec *c = r->connection; + slot_size = sizeof(new_score_rec.client) - 1; + strncpy(new_score_rec.client, get_remote_host(c, r->per_dir_config, + REMOTE_NOLOOKUP), slot_size); + new_score_rec.client[slot_size] = '\0'; + slot_size = sizeof(new_score_rec.request) - 1; + strncpy(new_score_rec.request, (r->the_request ? r->the_request : + "NULL"), slot_size); + new_score_rec.request[slot_size] = '\0'; + slot_size = sizeof(new_score_rec.vhost) - 1; + strncpy(new_score_rec.vhost,r->server->server_hostname, slot_size); + new_score_rec.vhost[slot_size] = '\0'; + } +#endif + +#ifndef SCOREBOARD_FILE + memcpy(&scoreboard_image->servers[child_num], &new_score_rec, sizeof new_score_rec); +#else + lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0); + force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score)); +#endif + + return old_status; +} + +void update_scoreboard_global() + { +#ifdef SCOREBOARD_FILE + lseek(scoreboard_fd, + (char *)&scoreboard_image->global-(char *)scoreboard_image,0); + force_write(scoreboard_fd,(char *)&scoreboard_image->global, + sizeof scoreboard_image->global); +#endif + } + +int get_child_status (int child_num) +{ + if (child_num<0 || child_num>=HARD_SERVER_LIMIT) + return -1; + else + return scoreboard_image->servers[child_num].status; +} + +int count_busy_servers () +{ + int i; + int res = 0; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image->servers[i].status == SERVER_BUSY_READ || + scoreboard_image->servers[i].status == SERVER_BUSY_WRITE || + scoreboard_image->servers[i].status == SERVER_BUSY_KEEPALIVE || + scoreboard_image->servers[i].status == SERVER_BUSY_LOG || + scoreboard_image->servers[i].status == SERVER_BUSY_DNS) + ++res; + return res; +} + +int count_live_servers() + { + int i; + int res = 0; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image->servers[i].status != SERVER_DEAD) + ++res; + return res; + } + +short_score get_scoreboard_info(int i) +{ + return (scoreboard_image->servers[i]); +} + +#if defined(STATUS) +static void increment_counts (int child_num, request_rec *r) +{ + long int bs=0; + short_score new_score_rec; + + sync_scoreboard_image(); + new_score_rec = scoreboard_image->servers[child_num]; + if (r->sent_bodyct) + bgetopt(r->connection->client, BO_BYTECT, &bs); + + new_score_rec.access_count ++; + new_score_rec.my_access_count ++; + new_score_rec.conn_count ++; + new_score_rec.bytes_served += (unsigned long)bs; + new_score_rec.my_bytes_served += (unsigned long)bs; + new_score_rec.conn_bytes += (unsigned long)bs; + + times(&new_score_rec.times); + + +#ifndef SCOREBOARD_FILE + memcpy(&scoreboard_image->servers[child_num], &new_score_rec, sizeof(short_score)); +#else + lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0); + force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score)); +#endif +} +#endif + +int count_idle_servers () +{ + int i; + int res = 0; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image->servers[i].status == SERVER_READY) + ++res; + + return res; +} + +int find_free_child_num () +{ + int i; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image->servers[i].status == SERVER_DEAD) + return i; + + return -1; +} + +int find_child_by_pid (int pid) +{ + int i; + + for (i = 0; i < HARD_SERVER_LIMIT; ++i) + if (scoreboard_image->servers[i].pid == pid) + return i; + + return -1; +} + +void reclaim_child_processes () +{ + int i, status; + int my_pid = getpid(); + + sync_scoreboard_image(); + for (i = 0; i < HARD_SERVER_LIMIT; ++i) { + int pid = scoreboard_image->servers[i].pid; + + if (pid != my_pid && pid != 0) { + int waitret = 0, + tries = 1; + + while (waitret == 0 && tries <= 4) { + long int waittime = 4096; /* in usecs */ + struct timeval tv; + + /* don't want to hold up progress any more than + * necessary, so keep checking to see if the child + * has exited with an exponential backoff. + * Currently set for a maximum wait of a bit over + * four seconds. + */ + while (((waitret = waitpid(pid, &status, WNOHANG)) == 0) && + waittime < 3000000) { + tv.tv_sec = waittime / 1000000; + tv.tv_usec = waittime % 1000000; + waittime = waittime * 2; + select(0, NULL, NULL, NULL, &tv); + } + if (waitret == 0) { + switch (tries) { + case 1: + /* perhaps it missed the SIGHUP, lets try again */ + log_printf(server_conf, "child process %d did not exit, sending another SIGHUP", pid); + kill(pid, SIGHUP); + break; + case 2: + /* ok, now it's being annoying */ + log_printf(server_conf, "child process %d still did not exit, sending a SIGTERM", pid); + kill(pid, SIGTERM); + break; + case 3: + /* die child scum */ + log_printf(server_conf, "child process %d still did not exit, sending a SIGKILL", pid); + kill(pid, SIGKILL); + break; + case 4: + /* 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. + */ + log_printf(server_conf, "could not make child process %d exit, attempting to continue anyway", pid); + break; + } + } + tries++; + } + } + } +} + +#if defined(BROKEN_WAIT) || defined(NEED_WAITPID) +/* +Some systems appear to fail to deliver dead children to wait() at times. +This sorts them out. In fact, this may have been caused by a race condition +in wait_or_timeout(). But this routine is still useful for systems with no +waitpid(). +*/ +int reap_children () +{ + int status, n; + int ret = 0; + + for (n = 0; n < HARD_SERVER_LIMIT; ++n) { + if (scoreboard_image->servers[n].status != SERVER_DEAD + && waitpid (scoreboard_image->servers[n].pid, &status, WNOHANG) + == -1 + && errno == ECHILD) { + sync_scoreboard_image (); + update_child_status (n, SERVER_DEAD, NULL); + ret = 1; + } + } + return ret; +} +#endif + +/* Finally, this routine is used by the caretaker process to wait for + * a while... + */ + +static int wait_or_timeout () +{ +#ifndef NEED_WAITPID + int ret; + + ret = waitpid (-1, NULL, WNOHANG); + if (ret == -1 && errno == EINTR) { + return -1; + } + if (ret <= 0) { + sleep (1); + return -1; + } + return ret; +#else + if (!reap_children ()) { + sleep(1); + } + return -1; +#endif +} + + +void sig_term() { + log_error("httpd: caught SIGTERM, shutting down", server_conf); + cleanup_scoreboard(); + ap_killpg (pgrp, SIGKILL); + close(sd); + exit(1); +} + +void bus_error(void) { + char emsg[256]; + + ap_snprintf + ( + emsg, + sizeof(emsg) - 1, + "httpd: caught SIGBUS, attempting to dump core in %s", + server_root + ); + log_error(emsg, server_conf); + chdir(server_root); + abort(); + exit(1); +} + +void seg_fault() { + char emsg[256]; + + ap_snprintf + ( + emsg, + sizeof(emsg) - 1, + "httpd: caught SIGSEGV, attempting to dump core in %s", + server_root + ); + log_error(emsg, server_conf); + chdir(server_root); + abort(); + exit(1); +} + +void just_die() /* SIGHUP to child process??? */ +{ + exit (0); +} + +static int deferred_die; + +static void deferred_die_handler () +{ + deferred_die = 1; +} + +/* volatile just in case */ +static volatile int restart_pending; +static volatile int is_graceful; +static volatile int generation; + +static void restart (int sig) +{ + is_graceful = (sig == SIGUSR1); + restart_pending = 1; +} + + +void set_signals() +{ +#ifndef NO_USE_SIGACTION + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (!one_process) { + sa.sa_handler = (void (*)())seg_fault; + if (sigaction (SIGSEGV, &sa, NULL) < 0) + log_unixerr ("sigaction(SIGSEGV)", NULL, NULL, server_conf); + sa.sa_handler = (void (*)())bus_error; + if (sigaction (SIGBUS, &sa, NULL) < 0) + log_unixerr ("sigaction(SIGBUS)", NULL, NULL, server_conf); + } + sa.sa_handler = (void (*)())sig_term; + if (sigaction (SIGTERM, &sa, NULL) < 0) + log_unixerr ("sigaction(SIGTERM)", NULL, NULL, server_conf); + + /* wait_or_timeout uses sleep() which could deliver a SIGALRM just as we're + * trying to process the restart requests. That's not good. restart + * cleans out the SIGALRM handler, but this totally avoids the race + * condition between when the restart request is made and when the handler + * is invoked. + * + * We also don't want to ignore HUPs and USR1 while we're busy processing + * one. + */ + sigaddset (&sa.sa_mask, SIGALRM); + sigaddset (&sa.sa_mask, SIGHUP); + sigaddset (&sa.sa_mask, SIGUSR1); + sa.sa_handler = (void (*)())restart; + if (sigaction (SIGHUP, &sa, NULL) < 0) + log_unixerr ("sigaction(SIGHUP)", NULL, NULL, server_conf); + if (sigaction (SIGUSR1, &sa, NULL) < 0) + log_unixerr ("sigaction(SIGUSR1)", NULL, NULL, server_conf); +#else + if(!one_process) { + signal (SIGSEGV, (void (*)())seg_fault); + signal (SIGBUS, (void (*)())bus_error); + } + + signal (SIGTERM, (void (*)())sig_term); + signal (SIGHUP, (void (*)())restart); + signal (SIGUSR1, (void (*)())restart); +#endif +} + + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +void detach() +{ + int x; + + chdir("/"); +#ifndef MPE +/* 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,"httpd: unable to fork new process\n"); + exit(1); + } +#endif +#ifndef NO_SETSID + if((pgrp=setsid()) == -1) { + perror("setsid"); + fprintf(stderr,"httpd: setsid failed\n"); + exit(1); + } +#elif defined(NEXT) + if(setpgrp(0,getpid()) == -1 || (pgrp = getpgrp(0)) == -1) { + perror("setpgrp"); + fprintf(stderr,"httpd: setpgrp or getpgrp failed\n"); + exit(1); + } +#elif defined(__EMX__) + /* OS/2 doesn'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,"httpd: setpgrp failed\n"); + exit(1); + } +#endif +} + +/* Reset group privileges, after rereading the config files + * (our uid may have changed, and if so, we want the new perms). + * + * Don't reset the uid yet --- we do that only in the child process, + * so as not to lose any root privs. But we can set the group stuff + * now, once, as opposed to once per each new child. + * + * 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() +{ + if(!geteuid()) { + char *name; + + /* Get username if passed as a uid */ + + if (user_name[0] == '#') { + struct passwd* ent; + uid_t uid=atoi(&user_name[1]); + + if ((ent = getpwuid(uid)) == NULL) { + log_unixerr("getpwuid",NULL,"couldn't determine user name from uid", server_conf); + exit(1); + } + + name = ent->pw_name; + } else name = user_name; + +#ifndef __EMX__ + /* OS/2 dosen't support groups. */ + + /* Reset `groups' attributes. */ + + if (initgroups(name, group_id) == -1) { + log_unixerr("initgroups", NULL, "unable to set groups", server_conf); + exit (1); + } +#ifdef MULTIPLE_GROUPS + if (getgroups(NGROUPS_MAX, group_id_list) == -1) { + log_unixerr("getgroups", NULL, "unable to get group list", server_conf); + exit (1); + } +#endif + if (setgid(group_id) == -1) { + log_unixerr("setgid", NULL, "unable to set group id", server_conf); + exit (1); + } +#endif + } +} + +/* check to see if we have the 'suexec' setuid wrapper installed */ +int init_suexec () +{ + struct stat wrapper; + + if ((stat(SUEXEC_BIN, &wrapper)) != 0) + return (suexec_enabled); + + if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) { + suexec_enabled = 1; + fprintf(stderr, "Configuring Apache for use with suexec wrapper.\n"); + } + + return (suexec_enabled); +} + +/***************************************************************** + * Connection structures and accounting... + * Should these be global? Only to this file, at least... + */ + +pool *pconf; /* Pool for config stuff */ +pool *ptrans; /* Pool for per-transaction stuff */ + +static server_rec *find_virtual_server (struct in_addr server_ip, + unsigned port, server_rec *server) +{ + server_rec *virt; + server_addr_rec *sar; + server_rec *def; + + def = server; + for (virt = server->next; virt; virt = virt->next) { + for (sar = virt->addrs; sar; sar = sar->next) { + if ((virt->is_virtual == 1) && /* VirtualHost */ + (sar->host_addr.s_addr == htonl(INADDR_ANY) || + sar->host_addr.s_addr == server_ip.s_addr) && + (sar->host_port == 0 || sar->host_port == port)) { + return virt; + } else if ( sar->host_addr.s_addr == DEFAULT_VHOST_ADDR + && (sar->host_port == 0 || sar->host_port == port)) { + /* this is so that you can build a server that is the + "default" for any interface which isn't explicitly + specified. So that you can implement "deny anything + which isn't expressly permitted" -djg */ + def = virt; + } + } + } + + return def; +} + +void default_server_hostnames(server_rec *s) +{ + struct hostent *h; + struct in_addr *main_addr; + int num_addr; + char *def_hostname; + int n; + server_addr_rec *sar; + int has_default_vhost_addr; + unsigned mainport = s->port; + int from_local=0; + + /* Main host first */ + + if (!s->server_hostname) { + s->server_hostname = get_local_host(pconf); + from_local = 1; + } + + def_hostname = s->server_hostname; + h = gethostbyname(def_hostname); + if( h == NULL ) { + fprintf(stderr,"httpd: cannot determine the IP address of "); + if (from_local) { + fprintf(stderr,"the local host (%s). Use ServerName to set it manually.\n", + s->server_hostname ? s->server_hostname : "<NULL>"); + } else { + fprintf(stderr,"the specified ServerName (%s).\n", + s->server_hostname ? s->server_hostname : "<NULL>"); + }; + exit(1); + } + /* we need to use gethostbyaddr below... and since it shares a static + area with gethostbyname it'd clobber the value we just got. So + we need to make a copy. -djg */ + for (num_addr = 0; h->h_addr_list[num_addr] != NULL; num_addr++) { + /* nop */ + } + main_addr = palloc( pconf, sizeof( *main_addr ) * num_addr ); + for (n = 0; n < num_addr; n++) { + main_addr[n] = *(struct in_addr *)h->h_addr_list[n]; + } + + /* Then virtual hosts */ + + for (s = s->next; s; s = s->next) { + /* Check to see if we might be a HTTP/1.1 virtual host - same IP */ + has_default_vhost_addr = 0; + for (n = 0; n < num_addr; n++) { + for(sar = s->addrs; sar; sar = sar->next) { + if (sar->host_addr.s_addr == main_addr[n].s_addr && + s->port == mainport) + s->is_virtual = 2; + if( sar->host_addr.s_addr == DEFAULT_VHOST_ADDR ) { + has_default_vhost_addr = 1; + } + } + } + + /* FIXME: some of this decision doesn't make a lot of sense in + the presence of multiple addresses on the <VirtualHost> + directive. It should issue warnings here perhaps. -djg */ + if (!s->server_hostname) { + if (s->is_virtual == 2) { + if (s->addrs) { + s->server_hostname = s->addrs->virthost; + } else { + /* 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 = "bogus_host_without_forward_dns"; + } + } else if (has_default_vhost_addr) { + s->server_hostname = def_hostname; + } else { + if (s->addrs + && (h = gethostbyaddr ((char *)&(s->addrs->host_addr), + sizeof (struct in_addr), AF_INET))) { + s->server_hostname = pstrdup (pconf, (char *)h->h_name); + } else { + /* again, what can we do? They didn't specify a + ServerName, and their DNS isn't working. -djg */ + if (s->addrs) { + fprintf(stderr, "Failed to resolve server name " + "for %s (check DNS)\n", + inet_ntoa(s->addrs->host_addr)); + } + s->server_hostname = "bogus_host_without_reverse_dns"; + } + } + } + } +} + +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 *)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->server = find_virtual_server(saddr->sin_addr, ntohs(saddr->sin_port), + server); + conn->base_server = conn->server; + conn->client = inout; + + conn->remote_addr = *remaddr; + conn->remote_ip = pstrdup (conn->pool, + inet_ntoa(conn->remote_addr.sin_addr)); + + return conn; +} + +#if defined(TCP_NODELAY) && !defined(MPE) +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) { + log_unixerr("setsockopt", "(TCP_NODELAY)", NULL, server_conf); + } +} +#else +#define sock_disable_nagle(s) /* NOOP */ +#endif + +/***************************************************************** + * 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 int child_num; +static fd_set main_fds; + +void child_main(int child_num_arg) +{ +#if defined(UW) + size_t clen; +#else + int clen; +#endif + struct sockaddr sa_server; + struct sockaddr sa_client; + + csd = -1; + dupped_csd = -1; + child_num = child_num_arg; + requests_this_child = 0; + + reopen_scoreboard(pconf); + (void)update_child_status(child_num, SERVER_READY, (request_rec*)NULL); + +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && user_id > 1) { + GETPRIVMODE(); + if (setuid(user_id) == -1) { + GETUSERMODE(); +#else + /* Only try to switch if we're running as root */ + if (!geteuid() && setuid(user_id) == -1) { +#endif + log_unixerr("setuid", NULL, "unable to change uid", server_conf); + exit (1); + } +#ifdef MPE + GETUSERMODE(); + } +#endif + + /* + * Setup the jump buffers so that we can return here after + * a signal or a timeout (yeah, I know, same thing). + */ + ap_setjmp (jmpbuffer); +#ifndef __EMX__ +#ifdef SIGURG + signal(SIGURG, timeout); +#endif +#endif + + while (1) { + int errsave; + 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. */ + signal (SIGUSR1, (void (*)())just_die); + + /* + * (Re)initialize this child to a pre-connection state. + */ + + alarm(0); /* Cancel any outstanding alarms. */ + timeout_req = NULL; /* No request in progress */ + current_conn = NULL; + signal(SIGPIPE, timeout); + + clear_pool (ptrans); + + sync_scoreboard_image(); + if (scoreboard_image->global.exit_generation >= generation) + exit(0); + + if ((count_idle_servers() >= daemons_max_free) + || (max_requests_per_child > 0 + && ++requests_this_child >= max_requests_per_child)) + { + exit(0); + } + + (void)update_child_status(child_num, SERVER_READY, (request_rec*)NULL); + + if (listeners == NULL) { + FD_ZERO(&listenfds); + FD_SET(sd, &listenfds); + listenmaxfd = sd; + } + + /* + * Wait for an acceptable connection to arrive. + */ + + accept_mutex_on(); /* Lock around "accept", if necessary */ + + for (;;) { + memcpy(&main_fds, &listenfds, sizeof(fd_set)); +#ifdef SELECT_NEEDS_CAST + srv = select(listenmaxfd+1, (int*)&main_fds, NULL, NULL, NULL); +#else + srv = select(listenmaxfd+1, &main_fds, NULL, NULL, NULL); +#endif + errsave = errno; + + sync_scoreboard_image(); + if (scoreboard_image->global.exit_generation >= generation) + exit(0); + + errno = errsave; + 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. + */ + log_unixerr("select", "(listen)", NULL, server_conf); + exit(1); + } + + if (srv <= 0) + continue; + + if (listeners != NULL) { + for (sd = listenmaxfd; sd >= 0; sd--) + if (FD_ISSET(sd, &main_fds)) break; + if (sd < 0) + continue; + } + + /* if we accept() something we don't want to die, so we have to + * defer the exit + */ + deferred_die = 0; + signal (SIGUSR1, (void (*)())deferred_die_handler); + for (;;) { + clen = sizeof(sa_client); + csd = 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 */ + 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; + + default: + log_unixerr("accept", "(client socket)", NULL, server_conf); + exit(1); + } + } + + /* go around again, safe to die */ + signal (SIGUSR1, (void (*)())just_die); + if (deferred_die) { + /* ok maybe not, see ya later */ + exit (0); + } + } + + accept_mutex_off(); /* unlock after "accept" */ + + /* 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); + + note_cleanups_for_fd(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) { + log_unixerr("getsockname", NULL, NULL, server_conf); + continue; + } + + sock_disable_nagle(csd); + + (void)update_child_status(child_num, SERVER_BUSY_READ, + (request_rec*)NULL); + + conn_io = bcreate(ptrans, B_RDWR); + dupped_csd = csd; +#if defined(NEED_DUPPED_CSD) + if ((dupped_csd = dup(csd)) < 0) { + log_unixerr("dup", NULL, "couldn't duplicate csd", server_conf); + dupped_csd = csd; /* Oh well... */ + } + note_cleanups_for_fd(ptrans,dupped_csd); +#endif + 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 = read_request(current_conn)) != NULL) { + + /* ok we've read the request... it's a little too late + * to do a graceful restart, so ignore them for now. + */ + signal (SIGUSR1, SIG_IGN); + + (void)update_child_status(child_num, SERVER_BUSY_WRITE, r); + + process_request(r); +#if defined(STATUS) + increment_counts(child_num, r); +#endif + if (!current_conn->keepalive || current_conn->aborted) + break; + + destroy_pool(r->pool); + (void)update_child_status(child_num, SERVER_BUSY_KEEPALIVE, + (request_rec*)NULL); + + sync_scoreboard_image(); + if (scoreboard_image->global.exit_generation >= generation) { + bclose(conn_io); + 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. + */ + signal (SIGUSR1, (void (*)())just_die); + } + + /* + * 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 + 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 { + bsetflag(conn_io, B_EOUT, 1); + bclose(conn_io); + } +#endif + } +} + +int make_child(server_rec *server_conf, int child_num) +{ + int pid; + + if (one_process) { + signal (SIGHUP, (void (*)())just_die); + signal (SIGTERM, (void (*)())just_die); + child_main (child_num); + } + + Explain1 ("Starting new child in slot %d", child_num); + (void)update_child_status (child_num, SERVER_STARTING, (request_rec *)NULL); + + if ((pid = fork()) == -1) { + log_unixerr("fork", NULL, "Unable to fork new process", server_conf); + + /* fork didn't succeed. Fix the scoreboard or else + * it will say SERVER_STARTING forever and ever + */ + (void)update_child_status (child_num, 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) { + /* 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, (void (*)())just_die); + signal (SIGUSR1, (void (*)())just_die); + signal (SIGTERM, (void (*)())just_die); + child_main (child_num); + } + + /* If the parent proceeds with a restart before the child has written + * their pid into the scoreboard we'll end up "forgetting" about the + * child. So we write the child pid into the scoreboard now. (This + * is safe, because the child is going to be writing the same value + * to the same word.) + * XXX: this needs to be sync'd to disk in the non shared memory stuff + */ + scoreboard_image->servers[child_num].pid = pid; + + return 0; +} + +static int make_sock(pool *pconf, const struct sockaddr_in *server) +{ + int s; + int one = 1; + + if ((s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == -1) { + log_unixerr("socket", NULL, "Failed to get a socket, exiting child", + server_conf); + 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. + */ +#ifndef SOLARIS2 + s = ap_slack(s, AP_SLACK_HIGH); + + note_cleanups_for_fd(pconf, s); /* arrange to close on exec or restart */ +#endif + +#ifndef MPE +/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */ + if (setsockopt(s, SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(int)) < 0) { + log_unixerr("setsockopt", "(SO_REUSEADDR)", NULL, server_conf); + exit(1); + } + one = 1; + if (setsockopt(s, SOL_SOCKET,SO_KEEPALIVE,(char *)&one,sizeof(int)) < 0) { + log_unixerr("setsockopt", "(SO_KEEPALIVE)", NULL, server_conf); + exit(1); + } +#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 <johnh@isi.edu> 25-Oct-96 + * + * If no size is specified, use the kernel default. + */ + if (server_conf->send_buffer_size) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, + (char *)&server_conf->send_buffer_size, sizeof(int)) < 0) { + log_unixerr("setsockopt", "(SO_SNDBUF)", + "Failed to set SendBufferSize, using default", + server_conf); + /* not a fatal error */ + } + } + +#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) + { + perror("bind"); +#ifdef MPE + if (ntohs(server->sin_port) < 1024) GETUSERMODE(); +#endif + if (server->sin_addr.s_addr != htonl(INADDR_ANY)) + fprintf(stderr,"httpd: could not bind to address %s port %d\n", + inet_ntoa(server->sin_addr), ntohs(server->sin_port)); + else + fprintf(stderr,"httpd: could not bind to port %d\n", + ntohs(server->sin_port)); + exit(1); + } +#ifdef MPE + if (ntohs(server->sin_port) < 1024) GETUSERMODE(); +#endif + listen(s, 512); + +#ifdef SOLARIS2 + s = ap_slack(s, AP_SLACK_HIGH); + + note_cleanups_for_fd(pconf, s); /* arrange to close on exec or restart */ +#endif + return s; +} + +static listen_rec *old_listeners; + +static void copy_listeners(pool *p) + { + listen_rec *lr; + + assert(old_listeners == NULL); + for(lr=listeners ; lr ; lr=lr->next) + { + listen_rec *nr=malloc(sizeof *nr); + if (nr == NULL) { + fprintf (stderr, "Ouch! malloc failed in copy_listeners()\n"); + exit (1); + } + *nr=*lr; + kill_cleanups_for_fd(p,nr->fd); + nr->next=old_listeners; + assert(!nr->used); + old_listeners=nr; + } + } + +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() + { + listen_rec *or,*next; + + for(or=old_listeners ; or ; or=next) + { + next=or->next; + if(!or->used) + close(or->fd); + free(or); + } + old_listeners=NULL; + } + +/***************************************************************** + * Executive routines. + */ + +void standalone_main(int argc, char **argv) +{ + struct sockaddr_in sa_server; + int saved_sd; + int remaining_children_to_start; + + standalone = 1; + sd = listenmaxfd = -1; + + is_graceful = 0; + ++generation; + + if (!one_process) detach (); + + do { + copy_listeners(pconf); + saved_sd = sd; + if (!is_graceful) { + restart_time = time(NULL); + } +#ifdef SCOREBOARD_FILE + else { + kill_cleanups_for_fd (pconf, scoreboard_fd); + } +#endif + clear_pool (pconf); + ptrans = make_sub_pool (pconf); + + server_conf = read_config (pconf, ptrans, server_confname); + + if (listeners == NULL) { + if (!is_graceful) { + memset ((char *)&sa_server, 0, sizeof (sa_server)); + sa_server.sin_family = AF_INET; + sa_server.sin_addr = bind_address; + sa_server.sin_port = htons (server_conf->port); + sd = make_sock (pconf, &sa_server); + } + else { + sd = saved_sd; + note_cleanups_for_fd(pconf, sd); + } + } + else { + listen_rec *lr; + int fd; + + listenmaxfd = -1; + FD_ZERO (&listenfds); + for (lr = listeners; lr != NULL; lr = lr->next) + { + fd = find_listener (lr); + if (fd < 0) { + fd = make_sock (pconf, &lr->local_addr); + } + FD_SET (fd, &listenfds); + if (fd > listenmaxfd) listenmaxfd = fd; + lr->fd = fd; + } + close_unused_listeners (); + sd = -1; + } + + init_modules (pconf, server_conf); + open_logs (server_conf, pconf); + set_group_privs (); + accept_mutex_init (pconf); + if (!is_graceful) { + reinit_scoreboard(pconf); + } +#ifdef SCOREBOARD_FILE + else { + scoreboard_fname = server_root_relative (pconf, scoreboard_fname); + note_cleanups_for_fd (pconf, scoreboard_fd); + } +#endif + + default_server_hostnames (server_conf); + + set_signals (); + log_pid (pconf, pid_fname); + + if (daemons_max_free < daemons_min_free + 1) /* Don't thrash... */ + daemons_max_free = 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 = daemons_to_start; + if( remaining_children_to_start > daemons_limit ) { + remaining_children_to_start = daemons_limit; + } + if (!is_graceful) { + while (remaining_children_to_start) { + --remaining_children_to_start; + make_child (server_conf, remaining_children_to_start); + } + } + + log_error ("Server configured -- resuming normal operations", + server_conf); + restart_pending = 0; + + while (!restart_pending) { + int child_slot; + int pid = wait_or_timeout (); + + /* 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) { + /* Child died... note that it's gone in the scoreboard. */ + sync_scoreboard_image (); + child_slot = find_child_by_pid (pid); + Explain2 ("Reaping child %d slot %d", pid, child_slot); + if (child_slot >= 0) { + (void)update_child_status (child_slot, SERVER_DEAD, + (request_rec *)NULL); + } else if (is_graceful) { + /* Great, we've probably just lost a slot in the + * scoreboard. Somehow we don't know about this + * child. + */ + log_printf (server_conf, + "long lost child came home! (pid %d)", pid ); + } + } 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. + */ + while (remaining_children_to_start > 0) { + child_slot = find_free_child_num (); + if (child_slot < 0 || child_slot >= daemons_limit) { + remaining_children_to_start = 0; + break; + } + if (make_child (server_conf, child_slot) < 0) { + remaining_children_to_start = 0; + break; + } + --remaining_children_to_start; + } + /* 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; + } + + sync_scoreboard_image (); + if ((remaining_children_to_start + || (count_idle_servers () < daemons_min_free)) + && (child_slot = find_free_child_num ()) >= 0 + && child_slot < daemons_limit) { + make_child (server_conf, child_slot); + } + if (remaining_children_to_start) { + --remaining_children_to_start; + } + } + + /* we've been told to restart */ + signal (SIGHUP, SIG_IGN); + signal (SIGUSR1, SIG_IGN); + + if (one_process) { + /* not worth thinking about */ + exit (0); + } + + if (is_graceful) { +#ifndef SCOREBOARD_FILE + int i; +#endif + + /* USE WITH CAUTION: Graceful restarts are not known to work + * in various configurations on the architectures we support. */ + scoreboard_image->global.exit_generation = generation; + update_scoreboard_global (); + + log_error ("SIGUSR1 received. Doing graceful restart",server_conf); + kill_cleanups_for_fd (pconf, sd); + /* kill off the idle ones */ + if (ap_killpg(pgrp, SIGUSR1) < 0) { + log_unixerr ("killpg SIGUSR1", NULL, NULL, server_conf); + } +#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. + */ + sync_scoreboard_image(); + for (i = 0; i < daemons_limit; ++i ) { + if (scoreboard_image->servers[i].status != SERVER_DEAD) { + scoreboard_image->servers[i].status = SERVER_GRACEFUL; + } + } +#endif + } + else { + /* Kill 'em off */ + if (ap_killpg (pgrp, SIGHUP) < 0) { + log_unixerr ("killpg SIGHUP", NULL, NULL, server_conf); + } + reclaim_child_processes(); /* Not when just starting up */ + log_error ("SIGHUP received. Attempting to restart", server_conf); + } + ++generation; + + } while (restart_pending); + +} /* standalone_main */ + +extern char *optarg; +extern int optind; + +int +main(int argc, char *argv[]) +{ + int c; + +#ifdef AUX + (void)set42sig(); +#endif + +#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 + + init_alloc(); + pconf = permanent_pool; + ptrans = make_sub_pool(pconf); + + server_argv0 = argv[0]; + strncpy (server_root, HTTPD_ROOT, sizeof(server_root)-1); + server_root[sizeof(server_root)-1] = '\0'; + strncpy (server_confname, SERVER_CONFIG_FILE, sizeof(server_root)-1); + server_confname[sizeof(server_confname)-1] = '\0'; + + while((c = getopt(argc,argv,"Xd:f:vhl")) != -1) { + switch(c) { + case 'd': + strncpy (server_root, optarg, sizeof(server_root)-1); + server_root[sizeof(server_root)-1] = '\0'; + break; + case 'f': + strncpy (server_confname, optarg, sizeof(server_confname)-1); + server_confname[sizeof(server_confname)-1] = '\0'; + break; + case 'v': + printf("Server version %s.\n",SERVER_VERSION); + exit(0); + case 'h': + show_directives(); + exit(0); + case 'l': + show_modules(); + exit(0); + case 'X': + ++one_process; /* Weird debugging mode. */ + break; + case '?': + usage(argv[0]); + } + } + +#ifdef __EMX__ + printf("%s \n",SERVER_VERSION); + printf("OS/2 port by Garey Smiley <garey@slink.com> \n"); +#endif + + setup_prelinked_modules(); + + suexec_enabled = init_suexec(); + server_conf = read_config (pconf, ptrans, server_confname); + init_modules (pconf, server_conf); + + if(standalone) { + clear_pool (pconf); /* standalone_main rereads... */ + standalone_main(argc, argv); + } + else { + conn_rec *conn; + request_rec *r; + struct sockaddr sa_server, sa_client; + BUFF *cio; + + open_logs(server_conf, pconf); + set_group_privs(); + default_server_hostnames (server_conf); + +#ifdef MPE + /* Only try to switch if we're running as MANAGER.SYS */ + if (geteuid() == 1 && user_id > 1) { + GETPRIVMODE(); + if (setuid(user_id) == -1) { + GETUSERMODE(); +#else + /* Only try to switch if we're running as root */ + if(!geteuid() && setuid(user_id) == -1) { +#endif + log_unixerr("setuid", NULL, "unable to change uid", server_conf); + exit (1); + } +#ifdef MPE + GETUSERMODE(); + } +#endif + if (ap_setjmp (jmpbuffer)) { + exit (0); + } + + c = sizeof(sa_client); + if ((getpeername(fileno(stdin), &sa_client, &c)) < 0) + { +/* get peername will fail if the input isn't a socket */ + perror("getpeername"); + memset(&sa_client, '\0', sizeof(sa_client)); + } + + c = sizeof(sa_server); + if(getsockname(fileno(stdin), &sa_server, &c) < 0) { + perror("getsockname"); + fprintf(stderr, "Error getting local address\n"); + exit(1); + } + server_conf->port =ntohs(((struct sockaddr_in *)&sa_server)->sin_port); + cio = bcreate(ptrans, B_RDWR); +#ifdef 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. */ + + cio->fd = fileno(stdin); +#else + cio->fd = fileno(stdout); +#endif + cio->fd_in = fileno(stdin); + conn = new_connection (ptrans, server_conf, cio, + (struct sockaddr_in *)&sa_client, + (struct sockaddr_in *)&sa_server,-1); + r = read_request (conn); + if (r) process_request (r); /* else premature EOF (ignore) */ + + while (r && conn->keepalive && !conn->aborted) { + destroy_pool(r->pool); + r = read_request (conn); + if (r) process_request (r); + } + + bclose(cio); + } + exit (0); +} + +#ifdef __EMX__ +#ifdef HAVE_MMAP +/* 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; +} +#endif +#endif + diff --git a/usr.sbin/httpd/src/http_main.h b/usr.sbin/httpd/src/http_main.h new file mode 100644 index 00000000000..7d3c2f5a789 --- /dev/null +++ b/usr.sbin/httpd/src/http_main.h @@ -0,0 +1,99 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * Routines in http_main.c which other code --- in particular modules --- + * may want to call. Right now, that's limited to timeout handling. + * There are two functions which modules can call to trigger a timeout + * (with the per-virtual-server timeout duration); these are hard_timeout + * and soft_timeout. + * + * The difference between the two is what happens when the timeout + * expires (or earlier than that, if the client connection aborts) --- + * a soft_timeout just puts the connection to the client in an + * "aborted" state, which will cause http_protocol.c to stop trying to + * talk to the client, but otherwise allows the code to continue normally. + * hard_timeout(), by contrast, logs the request, and then aborts it + * completely --- longjmp()ing out to the accept() loop in http_main. + * Any resources tied into the request's resource pool will be cleaned up; + * everything that isn't will leak. + * + * soft_timeout() is recommended as a general rule, because it gives your + * code a chance to clean up. However, hard_timeout() may be the most + * convenient way of dealing with timeouts waiting for some external + * resource other than the client, if you can live with the restrictions. + * + * (When a hard timeout is in scope, critical sections can be guarded + * with block_alarms() and unblock_alarms() --- these are declared in + * alloc.c because they are most often used in conjunction with + * routines to allocate something or other, to make sure that the + * cleanup does get registered before any alarm is allowed to happen + * which might require it to be cleaned up; they * are, however, + * implemented in http_main.c). + * + * kill_timeout() will disarm either variety of timeout. + * + * reset_timeout() resets the timeout in progress. + */ + +void hard_timeout (char *, request_rec *); +void keepalive_timeout (char *, request_rec *); +void soft_timeout (char *, request_rec *); +void kill_timeout (request_rec *); +void reset_timeout (request_rec *); + +void sync_scoreboard_image (); +int update_child_status (int child_num, int status, request_rec *r); +int get_child_status (int child_num); +int count_busy_servers (); +int count_idle_servers (); + diff --git a/usr.sbin/httpd/src/http_protocol.c b/usr.sbin/httpd/src/http_protocol.c new file mode 100644 index 00000000000..f34097260c1 --- /dev/null +++ b/usr.sbin/httpd/src/http_protocol.c @@ -0,0 +1,2047 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * 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_log.h" /* For errors detected in basic auth + * common support code... + */ +#include "util_date.h" /* For parseHTTPdate and BAD_DATE */ +#include <stdarg.h> + +#define SET_BYTES_SENT(r) \ + do { if (r->sent_bodyct) \ + 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 > *end) + return 0; + + if (*end >= clength) + *end = clength - 1; + + return 1; +} + +static int internal_byterange(int, long*, request_rec*, char**, long*, long*); + +int set_byterange (request_rec *r) +{ + char *range, *if_range, *match; + char ts[MAX_STRING_LEN]; + 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 = table_get(r->headers_in, "Range"))) + range = table_get(r->headers_in, "Request-Range"); + + if (!range || strncasecmp(range, "bytes=", 6)) { + table_set (r->headers_out, "Accept-Ranges", "bytes"); + return 0; + } + + /* Check the If-Range header for Etag or Date */ + + if ((if_range = table_get(r->headers_in, "If-Range"))) { + if (if_range[0] == '"') { + if (!(match = table_get(r->headers_out, "Etag")) || + (strcasecmp(if_range, match) != 0)) + return 0; + } + else if (!(match = table_get(r->headers_out, "Last-Modified")) || + (strcasecmp(if_range, match) != 0)) + return 0; + } + + if (!strchr(range, ',')) { + /* A single range */ + if (!parse_byterange(pstrdup(r->pool, range + 6), r->clength, + &range_start, &range_end)) + return 0; + + r->byterange = 1; + + ap_snprintf(ts, sizeof(ts), "bytes %ld-%ld/%ld", + range_start, range_end, r->clength); + table_set(r->headers_out, "Content-Range", ts); + ap_snprintf(ts, sizeof(ts), "%ld", range_end - range_start + 1); + table_set(r->headers_out, "Content-Length", ts); + } + else { + /* a multiple range */ + char boundary[33]; /* Long enough */ + char *r_range = pstrdup(r->pool, range + 6); + long tlength = 0; + + r->byterange = 2; + ap_snprintf(boundary, sizeof(boundary), "%lx%lx", + r->request_time, (long)getpid()); + r->boundary = pstrdup(r->pool, boundary); + while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL)); + ap_snprintf(ts, sizeof(ts), "%ld", tlength); + table_set(r->headers_out, "Content-Length", ts); + } + + r->status = PARTIAL_CONTENT; + r->range = range + 6; + + return 1; +} + +int 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, + char **r_range, long *offset, long *length) +{ + long range_start, range_end; + char *range; + + if (!**r_range) { + if (r->byterange > 1) { + if (realreq) + rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL); + else + *tlength += 4 + strlen(r->boundary) + 4; + } + return 0; + } + + range = getword_nc(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) { + char *ct = r->content_type ? r->content_type : default_type(r); + char ts[MAX_STRING_LEN]; + + ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end, r->clength); + if (realreq) + 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; +} + +int set_content_length (request_rec *r, long clength) +{ + char ts[MAX_STRING_LEN]; + + r->clength = clength; + + ap_snprintf (ts, sizeof(ts), "%ld", clength); + table_set (r->headers_out, "Content-Length", ts); + + return 0; +} + +int set_keepalive(request_rec *r) +{ + int ka_sent = 0; + int wimpy = find_token(r->pool, + table_get(r->headers_out, "Connection"), "close"); + char *conn = 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 || + table_get(r->headers_out, "Content-Length") || + find_last_token(r->pool, + table_get(r->headers_out, "Transfer-Encoding"), + "chunked") || + ((r->proto_num >= 1001) && (r->chunked = 1))) && + r->server->keep_alive && + (r->server->keep_alive_timeout > 0) && + ((r->server->keep_alive_max == 0) || + (r->server->keep_alive_max > r->connection->keepalives)) && + !status_drops_connection(r->status) && + !wimpy && + !find_token(r->pool, conn, "close") && + (!table_get(r->subprocess_env, "nokeepalive") || + table_get(r->headers_in, "Via")) && + ((ka_sent = find_token(r->pool, conn, "keep-alive")) || + (r->proto_num >= 1001)) + ) { + char header[256]; + 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_snprintf(header, sizeof(header), "timeout=%d, max=%d", + r->server->keep_alive_timeout, left); + else + ap_snprintf(header, sizeof(header), "timeout=%d", + r->server->keep_alive_timeout); + table_set(r->headers_out, "Keep-Alive", header); + table_merge(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) + table_merge(r->headers_out, "Connection", "close"); + + r->connection->keepalive = 0; + + return 0; +} + +int set_last_modified(request_rec *r, time_t mtime) +{ + char *etag, weak_etag[MAX_STRING_LEN]; + char *if_match, *if_modified_since, *if_unmodified, *if_nonematch; + time_t now = time(NULL); + + if (now < 0) + now = r->request_time; + + table_set(r->headers_out, "Last-Modified", + gm_timestr_822(r->pool, (mtime > now) ? now : mtime)); + + /* 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. + */ + + if (r->finfo.st_mode != 0) + ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx-%lx-%lx\"", + (unsigned long)r->finfo.st_ino, + (unsigned long)r->finfo.st_size, (unsigned long)mtime); + else + ap_snprintf(weak_etag, sizeof(weak_etag), "W/\"%lx\"", + (unsigned long)mtime); + + etag = weak_etag + ((r->request_time - mtime > 1) ? 2 : 0); + table_set(r->headers_out, "ETag", etag); + + /* 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. + */ + + if (!is_HTTP_SUCCESS(r->status) || r->no_local_copy) + return OK; + + /* If an If-Match request-header field was given and + * if our ETag does not match any of the entity tags in that field + * and the field value is not "*" (meaning match anything), then + * respond with a status of 412 (Precondition Failed). + */ + + if ((if_match = table_get(r->headers_in, "If-Match")) != NULL) { + if ((if_match[0] != '*') && !find_token(r->pool, if_match, etag)) + return HTTP_PRECONDITION_FAILED; + } + + /* 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). + */ + + else if ((if_unmodified = table_get(r->headers_in, "If-Unmodified-Since")) + != NULL) { + time_t ius = parseHTTPdate(if_unmodified); + + if ((ius != BAD_DATE) && (mtime > ius)) + return HTTP_PRECONDITION_FAILED; + } + + /* If an If-None-Match request-header field was given and + * if our ETag matches any of the entity tags in that field or + * if the field value is "*" (meaning match anything), then + * if the request method was GET or HEAD, the server SHOULD + * respond with a 304 (Not Modified) response. + * For all other request methods, the server MUST + * respond with a status of 412 (Precondition Failed). + */ + + if ((if_nonematch = table_get(r->headers_in, "If-None-Match")) != NULL) { + if ((if_nonematch[0] == '*') || find_token(r->pool,if_nonematch,etag)) + return (r->method_number == M_GET) ? HTTP_NOT_MODIFIED + : 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 = + table_get(r->headers_in, "If-Modified-Since")) != NULL)) { + time_t ims = parseHTTPdate(if_modified_since); + + if ((ims >= mtime) && (ims <= r->request_time)) + return HTTP_NOT_MODIFIED; + } + + return OK; +} + +/* 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 = 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? */ + *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) && + (blookc(&next, in) == 1) && + ((next == ' ') || (next == '\t'))); + + return total; +} + +void parse_uri (request_rec *r, const char *uri) +{ + const char *s; + +#ifdef __EMX__ + /* Variable for OS/2 fix below. */ + int loop; +#endif + +/* A proxy request contains a ':' early on, but not as first character */ + for (s=uri; s != '\0'; s++) + if (!isalnum(*s) && *s != '+' && *s != '-' && *s != '.') break; + + if (*s == ':' && s != uri) + { + r->proxyreq = 1; + r->uri = pstrdup(r->pool, uri); + r->args = NULL; + } + else if (r->method && !strcmp(r->method, "TRACE")) { + r->proxyreq = 0; + r->uri = pstrdup(r->pool, uri); + r->args = NULL; + } + else { + r->proxyreq = 0; + r->uri = getword (r->pool, &uri, '?'); + +#ifdef __EMX__ + /* 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. */ + for (loop = 0; loop <= strlen(r->uri); ++loop) { + if (r->uri[loop] == '\\') + r->uri[loop] = '/'; + }; + + /* Fix OS/2 HPFS filename case problem. */ + r->uri = strlwr(r->uri); +#endif + + if (*uri) r->args= pstrdup(r->pool, uri); + else r->args = NULL; + } +} + +const char *check_fulluri (request_rec *r, const char *uri) +{ + char *name, *host; + int i; + unsigned port; + server_addr_rec * sar; + + /* This routine parses full URLs, if they match the server */ + if (strncasecmp(uri, "http://", 7)) return uri; + name = pstrdup(r->pool, uri + 7); + + /* Find the hostname, assuming a valid request */ + i = ind(name, '/'); + name[i] = '\0'; + + /* Find the port */ + host = getword_nc(r->pool, &name, ':'); + if (*name) port = atoi(name); + else port = 80; + + /* Make sure ports patch */ + if (port != r->server->port) { + for (sar = r->server->addrs; sar; sar = sar->next) { + if( (sar->host_port == 0) || (port == sar->host_port) ) + break; + } + if (!sar) return uri; + } + + /* Save it for later use */ + r->hostname = pstrdup(r->pool, host); + r->hostlen = 7 + i; + + /* The easy cases first */ + if (!strcasecmp(host, r->server->server_hostname)) { + return (uri + r->hostlen); + } + else if (!strcmp(host, inet_ntoa(r->connection->local_addr.sin_addr))) { + return (uri + r->hostlen); + } + + /* Now things get a bit trickier - check the IP address(es) of the host */ + /* they gave, see if it matches ours. */ + else { + struct hostent *hp; + int n; + + if ((hp = gethostbyname(host))) { + for (n = 0; hp->h_addr_list[n] != NULL; n++) { + if (r->connection->local_addr.sin_addr.s_addr == + (((struct in_addr *)(hp->h_addr_list[n]))->s_addr)) { + return (uri + r->hostlen); + } + } + } + } + + return uri; +} + +int read_request_line (request_rec *r) +{ + char l[HUGE_STRING_LEN]; + const char *ll = l, *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. + */ + bsetflag( conn->client, B_SAFEREAD, 1 ); + while ((len = getline(l, HUGE_STRING_LEN, conn->client, 0)) <= 0) { + if ((len < 0) || bgetflag(conn->client, B_EOF)) { + bsetflag( conn->client, B_SAFEREAD, 0 ); + return 0; + } + } + /* we've probably got something to do, ignore graceful restart requests */ + signal (SIGUSR1, SIG_IGN); + bsetflag( conn->client, B_SAFEREAD, 0 ); + if (len == (HUGE_STRING_LEN - 1)) { + log_printf(r->server, "request failed for %s, reason: URI too long", + get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME)); + r->status = HTTP_REQUEST_URI_TOO_LARGE; + return 0; + } + + r->request_time = time(NULL); + r->the_request = pstrdup (r->pool, l); + r->method = getword_white(r->pool, &ll); + uri = getword_white(r->pool, &ll); + uri = check_fulluri(r, uri); + parse_uri (r, uri); + + r->assbackwards = (ll[0] == '\0'); + r->protocol = pstrdup (r->pool, ll[0] ? ll : "HTTP/0.9"); + sscanf(r->protocol, "HTTP/%d.%d", &major, &minor); + r->proto_num = 1000*major + minor; + + return 1; +} + +void get_mime_headers (request_rec *r) +{ + conn_rec *c = r->connection; + int len; + char *value; + char field[MAX_STRING_LEN]; + + /* Read header lines until we get the empty separator line, + * a read error, the connection closes (EOF), or we timeout. + * Should we also check for overflow (len == MAX_STRING_LEN-1)? + */ + while ((len = getline(field, MAX_STRING_LEN, c->client, 1)) > 0) { + + if (!(value = strchr(field,':'))) /* Find the colon separator */ + continue; /* or should puke 400 here */ + + *value = '\0'; + ++value; + while (isspace(*value)) ++value; /* Skip to start of value */ + + table_merge(r->headers_in, field, value); + } +} + +#define ADDR_MATCHES(addr1,addr2) \ + (addr1.s_addr == addr2.s_addr) || (addr1.s_addr == htonl(INADDR_ANY)) \ + || (addr1.s_addr == DEFAULT_VHOST_ADDR) + +static void check_hostalias (request_rec *r) +{ + const char *hostname=r->hostname; + char *host = getword(r->pool, &hostname, ':'); /* Get rid of port */ + unsigned port = (*hostname) ? atoi(hostname) : 80; + server_rec *s = r->server; + server_addr_rec * sar; + int l; + +/* make sure the client can't spoof the port; + * have to check all possiblities to see if the server + * should be listening. */ + if (port != r->server->port) { + for (sar = s->addrs; sar; sar = sar->next) { + if ( (port == sar->host_port) || (sar->host_port == 0) ) + break; + } + if (!sar) return; + } + + l = strlen(host)-1; + if ((host[l]) == '.') { + host[l] = '\0'; + } + + r->hostname = host; + + for (s = r->server->next; s; s = s->next) { + const char *names; + server_addr_rec *sar; + + if (s->addrs == NULL) { + /* this server has been disabled because of DNS screwups during + configuration */ + continue; + } +/* ok, now there are several possibilities, and we're matching the + * hostname, the port, and r->connection->local_addr. The last is + * required so as to only respond on an address to which this vhost + * should actually be listening. + * + * Either we can match s->server_name and s->port while matching + * against the ip address in a record in the s->addrs list *or* we + * can match s->server_name and a complete record in the s->addrs + * list *or* we can match the virtual host name and the address and + * the port of a record in the s->addrs list. + */ + if (!strcasecmp(host,s->server_hostname)) { /* ServerName matches hostname */ + if (port == s->port) { /* possibly configured by Port */ + for (sar = s->addrs; sar; sar = sar->next) { + if (ADDR_MATCHES(sar->host_addr,r->connection->local_addr.sin_addr)) + break; /* SN matches, Port matches, and one IP addr matches */ + } + } else { /* check to see if an addr matches */ + for (sar = s->addrs; sar; sar = sar->next) { + if (((port == sar->host_port) || (sar->host_port == 0)) + && (ADDR_MATCHES(sar->host_addr, + r->connection->local_addr.sin_addr))) + break; /* SN matches, and a addr matches IP & port */ + } + } + if (sar) { /* we got a match */ + r->server = r->connection->server = s; + if (r->hostlen && !strncasecmp(r->uri, "http://", 7)) { + r->uri += r->hostlen; + parse_uri(r, r->uri); + /* we still might want to do something below (ie. set r->proxyreq) */ + } + } + } /* ServerName doesn't match */ + + /* now s->addrs list, include the names, from the VirtualHost directive */ + for (sar = s->addrs; sar; sar = sar->next) { + if (((sar->host_port==0) || (port==sar->host_port)) + && (ADDR_MATCHES(sar->host_addr,r->connection->local_addr.sin_addr)) + && !strcasecmp(host,sar->virthost)) { + /* ok, an element in the addrs list matched all three items */ + r->server = r->connection->server = s; + if (r->hostlen && !strncasecmp(r->uri, "http://", 7)) { + r->uri += r->hostlen; + r->proxyreq = 0; + } + } + } + + /* search all the aliases from ServerAlias directive + * ServerAlias acts like a wildcard, so as to help deal with the + * transition when the DNS for a given host changes. + */ + names = s->names; + if (names) { + while (*names) { + char *name = getword_conf (r->pool, &names); + + if ((is_matchexp(name) && !strcasecmp_match(host, name)) || + (!strcasecmp(host, name))) { + r->server = r->connection->server = s; + if (r->hostlen && !strncasecmp(r->uri, "http://", 7)) { + r->uri += r->hostlen; + r->proxyreq = 0; + } + } + } + } + } +} + +void check_serverpath (request_rec *r) +{ + server_rec *s; + server_addr_rec * sar = NULL; + int port = 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. + */ + + for (s = r->server->next; s; s = s->next) { + /* we should check to make sure that this server should be listening + * at all. + * + * this code is duplicated in check_hostalias, but only one of these + * two functions runs for a given request. + */ + if (!s->addrs) continue; + + if( (port == s->port) ) { + for(sar = s->addrs; sar; sar = sar->next) { + if(ADDR_MATCHES(sar->host_addr, r->connection->local_addr.sin_addr)) + break; + } + } + else { + for(sar = s->addrs; sar; sar = sar->next) { + if( ( (port == sar->host_port) || (sar->host_port == 0) ) + && ( ADDR_MATCHES(sar->host_addr, + r->connection->local_addr.sin_addr) ) ) + break; + } + } + + if (!sar) continue; /* no match */ + + 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; + } +} + +request_rec *read_request (conn_rec *conn) +{ + request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec)); + + r->connection = conn; + conn->server = conn->base_server; + r->server = conn->server; + r->pool = make_sub_pool(conn->pool); + + conn->keptalive = conn->keepalive; + conn->keepalive = 0; + + conn->user = NULL; + conn->auth_type = NULL; + + r->headers_in = make_table (r->pool, 50); + r->subprocess_env = make_table (r->pool, 50); + r->headers_out = make_table (r->pool, 5); + r->err_headers_out = make_table (r->pool, 5); + r->notes = make_table (r->pool, 5); + + r->request_config = create_request_config (r->pool); + r->per_dir_config = r->server->lookup_defaults; /* For now. */ + + 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 */ + + /* Get the request... */ + + keepalive_timeout("read request line", r); + if (!read_request_line (r)) { + kill_timeout(r); + return NULL; + } + if (!r->assbackwards) { + hard_timeout("read request headers", r); + get_mime_headers (r); + } + kill_timeout(r); + + r->status = HTTP_OK; /* Until further notice. */ + + /* handle Host header here, to get virtual server */ + + if (r->hostname || (r->hostname = table_get(r->headers_in, "Host"))) + check_hostalias(r); + else + check_serverpath(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(!strcmp(r->method, "HEAD")) { + r->header_only=1; + r->method_number = M_GET; + } + else if(!strcmp(r->method, "GET")) + r->method_number = M_GET; + else if(!strcmp(r->method,"POST")) + r->method_number = M_POST; + else if(!strcmp(r->method,"PUT")) + r->method_number = M_PUT; + else if(!strcmp(r->method,"DELETE")) + r->method_number = M_DELETE; + else if(!strcmp(r->method,"CONNECT")) + r->method_number = M_CONNECT; + else if(!strcmp(r->method,"OPTIONS")) + r->method_number = M_OPTIONS; + else if(!strcmp(r->method,"TRACE")) + r->method_number = M_TRACE; + else + r->method_number = M_INVALID; /* Will eventually croak. */ + + 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 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 = copy_table (rnew->pool, r->subprocess_env); + rnew->headers_out = make_table (rnew->pool, 5); + rnew->err_headers_out = make_table (rnew->pool, 5); + rnew->notes = make_table (rnew->pool, 5); + + rnew->read_length = r->read_length; + rnew->read_body = REQUEST_NO_BODY; + + rnew->main = (request_rec *)r; +} + +void finalize_sub_req_protocol (request_rec *sub) +{ + SET_BYTES_SENT (sub->main); +} + +/* Support for the Basic authentication protocol, and a bit for Digest. + */ + +void note_auth_failure(request_rec *r) +{ + if (!strcasecmp(auth_type(r), "Basic")) + note_basic_auth_failure(r); + else if(!strcasecmp(auth_type(r), "Digest")) + note_digest_auth_failure(r); +} + +void note_basic_auth_failure(request_rec *r) +{ + if (strcasecmp(auth_type(r), "Basic")) + note_auth_failure(r); + else + table_set (r->err_headers_out, "WWW-Authenticate", + pstrcat(r->pool, "Basic realm=\"", auth_name(r), "\"", NULL)); +} + +void note_digest_auth_failure(request_rec *r) +{ + char nonce[256]; + + ap_snprintf(nonce, sizeof(nonce), "%lu", r->request_time); + table_set (r->err_headers_out, "WWW-Authenticate", + pstrcat(r->pool, "Digest realm=\"", auth_name(r), + "\", nonce=\"", nonce, "\"", NULL)); +} + +int get_basic_auth_pw (request_rec *r, char **pw) +{ + const char *auth_line = table_get (r->headers_in, "Authorization"); + char *t; + + if(!(t = auth_type(r)) || strcasecmp(t, "Basic")) + return DECLINED; + + if (!auth_name (r)) { + log_reason ("need AuthName", r->uri, r); + return SERVER_ERROR; + } + + if(!auth_line) { + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + + if (strcasecmp(getword (r->pool, &auth_line, ' '), "Basic")) { + /* Client tried to authenticate using wrong auth scheme */ + log_reason ("client used wrong authentication scheme", r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + + t = uudecode (r->pool, auth_line); + r->connection->user = getword_nulls_nc (r->connection->pool, &t, ':'); + r->connection->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. + */ + +static char *status_lines[] = { + "100 Continue", + "101 Switching Protocols", +#define LEVEL_200 2 + "200 OK", + "201 Created", + "202 Accepted", + "203 Non-Authoritative Information", + "204 No Content", + "205 Reset Content", + "206 Partial Content", +#define LEVEL_300 9 + "300 Multiple Choices", + "301 Moved Permanently", + "302 Moved Temporarily", + "303 See Other", + "304 Not Modified", + "305 Use Proxy", +#define LEVEL_400 15 + "400 Bad Request", + "401 Authorization Required", + "402 Payment Required", + "403 Forbidden", + "404 File 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", +#define LEVEL_500 31 + "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 Varies" +}; + +/* 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]; + */ + +int 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. + */ +int send_header_field (request_rec *r, const char *fieldname, + const char *fieldval) +{ + return (0 < bvputs(r->connection->client, + fieldname, ": ", fieldval, "\015\012", NULL)); +} + +void basic_http_header (request_rec *r) +{ + char *protocol; + + if (r->assbackwards) return; + + if (!r->status_line) + r->status_line = status_lines[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 == 1000 + && table_get(r->subprocess_env,"force-response-1.0"))) { + + protocol = "HTTP/1.0"; + r->connection->keepalive = -1; + } + else + protocol = SERVER_PROTOCOL; + + /* Output the HTTP/1.x Status-Line and the Date and Server fields */ + + bvputs(r->connection->client, + protocol, " ", r->status_line, "\015\012", NULL); + + send_header_field(r, "Date", gm_timestr_822(r->pool, r->request_time)); + send_header_field(r, "Server", SERVER_VERSION); + + table_unset(r->headers_out, "Date"); /* Avoid bogosity */ + table_unset(r->headers_out, "Server"); +} + +/* 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 the 256th or 257th byte 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; + + bgetopt(client, BO_BYTECT, &bs); + if (bs >= 255 && bs <= 257) + bputs("X-Pad: avoid browser bug\015\012", client); + + 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 + 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_OPTIONS)) ? ", OPTIONS" : "", + ", TRACE", + NULL); +} + +int send_http_trace (request_rec *r) +{ + int rv; + + /* Get the original request */ + while (r->prev) r = r->prev; + + if ((rv = setup_client_block(r, REQUEST_NO_BODY))) + return rv; + + hard_timeout("send TRACE", r); + + r->content_type = "message/http"; + send_http_header(r); + + /* Now we recreate the request, and echo it back */ + + rvputs( r, r->the_request, "\015\012", NULL ); + + table_do((int (*)(void *, const char *, const char *))send_header_field, + (void *)r, r->headers_in, NULL); + bputs("\015\012", r->connection->client); + + kill_timeout(r); + return OK; +} + +int send_http_options(request_rec *r) +{ + const long int zero = 0L; + + if (r->assbackwards) return DECLINED; + + hard_timeout("send OPTIONS", r); + + basic_http_header(r); + + table_set(r->headers_out, "Content-Length", "0"); + table_set(r->headers_out, "Allow", make_allow(r)); + set_keepalive(r); + + table_do((int (*)(void *, const char *, const char *))send_header_field, + (void *)r, r->headers_out, NULL); + + terminate_header(r->connection->client); + + kill_timeout(r); + 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) { + char *ua; + return (table_get(r->headers_in, "Request-Range") || + ((ua = table_get(r->headers_in, "User-Agent")) + && strstr(ua, "MSIE 3"))); +} + +void send_http_header(request_rec *r) +{ + int i; + const long int zero = 0L; + + if (r->assbackwards) { + if(!r->main) + 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 (!is_empty_table(r->err_headers_out)) + r->headers_out = overlay_tables(r->pool, r->err_headers_out, + r->headers_out); + + hard_timeout("send headers", r); + + basic_http_header(r); + + set_keepalive(r); + + if (r->chunked) { + table_merge(r->headers_out, "Transfer-Encoding", "chunked"); + table_unset(r->headers_out, "Content-Length"); + } + + if (r->byterange > 1) + table_set(r->headers_out, "Content-Type", + pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/", + "byteranges; boundary=", r->boundary, NULL)); + else if (r->content_type) + table_set(r->headers_out, "Content-Type", r->content_type); + else + table_set(r->headers_out, "Content-Type", default_type(r)); + + if (r->content_encoding) + table_set(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) { + table_merge(r->headers_out, "Content-Language", + ((char**)(r->content_languages->elts))[i]); + } + } + else if (r->content_language) + table_set(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 && !table_get(r->headers_out, "Expires")) + table_add(r->headers_out, "Expires", + gm_timestr_822(r->pool, r->request_time)); + + /* Send the entire table of header fields, terminated by an empty line. */ + + table_do((int (*)(void *, const char *, const char *))send_header_field, + (void *)r, r->headers_out, NULL); + + terminate_header(r->connection->client); + + kill_timeout(r); + + 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) bsetflag(r->connection->client, B_CHUNK, 1); +} + +void finalize_request_protocol (request_rec *r) +{ + /* Turn off chunked encoding */ + + if (r->chunked && !r->connection->aborted) { + soft_timeout("send ending chunk", r); + bsetflag(r->connection->client, B_CHUNK, 0); + bputs("0\015\012", r->connection->client); + /* If we had footer "headers", we'd send them now */ + bputs("\015\012", r->connection->client); + 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. + */ + +int setup_client_block (request_rec *r, int read_policy) +{ + char *tenc = table_get(r->headers_in, "Transfer-Encoding"); + char *lenp = table_get(r->headers_in, "Content-Length"); + + r->read_body = read_policy; + r->read_chunked = 0; + r->remaining = 0; + + if (tenc) { + if (strcasecmp(tenc, "chunked")) { + log_printf(r->server, "Unknown Transfer-Encoding %s", tenc); + return HTTP_BAD_REQUEST; + } + if (r->read_body == REQUEST_CHUNKED_ERROR) { + log_reason("chunked Transfer-Encoding forbidden", r->uri, r); + return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED; + } + + r->read_chunked = 1; + } + else if (lenp) { + char *pos = lenp; + + while (isdigit(*pos) || isspace(*pos)) ++pos; + if (*pos != '\0') { + log_printf(r->server, "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))) { + log_printf(r->server, "%s with body is not allowed for %s", + r->method, r->uri); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + return OK; +} + +int 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->read_chunked && (r->remaining <= 0)) + return 0; + + if (r->proto_num >= 1001) { /* sending 100 Continue interim response */ + bvputs(r->connection->client, + SERVER_PROTOCOL, " ", status_lines[0], "\015\012\015\012", NULL); + bflush(r->connection->client); + } + + return 1; +} + +static long get_chunk_size (char *b) +{ + long chunksize = 0; + + while (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. + */ +long get_client_block (request_rec *r, char *buffer, int bufsiz) +{ + int c; + long len_read, len_to_read; + long chunk_start = 0; + + if (!r->read_chunked) { /* Content-length read */ + len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; + len_read = 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 */ + + 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)) + || !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); + table_unset(r->headers_in, "Transfer-Encoding"); + table_set(r->headers_in, "Content-Length", 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 = 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 = bgetc(r->connection->client)) == CR) { + c = 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 = discard_request_body(r)) != OK) + * return retval; + */ +int discard_request_body(request_rec *r) +{ + int rv; + + if ((rv = setup_client_block(r, REQUEST_CHUNKED_PASS))) + return rv; + + if (should_client_block(r)) { + char dumpbuf[HUGE_STRING_LEN]; + + hard_timeout("reading request body", r); + while ((rv = get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0) + continue; + kill_timeout(r); + + if (rv < 0) + return HTTP_BAD_REQUEST; + } + return OK; +} + +/* + * Send the body of a response to the client. + */ +long send_fd(FILE *f, request_rec *r) { return send_fd_length(f, r, -1); } + +long 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; + + 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 = bwrite(r->connection->client, &buf[o], n); + if (w > 0) { + reset_timeout(r); /* reset timeout after successful write */ + total_bytes_sent += w; + n-=w; + o+=w; + } + else if (w < 0) { + if (r->connection->aborted) + break; + else if (errno == EAGAIN) + continue; + else { + log_unixerr("send body lost connection to", + get_remote_host(r->connection, + r->per_dir_config, REMOTE_NAME), + NULL, r->server); + bsetflag(r->connection->client, B_EOUT, 1); + r->connection->aborted = 1; + break; + } + } + } + } + + kill_timeout(r); + SET_BYTES_SENT(r); + return total_bytes_sent; +} + +int rputc (int c, request_rec *r) +{ + if (r->connection->aborted) return EOF; + bputc(c, r->connection->client); + SET_BYTES_SENT(r); + return c; +} + +int rputs(const char *str, request_rec *r) +{ + if (r->connection->aborted) return EOF; + SET_BYTES_SENT(r); + return bputs(str, r->connection->client); +} + +int rwrite(const void *buf, int nbyte, request_rec *r) +{ + int n; + if (r->connection->aborted) return EOF; + n=bwrite(r->connection->client, buf, nbyte); + SET_BYTES_SENT(r); + return n; +} + +int rprintf(request_rec *r,const char *fmt,...) +{ + va_list vlist; + int n; + + if(r->connection->aborted) return EOF; + va_start(vlist,fmt); + n=vbprintf(r->connection->client,fmt,vlist); + va_end(vlist); + SET_BYTES_SENT(r); + return n; +} + +int 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 = bwrite(fb, x, j); + if (i != j) + { + va_end(args); + return -1; + } + k += i; + } + va_end(args); + + SET_BYTES_SENT(r); + return k; +} + +int rflush (request_rec *r) { + return bflush(r->connection->client); +} + +/* 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 send_error_response (request_rec *r, int recursive_error) +{ + BUFF *fd = r->connection->client; + int status = r->status; + int idx = index_of_response (status); + char *custom_response; + char *location = pstrdup(r->pool, table_get(r->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 (!is_empty_table(r->err_headers_out)) + r->headers_out = overlay_tables(r->pool, r->err_headers_out, + r->headers_out); + hard_timeout("send 304", r); + + basic_http_header(r); + set_keepalive(r); + + table_do((int (*)(void *, const char *, const char *))send_header_field, + (void *)r, r->headers_out, + "Connection", + "Keep-Alive", + "ETag", + "Content-Location", + "Expires", + "Cache-Control", + "Vary", + "Warning", + "WWW-Authenticate", + NULL); + + terminate_header(r->connection->client); + + kill_timeout(r); + return; + } + + if (status == HTTP_NO_CONTENT) { + send_http_header(r); + 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; + clear_table(r->err_headers_out); + + if (location && *location + && (is_HTTP_REDIRECT(status) || status == HTTP_CREATED)) + table_set(r->headers_out, "Location", location); + + 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)) + table_set(r->headers_out, "Allow", make_allow(r)); + + send_http_header(r); + + if (r->header_only) { + finalize_request_protocol(r); + return; + } + } + + hard_timeout("send error body", r); + + if ((custom_response = 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 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] == '\"') { + bputs(custom_response+1, fd); + kill_timeout(r); + finalize_request_protocol(r); + return; + } + /* Redirect failed, so get back the original error + */ + while (r->prev && (r->prev->status != HTTP_OK)) + r = r->prev; + } + { + char *title = status_lines[idx]; + /* folks decided they didn't want the error code in the H1 text */ + + char *h1 = 4 + status_lines[idx]; + + bvputs + ( + fd, + "<HTML><HEAD>\n<TITLE>", + title, + "</TITLE>\n</HEAD><BODY>\n<H1>", + h1, + "</H1>\n", + NULL + ); + + switch (status) { + case REDIRECT: + case MOVED: + bvputs(fd, "The document has moved <A HREF=\"", + escape_html(r->pool, location), "\">here</A>.<P>\n", NULL); + break; + case HTTP_SEE_OTHER: + bvputs(fd, "The answer to your request is located <A HREF=\"", + escape_html(r->pool, location), "\">here</A>.<P>\n", NULL); + break; + case HTTP_USE_PROXY: + bvputs(fd, "This resource is only accessible through the proxy\n", + escape_html(r->pool, location), "<BR>\nYou will need to ", + "configure your client to use that proxy.<P>\n", NULL); + break; + case AUTH_REQUIRED: + bputs("This server could not verify that you\n", fd); + bputs("are authorized to access the document you\n", fd); + bputs("requested. Either you supplied the wrong\n", fd); + bputs("credentials (e.g., bad password), or your\n", fd); + bputs("browser doesn't understand how to supply\n", fd); + bputs("the credentials required.<P>\n", fd); + break; + case BAD_REQUEST: + bputs("Your browser sent a request that\n", fd); + bputs("this server could not understand.<P>\n", fd); + break; + case FORBIDDEN: + bvputs(fd, "You don't have permission to access ", + escape_html(r->pool, r->uri), "\non this server.<P>\n", + NULL); + break; + case NOT_FOUND: + bvputs(fd, "The requested URL ", escape_html(r->pool, r->uri), + " was not found on this server.<P>\n", NULL); + break; + case METHOD_NOT_ALLOWED: + bvputs(fd, "The requested method ", r->method, " is not allowed " + "for the URL ", escape_html(r->pool, r->uri), + ".<P>\n", NULL); + break; + case NOT_ACCEPTABLE: + bvputs(fd, + "An appropriate representation of the requested resource ", + escape_html(r->pool, r->uri), + " could not be found on this server.<P>\n", NULL); + /* fall through */ + case MULTIPLE_CHOICES: + { + char *list; + if ((list = table_get (r->notes, "variant-list"))) + bputs(list, fd); + } + break; + case LENGTH_REQUIRED: + bvputs(fd, "A request of the requested method ", r->method, + " requires a valid Content-length.<P>\n", NULL); + break; + case PRECONDITION_FAILED: + bvputs(fd, "The precondition on the request for the URL ", + escape_html(r->pool, r->uri), " evaluated to false.<P>\n", + NULL); + break; + case NOT_IMPLEMENTED: + bvputs(fd, escape_html(r->pool, r->method), " to ", + escape_html(r->pool, r->uri), " not supported.<P>\n", NULL); + break; + case BAD_GATEWAY: + bputs("The proxy server received an invalid\015\012", fd); + bputs("response from an upstream server.<P>\015\012", fd); + break; + case VARIANT_ALSO_VARIES: + bvputs(fd, "A variant for the requested entity ", + escape_html(r->pool, r->uri), " is itself a ", + "transparently negotiable resource.<P>\n", NULL); + break; + case HTTP_REQUEST_TIME_OUT: + bputs("I'm tired of waiting for your request.\n", fd); + break; + case HTTP_GONE: + bvputs(fd, "The requested resource<BR>", + escape_html(r->pool, r->uri), + "<BR>\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: + bvputs(fd, "The requested resource<BR>", + escape_html(r->pool, r->uri), "<BR>\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: + bputs("The requested URL's length exceeds the capacity\n", fd); + bputs("limit for this server.\n", fd); + break; + case HTTP_UNSUPPORTED_MEDIA_TYPE: + bputs("The supplied request data is not in a format\n", fd); + bputs("acceptable for processing by this resource.\n", fd); + break; + case HTTP_SERVICE_UNAVAILABLE: + bputs("The server is temporarily unable to service your\n", fd); + bputs("request due to maintenance downtime or capacity\n", fd); + bputs("problems. Please try again later.\n", fd); + break; + case HTTP_GATEWAY_TIME_OUT: + bputs("The proxy server did not receive a timely response\n", fd); + bputs("from the upstream server.<P>\n", fd); + break; + default: /* HTTP_INTERNAL_SERVER_ERROR */ + bputs("The server encountered an internal error or\n", fd); + bputs("misconfiguration and was unable to complete\n", fd); + bputs("your request.<P>\n", fd); + bputs("Please contact the server administrator,\n ", fd); + bputs(escape_html(r->pool, r->server->server_admin), fd); + bputs(" and inform them of the time the error occurred,\n", fd); + bputs("and anything you might have done that may have\n", fd); + bputs("caused the error.<P>\n", fd); + break; + } + + if (recursive_error) { + bvputs(fd, "<P>Additionally, a ", + status_lines[index_of_response(recursive_error)], + "\nerror was encountered while trying to use an " + "ErrorDocument to handle the request.\n", NULL); + } + bputs("</BODY></HTML>\n", fd); + } + kill_timeout(r); + finalize_request_protocol(r); +} + +/* Finally, this... it's here to support nph- scripts + * Now what ever are we going to do about them when HTTP-NG packetization + * comes along? + */ + +void client_to_stdout (conn_rec *c) +{ + bflush(c->client); + dup2(c->client->fd, STDOUT_FILENO); +} diff --git a/usr.sbin/httpd/src/http_protocol.h b/usr.sbin/httpd/src/http_protocol.h new file mode 100644 index 00000000000..db608c253e9 --- /dev/null +++ b/usr.sbin/httpd/src/http_protocol.h @@ -0,0 +1,191 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * Prototypes for routines which either talk directly back to the user, + * or control the ones that eventually do. + */ + +/* Read a request and fill in the fields. */ + +request_rec *read_request (conn_rec *c); + +/* Send a single HTTP header field */ + +int send_header_field (request_rec *r, const char *fieldname, + const char *fieldval); + +/* Send the Status-Line and header fields for HTTP response */ + +void send_http_header (request_rec *l); + +/* Send the response to special method requests */ + +int send_http_trace (request_rec *r); +int send_http_options (request_rec *r); + +/* Finish up stuff after a request */ + +void finalize_request_protocol (request_rec *r); + +/* Send error back to client... last arg indicates error status in case + * we get an error in the process of trying to deal with an ErrorDocument + * to handle some other error. In that case, we print the default report + * for the first thing that went wrong, and more briefly report on the + * problem with the ErrorDocument. + */ + +void send_error_response (request_rec *r, int recursive_error); + +/* Set last modified header line from the lastmod date of the associated file. + * Also, set content length. + * + * May return an error status, typically USE_LOCAL_COPY (that when the + * permit_cache argument is set to one). + */ + +int set_content_length (request_rec *r, long length); +int set_keepalive (request_rec *r); +int set_last_modified (request_rec *r, time_t mtime); + +/* Other ways to send stuff at the client. All of these keep track + * of bytes_sent automatically. This indirection is intended to make + * it a little more painless to slide things like HTTP-NG packetization + * underneath the main body of the code later. In the meantime, it lets + * us centralize a bit of accounting (bytes_sent). + * + * These also return the number of bytes written by the call. + * They should only be called with a timeout registered, for obvious reaasons. + * (Ditto the send_header stuff). + */ + +long send_fd(FILE *f, request_rec *r); +long send_fd_length(FILE *f, request_rec *r, long length); + +/* Hmmm... could macrofy these for now, and maybe forever, though the + * definitions of the macros would get a whole lot hairier. + */ + +int rputc (int c, request_rec *r); +int rputs(const char *str, request_rec *r); +int rwrite(const void *buf, int nbyte, request_rec *r); +int rvputs(request_rec *r, ...); +int rprintf(request_rec *r,const char *fmt,...); +int rflush(request_rec *r); + +/* + * Index used in custom_responses array for a specific error code + * (only use outside protocol.c is in getting them configured). + */ + +int index_of_response (int status); + +/* Reading a block of data from the client connection (e.g., POST arg) */ + +int setup_client_block (request_rec *r, int read_policy); +int should_client_block (request_rec *r); +long get_client_block (request_rec *r, char *buffer, int bufsiz); +int discard_request_body (request_rec *r); + +/* Sending a byterange */ + +int set_byterange (request_rec *r); +int each_byterange (request_rec *r, long *offset, long *length); + +/* Finally, this charming little number is here to encapsulate the + * degree to which nph- scripts completely escape from any discipline + * the protocol code might care to impose (this as opposed to other + * scripts, which *partially* escape to the extent that they may try + * to explicitly set the status line). + */ + +void client_to_stdout (conn_rec *c); + + +/* Support for the Basic authentication protocol. Note that there's + * nothing that prevents these from being in mod_auth.c, except that other + * modules which wanted to provide their own variants on finding users and + * passwords for Basic auth (a fairly common request) would then require + * mod_auth to be loaded or they wouldn't work. + * + * get_basic_auth_pw returns 0 (OK) if it set the 'pw' argument (and assured + * a correct value in r->connection->user); otherwise it returns an error + * code, either SERVER_ERROR if things are really confused, AUTH_REQUIRED + * if no authentication at all seemed to be in use, or DECLINED if there + * was authentication but it wasn't Basic (in which case, the caller should + * presumably decline as well). + * + * note_basic_auth_failure arranges for the right stuff to be scribbled on + * the HTTP return so that the client knows how to authenticate itself the + * next time. As does note_digest_auth_failure for Digest auth. + * + * note_auth_failure does the same thing, but will call the correct one + * based on the authentication type in use. + * + */ + +void note_auth_failure(request_rec *r); +void note_basic_auth_failure(request_rec *r); +void note_digest_auth_failure(request_rec *r); +int get_basic_auth_pw (request_rec *r, char **pw); + +/* + * Setting up the protocol fields for subsidiary requests... + * Also, a wrapup function to keep the internal accounting straight. + */ + +void set_sub_req_protocol (request_rec *rnew, const request_rec *r); +void finalize_sub_req_protocol (request_rec *sub_r); + +/* This is also useful for putting sub_reqs and internal_redirects together */ + +void parse_uri (request_rec *r, const char *uri); diff --git a/usr.sbin/httpd/src/http_request.c b/usr.sbin/httpd/src/http_request.c new file mode 100644 index 00000000000..0ffa6ee6386 --- /dev/null +++ b/usr.sbin/httpd/src/http_request.c @@ -0,0 +1,1152 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * 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_log.h" +#include "http_main.h" +#include "scoreboard.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; + } + log_reason("object is not a file, directory or symlink", r->filename, r); + return HTTP_FORBIDDEN; +} + + +int check_symlinks (char *d, int opts) +{ + struct stat lfi, fi; + char *lastp; + int res; + +#ifdef __EMX__ + /* OS/2 dosen't have symlinks */ + return OK; +#else + + 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 + */ + +int get_path_info(request_rec *r) +{ + char *cp; + char *path = r->filename; + char *end = &path[strlen(path)]; + char *last_cp = NULL; + int rv; + + /* Advance over trailing slashes ... NOT part of filename */ + + for (cp = end; cp > path && cp[-1] == '/'; --cp) + continue; + + while (cp > path) { + + /* See if the pathname ending here exists... */ + + *cp = '\0'; + + 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 = pstrdup (r->pool, cp); + *cp = '\0'; + return OK; + } +#if defined(ENOENT) && defined(ENOTDIR) + else if (errno == ENOENT || errno == ENOTDIR) { +#else +#error ENOENT || ENOTDIR not defined -- check the comment below this line in the source for details + /* + * 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 line 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. + */ + + else { +#endif + last_cp = cp; + + while (--cp > path && *cp != '/') + continue; + + while (cp > path && cp[-1] == '/') + --cp; + } +#if defined(ENOENT) && defined(ENOTDIR) + else { +#if defined(EACCES) + if (errno != EACCES) +#endif + log_printf(r->server, + "access to %s failed for %s, reason: stat: %s (errno = %d)", + r->uri, get_remote_host(r->connection, r->per_dir_config, + REMOTE_NAME), strerror(errno), errno); + + return HTTP_FORBIDDEN; + } +#endif /* ENOENT && ENOTDIR */ + } + return OK; +} + +int directory_walk (request_rec *r) +{ + core_server_config *sconf = get_module_config (r->server->module_config, + &core_module); + array_header *sec_array = copy_array (r->pool, sconf->sec); + void *per_dir_defaults = r->server->lookup_defaults; + + core_dir_config **sec = (core_dir_config **)sec_array->elts; + int num_sec = sec_array->nelts; + char *test_filename = pstrdup (r->pool, r->filename); + + int num_dirs, res; + int i; + + /* 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 (test_filename == NULL) { + r->filename = 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. + */ + +#ifdef __EMX__ + /* Add OS/2 drive name support */ + if ((test_filename[0] != '/') && (test_filename[1] != ':')) +#else + if (test_filename[0] != '/') +#endif + { +/* fake filenames only match Directory sections */ + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_dir; + int j; + + for (j = 0; j < num_sec; ++j) { + + entry_config = sec[j]; + if (!entry_config) continue; + + entry_core =(core_dir_config *) + get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + this_conf = NULL; + if (entry_core->r) { + if (!regexec(entry_core->r, test_filename, 0, NULL, 0)) + this_conf = entry_config; + } + else if (entry_core->d_is_matchexp) { + if (!strcmp_match(test_filename, entry_dir)) + this_conf = entry_config; + } + else if (!strncmp (test_filename, entry_dir, strlen(entry_dir))) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = merge_per_dir_configs (r->pool, + per_dir_defaults, this_conf); + } + + r->per_dir_config = per_dir_defaults; + + return OK; + } + + no2slash (test_filename); + num_dirs = count_dirs(test_filename); + + res = get_path_info (r); + if (res != OK) { + return res; + } + + if ((res = check_safe_file(r))) { + return res; + } + + if (test_filename[strlen(test_filename)-1] == '/') + --num_dirs; + + if (S_ISDIR (r->finfo.st_mode)) { + ++num_dirs; + } + + for (i = 1; i <= num_dirs; ++i) { + core_dir_config *core_dir = + (core_dir_config *)get_module_config(per_dir_defaults, &core_module); + int overrides_here; + void *this_conf, *htaccess_conf = NULL; + char *this_dir = make_dirstr (r->pool, test_filename, i); + int j; + + /* Do symlink checks first, because they are done with the + * permissions appropriate to the *parent* directory... + */ + + if ((res = check_symlinks (this_dir, core_dir->opts))) + { + log_reason("Symbolic link not allowed", this_dir, r); + return res; + } + + /* Begin *this* level by looking for matching <Directory> sections from + * access.conf. + */ + + for (j = 0; j < num_sec; ++j) { + void *entry_config = sec[j]; + core_dir_config *entry_core; + char *entry_dir; + + if (!entry_config) continue; + + entry_core = + (core_dir_config *)get_module_config(entry_config, &core_module); + entry_dir = entry_core->d; + + this_conf = NULL; + if (entry_core->r) { + if (!regexec(entry_core->r, this_dir, 0, NULL, + (j == num_sec) ? 0 : REG_NOTEOL)) { + /* Don't try this wildcard again --- if it ends in '*' + * it'll match again, and subdirectories won't be able to + * override it... + */ + sec[j] = NULL; + this_conf = entry_config; + } + } + else if (entry_core->d_is_matchexp && + !strcmp_match(this_dir, entry_dir)) { + sec[j] = NULL; + this_conf = entry_config; + } + else if (!strcmp (this_dir, entry_dir)) + this_conf = entry_config; + + if (this_conf) { + per_dir_defaults = + merge_per_dir_configs (r->pool, per_dir_defaults, this_conf); + core_dir =(core_dir_config *)get_module_config(per_dir_defaults, + &core_module); + } + + } + + overrides_here = core_dir->override; + + /* If .htaccess files are enabled, check for one. + */ + + if (overrides_here) { + char *config_name = make_full_path(r->pool, this_dir, + sconf->access_name); + res = parse_htaccess (&htaccess_conf, r, overrides_here, + this_dir, config_name); + if (res) return res; + } + + if (htaccess_conf) + per_dir_defaults = + merge_per_dir_configs (r->pool, per_dir_defaults, + htaccess_conf); + + } + + 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, allow_options(r)))) { + log_reason("Symbolic link not allowed", r->filename, r); + return res; + } + + return OK; /* Can only "fail" if access denied + * by the symlink goop. + */ +} + +int location_walk (request_rec *r) +{ + core_server_config *sconf = get_module_config (r->server->module_config, + &core_module); + array_header *url_array = copy_array (r->pool, sconf->sec_url); + void *per_dir_defaults = r->per_dir_config; + + core_dir_config **url = (core_dir_config **)url_array->elts; + int len, num_url = url_array->nelts; + char *test_location = pstrdup (r->pool, r->uri); + + /* Collapse multiple slashes, if it's a path URL (we don't want to + * do anything to <Location http://...> or such). + */ + if (test_location[0] == '/') + no2slash (test_location); + + /* Go through the location entries, and check for matches. */ + + if (num_url) { + void *this_conf, *entry_config; + core_dir_config *entry_core; + char *entry_url; + int j; + +/* + * 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]; + if (!entry_config) continue; + + entry_core =(core_dir_config *) + get_module_config(entry_config, &core_module); + entry_url = entry_core->d; + + len = strlen(entry_url); + + this_conf = NULL; + + if (entry_core->r) { + if (!regexec(entry_core->r, test_location, 0, NULL, 0)) + this_conf = entry_config; + } + else if( entry_core->d_is_matchexp ) { + if (!strcmp_match(test_location, entry_url)) + 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 = merge_per_dir_configs (r->pool, + per_dir_defaults, this_conf); + } + + r->per_dir_config = per_dir_defaults; + } + + return OK; +} + +int file_walk (request_rec *r) +{ + core_dir_config *conf = get_module_config(r->per_dir_config, &core_module); + array_header *file_array = copy_array (r->pool, conf->sec); + void *per_dir_defaults = r->per_dir_config; + + core_dir_config **file = (core_dir_config **)file_array->elts; + int len, num_files = file_array->nelts; + char *test_file = pstrdup (r->pool, r->filename); + + /* Collapse multiple slashes */ + no2slash (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]; + if (!entry_config) continue; + + entry_core =(core_dir_config *) + get_module_config(entry_config, &core_module); + entry_file = entry_core->d; + + len = strlen(entry_file); + + this_conf = NULL; + + if (entry_core->r) { + if (!regexec(entry_core->r, test_file, 0, NULL, 0)) + this_conf = entry_config; + } + else if ( entry_core->d_is_matchexp ) { + if (!strcmp_match(test_file, entry_file)) + this_conf = entry_config; + } + else if (!strncmp (test_file, entry_file, len) && + (entry_file[len - 1] == '/' || + test_file[len] == '/' || test_file[len] == '\0')) + this_conf = entry_config; + + if (this_conf) + per_dir_defaults = 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... + */ + +request_rec *make_sub_request (const request_rec *r) +{ + pool *rrp = make_sub_pool (r->pool); + request_rec *rr = pcalloc (rrp, sizeof (request_rec)); + + rr->pool = rrp; + return rr; +} + + +request_rec *sub_req_lookup_uri (const char *new_file, const request_rec *r) +{ + request_rec *rnew; + int res; + char *udir; + + rnew = make_sub_request (r); + rnew->request_time = r->request_time; + rnew->connection = r->connection; + rnew->server = r->server; + rnew->request_config = create_request_config (rnew->pool); + rnew->htaccess = r->htaccess; /* copy htaccess cache */ + rnew->per_dir_config=r->server->lookup_defaults; + set_sub_req_protocol (rnew, r); + + if (new_file[0] == '/') + parse_uri(rnew, new_file); + else + { + udir = make_dirstr (rnew->pool, r->uri, count_dirs (r->uri)); + udir = escape_uri(rnew->pool, udir); /* re-escape it */ + parse_uri (rnew, make_full_path (rnew->pool, udir, new_file)); + } + + res = unescape_url (rnew->uri); + if (res) + { + rnew->status = res; + return rnew; + } + + getparents (rnew->uri); + + if ((res = location_walk (rnew))) { + rnew->status=res; + return rnew; + } + + res = 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)) + || ((satisfies(rnew)==SATISFY_ALL || satisfies(rnew)==SATISFY_NOSPEC)? + ((res = check_access (rnew)) + || (some_auth_required (rnew) && + ((res = check_user_id (rnew)) || (res = check_auth (rnew))))): + ((res = check_access (rnew)) + && (!some_auth_required (rnew) || + ((res = check_user_id (rnew)) || (res = check_auth (rnew))))) + ) + || (res = find_types (rnew)) + || (res = run_fixups (rnew)) + ) + { + rnew->status = res; + } + + return rnew; +} + +request_rec *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->request_time = r->request_time; + rnew->connection = r->connection; /* For now... */ + rnew->server = r->server; + rnew->request_config = create_request_config (rnew->pool); + rnew->htaccess = r->htaccess; /* copy htaccess cache */ + set_sub_req_protocol (rnew, r); + fdir = make_dirstr (rnew->pool, r->filename, count_dirs (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 = make_dirstr(rnew->pool, r->uri, count_dirs(r->uri)); + + rnew->uri = make_full_path (rnew->pool, udir, new_file); + rnew->filename = make_full_path (rnew->pool, fdir, new_file); + 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, allow_options (rnew)))) { + log_reason ("Symbolic link not allowed", rnew->filename, rnew); + 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 = find_types (rnew)) || (res = run_fixups (rnew))) { + rnew->status = res; + } + return rnew; + } + } + } else { + /* 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 = ((new_file[0] == '/') ? + pstrdup(rnew->pool,new_file) : + 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 + || ((satisfies(rnew)==SATISFY_ALL || satisfies(rnew)==SATISFY_NOSPEC)? + ((res = check_access (rnew)) + || (some_auth_required (rnew) && + ((res = check_user_id (rnew)) || (res = check_auth (rnew))))): + ((res = check_access (rnew)) + && (!some_auth_required (rnew) || + ((res = check_user_id (rnew)) || (res = check_auth (rnew))))) + ) + || (res = find_types (rnew)) + || (res = run_fixups (rnew)) + ) + { + rnew->status = res; + } + + return rnew; +} + +int run_sub_req (request_rec *r) +{ + int retval = invoke_handler (r); + finalize_sub_req_protocol (r); + return retval; +} + +void destroy_sub_req (request_rec *r) +{ + /* Reclaim the space */ + destroy_pool (r->pool); +} + +/***************************************************************** + * + * Mainline request processing... + */ + +void die(int type, request_rec *r) +{ + int error_index = index_of_response (type); + char *custom_response = response_code_string(r, error_index); + int recursive_error = 0; + + /* 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; + + /* + * 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) + && !status_drops_connection(r->status) + && r->connection && (r->connection->keepalive != -1)) { + (void) 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 (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; + table_set (r->headers_out, "Location", custom_response); + } else if ( custom_response[0] == '/') { + 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. + */ + table_set(r->subprocess_env, "REQUEST_METHOD", r->method); + r->method = pstrdup(r->pool, "GET"); + r->method_number = M_GET; + 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; + log_reason("Invalid error redirection directive", custom_response, + r); + } + } + + send_error_response (r, recursive_error); +} + +static void decl_die (int status, char *phase, request_rec *r) +{ + if (status == DECLINED) { + log_reason (pstrcat (r->pool, + "configuration error: couldn't ", + phase, NULL), + r->uri, + r); + die (SERVER_ERROR, r); + } + else die (status, r); +} + +int some_auth_required (request_rec *r) +{ + /* Is there a require line configured for the type of *this* req? */ + + array_header *reqs_arr = 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; +} + +void process_request_internal (request_rec *r) +{ + int access_status; + + /* Kludge to be reading the assbackwards field outside of protocol.c, + * but we've got to check for this sort of nonsense somewhere... + */ + + if (r->assbackwards && r->header_only) { + /* Client asked for headers only with HTTP/0.9, which doesn't + * send headers! Have to dink things even to make sure the + * error message comes through... + */ + log_reason ("client sent illegal HTTP/0.9 request", r->uri, r); + r->header_only = 0; + die (BAD_REQUEST, r); + return; + } + + if ((!r->hostname && (r->proto_num >= 1001)) || + ((r->proto_num == 1001) && !table_get(r->headers_in, "Host"))) { + /* Client sent us a 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. + */ + log_reason ("client sent HTTP/1.1 request without hostname (see RFC2068 sections 9 and 14.23)", + r->uri, r); + die (BAD_REQUEST, 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 = send_http_trace(r))) + die(access_status, r); + else + finalize_request_protocol(r); + return; + } + + access_status = unescape_url(r->uri); + if (access_status) + { + die(access_status, r); + return; + } + + getparents(r->uri); /* OK --- shrinking transformations... */ + } + + if ((access_status = location_walk (r))) { + die (access_status, r); + return; + } + + if ((access_status = translate_name (r))) { + decl_die (access_status, "translate", r); + return; + } + + if (r->proto_num > 1000 && table_get (r->subprocess_env, "downgrade-1.0")) { + r->proto_num = 1000; + } + + /* NB: directory_walk() clears the per_dir_config, so we don't inherit from + location_walk() above */ + + if ((access_status = directory_walk (r))) { + die (access_status, r); + return; + } + + if ((access_status = file_walk (r))) { + die (access_status, r); + return; + } + + if ((access_status = location_walk (r))) { + die (access_status, r); + return; + } + + if ((access_status = header_parse (r))) { + die (access_status, r); + return; + } + + switch (satisfies(r)) { + case SATISFY_ALL: case SATISFY_NOSPEC: + if ((access_status = check_access (r)) != 0) { + decl_die (access_status, "check access", r); + return; + } + if (some_auth_required (r)) { + if ((access_status = check_user_id (r)) != 0) { + decl_die (access_status, "check user. No user file?", r); + return; + } + if ((access_status = check_auth (r)) != 0) { + decl_die (access_status, "check access. No groups file?", r); + return; + } + } + break; + case SATISFY_ANY: + if ((access_status = check_access (r)) != 0) { + if (!some_auth_required (r)) { + decl_die (access_status, "check access", r); + return; + } + if ((access_status = check_user_id (r)) != 0) { + decl_die (access_status, "check user. No user file?", r); + return; + } + if ((access_status = check_auth (r)) != 0) { + decl_die (access_status, "check access. No groups file?", r); + return; + } + } + break; + } + + if ((access_status = find_types (r)) != 0) { + decl_die (access_status, "find types", r); + return; + } + + if ((access_status = run_fixups (r)) != 0) { + die (access_status, r); + return; + } + + if ((access_status = invoke_handler (r)) != 0) { + die (access_status, r); + return; + } + + /* Take care of little things that need to happen when we're done */ + finalize_request_protocol (r); +} + +void process_request (request_rec *r) +{ +#ifdef STATUS + int old_stat; +#endif /* STATUS */ + process_request_internal (r); +#ifdef STATUS + old_stat = update_child_status (r->connection->child_num, SERVER_BUSY_LOG, + r); +#endif /* STATUS */ + log_transaction (r); +#ifdef STATUS + (void)update_child_status (r->connection->child_num, old_stat, r); +#endif /* STATUS */ +} + +table *rename_original_env (pool *p, table *t) +{ + array_header *env_arr = table_elts (t); + table_entry *elts = (table_entry *)env_arr->elts; + table *new = make_table (p, env_arr->nelts); + int i; + + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) continue; + table_set (new, pstrcat (p, "REDIRECT_", elts[i].key, NULL), + elts[i].val); + } + + return new; +} + +request_rec *internal_internal_redirect (const char *new_uri, request_rec *r) +{ + request_rec *new = (request_rec *)pcalloc(r->pool, sizeof(request_rec)); + char t[256]; /* Long enough... */ + + new->connection = r->connection; + new->server = r->server; + new->pool = r->pool; + + /* A whole lot of this really ought to be shared with protocol.c... + * another missing cleanup. It's particularly inappropriate to be + * setting header_only, etc., here. + */ + + parse_uri (new, new_uri); + new->request_config = 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->method = r->method; + new->method_number = r->method_number; + 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->hostlen = r->hostlen; + new->request_time = r->request_time; + new->main = r->main; + + new->headers_in = r->headers_in; + new->headers_out = make_table (r->pool, 5); + new->err_headers_out = r->err_headers_out; + new->subprocess_env = rename_original_env (r->pool, r->subprocess_env); + new->notes = make_table (r->pool, 5); + new->htaccess = r->htaccess; /* copy .htaccess cache */ + + new->no_cache = r->no_cache; /* If we've already made up our minds + * about this, don't change 'em back! + */ + new->no_local_copy = r->no_local_copy; + + new->read_length = r->read_length; /* We can only read it once */ + + ap_snprintf (t, sizeof(t), "%d", r->status); + table_set (new->subprocess_env, "REDIRECT_STATUS", pstrdup (r->pool, t)); + + return new; +} + +void 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. + */ + +void 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); +} diff --git a/usr.sbin/httpd/src/http_request.h b/usr.sbin/httpd/src/http_request.h new file mode 100644 index 00000000000..f20d494cad9 --- /dev/null +++ b/usr.sbin/httpd/src/http_request.h @@ -0,0 +1,92 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* http_request.c is the code which handles the main line of request + * processing, once a request has been read in (finding the right per- + * directory configuration, building it if necessary, and calling all + * the module dispatch functions in the right order). + * + * The pieces here which are public to the modules, allow them to learn + * how the server would handle some other file or URI, or perhaps even + * direct the server to serve that other file instead of the one the + * client requested directly. + * + * There are two ways to do that. The first is the sub_request mechanism, + * which handles looking up files and URIs as adjuncts to some other + * request (e.g., directory entries for multiviews and directory listings); + * the lookup functions stop short of actually running the request, but + * (e.g., for includes), a module may call for the request to be run + * by calling run_sub_req. The space allocated to create sub_reqs can be + * reclaimed by calling destroy_sub_req --- be sure to copy anything you care + * about which was allocated in its pool elsewhere before doing this. + */ + +request_rec *sub_req_lookup_uri (const char *new_file, const request_rec *r); +request_rec *sub_req_lookup_file (const char *new_file, const request_rec *r); +int run_sub_req (request_rec *r); +void destroy_sub_req (request_rec *r); + +/* + * Then there's the case that you want some other request to be served + * as the top-level request INSTEAD of what the client requested directly. + * If so, call this from a handler, and then immediately return OK. + */ + +void internal_redirect (const char *new_uri, request_rec *); +void internal_redirect_handler (const char *new_uri, request_rec *); +int some_auth_required (request_rec *r); + +#ifdef CORE_PRIVATE +/* Function called by main.c to handle first-level request */ +void process_request (request_rec *); +int default_handler (request_rec *); +#endif diff --git a/usr.sbin/httpd/src/httpd.h b/usr.sbin/httpd/src/httpd.h new file mode 100644 index 00000000000..33bb73cd645 --- /dev/null +++ b/usr.sbin/httpd/src/httpd.h @@ -0,0 +1,766 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * httpd.h: header for simple (ha! not anymore) http daemon + */ + +/* Headers in which EVERYONE has an interest... */ + +#include "conf.h" +#include "alloc.h" +#include "buff.h" + +/* ----------------------------- config dir ------------------------------ */ + +/* Define this to be the default server home dir. Anything later in this + * file with a relative pathname will have this added. + */ +#ifndef HTTPD_ROOT +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define HTTPD_ROOT "/os2httpd" +#else +#define HTTPD_ROOT "/var/www" +#endif +#endif + +#ifndef DOCUMENT_LOCATION +/* Root of server */ +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DOCUMENT_LOCATION "/os2httpd/docs" +#else +#define DOCUMENT_LOCATION "/var/www/htdocs" +#endif +#endif + +/* Max. number of dynamically loaded modules */ +#define DYNAMIC_MODULE_LIMIT 64 + +/* Default administrator's address */ +#define DEFAULT_ADMIN "[no address given]" + +/* + * --------- You shouldn't have to edit anything below this line ---------- + * + * Any modifications to any defaults not defined above should be done in the + * respective config. file. + * + */ + + +/* -------------- Port number for server running standalone --------------- */ + +#define DEFAULT_PORT 80 + +/* --------- Default user name and group name running standalone ---------- */ +/* --- These may be specified as numbers by placing a # before a number --- */ + +#ifndef DEFAULT_USER +#define DEFAULT_USER "#-1" +#endif +#ifndef DEFAULT_GROUP +#define DEFAULT_GROUP "#-1" +#endif + +/* The name of the log files */ +#ifndef DEFAULT_XFERLOG +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DEFAULT_XFERLOG "logs/access.log" +#else +#define DEFAULT_XFERLOG "logs/access_log" +#endif +#endif /* DEFAULT_XFERLOG */ +#ifndef DEFAULT_ERRORLOG +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DEFAULT_ERRORLOG "logs/error.log" +#else +#define DEFAULT_ERRORLOG "logs/error_log" +#endif +#endif /* DEFAULT_ERRORLOG */ +#ifndef DEFAULT_PIDLOG +#define DEFAULT_PIDLOG "logs/httpd.pid" +#endif +#ifndef DEFAULT_SCOREBOARD +#define DEFAULT_SCOREBOARD "logs/apache_runtime_status" +#endif +#ifndef DEFAULT_LOCKFILE +#define DEFAULT_LOCKFILE "logs/accept.lock" +#endif + +/* Define this to be what your HTML directory content files are called */ +#define DEFAULT_INDEX "index.html" + +/* Define this to 1 if you want fancy indexing, 0 otherwise */ +#define DEFAULT_INDEXING 0 + +/* Define this to be what type you'd like returned for files with unknown */ +/* suffixes */ +#define DEFAULT_TYPE "text/plain" + +/* Define this to be what your per-directory security files are called */ +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define DEFAULT_ACCESS_FNAME "htaccess" +#else +#define DEFAULT_ACCESS_FNAME ".htaccess" +#endif + +/* The name of the server config file */ +#ifndef SERVER_CONFIG_FILE +#define SERVER_CONFIG_FILE "conf/httpd.conf" +#endif + +#ifndef RESOURCE_CONFIG_FILE +/* The name of the document config file */ +#define RESOURCE_CONFIG_FILE "conf/srm.conf" +#endif + +#ifndef TYPES_CONFIG_FILE +/* The name of the MIME types file */ +#define TYPES_CONFIG_FILE "conf/mime.types" +#endif + +#ifndef ACCESS_CONFIG_FILE +/* The name of the access file */ +#define ACCESS_CONFIG_FILE "conf/access.conf" +#endif + +/* Whether we should enable rfc1413 identity checking */ +#define DEFAULT_RFC1413 0 +/* The default directory in user's home dir */ +#define DEFAULT_USER_DIR "public_html" + +/* The default path for CGI scripts if none is currently set */ +#ifndef DEFAULT_PATH +#define DEFAULT_PATH "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin" +#endif + +/* The path to the Bourne shell, for parsed docs */ +#ifndef SHELL_PATH +#ifdef __EMX__ +/* Set default for OS/2 file system */ +#define SHELL_PATH "CMD.EXE" +#else +#define SHELL_PATH "/bin/sh" +#endif +#endif + +/* The path to the suExec wrapper, can be overridden in Configuration */ +#ifndef SUEXEC_BIN +#define SUEXEC_BIN "/usr/local/etc/httpd/sbin/suexec" +#endif + +/* The default string lengths */ +#define MAX_STRING_LEN HUGE_STRING_LEN +#define HUGE_STRING_LEN 8192 + +/* The timeout for waiting for messages */ +#define DEFAULT_TIMEOUT 300 + +/* The timeout for waiting for keepalive timeout until next request */ +#define DEFAULT_KEEPALIVE_TIMEOUT 15 + +/* The number of requests to entertain per connection */ +#define DEFAULT_KEEPALIVE 100 + +/* The size of the server's internal read-write buffers */ +#define IOBUFSIZE 8192 + +/* Number of servers to spawn off by default --- also, if fewer than + * this free when the caretaker checks, it will spawn more. + */ +#define DEFAULT_START_DAEMON 5 + +/* Maximum number of *free* server processes --- more than this, and + * they will die off. + */ + +#define DEFAULT_MAX_FREE_DAEMON 10 + +/* Minimum --- fewer than this, and more will be created */ + +#define DEFAULT_MIN_FREE_DAEMON 5 + +/* Limit on the total --- clients will be locked out if more servers than + * this are needed. It is intended solely to keep the server from crashing + * when things get out of hand. + * + * We keep a hard maximum number of servers, for two reasons --- first off, + * in case something goes seriously wrong, we want to stop the fork bomb + * short of actually crashing the machine we're running on by filling some + * kernel table. Secondly, it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifndef HARD_SERVER_LIMIT +#define HARD_SERVER_LIMIT 256 +#endif + +/* Number of requests to try to handle in a single process. If <= 0, + * the children don't die off. That's the default here, since I'm still + * interested in finding and stanching leaks. + */ + +#define DEFAULT_MAX_REQUESTS_PER_CHILD 0 + +/* If you have altered Apache and wish to change the SERVER_VERSION + * identifier below, please keep to the HTTP specification. This states that + * the identification string should consist of product tokens with an optional + * slash and version designator. Sub-products which form a significant part + * of the application can be listed, separated by whitespace, by adding + * their product tokens to EXTRA_CFLAGS in the Configuration file like so. + * + * EXTRA_CFLAGS="-DSERVER_SUBVERSION="MrWidget/0.1-alpha" + * + * The tokens are listed in order of their significance for identifying the + * application. + * + * "Product tokens should be short and to the point -- use of them for + * advertizing or other non-essential information is explicitly forbidden." + * + * Example: "Apache/1.1.0 MrWidget/0.1-alpha" + */ + +#define SERVER_BASEVERSION "Apache/1.2.6" /* SEE COMMENTS ABOVE */ +#ifdef SERVER_SUBVERSION +#define SERVER_VERSION SERVER_BASEVERSION " " SERVER_SUBVERSION +#else +#define SERVER_VERSION SERVER_BASEVERSION +#endif + +/* Numeric release version identifier: major minor bugfix betaseq + * Always increases along the same track as the source branch. + */ +#define APACHE_RELEASE 1020600 + +#define SERVER_PROTOCOL "HTTP/1.1" +#define SERVER_SUPPORT "http://www.apache.org/" + +#define DECLINED -1 /* Module declines to handle */ +#define OK 0 /* Module has handled this stage. */ + + +/* ----------------------- HTTP Status Codes ------------------------- */ + +#define RESPONSE_CODES 38 + +#define HTTP_CONTINUE 100 +#define HTTP_SWITCHING_PROTOCOLS 101 +#define HTTP_OK 200 +#define HTTP_CREATED 201 +#define HTTP_ACCEPTED 202 +#define HTTP_NON_AUTHORITATIVE 203 +#define HTTP_NO_CONTENT 204 +#define HTTP_RESET_CONTENT 205 +#define HTTP_PARTIAL_CONTENT 206 +#define HTTP_MULTIPLE_CHOICES 300 +#define HTTP_MOVED_PERMANENTLY 301 +#define HTTP_MOVED_TEMPORARILY 302 +#define HTTP_SEE_OTHER 303 +#define HTTP_NOT_MODIFIED 304 +#define HTTP_USE_PROXY 305 +#define HTTP_BAD_REQUEST 400 +#define HTTP_UNAUTHORIZED 401 +#define HTTP_PAYMENT_REQUIRED 402 +#define HTTP_FORBIDDEN 403 +#define HTTP_NOT_FOUND 404 +#define HTTP_METHOD_NOT_ALLOWED 405 +#define HTTP_NOT_ACCEPTABLE 406 +#define HTTP_PROXY_AUTHENTICATION_REQUIRED 407 +#define HTTP_REQUEST_TIME_OUT 408 +#define HTTP_CONFLICT 409 +#define HTTP_GONE 410 +#define HTTP_LENGTH_REQUIRED 411 +#define HTTP_PRECONDITION_FAILED 412 +#define HTTP_REQUEST_ENTITY_TOO_LARGE 413 +#define HTTP_REQUEST_URI_TOO_LARGE 414 +#define HTTP_UNSUPPORTED_MEDIA_TYPE 415 +#define HTTP_INTERNAL_SERVER_ERROR 500 +#define HTTP_NOT_IMPLEMENTED 501 +#define HTTP_BAD_GATEWAY 502 +#define HTTP_SERVICE_UNAVAILABLE 503 +#define HTTP_GATEWAY_TIME_OUT 504 +#define HTTP_VERSION_NOT_SUPPORTED 505 +#define HTTP_VARIANT_ALSO_VARIES 506 + +#define DOCUMENT_FOLLOWS HTTP_OK +#define PARTIAL_CONTENT HTTP_PARTIAL_CONTENT +#define MULTIPLE_CHOICES HTTP_MULTIPLE_CHOICES +#define MOVED HTTP_MOVED_PERMANENTLY +#define REDIRECT HTTP_MOVED_TEMPORARILY +#define USE_LOCAL_COPY HTTP_NOT_MODIFIED +#define BAD_REQUEST HTTP_BAD_REQUEST +#define AUTH_REQUIRED HTTP_UNAUTHORIZED +#define FORBIDDEN HTTP_FORBIDDEN +#define NOT_FOUND HTTP_NOT_FOUND +#define METHOD_NOT_ALLOWED HTTP_METHOD_NOT_ALLOWED +#define NOT_ACCEPTABLE HTTP_NOT_ACCEPTABLE +#define LENGTH_REQUIRED HTTP_LENGTH_REQUIRED +#define PRECONDITION_FAILED HTTP_PRECONDITION_FAILED +#define SERVER_ERROR HTTP_INTERNAL_SERVER_ERROR +#define NOT_IMPLEMENTED HTTP_NOT_IMPLEMENTED +#define BAD_GATEWAY HTTP_BAD_GATEWAY +#define VARIANT_ALSO_VARIES HTTP_VARIANT_ALSO_VARIES + +#define is_HTTP_INFO(x) (((x) >= 100)&&((x) < 200)) +#define is_HTTP_SUCCESS(x) (((x) >= 200)&&((x) < 300)) +#define is_HTTP_REDIRECT(x) (((x) >= 300)&&((x) < 400)) +#define is_HTTP_ERROR(x) (((x) >= 400)&&((x) < 600)) +#define is_HTTP_CLIENT_ERROR(x) (((x) >= 400)&&((x) < 500)) +#define is_HTTP_SERVER_ERROR(x) (((x) >= 500)&&((x) < 600)) + +#define status_drops_connection(x) (((x) == HTTP_BAD_REQUEST) || \ + ((x) == HTTP_REQUEST_TIME_OUT) || \ + ((x) == HTTP_LENGTH_REQUIRED) || \ + ((x) == HTTP_REQUEST_ENTITY_TOO_LARGE) || \ + ((x) == HTTP_REQUEST_URI_TOO_LARGE) || \ + ((x) == HTTP_INTERNAL_SERVER_ERROR) || \ + ((x) == HTTP_SERVICE_UNAVAILABLE)) + + +#define METHODS 8 +#define M_GET 0 +#define M_PUT 1 +#define M_POST 2 +#define M_DELETE 3 +#define M_CONNECT 4 +#define M_OPTIONS 5 +#define M_TRACE 6 +#define M_INVALID 7 + +#define CGI_MAGIC_TYPE "application/x-httpd-cgi" +#define INCLUDES_MAGIC_TYPE "text/x-server-parsed-html" +#define INCLUDES_MAGIC_TYPE3 "text/x-server-parsed-html3" +#define MAP_FILE_MAGIC_TYPE "application/x-type-map" +#define ASIS_MAGIC_TYPE "httpd/send-as-is" +#define DIR_MAGIC_TYPE "httpd/unix-directory" +#define STATUS_MAGIC_TYPE "application/x-httpd-status" + +/* Just in case your linefeed isn't the one the other end is expecting. */ +#define LF 10 +#define CR 13 + +/* Possible values for request_rec.read_body (set by handling module): + * 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. + */ +#define REQUEST_NO_BODY 0 +#define REQUEST_CHUNKED_ERROR 1 +#define REQUEST_CHUNKED_DECHUNK 2 +#define REQUEST_CHUNKED_PASS 3 + +/* Things which may vary per file-lookup WITHIN a request --- + * e.g., state of MIME config. Basically, the name of an object, info + * about the object, and any other info we may ahve which may need to + * change as we go poking around looking for it (e.g., overridden by + * .htaccess files). + * + * Note how the default state of almost all these things is properly + * zero, so that allocating it with pcalloc does the right thing without + * a whole lot of hairy initialization... so long as we are willing to + * make the (fairly) portable assumption that the bit pattern of a NULL + * pointer is, in fact, zero. + */ + +/* This represents the result of calling htaccess; these are cached for + * each request. + */ +struct htaccess_result +{ + char *dir; /* the directory to which this applies */ + int override; /* the overrides allowed for the .htaccess file */ + void *htaccess; /* the configuration directives */ +/* the next one, or NULL if no more; N.B. never change this */ + const struct htaccess_result *next; +}; + + +typedef struct conn_rec conn_rec; +typedef struct server_rec server_rec; +typedef struct request_rec request_rec; +typedef struct listen_rec listen_rec; + +struct request_rec { + + pool *pool; + conn_rec *connection; + server_rec *server; + + request_rec *next; /* If we wind up getting redirected, + * pointer to the request we redirected to. + */ + request_rec *prev; /* If this is an internal redirect, + * pointer to where we redirected *from*. + */ + + request_rec *main; /* If this is a sub_request (see request.h) + * pointer back to the main request. + */ + + /* Info about the request itself... we begin with stuff that only + * protocol.c should ever touch... + */ + + char *the_request; /* First line of request, so we can log it */ + int assbackwards; /* HTTP/0.9, "simple" request */ + int proxyreq; /* A proxy request */ + int header_only; /* HEAD request, as opposed to GET */ + char *protocol; /* Protocol, as given to us, or HTTP/0.9 */ + int proto_num; /* Number version of protocol; 1.1 = 1001 */ + char *hostname; /* Host, as set by full URI or Host: */ + int hostlen; /* Length of http://host:port in full URI */ + + time_t request_time; /* When the request started */ + + char *status_line; /* Status line, if set by script */ + int status; /* In any case */ + + /* Request method, two ways; also, protocol, etc.. Outside of protocol.c, + * look, but don't touch. + */ + + char *method; /* GET, HEAD, POST, etc. */ + int method_number; /* M_GET, M_POST, etc. */ + int allowed; /* Allowed methods - for 405, OPTIONS, etc */ + + int sent_bodyct; /* byte count in stream is for body */ + long bytes_sent; /* body byte count, for easy access */ + + /* HTTP/1.1 connection-level features */ + + int chunked; /* sending chunked transfer-coding */ + int byterange; /* number of byte ranges */ + char *boundary; /* multipart/byteranges boundary */ + char *range; /* The Range: header */ + long clength; /* The "real" content length */ + + long remaining; /* bytes left to read */ + long read_length; /* bytes that have been read */ + int read_body; /* how the request body should be read */ + int read_chunked; /* reading chunked transfer-coding */ + + /* MIME header environments, in and out. Also, an array containing + * environment variables to be passed to subprocesses, so people can + * write modules to add to that environment. + * + * The difference between headers_out and err_headers_out is that the + * latter are printed even on error, and persist across internal redirects + * (so the headers printed for ErrorDocument handlers will have them). + * + * The 'notes' table is for notes from one module to another, with no + * other set purpose in mind... + */ + + table *headers_in; + table *headers_out; + table *err_headers_out; + table *subprocess_env; + table *notes; + + char *content_type; /* Break these out --- we dispatch on 'em */ + char *handler; /* What we *really* dispatch on */ + + char *content_encoding; + char *content_language; /* for back-compat. only -- do not use */ + array_header *content_languages; /* array of (char*) */ + + int no_cache; + int no_local_copy; + + /* What object is being requested (either directly, or via include + * or content-negotiation mapping). + */ + + char *uri; /* complete URI for a proxy req, or + URL path for a non-proxy req */ + char *filename; + char *path_info; + char *args; /* QUERY_ARGS, if any */ + struct stat finfo; /* ST_MODE set to zero if no such file */ + + /* Various other config info which may change with .htaccess files + * These are config vectors, with one void* pointer for each module + * (the thing pointed to being the module's business). + */ + + void *per_dir_config; /* Options set in config files, etc. */ + void *request_config; /* Notes on *this* request */ + +/* + * a linked list of the configuration directives in the .htaccess files + * accessed by this request. + * N.B. always add to the head of the list, _never_ to the end. + * that way, a sub request's list can (temporarily) point to a parent's list + */ + const struct htaccess_result *htaccess; +}; + + +/* Things which are per connection + */ + +struct conn_rec { + + pool *pool; + server_rec *server; + server_rec *base_server; /* Physical vhost this conn come in on */ + + /* Information about the connection itself */ + + int child_num; /* The number of the child handling conn_rec */ + BUFF *client; /* Connetion to the guy */ + int aborted; /* Are we still talking? */ + + /* Who is the client? */ + + struct sockaddr_in local_addr; /* local address */ + struct sockaddr_in remote_addr;/* remote address */ + char *remote_ip; /* Client's IP address */ + char *remote_host; /* Client's DNS name, if known. + * NULL if DNS hasn't been checked, + * "" if it has and no address was found. + * N.B. Only access this though + * get_remote_host() */ + char *remote_logname; /* Only ever set if doing rfc1413 lookups. + * N.B. Only access this through + * get_remote_logname() */ + char *user; /* If an authentication check was made, + * this gets set to the user name. We assume + * that there's only one user per connection(!) + */ + char *auth_type; /* Ditto. */ + + int keepalive; /* Are we using HTTP Keep-Alive? */ + int keptalive; /* Did we use HTTP Keep-Alive? */ + int keepalives; /* How many times have we used it? */ +}; + +/* Per-vhost config... */ + +/* The address 255.255.255.255, when used as a virtualhost address, + * will become the "default" server when the ip doesn't match other vhosts. + */ +#define DEFAULT_VHOST_ADDR 0xfffffffful + +typedef struct server_addr_rec server_addr_rec; +struct server_addr_rec { + server_addr_rec *next; + struct in_addr host_addr; /* The bound address, for this server */ + unsigned short host_port; /* The bound port, for this server */ + char *virthost; /* The name given in <VirtualHost> */ +}; + + +struct server_rec { + + server_rec *next; + + /* Full locations of server config info */ + + char *srm_confname; + char *access_confname; + + /* Contact information */ + + char *server_admin; + char *server_hostname; + unsigned short port; /* for redirects, etc. */ + + /* Log files --- note that transfer log is now in the modules... */ + + char *error_fname; + FILE *error_log; + + /* Module-specific configuration for server, and defaults... */ + + int is_virtual; /* true if this is the virtual server */ + void *module_config; /* Config vector containing pointers to + * modules' per-server config structures. + */ + void *lookup_defaults; /* MIME type info, etc., before we start + * checking per-directory info. + */ + /* Transaction handling */ + + server_addr_rec *addrs; + int timeout; /* Timeout, in seconds, before we give up */ + int keep_alive_timeout; /* Seconds we'll wait for another request */ + int keep_alive_max; /* Maximum requests per connection */ + int keep_alive; /* Use persistent connections? */ + int send_buffer_size; /* size of TCP send buffer (in bytes) */ + + char *path; /* Pathname for ServerPath */ + int pathlen; /* Length of path */ + + char *names; /* Wildcarded names for ServerAlias servers */ + + uid_t server_uid; /* effective user id when calling exec wrapper */ + gid_t server_gid; /* effective group id when calling exec wrapper */ +}; + +/* These are more like real hosts than virtual hosts */ +struct listen_rec { + listen_rec *next; + struct sockaddr_in local_addr; /* local IP address and port */ + int fd; + int used; /* Only used during restart */ +/* more stuff here, like which protocol is bound to the port */ +}; + +/* Prototypes for utilities... util.c. + */ + +/* Time */ +extern const char month_snames[12][4]; + +struct tm *get_gmtoff(int *tz); +char *get_time(); +char *ht_time (pool *p, time_t t, const char *fmt, int gmt); +char *gm_timestr_822(pool *p, time_t t); + +/* String handling. The *_nc variants allow you to use non-const char **s as +arguments (unfortunately C won't automatically convert a char ** to a const +char **) */ + +char *getword(pool *p, const char **line, char stop); +char *getword_nc(pool *p, char **line, char stop); +char *getword_white(pool *p, const char **line); +char *getword_white_nc(pool *p, char **line); +char *getword_nulls (pool *p, const char **line, char stop); +char *getword_nulls_nc (pool *p, char **line, char stop); +char *getword_conf (pool *p, const char **line); +char *getword_conf_nc (pool *p, char **line); + +char *get_token (pool *p, char **accept_line, int accept_white); +int find_token (pool *p, const char *line, const char *tok); +int find_last_token (pool *p, const char *line, const char *tok); + +int is_url(const char *u); +extern int unescape_url(char *url); +void no2slash(char *name); +void getparents(char *name); +char *escape_path_segment(pool *p, const char *s); +char *os_escape_path(pool *p,const char *path,int partial); +#define escape_uri(ppool,path) os_escape_path(ppool,path,1) +extern char *escape_html(pool *p, const char *s); +char *construct_server(pool *p, const char *hostname, unsigned port); +char *construct_url (pool *p, const char *path, const server_rec *s); +char *escape_shell_cmd (pool *p, const char *s); + +int count_dirs(const char *path); +char *make_dirstr(pool *a, const char *s, int n); +char *make_full_path(pool *a, const char *dir, const char *f); + +int is_matchexp(const char *str); +int strcmp_match(const char *str, const char *exp); +int strcasecmp_match(const char *str, const char *exp); +char *uudecode (pool *, const char *); + +char *pregsub(pool *p, const char *input, const char *source, + size_t nmatch, regmatch_t pmatch[]); + +void str_tolower (char *); +int ind (const char *, char); /* Sigh... */ +int rind (const char *, char); + +int cfg_getline(char *s, int n, FILE *f); + +#ifdef NEED_STRERROR +char *strerror (int err); +#endif + +/* Misc system hackery */ + +uid_t uname2id(const char *name); +gid_t gname2id(const char *name); +int is_directory(const char *name); +int can_exec(const struct stat *); +void chdir_file(const char *file); + +char *get_local_host(pool *); +unsigned long get_virthost_addr (const char *hostname, unsigned short *port); + +extern time_t restart_time; + +/* + * Apache tries to keep all of its long term filehandles (such as log files, + * and sockets) above this number. This is to workaround problems in many + * third party libraries that are compiled with a small FD_SETSIZE. There + * should be no reason to lower this, because it's only advisory. If a file + * can't be allocated above this number then it will remain in the "slack" + * area. + * + * Only the low slack line is used by default. If HIGH_SLACK_LINE is defined + * then an attempt is also made to keep all non-FILE * files above the high + * slack line. This is to work around a Solaris C library limitation, where it + * uses an unsigned char to store the file descriptor. + */ +#ifndef LOW_SLACK_LINE +#define LOW_SLACK_LINE 15 +#endif +/* #define HIGH_SLACK_LINE 255 */ + +/* + * The ap_slack() function takes a fd, and tries to move it above the indicated + * line. It returns an fd which may or may not have moved above the line, and + * never fails. If the high line was requested and it fails it will also try + * the low line. + */ +int ap_slack (int fd, int line); +#define AP_SLACK_LOW 1 +#define AP_SLACK_HIGH 2 diff --git a/usr.sbin/httpd/src/md5.h b/usr.sbin/httpd/src/md5.h new file mode 100644 index 00000000000..a8ff86c4bd0 --- /dev/null +++ b/usr.sbin/httpd/src/md5.h @@ -0,0 +1,99 @@ +/* + * This is work is derived from material Copyright RSA Data Security, Inc. + * + * The RSA copyright statement and Licence for that original material is + * included below. This is followed by the Apache copyright statement and + * licence for the modifications made to that material. + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + + +/* MD5.H - header file for MD5C.C */ + +/* UINT4 defines a four byte word */ +typedef unsigned int UINT4; + +/* MD5 context. */ +typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +extern void MD5Init(MD5_CTX *context); +extern void MD5Update(MD5_CTX *context, const unsigned char *input, + unsigned int inputLen); +extern void MD5Final(unsigned char digest[16], MD5_CTX *context); diff --git a/usr.sbin/httpd/src/md5c.c b/usr.sbin/httpd/src/md5c.c new file mode 100644 index 00000000000..fd42bcb456e --- /dev/null +++ b/usr.sbin/httpd/src/md5c.c @@ -0,0 +1,354 @@ +/* + * This is work is derived from material Copyright RSA Data Security, Inc. + * + * The RSA copyright statement and Licence for that original material is + * included below. This is followed by the Apache copyright statement and + * licence for the modifications made to that material. + */ + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +#include <string.h> + +#include "md5.h" + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4 state[4], const unsigned char block[64]); +static void Encode(unsigned char *output, const UINT4 *input, + unsigned int len); +static void Decode(UINT4 *output, const unsigned char *input, + unsigned int len); + +static unsigned char PADDING[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +MD5Init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void +MD5Update(MD5_CTX *context, const unsigned char *input, unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += (UINT4)inputLen >> 29; + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) + { + memcpy(&context->buffer[index], input, partLen); + MD5Transform(context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform(context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy(&context->buffer[index], &input[i], inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void +MD5Final(unsigned char digest[16], MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset(context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ +static void +MD5Transform(UINT4 state[4], const unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset(x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void +Encode(unsigned char *output, const UINT4 *input, unsigned int len) +{ + unsigned int i, j; + UINT4 k; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + k = input[i]; + output[j] = (unsigned char)(k & 0xff); + output[j+1] = (unsigned char)((k >> 8) & 0xff); + output[j+2] = (unsigned char)((k >> 16) & 0xff); + output[j+3] = (unsigned char)((k >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void +Decode(UINT4 *output, const unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} diff --git a/usr.sbin/httpd/src/mod_access.c b/usr.sbin/httpd/src/mod_access.c new file mode 100644 index 00000000000..eb352380123 --- /dev/null +++ b/usr.sbin/httpd/src/mod_access.c @@ -0,0 +1,283 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * Security options etc. + * + * Module derived from code originally written by Rob McCool + * + */ + +#include "httpd.h" +#include "http_core.h" +#include "http_config.h" +#include "http_log.h" +#include "http_request.h" + +typedef struct { + char *from; + int limited; +} allowdeny; + +/* things in the 'order' array */ +#define DENY_THEN_ALLOW 0 +#define ALLOW_THEN_DENY 1 +#define MUTUAL_FAILURE 2 + +typedef struct { + int order[METHODS]; + array_header *allows; + array_header *denys; +} access_dir_conf; + +module access_module; + +void *create_access_dir_config (pool *p, char *dummy) +{ + access_dir_conf *conf = + (access_dir_conf *)pcalloc(p, sizeof(access_dir_conf)); + int i; + + for (i = 0; i < METHODS; ++i) conf->order[i] = DENY_THEN_ALLOW; + conf->allows = make_array (p, 1, sizeof (allowdeny)); + conf->denys = make_array (p, 1, sizeof (allowdeny)); + + return (void *)conf; +} + +const char *order (cmd_parms *cmd, void *dv, char *arg) +{ + access_dir_conf *d = (access_dir_conf *)dv; + int i, order; + + if (!strcasecmp (arg, "allow,deny")) order = ALLOW_THEN_DENY; + else if (!strcasecmp (arg, "deny,allow")) order = DENY_THEN_ALLOW; + else if (!strcasecmp (arg, "mutual-failure")) order = MUTUAL_FAILURE; + else return "unknown order"; + + for (i = 0; i < METHODS; ++i) + if (cmd->limited & (1 << i)) + d->order[i] = order; + + return NULL; +} + +const char *allow_cmd (cmd_parms *cmd, void *dv, char *from, char *where) +{ + access_dir_conf *d = (access_dir_conf *)dv; + allowdeny *a; + + if (strcasecmp (from, "from")) + return "allow and deny must be followed by 'from'"; + + a = (allowdeny *)push_array (cmd->info ? d->allows : d->denys); + a->from = pstrdup (cmd->pool, where); + a->limited = cmd->limited; + return NULL; +} + +static char its_an_allow; + +command_rec access_cmds[] = { +{ "order", order, NULL, OR_LIMIT, TAKE1, + "'allow,deny', 'deny,allow', or 'mutual-failure'" }, +{ "allow", allow_cmd, &its_an_allow, OR_LIMIT, ITERATE2, + "'from' followed by hostnames or IP-address wildcards" }, +{ "deny", allow_cmd, NULL, OR_LIMIT, ITERATE2, + "'from' followed by hostnames or IP-address wildcards" }, +{NULL} +}; + +int in_domain(const char *domain, const char *what) { + int dl=strlen(domain); + int wl=strlen(what); + + if((wl-dl) >= 0) { + if (strcasecmp(domain,&what[wl-dl]) != 0) return 0; + + /* Make sure we matched an *entire* subdomain --- if the user + * said 'allow from good.com', we don't want people from nogood.com + * to be able to get in. + */ + + if (wl == dl) return 1; /* matched whole thing */ + else return (domain[0] == '.' || what[wl - dl - 1] == '.'); + } else + return 0; +} + +int in_ip(char *domain, char *what) { + + /* Check a similar screw case to the one checked above --- + * "allow from 204.26.2" shouldn't let in people from 204.26.23 + */ + + int l = strlen(domain); + if (strncmp(domain,what,l) != 0) return 0; + if (domain[l - 1] == '.') return 1; + return (what[l] == '\0' || what[l] == '.'); +} + +static int is_ip(const char *host) +{ + while ((*host == '.') || isdigit(*host)) + host++; + return (*host == '\0'); +} + +int find_allowdeny (request_rec *r, array_header *a, int method) +{ + allowdeny *ap = (allowdeny *)a->elts; + int mmask = (1 << method); + int i; + int gothost = 0; + const char *remotehost = NULL; + + for (i = 0; i < a->nelts; ++i) { + if (!(mmask & ap[i].limited)) + continue; + + if (!strncmp(ap[i].from,"env=",4) && table_get(r->subprocess_env,ap[i].from+4)) + return 1; + + if (ap[i].from && !strcmp(ap[i].from, "user-agents")) { + char * this_agent = table_get(r->headers_in, "User-Agent"); + int j; + + if (!this_agent) return 0; + + for (j = i+1; j < a->nelts; ++j) { + if (strstr(this_agent, ap[j].from)) return 1; + } + return 0; + } + + if (!strcmp (ap[i].from, "all")) + return 1; + + if (!gothost) { + remotehost = get_remote_host(r->connection, r->per_dir_config, + REMOTE_HOST); + + if ((remotehost == NULL) || is_ip(remotehost)) + gothost = 1; + else + gothost = 2; + } + + if ((gothost == 2) && in_domain(ap[i].from, remotehost)) + return 1; + + if (in_ip (ap[i].from, r->connection->remote_ip)) + return 1; + } + + return 0; +} + +int check_dir_access (request_rec *r) +{ + int method = r->method_number; + access_dir_conf *a = + (access_dir_conf *) + get_module_config (r->per_dir_config, &access_module); + int ret = OK; + + if (a->order[method] == ALLOW_THEN_DENY) { + ret = FORBIDDEN; + if (find_allowdeny (r, a->allows, method)) + ret = OK; + if (find_allowdeny (r, a->denys, method)) + ret = FORBIDDEN; + } else if (a->order[method] == DENY_THEN_ALLOW) { + if (find_allowdeny (r, a->denys, method)) + ret = FORBIDDEN; + if (find_allowdeny (r, a->allows, method)) + ret = OK; + } + else { + if (find_allowdeny(r, a->allows, method) + && !find_allowdeny(r, a->denys, method)) + ret = OK; + else + ret = FORBIDDEN; + } + + if (ret == FORBIDDEN && ( + satisfies(r) != SATISFY_ANY || !some_auth_required(r) + )) { + log_reason ("Client denied by server configuration", r->filename, r); + } + + return ret; +} + + + +module access_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_access_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + access_cmds, + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + check_dir_access, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_actions.c b/usr.sbin/httpd/src/mod_actions.c new file mode 100644 index 00000000000..570147069ed --- /dev/null +++ b/usr.sbin/httpd/src/mod_actions.c @@ -0,0 +1,219 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * mod_actions.c: executes scripts based on MIME type + * + * by Alexei Kosut; based on mod_cgi.c, mod_mime.c and mod_includes.c, + * adapted by rst from original NCSA code by Rob McCool + * + * Usage instructions: + * + * Action mime/type /cgi-bin/script + * + * will activate /cgi-bin/script when a file of content type mime/type is + * requested. It sends the URL and file path of the requested document using + * the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" + +typedef struct { + table *action_types; /* Added with Action... */ + char *get; /* Added with Script GET */ + char *post; /* Added with Script POST */ + char *put; /* Added with Script PUT */ + char *delete; /* Added with Script DELETE */ +} action_dir_config; + +module action_module; + +void *create_action_dir_config (pool *p, char *dummy) +{ + action_dir_config *new = + (action_dir_config *) palloc (p, sizeof(action_dir_config)); + + new->action_types = make_table (p, 4); + new->get = NULL; + new->post = NULL; + new->put = NULL; + new->delete = NULL; + + return new; +} + +void *merge_action_dir_configs (pool *p, void *basev, void *addv) +{ + action_dir_config *base = (action_dir_config *)basev; + action_dir_config *add = (action_dir_config *)addv; + action_dir_config *new = + (action_dir_config *)palloc (p, sizeof(action_dir_config)); + + new->action_types = overlay_tables (p, add->action_types, + base->action_types); + + new->get = add->get ? add->get : base->get; + new->post = add->post ? add->post : base->post; + new->put = add->put ? add->put : base->put; + new->delete = add->delete ? add->delete : base->delete; + + return new; +} + +const char *add_action(cmd_parms *cmd, action_dir_config *m, char *type, + char *script) +{ + table_set (m->action_types, type, script); + return NULL; +} + +const char *set_script (cmd_parms *cmd, action_dir_config *m, char *method, + char *script) +{ + if (!strcmp(method, "GET")) + m->get = pstrdup(cmd->pool, script); + else if (!strcmp(method, "POST")) + m->post = pstrdup(cmd->pool, script); + else if (!strcmp(method, "PUT")) + m->put = pstrdup(cmd->pool, script); + else if (!strcmp(method, "DELETE")) + m->delete = pstrdup(cmd->pool, script); + else + return "Unknown method type for Script"; + + return NULL; +} + +command_rec action_cmds[] = { +{ "Action", add_action, NULL, OR_FILEINFO, TAKE2, + "a media type followed by a script name" }, +{ "Script", set_script, NULL, ACCESS_CONF|RSRC_CONF, TAKE2, + "a method followed by a script name" }, +{ NULL } +}; + +int action_handler (request_rec *r) +{ + action_dir_config *conf = + (action_dir_config *)get_module_config(r->per_dir_config,&action_module); + char *t, *action = r->handler ? r->handler : r->content_type; + char *script = NULL; + + /* Set allowed stuff */ + if (conf->get) r->allowed |= (1 << M_GET); + if (conf->post) r->allowed |= (1 << M_POST); + if (conf->put) r->allowed |= (1 << M_PUT); + if (conf->delete) r->allowed |= (1 << M_DELETE); + + /* First, check for the method-handling scripts */ + if ((r->method_number == M_GET) && r->args && conf->get) + script = conf->get; + else if ((r->method_number == M_POST) && conf->post) + script = conf->post; + else if ((r->method_number == M_PUT) && conf->put) + script = conf->put; + else if ((r->method_number == M_DELETE) && conf->delete) + script = conf->delete; + + /* Check for looping, which can happen if the CGI script isn't */ + if (script && r->prev && r->prev->prev) + return DECLINED; + + /* Second, check for actions (which override the method scripts) */ + if ((t = table_get(conf->action_types, + action ? action : default_type(r)))) { + script = t; + if (r->finfo.st_mode == 0) { + log_reason("File does not exist", r->filename, r); + return NOT_FOUND; + } + } + + if (script == NULL) + return DECLINED; + + internal_redirect_handler(pstrcat(r->pool, script, escape_uri(r->pool, + r->uri), r->args ? "?" : NULL, r->args, NULL), r); + return OK; +} + +handler_rec action_handlers[] = { +{ "*/*", action_handler }, +{ NULL } +}; + +module action_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_action_dir_config, /* dir config creater */ + merge_action_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + action_cmds, /* command table */ + action_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_alias.c b/usr.sbin/httpd/src/mod_alias.c new file mode 100644 index 00000000000..2d96fd713fd --- /dev/null +++ b/usr.sbin/httpd/src/mod_alias.c @@ -0,0 +1,329 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_alias.c: Stuff for dealing with directory aliases + * + * Original by Rob McCool, rewritten in succession by David Robinson + * and rst. + * + */ + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + char *real; + char *fake; + char *handler; + int redir_status; /* 301, 302, 303, 410, etc */ +} alias_entry; + +typedef struct { + array_header *aliases; + array_header *redirects; +} alias_server_conf; + +typedef struct { + array_header *redirects; +} alias_dir_conf; +module alias_module; + +void *create_alias_config (pool *p, server_rec *s) +{ + alias_server_conf *a = + (alias_server_conf *)pcalloc (p, sizeof(alias_server_conf)); + + a->aliases = make_array (p, 20, sizeof(alias_entry)); + a->redirects = make_array (p, 20, sizeof(alias_entry)); + return a; +} + +void *create_alias_dir_config (pool *p, char *d) +{ + alias_dir_conf *a = + (alias_dir_conf *)pcalloc (p, sizeof(alias_dir_conf)); + a->redirects = make_array (p, 2, sizeof(alias_entry)); + return a; +} +void *merge_alias_config (pool *p, void *basev, void *overridesv) +{ + alias_server_conf *a = + (alias_server_conf *)pcalloc (p, sizeof(alias_server_conf)); + alias_server_conf *base = (alias_server_conf *)basev, + *overrides = (alias_server_conf *)overridesv; + + a->aliases = append_arrays (p, overrides->aliases, base->aliases); + a->redirects = append_arrays (p, overrides->redirects, base->redirects); + return a; +} + +void *merge_alias_dir_config (pool *p, void *basev, void *overridesv) +{ + alias_dir_conf *a = + (alias_dir_conf *)pcalloc (p, sizeof(alias_dir_conf)); + alias_dir_conf *base = (alias_dir_conf *)basev, + *overrides = (alias_dir_conf *)overridesv; + a->redirects = append_arrays (p, overrides->redirects, base->redirects); + return a; +} + +const char *add_alias(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + alias_server_conf *conf = + (alias_server_conf *)get_module_config(s->module_config,&alias_module); + alias_entry *new = push_array (conf->aliases); + + /* XX r can NOT be relative to DocumentRoot here... compat bug. */ + + new->fake = f; new->real = r; new->handler = cmd->info; + return NULL; +} + +const char *add_redirect(cmd_parms *cmd, alias_dir_conf *dirconf, char *arg1, + char *arg2, char *arg3) +{ + alias_entry *new; + server_rec *s = cmd->server; + alias_server_conf *serverconf = + (alias_server_conf *)get_module_config(s->module_config,&alias_module); + int status = (int)cmd->info; + char *f = arg2; + char *url = arg3; + + if (!strcasecmp(arg1, "gone")) + status = HTTP_GONE; + else if (!strcasecmp(arg1, "permanent")) + status = HTTP_MOVED_PERMANENTLY; + else if (!strcasecmp(arg1, "temp")) + status = HTTP_MOVED_TEMPORARILY; + else if (!strcasecmp(arg1, "seeother")) + status = HTTP_SEE_OTHER; + else if (isdigit(*arg1)) + status = atoi(arg1); + else { + f = arg1; + url = arg2; + } + + if (is_HTTP_REDIRECT(status)) { + if (!url) return "URL to redirect to is missing"; + if (!is_url (url)) return "Redirect to non-URL"; + } + else { + if (url) return "Redirect URL not valid for this status"; + } + + if ( cmd->path ) + new = push_array (dirconf->redirects); + else + new = push_array (serverconf->redirects); + + new->fake = f; new->real = url; + new->redir_status = status; + return NULL; +} + +command_rec alias_cmds[] = { +{ "Alias", add_alias, NULL, RSRC_CONF, TAKE2, + "a fakename and a realname"}, +{ "ScriptAlias", add_alias, "cgi-script", RSRC_CONF, TAKE2, + "a fakename and a realname"}, +{ "Redirect", add_redirect, (void*)HTTP_MOVED_TEMPORARILY, + OR_FILEINFO, TAKE23, + "an optional status, then document to be redirected and destination URL" }, +{ "RedirectTemp", add_redirect, (void*)HTTP_MOVED_TEMPORARILY, + OR_FILEINFO, TAKE2, + "a document to be redirected, then the destination URL" }, +{ "RedirectPermanent", add_redirect, (void*)HTTP_MOVED_PERMANENTLY, + OR_FILEINFO, TAKE2, + "a document to be redirected, then the destination URL" }, +{ NULL } +}; + +int alias_matches (char *uri, char *alias_fakename) +{ + char *end_fakename = alias_fakename + strlen (alias_fakename); + char *aliasp = alias_fakename, *urip = uri; + + while (aliasp < end_fakename) { + if (*aliasp == '/') { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') return 0; + + while (*aliasp == '/') ++ aliasp; + while (*urip == '/') ++ urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +char *try_alias_list (request_rec *r, array_header *aliases, int doesc, int *status) +{ + alias_entry *entries = (alias_entry *)aliases->elts; + int i; + + for (i = 0; i < aliases->nelts; ++i) { + alias_entry *p = &entries[i]; + int l = alias_matches (r->uri, p->fake); + + if (l > 0) { + if (p->handler) { /* Set handler, and leave a note for mod_cgi */ + r->handler = pstrdup(r->pool, p->handler); + table_set (r->notes, "alias-forced-type", p->handler); + } + + *status = p->redir_status; + + if (doesc) { + char *escurl; + escurl = os_escape_path(r->pool, r->uri + l, 1); + + return pstrcat(r->pool, p->real, escurl, NULL); + } else + return pstrcat(r->pool, p->real, r->uri + l, NULL); + } + } + + return NULL; +} + +int translate_alias_redir(request_rec *r) +{ + void *sconf = r->server->module_config; + alias_server_conf *serverconf = + (alias_server_conf *)get_module_config(sconf, &alias_module); + char *ret; + int status; + +#ifdef __EMX__ + /* Add support for OS/2 drive names */ + if ((r->uri[0] != '/' && r->uri[0] != '\0') && r->uri[1] != ':') +#else + if (r->uri[0] != '/' && r->uri[0] != '\0') +#endif + return DECLINED; + + if ((ret = try_alias_list (r, serverconf->redirects, 1, &status)) != NULL) { + if (is_HTTP_REDIRECT(status)) { + /* include QUERY_STRING if any */ + if (r->args) { + ret = pstrcat (r->pool, ret, "?", r->args, NULL); + } + table_set (r->headers_out, "Location", ret); + } + return status; + } + + if ((ret = try_alias_list (r, serverconf->aliases, 0, &status)) != NULL) { + r->filename = ret; + return OK; + } + + return DECLINED; +} + +int fixup_redir(request_rec *r) +{ + void *dconf = r->per_dir_config; + alias_dir_conf *dirconf = + (alias_dir_conf *)get_module_config(dconf, &alias_module); + char *ret; + int status; + + /* It may have changed since last time, so try again */ + + if ((ret = try_alias_list (r, dirconf->redirects, 1, &status)) != NULL) { + if (is_HTTP_REDIRECT(status)) + table_set (r->headers_out, "Location", ret); + return status; + } + + return DECLINED; +} + +module alias_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_alias_dir_config, /* dir config creater */ + merge_alias_dir_config, /* dir merger --- default is to override */ + create_alias_config, /* server config */ + merge_alias_config, /* merge server configs */ + alias_cmds, /* command table */ + NULL, /* handlers */ + translate_alias_redir, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_redir, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_asis.c b/usr.sbin/httpd/src/mod_asis.c new file mode 100644 index 00000000000..47bf206d137 --- /dev/null +++ b/usr.sbin/httpd/src/mod_asis.c @@ -0,0 +1,131 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "http_log.h" +#include "util_script.h" +#include "http_main.h" +#include "http_request.h" + +int asis_handler (request_rec *r) +{ + FILE *f; + char *location; + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) return DECLINED; + if (r->finfo.st_mode == 0) { + log_reason("File does not exist", r->filename, r); + return NOT_FOUND; + } + + f = pfopen (r->pool, r->filename, "r"); + + if (f == NULL) { + log_reason("file permissions deny server access", r->filename, r); + return FORBIDDEN; + } + + scan_script_header (r, f); + location = table_get (r->headers_out, "Location"); + + if (location && location[0] == '/' && + ((r->status == HTTP_OK) || is_HTTP_REDIRECT(r->status))) { + + pfclose(r->pool, f); + + /* Internal redirect -- fake-up a pseudo-request */ + r->status = HTTP_OK; + + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + internal_redirect_handler (location, r); + return OK; + } + + send_http_header (r); + if (!r->header_only) send_fd (f, r); + + pfclose(r->pool, f); + return OK; +} + +handler_rec asis_handlers[] = { +{ ASIS_MAGIC_TYPE, asis_handler }, +{ "send-as-is", asis_handler }, +{ NULL } +}; + +module asis_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + NULL, /* command table */ + asis_handlers, /* handlers */ + NULL, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* pre-run fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_auth.c b/usr.sbin/httpd/src/mod_auth.c new file mode 100644 index 00000000000..acacaf07b2f --- /dev/null +++ b/usr.sbin/httpd/src/mod_auth.c @@ -0,0 +1,298 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_auth: authentication + * + * Rob McCool + * + * Adapted to Apache by rst. + * + * dirkx - Added Authoritative control to allow passing on to lower + * modules if and only if the user-id is not known to this + * module. A known user with a faulty or absent password still + * causes an AuthRequired. The default is 'Authoritative', i.e. + * no control is passed along. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#if defined(HAVE_CRYPT_H) +#include <crypt.h> +#endif + +typedef struct auth_config_struct { + char *auth_pwfile; + char *auth_grpfile; + int auth_authoritative; +} auth_config_rec; + +void *create_auth_dir_config (pool *p, char *d) +{ + auth_config_rec *sec = + (auth_config_rec *) pcalloc (p, sizeof(auth_config_rec)); + sec->auth_pwfile = NULL; /* just to illustrate the default really */ + sec->auth_grpfile = NULL; /* unless you have a broken HP cc */ + sec->auth_authoritative = 1; /* keep the fortress secure by default */ + return sec; +} + +const char *set_auth_slot (cmd_parms *cmd, void *offset, char *f, char *t) +{ + if (t && strcmp(t, "standard")) + return pstrcat(cmd->pool, "Invalid auth file type: ", t, NULL); + + return set_string_slot(cmd, offset, f); +} + +command_rec auth_cmds[] = { +{ "AuthUserFile", set_auth_slot, + (void*)XtOffsetOf(auth_config_rec,auth_pwfile), OR_AUTHCFG, TAKE12, NULL }, +{ "AuthGroupFile", set_auth_slot, + (void*)XtOffsetOf(auth_config_rec,auth_grpfile), OR_AUTHCFG, TAKE12, NULL }, +{ "AuthAuthoritative", set_flag_slot, + (void*)XtOffsetOf(auth_config_rec,auth_authoritative), + OR_AUTHCFG, FLAG, + "Set to 'no' to allow access control to be passed along to lower modules if the UserID is not known to this module" }, +{ NULL } +}; + +module auth_module; + +char *get_pw(request_rec *r, char *user, char *auth_pwfile) +{ + FILE *f; + char l[MAX_STRING_LEN]; + const char *rpw, *w; + + if(!(f=pfopen(r->pool, auth_pwfile, "r"))) { + log_reason ("Could not open password file", auth_pwfile, r); + return NULL; + } + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + if((l[0] == '#') || (!l[0])) continue; + rpw = l; + w = getword(r->pool, &rpw, ':'); + + if(!strcmp(user,w)) { + pfclose(r->pool, f); + return pstrdup (r->pool, rpw); + } + } + pfclose(r->pool, f); + return NULL; +} + +table *groups_for_user (pool *p, char *user, char *grpfile) { + FILE *f; + table *grps = make_table (p, 15); + pool *sp; + char l[MAX_STRING_LEN]; + const char *group_name, *ll, *w; + + if(!(f=pfopen(p, grpfile, "r"))) + return NULL; + + sp = make_sub_pool (p); + + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + if((l[0] == '#') || (!l[0])) continue; + ll = l; + clear_pool (sp); + + group_name = getword(sp, &ll, ':'); + + while(ll[0]) { + w = getword_conf (sp, &ll); + if(!strcmp(w,user)) { + table_set (grps, group_name, "in"); + break; + } + } + } + pfclose(p, f); + destroy_pool (sp); + return grps; +} + +/* These functions return 0 if client is OK, and proper error status + * if not... either AUTH_REQUIRED, if we made a check, and it failed, or + * SERVER_ERROR, if things are so totally confused that we couldn't + * figure out how to tell if the client is authorized or not. + * + * If they return DECLINED, and all other modules also decline, that's + * treated by the server core as a configuration error, logged and + * reported as such. + */ + +/* Determine user ID, and check if it really is that user, for HTTP + * basic authentication... + */ + +int authenticate_basic_user (request_rec *r) +{ + auth_config_rec *sec = + (auth_config_rec *)get_module_config (r->per_dir_config, &auth_module); + conn_rec *c = r->connection; + char *sent_pw, *real_pw; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_basic_auth_pw (r, &sent_pw))) return res; + + if(!sec->auth_pwfile) + return DECLINED; + + if (!(real_pw = get_pw(r, c->user, sec->auth_pwfile))) { + if (!(sec->auth_authoritative)) + return DECLINED; + ap_snprintf(errstr, sizeof(errstr), "user %s not found",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + /* anyone know where the prototype for crypt is? */ + if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) { + ap_snprintf(errstr, sizeof(errstr), "user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int check_user_access (request_rec *r) { + auth_config_rec *sec = + (auth_config_rec *)get_module_config (r->per_dir_config, &auth_module); + char *user = r->connection->user; + int m = r->method_number; + int method_restricted = 0; + register int x; + const char *t, *w; + table *grpstatus; + array_header *reqs_arr = requires (r); + require_line *reqs; + + /* BUG FIX: tadc, 11-Nov-1995. If there is no "requires" directive, + * then any user will do. + */ + if (!reqs_arr) + return (OK); + reqs = (require_line *)reqs_arr->elts; + + if(sec->auth_grpfile) + grpstatus = groups_for_user (r->pool, user, sec->auth_grpfile); + else + grpstatus = NULL; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + method_restricted = 1; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + if(!strcmp(w,"valid-user")) + return OK; + if(!strcmp(w,"user")) { + while(t[0]) { + w = getword_conf (r->pool, &t); + if(!strcmp(user,w)) + return OK; + } + } + else if(!strcmp(w,"group")) { + if(!grpstatus) + return DECLINED; /* DBM group? Something else? */ + + while(t[0]) { + w = getword_conf(r->pool, &t); + if(table_get (grpstatus, w)) + return OK; + } + } + } + + if (!method_restricted) + return OK; + + if (!(sec -> auth_authoritative)) + return DECLINED; + + note_basic_auth_failure (r); + return AUTH_REQUIRED; +} + +module auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + authenticate_basic_user, /* check_user_id */ + check_user_access, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_auth_anon.c b/usr.sbin/httpd/src/mod_auth_anon.c new file mode 100644 index 00000000000..2bb90b4d823 --- /dev/null +++ b/usr.sbin/httpd/src/mod_auth_anon.c @@ -0,0 +1,299 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * http_auth: authentication + * + * Rob McCool & Brian Behlendorf. + * + * Adapted to Apache by rst. + * + * Version 0.5 May 1996 + * + * Modified by Dirk.vanGulik@jrc.it to + * + * Adapted to allow anonymous logins, just like with Anon-FTP, when + * one gives the magic user name 'anonymous' and ones email address + * as the password. + * + * Just add the following tokes to your <directory> setup: + * + * Anonymous magic-user-id [magic-user-id]... + * + * Anonymous_MustGiveEmail [ on | off ] default = off + * Anonymous_LogEmail [ on | off ] default = on + * Anonymous_VerifyEmail [ on | off ] default = off + * Anonymous_NoUserId [ on | off ] default = off + * Anonymous_Authoritative [ on | off ] default = off + * + * The magic user id is something like 'anonymous', it is NOT case sensitive. + * + * The MustGiveEmail flag can be used to force users to enter something + * in the password field (like an email address). Default is off. + * + * Furthermore the 'NoUserID' flag can be set to allow completely empty + * usernames in as well; this can be is convenient as a single return + * in broken GUIs like W95 is often given by the user. The Default is off. + * + * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" + +typedef struct auth_anon { + char *password; + struct auth_anon * next; + } auth_anon; + +typedef struct { + + auth_anon *auth_anon_passwords; + int auth_anon_nouserid; + int auth_anon_logemail; + int auth_anon_verifyemail; + int auth_anon_mustemail; + int auth_anon_authoritative; + +} anon_auth_config_rec; + +void *create_anon_auth_dir_config (pool *p, char *d) +{ + anon_auth_config_rec * sec = (anon_auth_config_rec *) + pcalloc (p, sizeof(anon_auth_config_rec)); + + if (!sec) return NULL; /* no memory... */ + + /* just to illustrate the defaults really. */ + sec -> auth_anon_passwords =NULL; + + sec -> auth_anon_nouserid =0; + sec -> auth_anon_logemail =1; + sec -> auth_anon_verifyemail =0; + sec -> auth_anon_mustemail =1; + sec -> auth_anon_authoritative =0; + return sec; +} + +const char *anon_set_passwd_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_mustemail=arg; + return NULL; +} + +const char *anon_set_userid_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_nouserid=arg; + return NULL; +} +const char *anon_set_logemail_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_logemail=arg; + return NULL; +} +const char *anon_set_verifyemail_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_verifyemail=arg; + return NULL; +} +const char *anon_set_authoritative_flag (cmd_parms *cmd, + anon_auth_config_rec *sec, int arg) { + sec->auth_anon_authoritative=arg; + return NULL; +} + +const char *anon_set_string_slots (cmd_parms *cmd, + anon_auth_config_rec *sec, char *arg) { + + auth_anon * first; + + if (!(*arg)) + return "Anonymous string cannot be empty, use Anonymous_NoUserId instead"; + + /* squeeze in a record */ + first = sec->auth_anon_passwords; + + if ( + (!(sec->auth_anon_passwords=(auth_anon *) palloc(cmd -> pool, sizeof(auth_anon)))) || + (!(sec->auth_anon_passwords->password = pstrdup(cmd -> pool, arg))) + ) return "Failed to claim memory for an anonymous password..."; + + /* and repair the next */ + sec->auth_anon_passwords->next = first; + + return NULL; +} + +command_rec anon_auth_cmds[] = { +{ "Anonymous", anon_set_string_slots, + NULL,OR_AUTHCFG, ITERATE, NULL }, +{ "Anonymous_MustGiveEmail", anon_set_passwd_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_NoUserId", anon_set_userid_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_VerifyEmail", anon_set_verifyemail_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_LogEmail", anon_set_logemail_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, +{ "Anonymous_Authoritative", anon_set_authoritative_flag, NULL, OR_AUTHCFG, FLAG, + "Limited to 'on' or 'off'" }, + +{ NULL } +}; + +module anon_auth_module; + +int anon_authenticate_basic_user (request_rec *r) +{ + anon_auth_config_rec *sec = + (anon_auth_config_rec *)get_module_config (r->per_dir_config, + &anon_auth_module); + conn_rec *c = r->connection; + char *send_pw; + char errstr[MAX_STRING_LEN]; + int res=DECLINED; + + + if ((res=get_basic_auth_pw (r,&send_pw))) + return res; + + /* Ignore if we are not configured */ + if (!sec->auth_anon_passwords) return DECLINED; + + /* Do we allow an empty userID and/or is it the magic one + */ + + if ( (!(c->user[0])) && (sec->auth_anon_nouserid) ) { + res=OK; + } else { + auth_anon *p=sec->auth_anon_passwords; + res=DECLINED; + while ((res == DECLINED) && (p !=NULL)) { + if (!(strcasecmp(c->user,p->password))) + res=OK; + p=p->next; + } + } + if ( + /* username is OK */ + (res == OK) && + /* password been filled out ? */ + ( (!sec->auth_anon_mustemail) || strlen(send_pw) ) && + /* does the password look like an email address ? */ + ( (!sec->auth_anon_verifyemail) || + ((strpbrk("@",send_pw) != NULL) + && + (strpbrk(".",send_pw) != NULL)) + ) + ) { + if (sec->auth_anon_logemail && r->prev == NULL && r->main == NULL) { + ap_snprintf(errstr, sizeof(errstr), "Anonymous: Passwd <%s> Accepted", + send_pw ? send_pw : "\'none\'"); + log_error (errstr, r->server ); + } + return OK; + } else { + if (sec->auth_anon_authoritative) { + ap_snprintf(errstr, sizeof(errstr), + "Anonymous: Authoritative, Passwd <%s> not accepted", + send_pw ? send_pw : "\'none\'"); + log_error(errstr,r->server); + return AUTH_REQUIRED; + } + /* Drop out the bottom to return DECLINED */ + } + + + return DECLINED; +} + +int check_anon_access (request_rec *r) { + +#ifdef NOTYET + conn_rec *c = r->connection; + anon_auth_config_rec *sec = + (anon_auth_config_rec *)get_module_config (r->per_dir_config, + &anon_auth_module); + + if (!sec->auth_anon) return DECLINED; + + if ( strcasecmp(r->connection->user,sec->auth_anon )) + return DECLINED; + + return OK; +#endif + return DECLINED; +} + + +module anon_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_anon_auth_dir_config, /* dir config creater */ + NULL, /* dir merger ensure strictness */ + NULL, /* server config */ + NULL, /* merge server config */ + anon_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + anon_authenticate_basic_user,/* check_user_id */ + check_anon_access, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_auth_db.c b/usr.sbin/httpd/src/mod_auth_db.c new file mode 100644 index 00000000000..76d21d7ea6c --- /dev/null +++ b/usr.sbin/httpd/src/mod_auth_db.c @@ -0,0 +1,303 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * mod_auth_db: authentication + * + * Original work by Rob McCool & Brian Behlendorf. + * + * Adapted to Apache by rst (mod_auth_dbm) + * + * Adapted for Berkeley DB by Andrew Cohen + * + * mod_auth_db was based on mod_auth_dbm. + * + * Warning, this is not a drop in replacement for mod_auth_dbm, + * for people wanting to switch from dbm to Berkeley DB. + * It requires the use of AuthDBUserFile and AuthDBGroupFile + * instead of AuthDBMUserFile AuthDBMGroupFile + * + * Also, in the configuration file you need to specify + * db_auth_module rather than dbm_auth_module + * + * On some BSD systems (e.g. FreeBSD and NetBSD) dbm is automatically + * mapped to Berkeley DB. You can use either mod_auth_dbm or + * mod_auth_db. The latter makes it more obvious that it's Berkeley. + * + * dirkx - Added Authoritative control to allow passing on to lower + * modules if and only if the user-id is not known to this + * module. A known user with a faulty or absent password still + * causes an AuthRequired. The default is 'Authoritative', i.e. + * no control is passed along. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include <db.h> + +typedef struct { + + char *auth_dbpwfile; + char *auth_dbgrpfile; + int auth_dbauthoritative; +} db_auth_config_rec; + +void *create_db_auth_dir_config (pool *p, char *d) +{ + db_auth_config_rec *sec + = (db_auth_config_rec *)pcalloc (p, sizeof(db_auth_config_rec)); + sec->auth_dbpwfile = NULL; + sec->auth_dbgrpfile = NULL; + sec->auth_dbauthoritative=1; /* fortress is secure by default */ + return sec; +} + +const char *set_db_slot (cmd_parms *cmd, void *offset, char *f, char *t) +{ + if (!t || strcmp(t, "db")) + return DECLINE_CMD; + + return set_string_slot(cmd, offset, f); +} + +command_rec db_auth_cmds[] = { +{ "AuthDBUserFile", set_string_slot, + (void*)XtOffsetOf(db_auth_config_rec, auth_dbpwfile), + OR_AUTHCFG, TAKE1, NULL }, +{ "AuthDBGroupFile", set_string_slot, + (void*)XtOffsetOf(db_auth_config_rec, auth_dbgrpfile), + OR_AUTHCFG, TAKE1, NULL }, +{ "AuthUserFile", set_db_slot, + (void*)XtOffsetOf(db_auth_config_rec, auth_dbpwfile), + OR_AUTHCFG, TAKE12, NULL }, +{ "AuthGroupFile", set_db_slot, + (void*)XtOffsetOf(db_auth_config_rec, auth_dbgrpfile), + OR_AUTHCFG, TAKE12, NULL }, +{ "AuthDBAuthoritative", set_flag_slot, + (void*)XtOffsetOf(db_auth_config_rec, auth_dbauthoritative), + OR_AUTHCFG, FLAG, + "Set to 'no' to allow access control to be passed along to lower modules if the userID is not known to this module" }, +{ NULL } +}; + +module db_auth_module; + +char *get_db_pw(request_rec *r, char *user, const char *auth_dbpwfile) { + DB *f; + DBT d, q; + char *pw = NULL; + + q.data = user; + q.size = strlen(q.data); + + if(!(f=dbopen(auth_dbpwfile,O_RDONLY,0664,DB_HASH,NULL))) { + log_reason ("could not open db auth file", (char *) auth_dbpwfile, r); + return NULL; + } + + if (!((f->get)(f,&q,&d,0))) { + pw = palloc (r->pool, d.size + 1); + strncpy(pw,d.data,d.size); + pw[d.size] = '\0'; /* Terminate the string */ + } + + (f->close)(f); + return pw; +} + +/* We do something strange with the group file. If the group file + * contains any : we assume the format is + * key=username value=":"groupname [":"anything here is ignored] + * otherwise we now (0.8.14+) assume that the format is + * key=username value=groupname + * The first allows the password and group files to be the same + * physical DB file; key=username value=password":"groupname[":"anything] + * + * mark@telescope.org, 22Sep95 + */ + +char *get_db_grp(request_rec *r, char *user, const char *auth_dbgrpfile) { + char *grp_data = get_db_pw (r, user, auth_dbgrpfile); + char *grp_colon; char *grp_colon2; + + if (grp_data == NULL) return NULL; + + if ((grp_colon = strchr(grp_data, ':'))!=NULL) { + grp_colon2 = strchr(++grp_colon, ':'); + if (grp_colon2) *grp_colon2='\0'; + return grp_colon; + } + return grp_data; +} + +int db_authenticate_basic_user (request_rec *r) +{ + db_auth_config_rec *sec = + (db_auth_config_rec *)get_module_config (r->per_dir_config, + &db_auth_module); + conn_rec *c = r->connection; + char *sent_pw, *real_pw, *colon_pw; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_basic_auth_pw (r, &sent_pw))) + return res; + + if(!sec->auth_dbpwfile) + return DECLINED; + + if(!(real_pw = get_db_pw(r, c->user, sec->auth_dbpwfile))) { + if (!(sec -> auth_dbauthoritative)) + return DECLINED; + ap_snprintf(errstr, sizeof(errstr), "DB user %s not found", c->user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + /* Password is up to first : if exists */ + colon_pw = strchr(real_pw,':'); + if (colon_pw) *colon_pw='\0'; + /* anyone know where the prototype for crypt is? */ + if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) { + ap_snprintf(errstr, sizeof(errstr), + "user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int db_check_auth(request_rec *r) { + db_auth_config_rec *sec = + (db_auth_config_rec *)get_module_config (r->per_dir_config, + &db_auth_module); + char *user = r->connection->user; + int m = r->method_number; + char errstr[MAX_STRING_LEN]; + + array_header *reqs_arr = requires (r); + require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL; + + register int x; + const char *t; + char *w; + + if (!sec->auth_dbgrpfile) return DECLINED; + if (!reqs_arr) return DECLINED; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + + if(!strcmp(w,"group") && sec->auth_dbgrpfile) { + const char *orig_groups,*groups; + char *v; + + if (!(groups = get_db_grp(r, user, sec->auth_dbgrpfile))) { + if (!(sec->auth_dbauthoritative)) + return DECLINED; + ap_snprintf(errstr, sizeof(errstr), + "user %s not in DB group file %s", + user, sec->auth_dbgrpfile); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + orig_groups = groups; + while(t[0]) { + w = getword(r->pool, &t, ' '); + groups = orig_groups; + while(groups[0]) { + v = getword(r->pool, &groups,','); + if(!strcmp(v,w)) + return OK; + } + } + ap_snprintf(errstr, sizeof(errstr), + "user %s not in right group",user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + } + + return DECLINED; +} + + +module db_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_db_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + db_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + db_authenticate_basic_user, /* check_user_id */ + db_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_auth_dbm.c b/usr.sbin/httpd/src/mod_auth_dbm.c new file mode 100644 index 00000000000..d383264e9f6 --- /dev/null +++ b/usr.sbin/httpd/src/mod_auth_dbm.c @@ -0,0 +1,291 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_auth: authentication + * + * Rob McCool & Brian Behlendorf. + * + * Adapted to Apache by rst. + * + * dirkx - Added Authoritative control to allow passing on to lower + * modules if and only if the user-id is not known to this + * module. A known user with a faulty or absent password still + * causes an AuthRequired. The default is 'Authoritative', i.e. + * no control is passed along. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include <ndbm.h> + +typedef struct { + + char *auth_dbmpwfile; + char *auth_dbmgrpfile; + int auth_dbmauthoritative; + +} dbm_auth_config_rec; + +void *create_dbm_auth_dir_config (pool *p, char *d) +{ + dbm_auth_config_rec *sec + = (dbm_auth_config_rec *)pcalloc (p, sizeof(dbm_auth_config_rec)); + + sec->auth_dbmpwfile = NULL; + sec->auth_dbmgrpfile = NULL; + sec->auth_dbmauthoritative = 1; /* fortress is secure by default */ + + return sec; +} + +const char *set_dbm_slot (cmd_parms *cmd, void *offset, char *f, char *t) +{ + if (!t || strcmp(t, "dbm")) + return DECLINE_CMD; + + return set_string_slot(cmd, offset, f); +} + +command_rec dbm_auth_cmds[] = { +{ "AuthDBMUserFile", set_string_slot, + (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile), + OR_AUTHCFG, TAKE1, NULL }, +{ "AuthDBMGroupFile", set_string_slot, + (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile), + OR_AUTHCFG, TAKE1, NULL }, +{ "AuthUserFile", set_dbm_slot, + (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile), + OR_AUTHCFG, TAKE12, NULL }, +{ "AuthGroupFile", set_dbm_slot, + (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile), + OR_AUTHCFG, TAKE12, NULL }, +{ "AuthDBMAuthoritative", set_flag_slot, + (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmauthoritative), + OR_AUTHCFG, FLAG, "Set to 'no' to allow access control to be passed along to lower modules, if the UserID is not known in this module" }, +{ NULL } +}; + +module dbm_auth_module; + +char *get_dbm_pw(request_rec *r, char *user, char *auth_dbmpwfile) { + DBM *f; + datum d, q; + char *pw = NULL; + + q.dptr = user; + q.dsize = strlen(q.dptr); + + if(!(f=dbm_open(auth_dbmpwfile,O_RDONLY,0664))) { + log_reason ("could not open dbm auth file", auth_dbmpwfile, r); + return NULL; + } + + d = dbm_fetch(f, q); + + if (d.dptr) { + pw = palloc (r->pool, d.dsize + 1); + strncpy(pw,d.dptr,d.dsize); + pw[d.dsize] = '\0'; /* Terminate the string */ + } + + dbm_close(f); + return pw; +} + +/* We do something strange with the group file. If the group file + * contains any : we assume the format is + * key=username value=":"groupname [":"anything here is ignored] + * otherwise we now (0.8.14+) assume that the format is + * key=username value=groupname + * The first allows the password and group files to be the same + * physical DBM file; key=username value=password":"groupname[":"anything] + * + * mark@telescope.org, 22Sep95 + */ + +char *get_dbm_grp(request_rec *r, char *user, char *auth_dbmgrpfile) { + char *grp_data = get_dbm_pw (r, user, auth_dbmgrpfile); + char *grp_colon; char *grp_colon2; + + if (grp_data == NULL) return NULL; + + if ((grp_colon = strchr(grp_data, ':'))!=NULL) { + grp_colon2 = strchr(++grp_colon, ':'); + if (grp_colon2) *grp_colon2='\0'; + return grp_colon; + } + return grp_data; +} + +int dbm_authenticate_basic_user (request_rec *r) +{ + dbm_auth_config_rec *sec = + (dbm_auth_config_rec *)get_module_config (r->per_dir_config, + &dbm_auth_module); + conn_rec *c = r->connection; + char *sent_pw, *real_pw, *colon_pw; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_basic_auth_pw (r, &sent_pw))) + return res; + + if(!sec->auth_dbmpwfile) + return DECLINED; + + if(!(real_pw = get_dbm_pw(r, c->user, sec->auth_dbmpwfile))) { + if (!(sec->auth_dbmauthoritative)) + return DECLINED; + ap_snprintf(errstr, sizeof(errstr), "DBM user %s not found", c->user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + /* Password is up to first : if exists */ + colon_pw = strchr(real_pw,':'); + if (colon_pw) *colon_pw='\0'; + /* anyone know where the prototype for crypt is? */ + if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) { + ap_snprintf(errstr, sizeof(errstr), + "user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int dbm_check_auth(request_rec *r) { + dbm_auth_config_rec *sec = + (dbm_auth_config_rec *)get_module_config (r->per_dir_config, + &dbm_auth_module); + char *user = r->connection->user; + int m = r->method_number; + char errstr[MAX_STRING_LEN]; + + array_header *reqs_arr = requires (r); + require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL; + + register int x; + const char *t; + char *w; + + if (!sec->auth_dbmgrpfile) return DECLINED; + if (!reqs_arr) return DECLINED; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + + if(!strcmp(w,"group") && sec->auth_dbmgrpfile) { + const char *orig_groups,*groups; + char *v; + + if (!(groups = get_dbm_grp(r, user, sec->auth_dbmgrpfile))) { + if (!(sec->auth_dbmauthoritative)) + return DECLINED; + ap_snprintf(errstr, sizeof(errstr), + "user %s not in DBM group file %s", + user, sec->auth_dbmgrpfile); + log_reason (errstr, r->filename, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + orig_groups = groups; + while(t[0]) { + w = getword(r->pool, &t, ' '); + groups = orig_groups; + while(groups[0]) { + v = getword(r->pool, &groups,','); + if(!strcmp(v,w)) + return OK; + } + } + ap_snprintf(errstr, sizeof(errstr), + "user %s not in right group",user); + log_reason (errstr, r->filename, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + } + } + + return DECLINED; +} + + +module dbm_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dbm_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + dbm_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + dbm_authenticate_basic_user, /* check_user_id */ + dbm_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_auth_msql.c b/usr.sbin/httpd/src/mod_auth_msql.c new file mode 100644 index 00000000000..dc8064276e8 --- /dev/null +++ b/usr.sbin/httpd/src/mod_auth_msql.c @@ -0,0 +1,996 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * mod_auth_msql: authentication + * + * Rob McCool & Brian Behlendorf. + * + * Adapted to Apache by rst. + * + * Addapted for use with the mSQL database + * (see ftp:/ftp.bond.edu.au/pub/Minerva/mSQL) + * + * Version 1.0 May 1996 - Blame: Dirk.vanGulik@jrc.it. + * + * A (sometimes more up to date) version of the documentation + * can be found at the http://www.apache.org site or at + * http://me-www.jrc.it/~dirkx/mod_auth_msql.html. + * + * Outline: + * + * This module allows access control using the public domain + * mSQL database; a fast but limted SQL engine which can be + * contacted over an internal unix domain protocol as well as + * over normal inter-machine tcp/ip socket communication. + * + * An example table could be: + * + * create table user_records ( + * User_id char(32) primary key, + * Cpasswd char(32), + * [ Xgroup char(32) ] + * ) \g + * + * The user_id can be as long as desired; however some of the + * popular web browsers truncate, or stop the user from entering + * names longer than 32 characters. Furthermore the 'crypt' function + * on your platform might impose further limits. Also use of + * the 'require users uid [uid..]' directive in the access.conf file, + * where the user ids are separated by spaces can possibly prohibit the + * use of spaces in your user-names. Also, not the MAX_FIELD_LEN define + * somewhere below. + * + * To use the above, the following example could be in your access.conf + * file. Also there is a more elaborate description afther this example. + * + * <directory /web/docs/private> + * + * Auth_MSQLhost localhost + * or + * Auth_MSQLhost datab.machine.your.org + * + * If this directive is ommited, or set to + * localhost, the machine on which apache + * runs is assumed, and the faster /dev/msql + * communication channel will be used. Otherwise + * it is the machine to contact by tcp/ip. + * + * Auth_MSQLdatabase www + * + * The name of the database on the above machine, + * which contains *both* the tables for group and + * for user/passwords. Currently it is not possible + * to have these split over two databases. Make + * sure that the msql.acl (access control file) of + * mSQL does indeed allow the effective uid of the + * web server read access to this database. Check the + * httpd.conf file for this uid. + * + * Auth_MSQLpwd_table user_records + * + * Here the table which contain the uid/password combination + * is specified. + * + * Auth_MSQLuid_field User_id + * Auth_MSQLpwd_field Cpasswd + * + * These two directive specify the field names in the 'user_record' + * table. If this module is compiled with the BACKWARD_VITEK + * compatibility switch, the defaults 'user' and 'password' are + * assumed if you do not specify them. Currently the user_id field + * *MUST* be a primary key or one must ensure that each user only + * occurs *once* in the table. If a UID occurs twice access is + * denied by default. + * + * Auth_MSQLgrp_table user_records + * Auth_MSQLgrp_field Xgroup + * + * Optionaly one can also specify a table which contains the + * user/group combinations. This can be the same table which + * also contains the username/password combinations. However + * if a user belongs to two or more groups, one will have to + * use a differt table with multiple entries. + * + * Auth_MSQL_nopasswd off + * Auth_MSQL_Authoritative on + * Auth_MSQL_EncryptedPasswords on + * + * These three optional fields (all set to the sensible defaults, + * so you really do not have to enter them) are described in more + * detail below. If you choose to set these to any other values than + * the above be very sure you understand the security implications and + * do verify that apache does what you exect it to do. + * + * AuthName example mSQL realm + * AuthType basic + * + * Normal apache/ncsa tokens for access control + * + * <limit get post head> + * order deny,allow + * allow from all + * + * require valid-user + * 'valid-user'; allow in any user which has a valid uid/passwd + * pair in the above pwd_table. + * or + * require user smith jones + * Limit access to users who have a valid uid/passwd pair in the + * above pwd_table AND whose uid is 'smith' or 'jones'. Do note that + * the uid's are separated by 'spaces' for historic (ncsa) reasons. + * So allowing uids with spaces might cause problems. + * + * require group has_paid + * Optionally also ensure that the uid has the value 'has_paid' in the group + * field in the group table. + * </limit> + * </directory> + * + * End of the example + * + * - full description of all tokens: - + * + * Directives: + * + * Auth_MSQLhost Hostname of the machine running + * the mSQL demon. The effective uid + * of the server should be allowed + * access. If not given, or if it is + * the magic name 'localhost', it is + * passed to the mSQL libary as a null + * pointer. This effectively forces it + * to use /dev/msql rather than the + * (slower) socket communication. + * + * Auth_MSQLdatabase Name of the database in which the following + * table(s) are contained. + * + * Auth_MSQLpwd_table Contains at least the fields with the + * username and the (encrypted) password. Each + * uid should only occur once in this table and + * for performance reasons should be a primary key. + * Normally this table is compulsory, but it is + * possible to use a fall-through to other methods + * and use the mSQL module for group control only; + * see the Authoritative directive below. + * + * Auth_MSQLgrp_table Contains at least the fields with the + * username and the groupname. A user which + * is in multiple groups has therefore + * multiple entries; this might be some per- + * formance problems associated with this; and one + * might consider to have separate tables for each + * group (rather than all groups in one table) if + * your directory structure allows for it. + * One only needs to specify this table when doing + * group control. + * + * Auth_MSQLuid_field Name of the field containing the username + * Auth_MSQLpwd_field Fieldname for the passwords + * Auth_MSQLgrp_field Fieldname for the groupname + * + * Only the fields used need to be specified. When this + * module is compiled with the BACKWARD_VITEK option the + * uid and pwd field names default to 'user' and 'password'. + * + * + * Auth_MSQL_nopasswd <on|off> + * skip password comparison if passwd field is + * empty; i.e. allow 'any' password. This is off + * by default; thus to ensure that an empty field + * in the mSQL table does not allow people in by + * default with a random password. + * + * Auth_MSQL_Authoritative <on|off> + * default is 'on'. When set on, there is no + * fall through to other authorization methods. So if a + * user is not in the mSQL dbase table (and perhaps + * not in the right group) or has the password wrong, then + * he or she is denied access. When this directive is set to + * 'off' control is passed on to any other authorization + * modules, such as the basic auth module wih the htpasswd + * file and or the unix-(g)dbm modules. + * The default is 'ON' to avoid nasty 'fall-through' sur- + * prizes. Do be sure you know what you decide to switch + * it off. + * + * Auth_MSQL_EncryptedPasswords <on|off> + * default is on. When set on, the values in the + * pwd_field are assumed to be crypted using *your* + * machines 'crypt' function; and the incoming password + * is 'crypt'ed before comparison. When this function is + * off, the comparison is done directly with the plaintext + * entered password. (Yes; http-basic-auth does send the + * password as plaintext over the wire :-( ). The default + * is a sensible 'on', and I personally thing that it is + * a *very-bad-idea* to change this. However a multi + * vendor or international environment (which sometimes + * leads to different crypts functions) might force you to. + * + * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx + * 23 Nov 1995, 24 Feb 1996, 16 May 1996. + * + * Version 0.0 First release + * 0.1 Update to apache 1.00 + * 0.2 added lines which got missing god knows when + * and which did the valid-user authentification + * no good at all ! + * 0.3 Added 'Auth_MSQL_nopasswd' option + * 0.4 Cleaned out the error messages mess. + * 0.6 Inconsistency with gid/grp in comment/token/source + * Make sure you really use 'Auth_MSQLgrp_field' as + * indicated above. + * 0.7 *host to host fixed. Credits go to Rob Stout, + * <stout@lava.et.tudelft.nl> for spotting this one. + * 0.8 Authoritative directive added. See above. + * 0.9 palloc return code check(s), should be backward compatible with + * 1.11 version of Vivek Khera <khera@kciLink.com> msql module, + * fixed broken err msg in group control, changed command table + * messages to make more sense when displayed in that new module + * management tool. Added EncryptedPassword on/off functionality. + * msqlClose() statements added upon error. Support for persistent + * connections with the mSQL database (riscy). Escaping of ' and \. + * Replaced some MAX_STRING_LEN claims. + * 1.0 removed some error check as they where already done elsehwere + * NumFields -> NumRows (Thanks Vitek). More stack memory. + * 1.1 no logging of empty password strings. + * 1.2 Problem with the Backward vitek which cause it to check + * even if msql_auth was not configured; Also more carefull + * with the authoritative stuff; caught by thomas@marvin.calvacom.fr. + * 1.3 Even more changes to get it right; that BACKWARD thing was a bad + * idea. + */ + + +#define ONLY_ONCE 1 +/* + * If the mSQL table containing the uid/passwd combination does + * not have the uid field as a primary key, it is possible for the + * uid to occur more than once in the table with possibly different + * passwords. When this module is compiled with the ONLY_ONCE directive + * set, access is denied if the uid occures more than once in the + * uid/passwd table. If you choose not to set it, the software takes + * the first pair returned and ignores any further pairs. The SQL + * statement used for this is + * + * "select password form pwd_table where user='uid'" + * + * this might lead to unpredictable results. For this reason as well + * as for performance reasons you are strongly adviced to make the + * uid field a primary key. Use at your own peril :-) + */ + +#undef KEEP_MSQL_CONNECTION_OPEN +/* + * Normally the (tcp/ip) connection with the database is opened and + * closed for each SQL query. When the httpd-server and the database + * are on the same machine, and /dev/msql is used this does not + * cause a serious overhead. However when your platform does not + * support this (see the mSQL documentation) or when the web server + * and the database are on different machines the overhead can be + * considerable. When the above is set defined the server leaves the + * connection open; i.e. no call to msqlClose(). If an error occures + * an attempt is made to re-open the connection for the next http-rq. + * + * This has a number of very serious drawbacks + * - It costs 2 already rare filedescriptors for each child. + * - It costs msql-connections, typically one per child. The (compiled in) + * number of connections mSQL can handle is low, typically 6 or 12. + * which might prohibit access to the mSQL database for later + * processes. + * - when a child dies, it might not free that connection properly + * or quick enough. + * - When errors start to occur, connection/file-descr resources might + * become exausted very quickly. + * + * In short; use this at your own peril and only in a highly controled and + * monitored environment + */ + +#define BACKWARD_VITEK +#define VITEX_uid_name "user" +#define VITEX_gid_name "passwd" +/* A second mSQL auth module for apache has also been developed by + * Vivek Khera <khera@kciLink.com> and was subsequently distributed + * with some early versions of Apache. It can be optained from + * ftp://ftp.kcilink.com/pub/mod_auth_msql.c*. Older 'vitek' versions had + * the field/table names compiled in; newer versions, v.1.11 have + * more access.conf configuration options; however these where + * choosen not to be in line the 'ewse' version of this module. Also, + * the 'vitek' module does not give group control or 'empty' password + * control. + * + * To get things slightly more in line this version (0.9) should + * be backward compatible with the vitek module by: + * + * - adding support for the EncryptedPassword on/off functionality + * + * - adding support for the different spelling fo the 4 configuration + * tokens for user-table-name, user/password-field-name and dbase-name. + * + * - setting some field names to a default which used to be hard + * coded in in older vitek modules. + * + * If this troubles you; remove the 'BACKWARD_VITEX' define. + */ + +/* get some sensible values; rather than that big MAX_STRING_LEN, + */ + +/* Max field value length limit; well above the limit of some browsers :-) + */ +#define MAX_FIELD_LEN (64) +/* the next two values can be pulled from msql_priv.c, which is *NOT* copied to your + * /usr/local/include as part of the normal install procedure which comes with + * mSQL. + */ +#define MSQL_FIELD_NAME_LEN (19) +#define MSQL_TABLE_NAME_LEN (19) +/* We only do the following two queries: + * + * - for the user/passwd combination + * select PWDFIELD from PWDTABEL where USERFIELD='UID' + * + * - optionally for the user/group combination: + * select GROUPFIELD from GROUPTABLE where USERFIELD='UID' and GROUPFIELD='GID' + * + * This leads to the following limits: (we are ignoring escaping a wee bit bit here + * assuming not more than 24 escapes.) + */ + +#define MAX_QUERY_LEN (32+24+MAX_FIELD_LEN*2+3*MSQL_FIELD_NAME_LEN+1*MSQL_TABLE_NAME_LEN) + + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include <msql.h> +#ifdef HAVE_CRYPT_H +#include <crypt.h> +#endif + +typedef struct { + + char *auth_msql_host; + char *auth_msql_database; + + char *auth_msql_pwd_table; + char *auth_msql_grp_table; + + char *auth_msql_pwd_field; + char *auth_msql_uname_field; + char *auth_msql_grp_field; + + int auth_msql_nopasswd; + int auth_msql_authoritative; + int auth_msql_encrypted; + +} msql_auth_config_rec; + +void *create_msql_auth_dir_config (pool *p, char *d) +{ + msql_auth_config_rec * sec= (msql_auth_config_rec *) pcalloc (p, sizeof(msql_auth_config_rec)); + + sec->auth_msql_host = NULL; /* just to enforce the default 'localhost' behaviour */ + + /* just in case, to be nice... */ + sec->auth_msql_database = NULL; + sec->auth_msql_pwd_table = NULL; + sec->auth_msql_grp_table = NULL; + sec->auth_msql_pwd_field = NULL; + sec->auth_msql_uname_field = NULL; + sec->auth_msql_grp_field = NULL; + + + sec->auth_msql_authoritative = 1; /* set some defaults, just in case... */ + sec->auth_msql_encrypted = 1; + sec->auth_msql_nopasswd = 0; + +#ifdef BACKWARD_VITEK + /* these are for backward compatibility with the Vivek + * msql module, as it used to have compile-time defaults. + */ + sec->auth_msql_uname_field = VITEX_uid_name; + sec->auth_msql_pwd_field = VITEX_gid_name; +#endif + + return sec; +} + +const char *set_passwd_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) { + sec->auth_msql_nopasswd=arg; + return NULL; +} + +const char *set_authoritative_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) { + sec->auth_msql_authoritative=arg; + return NULL; +} + +const char *set_crypted_password_flag (cmd_parms *cmd, msql_auth_config_rec *sec , int arg) { + sec->auth_msql_encrypted = arg; + return NULL; +} + +const char *msql_set_string_slot (cmd_parms *cmd, char *struct_ptr, char *arg) { + int offset = (int)cmd->info; + *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg); + return NULL; +} + + +command_rec msql_auth_cmds[] = { +{ "Auth_MSQLhost", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host), + OR_AUTHCFG, TAKE1, "Host on which the mSQL database engine resides (defaults to localhost)" }, + +{ "Auth_MSQLdatabase", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database), + OR_AUTHCFG, TAKE1, "Name of the mSQL database which contains the password (and possibly the group) tables. " }, + +{ "Auth_MSQLpwd_table", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table), + OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the password/user-name combination" }, + +{ "Auth_MSQLgrp_table", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table), + OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the group-name/user-name combination; can be the same as the password-table." }, + +{ "Auth_MSQLpwd_field", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field), + OR_AUTHCFG, TAKE1, "The name of the field in the mSQL password table" }, + +{ "Auth_MSQLuid_field", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field), + OR_AUTHCFG, TAKE1, "The name of the user-name field in the mSQL password (and possibly group) table(s)." }, + +{ "Auth_MSQLgrp_field", msql_set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field), + OR_AUTHCFG, TAKE1, + "The name of the group field in the mSQL group table; must be set if you want to use groups." }, + +{ "Auth_MSQL_nopasswd", set_passwd_flag, NULL, OR_AUTHCFG, FLAG, + "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." }, + +{ "Auth_MSQL_Authoritative", set_authoritative_flag, NULL, OR_AUTHCFG, FLAG, + "When 'on' the mSQL database is taken to be authoritative and access control is not passed along to other db or access modules." }, + +{ "Auth_MSQL_EncryptedPasswords", set_crypted_password_flag, NULL, OR_AUTHCFG, FLAG, + "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." }, + +#ifdef BACKWARD_VITEK +/* These 'altenative' tokens should ensure backward compatibility + * with viteks mSQL module. The only difference is the spelling. + * Note that these tokens do not allow group configuration. + */ +{ "AuthMSQLHost", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host), + OR_AUTHCFG, TAKE1, "mSQL server hostname" }, +{ "AuthMSQLDB", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database), + OR_AUTHCFG, TAKE1, "mSQL database name" }, +{ "AuthMSQLUserTable", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table), + OR_AUTHCFG, TAKE1, "mSQL user table name" }, +{ "AuthMSQLGroupTable", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table), + OR_AUTHCFG, TAKE1, "mSQL group table name" }, +{ "AuthMSQLNameField", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field), + OR_AUTHCFG, TAKE1, "mSQL User ID field name within table" }, +{ "AuthMSQLGroupField", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field), + OR_AUTHCFG, TAKE1, "mSQL Group field name within table" }, +{ "AuthMSQLPasswordField", set_string_slot, + (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field), + OR_AUTHCFG, TAKE1, "mSQL Password field name within table" }, +{ "AuthMSQLCryptedPasswords", set_crypted_password_flag, NULL, + OR_AUTHCFG, FLAG, "mSQL passwords are stored encrypted if On" }, + +#endif + +{ NULL } +}; + +module msql_auth_module; + +/* boring little routine which escapes the ' and \ in the + * SQL query. See the mSQL FAQ for more information :-) on + * this very popular subject in the msql-mailing list. + */ +char *msql_escape(char *out, char *in, char *msql_errstr) { + + register int i=0,j=0; + + do { + /* do we need to escape */ + if ( (in[i] == '\'') || (in[i] == '\\')) { + + /* does this fit ? */ + if (j >= (MAX_FIELD_LEN-1)) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "Could not escape '%s', longer than %d",in,MAX_FIELD_LEN); + return NULL; + }; + + out[j++] = '\\'; /* insert that escaping slash for good measure */ + }; + + /* Do things still fit ? */ + if (j >= MAX_FIELD_LEN) return NULL; + + } while ( ( out[j++] = in[i++]) != '\0' ); + + return out; +} + +/* get the password for uname=user, and copy it + * into r. Assume that user is a string and stored + * as such in the mSQL database + */ +char *do_msql_query(request_rec *r, char *query, msql_auth_config_rec *sec, int once , char *msql_errstr) { + + static int sock=-1; + int hit; + m_result *results; + m_row currow; + + char *result=NULL; + char *host=sec->auth_msql_host; + +#ifndef KEEP_MSQL_CONNECTION_OPEN + sock=-1; +#endif + + /* force fast access over /dev/msql */ + + if ((host) && (!(strcasecmp(host,"localhost")))) + host=NULL; + + /* (re) open if nessecary + */ + if (sock==-1) if ((sock=msqlConnect(host)) == -1) { + ap_snprintf (msql_errstr, MAX_STRING_LEN, + "mSQL: Could not connect to Msql DB %s (%s)", + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg); + return NULL; + } + + /* we always do this, as it avoids book-keeping + * and is quite cheap anyway + */ + if (msqlSelectDB(sock,sec->auth_msql_database) == -1 ) { + ap_snprintf (msql_errstr, MAX_STRING_LEN, + "mSQL: Could not select Msql Table \'%s\' on host \'%s\'(%s)", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg); + msqlClose(sock); + sock=-1; + return NULL; + } + + if (msqlQuery(sock,query) == -1 ) { + ap_snprintf (msql_errstr, MAX_STRING_LEN, + "mSQL: Could not Query database '%s' on host '%s' (%s) with query [%s]", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg, + ( query ? query : "\'unset!\'") ); + msqlClose(sock); + sock=-1; + return NULL; + } + + if (!(results=msqlStoreResult())) { + ap_snprintf (msql_errstr, MAX_STRING_LEN, + "mSQL: Could not get the results from mSQL database \'%s\' on \'%s\' (%s) with query [%s]", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"), + msqlErrMsg, + ( query ? query : "\'unset!\'") ); + msqlClose(sock); + sock=-1; + return NULL; + }; + + hit=msqlNumRows(results); + + if (( once ) && ( hit >1 )) { + /* complain if there are to many + * matches. + */ + ap_snprintf (msql_errstr, MAX_STRING_LEN, + "mSQL: More than %d matches (%d) whith query [%s]", + once,hit,( query ? query : "\'unset!\'") ); + } else + /* if we have a it, try to get it + */ + if ( hit ) { + if ( (currow=msqlFetchRow(results)) != NULL) { + /* copy the first matching field value */ + if (!(result=palloc(r->pool,strlen(currow[0])+1))) { + ap_snprintf (msql_errstr, MAX_STRING_LEN, + "mSQL: Could not get memory for mSQL %s (%s) with [%s]", + (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"), + msqlErrMsg, + ( query ? query : "\'unset!\'") ); + /* do not return right away, to ensure Free/Close. + */ + } else { + strcpy(result,currow[0]); + }; + } + }; + + /* ignore errors, functions are voids anyway. */ + msqlFreeResult(results); + +#ifndef KEEP_MSQL_CONNECTION_OPEN + /* close the connection, unless explicitly told not to. Do note that + * we do not have a decent closing option of child termination due + * the lack of hooks in the API (or my understanding thereof) + */ + msqlClose(sock); + sock=-1; +#endif + + return result; +} + +char *get_msql_pw(request_rec *r, char *user, msql_auth_config_rec *sec ,char *msql_errstr) { + char query[MAX_QUERY_LEN]; + char esc_user[MAX_FIELD_LEN]; + + /* do we have enough information to build a query */ + if ( + (!sec->auth_msql_pwd_table) || + (!sec->auth_msql_pwd_field) || + (!sec->auth_msql_uname_field) + ) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: Missing parameters for password lookup: %s%s%s", + (sec->auth_msql_pwd_table ? "" : "Password table "), + (sec->auth_msql_pwd_field ? "" : "Password field name "), + (sec->auth_msql_uname_field ? "" : "UserID field name ") + ); + return NULL; + }; + + if (!(msql_escape(esc_user, user, msql_errstr))) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: Could not cope/escape the '%s' user_id value; ",user); + return NULL; + }; + ap_snprintf(query, sizeof(query), + "select %s from %s where %s='%s'", + sec->auth_msql_pwd_field, + sec->auth_msql_pwd_table, + sec->auth_msql_uname_field, + esc_user + ); + + return do_msql_query(r,query,sec,ONLY_ONCE,msql_errstr); +} + +char *get_msql_grp(request_rec *r, char *group,char *user, msql_auth_config_rec *sec, char *msql_errstr) { + char query[MAX_QUERY_LEN]; + + char esc_user[MAX_FIELD_LEN]; + char esc_group[MAX_FIELD_LEN]; + + /* do we have enough information to build a query */ + if ( + (!sec->auth_msql_grp_table) || + (!sec->auth_msql_grp_field) || + (!sec->auth_msql_uname_field) + ) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: Missing parameters for group lookup: %s%s%s", + (sec->auth_msql_grp_table ? "" : "Group table "), + (sec->auth_msql_grp_field ? "" : "GroupID field name "), + (sec->auth_msql_uname_field ? "" : "UserID field name ") + ); + return NULL; + }; + + if (!(msql_escape(esc_user, user,msql_errstr))) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: Could not cope/escape the '%s' user_id value",user); + + return NULL; + }; + if (!(msql_escape(esc_group, group,msql_errstr))) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: Could not cope/escape the '%s' group_id value",group); + + return NULL; + }; + + ap_snprintf(query, sizeof(query), + "select %s from %s where %s='%s' and %s='%s'", + sec->auth_msql_grp_field, + sec->auth_msql_grp_table, + sec->auth_msql_uname_field,esc_user, + sec->auth_msql_grp_field, esc_group + ); + + return do_msql_query(r,query,sec,0,msql_errstr); +} + + +int msql_authenticate_basic_user (request_rec *r) +{ + msql_auth_config_rec *sec = + (msql_auth_config_rec *)get_module_config (r->per_dir_config, + &msql_auth_module); + char msql_errstr[MAX_STRING_LEN]; + /* msql_errstr must be MAX_STRING_LEN in size unless you + * change size in ap_snprintf() calls + */ + conn_rec *c = r->connection; + char *sent_pw, *real_pw; + int res; + msql_errstr[0]='\0'; + + if ((res = get_basic_auth_pw (r, &sent_pw))) + return res; + + /* if mSQL *password* checking is configured in any way, i.e. then + * handle it, if not decline and leave it to the next in line.. + * We do not check on dbase, group, userid or host name, as it is + * perfectly possible to only do group control with mSQL and leave + * user control to the next (dbm) guy in line. + * We no longer check on the user field name; to avoid problems + * with Backward VITEK. + */ + if (!sec->auth_msql_pwd_table) return DECLINED; + + if(!(real_pw = get_msql_pw(r, c->user, sec,msql_errstr ))) { + if ( msql_errstr[0] ) { + res = SERVER_ERROR; + } else { + if (sec->auth_msql_authoritative) { + /* insist that the user is in the database + */ + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: Password for user %s not found", c->user); + note_basic_auth_failure (r); + res = AUTH_REQUIRED; + } else { + /* pass control on to the next authorization module. + */ + return DECLINED; + }; /* if authoritative */ + }; /* if no error */ + log_reason (msql_errstr, r->filename, r); + return res; + } + + /* allow no password, if the flag is set and the password + * is empty. But be sure to log this. + */ + + if ((sec->auth_msql_nopasswd) && (!strlen(real_pw))) { +/* + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: user %s: Empty/'any' password accepted",c->user); + log_reason (msql_errstr, r->uri, r); + */ + return OK; + }; + + /* if the flag is off however, keep that kind of stuff at + * an arms length. + */ + if ((!strlen(real_pw)) || (!strlen(sent_pw))) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL: user %s: Empty Password(s) Rejected",c->user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + }; + + if(sec->auth_msql_encrypted) { + /* anyone know where the prototype for crypt is? + * + * PLEASE NOTE: + * The crypt function (at least under FreeBSD 2.0.5) returns + * a ptr to a *static* array (max 120 chars) and does *not* + * modify the string pointed at by sent_pw ! + */ + sent_pw=(char *)crypt(sent_pw,real_pw); + }; + + if (strcmp(real_pw,sent_pw)) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, + "mSQL user %s: password mismatch",c->user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int msql_check_auth (request_rec *r) { + int user_result=DECLINED,group_result=DECLINED; + + msql_auth_config_rec *sec = + (msql_auth_config_rec *)get_module_config (r->per_dir_config, + &msql_auth_module); + char msql_errstr[MAX_STRING_LEN]; + /* msql_errstr must be MAX_STRING_LEN in size unless you + * change size in ap_snprintf() calls + */ + char *user = r->connection->user; + int m = r->method_number; + array_header *reqs_arr = requires (r); + require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL; + + register int x; + const char *t, *w; + msql_errstr[0]='\0'; + + /* If we are not configured, ignore */ + if (!sec->auth_msql_pwd_table) return DECLINED; + + if (!reqs_arr) { + if (sec->auth_msql_authoritative) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, "user %s denied, no access rules specified (MSQL-Authoritative) ",user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + }; + return DECLINED; + }; + + for(x=0; (x < reqs_arr->nelts) ; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + + if ((user_result != OK) && (!strcmp(w,"user"))) { + user_result=AUTH_REQUIRED; + while(t[0]) { + w = getword_conf (r->pool, &t); + if (!strcmp(user,w)) { + user_result= OK; + break; + }; + } + if ((sec->auth_msql_authoritative) && ( user_result != OK)) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, "User %s not found (MSQL-Auhtorative)",user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + }; + } + + if ( (group_result != OK) && + (!strcmp(w,"group")) && + (sec->auth_msql_grp_table) && + (sec->auth_msql_grp_field) + ) { + /* look up the membership for each of the groups in the table + */ + group_result=AUTH_REQUIRED; + while ( (t[0]) && (group_result != OK) && (!msql_errstr[0]) ) { + if (get_msql_grp(r,getword(r->pool, &t, ' '),user,sec,msql_errstr)) { + group_result= OK; + break; + }; + }; + + if (msql_errstr[0]) { + log_reason (msql_errstr, r->filename, r); + return SERVER_ERROR; + }; + + if ( (sec->auth_msql_authoritative) && (group_result != OK) ) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, "user %s not in right groups (MSQL-Authoritative) ",user); + log_reason (msql_errstr, r->uri, r); + note_basic_auth_failure(r); + return AUTH_REQUIRED; + }; + }; + + if(!strcmp(w,"valid-user")) { + user_result= OK; + }; + } + + /* Get serious if we are authoritative, previous + * returns are only if msql yielded a correct result. + * This really is not needed. + */ + if (((group_result == AUTH_REQUIRED) || (user_result == AUTH_REQUIRED)) && (sec->auth_msql_authoritative) ) { + ap_snprintf(msql_errstr, MAX_STRING_LEN, "mSQL-Authoritative: Access denied on %s %s rule(s) ", + (group_result == AUTH_REQUIRED) ? "USER" : "", + (user_result == AUTH_REQUIRED) ? "GROUP" : "" + ); + log_reason (msql_errstr, r->uri, r); + return AUTH_REQUIRED; + }; + + if ( (user_result == OK) || (group_result == OK)) + return OK; + + return DECLINED; +} + + +module msql_auth_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_msql_auth_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + msql_auth_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + msql_authenticate_basic_user,/* check_user_id */ + msql_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* pre-run fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_browser.c b/usr.sbin/httpd/src/mod_browser.c new file mode 100644 index 00000000000..3779cdec087 --- /dev/null +++ b/usr.sbin/httpd/src/mod_browser.c @@ -0,0 +1,189 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * mod_browser.c + * Set environment variables based on browser support. + * + * Alexei Kosut <akosut@organic.com> + */ + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + char *name; + regex_t *preg; + table *features; +} browser_entry; + +typedef struct { + array_header *browsers; +} browser_server_config_rec; + +module browser_module; + +void *create_browser_config (pool *p, server_rec *dummy) +{ + browser_server_config_rec *new = + (browser_server_config_rec *) palloc (p, sizeof(browser_server_config_rec)); + + new->browsers = make_array (p, 20, sizeof(browser_entry)); + return (void *)new; +} + +void *merge_browser_config (pool *p, void *basev, void *overridesv) +{ + browser_server_config_rec *a = + pcalloc(p, sizeof(browser_server_config_rec)); + browser_server_config_rec *base = basev, *overrides = overridesv; + + a->browsers = append_arrays(p, base->browsers, overrides->browsers); + return a; +} + +const char *add_browser(cmd_parms *cmd, void *dummy, char *name, + const char *feature) +{ + browser_server_config_rec *sconf = + get_module_config (cmd->server->module_config, &browser_module); + browser_entry *new, *entries = (browser_entry *)sconf->browsers->elts; + char *var; + int i, cflags = (int)cmd->info; + + /* First, try to merge into an existing entry */ + + for (i = 0; i < sconf->browsers->nelts; ++i) { + browser_entry *b = &entries[i]; + if (!strcmp(b->name, name)) { + var = getword(cmd->pool, &feature, '='); + if (*feature) table_set(b->features, var, feature); + else if (*var == '!') table_set(b->features, var + 1, "!"); + else table_set(b->features, var, "1"); + return NULL; + } + } + + /* If none was found, create a new entry */ + + new = push_array(sconf->browsers); + new->name = name; + new->preg = pregcomp (cmd->pool, name, REG_EXTENDED|REG_NOSUB|cflags); + if (new->preg == NULL) { + return "Browser regex could not be compiled."; + } + new->features = make_table(cmd->pool, 5); + + var = getword(cmd->pool, &feature, '='); + if (*feature) table_set(new->features, var, feature); + else if (*var == '!') table_set(new->features, var + 1, "!"); + else table_set(new->features, var, "1"); + + return NULL; +} + +command_rec browser_module_cmds[] = { +{ "BrowserMatch", add_browser, (void*)0, + RSRC_CONF, ITERATE2, "A browser regex and a list of variables." }, +{ "BrowserMatchNoCase", add_browser, (void*)REG_ICASE, + RSRC_CONF, ITERATE2, "a browser regex and a list of variables." }, +{ NULL }, +}; + +static int browser_match(request_rec *r) +{ + server_rec *s = r->server; + browser_server_config_rec *sconf = get_module_config (s->module_config, + &browser_module); + browser_entry *entries = (browser_entry *)sconf->browsers->elts; + table_entry *elts; + char *ua = table_get(r->headers_in, "User-Agent"); + int i, j; + + if (!ua) return DECLINED; + + for (i = 0; i < sconf->browsers->nelts; ++i) { + browser_entry *b = &entries[i]; + + if (!regexec(b->preg, ua, 0, NULL, 0)) { + elts = (table_entry *)b->features->elts; + + for (j = 0; j < b->features->nelts; ++j) { + if (!strcmp(elts[j].val, "!")) + table_unset(r->subprocess_env, elts[j].key); + else + table_set(r->subprocess_env, elts[j].key, elts[j].val); + } + } + } + + return DECLINED; +} + +module browser_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_browser_config, /* server config */ + merge_browser_config, /* merge server configs */ + browser_module_cmds, /* command table */ + NULL, /* handlers */ + browser_match, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_cern_meta.c b/usr.sbin/httpd/src/mod_cern_meta.c new file mode 100644 index 00000000000..d05677c8c25 --- /dev/null +++ b/usr.sbin/httpd/src/mod_cern_meta.c @@ -0,0 +1,325 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * mod_cern_meta.c + * version 0.0.5 + * status beta + * + * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 25.Jan.96 + * + * Emulate the CERN HTTPD Meta file semantics. Meta files are HTTP + * headers that can be output in addition to the normal range of + * headers for each file accessed. They appear rather like the Apache + * .asis files, and are able to provide a crude way of influencing + * the Expires: header, as well as providing other curiosities. + * There are many ways to manage meta information, this one was + * chosen because there is already a large number of CERN users + * who can exploit this module. It should be noted that there are probably + * more sensitive ways of managing the Expires: header specifically. + * + * The module obeys the following directives, which can only appear + * in the server's .conf files and not in any .htaccess file. + * + * MetaDir <directory name> + * + * specifies the name of the directory in which Apache can find + * meta information files. The directory is usually a 'hidden' + * subdirectory of the directory that contains the file being + * accessed. eg: + * + * # .meta files are in the *same* directory as the + * # file being accessed + * MetaDir . + * + * the default is to look in a '.web' subdirectory. This is the + * same as for CERN 3.+ webservers and behaviour is the same as + * for the directive: + * + * MetaDir .web + * + * MetaSuffix <meta file suffix> + * + * specifies the file name suffix for the file containing the + * meta information. eg: + * + * # our meta files are suffixed with '.cern_meta' + * MetaSuffix .cern_meta + * + * the default is to look for files with the suffix '.meta'. This + * behaviour is the same as for the directive: + * + * MetaSuffix .meta + * + * When accessing the file + * + * DOCUMENT_ROOT/somedir/index.html + * + * this module will look for the file + * + * DOCUMENT_ROOT/somedir/.web/index.html.meta + * + * and will use its contents to generate additional MIME header + * information. + * + * For more information on the CERN Meta file semantics see: + * + * http://www.w3.org/hypertext/WWW/Daemon/User/Config/General.html#MetaDir + * + * Change-log: + * 29.Jan.96 pfopen/pfclose instead of fopen/fclose + * DECLINE when real file not found, we may be checking each + * of the index.html/index.shtml/index.htm variants and don't + * need to report missing ones as spurious errors. + * 31.Jan.96 log_error reports about a malformed .meta file, rather + * than a script error. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include <sys/types.h> +#include <sys/stat.h> +#include "util_script.h" +#include "http_log.h" +#include "http_request.h" + +#define DEFAULT_METADIR ".web" +#define DEFAULT_METASUFFIX ".meta" + +module cern_meta_module; + +typedef struct { + char *metadir; + char *metasuffix; +} cern_meta_config; + +void *create_cern_meta_config (pool *p, server_rec *dummy) +{ + cern_meta_config *new = + (cern_meta_config *) palloc (p, sizeof(cern_meta_config)); + + new->metadir = DEFAULT_METADIR; + new->metasuffix = DEFAULT_METASUFFIX; + + return new; +} + +const char *set_metadir (cmd_parms *parms, void *dummy, char *arg) +{ + cern_meta_config *cmc ; + + cmc = get_module_config (parms->server->module_config, + &cern_meta_module); + cmc->metadir = arg; + return NULL; +} + +const char *set_metasuffix (cmd_parms *parms, void *dummy, char *arg) +{ + cern_meta_config *cmc ; + + cmc = get_module_config (parms->server->module_config, + &cern_meta_module); + cmc->metasuffix = arg; + return NULL; +} + +command_rec cern_meta_cmds[] = { +{ "MetaDir", set_metadir, NULL, RSRC_CONF, TAKE1, + "the name of the directory containing meta files"}, +{ "MetaSuffix", set_metasuffix, NULL, RSRC_CONF, TAKE1, + "the filename suffix for meta files"}, +{ NULL } +}; + +int scan_meta_file(request_rec *r, FILE *f) +{ + char w[MAX_STRING_LEN]; + char *l; + int p; + + while( fgets(w, MAX_STRING_LEN-1, f) != NULL ) { + + /* 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(w[0] == '\0') { + return OK; + } + + /* if we see a bogus header don't ignore it. Shout and scream */ + + if(!(l = strchr(w,':'))) { + log_reason ("malformed header in meta file", r->filename, r); + return SERVER_ERROR; + } + + *l++ = '\0'; + while (*l && isspace (*l)) ++l; + + if(!strcasecmp(w,"Content-type")) { + + /* Nuke trailing whitespace */ + + char *endp = l + strlen(l) - 1; + while (endp > l && isspace(*endp)) *endp-- = '\0'; + + r->content_type = pstrdup (r->pool, l); + } + else if(!strcasecmp(w,"Status")) { + sscanf(l, "%d", &r->status); + r->status_line = pstrdup(r->pool, l); + } + else { + table_set (r->headers_out, w, l); + } + } + return OK; +} + +int add_cern_meta_data(request_rec *r) +{ + char *metafilename; + char *last_slash; + char *real_file; + char *scrap_book; + FILE *f; + cern_meta_config *cmc ; + int rv; + request_rec *rr; + + cmc = get_module_config (r->server->module_config, + &cern_meta_module); + + /* if ./.web/$1.meta exists then output 'asis' */ + + if (r->finfo.st_mode == 0) { + return DECLINED; + }; + + /* does uri end in a trailing slash? */ + if ( r->uri[strlen(r->uri) - 1] == '/' ) { + return DECLINED; + }; + + /* what directory is this file in? */ + scrap_book = pstrdup( r->pool, r->filename ); + /* skip leading slash, recovered in later processing */ + scrap_book++; + last_slash = strrchr( scrap_book, '/' ); + if ( last_slash != NULL ) { + /* skip over last slash */ + real_file = last_slash; + real_file++; + *last_slash = '\0'; + } else { + /* no last slash, buh?! */ + log_reason("internal error in mod_cern_meta", r->filename, r); + /* should really barf, but hey, let's be friends... */ + return DECLINED; + }; + + metafilename = pstrcat(r->pool, "/", scrap_book, "/", cmc->metadir, "/", real_file, cmc->metasuffix, NULL); + + /* XXX: it sucks to require this subrequest to complete, because this + * means people must leave their meta files accessible to the world. + * A better solution might be a "safe open" feature of pfopen to avoid + * pipes, symlinks, and crap like that. + */ + rr = sub_req_lookup_file (metafilename, r); + if (rr->status != HTTP_OK) { + destroy_sub_req (rr); + return DECLINED; + } + destroy_sub_req (rr); + + f = pfopen (r->pool, metafilename, "r"); + if (f == NULL) { + if (errno == ENOENT) { + return DECLINED; + } + log_reason("meta file permissions deny server access", metafilename, r); + return FORBIDDEN; + }; + + /* read the headers in */ + rv = scan_meta_file(r, f); + pfclose( r->pool, f ); + + return rv; +} + +module cern_meta_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_cern_meta_config, /* server config */ + NULL, /* merge server configs */ + cern_meta_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + add_cern_meta_data, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_cgi.c b/usr.sbin/httpd/src/mod_cgi.c new file mode 100644 index 00000000000..6931d128878 --- /dev/null +++ b/usr.sbin/httpd/src/mod_cgi.c @@ -0,0 +1,575 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_script: keeps all script-related ramblings together. + * + * Compliant to CGI/1.1 spec + * + * Adapted by rst from original NCSA code by Rob McCool + * + * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for + * custom error responses, and DOCUMENT_ROOT because we found it useful. + * It also adds SERVER_ADMIN - useful for scripts to know who to mail when + * they fail. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" +#include "http_conf_globals.h" + +module cgi_module; + +/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI + * in ScriptAliased directories, which means we need to know if this + * request came through ScriptAlias or not... so the Alias module + * leaves a note for us. + */ + +int is_scriptaliased (request_rec *r) +{ + char *t = table_get (r->notes, "alias-forced-type"); + return t && (!strcasecmp (t, "cgi-script")); +} + +/* Configuration stuff */ + +#define DEFAULT_LOGBYTES 10385760 +#define DEFAULT_BUFBYTES 1024 + +typedef struct { + char *logname; + long logbytes; + int bufbytes; +} cgi_server_conf; + +void *create_cgi_config (pool *p, server_rec *s) +{ + cgi_server_conf *c = + (cgi_server_conf *)pcalloc (p, sizeof(cgi_server_conf)); + + c->logname = NULL; + c->logbytes = DEFAULT_LOGBYTES; + c->bufbytes = DEFAULT_BUFBYTES; + + return c; +} + +void *merge_cgi_config (pool *p, void *basev, void *overridesv) +{ + cgi_server_conf *base = (cgi_server_conf *)basev, + *overrides = (cgi_server_conf *)overridesv; + + return overrides->logname ? overrides : base; +} + +const char *set_scriptlog (cmd_parms *cmd, void *dummy, char *arg) { + server_rec *s = cmd->server; + cgi_server_conf *conf = + (cgi_server_conf *)get_module_config(s->module_config, &cgi_module); + + conf->logname = arg; + return NULL; +} + +const char *set_scriptlog_length (cmd_parms *cmd, void *dummy, char *arg) { + server_rec *s = cmd->server; + cgi_server_conf *conf = + (cgi_server_conf *)get_module_config(s->module_config, &cgi_module); + + conf->logbytes = atol (arg); + return NULL; +} + +const char *set_scriptlog_buffer (cmd_parms *cmd, void *dummy, char *arg) { + server_rec *s = cmd->server; + cgi_server_conf *conf = + (cgi_server_conf *)get_module_config(s->module_config, &cgi_module); + + conf->bufbytes = atoi (arg); + return NULL; +} + +command_rec cgi_cmds[] = { +{ "ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1, + "the name of a log for script debugging info"}, +{ "ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1, + "the maximum length (in bytes) of the script debug log"}, +{ "ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1, + "the maximum size (in bytes) to record of a POST request"}, +{ NULL} +}; + +static int log_scripterror(request_rec *r, cgi_server_conf *conf, int ret, + char *error) +{ + FILE *f; + + log_reason(error, r->filename, r); + + if (!conf->logname || + ((stat(server_root_relative(r->pool, conf->logname), &r->finfo) == 0) + && (r->finfo.st_size > conf->logbytes)) || + ((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname), + "a")) == NULL)) { + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ + fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/etc/httpd/cgi-bin */ + fprintf(f, "%%%% %d %s\n", ret, r->filename); + + fprintf(f, "%%error\n%s\n", error); + + pfclose(r->pool, f); + return ret; +} + +static int log_script(request_rec *r, cgi_server_conf *conf, int ret, + char *dbuf, char *sbuf, FILE *script_in, FILE *script_err) +{ + table *hdrs_arr = r->headers_in; + table_entry *hdrs = (table_entry *)hdrs_arr->elts; + char argsbuffer[HUGE_STRING_LEN]; + FILE *f; + int i; + + if (!conf->logname || + ((stat(server_root_relative(r->pool, conf->logname), &r->finfo) == 0) + && (r->finfo.st_size > conf->logbytes)) || + ((f = pfopen(r->pool, server_root_relative(r->pool, conf->logname), + "a")) == NULL)) { + /* Soak up script output */ + while (fgets(argsbuffer, MAX_STRING_LEN-1, script_in) != NULL) + continue; + while (fgets(argsbuffer, MAX_STRING_LEN-1, script_err) != NULL) + continue; + return ret; + } + + /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ + fprintf(f, "%%%% [%s] %s %s%s%s %s\n", get_time(), r->method, r->uri, + r->args ? "?" : "", r->args ? r->args : "", r->protocol); + /* "%% 500 /usr/local/etc/httpd/cgi-bin */ + fprintf(f, "%%%% %d %s\n", ret, r->filename); + + fputs("%request\n", f); + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) continue; + fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + if ((r->method_number == M_POST || r->method_number == M_PUT) + && dbuf && *dbuf) { + fprintf(f, "\n%s\n", dbuf); + } + + fputs("%response\n", f); + hdrs_arr = r->err_headers_out; + hdrs = (table_entry *)hdrs_arr->elts; + + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (!hdrs[i].key) continue; + fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); + } + + if (sbuf && *sbuf) + fprintf(f, "%s\n", sbuf); + + *argsbuffer = '\0'; + fgets(argsbuffer, HUGE_STRING_LEN-1, script_in); + if (*argsbuffer) { + fputs("%stdout\n", f); + fputs(argsbuffer, f); + while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) + fputs(argsbuffer, f); + fputs("\n", f); + } + + *argsbuffer = '\0'; + fgets(argsbuffer, HUGE_STRING_LEN-1, script_err); + if (*argsbuffer) { + fputs("%stderr\n", f); + fputs(argsbuffer, f); + while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_err) != NULL) + fputs(argsbuffer, f); + fputs("\n", f); + } + + pfclose(r->main ? r->main->pool : r->pool, script_in); + pfclose(r->main ? r->main->pool : r->pool, script_err); + + pfclose(r->pool, f); + return ret; +} + +/**************************************************************** + * + * Actual CGI handling... + */ + + +struct cgi_child_stuff { + request_rec *r; + int nph; + int debug; + char *argv0; +}; + +void cgi_child (void *child_stuff) +{ + struct cgi_child_stuff *cld = (struct cgi_child_stuff *)child_stuff; + request_rec *r = cld->r; + char *argv0 = cld->argv0; + int nph = cld->nph; + +#ifdef DEBUG_CGI +#ifdef __EMX__ + /* Under OS/2 need to use device con. */ + FILE *dbg = fopen ("con", "w"); +#else + FILE *dbg = fopen ("/dev/tty", "w"); +#endif + int i; +#endif + + char **env; + char err_string[HUGE_STRING_LEN]; + +#ifdef DEBUG_CGI + fprintf (dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n", + r->filename, nph ? "NPH " : "", argv0); +#endif + + add_cgi_vars (r); + env = create_environment (r->pool, r->subprocess_env); + +#ifdef DEBUG_CGI + fprintf (dbg, "Environment: \n"); + for (i = 0; env[i]; ++i) fprintf (dbg, "'%s'\n", env[i]); +#endif + + chdir_file (r->filename); + if (!cld->debug) + error_log2stderr (r->server); + +#ifndef __EMX__ + if (nph) client_to_stdout (r->connection); +#endif + + /* Transumute outselves into the script. + * NB only ISINDEX scripts get decoded arguments. + */ + + cleanup_for_exec(); + + call_exec(r, argv0, env, 0); + + /* Uh oh. Still here. Where's the kaboom? There was supposed to be an + * EARTH-shattering kaboom! + * + * Oh, well. Muddle through as best we can... + * + * (NB we can't use log_error, or anything like that, because we + * just closed the file descriptor which r->server->error_log + * was tied to in cleanup_for_exec(). It's only available on stderr + * now, so that's what we use). + */ + + ap_snprintf(err_string, sizeof(err_string), + "exec of %s failed, reason: %s (errno = %d)\n", + r->filename, strerror(errno), errno); + write(2, err_string, strlen(err_string)); + exit(0); +} + +int cgi_handler (request_rec *r) +{ + int retval, nph, dbpos = 0; + char *argv0, *dbuf = NULL; + FILE *script_out, *script_in, *script_err; + char argsbuffer[HUGE_STRING_LEN]; + int is_included = !strcmp (r->protocol, "INCLUDED"); + void *sconf = r->server->module_config; + cgi_server_conf *conf = + (cgi_server_conf *)get_module_config(sconf, &cgi_module); + + struct cgi_child_stuff cld; + pid_t child_pid; + + if (r->method_number == M_OPTIONS) { + /* 99 out of 100 CGI scripts, this is all they support */ + r->allowed |= (1 << M_GET); + r->allowed |= (1 << M_POST); + return DECLINED; + } + + if((argv0 = strrchr(r->filename,'/')) != NULL) + argv0++; + else argv0 = r->filename; + + nph = !(strncmp(argv0,"nph-",4)); + + if (!(allow_options (r) & OPT_EXECCGI) && !is_scriptaliased (r)) + return log_scripterror(r, conf, FORBIDDEN, + "Options ExecCGI is off in this directory"); + if (nph && is_included) + return log_scripterror(r, conf, FORBIDDEN, + "attempt to include NPH CGI script"); + + if (S_ISDIR(r->finfo.st_mode)) + return log_scripterror(r, conf, FORBIDDEN, + "attempt to invoke directory as script"); +#ifdef __EMX__ + /* Allow for cgi files without the .EXE extension on them under OS/2 */ + if (r->finfo.st_mode == 0) { + struct stat statbuf; + + r->filename = pstrcat (r->pool, r->filename, ".EXE", NULL); + + if ((stat(r->filename, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) { + return log_scripterror(r, conf, NOT_FOUND, + "script not found or unable to stat"); + } + } +#else + if (r->finfo.st_mode == 0) + return log_scripterror(r, conf, NOT_FOUND, + "script not found or unable to stat"); +#endif + if (!suexec_enabled) { + if (!can_exec(&r->finfo)) + return log_scripterror(r, conf, FORBIDDEN, + "file permissions deny server execution"); + } + + if ((retval = setup_client_block(r, REQUEST_CHUNKED_ERROR))) + return retval; + + add_common_vars (r); + cld.argv0 = argv0; cld.r = r; cld.nph = nph; + cld.debug = conf->logname ? 1 : 0; + + if (!(child_pid = + /* + * we spawn out of r->main if it's there so that we can avoid + * waiting for free_proc_chain to cleanup in the middle of an + * SSI request -djg + */ + spawn_child_err (r->main ? r->main->pool : r->pool, cgi_child, + (void *)&cld, + nph ? just_wait : kill_after_timeout, +#ifdef __EMX__ + &script_out, &script_in, &script_err))) { +#else + &script_out, nph ? NULL : &script_in, + &script_err))) { +#endif + log_reason ("couldn't spawn child process", r->filename, r); + return SERVER_ERROR; + } + + /* Transfer any put/post args, CERN style... + * Note that if a buggy script fails to read everything we throw + * at it, or a buggy client sends too much, we get a SIGPIPE, so + * we have to ignore SIGPIPE while doing this. CERN does the same + * (and in fact, they pretty nearly guarantee themselves a SIGPIPE + * on every invocation by chasing the real client data with a + * spurious newline). + */ + + if (should_client_block(r)) { + void (*handler)(); + int dbsize, len_read; + + if (conf->logname) { + dbuf = pcalloc(r->pool, conf->bufbytes+1); + dbpos = 0; + } + + hard_timeout ("copy script args", r); + handler = signal (SIGPIPE, SIG_IGN); + + while ((len_read = + get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) + { + if (conf->logname) { + if ((dbpos + len_read) > conf->bufbytes) { + dbsize = conf->bufbytes - dbpos; + } + else { + dbsize = len_read; + } + memcpy(dbuf + dbpos, argsbuffer, dbsize); + dbpos += dbsize; + } + reset_timeout(r); + if (fwrite(argsbuffer, sizeof(char), len_read, script_out) + < (size_t)len_read) { + /* silly script stopped reading, soak up remaining message */ + while (get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) + ; /* dump it */ + break; + } + } + + fflush (script_out); + signal (SIGPIPE, handler); + + kill_timeout (r); + } + + pfclose (r->main ? r->main->pool : r->pool, script_out); + + /* Handle script return... */ + if (script_in && !nph) { + char *location, sbuf[MAX_STRING_LEN]; + int ret; + + if ((ret = scan_script_header_err(r, script_in, sbuf))) + return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err); + + location = table_get (r->headers_out, "Location"); + + if (location && location[0] == '/' && r->status == 200) { + + /* Soak up all the script output */ + hard_timeout ("read from script", r); + while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_in) + > 0) + continue; + while (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err) + > 0) + continue; + kill_timeout (r); + + + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + /* We already read the message body (if any), so don't allow + * the redirected request to think it has one. We can ignore + * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. + */ + table_unset(r->headers_in, "Content-Length"); + + internal_redirect_handler (location, r); + return OK; + } + else if (location && r->status == 200) { + /* XX Note that if a script wants to produce its own Redirect + * body, it now has to explicitly *say* "Status: 302" + */ + return REDIRECT; + } + + send_http_header(r); + if (!r->header_only) + send_fd(script_in, r); + pfclose (r->main ? r->main->pool : r->pool, script_in); + + /* Soak up stderr */ + soft_timeout("soaking script stderr", r); + while (!r->connection->aborted && + (fread(argsbuffer, sizeof(char), HUGE_STRING_LEN, script_err) > 0)) + continue; + kill_timeout(r); + pfclose (r->main ? r->main->pool : r->pool, script_err); + } + + if (nph) { +#ifdef __EMX__ + while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) { + bputs(argsbuffer, r->connection->client); + } +#else + waitpid(child_pid, (int*)0, 0); +#endif + } + + return OK; /* NOT r->status, even if it has changed. */ +} + +handler_rec cgi_handlers[] = { +{ CGI_MAGIC_TYPE, cgi_handler }, +{ "cgi-script", cgi_handler }, +{ NULL } +}; + +module cgi_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_cgi_config, /* server config */ + merge_cgi_config, /* merge server config */ + cgi_cmds, /* command table */ + cgi_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_digest.c b/usr.sbin/httpd/src/mod_digest.c new file mode 100644 index 00000000000..814f416d3e2 --- /dev/null +++ b/usr.sbin/httpd/src/mod_digest.c @@ -0,0 +1,363 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * mod_digest: MD5 digest authentication + * + * by Alexei Kosut <akosut@nueva.pvt.k12.ca.us> + * based on mod_auth, by Rob McCool and Robert S. Thau + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "util_md5.h" + +typedef struct digest_config_struct { + char *pwfile; +} digest_config_rec; + +typedef struct digest_header_struct { + char *username; + char *realm; + char *nonce; + char *requested_uri; + char *digest; +} digest_header_rec; + +void *create_digest_dir_config (pool *p, char *d) +{ + return pcalloc (p, sizeof(digest_config_rec)); +} + +const char *set_digest_slot (cmd_parms *cmd, void *offset, char *f, char *t) +{ + if (t && strcmp(t, "standard")) + return pstrcat(cmd->pool, "Invalid auth file type: ", t, NULL); + + return set_string_slot(cmd, offset, f); +} + +command_rec digest_cmds[] = { +{ "AuthDigestFile", set_digest_slot, + (void*)XtOffsetOf(digest_config_rec,pwfile), OR_AUTHCFG, TAKE12, NULL }, +{ NULL } +}; + +module digest_module; + +char *get_hash(request_rec *r, char *user, char *auth_pwfile) +{ + FILE *f; + char l[MAX_STRING_LEN]; + const char *rpw; + char *w, *x; + + if(!(f=pfopen(r->pool, auth_pwfile, "r"))) { + log_reason ("Could not open password file", auth_pwfile, r); + return NULL; + } + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + if((l[0] == '#') || (!l[0])) continue; + rpw = l; + w = getword(r->pool, &rpw, ':'); + x = getword(r->pool, &rpw, ':'); + + if(x && w && !strcmp(user,w) && !strcmp(auth_name(r), x)) { + pfclose(r->pool, f); + return pstrdup (r->pool, rpw); + } + } + pfclose(r->pool, f); + return NULL; +} + +/* Parse the Authorization header, if it exists */ + +int get_digest_rec(request_rec *r, digest_header_rec *response) { + const char *auth_line = table_get(r->headers_in, "Authorization"); + int l; + int s = 0, vk = 0, vv = 0; + char *t, *key, *value; + + if (!(t = auth_type(r)) || strcasecmp(t, "Digest")) + return DECLINED; + + if (!auth_name (r)) { + log_reason ("need AuthName", r->uri, r); + return SERVER_ERROR; + } + + if (!auth_line) { + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + + if (strcasecmp(getword (r->pool, &auth_line, ' '), "Digest")) { + /* Client tried to authenticate using wrong auth scheme */ + log_reason ("client used wrong authentication scheme", r->uri, r); + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + + l = strlen(auth_line); + + key=palloc(r->pool,l); + value=palloc(r->pool,l); + + /* There's probably a better way to do this, but for the time being... */ + +#define D_KEY 0 +#define D_VALUE 1 +#define D_STRING 2 +#define D_EXIT -1 + + while (s != D_EXIT) { + switch (s) { + case D_STRING: + if (auth_line[0] == '\"') { + s = D_VALUE; + } + else { + value[vv] = auth_line[0]; + vv++; + } + auth_line++; + break; + + case D_VALUE: + if (isalnum(auth_line[0])) { + value[vv] = auth_line[0]; + vv++; + } + else if (auth_line[0] == '\"') { + s = D_STRING; + } + else { + value[vv] = '\0'; + + if (!strcasecmp(key, "username")) + response->username = pstrdup(r->pool, value); + else if (!strcasecmp(key, "realm")) + response->realm = pstrdup(r->pool, value); + else if (!strcasecmp(key, "nonce")) + response->nonce = pstrdup(r->pool, value); + else if (!strcasecmp(key, "uri")) + response->requested_uri = pstrdup(r->pool, value); + else if (!strcasecmp(key, "response")) + response->digest = pstrdup(r->pool, value); + + vv = 0; + s = D_KEY; + } + auth_line++; + break; + + case D_KEY: + if (isalnum(auth_line[0])) { + key[vk] = auth_line[0]; + vk++; + } + else if (auth_line[0] == '=') { + key[vk] = '\0'; + vk = 0; + s = D_VALUE; + } + auth_line++; + break; + } + + if (auth_line[-1] == '\0') + s = D_EXIT; + } + + if (!response->username || !response->realm || !response->nonce || + !response->requested_uri || !response->digest) { + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + + r->connection->user = response->username; + r->connection->auth_type = "Digest"; + + return OK; +} + +/* The actual MD5 code... whee */ + +char *find_digest(request_rec *r, digest_header_rec *h, char *a1) { + return md5(r->pool, + (unsigned char *)pstrcat(r->pool, a1, ":", h->nonce, ":", + md5(r->pool, + (unsigned char *)pstrcat(r->pool,r->method,":", + h->requested_uri,NULL)), + NULL)); +} + +/* These functions return 0 if client is OK, and proper error status + * if not... either AUTH_REQUIRED, if we made a check, and it failed, or + * SERVER_ERROR, if things are so totally confused that we couldn't + * figure out how to tell if the client is authorized or not. + * + * If they return DECLINED, and all other modules also decline, that's + * treated by the server core as a configuration error, logged and + * reported as such. + */ + +/* Determine user ID, and check if it really is that user, for HTTP + * basic authentication... + */ + +int authenticate_digest_user (request_rec *r) +{ + digest_config_rec *sec = + (digest_config_rec *)get_module_config (r->per_dir_config, + &digest_module); + digest_header_rec *response = pcalloc (r->pool, sizeof(digest_header_rec)); + conn_rec *c = r->connection; + char *a1; + char errstr[MAX_STRING_LEN]; + int res; + + if ((res = get_digest_rec (r, response))) return res; + + if(!sec->pwfile) + return DECLINED; + + if (!(a1 = get_hash(r, c->user, sec->pwfile))) { + ap_snprintf(errstr, sizeof(errstr), "user %s not found",c->user); + log_reason (errstr, r->uri, r); + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + /* anyone know where the prototype for crypt is? */ + if(strcmp(response->digest, find_digest(r, response, a1))) { + ap_snprintf(errstr, sizeof(errstr), "user %s: password mismatch",c->user); + log_reason (errstr, r->uri, r); + note_digest_auth_failure (r); + return AUTH_REQUIRED; + } + return OK; +} + +/* Checking ID */ + +int digest_check_auth (request_rec *r) { + char *user = r->connection->user; + int m = r->method_number; + int method_restricted = 0; + register int x; + const char *t; + char *w; + array_header *reqs_arr; + require_line *reqs; + + if (!(t = auth_type(r)) || strcasecmp(t, "Digest")) + return DECLINED; + + reqs_arr = requires (r); + /* If there is no "requires" directive, + * then any user will do. + */ + if (!reqs_arr) + return OK; + reqs = (require_line *)reqs_arr->elts; + + for(x=0; x < reqs_arr->nelts; x++) { + + if (! (reqs[x].method_mask & (1 << m))) continue; + + method_restricted = 1; + + t = reqs[x].requirement; + w = getword(r->pool, &t, ' '); + if(!strcmp(w,"valid-user")) + return OK; + else if(!strcmp(w,"user")) { + while(t[0]) { + w = getword_conf (r->pool, &t); + if(!strcmp(user,w)) + return OK; + } + } + else + return DECLINED; + } + + if (!method_restricted) + return OK; + + note_digest_auth_failure(r); + return AUTH_REQUIRED; +} + +module digest_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_digest_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + digest_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + authenticate_digest_user, /* check_user_id */ + digest_check_auth, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_dir.c b/usr.sbin/httpd/src/mod_dir.c new file mode 100644 index 00000000000..435b5d3378f --- /dev/null +++ b/usr.sbin/httpd/src/mod_dir.c @@ -0,0 +1,911 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_dir.c: Handles the on-the-fly html index generation + * + * Rob McCool + * 3/23/93 + * + * Adapted to Apache by rst. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_request.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" + +module dir_module; + +/**************************************************************** + * + * Handling configuration directives... + */ + +#define FANCY_INDEXING 1 /* Indexing options */ +#define ICONS_ARE_LINKS 2 +#define SCAN_HTML_TITLES 4 +#define SUPPRESS_LAST_MOD 8 +#define SUPPRESS_SIZE 16 +#define SUPPRESS_DESC 32 + +struct item { + char *type; + char *apply_to; + char *apply_path; + char *data; +}; + +typedef struct dir_config_struct { + + char *default_icon; + char *index_names; + + array_header *icon_list, *alt_list, *desc_list, *ign_list; + array_header *hdr_list, *rdme_list, *opts_list; + +} dir_config_rec; + +char c_by_encoding, c_by_type, c_by_path; + +#define BY_ENCODING &c_by_encoding +#define BY_TYPE &c_by_type +#define BY_PATH &c_by_path + +void push_item(array_header *arr, char *type, char *to, char *path, char *data) +{ + struct item *p = (struct item *)push_array(arr); + + if (!to) to = ""; + if (!path) path = ""; + + p->type = type; + p->data = data ? pstrdup(arr->pool, data): NULL; + p->apply_path = pstrcat(arr->pool, path, "*", NULL); + + if((type == BY_PATH) && (!is_matchexp(to))) + p->apply_to = pstrcat (arr->pool, "*", to, NULL); + else if (to) + p->apply_to = pstrdup (arr->pool, to); + else + p->apply_to = NULL; +} + +const char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to) +{ + if (cmd->info == BY_PATH) + if(!strcmp(to,"**DIRECTORY**")) + to = "^^DIRECTORY^^"; + + push_item(((dir_config_rec *)d)->alt_list, cmd->info, to, cmd->path, alt); + return NULL; +} + +const char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to) +{ + char *iconbak = pstrdup (cmd->pool, icon); + + if(icon[0] == '(') { + char *alt = getword_nc (cmd->pool, &iconbak, ','); + iconbak[strlen(iconbak) - 1] = '\0'; /* Lose closing paren */ + add_alt(cmd, d, &alt[1], to); + } + if(cmd->info == BY_PATH) + if(!strcmp(to,"**DIRECTORY**")) + to = "^^DIRECTORY^^"; + + push_item(((dir_config_rec *)d)->icon_list, cmd->info, to, cmd->path, + iconbak); + return NULL; +} + +const char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to) +{ + push_item(((dir_config_rec *)d)->desc_list, cmd->info, to, cmd->path,desc); + return NULL; +} + +const char *add_ignore(cmd_parms *cmd, void *d, char *ext) { + push_item(((dir_config_rec *)d)->ign_list, 0, ext, cmd->path, NULL); + return NULL; +} + +const char *add_header(cmd_parms *cmd, void *d, char *name) { + if (strchr (name, '/')) { + return "HeaderName cannot contain a /"; + } + push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name); + return NULL; +} + +const char *add_readme(cmd_parms *cmd, void *d, char *name) { + if (strchr (name, '/')) { + return "ReadmeName cannot contain a /"; + } + push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name); + return NULL; +} + + +const char *add_opts_int(cmd_parms *cmd, void *d, int opts) { + push_item(((dir_config_rec *)d)->opts_list, (char*)opts, NULL, + cmd->path, NULL); + return NULL; +} + +const char *fancy_indexing (cmd_parms *cmd, void *d, int arg) +{ + return add_opts_int (cmd, d, arg? FANCY_INDEXING : 0); +} + +const char *add_opts(cmd_parms *cmd, void *d, const char *optstr) { + char *w; + int opts = 0; + + while(optstr[0]) { + w = getword_conf(cmd->pool, &optstr); + if(!strcasecmp(w,"FancyIndexing")) + opts |= FANCY_INDEXING; + else if(!strcasecmp(w,"IconsAreLinks")) + opts |= ICONS_ARE_LINKS; + else if(!strcasecmp(w,"ScanHTMLTitles")) + opts |= SCAN_HTML_TITLES; + else if(!strcasecmp(w,"SuppressLastModified")) + opts |= SUPPRESS_LAST_MOD; + else if(!strcasecmp(w,"SuppressSize")) + opts |= SUPPRESS_SIZE; + else if(!strcasecmp(w,"SuppressDescription")) + opts |= SUPPRESS_DESC; + else if(!strcasecmp(w,"None")) + opts = 0; + else + return "Invalid directory indexing option"; + } + return add_opts_int(cmd, d, opts); +} + +#define DIR_CMD_PERMS OR_INDEXES + +command_rec dir_cmds[] = { +{ "AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more filenames" }, +{ "AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more MIME types" }, +{ "AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "an icon URL followed by one or more content encodings" }, +{ "AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more filenames" }, +{ "AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more MIME types" }, +{ "AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2, + "alternate descriptive text followed by one or more content encodings" }, +{ "IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS, + "one or more index options" }, +{ "IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE, + "one or more file extensions" }, +{ "AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2, + "Descriptive text followed by one or more filenames" }, +{ "HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename" }, +{ "ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename" }, +{ "FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG, NULL }, +{ "DefaultIcon", set_string_slot, + (void*)XtOffsetOf(dir_config_rec, default_icon), + DIR_CMD_PERMS, TAKE1, "an icon URL"}, +{ "DirectoryIndex", set_string_slot, + (void*)XtOffsetOf(dir_config_rec, index_names), + DIR_CMD_PERMS, RAW_ARGS, NULL }, +{ NULL } +}; + +void *create_dir_config (pool *p, char *dummy) +{ + dir_config_rec *new = + (dir_config_rec *) pcalloc (p, sizeof(dir_config_rec)); + + new->index_names = NULL; + new->icon_list = make_array (p, 4, sizeof (struct item)); + new->alt_list = make_array (p, 4, sizeof (struct item)); + new->desc_list = make_array (p, 4, sizeof (struct item)); + new->ign_list = make_array (p, 4, sizeof (struct item)); + new->hdr_list = make_array (p, 4, sizeof (struct item)); + new->rdme_list = make_array (p, 4, sizeof (struct item)); + new->opts_list = make_array (p, 4, sizeof (struct item)); + + return (void *)new; +} + +void *merge_dir_configs (pool *p, void *basev, void *addv) +{ + dir_config_rec *new=(dir_config_rec*)pcalloc (p, sizeof(dir_config_rec)); + dir_config_rec *base = (dir_config_rec *)basev; + dir_config_rec *add = (dir_config_rec *)addv; + + new->default_icon = add->default_icon?add->default_icon:base->default_icon; + new->index_names = add->index_names? add->index_names: base->index_names; + + new->alt_list = append_arrays (p, add->alt_list, base->alt_list); + new->ign_list = append_arrays (p, add->ign_list, base->ign_list); + new->hdr_list = append_arrays (p, add->hdr_list, base->hdr_list); + new->desc_list = append_arrays (p, add->desc_list, base->desc_list); + new->icon_list = append_arrays (p, add->icon_list, base->icon_list); + new->rdme_list = append_arrays (p, add->rdme_list, base->rdme_list); + new->opts_list = append_arrays (p, add->opts_list, base->opts_list); + + return new; +} + +/**************************************************************** + * + * Looking things up in config entries... + */ + +/* Structure used to hold entries when we're actually building an index */ + +struct ent { + char *name; + char *icon; + char *alt; + char *desc; + size_t size; + time_t lm; + struct ent *next; +}; + +char *find_item(request_rec *r, array_header *list, int path_only) { + char *content_type = r->content_type; + char *content_encoding = r->content_encoding; + char *path = r->filename; + + struct item *items = (struct item *)list->elts; + int i; + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + + /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */ + if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) { + if(!*(p->apply_to)) + return p->data; + else if(p->type == BY_PATH || path[0] == '^') { + if(!strcmp_match(path,p->apply_to)) + return p->data; + } else if(!path_only) { + if(!content_encoding) { + if(p->type == BY_TYPE) { + if(content_type && !strcmp_match(content_type,p->apply_to)) + return p->data; + } + } else { + if(p->type == BY_ENCODING) { + if(!strcmp_match(content_encoding,p->apply_to)) + return p->data; + } + } + } + } + } + return NULL; +} + +#define find_icon(d,p,t) find_item(p,d->icon_list,t) +#define find_alt(d,p,t) find_item(p,d->alt_list,t) +#define find_desc(d,p) find_item(p,d->desc_list,0) +#define find_header(d,p) find_item(p,d->hdr_list,0) +#define find_readme(d,p) find_item(p,d->rdme_list,0) + +char *find_default_icon (dir_config_rec *d, char *bogus_name) +{ + request_rec r; + + /* Bleah. I tried to clean up find_item, and it lead to this bit + * of ugliness. Note that the fields initialized are precisely + * those that find_item looks at... + */ + + r.filename = bogus_name; + r.content_type = r.content_encoding = NULL; + + return find_item (&r, d->icon_list, 1); +} + +int ignore_entry(dir_config_rec *d, char *path) { + array_header *list = d->ign_list; + struct item *items = (struct item *)list->elts; + char *tt; + int i; + + if((tt=strrchr(path,'/')) == NULL) + tt=path; + else { + tt++; + } + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + char *ap; + + if((ap=strrchr(p->apply_to,'/')) == NULL) + ap=p->apply_to; + else + ap++; + + if(!strcmp_match(path,p->apply_path) && !strcmp_match(tt,ap)) + return 1; + } + return 0; +} + +int find_opts(dir_config_rec *d, request_rec *r) { + char *path = r->filename; + array_header *list = d->opts_list; + struct item *items = (struct item *)list->elts; + int i; + + for (i = 0; i < list->nelts; ++i) { + struct item *p = &items[i]; + + if(!strcmp_match(path,p->apply_path)) + return (int)p->type; + } + return 0; +} + +/***************************************************************** + * + * Actually generating output + */ + + +int insert_readme(char *name, char *readme_fname, int rule, request_rec *r) { + char *fn; + FILE *f; + struct stat finfo; + int plaintext=0; + request_rec *rr; + + /* XXX: this is a load of crap, it needs to do a full sub_req_lookup_uri */ + fn = make_full_path(r->pool, name, readme_fname); + fn = pstrcat(r->pool, fn, ".html", NULL); + if(stat(fn,&finfo) == -1) { + /* A brief fake multiviews search for README.html */ + fn[strlen(fn)-5] = '\0'; + if(stat(fn,&finfo) == -1) + return 0; + plaintext=1; + if(rule) rputs("<HR>\n", r); + rputs("<PRE>\n", r); + } + else if (rule) rputs("<HR>\n", r); + /* XXX: when the above is rewritten properly, this necessary security + * check will be redundant. -djg */ + rr = sub_req_lookup_file (fn, r); + if (rr->status != HTTP_OK) { + destroy_sub_req (rr); + return 0; + } + destroy_sub_req (rr); + if(!(f = pfopen(r->pool,fn,"r"))) + return 0; + if (!plaintext) + send_fd(f, r); + else + { + char buf[IOBUFSIZE+1]; + int i, n, c, ch; + while (!feof(f)) + { + do n = fread(buf, sizeof(char), IOBUFSIZE, f); + while (n == -1 && ferror(f) && errno == EINTR); + if (n == -1 || n == 0) break; + buf[n] = '\0'; + c = 0; + while (c < n) + { + for (i=c; i < n; i++) + if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') break; + ch = buf[i]; + buf[i] = '\0'; + rputs(&buf[c], r); + if (ch == '<') rputs("<", r); + else if (ch == '>') rputs(">", r); + else if (ch == '&') rputs("&", r); + c = i + 1; + } + } + } + pfclose(r->pool, f); + if(plaintext) + rputs("</PRE>\n", r); + return 1; +} + + +char *find_title(request_rec *r) { + char titlebuf[MAX_STRING_LEN], *find = "<TITLE>"; + FILE *thefile = NULL; + int x,y,n,p; + + if (r->status != HTTP_OK) { + return NULL; + } + if (r->content_type && !strcasecmp(r->content_type,"text/html") && !r->content_encoding) { + if(!(thefile = pfopen(r->pool, r->filename,"r"))) + return NULL; + n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile); + titlebuf[n] = '\0'; + for(x=0,p=0;titlebuf[x];x++) { + if(toupper(titlebuf[x]) == find[p]) { + if(!find[++p]) { + if((p = ind(&titlebuf[++x],'<')) != -1) + titlebuf[x+p] = '\0'; + /* Scan for line breaks for Tanmoy's secretary */ + for(y=x;titlebuf[y];y++) + if((titlebuf[y] == CR) || (titlebuf[y] == LF)) + titlebuf[y] = ' '; + pfclose (r->pool, thefile); + return pstrdup(r->pool, &titlebuf[x]); + } + } else p=0; + } + pfclose(r->pool, thefile); + } + return NULL; +} + +struct ent *make_dir_entry(char *name, int dir_opts, + dir_config_rec *d, request_rec *r) +{ + struct ent *p; + + if((name[0] == '.') && (!name[1])) + return(NULL); + + if (ignore_entry(d, make_full_path (r->pool, r->filename, name))) + return(NULL); + + p=(struct ent *)pcalloc(r->pool, sizeof(struct ent)); + p->name = pstrdup (r->pool, name); + p->size = -1; + p->icon = NULL; + p->alt = NULL; + p->desc = NULL; + p->lm = -1; + + if(dir_opts & FANCY_INDEXING) { + request_rec *rr = sub_req_lookup_file (name, r); + + if (rr->finfo.st_mode != 0) { + p->lm = rr->finfo.st_mtime; + if(S_ISDIR(rr->finfo.st_mode)) { + if(!(p->icon = find_icon(d,rr,1))) + p->icon = find_default_icon(d,"^^DIRECTORY^^"); + if(!(p->alt = find_alt(d,rr,1))) + p->alt = "DIR"; + p->size = -1; + p->name = pstrcat (r->pool, name, "/", NULL); + } + else { + p->icon = find_icon(d, rr, 0); + p->alt = find_alt(d, rr, 0); + p->size = rr->finfo.st_size; + } + } + + p->desc = find_desc(d, rr); + + if((!p->desc) && (dir_opts & SCAN_HTML_TITLES)) + p->desc = pstrdup (r->pool, find_title(rr)); + + destroy_sub_req (rr); + } + return(p); +} + +char *terminate_description(dir_config_rec *d, char *desc, int dir_opts) { + int maxsize = 23; + register int x; + + if(dir_opts & SUPPRESS_LAST_MOD) maxsize += 17; + if(dir_opts & SUPPRESS_SIZE) maxsize += 7; + + for(x=0;desc[x] && maxsize;x++) { + if(desc[x] == '<') { + while(desc[x] != '>') { + if(!desc[x]) { + maxsize = 0; + break; + } + ++x; + } + } + else --maxsize; + } + if(!maxsize) { + desc[x-1] = '>'; /* Grump. */ + desc[x] = '\0'; /* Double Grump! */ + } + return desc; +} + +void output_directories(struct ent **ar, int n, + dir_config_rec *d, request_rec *r, int dir_opts) +{ + int x, len; + char *name = r->uri; + char *tp; + pool *scratch = make_sub_pool (r->pool); + + if(name[0] == '\0') name = "/"; + + if(dir_opts & FANCY_INDEXING) { + rputs("<PRE>", r); + if((tp = find_default_icon(d,"^^BLANKICON^^"))) + rvputs(r, "<IMG SRC=\"", escape_html(scratch, tp), + "\" ALT=\" \"> ", NULL); + rputs("Name ", r); + if(!(dir_opts & SUPPRESS_LAST_MOD)) + rputs("Last modified ", r); + if(!(dir_opts & SUPPRESS_SIZE)) + rputs("Size ", r); + if(!(dir_opts & SUPPRESS_DESC)) + rputs("Description", r); + rputs("\n<HR>\n", r); + } + else { + rputs("<UL>", r); + } + + for(x=0;x<n;x++) { + char *anchor = NULL, *t = NULL, *t2 = NULL; + + clear_pool (scratch); + + if((!strcmp(ar[x]->name,"../")) || (!strcmp(ar[x]->name,".."))) { + char *t = make_full_path (scratch, name, "../"); + getparents(t); + if(t[0] == '\0') t = "/"; + anchor = pstrcat (scratch, "<A HREF=\"", + escape_html(scratch, os_escape_path(scratch, t, 0)), + "\">", NULL); + t2 = "Parent Directory</A> "; + } + else { + t = ar[x]->name; + len = strlen(t); + if(len > 23) { + t2 = pstrdup(scratch, t); + t2[21] = '.'; + t2[22] = '.'; + t2[23] = '\0'; + t2 = escape_html(scratch, t2); + t2 = pstrcat(scratch, t2, "</A>", NULL); + } else + { + char buff[24]=" "; + t2 = escape_html(scratch, t); + buff[23-len] = '\0'; + t2 = pstrcat(scratch, t2, "</A>", buff, NULL); + } + anchor = pstrcat (scratch, "<A HREF=\"", + escape_html(scratch, os_escape_path(scratch, t, 0)), + "\">", NULL); + } + + if(dir_opts & FANCY_INDEXING) { + if(dir_opts & ICONS_ARE_LINKS) + rputs(anchor, r); + if((ar[x]->icon) || d->default_icon) { + rvputs(r, "<IMG SRC=\"", + escape_html(scratch, ar[x]->icon ? + ar[x]->icon : d->default_icon), + "\" ALT=\"[", (ar[x]->alt ? ar[x]->alt : " "), + "]\">", NULL); + } + if(dir_opts & ICONS_ARE_LINKS) + rputs("</A>", r); + + rvputs(r," ", anchor, t2, NULL); + if(!(dir_opts & SUPPRESS_LAST_MOD)) { + if(ar[x]->lm != -1) { + char time[MAX_STRING_LEN]; + struct tm *ts = localtime(&ar[x]->lm); + strftime(time,MAX_STRING_LEN,"%d-%b-%y %H:%M ",ts); + rputs(time, r); + } + else { + rputs(" ", r); + } + } + if(!(dir_opts & SUPPRESS_SIZE)) { + send_size(ar[x]->size,r); + rputs(" ", r); + } + if(!(dir_opts & SUPPRESS_DESC)) { + if(ar[x]->desc) { + rputs(terminate_description(d, ar[x]->desc, dir_opts), r); + } + } + } + else + rvputs(r, "<LI> ", anchor," ", t2, NULL); + rputc('\n', r); + } + if(dir_opts & FANCY_INDEXING) { + rputs("</PRE>", r); + } + else { + rputs("</UL>", r); + } +} + + +int dsortf(struct ent **s1,struct ent **s2) +{ + return(strcmp((*s1)->name,(*s2)->name)); +} + + +int index_directory(request_rec *r, dir_config_rec *dir_conf) +{ + char *title_name = escape_html(r->pool, r->uri); + char *title_endp; + char *name = r->filename; + + DIR *d; + struct DIR_TYPE *dstruct; + int num_ent=0,x; + struct ent *head,*p; + struct ent **ar = NULL; + char *tmp; + int dir_opts = find_opts(dir_conf, r); + + if(!(d=opendir(name))) { + log_reason ("Can't open directory for index", r->filename, r); + return HTTP_FORBIDDEN; + } + + r->content_type = "text/html"; + + send_http_header(r); + + if (r->header_only) { + closedir (d); + return 0; + } + hard_timeout("send directory", r); + + /* Spew HTML preamble */ + + title_endp = title_name + strlen(title_name) - 1; + + while (title_endp > title_name && *title_endp == '/') + *title_endp-- = '\0'; + + rvputs + ( + r, + "<HTML><HEAD>\n<TITLE>Index of ", + title_name, + "</TITLE>\n</HEAD><BODY>\n", + NULL + ); + + if((!(tmp = find_header(dir_conf,r))) || (!(insert_readme(name,tmp,0,r)))) + rvputs(r, "<H1>Index of ", title_name, "</H1>\n", NULL); + + /* + * Since we don't know how many dir. entries there are, put them into a + * linked list and then arrayificate them so qsort can use them. + */ + head=NULL; + while((dstruct=readdir(d))) { + if((p = make_dir_entry(dstruct->d_name, dir_opts, dir_conf, r))) { + p->next=head; + head=p; + num_ent++; + } + } + if (num_ent > 0) { + ar=(struct ent **) palloc(r->pool, num_ent*sizeof(struct ent *)); + p=head; + x=0; + while(p) { + ar[x++]=p; + p = p->next; + } + + qsort((void *)ar,num_ent,sizeof(struct ent *), +#ifdef ULTRIX_BRAIN_DEATH + (int (*))dsortf); +#else + (int (*)(const void *,const void *))dsortf); +#endif + } + output_directories(ar, num_ent, dir_conf, r, dir_opts); + closedir(d); + + if (dir_opts & FANCY_INDEXING) + if((tmp = find_readme(dir_conf, r))) + insert_readme(name,tmp,1,r); + else { + rputs("</UL>", r); + } + + rputs ("</BODY></HTML>\n", r); + + kill_timeout(r); + return 0; +} + +/* The formal handler... */ + +int handle_dir (request_rec *r) +{ + dir_config_rec *d = + (dir_config_rec *)get_module_config (r->per_dir_config, &dir_module); + const char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX; + int allow_opts = allow_options (r); + int error_notfound = 0; + + if (r->uri[0] == '\0' || r->uri[strlen(r->uri)-1] != '/') { + char* ifile; + if (r->args != NULL) + ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri), + "/", "?", r->args, NULL); + else + ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri), + "/", NULL); + + table_set (r->headers_out, "Location", + construct_url(r->pool, ifile, r->server)); + return HTTP_MOVED_PERMANENTLY; + } + + /* KLUDGE --- make the sub_req lookups happen in the right directory. + * Fixing this in the sub_req_lookup functions themselves is difficult, + * and would probably break virtual includes... + */ + + r->filename = pstrcat (r->pool, r->filename, "/", NULL); + + while (*names_ptr) { + + char *name_ptr = getword_conf (r->pool, &names_ptr); + request_rec *rr = sub_req_lookup_uri (name_ptr, r); + + if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) { + char* new_uri = escape_uri(r->pool, rr->uri); + + if (rr->args != NULL) + new_uri = pstrcat(r->pool, new_uri, "?", rr->args, NULL); + else if (r->args != NULL) + new_uri = pstrcat(r->pool, new_uri, "?", r->args, NULL); + + destroy_sub_req (rr); + internal_redirect (new_uri, r); + return OK; + } + + /* If the request returned a redirect, propagate it to the client */ + + if (is_HTTP_REDIRECT(rr->status) || + (rr->status == HTTP_NOT_ACCEPTABLE && *names_ptr == '\0')) { + + error_notfound = rr->status; + r->notes = overlay_tables(r->pool, r->notes, rr->notes); + r->headers_out = overlay_tables(r->pool, r->headers_out, + rr->headers_out); + r->err_headers_out = overlay_tables(r->pool, r->err_headers_out, + rr->err_headers_out); + destroy_sub_req(rr); + return error_notfound; + } + + /* If the request returned something other than 404 (or 200), + * it means the module encountered some sort of problem. To be + * secure, we should return the error, rather than create + * along a (possibly unsafe) directory index. + * + * So we store the error, and if none of the listed files + * exist, we return the last error response we got, instead + * of a directory listing. + */ + if (rr->status && rr->status != HTTP_NOT_FOUND && rr->status != HTTP_OK) + error_notfound = rr->status; + + destroy_sub_req (rr); + } + + if (error_notfound) + return error_notfound; + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) return NOT_IMPLEMENTED; + + /* OK, nothing easy. Trot out the heavy artillery... */ + + if (allow_opts & OPT_INDEXES) + return index_directory (r, d); + else { + log_reason ("Directory index forbidden by rule", r->filename, r); + return HTTP_FORBIDDEN; + } +} + + +handler_rec dir_handlers[] = { +{ DIR_MAGIC_TYPE, handle_dir }, +{ NULL } +}; + +module dir_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dir_config, /* dir config creater */ + merge_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + dir_cmds, /* command table */ + dir_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_dld.c b/usr.sbin/httpd/src/mod_dld.c new file mode 100644 index 00000000000..ac6ff33a7ad --- /dev/null +++ b/usr.sbin/httpd/src/mod_dld.c @@ -0,0 +1,190 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * A first stab at dynamic loading, using the GNU dld library + * (or at least, an embarassingly old version of it...). + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" /* server_argv0. Sigh... */ +#include <dld.h> + +/* + * The hard part of implementing LoadModule is deciding what to do about + * rereading the config files. This proof-of-concept implementation takes the + * cheap way out: we only actually load the modules the first time through. + */ + +static int been_there_done_that = 0; /* Loaded the modules yet? */ +static int have_symbol_table = 0; + +char *insure_dld_sane() +{ + int errcode; + char *bin_name; + + if (have_symbol_table) return NULL; + + bin_name = dld_find_executable (server_argv0); + + if ((errcode = dld_init (bin_name))) { + dld_perror (server_argv0); + return "Cannot find server binary (needed for dynamic linking)."; + } + + have_symbol_table = 1; + return NULL; +} + +char *link_file (pool *p, char *filename) +{ + int errcode; + + filename = server_root_relative (p, filename); + if ((errcode = dld_link (filename))) { + dld_perror (server_argv0); + return pstrcat (p, "Cannot load ", filename, " into server", NULL); + } + return NULL; +} + +char *load_module (cmd_parms *cmd, void *dummy, char *modname, char *filename) +{ + char *errname; + module *modp; + + if (been_there_done_that) return NULL; + + if ((errname = insure_dld_sane())) return errname; + if ((errname = link_file (cmd->pool, filename))) return errname; + if (!(modp = (module *)dld_get_symbol (modname))) { + return pstrcat (cmd->pool, "Can't find module ", modname, + " in file ", filename, NULL); + } + + add_module (modp); + + /* Alethea Patch (rws,djw2) - need to run configuration functions + in new modules */ + + if (modp->create_server_config) + ((void**)cmd->server->module_config)[modp->module_index]= + (*modp->create_server_config)(cmd->pool, cmd->server); + + if (modp->create_dir_config) + ((void**)cmd->server->lookup_defaults)[modp->module_index]= + (*modp->create_dir_config)(cmd->pool, NULL); + + + return NULL; +} + +char *load_file (cmd_parms *cmd, void *dummy, char *filename) +{ + char *errname; + + if (been_there_done_that) return NULL; + + if ((errname = insure_dld_sane())) return errname; + if ((errname = link_file (cmd->pool, filename))) return errname; + return NULL; +} + +void check_loaded_modules (server_rec *dummy, pool *p) +{ + if (been_there_done_that) return; + + if (dld_undefined_sym_count > 0) { + /* Screwup. Do the best we can to inform the user, and exit */ + char **bad_syms = dld_list_undefined_sym(); + int i; + + fprintf(stderr, "Dynamic linking error --- symbols left undefined.\n"); + fprintf(stderr, "(It may help to relink libraries).\n"); + fprintf(stderr, "Undefined symbols follow:\n"); + + for (i = 0; i < dld_undefined_sym_count; ++i) + fprintf (stderr, "%s\n", bad_syms[i]); + + exit (1); + } + + been_there_done_that = 1; +} + +command_rec dld_cmds[] = { +{ "LoadModule", load_module, NULL, RSRC_CONF, TAKE2, + "a module name, and the name of a file to load it from"}, +{ "LoadFile", load_file, NULL, RSRC_CONF, ITERATE, + "files or libraries to link into the server at runtime"}, +{ NULL } +}; + +module dld_module = { + STANDARD_MODULE_STUFF, + check_loaded_modules, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + NULL, /* server config */ + NULL, /* merge server config */ + dld_cmds, /* command table */ + NULL, /* 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/usr.sbin/httpd/src/mod_env.c b/usr.sbin/httpd/src/mod_env.c new file mode 100644 index 00000000000..8ae697038da --- /dev/null +++ b/usr.sbin/httpd/src/mod_env.c @@ -0,0 +1,261 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * mod_env.c + * version 0.0.5 + * status beta + * Pass environment variables to CGI/SSI scripts. + * + * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 06.Dec.95 + * + * Change log: + * 08.Dec.95 Now allows PassEnv directive to appear more than once in + * conf files. + * 10.Dec.95 optimisation. getenv() only called at startup and used + * to build a fast-to-access table. table used to build + * per-server environment for each request. + * robustness. better able to handle errors in configuration + * files: + * 1) PassEnv directive present, but no environment variable listed + * 2) PassEnv FOO present, but $FOO not present in environment + * 3) no PassEnv directive present + * 23.Dec.95 Now allows SetEnv directive with same semantics as 'sh' setenv: + * SetEnv Var sets Var to the empty string + * SetEnv Var Val sets Var to the value Val + * Values containing whitespace should be quoted, eg: + * SetEnv Var "this is some text" + * Environment variables take their value from the last instance + * of PassEnv / SetEnv to be reached in the configuration file. + * For example, the sequence: + * PassEnv FOO + * SetEnv FOO override + * Causes FOO to take the value 'override'. + * 23.Feb.96 Added UnsetEnv directive to allow environment variables + * to be removed. + * Virtual hosts now 'inherit' parent server environment which + * they're able to overwrite with their own directives or + * selectively ignore with UnsetEnv. + * *** IMPORTANT - the way that virtual hosts inherit their *** + * *** environment variables from the default server's *** + * *** configuration has changed. You should test your *** + * *** configuration carefully before accepting this *** + * *** version of the module in a live webserver which used *** + * *** older versions of the module. *** + */ + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + table *vars; + char *unsetenv; + int vars_present; +} env_server_config_rec; + +module env_module; + +void *create_env_server_config (pool *p, server_rec *dummy) +{ + env_server_config_rec *new = + (env_server_config_rec *) palloc (p, sizeof(env_server_config_rec)); + new->vars = make_table (p, 50); + new->unsetenv = ""; + new->vars_present = 0; + return (void *) new; +} + +void *merge_env_server_configs (pool *p, void *basev, void *addv) +{ + env_server_config_rec *base = (env_server_config_rec *)basev; + env_server_config_rec *add = (env_server_config_rec *)addv; + env_server_config_rec *new = + (env_server_config_rec *)palloc (p, sizeof(env_server_config_rec)); + + table *new_table; + table_entry *elts; + + int i; + const char *uenv, *unset; + + /* + * new_table = copy_table( p, base->vars ); + * foreach $element ( @add->vars ) { + * table_set( new_table, $element.key, $element.val ); + * }; + * foreach $unsetenv ( @UNSETENV ) { + * table_unset( new_table, $unsetenv ); + * } + */ + + new_table = copy_table( p, base->vars ); + + elts = (table_entry *) add->vars->elts; + + for ( i = 0; i < add->vars->nelts; ++i ) { + table_set( new_table, elts[i].key, elts[i].val ); + } + + unset = add->unsetenv; + uenv = getword_conf( p, &unset ); + while ( uenv[0] != '\0' ) { + table_unset( new_table, uenv ); + uenv = getword_conf( p, &unset ); + } + + new->vars = new_table; + + new->vars_present = base->vars_present || add->vars_present; + + return new; +} + +const char *add_env_module_vars_passed (cmd_parms *cmd, char *struct_ptr, + const char *arg) +{ + env_server_config_rec *sconf = + get_module_config (cmd->server->module_config, &env_module); + table *vars = sconf->vars; + char *env_var; + char *name_ptr; + + while (*arg) { + name_ptr = getword_conf (cmd->pool, &arg); + env_var = getenv(name_ptr); + if ( env_var != NULL ) { + sconf->vars_present = 1; + table_set (vars, name_ptr, env_var); + } + } + return NULL; +} + +const char *add_env_module_vars_set (cmd_parms *cmd, char *struct_ptr, + const char *arg) +{ + env_server_config_rec *sconf = + get_module_config (cmd->server->module_config, &env_module); + table *vars = sconf->vars; + char *name, *value; + + name = getword_conf( cmd->pool, &arg ); + value = getword_conf( cmd->pool, &arg ); + + /* name is mandatory, value is optional. no value means + * set the variable to an empty string + */ + + + if ( (*name == '\0') || (*arg != '\0')) { + return "SetEnv takes one or two arguments. An environment variable name and an optional value to pass to CGI." ; + } + + sconf->vars_present = 1; + table_set (vars, name, value); + + return NULL; +} + +const char *add_env_module_vars_unset (cmd_parms *cmd, char *struct_ptr, + char *arg) +{ + env_server_config_rec *sconf = + get_module_config (cmd->server->module_config, &env_module); + sconf->unsetenv = sconf->unsetenv ? + pstrcat( cmd->pool, sconf->unsetenv, " ", arg, NULL ) : + pstrdup( cmd->pool, arg ); + return NULL; +} + +command_rec env_module_cmds[] = { +{ "PassEnv", add_env_module_vars_passed, NULL, + RSRC_CONF, RAW_ARGS, "a list of environment variables to pass to CGI." }, +{ "SetEnv", add_env_module_vars_set, NULL, + RSRC_CONF, RAW_ARGS, "an environment variable name and a value to pass to CGI." }, +{ "UnsetEnv", add_env_module_vars_unset, NULL, + RSRC_CONF, RAW_ARGS, "a list of variables to remove from the CGI environment." }, +{ NULL }, +}; + +int fixup_env_module(request_rec *r) +{ + table *e = r->subprocess_env; + server_rec *s = r->server; + env_server_config_rec *sconf = get_module_config (s->module_config, + &env_module); + table *vars = sconf->vars; + + if ( !sconf->vars_present ) return DECLINED; + + r->subprocess_env = overlay_tables( r->pool, e, vars ); + + return OK; +} + +module env_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_env_server_config, /* server config */ + merge_env_server_configs, /* merge server configs */ + env_module_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_env_module, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_expires.c b/usr.sbin/httpd/src/mod_expires.c new file mode 100644 index 00000000000..226726a0a93 --- /dev/null +++ b/usr.sbin/httpd/src/mod_expires.c @@ -0,0 +1,477 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * mod_expires.c + * version 0.0.11 + * status beta + * + * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 26.Jan.96 + * + * This module allows you to control the form of the Expires: header + * that Apache issues for each access. Directives can appear in + * configuration files or in .htaccess files so expiry semantics can + * be defined on a per-directory basis. + * + * DIRECTIVE SYNTAX + * + * Valid directives are: + * + * ExpiresActive on | off + * ExpiresDefault <code><seconds> + * ExpiresByType type/encoding <code><seconds> + * + * Valid values for <code> are: + * + * 'M' expires header shows file modification date + <seconds> + * 'A' expires header shows access time + <seconds> + * + * [I'm not sure which of these is best under different + * circumstances, I guess it's for other people to explore. + * The effects may be indistinguishable for a number of cases] + * + * <seconds> should be an integer value [acceptable to atoi()] + * + * There is NO space between the <code> and <seconds>. + * + * For example, a directory which contains information which changes + * frequently might contain: + * + * # reports generated by cron every hour. don't let caches + * # hold onto stale information + * ExpiresDefault M3600 + * + * Another example, our html pages can change all the time, the gifs + * tend not to change often: + * + * # pages are hot (1 week), images are cold (1 month) + * ExpiresByType text/html A604800 + * ExpiresByType image/gif A2592000 + * + * Expires can be turned on for all URLs on the server by placing the + * following directive in a conf file: + * + * ExpiresActive on + * + * ExpiresActive can also appear in .htaccess files, enabling the + * behaviour to be turned on or off for each chosen directory. + * + * # turn off Expires behaviour in this directory + * # and subdirectories + * ExpiresActive off + * + * Directives defined for a directory are valid in subdirectories + * unless explicitly overridden by new directives in the subdirectory + * .htaccess files. + * + * ALTERNATIVE DIRECTIVE SYNTAX + * + * Directives can also be defined in a more readable syntax of the form: + * + * ExpiresDefault "<base> [plus] {<num> <type>}*" + * ExpiresByType type/encoding "<base> [plus] {<num> <type>}*" + * + * where <base> is one of: + * access + * now equivalent to 'access' + * modification + * + * where the 'plus' keyword is optional + * + * where <num> should be an integer value [acceptable to atoi()] + * + * where <type> is one of: + * years + * months + * weeks + * days + * hours + * minutes + * seconds + * + * For example, any of the following directives can be used to make + * documents expire 1 month after being accessed, by default: + * + * ExpiresDefault "access plus 1 month" + * ExpiresDefault "access plus 4 weeks" + * ExpiresDefault "access plus 30 days" + * + * The expiry time can be fine-tuned by adding several '<num> <type>' + * clauses: + * + * ExpiresByType text/html "access plus 1 month 15 days 2 hours" + * ExpiresByType image/gif "modification plus 5 hours 3 minutes" + * + * --- + * + * Change-log: + * 29.Jan.96 Hardened the add_* functions. Server will now bail out + * if bad directives are given in the conf files. + * 02.Feb.96 Returns DECLINED if not 'ExpiresActive on', giving other + * expires-aware modules a chance to play with the same + * directives. [Michael Rutman] + * 03.Feb.96 Call tzset() before localtime(). Trying to get the module + * to work properly in non GMT timezones. + * 12.Feb.96 Modified directive syntax to allow more readable commands: + * ExpiresDefault "now plus 10 days 20 seconds" + * ExpiresDefault "access plus 30 days" + * ExpiresDefault "modification plus 1 year 10 months 30 days" + * 13.Feb.96 Fix call to table_get() with NULL 2nd parameter [Rob Hartill] + * 19.Feb.96 Call gm_timestr_822() to get time formatted correctly, can't + * rely on presence of HTTP_TIME_FORMAT in Apache 1.1+. + * 21.Feb.96 This version (0.0.9) reverses assumptions made in 0.0.8 + * about star/star handlers. Reverting to 0.0.7 behaviour. + * 08.Jun.96 allows ExpiresDefault to be used with responses that use + * the DefaultType by not DECLINING, but instead skipping + * the table_get check and then looking for an ExpiresDefault. + * [Rob Hartill] + * 04.Nov.96 'const' definitions added. + * + * TODO + * add support for Cache-Control: max-age=20 from the HTTP/1.1 + * proposal (in this case, a ttl of 20 seconds) [ask roy] + * add per-file expiry and explicit expiry times - duplicates some + * of the mod_cern_meta.c functionality. eg: + * ExpiresExplicit index.html "modification plus 30 days" + * + * BUGS + * Hi, welcome to the internet. + */ + +#include <ctype.h> +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" + +typedef struct { + int active; + char *expiresdefault; + table *expiresbytype; +} expires_dir_config; + +/* from mod_dir, why is this alias used? + */ +#define DIR_CMD_PERMS OR_INDEXES + +#define ACTIVE_ON 1 +#define ACTIVE_OFF 0 +#define ACTIVE_DONTCARE 2 + +module expires_module; + +void *create_dir_expires_config (pool *p, char *dummy) +{ + expires_dir_config *new = + (expires_dir_config *) pcalloc (p, sizeof(expires_dir_config)); + new->active = ACTIVE_DONTCARE; + new->expiresdefault = ""; + new->expiresbytype = make_table(p, 4); + return (void *)new; +} + +const char *set_expiresactive (cmd_parms *cmd, expires_dir_config *dir_config, int arg) +{ + /* if we're here at all it's because someone explicitly + * set the active flag + */ + dir_config->active = ACTIVE_ON; + if ( arg == 0 ) { + dir_config->active = ACTIVE_OFF; + }; + return NULL; +} + +/* check_code() parse 'code' and return NULL or an error response + * string. If we return NULL then real_code contains code converted + * to the cnnnn format. + */ +char *check_code( pool *pool, const char *code, char **real_code ) +{ + char *word; + char base = 'X'; + int modifier = 0; + int num = 0; + int factor = 0; + char foo[MAX_STRING_LEN]; + + /* 0.0.4 compatibility? + */ + if ( (code[0] == 'A') || (code[0] == 'M') ) { + *real_code = pstrdup( pool, code ); + return NULL; + }; + + /* <base> [plus] {<num> <type>}* + */ + + /* <base> + */ + word = getword_conf( pool, &code ); + if ( !strncasecmp( word, "now", 1 ) || + !strncasecmp( word, "access", 1 ) ) { + base = 'A'; + } else if ( !strncasecmp( word, "modification", 1 ) ) { + base = 'M'; + } else { + return pstrcat( pool, "bad expires code, unrecognised <base> '", + word, "'", NULL); + }; + + /* [plus] + */ + word = getword_conf( pool, &code ); + if ( !strncasecmp( word, "plus", 1 ) ) { + word = getword_conf( pool, &code ); + }; + + /* {<num> <type>}* + */ + while ( word[0] ) { + /* <num> + */ + if (isdigit(word[0])) { + num = atoi( word ); + } else { + return pstrcat( pool, "bad expires code, numeric value expected <num> '", + word, "'", NULL); + }; + + /* <type> + */ + word = getword_conf( pool, &code ); + if ( word[0] ) { + /* do nothing */ + } else { + return pstrcat( pool, "bad expires code, missing <type>", NULL); + }; + + factor = 0; + if ( !strncasecmp( word, "years", 1 ) ) { + factor = 60*60*24*365; + } else if ( !strncasecmp( word, "months", 2 ) ) { + factor = 60*60*24*30; + } else if ( !strncasecmp( word, "weeks", 1 ) ) { + factor = 60*60*24*7; + } else if ( !strncasecmp( word, "days", 1 ) ) { + factor = 60*60*24; + } else if ( !strncasecmp( word, "hours", 1 ) ) { + factor = 60*60; + } else if ( !strncasecmp( word, "minutes", 2 ) ) { + factor = 60; + } else if ( !strncasecmp( word, "seconds", 1 ) ) { + factor = 1; + } else { + return pstrcat( pool, "bad expires code, unrecognised <type>", + "'", word, "'", NULL); + }; + + modifier = modifier + factor * num; + + /* next <num> + */ + word = getword_conf( pool, &code ); + }; + + ap_snprintf(foo, sizeof(foo), "%c%d", base, modifier ); + *real_code = pstrdup( pool, foo ); + + return NULL; +} + +const char *set_expiresbytype(cmd_parms *cmd, expires_dir_config *dir_config, char *mime, char *code) +{ + char *response, *real_code; + + if ( (response = check_code( cmd->pool, code, &real_code )) == NULL ) { + table_set (dir_config->expiresbytype, mime, real_code); + return NULL; + }; + return pstrcat( cmd->pool, + "'ExpiresByType ", mime, " ", code, "': ", response, NULL ); +} + +const char *set_expiresdefault (cmd_parms *cmd, expires_dir_config *dir_config, char *code) +{ + char *response, *real_code; + + if ( (response = check_code( cmd->pool, code, &real_code )) == NULL ) { + dir_config->expiresdefault = pstrdup( cmd->pool, real_code ); + return NULL; + }; + return pstrcat( cmd->pool, + "'ExpiresDefault ", code, "': ", response, NULL ); +} + +command_rec expires_cmds[] = { +{ "ExpiresActive", set_expiresactive, NULL, DIR_CMD_PERMS, FLAG, NULL}, +{ "ExpiresBytype", set_expiresbytype, NULL, DIR_CMD_PERMS, TAKE2, + "a mime type followed by an expiry date code"}, +{ "ExpiresDefault", set_expiresdefault, NULL, DIR_CMD_PERMS, TAKE1, + "an expiry date code"}, +{ NULL } +}; + +void *merge_expires_dir_configs (pool *p, void *basev, void *addv) +{ + expires_dir_config *new= (expires_dir_config*)pcalloc (p, sizeof(expires_dir_config)); + expires_dir_config *base = (expires_dir_config *)basev; + expires_dir_config *add = (expires_dir_config *)addv; + + if ( add->active == ACTIVE_DONTCARE ) { + new->active = base->active; + } else { + new->active = add->active; + }; + + if ( add->expiresdefault != '\0' ) { + new->expiresdefault = add->expiresdefault; + }; + + new->expiresbytype = overlay_tables (p, add->expiresbytype, + base->expiresbytype); + return new; +} + +int add_expires(request_rec *r) +{ + expires_dir_config *conf = + (expires_dir_config *)get_module_config(r->per_dir_config, &expires_module); + char *code; + time_t base; + time_t additional; + time_t expires; + + if ( r->finfo.st_mode == 0 ) + return DECLINED; + + /* COMMA bites my ass... + */ + if ( conf == NULL ) { + log_reason ("internal error in expires_module; add_expires(), conf == NULL", r->filename, r); + return SERVER_ERROR; + }; + + if ( conf->active != ACTIVE_ON ) + return DECLINED; + + /* we perhaps could use the default_type(r) in its place but that + * may be 2nd guesing the desired configuration... calling table_get + * with a NULL key will SEGV us + * + * I still don't know *why* r->content_type would ever be NULL, this + * is possibly a result of fixups being called in many different + * places. Fixups is probably the wrong place to be doing all this + * work... Bah. + * + * Changed as of 08.Jun.96 don't DECLINE, look for an ExpiresDefault. + */ + if ( r->content_type == NULL ) + code = NULL; + else + code = (char *) table_get( conf->expiresbytype, r->content_type ); + + if ( code == NULL ) { + /* no expires defined for that type, is there a default? */ + code = conf->expiresdefault; + + if ( code[0] == '\0' ) + return OK; + }; + + /* we have our code */ + + switch (code[0]) { + case 'M': + base = r->finfo.st_mtime; + additional = atoi( &code[1] ); + break; + case 'A': + /* there's been some discussion and it's possible that + * 'access time' will be stored in request structure + */ + base = time( NULL ); + additional = atoi( &code[1] ); + break; + default: + /* expecting the add_* routines to be case-hardened this + * is just a reminder that module is beta + */ + log_reason ("internal error in expires_module; bad expires code", r->filename, r); + return SERVER_ERROR; + }; + + expires = base + additional; + tzset(); /* redundant? called implicitly by localtime, at least + * under FreeBSD + */ + table_set( r->headers_out, "Expires", gm_timestr_822( r->pool, expires )); + return OK; +} + +module expires_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_dir_expires_config, /* dir config creater */ + merge_expires_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server configs */ + expires_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + add_expires, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_headers.c b/usr.sbin/httpd/src/mod_headers.c new file mode 100644 index 00000000000..3976be7ba8a --- /dev/null +++ b/usr.sbin/httpd/src/mod_headers.c @@ -0,0 +1,253 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* + * mod_headers.c: Add/append/remove HTTP response headers + * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996 + * + * New directive, Header, can be used to add/replace/remove HTTP headers. + * Valid in both per-server and per-dir configurations. + * + * Syntax is: + * + * Header action header value + * + * Where action is one of: + * set - set this header, replacing any old value + * add - add this header, possible resulting in two or more + * headers with the same name + * append - append this text onto any existing header of this same + * unset - remove this header + * + * Where action is unset, the third argument (value) should not be given. + * The header name can include the colon, or not. + * + * The Header directive can only be used where allowed by the FileInfo + * override. + * + * When the request is processed, the header directives are processed in + * this order: firstly, the main server, then the virtual server handling + * this request (if any), then any <Directory> sections (working downwards + * from the root dir), then an <Location> sections (working down from + * shortest URL component), the any <File> sections. This order is + * important if any 'set' or 'unset' actions are used. For example, + * the following two directives have different effect if applied in + * the reverse order: + * + * Header append Author "John P. Doe" + * Header unset Author + * + * Examples: + * + * To set the "Author" header, use + * Header add Author "John P. Doe" + * + * To remove a header: + * Header unset Author + * + */ + +#include "httpd.h" +#include "http_config.h" + +typedef enum { + hdr_add = 'a', /* add header (could mean multiple hdrs) */ + hdr_set = 's', /* set (replace old value) */ + hdr_append = 'm', /* append (merge into any old value) */ + hdr_unset = 'u' /* unset header */ +} hdr_actions; + +typedef struct { + hdr_actions action; + char *header; + char *value; +} header_entry; + +/* + * headers_conf is our per-module configuration. This is used as both + * a per-dir and per-server config + */ +typedef struct { + array_header *headers; +} headers_conf; + +module headers_module; + +void *create_headers_config (pool *p, server_rec *s) +{ + headers_conf *a = + (headers_conf *)pcalloc (p, sizeof(headers_conf)); + + a->headers = make_array (p, 2, sizeof(header_entry)); + return a; +} + +void *create_headers_dir_config (pool *p, char *d) +{ + return (headers_conf*)create_headers_config(p, NULL); +} + +void *merge_headers_config (pool *p, void *basev, void *overridesv) +{ + headers_conf *a = + (headers_conf *)pcalloc (p, sizeof(headers_conf)); + headers_conf *base = (headers_conf *)basev, + *overrides = (headers_conf *)overridesv; + + a->headers = append_arrays(p, base->headers, overrides->headers); + + return a; +} + + +const char *header_cmd(cmd_parms *cmd, headers_conf *dirconf, char *action, char *hdr, char *value) +{ + header_entry *new; + server_rec *s = cmd->server; + headers_conf *serverconf = + (headers_conf *)get_module_config(s->module_config,&headers_module); + char *colon; + + if ( cmd->path ) + { + new = (header_entry*)push_array(dirconf->headers); + } + else + { + new = (header_entry*)push_array(serverconf->headers); + } + + if (!strcasecmp(action, "set")) new->action = hdr_set; + else if (!strcasecmp(action, "add")) new->action = hdr_add; + else if (!strcasecmp(action, "append")) new->action = hdr_append; + else if (!strcasecmp(action, "unset")) new->action = hdr_unset; + else + return "first argument must be add, set, append or unset."; + + if (new->action == hdr_unset) { + if (value) return "Header unset takes two arguments"; + } + else if (!value) + return "Header requires three arguments"; + + if ((colon = strchr(hdr, ':'))) + *colon = '\0'; + + new->header = pstrdup(cmd->pool, hdr); + new->value = value ? pstrdup(cmd->pool, value) : NULL; + + return NULL; +} + +command_rec headers_cmds[] = { +{ "Header", header_cmd, NULL, OR_FILEINFO, TAKE23, + "an action, header and value"}, +{ NULL } +}; + +void do_headers_fixup(request_rec *r, array_header *headers) +{ + int i; + + for (i = 0; i < headers->nelts; ++i) { + header_entry *hdr = &((header_entry*)(headers->elts))[i]; + switch (hdr->action) { + case hdr_add: + table_add(r->headers_out, hdr->header, hdr->value); + break; + case hdr_append: + table_merge(r->headers_out, hdr->header, hdr->value); + break; + case hdr_set: + table_set(r->headers_out, hdr->header, hdr->value); + break; + case hdr_unset: + table_unset(r->headers_out, hdr->header); + break; + } + } + +} + +int fixup_headers(request_rec *r) +{ + void *sconf = r->server->module_config; + headers_conf *serverconf = + (headers_conf *)get_module_config(sconf, &headers_module); + void *dconf = r->per_dir_config; + headers_conf *dirconf = + (headers_conf *)get_module_config(dconf, &headers_module); + + do_headers_fixup(r, serverconf->headers); + do_headers_fixup(r, dirconf->headers); + + return DECLINED; +} + +module headers_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_headers_dir_config, /* dir config creater */ + merge_headers_config, /* dir merger --- default is to override */ + create_headers_config, /* server config */ + merge_headers_config, /* merge server configs */ + headers_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + fixup_headers, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_imap.c b/usr.sbin/httpd/src/mod_imap.c new file mode 100644 index 00000000000..3282dfe71f3 --- /dev/null +++ b/usr.sbin/httpd/src/mod_imap.c @@ -0,0 +1,877 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + * + */ + +/* + * This imagemap module started as a port of the original imagemap.c + * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu). + * This version includes the mapping algorithms found in version 1.3 + * of imagemap.c. + * + * Contributors to this code include: + * + * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu + * + * Eric Haines, erich@eye.com + * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com + * + * Randy Terbush, randy@zyzzyva.com + * port to Apache module format, "base_uri" and support for relative URLs + * + * James H. Cloos, Jr., cloos@jhcloos.com + * Added point datatype, using code in NCSA's version 1.8 imagemap.c + * program, as distributed with version 1.4.1 of their server. + * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu + * + * Nathan Kurz, nate@tripod.com + * Rewrite/reorganization. New handling of default, base and relative URLs. + * New Configuration directives: + * ImapMenu {none, formatted, semiformatted, unformatted} + * ImapDefault {error, nocontent, referer, menu, URL} + * ImapBase {map, referer, URL} + * Support for creating non-graphical menu added. (backwards compatible): + * Old: directive URL [x,y ...] + * New: directive URL "Menu text" [x,y ...] + * or: directive URL x,y ... "Menu text" + * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca. + * + * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" + +#define IMAP_MAGIC_TYPE "application/x-httpd-imap" +#define MAXVERTS 100 +#define X 0 +#define Y 1 + +#define IMAP_MENU_DEFAULT "formatted" +#define IMAP_DEFAULT_DEFAULT "nocontent" +#define IMAP_BASE_DEFAULT "map" + +#ifdef SUNOS4 +double strtod(); /* SunOS needed this */ +#endif + +module imap_module; + +typedef struct { + char *imap_menu; + char *imap_default; + char *imap_base; +} imap_conf_rec; + +static void *create_imap_dir_config(pool *p, char *dummy) +{ + imap_conf_rec *icr = + (imap_conf_rec *) palloc(p, sizeof(imap_conf_rec)); + + icr->imap_menu = NULL; + icr->imap_default = NULL; + icr->imap_base = NULL; + + return icr; +} + +static void *merge_imap_dir_configs(pool *p, void *basev, void *addv) +{ + imap_conf_rec *new = (imap_conf_rec *) pcalloc(p, sizeof(imap_conf_rec)); + imap_conf_rec *base = (imap_conf_rec *) basev; + imap_conf_rec *add = (imap_conf_rec *) addv; + + new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu; + new->imap_default = add->imap_default ? add->imap_default : base->imap_default; + new->imap_base = add->imap_base ? add->imap_base : base->imap_base; + + return new; +} + + +static command_rec imap_cmds[] = +{ + {"ImapMenu", set_string_slot, + (void *) XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1, + "the type of menu generated: none, formatted, semiformatted, unformatted"}, + {"ImapDefault", set_string_slot, + (void *) XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1, + "the action taken if no match: error, nocontent, referer, menu, URL"}, + {"ImapBase", set_string_slot, + (void *) XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1, + "the base for all URL's: map, referer, URL (or start of)"}, + {NULL} +}; + +static int pointinrect(const double point[2], double coords[MAXVERTS][2]) +{ + double max[2], min[2]; + if (coords[0][X] > coords[1][X]) { + max[0] = coords[0][X]; + min[0] = coords[1][X]; + } + else { + max[0] = coords[1][X]; + min[0] = coords[0][X]; + } + + if (coords[0][Y] > coords[1][Y]) { + max[1] = coords[0][Y]; + min[1] = coords[1][Y]; + } + else { + max[1] = coords[1][Y]; + min[1] = coords[0][Y]; + } + + return ((point[X] >= min[0] && point[X] <= max[0]) && + (point[Y] >= min[1] && point[Y] <= max[1])); +} + +static int pointincircle(const double point[2], double coords[MAXVERTS][2]) +{ + double radius1, radius2; + + radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y])) + + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X])); + + radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y])) + + ((coords[0][X] - point[X]) * (coords[0][X] - point[X])); + + return (radius2 <= radius1); +} + +static int pointinpoly(const double point[2], double pgon[MAXVERTS][2]) +{ + int i, numverts, inside_flag, xflag0; + int crossings; + double *p; + const double *stop; + double tx, ty, y; + + for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++); + + numverts = i; + crossings = 0; + + tx = point[X]; + ty = point[Y]; + y = pgon[numverts - 1][Y]; + + p = (double *) pgon + 1; + if ((y >= ty) != (*p >= ty)) { + + if ((xflag0 = (pgon[numverts - 1][X] >= tx)) == (*(double *) pgon >= tx)) { + if (xflag0) + crossings++; + } + else { + crossings += (pgon[numverts - 1][X] - (y - ty) * + (*(double *) pgon - pgon[numverts - 1][X]) / + (*p - y)) >= tx; + } + } + + stop = pgon[numverts]; + + for (y = *p, p += 2; p < stop; y = *p, p += 2) { + + if (y >= ty) { + + while ((p < stop) && (*p >= ty)) + p += 2; + + if (p >= stop) + break; + if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) { + + if (xflag0) + crossings++; + } + else { + crossings += (*(p - 3) - (*(p - 2) - ty) * + (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx; + } + } + else { + while ((p < stop) && (*p < ty)) + p += 2; + + if (p >= stop) + break; + + if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) { + if (xflag0) + crossings++; + } + else { + crossings += (*(p - 3) - (*(p - 2) - ty) * + (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx; + } + } + } + + inside_flag = crossings & 0x01; + return (inside_flag); +} + + +static int is_closer(const double point[2], double coords[MAXVERTS][2], + double *closest) +{ + double dist_squared = ((point[X] - coords[0][X]) * (point[X] - coords[0][X])) + + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y])); + + if (point[X] < 0 || point[Y] < 0) + return (0); /* don't mess around with negative coordinates */ + + if (*closest < 0 || dist_squared < *closest) { + *closest = dist_squared; + return (1); /* if this is the first point or is the closest yet + set 'closest' equal to this distance^2 */ + } + + return (0); /* if it's not the first or closest */ + +} + +static double get_x_coord(const char *args) +{ + char *endptr; /* we want it non-null */ + double x_coord = -1; /* -1 is returned if no coordinate is given */ + + if (args == NULL) + return (-1); /* in case we aren't passed anything */ + + while (*args && !isdigit(*args) && *args != ',') + args++; /* jump to the first digit, but not past a comma or end */ + + x_coord = strtod(args, &endptr); + + if (endptr > args) /* if a conversion was made */ + return (x_coord); + + return (-1); /* else if no conversion was made, or if no args was given */ +} + +static double get_y_coord(const char *args) +{ + char *endptr; /* we want it non-null */ + char *start_of_y = NULL; + double y_coord = -1; /* -1 is returned on error */ + + if (args == NULL) + return (-1); /* in case we aren't passed anything */ + + start_of_y = strchr(args, ','); /* the comma */ + + if (start_of_y) { + + start_of_y++; /* start looking at the character after the comma */ + + while (*start_of_y && !isdigit(*start_of_y)) + start_of_y++; /* jump to the first digit, but not past the end */ + + y_coord = strtod(start_of_y, &endptr); + + if (endptr > start_of_y) + return (y_coord); + } + + return (-1); /* if no conversion was made, or no comma was found in args */ +} + + +/* See if string has a "quoted part", and if so set *quoted_part to + * the first character of the quoted part, then hammer a \0 onto the + * trailing quote, and set *string to point at the first character + * past the second quote. + * + * Otherwise set *quoted_part to NULL, and leave *string alone. + */ +static void read_quoted(char **string, char **quoted_part) +{ + char *strp = *string; + + /* assume there's no quoted part */ + *quoted_part = NULL; + + while (isspace(*strp)) + strp++; /* go along string until non-whitespace */ + + if (*strp == '"') { /* if that character is a double quote */ + strp++; /* step over it */ + *quoted_part = strp; /* note where the quoted part begins */ + + while (*strp && *strp != '"') { + ++strp; /* skip the quoted portion */ + } + + *strp = '\0'; /* end the string with a NUL */ + + strp++; /* step over the last double quote */ + *string = strp; + } +} + +/* + * returns the mapped URL or NULL. + */ +static char *imap_url(request_rec *r, const char *base, const char *value) +{ +/* translates a value into a URL. */ + int slen, clen; + char *string_pos = NULL; + const char *string_pos_const = NULL; + char *directory = NULL; + char *referer = NULL; + char *my_base; + + if (!strcasecmp(value, "map") || !strcasecmp(value, "menu")) { + return construct_url(r->pool, r->uri, r->server); + } + + if (!strcasecmp(value, "nocontent") || !strcasecmp(value, "error")) { + return pstrdup(r->pool, value); /* these are handled elsewhere, so just copy them */ + } + + if (!strcasecmp(value, "referer")) { + referer = table_get(r->headers_in, "Referer"); + if (referer && *referer) { + return pstrdup(r->pool, referer); + } + else { + /* XXX: This used to do *value = '\0'; ... which is totally bogus + * because it hammers the passed in value, which can be a string constant, + * or part of a config, or whatever. Total garbage. This works around + * that without changing the rest of this code much + */ + value = ""; /* if 'referer' but no referring page, null the value */ + } + } + + string_pos_const = value; + while (isalpha(*string_pos_const)) + string_pos_const++; /* go along the URL from the map until a non-letter */ + if (*string_pos_const == ':') { + /* if letters and then a colon (like http:) */ + /* it's an absolute URL, so use it! */ + return pstrdup(r->pool, value); + } + + if (!base || !*base) { + if (value && *value) { + return pstrdup(r->pool, value); /* no base: use what is given */ + } + /* no base, no value: pick a simple default */ + return construct_url(r->pool, "/", r->server); + } + + /* must be a relative URL to be combined with base */ + if (strchr(base, '/') == NULL && (!strncmp(value, "../", 3) || !strcmp(value, ".."))) { + log_reason("invalid base directive in map file: %s", r->uri, r); + return NULL; + } + my_base = pstrdup(r->pool, base); + string_pos = my_base; + while (*string_pos) { + if (*string_pos == '/' && *(string_pos + 1) == '/') { + string_pos += 2; /* if there are two slashes, jump over them */ + continue; + } + if (*string_pos == '/') { /* the first single slash */ + if (value[0] == '/') { + *string_pos = '\0'; + } /* if the URL from the map starts from root, end the + base URL string at the first single slash */ + else { + directory = string_pos; /* save the start of the directory portion */ + + string_pos = strrchr(string_pos, '/'); /* now reuse string_pos */ + string_pos++; /* step over that last slash */ + *string_pos = '\0'; + } /* but if the map url is relative, leave the + slash on the base (if there is one) */ + break; + } + string_pos++; /* until we get to the end of my_base without finding + a slash by itself */ + } + + while (!strncmp(value, "../", 3) || !strcmp(value, "..")) { + + if (directory && (slen = strlen(directory))) { + + /* for each '..', knock a directory off the end + by ending the string right at the last slash. + But only consider the directory portion: don't eat + into the server name. And only try if a directory + portion was found */ + + clen = slen - 1; + + while ((slen - clen) == 1) { + + if ((string_pos = strrchr(directory, '/'))) + *string_pos = '\0'; + clen = strlen(directory); + if (clen == 0) + break; + } + + value += 2; /* jump over the '..' that we found in the value */ + } + else if (directory) { + log_reason("invalid directory name in map file: %s", r->uri, r); + return NULL; + } + + if (!strncmp(value, "/../", 4) || !strcmp(value, "/..")) + value++; /* step over the '/' if there are more '..' to do. + this way, we leave the starting '/' on value after + the last '..', but get rid of it otherwise */ + + } /* by this point, value does not start with '..' */ + + if (value && *value) { + return pstrcat(r->pool, my_base, value, NULL); + } + return my_base; +} + +static int imap_reply(request_rec *r, char *redirect) +{ + if (!strcasecmp(redirect, "error")) { + return SERVER_ERROR; /* they actually requested an error! */ + } + if (!strcasecmp(redirect, "nocontent")) { + return HTTP_NO_CONTENT; /* tell the client to keep the page it has */ + } + if (redirect && *redirect) { + table_set(r->headers_out, "Location", redirect); + return REDIRECT; /* must be a URL, so redirect to it */ + } + return SERVER_ERROR; +} + +static void menu_header(request_rec *r, char *menu) +{ + r->content_type = "text/html"; + send_http_header(r); + hard_timeout("send menu", r); /* killed in menu_footer */ + + rvputs(r, "<html><head>\n<title>Menu for ", r->uri, + "</title>\n</head><body>\n", NULL); + + if (!strcasecmp(menu, "formatted")) { + rvputs(r, "<h1>Menu for ", r->uri, "</h1>\n<hr>\n\n", NULL); + } + + return; +} + +static void menu_blank(request_rec *r, char *menu) +{ + if (!strcasecmp(menu, "formatted")) { + rputs("\n", r); + } + if (!strcasecmp(menu, "semiformatted")) { + rputs("<br>\n", r); + } + if (!strcasecmp(menu, "unformatted")) { + rputs("\n", r); + } + return; +} + +static void menu_comment(request_rec *r, char *menu, char *comment) +{ + if (!strcasecmp(menu, "formatted")) { + rputs("\n", r); /* print just a newline if 'formatted' */ + } + if (!strcasecmp(menu, "semiformatted") && *comment) { + rvputs(r, comment, "\n", NULL); + } + if (!strcasecmp(menu, "unformatted") && *comment) { + rvputs(r, comment, "\n", NULL); + } + return; /* comments are ignored in the 'formatted' form */ +} + +static void menu_default(request_rec *r, char *menu, char *href, char *text) +{ + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, these aren'te really href's */ + } + if (!strcasecmp(menu, "formatted")) { + rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n", + NULL); + } + if (!strcasecmp(menu, "semiformatted")) { + rvputs(r, "<pre>(Default) <a href=\"", href, "\">", text, "</a></pre>\n", + NULL); + } + if (!strcasecmp(menu, "unformatted")) { + rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL); + } + return; +} + +static void menu_directive(request_rec *r, char *menu, char *href, char *text) +{ + if (!strcasecmp(href, "error") || !strcasecmp(href, "nocontent")) { + return; /* don't print such lines, as this isn't really an href */ + } + if (!strcasecmp(menu, "formatted")) { + rvputs(r, "<pre> <a href=\"", href, "\">", text, "</a></pre>\n", + NULL); + } + if (!strcasecmp(menu, "semiformatted")) { + rvputs(r, "<pre> <a href=\"", href, "\">", text, "</a></pre>\n", + NULL); + } + if (!strcasecmp(menu, "unformatted")) { + rvputs(r, "<a href=\"", href, "\">", text, "</a>", NULL); + } + return; +} + +static void menu_footer(request_rec *r) +{ + rputs("\n\n</body>\n</html>\n", r); /* finish the menu */ + kill_timeout(r); +} + +static int imap_handler(request_rec *r) +{ + char input[MAX_STRING_LEN]; + char *directive; + char *value; + char *href_text; + char *base; + char *redirect; + char *mapdflt; + char *closest = NULL; + double closest_yet = -1; + + double testpoint[2]; + double pointarray[MAXVERTS + 1][2]; + int vertex; + + char *string_pos; + int showmenu = 0; + + imap_conf_rec *icr = get_module_config(r->per_dir_config, &imap_module); + + char *imap_menu = icr->imap_menu ? icr->imap_menu : IMAP_MENU_DEFAULT; + char *imap_default = icr->imap_default + ? icr->imap_default : IMAP_DEFAULT_DEFAULT; + char *imap_base = icr->imap_base ? icr->imap_base : IMAP_BASE_DEFAULT; + + FILE *imap; + + if (r->method_number != M_GET) + return DECLINED; + + imap = pfopen(r->pool, r->filename, "r"); + + if (!imap) + return NOT_FOUND; + + base = imap_url(r, NULL, imap_base); /* set base according to default */ + if (!base) + return HTTP_INTERNAL_SERVER_ERROR; + mapdflt = imap_url(r, NULL, imap_default); /* and default to global default */ + if (!mapdflt) + return HTTP_INTERNAL_SERVER_ERROR; + + testpoint[X] = get_x_coord(r->args); + testpoint[Y] = get_y_coord(r->args); + + if ((testpoint[X] == -1 || testpoint[Y] == -1) || + (testpoint[X] == 0 && testpoint[Y] == 0)) { + /* if either is -1 or if both are zero (new Lynx) */ + /* we don't have valid coordinates */ + testpoint[X] = -1; + testpoint[Y] = -1; + if (strncasecmp(imap_menu, "none", 2)) + showmenu = 1; /* show the menu _unless_ ImapMenu is 'none' or 'no' */ + } + + if (showmenu) { /* send start of imagemap menu if we're going to */ + menu_header(r, imap_menu); + } + + while (!cfg_getline(input, sizeof(input), imap)) { + if (!input[0]) { + if (showmenu) { + menu_blank(r, imap_menu); + } + continue; + } + + if (input[0] == '#') { + if (showmenu) { + menu_comment(r, imap_menu, input + 1); + } + continue; + } /* blank lines and comments are ignored if we aren't printing a menu */ + + /* find the first two space delimited fields, recall that + * cfg_getline has removed leading/trailing whitespace and + * compressed the other whitespace down to one space a piece + * + * note that we're tokenizing as we go... if we were to use the + * getword() class of functions we would end up allocating extra + * memory for every line of the map file + */ + string_pos = input; + if (!*string_pos) /* need at least two fields */ + goto need_2_fields; + + directive = string_pos; + while (*string_pos && *string_pos != ' ') /* past directive */ + ++string_pos; + if (!*string_pos) /* need at least two fields */ + goto need_2_fields; + *string_pos++ = '\0'; + + if (!*string_pos) /* need at least two fields */ + goto need_2_fields; + value = string_pos; + while (*string_pos && *string_pos != ' ') /* past value */ + ++string_pos; + if (*string_pos == ' ') { + *string_pos++ = '\0'; + } + else { + /* end of input, don't advance past it */ + *string_pos = '\0'; + } + + if (!strncasecmp(directive, "base", 4)) { /* base, base_uri */ + base = imap_url(r, NULL, value); + if (!base) + goto menu_bail; + continue; /* base is never printed to a menu */ + } + + read_quoted(&string_pos, &href_text); + + if (!strcasecmp(directive, "default")) { /* default */ + mapdflt = imap_url(r, NULL, value); + if (!mapdflt) + goto menu_bail; + if (showmenu) { /* print the default if there's a menu */ + redirect = imap_url(r, base, mapdflt); + if (!redirect) + goto menu_bail; + menu_default(r, imap_menu, redirect, href_text ? href_text : mapdflt); + } + continue; + } + + vertex = 0; + while (vertex < MAXVERTS && + sscanf(string_pos, "%lf%*[, ]%lf", + &pointarray[vertex][X], &pointarray[vertex][Y]) == 2) { + /* Now skip what we just read... we can't use ANSIism %n */ + while (isspace(*string_pos)) /* past whitespace */ + string_pos++; + while (isdigit(*string_pos)) /* and the 1st number */ + string_pos++; + string_pos++; /* skip the ',' */ + while (isspace(*string_pos)) /* past any more whitespace */ + string_pos++; + while (isdigit(*string_pos)) /* 2nd number */ + string_pos++; + vertex++; + } /* so long as there are more vertices to read, and + we have room, read them in. We start where we left + off of the last sscanf, not at the beginning. */ + + pointarray[vertex][X] = -1; /* signals the end of vertices */ + + if (showmenu) { + if (!href_text) { + read_quoted(&string_pos, &href_text); /* href text could be here instead */ + } + redirect = imap_url(r, base, value); + if (!redirect) + goto menu_bail; + menu_directive(r, imap_menu, redirect, href_text ? href_text : value); + continue; + } + /* note that we don't make it past here if we are making a menu */ + + if (testpoint[X] == -1 || pointarray[0][X] == -1) + continue; /* don't try the following tests if testpoints + are invalid, or if there are no coordinates */ + + if (!strcasecmp(directive, "poly")) { /* poly */ + + if (pointinpoly(testpoint, pointarray)) { + pfclose(r->pool, imap); + redirect = imap_url(r, base, value); + if (!redirect) + return HTTP_INTERNAL_SERVER_ERROR; + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "circle")) { /* circle */ + + if (pointincircle(testpoint, pointarray)) { + pfclose(r->pool, imap); + redirect = imap_url(r, base, value); + if (!redirect) + return HTTP_INTERNAL_SERVER_ERROR; + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "rect")) { /* rect */ + + if (pointinrect(testpoint, pointarray)) { + pfclose(r->pool, imap); + redirect = imap_url(r, base, value); + if (!redirect) + return HTTP_INTERNAL_SERVER_ERROR; + return (imap_reply(r, redirect)); + } + continue; + } + + if (!strcasecmp(directive, "point")) { /* point */ + + if (is_closer(testpoint, pointarray, &closest_yet)) { + closest = pstrdup(r->pool, value); + } + + continue; + } /* move on to next line whether it's closest or not */ + + } /* nothing matched, so we get another line! */ + + pfclose(r->pool, imap); /* we are done with the map file, so close it */ + + if (showmenu) { + menu_footer(r); /* finish the menu and we are done */ + return OK; + } + + if (closest) { /* if a 'point' directive has been seen */ + redirect = imap_url(r, base, closest); + if (!redirect) + return HTTP_INTERNAL_SERVER_ERROR; + return (imap_reply(r, redirect)); + } + + if (mapdflt) { /* a default should be defined, even if only 'nocontent' */ + redirect = imap_url(r, base, mapdflt); + if (!redirect) + return HTTP_INTERNAL_SERVER_ERROR; + return (imap_reply(r, redirect)); + } + + return SERVER_ERROR; /* If we make it this far, we failed. They lose! */ + +need_2_fields: + log_reason("all map file lines require at least two fields", r->uri, r); + /* fall through */ +menu_bail: + pfclose(r->pool, imap); + if (showmenu) { + /* There's not much else we can do ... we've already sent the headers + * to the client. + */ + rputs("\n\n[an internal server error occured]\n", r); + menu_footer(r); + return OK; + } + return HTTP_INTERNAL_SERVER_ERROR; +} + + +static handler_rec imap_handlers[] = +{ + {IMAP_MAGIC_TYPE, imap_handler}, + {"imap-file", imap_handler}, + {NULL} +}; + +module imap_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_imap_dir_config, /* dir config creater */ + merge_imap_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + imap_cmds, /* command table */ + imap_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_include.c b/usr.sbin/httpd/src/mod_include.c new file mode 100644 index 00000000000..2b53f24aaa1 --- /dev/null +++ b/usr.sbin/httpd/src/mod_include.c @@ -0,0 +1,2329 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_include.c: Handles the server-parsed HTML documents + * + * Original by Rob McCool; substantial fixups by David Robinson; + * incorporated into the Apache module framework by rst. + * + */ +/* + * sub key may be anything a Perl*Handler can be: + * subroutine name, package name (defaults to package::handler), + * Class->method call or anoymous sub {} + * + * Child <!--#perl sub="sub {print $$}" --> accessed + * <!--#perl sub="sub {print ++$Access::Cnt }" --> times. <br> + * + * <!--#perl arg="one" sub="mymod::includer" --> + * + * -Doug MacEachern + */ + +#ifdef USE_PERL_SSI +#include "config.h" +#ifdef USE_SFIO +#undef USE_SFIO +#define USE_STDIO +#endif +#include "modules/perl/mod_perl.h" +#else +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#endif + +#define STARTING_SEQUENCE "<!--#" +#define ENDING_SEQUENCE "-->" +#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" +#define DEFAULT_TIME_FORMAT "%A, %d-%b-%Y %H:%M:%S %Z" +#define SIZEFMT_BYTES 0 +#define SIZEFMT_KMG 1 + + +static void safe_copy(char *dest, const char *src, size_t max_len) +{ + strncpy(dest, src, max_len - 1); + dest[max_len - 1] = '\0'; +} + +/* ------------------------ Environment function -------------------------- */ + +static void add_include_vars(request_rec *r, char *timefmt) +{ + struct passwd *pw; + table *e = r->subprocess_env; + char *t; + time_t date = r->request_time; + + table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0)); + table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1)); + table_set(e, "LAST_MODIFIED", + ht_time(r->pool, r->finfo.st_mtime, timefmt, 0)); + table_set(e, "DOCUMENT_URI", r->uri); + table_set(e, "DOCUMENT_PATH_INFO", r->path_info); + pw = getpwuid(r->finfo.st_uid); + if (pw) { + table_set(e, "USER_NAME", pw->pw_name); + } + else { + char uid[16]; + ap_snprintf(uid, sizeof(uid), "user#%lu", + (unsigned long) r->finfo.st_uid); + table_set(e, "USER_NAME", uid); + } + + if ((t = strrchr(r->filename, '/'))) { + table_set(e, "DOCUMENT_NAME", ++t); + } + else { + table_set(e, "DOCUMENT_NAME", r->uri); + } + if (r->args) { + char *arg_copy = pstrdup(r->pool, r->args); + + unescape_url(arg_copy); + table_set(e, "QUERY_STRING_UNESCAPED", + escape_shell_cmd(r->pool, arg_copy)); + } +} + + + +/* --------------------------- Parser functions --------------------------- */ + +#define OUTBUFSIZE 4096 +/* PUT_CHAR and FLUSH_BUF currently only work within the scope of + * find_string(); they are hacks to avoid calling rputc for each and + * every character output. A common set of buffering calls for this + * type of output SHOULD be implemented. + */ +#define PUT_CHAR(c,r) \ + { \ + outbuf[outind++] = c; \ + if (outind == OUTBUFSIZE) { \ + FLUSH_BUF(r) \ + }; \ + } + +/* there SHOULD be some error checking on the return value of + * rwrite, however it is unclear what the API for rwrite returning + * errors is and little can really be done to help the error in + * any case. + */ +#define FLUSH_BUF(r) \ + { \ + rwrite(outbuf, outind, r); \ + outind = 0; \ + } + +/* + * f: file handle being read from + * c: character to read into + * ret: return value to use if input fails + * r: current request_rec + * + * This macro is redefined after find_string() for historical reasons + * to avoid too many code changes. This is one of the many things + * that should be fixed. + */ +#define GET_CHAR(f,c,ret,r) \ + { \ + int i = getc(f); \ + if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \ + if (ferror(f)) { \ + fprintf(stderr, "encountered error in GET_CHAR macro, " \ + "mod_include.\n"); \ + } \ + FLUSH_BUF(r); \ + pfclose(r->pool, f); \ + return ret; \ + } \ + c = (char)i; \ + } + +static int find_string(FILE *in, const char *str, request_rec *r, int printing) +{ + int x, l = strlen(str), p; + char outbuf[OUTBUFSIZE]; + int outind = 0; + char c; + + p = 0; + while (1) { + GET_CHAR(in, c, 1, r); + if (c == str[p]) { + if ((++p) == l) { + FLUSH_BUF(r); + return 0; + } + } + else { + if (printing) { + for (x = 0; x < p; x++) { + PUT_CHAR(str[x], r); + } + PUT_CHAR(c, r); + } + p = 0; + } + } +} + +#undef FLUSH_BUF +#undef PUT_CHAR +#undef GET_CHAR +#define GET_CHAR(f,c,r,p) \ + { \ + int i = getc(f); \ + if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \ + if (ferror(f)) { \ + fprintf(stderr, "encountered error in GET_CHAR macro, " \ + "mod_include.\n"); \ + } \ + pfclose(p, f); \ + return r; \ + } \ + c = (char)i; \ + } + +/* + * decodes a string containing html entities or numeric character references. + * 's' is overwritten with the decoded string. + * If 's' is syntatically incorrect, then the followed fixups will be made: + * unknown entities will be left undecoded; + * references to unused numeric characters will be deleted. + * In particular, � will not be decoded, but will be deleted. + * + * drtr + */ + +/* maximum length of any ISO-LATIN-1 HTML entity name. */ +#define MAXENTLEN (6) + +/* The following is a shrinking transformation, therefore safe. */ + +static void decodehtml(char *s) +{ + int val, i, j; + char *p = s; + const char *ents; + static const char * const entlist[MAXENTLEN + 1] = + { + NULL, /* 0 */ + NULL, /* 1 */ + "lt\074gt\076", /* 2 */ + "amp\046ETH\320eth\360", /* 3 */ + "quot\042Auml\304Euml\313Iuml\317Ouml\326Uuml\334auml\344euml\353\ +iuml\357ouml\366uuml\374yuml\377", /* 4 */ + "Acirc\302Aring\305AElig\306Ecirc\312Icirc\316Ocirc\324Ucirc\333\ +THORN\336szlig\337acirc\342aring\345aelig\346ecirc\352icirc\356ocirc\364\ +ucirc\373thorn\376", /* 5 */ + "Agrave\300Aacute\301Atilde\303Ccedil\307Egrave\310Eacute\311\ +Igrave\314Iacute\315Ntilde\321Ograve\322Oacute\323Otilde\325Oslash\330\ +Ugrave\331Uacute\332Yacute\335agrave\340aacute\341atilde\343ccedil\347\ +egrave\350eacute\351igrave\354iacute\355ntilde\361ograve\362oacute\363\ +otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */ + }; + + for (; *s != '\0'; s++, p++) { + if (*s != '&') { + *p = *s; + continue; + } + /* find end of entity */ + for (i = 1; s[i] != ';' && s[i] != '\0'; i++) { + continue; + } + + if (s[i] == '\0') { /* treat as normal data */ + *p = *s; + continue; + } + + /* is it numeric ? */ + if (s[1] == '#') { + for (j = 2, val = 0; j < i && isdigit(s[j]); j++) { + val = val * 10 + s[j] - '0'; + } + s += i; + if (j < i || val <= 8 || (val >= 11 && val <= 31) || + (val >= 127 && val <= 160) || val >= 256) { + p--; /* no data to output */ + } + else { + *p = val; + } + } + else { + j = i - 1; + if (i - 1 > MAXENTLEN || entlist[i - 1] == NULL) { + /* wrong length */ + *p = '&'; + continue; /* skip it */ + } + for (ents = entlist[i - 1]; *ents != '\0'; ents += i) { + if (strncmp(s + 1, ents, i - 1) == 0) { + break; + } + } + + if (*ents == '\0') { + *p = '&'; /* unknown */ + } + else { + *p = ((const unsigned char *) ents)[i - 1]; + s += i; + } + } + } + + *p = '\0'; +} + +/* + * extract the next tag name and value. + * if there are no more tags, set the tag name to 'done' + * the tag value is html decoded if dodecode is non-zero + */ + +static char *get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode) +{ + char *t = tag, *tag_val, c, term; + + /* makes code below a little less cluttered */ + --tagbuf_len; + + do { /* skip whitespace */ + GET_CHAR(in, c, NULL, p); + } while (isspace(c)); + + /* tags can't start with - */ + if (c == '-') { + GET_CHAR(in, c, NULL, p); + if (c == '-') { + do { + GET_CHAR(in, c, NULL, p); + } while (isspace(c)); + if (c == '>') { + safe_copy(tag, "done", tagbuf_len); + return tag; + } + } + return NULL; /* failed */ + } + + /* find end of tag name */ + while (1) { + if (t - tag == tagbuf_len) { + *t = '\0'; + return NULL; + } + if (c == '=' || isspace(c)) { + break; + } + *(t++) = tolower(c); + GET_CHAR(in, c, NULL, p); + } + + *t++ = '\0'; + tag_val = t; + + while (isspace(c)) { + GET_CHAR(in, c, NULL, p); /* space before = */ + } + if (c != '=') { + ungetc(c, in); + return NULL; + } + + do { + GET_CHAR(in, c, NULL, p); /* space after = */ + } while (isspace(c)); + + /* we should allow a 'name' as a value */ + + if (c != '"' && c != '\'') { + return NULL; + } + term = c; + while (1) { + GET_CHAR(in, c, NULL, p); + if (t - tag == tagbuf_len) { + *t = '\0'; + return NULL; + } +/* Want to accept \" as a valid character within a string. */ + if (c == '\\') { + *(t++) = c; /* Add backslash */ + GET_CHAR(in, c, NULL, p); + if (c == term) { /* Only if */ + *(--t) = c; /* Replace backslash ONLY for terminator */ + } + } + else if (c == term) { + break; + } + *(t++) = c; + } + *t = '\0'; + if (dodecode) { + decodehtml(tag_val); + } + return pstrdup(p, tag_val); +} + +static int get_directive(FILE *in, char *dest, size_t len, pool *p) +{ + char *d = dest; + char c; + + /* make room for nul terminator */ + --len; + + /* skip initial whitespace */ + while (1) { + GET_CHAR(in, c, 1, p); + if (!isspace(c)) { + break; + } + } + /* now get directive */ + while (1) { + if (d - dest == len) { + return 1; + } + *d++ = tolower(c); + GET_CHAR(in, c, 1, p); + if (isspace(c)) { + break; + } + } + *d = '\0'; + return 0; +} + +/* + * Do variable substitution on strings + */ +static void parse_string(request_rec *r, const char *in, char *out, + size_t length, int leave_name) +{ + char ch; + char *next = out; + char *end_out; + + /* leave room for nul terminator */ + end_out = out + length - 1; + + while ((ch = *in++) != '\0') { + switch (ch) { + case '\\': + if (next == end_out) { + /* truncated */ + *next = '\0'; + return; + } + if (*in == '$') { + *next++ = *in++; + } + else { + *next++ = ch; + } + break; + case '$': + { + char var[MAX_STRING_LEN]; + const char *start_of_var_name; + const char *end_of_var_name; /* end of var name + 1 */ + const char *expansion; + const char *val; + size_t l; + + /* guess that the expansion won't happen */ + expansion = in - 1; + if (*in == '{') { + ++in; + start_of_var_name = in; + in = strchr(in, '}'); + if (in == NULL) { + log_printf(r->server, + "Missing '}' on variable \"%s\" in %s", + expansion, r->filename); + *next = '\0'; + return; + } + end_of_var_name = in; + ++in; + } + else { + start_of_var_name = in; + while (isalnum(*in) || *in == '_') { + ++in; + } + end_of_var_name = in; + } + /* what a pain, too bad there's no table_getn where you can + * pass a non-nul terminated string */ + l = end_of_var_name - start_of_var_name; + l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l; + memcpy(var, start_of_var_name, l); + var[l] = '\0'; + + val = table_get(r->subprocess_env, var); + if (val) { + expansion = val; + l = strlen(expansion); + } + else if (leave_name) { + l = in - expansion; + } + else { + break; /* no expansion to be done */ + } + l = (l > end_out - next) ? (end_out - next) : l; + memcpy(next, expansion, l); + next += l; + break; + } + default: + if (next == end_out) { + /* truncated */ + *next = '\0'; + return; + } + *next++ = ch; + break; + } + } + *next = '\0'; + return; +} + +/* --------------------------- Action handlers ---------------------------- */ + +static int include_cgi(char *s, request_rec *r) +{ + request_rec *rr = sub_req_lookup_uri(s, r); + int rr_status; + + if (rr->status != HTTP_OK) { + return -1; + } + + /* No hardwired path info or query allowed */ + + if ((rr->path_info && rr->path_info[0]) || rr->args) { + return -1; + } + if (rr->finfo.st_mode == 0) { + return -1; + } + + /* Script gets parameters of the *document*, for back compatibility */ + + rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */ + rr->args = r->args; + + /* Force sub_req to be treated as a CGI request, even if ordinary + * typing rules would have called it something else. + */ + + rr->content_type = CGI_MAGIC_TYPE; + + /* Run it. */ + + rr_status = run_sub_req(rr); + if (is_HTTP_REDIRECT(rr_status)) { + char *location = table_get(rr->headers_out, "Location"); + location = escape_html(rr->pool, location); + rvputs(r, "<A HREF=\"", location, "\">", location, "</A>", NULL); + } + + destroy_sub_req(rr); + chdir_file(r->filename); + + return 0; +} + +/* ensure that path is relative, and does not contain ".." elements + * ensentially ensure that it does not match the regex: + * (^/|(^|/)\.\.(/|$)) + * XXX: this needs os abstraction... consider c:..\foo in win32 + */ +static int is_only_below(const char *path) +{ + if (path[0] == '/') { + return 0; + } + if (path[0] == '.' && path[1] == '.' + && (path[2] == '\0' || path[2] == '/')) { + return 0; + } + while (*path) { + if (*path == '/' && path[1] == '.' && path[2] == '.' + && (path[3] == '\0' || path[3] == '/')) { + return 0; + } + ++path; + } + return 1; +} + +static int handle_include(FILE *in, request_rec *r, const char *error, int noexec) +{ + char tag[MAX_STRING_LEN]; + char parsed_string[MAX_STRING_LEN]; + char *tag_val; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + if (!strcmp(tag, "file") || !strcmp(tag, "virtual")) { + request_rec *rr = NULL; + char *error_fmt = NULL; + + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (tag[0] == 'f') { + /* be safe; only files in this directory or below allowed */ + if (!is_only_below(parsed_string)) { + error_fmt = "unable to include file \"%s\" " + "in parsed file %s"; + } + else { + rr = sub_req_lookup_file(parsed_string, r); + } + } + else { + rr = sub_req_lookup_uri(parsed_string, r); + } + + if (!error_fmt && rr->status != HTTP_OK) { + error_fmt = "unable to include \"%s\" in parsed file %s"; + } + + if (!error_fmt && noexec && rr->content_type + && (strncmp(rr->content_type, "text/", 5))) { + error_fmt = "unable to include potential exec \"%s\" " + "in parsed file %s"; + } + if (error_fmt == NULL) { + request_rec *p; + + for (p = r; p != NULL; p = p->main) { + if (strcmp(p->filename, rr->filename) == 0) { + break; + } + } + if (p != NULL) { + error_fmt = "Recursive include of \"%s\" " + "in parsed file %s"; + } + } + + if (!error_fmt && run_sub_req(rr)) { + error_fmt = "unable to include \"%s\" in parsed file %s"; + } + chdir_file(r->filename); + + if (error_fmt) { + log_printf(r->server, error_fmt, tag_val, r->filename); + rputs(error, r); + } + + if (rr != NULL) { + destroy_sub_req(rr); + } + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + log_printf(r->server, + "unknown parameter \"%s\" to tag include in %s", + tag, r->filename); + rputs(error, r); + } + } +} + +typedef struct { + request_rec *r; + char *s; +} include_cmd_arg; + +static void include_cmd_child(void *arg) +{ + request_rec *r = ((include_cmd_arg *) arg)->r; + char *s = ((include_cmd_arg *) arg)->s; + table *env = r->subprocess_env; +#ifdef DEBUG_INCLUDE_CMD + FILE *dbg = fopen("/dev/tty", "w"); +#endif + char err_string[MAX_STRING_LEN]; + +#ifdef DEBUG_INCLUDE_CMD +#ifdef __EMX__ + /* under OS/2 /dev/tty is referenced as con */ + FILE *dbg = fopen("con", "w"); +#else + fprintf(dbg, "Attempting to include command '%s'\n", s); +#endif +#endif + + if (r->path_info && r->path_info[0] != '\0') { + request_rec *pa_req; + + table_set(env, "PATH_INFO", escape_shell_cmd(r->pool, r->path_info)); + + pa_req = sub_req_lookup_uri(escape_uri(r->pool, r->path_info), r); + if (pa_req->filename) { + table_set(env, "PATH_TRANSLATED", + pstrcat(r->pool, pa_req->filename, pa_req->path_info, + NULL)); + } + } + + if (r->args) { + char *arg_copy = pstrdup(r->pool, r->args); + + table_set(env, "QUERY_STRING", r->args); + unescape_url(arg_copy); + table_set(env, "QUERY_STRING_UNESCAPED", + escape_shell_cmd(r->pool, arg_copy)); + } + + error_log2stderr(r->server); + +#ifdef DEBUG_INCLUDE_CMD + fprintf(dbg, "Attempting to exec '%s'\n", s); +#endif + cleanup_for_exec(); + /* set shellcmd flag to pass arg to SHELL_PATH */ + call_exec(r, s, create_environment(r->pool, env), 1); + /* Oh, drat. We're still here. The log file descriptors are closed, + * so we have to whimper a complaint onto stderr... + */ + +#ifdef DEBUG_INCLUDE_CMD + fprintf(dbg, "Exec failed\n"); +#endif + ap_snprintf(err_string, sizeof(err_string), + "httpd: exec of %s failed, reason: %s (errno = %d)\n", + SHELL_PATH, strerror(errno), errno); + write(STDERR_FILENO, err_string, strlen(err_string)); + exit(0); +} + +static int include_cmd(char *s, request_rec *r) +{ + include_cmd_arg arg; + FILE *f; + + arg.r = r; + arg.s = s; + + if (!spawn_child(r->pool, include_cmd_child, &arg, + kill_after_timeout, NULL, &f)) { + return -1; + } + + send_fd(f, r); + pfclose(r->pool, f); /* will wait for zombie when + * r->pool is cleared + */ + return 0; +} + + +static int handle_exec(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char *file = r->filename; + char parsed_string[MAX_STRING_LEN]; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + if (!strcmp(tag, "cmd")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1); + if (include_cmd(parsed_string, r) == -1) { + log_printf(r->server, + "execution failure for parameter \"%s\" " + "to tag exec in file %s", + tag, r->filename); + rputs(error, r); + } + /* just in case some stooge changed directories */ + chdir_file(r->filename); + } + else if (!strcmp(tag, "cgi")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (include_cgi(parsed_string, r) == -1) { + log_printf(r->server, + "invalid CGI ref \"%s\" in %s", tag_val, file); + rputs(error, r); + } + /* grumble groan */ + chdir_file(r->filename); + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + log_printf(r->server, + "unknown parameter \"%s\" to tag exec in %s", + tag, file); + rputs(error, r); + } + } + +} + +static int handle_echo(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + if (!strcmp(tag, "var")) { + char *val = table_get(r->subprocess_env, tag_val); + + if (val) { + rputs(val, r); + } + else { + rputs("(none)", r); + } + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + log_printf(r->server, + "unknown parameter \"%s\" to tag echo in %s", + tag, r->filename); + rputs(error, r); + } + } +} + +#ifdef USE_PERL_SSI +static int handle_perl(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + SV *sub = Nullsv; + AV *av = newAV(); + + if (!(allow_options(r) & OPT_INCLUDES)) { + log_printf(r->server, + "httpd: #perl SSI disallowed by IncludesNoExec in %s", + r->filename); + return DECLINED; + } + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + break; + } + if (strnEQ(tag, "sub", 3)) { + sub = newSVpv(tag_val, 0); + } + else if (strnEQ(tag, "arg", 3)) { + av_push(av, newSVpv(tag_val, 0)); + } + else if (strnEQ(tag, "done", 4)) { + break; + } + } + perl_stdout2client(r); + perl_call_handler(sub, r, av); + return OK; +} +#endif + +/* error and tf must point to a string with room for at + * least MAX_STRING_LEN characters + */ +static int handle_config(FILE *in, request_rec *r, char *error, char *tf, + int *sizefmt) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char parsed_string[MAX_STRING_LEN]; + table *env = r->subprocess_env; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) { + return 1; + } + if (!strcmp(tag, "errmsg")) { + parse_string(r, tag_val, error, MAX_STRING_LEN, 0); + } + else if (!strcmp(tag, "timefmt")) { + time_t date = r->request_time; + + parse_string(r, tag_val, tf, MAX_STRING_LEN, 0); + table_set(env, "DATE_LOCAL", ht_time(r->pool, date, tf, 0)); + table_set(env, "DATE_GMT", ht_time(r->pool, date, tf, 1)); + table_set(env, "LAST_MODIFIED", + ht_time(r->pool, r->finfo.st_mtime, tf, 0)); + } + else if (!strcmp(tag, "sizefmt")) { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + decodehtml(parsed_string); + if (!strcmp(parsed_string, "bytes")) { + *sizefmt = SIZEFMT_BYTES; + } + else if (!strcmp(parsed_string, "abbrev")) { + *sizefmt = SIZEFMT_KMG; + } + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + log_printf(r->server, + "unknown parameter \"%s\" to tag config in %s", + tag, r->filename); + rputs(error, r); + } + } +} + + +static int find_file(request_rec *r, const char *directive, const char *tag, + char *tag_val, struct stat *finfo, const char *error) +{ + char *to_send; + + if (!strcmp(tag, "file")) { + getparents(tag_val); /* get rid of any nasties */ + to_send = make_full_path(r->pool, "./", tag_val); + if (stat(to_send, finfo) == -1) { + log_printf(r->server, + "unable to get information about \"%s\" " + "in parsed file %s", + to_send, r->filename); + rputs(error, r); + return -1; + } + return 0; + } + else if (!strcmp(tag, "virtual")) { + request_rec *rr = sub_req_lookup_uri(tag_val, r); + + if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) { + memcpy((char *) finfo, (const char *) &rr->finfo, + sizeof(struct stat)); + destroy_sub_req(rr); + return 0; + } + else { + log_printf(r->server, + "unable to get information about \"%s\" " + "in parsed file %s", + tag_val, r->filename); + rputs(error, r); + destroy_sub_req(rr); + return -1; + } + } + else { + log_printf(r->server, + "unknown parameter \"%s\" to tag %s in %s", + tag, directive, r->filename); + rputs(error, r); + return -1; + } +} + + +static int handle_fsize(FILE *in, request_rec *r, const char *error, int sizefmt) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + struct stat finfo; + char parsed_string[MAX_STRING_LEN]; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) { + if (sizefmt == SIZEFMT_KMG) { + send_size(finfo.st_size, r); + } + else { + int l, x; +#if defined(BSD) && BSD > 199305 + /* ap_snprintf can't handle %qd */ + sprintf(tag, "%qd", finfo.st_size); +#else + ap_snprintf(tag, sizeof(tag), "%ld", finfo.st_size); +#endif + l = strlen(tag); /* grrr */ + for (x = 0; x < l; x++) { + if (x && (!((l - x) % 3))) { + rputc(',', r); + } + rputc(tag[x], r); + } + } + } + } + } +} + +static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + struct stat finfo; + char parsed_string[MAX_STRING_LEN]; + + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + return 0; + } + else { + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) { + rputs(ht_time(r->pool, finfo.st_mtime, tf, 0), r); + } + } + } +} + +static int re_check(request_rec *r, char *string, char *rexp) +{ + regex_t *compiled; + int regex_error; + + compiled = pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB); + if (compiled == NULL) { + log_printf(r->server, "unable to compile pattern \"%s\"", rexp); + return -1; + } + regex_error = regexec(compiled, string, 0, (regmatch_t *) NULL, 0); + pregfree(r->pool, compiled); + return (!regex_error); +} + +enum token_type { + token_string, + token_and, token_or, token_not, token_eq, token_ne, + token_rbrace, token_lbrace, token_group, + token_ge, token_le, token_gt, token_lt +}; +struct token { + enum token_type type; + char value[MAX_STRING_LEN]; +}; + +/* there is an implicit assumption here that string is at most MAX_STRING_LEN-1 + * characters long... + */ +static const char *get_ptoken(request_rec *r, const char *string, struct token *token) +{ + char ch; + int next = 0; + int qs = 0; + + /* Skip leading white space */ + if (string == (char *) NULL) { + return (char *) NULL; + } + while ((ch = *string++)) { + if (!isspace(ch)) { + break; + } + } + if (ch == '\0') { + return (char *) NULL; + } + + token->type = token_string; /* the default type */ + switch (ch) { + case '(': + token->type = token_lbrace; + return (string); + case ')': + token->type = token_rbrace; + return (string); + case '=': + token->type = token_eq; + return (string); + case '!': + if (*string == '=') { + token->type = token_ne; + return (string + 1); + } + else { + token->type = token_not; + return (string); + } + case '\'': + token->type = token_string; + qs = 1; + break; + case '|': + if (*string == '|') { + token->type = token_or; + return (string + 1); + } + break; + case '&': + if (*string == '&') { + token->type = token_and; + return (string + 1); + } + break; + case '>': + if (*string == '=') { + token->type = token_ge; + return (string + 1); + } + else { + token->type = token_gt; + return (string); + } + case '<': + if (*string == '=') { + token->type = token_le; + return (string + 1); + } + else { + token->type = token_lt; + return (string); + } + default: + token->type = token_string; + break; + } + /* We should only be here if we are in a string */ + if (!qs) { + token->value[next++] = ch; + } + + /* + * Yes I know that goto's are BAD. But, c doesn't allow me to + * exit a loop from a switch statement. Yes, I could use a flag, + * but that is (IMHO) even less readable/maintainable than the goto. + */ + /* + * I used the ++string throughout this section so that string + * ends up pointing to the next token and I can just return it + */ + for (ch = *string; ch != '\0'; ch = *++string) { + if (ch == '\\') { + if ((ch = *++string) == '\0') { + goto TOKEN_DONE; + } + token->value[next++] = ch; + continue; + } + if (!qs) { + if (isspace(ch)) { + goto TOKEN_DONE; + } + switch (ch) { + case '(': + goto TOKEN_DONE; + case ')': + goto TOKEN_DONE; + case '=': + goto TOKEN_DONE; + case '!': + goto TOKEN_DONE; + case '|': + if (*(string + 1) == '|') { + goto TOKEN_DONE; + } + break; + case '&': + if (*(string + 1) == '&') { + goto TOKEN_DONE; + } + break; + case '<': + goto TOKEN_DONE; + case '>': + goto TOKEN_DONE; + } + token->value[next++] = ch; + } + else { + if (ch == '\'') { + qs = 0; + ++string; + goto TOKEN_DONE; + } + token->value[next++] = ch; + } + } + TOKEN_DONE: + /* If qs is still set, I have an unmatched ' */ + if (qs) { + rputs("\nUnmatched '\n", r); + next = 0; + } + token->value[next] = '\0'; + return (string); +} + + +/* + * Hey I still know that goto's are BAD. I don't think that I've ever + * used two in the same project, let alone the same file before. But, + * I absolutely want to make sure that I clean up the memory in all + * cases. And, without rewriting this completely, the easiest way + * is to just branch to the return code which cleans it up. + */ +/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1 + * characters long... + */ +static int parse_expr(request_rec *r, const char *expr, const char *error) +{ + struct parse_node { + struct parse_node *left, *right, *parent; + struct token token; + int value, done; + } *root, *current, *new; + const char *parse; + char buffer[MAX_STRING_LEN]; + pool *expr_pool; + int retval = 0; + + if ((parse = expr) == (char *) NULL) { + return (0); + } + root = current = (struct parse_node *) NULL; + expr_pool = make_sub_pool(r->pool); + + /* Create Parse Tree */ + while (1) { + new = (struct parse_node *) palloc(expr_pool, + sizeof(struct parse_node)); + new->parent = new->left = new->right = (struct parse_node *) NULL; + new->done = 0; + if ((parse = get_ptoken(r, parse, &new->token)) == (char *) NULL) { + break; + } + switch (new->token.type) { + + case token_string: +#ifdef DEBUG_INCLUDE + rvputs(r, " Token: string (", new->token.value, ")\n", NULL); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + switch (current->token.type) { + case token_string: + if (current->token.value[0] != '\0') { + strncat(current->token.value, " ", + MAX_STRING_LEN - strlen(current->token.value) - 1); + } + strncat(current->token.value, new->token.value, + MAX_STRING_LEN - strlen(current->token.value) - 1); + current->token.value[sizeof(current->token.value) - 1] = '\0'; + break; + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_not: + case token_ge: + case token_gt: + case token_le: + case token_lt: + new->parent = current; + current = current->right = new; + break; + default: + log_printf(r->server, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + break; + + case token_and: + case token_or: +#ifdef DEBUG_INCLUDE + rputs(" Token: and/or\n", r); +#endif + if (current == (struct parse_node *) NULL) { + log_printf(r->server, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: + case token_group: + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_ge: + case token_gt: + case token_le: + case token_lt: + current = current->parent; + continue; + case token_lbrace: + break; + default: + log_printf(r->server, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_not: +#ifdef DEBUG_INCLUDE + rputs(" Token: not\n", r); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_ge: + case token_gt: + case token_le: + case token_lt: + break; + default: + log_printf(r->server, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_eq: + case token_ne: + case token_ge: + case token_gt: + case token_le: + case token_lt: +#ifdef DEBUG_INCLUDE + rputs(" Token: eq/ne/ge/gt/le/lt\n", r); +#endif + if (current == (struct parse_node *) NULL) { + log_printf(r->server, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: + case token_group: + current = current->parent; + continue; + case token_lbrace: + case token_and: + case token_or: + break; + case token_not: + case token_eq: + case token_ne: + case token_ge: + case token_gt: + case token_le: + case token_lt: + default: + log_printf(r->server, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + + case token_rbrace: +#ifdef DEBUG_INCLUDE + rputs(" Token: rbrace\n", r); +#endif + while (current != (struct parse_node *) NULL) { + if (current->token.type == token_lbrace) { + current->token.type = token_group; + break; + } + current = current->parent; + } + if (current == (struct parse_node *) NULL) { + log_printf(r->server, "Unmatched ')' in \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + break; + + case token_lbrace: +#ifdef DEBUG_INCLUDE + rputs(" Token: lbrace\n", r); +#endif + if (current == (struct parse_node *) NULL) { + root = current = new; + break; + } + /* Percolate upwards */ + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_not: + case token_eq: + case token_ne: + case token_and: + case token_or: + case token_lbrace: + case token_ge: + case token_gt: + case token_le: + case token_lt: + break; + case token_string: + case token_group: + default: + log_printf(r->server, + "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + break; + } + if (current == (struct parse_node *) NULL) { + new->left = root; + new->left->parent = new; + new->parent = (struct parse_node *) NULL; + root = new; + } + else { + new->left = current->right; + current->right = new; + new->parent = current; + } + current = new; + break; + default: + break; + } + } + + /* Evaluate Parse Tree */ + current = root; + while (current != (struct parse_node *) NULL) { + switch (current->token.type) { + case token_string: +#ifdef DEBUG_INCLUDE + rputs(" Evaluate string\n", r); +#endif + parse_string(r, current->token.value, buffer, sizeof(buffer), 0); + safe_copy(current->token.value, buffer, sizeof(current->token.value)); + current->value = (current->token.value[0] != '\0'); + current->done = 1; + current = current->parent; + break; + + case token_and: + case token_or: +#ifdef DEBUG_INCLUDE + rputs(" Evaluate and/or\n", r); +#endif + if (current->left == (struct parse_node *) NULL || + current->right == (struct parse_node *) NULL) { + log_printf(r->server, "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + if (!current->left->done) { + switch (current->left->token.type) { + case token_string: + parse_string(r, current->left->token.value, + buffer, sizeof(buffer), 0); + safe_copy(current->left->token.value, buffer, + sizeof(current->left->token.value)); + current->left->value = (current->left->token.value[0] != '\0'); + current->left->done = 1; + break; + default: + current = current->left; + continue; + } + } + if (!current->right->done) { + switch (current->right->token.type) { + case token_string: + parse_string(r, current->right->token.value, + buffer, sizeof(buffer), 0); + safe_copy(current->right->token.value, buffer, + sizeof(current->right->token.value)); + current->right->value = (current->right->token.value[0] != '\0'); + current->right->done = 1; + break; + default: + current = current->right; + continue; + } + } +#ifdef DEBUG_INCLUDE + rvputs(r, " Left: ", current->left->value ? "1" : "0", + "\n", NULL); + rvputs(r, " Right: ", current->right->value ? "1" : "0", + "\n", NULL); +#endif + if (current->token.type == token_and) { + current->value = current->left->value && current->right->value; + } + else { + current->value = current->left->value || current->right->value; + } +#ifdef DEBUG_INCLUDE + rvputs(r, " Returning ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_eq: + case token_ne: +#ifdef DEBUG_INCLUDE + rputs(" Evaluate eq/ne\n", r); +#endif + if ((current->left == (struct parse_node *) NULL) || + (current->right == (struct parse_node *) NULL) || + (current->left->token.type != token_string) || + (current->right->token.type != token_string)) { + log_printf(r->server, "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + parse_string(r, current->left->token.value, + buffer, sizeof(buffer), 0); + safe_copy(current->left->token.value, buffer, + sizeof(current->left->token.value)); + parse_string(r, current->right->token.value, + buffer, sizeof(buffer), 0); + safe_copy(current->right->token.value, buffer, + sizeof(current->right->token.value)); + if (current->right->token.value[0] == '/') { + int len; + len = strlen(current->right->token.value); + if (current->right->token.value[len - 1] == '/') { + current->right->token.value[len - 1] = '\0'; + } + else { + log_printf(r->server, "Invalid rexp \"%s\" in file %s", + current->right->token.value, r->filename); + rputs(error, r); + goto RETURN; + } +#ifdef DEBUG_INCLUDE + rvputs(r, " Re Compare (", current->left->token.value, + ") with /", ¤t->right->token.value[1], "/\n", NULL); +#endif + current->value = + re_check(r, current->left->token.value, + ¤t->right->token.value[1]); + } + else { +#ifdef DEBUG_INCLUDE + rvputs(r, " Compare (", current->left->token.value, + ") with (", current->right->token.value, ")\n", NULL); +#endif + current->value = + (strcmp(current->left->token.value, + current->right->token.value) == 0); + } + if (current->token.type == token_ne) { + current->value = !current->value; + } +#ifdef DEBUG_INCLUDE + rvputs(r, " Returning ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + case token_ge: + case token_gt: + case token_le: + case token_lt: +#ifdef DEBUG_INCLUDE + rputs(" Evaluate ge/gt/le/lt\n", r); +#endif + if ((current->left == (struct parse_node *) NULL) || + (current->right == (struct parse_node *) NULL) || + (current->left->token.type != token_string) || + (current->right->token.type != token_string)) { + log_printf(r->server, "Invalid expression \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + } + parse_string(r, current->left->token.value, + buffer, sizeof(buffer), 0); + safe_copy(current->left->token.value, buffer, + sizeof(current->left->token.value)); + parse_string(r, current->right->token.value, + buffer, sizeof(buffer), 0); + safe_copy(current->right->token.value, buffer, + sizeof(current->right->token.value)); +#ifdef DEBUG_INCLUDE + rvputs(r, " Compare (", current->left->token.value, + ") with (", current->right->token.value, ")\n", NULL); +#endif + current->value = + strcmp(current->left->token.value, + current->right->token.value); + if (current->token.type == token_ge) { + current->value = current->value >= 0; + } + else if (current->token.type == token_gt) { + current->value = current->value > 0; + } + else if (current->token.type == token_le) { + current->value = current->value <= 0; + } + else if (current->token.type == token_lt) { + current->value = current->value < 0; + } + else { + current->value = 0; /* Don't return -1 if unknown token */ + } +#ifdef DEBUG_INCLUDE + rvputs(r, " Returning ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_not: + if (current->right != (struct parse_node *) NULL) { + if (!current->right->done) { + current = current->right; + continue; + } + current->value = !current->right->value; + } + else { + current->value = 0; + } +#ifdef DEBUG_INCLUDE + rvputs(r, " Evaluate !: ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_group: + if (current->right != (struct parse_node *) NULL) { + if (!current->right->done) { + current = current->right; + continue; + } + current->value = current->right->value; + } + else { + current->value = 1; + } +#ifdef DEBUG_INCLUDE + rvputs(r, " Evaluate (): ", current->value ? "1" : "0", + "\n", NULL); +#endif + current->done = 1; + current = current->parent; + break; + + case token_lbrace: + log_printf(r->server, "Unmatched '(' in \"%s\" in file %s", + expr, r->filename); + rputs(error, r); + goto RETURN; + + case token_rbrace: + log_printf(r->server, "Unmatched ')' in \"%s\" in file %s\n", + expr, r->filename); + rputs(error, r); + goto RETURN; + + default: + log_printf(r->server, "bad token type"); + rputs(error, r); + goto RETURN; + } + } + + retval = (root == (struct parse_node *) NULL) ? 0 : root->value; + RETURN: + destroy_pool(expr_pool); + return (retval); +} + +static int handle_if(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char *expr; + + expr = NULL; + while (1) { + tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0); + if (*tag == '\0') { + return 1; + } + else if (!strcmp(tag, "done")) { + if (expr == NULL) { + log_printf(r->server, "missing expr in if statement: %s", + r->filename); + rputs(error, r); + return 1; + } + *printing = *conditional_status = parse_expr(r, expr, error); +#ifdef DEBUG_INCLUDE + rvputs(r, "**** if conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + return 0; + } + else if (!strcmp(tag, "expr")) { + expr = tag_val; +#ifdef DEBUG_INCLUDE + rvputs(r, "**** if expr=\"", expr, "\"\n", NULL); +#endif + } + else { + log_printf(r->server, "unknown parameter \"%s\" to tag if in %s", + tag, r->filename); + rputs(error, r); + } + } +} + +static int handle_elif(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + char *expr; + + expr = NULL; + while (1) { + tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0); + if (*tag == '\0') { + return 1; + } + else if (!strcmp(tag, "done")) { +#ifdef DEBUG_INCLUDE + rvputs(r, "**** elif conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + if (*conditional_status) { + *printing = 0; + return (0); + } + if (expr == NULL) { + log_printf(r->server, "missing expr in elif statement: %s", + r->filename); + rputs(error, r); + return 1; + } + *printing = *conditional_status = parse_expr(r, expr, error); +#ifdef DEBUG_INCLUDE + rvputs(r, "**** elif conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + return 0; + } + else if (!strcmp(tag, "expr")) { + expr = tag_val; +#ifdef DEBUG_INCLUDE + rvputs(r, "**** if expr=\"", expr, "\"\n", NULL); +#endif + } + else { + log_printf(r->server, "unknown parameter \"%s\" to tag if in %s", + tag, r->filename); + rputs(error, r); + } + } +} + +static int handle_else(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { +#ifdef DEBUG_INCLUDE + rvputs(r, "**** else conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + *printing = !(*conditional_status); + *conditional_status = 1; + return 0; + } + else { + log_printf(r->server, "else directive does not take tags in %s", + r->filename); + if (*printing) { + rputs(error, r); + } + return -1; + } +} + +static int handle_endif(FILE *in, request_rec *r, const char *error, + int *conditional_status, int *printing) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { +#ifdef DEBUG_INCLUDE + rvputs(r, "**** endif conditional_status=\"", + *conditional_status ? "1" : "0", "\"\n", NULL); +#endif + *printing = 1; + *conditional_status = 1; + return 0; + } + else { + log_printf(r->server, "endif directive does not take tags in %s", + r->filename); + rputs(error, r); + return -1; + } +} + +static int handle_set(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char parsed_string[MAX_STRING_LEN]; + char *tag_val; + char *var; + + var = (char *) NULL; + while (1) { + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + return 0; + } + else if (!strcmp(tag, "var")) { + var = tag_val; + } + else if (!strcmp(tag, "value")) { + if (var == (char *) NULL) { + log_printf(r->server, + "variable must precede value in set directive in %s", + r->filename); + rputs(error, r); + return -1; + } + parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + table_set(r->subprocess_env, var, parsed_string); + } + else { + log_printf(r->server, "Invalid tag for set directive in %s", + r->filename); + rputs(error, r); + return -1; + } + } +} + +static int handle_printenv(FILE *in, request_rec *r, const char *error) +{ + char tag[MAX_STRING_LEN]; + char *tag_val; + table_entry *elts = (table_entry *) r->subprocess_env->elts; + int i; + + if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) { + return 1; + } + else if (!strcmp(tag, "done")) { + for (i = 0; i < r->subprocess_env->nelts; ++i) { + rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL); + } + return 0; + } + else { + log_printf(r->server, "printenv directive does not take tags in %s", + r->filename); + rputs(error, r); + return -1; + } +} + + + +/* -------------------------- The main function --------------------------- */ + +/* This is a stub which parses a file descriptor. */ + +static void send_parsed_content(FILE *f, request_rec *r) +{ + char directive[MAX_STRING_LEN], error[MAX_STRING_LEN]; + char timefmt[MAX_STRING_LEN]; + int noexec = allow_options(r) & OPT_INCNOEXEC; + int ret, sizefmt; + int if_nesting; + int printing; + int conditional_status; + + safe_copy(error, DEFAULT_ERROR_MSG, sizeof(error)); + safe_copy(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt)); + sizefmt = SIZEFMT_KMG; + +/* Turn printing on */ + printing = conditional_status = 1; + if_nesting = 0; + + chdir_file(r->filename); + if (r->args) { /* add QUERY stuff to env cause it ain't yet */ + char *arg_copy = pstrdup(r->pool, r->args); + + table_set(r->subprocess_env, "QUERY_STRING", r->args); + unescape_url(arg_copy); + table_set(r->subprocess_env, "QUERY_STRING_UNESCAPED", + escape_shell_cmd(r->pool, arg_copy)); + } + + while (1) { + if (!find_string(f, STARTING_SEQUENCE, r, printing)) { + if (get_directive(f, directive, sizeof(directive), r->pool)) { + log_printf(r->server, + "mod_include: error reading directive in %s", + r->filename); + rputs(error, r); + return; + } + if (!strcmp(directive, "if")) { + if (!printing) { + if_nesting++; + } + else { + ret = handle_if(f, r, error, &conditional_status, + &printing); + if_nesting = 0; + } + continue; + } + else if (!strcmp(directive, "else")) { + if (!if_nesting) { + ret = handle_else(f, r, error, &conditional_status, + &printing); + } + continue; + } + else if (!strcmp(directive, "elif")) { + if (!if_nesting) { + ret = handle_elif(f, r, error, &conditional_status, + &printing); + } + continue; + } + else if (!strcmp(directive, "endif")) { + if (!if_nesting) { + ret = handle_endif(f, r, error, &conditional_status, + &printing); + } + else { + if_nesting--; + } + continue; + } + if (!printing) { + continue; + } + if (!strcmp(directive, "exec")) { + if (noexec) { + log_printf(r->server, + "httpd: exec used but not allowed in %s", + r->filename); + if (printing) { + rputs(error, r); + } + ret = find_string(f, ENDING_SEQUENCE, r, 0); + } + else { + ret = handle_exec(f, r, error); + } + } + else if (!strcmp(directive, "config")) { + ret = handle_config(f, r, error, timefmt, &sizefmt); + } + else if (!strcmp(directive, "set")) { + ret = handle_set(f, r, error); + } + else if (!strcmp(directive, "include")) { + ret = handle_include(f, r, error, noexec); + } + else if (!strcmp(directive, "echo")) { + ret = handle_echo(f, r, error); + } + else if (!strcmp(directive, "fsize")) { + ret = handle_fsize(f, r, error, sizefmt); + } + else if (!strcmp(directive, "flastmod")) { + ret = handle_flastmod(f, r, error, timefmt); + } + else if (!strcmp(directive, "printenv")) { + ret = handle_printenv(f, r, error); + } +#ifdef USE_PERL_SSI + else if (!strcmp(directive, "perl")) { + ret = handle_perl(f, r, error); + } +#endif + else { + log_printf(r->server, "httpd: unknown directive \"%s\" " + "in parsed doc %s", + directive, r->filename); + if (printing) { + rputs(error, r); + } + ret = find_string(f, ENDING_SEQUENCE, r, 0); + } + if (ret) { + log_printf(r->server, "httpd: premature EOF in parsed file %s", + r->filename); + return; + } + } + else { + return; + } + } +} + +/***************************************************************** + * + * XBITHACK. Sigh... NB it's configurable per-directory; the compile-time + * option only changes the default. + */ + +module includes_module; +enum xbithack { + xbithack_off, xbithack_on, xbithack_full +}; + +#ifdef XBITHACK +#define DEFAULT_XBITHACK xbithack_full +#else +#define DEFAULT_XBITHACK xbithack_off +#endif + +static void *create_includes_dir_config(pool *p, char *dummy) +{ + enum xbithack *result = (enum xbithack *) palloc(p, sizeof(enum xbithack)); + *result = DEFAULT_XBITHACK; + return result; +} + +static const char *set_xbithack(cmd_parms *cmd, void *xbp, char *arg) +{ + enum xbithack *state = (enum xbithack *) xbp; + + if (!strcasecmp(arg, "off")) { + *state = xbithack_off; + } + else if (!strcasecmp(arg, "on")) { + *state = xbithack_on; + } + else if (!strcasecmp(arg, "full")) { + *state = xbithack_full; + } + else { + return "XBitHack must be set to Off, On, or Full"; + } + + return NULL; +} + +static int send_parsed_file(request_rec *r) +{ + FILE *f; + enum xbithack *state = + (enum xbithack *) get_module_config(r->per_dir_config, &includes_module); + int errstatus; + + if (!(allow_options(r) & OPT_INCLUDES)) { + return DECLINED; + } + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) { + return DECLINED; + } + if (r->finfo.st_mode == 0) { + log_printf(r->server, "File does not exist: %s", + (r->path_info + ? pstrcat(r->pool, r->filename, r->path_info, NULL) + : r->filename)); + return HTTP_NOT_FOUND; + } + + if (!(f = pfopen(r->pool, r->filename, "r"))) { + log_printf(r->server, + "file permissions deny server access: %s", r->filename); + return HTTP_FORBIDDEN; + } + + if (*state == xbithack_full +#ifndef __EMX__ + /* OS/2 dosen't support Groups. */ + && (r->finfo.st_mode & S_IXGRP) +#endif + ) { + errstatus = set_last_modified (r, r->finfo.st_mtime); + table_unset(r->headers_out, "ETag"); + if (errstatus) { + return errstatus; + } + } + + send_http_header(r); + + if (r->header_only) { + pfclose(r->pool, f); + return OK; + } + + if (r->main) { + /* Kludge --- for nested includes, we want to keep the + * subprocess environment of the base document (for compatibility); + * that means torquing our own last_modified date as well so that + * the LAST_MODIFIED variable gets reset to the proper value if + * the nested document resets <!--#config timefmt--> + */ + r->subprocess_env = r->main->subprocess_env; + r->finfo.st_mtime = r->main->finfo.st_mtime; + } + else { + add_common_vars(r); + add_cgi_vars(r); + add_include_vars(r, DEFAULT_TIME_FORMAT); + } + hard_timeout("send SSI", r); + + send_parsed_content(f, r); + + kill_timeout(r); + return OK; +} + +static int send_shtml_file(request_rec *r) +{ + r->content_type = "text/html"; + return send_parsed_file(r); +} + +static int xbithack_handler(request_rec *r) +{ +#ifdef __EMX__ + /* OS/2 dosen't currently support the xbithack. This is being worked on. */ + return DECLINED; +#else + enum xbithack *state; + + if (!(r->finfo.st_mode & S_IXUSR)) { + return DECLINED; + } + + state = (enum xbithack *) get_module_config(r->per_dir_config, + &includes_module); + + if (*state == xbithack_off) { + return DECLINED; + } + return send_parsed_file(r); +#endif +} + +static command_rec includes_cmds[] = +{ + {"XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full"}, + {NULL} +}; + +static handler_rec includes_handlers[] = +{ + {INCLUDES_MAGIC_TYPE, send_shtml_file}, + {INCLUDES_MAGIC_TYPE3, send_shtml_file}, + {"server-parsed", send_parsed_file}, + {"text/html", xbithack_handler}, + {NULL} +}; + +module includes_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_includes_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + includes_cmds, /* command table */ + includes_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_info.c b/usr.sbin/httpd/src/mod_info.c new file mode 100644 index 00000000000..9aedbec2cd5 --- /dev/null +++ b/usr.sbin/httpd/src/mod_info.c @@ -0,0 +1,455 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * Info Module. Display configuration information for the server and + * all included modules. + * + * <Location /server-info> + * SetHandler server-info + * </Location> + * + * GET /server-info - Returns full configuration page for server and all modules + * GET /server-info?server - Returns server configuration only + * GET /server-info?module_name - Returns configuration for a single module + * GET /server-info?list - Returns quick list of included modules + * + * Rasmus Lerdorf <rasmus@vex.net>, May 1996 + * + * 05.01.96 Initial Version + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" +#include "util_script.h" + +typedef struct mod_info_config_lines { + char *cmd; + char *line; + struct mod_info_config_lines *next; +} mod_info_config_lines; + +module info_module; +extern module *top_module; + +char *mod_info_html_cmd_string(char *string) { + char *s,*t; + static char ret[256]; /* What is the max size of a command? */ + char *end_ret; + + ret[0]='\0'; + s = string; + t=ret; + end_ret = t + sizeof(ret); + while((*s) && ((t-ret) < sizeof(ret))) { + if(*s=='<') { + strncpy(t,"<", end_ret - t); + t+=4; + } else if(*s=='>') { + strncpy(t,">", end_ret - t); + t+=4; + } else if(*s=='&') { + strncpy(t,"&", end_ret - t); + t+=5; + } else { + *t++=*s; + } + s++; + } + *t='\0'; + return(ret); +} + +mod_info_config_lines *mod_info_load_config(pool *p, char *filename, request_rec *r) { + char s[MAX_STRING_LEN]; + FILE *fp; + mod_info_config_lines *new, *ret=NULL, *prev=NULL; + char *t,*tt,o, *msg; + + fp = pfopen(p,filename,"r"); + if(!fp) { + msg = pstrcat + ( + r->pool, + "mod_info: couldn't open config file ", + filename, + NULL + ); + log_error (msg, r->server); + return NULL; + } + while(!cfg_getline(s,MAX_STRING_LEN,fp)) { + if(*s=='#') continue; /* skip comments */ + new = palloc(p,sizeof(struct mod_info_config_lines)); + new->next = NULL; + if(!ret) ret=new; + if(prev) prev->next=new; + t=strchr(s,' '); + tt=strchr(s,'\t'); + if(t && tt) t = (t<tt)?t:tt; + else if(tt) t=tt; + if(t) { + o=*t; + *t='\0'; + new->cmd = pstrdup(p,s); + new->line = pstrdup(p,t+1); + *t=o; + } else { + new->cmd = pstrdup(p,s); + new->line = NULL; + } + prev=new; + } + pfclose(p,fp); + return(ret); +} + +void mod_info_module_cmds(request_rec *r, mod_info_config_lines *cfg, command_rec *cmds,char *label) { + command_rec *cmd=cmds; + mod_info_config_lines *li=cfg,*li_st=NULL,*li_se=NULL,*block_start=NULL; + int lab=0, nest=0; + + while(li) { + if(!strncasecmp(li->cmd,"<directory",10) || + !strncasecmp(li->cmd,"<location",9) || + !strncasecmp(li->cmd,"<limit",6) || + !strncasecmp(li->cmd,"<files",6)) { + if(nest) li_se=li; + else li_st=li; + li=li->next; + nest++; + continue; + } else if(nest && (!strncasecmp(li->cmd,"</limit",7) || + !strncasecmp(li->cmd,"</location",10) || + !strncasecmp(li->cmd,"</directory",11) || + !strncasecmp(li->cmd,"</files",7))) { + if(block_start) { + if((nest==1 && block_start==li_st) || (nest==2 && block_start==li_se)) { + rputs("<dd><tt>",r); + if(nest==2) rputs(" ",r); + rputs(mod_info_html_cmd_string(li->cmd),r); + rputs(" ",r); + if(li->line) rputs(mod_info_html_cmd_string(li->line),r); + rputs("</tt>\n",r); + nest--; + if(!nest) { + block_start=NULL; + li_st=NULL; + } else { + block_start=li_st; + } + li_se=NULL; + } else { + nest--; + if(!nest) { + li_st=NULL; + } + li_se=NULL; + } + } else { + nest--; + if(!nest) { + li_st=NULL; + } + li_se=NULL; + } + li=li->next; + continue; + } + cmd = cmds; + while(cmd) { + if(cmd->name) { + if(!strcasecmp(cmd->name,li->cmd)) { + if(!lab) { + rputs("<dt><strong>",r); + rputs(label,r); + rputs("</strong>\n",r); + lab=1; + } + if(((nest && block_start==NULL) || (nest==2 && block_start==li_st)) + && (strncasecmp(li->cmd,"<directory",10) && + strncasecmp(li->cmd,"<location",9) && strncasecmp(li->cmd,"<limit",6) && + strncasecmp(li->cmd,"</limit",7) && strncasecmp(li->cmd,"</location",10) && + strncasecmp(li->cmd,"</directory",11) && + strncasecmp(li->cmd,"</files",7))) { + rputs("<dd><tt>",r); + rputs(mod_info_html_cmd_string(li_st->cmd),r); + rputs(" ",r); + if(li_st->line) rputs(mod_info_html_cmd_string(li_st->line),r); + rputs("</tt>\n",r); + block_start=li_st; + if(li_se) { + rputs("<dd><tt> ",r); + rputs(mod_info_html_cmd_string(li_se->cmd),r); + rputs(" ",r); + if(li_se->line) rputs(mod_info_html_cmd_string(li_se->line),r); + rputs("</tt>\n",r); + block_start=li_se; + } + } + rputs("<dd><tt>",r); + if(nest) rputs(" ",r); + if(nest==2) rputs(" ",r); + rputs(mod_info_html_cmd_string(li->cmd),r); + if(li->line) { + rputs(" <i>",r); + rputs(mod_info_html_cmd_string(li->line),r); + rputs("</i></tt>",r); + } + } + } else break; + cmd++; + } + li = li->next; + } +} + +int display_info(request_rec *r) { + module *modp = NULL; + char buf[512], *cfname; + command_rec *cmd=NULL; + handler_rec *hand=NULL; + server_rec *serv = r->server; + int comma=0; + mod_info_config_lines *mod_info_cfg_httpd=NULL; + mod_info_config_lines *mod_info_cfg_srm=NULL; + mod_info_config_lines *mod_info_cfg_access=NULL; + extern int standalone; + extern uid_t user_id; + extern char *user_name; + extern gid_t group_id; + extern int max_requests_per_child; + extern char *pid_fname; + extern char *scoreboard_fname; + extern int daemons_to_start; + extern int daemons_min_free; + extern int daemons_max_free; + extern int daemons_limit; + extern char server_root[MAX_STRING_LEN]; + extern char server_confname[MAX_STRING_LEN]; + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) + return DECLINED; + + r->content_type = "text/html"; + send_http_header(r); + if(r->header_only) { + return 0; + } + hard_timeout("send server info", r); + + rputs("<html><head><title>Server Information</title></head>\n",r); + rputs("<body><h1 align=center>Apache Server Information</h1>\n",r); + if(!r->args || strcasecmp(r->args,"list")) { + cfname = server_root_relative (r->pool, server_confname); + mod_info_cfg_httpd = mod_info_load_config (r->pool, cfname, r); + cfname = server_root_relative (r->pool, serv->srm_confname); + mod_info_cfg_srm = mod_info_load_config(r->pool, cfname, r); + cfname = server_root_relative (r->pool, serv->access_confname); + mod_info_cfg_access = mod_info_load_config (r->pool, cfname, r); + if(!r->args) { + rputs("<tt><a href=\"#server\">Server Settings</a>, ",r); + for(modp = top_module; modp; modp = modp->next) { + ap_snprintf(buf, sizeof(buf), "<a href=\"#%s\">%s</a>",modp->name,modp->name); + rputs(buf, r); + if(modp->next) rputs(", ",r); + } + rputs("</tt><hr>",r); + + } + if(!r->args || !strcasecmp(r->args,"server")) { + ap_snprintf(buf, sizeof(buf), "<a name=\"server\"><strong>Server Version:</strong> <font size=+1><tt>%s</tt></a></font><br>\n",SERVER_VERSION); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>API Version:</strong> <tt>%d</tt><br>\n",MODULE_MAGIC_NUMBER); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Run Mode:</strong> <tt>%s</tt><br>\n",standalone?"standalone":"inetd"); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>User/Group:</strong> <tt>%s(%d)/%d</tt><br>\n",user_name,(int)user_id,(int)group_id); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Hostname/port:</strong> <tt>%s:%u</tt><br>\n",serv->server_hostname,serv->port); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Daemons:</strong> <tt>start: %d min idle: %d max idle: %d max: %d</tt><br>\n",daemons_to_start,daemons_min_free,daemons_max_free,daemons_limit); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Max Requests:</strong> <tt>per child: %d keep alive: %s max per connection: %d</tt><br>\n",max_requests_per_child,serv->keep_alive ? "on":"off", serv->keep_alive_max); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Timeouts:</strong> <tt>connection: %d keep-alive: %d</tt><br>",serv->timeout,serv->keep_alive_timeout); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Server Root:</strong> <tt>%s</tt><br>\n",server_root); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Config File:</strong> <tt>%s</tt><br>\n",server_confname); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>PID File:</strong> <tt>%s</tt><br>\n",pid_fname); + rputs(buf,r); + ap_snprintf(buf, sizeof(buf), "<strong>Scoreboard File:</strong> <tt>%s</tt><br>\n",scoreboard_fname); + rputs(buf,r); + } + rputs("<hr><dl>",r); + for(modp = top_module; modp; modp = modp->next) { + if(!r->args || !strcasecmp(modp->name,r->args)) { + ap_snprintf(buf, sizeof(buf), "<dt><a name=\"%s\"><strong>Module Name:</strong> <font size=+1><tt>%s</tt></a></font>\n",modp->name,modp->name); + rputs(buf,r); + rputs("<dt><strong>Content-types affected:</strong>",r); + hand = modp->handlers; + if(hand) { + while(hand) { + if(hand->content_type) { + ap_snprintf(buf, sizeof(buf), " <tt>%s</tt>\n",hand->content_type); + rputs(buf,r); + } else break; + hand++; + if(hand && hand->content_type) rputs(",",r); + } + } else { + rputs("<tt> none</tt>",r); + } + rputs("<dt><strong>Module Groups:</strong> \n",r); + if(modp->translate_handler) { + rputs("<tt>Translate Handler</tt>\n",r); + comma=1; + } + if(modp->check_user_id) { + if(comma) rputs(", ",r); + rputs("<tt>User ID Checking</tt>\n",r); + comma=1; + } + if(modp->auth_checker) { + if(comma) rputs(", ",r); + rputs("<tt>Authentication Checking</tt>\n",r); + comma=1; + } + if(modp->access_checker) { + if(comma) rputs(", ",r); + rputs("<tt>Access Checking</tt>\n",r); + comma=1; + } + if(modp->type_checker) { + if(comma) rputs(", ",r); + rputs("<tt>Type Checking</tt>\n",r); + comma=1; + } + if(modp->fixer_upper) { + if(comma) rputs(", ",r); + rputs("<tt>Header Fixer</tt>\n",r); + comma=1; + } + if(modp->logger) { + if(comma) rputs(", ",r); + rputs("<tt>Logging</tt>\n",r); + comma=1; + } + if(!comma) rputs("<tt> none</tt>",r); + comma=0; + rputs("<dt><strong>Module Configuration Commands:</strong> ",r); + cmd = modp->cmds; + if(cmd) { + while(cmd) { + if(cmd->name) { + ap_snprintf(buf, sizeof(buf), "<dd><tt>%s - <i>",mod_info_html_cmd_string(cmd->name)); + rputs(buf,r); + if(cmd->errmsg) rputs(cmd->errmsg,r); + rputs("</i></tt>\n",r); + } else break; + cmd++; + } + rputs("<dt><strong>Current Configuration:</strong>\n",r); + mod_info_module_cmds(r,mod_info_cfg_httpd,modp->cmds,"httpd.conf"); + mod_info_module_cmds(r,mod_info_cfg_srm,modp->cmds,"srm.conf"); + mod_info_module_cmds(r,mod_info_cfg_access,modp->cmds,"access.conf"); + } else { + rputs("<tt> none</tt>\n",r); + } + rputs("<dt><hr>\n",r); + if(r->args) break; + } + } + if(!modp && r->args && strcasecmp(r->args,"server")) rputs("<b>No such module</b>\n",r); + } else { + for(modp = top_module; modp; modp = modp->next) { + rputs(modp->name,r); + if(modp->next) rputs("<br>",r); + } + } + rputs("</dl></body></html>\n",r); + /* Done, turn off timeout, close file and return */ + kill_timeout(r); + return 0; +} + +handler_rec info_handlers[] = { + { "server-info", display_info }, + { NULL } +}; + +module info_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + info_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_log_agent.c b/usr.sbin/httpd/src/mod_log_agent.c new file mode 100644 index 00000000000..234de33ab24 --- /dev/null +++ b/usr.sbin/httpd/src/mod_log_agent.c @@ -0,0 +1,198 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + + +#include "httpd.h" +#include "http_config.h" + +module agent_log_module; + +static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); +#ifdef __EMX__ +/* OS/2 dosen't support users and groups */ +static mode_t xfer_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +typedef struct { + char *fname; + int agent_fd; +} agent_log_state; + +void *make_agent_log_state (pool *p, server_rec *s) +{ + agent_log_state *cls = + (agent_log_state *)palloc (p, sizeof (agent_log_state)); + + cls->fname = ""; + cls->agent_fd = -1; + + + return (void *)cls; +} + +const char *set_agent_log (cmd_parms *parms, void *dummy, char *arg) +{ + agent_log_state *cls = get_module_config (parms->server->module_config, + &agent_log_module); + + cls->fname = arg; + return NULL; +} + +command_rec agent_log_cmds[] = { +{ "AgentLog", set_agent_log, NULL, RSRC_CONF, TAKE1, + "the filename of the agent log" }, +{ NULL } +}; + +void agent_log_child (void *cmd) +{ + /* Child process code for 'AgentLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); +#ifdef __EMX__ + /* For OS/2 we need to use a '/' */ + execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + perror ("exec"); + fprintf (stderr, "Exec of shell for logging failed!!!\n"); + exit (1); +} + +void open_agent_log (server_rec *s, pool *p) +{ + agent_log_state *cls = get_module_config (s->module_config, + &agent_log_module); + + char *fname = server_root_relative (p, cls->fname); + + if (cls->agent_fd > 0) return; /* virtual log shared w/main server */ + + if (*cls->fname == '|') { + FILE *dummy; + + if (!spawn_child (p, agent_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL)) { + perror ("spawn_child"); + fprintf (stderr, "Couldn't fork child for AgentLog process\n"); + exit (1); + } + + cls->agent_fd = fileno (dummy); + } + else if(*cls->fname != '\0') { + if((cls->agent_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + perror("open"); + fprintf(stderr,"httpd: could not open agent log file %s.\n", fname); + exit(1); + } + } +} + +void init_agent_log (server_rec *s, pool *p) +{ + for (; s; s = s->next) open_agent_log (s, p); +} + +int agent_log_transaction(request_rec *orig) +{ + agent_log_state *cls = get_module_config (orig->server->module_config, + &agent_log_module); + + char str[HUGE_STRING_LEN]; + char *agent; + request_rec *r; + + if(cls->agent_fd <0) + return OK; + + for (r = orig; r->next; r = r->next) + continue; + if (*cls->fname == '\0') /* Don't log agent */ + return DECLINED; + + agent = table_get(orig->headers_in, "User-Agent"); + if(agent != NULL) + { + ap_snprintf(str, sizeof(str), "%s\n", agent); + write(cls->agent_fd, str, strlen(str)); + } + + return OK; +} + +module agent_log_module = { + STANDARD_MODULE_STUFF, + init_agent_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_agent_log_state, /* server config */ + NULL, /* merge server config */ + agent_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + agent_log_transaction, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_log_config.c b/usr.sbin/httpd/src/mod_log_config.c new file mode 100644 index 00000000000..d383cd5f6bc --- /dev/null +++ b/usr.sbin/httpd/src/mod_log_config.c @@ -0,0 +1,787 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * This is module implements the TransferLog directive (same as the + * common log module), and additional directives, LogFormat and CustomLog. + * + * + * Syntax: + * + * TransferLog fn Logs transfers to fn in standard log format, unless + * a custom format is set with LogFormat + * LogFormat format Set a log format from TransferLog files + * CustomLog fn format + * Log to file fn with format given by the format + * argument + * + * CookieLog fn For backwards compatability with old Cookie + * logging module - now deprecated. + * + * There can be any number of TransferLog and CustomLog + * commands. Each request will be logged to _ALL_ the + * named files, in the appropriate format. + * + * If no TransferLog or CustomLog directive appears in a VirtualHost, + * the request will be logged to the log file(s) defined outside + * the virtual host section. If a TransferLog or CustomLog directive + * appears in the VirtualHost section, the log files defined outside + * the VirtualHost will _not_ be used. This makes this module compatable + * with the CLF and config log modules, where the use of TransferLog + * inside the VirtualHost section overrides its use outside. + * + * Examples: + * + * TransferLog logs/access_log + * <VirtualHost> + * LogFormat "... custom format ..." + * TransferLog log/virtual_only + * CustomLog log/virtual_useragents "%t %{user-agent}i" + * </VirtualHost> + * + * This will log using CLF to access_log any requests handled by the + * main server, while any requests to the virtual host will be logged + * with the "... custom format..." to virtual_only _AND_ using + * the custom user-agent log to virtual_useragents. + * + * Note that the NCSA referer and user-agent logs are easily added with + * CustomLog: + * CustomLog logs/referer "%{referer}i -> %U" + * CustomLog logs/agent "%{user-agent}i" + * + * Except: no RefererIgnore functionality + * logs '-' if no Referer or User-Agent instead of nothing + * + * But using this method allows much easier modification of the + * log format, e.g. to log hosts along with UA: + * CustomLog logs/referer "%{referer}i %U %h" + * + * The argument to LogFormat and CustomLog is a string, which can include + * literal characters copied into the log files, and '%' directives as + * follows: + * + * %...b: bytes sent, excluding HTTP headers. + * %...{FOOBAR}e: The contents of the environment variable FOOBAR + * %...f: filename + * %...h: remote host + * %...{Foobar}i: The contents of Foobar: header line(s) in the request + * sent to the client. + * %...l: remote logname (from identd, if supplied) + * %...{Foobar}n: The contents of note "Foobar" from another module. + * %...{Foobar}o: The contents of Foobar: header line(s) in the reply. + * %...p: the port the request was served to + * %...P: the process ID of the child that serviced the request. + * %...r: first line of request + * %...s: status. For requests that got internally redirected, this + * is status of the *original* request --- %...>s for the last. + * %...t: time, in common log format time format + * %...{format}t: The time, in the form given by format, which should + * be in strftime(3) format. + * %...T: the time taken to serve the request, in seconds. + * %...u: remote user (from auth; may be bogus if return status (%s) is 401) + * %...U: the URL path requested. + * %...v: the name of the server (i.e. which virtual host?) + * + * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can + * indicate conditions for inclusion of the item (which will cause it + * to be replaced with '-' if the condition is not met). Note that + * there is no escaping performed on the strings from %r, %...i and + * %...o; some with long memories may remember that I thought this was + * a bad idea, once upon a time, and I'm still not comfortable with + * it, but it is difficult to see how to "do the right thing" with all + * of '%..i', unless we URL-escape everything and break with CLF. + * + * The forms of condition are a list of HTTP status codes, which may + * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs + * User-agent: on 400 errors and 501 errors (Bad Request, Not + * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all + * requests which did *not* return some sort of normal status. + * + * The default LogFormat reproduces CLF; see below. + * + * The way this is supposed to work with virtual hosts is as follows: + * a virtual host can have its own LogFormat, or its own TransferLog. + * If it doesn't have its own LogFormat, it inherits from the main + * server. If it doesn't have its own TransferLog, it writes to the + * same descriptor (meaning the same process for "| ..."). + * + * --- rst */ + +#define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %>s %b" + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" /* For REMOTE_NAME */ + +module config_log_module; + +static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); +#ifdef __EMX__ +/* OS/2 dosen't support users and groups */ +static mode_t xfer_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +/* + * multi_log_state is our per-(virtual)-server configuration. We store + * an array of the logs we are going to use, each of type config_log_state. + * If a default log format is given by LogFormat, store in default_format + * (backward compat. with mod_log_config). We also store a pointer to + * the logs specified for the main server for virtual servers, so that + * if this vhost has now logs defined, we can use the main server's + * logs instead. + * + * So, for the main server, config_logs contains a list of the log files + * and server_config_logs in empty. For a vhost, server_config_logs + * points to the same array as config_logs in the main server, and + * config_logs points to the array of logs defined inside this vhost, + * which might be empty. + */ + +typedef struct { + array_header *default_format; + array_header *config_logs; + array_header *server_config_logs; +} multi_log_state; + +/* + * config_log_state holds the status of a single log file. fname cannot + * be NULL. format might be NULL, in which case the default_format from + * the multi_log_state should be used, or if that is NULL as well, use + * the CLF. log_fd is -1 before the log file is opened and set to a valid + * fd after it is opened. + */ + +typedef struct { + char *fname; + array_header *format; + int log_fd; +} config_log_state; + +/* + * Format items... + */ + +typedef char *(*item_key_func)(request_rec *, char *); + +typedef struct { + item_key_func func; + char *arg; + int condition_sense; + int want_orig; + array_header *conditions; +} log_format_item; + +char *format_integer(pool *p, int i) +{ + char dummy[40]; + ap_snprintf (dummy, sizeof(dummy), "%d", i); + return pstrdup (p, dummy); +} + +static char *pfmt(pool *p, int i) +{ + if (i <= 0) return "-"; + else return format_integer (p, i); +} + +char *constant_item (request_rec *dummy, char *stuff) { return stuff; } + +char *log_remote_host (request_rec *r, char *a) +{ return (char *)get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME); } + +char *log_remote_logname(request_rec *r, char *a) +{return (char *)get_remote_logname(r);} + +char *log_remote_user (request_rec *r, char *a) { + char *rvalue = r->connection->user; + + if (rvalue == NULL) { + rvalue = "-"; + } else if (strlen (rvalue) == 0) { + rvalue = "\"\""; + } + return rvalue; +} + +char *log_request_line (request_rec *r, char *a) +{ return r->the_request; } + +char *log_request_file (request_rec *r, char *a) +{ return r->filename; } +char *log_request_uri (request_rec *r, char *a) +{ return r->uri; } +char *log_status (request_rec *r, char *a) +{ return pfmt(r->pool, r->status); } + +char *log_bytes_sent (request_rec *r, char *a) +{ + if (!r->sent_bodyct) return "-"; + else + { + long int bs; + char dummy[40]; + bgetopt(r->connection->client, BO_BYTECT, &bs); + ap_snprintf(dummy, sizeof(dummy), "%ld", bs); + return pstrdup(r->pool, dummy); + } +} + +char *log_header_in (request_rec *r, char *a) +{ return table_get (r->headers_in, a); } + +char *log_header_out (request_rec *r, char *a) +{ + char *cp = table_get (r->headers_out, a); + if (!strcasecmp(a, "Content-type") && r->content_type) + cp = r->content_type; + if (cp) return cp; + return table_get (r->err_headers_out, a); +} + +char *log_note (request_rec *r, char *a) +{ return table_get (r->notes, a); } +char *log_env_var (request_rec *r, char *a) +{ return table_get (r->subprocess_env, a); } + +char *log_request_time (request_rec *r, char *a) +{ + int timz; + struct tm *t; + char tstr[MAX_STRING_LEN]; + + t = get_gmtoff(&timz); + + if (a && *a) /* Custom format */ + strftime(tstr, MAX_STRING_LEN, a, t); + else { /* CLF format */ + char sign = (timz < 0 ? '-' : '+'); + + if(timz < 0) timz = -timz; + + strftime(tstr,MAX_STRING_LEN,"[%d/%b/%Y:%H:%M:%S ",t); + ap_snprintf (tstr + strlen(tstr), sizeof(tstr)-strlen(tstr), + "%c%.2d%.2d]", sign, timz/60, timz%60); + } + + return pstrdup (r->pool, tstr); +} + +char *log_request_duration (request_rec *r, char *a) { + char duration[22]; /* Long enough for 2^64 */ + + ap_snprintf(duration, sizeof(duration), "%ld", time(NULL) - r->request_time); + return pstrdup(r->pool, duration); +} + +char *log_virtual_host (request_rec *r, char *a) { + return pstrdup(r->pool, r->server->server_hostname); +} + +char *log_server_port (request_rec *r, char *a) { + char portnum[22]; + + ap_snprintf(portnum, sizeof(portnum), "%u", r->server->port); + return pstrdup(r->pool, portnum); +} + +char *log_child_pid (request_rec *r, char *a) { + char pidnum[22]; + ap_snprintf(pidnum, sizeof(pidnum), "%ld", (long)getpid()); + return pstrdup(r->pool, pidnum); +} +/***************************************************************** + * + * Parsing the log format string + */ + +struct log_item_list { + char ch; + item_key_func func; + int want_orig_default; +} log_item_keys[] = { + { 'h', log_remote_host, 0 }, + { 'l', log_remote_logname, 0 }, + { 'u', log_remote_user, 0 }, + { 't', log_request_time, 0 }, + { 'T', log_request_duration, 1 }, + { 'r', log_request_line, 1 }, + { 'f', log_request_file, 0 }, + { 'U', log_request_uri, 1 }, + { 's', log_status, 1 }, + { 'b', log_bytes_sent, 0 }, + { 'i', log_header_in, 0 }, + { 'o', log_header_out, 0 }, + { 'n', log_note, 0 }, + { 'e', log_env_var, 0 }, + { 'v', log_virtual_host, 0 }, + { 'p', log_server_port, 0 }, + { 'P', log_child_pid, 0 }, + { '\0' } +}; + +struct log_item_list *find_log_func (char k) +{ + int i; + + for (i = 0; log_item_keys[i].ch; ++i) + if (k == log_item_keys[i].ch) + return &log_item_keys[i]; + + return NULL; +} + +char *log_format_substring (pool *p, const char *start, const char *end) +{ + char *res = palloc (p, end - start + 1); + strncpy (res, start, end - start); + res[end - start] = '\0'; + return res; +} + +char *parse_log_misc_string (pool *p, log_format_item *it, const char **sa) +{ + const char *s = *sa; + + it->func = constant_item; + it->conditions = NULL; + + while (*s && *s != '%') ++s; + it->arg = log_format_substring (p, *sa, s); + *sa = s; + + return NULL; +} + +char *parse_log_item (pool *p, log_format_item *it, const char **sa) +{ + const char *s = *sa; + if (*s != '%') return parse_log_misc_string (p, it, sa); + + ++s; + it->condition_sense = 0; + it->conditions = NULL; + it->want_orig = -1; + it->arg = ""; /* For safety's sake... */ + + while (*s) { + int i; + struct log_item_list *l; + + switch (*s) { + case '!': + ++s; + it->condition_sense = !it->condition_sense; + break; + + case '<': + ++s; + it->want_orig = 1; + break; + + case '>': + ++s; + it->want_orig = 0; + break; + + case ',': + ++s; + break; + + case '{': + ++s; + it->arg = getword (p, &s, '}'); + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + i = *s - '0'; + while (isdigit (*++s)) i = i * 10 + (*s) - '0'; + if (!it->conditions) + it->conditions = make_array (p, 4, sizeof(int)); + *(int *)push_array(it->conditions) = i; + break; + + default: + l = find_log_func (*s++); + if (!l) { + char dummy[] = { '\0', '\0'}; + dummy[0] = s[-1]; + return pstrcat (p, "Unrecognized LogFormat directive %", + dummy, NULL); + } + it->func = l->func; + if (it->want_orig == -1) it->want_orig = l->want_orig_default; + *sa = s; + return NULL; + } + } + + return "Ran off end of LogFormat parsing args to some directive"; +} + +array_header *parse_log_string (pool *p, const char *s, const char **err) +{ + array_header *a = make_array (p, 30, sizeof (log_format_item)); + char *res; + + while (*s) { + if ((res = parse_log_item (p, (log_format_item *)push_array(a), &s))) { + *err = res; + return NULL; + } + } + + s = "\n"; + parse_log_item (p, (log_format_item *)push_array(a), &s); + return a; +} + +/***************************************************************** + * + * Actually logging. + */ + +char *process_item(request_rec *r, request_rec *orig, log_format_item *item) +{ + char *cp; + + /* First, see if we need to process this thing at all... */ + + if (item->conditions && item->conditions->nelts != 0) { + int i; + int *conds = (int *)item->conditions->elts; + int in_list = 0; + + for (i = 0; i < item->conditions->nelts; ++i) + if (r->status == conds[i]) { + in_list = 1; + break; + } + + if ((item->condition_sense && in_list) + || (!item->condition_sense && !in_list)) + { + return "-"; + } + } + + /* We do. Do it... */ + + cp = (*item->func)(item->want_orig ? orig : r, item->arg); + return cp ? cp : "-"; +} + +int config_log_transaction(request_rec *r, config_log_state *cls, + array_header *default_format) { + array_header *strsa; + log_format_item *items; + char *str, **strs, *s; + request_rec *orig; + int i; + int len = 0; + array_header *format; + + format = cls->format ? cls->format : default_format; + + strsa= make_array(r->pool, format->nelts,sizeof(char*)); + items = (log_format_item *)format->elts; + + orig = r; + while (orig->prev) orig = orig->prev; + while (r->next) r = r->next; + + for (i = 0; i < format->nelts; ++i) + *((char**)push_array (strsa)) = process_item (r, orig, &items[i]); + + strs = (char **)strsa->elts; + + for (i = 0; i < format->nelts; ++i) + len += strlen (strs[i]); + + str = palloc (r->pool, len + 1); + + for (i = 0, s = str; i < format->nelts; ++i) { + strcpy (s, strs[i]); + s += strlen (strs[i]); + } + + write(cls->log_fd, str, strlen(str)); + + return OK; +} + +int multi_log_transaction(request_rec *r) +{ + multi_log_state *mls = get_module_config (r->server->module_config, + &config_log_module); + config_log_state *clsarray; + int i; + + if (mls->config_logs->nelts) { + clsarray = (config_log_state *)mls->config_logs->elts; + for (i = 0; i < mls->config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + config_log_transaction(r, cls, mls->default_format); + } + } + else if (mls->server_config_logs) { + clsarray = (config_log_state *)mls->server_config_logs->elts; + for (i = 0; i < mls->server_config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + config_log_transaction(r, cls, mls->default_format); + } + } + + return OK; +} + +/***************************************************************** + * + * Module glue... + */ + +void *make_config_log_state (pool *p, server_rec *s) +{ + multi_log_state *mls = + (multi_log_state *)palloc(p, sizeof (multi_log_state)); + + mls->config_logs = + make_array(p, 5, sizeof (config_log_state)); + mls->default_format = NULL; + mls->server_config_logs = NULL; + + return mls; +} + +/* + * Use the merger to simply add a pointer from the vhost log state + * to the log of logs specified for the non-vhost configuration + */ + +void *merge_config_log_state (pool *p, void *basev, void *addv) +{ + multi_log_state *base = (multi_log_state *)basev; + multi_log_state *add = (multi_log_state *)addv; + + add->server_config_logs = base->config_logs; + if (!add->default_format) + add->default_format = base->default_format; + + return add; +} + +const char *log_format (cmd_parms *cmd, void *dummy, char *arg) +{ + const char *err_string = NULL; + multi_log_state *mls = get_module_config (cmd->server->module_config, + &config_log_module); + + mls->default_format = parse_log_string (cmd->pool, arg, &err_string); + return err_string; +} + +const char *add_custom_log(cmd_parms *cmd, void *dummy, char *fn, char *fmt) +{ + const char *err_string = NULL; + multi_log_state *mls = get_module_config (cmd->server->module_config, + &config_log_module); + config_log_state *cls; + + cls = (config_log_state*)push_array(mls->config_logs); + cls->fname = fn; + if (!fmt) + cls->format = NULL; + else + cls->format = parse_log_string (cmd->pool, fmt, &err_string); + cls->log_fd = -1; + + return err_string; +} + +const char *set_transfer_log(cmd_parms *cmd, void *dummy, char *fn) +{ + return add_custom_log(cmd, dummy, fn, NULL); +} + +const char *set_cookie_log(cmd_parms *cmd, void *dummy, char *fn) +{ + return add_custom_log(cmd, dummy, fn, "%{Cookie}n \"%r\" %t"); +} + +command_rec config_log_cmds[] = { +{ "CustomLog", add_custom_log, NULL, RSRC_CONF, TAKE2, + "a file name and a custom log format string" }, +{ "TransferLog", set_transfer_log, NULL, RSRC_CONF, TAKE1, + "the filename of the access log" }, +{ "LogFormat", log_format, NULL, RSRC_CONF, TAKE1, + "a log format string (see docs)" }, +{ "CookieLog", set_cookie_log, NULL, RSRC_CONF, TAKE1, + "the filename of the cookie log" }, +{ NULL } +}; + +void config_log_child (void *cmd) +{ + /* 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... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); +#ifdef __EMX__ + /* For OS/2 we need to use a '/' */ + execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + perror ("exec"); + fprintf (stderr, "Exec of shell for logging failed!!!\n"); + exit (1); +} + +config_log_state *open_config_log (server_rec *s, pool *p, + config_log_state *cls, + array_header *default_format) { + if (cls->log_fd > 0) return cls; /* virtual config shared w/main server */ + + if (*cls->fname == '|') { + FILE *dummy; + + if (!spawn_child (p, config_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL)) { + perror ("spawn_child"); + fprintf (stderr, "Couldn't fork child for TransferLog process\n"); + exit (1); + } + + cls->log_fd = fileno (dummy); + } + else { + char *fname = server_root_relative (p, cls->fname); + if((cls->log_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + perror("open"); + fprintf (stderr, + "httpd: could not open transfer log file %s.\n", fname); + exit(1); + } + } + + return cls; +} + +config_log_state *open_multi_logs (server_rec *s, pool *p) +{ + int i; + multi_log_state *mls = get_module_config(s->module_config, + &config_log_module); + config_log_state *clsarray; + const char *dummy; + + if (!mls->default_format) + mls->default_format = parse_log_string (p, DEFAULT_LOG_FORMAT, &dummy); + + if (mls->config_logs->nelts) { + clsarray = (config_log_state *)mls->config_logs->elts; + for (i = 0; i < mls->config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + cls = open_config_log(s, p, cls, mls->default_format); + } + } + else if (mls->server_config_logs) { + clsarray = (config_log_state *)mls->server_config_logs->elts; + for (i = 0; i < mls->server_config_logs->nelts; ++i) { + config_log_state *cls = &clsarray[i]; + + cls = open_config_log(s, p, cls, mls->default_format); + } + } + + return NULL; +} + +void init_config_log (server_rec *s, pool *p) +{ + /* First, do "physical" server, which gets default log fd and format + * for the virtual servers, if they don't override... + */ + + open_multi_logs (s, p); + + /* Then, virtual servers */ + + for (s = s->next; s; s = s->next) open_multi_logs (s, p); +} + +module config_log_module = { + STANDARD_MODULE_STUFF, + init_config_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_config_log_state, /* server config */ + merge_config_log_state, /* merge server config */ + config_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + multi_log_transaction, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_log_referer.c b/usr.sbin/httpd/src/mod_log_referer.c new file mode 100644 index 00000000000..e494298944c --- /dev/null +++ b/usr.sbin/httpd/src/mod_log_referer.c @@ -0,0 +1,235 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + + +#include "httpd.h" +#include "http_config.h" + +module referer_log_module; + +static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); + +#ifdef __EMX__ +/* OS/2 lacks support for users and groups */ +static mode_t xfer_mode = ( S_IREAD | S_IWRITE ); +#else +static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); +#endif + +typedef struct { + char *fname; + int referer_fd; + array_header *referer_ignore_list; +} referer_log_state; + +void *make_referer_log_state (pool *p, server_rec *s) +{ + referer_log_state *cls = + (referer_log_state *)palloc (p, sizeof (referer_log_state)); + + cls->fname = ""; + cls->referer_fd = -1; + cls->referer_ignore_list = make_array(p, 1, sizeof(char *)); + return (void *)cls; +} + +const char *set_referer_log (cmd_parms *parms, void *dummy, char *arg) +{ + referer_log_state *cls = get_module_config (parms->server->module_config, + &referer_log_module); + + cls->fname = arg; + return NULL; +} + +const char *add_referer_ignore (cmd_parms *parms, void *dummy, char *arg) +{ + char **addme; + referer_log_state *cls = get_module_config (parms->server->module_config, + &referer_log_module); + + addme = push_array(cls->referer_ignore_list); + *addme = pstrdup(cls->referer_ignore_list->pool, arg); + return NULL; +} + +command_rec referer_log_cmds[] = { +{ "RefererLog", set_referer_log, NULL, RSRC_CONF, TAKE1, + "the filename of the referer log" }, +{ "RefererIgnore", add_referer_ignore, NULL, RSRC_CONF, ITERATE, + "referer hostnames to ignore" }, +{ NULL } +}; + +void referer_log_child (void *cmd) +{ + /* Child process code for 'RefererLog "|..."'; + * may want a common framework for this, since I expect it will + * be common for other foo-loggers to want this sort of thing... + */ + + cleanup_for_exec(); + signal (SIGHUP, SIG_IGN); +#ifdef __EMX__ + /* For OS/2 we need to use a '/' */ + execl (SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + perror ("execl"); + fprintf (stderr, "Exec of shell for logging failed!!!\n"); + exit (1); +} + +void open_referer_log (server_rec *s, pool *p) +{ + referer_log_state *cls = get_module_config (s->module_config, + &referer_log_module); + + char *fname = server_root_relative (p, cls->fname); + + if (cls->referer_fd > 0) return; /* virtual log shared w/main server */ + + if (*cls->fname == '|') { + FILE *dummy; + + if (!spawn_child (p, referer_log_child, (void *)(cls->fname+1), + kill_after_timeout, &dummy, NULL)) { + perror ("spawn_child"); + fprintf (stderr, "Couldn't fork child for RefererLog process\n"); + exit (1); + } + + cls->referer_fd = fileno (dummy); + } + else if(*cls->fname != '\0') { + if((cls->referer_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) { + perror("open"); + fprintf(stderr,"httpd: could not open referer log file %s.\n", fname); + exit(1); + } + } +} + +void init_referer_log (server_rec *s, pool *p) +{ + for (; s; s = s->next) open_referer_log (s, p); +} + +int referer_log_transaction(request_rec *orig) +{ + char **ptrptr, **ptrptr2; + referer_log_state *cls = get_module_config (orig->server->module_config, + &referer_log_module); + + char *str; + char *referer; + request_rec *r; + + if(cls->referer_fd <0) + return OK; + + for (r = orig; r->next; r = r->next) + continue; + if (*cls->fname == '\0') /* Don't log referer */ + return DECLINED; + + referer = table_get(orig->headers_in, "Referer"); + if(referer != NULL) + { + + + /* The following is an upsetting mess of pointers, I'm sorry + Anyone with the motiviation and/or the time should feel free + to make this cleaner... */ + + ptrptr2 = (char **) (cls->referer_ignore_list->elts + + (cls->referer_ignore_list->nelts * + cls->referer_ignore_list->elt_size)); + + /* Go through each element of the ignore list and compare it to the + referer_host. If we get a match, return without logging */ + + for(ptrptr = (char **) cls->referer_ignore_list->elts; + ptrptr < ptrptr2; + ptrptr = (char **)((char *)ptrptr + cls->referer_ignore_list->elt_size)) + { + if(strstr(referer, *ptrptr)) + return OK; + } + + + str = pstrcat(orig->pool, referer, " -> ", r->uri, "\n", NULL); + write(cls->referer_fd, str, strlen(str)); + } + + return OK; +} + +module referer_log_module = { + STANDARD_MODULE_STUFF, + init_referer_log, /* initializer */ + NULL, /* create per-dir config */ + NULL, /* merge per-dir config */ + make_referer_log_state, /* server config */ + NULL, /* merge server config */ + referer_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + referer_log_transaction, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_mime.c b/usr.sbin/httpd/src/mod_mime.c new file mode 100644 index 00000000000..dc84975e1ae --- /dev/null +++ b/usr.sbin/httpd/src/mod_mime.c @@ -0,0 +1,324 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * http_mime.c: Sends/gets MIME headers for requests + * + * Rob McCool + * + */ + +#define MIME_PRIVATE + +#include "httpd.h" +#include "http_config.h" + +typedef struct { + table *forced_types; /* Additional AddTyped stuff */ + table *encoding_types; /* Added with AddEncoding... */ + table *language_types; /* Added with AddLanguage... */ + table *handlers; /* Added with AddHandler... */ + + char *type; /* Type forced with ForceType */ + char *handler; /* Handler forced with SetHandler */ +} mime_dir_config; + +module mime_module; + +void *create_mime_dir_config (pool *p, char *dummy) +{ + mime_dir_config *new = + (mime_dir_config *) palloc (p, sizeof(mime_dir_config)); + + new->forced_types = make_table (p, 4); + new->encoding_types = make_table (p, 4); + new->language_types = make_table (p, 4); + new->handlers = make_table (p, 4); + + new->type = NULL; + new->handler = NULL; + + return new; +} + +void *merge_mime_dir_configs (pool *p, void *basev, void *addv) +{ + mime_dir_config *base = (mime_dir_config *)basev; + mime_dir_config *add = (mime_dir_config *)addv; + mime_dir_config *new = + (mime_dir_config *)palloc (p, sizeof(mime_dir_config)); + + new->forced_types = overlay_tables (p, add->forced_types, + base->forced_types); + new->encoding_types = overlay_tables (p, add->encoding_types, + base->encoding_types); + new->language_types = overlay_tables (p, add->language_types, + base->language_types); + new->handlers = overlay_tables (p, add->handlers, + base->handlers); + + new->type = add->type ? add->type : base->type; + new->handler = add->handler ? add->handler : base->handler; + + return new; +} + +const char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->forced_types, ext, ct); + return NULL; +} + +const char *add_encoding(cmd_parms *cmd, mime_dir_config *m, char *enc, + char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->encoding_types, ext, enc); + return NULL; +} + +const char *add_language(cmd_parms *cmd, mime_dir_config *m, char *lang, + char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->language_types, ext, lang); + return NULL; +} + +const char *add_handler(cmd_parms *cmd, mime_dir_config *m, char *hdlr, + char *ext) +{ + if (*ext == '.') ++ext; + table_set (m->handlers, ext, hdlr); + return NULL; +} + +/* The sole bit of server configuration that the MIME module has is + * the name of its config file, so... + */ + +const char *set_types_config (cmd_parms *cmd, void *dummy, char *arg) +{ + set_module_config (cmd->server->module_config, &mime_module, + pstrdup (cmd->pool, arg)); + return NULL; +} + +command_rec mime_cmds[] = { +{ "AddType", add_type, NULL, OR_FILEINFO, ITERATE2, + "a mime type followed by one or more file extensions" }, +{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, ITERATE2, + "an encoding (e.g., gzip), followed by one or more file extensions" }, +{ "AddLanguage", add_language, NULL, OR_FILEINFO, ITERATE2, + "a language (e.g., fr), followed by one or more file extensions" }, +{ "AddHandler", add_handler, NULL, OR_FILEINFO, ITERATE2, + "a handler name followed by one or more file extensions" }, +{ "ForceType", set_string_slot, (void*)XtOffsetOf(mime_dir_config, type), + OR_FILEINFO, TAKE1, "a media type" }, +{ "SetHandler", set_string_slot, (void*)XtOffsetOf(mime_dir_config, handler), + OR_FILEINFO, TAKE1, "a handler name" }, +{ "TypesConfig", set_types_config, NULL, RSRC_CONF, TAKE1, + "the MIME types config file" }, +{ NULL } +}; + +/* Hash table --- only one of these per daemon; virtual hosts can + * get private versions through AddType... + */ + +#define MIME_HASHSIZE 27 +#define hash(i) (isalpha(i) ? (tolower(i)) - 'a' : 26) + +static table *hash_buckets[MIME_HASHSIZE]; + +void init_mime (server_rec *s, pool *p) +{ + FILE *f; + char l[MAX_STRING_LEN]; + int x; + char *types_confname = get_module_config (s->module_config, &mime_module); + + if (!types_confname) types_confname = TYPES_CONFIG_FILE; + + types_confname = server_root_relative (p, types_confname); + + if(!(f = fopen(types_confname,"r"))) { + perror("fopen"); + fprintf(stderr,"httpd: could not open mime types file %s\n", + types_confname); + exit(1); + } + + for(x=0;x<27;x++) + hash_buckets[x] = make_table (p, 10); + + while(!(cfg_getline(l,MAX_STRING_LEN,f))) { + const char *ll = l, *ct; + + if(l[0] == '#') continue; + ct = getword_conf (p, &ll); + + while(ll[0]) { + char *ext = getword_conf (p, &ll); + str_tolower (ext); /* ??? */ + table_set (hash_buckets[hash(ext[0])], ext, ct); + } + } + fclose(f); +} + +int find_ct(request_rec *r) +{ + const char *fn = strrchr(r->filename, '/'); + mime_dir_config *conf = + (mime_dir_config *)get_module_config(r->per_dir_config, &mime_module); + char *ext, *type, *orighandler = r->handler; + + if (S_ISDIR(r->finfo.st_mode)) { + r->content_type = DIR_MAGIC_TYPE; + return OK; + } + + /* TM -- FIXME + * + * if r->filename does not contain a '/', the following passes a null + * pointer to getword, causing a SEGV .. + */ + + if(fn == NULL) fn = r->filename; + + /* Parse filename extensions, which can be in any order */ + while ((ext = getword(r->pool, &fn, '.')) && *ext) { + int found = 0; + + /* Check for Content-Type */ + if ((type = table_get (conf->forced_types, ext)) + || (type = table_get (hash_buckets[hash(*ext)], ext))) { + r->content_type = type; + found = 1; + } + + /* Check for Content-Language */ + if ((type = table_get (conf->language_types, ext))) { + char **new; + + r->content_language = type; /* back compat. only */ + if (!r->content_languages) + r->content_languages = make_array (r->pool, 2, sizeof(char*)); + new = (char **)push_array (r->content_languages); + *new = type; + found = 1; + } + + /* Check for Content-Encoding */ + if ((type = table_get (conf->encoding_types, ext))) { + if (!r->content_encoding) + r->content_encoding = type; + else + r->content_encoding = pstrcat(r->pool, r->content_encoding, + ", ", type, NULL); + found = 1; + } + + /* Check for a special handler, but not for proxy request */ + if ((type = table_get (conf->handlers, ext)) && !r->proxyreq) { + r->handler = type; + found = 1; + } + + /* This is to deal with cases such as foo.gif.bak, which we want + * to not have a type. So if we find an unknown extension, we + * zap the type/language/encoding and reset the handler + */ + + if (!found) { + r->content_type = NULL; + r->content_language = NULL; + r->content_languages = NULL; + r->content_encoding = NULL; + r->handler = orighandler; + } + + } + + /* Check for overrides with ForceType/SetHandler */ + + if (conf->type && strcmp(conf->type, "none")) + r->content_type = pstrdup(r->pool, conf->type); + if (conf->handler && strcmp(conf->handler, "none")) + r->handler = pstrdup(r->pool, conf->handler); + + if (!r->content_type) return DECLINED; + + return OK; +} + + +module mime_module = { + STANDARD_MODULE_STUFF, + init_mime, /* initializer */ + create_mime_dir_config, + merge_mime_dir_configs, + NULL, /* server config */ + NULL, /* merge server config */ + mime_cmds, + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + find_ct, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_negotiation.c b/usr.sbin/httpd/src/mod_negotiation.c new file mode 100644 index 00000000000..84e15f0ec4e --- /dev/null +++ b/usr.sbin/httpd/src/mod_negotiation.c @@ -0,0 +1,2052 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * mod_negotiation.c: keeps track of MIME types the client is willing to + * accept, and contains code to handle type arbitration. + * + * rst + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_log.h" +#include "util_script.h" + +/* define TCN_02 to allow for Holtman I-D transparent negotiation. + * This file currently implements the draft-02, except for + * anything to do with features and cache-control (max-age etc) + * + * Since the draft is just that, and we don't yet implement + * everything, regard the transparent negotiation stuff as experimental. + */ +/*#define TCN_02*/ + +/* Commands --- configuring document caching on a per (virtual?) + * server basis... + */ + +typedef struct { + array_header *language_priority; +} neg_dir_config; + +module negotiation_module; + +char *merge_string_array (pool *p, array_header *arr, char *sep) +{ + int i; + char *t = ""; + + for (i = 0; i < arr->nelts; i++) { + t = pstrcat(p, t, i ? sep : "", ((char**)arr->elts)[i], NULL); + } + return t; +} + +void *create_neg_dir_config (pool *p, char *dummy) +{ + neg_dir_config *new = + (neg_dir_config *) palloc (p, sizeof (neg_dir_config)); + + new->language_priority = make_array (p, 4, sizeof (char *)); + return new; +} + +void *merge_neg_dir_configs (pool *p, void *basev, void *addv) +{ + neg_dir_config *base = (neg_dir_config *)basev; + neg_dir_config *add = (neg_dir_config *)addv; + neg_dir_config *new = + (neg_dir_config *) palloc (p, sizeof (neg_dir_config)); + + /* give priority to the config in the subdirectory */ + new->language_priority = append_arrays (p, add->language_priority, + base->language_priority); + return new; +} + +const char *set_language_priority (cmd_parms *cmd, void *n, char *lang) +{ + array_header *arr = ((neg_dir_config *) n)->language_priority; + char **langp = (char **) push_array (arr); + + *langp = pstrdup (arr->pool, lang); + return NULL; +} + +const char *cache_negotiated_docs (cmd_parms *cmd, void *dummy, char *dummy2) +{ + void *server_conf = cmd->server->module_config; + + set_module_config (server_conf, &negotiation_module, "Cache"); + return NULL; +} + +int do_cache_negotiated_docs (server_rec *s) +{ + return (get_module_config (s->module_config, &negotiation_module) != NULL); +} + +command_rec negotiation_cmds[] = { +{ "CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF, RAW_ARGS, + NULL }, +{ "LanguagePriority", set_language_priority, NULL, OR_FILEINFO, ITERATE, + NULL }, +{ NULL } +}; + +/* Record of available info on a media type specified by the client + * (we also use 'em for encodings and languages) + */ + +typedef struct accept_rec { + char *type_name; + float quality; + float max_bytes; + float level; + char *charset; /* for content-type only */ +} accept_rec; + +/* Record of available info on a particular variant + * + * Note that a few of these fields are updated by the actual negotiation + * code. These are: + * + * level_matched --- initialized to zero. Set to the value of level + * if the client actually accepts this media type at that + * level (and *not* if it got in on a wildcard). See level_cmp + * below. + */ + +typedef struct var_rec { + request_rec *sub_req; /* May be NULL (is, for map files) */ + char *type_name; + char *file_name; + char *content_encoding; + array_header *content_languages; /* list of languages for this variant */ + char *content_charset; + char *description; + + /* The next five items give the quality values for the dimensions + * of negotiation for this variant. They are obtained from the + * appropriate header lines, except for accept_type_quality, which + * is obtained from the variant itself (the 'qs' parameter value + * from the variant's mime-type). Apart from type_quality, + * these values are set when we find the quality for each variant + * (see best_match()). type_quality is set from the 'qs' parameter + * of the variant description or mime type: see set_mime_fields(). + */ + float lang_quality; /* quality of this variant's language */ + int encoding_quality; /* ditto encoding (1 or 0 only) */ + float charset_quality; /* ditto charset */ + float accept_type_quality; /* ditto media type */ + float type_quality; /* quality of source for this type */ + + /* Now some special values */ + float level; /* Auxiliary to content-type... */ + float bytes; /* content length, if known */ + int lang_index; /* pre HTTP/1.1 language priority stuff */ + int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */ + + /* Above are all written-once properties of the variant. The + * three fields below are changed during negotiation: + */ + + float level_matched; + int mime_stars; + int definite; +} var_rec; + +/* Something to carry around the state of negotiation (and to keep + * all of this thread-safe)... + */ + +typedef struct { + pool *pool; + request_rec *r; + char *dir_name; + int accept_q; /* 1 if an Accept item has a q= param */ + float default_lang_quality; /* fiddle lang q for variants with no lang */ + + + array_header *accepts; /* accept_recs */ + int have_accept_header; /* 1 if Accept-Header present */ + array_header *accept_encodings; /* accept_recs */ + array_header *accept_charsets; /* accept_recs */ + array_header *accept_langs; /* accept_recs */ + array_header *avail_vars; /* available variants */ + + int ua_can_negotiate; /* 1 if ua can do transparent negotiate */ + int use_transparent_neg; /* 1 if we are using transparent neg */ + int short_accept_headers; /* 1 if ua does trans neg & sent short accpt */ +} negotiation_state; + +/* A few functions to manipulate var_recs. + * Cleaning out the fields... + */ + +void clean_var_rec (var_rec *mime_info) +{ + mime_info->sub_req = NULL; + mime_info->type_name = ""; + mime_info->file_name = ""; + mime_info->content_encoding = ""; + mime_info->content_languages = NULL; + mime_info->content_charset = ""; + mime_info->description = ""; + + mime_info->is_pseudo_html = 0; + mime_info->level = 0.0; + mime_info->level_matched = 0.0; + mime_info->bytes = 0; + mime_info->lang_index = -1; + mime_info->mime_stars = 0; + mime_info->definite = 1; + + mime_info->charset_quality = 1.0; + mime_info->type_quality = 0.0; + mime_info->encoding_quality = 1; + mime_info->lang_quality = 1.0; + mime_info->accept_type_quality = 1.0; +} + +/* Initializing the relevant fields of a variant record from the + * accept_info read out of its content-type, one way or another. + */ + +void set_mime_fields (var_rec *var, accept_rec *mime_info) +{ + var->type_name = mime_info->type_name; + var->type_quality = mime_info->quality; + var->level = mime_info->level; + var->content_charset = mime_info->charset; + + var->is_pseudo_html = + (!strcmp (var->type_name, "text/html") + || !strcmp (var->type_name, INCLUDES_MAGIC_TYPE) + || !strcmp (var->type_name, INCLUDES_MAGIC_TYPE3)); +} + +/***************************************************************** + * + * Parsing (lists of) media types and their parameters, as seen in + * HTTPD header lines and elsewhere. + */ + +/* + * Get a single mime type entry --- one media type and parameters; + * enter the values we recognize into the argument accept_rec + */ + +char *get_entry (pool *p, accept_rec *result, char *accept_line) +{ + result->quality = 1.0; + result->max_bytes = 0.0; + result->level = 0.0; + result->charset = ""; + + /* Note that this handles what I gather is the "old format", + * + * Accept: text/html text/plain moo/zot + * + * without any compatibility kludges --- if the token after the + * MIME type begins with a semicolon, we know we're looking at parms, + * otherwise, we know we aren't. (So why all the pissing and moaning + * in the CERN server code? I must be missing something). + */ + + result->type_name = get_token (p, &accept_line, 0); + str_tolower (result->type_name); /* You want case-insensitive, + * you'll *get* case-insensitive. + */ + + + /* KLUDGE!!! Default HTML to level 2.0 unless the browser + * *explicitly* says something else. + */ + + if (!strcmp (result->type_name, "text/html") + && result->level == 0.0) + result->level = 2.0; + else if (!strcmp (result->type_name, INCLUDES_MAGIC_TYPE)) + result->level = 2.0; + else if (!strcmp (result->type_name, INCLUDES_MAGIC_TYPE3)) + result->level = 3.0; + + while (*accept_line == ';') { + /* Parameters ... */ + + char *parm; + char *cp; + char *end; + + ++accept_line; + parm = get_token (p, &accept_line, 1); + + /* Look for 'var = value' --- and make sure the var is in lcase. */ + + for (cp = parm; *cp && !isspace(*cp) && *cp != '='; ++cp) + *cp = tolower(*cp); + + if (!*cp) continue; /* No '='; just ignore it. */ + + *cp++ = '\0'; /* Delimit var */ + while (*cp && (isspace(*cp) || *cp == '=')) + ++cp; + + if (*cp == '"') { + ++cp; + for (end = cp; *end && + *end != '\n' && *end != '\r' && *end != '\"'; + end++) + ; + } + else { + for (end = cp; *end && !isspace(*end); end++) + ; + } + if (*end) + *end = '\0'; /* strip ending quote or return */ + str_tolower(cp); + + if (parm[0] == 'q' + && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) + result->quality = atof(cp); + else if (parm[0] == 'm' && parm[1] == 'x' && + parm[2] == 'b' && parm[3] == '\0') + result->max_bytes = atof(cp); + else if (parm[0] == 'l' && !strcmp (&parm[1], "evel")) + result->level = atof(cp); + else if (!strcmp(parm, "charset")) + result->charset = cp; + } + + if (*accept_line == ',') ++accept_line; + + return accept_line; +} + +/***************************************************************** + * + * Dealing with header lines ... + * + * Accept, Accept-Charset, Accept-Language and Accept-Encoding + * are handled by do_header_line() - they all have the same + * basic structure of a list of items of the format + * name; q=N; charset=TEXT + * + * where q is only valid in Accept, Accept-Charset and Accept-Languages, + * and charset is only valid in Accept. + */ + +array_header *do_header_line (pool *p, char *accept_line) +{ + array_header *accept_recs = make_array (p, 40, sizeof (accept_rec)); + + if (!accept_line) return accept_recs; + + while (*accept_line) { + accept_rec *new = (accept_rec *)push_array (accept_recs); + accept_line = get_entry (p, new, accept_line); + } + + return accept_recs; +} + +/* Given the text of the Content-Languages: line from the var map file, + * return an array containing the languages of this variant + */ + +array_header *do_languages_line (pool *p, char **lang_line) +{ + array_header *lang_recs = make_array (p, 2, sizeof (char *)); + + if (!lang_line) return lang_recs; + + while (**lang_line) { + char **new = (char **)push_array (lang_recs); + *new = get_token (p, lang_line, 0); + str_tolower (*new); + if (**lang_line == ',' || **lang_line == ';') + ++(*lang_line); + } + + return lang_recs; +} + +/***************************************************************** + * + * Handling header lines from clients... + */ + +negotiation_state *parse_accept_headers (request_rec *r) +{ + negotiation_state *new = + (negotiation_state *)pcalloc (r->pool, sizeof (negotiation_state)); + accept_rec *elts; + table *hdrs = r->headers_in; + int i; + char *hdr; + + new->pool = r->pool; + new->r = r; + new->dir_name = make_dirstr(r->pool, r->filename, count_dirs(r->filename)); + + new->accepts = do_header_line (r->pool, table_get (hdrs, "Accept")); + + hdr = table_get (hdrs, "Accept-encoding"); + if (hdr) + new->have_accept_header = 1; + new->accept_encodings = do_header_line (r->pool, hdr); + + new->accept_langs = + do_header_line (r->pool, table_get (hdrs, "Accept-language")); + new->accept_charsets = + do_header_line (r->pool, table_get (hdrs, "Accept-charset")); + new->avail_vars = make_array (r->pool, 40, sizeof (var_rec)); + +#ifdef TCN_02 + if (table_get(r->headers_in, "Negotiate")) { + /* Negotiate: header tells us UA does transparent negotiation + * We have to decide whether we want to ... for now, yes, + * we do */ + + new->ua_can_negotiate = 1; + if (r->method_number == M_GET) + new->use_transparent_neg = 1; /* should be configurable */ + + /* Check for 'Short Accept', ie either no Accept: header, + * or just "Accept: * / *" */ + if (new->accepts->nelts == 0 || + (new->accepts->nelts == 1 && + (!strcmp(((accept_rec *)new->accepts->elts)[0].type_name, + "*/*")))) { + /* Using short accept header */ + new->short_accept_headers = 1; + } + } +#endif + + if (!new->use_transparent_neg) { + /* Now we check for q-values. If they're all 1.0, we assume the + * client is "broken", and we are allowed to fiddle with the + * values later. Otherwise, we leave them alone. + */ + + elts = (accept_rec *)new->accepts->elts; + + for (i = 0; i < new->accepts->nelts; ++i) + if (elts[i].quality < 1.0) new->accept_q = 1; + } + else new->accept_q = 1; + + return new; +} + +/* Sometimes clients will give us no Accept info at all; this routine sets + * up the standard default for that case, and also arranges for us to be + * willing to run a CGI script if we find one. (In fact, we set up to + * dramatically prefer CGI scripts in cases where that's appropriate, + * e.g., POST). + */ + +void maybe_add_default_encodings(negotiation_state *neg, int prefer_scripts) +{ + accept_rec *new_accept = (accept_rec *)push_array (neg->accepts); + + new_accept->type_name = CGI_MAGIC_TYPE; + new_accept->quality = prefer_scripts ? 1e-20 : 1e20; + new_accept->level = 0.0; + new_accept->max_bytes = 0.0; + + if (neg->accepts->nelts > 1) return; + + new_accept = (accept_rec *)push_array (neg->accepts); + + new_accept->type_name = "*/*"; + new_accept->quality = 1.0; + new_accept->level = 0.0; + new_accept->max_bytes = 0.0; +} + +/***************************************************************** + * + * Parsing type-map files, in Roy's meta/http format augmented with + * #-comments. + */ + +/* Reading RFC822-style header lines, ignoring #-comments and + * handling continuations. + */ + +enum header_state { header_eof, header_seen, header_sep }; + +enum header_state get_header_line (char *buffer, int len, FILE *map) +{ + char *buf_end = buffer + len; + char *cp; + int c; + + /* Get a noncommented line */ + + do { + if (fgets(buffer, MAX_STRING_LEN, map) == NULL) + return header_eof; + } while (buffer[0] == '#'); + + /* If blank, just return it --- this ends information on this variant */ + + for (cp = buffer; *cp && isspace (*cp); ++cp) + continue; + + if (*cp == '\0') return header_sep; + + /* If non-blank, go looking for header lines, but note that we still + * have to treat comments specially... + */ + + cp += strlen(cp); + + while ((c = getc(map)) != EOF) + { + if (c == '#') { + /* Comment line */ + while ((c = getc(map)) != EOF && c != '\n') + continue; + } else if (isspace(c)) { + /* Leading whitespace. POSSIBLE continuation line + * Also, possibly blank --- if so, we ungetc() the final newline + * so that we will pick up the blank line the next time 'round. + */ + + while (c != EOF && c != '\n' && isspace(c)) + c = getc(map); + + ungetc (c, map); + + if (c == '\n') return header_seen; /* Blank line */ + + /* Continuation */ + + while (cp < buf_end - 2 && (c = getc(map)) != EOF && c != '\n') + *cp++ = c; + + *cp++ = '\n'; + *cp = '\0'; + } else { + + /* Line beginning with something other than whitespace */ + + ungetc (c, map); + return header_seen; + } + } + + return header_seen; +} + +/* Stripping out RFC822 comments */ + +void strip_paren_comments (char *hdr) +{ + /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */ + + while (*hdr) { + if (*hdr == '"') { + while (*++hdr && *hdr != '"') + continue; + ++hdr; + } + else if (*hdr == '(') { + while (*hdr && *hdr != ')') *hdr++ = ' '; + + if (*hdr) *hdr++ = ' '; + } + else ++hdr; + } +} + +/* Getting to a header body from the header */ + +char *lcase_header_name_return_body (char *header, request_rec *r) +{ + char *cp = header; + + for ( ; *cp && *cp != ':' ; ++cp) { + *cp = tolower(*cp); + } + + if (!*cp) { + log_reason ("Syntax error in type map --- no ':'", r->filename, r); + return NULL; + } + + do ++cp; while (*cp && isspace (*cp)); + + if (!*cp) { + log_reason ("Syntax error in type map --- no header body", + r->filename, r); + return NULL; + } + + return cp; +} + +static int read_type_map (negotiation_state *neg, request_rec *rr) +{ + request_rec *r = neg->r; + FILE *map; + char buffer[MAX_STRING_LEN]; + enum header_state hstate; + struct var_rec mime_info; + + if (rr->status != HTTP_OK) { + return rr->status; + } + map = pfopen (neg->pool, rr->filename, "r"); + if (map == NULL) { + log_reason("cannot access type map file", rr->filename, r); + return FORBIDDEN; + } + + clean_var_rec (&mime_info); + + do { + hstate = get_header_line (buffer, MAX_STRING_LEN, map); + + if (hstate == header_seen) { + char *body = lcase_header_name_return_body (buffer, neg->r); + + if (body == NULL) return SERVER_ERROR; + + strip_paren_comments (body); + + if (!strncmp (buffer, "uri:", 4)) { + mime_info.file_name = get_token (neg->pool, &body, 0); + } + else if (!strncmp (buffer, "content-type:", 13)) { + struct accept_rec accept_info; + + get_entry (neg->pool, &accept_info, body); + set_mime_fields (&mime_info, &accept_info); + } + else if (!strncmp (buffer, "content-length:", 15)) { + mime_info.bytes = atoi(body); + } + else if (!strncmp (buffer, "content-language:", 17)) { + mime_info.content_languages = + do_languages_line(neg->pool, &body); + } + else if (!strncmp (buffer, "content-encoding:", 17)) { + mime_info.content_encoding = get_token (neg->pool, &body, 0); + str_tolower (mime_info.content_encoding); + } + else if (!strncmp (buffer, "description:", 12)) { + mime_info.description = get_token (neg->pool, &body, 0); + } + } else { + if (mime_info.type_quality > 0 && *mime_info.file_name) + { + void *new_var = push_array (neg->avail_vars); + memcpy (new_var, (void *)&mime_info, sizeof (var_rec)); + } + + + clean_var_rec(&mime_info); + } + } while (hstate != header_eof); + + pfclose (neg->pool, map); + return OK; +} + +/***************************************************************** + * + * Same, except we use a filtered directory listing as the map... + */ + +int read_types_multi (negotiation_state *neg) +{ + request_rec *r = neg->r; + + char *filp; + int prefix_len; + DIR *dirp; + struct DIR_TYPE *dir_entry; + struct var_rec mime_info; + struct accept_rec accept_info; + void *new_var; + + clean_var_rec (&mime_info); + + if (!(filp = strrchr (r->filename, '/'))) return DECLINED; /* Weird... */ + + if (strncmp(r->filename, "proxy:", 6) == 0) + return DECLINED; + + ++filp; + prefix_len = strlen (filp); + + dirp = opendir (neg->dir_name); /* Not pool protected; sigh... */ + + if (dirp == NULL) { + log_reason("cannot read directory for multi", neg->dir_name, r); + return FORBIDDEN; + } + + while ((dir_entry = readdir (dirp))) { + + request_rec *sub_req; + + /* Do we have a match? */ + + if (strncmp (dir_entry->d_name, filp, prefix_len)) continue; + if (dir_entry->d_name[prefix_len] != '.') continue; + + /* Yep. See if it's something which we have access to, and + * which has a known type and encoding (as opposed to something + * which we'll be slapping default_type on later). + */ + + sub_req = sub_req_lookup_file (dir_entry->d_name, r); + + /* If it has a handler, we'll pretend it's a CGI script, + * since that's a good indication of the sort of thing it + * might be doing. + */ + if (sub_req->handler && !sub_req->content_type) + sub_req->content_type = CGI_MAGIC_TYPE; + + if (sub_req->status != HTTP_OK || !sub_req->content_type) { + destroy_sub_req(sub_req); + continue; + } + + /* If it's a map file, we use that instead of the map + * we're building... + */ + + if (((sub_req->content_type) && + !strcmp (sub_req->content_type, MAP_FILE_MAGIC_TYPE)) || + ((sub_req->handler) && + !strcmp (sub_req->handler, "type-map"))) { + closedir(dirp); + + neg->avail_vars->nelts = 0; + return read_type_map (neg, sub_req); + } + + /* Have reasonable variant --- gather notes. + */ + + mime_info.sub_req = sub_req; + mime_info.file_name = pstrdup(neg->pool, dir_entry->d_name); + if (sub_req->content_encoding) { + mime_info.content_encoding = sub_req->content_encoding; + str_tolower(mime_info.content_encoding); + } + if (sub_req->content_languages) { + int i; + mime_info.content_languages = sub_req->content_languages; + if (mime_info.content_languages) + for (i = 0; i < mime_info.content_languages->nelts; ++i) + str_tolower(((char**) + (mime_info.content_languages->elts))[i]); + } + + get_entry (neg->pool, &accept_info, sub_req->content_type); + set_mime_fields (&mime_info, &accept_info); + + new_var = push_array (neg->avail_vars); + memcpy (new_var, (void *)&mime_info, sizeof (var_rec)); + + clean_var_rec(&mime_info); + } + + closedir(dirp); + return OK; +} + + +/***************************************************************** + * And now for the code you've been waiting for... actually + * finding a match to the client's requirements. + */ + +/* Matching MIME types ... the star/star and foo/star commenting conventions + * are implemented here. (You know what I mean by star/star, but just + * try mentioning those three characters in a C comment). Using strcmp() + * is legit, because everything has already been smashed to lowercase. + * + * Note also that if we get an exact match on the media type, we update + * level_matched for use in level_cmp below... + * + * We also give a value for mime_stars, which is used later. It should + * be 1 for star/star, 2 for type/star and 3 for type/subtype. + */ + +int mime_match (accept_rec *accept, var_rec *avail) +{ + char *accept_type = accept->type_name; + char *avail_type = avail->type_name; + int len = strlen(accept_type); + + if (accept_type[0] == '*') { /* Anything matches star/star */ + if (avail->mime_stars < 1) + avail->mime_stars = 1; + return 1; + } + else if ((accept_type[len - 1] == '*') && + !strncmp (accept_type, avail_type, len - 2)) { + if (avail->mime_stars < 2) + avail->mime_stars = 2; + return 1; + } + else if (!strcmp (accept_type, avail_type) + || (!strcmp (accept_type, "text/html") + && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE) + || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) { + if (accept->level >= avail->level) { + avail->level_matched = avail->level; + avail->mime_stars = 3; + return 1; + } + } + + return OK; +} + +/* This code implements a piece of the tie-breaking algorithm between + * variants of equal quality. This piece is the treatment of variants + * of the same base media type, but different levels. What we want to + * return is the variant at the highest level that the client explicitly + * claimed to accept. + * + * If all the variants available are at a higher level than that, or if + * the client didn't say anything specific about this media type at all + * and these variants just got in on a wildcard, we prefer the lowest + * level, on grounds that that's the one that the client is least likely + * to choke on. + * + * (This is all motivated by treatment of levels in HTML --- we only + * want to give level 3 to browsers that explicitly ask for it; browsers + * that don't, including HTTP/0.9 browsers that only get the implicit + * "Accept: * / *" [space added to avoid confusing cpp --- no, that + * syntax doesn't really work] should get HTML2 if available). + * + * (Note that this code only comes into play when we are choosing among + * variants of equal quality, where the draft standard gives us a fair + * bit of leeway about what to do. It ain't specified by the standard; + * rather, it is a choice made by this server about what to do in cases + * where the standard does not specify a unique course of action). + */ + +int level_cmp (var_rec *var1, var_rec *var2) +{ + /* Levels are only comparable between matching media types */ + + if (var1->is_pseudo_html && !var2->is_pseudo_html) + return 0; + + if (!var1->is_pseudo_html && strcmp (var1->type_name, var2->type_name)) + return 0; + + /* Take highest level that matched, if either did match. */ + + if (var1->level_matched > var2->level_matched) return 1; + if (var1->level_matched < var2->level_matched) return -1; + + /* Neither matched. Take lowest level, if there's a difference. */ + + if (var1->level < var2->level) return 1; + if (var1->level > var2->level) return -1; + + /* Tied */ + + return 0; +} + +/* Finding languages. The main entry point is set_language_quality() + * which is called for each variant. It sets two elements in the + * variant record: + * language_quality - the 'q' value of the 'best' matching language + * from Accept-Language: header (HTTP/1.1) + * lang_index - Pre HTTP/1.1 language priority, using + * position of language on the Accept-Language: + * header, if present, else LanguagePriority + * directive order. + * + * When we do the variant checking for best variant, we use language + * quality first, and if a tie, language_index next (this only + * applies when _not_ using the network algorithm). If using + * network algorithm, lang_index is never used. + * + * set_language_quality() calls find_lang_index() and find_default_index() + * to set lang_index. + */ + +int find_lang_index (array_header *accept_langs, char *lang) +{ + accept_rec *accs; + int i; + + if (!lang) + return -1; + + accs = (accept_rec *)accept_langs->elts; + + for (i = 0; i < accept_langs->nelts; ++i) + if (!strncmp (lang, accs[i].type_name, strlen(accs[i].type_name))) + return i; + + return -1; +} + +/* This function returns the priority of a given language + * according to LanguagePriority. It is used in case of a tie + * between several languages. + */ + +int find_default_index (neg_dir_config *conf, char *lang) +{ + array_header *arr; + int nelts; + char **elts; + int i; + + if (!lang) + return -1; + + arr = conf->language_priority; + nelts = arr->nelts; + elts = (char **) arr->elts; + + for (i = 0; i < nelts; ++i) + if (!strcasecmp (elts[i], lang)) + return i; + + return -1; +} + +/* set_default_lang_quality() sets the quality we apply to variants + * which have no language assigned to them. If none of the variants + * have a language, we are not negotiating on language, so all are + * acceptable, and we set the default q value to 1.0. However if + * some of the variants have languages, we set this default to 0.001. + * The value of this default will be applied to all variants with + * no explicit language -- which will have the effect of making them + * acceptable, but only if no variants with an explicit language + * are acceptable. The default q value set here is assigned to variants + * with no language type in set_language_quality(). + * + * Note that if using the transparent negotiation network algorythm, + * we don't use this fiddle. + */ + +void set_default_lang_quality(negotiation_state *neg) +{ + var_rec *avail_recs = (var_rec *)neg->avail_vars->elts; + int j; + + if (!neg->use_transparent_neg) + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant->content_languages && + variant->content_languages->nelts) { + neg->default_lang_quality = 0.001; + return; + } + } + + neg->default_lang_quality = 1.0; +} + +/* Set the language_quality value in the variant record. Also + * assigns lang_index for back-compat. + * + * To find the language_quality value, we look for the 'q' value + * of the 'best' matching language on the Accept-Language: + * header. The'best' match is the language on Accept-Language: + * header which matches the language of this variant either fully, + * or as far as the prefix marker (-). If two or more languages + * match, use the longest string from the Accept-Language: header + * (see HTTP/1.1 [14.4]) + * + * When a variant has multiple languages, we find the 'best' + * match for each variant language tag as above, then select the + * one with the highest q value. Because both the accept-header + * and variant can have multiple languages, we now have a hairy + * loop-within-a-loop here. + * + * If the variant has no language and we have no Accept-Language + * items, leave the quality at 1.0 and return. + * + * If the variant has no language, we use the default as set by + * set_default_lang_quality() (1.0 if we are not negotiating on + * language, 0.001 if we are). + * + * Following the setting of the language quality, we drop through to + * set the old 'lang_index'. This is set based on either the order + * of the languages on the Accept-Language header, or the + * order on the LanguagePriority directive. This is only used + * in the negotiation if the language qualities tie. + */ + +void set_language_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + int naccept = neg->accept_langs->nelts; + int index; + neg_dir_config *conf = NULL; + char *firstlang; + + if (naccept == 0) + conf = (neg_dir_config *) get_module_config (neg->r->per_dir_config, + &negotiation_module); + + if (naccept == 0 && (!variant->content_languages || + !variant->content_languages->nelts)) + return; /* no accept-language and no variant lang */ + + if (!variant->content_languages || !variant->content_languages->nelts) { + /* This variant has no content-language, so use the default + * quality factor for variants with no content-language + * (previously set by set_default_lang_quality()). */ + variant->lang_quality = neg->default_lang_quality; + + if (naccept == 0) + return; /* no accept-language items */ + + } + else if (naccept) { + /* Variant has one (or more) languages, and we have one (or more) + * language ranges on the Accept-Language header. Look for + * the best match. We do this by going through each language + * on the variant description looking for a match on the + * Accept-Language header. The best match is the longest matching + * language on the header. The final result is the best q value + * from all the languages on the variant description. + */ + int j; + float fiddle_q = 0.0; + accept_rec *accs = (accept_rec *)neg->accept_langs->elts; + accept_rec *best = NULL, *star = NULL; + char *p; + + for (j = 0; j < variant->content_languages->nelts; ++j) { + char *lang; /* language from variant description */ + accept_rec *bestthistag = NULL; + int prefixlen = 0; + int longest_lang_range_len = 0; + int len; + /* lang is the variant's language-tag, which is the one + * we are allowed to use the prefix of in HTTP/1.1 + */ + lang = ((char **)(variant->content_languages->elts))[j]; + p = strchr(lang, '-'); /* find prefix part (if any) */ + if (p) + prefixlen = p - lang; + + /* now find the best (i.e. longest) matching Accept-Language + * header language. We put the best match for this tag in + * bestthistag. We cannot update the overall best (based on + * q value) because the best match for this tag is the longest + * language item on the accept header, not necessarily the + * highest q. + */ + for (i = 0; i < neg->accept_langs->nelts; ++i) { + if (!strcmp(accs[i].type_name, "*")) { + if (!star) + star = &accs[i]; + continue; + } + + /* Find language. We match if either the variant language + * tag exactly matches, or the prefix of the tag up to the + * '-' character matches the whole of the language in the + * Accept-Language header. We only use this accept-language + * item as the best match for the current tag if it + * is longer than the previous best match */ + if ((!strcmp (lang, accs[i].type_name) || + (prefixlen && + !strncmp(lang, accs[i].type_name, prefixlen) && + (accs[i].type_name[prefixlen] == '\0'))) && + ((len = strlen(accs[i].type_name)) > + longest_lang_range_len)) { + longest_lang_range_len = len; + bestthistag = &accs[i]; + } + + if (! bestthistag) { + /* The next bit is a fiddle. Some browsers might be + * configured to send more specific language ranges + * than desirable. For example, an Accept-Language of + * en-US should never match variants with languages en + * or en-GB. But US English speakers might pick en-US + * as their language choice. So this fiddle checks if + * the language range has a prefix, and if so, it + * matches variants which match that prefix with a + * priority of 0.001. So a request for en-US would + * match variants of types en and en-GB, but at much + * lower priority than matches of en-US directly, or + * of any other language listed on the Accept-Language + * header + */ + if ((p = strchr(accs[i].type_name, '-'))) { + int plen = p - accs[i].type_name; + if (!strncmp(lang, accs[i].type_name, plen)) + fiddle_q = 0.001; + } + } + } + /* Finished looking at Accept-Language headers, the best + * (longest) match is in bestthistag, or NULL if no match + */ + if (!best || + (bestthistag && bestthistag->quality > best->quality)) + best = bestthistag; + } + + variant->lang_quality = best ? best->quality : + (star ? star->quality : fiddle_q); + } + + /* Now set the old lang_index field. Since this is old + * stuff anyway, don't both with handling multiple languages + * per variant, just use the first one assigned to it + */ + index = 0; + if (variant->content_languages && variant->content_languages->nelts) + firstlang = ((char**)variant->content_languages->elts)[0]; + else + firstlang = ""; + if (naccept == 0) /* Client doesn't care */ + index = find_default_index (conf, firstlang); + else /* Client has Accept-Language */ + index = find_lang_index (neg->accept_langs, firstlang); + variant->lang_index = index; + + return; +} + +/* Determining the content length --- if the map didn't tell us, + * we have to do a stat() and remember for next time. + * + * Grump. For Apache, even the first stat here may well be + * redundant (for multiviews) with a stat() done by the sub_req + * machinery. At some point, that ought to be fixed. + */ + +int find_content_length(negotiation_state *neg, var_rec *variant) +{ + struct stat statb; + + if (variant->bytes == 0) { + char *fullname = make_full_path (neg->pool, neg->dir_name, + variant->file_name); + + if (stat (fullname, &statb) >= 0) variant->bytes = statb.st_size; + } + + return variant->bytes; +} + +/* For a given variant, find the best matching Accept: header + * and assign the Accept: header's quality value to the + * accept_type_quality field of the variant, for later use in + * determining the best matching variant. + */ + +void set_accept_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + accept_rec *accept_recs = (accept_rec *)neg->accepts->elts; + float q = 0.0; + int q_definite = 1; + + /* if no Accept: header, leave quality alone (will + * remain at the default value of 1) */ + if (!neg->accepts || neg->accepts->nelts == 0) + return; + + /* + * Go through each of the ranges on the Accept: header, + * looking for the 'best' match with this variant's + * content-type. We use the best match's quality + * value (from the Accept: header) for this variant's + * accept_type_quality field. + * + * The best match is determined like this: + * type/type is better than type/ * is better than * / * + * if match is type/type, use the level mime param if available + */ + for (i = 0; i < neg->accepts->nelts; ++i) { + + accept_rec *type = &accept_recs[i]; + int prev_mime_stars; + + prev_mime_stars = variant->mime_stars; + + if (!mime_match(type, variant)) + continue; /* didn't match the content type at all */ + else + /* did match - see if there were less or more stars than + * in previous match + */ + if (prev_mime_stars == variant->mime_stars) + continue; /* more stars => not as good a match */ + + /* Check maxbytes -- not in HTTP/1.1 or Holtman */ + + if (type->max_bytes > 0 + && (find_content_length(neg, variant) + > type->max_bytes)) + continue; + + /* If we are allowed to mess with the q-values, + * make wildcards very low, so we have a low chance + * of ending up with them if there's something better. + */ + + if (!neg->accept_q && variant->mime_stars == 1) q = 0.01; + else if (!neg->accept_q && variant->mime_stars == 2) q = 0.02; + else q = type->quality; + + q_definite = (variant->mime_stars == 3); + } + variant->accept_type_quality = q; + variant->definite=variant->definite && q_definite; + + /* if the _best_ quality we got for this variant was 0.0, + * eliminate it now */ +} + +/* For a given variant, find the 'q' value of the charset given + * on the Accept-Charset line. If not charsets are listed, + * assume value of '1'. + */ + +void set_charset_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + accept_rec *accept_recs = (accept_rec *)neg->accept_charsets->elts; + char *charset = variant->content_charset; + accept_rec *star = NULL; + + /* if no Accept-Charset: header, leave quality alone (will + * remain at the default value of 1) */ + if (!neg->accept_charsets || neg->accept_charsets->nelts == 0) + return; + + if (charset == NULL || !*charset) charset = "iso-8859-1"; + + /* + * Go through each of the items on the Accept-Charset: header, + * looking for a match with this variant's charset. If none + * match, charset is unacceptable, so set quality to 0. + */ + for (i = 0; i < neg->accept_charsets->nelts; ++i) { + + accept_rec *type = &accept_recs[i]; + + if (!strcmp(type->type_name, charset)) { + variant->charset_quality = type->quality; + return; + } else + if (strcmp(type->type_name, "*") == 0) { + star = type; + } + } + /* No explicit match */ + if (star) { + variant->charset_quality = star->quality; + return; + } + /* If this variant is in charset iso-8859-1, the default is 1.0 */ + if (strcmp(charset, "iso-8859-1") == 0) { + variant->charset_quality = 1.0; + } else { + variant->charset_quality = 0.0; + } +} + +/* For a given variant, find the best matching Accept: header + * and assign the Accept: header's quality value to the + * accept_type_quality field of the variant, for later use in + * determining the best matching variant. + */ + +/* is_identity_encoding is included for back-compat, but does anyone + * use 7bit, 8bin or binary in their var files?? + */ + +int is_identity_encoding (char *enc) +{ + return (!enc || !enc[0] || !strcmp (enc, "7bit") || !strcmp (enc, "8bit") + || !strcmp (enc, "binary")); +} + +void set_encoding_quality(negotiation_state *neg, var_rec *variant) +{ + int i; + accept_rec *accept_recs = (accept_rec *)neg->accept_encodings->elts; + char *enc = variant->content_encoding; + + if (!enc || is_identity_encoding(enc)) + return; + + + /* if no Accept: header, leave quality alone (will + * remain at the default value of 1) */ + if (neg->accept_encodings->nelts == 0) { + /* If we had an empty Accept-Encoding header, assume that + * no encodings are acceptable, else all encodings are ok */ + variant->encoding_quality = neg->have_accept_header ? 0 : 1; + return; + } + + /* Go through each of the encodings on the Accept-Encoding: header, + * looking for a match with our encoding + */ + for (i = 0; i < neg->accept_encodings->nelts; ++i) { + char *name = accept_recs[i].type_name; + + if (!strcmp(name, enc)) { + variant->encoding_quality = 1; + return; + } + } + + /* Encoding not found on Accept-Encoding: header, so it is + * _not_ acceptable */ + variant->encoding_quality = 0; +} + +/* Possible results of the network algorithm */ +enum algorithm_results { + na_not_applied = -1, /* net algorithm not used */ + na_choice = 1, /* choose variant */ + na_list /* list variants */ +}; + +/* + * This is a heavily-rewritten 'best_match' function. For a start, it + * now returns an int, which has one of the three values: na_not_applied, + * na_choice or na_list, which give the result of the network algorithm + * (if it was not applied, the return value is na_not_applied). + * The best variable is returned in *pbest. It also has two possible + * algorithms for determining the best match: the network algorithm, + * and the standard Apache algorithm. These are split out into + * separate functions (is_variant_better_na() and is_variant_better()). + * + * Previously, best_match iterated first through the content_types + * in the Accept: header, then checked each variant, and eliminated + * those that didn't match the variant's type. We cannot do this because + * we need full information, including language, charset, etc + * quality for _every_ variant, for the Alternates: header, + * and (possibly) the human-readable choice responses or 406 errors. + * + * After the 'best' (if any) is determined, the overall result of + * the negotiation is obtained. If the network algorithm was not + * in use, the result is na_not_applied. Else the result is + * na_list if 'short accept header' is in use, else na_list + * if _no_ best match was found, or na_choice if a best match + * was found. + */ + +/* Firstly, the negotiation 'network algorithm' from Holtman. + */ + +int is_variant_better_na(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq) +{ + float bestq = *p_bestq, q; + + /* Note: Encoding is not negotiated in the Holtman + * transparent neg draft, so we ignored it here. But + * it does mean we could return encodings the UA + * or proxy cannot handle. Eek. */ + + q = variant->accept_type_quality * + variant->type_quality * + variant->charset_quality * + variant->lang_quality; + +#ifdef NEG_DEBUG + fprintf(stderr, "Variant: file=%s type=%s lang=%s acceptq=%1.3f langq=%1.3f typeq=%1.3f q=%1.3f definite=%d\n", + variant->file_name ? variant->file_name : "", + variant->type_name ? variant->type_name : "", + variant->content_languages ? merge_string_array(neg->pool, variant->content_languages, ",") : "", + variant->accept_type_quality, + variant->lang_quality, + variant->type_quality, + q, + variant->definite + ); +#endif + + if (q > bestq) { + *p_bestq = q; + return 1; + } + if (q == bestq) { + /* If the best variant's charset is ISO-8859-1 and this variant has + the same charset quality, then we prefer this variant */ + if (variant->charset_quality == best->charset_quality && + (variant->content_charset != NULL && + *variant->content_charset != '\0' && + strcmp(variant->content_charset, "iso-8859-1") != 0) && + (best->content_charset == NULL || + *best->content_charset == '\0' || + strcmp(best->content_charset, "iso-8859-1") == 0)) { + *p_bestq = q; + return 1; + } + } + return 0; +} + +/* Negotiation algorithm as used by previous versions of Apache + * (just about). + */ + +float is_variant_better(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq) +{ + float bestq = *p_bestq, q; + int levcmp; + + /* + * For non-transparent negotiation, server can choose how + * to handle the negotiation. We'll use the following in + * order: content-type, language, content-type level, charset, + * content length. + * + * For each check, we have three possible outcomes: + * This variant is worse than current best: return 0 + * This variant is better than the current best: + * assign this variant's q to *p_bestq, and return 1 + * This variant is just as desirable as the current best: + * drop through to the next test. + * + * This code is written in this long-winded way to allow future + * customisation, either by the addition of additional + * checks, or to allow the order of the checks to be determined + * by configuration options (e.g. we might prefer to check + * language quality _before_ content type). + */ + + /* First though, eliminate this variant if it is not + * acceptable by type, charset, encoding or language. + */ + + if (variant->encoding_quality == 0 || + variant->lang_quality == 0 || + variant->type_quality == 0 || + variant->charset_quality == 0 || + variant->accept_type_quality == 0) + return 0; /* don't consider unacceptables */ + + q = variant->accept_type_quality * variant->type_quality; + if (q == 0.0 || q < bestq) return 0; + if (q > bestq || !best) { + *p_bestq = q; + return 1; + } + + /* language */ + if (variant->lang_quality < best->lang_quality) + return 0; + if (variant->lang_quality > best->lang_quality) { + *p_bestq = q; + return 1; + } + + /* if language qualities were equal, try the LanguagePriority + * stuff */ + if (best->lang_index != -1 && variant->lang_index > best->lang_index) + return 0; + if (variant->lang_index != -1 && + (variant->lang_index < best->lang_index || best->lang_index == -1)) { + *p_bestq = q; + return 1; + } + + /* content-type level (text/html only?) */ + levcmp = level_cmp (variant, best); + if (levcmp == -1) return 0; + if (levcmp == 1) { + *p_bestq = q; + return 1; + } + + /* encoding -- can only be 1 or 0, and if 0 we eliminated this + * variant at the start of this function. However we + * prefer variants with no encoding over those with encoding */ + if (!*best->content_encoding && *variant->content_encoding) + return 0; + if (*best->content_encoding && !*variant->content_encoding) { + *p_bestq = q; + return 1; + } + + + /* charset */ + if (variant->charset_quality < best->charset_quality) + return 0; + /* If the best variant's charset is ISO-8859-1 and this variant has + the same charset quality, then we prefer this variant */ + if (variant->charset_quality > best->charset_quality || + ((variant->content_charset != NULL && + *variant->content_charset != '\0' && + strcmp(variant->content_charset, "iso-8859-1") != 0) && + (best->content_charset == NULL || + *best->content_charset == '\0' || + strcmp(best->content_charset, "iso-8859-1") == 0))) { + *p_bestq = q; + return 1; + } + + + /* content length if all else equal */ + if (find_content_length(neg, variant) + >= + find_content_length(neg, best)) + return 0; + + /* ok, to get here means every thing turned out equal, except + * we have a shorter content length, so use this variant */ + *p_bestq = q; + return 1; +} + +int best_match(negotiation_state *neg, var_rec **pbest) +{ + int j; + var_rec *best = NULL; + float bestq = 0.0; + enum algorithm_results algorithm_result = na_not_applied; + + var_rec *avail_recs = (var_rec *)neg->avail_vars->elts; + + set_default_lang_quality(neg); + + /* + * Find the 'best' variant + */ + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + + var_rec *variant = &avail_recs[j]; + + /* Find all the relevant 'quality' values from the + * Accept... headers, and store in the variant + */ + set_accept_quality(neg, variant); + set_language_quality(neg, variant); + set_encoding_quality(neg, variant); + set_charset_quality(neg, variant); + + /* Now find out if this variant is better than the current + * best, either using the network algorithm, or Apache's + * internal server-driven algorithm. Presumably other + * server-driven algorithms are possible, and could be + * implemented here. + */ + + if (neg->use_transparent_neg) { + if (is_variant_better_na(neg, variant, best, &bestq)) + best = variant; + } + else { + if (is_variant_better(neg, variant, best, &bestq)) + best = variant; + } + } + + /* We now either have a best variant, or no best variant + */ + if (neg->use_transparent_neg) { + if (neg->short_accept_headers) + algorithm_result = na_list; + else { + /* From Holtman, result is: + * If variant & URI are not neigbors, list_ua or list_os + * Else + * If UA can do trans neg + * IF best is definite && best q > 0, choice_ua + * ELSE list_ua + * ELSE + * IF best q > 0, choose_os + * ELSE list_os (or forward_os on proxy) + */ + + /* assume variant and URI are neigbors (since URI in + * var map must be in same directory) */ + + if(neg->use_transparent_neg) + algorithm_result = (best && best->definite) && (bestq>0) + ? na_choice : na_list; + else + algorithm_result = bestq>0 ? na_choice : na_list; + } + } + + *pbest = best; + return algorithm_result; +} + +/* + * Sets the Alternates and Vary headers, used if we are going to + * return 406 Not Acceptable status, a 300 Multiple Choice status, + * or a Choice response. + * + * 'type' is the result of the network algorithm, if applied. + * We do different things if the network algorithm was not applied + * (type == na_not_applied): no Alternates header, and Vary: + * does not include 'negotiate'. + * + * We should also add a max-age lifetime for the Alternates header, + * but how long we we give it? Presumably this should be + * configurable in the map file. + */ + +void set_neg_headers(request_rec *r, negotiation_state *neg, int na_result) +{ + int j; + var_rec *avail_recs = (var_rec *)neg->avail_vars->elts; + char *sample_type = NULL; + char *sample_language = NULL; + char *sample_encoding = NULL; + char *sample_charset = NULL; + int vary_by_type = 0; + int vary_by_language = 0; + int vary_by_charset = 0; + int vary_by_encoding = 0; + array_header *hdrs; + + /* Put headers into err_headers_out, new send_http_header() + * outputs both headers_out and err_headers_out */ + hdrs = r->err_headers_out; + + for (j = 0; j < neg->avail_vars->nelts; ++j) { + + var_rec *variant = &avail_recs[j]; + char *rec; + char qstr[6]; + long len; + char lenstr[22]; /* enough for 2^64 */ + + ap_snprintf(qstr, sizeof(qstr), "%1.3f", variant->type_quality); + + /* Strip trailing zeros (saves those valuable network bytes) */ + if (qstr[4] == '0') { + qstr[4] = '\0'; + if (qstr[3] == '0') { + qstr[3] = '\0'; + if (qstr[2] == '0') { + qstr[1] = '\0'; + } + } + } + + rec = pstrcat(r->pool, "{\"", variant->file_name, "\" ", qstr, NULL); + if (variant->type_name) { + if (*variant->type_name) + rec = pstrcat(r->pool, rec, " {type ", + variant->type_name, "}", NULL); + if (!sample_type) sample_type = variant->type_name; + else if (strcmp(sample_type, variant->type_name)) + vary_by_type = 1; + } + if (variant->content_languages && variant->content_languages->nelts) { + char *langs = + merge_string_array (r->pool, variant->content_languages, ","); + rec = pstrcat(r->pool, rec, " {language ", langs, "}", NULL); + if (!sample_language) sample_language = langs; + else if (strcmp(sample_language, langs)) + vary_by_language = 1; + } + if (variant->content_encoding) { + if (!sample_encoding) sample_encoding = variant->content_encoding; + else if (strcmp(sample_encoding, variant->content_encoding)) + vary_by_encoding = 1; + } + if (variant->content_charset) { + if (*variant->content_charset) + rec = pstrcat(r->pool, rec, " {charset ", + variant->content_charset, "}", NULL); + if (!sample_charset) sample_charset = variant->content_charset; + else if (strcmp(sample_charset, variant->content_charset)) + vary_by_charset = 1; + } + if ((len = find_content_length(neg, variant)) != 0) { + ap_snprintf(lenstr, sizeof(lenstr), "%ld", len); + rec = pstrcat(r->pool, rec, " {length ", lenstr, "}", NULL); + } + + rec = pstrcat(r->pool, rec, "}", NULL); + + if (na_result != na_not_applied) + table_merge(hdrs, "Alternates", rec); + } + + if (na_result != na_not_applied) + table_merge(hdrs, "Vary", "negotiate"); + if (vary_by_type) + table_merge(hdrs, "Vary", "accept"); + if (vary_by_language) + table_merge(hdrs, "Vary", "accept-language"); + if (vary_by_charset) + table_merge(hdrs, "Vary", "accept-charset"); + if (vary_by_encoding && na_result == na_not_applied) + table_merge(hdrs, "Vary", "accept-encoding"); +} + +/********************************************************************** + * + * Return an HTML list of variants. This is output as part of the + * 300 or 406 status body. + */ + +char *make_variant_list (request_rec *r, negotiation_state *neg) +{ + int i; + char *t; + + t = pstrdup(r->pool, "Available variants:\n<ul>\n"); + for (i = 0; i < neg->avail_vars->nelts; ++i) { + var_rec *variant = &((var_rec *)neg->avail_vars->elts)[i]; + char *filename = variant->file_name ? variant->file_name : ""; + array_header *languages = variant->content_languages; + char *description = variant->description ? variant->description : ""; + + /* The format isn't very neat, and it would be nice to make + * the tags human readable (eg replace 'language en' with + * 'English'). */ + t = pstrcat(r->pool, t, "<li><a href=\"", filename, "\">", + filename, "</a> ", description, NULL); + if (variant->type_name && *variant->type_name) + t = pstrcat(r->pool, t, ", type ", variant->type_name, NULL); + if (languages && languages->nelts) + t = pstrcat(r->pool, t, ", language ", + merge_string_array(r->pool, languages, ", "), + NULL); + if (variant->content_charset && *variant->content_charset) + t = pstrcat(r->pool, t, ", charset ", variant->content_charset, NULL); + t = pstrcat(r->pool, t, "\n", NULL); + } + t = pstrcat(r->pool, t, "</ul>\n", NULL); + + return t; +} + +void store_variant_list (request_rec *r, negotiation_state *neg) +{ + if (r->main == NULL) { + table_set (r->notes, "variant-list", make_variant_list (r, neg)); + } else { + table_set (r->main->notes, "variant-list", make_variant_list (r->main, neg)); + } +} + +/* Called if we got a "Choice" response from the network algorithm. + * It checks the result of the chosen variant to see if it + * is itself negotiated (if so, return error VARIANT_ALSO_VARIES). + * Otherwise, add the appropriate headers to the current response. + */ + +int setup_choice_response(request_rec *r, negotiation_state *neg, var_rec *variant) +{ + request_rec *sub_req; + char *sub_vary; + + if (!variant->sub_req) { + int status; + + sub_req = sub_req_lookup_file(variant->file_name, r); + status = sub_req->status; + if (status != HTTP_OK && status != HTTP_MULTIPLE_CHOICES) { + destroy_sub_req(sub_req); + return status; + } + variant->sub_req = sub_req; + } + else + sub_req = variant->sub_req; + + + /* The network algorithm told us to return a "Choice" + * response. This is the normal variant response, with + * some extra headers. First, ensure that the chosen + * variant did not itself return a "List" or "Choice" response. + * If not, set the appropriate headers, and fall through to + * the normal variant handling + */ + + if ((sub_req->status == HTTP_MULTIPLE_CHOICES) || + (table_get(sub_req->err_headers_out, "Alternates")) || + (table_get(sub_req->err_headers_out, "Content-Location"))) + return VARIANT_ALSO_VARIES; + + if ((sub_vary = table_get(sub_req->err_headers_out, "Vary")) != NULL) + table_set(r->err_headers_out, "Variant-Vary", sub_vary); + table_set(r->err_headers_out, "Content-Location", variant->file_name); + set_neg_headers(r, neg, na_choice); /* add Alternates and Vary */ + /* to do: add Expires */ + + return 0; +} + +/**************************************************************** + * + * Executive... + */ + +int handle_map_file (request_rec *r) +{ + negotiation_state *neg = parse_accept_headers (r); + var_rec *best; + int res; + int na_result; + + char *udir; + + if ((res = read_type_map (neg, r))) return res; + + maybe_add_default_encodings(neg, 0); + + na_result = best_match(neg, &best); + + /* na_result is one of + * na_not_applied: we didn't use the network algorithm + * na_choice: return a "Choice" response + * na_list: return a "List" response (no variant chosen) + */ + + if (na_result == na_list) { + set_neg_headers(r, neg, na_list); + store_variant_list (r, neg); + return MULTIPLE_CHOICES; + } + + if (!best) { + log_reason ("no acceptable variant", r->filename, r); + + set_neg_headers(r, neg, na_result); + store_variant_list (r, neg); + return NOT_ACCEPTABLE; + } + + if (na_result == na_choice) + if ((res = setup_choice_response(r, neg, best)) != 0) + return res; + + /* Make sure caching works - Vary should handle HTTP/1.1, but for + * HTTP/1.0, we can't allow caching at all. NB that we merge the + * header in case some other module negotiates on something else. + */ + if (!do_cache_negotiated_docs(r->server) && (r->proto_num < 1001)) + r->no_cache = 1; + + if (na_result == na_not_applied) + set_neg_headers(r, neg, na_not_applied); + + if (r->path_info && *r->path_info) { + r->uri[find_path_info(r->uri, r->path_info)] = '\0'; + } + udir = make_dirstr (r->pool, r->uri, count_dirs (r->uri)); + udir = escape_uri(r->pool, udir); + internal_redirect(pstrcat(r->pool, udir, best->file_name, r->path_info, + NULL), r); + return OK; +} + +int handle_multi (request_rec *r) +{ + negotiation_state *neg; + var_rec *best, *avail_recs; + request_rec *sub_req; + int res; + int j; + int na_result; /* result of network algorithm */ + + if (r->finfo.st_mode != 0 || !(allow_options (r) & OPT_MULTI)) + return DECLINED; + + neg = parse_accept_headers (r); + + if ((res = read_types_multi (neg))) { +return_from_multi: + /* free all allocated memory from subrequests */ + avail_recs = (var_rec *)neg->avail_vars->elts; + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant->sub_req) { + destroy_sub_req(variant->sub_req); + } + } + return res; + } + if (neg->avail_vars->nelts == 0) return DECLINED; + + maybe_add_default_encodings(neg, + r->method_number != M_GET + || r->args || r->path_info); + + na_result = best_match(neg, &best); + if (na_result == na_list) { + /* + * Network algorithm tols us to output a "List" response. + * This is output at a 300 status code, which we will + * return. The list of variants will be stored in r->notes + * under the name "variants-list". + */ + set_neg_headers(r, neg, na_list); /* set Alternates: and Vary: */ + + store_variant_list (r, neg); + res = MULTIPLE_CHOICES; + goto return_from_multi; + } + + if (!best) { + log_reason ("no acceptable variant", r->filename, r); + + set_neg_headers (r, neg, na_result); + store_variant_list (r, neg); + res = NOT_ACCEPTABLE; + goto return_from_multi; + } + + if (na_result == na_choice) + if ((res = setup_choice_response(r, neg, best)) != 0) { + goto return_from_multi; + } + + if (! (sub_req = best->sub_req)) { + /* We got this out of a map file, so we don't actually have + * a sub_req structure yet. Get one now. + */ + + sub_req = sub_req_lookup_file (best->file_name, r); + if (sub_req->status != HTTP_OK) { + res = sub_req->status; + destroy_sub_req(sub_req); + goto return_from_multi; + } + } + + /* BLETCH --- don't multi-resolve non-ordinary files */ + + if (!S_ISREG(sub_req->finfo.st_mode)) { + res = NOT_FOUND; + goto return_from_multi; + } + + /* Otherwise, use it. */ + + if (!do_cache_negotiated_docs(r->server) && (r->proto_num < 1001)) + r->no_cache = 1; + + if (na_result == na_not_applied) + set_neg_headers(r, neg, na_not_applied); + + r->filename = sub_req->filename; + r->handler = sub_req->handler; + r->content_type = sub_req->content_type; + r->content_encoding = sub_req->content_encoding; + r->content_languages = sub_req->content_languages; + r->content_language = sub_req->content_language; + r->finfo = sub_req->finfo; + r->per_dir_config = sub_req->per_dir_config; + /* copy output headers from subrequest, but leave negotiation headers */ + r->notes = overlay_tables(r->pool, sub_req->notes, r->notes); + r->headers_out = overlay_tables(r->pool, sub_req->headers_out, + r->headers_out); + r->err_headers_out = overlay_tables(r->pool, sub_req->err_headers_out, + r->err_headers_out); + r->subprocess_env = overlay_tables(r->pool, sub_req->subprocess_env, + r->subprocess_env); + avail_recs = (var_rec *)neg->avail_vars->elts; + for (j = 0; j < neg->avail_vars->nelts; ++j) { + var_rec *variant = &avail_recs[j]; + if (variant != best && variant->sub_req) { + destroy_sub_req(variant->sub_req); + } + } + return OK; +} + +handler_rec negotiation_handlers[] = { +{ MAP_FILE_MAGIC_TYPE, handle_map_file }, +{ "type-map", handle_map_file }, +{ NULL } +}; + +module negotiation_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + create_neg_dir_config, /* dir config creater */ + merge_neg_dir_configs, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + negotiation_cmds, /* command table */ + negotiation_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + handle_multi, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_rewrite.c b/usr.sbin/httpd/src/mod_rewrite.c new file mode 100644 index 00000000000..15fb1d8dffa --- /dev/null +++ b/usr.sbin/httpd/src/mod_rewrite.c @@ -0,0 +1,3335 @@ + +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + + +/* _ _ _ +** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ +** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ +** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/ +** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___| +** |_____| +** +** URL Rewriting Module +** +** This module uses a rule-based rewriting engine (based on a +** regular-expression parser) to rewrite requested URLs on the fly. +** +** It supports an unlimited number of additional rule conditions (which can +** operate on a lot of variables, even on HTTP headers) for granular +** matching and even external database lookups (either via plain text +** tables, DBM hash files or even external processes) for advanced URL +** substitution. +** +** It operates on the full URLs (including the PATH_INFO part) both in +** per-server context (httpd.conf) and per-dir context (.htaccess) and even +** can generate QUERY_STRING parts on result. The rewriting result finally +** can lead to internal subprocessing, external request redirection or even +** to internal proxy throughput. +** +** This module was originally written in April 1996 and +** gifted exclusively to the The Apache Group in July 1997 by +** +** Ralf S. Engelschall +** rse@engelschall.com +** www.engelschall.com +*/ + + + + + /* from the underlaying Unix system ... */ +#include <string.h> +#include <stdarg.h> +#include <time.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <netinet/in.h> + + /* from the Apache server ... */ +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_log.h" + + /* now our own stuff ... */ +#include "mod_rewrite.h" + + + + +/* +** +-------------------------------------------------------+ +** | | +** | static module configuration +** | | +** +-------------------------------------------------------+ +*/ + + +/* +** +** our interface to the Apache server kernel +** +** keep in mind: +** +** o Runtime logic of a request is as following: +** +** while(request or subrequest) { +** foreach(stage #1...#9) { +** foreach(module) { (**) +** try to run hook +** } +** } +** } +** +** o the order of modules at (**) is the inverted order as +** given in the "Configuration" file, i.e. the last module +** specified is the first one called for each hook! +** The core module is always the last! +** +** o there are two different types of result checking and +** continue processing: +** for hook #1,#4,#5,#6,#8: +** hook run loop stops on first modules which gives +** back a result != DECLINED, i.e. it usually returns OK +** which says "OK, module has handled this _stage_" and for #1 +** this have not to mean "Ok, the filename is now valid". +** for hook #2,#3,#7,#9: +** all hooks are run, independend of result +** +** o at the last stage, the core module always +** - says "BAD_REQUEST" if r->filename does not begin with "/" +** - prefix URL with document_root or replaced server_root +** with document_root and sets r->filename +** - always return a "OK" independed if the file really exists +** or not! +** +*/ + + /* the table of commands we provide */ +static command_rec command_table[] = { + { "RewriteEngine", cmd_rewriteengine, NULL, OR_FILEINFO, FLAG, + "On or Off to enable or disable (default) the whole rewriting engine" }, + { "RewriteOptions", cmd_rewriteoptions, NULL, OR_FILEINFO, ITERATE, + "List of option strings to set" }, + { "RewriteBase", cmd_rewritebase, NULL, OR_FILEINFO, TAKE1, + "the base URL of the per-directory context" }, + { "RewriteCond", cmd_rewritecond, NULL, OR_FILEINFO, RAW_ARGS, + "a input string and a to be applied regexp-pattern" }, + { "RewriteRule", cmd_rewriterule, NULL, OR_FILEINFO, RAW_ARGS, + "a URL-applied regexp-pattern and a substitution URL" }, + { "RewriteMap", cmd_rewritemap, NULL, RSRC_CONF, TAKE2, + "a mapname and a filename" }, + { "RewriteLog", cmd_rewritelog, NULL, RSRC_CONF, TAKE1, + "the filename of the rewriting logfile" }, + { "RewriteLogLevel", cmd_rewriteloglevel, NULL, RSRC_CONF, TAKE1, + "the level of the rewriting logfile verbosity (0=none, 1=std, .., 9=max)" }, + { NULL } +}; + + /* the table of content handlers we provide */ +static handler_rec handler_table[] = { + { "redirect-handler", handler_redirect }, + { NULL } +}; + + /* the main config structure */ +module rewrite_module = { + STANDARD_MODULE_STUFF, + + init_module, /* module initializer */ + + config_perdir_create, /* create per-dir config structures */ + config_perdir_merge, /* merge per-dir config structures */ + config_server_create, /* create per-server config structures */ + config_server_merge, /* merge per-server config structures */ + command_table, /* table of config file commands */ + + handler_table, /* [#8] table of MIME-typed-dispatched request action handlers */ + + hook_uri2file, /* [#1] URI to filename translation */ + + NULL, /* [#4] check_user_id: get and validate user id from the HTTP request */ + NULL, /* [#5] check_auth: check if the user is ok _here_ */ + NULL, /* [#2] check_access: check access by host address, etc. */ + + hook_mimetype, /* [#6] determine MIME type */ + + hook_fixup, /* [#7] pre-run fixups */ + NULL, /* [#9] log a transaction */ + NULL /* [#3] header parser */ +}; + + /* the cache */ +static cache *cachep; + + /* whether proxy module is available or not */ +static int proxy_available; + + /* the txt mapfile parsing stuff */ +#define MAPFILE_PATTERN "^([^ \t]+)[ \t]+([^ \t]+).*$" +#define MAPFILE_OUTPUT "$1,$2" +static regex_t *lookup_map_txtfile_regexp = NULL; +static regmatch_t lookup_map_txtfile_regmatch[10]; + + + + +/* +** +-------------------------------------------------------+ +** | | +** | configuration directive handling +** | | +** +-------------------------------------------------------+ +*/ + + +/* +** +** per-server configuration structure handling +** +*/ + +static void *config_server_create(pool *p, server_rec *s) +{ + rewrite_server_conf *a; + + a = (rewrite_server_conf *)pcalloc(p, sizeof(rewrite_server_conf)); + + a->state = ENGINE_DISABLED; + a->options = OPTION_NONE; + a->rewritelogfile = NULL; + a->rewritelogfp = -1; + a->rewriteloglevel = 1; + a->rewritemaps = make_array(p, 2, sizeof(rewritemap_entry)); + a->rewriteconds = make_array(p, 2, sizeof(rewritecond_entry)); + a->rewriterules = make_array(p, 2, sizeof(rewriterule_entry)); + + return (void *)a; +} + +static void *config_server_merge(pool *p, void *basev, void *overridesv) +{ + rewrite_server_conf *a, *base, *overrides; + + a = (rewrite_server_conf *)pcalloc(p, sizeof(rewrite_server_conf)); + base = (rewrite_server_conf *)basev; + overrides = (rewrite_server_conf *)overridesv; + + a->state = overrides->state; + a->options = overrides->options; + a->rewritelogfile = base->rewritelogfile != NULL ? base->rewritelogfile : overrides->rewritelogfile; + a->rewritelogfp = base->rewritelogfp != -1 ? base->rewritelogfp : overrides->rewritelogfp; + a->rewriteloglevel = overrides->rewriteloglevel; + + if (a->options & OPTION_INHERIT) { + a->rewritemaps = append_arrays(p, overrides->rewritemaps, base->rewritemaps); + a->rewriteconds = append_arrays(p, overrides->rewriteconds, base->rewriteconds); + a->rewriterules = append_arrays(p, overrides->rewriterules, base->rewriterules); + } + else { + a->rewritemaps = overrides->rewritemaps; + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + + +/* +** +** per-directory configuration structure handling +** +*/ + +static void *config_perdir_create(pool *p, char *path) +{ + rewrite_perdir_conf *a; + + a = (rewrite_perdir_conf *)pcalloc(p, sizeof(rewrite_perdir_conf)); + + a->state = ENGINE_DISABLED; + a->options = OPTION_NONE; + a->baseurl = NULL; + a->rewriteconds = make_array(p, 2, sizeof(rewritecond_entry)); + a->rewriterules = make_array(p, 2, sizeof(rewriterule_entry)); + + if (path == NULL) + a->directory = NULL; + else { + /* make sure it has a trailing slash */ + if (path[strlen(path)-1] == '/') + a->directory = pstrdup(p, path); + else + a->directory = pstrcat(p, path, "/", NULL); + } + + return (void *)a; +} + +static void *config_perdir_merge(pool *p, void *basev, void *overridesv) +{ + rewrite_perdir_conf *a, *base, *overrides; + + a = (rewrite_perdir_conf *)pcalloc(p, sizeof(rewrite_perdir_conf)); + base = (rewrite_perdir_conf *)basev; + overrides = (rewrite_perdir_conf *)overridesv; + + a->state = overrides->state; + a->options = overrides->options; + a->directory = overrides->directory; + a->baseurl = overrides->baseurl; + + if (a->options & OPTION_INHERIT) { + a->rewriteconds = append_arrays(p, overrides->rewriteconds, base->rewriteconds); + a->rewriterules = append_arrays(p, overrides->rewriterules, base->rewriterules); + } + else { + a->rewriteconds = overrides->rewriteconds; + a->rewriterules = overrides->rewriterules; + } + + return (void *)a; +} + + +/* +** +** the configuration commands +** +*/ + +static const char *cmd_rewriteengine(cmd_parms *cmd, rewrite_perdir_conf *dconf, int flag) +{ + rewrite_server_conf *sconf; + + sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module); + if (cmd->path == NULL) /* is server command */ + sconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + else /* is per-directory command */ + dconf->state = (flag ? ENGINE_ENABLED : ENGINE_DISABLED); + + return NULL; +} + +static const char *cmd_rewriteoptions(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *option) +{ + rewrite_server_conf *sconf; + const char *err; + + sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module); + if (cmd->path == NULL) /* is server command */ + err = cmd_rewriteoptions_setoption(cmd->pool, &(sconf->options), option); + else /* is per-directory command */ + err = cmd_rewriteoptions_setoption(cmd->pool, &(dconf->options), option); + + return err; +} + +static const char *cmd_rewriteoptions_setoption(pool *p, int *options, char *name) +{ + if (strcasecmp(name, "inherit") == 0) + *options |= OPTION_INHERIT; + else + return pstrcat(p, "RewriteOptions: unknown option '", name, "'\n", NULL); + return NULL; +} + +static const char *cmd_rewritelog(cmd_parms *cmd, void *dconf, char *a1) +{ + rewrite_server_conf *sconf; + + sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module); + sconf->rewritelogfile = a1; + + return NULL; +} + +static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1) +{ + rewrite_server_conf *sconf; + + sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module); + sconf->rewriteloglevel = atoi(a1); + + return NULL; +} + +static const char *cmd_rewritemap(cmd_parms *cmd, void *dconf, char *a1, char *a2) +{ + rewrite_server_conf *sconf; + rewritemap_entry *new; + struct stat st; + + sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module); + new = push_array(sconf->rewritemaps); + + new->name = a1; + if (strncmp(a2, "txt:", 4) == 0) { + new->type = MAPTYPE_TXT; + new->datafile = a2+4; + new->checkfile = a2+4; + } + else if (strncmp(a2, "dbm:", 4) == 0) { +#ifdef HAS_NDBM_LIB + new->type = MAPTYPE_DBM; + new->datafile = a2+4; + new->checkfile = pstrcat(cmd->pool, a2+4, NDBM_FILE_SUFFIX, NULL); +#else + return pstrdup(cmd->pool, "RewriteMap: cannot use NDBM mapfile, because no NDBM support compiled in"); +#endif + } + else if (strncmp(a2, "prg:", 4) == 0) { + new->type = MAPTYPE_PRG; + new->datafile = a2+4; + new->checkfile = a2+4; + } + else { + new->type = MAPTYPE_TXT; + new->datafile = a2; + new->checkfile = a2; + } + new->fpin = 0; + new->fpout = 0; + + if (new->checkfile) + if (stat(new->checkfile, &st) == -1) + return pstrcat(cmd->pool, "RewriteMap: map file or program not found:", new->checkfile, NULL); + + return NULL; +} + +static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *a1) +{ + if (cmd->path == NULL || dconf == NULL) + return "RewriteBase: only valid in per-directory config files"; + if (a1[0] != '/') + return "RewriteBase: argument is not a valid URL"; + if (a1[0] == '\0') + return "RewriteBase: empty URL not allowed"; + + dconf->baseurl = pstrdup(cmd->pool, a1); + + return NULL; +} + +static const char *cmd_rewritecond(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str) +{ + rewrite_server_conf *sconf; + rewritecond_entry *new; + regex_t *regexp; + char *a1; + char *a2; + char *a3; + char *cp; + const char *err; + int rc; + + sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module); + + /* make a new entry in the internal temporary rewrite rule list */ + if (cmd->path == NULL) /* is server command */ + new = push_array(sconf->rewriteconds); + else /* is per-directory command */ + new = push_array(dconf->rewriteconds); + + /* parse the argument line ourself */ + if (parseargline(str, &a1, &a2, &a3)) + return pstrcat(cmd->pool, "RewriteCond: bad argument line '", str, "'\n", NULL); + + /* arg1: the input string */ + new->input = pstrdup(cmd->pool, a1); + + /* arg3: optional flags field + (this have to be first parsed, because we need to + know if the regex should be compiled with ICASE!) */ + new->flags = CONDFLAG_NONE; + if (a3 != NULL) { + if ((err = cmd_rewritecond_parseflagfield(cmd->pool, new, a3)) != NULL) + return err; + } + + /* arg2: the pattern + try to compile the regexp to test if is ok */ + cp = a2; + if (cp[0] == '!') { + new->flags |= CONDFLAG_NOTMATCH; + cp++; + } + + /* now be careful: Under the POSIX regex library + we can compile the pattern for case-insensitive matching, + under the old V8 library we have to do it self via a hack */ + if (new->flags & CONDFLAG_NOCASE) + rc = ((regexp = pregcomp(cmd->pool, cp, REG_EXTENDED|REG_ICASE)) == NULL); + else + rc = ((regexp = pregcomp(cmd->pool, cp, REG_EXTENDED)) == NULL); + if (rc) + return pstrcat(cmd->pool, "RewriteCond: cannot compile regular expression '", a2, "'\n", NULL); + new->pattern = pstrdup(cmd->pool, cp); + new->regexp = regexp; + + return NULL; +} + +static const char *cmd_rewritecond_parseflagfield(pool *p, rewritecond_entry *cfg, char *str) +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *key; + char *val; + const char *err; + + if (str[0] != '[' || str[strlen(str)-1] != ']') + return pstrdup(p, "RewriteCond: bad flag delimiters"); + + cp = str+1; + str[strlen(str)-1] = ','; /* for simpler parsing */ + for ( ; *cp != '\0'; ) { + /* skip whitespaces */ + for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++) + ; + if (*cp == '\0') + break; + cp1 = cp; + if ((cp2 = strchr(cp, ',')) != NULL) { + cp = cp2+1; + for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--) + ; + *cp2 = '\0'; + if ((cp3 = strchr(cp1, '=')) != NULL) { + *cp3 = '\0'; + key = cp1; + val = cp3+1; + } + else { + key = cp1; + val = ""; + } + if ((err = cmd_rewritecond_setflag(p, cfg, key, val)) != NULL) + return err; + } + else + break; + } + + return NULL; +} + +static const char *cmd_rewritecond_setflag(pool *p, rewritecond_entry *cfg, char *key, char *val) +{ + if ( strcasecmp(key, "nocase") == 0 + || strcasecmp(key, "NC") == 0 ) { + cfg->flags |= CONDFLAG_NOCASE; + } + else if ( strcasecmp(key, "ornext") == 0 + || strcasecmp(key, "OR") == 0 ) { + cfg->flags |= CONDFLAG_ORNEXT; + } + else { + return pstrcat(p, "RewriteCond: unknown flag '", key, "'\n", NULL); + } + return NULL; +} + +/* NON static */ +const char *cmd_rewriterule(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str) +{ + rewrite_server_conf *sconf; + rewriterule_entry *new; + regex_t *regexp; + char *a1; + char *a2; + char *a3; + char *cp; + const char *err; + + sconf = (rewrite_server_conf *)get_module_config(cmd->server->module_config, &rewrite_module); + + /* make a new entry in the internal rewrite rule list */ + if (cmd->path == NULL) /* is server command */ + new = push_array(sconf->rewriterules); + else /* is per-directory command */ + new = push_array(dconf->rewriterules); + + /* parse the argument line ourself */ + if (parseargline(str, &a1, &a2, &a3)) + return pstrcat(cmd->pool, "RewriteRule: bad argument line '", str, "'\n", NULL); + + /* arg1: the pattern + try to compile the regexp to test if is ok */ + new->flags = RULEFLAG_NONE; + cp = a1; + if (cp[0] == '!') { + new->flags |= RULEFLAG_NOTMATCH; + cp++; + } + if ((regexp = pregcomp(cmd->pool, cp, REG_EXTENDED)) == NULL) + return pstrcat(cmd->pool, "RewriteRule: cannot compile regular expression '", a1, "'\n", NULL); + new->pattern = pstrdup(cmd->pool, cp); + new->regexp = regexp; + + /* arg2: the output string + replace the $<N> by \<n> which is needed by the currently + used Regular Expression library */ + new->output = pstrdup(cmd->pool, a2); + + /* arg3: optional flags field */ + new->forced_mimetype = NULL; + new->forced_responsecode = HTTP_MOVED_TEMPORARILY; + new->env[0] = NULL; + new->skip = 0; + if (a3 != NULL) { + if ((err = cmd_rewriterule_parseflagfield(cmd->pool, new, a3)) != NULL) + return err; + } + + /* now, if the server or per-dir config holds an + array of RewriteCond entries, we take it for us + and clear the array */ + if (cmd->path == NULL) { /* is server command */ + new->rewriteconds = sconf->rewriteconds; + sconf->rewriteconds = make_array(cmd->pool, 2, sizeof(rewritecond_entry)); + } + else { /* is per-directory command */ + new->rewriteconds = dconf->rewriteconds; + dconf->rewriteconds = make_array(cmd->pool, 2, sizeof(rewritecond_entry)); + } + + return NULL; +} + +static const char *cmd_rewriterule_parseflagfield(pool *p, rewriterule_entry *cfg, char *str) +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *key; + char *val; + const char *err; + + if (str[0] != '[' || str[strlen(str)-1] != ']') + return pstrdup(p, "RewriteRule: bad flag delimiters"); + + cp = str+1; + str[strlen(str)-1] = ','; /* for simpler parsing */ + for ( ; *cp != '\0'; ) { + /* skip whitespaces */ + for ( ; (*cp == ' ' || *cp == '\t') && *cp != '\0'; cp++) + ; + if (*cp == '\0') + break; + cp1 = cp; + if ((cp2 = strchr(cp, ',')) != NULL) { + cp = cp2+1; + for ( ; (*(cp2-1) == ' ' || *(cp2-1) == '\t'); cp2--) + ; + *cp2 = '\0'; + if ((cp3 = strchr(cp1, '=')) != NULL) { + *cp3 = '\0'; + key = cp1; + val = cp3+1; + } + else { + key = cp1; + val = ""; + } + if ((err = cmd_rewriterule_setflag(p, cfg, key, val)) != NULL) + return err; + } + else + break; + } + + return NULL; +} + +static const char *cmd_rewriterule_setflag(pool *p, rewriterule_entry *cfg, char *key, char *val) +{ + int status = 0; + int i; + + if ( strcasecmp(key, "redirect") == 0 + || strcasecmp(key, "R") == 0 ) { + cfg->flags |= RULEFLAG_FORCEREDIRECT; + if (strlen(val) > 0) { + if (strcasecmp(val, "permanent") == 0) + status = HTTP_MOVED_PERMANENTLY; + else if (strcasecmp(val, "temp") == 0) + status = HTTP_MOVED_TEMPORARILY; + else if (strcasecmp(val, "seeother") == 0) + status = HTTP_SEE_OTHER; + else if (isdigit(*val)) + status = atoi(val); + if (!is_HTTP_REDIRECT(status)) + return pstrdup(p, "RewriteRule: invalid HTTP response code for flag 'R'"); + cfg->forced_responsecode = status; + } + } + else if ( strcasecmp(key, "last") == 0 + || strcasecmp(key, "L") == 0 ) { + cfg->flags |= RULEFLAG_LASTRULE; + } + else if ( strcasecmp(key, "next") == 0 + || strcasecmp(key, "N") == 0 ) { + cfg->flags |= RULEFLAG_NEWROUND; + } + else if ( strcasecmp(key, "chain") == 0 + || strcasecmp(key, "C") == 0 ) { + cfg->flags |= RULEFLAG_CHAIN; + } + else if ( strcasecmp(key, "type") == 0 + || strcasecmp(key, "T") == 0 ) { + cfg->forced_mimetype = pstrdup(p, val); + } + else if ( strcasecmp(key, "env") == 0 + || strcasecmp(key, "E") == 0 ) { + for (i = 0; (cfg->env[i] != NULL) && (i < MAX_ENV_FLAGS); i++) + ; + if (i < MAX_ENV_FLAGS) { + cfg->env[i] = pstrdup(p, val); + cfg->env[i+1] = NULL; + } + else + return pstrdup(p, "RewriteRule: to much environment flags 'E'"); + } + else if ( strcasecmp(key, "nosubreq") == 0 + || strcasecmp(key, "NS") == 0 ) { + cfg->flags |= RULEFLAG_IGNOREONSUBREQ; + } + else if ( strcasecmp(key, "proxy") == 0 + || strcasecmp(key, "P") == 0 ) { + cfg->flags |= RULEFLAG_PROXY; + } + else if ( strcasecmp(key, "passthrough") == 0 + || strcasecmp(key, "PT") == 0 ) { + cfg->flags |= RULEFLAG_PASSTHROUGH; + } + else if ( strcasecmp(key, "skip") == 0 + || strcasecmp(key, "S") == 0 ) { + cfg->skip = atoi(val); + } + else if ( strcasecmp(key, "forbidden") == 0 + || strcasecmp(key, "F") == 0 ) { + cfg->flags |= RULEFLAG_FORBIDDEN; + } + else if ( strcasecmp(key, "gone") == 0 + || strcasecmp(key, "G") == 0 ) { + cfg->flags |= RULEFLAG_GONE; + } + else if ( strcasecmp(key, "qsappend") == 0 + || strcasecmp(key, "QSA") == 0 ) { + cfg->flags |= RULEFLAG_QSAPPEND; + } + else { + return pstrcat(p, "RewriteRule: unknown flag '", key, "'\n", NULL); + } + return NULL; +} + + +/* +** +** module initialisation +** [called from read_config() after all +** config commands were already called] +** +*/ + +static void init_module(server_rec *s, pool *p) +{ + /* step through the servers and + - open eachs rewriting logfile + - open the RewriteMap prg:xxx programs */ + for (; s; s = s->next) { + open_rewritelog(s, p); + run_rewritemap_programs(s, p); + } + + /* create the lookup cache */ + cachep = init_cache(p); + + /* check if proxy module is available */ + proxy_available = is_proxy_available(s); + + /* precompile a static pattern + for the txt mapfile parsing */ + lookup_map_txtfile_regexp = pregcomp(p, MAPFILE_PATTERN, REG_EXTENDED); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | runtime hooks +** | | +** +-------------------------------------------------------+ +*/ + + +/* +** +** URI-to-filename hook +** +** [used for the rewriting engine triggered by +** the per-server 'RewriteRule' directives] +** +*/ + +static int hook_uri2file(request_rec *r) +{ + void *sconf; + rewrite_server_conf *conf; + char *var; + char *thisserver, *thisport, *thisurl; + char buf[512]; + char docroot[512]; + char *cp, *cp2; + struct stat finfo; + int n; + int l; + + /* + * retrieve the config structures + */ + sconf = r->server->module_config; + conf = (rewrite_server_conf *)get_module_config(sconf, &rewrite_module); + + /* + * only do something under runtime if the engine is really enabled, + * else return immediately! + */ + if (conf->state == ENGINE_DISABLED) + return DECLINED; + + /* + * add the SCRIPT_URL variable to the env. this is a bit complicated + * due to the fact that apache uses subrequests and internal redirects + */ + + if (r->main == NULL) { + var = pstrcat(r->pool, "REDIRECT_", ENVVAR_SCRIPT_URL, NULL); + var = table_get(r->subprocess_env, var); + if (var == NULL) + table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, pstrdup(r->pool, r->uri)); + else + table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, pstrdup(r->pool, var)); + } + else { + var = table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL); + table_set(r->subprocess_env, ENVVAR_SCRIPT_URL, pstrdup(r->pool, var)); + } + + /* + * create the SCRIPT_URI variable for the env + */ + + /* add the canonical URI of this URL */ + thisserver = r->server->server_hostname; +#ifdef APACHE_SSL + if (((!r->connection->client->ssl) && (r->server->port == DEFAULT_PORT)) || + ((r->connection->client->ssl) && (r->server->port == 443))) +#else + if (r->server->port == DEFAULT_PORT) +#endif + thisport = ""; + else { + ap_snprintf(buf, sizeof(buf), ":%u", r->server->port); + thisport = pstrdup(r->pool, buf); + } + thisurl = table_get(r->subprocess_env, ENVVAR_SCRIPT_URL); + + /* set the variable */ +#ifdef APACHE_SSL + var = pstrcat(r->pool, http_method(r), "://", thisserver, thisport, thisurl, NULL); +#else + var = pstrcat(r->pool, "http://", thisserver, thisport, thisurl, NULL); +#endif + table_set(r->subprocess_env, ENVVAR_SCRIPT_URI, pstrdup(r->pool, var)); + + + /* if filename was not initially set, + we start with the requested URI */ + if (r->filename == NULL) { + r->filename = pstrdup(r->pool, r->uri); + rewritelog(r, 2, "init rewrite engine with requested uri %s", r->filename); + } + + /* + * now apply the rules ... + */ + if (apply_rewrite_list(r, conf->rewriterules, NULL)) { + + if (strlen(r->filename) > 6 && + strncmp(r->filename, "proxy:", 6) == 0) { + /* it should be go on as an internal proxy request */ + + /* check if the proxy module is enabled, so + we can actually use it! */ + if (!proxy_available) { + log_reason("attempt to make remote request from mod_rewrite " + "without proxy enabled", r->filename, r); + return FORBIDDEN; + } + + /* make sure the QUERY_STRING and + PATH_INFO parts get incorporated */ + r->filename = pstrcat(r->pool, r->filename, + r->path_info ? r->path_info : "", + r->args ? "?" : NULL, r->args, + NULL); + + /* now make sure the request gets handled by the + proxy handler */ + r->proxyreq = 1; + r->handler = "proxy-server"; + + rewritelog(r, 1, "go-ahead with proxy request %s [OK]", r->filename); + return OK; + } + else if ( (strlen(r->filename) > 7 && + strncasecmp(r->filename, "http://", 7) == 0) + || (strlen(r->filename) > 8 && + strncasecmp(r->filename, "https://", 8) == 0) + || (strlen(r->filename) > 9 && + strncasecmp(r->filename, "gopher://", 9) == 0) + || (strlen(r->filename) > 6 && + strncasecmp(r->filename, "ftp://", 6) == 0) ) { + /* it was finally rewritten to a remote URL */ + + /* skip 'scheme:' */ + for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++) + ; + /* skip '://' */ + cp += 3; + /* skip host part */ + for ( ; *cp != '/' && *cp != '\0'; cp++) + ; + if (*cp != '\0') { + rewritelog(r, 1, "escaping %s for redirect", r->filename); + cp2 = escape_uri(r->pool, cp); + *cp = '\0'; + r->filename = pstrcat(r->pool, r->filename, cp2, NULL); + } + + /* append the QUERY_STRING part */ + if (r->args != NULL) + r->filename = pstrcat(r->pool, r->filename, "?", r->args, NULL); + + /* determine HTTP redirect response code */ + if (is_HTTP_REDIRECT(r->status)) { + n = r->status; + r->status = HTTP_OK; /* make Apache kernel happy */ + } + else + n = REDIRECT; + + /* now do the redirection */ + table_set(r->headers_out, "Location", r->filename); + rewritelog(r, 1, "redirect to %s [REDIRECT/%d]", r->filename, n); + return n; + } + else if (strlen(r->filename) > 10 && + strncmp(r->filename, "forbidden:", 10) == 0) { + /* This URLs is forced to be forbidden for the requester */ + return FORBIDDEN; + } + else if (strlen(r->filename) > 5 && + strncmp(r->filename, "gone:", 5) == 0) { + /* This URLs is forced to be gone */ + return HTTP_GONE; + } + else if (strlen(r->filename) > 12 && + strncmp(r->filename, "passthrough:", 12) == 0) { + /* Hack because of underpowered API: passing the current + rewritten filename through to other URL-to-filename handlers + just as it were the requested URL. This is to enable + post-processing by mod_alias, etc. which always act on + r->uri! The difference here is: We do not try to + add the document root */ + r->uri = pstrdup(r->pool, r->filename+12); + return DECLINED; + } + else { + /* it was finally rewritten to a local path */ + + /* expand "/~user" prefix */ + r->filename = expand_tildepaths(r, r->filename); + + rewritelog(r, 2, "local path result: %s", r->filename); + + /* the filename has to start with a slash! */ + if (r->filename[0] != '/') + return BAD_REQUEST; + + /* if there is no valid prefix, we have + to emulate the translator from the core and + prefix the filename with document_root + + NOTICE: + We cannot leave out the prefix_stat because + - when we always prefix with document_root + then no absolute path can be created, e.g. via + emulating a ScriptAlias directive, etc. + - when we always NOT prefix with document_root + then the files under document_root have to + be references directly and document_root + gets never used and will be a dummy parameter - + this is also bad + + BUT: + Under real Unix systems this is no problem, + because we only do stat() on the first directory + and this gets cached by the kernel for along time! + */ + n = prefix_stat(r->filename, &finfo); + if (n == 0) { + if ((cp = document_root(r)) != NULL) { + strncpy(docroot, cp, sizeof(docroot)-1); + EOS_PARANOIA(docroot); + + /* always NOT have a trailing slash */ + l = strlen(docroot); + if (docroot[l-1] == '/') { + docroot[l-1] = '\0'; + } + if (r->server->path && !strncmp(r->filename, r->server->path, r->server->pathlen)) + r->filename = pstrcat(r->pool, docroot, (r->filename + r->server->pathlen), NULL); + else + r->filename = pstrcat(r->pool, docroot, r->filename, NULL); + rewritelog(r, 2, "prefixed with document_root to %s", r->filename); + } + } + + rewritelog(r, 1, "go-ahead with %s [OK]", r->filename); + return OK; + } + } + else { + rewritelog(r, 1, "pass through %s", r->filename); + return DECLINED; + } +} + + +/* +** +** MIME-type hook +** +** [used to support the forced-MIME-type feature] +** +*/ + +static int hook_mimetype(request_rec *r) +{ + char *t; + + /* now check if we have to force a MIME-type */ + t = table_get(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR); + if (t == NULL) + return DECLINED; + else { + rewritelog(r, 1, "force filename %s to have MIME-type '%s'", r->filename, t); + r->content_type = t; + return OK; + } +} + + +/* +** +** Fixup hook +** +** [used for the rewriting engine triggered by +** the per-directory 'RewriteRule' directives] +** +*/ + +static int hook_fixup(request_rec *r) +{ + rewrite_perdir_conf *dconf; + char *cp; + char *cp2; + char *prefix; + int l; + int n; + char *ofilename; + + dconf = (rewrite_perdir_conf *)get_module_config(r->per_dir_config, &rewrite_module); + + /* if there is no per-dir config we return immediately */ + if (dconf == NULL) + return DECLINED; + + /* we shouldn't do anything in subrequests */ + if (r->main != NULL) + return DECLINED; + + /* if there are no real (i.e. no RewriteRule directives!) + per-dir config of us, we return also immediately */ + if (dconf->directory == NULL) + return DECLINED; + + /* + * only do something under runtime if the engine is really enabled, + * for this directory, else return immediately! + */ + if (!(allow_options(r) & (OPT_SYM_LINKS | OPT_SYM_OWNER))) { + /* FollowSymLinks is mandatory! */ + log_reason("Options FollowSymLinks or SymLinksIfOwnerMatch is off which implies that RewriteRule directive is forbidden", r->filename, r); + return FORBIDDEN; + } + else { + /* FollowSymLinks is given, but the user can + still turn off the rewriting engine */ + if (dconf->state == ENGINE_DISABLED) + return DECLINED; + } + + /* + * remember the current filename before rewriting for later check + * to prevent deadlooping because of internal redirects + * on final URL/filename which can be equal to the inital one. + */ + ofilename = r->filename; + + /* + * now apply the rules ... + */ + if (apply_rewrite_list(r, dconf->rewriterules, dconf->directory)) { + + if (strlen(r->filename) > 6 && + strncmp(r->filename, "proxy:", 6) == 0) { + /* it should go on as an internal proxy request */ + + /* make sure the QUERY_STRING and + PATH_INFO parts get incorporated */ + r->filename = pstrcat(r->pool, r->filename, + /* r->path_info was already + appended by the rewriting engine + because of the per-dir context! */ + r->args ? "?" : NULL, r->args, + NULL); + + /* now make sure the request gets handled by the + proxy handler */ + r->proxyreq = 1; + r->handler = "proxy-server"; + + rewritelog(r, 1, "[per-dir %s] go-ahead with proxy request %s [OK]", dconf->directory, r->filename); + return OK; + } + else if ( (strlen(r->filename) > 7 && + strncasecmp(r->filename, "http://", 7) == 0) + || (strlen(r->filename) > 8 && + strncasecmp(r->filename, "https://", 8) == 0) + || (strlen(r->filename) > 9 && + strncasecmp(r->filename, "gopher://", 9) == 0) + || (strlen(r->filename) > 6 && + strncasecmp(r->filename, "ftp://", 6) == 0) ) { + /* it was finally rewritten to a remote URL */ + + /* because we are in a per-dir context + first try to replace the directory with its base-URL + if there is a base-URL available */ + if (dconf->baseurl != NULL) { + /* skip 'scheme:' */ + for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++) + ; + /* skip '://' */ + cp += 3; + if ((cp = strchr(cp, '/')) != NULL) { + rewritelog(r, 2, "[per-dir %s] trying to replace prefix %s with %s", dconf->directory, dconf->directory, dconf->baseurl); + cp2 = subst_prefix_path(r, cp, dconf->directory, dconf->baseurl); + if (strcmp(cp2, cp) != 0) { + *cp = '\0'; + r->filename = pstrcat(r->pool, r->filename, cp2, NULL); + } + } + } + + /* now prepare the redirect... */ + + /* skip 'scheme:' */ + for (cp = r->filename; *cp != ':' && *cp != '\0'; cp++) + ; + /* skip '://' */ + cp += 3; + /* skip host part */ + for ( ; *cp != '/' && *cp != '\0'; cp++) + ; + if (*cp != '\0') { + rewritelog(r, 1, "[per-dir %s] escaping %s for redirect", dconf->directory, r->filename); + cp2 = escape_uri(r->pool, cp); + *cp = '\0'; + r->filename = pstrcat(r->pool, r->filename, cp2, NULL); + } + + /* append the QUERY_STRING part */ + if (r->args != NULL) + r->filename = pstrcat(r->pool, r->filename, "?", r->args, NULL); + + /* determine HTTP redirect response code */ + if (is_HTTP_REDIRECT(r->status)) { + n = r->status; + r->status = HTTP_OK; /* make Apache kernel happy */ + } + else + n = REDIRECT; + + /* now do the redirection */ + table_set(r->headers_out, "Location", r->filename); + rewritelog(r, 1, "[per-dir %s] redirect to %s [REDIRECT/%d]", dconf->directory, r->filename, n); + return n; + } + else if (strlen(r->filename) > 10 && + strncmp(r->filename, "forbidden:", 10) == 0) { + /* This URLs is forced to be forbidden for the requester */ + return FORBIDDEN; + } + else if (strlen(r->filename) > 5 && + strncmp(r->filename, "gone:", 5) == 0) { + /* This URLs is forced to be gone */ + return HTTP_GONE; + } + else { + /* it was finally rewritten to a local path */ + + /* if someone used the PASSTHROUGH flag in per-dir + context we just ignore it. It is only useful + in per-server context */ + if (strlen(r->filename) > 12 && + strncmp(r->filename, "passthrough:", 12) == 0) { + r->filename = pstrdup(r->pool, r->filename+12); + } + + /* the filename has to start with a slash! */ + if (r->filename[0] != '/') + return BAD_REQUEST; + + /* Check for deadlooping: + * At this point we KNOW that at least one rewriting + * rule was applied, but when the resulting URL is + * the same as the initial URL, we are not allowed to + * use the following internal redirection stuff because + * this would lead to a deadloop. + */ + if (strcmp(r->filename, ofilename) == 0) { + rewritelog(r, 1, "[per-dir %s] initial URL equal rewritten URL: %s [IGNORING REWRITE]", dconf->directory, r->filename); + return OK; + } + + /* if there is a valid base-URL then substitute + the per-dir prefix with this base-URL if the + current filename still is inside this per-dir + context. If not then treat the result as a + plain URL */ + if (dconf->baseurl != NULL) { + rewritelog(r, 2, "[per-dir %s] trying to replace prefix %s with %s", dconf->directory, dconf->directory, dconf->baseurl); + r->filename = subst_prefix_path(r, r->filename, dconf->directory, dconf->baseurl); + } + else { + /* if no explicit base-URL exists we assume + that the directory prefix is also a valid URL + for this webserver and only try to remove the + document_root if it is prefix */ + + if ((cp = document_root(r)) != NULL) { + prefix = pstrdup(r->pool, cp); + /* always NOT have a trailing slash */ + l = strlen(prefix); + if (prefix[l-1] == '/') { + prefix[l-1] = '\0'; + l--; + } + if (strncmp(r->filename, prefix, l) == 0) { + rewritelog(r, 2, "[per-dir %s] strip document_root prefix: %s -> %s", dconf->directory, r->filename, r->filename+l); + r->filename = pstrdup(r->pool, r->filename+l); + } + } + } + + /* now initiate the internal redirect */ + rewritelog(r, 1, "[per-dir %s] internal redirect with %s [INTERNAL REDIRECT]", dconf->directory, r->filename); + r->filename = pstrcat(r->pool, "redirect:", r->filename, NULL); + r->handler = "redirect-handler"; + return OK; + } + } + else { + rewritelog(r, 1, "[per-dir %s] pass through %s", dconf->directory, r->filename); + return DECLINED; + } +} + + +/* +** +** Content-Handlers +** +** [used for redirect support] +** +*/ + +static int handler_redirect(request_rec *r) +{ + /* just make sure that we are really meant! */ + if (strncmp(r->filename, "redirect:", 9) != 0) + return DECLINED; + + /* now do the internal redirect */ + internal_redirect(pstrcat(r->pool, r->filename+9, + r->args ? "?" : NULL, r->args, NULL), r); + + /* and return gracefully */ + return OK; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | rewriting engine +** | | +** +-------------------------------------------------------+ +*/ + + +static int apply_rewrite_list(request_rec *r, array_header *rewriterules, char *perdir) +{ + rewriterule_entry *entries; + rewriterule_entry *p; + int i; + int changed; + int rc; + int s; + + entries = (rewriterule_entry *)rewriterules->elts; + changed = 0; + loop: + for (i = 0; i < rewriterules->nelts; i++) { + p = &entries[i]; + + /* ignore this rule on subrequests if we are explicitly asked to do so + or this is a proxy throughput or a forced redirect rule */ + if (r->main != NULL && + (p->flags & RULEFLAG_IGNOREONSUBREQ || + p->flags & RULEFLAG_PROXY || + p->flags & RULEFLAG_FORCEREDIRECT )) + continue; + + /* apply the current rule */ + rc = apply_rewrite_rule(r, p, perdir); + if (rc) { + if (rc != 2) /* not a match-only rule */ + changed = 1; + if (p->flags & RULEFLAG_PASSTHROUGH) { + rewritelog(r, 2, "forcing '%s' to get passed through to next URI-to-filename handler", r->filename); + r->filename = pstrcat(r->pool, "passthrough:", r->filename, NULL); + changed = 1; + break; + } + if (p->flags & RULEFLAG_FORBIDDEN) { + rewritelog(r, 2, "forcing '%s' to be forbidden", r->filename); + r->filename = pstrcat(r->pool, "forbidden:", r->filename, NULL); + changed = 1; + break; + } + if (p->flags & RULEFLAG_GONE) { + rewritelog(r, 2, "forcing '%s' to be gone", r->filename); + r->filename = pstrcat(r->pool, "gone:", r->filename, NULL); + changed = 1; + break; + } + if (p->flags & RULEFLAG_PROXY) + break; + if (p->flags & RULEFLAG_LASTRULE) + break; + if (p->flags & RULEFLAG_NEWROUND) + goto loop; + + /* if we are forced to skip N next rules, do it now */ + if (p->skip > 0) { + s = p->skip; + while ( i < rewriterules->nelts + && s > 0) { + i++; + p = &entries[i]; + s--; + } + } + } + else { + /* if current rule is chained with next rule(s), + skip all this next rule(s) */ + while ( i < rewriterules->nelts + && p->flags & RULEFLAG_CHAIN) { + i++; + p = &entries[i]; + } + } + } + return changed; +} + +static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, char *perdir) +{ + char *uri; + char *output; + int flags; + char newuri[MAX_STRING_LEN]; + char env[MAX_STRING_LEN]; + char port[32]; + char env2[MAX_STRING_LEN]; + regex_t *regexp; + regmatch_t regmatch[10]; + int rc; + int prefixstrip; + int i; + int failed; + array_header *rewriteconds; + rewritecond_entry *conds; + rewritecond_entry *c; + + uri = r->filename; + regexp = p->regexp; + output = p->output; + flags = p->flags; + + if (perdir != NULL && r->path_info != NULL && r->path_info[0] != '\0') { + rewritelog(r, 3, "[per-dir %s] add path-info postfix: %s -> %s%s", perdir, uri, uri, r->path_info); + uri = pstrcat(r->pool, uri, r->path_info, NULL); + } + + prefixstrip = 0; + if (perdir != NULL) { + /* this is a per-directory match */ + if ( strlen(uri) >= strlen(perdir) + && strncmp(uri, perdir, strlen(perdir)) == 0) { + rewritelog(r, 3, "[per-dir %s] strip per-dir prefix: %s -> %s", perdir, uri, uri+strlen(perdir)); + uri = uri+strlen(perdir); + prefixstrip = 1; + } + } + + if (perdir != NULL) + rewritelog(r, 3, "[per-dir %s] applying pattern '%s' to uri '%s'", perdir, p->pattern, uri); + + rc = (regexec(regexp, uri, regexp->re_nsub+1, regmatch, 0) == 0); /* try to match the pattern */ + if (( rc && !(p->flags & RULEFLAG_NOTMATCH)) || + (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) { + + /* ok, the pattern matched, but we now additionally have to check + for any preconditions which have to be also true. We do this + at this very late stage to avoid unnessesary checks which + slow down the rewriting engine!! */ + rewriteconds = p->rewriteconds; + conds = (rewritecond_entry *)rewriteconds->elts; + failed = 0; + for (i = 0; i < rewriteconds->nelts; i++) { + c = &conds[i]; + rc = apply_rewrite_cond(r, c, perdir); + if (c->flags & CONDFLAG_ORNEXT) { + /* there is a "or" flag */ + if (rc == 0) { + /* one cond is false, but another can be true... */ + continue; + } + else { + /* one true cond is enough, so skip the other conds + of the "ornext" chained conds */ + while ( i < rewriteconds->nelts + && c->flags & CONDFLAG_ORNEXT) { + i++; + c = &conds[i]; + } + continue; + } + } + else { + /* no "or" flag, so a single fail means total fail */ + if (rc == 0) { /* failed */ + failed = 1; + break; + } + } + } + if (failed) + return 0; /* if any condition fails this complete rule fails */ + + /* if this is a pure matching rule we return immediately */ + if (strcmp(output, "-") == 0) { + /* but before we set the env variables... */ + for (i = 0; p->env[i] != NULL; i++) { + strncpy(env2, p->env[i], sizeof(env2)-1); + EOS_PARANOIA(env2); + strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */ + EOS_PARANOIA(env); + add_env_variable(r, env); + } + return 2; + } + + /* if this is a forced proxy request ... */ + if (p->flags & RULEFLAG_PROXY) { + if (p->flags & RULEFLAG_NOTMATCH) { + output = pstrcat(r->pool, "proxy:", output, NULL); + strncpy(newuri, output, sizeof(newuri)-1); + EOS_PARANOIA(newuri); + expand_variables_inbuffer(r, newuri, sizeof(newuri));/* expand %{...} */ + expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */ + } + else { + output = pstrcat(r->pool, "proxy:", output, NULL); + strncpy(newuri, pregsub(r->pool, output, uri, regexp->re_nsub+1, regmatch), sizeof(newuri)-1); /* substitute in output */ + EOS_PARANOIA(newuri); + for (i = 0; p->env[i] != NULL; i++) { + strncpy(env2, p->env[i], sizeof(env2)-1); + EOS_PARANOIA(env2); + strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */ + EOS_PARANOIA(env); + add_env_variable(r, env); + } + expand_variables_inbuffer(r, newuri, sizeof(newuri)); /* expand %{...} */ + expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */ + } + if (perdir == NULL) + rewritelog(r, 2, "rewrite %s -> %s", r->filename, newuri); + else + rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, r->filename, newuri); + r->filename = pstrdup(r->pool, newuri); + return 1; + } + + /* if this is an implicit redirect in a per-dir rule */ + i = strlen(output); + if (perdir != NULL + && ( (i > 7 && strncasecmp(output, "http://", 7) == 0) + || (i > 8 && strncasecmp(output, "https://", 8) == 0) + || (i > 9 && strncasecmp(output, "gopher://", 9) == 0) + || (i > 6 && strncasecmp(output, "ftp://", 6) == 0) ) ) { + if (p->flags & RULEFLAG_NOTMATCH) { + strncpy(newuri, output, sizeof(newuri)-1); + EOS_PARANOIA(newuri); + expand_variables_inbuffer(r, newuri, sizeof(newuri));/* expand %{...} */ + expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */ + } + else { + strncpy(newuri, pregsub(r->pool, output, uri, regexp->re_nsub+1, regmatch), sizeof(newuri)-1); /* substitute in output */ + EOS_PARANOIA(newuri); + for (i = 0; p->env[i] != NULL; i++) { + strncpy(env2, p->env[i], sizeof(env2)-1); + EOS_PARANOIA(env2); + strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */ + EOS_PARANOIA(env); + add_env_variable(r, env); + } + expand_variables_inbuffer(r, newuri, sizeof(newuri));/* expand %{...} */ + expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */ + } + rewritelog(r, 2, "[per-dir %s] redirect %s -> %s", perdir, r->filename, newuri); + r->filename = pstrdup(r->pool, newuri); + splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND); + r->status = p->forced_responsecode; + return 1; + } + + /* add again the previously stripped perdir prefix if the new + URI is not a new one (i.e. prefixed by a slash which means + that it is not for this per-dir context) */ + if (prefixstrip && output[0] != '/') { + rewritelog(r, 3, "[per-dir %s] add per-dir prefix: %s -> %s%s", perdir, output, perdir, output); + output = pstrcat(r->pool, perdir, output, NULL); + } + + if (p->flags & RULEFLAG_NOTMATCH) { + /* just overtake the URI */ + strncpy(newuri, output, sizeof(newuri)-1); + EOS_PARANOIA(newuri); + } + else { + /* substitute in output */ + strncpy(newuri, pregsub(r->pool, output, uri, regexp->re_nsub+1, regmatch), sizeof(newuri)-1); /* substitute in output */ + EOS_PARANOIA(newuri); + for (i = 0; p->env[i] != NULL; i++) { + strncpy(env2, p->env[i], sizeof(env2)-1); + EOS_PARANOIA(env2); + strncpy(env, pregsub(r->pool, env2, uri, regexp->re_nsub+1, regmatch), sizeof(env)-1); /* substitute in output */ + EOS_PARANOIA(env); + add_env_variable(r, env); + } + } + expand_variables_inbuffer(r, newuri, sizeof(newuri)); /* expand %{...} */ + expand_map_lookups(r, newuri, sizeof(newuri)); /* expand ${...} */ + + if (perdir == NULL) + rewritelog(r, 2, "rewrite %s -> %s", uri, newuri); + else + rewritelog(r, 2, "[per-dir %s] rewrite %s -> %s", perdir, uri, newuri); + + r->filename = pstrdup(r->pool, newuri); + + /* reduce http[s]://<ourhost>[:<port>] */ + reduce_uri(r); + + /* split out on-the-fly generated QUERY_STRING '....?xxxxx&xxxx...' */ + splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND); + + /* if a MIME-type should be later forced for this URL, then remember this */ + if (p->forced_mimetype != NULL) { + table_set(r->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR, p->forced_mimetype); + if (perdir == NULL) + rewritelog(r, 2, "remember %s to have MIME-type '%s'", r->filename, p->forced_mimetype); + else + rewritelog(r, 2, "[per-dir %s] remember %s to have MIME-type '%s'", perdir, r->filename, p->forced_mimetype); + } + + /* if we are forced to do a explicit redirect by [R] flag + and the current URL still is not a fully qualified one we + finally prefix it with http[s]://<ourname> explicitly */ + if (flags & RULEFLAG_FORCEREDIRECT) { + r->status = p->forced_responsecode; + if ( !(strlen(r->filename) > 7 && + strncasecmp(r->filename, "http://", 7) == 0) + && !(strlen(r->filename) > 8 && + strncasecmp(r->filename, "https://", 8) == 0) + && !(strlen(r->filename) > 9 && + strncasecmp(r->filename, "gopher://", 9) == 0) + && !(strlen(r->filename) > 6 && + strncasecmp(r->filename, "ftp://", 6) == 0) ) { + +#ifdef APACHE_SSL + if ((!r->connection->client->ssl && r->server->port == DEFAULT_PORT) || + ( r->connection->client->ssl && r->server->port == 443) ) +#else + if (r->server->port == DEFAULT_PORT) +#endif + port[0] = '\0'; + else + ap_snprintf(port, sizeof(port), ":%u", r->server->port); + if (r->filename[0] == '/') +#ifdef APACHE_SSL + ap_snprintf(newuri, sizeof(newuri), "%s://%s%s%s", http_method(r), r->server->server_hostname, port, r->filename); +#else + ap_snprintf(newuri, sizeof(newuri), "http://%s%s%s", r->server->server_hostname, port, r->filename); +#endif + else +#ifdef APACHE_SSL + ap_snprintf(newuri, sizeof(newuri), "%s://%s%s/%s", http_method(r), r->server->server_hostname, port, r->filename); +#else + ap_snprintf(newuri, sizeof(newuri), "http://%s%s/%s", r->server->server_hostname, port, r->filename); +#endif + if (perdir == NULL) + rewritelog(r, 2, "prepare forced redirect %s -> %s", r->filename, newuri); + else + rewritelog(r, 2, "[per-dir %s] prepare forced redirect %s -> %s", perdir, r->filename, newuri); + r->filename = pstrdup(r->pool, newuri); + return 1; + } + } + + return 1; + } + return 0; +} + +static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, char *perdir) +{ + char *input; + int rc; + struct stat sb; + request_rec *rsub; + + /* first, we have to expand the input string to match */ + input = expand_variables(r, p->input); + + rc = 0; + if (strcmp(p->pattern, "-f") == 0) { + if (stat(input, &sb) == 0) + if (S_ISREG(sb.st_mode)) + rc = 1; + } + else if (strcmp(p->pattern, "-s") == 0) { + if (stat(input, &sb) == 0) + if (S_ISREG(sb.st_mode) && sb.st_size > 0) + rc = 1; + } + else if (strcmp(p->pattern, "-l") == 0) { +#ifndef __EMX__ +/* OS/2 dosen't support links. */ + if (stat(input, &sb) == 0) + if (S_ISLNK(sb.st_mode)) + rc = 1; +#endif + } + else if (strcmp(p->pattern, "-d") == 0) { + if (stat(input, &sb) == 0) + if (S_ISDIR(sb.st_mode)) + rc = 1; + } + else if (strcmp(p->pattern, "-U") == 0) { + /* avoid infinite subrequest recursion */ + if (strlen(input) > 0 /* nonempty path, and */ + && ( r->main == NULL /* - either not in a subrequest */ + || ( r->main->uri != NULL /* - or in a subrequest...*/ + && r->uri != NULL /* ...and then URIs aren't NULL... */ + /* ...and sub and main URIs differ */ + && strcmp(r->main->uri, r->uri) != 0) ) ) { + + /* run a URI-based subrequest */ + rsub = sub_req_lookup_uri(input, r); + + /* URI exists for any result up to 3xx, redirects allowed */ + if (rsub->status < 400) + rc = 1; + + /* log it */ + rewritelog(r, 5, "RewriteCond URI (-U) check: path=%s -> status=%d", input, rsub->status); + + /* cleanup by destroying the subrequest */ + destroy_sub_req(rsub); + } + } + else if (strcmp(p->pattern, "-F") == 0) { + /* avoid infinite subrequest recursion */ + if (strlen(input) > 0 /* nonempty path, and */ + && ( r->main == NULL /* - either not in a subrequest */ + || ( r->main->uri != NULL /* - or in a subrequest...*/ + && r->uri != NULL /* ...and then URIs aren't NULL... */ + /* ...and sub and main URIs differ */ + && strcmp(r->main->uri, r->uri) != 0) ) ) { + + /* process a file-based subrequest: + this differs from -U in that no path translation is done. */ + rsub = sub_req_lookup_file(input, r); + + /* file exists for any result up to 2xx, no redirects */ + if (rsub->status < 300 && + /* double-check that file exists since default result is 200 */ + stat(rsub->filename, &sb) == 0) + rc = 1; + + /* log it */ + rewritelog(r, 5, "RewriteCond file (-F) check: path=%s -> file=%s status=%d", input, rsub->filename, rsub->status); + + /* cleanup by destroying the subrequest */ + destroy_sub_req(rsub); + } + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '>') { + rc = (compare_lexicography(input, p->pattern+1) == 1 ? 1 : 0); + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '<') { + rc = (compare_lexicography(input, p->pattern+1) == -1 ? 1 : 0); + } + else if (strlen(p->pattern) > 1 && *(p->pattern) == '=') { + rc = (strcmp(input, p->pattern+1) == 0 ? 1 : 0); + } + else { + /* it is really a regexp pattern, so apply it */ + rc = (regexec(p->regexp, input, 0, NULL, 0) == 0); + } + + /* if this is a non-matching regexp, just negate the result */ + if (p->flags & CONDFLAG_NOTMATCH) + rc = !rc; + + rewritelog(r, 4, "RewriteCond: input='%s' pattern='%s%s' => %s", + input, (p->flags & CONDFLAG_NOTMATCH ? "!" : ""), + p->pattern, rc ? "matched" : "not-matched"); + + /* end just return the result */ + return rc; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | URL transformation functions +** | | +** +-------------------------------------------------------+ +*/ + + +/* +** +** split out a QUERY_STRING part from +** the current URI string +** +*/ + +static void splitout_queryargs(request_rec *r, int qsappend) +{ + char *q; + char *olduri; + + q = strchr(r->filename, '?'); + if (q != NULL) { + olduri = pstrdup(r->pool, r->filename); + *q++ = '\0'; + if (qsappend) + r->args = pstrcat(r->pool, q, "&", r->args, NULL); + else + r->args = pstrdup(r->pool, q); + if (strlen(r->args) == 0) { + r->args = NULL; + rewritelog(r, 3, "split uri=%s -> uri=%s, args=<none>", olduri, r->filename); + } + else { + if (r->args[strlen(r->args)-1] == '&') + r->args[strlen(r->args)-1] = '\0'; + rewritelog(r, 3, "split uri=%s -> uri=%s, args=%s", olduri, r->filename, r->args); + } + } + return; +} + + +/* +** +** strip 'http[s]://ourhost/' from URI +** +*/ + +static void reduce_uri(request_rec *r) +{ + char *cp; + unsigned short port; + char *portp; + char *hostp; + char *url; + char c; + char host[LONG_STRING_LEN]; + char buf[MAX_STRING_LEN]; + char *olduri; + +#ifdef APACHE_SSL + if ( (!r->connection->client->ssl && + strncasecmp(r->filename, "http://", 7) == 0) + || (r->connection->client->ssl && + strncasecmp(r->filename, "https://", 8) == 0)) { +#else + if (strncasecmp(r->filename, "http://", 7) == 0) { +#endif + /* there was really a rewrite to a remote path */ + + olduri = pstrdup(r->pool, r->filename); /* save for logging */ + + /* cut the hostname and port out of the URI */ +#ifdef APACHE_SSL + strncpy(buf, r->filename+strlen(http_method(r))+3, sizeof(buf)-1); +#else + strncpy(buf, r->filename+7, sizeof(buf)-1); +#endif + EOS_PARANOIA(buf); + hostp = buf; + for (cp = hostp; *cp != '\0' && *cp != '/' && *cp != ':'; cp++) + ; + if (*cp == ':') { + /* set host */ + *cp++ = '\0'; + strncpy(host, hostp, sizeof(host)-1); + EOS_PARANOIA(host); + /* set port */ + portp = cp; + for (; *cp != '\0' && *cp != '/'; cp++) + ; + c = *cp; + *cp = '\0'; + port = atoi(portp); + *cp = c; + /* set remaining url */ + url = cp; + } + else if (*cp == '/') { + /* set host */ + *cp = '\0'; + strncpy(host, hostp, sizeof(host)-1); + EOS_PARANOIA(host); + *cp = '/'; + /* set port */ + port = DEFAULT_PORT; + /* set remaining url */ + url = cp; + } + else { + /* set host */ + strncpy(host, hostp, sizeof(host)-1); + EOS_PARANOIA(host); + /* set port */ + port = DEFAULT_PORT; + /* set remaining url */ + url = "/"; + } + + /* now check whether we could reduce it to a local path... */ + if (is_this_our_host(r, host) && port == r->server->port) { + /* this is our host, so only the URL remains */ + r->filename = pstrdup(r->pool, url); + rewritelog(r, 3, "reduce %s -> %s", olduri, r->filename); + } + } + return; +} + + +/* +** +** Expand tilde-paths (~user) through +** Unix /etc/passwd database information +** +*/ + +static char *expand_tildepaths(request_rec *r, char *uri) +{ + char user[LONG_STRING_LEN]; + struct passwd *pw; + char *newuri; + int i, j; + + newuri = uri; + if (uri != NULL && strlen(uri) > 2 && uri[0] == '/' && uri[1] == '~') { + /* cut out the username */ + for (j = 0, i = 2; j < sizeof(user)-1 && uri[i] != '\0' && + ( (uri[i] >= '0' && uri[i] <= '9') + || (uri[i] >= 'a' && uri[i] <= 'z') + || (uri[i] >= 'A' && uri[i] <= 'Z')); ) + user[j++] = uri[i++]; + user[j] = '\0'; + + /* lookup username in systems passwd file */ + if ((pw = getpwnam(user)) != NULL) { + /* ok, user was found, so expand the ~user string */ + if (uri[i] != '\0') { + /* ~user/anything... has to be expanded */ + if (pw->pw_dir[strlen(pw->pw_dir)-1] == '/') + pw->pw_dir[strlen(pw->pw_dir)-1] = '\0'; + newuri = pstrcat(r->pool, pw->pw_dir, uri+i, NULL); + } + else { + /* only ~user has to be expanded */ + newuri = pstrdup(r->pool, pw->pw_dir); + } + } + } + return newuri; +} + + +/* +** +** mapfile expansion support +** i.e. expansion of MAP lookup directives +** ${<mapname>:<key>} in RewriteRule rhs +** +*/ + +#define limit_length(n) (n > LONG_STRING_LEN-1 ? LONG_STRING_LEN-1 : n) + +static void expand_map_lookups(request_rec *r, char *uri, int uri_len) +{ + char newuri[MAX_STRING_LEN]; + char *cpI; + char *cpIE; + char *cpO; + char *cpT; + char *cpT2; + char mapname[LONG_STRING_LEN]; + char mapkey[LONG_STRING_LEN]; + char defaultvalue[LONG_STRING_LEN]; + int n; + + cpI = uri; + cpIE = cpI+strlen(cpI); + cpO = newuri; + while (cpI < cpIE) { + if (cpI+6 < cpIE && strncmp(cpI, "${", 2) == 0) { + /* missing delimiter -> take it as plain text */ + if ( strchr(cpI+2, ':') == NULL + || strchr(cpI+2, '}') == NULL) { + memcpy(cpO, cpI, 2); + cpO += 2; + cpI += 2; + continue; + } + cpI += 2; + + cpT = strchr(cpI, ':'); + n = cpT-cpI; + memcpy(mapname, cpI, limit_length(n)); + mapname[limit_length(n)] = '\0'; + cpI += n+1; + + cpT2 = strchr(cpI, '|'); + cpT = strchr(cpI, '}'); + if (cpT2 != NULL && cpT2 < cpT) { + n = cpT2-cpI; + memcpy(mapkey, cpI, limit_length(n)); + mapkey[limit_length(n)] = '\0'; + cpI += n+1; + + n = cpT-cpI; + memcpy(defaultvalue, cpI, limit_length(n)); + defaultvalue[limit_length(n)] = '\0'; + cpI += n+1; + } + else { + n = cpT-cpI; + memcpy(mapkey, cpI, limit_length(n)); + mapkey[limit_length(n)] = '\0'; + cpI += n+1; + + defaultvalue[0] = '\0'; + } + + cpT = lookup_map(r, mapname, mapkey); + if (cpT != NULL) { + n = strlen(cpT); + if (cpO + n >= newuri + sizeof(newuri)) { + log_printf(r->server, "insufficient space in expand_map_lookups, aborting"); + return; + } + memcpy(cpO, cpT, n); + cpO += n; + } + else { + n = strlen(defaultvalue); + if (cpO + n >= newuri + sizeof(newuri)) { + log_printf(r->server, "insufficient space in expand_map_lookups, aborting"); + return; + } + memcpy(cpO, defaultvalue, n); + cpO += n; + } + } + else { + cpT = strstr(cpI, "${"); + if (cpT == NULL) + cpT = cpI+strlen(cpI); + n = cpT-cpI; + if (cpO + n >= newuri + sizeof(newuri)) { + log_printf(r->server, "insufficient space in expand_map_lookups, aborting"); + return; + } + memcpy(cpO, cpI, n); + cpO += n; + cpI += n; + } + } + *cpO = '\0'; + strncpy(uri, newuri, uri_len-1); + uri[uri_len-1] = '\0'; + return; +} + +#undef limit_length + + + +/* +** +-------------------------------------------------------+ +** | | +** | DBM hashfile support +** | | +** +-------------------------------------------------------+ +*/ + + +static char *lookup_map(request_rec *r, char *name, char *key) +{ + void *sconf; + rewrite_server_conf *conf; + array_header *rewritemaps; + rewritemap_entry *entries; + rewritemap_entry *s; + char *value; + struct stat st; + int i; + + /* get map configuration */ + sconf = r->server->module_config; + conf = (rewrite_server_conf *)get_module_config(sconf, &rewrite_module); + rewritemaps = conf->rewritemaps; + + entries = (rewritemap_entry *)rewritemaps->elts; + for (i = 0; i < rewritemaps->nelts; i++) { + s = &entries[i]; + if (strcmp(s->name, name) == 0) { + if (s->type == MAPTYPE_TXT) { + stat(s->checkfile, &st); /* existence was checked at startup! */ + value = get_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key); + if (value == NULL) { + rewritelog(r, 6, "cache lookup FAILED, forcing new map lookup"); + if ((value = lookup_map_txtfile(r, s->datafile, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s[txt] -> val=%s", s->name, key, value); + set_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[txt] key=%s", s->name, key); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[txt] key=%s -> val=%s", s->name, key, value); + return value; + } + } + else if (s->type == MAPTYPE_DBM) { +#if HAS_NDBM_LIB + stat(s->checkfile, &st); /* existence was checked at startup! */ + value = get_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key); + if (value == NULL) { + rewritelog(r, 6, "cache lookup FAILED, forcing new map lookup"); + if ((value = lookup_map_dbmfile(r, s->datafile, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s[dbm] key=%s -> val=%s", s->name, key, value); + set_cache_string(cachep, s->name, CACHEMODE_TS, st.st_mtime, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s[dbm] key=%s", s->name, key); + return NULL; + } + } + else { + rewritelog(r, 5, "cache lookup OK: map=%s[dbm] key=%s -> val=%s", s->name, key, value); + return value; + } +#else + return NULL; +#endif + } + else if (s->type == MAPTYPE_PRG) { + if ((value = lookup_map_program(r, s->fpin, s->fpout, key)) != NULL) { + rewritelog(r, 5, "map lookup OK: map=%s key=%s -> val=%s", s->name, key, value); + return value; + } + else { + rewritelog(r, 5, "map lookup FAILED: map=%s key=%s", s->name, key); + } + } + } + } + return NULL; +} + + +static char *lookup_map_txtfile(request_rec *r, char *file, char *key) +{ + FILE *fp = NULL; + char line[1024]; + char output[1024]; + char result[1024]; + char *value = NULL; + char *cpT; + char *curkey; + char *curval; + + if ((fp = pfopen(r->pool, file, "r")) == NULL) + return NULL; + + strncpy(output, MAPFILE_OUTPUT, sizeof(output)-1); + EOS_PARANOIA(output); + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + if (regexec(lookup_map_txtfile_regexp, line, lookup_map_txtfile_regexp->re_nsub+1, lookup_map_txtfile_regmatch, 0) == 0) { + strncpy(result, pregsub(r->pool, output, line, lookup_map_txtfile_regexp->re_nsub+1, lookup_map_txtfile_regmatch), sizeof(result)-1); /* substitute in output */ + EOS_PARANOIA(result); + cpT = strchr(result, ','); + *cpT = '\0'; + curkey = result; + curval = cpT+1; + + if (strcmp(curkey, key) == 0) { + value = pstrdup(r->pool, curval); + break; + } + } + } + pfclose(r->pool, fp); + return value; +} + +#if HAS_NDBM_LIB +static char *lookup_map_dbmfile(request_rec *r, char *file, char *key) +{ + DBM *dbmfp = NULL; + datum dbmkey; + datum dbmval; + char *value = NULL; + char buf[MAX_STRING_LEN]; + + dbmkey.dptr = key; + dbmkey.dsize = (strlen(key) < sizeof(buf) - 1 ? strlen(key) : sizeof(buf)-1); + if ((dbmfp = dbm_open(file, O_RDONLY, 0666)) != NULL) { + dbmval = dbm_fetch(dbmfp, dbmkey); + if (dbmval.dptr != NULL) { + memcpy(buf, dbmval.dptr, dbmval.dsize); + buf[dbmval.dsize] = '\0'; + value = pstrdup(r->pool, buf); + } + dbm_close(dbmfp); + } + return value; +} +#endif + +static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key) +{ + char buf[LONG_STRING_LEN]; + char c; + int i; + + /* lock the channel */ +#ifdef USE_PIPE_LOCKING + fd_lock(fpin); +#endif + + /* write out the request key */ + write(fpin, key, strlen(key)); + write(fpin, "\n", 1); + + /* read in the response value */ + i = 0; + while (read(fpout, &c, 1) == 1 && (i < LONG_STRING_LEN-1)) { + if (c == '\n') + break; + buf[i++] = c; + } + buf[i] = '\0'; + + /* unlock the channel */ +#ifdef USE_PIPE_LOCKING + fd_unlock(fpin); +#endif + + if (strcasecmp(buf, "NULL") == 0) + return NULL; + else + return pstrdup(r->pool, buf); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | rewriting logfile support +** | | +** +-------------------------------------------------------+ +*/ + + +static void open_rewritelog(server_rec *s, pool *p) +{ + rewrite_server_conf *conf; + char *fname; + FILE *fp; + int rewritelog_flags = ( O_WRONLY|O_APPEND|O_CREAT ); + mode_t rewritelog_mode = ( S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ); + + conf = get_module_config(s->module_config, &rewrite_module); + + if (conf->rewritelogfile == NULL) + return; + if (*(conf->rewritelogfile) == '\0') + return; + if (conf->rewritelogfp > 0) + return; /* virtual log shared w/main server */ + + fname = server_root_relative(p, conf->rewritelogfile); + + if (*conf->rewritelogfile == '|') { + if (!spawn_child(p, rewritelog_child, (void *)(conf->rewritelogfile+1), + kill_after_timeout, &fp, NULL)) { + perror("spawn_child"); + fprintf (stderr, "mod_rewrite: could not fork child for RewriteLog process\n"); + exit (1); + } + conf->rewritelogfp = fileno(fp); + } + else if (*conf->rewritelogfile != '\0') { + if ((conf->rewritelogfp = popenf(p, fname, rewritelog_flags, rewritelog_mode)) < 0) { + perror("open"); + fprintf(stderr, "mod_rewrite: could not open RewriteLog file %s.\n", fname); + exit(1); + } + } + return; +} + +/* Child process code for 'RewriteLog "|..."' */ +static void rewritelog_child(void *cmd) +{ + cleanup_for_exec(); + signal(SIGHUP, SIG_IGN); +#ifdef __EMX__ + /* OS/2 needs a '/' */ + execl(SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + exit(1); +} + +static void rewritelog(request_rec *r, int level, const char *text, ...) +{ + rewrite_server_conf *conf; + conn_rec *connect; + char *str1; + char str2[512]; + char str3[1024]; + char type[20]; + char redir[20]; + va_list ap; + int i; + request_rec *req; + char *ruser; + const char *rhost; + + va_start(ap, text); + conf = get_module_config(r->server->module_config, &rewrite_module); + connect = r->connection; + + if (conf->rewritelogfp <0) + return; + if (conf->rewritelogfile == NULL) + return; + if (*(conf->rewritelogfile) == '\0') + return; + + if (level > conf->rewriteloglevel) + return; + + if (connect->user == NULL) { + ruser = "-"; + } + else if (strlen (connect->user) != 0) { + ruser = connect->user; + } + else { + ruser = "\"\""; + } + + rhost = get_remote_host(connect, r->server->module_config, REMOTE_NOLOOKUP); + if (rhost == NULL) + rhost = "UNKNOWN-HOST"; + + str1 = pstrcat(r->pool, rhost, " ", + (connect->remote_logname != NULL ? connect->remote_logname : "-"), " ", + ruser, NULL); + ap_vsnprintf(str2, sizeof(str2), text, ap); + + if (r->main == NULL) + strcpy(type, "initial"); + else + strcpy(type, "subreq"); + + for (i = 0, req = r; req->prev != NULL; req = req->prev) + i++; + if (i == 0) + redir[0] = '\0'; + else + ap_snprintf(redir, sizeof(redir), "/redir#%d", i); + + ap_snprintf(str3, sizeof(str3), "%s %s [%s/sid#%x][rid#%x/%s%s] (%d) %s\n", str1, current_logtime(r), r->server->server_hostname, (unsigned int)(r->server), (unsigned int)r, type, redir, level, str2); + + fd_lock(conf->rewritelogfp); + write(conf->rewritelogfp, str3, strlen(str3)); + fd_unlock(conf->rewritelogfp); + + va_end(ap); + return; +} + +static char *current_logtime(request_rec *r) +{ + int timz; + struct tm *t; + char tstr[80]; + char sign; + + t = get_gmtoff(&timz); + sign = (timz < 0 ? '-' : '+'); + if(timz < 0) + timz = -timz; + + strftime(tstr, 80, "[%d/%b/%Y:%H:%M:%S ", t); + ap_snprintf(tstr + strlen(tstr), 80-strlen(tstr), "%c%.2d%.2d]", sign, timz/60, timz%60); + return pstrdup(r->pool, tstr); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | program map support +** | | +** +-------------------------------------------------------+ +*/ + +static void run_rewritemap_programs(server_rec *s, pool *p) +{ + rewrite_server_conf *conf; + char *fname; + FILE *fpin; + FILE *fpout; + array_header *rewritemaps; + rewritemap_entry *entries; + rewritemap_entry *map; + int i; + int rc; + + conf = get_module_config(s->module_config, &rewrite_module); + + rewritemaps = conf->rewritemaps; + entries = (rewritemap_entry *)rewritemaps->elts; + for (i = 0; i < rewritemaps->nelts; i++) { + map = &entries[i]; + if (map->type != MAPTYPE_PRG) + continue; + if (map->datafile == NULL || + *(map->datafile) == '\0' || + map->fpin > 0 || + map->fpout > 0 ) + continue; + fname = server_root_relative(p, map->datafile); + fpin = NULL; + fpout = NULL; + rc = spawn_child(p, rewritemap_program_child, (void *)map->datafile, kill_after_timeout, &fpin, &fpout); + if (rc == 0 || fpin == NULL || fpout == NULL) { + perror("spawn_child"); + fprintf(stderr, "mod_rewrite: could not fork child for RewriteMap process\n"); + exit(1); + } + map->fpin = fileno(fpin); + map->fpout = fileno(fpout); + } + return; +} + +/* child process code */ +static void rewritemap_program_child(void *cmd) +{ + cleanup_for_exec(); + signal(SIGHUP, SIG_IGN); +#ifdef __EMX__ + /* OS/2 needs a '/' */ + execl(SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + exit(1); +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | environment variable support +** | | +** +-------------------------------------------------------+ +*/ + + +static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len) +{ + char *newbuf; + newbuf = expand_variables(r, buf); + if (strcmp(newbuf, buf) != 0) { + strncpy(buf, newbuf, buf_len-1); + buf[buf_len-1] = '\0'; + } + return; +} + +static char *expand_variables(request_rec *r, char *str) +{ + char output[MAX_STRING_LEN]; + char input[MAX_STRING_LEN]; + char *cp; + char *cp2; + char *cp3; + int expanded; + + strncpy(input, str, sizeof(input)-1); + EOS_PARANOIA(input); + output[0] = '\0'; + expanded = 0; + for (cp = input; cp < input+MAX_STRING_LEN; ) { + if ((cp2 = strstr(cp, "%{")) != NULL) { + if ((cp3 = strstr(cp2, "}")) != NULL) { + *cp2 = '\0'; + strncpy(&output[strlen(output)], cp, sizeof(output)-strlen(output)-1); + + cp2 += 2; + *cp3 = '\0'; + strncpy(&output[strlen(output)], lookup_variable(r, cp2), sizeof(output)-strlen(output)-1); + + cp = cp3+1; + expanded = 1; + continue; + } + } + strncpy(&output[strlen(output)], cp, sizeof(output)-strlen(output)-1); + EOS_PARANOIA(output); + break; + } + return expanded ? pstrdup(r->pool, output) : str; +} + +static char *lookup_variable(request_rec *r, char *var) +{ + char *result; + char resultbuf[LONG_STRING_LEN]; + time_t tc; + struct tm *tm; + request_rec *rsub; + struct passwd *pw; + struct group *gr; + struct stat finfo; + + result = NULL; + + /* HTTP headers */ + if (strcasecmp(var, "HTTP_USER_AGENT") == 0) { + result = lookup_header(r, "User-Agent"); + } + else if (strcasecmp(var, "HTTP_REFERER") == 0) { + result = lookup_header(r, "Referer"); + } + else if (strcasecmp(var, "HTTP_COOKIE") == 0) { + result = lookup_header(r, "Cookie"); + } + else if (strcasecmp(var, "HTTP_FORWARDED") == 0) { + result = lookup_header(r, "Forwarded"); + } + else if (strcasecmp(var, "HTTP_HOST") == 0) { + result = lookup_header(r, "Host"); + } + else if (strcasecmp(var, "HTTP_PROXY_CONNECTION") == 0) { + result = lookup_header(r, "Proxy-Connection"); + } + else if (strcasecmp(var, "HTTP_ACCEPT") == 0) { + result = lookup_header(r, "Accept"); + } + /* all other headers from which we are still not know about */ + else if (strlen(var) > 5 && strncasecmp(var, "HTTP:", 5) == 0) { + result = lookup_header(r, var+5); + } + + /* connection stuff */ + else if (strcasecmp(var, "REMOTE_ADDR") == 0) { + result = r->connection->remote_ip; + } + else if (strcasecmp(var, "REMOTE_HOST") == 0) { + result = (char *)get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME); + } + else if (strcasecmp(var, "REMOTE_USER") == 0) { + result = r->connection->user; + } + else if (strcasecmp(var, "REMOTE_IDENT") == 0) { + result = (char *)get_remote_logname(r); + } + + /* request stuff */ + else if (strcasecmp(var, "THE_REQUEST") == 0) { /* non-standard */ + result = r->the_request; + } + else if (strcasecmp(var, "REQUEST_METHOD") == 0) { + result = r->method; + } + else if (strcasecmp(var, "REQUEST_URI") == 0) { /* non-standard */ + result = r->uri; + } + else if (strcasecmp(var, "SCRIPT_FILENAME") == 0 || + strcasecmp(var, "REQUEST_FILENAME") == 0 ) { + result = r->filename; + } + else if (strcasecmp(var, "PATH_INFO") == 0) { + result = r->path_info; + } + else if (strcasecmp(var, "QUERY_STRING") == 0) { + result = r->args; + } + else if (strcasecmp(var, "AUTH_TYPE") == 0) { + result = r->connection->auth_type; + } + else if (strcasecmp(var, "IS_SUBREQ") == 0) { /* non-standard */ + result = (r->main != NULL ? "true" : "false"); + } + + /* internal server stuff */ + else if (strcasecmp(var, "DOCUMENT_ROOT") == 0) { + result = document_root(r); + } + else if (strcasecmp(var, "SERVER_ADMIN") == 0) { + result = r->server->server_admin; + } + else if (strcasecmp(var, "SERVER_NAME") == 0) { + result = r->server->server_hostname; + } + else if (strcasecmp(var, "SERVER_PORT") == 0) { + ap_snprintf(resultbuf, sizeof(resultbuf), "%u", r->server->port); + result = resultbuf; + } + else if (strcasecmp(var, "SERVER_PROTOCOL") == 0) { + result = r->protocol; + } + else if (strcasecmp(var, "SERVER_SOFTWARE") == 0) { + result = pstrdup(r->pool, SERVER_VERSION); + } + else if (strcasecmp(var, "API_VERSION") == 0) { /* non-standard */ + ap_snprintf(resultbuf, sizeof(resultbuf), "%d", MODULE_MAGIC_NUMBER); + result = resultbuf; + } + + /* underlaying Unix system stuff */ + else if (strcasecmp(var, "TIME_YEAR") == 0) { + tc = time(NULL); + tm = localtime(&tc); + ap_snprintf(resultbuf, sizeof(resultbuf), "%02d%02d", (tm->tm_year / 100) + 19, tm->tm_year % 100); + result = resultbuf; + } +#define MKTIMESTR(format, tmfield) \ + tc = time(NULL); \ + tm = localtime(&tc); \ + ap_snprintf(resultbuf, sizeof(resultbuf), format, tm->tmfield); \ + result = resultbuf; + else if (strcasecmp(var, "TIME_MON") == 0) { + MKTIMESTR("%02d", tm_mon+1) + } + else if (strcasecmp(var, "TIME_DAY") == 0) { + MKTIMESTR("%02d", tm_mday) + } + else if (strcasecmp(var, "TIME_HOUR") == 0) { + MKTIMESTR("%02d", tm_hour) + } + else if (strcasecmp(var, "TIME_MIN") == 0) { + MKTIMESTR("%02d", tm_min) + } + else if (strcasecmp(var, "TIME_SEC") == 0) { + MKTIMESTR("%02d", tm_sec) + } + else if (strcasecmp(var, "TIME_WDAY") == 0) { + MKTIMESTR("%d", tm_wday) + } + else if (strcasecmp(var, "TIME") == 0) { + tc = time(NULL); + tm = localtime(&tc); + ap_snprintf(resultbuf, sizeof(resultbuf), "%02d%02d%02d%02d%02d%02d%02d", + (tm->tm_year / 100) + 19, (tm->tm_year % 100), + tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + result = resultbuf; + rewritelog(r, 1, "RESULT='%s'", result); + } + + /* all other env-variables from the parent Apache process */ + else if (strlen(var) > 4 && strncasecmp(var, "ENV:", 4) == 0) { + /* first try the internal Apache notes structure */ + result = table_get(r->notes, var+4); + /* second try the internal Apache env structure */ + if (result == NULL) + result = table_get(r->subprocess_env, var+4); + /* third try the external OS env */ + if (result == NULL) + result = getenv(var+4); + } + +#define LOOKAHEAD(subrecfunc) \ + if ( \ + /* filename is safe to use */ \ + r->filename != NULL \ + /* - and we're either not in a subrequest */ \ + && ( r->main == NULL \ + /* - or in a subrequest where paths are non-NULL... */ \ + || ( r->main->uri != NULL && r->uri != NULL \ + /* ...and sub and main paths differ */ \ + && strcmp(r->main->uri, r->uri) != 0))) { \ + /* process a file-based subrequest */ \ + rsub = subrecfunc(r->filename, r); \ + /* now recursively lookup the variable in the sub_req */ \ + result = lookup_variable(rsub, var+5); \ + /* copy it up to our scope before we destroy the sub_req's pool */ \ + result = pstrdup(r->pool, result); \ + /* cleanup by destroying the subrequest */ \ + destroy_sub_req(rsub); \ + /* log it */ \ + rewritelog(r, 5, "lookahead: path=%s var=%s -> val=%s", r->filename, var+5, result); \ + /* return ourself to prevent re-pstrdup */ \ + return result; \ + } + + /* look-ahead for parameter through URI-based sub-request */ + else if (strlen(var) > 5 && strncasecmp(var, "LA-U:", 5) == 0) { + LOOKAHEAD(sub_req_lookup_uri) + } + /* look-ahead for parameter through file-based sub-request */ + else if (strlen(var) > 5 && strncasecmp(var, "LA-F:", 5) == 0) { + LOOKAHEAD(sub_req_lookup_file) + } + + /* file stuff */ + else if (strcasecmp(var, "SCRIPT_USER") == 0) { + result = pstrdup(r->pool, "<unknown>"); + if (r->finfo.st_mode != 0) { + if ((pw = getpwuid(r->finfo.st_uid)) != NULL) { + result = pstrdup(r->pool, pw->pw_name); + } + } + else { + if (stat(r->filename, &finfo) == 0) { + if ((pw = getpwuid(finfo.st_uid)) != NULL) { + result = pstrdup(r->pool, pw->pw_name); + } + } + } + } + else if (strcasecmp(var, "SCRIPT_GROUP") == 0) { + result = pstrdup(r->pool, "<unknown>"); + if (r->finfo.st_mode != 0) { + if ((gr = getgrgid(r->finfo.st_gid)) != NULL) { + result = pstrdup(r->pool, gr->gr_name); + } + } + else { + if (stat(r->filename, &finfo) == 0) { + if ((gr = getgrgid(finfo.st_gid)) != NULL) { + result = pstrdup(r->pool, gr->gr_name); + } + } + } + } + + if (result == NULL) + return pstrdup(r->pool, ""); + else + return pstrdup(r->pool, result); +} + +static char *lookup_header(request_rec *r, const char *name) +{ + array_header *hdrs_arr; + table_entry *hdrs; + int i; + + hdrs_arr = table_elts(r->headers_in); + hdrs = (table_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (hdrs[i].key == NULL) + continue; + if (strcasecmp(hdrs[i].key, name) == 0) + return hdrs[i].val; + } + return NULL; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | caching support +** | | +** +-------------------------------------------------------+ +*/ + + +static cache *init_cache(pool *p) +{ + cache *c; + + c = (cache *)palloc(p, sizeof(cache)); + c->pool = make_sub_pool(p); + c->lists = make_array(c->pool, 2, sizeof(cachelist)); + return c; +} + +static void set_cache_string(cache *c, char *res, int mode, time_t time, char *key, char *value) +{ + cacheentry ce; + + ce.time = time; + ce.key = key; + ce.value = value; + store_cache_string(c, res, &ce); + return; +} + +static char *get_cache_string(cache *c, char *res, int mode, time_t time, char *key) +{ + cacheentry *ce; + + ce = retrieve_cache_string(c, res, key); + if (ce == NULL) + return NULL; + if (mode & CACHEMODE_TS) { + if (time != ce->time) + return NULL; + } + else if (mode & CACHEMODE_TTL) { + if (time > ce->time) + return NULL; + } + return pstrdup(c->pool, ce->value); +} + +static void store_cache_string(cache *c, char *res, cacheentry *ce) +{ + int i; + int j; + cachelist *l; + cacheentry *e; + int found_list; + + found_list = 0; + /* first try to edit an existing entry */ + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + found_list = 1; + for (j = 0; j < l->entries->nelts; j++) { + e = &(((cacheentry *)l->entries->elts)[j]); + if (strcmp(e->key, ce->key) == 0) { + e->time = ce->time; + e->value = pstrdup(c->pool, ce->value); + return; + } + } + } + } + + /* create a needed new list */ + if (!found_list) { + l = push_array(c->lists); + l->resource = pstrdup(c->pool, res); + l->entries = make_array(c->pool, 2, sizeof(cacheentry)); + } + + /* create the new entry */ + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + e = push_array(l->entries); + e->time = ce->time; + e->key = pstrdup(c->pool, ce->key); + e->value = pstrdup(c->pool, ce->value); + return; + } + } + + /* not reached, but when it is no problem... */ + return; +} + +static cacheentry *retrieve_cache_string(cache *c, char *res, char *key) +{ + int i; + int j; + cachelist *l; + cacheentry *e; + + for (i = 0; i < c->lists->nelts; i++) { + l = &(((cachelist *)c->lists->elts)[i]); + if (strcmp(l->resource, res) == 0) { + for (j = 0; j < l->entries->nelts; j++) { + e = &(((cacheentry *)l->entries->elts)[j]); + if (strcmp(e->key, key) == 0) { + return e; + } + } + } + } + return NULL; +} + + + + +/* +** +-------------------------------------------------------+ +** | | +** | misc functions +** | | +** +-------------------------------------------------------+ +*/ + +static char *subst_prefix_path(request_rec *r, char *input, char *match, char *subst) +{ + char matchbuf[LONG_STRING_LEN]; + char substbuf[LONG_STRING_LEN]; + char *output; + int l; + + output = input; + + /* first create a match string which always has a trailing slash */ + strncpy(matchbuf, match, sizeof(matchbuf)-1); + EOS_PARANOIA(matchbuf); + l = strlen(matchbuf); + if (matchbuf[l-1] != '/') { + matchbuf[l] = '/'; + matchbuf[l+1] = '\0'; + l++; + } + /* now compare the prefix */ + if (strncmp(input, matchbuf, l) == 0) { + rewritelog(r, 5, "strip matching prefix: %s -> %s", output, output+l); + output = pstrdup(r->pool, output+l); + + /* and now add the base-URL as replacement prefix */ + strncpy(substbuf, subst, sizeof(substbuf)-1); + EOS_PARANOIA(substbuf); + l = strlen(substbuf); + if (substbuf[l-1] != '/') { + substbuf[l] = '/'; + substbuf[l+1] = '\0'; + l++; + } + if (output[0] == '/') { + rewritelog(r, 4, "add subst prefix: %s -> %s%s", output, substbuf, output+1); + output = pstrcat(r->pool, substbuf, output+1, NULL); + } + else { + rewritelog(r, 4, "add subst prefix: %s -> %s%s", output, substbuf, output); + output = pstrcat(r->pool, substbuf, output, NULL); + } + } + return output; +} + + +/* +** +** own command line parser which don't have the '\\' problem +** +*/ + +static int parseargline(char *str, char **a1, char **a2, char **a3) +{ + char *cp; + int isquoted; + +#define SKIP_WHITESPACE(cp) \ + for ( ; *cp == ' ' || *cp == '\t'; ) \ + cp++; + +#define CHECK_QUOTATION(cp,isquoted) \ + isquoted = 0; \ + if (*cp == '"') { \ + isquoted = 1; \ + cp++; \ + } + +#define DETERMINE_NEXTSTRING(cp,isquoted) \ + for ( ; *cp != '\0'; cp++) { \ + if ( (isquoted && (*cp == ' ' || *cp == '\t')) \ + || (*cp == '\\' && (*(cp+1) == ' ' || *(cp+1) == '\t'))) { \ + cp++; \ + continue; \ + } \ + if ( (!isquoted && (*cp == ' ' || *cp == '\t')) \ + || (isquoted && *cp == '"') ) \ + break; \ + } + + cp = str; + SKIP_WHITESPACE(cp); + + /* determine first argument */ + CHECK_QUOTATION(cp, isquoted); + *a1 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + if (*cp == '\0') + return 1; + *cp++ = '\0'; + + SKIP_WHITESPACE(cp); + + /* determine second argument */ + CHECK_QUOTATION(cp, isquoted); + *a2 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + if (*cp == '\0') { + *cp++ = '\0'; + *a3 = NULL; + return 0; + } + *cp++ = '\0'; + + SKIP_WHITESPACE(cp); + + /* again check if there are only two arguments */ + if (*cp == '\0') { + *cp++ = '\0'; + *a3 = NULL; + return 0; + } + + /* determine second argument */ + CHECK_QUOTATION(cp, isquoted); + *a3 = cp; + DETERMINE_NEXTSTRING(cp, isquoted); + *cp++ = '\0'; + + return 0; +} + + +static void add_env_variable(request_rec *r, char *s) +{ + char var[MAX_STRING_LEN]; + char val[MAX_STRING_LEN]; + char *cp; + int n; + + if ((cp = strchr(s, ':')) != NULL) { + n = ((cp-s) > MAX_STRING_LEN-1 ? MAX_STRING_LEN-1 : (cp-s)); + memcpy(var, s, n); + var[n] = '\0'; + strncpy(val, cp+1, sizeof(val)-1); + EOS_PARANOIA(val); + table_set(r->subprocess_env, pstrdup(r->pool, var), pstrdup(r->pool, val)); + rewritelog(r, 5, "setting env variable '%s' to '%s'", var, val); + } +} + + + +/* +** +** stat() for only the prefix of a path +** +*/ + +static int prefix_stat(const char *path, struct stat *sb) +{ + char curpath[LONG_STRING_LEN]; + char *cp; + + strncpy(curpath, path, sizeof(curpath)-1); + EOS_PARANOIA(curpath); + if (curpath[0] != '/') + return 0; + if ((cp = strchr(curpath+1, '/')) != NULL) + *cp = '\0'; + if (stat(curpath, sb) == 0) + return 1; + else + return 0; +} + + +/* +** +** special DNS lookup functions +** +*/ + +static int is_this_our_host(request_rec *r, char *testhost) +{ + char **cppHNLour; + char **cppHNLtest; + char *ourhostname; + char *ourhostip; + const char *names; + char *name; + int i, j; + server_addr_rec *sar; + + /* we can check: + r-> + char *hostname Host, as set by full URI or Host: + int hostlen Length of http://host:port in full URI + r->server-> + int is_virtual 0=main, 1=ip-virtual, 2=non-ip-virtual + char *server_hostname used on compare to r->hostname + inet_ntoa(r->connection->local_addr.sin_addr) + used on compare to r->hostname + unsigned short port for redirects + char *path name of ServerPath + int pathlen len of ServerPath + char *names Wildcarded names for ServerAlias servers + r->server->addrs-> + struct in_addr host_addr The bound address, for this server + short host_port The bound port, for this server + char *virthost The name given in <VirtualHost> + */ + + ourhostname = r->server->server_hostname; + ourhostip = inet_ntoa(r->connection->local_addr.sin_addr); + + /* just a simple common case */ + if (strcmp(testhost, ourhostname) == 0 || + strcmp(testhost, ourhostip) == 0 ) + return YES; + + /* now the complicated cases */ + if (!r->server->is_virtual) { + /* main servers */ + + /* check for the alternative IP addresses */ + if ((cppHNLour = resolv_ipaddr_list(r, ourhostname)) == NULL) + return NO; + if ((cppHNLtest = resolv_ipaddr_list(r, testhost)) == NULL) + return NO; + for (i = 0; cppHNLtest[i] != NULL; i++) { + for (j = 0; cppHNLour[j] != NULL; j++) { + if (strcmp(cppHNLtest[i], cppHNLour[j]) == 0) { + return YES; + } + } + } + } + else if (r->server->is_virtual) { + /* virtual servers */ + + /* check for the names supplied in the VirtualHost directive */ + for(sar = r->server->addrs; sar != NULL; sar = sar->next) { + if(strcasecmp(sar->virthost, testhost) == 0) + return YES; + } + + /* check for the virtual-server aliases */ + if (r->server->names != NULL && r->server->names[0] != '\0') { + names = r->server->names; + while (*names != '\0') { + name = getword_conf(r->pool, &names); + if ((is_matchexp(name) && !strcasecmp_match(testhost, name)) || + (strcasecmp(testhost, name) == 0) ) { + return YES; + } + } + } + } + return NO; +} + +static int isaddr(char *host) +{ + char *cp; + + /* Null pointers and empty strings + are not addresses. */ + if (host == NULL) + return NO; + if (*host == '\0') + return NO; + /* Make sure it has only digits and dots. */ + for (cp = host; *cp; cp++) { + if (!isdigit(*cp) && *cp != '.') + return NO; + } + /* If it has a trailing dot, + don't treat it as an address. */ + if (*(cp-1) == '.') + return NO; + return YES; +} + +static char **resolv_ipaddr_list(request_rec *r, char *name) +{ + char **cppHNL; + struct hostent *hep; + int i; + + if (isaddr(name)) + hep = gethostbyaddr(name, sizeof(struct in_addr), AF_INET); + else + hep = gethostbyname(name); + if (hep == NULL) + return NULL; + for (i = 0; hep->h_addr_list[i]; i++) + ; + cppHNL = (char **)palloc(r->pool, sizeof(char *)*(i+1)); + for (i = 0; hep->h_addr_list[i]; i++) + cppHNL[i] = pstrdup(r->pool, inet_ntoa(*((struct in_addr *)(hep->h_addr_list[i]))) ); + cppHNL[i] = NULL; + return cppHNL; +} + + +/* +** +** check if proxy module is available +** i.e. if it is compiled in and turned on +** +*/ + +static int is_proxy_available(server_rec *s) +{ + return (find_linked_module("mod_proxy.c") != NULL); +} + + +/* +** +** File locking +** +*/ + +#ifdef USE_FCNTL +static struct flock lock_it; +static struct flock unlock_it; +#endif + +static void fd_lock(int fd) +{ + int rc; + +#ifdef USE_FCNTL + 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 */ + + while ( ((rc = fcntl(fd, F_SETLKW, &lock_it)) < 0) + && (errno == EINTR) ) + continue; +#endif +#ifdef USE_FLOCK + while ( ((rc = flock(fd, LOCK_EX)) < 0) + && (errno == EINTR) ) + continue; +#endif + + if (rc < 0) { +#ifdef USE_FLOCK + perror("flock"); +#else + perror("fcntl"); +#endif + fprintf(stderr, "Error getting lock. Exiting!"); + exit(1); + } + return; +} + +static void fd_unlock(int fd) +{ + int rc; + +#ifdef USE_FCNTL + 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; /* unlock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + rc = fcntl(fd, F_SETLKW, &unlock_it); +#endif +#ifdef USE_FLOCK + rc = flock(fd, LOCK_UN); +#endif + + if (rc < 0) { +#ifdef USE_FLOCK + perror("flock"); +#else + perror("fcntl"); +#endif + fprintf(stderr, "Error freeing lock. Exiting!"); + exit(1); + } +} + +/* +** +** Lexicographic Compare +** +*/ + +static int compare_lexicography(char *cpNum1, char *cpNum2) +{ + int i; + int n1, n2; + + n1 = strlen(cpNum1); + n2 = strlen(cpNum2); + if (n1 > n2) + return 1; + if (n1 < n2) + return -1; + for (i = 0; i < n1; i++) { + if (cpNum1[i] > cpNum2[i]) + return 1; + if (cpNum1[i] < cpNum2[i]) + return -1; + } + return 0; +} + + +/*EOF*/ diff --git a/usr.sbin/httpd/src/mod_rewrite.h b/usr.sbin/httpd/src/mod_rewrite.h new file mode 100644 index 00000000000..10f91070a85 --- /dev/null +++ b/usr.sbin/httpd/src/mod_rewrite.h @@ -0,0 +1,400 @@ + +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + + +#ifndef _MOD_REWRITE_H +#define _MOD_REWRITE_H 1 + +/* +** _ _ _ +** _ __ ___ ___ __| | _ __ _____ ___ __(_) |_ ___ +** | '_ ` _ \ / _ \ / _` | | '__/ _ \ \ /\ / / '__| | __/ _ \ +** | | | | | | (_) | (_| | | | | __/\ V V /| | | | || __/ +** |_| |_| |_|\___/ \__,_|___|_| \___| \_/\_/ |_| |_|\__\___| +** |_____| +** +** URL Rewriting Module +** +** This module uses a rule-based rewriting engine (based on a +** regular-expression parser) to rewrite requested URLs on the fly. +** +** It supports an unlimited number of additional rule conditions (which can +** operate on a lot of variables, even on HTTP headers) for granular +** matching and even external database lookups (either via plain text +** tables, DBM hash files or even external processes) for advanced URL +** substitution. +** +** It operates on the full URLs (including the PATH_INFO part) both in +** per-server context (httpd.conf) and per-dir context (.htaccess) and even +** can generate QUERY_STRING parts on result. The rewriting result finally +** can lead to internal subprocessing, external request redirection or even +** to internal proxy throughput. +** +** This module was originally written in April 1996 and +** gifted exclusively to the The Apache Group in July 1997 by +** +** Ralf S. Engelschall +** rse@engelschall.com +** www.engelschall.com +*/ + + + + + /* The NDBM support: + We support only NDBM files. + But we have to stat the file for the mtime, + so we also need to know the file extension */ +#if HAS_NDBM_LIB +#include <ndbm.h> +#if (__FreeBSD__) +#define NDBM_FILE_SUFFIX ".db" +#else +#define NDBM_FILE_SUFFIX ".pag" +#endif +#endif + + + /* The locking support: + Try to determine whether we should use fcntl() or flock(). + Would be better conf.h could provide this... :-( */ +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) +#define USE_FCNTL 1 +#include <fcntl.h> +#endif +#if defined(USE_FLOCK_SERIALIZED_ACCEPT) +#define USE_FLOCK 1 +#include <sys/file.h> +#endif +#if !defined(USE_FCNTL) && !defined(USE_FLOCK) +#define USE_FLOCK 1 +#ifndef MPE +#include <sys/file.h> +#endif +#ifndef LOCK_UN +#undef USE_FLOCK +#define USE_FCNTL 1 +#include <fcntl.h> +#endif +#endif +#ifdef AIX +#undef USE_FLOCK +#define USE_FCNTL 1 +#include <fcntl.h> +#endif + + /* The locking support for the RewriteMap programs: + Locking a pipe to the child works fine under most + Unix derivates, but braindead SunOS 4.1.x has + problems with this approach... */ +#define USE_PIPE_LOCKING 1 +#ifdef SUNOS4 +#undef USE_PIPE_LOCKING +#endif + + +/* +** +** Some defines +** +*/ + +#define ENVVAR_SCRIPT_URL "SCRIPT_URL" +#define ENVVAR_SCRIPT_URI "SCRIPT_URI" + +#ifndef SUPPORT_DBM_REWRITEMAP +#define SUPPORT_DBM_REWRITEMAP 0 +#endif + +#define REWRITE_FORCED_MIMETYPE_NOTEVAR "rewrite-forced-mimetype" + +#define CONDFLAG_NONE 1<<0 +#define CONDFLAG_NOCASE 1<<1 +#define CONDFLAG_NOTMATCH 1<<2 +#define CONDFLAG_ORNEXT 1<<3 + +#define RULEFLAG_NONE 1<<0 +#define RULEFLAG_FORCEREDIRECT 1<<1 +#define RULEFLAG_LASTRULE 1<<2 +#define RULEFLAG_NEWROUND 1<<3 +#define RULEFLAG_CHAIN 1<<4 +#define RULEFLAG_IGNOREONSUBREQ 1<<5 +#define RULEFLAG_NOTMATCH 1<<6 +#define RULEFLAG_PROXY 1<<7 +#define RULEFLAG_PASSTHROUGH 1<<8 +#define RULEFLAG_FORBIDDEN 1<<9 +#define RULEFLAG_GONE 1<<10 +#define RULEFLAG_QSAPPEND 1<<11 + +#define MAPTYPE_TXT 1<<0 +#define MAPTYPE_DBM 1<<1 +#define MAPTYPE_PRG 1<<2 + +#define ENGINE_DISABLED 1<<0 +#define ENGINE_ENABLED 1<<1 + +#define OPTION_NONE 1<<0 +#define OPTION_INHERIT 1<<1 + +#define CACHEMODE_TS 1<<0 +#define CACHEMODE_TTL 1<<1 + +#ifndef FALSE +#define FALSE 0 +#define TRUE !FALSE +#endif + +#ifndef NO +#define NO FALSE +#define YES TRUE +#endif + +#ifndef LONG_STRING_LEN +#define LONG_STRING_LEN 2048 +#endif + +#define MAX_ENV_FLAGS 5 + +#define EOS_PARANOIA(ca) ca[sizeof(ca)-1] = '\0' + + +/* +** +** our private data structures we handle with +** +*/ + + /* the list structures for holding the mapfile information + and the rewrite rules */ + +typedef struct { + char *name; /* the name of the map */ + char *datafile; /* the file which contains the data of the map */ + char *checkfile; /* the file which stays for existence of the map */ + int type; /* the type of the map */ + int fpin; /* in filepointer for program maps */ + int fpout; /* out filepointer for program maps */ +} rewritemap_entry; + +typedef struct { + char *input; /* Input string of RewriteCond */ + char *pattern; /* the RegExp pattern string */ + regex_t *regexp; + int flags; /* Flags which control the match */ +} rewritecond_entry; + +typedef struct { + array_header *rewriteconds; /* the corresponding RewriteCond entries */ + char *pattern; /* the RegExp pattern string */ + regex_t *regexp; /* the RegExp pattern compilation */ + char *output; /* the Substitution string */ + int flags; /* Flags which control the substitution */ + char *forced_mimetype; /* forced MIME type of substitution */ + int forced_responsecode; /* forced HTTP redirect response status */ + char *env[MAX_ENV_FLAGS+1];/* added environment variables */ + int skip; /* number of next rules to skip */ +} rewriterule_entry; + + + /* the per-server or per-virtual-server configuration + statically generated once on startup for every server */ + +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + char *rewritelogfile; /* the RewriteLog filename */ + int rewritelogfp; /* the RewriteLog open filepointer */ + int rewriteloglevel; /* the RewriteLog level of verbosity */ + array_header *rewritemaps; /* the RewriteMap entries */ + array_header *rewriteconds; /* the RewriteCond entries (temporary) */ + array_header *rewriterules; /* the RewriteRule entries */ +} rewrite_server_conf; + + + /* the per-directory configuration + individually generated on-the-fly by Apache server for current request */ + +typedef struct { + int state; /* the RewriteEngine state */ + int options; /* the RewriteOption state */ + array_header *rewriteconds; /* the RewriteCond entries (temporary) */ + array_header *rewriterules; /* the RewriteRule entries */ + char *directory; /* the directory where it applies */ + char *baseurl; /* the base-URL where it applies */ +} rewrite_perdir_conf; + + + /* the cache structures */ + +typedef struct cacheentry { + time_t time; + char *key; + char *value; +} cacheentry; + +typedef struct cachelist { + char *resource; + array_header *entries; +} cachelist; + +typedef struct cache { + pool *pool; + array_header *lists; +} cache; + + +/* +** +** forward declarations +** +*/ + + /* config structure handling */ +static void *config_server_create(pool *p, server_rec *s); +static void *config_server_merge (pool *p, void *basev, void *overridesv); +static void *config_perdir_create(pool *p, char *path); +static void *config_perdir_merge (pool *p, void *basev, void *overridesv); + + /* config directive handling */ +static const char *cmd_rewriteengine (cmd_parms *cmd, rewrite_perdir_conf *dconf, int flag); +static const char *cmd_rewriteoptions (cmd_parms *cmd, rewrite_perdir_conf *dconf, char *option); +static const char *cmd_rewriteoptions_setoption(pool *p, int *options, char *name); +static const char *cmd_rewritelog (cmd_parms *cmd, void *dconf, char *a1); +static const char *cmd_rewriteloglevel(cmd_parms *cmd, void *dconf, char *a1); +static const char *cmd_rewritemap (cmd_parms *cmd, void *dconf, char *a1, char *a2); + +static const char *cmd_rewritebase(cmd_parms *cmd, rewrite_perdir_conf *dconf, char *a1); + +static const char *cmd_rewritecond (cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str); +static const char *cmd_rewritecond_parseflagfield(pool *p, rewritecond_entry *new, char *str); +static const char *cmd_rewritecond_setflag (pool *p, rewritecond_entry *cfg, char *key, char *val); + +extern const char *cmd_rewriterule (cmd_parms *cmd, rewrite_perdir_conf *dconf, char *str); +static const char *cmd_rewriterule_parseflagfield(pool *p, rewriterule_entry *new, char *str); +static const char *cmd_rewriterule_setflag (pool *p, rewriterule_entry *cfg, char *key, char *val); + + /* initialisation */ +static void init_module(server_rec *s, pool *p); + + /* runtime hooks */ +static int hook_uri2file (request_rec *r); +static int hook_mimetype (request_rec *r); +static int hook_fixup (request_rec *r); +static int handler_redirect(request_rec *r); + + /* rewriting engine */ +static int apply_rewrite_list(request_rec *r, array_header *rewriterules, char *perdir); +static int apply_rewrite_rule(request_rec *r, rewriterule_entry *p, char *perdir); +static int apply_rewrite_cond(request_rec *r, rewritecond_entry *p, char *perdir); + + /* URI transformation function */ +static void splitout_queryargs(request_rec *r, int qsappend); +static void reduce_uri(request_rec *r); +static char *expand_tildepaths(request_rec *r, char *uri); +static void expand_map_lookups(request_rec *r, char *uri, int uri_len); + + /* DBM hashfile support functions */ +static char *lookup_map(request_rec *r, char *name, char *key); +static char *lookup_map_txtfile(request_rec *r, char *file, char *key); +#if HAS_NDBM_LIB +static char *lookup_map_dbmfile(request_rec *r, char *file, char *key); +#endif +static char *lookup_map_program(request_rec *r, int fpin, int fpout, char *key); + + /* rewriting logfile support */ +static void open_rewritelog(server_rec *s, pool *p); +static void rewritelog_child(void *cmd); +static void rewritelog(request_rec *r, int level, const char *text, ...); +static char *current_logtime(request_rec *r); + + /* program map support */ +static void run_rewritemap_programs(server_rec *s, pool *p); +static void rewritemap_program_child(void *cmd); + + /* env variable support */ +static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len); +static char *expand_variables(request_rec *r, char *str); +static char *lookup_variable(request_rec *r, char *var); +static char *lookup_header(request_rec *r, const char *name); + + /* caching functions */ +static cache *init_cache(pool *p); +static char *get_cache_string(cache *c, char *res, int mode, time_t mtime, char *key); +static void set_cache_string(cache *c, char *res, int mode, time_t mtime, char *key, char *value); +static cacheentry *retrieve_cache_string(cache *c, char *res, char *key); +static void store_cache_string(cache *c, char *res, cacheentry *ce); + + /* misc functions */ +static char *subst_prefix_path(request_rec *r, char *input, char *match, char *subst); +static int parseargline(char *str, char **a1, char **a2, char **a3); +static int prefix_stat(const char *path, struct stat *sb); +static void add_env_variable(request_rec *r, char *s); + + /* DNS functions */ +static int is_this_our_host(request_rec *r, char *testhost); +static int isaddr(char *host); +static char **resolv_ipaddr_list(request_rec *r, char *name); + + /* Proxy Module check */ +static int is_proxy_available(server_rec *s); + + /* File locking */ +static void fd_lock(int fd); +static void fd_unlock(int fd); + + /* Lexicographic Comparison */ +static int compare_lexicography(char *cpNum1, char *cpNum2); + +#endif /* _MOD_REWRITE_H */ + +/*EOF*/ diff --git a/usr.sbin/httpd/src/mod_status.c b/usr.sbin/httpd/src/mod_status.c new file mode 100644 index 00000000000..c00a3a4d5f9 --- /dev/null +++ b/usr.sbin/httpd/src/mod_status.c @@ -0,0 +1,643 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* Status Module. Display lots of internal data about how Apache is + * performing and the state of all children processes. + * + * To enable this, add the following lines into any config file: + * + * <Location /server-status> + * SetHandler server-status + * </Location> + * + * You may want to protect this location by password or domain so no one + * else can look at it. Then you can access the statistics with a URL like: + * + * http://your_server_name/server-status + * + * /server-status - Returns page using tables + * /server-status?notable - Returns page for browsers without table support + * /server-status?refresh - Returns page with 1 second refresh + * /server-status?refresh=6 - Returns page with refresh every 6 seconds + * /server-status?auto - Returns page with data for automatic parsing + * + * Mark Cox, mark@ukweb.com, November 1995 + * + * 12.11.95 Initial version for www.telescope.org + * 13.3.96 Updated to remove rprintf's [Mark] + * 18.3.96 Added CPU usage, process information, and tidied [Ben Laurie] + * 18.3.96 Make extra Scoreboard variables #definable + * 25.3.96 Make short report have full precision [Ben Laurie suggested] + * 25.3.96 Show uptime better [Mark/Ben Laurie] + * 29.3.96 Better HTML and explanation [Mark/Rob Hartill suggested] + * 09.4.96 Added message for non-STATUS compiled version + * 18.4.96 Added per child and per slot counters [Jim Jagielski] + * 01.5.96 Table format, cleanup, even more spiffy data [Chuck Murcko/Jim J.] + * 18.5.96 Adapted to use new rprintf() routine, incidentally fixing a missing + * piece in short reports [Ben Laurie] + * 21.5.96 Additional Status codes (DNS and LOGGING only enabled if + extended STATUS is enabled) [George Burgyan/Jim J.] */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "util_script.h" +#include <time.h> +#include "scoreboard.h" +#include "http_log.h" + +#ifdef NEXT +#include <machine/param.h> +#endif + +#define STATUS_MAXLINE 64 + +#define KBYTE 1024 +#define MBYTE 1048576L +#define GBYTE 1073741824L + +module status_module; + +/* Format the number of bytes nicely */ + +void format_byte_out(request_rec *r,unsigned long bytes) +{ + if (bytes < (5 * KBYTE)) + rprintf(r,"%d B",(int)bytes); + else if (bytes < (MBYTE / 2)) + rprintf(r,"%.1f kB",(float)bytes/KBYTE); + else if (bytes < (GBYTE / 2)) + rprintf(r,"%.1f MB",(float)bytes/MBYTE); + else + rprintf(r,"%.1f GB",(float)bytes/GBYTE); +} + +void format_kbyte_out(request_rec *r,unsigned long kbytes) +{ + if (kbytes < KBYTE) + rprintf(r,"%d kB",(int)kbytes); + else if (kbytes < MBYTE) + rprintf(r,"%.1f MB",(float)kbytes/KBYTE); + else + rprintf(r,"%.1f GB",(float)kbytes/MBYTE); +} + +void show_time(request_rec *r,time_t tsecs) +{ + long days,hrs,mins,secs; + char buf[100]; + char *s; + + secs=tsecs%60; + tsecs/=60; + mins=tsecs%60; + tsecs/=60; + hrs=tsecs%24; + days=tsecs/24; + s=buf; + *s='\0'; + if(days) + rprintf(r," %ld day%s",days,days==1?"":"s"); + if(hrs) + rprintf(r," %ld hour%s",hrs,hrs==1?"":"s"); + if(mins) + rprintf(r," %ld minute%s",mins,mins==1?"":"s"); + if(secs) + rprintf(r," %ld second%s",secs,secs==1?"":"s"); +} + +#if defined(SUNOS4) +double +difftime(time1, time0) + time_t time1, time0; +{ + return(time1 - time0); +} +#endif + +/* Main handler for x-httpd-status requests */ + +/* ID values for command table */ + +#define STAT_OPT_END -1 +#define STAT_OPT_REFRESH 0 +#define STAT_OPT_NOTABLE 1 +#define STAT_OPT_AUTO 2 + +struct stat_opt +{ + int id; + char *form_data_str; + char *hdr_out_str; +}; + +int status_handler (request_rec *r) +{ + struct stat_opt options[] = /* see #defines above */ + { + { STAT_OPT_REFRESH, "refresh", "Refresh" }, + { STAT_OPT_NOTABLE, "notable", NULL }, + { STAT_OPT_AUTO, "auto", NULL }, + { STAT_OPT_END, NULL, NULL } + }; + char *loc; + time_t nowtime=time(NULL); + time_t up_time; + int i,res; + int ready=0; + int busy=0; +#if defined(STATUS) + unsigned long count=0; + unsigned long lres,bytes; + unsigned long my_lres,my_bytes,conn_bytes; + unsigned short conn_lres; + unsigned long bcount=0; + unsigned long kbcount=0; +#ifdef NEXT + float tick=HZ; +#else + float tick=sysconf(_SC_CLK_TCK); +#endif +#endif /* STATUS */ + int short_report=0; + int no_table_report=0; + server_rec *server = r->server; + short_score score_record; + char status[]="??????????"; + char stat_buffer[HARD_SERVER_LIMIT]; + clock_t tu,ts,tcu,tcs; + + tu=ts=tcu=tcs=0; + + status[SERVER_DEAD]='.'; /* We don't want to assume these are in */ + status[SERVER_READY]='_'; /* any particular order in scoreboard.h */ + status[SERVER_STARTING]='S'; + status[SERVER_BUSY_READ]='R'; + status[SERVER_BUSY_WRITE]='W'; + status[SERVER_BUSY_KEEPALIVE]='K'; + status[SERVER_BUSY_LOG]='L'; + status[SERVER_BUSY_DNS]='D'; + status[SERVER_GRACEFUL]='G'; + + if (!exists_scoreboard_image()) { + log_printf(r->server, "Server status unavailable in inetd mode"); + return HTTP_NOT_IMPLEMENTED; + } + r->allowed = (1 << M_GET) | (1 << M_TRACE); + if (r->method_number != M_GET) return HTTP_METHOD_NOT_ALLOWED; + + r->content_type = "text/html"; + + /* + * Simple table-driven form data set parser that lets you alter the header + */ + + if (r->args) + { + i = 0; + while (options[i].id != STAT_OPT_END) + { + if ((loc = strstr(r->args,options[i].form_data_str)) != NULL) + { + switch (options[i].id) + { + case STAT_OPT_REFRESH: + if(*(loc + strlen(options[i].form_data_str)) == '=') + table_set(r->headers_out,options[i].hdr_out_str, + loc+strlen(options[i].hdr_out_str)+1); + else + table_set(r->headers_out,options[i].hdr_out_str,"1"); + break; + case STAT_OPT_NOTABLE: + no_table_report = 1; + break; + case STAT_OPT_AUTO: + r->content_type = "text/plain"; + short_report = 1; + break; + } + } + i++; + } + } + + send_http_header(r); + + if (r->header_only) + return 0; + + sync_scoreboard_image(); + for (i = 0; i<HARD_SERVER_LIMIT; ++i) + { + score_record = get_scoreboard_info(i); + res = score_record.status; + stat_buffer[i] = status[res]; + if (res == SERVER_READY) + ready++; + else if (res == SERVER_BUSY_READ || res==SERVER_BUSY_WRITE || + res == SERVER_STARTING || res==SERVER_BUSY_KEEPALIVE || + res == SERVER_BUSY_LOG || res==SERVER_BUSY_DNS || + res == SERVER_GRACEFUL) + busy++; +#if defined(STATUS) + lres = score_record.access_count; + bytes= score_record.bytes_served; + if (lres!=0 || (score_record.status != SERVER_READY + && score_record.status != SERVER_DEAD)) + { + tu+=score_record.times.tms_utime; + ts+=score_record.times.tms_stime; + tcu+=score_record.times.tms_cutime; + tcs+=score_record.times.tms_cstime; + count+=lres; + bcount+=bytes; + if (bcount>=KBYTE) { + kbcount += (bcount >> 10); + bcount = bcount & 0x3ff; + } + } +#endif /* STATUS */ + } + + up_time=nowtime-restart_time; + + hard_timeout("send status info", r); + + if (!short_report) + { + rputs("<HTML><HEAD>\n<TITLE>Apache Status</TITLE>\n</HEAD><BODY>\n",r); + rputs("<H1>Apache Server Status for ",r); + rvputs(r,server->server_hostname,"</H1>\n\n",NULL); + rvputs(r,"Current Time: ",asctime(localtime(&nowtime)),"<br>\n",NULL); + rvputs(r,"Restart Time: ",asctime(localtime(&restart_time)),"<br>\n", + NULL); + rputs("Server uptime: ",r); + show_time(r,up_time); + rputs("<br>\n",r); + } + +#if defined(STATUS) + if (short_report) + { + rprintf(r,"Total Accesses: %lu\nTotal kBytes: %lu\n",count,kbcount); + +#ifndef __EMX__ + /* Allow for OS/2 not having CPU stats */ + if(ts || tu || tcu || tcs) + rprintf(r,"CPULoad: %g\n",(tu+ts+tcu+tcs)/tick/up_time*100.); +#endif + + rprintf(r,"Uptime: %ld\n",(long)(up_time)); + if (up_time>0) + rprintf(r,"ReqPerSec: %g\n",(float)count/(float)up_time); + + if (up_time>0) + rprintf(r,"BytesPerSec: %g\n",KBYTE*(float)kbcount/(float)up_time); + + if (count>0) + rprintf(r,"BytesPerReq: %g\n",KBYTE*(float)kbcount/(float)count); + } else /* !short_report */ + { + rprintf(r,"Total accesses: %lu - Total Traffic: ", count); + format_kbyte_out(r,kbcount); + +#ifndef __EMX__ + /* Allow for OS/2 not having CPU stats */ + rputs("<br>\n",r); + rprintf(r,"CPU Usage: u%g s%g cu%g cs%g", + tu/tick,ts/tick,tcu/tick,tcs/tick); + + if(ts || tu || tcu || tcs) + rprintf(r," - %.3g%% CPU load",(tu+ts+tcu+tcs)/tick/up_time*100.); +#endif + + rputs("<br>\n",r); + + if (up_time>0) + rprintf(r,"%.3g requests/sec - ", + (float)count/(float)up_time); + + if (up_time>0) + { + format_byte_out(r,KBYTE*(float)kbcount/(float)up_time); + rputs("/second - ",r); + } + + if (count>0) + { + format_byte_out(r,KBYTE*(float)kbcount/(float)count); + rputs("/request",r); + } + + rputs("<br>\n",r); + } /* short_report */ +#endif /* STATUS */ + + if (!short_report) + rprintf(r,"\n%d requests currently being processed, %d idle servers\n" + ,busy,ready); + else + rprintf(r,"BusyServers: %d\nIdleServers: %d\n",busy,ready); + + /* send the scoreboard 'table' out */ + + if(!short_report) + rputs("<PRE>",r); + else + rputs("Scoreboard: ",r); + + for (i = 0; i<HARD_SERVER_LIMIT; ++i) + { + rputc(stat_buffer[i], r); + if((i%STATUS_MAXLINE == (STATUS_MAXLINE - 1))&&!short_report) + rputs("\n",r); + } + + if (short_report) + rputs("\n",r); + else { + rputs("</PRE>\n",r); + rputs("Scoreboard Key: <br>\n",r); + rputs("\"<B><code>_</code></B>\" Waiting for Connection, \n",r); + rputs("\"<B><code>S</code></B>\" Starting up, \n",r); + rputs("\"<B><code>R</code></B>\" Reading Request,<BR>\n",r); + rputs("\"<B><code>W</code></B>\" Sending Reply, \n",r); + rputs("\"<B><code>K</code></B>\" Keepalive (read), \n",r); + rputs("\"<B><code>D</code></B>\" DNS Lookup,<BR>\n",r); + rputs("\"<B><code>L</code></B>\" Logging, \n",r); + rputs("\"<B><code>G</code></B>\" Gracefully finishing, \n",r); + rputs("\"<B><code>.</code></B>\" Open slot with no current process<P>\n",r); + } + +#if defined(STATUS) + if (!short_report) + if(no_table_report) + rputs("<p><hr><h2>Server Details</h2>\n\n",r); + else +#ifdef __EMX__ + /* Allow for OS/2 not having CPU stats */ + rputs("<p>\n\n<table border=0><tr><th>Srv<th>PID<th>Acc<th>M\n<th>SS<th>Conn<th>Child<th>Slot<th>Host<th>VHost<th>Request</tr>\n\n",r); +#else + rputs("<p>\n\n<table border=0><tr><th>Srv<th>PID<th>Acc<th>M<th>CPU\n<th>SS<th>Conn<th>Child<th>Slot<th>Host<th>VHost<th>Request</tr>\n\n",r); +#endif + + + for (i = 0; i<HARD_SERVER_LIMIT; ++i) + { + score_record=get_scoreboard_info(i); + lres = score_record.access_count; + my_lres = score_record.my_access_count; + conn_lres = score_record.conn_count; + bytes= score_record.bytes_served; + my_bytes = score_record.my_bytes_served; + conn_bytes = score_record.conn_bytes; + if (lres!=0 || (score_record.status != SERVER_READY + && score_record.status != SERVER_DEAD)) + { + if (!short_report) + { + if (no_table_report) + { + rprintf(r,"<b>Server %d</b> (%d): %d|%lu|%lu [", + i,(int)score_record.pid,(int)conn_lres,my_lres,lres); + + switch (score_record.status) + { + case SERVER_READY: + rputs("Ready",r); + break; + case SERVER_STARTING: + rputs("Starting",r); + break; + case SERVER_BUSY_READ: + rputs("<b>Read</b>",r); + break; + case SERVER_BUSY_WRITE: + rputs("<b>Write</b>",r); + break; + case SERVER_BUSY_KEEPALIVE: + rputs("<b>Keepalive</b>",r); + break; + case SERVER_BUSY_LOG: + rputs("<b>Logging</b>",r); + break; + case SERVER_BUSY_DNS: + rputs("<b>DNS lookup</b>",r); + break; + case SERVER_DEAD: + rputs("Dead",r); + break; + case SERVER_GRACEFUL: + rputs("Graceful",r); + break; + default: + rputs("?STATE?",r); + break; + } +#ifdef __EMX__ + /* Allow for OS/2 not having CPU stats */ + rprintf(r,"]\n %s (", +#else + + rprintf(r,"] u%g s%g cu%g cs%g\n %s (", + score_record.times.tms_utime/tick, + score_record.times.tms_stime/tick, + score_record.times.tms_cutime/tick, + score_record.times.tms_cstime/tick, +#endif + asctime(localtime(&score_record.last_used))); + format_byte_out(r,conn_bytes); + rputs("|",r); + format_byte_out(r,my_bytes); + rputs("|",r); + format_byte_out(r,bytes); + rputs(")\n",r); + rprintf(r," <i>%s {%s}</i><br>\n\n", + score_record.client, + escape_html(r->pool, score_record.request)); + } + else /* !no_table_report */ + { + rprintf(r,"<tr><td><b>%d</b><td>%d<td>%d/%lu/%lu", + i,(int)score_record.pid,(int)conn_lres,my_lres,lres); + + switch (score_record.status) + { + case SERVER_READY: + rputs("<td>_",r); + break; + case SERVER_STARTING: + rputs("<td><b>S</b>",r); + break; + case SERVER_BUSY_READ: + rputs("<td><b>R</b>",r); + break; + case SERVER_BUSY_WRITE: + rputs("<td><b>W</b>",r); + break; + case SERVER_BUSY_KEEPALIVE: + rputs("<td><b>K</b>",r); + break; + case SERVER_BUSY_LOG: + rputs("<td><b>L</b>",r); + break; + case SERVER_BUSY_DNS: + rputs("<td><b>D</b>",r); + break; + case SERVER_DEAD: + rputs("<td>.",r); + break; + case SERVER_GRACEFUL: + rputs("<td>G",r); + break; + default: + rputs("<td>?",r); + break; + } +#ifdef __EMX__ + /* Allow for OS/2 not having CPU stats */ + rprintf(r,"\n<td>%.0f", +#else + rprintf(r,"\n<td>%.2f<td>%.0f", + (score_record.times.tms_utime + + score_record.times.tms_stime + + score_record.times.tms_cutime + + score_record.times.tms_cstime)/tick, +#endif + difftime(nowtime, score_record.last_used)); + rprintf(r,"<td>%-1.1f<td>%-2.2f<td>%-2.2f\n", + (float)conn_bytes/KBYTE, (float)my_bytes/MBYTE, + (float)bytes/MBYTE); + rprintf(r,"<td>%s<td nowrap>%s<td nowrap>%s</tr>\n\n", + score_record.client, score_record.vhost, + escape_html(r->pool, score_record.request)); + } /* no_table_report */ + } /* !short_report */ + } /* if (<active child>) */ + } /* for () */ + + if (!(short_report || no_table_report)) + { +#ifdef __EMX__ + rputs("</table>\n \ +<hr> \ +<table>\n \ +<tr><th>Srv<td>Server number\n \ +<tr><th>PID<td>OS process ID\n \ +<tr><th>Acc<td>Number of accesses this connection / this child / this slot\n \ +<tr><th>M<td>Mode of operation\n \ +<tr><th>SS<td>Seconds since beginning of most recent request\n \ +<tr><th>Conn<td>Kilobytes transferred this connection\n \ +<tr><th>Child<td>Megabytes transferred this child\n \ +<tr><th>Slot<td>Total megabytes transferred this slot\n \ +</table>\n",r); +#else + rputs("</table>\n \ +<hr> \ +<table>\n \ +<tr><th>Srv<td>Server number\n \ +<tr><th>PID<td>OS process ID\n \ +<tr><th>Acc<td>Number of accesses this connection / this child / this slot\n \ +<tr><th>M<td>Mode of operation\n \ +<tr><th>CPU<td>CPU usage, number of seconds\n \ +<tr><th>SS<td>Seconds since beginning of most recent request\n \ +<tr><th>Conn<td>Kilobytes transferred this connection\n \ +<tr><th>Child<td>Megabytes transferred this child\n \ +<tr><th>Slot<td>Total megabytes transferred this slot\n \ +</table>\n",r); +#endif + } + +#else /* !defined(STATUS) */ + + rputs("<hr>To obtain a full report with current status information and",r); + rputs(" DNS and LOGGING status codes \n",r); + rputs("you need to recompile Apache after adding the line <pre>",r); + rputs("Rule STATUS=yes</pre>into the file <code>Configuration</code>\n",r); + +#endif /* STATUS */ + + if (!short_report) + rputs("</BODY></HTML>\n",r); + + kill_timeout(r); + return 0; +} + +handler_rec status_handlers[] = +{ +{ STATUS_MAGIC_TYPE, status_handler }, +{ "server-status", status_handler }, +{ NULL } +}; + +module status_module = +{ + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + NULL, /* command table */ + status_handlers, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_userdir.c b/usr.sbin/httpd/src/mod_userdir.c new file mode 100644 index 00000000000..d251e92b60b --- /dev/null +++ b/usr.sbin/httpd/src/mod_userdir.c @@ -0,0 +1,214 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * mod_userdir... implement the UserDir command. Broken away from the + * Alias stuff for a couple of good and not-so-good reasons: + * + * 1) It shows a real minimal working example of how to do something like + * this. + * 2) I know people who are actually interested in changing this *particular* + * aspect of server functionality without changing the rest of it. That's + * what this whole modular arrangement is supposed to be good at... + * + * Modified by Alexei Kosut to support the following constructs + * (server running at www.foo.com, request for /~bar/one/two.html) + * + * UserDir public_html -> ~bar/public_html/one/two.html + * UserDir /usr/web -> /usr/web/bar/one/two.html + * UserDir /home/ * /www -> /home/bar/www/one/two.html + * NOTE: theses ^ ^ space only added allow it to work in a comment, ignore + * UserDir http://x/users -> (302) http://x/users/bar/one/two.html + * UserDir http://x/ * /y -> (302) http://x/bar/y/one/two.html + * NOTE: here also ^ ^ + * + * In addition, you can use multiple entries, to specify alternate + * user directories (a la Directory Index). For example: + * + * UserDir public_html /usr/web http://www.xyz.com/users + * + */ + +#include "httpd.h" +#include "http_config.h" + +module userdir_module; + +/* + * Sever config for this module is a little unconventional... + * It's just one string anyway, so why pretend? + */ + +void *create_userdir_config (pool *dummy, server_rec *s) { + return (void*)DEFAULT_USER_DIR; +} + +const char *set_user_dir (cmd_parms *cmd, void *dummy, char *arg) +{ + void *server_conf = cmd->server->module_config; + + set_module_config (server_conf, &userdir_module, pstrdup (cmd->pool, arg)); + return NULL; +} + +command_rec userdir_cmds[] = { +{ "UserDir", set_user_dir, NULL, RSRC_CONF, RAW_ARGS, + "the public subdirectory in users' home directories, or 'disabled'" }, +{ NULL } +}; + +int translate_userdir (request_rec *r) +{ + void *server_conf = r->server->module_config; + const char *userdirs = (char *)get_module_config(server_conf, + &userdir_module); + char *name = r->uri; + const char *w, *dname, *redirect; + char *x = NULL; + struct stat statbuf; + + if (userdirs == NULL || !strcasecmp(userdirs, "disabled") || + (name[0] != '/') || (name[1] != '~')) { + return DECLINED; + } + + while (*userdirs) { + const char *userdir = getword_conf (r->pool, &userdirs); + char *filename = NULL; + + dname = name + 2; + w = getword(r->pool, &dname, '/'); + + /* disallow the empty username, . and .. */ + if (w[0] == '\0' || (w[1] == '.' && (w[2] == '\0' || (w[2] == '.' && w[3] == '\0')))) + return DECLINED; + + /* The 'dname' funny business involves backing it up to capture + * the '/' delimiting the "/~user" part from the rest of the URL, + * in case there was one (the case where there wasn't being just + * "GET /~user HTTP/1.0", for which we don't want to tack on a + * '/' onto the filename). + */ + + if (dname[-1] == '/') --dname; + + if (strchr(userdir, '*')) + x = getword(r->pool, &userdir, '*'); + +#ifdef __EMX__ + /* Add support for OS/2 drive letters */ + if ((userdir[0] == '/') || (userdir[1] == ':') || (userdir[0] == '\0')) { +#else + if ((userdir[0] == '/') || (userdir[0] == '\0')) { +#endif + if (x) { + if (strchr(x, ':')) { + redirect = pstrcat(r->pool, x, w, userdir, dname, NULL); + table_set (r->headers_out, "Location", redirect); + return REDIRECT; + } + else + filename = pstrcat (r->pool, x, w, userdir, NULL); + } + else + filename = pstrcat (r->pool, userdir, "/", w, NULL); + } + else if (strchr(userdir, ':')) { + redirect = pstrcat(r->pool, userdir, "/", w, dname, NULL); + table_set (r->headers_out, "Location", redirect); + return REDIRECT; + } + else { + struct passwd *pw; + if((pw=getpwnam(w))) +#ifdef __EMX__ + /* Need to manually add user name for OS/2 */ + filename = pstrcat (r->pool, pw->pw_dir, w, "/", userdir, NULL); +#else + filename = pstrcat (r->pool, pw->pw_dir, "/", userdir, NULL); +#endif + + } + + /* Now see if it exists, or we're at the last entry. If we are at the + last entry, then use the filename generated (if there is one) anyway, + in the hope that some handler might handle it. This can be used, for + example, to run a CGI script for the user. + */ + if (filename && (!*userdirs || stat(filename, &statbuf) != -1)) { + r->filename = pstrcat(r->pool, filename, dname, NULL); + r->finfo = statbuf; + return OK; + } + } + + return DECLINED; +} + +module userdir_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_userdir_config, /* server config */ + NULL, /* merge server config */ + userdir_cmds, /* command table */ + NULL, /* handlers */ + translate_userdir, /*filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + NULL, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/mod_usertrack.c b/usr.sbin/httpd/src/mod_usertrack.c new file mode 100644 index 00000000000..5be43124151 --- /dev/null +++ b/usr.sbin/httpd/src/mod_usertrack.c @@ -0,0 +1,328 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* User Tracking Module (Was mod_cookies.c) + * + * This Apache module is designed to track users paths through a site. + * It uses the client-side state ("Cookie") protocol developed by Netscape. + * It is known to work on Netscape browsers, Microsoft Internet + * Explorer and others currently being developed. + * + * Each time a page is requested we look to see if the browser is sending + * us a Cookie: header that we previously generated. + * + * If we don't find one then the user hasn't been to this site since + * starting their browser or their browser doesn't support cookies. So + * we generate a unique Cookie for the transaction and send it back to + * the browser (via a "Set-Cookie" header) + * Future requests from the same browser should keep the same Cookie line. + * + * By matching up all the requests with the same cookie you can + * work out exactly what path a user took through your site. To log + * the cookie use the " %{Cookie}n " directive in a custom access log; + * + * Example 1 : If you currently use the standard Log file format (CLF) + * and use the command "TransferLog somefilename", add the line + * LogFormat "%h %l %u %t \"%r\" %s %b %{Cookie}n" + * to your config file. + * + * Example 2 : If you used to use the old "CookieLog" directive, you + * can emulate it by adding the following command to your config file + * CustomLog filename "%{Cookie}n \"%r\" %t" + * + * Notes: + * 1. This code now logs the initial transaction (the one that created + * the cookie to start with). + * 2. This module has been designed to not interfere with other Cookies + * your site may be using; just avoid sending out cookies with + * the name "Apache=" or things will get confused. + * 3. If you want you can modify the Set-Cookie line so that the Cookie + * never expires. You would then get the same Cookie each time the + * user revisits your site. + * + * Mark Cox, mark@ukweb.com, 6 July 95 + * + * This file replaces mod_cookies.c + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#ifndef MPE +#include <sys/time.h> +#endif + +module usertrack_module; + +typedef struct { + int always; + time_t expires; +} cookie_log_state; + +/* Define this to allow post-2000 cookies. Cookies use two-digit dates, + * so it might be dicey. (Netscape does it correctly, but others may not) + */ +#define MILLENIAL_COOKIES + +/* Make Cookie: Now we have to generate something that is going to be + * pretty unique. We can base it on the pid, time, hostip */ + +#define COOKIE_NAME "Apache=" + +void make_cookie(request_rec *r) +{ + cookie_log_state *cls = get_module_config (r->server->module_config, + &usertrack_module); +#ifdef MPE + clock_t mpe_times; + struct tms mpe_tms; +#else + struct timeval tv; + struct timezone tz = { 0 , 0 }; +#endif + /* 1024 == hardcoded constants */ + char *new_cookie = palloc( r->pool, 1024); + char *cookiebuf = palloc( r->pool, 1024); + char *dot; + const char *rname = pstrdup(r->pool, + get_remote_host(r->connection, r->per_dir_config, + REMOTE_NAME)); + + if ((dot = strchr(rname,'.'))) *dot='\0'; /* First bit of hostname */ + +#ifdef MPE +/* MPE lacks gettimeofday(), so we must use time() to obtain the epoch + seconds, and then times() to obtain CPU clock ticks (milliseconds). + Combine this together to obtain a hopefully unique cookie ID. */ + + mpe_times=times(&mpe_tms); + + ap_snprintf(cookiebuf, 1024, "%s%d%ld%ld", rname, (int)getpid(), + (long)time(NULL), (long)mpe_tms.tms_utime); +#else + gettimeofday(&tv, &tz); + + ap_snprintf(cookiebuf, 1024, "%s%d%ld%d", rname, (int)getpid(), + (long)tv.tv_sec, (int)tv.tv_usec/1000); +#endif + + if (cls->expires) { + static const char *const days[7]= + {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + struct tm *tms; + time_t when = time(NULL) + cls->expires; + +#ifndef MILLENIAL_COOKIES + /* Only two-digit date string, so we can't trust "00" or more. + * Therefore, we knock it all back to just before midnight on + * 1/1/2000 (which is 946684799) + */ + + if (when > 946684799) + when = 946684799; +#endif + tms = gmtime(&when); + + /* Cookie with date; as strftime '%a, %d-%h-%y %H:%M:%S GMT' */ + ap_snprintf(new_cookie, 1024, + "%s%s; path=/; expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT", + COOKIE_NAME, cookiebuf, days[tms->tm_wday], + tms->tm_mday, month_snames[tms->tm_mon], + (tms->tm_year >= 100) ? tms->tm_year - 100 : tms->tm_year, + tms->tm_hour, tms->tm_min, tms->tm_sec); + } + else + ap_snprintf(new_cookie, 1024, "%s%s; path=/", COOKIE_NAME, cookiebuf); + + table_set(r->headers_out,"Set-Cookie",new_cookie); + table_set(r->notes, "cookie", cookiebuf); /* log first time */ + return; +} + +int spot_cookie(request_rec *r) +{ + int *enable = (int *)get_module_config(r->per_dir_config, + &usertrack_module); + char *cookie; + char *value; + + if (!*enable) return DECLINED; + + if ((cookie = table_get (r->headers_in, "Cookie"))) + if ((value=strstr(cookie,COOKIE_NAME))) { + char *cookiebuf, *cookieend; + + value+=strlen(COOKIE_NAME); + cookiebuf=pstrdup( r->pool, value ); + cookieend=strchr(cookiebuf,';'); + if (cookieend) *cookieend='\0'; /* Ignore anything after a ; */ + + /* Set the cookie in a note, for logging */ + table_set(r->notes, "cookie", cookiebuf); + + return DECLINED; /* Theres already a cookie, no new one */ + } + make_cookie(r); + return OK; /* We set our cookie */ +} + +void *make_cookie_log_state (pool *p, server_rec *s) +{ + cookie_log_state *cls = + (cookie_log_state *)palloc (p, sizeof (cookie_log_state)); + + cls->expires = 0; + + return (void *)cls; +} + +void *make_cookie_dir (pool *p, char *d) { + return (void *)pcalloc(p, sizeof(int)); +} + +const char *set_cookie_enable (cmd_parms *cmd, int *c, int arg) +{ + *c = arg; + return NULL; +} + +const char *set_cookie_exp (cmd_parms *parms, void *dummy, const char *arg) +{ + cookie_log_state *cls = get_module_config (parms->server->module_config, + &usertrack_module); + time_t factor, modifier = 0; + time_t num = 0; + char *word; + + /* The simple case first - all numbers (we assume) */ + if (isdigit(arg[0]) && isdigit(arg[strlen(arg)-1])) { + cls->expires = atol(arg); + return NULL; + } + + /* The harder case - stolen from mod_expires + * CookieExpires "[plus] {<num> <type>}*" + */ + + word = getword_conf( parms->pool, &arg ); + if ( !strncasecmp( word, "plus", 1 ) ) { + word = getword_conf( parms->pool, &arg ); + }; + + /* {<num> <type>}* */ + while ( word[0] ) { + /* <num> */ + if ( strchr("0123456789", word[0]) != NULL ) + num = atoi( word ); + else + return "bad expires code, numeric value expected."; + + /* <type> */ + word = getword_conf( parms->pool, &arg ); + if (!word[0] ) + return "bad expires code, missing <type>"; + + factor = 0; + if ( !strncasecmp( word, "years", 1 ) ) + factor = 60*60*24*365; + else if ( !strncasecmp( word, "months", 2 ) ) + factor = 60*60*24*30; + else if ( !strncasecmp( word, "weeks", 1 ) ) + factor = 60*60*24*7; + else if ( !strncasecmp( word, "days", 1 ) ) + factor = 60*60*24; + else if ( !strncasecmp( word, "hours", 1 ) ) + factor = 60*60; + else if ( !strncasecmp( word, "minutes", 2 ) ) + factor = 60; + else if ( !strncasecmp( word, "seconds", 1 ) ) + factor = 1; + else + return "bad expires code, unrecognized type"; + + modifier = modifier + factor * num; + + /* next <num> */ + word = getword_conf( parms->pool, &arg ); + } + + cls->expires = modifier; + + return NULL; +} + +command_rec cookie_log_cmds[] = { +{ "CookieExpires", set_cookie_exp, NULL, RSRC_CONF, TAKE1, + "an expiry date code" }, +{ "CookieTracking", set_cookie_enable, NULL, OR_FILEINFO, FLAG, + "whether or not to enable cookies" }, +{ NULL } +}; + +module usertrack_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + make_cookie_dir, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + make_cookie_log_state, /* server config */ + NULL, /* merge server configs */ + cookie_log_cmds, /* command table */ + NULL, /* handlers */ + NULL, /* filename translation */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + spot_cookie, /* fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; diff --git a/usr.sbin/httpd/src/modules/example/Makefile b/usr.sbin/httpd/src/modules/example/Makefile new file mode 100644 index 00000000000..9bec391a3bf --- /dev/null +++ b/usr.sbin/httpd/src/modules/example/Makefile @@ -0,0 +1,107 @@ +# ==================================================================== +# Copyright (c) 1995-1997 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. 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://www.apache.org/>. +# +# Makefile for the Apache example module +# + +# +# This normally lives in modules/example under the Apache source +# directory. If the depth or relationship changes, the following line +# may need to be changed. +# +INCDIR=../.. + +# +# Everything below this point should be invariant. +# +SHELL=/bin/sh + +# +# We inherit the definitions of CC, AUX_CFLAGS, and RANLIB from an +# upline make(1) call. +# +CFLAGS=-I$(INCDIR) $(AUX_CFLAGS) + +MODULES=mod_example.o +OBJS= \ + $(MODULES) + +# +# Now the rules saying how things are built. +# +.c.o: + $(CC) -c $(CFLAGS) $< + +all: $(OBJS) + +clean: + rm -f $(OBJS) + +# +# Finally, what depnds upon which, so make can figure out what it needs +# to do. +# + +# +# Make sure that things get rebuilt if the Makefiles are changed. +# +$(OBJS): \ + Makefile \ + $(INCDIR)/Makefile + +mod_example.o: \ + $(INCDIR)/httpd.h \ + $(INCDIR)/http_config.h \ + $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h \ + $(INCDIR)/http_main.h \ + $(INCDIR)/http_protocol.h \ + $(INCDIR)/util_script.h \ + mod_example.c diff --git a/usr.sbin/httpd/src/modules/example/README b/usr.sbin/httpd/src/modules/example/README new file mode 100644 index 00000000000..77abc097c00 --- /dev/null +++ b/usr.sbin/httpd/src/modules/example/README @@ -0,0 +1,53 @@ +README for Apache 1.2 Example Module +[April, 1997] + +The files in the src/modules/example directory under the Apache +distribution directory tree are provided as an example to those that +wish to write modules that use the Apache API. + +The main file is mod_example.c, which illustrates all the different +callback mechanisms and call syntaces. By no means does an add-on +module need to include routines for all of the callbacks - quite the +contrary! + +The example module is an actual working module. If you link it into +your server, enable the "example-handler" handler for a location, and then +browse to that location, you will see a display of some of the tracing +the example module did as the various callbacks were made. + +To include the example module in your server, follow the steps below: + + 1. Uncomment the "Module example_module" line near the bottom of + the src/Configuration file. If there isn't one, add it; it + should look like this: + + Module example_module modules/example/mod_example.o + + 2. Run the src/Configure script ("cd src; ./Configure"). This will + build the Makefile for the server itself, and update the + src/modules/Makefile for any additional modules you have + requested from beneath that subdirectory. + + 3. Make the server (run "make" in the src directory). + +To add another module of your own: + + A. mkdir src/modules/mymodule + B. cp src/modules/example/* src/modules/mymodule + C. Modify the files in the new directory + D. Follow steps [1] through [3] above, with appropriate changes. + +To activate the example module, include a block similar to the +following in your srm.conf file: + + <Location /example-info> + SetHandler example-handler + </Location> + +As an alternative, you can put the following into a .htaccess file and +then request the file "test.example" from that location: + + AddHandler example-handler .example + +After reloading/restarting your server, you should be able to browse +to this location and see the brief display mentioned earlier. diff --git a/usr.sbin/httpd/src/modules/example/mod_example.c b/usr.sbin/httpd/src/modules/example/mod_example.c new file mode 100644 index 00000000000..1aeda876d01 --- /dev/null +++ b/usr.sbin/httpd/src/modules/example/mod_example.c @@ -0,0 +1,1110 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * Apache example module. Provide demonstrations of how modules do things. + * + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_main.h" +#include "http_protocol.h" +#include "util_script.h" + +#include <stdio.h> + +/*--------------------------------------------------------------------------*/ +/* */ +/* Data declarations. */ +/* */ +/* Here are the static cells and structure declarations private to our */ +/* module. */ +/* */ +/*--------------------------------------------------------------------------*/ + +/* + * Sample configuration record. Used for both per-directory and per-server + * configuration data. + * + * It's perfectly reasonable to have two different structures for the two + * different environments. The same command handlers will be called for + * both, though, so the handlers need to be able to tell them apart. One + * possibility is for both structures to start with an int which is zero for + * one and 1 for the other. + * + * Note that while the per-directory and per-server configuration records are + * available to most of the module handlers, they should be treated as + * READ-ONLY by all except the command and merge handlers. Sometimes handlers + * are handed a record that applies to the current location by implication or + * inheritance, and modifying it will change the rules for other locations. + */ +typedef struct example_config { + int cmode; /* Environment to which record applies (directory, */ + /* server, or combination). */ +#define CONFIG_MODE_SERVER 1 +#define CONFIG_MODE_DIRECTORY 2 +#define CONFIG_MODE_COMBO 3 /* Shouldn't ever happen. */ + int local; /* Boolean: was "Example" directive declared here? */ + int congenital; /* Boolean: did we inherit an "Example"? */ + char *trace; /* Pointer to trace string. */ + char *loc; /* Location to which this record applies. */ +} example_config; + +/* + * Let's set up a module-local static cell to point to the accreting callback + * trace. As each API callback is made to us, we'll tack on the particulars + * to whatever we've already recorded. To avoid massive memory bloat as + * directories are walked again and again, we record the routine/environment + * the first time (non-request context only), and ignore subsequent calls for + * the same routine/environment. + */ +static char *trace = NULL; +static table *static_calls_made = NULL; + +/* + * To avoid leaking memory from pools other than the per-request one, we + * allocate a module-private pool, and then use a sub-pool of that which gets + * freed each time we modify the trace. That way previous layers of trace + * data don't get lost. + */ +static pool *example_pool = NULL; +static pool *example_subpool = NULL; + +/* + * Declare ourselves so the configuration routines can find and know us. + * We'll fill it in at the end of the module. + */ +module example_module; + +/*--------------------------------------------------------------------------*/ +/* */ +/* The following pseudo-prototype declarations illustrate the parameters */ +/* passed to command handlers for the different types of directive */ +/* syntax. If an argument was specified in the directive definition */ +/* (look for "command_rec" below), it's available to the command handler */ +/* via the (void *) info field in the cmd_parms argument passed to the */ +/* handler (cmd->info for the examples below). */ +/* */ +/*--------------------------------------------------------------------------*/ + +/* + * Command handler for a NO_ARGS directive. + * + * static const char *handle_NO_ARGS + * (cmd_parms *cmd, void *mconfig); + */ + +/* + * Command handler for a RAW_ARGS directive. The "args" argument is the text + * of the commandline following the directive itself. + * + * static const char *handle_RAW_ARGS + * (cmd_parms *cmd, void *mconfig, const char *args); + */ + +/* + * Command handler for a TAKE1 directive. The single parameter is passed in + * "word1". + * + * static const char *handle_TAKE1 + * (cmd_parms *cmd, void *mconfig, char *word1); + */ + +/* + * Command handler for a TAKE2 directive. TAKE2 commands must always have + * exactly two arguments. + * + * static const char *handle_TAKE2 + * (cmd_parms *cmd, void *mconfig, char *word1, char *word2); + */ + +/* + * Command handler for a TAKE3 directive. Like TAKE2, these must have exactly + * three arguments, or the parser complains and doesn't bother calling us. + * + * static const char *handle_TAKE3 + * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a TAKE12 directive. These can take either one or two + * arguments. + * - word2 is a NULL pointer if no second argument was specified. + * + * static const char *handle_TAKE12 + * (cmd_parms *cmd, void *mconfig, char *word1, char *word2); + */ + +/* + * Command handler for a TAKE123 directive. A TAKE123 directive can be given, + * as might be expected, one, two, or three arguments. + * - word2 is a NULL pointer if no second argument was specified. + * - word3 is a NULL pointer if no third argument was specified. + * + * static const char *handle_TAKE123 + * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a TAKE13 directive. Either one or three arguments are + * permitted - no two-parameters-only syntax is allowed. + * - word2 and word3 are NULL pointers if only one argument was specified. + * + * static const char *handle_TAKE13 + * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a TAKE23 directive. At least two and as many as three + * arguments must be specified. + * - word3 is a NULL pointer if no third argument was specified. + * + * static const char *handle_TAKE23 + * (cmd_parms *cmd, void *mconfig, char *word1, char *word2, char *word3); + */ + +/* + * Command handler for a ITERATE directive. + * - Handler is called once for each of n arguments given to the directive. + * - word1 points to each argument in turn. + * + * static const char *handle_ITERATE + * (cmd_parms *cmd, void *mconfig, char *word1); + */ + +/* + * Command handler for a ITERATE2 directive. + * - Handler is called once for each of the second and subsequent arguments + * given to the directive. + * - word1 is the same for each call for a particular directive instance (the + * first argument). + * - word2 points to each of the second and subsequent arguments in turn. + * + * static const char *handle_ITERATE2 + * (cmd_parms *cmd, void *mconfig, char *word1, char *word2); + */ + +/*--------------------------------------------------------------------------*/ +/* */ +/* These routines are strictly internal to this module, and support its */ +/* operation. They are not referenced by any external portion of the */ +/* server. */ +/* */ +/*--------------------------------------------------------------------------*/ + +/* + * Locate our directory configuration record for the current request. + */ +static example_config *our_dconfig + (request_rec *r) { + + return (example_config *) get_module_config + ( + r->per_dir_config, + &example_module + ); +} + +/* + * Locate our server configuration record for the specified server. + */ +static example_config *our_sconfig + (server_rec *s) { + + return (example_config *) get_module_config + ( + s->module_config, + &example_module + ); +} + +/* + * Likewise for our configuration record for the specified request. + */ +static example_config *our_rconfig + (request_rec *r) { + + return (example_config *) get_module_config + ( + r->request_config, + &example_module + ); +} + +/* + * This routine sets up some module-wide cells if they haven't been already. + */ +static void setup_module_cells () { + /* + * If we haven't already allocated our module-private pool, do so now. + */ + if (example_pool == NULL) { + example_pool = make_sub_pool (NULL); + }; + /* + * Likewise for the table of routine/environment pairs we visit outside of + * request context. + */ + if (static_calls_made == NULL) { + static_calls_made = make_table (example_pool, 16); + }; +} + +/* + * This routine is used to add a trace of a callback to the list. We're + * passed the server record (if available), the request record (if available), + * a pointer to our private configuration record (if available) for the + * environment to which the callback is supposed to apply, and some text. We + * turn this into a textual representation and add it to the tail of the list. + * The list can be displayed by the example_handler() routine. + * + * If the call occurs within a request context (i.e., we're passed a request + * record), we put the trace into the request pool and attach it to the + * request via the notes mechanism. Otherwise, the trace gets added + * to the static (non-request-specific) list. + * + * Note that the r->notes table is only for storing strings; if you need to + * maintain per-request data of any other type, you need to use another + * mechanism. + */ + +#define TRACE_NOTE "example-trace" + +static void trace_add + (server_rec *s, request_rec *r, example_config *mconfig, + const char *note) { + + char *sofar; + char *addon; + char *where; + pool *p; + char *trace_copy; + example_config + *rconfig; + + /* + * Make sure our pools and tables are set up - we need 'em. + */ + setup_module_cells (); + /* + * Now, if we're in request-context, we use the request pool. + */ + if (r != NULL) { + p = r->pool; + if ((trace_copy = table_get (r->notes, TRACE_NOTE)) == NULL) { + trace_copy = ""; + } + } else { + /* + * We're not in request context, so the trace gets attached to our + * module-wide pool. We do the create/destroy every time we're called + * in non-request context; this avoids leaking memory in some of + * the subsequent calls that allocate memory only once (such as the + * key formation below). + * + * Make a new sub-pool and copy any existing trace to it. Point the + * trace cell at the copied value. + */ + p = make_sub_pool (example_pool); + if (trace != NULL) { + trace = pstrdup (p, trace); + } + /* + * Now, if we have a sub-pool from before, nuke it and replace with + * the one we just allocated. + */ + if (example_subpool != NULL) { + destroy_pool (example_subpool); + } + example_subpool = p; + trace_copy = trace; + } + /* + * If we weren't passed a configuration record, we can't figure out to + * what location this call applies. This only happens for co-routines + * that don't operate in a particular directory or server context. If we + * got a valid record, extract the location (directory or server) to which + * it applies. + */ + where = (mconfig != NULL) ? mconfig->loc : "nowhere"; + where = (where != NULL) ? where : ""; + /* + * Now, if we're not in request context, see if we've been called with + * this particular combination before. The table is allocated in the + * module's private pool, which doesn't get destroyed. + */ + if (r == NULL) { + char *key; + + key = pstrcat (p, note, ":", where, NULL); + if (table_get (static_calls_made, key) != NULL) { + /* + * Been here, done this. + */ + return; + } else { + /* + * First time for this combination of routine and environment - + * log it so we don't do it again. + */ + table_set (static_calls_made, key, "been here"); + } + } + addon = pstrcat + ( + p, + " <LI>\n", + " <DL>\n", + " <DT><SAMP>", + note, + "</SAMP>\n", + " </DT>\n", + " <DD><SAMP>[", + where, + "]</SAMP>\n", + " </DD>\n", + " </DL>\n", + " </LI>\n", + NULL + ); + sofar = (trace_copy == NULL) ? "" : trace_copy; + trace_copy = pstrcat (p, sofar, addon, NULL); + if (r != NULL) { + table_set (r->notes, TRACE_NOTE, trace_copy); + } else { + trace = trace_copy; + } + /* + * You *could* uncomment the following if you wanted to see the calling + * sequence reported in the server's error_log, but beware - almost all of + * these co-routines are called for every single request, and the impact + * on the size (and readability) of the error_log is considerable. + */ +/* + if (s != NULL) { + log_printf (s, "mod_example: %s", note); + } + */ +} + +/*--------------------------------------------------------------------------*/ +/* We prototyped the various syntax for command handlers (routines that */ +/* are called when the configuration parser detects a directive declared */ +/* by our module) earlier. Now we actually declare a "real" routine that */ +/* will be invoked by the parser when our "real" directive is */ +/* encountered. */ +/* */ +/* If a command handler encounters a problem processing the directive, it */ +/* signals this fact by returning a non-NULL pointer to a string */ +/* describing the problem. */ +/* */ +/* The magic return value DECLINE_CMD is used to deal with directives */ +/* that might be declared by multiple modules. If the command handler */ +/* returns NULL, the directive was processed; if it returns DECLINE_CMD, */ +/* the next module (if any) that declares the directive is given a chance */ +/* at it. If it returns any other value, it's treated as the text of an */ +/* error message. */ +/*--------------------------------------------------------------------------*/ +/* + * Command handler for the NO_ARGS "Example" directive. All we do is mark the + * call in the trace log, and flag the applicability of the directive to the + * current location in that location's configuration record. + */ +static const char *cmd_example + (cmd_parms *cmd, void *mconfig) { + + example_config + *cfg = (example_config *) mconfig; + + /* + * "Example Wuz Here" + */ + cfg->local = 1; + trace_add (cmd->server, NULL, cfg, "cmd_example()"); + return NULL; +} + +/*--------------------------------------------------------------------------*/ +/* */ +/* Now we declare our content handlers, which are invoked when the server */ +/* encounters a document which our module is supposed to have a chance to */ +/* see. (See mod_mime's SetHandler and AddHandler directives, and the */ +/* mod_info and mod_status examples, for more details.) */ +/* */ +/* Since content handlers are dumping data directly into the connexion */ +/* (using the r*() routines, such as rputs() and rprintf()) without */ +/* intervention by other parts of the server, they need to make */ +/* sure any accumulated HTTP headers are sent first. This is done by */ +/* calling send_http_header(). Otherwise, no header will be sent at all, */ +/* and the output sent to the client will actually be HTTP-uncompliant. */ +/*--------------------------------------------------------------------------*/ +/* + * Sample content handler. All this does is display the call list that has + * been built up so far. + * + * The return value instructs the caller concerning what happened and what to + * do next: + * OK ("we did our thing") + * DECLINED ("this isn't something with which we want to get involved") + * HTTP_mumble ("an error status should be reported") + */ +static int example_handler + (request_rec *r) { + + example_config + *dcfg; + example_config + *rcfg; + + dcfg = our_dconfig (r); + trace_add (r->server, r, dcfg, "example_handler()"); + /* + * We're about to start sending content, so we need to force the HTTP + * headers to be sent at this point. Otherwise, no headers will be sent + * at all. We can set any we like first, of course. **NOTE** Here's + * where you set the "Content-type" header, and you do so by putting it in + * r->content_type, *not* r->headers_out("Content-type"). If you don't + * set it, it will be filled in with the server's default type (typically + * "text/plain"). + * + * We also need to start a timer so the server can know if the connexion + * is broken. + */ + r->content_type = "text/html"; + soft_timeout ("send example call trace", r); + send_http_header (r); + /* + * If we're only supposed to send header information (HEAD request), we're + * already there. + */ + if (r->header_only) { + kill_timeout (r); + return OK; + } + + /* + * Now send our actual output. Since we tagged this as being + * "text/html", we need to embed any HTML. + */ + rputs ("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n", r); + rputs ("<HTML>\n", r); + rputs (" <HEAD>\n", r); + rputs (" <TITLE>mod_example Module Content-Handler Output\n", r); + rputs (" </TITLE>\n", r); + rputs (" </HEAD>\n", r); + rputs (" <BODY>\n", r); + rputs (" <H1><SAMP>mod_example</SAMP> Module Content-Handler Output\n", r); + rputs (" </H1>\n", r); + rputs (" <P>\n", r); + rputs (" The format for the callback trace is:\n", r); + rputs (" </P>\n", r); + rputs (" <DL>\n", r); + rputs (" <DT><EM>n</EM>.<SAMP><routine-name>", r); + rputs ("(<routine-data>)</SAMP>\n", r); + rputs (" </DT>\n", r); + rputs (" <DD><SAMP>[<applies-to>]</SAMP>\n", r); + rputs (" </DD>\n", r); + rputs (" </DL>\n", r); + rputs (" <P>\n", r); + rputs (" The <SAMP><routine-data></SAMP> is supplied by\n", r); + rputs (" the routine when it requests the trace,\n", r); + rputs (" and the <SAMP><applies-to></SAMP> is extracted\n", r); + rputs (" from the configuration record at the time of the trace.\n", r); + rputs (" <STRONG>SVR()</STRONG> indicates a server environment\n", r); + rputs (" (blank means the main or default server, otherwise it's\n", r); + rputs (" the name of the VirtualHost); <STRONG>DIR()</STRONG>\n", r); + rputs (" indicates a location in the URL or filesystem\n", r); + rputs (" namespace.\n", r); + rputs (" </P>\n", r); + rprintf + ( + r, + " <H2>Static callbacks so far:</H2>\n <OL>\n%s </OL>\n", + trace + ); + rprintf + ( + r, + " <H2>Request-specific callbacks so far:</H2>\n <OL>\n%s </OL>\n", + table_get (r->notes, TRACE_NOTE) + ); + rputs (" <H2>Environment for <EM>this</EM> call:</H2>\n", r); + rputs (" <UL>\n", r); + rprintf (r, " <LI>Applies-to: <SAMP>%s</SAMP>\n </LI>\n", dcfg->loc); + rprintf + ( + r, + " <LI>\"Example\" directive declared here: %s\n </LI>\n", + (dcfg->local ? "YES" : "NO") + ); + rprintf + ( + r, + " <LI>\"Example\" inherited: %s\n </LI>\n", + (dcfg->congenital ? "YES" : "NO") + ); + rputs (" </UL>\n", r); + rputs (" </BODY>\n", r); + rputs ("</HTML>\n", r); + /* + * We're all done, so cancel the timeout we set. Since this is probably + * the end of the request we *could* assume this would be done during + * post-processing - but it's possible that another handler might be + * called and inherit our outstanding timer. Not good; to each its own. + */ + kill_timeout (r); + /* + * We did what we wanted to do, so tell the rest of the server we + * succeeded. + */ + return OK; +} + +/*--------------------------------------------------------------------------*/ +/* */ +/* Now let's declare routines for each of the callback phase in order. */ +/* (That's the order in which they're listed in the callback list, *not */ +/* the order in which the server calls them! See the command_rec */ +/* declaration near the bottom of this file.) Note that these may be */ +/* called for situations that don't relate primarily to our function - in */ +/* other words, the fixup handler shouldn't assume that the request has */ +/* to do with "example" stuff. */ +/* */ +/* With the exception of the content handler, all of our routines will be */ +/* called for each request, unless an earlier handler from another module */ +/* aborted the sequence. */ +/* */ +/* Handlers that are declared as "int" can return the following: */ +/* */ +/* OK Handler accepted the request and did its thing with it. */ +/* DECLINED Handler took no action. */ +/* HTTP_mumble Handler looked at request and found it wanting. */ +/* */ +/* What the server does after calling a module handler depends upon the */ +/* handler's return value. In all cases, if the handler returns */ +/* DECLINED, the server will continue to the next module with an handler */ +/* for the current phase. However, if the handler return a non-OK, */ +/* non-DECLINED status, the server aborts the request right there. If */ +/* the handler returns OK, the server's next action is phase-specific; */ +/* see the individual handler comments below for details. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * This function is called during server initialisation. Any information + * that needs to be recorded must be in static cells, since there's no + * configuration record. + * + * There is no return value. + */ + +/* + * All our module-initialiser does is add its trace to the log. + */ +static void example_init + (server_rec *s, pool *p) { + + char *note; + char *sname = s->server_hostname; + + /* + * Set up any module cells that ought to be initialised. + */ + setup_module_cells (); + /* + * The arbitrary text we add to our trace entry indicates for which server + * we're being called. + */ + sname = (sname != NULL) ? sname : ""; + note = pstrcat (p, "example_init(", sname, ")", NULL); + trace_add (s, NULL, NULL, note); +} + +/* + * This function gets called to create up a per-directory configuration + * record. This will be called for the "default" server environment, and for + * each directory for which the parser finds any of our directives applicable. + * If a directory doesn't have any of our directives involved (i.e., they + * aren't in the .htaccess file, or a <Location>, <Directory>, or related + * block), this routine will *not* be called - the configuration for the + * closest ancestor is used. + * + * The return value is a pointer to the created module-specific + * structure. + */ +static void *example_dir_create + (pool *p, char *dirspec) { + + example_config + *cfg; + char *dname = dirspec; + + /* + * Allocate the space for our record from the pool supplied. + */ + cfg = (example_config *) pcalloc (p, sizeof(example_config)); + /* + * Now fill in the defaults. If there are any `parent' configuration + * records, they'll get merged as part of a separate callback. + */ + cfg->local = 0; + cfg->congenital = 0; + cfg->cmode = CONFIG_MODE_DIRECTORY; + /* + * Finally, add our trace to the callback list. + */ + dname = (dname != NULL) ? dname : ""; + cfg->loc = pstrcat (p, "DIR(", dname, ")", NULL); + trace_add (NULL, NULL, cfg, "example_dir_create()"); + return (void *) cfg; +} + +/* + * This function gets called to merge two per-directory configuration + * records. This is typically done to cope with things like .htaccess files + * or <Location> directives for directories that are beneath one for which a + * configuration record was already created. The routine has the + * responsibility of creating a new record and merging the contents of the + * other two into it appropriately. If the module doesn't declare a merge + * routine, the record for the closest ancestor location (that has one) is + * used exclusively. + * + * The routine MUST NOT modify any of its arguments! + * + * The return value is a pointer to the created module-specific structure + * containing the merged values. + */ +static void *example_dir_merge + (pool *p, void *parent_conf, void *newloc_conf) { + + example_config + *merged_config = + (example_config *) pcalloc (p, sizeof(example_config)); + example_config + *pconf = (example_config *) parent_conf; + example_config + *nconf = (example_config *) newloc_conf; + char *note; + + /* + * Some things get copied directly from the more-specific record, rather + * than getting merged. + */ + merged_config->local = nconf->local; + merged_config->loc = pstrdup (p, nconf->loc); + /* + * Others, like the setting of the `congenital' flag, get ORed in. The + * setting of that particular flag, for instance, is TRUE if it was ever + * true anywhere in the upstream configuration. + */ + merged_config->congenital = (pconf->congenital | pconf->local); + /* + * If we're merging records for two different types of environment (server + * and directory), mark the new record appropriately. Otherwise, inherit + * the current value. + */ + merged_config->cmode = + (pconf->cmode == nconf->cmode) ? pconf->cmode : CONFIG_MODE_COMBO; + /* + * Now just record our being called in the trace list. Include the + * locations we were asked to merge. + */ + note = pstrcat + ( + p, + "example_dir_merge(\"", + pconf->loc, + "\",\"", + nconf->loc, + "\")", + NULL + ); + trace_add (NULL, NULL, merged_config, note); + return (void *) merged_config; +} + +/* + * This function gets called to create a per-server configuration + * record. It will always be called for the "default" server. + * + * The return value is a pointer to the created module-specific + * structure. + */ +static void *example_server_create + (pool *p, server_rec *s) { + + example_config + *cfg; + char *sname = s->server_hostname; + + /* + * As with the example_dir_create() reoutine, we allocate and fill in an + * empty record. + */ + cfg = (example_config *) pcalloc (p, sizeof(example_config)); + cfg->local = 0; + cfg->congenital = 0; + cfg->cmode = CONFIG_MODE_SERVER; + /* + * Note that we were called in the trace list. + */ + sname = (sname != NULL) ? sname : ""; + cfg->loc = pstrcat (p, "SVR(", sname, ")", NULL); + trace_add (s, NULL, cfg, "example_server_create()"); + return (void *) cfg; +} + +/* + * This function gets called to merge two per-server configuration + * records. This is typically done to cope with things like virtual hosts and + * the default server configuration The routine has the responsibility of + * creating a new record and merging the contents of the other two into it + * appropriately. If the module doesn't declare a merge routine, the more + * specific existing record is used exclusively. + * + * The routine MUST NOT modify any of its arguments! + * + * The return value is a pointer to the created module-specific structure + * containing the merged values. + */ +static void *example_server_merge + (pool *p, void *server1_conf, void *server2_conf) { + + example_config + *merged_config = + (example_config *) pcalloc (p, sizeof(example_config)); + example_config + *s1conf = (example_config *) server1_conf; + example_config + *s2conf = (example_config *) server2_conf; + char *note; + + /* + * Our inheritance rules are our own, and part of our module's semantics. + * Basically, just note whence we came. + */ + merged_config->cmode = + (s1conf->cmode == s2conf->cmode) ? s1conf->cmode : CONFIG_MODE_COMBO; + merged_config->local = s2conf->local; + merged_config->congenital = (s1conf->congenital | s1conf->local); + merged_config->loc = pstrdup (p, s2conf->loc); + /* + * Trace our call, including what we were asked to merge. + */ + note = pstrcat + ( + p, + "example_server_merge(\"", + s1conf->loc, + "\",\"", + s2conf->loc, + "\")", + NULL + ); + trace_add (NULL, NULL, merged_config, note); + return (void *) merged_config; +} + +/* + * This routine gives our module an opportunity to translate the URI into an + * actual filename. If we don't do anything special, the server's default + * rules (Alias directives and the like) will continue to be followed. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no + * further modules are called for this phase. + */ +static int example_xlate + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + /* + * We don't actually *do* anything here, except note the fact that we were + * called. + */ + trace_add (r->server, r, cfg, "example_xlate()"); + return DECLINED; +} + +/* + * This routine is called to check the authentication information sent with + * the request (such as looking up the user in a database and verifying that + * the [encrypted] password sent matches the one in the database). + * + * The return value is OK, DECLINED, or some HTTP_mumble error (typically + * HTTP_UNAUTHORIZED). If we return OK, no other modules are given a chance + * at the request during this phase. + */ +static int example_ckuser + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + /* + * Don't do anything except log the call. + */ + trace_add (r->server, r, cfg, "example_ckuser()"); + return DECLINED; +} + +/* + * This routine is called to check to see if the resource being requested + * requires authorisation. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no + * other modules are called during this phase. + * + * If *all* modules return DECLINED, the request is aborted with a server + * error. + */ +static int example_ckauth + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + /* + * Log the call and return OK, or access will be denied (even though we + * didn't actually do anything). + */ + trace_add (r->server, r, cfg, "example_ckauth()"); + return OK; +} + +/* + * This routine is called to check for any module-specific restrictions placed + * upon the requested resource. (See the mod_access module for an example.) + * + * The return value is OK, DECLINED, or HTTP_mumble. All modules with an + * handler for this phase are called regardless of whether their predecessors + * return OK or DECLINED. The first one to return any other status, however, + * will abort the sequence (and the request) as usual. + */ +static int example_ckaccess + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + trace_add (r->server, r, cfg, "example_ckaccess()"); + return OK; +} + +/* + * This routine is called to determine and/or set the various document type + * information bits, like Content-type (via r->content_type), language, et + * cetera. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, no + * further modules are given a chance at the request for this phase. + */ +static int example_typer + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + /* + * Log the call, but don't do anything else - and report truthfully that + * we didn't do anything. + */ + trace_add (r->server, r, cfg, "example_typer()"); + return DECLINED; +} + +/* + * This routine is called to perform any module-specific fixing of header + * fields, et cetera. It is invoked just before any content-handler. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, the + * server will still call any remaining modules with an handler for this + * phase. + */ +static int example_fixer + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + /* + * Log the call and exit. + */ + trace_add (r->server, r, cfg, "example_fixer()"); + return OK; +} + +/* + * This routine is called to perform any module-specific logging activities + * over and above the normal server things. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any + * remaining modules with an handler for this phase will still be called. + */ +static int example_logger + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + trace_add (r->server, r, cfg, "example_logger()"); + return DECLINED; +} + +/* + * This routine is called to give the module a chance to look at the request + * headers and take any appropriate specific actions early in the processing + * sequence. + * + * The return value is OK, DECLINED, or HTTP_mumble. If we return OK, any + * remaining modules with handlers for this phase will still be called. + */ +static int example_hparser + (request_rec *r) { + + example_config + *cfg; + + cfg = our_dconfig (r); + trace_add (r->server, r, cfg, "example_hparser()"); + return DECLINED; +} + +/*--------------------------------------------------------------------------*/ +/* */ +/* All of the routines have been declared now. Here's the list of */ +/* directives specific to our module, and information about where they */ +/* may appear and how the command parser should pass them to us for */ +/* processing. Note that care must be taken to ensure that there are NO */ +/* collisions of directive names between modules. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * List of directives specific to our module. + */ +command_rec example_commands[] = { + { + "Example", /* directive name */ + cmd_example, /* action routine for directive */ + NULL, /* argument to include in call */ + OR_OPTIONS, /* where available */ + NO_ARGS, /* arguments */ + "Example directive - no arguments" + /* directive description */ + }, + { NULL } +}; + +/*--------------------------------------------------------------------------*/ +/* */ +/* Now the list of content handlers available from this module. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * List of content handlers our module supplies. Each handler is defined by + * two parts: a name by which it can be referenced (such as by + * {Add,Set}Handler), and the actual routine name. The list is terminated by + * a NULL block, since it can be of variable length. + * + * Note that content-handlers are invoked on a most-specific to least-specific + * basis; that is, a handler that is declared for "text/plain" will be + * invoked before one that was declared for "text / *". Note also that + * if a content-handler returns anything except DECLINED, no other + * content-handlers will be called. + */ +handler_rec example_handlers[] = { + { "example-handler", example_handler }, + { NULL } +}; + +/*--------------------------------------------------------------------------*/ +/* */ +/* Finally, the list of callback routines and data structures that */ +/* provide the hooks into our module from the other parts of the server. */ +/* */ +/*--------------------------------------------------------------------------*/ +/* + * Module definition for configuration. If a particular callback is not + * needed, replace its routine name below with the word NULL. + * + * The number in brackets indicates the order in which the routine is called + * during request processing. Note that not all routines are necessarily + * called (such as if a resource doesn't have access restrictions). + */ +module example_module = { + STANDARD_MODULE_STUFF, + example_init, /* initializer */ + example_dir_create, /* per-directory config creater */ + example_dir_merge, /* dir config merger - default is to override */ + example_server_create, /* server config creator */ + example_server_merge, /* server config merger */ + example_commands, /* command table */ + example_handlers, /* [6] list of handlers */ + example_xlate, /* [1] filename-to-URI translation */ + example_ckuser, /* [4] check/validate HTTP user_id */ + example_ckauth, /* [5] check HTTP user_id is valid *here* */ + example_ckaccess, /* [3] check access by host address, etc. */ + example_typer, /* [6] MIME type checker/setter */ + example_fixer, /* [7] fixups */ + example_logger, /* [9] logger */ + example_hparser /* [2] header parser */ +}; diff --git a/usr.sbin/httpd/src/modules/proxy/Makefile b/usr.sbin/httpd/src/modules/proxy/Makefile new file mode 100644 index 00000000000..102dea2a25f --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/Makefile @@ -0,0 +1,89 @@ +# ==================================================================== +# Copyright (c) 1995-1997 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. 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://www.apache.org/>. +# +# Makefile for the Apache proxy library +# + +SHELL = /bin/sh + +INCDIR = ../.. + +LIB=libproxy.a + +# AUX_CFLAGS comes from higher level Makefile +CFLAGS=-I$(INCDIR) $(AUX_CFLAGS) + +# Internal stuff, should not need changing. +PROXYSRC=mod_proxy.c proxy_cache.c proxy_connect.c proxy_ftp.c proxy_http.c \ +proxy_util.c + +OBJS=$(PROXYSRC:.c=.o) + +default: force $(LIB) + +.c.a: + $(MAKE) $(CFLAGS) $< + +force: + rm -f $(LIB) + +$(LIB): $(OBJS) + ar crv $@ $(OBJS) + $(RANLIB) $@ + +clean: + rm -f *.o libproxy.a + +# dependencies +$(OBJS): mod_proxy.h $(INCDIR)/httpd.h $(INCDIR)/http_config.h $(INCDIR)/http_protocol.h +proxy_cache.o proxy_connect.o proxy_ftp.o proxy_http.o proxy_util.o: $(INCDIR)/http_main.h +proxy_cache.o proxy_connect.o proxy_http.o: $(INCDIR)/http_log.h +proxy_cache.o proxy_http.o: $(INCDIR)/util_date.h +proxy_cache.o proxy_util.o: $(INCDIR)/md5.h diff --git a/usr.sbin/httpd/src/modules/proxy/mod_proxy.c b/usr.sbin/httpd/src/modules/proxy/mod_proxy.c new file mode 100644 index 00000000000..ce8dc88af15 --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/mod_proxy.c @@ -0,0 +1,554 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +#include "mod_proxy.h" + +/* Some WWW schemes and their default ports; this is basically /etc/services */ +/* This will become global when the protocol abstraction comes */ +static struct proxy_services defports[]={ + { "ftp", DEFAULT_FTP_PORT}, + { "gopher", DEFAULT_GOPHER_PORT}, + { "http", DEFAULT_PORT}, + { "nntp", DEFAULT_NNTP_PORT}, + { "wais", DEFAULT_WAIS_PORT}, + { "https", DEFAULT_HTTPS_PORT}, + { "snews", DEFAULT_SNEWS_PORT}, + { "prospero", DEFAULT_PROSPERO_PORT}, + { NULL, -1} /* unknown port */ +}; + +/* + * A Web proxy module. Stages: + * + * translate_name: set filename to proxy:<URL> + * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy: + * fix_ups: convert the URL stored in the filename to the + * canonical form. + * handler: handle proxy requests + */ + +/* -------------------------------------------------------------- */ +/* Translate the URL into a 'filename' */ + +static int +alias_match(char *uri, char *alias_fakename) +{ + char *end_fakename = alias_fakename + strlen (alias_fakename); + char *aliasp = alias_fakename, *urip = uri; + + while (aliasp < end_fakename) + { + if (*aliasp == '/') + { + /* any number of '/' in the alias matches any number in + * the supplied URI, but there must be at least one... + */ + if (*urip != '/') return 0; + + while (*aliasp == '/') ++ aliasp; + while (*urip == '/') ++ urip; + } + else { + /* Other characters are compared literally */ + if (*urip++ != *aliasp++) return 0; + } + } + + /* Check last alias path component matched all the way */ + + if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/') + return 0; + + /* Return number of characters from URI which matched (may be + * greater than length of alias, since we may have matched + * doubled slashes) + */ + + return urip - uri; +} + +static int +proxy_trans(request_rec *r) +{ + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + + if (r->proxyreq) + { + if (!conf->req) return DECLINED; + + r->filename = pstrcat(r->pool, "proxy:", r->uri, NULL); + r->handler = "proxy-server"; + return OK; + } else + { + int i, len; + struct proxy_alias *ent=(struct proxy_alias *)conf->aliases->elts; + + for (i=0; i < conf->aliases->nelts; i++) + { + len = alias_match(r->uri, ent[i].fake); + + if (len > 0) + { + r->filename = pstrcat(r->pool, "proxy:", ent[i].real, + r->uri + len, NULL); + r->handler = "proxy-server"; + return OK; + } + } + return DECLINED; + } +} + +/* -------------------------------------------------------------- */ +/* Fixup the filename */ + +/* + * Canonicalise the URL + */ +static int +proxy_fixup(request_rec *r) +{ + char *url, *p; + int i; + + if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; + + url = &r->filename[6]; +/* lowercase the scheme */ + p = strchr(url, ':'); + if (p == NULL || p == url) return BAD_REQUEST; + for (i=0; i != p - url; i++) url[i] = tolower(url[i]); + +/* canonicalise each specific scheme */ + if (strncmp(url, "http:", 5) == 0) + return proxy_http_canon(r, url+5, "http", DEFAULT_PORT); + else if (strncmp(url, "ftp:", 4) == 0) + return proxy_ftp_canon(r, url+4); + else return OK; /* otherwise; we've done the best we can */ +} + +/* -------------------------------------------------------------- */ +/* Invoke handler */ + +static int +proxy_handler(request_rec *r) +{ + char *url, *scheme, *p; + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + array_header *proxies=conf->proxies; + struct proxy_remote *ents=(struct proxy_remote *)proxies->elts; + int i, rc; + struct cache_req *cr; + + if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; + + if ((rc = setup_client_block(r, REQUEST_CHUNKED_ERROR))) + return rc; + + url = r->filename + 6; + p = strchr(url, ':'); + if (p == NULL) return BAD_REQUEST; + + rc = proxy_cache_check(r, url, &conf->cache, &cr); + if (rc != DECLINED) return rc; + + *p = '\0'; + scheme = pstrdup(r->pool, url); + *p = ':'; + +/* firstly, try a proxy */ + + for (i=0; i < proxies->nelts; i++) + { + p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ + if (strcmp(ents[i].scheme, "*") == 0 || + (p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) || + (p != NULL && + strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) + { +/* we only know how to handle communication to a proxy via http */ + if (strcasecmp(ents[i].protocol, "http") == 0) + rc = proxy_http_handler(r, cr, url, ents[i].hostname, + ents[i].port); + else rc = DECLINED; + + /* an error or success */ + if (rc != DECLINED && rc != BAD_GATEWAY) return rc; + /* we failed to talk to the upstream proxy */ + } + } + +/* otherwise, try it direct */ +/* N.B. what if we're behind a firewall, where we must use a proxy or + * give up?? + */ + /* handle the scheme */ + if (r->method_number == M_CONNECT) + return proxy_connect_handler(r, cr, url); + if (strcasecmp(scheme, "http") == 0) + return proxy_http_handler(r, cr, url, NULL, 0); + if (strcasecmp(scheme, "ftp") == 0) + return proxy_ftp_handler(r, cr, url); + else return NOT_IMPLEMENTED; +} + +/* -------------------------------------------------------------- */ +/* Setup configurable data */ + +static void * +create_proxy_config(pool *p, server_rec *s) +{ + proxy_server_conf *ps = pcalloc(p, sizeof(proxy_server_conf)); + + ps->proxies = make_array(p, 10, sizeof(struct proxy_remote)); + ps->aliases = make_array(p, 10, sizeof(struct proxy_alias)); + ps->noproxies = make_array(p, 10, sizeof(struct noproxy_entry)); + ps->nocaches = make_array(p, 10, sizeof(struct nocache_entry)); + ps->req = 0; + + ps->cache.root = NULL; + ps->cache.space = DEFAULT_CACHE_SPACE; + ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE; + ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE; + ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR; + ps->cache.gcinterval = -1; + /* at these levels, the cache can have 2^18 directories (256,000) */ + ps->cache.dirlevels=3; + ps->cache.dirlength=1; + + return ps; +} + +static const char * +add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(s->module_config,&proxy_module); + struct proxy_remote *new; + char *p, *q; + int port; + + p = strchr(r, ':'); + if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0') + return "Bad syntax for a remote proxy server"; + q = strchr(p + 3, ':'); + if (q != NULL) + { + if (sscanf(q+1, "%u", &port) != 1 || port > 65535) + return "Bad syntax for a remote proxy server (bad port number)"; + *q = '\0'; + } else port = -1; + *p = '\0'; + if (strchr(f, ':') == NULL) str_tolower(f); /* lowercase scheme */ + str_tolower(p + 3); /* lowercase hostname */ + + if (port == -1) + { + int i; + for (i=0; defports[i].scheme != NULL; i++) + if (strcasecmp(defports[i].scheme, r) == 0) break; + port = defports[i].port; + } + + new = push_array (conf->proxies); + new->scheme = f; + new->protocol = r; + new->hostname = p + 3; + new->port = port; + return NULL; +} + +static const char * +add_pass(cmd_parms *cmd, void *dummy, char *f, char *r) +{ + server_rec *s = cmd->server; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(s->module_config,&proxy_module); + struct proxy_alias *new; + + new = push_array (conf->aliases); + new->fake = f; + new->real = r; + return NULL; +} + +static const char * +set_proxy_exclude(cmd_parms *parms, void *dummy, char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + get_module_config (s->module_config, &proxy_module); + struct noproxy_entry *new; + struct noproxy_entry *list=(struct noproxy_entry*)conf->noproxies->elts; + struct hostent hp; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i=0; i < conf->noproxies->nelts; i++) + { + if (strcmp(arg, list[i].name) == 0) + found = 1; + } + + if (!found) + { + new = push_array (conf->noproxies); + new->name = arg; + /* Don't do name lookups on things that aren't dotted */ + if (strchr(arg, '.') != NULL && proxy_host2addr(new->name, &hp) == NULL) + memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); + else + new->addr.s_addr = 0; + } + return NULL; +} + +static const char * +set_proxy_req(cmd_parms *parms, void *dummy, int flag) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + + psf->req = flag; + return NULL; +} + + +static const char * +set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + int val; + + if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer"; + psf->cache.space = val; + return NULL; +} + +static const char * +set_cache_root(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + + psf->cache.root = arg; + + return NULL; +} + +static const char * +set_cache_factor(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.lmfactor = val; + + return NULL; +} + +static const char * +set_cache_maxex(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.maxexpire = (int)(val * (double)SEC_ONE_HR); + return NULL; +} + +static const char * +set_cache_defex(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.defaultexpire = (int)(val * (double)SEC_ONE_HR); + return NULL; +} + +static const char * +set_cache_gcint(cmd_parms *parms, void *dummy, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float"; + psf->cache.gcinterval = (int)(val * (double)SEC_ONE_HR); + return NULL; +} + +static const char * +set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + int val; + + if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer"; + psf->cache.dirlevels = val; + return NULL; +} + +static const char * +set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg) +{ + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + int val; + + if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer"; + psf->cache.dirlength = val; + return NULL; +} + +static const char * +set_cache_exclude(cmd_parms *parms, void *dummy, char *arg) +{ + server_rec *s = parms->server; + proxy_server_conf *conf = + get_module_config (s->module_config, &proxy_module); + struct nocache_entry *new; + struct nocache_entry *list=(struct nocache_entry*)conf->nocaches->elts; + struct hostent hp; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i=0; i < conf->nocaches->nelts; i++) + { + if (strcmp(arg, list[i].name) == 0) + found = 1; + } + + if (!found) + { + new = push_array (conf->nocaches); + new->name = arg; + /* Don't do name lookups on things that aren't dotted */ + if (strchr(arg, '.') != NULL && proxy_host2addr(new->name, &hp) == NULL) + memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr)); + else + new->addr.s_addr= 0; + } + return NULL; +} + +static handler_rec proxy_handlers[] = { +{ "proxy-server", proxy_handler }, +{ NULL } +}; + +static command_rec proxy_cmds[] = { +{ "ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG, + "on if the true proxy requests should be accepted"}, +{ "ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2, + "a scheme, partial URL or '*' and a proxy server"}, +{ "ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2, + "a virtual path and a URL"}, +{ "ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, ITERATE, + "A list of names, hosts or domains to which the proxy will not connect" }, +{ "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1, + "The directory to store cache files"}, +{ "CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1, + "The maximum disk space used by the cache in Kb"}, +{ "CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1, + "The maximum time in hours to cache a document"}, +{ "CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1, + "The default time in hours to cache a document"}, +{ "CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1, + "The factor used to estimate Expires date from LastModified date"}, +{ "CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1, + "The interval between garbage collections, in hours"}, +{ "CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1, + "The number of levels of subdirectories in the cache" }, +{ "CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1, + "The number of characters in subdirectory names" }, +{ "NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE, + "A list of names, hosts or domains for which caching is *not* provided" }, +{ NULL } +}; + +module proxy_module = { + STANDARD_MODULE_STUFF, + NULL, /* initializer */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_proxy_config, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + proxy_cmds, /* command table */ + proxy_handlers, /* handlers */ + proxy_trans, /* translate_handler */ + NULL, /* check_user_id */ + NULL, /* check auth */ + NULL, /* check access */ + NULL, /* type_checker */ + proxy_fixup, /* pre-run fixups */ + NULL, /* logger */ + NULL /* header parser */ +}; + diff --git a/usr.sbin/httpd/src/modules/proxy/mod_proxy.h b/usr.sbin/httpd/src/modules/proxy/mod_proxy.h new file mode 100644 index 00000000000..3287fb2a86a --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/mod_proxy.h @@ -0,0 +1,273 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* + * Main include file for the Apache proxy + */ + +/* + +Note that the Explain() stuff is not yet complete. +Also note numerous FIXMEs and CHECKMEs which should be eliminated. + +If TESTING is set, then garbage collection doesn't delete ... probably a good +idea when hacking. + +This code is still experimental! + +Things to do: + +1. Make it garbage collect in the background, not while someone is waiting for +a response! + +2. Check the logic thoroughly. + +3. Empty directories are only removed the next time round (but this does avoid +two passes). Consider doing them the first time round. + +Ben Laurie <ben@algroup.co.uk> 30 Mar 96 + +More things to do: + +0. Code cleanup (ongoing) + +1. add 230 response output for ftp now that it works + +2. Make the ftp proxy transparent, also same with (future) gopher & wais + +3. Use protocol handler struct a la Apache module handlers (Dirk van Gulik) + +4. Use a cache expiry database for more efficient GC (Jeremy Wohl) + +5. Bulletproof GC against SIGALRM + +Chuck Murcko <chuck@topsail.org> 15 April 1997 + +*/ + +#define TESTING 0 +#undef EXPLAIN + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" + +#include "explain.h" + +DEF_Explain + + +extern module proxy_module; + + +/* for proxy_canonenc() */ +enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm }; + +#define HDR_APP (0) /* append header, for proxy_add_header() */ +#define HDR_REP (1) /* replace header, for proxy_add_header() */ + +/* number of characters in the hash */ +#define HASH_LEN (22*2) + +#define SEC_ONE_DAY 86400 /* one day, in seconds */ +#define SEC_ONE_HR 3600 /* one hour, in seconds */ + +#define DEFAULT_FTP_DATA_PORT 20 +#define DEFAULT_FTP_PORT 21 +#define DEFAULT_GOPHER_PORT 70 +#define DEFAULT_NNTP_PORT 119 +#define DEFAULT_WAIS_PORT 210 +#define DEFAULT_HTTPS_PORT 443 +#define DEFAULT_SNEWS_PORT 563 +#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */ + +/* Some WWW schemes and their default ports; this is basically /etc/services */ +struct proxy_services +{ + const char *scheme; + int port; +}; + +/* static information about a remote proxy */ +struct proxy_remote +{ + const char *scheme; /* the schemes handled by this proxy, or '*' */ + const char *protocol; /* the scheme used to talk to this proxy */ + const char *hostname; /* the hostname of this proxy */ + int port; /* the port for this proxy */ +}; + +struct proxy_alias { + char *real; + char *fake; +}; + +struct noproxy_entry { + char *name; + struct in_addr addr; +}; + +struct nocache_entry { + char *name; + struct in_addr addr; +}; + +#define DEFAULT_CACHE_SPACE 5 +#define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY +#define DEFAULT_CACHE_EXPIRE SEC_ONE_HR +#define DEFAULT_CACHE_LMFACTOR (0.1) + +/* static information about the local cache */ +struct cache_conf +{ + const char *root; /* the location of the cache directory */ + int space; /* Maximum cache size (in 1024 bytes) */ + int maxexpire; /* Maximum time to keep cached files in secs */ + int defaultexpire; /* default time to keep cached file in secs */ + double lmfactor; /* factor for estimating expires date */ + int gcinterval; /* garbage collection interval, in seconds */ + int dirlevels; /* Number of levels of subdirectories */ + int dirlength; /* Length of subdirectory names */ +}; + +typedef struct +{ + struct cache_conf cache; /* cache configuration */ + array_header *proxies; + array_header *aliases; + array_header *noproxies; + array_header *nocaches; + int req; /* true if proxy requests are enabled */ +} proxy_server_conf; + +struct hdr_entry +{ + char *field; + char *value; +}; + +/* caching information about a request */ +struct cache_req +{ + request_rec *req; /* the request */ + char *url; /* the URL requested */ + char *filename; /* name of the cache file, or NULL if no cache */ + char *tempfile; /* name of the temporary file, of NULL if not caching */ + time_t ims; /* if-modified-since date of request; -1 if no header */ + BUFF *fp; /* the cache file descriptor if the file is cached + and may be returned, or NULL if the file is + not cached (or must be reloaded) */ + time_t expire; /* calculated expire date of cached entity */ + time_t lmod; /* last-modified date of cached entity */ + time_t date; /* the date the cached file was last touched */ + int version; /* update count of the file */ + unsigned int len; /* content length */ + char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */ + int status; /* the status of the cached file */ + char *resp_line; /* the whole status like (protocol, code + message) */ + array_header *hdrs; /* the HTTP headers of the file */ +}; + +/* Function prototypes */ + +/* proxy_cache.c */ + +void proxy_cache_tidy(struct cache_req *c); +int proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, + struct cache_req **cr); +int proxy_cache_update(struct cache_req *c, array_header *resp_hdrs, + const int is_HTTP1, int nocache); +void proxy_garbage_coll(request_rec *r); + +/* proxy_connect.c */ + +int proxy_connect_handler(request_rec *r, struct cache_req *c, char *url); + +/* proxy_ftp.c */ + +int proxy_ftp_canon(request_rec *r, char *url); +int proxy_ftp_handler(request_rec *r, struct cache_req *c, char *url); + +/* proxy_http.c */ + +int proxy_http_canon(request_rec *r, char *url, const char *scheme, + int def_port); +int proxy_http_handler(request_rec *r, struct cache_req *c, char *url, + const char *proxyhost, int proxyport); + +/* proxy_util.c */ + +int proxy_hex2c(const char *x); +void proxy_c2hex(int ch, char *x); +char *proxy_canonenc(pool *p, const char *x, int len, enum enctype t, + int isenc); +char *proxy_canon_netloc(pool *pool, char **const urlp, char **userp, + char **passwordp, char **hostp, int *port); +char *proxy_date_canon(pool *p, char *x); +array_header *proxy_read_headers(pool *pool, char *buffer, int size, BUFF *f); +long int proxy_send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c); +struct hdr_entry *proxy_get_header(array_header *hdrs_arr, const char *name); +struct hdr_entry *proxy_add_header(array_header *hdrs_arr, char *field, + char *value, int rep); +void proxy_del_header(array_header *hdrs_arr, const char *field); +void proxy_send_headers(BUFF *fp, const char *respline, array_header *hdrs_arr); +int proxy_liststr(const char *list, const char *val); +void proxy_hash(const char *it, char *val,int ndepth,int nlength); +int proxy_hex2sec(const char *x); +void proxy_sec2hex(int t, char *y); +void proxy_log_uerror(const char *routine, const char *file, const char *err, + server_rec *s); +BUFF *proxy_cache_error(struct cache_req *r); +int proxyerror(request_rec *r, const char *message); +const char *proxy_host2addr(const char *host, struct hostent *reqhp); +int proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r); + diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_cache.c b/usr.sbin/httpd/src/modules/proxy/proxy_cache.c new file mode 100644 index 00000000000..56f0a95cd26 --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/proxy_cache.c @@ -0,0 +1,933 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* Cache and garbage collection routines for Apache proxy */ + +#include "md5.h" + +#include "mod_proxy.h" +#include "http_log.h" +#include "http_main.h" +#include "util_date.h" +#include <utime.h> + +#define abs(c) ((c) >= 0 ? (c) : -(c)) + +struct gc_ent +{ + unsigned long int len; + time_t expire; + char file[HASH_LEN+1]; + +}; + +static int +gcdiff(const void *ap, const void *bp) +{ + const struct gc_ent *a=*(struct gc_ent **)ap, *b=*(struct gc_ent **)bp; + + if (a->expire > b->expire) return 1; + else if (a->expire < b->expire) return -1; + else return 0; +} + +static int curbytes, cachesize, every; +static unsigned long int curblocks; +static time_t now, expire; +static char *filename; +static int filenamelen; + +static int sub_garbage_coll(request_rec *r,array_header *files, + const char *cachedir,const char *cachesubdir); + +void proxy_garbage_coll(request_rec *r) + { + const char *cachedir; + void *sconf = r->server->module_config; + proxy_server_conf *pconf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + const struct cache_conf *conf=&pconf->cache; + array_header *files; + struct stat buf; + struct gc_ent *fent,**elts; + int i, timefd; + static time_t lastcheck=BAD_DATE; /* static data!!! */ + + cachedir = conf->root; + cachesize = conf->space; + every = conf->gcinterval; + + if (cachedir == NULL || every == -1) return; + now = time(NULL); + if (now != -1 && lastcheck != BAD_DATE && now < lastcheck + every) return; + + block_alarms(); /* avoid SIGALRM on big cache cleanup */ + + filenamelen = strlen(cachedir) + HASH_LEN + 2; + filename = palloc(r->pool, filenamelen); + ap_snprintf(filename, filenamelen, "%s/.time", cachedir); + + if (stat(filename, &buf) == -1) /* does not exist */ + { + if (errno != ENOENT) + { + proxy_log_uerror("stat", filename, NULL, r->server); + unblock_alarms(); + return; + } + if ((timefd = creat(filename, 0666)) == -1) + { + if (errno != EEXIST) + proxy_log_uerror("creat", filename, NULL, r->server); + else + lastcheck = abs(now); /* someone else got in there */ + unblock_alarms(); + return; + } + close(timefd); + } else + { + lastcheck = buf.st_mtime; /* save the time */ + if (now < lastcheck + every) + { + unblock_alarms(); + return; + } + if (utime(filename, NULL) == -1) + proxy_log_uerror("utimes", filename, NULL, r->server); + } + files = make_array(r->pool, 100, sizeof(struct gc_ent *)); + curblocks = 0; + curbytes = 0; + + sub_garbage_coll(r,files,cachedir,"/"); + + if (curblocks < cachesize || curblocks + curbytes <= cachesize) + { + unblock_alarms(); + return; + } + + qsort(files->elts, files->nelts, sizeof(struct gc_ent *), gcdiff); + + elts = (struct gc_ent **)files->elts; + for (i=0; i < files->nelts; i++) + { + fent = elts[i]; + ap_snprintf(filename, filenamelen, "%s%s", cachedir, fent->file); + Explain3("GC Unlinking %s (expiry %ld, now %ld)",filename,fent->expire,now); +#if TESTING + fprintf(stderr,"Would unlink %s\n",filename); +#else + if (unlink(filename) == -1) + { + if (errno != ENOENT) + proxy_log_uerror("unlink", filename, NULL, r->server); + } + else +#endif + { + curblocks -= fent->len >> 10; + curbytes -= fent->len & 0x3FF; + if (curbytes < 0) + { + curbytes += 1024; + curblocks--; + } + if (curblocks < cachesize || curblocks + curbytes <= cachesize) + break; + } + } + unblock_alarms(); +} + +static int sub_garbage_coll(request_rec *r,array_header *files, + const char *cachebasedir,const char *cachesubdir) +{ + char line[27]; + char cachedir[HUGE_STRING_LEN]; + struct stat buf; + int fd,i; + DIR *dir; +#if defined(NEXT) + struct DIR_TYPE *ent; +#else + struct dirent *ent; +#endif + struct gc_ent *fent; + int nfiles=0; + + ap_snprintf(cachedir, sizeof(cachedir), "%s%s",cachebasedir,cachesubdir); + Explain1("GC Examining directory %s",cachedir); + dir = opendir(cachedir); + if (dir == NULL) + { + proxy_log_uerror("opendir", cachedir, NULL, r->server); + return 0; + } + + while ((ent = readdir(dir)) != NULL) + { + if (ent->d_name[0] == '.') continue; + ap_snprintf(filename, filenamelen, "%s%s", cachedir, ent->d_name); + Explain1("GC Examining file %s",filename); +/* is it a temporary file? */ + if (strncmp(ent->d_name, "tmp", 3) == 0) + { +/* then stat it to see how old it is; delete temporary files > 1 day old */ + if (stat(filename, &buf) == -1) + { + if (errno != ENOENT) + proxy_log_uerror("stat", filename, NULL, r->server); + } else if (now != -1 && buf.st_atime < now - SEC_ONE_DAY && + buf.st_mtime < now - SEC_ONE_DAY) + { + Explain1("GC unlink %s",filename); +#if TESTING + fprintf(stderr,"Would unlink %s\n",filename); +#else + unlink(filename); +#endif + } + continue; + } + ++nfiles; +/* is it another file? */ + /* FIXME: Shouldn't any unexpected files be deleted? */ + /* if (strlen(ent->d_name) != HASH_LEN) continue; */ + +/* read the file */ + fd = open(filename, O_RDONLY); + if (fd == -1) + { + if (errno != ENOENT) proxy_log_uerror("open", filename,NULL, + r->server); + continue; + } + if (fstat(fd, &buf) == -1) + { + proxy_log_uerror("fstat", filename, NULL, r->server); + close(fd); + continue; + } + if(S_ISDIR(buf.st_mode)) + { + char newcachedir[HUGE_STRING_LEN]; + close(fd); + ap_snprintf(newcachedir, sizeof(newcachedir), + "%s%s/",cachesubdir,ent->d_name); + if(!sub_garbage_coll(r,files,cachebasedir,newcachedir)) + { + ap_snprintf(newcachedir, sizeof(newcachedir), + "%s%s",cachedir,ent->d_name); +#if TESTING + fprintf(stderr,"Would remove directory %s\n",newcachedir); +#else + rmdir(newcachedir); +#endif + --nfiles; + } + continue; + } + + i = read(fd, line, 26); + if (i == -1) + { + proxy_log_uerror("read", filename, NULL, r->server); + close(fd); + continue; + } + close(fd); + line[i] = '\0'; + expire = proxy_hex2sec(line+18); + if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") || + expire == BAD_DATE) + { + /* bad file */ + if (now != -1 && buf.st_atime > now + SEC_ONE_DAY && + buf.st_mtime > now + SEC_ONE_DAY) + { + log_error("proxy: deleting bad cache file", r->server); +#if TESTING + fprintf(stderr,"Would unlink bad file %s\n",filename); +#else + unlink(filename); +#endif + } + continue; + } + +/* + * we need to calculate an 'old' factor, and remove the 'oldest' files + * so that the space requirement is met; sort by the expires date of the + * file. + * + */ + /* FIXME: We should make the array an array of gc_ents, not gc_ent *s + */ + fent = palloc(r->pool, sizeof(struct gc_ent)); + fent->len = buf.st_size; + fent->expire = expire; + ap_snprintf(fent->file, sizeof(fent->file), "%s%s", cachesubdir, + ent->d_name); + *(struct gc_ent **)push_array(files) = fent; + +/* accumulate in blocks, to cope with directories > 4Gb */ + curblocks += buf.st_size >> 10; /* Kbytes */ + curbytes += buf.st_size & 0x3FF; + if (curbytes >= 1024) + { + curbytes -= 1024; + curblocks++; + } + } + + closedir(dir); + + return nfiles; + +} + +/* + * read a cache file; + * returns 1 on success, + * 0 on failure (bad file or wrong URL) + * -1 on UNIX error + */ +static int +rdcache(pool *pool, BUFF *cachefp, struct cache_req *c) +{ + char urlbuff[1034], *p; + int len; +/* read the data from the cache file */ +/* format + * date SP lastmod SP expire SP count SP content-length CRLF + * dates are stored as hex seconds since 1970 + */ + len = bgets(urlbuff, 1034, cachefp); + if (len == -1) return -1; + if (len == 0 || urlbuff[len-1] != '\n') return 0; + urlbuff[len-1] = '\0'; + + if (!checkmask(urlbuff, + "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&")) + return 0; + + c->date = proxy_hex2sec(urlbuff); + c->lmod = proxy_hex2sec(urlbuff+9); + c->expire = proxy_hex2sec(urlbuff+18); + c->version = proxy_hex2sec(urlbuff+27); + c->len = proxy_hex2sec(urlbuff+36); + +/* check that we have the same URL */ + len = bgets(urlbuff, 1034, cachefp); + if (len == -1) return -1; + if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 || + urlbuff[len-1] != '\n') + return 0; + urlbuff[len-1] = '\0'; + if (strcmp(urlbuff+7, c->url) != 0) return 0; + +/* What follows is the message */ + len = bgets(urlbuff, 1034, cachefp); + if (len == -1) return -1; + if (len == 0 || urlbuff[len-1] != '\n') return 0; + urlbuff[--len] = '\0'; + + c->resp_line = pstrdup(pool, urlbuff); + p = strchr(urlbuff, ' '); + if (p == NULL) return 0; + + c->status = atoi(p); + c->hdrs = proxy_read_headers(pool, urlbuff, 1034, cachefp); + if (c->hdrs == NULL) return -1; + if (c->len != -1) /* add a content-length header */ + { + struct hdr_entry *q; + q = proxy_get_header(c->hdrs, "Content-Length"); + if (q == NULL) + { + p = palloc(pool, 15); + ap_snprintf(p, 15, "%u", c->len); + proxy_add_header(c->hdrs, "Content-Length", p, HDR_REP); + } + } + return 1; +} + + +/* + * Call this to test for a resource in the cache + * Returns DECLINED if we need to check the remote host + * or an HTTP status code if successful + * + * Functions: + * if URL is cached then + * if cached file is not expired then + * if last modified after if-modified-since then send body + * else send 304 Not modified + * else + * if last modified after if-modified-since then add + * last modified date to request + */ +int +proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf, + struct cache_req **cr) +{ + char hashfile[33], *imstr, *pragma, *p, *auth; + struct cache_req *c; + time_t now; + BUFF *cachefp; + int cfd, i; + const long int zero=0L; + void *sconf = r->server->module_config; + proxy_server_conf *pconf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + + c = pcalloc(r->pool, sizeof(struct cache_req)); + *cr = c; + c->req = r; + c->url = pstrdup(r->pool, url); + +/* get the If-Modified-Since date of the request */ + c->ims = BAD_DATE; + imstr = table_get(r->headers_in, "If-Modified-Since"); + if (imstr != NULL) + { +/* this may modify the value in the original table */ + imstr = proxy_date_canon(r->pool, imstr); + c->ims = parseHTTPdate(imstr); + if (c->ims == BAD_DATE) /* bad or out of range date; remove it */ + table_set(r->headers_in, "If-Modified-Since", NULL); + } + +/* find the filename for this cache entry */ + proxy_hash(url, hashfile,pconf->cache.dirlevels,pconf->cache.dirlength); + if (conf->root != NULL) + c->filename = pstrcat(r->pool, conf->root, "/", hashfile, NULL); + else + c->filename = NULL; + + cachefp = NULL; +/* find out about whether the request can access the cache */ + pragma = table_get(r->headers_in, "Pragma"); + auth = table_get(r->headers_in, "Authorization"); + Explain5("Request for %s, pragma=%s, auth=%s, ims=%ld, imstr=%s",url, + pragma,auth,c->ims,imstr); + if (c->filename != NULL && r->method_number == M_GET && + strlen(url) < 1024 && !proxy_liststr(pragma, "no-cache") && + auth == NULL) + { + Explain1("Check file %s",c->filename); + cfd = open(c->filename, O_RDWR); + if (cfd != -1) + { + note_cleanups_for_fd(r->pool, cfd); + cachefp = bcreate(r->pool, B_RD | B_WR); + bpushfd(cachefp, cfd, cfd); + } else if (errno != ENOENT) + proxy_log_uerror("open", c->filename, + "proxy: error opening cache file", r->server); +#ifdef EXPLAIN + else + Explain1("File %s not found",c->filename); +#endif + } + + if (cachefp != NULL) + { + i = rdcache(r->pool, cachefp, c); + if (i == -1) + proxy_log_uerror("read", c->filename, + "proxy: error reading cache file", r->server); + else if (i == 0) + log_error("proxy: bad cache file", r->server); + if (i != 1) + { + pclosef(r->pool, cachefp->fd); + cachefp = NULL; + } + } + if (cachefp == NULL) + c->hdrs = make_array(r->pool, 2, sizeof(struct hdr_entry)); + /* FIXME: Shouldn't we check the URL somewhere? */ + now = time(NULL); +/* Ok, have we got some un-expired data? */ + if (cachefp != NULL && c->expire != BAD_DATE && now < c->expire) + { + Explain0("Unexpired data available"); +/* check IMS */ + if (c->lmod != BAD_DATE && c->ims != BAD_DATE && c->ims >= c->lmod) + { +/* has the cached file changed since this request? */ + if (c->date == BAD_DATE || c->date > c->ims) + { +/* No, but these header values may have changed, so we send them with the + * 304 response + */ + /* CHECKME: surely this was wrong? (Ben) + p = table_get(r->headers_in, "Expires"); + */ + p = table_get(c->hdrs, "Expires"); + if (p != NULL) table_set(r->headers_out, "Expires", p); + } + pclosef(r->pool, cachefp->fd); + Explain0("Use local copy, cached file hasn't changed"); + return USE_LOCAL_COPY; + } + +/* Ok, has been modified */ + Explain0("Local copy modified, send it"); + r->status_line = strchr(c->resp_line, ' ') + 1; + r->status = c->status; + if (!r->assbackwards) { + soft_timeout("proxy send headers", r); + proxy_send_headers(r->connection->client, c->resp_line, c->hdrs); + kill_timeout(r); + } + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; + if (!r->header_only) proxy_send_fb (cachefp, r, NULL, NULL); + pclosef(r->pool, cachefp->fd); + return OK; + } + +/* if we already have data and a last-modified date, and it is not a head + * request, then add an If-Modified-Since + */ + + if (cachefp != NULL && c->lmod != BAD_DATE && !r->header_only) + { +/* + * use the later of the one from the request and the last-modified date + * from the cache + */ + if (c->ims == BAD_DATE || c->ims < c->lmod) + { + struct hdr_entry *q; + + q = proxy_get_header(c->hdrs, "Last-Modified"); + + if (q != NULL && q->value != NULL) + table_set(r->headers_in, "If-Modified-Since", + (char *)q->value); + } + } + c->fp = cachefp; + + Explain0("Local copy not present or expired. Declining."); + + return DECLINED; +} + +/* + * Having read the response from the client, decide what to do + * If the response is not cachable, then delete any previously cached + * response, and copy data from remote server to client. + * Functions: + * parse dates + * check for an uncachable response + * calculate an expiry date, if one is not provided + * if the remote file has not been modified, then return the document + * from the cache, maybe updating the header line + * otherwise, delete the old cached file and open a new temporary file + */ +int +proxy_cache_update(struct cache_req *c, array_header *resp_hdrs, + const int is_HTTP1, int nocache) +{ + request_rec *r=c->req; + char *p; + int i; + struct hdr_entry *expire, *dates, *lmods, *clen; + time_t expc, date, lmod, now; + char buff[46]; + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + const long int zero=0L; + + c->tempfile = NULL; + +/* we've received the response */ +/* read expiry date; if a bad date, then leave it so the client can + * read it + */ + expire = proxy_get_header(resp_hdrs, "Expires"); + if (expire != NULL) expc = parseHTTPdate(expire->value); + else expc = BAD_DATE; + +/* + * read the last-modified date; if the date is bad, then delete it + */ + lmods = proxy_get_header(resp_hdrs, "Last-Modified"); + if (lmods != NULL) + { + lmod = parseHTTPdate(lmods->value); + if (lmod == BAD_DATE) + { +/* kill last modified date */ + lmods->value = NULL; + lmods = NULL; + } + } else + lmod = BAD_DATE; + +/* + * what responses should we not cache? + * Unknown status responses and those known to be uncacheable + * 304 response when we have no valid cache file, or + * 200 response from HTTP/1.0 and up without a Last-Modified header, or + * HEAD requests, or + * requests with an Authorization header, or + * protocol requests nocache (e.g. ftp with user/password) + */ + if ((r->status != 200 && r->status != 301 && r->status != 304) || + (expire != NULL && expc == BAD_DATE) || + (r->status == 304 && c->fp == NULL) || + (r->status == 200 && lmods == NULL && is_HTTP1) || + r->header_only || + table_get(r->headers_in, "Authorization") != NULL || + nocache) + { + Explain1("Response is not cacheable, unlinking %s",c->filename); +/* close the file */ + if (c->fp != NULL) + { + pclosef(r->pool, c->fp->fd); + c->fp = NULL; + } +/* delete the previously cached file */ + unlink(c->filename); + return DECLINED; /* send data to client but not cache */ + } + +/* otherwise, we are going to cache the response */ +/* + * Read the date. Generate one if one is not supplied + */ + dates = proxy_get_header(resp_hdrs, "Date"); + if (dates != NULL) date = parseHTTPdate(dates->value); + else date = BAD_DATE; + + now = time(NULL); + + if (date == BAD_DATE) /* No, or bad date */ + { +/* no date header! */ +/* add one; N.B. use the time _now_ rather than when we were checking the cache + */ + date = abs(now); + p = gm_timestr_822(r->pool, now); + dates = proxy_add_header(resp_hdrs, "Date", p, HDR_REP); + Explain0("Added date header"); + } + +/* check last-modified date */ + if (lmod != BAD_DATE && lmod > date) +/* if its in the future, then replace by date */ + { + lmod = date; + lmods->value = dates->value; + Explain0("Last modified is in the future, replacing with now"); + } +/* if the response did not contain the header, then use the cached version */ + if (lmod == BAD_DATE && c->fp != NULL) + { + lmod = c->lmod; + Explain0("Reusing cached last modified"); + } + +/* we now need to calculate the expire data for the object. */ + if (expire == NULL && c->fp != NULL) /* no expiry data sent in response */ + { + expire = proxy_get_header(c->hdrs, "Expires"); + if (expire != NULL) expc = parseHTTPdate(expire->value); + } +/* so we now have the expiry date */ +/* if no expiry date then + * if lastmod + * expiry date = now + min((date - lastmod) * factor, maxexpire) + * else + * expire date = now + defaultexpire + */ + Explain1("Expiry date is %ld",expc); + if (expc == BAD_DATE) + { + if (lmod != BAD_DATE) + { + double x = (double)(date - lmod)*conf->cache.lmfactor; + double maxex=conf->cache.maxexpire; + if (x > maxex) x = maxex; + expc = abs(now) + (int)x; + } else + expc = abs(now) + conf->cache.defaultexpire; + Explain1("Expiry date calculated %ld",expc); + } + +/* get the content-length header */ + clen = proxy_get_header(c->hdrs, "Content-Length"); + if (clen == NULL) c->len = -1; + else c->len = atoi(clen->value); + + proxy_sec2hex(date, buff); + buff[8] = ' '; + proxy_sec2hex(lmod, buff+9); + buff[17] = ' '; + proxy_sec2hex(expc, buff+18); + buff[26] = ' '; + proxy_sec2hex(c->version++, buff+27); + buff[35] = ' '; + proxy_sec2hex(c->len, buff+36); + buff[44] = '\n'; + buff[45] = '\0'; + +/* if file not modified */ + if (r->status == 304) + { + if (c->ims != BAD_DATE && lmod != BAD_DATE && lmod <= c->ims) + { +/* set any changed headers somehow */ +/* update dates and version, but not content-length */ + if (lmod != c->lmod || expc != c->expire || date != c->date) + { + off_t curpos=lseek(c->fp->fd, 0, SEEK_SET); + if (curpos == -1) + proxy_log_uerror("lseek", c->filename, + "proxy: error seeking on cache file",r->server); + else if (write(c->fp->fd, buff, 35) == -1) + proxy_log_uerror("write", c->filename, + "proxy: error updating cache file", r->server); + } + pclosef(r->pool, c->fp->fd); + Explain0("Remote document not modified, use local copy"); + /* CHECKME: Is this right? Shouldn't we check IMS again here? */ + return USE_LOCAL_COPY; + } else + { +/* return the whole document */ + Explain0("Remote document updated, sending"); + r->status_line = strchr(c->resp_line, ' ') + 1; + r->status = c->status; + if (!r->assbackwards) { + soft_timeout("proxy send headers", r); + proxy_send_headers(r->connection->client, c->resp_line, + c->hdrs); + kill_timeout(r); + } + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; + if (!r->header_only) proxy_send_fb (c->fp, r, NULL, NULL); +/* set any changed headers somehow */ +/* update dates and version, but not content-length */ + if (lmod != c->lmod || expc != c->expire || date != c->date) + { + off_t curpos=lseek(c->fp->fd, 0, SEEK_SET); + + if (curpos == -1) + proxy_log_uerror("lseek", c->filename, + "proxy: error seeking on cache file",r->server); + else if (write(c->fp->fd, buff, 35) == -1) + proxy_log_uerror("write", c->filename, + "proxy: error updating cache file", r->server); + } + pclosef(r->pool, c->fp->fd); + return OK; + } + } +/* new or modified file */ + if (c->fp != NULL) + { + pclosef(r->pool, c->fp->fd); + c->fp->fd = -1; + } + c->version = 0; + proxy_sec2hex(0, buff+27); + buff[35] = ' '; + +/* open temporary file */ +#define TMPFILESTR "/tmpXXXXXX" + if (conf->cache.root == NULL) + return DECLINED; + c->tempfile=palloc(r->pool,strlen(conf->cache.root)+sizeof(TMPFILESTR)); + strcpy(c->tempfile,conf->cache.root); + strcat(c->tempfile,TMPFILESTR); +#undef TMPFILESTR + p = mktemp(c->tempfile); + if (p == NULL) + return DECLINED; + + Explain1("Create temporary file %s",c->tempfile); + + i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL, 0622); + if (i == -1) + { + proxy_log_uerror("open", c->tempfile, + "proxy: error creating cache file", r->server); + return DECLINED; + } + note_cleanups_for_fd(r->pool, i); + c->fp = bcreate(r->pool, B_WR); + bpushfd(c->fp, -1, i); + + if (bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) + { + proxy_log_uerror("write", c->tempfile, + "proxy: error writing cache file", r->server); + pclosef(r->pool, c->fp->fd); + unlink(c->tempfile); + c->fp = NULL; + } + return DECLINED; +} + +void +proxy_cache_tidy(struct cache_req *c) +{ + server_rec *s=c->req->server; + long int bc; + + if (c->fp == NULL) return; + + bgetopt(c->req->connection->client, BO_BYTECT, &bc); + + if (c->len != -1) + { +/* file lengths don't match; don't cache it */ + if (bc != c->len) + { + pclosef(c->req->pool, c->fp->fd); /* no need to flush */ + unlink(c->tempfile); + return; + } + } else + if (c->req->connection->aborted) { + pclosef(c->req->pool, c->fp->fd); /* no need to flush */ + unlink(c->tempfile); + return; + } else + { +/* update content-length of file */ + char buff[9]; + off_t curpos; + + c->len = bc; + bflush(c->fp); + proxy_sec2hex(c->len, buff); + curpos = lseek(c->fp->fd, 36, SEEK_SET); + if (curpos == -1) + proxy_log_uerror("lseek", c->tempfile, + "proxy: error seeking on cache file", s); + else if (write(c->fp->fd, buff, 8) == -1) + proxy_log_uerror("write", c->tempfile, + "proxy: error updating cache file", s); + } + + if (bflush(c->fp) == -1) + { + proxy_log_uerror("write", c->tempfile, + "proxy: error writing to cache file", s); + pclosef(c->req->pool, c->fp->fd); + unlink(c->tempfile); + return; + } + + if (pclosef(c->req->pool, c->fp->fd) == -1) + { + proxy_log_uerror("close", c->tempfile, + "proxy: error closing cache file", s); + unlink(c->tempfile); + return; + } + + if (unlink(c->filename) == -1 && errno != ENOENT) + { + proxy_log_uerror("unlink", c->filename, + "proxy: error deleting old cache file", s); + } else + { + char *p; + proxy_server_conf *conf= + (proxy_server_conf *)get_module_config(s->module_config,&proxy_module); + + for(p=c->filename+strlen(conf->cache.root)+1 ; ; ) + { + p=strchr(p,'/'); + if(!p) + break; + *p='\0'; + if(mkdir(c->filename,S_IREAD|S_IWRITE|S_IEXEC) < 0 && errno != EEXIST) + proxy_log_uerror("mkdir",c->filename, + "proxy: error creating cache directory",s); + *p='/'; + ++p; + } +#ifdef __EMX__ + /* Under OS/2 use rename. */ + if (rename(c->tempfile, c->filename) == -1) + proxy_log_uerror("rename", c->filename, + "proxy: error renaming cache file", s); +} +#else + + if (link(c->tempfile, c->filename) == -1) + proxy_log_uerror("link", c->filename, + "proxy: error linking cache file", s); + } + + if (unlink(c->tempfile) == -1) + proxy_log_uerror("unlink", c->tempfile, + "proxy: error deleting temp file",s); +#endif + +} + diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_connect.c b/usr.sbin/httpd/src/modules/proxy/proxy_connect.c new file mode 100644 index 00000000000..ba120271b66 --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/proxy_connect.c @@ -0,0 +1,228 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* CONNECT method SSL handling for Apache proxy */ + +#include "mod_proxy.h" +#include "http_log.h" +#include "http_main.h" + +#ifdef HAVE_BSTRING_H +#include <bstring.h> /* for IRIX, FD_SET calls bzero() */ +#endif + +/* + * This handles Netscape CONNECT method secure proxy requests. + * A connection is opened to the specified host and data is + * passed through between the WWW site and the browser. + * + * This code is based on the INTERNET-DRAFT document + * "Tunneling SSL Through a WWW Proxy" currently at + * http://www.mcom.com/newsref/std/tunneling_ssl.html. + * + * FIXME: this is bad, because it does its own socket I/O + * instead of using the I/O in buff.c. However, + * the I/O in buff.c blocks on reads, and because + * this function doesn't know how much data will + * be sent either way (or when) it can't use blocking + * I/O. This may be very implementation-specific + * (to Linux). Any suggestions? + * FIXME: this doesn't log the number of bytes sent, but + * that may be okay, since the data is supposed to + * be transparent. In fact, this doesn't log at all + * yet. 8^) + * FIXME: doesn't check any headers initally sent from the + * client. + * FIXME: should allow authentication, but hopefully the + * generic proxy authentication is good enough. + * FIXME: no check for r->assbackwards, whatever that is. + */ + +int +proxy_connect_handler(request_rec *r, struct cache_req *c, char *url) +{ + struct sockaddr_in server; + struct in_addr destaddr; + struct hostent server_hp; + const char *host, *err; + char *p; + int port, sock; + char buffer[HUGE_STRING_LEN]; + int nbytes, i, j; + fd_set fds; + + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + struct noproxy_entry *npent=(struct noproxy_entry *)conf->noproxies->elts; + + memset(&server, '\0', sizeof(server)); + server.sin_family=AF_INET; + + /* Break the URL into host:port pairs */ + + host = url; + p = strchr(url, ':'); + if (p==NULL) + port = DEFAULT_HTTPS_PORT; + else + { + port = atoi(p+1); + *p='\0'; + } + +/* check if ProxyBlock directive on this host */ + destaddr.s_addr = inet_addr(host); + for (i=0; i < conf->noproxies->nelts; i++) + { + if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) + || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') + return proxyerror(r, "Connect to remote machine blocked"); + } + + switch (port) + { + case DEFAULT_HTTPS_PORT: + case DEFAULT_SNEWS_PORT: + break; + default: + return HTTP_SERVICE_UNAVAILABLE; + } + + Explain2("CONNECT to %s on port %d", host, port); + + server.sin_port = htons(port); + err = proxy_host2addr(host, &server_hp); + if (err != NULL) + return proxyerror(r, err); /* give up */ + + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + { + log_error("proxy: error creating socket", r->server); + return SERVER_ERROR; + } + note_cleanups_for_fd(r->pool, sock); + + j = 0; + while (server_hp.h_addr_list[j] != NULL) { + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; + } + if (i == -1 ) + return proxyerror(r, "Could not connect to remote machine"); + + Explain0("Returning 200 OK Status"); + + rvputs(r, "HTTP/1.0 200 Connection established\015\012", NULL); + rvputs(r, "Proxy-agent: ", SERVER_VERSION, "\015\012\015\012", NULL); + bflush(r->connection->client); + + while (1) /* Infinite loop until error (one side closes the connection) */ + { + FD_ZERO(&fds); + FD_SET(sock, &fds); + FD_SET(r->connection->client->fd, &fds); + + Explain0("Going to sleep (select)"); + i = select((r->connection->client->fd > sock ? + r->connection->client->fd+1 : +#ifdef HPUX + sock+1), (int*)&fds, NULL, NULL, NULL); +#else + sock+1), &fds, NULL, NULL, NULL); +#endif + Explain1("Woke from select(), i=%d",i); + + if (i) + { + if (FD_ISSET(sock, &fds)) + { + Explain0("sock was set"); + if((nbytes=read(sock,buffer,HUGE_STRING_LEN))!=0) + { + if (nbytes==-1) + break; + if (write(r->connection->client->fd, buffer, nbytes)==EOF) + break; + Explain1("Wrote %d bytes to client", nbytes); + } + else break; + } + else if (FD_ISSET(r->connection->client->fd, &fds)) + { + Explain0("client->fd was set"); + if((nbytes=read(r->connection->client->fd,buffer, + HUGE_STRING_LEN))!=0) + { + if (nbytes==-1) + break; + if (write(sock,buffer,nbytes)==EOF) + break; + Explain1("Wrote %d bytes to server", nbytes); + } + else break; + } + else break; /* Must be done waiting */ + } + else break; + } + + pclosef(r->pool,sock); + + return OK; +} + diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c b/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c new file mode 100644 index 00000000000..f460adf9a25 --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/proxy_ftp.c @@ -0,0 +1,1015 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* FTP routines for Apache proxy */ + +#include "mod_proxy.h" +#include "http_main.h" + +extern int find_ct(request_rec *r); + +/* + * Decodes a '%' escaped string, and returns the number of characters + */ +static int +decodeenc(char *x) +{ + int i, j, ch; + + if (x[0] == '\0') return 0; /* special case for no characters */ + for (i=0, j=0; x[i] != '\0'; i++, j++) + { +/* decode it if not already done */ + ch = x[i]; + if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2])) + { + ch = proxy_hex2c(&x[i+1]); + i += 2; + } + x[j] = ch; + } + x[j] = '\0'; + return j; +} + +/* + * checks an encoded ftp string for bad characters, namely, CR, LF or + * non-ascii character + */ +static int +ftp_check_string(const char *x) +{ + int i, ch; + + for (i=0; x[i] != '\0'; i++) + { + ch = x[i]; + if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2])) + { + ch = proxy_hex2c(&x[i+1]); + i += 2; + } + if (ch == '\015' || ch == '\012' || (ch & 0x80)) return 0; + } + return 1; +} + +/* + * Canonicalise ftp URLs. + */ +int +proxy_ftp_canon(request_rec *r, char *url) +{ + char *user, *password, *host, *path, *parms, *p, sport[7]; + pool *pool=r->pool; + const char *err; + int port; + + port = DEFAULT_FTP_PORT; + err = proxy_canon_netloc(pool, &url, &user, &password, &host, &port); + if (err) return BAD_REQUEST; + if (user != NULL && !ftp_check_string(user)) return BAD_REQUEST; + if (password != NULL && !ftp_check_string(password)) return BAD_REQUEST; + +/* now parse path/parameters args, according to rfc1738 */ +/* N.B. if this isn't a true proxy request, then the URL path + * (but not query args) has already been decoded. + * This gives rise to the problem of a ; being decoded into the + * path. + */ + p = strchr(url, ';'); + if (p != NULL) + { + *(p++) = '\0'; + parms = proxy_canonenc(pool, p, strlen(p), enc_parm, r->proxyreq); + if (parms == NULL) return BAD_REQUEST; + } else + parms = ""; + + path = proxy_canonenc(pool, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) return BAD_REQUEST; + if (!ftp_check_string(path)) return BAD_REQUEST; + + if (!r->proxyreq && r->args != NULL) + { + if (p != NULL) + { + p = proxy_canonenc(pool, r->args, strlen(r->args), enc_parm, 1); + if (p == NULL) return BAD_REQUEST; + parms = pstrcat(pool, parms, "?", p, NULL); + } + else + { + p = proxy_canonenc(pool, r->args, strlen(r->args), enc_fpath, 1); + if (p == NULL) return BAD_REQUEST; + path = pstrcat(pool, path, "?", p, NULL); + } + r->args = NULL; + } + +/* now, rebuild URL */ + + if (port != DEFAULT_FTP_PORT) ap_snprintf(sport, sizeof(sport), ":%d", port); + else sport[0] = '\0'; + + r->filename = pstrcat(pool, "proxy:ftp://", (user != NULL) ? user : "", + (password != NULL) ? ":" : "", + (password != NULL) ? password : "", + (user != NULL) ? "@" : "", host, sport, "/", path, + (parms[0] != '\0') ? ";" : "", parms, NULL); + + return OK; +} + +/* + * Returns the ftp status code; + * or -1 on I/O error, 0 on data error + */ +static int +ftp_getrc(BUFF *f) +{ + int i, len, status; + char linebuff[100], buff[5]; + + len = bgets(linebuff, 100, f); + if (len == -1) return -1; +/* check format */ + if (len < 5 || !isdigit(linebuff[0]) || !isdigit(linebuff[1]) || + !isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) + status = 0; + else + status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; + + if (linebuff[len-1] != '\n') + { + i = bskiplf(f); + } + +/* skip continuation lines */ + if (linebuff[3] == '-') + { + memcpy(buff, linebuff, 3); + buff[3] = ' '; + do + { + len = bgets(linebuff, 100, f); + if (len == -1) return -1; + if (linebuff[len-1] != '\n') + { + i = bskiplf(f); + } + } while (memcmp(linebuff, buff, 4) != 0); + } + + return status; +} + +static char * +encode_space(request_rec *r, char *path) +{ + pool *pool=r->pool; + char *newpath; + int i, j, len; + + len = strlen(path); + newpath = palloc(pool, 3 * len + 1); + for (i=0, j=0; i < len; i++, j++) { + if (path[i] != ' ') + newpath[j] = path[i]; + else { + proxy_c2hex(' ', &newpath[j]); + j += 2; + } + } + newpath[j] = '\0'; + return newpath; +} + +static long int +send_dir(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c, char *url) +{ + char buf[IOBUFSIZE]; + char buf2[IOBUFSIZE]; + char *filename; + char *tempurl; + char *newurlptr; + int searchidx = 0; + char *searchptr = NULL; + int firstfile = 1; + char urlptr[HUGE_STRING_LEN]; + long total_bytes_sent; + register int n, o, w; + conn_rec *con = r->connection; + + tempurl = pstrdup(r->pool, url); + if ((n = strcspn(tempurl, "@")) != strlen(tempurl)) /* hide user/passwd */ + { + memmove(tempurl + (n - 5), tempurl, 6); + tempurl += n - 5; /* leave room for ftp:// */ + } + + n = decodeenc(tempurl); + ap_snprintf(buf, sizeof(buf), "<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>Directory %s</H1><HR><PRE>", tempurl, tempurl); + bwrite(con->client, buf, strlen(buf)); + if (f2 != NULL) bwrite(f2, buf, strlen(buf)); + total_bytes_sent=strlen(buf); + while(!con->aborted) + { + n = bgets(buf, IOBUFSIZE, f); + if (n == -1) /* input error */ + { + if (f2 != NULL) f2 = proxy_cache_error(c); + break; + } + if (n == 0) break; /* EOF */ + if(buf[0]=='l') + { + char *link; + + link=strstr(buf, " -> "); + filename=link; + do filename--; while (filename[0]!=' '); + *(filename++)=0; + *(link++)=0; + ap_snprintf(urlptr, sizeof(urlptr), "%s%s%s",url,(url[strlen(url)-1]=='/' ? "" : "/"), filename); + ap_snprintf(buf2, sizeof(urlptr), "%s <A HREF=\"%s\">%s %s</A>\015\012", buf, urlptr, filename, link); + strncpy(buf, buf2, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + n=strlen(buf); + } + else if (strrchr(buf, ' ') && (buf[0]=='d' || buf[0]=='-' || + buf[0]=='l' || isdigit(buf[0])) ) { + if(isdigit(buf[0])) { /* handle DOS dir */ + searchptr = strchr(buf, '<'); + if(searchptr != NULL) + *searchptr = '['; + searchptr = strchr(buf, '>'); + if(searchptr != NULL) + *searchptr = ']'; + } + + filename=strrchr(buf, ' '); + *(filename++)=0; + filename[strlen(filename)-1]=0; + + /* handle filenames with spaces in 'em */ + if(!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) { + firstfile = 0; + searchidx = filename - buf; + } + else if (searchidx != 0 && buf[searchidx] != 0) { + *(--filename) = ' '; + buf[searchidx - 1] = 0; + filename = &buf[searchidx]; + } + + /* Special handling for '.' and '..' */ + if (!strcmp(filename, ".")) + { + ap_snprintf(urlptr, sizeof(urlptr), "%s",url); + ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename); + } + else if (!strcmp(filename, "..")) + { + char temp[200]; + char newpath[200]; + char *method, *host, *path, *newfile; + + strncpy(temp, url, sizeof(temp)-1); + temp[sizeof(temp)-1] = '\0'; + method=temp; + + host=strchr(method,':'); + if (host == NULL) host=""; + else *(host++)=0; + host++; host++; + + path=strchr(host,'/'); + if (path == NULL) path=""; + else *(path++)=0; + + strncpy(newpath, path, sizeof(newpath)-1); + newpath[sizeof(newpath)-1] = '\0'; + newfile=strrchr(newpath,'/'); + if (newfile) *(newfile)=0; + else newpath[0]=0; + + ap_snprintf(urlptr, sizeof(urlptr), "%s://%s/%s",method,host,newpath); + ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename); + } + else + { + ap_snprintf(urlptr, sizeof(urlptr), "%s%s%s",url,(url[strlen(url)-1]=='/' ? "" : "/"), filename); + newurlptr = encode_space(r, urlptr); + ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\015\012", buf, newurlptr, filename); + } + strncpy(buf, buf2, sizeof(buf)); + buf[sizeof(buf)-1] = '\0'; + n=strlen(buf); + } + + o=0; + total_bytes_sent += n; + + if (f2 != NULL) + if (bwrite(f2, buf, n) != n) f2 = proxy_cache_error(c); + + while(n && !r->connection->aborted) { + w = bwrite(con->client, &buf[o], n); + if (w <= 0) + break; + reset_timeout(r); /* reset timeout after successfule write */ + n-=w; + o+=w; + } + } + bputs("</PRE><HR></BODY></HTML>\015\012", con->client); + if (f2 != NULL) { + bputs("</PRE><HR></BODY></HTML>\015\012", f2); + } + total_bytes_sent+=strlen(buf); + bflush(con->client); + + return total_bytes_sent; +} + +/* + * Handles direct access of ftp:// URLs + * Original (Non-PASV) version from + * Troy Morrison <spiffnet@zoom.com> + * PASV added by Chuck + */ +int +proxy_ftp_handler(request_rec *r, struct cache_req *c, char *url) +{ + char *host, *path, *p, *user, *password, *parms; + const char *err; + int port, userlen, i, j, len, sock, dsock, rc, nocache; + int passlen = 0; + int csd = 0; + struct sockaddr_in server; + struct hostent server_hp; + struct hdr_entry *hdr; + struct in_addr destaddr; + array_header *resp_hdrs; + BUFF *f, *cache; + BUFF *data = NULL; + pool *pool=r->pool; + const int one=1; + const long int zero=0L; + + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + struct noproxy_entry *npent=(struct noproxy_entry *)conf->noproxies->elts; + struct nocache_entry *ncent=(struct nocache_entry *)conf->nocaches->elts; + +/* stuff for PASV mode */ + unsigned int presult, h0, h1, h2, h3, p0, p1; + unsigned int paddr; + unsigned short pport; + struct sockaddr_in data_addr; + int pasvmode = 0; + char pasv[64]; + char *pstr; + +/* we only support GET and HEAD */ + + if (r->method_number != M_GET) return NOT_IMPLEMENTED; + +/* We break the URL into host, port, path-search */ + + host = pstrdup(pool, url + 6); + port = DEFAULT_FTP_PORT; + path = strchr(host, '/'); + if (path == NULL) + path = ""; + else + *(path++) = '\0'; + + user = password = NULL; + nocache = 0; + p = strchr(host, '@'); + if (p != NULL) + { + (*p++) = '\0'; + user = host; + host = p; +/* find password */ + p = strchr(user, ':'); + if (p != NULL) + { + *(p++) = '\0'; + password = p; + passlen = decodeenc(password); + } + userlen = decodeenc(user); + nocache = 1; /* don't cache when a username is supplied */ + } else + { + user = "anonymous"; + userlen = 9; + + password = "apache_proxy@"; + passlen = strlen(password); + } + + p = strchr(host, ':'); + if (p != NULL) + { + *(p++) = '\0'; + if (isdigit(*p)) + port = atoi(p); + } + +/* check if ProxyBlock directive on this host */ + destaddr.s_addr = inet_addr(host); + for (i=0; i < conf->noproxies->nelts; i++) + { + if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL) + || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') + return proxyerror(r, "Connect to remote machine blocked"); + } + + Explain2("FTP: connect to %s:%d",host,port); + + parms = strchr(path, ';'); + if (parms != NULL) *(parms++) = '\0'; + + memset(&server, 0, sizeof(struct sockaddr_in)); + server.sin_family = AF_INET; + server.sin_port = htons(port); + err = proxy_host2addr(host, &server_hp); + if (err != NULL) return proxyerror(r, err); /* give up */ + + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + { + proxy_log_uerror("socket", NULL, "proxy: error creating socket", + r->server); + return SERVER_ERROR; + } + note_cleanups_for_fd(pool, sock); + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, + sizeof(int)) == -1) + { + proxy_log_uerror("setsockopt", NULL, + "proxy: error setting reuseaddr option", r->server); + pclosef(pool, sock); + return SERVER_ERROR; + } + + j = 0; + while (server_hp.h_addr_list[j] != NULL) { + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; + } + if (i == -1) + return proxyerror(r, "Could not connect to remote machine"); + + f = bcreate(pool, B_RDWR); + bpushfd(f, sock, sock); +/* shouldn't we implement telnet control options here? */ + +/* possible results: 120, 220, 421 */ + hard_timeout ("proxy ftp", r); + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + if (i == -1) { + kill_timeout(r); + return proxyerror(r, "Error reading from remote server"); + } + if (i != 220) { + kill_timeout(r); + return BAD_GATEWAY; + } + + Explain0("FTP: connected."); + + bputs("USER ", f); + bwrite(f, user, userlen); + bputs("\015\012", f); + bflush(f); /* capture any errors */ + Explain1("FTP: USER %s",user); + +/* possible results; 230, 331, 332, 421, 500, 501, 530 */ +/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d",i); + if (i == -1) { + kill_timeout(r); + return proxyerror(r, "Error sending to remote server"); + } + if (i == 530) { + kill_timeout(r); + return proxyerror(r, "Not logged in"); + } + if (i != 230 && i != 331) { + kill_timeout(r); + return BAD_GATEWAY; + } + + if (i == 331) /* send password */ + { + if (password == NULL) return FORBIDDEN; + bputs("PASS ", f); + bwrite(f, password, passlen); + bputs("\015\012", f); + bflush(f); + Explain1("FTP: PASS %s",password); +/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d",i); + if (i == -1) { + kill_timeout(r); + return proxyerror(r, "Error sending to remote server"); + } + if (i == 332) { + kill_timeout(r); + return proxyerror(r, "Need account for login"); + } + if (i == 530) { + kill_timeout(r); + return proxyerror(r, "Not logged in"); + } + if (i != 230 && i != 202) { + kill_timeout(r); + return BAD_GATEWAY; + } + } + +/* set the directory */ +/* this is what we must do if we don't know the OS type of the remote + * machine + */ + for (;;) + { + p = strchr(path, '/'); + if (p == NULL) break; + *p = '\0'; + + len = decodeenc(path); + bputs("CWD ", f); + bwrite(f, path, len); + bputs("\015\012", f); + bflush(f); + Explain1("FTP: CWD %s",path); +/* responses: 250, 421, 500, 501, 502, 530, 550 */ +/* 1,3 error, 2 success, 4,5 failure */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d",i); + if (i == -1) { + kill_timeout(r); + return proxyerror(r, "Error sending to remote server"); + } + if (i == 550) { + kill_timeout(r); + return NOT_FOUND; + } + if (i != 250) { + kill_timeout(r); + return BAD_GATEWAY; + } + + path = p + 1; + } + + if (parms != NULL && strncmp(parms, "type=", 5) == 0) + { + parms += 5; + if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') || + parms[1] != '\0') parms = ""; + } + else parms = ""; + + /* changed to make binary transfers the default */ + + if (parms[0] != 'a') + { + /* set type to image */ + /* TM - Added \015\012 to the end of TYPE I, otherwise it hangs the + connection */ + bputs("TYPE I\015\012", f); + bflush(f); + Explain0("FTP: TYPE I"); +/* responses: 200, 421, 500, 501, 504, 530 */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d",i); + if (i == -1) { + kill_timeout(r); + return proxyerror(r, "Error sending to remote server"); + } + if (i != 200 && i != 504) { + kill_timeout(r); + return BAD_GATEWAY; + } +/* Allow not implemented */ + if (i == 504) + parms[0] = '\0'; + } + +/* try to set up PASV data connection first */ + dsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (dsock == -1) + { + proxy_log_uerror("socket", NULL, "proxy: error creating PASV socket", + r->server); + pclosef(pool, sock); + kill_timeout(r); + return SERVER_ERROR; + } + note_cleanups_for_fd(pool, dsock); + + bputs("PASV\015\012", f); + bflush(f); + Explain0("FTP: PASV command issued"); +/* possible results: 227, 421, 500, 501, 502, 530 */ + i = bgets(pasv, sizeof(pasv), f); + + if (i == -1) + { + proxy_log_uerror("command", NULL, "PASV: control connection is toast", + r->server); + pclosef(pool, dsock); + pclosef(pool, sock); + kill_timeout(r); + return SERVER_ERROR; + } else + { + pasv[i-1] = '\0'; + pstr = strtok(pasv, " "); /* separate result code */ + if (pstr != NULL) + { + presult = atoi(pstr); + pstr = strtok(NULL, "("); /* separate address & port params */ + if (pstr != NULL) + pstr = strtok(NULL, ")"); + } + else + presult = atoi(pasv); + + Explain1("FTP: returned status %d", presult); + + if (presult == 227 && pstr != NULL && (sscanf(pstr, + "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) + { + /* pardon the parens, but it makes gcc happy */ + paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0; + pport = (p1 << 8) + p0; + Explain5("FTP: contacting host %d.%d.%d.%d:%d", + h3, h2, h1, h0, pport); + data_addr.sin_family = AF_INET; + data_addr.sin_addr.s_addr = htonl(paddr); + data_addr.sin_port = htons(pport); + i = proxy_doconnect(dsock, &data_addr, r); + + if (i == -1) { + kill_timeout(r); + return proxyerror(r, "Could not connect to remote machine"); + } + else { + data = bcreate(pool, B_RDWR); + bpushfd(data, dsock, dsock); + pasvmode = 1; + } + } else + pclosef(pool, dsock); /* and try the regular way */ + } + + if (!pasvmode) /* set up data connection */ + { + len = sizeof(struct sockaddr_in); + if (getsockname(sock, (struct sockaddr *)&server, &len) < 0) + { + proxy_log_uerror("getsockname", NULL, + "proxy: error getting socket address", r->server); + pclosef(pool, sock); + kill_timeout(r); + return SERVER_ERROR; + } + + dsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (dsock == -1) + { + proxy_log_uerror("socket", NULL, "proxy: error creating socket", + r->server); + pclosef(pool, sock); + kill_timeout(r); + return SERVER_ERROR; + } + note_cleanups_for_fd(pool, dsock); + + if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, + sizeof(int)) == -1) + { + proxy_log_uerror("setsockopt", NULL, + "proxy: error setting reuseaddr option", r->server); + pclosef(pool, dsock); + pclosef(pool, sock); + kill_timeout(r); + return SERVER_ERROR; + } + + if (bind(dsock, (struct sockaddr *)&server, + sizeof(struct sockaddr_in)) == -1) + { + char buff[22]; + + ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port); + proxy_log_uerror("bind", buff, + "proxy: error binding to ftp data socket", r->server); + pclosef(pool, sock); + pclosef(pool, dsock); + } + listen(dsock, 2); /* only need a short queue */ + } + +/* set request */ + len = decodeenc(path); + + /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */ + + if(len==0) + { + parms="d"; + } else + { + bputs("SIZE ", f); + bwrite(f, path, len); + bputs("\015\012", f); + bflush(f); + Explain1("FTP: SIZE %s",path); + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + if (i != 500) /* Size command not recognized */ + { + if (i==550) /* Not a regular file */ + { + Explain0("FTP: SIZE shows this is a directory"); + parms="d"; + bputs("CWD ", f); + bwrite(f, path, len); + bputs("\015\012", f); + bflush(f); + Explain1("FTP: CWD %s",path); + i = ftp_getrc(f); + Explain1("FTP: returned status %d", i); + if (i == -1) { + kill_timeout(r); + return proxyerror(r, "Error sending to remote server"); + } + if (i == 550) { + kill_timeout(r); + return NOT_FOUND; + } + if (i != 250) { + kill_timeout(r); + return BAD_GATEWAY; + } + path=""; len=0; + } + } + } + + if (parms[0] == 'd') + { + if (len != 0) bputs("LIST ", f); + else bputs("LIST -lag", f); + Explain1("FTP: LIST %s",(len==0 ? "" : path)); + } + else + { + bputs("RETR ", f); + Explain1("FTP: RETR %s",path); + } + bwrite(f, path, len); + bputs("\015\012", f); + bflush(f); +/* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550 + NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */ + rc = ftp_getrc(f); + Explain1("FTP: returned status %d",rc); + if (rc == -1) { + kill_timeout(r); + return proxyerror(r, "Error sending to remote server"); + } + if (rc == 550) + { + Explain0("FTP: RETR failed, trying LIST instead"); + parms="d"; + bputs("CWD ", f); + bwrite(f, path, len); + bputs("\015\012", f); + bflush(f); + Explain1("FTP: CWD %s", path); + rc = ftp_getrc(f); + Explain1("FTP: returned status %d", rc); + if (rc == -1) { + kill_timeout(r); + return proxyerror(r, "Error sending to remote server"); + } + if (rc == 550) { + kill_timeout(r); + return NOT_FOUND; + } + if (rc != 250) { + kill_timeout(r); + return BAD_GATEWAY; + } + + bputs("LIST -lag\015\012", f); + bflush(f); + Explain0("FTP: LIST -lag"); + rc = ftp_getrc(f); + Explain1("FTP: returned status %d", rc); + if (rc == -1) return proxyerror(r, "Error sending to remote server"); + } + kill_timeout(r); + if (rc != 125 && rc != 150 && rc != 226 && rc != 250) return BAD_GATEWAY; + + r->status = 200; + r->status_line = "200 OK"; + + resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry)); + if (parms[0] == 'd') + proxy_add_header(resp_hdrs, "Content-Type", "text/html", HDR_REP); + else + { + find_ct(r); + if(r->content_type != NULL) + { + proxy_add_header(resp_hdrs, "Content-Type", r->content_type, + HDR_REP); + Explain1("FTP: Content-Type set to %s",r->content_type); + } else + { + proxy_add_header(resp_hdrs, "Content-Type", "text/plain", HDR_REP); + } + } + +/* check if NoCache directive on this host */ + for (i=0; i < conf->nocaches->nelts; i++) + { + if ((ncent[i].name != NULL && strstr(host, ncent[i].name) != NULL) + || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*') + nocache = 1; + } + + i = proxy_cache_update(c, resp_hdrs, 0, nocache); + + if (i != DECLINED) + { + pclosef(pool, dsock); + pclosef(pool, sock); + return i; + } + cache = c->fp; + + if (!pasvmode) /* wait for connection */ + { + hard_timeout ("proxy ftp data connect", r); + len = sizeof(struct sockaddr_in); + do csd = accept(dsock, (struct sockaddr *)&server, &len); + while (csd == -1 && errno == EINTR); + if (csd == -1) + { + proxy_log_uerror("accept", NULL, + "proxy: failed to accept data connection", r->server); + pclosef(pool, dsock); + pclosef(pool, sock); + kill_timeout(r); + proxy_cache_error(c); + return BAD_GATEWAY; + } + note_cleanups_for_fd(pool, csd); + data = bcreate(pool, B_RDWR); + bpushfd(data, csd, -1); + kill_timeout(r); + } + + hard_timeout ("proxy receive", r); +/* send response */ +/* write status line */ + if (!r->assbackwards) + rvputs(r, "HTTP/1.0 ", r->status_line, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, "HTTP/1.0 ", r->status_line, "\015\012", + NULL) == -1) + cache = proxy_cache_error(c); + +/* send headers */ + len = resp_hdrs->nelts; + hdr = (struct hdr_entry *)resp_hdrs->elts; + for (i=0; i < len; i++) + { + if (hdr[i].field == NULL || hdr[i].value == NULL || + hdr[i].value[0] == '\0') continue; + if (!r->assbackwards) + rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012", + NULL) == -1) + cache = proxy_cache_error(c); + } + + if (!r->assbackwards) rputs("\015\012", r); + if (cache != NULL) + if (bputs("\015\012", cache) == -1) cache = proxy_cache_error(c); + + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; +/* send body */ + if (!r->header_only) + { + if (parms[0] != 'd') proxy_send_fb(data, r, cache, c); + else send_dir(data, r, cache, c, url); + + if (rc == 125 || rc == 150) rc = ftp_getrc(f); + if (rc != 226 && rc != 250) proxy_cache_error(c); + } + else + { +/* abort the transfer */ + bputs("ABOR\015\012", f); + bflush(f); + if (!pasvmode) + pclosef(pool, csd); + Explain0("FTP: ABOR"); +/* responses: 225, 226, 421, 500, 501, 502 */ + i = ftp_getrc(f); + Explain1("FTP: returned status %d",i); + } + + kill_timeout(r); + proxy_cache_tidy(c); + +/* finish */ + bputs("QUIT\015\012", f); + bflush(f); + Explain0("FTP: QUIT"); +/* responses: 221, 500 */ + + if (!pasvmode) + pclosef(pool, csd); + pclosef(pool, dsock); + pclosef(pool, sock); + + proxy_garbage_coll(r); + + return OK; +} + diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_http.c b/usr.sbin/httpd/src/modules/proxy/proxy_http.c new file mode 100644 index 00000000000..1144564ee57 --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/proxy_http.c @@ -0,0 +1,413 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* HTTP routines for Apache proxy */ + +#include "mod_proxy.h" +#include "http_log.h" +#include "http_main.h" +#include "util_date.h" + +/* + * Canonicalise http-like URLs. + * scheme is the scheme for the URL + * url is the URL starting with the first '/' + * def_port is the default port for this scheme. + */ +int +proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_port) +{ + char *host, *path, *search, *p, sport[7]; + const char *err; + int port; + +/* do syntatic check. + * We break the URL into host, port, path, search + */ + port = def_port; + err = proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port); + if (err) return BAD_REQUEST; + +/* now parse path/search args, according to rfc1738 */ +/* N.B. if this isn't a true proxy request, then the URL _path_ + * has already been decoded + */ + if (r->proxyreq) + { + p = strchr(url, '?'); + if (p != NULL) *(p++) = '\0'; + } else + p = r->args; + +/* process path */ + path = proxy_canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); + if (path == NULL) return BAD_REQUEST; + +/* process search */ + if (p != NULL) + { + search = p; + if (search == NULL) return BAD_REQUEST; + } else + search = NULL; + + if (port != def_port) ap_snprintf(sport, sizeof(sport), ":%d", port); + else sport[0] = '\0'; + + r->filename = pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/", + path, (search) ? "?" : "", (search) ? search : "", NULL); + return OK; +} + +/* Clear all connection-based headers from the incoming headers table */ +static void clear_connection (table *headers) +{ + char *name; + char *next = table_get(headers, "Connection"); + + if (!next) return; + + while (*next) { + name = next; + while (*next && !isspace(*next) && (*next != ',')) ++next; + while (*next && (isspace(*next) || (*next == ','))) { + *next = '\0'; + ++next; + } + table_unset(headers, name); + } + table_unset(headers, "Connection"); +} + +/* + * This handles http:// URLs, and other URLs using a remote proxy over http + * If proxyhost is NULL, then contact the server directly, otherwise + * go via the proxy. + * Note that if a proxy is used, then URLs other than http: can be accessed, + * also, if we have trouble which is clearly specific to the proxy, then + * we return DECLINED so that we can try another proxy. (Or the direct + * route.) + */ +int +proxy_http_handler(request_rec *r, struct cache_req *c, char *url, + const char *proxyhost, int proxyport) +{ + char *p; + const char *err, *desthost; + int i, j, sock, len, backasswards; + array_header *reqhdrs_arr, *resp_hdrs; + table_entry *reqhdrs; + struct sockaddr_in server; + struct in_addr destaddr; + struct hostent server_hp; + BUFF *f, *cache; + struct hdr_entry *hdr; + char buffer[HUGE_STRING_LEN]; + pool *pool=r->pool; + const long int zero=0L; + int destport = 0; + char *destportstr = NULL; + char *urlptr = NULL; + + void *sconf = r->server->module_config; + proxy_server_conf *conf = + (proxy_server_conf *)get_module_config(sconf, &proxy_module); + struct noproxy_entry *npent=(struct noproxy_entry *)conf->noproxies->elts; + struct nocache_entry *ncent=(struct nocache_entry *)conf->nocaches->elts; + int nocache = 0; + + memset(&server, '\0', sizeof(server)); + server.sin_family = AF_INET; + +/* We break the URL into host, port, path-search */ + + urlptr = strstr(url,"://"); + if (urlptr == NULL) return BAD_REQUEST; + urlptr += 3; + destport = DEFAULT_PORT; + p = strchr(urlptr, '/'); + if (p == NULL) + { + desthost = pstrdup(pool, urlptr); + urlptr = "/"; + } else + { + char *q = palloc(pool, p-urlptr+1); + memcpy(q, urlptr, p-urlptr); + q[p-urlptr] = '\0'; + urlptr = p; + desthost = q; + } + + p = strchr(desthost, ':'); + if (p != NULL) + { + *(p++) = '\0'; + if (isdigit(*p)) + { + destport = atoi(p); + destportstr = p; + } + } + +/* check if ProxyBlock directive on this host */ + destaddr.s_addr = inet_addr(desthost); + for (i=0; i < conf->noproxies->nelts; i++) + { + if ((npent[i].name != NULL && strstr(desthost, npent[i].name) != NULL) + || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*') + return proxyerror(r, "Connect to remote machine blocked"); + } + + if (proxyhost != NULL) + { + server.sin_port = htons(proxyport); + err = proxy_host2addr(proxyhost, &server_hp); + if (err != NULL) return DECLINED; /* try another */ + } else + { + server.sin_port = htons(destport); + err = proxy_host2addr(desthost, &server_hp); + if (err != NULL) return proxyerror(r, err); /* give up */ + } + + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + { + log_error("proxy: error creating socket", r->server); + return SERVER_ERROR; + } + note_cleanups_for_fd(pool, sock); + + + j = 0; + while (server_hp.h_addr_list[j] != NULL) { + memcpy(&server.sin_addr, server_hp.h_addr_list[j], + sizeof(struct in_addr)); + i = proxy_doconnect(sock, &server, r); + if (i == 0) + break; + j++; + } + if (i == -1) + { + if (proxyhost != NULL) return DECLINED; /* try again another way */ + else return proxyerror(r, "Could not connect to remote machine"); + } + + clear_connection(r->headers_in); /* Strip connection-based headers */ + + f = bcreate(pool, B_RDWR); + bpushfd(f, sock, sock); + + hard_timeout ("proxy send", r); + bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0\015\012", + NULL); + bvputs(f, "Host: ", desthost, NULL); + if (destportstr != NULL && destport != DEFAULT_PORT) + bvputs(f, ":", destportstr, "\015\012", NULL); + else + bputs("\015\012", f); + + reqhdrs_arr = table_elts (r->headers_in); + reqhdrs = (table_entry *)reqhdrs_arr->elts; + for (i=0; i < reqhdrs_arr->nelts; i++) + { + if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL + || !strcasecmp(reqhdrs[i].key, "Host")) /* already sent if there */ + continue; + bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, "\015\012", NULL); + } + + bputs("\015\012", f); +/* send the request data, if any. N.B. should we trap SIGPIPE ? */ + + if (should_client_block(r)) + { + while ((i = get_client_block(r, buffer, HUGE_STRING_LEN)) > 0) + bwrite(f, buffer, i); + } + bflush(f); + kill_timeout(r); + + hard_timeout ("proxy receive", r); + + len = bgets(buffer, HUGE_STRING_LEN-1, f); + if (len == -1 || len == 0) + { + pclosef(pool, sock); + kill_timeout(r); + return proxyerror(r, "Error reading from remote server"); + } + +/* Is it an HTTP/1 response? This is buggy if we ever see an HTTP/1.10 */ + if (checkmask(buffer, "HTTP/#.# ###*")) + { +/* If not an HTTP/1 messsage or if the status line was > 8192 bytes */ + if (buffer[5] != '1' || buffer[len-1] != '\n') + { + pclosef(pool, sock); + kill_timeout(r); + return BAD_GATEWAY; + } + backasswards = 0; + buffer[--len] = '\0'; + + buffer[12] = '\0'; + r->status = atoi(&buffer[9]); + buffer[12] = ' '; + r->status_line = pstrdup(pool, &buffer[9]); + +/* read the headers. */ +/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ +/* Also, take care with headers with multiple occurences. */ + + resp_hdrs = proxy_read_headers(pool, buffer, HUGE_STRING_LEN, f); + + clear_connection((table *)resp_hdrs); /* Strip Connection hdrs */ + } + else + { +/* an http/0.9 response */ + backasswards = 1; + r->status = 200; + r->status_line = "200 OK"; + +/* no headers */ + resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry)); + } + + kill_timeout(r); + +/* + * HTTP/1.0 requires us to accept 3 types of dates, but only generate + * one type + */ + + hdr = (struct hdr_entry *)resp_hdrs->elts; + for (i=0; i < resp_hdrs->nelts; i++) + { + if (hdr[i].value[0] == '\0') continue; + p = hdr[i].field; + if (strcasecmp(p, "Date") == 0 || + strcasecmp(p, "Last-Modified") == 0 || + strcasecmp(p, "Expires") == 0) + hdr[i].value = proxy_date_canon(pool, hdr[i].value); + } + +/* check if NoCache directive on this host */ + for (i=0; i < conf->nocaches->nelts; i++) + { + if ((ncent[i].name != NULL && strstr(desthost, ncent[i].name) != NULL) + || destaddr.s_addr == ncent[i].addr.s_addr || ncent[i].name[0] == '*') + nocache = 1; + } + + i = proxy_cache_update(c, resp_hdrs, !backasswards, nocache); + if (i != DECLINED) + { + pclosef(pool, sock); + return i; + } + + cache = c->fp; + + hard_timeout ("proxy receive", r); + +/* write status line */ + if (!r->assbackwards) + rvputs(r, "HTTP/1.0 ", r->status_line, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, "HTTP/1.0 ", r->status_line, "\015\012", NULL) == -1) + cache = proxy_cache_error(c); + +/* send headers */ + for (i=0; i < resp_hdrs->nelts; i++) + { + if (hdr[i].field == NULL || hdr[i].value == NULL || + hdr[i].value[0] == '\0') continue; + if (!r->assbackwards) + rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL); + if (cache != NULL) + if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012", + NULL) == -1) + cache = proxy_cache_error(c); + } + + if (!r->assbackwards) rputs("\015\012", r); + if (cache != NULL) + if (bputs("\015\012", cache) == -1) cache = proxy_cache_error(c); + + bsetopt(r->connection->client, BO_BYTECT, &zero); + r->sent_bodyct = 1; +/* Is it an HTTP/0.9 respose? If so, send the extra data */ + if (backasswards) + { + bwrite(r->connection->client, buffer, len); + if (cache != NULL) + if (bwrite(f, buffer, len) != len) cache = proxy_cache_error(c); + } + kill_timeout(r); + +/* send body */ +/* if header only, then cache will be NULL */ +/* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */ + if (!r->header_only) proxy_send_fb(f, r, cache, c); + + proxy_cache_tidy(c); + + pclosef(pool, sock); + + proxy_garbage_coll(r); + return OK; +} + diff --git a/usr.sbin/httpd/src/modules/proxy/proxy_util.c b/usr.sbin/httpd/src/modules/proxy/proxy_util.c new file mode 100644 index 00000000000..524f4e04856 --- /dev/null +++ b/usr.sbin/httpd/src/modules/proxy/proxy_util.c @@ -0,0 +1,750 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* Utility routines for Apache proxy */ + +#include "mod_proxy.h" +#include "http_main.h" +#include "md5.h" + +/* already called in the knowledge that the characters are hex digits */ +int +proxy_hex2c(const char *x) +{ + int i, ch; + + ch = x[0]; + if (isdigit(ch)) i = ch - '0'; + else if (isupper(ch)) i = ch - ('A' - 10); + else i = ch - ('a' - 10); + i <<= 4; + + ch = x[1]; + if (isdigit(ch)) i += ch - '0'; + else if (isupper(ch)) i += ch - ('A' - 10); + else i += ch - ('a' - 10); + return i; +} + +void +proxy_c2hex(int ch, char *x) +{ + int i; + + x[0] = '%'; + i = (ch & 0xF0) >> 4; + if (i >= 10) x[1] = ('A' - 10) + i; + else x[1] = '0' + i; + + i = ch & 0x0F; + if (i >= 10) x[2] = ('A' - 10) + i; + else x[2] = '0' + i; +} + +/* + * canonicalise a URL-encoded string + */ + +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +char * +proxy_canonenc(pool *p, const char *x, int len, enum enctype t, int isenc) +{ + int i, j, ispath, ch; + char *y; + const char *allowed; /* characters which should not be encoded */ + const char *reserved; /* characters which much not be en/de-coded */ + +/* N.B. in addition to :@&=, this allows ';' in an http path + * and '?' in an ftp path -- this may be revised + * + * Also, it makes a '+' character in a search string reserved, as + * it may be form-encoded. (Although RFC 1738 doesn't allow this - + * it only permits ; / ? : @ = & as reserved chars.) + */ + if (t == enc_path) allowed = "$-_.+!*'(),;:@&="; + else if (t == enc_search) allowed = "$-_.!*'(),;:@&="; + else if (t == enc_user) allowed = "$-_.+!*'(),;@&="; + else if (t == enc_fpath) allowed = "$-_.+!*'(),?:@&="; + else /* if (t == enc_parm) */ allowed = "$-_.+!*'(),?/:@&="; + + if (t == enc_path) reserved = "/"; + else if (t == enc_search) reserved = "+"; + else reserved = ""; + + y = palloc(p, 3*len+1); + ispath = (t == enc_path); + + for (i=0, j=0; i < len; i++, j++) + { +/* always handle '/' first */ + ch = x[i]; + if (ind(reserved, ch) != -1) + { + y[j] = ch; + continue; + } +/* decode it if not already done */ + if (isenc && ch == '%') + { + if (!isxdigit(x[i+1]) || !isxdigit(x[i+2])) + return NULL; + ch = proxy_hex2c(&x[i+1]); + i += 2; + if (ch != 0 && ind(reserved, ch) != -1) + { /* keep it encoded */ + proxy_c2hex(ch, &y[j]); + j += 2; + continue; + } + } +/* recode it, if necessary */ + if (!isalnum(ch) && ind(allowed, ch) == -1) + { + proxy_c2hex(ch, &y[j]); + j += 2; + } else y[j] = ch; + } + y[j] = '\0'; + return y; +} + +/* + * Parses network-location. + * urlp on input the URL; on output the path, after the leading / + * user NULL if no user/password permitted + * password holder for password + * host holder for host + * port port number; only set if one is supplied. + * + * Returns an error string. + */ +char * +proxy_canon_netloc(pool *pool, char **const urlp, char **userp, + char **passwordp, char **hostp, int *port) +{ + int i; + char *p, *host, *url=*urlp; + + if (url[0] != '/' || url[1] != '/') return "Malformed URL"; + host = url + 2; + url = strchr(host, '/'); + if (url == NULL) + url = ""; + else + *(url++) = '\0'; /* skip seperating '/' */ + + if (userp != NULL) + { + char *user=NULL, *password = NULL; + p = strchr(host, '@'); + + if (p != NULL) + { + *p = '\0'; + user = host; + host = p + 1; + +/* find password */ + p = strchr(user, ':'); + if (p != NULL) + { + *p = '\0'; + password = proxy_canonenc(pool, p+1, strlen(p+1), enc_user, 1); + if (password == NULL) + return "Bad %-escape in URL (password)"; + } + + user = proxy_canonenc(pool, user, strlen(user), enc_user, 1); + if (user == NULL) return "Bad %-escape in URL (username)"; + } + *userp = user; + *passwordp = password; + } + + p = strchr(host, ':'); + if (p != NULL) + { + *(p++) = '\0'; + + for (i=0; p[i] != '\0'; i++) + if (!isdigit(p[i])) break; + + if (i == 0 || p[i] != '\0') + return "Bad port number in URL"; + *port = atoi(p); + if (*port > 65535) return "Port number in URL > 65535"; + } + str_tolower(host); /* DNS names are case-insensitive */ + if (*host == '\0') return "Missing host in URL"; +/* check hostname syntax */ + for (i=0; host[i] != '\0'; i++) + if (!isdigit(host[i]) && host[i] != '.') + break; + /* must be an IP address */ + if (host[i] == '\0' && (inet_addr(host) == -1 || inet_network(host) == -1)) + return "Bad IP address in URL"; + + *urlp = url; + *hostp = host; + + return NULL; +} + +static const char *lwday[7]= +{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +static const char *wday[7]= +{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +static const char *months[12]= +{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "Dec"}; + +/* + * If the date is a valid RFC 850 date or asctime() date, then it + * is converted to the RFC 1123 format, otherwise it is not modified. + * This routine is not very fast at doing conversions, as it uses + * sscanf and sprintf. However, if the date is already correctly + * formatted, then it exits very quickly. + */ +char * +proxy_date_canon(pool *p, char *x) +{ + int wk, mday, year, hour, min, sec, mon; + char *q, month[4], zone[4], week[4]; + + q = strchr(x, ','); + /* check for RFC 850 date */ + if (q != NULL && q - x > 3 && q[1] == ' ') + { + *q = '\0'; + for (wk=0; wk < 7; wk++) + if (strcmp(x, lwday[wk]) == 0) break; + *q = ','; + if (wk == 7) return x; /* not a valid date */ + if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || + q[17] != ':' || strcmp(&q[20], " GMT") != 0) return x; + if (sscanf(q+2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, + &hour, &min, &sec, zone) != 7) return x; + if (year < 70) year += 2000; + else year += 1900; + } else + { +/* check for acstime() date */ + if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || + x[16] != ':' || x[19] != ' ' || x[24] != '\0') return x; + if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, + &min, &sec, &year) != 7) return x; + for (wk=0; wk < 7; wk++) + if (strcmp(week, wday[wk]) == 0) break; + if (wk == 7) return x; + } + +/* check date */ + for (mon=0; mon < 12; mon++) if (strcmp(month, months[mon]) == 0) break; + if (mon == 12) return x; + + if (strlen(x)+1 < 30) { + x = palloc(p, 30); + } + /* format: "Wed, 17 Dec 1997 00:53:40 GMT" (29 chars data) */ + ap_snprintf(x, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", wday[wk], mday, + months[mon], year, hour, min, sec); + return x; +} + +/* + * Reads headers from a buffer and returns an array of headers. + * Returns NULL on file error + */ +array_header * +proxy_read_headers(pool *pool, char *buffer, int size, BUFF *f) +{ + int gotcr, len, i, j; + array_header *resp_hdrs; + struct hdr_entry *hdr; + char *p; + + resp_hdrs = make_array(pool, 10, sizeof(struct hdr_entry)); + hdr = NULL; + + gotcr = 1; + for (;;) + { + len = bgets(buffer, size, f); + if (len == -1) return NULL; + if (len == 0) break; + if (buffer[len-1] == '\n') + { + buffer[--len] = '\0'; + i = 1; + } else + i = 0; + + if (!gotcr || buffer[0] == ' ' || buffer[0] == '\t') + { + /* a continuation header */ + if (hdr == NULL) + { + /* error!! */ + if (!i) + { + i = bskiplf(f); + if (i == -1) return NULL; + } + gotcr = 1; + continue; + } + hdr->value = pstrcat(pool, hdr->value, buffer, NULL); + } + else if (gotcr && len == 0) break; + else + { + p = strchr(buffer, ':'); + if (p == NULL) + { + /* error!! */ + if (!gotcr) + { + i = bskiplf(f); + if (i == -1) return NULL; + } + gotcr = 1; + hdr = NULL; + continue; + } + hdr = push_array(resp_hdrs); + *(p++) = '\0'; + hdr->field = pstrdup(pool, buffer); + while (*p == ' ' || *p == '\t') p++; + hdr->value = pstrdup(pool, p); + gotcr = i; + } + } + + hdr = (struct hdr_entry *)resp_hdrs->elts; + for (i=0; i < resp_hdrs->nelts; i++) + { + p = hdr[i].value; + j = strlen(p); + while (j > 0 && (p[j-1] == ' ' || p[j-1] == '\t')) j--; + p[j] = '\0'; + } + + return resp_hdrs; +} + +long int +proxy_send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c) +{ + char buf[IOBUFSIZE]; + long total_bytes_sent; + register int n,o,w; + conn_rec *con = r->connection; + + total_bytes_sent = 0; + + /* Since we are reading from one buffer and writing to another, + * it is unsafe to do a soft_timeout here, at least until the proxy + * has its own timeout handler which can set both buffers to EOUT. + */ + hard_timeout("proxy send body", r); + + while (!con->aborted && f != NULL) { + n = bread(f, buf, IOBUFSIZE); + if (n == -1) /* input error */ + { + if (f2 != NULL) f2 = proxy_cache_error(c); + break; + } + if (n == 0) break; /* EOF */ + o=0; + total_bytes_sent += n; + + if (f2 != NULL) + if (bwrite(f2, buf, n) != n) f2 = proxy_cache_error(c); + + while(n && !con->aborted) { + w = bwrite(con->client, &buf[o], n); + if (w <= 0) { + if (f2 != NULL) { + pclosef(c->req->pool, c->fp->fd); + c->fp = NULL; + unlink(c->tempfile); + } + break; + } + reset_timeout(r); /* reset timeout after successful write */ + n-=w; + o+=w; + } + } + if (!con->aborted) + bflush(con->client); + + kill_timeout(r); + return total_bytes_sent; +} + +/* + * Read a header from the array, returning the first entry + */ +struct hdr_entry * +proxy_get_header(array_header *hdrs_arr, const char *name) +{ + struct hdr_entry *hdrs; + int i; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; i++) + if (hdrs[i].field != NULL && strcasecmp(name, hdrs[i].field) == 0) + return &hdrs[i]; + + return NULL; +} + +/* + * Add to the header reply, either concatenating, or replacing existin + * headers. It stores the pointers provided, so make sure the data + * is not subsequently overwritten + */ +struct hdr_entry * +proxy_add_header(array_header *hdrs_arr, char *field, char *value, + int rep) +{ + int i; + struct hdr_entry *hdrs; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + if (rep) + for (i = 0; i < hdrs_arr->nelts; i++) + if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) + { + hdrs[i].value = value; + return hdrs; + } + + hdrs = push_array(hdrs_arr); + hdrs->field = field; + hdrs->value = value; + + return hdrs; +} + +void +proxy_del_header(array_header *hdrs_arr, const char *field) +{ + int i; + struct hdr_entry *hdrs; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + + for (i = 0; i < hdrs_arr->nelts; i++) + if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) + hdrs[i].value = NULL; +} + +/* + * Sends response line and headers + * A timeout should be set before calling this routine. + */ +void +proxy_send_headers(BUFF *fp, const char *respline, array_header *hdrs_arr) +{ + struct hdr_entry *hdrs; + int i; + + hdrs = (struct hdr_entry *)hdrs_arr->elts; + + bputs(respline, fp); + bputs("\015\012", fp); + for (i = 0; i < hdrs_arr->nelts; i++) + { + if (hdrs[i].field == NULL) continue; + bvputs(fp, hdrs[i].field, ": ", hdrs[i].value, "\015\012", NULL); + } + + bputs("\015\012", fp); +} + + +/* + * list is a comma-separated list of case-insensitive tokens, with + * optional whitespace around the tokens. + * The return returns 1 if the token val is found in the list, or 0 + * otherwise. + */ +int +proxy_liststr(const char *list, const char *val) +{ + int len, i; + const char *p; + + len = strlen(val); + + while (list != NULL) + { + p = strchr(list, ','); + if (p != NULL) + { + i = p - list; + do p++; while (isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && isspace(list[i-1])) i--; + if (i == len && strncasecmp(list, val, len) == 0) return 1; + list = p; + } + return 0; +} + +void +proxy_hash(const char *it, char *val,int ndepth,int nlength) +{ + MD5_CTX context; + unsigned char digest[16]; + char tmp[22]; + int i, k, d; + unsigned int x; + static const char table[64]= +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; + + MD5Init(&context); + MD5Update(&context, (const unsigned char *)it, strlen(it)); + MD5Final(digest, &context); + +/* encode 128 bits as 22 characters, using a modified uuencoding */ +/* the encoding is 3 bytes -> 4 characters + * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters + */ + for (i=0, k=0; i < 15; i += 3) + { + x = (digest[i] << 16) | (digest[i+1] << 8) | digest[i+2]; + tmp[k++] = table[x >> 18]; + tmp[k++] = table[(x >> 12) & 0x3f]; + tmp[k++] = table[(x >> 6) & 0x3f]; + tmp[k++] = table[x & 0x3f]; + } +/* one byte left */ + x = digest[15]; + tmp[k++] = table[x >> 2]; /* use up 6 bits */ + tmp[k++] = table[(x << 4) & 0x3f]; + /* now split into directory levels */ + + for(i=k=d=0 ; d < ndepth ; ++d) + { + strncpy(&val[i],&tmp[k],nlength); + k+=nlength; + val[i+nlength]='/'; + i+=nlength+1; + } + memcpy(&val[i],&tmp[k],22-k); + val[i+22-k]='\0'; +} + +/* + * Converts 8 hex digits to a time integer + */ +int +proxy_hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i=0, j=0; i < 8; i++) + { + ch = x[i]; + j <<= 4; + if (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; +} + +/* + * Converts a time integer to 8 hex digits + */ +void +proxy_sec2hex(int t, char *y) +{ + int i, ch; + unsigned int j=t; + + for (i=7; i >= 0; i--) + { + ch = j & 0xF; + j >>= 4; + if (ch >= 10) y[i] = ch + ('A' - 10); + else y[i] = ch + '0'; + } + y[8] = '\0'; +} + +void +proxy_log_uerror(const char *routine, const char *file, const char *err, + server_rec *s) +{ + char *p, *q; + + q = get_time(); + p = strerror(errno); + + if (err != NULL) + { + fprintf(s->error_log, "[%s] %s\n", q, err); + if (file != NULL) + fprintf(s->error_log, "- %s: %s: %s\n", routine, file, p); + else + fprintf(s->error_log, "- %s: %s\n", routine, p); + } else + { + if (file != NULL) + fprintf(s->error_log, "[%s] %s: %s: %s\n", q, routine, file, p); + else + fprintf(s->error_log, "[%s] %s: %s\n", q, routine, p); + } + + fflush(s->error_log); +} + +BUFF * +proxy_cache_error(struct cache_req *c) +{ + proxy_log_uerror("write", c->tempfile, "proxy: error writing to cache file", + c->req->server); + pclosef(c->req->pool, c->fp->fd); + c->fp = NULL; + unlink(c->tempfile); + return NULL; +} + +int +proxyerror(request_rec *r, const char *message) +{ + r->status = SERVER_ERROR; + r->status_line = "500 Proxy Error"; + r->content_type = "text/html"; + + send_http_header(r); + soft_timeout("proxy error", r); + + rvputs(r, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\015\012\ +<html><head><title>Proxy Error</title><head>\015\012<body><h1>Proxy Error\ +</h1>\015\012The proxy server could not handle this request.\ +\015\012<p>\015\012Reason: <b>", message, "</b>\015\012</body><html>\015\012", + NULL); + + kill_timeout(r); + return OK; +} + +/* + * This routine returns its own error message + */ +const char * +proxy_host2addr(const char *host, struct hostent *reqhp) +{ + int i; + struct hostent *hp; + static struct hostent hpbuf; + static u_long ipaddr; + static char* charpbuf[2]; + + for (i=0; host[i] != '\0'; i++) + if (!isdigit(host[i]) && host[i] != '.') + break; + + if (host[i] != '\0') + { + hp = gethostbyname(host); + if (hp == NULL) + return "Host not found"; + } else + { + ipaddr = inet_addr(host); + hp = gethostbyaddr((char *)&ipaddr, sizeof(u_long), AF_INET); + if (hp == NULL) { + memset(&hpbuf, 0, sizeof(hpbuf)); + hpbuf.h_name = 0; + hpbuf.h_addrtype = AF_INET; + hpbuf.h_length = sizeof(u_long); + hpbuf.h_addr_list = charpbuf; + hpbuf.h_addr_list[0] = (char*)&ipaddr; + hpbuf.h_addr_list[1] = 0; + hp = &hpbuf; + } + } + memcpy(reqhp, hp, sizeof(struct hostent)); + return NULL; +} + +int +proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r) +{ + int i; + + hard_timeout("proxy connect", r); + do i = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + while (i == -1 && errno == EINTR); + if (i == -1) proxy_log_uerror("connect", NULL, NULL, r->server); + kill_timeout(r); + + return i; +} + diff --git a/usr.sbin/httpd/src/regex/COPYRIGHT b/usr.sbin/httpd/src/regex/COPYRIGHT new file mode 100644 index 00000000000..d43362fbfc9 --- /dev/null +++ b/usr.sbin/httpd/src/regex/COPYRIGHT @@ -0,0 +1,20 @@ +Copyright 1992, 1993, 1994 Henry Spencer. All rights reserved. +This software is not subject to any license of the American Telephone +and Telegraph Company or of the Regents of the University of California. + +Permission is granted to anyone to use this software for any purpose on +any computer system, and to alter it and redistribute it, subject +to the following restrictions: + +1. The author is not responsible for the consequences of use of this + software, no matter how awful, even if they arise from flaws in it. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits must appear in the documentation. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits must appear in the documentation. + +4. This notice may not be removed or altered. diff --git a/usr.sbin/httpd/src/regex/Makefile b/usr.sbin/httpd/src/regex/Makefile new file mode 100644 index 00000000000..e4ea0154bea --- /dev/null +++ b/usr.sbin/httpd/src/regex/Makefile @@ -0,0 +1,133 @@ +SHELL = /bin/sh + +# You probably want to take -DREDEBUG out of CFLAGS, and put something like +# -O in, *after* testing (-DREDEBUG strengthens testing by enabling a lot of +# internal assertion checking and some debugging facilities). +# Put -Dconst= in for a pre-ANSI compiler. +# Do not take -DPOSIX_MISTAKE out. +# REGCFLAGS isn't important to you (it's for my use in some special contexts). +CFLAGS=-I. -DPOSIX_MISTAKE $(AUX_CFLAGS) + +# If you have a pre-ANSI compiler, put -o into MKHFLAGS. If you want +# the Berkeley __P macro, put -b in. +MKHFLAGS= + +# Flags for linking but not compiling, if any. +LDFLAGS= + +# Extra libraries for linking, if any. +LIBS= + +# Internal stuff, should not need changing. +OBJPRODN=regcomp.o regexec.o regerror.o regfree.o +OBJS=$(OBJPRODN) split.o debug.o main.o +H=cclass.h cname.h regex2.h utils.h +REGSRC=regcomp.c regerror.c regexec.c regfree.c +ALLSRC=$(REGSRC) engine.c debug.c main.c split.c + +# Stuff that matters only if you're trying to lint the package. +LINTFLAGS=-I. -Dstatic= -Dconst= -DREDEBUG +LINTC=regcomp.c regexec.c regerror.c regfree.c debug.c main.c +JUNKLINT=possible pointer alignment|null effect + +# arrangements to build forward-reference header files +.SUFFIXES: .ih .h +.c.ih: + sh ./mkh $(MKHFLAGS) -p $< >$@ + +default: r + +lib: purge $(OBJPRODN) + rm -f libregex.a + ar crv libregex.a $(OBJPRODN) + $(RANLIB) libregex.a + +purge: + rm -f *.o + +# stuff to build regex.h +REGEXH=regex.h +REGEXHSRC=regex2.h $(REGSRC) +$(REGEXH): $(REGEXHSRC) mkh + sh ./mkh $(MKHFLAGS) -i _REGEX_H_ $(REGEXHSRC) >regex.tmp + cmp -s regex.tmp regex.h 2>/dev/null || cp regex.tmp regex.h + rm -f regex.tmp + +# dependencies +$(OBJPRODN) debug.o: utils.h regex.h regex2.h +regcomp.o: cclass.h cname.h regcomp.ih +regexec.o: engine.c engine.ih +regerror.o: regerror.ih +debug.o: debug.ih +main.o: main.ih + +# tester +re: $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + +# regression test +r: re tests + ./re <tests + ./re -el <tests + ./re -er <tests + +# 57 variants, and other stuff, for development use -- not useful to you +ra: ./re tests + -./re <tests + -./re -el <tests + -./re -er <tests + +rx: ./re tests + ./re -x <tests + ./re -x -el <tests + ./re -x -er <tests + +t: ./re tests + -time ./re <tests + -time ./re -cs <tests + -time ./re -el <tests + -time ./re -cs -el <tests + +l: $(LINTC) + lint $(LINTFLAGS) -h $(LINTC) 2>&1 | egrep -v '$(JUNKLINT)' | tee lint + +fullprint: + ti README WHATSNEW notes todo | list + ti *.h | list + list *.c + list regex.3 regex.7 + +print: + ti README WHATSNEW notes todo | list + ti *.h | list + list reg*.c engine.c + + +mf.tmp: Makefile + sed '/^REGEXH=/s/=.*/=regex.h/' Makefile | sed '/#DEL$$/d' >$@ + +DTRH=cclass.h cname.h regex2.h utils.h +PRE=COPYRIGHT README WHATSNEW +POST=mkh regex.3 regex.7 tests $(DTRH) $(ALLSRC) fake/*.[ch] +FILES=$(PRE) Makefile $(POST) +DTR=$(PRE) Makefile=mf.tmp $(POST) +dtr: $(FILES) mf.tmp + makedtr $(DTR) >$@ + rm mf.tmp + +cio: $(FILES) + cio $(FILES) + +rdf: $(FILES) + rcsdiff -c $(FILES) 2>&1 | p + +# various forms of cleanup +tidy: + rm -f junk* core core.* *.core dtr *.tmp lint + +clean: tidy + rm -f *.o *.s *.ih re libregex.a + +# don't do this one unless you know what you're doing +spotless: clean + rm -f mkh regex.h diff --git a/usr.sbin/httpd/src/regex/README b/usr.sbin/httpd/src/regex/README new file mode 100644 index 00000000000..cea9b67b666 --- /dev/null +++ b/usr.sbin/httpd/src/regex/README @@ -0,0 +1,32 @@ +alpha3.4 release. +Thu Mar 17 23:17:18 EST 1994 +henry@zoo.toronto.edu + +See WHATSNEW for change listing. + +installation notes: +-------- +Read the comments at the beginning of Makefile before running. + +Utils.h contains some things that just might have to be modified on +some systems, as well as a nested include (ugh) of <assert.h>. + +The "fake" directory contains quick-and-dirty fakes for some header +files and routines that old systems may not have. Note also that +-DUSEBCOPY will make utils.h substitute bcopy() for memmove(). + +After that, "make r" will build regcomp.o, regexec.o, regfree.o, +and regerror.o (the actual routines), bundle them together into a test +program, and run regression tests on them. No output is good output. + +"make lib" builds just the .o files for the actual routines (when +you're happy with testing and have adjusted CFLAGS for production), +and puts them together into libregex.a. You can pick up either the +library or *.o ("make lib" makes sure there are no other .o files left +around to confuse things). + +Main.c, debug.c, split.c are used for regression testing but are not part +of the RE routines themselves. + +Regex.h goes in /usr/include. All other .h files are internal only. +-------- diff --git a/usr.sbin/httpd/src/regex/WHATSNEW b/usr.sbin/httpd/src/regex/WHATSNEW new file mode 100644 index 00000000000..6e82e1dae0c --- /dev/null +++ b/usr.sbin/httpd/src/regex/WHATSNEW @@ -0,0 +1,92 @@ +New in alpha3.4: The complex bug alluded to below has been fixed (in a +slightly kludgey temporary way that may hurt efficiency a bit; this is +another "get it out the door for 4.4" release). The tests at the end of +the tests file have accordingly been uncommented. The primary sign of +the bug was that something like a?b matching ab matched b rather than ab. +(The bug was essentially specific to this exact situation, else it would +have shown up earlier.) + +New in alpha3.3: The definition of word boundaries has been altered +slightly, to more closely match the usual programming notion that "_" +is an alphabetic. Stuff used for pre-ANSI systems is now in a subdir, +and the makefile no longer alludes to it in mysterious ways. The +makefile has generally been cleaned up some. Fixes have been made +(again!) so that the regression test will run without -DREDEBUG, at +the cost of weaker checking. A workaround for a bug in some folks' +<assert.h> has been added. And some more things have been added to +tests, including a couple right at the end which are commented out +because the code currently flunks them (complex bug; fix coming). +Plus the usual minor cleanup. + +New in alpha3.2: Assorted bits of cleanup and portability improvement +(the development base is now a BSDI system using GCC instead of an ancient +Sun system, and the newer compiler exposed some glitches). Fix for a +serious bug that affected REs using many [] (including REG_ICASE REs +because of the way they are implemented), *sometimes*, depending on +memory-allocation patterns. The header-file prototypes no longer name +the parameters, avoiding possible name conflicts. The possibility that +some clot has defined CHAR_MIN as (say) `-128' instead of `(-128)' is +now handled gracefully. "uchar" is no longer used as an internal type +name (too many people have the same idea). Still the same old lousy +performance, alas. + +New in alpha3.1: Basically nothing, this release is just a bookkeeping +convenience. Stay tuned. + +New in alpha3.0: Performance is no better, alas, but some fixes have been +made and some functionality has been added. (This is basically the "get +it out the door in time for 4.4" release.) One bug fix: regfree() didn't +free the main internal structure (how embarrassing). It is now possible +to put NULs in either the RE or the target string, using (resp.) a new +REG_PEND flag and the old REG_STARTEND flag. The REG_NOSPEC flag to +regcomp() makes all characters ordinary, so you can match a literal +string easily (this will become more useful when performance improves!). +There are now primitives to match beginnings and ends of words, although +the syntax is disgusting and so is the implementation. The REG_ATOI +debugging interface has changed a bit. And there has been considerable +internal cleanup of various kinds. + +New in alpha2.3: Split change list out of README, and moved flags notes +into Makefile. Macro-ized the name of regex(7) in regex(3), since it has +to change for 4.4BSD. Cleanup work in engine.c, and some new regression +tests to catch tricky cases thereof. + +New in alpha2.2: Out-of-date manpages updated. Regerror() acquires two +small extensions -- REG_ITOA and REG_ATOI -- which avoid debugging kludges +in my own test program and might be useful to others for similar purposes. +The regression test will now compile (and run) without REDEBUG. The +BRE \$ bug is fixed. Most uses of "uchar" are gone; it's all chars now. +Char/uchar parameters are now written int/unsigned, to avoid possible +portability problems with unpromoted parameters. Some unsigned casts have +been introduced to minimize portability problems with shifting into sign +bits. + +New in alpha2.1: Lots of little stuff, cleanup and fixes. The one big +thing is that regex.h is now generated, using mkh, rather than being +supplied in the distribution; due to circularities in dependencies, +you have to build regex.h explicitly by "make h". The two known bugs +have been fixed (and the regression test now checks for them), as has a +problem with assertions not being suppressed in the absence of REDEBUG. +No performance work yet. + +New in alpha2: Backslash-anything is an ordinary character, not an +error (except, of course, for the handful of backslashed metacharacters +in BREs), which should reduce script breakage. The regression test +checks *where* null strings are supposed to match, and has generally +been tightened up somewhat. Small bug fixes in parameter passing (not +harmful, but technically errors) and some other areas. Debugging +invoked by defining REDEBUG rather than not defining NDEBUG. + +New in alpha+3: full prototyping for internal routines, using a little +helper program, mkh, which extracts prototypes given in stylized comments. +More minor cleanup. Buglet fix: it's CHAR_BIT, not CHAR_BITS. Simple +pre-screening of input when a literal string is known to be part of the +RE; this does wonders for performance. + +New in alpha+2: minor bits of cleanup. Notably, the number "32" for the +word width isn't hardwired into regexec.c any more, the public header +file prototypes the functions if __STDC__ is defined, and some small typos +in the manpages have been fixed. + +New in alpha+1: improvements to the manual pages, and an important +extension, the REG_STARTEND option to regexec(). diff --git a/usr.sbin/httpd/src/regex/cclass.h b/usr.sbin/httpd/src/regex/cclass.h new file mode 100644 index 00000000000..727cbb92552 --- /dev/null +++ b/usr.sbin/httpd/src/regex/cclass.h @@ -0,0 +1,31 @@ +/* character-class table */ +static struct cclass { + char *name; + char *chars; + char *multis; +} cclasses[] = { + { "alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789", "" }, + { "alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "" }, + { "blank", " \t", "" }, + { "cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\ +\25\26\27\30\31\32\33\34\35\36\37\177", "" }, + { "digit", "0123456789", "" }, + { "graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "" }, + { "lower", "abcdefghijklmnopqrstuvwxyz", + "" }, + { "print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ", + "" }, + { "punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "" }, + { "space", "\t\n\v\f\r ", "" }, + { "upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "" }, + { "xdigit", "0123456789ABCDEFabcdef", + "" }, + { NULL, 0, "" } +}; diff --git a/usr.sbin/httpd/src/regex/cname.h b/usr.sbin/httpd/src/regex/cname.h new file mode 100644 index 00000000000..ff116e55e23 --- /dev/null +++ b/usr.sbin/httpd/src/regex/cname.h @@ -0,0 +1,102 @@ +/* character-name table */ +static struct cname { + char *name; + char code; +} cnames[] = { + { "NUL", '\0' }, + { "SOH", '\001' }, + { "STX", '\002' }, + { "ETX", '\003' }, + { "EOT", '\004' }, + { "ENQ", '\005' }, + { "ACK", '\006' }, + { "BEL", '\007' }, + { "alert", '\007' }, + { "BS", '\010' }, + { "backspace", '\b' }, + { "HT", '\011' }, + { "tab", '\t' }, + { "LF", '\012' }, + { "newline", '\n' }, + { "VT", '\013' }, + { "vertical-tab", '\v' }, + { "FF", '\014' }, + { "form-feed", '\f' }, + { "CR", '\015' }, + { "carriage-return", '\r' }, + { "SO", '\016' }, + { "SI", '\017' }, + { "DLE", '\020' }, + { "DC1", '\021' }, + { "DC2", '\022' }, + { "DC3", '\023' }, + { "DC4", '\024' }, + { "NAK", '\025' }, + { "SYN", '\026' }, + { "ETB", '\027' }, + { "CAN", '\030' }, + { "EM", '\031' }, + { "SUB", '\032' }, + { "ESC", '\033' }, + { "IS4", '\034' }, + { "FS", '\034' }, + { "IS3", '\035' }, + { "GS", '\035' }, + { "IS2", '\036' }, + { "RS", '\036' }, + { "IS1", '\037' }, + { "US", '\037' }, + { "space", ' ' }, + { "exclamation-mark", '!' }, + { "quotation-mark", '"' }, + { "number-sign", '#' }, + { "dollar-sign", '$' }, + { "percent-sign", '%' }, + { "ampersand", '&' }, + { "apostrophe", '\'' }, + { "left-parenthesis", '(' }, + { "right-parenthesis", ')' }, + { "asterisk", '*' }, + { "plus-sign", '+' }, + { "comma", ',' }, + { "hyphen", '-' }, + { "hyphen-minus", '-' }, + { "period", '.' }, + { "full-stop", '.' }, + { "slash", '/' }, + { "solidus", '/' }, + { "zero", '0' }, + { "one", '1' }, + { "two", '2' }, + { "three", '3' }, + { "four", '4' }, + { "five", '5' }, + { "six", '6' }, + { "seven", '7' }, + { "eight", '8' }, + { "nine", '9' }, + { "colon", ':' }, + { "semicolon", ';' }, + { "less-than-sign", '<' }, + { "equals-sign", '=' }, + { "greater-than-sign", '>' }, + { "question-mark", '?' }, + { "commercial-at", '@' }, + { "left-square-bracket", '[' }, + { "backslash", '\\' }, + { "reverse-solidus", '\\' }, + { "right-square-bracket", ']' }, + { "circumflex", '^' }, + { "circumflex-accent", '^' }, + { "underscore", '_' }, + { "low-line", '_' }, + { "grave-accent", '`' }, + { "left-brace", '{' }, + { "left-curly-bracket", '{' }, + { "vertical-line", '|' }, + { "right-brace", '}' }, + { "right-curly-bracket", '}' }, + { "tilde", '~' }, + { "DEL", '\177' }, + { NULL, 0 } +}; diff --git a/usr.sbin/httpd/src/regex/debug.c b/usr.sbin/httpd/src/regex/debug.c new file mode 100644 index 00000000000..c0feaeb1694 --- /dev/null +++ b/usr.sbin/httpd/src/regex/debug.c @@ -0,0 +1,242 @@ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/types.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" +#include "debug.ih" + +/* + - regprint - print a regexp for debugging + == void regprint(regex_t *r, FILE *d); + */ +void +regprint(r, d) +regex_t *r; +FILE *d; +{ + register struct re_guts *g = r->re_g; + register int i; + register int c; + register int last; + int nincat[NC]; + + fprintf(d, "%ld states, %d categories", (long)g->nstates, + g->ncategories); + fprintf(d, ", first %ld last %ld", (long)g->firststate, + (long)g->laststate); + if (g->iflags&USEBOL) + fprintf(d, ", USEBOL"); + if (g->iflags&USEEOL) + fprintf(d, ", USEEOL"); + if (g->iflags&BAD) + fprintf(d, ", BAD"); + if (g->nsub > 0) + fprintf(d, ", nsub=%ld", (long)g->nsub); + if (g->must != NULL) + fprintf(d, ", must(%ld) `%*s'", (long)g->mlen, (int)g->mlen, + g->must); + if (g->backrefs) + fprintf(d, ", backrefs"); + if (g->nplus > 0) + fprintf(d, ", nplus %ld", (long)g->nplus); + fprintf(d, "\n"); + s_print(g, d); + for (i = 0; i < g->ncategories; i++) { + nincat[i] = 0; + for (c = CHAR_MIN; c <= CHAR_MAX; c++) + if (g->categories[c] == i) + nincat[i]++; + } + fprintf(d, "cc0#%d", nincat[0]); + for (i = 1; i < g->ncategories; i++) + if (nincat[i] == 1) { + for (c = CHAR_MIN; c <= CHAR_MAX; c++) + if (g->categories[c] == i) + break; + fprintf(d, ", %d=%s", i, regchar(c)); + } + fprintf(d, "\n"); + for (i = 1; i < g->ncategories; i++) + if (nincat[i] != 1) { + fprintf(d, "cc%d\t", i); + last = -1; + for (c = CHAR_MIN; c <= CHAR_MAX+1; c++) /* +1 does flush */ + if (c <= CHAR_MAX && g->categories[c] == i) { + if (last < 0) { + fprintf(d, "%s", regchar(c)); + last = c; + } + } else { + if (last >= 0) { + if (last != c-1) + fprintf(d, "-%s", + regchar(c-1)); + last = -1; + } + } + fprintf(d, "\n"); + } +} + +/* + - s_print - print the strip for debugging + == static void s_print(register struct re_guts *g, FILE *d); + */ +static void +s_print(g, d) +register struct re_guts *g; +FILE *d; +{ + register sop *s; + register cset *cs; + register int i; + register int done = 0; + register sop opnd; + register int col = 0; + register int last; + register sopno offset = 2; +# define GAP() { if (offset % 5 == 0) { \ + if (col > 40) { \ + fprintf(d, "\n\t"); \ + col = 0; \ + } else { \ + fprintf(d, " "); \ + col++; \ + } \ + } else \ + col++; \ + offset++; \ + } + + if (OP(g->strip[0]) != OEND) + fprintf(d, "missing initial OEND!\n"); + for (s = &g->strip[1]; !done; s++) { + opnd = OPND(*s); + switch (OP(*s)) { + case OEND: + fprintf(d, "\n"); + done = 1; + break; + case OCHAR: + if (strchr("\\|()^$.[+*?{}!<> ", (char)opnd) != NULL) + fprintf(d, "\\%c", (char)opnd); + else + fprintf(d, "%s", regchar((char)opnd)); + break; + case OBOL: + fprintf(d, "^"); + break; + case OEOL: + fprintf(d, "$"); + break; + case OBOW: + fprintf(d, "\\{"); + break; + case OEOW: + fprintf(d, "\\}"); + break; + case OANY: + fprintf(d, "."); + break; + case OANYOF: + fprintf(d, "[(%ld)", (long)opnd); + cs = &g->sets[opnd]; + last = -1; + for (i = 0; i < g->csetsize+1; i++) /* +1 flushes */ + if (CHIN(cs, i) && i < g->csetsize) { + if (last < 0) { + fprintf(d, "%s", regchar(i)); + last = i; + } + } else { + if (last >= 0) { + if (last != i-1) + fprintf(d, "-%s", + regchar(i-1)); + last = -1; + } + } + fprintf(d, "]"); + break; + case OBACK_: + fprintf(d, "(\\<%ld>", (long)opnd); + break; + case O_BACK: + fprintf(d, "<%ld>\\)", (long)opnd); + break; + case OPLUS_: + fprintf(d, "(+"); + if (OP(*(s+opnd)) != O_PLUS) + fprintf(d, "<%ld>", (long)opnd); + break; + case O_PLUS: + if (OP(*(s-opnd)) != OPLUS_) + fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "+)"); + break; + case OQUEST_: + fprintf(d, "(?"); + if (OP(*(s+opnd)) != O_QUEST) + fprintf(d, "<%ld>", (long)opnd); + break; + case O_QUEST: + if (OP(*(s-opnd)) != OQUEST_) + fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "?)"); + break; + case OLPAREN: + fprintf(d, "((<%ld>", (long)opnd); + break; + case ORPAREN: + fprintf(d, "<%ld>))", (long)opnd); + break; + case OCH_: + fprintf(d, "<"); + if (OP(*(s+opnd)) != OOR2) + fprintf(d, "<%ld>", (long)opnd); + break; + case OOR1: + if (OP(*(s-opnd)) != OOR1 && OP(*(s-opnd)) != OCH_) + fprintf(d, "<%ld>", (long)opnd); + fprintf(d, "|"); + break; + case OOR2: + fprintf(d, "|"); + if (OP(*(s+opnd)) != OOR2 && OP(*(s+opnd)) != O_CH) + fprintf(d, "<%ld>", (long)opnd); + break; + case O_CH: + if (OP(*(s-opnd)) != OOR1) + fprintf(d, "<%ld>", (long)opnd); + fprintf(d, ">"); + break; + default: + fprintf(d, "!%ld(%ld)!", OP(*s), opnd); + break; + } + if (!done) + GAP(); + } +} + +/* + - regchar - make a character printable + == static char *regchar(int ch); + */ +static char * /* -> representation */ +regchar(ch) +int ch; +{ + static char buf[10]; + + if (isprint(ch) || ch == ' ') + sprintf(buf, "%c", ch); + else + sprintf(buf, "\\%o", ch); + return(buf); +} diff --git a/usr.sbin/httpd/src/regex/engine.c b/usr.sbin/httpd/src/regex/engine.c new file mode 100644 index 00000000000..f3b36b3e01b --- /dev/null +++ b/usr.sbin/httpd/src/regex/engine.c @@ -0,0 +1,1019 @@ +/* + * The matching engine and friends. This file is #included by regexec.c + * after suitable #defines of a variety of macros used herein, so that + * different state representations can be used without duplicating masses + * of code. + */ + +#ifdef SNAMES +#define matcher smatcher +#define fast sfast +#define slow sslow +#define dissect sdissect +#define backref sbackref +#define step sstep +#define print sprint +#define at sat +#define match smat +#endif +#ifdef LNAMES +#define matcher lmatcher +#define fast lfast +#define slow lslow +#define dissect ldissect +#define backref lbackref +#define step lstep +#define print lprint +#define at lat +#define match lmat +#endif + +/* another structure passed up and down to avoid zillions of parameters */ +struct match { + struct re_guts *g; + int eflags; + regmatch_t *pmatch; /* [nsub+1] (0 element unused) */ + char *offp; /* offsets work from here */ + char *beginp; /* start of string -- virtual NUL precedes */ + char *endp; /* end of string -- virtual NUL here */ + char *coldp; /* can be no match starting before here */ + char **lastpos; /* [nplus+1] */ + STATEVARS; + states st; /* current states */ + states fresh; /* states for a fresh start */ + states tmp; /* temporary */ + states empty; /* empty set of states */ +}; + +#include "engine.ih" + +#ifdef REDEBUG +#define SP(t, s, c) print(m, t, s, c, stdout) +#define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2) +#define NOTE(str) { if (m->eflags®_TRACE) printf("=%s\n", (str)); } +#else +#define SP(t, s, c) /* nothing */ +#define AT(t, p1, p2, s1, s2) /* nothing */ +#define NOTE(s) /* nothing */ +#endif + +/* + - matcher - the actual matching engine + == static int matcher(register struct re_guts *g, char *string, \ + == size_t nmatch, regmatch_t pmatch[], int eflags); + */ +static int /* 0 success, REG_NOMATCH failure */ +matcher(g, string, nmatch, pmatch, eflags) +register struct re_guts *g; +char *string; +size_t nmatch; +regmatch_t pmatch[]; +int eflags; +{ + register char *endp; + register int i; + struct match mv; + register struct match *m = &mv; + register char *dp; + register const sopno gf = g->firststate+1; /* +1 for OEND */ + register const sopno gl = g->laststate; + char *start; + char *stop; + + /* simplify the situation where possible */ + if (g->cflags®_NOSUB) + nmatch = 0; + if (eflags®_STARTEND) { + start = string + pmatch[0].rm_so; + stop = string + pmatch[0].rm_eo; + } else { + start = string; + stop = start + strlen(start); + } + if (stop < start) + return(REG_INVARG); + + /* prescreening; this does wonders for this rather slow code */ + if (g->must != NULL) { + for (dp = start; dp < stop; dp++) + if (*dp == g->must[0] && stop - dp >= g->mlen && + memcmp(dp, g->must, (size_t)g->mlen) == 0) + break; + if (dp == stop) /* we didn't find g->must */ + return(REG_NOMATCH); + } + + /* match struct setup */ + m->g = g; + m->eflags = eflags; + m->pmatch = NULL; + m->lastpos = NULL; + m->offp = string; + m->beginp = start; + m->endp = stop; + STATESETUP(m, 4); + SETUP(m->st); + SETUP(m->fresh); + SETUP(m->tmp); + SETUP(m->empty); + CLEAR(m->empty); + + /* this loop does only one repetition except for backrefs */ + for (;;) { + endp = fast(m, start, stop, gf, gl); + if (endp == NULL) { /* a miss */ + STATETEARDOWN(m); + return(REG_NOMATCH); + } + if (nmatch == 0 && !g->backrefs) + break; /* no further info needed */ + + /* where? */ + assert(m->coldp != NULL); + for (;;) { + NOTE("finding start"); + endp = slow(m, m->coldp, stop, gf, gl); + if (endp != NULL) + break; + assert(m->coldp < m->endp); + m->coldp++; + } + if (nmatch == 1 && !g->backrefs) + break; /* no further info needed */ + + /* oh my, he wants the subexpressions... */ + if (m->pmatch == NULL) + m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) * + sizeof(regmatch_t)); + if (m->pmatch == NULL) { + STATETEARDOWN(m); + return(REG_ESPACE); + } + for (i = 1; i <= m->g->nsub; i++) + m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1; + if (!g->backrefs && !(m->eflags®_BACKR)) { + NOTE("dissecting"); + dp = dissect(m, m->coldp, endp, gf, gl); + } else { + if (g->nplus > 0 && m->lastpos == NULL) + m->lastpos = (char **)malloc((g->nplus+1) * + sizeof(char *)); + if (g->nplus > 0 && m->lastpos == NULL) { + free(m->pmatch); + STATETEARDOWN(m); + return(REG_ESPACE); + } + NOTE("backref dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); + } + if (dp != NULL) + break; + + /* uh-oh... we couldn't find a subexpression-level match */ + assert(g->backrefs); /* must be back references doing it */ + assert(g->nplus == 0 || m->lastpos != NULL); + for (;;) { + if (dp != NULL || endp <= m->coldp) + break; /* defeat */ + NOTE("backoff"); + endp = slow(m, m->coldp, endp-1, gf, gl); + if (endp == NULL) + break; /* defeat */ + /* try it on a shorter possibility */ +#ifndef NDEBUG + for (i = 1; i <= m->g->nsub; i++) { + assert(m->pmatch[i].rm_so == -1); + assert(m->pmatch[i].rm_eo == -1); + } +#endif + NOTE("backoff dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); + } + assert(dp == NULL || dp == endp); + if (dp != NULL) /* found a shorter one */ + break; + + /* despite initial appearances, there is no match here */ + NOTE("false alarm"); + start = m->coldp + 1; /* recycle starting later */ + assert(start <= stop); + } + + /* fill in the details if requested */ + if (nmatch > 0) { + pmatch[0].rm_so = m->coldp - m->offp; + pmatch[0].rm_eo = endp - m->offp; + } + if (nmatch > 1) { + assert(m->pmatch != NULL); + for (i = 1; i < nmatch; i++) + if (i <= m->g->nsub) + pmatch[i] = m->pmatch[i]; + else { + pmatch[i].rm_so = -1; + pmatch[i].rm_eo = -1; + } + } + + if (m->pmatch != NULL) + free((char *)m->pmatch); + if (m->lastpos != NULL) + free((char *)m->lastpos); + STATETEARDOWN(m); + return(0); +} + +/* + - dissect - figure out what matched what, no back references + == static char *dissect(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* == stop (success) always */ +dissect(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register int i; + register sopno ss; /* start sop of current subRE */ + register sopno es; /* end sop of current subRE */ + register char *sp; /* start of string matched by it */ + register char *stp; /* string matched by it cannot pass here */ + register char *rest; /* start of rest of string */ + register char *tail; /* string unmatched by rest of RE */ + register sopno ssub; /* start sop of subsubRE */ + register sopno esub; /* end sop of subsubRE */ + register char *ssp; /* start of string matched by subsubRE */ + register char *sep; /* end of string matched by subsubRE */ + register char *oldssp; /* previous ssp */ + register char *dp; + + AT("diss", start, stop, startst, stopst); + sp = start; + for (ss = startst; ss < stopst; ss = es) { + /* identify end of subRE */ + es = ss; + switch (OP(m->g->strip[es])) { + case OPLUS_: + case OQUEST_: + es += OPND(m->g->strip[es]); + break; + case OCH_: + while (OP(m->g->strip[es]) != O_CH) + es += OPND(m->g->strip[es]); + break; + } + es++; + + /* figure out what it matched */ + switch (OP(m->g->strip[ss])) { + case OEND: + assert(nope); + break; + case OCHAR: + sp++; + break; + case OBOL: + case OEOL: + case OBOW: + case OEOW: + break; + case OANY: + case OANYOF: + sp++; + break; + case OBACK_: + case O_BACK: + assert(nope); + break; + /* cases where length of match is hard to find */ + case OQUEST_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + /* did innards match? */ + if (slow(m, sp, rest, ssub, esub) != NULL) { + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + } else /* no */ + assert(sp == rest); + sp = rest; + break; + case OPLUS_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + ssp = sp; + oldssp = ssp; + for (;;) { /* find last match of innards */ + sep = slow(m, ssp, rest, ssub, esub); + if (sep == NULL || sep == ssp) + break; /* failed or matched null */ + oldssp = ssp; /* on to next try */ + ssp = sep; + } + if (sep == NULL) { + /* last successful match */ + sep = ssp; + ssp = oldssp; + } + assert(sep == rest); /* must exhaust substring */ + assert(slow(m, ssp, sep, ssub, esub) == rest); + dp = dissect(m, ssp, sep, ssub, esub); + assert(dp == sep); + sp = rest; + break; + case OCH_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = ss + OPND(m->g->strip[ss]) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + if (slow(m, sp, rest, ssub, esub) == rest) + break; /* it matched all of it */ + /* that one missed, try next one */ + assert(OP(m->g->strip[esub]) == OOR1); + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + sp = rest; + break; + case O_PLUS: + case O_QUEST: + case OOR1: + case OOR2: + case O_CH: + assert(nope); + break; + case OLPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_so = sp - m->offp; + break; + case ORPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_eo = sp - m->offp; + break; + default: /* uh oh */ + assert(nope); + break; + } + } + + assert(sp == stop); + return(sp); +} + +/* + - backref - figure out what matched what, figuring in back references + == static char *backref(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst, sopno lev); + */ +static char * /* == stop (success) or NULL (failure) */ +backref(m, start, stop, startst, stopst, lev) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +sopno lev; /* PLUS nesting level */ +{ + register int i; + register sopno ss; /* start sop of current subRE */ + register char *sp; /* start of string matched by it */ + register sopno ssub; /* start sop of subsubRE */ + register sopno esub; /* end sop of subsubRE */ + register char *ssp; /* start of string matched by subsubRE */ + register char *dp; + register size_t len; + register int hard; + register sop s; + register regoff_t offsave; + register cset *cs; + + AT("back", start, stop, startst, stopst); + sp = start; + + /* get as far as we can with easy stuff */ + hard = 0; + for (ss = startst; !hard && ss < stopst; ss++) + switch (OP(s = m->g->strip[ss])) { + case OCHAR: + if (sp == stop || *sp++ != (char)OPND(s)) + return(NULL); + break; + case OANY: + if (sp == stop) + return(NULL); + sp++; + break; + case OANYOF: + cs = &m->g->sets[OPND(s)]; + if (sp == stop || !CHIN(cs, *sp++)) + return(NULL); + break; + case OBOL: + if ( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOL: + if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OBOW: + if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp > m->beginp && + !ISWORD(*(sp-1))) ) && + (sp < m->endp && ISWORD(*sp)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOW: + if (( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp < m->endp && !ISWORD(*sp)) ) && + (sp > m->beginp && ISWORD(*(sp-1))) ) + { /* yes */ } + else + return(NULL); + break; + case O_QUEST: + break; + case OOR1: /* matches null but needs to skip */ + ss++; + s = m->g->strip[ss]; + do { + assert(OP(s) == OOR2); + ss += OPND(s); + } while (OP(s = m->g->strip[ss]) != O_CH); + /* note that the ss++ gets us past the O_CH */ + break; + default: /* have to make a choice */ + hard = 1; + break; + } + if (!hard) { /* that was it! */ + if (sp != stop) + return(NULL); + return(sp); + } + ss--; /* adjust for the for's final increment */ + + /* the hard stuff */ + AT("hard", sp, stop, ss, stopst); + s = m->g->strip[ss]; + switch (OP(s)) { + case OBACK_: /* the vilest depths */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + if (m->pmatch[i].rm_eo == -1) + return(NULL); + assert(m->pmatch[i].rm_so != -1); + len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so; + assert(stop - m->beginp >= len); + if (sp > stop - len) + return(NULL); /* not enough left to match */ + ssp = m->offp + m->pmatch[i].rm_so; + if (memcmp(sp, ssp, len) != 0) + return(NULL); + while (m->g->strip[ss] != SOP(O_BACK, i)) + ss++; + return(backref(m, sp+len, stop, ss+1, stopst, lev)); + break; + case OQUEST_: /* to null or not */ + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); /* not */ + return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev)); + break; + case OPLUS_: + assert(m->lastpos != NULL); + assert(lev+1 <= m->g->nplus); + m->lastpos[lev+1] = sp; + return(backref(m, sp, stop, ss+1, stopst, lev+1)); + break; + case O_PLUS: + if (sp == m->lastpos[lev]) /* last pass matched null */ + return(backref(m, sp, stop, ss+1, stopst, lev-1)); + /* try another pass */ + m->lastpos[lev] = sp; + dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev); + if (dp == NULL) + return(backref(m, sp, stop, ss+1, stopst, lev-1)); + else + return(dp); + break; + case OCH_: /* find the right one, if any */ + ssub = ss + 1; + esub = ss + OPND(s) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + dp = backref(m, sp, stop, ssub, esub, lev); + if (dp != NULL) + return(dp); + /* that one missed, try next one */ + if (OP(m->g->strip[esub]) == O_CH) + return(NULL); /* there is none */ + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + break; + case OLPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_so; + m->pmatch[i].rm_so = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_so = offsave; + return(NULL); + break; + case ORPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_eo; + m->pmatch[i].rm_eo = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_eo = offsave; + return(NULL); + break; + default: /* uh oh */ + assert(nope); + break; + } + + /* "can't happen" */ + assert(nope); + /* NOTREACHED */ + return( NULL ); +} + +/* + - fast - step through the string at top speed + == static char *fast(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* where tentative match ended, or NULL */ +fast(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register states st = m->st; + register states fresh = m->fresh; + register states tmp = m->tmp; + register char *p = start; + register int c = (start == m->beginp) ? OUT : *(start-1); + register int lastc; /* previous c */ + register int flagch; + register int i; + register char *coldp; /* last p after which no match was underway */ + + CLEAR(st); + SET1(st, startst); + st = step(m->g, startst, stopst, st, NOTHING, st); + ASSIGN(fresh, st); + SP("start", st, *p); + coldp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + if (EQ(st, fresh)) + coldp = p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("boleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("boweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, fresh); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("aft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + assert(coldp != NULL); + m->coldp = coldp; + if (ISSET(st, stopst)) + return(p+1); + else + return(NULL); +} + +/* + - slow - step through the string more deliberately + == static char *slow(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* where it ended */ +slow(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register states st = m->st; + register states empty = m->empty; + register states tmp = m->tmp; + register char *p = start; + register int c = (start == m->beginp) ? OUT : *(start-1); + register int lastc; /* previous c */ + register int flagch; + register int i; + register char *matchp; /* last p at which a match ended */ + + AT("slow", start, stop, startst, stopst); + CLEAR(st); + SET1(st, startst); + SP("sstart", st, *p); + st = step(m->g, startst, stopst, st, NOTHING, st); + matchp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst)) + matchp = p; + if (EQ(st, empty) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, empty); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("saft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + return(matchp); +} + + +/* + - step - map set of states reachable before char to set reachable after + == static states step(register struct re_guts *g, sopno start, sopno stop, \ + == register states bef, int ch, register states aft); + == #define BOL (OUT+1) + == #define EOL (BOL+1) + == #define BOLEOL (BOL+2) + == #define NOTHING (BOL+3) + == #define BOW (BOL+4) + == #define EOW (BOL+5) + == #define CODEMAX (BOL+5) // highest code used + == #define NONCHAR(c) ((c) > CHAR_MAX) + == #define NNONCHAR (CODEMAX-CHAR_MAX) + */ +static states +step(g, start, stop, bef, ch, aft) +register struct re_guts *g; +sopno start; /* start state within strip */ +sopno stop; /* state after stop state within strip */ +register states bef; /* states reachable before */ +int ch; /* character or NONCHAR code */ +register states aft; /* states already known reachable after */ +{ + register cset *cs; + register sop s; + register sopno pc; + register onestate here; /* note, macros know this name */ + register sopno look; + register int i; + + for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) { + s = g->strip[pc]; + switch (OP(s)) { + case OEND: + assert(pc == stop-1); + break; + case OCHAR: + /* only characters can match */ + assert(!NONCHAR(ch) || ch != (char)OPND(s)); + if (ch == (char)OPND(s)) + FWD(aft, bef, 1); + break; + case OBOL: + if (ch == BOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OEOL: + if (ch == EOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OBOW: + if (ch == BOW) + FWD(aft, bef, 1); + break; + case OEOW: + if (ch == EOW) + FWD(aft, bef, 1); + break; + case OANY: + if (!NONCHAR(ch)) + FWD(aft, bef, 1); + break; + case OANYOF: + cs = &g->sets[OPND(s)]; + if (!NONCHAR(ch) && CHIN(cs, ch)) + FWD(aft, bef, 1); + break; + case OBACK_: /* ignored here */ + case O_BACK: + FWD(aft, aft, 1); + break; + case OPLUS_: /* forward, this is just an empty */ + FWD(aft, aft, 1); + break; + case O_PLUS: /* both forward and back */ + FWD(aft, aft, 1); + i = ISSETBACK(aft, OPND(s)); + BACK(aft, aft, OPND(s)); + if (!i && ISSETBACK(aft, OPND(s))) { + /* oho, must reconsider loop body */ + pc -= OPND(s) + 1; + INIT(here, pc); + } + break; + case OQUEST_: /* two branches, both forward */ + FWD(aft, aft, 1); + FWD(aft, aft, OPND(s)); + break; + case O_QUEST: /* just an empty */ + FWD(aft, aft, 1); + break; + case OLPAREN: /* not significant here */ + case ORPAREN: + FWD(aft, aft, 1); + break; + case OCH_: /* mark the first two branches */ + FWD(aft, aft, 1); + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + break; + case OOR1: /* done a branch, find the O_CH */ + if (ISSTATEIN(aft, here)) { + for (look = 1; + OP(s = g->strip[pc+look]) != O_CH; + look += OPND(s)) + assert(OP(s) == OOR2); + FWD(aft, aft, look); + } + break; + case OOR2: /* propagate OCH_'s marking */ + FWD(aft, aft, 1); + if (OP(g->strip[pc+OPND(s)]) != O_CH) { + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + } + break; + case O_CH: /* just empty */ + FWD(aft, aft, 1); + break; + default: /* ooooops... */ + assert(nope); + break; + } + } + + return(aft); +} + +#ifdef REDEBUG +/* + - print - print a set of states + == #ifdef REDEBUG + == static void print(struct match *m, char *caption, states st, \ + == int ch, FILE *d); + == #endif + */ +static void +print(m, caption, st, ch, d) +struct match *m; +char *caption; +states st; +int ch; +FILE *d; +{ + register struct re_guts *g = m->g; + register int i; + register int first = 1; + + if (!(m->eflags®_TRACE)) + return; + + fprintf(d, "%s", caption); + if (ch != '\0') + fprintf(d, " %s", pchar(ch)); + for (i = 0; i < g->nstates; i++) + if (ISSET(st, i)) { + fprintf(d, "%s%d", (first) ? "\t" : ", ", i); + first = 0; + } + fprintf(d, "\n"); +} + +/* + - at - print current situation + == #ifdef REDEBUG + == static void at(struct match *m, char *title, char *start, char *stop, \ + == sopno startst, sopno stopst); + == #endif + */ +static void +at(m, title, start, stop, startst, stopst) +struct match *m; +char *title; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + if (!(m->eflags®_TRACE)) + return; + + printf("%s %s-", title, pchar(*start)); + printf("%s ", pchar(*stop)); + printf("%ld-%ld\n", (long)startst, (long)stopst); +} + +#ifndef PCHARDONE +#define PCHARDONE /* never again */ +/* + - pchar - make a character printable + == #ifdef REDEBUG + == static char *pchar(int ch); + == #endif + * + * Is this identical to regchar() over in debug.c? Well, yes. But a + * duplicate here avoids having a debugging-capable regexec.o tied to + * a matching debug.o, and this is convenient. It all disappears in + * the non-debug compilation anyway, so it doesn't matter much. + */ +static char * /* -> representation */ +pchar(ch) +int ch; +{ + static char pbuf[10]; + + if (isprint(ch) || ch == ' ') + sprintf(pbuf, "%c", ch); + else + sprintf(pbuf, "\\%o", ch); + return(pbuf); +} +#endif +#endif + +#undef matcher +#undef fast +#undef slow +#undef dissect +#undef backref +#undef step +#undef print +#undef at +#undef match diff --git a/usr.sbin/httpd/src/regex/main.c b/usr.sbin/httpd/src/regex/main.c new file mode 100644 index 00000000000..657338a2c19 --- /dev/null +++ b/usr.sbin/httpd/src/regex/main.c @@ -0,0 +1,510 @@ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <regex.h> +#include <assert.h> +#include <stdlib.h> + +#include "main.ih" + +char *progname; +int debug = 0; +int line = 0; +int status = 0; + +int copts = REG_EXTENDED; +int eopts = 0; +regoff_t startoff = 0; +regoff_t endoff = 0; + + +extern int split(); +extern void regprint(); + +/* + - main - do the simple case, hand off to regress() for regression + */ +int main(argc, argv) +int argc; +char *argv[]; +{ + regex_t re; +# define NS 10 + regmatch_t subs[NS]; + char erbuf[100]; + int err; + size_t len; + int c; + int errflg = 0; + register int i; + extern int optind; + extern char *optarg; + + progname = argv[0]; + + while ((c = getopt(argc, argv, "c:e:S:E:x")) != EOF) + switch (c) { + case 'c': /* compile options */ + copts = options('c', optarg); + break; + case 'e': /* execute options */ + eopts = options('e', optarg); + break; + case 'S': /* start offset */ + startoff = (regoff_t)atoi(optarg); + break; + case 'E': /* end offset */ + endoff = (regoff_t)atoi(optarg); + break; + case 'x': /* Debugging. */ + debug++; + break; + case '?': + default: + errflg++; + break; + } + if (errflg) { + fprintf(stderr, "usage: %s ", progname); + fprintf(stderr, "[-c copt][-C][-d] [re]\n"); + exit(2); + } + + if (optind >= argc) { + regress(stdin); + exit(status); + } + + err = regcomp(&re, argv[optind++], copts); + if (err) { + len = regerror(err, &re, erbuf, sizeof(erbuf)); + fprintf(stderr, "error %s, %d/%d `%s'\n", + eprint(err), len, sizeof(erbuf), erbuf); + exit(status); + } + regprint(&re, stdout); + + if (optind >= argc) { + regfree(&re); + exit(status); + } + + if (eopts®_STARTEND) { + subs[0].rm_so = startoff; + subs[0].rm_eo = strlen(argv[optind]) - endoff; + } + err = regexec(&re, argv[optind], (size_t)NS, subs, eopts); + if (err) { + len = regerror(err, &re, erbuf, sizeof(erbuf)); + fprintf(stderr, "error %s, %d/%d `%s'\n", + eprint(err), len, sizeof(erbuf), erbuf); + exit(status); + } + if (!(copts®_NOSUB)) { + len = (int)(subs[0].rm_eo - subs[0].rm_so); + if (subs[0].rm_so != -1) { + if (len != 0) + printf("match `%.*s'\n", (int)len, + argv[optind] + subs[0].rm_so); + else + printf("match `'@%.1s\n", + argv[optind] + subs[0].rm_so); + } + for (i = 1; i < NS; i++) + if (subs[i].rm_so != -1) + printf("(%d) `%.*s'\n", i, + (int)(subs[i].rm_eo - subs[i].rm_so), + argv[optind] + subs[i].rm_so); + } + exit(status); +} + +/* + - regress - main loop of regression test + == void regress(FILE *in); + */ +void +regress(in) +FILE *in; +{ + char inbuf[1000]; +# define MAXF 10 + char *f[MAXF]; + int nf; + int i; + char erbuf[100]; + size_t ne; + char *badpat = "invalid regular expression"; +# define SHORT 10 + char *bpname = "REG_BADPAT"; + regex_t re; + + while (fgets(inbuf, sizeof(inbuf), in) != NULL) { + line++; + if (inbuf[0] == '#' || inbuf[0] == '\n') + continue; /* NOTE CONTINUE */ + inbuf[strlen(inbuf)-1] = '\0'; /* get rid of stupid \n */ + if (debug) + fprintf(stdout, "%d:\n", line); + nf = split(inbuf, f, MAXF, "\t\t"); + if (nf < 3) { + fprintf(stderr, "bad input, line %d\n", line); + exit(1); + } + for (i = 0; i < nf; i++) + if (strcmp(f[i], "\"\"") == 0) + f[i] = ""; + if (nf <= 3) + f[3] = NULL; + if (nf <= 4) + f[4] = NULL; + try(f[0], f[1], f[2], f[3], f[4], options('c', f[1])); + if (opt('&', f[1])) /* try with either type of RE */ + try(f[0], f[1], f[2], f[3], f[4], + options('c', f[1]) &~ REG_EXTENDED); + } + + ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf)); + if (strcmp(erbuf, badpat) != 0 || ne != strlen(badpat)+1) { + fprintf(stderr, "end: regerror() test gave `%s' not `%s'\n", + erbuf, badpat); + status = 1; + } + ne = regerror(REG_BADPAT, (regex_t *)NULL, erbuf, (size_t)SHORT); + if (strncmp(erbuf, badpat, SHORT-1) != 0 || erbuf[SHORT-1] != '\0' || + ne != strlen(badpat)+1) { + fprintf(stderr, "end: regerror() short test gave `%s' not `%.*s'\n", + erbuf, SHORT-1, badpat); + status = 1; + } + ne = regerror(REG_ITOA|REG_BADPAT, (regex_t *)NULL, erbuf, sizeof(erbuf)); + if (strcmp(erbuf, bpname) != 0 || ne != strlen(bpname)+1) { + fprintf(stderr, "end: regerror() ITOA test gave `%s' not `%s'\n", + erbuf, bpname); + status = 1; + } + re.re_endp = bpname; + ne = regerror(REG_ATOI, &re, erbuf, sizeof(erbuf)); + if (atoi(erbuf) != (int)REG_BADPAT) { + fprintf(stderr, "end: regerror() ATOI test gave `%s' not `%ld'\n", + erbuf, (long)REG_BADPAT); + status = 1; + } else if (ne != strlen(erbuf)+1) { + fprintf(stderr, "end: regerror() ATOI test len(`%s') = %ld\n", + erbuf, (long)REG_BADPAT); + status = 1; + } +} + +/* + - try - try it, and report on problems + == void try(char *f0, char *f1, char *f2, char *f3, char *f4, int opts); + */ +void +try(f0, f1, f2, f3, f4, opts) +char *f0; +char *f1; +char *f2; +char *f3; +char *f4; +int opts; /* may not match f1 */ +{ + regex_t re; +# define NSUBS 10 + regmatch_t subs[NSUBS]; +# define NSHOULD 15 + char *should[NSHOULD]; + int nshould; + char erbuf[100]; + int err; + int len; + char *type = (opts & REG_EXTENDED) ? "ERE" : "BRE"; + register int i; + char *grump; + char f0copy[1000]; + char f2copy[1000]; + + strcpy(f0copy, f0); + re.re_endp = (opts®_PEND) ? f0copy + strlen(f0copy) : NULL; + fixstr(f0copy); + err = regcomp(&re, f0copy, opts); + if (err != 0 && (!opt('C', f1) || err != efind(f2))) { + /* unexpected error or wrong error */ + len = regerror(err, &re, erbuf, sizeof(erbuf)); + fprintf(stderr, "%d: %s error %s, %d/%d `%s'\n", + line, type, eprint(err), len, + sizeof(erbuf), erbuf); + status = 1; + } else if (err == 0 && opt('C', f1)) { + /* unexpected success */ + fprintf(stderr, "%d: %s should have given REG_%s\n", + line, type, f2); + status = 1; + err = 1; /* so we won't try regexec */ + } + + if (err != 0) { + regfree(&re); + return; + } + + strcpy(f2copy, f2); + fixstr(f2copy); + + if (options('e', f1)®_STARTEND) { + if (strchr(f2, '(') == NULL || strchr(f2, ')') == NULL) + fprintf(stderr, "%d: bad STARTEND syntax\n", line); + subs[0].rm_so = strchr(f2, '(') - f2 + 1; + subs[0].rm_eo = strchr(f2, ')') - f2; + } + err = regexec(&re, f2copy, NSUBS, subs, options('e', f1)); + + if (err != 0 && (f3 != NULL || err != REG_NOMATCH)) { + /* unexpected error or wrong error */ + len = regerror(err, &re, erbuf, sizeof(erbuf)); + fprintf(stderr, "%d: %s exec error %s, %d/%d `%s'\n", + line, type, eprint(err), len, + sizeof(erbuf), erbuf); + status = 1; + } else if (err != 0) { + /* nothing more to check */ + } else if (f3 == NULL) { + /* unexpected success */ + fprintf(stderr, "%d: %s exec should have failed\n", + line, type); + status = 1; + err = 1; /* just on principle */ + } else if (opts®_NOSUB) { + /* nothing more to check */ + } else if ((grump = check(f2, subs[0], f3)) != NULL) { + fprintf(stderr, "%d: %s %s\n", line, type, grump); + status = 1; + err = 1; + } + + if (err != 0 || f4 == NULL) { + regfree(&re); + return; + } + + for (i = 1; i < NSHOULD; i++) + should[i] = NULL; + nshould = split(f4, should+1, NSHOULD-1, ","); + if (nshould == 0) { + nshould = 1; + should[1] = ""; + } + for (i = 1; i < NSUBS; i++) { + grump = check(f2, subs[i], should[i]); + if (grump != NULL) { + fprintf(stderr, "%d: %s $%d %s\n", line, + type, i, grump); + status = 1; + err = 1; + } + } + + regfree(&re); +} + +/* + - options - pick options out of a regression-test string + == int options(int type, char *s); + */ +int +options(type, s) +int type; /* 'c' compile, 'e' exec */ +char *s; +{ + register char *p; + register int o = (type == 'c') ? copts : eopts; + register char *legal = (type == 'c') ? "bisnmp" : "^$#tl"; + + for (p = s; *p != '\0'; p++) + if (strchr(legal, *p) != NULL) + switch (*p) { + case 'b': + o &= ~REG_EXTENDED; + break; + case 'i': + o |= REG_ICASE; + break; + case 's': + o |= REG_NOSUB; + break; + case 'n': + o |= REG_NEWLINE; + break; + case 'm': + o &= ~REG_EXTENDED; + o |= REG_NOSPEC; + break; + case 'p': + o |= REG_PEND; + break; + case '^': + o |= REG_NOTBOL; + break; + case '$': + o |= REG_NOTEOL; + break; + case '#': + o |= REG_STARTEND; + break; + case 't': /* trace */ + o |= REG_TRACE; + break; + case 'l': /* force long representation */ + o |= REG_LARGE; + break; + case 'r': /* force backref use */ + o |= REG_BACKR; + break; + } + return(o); +} + +/* + - opt - is a particular option in a regression string? + == int opt(int c, char *s); + */ +int /* predicate */ +opt(c, s) +int c; +char *s; +{ + return(strchr(s, c) != NULL); +} + +/* + - fixstr - transform magic characters in strings + == void fixstr(register char *p); + */ +void +fixstr(p) +register char *p; +{ + if (p == NULL) + return; + + for (; *p != '\0'; p++) + if (*p == 'N') + *p = '\n'; + else if (*p == 'T') + *p = '\t'; + else if (*p == 'S') + *p = ' '; + else if (*p == 'Z') + *p = '\0'; +} + +/* + - check - check a substring match + == char *check(char *str, regmatch_t sub, char *should); + */ +char * /* NULL or complaint */ +check(str, sub, should) +char *str; +regmatch_t sub; +char *should; +{ + register int len; + register int shlen; + register char *p; + static char grump[500]; + register char *at = NULL; + + if (should != NULL && strcmp(should, "-") == 0) + should = NULL; + if (should != NULL && should[0] == '@') { + at = should + 1; + should = ""; + } + + /* check rm_so and rm_eo for consistency */ + if (sub.rm_so > sub.rm_eo || (sub.rm_so == -1 && sub.rm_eo != -1) || + (sub.rm_so != -1 && sub.rm_eo == -1) || + (sub.rm_so != -1 && sub.rm_so < 0) || + (sub.rm_eo != -1 && sub.rm_eo < 0) ) { + sprintf(grump, "start %ld end %ld", (long)sub.rm_so, + (long)sub.rm_eo); + return(grump); + } + + /* check for no match */ + if (sub.rm_so == -1 && should == NULL) + return(NULL); + if (sub.rm_so == -1) + return("did not match"); + + /* check for in range */ + if (sub.rm_eo > strlen(str)) { + sprintf(grump, "start %ld end %ld, past end of string", + (long)sub.rm_so, (long)sub.rm_eo); + return(grump); + } + + len = (int)(sub.rm_eo - sub.rm_so); + shlen = (int)strlen(should); + p = str + sub.rm_so; + + /* check for not supposed to match */ + if (should == NULL) { + sprintf(grump, "matched `%.*s'", len, p); + return(grump); + } + + /* check for wrong match */ + if (len != shlen || strncmp(p, should, (size_t)shlen) != 0) { + sprintf(grump, "matched `%.*s' instead", len, p); + return(grump); + } + if (shlen > 0) + return(NULL); + + /* check null match in right place */ + if (at == NULL) + return(NULL); + shlen = strlen(at); + if (shlen == 0) + shlen = 1; /* force check for end-of-string */ + if (strncmp(p, at, shlen) != 0) { + sprintf(grump, "matched null at `%.20s'", p); + return(grump); + } + return(NULL); +} + +/* + - eprint - convert error number to name + == static char *eprint(int err); + */ +static char * +eprint(err) +int err; +{ + static char epbuf[100]; + size_t len; + + len = regerror(REG_ITOA|err, (regex_t *)NULL, epbuf, sizeof(epbuf)); + assert(len <= sizeof(epbuf)); + return(epbuf); +} + +/* + - efind - convert error name to number + == static int efind(char *name); + */ +static int +efind(name) +char *name; +{ + static char efbuf[100]; + regex_t re; + + sprintf(efbuf, "REG_%s", name); + assert(strlen(efbuf) < sizeof(efbuf)); + re.re_endp = efbuf; + (void) regerror(REG_ATOI, &re, efbuf, sizeof(efbuf)); + return(atoi(efbuf)); +} diff --git a/usr.sbin/httpd/src/regex/mkh b/usr.sbin/httpd/src/regex/mkh new file mode 100644 index 00000000000..252b246c7bd --- /dev/null +++ b/usr.sbin/httpd/src/regex/mkh @@ -0,0 +1,76 @@ +#! /bin/sh +# mkh - pull headers out of C source +PATH=/bin:/usr/bin ; export PATH + +# egrep pattern to pick out marked lines +egrep='^ =([ ]|$)' + +# Sed program to process marked lines into lines for the header file. +# The markers have already been removed. Two things are done here: removal +# of backslashed newlines, and some fudging of comments. The first is done +# because -o needs to have prototypes on one line to strip them down. +# Getting comments into the output is tricky; we turn C++-style // comments +# into /* */ comments, after altering any existing */'s to avoid trouble. +peel=' /\\$/N + /\\\n[ ]*/s///g + /\/\//s;\*/;* /;g + /\/\//s;//\(.*\);/*\1 */;' + +for a +do + case "$a" in + -o) # old (pre-function-prototype) compiler + # add code to comment out argument lists + peel="$peel + "'/^\([^#\/][^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1(/*\2*/);' + shift + ;; + -b) # funny Berkeley __P macro + peel="$peel + "'/^\([^#\/][^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1 __P((\2));' + shift + ;; + -s) # compiler doesn't like `static foo();' + # add code to get rid of the `static' + peel="$peel + "'/^static[ ][^\/]*[a-zA-Z0-9_)](.*)/s;static.;;' + shift + ;; + -p) # private declarations + egrep='^ ==([ ]|$)' + shift + ;; + -i) # wrap in #ifndef, argument is name + ifndef="$2" + shift ; shift + ;; + *) break + ;; + esac +done + +if test " $ifndef" != " " +then + echo "#ifndef $ifndef" + echo "#define $ifndef /* never again */" +fi +echo "/* ========= begin header generated by $0 ========= */" +echo '#ifdef __cplusplus' +echo 'extern "C" {' +echo '#endif' +for f +do + echo + echo "/* === $f === */" + egrep "$egrep" $f | sed 's/^ ==*[ ]//;s/^ ==*$//' | sed "$peel" + echo +done +echo '#ifdef __cplusplus' +echo '}' +echo '#endif' +echo "/* ========= end header generated by $0 ========= */" +if test " $ifndef" != " " +then + echo "#endif" +fi +exit 0 diff --git a/usr.sbin/httpd/src/regex/regcomp.c b/usr.sbin/httpd/src/regex/regcomp.c new file mode 100644 index 00000000000..c3a7b1ae1fe --- /dev/null +++ b/usr.sbin/httpd/src/regex/regcomp.c @@ -0,0 +1,1546 @@ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +#include "cclass.h" +#include "cname.h" + +/* + * parse structure, passed up and down to avoid global variables and + * other clumsinesses + */ +struct parse { + char *next; /* next character in RE */ + char *end; /* end of string (-> NUL normally) */ + int error; /* has an error been seen? */ + sop *strip; /* malloced strip */ + sopno ssize; /* malloced strip size (allocated) */ + sopno slen; /* malloced strip length (used) */ + int ncsalloc; /* number of csets allocated */ + struct re_guts *g; +# define NPAREN 10 /* we need to remember () 1-9 for back refs */ + sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ + sopno pend[NPAREN]; /* -> ) ([0] unused) */ +}; + +#include "regcomp.ih" + +static char nuls[10]; /* place to point scanner in event of error */ + +/* + * macros for use with parse structure + * BEWARE: these know that the parse structure is named `p' !!! + */ +#define PEEK() (*p->next) +#define PEEK2() (*(p->next+1)) +#define MORE() (p->next < p->end) +#define MORE2() (p->next+1 < p->end) +#define SEE(c) (MORE() && PEEK() == (c)) +#define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) +#define EAT(c) ((SEE(c)) ? (NEXT1(), 1) : 0) +#define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) +#define NEXT1() (p->next++) +#define NEXT2() (p->next += 2) +#define NEXTn(n) (p->next += (n)) +#define GETNEXT() (*p->next++) +#define SETERROR(e) seterr(p, (e)) +#define REQUIRE(co, e) ((void)((co) || SETERROR(e))) +#define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) +#define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) +#define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) +#define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) +#define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) +#define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) +#define ASTERN(sop, pos) EMIT(sop, HERE()-pos) +#define HERE() (p->slen) +#define THERE() (p->slen - 1) +#define THERETHERE() (p->slen - 2) +#define DROP(n) (p->slen -= (n)) + +#ifndef NDEBUG +static int never = 0; /* for use in asserts; shuts lint up */ +#else +#define never 0 /* some <assert.h>s have bugs too */ +#endif + +/* + - regcomp - interface for parser and compilation + = extern int regcomp(regex_t *, const char *, int); + = #define REG_BASIC 0000 + = #define REG_EXTENDED 0001 + = #define REG_ICASE 0002 + = #define REG_NOSUB 0004 + = #define REG_NEWLINE 0010 + = #define REG_NOSPEC 0020 + = #define REG_PEND 0040 + = #define REG_DUMP 0200 + */ +int /* 0 success, otherwise REG_something */ +regcomp(preg, pattern, cflags) +regex_t *preg; +const char *pattern; +int cflags; +{ + struct parse pa; + register struct re_guts *g; + register struct parse *p = &pa; + register int i; + register size_t len; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&~REG_DUMP) +#endif + + cflags = GOODFLAGS(cflags); + if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) + return(REG_INVARG); + + if (cflags®_PEND) { + if (preg->re_endp < pattern) + return(REG_INVARG); + len = preg->re_endp - pattern; + } else + len = strlen((char *)pattern); + + /* do the mallocs early so failure handling is easy */ + g = (struct re_guts *)malloc(sizeof(struct re_guts) + + (NC-1)*sizeof(cat_t)); + if (g == NULL) + return(REG_ESPACE); + p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ + p->strip = (sop *)malloc(p->ssize * sizeof(sop)); + p->slen = 0; + if (p->strip == NULL) { + free((char *)g); + return(REG_ESPACE); + } + + /* set things up */ + p->g = g; + p->next = (char *)pattern; /* convenience; we do not modify it */ + p->end = p->next + len; + p->error = 0; + p->ncsalloc = 0; + for (i = 0; i < NPAREN; i++) { + p->pbegin[i] = 0; + p->pend[i] = 0; + } + g->csetsize = NC; + g->sets = NULL; + g->setbits = NULL; + g->ncsets = 0; + g->cflags = cflags; + g->iflags = 0; + g->nbol = 0; + g->neol = 0; + g->must = NULL; + g->mlen = 0; + g->nsub = 0; + g->ncategories = 1; /* category 0 is "everything else" */ + g->categories = &g->catspace[-(CHAR_MIN)]; + (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t)); + g->backrefs = 0; + + /* do it */ + EMIT(OEND, 0); + g->firststate = THERE(); + if (cflags®_EXTENDED) + p_ere(p, OUT); + else if (cflags®_NOSPEC) + p_str(p); + else + p_bre(p, OUT, OUT); + EMIT(OEND, 0); + g->laststate = THERE(); + + /* tidy up loose ends and fill things in */ + categorize(p, g); + stripsnug(p, g); + findmust(p, g); + g->nplus = pluscount(p, g); + g->magic = MAGIC2; + preg->re_nsub = g->nsub; + preg->re_g = g; + preg->re_magic = MAGIC1; +#ifndef REDEBUG + /* not debugging, so can't rely on the assert() in regexec() */ + if (g->iflags&BAD) + SETERROR(REG_ASSERT); +#endif + + /* win or lose, we're done */ + if (p->error != 0) /* lose */ + regfree(preg); + return(p->error); +} + +/* + - p_ere - ERE parser top level, concatenation and alternation + == static void p_ere(register struct parse *p, int stop); + */ +static void +p_ere(p, stop) +register struct parse *p; +int stop; /* character this ERE should end at */ +{ + register char c; + register sopno prevback = 0; + register sopno prevfwd = 0; + register sopno conc; + register int first = 1; /* is this the first alternative? */ + + for (;;) { + /* do a bunch of concatenated expressions */ + conc = HERE(); + while (MORE() && (c = PEEK()) != '|' && c != stop) + p_ere_exp(p); + REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ + + if (!EAT('|')) + break; /* NOTE BREAK OUT */ + + if (first) { + INSERT(OCH_, conc); /* offset is wrong */ + prevfwd = conc; + prevback = conc; + first = 0; + } + ASTERN(OOR1, prevback); + prevback = THERE(); + AHEAD(prevfwd); /* fix previous offset */ + prevfwd = HERE(); + EMIT(OOR2, 0); /* offset is very wrong */ + } + + if (!first) { /* tail-end fixups */ + AHEAD(prevfwd); + ASTERN(O_CH, prevback); + } + + assert(!MORE() || SEE(stop)); +} + +/* + - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op + == static void p_ere_exp(register struct parse *p); + */ +static void +p_ere_exp(p) +register struct parse *p; +{ + register char c; + register sopno pos; + register int count; + register int count2; + register sopno subno; + int wascaret = 0; + + assert(MORE()); /* caller should have ensured this */ + c = GETNEXT(); + + pos = HERE(); + switch (c) { + case '(': + REQUIRE(MORE(), REG_EPAREN); + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + if (!SEE(')')) + p_ere(p, ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + MUSTEAT(')', REG_EPAREN); + break; +#ifndef POSIX_MISTAKE + case ')': /* happens only if no current unmatched ( */ + /* + * You may ask, why the ifndef? Because I didn't notice + * this until slightly too late for 1003.2, and none of the + * other 1003.2 regular-expression reviewers noticed it at + * all. So an unmatched ) is legal POSIX, at least until + * we can get it fixed. + */ + SETERROR(REG_EPAREN); + break; +#endif + case '^': + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + wascaret = 1; + break; + case '$': + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + break; + case '|': + SETERROR(REG_EMPTY); + break; + case '*': + case '+': + case '?': + SETERROR(REG_BADRPT); + break; + case '.': + if (p->g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case '\\': + REQUIRE(MORE(), REG_EESCAPE); + c = GETNEXT(); + ordinary(p, c); + break; + case '{': /* okay as ordinary except if digit follows */ + REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, c); + break; + } + + if (!MORE()) + return; + c = PEEK(); + /* we call { a repetition if followed by a digit */ + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit(PEEK2())) )) + return; /* no repetition, we're done */ + NEXT1(); + + REQUIRE(!wascaret, REG_BADRPT); + switch (c) { + case '*': /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + break; + case '+': + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + break; + case '?': + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, pos); /* offset slightly wrong */ + ASTERN(OOR1, pos); /* this one's right */ + AHEAD(pos); /* fix the OCH_ */ + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + break; + case '{': + count = p_count(p); + if (EAT(',')) { + if (isdigit(PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EAT('}')) { /* error heuristics */ + while (MORE() && PEEK() != '}') + NEXT1(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + break; + } + + if (!MORE()) + return; + c = PEEK(); + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit(PEEK2())) ) ) + return; + SETERROR(REG_BADRPT); +} + +/* + - p_str - string (no metacharacters) "parser" + == static void p_str(register struct parse *p); + */ +static void +p_str(p) +register struct parse *p; +{ + REQUIRE(MORE(), REG_EMPTY); + while (MORE()) + ordinary(p, GETNEXT()); +} + +/* + - p_bre - BRE parser top level, anchoring and concatenation + == static void p_bre(register struct parse *p, register int end1, \ + == register int end2); + * Giving end1 as OUT essentially eliminates the end1/end2 check. + * + * This implementation is a bit of a kludge, in that a trailing $ is first + * taken as an ordinary character and then revised to be an anchor. The + * only undesirable side effect is that '$' gets included as a character + * category in such cases. This is fairly harmless; not worth fixing. + * The amount of lookahead needed to avoid this kludge is excessive. + */ +static void +p_bre(p, end1, end2) +register struct parse *p; +register int end1; /* first terminating character */ +register int end2; /* second terminating character */ +{ + register sopno start = HERE(); + register int first = 1; /* first subexpression? */ + register int wasdollar = 0; + + if (EAT('^')) { + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + } + while (MORE() && !SEETWO(end1, end2)) { + wasdollar = p_simp_re(p, first); + first = 0; + } + if (wasdollar) { /* oops, that was a trailing anchor */ + DROP(1); + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + } + + REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ +} + +/* + - p_simp_re - parse a simple RE, an atom possibly followed by a repetition + == static int p_simp_re(register struct parse *p, int starordinary); + */ +static int /* was the simple RE an unbackslashed $? */ +p_simp_re(p, starordinary) +register struct parse *p; +int starordinary; /* is a leading * an ordinary character? */ +{ + register int c; + register int count; + register int count2; + register sopno pos; + register int i; + register sopno subno; +# define BACKSL (1<<CHAR_BIT) + + pos = HERE(); /* repetion op, if any, covers from here */ + + assert(MORE()); /* caller should have ensured this */ + c = GETNEXT(); + if (c == '\\') { + REQUIRE(MORE(), REG_EESCAPE); + c = BACKSL | (unsigned char)GETNEXT(); + } + switch (c) { + case '.': + if (p->g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case BACKSL|'{': + SETERROR(REG_BADRPT); + break; + case BACKSL|'(': + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + /* the MORE here is an error heuristic */ + if (MORE() && !SEETWO('\\', ')')) + p_bre(p, '\\', ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + REQUIRE(EATTWO('\\', ')'), REG_EPAREN); + break; + case BACKSL|')': /* should not get here -- must be user */ + case BACKSL|'}': + SETERROR(REG_EPAREN); + break; + case BACKSL|'1': + case BACKSL|'2': + case BACKSL|'3': + case BACKSL|'4': + case BACKSL|'5': + case BACKSL|'6': + case BACKSL|'7': + case BACKSL|'8': + case BACKSL|'9': + i = (c&~BACKSL) - '0'; + assert(i < NPAREN); + if (p->pend[i] != 0) { + assert(i <= p->g->nsub); + EMIT(OBACK_, i); + assert(p->pbegin[i] != 0); + assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); + assert(OP(p->strip[p->pend[i]]) == ORPAREN); + (void) dupl(p, p->pbegin[i]+1, p->pend[i]); + EMIT(O_BACK, i); + } else + SETERROR(REG_ESUBREG); + p->g->backrefs = 1; + break; + case '*': + REQUIRE(starordinary, REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, c &~ BACKSL); + break; + } + + if (EAT('*')) { /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + } else if (EATTWO('\\', '{')) { + count = p_count(p); + if (EAT(',')) { + if (MORE() && isdigit(PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EATTWO('\\', '}')) { /* error heuristics */ + while (MORE() && !SEETWO('\\', '}')) + NEXT1(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */ + return(1); + + return(0); +} + +/* + - p_count - parse a repetition count + == static int p_count(register struct parse *p); + */ +static int /* the value */ +p_count(p) +register struct parse *p; +{ + register int count = 0; + register int ndigits = 0; + + while (MORE() && isdigit(PEEK()) && count <= DUPMAX) { + count = count*10 + (GETNEXT() - '0'); + ndigits++; + } + + REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); + return(count); +} + +/* + - p_bracket - parse a bracketed character list + == static void p_bracket(register struct parse *p); + * + * Note a significant property of this code: if the allocset() did SETERROR, + * no set operations are done. + */ +static void +p_bracket(p) +register struct parse *p; +{ + register cset *cs = allocset(p); + register int invert = 0; + + /* Dept of Truly Sickening Special-Case Kludges */ + if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { + EMIT(OBOW, 0); + NEXTn(6); + return; + } + if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { + EMIT(OEOW, 0); + NEXTn(6); + return; + } + + if (EAT('^')) + invert++; /* make note to invert set at end */ + if (EAT(']')) + CHadd(cs, ']'); + else if (EAT('-')) + CHadd(cs, '-'); + while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) + p_b_term(p, cs); + if (EAT('-')) + CHadd(cs, '-'); + MUSTEAT(']', REG_EBRACK); + + if (p->error != 0) /* don't mess things up further */ + return; + + if (p->g->cflags®_ICASE) { + register int i; + register int ci; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i) && isalpha(i)) { + ci = othercase(i); + if (ci != i) + CHadd(cs, ci); + } + if (cs->multis != NULL) + mccase(p, cs); + } + if (invert) { + register int i; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i)) + CHsub(cs, i); + else + CHadd(cs, i); + if (p->g->cflags®_NEWLINE) + CHsub(cs, '\n'); + if (cs->multis != NULL) + mcinvert(p, cs); + } + + assert(cs->multis == NULL); /* xxx */ + + if (nch(p, cs) == 1) { /* optimize singleton sets */ + ordinary(p, firstch(p, cs)); + freeset(p, cs); + } else + EMIT(OANYOF, freezeset(p, cs)); +} + +/* + - p_b_term - parse one term of a bracketed character list + == static void p_b_term(register struct parse *p, register cset *cs); + */ +static void +p_b_term(p, cs) +register struct parse *p; +register cset *cs; +{ + register char c; + register char start, finish; + register int i; + + /* classify what we've got */ + switch ((MORE()) ? PEEK() : '\0') { + case '[': + c = (MORE2()) ? PEEK2() : '\0'; + break; + case '-': + SETERROR(REG_ERANGE); + return; /* NOTE RETURN */ + break; + default: + c = '\0'; + break; + } + + switch (c) { + case ':': /* character class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECTYPE); + p_b_cclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO(':', ']'), REG_ECTYPE); + break; + case '=': /* equivalence class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECOLLATE); + p_b_eclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); + break; + default: /* symbol, ordinary character, or range */ +/* xxx revision needed for multichar stuff */ + start = p_b_symbol(p); + if (SEE('-') && MORE2() && PEEK2() != ']') { + /* range */ + NEXT1(); + if (EAT('-')) + finish = '-'; + else + finish = p_b_symbol(p); + } else + finish = start; +/* xxx what about signed chars here... */ + REQUIRE(start <= finish, REG_ERANGE); + for (i = start; i <= finish; i++) + CHadd(cs, i); + break; + } +} + +/* + - p_b_cclass - parse a character-class name and deal with it + == static void p_b_cclass(register struct parse *p, register cset *cs); + */ +static void +p_b_cclass(p, cs) +register struct parse *p; +register cset *cs; +{ + register char *sp = p->next; + register struct cclass *cp; + register size_t len; + register char *u; + register char c; + + while (MORE() && isalpha(PEEK())) + NEXT1(); + len = p->next - sp; + for (cp = cclasses; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + break; + if (cp->name == NULL) { + /* oops, didn't find it */ + SETERROR(REG_ECTYPE); + return; + } + + u = cp->chars; + while ((c = *u++) != '\0') + CHadd(cs, c); + for (u = cp->multis; *u != '\0'; u += strlen(u) + 1) + MCadd(p, cs, u); +} + +/* + - p_b_eclass - parse an equivalence-class name and deal with it + == static void p_b_eclass(register struct parse *p, register cset *cs); + * + * This implementation is incomplete. xxx + */ +static void +p_b_eclass(p, cs) +register struct parse *p; +register cset *cs; +{ + register char c; + + c = p_b_coll_elem(p, '='); + CHadd(cs, c); +} + +/* + - p_b_symbol - parse a character or [..]ed multicharacter collating symbol + == static char p_b_symbol(register struct parse *p); + */ +static char /* value of symbol */ +p_b_symbol(p) +register struct parse *p; +{ + register char value; + + REQUIRE(MORE(), REG_EBRACK); + if (!EATTWO('[', '.')) + return(GETNEXT()); + + /* collating symbol */ + value = p_b_coll_elem(p, '.'); + REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); + return(value); +} + +/* + - p_b_coll_elem - parse a collating-element name and look it up + == static char p_b_coll_elem(register struct parse *p, int endc); + */ +static char /* value of collating element */ +p_b_coll_elem(p, endc) +register struct parse *p; +int endc; /* name ended by endc,']' */ +{ + register char *sp = p->next; + register struct cname *cp; + register int len; + + while (MORE() && !SEETWO(endc, ']')) + NEXT1(); + if (!MORE()) { + SETERROR(REG_EBRACK); + return(0); + } + len = p->next - sp; + for (cp = cnames; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + return(cp->code); /* known name */ + if (len == 1) + return(*sp); /* single character */ + SETERROR(REG_ECOLLATE); /* neither */ + return(0); +} + +/* + - othercase - return the case counterpart of an alphabetic + == static char othercase(int ch); + */ +static char /* if no counterpart, return ch */ +othercase(ch) +int ch; +{ + assert(isalpha(ch)); + if (isupper(ch)) + return(tolower(ch)); + else if (islower(ch)) + return(toupper(ch)); + else /* peculiar, but could happen */ + return(ch); +} + +/* + - bothcases - emit a dualcase version of a two-case character + == static void bothcases(register struct parse *p, int ch); + * + * Boy, is this implementation ever a kludge... + */ +static void +bothcases(p, ch) +register struct parse *p; +int ch; +{ + register char *oldnext = p->next; + register char *oldend = p->end; + char bracket[3]; + + assert(othercase(ch) != ch); /* p_bracket() would recurse */ + p->next = bracket; + p->end = bracket+2; + bracket[0] = ch; + bracket[1] = ']'; + bracket[2] = '\0'; + p_bracket(p); + assert(p->next == bracket+2); + p->next = oldnext; + p->end = oldend; +} + +/* + - ordinary - emit an ordinary character + == static void ordinary(register struct parse *p, register int ch); + */ +static void +ordinary(p, ch) +register struct parse *p; +register int ch; +{ + register cat_t *cap = p->g->categories; + + if ((p->g->cflags®_ICASE) && isalpha(ch) && othercase(ch) != ch) + bothcases(p, ch); + else { + EMIT(OCHAR, (unsigned char)ch); + if (cap[ch] == 0) + cap[ch] = p->g->ncategories++; + } +} + +/* + - nonnewline - emit REG_NEWLINE version of OANY + == static void nonnewline(register struct parse *p); + * + * Boy, is this implementation ever a kludge... + */ +static void +nonnewline(p) +register struct parse *p; +{ + register char *oldnext = p->next; + register char *oldend = p->end; + char bracket[4]; + + p->next = bracket; + p->end = bracket+3; + bracket[0] = '^'; + bracket[1] = '\n'; + bracket[2] = ']'; + bracket[3] = '\0'; + p_bracket(p); + assert(p->next == bracket+3); + p->next = oldnext; + p->end = oldend; +} + +/* + - repeat - generate code for a bounded repetition, recursively if needed + == static void repeat(register struct parse *p, sopno start, int from, int to); + */ +static void +repeat(p, start, from, to) +register struct parse *p; +sopno start; /* operand from here to end of strip */ +int from; /* repeated from this number */ +int to; /* to this number of times (maybe INFINITY) */ +{ + register sopno finish = HERE(); +# define N 2 +# define INF 3 +# define REP(f, t) ((f)*8 + (t)) +# define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) + register sopno copy; + + if (p->error != 0) /* head off possible runaway recursion */ + return; + + assert(from <= to); + + switch (REP(MAP(from), MAP(to))) { + case REP(0, 0): /* must be user doing this */ + DROP(finish-start); /* drop the operand */ + break; + case REP(0, 1): /* as x{1,1}? */ + case REP(0, N): /* as x{1,n}? */ + case REP(0, INF): /* as x{1,}? */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); /* offset is wrong... */ + repeat(p, start+1, 1, to); + ASTERN(OOR1, start); + AHEAD(start); /* ... fix it */ + EMIT(OOR2, 0); + AHEAD(THERE()); + ASTERN(O_CH, THERETHERE()); + break; + case REP(1, 1): /* trivial case */ + /* done */ + break; + case REP(1, N): /* as x?x{1,n-1} */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); + ASTERN(OOR1, start); + AHEAD(start); + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + copy = dupl(p, start+1, finish+1); + assert(copy == finish+4); + repeat(p, copy, 1, to-1); + break; + case REP(1, INF): /* as x+ */ + INSERT(OPLUS_, start); + ASTERN(O_PLUS, start); + break; + case REP(N, N): /* as xx{m-1,n-1} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to-1); + break; + case REP(N, INF): /* as xx{n-1,INF} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to); + break; + default: /* "can't happen" */ + SETERROR(REG_ASSERT); /* just in case */ + break; + } +} + +/* + - seterr - set an error condition + == static int seterr(register struct parse *p, int e); + */ +static int /* useless but makes type checking happy */ +seterr(p, e) +register struct parse *p; +int e; +{ + if (p->error == 0) /* keep earliest error condition */ + p->error = e; + p->next = nuls; /* try to bring things to a halt */ + p->end = nuls; + return(0); /* make the return value well-defined */ +} + +/* + - allocset - allocate a set of characters for [] + == static cset *allocset(register struct parse *p); + */ +static cset * +allocset(p) +register struct parse *p; +{ + register int no = p->g->ncsets++; + register size_t nc; + register size_t nbytes; + register cset *cs; + register size_t css = (size_t)p->g->csetsize; + register int i; + + if (no >= p->ncsalloc) { /* need another column of space */ + p->ncsalloc += CHAR_BIT; + nc = p->ncsalloc; + assert(nc % CHAR_BIT == 0); + nbytes = nc / CHAR_BIT * css; + if (p->g->sets == NULL) + p->g->sets = (cset *)malloc(nc * sizeof(cset)); + else + p->g->sets = (cset *)realloc((char *)p->g->sets, + nc * sizeof(cset)); + if (p->g->setbits == NULL) + p->g->setbits = (uch *)malloc(nbytes); + else { + p->g->setbits = (uch *)realloc((char *)p->g->setbits, + nbytes); + /* xxx this isn't right if setbits is now NULL */ + for (i = 0; i < no; i++) + p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT); + } + if (p->g->sets != NULL && p->g->setbits != NULL) + (void) memset((char *)p->g->setbits + (nbytes - css), + 0, css); + else { + no = 0; + SETERROR(REG_ESPACE); + /* caller's responsibility not to do set ops */ + } + } + + assert(p->g->sets != NULL); /* xxx */ + cs = &p->g->sets[no]; + cs->ptr = p->g->setbits + css*((no)/CHAR_BIT); + cs->mask = 1 << ((no) % CHAR_BIT); + cs->hash = 0; + cs->smultis = 0; + cs->multis = NULL; + + return(cs); +} + +/* + - freeset - free a now-unused set + == static void freeset(register struct parse *p, register cset *cs); + */ +static void +freeset(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register cset *top = &p->g->sets[p->g->ncsets]; + register size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + CHsub(cs, i); + if (cs == top-1) /* recover only the easy case */ + p->g->ncsets--; +} + +/* + - freezeset - final processing on a set of characters + == static int freezeset(register struct parse *p, register cset *cs); + * + * The main task here is merging identical sets. This is usually a waste + * of time (although the hash code minimizes the overhead), but can win + * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash + * is done using addition rather than xor -- all ASCII [aA] sets xor to + * the same value! + */ +static int /* set number */ +freezeset(p, cs) +register struct parse *p; +register cset *cs; +{ + register uch h = cs->hash; + register int i; + register cset *top = &p->g->sets[p->g->ncsets]; + register cset *cs2; + register size_t css = (size_t)p->g->csetsize; + + /* look for an earlier one which is the same */ + for (cs2 = &p->g->sets[0]; cs2 < top; cs2++) + if (cs2->hash == h && cs2 != cs) { + /* maybe */ + for (i = 0; i < css; i++) + if (!!CHIN(cs2, i) != !!CHIN(cs, i)) + break; /* no */ + if (i == css) + break; /* yes */ + } + + if (cs2 < top) { /* found one */ + freeset(p, cs); + cs = cs2; + } + + return((int)(cs - p->g->sets)); +} + +/* + - firstch - return first character in a set (which must have at least one) + == static int firstch(register struct parse *p, register cset *cs); + */ +static int /* character; there is no "none" value */ +firstch(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + return((char)i); + assert(never); + return(0); /* arbitrary */ +} + +/* + - nch - number of characters in a set + == static int nch(register struct parse *p, register cset *cs); + */ +static int +nch(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register size_t css = (size_t)p->g->csetsize; + register int n = 0; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + n++; + return(n); +} + +/* + - mcadd - add a collating element to a cset + == static void mcadd(register struct parse *p, register cset *cs, \ + == register char *cp); + */ +static void +mcadd(p, cs, cp) +register struct parse *p; +register cset *cs; +register char *cp; +{ + register size_t oldend = cs->smultis; + + cs->smultis += strlen(cp) + 1; + if (cs->multis == NULL) + cs->multis = malloc(cs->smultis); + else + cs->multis = realloc(cs->multis, cs->smultis); + if (cs->multis == NULL) { + SETERROR(REG_ESPACE); + return; + } + + (void) strcpy(cs->multis + oldend - 1, cp); + cs->multis[cs->smultis - 1] = '\0'; +} + + +/* + - mcinvert - invert the list of collating elements in a cset + == static void mcinvert(register struct parse *p, register cset *cs); + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +static void +mcinvert(p, cs) +register struct parse *p; +register cset *cs; +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - mccase - add case counterparts of the list of collating elements in a cset + == static void mccase(register struct parse *p, register cset *cs); + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +static void +mccase(p, cs) +register struct parse *p; +register cset *cs; +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - isinsets - is this character in any sets? + == static int isinsets(register struct re_guts *g, int c); + */ +static int /* predicate */ +isinsets(g, c) +register struct re_guts *g; +int c; +{ + register uch *col; + register int i; + register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + register unsigned uc = (unsigned char)c; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc] != 0) + return(1); + return(0); +} + +/* + - samesets - are these two characters in exactly the same sets? + == static int samesets(register struct re_guts *g, int c1, int c2); + */ +static int /* predicate */ +samesets(g, c1, c2) +register struct re_guts *g; +int c1; +int c2; +{ + register uch *col; + register int i; + register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + register unsigned uc1 = (unsigned char)c1; + register unsigned uc2 = (unsigned char)c2; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc1] != col[uc2]) + return(0); + return(1); +} + +/* + - categorize - sort out character categories + == static void categorize(struct parse *p, register struct re_guts *g); + */ +static void +categorize(p, g) +struct parse *p; +register struct re_guts *g; +{ + register cat_t *cats = g->categories; + register int c; + register int c2; + register cat_t cat; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + for (c = CHAR_MIN; c <= CHAR_MAX; c++) + if (cats[c] == 0 && isinsets(g, c)) { + cat = g->ncategories++; + cats[c] = cat; + for (c2 = c+1; c2 <= CHAR_MAX; c2++) + if (cats[c2] == 0 && samesets(g, c, c2)) + cats[c2] = cat; + } +} + +/* + - dupl - emit a duplicate of a bunch of sops + == static sopno dupl(register struct parse *p, sopno start, sopno finish); + */ +static sopno /* start of duplicate */ +dupl(p, start, finish) +register struct parse *p; +sopno start; /* from here */ +sopno finish; /* to this less one */ +{ + register sopno ret = HERE(); + register sopno len = finish - start; + + assert(finish >= start); + if (len == 0) + return(ret); + enlarge(p, p->ssize + len); /* this many unexpected additions */ + assert(p->ssize >= p->slen + len); + (void) memcpy((char *)(p->strip + p->slen), + (char *)(p->strip + start), (size_t)len*sizeof(sop)); + p->slen += len; + return(ret); +} + +/* + - doemit - emit a strip operator + == static void doemit(register struct parse *p, sop op, size_t opnd); + * + * It might seem better to implement this as a macro with a function as + * hard-case backup, but it's just too big and messy unless there are + * some changes to the data structures. Maybe later. + */ +static void +doemit(p, op, opnd) +register struct parse *p; +sop op; +size_t opnd; +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* deal with oversize operands ("can't happen", more or less) */ + assert(opnd < 1<<OPSHIFT); + + /* deal with undersized strip */ + if (p->slen >= p->ssize) + enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */ + assert(p->slen < p->ssize); + + /* finally, it's all reduced to the easy case */ + p->strip[p->slen++] = SOP(op, opnd); +} + +/* + - doinsert - insert a sop into the strip + == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos); + */ +static void +doinsert(p, op, opnd, pos) +register struct parse *p; +sop op; +size_t opnd; +sopno pos; +{ + register sopno sn; + register sop s; + register int i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + sn = HERE(); + EMIT(op, opnd); /* do checks, ensure space */ + assert(HERE() == sn+1); + s = p->strip[sn]; + + /* adjust paren pointers */ + assert(pos > 0); + for (i = 1; i < NPAREN; i++) { + if (p->pbegin[i] >= pos) { + p->pbegin[i]++; + } + if (p->pend[i] >= pos) { + p->pend[i]++; + } + } + + memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], + (HERE()-pos-1)*sizeof(sop)); + p->strip[pos] = s; +} + +/* + - dofwd - complete a forward reference + == static void dofwd(register struct parse *p, sopno pos, sop value); + */ +static void +dofwd(p, pos, value) +register struct parse *p; +register sopno pos; +sop value; +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + assert(value < 1<<OPSHIFT); + p->strip[pos] = OP(p->strip[pos]) | value; +} + +/* + - enlarge - enlarge the strip + == static void enlarge(register struct parse *p, sopno size); + */ +static void +enlarge(p, size) +register struct parse *p; +register sopno size; +{ + register sop *sp; + + if (p->ssize >= size) + return; + + sp = (sop *)realloc(p->strip, size*sizeof(sop)); + if (sp == NULL) { + SETERROR(REG_ESPACE); + return; + } + p->strip = sp; + p->ssize = size; +} + +/* + - stripsnug - compact the strip + == static void stripsnug(register struct parse *p, register struct re_guts *g); + */ +static void +stripsnug(p, g) +register struct parse *p; +register struct re_guts *g; +{ + g->nstates = p->slen; + g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); + if (g->strip == NULL) { + SETERROR(REG_ESPACE); + g->strip = p->strip; + } +} + +/* + - findmust - fill in must and mlen with longest mandatory literal string + == static void findmust(register struct parse *p, register struct re_guts *g); + * + * This algorithm could do fancy things like analyzing the operands of | + * for common subsequences. Someday. This code is simple and finds most + * of the interesting cases. + * + * Note that must and mlen got initialized during setup. + */ +static void +findmust(p, g) +struct parse *p; +register struct re_guts *g; +{ + register sop *scan; + sop *start = NULL; + register sop *newstart = NULL; + register sopno newlen; + register sop s; + register char *cp; + register sopno i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* find the longest OCHAR sequence in strip */ + newlen = 0; + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OCHAR: /* sequence member */ + if (newlen == 0) /* new sequence */ + newstart = scan - 1; + newlen++; + break; + case OPLUS_: /* things that don't break one */ + case OLPAREN: + case ORPAREN: + break; + case OQUEST_: /* things that must be skipped */ + case OCH_: + scan--; + do { + scan += OPND(s); + s = *scan; + /* assert() interferes w debug printouts */ + if (OP(s) != O_QUEST && OP(s) != O_CH && + OP(s) != OOR2) { + g->iflags |= BAD; + return; + } + } while (OP(s) != O_QUEST && OP(s) != O_CH); + /* fallthrough */ + default: /* things that break a sequence */ + if (newlen > g->mlen) { /* ends one */ + start = newstart; + g->mlen = newlen; + } + newlen = 0; + break; + } + } while (OP(s) != OEND); + + if (g->mlen == 0) /* there isn't one */ + return; + + /* turn it into a character string */ + g->must = malloc((size_t)g->mlen + 1); + if (g->must == NULL) { /* argh; just forget it */ + g->mlen = 0; + return; + } + cp = g->must; + scan = start; + for (i = g->mlen; i > 0; i--) { + while (OP(s = *scan++) != OCHAR) + continue; + assert(cp < g->must + g->mlen); + *cp++ = (char)OPND(s); + } + assert(cp == g->must + g->mlen); + *cp++ = '\0'; /* just on general principles */ +} + +/* + - pluscount - count + nesting + == static sopno pluscount(register struct parse *p, register struct re_guts *g); + */ +static sopno /* nesting depth */ +pluscount(p, g) +struct parse *p; +register struct re_guts *g; +{ + register sop *scan; + register sop s; + register sopno plusnest = 0; + register sopno maxnest = 0; + + if (p->error != 0) + return(0); /* there may not be an OEND */ + + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OPLUS_: + plusnest++; + break; + case O_PLUS: + if (plusnest > maxnest) + maxnest = plusnest; + plusnest--; + break; + } + } while (OP(s) != OEND); + if (plusnest != 0) + g->iflags |= BAD; + return(maxnest); +} diff --git a/usr.sbin/httpd/src/regex/regerror.c b/usr.sbin/httpd/src/regex/regerror.c new file mode 100644 index 00000000000..850b0e61b57 --- /dev/null +++ b/usr.sbin/httpd/src/regex/regerror.c @@ -0,0 +1,124 @@ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" +#include "regerror.ih" + +/* + = #define REG_NOMATCH 1 + = #define REG_BADPAT 2 + = #define REG_ECOLLATE 3 + = #define REG_ECTYPE 4 + = #define REG_EESCAPE 5 + = #define REG_ESUBREG 6 + = #define REG_EBRACK 7 + = #define REG_EPAREN 8 + = #define REG_EBRACE 9 + = #define REG_BADBR 10 + = #define REG_ERANGE 11 + = #define REG_ESPACE 12 + = #define REG_BADRPT 13 + = #define REG_EMPTY 14 + = #define REG_ASSERT 15 + = #define REG_INVARG 16 + = #define REG_ATOI 255 // convert name to number (!) + = #define REG_ITOA 0400 // convert number to name (!) + */ +static struct rerr { + int code; + char *name; + char *explain; +} rerrs[] = { + { REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match" }, + { REG_BADPAT, "REG_BADPAT", "invalid regular expression" }, + { REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element" }, + { REG_ECTYPE, "REG_ECTYPE", "invalid character class" }, + { REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)" }, + { REG_ESUBREG, "REG_ESUBREG", "invalid backreference number" }, + { REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced" }, + { REG_EPAREN, "REG_EPAREN", "parentheses not balanced" }, + { REG_EBRACE, "REG_EBRACE", "braces not balanced" }, + { REG_BADBR, "REG_BADBR", "invalid repetition count(s)" }, + { REG_ERANGE, "REG_ERANGE", "invalid character range" }, + { REG_ESPACE, "REG_ESPACE", "out of memory" }, + { REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid" }, + { REG_EMPTY, "REG_EMPTY", "empty (sub)expression" }, + { REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug" }, + { REG_INVARG, "REG_INVARG", "invalid argument to regex routine" }, + { 0, "", "*** unknown regexp error code ***" } +}; + +/* + - regerror - the interface to error numbers + = extern size_t regerror(int, const regex_t *, char *, size_t); + */ +/* ARGSUSED */ +size_t +regerror(errcode, preg, errbuf, errbuf_size) +int errcode; +const regex_t *preg; +char *errbuf; +size_t errbuf_size; +{ + register struct rerr *r; + register size_t len; + register int target = errcode &~ REG_ITOA; + register char *s; + char convbuf[50]; + + if (errcode == REG_ATOI) + s = regatoi(preg, convbuf); + else { + for (r = rerrs; r->code != 0; r++) + if (r->code == target) + break; + + if (errcode®_ITOA) { + if (r->code != 0) + (void) strcpy(convbuf, r->name); + else + sprintf(convbuf, "REG_0x%x", target); + assert(strlen(convbuf) < sizeof(convbuf)); + s = convbuf; + } else + s = r->explain; + } + + len = strlen(s) + 1; + if (errbuf_size > 0) { + if (errbuf_size > len) + (void) strcpy(errbuf, s); + else { + (void) strncpy(errbuf, s, errbuf_size-1); + errbuf[errbuf_size-1] = '\0'; + } + } + + return(len); +} + +/* + - regatoi - internal routine to implement REG_ATOI + == static char *regatoi(const regex_t *preg, char *localbuf); + */ +static char * +regatoi(preg, localbuf) +const regex_t *preg; +char *localbuf; +{ + register struct rerr *r; + + for (r = rerrs; r->code != 0; r++) + if (strcmp(r->name, preg->re_endp) == 0) + break; + if (r->code == 0) + return("0"); + + sprintf(localbuf, "%d", r->code); + return(localbuf); +} diff --git a/usr.sbin/httpd/src/regex/regex.3 b/usr.sbin/httpd/src/regex/regex.3 new file mode 100644 index 00000000000..100c8a7f71c --- /dev/null +++ b/usr.sbin/httpd/src/regex/regex.3 @@ -0,0 +1,502 @@ +.TH REGEX 3 "17 May 1993" +.BY "Henry Spencer" +.de ZR +.\" one other place knows this name: the SEE ALSO section +.IR regex (7) \\$1 +.. +.SH NAME +regcomp, regexec, regerror, regfree \- regular-expression library +.SH SYNOPSIS +.ft B +.\".na +#include <sys/types.h> +.br +#include <regex.h> +.HP 10 +int regcomp(regex_t\ *preg, const\ char\ *pattern, int\ cflags); +.HP +int\ regexec(const\ regex_t\ *preg, const\ char\ *string, +size_t\ nmatch, regmatch_t\ pmatch[], int\ eflags); +.HP +size_t\ regerror(int\ errcode, const\ regex_t\ *preg, +char\ *errbuf, size_t\ errbuf_size); +.HP +void\ regfree(regex_t\ *preg); +.\".ad +.ft +.SH DESCRIPTION +These routines implement POSIX 1003.2 regular expressions (``RE''s); +see +.ZR . +.I Regcomp +compiles an RE written as a string into an internal form, +.I regexec +matches that internal form against a string and reports results, +.I regerror +transforms error codes from either into human-readable messages, +and +.I regfree +frees any dynamically-allocated storage used by the internal form +of an RE. +.PP +The header +.I <regex.h> +declares two structure types, +.I regex_t +and +.IR regmatch_t , +the former for compiled internal forms and the latter for match reporting. +It also declares the four functions, +a type +.IR regoff_t , +and a number of constants with names starting with ``REG_''. +.PP +.I Regcomp +compiles the regular expression contained in the +.I pattern +string, +subject to the flags in +.IR cflags , +and places the results in the +.I regex_t +structure pointed to by +.IR preg . +.I Cflags +is the bitwise OR of zero or more of the following flags: +.IP REG_EXTENDED \w'REG_EXTENDED'u+2n +Compile modern (``extended'') REs, +rather than the obsolete (``basic'') REs that +are the default. +.IP REG_BASIC +This is a synonym for 0, +provided as a counterpart to REG_EXTENDED to improve readability. +.IP REG_NOSPEC +Compile with recognition of all special characters turned off. +All characters are thus considered ordinary, +so the ``RE'' is a literal string. +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +REG_EXTENDED and REG_NOSPEC may not be used +in the same call to +.IR regcomp . +.IP REG_ICASE +Compile for matching that ignores upper/lower case distinctions. +See +.ZR . +.IP REG_NOSUB +Compile for matching that need only report success or failure, +not what was matched. +.IP REG_NEWLINE +Compile for newline-sensitive matching. +By default, newline is a completely ordinary character with no special +meaning in either REs or strings. +With this flag, +`[^' bracket expressions and `.' never match newline, +a `^' anchor matches the null string after any newline in the string +in addition to its normal function, +and the `$' anchor matches the null string before any newline in the +string in addition to its normal function. +.IP REG_PEND +The regular expression ends, +not at the first NUL, +but just before the character pointed to by the +.I re_endp +member of the structure pointed to by +.IR preg . +The +.I re_endp +member is of type +.IR const\ char\ * . +This flag permits inclusion of NULs in the RE; +they are considered ordinary characters. +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +.PP +When successful, +.I regcomp +returns 0 and fills in the structure pointed to by +.IR preg . +One member of that structure +(other than +.IR re_endp ) +is publicized: +.IR re_nsub , +of type +.IR size_t , +contains the number of parenthesized subexpressions within the RE +(except that the value of this member is undefined if the +REG_NOSUB flag was used). +If +.I regcomp +fails, it returns a non-zero error code; +see DIAGNOSTICS. +.PP +.I Regexec +matches the compiled RE pointed to by +.I preg +against the +.IR string , +subject to the flags in +.IR eflags , +and reports results using +.IR nmatch , +.IR pmatch , +and the returned value. +The RE must have been compiled by a previous invocation of +.IR regcomp . +The compiled form is not altered during execution of +.IR regexec , +so a single compiled RE can be used simultaneously by multiple threads. +.PP +By default, +the NUL-terminated string pointed to by +.I string +is considered to be the text of an entire line, minus any terminating +newline. +The +.I eflags +argument is the bitwise OR of zero or more of the following flags: +.IP REG_NOTBOL \w'REG_STARTEND'u+2n +The first character of +the string +is not the beginning of a line, so the `^' anchor should not match before it. +This does not affect the behavior of newlines under REG_NEWLINE. +.IP REG_NOTEOL +The NUL terminating +the string +does not end a line, so the `$' anchor should not match before it. +This does not affect the behavior of newlines under REG_NEWLINE. +.IP REG_STARTEND +The string is considered to start at +\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_so\fR +and to have a terminating NUL located at +\fIstring\fR\ + \fIpmatch\fR[0].\fIrm_eo\fR +(there need not actually be a NUL at that location), +regardless of the value of +.IR nmatch . +See below for the definition of +.IR pmatch +and +.IR nmatch . +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +Note that a non-zero \fIrm_so\fR does not imply REG_NOTBOL; +REG_STARTEND affects only the location of the string, +not how it is matched. +.PP +See +.ZR +for a discussion of what is matched in situations where an RE or a +portion thereof could match any of several substrings of +.IR string . +.PP +Normally, +.I regexec +returns 0 for success and the non-zero code REG_NOMATCH for failure. +Other non-zero error codes may be returned in exceptional situations; +see DIAGNOSTICS. +.PP +If REG_NOSUB was specified in the compilation of the RE, +or if +.I nmatch +is 0, +.I regexec +ignores the +.I pmatch +argument (but see below for the case where REG_STARTEND is specified). +Otherwise, +.I pmatch +points to an array of +.I nmatch +structures of type +.IR regmatch_t . +Such a structure has at least the members +.I rm_so +and +.IR rm_eo , +both of type +.I regoff_t +(a signed arithmetic type at least as large as an +.I off_t +and a +.IR ssize_t ), +containing respectively the offset of the first character of a substring +and the offset of the first character after the end of the substring. +Offsets are measured from the beginning of the +.I string +argument given to +.IR regexec . +An empty substring is denoted by equal offsets, +both indicating the character following the empty substring. +.PP +The 0th member of the +.I pmatch +array is filled in to indicate what substring of +.I string +was matched by the entire RE. +Remaining members report what substring was matched by parenthesized +subexpressions within the RE; +member +.I i +reports subexpression +.IR i , +with subexpressions counted (starting at 1) by the order of their opening +parentheses in the RE, left to right. +Unused entries in the array\(emcorresponding either to subexpressions that +did not participate in the match at all, or to subexpressions that do not +exist in the RE (that is, \fIi\fR\ > \fIpreg\fR\->\fIre_nsub\fR)\(emhave both +.I rm_so +and +.I rm_eo +set to \-1. +If a subexpression participated in the match several times, +the reported substring is the last one it matched. +(Note, as an example in particular, that when the RE `(b*)+' matches `bbb', +the parenthesized subexpression matches each of the three `b's and then +an infinite number of empty strings following the last `b', +so the reported substring is one of the empties.) +.PP +If REG_STARTEND is specified, +.I pmatch +must point to at least one +.I regmatch_t +(even if +.I nmatch +is 0 or REG_NOSUB was specified), +to hold the input offsets for REG_STARTEND. +Use for output is still entirely controlled by +.IR nmatch ; +if +.I nmatch +is 0 or REG_NOSUB was specified, +the value of +.IR pmatch [0] +will not be changed by a successful +.IR regexec . +.PP +.I Regerror +maps a non-zero +.I errcode +from either +.I regcomp +or +.I regexec +to a human-readable, printable message. +If +.I preg +is non-NULL, +the error code should have arisen from use of +the +.I regex_t +pointed to by +.IR preg , +and if the error code came from +.IR regcomp , +it should have been the result from the most recent +.I regcomp +using that +.IR regex_t . +.RI ( Regerror +may be able to supply a more detailed message using information +from the +.IR regex_t .) +.I Regerror +places the NUL-terminated message into the buffer pointed to by +.IR errbuf , +limiting the length (including the NUL) to at most +.I errbuf_size +bytes. +If the whole message won't fit, +as much of it as will fit before the terminating NUL is supplied. +In any case, +the returned value is the size of buffer needed to hold the whole +message (including terminating NUL). +If +.I errbuf_size +is 0, +.I errbuf +is ignored but the return value is still correct. +.PP +If the +.I errcode +given to +.I regerror +is first ORed with REG_ITOA, +the ``message'' that results is the printable name of the error code, +e.g. ``REG_NOMATCH'', +rather than an explanation thereof. +If +.I errcode +is REG_ATOI, +then +.I preg +shall be non-NULL and the +.I re_endp +member of the structure it points to +must point to the printable name of an error code; +in this case, the result in +.I errbuf +is the decimal digits of +the numeric value of the error code +(0 if the name is not recognized). +REG_ITOA and REG_ATOI are intended primarily as debugging facilities; +they are extensions, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +Be warned also that they are considered experimental and changes are possible. +.PP +.I Regfree +frees any dynamically-allocated storage associated with the compiled RE +pointed to by +.IR preg . +The remaining +.I regex_t +is no longer a valid compiled RE +and the effect of supplying it to +.I regexec +or +.I regerror +is undefined. +.PP +None of these functions references global variables except for tables +of constants; +all are safe for use from multiple threads if the arguments are safe. +.SH IMPLEMENTATION CHOICES +There are a number of decisions that 1003.2 leaves up to the implementor, +either by explicitly saying ``undefined'' or by virtue of them being +forbidden by the RE grammar. +This implementation treats them as follows. +.PP +See +.ZR +for a discussion of the definition of case-independent matching. +.PP +There is no particular limit on the length of REs, +except insofar as memory is limited. +Memory usage is approximately linear in RE size, and largely insensitive +to RE complexity, except for bounded repetitions. +See BUGS for one short RE using them +that will run almost any system out of memory. +.PP +A backslashed character other than one specifically given a magic meaning +by 1003.2 (such magic meanings occur only in obsolete [``basic''] REs) +is taken as an ordinary character. +.PP +Any unmatched [ is a REG_EBRACK error. +.PP +Equivalence classes cannot begin or end bracket-expression ranges. +The endpoint of one range cannot begin another. +.PP +RE_DUP_MAX, the limit on repetition counts in bounded repetitions, is 255. +.PP +A repetition operator (?, *, +, or bounds) cannot follow another +repetition operator. +A repetition operator cannot begin an expression or subexpression +or follow `^' or `|'. +.PP +`|' cannot appear first or last in a (sub)expression or after another `|', +i.e. an operand of `|' cannot be an empty subexpression. +An empty parenthesized subexpression, `()', is legal and matches an +empty (sub)string. +An empty string is not a legal RE. +.PP +A `{' followed by a digit is considered the beginning of bounds for a +bounded repetition, which must then follow the syntax for bounds. +A `{' \fInot\fR followed by a digit is considered an ordinary character. +.PP +`^' and `$' beginning and ending subexpressions in obsolete (``basic'') +REs are anchors, not ordinary characters. +.SH SEE ALSO +grep(1), regex(7) +.PP +POSIX 1003.2, sections 2.8 (Regular Expression Notation) +and +B.5 (C Binding for Regular Expression Matching). +.SH DIAGNOSTICS +Non-zero error codes from +.I regcomp +and +.I regexec +include the following: +.PP +.nf +.ta \w'REG_ECOLLATE'u+3n +REG_NOMATCH regexec() failed to match +REG_BADPAT invalid regular expression +REG_ECOLLATE invalid collating element +REG_ECTYPE invalid character class +REG_EESCAPE \e applied to unescapable character +REG_ESUBREG invalid backreference number +REG_EBRACK brackets [ ] not balanced +REG_EPAREN parentheses ( ) not balanced +REG_EBRACE braces { } not balanced +REG_BADBR invalid repetition count(s) in { } +REG_ERANGE invalid character range in [ ] +REG_ESPACE ran out of memory +REG_BADRPT ?, *, or + operand invalid +REG_EMPTY empty (sub)expression +REG_ASSERT ``can't happen''\(emyou found a bug +REG_INVARG invalid argument, e.g. negative-length string +.fi +.SH HISTORY +Written by Henry Spencer at University of Toronto, +henry@zoo.toronto.edu. +.SH BUGS +This is an alpha release with known defects. +Please report problems. +.PP +There is one known functionality bug. +The implementation of internationalization is incomplete: +the locale is always assumed to be the default one of 1003.2, +and only the collating elements etc. of that locale are available. +.PP +The back-reference code is subtle and doubts linger about its correctness +in complex cases. +.PP +.I Regexec +performance is poor. +This will improve with later releases. +.I Nmatch +exceeding 0 is expensive; +.I nmatch +exceeding 1 is worse. +.I Regexec +is largely insensitive to RE complexity \fIexcept\fR that back +references are massively expensive. +RE length does matter; in particular, there is a strong speed bonus +for keeping RE length under about 30 characters, +with most special characters counting roughly double. +.PP +.I Regcomp +implements bounded repetitions by macro expansion, +which is costly in time and space if counts are large +or bounded repetitions are nested. +An RE like, say, +`((((a{1,100}){1,100}){1,100}){1,100}){1,100}' +will (eventually) run almost any existing machine out of swap space. +.PP +There are suspected problems with response to obscure error conditions. +Notably, +certain kinds of internal overflow, +produced only by truly enormous REs or by multiply nested bounded repetitions, +are probably not handled well. +.PP +Due to a mistake in 1003.2, things like `a)b' are legal REs because `)' is +a special character only in the presence of a previous unmatched `('. +This can't be fixed until the spec is fixed. +.PP +The standard's definition of back references is vague. +For example, does +`a\e(\e(b\e)*\e2\e)*d' match `abbbd'? +Until the standard is clarified, +behavior in such cases should not be relied on. +.PP +The implementation of word-boundary matching is a bit of a kludge, +and bugs may lurk in combinations of word-boundary matching and anchoring. diff --git a/usr.sbin/httpd/src/regex/regex.7 b/usr.sbin/httpd/src/regex/regex.7 new file mode 100644 index 00000000000..d89012bda1d --- /dev/null +++ b/usr.sbin/httpd/src/regex/regex.7 @@ -0,0 +1,233 @@ +.TH REGEX 7 "7 Feb 1994" +.BY "Henry Spencer" +.SH NAME +regex \- POSIX 1003.2 regular expressions +.SH DESCRIPTION +Regular expressions (``RE''s), +as defined in POSIX 1003.2, come in two forms: +modern REs (roughly those of +.IR egrep ; +1003.2 calls these ``extended'' REs) +and obsolete REs (roughly those of +.IR ed ; +1003.2 ``basic'' REs). +Obsolete REs mostly exist for backward compatibility in some old programs; +they will be discussed at the end. +1003.2 leaves some aspects of RE syntax and semantics open; +`\(dg' marks decisions on these aspects that +may not be fully portable to other 1003.2 implementations. +.PP +A (modern) RE is one\(dg or more non-empty\(dg \fIbranches\fR, +separated by `|'. +It matches anything that matches one of the branches. +.PP +A branch is one\(dg or more \fIpieces\fR, concatenated. +It matches a match for the first, followed by a match for the second, etc. +.PP +A piece is an \fIatom\fR possibly followed +by a single\(dg `*', `+', `?', or \fIbound\fR. +An atom followed by `*' matches a sequence of 0 or more matches of the atom. +An atom followed by `+' matches a sequence of 1 or more matches of the atom. +An atom followed by `?' matches a sequence of 0 or 1 matches of the atom. +.PP +A \fIbound\fR is `{' followed by an unsigned decimal integer, +possibly followed by `,' +possibly followed by another unsigned decimal integer, +always followed by `}'. +The integers must lie between 0 and RE_DUP_MAX (255\(dg) inclusive, +and if there are two of them, the first may not exceed the second. +An atom followed by a bound containing one integer \fIi\fR +and no comma matches +a sequence of exactly \fIi\fR matches of the atom. +An atom followed by a bound +containing one integer \fIi\fR and a comma matches +a sequence of \fIi\fR or more matches of the atom. +An atom followed by a bound +containing two integers \fIi\fR and \fIj\fR matches +a sequence of \fIi\fR through \fIj\fR (inclusive) matches of the atom. +.PP +An atom is a regular expression enclosed in `()' (matching a match for the +regular expression), +an empty set of `()' (matching the null string)\(dg, +a \fIbracket expression\fR (see below), `.' +(matching any single character), `^' (matching the null string at the +beginning of a line), `$' (matching the null string at the +end of a line), a `\e' followed by one of the characters +`^.[$()|*+?{\e' +(matching that character taken as an ordinary character), +a `\e' followed by any other character\(dg +(matching that character taken as an ordinary character, +as if the `\e' had not been present\(dg), +or a single character with no other significance (matching that character). +A `{' followed by a character other than a digit is an ordinary +character, not the beginning of a bound\(dg. +It is illegal to end an RE with `\e'. +.PP +A \fIbracket expression\fR is a list of characters enclosed in `[]'. +It normally matches any single character from the list (but see below). +If the list begins with `^', +it matches any single character +(but see below) \fInot\fR from the rest of the list. +If two characters in the list are separated by `\-', this is shorthand +for the full \fIrange\fR of characters between those two (inclusive) in the +collating sequence, +e.g. `[0-9]' in ASCII matches any decimal digit. +It is illegal\(dg for two ranges to share an +endpoint, e.g. `a-c-e'. +Ranges are very collating-sequence-dependent, +and portable programs should avoid relying on them. +.PP +To include a literal `]' in the list, make it the first character +(following a possible `^'). +To include a literal `\-', make it the first or last character, +or the second endpoint of a range. +To use a literal `\-' as the first endpoint of a range, +enclose it in `[.' and `.]' to make it a collating element (see below). +With the exception of these and some combinations using `[' (see next +paragraphs), all other special characters, including `\e', lose their +special significance within a bracket expression. +.PP +Within a bracket expression, a collating element (a character, +a multi-character sequence that collates as if it were a single character, +or a collating-sequence name for either) +enclosed in `[.' and `.]' stands for the +sequence of characters of that collating element. +The sequence is a single element of the bracket expression's list. +A bracket expression containing a multi-character collating element +can thus match more than one character, +e.g. if the collating sequence includes a `ch' collating element, +then the RE `[[.ch.]]*c' matches the first five characters +of `chchcc'. +.PP +Within a bracket expression, a collating element enclosed in `[=' and +`=]' is an equivalence class, standing for the sequences of characters +of all collating elements equivalent to that one, including itself. +(If there are no other equivalent collating elements, +the treatment is as if the enclosing delimiters were `[.' and `.]'.) +For example, if o and \o'o^' are the members of an equivalence class, +then `[[=o=]]', `[[=\o'o^'=]]', and `[o\o'o^']' are all synonymous. +An equivalence class may not\(dg be an endpoint +of a range. +.PP +Within a bracket expression, the name of a \fIcharacter class\fR enclosed +in `[:' and `:]' stands for the list of all characters belonging to that +class. +Standard character class names are: +.PP +.RS +.nf +.ta 3c 6c 9c +alnum digit punct +alpha graph space +blank lower upper +cntrl print xdigit +.fi +.RE +.PP +These stand for the character classes defined in +.IR ctype (3). +A locale may provide others. +A character class may not be used as an endpoint of a range. +.PP +There are two special cases\(dg of bracket expressions: +the bracket expressions `[[:<:]]' and `[[:>:]]' match the null string at +the beginning and end of a word respectively. +A word is defined as a sequence of +word characters +which is neither preceded nor followed by +word characters. +A word character is an +.I alnum +character (as defined by +.IR ctype (3)) +or an underscore. +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +.PP +In the event that an RE could match more than one substring of a given +string, +the RE matches the one starting earliest in the string. +If the RE could match more than one substring starting at that point, +it matches the longest. +Subexpressions also match the longest possible substrings, subject to +the constraint that the whole match be as long as possible, +with subexpressions starting earlier in the RE taking priority over +ones starting later. +Note that higher-level subexpressions thus take priority over +their lower-level component subexpressions. +.PP +Match lengths are measured in characters, not collating elements. +A null string is considered longer than no match at all. +For example, +`bb*' matches the three middle characters of `abbbc', +`(wee|week)(knights|nights)' matches all ten characters of `weeknights', +when `(.*).*' is matched against `abc' the parenthesized subexpression +matches all three characters, and +when `(a*)*' is matched against `bc' both the whole RE and the parenthesized +subexpression match the null string. +.PP +If case-independent matching is specified, +the effect is much as if all case distinctions had vanished from the +alphabet. +When an alphabetic that exists in multiple cases appears as an +ordinary character outside a bracket expression, it is effectively +transformed into a bracket expression containing both cases, +e.g. `x' becomes `[xX]'. +When it appears inside a bracket expression, all case counterparts +of it are added to the bracket expression, so that (e.g.) `[x]' +becomes `[xX]' and `[^x]' becomes `[^xX]'. +.PP +No particular limit is imposed on the length of REs\(dg. +Programs intended to be portable should not employ REs longer +than 256 bytes, +as an implementation can refuse to accept such REs and remain +POSIX-compliant. +.PP +Obsolete (``basic'') regular expressions differ in several respects. +`|', `+', and `?' are ordinary characters and there is no equivalent +for their functionality. +The delimiters for bounds are `\e{' and `\e}', +with `{' and `}' by themselves ordinary characters. +The parentheses for nested subexpressions are `\e(' and `\e)', +with `(' and `)' by themselves ordinary characters. +`^' is an ordinary character except at the beginning of the +RE or\(dg the beginning of a parenthesized subexpression, +`$' is an ordinary character except at the end of the +RE or\(dg the end of a parenthesized subexpression, +and `*' is an ordinary character if it appears at the beginning of the +RE or the beginning of a parenthesized subexpression +(after a possible leading `^'). +Finally, there is one new type of atom, a \fIback reference\fR: +`\e' followed by a non-zero decimal digit \fId\fR +matches the same sequence of characters +matched by the \fId\fRth parenthesized subexpression +(numbering subexpressions by the positions of their opening parentheses, +left to right), +so that (e.g.) `\e([bc]\e)\e1' matches `bb' or `cc' but not `bc'. +.SH SEE ALSO +regex(3) +.PP +POSIX 1003.2, section 2.8 (Regular Expression Notation). +.SH BUGS +Having two kinds of REs is a botch. +.PP +The current 1003.2 spec says that `)' is an ordinary character in +the absence of an unmatched `('; +this was an unintentional result of a wording error, +and change is likely. +Avoid relying on it. +.PP +Back references are a dreadful botch, +posing major problems for efficient implementations. +They are also somewhat vaguely defined +(does +`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?). +Avoid using them. +.PP +1003.2's specification of case-independent matching is vague. +The ``one case implies all cases'' definition given above +is current consensus among implementors as to the right interpretation. +.PP +The syntax for word boundaries is incredibly ugly. diff --git a/usr.sbin/httpd/src/regex/regex.h b/usr.sbin/httpd/src/regex/regex.h new file mode 100644 index 00000000000..dde954d8332 --- /dev/null +++ b/usr.sbin/httpd/src/regex/regex.h @@ -0,0 +1,73 @@ +#ifndef _REGEX_H_ +#define _REGEX_H_ /* never again */ +/* ========= begin header generated by ./mkh ========= */ +#ifdef __cplusplus +extern "C" { +#endif + +/* === regex2.h === */ +typedef off_t regoff_t; +typedef struct { + int re_magic; + size_t re_nsub; /* number of parenthesized subexpressions */ + const char *re_endp; /* end pointer for REG_PEND */ + struct re_guts *re_g; /* none of your business :-) */ +} regex_t; +typedef struct { + regoff_t rm_so; /* start of match */ + regoff_t rm_eo; /* end of match */ +} regmatch_t; + + +/* === regcomp.c === */ +extern int regcomp(regex_t *, const char *, int); +#define REG_BASIC 0000 +#define REG_EXTENDED 0001 +#define REG_ICASE 0002 +#define REG_NOSUB 0004 +#define REG_NEWLINE 0010 +#define REG_NOSPEC 0020 +#define REG_PEND 0040 +#define REG_DUMP 0200 + + +/* === regerror.c === */ +#define REG_NOMATCH 1 +#define REG_BADPAT 2 +#define REG_ECOLLATE 3 +#define REG_ECTYPE 4 +#define REG_EESCAPE 5 +#define REG_ESUBREG 6 +#define REG_EBRACK 7 +#define REG_EPAREN 8 +#define REG_EBRACE 9 +#define REG_BADBR 10 +#define REG_ERANGE 11 +#define REG_ESPACE 12 +#define REG_BADRPT 13 +#define REG_EMPTY 14 +#define REG_ASSERT 15 +#define REG_INVARG 16 +#define REG_ATOI 255 /* convert name to number (!) */ +#define REG_ITOA 0400 /* convert number to name (!) */ +extern size_t regerror(int, const regex_t *, char *, size_t); + + +/* === regexec.c === */ +extern int regexec(const regex_t *, const char *, size_t, regmatch_t [], int); +#define REG_NOTBOL 00001 +#define REG_NOTEOL 00002 +#define REG_STARTEND 00004 +#define REG_TRACE 00400 /* tracing of execution */ +#define REG_LARGE 01000 /* force large representation */ +#define REG_BACKR 02000 /* force use of backref code */ + + +/* === regfree.c === */ +extern void regfree(regex_t *); + +#ifdef __cplusplus +} +#endif +/* ========= end header generated by ./mkh ========= */ +#endif diff --git a/usr.sbin/httpd/src/regex/regex2.h b/usr.sbin/httpd/src/regex/regex2.h new file mode 100644 index 00000000000..6ec57b2ed4e --- /dev/null +++ b/usr.sbin/httpd/src/regex/regex2.h @@ -0,0 +1,132 @@ +/* + * First, the stuff that ends up in the outside-world include file + = typedef off_t regoff_t; + = typedef struct { + = int re_magic; + = size_t re_nsub; // number of parenthesized subexpressions + = const char *re_endp; // end pointer for REG_PEND + = struct re_guts *re_g; // none of your business :-) + = } regex_t; + = typedef struct { + = regoff_t rm_so; // start of match + = regoff_t rm_eo; // end of match + = } regmatch_t; + */ +/* + * internals of regex_t + */ +#define MAGIC1 ((('r'^0200)<<8) | 'e') + +/* + * The internal representation is a *strip*, a sequence of + * operators ending with an endmarker. (Some terminology etc. is a + * historical relic of earlier versions which used multiple strips.) + * Certain oddities in the representation are there to permit running + * the machinery backwards; in particular, any deviation from sequential + * flow must be marked at both its source and its destination. Some + * fine points: + * + * - OPLUS_ and O_PLUS are *inside* the loop they create. + * - OQUEST_ and O_QUEST are *outside* the bypass they create. + * - OCH_ and O_CH are *outside* the multi-way branch they create, while + * OOR1 and OOR2 are respectively the end and the beginning of one of + * the branches. Note that there is an implicit OOR2 following OCH_ + * and an implicit OOR1 preceding O_CH. + * + * In state representations, an operator's bit is on to signify a state + * immediately *preceding* "execution" of that operator. + */ +typedef unsigned long sop; /* strip operator */ +typedef long sopno; +#define OPRMASK 0xf8000000 +#define OPDMASK 0x07ffffff +#define OPSHIFT ((unsigned)27) +#define OP(n) ((n)&OPRMASK) +#define OPND(n) ((n)&OPDMASK) +#define SOP(op, opnd) ((op)|(opnd)) +/* operators meaning operand */ +/* (back, fwd are offsets) */ +#define OEND (1<<OPSHIFT) /* endmarker - */ +#define OCHAR (2<<OPSHIFT) /* character unsigned char */ +#define OBOL (3<<OPSHIFT) /* left anchor - */ +#define OEOL (4<<OPSHIFT) /* right anchor - */ +#define OANY (5<<OPSHIFT) /* . - */ +#define OANYOF (6<<OPSHIFT) /* [...] set number */ +#define OBACK_ (7<<OPSHIFT) /* begin \d paren number */ +#define O_BACK (8<<OPSHIFT) /* end \d paren number */ +#define OPLUS_ (9<<OPSHIFT) /* + prefix fwd to suffix */ +#define O_PLUS (10<<OPSHIFT) /* + suffix back to prefix */ +#define OQUEST_ (11<<OPSHIFT) /* ? prefix fwd to suffix */ +#define O_QUEST (12<<OPSHIFT) /* ? suffix back to prefix */ +#define OLPAREN (13<<OPSHIFT) /* ( fwd to ) */ +#define ORPAREN (14<<OPSHIFT) /* ) back to ( */ +#define OCH_ (15<<OPSHIFT) /* begin choice fwd to OOR2 */ +#define OOR1 (16u<<OPSHIFT) /* | pt. 1 back to OOR1 or OCH_ */ +#define OOR2 (17u<<OPSHIFT) /* | pt. 2 fwd to OOR2 or O_CH */ +#define O_CH (18u<<OPSHIFT) /* end choice back to OOR1 */ +#define OBOW (19u<<OPSHIFT) /* begin word - */ +#define OEOW (20u<<OPSHIFT) /* end word - */ + +/* + * Structure for [] character-set representation. Character sets are + * done as bit vectors, grouped 8 to a byte vector for compactness. + * The individual set therefore has both a pointer to the byte vector + * and a mask to pick out the relevant bit of each byte. A hash code + * simplifies testing whether two sets could be identical. + * + * This will get trickier for multicharacter collating elements. As + * preliminary hooks for dealing with such things, we also carry along + * a string of multi-character elements, and decide the size of the + * vectors at run time. + */ +typedef struct { + uch *ptr; /* -> uch [csetsize] */ + uch mask; /* bit within array */ + uch hash; /* hash code */ + size_t smultis; + char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */ +} cset; +/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */ +#define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c)) +#define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c)) +#define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask) +#define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */ + +/* stuff for character categories */ +typedef unsigned char cat_t; + +/* + * main compiled-expression structure + */ +struct re_guts { + int magic; +# define MAGIC2 ((('R'^0200)<<8)|'E') + sop *strip; /* malloced area for strip */ + int csetsize; /* number of bits in a cset vector */ + int ncsets; /* number of csets in use */ + cset *sets; /* -> cset [ncsets] */ + uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */ + int cflags; /* copy of regcomp() cflags argument */ + sopno nstates; /* = number of sops */ + sopno firststate; /* the initial OEND (normally 0) */ + sopno laststate; /* the final OEND */ + int iflags; /* internal flags */ +# define USEBOL 01 /* used ^ */ +# define USEEOL 02 /* used $ */ +# define BAD 04 /* something wrong */ + int nbol; /* number of ^ used */ + int neol; /* number of $ used */ + int ncategories; /* how many character categories */ + cat_t *categories; /* ->catspace[-CHAR_MIN] */ + char *must; /* match must contain this string */ + int mlen; /* length of must */ + size_t nsub; /* copy of re_nsub */ + int backrefs; /* does it use back references? */ + sopno nplus; /* how deep does it nest +s? */ + /* catspace must be last */ + cat_t catspace[1]; /* actually [NC] */ +}; + +/* misc utilities */ +#define OUT (CHAR_MAX+1) /* a non-character value */ +#define ISWORD(c) (isalnum(c) || (c) == '_') diff --git a/usr.sbin/httpd/src/regex/regexec.c b/usr.sbin/httpd/src/regex/regexec.c new file mode 100644 index 00000000000..63bb1c37130 --- /dev/null +++ b/usr.sbin/httpd/src/regex/regexec.c @@ -0,0 +1,140 @@ +/* + * the outer shell of regexec() + * + * This file includes engine.c *twice*, after muchos fiddling with the + * macros that code uses. This lets the same code operate on two different + * representations for state sets. + */ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <ctype.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +#ifndef NDEBUG +static int nope = 0; /* for use in asserts; shuts lint up */ +#endif + +/* macros for manipulating states, small version */ +#define states long +#define states1 states /* for later use in regexec() decision */ +#define CLEAR(v) ((v) = 0) +#define SET0(v, n) ((v) &= ~(1 << (n))) +#define SET1(v, n) ((v) |= 1 << (n)) +#define ISSET(v, n) ((v) & (1 << (n))) +#define ASSIGN(d, s) ((d) = (s)) +#define EQ(a, b) ((a) == (b)) +#define STATEVARS int dummy /* dummy version */ +#define STATESETUP(m, n) /* nothing */ +#define STATETEARDOWN(m) /* nothing */ +#define SETUP(v) ((v) = 0) +#define onestate int +#define INIT(o, n) ((o) = (unsigned)1 << (n)) +#define INC(o) ((o) <<= 1) +#define ISSTATEIN(v, o) ((v) & (o)) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) << (n)) +#define BACK(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) >> (n)) +#define ISSETBACK(v, n) ((v) & ((unsigned)here >> (n))) +/* function names */ +#define SNAMES /* engine.c looks after details */ + +#include "engine.c" + +/* now undo things */ +#undef states +#undef CLEAR +#undef SET0 +#undef SET1 +#undef ISSET +#undef ASSIGN +#undef EQ +#undef STATEVARS +#undef STATESETUP +#undef STATETEARDOWN +#undef SETUP +#undef onestate +#undef INIT +#undef INC +#undef ISSTATEIN +#undef FWD +#undef BACK +#undef ISSETBACK +#undef SNAMES + +/* macros for manipulating states, large version */ +#define states char * +#define CLEAR(v) memset(v, 0, m->g->nstates) +#define SET0(v, n) ((v)[n] = 0) +#define SET1(v, n) ((v)[n] = 1) +#define ISSET(v, n) ((v)[n]) +#define ASSIGN(d, s) memcpy(d, s, m->g->nstates) +#define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0) +#define STATEVARS int vn; char *space +#define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \ + if ((m)->space == NULL) return(REG_ESPACE); \ + (m)->vn = 0; } +#define STATETEARDOWN(m) { free((m)->space); } +#define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates]) +#define onestate int +#define INIT(o, n) ((o) = (n)) +#define INC(o) ((o)++) +#define ISSTATEIN(v, o) ((v)[o]) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here]) +#define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here]) +#define ISSETBACK(v, n) ((v)[here - (n)]) +/* function names */ +#define LNAMES /* flag */ + +#include "engine.c" + +/* + - regexec - interface for matching + = extern int regexec(const regex_t *, const char *, size_t, \ + = regmatch_t [], int); + = #define REG_NOTBOL 00001 + = #define REG_NOTEOL 00002 + = #define REG_STARTEND 00004 + = #define REG_TRACE 00400 // tracing of execution + = #define REG_LARGE 01000 // force large representation + = #define REG_BACKR 02000 // force use of backref code + * + * We put this here so we can exploit knowledge of the state representation + * when choosing which matcher to call. Also, by this point the matchers + * have been prototyped. + */ +int /* 0 success, REG_NOMATCH failure */ +regexec(preg, string, nmatch, pmatch, eflags) +const regex_t *preg; +const char *string; +size_t nmatch; +regmatch_t pmatch[]; +int eflags; +{ + register struct re_guts *g = preg->re_g; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND)) +#endif + + if (preg->re_magic != MAGIC1 || g->magic != MAGIC2) + return(REG_BADPAT); + assert(!(g->iflags&BAD)); + if (g->iflags&BAD) /* backstop for no-debug case */ + return(REG_BADPAT); + eflags = GOODFLAGS(eflags); + + if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags®_LARGE)) + return(smatcher(g, (char *)string, nmatch, pmatch, eflags)); + else + return(lmatcher(g, (char *)string, nmatch, pmatch, eflags)); +} diff --git a/usr.sbin/httpd/src/regex/regfree.c b/usr.sbin/httpd/src/regex/regfree.c new file mode 100644 index 00000000000..9a6acf1733a --- /dev/null +++ b/usr.sbin/httpd/src/regex/regfree.c @@ -0,0 +1,37 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +/* + - regfree - free everything + = extern void regfree(regex_t *); + */ +void +regfree(preg) +regex_t *preg; +{ + register struct re_guts *g; + + if (preg->re_magic != MAGIC1) /* oops */ + return; /* nice to complain, but hard */ + + g = preg->re_g; + if (g == NULL || g->magic != MAGIC2) /* oops again */ + return; + preg->re_magic = 0; /* mark it invalid */ + g->magic = 0; /* mark it invalid */ + + if (g->strip != NULL) + free((char *)g->strip); + if (g->sets != NULL) + free((char *)g->sets); + if (g->setbits != NULL) + free((char *)g->setbits); + if (g->must != NULL) + free(g->must); + free((char *)g); +} diff --git a/usr.sbin/httpd/src/regex/split.c b/usr.sbin/httpd/src/regex/split.c new file mode 100644 index 00000000000..188bdb775b9 --- /dev/null +++ b/usr.sbin/httpd/src/regex/split.c @@ -0,0 +1,316 @@ +#include <stdio.h> +#include <string.h> + +/* + - split - divide a string into fields, like awk split() + = int split(char *string, char *fields[], int nfields, char *sep); + */ +int /* number of fields, including overflow */ +split(string, fields, nfields, sep) +char *string; +char *fields[]; /* list is not NULL-terminated */ +int nfields; /* number of entries available in fields[] */ +char *sep; /* "" white, "c" single char, "ab" [ab]+ */ +{ + register char *p = string; + register char c; /* latest character */ + register char sepc = sep[0]; + register char sepc2; + register int fn; + register char **fp = fields; + register char *sepp; + register int trimtrail; + + /* white space */ + if (sepc == '\0') { + while ((c = *p++) == ' ' || c == '\t') + continue; + p--; + trimtrail = 1; + sep = " \t"; /* note, code below knows this is 2 long */ + sepc = ' '; + } else + trimtrail = 0; + sepc2 = sep[1]; /* now we can safely pick this up */ + + /* catch empties */ + if (*p == '\0') + return(0); + + /* single separator */ + if (sepc2 == '\0') { + fn = nfields; + for (;;) { + *fp++ = p; + fn--; + if (fn == 0) + break; + while ((c = *p++) != sepc) + if (c == '\0') + return(nfields - fn); + *(p-1) = '\0'; + } + /* we have overflowed the fields vector -- just count them */ + fn = nfields; + for (;;) { + while ((c = *p++) != sepc) + if (c == '\0') + return(fn); + fn++; + } + /* not reached */ + } + + /* two separators */ + if (sep[2] == '\0') { + fn = nfields; + for (;;) { + *fp++ = p; + fn--; + while ((c = *p++) != sepc && c != sepc2) + if (c == '\0') { + if (trimtrail && **(fp-1) == '\0') + fn++; + return(nfields - fn); + } + if (fn == 0) + break; + *(p-1) = '\0'; + while ((c = *p++) == sepc || c == sepc2) + continue; + p--; + } + /* we have overflowed the fields vector -- just count them */ + fn = nfields; + while (c != '\0') { + while ((c = *p++) == sepc || c == sepc2) + continue; + p--; + fn++; + while ((c = *p++) != '\0' && c != sepc && c != sepc2) + continue; + } + /* might have to trim trailing white space */ + if (trimtrail) { + p--; + while ((c = *--p) == sepc || c == sepc2) + continue; + p++; + if (*p != '\0') { + if (fn == nfields+1) + *p = '\0'; + fn--; + } + } + return(fn); + } + + /* n separators */ + fn = 0; + for (;;) { + if (fn < nfields) + *fp++ = p; + fn++; + for (;;) { + c = *p++; + if (c == '\0') + return(fn); + sepp = sep; + while ((sepc = *sepp++) != '\0' && sepc != c) + continue; + if (sepc != '\0') /* it was a separator */ + break; + } + if (fn < nfields) + *(p-1) = '\0'; + for (;;) { + c = *p++; + sepp = sep; + while ((sepc = *sepp++) != '\0' && sepc != c) + continue; + if (sepc == '\0') /* it wasn't a separator */ + break; + } + p--; + } + + /* not reached */ +} + +#ifdef TEST_SPLIT + + +/* + * test program + * pgm runs regression + * pgm sep splits stdin lines by sep + * pgm str sep splits str by sep + * pgm str sep n splits str by sep n times + */ +int +main(argc, argv) +int argc; +char *argv[]; +{ + char buf[512]; + register int n; +# define MNF 10 + char *fields[MNF]; + + if (argc > 4) + for (n = atoi(argv[3]); n > 0; n--) { + (void) strcpy(buf, argv[1]); + } + else if (argc > 3) + for (n = atoi(argv[3]); n > 0; n--) { + (void) strcpy(buf, argv[1]); + (void) split(buf, fields, MNF, argv[2]); + } + else if (argc > 2) + dosplit(argv[1], argv[2]); + else if (argc > 1) + while (fgets(buf, sizeof(buf), stdin) != NULL) { + buf[strlen(buf)-1] = '\0'; /* stomp newline */ + dosplit(buf, argv[1]); + } + else + regress(); + + exit(0); +} + +dosplit(string, seps) +char *string; +char *seps; +{ +# define NF 5 + char *fields[NF]; + register int nf; + + nf = split(string, fields, NF, seps); + print(nf, NF, fields); +} + +print(nf, nfp, fields) +int nf; +int nfp; +char *fields[]; +{ + register int fn; + register int bound; + + bound = (nf > nfp) ? nfp : nf; + printf("%d:\t", nf); + for (fn = 0; fn < bound; fn++) + printf("\"%s\"%s", fields[fn], (fn+1 < nf) ? ", " : "\n"); +} + +#define RNF 5 /* some table entries know this */ +struct { + char *str; + char *seps; + int nf; + char *fi[RNF]; +} tests[] = { + "", " ", 0, { "" }, + " ", " ", 2, { "", "" }, + "x", " ", 1, { "x" }, + "xy", " ", 1, { "xy" }, + "x y", " ", 2, { "x", "y" }, + "abc def g ", " ", 5, { "abc", "def", "", "g", "" }, + " a bcd", " ", 4, { "", "", "a", "bcd" }, + "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" }, + " a b c d ", " ", 6, { "", "a", "b", "c", "d " }, + + "", " _", 0, { "" }, + " ", " _", 2, { "", "" }, + "x", " _", 1, { "x" }, + "x y", " _", 2, { "x", "y" }, + "ab _ cd", " _", 2, { "ab", "cd" }, + " a_b c ", " _", 5, { "", "a", "b", "c", "" }, + "a b c_d e f", " _", 6, { "a", "b", "c", "d", "e f" }, + " a b c d ", " _", 6, { "", "a", "b", "c", "d " }, + + "", " _~", 0, { "" }, + " ", " _~", 2, { "", "" }, + "x", " _~", 1, { "x" }, + "x y", " _~", 2, { "x", "y" }, + "ab _~ cd", " _~", 2, { "ab", "cd" }, + " a_b c~", " _~", 5, { "", "a", "b", "c", "" }, + "a b_c d~e f", " _~", 6, { "a", "b", "c", "d", "e f" }, + "~a b c d ", " _~", 6, { "", "a", "b", "c", "d " }, + + "", " _~-", 0, { "" }, + " ", " _~-", 2, { "", "" }, + "x", " _~-", 1, { "x" }, + "x y", " _~-", 2, { "x", "y" }, + "ab _~- cd", " _~-", 2, { "ab", "cd" }, + " a_b c~", " _~-", 5, { "", "a", "b", "c", "" }, + "a b_c-d~e f", " _~-", 6, { "a", "b", "c", "d", "e f" }, + "~a-b c d ", " _~-", 6, { "", "a", "b", "c", "d " }, + + "", " ", 0, { "" }, + " ", " ", 2, { "", "" }, + "x", " ", 1, { "x" }, + "xy", " ", 1, { "xy" }, + "x y", " ", 2, { "x", "y" }, + "abc def g ", " ", 4, { "abc", "def", "g", "" }, + " a bcd", " ", 3, { "", "a", "bcd" }, + "a b c d e f", " ", 6, { "a", "b", "c", "d", "e f" }, + " a b c d ", " ", 6, { "", "a", "b", "c", "d " }, + + "", "", 0, { "" }, + " ", "", 0, { "" }, + "x", "", 1, { "x" }, + "xy", "", 1, { "xy" }, + "x y", "", 2, { "x", "y" }, + "abc def g ", "", 3, { "abc", "def", "g" }, + "\t a bcd", "", 2, { "a", "bcd" }, + " a \tb\t c ", "", 3, { "a", "b", "c" }, + "a b c d e ", "", 5, { "a", "b", "c", "d", "e" }, + "a b\tc d e f", "", 6, { "a", "b", "c", "d", "e f" }, + " a b c d e f ", "", 6, { "a", "b", "c", "d", "e f " }, + + NULL, NULL, 0, { NULL }, +}; + +regress() +{ + char buf[512]; + register int n; + char *fields[RNF+1]; + register int nf; + register int i; + register int printit; + register char *f; + + for (n = 0; tests[n].str != NULL; n++) { + (void) strcpy(buf, tests[n].str); + fields[RNF] = NULL; + nf = split(buf, fields, RNF, tests[n].seps); + printit = 0; + if (nf != tests[n].nf) { + printf("split `%s' by `%s' gave %d fields, not %d\n", + tests[n].str, tests[n].seps, nf, tests[n].nf); + printit = 1; + } else if (fields[RNF] != NULL) { + printf("split() went beyond array end\n"); + printit = 1; + } else { + for (i = 0; i < nf && i < RNF; i++) { + f = fields[i]; + if (f == NULL) + f = "(NULL)"; + if (strcmp(f, tests[n].fi[i]) != 0) { + printf("split `%s' by `%s', field %d is `%s', not `%s'\n", + tests[n].str, tests[n].seps, + i, fields[i], tests[n].fi[i]); + printit = 1; + } + } + } + if (printit) + print(nf, RNF, fields); + } +} +#endif diff --git a/usr.sbin/httpd/src/regex/tests b/usr.sbin/httpd/src/regex/tests new file mode 100644 index 00000000000..c05846177f5 --- /dev/null +++ b/usr.sbin/httpd/src/regex/tests @@ -0,0 +1,475 @@ +# regular expression test set +# Lines are at least three fields, separated by one or more tabs. "" stands +# for an empty field. First field is an RE. Second field is flags. If +# C flag given, regcomp() is expected to fail, and the third field is the +# error name (minus the leading REG_). +# +# Otherwise it is expected to succeed, and the third field is the string to +# try matching it against. If there is no fourth field, the match is +# expected to fail. If there is a fourth field, it is the substring that +# the RE is expected to match. If there is a fifth field, it is a comma- +# separated list of what the subexpressions should match, with - indicating +# no match for that one. In both the fourth and fifth fields, a (sub)field +# starting with @ indicates that the (sub)expression is expected to match +# a null string followed by the stuff after the @; this provides a way to +# test where null strings match. The character `N' in REs and strings +# is newline, `S' is space, `T' is tab, `Z' is NUL. +# +# The full list of flags: +# - placeholder, does nothing +# b RE is a BRE, not an ERE +# & try it as both an ERE and a BRE +# C regcomp() error expected, third field is error name +# i REG_ICASE +# m ("mundane") REG_NOSPEC +# s REG_NOSUB (not really testable) +# n REG_NEWLINE +# ^ REG_NOTBOL +# $ REG_NOTEOL +# # REG_STARTEND (see below) +# p REG_PEND +# +# For REG_STARTEND, the start/end offsets are those of the substring +# enclosed in (). + +# basics +a & a a +abc & abc abc +abc|de - abc abc +a|b|c - abc a + +# parentheses and perversions thereof +a(b)c - abc abc +a\(b\)c b abc abc +a( C EPAREN +a( b a( a( +a\( - a( a( +a\( bC EPAREN +a\(b bC EPAREN +a(b C EPAREN +a(b b a(b a(b +# gag me with a right parenthesis -- 1003.2 goofed here (my fault, partly) +a) - a) a) +) - ) ) +# end gagging (in a just world, those *should* give EPAREN) +a) b a) a) +a\) bC EPAREN +\) bC EPAREN +a()b - ab ab +a\(\)b b ab ab + +# anchoring and REG_NEWLINE +^abc$ & abc abc +a^b - a^b +a^b b a^b a^b +a$b - a$b +a$b b a$b a$b +^ & abc @abc +$ & abc @ +^$ & "" @ +$^ - "" @ +\($\)\(^\) b "" @ +# stop retching, those are legitimate (although disgusting) +^^ - "" @ +$$ - "" @ +b$ & abNc +b$ &n abNc b +^b$ & aNbNc +^b$ &n aNbNc b +^$ &n aNNb @Nb +^$ n abc +^$ n abcN @ +$^ n aNNb @Nb +\($\)\(^\) bn aNNb @Nb +^^ n^ aNNb @Nb +$$ n aNNb @NN +^a ^ a +a$ $ a +^a ^n aNb +^b ^n aNb b +a$ $n bNa +b$ $n bNa b +a*(^b$)c* - b b +a*\(^b$\)c* b b b + +# certain syntax errors and non-errors +| C EMPTY +| b | | +* C BADRPT +* b * * ++ C BADRPT +? C BADRPT +"" &C EMPTY +() - abc @abc +\(\) b abc @abc +a||b C EMPTY +|ab C EMPTY +ab| C EMPTY +(|a)b C EMPTY +(a|)b C EMPTY +(*a) C BADRPT +(+a) C BADRPT +(?a) C BADRPT +({1}a) C BADRPT +\(\{1\}a\) bC BADRPT +(a|*b) C BADRPT +(a|+b) C BADRPT +(a|?b) C BADRPT +(a|{1}b) C BADRPT +^* C BADRPT +^* b * * +^+ C BADRPT +^? C BADRPT +^{1} C BADRPT +^\{1\} bC BADRPT + +# metacharacters, backslashes +a.c & abc abc +a[bc]d & abd abd +a\*c & a*c a*c +a\\b & a\b a\b +a\\\*b & a\*b a\*b +a\bc & abc abc +a\ &C EESCAPE +a\\bc & a\bc a\bc +\{ bC BADRPT +a\[b & a[b a[b +a[b &C EBRACK +# trailing $ is a peculiar special case for the BRE code +a$ & a a +a$ & a$ +a\$ & a +a\$ & a$ a$ +a\\$ & a +a\\$ & a$ +a\\$ & a\$ +a\\$ & a\ a\ + +# back references, ugh +a\(b\)\2c bC ESUBREG +a\(b\1\)c bC ESUBREG +a\(b*\)c\1d b abbcbbd abbcbbd bb +a\(b*\)c\1d b abbcbd +a\(b*\)c\1d b abbcbbbd +^\(.\)\1 b abc +a\([bc]\)\1d b abcdabbd abbd b +a\(\([bc]\)\2\)*d b abbccd abbccd +a\(\([bc]\)\2\)*d b abbcbd +# actually, this next one probably ought to fail, but the spec is unclear +a\(\(b\)*\2\)*d b abbbd abbbd +# here is a case that no NFA implementation does right +\(ab*\)[ab]*\1 b ababaaa ababaaa a +# check out normal matching in the presence of back refs +\(a\)\1bcd b aabcd aabcd +\(a\)\1bc*d b aabcd aabcd +\(a\)\1bc*d b aabd aabd +\(a\)\1bc*d b aabcccd aabcccd +\(a\)\1bc*[ce]d b aabcccd aabcccd +^\(a\)\1b\(c\)*cd$ b aabcccd aabcccd + +# ordinary repetitions +ab*c & abc abc +ab+c - abc abc +ab?c - abc abc +a\(*\)b b a*b a*b +a\(**\)b b ab ab +a\(***\)b bC BADRPT +*a b *a *a +**a b a a +***a bC BADRPT + +# the dreaded bounded repetitions +{ & { { +{abc & {abc {abc +{1 C BADRPT +{1} C BADRPT +a{b & a{b a{b +a{1}b - ab ab +a\{1\}b b ab ab +a{1,}b - ab ab +a\{1,\}b b ab ab +a{1,2}b - aab aab +a\{1,2\}b b aab aab +a{1 C EBRACE +a\{1 bC EBRACE +a{1a C EBRACE +a\{1a bC EBRACE +a{1a} C BADBR +a\{1a\} bC BADBR +a{,2} - a{,2} a{,2} +a\{,2\} bC BADBR +a{,} - a{,} a{,} +a\{,\} bC BADBR +a{1,x} C BADBR +a\{1,x\} bC BADBR +a{1,x C EBRACE +a\{1,x bC EBRACE +a{300} C BADBR +a\{300\} bC BADBR +a{1,0} C BADBR +a\{1,0\} bC BADBR +ab{0,0}c - abcac ac +ab\{0,0\}c b abcac ac +ab{0,1}c - abcac abc +ab\{0,1\}c b abcac abc +ab{0,3}c - abbcac abbc +ab\{0,3\}c b abbcac abbc +ab{1,1}c - acabc abc +ab\{1,1\}c b acabc abc +ab{1,3}c - acabc abc +ab\{1,3\}c b acabc abc +ab{2,2}c - abcabbc abbc +ab\{2,2\}c b abcabbc abbc +ab{2,4}c - abcabbc abbc +ab\{2,4\}c b abcabbc abbc +((a{1,10}){1,10}){1,10} - a a a,a + +# multiple repetitions +a** &C BADRPT +a++ C BADRPT +a?? C BADRPT +a*+ C BADRPT +a*? C BADRPT +a+* C BADRPT +a+? C BADRPT +a?* C BADRPT +a?+ C BADRPT +a{1}{1} C BADRPT +a*{1} C BADRPT +a+{1} C BADRPT +a?{1} C BADRPT +a{1}* C BADRPT +a{1}+ C BADRPT +a{1}? C BADRPT +a*{b} - a{b} a{b} +a\{1\}\{1\} bC BADRPT +a*\{1\} bC BADRPT +a\{1\}* bC BADRPT + +# brackets, and numerous perversions thereof +a[b]c & abc abc +a[ab]c & abc abc +a[^ab]c & adc adc +a[]b]c & a]c a]c +a[[b]c & a[c a[c +a[-b]c & a-c a-c +a[^]b]c & adc adc +a[^-b]c & adc adc +a[b-]c & a-c a-c +a[b &C EBRACK +a[] &C EBRACK +a[1-3]c & a2c a2c +a[3-1]c &C ERANGE +a[1-3-5]c &C ERANGE +a[[.-.]--]c & a-c a-c +a[1- &C ERANGE +a[[. &C EBRACK +a[[.x &C EBRACK +a[[.x. &C EBRACK +a[[.x.] &C EBRACK +a[[.x.]] & ax ax +a[[.x,.]] &C ECOLLATE +a[[.one.]]b & a1b a1b +a[[.notdef.]]b &C ECOLLATE +a[[.].]]b & a]b a]b +a[[:alpha:]]c & abc abc +a[[:notdef:]]c &C ECTYPE +a[[: &C EBRACK +a[[:alpha &C EBRACK +a[[:alpha:] &C EBRACK +a[[:alpha,:] &C ECTYPE +a[[:]:]]b &C ECTYPE +a[[:-:]]b &C ECTYPE +a[[:alph:]] &C ECTYPE +a[[:alphabet:]] &C ECTYPE +[[:alnum:]]+ - -%@a0X- a0X +[[:alpha:]]+ - -%@aX0- aX +[[:blank:]]+ - aSSTb SST +[[:cntrl:]]+ - aNTb NT +[[:digit:]]+ - a019b 019 +[[:graph:]]+ - Sa%bS a%b +[[:lower:]]+ - AabC ab +[[:print:]]+ - NaSbN aSb +[[:punct:]]+ - S%-&T %-& +[[:space:]]+ - aSNTb SNT +[[:upper:]]+ - aBCd BC +[[:xdigit:]]+ - p0f3Cq 0f3C +a[[=b=]]c & abc abc +a[[= &C EBRACK +a[[=b &C EBRACK +a[[=b= &C EBRACK +a[[=b=] &C EBRACK +a[[=b,=]] &C ECOLLATE +a[[=one=]]b & a1b a1b + +# complexities +a(((b)))c - abc abc +a(b|(c))d - abd abd +a(b*|c)d - abbd abbd +# just gotta have one DFA-buster, of course +a[ab]{20} - aaaaabaaaabaaaabaaaab aaaaabaaaabaaaabaaaab +# and an inline expansion in case somebody gets tricky +a[ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab] - aaaaabaaaabaaaabaaaab aaaaabaaaabaaaabaaaab +# and in case somebody just slips in an NFA... +a[ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab][ab](wee|week)(knights|night) - aaaaabaaaabaaaabaaaabweeknights aaaaabaaaabaaaabaaaabweeknights +# fish for anomalies as the number of states passes 32 +12345678901234567890123456789 - a12345678901234567890123456789b 12345678901234567890123456789 +123456789012345678901234567890 - a123456789012345678901234567890b 123456789012345678901234567890 +1234567890123456789012345678901 - a1234567890123456789012345678901b 1234567890123456789012345678901 +12345678901234567890123456789012 - a12345678901234567890123456789012b 12345678901234567890123456789012 +123456789012345678901234567890123 - a123456789012345678901234567890123b 123456789012345678901234567890123 +# and one really big one, beyond any plausible word width +1234567890123456789012345678901234567890123456789012345678901234567890 - a1234567890123456789012345678901234567890123456789012345678901234567890b 1234567890123456789012345678901234567890123456789012345678901234567890 +# fish for problems as brackets go past 8 +[ab][cd][ef][gh][ij][kl][mn] - xacegikmoq acegikm +[ab][cd][ef][gh][ij][kl][mn][op] - xacegikmoq acegikmo +[ab][cd][ef][gh][ij][kl][mn][op][qr] - xacegikmoqy acegikmoq +[ab][cd][ef][gh][ij][kl][mn][op][q] - xacegikmoqy acegikmoq + +# subtleties of matching +abc & xabcy abc +a\(b\)?c\1d b acd +aBc i Abc Abc +a[Bc]*d i abBCcd abBCcd +0[[:upper:]]1 &i 0a1 0a1 +0[[:lower:]]1 &i 0A1 0A1 +a[^b]c &i abc +a[^b]c &i aBc +a[^b]c &i adc adc +[a]b[c] - abc abc +[a]b[a] - aba aba +[abc]b[abc] - abc abc +[abc]b[abd] - abd abd +a(b?c)+d - accd accd +(wee|week)(knights|night) - weeknights weeknights +(we|wee|week|frob)(knights|night|day) - weeknights weeknights +a[bc]d - xyzaaabcaababdacd abd +a[ab]c - aaabc abc +abc s abc abc +a* & b @b + +# Let's have some fun -- try to match a C comment. +# first the obvious, which looks okay at first glance... +/\*.*\*/ - /*x*/ /*x*/ +# but... +/\*.*\*/ - /*x*/y/*z*/ /*x*/y/*z*/ +# okay, we must not match */ inside; try to do that... +/\*([^*]|\*[^/])*\*/ - /*x*/ /*x*/ +/\*([^*]|\*[^/])*\*/ - /*x*/y/*z*/ /*x*/ +# but... +/\*([^*]|\*[^/])*\*/ - /*x**/y/*z*/ /*x**/y/*z*/ +# and a still fancier version, which does it right (I think)... +/\*([^*]|\*+[^*/])*\*+/ - /*x*/ /*x*/ +/\*([^*]|\*+[^*/])*\*+/ - /*x*/y/*z*/ /*x*/ +/\*([^*]|\*+[^*/])*\*+/ - /*x**/y/*z*/ /*x**/ +/\*([^*]|\*+[^*/])*\*+/ - /*x****/y/*z*/ /*x****/ +/\*([^*]|\*+[^*/])*\*+/ - /*x**x*/y/*z*/ /*x**x*/ +/\*([^*]|\*+[^*/])*\*+/ - /*x***x/y/*z*/ /*x***x/y/*z*/ + +# subexpressions +a(b)(c)d - abcd abcd b,c +a(((b)))c - abc abc b,b,b +a(b|(c))d - abd abd b,- +a(b*|c|e)d - abbd abbd bb +a(b*|c|e)d - acd acd c +a(b*|c|e)d - ad ad @d +a(b?)c - abc abc b +a(b?)c - ac ac @c +a(b+)c - abc abc b +a(b+)c - abbbc abbbc bbb +a(b*)c - ac ac @c +(a|ab)(bc([de]+)f|cde) - abcdef abcdef a,bcdef,de +# the regression tester only asks for 9 subexpressions +a(b)(c)(d)(e)(f)(g)(h)(i)(j)k - abcdefghijk abcdefghijk b,c,d,e,f,g,h,i,j +a(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)l - abcdefghijkl abcdefghijkl b,c,d,e,f,g,h,i,j,k +a([bc]?)c - abc abc b +a([bc]?)c - ac ac @c +a([bc]+)c - abc abc b +a([bc]+)c - abcc abcc bc +a([bc]+)bc - abcbc abcbc bc +a(bb+|b)b - abb abb b +a(bbb+|bb+|b)b - abb abb b +a(bbb+|bb+|b)b - abbb abbb bb +a(bbb+|bb+|b)bb - abbb abbb b +(.*).* - abcdef abcdef abcdef +(a*)* - bc @b @b + +# do we get the right subexpression when it is used more than once? +a(b|c)*d - ad ad - +a(b|c)*d - abcd abcd c +a(b|c)+d - abd abd b +a(b|c)+d - abcd abcd c +a(b|c?)+d - ad ad @d +a(b|c?)+d - abcd abcd @d +a(b|c){0,0}d - ad ad - +a(b|c){0,1}d - ad ad - +a(b|c){0,1}d - abd abd b +a(b|c){0,2}d - ad ad - +a(b|c){0,2}d - abcd abcd c +a(b|c){0,}d - ad ad - +a(b|c){0,}d - abcd abcd c +a(b|c){1,1}d - abd abd b +a(b|c){1,1}d - acd acd c +a(b|c){1,2}d - abd abd b +a(b|c){1,2}d - abcd abcd c +a(b|c){1,}d - abd abd b +a(b|c){1,}d - abcd abcd c +a(b|c){2,2}d - acbd acbd b +a(b|c){2,2}d - abcd abcd c +a(b|c){2,4}d - abcd abcd c +a(b|c){2,4}d - abcbd abcbd b +a(b|c){2,4}d - abcbcd abcbcd c +a(b|c){2,}d - abcd abcd c +a(b|c){2,}d - abcbd abcbd b +a(b+|((c)*))+d - abd abd @d,@d,- +a(b+|((c)*))+d - abcd abcd @d,@d,- + +# check out the STARTEND option +[abc] &# a(b)c b +[abc] &# a(d)c +[abc] &# a(bc)d b +[abc] &# a(dc)d c +. &# a()c +b.*c &# b(bc)c bc +b.* &# b(bc)c bc +.*c &# b(bc)c bc + +# plain strings, with the NOSPEC flag +abc m abc abc +abc m xabcy abc +abc m xyz +a*b m aba*b a*b +a*b m ab +"" mC EMPTY + +# cases involving NULs +aZb & a a +aZb &p a +aZb &p# (aZb) aZb +aZ*b &p# (ab) ab +a.b &# (aZb) aZb +a.* &# (aZb)c aZb + +# word boundaries (ick) +[[:<:]]a & a a +[[:<:]]a & ba +[[:<:]]a & -a a +a[[:>:]] & a a +a[[:>:]] & ab +a[[:>:]] & a- a +[[:<:]]a.c[[:>:]] & axcd-dayc-dazce-abc abc +[[:<:]]a.c[[:>:]] & axcd-dayc-dazce-abc-q abc +[[:<:]]a.c[[:>:]] & axc-dayc-dazce-abc axc +[[:<:]]b.c[[:>:]] & a_bxc-byc_d-bzc-q bzc +[[:<:]].x..[[:>:]] & y_xa_-_xb_y-_xc_-axdc _xc_ +[[:<:]]a_b[[:>:]] & x_a_b + +# past problems, and suspected problems +(A[1])|(A[2])|(A[3])|(A[4])|(A[5])|(A[6])|(A[7])|(A[8])|(A[9])|(A[A]) - A1 A1 +abcdefghijklmnop i abcdefghijklmnop abcdefghijklmnop +abcdefghijklmnopqrstuv i abcdefghijklmnopqrstuv abcdefghijklmnopqrstuv +(ALAK)|(ALT[AB])|(CC[123]1)|(CM[123]1)|(GAMC)|(LC[23][EO ])|(SEM[1234])|(SL[ES][12])|(SLWW)|(SLF )|(SLDT)|(VWH[12])|(WH[34][EW])|(WP1[ESN]) - CC11 CC11 +CC[13]1|a{21}[23][EO][123][Es][12]a{15}aa[34][EW]aaaaaaa[X]a - CC11 CC11 +Char \([a-z0-9_]*\)\[.* b Char xyz[k Char xyz[k xyz +a?b - ab ab +-\{0,1\}[0-9]*$ b -5 -5 diff --git a/usr.sbin/httpd/src/regex/utils.h b/usr.sbin/httpd/src/regex/utils.h new file mode 100644 index 00000000000..f271f759b11 --- /dev/null +++ b/usr.sbin/httpd/src/regex/utils.h @@ -0,0 +1,22 @@ +/* utility definitions */ +#ifndef _POSIX2_RE_DUP_MAX +#define _POSIX2_RE_DUP_MAX 255 +#endif + +#define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */ +#define INFINITY (DUPMAX + 1) +#define NC (CHAR_MAX - CHAR_MIN + 1) +typedef unsigned char uch; + +/* switch off assertions (if not already off) if no REDEBUG */ +#ifndef REDEBUG +#ifndef NDEBUG +#define NDEBUG /* no assertions please */ +#endif +#endif +#include <assert.h> + +/* for old systems with bcopy() but no memmove() */ +#ifdef USEBCOPY +#define memmove(d, s, c) bcopy(s, d, c) +#endif diff --git a/usr.sbin/httpd/src/rfc1413.c b/usr.sbin/httpd/src/rfc1413.c new file mode 100644 index 00000000000..858a1b0b241 --- /dev/null +++ b/usr.sbin/httpd/src/rfc1413.c @@ -0,0 +1,226 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * 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 conf.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 log_unixerr */ +#include "rfc1413.h" + +#ifndef SCO +extern char *strchr(); +extern char *inet_ntoa(); +#endif + +/* 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 rfc1413_timeout = RFC1413_TIMEOUT; /* Global so it can be changed */ + +JMP_BUF timebuf; + +/* bind_connect - bind both ends of a socket */ + +static int +get_rfc1413(int sock, const struct sockaddr_in *our_sin, + const struct sockaddr_in *rmt_sin, char user[256], 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]; + + /* + * 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) + { + log_unixerr("bind", NULL, "rfc1413: Error binding to local port", srv); + 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 */ + ap_snprintf(buffer, sizeof(buffer), "%u,%u\r\n", ntohs(rmt_sin->sin_port), + ntohs(our_sin->sin_port)); + do i = write(sock, buffer, strlen(buffer)); + while (i == -1 && errno == EINTR); + if (i == -1) + { + log_unixerr("write", NULL, "rfc1413: error sending request", srv); + return -1; + } + + /* + * Read response from server. We assume that all the data + * comes in a single packet. + */ + + do i = read(sock, buffer, RFC1413_MAXDATA); + while (i == -1 && errno == EINTR); + if (i == -1) + { + log_unixerr("read", NULL, "rfc1413: error reading response", srv); + return -1; + } + + buffer[i] = '\0'; +/* RFC1413_USERLEN = 512 */ + 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 * +rfc1413(conn_rec *conn, server_rec *srv) +{ + static char user[RFC1413_USERLEN+1]; /* XXX */ + static char *result; + static int sock; + + result = FROM_UNKNOWN; + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) + { + log_unixerr("socket", NULL, "rfc1413: error creating socket", srv); + conn->remote_logname = result; + } + + /* + * Set up a timer so we won't get stuck while waiting for the server. + */ + if (ap_setjmp(timebuf) == 0) + { + signal(SIGALRM, ident_timeout); + alarm(rfc1413_timeout); + + if (get_rfc1413(sock, &conn->local_addr, &conn->remote_addr, user, + srv) + >= 0) + result = user; + + alarm(0); + } + close(sock); + conn->remote_logname = result; + + return conn->remote_logname; +} diff --git a/usr.sbin/httpd/src/rfc1413.h b/usr.sbin/httpd/src/rfc1413.h new file mode 100644 index 00000000000..91bf42e4aef --- /dev/null +++ b/usr.sbin/httpd/src/rfc1413.h @@ -0,0 +1,53 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +extern char *rfc1413(conn_rec *conn, server_rec *srv); diff --git a/usr.sbin/httpd/src/scoreboard.h b/usr.sbin/httpd/src/scoreboard.h new file mode 100644 index 00000000000..5aa808cb4d6 --- /dev/null +++ b/usr.sbin/httpd/src/scoreboard.h @@ -0,0 +1,110 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +#include <sys/times.h> + +/* Scoreboard info on a process is, for now, kept very brief --- + * just status value and pid (the latter so that the caretaker process + * can properly update the scoreboard when a process dies). We may want + * to eventually add a separate set of long_score structures which would + * give, for each process, the number of requests serviced, and info on + * the current, or most recent, request. + * + * Status values: + */ + +#define SERVER_UNKNOWN (-1) /* should never be in this state */ +#define SERVER_DEAD 0 +#define SERVER_READY 1 /* Waiting for connection (or accept() lock) */ +#define SERVER_STARTING 3 /* Server Starting up */ +#define SERVER_BUSY_READ 2 /* Reading a client request */ +#define SERVER_BUSY_WRITE 4 /* Processing a client request */ +#define SERVER_BUSY_KEEPALIVE 5 /* Waiting for more requests via keepalive */ +#define SERVER_BUSY_LOG 6 /* Logging the request */ +#define SERVER_BUSY_DNS 7 /* Looking up a hostname */ +#define SERVER_GRACEFUL 8 /* server is gracefully finishing request */ + +typedef struct { + pid_t pid; + char status; +#if defined(STATUS) + unsigned long access_count; + unsigned long bytes_served; + unsigned long my_access_count; + unsigned long my_bytes_served; + unsigned long conn_bytes; + unsigned short conn_count; + struct tms times; + time_t last_used; + char client[32]; /* Keep 'em small... */ + char request[64]; /* We just want an idea... */ + char vhost[32]; /* What virtual host is being accessed? */ +#endif +} short_score; + +typedef struct + { + int exit_generation; /* Set by the main process if a graceful + restart is required */ + } global_score; + +typedef struct + { + short_score servers[HARD_SERVER_LIMIT]; + global_score global; + } scoreboard; + +#define SCOREBOARD_SIZE sizeof(scoreboard) + +extern void sync_scoreboard_image(void); +short_score get_scoreboard_info(int x); +int exists_scoreboard_image (); diff --git a/usr.sbin/httpd/src/util.c b/usr.sbin/httpd/src/util.c new file mode 100644 index 00000000000..12096af0932 --- /dev/null +++ b/usr.sbin/httpd/src/util.c @@ -0,0 +1,1369 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * util.c: string utility things + * + * 3/21/93 Rob McCool + * 1995-96 Many changes by the Apache Group + * + */ + +#include "httpd.h" +#include "http_conf_globals.h" /* for user_id & group_id */ + +const char month_snames[12][4] = { + "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" +}; + +char *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); +} + +char *ht_time(pool *p, time_t t, const char *fmt, int gmt) { + char ts[MAX_STRING_LEN]; + struct tm *tms; + + tms = (gmt ? gmtime(&t) : localtime(&t)); + + /* check return code? */ + strftime(ts,MAX_STRING_LEN,fmt,tms); + ts[MAX_STRING_LEN - 1] = '\0'; + return pstrdup (p, ts); +} + +char *gm_timestr_822(pool *p, time_t sec) { + static const char *const days[7]= + {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + char ts[50]; + struct tm *tms; + + tms = gmtime(&sec); + +/* RFC date format; as strftime '%a, %d %b %Y %T GMT' */ + ap_snprintf(ts, sizeof(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); + + return pstrdup (p, ts); +} + +/* What a pain in the ass. */ +#if defined(HAVE_GMTOFF) +struct tm *get_gmtoff(int *tz) { + time_t tt = time(NULL); + struct tm *t; + + t = localtime(&tt); + *tz = (int) (t->tm_gmtoff / 60); + return t; +} +#else +struct tm *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 + + +/* 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? + */ +int 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 = 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'); +} + +int 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 = strcasecmp_match(&str[x++],&exp[y])) != 1) + return ret; + } + return -1; + } else + if((exp[y] != '?') && (tolower(str[x]) != tolower(exp[y]))) + return 1; + } + return (str[x] != '\0'); +} + +int is_matchexp(const char *str) { + register int x; + + for(x=0;str[x];x++) + if((str[x] == '*') || (str[x] == '?')) + return 1; + return 0; +} + +/* This function substitutes for $0-$9, filling in regular expression + * submatches. Pass it the same nmatch and pmatch arguments that you + * passed 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. + */ + +char *pregsub(pool *p, const char *input, const char *source, + size_t nmatch, regmatch_t pmatch[]) { + const char *src = input; + char *dest, *dst; + char c; + int no, len; + + if (!source) return NULL; + if (!nmatch) return pstrdup(p, src); + + /* First pass, find the size */ + + len = 0; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && isdigit(*src)) + no = *src++ - '0'; + else + no = -1; + + if (no < 0) { /* 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 = pcalloc(p, len + 1); + + /* Now actually fill in the string */ + + src = input; + + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '$' && isdigit(*src)) + no = *src++ - '0'; + else + no = -1; + + if (no < 0) { /* 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; + strncpy(dst, source + pmatch[no].rm_so, len); + dst += len; + if (*(dst-1) == '\0') /* strncpy hit NULL. */ + return NULL; + } + + } + *dst = '\0'; + + return dest; +} + +/* + * Parse .. so we don't compromise security + */ +void 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'; + } +} + +void no2slash(char *name) +{ + char *d, *s; + + s = d = name; + while (*s) { + if ((*d++ = *s) == '/') { + do { + ++s; + } while (*s == '/'); + } + else { + ++s; + } + } + *d = '\0'; +} + +char *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 = palloc(p, x + 2); + strncpy (res, s, x); + res[x] = '/'; + res[x+1] = '\0'; + return res; + } + } + + if (s[strlen(s) - 1] == '/') + return pstrdup (p, s); + else + return pstrcat (p, s, "/", NULL); +} + +int count_dirs(const char *path) { + register int x,n; + + for(x=0,n=0;path[x];x++) + if(path[x] == '/') n++; + return n; +} + + +void chdir_file(const char *file) { + int i; + + if((i = rind(file,'/')) == -1) + return; + ((char *)file)[i] = '\0'; + chdir(file); + ((char *)file)[i] = '/'; +} + +char *getword_nc(pool* atrans, char **line, char stop) + { + return getword(atrans,(const char **)line,stop); + } + +char *getword(pool* atrans, const char **line, char stop) { + int pos = ind(*line, stop); + char *res; + + if (pos == -1) { + res = pstrdup (atrans, *line); + *line += strlen (*line); + return res; + } + + res = palloc(atrans, pos + 1); + strncpy (res, *line, pos); + res[pos] = '\0'; + + while ((*line)[pos] == stop) ++pos; + + *line += pos; + + return res; +} + +char *getword_white_nc(pool* atrans, char **line) +{ + return getword_white(atrans,(const char **)line); +} + +char *getword_white(pool* atrans, const char **line) { + int pos = -1, x; + char *res; + + for(x=0;(*line)[x];x++) { + if (isspace((*line)[x])) { + pos=x; + break; + } + } + + if (pos == -1) { + res = pstrdup (atrans, *line); + *line += strlen (*line); + return res; + } + + res = palloc(atrans, pos + 1); + strncpy (res, *line, pos); + res[pos] = '\0'; + + while (isspace((*line)[pos])) ++pos; + + *line += pos; + + return res; +} + +char *getword_nulls_nc(pool* atrans, char **line, char stop) +{ + return getword_nulls(atrans,(const char **)line,stop); +} + +char *getword_nulls(pool* atrans, const char **line, char stop) { + int pos = ind(*line, stop); + char *res; + + if (pos == -1) { + res = pstrdup (atrans, *line); + *line += strlen (*line); + return res; + } + + res = palloc(atrans, pos + 1); + strncpy (res, *line, pos); + res[pos] = '\0'; + + ++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 = 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; +} + +char *getword_conf_nc(pool* p, char **line) { + return getword_conf(p,(const char **)line); +} + +char *getword_conf(pool* p, const char **line) { + const char *str = *line, *strend; + char *res; + char quote; + + while (*str && 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 && !isspace (*strend)) + ++strend; + + res = substring_conf (p, str, strend - str, 0); + } + + while (*strend && isspace(*strend)) ++ strend; + *line = strend; + return res; +} + +#ifdef UNDEF +/* this function is dangerous, and superceded by getword_white, so don't use it + */ +void cfg_getword(char *word, char *line) { + int x=0,y; + + for(x=0;line[x] && isspace(line[x]);x++); + y=0; + while(1) { + if(!(word[y] = line[x])) + break; + if(isspace(line[x])) + if((!x) || (line[x-1] != '\\')) + break; + if(line[x] != '\\') ++y; + ++x; + } + word[y] = '\0'; + while(line[x] && isspace(line[x])) ++x; + for(y=0;(line[y] = line[x]);++x,++y); +} +#endif + +int +cfg_getline(char *s, int n, FILE *f) { + register int i=0, c; + + s[0] = '\0'; + /* skip leading whitespace */ + do { + c = getc(f); + } while (c == '\t' || c == ' '); + + if(c == EOF) + return 1; + + if(n < 2) { + /* too small, assume caller is crazy */ + return 1; + } + + while(1) { + if((c == '\t') || (c == ' ')) { + s[i++] = ' '; + while((c == '\t') || (c == ' ')) + c = getc(f); + } + if(c == CR) { + c = getc(f); + } + if(c == EOF || c == 0x4 || c == LF || i >= (n-2)) { + /* blast trailing whitespace */ + while(i && (s[i-1] == ' ')) --i; + s[i] = '\0'; + return 0; + } + s[i] = c; + ++i; + c = getc(f); + } +} + +/* 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. + */ + +char *get_token (pool *p, char **accept_line, int accept_white) +{ + char *ptr = *accept_line; + char *tok_start; + char *token; + int tok_len; + + /* Find first non-white byte */ + + while (*ptr && isspace(*ptr)) + ++ptr; + + tok_start = ptr; + + /* find token end, skipping over quoted strings. + * (comments are already gone). + */ + + while (*ptr && (accept_white || !isspace(*ptr)) + && *ptr != ';' && *ptr != ',') + { + if (*ptr++ == '"') + while (*ptr) + if (*ptr++ == '"') break; + } + + tok_len = ptr - tok_start; + token = palloc (p, tok_len + 1); + strncpy (token, tok_start, tok_len); + token[tok_len] = '\0'; + + /* Advance accept_line pointer to the next non-white byte */ + + while (*ptr && isspace(*ptr)) + ++ptr; + + *accept_line = ptr; + return token; +} + +static char* tspecials = " \t()<>@,;:\\/[]?={}"; + +/* Next HTTP token from a header line. Warning --- destructive! + * Use only with a copy! + */ + +static char *next_token (char **toks) { + char *cp = *toks; + char *ret; + + while (*cp && (iscntrl (*cp) || strchr (tspecials, *cp))) { + if (*cp == '"') + while (*cp && (*cp != '"')) ++cp; + else + ++cp; + } + + if (!*cp) ret = NULL; + else { + ret = cp; + + while (*cp && !iscntrl(*cp) && !strchr (tspecials, *cp)) + ++cp; + + if (*cp) { + *toks = cp + 1; + *cp = '\0'; + } + else *toks = cp; + } + + return ret; +} + +int find_token (pool *p, const char *line, const char *tok) { + char *ltok; + char *lcopy; + + if (!line) return 0; + + lcopy = pstrdup (p, line); + while ((ltok = next_token (&lcopy))) + if (!strcasecmp (ltok, tok)) + return 1; + + return 0; +} + +int 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) && !(isspace(line[lidx-1]) || line[lidx-1] == ','))) + return 0; + + return (strncasecmp(&line[lidx], tok, tlen) == 0); +} + +char *escape_shell_cmd(pool *p, const char *s) { + register int x,y,l; + char *cmd; + + l=strlen(s); + cmd = palloc (p, 2 * l + 1); /* Be safe */ + strcpy (cmd, s); + + for(x=0;cmd[x];x++) { + +#ifdef __EMX__ + /* Don't allow '&' in parameters under OS/2. */ + /* This can be used to send commands to the shell. */ + if (cmd[x] == '&') { + cmd[x] = ' '; + } +#endif + + if(ind("&;`'\"|*?~<>^()[]{}$\\\n",cmd[x]) != -1){ + for(y=l+1;y>x;y--) + cmd[y] = cmd[y-1]; + l++; /* length has been increased */ + cmd[x] = '\\'; + x++; /* skip the character */ + } + } + + return cmd; +} + +void plustospace(char *str) { + register int x; + + for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' '; +} + +void spacetoplus(char *str) { + register int x; + + for(x=0;str[x];x++) if(str[x] == ' ') str[x] = '+'; +} + +static char x2c(const char *what) { + register char digit; + + 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')); + 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 + */ +int +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 (!isxdigit(url[y+1]) || !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; +} + +char *construct_server(pool *p, const char *hostname, unsigned port) { + char portnum[22]; + /* Long enough, even if port > 16 bits for some reason */ + + if (port == DEFAULT_PORT) + return (char *)hostname; + else { + ap_snprintf (portnum, sizeof(portnum), "%u", port); + return pstrcat (p, hostname, ":", portnum, NULL); + } +} + +char *construct_url(pool *p, const char *uri, const server_rec *s) { + return pstrcat (p, "http://", + construct_server(p, s->server_hostname, s->port), + uri, NULL); +} + +#define c2x(what,where) sprintf(where,"%%%02x",(unsigned char)what) + +/* +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 "./"). +*/ + +char *escape_path_segment(pool *p, const char *segment) { + register int x,y; + char *copy = palloc (p, 3 * strlen (segment) + 1); + + for(x=0,y=0; segment[x]; x++,y++) { + char c=segment[x]; + if((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c >'9') + && ind("$-_.+!*'(),:@&=~",c) == -1) + { + c2x(c,©[y]); + y+=2; + } + else + copy[y]=c; + } + copy[y] = '\0'; + return copy; +} + +char *os_escape_path(pool *p,const char *path,int partial) { + char *copy=palloc(p,3*strlen(path)+3); + char *s=copy; + + if(!partial) + { + int colon=ind(path,':'); + int slash=ind(path,'/'); + + if(colon >= 0 && (colon < slash || slash < 0)) + { + *s++='.'; + *s++='/'; + } + } + for( ; *path ; ++path) + { + char c=*path; + if((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c >'9') + && ind("$-_.+!*'(),:@&=/~",c) == -1) + { + c2x(c,s); + s+=3; + } + else + *s++=c; + } + *s='\0'; + return copy; +} + +/* escape_uri is now a macro for os_escape_path */ + +char *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 pstrdup(p, s); + x = 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; +} + +int 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)); +} + +char *make_full_path(pool *a, const char *src1, const char *src2) { + register int x; + + x = strlen(src1); + if (x == 0) return pstrcat (a, "/", src2, NULL); + + if (src1[x - 1] != '/') return pstrcat (a, src1, "/", src2, NULL); + else return pstrcat (a, src1, src2, NULL); +} + +/* + * Check for an absoluteURI syntax (see section 3.2 in RFC2068). + */ +int is_url(const char *u) { + register int x; + + for (x = 0; u[x] != ':'; x++) { + if ((! u[x]) || + ((! isalpha(u[x])) && (! isdigit(u[x])) && + (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) { + return 0; + } + } + + return (x ? 1 : 0); /* If the first character is ':', it's broken, too */ +} + +int can_exec(const struct stat *finfo) { +#ifdef MULTIPLE_GROUPS + int cnt; +#endif +#ifdef __EMX__ + /* OS/2 dosen't have Users and Groups */ + return 1; +#else + if(user_id == finfo->st_uid) + if(finfo->st_mode & S_IXUSR) + return 1; + if(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 *dup; + + if(!(dup = (char *)malloc (strlen (str) + 1))) + return NULL; + dup = strcpy (dup, str); + + return dup; +} +#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 = tolower(*p) - 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 = tolower(*p) - tolower(*q); + if (diff) return diff; + } + /*NOTREACHED*/ +} +#endif + + + +#ifdef NEED_INITGROUPS +int initgroups(const char *name, gid_t basegid) +{ +#if defined(QNX) || defined(MPE) +/* QNX and MPE 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 */ +/* httpd does not use the options so this doesn't implement them */ +int waitpid(pid_t pid, int *statusp, int options) +{ + int tmp_pid; + if ( kill ( pid,0 ) == -1) { + errno=ECHILD; + return -1; + } + while ((( tmp_pid = wait(statusp)) != pid) && ( tmp_pid != -1 )); + return tmp_pid; +} +#endif + +int ind(const char *s, char c) { + register int x; + + for(x=0;s[x];x++) + if(s[x] == c) return x; + + return -1; +} + +int 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; +} + +void str_tolower(char *str) { + while(*str) { + *str = tolower(*str); + ++str; + } +} + +uid_t uname2id(const char *name) { + struct passwd *ent; + + if(name[0] == '#') + return(atoi(&name[1])); + + if(!(ent = getpwnam(name))) { + fprintf(stderr,"httpd: bad user name %s\n",name); + exit(1); + } + return(ent->pw_uid); +} + +gid_t gname2id(const char *name) { + struct group *ent; + + if(name[0] == '#') + return(atoi(&name[1])); + + if(!(ent = getgrnam(name))) { + fprintf(stderr,"httpd: bad group name %s\n",name); + exit(1); + } + return(ent->gr_gid); +} + +#if 0 +int get_portnum(int sd) { + struct sockaddr addr; + int len; + + len = sizeof(struct sockaddr); + if(getsockname(sd,&addr,&len) < 0) + return -1; + return ntohs(((struct sockaddr_in *)&addr)->sin_port); +} + +struct in_addr get_local_addr(int sd) { + struct sockaddr addr; + int len; + + len = sizeof(struct sockaddr); + if(getsockname(sd,&addr,&len) < 0) { + perror ("getsockname"); + fprintf (stderr, "Can't get local host address!\n"); + exit(1); + } + + return ((struct sockaddr_in *)&addr)->sin_addr; +} +#endif + +/* + * Parses a host of the form <address>[:port] + * :port is permitted if 'port' is not NULL + */ +unsigned long get_virthost_addr (const 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); + } + +#ifdef DGUX + my_addr = inet_network(w); +#else + my_addr = inet_addr(w); +#endif + 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(ind(p->h_name,'.') == -1) { + for(x=0;p->h_aliases[x];++x) { + if((ind(p->h_aliases[x],'.') != -1) && + (!strncasecmp(p->h_aliases[x],p->h_name,strlen(p->h_name)))) + return pstrdup(a, p->h_aliases[x]); + } + return NULL; + } + return pstrdup(a, (void *)p->h_name); +} + +char *get_local_host(pool *a) +{ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + char str[MAXHOSTNAMELEN+1]; + char *server_hostname; + struct hostent *p; + + if( gethostname( str, sizeof( str ) - 1 ) != 0 ) { + perror( "Unable to gethostname" ); + exit(1); + } + str[MAXHOSTNAMELEN] = '\0'; + if((!(p=gethostbyname(str))) || (!(server_hostname = find_fqdn(a, p)))) { + fprintf(stderr,"httpd: cannot determine local host name.\n"); + fprintf(stderr,"Use ServerName to set it manually.\n"); + exit(1); + } + + return server_hostname; +} + +/* aaaack but it's fast and const should make it shared text page. */ +const int pr2six[256]={ + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63, + 52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9, + 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27, + 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64 +}; + +char *uudecode(pool *p, const char *bufcoded) { + int nbytesdecoded; + register unsigned char *bufin; + register char *bufplain; + register unsigned char *bufout; + register int nprbytes; + + /* Strip leading whitespace. */ + + while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++; + + /* Figure out how many characters are in the input buffer. + * Allocate this many from the per-transaction pool for the result. + */ + bufin = (unsigned char *)bufcoded; + while(pr2six[*(bufin++)] <= 63); + nprbytes = (char *)bufin - bufcoded - 1; + nbytesdecoded = ((nprbytes+3)/4) * 3; + + bufplain = palloc(p, nbytesdecoded + 1); + bufout = (unsigned char *)bufplain; + + bufin = (unsigned char *)bufcoded; + + while (nprbytes > 0) { + *(bufout++) = + (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4); + *(bufout++) = + (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2); + *(bufout++) = + (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]); + bufin += 4; + nprbytes -= 4; + } + + if(nprbytes & 03) { + if(pr2six[bufin[-2]] > 63) + nbytesdecoded -= 2; + else + nbytesdecoded -= 1; + } + bufplain[nbytesdecoded] = '\0'; + return bufplain; +} + +#ifdef __EMX__ +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); +}; +#endif + + +#ifdef NEED_STRERROR +char * +strerror (int err) { + + char *p; + extern char *const sys_errlist[]; + + p = sys_errlist[err]; + return (p); +} +#endif + + +int ap_slack (int fd, int line) +{ +#if !defined(F_DUPFD) || defined(NO_SLACK) + return fd; +#else + int new_fd; + +#ifdef HIGH_SLACK_LINE + if (line == AP_SLACK_HIGH) { + new_fd = fcntl (fd, F_DUPFD, HIGH_SLACK_LINE); + if (new_fd != -1) { + close (fd); + return new_fd; + } + } +#endif + /* otherwise just assume line == AP_SLACK_LOW */ + new_fd = fcntl (fd, F_DUPFD, LOW_SLACK_LINE); + if (new_fd == -1) { + return fd; + } + close (fd); + return new_fd; +#endif +} diff --git a/usr.sbin/httpd/src/util_date.c b/usr.sbin/httpd/src/util_date.c new file mode 100644 index 00000000000..e544a0fe1cc --- /dev/null +++ b/usr.sbin/httpd/src/util_date.c @@ -0,0 +1,296 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* + * 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 "util_date.h" +#include <ctype.h> +#include <string.h> + +/* + * 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 + * <x> - exact match for any other character + */ +int 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 (!isupper(d)) return 0; + break; + case '$': if (!islower(d)) return 0; + break; + case '#': if (!isdigit(d)) return 0; + break; + case '&': if (!isxdigit(d)) return 0; + break; + case '~': if ((d != ' ') && !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(). + */ +time_t tm2sec(const struct tm *t) +{ + int year; + time_t days; + 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 <andrew@vancouver-webpages.com>, 29 Jul 96 + * but many changes since then. + * + */ +time_t parseHTTPdate(const char *date) +{ + struct tm ds; + int mint, mon; + const char *monstr, *timstr; + 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 && 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 (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 (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 (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 tm2sec(&ds); +} + diff --git a/usr.sbin/httpd/src/util_date.h b/usr.sbin/httpd/src/util_date.h new file mode 100644 index 00000000000..edfd34ad431 --- /dev/null +++ b/usr.sbin/httpd/src/util_date.h @@ -0,0 +1,63 @@ +/* ==================================================================== + * Copyright (c) 1996,1997 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. 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://www.apache.org/>. + * + */ + +/* + * util_date.h: prototypes for date parsing utility routines + */ + +#include <time.h> + +#define BAD_DATE (time_t)0 + +int checkmask (const char *data, const char *mask); +time_t tm2sec (const struct tm *t); +time_t parseHTTPdate (const char *date); diff --git a/usr.sbin/httpd/src/util_md5.c b/usr.sbin/httpd/src/util_md5.c new file mode 100644 index 00000000000..bf5565e21f0 --- /dev/null +++ b/usr.sbin/httpd/src/util_md5.c @@ -0,0 +1,192 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/************************************************************************ + * 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" + +char *md5 (pool *p, unsigned char *string) +{ + MD5_CTX my_md5; + unsigned char hash[16]; + char *r, result[33]; + int i; + + /* + * Take the MD5 hash of the string argument. + */ + + MD5Init(&my_md5); + MD5Update(&my_md5, string, strlen((const char *)string)); + MD5Final(hash, &my_md5); + + for (i=0, r=result; i<16; i++, r+=2) + sprintf(r, "%02x", hash[i]); + *r = '\0'; + + return pstrdup(p, result); +} + +/* 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+/"; + +char *md5contextTo64(pool *a, MD5_CTX *context) +{ + unsigned char digest[18]; + char *encodedDigest; + int i; + char *p; + + encodedDigest = (char *)pcalloc(a, 25 * sizeof(char)); + + 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; +} + +char *md5digest(pool *p, FILE *infile) +{ + MD5_CTX context; + unsigned char buf[1000]; + long length = 0; + int nbytes; + + MD5Init(&context); + while ((nbytes = fread(buf, 1, sizeof(buf), infile))) { + length += nbytes; + MD5Update(&context, buf, nbytes); + } + rewind(infile); + return md5contextTo64(p, &context); +} + diff --git a/usr.sbin/httpd/src/util_md5.h b/usr.sbin/httpd/src/util_md5.h new file mode 100644 index 00000000000..63e2c37cd2b --- /dev/null +++ b/usr.sbin/httpd/src/util_md5.h @@ -0,0 +1,58 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +#include "md5.h" + +char *md5(pool *a, unsigned char *string); +char *md5contextTo64(pool *p, MD5_CTX *context); +char *md5digest(pool *p, FILE *infile); + diff --git a/usr.sbin/httpd/src/util_script.c b/usr.sbin/httpd/src/util_script.c new file mode 100644 index 00000000000..3fcd3a66cd2 --- /dev/null +++ b/usr.sbin/httpd/src/util_script.c @@ -0,0 +1,641 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +#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 <assert.h> + +/* + * 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. + */ +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 **)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 = getword_nulls(p, &args, '+'); + unescape_url(w); + av[idx++] = escape_shell_cmd(p, w); + } + av[idx] = NULL; + return av; +} + + +static char *http2env(pool *a, char *w) +{ + char *res = pstrcat (a, "HTTP_", w, NULL); + char *cp = res; + + while (*++cp) + if (*cp == '-') *cp = '_'; + else *cp = toupper(*cp); + + return res; +} + +char **create_environment(pool *p, table *t) +{ + array_header *env_arr = table_elts (t); + table_entry *elts = (table_entry *)env_arr->elts; + char **env = (char **)palloc (p, (env_arr->nelts + 2) *sizeof (char *)); + int i, j; + char *tz; + + j = 0; + tz = getenv("TZ"); + if (tz!= NULL) env[j++] = pstrcat(p, "TZ=", tz, NULL); + for (i = 0; i < env_arr->nelts; ++i) { + if (!elts[i].key) continue; + env[j++] = pstrcat (p, elts[i].key, "=", elts[i].val, NULL); + } + + env[j] = NULL; + return env; +} + +void add_common_vars(request_rec *r) +{ + table *e = r->subprocess_env; + server_rec *s = r->server; + conn_rec *c = r->connection; + const char *rem_logname; + + char port[40],*env_path; + + array_header *hdrs_arr = table_elts (r->headers_in); + table_entry *hdrs = (table_entry *)hdrs_arr->elts; + int i; + + /* 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")) + table_set (e, "CONTENT_TYPE", hdrs[i].val); + else if (!strcasecmp (hdrs[i].key, "Content-length")) + table_set (e, "CONTENT_LENGTH", hdrs[i].val); + else if (!strcasecmp (hdrs[i].key, "Authorization")) + continue; + else + table_set (e, http2env (r->pool, hdrs[i].key), hdrs[i].val); + } + + ap_snprintf(port, sizeof(port), "%u", s->port); + + if(!(env_path = getenv("PATH"))) + env_path=DEFAULT_PATH; + + table_set (e, "PATH", env_path); + table_set (e, "SERVER_SOFTWARE", SERVER_VERSION); + table_set (e, "SERVER_NAME", s->server_hostname); + table_set (e, "SERVER_PORT", port); + table_set (e, "REMOTE_HOST", + get_remote_host(c, r->per_dir_config, REMOTE_NAME)); + table_set (e, "REMOTE_ADDR", c->remote_ip); + table_set (e, "DOCUMENT_ROOT", document_root(r)); /* Apache */ + table_set (e, "SERVER_ADMIN", s->server_admin); /* Apache */ + table_set (e, "SCRIPT_FILENAME", r->filename); /* Apache */ + + ap_snprintf(port, sizeof(port), "%d", ntohs(c->remote_addr.sin_port)); + table_set (e, "REMOTE_PORT", port); /* Apache */ + + if (c->user) table_set(e, "REMOTE_USER", c->user); + if (c->auth_type) table_set(e, "AUTH_TYPE", c->auth_type); + rem_logname = get_remote_logname(r); + if (rem_logname) table_set(e, "REMOTE_IDENT", rem_logname); + + /* Apache custom error responses. If we have redirected set two new vars */ + + if (r->prev) { + if (r->prev->args) table_set(e,"REDIRECT_QUERY_STRING", r->prev->args); + if (r->prev->uri) table_set (e, "REDIRECT_URL", r->prev->uri); + } +} + +/* 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. + */ + +int find_path_info (char *uri, 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 *)pcalloc(r->pool, 1); + + first = r->the_request; /* use the request-line */ + + while (*first && !isspace(*first)) ++first; /* skip over the method */ + while (isspace(*first)) ++first; /* and the space(s) */ + + last = first; + while (*last && !isspace(*last)) ++last; /* end at next whitespace */ + + return pstrndup(r->pool, first, last - first); +} + +void add_cgi_vars(request_rec *r) +{ + table *e = r->subprocess_env; + + table_set (e, "GATEWAY_INTERFACE","CGI/1.1"); + table_set (e, "SERVER_PROTOCOL", r->protocol); + table_set (e, "REQUEST_METHOD", r->method); + table_set (e, "QUERY_STRING", r->args ? r->args : ""); + table_set (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")) { + table_set (e, "SCRIPT_NAME", r->uri); + if (r->path_info && *r->path_info) + table_set (e, "PATH_INFO", r->path_info); + } else if (!r->path_info || !*r->path_info) { + table_set (e, "SCRIPT_NAME", r->uri); + } else { + int path_info_start = find_path_info (r->uri, r->path_info); + + table_set (e, "SCRIPT_NAME", pstrndup(r->pool, r->uri, + path_info_start)); + + table_set (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 = sub_req_lookup_uri( + escape_uri(r->pool, r->path_info), r); + + /* Don't bother destroying pa_req --- it's only created in + * child processes which are about to jettison their address + * space anyway. BTW, we concatenate filename and path_info + * from the sub_request to be compatible in case the PATH_INFO + * is pointing to an object which doesn't exist. + */ + + if (pa_req->filename) + table_set (e, "PATH_TRANSLATED", + pstrcat (r->pool, pa_req->filename, pa_req->path_info, + NULL)); + } +} + +int scan_script_header_err(request_rec *r, FILE *f, char *buffer) +{ + char x[MAX_STRING_LEN]; + char *w, *l; + int p; + + if (buffer) *buffer = '\0'; + w = buffer ? buffer : x; + + hard_timeout ("read script header", r); + + while(1) { + + if (fgets(w, MAX_STRING_LEN-1, f) == NULL) { + kill_timeout (r); + log_reason ("Premature end of script headers", r->filename, r); + return 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(w[0] == '\0') { + kill_timeout (r); + return OK; + } + + /* if we see a bogus header don't ignore it. Shout and scream */ + + 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 (fgets(w, MAX_STRING_LEN-1, f) != NULL) + continue; + + kill_timeout (r); + log_reason (malformed, r->filename, r); + return SERVER_ERROR; + } + + *l++ = '\0'; + while (*l && isspace (*l)) ++l; + + if(!strcasecmp(w,"Content-type")) { + + /* Nuke trailing whitespace */ + + char *endp = l + strlen(l) - 1; + while (endp > l && isspace(*endp)) *endp-- = '\0'; + + r->content_type = pstrdup (r->pool, l); + } + else if(!strcasecmp(w,"Status")) { + sscanf(l, "%d", &r->status); + r->status_line = pstrdup(r->pool, l); + } + else if(!strcasecmp(w,"Location")) { + table_set (r->headers_out, w, l); + } + else if(!strcasecmp(w,"Content-Length")) { + table_set (r->headers_out, w, l); + } + else if(!strcasecmp(w,"Transfer-Encoding")) { + table_set (r->headers_out, w, l); + } + +/* 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. + */ + else if(!strcasecmp(w, "Set-Cookie")) { + table_add(r->err_headers_out, w, l); + } + else { + table_merge (r->err_headers_out, w, l); + } + } +} + +void send_size(size_t size, request_rec *r) { + char ss[20]; + + if(size == -1) + strcpy(ss, " -"); + else if(!size) + strcpy(ss, " 0k"); + else if(size < 1024) + strcpy(ss, " 1k"); + else if(size < 1048576) + ap_snprintf(ss, sizeof(ss), "%4dk", (size + 512) / 1024); + else if(size < 103809024) + ap_snprintf(ss, sizeof(ss), "%4.1fM", size / 1048576.0); + else + ap_snprintf(ss, sizeof(ss), "%4dM", (size + 524288) / 1048576); + rputs(ss, r); +} + +#ifdef __EMX__ +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 **)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 = getword(p, &args, '+'); + unescape_url(w); + av[x] = escape_shell_cmd(p, w); + } + av[n] = NULL; + return av; +} +#endif + + +void call_exec (request_rec *r, char *argv0, char **env, int shellcmd) +{ +#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ + defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) + + core_dir_config *conf = + (core_dir_config *)get_module_config(r->per_dir_config, &core_module); + +#endif + + /* 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. + */ + r->server->error_log = stderr; + +#ifdef RLIMIT_CPU + if (conf->limit_cpu != NULL) + if ((setrlimit (RLIMIT_CPU, conf->limit_cpu)) != 0) + log_unixerr("setrlimit", NULL, "failed to set CPU usage limit", + r->server); +#endif +#ifdef RLIMIT_NPROC + if (conf->limit_nproc != NULL) + if ((setrlimit (RLIMIT_NPROC, conf->limit_nproc)) != 0) + log_unixerr("setrlimit", NULL, "failed to set process limit", + r->server); +#endif +#ifdef RLIMIT_DATA + if (conf->limit_mem != NULL) + if ((setrlimit (RLIMIT_DATA, conf->limit_mem)) != 0) + log_unixerr("setrlimit", NULL, "failed to set memory usage limit", + r->server); +#endif +#ifdef RLIMIT_VMEM + if (conf->limit_mem != NULL) + if ((setrlimit (RLIMIT_VMEM, conf->limit_mem)) != 0) + log_unixerr("setrlimit", NULL, "failed to set memory usage limit", + r->server); +#endif + +#ifdef __EMX__ + { + /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */ + int is_script; + char interpreter[2048]; /* hope this is large enough for the interpreter path */ + FILE * program; + program = fopen (r->filename, "r"); + if (!program) { + char err_string[HUGE_STRING_LEN]; + ap_snprintf(err_string, sizeof(err_string), + "open of %s failed, reason: fopen: %s (errno = %d)\n", + r->filename, strerror(errno), errno); + + /* write(2, err_string, strlen(err_string)); */ + /* exit(0); */ + log_unixerr("fopen", NULL, err_string, r->server); + return; + } + fgets (interpreter, 2048, program); + fclose (program); + if (!strncmp (interpreter, "#!", 2)) { + is_script = 1; + interpreter[strlen(interpreter)-1] = '\0'; + } else { + is_script = 0; + } + + if ((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) { + int emxloop; + char *emxtemp; + + /* For OS/2 place the variables in the current + enviornment then it will be inherited. This way + the program will also get all of OS/2's other SETs. */ + for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++) + putenv(emxtemp); + + /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */ + if (is_script) { + /* here's the stuff to run the interpreter */ + execl (interpreter+2, 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); + execl("CMD.EXE", "CMD.EXE", "/C", r->filename, NULL); + } + else { + execl(r->filename, argv0, NULL); + } + } + else { + int emxloop; + char *emxtemp; + + /* For OS/2 place the variables in the current + environment so that they will be inherited. This way + the program will also get all of OS/2's other SETs. */ + for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++) + putenv(emxtemp); + + if (strstr(strupr(r->filename), ".CMD") > 0) { + /* Special case to allow use of REXX commands as scripts. */ + os2pathname(r->filename); + execv("CMD.EXE", create_argv_cmd(r->pool, argv0, r->args, r->filename)); + } + else + execv(r->filename, + create_argv(r->pool, NULL, NULL, NULL, argv0, r->args)); + } + } +#else + if ( suexec_enabled && + ((r->server->server_uid != user_id) || + (r->server->server_gid != 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 = pstrdup(r->pool, r->uri + 2); + int pos = ind(username, '/'); + + if (pos >= 0) username[pos] = '\0'; + + if ((pw = getpwnam(username)) == NULL) { + log_unixerr("getpwnam",username,"invalid username",r->server); + return; + } + execuser = pstrcat(r->pool, "~", pw->pw_name, NULL); + user_gid = pw->pw_gid; + + if ((gr = getgrgid(user_gid)) == NULL) { + if ((grpname = palloc (r->pool, 16)) == NULL) + return; + else + ap_snprintf(grpname, 16, "%d", user_gid); + } + else + grpname = gr->gr_name; + } + else { + if ((pw = getpwuid (r->server->server_uid)) == NULL) { + log_unixerr("getpwuid", NULL, "invalid userid", r->server); + return; + } + execuser = pstrdup(r->pool, pw->pw_name); + + if ((gr = getgrgid (r->server->server_gid)) == NULL) { + log_unixerr("getgrgid", NULL, "invalid groupid", r->server); + return; + } + grpname = gr->gr_name; + } + + if (shellcmd) + execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, NULL, env); + + else if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) + 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]) || (ind(r->args,'=') >= 0)) + execle(r->filename, argv0, NULL, env); + + else + execve(r->filename, + create_argv(r->pool, NULL, NULL, NULL, argv0, r->args), + env); + } +#endif +} diff --git a/usr.sbin/httpd/src/util_script.h b/usr.sbin/httpd/src/util_script.h new file mode 100644 index 00000000000..28258767781 --- /dev/null +++ b/usr.sbin/httpd/src/util_script.h @@ -0,0 +1,69 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +#ifndef APACHE_ARG_MAX +#ifdef _POSIX_ARG_MAX +#define APACHE_ARG_MAX _POSIX_ARG_MAX +#else +#define APACHE_ARG_MAX 512 +#endif +#endif + +char **create_environment(pool *p, table *t); +int find_path_info(char *uri, char *path_info); +void add_cgi_vars(request_rec *r); +void add_common_vars(request_rec *r); +#define scan_script_header(a1,a2) scan_script_header_err(a1,a2,NULL) +int scan_script_header_err(request_rec *r, FILE *f, char *buffer); +void send_size(size_t size, request_rec *r); +void call_exec (request_rec *r, char *argv0, char **env, int shellcmd); + diff --git a/usr.sbin/httpd/src/util_snprintf.c b/usr.sbin/httpd/src/util_snprintf.c new file mode 100644 index 00000000000..d37c634f753 --- /dev/null +++ b/usr.sbin/httpd/src/util_snprintf.c @@ -0,0 +1,949 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + * This code is based on, and used with the permission of, the + * SIO stdio-replacement strx_* functions by Panos Tsirigotis + * <panos@alumni.cs.colorado.edu> for xinetd. + */ + +#include "conf.h" + +#ifndef HAVE_SNPRINTF + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <math.h> + +#ifdef HAVE_CVT + +# define ap_ecvt ecvt +# define ap_fcvt fcvt +# define ap_gcvt gcvt + +#else + +/* + * cvt.c - IEEE floating point formatting routines for FreeBSD + * from GNU libc-4.6.27 + */ + +/* + * ap_ecvt converts to decimal + * the number of digits is specified by ndigit + * decpt is set to the position of the decimal point + * sign is set to 0 for positive, 1 for negative + */ + +#define NDIG 80 + +static char * + ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag) +{ + register int r2; + double fi, fj; + register char *p, *p1; + static char buf[NDIG]; + + if (ndigits >= NDIG - 1) + ndigits = NDIG - 2; + r2 = 0; + *sign = 0; + p = &buf[0]; + if (arg < 0) { + *sign = 1; + arg = -arg; + } + arg = modf(arg, &fi); + p1 = &buf[NDIG]; + /* + * Do integer part + */ + if (fi != 0) { + p1 = &buf[NDIG]; + while (fi != 0) { + fj = modf(fi / 10, &fi); + *--p1 = (int) ((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < &buf[NDIG]) + *p++ = *p1++; + } + else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + p1 = &buf[ndigits]; + if (eflag == 0) + p1 += r2; + *decpt = r2; + if (p1 < &buf[0]) { + buf[0] = '\0'; + return (buf); + } + while (p <= p1 && p < &buf[NDIG]) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int) fj + '0'; + } + if (p1 >= &buf[NDIG]) { + buf[NDIG - 1] = '\0'; + return (buf); + } + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > buf) + ++ * --p1; + else { + *p1 = '1'; + (*decpt)++; + if (eflag == 0) { + if (p > buf) + *p = '0'; + p++; + } + } + } + *p = '\0'; + return (buf); +} + +static char * + ap_ecvt(double arg, int ndigits, int *decpt, int *sign) +{ + return (ap_cvt(arg, ndigits, decpt, sign, 1)); +} + +static char * + ap_fcvt(double arg, int ndigits, int *decpt, int *sign) +{ + return (ap_cvt(arg, ndigits, decpt, sign, 0)); +} + +/* + * ap_gcvt - Floating output conversion to + * minimal length string + */ + +static char * + ap_gcvt(double number, int ndigit, char *buf) +{ + int sign, decpt; + register char *p1, *p2; + register i; + + p1 = ap_ecvt(number, ndigit, &decpt, &sign); + p2 = buf; + if (sign) + *p2++ = '-'; + for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) + ndigit--; + if ((decpt >= 0 && decpt - ndigit > 4) + || (decpt < 0 && decpt < -3)) { /* use E-style */ + decpt--; + *p2++ = *p1++; + *p2++ = '.'; + for (i = 1; i < ndigit; i++) + *p2++ = *p1++; + *p2++ = 'e'; + if (decpt < 0) { + decpt = -decpt; + *p2++ = '-'; + } + else + *p2++ = '+'; + if (decpt / 100 > 0) + *p2++ = decpt / 100 + '0'; + if (decpt / 10 > 0) + *p2++ = (decpt % 100) / 10 + '0'; + *p2++ = decpt % 10 + '0'; + } + else { + if (decpt <= 0) { + if (*p1 != '0') + *p2++ = '.'; + while (decpt < 0) { + decpt++; + *p2++ = '0'; + } + } + for (i = 1; i <= ndigit; i++) { + *p2++ = *p1++; + if (i == decpt) + *p2++ = '.'; + } + if (ndigit < decpt) { + while (ndigit++ < decpt) + *p2++ = '0'; + *p2++ = '.'; + } + } + if (p2[-1] == '.') + p2--; + *p2 = '\0'; + return (buf); +} + +#endif /* HAVE_CVT */ + +typedef enum { + NO = 0, YES = 1 +} boolean_e; + +#define FALSE 0 +#define TRUE 1 +#define NUL '\0' +#define INT_NULL ((int *)0) +#define WIDE_INT long + +typedef WIDE_INT wide_int; +typedef unsigned WIDE_INT u_wide_int; +typedef int bool_int; + +#define S_NULL "(null)" +#define S_NULL_LEN 6 + +#define FLOAT_DIGITS 6 +#define EXPONENT_LENGTH 10 + +/* + * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions + * + * XXX: this is a magic number; do not decrease it + */ +#define NUM_BUF_SIZE 512 + + +/* + * Descriptor for buffer area + */ +struct buf_area { + char *buf_end; + char *nextb; /* pointer to next byte to read/write */ +}; + +typedef struct buf_area buffy; + +/* + * The INS_CHAR macro inserts a character in the buffer and writes + * the buffer back to disk if necessary + * It uses the char pointers sp and bep: + * sp points to the next available character in the buffer + * bep points to the end-of-buffer+1 + * While using this macro, note that the nextb pointer is NOT updated. + * + * NOTE: Evaluation of the c argument should not have any side-effects + */ +#define INS_CHAR( c, sp, bep, cc ) \ + { \ + if ( sp < bep ) \ + { \ + *sp++ = c ; \ + cc++ ; \ + } \ + } + +#define NUM( c ) ( c - '0' ) + +#define STR_TO_DEC( str, num ) \ + num = NUM( *str++ ) ; \ + while ( isdigit( *str ) ) \ + { \ + num *= 10 ; \ + num += NUM( *str++ ) ; \ + } + +/* + * This macro does zero padding so that the precision + * requirement is satisfied. The padding is done by + * adding '0's to the left of the string that is going + * to be printed. + */ +#define FIX_PRECISION( adjust, precision, s, s_len ) \ + if ( adjust ) \ + while ( s_len < precision ) \ + { \ + *--s = '0' ; \ + s_len++ ; \ + } + +/* + * Macro that does padding. The padding is done by printing + * the character ch. + */ +#define PAD( width, len, ch ) do \ + { \ + INS_CHAR( ch, sp, bep, cc ) ; \ + width-- ; \ + } \ + while ( width > len ) + +/* + * Prefix the character ch to the string str + * Increase length + * Set the has_prefix flag + */ +#define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES + + +/* + * Convert num to its decimal format. + * Return value: + * - a pointer to a string containing the number (no sign) + * - len contains the length of the string + * - is_negative is set to TRUE or FALSE depending on the sign + * of the number (always set to FALSE if is_unsigned is TRUE) + * + * The caller provides a buffer for the string: that is the buf_end argument + * which is a pointer to the END of the buffer + 1 (i.e. if the buffer + * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) + */ +static char * + conv_10(register wide_int num, register bool_int is_unsigned, + register bool_int * is_negative, char *buf_end, register int *len) +{ + register char *p = buf_end; + register u_wide_int magnitude; + + if (is_unsigned) { + magnitude = (u_wide_int) num; + *is_negative = FALSE; + } + else { + *is_negative = (num < 0); + + /* + * On a 2's complement machine, negating the most negative integer + * results in a number that cannot be represented as a signed integer. + * Here is what we do to obtain the number's magnitude: + * a. add 1 to the number + * b. negate it (becomes positive) + * c. convert it to unsigned + * d. add 1 + */ + if (*is_negative) { + wide_int t = num + 1; + + magnitude = ((u_wide_int) - t) + 1; + } + else + magnitude = (u_wide_int) num; + } + + /* + * We use a do-while loop so that we write at least 1 digit + */ + do { + register u_wide_int new_magnitude = magnitude / 10; + + *--p = magnitude - new_magnitude * 10 + '0'; + magnitude = new_magnitude; + } + while (magnitude); + + *len = buf_end - p; + return (p); +} + + + +/* + * Convert a floating point number to a string formats 'f', 'e' or 'E'. + * The result is placed in buf, and len denotes the length of the string + * The sign is returned in the is_negative argument (and is not placed + * in buf). + */ +static char * + conv_fp(register char format, register double num, +boolean_e add_dp, int precision, bool_int * is_negative, char *buf, int *len) +{ + register char *s = buf; + register char *p; + int decimal_point; + + if (format == 'f') + p = ap_fcvt(num, precision, &decimal_point, is_negative); + else /* either e or E format */ + p = ap_ecvt(num, precision + 1, &decimal_point, is_negative); + + /* + * Check for Infinity and NaN + */ + if (isalpha(*p)) { + *len = strlen(strcpy(buf, p)); + *is_negative = FALSE; + return (buf); + } + + if (format == 'f') + if (decimal_point <= 0) { + *s++ = '0'; + if (precision > 0) { + *s++ = '.'; + while (decimal_point++ < 0) + *s++ = '0'; + } + else if (add_dp) + *s++ = '.'; + } + else { + while (decimal_point-- > 0) + *s++ = *p++; + if (precision > 0 || add_dp) + *s++ = '.'; + } + else { + *s++ = *p++; + if (precision > 0 || add_dp) + *s++ = '.'; + } + + /* + * copy the rest of p, the NUL is NOT copied + */ + while (*p) + *s++ = *p++; + + if (format != 'f') { + char temp[EXPONENT_LENGTH]; /* for exponent conversion */ + int t_len; + bool_int exponent_is_negative; + + *s++ = format; /* either e or E */ + decimal_point--; + if (decimal_point != 0) { + p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, + &temp[EXPONENT_LENGTH], &t_len); + *s++ = exponent_is_negative ? '-' : '+'; + + /* + * Make sure the exponent has at least 2 digits + */ + if (t_len == 1) + *s++ = '0'; + while (t_len--) + *s++ = *p++; + } + else { + *s++ = '+'; + *s++ = '0'; + *s++ = '0'; + } + } + + *len = s - buf; + return (buf); +} + + +/* + * Convert num to a base X number where X is a power of 2. nbits determines X. + * For example, if nbits is 3, we do base 8 conversion + * Return value: + * a pointer to a string containing the number + * + * The caller provides a buffer for the string: that is the buf_end argument + * which is a pointer to the END of the buffer + 1 (i.e. if the buffer + * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) + */ +static char * + conv_p2(register u_wide_int num, register int nbits, + char format, char *buf_end, register int *len) +{ + register int mask = (1 << nbits) - 1; + register char *p = buf_end; + static char low_digits[] = "0123456789abcdef"; + static char upper_digits[] = "0123456789ABCDEF"; + register char *digits = (format == 'X') ? upper_digits : low_digits; + + do { + *--p = digits[num & mask]; + num >>= nbits; + } + while (num); + + *len = buf_end - p; + return (p); +} + + +/* + * Do format conversion placing the output in buffer + */ +static int format_converter(register buffy * odp, const char *fmt, + va_list ap) +{ + register char *sp; + register char *bep; + register int cc = 0; + register int i; + + register char *s = NULL; + char *q; + int s_len; + + register int min_width = 0; + int precision = 0; + enum { + LEFT, RIGHT + } adjust; + char pad_char; + char prefix_char; + + double fp_num; + wide_int i_num = (wide_int) 0; + u_wide_int ui_num; + + char num_buf[NUM_BUF_SIZE]; + char char_buf[2]; /* for printing %% and %<unknown> */ + + /* + * Flag variables + */ + boolean_e is_long; + boolean_e alternate_form; + boolean_e print_sign; + boolean_e print_blank; + boolean_e adjust_precision; + boolean_e adjust_width; + bool_int is_negative; + + sp = odp->nextb; + bep = odp->buf_end; + + while (*fmt) { + if (*fmt != '%') { + INS_CHAR(*fmt, sp, bep, cc); + } + else { + /* + * Default variable settings + */ + adjust = RIGHT; + alternate_form = print_sign = print_blank = NO; + pad_char = ' '; + prefix_char = NUL; + + fmt++; + + /* + * Try to avoid checking for flags, width or precision + */ + if (isascii(*fmt) && !islower(*fmt)) { + /* + * Recognize flags: -, #, BLANK, + + */ + for (;; fmt++) { + if (*fmt == '-') + adjust = LEFT; + else if (*fmt == '+') + print_sign = YES; + else if (*fmt == '#') + alternate_form = YES; + else if (*fmt == ' ') + print_blank = YES; + else if (*fmt == '0') + pad_char = '0'; + else + break; + } + + /* + * Check if a width was specified + */ + if (isdigit(*fmt)) { + STR_TO_DEC(fmt, min_width); + adjust_width = YES; + } + else if (*fmt == '*') { + min_width = va_arg(ap, int); + fmt++; + adjust_width = YES; + if (min_width < 0) { + adjust = LEFT; + min_width = -min_width; + } + } + else + adjust_width = NO; + + /* + * Check if a precision was specified + * + * XXX: an unreasonable amount of precision may be specified + * resulting in overflow of num_buf. Currently we + * ignore this possibility. + */ + if (*fmt == '.') { + adjust_precision = YES; + fmt++; + if (isdigit(*fmt)) { + STR_TO_DEC(fmt, precision); + } + else if (*fmt == '*') { + precision = va_arg(ap, int); + fmt++; + if (precision < 0) + precision = 0; + } + else + precision = 0; + } + else + adjust_precision = NO; + } + else + adjust_precision = adjust_width = NO; + + /* + * Modifier check + */ + if (*fmt == 'l') { + is_long = YES; + fmt++; + } + else + is_long = NO; + + /* + * Argument extraction and printing. + * First we determine the argument type. + * Then, we convert the argument to a string. + * On exit from the switch, s points to the string that + * must be printed, s_len has the length of the string + * The precision requirements, if any, are reflected in s_len. + * + * NOTE: pad_char may be set to '0' because of the 0 flag. + * It is reset to ' ' by non-numeric formats + */ + switch (*fmt) { + case 'u': + if (is_long) + i_num = va_arg(ap, u_wide_int); + else + i_num = (wide_int) va_arg(ap, unsigned int); + /* + * The rest also applies to other integer formats, so fall + * into that case. + */ + case 'd': + case 'i': + /* + * Get the arg if we haven't already. + */ + if ((*fmt) != 'u') { + if (is_long) + i_num = va_arg(ap, wide_int); + else + i_num = (wide_int) va_arg(ap, int); + }; + s = conv_10(i_num, (*fmt) == 'u', &is_negative, + &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + + if (*fmt != 'u') { + if (is_negative) + prefix_char = '-'; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + } + break; + + + case 'o': + if (is_long) + ui_num = va_arg(ap, u_wide_int); + else + ui_num = (u_wide_int) va_arg(ap, unsigned int); + s = conv_p2(ui_num, 3, *fmt, + &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && *s != '0') { + *--s = '0'; + s_len++; + } + break; + + + case 'x': + case 'X': + if (is_long) + ui_num = (u_wide_int) va_arg(ap, u_wide_int); + else + ui_num = (u_wide_int) va_arg(ap, unsigned int); + s = conv_p2(ui_num, 4, *fmt, + &num_buf[NUM_BUF_SIZE], &s_len); + FIX_PRECISION(adjust_precision, precision, s, s_len); + if (alternate_form && i_num != 0) { + *--s = *fmt; /* 'x' or 'X' */ + *--s = '0'; + s_len += 2; + } + break; + + + case 's': + s = va_arg(ap, char *); + if (s != NULL) { + s_len = strlen(s); + if (adjust_precision && precision < s_len) + s_len = precision; + } + else { + s = S_NULL; + s_len = S_NULL_LEN; + } + pad_char = ' '; + break; + + + case 'f': + case 'e': + case 'E': + fp_num = va_arg(ap, double); + + s = conv_fp(*fmt, fp_num, alternate_form, + (adjust_precision == NO) ? FLOAT_DIGITS : precision, + &is_negative, &num_buf[1], &s_len); + if (is_negative) + prefix_char = '-'; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + break; + + + case 'g': + case 'G': + if (adjust_precision == NO) + precision = FLOAT_DIGITS; + else if (precision == 0) + precision = 1; + /* + * * We use &num_buf[ 1 ], so that we have room for the sign + */ + s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1]); + if (*s == '-') + prefix_char = *s++; + else if (print_sign) + prefix_char = '+'; + else if (print_blank) + prefix_char = ' '; + + s_len = strlen(s); + + if (alternate_form && (q = strchr(s, '.')) == NULL) + s[s_len++] = '.'; + if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) + *q = 'E'; + break; + + + case 'c': + char_buf[0] = (char) (va_arg(ap, int)); + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + + case '%': + char_buf[0] = '%'; + s = &char_buf[0]; + s_len = 1; + pad_char = ' '; + break; + + + case 'n': + *(va_arg(ap, int *)) = cc; + break; + + /* + * Always extract the argument as a "char *" pointer. We + * should be using "void *" but there are still machines + * that don't understand it. + * If the pointer size is equal to the size of an unsigned + * integer we convert the pointer to a hex number, otherwise + * we print "%p" to indicate that we don't handle "%p". + */ + case 'p': + ui_num = (u_wide_int) va_arg(ap, char *); + + if (sizeof(char *) <= sizeof(u_wide_int)) + s = conv_p2(ui_num, 4, 'x', + &num_buf[NUM_BUF_SIZE], &s_len); + else { + s = "%p"; + s_len = 2; + } + pad_char = ' '; + break; + + + case NUL: + /* + * The last character of the format string was %. + * We ignore it. + */ + continue; + + + /* + * The default case is for unrecognized %'s. + * We print %<char> to help the user identify what + * option is not understood. + * This is also useful in case the user wants to pass + * the output of format_converter to another function + * that understands some other %<char> (like syslog). + * Note that we can't point s inside fmt because the + * unknown <char> could be preceded by width etc. + */ + default: + char_buf[0] = '%'; + char_buf[1] = *fmt; + s = char_buf; + s_len = 2; + pad_char = ' '; + break; + } + + if (prefix_char != NUL) { + *--s = prefix_char; + s_len++; + } + + if (adjust_width && adjust == RIGHT && min_width > s_len) { + if (pad_char == '0' && prefix_char != NUL) { + INS_CHAR(*s, sp, bep, cc) + s++; + s_len--; + min_width--; + } + PAD(min_width, s_len, pad_char); + } + + /* + * Print the string s. + */ + for (i = s_len; i != 0; i--) { + INS_CHAR(*s, sp, bep, cc); + s++; + } + + if (adjust_width && adjust == LEFT && min_width > s_len) + PAD(min_width, s_len, pad_char); + } + fmt++; + } + odp->nextb = sp; + return (cc); +} + + +/* + * This is the general purpose conversion function. + */ +static void strx_printv(int *ccp, char *buf, size_t len, const char *format, + va_list ap) +{ + buffy od; + int cc; + + /* + * First initialize the descriptor + * Notice that if no length is given, we initialize buf_end to the + * highest possible address. + */ + od.buf_end = len ? &buf[len] : (char *) ~0; + od.nextb = buf; + + /* + * Do the conversion + */ + cc = format_converter(&od, format, ap); + if (len == 0 || od.nextb <= od.buf_end) + *(od.nextb) = '\0'; + if (ccp) + *ccp = cc; +} + + +int ap_snprintf(char *buf, size_t len, const char *format,...) +{ + int cc; + va_list ap; + + va_start(ap, format); + strx_printv(&cc, buf, (len - 1), format, ap); + va_end(ap); + return (cc); +} + + +int ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap) +{ + int cc; + + strx_printv(&cc, buf, (len - 1), format, ap); + return (cc); +} + +#endif /* HAVE_SNPRINTF */ diff --git a/usr.sbin/httpd/support/Makefile b/usr.sbin/httpd/support/Makefile new file mode 100644 index 00000000000..6270d87db1e --- /dev/null +++ b/usr.sbin/httpd/support/Makefile @@ -0,0 +1,52 @@ +############### +# Platform: OpenBSD +# Final Rules: +# Rule WANTHSREGEX=no +############### +CC=gcc +OPTIM=-O2 +CFLAGS1= +INCLUDES1= +LIBS1= +LFLAGS1= +BROKEN_BPRINTF_FLAGS= +REGLIB= +RANLIB=ranlib +SHELL=/bin/sh + +#### End of Configure created section #### +# Apache makefile template (well, suffix). + +# This is combined with the information in the "Configuration" file +# by the configure script to make the actual Makefile. + +CFLAGS=$(OPTIM) $(CFLAGS1) $(EXTRA_CFLAGS) +LIBS=$(EXTRA_LIBS) $(LIBS1) +INCLUDES=-I../src -I../src/regex $(INCLUDES1) $(EXTRA_INCLUDES) +LFLAGS=$(LFLAGS1) $(EXTRA_LFLAGS) + +.c.o: + $(CC) -c $(CFLAGS) $(INCLUDES) $< + +TARGETS=htpasswd htdigest httpd_monitor rotatelogs logresolve + +all: $(TARGETS) + +htpasswd: htpasswd.c + $(CC) $(CFLAGS) htpasswd.c -o htpasswd $(LIBS) + +htdigest: htdigest.c + $(CC) $(CFLAGS) htdigest.c -o htdigest + +httpd_monitor: httpd_monitor.c + $(CC) $(INCLUDES) $(CFLAGS) httpd_monitor.c -o httpd_monitor + +rotatelogs: rotatelogs.c + $(CC) $(INCLUDES) $(CFLAGS) rotatelogs.c -o rotatelogs + +logresolve: logresolve.c + $(CC) $(INCLUDES) $(CFLAGS) logresolve.c -o logresolve $(LIBS) + +clean: + rm -f $(TARGETS) + diff --git a/usr.sbin/httpd/support/Makefile.tmpl b/usr.sbin/httpd/support/Makefile.tmpl new file mode 100644 index 00000000000..84aebc02014 --- /dev/null +++ b/usr.sbin/httpd/support/Makefile.tmpl @@ -0,0 +1,35 @@ +# Apache makefile template (well, suffix). + +# This is combined with the information in the "Configuration" file +# by the configure script to make the actual Makefile. + +CFLAGS=$(OPTIM) $(CFLAGS1) $(EXTRA_CFLAGS) +LIBS=$(EXTRA_LIBS) $(LIBS1) +INCLUDES=-I../src -I../src/regex $(INCLUDES1) $(EXTRA_INCLUDES) +LFLAGS=$(LFLAGS1) $(EXTRA_LFLAGS) + +.c.o: + $(CC) -c $(CFLAGS) $(INCLUDES) $< + +TARGETS=htpasswd htdigest httpd_monitor rotatelogs logresolve + +all: $(TARGETS) + +htpasswd: htpasswd.c + $(CC) $(CFLAGS) htpasswd.c -o htpasswd $(LIBS) + +htdigest: htdigest.c + $(CC) $(CFLAGS) htdigest.c -o htdigest + +httpd_monitor: httpd_monitor.c + $(CC) $(INCLUDES) $(CFLAGS) httpd_monitor.c -o httpd_monitor + +rotatelogs: rotatelogs.c + $(CC) $(INCLUDES) $(CFLAGS) rotatelogs.c -o rotatelogs + +logresolve: logresolve.c + $(CC) $(INCLUDES) $(CFLAGS) logresolve.c -o logresolve $(LIBS) + +clean: + rm -f $(TARGETS) + diff --git a/usr.sbin/httpd/support/cls.c b/usr.sbin/httpd/support/cls.c new file mode 100644 index 00000000000..2c553cec93d --- /dev/null +++ b/usr.sbin/httpd/support/cls.c @@ -0,0 +1,165 @@ +#include <ctype.h> +#include <dirent.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +/* + * Compare a string to a mask + * Mask characters: + * @ - uppercase letter + * # - lowercase letter + * & - hex digit + * # - digit + * * - swallow remaining characters + * <x> - 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/usr.sbin/httpd/support/dbmmanage b/usr.sbin/httpd/support/dbmmanage new file mode 100644 index 00000000000..8afc45f0045 --- /dev/null +++ b/usr.sbin/httpd/support/dbmmanage @@ -0,0 +1,126 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995-1997 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. 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://www.apache.org/>. + + +# usage: dbmmanage <DBMfile> <command> <key> <value> <group> +# +# commands: add, delete, view, adduser +# +# no values needed for delete, no keys or values needed for view. +# to change a value, simply use "add". +# adduser encrypts the password: +# dbmmanage <dbm file> adduser <person> <password> +# +# <group> is optional, and may also be supplied to add the user +# to a specified group: +# dbmmanage <dbm file> adduser <person> <password> <group> + +if (scalar(@ARGV) < 2) { + print "Too few arguments.\n"; + exit; +} + +$file=$ARGV[0]; +$command=$ARGV[1]; +$key=$ARGV[2]; +$value=$ARGV[3]; +$group=$ARGV[4]; + +# create a random salt +@range=('0'..'9','a'..'z','A'..'Z'); +srand($$|time); +$salt=$range[rand(int($#range)+1)] . $range[rand(int($#range)+1)]; + +if ($command eq "add") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $value .= ":$group" if $group ne ""; + $DB{$key} = $value; + dbmclose(%DB); + print "Entry $key added with value $value.\n"; + exit; +} + +if ($command eq "adduser") { + $hash = crypt($value, "$salt"); + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $hash .= ":$group" if $group ne ""; + $value .= ":$group" if $group ne ""; + $DB{$key} = $hash; + dbmclose(%DB); + print "User $key added with password $value, encrypted to $hash\n"; + exit; +} + +if ($command eq "delete") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + delete($DB{$key}); + dbmclose(%DB); + exit; +} + +if ($command eq "view") { + dbmopen(%DB, $file, undef) || die "Error: $!\n"; + $return_status = 1; + unless ($key) { + while (($nkey,$val) = each %DB) { + print "$nkey = $val\n"; + } + } else { + $return_status = 0 if defined $DB{$key}; + print "$key = $DB{$key}\n"; + } + dbmclose(%DB); + exit($return_status); +} + +print "Command unrecognized - must be one of: view, add, adduser, delete.\n"; + diff --git a/usr.sbin/httpd/support/dbmmanage.new b/usr.sbin/httpd/support/dbmmanage.new new file mode 100644 index 00000000000..48d9f26d7ee --- /dev/null +++ b/usr.sbin/httpd/support/dbmmanage.new @@ -0,0 +1,140 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995-1997 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. 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://www.apache.org/>. + + +# usage: dbmmanage <DBMfile> <command> <key> <value> +# +# commands: add, delete, view, adduser +# +# no values needed for delete, no keys or values needed for view. +# to change a value, simply use "add". +# adduser encrypts the password: +# dbmmanage <dbm file> adduser <person> <password> + +if (@ARGV < 2) { + die "Too few arguments.\ + Usage: dbmmanage <DBMfile> <command> [<key> [<value>]]\ + where command is add, delete, view or adduser.\ + No value required for \"delete\", no key or value for \"view\".\ + To change a value, simply use \"add\".\ + \"adduser\" encrypts the value and uses it as the user's password\n"; +} + +($file,$command,$key,$value) = @ARGV; + +$file =~ s/\.db.?$//; # remove ".db" or ".dbX" extension if any +$file =~ s/\.pag$//; # remove ".pag" and ".dir" as well. +$file =~ s/\.dir$//; # these are all common DBM extensions. + +if ($command eq "add") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $DB{$key} = $value; + dbmclose(%DB); + print "Entry $key added with value $value.\n"; +} elsif ($command eq "adduser") { + srand; # needs to be done only once. + $salt = &compute_salt(0); # change to compute_salt(1) for new crypt() + $hash = crypt($value, $salt); + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + $DB{$key} = $hash; + dbmclose(%DB); + print "User $key added with password ``$value'', encrypted to $hash\n"; +} elsif ($command eq "delete") { + dbmopen(%DB, $file, 0664) || die "Error: $!\n"; + delete($DB{$key}); + dbmclose(%DB); +} elsif ($command eq "view") { + dbmopen(%DB, $file, undef) || die "Error: $!\n"; + unless ($key) { + while (($nkey,$val) = each %DB) { + print "$nkey = $val\n"; + } + } else { + print "$key = $DB{$key}\n"; + } + dbmclose(%DB); +} else { + print "Command unrecognized - must be one of: view, add, adduser, delete.\n"; +} + +exit(0); + +# if $newstyle is 1, 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. +sub compute_salt { + local($newstyle) = @_; + local($salt); + if ($newstyle) { + $salt = "_" . &randchar(1) . "a.." . &randchar(4); + } else { + $salt = &randchar(2); + } + $salt; +} + +# return $count random characters +sub randchar { + local($count) = @_; + local($str) = ""; + local($enc) = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + while ($count--) { + # 64 = length($enc) in call to rand() below + $str .= substr($enc,int(rand(64)),1); + } + $str; +} diff --git a/usr.sbin/httpd/support/dbmmanage.readme b/usr.sbin/httpd/support/dbmmanage.readme new file mode 100644 index 00000000000..8ef9e688864 --- /dev/null +++ b/usr.sbin/httpd/support/dbmmanage.readme @@ -0,0 +1,7 @@ + +Two versions of the dbmmanage script are included with this release. +One is the old faithful version, which should continue to work if you've +been using it; the other is a newer cut, which can be easily modified to +support the newer extended crypt routines which are present on some +systems (including 4.4BSD derivatives); this newer version is, for the +nonce, experimental... diff --git a/usr.sbin/httpd/support/htdigest.c b/usr.sbin/httpd/support/htdigest.c new file mode 100644 index 00000000000..9d35b44a948 --- /dev/null +++ b/usr.sbin/httpd/support/htdigest.c @@ -0,0 +1,187 @@ +/* + * htdigest.c: simple program for manipulating digest passwd file for Apache + * + * by Alexei Kosut, based on htpasswd.c, by Rob McCool + */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#ifdef MPE +#include <signal.h> +#else +#include <sys/signal.h> +#endif +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +/* This is probably the easiest way to do it */ +#include "../src/md5c.c" + +#define LF 10 +#define CR 13 + +#define MAX_STRING_LEN 256 + +char *tn; + +char *strd(char *s) { + char *d; + + d=(char *)malloc(strlen(s) + 1); + strcpy(d,s); + return(d); +} + +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++])); +} + +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; + } +} + +void putline(FILE *f,char *l) { + int x; + + for(x=0;l[x];x++) fputc(l[x],f); + fputc('\n',f); +} + + +void add_password(char *user, char *realm, FILE *f) { + char *pw; + MD5_CTX context; + unsigned char digest[16]; + char string[MAX_STRING_LEN]; + unsigned int i; + + pw = strd((char *) getpass("New password:")); + if(strcmp(pw,(char *) getpass("Re-type new password:"))) { + fprintf(stderr,"They don't match, sorry.\n"); + if(tn) + unlink(tn); + exit(1); + } + fprintf(f,"%s:%s:",user,realm); + + /* Do MD5 stuff */ + sprintf(string, "%s:%s:%s", user, realm, pw); + + MD5Init (&context); + MD5Update (&context, (unsigned char *) string, strlen(string)); + MD5Final (digest, &context); + + for (i = 0; i < 16; i++) + fprintf(f, "%02x", digest[i]); + + fprintf(f, "\n"); +} + +void usage() { + fprintf(stderr,"Usage: htdigest [-c] passwordfile realm username\n"); + fprintf(stderr,"The -c flag creates a new file.\n"); + exit(1); +} + +void interrupted() { + fprintf(stderr,"Interrupted.\n"); + if(tn) unlink(tn); + exit(1); +} + +void 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 (*)())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); + sprintf(command,"cp %s %s",tn,argv[1]); + system(command); + unlink(tn); +} diff --git a/usr.sbin/httpd/support/htpasswd.1 b/usr.sbin/httpd/support/htpasswd.1 new file mode 100644 index 00000000000..a3819ca17c2 --- /dev/null +++ b/usr.sbin/httpd/support/htpasswd.1 @@ -0,0 +1,88 @@ +.TH htpasswd 1 "February 1997" +.\" Copyright (c) 1997 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. 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://www.apache.org/>. +.SH NAME +htpasswd \- Create and update user authentication files +.SH SYNOPSIS +.B htpasswd +[ +.B \-c +] +.I passwdfile +.I username +.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. +Resources available from the +.B httpd +Apache web server can be restricted to just the users listed +in the files created by htpasswd. This program can only be used +when the usernames are stored in a flat-file. To use a +DBM database see +\fBdbmmanage\fP and \fBdbmmanage.new\fP. +.PP +This manual page only lists the command line arguments. For details of +the directives necessary to configure user authentication in 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 user name 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\fIusername\fP +The username 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 +httpd(8) diff --git a/usr.sbin/httpd/support/htpasswd.c b/usr.sbin/httpd/support/htpasswd.c new file mode 100644 index 00000000000..17efe5b004a --- /dev/null +++ b/usr.sbin/httpd/support/htpasswd.c @@ -0,0 +1,198 @@ +/* + * htpasswd.c: simple program for manipulating password file for NCSA httpd + * + * Rob McCool + */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#ifdef MPE +#include <signal.h> +#else +#include <sys/signal.h> +#endif +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#define LF 10 +#define CR 13 + +#define MAX_STRING_LEN 256 + +char *tn; + +char *strd(char *s) { + char *d; + + d=(char *)malloc(strlen(s) + 1); + strcpy(d,s); + return(d); +} + +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++])); +} + +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; + } +} + +void putline(FILE *f,char *l) { + int x; + + for(x=0;l[x];x++) fputc(l[x],f); + fputc('\n',f); +} + + +/* From local_passwd.c (C) Regents of Univ. of California blah blah */ +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +void to64(register char *s, register long v, register int n) { + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +#ifdef MPE +/* MPE lacks getpass() and a way to suppress stdin echo. So for now, just +issue the prompt and read the results with echo. (Ugh). */ + +char *getpass(const char *prompt) { + +static char password[81]; + +fputs(prompt,stderr); +gets((char *)&password); + +if (strlen((char *)&password) > 8) { + password[8]='\0'; +} + +return (char *)&password; +} +#endif + +void add_password(char *user, FILE *f) { + char *pw, *cpw, salt[3]; + + pw = strd((char *) getpass("New password:")); + if(strcmp(pw,(char *) getpass("Re-type new password:"))) { + fprintf(stderr,"They don't match, sorry.\n"); + if(tn) + unlink(tn); + exit(1); + } + (void)srand((int)time((time_t *)NULL)); + to64(&salt[0],rand(),2); + cpw = crypt(pw,salt); + free(pw); + fprintf(f,"%s:%s\n",user,cpw); +} + +void usage() { + fprintf(stderr,"Usage: htpasswd [-c] passwordfile username\n"); + fprintf(stderr,"The -c flag creates a new file.\n"); + exit(1); +} + +void interrupted() { + fprintf(stderr,"Interrupted.\n"); + if(tn) unlink(tn); + exit(1); +} + +void main(int argc, char *argv[]) { + FILE *tfp,*f; + char user[MAX_STRING_LEN]; + char line[MAX_STRING_LEN]; + char l[MAX_STRING_LEN]; + char w[MAX_STRING_LEN]; + char command[MAX_STRING_LEN]; + int found; + + tn = NULL; + signal(SIGINT,(void (*)())interrupted); + if(argc == 4) { + 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.\n",argv[3]); + add_password(argv[3],tfp); + fclose(tfp); + exit(0); + } else if(argc != 3) 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[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,':'); + if(strcmp(user,w)) { + putline(tfp,line); + continue; + } + else { + printf("Changing password for user %s\n",user); + add_password(user,tfp); + found = 1; + } + } + if(!found) { + printf("Adding user %s\n",user); + add_password(user,tfp); + } + fclose(f); + fclose(tfp); + sprintf(command,"cp %s %s",tn,argv[1]); + system(command); + unlink(tn); +} diff --git a/usr.sbin/httpd/support/httpd.8 b/usr.sbin/httpd/support/httpd.8 new file mode 100644 index 00000000000..d456e38b101 --- /dev/null +++ b/usr.sbin/httpd/support/httpd.8 @@ -0,0 +1,125 @@ +.TH httpd 8 "February 1997" +.\" Copyright (c) 1995-1997 David Robinson. All rights reserved. +.\" Copyright (c) 1997 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. 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://www.apache.org/>. +.SH NAME +httpd \- Apache hypertext transfer protocol server +.SH SYNOPSIS +.B httpd +[ +.B \-hlvX? +] [ +.BI \-d " serverroot" +] [ +.BI \-f " config" +] +.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 +is 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 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 httpd. +.SH OPTIONS +.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/etc/httpd\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 +.B \-h +Output a list of directives together with expected arguments and +places where the directive is valid. +.TP +.B \-l +Output a list of modules compiled into the server. +.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 httpd, and then exit. +.TP +.B \-? +Print a list of the httpd options, and then exit. +.SH FILES +.PD 0 +.B /usr/local/etc/httpd/conf/httpd.conf +.br +.B /usr/local/etc/httpd/conf/srm.conf +.br +.B /usr/local/etc/httpd/conf/access.conf +.br +.B /usr/local/etc/httpd/conf/mime.types +.br +.B /usr/local/etc/httpd/logs/error_log +.br +.B /usr/local/etc/httpd/logs/access_log +.br +.B /usr/local/etc/httpd/logs/httpd.pid +.PD +.SH SEE ALSO +.BR inetd (8). diff --git a/usr.sbin/httpd/support/httpd_monitor.c b/usr.sbin/httpd/support/httpd_monitor.c new file mode 100644 index 00000000000..847a03a77cb --- /dev/null +++ b/usr.sbin/httpd/support/httpd_monitor.c @@ -0,0 +1,307 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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 + * IT'S 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://www.apache.org/>. + + + * simple script to monitor the child Apache processes + * Usage: + * httpd_monitor [ -d serverdir | -f conffile ] [ -s sleep_time ] + * -d/-f options specify server dir or config files, as per + * httpd. + * -s specifies how long to pause between screen updates + * If you choose 0, it might chew up lots of CPU time. + * + * Output explanation.. + * + * s = sleeping but "ready to go" child (this is '_' in mod_status) + * R = active child - writing to client + * W = active child - reading from client + * K = active child - waiting for additional request on kept-alive connection + * D = active child - doing DNS lookup + * L = active child - logging + * _ = dead child (no longer needed) (this is '.' in mod_status) + * t = just starting (this is 'S' in mod_status) + * + * + * Jim Jagielski <jim@jaguNET.com> + * v1.0 Notes: + * This code is much more ugly and complicated than it + * needs to be. + * + * v1.1: + * Minor fixes + * + * v1.2: + * Handles Apache 1.1.* scoreboard format (W/K/D/L states) -- PCS 09Jul96 + */ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "../src/httpd.h" +#include "../src/scoreboard.h" + +#define DEFAULT_SLEEPTIME 2 +#define ASIZE 1024 +#define MAX_PROC HARD_SERVER_LIMIT + +int +main(argc, argv) +int argc; +char **argv; +{ + short_score scoreboard_image; + FILE *afile; + char conf_name[ASIZE]; + char pid_name[ASIZE]; + char score_name[ASIZE]; + char tbuf[ASIZE]; + char *ptmp; + static char kid_stat[] = { '_', 's', 'R', 't', 'W', 'K', 'L', 'D' }; + int achar; + long thepid; + int score_fd; + int sleep_time = DEFAULT_SLEEPTIME; + int last_len = 0; + int kiddies; + int running, dead, total, loop; + short got_config = 0; + struct stat statbuf; + time_t last_time = 0; + extern char *optarg; + extern int optind, opterr; + void lookfor(); + + int usage(); + + /* + * Handle the options. Using getopt() is most probably overkill, + * but let's think about the future! + */ + strcpy(conf_name, HTTPD_ROOT); + while((achar = getopt(argc,argv,"s:d:f:")) != -1) { + switch(achar) { + case 'd': + strcpy(conf_name, optarg); + break; + case 'f': + strcpy(conf_name, optarg); + got_config = 1; + break; + case 's': + sleep_time = atoi(optarg); + break; + case '?': + usage(argv[0]); + } + } + + /* + * Now build the name of the httpd.conf file + */ + if (!got_config) { + strcat(conf_name, "/"); + strcat(conf_name, SERVER_CONFIG_FILE); + } + + /* + * Make sure we have the right file... Barf if not + */ + if (!(afile = fopen(conf_name, "r"))) { + perror("httpd_monitor"); + fprintf(stderr, "Can't open config file: %s\n", conf_name); + exit(1); + } + /* + * now scan thru the ConfigFile to look for the items that + * interest us + */ + lookfor(pid_name, score_name, afile); + fclose(afile); + + /* + * now open the PidFile and then the ScoreBoardFile + */ + if (!(afile = fopen(pid_name, "r"))) { + perror("httpd_monitor"); + fprintf(stderr, "Can't open PIDfile: %s\n", pid_name); + exit(1); + } + fscanf(afile, "%ld", &thepid); + fclose(afile); + + /* + * Enough taters, time for the MEAT! + */ + for(;;sleep(sleep_time)) { + if (stat(score_name, &statbuf)) { + perror("httpd_monitor"); + fprintf(stderr, "Can't stat scoreboard file: %s\n", score_name); + exit(1); + } + if (last_time == statbuf.st_mtime) + continue; /* tricky ;) */ + last_time = statbuf.st_mtime; /* for next time */ + if ((score_fd = open(score_name, 0)) == -1 ) { + perror("httpd_monitor"); + fprintf(stderr, "Can't open scoreboard file: %s\n", score_name); + exit(1); + } + /* + * all that for _this_ + */ + running = dead = total = 0; + ptmp = tbuf; + *ptmp = '\0'; + for(kiddies=0;kiddies<MAX_PROC; kiddies++) { + read(score_fd, (char *)&scoreboard_image, sizeof(short_score)); + achar = kid_stat[(int)scoreboard_image.status]; + if (scoreboard_image.pid != 0 && scoreboard_image.pid != thepid) { + total++; + if (scoreboard_image.status != SERVER_DEAD && + scoreboard_image.status != SERVER_READY) + running++; + *ptmp = achar; + *++ptmp = '\0'; + } + } + close(score_fd); + sprintf(ptmp, " (%d/%d)", running, total); + for(loop=1;loop<=last_len;loop++) + putchar('\010'); + if (last_len > strlen(tbuf)) { + for(loop=1;loop<=last_len;loop++) + putchar(' '); + for(loop=1;loop<=last_len;loop++) + putchar('\010'); + } + printf("%s", tbuf); + fflush(stdout); + last_len = strlen(tbuf); + } /* for */ +} + +int +usage(arg) +char *arg; +{ + printf("httpd_monitor: Usage\n"); + printf(" httpd_monitor [ -d config-dir] [ -s sleep-time ]\n"); + printf(" Defaults: config-dir = %s\n", HTTPD_ROOT); + printf(" sleep-time = %d seconds\n", DEFAULT_SLEEPTIME); + exit(0); +} + +/* + * This function uses some hard-wired knowledge about the + * Apache httpd.conf file setup (basically names of the 3 + * parameters we are interested in) + * + * We basically scan thru the file and grab the 3 values we + * need. This could be done better... + */ +void +lookfor(pidname, scorename, thefile) +char *pidname, *scorename; +FILE *thefile; +{ + char line[ASIZE], param[ASIZE], value[ASIZE]; + char sroot[ASIZE], pidfile[ASIZE], scorefile[ASIZE]; + + *sroot = *pidfile = *scorefile = '\0'; + while (!(feof(thefile))) { + fgets(line, ASIZE-1, thefile); + *value = '\0'; /* protect braindead sscanf() */ + sscanf(line, "%s %s", param, value); + if (strcmp(param, "PidFile")==0 && *value) + strcpy(pidfile, value); + if (strcmp(param, "ScoreBoardFile")==0 && *value) + strcpy(scorefile, value); + if (strcmp(param, "ServerRoot")==0 && *value) + strcpy(sroot, value); + } + + /* + * We've reached EOF... we should have encountered the + * ServerRoot line... if not, we bail out + */ + if (!*sroot) { + fprintf(stderr, "Can't find ServerRoot!\n"); + exit(1); + } + + /* + * Not finding PidFile or ScoreBoardFile is OK, since + * we have defaults for them + */ + if (!*pidfile) + strcpy(pidfile, DEFAULT_PIDLOG); + if (!*scorefile) + strcpy(scorefile, DEFAULT_SCOREBOARD); + + /* + * Relative or absolute? Handle both + */ + if (*pidfile == '/') + strcpy(pidname, pidfile); + else { + strcpy(pidname, sroot); + strcat(pidname, "/"); + strcat(pidname, pidfile); + } + if (*scorefile == '/') + strcpy(scorename, scorefile); + else { + strcpy(scorename, sroot); + strcat(scorename, "/"); + strcat(scorename, scorefile); + } +} + diff --git a/usr.sbin/httpd/support/log_server_status b/usr.sbin/httpd/support/log_server_status new file mode 100644 index 00000000000..4f972591cff --- /dev/null +++ b/usr.sbin/httpd/support/log_server_status @@ -0,0 +1,110 @@ +#!/usr/local/bin/perl + +# ==================================================================== +# Copyright (c) 1995-1997 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. 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://www.apache.org/>. + + +# 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/960312" +$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 + +{ + $date=`date +%y%m%d:%H%M%S`; + chop($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 (<S>) { + $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/usr.sbin/httpd/support/logresolve.c b/usr.sbin/httpd/support/logresolve.c new file mode 100644 index 00000000000..86f979947a0 --- /dev/null +++ b/usr.sbin/httpd/support/logresolve.c @@ -0,0 +1,369 @@ +/*** ***\ + 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 <sys/types.h> + +#include <ctype.h> +#include <netdb.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <sys/socket.h> + +#include <netinet/in.h> +#ifndef MPE +#include <arpa/inet.h> +#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 + +#ifdef MPE +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 + */ + +/* 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(ipnum, string, check) +struct in_addr ipnum; +char *string; +int check; +{ + struct nsrec **current, *new; + struct hostent *hostdata; + char *name; + extern int h_errno; /* some machines don't have this in their headers */ + + 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(output) +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(s, n) +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(argc, argv) +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"); + 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/usr.sbin/httpd/support/phf_abuse_log.cgi b/usr.sbin/httpd/support/phf_abuse_log.cgi new file mode 100644 index 00000000000..9ce2749c571 --- /dev/null +++ b/usr.sbin/httpd/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 "<Location /cgi-bin/phf*>" 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\n<BLINK>Smile, you're on Candid Camera.</BLINK>\n"; diff --git a/usr.sbin/httpd/support/rotatelogs.c b/usr.sbin/httpd/support/rotatelogs.c new file mode 100644 index 00000000000..08066923854 --- /dev/null +++ b/usr.sbin/httpd/support/rotatelogs.c @@ -0,0 +1,84 @@ +/* + +Simple program to rotate Apache logs without having to kill the server. + +Contributed by Ben Laurie <ben@algroup.co.uk> + +12 Mar 1996 + +*/ + +#define BUFSIZE 65536 +#define MAX_PATH 1024 + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +void main(int argc,char **argv) + { + char buf[BUFSIZE],buf2[MAX_PATH]; + time_t tLogEnd; + time_t tRotation; + int nLogFD=-1; + int nRead; + char *szLogRoot; + + if(argc != 3) + { + fprintf(stderr,"%s <logfile> <rotation time in seconds>\n\n",argv[0]); +#ifdef __EMX__ + 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\n"); + fprintf(stderr,"system time at which the log nominally starts (N.B. this time will always be a\n"); + fprintf(stderr,"multiple of the rotation time, so you can synchronize cron scripts with it).\n"); + fprintf(stderr,"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/usr.sbin/httpd/support/suexec.c b/usr.sbin/httpd/support/suexec.c new file mode 100644 index 00000000000..3e04a095b88 --- /dev/null +++ b/usr.sbin/httpd/support/suexec.c @@ -0,0 +1,491 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * 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 "suexec.h" + +#include <sys/param.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <time.h> +#include <sys/stat.h> + +#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; + +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", + "SCRIPT_FILENAME", + "SCRIPT_NAME", + "SCRIPT_URI", + "SCRIPT_URL", + "SERVER_ADMIN", + "SERVER_NAME", + "SERVER_PORT", + "SERVER_PROTOCOL", + "SERVER_SOFTWARE", + "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, "[%.2d:%.2d:%.2d %.2d-%.2d-%.2d]: ", lt->tm_hour, lt->tm_min, + lt->tm_sec, lt->tm_mday, (lt->tm_mon + 1), lt->tm_year); + + vfprintf(log, fmt, ap); + + fflush(log); +#endif /* LOG_EXEC */ + return; +} + +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; +} + +void clean_env() +{ + 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 env mem\n"); + exit(120); + } + + for (ep = environ; *ep && cidx < AP_ENVBUF; 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; + } + } + } + } + + sprintf(pathbuf, "PATH=%s", SAFE_PATH); + cleanenv[cidx] = strdup(pathbuf); + 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. + */ + if (strcmp(HTTPD_USER, pw->pw_name)) { + log_err("user mismatch (%s)\n", pw->pw_name); + exit(103); + } + + /* + * 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); + } + + /* + * 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) %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); + } + + 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. + */ + fclose(log); + log = NULL; + + /* + * Execute the command, replacing our image with its own. + */ + execv(cmd, &argv[3]); + + /* + * (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("exec failed (%s)\n", cmd); + exit(255); +} diff --git a/usr.sbin/httpd/support/suexec.h b/usr.sbin/httpd/support/suexec.h new file mode 100644 index 00000000000..91cbc86950a --- /dev/null +++ b/usr.sbin/httpd/support/suexec.h @@ -0,0 +1,137 @@ +/* ==================================================================== + * Copyright (c) 1995-1997 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. 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://www.apache.org/>. + * + */ + +/* + * suexec.h -- user-definable variables for the suexec wrapper code. + */ + + +#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/etc/httpd/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/etc/httpd/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 */ |