2018-08-02 15:41:30 +02:00
/*
* This ' Tiny Web Server ’ is derived from nweb , which was developed
* by Nigel Griffiths and published at
* https : //www.ibm.com/developerworks/systems/library/es-nweb/index.html.
2018-08-02 21:11:52 +02:00
*
* The web server was released under the IBM License Agreement .
*/
2018-08-02 15:41:30 +02:00
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <fcntl.h>
# include <signal.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
# ifndef __hermit__
# include <arpa/inet.h>
# endif
# define VERSION 23
# define BUFSIZE 8096
# define ERROR 42
# define LOG 44
# define FORBIDDEN 403
# define NOTFOUND 404
struct {
char * ext ;
char * filetype ;
} extensions [ ] = {
{ " gif " , " image/gif " } ,
{ " jpg " , " image/jpg " } ,
{ " jpeg " , " image/jpeg " } ,
{ " png " , " image/png " } ,
{ " ico " , " image/ico " } ,
{ " zip " , " image/zip " } ,
{ " gz " , " image/gz " } ,
{ " tar " , " image/tar " } ,
{ " htm " , " text/html " } ,
{ " html " , " text/html " } ,
{ 0 , 0 } } ;
2018-08-02 22:48:09 +02:00
static ssize_t nweb_write ( int fd , const char * buf , size_t len )
{
size_t pos = 0 ;
while ( pos < len ) {
ssize_t r = write ( fd , buf + pos , len - pos ) ;
if ( r > 0 )
pos + = r ;
}
return pos ;
}
2018-08-02 21:11:52 +02:00
static void logger ( int type , char * s1 , char * s2 , int socket_fd )
2018-08-02 15:41:30 +02:00
{
int fd ;
char logbuffer [ BUFSIZE * 2 ] ;
switch ( type ) {
case ERROR : ( void ) sprintf ( logbuffer , " ERROR: %s:%s Errno=%d exiting pid=%d " , s1 , s2 , errno , getpid ( ) ) ;
break ;
case FORBIDDEN :
2018-08-02 22:48:09 +02:00
( void ) nweb_write ( socket_fd , " HTTP/1.1 403 Forbidden \n Content-Length: 185 \n Connection: close \n Content-Type: text/html \n \n <html><head> \n <title>403 Forbidden</title> \n </head><body> \n <h1>Forbidden</h1> \n The requested URL, file type or operation is not allowed on this simple static file webserver. \n </body></html> \n " , 271 ) ;
2018-08-02 15:41:30 +02:00
( void ) sprintf ( logbuffer , " FORBIDDEN: %s:%s " , s1 , s2 ) ;
break ;
case NOTFOUND :
2018-08-02 22:48:09 +02:00
( void ) nweb_write ( socket_fd , " HTTP/1.1 404 Not Found \n Content-Length: 136 \n Connection: close \n Content-Type: text/html \n \n <html><head> \n <title>404 Not Found</title> \n </head><body> \n <h1>Not Found</h1> \n The requested URL was not found on this server. \n </body></html> \n " , 224 ) ;
2018-08-02 15:41:30 +02:00
( void ) sprintf ( logbuffer , " NOT FOUND: %s:%s " , s1 , s2 ) ;
break ;
case LOG : ( void ) sprintf ( logbuffer , " INFO: %s:%s:%d " , s1 , s2 , socket_fd ) ; break ;
}
/* No checks here, nothing can be done with a failure anyway */
if ( ( fd = open ( " nweb.log " , O_CREAT | O_WRONLY | O_APPEND , 0644 ) ) > = 0 ) {
2018-08-02 22:48:09 +02:00
( void ) nweb_write ( fd , logbuffer , strlen ( logbuffer ) ) ;
( void ) nweb_write ( fd , " \n " , 1 ) ;
2018-08-02 15:41:30 +02:00
( void ) close ( fd ) ;
}
if ( type = = ERROR | | type = = NOTFOUND | | type = = FORBIDDEN ) exit ( 3 ) ;
}
/* this is a child web server process, so we can exit on errors */
2018-08-02 21:11:52 +02:00
static void web ( int fd , int hit )
2018-08-02 15:41:30 +02:00
{
int j , file_fd , buflen ;
long i , ret , len ;
char * fstr ;
2018-08-02 22:48:09 +02:00
char buffer [ BUFSIZE + 1 ] ; /* static so zero filled */
2018-08-02 15:41:30 +02:00
ret = read ( fd , buffer , BUFSIZE ) ; /* read Web request in one go */
if ( ret = = 0 | | ret = = - 1 ) { /* read failure stop now */
logger ( FORBIDDEN , " failed to read browser request " , " " , fd ) ;
}
if ( ret > 0 & & ret < BUFSIZE ) /* return code is valid chars */
buffer [ ret ] = 0 ; /* terminate the buffer */
else buffer [ 0 ] = 0 ;
for ( i = 0 ; i < ret ; i + + ) /* remove CF and LF characters */
if ( buffer [ i ] = = ' \r ' | | buffer [ i ] = = ' \n ' )
buffer [ i ] = ' * ' ;
logger ( LOG , " request " , buffer , hit ) ;
if ( strncmp ( buffer , " GET " , 4 ) & & strncmp ( buffer , " get " , 4 ) ) {
logger ( FORBIDDEN , " Only simple GET operation supported " , buffer , fd ) ;
}
for ( i = 4 ; i < BUFSIZE ; i + + ) { /* null terminate after the second space to ignore extra stuff */
if ( buffer [ i ] = = ' ' ) { /* string is "GET URL " +lots of other stuff */
buffer [ i ] = 0 ;
break ;
}
}
for ( j = 0 ; j < i - 1 ; j + + ) /* check for illegal parent directory use .. */
if ( buffer [ j ] = = ' . ' & & buffer [ j + 1 ] = = ' . ' ) {
logger ( FORBIDDEN , " Parent directory (..) path names not supported " , buffer , fd ) ;
}
if ( ! strncmp ( & buffer [ 0 ] , " GET / \0 " , 6 ) | | ! strncmp ( & buffer [ 0 ] , " get / \0 " , 6 ) ) /* convert no filename to index file */
( void ) strcpy ( buffer , " GET /index.html " ) ;
/* work out the file type and check we support it */
buflen = strlen ( buffer ) ;
fstr = ( char * ) 0 ;
for ( i = 0 ; extensions [ i ] . ext ! = 0 ; i + + ) {
len = strlen ( extensions [ i ] . ext ) ;
if ( ! strncmp ( & buffer [ buflen - len ] , extensions [ i ] . ext , len ) ) {
fstr = extensions [ i ] . filetype ;
break ;
}
}
if ( fstr = = 0 ) logger ( FORBIDDEN , " file extension type not supported " , buffer , fd ) ;
if ( ( file_fd = open ( & buffer [ 5 ] , O_RDONLY ) ) = = - 1 ) { /* open the file for reading */
logger ( NOTFOUND , " failed to open file " , & buffer [ 5 ] , fd ) ;
}
logger ( LOG , " SEND " , & buffer [ 5 ] , hit ) ;
len = ( long ) lseek ( file_fd , ( off_t ) 0 , SEEK_END ) ; /* lseek to the file end to find the length */
( void ) lseek ( file_fd , ( off_t ) 0 , SEEK_SET ) ; /* lseek back to the file start ready for reading */
( void ) sprintf ( buffer , " HTTP/1.1 200 OK \n Server: nweb/%d.0 \n Content-Length: %ld \n Connection: close \n Content-Type: %s \n \n " , VERSION , len , fstr ) ; /* Header + a blank line */
logger ( LOG , " Header " , buffer , hit ) ;
2018-08-02 22:48:09 +02:00
( void ) nweb_write ( fd , buffer , strlen ( buffer ) ) ;
2018-08-02 15:41:30 +02:00
/* send file in 8KB block - last block may be smaller */
while ( ( ret = read ( file_fd , buffer , BUFSIZE ) ) > 0 ) {
2018-08-02 22:48:09 +02:00
( void ) nweb_write ( fd , buffer , ret ) ;
2018-08-02 15:41:30 +02:00
}
# ifdef __hermit__
2018-08-02 21:11:52 +02:00
close ( fd ) ;
2018-08-02 15:41:30 +02:00
# else
sleep ( 1 ) ; /* allow socket to drain before signalling the socket is closed */
close ( fd ) ;
exit ( 1 ) ;
2018-08-02 21:11:52 +02:00
# endif
2018-08-02 15:41:30 +02:00
}
# ifdef __hermit__
# include <pthread.h>
typedef struct {
int fd ;
int hit ;
} web_t ;
2018-08-02 21:11:52 +02:00
static void * thread_func ( void * arg )
2018-08-02 15:41:30 +02:00
{
web_t * w = ( web_t * ) arg ;
web ( w - > fd , w - > hit ) ;
free ( w ) ;
return NULL ;
}
2018-08-02 21:11:52 +02:00
static void webthr ( int fd , int hit )
2018-08-02 15:41:30 +02:00
{
web_t * w = malloc ( sizeof ( web_t ) ) ;
w - > fd = fd ;
w - > hit = hit ;
int ret = pthread_create ( NULL , NULL , thread_func , ( void * ) w ) ;
if ( ret )
fprintf ( stderr , " Unable to create thread: %d \n " , ret ) ;
2018-08-02 21:11:52 +02:00
sched_yield ( ) ;
2018-08-02 15:41:30 +02:00
}
# endif
int main ( int argc , char * * argv )
{
int i , port , pid , listenfd , socketfd , hit ;
socklen_t length ;
static struct sockaddr_in cli_addr ; /* static = initialised to zeros */
static struct sockaddr_in serv_addr ; /* static = initialised to zeros */
# ifndef __hermit__
if ( argc < 3 | | argc > 3 | | ! strcmp ( argv [ 1 ] , " -? " ) ) {
( void ) printf ( " hint: nweb Port-Number Top-Directory \t \t version %d \n \n "
" \t nweb is a small and very safe mini web server \n "
" \t nweb only servers out file/web pages with extensions named below \n "
" \t and only from the named directory or its sub-directories. \n "
" \t There is no fancy features = safe and secure. \n \n "
" \t Example: nweb 8181 /home/nwebdir & \n \n "
" \t Only Supports: " , VERSION ) ;
for ( i = 0 ; extensions [ i ] . ext ! = 0 ; i + + )
( void ) printf ( " %s " , extensions [ i ] . ext ) ;
( void ) printf ( " \n \t Not Supported: URLs including \" .. \" , Java, Javascript, CGI \n "
" \t Not Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n "
" \t No warranty given or implied \n \t Nigel Griffiths nag@uk.ibm.com \n " ) ;
exit ( 0 ) ;
}
if ( ! strncmp ( argv [ 2 ] , " / " , 2 ) | | ! strncmp ( argv [ 2 ] , " /etc " , 5 ) | |
! strncmp ( argv [ 2 ] , " /bin " , 5 ) | | ! strncmp ( argv [ 2 ] , " /lib " , 5 ) | |
! strncmp ( argv [ 2 ] , " /tmp " , 5 ) | | ! strncmp ( argv [ 2 ] , " /usr " , 5 ) | |
! strncmp ( argv [ 2 ] , " /dev " , 5 ) | | ! strncmp ( argv [ 2 ] , " /sbin " , 6 ) ) {
( void ) printf ( " ERROR: Bad top directory %s, see nweb -? \n " , argv [ 2 ] ) ;
exit ( 3 ) ;
}
if ( chdir ( argv [ 2 ] ) = = - 1 ) {
( void ) printf ( " ERROR: Can't Change to directory %s \n " , argv [ 2 ] ) ;
exit ( 4 ) ;
}
/* Become deamon + unstopable and no zombies children (= no wait()) */
if ( fork ( ) ! = 0 )
return 0 ; /* parent returns OK to shell */
( void ) signal ( SIGCLD , SIG_IGN ) ; /* ignore child death */
( void ) signal ( SIGHUP , SIG_IGN ) ; /* ignore terminal hangups */
for ( i = 0 ; i < 32 ; i + + )
( void ) close ( i ) ; /* close open files */
( void ) setpgrp ( ) ; /* break away from process group */
# else
if ( argc < 2 | | argc > 2 | | ! strcmp ( argv [ 1 ] , " -? " ) ) {
( void ) printf ( " hint: nweb Port-Number Top-Directory \t \t version %d \n \n "
" \t nweb is a small and very safe mini web server \n "
" \t nweb only servers out file/web pages with extensions named below \n "
" \t and only from the named directory or its sub-directories. \n "
" \t There is no fancy features = safe and secure. \n \n "
" \t Example: nweb 8181 & \n \n "
" \t Supported: current working directory \n "
" \t Only Supports: " , VERSION ) ;
for ( i = 0 ; extensions [ i ] . ext ! = 0 ; i + + )
( void ) printf ( " %s " , extensions [ i ] . ext ) ;
( void ) printf ( " \n \t Not Supported: URLs including \" .. \" , Java, Javascript, CGI \n "
" \t No warranty given or implied \n \t Nigel Griffiths nag@uk.ibm.com \n " ) ;
exit ( 0 ) ;
}
# endif
logger ( LOG , " nweb starting " , argv [ 1 ] , getpid ( ) ) ;
/* setup the network socket */
if ( ( listenfd = socket ( AF_INET , SOCK_STREAM , 0 ) ) < 0 )
logger ( ERROR , " system call " , " socket " , 0 ) ;
port = atoi ( argv [ 1 ] ) ;
if ( port < 0 | | port > 60000 )
logger ( ERROR , " Invalid port number (try 1->60000) " , argv [ 1 ] , 0 ) ;
serv_addr . sin_family = AF_INET ;
serv_addr . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
serv_addr . sin_port = htons ( port ) ;
if ( bind ( listenfd , ( struct sockaddr * ) & serv_addr , sizeof ( serv_addr ) ) < 0 )
logger ( ERROR , " system call " , " bind " , 0 ) ;
if ( listen ( listenfd , 64 ) < 0 )
logger ( ERROR , " system call " , " listen " , 0 ) ;
for ( hit = 1 ; ; hit + + ) {
length = sizeof ( cli_addr ) ;
if ( ( socketfd = accept ( listenfd , ( struct sockaddr * ) & cli_addr , & length ) ) < 0 )
logger ( ERROR , " system call " , " accept " , 0 ) ;
# ifdef __hermit__
2018-08-02 21:11:52 +02:00
//webthr(socketfd,hit);
web ( socketfd , hit ) ;
2018-08-02 15:41:30 +02:00
# else
if ( ( pid = fork ( ) ) < 0 ) {
logger ( ERROR , " system call " , " fork " , 0 ) ;
}
else {
if ( pid = = 0 ) { /* child */
( void ) close ( listenfd ) ;
web ( socketfd , hit ) ; /* never returns */
} else { /* parent */
( void ) close ( socketfd ) ;
}
}
# endif
}
}