main
HwangKC 2024-05-24 12:19:45 +08:00
parent 591cf4b4c8
commit a386d0c62b
1830 changed files with 1283788 additions and 0 deletions

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,38 @@
Liam Widdowson <lbw@telstra.com>
Lots of improvement for Solaris support
Wes Hardaker <wjhardaker@ucdavis.edu>
Michal Kara <lemming@netcentrum.cz>
mmap_cache buglet fix
Matt Callaway <matt@securepipe.com>
CGI chdir issue
Jari Korva <jpkorva@iki.fi>
IPv6 patch
William Meadows <wmeadows@linux-support.net>
escape.c bug notification and (first) fix
Thomas Neumann <tn@tmr-online.de>
*BSD compilation/core bugfixes
Paul Saab <paul@mu.org>
Remove SO_REUSEADDR setting on each client socket
David N. Welton <davidw@linuxcare.com>
Added "Listen" directive for server bind address
Craig Silverstein <csilvers@google.com>
patches for config.h and config.c
Russ Nelson <nelson@crynwr.com>
Original, Experimental IP-based Virtual Host code
Landon Curt Noll http://www.isthe.com/chongo
Allow non-standard date format 31 September 2000 23:59:59 GMT
Skip whitespace before HTTP/major.minor
Brieuc Jeunhomme <bbp@via.ecp.fr>
fix buglet in alias expansion

View File

@ -0,0 +1,433 @@
** Changes from 0.94.12 to 0.94.13
* Change many instances of log_error_mesg + exit to DIE macro
* Change all instance of log_error_mesg (without exit) to WARN macro
* do a much better job of checking return values from malloc and
especially strdup.
* check results of calling umask and getrlimit
* server_s is no longer a global int
* check results of fork via switch instead of if (fork())
* check for getopt.h and include it if found
* remove unused #defines, and add WARN macro, and replace
many calls to log_error_mesg(..) with WARN macro
* fix bug in get_commonlog_time where time_offset calculation was
the opposite of what it should be ('-' and '+' were swapped)
* fix compatability bug with old and newer versions of flex/yacc
* add check for AC_FUNC_MMAP to configure.in
* fix really lame thinko in normalize_path, which would prepend the
results of earlier calls to results from later calls
* Add MaxConnections, a configuration directive which allows the
user to specify the maximum number of connections that Boa will
accept concurrently.
* add SERVER_ADDR and REQUEST_URI to environment of CGI
* handle SIGBUS during writes of data that has been memory mapped
* minor optimization in select.c that prevents DEAD requests from
being added to the block set
* fix bug in CGI environment script_name - closes sf.net bug #576725
* make 'status' variable local to requests.c, not local to every file
by forgetting to declare 'extern' in globals.h :-|
* make getsockname non-fatal, and do it every time because we may
need it for the CGI
* some minor refactoring optimizations in hash.c
** Changes from 0.94.11 to 0.94.12
* Renamed Changelog ChangeLog, and moved up to top-level directory
* Next 3 items due in part or whole thanks to
Liam Widdowson
* when printf'ing a pid type, force to int, because it could be
something else on other platforms. Should probably change it to
a long, and use that.
* backported chroot commandline support from 0.95
* backported support for strdup, strstr, alphasort, and scandir
from 0.95
* Fixed src/Makefile.in -- it didn't remove index_dir.o
* backport create_temporary_file from 0.95
(instead of using tmpnam)
* Allow non-standard date format 31 September 2000 23:59:59 GMT
Patch by Landon Curt Noll
* Skip whitespace before HTTP/major.minor
Adapted patch from Landon Curt Noll
* open /dev/null first thing (affects chrooting)
* properly handle sigalrm -- use sigalrm_flag and sigalrm_run
instead of handling the signal in the signal handler
* update manpage slightly
* send 400 BAD Request when resource does not start with '/'
* add grp.h to boa.h's includes -- remove from boa.c and config.c
* removed duplicate header includes from boa.c, config.c, get.c,
ip.c, request.c, response.c
* factor out creating the server socket and dropping privs
into create_server_socket and drop_privs
* type all functions in boa.c (except main) as static
* set umask after opening /dev/null
* tie stdin/stdout to /dev/null before commandline parse
* removed old, unused chroot code
* move builds_needs_escape earlier in the startup
* move fork later in the startup
* type all c_set_* as static in config.c
* don't bother trying to change uid/gid (or error if the
requested uid/gid doesn't exist) if not UID 0
* return more appropriate error code when foo.html gives
access denied, but foo.html.gz gives some other error
(essentially report error associated with foo.html, not foo.html.gz)
* send NOT Implemented when an unknown method is attempted
* always attempt a 32k read right before close
(stopgap until blackhole can be merged)
* allow more than 1 space in logline between method, resource,
and http version
* don't use inline functions
* update configure.in so that autoconf 2.50 doesn't complain (as much)
* properly use VPATH and srcdir according to autoconf docs
* change curly-braces to parentheses in Makefilein
* use $^ instead of manually listing the dependencies in Makefile.in
* remove tests section in Makefile.in
* write tags not TAGS in Makefile.in
* Add gethostbyname and inet_aton to function checks
* Add code from 0.95 which checks for socket in -lsocket,
inet_aton in -lresolv, and gethost{by}name in -lnsl
* Also remove broken bc-based "how big is an unsigned int" checks:
assume minimum of 32 bits and check in escape.c at runtime.
* Added new file: README.chroot.solaris, based on a
modified version by Liam Widdowson
* Add check_struct_for.m4, which allows us to check a structure
for a member (found at http://www.gnu.org/software/ac-archive/
authored by Wes Hardaker
* Call "aclocal -I ." to rebuild aclocal.m4
* Using new check-struct-for-member autoconf macro, check
for tm_gmtoff and tm_zone in struct tm -- useful in
portability tests for localtime.
* Also check sockaddr_in for structure sin_len so we can set
it properly.
* index_dir.c (which ends up in boa_indexer) can now be compiled
with USE_LOCALTIME, and if so, it will report the local time
using the timezone name. Otherwise it uses UTC time and UTC
timezone designation.
* fix buglet in mmap_cache.c which shows up when under
heavy load by many different files.
Found and squashed by Michal Kara
* normalize paths on Aliases, log files, server root, dirmaker
This makes sure that paths are 'absolute'
* don't generate DOCUMENT_ROOT or SERVER_ROOT,
CGIs have no business knowing that information
* if CGI, chdir to the cgi's root path
Bug found by Matt Callaway
* remove ChrootPath and PidFile directives from the parser
(they aren't used anyway)
* keep track of maximum file descriptor in use to optimize call
to select()
* apply IPv6 patch from Jari Korva
* optimize keep-alive copy data routine
* try to use memcpy instead of strcpy/strcat in more places (alias.c)
* update .depend file
* use fcntl + GET_FL to get a file descriptor's flags, then
add or remove only the bits we want to set. This prevents
accidentally setting or unsettings bits we don't have anything
to do with inadvertantly. (removed, at least temporarily.
Show me a system where it is needed -- LRD)
* make sure to call FD_ZERO when we handle a restart
* in read.c, don't call boa_perror on read failure -- socket is
dead or messed up anyway, no reason to try to write to it.
* explicit .SUFFIXES in Makefile.in
* boa.objdump target added
* use @MAKE_SET@ (for when $(MAKE) != "make")
* add -Wundef -Wwrite-strings -Wredundant-decls -Winline to GCC_FLAGS
* change Paul Phillips' and Larry Doolittle's emails in source
* add --disable-debug, --enable-profile, --with-dmalloc, and --with-efence
* test for failed-but-return-was-successful setuid:
http://www.securityfocus.com/bid/1322
* use _exit not exit in CGI child
* always place new keepalive request on blocked list, we can't be
sure of the state of the active list, and since enqueue places
things at the *front* of the list, it doesn't do us much good
to place the new request on the active list anyway.
* update some Copyright statements for 2002
* When comparing the uri to an alias, only compare if
the uri length is greater than or equal to the length of the alias
* in init_script_alias, make sure to check for document_root before
trying to use it
* script_name is now just a copy of the request, rather
than some complicated variation on the pathname
* change the way the CGI environment is handled.
Now, it is allocated at request allocation time, and exists
throughout the life of the structure.
* check memory allocations, etc.. when creating the static
CGI environment and when making new CGI environment variables
* wait until process_option_end to call unescape_uri, clean_pathname,
and translate_uri
* remove debian package information
* move RedHat packaging information to contrib
* remove tests -- they weren't usable anyway
* add some new hash routines, and use djb2 (a variant on a
hash algorithm popularized by Dan J. Bernstein)
* a side-effect of the new hash routines is a bugfix,
involving negative return values from hash routines.
This has been fixed.
* add a routine, show_hash_stats, which is called with other
statistical output via sigalarm
* remove some duplicate prototypes from config.c
* make simple_itoa take an unsigned int
* try to make NOBLOCK handling in compat.h compatible with Solaris
* make sure to update current_time before calling signal handlers
* alter primary loop to make sure that select gets called even
when there are requests that are not blocking, and call fdset_update
and process_requests (when appropriate) after signal handlers but
before select to make sure that blocked requests are still handled
by select after a sighup. (Thanks to Karl Olsen)
* pull select loop into select.c
* poll server socket once per active connection
* add send_r_service_unavailable and use it when appropriate
* state uptime in seconds at normal program termination
* include sys/fcntl.h if it is found by configure
* fix POST bug where a content-length < 0 would cause Boa to
consume its full share of CPU until killed
Bug report by Landon Curt Noll
* add CGIPath configuration variable
based upon a patch by Landon Curt Noll
* add function boa_atoi, which wraps atoi, but does not
accept negative values. Additionally, it checks to make sure
the converted value and the original value are the same, avoiding
issues like "124.3" -> "123" and "123abc" -=> "123".
Either a value is an int or it isn't - no middle ground.
* use boa_atoi to convert content-length from client.
* add new #define - SINGLE_POST_LIMIT_DEFAULT, which defines
(in bytes) the *default* single_post_limit.
* single_post_limit is now in bytes.
* when adding aliases, only "normalize" paths that start
with "./" - this is a departure from previous behavior
* add "?" to the list of characters that it is safe to leave unescaped
* clean up Makefile.in of no-longer-pertinent comments
* add send_r_bad_gateway and use it
* tie stderr to either cgi_log_fd or devnullfd - either way
make sure stderr is a valid filehandle before cgi execution
* cgi_env is no longer allocated, it's part of the struct now
* fix bug in CgiPath logic
* when unable to allocate memory for an environment variable, log it
* add clear_common_env, which de-allocates the cgi_common_env stuff
[NEVER USE THIS outside of a terminal signal handler!]
* don't be so wasteful of memory in normalize_path
* adapted fix for alias expansion from Brieuc Jeunhomme
** Changes from 0.94.10.1 to 0.94.11
* use LIBS in Makefile.in (which propagates from autoconf)
* properly free memory allocated by scandir in index_dir.c
* rearrange some header files and includes
* on reads and writes, don't check for -1, check for < 0
* include fix by William Meadows
for escape.c which fixes segfaults due to improper allocation
* above fix by William Meadows no longer needed;
escape.c and escape.h rewritten by Larry Doolittle -- requires
at least 32 bit words, but is correct (jdn's 1st attempt was faulty)
** Changes from 0.94.10 to 0.94.10.1
* Actually update the SERVER_VERSION in src/defines.h
** Changes from 0.94.9 to 0.94.10
* Fixes escaping rules
* Fixes segfault when directory_index is undefined and
directory needs to be generated
* adds dummy signal handlers for SIGUSR1 and SIGUSR2 (Closes SF #425921)
* Update documentation regarding mime.types (Closes Debian #69991)
* Make sure documentation builds (Closes Debian #110818)
** Changes from 0.94.8.3 to 0.94.9
* src/Makefile.in updated to take CFLAGS, LIBS, and LDFLAGS
from autoconf
* Update escaping rules with latest RFC
* unescape_uri skips fragments and also stop parsing at '?'
* Don't accept fd over FD_SETSIZE in request.c:get_request
* use backported documentation from 0.95
* make sure POST fd gets closed even on client cancel
* use backported index_dir.c from 0.95
* support subdirectories in ScriptAlias directories
* add SinglePostLimit (int, in Kilobytes) to config system
* check for ENOSPC on body write
* use environment variable TMP (or "/tmp" if not available),
and chdir there when boa exits.
* add 1-time-only hack to make a 32kB read at the end of a request
on POST or PUT
* close unused file descriptors (/dev/null in boa.c, and the
unused part of the pipes call in cgi.c)
* made Makefile.in VPATH happy
** Changes from 0.94.8.2 to 0.94.8.3
* Move unescape_uri *before* clean_pathname to prevent
encoding of / and .. in pathname
* wrap execution of GUNZIP in cgi.c with #ifdef GUNZIP
* stop parsing when fragment found in URL ('#')
** Changes from 0.94.8.1 to 0.94.8.2
* close pipes[1] in child and generate HTTP_REFERER environment
variable in cgi.c
* Minor changes to the Debian package
** Changes from 0.94.8 to 0.94.8.1
* Change umask call from (umask(0600)) to (umask(~0600))
** Changes from 0.94.7 to 0.94.8
* Fix major thinko in temp file permissions
* unlink temporary file immediately following creation
* implement maximum # of active connections at 10 less than RLIMIT_NOFILE
to avoid or eliminate crashes resulting from running out of
file descriptors
* Fix thinko in POST
** Changes from 0.94.6 to 0.94.7
* STDIN and STDOUT are now tied to /dev/null
* sets PATH_MAX to 2048 if not defined (for Hurd)
* core dumps (should never happen) would be located in /tmp
* alter behavior when select gets a EBADF
* add translation for the \" char -> &quot;
* remove use of sys_errlist. Use perror.
* better makedist.sh (still a stupid program though)
** Changes from 0.94.5 to 0.94.6
* Removed doc++ commenting
* Removed erroneous debugging statments
* Move some stuff out of config.c (read_config_file) to boa.c
* Altered some of fixup_server_root()
* Bug fix in get.c re: automatic gunzip
* Added some stubs for chroot code (*not* ready yet)
** Changes from 0.94.4 to 0.94.5
* Alteration of most of the comments and such for doc++ use
* Fixed buffer overflow in alias.c
* Fixed buffer underflow in util.c
** Changes from 0.94.3 to 0.94.4
* Better escaping of data to user, both for HTTP headers and HTML body
* Proper escaping of output in CGI example perl scripts
** Changes from 0.94.0 to 0.94.2
* Fixed obnoxious pipeline bug
* Fixed (sorta) a compilation/core bug for *BSD systems
Original code by Thomas Neumann
* Moved to GPLv2
* Changed manpage to section 8
* boa.sgml now references a .png file instead of evil .gif
** Changes from 0.93.19.2 to 0.94.0
* Added UseGMT to the configuration parser
* util.c commonlog now logs in Apache-style commonlog time format
* Remove SO_SNDBUF on-start message
** Changes from 0.93.19 to 0.93.19.2
* Changed to combined log (from NCSA access_log format) ala Drew Streib
* Altered POST cgi code to handle bug in Netscape
* SO_SNDBUF changes by Larry
** Changes from 0.93.17.2 to 0.93.19 (all 0.93.18.x changes inclusive)
* Update of some copyright statements for 99
* Replacement of sprintf with strlen/memcpy or strcpy/strcat
wherever possible
* Significant rearrangement in alias.c, minor functional differences
(some CGI environment variables handled differently)
* Removal of die function. Replace with log_err_mesg and exit.
* initial IPv6 stubs and support
* Move #include "config.h" to top of boa.h where it will do some good
* Stubs and functions for strstr and strdup
* Seperation of buffer code into it's own file
* Significant changes to cgi.c et al (cgi_header.c, etc...)
* Speed patches by removal of "extra" calls to time(): Use global variable!
* pipelining changes... it works now.
* require content-length from clients (ala rfc1945)
* alter body_read and body_write to work more efficiently with known content-length
* move read(2) part to *after* parsing...
* added support for additional header message in send_redirect_temp
* change use of NO_ZERO_FILL_LENGTH to offsetof() use
* Remove SO_REUSEADDR setting on each client socket, Paul Saab
* Avoid SO_SNDBUF setting if possible
* Large quantities of otherwise not-insignificant changes
** Changes from 0.93.17.2 to 0.93.17.3
* Put on-the-fly directories back in, stripped down from the 0.92 version
* Fixed DocumentRoot, ServerAdmin and ServerName null-value handling in
CGI environment generation
* Fixed argument order in Script* directives (bug introduced in 0.93.17.2)
* Got rid of MAX_CGI_VARS because it was not being used consistently, or
for that matter, at all, really.
* Added some more FASCIST_LOGGING to cgi.c
* Minor mmap patch by LRD for request.c
** Changes from 0.93.17.1 to 0.93.17.2
* Added "Listen" directive for server bind address, as most recently
suggested by David N. Welton
* Put virtualhost feature in, was experimental in 0.92q
** Changes from 0.93.16.2 to 0.93.17.1
* New config file parser (supposed to be more maintainable) (LRD)
* Support for "|command" and ":host:port" syntax for logfiles (untested) (LRD)
** Changes for the 0.93 version **
* Huge quantities of changes
* keepalive Bugfix in 0.93.16.2 by Jon Nelson
report by Craig Silverstein of Google fame.
* patch for config.h by Craig Silverstein
* fixed "Parent Directory" problem in boa_indexer for title "/"
(Debian bug #36165)
* More Craig Silverstein
modifications, namely:
ErrorLog (if omitted, print to stderr)
DocumentRoot (if omitted, can only server user-dir files)
DirectoryIndex (if omitted, always use DirectoryMaker)
MimeTypes (if omitted, don't load -- users can use AddType instead)
** Changes from v0.92o to v0.92p **
* Documented misbehavior of CGI, SIGHUP, short aliases, stale dircache.
* Documented how to patch signals.c for use on SunOS.
* Closed file descriptor leak when redirecting a bare directory URL to
one with an appended "/".
* Closed potential file descriptor leak if errors encountered generating
on-the-fly index.
* Cleaned up include file handling to be simultaneously compatible with
Linux, SunOS, HP-UX, and AIX.
* Supress message body for codes 302, 400, 403, 404, 500, and 501 if
incoming request is "HEAD".
** Changes from v0.91 to v0.92o **
(0.92o released 27 December, 1996)
* Maintenance handover from Paul Phillips to Larry Doolittle
* Changed (char)NULL to '\0'
* Cleaned up signal handler prototypes in signals.c
* Modified handling of CGI environment variable PATH_TRANSLATED,
should now work the same as NCSA.
* More conservative buffer size in add_cgi_env()
* Build argv list for a CGI script according to spec
* Speedup process_header_line, eliminate potential memory leak
* Occasional spelling fixes and lint removal
* Added REMOTE_PORT env var for CGI scripts, to allow easy ident lookups
* Changed rfc822 time format
* Log timeouts and broken connections
* Fix mime suffix handling for filenames with multiple "."s
* Initialize conn->time_last, fixes bug with rapid-fire connections
* Performance tweak to req_write()
* Changed http_version from float to char[8]
* Rewrote on-the-fly directory generation; it works now
* Added user configurable dircache directory in boa.conf
* Fixed "simple" response bugs, including incorrect CGI handling
* Keepalive (HTTP/1.1 draft) support, mostly by Jon Nelson
* Close data_fd in 304 Not Modified flow of control
* Switch socket flags to non-blocking before cgi handoff
* Try to handle errno properly in the face of multiple errors
* Close fd's of all other transactions before cgi handoff
* Move real work for sighup and sigchld out of signal handler
* Fix free(req->cgi_env) in request.c
* Response message cleanup - better match to HTML-2.0 DTD
* Experimental Virtual Host code from Russ Nelson
* Expand buffer for escaped URI in init_get()
* SIGTERM triggers lame duck mode until all pending transactions complete
* Close and unlink temp file for POST in parent process
** Changes from v0.90 to v0.91 **
* Cleaned up main while loop
* Optimized request line parsing
* Added state machine for header reads -- necessary to deal wtih
possibility of obtaining header data in multiple reads. This
also allows interactive use of server.
* Added 500/501 return codes for various conditions
** v0.90 **
* Initial release

View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@ -0,0 +1,15 @@
This is Boa, a high performance web server for Unix-alike computers,
covered by the Gnu General Public License. This is version 0.94,
released January 2000. It is well tested and appears to be of
at least "gamma" quality.
Boa was created in 1991 by Paul Phillips <paulp@go2net.com>. It is now being
maintained and enhanced by Larry Doolittle <ldoolitt@boa.org>
and Jon Nelson <jnelson@boa.org>.
For more information (including installation instructions) examine
the file docs/boa.txt or docs/boa.dvi, point your web browser to docs/boa.html,
or visit the Boa homepage at
http://www.boa.org/

View File

@ -0,0 +1,194 @@
# Boa v0.94 configuration file
# File format has not changed from 0.93
# File format has changed little from 0.92
# version changes are noted in the comments
#
# The Boa configuration file is parsed with a lex/yacc or flex/bison
# generated parser. If it reports an error, the line number will be
# provided; it should be easy to spot. The syntax of each of these
# rules is very simple, and they can occur in any order. Where possible
# these directives mimic those of NCSA httpd 1.3; I saw no reason to
# introduce gratuitous differences.
# $Id: boa.conf,v 1.25 2002/03/22 04:33:09 jnelson Exp $
# The "ServerRoot" is not in this configuration file. It can be compiled
# into the server (see defines.h) or specified on the command line with
# the -c option, for example:
#
# boa -c /usr/local/boa
# Port: The port Boa runs on. The default port for http servers is 80.
# If it is less than 1024, the server must be started as root.
Port 80
# Listen: the Internet address to bind(2) to. If you leave it out,
# it takes the behavior before 0.93.17.2, which is to bind to all
# addresses (INADDR_ANY). You only get one "Listen" directive,
# if you want service on multiple IP addresses, you have three choices:
# 1. Run boa without a "Listen" directive
# a. All addresses are treated the same; makes sense if the addresses
# are localhost, ppp, and eth0.
# b. Use the VirtualHost directive below to point requests to different
# files. Should be good for a very large number of addresses (web
# hosting clients).
# 2. Run one copy of boa per IP address, each has its own configuration
# with a "Listen" directive. No big deal up to a few tens of addresses.
# Nice separation between clients.
# The name you provide gets run through inet_aton(3), so you have to use dotted
# quad notation. This configuration is too important to trust some DNS.
#Listen 192.68.0.5
# User: The name or UID the server should run as.
# Group: The group name or GID the server should run as.
User nobody
Group nogroup
# ServerAdmin: The email address where server problems should be sent.
# Note: this is not currently used, except as an environment variable
# for CGIs.
#ServerAdmin root@localhost
# ErrorLog: The location of the error log file. If this does not start
# with /, it is considered relative to the server root.
# Set to /dev/null if you don't want errors logged.
# If unset, defaults to /dev/stderr
ErrorLog /var/log/boa/error_log
# Please NOTE: Sending the logs to a pipe ('|'), as shown below,
# is somewhat experimental and might fail under heavy load.
# "Usual libc implementations of printf will stall the whole
# process if the receiving end of a pipe stops reading."
#ErrorLog "|/usr/sbin/cronolog --symlink=/var/log/boa/error_log /var/log/boa/error-%Y%m%d.log"
# AccessLog: The location of the access log file. If this does not
# start with /, it is considered relative to the server root.
# Comment out or set to /dev/null (less effective) to disable
# Access logging.
AccessLog /var/log/boa/access_log
# Please NOTE: Sending the logs to a pipe ('|'), as shown below,
# is somewhat experimental and might fail under heavy load.
# "Usual libc implementations of printf will stall the whole
# process if the receiving end of a pipe stops reading."
#AccessLog "|/usr/sbin/cronolog --symlink=/var/log/boa/access_log /var/log/boa/access-%Y%m%d.log"
# UseLocaltime: Logical switch. Uncomment to use localtime
# instead of UTC time
#UseLocaltime
# VerboseCGILogs: this is just a logical switch.
# It simply notes the start and stop times of cgis in the error log
# Comment out to disable.
#VerboseCGILogs
# ServerName: the name of this server that should be sent back to
# clients if different than that returned by gethostname + gethostbyname
#ServerName www.your.org.here
# VirtualHost: a logical switch.
# Comment out to disable.
# Given DocumentRoot /var/www, requests on interface 'A' or IP 'IP-A'
# become /var/www/IP-A.
# Example: http://localhost/ becomes /var/www/127.0.0.1
#
# Not used until version 0.93.17.2. This "feature" also breaks commonlog
# output rules, it prepends the interface number to each access_log line.
# You are expected to fix that problem with a postprocessing script.
#VirtualHost
# DocumentRoot: The root directory of the HTML documents.
# Comment out to disable server non user files.
DocumentRoot /var/www
# 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 to use as a pre-written HTML
# directory index. Please MAKE AND USE THESE FILES. On the
# fly creation of directory indexes can be _slow_.
# Comment out to always use DirectoryMaker
DirectoryIndex index.html
# DirectoryMaker: Name of program used to create a directory listing.
# Comment out to disable directory listings. If both this and
# DirectoryIndex are commented out, accessing a directory will give
# an error (though accessing files in the directory are still ok).
DirectoryMaker /usr/lib/boa/boa_indexer
# DirectoryCache: If DirectoryIndex doesn't exist, and DirectoryMaker
# has been commented out, the the on-the-fly indexing of Boa can be used
# to generate indexes of directories. Be warned that the output is
# extremely minimal and can cause delays when slow disks are used.
# Note: The DirectoryCache must be writable by the same user/group that
# Boa runs as.
# DirectoryCache /var/spool/boa/dircache
# KeepAliveMax: Number of KeepAlive requests to allow per connection
# Comment out, or set to 0 to disable keepalive processing
KeepAliveMax 1000
# KeepAliveTimeout: seconds to wait before keepalive connection times out
KeepAliveTimeout 10
# MimeTypes: This is the file that is used to generate mime type pairs
# and Content-Type fields for boa.
# Set to /dev/null if you do not want to load a mime types file.
# Do *not* comment out (better use AddType!)
MimeTypes /etc/mime.types
# DefaultType: MIME type used if the file extension is unknown, or there
# is no file extension.
DefaultType text/plain
# CGIPath: The value of the $PATH environment variable given to CGI progs.
CGIPath /bin:/usr/bin:/usr/local/bin
# SinglePostLimit: The maximum allowable number of bytes in
# a single POST. Default is normally 1MB.
# AddType: adds types without editing mime.types
# Example: AddType type extension [extension ...]
# Uncomment the next line if you want .cgi files to execute from anywhere
#AddType application/x-httpd-cgi cgi
# Redirect, Alias, and ScriptAlias all have the same semantics -- they
# match the beginning of a request and take appropriate action. Use
# Redirect for other servers, Alias for the same server, and ScriptAlias
# to enable directories for script execution.
# 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.
# Example: Redirect /bar http://elsewhere/feh/bar
# Aliases: Aliases one path to another.
# Example: Alias /path1/bar /path2/foo
Alias /doc /usr/doc
# ScriptAlias: Maps a virtual path to a directory for serving scripts
# Example: ScriptAlias /htbin/ /www/htbin/
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/

View File

@ -0,0 +1,158 @@
Boa chroot mini-HOWTO
===================================================
by Liam Widdowson <lbw@telstra.com>
modified slightly by Jon Nelson <jnelson@boa.org>
The following is required to get Boa working in a chroot jail. Whilst this
README is about Solaris specifically, the principals here will apply to
other operating systems.
The following assumptions are made:
- Boa has been compiled and installed in /opt/boa
- The chroot jail will be created in /var/www
- A user and group 'www' have been created.
Make sure you change the above directories to suit your system.
Your boa.conf should look something like the following:
## begin config file
Port 80
User www
Group www
# Note, these paths are used releative to the chroot jail. i.e /var/log is
# really /var/www/var/log
ErrorLog /var/log/error_log
AccessLog /var/log/access_log
DocumentRoot /var/www
# You won't be able to access user home directories outside of the chroot
# but you may replicate them into the chroot jail. You'll need a working
# and valid /etc/passwd as well
UserDir public_html
DirectoryIndex index.html
# this binary must exist in the chroot jail. Again, the path is relative.
DirectoryMaker /usr/bin/boa_indexer
KeepAliveMax 1000
KeepAliveTimeout 10
# this file must exist inside AND outside the chroot jail.
MimeTypes /opt/boa/mime.types
DefaultType text/plain
## end config file
Once the configuration file is created, you must begin creating your
chroot jail. A variety of libraries, timezone files, device files and other
bits and pieces must be copied in order for this to work. Below is a ls -lR
of what your jail should be at a minimum:
.:
total 10
drwxr-xr-x 2 root other 512 Jan 21 18:58 dev
drwxr-xr-x 2 root other 512 Jan 21 19:20 etc
drwxr-xr-x 3 root other 512 Jan 21 19:20 opt
drwxr-xr-x 5 root other 512 Jan 21 19:08 usr
drwxr-xr-x 4 root other 512 Jan 21 18:57 var
./dev:
total 0
crw-rw-rw- 1 root other 13, 2 Jan 21 18:58 null
crw-rw-rw- 1 root other 41, 0 Jan 21 18:58 udp
./etc:
total 16
-r-xr-xr-x 1 root other 482 Jan 21 19:20 TIMEZONE
-r--r--r-- 1 root other 74 Jan 21 19:20 hosts
-rw-r--r-- 1 root other 1239 Jan 21 19:20 netconfig
-rw-r--r-- 1 root other 1298 Jan 21 19:20 nsswitch.conf
-r--r--r-- 1 root other 514 Jan 21 19:44 passwd
-rw-r--r-- 1 root other 94 Jan 21 19:20 resolv.conf
drwx------ 2 root other 512 Jan 21 19:20 boa
./boa:
total 4
-rw-r--r-- 1 root other 1234 Jan 21 19:26 boa.conf
./opt:
total 2
drwxr-xr-x 2 root other 512 Jan 21 19:26 boa
./opt/boa:
total 20
-rw-r--r-- 1 root other 9964 Jan 21 19:26 mime.types
./usr:
total 6
drwxr-xr-x 2 root other 512 Jan 21 19:21 bin
drwxr-xr-x 2 root other 512 Jan 21 19:03 lib
drwxr-xr-x 3 root other 512 Jan 21 19:08 share
./usr/bin:
total 18
-rwxr-xr-x 1 root other 8944 Jan 21 19:23 boa_indexer
./usr/lib:
total 5094
-rwxr-xr-x 1 root other 185020 Jan 21 19:03 ld.so.1
-rwxr-xr-x 1 root other 1126652 Jan 21 18:56 libc.so.1
-rwxr-xr-x 1 root other 4308 Jan 21 18:56 libdl.so.1
-rwxr-xr-x 1 root other 24968 Jan 21 18:56 libmp.so.2
-rwxr-xr-x 1 root other 883500 Jan 21 18:56 libnsl.so.1
-rwxr-xr-x 1 root other 265860 Jan 21 18:56 libresolv.so.2
-rwxr-xr-x 1 root other 70260 Jan 21 18:56 libsocket.so.1
./usr/share:
total 2
drwxr-xr-x 3 root other 512 Jan 21 19:08 lib
./usr/share/lib:
total 2
drwxr-xr-x 3 root other 512 Jan 21 19:08 zoneinfo
./usr/share/lib/zoneinfo:
total 2
drwxr-xr-x 2 root other 512 Jan 21 19:09 Australia
./usr/share/lib/zoneinfo/Australia:
total 22
-rw-r--r-- 1 root other 785 Jan 21 19:09 ACT
-rw-r--r-- 1 root other 785 Jan 21 19:09 Broken_Hill
-rw-r--r-- 1 root other 663 Jan 21 19:09 LHI
-rw-r--r-- 1 root other 785 Jan 21 19:09 NSW
-rw-r--r-- 1 root other 104 Jan 21 19:09 North
-rw-r--r-- 1 root other 160 Jan 21 19:09 Queensland
-rw-r--r-- 1 root other 785 Jan 21 19:09 South
-rw-r--r-- 1 root other 825 Jan 21 19:09 Tasmania
-rw-r--r-- 1 root other 785 Jan 21 19:09 Victoria
-rw-r--r-- 1 root other 150 Jan 21 19:09 West
-rw-r--r-- 1 root other 785 Jan 21 19:09 Yancowinna
./var:
total 4
drwxr-xr-x 2 www www 512 Jan 21 19:44 log
drwxr-xr-x 2 root other 512 Jan 21 18:57 www
./var/log:
total 4
-rw-r--r-- 1 root other 202 Jan 21 19:47 access_log
-rw-r--r-- 1 root other 590 Jan 21 19:49 error_log
./var/www:
total 0
Note, your boa binary should be kept outside of the chroot jail as
they are not required.
The commandline issued to boa requires "-r /var/www" which tells
boa to chroot to /var/www before it does anything else, including
reading its configuration file.
That's all that's required. Start your new chrooting boa up and enjoy!

View File

@ -0,0 +1,183 @@
# Boa v0.94 configuration file
# File format has not changed from 0.93
# File format has changed little from 0.92
# version changes are noted in the comments
#
# The Boa configuration file is parsed with a lex/yacc or flex/bison
# generated parser. If it reports an error, the line number will be
# provided; it should be easy to spot. The syntax of each of these
# rules is very simple, and they can occur in any order. Where possible
# these directives mimic those of NCSA httpd 1.3; I saw no reason to
# introduce gratuitous differences.
# $Id: boa.conf,v 1.2 2001/09/25 03:28:31 jnelson Exp $
# The "ServerRoot" is not in this configuration file. It can be compiled
# into the server (see defines.h) or specified on the command line with
# the -c option, for example:
#
# boa -c /usr/local/boa
# Port: The port Boa runs on. The default port for http servers is 80.
# If it is less than 1024, the server must be started as root.
Port 80
# Listen: the Internet address to bind(2) to. If you leave it out,
# it takes the behavior before 0.93.17.2, which is to bind to all
# addresses (INADDR_ANY). You only get one "Listen" directive,
# if you want service on multiple IP addresses, you have three choices:
# 1. Run boa without a "Listen" directive
# a. All addresses are treated the same; makes sense if the addresses
# are localhost, ppp, and eth0.
# b. Use the VirtualHost directive below to point requests to different
# files. Should be good for a very large number of addresses (web
# hosting clients).
# 2. Run one copy of boa per IP address, each has its own configuration
# with a "Listen" directive. No big deal up to a few tens of addresses.
# Nice separation between clients.
# The name you provide gets run through inet_aton(3), so you have to use dotted
# quad notation. This configuration is too important to trust some DNS.
#Listen 192.68.0.5
# User: The name or UID the server should run as.
# Group: The group name or GID the server should run as.
User nobody
Group nobody
# ServerAdmin: The email address where server problems should be sent.
# Note: this is not currently used, except as an environment variable
# for CGIs.
#ServerAdmin root@localhost
# ErrorLog: The location of the error log file. If this does not start
# with /, it is considered relative to the server root.
# Set to /dev/null if you don't want errors logged.
# If unset, defaults to /dev/stderr
ErrorLog /var/log/boa/error_log
# Please NOTE: Sending the logs to a pipe ('|'), as shown below,
# is somewhat experimental and might fail under heavy load.
# "Usual libc implementations of printf will stall the whole
# process if the receiving end of a pipe stops reading."
#ErrorLog "|/usr/sbin/cronolog --symlink=/var/log/boa/error_log /var/log/boa/error-%Y%m%d.log"
# AccessLog: The location of the access log file. If this does not
# start with /, it is considered relative to the server root.
# Comment out or set to /dev/null (less effective) to disable
# Access logging.
AccessLog /var/log/boa/access_log
# Please NOTE: Sending the logs to a pipe ('|'), as shown below,
# is somewhat experimental and might fail under heavy load.
# "Usual libc implementations of printf will stall the whole
# process if the receiving end of a pipe stops reading."
#AccessLog "|/usr/sbin/cronolog --symlink=/var/log/boa/access_log /var/log/boa/access-%Y%m%d.log"
# VerboseCGILogs: this is just a logical switch.
# It simply notes the start and stop times of cgis in the error log
# Comment out to disable.
#VerboseCGILogs
# ServerName: the name of this server that should be sent back to
# clients if different than that returned by gethostname + gethostbyname
#ServerName www.your.org.here
# VirtualHost: a logical switch.
# Comment out to disable.
# Given DocumentRoot /var/www, requests on interface 'A' or IP 'IP-A'
# become /var/www/IP-A.
# Example: http://localhost/ becomes /var/www/127.0.0.1
#
# Not used until version 0.93.17.2. This "feature" also breaks commonlog
# output rules, it prepends the interface number to each access_log line.
# You are expected to fix that problem with a postprocessing script.
#VirtualHost
# DocumentRoot: The root directory of the HTML documents.
# Comment out to disable server non user files.
DocumentRoot /home/httpd/html
# 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 to use as a pre-written HTML
# directory index. Please MAKE AND USE THESE FILES. On the
# fly creation of directory indexes can be _slow_.
# Comment out to always use DirectoryMaker
DirectoryIndex index.html
# DirectoryMaker: Name of program used to create a directory listing.
# Comment out to disable directory listings. If both this and
# DirectoryIndex are commented out, accessing a directory will give
# an error (though accessing files in the directory are still ok).
DirectoryMaker /usr/lib/boa/boa_indexer
# DirectoryCache: If DirectoryIndex doesn't exist, and DirectoryMaker
# has been commented out, the the on-the-fly indexing of Boa can be used
# to generate indexes of directories. Be warned that the output is
# extremely minimal and can cause delays when slow disks are used.
# Note: The DirectoryCache must be writable by the same user/group that
# Boa runs as.
# DirectoryCache /var/spool/boa/dircache
# KeepAliveMax: Number of KeepAlive requests to allow per connection
# Comment out, or set to 0 to disable keepalive processing
KeepAliveMax 1000
# KeepAliveTimeout: seconds to wait before keepalive connection times out
KeepAliveTimeout 10
# MimeTypes: This is the file that is used to generate mime type pairs
# and Content-Type fields for boa.
# Set to /dev/null if you do not want to load a mime types file.
# Do *not* comment out (better use AddType!)
MimeTypes /etc/mime.types
# DefaultType: MIME type used if the file extension is unknown, or there
# is no file extension.
DefaultType text/plain
# AddType: adds types without editing mime.types
# Example: AddType type extension [extension ...]
# Uncomment the next line if you want .cgi files to execute from anywhere
#AddType application/x-httpd-cgi cgi
# Redirect, Alias, and ScriptAlias all have the same semantics -- they
# match the beginning of a request and take appropriate action. Use
# Redirect for other servers, Alias for the same server, and ScriptAlias
# to enable directories for script execution.
# 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.
# Example: Redirect /bar http://elsewhere/feh/bar
# Aliases: Aliases one path to another.
# Example: Alias /path1/bar /path2/foo
Alias /doc /usr/doc
# ScriptAlias: Maps a virtual path to a directory for serving scripts
# Example: ScriptAlias /htbin/ /www/htbin/
ScriptAlias /cgi-bin/ /home/httpd/cgi-bin/

View File

@ -0,0 +1,45 @@
#!/bin/sh
# The following two lines enable chkconfig(1) to manipulate this script
# chkconfig: 345 87 13
# description: Boa is a World Wide Web server. It is used to serve \
# HTML files and CGI.
# processname: boa
# config: /etc/boa/boa.conf
# There is no pid file
# Source function library.
. /etc/rc.d/init.d/functions
# See how we were called.
case "$1" in
start)
echo -n "Starting boa: "
daemon boa
touch /var/lock/subsys/boa
echo
;;
stop)
echo -n "Shutting down boa: "
killproc boa
echo
rm -f /var/lock/subsys/boa
rm -f /var/run/boa.pid
;;
status)
status boa
;;
restart)
$0 stop
$0 start
;;
reload)
echo -n "Reloading boa: "
killproc boa -HUP
echo
;;
*)
echo "Usage: $0 {start|stop|restart|reload|status}"
exit 1
esac
exit 0

View File

@ -0,0 +1,6 @@
/var/log/boa/*_log {
weekly
compress
copytruncate
missingok
}

View File

@ -0,0 +1,89 @@
Summary: a single-tasking high performance http server
Name: boa
Version: 0.94.9
Release: 1
Group: System Environment/Daemons
Source: http://www.boa.org/boa-%{version}.tar.gz
Copyright: GNU general public license
Requires: /etc/mime.types
Prereq: /sbin/chkconfig, man, gzip
Provides: setup webserver
Buildroot: /usr/tmp/boa
%description
Boa is a single-tasking HTTP server. That means that
unlike traditional web servers, it does not fork for each
incoming connection, nor does it fork many copies of
itself to handle multiple connections. It internally mul­
tiplexes all of the ongoing HTTP connections, and forks
only for CGI programs (which must be separate processes.)
Preliminary tests show Boa is more than twice as fast as
Apache.
Boa was created in 1991 by Paul Phillips <psp@well.com>. It is now being
maintained and enhanced by Larry Doolittle <ldoolitt@boa.org>
and Jon Nelson <jnelson@boa.org>.
For more information (including installation instructions) examine
the file docs/boa.txt or docs/boa.dvi, point your web browser to docs/boa.html,
or visit the Boa homepage at
http://www.boa.org/
%changelog
* Thu Aug 6 2000 Jonathon D Nelson <jnelson@boa.org>
- revamp packaging based upon examples provided by
Jules Stuifbergen <jules@zjuul.net> and others
%prep
%setup -T -b 0
%build
(cd src && CFLAGS=$RPM_OPT_FLAGS ./configure --prefix=$RPM_BUILD_ROOT)
(cd src && make)
(cd docs && gzip -c boa.8 > boa.8.gz)
(cd docs && make boa.html)
%clean
rm -rf $RPM_BUILD_ROOT
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/etc/{boa,logrotate.d}
mkdir -p $RPM_BUILD_ROOT/usr/sbin
mkdir -p $RPM_BUILD_ROOT/home/httpd/{html,cgi-bin}
mkdir -p $RPM_BUILD_ROOT/var/log/boa
mkdir -p $RPM_BUILD_ROOT/usr/lib/boa
mkdir -p $RPM_BUILD_ROOT/usr/man/man8
mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
install -m755 src/boa $RPM_BUILD_ROOT/usr/sbin/
install -m755 src/boa_indexer $RPM_BUILD_ROOT/usr/lib/boa/
install -m644 redhat/boa.conf $RPM_BUILD_ROOT/etc/boa/
install -m755 redhat/boa.init $RPM_BUILD_ROOT/etc/rc.d/init.d/boa
mv docs/boa.8.gz $RPM_BUILD_ROOT/usr/man/man8/
install -m644 redhat/boa.logrotate $RPM_BUILD_ROOT/etc/logrotate.d/boa
touch $RPM_BUILD_ROOT/var/log/boa/{error,access}_log
%post
/sbin/chkconfig boa reset
%preun
/etc/rc.d/init.d/boa stop
/sbin/chkconfig --del boa
%files
%defattr(-,root,root)
%dir /home/httpd/html
%dir /home/httpd/cgi-bin
%dir /var/log/boa
%doc Gnu_License README docs/*
%doc /usr/man/man8/*
%config /etc/boa/boa.conf
%config /etc/rc.d/init.d/boa
%config /etc/logrotate.d/boa
%attr(600,nobody,nobody)/var/log/boa/error_log
%attr(600,nobody,nobody)/var/log/boa/access_log
/usr/sbin/boa
/usr/lib/boa/boa_indexer

View File

@ -0,0 +1,31 @@
all: boa.html boa_toc.html boa.dvi boa.info boa.txt
boa.info: boa.texi
makeinfo --number-sections boa.texi
boa.dvi: boa.texi
texi2dvi --clean boa.texi
boa.pdf: boa.texi
texi2dvi --pdf --clean boa.texi
boa.ps: boa.dvi
dvips -o boa.ps boa.dvi
boa_toc.html: boa.texi
texi2html -split_chapter -menu boa.texi
boa.html: boa.texi
makeinfo --html --number-sections --no-split -o - boa.texi | \
sed -e 's/Node:.*//' | sed -e 's/Next:.*//' | \
sed -e 's/Previous:.*//' | sed -e 's/Up:.*//' > boa.html
boa.txt: boa.texi
makeinfo --no-headers --no-split -o boa.txt boa.texi
cleanup:
rm -f boa.{cp,fn,fns,ky,log,pg,toc,tp,vr,vrs,aux} *~
clean: cleanup
rm -f boa.{html,txt,dvi,ps,pdf,info} boa_*.html

View File

@ -0,0 +1,196 @@
.TH BOA 8 "Jan 22 2000" "Version 0.94"
.SH NAME
.B boa \- a single\-tasking high performance http server
.SH SYNOPSIS
.B boa
.RB [ -c
.IR server_root ]
.RB [ -r
.IR chroot ]
.RB [ -d ]
.SH DESCRIPTION
Boa is a single-tasking HTTP server. That means that unlike traditional web
servers, it does not fork for each incoming connection, nor does it fork many
copies of itself to handle multiple connections. It internally multiplexes all
of the ongoing HTTP connections, and forks only for CGI programs (which must be
separate processes.) Preliminary tests show Boa is more than twice as fast as
Apache.
.PP
The primary design goals of Boa are speed and security. Security, in the sense
of "can't be subverted by a malicious user", not "fine grained access control
and encrypted communications". Boa is not intended as a feature-packed server;
if you want one of those, check out WN from John Franks. Modifications to Boa
that improve its speed, security, robustness, and portability, are eagerly
sought. Other features may be added if they can be achieved without hurting the
primary goals.
.SH OPTIONS
.IP \fB-d\fR
instruct Boa not to fork itself (non-daemonize).
.IP "\fB-c \fIserver_root\fR"
choose a server root overriding the default SERVER_ROOT #define in
.I defines.h
The server root must hold your local copy of the configuration file
.IP "\fB-r \fIchroot\fR"
instruct Boa where to chdir and chroot to. The chdir/chroot
is done before the configuration file is read, or any log
files are opened.
.SH FILES
.TP
\fBboa.conf\fR \- the sole configuration file for Boa.
The directives in this file are defined in the
.B DIRECTIVES
section.
.TP
\fBmime.types\fR \- the
MimeTypes <filename>
defines what Content-Type Boa will send in an HTTP/1.0
or better transaction.
.SH DIRECTIVES
The Boa configuration file is parsed with a lex/yacc or flex/bison generated
parser. If it reports an error, the line number will be provided; it should
be easy to spot. The syntax of each of these rules is very simple, and they
can occur in any order. Where possible, these directives mimic those of NCSA
httpd 1.3; We saw no reason to introduce gratuitous differences.
.PP
Note: the "ServerRoot" is not in this configuration file. It can be compiled
into the server (see
.I defines.h
) or specified on the command line with the
.B -c
option.
The following directives are contained in the
.I boa.conf
file, and most, but not all, are required.
.TP
Port <integer>
This is the port that Boa runs on. The default port for http servers is 80.
If it is less than 1024, the server must be started as root.
.TP
User <user name or UID>
The name or UID the server should run as. For Boa to attempt this, the
server must be started as root.
.TP
Group <group name or GID>
The group name or GID the server should run as. For Boa to attempt this,
the server must be started as root.
.TP
ServerAdmin <email address>
The email address where server problems should be sent.
Note: this is not currently used.
.TP
ErrorLog <filename>
The location of the error log file. If this does not start with
/, it is considered relative to the server root.
Set to /dev/null if you don't want errors logged.
.TP
AccessLog <filename>
The location of the access log file. If this does not start with /, it is
considered relative to the server root.
Comment out or set to /dev/null (less effective) to disable access logging.
.TP
VerboseCGILogs
This is a logical switch and does not take any parameters.
Comment out to disable.
.TP
ServerName <server_name>
The name of this server that should be sent back to
clients if different than that returned by gethostname.
.Tp
VirtualHost
This is a logical switch and does not take any parameters.
Comment out to disable.
Given DocumentRoot /var/www, requests on interface 'A' or IP 'IP-A'
become /var/www/IP-A.
Example: http://localhost/ becomes /var/www/127.0.0.1
.TP
DocumentRoot <directory>
The root directory of the HTML documents. If this does not start with
/, it is considered relative to the server root.
.TP
UserDir <directory>
The name of the directory which is appended onto a user's home directory if a
~user request is received.
.TP
DirectoryIndex <filename>
Name of the file to use as a pre-written HTML directory index. Please make
and use these files. On the fly creation of directory indexes can be slow.
.TP
DirectoryMaker <directory>
Name of the program used to generate on-the-fly directory listings.
The program must take one or two command-line arguments, the first
being the directory to index (absolute), and the second, which is optional,
contains what Boa would have the "title" of the document be.
Comment out if you don't want on the fly directory listings.
If this does not start with
/, it is considered relative to the server root.
.TP
KeepAliveMax <integer>
Number of KeepAlive requests to allow per connection. Comment out, or set
to 0 to disable keepalive processing.
.TP
KeepAliveTimeout <integer>
Number of seconds to wait before keepalive connections time out.
.TP
MimeTypes <file>
The location of the
.I mime.types
file. If this does not start with /, it is considered relative to
the server root. Set to /dev/null if you do not want to load a mime types
file. Do *not* comment out (better use AddType!)
.TP
DefaultType <mime type>
MIME type used if the file extension is unknown, or there is no file extension.
.TP
AddType <mime type> <extension> [extension...]
Associates a MIME type with an extension or extensions.
.TP
Redirect, Alias, and ScriptAlias <path1> <path2>
Redirect, Alias, and ScriptAlias all have the same semantics \-\- they
match the beginning of a request and take appropriate action. Use
Redirect for other servers, Alias for the same server, and ScriptAlias to
enable directories for script execution.
Redirect allows you to tell clients about documents which used to exist
in your server's namespace, but do not anymore. This allows you tell
the clients where to look for the relocated document.
Alias aliases one path to another. Of course, symbolic links in the
file system work fine too.
ScriptAlias maps a virtual path to a directory for serving scripts.
.PP
Please see the included
.I boa.conf
for defaults and examples.
.SH HISTORY
Like the Linux kernel, even numbered versions are "stable", and odd numbered
versions are "unstable", or rather, "development".
Versions 0.91 and 0.91beta of Boa were released by Paul Phillips <paulp@go2net.com>
.PP
Version 0.92 was released by Larry Doolittle on
December 12, 1996.
.PP
Version 0.93 was the development version of 0.94.
.PP
Version 0.94 was released 22 Jan 2000.
.SH BUGS
There are probably bugs, but we are not aware of any at this time.
.SH AUTHOR
Boa was created by Paul Phillips <paulp@go2net.com>. It is now being maintained and
enhanced by Larry Doolittle
<ldoolitt@boa.org> and
Jon Nelson <jnelson@boa.org>.
.PP
Linux is the development platform at the moment, other
OS's are known to work. If you'd like to
contribute to this effort, contact Larry or Jon via e-mail.
.SH LICENSE
This program is distributed under the GNU General Public License, as noted in
each source file.

View File

@ -0,0 +1,772 @@
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename boa.info
@settitle The Boa HTTP Daemon
@set UPDATED Last Updated: 2 Jan 2001
@set COPYPHRASE Copyright @copyright{} 1996-2001 Jon Nelson and Larry Doolittle
@set VERSION $Revision: 1.5 $
@paragraphindent asis
@iftex
@parindent 0pt
@end iftex
@c @setchapternewpage odd
@c %**end of header
@iftex
@titlepage
@title The Boa HTTP Daemon
@c @sp 2
@end iftex
@ifinfo
This file documents Boa, an HTTP daemon for UN*X like machines.
@end ifinfo
@html
<h1 align="center">The Boa HTTP Daemon</h1>
<center><img src="boa_banner.png"></center>
@end html
@ifinfo
@dircategory Networking
@direntry
* Boa: (boa). The Boa Webserver
@end direntry
@end ifinfo
@comment node-name, next, previous, up
@node Top, Introduction, , (dir)
Welcome to the documentation for Boa, a high performance
HTTP Server for UN*X-alike computers, covered by the
@uref{Gnu_License,GNU General Public License}.
The on-line, updated copy of this documentation lives at
@uref{http://www.boa.org/,http://www.boa.org/}
@sp 1
@center @value{COPYPHRASE}
@center @value{UPDATED}, @value{VERSION}
@iftex
@end titlepage
@contents
@end iftex
@menu
* Introduction::
* Installation and Usage::
* Limits and Design Philosophy::
* Appendix::
-- Detailed Node Listing --
Installation
* Files Used by Boa::
* Compile-Time and Command-Line Options::
* boa.conf Directives::
* Security::
Limits and Design Philosophy
* Limits::
* Differences between Boa and other web servers::
* Unexpected Behavior::
Appendix
* License::
* Acknowledgments::
* Reference Documents::
* Other HTTP Servers::
* Benchmarks::
* Tools::
* Authors::
@end menu
@comment node-name, next, previous, up
@node Introduction, Installation and Usage,top,top
@chapter Introduction
Boa is a single-tasking HTTP server. That means that unlike
traditional web servers, it does not fork for each incoming
connection, nor does it fork many copies of itself to handle multiple
connections. It internally multiplexes all of the ongoing HTTP
connections, and forks only for CGI programs (which must be separate
processes), automatic directory generation, and automatic file
gunzipping. Preliminary tests show Boa is capable of
handling several thousand hits per second on a 300 MHz Pentium and
dozens of hits per second on a lowly 20 MHz 386/SX.
The primary design goals of Boa are speed and security. Security,
in the sense of @emph{can't be subverted by a malicious user,} not
@emph{fine grained access control and encrypted communications}.
Boa is not intended as a feature-packed server; if you want one of those,
check out
WN (@uref{http://hopf.math.nwu.edu/}) from John Franks.
Modifications to Boa that improve its speed, security, robustness, and
portability, are eagerly sought. Other features may be added if they
can be achieved without hurting the primary goals.
Boa was created in 1991 by Paul Phillips (@email{psp@@well.com}).
It is now being maintained and enhanced by Larry Doolittle
(@email{ldoolitt@@boa.org}) and Jon Nelson
(@email{jnelson@@boa.org}).
Please see the acknowledgement section for further
details.
GNU/Linux is the development platform at the moment, other OS's are known to work.
If you'd like to contribute to this effort, contact Larry or Jon via e-mail.
@comment node-name, next, previous, up
@node Installation and Usage, Limits and Design Philosophy, Introduction,top
@chapter Installation and Usage
Boa is currently being developed and tested on GNU/Linux/i386.
The code is straightforward (more so than most other servers),
so it should run easily on most modern Unix-alike platforms. Recent
versions of Boa worked fine on FreeBSD, SunOS 4.1.4, GNU/Linux-SPARC,
and HP-UX 9.0. Pre-1.2.0 GNU/Linux kernels may not work because of
deficient mmap() implementations.
@menu
* Installation::
* Files Used by Boa::
* Compile-Time and Command-Line Options::
* Security::
@end menu
@comment node-name, next, previous, up
@node Installation,Files Used by Boa,,Installation and Usage
@section Installation
@enumerate
@item Unpack
@enumerate
@item Choose, and cd into, a convenient directory for the package.
@item @kbd{tar -xvzf boa-0.94.tar.gz}, or for those of you with an archaic
(non-GNU) tar; @kbd{gzip -cd &lt; boa-0.94.tar.gz | tar -xvf -}
@item Read the documentation. Really.
@end enumerate
@item Build
@enumerate
@item cd into the @t{src} directory.
@item (optional) Change the default SERVER_ROOT by setting the #define
at the top of src/defines.h
@item Type @kbd{./configure; make}
@item Report any errors to the maintainers for resolution, or strike
out on your own.
@end enumerate
@item Configure
@enumerate
@item Choose a user and server port under which Boa can run. The
traditional port is 80, and user @t{nobody} (create if
you need to) is often a good selection for security purposes.
If you don't have (or choose not to use) root privileges, you
can not use port numbers less than 1024, nor can you switch user id.
@item Choose a server root. The @t{conf} directory within the
server root must hold your copy of the configuration file
@emph{boa.conf}
@item Choose locations for log files, CGI programs (if any), and
the base of your URL tree.
@item Set the location of the @t{mime.types} file.
@item Edit @emph{conf/boa.conf} according to your
choices above (this file documents itself). Read through this file
to see what other features you can configure.
@end enumerate
@item Start
@itemize
@item Start Boa. If you didn't build the right SERVER_ROOT into the
binary, you can specify it on the command line with the -c option
(command line takes precedence).
@example
Example: ./boa -c /usr/local/boa
@end example
@end itemize
@item Test
@itemize
@item At this point the server should run and serve documents.
If not, check the error_log file for clues.
@end itemize
@item Install
@itemize
@item Copy the binary to a safe place, and put the invocation into
your system startup scripts. Use the same -c option you used
in your initial tests.
@end itemize
@end enumerate
@comment node-name, next, previous, up
@node Files Used by Boa, Compile-Time and Command-Line Options, Installation,Installation and Usage
@section Files Used by Boa
@ftable @file
@item boa.conf
This file is the sole configuration file for Boa. The directives in this
file are defined in the DIRECTIVES section.
@item mime.types
The MimeTypes <filename> defines what Content-Type Boa will
send in an HTTP/1.0 or better transaction.
Set to /dev/null if you do not want to load a mime types file.
Do *not* comment out (better use AddType!)
@end ftable
@comment node-name, next, previous, up
@node Compile-Time and Command-Line Options, boa.conf Directives, Files Used by Boa,Installation and Usage
@section Compile-Time and Command-Line Options
@table @var
@item SERVER_ROOT
@itemx -c
The default server root as #defined by @var{SERVER_ROOT} in
@file{defines.h} can be overridden on the commandline using the
@option{-c} option. The server root must hold your local copy of the
configuration file @file{boa.conf}.
@example
Example: /usr/sbin/boa -c /etc/boa
@end example
@end table
@comment node-name, next, previous, up
@node boa.conf Directives, Security, Compile-Time and Command-Line Options, (top)
@section boa.conf Directives
The Boa configuration file is parsed with a lex/yacc or flex/bison
generated parser. If it reports an error, the line number will be
provided; it should be easy to spot. The syntax of each of these rules
is very simple, and they can occur in any order. Where possible, these
directives mimic those of NCSA httpd 1.3; I (Paul Phillips) saw no reason
to introduce gratuitous differences.
Note: the "ServerRoot" is not in this configuration file. It can be
compiled into the server (see @file{defines.h}) or specified on the command
line with the @command{-c} option.
The following directives are contained in the @file{boa.conf} file, and most,
but not all, are required.
@table @option
@item Port <Integer>
This is the port that Boa runs on. The default port for http servers is 80.
If it is less than 1024, the server must be started as root.
@item Listen <IP>
The Internet address to bind(2) to, in quadded-octet form (numbers).
If you leave it out, it binds to all addresses (INADDR_ANY).
The name you provide gets run through inet_aton(3), so you have to
use dotted quad notation. This configuration is too important to trust some DNS.
You only get one "Listen" directive, if you want service on multiple
IP addresses, you have three choices:
@enumerate
@item Run boa without a "Listen" directive:
@itemize @bullet
@item All addresses are treated the same; makes sense if the addresses
are localhost, ppp, and eth0.
@item Use the VirtualHost directive below to point requests to different files.
Should be good for a very large number of addresses (web hosting clients).
@end itemize
@item Run one copy of boa per IP address:
@itemize @bullet
@item Each instance has its own configuration with its own
"Listen" directive. No big deal up to a few tens of addresses. Nice separation
between clients.
@end itemize
@end enumerate
@item User <username or UID>
The name or UID the server should run as. For Boa to attempt this, the
server must be started as root.
@item Group <groupname or GID>
The group name or GID the server should run as. For Boa to attempt this,
the server must be started as root.
@item ServerAdmin <email address>
The email address where server problems should be sent. Note: this is not
currently used.
@item ErrorLog <filename>
The location of the error log file. If this does not start with /, it is
considered relative to the server root. Set to /dev/null if you don't want
errors logged.
@item AccessLog <filename>
The location of the access log file. If this does not start with /, it is
considered relative to the server root. Comment out or set to /dev/null
(less effective) to disable access logging.
@item VerboseCGILogs
This is a logical switch and does not take any parameters. Comment out to
disable. All it does is switch on or off logging of when CGIs are launched and when
the children return.
@item CgiLog <filename>
The location of the CGI error log file. If
specified, this is the file that the stderr of CGIs is tied to. Otherwise, writes
to stderr meet the bit bucket.
@item ServerName <server_name>
The name of this server that should be sent back to clients if different
than that returned by gethostname.
@item VirtualHost
This is a logical switch and does not take any parameters.
Comment out to disable. Given DocumentRoot /var/www, requests on interface `A' or
IP `IP-A' become /var/www/IP-A. Example: http://localhost/ becomes
/var/www/127.0.0.1
@item DocumentRoot <directory>
The root directory of the HTML documents. If this does not start with /,
it is considered relative to the server root.
@item UserDir <directory>
The name of the directory which is appended onto a user's home directory
if a ~user request is received.
@item DirectoryIndex <filename>
Name of the file to use as a pre-written HTML directory index. Please
make and use these files. On the fly creation of directory indexes
can be slow.
@item DirectoryMaker <full pathname to program>
Name of the program used
to generate on-the-fly directory listings. The program must take one or two
command-line arguments, the first being the directory to index (absolute), and the
second, which is optional, should be the "title" of the document be. Comment out if
you don't want on the fly directory listings. If this does not start with /, it is
considered relative to the server root.
@item DirectoryCache <directory>
DirectoryCache: If DirectoryIndex doesn't exist, and DirectoryMaker has been
commented out, the the on-the-fly indexing of Boa can be used to generate indexes
of directories. Be warned that the output is extremely minimal and can cause
delays when slow disks are used. Note: The DirectoryCache must be writable by the
same user/group that Boa runs as.
@item KeepAliveMax <integer>
Number of KeepAlive requests to allow per connection. Comment out, or set
to 0 to disable keepalive processing.
@item KeepAliveTimeout <integer>
Number of seconds to wait before keepalive connections time out.
@item MimeTypes <file>
The location of the mime.types file. If this does not start with /, it is
considered relative to the server root.
Comment out to avoid loading mime.types (better use AddType!)
@item DefaultType <mime type>
MIME type used if the file extension is unknown, or there is no file
extension.
@item AddType <mime type> <extension> extension...
Associates a MIME type
with an extension or extensions.
@item Redirect, Alias, and ScriptAlias
Redirect, Alias, and ScriptAlias all have the same semantics --
they match the beginning of a request and take appropriate action.
Use Redirect for other servers, Alias for the same server, and
ScriptAlias to enable directories for script execution.
@item Redirect <path1> <path2>
allows you to tell clients about documents which used to exist
in your server's namespace, but do not anymore. This allows you
tell the clients where to look for the relocated document.
@item Alias <path1> <path2>
aliases one path to another. Of course, symbolic links in the
file system work fine too.
@item ScriptAlias <path1> <path2>
maps a virtual path to a directory for serving scripts.
@end table
@comment node-name, next, previous, up
@node Security, , boa.conf Directives, Installation and Usage
@section Security
Boa has been designed to use the existing file system security. In
@file{boa.conf}, the directives @emph{user} and
@emph{group} determine who Boa will run as, if launched by root.
By default, the user/group is nobody/nogroup. This allows quite a bit
of flexibility. For example, if you want to disallow access to otherwise
accessible directories or files, simply make them inaccessible to
nobody/nogroup. If the user that Boa runs as is "boa" and the groups that
"boa" belongs to include "web-stuff" then files/directories accessible
by users with group "web-stuff" will also be accessible to Boa.
The February 2000 hoo-rah from
@uref{http://www.cert.org/advisories/CA-2000-02.html,CERT advisory CA-2000-02}
has little to do with Boa. As of version 0.94.4, Boa's escaping rules have
been cleaned up a little, but they weren't that bad before. The example CGI
programs have been updated to show what effort is needed there. If you
write, maintain, or use CGI programs under Boa (or any other server) it's
worth your while to read and understand this advisory. The real problem,
however, boils down to browser and web page designers emphasizing frills
over content and security. The market leading browsers assume (incorrectly)
that all web pages are trustworthy.
@comment node-name, next, previous, up
@node Limits and Design Philosophy,Appendix, Installation and Usage,top
@chapter Limits and Design Philosophy
There are many issues that become more difficult to resolve in a single
tasking web server than in the normal forking model. Here is a partial
list -- there are probably others that haven't been encountered yet.
@menu
* Limits::
* Differences between Boa and other web servers::
* Unexpected Behavior::
@end menu
@comment node-name, next, previous, up
@node Limits,Differences between Boa and other web servers,,Limits and Design Philosophy
@section Limits
@itemize @bullet
@item Slow file systems
The file systems being served should be much faster than the
network connection to the HTTP requests, or performance will suffer.
For instance, if a document is served from a CD-ROM, the whole server
(including all other currently incomplete data transfers) will stall
while the CD-ROM spins up. This is a consequence of the fact that Boa
mmap()'s each file being served, and lets the kernel read and cache
pages as best it knows how. When the files come from a local disk
(the faster the better), this is no problem, and in fact delivers
nearly ideal performance under heavy load. Avoid serving documents
from NFS and CD-ROM unless you have even slower inbound net
connections (e.g., POTS SLIP).
@item DNS lookups
Writing a nonblocking gethostbyaddr is a difficult and not very
enjoyable task. Paul Phillips experimented with several methods,
including a separate logging process, before removing hostname
lookups entirely. There is a companion program with Boa
@file{util/resolver.pl} that will postprocess the logfiles and
replace IP addresses with hostnames, which is much faster no matter
what sort of server you run.
@item Identd lookups
Same difficulties as hostname lookups; not included.
Boa provides a REMOTE_PORT environment variable, in addition
to REMOTE_ADDR, so that a CGI program can do its own ident.
See the end of @t{examples/cgi-test.cgi}.
@item Password file lookups via NIS
If users are allowed to serve HTML from their home directories,
password file lookups can potentially block the process. To lessen
the impact, each user's home directory is cached by Boa so it need
only be looked up once.
@item Running out of file descriptors
Since a file descriptor is needed for every ongoing connection
(two for non-nph CGIs, directories, and automatic gunzipping of files),
it is possible though highly improbable to run out of file
descriptors. The symptoms of this conditions may vary with
your particular unix variant, but you will probably see log
entries giving an error message for @t{accept}.
Try to build your kernel to give an adequate number for
your usage - GNU/Linux provides 256 out of the box, more than
enough for most people.
@end itemize
@comment node-name, next, previous, up
@node Differences between Boa and other web servers,Unexpected Behavior,Limits,Limits and Design Philosophy
@section Differences between Boa and other web servers
In the pursuit of speed and simplicity, some aspects of Boa differ
from the popular web servers. In no particular order:
@itemize @bullet
@item @var{REMOTE_HOST} environment variable not set for CGI programs
The @var{REMOTE_HOST} environment variable is not set for CGI programs,
for reasons already described. This is easily worked around because the
IP address is provided in the @var{REMOTE_HOST} variable, so (if the CGI
program actually cares) gethostbyaddr or a variant can be used.
@item There are no server side includes (@acronym{SSI}) in Boa
We don't like them, and they are too slow to parse. We will consider
more efficient alternatives.
@item There are no access control features
Boa will follow symbolic links, and serve any file that it can
read. The expectation is that you will configure Boa to run as user
"nobody", and only files configured world readable will come
out.
@item No chroot option
There is no option to run chrooted. If anybody wants this, and is
willing to try out experimental code, contact the maintainers.
@end itemize
@comment node-name, next, previous, up
@node Unexpected Behavior,,Differences between Boa and other web servers,Limits and Design Philosophy
@section Unexpected Behavior
@itemize @bullet
@item SIGHUP handling
Like any good server, Boa traps SIGHUP and rereads @file{boa.conf}.
However, under normal circumstances, it has already given away
permissions, so many items listed in @file{boa.conf} can not take effect.
No attempt is made to change uid, gid, log files, or server port.
All other configuration changes should take place smoothly.
@item Relative URL handling
Not all browsers handle relative URLs correctly. Boa will not
cover up for this browser bug, and will typically report 404 Not Found
for URL's containing odd combinations of "../" 's.
Note: As of version 0.95.0 (unreleased) the URL parser has been
rewritten and *does* correctly handle relative URLs.
@end itemize
@comment node-name, next, previous, up
@node Appendix,,Limits and Design Philosophy,top
@appendix Appendix
@menu
* License::
* Acknowledgments::
* Reference Documents::
* Other HTTP Servers::
* Benchmarks::
* Tools::
* Authors::
@end menu
@comment node-name, next, previous, up
@node License,Acknowledgments,,Appendix
@section License
This program is distributed under the
@uref{http://www.gnu.org/copyleft/gpl.html,GNU General Public License}.
as noted in each source file:
@*
@smallexample
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <psp@@well.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
@end smallexample
@comment node-name, next, previous, up
@node Acknowledgments,Reference Documents,License,Appendix
@section Acknowledgments
Paul Phillips wrote the first versions of Boa, up to and including
version 0.91. Version 0.92 of Boa was officially released December 1996
by Larry Doolittle. Version 0.93 was the development version of 0.94,
which was released in February 2000.
The Boa Webserver is currently (Feb 2000) maintained and enhanced by
Larry Doolittle (@email{ldoolitt@@boa.org})
and Jon Nelson (@email{jnelson@@boa.org}).
We would like to thank Russ Nelson (@email{nelson@@crynwr.com})
for hosting the @uref{http://www.boa.org,web site}.
We would also like to thank Paul Philips for writing code that is
worth maintaining and supporting.
Many people have contributed to Boa, including (but not
limited to) Charles F. Randall (@email{randall@@goldsys.com})
Christoph Lameter (@email{<chris@@waterf.org>}),
Russ Nelson (@email{<nelson@@crynwr.com>}), Alain Magloire
(@email{<alain.magloire@@rcsm.ee.mcgill.ca>}),
and more recently, M. Drew Streib (@email{<dtype@@linux.com>}).
Paul Phillips records his acknowledgments as follows:
@quotation
Thanks to everyone in the WWW community, in general a great bunch of people.
Special thanks to Clem Taylor (@email{<ctaylor@@eecis.udel.edu>}), who
provided invaluable feedback on many of my ideas, and offered good
ones of his own. Also thanks to John Franks, author of wn, for
writing what I believe is the best webserver out there.
@end quotation
@comment node-name, next, previous, up
@node Reference Documents,Other HTTP Servers,Acknowledgments,Appendix
@section Reference Documents
Links to documents relevant to
@uref{http://www.boa.org/,Boa}
development and usage. Incomplete, we're still working on this.
NCSA has a decent
@uref{http://hoohoo.ncsa.uiuc.edu/docs/Library.html,page} along
these lines, too.
Also see Yahoo's List
@* @uref{http://www.yahoo.com/Computers_and_Internet/Software/Internet/World_Wide_Web/Servers/}
@itemize
@item W3O HTTP page
@* @uref{http://www.w3.org/pub/WWW/Protocols/}
@item RFC 1945 HTTP-1.0 (informational)
@* @uref{http://ds.internic.net/rfc/rfc1945.txt}
@item IETF Working Group Draft 07 of HTTP-1.1
@* @uref{http://www.w3.org/pub/WWW/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-07.txt}
@item HTTP: A protocol for networked information
@* @uref{http://www.w3.org/pub/WWW/Protocols/HTTP/HTTP2.html}
@item The Common Gateway Interface (CGI)
@* @uref{http://hoohoo.ncsa.uiuc.edu/cgi/overview.html}
@item RFC 1738 URL syntax and semantics
@* @uref{http://ds.internic.net/rfc/rfc1738.txt}
@item RFC 1808 Relative URL syntax and semantics
@* @uref{http://ds.internic.net/rfc/rfc1808.txt}
@end itemize
@comment node-name, next, previous, up
@node Other HTTP Servers,Benchmarks,Reference Documents,Appendix
@section Other HTTP Servers
For unix-alike platforms, with published source code.
@itemize
@item tiny/turbo/throttling httpd very similar to Boa, with a throttling
feature
@* @uref{http://www.acme.com/software/thttpd/}
@item Roxen: based on ulpc interpreter, non-forking (interpreter implements
threading), GPL'd
@* @uref{http://www.roxen.com/}
@item WN: featureful, GPL'd
@* @uref{http://hopf.math.nwu.edu/}
@item Apache: fast, PD
@* @uref{http://www.apache.org/}
@item NCSA: standard, legal status?
@* @uref{http://hoohoo.ncsa.uiuc.edu/}
@item CERN: standard, PD, supports proxy
@* @uref{http://www.w3.org/pub/WWW/Daemon/Status.html}
@item xs-httpd 2.0: small, fast, pseudo-GPL'd
@* @uref{http://www.stack.nl/~sven/xs-httpd/}
@item bozohttpd.tar.gz sources, in perl
@* @uref{ftp://ftp.eterna.com.au/bozo/bsf/attware/bozohttpd.tar.gz}
@item Squid is actually an "Internet Object Cache"
@* @uref{http://squid.nlanr.net/Squid/}
@end itemize
Also worth mentioning is Zeus.
It is commercial, with a free demo, so it doesn't belong on the list above.
Zeus seems to be based on technology similar to Boa and thttpd,
but with more bells and whistles.
@* @uref{http://www.zeus.co.uk/products/server/}
@comment node-name, next, previous, up
@node Benchmarks,Tools,Other HTTP Servers,Appendix
@section Benchmarks
@itemize
@item ZeusBench (broken link)
@* @uref{http://www.zeus.co.uk/products/server/intro/bench2/zeusbench.shtml}
@item WebBench (binary-ware)
@* @uref{http://web1.zdnet.com/zdbop/webbench/webbench.html}
@item WebStone
@* @uref{http://www.mindcraft.com/benchmarks/webstone/}
@item SpecWeb96
@* @uref{http://www.specbench.org/osg/web96/}
@end itemize
@comment node-name, next, previous, up
@node Tools,Authors,Benchmarks,Appendix
@section Tools
@itemize
@item Analog logfile analyzer
@* @uref{http://www.statslab.cam.ac.uk/@~sret1/analog/}
@item wwwstat logfile analyzer
@* @uref{http://www.ics.uci.edu/pub/websoft/wwwstat/}
@item gwstat wwwstat postprocessor
@* @uref{http://dis.cs.umass.edu/stats/gwstat.html}
@item The Webalizer logfile analyzer
@* @uref{http://www.usagl.net/webalizer/}
@item cgiwrap
@* @uref{http://www.umr.edu/@~cgiwrap/}
@item suEXEC (Boa would need to be ..umm.. "adjusted" to support this)
@* @uref{http://www.apache.org/docs/suexec.html}
@end itemize
Note: References last checked: 06 October 1997
@comment node-name, next, previous, up
@node Authors,,Tools,Appendix
@section Authors
@itemize
@item Conversion from linuxdoc SGML to texinfo by Jon Nelson
@item Conversion to linuxdoc SGML by Jon Nelson
@item Original HTML documentation by Larry Doolittle
@item @value{COPYPHRASE}
@end itemize
@c variable
@c @printindex vr
@c concept
@c @printindex cp
@c function
@c @printindex fn
@c key
@c @printindex ky
@c program
@c @printindex pg
@c data type
@c @printindex tp
@bye

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,120 @@
#! /usr/bin/perl
# Remember that CGI programs have to close out the HTTP header
# (with a pair of newlines), after giving the Content-type:
# and any other relevant or available header information.
# Unlike CGI programs running under Apache, CGI programs under Boa
# should understand some simple HTTP options. The header (and the
# double-newline) should not be printed if the incoming request was
# in HTTP/0.9. Also, we should stop after the header if
# REQUEST_METHOD == "HEAD". Under Apache, nph- programs also have
# to worry about such stuff.
# Feb 3, 2000 -- updated to support POST, and avoid passing
# Malicious HTML Tags as described in CERT's CA-2000-02 advisory.
if ($ENV{"SERVER_PROTOCOL"} ne "HTTP/0.9") {
print "Content-type: text/html; charset=ISO-8859-1\r\n\r\n";
}
exit 0 if ($ENV{"REQUEST_METHOD"} eq "HEAD");
print "<html><head><title>Boa CGI test</title></head><body>\n";
print "<H2>Boa CGI test</H2>\n\n";
$now=`date`;
chomp($now);
print "Date: $now\n";
print "<p>\n";
print "Method: $ENV{\"REQUEST_METHOD\"}\n";
print "<p>\n";
print "<table border=1>\n";
print "<tr><td>Basic GET Form:<br>";
print " <form method=\"get\">\n\
<input type=\"text\" name=\"parameter_1\" size=5 maxlength=5>\
<select name=\"select_1\">\
<option>foo</option>\
<option>bar</option>\
</select>\
<input type=\"submit\" NAME=SUBMIT VALUE=\"Submit\">\
</form>";
print "</td>";
print "<td>Basic POST Form:<br>";
print "<form method=\"post\">\n\
<input type=\"text\" name=\"parameter_1\" size=5 maxlength=5>\
<select name=\"select_1\">\
<option>foo</option>\
<option>bar</option>\
</select>\
<input type=\"submit\" NAME=SUBMIT VALUE=\"Submit\">\
</form>";
print "</td>";
print "</tr>\n";
print "<tr><td colspan=2>Sample ISINDEX form:<br>\n";
print "<a href=\"$ENV{\"SCRIPT_NAME\"}?param1+param2+param3\">$ENV{\"SCRIPT_NAME\"}?param1+param2+param3</a>\n";
print "</td></tr>";
print "</table>\n";
print "<p>Query String: $ENV{\"QUERY_STRING\"}\n";
# arguments list
print "<p>\nArguments:\n<ol>\n";
if ($#ARGV >= 0) {
while ($a=shift(@ARGV)) {
$a=~s/&/&amp;/g;
$a=~s/</&lt;/g;
$a=~s/>/&gt;/g;
print "<li>$a\n";
}
}
print "</ol>\n";
# environment list
print "<P>\nEnvironment:\n<UL>\n";
foreach $i (keys %ENV) {
$a=$ENV{$i};
$a=~s/&/&amp;/g;
$a=~s/</&lt;/g;
$a=~s/>/&gt;/g;
$i=~s/&/&amp;/g;
$i=~s/</&lt;/g;
$i=~s/>/&gt;/g;
print "<li>$i = $a\n";
}
print "</UL>\n";
if ($ENV{REQUEST_METHOD} eq "POST") {
print "Input stream:<br><hr><pre>\n";
while (<stdin>) {
s/&/&amp;/g;
s/</&lt;/g;
s/>/&gt;/g;
print "$_";
}
print "</pre><hr>\n";
} else {
print "No input stream: (not POST)<p>";
}
print "id: ", `id`, "\n<p>\n";
if ($ENV{"QUERY_STRING"}=~/ident/ && $ENV{"REMOTE_PORT"} ne "") {
# Uses idlookup-1.2 from Peter Eriksson <pen@lysator.liu.se>
# ftp://coast.cs.purdue.edu/pub/tools/unix/ident/tools/idlookup-1.2.tar.gz
# Could use modification to timeout and trap stderr messages
$a="idlookup ".
$ENV{"REMOTE_ADDR"}." ".$ENV{"REMOTE_PORT"}." ".$ENV{"SERVER_PORT"};
$b=qx/$a/;
print "ident output:<br><pre>\n$b</pre>\n";
}
print "\n<EM>Boa http server</EM>\n";
print "</body></html>\n";
exit 0;

View File

@ -0,0 +1,69 @@
#!/usr/bin/perl
# Remember that CGI programs have to close out the HTTP header
# (with a pair of newlines), after giving the Content-type:
# and any other relevant or available header information.
# This test program always reports success (200 OK), and
# correctly uses SERVER_PROTOCOL and REQUEST_METHOD to decide
# whether or not to send the headers and content.
# Feb 3, 2000 -- updated to support POST, and avoid passing
# Malicious HTML Tags as described in CERT's CA-2000-02 advisory.
$now=`date`;
chomp($now);
if ($ENV{"SERVER_PROTOCOL"} ne "HTTP/0.9") {
print "HTTP/1.0 200 OK\r\nDate: $now\r\n";
print "Connection: close\r\n";
print "Content-type: text/html; charset=ISO-8859-1\r\n\r\n";
}
exit 0 if ($ENV{"REQUEST_METHOD"} eq "HEAD");
print "<html><head><title>Boa nph-CGI test</title></head><body>\n";
print "<H2>Boa nph-CGI test</H2>\n\n";
print "Date: $now\n";
print "<P>\n\n<UL>\n";
foreach (keys %ENV) {
$a= $ENV{$_};
$a=~s/&/&amp;/g;
$a=~s/</&lt;/g;
$a=~s/>/&gt;/g;
print "<LI>$_ == $a\n";
}
print "</UL>\n";
if ($ENV{REQUEST_METHOD} eq "POST") {
print "Input stream:<br><hr><pre>\n";
while (<stdin>) {
s/&/&amp;/g;
s/</&lt;/g;
s/>/&gt;/g;
print "$_";
}
print "</pre><hr>\n";
}
print "id: ", `id`, "\n<p>\n";
if ($ENV{"QUERY_STRING"}=~/ident/ && $ENV{"REMOTE_PORT"} ne "") {
# Uses idlookup-1.2 from Peter Eriksson <pen@lysator.liu.se>
# ftp://coast.cs.purdue.edu/pub/tools/unix/ident/tools/idlookup-1.2.tar.gz
# Could use modification to timeout and trap stderr messages
$a="idlookup ".
$ENV{"REMOTE_ADDR"}." ".$ENV{"REMOTE_PORT"}." ".$ENV{"SERVER_PORT"};
$b=qx/$a/;
print "ident output:<br><pre>\n$b</pre>\n";
}
print "\n<EM>Boa http server</EM>\n";
print "</body></html>\n";
exit 0;

View File

@ -0,0 +1,24 @@
#!/usr/bin/perl
# IP address resolver for Boa
# If you want an "in place" change to the log file,
# change the first line to
#!/usr/local/bin/perl -i.bak
# Otherwise, send the output of this program wherever you want:
# resolver.pl access_log >access_log_resolved
$AF_INET = 2;
while(<>) {
next unless (($ip, $rest) = /([\d\.]+) (.*)/o);
if(!$hosts{$ip}) {
$packed_ip = pack('C4', split(/\./, $ip));
$host = (gethostbyaddr($packed_ip, $AF_INET))[0];
$hosts{$ip} = ($host ? $host : $ip);
}
print "$hosts{$ip} $rest\n";
}

View File

@ -0,0 +1,7 @@
#include <string.h>
#include "compat.h"
int alphasort(const struct dirent **a, const struct dirent **b)
{
return (strcmp((*a)->d_name, (*b)->d_name));
}

View File

@ -0,0 +1,85 @@
/* -*- Mode: C; c-file-style: "gnu" -*- */
/*
Copyright (c) 2000 Petter Reinholdtsen
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
* scandir.c -- if scandir() is missing, make a replacement
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include "compat.h"
/*
* XXX This is a simple hack version which doesn't sort the data, and
* just passes all unsorted.
*/
int
scandir(const char *dir, struct dirent ***namelist,
int (*select) (const struct dirent *),
int (*compar) (const struct dirent **, const struct dirent **))
{
DIR *d = opendir(dir);
struct dirent *current;
struct dirent **names;
int count = 0;
int pos = 0;
int result = -1;
if (NULL == d)
return -1;
while (NULL != readdir(d))
count++;
names = malloc(sizeof (struct dirent *) * count);
closedir(d);
d = opendir(dir);
if (NULL == d)
return -1;
while (NULL != (current = readdir(d))) {
if (NULL == select || select(current)) {
struct dirent *copyentry = malloc(current->d_reclen);
memcpy(copyentry, current, current->d_reclen);
names[pos] = copyentry;
pos++;
}
}
result = closedir(d);
if (pos != count)
names = realloc(names, sizeof (struct dirent *) * pos);
*namelist = names;
return pos;
}

View File

@ -0,0 +1,36 @@
/*
* Name: strstr and strdup
*
* These are the standard library utilities. We define them here for
* systems that don't have them.
*/
#ifndef HAVE_STRSTR
char *strstr(char *s1, char *s2)
{ /* from libiberty */
char *p;
int len = strlen(s2);
if (*s2 == '\0') /* everything matches empty string */
return s1;
for (p = s1; (p = strchr(p, *s2)) != NULL; p = strchr(p + 1, *s2)) {
if (strncmp(p, s2, len) == 0)
return (p);
}
return NULL;
}
#endif
#ifndef HAVE_STRDUP
char *strdup(char *s)
{
char *retval;
retval = (char *) malloc(strlen(s) + 1);
if (retval == NULL) {
perror("boa: out of memory in strdup");
exit(1);
}
return strcpy(retval, s);
}
#endif

View File

@ -0,0 +1,28 @@
alias.o: alias.c boa.h config.h compat.h defines.h globals.h escape.h
boa.o: boa.c boa.h config.h compat.h defines.h globals.h escape.h
buffer.o: buffer.c boa.h config.h compat.h defines.h globals.h \
escape.h
cgi.o: cgi.c boa.h config.h compat.h defines.h globals.h escape.h
cgi_header.o: cgi_header.c boa.h config.h compat.h defines.h globals.h \
escape.h
config.o: config.c boa.h config.h compat.h defines.h globals.h \
escape.h y.tab.h parse.h
escape.o: escape.c boa.h config.h compat.h defines.h globals.h \
escape.h
get.o: get.c boa.h config.h compat.h defines.h globals.h escape.h
hash.o: hash.c boa.h config.h compat.h defines.h globals.h escape.h
ip.o: ip.c boa.h config.h compat.h defines.h globals.h escape.h
log.o: log.c boa.h config.h compat.h defines.h globals.h escape.h
mmap_cache.o: mmap_cache.c boa.h config.h compat.h defines.h globals.h \
escape.h
pipe.o: pipe.c boa.h config.h compat.h defines.h globals.h escape.h
queue.o: queue.c boa.h config.h compat.h defines.h globals.h escape.h
read.o: read.c boa.h config.h compat.h defines.h globals.h escape.h
request.o: request.c boa.h config.h compat.h defines.h globals.h \
escape.h
response.o: response.c boa.h config.h compat.h defines.h globals.h \
escape.h
signals.o: signals.c boa.h config.h compat.h defines.h globals.h \
escape.h
util.o: util.c boa.h config.h compat.h defines.h globals.h escape.h
sublog.o: sublog.c

View File

@ -0,0 +1,90 @@
# Generated automatically from Makefile.in by configure.
# $Id: Makefile.in,v 1.59 2002/03/24 22:20:19 jnelson Exp $
.SUFFIXES:
.SUFFIXES: .o .c
.PHONY: clean mrclean distclean depend all dist
GCC_FLAGS = -Wstrict-prototypes -Wpointer-arith -Wcast-align -Wcast-qual\
-Wtraditional\
-Wshadow\
-Wconversion\
-Waggregate-return\
-Wmissing-prototypes\
-Wnested-externs\
-Wall \
-Wundef -Wwrite-strings -Wredundant-decls -Winline
srcdir = .
VPATH = .:./../extras
LDFLAGS = -g
LIBS =
CFLAGS = -g -O2 -pipe -Wall -I.
# Change these if necessary
YACC = bison -y
LEX = lex
CC = gcc
CPP = gcc -E
SOURCES = alias.c boa.c buffer.c cgi.c cgi_header.c config.c escape.c \
get.c hash.c ip.c log.c mmap_cache.c pipe.c queue.c read.c \
request.c response.c select.c signals.c util.c sublog.c
OBJS = y.tab.o lex.yy.o $(SOURCES:.c=.o) timestamp.o
all: boa boa_indexer
boa: $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
boa_indexer: index_dir.o escape.o
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
clean:
rm -f $(OBJS) boa core lex.yy.c y.tab.c y.tab.h *~ boa_indexer index_dir.o
distclean: mrclean
mrclean: clean
rm -f config.status config.cache config.h Makefile config.log
# parser dependencies
y.tab.c y.tab.h: boa_grammar.y
$(YACC) -d $^
lex.yy.c: boa_lexer.l
$(LEX) $^
# timestamp
timestamp.o: $(SOURCES) boa_grammar.y boa_lexer.l
# depend stuff
.depend:
$(CPP) -MM $(SOURCES) > .depend
depend:
-rm -f .depend
$(MAKE) .depend
include .depend
# tags
tags: $(SOURCES)
ctags -o tags $^ *.h
# dist
dist:
$(MAKE) clean
./makedist.sh
# object dump
boa.objdump: boa
objdump --disassemble-all --source boa > $@

View File

@ -0,0 +1,89 @@
# $Id: Makefile.in,v 1.59 2002/03/24 22:20:19 jnelson Exp $
.SUFFIXES:
.SUFFIXES: .o .c
.PHONY: clean mrclean distclean depend all dist
@SET_MAKE@
GCC_FLAGS = -Wstrict-prototypes -Wpointer-arith -Wcast-align -Wcast-qual\
-Wtraditional\
-Wshadow\
-Wconversion\
-Waggregate-return\
-Wmissing-prototypes\
-Wnested-externs\
-Wall \
-Wundef -Wwrite-strings -Wredundant-decls -Winline
srcdir = @srcdir@
VPATH = @srcdir@:@srcdir@/../extras
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
CFLAGS = @CFLAGS@ -I.
# Change these if necessary
YACC = @YACC@
LEX = @LEX@
CC = @CC@
CPP = @CPP@
SOURCES = alias.c boa.c buffer.c cgi.c cgi_header.c config.c escape.c \
get.c hash.c ip.c log.c mmap_cache.c pipe.c queue.c read.c \
request.c response.c select.c signals.c util.c sublog.c
OBJS = y.tab.o lex.yy.o $(SOURCES:.c=.o) timestamp.o @STRUTIL@
all: boa boa_indexer
boa: $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
boa_indexer: index_dir.o escape.o @SCANDIR@ @ALPHASORT@ @STRUTIL@
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
clean:
rm -f $(OBJS) boa core lex.yy.c y.tab.c y.tab.h *~ boa_indexer index_dir.o @SCANDIR@ @ALPHASORT@ @STRUTIL@
distclean: mrclean
mrclean: clean
rm -f config.status config.cache config.h Makefile config.log
# parser dependencies
y.tab.c y.tab.h: boa_grammar.y
$(YACC) -d $^
lex.yy.c: boa_lexer.l
$(LEX) $^
# timestamp
timestamp.o: $(SOURCES) boa_grammar.y boa_lexer.l
# depend stuff
.depend:
$(CPP) -MM $(SOURCES) > .depend
depend:
-rm -f .depend
$(MAKE) .depend
include .depend
# tags
tags: $(SOURCES)
ctags -o tags $^ *.h
# dist
dist:
$(MAKE) clean
./makedist.sh
# object dump
boa.objdump: boa
objdump --disassemble-all --source boa > $@

View File

@ -0,0 +1,15 @@
/* define if GUNZIP found */
#undef GUNZIP
/* sockaddr_in has sin_len member */
#undef HAVE_SIN_LEN
/* how many times we shift left for unsigned long */
#undef NEEDS_ESCAPE_SHIFT
/* if struct tm has tm_gmtoff structure */
#undef HAVE_TM_GMTOFF
/* if struct tm has tm_zone structure */
#undef HAVE_TM_ZONE

View File

@ -0,0 +1,53 @@
dnl aclocal.m4 generated automatically by aclocal 1.4-p5
dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
dnl This program is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
dnl PARTICULAR PURPOSE.
AC_DEFUN([AC_CHECK_STRUCT_FOR],[
ac_safe_struct=`echo "$2" | sed 'y%./+-%__p_%'`
ac_safe_member=`echo "$3" | sed 'y%./+-%__p_%'`
ac_safe_all="ac_cv_struct_${ac_safe_struct}_has_${ac_safe_member}"
changequote(, )dnl
ac_uc_define=STRUCT_`echo "${ac_safe_struct}_HAS_${ac_safe_member}" | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
changequote([, ])dnl
AC_MSG_CHECKING([for $2.$3])
AC_CACHE_VAL($ac_safe_all,
[
if test "x$4" = "x"; then
defineit="= 0"
elif test "x$4" = "xno"; then
defineit=""
else
defineit="$4"
fi
AC_TRY_COMPILE([
$1
],[
struct $2 testit;
testit.$3 $defineit;
], eval "${ac_safe_all}=yes", eval "${ac_safe_all}=no" )
])
if eval "test \"x$`echo ${ac_safe_all}`\" = \"xyes\""; then
AC_MSG_RESULT(yes)
AC_DEFINE_UNQUOTED($ac_uc_define)
else
AC_MSG_RESULT(no)
fi
])
dnl AC_CHECK_STRUCT_FOR(INCLUDES,STRUCT,MEMBER,DEFINE,[no])
dnl 1.1 (2000/09/19)
dnl Wes Hardaker <wjhardaker@ucdavis.edu>
dnl ----------------------------------------------------------

View File

@ -0,0 +1,632 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996 Russ Nelson <nelson@crynwr.com>
* Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: alias.c,v 1.70.2.4 2002/07/28 02:46:52 jnelson Exp $ */
#include "boa.h"
static alias *alias_hashtable[ALIAS_HASHTABLE_SIZE];
int get_alias_hash_value(char *file);
/*
* Name: get_alias_hash_value
*
* Description: adds the ASCII values of the file letters
* and mods by the hashtable size to get the hash value
* Note: stops at first '/' (or '\0')
*/
int get_alias_hash_value(char *file)
{
unsigned int hash = 0;
unsigned int index = 0;
unsigned char c;
hash = file[index++];
while ((c = file[index++]) && c != '/')
hash += (unsigned int) c;
return hash % ALIAS_HASHTABLE_SIZE;
}
/*
* Name: add_alias
*
* Description: add an Alias, Redirect, or ScriptAlias to the
* alias hash table.
*/
void add_alias(char *fakename, char *realname, int type)
{
int hash;
alias *old, *new;
int fakelen, reallen;
/* sanity checking */
if (fakename == NULL || realname == NULL) {
DIE("NULL values sent to add_alias");
}
fakelen = strlen(fakename);
reallen = strlen(realname);
if (fakelen == 0 || reallen == 0) {
DIE("empty values sent to add_alias");
}
hash = get_alias_hash_value(fakename);
old = alias_hashtable[hash];
if (old) {
while (old->next) {
if (!strcmp(fakename, old->fakename)) /* don't add twice */
return;
old = old->next;
}
}
new = (alias *) malloc(sizeof (alias));
if (!new) {
DIE("out of memory adding alias to hash");
}
if (old)
old->next = new;
else
alias_hashtable[hash] = new;
new->fakename = strdup(fakename);
if (!new->fakename) {
DIE("failed strdup");
}
new->fake_len = fakelen;
/* check for "here" */
if (realname[0] == '.' && realname[1] == '/') {
new->realname = normalize_path(realname+2);
if (!new->realname) {
/* superfluous - normalize_path checks for NULL return values. */
DIE("normalize_path returned NULL");
}
reallen = strlen(new->realname);
} else {
new->realname = strdup(realname);
if (!new->realname) {
DIE("strdup of realname failed");
}
}
new->real_len = reallen;
new->type = type;
new->next = NULL;
}
/*
* Name: find_alias
*
* Description: Locates uri in the alias hashtable if it exists.
*
* Returns:
*
* alias structure or NULL if not found
*/
alias *find_alias(char *uri, int urilen)
{
alias *current;
int hash;
/* Find ScriptAlias, Alias, or Redirect */
if (urilen == 0)
urilen = strlen(uri);
hash = get_alias_hash_value(uri);
current = alias_hashtable[hash];
while (current) {
#ifdef FASCIST_LOGGING
fprintf(stderr,
"%s:%d - comparing \"%s\" (request) to \"%s\" (alias): ",
__FILE__, __LINE__, uri, current->fakename);
#endif
/* current->fake_len must always be:
* shorter or equal to the uri
*/
/*
* when performing matches:
* If the virtual part of the url ends in '/', and
* we get a match, stop there.
* Otherwise, we require '/' or '\0' at the end of the url.
* We only check if the virtual path does *not* end in '/'
*/
if (current->fake_len <= urilen &&
!memcmp(uri, current->fakename, current->fake_len) &&
(current->fakename[current->fake_len - 1] == '/' ||
(current->fakename[current->fake_len - 1] != '/' &&
(uri[current->fake_len] == '\0' ||
uri[current->fake_len] == '/')))) {
#ifdef FASCIST_LOGGING
fprintf(stderr, "Got it!\n");
#endif
return current;
}
#ifdef FASCIST_LOGGING
else
fprintf(stderr, "Don't Got it!\n");
#endif
current = current->next;
}
return current;
}
/*
* Name: translate_uri
*
* Description: Parse a request's virtual path.
* Sets query_string, pathname directly.
* Also sets path_info, path_translated, and script_name via
* init_script_alias
*
* Note: NPH in user dir is currently broken
*
* Note -- this should be broken up.
*
* Return values:
* 0: failure, close it down
* 1: success, continue
*/
int translate_uri(request * req)
{
static char buffer[MAX_HEADER_LENGTH + 1];
char *req_urip;
alias *current;
char *p;
int uri_len;
req_urip = req->request_uri;
if (req_urip[0] != '/') {
send_r_bad_request(req);
return 0;
}
uri_len = strlen(req->request_uri);
current = find_alias(req->request_uri, uri_len);
if (current) {
if (current->type == SCRIPTALIAS) /* Script */
return init_script_alias(req, current, uri_len);
/* not a script alias, therefore begin filling in data */
{
int len;
len = current->real_len;
len += uri_len - current->fake_len;
if (len > MAX_HEADER_LENGTH) {
log_error_doc(req);
fputs("uri too long!\n", stderr);
send_r_bad_request(req);
return 0;
}
memcpy(buffer, current->realname, current->real_len);
memcpy(buffer + current->real_len,
req->request_uri + current->fake_len,
uri_len - current->fake_len + 1);
}
if (current->type == REDIRECT) { /* Redirect */
if (req->method == M_POST) { /* POST to non-script */
/* it's not a cgi, but we try to POST??? */
send_r_bad_request(req);
return 0; /* not a script alias, therefore begin filling in data */
}
send_r_moved_temp(req, buffer, "");
return 0;
} else { /* Alias */
req->pathname = strdup(buffer);
if (!req->pathname) {
send_r_error(req);
WARN("unable to strdup buffer onto req->pathname");
return 0;
}
return 1;
}
}
/*
The reason why this is *not* an 'else if' is that,
after aliasing, we still have to check for '~' expansion
*/
if (user_dir && req->request_uri[1] == '~') {
char *user_homedir;
req_urip = req->request_uri + 2;
/* since we have uri_len which is from strlen(req->request_uri) */
p = memchr(req_urip, '/', uri_len - 2);
if (p)
*p = '\0';
user_homedir = get_home_dir(req_urip);
if (p) /* have to restore request_uri in case of error */
*p = '/';
if (!user_homedir) { /*no such user */
send_r_not_found(req);
return 0;
}
{
int l1 = strlen(user_homedir);
int l2 = strlen(user_dir);
int l3 = (p ? strlen(p) : 0);
if (l1 + l2 + l3 + 1 > MAX_HEADER_LENGTH) {
log_error_doc(req);
fputs("uri too long!\n", stderr);
send_r_bad_request(req);
return 0;
}
memcpy(buffer, user_homedir, l1);
buffer[l1] = '/';
memcpy(buffer + l1 + 1, user_dir, l2 + 1);
if (p)
memcpy(buffer + l1 + 1 + l2, p, l3 + 1);
}
} else if (document_root) {
/* no aliasing, no userdir... */
int l1, l2, l3;
l1 = strlen(document_root);
l2 = strlen(req->request_uri);
if (virtualhost)
l3 = strlen(req->local_ip_addr);
else
l3 = 0;
if (l1 + l2 + l3 + 1 > MAX_HEADER_LENGTH) {
log_error_doc(req);
fputs("uri too long!\n", stderr);
send_r_bad_request(req);
return 0;
}
/* the 'l2 + 1' is there so we copy the '\0' as well */
memcpy(buffer, document_root, l1);
if (virtualhost) {
buffer[l1] = '/';
memcpy(buffer + l1 + 1, req->local_ip_addr, l3);
memcpy(buffer + l1 + 1 + l3, req->request_uri, l2 + 1);
} else
memcpy(buffer + l1, req->request_uri, l2 + 1);
} else {
/* not aliased. not userdir. not part of document_root. BAIL */
send_r_not_found(req);
return 0;
}
/* if here,
* o it may be aliased but it's not a redirect or a script...
* o it may be a homedir
* o it may be a document_root resource (with or without virtual host)
*/
req->pathname = strdup(buffer);
if (!req->pathname) {
WARN("Could not strdup buffer for req->pathname!");
send_r_error(req);
return 0;
}
/* below we support cgis outside of a ScriptAlias */
if (strcmp(CGI_MIME_TYPE, get_mime_type(buffer)) == 0) { /* cgi */
#ifdef FASCIST_LOGGING
log_error_time();
fprintf(stderr, "%s:%d - buffer is: \"%s\"\n",
__FILE__, __LINE__, buffer);
#endif
/* FIXME */
/* script_name could end up as /cgi-bin/bob/extra_path */
req->script_name = strdup(req->request_uri);
if (!req->script_name) {
WARN("Could not strdup req->request_uri for req->script_name");
send_r_error(req);
return 0;
}
if (req->simple)
req->is_cgi = NPH;
else
req->is_cgi = CGI;
return 1;
} else if (req->method == M_POST) { /* POST to non-script */
/* it's not a cgi, but we try to POST??? */
send_r_bad_request(req);
return 0;
} else /* we are done!! */
return 1;
}
/*
* Name: init_script_alias
*
* Description: Performs full parsing on a ScriptAlias request
* Sets path_info and script_name
*
* Return values:
*
* 0: failure, shut down
* 1: success, continue
*/
int init_script_alias(request * req, alias * current1, int uri_len)
{
static char pathname[MAX_HEADER_LENGTH + 1];
struct stat statbuf;
static char buffer[MAX_HEADER_LENGTH + 1];
int index = 0;
char c;
int err;
/* copies the "real" path + the non-alias portion of the
uri to pathname.
*/
if (uri_len - current1->fake_len + current1->real_len >
MAX_HEADER_LENGTH) {
log_error_doc(req);
fputs("uri too long!\n", stderr);
send_r_bad_request(req);
return 0;
}
memcpy(pathname, current1->realname, current1->real_len);
memcpy(pathname + current1->real_len,
&req->request_uri[current1->fake_len],
uri_len - current1->fake_len + 1); /* the +1 copies the NUL */
#ifdef FASCIST_LOGGING
log_error_time();
fprintf(stderr,
"%s:%d - pathname in init_script_alias is: \"%s\" (\"%s\")\n",
__FILE__, __LINE__, pathname, pathname + current1->real_len);
#endif
if (strncmp("nph-", pathname + current1->real_len, 4) == 0
|| req->simple) req->is_cgi = NPH;
else
req->is_cgi = CGI;
/* start at the beginning of the actual uri...
(in /cgi-bin/bob, start at the 'b' in bob */
index = current1->real_len;
/* go to first and successive '/' and keep checking
* if it is a full pathname
* on success (stat (not lstat) of file is a *regular file*)
*/
do {
c = pathname[++index];
if (c == '/') {
pathname[index] = '\0';
err = stat(pathname, &statbuf);
pathname[index] = '/';
if (err == -1) {
send_r_not_found(req);
return 0;
}
/* is it a dir? */
if (!S_ISDIR(statbuf.st_mode)) {
/* check access */
if (!(statbuf.st_mode &
(S_IFREG | /* regular file */
(S_IRUSR | S_IXUSR) | /* u+rx */
(S_IRGRP | S_IXGRP) | /* g+rx */
(S_IROTH | S_IXOTH)))) { /* o+rx */
send_r_forbidden(req);
return 0;
}
/* stop here */
break;
}
}
} while (c != '\0');
req->script_name = strdup(req->request_uri);
if (!req->script_name) {
send_r_error(req);
WARN("unable to strdup req->request_uri for req->script_name");
return 0;
}
if (c == '\0') {
err = stat(pathname, &statbuf);
if (err == -1) {
send_r_not_found(req);
return 0;
}
/* is it a dir? */
if (!S_ISDIR(statbuf.st_mode)) {
/* check access */
if (!(statbuf.st_mode &
(S_IFREG | /* regular file */
(S_IRUSR | S_IXUSR) | /* u+rx */
(S_IRGRP | S_IXGRP) | /* g+rx */
(S_IROTH | S_IXOTH)))) { /* o+rx */
send_r_forbidden(req);
return 0;
}
/* stop here */
} else {
send_r_forbidden(req);
return 0;
}
}
/* we have path_info if c == '/'... still have to check for query */
else if (c == '/') {
int hash;
alias *current;
int path_len;
req->path_info = strdup(pathname + index);
if (!req->path_info) {
send_r_error(req);
WARN("unable to strdup pathname + index for req->path_info");
return 0;
}
pathname[index] = '\0'; /* strip path_info from path */
path_len = strlen(req->path_info);
/* we need to fix script_name here */
/* index points into pathname, which is
* realname/cginame/foo
* and index points to the '/foo' part
*/
req->script_name[strlen(req->script_name) - path_len] = '\0'; /* zap off the /foo part */
/* now, we have to re-alias the extra path info....
this sucks.
*/
hash = get_alias_hash_value(req->path_info);
current = alias_hashtable[hash];
while (current && !req->path_translated) {
if (!strncmp(req->path_info, current->fakename,
current->fake_len)) {
if (current->real_len +
path_len - current->fake_len > MAX_HEADER_LENGTH) {
log_error_doc(req);
fputs("uri too long!\n", stderr);
send_r_bad_request(req);
return 0;
}
memcpy(buffer, current->realname, current->real_len);
strcpy(buffer + current->real_len,
&req->path_info[current->fake_len]);
req->path_translated = strdup(buffer);
if (!req->path_translated) {
send_r_error(req);
WARN("unable to strdup buffer for req->path_translated");
return 0;
}
}
current = current->next;
}
/* no alias... try userdir */
if (!req->path_translated && user_dir && req->path_info[1] == '~') {
char *user_homedir;
char *p;
p = strchr(pathname + index + 1, '/');
if (p)
*p = '\0';
user_homedir = get_home_dir(pathname + index + 2);
if (p)
*p = '/';
if (!user_homedir) { /* no such user */
send_r_not_found(req);
return 0;
}
{
int l1 = strlen(user_homedir);
int l2 = strlen(user_dir);
int l3;
if (p)
l3 = strlen(p);
else
l3 = 0;
req->path_translated = malloc(l1 + l2 + l3 + 2);
if (req->path_translated == NULL) {
send_r_error(req);
WARN("unable to malloc memory for req->path_translated");
return 0;
}
memcpy(req->path_translated, user_homedir, l1);
req->path_translated[l1] = '/';
memcpy(req->path_translated + l1 + 1, user_dir, l2 + 1);
if (p)
memcpy(req->path_translated + l1 + 1 + l2, p, l3 + 1);
}
}
if (!req->path_translated && document_root) {
/* no userdir, no aliasing... try document root */
int l1, l2;
l1 = strlen(document_root);
l2 = path_len;
req->path_translated = malloc(l1 + l2 + 1);
if (req->path_translated == NULL) {
send_r_error(req);
WARN("unable to malloc memory for req->path_translated");
return 0;
}
memcpy(req->path_translated, document_root, l1);
memcpy(req->path_translated + l1, req->path_info, l2 + 1);
}
}
req->pathname = strdup(pathname);
if (!req->pathname) {
send_r_error(req);
WARN("unable to strdup pathname for req->pathname");
return 0;
}
return 1;
}
/*
* Empties the alias hashtable, deallocating any allocated memory.
*/
void dump_alias(void)
{
int i;
alias *temp;
for (i = 0; i < ALIAS_HASHTABLE_SIZE; ++i) { /* these limits OK? */
if (alias_hashtable[i]) {
temp = alias_hashtable[i];
while (temp) {
alias *temp_next;
if (temp->fakename)
free(temp->fakename);
if (temp->realname)
free(temp->realname);
temp_next = temp->next;
free(temp);
temp = temp_next;
}
alias_hashtable[i] = NULL;
}
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,277 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996 Charles F. Randall <crandall@goldsys.com>
* Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: boa.c,v 1.99.2.2 2002/07/23 15:50:29 jnelson Exp $*/
#include "boa.h"
#include <sys/resource.h>
/* globals */
int backlog = SO_MAXCONN;
time_t start_time;
int sighup_flag = 0; /* 1 => signal has happened, needs attention */
int sigchld_flag = 0; /* 1 => signal has happened, needs attention */
int sigalrm_flag = 0; /* 1 => signal has happened, needs attention */
int sigterm_flag = 0; /* lame duck mode */
time_t current_time;
int max_fd = 0;
int pending_requests = 0;
/* static to boa.c */
static void fixup_server_root(void);
static int create_server_socket(void);
static void drop_privs(void);
static int sock_opt = 1;
static int do_fork = 1;
int devnullfd = -1;
int main(int argc, char **argv)
{
int c; /* command line arg */
int server_s; /* boa socket */
/* set umask to u+rw, u-x, go-rwx */
c = umask(~0600);
if (c == -1) {
perror("umask");
exit(1);
}
devnullfd = open("/dev/null", 0);
/* make STDIN and STDOUT point to /dev/null */
if (devnullfd == -1) {
DIE("can't open /dev/null");
}
if (dup2(devnullfd, STDIN_FILENO) == -1) {
DIE("can't dup2 /dev/null to STDIN_FILENO");
}
if (dup2(devnullfd, STDOUT_FILENO) == -1) {
DIE("can't dup2 /dev/null to STDOUT_FILENO");
}
/* but first, update timestamp, because log_error_time uses it */
(void) time(&current_time);
while ((c = getopt(argc, argv, "c:r:d")) != -1) {
switch (c) {
case 'c':
if (server_root)
free(server_root);
server_root = strdup(optarg);
if (!server_root) {
perror("strdup (for server_root)");
exit(1);
}
break;
case 'r':
if (chdir(optarg) == -1) {
log_error_time();
perror("chdir (to chroot)");
exit(1);
}
if (chroot(optarg) == -1) {
log_error_time();
perror("chroot");
exit(1);
}
if (chdir("/") == -1) {
log_error_time();
perror("chdir (after chroot)");
exit(1);
}
break;
case 'd':
do_fork = 0;
break;
default:
fprintf(stderr, "Usage: %s [-c serverroot] [-r chroot] [-d]\n", argv[0]);
exit(1);
}
}
fixup_server_root();
read_config_files();
open_logs();
server_s = create_server_socket();
init_signals();
drop_privs();
create_common_env();
build_needs_escape();
if (max_connections < 1) {
struct rlimit rl;
/* has not been set explicitly */
c = getrlimit(RLIMIT_NOFILE, &rl);
if (c < 0) {
perror("getrlimit");
exit(1);
}
max_connections = rl.rlim_cur;
}
/* background ourself */
if (do_fork) {
switch(fork()) {
case -1:
/* error */
perror("fork");
exit(1);
break;
case 0:
/* child, success */
break;
default:
/* parent, success */
exit(0);
break;
}
}
/* main loop */
timestamp();
status.requests = 0;
status.errors = 0;
start_time = current_time;
select_loop(server_s);
return 0;
}
static int create_server_socket(void)
{
int server_s;
server_s = socket(SERVER_AF, SOCK_STREAM, IPPROTO_TCP);
if (server_s == -1) {
DIE("unable to create socket");
}
/* server socket is nonblocking */
if (set_nonblock_fd(server_s) == -1) {
DIE("fcntl: unable to set server socket to nonblocking");
}
/* close server socket on exec so cgi's can't write to it */
if (fcntl(server_s, F_SETFD, 1) == -1) {
DIE("can't set close-on-exec on server socket!");
}
/* reuse socket addr */
if ((setsockopt(server_s, SOL_SOCKET, SO_REUSEADDR, (void *) &sock_opt,
sizeof (sock_opt))) == -1) {
DIE("setsockopt");
}
/* internet family-specific code encapsulated in bind_server() */
if (bind_server(server_s, server_ip) == -1) {
DIE("unable to bind");
}
/* listen: large number just in case your kernel is nicely tweaked */
if (listen(server_s, backlog) == -1) {
DIE("unable to listen");
}
return server_s;
}
static void drop_privs(void)
{
/* give away our privs if we can */
if (getuid() == 0) {
struct passwd *passwdbuf;
passwdbuf = getpwuid(server_uid);
if (passwdbuf == NULL) {
DIE("getpwuid");
}
if (initgroups(passwdbuf->pw_name, passwdbuf->pw_gid) == -1) {
DIE("initgroups");
}
if (setgid(server_gid) == -1) {
DIE("setgid");
}
if (setuid(server_uid) == -1) {
DIE("setuid");
}
/* test for failed-but-return-was-successful setuid
* http://www.securityportal.com/list-archive/bugtraq/2000/Jun/0101.html
*/
/*if (setuid(0) != -1) {
DIE("icky Linux kernel bug!");
}*/
} else {
if (server_gid || server_uid) {
log_error_time();
fprintf(stderr, "Warning: "
"Not running as root: no attempt to change"
" to uid %d gid %d\n", server_uid, server_gid);
}
server_gid = getgid();
server_uid = getuid();
}
}
/*
* Name: fixup_server_root
*
* Description: Makes sure the server root is valid.
*
*/
static void fixup_server_root()
{
char *dirbuf;
if (!server_root) {
#ifdef SERVER_ROOT
server_root = strdup(SERVER_ROOT);
if (!server_root) {
perror("strdup (SERVER_ROOT)");
exit(1);
}
#else
fputs("boa: don't know where server root is. Please #define "
"SERVER_ROOT in boa.h\n"
"and recompile, or use the -c command line option to "
"specify it.\n", stderr);
exit(1);
#endif
}
if (chdir(server_root) == -1) {
fprintf(stderr, "Could not chdir to \"%s\": aborting\n",
server_root);
exit(1);
}
dirbuf = normalize_path(server_root);
free(server_root);
server_root = dirbuf;
}

View File

@ -0,0 +1,195 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996-99 Larry Doolittle <ldoolitt@jlab.org>
* Some changes Copyright (C) 1997-99 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: boa.h,v 1.63.2.2 2002/07/26 03:03:44 jnelson Exp $*/
#ifndef _BOA_H
#define _BOA_H
#include <errno.h>
#include <stdlib.h> /* malloc, free, etc. */
#include <stdio.h> /* stdin, stdout, stderr */
#include <string.h> /* strdup */
#include <ctype.h>
#include <time.h> /* localtime, time */
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h> /* OPEN_MAX */
#include <setjmp.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/types.h> /* socket, bind, accept */
#include <sys/socket.h> /* socket, bind, accept, setsockopt, */
#include <sys/stat.h> /* open */
#include "compat.h" /* oh what fun is porting */
#include "defines.h"
#include "globals.h"
/* alias */
void add_alias(char *fakename, char *realname, int script);
int translate_uri(request * req);
int init_script_alias(request * req, alias * current, int uri_len);
void dump_alias(void);
/* config */
void read_config_files(void);
/* escape */
#include "escape.h"
/* get */
int init_get(request * req);
int process_get(request * req);
int get_dir(request * req, struct stat *statbuf);
/* hash */
unsigned get_mime_hash_value(char *extension);
char *get_mime_type(char *filename);
char *get_home_dir(char *name);
void dump_mime(void);
void dump_passwd(void);
void show_hash_stats(void);
/* log */
void open_logs(void);
void close_access_log(void);
void log_access(request * req);
void log_error_doc(request * req);
void boa_perror(request * req, char *message);
void log_error_time(void);
void log_error_mesg(char *file, int line, char *mesg);
/* queue */
void block_request(request * req);
void ready_request(request * req);
void dequeue(request ** head, request * req);
void enqueue(request ** head, request * req);
/* read */
int read_header(request * req);
int read_body(request * req);
int write_body(request * req);
/* request */
request *new_request(void);
void get_request(int);
void process_requests(int server_s);
int process_header_end(request * req);
int process_header_line(request * req);
int process_logline(request * req);
int process_option_line(request * req);
void add_accept_header(request * req, char *mime_type);
void free_requests(void);
/* response */
void print_ka_phrase(request * req);
void print_content_type(request * req);
void print_content_length(request * req);
void print_last_modified(request * req);
void print_http_headers(request * req);
void send_r_request_ok(request * req); /* 200 */
void send_r_moved_perm(request * req, char *url); /* 301 */
void send_r_moved_temp(request * req, char *url, char *more_hdr); /* 302 */
void send_r_not_modified(request * req); /* 304 */
void send_r_bad_request(request * req); /* 400 */
void send_r_unauthorized(request * req, char *name); /* 401 */
void send_r_forbidden(request * req); /* 403 */
void send_r_not_found(request * req); /* 404 */
void send_r_error(request * req); /* 500 */
void send_r_not_implemented(request * req); /* 501 */
void send_r_bad_gateway(request * req); /* 502 */
void send_r_service_unavailable(request * req); /* 503 */
void send_r_bad_version(request * req); /* 505 */
/* cgi */
void create_common_env(void);
void clear_common_env(void);
int add_cgi_env(request * req, char *key, char *value, int http_prefix);
int complete_env(request * req);
void create_argv(request * req, char **aargv);
int init_cgi(request * req);
/* signals */
void init_signals(void);
void sighup_run(void);
void sigchld_run(void);
void sigalrm_run(void);
void sigterm_stage1_run(int);
void sigterm_stage2_run();
/* util.c */
void clean_pathname(char *pathname);
char *get_commonlog_time(void);
void rfc822_time_buf(char *buf, time_t s);
char *simple_itoa(unsigned int i);
int boa_atoi(char *s);
char *escape_string(char *inp, char *buf);
int month2int(char *month);
int modified_since(time_t * mtime, char *if_modified_since);
char *to_upper(char *str);
int unescape_uri(char *uri, char **query_string);
int create_temporary_file(short want_unlink, char *storage, int size);
char * normalize_path(char *path);
int real_set_block_fd(int fd);
int real_set_nonblock_fd(int fd);
/* buffer */
int req_write(request * req, char *msg);
void reset_output_buffer(request *req);
int req_write_escape_http(request * req, char *msg);
int req_write_escape_html(request * req, char *msg);
int req_flush(request * req);
char *escape_uri(char *uri);
/* timestamp */
void timestamp(void);
/* mmap_cache */
struct mmap_entry *find_mmap(int data_fd, struct stat *s);
void release_mmap(struct mmap_entry *e);
/* sublog */
int open_gen_fd(char *spec);
int process_cgi_header(request * req);
/* pipe */
int read_from_pipe(request * req);
int write_from_pipe(request * req);
/* ip */
int bind_server(int server_s, char *ip);
char *ascii_sockaddr(struct SOCKADDR *s, char *dest, int len);
int net_port(struct SOCKADDR *s);
/* select */
void select_loop(int server_s);
#endif

Binary file not shown.

View File

@ -0,0 +1,127 @@
%{
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <psp@well.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: boa_grammar.y,v 1.14 1999/10/12 14:49:07 jon Exp $*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* #include "boa.h" */
#include "parse.h"
int yyerror(char * msg);
/* yydebug = 1; */
#ifdef DEBUG
#define DBG(x) x
#else
#define DBG(x)
#endif
char *arg1hold;
char mime_type[256]; /* global to inherit */
%}
%union {
char * sval;
int ival;
struct ccommand * cval;
};
/* boa.conf tokens */
%token <cval> STMT_NO_ARGS STMT_ONE_ARG STMT_TWO_ARGS
/* mime.type tokens */
%token <sval> MIMETYPE
%token <sval> STRING
%token <ival> INTEGER
%start ConfigFiles
%%
ConfigFiles: BoaConfigStmts MimeTypeStmts
;
BoaConfigStmts: BoaConfigStmts BoaConfigStmt
| /* empty */
;
BoaConfigStmt:
StmtNoArgs
| StmtOneArg
| StmtTwoArgs
;
StmtNoArgs: STMT_NO_ARGS
{ if ($1->action) {
DBG(printf("StmtNoArgs: %s\n",$1->name);)
$1->action(NULL,NULL,$1->object);
}
}
;
StmtOneArg: STMT_ONE_ARG STRING
{ if ($1->action) {
DBG(printf("StmtOneArg: %s %s\n",$1->name,$2);)
$1->action($2,NULL,$1->object);
}
}
;
StmtTwoArgs: STMT_TWO_ARGS STRING
{ arg1hold = strdup($2); }
STRING
{ if ($1->action) {
DBG(printf("StmtTwoArgs: '%s' '%s' '%s'\n",
$1->name,arg1hold,$4);)
$1->action($4,arg1hold,$1->object);
}
free(arg1hold);
}
;
/******************* mime.types **********************/
MimeTypeStmts: MimeTypeStmts MimeTypeStmt
| /* empty */
;
MimeTypeStmt: MIMETYPE
{ strcpy(mime_type, $1); }
ExtensionList
;
ExtensionList: ExtensionList Extension
| /* empty */
;
Extension: STRING
{ add_mime_type($1, mime_type); }
;
%%

Binary file not shown.

View File

@ -0,0 +1,155 @@
%{
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <psp@well.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: boa_lexer.l,v 1.13.2.1 2002/07/07 23:19:55 jnelson Exp $*/
#include "y.tab.h"
#include <stdlib.h>
#include <unistd.h>
#include "parse.h"
#define MAX_STR_CONST 1024
#define qspush(c) \
if (string_buf_ptr < string_buf+MAX_STR_CONST) \
*string_buf_ptr++ = c; \
else \
yyerror("quoted string overflow");
char *mime_types = NULL;
static int file = 0;
int lineno = 1;
struct ccommand *k;
char string_buf[MAX_STR_CONST];
char *string_buf_ptr;
%}
%s MIME
/* Quoted string handling (almost) straight out of
the flex 2.5 man page, April 1995 */
%x STR
%%
[ \t]+ ;
#.* ;
<MIME>[^ \t\n]+\/[^ \t\n]+ { yylval.sval = yytext; return MIMETYPE; }
[^ \"\t\n]+ { /* XXX could use better checks that we are in a state to
* accept keywords; this version matches original behavior */
if ((YYSTATE==INITIAL) && (k=lookup_keyword(yytext))) {
yylval.cval=k;
return (k->type);
} else { yylval.sval = yytext; return STRING; }
}
\" {
string_buf_ptr = string_buf;
BEGIN(STR);
}
<STR>{
\" { /* saw closing quote - all done */
BEGIN(INITIAL);
*string_buf_ptr = '\0';
/* return string constant token type and value to parser */
yylval.sval = string_buf; return STRING;
}
\n {
/* error - unterminated string constant */
/* generate error message */
yyerror("unterminated string constant");
}
\\[0-7]{1,3} {
/* octal escape sequence */
int result;
(void) sscanf( yytext + 1, "%o", &result );
if ( result > 0xff )
{ /* error, constant is out-of-bounds */ }
qspush(result);
}
\\[0-9]+ {
/* generate error - bad escape sequence; something
* like '\48' or '\0777777'
*/
yyerror("bad escape sequence");
}
\\n qspush('\n');
\\t qspush('\t');
\\r qspush('\r');
\\b qspush('\b');
\\f qspush('\f');
\\(.|\n) *string_buf_ptr++ = yytext[1];
[^\\\n\"]+ {
char *yptr = yytext;
while ( *yptr )
qspush(*yptr++);
}
}
/* End of <STR> dependence */
\n { lineno++; }
%%
/* In yywrap we track which file we are on.
* 1: close boa.conf, open mime.types
* 2: return 1;
*/
int yywrap()
{
fclose(yyin);
file++;
switch(file) {
case 1:
yyin = fopen(mime_types, "r");
if(!yyin) {
fprintf(stderr, "Could not open mime.types file, \"%s\", "
"for reading\n", mime_types);
exit(1);
}
BEGIN MIME;
return 0;
default:
BEGIN INITIAL;
file = 0; /* in case we reread config files */
return 1;
}
}
int yyerror(char * msg)
{
fprintf(stderr, "Error on line %d of %s: %s\n", lineno,
(file == 0 ? "boa.conf" : "mime.types"), msg);
return 1;
}

View File

@ -0,0 +1,259 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1999 Jon Nelson <jnelson@boa.org>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: buffer.c,v 1.10.2.2 2002/07/26 03:03:44 jnelson Exp $ */
#include "boa.h"
#include "escape.h"
#define INT_TO_HEX(x) \
((((x)-10)>=0)?('A'+((x)-10)):('0'+(x)))
/*
* Name: req_write
*
* Description: Buffers data before sending to client.
* Returns: -1 for error, otherwise how much is stored
*/
int req_write(request * req, char *msg)
{
int msg_len;
msg_len = strlen(msg);
if (!msg_len || req->status == DEAD)
return req->buffer_end;
if (req->buffer_end + msg_len > BUFFER_SIZE) {
log_error_time();
fprintf(stderr, "Ran out of Buffer space!\n");
req->status = DEAD;
return -1;
}
memcpy(req->buffer + req->buffer_end, msg, msg_len);
req->buffer_end += msg_len;
return req->buffer_end;
}
void reset_output_buffer(request *req)
{
req->buffer_end = 0;
}
/*
* Name: req_write_escape_http
* Description: Buffers and "escapes" data before sending to client.
* as above, but translates as it copies, into a form suitably
* encoded for URLs in HTTP headers.
* Returns: -1 for error, otherwise how much is stored
*/
int req_write_escape_http(request * req, char *msg)
{
char c, *inp, *dest;
int left;
inp = msg;
dest = req->buffer + req->buffer_end;
/* 3 is a guard band, since we don't check the destination pointer
* in the middle of a transfer of up to 3 bytes */
left = BUFFER_SIZE - req->buffer_end - 3;
while ((c = *inp++) && left > 0) {
if (needs_escape((unsigned int) c)) {
*dest++ = '%';
*dest++ = INT_TO_HEX(c >> 4);
*dest++ = INT_TO_HEX(c & 15);
left -= 3;
} else {
*dest++ = c;
left--;
}
}
req->buffer_end = dest - req->buffer;
if (left == 0) {
log_error_time();
fprintf(stderr, "Ran out of Buffer space!\n");
req->status = DEAD;
return -1;
}
return req->buffer_end;
}
/*
* Name: req_write_escape_html
* Description: Buffers and "escapes" data before sending to client.
* as above, but translates as it copies, into a form suitably
* encoded for HTML bodies.
* Returns: -1 for error, otherwise how much is stored
*/
int req_write_escape_html(request * req, char *msg)
{
char c, *inp, *dest;
int left;
inp = msg;
dest = req->buffer + req->buffer_end;
/* 5 is a guard band, since we don't check the destination pointer
* in the middle of a transfer of up to 5 bytes */
left = BUFFER_SIZE - req->buffer_end - 5;
while ((c = *inp++) && left > 0) {
switch (c) {
case '>':
*dest++ = '&';
*dest++ = 'g';
*dest++ = 't';
*dest++ = ';';
left -= 4;
break;
case '<':
*dest++ = '&';
*dest++ = 'l';
*dest++ = 't';
*dest++ = ';';
left -= 4;
break;
case '&':
*dest++ = '&';
*dest++ = 'a';
*dest++ = 'm';
*dest++ = 'p';
*dest++ = ';';
left -= 5;
break;
case '\"':
*dest++ = '&';
*dest++ = 'q';
*dest++ = 'u';
*dest++ = 'o';
*dest++ = 't';
*dest++ = ';';
left -= 6;
break;
default:
*dest++ = c;
left--;
}
}
req->buffer_end = dest - req->buffer;
if (left == 0) {
log_error_time();
fprintf(stderr, "Ran out of Buffer space!\n");
req->status = DEAD;
return -1;
}
return req->buffer_end;
}
/*
* Name: flush_req
*
* Description: Sends any backlogged buffer to client.
*
* Returns: -2 for error, -1 for blocked, otherwise how much is stored
*/
int req_flush(request * req)
{
int bytes_to_write;
bytes_to_write = req->buffer_end - req->buffer_start;
if (req->status == DEAD)
return -2;
if (bytes_to_write) {
int bytes_written;
bytes_written = write(req->fd, req->buffer + req->buffer_start,
bytes_to_write);
if (bytes_written < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN)
return -1; /* request blocked at the pipe level, but keep going */
else {
req->buffer_start = req->buffer_end = 0;
if (errno != EPIPE)
perror("buffer flush"); /* OK to disable if your logs get too big */
req->status = DEAD;
req->buffer_end = 0;
return -2;
}
}
#ifdef FASCIST_LOGGING
log_error_time();
fprintf(stderr, "%s:%d - Wrote \"", __FILE__, __LINE__);
fwrite(req->buffer + req->buffer_start, sizeof (char),
bytes_written, stderr);
fprintf(stderr, "\" (%d bytes)\n", bytes_written);
#endif
req->buffer_start += bytes_written;
}
if (req->buffer_start == req->buffer_end)
req->buffer_start = req->buffer_end = 0;
return req->buffer_end; /* successful */
}
/*
* Name: escape_string
*
* Description: escapes the string inp. Uses variable buf. If buf is
* NULL when the program starts, it will attempt to dynamically allocate
* the space that it needs, otherwise it will assume that the user
* has already allocated enough space for the variable buf, which
* could be up to 3 times the size of inp. If the routine dynamically
* allocates the space, the user is responsible for freeing it afterwords
* Returns: NULL on error, pointer to string otherwise.
* Note: this function doesn't really belong here, I plopped it here to
* work around a "bug" in escape.h (it defines a global, so can't be
* used in multiple source files). Actually, this routine shouldn't
* exist anywhere, it's only usage is in get.c's handling of on-the-fly
* directory generation, which would be better configured to use a combination
* of req_write_escape_http and req_write_escape_html. That would involve
* more work than I'm willing to put in right now, though, so here we are.
*/
char *escape_string(char *inp, char *buf)
{
int max;
char *index;
unsigned char c;
max = strlen(inp) * 3;
if (buf == NULL && max)
buf = malloc(sizeof (char) * max + 1);
if (buf == NULL) {
log_error_time();
perror("malloc");
return NULL;
}
index = buf;
while ((c = *inp++) && max > 0) {
if (needs_escape((unsigned int) c)) {
*index++ = '%';
*index++ = INT_TO_HEX(c >> 4);
*index++ = INT_TO_HEX(c & 15);
} else
*index++ = c;
}
*index = '\0';
return buf;
}

Binary file not shown.

View File

@ -0,0 +1,556 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996 Charles F. Randall <crandall@goldsys.com>
* Some changes Copyright (C) 1997-2002 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: cgi.c,v 1.83.2.3 2002/07/23 15:49:54 jnelson Exp $ */
#include "boa.h"
static char *env_gen_extra(const char *key, const char *value, int extra);
int verbose_cgi_logs = 0;
/* The +1 is for the the NULL in complete_env */
static char *common_cgi_env[COMMON_CGI_COUNT + 1];
/*
* Name: create_common_env
*
* Description: Set up the environment variables that are common to
* all CGI scripts
*/
void create_common_env()
{
int index = 0, i;
/* NOTE NOTE NOTE:
If you (the reader) someday modify this chunk of code to
handle more "common" CGI environment variables, then bump the
value COMMON_CGI_COUNT in defines.h UP
Also, in the case of document_root and server_admin, two variables
that may or may not be defined depending on the way the server
is configured, we check for null values and use an empty
string to denote a NULL value to the environment, as per the
specification. The quote for which follows:
"In all cases, a missing environment variable is
equivalent to a zero-length (NULL) value, and vice versa."
*/
common_cgi_env[index++] = env_gen_extra("PATH",
((cgi_path != NULL) ? cgi_path : DEFAULT_PATH), 0);
common_cgi_env[index++] = env_gen_extra("SERVER_SOFTWARE", SERVER_VERSION, 0);
common_cgi_env[index++] = env_gen_extra("SERVER_NAME", server_name, 0);
common_cgi_env[index++] = env_gen_extra("GATEWAY_INTERFACE", CGI_VERSION, 0);
common_cgi_env[index++] =
env_gen_extra("SERVER_PORT", simple_itoa(server_port), 0);
/* NCSA and APACHE added -- not in CGI spec */
/* common_cgi_env[index++] = env_gen_extra("DOCUMENT_ROOT", document_root); */
/* NCSA added */
/* common_cgi_env[index++] = env_gen_extra("SERVER_ROOT", server_root); */
/* APACHE added */
common_cgi_env[index++] = env_gen_extra("SERVER_ADMIN", server_admin, 0);
common_cgi_env[index] = NULL;
/* Sanity checking -- make *sure* the memory got allocated */
if (index > COMMON_CGI_COUNT) {
log_error_time();
fprintf(stderr, "COMMON_CGI_COUNT not high enough.\n");
exit(1);
}
for(i = 0;i < index;++i) {
if (common_cgi_env[i] == NULL) {
log_error_time();
fprintf(stderr, "Unable to allocate a component of common_cgi_env - out of memory.\n");
exit(1);
}
}
}
void clear_common_env(void)
{
int i;
for(i = 0;i <= COMMON_CGI_COUNT;++i) {
if (common_cgi_env[i] != NULL) {
free(common_cgi_env[i]);
common_cgi_env[i] = NULL;
}
}
}
/*
* Name: env_gen_extra
* (and via a not-so-tricky #define, env_gen)
* This routine calls malloc: please free the memory when you are done
*/
static char *env_gen_extra(const char *key, const char *value, int extra)
{
char *result;
int key_len, value_len;
if (value == NULL) /* ServerAdmin may not be defined, eg */
value = "";
key_len = strlen(key);
value_len = strlen(value);
/* leave room for '=' sign and null terminator */
result = malloc(extra + key_len + value_len + 2);
if (result) {
memcpy(result + extra, key, key_len);
*(result + extra + key_len) = '=';
memcpy(result + extra + key_len + 1, value, value_len);
*(result + extra + key_len + value_len + 1) = '\0';
} else {
log_error_time();
perror("malloc");
log_error_time();
fprintf(stderr,
"tried to allocate (key=value) extra=%d: %s=%s\n",
extra, key, value);
}
return result;
}
/*
* Name: add_cgi_env
*
* Description: adds a variable to CGI's environment
* Used for HTTP_ headers
*/
int add_cgi_env(request * req, char *key, char *value, int http_prefix)
{
char *p;
int prefix_len;
if (http_prefix) {
prefix_len = 5;
} else {
prefix_len = 0;
}
if (req->cgi_env_index < CGI_ENV_MAX) {
p = env_gen_extra(key, value, prefix_len);
if (!p) {
log_error_time();
fprintf(stderr, "Unable to generate additional CGI Environment"
"variable -- ran out of memory!\n");
}
if (prefix_len)
memcpy(p, "HTTP_", 5);
req->cgi_env[req->cgi_env_index++] = p;
return 1;
} else {
log_error_time();
fprintf(stderr, "Unable to generate additional CGI Environment"
"variable -- not enough space!\n");
}
return 0;
}
#define my_add_cgi_env(req, key, value) { \
int ok = add_cgi_env(req, key, value, 0); \
if (!ok) return 0; \
}
/*
* Name: complete_env
*
* Description: adds the known client header env variables
* and terminates the environment array
*/
int complete_env(request * req)
{
int i;
for (i = 0; common_cgi_env[i]; i++)
req->cgi_env[i] = common_cgi_env[i];
{
char *w;
switch (req->method) {
case M_POST:
w = "POST";
break;
case M_HEAD:
w = "HEAD";
break;
case M_GET:
w = "GET";
break;
default:
w = "UNKNOWN";
break;
}
my_add_cgi_env(req, "REQUEST_METHOD", w);
}
my_add_cgi_env(req, "SERVER_ADDR", req->local_ip_addr);
my_add_cgi_env(req, "SERVER_PROTOCOL", req->http_version);
my_add_cgi_env(req, "REQUEST_URI", req->request_uri);
if (req->path_info)
my_add_cgi_env(req, "PATH_INFO", req->path_info);
if (req->path_translated)
/* while path_translated depends on path_info,
* there are cases when path_translated might
* not exist when path_info does
*/
my_add_cgi_env(req, "PATH_TRANSLATED", req->path_translated);
my_add_cgi_env(req, "SCRIPT_NAME", req->script_name);
if (req->query_string)
my_add_cgi_env(req, "QUERY_STRING", req->query_string);
my_add_cgi_env(req, "REMOTE_ADDR", req->remote_ip_addr);
my_add_cgi_env(req, "REMOTE_PORT", simple_itoa(req->remote_port));
if (req->method == M_POST) {
if (req->content_type) {
my_add_cgi_env(req, "CONTENT_TYPE", req->content_type);
} else {
my_add_cgi_env(req, "CONTENT_TYPE", default_type);
}
if (req->content_length) {
my_add_cgi_env(req, "CONTENT_LENGTH", req->content_length);
}
}
#ifdef ACCEPT_ON
if (req->accept[0])
my_add_cgi_env(req, "HTTP_ACCEPT", req->accept);
#endif
if (req->cgi_env_index < CGI_ENV_MAX + 1) {
req->cgi_env[req->cgi_env_index] = NULL; /* terminate */
return 1;
}
log_error_time();
fprintf(stderr, "Not enough space in CGI environment for remainder"\
" of variables.\n");
return 0;
}
/*
* Name: make_args_cgi
*
* Build argv list for a CGI script according to spec
*
*/
void create_argv(request * req, char **aargv)
{
char *p, *q, *r;
int aargc;
q = req->query_string;
aargv[0] = req->pathname;
/* here, we handle a special "indexed" query string.
* Taken from the CGI/1.1 SPEC:
* This is identified by a GET or HEAD request with a query string
* with no *unencoded* '=' in it.
* For such a request, I'm supposed to parse the search string
* into words, according to the following rules:
search-string = search-word *( "+" search-word )
search-word = 1*schar
schar = xunreserved | escaped | xreserved
xunreserved = alpha | digit | xsafe | extra
xsafe = "$" | "-" | "_" | "."
xreserved = ";" | "/" | "?" | ":" | "@" | "&"
After parsing, each word is URL-decoded, optionally encoded in a system
defined manner, and then the argument list
is set to the list of words.
Thus, schar is alpha|digit|"$"|"-"|"_"|"."|";"|"/"|"?"|":"|"@"|"&"
As of this writing, escape.pl escapes the following chars:
"-", "_", ".", "!", "~", "*", "'", "(", ")",
"0".."9", "A".."Z", "a".."z",
";", "/", "?", ":", "@", "&", "=", "+", "\$", ","
Which therefore means
"=", "+", "~", "!", "*", "'", "(", ")", ","
are *not* escaped and should be?
Wait, we don't do any escaping, and nor should we.
According to the RFC draft, we unescape and then re-escape
in a "system defined manner" (here: none).
The CGI/1.1 draft (03, latest is 1999???) is very unclear here.
I am using the latest published RFC, 2396, for what does and does
not need escaping.
Since boa builds the argument list and does not call /bin/sh,
(boa uses execve for CGI)
*/
if (q && !strchr(q, '=')) {
/* we have an 'index' style */
q = strdup(q);
if (!q) {
WARN("unable to strdup 'q' in create_argv!");
}
for (aargc = 1; q && (aargc < CGI_ARGC_MAX);) {
r = q;
/* for an index-style CGI, + is used to seperate arguments
* an escaped '+' is of no concern to us
*/
if ((p = strchr(q, '+'))) {
*p = '\0';
q = p + 1;
} else {
q = NULL;
}
if (unescape_uri(r, NULL)) {
/* printf("parameter %d: %s\n",aargc,r); */
aargv[aargc++] = r;
}
}
aargv[aargc] = NULL;
} else {
aargv[1] = NULL;
}
}
/*
* Name: init_cgi
*
* Description: Called for GET/POST requests that refer to ScriptAlias
* directories or application/x-httpd-cgi files. Ties stdout to socket,
* stdin to data if POST, and execs CGI.
* stderr remains tied to our log file; is this good?
*
* Returns:
* 0 - error or NPH, either way the socket is closed
* 1 - success
*/
int init_cgi(request * req)
{
int child_pid;
int pipes[2];
int use_pipes = 0;
SQUASH_KA(req);
if (req->is_cgi) {
if (complete_env(req) == 0) {
return 0;
}
}
#ifdef FASCIST_LOGGING
{
int i;
for (i = 0; i < req->cgi_env_index; ++i)
fprintf(stderr, "%s - environment variable for cgi: \"%s\"\n",
__FILE__, req->cgi_env[i]);
}
#endif
if (req->is_cgi == CGI || 1) {
use_pipes = 1;
if (pipe(pipes) == -1) {
log_error_time();
perror("pipe");
return 0;
}
/* set the read end of the socket to non-blocking */
if (set_nonblock_fd(pipes[0]) == -1) {
log_error_time();
perror("cgi-fcntl");
close(pipes[0]);
close(pipes[1]);
return 0;
}
}
child_pid = fork();
switch(child_pid) {
case -1:
/* fork unsuccessful */
log_error_time();
perror("fork");
if (use_pipes) {
close(pipes[0]);
close(pipes[1]);
}
send_r_error(req);
/* FIXME: There is aproblem here. send_r_error would work
for NPH and CGI, but not for GUNZIP. Fix that. */
/* i'd like to send_r_error, but.... */
return 0;
break;
case 0:
/* child */
if (req->is_cgi == CGI || req->is_cgi == NPH) {
char *foo = strdup(req->pathname);
char *c;
if (!foo) {
WARN("unable to strdup pathname for req->pathname");
_exit(1);
}
c = strrchr(foo, '/');
if (c) {
++c;
*c = '\0';
} else {
/* we have a serious problem */
log_error_time();
perror("chdir");
if (use_pipes)
close(pipes[1]);
_exit(1);
}
if (chdir(foo) != 0) {
log_error_time();
perror("chdir");
if (use_pipes)
close(pipes[1]);
_exit(1);
}
}
if (use_pipes) {
close(pipes[0]);
/* tie cgi's STDOUT to it's write end of pipe */
if (dup2(pipes[1], STDOUT_FILENO) == -1) {
log_error_time();
perror("dup2 - pipes");
close(pipes[1]);
_exit(1);
}
close(pipes[1]);
if (set_block_fd(STDOUT_FILENO) == -1) {
log_error_time();
perror("cgi-fcntl");
_exit(1);
}
} else {
/* tie stdout to socket */
if (dup2(req->fd, STDOUT_FILENO) == -1) {
log_error_time();
perror("dup2 - fd");
_exit(1);
}
/* Switch socket flags back to blocking */
if (set_block_fd(req->fd) == -1) {
log_error_time();
perror("cgi-fcntl");
_exit(1);
}
}
/* tie post_data_fd to POST stdin */
if (req->method == M_POST) { /* tie stdin to file */
lseek(req->post_data_fd, SEEK_SET, 0);
dup2(req->post_data_fd, STDIN_FILENO);
close(req->post_data_fd);
}
/* Close access log, so CGI program can't scribble
* where it shouldn't
*/
close_access_log();
/*
* tie STDERR to cgi_log_fd
* cgi_log_fd will automatically close, close-on-exec rocks!
* if we don't tied STDERR (current log_error) to cgi_log_fd,
* then we ought to close it.
*/
if (!cgi_log_fd)
dup2(devnullfd, STDERR_FILENO);
else
dup2(cgi_log_fd, STDERR_FILENO);
if (req->is_cgi) {
char *aargv[CGI_ARGC_MAX + 1];
create_argv(req, aargv);
execve(req->pathname, aargv, req->cgi_env);
} else {
if (req->pathname[strlen(req->pathname) - 1] == '/')
execl(dirmaker, dirmaker, req->pathname, req->request_uri,
NULL);
#ifdef GUNZIP
else
execl(GUNZIP, GUNZIP, "--stdout", "--decompress",
req->pathname, NULL);
#endif
}
/* execve failed */
WARN(req->pathname);
_exit(1);
break;
default:
/* parent */
/* if here, fork was successful */
if (verbose_cgi_logs) {
log_error_time();
fprintf(stderr, "Forked child \"%s\" pid %d\n",
req->pathname, child_pid);
}
if (req->method == M_POST) {
close(req->post_data_fd); /* child closed it too */
req->post_data_fd = 0;
}
/* NPH, GUNZIP, etc... all go straight to the fd */
if (!use_pipes)
return 0;
close(pipes[1]);
req->data_fd = pipes[0];
req->status = PIPE_READ;
if (req->is_cgi == CGI) {
req->cgi_status = CGI_PARSE; /* got to parse cgi header */
/* for cgi_header... I get half the buffer! */
req->header_line = req->header_end =
(req->buffer + BUFFER_SIZE / 2);
} else {
req->cgi_status = CGI_BUFFER;
/* I get all the buffer! */
req->header_line = req->header_end = req->buffer;
}
/* reset req->filepos for logging (it's used in pipe.c) */
/* still don't know why req->filesize might be reset though */
req->filepos = 0;
break;
}
return 1;
}

Binary file not shown.

View File

@ -0,0 +1,176 @@
/*
* Boa, an http server
* cgi_header.c - cgi header parsing and control
* Copyright (C) 1997-99 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "boa.h"
/* process_cgi_header
* returns 0 -=> error or HEAD, close down.
* returns 1 -=> done processing
* leaves req->cgi_status as WRITE
*/
/*
The server MUST also resolve any conflicts between header fields returned by
the script and header fields that it would otherwise send itself.
...
At least one CGI-Field MUST be supplied, but no CGI field name may be used
more than once in a response. If a body is supplied, then a
"Content-type" header field MUST be supplied by the script,
otherwise the script MUST send a "Location" or "Status" header
field. If a Location CGI-Field is returned, then the script
MUST NOT supply any HTTP-Fields.
*/
/* TODO:
We still need to cycle through the data before the end of the headers,
line-by-line, and check for any problems with the CGI
outputting overriding http responses, etc...
*/
int process_cgi_header(request * req)
{
char *buf;
char *c;
if (req->cgi_status != CGI_DONE)
req->cgi_status = CGI_BUFFER;
buf = req->header_line;
c = strstr(buf, "\n\r\n");
if (c == NULL) {
c = strstr(buf, "\n\n");
if (c == NULL) {
log_error_time();
fputs("cgi_header: unable to find LFLF\n", stderr);
#ifdef FASCIST_LOGGING
log_error_time();
fprintf(stderr, "\"%s\"\n", buf);
#endif
send_r_bad_gateway(req);
return 0;
}
}
if (req->simple) {
if (*(c + 1) == '\r')
req->header_line = c + 2;
else
req->header_line = c + 1;
return 1;
}
if (!strncasecmp(buf, "Status: ", 8)) {
req->header_line--;
memcpy(req->header_line, "HTTP/1.0 ", 9);
} else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
#ifdef FASCIST_LOGGING
log_error_time();
fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
__FILE__, __LINE__, buf + 10);
#endif
if (buf[10] == '/') { /* virtual path */
log_error_time();
fprintf(stderr,
"server does not support internal redirection: " \
"\"%s\"\n", buf + 10);
send_r_bad_request(req);
/*
* We (I, Jon) have declined to support absolute-path parsing
* because I see it as a major security hole.
* Location: /etc/passwd or Location: /etc/shadow is not funny.
*
* Also, the below code is borked.
* request_uri could contain /cgi-bin/bob/extra_path
*/
/*
strcpy(req->request_uri, buf + 10);
return internal_redirect(req);
*/
} else { /* URL */
char *c2;
c2 = strchr(buf + 10, '\n');
/* c2 cannot ever equal NULL here because we already have found one */
--c2;
while (*c2 == '\r')
--c2;
++c2;
/* c2 now points to a '\r' or the '\n' */
*c2++ = '\0'; /* end header */
/* first next header, or is at req->header_end */
while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
++c2;
if (c2 == req->header_end)
send_r_moved_temp(req, buf + 10, "");
else
send_r_moved_temp(req, buf + 10, c2);
}
req->status = DONE;
return 1;
} else { /* not location and not status */
char *dest;
int howmuch;
send_r_request_ok(req); /* does not terminate */
/* got to do special things because
a) we have a single buffer divided into 2 pieces
b) we need to merge those pieces
Easiest way is to memmove the cgi data backward until
it touches the buffered data, then reset the cgi data pointers
*/
dest = req->buffer + req->buffer_end;
if (req->method == M_HEAD) {
if (*(c + 1) == '\r')
req->header_end = c + 2;
else
req->header_end = c + 1;
req->cgi_status = CGI_DONE;
}
howmuch = req->header_end - req->header_line;
if (dest + howmuch > req->buffer + BUFFER_SIZE) {
/* big problem */
log_error_time();
fprintf(stderr, "Too much data to move! Aborting! %s %d\n",
__FILE__, __LINE__);
/* reset buffer pointers because we already called
send_r_request_ok... */
req->buffer_start = req->buffer_end = 0;
send_r_error(req);
return 0;
}
memmove(dest, req->header_line, howmuch);
req->buffer_end += howmuch;
req->header_line = req->buffer + req->buffer_end;
req->header_end = req->header_line;
req_flush(req);
if (req->method == M_HEAD)
return 0;
}
return 1;
}

Binary file not shown.

View File

@ -0,0 +1,40 @@
AC_DEFUN([AC_CHECK_STRUCT_FOR],[
ac_safe_struct=`echo "$2" | sed 'y%./+-%__p_%'`
ac_safe_member=`echo "$3" | sed 'y%./+-%__p_%'`
ac_safe_all="ac_cv_struct_${ac_safe_struct}_has_${ac_safe_member}"
changequote(, )dnl
ac_uc_define=STRUCT_`echo "${ac_safe_struct}_HAS_${ac_safe_member}" | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
changequote([, ])dnl
AC_MSG_CHECKING([for $2.$3])
AC_CACHE_VAL($ac_safe_all,
[
if test "x$4" = "x"; then
defineit="= 0"
elif test "x$4" = "xno"; then
defineit=""
else
defineit="$4"
fi
AC_TRY_COMPILE([
$1
],[
struct $2 testit;
testit.$3 $defineit;
], eval "${ac_safe_all}=yes", eval "${ac_safe_all}=no" )
])
if eval "test \"x$`echo ${ac_safe_all}`\" = \"xyes\""; then
AC_MSG_RESULT(yes)
AC_DEFINE_UNQUOTED($ac_uc_define)
else
AC_MSG_RESULT(no)
fi
])
dnl AC_CHECK_STRUCT_FOR(INCLUDES,STRUCT,MEMBER,DEFINE,[no])
dnl 1.1 (2000/09/19)
dnl Wes Hardaker <wjhardaker@ucdavis.edu>
dnl ----------------------------------------------------------

View File

@ -0,0 +1,140 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1999-2000 Jon Nelson <jnelson@boa.org>
* and Larry Doolittle <ldoolitt@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: compat.h,v 1.18.2.1 2002/06/06 05:02:28 jnelson Exp $*/
#ifndef _COMPAT_H
#define _COMPAT_H
#include "config.h"
#ifdef TIME_WITH_SYS_TIME
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
#ifndef OPEN_MAX
#define OPEN_MAX 256
#endif
#ifndef NI_MAXHOST
#define NI_MAXHOST 20
#endif
#include <sys/socket.h>
#ifndef SO_MAXCONN
#define SO_MAXCONN 250
#endif
#ifndef PATH_MAX
#define PATH_MAX 2048
#endif
/* Wild guess time, probably better done with configure */
#ifdef O_NONBLOCK
#define NOBLOCK O_NONBLOCK /* Linux */
#else /* O_NONBLOCK */
#ifdef O_NDELAY
#define NOBLOCK O_NDELAY /* Sun */
#else /* O_NDELAY */
#error "Can't find a way to #define NOBLOCK"
#endif /* O_NDELAY */
#endif /* O_NONBLOCK */
#ifndef MAP_FILE
#define MAP_OPTIONS MAP_PRIVATE /* Sun */
#else
#define MAP_OPTIONS MAP_FILE|MAP_PRIVATE /* Linux */
#endif
#ifdef INET6
#define SOCKADDR sockaddr_storage
#define S_FAMILY __s_family
#define SERVER_AF AF_INET6
#else
#define SOCKADDR sockaddr_in
#define S_FAMILY sin_family
#define SERVER_AF AF_INET
#endif
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
/* below here, functions are provided in extras */
#ifndef HAVE_SCANDIR
int
scandir(const char *dir, struct dirent ***namelist,
int (*select) (const struct dirent *),
int (*compar) (const struct dirent **, const struct dirent **));
#endif
#ifndef HAVE_ALPHASORT
int alphasort(const struct dirent **a, const struct dirent **b);
#endif
#ifndef HAVE_STRSTR
char *strstr(char *s1, char *s2);
#endif
#ifndef HAVE_STRDUP
char *strdup(char *s);
#endif
#ifdef HAVE_TM_GMTOFF
#define TIMEZONE_OFFSET(foo) (foo)->tm_gmtoff
#else
#define TIMEZONE_OFFSET(foo) timezone
#endif
#ifdef HAVE_TM_ZONE
#define TIMEZONE(foo) foo##->tm_zone
#else
#define TIMEZONE(foo) *tzname
#endif
#ifdef HAVE_LIBDMALLOC
#define DMALLOC_FUNC_CHECK
#include <dmalloc.h>
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
#endif

View File

@ -0,0 +1,334 @@
/*
* Boa, an http server
* Copyright (C) 1999 Larry Doolittle <ldoolitt@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: config.c,v 1.31.2.3 2002/07/26 03:04:29 jnelson Exp $*/
#include "boa.h"
#include "y.tab.h"
#include "parse.h"
int yyparse(void); /* Better match the output of lex */
#ifdef DEBUG
#define DBG(x) x
#else
#define DBG(x)
#endif
int server_port;
uid_t server_uid;
gid_t server_gid;
char *server_root;
char *server_name;
char *server_admin;
char *server_ip;
int virtualhost;
long int max_connections;
char *document_root;
char *user_dir;
char *directory_index;
char *default_type;
char *dirmaker;
char *cachedir;
char *tempdir;
char *cgi_path = NULL;
int single_post_limit = SINGLE_POST_LIMIT_DEFAULT;
int ka_timeout;
int ka_max;
/* These came from log.c */
char *error_log_name;
char *access_log_name;
char *cgi_log_name;
int use_localtime;
/* These are new */
static void c_set_user(char *v1, char *v2, void *t);
static void c_set_group(char *v1, char *v2, void *t);
static void c_set_string(char *v1, char *v2, void *t);
static void c_set_int(char *v1, char *v2, void *t);
static void c_set_unity(char *v1, char *v2, void *t);
static void c_add_type(char *v1, char *v2, void *t);
static void c_add_alias(char *v1, char *v2, void *t);
/* Fakery to keep the value passed to action() a void *,
see usage in table and c_add_alias() below */
static int script_number = SCRIPTALIAS;
static int redirect_number = REDIRECT;
static int alias_number = ALIAS;
static uid_t current_uid=0;
/* Help keep the table below compact */
#define S0A STMT_NO_ARGS
#define S1A STMT_ONE_ARG
#define S2A STMT_TWO_ARGS
struct ccommand clist[] = {
{"Port", S1A, c_set_int, &server_port},
{"Listen", S1A, c_set_string, &server_ip},
{"BackLog", S1A, c_set_int, &backlog},
{"User", S1A, c_set_user, NULL},
{"Group", S1A, c_set_group, NULL},
{"ServerAdmin", S1A, c_set_string, &server_admin},
{"ServerRoot", S1A, c_set_string, &server_root},
{"ErrorLog", S1A, c_set_string, &error_log_name},
{"AccessLog", S1A, c_set_string, &access_log_name},
{"UseLocaltime", S0A, c_set_unity, &use_localtime},
{"CgiLog", S1A, c_set_string, &cgi_log_name},
{"VerboseCGILogs", S0A, c_set_unity, &verbose_cgi_logs},
{"ServerName", S1A, c_set_string, &server_name},
{"VirtualHost", S0A, c_set_unity, &virtualhost},
{"DocumentRoot", S1A, c_set_string, &document_root},
{"UserDir", S1A, c_set_string, &user_dir},
{"DirectoryIndex", S1A, c_set_string, &directory_index},
{"DirectoryMaker", S1A, c_set_string, &dirmaker},
{"DirectoryCache", S1A, c_set_string, &cachedir},
{"KeepAliveMax", S1A, c_set_int, &ka_max},
{"KeepAliveTimeout", S1A, c_set_int, &ka_timeout},
{"MimeTypes", S1A, c_set_string, &mime_types},
{"DefaultType", S1A, c_set_string, &default_type},
{"AddType", S2A, c_add_type, NULL},
{"ScriptAlias", S2A, c_add_alias, &script_number},
{"Redirect", S2A, c_add_alias, &redirect_number},
{"Alias", S2A, c_add_alias, &alias_number},
{"SinglePostLimit", S1A, c_set_int, &single_post_limit},
{"CGIPath", S1A, c_set_string, &cgi_path},
{"MaxConnections", S1A, c_set_int, &max_connections},
};
static void c_set_user(char *v1, char *v2, void *t)
{
struct passwd *passwdbuf;
char *endptr;
int i;
DBG(printf("User %s = ", v1);
)
i = strtol(v1, &endptr, 0);
if (*v1 != '\0' && *endptr == '\0') {
server_uid = i;
} else {
passwdbuf = getpwnam(v1);
if (!passwdbuf) {
if (current_uid)
return;
fprintf(stderr, "No such user: %s\n", v1);
exit(1);
}
server_uid = passwdbuf->pw_uid;
}
DBG(printf("%d\n", server_uid);
)
}
static void c_set_group(char *v1, char *v2, void *t)
{
struct group *groupbuf;
char *endptr;
int i;
DBG(printf("Group %s = ", v1);
)
i = strtol(v1, &endptr, 0);
if (*v1 != '\0' && *endptr == '\0') {
server_gid = i;
} else {
groupbuf = getgrnam(v1);
if (!groupbuf) {
if (current_uid)
return;
fprintf(stderr, "No such group: %s\n", v1);
exit(1);
}
server_gid = groupbuf->gr_gid;
}
DBG(printf("%d\n", server_gid);
)
}
static void c_set_string(char *v1, char *v2, void *t)
{
char *s;
DBG(printf("Setting pointer %p to string %s ..", t, v1);
)
if (t) {
s = *(char **) t;
if (s)
free(s);
*(char **) t = strdup(v1);
if (!*(char **) t) {
DIE("Unable to strdup in c_set_string");
}
DBG(printf("done.\n");
)
} else {
DBG(printf("skipped.\n");
)
}
}
static void c_set_int(char *v1, char *v2, void *t)
{
char *endptr;
int i;
DBG(printf("Setting pointer %p to integer string %s ..", t, v1);
)
if (t) {
i = strtol(v1, &endptr, 0); /* Automatic base 10/16/8 switching */
if (*v1 != '\0' && *endptr == '\0') {
*(int *) t = i;
DBG(printf(" Integer converted as %d, done\n", i);
)
} else {
/* XXX should tell line number to user */
fprintf(stderr, "Error: %s found where integer expected\n",
v1);
}
} else {
DBG(printf("skipped.\n");
)
}
}
static void c_set_unity(char *v1, char *v2, void *t)
{
DBG(printf("Setting pointer %p to unity\n", t);
)
if (t)
*(int *) t = 1;
}
static void c_add_type(char *v1, char *v2, void *t)
{
add_mime_type(v1, v2);
}
static void c_add_alias(char *v1, char *v2, void *t)
{
add_alias(v2, v1, *(int *) t);
}
struct ccommand *lookup_keyword(char *c)
{
struct ccommand *p;
DBG(printf("Checking string '%s' against keyword list\n", c);
)
for (p = clist;
p < clist + (sizeof (clist) / sizeof (struct ccommand)); p++) {
if (strcmp(c, p->name) == 0)
return p;
}
return NULL;
}
/*
* Name: read_config_files
*
* Description: Reads config files via yyparse, then makes sure that
* all required variables were set properly.
*/
void read_config_files(void)
{
char *temp;
current_uid = getuid();
yyin = fopen("boa.conf", "r");
if (!yyin) {
fputs("Could not open boa.conf for reading.\n", stderr);
exit(1);
}
if (yyparse()) {
fputs("Error parsing config files, exiting\n", stderr);
exit(1);
}
if (!server_name) {
struct hostent *he;
char temp_name[100];
if (gethostname(temp_name, 100) == -1) {
perror("gethostname:");
exit(1);
}
he = gethostbyname(temp_name);
if (he == NULL) {
perror("gethostbyname:");
exit(1);
}
server_name = strdup(he->h_name);
if (server_name == NULL) {
perror("strdup:");
exit(1);
}
}
tempdir = getenv("TMP");
if (tempdir == NULL)
tempdir = "/tmp";
if (single_post_limit < 0) {
fprintf(stderr, "Invalid value for single_post_limit: %d\n",
single_post_limit);
exit(1);
}
if (document_root) {
temp = normalize_path(document_root);
free(document_root);
document_root = temp;
}
if (error_log_name) {
temp = normalize_path(error_log_name);
free(error_log_name);
error_log_name = temp;
}
if (access_log_name) {
temp = normalize_path(access_log_name);
free(access_log_name);
access_log_name = temp;
}
if (cgi_log_name) {
temp = normalize_path(cgi_log_name);
free(cgi_log_name);
cgi_log_name = temp;
}
if (dirmaker) {
temp = normalize_path(dirmaker);
free(dirmaker);
dirmaker = temp;
}
#if 0
if (mime_types) {
temp = normalize_path(mime_types);
free(mime_types);
mime_types = temp;
}
#endif
}

View File

@ -0,0 +1,56 @@
# This file is a shell script that caches the results of configure
# tests run on this system so they can be shared between configure
# scripts and configure runs. It is not useful on other systems.
# If it contains results you don't want to keep, you may remove or edit it.
#
# By default, configure uses ./config.cache as the cache file,
# creating it if it does not exist already. You can give configure
# the --cache-file=FILE option to use a different cache file; that is
# what configure does when it calls configure scripts in
# subdirectories, so they share the cache.
# Giving --cache-file=/dev/null disables caching, for debugging configure.
# config.status only pays attention to the cache file if you give it the
# --recheck option to rerun configure.
#
ac_cv_c_const=${ac_cv_c_const='yes'}
ac_cv_func_alphasort=${ac_cv_func_alphasort='yes'}
ac_cv_func_getcwd=${ac_cv_func_getcwd='yes'}
ac_cv_func_gethostbyname=${ac_cv_func_gethostbyname='yes'}
ac_cv_func_gethostname=${ac_cv_func_gethostname='yes'}
ac_cv_func_getpagesize=${ac_cv_func_getpagesize='yes'}
ac_cv_func_inet_aton=${ac_cv_func_inet_aton='yes'}
ac_cv_func_mmap_fixed_mapped=${ac_cv_func_mmap_fixed_mapped='yes'}
ac_cv_func_scandir=${ac_cv_func_scandir='yes'}
ac_cv_func_select=${ac_cv_func_select='yes'}
ac_cv_func_setvbuf_reversed=${ac_cv_func_setvbuf_reversed='no'}
ac_cv_func_socket=${ac_cv_func_socket='yes'}
ac_cv_func_strdup=${ac_cv_func_strdup='yes'}
ac_cv_func_strstr=${ac_cv_func_strstr='yes'}
ac_cv_header_dirent_dirent_h=${ac_cv_header_dirent_dirent_h='yes'}
ac_cv_header_fcntl_h=${ac_cv_header_fcntl_h='yes'}
ac_cv_header_getopt_h=${ac_cv_header_getopt_h='yes'}
ac_cv_header_limits_h=${ac_cv_header_limits_h='yes'}
ac_cv_header_stdc=${ac_cv_header_stdc='yes'}
ac_cv_header_sys_fcntl_h=${ac_cv_header_sys_fcntl_h='yes'}
ac_cv_header_sys_select_h=${ac_cv_header_sys_select_h='yes'}
ac_cv_header_sys_time_h=${ac_cv_header_sys_time_h='yes'}
ac_cv_header_sys_wait_h=${ac_cv_header_sys_wait_h='yes'}
ac_cv_header_time=${ac_cv_header_time='yes'}
ac_cv_header_unistd_h=${ac_cv_header_unistd_h='yes'}
ac_cv_lib_dir_opendir=${ac_cv_lib_dir_opendir='no'}
ac_cv_lib_l_yywrap=${ac_cv_lib_l_yywrap='no'}
ac_cv_path_GUNZIP=${ac_cv_path_GUNZIP='/bin/gunzip'}
ac_cv_prog_CC=${ac_cv_prog_CC='gcc'}
ac_cv_prog_CPP=${ac_cv_prog_CPP='gcc -E'}
ac_cv_prog_LEX=${ac_cv_prog_LEX='lex'}
ac_cv_prog_YACC=${ac_cv_prog_YACC='bison -y'}
ac_cv_prog_cc_cross=${ac_cv_prog_cc_cross='no'}
ac_cv_prog_cc_g=${ac_cv_prog_cc_g='yes'}
ac_cv_prog_cc_works=${ac_cv_prog_cc_works='yes'}
ac_cv_prog_gcc=${ac_cv_prog_gcc='yes'}
ac_cv_prog_make_make_set=${ac_cv_prog_make_make_set='yes'}
ac_cv_struct_sockaddr_in_has_sin_len=${ac_cv_struct_sockaddr_in_has_sin_len='no'}
ac_cv_struct_tm_has_tm_gmtoff=${ac_cv_struct_tm_has_tm_gmtoff='yes'}
ac_cv_struct_tm_has_tm_zone=${ac_cv_struct_tm_has_tm_zone='yes'}
ac_cv_type_pid_t=${ac_cv_type_pid_t='yes'}
ac_cv_type_uid_t=${ac_cv_type_uid_t='yes'}

View File

@ -0,0 +1,118 @@
/* config.h. Generated automatically by configure. */
/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Define to empty if the keyword does not work. */
/* #undef const */
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef gid_t */
/* Define if you have a working `mmap' system call. */
#define HAVE_MMAP 1
/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
#define HAVE_SYS_WAIT_H 1
/* Define if your struct tm has tm_zone. */
#define HAVE_TM_ZONE 1
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef pid_t */
/* Define if the setvbuf function takes the buffering type as its second
argument and the buffer pointer as the third, as on System V
before release 3. */
/* #undef SETVBUF_REVERSED */
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Define to `int' if <sys/types.h> doesn't define. */
/* #undef uid_t */
/* define if GUNZIP found */
#define GUNZIP "/bin/gunzip"
/* sockaddr_in has sin_len member */
/* #undef HAVE_SIN_LEN */
/* if struct tm has tm_gmtoff structure */
#define HAVE_TM_GMTOFF 1
/* if struct tm has tm_zone structure */
#define HAVE_TM_ZONE 1
/* Define if you have the alphasort function. */
#define HAVE_ALPHASORT 1
/* Define if you have the getcwd function. */
#define HAVE_GETCWD 1
/* Define if you have the gethostbyname function. */
#define HAVE_GETHOSTBYNAME 1
/* Define if you have the gethostname function. */
#define HAVE_GETHOSTNAME 1
/* Define if you have the getpagesize function. */
#define HAVE_GETPAGESIZE 1
/* Define if you have the inet_aton function. */
#define HAVE_INET_ATON 1
/* Define if you have the scandir function. */
#define HAVE_SCANDIR 1
/* Define if you have the select function. */
#define HAVE_SELECT 1
/* Define if you have the socket function. */
#define HAVE_SOCKET 1
/* Define if you have the strdup function. */
#define HAVE_STRDUP 1
/* Define if you have the strstr function. */
#define HAVE_STRSTR 1
/* Define if you have the <dirent.h> header file. */
#define HAVE_DIRENT_H 1
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define if you have the <getopt.h> header file. */
#define HAVE_GETOPT_H 1
/* Define if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define if you have the <ndir.h> header file. */
/* #undef HAVE_NDIR_H */
/* Define if you have the <sys/dir.h> header file. */
/* #undef HAVE_SYS_DIR_H */
/* Define if you have the <sys/fcntl.h> header file. */
#define HAVE_SYS_FCNTL_H 1
/* Define if you have the <sys/ndir.h> header file. */
/* #undef HAVE_SYS_NDIR_H */
/* Define if you have the <sys/select.h> header file. */
#define HAVE_SYS_SELECT_H 1
/* Define if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define if you have the dmalloc library (-ldmalloc). */
/* #undef HAVE_LIBDMALLOC */
/* Define if you have the efence library (-lefence). */
/* #undef HAVE_LIBEFENCE */

View File

@ -0,0 +1,117 @@
/* config.h.in. Generated automatically from configure.in by autoheader. */
/* Define to empty if the keyword does not work. */
#undef const
/* Define to `int' if <sys/types.h> doesn't define. */
#undef gid_t
/* Define if you have a working `mmap' system call. */
#undef HAVE_MMAP
/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
#undef HAVE_SYS_WAIT_H
/* Define if your struct tm has tm_zone. */
#undef HAVE_TM_ZONE
/* Define to `int' if <sys/types.h> doesn't define. */
#undef pid_t
/* Define if the setvbuf function takes the buffering type as its second
argument and the buffer pointer as the third, as on System V
before release 3. */
#undef SETVBUF_REVERSED
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Define to `int' if <sys/types.h> doesn't define. */
#undef uid_t
/* define if GUNZIP found */
#undef GUNZIP
/* sockaddr_in has sin_len member */
#undef HAVE_SIN_LEN
/* if struct tm has tm_gmtoff structure */
#undef HAVE_TM_GMTOFF
/* if struct tm has tm_zone structure */
#undef HAVE_TM_ZONE
/* Define if you have the alphasort function. */
#undef HAVE_ALPHASORT
/* Define if you have the getcwd function. */
#undef HAVE_GETCWD
/* Define if you have the gethostbyname function. */
#undef HAVE_GETHOSTBYNAME
/* Define if you have the gethostname function. */
#undef HAVE_GETHOSTNAME
/* Define if you have the getpagesize function. */
#undef HAVE_GETPAGESIZE
/* Define if you have the inet_aton function. */
#undef HAVE_INET_ATON
/* Define if you have the scandir function. */
#undef HAVE_SCANDIR
/* Define if you have the select function. */
#undef HAVE_SELECT
/* Define if you have the socket function. */
#undef HAVE_SOCKET
/* Define if you have the strdup function. */
#undef HAVE_STRDUP
/* Define if you have the strstr function. */
#undef HAVE_STRSTR
/* Define if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define if you have the <ndir.h> header file. */
#undef HAVE_NDIR_H
/* Define if you have the <sys/dir.h> header file. */
#undef HAVE_SYS_DIR_H
/* Define if you have the <sys/fcntl.h> header file. */
#undef HAVE_SYS_FCNTL_H
/* Define if you have the <sys/ndir.h> header file. */
#undef HAVE_SYS_NDIR_H
/* Define if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have the dmalloc library (-ldmalloc). */
#undef HAVE_LIBDMALLOC
/* Define if you have the efence library (-lefence). */
#undef HAVE_LIBEFENCE

View File

@ -0,0 +1,53 @@
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
configure:537: checking for gunzip
configure:577: checking for flex
configure:611: checking for yywrap in -ll
configure:657: checking for bison
configure:690: checking for gcc
configure:803: checking whether the C compiler (gcc ) works
configure:819: gcc -o conftest conftest.c 1>&5
configure:816:1: warning: return type defaults to 'int' [-Wimplicit-int]
main(){return(0);}
^
configure:845: checking whether the C compiler (gcc ) is a cross-compiler
configure:850: checking whether we are using GNU C
configure:878: checking whether gcc accepts -g
configure:910: checking how to run the C preprocessor
configure:990: checking whether make sets ${MAKE}
configure:1025: checking for dirent.h that defines DIR
configure:1063: checking for opendir in -ldir
configure:1146: checking for ANSI C header files
configure:1250: checking for sys/wait.h that is POSIX.1 compatible
configure:1295: checking for fcntl.h
configure:1295: checking for sys/fcntl.h
configure:1295: checking for limits.h
configure:1295: checking for sys/time.h
configure:1295: checking for sys/select.h
configure:1335: checking for getopt.h
configure:1373: checking for working const
configure:1448: checking for uid_t in sys/types.h
configure:1482: checking for pid_t
configure:1515: checking whether time.h and sys/time.h may both be included
configure:1551: checking whether setvbuf arguments are reversed
configure:1600: checking for unistd.h
configure:1639: checking for getpagesize
configure:1692: checking for working mmap
configure:1865: checking for getcwd
configure:1865: checking for strdup
configure:1865: checking for strstr
configure:1920: checking for gethostname
configure:1920: checking for gethostbyname
configure:1920: checking for select
configure:1920: checking for socket
configure:1920: checking for inet_aton
configure:1975: checking for scandir
configure:1975: checking for alphasort
configure:2035: checking for tm.tm_gmtoff
configure:2110: checking for tm.tm_zone
configure:2186: checking for sockaddr_in.sin_len
configure:2441: checking compile and link profiling code
configure:2463: checking whether to compile and link debugging code
configure:2486: checking whether to link with the Dmalloc memory debugger/profiler
configure:2552: checking whether to link with the Electric Fence memory debugger

Binary file not shown.

View File

@ -0,0 +1,347 @@
#! /bin/sh
# Generated automatically by configure.
# Run this file to recreate the current configuration.
# This directory was configured as follows,
# on host hkc-VirtualBox:
#
# ./configure
#
# Compiler output produced by configure, useful for debugging
# configure, is in ./config.log if it exists.
ac_cs_usage="Usage: ./config.status [--recheck] [--version] [--help]"
for ac_option
do
case "$ac_option" in
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
echo "running ${CONFIG_SHELL-/bin/sh} ./configure --no-create --no-recursion"
exec ${CONFIG_SHELL-/bin/sh} ./configure --no-create --no-recursion ;;
-version | --version | --versio | --versi | --vers | --ver | --ve | --v)
echo "./config.status generated by autoconf version 2.13"
exit 0 ;;
-help | --help | --hel | --he | --h)
echo "$ac_cs_usage"; exit 0 ;;
*) echo "$ac_cs_usage"; exit 1 ;;
esac
done
ac_given_srcdir=.
trap 'rm -fr Makefile config.h conftest*; exit 1' 1 2 15
# Protect against being on the right side of a sed subst in config.status.
sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\&%]/\\&/g;
s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF
/^[ ]*VPATH[ ]*=[^:]*$/d
s%@SHELL@%/bin/sh%g
s%@CFLAGS@%-g -O2 -pipe -Wall%g
s%@CPPFLAGS@%%g
s%@CXXFLAGS@%%g
s%@FFLAGS@%%g
s%@DEFS@%-DHAVE_CONFIG_H%g
s%@LDFLAGS@% -g%g
s%@LIBS@% %g
s%@exec_prefix@%${prefix}%g
s%@prefix@%/usr/local%g
s%@program_transform_name@%s,x,x,%g
s%@bindir@%${exec_prefix}/bin%g
s%@sbindir@%${exec_prefix}/sbin%g
s%@libexecdir@%${exec_prefix}/libexec%g
s%@datadir@%${prefix}/share%g
s%@sysconfdir@%${prefix}/etc%g
s%@sharedstatedir@%${prefix}/com%g
s%@localstatedir@%${prefix}/var%g
s%@libdir@%${exec_prefix}/lib%g
s%@includedir@%${prefix}/include%g
s%@oldincludedir@%/usr/include%g
s%@infodir@%${prefix}/info%g
s%@mandir@%${prefix}/man%g
s%@GUNZIP@%/bin/gunzip%g
s%@LEX@%lex%g
s%@LEXLIB@%%g
s%@YACC@%bison -y%g
s%@CC@%gcc%g
s%@CPP@%gcc -E%g
s%@SET_MAKE@%%g
s%@STRUTIL@%%g
s%@ALPHASORT@%%g
s%@SCANDIR@%%g
CEOF
# Split the substitutions into bite-sized pieces for seds with
# small command number limits, like on Digital OSF/1 and HP-UX.
ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
ac_file=1 # Number of current file.
ac_beg=1 # First line for current file.
ac_end=$ac_max_sed_cmds # Line after last line for current file.
ac_more_lines=:
ac_sed_cmds=""
while $ac_more_lines; do
if test $ac_beg -gt 1; then
sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
else
sed "${ac_end}q" conftest.subs > conftest.s$ac_file
fi
if test ! -s conftest.s$ac_file; then
ac_more_lines=false
rm -f conftest.s$ac_file
else
if test -z "$ac_sed_cmds"; then
ac_sed_cmds="sed -f conftest.s$ac_file"
else
ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
fi
ac_file=`expr $ac_file + 1`
ac_beg=$ac_end
ac_end=`expr $ac_end + $ac_max_sed_cmds`
fi
done
if test -z "$ac_sed_cmds"; then
ac_sed_cmds=cat
fi
CONFIG_FILES=${CONFIG_FILES-"Makefile"}
for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
# Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
case "$ac_file" in
*:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
*) ac_file_in="${ac_file}.in" ;;
esac
# Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
# Remove last slash and all that follows it. Not all systems have dirname.
ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
# The file is in a subdirectory.
test ! -d "$ac_dir" && mkdir "$ac_dir"
ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
# A "../" for each directory in $ac_dir_suffix.
ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
else
ac_dir_suffix= ac_dots=
fi
case "$ac_given_srcdir" in
.) srcdir=.
if test -z "$ac_dots"; then top_srcdir=.
else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
/*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
*) # Relative path.
srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
top_srcdir="$ac_dots$ac_given_srcdir" ;;
esac
echo creating "$ac_file"
rm -f "$ac_file"
configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
case "$ac_file" in
*Makefile*) ac_comsub="1i\\
# $configure_input" ;;
*) ac_comsub= ;;
esac
ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
sed -e "$ac_comsub
s%@configure_input@%$configure_input%g
s%@srcdir@%$srcdir%g
s%@top_srcdir@%$top_srcdir%g
" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
fi; done
rm -f conftest.s*
# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
# NAME is the cpp macro being defined and VALUE is the value it is being given.
#
# ac_d sets the value in "#define NAME VALUE" lines.
ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
ac_dC='\3'
ac_dD='%g'
# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
ac_uB='\([ ]\)%\1#\2define\3'
ac_uC=' '
ac_uD='\4%g'
# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
ac_eB='$%\1#\2define\3'
ac_eC=' '
ac_eD='%g'
if test "${CONFIG_HEADERS+set}" != set; then
CONFIG_HEADERS="config.h"
fi
for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
# Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
case "$ac_file" in
*:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
*) ac_file_in="${ac_file}.in" ;;
esac
echo creating $ac_file
rm -f conftest.frag conftest.in conftest.out
ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
cat $ac_file_inputs > conftest.in
cat > conftest.frag <<CEOF
${ac_dA}GUNZIP${ac_dB}GUNZIP${ac_dC}"/bin/gunzip"${ac_dD}
${ac_uA}GUNZIP${ac_uB}GUNZIP${ac_uC}"/bin/gunzip"${ac_uD}
${ac_eA}GUNZIP${ac_eB}GUNZIP${ac_eC}"/bin/gunzip"${ac_eD}
${ac_dA}HAVE_DIRENT_H${ac_dB}HAVE_DIRENT_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_DIRENT_H${ac_uB}HAVE_DIRENT_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_DIRENT_H${ac_eB}HAVE_DIRENT_H${ac_eC}1${ac_eD}
${ac_dA}STDC_HEADERS${ac_dB}STDC_HEADERS${ac_dC}1${ac_dD}
${ac_uA}STDC_HEADERS${ac_uB}STDC_HEADERS${ac_uC}1${ac_uD}
${ac_eA}STDC_HEADERS${ac_eB}STDC_HEADERS${ac_eC}1${ac_eD}
${ac_dA}HAVE_SYS_WAIT_H${ac_dB}HAVE_SYS_WAIT_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_WAIT_H${ac_uB}HAVE_SYS_WAIT_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_WAIT_H${ac_eB}HAVE_SYS_WAIT_H${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_FCNTL_H${ac_dB}HAVE_FCNTL_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_FCNTL_H${ac_uB}HAVE_FCNTL_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_FCNTL_H${ac_eB}HAVE_FCNTL_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_SYS_FCNTL_H${ac_dB}HAVE_SYS_FCNTL_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_FCNTL_H${ac_uB}HAVE_SYS_FCNTL_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_FCNTL_H${ac_eB}HAVE_SYS_FCNTL_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_LIMITS_H${ac_dB}HAVE_LIMITS_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_LIMITS_H${ac_uB}HAVE_LIMITS_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_LIMITS_H${ac_eB}HAVE_LIMITS_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_SYS_TIME_H${ac_dB}HAVE_SYS_TIME_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_TIME_H${ac_uB}HAVE_SYS_TIME_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_TIME_H${ac_eB}HAVE_SYS_TIME_H${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_SYS_SELECT_H${ac_dB}HAVE_SYS_SELECT_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_SYS_SELECT_H${ac_uB}HAVE_SYS_SELECT_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_SYS_SELECT_H${ac_eB}HAVE_SYS_SELECT_H${ac_eC}1${ac_eD}
${ac_dA}HAVE_GETOPT_H${ac_dB}HAVE_GETOPT_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETOPT_H${ac_uB}HAVE_GETOPT_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETOPT_H${ac_eB}HAVE_GETOPT_H${ac_eC}1${ac_eD}
${ac_dA}TIME_WITH_SYS_TIME${ac_dB}TIME_WITH_SYS_TIME${ac_dC}1${ac_dD}
${ac_uA}TIME_WITH_SYS_TIME${ac_uB}TIME_WITH_SYS_TIME${ac_uC}1${ac_uD}
${ac_eA}TIME_WITH_SYS_TIME${ac_eB}TIME_WITH_SYS_TIME${ac_eC}1${ac_eD}
${ac_dA}HAVE_UNISTD_H${ac_dB}HAVE_UNISTD_H${ac_dC}1${ac_dD}
${ac_uA}HAVE_UNISTD_H${ac_uB}HAVE_UNISTD_H${ac_uC}1${ac_uD}
${ac_eA}HAVE_UNISTD_H${ac_eB}HAVE_UNISTD_H${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_GETPAGESIZE${ac_dB}HAVE_GETPAGESIZE${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETPAGESIZE${ac_uB}HAVE_GETPAGESIZE${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETPAGESIZE${ac_eB}HAVE_GETPAGESIZE${ac_eC}1${ac_eD}
${ac_dA}HAVE_MMAP${ac_dB}HAVE_MMAP${ac_dC}1${ac_dD}
${ac_uA}HAVE_MMAP${ac_uB}HAVE_MMAP${ac_uC}1${ac_uD}
${ac_eA}HAVE_MMAP${ac_eB}HAVE_MMAP${ac_eC}1${ac_eD}
${ac_dA}HAVE_GETCWD${ac_dB}HAVE_GETCWD${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETCWD${ac_uB}HAVE_GETCWD${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETCWD${ac_eB}HAVE_GETCWD${ac_eC}1${ac_eD}
${ac_dA}HAVE_STRDUP${ac_dB}HAVE_STRDUP${ac_dC}1${ac_dD}
${ac_uA}HAVE_STRDUP${ac_uB}HAVE_STRDUP${ac_uC}1${ac_uD}
${ac_eA}HAVE_STRDUP${ac_eB}HAVE_STRDUP${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_STRSTR${ac_dB}HAVE_STRSTR${ac_dC}1${ac_dD}
${ac_uA}HAVE_STRSTR${ac_uB}HAVE_STRSTR${ac_uC}1${ac_uD}
${ac_eA}HAVE_STRSTR${ac_eB}HAVE_STRSTR${ac_eC}1${ac_eD}
${ac_dA}HAVE_GETHOSTNAME${ac_dB}HAVE_GETHOSTNAME${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETHOSTNAME${ac_uB}HAVE_GETHOSTNAME${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETHOSTNAME${ac_eB}HAVE_GETHOSTNAME${ac_eC}1${ac_eD}
${ac_dA}HAVE_GETHOSTBYNAME${ac_dB}HAVE_GETHOSTBYNAME${ac_dC}1${ac_dD}
${ac_uA}HAVE_GETHOSTBYNAME${ac_uB}HAVE_GETHOSTBYNAME${ac_uC}1${ac_uD}
${ac_eA}HAVE_GETHOSTBYNAME${ac_eB}HAVE_GETHOSTBYNAME${ac_eC}1${ac_eD}
${ac_dA}HAVE_SELECT${ac_dB}HAVE_SELECT${ac_dC}1${ac_dD}
${ac_uA}HAVE_SELECT${ac_uB}HAVE_SELECT${ac_uC}1${ac_uD}
${ac_eA}HAVE_SELECT${ac_eB}HAVE_SELECT${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}HAVE_SOCKET${ac_dB}HAVE_SOCKET${ac_dC}1${ac_dD}
${ac_uA}HAVE_SOCKET${ac_uB}HAVE_SOCKET${ac_uC}1${ac_uD}
${ac_eA}HAVE_SOCKET${ac_eB}HAVE_SOCKET${ac_eC}1${ac_eD}
${ac_dA}HAVE_INET_ATON${ac_dB}HAVE_INET_ATON${ac_dC}1${ac_dD}
${ac_uA}HAVE_INET_ATON${ac_uB}HAVE_INET_ATON${ac_uC}1${ac_uD}
${ac_eA}HAVE_INET_ATON${ac_eB}HAVE_INET_ATON${ac_eC}1${ac_eD}
${ac_dA}HAVE_SCANDIR${ac_dB}HAVE_SCANDIR${ac_dC}1${ac_dD}
${ac_uA}HAVE_SCANDIR${ac_uB}HAVE_SCANDIR${ac_uC}1${ac_uD}
${ac_eA}HAVE_SCANDIR${ac_eB}HAVE_SCANDIR${ac_eC}1${ac_eD}
${ac_dA}HAVE_ALPHASORT${ac_dB}HAVE_ALPHASORT${ac_dC}1${ac_dD}
${ac_uA}HAVE_ALPHASORT${ac_uB}HAVE_ALPHASORT${ac_uC}1${ac_uD}
${ac_eA}HAVE_ALPHASORT${ac_eB}HAVE_ALPHASORT${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
${ac_dA}STRUCT_TM_HAS_TM_GMTOFF${ac_dB}STRUCT_TM_HAS_TM_GMTOFF${ac_dC}1${ac_dD}
${ac_uA}STRUCT_TM_HAS_TM_GMTOFF${ac_uB}STRUCT_TM_HAS_TM_GMTOFF${ac_uC}1${ac_uD}
${ac_eA}STRUCT_TM_HAS_TM_GMTOFF${ac_eB}STRUCT_TM_HAS_TM_GMTOFF${ac_eC}1${ac_eD}
${ac_dA}HAVE_TM_GMTOFF${ac_dB}HAVE_TM_GMTOFF${ac_dC}1${ac_dD}
${ac_uA}HAVE_TM_GMTOFF${ac_uB}HAVE_TM_GMTOFF${ac_uC}1${ac_uD}
${ac_eA}HAVE_TM_GMTOFF${ac_eB}HAVE_TM_GMTOFF${ac_eC}1${ac_eD}
${ac_dA}STRUCT_TM_HAS_TM_ZONE${ac_dB}STRUCT_TM_HAS_TM_ZONE${ac_dC}1${ac_dD}
${ac_uA}STRUCT_TM_HAS_TM_ZONE${ac_uB}STRUCT_TM_HAS_TM_ZONE${ac_uC}1${ac_uD}
${ac_eA}STRUCT_TM_HAS_TM_ZONE${ac_eB}STRUCT_TM_HAS_TM_ZONE${ac_eC}1${ac_eD}
${ac_dA}HAVE_TM_ZONE${ac_dB}HAVE_TM_ZONE${ac_dC}1${ac_dD}
${ac_uA}HAVE_TM_ZONE${ac_uB}HAVE_TM_ZONE${ac_uC}1${ac_uD}
${ac_eA}HAVE_TM_ZONE${ac_eB}HAVE_TM_ZONE${ac_eC}1${ac_eD}
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
cat > conftest.frag <<CEOF
s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
CEOF
sed -f conftest.frag conftest.in > conftest.out
rm -f conftest.in
mv conftest.out conftest.in
rm -f conftest.frag conftest.h
echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
cat conftest.in >> conftest.h
rm -f conftest.in
if cmp -s $ac_file conftest.h 2>/dev/null; then
echo "$ac_file is unchanged"
rm -f conftest.h
else
# Remove last slash and all that follows it. Not all systems have dirname.
ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
# The file is in a subdirectory.
test ! -d "$ac_dir" && mkdir "$ac_dir"
fi
rm -f $ac_file
mv conftest.h $ac_file
fi
fi; done
exit 0

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,196 @@
dnl $Id: configure.in,v 1.21.2.5 2002/07/28 02:38:32 jnelson Exp $
dnl Process this file with autoconf to produce a configure script.
AC_INIT(boa.c)
dnl Look for gunzip
AC_PATH_PROG(GUNZIP, gunzip)
AC_DEFINE_UNQUOTED(GUNZIP, "$ac_cv_path_GUNZIP")
dnl Checks for programs.
AC_PROG_LEX
AC_PROG_YACC
AC_PROG_CC
AC_PROG_CPP
AC_PROG_MAKE_SET
dnl Checks for libraries.
dnl Make config.h
AC_CONFIG_HEADER(config.h)
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(fcntl.h sys/fcntl.h limits.h sys/time.h sys/select.h)
AC_CHECK_HEADERS(getopt.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_UID_T
AC_TYPE_PID_T
AC_HEADER_TIME
dnl Checks for library functions.
AC_FUNC_SETVBUF_REVERSED
AC_FUNC_MMAP
AC_CHECK_FUNCS(getcwd strdup strstr)
AC_CHECK_FUNCS(gethostname gethostbyname select socket inet_aton)
AC_CHECK_FUNCS(scandir alphasort)
AC_CHECK_STRUCT_FOR([
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
],tm,tm_gmtoff)
if test "$ac_cv_struct_tm_has_tm_gmtoff" = "yes"; then
AC_DEFINE(HAVE_TM_GMTOFF)
fi
AC_CHECK_STRUCT_FOR([
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
],tm,tm_zone)
if test "$ac_cv_struct_tm_has_tm_zone" = "yes"; then
AC_DEFINE(HAVE_TM_ZONE)
fi
AC_CHECK_STRUCT_FOR([
#include <sys/types.h>
#include <netinet/in.h>
],sockaddr_in,sin_len)
if test "$ac_cv_struct_sockaddr_in_has_sin_len" = "yes"; then
AC_DEFINE(HAVE_SIN_LEN)
fi
if test $ac_cv_func_socket = no; then
# socket is not in the default libraries.
AC_CHECK_LIB(socket, socket,
[ MYLIBS="$MYLIBS -lsocket" ])
fi
if test $ac_cv_func_inet_aton = no; then
# inet_aton is not in the default libraries.
AC_CHECK_LIB(resolv, inet_aton, MYLIBS="$MYLIBS -lresolv")
fi
if test $ac_cv_func_gethostname = no; then
AC_CHECK_LIB(nsl, gethostname, MYLIBS="$MYLIBS -lnsl")
fi
dnl May end up with duplicate -lnsl -- oh well
if test $ac_cv_func_gethostbyname = no; then
AC_CHECK_LIB(nsl, gethostbyname, MYLIBS="$MYLIBS -lnsl")
fi
LIBS="$LIBS $MYLIBS"
if test $ac_cv_func_scandir = no; then
# scandir not defined, add it
SCANDIR="scandir.o"
fi
if test $ac_cv_func_alphasort = no; then
# alphasort not defined, add it
ALPHASORT="alphasort.o"
fi
if test $ac_cv_func_strdup = no -o $ac_cv_func_strstr = no; then
# strdup or strstr not defined
STRUTIL="strutil.o"
fi
if test -n "$GCC"; then
dnl if we are running gcc, use -pipe
test -n "$GCC" && CFLAGS="$CFLAGS -pipe"
AC_MSG_CHECKING(compile and link profiling code)
AC_ARG_ENABLE(profiling,
[ --enable-profiling Compile and link profiling code],
[
if test "$enableval" = "yes" ; then
AC_MSG_RESULT(yes)
CFLAGS="$CFLAGS -pg"
LDFLAGS="$LDFLAGS -g -pg"
else
AC_MSG_RESULT(no)
fi
],
[
AC_MSG_RESULT(no)
])
fi
AC_MSG_CHECKING(whether to compile and link debugging code)
AC_ARG_ENABLE(debug,
[ --disable-debug Compile and link debugging code],
[
if test "$enableval" = "yes" ; then
AC_MSG_RESULT(yes)
LDFLAGS="$LDFLAGS -g"
test -n "$GCC" && CFLAGS="$CFLAGS -Wall"
else
AC_MSG_RESULT(no)
fi
],
[
AC_MSG_RESULT(yes)
LDFLAGS="$LDFLAGS -g"
test -n "$GCC" && CFLAGS="$CFLAGS -Wall"
])
AC_MSG_CHECKING(whether to link with the Dmalloc memory debugger/profiler)
AC_ARG_WITH(dmalloc,
[ --with-dmalloc link with the Dmalloc memory debugger/profiler],
[
if test "$withval" = "yes"; then
AC_MSG_RESULT(trying)
AC_CHECK_LIB(dmalloc, dmalloc_shutdown)
else
AC_MSG_RESULT(no)
fi
],
[
AC_MSG_RESULT(no)
])
AC_MSG_CHECKING(whether to link with the Electric Fence memory debugger)
AC_ARG_WITH(efence,
[ --with-efence link with the Electric Fence memory debugger ],
[
if test "$withval" = "yes"; then
AC_MSG_RESULT(trying)
AC_CHECK_LIB(efence, main)
else
AC_MSG_RESULT(no)
fi
],
[
AC_MSG_RESULT(no)
])
dnl For anything that wasn't found but we have source for
AC_SUBST(STRUTIL)
AC_SUBST(ALPHASORT)
AC_SUBST(SCANDIR)
AC_OUTPUT(Makefile)

View File

@ -0,0 +1,198 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1997 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: defines.h,v 1.107.2.2 2002/06/07 02:57:23 jnelson Exp $*/
#ifndef _DEFINES_H
#define _DEFINES_H
/***** Change this, or use -c on the command line to specify it *****/
#ifndef SERVER_ROOT
#define SERVER_ROOT "/etc/boa"
#endif
/***** Change this via the CGIPath configuration value in boa.conf *****/
#define DEFAULT_PATH "/bin:/usr/bin:/usr/local/bin"
/***** Change this via the SinglePostLimit configuration value in boa.conf *****/
#define SINGLE_POST_LIMIT_DEFAULT 1024 * 1024 /* 1 MB */
/***** Various stuff that you may want to tweak, but probably shouldn't *****/
#define SOCKETBUF_SIZE 8192
#define MAX_HEADER_LENGTH 1024
#define CLIENT_STREAM_SIZE SOCKETBUF_SIZE
#define BUFFER_SIZE CLIENT_STREAM_SIZE
#define MIME_HASHTABLE_SIZE 47
#define ALIAS_HASHTABLE_SIZE 17
#define PASSWD_HASHTABLE_SIZE 47
#define REQUEST_TIMEOUT 60
#define CGI_MIME_TYPE "application/x-httpd-cgi"
/***** CHANGE ANYTHING BELOW THIS LINE AT YOUR OWN PERIL *****/
/***** You will probably introduce buffer overruns unless you know
what you are doing *****/
#define MAX_SITENAME_LENGTH 256
#define MAX_LOG_LENGTH MAX_HEADER_LENGTH + 1024
#define MAX_FILE_LENGTH NAME_MAX
#define MAX_PATH_LENGTH PATH_MAX
#ifdef ACCEPT_ON
#define MAX_ACCEPT_LENGTH MAX_HEADER_LENGTH
#else
#define MAX_ACCEPT_LENGTH 0
#endif
#ifndef SERVER_VERSION
#define SERVER_VERSION "Boa/0.94.13"
#endif
#define CGI_VERSION "CGI/1.1"
#define COMMON_CGI_COUNT 6
#define CGI_ENV_MAX 50
#define CGI_ARGC_MAX 128
/******************* RESPONSE CLASSES *****************/
#define R_INFORMATIONAL 1
#define R_SUCCESS 2
#define R_REDIRECTION 3
#define R_CLIENT_ERROR 4
#define R_SERVER_ERROR 5
/******************* RESPONSE CODES ******************/
#define R_REQUEST_OK 200
#define R_CREATED 201
#define R_ACCEPTED 202
#define R_PROVISIONAL 203 /* provisional information */
#define R_NO_CONTENT 204
#define R_MULTIPLE 300 /* multiple choices */
#define R_MOVED_PERM 301
#define R_MOVED_TEMP 302
#define R_NOT_MODIFIED 304
#define R_BAD_REQUEST 400
#define R_UNAUTHORIZED 401
#define R_PAYMENT 402 /* payment required */
#define R_FORBIDDEN 403
#define R_NOT_FOUND 404
#define R_METHOD_NA 405 /* method not allowed */
#define R_NONE_ACC 406 /* none acceptable */
#define R_PROXY 407 /* proxy authentication required */
#define R_REQUEST_TO 408 /* request timeout */
#define R_CONFLICT 409
#define R_GONE 410
#define R_ERROR 500 /* internal server error */
#define R_NOT_IMP 501 /* not implemented */
#define R_BAD_GATEWAY 502
#define R_SERVICE_UNAV 503 /* service unavailable */
#define R_GATEWAY_TO 504 /* gateway timeout */
#define R_BAD_VERSION 505
/****************** METHODS *****************/
#define M_GET 1
#define M_HEAD 2
#define M_PUT 3
#define M_POST 4
#define M_DELETE 5
#define M_LINK 6
#define M_UNLINK 7
/************** REQUEST STATUS (req->status) ***************/
#define READ_HEADER 0
#define ONE_CR 1
#define ONE_LF 2
#define TWO_CR 3
#define BODY_READ 4
#define BODY_WRITE 5
#define WRITE 6
#define PIPE_READ 7
#define PIPE_WRITE 8
#define DONE 9
#define DEAD 10
/************** CGI TYPE (req->is_cgi) ******************/
#define CGI 1
#define NPH 2
/************* ALIAS TYPES (aliasp->type) ***************/
#define ALIAS 0
#define SCRIPTALIAS 1
#define REDIRECT 2
/*********** KEEPALIVE CONSTANTS (req->keepalive) *******/
#define KA_INACTIVE 0
#define KA_STOPPED 1
#define KA_ACTIVE 2
/********* CGI STATUS CONSTANTS (req->cgi_status) *******/
#define CGI_PARSE 1
#define CGI_BUFFER 2
#define CGI_DONE 3
/*********** MMAP_LIST CONSTANTS ************************/
#define MMAP_LIST_SIZE 256
#define MMAP_LIST_MASK 255
#define MMAP_LIST_USE_MAX 128
#define MMAP_LIST_NEXT(i) (((i)+1)&MMAP_LIST_MASK)
#define MMAP_LIST_HASH(dev,ino,size) ((ino)&MMAP_LIST_MASK)
#define MAX_FILE_MMAP 100 * 1024 /* 100K */
/***************** USEFUL MACROS ************************/
#define SQUASH_KA(req) (req->keepalive=KA_STOPPED)
#define BOA_FD_SET(fd, where) { FD_SET(fd, where); \
if (fd > max_fd) max_fd = fd; \
}
/* If and when everyone has a modern gcc or other near-C99 compiler,
* change these to static inline functions. Also note that since
* we never fuss with O_APPEND append or O_ASYNC, we shouldn't have
* to perform an extra system call to F_GETFL first.
*/
#ifdef BOA_USE_GETFL
#define set_block_fd(fd) real_set_block_fd(fd)
#define set_nonblock_fd(fd) real_set_nonblock_fd(fd)
#else
#define set_block_fd(fd) fcntl(fd, F_SETFL, 0)
#define set_nonblock_fd(fd) fcntl(fd, F_SETFL, NOBLOCK)
#endif
#define DIE(mesg) log_error_mesg(__FILE__, __LINE__, mesg), exit(1)
#define WARN(mesg) log_error_mesg(__FILE__, __LINE__, mesg)
#endif

View File

@ -0,0 +1,95 @@
/*
* Boa, an http server
* escape.c
* Copyright (C) 2001 Jon Nelson <jnelson@boa.org>
* Based on escape.pl, Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Copyright (C) 2001 Larry Doolittle <ldoolitt@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: escape.c,v 1.7 2002/03/22 04:24:09 jnelson Exp $ */
/*
unreserved = alnum | mark
alnum = "0".."9" | "A".."Z" | "a".."z"
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
noescape = unreserved | ":" | "@" | "&" | "=" | "+" | "$" | "," | "/"
*/
#ifdef TEST
#include <stdio.h>
#include <stdlib.h>
#else
#include "boa.h"
#endif
#include "escape.h"
unsigned long _needs_escape[(NEEDS_ESCAPE_BITS+NEEDS_ESCAPE_WORD_LENGTH-1)/NEEDS_ESCAPE_WORD_LENGTH];
void build_needs_escape(void)
{
unsigned int a, b;
const unsigned char special[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"-_.!~*'():@&=+$,/?";
/* 21 Mar 2002 - jnelson - confirm with Apache 1.3.23 that '?'
* is safe to leave unescaped.
*/
unsigned short i, j;
b = 1;
for (a=0; b!=0; a++) b=b<<1;
/* I found $a bit positions available in an unsigned long. */
if (a < NEEDS_ESCAPE_WORD_LENGTH) {
fprintf(stderr,
"NEEDS_ESCAPE_SHIFT configuration error -- "\
"%d should be <= log2(%d)\n",
NEEDS_ESCAPE_SHIFT, a);
exit(1);
} else if (a >= 2*NEEDS_ESCAPE_WORD_LENGTH) {
/* needs_escape_shift configuration suboptimal */
} else {
/* Ahh, just right! */;
}
memset(_needs_escape, ~0, sizeof(_needs_escape));
for(i = 0; i < sizeof(special) - 1; ++i) {
j=special[i];
if (j>=NEEDS_ESCAPE_BITS) {
/* warning: character $j will be needlessly escaped. */
} else {
_needs_escape[NEEDS_ESCAPE_INDEX(j)]&=~NEEDS_ESCAPE_MASK(j);
}
}
}
#ifdef TEST
int main(void)
{
int i;
build_needs_escape();
for(i = 0; i <= NEEDS_ESCAPE_BITS; ++i) {
if (needs_escape(i)) {
fprintf(stdout, "%3d needs escape.\n", i);
}
}
return(0);
}
#endif

View File

@ -0,0 +1,47 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Copyright (C) 2001 Jon Nelson <jnelson@boa.org>
* Copyright (C) 2001 Larry Doolittle <ldoolitt@boa.org>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: escape.h,v 1.18 2002/01/31 03:12:21 jnelson Exp $ */
#include "config.h"
/* Highest character number that can possibly be passed through un-escaped */
#define NEEDS_ESCAPE_BITS 128
#ifndef NEEDS_ESCAPE_SHIFT
#define NEEDS_ESCAPE_SHIFT 5 /* 1 << 5 is 32 bits */
#endif
#define NEEDS_ESCAPE_WORD_LENGTH (1<<NEEDS_ESCAPE_SHIFT)
#define NEEDS_ESCAPE_INDEX(c) ((c)>>NEEDS_ESCAPE_SHIFT)
/* Assume variable shift is fast, otherwise this could be a table lookup */
#define NEEDS_ESCAPE_MASK(c) (1<<((c)&(NEEDS_ESCAPE_WORD_LENGTH - 1)))
/* Newer compilers could use an inline function.
* This macro works great, as long as you pass unsigned int or unsigned char.
*/
#define needs_escape(c) ((c)>=NEEDS_ESCAPE_BITS || _needs_escape[NEEDS_ESCAPE_INDEX(c)]&NEEDS_ESCAPE_MASK(c))
extern unsigned long _needs_escape[(NEEDS_ESCAPE_BITS+NEEDS_ESCAPE_WORD_LENGTH-1)/NEEDS_ESCAPE_WORD_LENGTH];
void build_needs_escape(void);

Binary file not shown.

View File

@ -0,0 +1,513 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996,99 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: get.c,v 1.76.2.5 2002/07/26 03:05:59 jnelson Exp $*/
#include "boa.h"
/* local prototypes */
int get_cachedir_file(request * req, struct stat *statbuf);
int index_directory(request * req, char *dest_filename);
/*
* Name: init_get
* Description: Initializes a non-script GET or HEAD request.
*
* Return values:
* 0: finished or error, request will be freed
* 1: successfully initialized, added to ready queue
*/
int init_get(request * req)
{
int data_fd, saved_errno;
struct stat statbuf;
volatile int bytes;
data_fd = open(req->pathname, O_RDONLY);
saved_errno = errno; /* might not get used */
#ifdef GUNZIP
if (data_fd == -1 && errno == ENOENT) {
/* cannot open */
/* it's either a gunzipped file or a directory */
char gzip_pathname[MAX_PATH_LENGTH];
int len;
len = strlen(req->pathname);
memcpy(gzip_pathname, req->pathname, len);
memcpy(gzip_pathname + len, ".gz", 3);
gzip_pathname[len + 3] = '\0';
data_fd = open(gzip_pathname, O_RDONLY);
if (data_fd != -1) {
close(data_fd);
req->response_status = R_REQUEST_OK;
if (req->pathname)
free(req->pathname);
req->pathname = strdup(gzip_pathname);
if (!req->pathname) {
log_error_time();
perror("strdup");
send_r_error(req);
return 0;
}
if (!req->simple) {
req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
print_http_headers(req);
print_content_type(req);
print_last_modified(req);
req_write(req, "\r\n");
req_flush(req);
}
if (req->method == M_HEAD)
return 0;
return init_cgi(req);
}
}
#endif
if (data_fd == -1) {
log_error_doc(req);
errno = saved_errno;
perror("document open");
if (saved_errno == ENOENT)
send_r_not_found(req);
else if (saved_errno == EACCES)
send_r_forbidden(req);
else
send_r_bad_request(req);
return 0;
}
fstat(data_fd, &statbuf);
if (S_ISDIR(statbuf.st_mode)) { /* directory */
close(data_fd); /* close dir */
if (req->pathname[strlen(req->pathname) - 1] != '/') {
char buffer[3 * MAX_PATH_LENGTH + 128];
if (server_port != 80)
sprintf(buffer, "http://%s:%d%s/", server_name,
server_port, req->request_uri);
else
sprintf(buffer, "http://%s%s/", server_name,
req->request_uri);
send_r_moved_perm(req, buffer);
return 0;
}
data_fd = get_dir(req, &statbuf); /* updates statbuf */
if (data_fd == -1) /* couldn't do it */
return 0; /* errors reported by get_dir */
else if (data_fd <= 1)
/* data_fd == 0 -> close it down, 1 -> continue */
return data_fd;
/* else, data_fd contains the fd of the file... */
}
if (req->if_modified_since &&
!modified_since(&(statbuf.st_mtime), req->if_modified_since)) {
send_r_not_modified(req);
close(data_fd);
return 0;
}
req->filesize = statbuf.st_size;
req->last_modified = statbuf.st_mtime;
if (req->method == M_HEAD || req->filesize == 0) {
send_r_request_ok(req);
close(data_fd);
return 0;
}
if (req->filesize > MAX_FILE_MMAP) {
send_r_request_ok(req); /* All's well */
req->status = PIPE_READ;
req->cgi_status = CGI_BUFFER;
req->data_fd = data_fd;
req_flush(req); /* this should *always* complete due to
the size of the I/O buffers */
req->header_line = req->header_end = req->buffer;
return 1;
}
if (req->filesize == 0) { /* done */
send_r_request_ok(req); /* All's well *so far* */
close(data_fd);
return 1;
}
/* NOTE: I (Jon Nelson) tried performing a read(2)
* into the output buffer provided the file data would
* fit, before mmapping, and if successful, writing that
* and stopping there -- all to avoid the cost
* of a mmap. Oddly, it was *slower* in benchmarks.
*/
req->mmap_entry_var = find_mmap(data_fd, &statbuf);
if (req->mmap_entry_var == NULL) {
req->buffer_end = 0;
if (errno == ENOENT)
send_r_not_found(req);
else if (errno == EACCES)
send_r_forbidden(req);
else
send_r_bad_request(req);
close(data_fd);
return 0;
}
req->data_mem = req->mmap_entry_var->mmap;
close(data_fd); /* close data file */
if ((long) req->data_mem == -1) {
boa_perror(req, "mmap");
return 0;
}
send_r_request_ok(req); /* All's well */
bytes = BUFFER_SIZE - req->buffer_end;
/* bytes is now how much the buffer can hold
* after the headers
*/
if (bytes > 0) {
if (bytes > req->filesize)
bytes = req->filesize;
if (sigsetjmp(env, 1) == 0) {
handle_sigbus = 1;
memcpy(req->buffer + req->buffer_end, req->data_mem, bytes);
handle_sigbus = 0;
/* OK, SIGBUS **after** this point is very bad! */
} else {
/* sigbus! */
log_error_doc(req);
reset_output_buffer(req);
send_r_error(req);
fprintf(stderr, "%sGot SIGBUS in memcpy!\n", get_commonlog_time());
return 0;
}
req->buffer_end += bytes;
req->filepos += bytes;
if (req->filesize == req->filepos) {
req_flush(req);
req->status = DONE;
}
}
/* We lose statbuf here, so make sure response has been sent */
return 1;
}
/*
* Name: process_get
* Description: Writes a chunk of data to the socket.
*
* Return values:
* -1: request blocked, move to blocked queue
* 0: EOF or error, close it down
* 1: successful write, recycle in ready queue
*/
int process_get(request * req)
{
int bytes_written;
volatile int bytes_to_write;
bytes_to_write = req->filesize - req->filepos;
if (bytes_to_write > SOCKETBUF_SIZE)
bytes_to_write = SOCKETBUF_SIZE;
if (sigsetjmp(env, 1) == 0) {
handle_sigbus = 1;
bytes_written = write(req->fd, req->data_mem + req->filepos,
bytes_to_write);
handle_sigbus = 0;
/* OK, SIGBUS **after** this point is very bad! */
} else {
/* sigbus! */
log_error_doc(req);
/* sending an error here is inappropriate
* if we are here, the file is mmapped, and thus,
* a content-length has been sent. If we send fewer bytes
* the client knows there has been a problem.
* We run the risk of accidentally sending the right number
* of bytes (or a few too many) and the client
* won't be the wiser.
*/
req->status = DEAD;
fprintf(stderr, "%sGot SIGBUS in write(2)!\n", get_commonlog_time());
return 0;
}
if (bytes_written < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN)
return -1;
/* request blocked at the pipe level, but keep going */
else {
if (errno != EPIPE) {
log_error_doc(req);
/* Can generate lots of log entries, */
perror("write");
/* OK to disable if your logs get too big */
}
req->status = DEAD;
return 0;
}
}
req->filepos += bytes_written;
if (req->filepos == req->filesize) { /* EOF */
return 0;
} else
return 1; /* more to do */
}
/*
* Name: get_dir
* Description: Called from process_get if the request is a directory.
* statbuf must describe directory on input, since we may need its
* device, inode, and mtime.
* statbuf is updated, since we may need to check mtimes of a cache.
* returns:
* -1 error
* 0 cgi (either gunzip or auto-generated)
* >0 file descriptor of file
*/
int get_dir(request * req, struct stat *statbuf)
{
char pathname_with_index[MAX_PATH_LENGTH];
int data_fd;
if (directory_index) { /* look for index.html first?? */
strcpy(pathname_with_index, req->pathname);
strcat(pathname_with_index, directory_index);
/*
sprintf(pathname_with_index, "%s%s", req->pathname, directory_index);
*/
data_fd = open(pathname_with_index, O_RDONLY);
if (data_fd != -1) { /* user's index file */
strcpy(req->request_uri, directory_index); /* for mimetype */
fstat(data_fd, statbuf);
return data_fd;
}
if (errno == EACCES) {
send_r_forbidden(req);
return -1;
} else if (errno != ENOENT) {
/* if there is an error *other* than EACCES or ENOENT */
send_r_not_found(req);
return -1;
}
#ifdef GUNZIP
/* if we are here, trying index.html didn't work
* try index.html.gz
*/
strcat(pathname_with_index, ".gz");
data_fd = open(pathname_with_index, O_RDONLY);
if (data_fd != -1) { /* user's index file */
close(data_fd);
req->response_status = R_REQUEST_OK;
SQUASH_KA(req);
if (req->pathname)
free(req->pathname);
req->pathname = strdup(pathname_with_index);
if (!req->pathname) {
log_error_time();
perror("strdup");
send_r_error(req);
return 0;
}
if (!req->simple) {
req_write(req, "HTTP/1.0 200 OK-GUNZIP\r\n");
print_http_headers(req);
print_last_modified(req);
req_write(req, "Content-Type: ");
req_write(req, get_mime_type(directory_index));
req_write(req, "\r\n\r\n");
req_flush(req);
}
if (req->method == M_HEAD)
return 0;
return init_cgi(req);
}
#endif
}
/* only here if index.html, index.html.gz don't exist */
if (dirmaker != NULL) { /* don't look for index.html... maybe automake? */
req->response_status = R_REQUEST_OK;
SQUASH_KA(req);
/* the indexer should take care of all headers */
if (!req->simple) {
req_write(req, "HTTP/1.0 200 OK\r\n");
print_http_headers(req);
print_last_modified(req);
req_write(req, "Content-Type: text/html\r\n\r\n");
req_flush(req);
}
if (req->method == M_HEAD)
return 0;
return init_cgi(req);
/* in this case, 0 means success */
} else if (cachedir) {
return get_cachedir_file(req, statbuf);
} else { /* neither index.html nor autogenerate are allowed */
send_r_forbidden(req);
return -1; /* nothing worked */
}
}
int get_cachedir_file(request * req, struct stat *statbuf)
{
char pathname_with_index[MAX_PATH_LENGTH];
int data_fd;
time_t real_dir_mtime;
real_dir_mtime = statbuf->st_mtime;
sprintf(pathname_with_index, "%s/dir.%d.%ld",
cachedir, (int) statbuf->st_dev, statbuf->st_ino);
data_fd = open(pathname_with_index, O_RDONLY);
if (data_fd != -1) { /* index cache */
fstat(data_fd, statbuf);
if (statbuf->st_mtime > real_dir_mtime) {
statbuf->st_mtime = real_dir_mtime; /* lie */
strcpy(req->request_uri, directory_index); /* for mimetype */
return data_fd;
}
close(data_fd);
unlink(pathname_with_index); /* cache is stale, delete it */
}
if (index_directory(req, pathname_with_index) == -1)
return -1;
data_fd = open(pathname_with_index, O_RDONLY); /* Last chance */
if (data_fd != -1) {
strcpy(req->request_uri, directory_index); /* for mimetype */
fstat(data_fd, statbuf);
statbuf->st_mtime = real_dir_mtime; /* lie */
return data_fd;
}
boa_perror(req, "re-opening dircache");
return -1; /* Nothing worked. */
}
/*
* Name: index_directory
* Description: Called from get_cachedir_file if a directory html
* has to be generated on the fly
* returns -1 for problem, else 0
* This version is the fastest, ugliest, and most accurate yet.
* It solves the "stale size or type" problem by not ever giving
* the size or type. This also speeds it up since no per-file
* stat() is required.
*/
int index_directory(request * req, char *dest_filename)
{
DIR *request_dir;
FILE *fdstream;
struct dirent *dirbuf;
int bytes = 0;
char *escname = NULL;
if (chdir(req->pathname) == -1) {
if (errno == EACCES || errno == EPERM) {
send_r_forbidden(req);
} else {
log_error_doc(req);
perror("chdir");
send_r_bad_request(req);
}
return -1;
}
request_dir = opendir(".");
if (request_dir == NULL) {
int errno_save = errno;
send_r_error(req);
log_error_time();
fprintf(stderr, "directory \"%s\": ", req->pathname);
errno = errno_save;
perror("opendir");
return -1;
}
fdstream = fopen(dest_filename, "w");
if (fdstream == NULL) {
boa_perror(req, "dircache fopen");
closedir(request_dir);
return -1;
}
bytes += fprintf(fdstream,
"<HTML><HEAD>\n<TITLE>Index of %s</TITLE>\n</HEAD>\n\n",
req->request_uri);
bytes += fprintf(fdstream, "<BODY>\n\n<H2>Index of %s</H2>\n\n<PRE>\n",
req->request_uri);
while ((dirbuf = readdir(request_dir))) {
if (!strcmp(dirbuf->d_name, "."))
continue;
if (!strcmp(dirbuf->d_name, "..")) {
bytes += fprintf(fdstream,
" [DIR] <A HREF=\"../\">Parent Directory</A>\n");
continue;
}
if ((escname = escape_string(dirbuf->d_name, NULL)) != NULL) {
bytes += fprintf(fdstream, " <A HREF=\"%s\">%s</A>\n",
escname, dirbuf->d_name);
free(escname);
escname = NULL;
}
}
closedir(request_dir);
bytes += fprintf(fdstream, "</PRE>\n\n</BODY>\n</HTML>\n");
fclose(fdstream);
chdir(server_root);
req->filesize = bytes; /* for logging transfer size */
return 0; /* success */
}

Binary file not shown.

View File

@ -0,0 +1,193 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@jlab.org>
* Some changes Copyright (C) 1997 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: globals.h,v 1.65.2.3 2002/07/24 03:03:59 jnelson Exp $*/
#ifndef _GLOBALS_H
#define _GLOBALS_H
struct mmap_entry {
dev_t dev;
ino_t ino;
char *mmap;
int use_count;
size_t len;
};
struct alias {
char *fakename; /* URI path to file */
char *realname; /* Actual path to file */
int type; /* ALIAS, SCRIPTALIAS, REDIRECT */
int fake_len; /* strlen of fakename */
int real_len; /* strlen of realname */
struct alias *next;
};
typedef struct alias alias;
struct request { /* pending requests */
int fd; /* client's socket fd */
int status; /* see #defines.h */
time_t time_last; /* time of last succ. op. */
char *pathname; /* pathname of requested file */
int simple; /* simple request? */
int keepalive; /* keepalive status */
int kacount; /* keepalive count */
int data_fd; /* fd of data */
unsigned long filesize; /* filesize */
unsigned long filepos; /* position in file */
char *data_mem; /* mmapped/malloced char array */
int method; /* M_GET, M_POST, etc. */
char *logline; /* line to log file */
char *header_line; /* beginning of un or incompletely processed header line */
char *header_end; /* last known end of header, or end of processed data */
int parse_pos; /* how much have we parsed */
int client_stream_pos; /* how much have we read... */
int buffer_start; /* where the buffer starts */
int buffer_end; /* where the buffer ends */
char *http_version; /* HTTP/?.? of req */
int response_status; /* R_NOT_FOUND etc. */
char *if_modified_since; /* If-Modified-Since */
time_t last_modified; /* Last-modified: */
char local_ip_addr[NI_MAXHOST]; /* for virtualhost */
/* CGI vars */
int remote_port; /* could be used for ident */
char remote_ip_addr[NI_MAXHOST]; /* after inet_ntoa */
int is_cgi; /* true if CGI/NPH */
int cgi_status;
int cgi_env_index; /* index into array */
/* Agent and referer for logfiles */
char *header_user_agent;
char *header_referer;
int post_data_fd; /* fd for post data tmpfile */
char *path_info; /* env variable */
char *path_translated; /* env variable */
char *script_name; /* env variable */
char *query_string; /* env variable */
char *content_type; /* env variable */
char *content_length; /* env variable */
struct mmap_entry *mmap_entry_var;
struct request *next; /* next */
struct request *prev; /* previous */
/* everything below this line is kept regardless */
char buffer[BUFFER_SIZE + 1]; /* generic I/O buffer */
char request_uri[MAX_HEADER_LENGTH + 1]; /* uri */
char client_stream[CLIENT_STREAM_SIZE]; /* data from client - fit or be hosed */
char *cgi_env[CGI_ENV_MAX + 4]; /* CGI environment */
#ifdef ACCEPT_ON
char accept[MAX_ACCEPT_LENGTH]; /* Accept: fields */
#endif
};
typedef struct request request;
struct status {
long requests;
long errors;
};
extern struct status status;
extern char *optarg; /* For getopt */
extern FILE *yyin; /* yacc input */
extern request *request_ready; /* first in ready list */
extern request *request_block; /* first in blocked list */
extern request *request_free; /* first in free list */
extern fd_set block_read_fdset; /* fds blocked on read */
extern fd_set block_write_fdset; /* fds blocked on write */
/* global server variables */
extern char *access_log_name;
extern char *error_log_name;
extern char *cgi_log_name;
extern int cgi_log_fd;
extern int use_localtime;
extern int server_port;
extern uid_t server_uid;
extern gid_t server_gid;
extern char *server_admin;
extern char *server_root;
extern char *server_name;
extern char *server_ip;
extern int max_fd;
extern int devnullfd;
extern char *document_root;
extern char *user_dir;
extern char *directory_index;
extern char *default_type;
extern char *dirmaker;
extern char *mime_types;
extern char *cachedir;
extern char *tempdir;
extern char *cgi_path;
extern int single_post_limit;
extern int ka_timeout;
extern int ka_max;
extern int sighup_flag;
extern int sigchld_flag;
extern int sigalrm_flag;
extern int sigterm_flag;
extern time_t start_time;
extern int pending_requests;
extern long int max_connections;
extern int verbose_cgi_logs;
extern int backlog;
extern time_t current_time;
extern int virtualhost;
extern int total_connections;
extern sigjmp_buf env;
extern int handle_sigbus;
#endif

View File

@ -0,0 +1,411 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1997 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: hash.c,v 1.14.4.4 2002/07/30 03:59:26 jnelson Exp $*/
#include "boa.h"
#include "parse.h"
/*
* There are two hash tables used, each with a key/value pair
* stored in a hash_struct. They are:
*
* mime_hashtable:
* key = file extension
* value = mime type
*
* passwd_hashtable:
* key = username
* value = home directory
*
*/
struct _hash_struct_ {
char *key;
char *value;
struct _hash_struct_ *next;
};
typedef struct _hash_struct_ hash_struct;
static hash_struct *mime_hashtable[MIME_HASHTABLE_SIZE];
static hash_struct *passwd_hashtable[PASSWD_HASHTABLE_SIZE];
#ifdef WANT_ICKY_HASH
static unsigned four_char_hash(char *buf);
#define boa_hash four_char_hash
#else
#ifdef WANT_SDBM_HASH
static unsigned sdbm_hash(char *str);
#define boa_hash sdbm_hash
#else
static unsigned djb2_hash(char *str);
#define boa_hash djb2_hash
#endif
#endif
#ifdef WANT_ICKY_HASH
static unsigned four_char_hash(char *buf)
{
unsigned int hash = (buf[0] +
(buf[1] ? buf[1] : 241 +
(buf[2] ? buf[2] : 251 +
(buf[3] ? buf[3] : 257))));
#ifdef DEBUG_HASH
log_error_time();
fprintf(stderr, "four_char_hash(%s) = %u\n", buf, hash);
#endif
return hash;
}
/* The next two hashes taken from
* http://www.cs.yorku.ca/~oz/hash.html
*
* In my (admittedly) very brief testing, djb2_hash performed
* very slightly better than sdbm_hash.
*/
#else
#define MAX_HASH_LENGTH 4
#ifdef WANT_SDBM_HASH
static unsigned sdbm_hash(char *str)
{
unsigned hash = 0;
int c;
short count = MAX_HASH_LENGTH;
#ifdef DEBUG_HASH
log_error_time();
fprintf(stderr, "sdbm_hash(%s) = ", str);
#endif
while ((c = *str++) && count--)
hash = c + (hash << 6) + (hash << 16) - hash;
#ifdef DEBUG_HASH
fprintf(stderr, "%u\n", hash);
#endif
return hash;
}
#else
static unsigned djb2_hash(char *str)
{
unsigned hash = 5381;
int c;
short count = MAX_HASH_LENGTH;
#ifdef DEBUG_HASH
log_error_time();
fprintf(stderr, "djb2_hash(%s) = ", str);
#endif
while ((c = *(str++)) && count--)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
#ifdef DEBUG_HASH
fprintf(stderr, "%u\n", hash);
#endif
return hash;
}
#endif
#endif
/*
* Name: add_mime_type
* Description: Adds a key/value pair to the mime_hashtable
*/
void add_mime_type(char *extension, char *type)
{
unsigned int hash;
hash_struct *current, *next;
if (!extension)
return;
hash = get_mime_hash_value(extension);
current = mime_hashtable[hash];
while (current) {
if (!strcmp(current->key, extension))
return; /* don't add extension twice */
if (current->next)
current = current->next;
else
break;
}
/* if here, we need to add a new one */
next = (hash_struct *) malloc(sizeof (hash_struct));
if (!next) {
DIE("malloc of hash_struct failed!");
}
next->key = strdup(extension);
if (!next->key)
DIE("malloc of hash_struct->key failed!");
next->value = strdup(type);
if (!next->value)
DIE("malloc of hash_struct->value failed!");
next->next = NULL;
if (!current) {
mime_hashtable[hash] = next;
} else {
current->next = next;
}
}
/*
* Name: get_mime_hash_value
*
* Description: adds the ASCII values of the file extension letters
* and mods by the hashtable size to get the hash value
*/
unsigned get_mime_hash_value(char *extension)
{
unsigned int hash = 0;
if (extension == NULL || extension[0] == '\0') {
/* FIXME */
log_error_time();
fprintf(stderr, "Attempt to hash NULL or empty string!\n");
return 0;
}
hash = boa_hash(extension);
hash %= MIME_HASHTABLE_SIZE;
return hash;
}
/*
* Name: get_mime_type
*
* Description: Returns the mime type for a supplied filename.
* Returns default type if not found.
*/
char *get_mime_type(char *filename)
{
char *extension;
hash_struct *current;
unsigned int hash;
extension = strrchr(filename, '.');
if (!extension || *extension++ == '\0')
return default_type;
hash = get_mime_hash_value(extension);
current = mime_hashtable[hash];
while (current) {
if (!strcmp(current->key, extension)) /* hit */
return current->value;
current = current->next;
}
return default_type;
}
/*
* Name: get_homedir_hash_value
*
* Description: adds the ASCII values of the username letters
* and mods by the hashtable size to get the hash value
*/
unsigned get_homedir_hash_value(char *name)
{
unsigned int hash = 0;
if (name == NULL || name[0] == '\0') {
/* FIXME */
log_error_time();
fprintf(stderr, "Attempt to hash NULL or empty string!\n");
return 0;
}
hash = boa_hash(name);
hash %= PASSWD_HASHTABLE_SIZE;
return hash;
}
/*
* Name: get_home_dir
*
* Description: Returns a point to the supplied user's home directory.
* Adds to the hashtable if it's not already present.
*
*/
char *get_home_dir(char *name)
{
struct passwd *passwdbuf;
hash_struct *current, *next;
unsigned int hash;
/* first check hash table -- if username is less than four characters,
just hash to zero (this should be very rare) */
hash = get_homedir_hash_value(name);
for(current = passwd_hashtable[hash];current;current = current->next) {
if (!strcmp(current->key, name)) /* hit */
return current->value;
if (!current->next)
break;
}
/* if here, we have to add a new one */
passwdbuf = getpwnam(name);
if (!passwdbuf) /* does not exist */
return NULL;
next = (hash_struct *) malloc(sizeof (hash_struct));
if (!next) {
WARN("malloc of hash_struct for passwd_hashtable failed!");
return NULL;
}
next->key = strdup(name);
if (!next->key) {
WARN("malloc of passwd_hashtable[hash]->key failed!");
free(next);
return NULL;
}
next->value = strdup(passwdbuf->pw_dir);
if (!next->value) {
WARN("malloc of passwd_hashtable[hash]->value failed!");
free(next->key);
free(next);
return NULL;
}
next->next = NULL;
if (!current) {
passwd_hashtable[hash] = next;
} else {
current->next = next;
}
return next->value;
}
void dump_mime(void)
{
int i;
hash_struct *temp;
for (i = 0; i < MIME_HASHTABLE_SIZE; ++i) { /* these limits OK? */
temp = mime_hashtable[i];
while (temp) {
hash_struct *temp_next;
temp_next = temp->next;
free(temp->key);
free(temp->value);
free(temp);
temp = temp_next;
}
mime_hashtable[i] = NULL;
}
}
void dump_passwd(void)
{
int i;
hash_struct *temp;
for (i = 0; i < PASSWD_HASHTABLE_SIZE; ++i) { /* these limits OK? */
temp = passwd_hashtable[i];
while (temp) {
hash_struct *temp_next;
temp_next = temp->next;
free(temp->key);
free(temp->value);
free(temp);
temp = temp_next;
}
passwd_hashtable[i] = NULL;
}
}
void show_hash_stats(void)
{
int i;
hash_struct *temp;
int total = 0;
int count;
for (i = 0; i < MIME_HASHTABLE_SIZE; ++i) { /* these limits OK? */
if (mime_hashtable[i]) {
count = 0;
temp = mime_hashtable[i];
while (temp) {
temp = temp->next;
++count;
}
#ifdef NOISY_SIGALRM
log_error_time();
fprintf(stderr, "mime_hashtable[%d] has %d entries\n",
i, count);
#endif
total += count;
}
}
log_error_time();
fprintf(stderr, "mime_hashtable has %d total entries\n",
total);
total = 0;
for (i = 0; i < PASSWD_HASHTABLE_SIZE; ++i) { /* these limits OK? */
if (passwd_hashtable[i]) {
temp = passwd_hashtable[i];
count = 0;
while (temp) {
temp = temp->next;
++count;
}
#ifdef NOISY_SIGALRM
log_error_time();
fprintf(stderr, "passwd_hashtable[%d] has %d entries\n",
i, count);
#endif
total += count;
}
}
log_error_time();
fprintf(stderr, "passwd_hashtable has %d total entries\n",
total);
}

Binary file not shown.

View File

@ -0,0 +1,355 @@
/*
* Copyright (C) 1997-2002 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: index_dir.c,v 1.32 2002/01/30 03:41:45 jnelson Exp $*/
#include <stdio.h>
#include <sys/stat.h>
#include <limits.h> /* for PATH_MAX */
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "compat.h"
#define MAX_FILE_LENGTH MAXNAMLEN
#define MAX_PATH_LENGTH PATH_MAX
#define INT_TO_HEX(x) \
((((x)-10)>=0)?('A'+((x)-10)):('0'+(x)))
#include "escape.h"
char *html_escape_string(char *inp, char *buf, const int len);
char *http_escape_string(char *inp, char *buf, const int len);
#if defined __GNUC__ && \
(__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 0))
#define CONST const
#else
#define CONST
#endif
int select_files(CONST struct dirent *d);
int index_directory(char *dir, char *title);
/*
* Name: html_escape_string
*/
char *html_escape_string(char *inp, char *dest, const int len)
{
int max;
char *buf;
unsigned char c;
max = len * 5;
if (dest == NULL && max)
dest = malloc(sizeof (unsigned char) * (max + 1));
if (dest == NULL)
return NULL;
buf = dest;
while ((c = *inp++)) {
switch (c) {
case '>':
*dest++ = '&';
*dest++ = 'g';
*dest++ = 't';
*dest++ = ';';
break;
case '<':
*dest++ = '&';
*dest++ = 'l';
*dest++ = 't';
*dest++ = ';';
break;
case '&':
*dest++ = '&';
*dest++ = 'a';
*dest++ = 'm';
*dest++ = 'p';
*dest++ = ';';
break;
default:
*dest++ = c;
}
}
*dest = '\0';
return buf;
}
/*
* Name: escape_string
*
* Description: escapes the string inp. Uses variable buf. If buf is
* NULL when the program starts, it will attempt to dynamically allocate
* the space that it needs, otherwise it will assume that the user
* has already allocated enough space for the variable buf, which
* could be up to 3 times the size of inp. If the routine dynamically
* allocates the space, the user is responsible for freeing it afterwords
* Returns: NULL on error, pointer to string otherwise.
*/
char *http_escape_string(char *inp, char *buf, const int len)
{
int max;
char *index;
unsigned char c;
max = len * 3;
if (buf == NULL && max)
buf = malloc(sizeof (unsigned char) * (max + 1));
if (buf == NULL)
return NULL;
index = buf;
while ((c = *inp++)) {
if (needs_escape((unsigned int) c)) {
*index++ = '%';
*index++ = INT_TO_HEX(c >> 4);
*index++ = INT_TO_HEX(c & 15);
} else
*index++ = c;
}
*index = '\0';
return buf;
}
void send_error(int error)
{
char *the_error;
switch (error) {
case 1:
the_error = "Not enough arguments were passed to the indexer.";
break;
case 2:
the_error = "The Directory Sorter ran out of Memory";
break;
case 3:
the_error =
"The was a problem changing to the appropriate directory.";
break;
case 4:
the_error = "There was an error escaping a string.";
case 5:
the_error = "Too many arguments were passed to the indexer.";
break;
case 6:
the_error = "No files in this directory.";
break;
default:
the_error = "An unknown error occurred producing the directory.";
break;
}
printf("<html>\n<head>\n<title>\n%s\n</title>\n"
"<body>\n%s\n</body>\n</html>\n", the_error, the_error);
}
int select_files(CONST struct dirent *dirbuf)
{
if (dirbuf->d_name[0] == '.')
return 0;
else
return 1;
}
/*
* Name: index_directory
* Description: Called from get_dir_mapping if a directory html
* has to be generated on the fly
* If no_slash is true, prepend slashes to hrefs
* returns -1 for problem, else 0
*/
int index_directory(char *dir, char *title)
{
struct dirent *dirbuf;
int numdir;
struct dirent **array;
struct stat statbuf;
char http_filename[MAX_FILE_LENGTH * 3];
char html_filename[MAX_FILE_LENGTH * 4];
int i;
if (chdir(dir) == -1) {
send_error(3);
return -1;
}
numdir = scandir(".", &array, select_files, alphasort);
if (numdir == -1) {
send_error(2);
return -1;
} else if (numdir == -2) {
send_error(6);
return -1;
}
printf("<html>\n"
"<head>\n<title>Index of %s</title>\n</head>\n\n"
"<body bgcolor=\"#ffffff\">\n"
"<H2>Index of %s</H2>\n"
"<table>\n%s",
title, title,
(strcmp(title, "/") == 0 ? "" :
"<tr><td colspan=3><h3>Directories</h3></td></tr>"
"<tr><td colspan=3><a href=\"../\">Parent Directory</a></td></tr>\n"));
for (i = 0; i < numdir; ++i) {
dirbuf = array[i];
if (stat(dirbuf->d_name, &statbuf) == -1)
continue;
if (!S_ISDIR(statbuf.st_mode))
continue;
if (html_escape_string(dirbuf->d_name, html_filename,
NAMLEN(dirbuf)) == NULL) {
send_error(4);
return -1;
}
if (http_escape_string(dirbuf->d_name, http_filename,
NAMLEN(dirbuf)) == NULL) {
send_error(4);
return -1;
}
printf("<tr>"
"<td width=\"40%%\"><a href=\"%s/\">%s/</a></td>"
"<td align=right>%s</td>"
"<td align=right>%ld bytes</td>"
"</tr>\n",
http_filename, html_filename,
ctime(&statbuf.st_mtime), (long) statbuf.st_size);
}
printf
("<tr><td colspan=3>&nbsp;</td></tr>\n<tr><td colspan=3><h3>Files</h3></td></tr>\n");
for (i = 0; i < numdir; ++i) {
int len;
dirbuf = array[i];
if (stat(dirbuf->d_name, &statbuf) == -1)
continue;
if (S_ISDIR(statbuf.st_mode))
continue;
if (html_escape_string(dirbuf->d_name, html_filename,
NAMLEN(dirbuf)) == NULL) {
send_error(4);
return -1;
}
if (http_escape_string(dirbuf->d_name, http_filename,
NAMLEN(dirbuf)) == NULL) {
send_error(4);
return -1;
}
len = strlen(http_filename);
#ifdef GUNZIP
if (len > 3 && !memcmp(http_filename + len - 3, ".gz", 3)) {
http_filename[len - 3] = '\0';
html_filename[strlen(html_filename) - 3] = '\0';
printf("<tr>"
"<td width=\"40%%\"><a href=\"%s\">%s</a> "
"<a href=\"%s.gz\">(.gz)</a></td>"
"<td align=right>%s</td>"
"<td align=right>%ld bytes</td>"
"</tr>\n",
http_filename, html_filename, http_filename,
ctime(&statbuf.st_mtime), (long) statbuf.st_size);
} else {
#endif
printf("<tr>"
"<td width=\"40%%\"><a href=\"%s\">%s</a></td>"
"<td align=right>%s</td>"
"<td align=right>%ld bytes</td>"
"</tr>\n",
http_filename, html_filename,
ctime(&statbuf.st_mtime), (long) statbuf.st_size);
#ifdef GUNZIP
}
#endif
}
/* hey -- even though this is a one-shot deal, we should
* still free memory we ought to free
* You never know -- this code might get used elsewhere!
*/
for (i = 0; i < numdir; ++i) {
free(array[i]);
array[i] = NULL;
}
free(array);
array = NULL;
return 0; /* success */
}
int main(int argc, char *argv[])
{
time_t timep;
struct tm *timeptr;
char *now;
if (argc < 3) {
send_error(1);
return -1;
} else if (argc > 3) {
send_error(5);
return -1;
}
build_needs_escape();
if (argv[2] == NULL)
index_directory(argv[1], argv[1]);
else
index_directory(argv[1], argv[2]);
time(&timep);
#ifdef USE_LOCALTIME
timeptr = localtime(&timep);
#else
timeptr = gmtime(&timep);
#endif
now = strdup(asctime(timeptr));
now[strlen(now) - 1] = '\0';
#ifdef USE_LOCALTIME
printf("</table>\n<hr noshade>\nIndex generated %s %s\n"
"<!-- This program is part of the Boa Webserver Copyright (C) 1991-2002 http://www.boa.org -->\n"
"</body>\n</html>\n", now, TIMEZONE(timeptr));
#else
printf("</table>\n<hr noshade>\nIndex generated %s UTC\n"
"<!-- This program is part of the Boa Webserver Copyright (C) 1991-2002 http://www.boa.org -->\n"
"</body>\n</html>\n", now);
#endif
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,114 @@
/*
* Boa, an http server
* Copyright (C) 1999 Larry Doolittle <ldoolitt@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
Encapsulation of ipv4 and ipv6 stuff, try to get rid of the ifdef's
elsewhere in the code.
The IPv6 code here is bsed on contributions from Martin Hinner <martin@tdp.cz>
and Arkadiusz Miskiewicz <misiek@misiek.eu.org>. This incarnation of that
code is untested. The original IPv4 code is based on original Boa code
from Paul Phillips <paulp@go2net.com>.
A goal is to compile in as many families as are supported, and
make the final choice at runtime.
globals.h:
#ifdef INET6
char remote_ip_addr[NI_MAXHOST];
#else
char remote_ip_addr[20]; after inet_ntoa
#endif
None of this code interacts with the rest of Boa except through
the parameter lists and return values.
Consider making these functions __inline__ and using this as a .h file
*/
#include "boa.h"
#include <arpa/inet.h> /* inet_ntoa */
/* Binds to the existing server_s, based on the configuration string
in server_ip. IPv6 version doesn't pay attention to server_ip yet. */
int bind_server(int server_s, char *server_ip)
{
#ifdef INET6
struct sockaddr_in6 server_sockaddr;
server_sockaddr.sin6_family = AF_INET6;
memcpy(&server_sockaddr.sin6_addr, &in6addr_any, sizeof (in6addr_any));
server_sockaddr.sin6_port = htons(server_port);
#else
struct sockaddr_in server_sockaddr;
memset(&server_sockaddr, 0, sizeof server_sockaddr);
#ifdef HAVE_SIN_LEN /* uncomment for BSDs */
server_sockaddr.sin_len = sizeof server_sockaddr;
#endif
server_sockaddr.sin_family = AF_INET;
if (server_ip != NULL) {
inet_aton(server_ip, &server_sockaddr.sin_addr);
} else {
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
}
server_sockaddr.sin_port = htons(server_port);
#endif
return bind(server_s, (struct sockaddr *) &server_sockaddr,
sizeof (server_sockaddr));
}
char *ascii_sockaddr(struct SOCKADDR *s, char *dest, int len)
{
#ifdef INET6
if (getnameinfo((struct sockaddr *) s,
sizeof(struct SOCKADDR),
dest, len, NULL, 0, NI_NUMERICHOST)) {
fprintf(stderr, "[IPv6] getnameinfo failed\n");
*dest = '\0';
}
#ifdef WHEN_DOES_THIS_APPLY
if ((s->__ss_family == AF_INET6) &&
IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6 *) s)->sin6_addr))) {
memmove(dest, dest+7, NI_MAXHOST);
}
#endif
#else
memmove(dest, inet_ntoa(s->sin_addr), len);
#endif
return dest;
}
int net_port(struct SOCKADDR *s)
{
int p = -1;
#ifdef INET6
char serv[NI_MAXSERV];
if (getnameinfo((struct sockaddr *) s,
sizeof(struct SOCKADDR),
NULL, 0, serv, sizeof(serv), NI_NUMERICSERV)) {
fprintf(stderr, "[IPv6] getnameinfo failed\n");
} else {
p = atoi(serv);
}
#else
p = ntohs(s->sin_port);
#endif
return p;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,229 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1999 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: log.c,v 1.36.2.3 2002/07/26 03:04:48 jnelson Exp $*/
#include "boa.h"
FILE *access_log;
char *error_log_name;
char *access_log_name;
char *cgi_log_name;
int cgi_log_fd;
FILE *fopen_gen_fd(char *spec, const char *mode);
FILE *fopen_gen_fd(char *spec, const char *mode)
{
int fd;
if (!spec || *spec == '\0')
return NULL;
fd = open_gen_fd(spec);
if (fd == -1)
return NULL;
return fdopen(fd, mode);
}
/*
* Name: open_logs
*
* Description: Opens access log, error log, and if specified, cgi log
* Ties stderr to error log, except during cgi execution, at which
* time cgi log is the stderr for cgis.
*
* Access log is line buffered, error log is not buffered.
*
*/
void open_logs(void)
{
int error_log;
/* if error_log_name is set, dup2 stderr to it */
/* otherwise, leave stderr alone */
/* we don't want to tie stderr to /dev/null */
if (error_log_name) {
/* open the log file */
if (!(error_log = open_gen_fd(error_log_name))) {
DIE("unable to open error log");
}
/* redirect stderr to error_log */
/*if (dup2(error_log, STDERR_FILENO) == -1) {
DIE("unable to dup2 the error log");
}*/
close(error_log);
}
/* set the close-on-exec to true */
if (fcntl(STDERR_FILENO, F_SETFD, 1) == -1) {
DIE("unable to fcntl the error log");
}
if (access_log_name) {
/* Used the "a" flag with fopen, but fopen_gen_fd builds that in
* implicitly when used as a file, and "a" is incompatible with
* pipes and network sockets. */
if (!(access_log = fopen_gen_fd(access_log_name, "w"))) {
int errno_save = errno;
fprintf(stderr, "Cannot open %s for logging: ",
access_log_name);
errno = errno_save;
perror("logfile open");
exit(errno);
}
/* line buffer the access log */
#ifdef SETVBUF_REVERSED
setvbuf(access_log, _IOLBF, (char *) NULL, 0);
#else
setvbuf(access_log, (char *) NULL, _IOLBF, 0);
#endif
} else
access_log = NULL;
if (cgi_log_name) {
cgi_log_fd = open_gen_fd(cgi_log_name);
if (cgi_log_fd == -1) {
WARN("open cgi_log");
free(cgi_log_name);
cgi_log_name = NULL;
cgi_log_fd = 0;
} else {
if (fcntl(cgi_log_fd, F_SETFD, 1) == -1) {
WARN("unable to set close-on-exec flag for cgi_log");
close(cgi_log_fd);
cgi_log_fd = 0;
free(cgi_log_name);
cgi_log_name = NULL;
}
}
}
}
/*
* Name: close_access_log
*
* Description: closes access_log file
*/
void close_access_log(void)
{
if (access_log)
fclose(access_log);
}
/*
* Name: log_access
*
* Description: Writes log data to access_log.
*/
void log_access(request * req)
{
if (access_log) {
if (virtualhost)
fprintf(access_log, "%s ", req->local_ip_addr);
fprintf(access_log, "%s - - %s\"%s\" %d %ld \"%s\" \"%s\"\n",
req->remote_ip_addr,
get_commonlog_time(),
req->logline,
req->response_status,
req->filepos,
(req->header_referer ? req->header_referer : "-"),
(req->header_user_agent ? req->header_user_agent : "-"));
}
}
/*
* Name: log_error_doc
*
* Description: Logs the current time and transaction identification
* to the stderr (the error log):
* should always be followed by an fprintf to stderr
*
* This function used to be implemented with a big fprintf, but not
* all fprintf's are reliable in the face of null string pointers
* (SunOS, in particular). As long as I had to add the checks for
* null pointers, I changed from fprintf to fputs.
*
* Example output:
[08/Nov/1997:01:05:03 -0600] request from 192.228.331.232 "GET /~joeblow/dir/ HTTP/1.0" ("/usr/user1/joeblow/public_html/dir/"): write: Broken pipe
*/
void log_error_doc(request * req)
{
int errno_save = errno;
fprintf(stderr, "%srequest from %s \"%s\" (\"%s\"): ",
get_commonlog_time(),
req->remote_ip_addr,
(req->logline != NULL ?
req->logline : "(null)"),
(req->pathname != NULL ? req->pathname : "(null)"));
errno = errno_save;
}
/*
* Name: boa_perror
*
* Description: logs an error to user and error file both
*
*/
void boa_perror(request * req, char *message)
{
log_error_doc(req);
perror(message); /* don't need to save errno because log_error_doc does */
send_r_error(req);
}
/*
* Name: log_error_time
*
* Description: Logs the current time to the stderr (the error log):
* should always be followed by an fprintf to stderr
*/
void log_error_time()
{
int errno_save = errno;
fputs(get_commonlog_time(), stderr);
errno = errno_save;
}
/*
* Name: log_error_mesg
*
* Description: performs a log_error_time, writes the file and lineno
* to stderr (saving errno), and then a perror with message
*
*/
void log_error_mesg(char *file, int line, char *mesg)
{
int errno_save = errno;
fprintf(stderr, "%s%s:%d - ", get_commonlog_time(), file, line);
errno = errno_save;
perror(mesg);
errno = errno_save;
}

Binary file not shown.

View File

@ -0,0 +1,152 @@
/*
* Boa, an http server
* Copyright (C) 1999 Larry Doolittle <ldoolitt@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: mmap_cache.c,v 1.9 2002/03/24 22:35:34 jnelson Exp $*/
#include "boa.h"
int mmap_list_entries_used = 0;
int mmap_list_total_requests = 0;
int mmap_list_hash_bounces = 0;
/* define local table variable */
static struct mmap_entry mmap_list[MMAP_LIST_SIZE];
struct mmap_entry *find_mmap(int data_fd, struct stat *s)
{
char *m;
int i, start;
mmap_list_total_requests++;
i = start = MMAP_LIST_HASH(s->st_dev, s->st_ino, s->st_size);
for (; mmap_list[i].use_count;) {
if (mmap_list[i].dev == s->st_dev &&
mmap_list[i].ino == s->st_ino &&
mmap_list[i].len == s->st_size) {
mmap_list[i].use_count++;
#ifdef DEBUG
fprintf(stderr,
"Old mmap_list entry %d use_count now %d (hash was %d)\n",
i, mmap_list[i].use_count, start);
#endif
return mmap_list + i;
}
mmap_list_hash_bounces++;
i = MMAP_LIST_NEXT(i);
/* Shouldn't happen, because of size limit enforcement below */
if (i == start)
return NULL;
}
/* didn't find an entry that matches our dev/inode/size.
There might be an entry that matches later in the table,
but that _should_ be rare. The worst case is that we
needlessly mmap() a file that is already mmap'd, but we
did that all the time before this code was written,
so it shouldn't be _too_ bad.
*/
/* Enforce a size limit here */
if (mmap_list_entries_used > MMAP_LIST_USE_MAX)
return NULL;
m = mmap(0, s->st_size, PROT_READ, MAP_OPTIONS, data_fd, 0);
if ((int) m == -1) {
/* boa_perror(req,"mmap"); */
return NULL;
}
#ifdef DEBUG
fprintf(stderr, "New mmap_list entry %d (hash was %d)\n", i, h);
#endif
mmap_list_entries_used++;
mmap_list[i].dev = s->st_dev;
mmap_list[i].ino = s->st_ino;
mmap_list[i].len = s->st_size;
mmap_list[i].mmap = m;
mmap_list[i].use_count = 1;
return mmap_list + i;
}
void release_mmap(struct mmap_entry *e)
{
if (!e)
return;
if (!e->use_count) {
#ifdef DEBUG
fprintf(stderr, "mmap_list(%p)->use_count already zero!\n", e);
#endif
return;
}
if (!--(e->use_count)) {
munmap(e->mmap, e->len);
mmap_list_entries_used--;
}
}
struct mmap_entry *find_named_mmap(char *fname)
{
int data_fd;
struct stat statbuf;
struct mmap_entry *e;
data_fd = open(fname, O_RDONLY);
if (data_fd == -1) {
perror(fname);
return NULL;
}
fstat(data_fd, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
#ifdef DEBUG
fprintf(stderr, "%s is a directory\n", fname);
#endif
return NULL;
}
e = find_mmap(data_fd, &statbuf);
close(data_fd);
return e;
}
/*
int main(int argc, char *argv[])
{
#define MAXTEST 2048
struct mmap_entry *mlist[MAXTEST];
char name[1024], *s;
int i, tests=0;
while (fgets(name,sizeof(name),stdin) && tests < MAXTEST) {
if (name[0]=='-') {
i=atoi(name+1);
release_mmap(mlist[i]);
mlist[i]=NULL;
} else {
if ((s=strchr(name,'\n'))) *s='\0';
mlist[tests] = find_named_mmap(name);
if (mlist[tests]) tests++;
else fprintf(stderr, "find_named_mmap(%s) failed\n",name);
}
}
fprintf(stderr, "mmap_list entries_used=%d ",mmap_list_entries_used);
fprintf(stderr, "total_requests=%d ",mmap_list_total_requests);
fprintf(stderr, "hash_bounces=%d\n",mmap_list_hash_bounces);
for (i=0; i<tests; i++) release_mmap(mlist[i]);
fprintf(stderr, "mmap_list entries_used=%d ",mmap_list_entries_used);
fprintf(stderr, "total_requests=%d ",mmap_list_total_requests);
fprintf(stderr, "hash_bounces=%d\n",mmap_list_hash_bounces);
*/

Binary file not shown.

View File

@ -0,0 +1,33 @@
/*
* Boa, an http server
* Copyright (C) 1999 Larry Doolittle <ldoolitt@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* parse.h
* minimum interaction point between Boa's parser (boa_lexer.l and
* boa_grammar.y) and the rest of Boa.
*/
/* $Id: parse.h,v 1.5 2000/02/12 21:52:45 jon Exp $*/
struct ccommand {
char *name;
int type;
void (*action) (char *, char *, void *);
void *object;
};
struct ccommand *lookup_keyword(char *c);
void add_mime_type(char *extension, char *type);

View File

@ -0,0 +1,136 @@
/*
* Boa, an http server
* Based on code Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1997-99 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: pipe.c,v 1.39 2002/01/21 02:19:16 jnelson Exp $*/
#include "boa.h"
/*
* Name: read_from_pipe
* Description: Reads data from a pipe
*
* Return values:
* -1: request blocked, move to blocked queue
* 0: EOF or error, close it down
* 1: successful read, recycle in ready queue
*/
int read_from_pipe(request * req)
{
int bytes_read, bytes_to_read =
BUFFER_SIZE - (req->header_end - req->buffer);
if (bytes_to_read == 0) { /* buffer full */
if (req->cgi_status == CGI_PARSE) { /* got+parsed header */
req->cgi_status = CGI_BUFFER;
*req->header_end = '\0'; /* points to end of read data */
/* Could the above statement overwrite data???
No, because req->header_end points to where new data
should begin, not where old data is.
*/
return process_cgi_header(req); /* cgi_status will change */
}
req->status = PIPE_WRITE;
return 1;
}
bytes_read = read(req->data_fd, req->header_end, bytes_to_read);
#ifdef FASCIST_LOGGING
if (bytes_read > 0) {
*(req->header_end + bytes_read) = '\0';
fprintf(stderr, "pipe.c - read %d bytes: \"%s\"\n",
bytes_read, req->header_end);
} else
fprintf(stderr, "pipe.c - read %d bytes\n", bytes_read);
fprintf(stderr, "status, cgi_status: %d, %d\n", req->status,
req->cgi_status);
#endif
if (bytes_read == -1) {
if (errno == EINTR)
return 1;
else if (errno == EWOULDBLOCK || errno == EAGAIN)
return -1; /* request blocked at the pipe level, but keep going */
else {
req->status = DEAD;
log_error_doc(req);
perror("pipe read");
return 0;
}
} else if (bytes_read == 0) { /* eof, write rest of buffer */
req->status = PIPE_WRITE;
if (req->cgi_status == CGI_PARSE) { /* hasn't processed header yet */
req->cgi_status = CGI_DONE;
*req->header_end = '\0'; /* points to end of read data */
return process_cgi_header(req); /* cgi_status will change */
}
req->cgi_status = CGI_DONE;
return 1;
}
req->header_end += bytes_read;
return 1;
}
/*
* Name: write_from_pipe
* Description: Writes data previously read from a pipe
*
* Return values:
* -1: request blocked, move to blocked queue
* 0: EOF or error, close it down
* 1: successful write, recycle in ready queue
*/
int write_from_pipe(request * req)
{
int bytes_written, bytes_to_write = req->header_end - req->header_line;
if (bytes_to_write == 0) {
if (req->cgi_status == CGI_DONE)
return 0;
req->status = PIPE_READ;
req->header_end = req->header_line = req->buffer;
return 1;
}
bytes_written = write(req->fd, req->header_line, bytes_to_write);
if (bytes_written == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN)
return -1; /* request blocked at the pipe level, but keep going */
else if (errno == EINTR)
return 1;
else {
req->status = DEAD;
send_r_error(req); /* maybe superfluous */
log_error_doc(req);
perror("pipe write");
return 0;
}
}
req->header_line += bytes_written;
req->filepos += bytes_written;
return 1;
}

Binary file not shown.

View File

@ -0,0 +1,131 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1997 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: queue.c,v 1.21 2002/01/21 02:19:16 jnelson Exp $*/
#include "boa.h"
request *request_ready = NULL; /* ready list head */
request *request_block = NULL; /* blocked list head */
request *request_free = NULL; /* free list head */
/*
* Name: block_request
*
* Description: Moves a request from the ready queue to the blocked queue
*/
void block_request(request * req)
{
dequeue(&request_ready, req);
enqueue(&request_block, req);
if (req->buffer_end) {
BOA_FD_SET(req->fd, &block_write_fdset);
} else {
switch (req->status) {
case WRITE:
case PIPE_WRITE:
case DONE:
BOA_FD_SET(req->fd, &block_write_fdset);
break;
case PIPE_READ:
BOA_FD_SET(req->data_fd, &block_read_fdset);
break;
case BODY_WRITE:
BOA_FD_SET(req->post_data_fd, &block_write_fdset);
break;
default:
BOA_FD_SET(req->fd, &block_read_fdset);
break;
}
}
}
/*
* Name: ready_request
*
* Description: Moves a request from the blocked queue to the ready queue
*/
void ready_request(request * req)
{
dequeue(&request_block, req);
enqueue(&request_ready, req);
if (req->buffer_end) {
FD_CLR(req->fd, &block_write_fdset);
} else {
switch (req->status) {
case WRITE:
case PIPE_WRITE:
case DONE:
FD_CLR(req->fd, &block_write_fdset);
break;
case PIPE_READ:
FD_CLR(req->data_fd, &block_read_fdset);
break;
case BODY_WRITE:
FD_CLR(req->post_data_fd, &block_write_fdset);
break;
default:
FD_CLR(req->fd, &block_read_fdset);
}
}
}
/*
* Name: dequeue
*
* Description: Removes a request from its current queue
*/
void dequeue(request ** head, request * req)
{
if (*head == req)
*head = req->next;
if (req->prev)
req->prev->next = req->next;
if (req->next)
req->next->prev = req->prev;
req->next = NULL;
req->prev = NULL;
}
/*
* Name: enqueue
*
* Description: Adds a request to the head of a queue
*/
void enqueue(request ** head, request * req)
{
if (*head)
(*head)->prev = req; /* previous head's prev is us */
req->next = *head; /* our next is previous head */
req->prev = NULL; /* first in list */
*head = req; /* now we are head */
}

Binary file not shown.

View File

@ -0,0 +1,377 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1997,99 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: read.c,v 1.49 2002/03/18 01:53:48 jnelson Exp $*/
#include "boa.h"
/*
* Name: read_header
* Description: Reads data from a request socket. Manages the current
* status via a state machine. Changes status from READ_HEADER to
* READ_BODY or WRITE as necessary.
*
* Return values:
* -1: request blocked, move to blocked queue
* 0: request done, close it down
* 1: more to do, leave on ready list
*/
int read_header(request * req)
{
int bytes, buf_bytes_left;
char *check, *buffer;
check = req->client_stream + req->parse_pos;
buffer = req->client_stream;
bytes = req->client_stream_pos;
#ifdef VERY_FASCIST_LOGGING
if (check < (buffer + bytes)) {
buffer[bytes] = '\0';
log_error_time();
fprintf(stderr, "%s:%d - Parsing headers (\"%s\")\n",
__FILE__, __LINE__, check);
}
#endif
while (check < (buffer + bytes)) {
switch (req->status) {
case READ_HEADER:
if (*check == '\r') {
req->status = ONE_CR;
req->header_end = check;
} else if (*check == '\n') {
req->status = ONE_LF;
req->header_end = check;
}
break;
case ONE_CR:
if (*check == '\n')
req->status = ONE_LF;
else if (*check != '\r')
req->status = READ_HEADER;
break;
case ONE_LF:
/* if here, we've found the end (for sure) of a header */
if (*check == '\r') /* could be end o headers */
req->status = TWO_CR;
else if (*check == '\n')
req->status = BODY_READ;
else
req->status = READ_HEADER;
break;
case TWO_CR:
if (*check == '\n')
req->status = BODY_READ;
else if (*check != '\r')
req->status = READ_HEADER;
break;
default:
break;
}
#ifdef VERY_FASCIST_LOGGING
log_error_time();
fprintf(stderr, "status, check: %d, %d\n", req->status, *check);
#endif
req->parse_pos++; /* update parse position */
check++;
if (req->status == ONE_LF) {
*req->header_end = '\0';
/* terminate string that begins at req->header_line */
if (req->logline) {
if (process_option_line(req) == 0) {
return 0;
}
} else {
if (process_logline(req) == 0)
return 0;
if (req->simple)
return process_header_end(req);
}
/* set header_line to point to beginning of new header */
req->header_line = check;
} else if (req->status == BODY_READ) {
#ifdef VERY_FASCIST_LOGGING
int retval;
log_error_time();
fprintf(stderr, "%s:%d -- got to body read.\n",
__FILE__, __LINE__);
retval = process_header_end(req);
#else
int retval = process_header_end(req);
#endif
/* process_header_end inits non-POST cgi's */
if (retval && req->method == M_POST) {
/* for body_{read,write}, set header_line to start of data,
and header_end to end of data */
req->header_line = check;
req->header_end =
req->client_stream + req->client_stream_pos;
req->status = BODY_WRITE;
/* so write it */
/* have to write first, or read will be confused
* because of the special case where the
* filesize is less than we have already read.
*/
/*
As quoted from RFC1945:
A valid Content-Length is required on all HTTP/1.0 POST requests. An
HTTP/1.0 server should respond with a 400 (bad request) message if it
cannot determine the length of the request message's content.
*/
if (req->content_length) {
int content_length;
content_length = boa_atoi(req->content_length);
/* Is a content-length of 0 legal? */
if (content_length <= 0) {
log_error_time();
fprintf(stderr, "Invalid Content-Length [%s] on POST!\n",
req->content_length);
send_r_bad_request(req);
return 0;
}
if (single_post_limit && content_length > single_post_limit) {
log_error_time();
fprintf(stderr, "Content-Length [%d] > SinglePostLimit [%d] on POST!\n",
content_length, single_post_limit);
send_r_bad_request(req);
return 0;
}
req->filesize = content_length;
req->filepos = 0;
if (req->header_end - req->header_line > req->filesize) {
req->header_end = req->header_line + req->filesize;
}
} else {
log_error_time();
fprintf(stderr, "Unknown Content-Length POST!\n");
send_r_bad_request(req);
return 0;
}
} /* either process_header_end failed or req->method != POST */
return retval; /* 0 - close it done, 1 - keep on ready */
} /* req->status == BODY_READ */
} /* done processing available buffer */
#ifdef VERY_FASCIST_LOGGING
log_error_time();
fprintf(stderr, "%s:%d - Done processing buffer. Status: %d\n",
__FILE__, __LINE__, req->status);
#endif
if (req->status < BODY_READ) {
/* only reached if request is split across more than one packet */
buf_bytes_left = CLIENT_STREAM_SIZE - req->client_stream_pos;
if (buf_bytes_left < 1) {
log_error_time();
fputs("buffer overrun - read.c, read_header - closing\n",
stderr);
req->status = DEAD;
return 0;
}
bytes = read(req->fd, buffer + req->client_stream_pos, buf_bytes_left);
if (bytes < 0) {
if (errno == EINTR)
return 1;
if (errno == EAGAIN || errno == EWOULDBLOCK) /* request blocked */
return -1;
/*
else if (errno == EBADF || errno == EPIPE) {
req->status = DEAD;
return 0;
*/
log_error_doc(req);
perror("header read"); /* don't need to save errno because log_error_doc does */
return 0;
} else if (bytes == 0) {
/*
log_error_time();
fputs("unexpected end of headers\n", stderr);
*/
return 0;
}
/* bytes is positive */
req->client_stream_pos += bytes;
#ifdef FASCIST_LOGGING1
log_error_time();
req->client_stream[req->client_stream_pos] = '\0';
fprintf(stderr, "%s:%d -- We read %d bytes: \"%s\"\n",
__FILE__, __LINE__, bytes,
#ifdef VERY_FASCIST_LOGGING2
req->client_stream + req->client_stream_pos - bytes);
#else
"");
#endif
#endif
return 1;
}
return 1;
}
/*
* Name: read_body
* Description: Reads body from a request socket for POST CGI
*
* Return values:
*
* -1: request blocked, move to blocked queue
* 0: request done, close it down
* 1: more to do, leave on ready list
*
As quoted from RFC1945:
A valid Content-Length is required on all HTTP/1.0 POST requests. An
HTTP/1.0 server should respond with a 400 (bad request) message if it
cannot determine the length of the request message's content.
*/
int read_body(request * req)
{
int bytes_read, bytes_to_read, bytes_free;
bytes_free = BUFFER_SIZE - (req->header_end - req->header_line);
bytes_to_read = req->filesize - req->filepos;
if (bytes_to_read > bytes_free)
bytes_to_read = bytes_free;
if (bytes_to_read <= 0) {
req->status = BODY_WRITE; /* go write it */
return 1;
}
bytes_read = read(req->fd, req->header_end, bytes_to_read);
if (bytes_read == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
/*
req->status = BODY_WRITE;
return 1;
*/
return -1;
} else {
boa_perror(req, "read body");
return 0;
}
} else if (bytes_read == 0) {
/* this is an error. premature end of body! */
log_error_time();
fprintf(stderr, "%s:%d - Premature end of body!!\n",
__FILE__, __LINE__);
send_r_bad_request(req);
return 0;
}
req->status = BODY_WRITE;
#ifdef FASCIST_LOGGING1
log_error_time();
fprintf(stderr, "%s:%d - read %d bytes.\n",
__FILE__, __LINE__, bytes_to_read);
#endif
req->header_end += bytes_read;
return 1;
}
/*
* Name: write_body
* Description: Writes a chunk of data to a file
*
* Return values:
* -1: request blocked, move to blocked queue
* 0: EOF or error, close it down
* 1: successful write, recycle in ready queue
*/
int write_body(request * req)
{
int bytes_written, bytes_to_write = req->header_end - req->header_line;
if (req->filepos + bytes_to_write > req->filesize)
bytes_to_write = req->filesize - req->filepos;
if (bytes_to_write == 0) { /* nothing left in buffer to write */
req->header_line = req->header_end = req->buffer;
if (req->filepos >= req->filesize)
return init_cgi(req);
/* if here, we can safely assume that there is more to read */
req->status = BODY_READ;
return 1;
}
bytes_written = write(req->post_data_fd,
req->header_line, bytes_to_write);
if (bytes_written == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN)
return -1; /* request blocked at the pipe level, but keep going */
else if (errno == EINTR)
return 1;
else if (errno == ENOSPC) {
/* 20010520 - Alfred Fluckiger */
/* No test was originally done in this case, which might */
/* lead to a "no space left on device" error. */
boa_perror(req, "write body"); /* OK to disable if your logs get too big */
return 0;
} else {
boa_perror(req, "write body"); /* OK to disable if your logs get too big */
return 0;
}
}
#ifdef FASCIST_LOGGING
log_error_time();
fprintf(stderr, "%s:%d - wrote %d bytes. %ld of %ld\n",
__FILE__, __LINE__,
bytes_written, req->filepos, req->filesize);
#endif
req->filepos += bytes_written;
req->header_line += bytes_written;
return 1; /* more to do */
}

Binary file not shown.

View File

@ -0,0 +1,749 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: request.c,v 1.112.2.3 2002/07/24 03:03:59 jnelson Exp $*/
#include "boa.h"
#include <stddef.h> /* for offsetof */
int total_connections;
struct status status;
static int sockbufsize = SOCKETBUF_SIZE;
/* function prototypes located in this file only */
static void free_request(request ** list_head_addr, request * req);
/*
* Name: new_request
* Description: Obtains a request struct off the free list, or if the
* free list is empty, allocates memory
*
* Return value: pointer to initialized request
*/
request *new_request(void)
{
request *req;
if (request_free) {
req = request_free; /* first on free list */
dequeue(&request_free, request_free); /* dequeue the head */
} else {
req = (request *) malloc(sizeof (request));
if (!req) {
log_error_time();
perror("malloc for new request");
return NULL;
}
}
memset(req, 0, offsetof(request, buffer) + 1);
return req;
}
/*
* Name: get_request
*
* Description: Polls the server socket for a request. If one exists,
* does some basic initialization and adds it to the ready queue;.
*/
void get_request(int server_s)
{
int fd; /* socket */
struct SOCKADDR remote_addr; /* address */
struct SOCKADDR salocal;
int remote_addrlen = sizeof (struct SOCKADDR);
request *conn; /* connection */
size_t len;
static int system_bufsize = 0; /* Default size of SNDBUF given by system */
remote_addr.S_FAMILY = 0xdead;
fd = accept(server_s, (struct sockaddr *) &remote_addr,
&remote_addrlen);
if (fd == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK)
/* abnormal error */
WARN("accept");
else
/* no requests */
pending_requests = 0;
return;
}
if (fd >= FD_SETSIZE) {
WARN("Got fd >= FD_SETSIZE.");
close(fd);
return;
}
#ifdef DEBUGNONINET
/* This shows up due to race conditions in some Linux kernels
when the client closes the socket sometime between
the select() and accept() syscalls.
Code and description by Larry Doolittle <ldoolitt@boa.org>
*/
#define HEX(x) (((x)>9)?(('a'-10)+(x)):('0'+(x)))
if (remote_addr.sin_family != AF_INET) {
struct sockaddr *bogus = (struct sockaddr *) &remote_addr;
char *ap, ablock[44];
int i;
close(fd);
log_error_time();
for (ap = ablock, i = 0; i < remote_addrlen && i < 14; i++) {
*ap++ = ' ';
*ap++ = HEX((bogus->sa_data[i] >> 4) & 0x0f);
*ap++ = HEX(bogus->sa_data[i] & 0x0f);
}
*ap = '\0';
fprintf(stderr, "non-INET connection attempt: socket %d, "
"sa_family = %hu, sa_data[%d] = %s\n",
fd, bogus->sa_family, remote_addrlen, ablock);
return;
}
#endif
/* XXX Either delete this, or document why it's needed */
/* Pointed out 3-Oct-1999 by Paul Saab <paul@mu.org> */
#ifdef REUSE_EACH_CLIENT_CONNECTION_SOCKET
if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &sock_opt,
sizeof (sock_opt))) == -1) {
DIE("setsockopt: unable to set SO_REUSEADDR");
}
#endif
len = sizeof(salocal);
if (getsockname(fd, (struct sockaddr *) &salocal, &len) != 0) {
WARN("getsockname");
close(fd);
return;
}
conn = new_request();
if (!conn) {
close(fd);
return;
}
conn->fd = fd;
conn->status = READ_HEADER;
conn->header_line = conn->client_stream;
conn->time_last = current_time;
conn->kacount = ka_max;
ascii_sockaddr(&salocal, conn->local_ip_addr, NI_MAXHOST);
/* nonblocking socket */
if (set_nonblock_fd(conn->fd) == -1)
WARN("fcntl: unable to set new socket to non-block");
/* set close on exec to true */
if (fcntl(conn->fd, F_SETFD, 1) == -1)
WARN("fctnl: unable to set close-on-exec for new socket");
/* Increase buffer size if we have to.
* Only ask the system the buffer size on the first request,
* and assume all subsequent sockets have the same size.
*/
if (system_bufsize == 0) {
len = sizeof (system_bufsize);
if (getsockopt
(conn->fd, SOL_SOCKET, SO_SNDBUF, &system_bufsize, &len) == 0
&& len == sizeof (system_bufsize)) {
/*
fprintf(stderr, "%sgetsockopt reports SNDBUF %d\n",
get_commonlog_time(), system_bufsize);
*/
;
} else {
WARN("getsockopt(SNDBUF)");
system_bufsize = 1;
}
}
if (system_bufsize < sockbufsize) {
if (setsockopt
(conn->fd, SOL_SOCKET, SO_SNDBUF, (void *) &sockbufsize,
sizeof (sockbufsize)) == -1) {
WARN("setsockopt: unable to set socket buffer size");
#ifdef DIE_ON_ERROR_TUNING_SNDBUF
exit(errno);
#endif
}
}
/* for log file and possible use by CGI programs */
ascii_sockaddr(&remote_addr, conn->remote_ip_addr, NI_MAXHOST);
/* for possible use by CGI programs */
conn->remote_port = net_port(&remote_addr);
status.requests++;
#ifdef USE_TCPNODELAY
/* Thanks to Jef Poskanzer <jef@acme.com> for this tweak */
{
int one = 1;
if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY,
(void *) &one, sizeof (one)) == -1) {
DIE("setsockopt: unable to set TCP_NODELAY");
}
}
#endif
#ifndef NO_RATE_LIMIT
if (conn->fd > max_connections) {
send_r_service_unavailable(conn);
conn->status = DONE;
pending_requests = 0;
}
#endif /* NO_RATE_LIMIT */
total_connections++;
enqueue(&request_ready, conn);
}
/*
* Name: free_request
*
* Description: Deallocates memory for a finished request and closes
* down socket.
*/
static void free_request(request ** list_head_addr, request * req)
{
int i;
/* free_request should *never* get called by anything but
process_requests */
if (req->buffer_end && req->status != DEAD) {
req->status = DONE;
return;
}
/* put request on the free list */
dequeue(list_head_addr, req); /* dequeue from ready or block list */
if (req->logline) /* access log */
log_access(req);
if (req->mmap_entry_var)
release_mmap(req->mmap_entry_var);
else if (req->data_mem)
munmap(req->data_mem, req->filesize);
if (req->data_fd)
close(req->data_fd);
if (req->post_data_fd)
close(req->post_data_fd);
if (req->response_status >= 400)
status.errors++;
for (i = COMMON_CGI_COUNT; i < req->cgi_env_index; ++i) {
if (req->cgi_env[i]) {
free(req->cgi_env[i]);
} else {
log_error_time();
fprintf(stderr, "Warning: CGI Environment contains NULL value" \
"(index %d of %d).\n", i, req->cgi_env_index);
}
}
if (req->pathname)
free(req->pathname);
if (req->path_info)
free(req->path_info);
if (req->path_translated)
free(req->path_translated);
if (req->script_name)
free(req->script_name);
if ((req->keepalive == KA_ACTIVE) &&
(req->response_status < 500) && req->kacount > 0) {
int bytes_to_move;
request *conn = new_request();
if (!conn) {
/* errors already reported */
enqueue(&request_free, req);
close(req->fd);
total_connections--;
return;
}
conn->fd = req->fd;
conn->status = READ_HEADER;
conn->header_line = conn->client_stream;
conn->kacount = req->kacount - 1;
/* close enough and we avoid a call to time(NULL) */
conn->time_last = req->time_last;
/* for log file and possible use by CGI programs */
memcpy(conn->remote_ip_addr, req->remote_ip_addr, NI_MAXHOST);
memcpy(conn->local_ip_addr, req->local_ip_addr, NI_MAXHOST);
/* for possible use by CGI programs */
conn->remote_port = req->remote_port;
status.requests++;
/* we haven't parsed beyond req->parse_pos, so... */
bytes_to_move = req->client_stream_pos - req->parse_pos;
if (bytes_to_move) {
memcpy(conn->client_stream,
req->client_stream + req->parse_pos, bytes_to_move);
conn->client_stream_pos = bytes_to_move;
}
enqueue(&request_block, conn);
BOA_FD_SET(conn->fd, &block_read_fdset);
enqueue(&request_free, req);
return;
}
/*
While debugging some weird errors, Jon Nelson learned that
some versions of Netscape Navigator break the
HTTP specification.
Some research on the issue brought up:
http://www.apache.org/docs/misc/known_client_problems.html
As quoted here:
"
Trailing CRLF on POSTs
This is a legacy issue. The CERN webserver required POST
data to have an extra CRLF following it. Thus many
clients send an extra CRLF that is not included in the
Content-Length of the request. Apache works around this
problem by eating any empty lines which appear before a
request.
"
Boa will (for now) hack around this stupid bug in Netscape
(and Internet Exploder)
by reading up to 32k after the connection is all but closed.
This should eliminate any remaining spurious crlf sent
by the client.
Building bugs *into* software to be compatable is
just plain wrong
*/
if (req->method == M_POST) {
char buf[32768];
read(req->fd, buf, 32768);
}
close(req->fd);
total_connections--;
enqueue(&request_free, req);
return;
}
/*
* Name: process_requests
*
* Description: Iterates through the ready queue, passing each request
* to the appropriate handler for processing. It monitors the
* return value from handler functions, all of which return -1
* to indicate a block, 0 on completion and 1 to remain on the
* ready list for more procesing.
*/
void process_requests(int server_s)
{
int retval = 0;
request *current, *trailer;
if (pending_requests) {
get_request(server_s);
#ifdef ORIGINAL_BEHAVIOR
pending_requests = 0;
#endif
}
current = request_ready;
while (current) {
time(&current_time);
if (current->buffer_end && /* there is data in the buffer */
current->status != DEAD && current->status != DONE) {
retval = req_flush(current);
/*
* retval can be -2=error, -1=blocked, or bytes left
*/
if (retval == -2) { /* error */
current->status = DEAD;
retval = 0;
} else if (retval >= 0) {
/* notice the >= which is different from below?
Here, we may just be flushing headers.
We don't want to return 0 because we are not DONE
or DEAD */
retval = 1;
}
} else {
switch (current->status) {
case READ_HEADER:
case ONE_CR:
case ONE_LF:
case TWO_CR:
retval = read_header(current);
break;
case BODY_READ:
retval = read_body(current);
break;
case BODY_WRITE:
retval = write_body(current);
break;
case WRITE:
retval = process_get(current);
break;
case PIPE_READ:
retval = read_from_pipe(current);
break;
case PIPE_WRITE:
retval = write_from_pipe(current);
break;
case DONE:
/* a non-status that will terminate the request */
retval = req_flush(current);
/*
* retval can be -2=error, -1=blocked, or bytes left
*/
if (retval == -2) { /* error */
current->status = DEAD;
retval = 0;
} else if (retval > 0) {
retval = 1;
}
break;
case DEAD:
retval = 0;
current->buffer_end = 0;
SQUASH_KA(current);
break;
default:
retval = 0;
fprintf(stderr, "Unknown status (%d), "
"closing!\n", current->status);
current->status = DEAD;
break;
}
}
if (sigterm_flag)
SQUASH_KA(current);
/* we put this here instead of after the switch so that
* if we are on the last request, and get_request is successful,
* current->next is valid!
*/
if (pending_requests)
get_request(server_s);
switch (retval) {
case -1: /* request blocked */
trailer = current;
current = current->next;
block_request(trailer);
break;
case 0: /* request complete */
current->time_last = current_time;
trailer = current;
current = current->next;
free_request(&request_ready, trailer);
break;
case 1: /* more to do */
current->time_last = current_time;
current = current->next;
break;
default:
log_error_time();
fprintf(stderr, "Unknown retval in process.c - "
"Status: %d, retval: %d\n", current->status, retval);
current = current->next;
break;
}
}
}
/*
* Name: process_logline
*
* Description: This is called with the first req->header_line received
* by a request, called "logline" because it is logged to a file.
* It is parsed to determine request type and method, then passed to
* translate_uri for further parsing. Also sets up CGI environment if
* needed.
*/
int process_logline(request * req)
{
char *stop, *stop2;
static char *SIMPLE_HTTP_VERSION = "HTTP/0.9";
req->logline = req->client_stream;
if (!memcmp(req->logline, "GET ", 4))
req->method = M_GET;
else if (!memcmp(req->logline, "HEAD ", 5))
/* head is just get w/no body */
req->method = M_HEAD;
else if (!memcmp(req->logline, "POST ", 5))
req->method = M_POST;
else {
log_error_time();
fprintf(stderr, "malformed request: \"%s\"\n", req->logline);
send_r_not_implemented(req);
return 0;
}
req->http_version = SIMPLE_HTTP_VERSION;
req->simple = 1;
/* Guaranteed to find ' ' since we matched a method above */
stop = req->logline + 3;
if (*stop != ' ')
++stop;
/* scan to start of non-whitespace */
while (*(++stop) == ' ');
stop2 = stop;
/* scan to end of non-whitespace */
while (*stop2 != '\0' && *stop2 != ' ')
++stop2;
if (stop2 - stop > MAX_HEADER_LENGTH) {
log_error_time();
fprintf(stderr, "URI too long %d: \"%s\"\n", MAX_HEADER_LENGTH,
req->logline);
send_r_bad_request(req);
return 0;
}
memcpy(req->request_uri, stop, stop2 - stop);
req->request_uri[stop2 - stop] = '\0';
if (*stop2 == ' ') {
/* if found, we should get an HTTP/x.x */
unsigned int p1, p2;
/* scan to end of whitespace */
++stop2;
while (*stop2 == ' ' && *stop2 != '\0')
++stop2;
/* scan in HTTP/major.minor */
if (sscanf(stop2, "HTTP/%u.%u", &p1, &p2) == 2) {
/* HTTP/{0.9,1.0,1.1} */
if (p1 == 1 && (p2 == 0 || p2 == 1)) {
req->http_version = stop2;
req->simple = 0;
} else if (p1 > 1 || (p1 != 0 && p2 > 1)) {
goto BAD_VERSION;
}
} else {
goto BAD_VERSION;
}
}
if (req->method == M_HEAD && req->simple) {
send_r_bad_request(req);
return 0;
}
req->cgi_env_index = COMMON_CGI_COUNT;
return 1;
BAD_VERSION:
log_error_time();
fprintf(stderr, "bogus HTTP version: \"%s\"\n", stop2);
send_r_bad_request(req);
return 0;
}
/*
* Name: process_header_end
*
* Description: takes a request and performs some final checking before
* init_cgi or init_get
* Returns 0 for error or NPH, or 1 for success
*/
int process_header_end(request * req)
{
if (!req->logline) {
send_r_error(req);
return 0;
}
/* Percent-decode request */
if (unescape_uri(req->request_uri, &(req->query_string)) == 0) {
log_error_doc(req);
fputs("Problem unescaping uri\n", stderr);
send_r_bad_request(req);
return 0;
}
/* clean pathname */
clean_pathname(req->request_uri);
if (req->request_uri[0] != '/') {
send_r_bad_request(req);
return 0;
}
if (translate_uri(req) == 0) { /* unescape, parse uri */
SQUASH_KA(req);
return 0; /* failure, close down */
}
if (req->method == M_POST) {
req->post_data_fd = create_temporary_file(1, NULL, 0);
if (req->post_data_fd == 0)
return(0);
return(1); /* success */
}
if (req->is_cgi) {
return init_cgi(req);
}
req->status = WRITE;
return init_get(req); /* get and head */
}
/*
* Name: process_option_line
*
* Description: Parses the contents of req->header_line and takes
* appropriate action.
*/
int process_option_line(request * req)
{
char c, *value, *line = req->header_line;
/* Start by aggressively hacking the in-place copy of the header line */
#ifdef FASCIST_LOGGING
log_error_time();
fprintf(stderr, "%s:%d - Parsing \"%s\"\n", __FILE__, __LINE__, line);
#endif
value = strchr(line, ':');
if (value == NULL)
return 0;
*value++ = '\0'; /* overwrite the : */
to_upper(line); /* header types are case-insensitive */
while ((c = *value) && (c == ' ' || c == '\t'))
value++;
if (!memcmp(line, "IF_MODIFIED_SINCE", 18) && !req->if_modified_since)
req->if_modified_since = value;
else if (!memcmp(line, "CONTENT_TYPE", 13) && !req->content_type)
req->content_type = value;
else if (!memcmp(line, "CONTENT_LENGTH", 15) && !req->content_length)
req->content_length = value;
else if (!memcmp(line, "CONNECTION", 11) &&
ka_max && req->keepalive != KA_STOPPED) {
req->keepalive = (!strncasecmp(value, "Keep-Alive", 10) ?
KA_ACTIVE : KA_STOPPED);
}
/* #ifdef ACCEPT_ON */
else if (!memcmp(line, "ACCEPT", 7))
add_accept_header(req, value);
/* #endif */
/* Need agent and referer for logs */
else if (!memcmp(line, "REFERER", 8)) {
req->header_referer = value;
if (!add_cgi_env(req, "REFERER", value, 1))
return 0;
} else if (!memcmp(line, "USER_AGENT", 11)) {
req->header_user_agent = value;
if (!add_cgi_env(req, "USER_AGENT", value, 1))
return 0;
} else {
if (!add_cgi_env(req, line, value, 1))
return 0;
}
return 1;
}
/*
* Name: add_accept_header
* Description: Adds a mime_type to a requests accept char buffer
* silently ignore any that don't fit -
* shouldn't happen because of relative buffer sizes
*/
void add_accept_header(request * req, char *mime_type)
{
#ifdef ACCEPT_ON
int l = strlen(req->accept);
int l2 = strlen(mime_type);
if ((l + l2 + 2) >= MAX_HEADER_LENGTH)
return;
if (req->accept[0] == '\0')
strcpy(req->accept, mime_type);
else {
req->accept[l] = ',';
req->accept[l + 1] = ' ';
memcpy(req->accept + l + 2, mime_type, l2 + 1);
/* the +1 is for the '\0' */
/*
sprintf(req->accept + l, ", %s", mime_type);
*/
}
#endif
}
void free_requests(void)
{
request *ptr, *next;
ptr = request_free;
while (ptr != NULL) {
next = ptr->next;
free(ptr);
ptr = next;
}
request_free = NULL;
}

Binary file not shown.

View File

@ -0,0 +1,362 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996-99 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: response.c,v 1.41.2.1 2002/06/06 05:08:54 jnelson Exp $*/
#include "boa.h"
#define HTML "text/html; charset=ISO-8859-1"
#define CRLF "\r\n"
void print_content_type(request * req)
{
req_write(req, "Content-Type: ");
req_write(req, get_mime_type(req->request_uri));
req_write(req, "\r\n");
}
void print_content_length(request * req)
{
req_write(req, "Content-Length: ");
req_write(req, simple_itoa(req->filesize));
req_write(req, "\r\n");
}
void print_last_modified(request * req)
{
static char lm[] = "Last-Modified: "
" " "\r\n";
rfc822_time_buf(lm + 15, req->last_modified);
req_write(req, lm);
}
void print_ka_phrase(request * req)
{
if (req->kacount > 0 &&
req->keepalive == KA_ACTIVE && req->response_status < 500) {
req_write(req, "Connection: Keep-Alive\r\nKeep-Alive: timeout=");
req_write(req, simple_itoa(ka_timeout));
req_write(req, ", max=");
req_write(req, simple_itoa(req->kacount));
req_write(req, "\r\n");
} else
req_write(req, "Connection: close\r\n");
}
void print_http_headers(request * req)
{
static char stuff[] = "Date: "
" "
"\r\nServer: " SERVER_VERSION "\r\n";
rfc822_time_buf(stuff + 6, 0);
req_write(req, stuff);
print_ka_phrase(req);
}
/* The routines above are only called by the routines below.
* The rest of Boa only enters through the routines below.
*/
/* R_REQUEST_OK: 200 */
void send_r_request_ok(request * req)
{
req->response_status = R_REQUEST_OK;
if (req->simple)
return;
req_write(req, "HTTP/1.0 200 OK\r\n");
print_http_headers(req);
if (!req->is_cgi) {
print_content_length(req);
print_last_modified(req);
print_content_type(req);
req_write(req, "\r\n");
}
}
/* R_MOVED_PERM: 301 */
void send_r_moved_perm(request * req, char *url)
{
SQUASH_KA(req);
req->response_status = R_MOVED_PERM;
if (!req->simple) {
req_write(req, "HTTP/1.0 301 Moved Permanently\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n");
req_write(req, "Location: ");
req_write_escape_http(req, url);
req_write(req, "\r\n\r\n");
}
if (req->method != M_HEAD) {
req_write(req,
"<HTML><HEAD><TITLE>301 Moved Permanently</TITLE></HEAD>\n"
"<BODY>\n<H1>301 Moved</H1>The document has moved\n"
"<A HREF=\"");
req_write_escape_html(req, url);
req_write(req, "\">here</A>.\n</BODY></HTML>\n");
}
req_flush(req);
}
/* R_MOVED_TEMP: 302 */
void send_r_moved_temp(request * req, char *url, char *more_hdr)
{
SQUASH_KA(req);
req->response_status = R_MOVED_TEMP;
if (!req->simple) {
req_write(req, "HTTP/1.0 302 Moved Temporarily\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n");
req_write(req, "Location: ");
req_write_escape_http(req, url);
req_write(req, "\r\n");
req_write(req, more_hdr);
req_write(req, "\r\n\r\n");
}
if (req->method != M_HEAD) {
req_write(req,
"<HTML><HEAD><TITLE>302 Moved Temporarily</TITLE></HEAD>\n"
"<BODY>\n<H1>302 Moved</H1>The document has moved\n"
"<A HREF=\"");
req_write_escape_html(req, url);
req_write(req, "\">here</A>.\n</BODY></HTML>\n");
}
req_flush(req);
}
/* R_NOT_MODIFIED: 304 */
void send_r_not_modified(request * req)
{
SQUASH_KA(req);
req->response_status = R_NOT_MODIFIED;
req_write(req, "HTTP/1.0 304 Not Modified\r\n");
print_http_headers(req);
print_content_type(req);
req_write(req, "\r\n");
req_flush(req);
}
/* R_BAD_REQUEST: 400 */
void send_r_bad_request(request * req)
{
SQUASH_KA(req);
req->response_status = R_BAD_REQUEST;
if (!req->simple) {
req_write(req, "HTTP/1.0 400 Bad Request\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */
}
if (req->method != M_HEAD)
req_write(req,
"<HTML><HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n"
"<BODY><H1>400 Bad Request</H1>\nYour client has issued "
"a malformed or illegal request.\n</BODY></HTML>\n");
req_flush(req);
}
/* R_UNAUTHORIZED: 401 */
void send_r_unauthorized(request * req, char *realm_name)
{
SQUASH_KA(req);
req->response_status = R_UNAUTHORIZED;
if (!req->simple) {
req_write(req, "HTTP/1.0 401 Unauthorized\r\n");
print_http_headers(req);
req_write(req, "WWW-Authenticate: Basic realm=\"");
req_write(req, realm_name);
req_write(req, "\"\r\n");
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */
}
if (req->method != M_HEAD) {
req_write(req,
"<HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD>\n"
"<BODY><H1>401 Unauthorized</H1>\nYour client does not "
"have permission to get URL ");
req_write_escape_html(req, req->request_uri);
req_write(req, " from this server.\n</BODY></HTML>\n");
}
req_flush(req);
}
/* R_FORBIDDEN: 403 */
void send_r_forbidden(request * req)
{
SQUASH_KA(req);
req->response_status = R_FORBIDDEN;
if (!req->simple) {
req_write(req, "HTTP/1.0 403 Forbidden\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */
}
if (req->method != M_HEAD) {
req_write(req, "<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>\n"
"<BODY><H1>403 Forbidden</H1>\nYour client does not "
"have permission to get URL ");
req_write_escape_html(req, req->request_uri);
req_write(req, " from this server.\n</BODY></HTML>\n");
}
req_flush(req);
}
/* R_NOT_FOUND: 404 */
void send_r_not_found(request * req)
{
SQUASH_KA(req);
req->response_status = R_NOT_FOUND;
if (!req->simple) {
req_write(req, "HTTP/1.0 404 Not Found\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */
}
if (req->method != M_HEAD) {
req_write(req, "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
"<BODY><H1>404 Not Found</H1>\nThe requested URL ");
req_write_escape_html(req, req->request_uri);
req_write(req, " was not found on this server.\n</BODY></HTML>\n");
}
req_flush(req);
}
/* R_ERROR: 500 */
void send_r_error(request * req)
{
SQUASH_KA(req);
req->response_status = R_ERROR;
if (!req->simple) {
req_write(req, "HTTP/1.0 500 Server Error\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */
}
if (req->method != M_HEAD) {
req_write(req,
"<HTML><HEAD><TITLE>500 Server Error</TITLE></HEAD>\n"
"<BODY><H1>500 Server Error</H1>\nThe server encountered "
"an internal error and could not complete your request.\n"
"</BODY></HTML>\n");
}
req_flush(req);
}
/* R_NOT_IMP: 501 */
void send_r_not_implemented(request * req)
{
SQUASH_KA(req);
req->response_status = R_NOT_IMP;
if (!req->simple) {
req_write(req, "HTTP/1.0 501 Not Implemented\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */
}
if (req->method != M_HEAD) {
req_write(req,
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>\n"
"<BODY><H1>501 Not Implemented</H1>\nPOST to non-script "
"is not supported in Boa.\n</BODY></HTML>\n");
}
req_flush(req);
}
/* R_BAD_GATEWAY: 502 */
void send_r_bad_gateway(request * req)
{
SQUASH_KA(req);
req->response_status = R_BAD_GATEWAY;
if (!req->simple) {
req_write(req, "HTTP/1.0 502 Bad Gateway" CRLF);
print_http_headers(req);
req_write(req, "Content-Type: " HTML CRLF CRLF); /* terminate header */
}
if (req->method != M_HEAD) {
req_write(req,
"<HTML><HEAD><TITLE>502 Bad Gateway</TITLE></HEAD>\n"
"<BODY><H1>502 Bad Gateway</H1>\nThe CGI was "
"not CGI/1.1 compliant.\n" "</BODY></HTML>\n");
}
req_flush(req);
}
/* R_SERVICE_UNAVAILABLE: 503 */
void send_r_service_unavailable(request * req) /* 503 */
{
static char body[] =
"<HTML><HEAD><TITLE>503 Service Unavailable</TITLE></HEAD>\n"
"<BODY><H1>503 Service Unavailable</H1>\n"
"There are too many connections in use right now.\r\n"
"Please try again later.\r\n</BODY></HTML>\n";
static int _body_len;
static char *body_len;
if (!_body_len)
_body_len = strlen(body);
if (!body_len)
body_len = strdup(simple_itoa(_body_len));
if (!body_len) {
log_error_time();
perror("strdup of _body_len from simple_itoa");
}
SQUASH_KA(req);
req->response_status = R_SERVICE_UNAV;
if (!req->simple) {
req_write(req, "HTTP/1.0 503 Service Unavailable\r\n");
print_http_headers(req);
if (body_len) {
req_write(req, "Content-Length: ");
req_write(req, body_len);
req_write(req, "\r\n");
}
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header
*/
}
if (req->method != M_HEAD) {
req_write(req, body);
}
req_flush(req);
}
/* R_NOT_IMP: 505 */
void send_r_bad_version(request * req)
{
SQUASH_KA(req);
req->response_status = R_BAD_VERSION;
if (!req->simple) {
req_write(req, "HTTP/1.0 505 HTTP Version Not Supported\r\n");
print_http_headers(req);
req_write(req, "Content-Type: " HTML "\r\n\r\n"); /* terminate header */
}
if (req->method != M_HEAD) {
req_write(req,
"<HTML><HEAD><TITLE>505 HTTP Version Not Supported</TITLE></HEAD>\n"
"<BODY><H1>505 HTTP Version Not Supported</H1>\nHTTP versions "
"other than 0.9 and 1.0 "
"are not supported in Boa.\n<p><p>Version encountered: ");
req_write(req, req->http_version);
req_write(req, "<p><p></BODY></HTML>\n");
}
req_flush(req);
}

Binary file not shown.

View File

@ -0,0 +1,183 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996 Charles F. Randall <crandall@goldsys.com>
* Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996-2002 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: select.c,v 1.1.2.2 2002/07/23 15:54:52 jnelson Exp $*/
#include "boa.h"
static void fdset_update(void);
fd_set block_read_fdset;
fd_set block_write_fdset;
static struct timeval req_timeout; /* timeval for select */
void select_loop(int server_s)
{
FD_ZERO(&block_read_fdset);
FD_ZERO(&block_write_fdset);
/* set server_s and req_timeout */
req_timeout.tv_sec = (ka_timeout ? ka_timeout : REQUEST_TIMEOUT);
req_timeout.tv_usec = 0l; /* reset timeout */
/* preset max_fd */
max_fd = -1;
while (1) {
if (sighup_flag)
sighup_run();
if (sigchld_flag)
sigchld_run();
if (sigalrm_flag)
sigalrm_run();
if (sigterm_flag) {
if (sigterm_flag == 1)
sigterm_stage1_run(server_s);
if (sigterm_flag == 2 && !request_ready && !request_block) {
sigterm_stage2_run();
}
}
/* reset max_fd */
max_fd = -1;
if (request_block)
/* move selected req's from request_block to request_ready */
fdset_update();
/* any blocked req's move from request_ready to request_block */
process_requests(server_s);
if (!sigterm_flag && total_connections < (max_connections - 10)) {
BOA_FD_SET(server_s, &block_read_fdset); /* server always set */
}
req_timeout.tv_sec = (request_ready ? 0 :
(ka_timeout ? ka_timeout : REQUEST_TIMEOUT));
req_timeout.tv_usec = 0l; /* reset timeout */
if (select(max_fd + 1, &block_read_fdset,
&block_write_fdset, NULL,
(request_ready || request_block ? &req_timeout : NULL)) == -1) {
/* what is the appropriate thing to do here on EBADF */
if (errno == EINTR)
continue; /* while(1) */
else if (errno != EBADF) {
DIE("select");
}
}
time(&current_time);
if (FD_ISSET(server_s, &block_read_fdset))
pending_requests = 1;
}
}
/*
* Name: fdset_update
*
* Description: iterate through the blocked requests, checking whether
* that file descriptor has been set by select. Update the fd_set to
* reflect current status.
*
* Here, we need to do some things:
* - keepalive timeouts simply close
* (this is special:: a keepalive timeout is a timeout where
keepalive is active but nothing has been read yet)
* - regular timeouts close + error
* - stuff in buffer and fd ready? write it out
* - fd ready for other actions? do them
*/
static void fdset_update(void)
{
request *current, *next;
for(current = request_block;current;current = next) {
time_t time_since = current_time - current->time_last;
next = current->next;
/* hmm, what if we are in "the middle" of a request and not
* just waiting for a new one... perhaps check to see if anything
* has been read via header position, etc... */
if (current->kacount < ka_max && /* we *are* in a keepalive */
(time_since >= ka_timeout) && /* ka timeout */
!current->logline) /* haven't read anything yet */
current->status = DEAD; /* connection keepalive timed out */
else if (time_since > REQUEST_TIMEOUT) {
log_error_doc(current);
fputs("connection timed out\n", stderr);
current->status = DEAD;
}
if (current->buffer_end && current->status < DEAD) {
if (FD_ISSET(current->fd, &block_write_fdset))
ready_request(current);
else {
BOA_FD_SET(current->fd, &block_write_fdset);
}
} else {
switch (current->status) {
case WRITE:
case PIPE_WRITE:
if (FD_ISSET(current->fd, &block_write_fdset))
ready_request(current);
else {
BOA_FD_SET(current->fd, &block_write_fdset);
}
break;
case BODY_WRITE:
if (FD_ISSET(current->post_data_fd, &block_write_fdset))
ready_request(current);
else {
BOA_FD_SET(current->post_data_fd, &block_write_fdset);
}
break;
case PIPE_READ:
if (FD_ISSET(current->data_fd, &block_read_fdset))
ready_request(current);
else {
BOA_FD_SET(current->data_fd, &block_read_fdset);
}
break;
case DONE:
if (FD_ISSET(current->fd, &block_write_fdset))
ready_request(current);
else {
BOA_FD_SET(current->fd, &block_write_fdset);
}
break;
case DEAD:
ready_request(current);
break;
default:
if (FD_ISSET(current->fd, &block_read_fdset))
ready_request(current);
else {
BOA_FD_SET(current->fd, &block_read_fdset);
}
break;
}
}
current = next;
}
}

Binary file not shown.

View File

@ -0,0 +1,231 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996-99 Jon Nelson <jnelson@boa.org>
* Some changes Copyright (C) 1997 Alain Magloire <alain.magloire@rcsm.ee.mcgill.ca>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: signals.c,v 1.37.2.2 2002/07/23 16:03:41 jnelson Exp $*/
#include "boa.h"
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h> /* wait */
#endif
#include <signal.h> /* signal */
sigjmp_buf env;
int handle_sigbus;
void sigsegv(int);
void sigbus(int);
void sigterm(int);
void sighup(int);
void sigint(int);
void sigchld(int);
void sigalrm(int);
/*
* Name: init_signals
* Description: Sets up signal handlers for all our friends.
*/
void init_signals(void)
{
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGSEGV);
sigaddset(&sa.sa_mask, SIGBUS);
sigaddset(&sa.sa_mask, SIGTERM);
sigaddset(&sa.sa_mask, SIGHUP);
sigaddset(&sa.sa_mask, SIGINT);
sigaddset(&sa.sa_mask, SIGPIPE);
sigaddset(&sa.sa_mask, SIGCHLD);
sigaddset(&sa.sa_mask, SIGALRM);
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_handler = sigsegv;
sigaction(SIGSEGV, &sa, NULL);
sa.sa_handler = sigbus;
sigaction(SIGBUS, &sa, NULL);
sa.sa_handler = sigterm;
sigaction(SIGTERM, &sa, NULL);
sa.sa_handler = sighup;
sigaction(SIGHUP, &sa, NULL);
sa.sa_handler = sigint;
sigaction(SIGINT, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
sa.sa_handler = sigchld;
sigaction(SIGCHLD, &sa, NULL);
sa.sa_handler = sigalrm;
sigaction(SIGALRM, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGUSR1, &sa, NULL);
sa.sa_handler = SIG_IGN;
sigaction(SIGUSR2, &sa, NULL);
}
void sigsegv(int dummy)
{
time(&current_time);
log_error_time();
fprintf(stderr, "caught SIGSEGV, dumping core in %s\n", tempdir);
fclose(stderr);
chdir(tempdir);
abort();
}
extern sigjmp_buf env;
extern int handle_sigbus;
void sigbus(int dummy)
{
if (handle_sigbus) {
longjmp(env, dummy);
}
time(&current_time);
log_error_time();
fprintf(stderr, "caught SIGBUS, dumping core in %s\n", tempdir);
fclose(stderr);
chdir(tempdir);
abort();
}
void sigterm(int dummy)
{
sigterm_flag = 1;
}
void sigterm_stage1_run(int server_s) /* lame duck mode */
{
time(&current_time);
log_error_time();
fputs("caught SIGTERM, starting shutdown\n", stderr);
FD_CLR(server_s, &block_read_fdset);
close(server_s);
sigterm_flag = 2;
}
void sigterm_stage2_run() /* lame duck mode */
{
log_error_time();
fprintf(stderr,
"exiting Boa normally (uptime %d seconds)\n",
(int) (current_time - start_time));
chdir(tempdir);
clear_common_env();
dump_mime();
dump_passwd();
dump_alias();
free_requests();
exit(0);
}
void sighup(int dummy)
{
sighup_flag = 1;
}
void sighup_run(void)
{
sighup_flag = 0;
time(&current_time);
log_error_time();
fputs("caught SIGHUP, restarting\n", stderr);
/* Philosophy change for 0.92: don't close and attempt reopen of logfiles,
* since usual permission structure prevents such reopening.
*/
FD_ZERO(&block_read_fdset);
FD_ZERO(&block_write_fdset);
/* clear_common_env(); NEVER DO THIS */
dump_mime();
dump_passwd();
dump_alias();
free_requests();
log_error_time();
fputs("re-reading configuration files\n", stderr);
read_config_files();
log_error_time();
fputs("successful restart\n", stderr);
}
void sigint(int dummy)
{
time(&current_time);
log_error_time();
fputs("caught SIGINT: shutting down\n", stderr);
fclose(stderr);
chdir(tempdir);
exit(1);
}
void sigchld(int dummy)
{
sigchld_flag = 1;
}
void sigchld_run(void)
{
int status;
pid_t pid;
sigchld_flag = 0;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
if (verbose_cgi_logs) {
time(&current_time);
log_error_time();
fprintf(stderr, "reaping child %d: status %d\n", (int) pid, status);
}
return;
}
void sigalrm(int dummy)
{
sigalrm_flag = 1;
}
void sigalrm_run(void)
{
time(&current_time);
log_error_time();
fprintf(stderr, "%ld requests, %ld errors\n",
status.requests, status.errors);
show_hash_stats();
sigalrm_flag = 0;
}

Binary file not shown.

View File

@ -0,0 +1,143 @@
/*
* Boa, an http server
* Copyright (C) 1999 Larry Doolittle <ldoolitt@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: sublog.c,v 1.6 2002/03/24 22:40:31 jnelson Exp $*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int open_pipe_fd(char *command);
int open_net_fd(char *spec);
int open_gen_fd(char *spec);
/* Like popen, but gives fd instead of FILE * */
int open_pipe_fd(char *command)
{
int pipe_fds[2];
int pid;
/* "man pipe" says "filedes[0] is for reading,
* filedes[1] is for writing. */
if (pipe(pipe_fds) == -1)
return -1;
pid = fork();
if (pid == 0) { /* child */
close(pipe_fds[1]);
if (pipe_fds[0] != 0) {
dup2(pipe_fds[0], 0);
close(pipe_fds[0]);
}
execl("/bin/sh", "sh", "-c", command, (char *) 0);
exit(127);
}
close(pipe_fds[0]);
if (pid < 0) {
close(pipe_fds[1]);
return -1;
}
return pipe_fds[1];
}
int open_net_fd(char *spec)
{
char *p;
int fd, port;
struct sockaddr_in sa;
struct hostent *he;
p = strchr(spec, ':');
if (!p)
return -1;
*p++ = '\0';
port = strtol(p, NULL, 10);
/* printf("Host %s, port %d\n",spec,port); */
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
he = gethostbyname(spec);
if (!he) {
herror("open_net_fd");
return -1;
}
memcpy(&sa.sin_addr, he->h_addr, he->h_length);
/* printf("using ip %s\n",inet_ntoa(sa.sin_addr)); */
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0)
return fd;
if (connect(fd, (struct sockaddr *) &sa, sizeof (sa)) < 0)
return -1;
return fd;
}
int open_gen_fd(char *spec)
{
int fd;
if (*spec == '|') {
fd = open_pipe_fd(spec + 1);
} else if (*spec == ':') {
fd = open_net_fd(spec + 1);
} else {
fd = open(spec,
O_WRONLY | O_CREAT | O_APPEND,
S_IRUSR | S_IWUSR | S_IROTH | S_IRGRP);
}
return fd;
}
#ifdef STANDALONE_TEST
int main(int argc, char *argv[])
{
char buff[1024];
int fd, nr, nw;
if (argc < 2) {
fprintf(stderr,
"usage: %s output-filename\n"
" %s |output-command\n"
" %s :host:port\n", argv[0], argv[0], argv[0]);
return 1;
}
fd = open_gen_fd(argv[1]);
if (fd < 0) {
perror("open_gen_fd");
exit(1);
}
while ((nr = read(0, buff, sizeof (buff))) != 0) {
if (nr < 0) {
if (errno == EINTR)
continue;
perror("read");
exit(1);
}
nw = write(fd, buff, nr);
if (nw < 0) {
perror("write");
exit(1);
}
}
return 0;
}
#endif

Binary file not shown.

View File

@ -0,0 +1,34 @@
/*
* Boa, an http server
* Copyright (C) 1998 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: timestamp.c,v 1.9 2001/11/06 03:38:50 jnelson Exp $*/
#include "boa.h"
void timestamp(void)
{
log_error_time();
fprintf(stderr, "boa: server version %s\n", SERVER_VERSION);
log_error_time();
fprintf(stderr, "boa: server built " __DATE__ " at " __TIME__ ".\n");
log_error_time();
fprintf(stderr, "boa: starting server pid=%d, port %d\n",
(int) getpid(), server_port);
}

Binary file not shown.

View File

@ -0,0 +1,555 @@
/*
* Boa, an http server
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
* Some changes Copyright (C) 1996,97 Larry Doolittle <ldoolitt@boa.org>
* Some changes Copyright (C) 1996 Charles F. Randall <crandall@goldsys.com>
* Some changes Copyright (C) 1996-99 Jon Nelson <jnelson@boa.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* $Id: util.c,v 1.61.2.3 2002/07/07 23:22:18 jnelson Exp $ */
#include "boa.h"
#define HEX_TO_DECIMAL(char1, char2) \
(((char1 >= 'A') ? (((char1 & 0xdf) - 'A') + 10) : (char1 - '0')) * 16) + \
(((char2 >= 'A') ? (((char2 & 0xdf) - 'A') + 10) : (char2 - '0')))
const char month_tab[48] =
"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ";
const char day_tab[] = "Sun,Mon,Tue,Wed,Thu,Fri,Sat,";
/*
* Name: clean_pathname
*
* Description: Replaces unsafe/incorrect instances of:
* //[...] with /
* /./ with /
* /../ with / (technically not what we want, but browsers should deal
* with this, not servers)
*/
void clean_pathname(char *pathname)
{
char *cleanpath, c;
cleanpath = pathname;
while ((c = *pathname++)) {
if (c == '/') {
while (1) {
if (*pathname == '/')
pathname++;
else if (*pathname == '.' && *(pathname + 1) == '/')
pathname += 2;
else if (*pathname == '.' && *(pathname + 1) == '.' &&
*(pathname + 2) == '/') {
pathname += 3;
} else
break;
}
c = '/';
}
*cleanpath++ = c;
}
*cleanpath = '\0';
}
/*
* Name: get_commonlog_time
*
* Description: Returns the current time in common log format in a static
* char buffer.
*
* commonlog time is exactly 25 characters long
* because this is only used in logging, we add " [" before and "] " after
* making 29 characters
* "[27/Feb/1998:20:20:04 +0000] "
*
* Constrast with rfc822 time:
* "Sun, 06 Nov 1994 08:49:37 GMT"
*
* Altered 10 Jan 2000 by Jon Nelson ala Drew Streib for non UTC logging
*
*/
char *get_commonlog_time(void)
{
struct tm *t;
char *p;
unsigned int a;
static char buf[30];
int time_offset;
if (use_localtime) {
t = localtime(&current_time);
time_offset = TIMEZONE_OFFSET(t);
} else {
t = gmtime(&current_time);
time_offset = 0;
}
p = buf + 29;
*p-- = '\0';
*p-- = ' ';
*p-- = ']';
a = abs(time_offset / 60);
*p-- = '0' + a % 10;
a /= 10;
*p-- = '0' + a % 6;
a /= 6;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = (time_offset >= 0) ? '+' : '-';
*p-- = ' ';
a = t->tm_sec;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = ':';
a = t->tm_min;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = ':';
a = t->tm_hour;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = ':';
a = 1900 + t->tm_year;
while (a) {
*p-- = '0' + a % 10;
a /= 10;
}
/* p points to an unused spot */
*p-- = '/';
p -= 2;
memcpy(p--, month_tab + 4 * (t->tm_mon), 3);
*p-- = '/';
a = t->tm_mday;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p = '[';
return p; /* should be same as returning buf */
}
/*
* Name: month2int
*
* Description: Turns a three letter month into a 0-11 int
*
* Note: This function is from wn-v1.07 -- it's clever and fast
*/
int month2int(char *monthname)
{
switch (*monthname) {
case 'A':
return (*++monthname == 'p' ? 3 : 7);
case 'D':
return (11);
case 'F':
return (1);
case 'J':
if (*++monthname == 'a')
return (0);
return (*++monthname == 'n' ? 5 : 6);
case 'M':
return (*(monthname + 2) == 'r' ? 2 : 4);
case 'N':
return (10);
case 'O':
return (9);
case 'S':
return (8);
default:
return (-1);
}
}
/*
* Name: modified_since
* Description: Decides whether a file's mtime is newer than the
* If-Modified-Since header of a request.
*
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
31 September 2000 23:59:59 GMT ; non-standard
* RETURN VALUES:
* 0: File has not been modified since specified time.
* 1: File has been.
* -1: Error!
*/
int modified_since(time_t * mtime, char *if_modified_since)
{
struct tm *file_gmt;
char *ims_info;
char monthname[10 + 1];
int day, month, year, hour, minute, second;
int comp;
ims_info = if_modified_since;
while (*ims_info != ' ' && *ims_info != '\0')
++ims_info;
if (*ims_info != ' ')
return -1;
/* the pre-space in the third scanf skips whitespace for the string */
if (sscanf(ims_info, "%d %3s %d %d:%d:%d GMT", /* RFC 1123 */
&day, monthname, &year, &hour, &minute, &second) == 6);
else if (sscanf(ims_info, "%d-%3s-%d %d:%d:%d GMT", /* RFC 1036 */
&day, monthname, &year, &hour, &minute, &second) == 6)
year += 1900;
else if (sscanf(ims_info, " %3s %d %d:%d:%d %d", /* asctime() format */
monthname, &day, &hour, &minute, &second, &year) == 6);
/* allow this non-standard date format: 31 September 2000 23:59:59 GMT */
/* NOTE: Use if_modified_since here, because the date *starts*
* with the day, versus a throwaway item
*/
else if (sscanf(if_modified_since, "%d %10s %d %d:%d:%d GMT",
&day, monthname, &year, &hour, &minute, &second) == 6);
else {
log_error_time();
fprintf(stderr, "Error in %s, line %d: Unable to sscanf \"%s\"\n",
__FILE__, __LINE__, ims_info);
return -1; /* error */
}
file_gmt = gmtime(mtime);
month = month2int(monthname);
/* Go through from years to seconds -- if they are ever unequal,
we know which one is newer and can return */
if ((comp = 1900 + file_gmt->tm_year - year))
return (comp > 0);
if ((comp = file_gmt->tm_mon - month))
return (comp > 0);
if ((comp = file_gmt->tm_mday - day))
return (comp > 0);
if ((comp = file_gmt->tm_hour - hour))
return (comp > 0);
if ((comp = file_gmt->tm_min - minute))
return (comp > 0);
if ((comp = file_gmt->tm_sec - second))
return (comp > 0);
return 0; /* this person must really be into the latest/greatest */
}
/*
* Name: to_upper
*
* Description: Turns a string into all upper case (for HTTP_ header forming)
* AND changes - into _
*/
char *to_upper(char *str)
{
char *start = str;
while (*str) {
if (*str == '-')
*str = '_';
else
*str = toupper(*str);
str++;
}
return start;
}
/*
* Name: unescape_uri
*
* Description: Decodes a uri, changing %xx encodings with the actual
* character. The query_string should already be gone.
*
* Return values:
* 1: success
* 0: illegal string
*/
int unescape_uri(char *uri, char ** query_string)
{
char c, d;
char *uri_old;
uri_old = uri;
while ((c = *uri_old)) {
if (c == '%') {
uri_old++;
if ((c = *uri_old++) && (d = *uri_old++))
*uri++ = HEX_TO_DECIMAL(c, d);
else
return 0; /* NULL in chars to be decoded */
} else if (c == '?') { /* query string */
if (query_string)
*query_string = ++uri_old;
/* stop here */
*uri = '\0';
return(1);
break;
} else if (c == '#') { /* fragment */
/* legal part of URL, but we do *not* care.
* However, we still have to look for the query string */
if (query_string) {
++uri_old;
while((c = *uri_old)) {
if (c == '?') {
*query_string = ++uri_old;
break;
}
++uri_old;
}
}
break;
} else {
*uri++ = c;
uri_old++;
}
}
*uri = '\0';
return 1;
}
/* rfc822 (1123) time is exactly 29 characters long
* "Sun, 06 Nov 1994 08:49:37 GMT"
*/
void rfc822_time_buf(char *buf, time_t s)
{
struct tm *t;
char *p;
unsigned int a;
if (!s) {
t = gmtime(&current_time);
} else
t = gmtime(&s);
p = buf + 28;
/* p points to the last char in the buf */
p -= 3;
/* p points to where the ' ' will go */
memcpy(p--, " GMT", 4);
a = t->tm_sec;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = ':';
a = t->tm_min;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = ':';
a = t->tm_hour;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = ' ';
a = 1900 + t->tm_year;
while (a) {
*p-- = '0' + a % 10;
a /= 10;
}
/* p points to an unused spot to where the space will go */
p -= 3;
/* p points to where the first char of the month will go */
memcpy(p--, month_tab + 4 * (t->tm_mon), 4);
*p-- = ' ';
a = t->tm_mday;
*p-- = '0' + a % 10;
*p-- = '0' + a / 10;
*p-- = ' ';
p -= 3;
memcpy(p, day_tab + t->tm_wday * 4, 4);
}
char *simple_itoa(unsigned int i)
{
/* 21 digits plus null terminator, good for 64-bit or smaller ints
* for bigger ints, use a bigger buffer!
*
* 4294967295 is, incidentally, MAX_UINT (on 32bit systems at this time)
* and is 10 bytes long
*/
static char local[22];
char *p = &local[21];
*p-- = '\0';
do {
*p-- = '0' + i % 10;
i /= 10;
} while (i > 0);
return p + 1;
}
/* I don't "do" negative conversions
* Therefore, -1 indicates error
*/
int boa_atoi(char *s)
{
int retval;
char *reconv;
if (!isdigit(*s))
return -1;
retval = atoi(s);
if (retval < 0)
return -1;
reconv = simple_itoa(retval);
if (memcmp(s,reconv,strlen(s)) != 0) {
return -1;
}
return retval;
}
int create_temporary_file(short want_unlink, char *storage, int size)
{
static char boa_tempfile[MAX_PATH_LENGTH + 1];
int fd;
snprintf(boa_tempfile, MAX_PATH_LENGTH,
"%s/boa-temp.XXXXXX", tempdir);
/* open temp file */
fd = mkstemp(boa_tempfile);
if (fd == -1) {
log_error_time();
perror("mkstemp");
return 0;
}
if (storage != NULL) {
int len = strlen(boa_tempfile);
if (len < size) {
memcpy(storage, boa_tempfile, len + 1);
} else {
close(fd);
fd = 0;
log_error_time();
fprintf(stderr, "not enough memory for memcpy in storage\n");
want_unlink = 1;
}
}
if (want_unlink) {
if (unlink(boa_tempfile) == -1) {
close(fd);
fd = 0;
log_error_time();
fprintf(stderr, "unlink temp file\n");
}
}
return (fd);
}
/*
* Name: normalize_path
*
* Description: Makes sure relative paths are made absolute
*
*/
#define DIRBUF_SIZE MAX_PATH_LENGTH * 2 + 1
char * normalize_path(char *path)
{
char dirbuf[DIRBUF_SIZE];
int len1, len2;
char *endpath;
if (path[0] == '/') {
endpath = strdup(path);
} else {
#ifndef HAVE_GETCWD
perror("boa: getcwd() not defined. Aborting.");
exit(1);
#endif
if (getcwd(dirbuf, DIRBUF_SIZE) == NULL) {
if (errno == ERANGE)
perror
("boa: getcwd() failed - unable to get working directory. "
"Aborting.");
else if (errno == EACCES)
perror("boa: getcwd() failed - No read access in current "
"directory. Aborting.");
else
perror("boa: getcwd() failed - unknown error. Aborting.");
exit(1);
}
/* OK, now the hard part. */
len1 = strlen(dirbuf);
len2 = strlen(path);
if (len1 + len2 > MAX_PATH_LENGTH * 2) {
perror("boa: eek. unable to normalize pathname");
exit(1);
}
if (strcmp(path,".") != 0) {
memcpy(dirbuf + len1, "/", 1);
memcpy(dirbuf + len1 + 1, path, len2 + 1);
}
/* fprintf(stderr, "boa: normalize gets \"%s\"\n", dirbuf); */
endpath = strdup(dirbuf);
}
if (endpath == NULL) {
fprintf(stderr,
"boa: Cannot strdup path. Aborting.\n");
exit(1);
}
return endpath;
}
int real_set_block_fd(int fd)
{
int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
return -1;
flags &= ~O_NONBLOCK;
flags = fcntl(fd, F_SETFL, flags);
return flags;
}
int real_set_nonblock_fd(int fd)
{
int flags;
flags = fcntl(fd, F_GETFL);
if (flags == -1)
return -1;
flags |= O_NONBLOCK;
flags = fcntl(fd, F_SETFL, flags);
return flags;
}

Binary file not shown.

View File

@ -0,0 +1,119 @@
#!/usr/bin/perl
# webindex, a world wide web directory generating program
# Copyright (C) 1997 Larry Doolittle <ldoolitt@jlab.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 1, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# likely future additions:
# incorporate user-supplied README files
# use config file to supply icons
# other formats and sort options, including one giving dates
# more robust handling of GCOS field
# nicer date format in footer
# usable as it stands, 11-Feb-1997 lrd
# $Id: webindex.pl,v 1.2 1999/10/12 14:49:08 jon Exp $
# *****configure me******
$index="INDEX.html";
# ^^^^^^^^^^
# The line above defines the output file name from webindex.
# You should make sure it lines up with the configuration of
# the web server on your system, i.e., the DirectoryIndex
# directive in NCSA, Apache, or Boa.
$magic="This file is unmodified webindex output";
($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell)
=getpwuid($<);
# Title of the page - there is no unique mapping from the unix
# filename to URL space. Take an easy way out and just give
# the name of the directory.
# Even this can be confused, look what happens if user joe
# makes a public_html/foo directory, and runs webindex on it.
# Now user bill makes a symbolic link from his public_html/bar
# ~joe/public_html/foo. This directory now has two URLs,
# http://some.server/~joe/foo/ and http://some.server/~bill/bar/ .
# The name that we generate is foo, which is only half right :-(
@A=split("/",`pwd`);
$here=pop(@A);
# We will happily create a new $index file, or overwrite
# one if it has the magic comment in it. We won't overwrite
# a carefully hand-crafted one, though.
if (open CHK,"<$index") {
while (<CHK>) {
last if ($found = / $magic /);
}
close(CHK);
die "existing $index not overwritten" if (!$found);
}
opendir DIR, "." || die "opendir";
open OUT,">$index" || die "fopen";
print OUT "<html><head>\n<title>Index of $here</title>\n</head>\n\n";
# This comment syntax should be compatible with all HTML versions
print OUT "<!-- $magic -->\n";
print OUT "<body>\n\n<h2>Index of $here</h2>\n\n";
@A=sort(readdir(DIR));
print OUT "<h3>Directories</h3>\n<ul>\n";
print OUT "<li><A HREF=\"../\">Parent Directory</a>\n";
for $a (@A) {
next if ($a eq ".");
next if ($a eq "..");
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($a);
if ( -d _) {
print OUT "<li><A HREF=\"$a/\">$a</A>\n";
}
}
print OUT "</ul>\n";
$found=0;
for $a (@A) {
next if ($a eq ".");
next if ($a eq "..");
next if ($a eq $index);
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks) = stat($a);
if (! -d _) {
if (!$found) {
$found=1;
print OUT "<h3>Files</h3><ul>\n";
}
print OUT "<li><A HREF=\"$a\">$a</A> ($size bytes)\n";
}
}
print OUT "</ul>\n" if $found;
closedir(DIR);
$now=localtime();
($who)=split(/,/,$gcos);
print OUT "<hr>Index created by <a href=\"/~$name/\">$who</a>
with <a href=\"http://recycle.jlab.org/webindex/\">webindex</a> at $now<br>\n";
print OUT "</body>\n</html>\n";
close OUT;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/* A Bison parser, made by GNU Bison 3.0.4. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
#ifndef YY_YY_Y_TAB_H_INCLUDED
# define YY_YY_Y_TAB_H_INCLUDED
/* Debug traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
#endif
#if YYDEBUG
extern int yydebug;
#endif
/* Token type. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
enum yytokentype
{
STMT_NO_ARGS = 258,
STMT_ONE_ARG = 259,
STMT_TWO_ARGS = 260,
MIMETYPE = 261,
STRING = 262,
INTEGER = 263
};
#endif
/* Tokens. */
#define STMT_NO_ARGS 258
#define STMT_ONE_ARG 259
#define STMT_TWO_ARGS 260
#define MIMETYPE 261
#define STRING 262
#define INTEGER 263
/* Value type. */
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
union YYSTYPE
{
#line 47 "boa_grammar.y" /* yacc.c:1909 */
char * sval;
int ival;
struct ccommand * cval;
#line 76 "y.tab.h" /* yacc.c:1909 */
};
typedef union YYSTYPE YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1
#endif
extern YYSTYPE yylval;
int yyparse (void);
#endif /* !YY_YY_Y_TAB_H_INCLUDED */

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
*.a
*.o
*.cgi
capture
cgicunittest

View File

@ -0,0 +1,32 @@
CFLAGS=-g -Wall -shared -fPIC
CC=gcc
AR=ar
RANLIB=ranlib
LIBS=-L./ -lcgic
all: libcgic.so cgictest.cgi capture
install: libcgic.so
cp libcgic.so /usr/local/lib
cp cgic.h /usr/local/include
@echo libcgic.so is in /usr/local/lib. cgic.h is in /usr/local/include.
libcgic.so: cgic.o cgic.h
rm -f libcgic.so
#$(AR) rc libcgic.a cgic.o
#$(RANLIB) libcgic.so
#mingw32 and cygwin users: replace .cgi with .exe
cgictest.cgi: cgictest.o libcgic.so
gcc cgictest.o -o cgictest.cgi ${LIBS}
capture: capture.o libcgic.so
gcc capture.o -o capture ${LIBS}
clean:
rm -f *.o *.so cgictest.cgi capture cgicunittest
test:
gcc -D UNIT_TEST=1 cgic.c -o cgicunittest
./cgicunittest

Some files were not shown because too many files have changed in this diff Show More