diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-10-09 00:00:47 +0000 |
commit | d6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch) | |
tree | e2be02f33984c48ec019c654051d27964e42c441 /net/khttpd | |
parent | 609d1e803baf519487233b765eb487f9ec227a18 (diff) |
Merge with 2.3.19.
Diffstat (limited to 'net/khttpd')
-rw-r--r-- | net/khttpd/Config.in | 4 | ||||
-rw-r--r-- | net/khttpd/Makefile | 28 | ||||
-rw-r--r-- | net/khttpd/README | 235 | ||||
-rw-r--r-- | net/khttpd/accept.c | 127 | ||||
-rw-r--r-- | net/khttpd/datasending.c | 235 | ||||
-rw-r--r-- | net/khttpd/logging.c | 95 | ||||
-rw-r--r-- | net/khttpd/main.c | 395 | ||||
-rw-r--r-- | net/khttpd/make_times_h.c | 122 | ||||
-rw-r--r-- | net/khttpd/misc.c | 242 | ||||
-rw-r--r-- | net/khttpd/prototypes.h | 131 | ||||
-rw-r--r-- | net/khttpd/rfc.c | 374 | ||||
-rw-r--r-- | net/khttpd/rfc_time.c | 227 | ||||
-rw-r--r-- | net/khttpd/security.c | 272 | ||||
-rw-r--r-- | net/khttpd/security.h | 12 | ||||
-rw-r--r-- | net/khttpd/sockets.c | 103 | ||||
-rw-r--r-- | net/khttpd/structure.h | 68 | ||||
-rw-r--r-- | net/khttpd/sysctl.c | 320 | ||||
-rw-r--r-- | net/khttpd/sysctl.h | 17 | ||||
-rw-r--r-- | net/khttpd/userspace.c | 248 | ||||
-rw-r--r-- | net/khttpd/waitheaders.c | 303 |
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(¤t->sigmask_lock); + tmpsig = current->blocked; + siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)| sigmask(SIGHUP)); + recalc_sigpending(current); + spin_unlock_irq(¤t->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(¤t->sigmask_lock); + tmpsig = current->blocked; + siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) ); + recalc_sigpending(current); + spin_unlock_irq(¤t->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; + +} |