Check-in [2204669e3b]
Overview
Comment:Merged in trunk
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | seccomp
Files: files | file ages | folders
SHA1:2204669e3bcce671c8df44ba6fb015a5d44c86d0
User & Date: rkeene on 2020-03-31 14:33:22
Other Links: manifest | tags
Context
2020-03-31
15:44
Added seccomp support check-in: 854cb424a1 user: rkeene tags: seccomp
14:33
Merged in trunk check-in: 2204669e3b user: rkeene tags: seccomp
14:30
Integrated remove-c11-atomics changes check-in: 7ee2e833d2 user: rkeene tags: trunk
2014-06-16
17:00
Added start of seccomp support check-in: 5e8db14086 user: rkeene tags: seccomp
Changes

Modified .fossil-settings/ignore-glob from [c62bf95d8d] to [3d8734b3a3].

     1      1   filed
     2      2   filed.o
            3  +filed-mime-types.h
            4  +compiled

Modified LICENSE from [71c9248593] to [a50b669ed6].

     1         -Copyright (c) 2014, Roy Keene
            1  +Copyright (c) 2014 - 2016, Roy Keene
     2      2   All rights reserved.
     3      3   
     4      4   Redistribution and use in source and binary forms, with or without
     5      5   modification, are permitted provided that the following conditions are met:
     6      6   	1. Redistributions of source code must retain the above copyright
     7      7   	   notice, this list of conditions and the following disclaimer.
     8      8   	2. Redistributions in binary form must reproduce the above copyright

Modified Makefile from [a294b2122c] to [8ce95f72a7].

     1      1   CC = gcc
     2         -CFLAGS = -Wall -Werror -W -pthread -O3 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
     3         -LDFLAGS = -pthread
     4         -LIBS = -lpthread
            2  +CFLAGS = -I. -Wall -W -pthread -O3 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE $(FILED_EXTRA_CFLAGS)
            3  +LDFLAGS = -pthread $(FILED_EXTRA_LDFLAGS)
            4  +LIBS = -lpthread $(FILED_EXTRA_LIBS)
     5      5   MIMETYPES = /etc/httpd/mime.types
     6      6   
     7      7   PREFIX = /usr/local
     8      8   prefix = $(PREFIX)
     9      9   bindir = $(prefix)/bin
    10     10   mandir = $(prefix)/share/man
           11  +srcdir = .
           12  +vpath %.c $(srcdir)
    11     13   
    12     14   all: filed
    13     15   
    14     16   filed: filed.o
    15     17   	$(CC) $(CFLAGS) $(LDFLAGS) -o "$@" $^ $(LIBS)
    16     18   
    17         -filed.o: filed.c filed-mime-types.h
           19  +filed.o: $(srcdir)/filed.c filed-mime-types.h
    18     20   
    19         -filed-mime-types.h: generate-mime-types
    20         -	./generate-mime-types "$(MIMETYPES)" > filed-mime-types.h.new
           21  +filed-mime-types.h: $(srcdir)/generate-mime-types $(srcdir)/mime.types
           22  +	'$(srcdir)/generate-mime-types' '$(MIMETYPES)' > filed-mime-types.h.new || \
           23  +		'$(srcdir)/generate-mime-types' '$(srcdir)/mime.types' > filed-mime-types.h.new
    21     24   	mv filed-mime-types.h.new filed-mime-types.h
    22     25   
    23         -install: filed filed.1
           26  +install: filed $(srcdir)/filed.1
    24     27   	test -d "$(DESTDIR)$(mandir)/man1" || mkdir -p "$(DESTDIR)$(mandir)/man1"
    25     28   	test -d "$(DESTDIR)$(bindir)" || mkdir -p "$(DESTDIR)$(bindir)"
    26         -	cp filed.1 "$(DESTDIR)$(mandir)/man1/"
           29  +	cp '$(srcdir)/filed.1' "$(DESTDIR)$(mandir)/man1/"
    27     30   	cp filed "$(DESTDIR)$(bindir)/"
    28     31   
    29     32   clean:
    30     33   	rm -f filed filed.o
    31     34   	rm -f filed-mime-types.h.new
    32     35   
    33     36   distclean: clean
    34     37   	rm -f filed-mime-types.h
    35     38   
    36     39   .PHONY: all install clean distclean

Modified README from [15c37ea3f2] to [dc3dfdd9b3].

    59     59   	This is an internal option and should only be used during development.
    60     60   
    61     61      3. Differing HTTP semantics (CFLAGS, -DFILED_NONBLOCK_HTTP=1)
    62     62   	It is possible that some HTTP clients may not process the HTTP stream
    63     63   	being delivered if they cannot write to the HTTP stream itself.  This
    64     64   	has not been observed yet, but it is possible.  If these semantics are
    65     65   	needed (and they should not be) then they can be enabled with this
    66         -	flag at the cost of performance
           66  +	flag at the cost of performance.
           67  +
           68  +   4. Differing chroot() semantics (CFLAGS, -DFILED_FAKE_CHROOT=1)
           69  +        In some cases it is desirable to mangle paths with a path prefix
           70  +        rather than call chroot() at startup.  This is less secure and slower
           71  +        and should be generally avoided -- however it may be necessary to do.
           72  +        In these cases the executable may be compiled with the
           73  +        FILED_FAKE_CHROOT C preprocessor macro defined and instead of calling
           74  +        chroot() all HTTP requests will have the root suffix specified as the
           75  +        argument to the "-r" or "--root" option prepended to them.
           76  +
           77  +   5. Differing "index.html" handling (CFLAGS, -DFILED_DONT_REDIRECT_DIRECTORIES=1)
           78  +        Normally "filed" redirects users who request a directory to the
           79  +        index.html file in that directory so that no memory allocations are
           80  +        required;  This option lets the server generate the new path.
    67     81   
    68         -   4. MIME Types (MIMETYPES)
    69         -	For single-file convience "filed" compiles the mapping of file
           82  +   6. MIME Types (MIMETYPES)
           83  +	For single-file convenience "filed" compiles the mapping of file
    70     84   	extensions (the string in the filename following its last dot ("."))
    71     85   	into the executable.  This mapping comes from a file in the format of
    72     86   		type1   type1_extension1 type1_extension2...
    73     87   		type2   type2_extension1 type2_extension2...
    74     88   		...
    75     89   	However it may not be desirable to include this mapping, or it may be
    76     90   	desirable to use your own mapping rather than the default one.  This
    77     91   	can be done by specifying the MIMETYPES macro to "make".  If no
    78     92   	mapping is desired, "/dev/null" may be specified.
    79     93   
    80     94   Log Files
    81     95   ---------
    82     96   Because "filed" relies on chroot(2) and setuid(2), log files cannot reliably
    83         -be re-opened.  If you need log rotation a second process, which can close and
    84         -re-open log files, must be used.  Any process may be used for writing logs to
    85         -but if the process does not support log rotation it will not provide that
           97  +be re-opened.  If you need log rotation then a second process, which can close
           98  +and re-open log files, must be used.  Any process may be used for writing logs
           99  +but if the process does not support log rotation then it will not provide that
    86    100   benefit.  For example, if you wish to write logs to syslogd(8) you can use
    87    101   logger(1), such as:
    88    102   	# ./filed --root /www --user nobody --log '|logger -t filed' --daemon

Added build/build-precompiled version [46b4f35d88].

            1  +#! /usr/bin/env bash
            2  +
            3  +# Ensure we are in the correct working directory
            4  +cd "$(dirname "$(which "$0")")/.." || exit 1
            5  +
            6  +# Determine the version of Filed
            7  +version=''
            8  +eval "$(( grep '^# *define  *FILED_VERSION' filed.c; echo '|version=FILED_VERSION' ) | cpp -E | grep '^|' | sed 's@^|@@')"
            9  +if [ -z "${version}" ]; then
           10  +	echo "Unable to determine which version of Filed we are compiling.  Aborting." >&2
           11  +
           12  +	exit 1
           13  +fi
           14  +
           15  +# Cleanup
           16  +rm -rf workdir-buildPrecompiled-*
           17  +
           18  +# Compile everything, all at once
           19  +idx=-1
           20  +for tryCompilerDir in "$(readlink -f ~/root/cross-compilers)" "$(readlink -f ~/devel/build-cc/TMP)"; do
           21  +	setup_cc="${tryCompilerDir}/setup-cc"
           22  +
           23  +	platforms=(
           24  +		$("${setup_cc}" | tail -n +2)
           25  +	)
           26  +
           27  +	for platform in "${platforms[@]}"; do
           28  +		idx=$[$idx + 1]
           29  +		(
           30  +			workdir="workdir-buildPrecompiled-${idx}-$(openssl rand 20 -hex)-platform-${platform}" || exit 1
           31  +			mkdir "${workdir}" || exit 1
           32  +			cd "${workdir}" || exit 1
           33  +
           34  +			eval $("${setup_cc}" "${platform}")
           35  +			make_extra=(
           36  +				-f ../Makefile
           37  +				srcdir=..
           38  +				CC="${CC}"
           39  +			)
           40  +
           41  +			case "${platform}" in
           42  +				*-musl-*|*-musl)
           43  +					make_extra=("${make_extra[@]}" FILED_EXTRA_LDFLAGS="-static")
           44  +					;;
           45  +			esac
           46  +
           47  +			make "${make_extra[@]}"
           48  +		) &
           49  +	done
           50  +done
           51  +
           52  +# Wait for that to get done
           53  +wait
           54  +
           55  +# Rename the files into place
           56  +mkdir -p compiled
           57  +for binary in workdir-buildPrecompiled-*/filed; do
           58  +	platform="$(echo "${binary}" | sed 's@^.*-platform-@@;s@/.*$@@')"
           59  +	mv "${binary}" "compiled/filed-${version}-${platform}"
           60  +done
           61  +
           62  +# Cleanup
           63  +rm -rf workdir-buildPrecompiled-*
           64  +
           65  +exit 0

Modified build/update-wiki from [4e17d94f35] to [c47eeaaa5c].

     1      1   #! /bin/bash
     2      2   
     3      3   (
     4      4   	echo '<H2>NAME</H2>'
     5      5   	man2html -H linux.die.net -M /man -p filed.1 | \
     6         -		sed '0,/<H2>NAME<\/H2>/ d;/<H2>Index<\/H2>/,$ d;s@<A HREF="../index.html">Return to Main Contents</A>@@;s@\[@\&#91;@;s@\]@\&#93;@' | \
            6  +		sed '0,/<H2>NAME<\/H2>/ d;/<H2>Index<\/H2>/,$ d;s@<A HREF="../index.html">Return to Main Contents</A>@@;s@\[@\&#91;@g;s@\]@\&#93;@g' | \
     7      7   		sed '$ d;/^ *$/ d' | \
     8      8   		sed 's@\(http://linux.die.net/man/[^+]*\)+@\1/@'
     9      9   ) | fossil wiki commit Manual

Modified filed.1 from [561444a505] to [193b38d17e].

     1      1   .PU
     2         -.TH FILED 1 "19 Feb 14" "filed 1.9"
            2  +.TH FILED 1 "22 Sep 2016" "filed 1.21"
     3      3   .SH NAME
     4      4   filed \- serve files over HTTP
     5      5   .SH SYNOPSIS
     6      6   .ll +10
     7      7   .B filed
     8      8   .RB [{ \-h | \-\-help }]
     9      9   .RB [{ \-d | \-\-daemon }]

Modified filed.c from [cb8ce29868] to [05973c0b4a].

            1  +/*
            2  + * Copyright (c) 2014 - 2016, Roy Keene
            3  + * All rights reserved.
            4  + * 
            5  + * Redistribution and use in source and binary forms, with or without
            6  + * modification, are permitted provided that the following conditions are met:
            7  + * 	1. Redistributions of source code must retain the above copyright
            8  + * 	   notice, this list of conditions and the following disclaimer.
            9  + * 	2. Redistributions in binary form must reproduce the above copyright
           10  + * 	   notice, this list of conditions and the following disclaimer in the
           11  + * 	   documentation and/or other materials provided with the distribution.
           12  + * 
           13  + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
           14  + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
           15  + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
           16  + * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
           17  + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
           18  + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
           19  + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
           20  + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
           21  + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
           22  + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
           23  + * POSSIBILITY OF SUCH DAMAGE.
           24  + */
     1     25   #include <sys/sendfile.h>
     2     26   #include <sys/socket.h>
     3     27   #include <sys/types.h>
     4     28   #include <sys/prctl.h>
     5     29   #include <arpa/inet.h>
     6     30   #include <sys/mman.h>
     7     31   #include <sys/stat.h>
................................................................................
    17     41   #include <fcntl.h>
    18     42   #include <stdio.h>
    19     43   #include <errno.h>
    20     44   #include <time.h>
    21     45   #include <pwd.h>
    22     46   
    23     47   /* Compile time constants */
    24         -#define FILED_VERSION "1.9"
           48  +#define FILED_VERSION "1.21"
    25     49   #define FILED_SENDFILE_MAX 16777215
    26     50   #define FILED_MAX_FAILURE_COUNT 30
    27     51   #define FILED_DEFAULT_TYPE "application/octet-stream"
    28     52   #define FILED_PATH_BUFFER_SIZE 1010
    29     53   
    30     54   /* Default values */
    31     55   #define PORT 80
    32     56   #define THREAD_COUNT 5
    33     57   #define BIND_ADDR "::"
    34     58   #define CACHE_SIZE 8209
    35     59   #define LOG_FILE "-"
           60  +
           61  +/* Fuzzing Test Code */
           62  +#ifdef FILED_TEST_AFL
           63  +#define FILED_DONT_LOG 1
           64  +#define pthread_create(a, x, y, z) afl_pthread_create(a, x, y, z)
           65  +#define bind(x, y, z) afl_bind(x, y, z)
           66  +#define socket(x, y, z) 8193
           67  +#define listen(x, y) 0
           68  +#define accept(x, y, z) afl_accept(x, y, z)
           69  +#define close(x) { if (strcmp(#x, "random_fd") == 0) { close(x); } else { exit(0); } }
           70  +#define fclose(x) exit(0)
           71  +
           72  +static int afl_accept(int x, void *addr, void *z) {
           73  +	((struct sockaddr_in6 *) addr)->sin6_family = AF_INET + AF_INET6 + 1;
           74  +	return(STDIN_FILENO);
           75  +	x = x;
           76  +	z = z;
           77  +}
           78  +
           79  +static int afl_bind(int x, void *y, socklen_t z) {
           80  +	return(8194);
           81  +	x = x;
           82  +	y = y;
           83  +	z = z;
           84  +}
           85  +
           86  +static int afl_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg) {
           87  +	start_routine(arg);
           88  +	exit(3);
           89  +	thread = thread;
           90  +	attr = attr;
           91  +}
           92  +#endif
    36     93   
    37     94   /* Configuration options that work threads need to be aware of */
    38     95   struct filed_options {
    39     96   	int vhosts_enabled;
           97  +	const char *fake_newroot;
    40     98   };
    41     99   
    42    100   /* Arguments for worker threads */
    43    101   struct filed_worker_thread_args {
    44    102   	int fd;
    45    103   	struct filed_options options;
    46    104   };
................................................................................
    91    149   			off_t length;   /*** Range length ***/
    92    150   		} range;
    93    151   
    94    152   		struct {
    95    153   			int present;
    96    154   			char host[FILED_PATH_BUFFER_SIZE];
    97    155   		} host;
          156  +
          157  +		enum {
          158  +			FILED_CONNECTION_CLOSE,
          159  +			FILED_CONNECTION_KEEP_ALIVE
          160  +		} connection;
    98    161   	} headers;
    99    162   };
   100    163   
   101    164   /* Log record */
   102    165   struct filed_log_entry {
   103    166   	/* Type of log entry */
   104    167   	enum {
................................................................................
   116    179   	/* Message buffer for type = MESSAGE */
   117    180   	/* Path buffer for type = TRANSFER */
   118    181   	char buffer[FILED_PATH_BUFFER_SIZE];
   119    182   
   120    183   	/* Items for type = TRANSFER */
   121    184   	int http_code;
   122    185   	const char *reason;
          186  +	time_t connecttime;
   123    187   	time_t starttime;
   124    188   	time_t endtime;
   125    189   	off_t req_offset;
   126    190   	off_t req_length;
   127    191   	off_t sent_length;
   128    192   	off_t file_length;
   129    193   	char ip[128];
................................................................................
   136    200   struct filed_fileinfo *filed_fileinfo_fdcache = NULL;
   137    201   unsigned int filed_fileinfo_fdcache_size = 0;
   138    202   
   139    203   /** Logging **/
   140    204   struct filed_log_entry *filed_log_msg_list;
   141    205   pthread_mutex_t filed_log_msg_list_mutex;
   142    206   pthread_cond_t filed_log_msg_list_ready;
          207  +
          208  +/* Signal Handler */
          209  +static void filed_signal_handler(int signal_number) {
          210  +	struct filed_fileinfo *cache;
          211  +	unsigned int idx;
          212  +
          213  +	switch (signal_number) {
          214  +		case SIGHUP:
          215  +			for (idx = 0; idx < filed_fileinfo_fdcache_size; idx++) {
          216  +				cache = &filed_fileinfo_fdcache[idx];
          217  +
          218  +				pthread_mutex_lock(&cache->mutex);
          219  +
          220  +				cache->path[0] = '\0';
          221  +				if (cache->fd >= 0) {
          222  +					close(cache->fd);
          223  +
          224  +					cache->fd = -1;
          225  +				}
          226  +
          227  +				cache->lastmod = "";
          228  +				cache->type = "";
          229  +
          230  +				pthread_mutex_unlock(&cache->mutex);
          231  +			}
          232  +			break;
          233  +	}
          234  +
          235  +	return;
          236  +}
   143    237   
   144    238   /* Initialize cache */
   145    239   static int filed_init_cache(unsigned int cache_size) {
   146    240   	unsigned int idx;
   147    241   	int mutex_init_ret;
   148    242   
   149    243   	/* Cache may not be re-initialized */
   150    244   	if (filed_fileinfo_fdcache_size != 0 || filed_fileinfo_fdcache != NULL) {
   151    245   		return(1);
   152    246   	}
          247  +
          248  +	/* Cache does not need to be allocated if cache is not enabled */
          249  +	if (cache_size == 0) {
          250  +		return(0);
          251  +	}
   153    252   
   154    253   	/* Allocate cache */
   155    254   	filed_fileinfo_fdcache_size = cache_size;
   156    255   	filed_fileinfo_fdcache = malloc(sizeof(*filed_fileinfo_fdcache) * filed_fileinfo_fdcache_size);
   157    256   	if (filed_fileinfo_fdcache == NULL) {
   158    257   		return(1);
   159    258   	}
................................................................................
   173    272   
   174    273   	return(0);
   175    274   }
   176    275   
   177    276   /* Initialize process */
   178    277   static int filed_init(unsigned int cache_size) {
   179    278   	static int called = 0;
          279  +	struct sigaction signal_handler_info;
          280  +	sigset_t signal_handler_mask;
          281  +	ssize_t read_ret = 0;
   180    282   	unsigned int random_value = 0;
   181    283   	int cache_ret;
   182    284   	int random_fd;
   183    285   
   184    286   	if (called) {
   185    287   		return(0);
   186    288   	}
   187    289   
   188    290   	called = 1;
   189    291   
   190    292   	/* Attempt to lock all memory to physical RAM (but don't care if we can't) */
   191    293   	mlockall(MCL_CURRENT | MCL_FUTURE);
   192    294   
   193         -	/* Ignore SIGPIPE */
   194         -	signal(SIGPIPE, SIG_IGN);
          295  +	/* Establish signal handlers */
          296  +	/* SIGPIPE should interrupt system calls */
          297  +	sigfillset(&signal_handler_mask);
          298  +	signal_handler_info.sa_handler = filed_signal_handler;
          299  +	signal_handler_info.sa_mask = signal_handler_mask;
          300  +	signal_handler_info.sa_flags = SA_RESTART;
          301  +	sigaction(SIGPIPE, &signal_handler_info, NULL);
          302  +
          303  +	/* Handle SIGHUP to release all caches */
          304  +	sigfillset(&signal_handler_mask);
          305  +	signal_handler_info.sa_handler = filed_signal_handler;
          306  +	signal_handler_info.sa_mask = signal_handler_mask;
          307  +	signal_handler_info.sa_flags = 0;
          308  +	sigaction(SIGHUP, &signal_handler_info, NULL);
   195    309   
   196    310   	/* Initialize cache structure */
   197    311   	cache_ret = filed_init_cache(cache_size);
   198    312   	if (cache_ret != 0) {
   199    313   		return(cache_ret);
   200    314   	}
   201    315   
   202    316   	/* Initialize random number generator */
   203    317   	random_fd = open("/dev/urandom", O_RDONLY);
   204    318   	if (random_fd >= 0) {
   205         -		read(random_fd, &random_value, sizeof(random_value));
          319  +		read_ret = read(random_fd, &random_value, sizeof(random_value));
   206    320   
   207    321   		close(random_fd);
   208    322   	}
   209    323   
   210    324   	random_value ^= getpid();
   211    325   	random_value ^= getuid();
   212    326   	random_value ^= time(NULL);
   213    327   
   214    328   	srandom(random_value);
   215    329   
   216    330   	return(0);
          331  +
          332  +	/* NOTREACH: Read may fail or succeed, we don't actually care */
          333  +	read_ret = read_ret;
   217    334   }
   218    335   
   219    336   /* Listen on a particular address/port */
   220    337   static int filed_listen(const char *address, unsigned int port) {
   221    338   	struct sockaddr_in6 addr_v6;
   222    339   	struct sockaddr_in addr_v4;
   223    340   	struct sockaddr *addr;
   224    341   	socklen_t addr_len;
   225    342   	int pton_ret, bind_ret, listen_ret;
   226    343   	int family;
   227    344   	int fd;
   228         -
   229    345   
   230    346   	family = AF_INET6;
   231    347   	pton_ret = inet_pton(family, address, &addr_v6.sin6_addr.s6_addr);
   232    348   	if (pton_ret != 1) {
   233    349   		family = AF_INET;
   234    350   		pton_ret = inet_pton(family, address, &addr_v4.sin_addr.s_addr);
   235    351   		if (pton_ret != 1) {
................................................................................
   277    393   #ifdef FILED_DONT_LOG
   278    394   #  define filed_logging_thread_init(x) 0
   279    395   #  define filed_log_msg_debug(x, ...) /**/
   280    396   #  define filed_log_msg(x, ...) /**/
   281    397   #  define filed_log_entry(x) /**/
   282    398   #  define filed_log_ip(x, ...) NULL
   283    399   #  define filed_log_new(x) &local_dummy_log
   284         -#  define filed_log_open(x) stdout
          400  +#  define filed_log_free(x) /**/
          401  +
          402  +/* Return logging handle */
          403  +static FILE *filed_log_open(const char *file) {
          404  +	return(stdout);
          405  +	file = file;
          406  +}
   285    407   #else
          408  +#  define filed_log_free(x) free(x)
   286    409   #  ifdef FILED_DEBUG
   287    410   #    define filed_log_msg_debug(x, ...) { fprintf(stderr, x, __VA_ARGS__); fprintf(stderr, "\n"); fflush(stderr); }
   288    411   #  else
   289    412   #    define filed_log_msg_debug(x, ...) /**/
   290    413   #  endif
   291    414   
   292    415   /* Initialize logging thread */
................................................................................
   339    462   							break;
   340    463   					}
   341    464   
   342    465   					if (curr->endtime == ((time_t) -1)) {
   343    466   						curr->endtime = now;
   344    467   					}
   345    468   
   346         -					fprintf(fp, "TRANSFER METHOD=%s PATH=%s SRC=%s:%i TIME.START=%llu TIME.END=%llu CODE.VALUE=%u CODE.REASON=%s REQUEST.OFFSET=%llu REQUEST.LENGTH=%llu FILE.LENGTH=%llu TRANSFER.LENGTH=%llu",
          469  +					fprintf(fp, "TRANSFER METHOD=%s PATH=%s SRC=%s:%i CLIENT.TIME.CONNECT=%llu REQUEST.TIME.START=%llu REQUEST.TIME.END=%llu CODE.VALUE=%u CODE.REASON=%s REQUEST.OFFSET=%llu REQUEST.LENGTH=%llu FILE.LENGTH=%llu TRANSFER.LENGTH=%llu",
   347    470   						method,
   348    471   						curr->buffer,
   349    472   						curr->ip, curr->port,
          473  +						(unsigned long long) curr->connecttime,
   350    474   						(unsigned long long) curr->starttime,
   351    475   						(unsigned long long) curr->endtime,
   352    476   						curr->http_code, curr->reason,
   353    477   						(unsigned long long) curr->req_offset,
   354    478   						(unsigned long long) curr->req_length,
   355    479   						(unsigned long long) curr->file_length,
   356    480   						(unsigned long long) curr->sent_length
   357    481   					);
   358    482   
   359    483   					break;
   360    484   			}
   361    485   			fprintf(fp, " THREAD=%llu TIME=%llu\n",
   362         -				(unsigned long long) curr->thread,
          486  +				(unsigned long long) ((intptr_t) curr->thread),
   363    487   				(unsigned long long) now
   364    488   			);
   365    489   			fflush(fp);
   366    490   
   367    491   			prev = curr;
   368    492   			curr = curr->_prev;
   369    493   
................................................................................
   393    517   	struct filed_log_entry *retval;
   394    518   
   395    519   	retval = malloc(sizeof(*retval));
   396    520   
   397    521   	if (initialize) {
   398    522   		retval->buffer[0] = '\0';
   399    523   		retval->http_code = -1;
          524  +		retval->connecttime = 0;
   400    525   		retval->starttime = 0;
   401    526   		retval->endtime = 0;
   402    527   		retval->req_offset = 0;
   403    528   		retval->req_length = 0;
   404    529   		retval->sent_length = 0;
   405    530   		retval->file_length = 0;
   406    531   		retval->ip[0] = '\0';
................................................................................
   475    600   	filed_log_msg_list = NULL;
   476    601   
   477    602   	pthread_mutex_init(&filed_log_msg_list_mutex, NULL);
   478    603   
   479    604   	pthread_create(&thread_id, NULL, filed_logging_thread, args);
   480    605   
   481    606   	filed_log_msg("START");
          607  +
          608  +	return(0);
          609  +}
          610  +#endif
          611  +
          612  +#ifdef FILED_DONT_TIMEOUT
          613  +#define filed_sockettimeout_thread_init() 0
          614  +#define filed_sockettimeout_init() 0
          615  +#define filed_sockettimeout_accept(x) /**/
          616  +#define filed_sockettimeout_processing_start(x) /**/
          617  +#define filed_sockettimeout_processing_end(x) /**/
          618  +#define filed_sockettimeout_close(x, y) /**/
          619  +#else
          620  +time_t filed_sockettimeout_time;
          621  +struct {
          622  +	time_t expiration_time;
          623  +	pthread_t thread_id;
          624  +	enum {
          625  +		filed_sockettimeout_valid,
          626  +		filed_sockettimeout_invalid,
          627  +	} valid;
          628  +} *filed_sockettimeout_sockstatus;
          629  +long filed_sockettimeout_sockstatus_length;
          630  +int filed_sockettimeout_devnull_fd;
          631  +pthread_mutex_t filed_sockettimeout_mutex = PTHREAD_MUTEX_INITIALIZER;
          632  +
          633  +static int filed_sockettimeout_sockfd_in_range(int sockfd) {
          634  +	if (sockfd < 3) {
          635  +		return(0);
          636  +	}
          637  +
          638  +	if (sockfd > filed_sockettimeout_sockstatus_length) {
          639  +		return(0);
          640  +	}
          641  +
          642  +	return(1);
          643  +}
          644  +
          645  +static void filed_sockettimeout_expire(int sockfd, int length, int lockheld) {
          646  +	time_t now, expire;
          647  +
          648  +	if (!lockheld) {
          649  +		pthread_mutex_lock(&filed_sockettimeout_mutex);
          650  +	}
          651  +
          652  +	now = filed_sockettimeout_time;
          653  +
          654  +	expire = now + length;
          655  +
          656  +	filed_sockettimeout_sockstatus[sockfd].expiration_time = expire;
          657  +
          658  +	if (!lockheld) {
          659  +		pthread_mutex_unlock(&filed_sockettimeout_mutex);
          660  +	}
          661  +
          662  +	return;
          663  +}
          664  +
          665  +static void filed_sockettimeout_accept(int sockfd) {
          666  +	if (!filed_sockettimeout_sockfd_in_range(sockfd)) {
          667  +		return;
          668  +	}
          669  +
          670  +	pthread_mutex_lock(&filed_sockettimeout_mutex);
          671  +
          672  +	filed_sockettimeout_expire(sockfd, 60, 1);
          673  +
          674  +	filed_sockettimeout_sockstatus[sockfd].thread_id = pthread_self();
          675  +
          676  +	filed_sockettimeout_sockstatus[sockfd].valid = filed_sockettimeout_valid;
          677  +
          678  +	pthread_mutex_unlock(&filed_sockettimeout_mutex);
          679  +
          680  +	return;
          681  +}
          682  +
          683  +static void filed_sockettimeout_processing_start(int sockfd) {
          684  +	if (!filed_sockettimeout_sockfd_in_range(sockfd)) {
          685  +		return;
          686  +	}
          687  +
          688  +	filed_sockettimeout_expire(sockfd, 86400, 0);
          689  +
          690  +	return;
          691  +}
          692  +
          693  +static void filed_sockettimeout_processing_end(int sockfd) {
          694  +	if (!filed_sockettimeout_sockfd_in_range(sockfd)) {
          695  +		return;
          696  +	}
          697  +
          698  +	filed_sockettimeout_expire(sockfd, 60, 0);
          699  +
          700  +	return;
          701  +}
          702  +
          703  +static void filed_sockettimeout_close(int sockfd, int lockheld) {
          704  +	if (!filed_sockettimeout_sockfd_in_range(sockfd)) {
          705  +		return;
          706  +	}
          707  +
          708  +	if (!lockheld) {
          709  +		pthread_mutex_lock(&filed_sockettimeout_mutex);
          710  +	}
          711  +
          712  +	filed_sockettimeout_sockstatus[sockfd].valid = filed_sockettimeout_invalid;
          713  +
          714  +	if (!lockheld) {
          715  +		pthread_mutex_unlock(&filed_sockettimeout_mutex);
          716  +	}
          717  +
          718  +	return;
          719  +}
          720  +
          721  +static void *filed_sockettimeout_thread(void *arg) {
          722  +	struct timespec sleep_time;
          723  +	time_t now, expiration_time;
          724  +	pthread_t thread_id;
          725  +	long idx;
          726  +	int count;
          727  +	int valid;
          728  +	int time_interval = 30;
          729  +	int check_period = 90;
          730  +
          731  +	while (1) {
          732  +		for (count = 0; count < (check_period / time_interval); count++) {
          733  +			sleep_time.tv_sec = time_interval;
          734  +			sleep_time.tv_nsec = 0;
          735  +			nanosleep(&sleep_time, NULL);
          736  +
          737  +			pthread_mutex_lock(&filed_sockettimeout_mutex);
          738  +
          739  +			now = time(NULL);
          740  +
          741  +			filed_sockettimeout_time = now;
          742  +
          743  +			pthread_mutex_unlock(&filed_sockettimeout_mutex);
          744  +		}
          745  +
          746  +		pthread_mutex_lock(&filed_sockettimeout_mutex);
          747  +
          748  +		for (idx = 0; idx < filed_sockettimeout_sockstatus_length; idx++) {
          749  +			valid = filed_sockettimeout_sockstatus[idx].valid;
          750  +
          751  +			if (valid != filed_sockettimeout_valid) {
          752  +				continue;
          753  +			}
          754  +
          755  +			expiration_time = filed_sockettimeout_sockstatus[idx].expiration_time;
          756  +
          757  +			thread_id = filed_sockettimeout_sockstatus[idx].thread_id;
          758  +
          759  +			if (expiration_time > now) {
          760  +				continue;
          761  +			}
          762  +
          763  +			filed_sockettimeout_close(idx, 1);
          764  +
          765  +			dup2(filed_sockettimeout_devnull_fd, idx);
          766  +
          767  +			pthread_kill(thread_id, SIGPIPE);
          768  +		}
          769  +
          770  +		pthread_mutex_unlock(&filed_sockettimeout_mutex);
          771  +	}
          772  +
          773  +	return(NULL);
          774  +
          775  +	/* NOTREACH: We don't actually take any arguments */
          776  +	arg = arg;
          777  +}
          778  +
          779  +static int filed_sockettimeout_thread_init(void) {
          780  +	pthread_t thread_id;
          781  +
          782  +	pthread_create(&thread_id, NULL, filed_sockettimeout_thread, NULL);
          783  +
          784  +	return(0);
          785  +}
          786  +
          787  +static int filed_sockettimeout_init(void) {
          788  +	long maxfd, idx;
          789  +
          790  +	maxfd = sysconf(_SC_OPEN_MAX);
          791  +	if (maxfd <= 0) {
          792  +		maxfd = 4096;
          793  +	}
          794  +
          795  +	filed_sockettimeout_sockstatus_length = maxfd;
          796  +	filed_sockettimeout_sockstatus = malloc(sizeof(*filed_sockettimeout_sockstatus) * filed_sockettimeout_sockstatus_length);
          797  +	if (filed_sockettimeout_sockstatus == NULL) {
          798  +		return(-1);
          799  +	}
          800  +
          801  +	for (idx = 0; idx < maxfd; idx++) {
          802  +		filed_sockettimeout_sockstatus[idx].valid = filed_sockettimeout_invalid;
          803  +	}
          804  +
          805  +	filed_sockettimeout_devnull_fd = open("/dev/null", O_RDWR);
          806  +	if (filed_sockettimeout_devnull_fd < 0) {
          807  +		return(-1);
          808  +	}
   482    809   
   483    810   	return(0);
   484    811   }
   485    812   #endif
   486    813   
   487    814   /* Format time per RFC2616 */
   488    815   static char *filed_format_time(char *buffer, size_t buffer_len, const time_t timeinfo) {
................................................................................
   562    889   		(unsigned long long) time(NULL),
   563    890   		(unsigned long long) random(),
   564    891   		(unsigned long long) random(),
   565    892   		(unsigned long long) random(),
   566    893   		(unsigned long long) random()
   567    894   	);
   568    895   }
          896  +
          897  +#ifdef FILED_FAKE_CHROOT
          898  +/* Translate a path into a fake chroot path */
          899  +static const char *filed_path_translate(const char *path, struct filed_options *options) {
          900  +	static __thread char pathBuffer[8192];
          901  +	int snprintf_ret;
          902  +
          903  +	/* If no alternative root is specified, return the unadorned path */
          904  +	if (!options->fake_newroot) {
          905  +		return(path);
          906  +	}
          907  +
          908  +	/* Verify that this request will not go outside of the specified root */
          909  +	if (strstr(path, "/../") != NULL || path[0] != '/') {
          910  +		filed_log_msg_debug("Unable to translate path \"%s\", contains invalid characters", path);
          911  +
          912  +		return(options->fake_newroot);
          913  +	}
          914  +
          915  +	/* Create the new path into our local (TLS) static buffer */
          916  +	snprintf_ret = snprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", options->fake_newroot, path);
          917  +	if (snprintf_ret < 0 || ((unsigned int) snprintf_ret) >= sizeof(pathBuffer)) {
          918  +		filed_log_msg_debug("Unable to translate path \"%s\", will not fit into new buffer", path);
          919  +
          920  +		return(options->fake_newroot);
          921  +	}
          922  +
          923  +	filed_log_msg_debug("Translating path \"%s\" into \"%s\"", path, pathBuffer);
          924  +
          925  +	/* Return the new path */
          926  +	return(pathBuffer);
          927  +}
          928  +
          929  +static void filed_path_translate_set_root(const char *var, struct filed_options *options, const char *val) {
          930  +	options->fake_newroot = strdup(val);
          931  +
          932  +	return;
          933  +
          934  +	/* var is only used in the macro -- discard it here */
          935  +	var = var;
          936  +}
          937  +#else
          938  +#define filed_path_translate(path, options) path
          939  +#define filed_path_translate_set_root(var, options, val) var = strdup(val)
          940  +#endif
   569    941   
   570    942   /* Open a file and return file information */
   571         -static struct filed_fileinfo *filed_open_file(const char *path, struct filed_fileinfo *buffer) {
          943  +static struct filed_fileinfo *filed_open_file(const char *path, struct filed_fileinfo *buffer, struct filed_options *options) {
   572    944   	struct filed_fileinfo *cache;
   573    945   	unsigned int cache_idx;
   574    946   	off_t len;
   575    947   	int fd;
   576    948   
   577         -	cache_idx = filed_hash((const unsigned char *) path, filed_fileinfo_fdcache_size);
          949  +	if (filed_fileinfo_fdcache_size != 0) {
          950  +		cache_idx = filed_hash((const unsigned char *) path, filed_fileinfo_fdcache_size);
   578    951   
   579         -	cache = &filed_fileinfo_fdcache[cache_idx];
          952  +		cache = &filed_fileinfo_fdcache[cache_idx];
   580    953   
   581         -	filed_log_msg_debug("Locking mutex for idx: %lu", (unsigned long) cache_idx);
          954  +		filed_log_msg_debug("Locking mutex for idx: %lu", (unsigned long) cache_idx);
   582    955   
   583         -	pthread_mutex_lock(&cache->mutex);
          956  +		pthread_mutex_lock(&cache->mutex);
   584    957   
   585         -	filed_log_msg_debug("Completed locking mutex for idx: %lu", (unsigned long) cache_idx);
          958  +		filed_log_msg_debug("Completed locking mutex for idx: %lu", (unsigned long) cache_idx);
          959  +	} else {
          960  +		cache_idx = 0;
          961  +		cache = buffer;
          962  +		cache->path[0] = '\0';
          963  +		cache->fd = -1;
          964  +	}
   586    965   
   587    966   	if (strcmp(path, cache->path) != 0) {
   588    967   		filed_log_msg_debug("Cache miss for idx: %lu: OLD \"%s\", NEW \"%s\"", (unsigned long) cache_idx, cache->path, path);
   589    968   
   590         -		fd = open(path, O_RDONLY | O_LARGEFILE);
          969  +		fd = open(filed_path_translate(path, options), O_RDONLY | O_LARGEFILE);
   591    970   		if (fd < 0) {
   592         -			pthread_mutex_unlock(&cache->mutex);
          971  +			if (filed_fileinfo_fdcache_size != 0) {
          972  +				pthread_mutex_unlock(&cache->mutex);
          973  +			}
   593    974   
   594    975   			return(NULL);
   595    976   		}
   596    977   
   597    978   		if (cache->fd >= 0) {
   598    979   			close(cache->fd);
   599    980   		}
................................................................................
   609    990   
   610    991   		/* XXX:TODO: Determine */
   611    992   		cache->lastmod = filed_format_time(cache->lastmod_b, sizeof(cache->lastmod_b), time(NULL) - 30);
   612    993   	} else {
   613    994   		filed_log_msg_debug("Cache hit for idx: %lu: PATH \"%s\"", (unsigned long) cache_idx, path);
   614    995   	}
   615    996   
   616         -	/*
   617         -	 * We have to make a duplicate FD, because once we release the cache
   618         -	 * mutex, the file descriptor may be closed
   619         -	 */
   620         -	fd = dup(cache->fd);
   621         -	if (fd < 0) {
          997  +	if (filed_fileinfo_fdcache_size != 0) {
          998  +		/*
          999  +		 * We have to make a duplicate FD, because once we release the cache
         1000  +		 * mutex, the file descriptor may be closed
         1001  +		 */
         1002  +		fd = dup(cache->fd);
         1003  +		if (fd < 0) {
         1004  +			pthread_mutex_unlock(&cache->mutex);
         1005  +
         1006  +			return(NULL);
         1007  +		}
         1008  +
         1009  +		buffer->fd = fd;
         1010  +		buffer->len = cache->len;
         1011  +		buffer->type = cache->type;
         1012  +		memcpy(buffer->lastmod_b, cache->lastmod_b, sizeof(buffer->lastmod_b));
         1013  +		memcpy(buffer->etag, cache->etag, sizeof(buffer->etag));
         1014  +		buffer->lastmod = buffer->lastmod_b + (cache->lastmod - cache->lastmod_b);
         1015  +
   622   1016   		pthread_mutex_unlock(&cache->mutex);
   623         -
   624         -		return(NULL);
   625   1017   	}
   626   1018   
   627         -	buffer->fd = fd;
   628         -	buffer->len = cache->len;
   629         -	buffer->type = cache->type;
   630         -	memcpy(buffer->lastmod_b, cache->lastmod_b, sizeof(buffer->lastmod_b));
   631         -	memcpy(buffer->etag, cache->etag, sizeof(buffer->etag));
   632         -	buffer->lastmod = buffer->lastmod_b + (cache->lastmod - cache->lastmod_b);
   633         -
   634         -	pthread_mutex_unlock(&cache->mutex);
   635         -
   636   1019   	return(buffer);
         1020  +
         1021  +	/* options is only used if fake chroot is enabled, confuse the compiler */
         1022  +	options = options;
   637   1023   }
   638   1024   
   639   1025   /* Process an HTTP request and return the path requested */
   640   1026   static struct filed_http_request *filed_get_http_request(FILE *fp, struct filed_http_request *buffer_st, struct filed_options *options) {
   641   1027   	char *method, *path;
   642   1028   	char *buffer, *workbuffer, *workbuffer_next;
   643   1029   	char *fgets_ret;
................................................................................
   649   1035   
   650   1036   	/* Set to default values */
   651   1037   	range_start = 0;
   652   1038   	range_end   = 0;
   653   1039   	range_request = 0;
   654   1040   	range_length = -1;
   655   1041   	buffer_st->headers.host.present = 0;
         1042  +	buffer_st->headers.connection = FILED_CONNECTION_CLOSE;
   656   1043   
   657   1044   	buffer = buffer_st->tmpbuf;
   658   1045   	buffer_len = sizeof(buffer_st->tmpbuf);
   659   1046   
   660   1047   	fgets_ret = fgets(buffer, buffer_len, fp);
   661   1048   	if (fgets_ret == NULL) {
   662   1049   		return(NULL);
................................................................................
   748   1135   
   749   1136   			workbuffer = buffer + 5;
   750   1137   			while (*workbuffer == ' ') {
   751   1138   				workbuffer++;
   752   1139   			}
   753   1140   
   754   1141   			strcpy(buffer_st->headers.host.host, workbuffer);
         1142  +		} else if (strncasecmp(buffer, "Connection: Keep-Alive", 22) == 0) {
         1143  +			buffer_st->headers.connection = FILED_CONNECTION_KEEP_ALIVE;
   755   1144   		}
   756   1145   
   757   1146   		if (memcmp(buffer, "\r\n", 2) == 0) {
   758   1147   			break;
   759   1148   		}
   760   1149   	}
   761   1150   
................................................................................
   822   1211   	/** reason must point to a globally allocated value **/
   823   1212   	log->reason = reason;
   824   1213   	log->http_code = error_number;
   825   1214   
   826   1215   	filed_log_entry(log);
   827   1216   
   828   1217   	/* Close connection */
         1218  +	filed_sockettimeout_close(fileno(fp), 0);
         1219  +
   829   1220   	fclose(fp);
   830   1221   
   831   1222   	return;
   832   1223   }
   833   1224   
   834   1225   /* Return a redirect to index.html */
         1226  +#ifndef FILED_DONT_REDIRECT_DIRECTORIES
   835   1227   static void filed_redirect_index(FILE *fp, const char *date_current, const char *path, struct filed_log_entry *log) {
   836         -	int http_code = 301;
         1228  +	int http_code = 302;
   837   1229   	fprintf(fp, "HTTP/1.1 %i OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: 0\r\nConnection: close\r\nLocation: %s\r\n\r\n",
   838   1230   		http_code,
   839   1231   		date_current,
   840   1232   		date_current,
   841   1233   		"index.html"
   842   1234   	);
   843   1235   
................................................................................
   844   1236   	/* Log redirect */
   845   1237   	log->reason = "redirect";
   846   1238   	log->http_code = http_code;
   847   1239   
   848   1240   	filed_log_entry(log);
   849   1241   
   850   1242   	/* Close connection */
         1243  +	filed_sockettimeout_close(fileno(fp), 0);
         1244  +
   851   1245   	fclose(fp);
   852   1246   
   853   1247   	return;
   854   1248   
   855   1249   	/* Currently unused: path */
   856   1250   	path = path;
   857   1251   }
         1252  +#endif
         1253  +
         1254  +/* Convert an enum representing the "Connection" header value to a string */
         1255  +static const char *filed_connection_str(int connection_value) {
         1256  +	switch (connection_value) {
         1257  +		case FILED_CONNECTION_CLOSE:
         1258  +			return("close");
         1259  +		case FILED_CONNECTION_KEEP_ALIVE:
         1260  +			return("keep-alive");
         1261  +	}
         1262  +
         1263  +	return("close");
         1264  +}
   858   1265   
   859   1266   /* Handle a single request from a client */
   860         -static void filed_handle_client(int fd, struct filed_http_request *request, struct filed_log_entry *log, struct filed_options *options) {
         1267  +static int filed_handle_client(int fd, struct filed_http_request *request, struct filed_log_entry *log, struct filed_options *options) {
   861   1268   	struct filed_fileinfo *fileinfo;
   862   1269   	ssize_t sendfile_ret;
   863   1270   	size_t sendfile_size;
   864   1271   	off_t sendfile_offset, sendfile_sent, sendfile_len;
   865   1272   	char *path;
   866   1273   	char *date_current, date_current_b[64];
   867   1274   	int http_code;
   868   1275   	FILE *fp;
   869   1276   
         1277  +	/* Indicate the connection start time */
         1278  +	log->connecttime = time(NULL);
         1279  +
   870   1280   	/* Determine current time */
   871   1281   	date_current = filed_format_time(date_current_b, sizeof(date_current_b), time(NULL));
   872   1282   
   873   1283   	/* Open socket as ANSI I/O for ease of use */
   874   1284   	fp = fdopen(fd, "w+b");
   875   1285   	if (fp == NULL) {
         1286  +		filed_sockettimeout_close(fd, 0);
         1287  +
   876   1288   		close(fd);
   877   1289   
   878   1290   		log->buffer[0] = '\0';
   879   1291   		log->http_code = -1;
   880   1292   		log->reason = "fdopen_failed";
   881   1293   
   882   1294   		filed_log_entry(log);
   883   1295   
   884         -		return;
         1296  +		return(FILED_CONNECTION_CLOSE);
   885   1297   	}
   886   1298   
   887   1299   	request = filed_get_http_request(fp, request, options);
   888   1300   
   889   1301   	if (request == NULL) {
   890   1302   		log->buffer[0] = '\0';
   891   1303   
   892   1304   		filed_error_page(fp, date_current, 500, FILED_REQUEST_METHOD_GET, "format", log);
   893   1305   
   894         -		return;
         1306  +		return(FILED_CONNECTION_CLOSE);
   895   1307   	}
         1308  +
         1309  +	filed_sockettimeout_processing_start(fd);
   896   1310   
   897   1311   	path = request->path;
   898   1312   	strcpy(log->buffer, path);
   899   1313   	log->method = request->method;
   900   1314   
   901   1315   	/* If the requested path is a directory, redirect to index page */
   902   1316   	if (request->type == FILED_REQUEST_TYPE_DIRECTORY) {
         1317  +#ifdef FILED_DONT_REDIRECT_DIRECTORIES
         1318  +		char localpath[8192];
         1319  +		int snprintf_ret;
         1320  +
         1321  +		snprintf_ret = snprintf(localpath, sizeof(localpath), "%s/index.html", path);
         1322  +
         1323  +		if (snprintf_ret <= 0 || snprintf_ret > (sizeof(localpath) - 1)) {
         1324  +			filed_error_page(fp, date_current, 500, request->method, "path_format", log);
         1325  +
         1326  +			return(FILED_CONNECTION_CLOSE);
         1327  +		}
         1328  +
         1329  +		path = localpath;
         1330  +#else
   903   1331   		filed_redirect_index(fp, date_current, path, log);
   904   1332   
   905         -		return;
         1333  +		return(FILED_CONNECTION_CLOSE);
         1334  +#endif
   906   1335   	}
   907   1336   
   908         -	fileinfo = filed_open_file(path, &request->fileinfo);
         1337  +	fileinfo = filed_open_file(path, &request->fileinfo, options);
   909   1338   	if (fileinfo == NULL) {
   910   1339   		filed_error_page(fp, date_current, 404, request->method, "open_failed", log);
   911   1340   
   912         -		return;
         1341  +		return(FILED_CONNECTION_CLOSE);
   913   1342   	}
   914   1343   
   915   1344   	if (request->headers.range.present) {
   916   1345   		if (request->headers.range.offset != 0 || request->headers.range.length >= 0) {
   917   1346   			if (request->headers.range.offset >= fileinfo->len) {
   918   1347   				filed_error_page(fp, date_current, 416, request->method, "range_invalid", log);
   919   1348   
   920   1349   				close(fileinfo->fd);
   921   1350   
   922         -				return;
         1351  +				return(FILED_CONNECTION_CLOSE);
   923   1352   			}
   924   1353   
   925   1354   			if (request->headers.range.length == ((off_t) -1)) {
   926   1355   				filed_log_msg_debug("Computing length to fit in bounds: fileinfo->len = %llu, request->headers.range.offset = %llu",
   927   1356   					(unsigned long long) fileinfo->len,
   928   1357   					(unsigned long long) request->headers.range.offset
   929   1358   				);
................................................................................
   943   1372   		http_code = 200;
   944   1373   
   945   1374   		/* Compute fake range parameters that includes the entire file */
   946   1375   		request->headers.range.offset = 0;
   947   1376   		request->headers.range.length = fileinfo->len;
   948   1377   	}
   949   1378   
   950         -	fprintf(fp, "HTTP/1.1 %i OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: %llu\r\nAccept-Ranges: bytes\r\nContent-Type: %s\r\nConnection: close\r\nETag: \"%s\"\r\n",
         1379  +	fprintf(fp, "HTTP/1.1 %i OK\r\nDate: %s\r\nServer: filed\r\nLast-Modified: %s\r\nContent-Length: %llu\r\nAccept-Ranges: bytes\r\nContent-Type: %s\r\nConnection: %s\r\nETag: \"%s\"\r\n",
   951   1380   		http_code,
   952   1381   		date_current,
   953   1382   		fileinfo->lastmod,
   954   1383   		(unsigned long long) request->headers.range.length,
   955   1384   		fileinfo->type,
         1385  +		filed_connection_str(request->headers.connection),
   956   1386   		fileinfo->etag
   957   1387   	);
   958   1388   
   959   1389   	if (http_code == 206) {
   960   1390   		fprintf(fp, "Content-Range: bytes %llu-%llu/%llu\r\n",
   961   1391   			(unsigned long long) request->headers.range.offset,
   962   1392   			(unsigned long long) (request->headers.range.offset + request->headers.range.length - 1),
................................................................................
  1041   1471   	log->endtime = (time_t) -1;
  1042   1472   	log->sent_length = sendfile_sent;
  1043   1473   
  1044   1474   	filed_log_entry(log);
  1045   1475   
  1046   1476   	close(fileinfo->fd);
  1047   1477   
  1048         -	fclose(fp);
         1478  +	if (request->headers.connection != FILED_CONNECTION_KEEP_ALIVE) {
         1479  +		filed_sockettimeout_close(fd, 0);
  1049   1480   
  1050         -	return;
         1481  +		fclose(fp);
         1482  +
         1483  +		return(FILED_CONNECTION_CLOSE);
         1484  +	}
         1485  +
         1486  +	filed_sockettimeout_processing_end(fd);
         1487  +
         1488  +	return(FILED_CONNECTION_KEEP_ALIVE);
  1051   1489   }
  1052   1490   
  1053   1491   /* Handle incoming connections */
  1054   1492   static void *filed_worker_thread(void *arg_v) {
  1055   1493   	struct filed_worker_thread_args *arg;
  1056   1494   	struct filed_http_request request;
  1057   1495   	struct filed_log_entry *log, local_dummy_log;
  1058   1496   	struct filed_options *options;
  1059   1497   	struct sockaddr_in6 addr;
  1060   1498   	socklen_t addrlen;
  1061   1499   	int failure_count = 0, max_failure_count = FILED_MAX_FAILURE_COUNT;
  1062         -	int master_fd, fd;
         1500  +	int connection_state = FILED_CONNECTION_CLOSE;
         1501  +	int master_fd, fd = -1;
  1063   1502   
  1064   1503   	/* Read arguments */
  1065   1504   	arg = arg_v;
  1066   1505   
  1067   1506   	master_fd = arg->fd;
  1068   1507   	options = &arg->options;
  1069   1508   
................................................................................
  1079   1518   			filed_log_msg("ALLOCATE_LOG_MSG_FAILED");
  1080   1519   
  1081   1520   			break;
  1082   1521   		}
  1083   1522   
  1084   1523   		log->type = FILED_LOG_TYPE_TRANSFER;
  1085   1524   
  1086         -		/* Accept a new client */
  1087         -		addrlen = sizeof(addr);
  1088         -		fd = accept(master_fd, (struct sockaddr *) &addr, &addrlen);
         1525  +		/* If we closed the old connection, accept a new one */
         1526  +		if (connection_state == FILED_CONNECTION_CLOSE) {
         1527  +			/* Accept a new client */
         1528  +			addrlen = sizeof(addr);
         1529  +
         1530  +			fd = accept(master_fd, (struct sockaddr *) &addr, &addrlen);
         1531  +		}
  1089   1532   
  1090   1533   		/*
  1091   1534   		 * If we fail, make a note of it so we don't go into a loop of
  1092   1535   		 * accept() failing
  1093   1536   		 */
  1094   1537   		if (fd < 0) {
  1095   1538   			/* Log the new connection */
  1096   1539   			filed_log_msg("ACCEPT_FAILED");
  1097   1540   
  1098   1541   			failure_count++;
  1099   1542   
  1100         -			free(log);
         1543  +			filed_log_free(log);
  1101   1544   
  1102   1545   			continue;
  1103   1546   		}
         1547  +
         1548  +		filed_sockettimeout_accept(fd);
  1104   1549   
  1105   1550   		/* Fill in log structure */
  1106   1551   		if (filed_log_ip((struct sockaddr *) &addr, log->ip, sizeof(log->ip)) == NULL) {
  1107   1552   			log->ip[0] = '\0';
  1108   1553   			log->port = 0;
  1109   1554   		} else {
  1110   1555   			log->port = addr.sin6_port;
  1111   1556   		}
  1112   1557   
  1113   1558   		/* Reset failure count*/
  1114   1559   		failure_count = 0;
  1115   1560   
  1116   1561   		/* Handle socket */
  1117         -		filed_handle_client(fd, &request, log, options);
         1562  +		connection_state = filed_handle_client(fd, &request, log, options);
  1118   1563   	}
  1119   1564   
  1120   1565   	/* Report error */
  1121   1566   	filed_log_msg("THREAD_DIED ABNORMAL");
  1122   1567   
  1123   1568   	return(NULL);
  1124   1569   
................................................................................
  1329   1774   
  1330   1775   	return(0);
  1331   1776   }
  1332   1777   
  1333   1778   /* Run process */
  1334   1779   int main(int argc, char **argv) {
  1335   1780   	struct option options[12];
  1336         -	struct filed_options thread_options;
         1781  +	struct filed_options thread_options = {0};
  1337   1782   	const char *bind_addr = BIND_ADDR, *newroot = NULL, *log_file = LOG_FILE;
  1338   1783   	FILE *log_fp;
  1339   1784   	uid_t user = 0;
  1340   1785   	int port = PORT, thread_count = THREAD_COUNT;
  1341   1786   	int cache_size = CACHE_SIZE;
  1342   1787   	int init_ret, chroot_ret, setuid_ret, lookup_ret, chdir_ret;
  1343   1788   	int setuid_enabled = 0, daemon_enabled = 0;
  1344   1789   	int ch;
  1345   1790   	int fd;
  1346   1791   
  1347         -	/* Set default values */
  1348         -	thread_options.vhosts_enabled = 0;
  1349         -
  1350   1792   	/* Process arguments */
  1351   1793   	filed_getopt_long_setopt(&options[0], "port", required_argument, 'p');
  1352   1794   	filed_getopt_long_setopt(&options[1], "threads", required_argument, 't');
  1353   1795   	filed_getopt_long_setopt(&options[2], "cache", required_argument, 'c');
  1354   1796   	filed_getopt_long_setopt(&options[3], "bind", required_argument, 'b');
  1355   1797   	filed_getopt_long_setopt(&options[4], "user", required_argument, 'u');
  1356   1798   	filed_getopt_long_setopt(&options[5], "root", required_argument, 'r');
................................................................................
  1380   1822   				if (lookup_ret != 0) {
  1381   1823   					filed_print_help(stderr, 0, "Invalid username specified");
  1382   1824   
  1383   1825   					return(1);
  1384   1826   				}
  1385   1827   				break;
  1386   1828   			case 'r':
  1387         -				newroot = strdup(optarg);
         1829  +				filed_path_translate_set_root(newroot, &thread_options, optarg);
  1388   1830   				break;
  1389   1831   			case 'l':
  1390   1832   				log_file = strdup(optarg);
  1391   1833   				break;
  1392   1834   			case 'd':
  1393   1835   				daemon_enabled = 1;
  1394   1836   				break;
................................................................................
  1423   1865   	/* Create listening socket */
  1424   1866   	fd = filed_listen(bind_addr, port);
  1425   1867   	if (fd < 0) {
  1426   1868   		perror("filed_listen");
  1427   1869   
  1428   1870   		return(1);
  1429   1871   	}
         1872  +
         1873  +	/* Initialize timeout structures */
         1874  +	init_ret = filed_sockettimeout_init();
         1875  +	if (init_ret != 0) {
         1876  +		perror("filed_sockettimeout_init");
         1877  +
         1878  +		return(8);
         1879  +	}
  1430   1880   
  1431   1881   	/* Become a daemon */
  1432   1882   	if (daemon_enabled) {
  1433   1883   		init_ret = filed_daemonize();
  1434   1884   		if (init_ret != 0) {
  1435   1885   			perror("filed_daemonize");
  1436   1886   
................................................................................
  1479   1929   	/* Create logging thread */
  1480   1930   	init_ret = filed_logging_thread_init(log_fp);
  1481   1931   	if (init_ret != 0) {
  1482   1932   		perror("filed_logging_thread_init");
  1483   1933   
  1484   1934   		return(4);
  1485   1935   	}
         1936  +
         1937  +	/* Create socket termination thread */
         1938  +	init_ret = filed_sockettimeout_thread_init();
         1939  +	if (init_ret != 0) {
         1940  +		perror("filed_sockettimeout_thread_init");
         1941  +
         1942  +		return(7);
         1943  +	}
  1486   1944   
  1487   1945   	/* Create worker threads */
  1488   1946   	init_ret = filed_worker_threads_init(fd, thread_count, &thread_options);
  1489   1947   	if (init_ret != 0) {
  1490   1948   		perror("filed_worker_threads_init");
  1491   1949   
  1492   1950   		return(5);
  1493   1951   	}
  1494   1952   
  1495   1953   	/* Wait for threads to exit */
  1496   1954   	/* XXX:TODO: Monitor thread usage */
  1497   1955   	while (1) {
  1498         -		sleep(60);
         1956  +		sleep(86400);
  1499   1957   	}
  1500   1958   
  1501   1959   	/* Return in failure */
  1502   1960   	return(2);
  1503   1961   }

Modified generate-mime-types from [64211e4a81] to [bd758d28dd].

    40     40   	set retval [expr {$retval % $mod}]
    41     41   
    42     42   	return $retval
    43     43   
    44     44   }
    45     45   
    46     46   # Read contents of mime types file
    47         -set fd [open $mimeinfofile]
    48         -set mimeinfo [read $fd]
    49         -close $fd
           47  +catch {
           48  +	set fd [open $mimeinfofile]
           49  +	set mimeinfo [read $fd]
           50  +	close $fd
           51  +}
           52  +
           53  +if {![info exists mimeinfo]} {
           54  +	puts stderr "Not using $mimeinfofile, unreadable."
           55  +
           56  +	exit 1
           57  +}
           58  +
           59  +puts stderr "Using $mimeinfofile as mime.types"
    50     60   
    51     61   # Parse into type and extensions pairs
    52     62   foreach line [split $mimeinfo "\n"] {
    53     63   	regsub {#.*} $line {} line
    54     64   	set line [string trim $line]
    55     65   
    56     66   	if {$line == ""} {

Added mime.types version [099ee45806].

            1  +text/html html htm
            2  +test/plain txt text
            3  +video/mp4 mp4 mpg4
            4  +audio/mpeg mp3 mpg3
            5  +application/zip zip