summaryrefslogtreecommitdiffstats
path: root/net/khttpd
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /net/khttpd
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'net/khttpd')
-rw-r--r--net/khttpd/Config.in4
-rw-r--r--net/khttpd/Makefile28
-rw-r--r--net/khttpd/README235
-rw-r--r--net/khttpd/accept.c127
-rw-r--r--net/khttpd/datasending.c235
-rw-r--r--net/khttpd/logging.c95
-rw-r--r--net/khttpd/main.c395
-rw-r--r--net/khttpd/make_times_h.c122
-rw-r--r--net/khttpd/misc.c242
-rw-r--r--net/khttpd/prototypes.h131
-rw-r--r--net/khttpd/rfc.c374
-rw-r--r--net/khttpd/rfc_time.c227
-rw-r--r--net/khttpd/security.c272
-rw-r--r--net/khttpd/security.h12
-rw-r--r--net/khttpd/sockets.c103
-rw-r--r--net/khttpd/structure.h68
-rw-r--r--net/khttpd/sysctl.c320
-rw-r--r--net/khttpd/sysctl.h17
-rw-r--r--net/khttpd/userspace.c248
-rw-r--r--net/khttpd/waitheaders.c303
20 files changed, 3558 insertions, 0 deletions
diff --git a/net/khttpd/Config.in b/net/khttpd/Config.in
new file mode 100644
index 000000000..478e42e1f
--- /dev/null
+++ b/net/khttpd/Config.in
@@ -0,0 +1,4 @@
+#
+# kHTTPd
+#
+tristate 'Kernel httpd acceleration (experimental)' CONFIG_KHTTPD
diff --git a/net/khttpd/Makefile b/net/khttpd/Makefile
new file mode 100644
index 000000000..bd7d5499b
--- /dev/null
+++ b/net/khttpd/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for kHTTPd
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := khttpd.o
+MOD_LIST_NAME := NET_MODULES
+
+M_OBJS := $(O_TARGET)
+O_OBJS := main.o accept.o datasending.o logging.o misc.o rfc.o rfc_time.o security.o \
+ sockets.o sysctl.o userspace.o waitheaders.o
+
+
+
+include $(TOPDIR)/Rules.make
+
+rfc_time.o: times.h
+
+
+make_times_h: make_times_h.c
+ $(HOSTCC) $(HOSTCFLAGS) -o make_times_h make_times_h.c
+
+times.h: make_times_h
+ ./make_times_h
diff --git a/net/khttpd/README b/net/khttpd/README
new file mode 100644
index 000000000..57d974051
--- /dev/null
+++ b/net/khttpd/README
@@ -0,0 +1,235 @@
+=====
+
+kHTTPd - Kernel httpd accelerator
+
+(C) 1999 by Arjan van de Ven
+Licensed under the terms of the GNU General Public License
+
+=====
+
+
+1. Introduction
+---------------
+ kHTTPd is a http-daemon (webserver) for Linux. kHTTPd is different from
+ other webservers in that it runs from within the Linux-kernel as a module
+ (device-driver).
+
+ kHTTPd handles only static (file based) web-pages, and passes all requests
+ for non-static information to a regular userspace-webserver such as Apache or
+ Zeus. The userspace-daemon doesn't have to be altered in any way.
+
+ Static web-pages are not a very complex thing to serve, but these are very
+ important nevertheless, since virtually all images are static, and a large
+ portion of the html-pages are static also. A "regular" webserver has little
+ added value for static pages, it is simply a "copy file to network"-operation.
+ This can be done very efficiently from within the Linux-kernel, for example
+ the nfs (network file system) daemon performs a similar task and also runs
+ in the kernel.
+
+ By "accelerating" the simple case within the kernel, userspace daemons can
+ do what they are very good at: Generating user-specific, dynamic content.
+
+ Note: This document sometimes uses "Apache" instead of "any webserver you
+ ever might want to use", just for reasons of readability.
+
+
+2. Quick Start
+--------------
+
+ 1) compile and load the module
+ 2) configure the module in /proc/sys/net/khttpd if needed
+ 3) echo 1 > /proc/sys/net/khttpd/start
+
+ unloading:
+
+ echo 1 > /proc/sys/net/khttpd/stop
+ echo 1 > /proc/sys/net/khttpd/unload
+ rmmod khttpd
+
+
+
+3. Configuration
+----------------
+
+ Modes of operation
+ ==================
+
+
+ There are two recommended modes of operation:
+
+ 1) "Apache" is main webserver, kHTTPd is assistant
+ clientport -> 80
+ serverport -> 8080 (or whatever)
+
+ 2) kHTTPd is main webserver, "Apache" is assistant
+ clientport -> 8080 (or whatever)
+ serverport -> 80
+
+
+ Configuring kHTTPd
+ ==================
+
+ Before you can start using kHTTPd, you have to configure it. This
+ is done through the /proc filesystem, and can thus be done from inside
+ a script. Most parameters can only be set when kHTTPd is not active.
+
+ The following things need configuration:
+
+ 1) The port where kHTTPd should listen for requests
+ 2) The port (on "localhost") where "Apache" is listening
+ 3) The location of the documents (documentroot)
+ 4) The strings that indicate dynamic content (optional)
+ [ "cgi-bin" is added by default ]
+
+ It is very important that the documentroot for kHTTPd matches the
+ documentroot for the userspace-daemon, as kHTTPd might "redirect"
+ any request to this userspace-daemon.
+
+ A typical script (for the first mode of operation) to do this would
+ look like:
+
+#!/bin/sh
+modprobe khttpd
+echo 80 > /proc/sys/net/khttpd/clientport
+echo 8080 > /proc/sys/net/khttpd/serverport
+echo /var/www > /proc/sys/net/khttpd/documentroot
+echo php3 > /proc/sys/net/khttpd/dynamic
+echo shtml > /proc/sys/net/khttpd/dynamic
+echo 1 > /proc/sys/net/khttpd/start
+
+ For the second mode of operation, this would be:
+
+#!/bin/sh
+modprobe khttpd
+echo 8080 > /proc/sys/net/khttpd/clientport
+echo 80 > /proc/sys/net/khttpd/serverport
+echo /var/www > /proc/sys/net/khttpd/documentroot
+echo php3 > /proc/sys/net/khttpd/dynamic
+echo shtml > /proc/sys/net/khttpd/dynamic
+echo 1 > /proc/sys/net/khttpd/start
+
+ In this case, you also have to change the configuration of the
+ userspace-daemon. For Apache, you do this by changing
+
+ Port 80
+
+ to
+
+ Port 8080
+
+ in /etc/apache/httpd.conf. For security-reasons, you can also change
+
+ BindAddress *
+
+ to
+
+ BindAddress 127.0.0.1
+
+ (in the same file) to prevent outside users from accessing Apache
+ directly.
+
+
+
+ Stopping kHTTPd
+ ===============
+ In order to change the configuration, you should stop kHTTPd by typing
+ echo 1 > /proc/sys/net/khttpd/stop
+ on a command-prompt.
+
+ If you want to unload the module, you should type
+ echo 1 > /proc/sys/net/khttpd/unload
+ after stopping kHTTPd first.
+
+ If this doesn't work fast enough for you (the commands above can wait for
+ a remote connection to close down), you can send the daemons a "HUP"
+ signal after you told them to stop. This will cause the daemon-threads to
+ stop immediately.
+
+ Note that the daemons will restart immediately if they are not told to
+ stop.
+
+
+
+4. Permissions
+--------------
+ The security model of kHTTPd is very strict. It can be, since there is a
+ userspace daemon that can handle the complex exceptions.
+
+ kHTTPd only serves a file if
+
+ 1) There is no "?" in the URL
+ 2) The URL starts with a "/"
+ 3) The file indicated by the URL exists
+ 4) The file is world-readable (*)
+ 5) The file is not a directory, executable or has the Sticky-bit
+ set (*)
+ 6) The URL doesn't contain any "forbidden" substrings such as ".."
+ and "cgi-bin" (*)
+ 7) The mime-type is known (*)
+
+ The items marked with a (*) are configurable through the
+ sysctl-parameters in /proc/sys/net/khttpd.
+
+
+ In all cases where any of the above conditions isn't met, the
+ userspace-daemon is handed the request.
+
+
+
+5. Parameters
+-------------
+ The following parameters are settable through /proc/sys/net/khttpd:
+
+ Name Default Description
+
+ serverport 8080 The port where kHTTPd listens on
+
+ clientport 80 The port of the userspace
+ http-daemon
+
+ threads 2 The number of server-threads. Should
+ be 1 per CPU for small websites, 2
+ per CPU for big (the active files
+ do not fit in the RAM) websites.
+
+ documentroot /var/www the directory where the
+ document-files are
+
+ start 0 Set to 1 to start kHTTPd
+ (this also resets "stop" to 0)
+
+ stop 0 Set to 1 to stop kHTTPd
+ (this also resets "start" to 0)
+
+ unload 0 Set to 1 to prepare kHTTPd for
+ unloading of the module
+
+ sloppymime 0 If set to 1, unknown mime-types are
+ set to text/html. If set to 0,
+ files with unknown mime-types are
+ handled by the userspace daemon
+
+ perm_required S_IROTH Minimum permissions required
+ (for values see "man 2 stat")
+
+ perm_forbid dir+sticky+ Permission-mask with "forbidden"
+ execute permissions.
+ (for values see "man 2 stat")
+
+ dynamic cgi-bin .. Strings that, if they are a subset
+ of the URL, indicate "dynamic
+ content"
+
+ maxconnect 1000 Maximum number of concurrent
+ connections
+
+6. More information
+-------------------
+ More information about the architecture of kHTTPd, the mailinglist and
+ configuration-examples can be found at the kHTTPd homepage:
+
+ http://www.fenrus.demon.nl
+
+ Bugreports, patches, etc can be send to the mailinglist
+ (khttpd-users@zgp.org) or to khttpd@fenrus.demon.nl
+
diff --git a/net/khttpd/accept.c b/net/khttpd/accept.c
new file mode 100644
index 000000000..9c1912c68
--- /dev/null
+++ b/net/khttpd/accept.c
@@ -0,0 +1,127 @@
+/*
+
+kHTTPd -- the next generation
+
+Accept connections
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include "structure.h"
+#include "prototypes.h"
+#include "sysctl.h"
+
+#include <linux/smp_lock.h>
+
+/*
+
+Purpose:
+
+AcceptConnections puts all "accepted" connections in the
+"WaitForHeader" queue.
+
+Return value:
+ The number of accepted connections
+*/
+
+
+int AcceptConnections(const int CPUNR, struct socket *Socket)
+{
+ struct http_request *NewRequest;
+ struct socket *NewSock;
+ int count = 0;
+ int error;
+
+ EnterFunction("AcceptConnections");
+
+ if (atomic_read(&ConnectCount)>sysctl_khttpd_maxconnect)
+ {
+ LeaveFunction("AcceptConnections - to many active connections");
+ return 0;
+ }
+
+ if (Socket==NULL) return 0;
+
+ /*
+ Quick test to see if there are connections on the queue.
+ This is cheaper than accept() itself because this saves us
+ the allocation of a new socket. (Which doesn't seem to be
+ used anyway)
+ */
+ if (Socket->sk->tp_pinfo.af_tcp.syn_wait_queue==NULL)
+ {
+ return 0;
+ }
+
+ error = 0;
+ while (error>=0)
+ {
+ NewSock = sock_alloc();
+ if (NewSock==NULL)
+ break;
+
+
+ NewSock->type = Socket->type;
+ NewSock->ops = Socket->ops;
+
+
+ error = Socket->ops->accept(Socket,NewSock,O_NONBLOCK);
+
+
+ if (error<0)
+ {
+ sock_release(NewSock);
+ break;
+ }
+
+ if (NewSock->sk->state==TCP_CLOSE)
+ {
+ sock_release(NewSock);
+ continue;
+ }
+
+ /* Allocate a request-entry for the connection */
+ NewRequest = kmalloc(sizeof(struct http_request),(int)GFP_KERNEL);
+
+ if (NewRequest == NULL)
+ {
+ Send50x(NewSock); /* Service not available. Try again later */
+ sock_release(NewSock);
+ break;
+ }
+ memset(NewRequest,0,sizeof(struct http_request));
+
+ NewRequest->sock = NewSock;
+
+ NewRequest->Next = threadinfo[CPUNR].WaitForHeaderQueue;
+
+ init_waitqueue_entry(&NewRequest->sleep,current);
+
+ add_wait_queue(NewSock->sk->sleep,&(NewRequest->sleep));
+
+ threadinfo[CPUNR].WaitForHeaderQueue = NewRequest;
+
+ atomic_inc(&ConnectCount);
+
+
+ count++;
+ }
+
+ LeaveFunction("AcceptConnections");
+ return count;
+}
diff --git a/net/khttpd/datasending.c b/net/khttpd/datasending.c
new file mode 100644
index 000000000..d435af64d
--- /dev/null
+++ b/net/khttpd/datasending.c
@@ -0,0 +1,235 @@
+/*
+
+kHTTPd -- the next generation
+
+Send actual file-data to the connections
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+/*
+
+Purpose:
+
+DataSending does the actual sending of file-data to the socket.
+
+Note: Since asynchronous reads do not -yet- exists, this might block!
+
+Return value:
+ The number of requests that changed status (ie: made some progress)
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/locks.h>
+#include <linux/skbuff.h>
+
+#include <net/tcp.h>
+
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+
+#include "structure.h"
+#include "prototypes.h"
+
+static char *Block[CONFIG_KHTTPD_NUMCPU];
+
+/*
+
+This send_actor is for use with do_generic_file_read (ie sendfile())
+It sends the data to the socket indicated by desc->buf.
+
+*/
+static int sock_send_actor(read_descriptor_t * desc, const char *area, unsigned long size)
+{
+ int written;
+ unsigned long count = desc->count;
+ struct socket *sock = (struct socket *) desc->buf;
+
+ if (size > count)
+ size = count;
+ written = SendBuffer_async(sock,(char *)area,size);
+
+ if (written < 0) {
+ desc->error = written;
+ written = 0;
+ }
+ desc->count = count - written;
+ desc->written += written;
+ return written;
+}
+
+
+
+
+int DataSending(const int CPUNR)
+{
+ struct http_request *CurrentRequest,**Prev;
+ int count = 0;
+
+ EnterFunction("DataSending");
+
+ Prev = &(threadinfo[CPUNR].DataSendingQueue);
+ CurrentRequest = threadinfo[CPUNR].DataSendingQueue;
+ while (CurrentRequest!=NULL)
+ {
+ int ReadSize,Space;
+ int retval;
+
+
+ /* First, test if the socket has any buffer-space left.
+ If not, no need to actually try to send something. */
+
+
+ Space = sock_wspace(CurrentRequest->sock->sk);
+
+ ReadSize = min(4*4096,CurrentRequest->FileLength - CurrentRequest->BytesSent);
+ ReadSize = min(ReadSize , Space );
+
+ if (ReadSize>0)
+ {
+ struct inode *inode;
+
+ inode = CurrentRequest->filp->f_dentry->d_inode;
+
+ if ( (inode!=NULL)&&(inode->i_op!=NULL)&&(inode->i_op->readpage!=NULL))
+ {
+ /* This does the actual transfer using sendfile */
+ read_descriptor_t desc;
+ loff_t *ppos;
+
+ CurrentRequest->filp->f_pos = CurrentRequest->BytesSent;
+
+ ppos = &CurrentRequest->filp->f_pos;
+
+ desc.written = 0;
+ desc.count = ReadSize;
+ desc.buf = (char *) CurrentRequest->sock;
+ desc.error = 0;
+ do_generic_file_read(CurrentRequest->filp, ppos, &desc, sock_send_actor);
+ if (desc.written>0)
+ {
+ CurrentRequest->BytesSent += desc.written;
+ count++;
+ }
+ }
+ else /* FS doesn't support sendfile() */
+ {
+ mm_segment_t oldfs;
+ CurrentRequest->filp->f_pos = CurrentRequest->BytesSent;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ retval = CurrentRequest->filp->f_op->read(CurrentRequest->filp, Block[CPUNR], ReadSize, &CurrentRequest->filp->f_pos);
+ set_fs(oldfs);
+
+ if (retval>0)
+ {
+ retval = SendBuffer_async(CurrentRequest->sock,Block[CPUNR],(size_t)retval);
+ if (retval>0)
+ {
+ CurrentRequest->BytesSent += retval;
+ count++;
+ }
+ }
+ }
+
+ }
+
+ /*
+ If end-of-file or closed connection: Finish this request
+ by moving it to the "logging" queue.
+ */
+ if ((CurrentRequest->BytesSent>=CurrentRequest->FileLength)||
+ (CurrentRequest->sock->sk->state!=TCP_ESTABLISHED
+ && CurrentRequest->sock->sk->state!=TCP_CLOSE_WAIT))
+ {
+ struct http_request *Next;
+ Next = CurrentRequest->Next;
+
+ lock_sock(CurrentRequest->sock->sk);
+ if (CurrentRequest->sock->sk->state == TCP_ESTABLISHED ||
+ CurrentRequest->sock->sk->state == TCP_CLOSE_WAIT)
+ {
+ CurrentRequest->sock->sk->nonagle = 0;
+ tcp_push_pending_frames(CurrentRequest->sock->sk,&(CurrentRequest->sock->sk->tp_pinfo.af_tcp));
+ }
+ release_sock(CurrentRequest->sock->sk);
+
+ (*Prev) = CurrentRequest->Next;
+
+ CurrentRequest->Next = threadinfo[CPUNR].LoggingQueue;
+ threadinfo[CPUNR].LoggingQueue = CurrentRequest;
+
+ CurrentRequest = Next;
+ continue;
+
+ }
+
+
+ Prev = &(CurrentRequest->Next);
+ CurrentRequest = CurrentRequest->Next;
+ }
+
+ LeaveFunction("DataSending");
+ return count;
+}
+
+int InitDataSending(int ThreadCount)
+{
+ int I,I2;
+
+ EnterFunction("InitDataSending");
+ I=0;
+ while (I<ThreadCount)
+ {
+ Block[I] = (char*)get_free_page((int)GFP_KERNEL);
+ if (Block[I] == NULL)
+ {
+ I2=0;
+ while (I2<I-1)
+ {
+ free_page((unsigned long)Block[I2++]);
+ }
+ LeaveFunction("InitDataSending - abort");
+ return -1;
+ }
+ I++;
+ }
+ LeaveFunction("InitDataSending");
+ return 0;
+}
+
+void StopDataSending(const int CPUNR)
+{
+ struct http_request *CurrentRequest,*Next;
+
+ EnterFunction("StopDataSending");
+ CurrentRequest = threadinfo[CPUNR].DataSendingQueue;
+
+ while (CurrentRequest!=NULL)
+ {
+ Next = CurrentRequest->Next;
+ CleanUpRequest(CurrentRequest);
+ CurrentRequest=Next;
+ }
+
+ threadinfo[CPUNR].DataSendingQueue = NULL;
+
+ free_page( (unsigned long)Block[CPUNR]);
+ LeaveFunction("StopDataSending");
+}
diff --git a/net/khttpd/logging.c b/net/khttpd/logging.c
new file mode 100644
index 000000000..229116788
--- /dev/null
+++ b/net/khttpd/logging.c
@@ -0,0 +1,95 @@
+/*
+
+kHTTPd -- the next generation
+
+logging.c takes care of shutting down a connection.
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/smp_lock.h>
+#include <net/tcp.h>
+#include <asm/uaccess.h>
+#include "structure.h"
+#include "prototypes.h"
+
+/*
+
+Purpose:
+
+Logging() terminates "finished" connections and will eventually log them to a
+userspace daemon.
+
+Return value:
+ The number of requests that changed status, thus the number of connections
+ that shut down.
+*/
+
+
+int Logging(const int CPUNR)
+{
+ struct http_request *CurrentRequest,*Req;
+ int count = 0;
+
+ EnterFunction("Logging");
+
+ CurrentRequest = threadinfo[CPUNR].LoggingQueue;
+
+ /* For now, all requests are removed immediatly, but this changes
+ when userspace-logging is added. */
+
+ while (CurrentRequest!=NULL)
+ {
+
+ Req = CurrentRequest->Next;
+
+ CleanUpRequest(CurrentRequest);
+
+ threadinfo[CPUNR].LoggingQueue = Req;
+
+ CurrentRequest = Req;
+
+ count++;
+
+ }
+
+ LeaveFunction("Logging");
+ return count;
+}
+
+
+
+void StopLogging(const int CPUNR)
+{
+ struct http_request *CurrentRequest,*Next;
+
+ EnterFunction("StopLogging");
+ CurrentRequest = threadinfo[CPUNR].LoggingQueue;
+
+ while (CurrentRequest!=NULL)
+ {
+ Next=CurrentRequest->Next;
+ CleanUpRequest(CurrentRequest);
+ CurrentRequest=Next;
+ }
+
+ threadinfo[CPUNR].LoggingQueue = NULL;
+ LeaveFunction("StopLogging");
+}
diff --git a/net/khttpd/main.c b/net/khttpd/main.c
new file mode 100644
index 000000000..5746bc4f7
--- /dev/null
+++ b/net/khttpd/main.c
@@ -0,0 +1,395 @@
+/*
+
+kHTTPd -- the next generation
+
+Main program
+
+
+kHTTPd TNG consists of 1 thread, this main-thread handles ALL connections
+simultanious. It does this by keeping queues with the requests in different
+stages.
+
+The stages are
+
+<not accepted> - TCP/IP connection is not accepted yet
+WaitForHeaders - Connection is accepted, waiting for headers
+DataSending - Headers decoded, sending file-data
+Userspace - Requires userspace daemon
+Logging - The request is finished, cleanup and logging
+
+A typical flow for a request would be:
+
+<not accepted>
+WaitForHeaders
+DataSending
+Logging
+
+or
+
+<not accepted>
+WaitForHeaders
+Userspace
+
+
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/smp_lock.h>
+#include <asm/unistd.h>
+
+#include "structure.h"
+#include "prototypes.h"
+#include "sysctl.h"
+
+struct khttpd_threadinfo threadinfo[CONFIG_KHTTPD_NUMCPU]; /* The actual work-queues */
+
+
+atomic_t ConnectCount;
+atomic_t DaemonCount;
+
+static int ActualThreads; /* The number of actual, active threads */
+
+
+static int ConnectionsPending(int CPUNR)
+{
+ if (threadinfo[CPUNR].DataSendingQueue!=NULL) return O_NONBLOCK;
+ if (threadinfo[CPUNR].WaitForHeaderQueue!=NULL) return O_NONBLOCK;
+ if (threadinfo[CPUNR].LoggingQueue!=NULL) return O_NONBLOCK;
+ if (threadinfo[CPUNR].UserspaceQueue!=NULL) return O_NONBLOCK;
+ return 0;
+}
+
+
+
+static wait_queue_head_t DummyWQ[CONFIG_KHTTPD_NUMCPU];
+static atomic_t Running[CONFIG_KHTTPD_NUMCPU];
+
+static int MainDaemon(void *cpu_pointer)
+{
+ int CPUNR;
+ sigset_t tmpsig;
+
+ DECLARE_WAITQUEUE(main_wait,current);
+
+ MOD_INC_USE_COUNT;
+
+
+ current->state |= TASK_EXCLUSIVE;
+
+ CPUNR=0;
+ if (cpu_pointer!=NULL)
+ CPUNR=(int)*(int*)cpu_pointer;
+
+ sprintf(current->comm,"khttpd - %i",CPUNR);
+ lock_kernel(); /* This seems to be required for exit_mm */
+ exit_mm(current);
+
+ init_waitqueue_head(&(DummyWQ[CPUNR]));
+
+
+ /* Block all signals except SIGKILL, SIGSTOP and SIGHUP */
+ spin_lock_irq(&current->sigmask_lock);
+ tmpsig = current->blocked;
+ siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)| sigmask(SIGHUP));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+
+ if (MainSocket->sk==NULL)
+ return 0;
+ add_wait_queue_exclusive(MainSocket->sk->sleep,&(main_wait));
+ atomic_inc(&DaemonCount);
+ atomic_set(&Running[CPUNR],1);
+
+ while (sysctl_khttpd_stop==0)
+ {
+ int changes = 0;
+
+
+
+ changes +=AcceptConnections(CPUNR,MainSocket);
+ if (ConnectionsPending(CPUNR))
+ {
+ changes +=WaitForHeaders(CPUNR);
+ changes +=DataSending(CPUNR);
+ changes +=Userspace(CPUNR);
+ changes +=Logging(CPUNR);
+ /* Test for incomming connections _again_, because it is possible
+ one came in during the other steps, and the wakeup doesn't happen
+ then.
+ */
+ changes +=AcceptConnections(CPUNR,MainSocket);
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE|TASK_EXCLUSIVE);
+ if (changes==0)
+ {
+ (void)interruptible_sleep_on_timeout(&(DummyWQ[CPUNR]),1);
+ if (CPUNR==0)
+ UpdateCurrentDate();
+ }
+
+ if (signal_pending(current)!=0)
+ {
+ (void)printk(KERN_NOTICE "kHTTPd: Ring Ring - signal received\n");
+ break;
+ }
+
+ }
+
+ remove_wait_queue(MainSocket->sk->sleep,&(main_wait));
+
+ StopWaitingForHeaders(CPUNR);
+ StopDataSending(CPUNR);
+ StopUserspace(CPUNR);
+ StopLogging(CPUNR);
+
+ atomic_set(&Running[CPUNR],0);
+ atomic_dec(&DaemonCount);
+ (void)printk(KERN_NOTICE "kHTTPd: Daemon %i has ended\n",CPUNR);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int CountBuf[CONFIG_KHTTPD_NUMCPU];
+
+static int errno;
+inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options);
+
+
+
+/*
+
+The ManagementDaemon has a very simple task: Start the real daemons when the user wants us
+to, and cleanup when the users wants to unload the module.
+
+Initially, kHTTPd didn't have this thread, but it is the only way to have "delayed activation",
+a feature required to prevent accidental activations resulting in unexpected backdoors.
+
+*/
+static int ManagementDaemon(void *unused)
+{
+ sigset_t tmpsig;
+ int waitpid_result;
+
+ DECLARE_WAIT_QUEUE_HEAD(WQ);
+
+ MOD_INC_USE_COUNT;
+
+ sprintf(current->comm,"khttpd manager");
+ lock_kernel(); /* This seems to be required for exit_mm */
+ exit_mm(current);
+
+
+ /* Block all signals except SIGKILL and SIGSTOP */
+ spin_lock_irq(&current->sigmask_lock);
+ tmpsig = current->blocked;
+ siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) );
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+
+ /* main loop */
+ while (sysctl_khttpd_unload==0)
+ {
+ int I;
+
+
+ /* First : wait for activation */
+
+ sysctl_khttpd_start = 0;
+
+ while ( (sysctl_khttpd_start==0) && (!signal_pending(current)) && (sysctl_khttpd_unload==0) )
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ interruptible_sleep_on_timeout(&WQ,HZ);
+ }
+
+ if ( (signal_pending(current)) || (sysctl_khttpd_unload!=0) )
+ break;
+
+ /* Then start listening and spawn the daemons */
+
+ if (StartListening(sysctl_khttpd_serverport)==0)
+ {
+ continue;
+ }
+
+ ActualThreads = sysctl_khttpd_threads;
+ if (ActualThreads<1)
+ ActualThreads = 1;
+
+ if (ActualThreads>CONFIG_KHTTPD_NUMCPU)
+ ActualThreads = CONFIG_KHTTPD_NUMCPU;
+
+ /* Write back the actual value */
+
+ sysctl_khttpd_threads = ActualThreads;
+
+ InitUserspace(ActualThreads);
+
+ if (InitDataSending(ActualThreads)!=0)
+ {
+ StopListening();
+ continue;
+ }
+ if (InitWaitHeaders(ActualThreads)!=0)
+ {
+ I=0;
+ while (I<ActualThreads)
+ {
+ StopDataSending(I);
+ I++;
+ }
+ StopListening();
+ continue;
+ }
+
+ /* Clean all queues */
+ memset(threadinfo, 0, sizeof(struct khttpd_threadinfo));
+
+
+
+ I=0;
+ while (I<ActualThreads)
+ {
+ atomic_set(&Running[I],1);
+ (void)kernel_thread(MainDaemon,&(CountBuf[I]),0);
+ I++;
+ }
+
+ /* Then wait for deactivation */
+ sysctl_khttpd_stop = 0;
+
+ while ( (sysctl_khttpd_stop==0) && (!signal_pending(current)) && (sysctl_khttpd_unload==0) )
+ {
+ if (atomic_read(&DaemonCount)<ActualThreads)
+ {
+ I=0;
+ while (I<ActualThreads)
+ {
+ if (atomic_read(&Running[I])==0)
+ {
+ atomic_set(&Running[I],1);
+ (void)kernel_thread(MainDaemon,&(CountBuf[I]),0);
+ (void)printk(KERN_CRIT "kHTTPd: Restarting daemon %i \n",I);
+ }
+ I++;
+ }
+ }
+ interruptible_sleep_on_timeout(&WQ,HZ);
+
+ /* reap the daemons */
+ waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
+
+ }
+
+
+ /* The user wants us to stop. So stop listening on the socket. */
+ if (sysctl_khttpd_stop!=0)
+ {
+ /* Wait for the daemons to stop, one second per iteration */
+ while (atomic_read(&DaemonCount)>0)
+ interruptible_sleep_on_timeout(&WQ,HZ);
+ StopListening();
+ }
+
+
+
+ }
+
+ sysctl_khttpd_stop = 1;
+
+ /* Wait for the daemons to stop, one second per iteration */
+ while (atomic_read(&DaemonCount)>0)
+ interruptible_sleep_on_timeout(&WQ,HZ);
+
+
+ waitpid_result = 1;
+ /* reap the zombie-daemons */
+ while (waitpid_result>0)
+ waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG);
+
+ StopListening();
+
+
+ (void)printk(KERN_NOTICE "kHTTPd: Management daemon stopped. \n You can unload the module now.\n");
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+int __init khttpd_init(void)
+{
+ int I;
+
+ I=0;
+ while (I<CONFIG_KHTTPD_NUMCPU)
+ {
+ CountBuf[I]=I;
+
+ I++;
+ }
+
+ atomic_set(&ConnectCount,0);
+ atomic_set(&DaemonCount,0);
+
+
+ /* Maybe the mime-types will be set-able through sysctl in the future */
+
+ AddMimeType(".htm","text/html");
+ AddMimeType("html","text/html");
+ AddMimeType(".gif","image/gif");
+ AddMimeType(".jpg","image/jpeg");
+ AddMimeType(".png","image/png");
+ AddMimeType("tiff","image/tiff");
+ AddMimeType(".zip","application/zip");
+ AddMimeType(".pdf","application/pdf");
+ AddMimeType("r.gz","application/x-gtar");
+ AddMimeType(".tgz","application/x-gtar");
+ AddMimeType(".deb","application/x-debian-package");
+ AddMimeType("lass","application/x-java");
+ AddMimeType(".mp3","audio/mpeg");
+ AddMimeType(".txt","text/plain");
+
+ AddDynamicString("..");
+ AddDynamicString("cgi-bin");
+
+ StartSysctl();
+
+ (void)kernel_thread(ManagementDaemon,NULL,0);
+
+ return 0;
+}
+
+void khttpd_cleanup(void)
+{
+ EndSysctl();
+}
+
+ module_init(khttpd_init)
+ module_exit(khttpd_cleanup)
diff --git a/net/khttpd/make_times_h.c b/net/khttpd/make_times_h.c
new file mode 100644
index 000000000..84a9d3c00
--- /dev/null
+++ b/net/khttpd/make_times_h.c
@@ -0,0 +1,122 @@
+/*
+
+This program generates the "times.h" file with the zulu-times of the first of
+every month of a decade.
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include <time.h>
+#include <stdio.h>
+
+static time_t GetDay(int D,int M,int Y)
+{
+ struct tm TM;
+
+ TM.tm_sec = 0;
+ TM.tm_min = 0;
+ TM.tm_hour = 0;
+ TM.tm_mday = D;
+ TM.tm_mon = M;
+ TM.tm_wday = 0;
+ TM.tm_yday = 0;
+ TM.tm_year = Y-1900;
+ TM.tm_isdst = 0;
+
+ return mktime(&TM);
+
+}
+static int WeekGetDay(int D,int M,int Y)
+{
+ struct tm TM;
+
+ TM.tm_sec = 0;
+ TM.tm_min = 0;
+ TM.tm_hour = 0;
+ TM.tm_mday = D;
+ TM.tm_mon = M;
+ TM.tm_year = Y-1900;
+ TM.tm_isdst = 0;
+ TM.tm_wday = 0;
+ TM.tm_yday = 0;
+
+ (void)mktime(&TM);
+
+ return TM.tm_wday;
+
+}
+
+int main(void)
+{
+ int M,Y;
+ FILE *file;
+
+ file=fopen("times.h","w");
+
+ if (file==NULL)
+ return 0;
+
+ fprintf(file,"static time_t TimeDays[10][13] = { \n");
+
+ Y=1997;
+ while (Y<2007)
+ {
+ M=0;
+ fprintf(file," { ");
+ while (M<12)
+ {
+ fprintf(file,"%i",(int)GetDay(1,M,Y));
+ fprintf(file,",\t");
+
+ M++;
+ }
+
+ fprintf(file,"%i } ",(int)GetDay(1,0,Y+1));
+ if (Y!=2006) fprintf(file,",");
+ fprintf(file,"\n");
+ Y++;
+ }
+ fprintf(file,"};\n");
+
+ fprintf(file,"static int WeekDays[10][13] = { \n");
+
+ Y=1997;
+ while (Y<2007)
+ {
+ M=0;
+ fprintf(file," { ");
+ while (M<12)
+ {
+ fprintf(file,"%i",(int)WeekGetDay(1,M,Y));
+ fprintf(file,",\t");
+
+ M++;
+ }
+
+ fprintf(file,"%i } ",(int)WeekGetDay(1,0,Y+1));
+ if (Y!=2006) fprintf(file,",");
+ fprintf(file,"\n");
+ Y++;
+ }
+ fprintf(file,"};\n");
+ fprintf(file,"#define KHTTPD_YEAROFFSET 1997\n");
+ fprintf(file,"#define KHTTPD_NUMYEARS 10\n");
+ (void)fclose(file);
+
+ return 0;
+}
diff --git a/net/khttpd/misc.c b/net/khttpd/misc.c
new file mode 100644
index 000000000..b459b51fe
--- /dev/null
+++ b/net/khttpd/misc.c
@@ -0,0 +1,242 @@
+/*
+
+kHTTPd -- the next generation
+
+General functions
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include <linux/kernel.h>
+
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/unistd.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+
+#include <asm/atomic.h>
+#include <asm/errno.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+#include "structure.h"
+#include "prototypes.h"
+
+#ifndef ECONNRESET
+#define ECONNRESET 102
+#endif
+
+
+/*
+
+Readrest reads and discards all pending input on a socket. This is required
+before closing the socket.
+
+*/
+static void ReadRest(struct socket *sock)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ int len;
+
+ mm_segment_t oldfs;
+
+
+ EnterFunction("ReadRest");
+
+
+ if (sock->sk==NULL)
+ return;
+
+ len = 1;
+
+ while (len>0)
+ {
+ static char Buffer[1024]; /* Never read, so doesn't need to
+ be SMP safe */
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_DONTWAIT;
+
+ msg.msg_iov->iov_base = &Buffer[0];
+ msg.msg_iov->iov_len = (__kernel_size_t)1024;
+
+ len = 0;
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock_recvmsg(sock,&msg,1024,MSG_DONTWAIT);
+ set_fs(oldfs);
+ }
+ LeaveFunction("ReadRest");
+}
+
+
+/*
+
+CleanUpRequest takes care of shutting down the connection, closing the file-pointer
+and releasing the memory of the request-structure. Do not try to access it afterwards!
+
+*/
+void CleanUpRequest(struct http_request *Req)
+{
+ EnterFunction("CleanUpRequest");
+
+ /* Close the socket ....*/
+ if ((Req->sock!=NULL)&&(Req->sock->sk!=NULL))
+ {
+ ReadRest(Req->sock);
+ remove_wait_queue(Req->sock->sk->sleep,&(Req->sleep));
+ sock_release(Req->sock);
+ }
+
+ /* ... and the file-pointer ... */
+ if (Req->filp!=NULL)
+ {
+ fput(Req->filp);
+ Req->filp = NULL;
+ }
+
+
+ /* ... and release the memory for the structure. */
+ kfree(Req);
+
+ atomic_dec(&ConnectCount);
+ LeaveFunction("CleanUpRequest");
+}
+
+
+/*
+
+SendBuffer and Sendbuffer_async send "Length" bytes from "Buffer" to the "sock"et.
+The _async-version is non-blocking.
+
+A positive return-value indicates the number of bytes sent, a negative value indicates
+an error-condition.
+
+*/
+int SendBuffer(struct socket *sock, const char *Buffer,const size_t Length)
+{
+ struct msghdr msg;
+ mm_segment_t oldfs;
+ struct iovec iov;
+ int len;
+
+ EnterFunction("SendBuffer");
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_NOSIGNAL;
+ msg.msg_iov->iov_len = (__kernel_size_t)Length;
+ msg.msg_iov->iov_base = (char*) Buffer;
+
+
+ len = 0;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock_sendmsg(sock,&msg,(size_t)(Length-len));
+ set_fs(oldfs);
+ LeaveFunction("SendBuffer");
+ return len;
+}
+
+int SendBuffer_async(struct socket *sock, const char *Buffer,const size_t Length)
+{
+ struct msghdr msg;
+ mm_segment_t oldfs;
+ struct iovec iov;
+ int len;
+
+ EnterFunction("SendBuffer_async");
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL;
+ msg.msg_iov->iov_base = (char*) Buffer;
+ msg.msg_iov->iov_len = (__kernel_size_t)Length;
+
+
+ if (sock->sk)
+ {
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock_sendmsg(sock,&msg,(size_t)(Length));
+ set_fs(oldfs);
+ } else
+ {
+ return -ECONNRESET;
+ }
+
+ LeaveFunction("SendBuffer_async");
+ return len;
+}
+
+
+
+
+/*
+
+HTTP header shortcuts. Hardcoded since these might be called in a low-memory
+situation, and they don't change anyhow.
+
+*/
+
+static char NoPerm[] = "HTTP/1.0 403 Forbidden\r\nServer: kHTTPd 0.1.6\r\n\r\n";
+static char TryLater[] = "HTTP/1.0 503 Service Unavailable\r\nServer: kHTTPd 0.1.6\r\nContent-Length: 15\r\n\r\nTry again later";
+static char NotModified[] = "HTTP/1.0 304 Not Modified\r\nServer: kHTTPd 0.1.6\r\n\r\n";
+
+
+void Send403(struct socket *sock)
+{
+ EnterFunction("Send403");
+ (void)SendBuffer(sock,NoPerm,strlen(NoPerm));
+ LeaveFunction("Send403");
+}
+
+void Send304(struct socket *sock)
+{
+ EnterFunction("Send304");
+ (void)SendBuffer(sock,NotModified,strlen(NotModified));
+ LeaveFunction("Send304");
+}
+
+void Send50x(struct socket *sock)
+{
+ EnterFunction("Send50x");
+ (void)SendBuffer(sock,TryLater,strlen(TryLater));
+ LeaveFunction("Send50x");
+}
+
diff --git a/net/khttpd/prototypes.h b/net/khttpd/prototypes.h
new file mode 100644
index 000000000..78e891d8e
--- /dev/null
+++ b/net/khttpd/prototypes.h
@@ -0,0 +1,131 @@
+#ifndef _INCLUDE_GUARD_PROTOTYPES_H
+#define _INCLUDE_GUARD_PROTOTYPES_H
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+
+#include "structure.h"
+
+
+/* General defines and stuff */
+
+
+#define CONFIG_KHTTPD_NUMCPU 16 /* Maximum number of threads */
+
+/* the TCP/IP stack defines a __BROKEN__ set of min/max functions !! */
+/* So we better define our own. */
+
+/* Broken means: working on unsigned data only, which is not acceptable
+ for kHTTPd and probably a lot of other functions. */
+
+#undef min
+#undef max
+#define min(a,b) ( (a) < (b) ? (a) : (b) )
+#define max(a,b) ( (a) > (b) ? (a) : (b) )
+
+#ifdef OOPSTRACE
+#define EnterFunction(x) printk("Enter: %s, %s line %i\n",x,__FILE__,__LINE__)
+#define LeaveFunction(x) printk("Leave: %s, %s line %i\n",x,__FILE__,__LINE__)
+#else
+#define EnterFunction(x) do {} while (0)
+#define LeaveFunction(x) do {} while (0)
+#endif
+
+
+
+/* sockets.c */
+int StartListening(const int Port);
+void StopListening(void);
+
+extern struct socket *MainSocket;
+
+
+/* sysctl.c */
+void StartSysctl(void);
+void EndSysctl(void);
+
+extern int sysctl_khttpd_stop;
+
+
+/* main.c */
+
+
+extern struct khttpd_threadinfo threadinfo[CONFIG_KHTTPD_NUMCPU];
+extern char CurrentTime[];
+extern atomic_t ConnectCount;
+extern struct wait_queue main_wait[CONFIG_KHTTPD_NUMCPU];
+
+/* misc.c */
+
+void CleanUpRequest(struct http_request *Req);
+int SendBuffer(struct socket *sock, const char *Buffer,const size_t Length);
+int SendBuffer_async(struct socket *sock, const char *Buffer,const size_t Length);
+void Send403(struct socket *sock);
+void Send304(struct socket *sock);
+void Send50x(struct socket *sock);
+
+/* accept.c */
+
+int AcceptConnections(const int CPUNR,struct socket *Socket);
+
+/* waitheaders.c */
+
+int WaitForHeaders(const int CPUNR);
+void StopWaitingForHeaders(const int CPUNR);
+int InitWaitHeaders(int ThreadCount);
+
+/* datasending.c */
+
+int DataSending(const int CPUNR);
+void StopDataSending(const int CPUNR);
+int InitDataSending(int ThreadCount);
+
+
+/* userspace.c */
+
+int Userspace(const int CPUNR);
+void StopUserspace(const int CPUNR);
+void InitUserspace(const int CPUNR);
+
+
+/* rfc_time.c */
+
+void time_Unix2RFC(const time_t Zulu,char *Buffer);
+void UpdateCurrentDate(void);
+time_t mimeTime_to_UnixTime(char *Q);
+extern int CurrentTime_i;
+
+/* rfc.c */
+
+void ParseHeader(char *Buffer,const int length, struct http_request *Head);
+char *ResolveMimeType(const char *File,__kernel_size_t *Len);
+void AddMimeType(const char *Ident,const char *Type);
+void SendHTTPHeader(struct http_request *Request);
+
+
+
+/* security.c */
+
+struct file *OpenFileForSecurity(char *Filename);
+void AddDynamicString(const char *String);
+void GetSecureString(char *String);
+
+
+/* logging.c */
+
+int Logging(const int CPUNR);
+void StopLogging(const int CPUNR);
+
+
+/* Other prototypes */
+
+
+
+#endif
diff --git a/net/khttpd/rfc.c b/net/khttpd/rfc.c
new file mode 100644
index 000000000..ff5e25451
--- /dev/null
+++ b/net/khttpd/rfc.c
@@ -0,0 +1,374 @@
+/*
+
+kHTTPd -- the next generation
+
+RFC related functions (headers and stuff)
+
+*/
+
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+
+#include <linux/kernel.h>
+
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/unistd.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+
+#include "prototypes.h"
+#include "structure.h"
+#include "sysctl.h"
+
+
+#define KHTTPD_NUMMIMETYPES 40
+
+static atomic_t MimeCount;
+
+struct MimeType
+{
+ __u32 identifier;
+ char type[64-sizeof(__u32)-sizeof(__kernel_size_t)];
+ __kernel_size_t len;
+};
+
+static struct MimeType MimeTypes[KHTTPD_NUMMIMETYPES];
+
+
+void AddMimeType(const char *Ident,const char *Type)
+{
+ __u32 *I;
+
+ EnterFunction("AddMimeType");
+
+ if (strlen(Ident)!=4)
+ {
+ (void)printk(KERN_ERR "httpd: Only 4-byte mime-identifiers are accepted\n");
+ return;
+ }
+
+ if (strlen(Type)>(64-sizeof(__u32)-sizeof(__kernel_size_t) ) )
+ {
+ (void)printk(KERN_ERR "httpd: Mime-string too long.\n");
+ return;
+ }
+
+ I=(__u32*)Ident;
+
+ /* FIXME: Need to lock-down all access to the mime-structure here */
+ /* For now, just don't add mime-types after initialisation */
+
+
+ MimeTypes[atomic_read(&MimeCount)].identifier=*I;
+ strncpy(MimeTypes[atomic_read(&MimeCount)].type,Type,(64-sizeof(__u32)-sizeof(__kernel_size_t)));
+ MimeTypes[atomic_read(&MimeCount)].len = strlen(Type);
+
+ atomic_inc(&MimeCount);
+ LeaveFunction("AddMimeType");
+}
+
+
+char *ResolveMimeType(const char *File,__kernel_size_t *Len)
+/*
+
+ The returned string is for READ ONLY, ownership of the memory is NOT
+ transfered.
+
+*/
+{
+ __u32 *I;
+ int pos,lc,filelen;
+
+ EnterFunction("ResolveMimeType");
+
+ *Len = 0;
+
+ if (File==NULL)
+ return NULL;
+
+ filelen = (int)strlen(File);
+
+ if (filelen<4)
+ {
+ return NULL;
+ }
+
+ /* The Merced-people are NOT going to like this! So this has to be fixed
+ in a later stage. */
+
+ pos = filelen-4;
+ I=(__u32*)(File+pos);
+
+ lc=0;
+
+ while (lc<atomic_read(&MimeCount))
+ {
+ if (MimeTypes[lc].identifier == *I)
+ {
+ *Len = MimeTypes[lc].len;
+ LeaveFunction("ResolveMimeType - success");
+ return MimeTypes[lc].type;
+ }
+ lc++;
+ }
+
+ if (sysctl_khttpd_sloppymime)
+ {
+ *Len = MimeTypes[0].len;
+ LeaveFunction("ResolveMimeType - unknown");
+ return MimeTypes[0].type;
+ }
+ else
+ {
+ LeaveFunction("ResolveMimeType - failure");
+ return NULL;
+ }
+}
+
+
+static char HeaderPart1[] = "HTTP/1.0 200 OK\r\nServer: kHTTPd/0.1.6\r\nDate: ";
+#ifdef BENCHMARK
+static char HeaderPart1b[] ="HTTP/1.0 200 OK";
+#endif
+static char HeaderPart3[] = "\r\nContent-type: ";
+static char HeaderPart5[] = "\r\nLast-modified: ";
+static char HeaderPart7[] = "\r\nContent-length: ";
+static char HeaderPart9[] = "\r\n\r\n";
+
+#ifdef BENCHMARK
+/* In BENCHMARK-mode, just send the bare essentials */
+void SendHTTPHeader(struct http_request *Request)
+{
+ struct msghdr msg;
+ mm_segment_t oldfs;
+ struct iovec iov[9];
+ int len,len2;
+
+
+ EnterFunction("SendHTTPHeader");
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov[0];
+ msg.msg_iovlen = 6;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0; /* Synchronous for now */
+
+ iov[0].iov_base = HeaderPart1b;
+ iov[0].iov_len = 15;
+ iov[1].iov_base = HeaderPart3;
+ iov[1].iov_len = 16;
+ iov[2].iov_base = Request->MimeType;
+ iov[2].iov_len = Request->MimeLength;
+
+ iov[3].iov_base = HeaderPart7;
+ iov[3].iov_len = 18;
+
+
+ sprintf(Request->LengthS,"%i",Request->FileLength);
+ iov[4].iov_base = Request->LengthS;
+ iov[4].iov_len = strlen(Request->LengthS);
+ iov[5].iov_base = HeaderPart9;
+ iov[5].iov_len = 4;
+
+ len2=15+16+18+iov[2].iov_len+iov[4].iov_len+4;
+
+
+ len = 0;
+
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock_sendmsg(Request->sock,&msg,len2);
+ set_fs(oldfs);
+
+
+ return;
+}
+#else
+void SendHTTPHeader(struct http_request *Request)
+{
+ struct msghdr msg;
+ mm_segment_t oldfs;
+ struct iovec iov[9];
+ int len,len2;
+ __kernel_size_t slen;
+
+ EnterFunction("SendHTTPHeader");
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &(iov[0]);
+ msg.msg_iovlen = 9;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0; /* Synchronous for now */
+
+ iov[0].iov_base = HeaderPart1;
+ iov[0].iov_len = 45;
+ iov[1].iov_base = CurrentTime;
+ iov[1].iov_len = 29;
+ iov[2].iov_base = HeaderPart3;
+ iov[2].iov_len = 16;
+
+ iov[3].iov_base = Request->MimeType;
+ iov[3].iov_len = Request->MimeLength;
+
+ iov[4].iov_base = HeaderPart5;
+ iov[4].iov_len = 17;
+ iov[5].iov_base = &(Request->TimeS[0]);
+ iov[5].iov_len = 29;
+ iov[6].iov_base = HeaderPart7;
+ iov[6].iov_len = 18;
+ iov[7].iov_base = &(Request->LengthS[0]);
+ slen = strlen(Request->LengthS);
+ iov[7].iov_len = slen;
+ iov[8].iov_base = HeaderPart9;
+ iov[8].iov_len = 4;
+
+ len2=45+2*29+16+17+18+slen+4+iov[3].iov_len;
+
+ len = 0;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock_sendmsg(Request->sock,&msg,len2);
+ set_fs(oldfs);
+ LeaveFunction("SendHTTPHeader");
+
+
+ return;
+}
+#endif
+
+
+
+/*
+
+Parse a HTTP-header. Be careful for buffer-overflows here, this is the most important
+place for this, since the remote-user controls the data.
+
+*/
+void ParseHeader(char *Buffer,const int length, struct http_request *Head)
+{
+ char *Endval,*EOL,*tmp;
+
+ EnterFunction("ParseHeader");
+ Endval = Buffer + length;
+
+ /* We want to parse only the first header if multiple headers are present */
+ tmp = strstr(Buffer,"\r\n\r\n");
+ if (tmp!=NULL)
+ Endval = tmp;
+
+
+ while (Buffer<Endval)
+ {
+ if (isspace(Buffer[0]))
+ {
+ Buffer++;
+ continue;
+ }
+
+
+ EOL=strchr(Buffer,'\n');
+
+ if (EOL==NULL) EOL=Endval;
+
+ if (EOL-Buffer<4)
+ {
+ Buffer++;
+ continue;
+ }
+
+ if (strncmp("GET ",Buffer,4)==0)
+ {
+ int PrefixLen;
+ Buffer+=4;
+
+ tmp=strchr(Buffer,' ');
+ if (tmp==0)
+ {
+ tmp=EOL-1;
+ Head->HTTPVER = 9;
+ } else
+ Head->HTTPVER = 10;
+
+ if (tmp>Endval) continue;
+
+ strncpy(Head->FileName,sysctl_khttpd_docroot,sizeof(Head->FileName));
+ PrefixLen = strlen(sysctl_khttpd_docroot);
+ Head->FileNameLength = min(255,tmp-Buffer+PrefixLen);
+
+ strncat(Head->FileName,Buffer,min(255-PrefixLen,tmp-Buffer));
+
+ Buffer=EOL+1;
+#ifdef BENCHMARK
+ break;
+#endif
+ continue;
+ }
+#ifndef BENCHMARK
+ if (strncmp("If-Modified-Since: ",Buffer,19)==0)
+ {
+ Buffer+=19;
+
+ strncpy(Head->IMS,Buffer,min(127,EOL-Buffer-1));
+
+ Buffer=EOL+1;
+ continue;
+ }
+
+ if (strncmp("User-Agent: ",Buffer,12)==0)
+ {
+ Buffer+=12;
+
+ strncpy(Head->Agent,Buffer,min(127,EOL-Buffer-1));
+
+ Buffer=EOL+1;
+ continue;
+ }
+
+
+ if (strncmp("Host: ",Buffer,6)==0)
+ {
+ Buffer+=6;
+
+ strncpy(Head->Host,Buffer,min(127,EOL-Buffer-1));
+
+ Buffer=EOL+1;
+ continue;
+ }
+#endif
+ Buffer = EOL+1; /* Skip line */
+ }
+ LeaveFunction("ParseHeader");
+}
diff --git a/net/khttpd/rfc_time.c b/net/khttpd/rfc_time.c
new file mode 100644
index 000000000..8672cce7c
--- /dev/null
+++ b/net/khttpd/rfc_time.c
@@ -0,0 +1,227 @@
+/*
+
+Functions related to time:
+
+1) rfc (string) time to unix-time
+2) unix-time to rfc (string) time
+3) current time to rfc (string) time for the "Date:" header
+
+*/
+
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include <linux/time.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/ctype.h>
+
+
+#include "times.h"
+#include "prototypes.h"
+static char *dayName[7] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static char *monthName[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+
+char CurrentTime[64];
+int CurrentTime_i;
+
+
+static char itoa_h[60]={'0','0','0','0','0','0','0','0','0','0',
+ '1','1','1','1','1','1','1','1','1','1',
+ '2','2','2','2','2','2','2','2','2','2',
+ '3','3','3','3','3','3','3','3','3','3',
+ '4','4','4','4','4','4','4','4','4','4',
+ '5','5','5','5','5','5','5','5','5','5'};
+
+static char itoa_l[60]={'0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9',
+ '0','1','2','3','4','5','6','7','8','9'};
+void time_Unix2RFC(const time_t Zulu,char *Buffer)
+{
+ int Y=0,M=0,D=0;
+ int H=0,Min=0,S=0,WD=0;
+ int I,I2;
+ time_t rest;
+
+
+
+ I=0;
+ while (I<KHTTPD_NUMYEARS)
+ {
+ if (TimeDays[I][0]>Zulu)
+ break;
+ I++;
+ }
+
+ Y=--I;
+ if (I<0)
+ {
+ Y=0;
+ goto BuildYear;
+ }
+ I2=0;
+ while (I2<=12)
+ {
+ if (TimeDays[I][I2]>Zulu)
+ break;
+ I2++;
+ }
+
+ M=I2-1;
+
+ rest=Zulu - TimeDays[Y][M];
+ WD=WeekDays[Y][M];
+ D=rest/86400;
+ rest=rest%86400;
+ WD+=D;
+ WD=WD%7;
+ H=rest/3600;
+ rest=rest%3600;
+ Min=rest/60;
+ rest=rest%60;
+ S=rest;
+
+BuildYear:
+ Y+=KHTTPD_YEAROFFSET;
+
+
+ /* Format: Day, 01 Mon 1999 01:01:01 GMT */
+
+/*
+ We want to do
+
+ sprintf( Buffer, "%s, %02i %s %04i %02i:%02i:%02i GMT",
+ dayName[ WD ], D+1, monthName[ M ], Y,
+ H, Min, S
+ );
+
+ but this is very expensive. Since the string is fixed length,
+ it is filled manually.
+*/
+ Buffer[0]=dayName[WD][0];
+ Buffer[1]=dayName[WD][1];
+ Buffer[2]=dayName[WD][2];
+ Buffer[3]=',';
+ Buffer[4]=' ';
+ Buffer[5]=itoa_h[D+1];
+ Buffer[6]=itoa_l[D+1];
+ Buffer[7]=' ';
+ Buffer[8]=monthName[M][0];
+ Buffer[9]=monthName[M][1];
+ Buffer[10]=monthName[M][2];
+ Buffer[11]=' ';
+ Buffer[12]=itoa_l[Y/1000];
+ Buffer[13]=itoa_l[(Y/100)%10];
+ Buffer[14]=itoa_l[(Y/10)%10];
+ Buffer[15]=itoa_l[Y%10];
+ Buffer[16]=' ';
+ Buffer[17]=itoa_h[H];
+ Buffer[18]=itoa_l[H];
+ Buffer[19]=':';
+ Buffer[20]=itoa_h[Min];
+ Buffer[21]=itoa_l[Min];
+ Buffer[22]=':';
+ Buffer[23]=itoa_h[S];
+ Buffer[24]=itoa_l[S];
+ Buffer[25]=' ';
+ Buffer[26]='G';
+ Buffer[27]='M';
+ Buffer[28]='T';
+ Buffer[29]=0;
+
+
+
+
+}
+
+void UpdateCurrentDate(void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ if (CurrentTime_i!=tv.tv_sec)
+ time_Unix2RFC(tv.tv_sec,CurrentTime);
+
+ CurrentTime_i = tv.tv_sec;
+}
+
+static int MonthHash[32] = {0,0,7,0,0,0,0,0,0,0,0,3,0,0,0,2,6,0,5,0,9,8,4,0,0,11,1,10,0,0,0,0};
+
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+
+__inline static int skip_atoi(char **s)
+{
+ int i=0;
+
+ while (is_digit(**s))
+ i = i*10 + *((*s)++) - '0';
+ return i;
+}
+
+time_t mimeTime_to_UnixTime(char *Q)
+{
+ int Y,M,D,H,Min,S;
+ unsigned int Hash;
+ time_t Temp;
+ char *s,**s2;
+
+ s=Q;
+ s2=&s;
+
+ if (strlen(s)<30) return 0;
+ if (s[3]!=',') return 0;
+ if (s[19]!=':') return 0;
+
+ s+=5; /* Skip day of week */
+ D = skip_atoi(s2); /* Day of month */
+ s++;
+ Hash = (unsigned char)s[0]+(unsigned char)s[2];
+ Hash = (Hash<<1) + (unsigned char)s[1];
+ Hash = (Hash&63)>>1;
+ M = MonthHash[Hash];
+ s+=4;
+ Y = skip_atoi(s2); /* Year */
+ s++;
+ H = skip_atoi(s2); /* Hour */
+ s++;
+ Min = skip_atoi(s2); /* Minutes */
+ s++;
+ S = skip_atoi(s2); /* Seconds */
+ s++;
+ if ((s[0]!='G')||(s[1]!='M')||(s[2]!='T'))
+ {
+ return 0; /* No GMT */
+ }
+
+ if (Y<KHTTPD_YEAROFFSET) Y = KHTTPD_YEAROFFSET;
+ if (Y>KHTTPD_YEAROFFSET+9) Y = KHTTPD_YEAROFFSET+9;
+
+ Temp = TimeDays[Y-KHTTPD_YEAROFFSET][M];
+ Temp += D*86400+H*3600+Min*60+S;
+
+ return Temp;
+}
diff --git a/net/khttpd/security.c b/net/khttpd/security.c
new file mode 100644
index 000000000..9964f0a24
--- /dev/null
+++ b/net/khttpd/security.c
@@ -0,0 +1,272 @@
+/*
+
+kHTTPd -- the next generation
+
+Permissions/Security functions
+
+*/
+
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+
+#include <linux/kernel.h>
+
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/smp_lock.h>
+#include <linux/un.h>
+#include <linux/unistd.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+#include <linux/file.h>
+
+#include "sysctl.h"
+#include "security.h"
+#include "prototypes.h"
+
+/*
+
+The basic security function answers "Userspace" when any one of the following
+conditions is met:
+
+1) The filename contains a "?" (this is before % decoding, all others are
+ after % decoding)
+2) The filename doesn't start with a "/"
+3) The file does not exist
+4) The file does not have enough permissions
+ (sysctl-configurable, default = worldreadble)
+5) The file has any of the "forbidden" permissions
+ (sysctl-configurable, default = execute, directory and sticky)
+6) The filename contains a string as defined in the "Dynamic" list.
+
+*/
+
+
+/* Prototypes */
+
+static void DecodeHexChars(char *URL);
+static struct DynamicString *DynamicList=NULL;
+
+
+
+/*
+
+The function "OpenFileForSecurity" returns either the "struct file" pointer
+of the file, or NULL. NULL means "let userspace handle it".
+
+*/
+struct file *OpenFileForSecurity(char *Filename)
+{
+ struct file *filp;
+ struct DynamicString *List;
+ umode_t permission;
+
+
+
+ EnterFunction("OpenFileForSecurity");
+ if (Filename==NULL)
+ return NULL;
+
+ if (strlen(Filename)>=256 ) return NULL; /* Sanity check */
+
+ /* Rule no. 1 -- No "?" characters */
+#ifndef BENCHMARK
+ if (strchr(Filename,'?')!=NULL)
+ return NULL;
+
+ /* Intermediate step: decode all %hex sequences */
+
+ DecodeHexChars(Filename);
+
+ /* Rule no. 2 -- Must start with a "/" */
+
+
+ if (Filename[0]!='/')
+ return NULL;
+
+#endif
+ /* Rule no. 3 -- Does the file exist ? */
+
+
+
+ filp = filp_open(Filename,00,O_RDONLY);
+
+
+ if ((IS_ERR(filp))||(filp==NULL)||(filp->f_dentry==NULL))
+ {
+ return NULL;
+ }
+
+#ifndef BENCHMARK
+ permission = filp->f_dentry->d_inode->i_mode;
+
+ /* Rule no. 4 : must have enough permissions */
+
+
+ if ((permission & sysctl_khttpd_permreq)==0)
+ {
+ if (filp!=NULL);
+ fput(filp);
+ filp=NULL;
+ return NULL;
+ }
+
+ /* Rule no. 5 : cannot have "forbidden" permission */
+
+
+ if ((permission & sysctl_khttpd_permforbid)!=0)
+ {
+ if (filp!=NULL)
+ fput(filp);
+ filp=NULL;
+ return NULL;
+ }
+
+ /* Rule no. 6 : No string in DynamicList can be a
+ substring of the filename */
+
+
+ List = DynamicList;
+
+ while (List!=NULL)
+ {
+ if (strstr(Filename,List->value)!=NULL)
+ {
+ if (filp!=NULL)
+ fput(filp);
+ filp=NULL;
+ return NULL;
+ }
+ List = List->Next;
+ }
+
+#endif
+ LeaveFunction("OpenFileForSecurity - success");
+
+ return filp;
+}
+
+/*
+
+DecodeHexChars does the actual %HEX decoding, in place.
+In place is possible because strings only get shorter by this.
+
+*/
+static void DecodeHexChars(char *URL)
+{
+ char *Source,*Dest;
+ int val,val2;
+
+ EnterFunction("DecodeHexChars");
+
+ Source = strchr(URL,'%');
+
+ if (Source==NULL)
+ return;
+
+ Dest = Source;
+
+ while (*Source!=0)
+ {
+ if (*Source=='%')
+ {
+ Source++;
+ val = *Source;
+
+ if (val>'Z') val-=0x20;
+ val = val - '0';
+ if (val<0) val=0;
+ if (val>9) val-=7;
+ if (val>15) val=15;
+
+ Source++;
+
+ val2 = *Source;
+
+ if (val2>'Z') val2-=0x20;
+ val2 = val2 - '0';
+ if (val2<0) val2=0;
+ if (val2>9) val2-=7;
+ if (val2>15) val2=15;
+
+ *Dest=val*16+val2;
+ } else *Dest = *Source;
+ Dest++;
+ Source++;
+ }
+ *Dest=0;
+
+ LeaveFunction("DecodeHexChars");
+}
+
+
+void AddDynamicString(const char *String)
+{
+ struct DynamicString *Temp;
+
+ EnterFunction("AddDynamicString");
+
+ Temp = (struct DynamicString*)kmalloc(sizeof(struct DynamicString),(int)GFP_KERNEL);
+
+ if (Temp==NULL)
+ return;
+
+ memset(Temp->value,0,sizeof(Temp->value));
+ strncpy(Temp->value,String,sizeof(Temp->value)-1);
+
+ Temp->Next = DynamicList;
+ DynamicList = Temp;
+
+ LeaveFunction("AddDynamicString");
+}
+
+void GetSecureString(char *String)
+{
+ struct DynamicString *Temp;
+ int max;
+
+ EnterFunction("GetSecureString");
+
+ *String = 0;
+
+ memset(String,0,255);
+
+ strncpy(String,"Dynamic strings are : -",255);
+ Temp = DynamicList;
+ while (Temp!=NULL)
+ {
+ max=253 - strlen(String) - strlen(Temp->value);
+ strncat(String,Temp->value,max);
+ max=253 - strlen(String) - 3;
+ strncat(String,"- -",max);
+ Temp = Temp->Next;
+ }
+
+ LeaveFunction("GetSecureString");
+}
diff --git a/net/khttpd/security.h b/net/khttpd/security.h
new file mode 100644
index 000000000..346beacbb
--- /dev/null
+++ b/net/khttpd/security.h
@@ -0,0 +1,12 @@
+#ifndef _INCLUDE_GUARD_SECURITY_H
+#define _INCLUDE_GUARD_SECURITY_H
+
+struct DynamicString;
+
+struct DynamicString
+{
+ struct DynamicString* Next;
+ char value[32-sizeof(void*)]; /* fill 1 cache-line */
+};
+
+#endif \ No newline at end of file
diff --git a/net/khttpd/sockets.c b/net/khttpd/sockets.c
new file mode 100644
index 000000000..8f8b5d250
--- /dev/null
+++ b/net/khttpd/sockets.c
@@ -0,0 +1,103 @@
+/*
+
+kHTTPd -- the next generation
+
+Basic socket functions
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+#include "prototypes.h"
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/version.h>
+#include <linux/smp_lock.h>
+#include <net/sock.h>
+
+
+/*
+
+MainSocket is shared by all threads, therefore it has to be
+a global variable.
+
+*/
+struct socket *MainSocket=NULL;
+
+
+int StartListening(const int Port)
+{
+ struct socket *sock;
+ struct sockaddr_in sin;
+ int error;
+
+ EnterFunction("StartListening");
+
+ /* First create a socket */
+
+ error = sock_create(PF_INET,SOCK_STREAM,IPPROTO_TCP,&sock);
+ if (error<0)
+ (void)printk(KERN_ERR "Error during creation of socket; terminating\n");
+
+
+
+ /* Now bind the socket */
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons((unsigned short)Port);
+
+ error = sock->ops->bind(sock,(struct sockaddr*)&sin,sizeof(sin));
+ if (error<0)
+ {
+ (void)printk(KERN_ERR "kHTTPd: Error binding socket. This means that some other \n");
+ (void)printk(KERN_ERR " daemon is (or was a short time ago) using port %i.\n",Port);
+ return 0;
+ }
+
+ sock->sk->reuse = 1;
+ sock->sk->nonagle = 0;
+ sock->sk->linger = 1;
+
+ /* Now, start listening on the socket */
+
+ /* I have no idea what a sane backlog-value is. 48 works so far. */
+
+ error=sock->ops->listen(sock,48);
+ if (error!=0)
+ (void)printk(KERN_ERR "kHTTPd: Error listening on socket \n");
+ sock->flags |= SO_ACCEPTCON;
+
+ MainSocket = sock;
+
+ EnterFunction("StartListening");
+ return 1;
+}
+
+void StopListening(void)
+{
+ struct socket *sock;
+
+ EnterFunction("StopListening");
+ if (MainSocket==NULL) return;
+
+ sock=MainSocket;
+ MainSocket = NULL;
+ sock_release(sock);
+
+ LeaveFunction("StopListening");
+}
diff --git a/net/khttpd/structure.h b/net/khttpd/structure.h
new file mode 100644
index 000000000..70a604aba
--- /dev/null
+++ b/net/khttpd/structure.h
@@ -0,0 +1,68 @@
+#ifndef _INCLUDE_GUARD_STRUCTURE_H_
+#define _INCLUDE_GUARD_STRUCTURE_H_
+
+#include <linux/time.h>
+#include <linux/wait.h>
+
+
+struct http_request;
+
+struct http_request
+{
+ /* Linked list */
+ struct http_request *Next;
+
+ /* Network and File data */
+ struct socket *sock;
+ struct file *filp;
+
+ /* Raw data about the file */
+
+ int FileLength; /* File length in bytes */
+ int Time; /* mtime of the file, unix format */
+ int BytesSent; /* The number of bytes already sent */
+ int IsForUserspace; /* 1 means let Userspace handle this one */
+
+ /* Wait queue */
+
+ wait_queue_t sleep; /* For putting in the socket's waitqueue */
+
+ /* HTTP request information */
+ char FileName[256]; /* The requested filename */
+ int FileNameLength; /* The length of the string representing the filename */
+ char Agent[128]; /* The agent-string of the remote browser */
+ char IMS[128]; /* If-modified-since time, rfc string format */
+ char Host[128]; /* Value given by the Host: header */
+ int HTTPVER; /* HTTP-version; 9 for 0.9, 10 for 1.0 and above */
+
+
+ /* Derived date from the above fields */
+ int IMS_Time; /* if-modified-since time, unix format */
+ char TimeS[64]; /* File mtime, rfc string representation */
+ char LengthS[14]; /* File length, string representation */
+ char *MimeType; /* Pointer to a string with the mime-type
+ based on the filename */
+ int MimeLength; /* The length of this string */
+
+};
+
+
+
+/*
+
+struct khttpd_threadinfo represents the four queues that 1 thread has to deal with.
+It is padded to occupy 1 (Intel) cache-line, to avoid "cacheline-pingpong".
+
+*/
+struct khttpd_threadinfo
+{
+ struct http_request* WaitForHeaderQueue;
+ struct http_request* DataSendingQueue;
+ struct http_request* LoggingQueue;
+ struct http_request* UserspaceQueue;
+ char dummy[16]; /* Padding for cache-lines */
+};
+
+
+
+#endif
diff --git a/net/khttpd/sysctl.c b/net/khttpd/sysctl.c
new file mode 100644
index 000000000..244eb76db
--- /dev/null
+++ b/net/khttpd/sysctl.c
@@ -0,0 +1,320 @@
+/*
+
+kHTTPd -- the next generation
+
+Sysctl interface
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/smp_lock.h>
+#include <linux/sysctl.h>
+#include <linux/un.h>
+#include <linux/unistd.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+#include <linux/file.h>
+#include "prototypes.h"
+
+
+
+char sysctl_khttpd_docroot[200] = "/var/www";
+int sysctl_khttpd_stop = 0;
+int sysctl_khttpd_start = 0;
+int sysctl_khttpd_unload = 0;
+int sysctl_khttpd_clientport = 80;
+int sysctl_khttpd_permreq = S_IROTH; /* "other" read-access is required by default*/
+int sysctl_khttpd_permforbid = S_IFDIR | S_ISVTX | S_IXOTH | S_IXGRP | S_IXUSR;
+ /* forbidden is execute, directory and sticky*/
+int sysctl_khttpd_logging = 0;
+int sysctl_khttpd_serverport= 8080;
+
+char sysctl_khttpd_dynamicstring[200];
+int sysctl_khttpd_sloppymime= 0;
+int sysctl_khttpd_threads = 2;
+int sysctl_khttpd_maxconnect = 1000;
+
+
+static struct ctl_table_header *khttpd_table_header;
+
+static int sysctl_SecureString(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen, void **context);
+static int proc_dosecurestring(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp);
+
+
+static ctl_table khttpd_table[] = {
+ { NET_KHTTPD_DOCROOT,
+ "documentroot",
+ &sysctl_khttpd_docroot,
+ sizeof(sysctl_khttpd_docroot),
+ 0644,
+ NULL,
+ proc_dostring,
+ &sysctl_string,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_STOP,
+ "stop",
+ &sysctl_khttpd_stop,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_START,
+ "start",
+ &sysctl_khttpd_start,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_UNLOAD,
+ "unload",
+ &sysctl_khttpd_unload,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_THREADS,
+ "threads",
+ &sysctl_khttpd_threads,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_MAXCONNECT,
+ "maxconnect",
+ &sysctl_khttpd_maxconnect,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_SLOPPYMIME,
+ "sloppymime",
+ &sysctl_khttpd_sloppymime,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_CLIENTPORT,
+ "clientport",
+ &sysctl_khttpd_clientport,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_PERMREQ,
+ "perm_required",
+ &sysctl_khttpd_permreq,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_PERMFORBID,
+ "perm_forbid",
+ &sysctl_khttpd_permforbid,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_LOGGING,
+ "logging",
+ &sysctl_khttpd_logging,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_SERVERPORT,
+ "serverport",
+ &sysctl_khttpd_serverport,
+ sizeof(int),
+ 0644,
+ NULL,
+ proc_dointvec,
+ &sysctl_intvec,
+ NULL,
+ NULL,
+ NULL
+ },
+ { NET_KHTTPD_DYNAMICSTRING,
+ "dynamic",
+ &sysctl_khttpd_dynamicstring,
+ sizeof(sysctl_khttpd_dynamicstring),
+ 0644,
+ NULL,
+ proc_dosecurestring,
+ &sysctl_SecureString,
+ NULL,
+ NULL,
+ NULL
+ },
+ {0,0,0,0,0,0,0,0,0,0,0} };
+
+
+static ctl_table khttpd_dir_table[] = {
+ {NET_KHTTPD, "khttpd", NULL, 0, 0555, khttpd_table,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0,0}
+};
+
+static ctl_table khttpd_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, khttpd_dir_table,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0,0}
+};
+
+
+void StartSysctl(void)
+{
+ khttpd_table_header = register_sysctl_table(khttpd_root_table,1);
+}
+
+
+void EndSysctl(void)
+{
+ unregister_sysctl_table(khttpd_table_header);
+}
+
+static int proc_dosecurestring(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ int len;
+ char *p, c=0;
+ char String[256];
+
+ if ((table->data==0) || (table->maxlen==0) || (*lenp==0) ||
+ ((filp->f_pos!=0) && (write==0))) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write!=0) {
+ len = 0;
+ p = buffer;
+ while (len < *lenp) {
+ if(get_user(c, p++))
+ return -EFAULT;
+ if (c == 0 || c == '\n')
+ break;
+ len++;
+ }
+ if (len >= table->maxlen)
+ len = table->maxlen-1;
+ if(copy_from_user(String, buffer,(unsigned long)len))
+ return -EFAULT;
+ ((char *) String)[len] = 0;
+ filp->f_pos += *lenp;
+ AddDynamicString(String);
+ } else {
+ GetSecureString(String);
+ len = strlen(String);
+ if (len > table->maxlen)
+ len = table->maxlen;
+ if (len > *lenp)
+ len = *lenp;
+ if (len!=0)
+ if(copy_to_user(buffer, String,(unsigned long)len))
+ return -EFAULT;
+ if (len < *lenp) {
+ if(put_user('\n', ((char *) buffer) + len))
+ return -EFAULT;
+ len++;
+ }
+ *lenp = len;
+ filp->f_pos += len;
+ }
+ return 0;
+}
+
+static int sysctl_SecureString (/*@unused@*/ctl_table *table,
+ /*@unused@*/int *name,
+ /*@unused@*/int nlen,
+ /*@unused@*/void *oldval,
+ /*@unused@*/size_t *oldlenp,
+ /*@unused@*/void *newval,
+ /*@unused@*/size_t newlen,
+ /*@unused@*/void **context)
+{
+ return -ENOSYS;
+}
diff --git a/net/khttpd/sysctl.h b/net/khttpd/sysctl.h
new file mode 100644
index 000000000..9e3050dd1
--- /dev/null
+++ b/net/khttpd/sysctl.h
@@ -0,0 +1,17 @@
+#ifndef _KHTTPD_INCLUDE_GUARD_SYSCTL_H
+#define _KHTTPD_INCLUDE_GUARD_SYSCTL_H
+
+extern char sysctl_khttpd_docroot[200];
+extern int sysctl_khttpd_stop;
+extern int sysctl_khttpd_start;
+extern int sysctl_khttpd_unload;
+extern int sysctl_khttpd_clientport;
+extern int sysctl_khttpd_permreq;
+extern int sysctl_khttpd_permforbid;
+extern int sysctl_khttpd_logging;
+extern int sysctl_khttpd_serverport;
+extern int sysctl_khttpd_sloppymime;
+extern int sysctl_khttpd_threads;
+extern int sysctl_khttpd_maxconnect;
+
+#endif
diff --git a/net/khttpd/userspace.c b/net/khttpd/userspace.c
new file mode 100644
index 000000000..2acb27ff1
--- /dev/null
+++ b/net/khttpd/userspace.c
@@ -0,0 +1,248 @@
+/*
+
+kHTTPd -- the next generation
+
+Pass connections to userspace-daemons
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+/*
+
+Purpose:
+
+Userspace() hands all requests in the queue to the userspace-daemon, if
+such beast exists.
+
+Return value:
+ The number of requests that changed status
+*/
+#include <linux/kernel.h>
+
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/smp_lock.h>
+#include <linux/un.h>
+#include <linux/unistd.h>
+#include <linux/wait.h>
+
+#include <net/ip.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+
+#include <linux/file.h>
+
+
+#include "structure.h"
+#include "prototypes.h"
+#include "sysctl.h"
+
+/* prototypes of local, static functions */
+static int AddSocketToAcceptQueue(struct socket *sock,const int Port);
+
+
+int Userspace(const int CPUNR)
+{
+ struct http_request *CurrentRequest,**Prev,*Next;
+
+ EnterFunction("Userspace");
+
+
+
+
+ CurrentRequest = threadinfo[CPUNR].UserspaceQueue;
+ Prev = &(threadinfo[CPUNR].UserspaceQueue);
+
+ while (CurrentRequest!=NULL)
+ {
+
+ /* Clean-up the waitqueue of the socket.. Bad things happen if
+ this is forgotten. */
+ if (CurrentRequest->sock!=NULL)
+ {
+ if ((CurrentRequest->sock!=NULL)&&(CurrentRequest->sock->sk!=NULL))
+ {
+ remove_wait_queue(CurrentRequest->sock->sk->sleep,&(CurrentRequest->sleep));
+ }
+ }
+
+
+ if (AddSocketToAcceptQueue(CurrentRequest->sock,sysctl_khttpd_clientport)>=0)
+ {
+
+ (*Prev) = CurrentRequest->Next;
+ Next = CurrentRequest->Next;
+
+
+ sock_release(CurrentRequest->sock);
+ CurrentRequest->sock = NULL; /* We no longer own it */
+
+ CleanUpRequest(CurrentRequest);
+
+ CurrentRequest = Next;
+ continue;
+
+ }
+ else /* No userspace-daemon present, or other problems with it */
+ {
+ (*Prev) = CurrentRequest->Next;
+ Next = CurrentRequest->Next;
+
+ Send403(CurrentRequest->sock); /* Sorry, no go... */
+
+ CleanUpRequest(CurrentRequest);
+
+ CurrentRequest = Next;
+ continue;
+
+ }
+
+
+ Prev = &(CurrentRequest->Next);
+ CurrentRequest = CurrentRequest->Next;
+ }
+
+ LeaveFunction("Userspace");
+ return 0;
+}
+
+void StopUserspace(const int CPUNR)
+{
+ struct http_request *CurrentRequest,*Next;
+
+ EnterFunction("StopUserspace");
+ CurrentRequest = threadinfo[CPUNR].UserspaceQueue;
+
+ while (CurrentRequest!=NULL)
+ {
+ Next= CurrentRequest->Next;
+ CleanUpRequest(CurrentRequest);
+ CurrentRequest=Next;
+ }
+ threadinfo[CPUNR].UserspaceQueue = NULL;
+
+ LeaveFunction("StopUserspace");
+}
+
+
+/*
+ "FindUserspace" returns the struct sock of the userspace-daemon, so that we can
+ "drop" our request in the accept-queue
+*/
+
+static struct sock *FindUserspace(const unsigned short Port)
+{
+ struct sock *sk;
+
+ EnterFunction("FindUserspace");
+
+ local_bh_disable();
+ sk = tcp_v4_lookup_listener(INADDR_ANY,Port,0);
+ local_bh_enable();
+ return sk;
+}
+
+static void dummy_destructor(struct open_request *req)
+{
+}
+
+static struct or_calltable Dummy =
+{
+ 0,
+ NULL,
+ NULL,
+ &dummy_destructor,
+ NULL
+};
+
+static int AddSocketToAcceptQueue(struct socket *sock,const int Port)
+{
+ struct open_request *req;
+ struct sock *sk;
+ struct tcp_opt *tp;
+
+ EnterFunction("AddSocketToAcceptQueue");
+
+
+ sk = FindUserspace((unsigned short)Port);
+
+ if (sk==NULL) /* No userspace-daemon found */
+ {
+ return -1;
+ }
+
+ lock_sock(sk);
+
+ if (sk->state != TCP_LISTEN ||
+ sk->ack_backlog > sk->max_ack_backlog) /* To many pending requests */
+ {
+ release_sock(sk);
+ sock_put(sk);
+ return -1;
+ }
+
+ req = tcp_openreq_alloc();
+
+ if (req==NULL)
+ {
+ release_sock(sk);
+ sock_put(sk);
+ return -1;
+ }
+
+ req->sk = sock->sk;
+ sock->sk = NULL;
+ sock->state = SS_UNCONNECTED;
+
+ req->class = &Dummy;
+ write_lock_irq(&req->sk->callback_lock);
+ req->sk->socket = NULL;
+ req->sk->sleep = NULL;
+ write_unlock_irq(&req->sk->callback_lock);
+
+ tp =&(sk->tp_pinfo.af_tcp);
+ sk->ack_backlog++;
+
+ tcp_synq_queue(tp,req);
+
+ sk->data_ready(sk, 0);
+
+ release_sock(sk);
+ sock_put(sk);
+
+ LeaveFunction("AddSocketToAcceptQueue");
+
+ return +1;
+
+
+
+}
+
+void InitUserspace(const int CPUNR)
+{
+}
+
+
diff --git a/net/khttpd/waitheaders.c b/net/khttpd/waitheaders.c
new file mode 100644
index 000000000..a7d4b82e0
--- /dev/null
+++ b/net/khttpd/waitheaders.c
@@ -0,0 +1,303 @@
+/*
+
+kHTTPd -- the next generation
+
+Wait for headers on the accepted connections
+
+*/
+/****************************************************************
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************/
+
+/*
+
+Purpose:
+
+WaitForHeaders polls all connections in "WaitForHeaderQueue" to see if
+headers have arived. If so, the headers are decoded and the request is
+moved to either the "SendingDataQueue" or the "UserspaceQueue".
+
+Return value:
+ The number of requests that changed status
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+
+#include <asm/uaccess.h>
+
+#include "structure.h"
+#include "prototypes.h"
+
+static char *Buffer[CONFIG_KHTTPD_NUMCPU];
+
+
+static int DecodeHeader(const int CPUNR, struct http_request *Request);
+
+
+int WaitForHeaders(const int CPUNR)
+{
+ struct http_request *CurrentRequest,**Prev;
+ struct sock *sk;
+ int count = 0;
+
+ EnterFunction("WaitForHeaders");
+
+ CurrentRequest = threadinfo[CPUNR].WaitForHeaderQueue;
+
+ Prev = &(threadinfo[CPUNR].WaitForHeaderQueue);
+
+ while (CurrentRequest!=NULL)
+ {
+
+ /* If the connection is lost, remove from queue */
+
+ if (CurrentRequest->sock->sk->state != TCP_ESTABLISHED
+ && CurrentRequest->sock->sk->state != TCP_CLOSE_WAIT)
+ {
+ struct http_request *Next;
+
+ Next = CurrentRequest->Next;
+
+ *Prev = CurrentRequest->Next;
+ CurrentRequest->Next = NULL;
+
+
+ CleanUpRequest(CurrentRequest);
+ CurrentRequest = Next;
+ continue;
+ }
+
+
+
+ /* If data pending, take action */
+
+ sk = CurrentRequest->sock->sk;
+
+ if (!skb_queue_empty(&(sk->receive_queue))) /* Do we have data ? */
+ {
+ struct http_request *Next;
+
+
+
+ /* Decode header */
+
+ if (DecodeHeader(CPUNR,CurrentRequest)<0)
+ {
+ CurrentRequest = CurrentRequest->Next;
+ continue;
+ }
+
+
+ /* Remove from WaitForHeaderQueue */
+
+ Next= CurrentRequest->Next;
+
+ *Prev = Next;
+ count++;
+
+ /* Add to either the UserspaceQueue or the DataSendingQueue */
+
+ if (CurrentRequest->IsForUserspace!=0)
+ {
+ CurrentRequest->Next = threadinfo[CPUNR].UserspaceQueue;
+ threadinfo[CPUNR].UserspaceQueue = CurrentRequest;
+ } else
+ {
+ CurrentRequest->Next = threadinfo[CPUNR].DataSendingQueue;
+ threadinfo[CPUNR].DataSendingQueue = CurrentRequest;
+ }
+
+ CurrentRequest = Next;
+ continue;
+
+ }
+
+
+ Prev = &(CurrentRequest->Next);
+ CurrentRequest = CurrentRequest->Next;
+ }
+
+ LeaveFunction("WaitHeaders");
+ return count;
+}
+
+void StopWaitingForHeaders(const int CPUNR)
+{
+ struct http_request *CurrentRequest,*Next;
+
+ EnterFunction("StopWaitingForHeaders");
+ CurrentRequest = threadinfo[CPUNR].WaitForHeaderQueue;
+
+ while (CurrentRequest!=NULL)
+ {
+ Next = CurrentRequest->Next;
+ CleanUpRequest(CurrentRequest);
+ CurrentRequest=Next;
+ }
+
+ threadinfo[CPUNR].WaitForHeaderQueue = NULL; /* The queue is empty now */
+
+ free_page((unsigned long)Buffer[CPUNR]);
+ Buffer[CPUNR]=NULL;
+
+ EnterFunction("StopWaitingForHeaders");
+}
+
+
+/*
+
+DecodeHeader peeks at the TCP/IP data, determines what the request is,
+fills the request-structure and sends the HTTP-header when apropriate.
+
+*/
+
+static int DecodeHeader(const int CPUNR, struct http_request *Request)
+{
+ struct msghdr msg;
+ struct iovec iov;
+ int len;
+
+ mm_segment_t oldfs;
+
+ EnterFunction("DecodeHeader");
+
+ /* First, read the data */
+
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ msg.msg_iov->iov_base = &Buffer[CPUNR][0];
+ msg.msg_iov->iov_len = (size_t)4095;
+
+ len = 0;
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ /* 4095 leaves a "0" to terminate the string */
+
+ len = sock_recvmsg(Request->sock,&msg,4095,MSG_PEEK);
+ set_fs(oldfs);
+
+ if (len<0) {
+ /* WONDERFUL. NO COMMENTS. --ANK */
+ Request->IsForUserspace = 1;
+ return 0;
+ }
+
+ if (len>=4094) /* BIG header, we cannot decode it so leave it to userspace */
+ {
+ Request->IsForUserspace = 1;
+ return 0;
+ }
+
+ /* Then, decode the header */
+
+
+ ParseHeader(Buffer[CPUNR],len,Request);
+
+ Request->filp = OpenFileForSecurity(Request->FileName);
+
+
+ Request->MimeType = ResolveMimeType(Request->FileName,&Request->MimeLength);
+
+
+ if (Request->MimeType==NULL) /* Unknown mime-type */
+ {
+ if (Request->filp!=NULL)
+ {
+ fput(Request->filp);
+ Request->filp = NULL;
+ }
+ Request->IsForUserspace = 1;
+
+ return 0;
+ }
+
+ if (Request->filp==NULL)
+ {
+ Request->IsForUserspace = 1;
+ return 0;
+ }
+ else
+ if ((Request->filp->f_dentry!=NULL)&&(Request->filp->f_dentry->d_inode!=NULL))
+ {
+ Request->FileLength = (int)Request->filp->f_dentry->d_inode->i_size;
+ Request->Time = Request->filp->f_dentry->d_inode->i_mtime;
+ Request->IMS_Time = mimeTime_to_UnixTime(Request->IMS);
+ sprintf(Request->LengthS,"%i",Request->FileLength);
+ time_Unix2RFC(min(Request->Time,CurrentTime_i),Request->TimeS);
+ /* The min() is required by rfc1945, section 10.10:
+ It is not allowed to send a filetime in the future */
+
+ if (Request->IMS_Time>Request->Time)
+ { /* Not modified since last time */
+ Send304(Request->sock);
+ Request->FileLength=0;
+ }
+ else /* Normal Case */
+ {
+ Request->sock->sk->nonagle = 2; /* this is TCP_CORK */
+ if (Request->HTTPVER!=9) /* HTTP/0.9 doesn't allow a header */
+ SendHTTPHeader(Request);
+ }
+
+
+ } else
+ {
+ /* Ehhh... */
+
+ printk(KERN_CRIT "kHTTPd: Unexpected filesystem response\n");
+ return -1;
+ }
+
+ LeaveFunction("DecodeHeader");
+ return 0;
+}
+
+
+int InitWaitHeaders(int ThreadCount)
+{
+ int I,I2;
+
+ EnterFunction("InitWaitHeaders");
+ I=0;
+ while (I<ThreadCount)
+ {
+ Buffer[I] = (char*)get_free_page((int)GFP_KERNEL);
+ if (Buffer[I] == NULL)
+ {
+ printk(KERN_CRIT "kHTTPd: Not enough memory for basic needs\n");
+ I2=0;
+ while (I2<I-1)
+ {
+ free_page( (unsigned long)Buffer[I2++]);
+ }
+ return -1;
+ }
+ I++;
+ }
+
+ LeaveFunction("InitWaitHeaders");
+ return 0;
+
+}