Commit 984e13d3 authored by Linus Torvalds's avatar Linus Torvalds

Automerge

parents 621f5626 ba04edba
......@@ -172,7 +172,6 @@ enum
NET_TR=14,
NET_DECNET=15,
NET_ECONET=16,
NET_KHTTPD=17
};
/* /proc/sys/kernel/random */
......@@ -494,23 +493,6 @@ enum {
NET_DECNET_DEBUG_LEVEL = 255
};
/* /proc/sys/net/khttpd/ */
enum {
NET_KHTTPD_DOCROOT = 1,
NET_KHTTPD_START = 2,
NET_KHTTPD_STOP = 3,
NET_KHTTPD_UNLOAD = 4,
NET_KHTTPD_CLIENTPORT = 5,
NET_KHTTPD_PERMREQ = 6,
NET_KHTTPD_PERMFORBID = 7,
NET_KHTTPD_LOGGING = 8,
NET_KHTTPD_SERVERPORT = 9,
NET_KHTTPD_DYNAMICSTRING= 10,
NET_KHTTPD_SLOPPYMIME = 11,
NET_KHTTPD_THREADS = 12,
NET_KHTTPD_MAXCONNECT = 13
};
/* /proc/sys/net/decnet/conf/<dev> */
enum {
NET_DECNET_CONF_LOOPBACK = -2,
......
......@@ -26,9 +26,6 @@ if [ "$CONFIG_INET" = "y" ]; then
source net/ipv6/Config.in
fi
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
source net/khttpd/Config.in
fi
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM
......
......@@ -16,7 +16,6 @@ obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/
obj-$(CONFIG_INET) += ipv4/
obj-$(CONFIG_UNIX) += unix/
obj-$(CONFIG_IPV6) += ipv6/
obj-$(CONFIG_KHTTPD) += khttpd/
obj-$(CONFIG_PACKET) += packet/
obj-$(CONFIG_NET_SCHED) += sched/
obj-$(CONFIG_BRIDGE) += bridge/
......
CONFIG_KHTTPD
The kernel httpd acceleration daemon (kHTTPd) is a (limited) web
server built into the kernel. It is limited since it can only serve
files from the file system and cannot deal with executable content
such as CGI scripts. Serving files is sped up if you use kHTTPd.
If kHTTPd is not able to fulfill a request, it can transparently
pass it through to a user space web server such as apache.
Saying "M" here builds the kHTTPd module; this is NOT enough to have
a working kHTTPd. For safety reasons, the module has to be activated
by doing a "echo 1 > /proc/sys/net/khttpd/start" after inserting the
module.
Before using this, read the README in net/khttpd !
The kHTTPd is experimental. Be careful when using it on a production
machine. Also note that kHTTPd doesn't support virtual servers yet.
#
# kHTTPd
#
tristate ' Kernel httpd acceleration (EXPERIMENTAL)' CONFIG_KHTTPD
#
# Makefile for kHTTPd
#
obj-$(CONFIG_KHTTPD) += khttpd.o
khttpd-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
host-progs := make_times_h
include $(TOPDIR)/Rules.make
# Dependencies on generated files need to be listed explicitly
$(obj)/rfc_time.o: $(obj)/times.h
# Generated files
$(obj)/times.h: $(obj)/make_times_h
$(obj)/make_times_h >$@
=====
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
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
/*
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 tcp_opt *tp;
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;
tp = tcp_sk(Socket->sk);
/*
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 (tp->accept_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;
}
/*
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/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, struct page *page, unsigned long offset, unsigned long size)
{
int written;
char *kaddr;
unsigned long count = desc->count;
struct socket *sock = (struct socket *) desc->buf;
mm_segment_t old_fs;
if (size > count)
size = count;
old_fs = get_fs();
set_fs(KERNEL_DS);
kaddr = kmap(page);
written = SendBuffer_async(sock, kaddr + offset, size);
kunmap(page);
set_fs(old_fs);
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_t(int, 4 * 4096, CurrentRequest->FileLength - CurrentRequest->BytesSent);
ReadSize = min_t(int, ReadSize, Space);
if (ReadSize>0)
{
struct inode *inode;
inode = CurrentRequest->filp->f_dentry->d_inode;
if (inode->i_mapping->a_ops->readpage) {
/* 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)
{
struct tcp_opt *tp =
tcp_sk(CurrentRequest->sock->sk);
tp->nonagle = 0;
tcp_push_pending_frames(CurrentRequest->sock->sk, tp);
}
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");
}
/*
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");
}
/*
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.
*
****************************************************************/
static int errno;
#define __KERNEL_SYSCALLS__
#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;
CPUNR=0;
if (cpu_pointer!=NULL)
CPUNR=(int)*(int*)cpu_pointer;
sprintf(current->comm,"khttpd - %i",CPUNR);
daemonize();
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();
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 incoming connections _again_, because it is possible
one came in during the other steps, and the wakeup doesn't happen
then.
*/
changes +=AcceptConnections(CPUNR,MainSocket);
}
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];
/*
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);
sprintf(current->comm,"khttpd manager");
daemonize();
/* 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();
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]), CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
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]), CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
(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;
MOD_INC_USE_COUNT;
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, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
return 0;
}
void khttpd_cleanup(void)
{
EndSysctl();
}
module_init(khttpd_init)
module_exit(khttpd_cleanup)
MODULE_LICENSE("GPL");
/*
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;
printf("static time_t TimeDays[10][13] = { \n");
Y=1997;
while (Y<2007)
{
M=0;
printf(" { ");
while (M<12)
{
printf("%i",(int)GetDay(1,M,Y));
printf(",\t");
M++;
}
printf("%i } ",(int)GetDay(1,0,Y+1));
if (Y!=2006) printf(",");
printf("\n");
Y++;
}
printf("};\n");
printf("static int WeekDays[10][13] = { \n");
Y=1997;
while (Y<2007)
{
M=0;
printf(" { ");
while (M<12)
{
printf("%i",(int)WeekGetDay(1,M,Y));
printf(",\t");
M++;
}
printf("%i } ",(int)WeekGetDay(1,0,Y+1));
if (Y!=2006) printf(",");
printf("\n");
Y++;
}
printf("};\n");
printf("#define KHTTPD_YEAROFFSET 1997\n");
printf("#define KHTTPD_NUMYEARS 10\n");
return 0;
}
/*
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/slab.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");
}
#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 <net/tcp.h>
#include <asm/uaccess.h>
#include "structure.h"
/* General defines and stuff */
#define CONFIG_KHTTPD_NUMCPU 16 /* Maximum number of threads */
#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
/*
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/slab.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
transferred.
*/
{
__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_t(unsigned int, 255, tmp - Buffer + PrefixLen);
strncat(Head->FileName,Buffer,min_t(unsigned int, 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_t(unsigned int, 127,EOL-Buffer-1));
Buffer=EOL+1;
continue;
}
if (strncmp("User-Agent: ",Buffer,12)==0)
{
Buffer+=12;
strncpy(Head->Agent,Buffer,min_t(unsigned int, 127,EOL-Buffer-1));
Buffer=EOL+1;
continue;
}
if (strncmp("Host: ",Buffer,6)==0)
{
Buffer+=6;
strncpy(Head->Host,Buffer,min_t(unsigned int, 127,EOL-Buffer-1));
Buffer=EOL+1;
continue;
}
#endif
Buffer = EOL+1; /* Skip line */
}
LeaveFunction("ParseHeader");
}
/*
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/slab.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;
}
/*
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/slab.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, O_RDONLY, 0);
if (IS_ERR(filp))
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");
}
#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
/*
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;
}
/* Grrr... setsockopt() does this. */
sock->sk->reuse = 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");
MainSocket = sock;
LeaveFunction("StartListening");
return 1;
}
void StopListening(void)
{
struct socket *sock;
EnterFunction("StopListening");
if (MainSocket==NULL) return;
sock=MainSocket;
MainSocket = NULL;
sock_release(sock);
LeaveFunction("StopListening");
}
#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 */
__kernel_size_t 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
/*
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/slab.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)
{
size_t 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;
}
#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
/*
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/slab.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, *nsk;
EnterFunction("AddSocketToAcceptQueue");
sk = FindUserspace((unsigned short)Port);
if (sk==NULL) /* No userspace-daemon found */
{
return -1;
}
lock_sock(sk);
if (sk->state != TCP_LISTEN || tcp_acceptq_is_full(sk))
{
release_sock(sk);
sock_put(sk);
return -1;
}
req = tcp_openreq_alloc();
if (req==NULL)
{
release_sock(sk);
sock_put(sk);
return -1;
}
nsk = sock->sk;
sock->sk = NULL;
sock->state = SS_UNCONNECTED;
req->class = &Dummy;
write_lock_bh(&nsk->callback_lock);
nsk->socket = NULL;
nsk->sleep = NULL;
write_unlock_bh(&nsk->callback_lock);
tcp_acceptq_queue(sk, req, nsk);
sk->data_ready(sk, 0);
release_sock(sk);
sock_put(sk);
LeaveFunction("AddSocketToAcceptQueue");
return +1;
}
void InitUserspace(const int CPUNR)
{
}
/*
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
{
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_t(unsigned int, 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 */
{
struct tcp_opt *tp = tcp_sk(Request->sock->sk);
tp->nonagle = 2; /* this is TCP_CORK */
if (Request->HTTPVER!=9) /* HTTP/0.9 doesn't allow a header */
SendHTTPHeader(Request);
}
}
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;
}
......@@ -56,7 +56,7 @@ extern __u32 sysctl_rmem_max;
extern struct net_proto_family inet_family_ops;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
#include <linux/in6.h>
#include <linux/icmpv6.h>
#include <net/ipv6.h>
......@@ -286,7 +286,7 @@ EXPORT_SYMBOL(dlci_ioctl_hook);
#endif
#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
#if defined (CONFIG_IPV6_MODULE)
/* inet functions common to v4 and v6 */
EXPORT_SYMBOL(inet_release);
EXPORT_SYMBOL(inet_stream_connect);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment