URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/linux-2.4/net/khttpd
- from Rev 1275 to Rev 1765
- ↔ Reverse comparison
Rev 1275 → Rev 1765
/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.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; |
} |
/waitheaders.c
0,0 → 1,302
/* |
|
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("WaitForHeaders"); |
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"); |
|
if (Buffer[CPUNR] == NULL) { |
/* see comments in main.c regarding buffer managemnet - dank */ |
printk(KERN_CRIT "khttpd: lost my buffer"); |
BUG(); |
} |
|
/* 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 */ |
{ |
Request->sock->sk->tp_pinfo.af_tcp.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; |
|
} |
/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 */ |
__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 |
/security.c
0,0 → 1,254
/* |
|
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 = NULL; |
struct DynamicString *List; |
umode_t permission; |
|
EnterFunction("OpenFileForSecurity"); |
if (Filename==NULL) |
goto out_error; |
|
if (strlen(Filename)>=256 ) |
goto out_error; /* Sanity check */ |
|
/* Rule no. 1 -- No "?" characters */ |
#ifndef BENCHMARK |
if (strchr(Filename,'?')!=NULL) |
goto out_error; |
|
/* Intermediate step: decode all %hex sequences */ |
|
DecodeHexChars(Filename); |
|
/* Rule no. 2 -- Must start with a "/" */ |
|
if (Filename[0]!='/') |
goto out_error; |
|
#endif |
/* Rule no. 3 -- Does the file exist ? */ |
|
filp = filp_open(Filename, O_RDONLY, 0); |
|
if (IS_ERR(filp)) |
goto out_error; |
|
#ifndef BENCHMARK |
permission = filp->f_dentry->d_inode->i_mode; |
|
/* Rule no. 4 : must have enough permissions */ |
|
if ((permission & sysctl_khttpd_permreq)==0) |
goto out_error_put; |
|
/* Rule no. 5 : cannot have "forbidden" permission */ |
|
if ((permission & sysctl_khttpd_permforbid)!=0) |
goto out_error_put; |
|
/* 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) |
goto out_error_put; |
|
List = List->Next; |
} |
|
#endif |
LeaveFunction("OpenFileForSecurity - success"); |
out: |
return filp; |
|
out_error_put: |
fput(filp); |
out_error: |
filp=NULL; |
LeaveFunction("OpenFileForSecurity - fail"); |
goto out; |
} |
|
/* |
|
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"); |
} |
/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/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"); |
} |
/sockets.c
0,0 → 1,101
/* |
|
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"); |
} |
/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 |
/README
0,0 → 1,247
===== |
|
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 |
sleep 2 |
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 stopped. |
|
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 |
|
Starting kHTTPd |
=============== |
Once you have set up the configuration, start kHTTPD by running |
echo 1 > /proc/sys/net/khttpd/start |
It may take a jiffie or two to start. |
|
Stopping kHTTPd |
=============== |
To stop kHTTPd, do |
echo 1 > /proc/sys/net/khttpd/stop |
It should stop in a jiffy or two. |
|
Unloading kHTTPd |
=============== |
To unload the module, do |
echo 1 > /proc/sys/net/khttpd/stop |
echo 1 > /proc/sys/net/khttpd/unload |
#killall -HUP khttpd |
sleep 2 |
rmmod khttpd |
|
If this doesn't work fast enough for you (unloading 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. |
|
|
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. Known Issues |
kHTTPd is *not* currently compatible with tmpfs. Trying to serve |
files stored on a tmpfs partition is known to cause kernel oopses |
as of 2.4.18. This is due to the same problem that prevents sendfile() |
from being usable with tmpfs. A tmpfs patch is floating around that seems |
to fix this, but has not been released as of 27 May 2002. |
kHTTPD does work fine with ramfs, though. |
|
There is debate about whether to remove kHTTPd from the main |
kernel sources. This will probably happen in the 2.5 kernel series, |
after which khttpd will still be available as a patch. |
|
The kHTTPd source code could use a good spring cleaning. |
|
7. 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 |
Mailing list archives are at |
http://lists.alt.org/mailman/listinfo/khttpd-users |
|
/sysctl.c
0,0 → 1,341
/* |
|
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; |
|
atomic_t khttpd_stopCount; |
|
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 int khttpd_stop_wrap_proc_dointvec(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, |
khttpd_stop_wrap_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; |
} |
|
/* A wrapper around proc_dointvec that computes |
* khttpd_stopCount = # of times sysctl_khttpd_stop has gone true |
* Sensing sysctl_khttpd_stop in other threads is racy; |
* sensing khttpd_stopCount in other threads is not. |
*/ |
static int khttpd_stop_wrap_proc_dointvec(ctl_table *table, int write, struct file *filp, |
void *buffer, size_t *lenp) |
{ |
int rv; |
int oldstop = sysctl_khttpd_stop; |
rv = proc_dointvec(table, write, filp, buffer, lenp); |
if (sysctl_khttpd_stop && !oldstop) |
atomic_inc(&khttpd_stopCount); |
|
return rv; |
} |
|
|
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; |
} |
/datasending.c
0,0 → 1,241
/* |
|
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, 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) |
{ |
CurrentRequest->sock->sk->tp_pinfo.af_tcp.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"); |
} |
/main.c
0,0 → 1,362
/* |
|
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; |
int old_stop_count; |
|
DECLARE_WAITQUEUE(main_wait,current); |
|
MOD_INC_USE_COUNT; |
|
/* Remember value of stop count. If it changes, user must have |
* asked us to stop. Sensing this is much less racy than |
* directly sensing sysctl_khttpd_stop. - dank |
*/ |
old_stop_count = atomic_read(&khttpd_stopCount); |
|
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(¤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 (old_stop_count == atomic_read(&khttpd_stopCount)) |
{ |
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(¤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; |
int old_stop_count; |
|
/* First : wait for activation */ |
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; |
sysctl_khttpd_stop = 0; |
|
/* Then start listening and spawn the daemons */ |
if (StartListening(sysctl_khttpd_serverport)==0) |
{ |
sysctl_khttpd_start = 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(); |
sysctl_khttpd_start = 0; |
continue; |
} |
if (InitWaitHeaders(ActualThreads)!=0) |
{ |
for (I=0; I<ActualThreads; I++) { |
StopDataSending(I); |
} |
StopListening(); |
sysctl_khttpd_start = 0; |
continue; |
} |
|
/* Clean all queues */ |
memset(threadinfo, 0, sizeof(struct khttpd_threadinfo)); |
|
for (I=0; I<ActualThreads; I++) { |
atomic_set(&Running[I],1); |
(void)kernel_thread(MainDaemon,&(CountBuf[I]), CLONE_FS | CLONE_FILES | CLONE_SIGHAND); |
} |
|
/* Then wait for deactivation */ |
/* Remember value of stop count. If it changes, user must |
* have asked us to stop. Sensing this is much less racy |
* than directly sensing sysctl_khttpd_stop. - dank |
*/ |
old_stop_count = atomic_read(&khttpd_stopCount); |
while ( ( old_stop_count == atomic_read(&khttpd_stopCount)) |
&& (!signal_pending(current)) |
&& (sysctl_khttpd_unload==0) ) |
{ |
/* Used to restart dead threads here, but it was buggy*/ |
interruptible_sleep_on_timeout(&WQ,HZ); |
} |
|
/* Wait for the daemons to stop, one second per iteration */ |
while (atomic_read(&DaemonCount)>0) |
interruptible_sleep_on_timeout(&WQ,HZ); |
StopListening(); |
sysctl_khttpd_start = 0; |
/* reap the zombie-daemons */ |
do |
waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG); |
while (waitpid_result>0); |
} |
sysctl_khttpd_start = 0; |
sysctl_khttpd_stop = 1; |
atomic_inc(&khttpd_stopCount); |
|
/* Wait for the daemons to stop, one second per iteration */ |
while (atomic_read(&DaemonCount)>0) |
interruptible_sleep_on_timeout(&WQ,HZ); |
StopListening(); |
/* reap the zombie-daemons */ |
do |
waitpid_result = waitpid(-1,NULL,__WCLONE|WNOHANG); |
while (waitpid_result>0); |
|
(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; |
|
for (I=0; I<CONFIG_KHTTPD_NUMCPU; I++) { |
CountBuf[I]=I; |
} |
|
atomic_set(&ConnectCount,0); |
atomic_set(&DaemonCount,0); |
atomic_set(&khttpd_stopCount,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"); |
/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; |
} |
/sysctl.h
0,0 → 1,20
#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; |
|
/* incremented each time sysctl_khttpd_stop goes nonzero */ |
extern atomic_t khttpd_stopCount; |
|
#endif |
/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"); |
} |
/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/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; |
} |
/Config.in
0,0 → 1,4
# |
# kHTTPd |
# |
tristate ' Kernel httpd acceleration (EXPERIMENTAL)' CONFIG_KHTTPD |
/userspace.c
0,0 → 1,243
/* |
|
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) |
{ |
} |
|
|
/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/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"); |
} |
|
/Makefile
0,0 → 1,25
# |
# 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 |
|
obj-m := $(O_TARGET) |
obj-y := 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 |
/prototypes.h
0,0 → 1,120
#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 */ |
|
#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 |