ported webfsd files
parent
983157101a
commit
4bb2aaf719
@ -0,0 +1,86 @@
|
|||||||
|
# config
|
||||||
|
-include Make.config
|
||||||
|
include mk/Variables.mk
|
||||||
|
|
||||||
|
TARGET := webfsd
|
||||||
|
OBJS := webfsd.o request.o response.o ls.o mime.o cgi.o
|
||||||
|
|
||||||
|
mimefile := "/etc/mime.types"
|
||||||
|
CFLAGS += -DMIMEFILE=\"$(mimefile)\"
|
||||||
|
CFLAGS += -DWEBFS_VERSION=\"$(VERSION)\"
|
||||||
|
CFLAGS += -D_GNU_SOURCE
|
||||||
|
CFLAGS += -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
|
||||||
|
|
||||||
|
# default target
|
||||||
|
all: build
|
||||||
|
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
|
||||||
|
include mk/Autoconf.mk
|
||||||
|
|
||||||
|
define make-config
|
||||||
|
LIB := $(LIB)
|
||||||
|
SYSTEM := $(call ac_uname)
|
||||||
|
USE_SENDFILE := yes
|
||||||
|
USE_THREADS := no
|
||||||
|
USE_SSL := $(call ac_header,openssl/ssl.h)
|
||||||
|
USE_DIET := $(call ac_binary,diet)
|
||||||
|
endef
|
||||||
|
|
||||||
|
# sendfile yes/no
|
||||||
|
ifneq ($(USE_SENDFILE),yes)
|
||||||
|
CFLAGS += -DNO_SENDFILE
|
||||||
|
endif
|
||||||
|
|
||||||
|
# threads yes/no
|
||||||
|
ifeq ($(USE_THREADS)-$(SYSTEM),yes-linux)
|
||||||
|
CFLAGS += -DUSE_THREADS=1 -D_REENTRANT
|
||||||
|
LDLIBS += -lpthread
|
||||||
|
endif
|
||||||
|
ifeq ($(USE_THREADS)-$(SYSTEM),yes-freebsd)
|
||||||
|
CFLAGS += -DUSE_THREADS=1 -D_REENTRANT -pthread
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# OpenSSL yes/no
|
||||||
|
ifeq ($(USE_SSL),yes)
|
||||||
|
CFLAGS += -DUSE_SSL=1
|
||||||
|
OBJS += ssl.o
|
||||||
|
LDLIBS += -lssl -lcrypto
|
||||||
|
endif
|
||||||
|
|
||||||
|
# dietlibc yes/no
|
||||||
|
ifeq ($(USE_DIET),yes)
|
||||||
|
CC := diet $(CC)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# solaris tweaks
|
||||||
|
ifeq ($(SYSTEM),sunos)
|
||||||
|
LDFLAGS += -L/usr/local/ssl/lib
|
||||||
|
LDLIBS += -lresolv -lsocket -lnsl
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#################################################################
|
||||||
|
# rules
|
||||||
|
|
||||||
|
build: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
|
||||||
|
install: $(TARGET)
|
||||||
|
$(INSTALL_DIR) $(bindir)
|
||||||
|
$(INSTALL_BINARY) $(TARGET) $(bindir)
|
||||||
|
$(INSTALL_DIR) $(mandir)/man1
|
||||||
|
$(INSTALL_DATA) webfsd.man $(mandir)/man1/webfsd.1
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *~ debian/*~ *.o $(depfiles)
|
||||||
|
|
||||||
|
realclean distclean: clean
|
||||||
|
rm -f $(TARGET) Make.config
|
||||||
|
|
||||||
|
include mk/Compile.mk
|
||||||
|
include mk/Maintainer.mk
|
||||||
|
-include mk/*.dep
|
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
howto compile and install this package
|
||||||
|
======================================
|
||||||
|
|
||||||
|
|
||||||
|
really short install instructions
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
$ make
|
||||||
|
$ su -c "make install"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
the more detailed version
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Make sure you use GNU make. The file name "GNUmakefile" isn't a joke,
|
||||||
|
this package really requires GNU make.
|
||||||
|
|
||||||
|
As first step make will do some config checks on your system and write
|
||||||
|
the results to Make.config. If you want to have a look at Make.config
|
||||||
|
before the actual build starts you can run this step separately using
|
||||||
|
"make config".
|
||||||
|
|
||||||
|
The Makefiles use the usual GNU-ish Makefile conventions for variable
|
||||||
|
names and default values, i.e. prefix=/usr/local, ...
|
||||||
|
|
||||||
|
The values for some frequently adapted variables are initialized from
|
||||||
|
the enviroment. Thus you can change the defaults simply by setting
|
||||||
|
environment variables:
|
||||||
|
|
||||||
|
$ prefix="/usr"
|
||||||
|
$ CFLAGS="-O3 -mcpu=i686"
|
||||||
|
$ export prefix CFLAGS
|
||||||
|
|
||||||
|
Almost any variable can be overridden on the make command line. It is
|
||||||
|
often used this way to install into some buildroot for packaging ...
|
||||||
|
|
||||||
|
$ su -c "make DESTDIR=/tmp/buildroot install"
|
||||||
|
|
||||||
|
... but it works for most other variables equally well. There are
|
||||||
|
some exceptions through, it usually does _not_ work for CFLAGS for
|
||||||
|
example.
|
||||||
|
|
||||||
|
Try "make verbose=yes" if you want to see the complete command lines
|
||||||
|
executed by make instead of the short messages (for trouble shooting,
|
||||||
|
because you like this way, for whatever reason ...). This also makes
|
||||||
|
the config checks performed by "make config" more verbose.
|
||||||
|
|
||||||
|
If you don't trust my Makefiles you can run "make -n install" to see
|
||||||
|
what "make install" would do on your system. It will produce
|
||||||
|
human-readable output (unlike automake ...).
|
||||||
|
|
||||||
|
Have fun,
|
||||||
|
|
||||||
|
Gerd
|
||||||
|
|
||||||
|
--
|
||||||
|
Gerd Knorr <kraxel@bytesex.org>
|
@ -0,0 +1,358 @@
|
|||||||
|
|
||||||
|
This is a simple http server for pure static content. You
|
||||||
|
can use it to serve the content of a ftp server via http for
|
||||||
|
example. It is also nice to export some files the quick way
|
||||||
|
by starting a http server in a few seconds, without editing
|
||||||
|
some config file first.
|
||||||
|
|
||||||
|
It uses sendfile() and knows how to use sendfile on linux and FreeBSD.
|
||||||
|
Adding other systems shouldn't be difficult. To use it with linux
|
||||||
|
you'll need a 2.2.x kernel and glibc 2.1.
|
||||||
|
|
||||||
|
There is some sendfile emulation code which uses a userland bounce
|
||||||
|
buffer, this allows to compile and use webfs on systems without
|
||||||
|
sendfile().
|
||||||
|
|
||||||
|
|
||||||
|
Features/Design:
|
||||||
|
================
|
||||||
|
|
||||||
|
* single process: select() + non-blocking I/O.
|
||||||
|
* trimmed to use as few system calls as possible per request.
|
||||||
|
* use sendfile to avoid copying data to userspace.
|
||||||
|
* optional thread support. Every thread has its own select
|
||||||
|
loop then (compile time option, off by default, edit the
|
||||||
|
Makefile to turn it on).
|
||||||
|
* automatically generates directory listings when asked for a
|
||||||
|
directory (check for index.html available as option), caches
|
||||||
|
the listings.
|
||||||
|
* no config file, just a few switches. Try "webfsd -h" for a
|
||||||
|
list, check the man page for a more indepth description.
|
||||||
|
* Uses /etc/mime.types to map file extentions to mime/types.
|
||||||
|
* Uses normal unix access rights, it will deliver every regular
|
||||||
|
file it is able to open for reading. If you want it to serve
|
||||||
|
public-readable files only, make sure it runs as nobody/nogroup.
|
||||||
|
* supports keep-alive and pipelined requests.
|
||||||
|
* serves byte ranges.
|
||||||
|
* supports virtual hosts.
|
||||||
|
* supports ipv6.
|
||||||
|
* optional logging in common log file format.
|
||||||
|
* optional error logging (to syslog / stderr).
|
||||||
|
* limited CGI support (GET requests only).
|
||||||
|
* optional SSL support.
|
||||||
|
|
||||||
|
|
||||||
|
Plans/BUGS/TODO
|
||||||
|
===============
|
||||||
|
|
||||||
|
* figure out why the acroread plugin doesn't like my
|
||||||
|
multipart/byteranges responses.
|
||||||
|
* benchmarking / profiling.
|
||||||
|
|
||||||
|
Don't expect much more features. I want to keep it small and
|
||||||
|
simple. It is supported to serve just files and to do this in a good
|
||||||
|
and fast way. It is supposed to be HTTP/1.1 (RfC 2068) compliant.
|
||||||
|
Conditional compliant as there is no entity tag support.
|
||||||
|
|
||||||
|
|
||||||
|
Compile/Install
|
||||||
|
===============
|
||||||
|
|
||||||
|
$ make
|
||||||
|
$ su -c "make install"
|
||||||
|
|
||||||
|
See INSTALL for more details.
|
||||||
|
|
||||||
|
|
||||||
|
Tuning
|
||||||
|
======
|
||||||
|
|
||||||
|
The default for the number of parallel connections is very low (32),
|
||||||
|
you might have to raise this.
|
||||||
|
|
||||||
|
You probably don't get better performance by turning on threads. For
|
||||||
|
static content I/O bandwidth is the bottleneck. My box easily fills
|
||||||
|
up the network bandwidth while webfsd uses less than 10% CPU time
|
||||||
|
(Pentium III/450 MHz, Fast Ethernet, Tulip card).
|
||||||
|
|
||||||
|
You might win with threads if you have a very fast network connection
|
||||||
|
and a lot of traffic. The sendfile() system call blocks if it has to
|
||||||
|
read from harddisk. While one thread waits for data in sendfile(),
|
||||||
|
another can keep the network card busy. You'll probably get best
|
||||||
|
results with a small number of threads (2-3) per CPU.
|
||||||
|
|
||||||
|
Enough RAM probably also helps to speed up things. Although webfs
|
||||||
|
itself will not need very much memory, your kernel will happily use
|
||||||
|
the memory as cache for the data sent out via sendfile().
|
||||||
|
|
||||||
|
I have no benchmark numbers for webfsd.
|
||||||
|
|
||||||
|
|
||||||
|
Security
|
||||||
|
========
|
||||||
|
|
||||||
|
I can't guarantee that there are no security flaws. If you find one,
|
||||||
|
report it as a bug. I've done my very best while writing webfsd, I hope
|
||||||
|
there are no serious bugs like buffer overflows (and no other bugs of
|
||||||
|
course...). If webfsd dumps core, you /have/ a problem; this really
|
||||||
|
shouldn't happen.
|
||||||
|
|
||||||
|
Don't use versions below 1.20, there are known security holes.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.21
|
||||||
|
===============
|
||||||
|
|
||||||
|
* large file support.
|
||||||
|
* s/sprintf/snprintf/ in some places.
|
||||||
|
* changed timestamp handling, webfs doesn't attempt to parse them
|
||||||
|
any more but does a strcmp of rfc1123 dates.
|
||||||
|
* access log uses local time not GMT now.
|
||||||
|
* some ssl/cgi cleanups (based on patches from Ludo Stellingwerff).
|
||||||
|
* misc fixes.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.20
|
||||||
|
===============
|
||||||
|
|
||||||
|
* CGI pipe setup bugfix.
|
||||||
|
* Don't allow ".." as hostname (security hole with vhosts enabled).
|
||||||
|
* fix buffer overflow in ls.c with very long file names.
|
||||||
|
* misc other fixes / cleanups.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.19
|
||||||
|
===============
|
||||||
|
|
||||||
|
* documentation spell fixes (Ludo Stellingwerff).
|
||||||
|
* added missing items (last two) to the 1.18 Changes notes
|
||||||
|
(pointed out by Jedi/Sector One <j@pureftpd.org>).
|
||||||
|
* Makefile changes.
|
||||||
|
* finished user home-directory support.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.18
|
||||||
|
===============
|
||||||
|
|
||||||
|
* added -j switch.
|
||||||
|
* compile fixes for the threaded version.
|
||||||
|
* use accept filters (FreeBSD).
|
||||||
|
* shuffled around access log locks.
|
||||||
|
* added optional SSL support (based on patches by
|
||||||
|
Ludo Stellingwerff <ludo@jonkers.nl>).
|
||||||
|
* run only the absolute needed code with root privileges
|
||||||
|
(bind+chroot) if installed suid-root.
|
||||||
|
* Makefile tweaks.
|
||||||
|
* fixed buffer overflow in request.c
|
||||||
|
* started user home-directory support.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.17
|
||||||
|
===============
|
||||||
|
|
||||||
|
* fix bug in request cleanup code (didn't cleanup properly after
|
||||||
|
byte-range requests, thus making webfsd bomb out on non-range
|
||||||
|
requests following a byte-range request on the same keep-alive
|
||||||
|
connection).
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.16
|
||||||
|
===============
|
||||||
|
|
||||||
|
* fix bug in %xx handling (adding CGI support broke this).
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.14
|
||||||
|
===============
|
||||||
|
|
||||||
|
* allways use Host: supplied hostname if needed (redirect, ...).
|
||||||
|
* added -4 / -6 switches.
|
||||||
|
* Added CGI support (GET requests only).
|
||||||
|
* compile fix for OpenBSD
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.13
|
||||||
|
===============
|
||||||
|
|
||||||
|
* fixed a bug in Basic authentication.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.11
|
||||||
|
===============
|
||||||
|
|
||||||
|
* bumped the version number this time :-)
|
||||||
|
* small freebsd update (use strmode).
|
||||||
|
* added -e switch.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.10
|
||||||
|
===============
|
||||||
|
|
||||||
|
* fixed byte rage header parser to deal correctly with 64bit off_t.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.9
|
||||||
|
==============
|
||||||
|
|
||||||
|
* added pidfile support.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.8
|
||||||
|
==============
|
||||||
|
|
||||||
|
* added TCP_CORK support.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.7
|
||||||
|
==============
|
||||||
|
|
||||||
|
* one more security fix (drop secondary groups).
|
||||||
|
* catch malloc() failures in ls.c.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.6
|
||||||
|
==============
|
||||||
|
|
||||||
|
* security fix (parsing option '-n' did unchecked strcpy).
|
||||||
|
* documentation updates.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.5
|
||||||
|
==============
|
||||||
|
|
||||||
|
* fixed the sloppy usage of addrlen for the ipv6 name lookup
|
||||||
|
functions. Linux worked fine, but the BSD folks have some
|
||||||
|
more strict checks...
|
||||||
|
* allow to write the access log to stdout (use "-" as filename)
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.4
|
||||||
|
==============
|
||||||
|
|
||||||
|
* fixed a bug in the base64 decoder (which broke basic auth for some
|
||||||
|
user/passwd combinations)
|
||||||
|
* added virtual host support.
|
||||||
|
* webfsd can chroot to $DOCUMENT_ROOT now.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.3
|
||||||
|
==============
|
||||||
|
|
||||||
|
* overwrite the -b user:pw command line option to hide the password
|
||||||
|
(doesn't show up in ps anymore)
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.2
|
||||||
|
==============
|
||||||
|
|
||||||
|
* added ipv6 support.
|
||||||
|
* bugfix in logfile timestamps.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.1
|
||||||
|
==============
|
||||||
|
|
||||||
|
* added basic authentication (one username/password for all files)
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 1.0
|
||||||
|
==============
|
||||||
|
|
||||||
|
* added some casts to compile cleanly on Solaris.
|
||||||
|
* new -F flag (don't run as daemon).
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.9
|
||||||
|
==============
|
||||||
|
|
||||||
|
* fixed a quoting bug.
|
||||||
|
* documentation updates, minor tweaks.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.8
|
||||||
|
==============
|
||||||
|
|
||||||
|
* fixed a bug in the directory cache.
|
||||||
|
* fixed uncatched malloc()/realloc() failures.
|
||||||
|
* added optional pthreads support. Edit the Makefile to turn
|
||||||
|
it on.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.7
|
||||||
|
==============
|
||||||
|
|
||||||
|
* some portability problems fixed (0.6 didn't compile on FreeBSD).
|
||||||
|
* added a sendfile() emulation based on read()/write() as fallback
|
||||||
|
if there is no sendfile() available.
|
||||||
|
* bugfix: '#' must be quoted too...
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.6
|
||||||
|
==============
|
||||||
|
|
||||||
|
* increased the listen backlog.
|
||||||
|
* optionally flush every logfile line to disk.
|
||||||
|
* new switch to specify the location of the mime.types file.
|
||||||
|
* byte range bug fixes.
|
||||||
|
* switch for the hostname has been changed ('-s' => '-n').
|
||||||
|
* optional log errors to the syslog (switch '-s').
|
||||||
|
* added sample start/stop script for RedHat.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.5
|
||||||
|
==============
|
||||||
|
|
||||||
|
* FreeBSD port (Charles Randall <crandall@matchlogic.com>)
|
||||||
|
* minor tweaks and spelling fixes.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.4
|
||||||
|
==============
|
||||||
|
|
||||||
|
* last-modified headers (and 304 responses) for directory listings.
|
||||||
|
* new switch: -f index.html (or whatever you want to use for
|
||||||
|
directory indices)
|
||||||
|
* killed the access() system calls in the ls() function.
|
||||||
|
* added cache for user/group names.
|
||||||
|
* wrote a manual page.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.3
|
||||||
|
==============
|
||||||
|
|
||||||
|
* multipart/byteranges improved: You'll get a correct Content-length:
|
||||||
|
header for the whole thing, and we can handle keep-alive on these
|
||||||
|
requests now.
|
||||||
|
* bugfix: catch accept() failures.
|
||||||
|
* bugfix: quote the path in 302 redirect responses.
|
||||||
|
* accept absolute URLs ("GET http://host/path HTTP/1.1")
|
||||||
|
* fixed handling of conditional GET requests (hope it is RFC-Compilant
|
||||||
|
now...).
|
||||||
|
* bugfix: '+' must be quoted using %xx.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.2
|
||||||
|
==============
|
||||||
|
|
||||||
|
* added URL quoting.
|
||||||
|
* root can set uid/gid now.
|
||||||
|
* webfs ditches any setuid/setgid priviliges after binding to the
|
||||||
|
TCP port by setting effective to real uid/gid. It should be safe
|
||||||
|
to install webfsd suid root to allow users to use ports below
|
||||||
|
1024 (and _only_ this of course). If anyone finds a flaw in this
|
||||||
|
code drop me a note.
|
||||||
|
* more verbose directory listing.
|
||||||
|
* added logging. It does the usual logfile reopen on SIGHUP.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 0.1
|
||||||
|
==============
|
||||||
|
|
||||||
|
* first public release.
|
||||||
|
|
||||||
|
|
||||||
|
Have fun,
|
||||||
|
Gerd
|
||||||
|
|
||||||
|
--
|
||||||
|
Gerd Knorr <kraxel@bytesex.org>
|
@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* started writing (limited) CGI support for webfsd
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include "httpd.h"
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
extern char **environ;
|
||||||
|
|
||||||
|
static char *env_wlist[] = {
|
||||||
|
"PATH", "HOME",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void env_add(struct strlist **list, char *name, char *value)
|
||||||
|
{
|
||||||
|
char *line;
|
||||||
|
|
||||||
|
line = malloc(strlen(name) + strlen(value) + 2);
|
||||||
|
sprintf(line,"%s=%s",name,value);
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"cgi: env %s\n",line);
|
||||||
|
list_add(list,line,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char**
|
||||||
|
env_convert(struct strlist *list)
|
||||||
|
{
|
||||||
|
struct strlist *elem;
|
||||||
|
char **env;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 2, elem = list; NULL != elem; elem = elem->next)
|
||||||
|
i++;
|
||||||
|
env = malloc(sizeof(char*)*i);
|
||||||
|
for (i = 0, elem = list; NULL != elem; elem = elem->next)
|
||||||
|
env[i++] = elem->line;
|
||||||
|
env[i++] = NULL;
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void env_copy(struct strlist **list)
|
||||||
|
{
|
||||||
|
int i,j,l;
|
||||||
|
|
||||||
|
for (i = 0; environ[i] != NULL; i++) {
|
||||||
|
for (j = 0; env_wlist[j] != NULL; j++) {
|
||||||
|
l = strlen(env_wlist[j]);
|
||||||
|
if (0 == strncmp(environ[i],env_wlist[j],l) &&
|
||||||
|
environ[i][l] == '=') {
|
||||||
|
env_add(list,env_wlist[j],environ[i]+l+1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
cgi_request(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage addr;
|
||||||
|
struct strlist *env = NULL, *item;
|
||||||
|
char host[65],serv[9];
|
||||||
|
char filename[1024], *h, *argv[2], envname[128];
|
||||||
|
int pid,p[2],i,length;
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: is cgi request\n",req->fd);
|
||||||
|
if (-1 == pipe(p)) {
|
||||||
|
mkerror(req,500,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pid = fork();
|
||||||
|
switch (pid) {
|
||||||
|
case -1:
|
||||||
|
/* error */
|
||||||
|
if (debug)
|
||||||
|
perror("fork");
|
||||||
|
mkerror(req,500,0);
|
||||||
|
return;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* parent - webfsd */
|
||||||
|
close(p[1]);
|
||||||
|
req->cgipid = pid;
|
||||||
|
req->cgipipe = p[0];
|
||||||
|
req->state = STATE_CGI_HEADER;
|
||||||
|
close_on_exec(req->cgipipe);
|
||||||
|
fcntl(req->cgipipe,F_SETFL,O_NONBLOCK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- below is the child process (cgi) code -------- */
|
||||||
|
|
||||||
|
/* lookup local socket (before it gets closed) */
|
||||||
|
length = sizeof(addr);
|
||||||
|
getsockname(req->fd,(struct sockaddr*)&addr,&length);
|
||||||
|
getnameinfo((struct sockaddr*)&addr,length,host,64,serv,8,
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||||
|
|
||||||
|
/* setup file descriptors */
|
||||||
|
dup2(p[1],1); /* pipe -> stdout */
|
||||||
|
if (have_tty) {
|
||||||
|
int devnull = open("/dev/null",O_RDWR);
|
||||||
|
dup2(devnull,0); /* stdin */
|
||||||
|
dup2(devnull,2); /* stderr */
|
||||||
|
close(devnull);
|
||||||
|
} else {
|
||||||
|
/* nothing -- already attached to /dev/null */
|
||||||
|
}
|
||||||
|
close_on_exec(p[0]);
|
||||||
|
close_on_exec(p[1]);
|
||||||
|
|
||||||
|
/* setup environment */
|
||||||
|
env_copy(&env);
|
||||||
|
|
||||||
|
env_add(&env,"DOCUMENT_ROOT",doc_root);
|
||||||
|
env_add(&env,"GATEWAY_INTERFACE","CGI/1.1");
|
||||||
|
env_add(&env,"QUERY_STRING",req->query);
|
||||||
|
env_add(&env,"REQUEST_URI",req->uri);
|
||||||
|
env_add(&env,"REMOTE_ADDR",req->peerhost);
|
||||||
|
env_add(&env,"REMOTE_PORT",req->peerserv);
|
||||||
|
env_add(&env,"REQUEST_METHOD",req->type);
|
||||||
|
env_add(&env,"SERVER_ADMIN","root@localhost");
|
||||||
|
env_add(&env,"SERVER_NAME",server_host);
|
||||||
|
env_add(&env,"SERVER_PROTOCOL","HTTP/1.1");
|
||||||
|
env_add(&env,"SERVER_SOFTWARE",server_name);
|
||||||
|
env_add(&env,"SERVER_ADDR",host);
|
||||||
|
env_add(&env,"SERVER_PORT",serv);
|
||||||
|
|
||||||
|
for (item = req->header; NULL != item; item = item->next) {
|
||||||
|
strcpy(envname,"HTTP_");
|
||||||
|
if (1 != sscanf(item->line,"%120[-A-Za-z]: %n",envname+5,&length))
|
||||||
|
continue;
|
||||||
|
for (i = 0; envname[i]; i++) {
|
||||||
|
if (isalpha(envname[i]))
|
||||||
|
envname[i] = toupper(envname[i]);
|
||||||
|
if ('-' == envname[i])
|
||||||
|
envname[i] = '_';
|
||||||
|
}
|
||||||
|
env_add(&env,envname,item->line+length);
|
||||||
|
}
|
||||||
|
|
||||||
|
h = req->path + strlen(cgipath);
|
||||||
|
h = strchr(h,'/');
|
||||||
|
if (h) {
|
||||||
|
env_add(&env,"PATH_INFO",h);
|
||||||
|
*h = 0;
|
||||||
|
} else {
|
||||||
|
env_add(&env,"PATH_INFO","");
|
||||||
|
}
|
||||||
|
env_add(&env,"SCRIPT_NAME",req->path);
|
||||||
|
snprintf(filename,sizeof(filename)-1,"%s%s",doc_root,req->path);
|
||||||
|
env_add(&env,"SCRIPT_FILENAME",filename);
|
||||||
|
|
||||||
|
/* start cgi app */
|
||||||
|
argv[0] = filename;
|
||||||
|
argv[1] = NULL;
|
||||||
|
execve(filename,argv,env_convert(env));
|
||||||
|
|
||||||
|
/* exec failed ... */
|
||||||
|
printf("Content-Type: text/plain\n"
|
||||||
|
"\n"
|
||||||
|
"execve %s: %s\n",
|
||||||
|
filename,strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
cgi_read_header(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
struct strlist *list = NULL;
|
||||||
|
char *h,*next,*status = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
restart:
|
||||||
|
rc = read(req->cgipipe, req->cgibuf+req->cgilen, MAX_HEADER-req->cgilen);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto restart;
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
mkerror(req,500,0);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
req->cgilen += rc;
|
||||||
|
req->cgibuf[req->cgilen] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* header complete ?? */
|
||||||
|
if (NULL != (h = strstr(req->cgibuf,"\r\n\r\n")) ||
|
||||||
|
NULL != (h = strstr(req->cgibuf,"\n\n"))) {
|
||||||
|
|
||||||
|
/* parse cgi header */
|
||||||
|
for (h = req->cgibuf;; h = next) {
|
||||||
|
next = strstr(h,"\n");
|
||||||
|
next[0] = 0;
|
||||||
|
if (next[-1] == '\r')
|
||||||
|
next[-1] = 0;
|
||||||
|
next++;
|
||||||
|
|
||||||
|
if (0 == strlen(h))
|
||||||
|
break;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: cgi: hdr %s\n",req->fd,h);
|
||||||
|
if (0 == strncasecmp(h,"Status: ",8)) {
|
||||||
|
status = h+8;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: cgi: status %s\n",req->fd,status);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strncasecmp(h,"Server:",7) ||
|
||||||
|
0 == strncasecmp(h,"Connection:",11) ||
|
||||||
|
0 == strncasecmp(h,"Accept-Ranges:",14) ||
|
||||||
|
0 == strncasecmp(h,"Date:",5))
|
||||||
|
/* webfsd adds them -- filter out */
|
||||||
|
continue;
|
||||||
|
list_add(&list,h,0);
|
||||||
|
}
|
||||||
|
mkcgi(req, status ? status : "200 OK", list);
|
||||||
|
list_free(&list);
|
||||||
|
req->cgipos = next - req->cgibuf;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: cgi: pos=%d len=%d\n",req->fd,
|
||||||
|
req->cgipos, req->cgilen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->cgilen == MAX_HEADER) {
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo "Content-Type: text/plain"
|
||||||
|
echo
|
||||||
|
ls -l /proc/$$/fd
|
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/perl -wU
|
||||||
|
sleep(5);
|
||||||
|
print "Content-Type: text/html\nStatus: 200 OK\nCache-Control:
|
||||||
|
no-store\nPragma: no-cache\nConnection: close\n\n";
|
||||||
|
|
||||||
|
#The next line seems to make it a lot worse, but also without it it goes
|
||||||
|
wrong.
|
||||||
|
open (STDERR, ">&STDOUT");
|
||||||
|
|
||||||
|
|
||||||
|
sleep(5);
|
||||||
|
print "<HTML><body> Test<br><br> Test2</body></html>\n";
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
cat <<EOF
|
||||||
|
Status: 302 Redirect
|
||||||
|
Location: http://$SERVER_NAME:$SERVER_PORT/
|
||||||
|
|
||||||
|
EOF
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo "Content-Type: text/plain"
|
||||||
|
echo
|
||||||
|
set | while read line; do echo $line; sleep 1; done
|
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
echo "Content-Type: text/plain"
|
||||||
|
echo
|
||||||
|
set
|
@ -0,0 +1,257 @@
|
|||||||
|
#include <sys/stat.h>
|
||||||
|
#ifdef USE_THREADS
|
||||||
|
# include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define STATE_READ_HEADER 1
|
||||||
|
#define STATE_PARSE_HEADER 2
|
||||||
|
#define STATE_WRITE_HEADER 3
|
||||||
|
#define STATE_WRITE_BODY 4
|
||||||
|
#define STATE_WRITE_FILE 5
|
||||||
|
#define STATE_WRITE_RANGES 6
|
||||||
|
#define STATE_FINISHED 7
|
||||||
|
|
||||||
|
#define STATE_KEEPALIVE 8
|
||||||
|
#define STATE_CLOSE 9
|
||||||
|
|
||||||
|
#define STATE_CGI_HEADER 10
|
||||||
|
#define STATE_CGI_BODY_IN 11
|
||||||
|
#define STATE_CGI_BODY_OUT 12
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
# include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_HEADER 4096
|
||||||
|
#define MAX_PATH 2048
|
||||||
|
#define MAX_HOST 64
|
||||||
|
#define MAX_MISC 16
|
||||||
|
#define BR_HEADER 512
|
||||||
|
|
||||||
|
#define S1(str) #str
|
||||||
|
#define S(str) S1(str)
|
||||||
|
|
||||||
|
#define RFC1123 "%a, %d %b %Y %H:%M:%S GMT"
|
||||||
|
|
||||||
|
struct DIRCACHE {
|
||||||
|
char path[1024];
|
||||||
|
char mtime[40];
|
||||||
|
time_t add;
|
||||||
|
char *html;
|
||||||
|
int length;
|
||||||
|
|
||||||
|
#ifdef USE_THREADS
|
||||||
|
pthread_mutex_t lock_refcount;
|
||||||
|
pthread_mutex_t lock_reading;
|
||||||
|
pthread_cond_t wait_reading;
|
||||||
|
#endif
|
||||||
|
int refcount;
|
||||||
|
int reading;
|
||||||
|
|
||||||
|
struct DIRCACHE *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct REQUEST {
|
||||||
|
int fd; /* socket handle */
|
||||||
|
int state; /* what to to ??? */
|
||||||
|
time_t ping; /* last read/write (for timeouts) */
|
||||||
|
int keep_alive;
|
||||||
|
int tcp_cork;
|
||||||
|
|
||||||
|
struct sockaddr_storage peer; /* client (log) */
|
||||||
|
char peerhost[MAX_HOST+1];
|
||||||
|
char peerserv[MAX_MISC+1];
|
||||||
|
|
||||||
|
/* request */
|
||||||
|
char hreq[MAX_HEADER+1]; /* request header */
|
||||||
|
int lreq; /* request length */
|
||||||
|
int hdata; /* data in hreq */
|
||||||
|
char type[MAX_MISC+1]; /* req type */
|
||||||
|
char hostname[MAX_HOST+1]; /* hostname */
|
||||||
|
char uri[MAX_PATH+1]; /* req uri */
|
||||||
|
char path[MAX_PATH+1]; /* file path */
|
||||||
|
char query[MAX_PATH+1]; /* query string */
|
||||||
|
int major,minor; /* http version */
|
||||||
|
char auth[64];
|
||||||
|
struct strlist *header;
|
||||||
|
char *if_modified;
|
||||||
|
char *if_unmodified;
|
||||||
|
char *if_range;
|
||||||
|
char *range_hdr;
|
||||||
|
int ranges;
|
||||||
|
off_t *r_start;
|
||||||
|
off_t *r_end;
|
||||||
|
char *r_head;
|
||||||
|
int *r_hlen;
|
||||||
|
|
||||||
|
/* response */
|
||||||
|
int status; /* status code (log) */
|
||||||
|
int bc; /* byte counter (log) */
|
||||||
|
char hres[MAX_HEADER+1]; /* response header */
|
||||||
|
int lres; /* header length */
|
||||||
|
char *mime; /* mime type */
|
||||||
|
char *body;
|
||||||
|
off_t lbody;
|
||||||
|
int bfd; /* file descriptor */
|
||||||
|
struct stat bst; /* file info */
|
||||||
|
char mtime[40]; /* RFC 1123 */
|
||||||
|
off_t written;
|
||||||
|
int head_only;
|
||||||
|
int rh,rb;
|
||||||
|
struct DIRCACHE *dir;
|
||||||
|
|
||||||
|
/* CGI */
|
||||||
|
int cgipid;
|
||||||
|
int cgipipe;
|
||||||
|
char cgibuf[MAX_HEADER+1];
|
||||||
|
int cgilen,cgipos;
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
/* SSL */
|
||||||
|
SSL *ssl_s;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* linked list */
|
||||||
|
struct REQUEST *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* --- string lists --------------------------------------------- */
|
||||||
|
|
||||||
|
struct strlist {
|
||||||
|
struct strlist *next;
|
||||||
|
char *line;
|
||||||
|
int free_the_mallocs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* add element (list head) */
|
||||||
|
static void inline
|
||||||
|
list_add(struct strlist **list, char *line, int free_the_mallocs)
|
||||||
|
{
|
||||||
|
struct strlist *elem = malloc(sizeof(struct strlist));
|
||||||
|
memset(elem,0,sizeof(struct strlist));
|
||||||
|
elem->next = *list;
|
||||||
|
elem->line = line;
|
||||||
|
elem->free_the_mallocs = free_the_mallocs;
|
||||||
|
*list = elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free whole list */
|
||||||
|
static void inline
|
||||||
|
list_free(struct strlist **list)
|
||||||
|
{
|
||||||
|
struct strlist *elem,*next;
|
||||||
|
|
||||||
|
for (elem = *list; NULL != elem; elem = next) {
|
||||||
|
next = elem->next;
|
||||||
|
if (elem->free_the_mallocs)
|
||||||
|
free(elem->line);
|
||||||
|
free(elem);
|
||||||
|
}
|
||||||
|
*list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- main.c --------------------------------------------------- */
|
||||||
|
|
||||||
|
extern int debug;
|
||||||
|
extern int tcp_port;
|
||||||
|
extern int max_dircache;
|
||||||
|
extern int virtualhosts;
|
||||||
|
extern int canonicalhost;
|
||||||
|
extern int do_chroot;
|
||||||
|
extern char *server_name;
|
||||||
|
extern char *indexhtml;
|
||||||
|
extern char *cgipath;
|
||||||
|
extern char *doc_root;
|
||||||
|
extern char server_host[];
|
||||||
|
extern char *userpass;
|
||||||
|
extern char *userdir;
|
||||||
|
extern int lifespan;
|
||||||
|
extern int no_listing;
|
||||||
|
extern time_t now;
|
||||||
|
extern int have_tty;
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
extern int with_ssl;
|
||||||
|
extern SSL_CTX *ctx;
|
||||||
|
extern BIO *sbio, *ssl_bio;
|
||||||
|
extern char *certificate;
|
||||||
|
extern char *password;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void xperror(int loglevel, char *txt, char *peerhost);
|
||||||
|
void xerror(int loglevel, char *txt, char *peerhost);
|
||||||
|
|
||||||
|
static void inline close_on_exec(int fd)
|
||||||
|
{
|
||||||
|
if (cgipath)
|
||||||
|
fcntl(fd,F_SETFD,FD_CLOEXEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- ssl.c ---------------------------------------------------- */
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
extern int ssl_read(struct REQUEST *req, char *buf, int len);
|
||||||
|
extern int ssl_write(struct REQUEST *req, char *buf, int len);
|
||||||
|
extern int ssl_blk_write(struct REQUEST *req, int offset, int len);
|
||||||
|
extern void init_ssl(void);
|
||||||
|
extern void open_ssl_session(struct REQUEST *req);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* --- request.c ------------------------------------------------ */
|
||||||
|
|
||||||
|
void read_request(struct REQUEST *req, int pipelined);
|
||||||
|
void parse_request(struct REQUEST *req);
|
||||||
|
|
||||||
|
/* --- response.c ----------------------------------------------- */
|
||||||
|
|
||||||
|
extern char *h200,*h206,*h302,*h304;
|
||||||
|
|
||||||
|
extern char *h403,*b403;
|
||||||
|
extern char *h404,*b404;
|
||||||
|
extern char *h500,*b500;
|
||||||
|
extern char *h501,*b501;
|
||||||
|
|
||||||
|
void mkerror(struct REQUEST *req, int status, int ka);
|
||||||
|
void mkredirect(struct REQUEST *req);
|
||||||
|
void mkheader(struct REQUEST *req, int status);
|
||||||
|
void mkcgi(struct REQUEST *req, char *status, struct strlist *header);
|
||||||
|
void write_request(struct REQUEST *req);
|
||||||
|
|
||||||
|
/* --- ls.c ----------------------------------------------------- */
|
||||||
|
|
||||||
|
void init_quote(void);
|
||||||
|
char* quote(unsigned char *path, int maxlength);
|
||||||
|
struct DIRCACHE *get_dir(struct REQUEST *req, char *filename);
|
||||||
|
void free_dir(struct DIRCACHE *dir);
|
||||||
|
|
||||||
|
/* --- mime.c --------------------------------------------------- */
|
||||||
|
|
||||||
|
char* get_mime(char *file);
|
||||||
|
void init_mime(char *file, char *def);
|
||||||
|
|
||||||
|
/* --- cgi.c ---------------------------------------------------- */
|
||||||
|
|
||||||
|
void cgi_request(struct REQUEST *req);
|
||||||
|
void cgi_read_header(struct REQUEST *req);
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#ifdef USE_THREADS
|
||||||
|
# define INIT_LOCK(mutex) pthread_mutex_init(&mutex,NULL)
|
||||||
|
# define FREE_LOCK(mutex) pthread_mutex_destroy(&mutex)
|
||||||
|
# define DO_LOCK(mutex) pthread_mutex_lock(&mutex)
|
||||||
|
# define DO_UNLOCK(mutex) pthread_mutex_unlock(&mutex)
|
||||||
|
# define INIT_COND(cond) pthread_cond_init(&cond,NULL)
|
||||||
|
# define FREE_COND(cond) pthread_cond_destroy(&cond)
|
||||||
|
# define BCAST_COND(cond) pthread_cond_broadcast(&cond);
|
||||||
|
# define WAIT_COND(cond,mutex) pthread_cond_wait(&cond,&mutex);
|
||||||
|
#else
|
||||||
|
# define INIT_LOCK(mutex) /* nothing */
|
||||||
|
# define FREE_LOCK(mutex) /* nothing */
|
||||||
|
# define DO_LOCK(mutex) /* nothing */
|
||||||
|
# define DO_UNLOCK(mutex) /* nothing */
|
||||||
|
# define INIT_COND(cond) /* nothing */
|
||||||
|
# define FREE_COND(cond) /* nothing */
|
||||||
|
# define BCAST_COND(cond) /* nothing */
|
||||||
|
# define WAIT_COND(cond,mutex) /* nothing */
|
||||||
|
#endif
|
@ -0,0 +1,504 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "httpd.h"
|
||||||
|
|
||||||
|
#define LS_ALLOC_SIZE (4 * 4096)
|
||||||
|
#define HOMEPAGE "http://bytesex.org/webfs.html"
|
||||||
|
|
||||||
|
#ifdef USE_THREADS
|
||||||
|
static pthread_mutex_t lock_dircache = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
|
||||||
|
#define CACHE_SIZE 32
|
||||||
|
|
||||||
|
static char*
|
||||||
|
xgetpwuid(uid_t uid)
|
||||||
|
{
|
||||||
|
static char *cache[CACHE_SIZE];
|
||||||
|
static uid_t uids[CACHE_SIZE];
|
||||||
|
static unsigned int used,next;
|
||||||
|
|
||||||
|
struct passwd *pw;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (do_chroot)
|
||||||
|
return NULL; /* would'nt work anyway .. */
|
||||||
|
|
||||||
|
for (i = 0; i < used; i++) {
|
||||||
|
if (uids[i] == uid)
|
||||||
|
return cache[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 404 */
|
||||||
|
pw = getpwuid(uid);
|
||||||
|
if (NULL != cache[next]) {
|
||||||
|
free(cache[next]);
|
||||||
|
cache[next] = NULL;
|
||||||
|
}
|
||||||
|
if (NULL != pw)
|
||||||
|
cache[next] = strdup(pw->pw_name);
|
||||||
|
uids[next] = uid;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"uid: %3d n=%2d, name=%s\n",
|
||||||
|
(int)uid, next, cache[next] ? cache[next] : "?");
|
||||||
|
|
||||||
|
next++;
|
||||||
|
if (CACHE_SIZE == next) next = 0;
|
||||||
|
if (used < CACHE_SIZE) used++;
|
||||||
|
|
||||||
|
return pw ? pw->pw_name : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
xgetgrgid(gid_t gid)
|
||||||
|
{
|
||||||
|
static char *cache[CACHE_SIZE];
|
||||||
|
static gid_t gids[CACHE_SIZE];
|
||||||
|
static unsigned int used,next;
|
||||||
|
|
||||||
|
struct group *gr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (do_chroot)
|
||||||
|
return NULL; /* would'nt work anyway .. */
|
||||||
|
|
||||||
|
for (i = 0; i < used; i++) {
|
||||||
|
if (gids[i] == gid)
|
||||||
|
return cache[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 404 */
|
||||||
|
gr = getgrgid(gid);
|
||||||
|
if (NULL != cache[next]) {
|
||||||
|
free(cache[next]);
|
||||||
|
cache[next] = NULL;
|
||||||
|
}
|
||||||
|
if (NULL != gr)
|
||||||
|
cache[next] = strdup(gr->gr_name);
|
||||||
|
gids[next] = gid;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"gid: %3d n=%2d, name=%s\n",
|
||||||
|
(int)gid,next,cache[next] ? cache[next] : "?");
|
||||||
|
|
||||||
|
next++;
|
||||||
|
if (CACHE_SIZE == next) next = 0;
|
||||||
|
if (used < CACHE_SIZE) used++;
|
||||||
|
|
||||||
|
return gr ? gr->gr_name : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
|
||||||
|
struct myfile {
|
||||||
|
int r;
|
||||||
|
struct stat s;
|
||||||
|
char n[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
compare_files(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct myfile *aa = *(struct myfile**)a;
|
||||||
|
const struct myfile *bb = *(struct myfile**)b;
|
||||||
|
|
||||||
|
if (S_ISDIR(aa->s.st_mode) != S_ISDIR(bb->s.st_mode))
|
||||||
|
return S_ISDIR(aa->s.st_mode) ? -1 : 1;
|
||||||
|
return strcmp(aa->n,bb->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char do_quote[256];
|
||||||
|
|
||||||
|
void
|
||||||
|
init_quote(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
do_quote[i] = (isalnum(i) || ispunct(i)) ? 0 : 1;
|
||||||
|
do_quote['+'] = 1;
|
||||||
|
do_quote['#'] = 1;
|
||||||
|
do_quote['%'] = 1;
|
||||||
|
do_quote['"'] = 1;
|
||||||
|
do_quote['?'] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
quote(unsigned char *path, int maxlength)
|
||||||
|
{
|
||||||
|
static unsigned char buf[2048]; /* FIXME: threads break this... */
|
||||||
|
int i,j,n=strlen(path);
|
||||||
|
|
||||||
|
if (n > maxlength)
|
||||||
|
n = maxlength;
|
||||||
|
|
||||||
|
for (i=0, j=0; i<n && j<sizeof(buf)-4; i++, j++) {
|
||||||
|
if (!do_quote[path[i]]) {
|
||||||
|
buf[j] = path[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sprintf(buf+j,"%%%02x",path[i]);
|
||||||
|
j += 2;
|
||||||
|
}
|
||||||
|
buf[j] = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
|
static void strmode(mode_t mode, char *dest)
|
||||||
|
{
|
||||||
|
static const char *rwx[] = {
|
||||||
|
"---","--x","-w-","-wx","r--","r-x","rw-","rwx" };
|
||||||
|
|
||||||
|
/* file type */
|
||||||
|
switch (mode & S_IFMT) {
|
||||||
|
case S_IFIFO: dest[0] = 'p'; break;
|
||||||
|
case S_IFCHR: dest[0] = 'c'; break;
|
||||||
|
case S_IFDIR: dest[0] = 'd'; break;
|
||||||
|
case S_IFBLK: dest[0] = 'b'; break;
|
||||||
|
case S_IFREG: dest[0] = '-'; break;
|
||||||
|
case S_IFLNK: dest[0] = 'l'; break;
|
||||||
|
case S_IFSOCK: dest[0] = '='; break;
|
||||||
|
default: dest[0] = '?'; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* access rights */
|
||||||
|
sprintf(dest+1,"%s%s%s",
|
||||||
|
rwx[(mode >> 6) & 0x7],
|
||||||
|
rwx[(mode >> 3) & 0x7],
|
||||||
|
rwx[mode & 0x7]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static char*
|
||||||
|
ls(time_t now, char *hostname, char *filename, char *path, int *length)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *file;
|
||||||
|
struct myfile **files = NULL;
|
||||||
|
struct myfile **re1;
|
||||||
|
char *h1,*h2,*re2,*buf = NULL;
|
||||||
|
int count,len,size,i,uid,gid;
|
||||||
|
char line[1024];
|
||||||
|
char *pw = NULL, *gr = NULL;
|
||||||
|
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"dir: reading %s\n",filename);
|
||||||
|
if (NULL == (dir = opendir(filename)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* read dir */
|
||||||
|
uid = getuid();
|
||||||
|
gid = getgid();
|
||||||
|
for (count = 0;; count++) {
|
||||||
|
if (NULL == (file = readdir(dir)))
|
||||||
|
break;
|
||||||
|
if (0 == strcmp(file->d_name,".")) {
|
||||||
|
/* skip the the "." directory */
|
||||||
|
count--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp(path,"/") && 0 == strcmp(file->d_name,"..")) {
|
||||||
|
/* skip the ".." directory in root dir */
|
||||||
|
count--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == (count % 64)) {
|
||||||
|
re1 = realloc(files,(count+64)*sizeof(struct myfile*));
|
||||||
|
if (NULL == re1)
|
||||||
|
goto oom;
|
||||||
|
files = re1;
|
||||||
|
}
|
||||||
|
|
||||||
|
files[count] = malloc(strlen(file->d_name)+sizeof(struct myfile));
|
||||||
|
if (NULL == files[count])
|
||||||
|
goto oom;
|
||||||
|
strcpy(files[count]->n,file->d_name);
|
||||||
|
sprintf(line,"%s/%s",filename,file->d_name);
|
||||||
|
if (-1 == stat(line,&files[count]->s)) {
|
||||||
|
free(files[count]);
|
||||||
|
count--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
files[count]->r = 0;
|
||||||
|
if (S_ISDIR(files[count]->s.st_mode) ||
|
||||||
|
S_ISREG(files[count]->s.st_mode)) {
|
||||||
|
if (files[count]->s.st_uid == uid &&
|
||||||
|
files[count]->s.st_mode & 0400)
|
||||||
|
files[count]->r = 1;
|
||||||
|
else if (files[count]->s.st_uid == gid &&
|
||||||
|
files[count]->s.st_mode & 0040)
|
||||||
|
files[count]->r = 1; /* FIXME: check additional groups */
|
||||||
|
else if (files[count]->s.st_mode & 0004)
|
||||||
|
files[count]->r = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
/* sort */
|
||||||
|
if (count)
|
||||||
|
qsort(files,count,sizeof(struct myfile*),compare_files);
|
||||||
|
|
||||||
|
/* output */
|
||||||
|
size = LS_ALLOC_SIZE;
|
||||||
|
buf = malloc(size);
|
||||||
|
if (NULL == buf)
|
||||||
|
goto oom;
|
||||||
|
len = 0;
|
||||||
|
|
||||||
|
len += sprintf(buf+len,
|
||||||
|
"<head><title>%s:%d%s</title></head>\n"
|
||||||
|
"<body bgcolor=white text=black link=darkblue vlink=firebrick alink=red>\n"
|
||||||
|
"<h1>listing: \n",
|
||||||
|
hostname,tcp_port,path);
|
||||||
|
|
||||||
|
h1 = path, h2 = path+1;
|
||||||
|
for (;;) {
|
||||||
|
if (len > size)
|
||||||
|
abort();
|
||||||
|
if (len+(LS_ALLOC_SIZE>>2) > size) {
|
||||||
|
size += LS_ALLOC_SIZE;
|
||||||
|
re2 = realloc(buf,size);
|
||||||
|
if (NULL == re2)
|
||||||
|
goto oom;
|
||||||
|
buf = re2;
|
||||||
|
}
|
||||||
|
len += sprintf(buf+len,"<a href=\"%s\">%*.*s</a>",
|
||||||
|
quote(path,h2-path),
|
||||||
|
(int)(h2-h1),
|
||||||
|
(int)(h2-h1),
|
||||||
|
h1);
|
||||||
|
h1 = h2;
|
||||||
|
h2 = strchr(h2,'/');
|
||||||
|
if (NULL == h2)
|
||||||
|
break;
|
||||||
|
h2++;
|
||||||
|
}
|
||||||
|
|
||||||
|
len += sprintf(buf+len,
|
||||||
|
"</h1><hr noshade size=1><pre>\n"
|
||||||
|
"<b>access user group date "
|
||||||
|
"size name</b>\n\n");
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (len > size)
|
||||||
|
abort();
|
||||||
|
if (len+(LS_ALLOC_SIZE>>2) > size) {
|
||||||
|
size += LS_ALLOC_SIZE;
|
||||||
|
re2 = realloc(buf,size);
|
||||||
|
if (NULL == re2)
|
||||||
|
goto oom;
|
||||||
|
buf = re2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mode */
|
||||||
|
strmode(files[i]->s.st_mode, buf+len);
|
||||||
|
len += 10;
|
||||||
|
buf[len++] = ' ';
|
||||||
|
buf[len++] = ' ';
|
||||||
|
|
||||||
|
/* user */
|
||||||
|
pw = xgetpwuid(files[i]->s.st_uid);
|
||||||
|
if (NULL != pw)
|
||||||
|
len += sprintf(buf+len,"%-8.8s ",pw);
|
||||||
|
else
|
||||||
|
len += sprintf(buf+len,"%8d ",(int)files[i]->s.st_uid);
|
||||||
|
|
||||||
|
/* group */
|
||||||
|
gr = xgetgrgid(files[i]->s.st_gid);
|
||||||
|
if (NULL != gr)
|
||||||
|
len += sprintf(buf+len,"%-8.8s ",gr);
|
||||||
|
else
|
||||||
|
len += sprintf(buf+len,"%8d ",(int)files[i]->s.st_gid);
|
||||||
|
|
||||||
|
/* mtime */
|
||||||
|
if (now - files[i]->s.st_mtime > 60*60*24*30*6)
|
||||||
|
len += strftime(buf+len,255,"%b %d %Y ",
|
||||||
|
gmtime(&files[i]->s.st_mtime));
|
||||||
|
else
|
||||||
|
len += strftime(buf+len,255,"%b %d %H:%M ",
|
||||||
|
gmtime(&files[i]->s.st_mtime));
|
||||||
|
|
||||||
|
/* size */
|
||||||
|
if (S_ISDIR(files[i]->s.st_mode)) {
|
||||||
|
len += sprintf(buf+len," <DIR> ");
|
||||||
|
} else if (!S_ISREG(files[i]->s.st_mode)) {
|
||||||
|
len += sprintf(buf+len," -- ");
|
||||||
|
} else if (files[i]->s.st_size < 1024*9) {
|
||||||
|
len += sprintf(buf+len,"%4d B ",
|
||||||
|
(int)files[i]->s.st_size);
|
||||||
|
} else if (files[i]->s.st_size < 1024*1024*9) {
|
||||||
|
len += sprintf(buf+len,"%4d kB ",
|
||||||
|
(int)(files[i]->s.st_size>>10));
|
||||||
|
} else if ((int64_t)(files[i]->s.st_size) < (int64_t)1024*1024*1024*9) {
|
||||||
|
len += sprintf(buf+len,"%4d MB ",
|
||||||
|
(int)(files[i]->s.st_size>>20));
|
||||||
|
} else if ((int64_t)(files[i]->s.st_size) < (int64_t)1024*1024*1024*1024*9) {
|
||||||
|
len += sprintf(buf+len,"%4d GB ",
|
||||||
|
(int)(files[i]->s.st_size>>30));
|
||||||
|
} else {
|
||||||
|
len += sprintf(buf+len,"%4d TB ",
|
||||||
|
(int)(files[i]->s.st_size>>40));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* filename */
|
||||||
|
if (files[i]->r) {
|
||||||
|
len += sprintf(buf+len,"<a href=\"%s%s\">%s</a>\n",
|
||||||
|
quote(files[i]->n,9999),
|
||||||
|
S_ISDIR(files[i]->s.st_mode) ? "/" : "",
|
||||||
|
files[i]->n);
|
||||||
|
} else {
|
||||||
|
len += sprintf(buf+len,"%s\n",files[i]->n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strftime(line,32,"%d/%b/%Y %H:%M:%S GMT",gmtime(&now));
|
||||||
|
len += sprintf(buf+len,
|
||||||
|
"</pre><hr noshade size=1>\n"
|
||||||
|
"<small><a href=\"%s\">%s</a> %s</small>\n"
|
||||||
|
"</body>\n",
|
||||||
|
HOMEPAGE,server_name,line);
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
free(files[i]);
|
||||||
|
if (count)
|
||||||
|
free(files);
|
||||||
|
|
||||||
|
/* return results */
|
||||||
|
*length = len;
|
||||||
|
return buf;
|
||||||
|
|
||||||
|
oom:
|
||||||
|
fprintf(stderr,"oom\n");
|
||||||
|
if (files) {
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
if (files[i])
|
||||||
|
free(files[i]);
|
||||||
|
free(files);
|
||||||
|
}
|
||||||
|
if (buf)
|
||||||
|
free(buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------- */
|
||||||
|
|
||||||
|
#define MAX_CACHE_AGE 3600 /* seconds */
|
||||||
|
|
||||||
|
struct DIRCACHE *dirs = NULL;
|
||||||
|
|
||||||
|
void free_dir(struct DIRCACHE *dir)
|
||||||
|
{
|
||||||
|
DO_LOCK(dir->lock_refcount);
|
||||||
|
dir->refcount--;
|
||||||
|
if (dir->refcount > 0) {
|
||||||
|
DO_UNLOCK(dir->lock_refcount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DO_UNLOCK(dir->lock_refcount);
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"dir: delete %s\n",dir->path);
|
||||||
|
FREE_LOCK(dir->lock_refcount);
|
||||||
|
FREE_LOCK(dir->lock_reading);
|
||||||
|
FREE_COND(dir->wait_reading);
|
||||||
|
if (NULL != dir->html)
|
||||||
|
free(dir->html);
|
||||||
|
free(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DIRCACHE*
|
||||||
|
get_dir(struct REQUEST *req, char *filename)
|
||||||
|
{
|
||||||
|
struct DIRCACHE *this,*prev;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DO_LOCK(lock_dircache);
|
||||||
|
for (prev = NULL, this = dirs, i=0; this != NULL;
|
||||||
|
prev = this, this = this->next, i++) {
|
||||||
|
if (0 == strcmp(filename,this->path)) {
|
||||||
|
/* remove from list */
|
||||||
|
if (NULL == prev)
|
||||||
|
dirs = this->next;
|
||||||
|
else
|
||||||
|
prev->next = this->next;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"dir: found %s\n",this->path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i > max_dircache) {
|
||||||
|
/* reached cache size limit -> free last element */
|
||||||
|
#if 0
|
||||||
|
if (this->next != NULL) {
|
||||||
|
fprintf(stderr,"panic: this should'nt happen (%s:%d)\n",
|
||||||
|
__FILE__, __LINE__);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
free_dir(this);
|
||||||
|
this = NULL;
|
||||||
|
prev->next = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this) {
|
||||||
|
/* check mtime and cache entry age */
|
||||||
|
if (now - this->add > MAX_CACHE_AGE ||
|
||||||
|
0 != strcmp(this->mtime, req->mtime)) {
|
||||||
|
free_dir(this);
|
||||||
|
this = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this) {
|
||||||
|
/* add a new cache entry to the list */
|
||||||
|
this = malloc(sizeof(struct DIRCACHE));
|
||||||
|
this->refcount = 2;
|
||||||
|
this->reading = 1;
|
||||||
|
INIT_LOCK(this->lock_refcount);
|
||||||
|
INIT_LOCK(this->lock_reading);
|
||||||
|
INIT_COND(this->wait_reading);
|
||||||
|
this->next = dirs;
|
||||||
|
dirs = this;
|
||||||
|
DO_UNLOCK(lock_dircache);
|
||||||
|
|
||||||
|
strcpy(this->path, filename);
|
||||||
|
strcpy(this->mtime, req->mtime);
|
||||||
|
this->add = now;
|
||||||
|
this->html = ls(now,req->hostname,filename,req->path,&(this->length));
|
||||||
|
|
||||||
|
DO_LOCK(this->lock_reading);
|
||||||
|
this->reading = 0;
|
||||||
|
BCAST_COND(this->wait_reading);
|
||||||
|
DO_UNLOCK(this->lock_reading);
|
||||||
|
} else {
|
||||||
|
/* add back to the list */
|
||||||
|
this->next = dirs;
|
||||||
|
dirs = this;
|
||||||
|
this->refcount++;
|
||||||
|
DO_UNLOCK(lock_dircache);
|
||||||
|
|
||||||
|
DO_LOCK(this->lock_reading);
|
||||||
|
if (this->reading)
|
||||||
|
WAIT_COND(this->wait_reading,this->lock_reading);
|
||||||
|
DO_UNLOCK(this->lock_reading);
|
||||||
|
}
|
||||||
|
|
||||||
|
req->body = this->html;
|
||||||
|
req->lbody = this->length;
|
||||||
|
return this;
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "httpd.h"
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
|
||||||
|
struct MIME {
|
||||||
|
char ext[8];
|
||||||
|
char type[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *mime_default;
|
||||||
|
static struct MIME *mime_types;
|
||||||
|
static int mime_count;
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_mime(char *ext, char *type)
|
||||||
|
{
|
||||||
|
if (0 == (mime_count % 64))
|
||||||
|
mime_types = realloc(mime_types,(mime_count+64)*sizeof(struct MIME));
|
||||||
|
strcpy(mime_types[mime_count].ext, ext);
|
||||||
|
strcpy(mime_types[mime_count].type,type);
|
||||||
|
mime_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
get_mime(char *file)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *ext;
|
||||||
|
|
||||||
|
ext = strrchr(file,'.');
|
||||||
|
if (NULL == ext)
|
||||||
|
return mime_default;
|
||||||
|
ext++;
|
||||||
|
for (i = 0; i < mime_count; i++) {
|
||||||
|
if (0 == strcasecmp(ext,mime_types[i].ext))
|
||||||
|
return mime_types[i].type;
|
||||||
|
}
|
||||||
|
return mime_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init_mime(char *file,char *def)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char line[128], type[64], ext[8];
|
||||||
|
int len,off;
|
||||||
|
|
||||||
|
mime_default = strdup(def);
|
||||||
|
if (NULL == (fp = fopen(file,"r"))) {
|
||||||
|
fprintf(stderr,"open %s: %s\n",file,strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (NULL != fgets(line,127,fp)) {
|
||||||
|
if (line[0] == '#')
|
||||||
|
continue;
|
||||||
|
if (1 != sscanf(line,"%63s%n",type,&len))
|
||||||
|
continue;
|
||||||
|
off = len;
|
||||||
|
for (;;) {
|
||||||
|
if (1 != sscanf(line+off,"%7s%n",ext,&len))
|
||||||
|
break;
|
||||||
|
off += len;
|
||||||
|
add_mime(ext,type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
*.dep
|
@ -0,0 +1,138 @@
|
|||||||
|
#
|
||||||
|
# simple autoconf system for GNU make
|
||||||
|
#
|
||||||
|
# (c) 2002-2004 Gerd Knorr <kraxel@bytesex.org>
|
||||||
|
#
|
||||||
|
# credits for creating this one go to the autotools people because
|
||||||
|
# they managed it to annoy lots of developers and users (including
|
||||||
|
# me) with version incompatibilities.
|
||||||
|
#
|
||||||
|
# This file is public domain. No warranty. If it breaks you keep
|
||||||
|
# both pieces.
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# verbose yes/no
|
||||||
|
verbose ?= no
|
||||||
|
|
||||||
|
# some stuff used by the tests
|
||||||
|
ifneq ($(verbose),no)
|
||||||
|
# verbose (for debug)
|
||||||
|
ac_init = echo "checking $(1) ... " >&2; rc=no
|
||||||
|
ac_b_cmd = echo "run: $(1)" >&2; $(1) >/dev/null && rc=yes
|
||||||
|
ac_s_cmd = echo "run: $(1)" >&2; rc=`$(1)`
|
||||||
|
ac_fini = echo "... result is $${rc}" >&2; echo >&2; echo "$${rc}"
|
||||||
|
else
|
||||||
|
# normal
|
||||||
|
ac_init = echo -n "checking $(1) ... " >&2; rc=no
|
||||||
|
ac_b_cmd = $(1) >/dev/null 2>&1 && rc=yes
|
||||||
|
ac_s_cmd = rc=`$(1) 2>/dev/null`
|
||||||
|
ac_fini = echo "$${rc}" >&2; echo "$${rc}"
|
||||||
|
endif
|
||||||
|
|
||||||
|
# some helpers to build cflags and related variables
|
||||||
|
ac_def_cflags_1 = $(if $(filter yes,$($(1))),-D$(1))
|
||||||
|
ac_lib_cflags = $(foreach lib,$(1),$(call ac_def_cflags_1,HAVE_LIB$(lib)))
|
||||||
|
ac_inc_cflags = $(foreach inc,$(1),$(call ac_def_cflags_1,HAVE_$(inc)))
|
||||||
|
ac_lib_mkvar_1 = $(if $(filter yes,$(HAVE_LIB$(1))),$($(1)_$(2)))
|
||||||
|
ac_lib_mkvar = $(foreach lib,$(1),$(call ac_lib_mkvar_1,$(lib),$(2)))
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# the tests ...
|
||||||
|
|
||||||
|
# get uname
|
||||||
|
ac_uname = $(shell \
|
||||||
|
$(call ac_init,for system);\
|
||||||
|
$(call ac_s_cmd,uname -s | tr 'A-Z' 'a-z');\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
# check for some header file
|
||||||
|
# args: header file
|
||||||
|
ac_header = $(shell \
|
||||||
|
$(call ac_init,for $(1));\
|
||||||
|
$(call ac_b_cmd,echo '\#include <$(1)>' |\
|
||||||
|
$(CC) $(CFLAGS) -E -);\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
# check for some function
|
||||||
|
# args: function [, additional libs ]
|
||||||
|
ac_func = $(shell \
|
||||||
|
$(call ac_init,for $(1));\
|
||||||
|
echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \
|
||||||
|
> __actest.c;\
|
||||||
|
$(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \
|
||||||
|
__actest __actest.c $(2));\
|
||||||
|
rm -f __actest __actest.c;\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
# check for some library
|
||||||
|
# args: function, library [, additional libs ]
|
||||||
|
ac_lib = $(shell \
|
||||||
|
$(call ac_init,for $(1) in $(2));\
|
||||||
|
echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \
|
||||||
|
> __actest.c;\
|
||||||
|
$(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \
|
||||||
|
__actest __actest.c -l$(2) $(3));\
|
||||||
|
rm -f __actest __actest.c;\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
# check if some compiler flag works
|
||||||
|
# args: compiler flag
|
||||||
|
ac_cflag = $(shell \
|
||||||
|
$(call ac_init,if $(CC) supports $(1));\
|
||||||
|
echo 'int main() {return 0;}' > __actest.c;\
|
||||||
|
$(call ac_b_cmd,$(CC) $(CFLAGS) $(1) $(LDFLAGS) -o \
|
||||||
|
__actest __actest.c);\
|
||||||
|
rm -f __actest __actest.c;\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
# check for some binary
|
||||||
|
# args: binary name
|
||||||
|
ac_binary = $(shell \
|
||||||
|
$(call ac_init,for $(1));\
|
||||||
|
$(call ac_s_cmd,which $(1));\
|
||||||
|
bin="$$rc";rc="no";\
|
||||||
|
$(call ac_b_cmd,test -x "$$$$bin");\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
# check if lib64 is used
|
||||||
|
ac_lib64 = $(shell \
|
||||||
|
$(call ac_init,for libdir name);\
|
||||||
|
$(call ac_s_cmd,$(CC) -print-search-dirs | grep -q lib64 &&\
|
||||||
|
echo "lib64" || echo "lib");\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
# check for x11 ressource dir prefix
|
||||||
|
ac_resdir = $(shell \
|
||||||
|
$(call ac_init,for X11 app-defaults prefix);\
|
||||||
|
$(call ac_s_cmd, test -d /etc/X11/app-defaults &&\
|
||||||
|
echo "/etc/X11" || echo "/usr/X11R6/lib/X11");\
|
||||||
|
$(call ac_fini))
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# build Make.config
|
||||||
|
|
||||||
|
define newline
|
||||||
|
|
||||||
|
|
||||||
|
endef
|
||||||
|
make-config-q = $(subst $(newline),\n,$(make-config))
|
||||||
|
|
||||||
|
ifeq ($(filter config,$(MAKECMDGOALS)),config)
|
||||||
|
.PHONY: Make.config
|
||||||
|
LIB := $(call ac_lib64)
|
||||||
|
else
|
||||||
|
LIB ?= $(call ac_lib64)
|
||||||
|
LIB := $(LIB)
|
||||||
|
endif
|
||||||
|
.PHONY: config
|
||||||
|
config: Make.config
|
||||||
|
@true
|
||||||
|
|
||||||
|
Make.config: $(srcdir)/GNUmakefile
|
||||||
|
@echo -e "$(make-config-q)" > $@
|
||||||
|
@echo
|
||||||
|
@echo "Make.config written, edit if needed"
|
||||||
|
@echo
|
@ -0,0 +1,88 @@
|
|||||||
|
#
|
||||||
|
# some rules to compile stuff ...
|
||||||
|
#
|
||||||
|
# (c) 2002-2004 Gerd Knorr <kraxel@bytesex.org>
|
||||||
|
#
|
||||||
|
# main features:
|
||||||
|
# * autodependencies via "cpp -MD"
|
||||||
|
# * fancy, non-verbose output
|
||||||
|
#
|
||||||
|
# This file is public domain. No warranty. If it breaks you keep
|
||||||
|
# both pieces.
|
||||||
|
#
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# verbose yes/no
|
||||||
|
verbose ?= no
|
||||||
|
|
||||||
|
# dependency files
|
||||||
|
tmpdep = mk/$(subst /,_,$*).tmp
|
||||||
|
depfile = mk/$(subst /,_,$*).dep
|
||||||
|
depfiles = mk/*.dep
|
||||||
|
|
||||||
|
compile_c = $(CC) $(CFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $<
|
||||||
|
compile_cc = $(CXX) $(CXXFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $<
|
||||||
|
fixup_deps = sed -e "s|.*\.o:|$@:|" < $(tmpdep) > $(depfile) && rm -f $(tmpdep)
|
||||||
|
cc_makedirs = mkdir -p $(dir $@) $(dir $(depfile))
|
||||||
|
|
||||||
|
link_app = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||||
|
link_so = $(CC) $(LDFLAGS) -shared -Wl,-soname,$(@F) -o $@ $^ $(LDLIBS)
|
||||||
|
ar_lib = rm -f $@ && ar -r $@ $^ && ranlib $@
|
||||||
|
|
||||||
|
moc_h = $(MOC) $< -o $@
|
||||||
|
msgfmt_po = msgfmt -o $@ $<
|
||||||
|
|
||||||
|
# non-verbose output
|
||||||
|
ifeq ($(verbose),no)
|
||||||
|
echo_compile_c = echo " CC " $@
|
||||||
|
echo_compile_cc = echo " CXX " $@
|
||||||
|
echo_link_app = echo " LD " $@
|
||||||
|
echo_link_so = echo " LD " $@
|
||||||
|
echo_ar_lib = echo " AR " $@
|
||||||
|
echo_moc_h = echo " MOC " $@
|
||||||
|
echo_msgfmt_po = echo " MSGFMT " $@
|
||||||
|
else
|
||||||
|
echo_compile_c = echo $(compile_c)
|
||||||
|
echo_compile_cc = echo $(compile_cc)
|
||||||
|
echo_link_app = echo $(link_app)
|
||||||
|
echo_link_so = echo $(link_so)
|
||||||
|
echo_ar_lib = echo $(ar_lib)
|
||||||
|
echo_moc_h = echo $(moc_h)
|
||||||
|
echo_msgfmt_po = echo $(msgfmt_po)
|
||||||
|
endif
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
@$(cc_makedirs)
|
||||||
|
@$(echo_compile_c)
|
||||||
|
@$(compile_c)
|
||||||
|
@$(fixup_deps)
|
||||||
|
|
||||||
|
%.o: %.cc
|
||||||
|
@$(cc_makedirs)
|
||||||
|
@$(echo_compile_cc)
|
||||||
|
@$(compile_cc)
|
||||||
|
@$(fixup_deps)
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
@$(cc_makedirs)
|
||||||
|
@$(echo_compile_cc)
|
||||||
|
@$(compile_cc)
|
||||||
|
@$(fixup_deps)
|
||||||
|
|
||||||
|
|
||||||
|
%.so: %.o
|
||||||
|
@$(echo_link_so)
|
||||||
|
@$(link_so)
|
||||||
|
|
||||||
|
%: %.o
|
||||||
|
@$(echo_link_app)
|
||||||
|
@$(link_app)
|
||||||
|
|
||||||
|
%.moc : %.h
|
||||||
|
@$(echo_moc_h)
|
||||||
|
@$(moc_h)
|
||||||
|
|
||||||
|
%.mo : %.po
|
||||||
|
@$(echo_msgfmt_po)
|
||||||
|
@$(msgfmt_po)
|
||||||
|
|
@ -0,0 +1,12 @@
|
|||||||
|
# just some maintainer stuff for me ...
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
make-sync-dir = $(HOME)/src/gnu-make
|
||||||
|
|
||||||
|
.PHONY: sync
|
||||||
|
sync:: distclean
|
||||||
|
test -d $(make-sync-dir)
|
||||||
|
rm -f $(srcdir)/INSTALL $(srcdir)/mk/*.mk
|
||||||
|
cp -v $(make-sync-dir)/INSTALL $(srcdir)/.
|
||||||
|
cp -v $(make-sync-dir)/*.mk $(srcdir)/mk
|
||||||
|
chmod 444 $(srcdir)/INSTALL $(srcdir)/mk/*.mk
|
@ -0,0 +1,46 @@
|
|||||||
|
# common variables ...
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# directories
|
||||||
|
DESTDIR =
|
||||||
|
srcdir ?= .
|
||||||
|
prefix ?= /usr/local
|
||||||
|
bindir = $(DESTDIR)$(prefix)/bin
|
||||||
|
mandir = $(DESTDIR)$(prefix)/share/man
|
||||||
|
locdir = $(DESTDIR)$(prefix)/share/locale
|
||||||
|
|
||||||
|
# package + version
|
||||||
|
empty :=
|
||||||
|
space := $(empty) $(empty)
|
||||||
|
ifneq ($(wildcard $(srcdir)/VERSION),)
|
||||||
|
VERSION := $(shell cat $(srcdir)/VERSION)
|
||||||
|
else
|
||||||
|
VERSION := 42
|
||||||
|
endif
|
||||||
|
|
||||||
|
# programs
|
||||||
|
CC ?= gcc
|
||||||
|
CXX ?= g++
|
||||||
|
MOC ?= $(if $(QTDIR),$(QTDIR)/bin/moc,moc)
|
||||||
|
INSTALL ?= install
|
||||||
|
INSTALL_BINARY := $(INSTALL) -s
|
||||||
|
INSTALL_SCRIPT := $(INSTALL)
|
||||||
|
INSTALL_DATA := $(INSTALL) -m 644
|
||||||
|
INSTALL_DIR := $(INSTALL) -d
|
||||||
|
|
||||||
|
# cflags
|
||||||
|
CFLAGS ?= -g -O2
|
||||||
|
CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes \
|
||||||
|
-Wpointer-arith -Wunused
|
||||||
|
|
||||||
|
# add /usr/local to the search path if something is in there ...
|
||||||
|
ifneq ($(wildcard /usr/local/include/*.h),)
|
||||||
|
CFLAGS += -I/usr/local/include
|
||||||
|
LDFLAGS += -L/usr/local/$(LIB)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# fixup include path for $(srcdir) != "."
|
||||||
|
ifneq ($(srcdir),.)
|
||||||
|
CFLAGS += -I. -I$(srcdir)
|
||||||
|
endif
|
||||||
|
|
@ -0,0 +1,628 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "httpd.h"
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
read_request(struct REQUEST *req, int pipelined)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char *h;
|
||||||
|
|
||||||
|
restart:
|
||||||
|
#ifdef USE_SSL
|
||||||
|
if (with_ssl)
|
||||||
|
rc = ssl_read(req, req->hreq + req->hdata, MAX_HEADER - req->hdata);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
rc = read(req->fd, req->hreq + req->hdata, MAX_HEADER - req->hdata);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
if (pipelined)
|
||||||
|
break; /* check if there is already a full request */
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (errno == EINTR)
|
||||||
|
goto restart;
|
||||||
|
xperror(LOG_INFO,"read",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
req->hdata += rc;
|
||||||
|
req->hreq[req->hdata] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if this looks like a http request after
|
||||||
|
the first few bytes... */
|
||||||
|
if (req->hdata < 5)
|
||||||
|
return;
|
||||||
|
if (strncmp(req->hreq,"GET ",4) != 0 &&
|
||||||
|
strncmp(req->hreq,"PUT ",4) != 0 &&
|
||||||
|
strncmp(req->hreq,"HEAD ",5) != 0 &&
|
||||||
|
strncmp(req->hreq,"POST ",5) != 0) {
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* header complete ?? */
|
||||||
|
if (NULL != (h = strstr(req->hreq,"\r\n\r\n")) ||
|
||||||
|
NULL != (h = strstr(req->hreq,"\n\n"))) {
|
||||||
|
if (*h == '\r') {
|
||||||
|
h += 4;
|
||||||
|
*(h-2) = 0;
|
||||||
|
} else {
|
||||||
|
h += 2;
|
||||||
|
*(h-1) = 0;
|
||||||
|
}
|
||||||
|
req->lreq = h - req->hreq;
|
||||||
|
req->state = STATE_PARSE_HEADER;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->hdata == MAX_HEADER) {
|
||||||
|
/* oops: buffer full, but found no complete request ... */
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static time_t
|
||||||
|
parse_date(char *line)
|
||||||
|
{
|
||||||
|
static char *m[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||||
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||||
|
char month[4];
|
||||||
|
struct tm tm;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
line = strchr(line,' '); /* skip weekday */
|
||||||
|
if (NULL == line)
|
||||||
|
return -1;
|
||||||
|
line++;
|
||||||
|
|
||||||
|
/* first: RFC 1123 date ... */
|
||||||
|
if (6 != sscanf(line,"%2d %3s %4d %2d:%2d:%2d GMT",
|
||||||
|
&tm.tm_mday,month,&tm.tm_year,
|
||||||
|
&tm.tm_hour,&tm.tm_min,&tm.tm_sec))
|
||||||
|
/* second: RFC 1036 date ... */
|
||||||
|
if (6 != sscanf(line,"%2d-%3s-%2d %2d:%2d:%2d GMT",
|
||||||
|
&tm.tm_mday,month,&tm.tm_year,
|
||||||
|
&tm.tm_hour,&tm.tm_min,&tm.tm_sec))
|
||||||
|
/* third: asctime() format */
|
||||||
|
if (6 != sscanf(line,"%3s %2d %2d:%2d:%2d %4d",
|
||||||
|
month,&tm.tm_mday,
|
||||||
|
&tm.tm_hour,&tm.tm_min,&tm.tm_sec,
|
||||||
|
&tm.tm_year))
|
||||||
|
/* none worked :-( */
|
||||||
|
return -1;
|
||||||
|
for (i = 0; i <= 11; i++)
|
||||||
|
if (0 == strcmp(month,m[i]))
|
||||||
|
break;
|
||||||
|
tm.tm_mon = i;
|
||||||
|
if (tm.tm_year > 1900)
|
||||||
|
tm.tm_year -= 1900;
|
||||||
|
|
||||||
|
return mktime(&tm);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static off_t
|
||||||
|
parse_off_t(char *str, int *pos)
|
||||||
|
{
|
||||||
|
off_t value = 0;
|
||||||
|
|
||||||
|
while (isdigit(str[*pos])) {
|
||||||
|
value *= 10;
|
||||||
|
value += str[*pos] - '0';
|
||||||
|
(*pos)++;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_ranges(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
char *h,*line = req->range_hdr;
|
||||||
|
int i,off;
|
||||||
|
|
||||||
|
for (h = line, req->ranges=1; *h != '\n' && *h != '\0'; h++)
|
||||||
|
if (*h == ',')
|
||||||
|
req->ranges++;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: %d ranges:",req->fd,req->ranges);
|
||||||
|
req->r_start = malloc(req->ranges*sizeof(off_t));
|
||||||
|
req->r_end = malloc(req->ranges*sizeof(off_t));
|
||||||
|
req->r_head = malloc((req->ranges+1)*BR_HEADER);
|
||||||
|
req->r_hlen = malloc((req->ranges+1)*sizeof(int));
|
||||||
|
if (NULL == req->r_start || NULL == req->r_end ||
|
||||||
|
NULL == req->r_head || NULL == req->r_hlen) {
|
||||||
|
if (req->r_start) free(req->r_start);
|
||||||
|
if (req->r_end) free(req->r_end);
|
||||||
|
if (req->r_head) free(req->r_head);
|
||||||
|
if (req->r_hlen) free(req->r_hlen);
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"oom\n");
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
for (i = 0, off=0; i < req->ranges; i++) {
|
||||||
|
if (line[off] == '-') {
|
||||||
|
off++;
|
||||||
|
if (!isdigit(line[off]))
|
||||||
|
goto parse_error;
|
||||||
|
req->r_start[i] = req->bst.st_size - parse_off_t(line,&off);
|
||||||
|
req->r_end[i] = req->bst.st_size;
|
||||||
|
} else {
|
||||||
|
if (!isdigit(line[off]))
|
||||||
|
goto parse_error;
|
||||||
|
req->r_start[i] = parse_off_t(line,&off);
|
||||||
|
if (line[off] != '-')
|
||||||
|
goto parse_error;
|
||||||
|
off++;
|
||||||
|
if (isdigit(line[off]))
|
||||||
|
req->r_end[i] = parse_off_t(line,&off) +1;
|
||||||
|
else
|
||||||
|
req->r_end[i] = req->bst.st_size;
|
||||||
|
}
|
||||||
|
off++; /* skip "," */
|
||||||
|
/* ranges ok? */
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr," %d-%d",
|
||||||
|
(int)(req->r_start[i]),
|
||||||
|
(int)(req->r_end[i]));
|
||||||
|
if (req->r_start[i] > req->r_end[i] ||
|
||||||
|
req->r_end[i] > req->bst.st_size)
|
||||||
|
goto parse_error;
|
||||||
|
}
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr," ok\n");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
parse_error:
|
||||||
|
req->ranges = 0;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr," range error\n");
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
unhex(unsigned char c)
|
||||||
|
{
|
||||||
|
if (c < '@')
|
||||||
|
return c - '0';
|
||||||
|
return (c & 0x0f) + 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* handle %hex quoting, also split path / querystring */
|
||||||
|
static void
|
||||||
|
unquote(unsigned char *path, unsigned char *qs, unsigned char *src)
|
||||||
|
{
|
||||||
|
int q;
|
||||||
|
unsigned char *dst;
|
||||||
|
|
||||||
|
q=0;
|
||||||
|
dst = path;
|
||||||
|
while (src[0] != 0) {
|
||||||
|
if (!q && *src == '?') {
|
||||||
|
q = 1;
|
||||||
|
*dst = 0;
|
||||||
|
dst = qs;
|
||||||
|
src++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (q && *src == '+') {
|
||||||
|
*dst = ' ';
|
||||||
|
} else if ((*src == '%') && isxdigit(src[1]) && isxdigit(src[2])) {
|
||||||
|
*dst = (unhex(src[1]) << 4) | unhex(src[2]);
|
||||||
|
src += 2;
|
||||||
|
} else {
|
||||||
|
*dst = *src;
|
||||||
|
}
|
||||||
|
dst++;
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
*dst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete unneeded path elements */
|
||||||
|
static void
|
||||||
|
fixpath(char *path)
|
||||||
|
{
|
||||||
|
char *dst = path;
|
||||||
|
char *src = path;
|
||||||
|
|
||||||
|
for (;*src;) {
|
||||||
|
if (0 == strncmp(src,"//",2)) {
|
||||||
|
src++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strncmp(src,"/./",3)) {
|
||||||
|
src+=2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*(dst++) = *(src++);
|
||||||
|
}
|
||||||
|
*dst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int base64_table[] = {
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||||
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||||
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||||
|
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||||
|
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
decode_base64(unsigned char *dest, unsigned char *src, int maxlen)
|
||||||
|
{
|
||||||
|
int a,b,d;
|
||||||
|
|
||||||
|
for (a=0, b=0, d=0; *src != 0 && d < maxlen; src++) {
|
||||||
|
if (*src >= 128 || -1 == base64_table[*src])
|
||||||
|
break;
|
||||||
|
a = (a<<6) | base64_table[*src];
|
||||||
|
b += 6;
|
||||||
|
if (b >= 8) {
|
||||||
|
b -= 8;
|
||||||
|
dest[d++] = (a >> b) & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest[d] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sanity_checks(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* path: must start with a '/' */
|
||||||
|
if (req->path[0] != '/') {
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* path: must not contain "/../" */
|
||||||
|
if (strstr(req->path,"/../")) {
|
||||||
|
mkerror(req,403,1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->hostname[0] == '\0')
|
||||||
|
/* no hostname specified */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* validate hostname */
|
||||||
|
for (i = 0; req->hostname[i] != '\0'; i++) {
|
||||||
|
switch (req->hostname[i]) {
|
||||||
|
case 'A' ... 'Z':
|
||||||
|
req->hostname[i] += 32; /* lowercase */
|
||||||
|
case 'a' ... 'z':
|
||||||
|
case '0' ... '9':
|
||||||
|
case '-':
|
||||||
|
/* these are fine as-is */
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
/* some extra checks */
|
||||||
|
if (0 == i) {
|
||||||
|
/* don't allow a dot as first character */
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ('.' == req->hostname[i-1]) {
|
||||||
|
/* don't allow two dots in sequence */
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* invalid character */
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
parse_request(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
char filename[MAX_PATH+1], proto[MAX_MISC+1], *h;
|
||||||
|
int port, rc, len;
|
||||||
|
struct passwd *pw=NULL;
|
||||||
|
|
||||||
|
if (debug > 2)
|
||||||
|
fprintf(stderr,"%s\n",req->hreq);
|
||||||
|
|
||||||
|
/* parse request. Hehe, scanf is powerfull :-) */
|
||||||
|
if (4 != sscanf(req->hreq,
|
||||||
|
"%" S(MAX_MISC) "[A-Z] "
|
||||||
|
"%" S(MAX_PATH) "[^ \t\r\n] HTTP/%d.%d",
|
||||||
|
req->type, filename, &(req->major),&(req->minor))) {
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (filename[0] == '/') {
|
||||||
|
strncpy(req->uri,filename,sizeof(req->uri)-1);
|
||||||
|
} else {
|
||||||
|
port = 0;
|
||||||
|
*proto = 0;
|
||||||
|
if (4 != sscanf(filename,
|
||||||
|
"%" S(MAX_MISC) "[a-zA-Z]://"
|
||||||
|
"%" S(MAX_HOST) "[a-zA-Z0-9.-]:%d"
|
||||||
|
"%" S(MAX_PATH) "[^ \t\r\n]",
|
||||||
|
proto, req->hostname, &port, req->uri) &&
|
||||||
|
3 != sscanf(filename,
|
||||||
|
"%" S(MAX_MISC) "[a-zA-Z]://"
|
||||||
|
"%" S(MAX_HOST) "[a-zA-Z0-9.-]"
|
||||||
|
"%" S(MAX_PATH) "[^ \t\r\n]",
|
||||||
|
proto, req->hostname, req->uri)) {
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (*proto != 0 && 0 != strcasecmp(proto,"http")) {
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unquote(req->path,req->query,req->uri);
|
||||||
|
fixpath(req->path);
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: %s \"%s\" HTTP/%d.%d\n",
|
||||||
|
req->fd, req->type, req->path, req->major, req->minor);
|
||||||
|
|
||||||
|
if (0 != strcmp(req->type,"GET") &&
|
||||||
|
0 != strcmp(req->type,"HEAD")) {
|
||||||
|
mkerror(req,501,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == strcmp(req->type,"HEAD")) {
|
||||||
|
req->head_only = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse header lines */
|
||||||
|
req->keep_alive = req->minor;
|
||||||
|
for (h = req->hreq; h - req->hreq < req->lreq;) {
|
||||||
|
h = strchr(h,'\n');
|
||||||
|
if (NULL == h)
|
||||||
|
break;
|
||||||
|
h++;
|
||||||
|
|
||||||
|
h[-2] = 0;
|
||||||
|
h[-1] = 0;
|
||||||
|
list_add(&req->header,h,0);
|
||||||
|
|
||||||
|
if (0 == strncasecmp(h,"Connection: ",12)) {
|
||||||
|
req->keep_alive = (0 == strncasecmp(h+12,"Keep-Alive",10));
|
||||||
|
|
||||||
|
} else if (0 == strncasecmp(h,"Host: ",6)) {
|
||||||
|
if (2 != sscanf(h+6,"%" S(MAX_HOST) "[a-zA-Z0-9.-]:%d",
|
||||||
|
req->hostname,&port))
|
||||||
|
sscanf(h+6,"%" S(MAX_HOST) "[a-zA-Z0-9.-]",
|
||||||
|
req->hostname);
|
||||||
|
|
||||||
|
} else if (0 == strncasecmp(h,"If-Modified-Since: ",19)) {
|
||||||
|
req->if_modified = h+19;
|
||||||
|
|
||||||
|
} else if (0 == strncasecmp(h,"If-Unmodified-Since: ",21)) {
|
||||||
|
req->if_unmodified = h+21;
|
||||||
|
|
||||||
|
} else if (0 == strncasecmp(h,"If-Range: ",10)) {
|
||||||
|
req->if_range = h+10;
|
||||||
|
|
||||||
|
} else if (0 == strncasecmp(h,"Authorization: Basic ",21)) {
|
||||||
|
decode_base64(req->auth,h+21,sizeof(req->auth)-1);
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: auth: %s\n",req->fd,req->auth);
|
||||||
|
|
||||||
|
} else if (0 == strncasecmp(h,"Range: bytes=",13)) {
|
||||||
|
/* parsing must be done after fstat, we need the file size
|
||||||
|
for the boundary checks */
|
||||||
|
req->range_hdr = h+13;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (debug) {
|
||||||
|
if (req->if_modified)
|
||||||
|
fprintf(stderr,"%03d: if-modified-since: \"%s\"\n",
|
||||||
|
req->fd, req->if_modified);
|
||||||
|
if (req->if_unmodified)
|
||||||
|
fprintf(stderr,"%03d: if-unmodified-since: \"%s\"\n",
|
||||||
|
req->fd, req->if_unmodified);
|
||||||
|
if (req->if_range)
|
||||||
|
fprintf(stderr,"%03d: if-range: \"%s\"\n",
|
||||||
|
req->fd, req->if_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* take care about the hostname */
|
||||||
|
if (virtualhosts) {
|
||||||
|
if (req->hostname[0] == 0) {
|
||||||
|
if (req->minor > 0) {
|
||||||
|
/* HTTP/1.1 clients MUST specify a hostname */
|
||||||
|
mkerror(req,400,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strncpy(req->hostname,server_host,sizeof(req->hostname)-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (req->hostname[0] == '\0' || canonicalhost)
|
||||||
|
strncpy(req->hostname,server_host,sizeof(req->hostname)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* checks */
|
||||||
|
if (0 != sanity_checks(req))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* check basic auth */
|
||||||
|
if (NULL != userpass && 0 != strcmp(userpass,req->auth)) {
|
||||||
|
mkerror(req,401,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is CGI ? */
|
||||||
|
if (NULL != cgipath &&
|
||||||
|
0 == strncmp(req->path,cgipath,strlen(cgipath))) {
|
||||||
|
cgi_request(req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build filename */
|
||||||
|
if (userdir && '~' == req->path[1]) {
|
||||||
|
/* expand user directories, i.e.
|
||||||
|
/~user/path/file => $HOME/public_html/path/file */
|
||||||
|
h = strchr(req->path+2,'/');
|
||||||
|
if (NULL == h) {
|
||||||
|
mkerror(req,404,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*h = 0;
|
||||||
|
pw = getpwnam(req->path+2);
|
||||||
|
*h = '/';
|
||||||
|
if (NULL == pw) {
|
||||||
|
mkerror(req,404,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
len = snprintf(filename, sizeof(filename)-1,
|
||||||
|
"%s/%s/%s", pw->pw_dir, userdir, h+1);
|
||||||
|
} else {
|
||||||
|
len = snprintf(filename, sizeof(filename)-1,
|
||||||
|
"%s%s%s%s",
|
||||||
|
do_chroot ? "" : doc_root,
|
||||||
|
virtualhosts ? "/" : "",
|
||||||
|
virtualhosts ? req->hostname : "",
|
||||||
|
req->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
h = filename +len -1;
|
||||||
|
if (*h == '/') {
|
||||||
|
/* looks like the client asks for a directory */
|
||||||
|
if (indexhtml) {
|
||||||
|
/* check for index file */
|
||||||
|
strncpy(h+1, indexhtml, sizeof(filename) -len -1);
|
||||||
|
if (-1 != (req->bfd = open(filename,O_RDONLY))) {
|
||||||
|
/* ok, we have one */
|
||||||
|
close_on_exec(req->bfd);
|
||||||
|
goto regular_file;
|
||||||
|
} else {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
/* no such file or directory => listing */
|
||||||
|
h[1] = '\0';
|
||||||
|
} else {
|
||||||
|
mkerror(req,403,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (no_listing) {
|
||||||
|
mkerror(req,403,1);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (-1 == stat(filename,&(req->bst))) {
|
||||||
|
if (errno == EACCES) {
|
||||||
|
mkerror(req,403,1);
|
||||||
|
} else {
|
||||||
|
mkerror(req,404,1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strftime(req->mtime, sizeof(req->mtime), RFC1123, gmtime(&req->bst.st_mtime));
|
||||||
|
req->mime = "text/html";
|
||||||
|
req->dir = get_dir(req,filename);
|
||||||
|
if (NULL == req->body) {
|
||||||
|
/* We arrive here if opendir failed, probably due to -EPERM
|
||||||
|
* It does exist (see the stat() call above) */
|
||||||
|
mkerror(req,403,1);
|
||||||
|
return;
|
||||||
|
} else if (NULL != req->if_modified &&
|
||||||
|
0 == strcmp(req->if_modified, req->mtime)) {
|
||||||
|
/* 304 not modified */
|
||||||
|
mkheader(req,304);
|
||||||
|
req->head_only = 1;
|
||||||
|
} else {
|
||||||
|
/* 200 OK */
|
||||||
|
mkheader(req,200);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* it is /probably/ a regular file */
|
||||||
|
if (-1 == (req->bfd = open(filename,O_RDONLY))) {
|
||||||
|
if (errno == EACCES) {
|
||||||
|
mkerror(req,403,1);
|
||||||
|
} else {
|
||||||
|
mkerror(req,404,1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regular_file:
|
||||||
|
fstat(req->bfd,&(req->bst));
|
||||||
|
if (req->range_hdr)
|
||||||
|
if (0 != (rc = parse_ranges(req))) {
|
||||||
|
mkerror(req,rc,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISREG(req->bst.st_mode)) {
|
||||||
|
/* /not/ a regular file */
|
||||||
|
close(req->bfd);
|
||||||
|
req->bfd = -1;
|
||||||
|
if (S_ISDIR(req->bst.st_mode)) {
|
||||||
|
/* oops: a directory without trailing slash */
|
||||||
|
strcat(req->path,"/");
|
||||||
|
mkredirect(req);
|
||||||
|
} else {
|
||||||
|
/* anything else is'nt allowed here */
|
||||||
|
mkerror(req,403,1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* it is /really/ a regular file */
|
||||||
|
req->mime = get_mime(filename);
|
||||||
|
strftime(req->mtime, sizeof(req->mtime), RFC1123, gmtime(&req->bst.st_mtime));
|
||||||
|
if (NULL != req->if_range && 0 != strcmp(req->if_range, req->mtime))
|
||||||
|
/* mtime mismatch -> no ranges */
|
||||||
|
req->ranges = 0;
|
||||||
|
if (NULL != req->if_unmodified && 0 != strcmp(req->if_unmodified, req->mtime)) {
|
||||||
|
/* 412 precondition failed */
|
||||||
|
mkerror(req,412,1);
|
||||||
|
} else if (NULL != req->if_modified && 0 == strcmp(req->if_modified, req->mtime)) {
|
||||||
|
/* 304 not modified */
|
||||||
|
mkheader(req,304);
|
||||||
|
req->head_only = 1;
|
||||||
|
} else if (req->ranges > 0) {
|
||||||
|
/* send byte range(s) */
|
||||||
|
mkheader(req,206);
|
||||||
|
} else {
|
||||||
|
/* normal */
|
||||||
|
mkheader(req,200);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
@ -0,0 +1,556 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
|
#include "httpd.h"
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
/* os-specific sendfile() wrapper */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* int xsendfile(out,in,offset,bytes)
|
||||||
|
*
|
||||||
|
* out - outgoing filedescriptor (i.e. the socket)
|
||||||
|
* in - incoming filedescriptor (i.e. the file to send out)
|
||||||
|
* offset - file offset (where to start)
|
||||||
|
* bytes - number of bytes to send
|
||||||
|
*
|
||||||
|
* return value
|
||||||
|
* on error: -1 and errno set.
|
||||||
|
* on success: the number of successfully written bytes (which might
|
||||||
|
* be smaller than bytes, we are doing nonblocking I/O).
|
||||||
|
* extra hint: much like write(2) works.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline size_t off_to_size(off_t off_bytes)
|
||||||
|
{
|
||||||
|
if (off_bytes > SSIZE_MAX)
|
||||||
|
return SSIZE_MAX;
|
||||||
|
return off_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__) && !defined(NO_SENDFILE)
|
||||||
|
|
||||||
|
# include <sys/sendfile.h>
|
||||||
|
static ssize_t xsendfile(int out, int in, off_t offset, off_t off_bytes)
|
||||||
|
{
|
||||||
|
size_t bytes = off_to_size(off_bytes);
|
||||||
|
return sendfile(out, in, &offset, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__FreeBSD__) && !defined(NO_SENDFILE)
|
||||||
|
|
||||||
|
static ssize_t xsendfile(int out, int in, off_t offset, off_t off_bytes)
|
||||||
|
{
|
||||||
|
size_t bytes = off_to_size(off_bytes);
|
||||||
|
off_t nbytes = 0;
|
||||||
|
|
||||||
|
if (-1 == sendfile(in, out, offset, bytes, NULL, &nbytes, 0)) {
|
||||||
|
/* Why the heck FreeBSD returns an /error/ if it has done a partial
|
||||||
|
write? With non-blocking I/O this absolutely normal behavoir and
|
||||||
|
no error at all. Stupid. */
|
||||||
|
if (errno == EAGAIN && nbytes > 0)
|
||||||
|
return nbytes;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return nbytes;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
# warning using slow sendfile() emulation.
|
||||||
|
|
||||||
|
/* Poor man's sendfile() implementation. Performance sucks, but it works. */
|
||||||
|
# define BUFSIZE 16384
|
||||||
|
|
||||||
|
static ssize_t xsendfile(int out, int in, off_t offset, off_t off_bytes)
|
||||||
|
{
|
||||||
|
char buf[BUFSIZE];
|
||||||
|
ssize_t nread;
|
||||||
|
ssize_t nsent, nsent_total;
|
||||||
|
size_t bytes = off_to_size(off_bytes);
|
||||||
|
|
||||||
|
if (lseek(in, offset, SEEK_SET) == -1) {
|
||||||
|
if (debug)
|
||||||
|
perror("lseek");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsent = nsent_total = 0;
|
||||||
|
for (;bytes > 0;) {
|
||||||
|
/* read a block */
|
||||||
|
nread = read(in, buf, (bytes < BUFSIZE) ? bytes : BUFSIZE);
|
||||||
|
if (-1 == nread) {
|
||||||
|
if (debug)
|
||||||
|
perror("read");
|
||||||
|
return nsent_total ? nsent_total : -1;
|
||||||
|
}
|
||||||
|
if (0 == nread)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* write it out */
|
||||||
|
nsent = write(out, buf, nread);
|
||||||
|
if (-1 == nsent)
|
||||||
|
return nsent_total ? nsent_total : -1;
|
||||||
|
|
||||||
|
nsent_total += nsent;
|
||||||
|
if (nsent < nread)
|
||||||
|
/* that was a partial write only. Queue full. Bailout here,
|
||||||
|
the next write would return EAGAIN anyway... */
|
||||||
|
break;
|
||||||
|
|
||||||
|
bytes -= nread;
|
||||||
|
}
|
||||||
|
return nsent_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
|
||||||
|
static inline int wrap_xsendfile(struct REQUEST *req, off_t off, off_t bytes)
|
||||||
|
{
|
||||||
|
if (with_ssl)
|
||||||
|
return ssl_blk_write(req, off, off_to_size(bytes));
|
||||||
|
else
|
||||||
|
return xsendfile(req->fd, req->bfd, off, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int wrap_write(struct REQUEST *req, void *buf, off_t bytes)
|
||||||
|
{
|
||||||
|
if (with_ssl)
|
||||||
|
return ssl_write(req, buf, off_to_size(bytes));
|
||||||
|
else
|
||||||
|
return write(req->fd, buf, off_to_size(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
# define wrap_xsendfile(req,off,bytes) xsendfile(req->fd,req->bfd,off,bytes)
|
||||||
|
# define wrap_write(req,buf,bytes) write(req->fd,buf,bytes);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static struct HTTP_STATUS {
|
||||||
|
int status;
|
||||||
|
char *head;
|
||||||
|
char *body;
|
||||||
|
} http[] = {
|
||||||
|
{ 200, "200 OK", NULL },
|
||||||
|
{ 206, "206 Partial Content", NULL },
|
||||||
|
{ 304, "304 Not Modified", NULL },
|
||||||
|
{ 400, "400 Bad Request", "*PLONK*\n" },
|
||||||
|
{ 401, "401 Authentication required", "Authentication required\n" },
|
||||||
|
{ 403, "403 Forbidden", "Access denied\n" },
|
||||||
|
{ 404, "404 Not Found", "File or directory not found\n" },
|
||||||
|
{ 408, "408 Request Timeout", "Request Timeout\n" },
|
||||||
|
{ 412, "412 Precondition failed.", "Precondition failed\n" },
|
||||||
|
{ 500, "500 Internal Server Error", "Sorry folks\n" },
|
||||||
|
{ 501, "501 Not Implemented", "Sorry folks\n" },
|
||||||
|
{ 0, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#define RESPONSE_START \
|
||||||
|
"HTTP/1.1 %s\r\n" \
|
||||||
|
"Server: %s\r\n" \
|
||||||
|
"Connection: %s\r\n" \
|
||||||
|
"Accept-Ranges: bytes\r\n"
|
||||||
|
#define BOUNDARY \
|
||||||
|
"XXX_CUT_HERE_%ld_XXX"
|
||||||
|
|
||||||
|
void
|
||||||
|
mkerror(struct REQUEST *req, int status, int ka)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; http[i].status != 0; i++)
|
||||||
|
if (http[i].status == status)
|
||||||
|
break;
|
||||||
|
req->status = status;
|
||||||
|
req->body = http[i].body;
|
||||||
|
req->lbody = strlen(req->body);
|
||||||
|
if (!ka)
|
||||||
|
req->keep_alive = 0;
|
||||||
|
req->lres = sprintf(req->hres,
|
||||||
|
RESPONSE_START
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Content-Length: %" PRId64 "\r\n",
|
||||||
|
http[i].head,server_name,
|
||||||
|
req->keep_alive ? "Keep-Alive" : "Close",
|
||||||
|
(int64_t)req->lbody);
|
||||||
|
if (401 == status)
|
||||||
|
req->lres += sprintf(req->hres+req->lres,
|
||||||
|
"WWW-Authenticate: Basic realm=\"webfs\"\r\n");
|
||||||
|
req->lres += strftime(req->hres+req->lres,80,
|
||||||
|
"Date: " RFC1123 "\r\n\r\n",
|
||||||
|
gmtime(&now));
|
||||||
|
req->state = STATE_WRITE_HEADER;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: error: %d, connection=%s\n",
|
||||||
|
req->fd, status, req->keep_alive ? "Keep-Alive" : "Close");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mkredirect(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
req->status = 302;
|
||||||
|
req->body = req->path;
|
||||||
|
req->lbody = strlen(req->body);
|
||||||
|
req->lres = sprintf(req->hres,
|
||||||
|
RESPONSE_START
|
||||||
|
"Location: http://%s:%d%s\r\n"
|
||||||
|
"Content-Type: text/plain\r\n"
|
||||||
|
"Content-Length: %" PRId64 "\r\n",
|
||||||
|
"302 Redirect",server_name,
|
||||||
|
req->keep_alive ? "Keep-Alive" : "Close",
|
||||||
|
req->hostname,tcp_port,quote(req->path,9999),
|
||||||
|
(int64_t)req->lbody);
|
||||||
|
req->lres += strftime(req->hres+req->lres,80,
|
||||||
|
"Date: " RFC1123 "\r\n\r\n",
|
||||||
|
gmtime(&now));
|
||||||
|
req->state = STATE_WRITE_HEADER;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: 302 redirect: %s, connection=%s\n",
|
||||||
|
req->fd, req->path, req->keep_alive ? "Keep-Alive" : "Close");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mkmulti(struct REQUEST *req, int i)
|
||||||
|
{
|
||||||
|
req->r_hlen[i] = sprintf(req->r_head+i*BR_HEADER,
|
||||||
|
"\r\n--" BOUNDARY "\r\n"
|
||||||
|
"Content-type: %s\r\n"
|
||||||
|
"Content-range: bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n"
|
||||||
|
"\r\n",
|
||||||
|
now, req->mime,
|
||||||
|
(int64_t)req->r_start[i],
|
||||||
|
(int64_t)req->r_end[i]-1,
|
||||||
|
(int64_t)req->bst.st_size);
|
||||||
|
if (debug > 1)
|
||||||
|
fprintf(stderr,"%03d: send range: %" PRId64 "-%" PRId64 "/%" PRId64 " (%" PRId64 " byte)\n",
|
||||||
|
req->fd,
|
||||||
|
(int64_t)req->r_start[i],
|
||||||
|
(int64_t)req->r_end[i],
|
||||||
|
(int64_t)req->bst.st_size,
|
||||||
|
(int64_t)(req->r_end[i]-req->r_start[i]));
|
||||||
|
return req->r_hlen[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mkheader(struct REQUEST *req, int status)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
off_t len;
|
||||||
|
time_t expires;
|
||||||
|
|
||||||
|
for (i = 0; http[i].status != 0; i++)
|
||||||
|
if (http[i].status == status)
|
||||||
|
break;
|
||||||
|
req->status = status;
|
||||||
|
req->lres = sprintf(req->hres,
|
||||||
|
RESPONSE_START,
|
||||||
|
http[i].head,server_name,
|
||||||
|
req->keep_alive ? "Keep-Alive" : "Close");
|
||||||
|
if (req->ranges == 0) {
|
||||||
|
req->lres += sprintf(req->hres+req->lres,
|
||||||
|
"Content-Type: %s\r\n"
|
||||||
|
"Content-Length: %" PRId64 "\r\n",
|
||||||
|
req->mime,
|
||||||
|
(int64_t)(req->body ? req->lbody : req->bst.st_size));
|
||||||
|
} else if (req->ranges == 1) {
|
||||||
|
req->lres += sprintf(req->hres+req->lres,
|
||||||
|
"Content-Type: %s\r\n"
|
||||||
|
"Content-Range: bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n"
|
||||||
|
"Content-Length: %" PRId64 "\r\n",
|
||||||
|
req->mime,
|
||||||
|
(int64_t)req->r_start[0],
|
||||||
|
(int64_t)req->r_end[0]-1,
|
||||||
|
(int64_t)req->bst.st_size,
|
||||||
|
(int64_t)(req->r_end[0]-req->r_start[0]));
|
||||||
|
} else {
|
||||||
|
for (i = 0, len = 0; i < req->ranges; i++) {
|
||||||
|
len += mkmulti(req,i);
|
||||||
|
len += req->r_end[i]-req->r_start[i];
|
||||||
|
}
|
||||||
|
req->r_hlen[i] = sprintf(req->r_head+i*BR_HEADER,
|
||||||
|
"\r\n--" BOUNDARY "--\r\n",
|
||||||
|
now);
|
||||||
|
len += req->r_hlen[i];
|
||||||
|
req->lres += sprintf(req->hres+req->lres,
|
||||||
|
"Content-Type: multipart/byteranges;"
|
||||||
|
" boundary=" BOUNDARY "\r\n"
|
||||||
|
"Content-Length: %" PRId64 "\r\n",
|
||||||
|
now, (int64_t)len);
|
||||||
|
}
|
||||||
|
if (req->mtime[0] != '\0') {
|
||||||
|
req->lres += sprintf(req->hres+req->lres,
|
||||||
|
"Last-Modified: %s\r\n",
|
||||||
|
req->mtime);
|
||||||
|
if (-1 != lifespan) {
|
||||||
|
expires = req->bst.st_mtime + lifespan;
|
||||||
|
req->lres += strftime(req->hres+req->lres,80,
|
||||||
|
"Expires: " RFC1123 "\r\n",
|
||||||
|
gmtime(&expires));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req->lres += strftime(req->hres+req->lres,80,
|
||||||
|
"Date: " RFC1123 "\r\n\r\n",
|
||||||
|
gmtime(&now));
|
||||||
|
req->state = STATE_WRITE_HEADER;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: %d, connection=%s\n",
|
||||||
|
req->fd, status, req->keep_alive ? "Keep-Alive" : "Close");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mkcgi(struct REQUEST *req, char *status, struct strlist *header)
|
||||||
|
{
|
||||||
|
req->status = atoi(status);
|
||||||
|
req->keep_alive = 0;
|
||||||
|
req->lres = sprintf(req->hres,
|
||||||
|
RESPONSE_START,
|
||||||
|
status, server_name,"Close");
|
||||||
|
for (; NULL != header; header = header->next)
|
||||||
|
req->lres += sprintf(req->hres+req->lres,"%s\r\n",header->line);
|
||||||
|
req->lres += strftime(req->hres+req->lres,80,
|
||||||
|
"Date: " RFC1123 "\r\n\r\n",
|
||||||
|
gmtime(&now));
|
||||||
|
req->state = STATE_WRITE_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
void write_request(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
switch (req->state) {
|
||||||
|
case STATE_WRITE_HEADER:
|
||||||
|
#ifdef TCP_CORK
|
||||||
|
if (0 == req->tcp_cork && !req->head_only) {
|
||||||
|
req->tcp_cork = 1;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: tcp_cork=%d\n",req->fd,req->tcp_cork);
|
||||||
|
setsockopt(req->fd,SOL_TCP,TCP_CORK,&req->tcp_cork,sizeof(int));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
rc = wrap_write(req,req->hres + req->written,
|
||||||
|
req->lres - req->written);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
xperror(LOG_INFO,"write",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
req->written += rc;
|
||||||
|
req->bc += rc;
|
||||||
|
if (req->written != req->lres)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req->written = 0;
|
||||||
|
if (req->head_only) {
|
||||||
|
req->state = STATE_FINISHED;
|
||||||
|
return;
|
||||||
|
} else if (req->cgipid) {
|
||||||
|
req->state = (req->cgipos != req->cgilen) ?
|
||||||
|
STATE_CGI_BODY_OUT : STATE_CGI_BODY_IN;
|
||||||
|
} else if (req->body) {
|
||||||
|
req->state = STATE_WRITE_BODY;
|
||||||
|
} else if (req->ranges == 1) {
|
||||||
|
req->state = STATE_WRITE_RANGES;
|
||||||
|
req->rh = -1;
|
||||||
|
req->rb = 0;
|
||||||
|
req->written = req->r_start[0];
|
||||||
|
} else if (req->ranges > 1) {
|
||||||
|
req->state = STATE_WRITE_RANGES;
|
||||||
|
req->rh = 0;
|
||||||
|
req->rb = -1;
|
||||||
|
} else {
|
||||||
|
req->state = STATE_WRITE_FILE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_WRITE_BODY:
|
||||||
|
rc = wrap_write(req,req->body + req->written,
|
||||||
|
req->lbody - req->written);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
xperror(LOG_INFO,"write",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
req->written += rc;
|
||||||
|
req->bc += rc;
|
||||||
|
if (req->written != req->lbody)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req->state = STATE_FINISHED;
|
||||||
|
return;
|
||||||
|
case STATE_WRITE_FILE:
|
||||||
|
rc = wrap_xsendfile(req, req->written,
|
||||||
|
req->bst.st_size - req->written);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
xperror(LOG_INFO,"sendfile",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
if (debug > 1)
|
||||||
|
fprintf(stderr,"%03d: %" PRId64 "/%" PRId64 " (%d%%)\r",req->fd,
|
||||||
|
(int64_t)req->written,(int64_t)req->bst.st_size,
|
||||||
|
(int)(req->written*100/req->bst.st_size));
|
||||||
|
req->written += rc;
|
||||||
|
req->bc += rc;
|
||||||
|
if (req->written != req->bst.st_size)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req->state = STATE_FINISHED;
|
||||||
|
return;
|
||||||
|
case STATE_WRITE_RANGES:
|
||||||
|
if (-1 != req->rh) {
|
||||||
|
/* write header */
|
||||||
|
rc = wrap_write(req,
|
||||||
|
req->r_head + req->rh*BR_HEADER + req->written,
|
||||||
|
req->r_hlen[req->rh] - req->written);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
xperror(LOG_INFO,"write",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
req->written += rc;
|
||||||
|
req->bc += rc;
|
||||||
|
if (req->written != req->r_hlen[req->rh])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req->rh == req->ranges) {
|
||||||
|
/* done -- no more ranges */
|
||||||
|
req->state = STATE_FINISHED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* prepare for body writeout */
|
||||||
|
req->rb = req->rh;
|
||||||
|
req->rh = -1;
|
||||||
|
req->written = req->r_start[req->rb];
|
||||||
|
}
|
||||||
|
if (-1 != req->rb) {
|
||||||
|
/* write body */
|
||||||
|
rc = wrap_xsendfile(req, req->written,
|
||||||
|
req->r_end[req->rb] - req->written);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
xperror(LOG_INFO,"sendfile",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
req->written += rc;
|
||||||
|
req->bc += rc;
|
||||||
|
if (req->written != req->r_end[req->rb])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* prepare for next subheader writeout */
|
||||||
|
req->rh = req->rb+1;
|
||||||
|
req->rb = -1;
|
||||||
|
req->written = 0;
|
||||||
|
if (req->ranges == 1) {
|
||||||
|
/* single range only */
|
||||||
|
req->state = STATE_FINISHED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_CGI_BODY_IN:
|
||||||
|
rc = read(req->cgipipe, req->cgibuf, MAX_HEADER);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
xperror(LOG_INFO,"cgi read",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_FINISHED;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: cgi: in %d\n",req->fd,rc);
|
||||||
|
req->cgipos = 0;
|
||||||
|
req->cgilen = rc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
req->state = STATE_CGI_BODY_OUT;
|
||||||
|
break;
|
||||||
|
case STATE_CGI_BODY_OUT:
|
||||||
|
rc = wrap_write(req,req->cgibuf + req->cgipos,
|
||||||
|
req->cgilen - req->cgipos);
|
||||||
|
switch (rc) {
|
||||||
|
case -1:
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return;
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
xperror(LOG_INFO,"write",req->peerhost);
|
||||||
|
/* fall through */
|
||||||
|
case 0:
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: cgi: out %d\n",req->fd,rc);
|
||||||
|
req->cgipos += rc;
|
||||||
|
req->bc += rc;
|
||||||
|
if (req->cgipos != req->cgilen)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req->state = STATE_CGI_BODY_IN;
|
||||||
|
break;
|
||||||
|
} /* switch(state) */
|
||||||
|
} /* for (;;) */
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "httpd.h"
|
||||||
|
|
||||||
|
#ifdef USE_THREADS
|
||||||
|
static pthread_mutex_t lock_ssl = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ssl_read(struct REQUEST *req, char *buf, int len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
ERR_clear_error();
|
||||||
|
rc = SSL_read(req->ssl_s, buf, len);
|
||||||
|
if (rc < 0 && SSL_get_error(req->ssl_s, rc) == SSL_ERROR_WANT_READ) {
|
||||||
|
errno = EAGAIN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
unsigned long err;
|
||||||
|
while (0 != (err = ERR_get_error()))
|
||||||
|
fprintf(stderr, "%03d: ssl read error: %s\n", req->fd,
|
||||||
|
ERR_error_string(err, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ssl_write(struct REQUEST *req, char *buf, int len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
ERR_clear_error();
|
||||||
|
rc = SSL_write(req->ssl_s, buf, len);
|
||||||
|
if (rc < 0 && SSL_get_error(req->ssl_s, rc) == SSL_ERROR_WANT_WRITE) {
|
||||||
|
errno = EAGAIN;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
unsigned long err;
|
||||||
|
while (0 != (err = ERR_get_error()))
|
||||||
|
fprintf(stderr, "%03d: ssl read error: %s\n", req->fd,
|
||||||
|
ERR_error_string(err, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
errno = EIO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ssl_blk_write(struct REQUEST *req, int offset, int len)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char buf[4096];
|
||||||
|
|
||||||
|
if (lseek(req->bfd, offset, SEEK_SET) == -1) {
|
||||||
|
if (debug)
|
||||||
|
perror("lseek");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > sizeof(buf))
|
||||||
|
len = sizeof(buf);
|
||||||
|
rc = read(req->bfd, buf, len);
|
||||||
|
if (rc <= 0) {
|
||||||
|
/* shouldn't happen ... */
|
||||||
|
req->state = STATE_CLOSE;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
return ssl_write(req, buf, rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int password_cb(char *buf, int num, int rwflag, void *userdata)
|
||||||
|
{
|
||||||
|
if (NULL == password)
|
||||||
|
return 0;
|
||||||
|
if (num < strlen(password)+1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
strcpy(buf,password);
|
||||||
|
return(strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_ssl(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
OpenSSL_add_all_algorithms();
|
||||||
|
SSL_load_error_strings();
|
||||||
|
SSL_library_init();
|
||||||
|
ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
|
if (NULL == ctx) {
|
||||||
|
fprintf(stderr, "SSL init error [%s]",strerror(errno));
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = SSL_CTX_use_certificate_chain_file(ctx, certificate);
|
||||||
|
switch (rc) {
|
||||||
|
case 1:
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "SSL certificate load ok\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "SSL cert load error [%s]\n",
|
||||||
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_default_passwd_cb(ctx, password_cb);
|
||||||
|
SSL_CTX_use_PrivateKey_file(ctx, certificate, SSL_FILETYPE_PEM);
|
||||||
|
switch (rc) {
|
||||||
|
case 1:
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "SSL private key load ok\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "SSL privkey load error [%s]\n",
|
||||||
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_ssl_session(struct REQUEST *req)
|
||||||
|
{
|
||||||
|
DO_LOCK(lock_ssl);
|
||||||
|
req->ssl_s = SSL_new(ctx);
|
||||||
|
if (req->ssl_s == NULL) {
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr,"%03d: SSL session init error [%s]\n",
|
||||||
|
req->fd, strerror(errno));
|
||||||
|
/* FIXME: how to handle that one? */
|
||||||
|
}
|
||||||
|
SSL_set_fd(req->ssl_s, req->fd);
|
||||||
|
SSL_set_accept_state(req->ssl_s);
|
||||||
|
SSL_set_read_ahead(req->ssl_s, 0); /* to prevent unwanted buffering in ssl layer */
|
||||||
|
DO_UNLOCK(lock_ssl);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
just for testing purposes -- two certificates:
|
||||||
|
|
||||||
|
server.pem
|
||||||
|
ssl server certificate + private key. You can use that one
|
||||||
|
as certificate file for webfsd.
|
||||||
|
|
||||||
|
root.pem
|
||||||
|
self-signed root ca certificate, this one was used to sign
|
||||||
|
the server certificate.
|
||||||
|
|
@ -0,0 +1,59 @@
|
|||||||
|
Certificate:
|
||||||
|
Data:
|
||||||
|
Version: 3 (0x2)
|
||||||
|
Serial Number: 0 (0x0)
|
||||||
|
Signature Algorithm: md5WithRSAEncryption
|
||||||
|
Issuer: C=DE, ST=Germany, L=Berlin, O=World domination. Fast., CN=bytesex.org mini ca/Email=kraxel@bytesex.org
|
||||||
|
Validity
|
||||||
|
Not Before: Jul 27 13:21:29 2002 GMT
|
||||||
|
Not After : Jul 26 13:21:29 2007 GMT
|
||||||
|
Subject: C=DE, ST=Germany, L=Berlin, O=World domination. Fast., CN=bytesex.org mini ca/Email=kraxel@bytesex.org
|
||||||
|
Subject Public Key Info:
|
||||||
|
Public Key Algorithm: rsaEncryption
|
||||||
|
RSA Public Key: (1024 bit)
|
||||||
|
Modulus (1024 bit):
|
||||||
|
00:c4:f7:a1:38:df:c9:ef:45:da:39:d1:28:ea:da:
|
||||||
|
6f:09:33:e0:be:90:c9:51:47:6c:af:1e:28:92:f5:
|
||||||
|
48:f8:8a:ee:62:cd:58:7e:4e:5e:89:65:ba:12:e8:
|
||||||
|
40:aa:a7:8b:0c:d2:b9:b7:56:b7:39:80:9c:32:84:
|
||||||
|
44:3f:6d:71:cb:33:53:fc:f6:4c:c3:3e:08:78:81:
|
||||||
|
e6:9c:11:8a:39:98:11:d2:39:1c:00:cb:37:11:a9:
|
||||||
|
05:73:b1:8c:79:80:03:8e:74:b5:98:6f:66:0b:bb:
|
||||||
|
c8:27:c9:0e:a8:fb:41:99:8e:3f:06:a3:a7:52:06:
|
||||||
|
91:85:3b:20:a3:00:ad:4e:05
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
X509v3 extensions:
|
||||||
|
X509v3 Basic Constraints:
|
||||||
|
CA:TRUE
|
||||||
|
Netscape Cert Type:
|
||||||
|
SSL CA, S/MIME CA, Object Signing CA
|
||||||
|
Netscape Comment:
|
||||||
|
This is a *TEST* root CA, everything gets signed.
|
||||||
|
Signature Algorithm: md5WithRSAEncryption
|
||||||
|
14:dd:be:57:94:bb:14:80:22:3b:44:7f:ac:b2:1c:4c:0f:60:
|
||||||
|
3d:5e:a7:89:27:1c:69:c7:a1:8d:bb:9d:cd:a4:c4:a2:11:6c:
|
||||||
|
ce:a0:1e:c4:f8:1e:6b:28:a4:b1:ee:4d:5d:36:7e:e1:92:58:
|
||||||
|
e1:39:0a:23:df:40:0a:b7:04:ba:b4:09:3c:f6:47:a0:14:21:
|
||||||
|
e3:6e:42:10:89:04:98:8c:14:93:ad:cd:60:e2:98:c6:46:27:
|
||||||
|
0f:f5:ac:7d:42:5f:71:6f:4e:15:95:be:38:20:f9:76:be:9b:
|
||||||
|
9a:8f:f1:8b:a6:20:5e:c4:76:f1:b2:a0:94:cf:43:0f:b4:a6:
|
||||||
|
07:e1
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDAzCCAmygAwIBAgIBADANBgkqhkiG9w0BAQQFADCBkzELMAkGA1UEBhMCREUx
|
||||||
|
EDAOBgNVBAgTB0dlcm1hbnkxDzANBgNVBAcTBkJlcmxpbjEgMB4GA1UEChMXV29y
|
||||||
|
bGQgZG9taW5hdGlvbi4gRmFzdC4xHDAaBgNVBAMTE2J5dGVzZXgub3JnIG1pbmkg
|
||||||
|
Y2ExITAfBgkqhkiG9w0BCQEWEmtyYXhlbEBieXRlc2V4Lm9yZzAeFw0wMjA3Mjcx
|
||||||
|
MzIxMjlaFw0wNzA3MjYxMzIxMjlaMIGTMQswCQYDVQQGEwJERTEQMA4GA1UECBMH
|
||||||
|
R2VybWFueTEPMA0GA1UEBxMGQmVybGluMSAwHgYDVQQKExdXb3JsZCBkb21pbmF0
|
||||||
|
aW9uLiBGYXN0LjEcMBoGA1UEAxMTYnl0ZXNleC5vcmcgbWluaSBjYTEhMB8GCSqG
|
||||||
|
SIb3DQEJARYSa3JheGVsQGJ5dGVzZXgub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GN
|
||||||
|
ADCBiQKBgQDE96E438nvRdo50Sjq2m8JM+C+kMlRR2yvHiiS9Uj4iu5izVh+Tl6J
|
||||||
|
ZboS6ECqp4sM0rm3Vrc5gJwyhEQ/bXHLM1P89kzDPgh4geacEYo5mBHSORwAyzcR
|
||||||
|
qQVzsYx5gAOOdLWYb2YLu8gnyQ6o+0GZjj8Go6dSBpGFOyCjAK1OBQIDAQABo2Uw
|
||||||
|
YzAMBgNVHRMEBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIABzBABglghkgBhvhCAQ0E
|
||||||
|
MxYxVGhpcyBpcyBhICpURVNUKiByb290IENBLCBldmVyeXRoaW5nIGdldHMgc2ln
|
||||||
|
bmVkLjANBgkqhkiG9w0BAQQFAAOBgQAU3b5XlLsUgCI7RH+sshxMD2A9XqeJJxxp
|
||||||
|
x6GNu53NpMSiEWzOoB7E+B5rKKSx7k1dNn7hkljhOQoj30AKtwS6tAk89kegFCHj
|
||||||
|
bkIQiQSYjBSTrc1g4pjGRicP9ax9Ql9xb04Vlb44IPl2vpuaj/GLpiBexHbxsqCU
|
||||||
|
z0MPtKYH4Q==
|
||||||
|
-----END CERTIFICATE-----
|
@ -0,0 +1,73 @@
|
|||||||
|
Certificate:
|
||||||
|
Data:
|
||||||
|
Version: 3 (0x2)
|
||||||
|
Serial Number: 2 (0x2)
|
||||||
|
Signature Algorithm: md5WithRSAEncryption
|
||||||
|
Issuer: C=DE, ST=Germany, L=Berlin, O=World domination. Fast., CN=bytesex.org mini ca/Email=kraxel@bytesex.org
|
||||||
|
Validity
|
||||||
|
Not Before: Jul 27 13:34:31 2002 GMT
|
||||||
|
Not After : Jul 26 13:34:31 2004 GMT
|
||||||
|
Subject: C=DE, ST=World, L=some city somewhere, O=webfsd fan group, CN=localhost/Email=root@localhost
|
||||||
|
Subject Public Key Info:
|
||||||
|
Public Key Algorithm: rsaEncryption
|
||||||
|
RSA Public Key: (1024 bit)
|
||||||
|
Modulus (1024 bit):
|
||||||
|
00:e4:e6:c1:f4:b9:59:6f:c6:81:c6:5f:cb:4b:4b:
|
||||||
|
b5:68:3c:2d:cf:bf:c6:5f:38:bb:e4:f2:16:0b:fa:
|
||||||
|
dc:ec:41:95:f6:c7:77:78:c8:a2:06:e7:4b:21:6c:
|
||||||
|
77:2f:48:97:d6:ee:df:4e:f1:4f:6a:43:bf:01:99:
|
||||||
|
2a:04:54:39:d9:68:0f:21:61:c4:5c:6b:67:49:77:
|
||||||
|
e0:85:80:75:ba:77:06:fd:b6:a7:c3:b8:06:0b:ac:
|
||||||
|
13:d3:00:eb:dc:18:ae:09:9d:fc:2e:43:28:b8:1c:
|
||||||
|
da:cb:3b:e3:2d:e0:60:8a:de:f3:24:92:81:0a:16:
|
||||||
|
8b:9f:aa:9a:1b:09:0c:3c:2f
|
||||||
|
Exponent: 65537 (0x10001)
|
||||||
|
X509v3 extensions:
|
||||||
|
X509v3 Basic Constraints:
|
||||||
|
CA:FALSE
|
||||||
|
Netscape Cert Type:
|
||||||
|
SSL Server
|
||||||
|
Netscape Comment:
|
||||||
|
This is a *TEST* server certificate.
|
||||||
|
Signature Algorithm: md5WithRSAEncryption
|
||||||
|
39:ef:00:3c:1b:2f:cd:c1:6e:3c:da:6a:b4:7b:d1:a9:46:b6:
|
||||||
|
f1:20:7b:fe:77:4b:f6:0e:bc:41:0d:63:1d:d1:f6:f9:37:83:
|
||||||
|
cf:93:d3:ec:3a:e2:5b:7e:70:7a:de:6f:7a:fb:ee:59:d7:e8:
|
||||||
|
f0:d3:ea:81:f1:09:00:a4:e7:c2:ec:3c:8d:7c:19:85:47:6a:
|
||||||
|
76:63:c7:ce:68:95:79:dd:c7:2a:39:5f:df:0c:51:2d:22:29:
|
||||||
|
93:c4:ed:90:1b:54:cf:27:10:7c:7c:bf:4a:32:18:9f:2e:02:
|
||||||
|
8a:cb:6f:c9:69:b3:e1:ef:e3:0d:98:1e:a3:22:80:54:84:05:
|
||||||
|
15:ff
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIC6TCCAlKgAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBkzELMAkGA1UEBhMCREUx
|
||||||
|
EDAOBgNVBAgTB0dlcm1hbnkxDzANBgNVBAcTBkJlcmxpbjEgMB4GA1UEChMXV29y
|
||||||
|
bGQgZG9taW5hdGlvbi4gRmFzdC4xHDAaBgNVBAMTE2J5dGVzZXgub3JnIG1pbmkg
|
||||||
|
Y2ExITAfBgkqhkiG9w0BCQEWEmtyYXhlbEBieXRlc2V4Lm9yZzAeFw0wMjA3Mjcx
|
||||||
|
MzM0MzFaFw0wNDA3MjYxMzM0MzFaMIGJMQswCQYDVQQGEwJERTEOMAwGA1UECBMF
|
||||||
|
V29ybGQxHDAaBgNVBAcTE3NvbWUgY2l0eSBzb21ld2hlcmUxGTAXBgNVBAoTEHdl
|
||||||
|
YmZzZCBmYW4gZ3JvdXAxEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJ
|
||||||
|
ARYOcm9vdEBsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOTm
|
||||||
|
wfS5WW/GgcZfy0tLtWg8Lc+/xl84u+TyFgv63OxBlfbHd3jIogbnSyFsdy9Il9bu
|
||||||
|
307xT2pDvwGZKgRUOdloDyFhxFxrZ0l34IWAdbp3Bv22p8O4BgusE9MA69wYrgmd
|
||||||
|
/C5DKLgc2ss74y3gYIre8ySSgQoWi5+qmhsJDDwvAgMBAAGjVTBTMAkGA1UdEwQC
|
||||||
|
MAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRUaGlzIGlzIGEg
|
||||||
|
KlRFU1QqIHNlcnZlciBjZXJ0aWZpY2F0ZS4wDQYJKoZIhvcNAQEEBQADgYEAOe8A
|
||||||
|
PBsvzcFuPNpqtHvRqUa28SB7/ndL9g68QQ1jHdH2+TeDz5PT7DriW35wet5vevvu
|
||||||
|
Wdfo8NPqgfEJAKTnwuw8jXwZhUdqdmPHzmiVed3HKjlf3wxRLSIpk8TtkBtUzycQ
|
||||||
|
fHy/SjIYny4CistvyWmz4e/jDZgeoyKAVIQFFf8=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXwIBAAKBgQDk5sH0uVlvxoHGX8tLS7VoPC3Pv8ZfOLvk8hYL+tzsQZX2x3d4
|
||||||
|
yKIG50shbHcvSJfW7t9O8U9qQ78BmSoEVDnZaA8hYcRca2dJd+CFgHW6dwb9tqfD
|
||||||
|
uAYLrBPTAOvcGK4JnfwuQyi4HNrLO+Mt4GCK3vMkkoEKFoufqpobCQw8LwIDAQAB
|
||||||
|
AoGBAN1o1/N/1aLowF7CCkKq2K4ah7WMKrWwiw7Jm8m6vjdIuWYbStTqPM+wqluK
|
||||||
|
Lz7tWPHt3NLfV5HSNc+19454afqs3NH7bNruUYpvfih2t/3mDuVggq4DemAgrnm/
|
||||||
|
afYchlDvEttDgpXzYoSSDb2D11HxQM6XndkI21xl6mp9zdEBAkEA9+m4tSO8qVSr
|
||||||
|
dicBis5uN4UaMVx0ySWn85sqoIFJLMi53ZCuHmi8v7p6VKTYuHwGYEZ56R5FToVu
|
||||||
|
3Fz846Ra9QJBAOxeRhKXvwz5cnPtXkUgbh0UxkDFXqyMgZeWqTmiDgya0nGTY+Gw
|
||||||
|
gEAUlrt9l2DtWKbq9HsziIi/3pyWeh9+DBMCQQDBG5xV9L1bROm+QgnwfnXZ52MM
|
||||||
|
uhD6McvOdLpShgJi0QP+c1k9tKX5zp7FWha6NVmeGqeRj5O64zMEkaYnB/oVAkEA
|
||||||
|
hYkpK13hiJHwsD+9D26n5vQSoQsgVnk2yY5LYo0EROi+1X2AY0PU4N8A3UGx4QeW
|
||||||
|
Gw8IOgY+L4u+V1bH/by3UwJBANYKCGRtJ5fAipTlY+DfRpbUwE+P///JX8b8sQhc
|
||||||
|
BIkPBR6C2jvQ9wC1MWiHp/kFvKyT+ul1T3rGnWugm+OTdhQ=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
@ -0,0 +1,39 @@
|
|||||||
|
Name: webfs
|
||||||
|
Summary: lightweight http server for static content
|
||||||
|
Version: 1.21
|
||||||
|
Release: 0
|
||||||
|
Source0: %{name}-%{version}.tar.gz
|
||||||
|
Copyright: GPL
|
||||||
|
Group: Network/Daemons
|
||||||
|
Buildroot: %{_tmppath}/root-%{name}-%{version}
|
||||||
|
|
||||||
|
%description
|
||||||
|
This is a simple http server for purely static content. You
|
||||||
|
can use it to serve the content of a ftp server via http for
|
||||||
|
example. It is also nice to export some files the quick way
|
||||||
|
by starting a http server in a few seconds, without editing
|
||||||
|
some config file first.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q
|
||||||
|
|
||||||
|
%build
|
||||||
|
export CFLAGS="$RPM_OPT_FLAGS"
|
||||||
|
make prefix=/usr
|
||||||
|
|
||||||
|
%install
|
||||||
|
if test "%{buildroot}" != ""; then
|
||||||
|
rm -rf "%{buildroot}"
|
||||||
|
fi
|
||||||
|
make prefix=/usr DESTDIR=%{buildroot} install
|
||||||
|
|
||||||
|
%files
|
||||||
|
%defattr(-,root,root)
|
||||||
|
/usr/bin/webfsd
|
||||||
|
/usr/share/man/man1/webfsd.1*
|
||||||
|
%doc README COPYING
|
||||||
|
|
||||||
|
%clean
|
||||||
|
if test "%{buildroot}" != ""; then
|
||||||
|
rm -rf "%{buildroot}"
|
||||||
|
fi
|
@ -0,0 +1,165 @@
|
|||||||
|
.TH webfsd 1 "(c) 1999 Gerd Knorr"
|
||||||
|
.SH NAME
|
||||||
|
webfsd - a lightweight http server
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B webfsd [ options ]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
This is a simple http server for purely static content. You
|
||||||
|
can use it to serve the content of a ftp server via http for
|
||||||
|
example. It is also nice to export some files the quick way
|
||||||
|
by starting a http server in a few seconds, without editing
|
||||||
|
some config file first.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B -h
|
||||||
|
Print a short \fBh\fPelp text and the default values for all options.
|
||||||
|
.TP
|
||||||
|
.B -4
|
||||||
|
Use IPv\fB4\fP.
|
||||||
|
.TP
|
||||||
|
.B -6
|
||||||
|
Use IPv\fB6\fP.
|
||||||
|
.TP
|
||||||
|
.B -d
|
||||||
|
Enable \fBd\fPebug output.
|
||||||
|
.TP
|
||||||
|
.B -s
|
||||||
|
Write a start/stop notice and serious errors to the \fBs\fPyslog.
|
||||||
|
Specify this option twice to get a verbose log (additional log
|
||||||
|
events like dropped connections).
|
||||||
|
.TP
|
||||||
|
.B -t sec
|
||||||
|
Set network \fBt\fPimeout to >sec< seconds.
|
||||||
|
.TP
|
||||||
|
.B -c n
|
||||||
|
Set the number of allowed parallel \fBc\fPonnections to >n<. This is
|
||||||
|
a per-thread limit.
|
||||||
|
.TP
|
||||||
|
.B -a n
|
||||||
|
Configure the size of the directory cache. Webfs has a
|
||||||
|
cache for directory listings. The directory will be
|
||||||
|
reread if the cached copy is more than one hour old or if
|
||||||
|
the mtime of the directory has changed. The mtime will be
|
||||||
|
updated if a file is created or deleted. It will \fBnot\fP
|
||||||
|
be updated if a file is only modified, so you might get
|
||||||
|
outdated time stamps and file sizes.
|
||||||
|
.TP
|
||||||
|
.B -j
|
||||||
|
Do not generate a directory listing if the index-file isn't found.
|
||||||
|
.TP
|
||||||
|
.B -y n
|
||||||
|
Set the number of threads to spawn (if compiled with thread support).
|
||||||
|
.TP
|
||||||
|
.B -p port
|
||||||
|
Listen on \fBp\fPort >port< for incoming connections.
|
||||||
|
.TP
|
||||||
|
.B -r dir
|
||||||
|
Set document \fBr\fPoot to >dir<.
|
||||||
|
.TP
|
||||||
|
.B -R dir
|
||||||
|
Set document root to >dir< and chroot to >dir< before start
|
||||||
|
serving files. Note that this affects the path for the access log
|
||||||
|
file and pidfile too.
|
||||||
|
.TP
|
||||||
|
.B -f file
|
||||||
|
Use >file< as index \fBf\fPile for directories. If a client
|
||||||
|
asks for a directory, it will get >file< as response if such
|
||||||
|
a file exists in the directory and a directory listing otherwise.
|
||||||
|
index.html is a frequently used filename.
|
||||||
|
.TP
|
||||||
|
.B -n hostname
|
||||||
|
Set the host\fBn\fPame which the server should use (required
|
||||||
|
for redirects).
|
||||||
|
.TP
|
||||||
|
.B -i ip
|
||||||
|
Bind to \fBI\fPP-address >ip<.
|
||||||
|
.TP
|
||||||
|
.B -l log
|
||||||
|
\fBL\fPog all requests to the logfile >log< (common log format).
|
||||||
|
Using "-" as filename makes webfsd print the access log to stdout,
|
||||||
|
which is only useful together with the -F switch (see below).
|
||||||
|
.TP
|
||||||
|
.B -L log
|
||||||
|
Same as above, but additional flush every line. Useful if you
|
||||||
|
want monitor the logfile with tail -f.
|
||||||
|
.TP
|
||||||
|
.B -m file
|
||||||
|
Read \fBm\fPime types from >file<. Default is /etc/mime.types.
|
||||||
|
The mime types are read before chroot() is called (when started
|
||||||
|
with -R).
|
||||||
|
.TP
|
||||||
|
.B -k file
|
||||||
|
Use >file< as pidfile.
|
||||||
|
.TP
|
||||||
|
.B -u user
|
||||||
|
Set \fBu\fPid to >user< (after binding to the tcp port). This
|
||||||
|
option is allowed for root only.
|
||||||
|
.TP
|
||||||
|
.B -g group
|
||||||
|
Set \fBg\fPid to >group< (after binding to the tcp port). This
|
||||||
|
option is allowed for root only.
|
||||||
|
.TP
|
||||||
|
.B -F
|
||||||
|
Don't run as daemon. Webfsd will not fork into background, not detach
|
||||||
|
from terminal and report errors to stderr.
|
||||||
|
.TP
|
||||||
|
.B -b user:pass
|
||||||
|
Set user+password for the exported files. Only a single
|
||||||
|
username/password combination for all files is supported.
|
||||||
|
.TP
|
||||||
|
.B -e sec
|
||||||
|
\fBE\fPxpire documents after >sec< seconds. You can use that to
|
||||||
|
make sure the clients receive fresh data if the content within your
|
||||||
|
document root is updated in regular intervals. Webfsd will send
|
||||||
|
a Expires: header set to last-modified time plus >sec< seconds, so
|
||||||
|
you can simply use the update interval for >sec<.
|
||||||
|
.TP
|
||||||
|
.B -v
|
||||||
|
Enable \fBv\fPirtual hosts. This has the effect that webfsd expects
|
||||||
|
directories with the hostnames (lowercase) under document root. If
|
||||||
|
started this way: "webfsd -v -r /home/web", it will look for the file
|
||||||
|
/home/web/ftp.foobar.org/path/file when asked for
|
||||||
|
http://ftp.FOObar.org:8000/path/file.
|
||||||
|
.TP
|
||||||
|
.B -x path
|
||||||
|
Use >path< as CGI directory. >path< is interpreted relative to the
|
||||||
|
document root. Note that CGI support is limited to GET requests.
|
||||||
|
.TP
|
||||||
|
.B -S
|
||||||
|
\fBS\fPecure web server mode. Warning: This mode is strictly for https.
|
||||||
|
.TP
|
||||||
|
.B -C
|
||||||
|
File to use as SSL \fBc\fPertificate. This file must be in chained PEM
|
||||||
|
format, first the privat RSA key, followed by the certificate.
|
||||||
|
.TP
|
||||||
|
.B -P
|
||||||
|
\fBP\fPassword for accessing the SSL certificate.
|
||||||
|
.P
|
||||||
|
Webfsd can be installed suid root (although the default install
|
||||||
|
isn't suid root). This allows users to start webfsd chroot()ed
|
||||||
|
and to bind to ports below 1024. Webfsd will drop root privileges
|
||||||
|
before it starts serving files.
|
||||||
|
.P
|
||||||
|
Access control simply relies on Unix file permissions. Webfsd will
|
||||||
|
serve any regular file and provide listings for any directory it is
|
||||||
|
able to open(2).
|
||||||
|
.SH AUTHOR
|
||||||
|
Gerd Knorr <kraxel@bytesex.org>
|
||||||
|
.br
|
||||||
|
FreeBSD port by Charles F. Randall <cfr@pobox.com>
|
||||||
|
.SH COPYRIGHT
|
||||||
|
Copyright (C) 1999,2000 Gerd Knorr <kraxel@bytesex.org>
|
||||||
|
.P
|
||||||
|
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.
|
||||||
|
.P
|
||||||
|
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.
|
||||||
|
.P
|
||||||
|
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.
|
Loading…
Reference in New Issue