nweb - Tiny and Safe Web Server in 200 Lines of C Code on FreeBSD


15:03 | 0 Comments

IBM employee Nigel Griffiths published and regularly update an interesting article [1] on developerWorks about how a web server works. He actually wrote tiny web server named nweb in 200 lines C code and explains how each part of the code is used to serve the static content. Below is small quote from the article introduction.

Have you ever wanted to run a tiny, safe web server without worrying about using a fully blown web server that could be complex to install and configure? Do you wonder how to write a program that accepts incoming messages with a network socket? Have you ever just wanted your own Web server to experiment and learn with? Further updates in 2012 to support recent web-server and browser standards and a code refresh.

Well, look no further - nweb is what you need. This is a simple Web server that has only 200 lines of C source code. It runs as a regular user and can't run any server-side scripts or programs, so it can't open up any special privileges or security holes.

This article covers:

  • What the nweb server program offers
  • Summary of C functions features in the program
  • Pseudo code to aid understanding of the flow of the code
  • Network socket system calls used and other system calls
  • How the client side operates
  • C source code

At the bottom of the page You can download the nweb source code (file nweb23.c after extract), the code compiles and runs on the following systems:

  • AIX 6.1 TL7 (POWER)
  • Ubuntu 12.4 (amd64)
  • Fedora 17 Linux (amd64)
  • OpenSUSE 12.1 (amd64)
  • Debian Squeeze (ARMv6/Raspberry Pi)

... but it will not compile on FreeBSD:

# gcc nweb23.c
nweb23.c: In function 'main':
nweb23.c:165: error: 'SIGCLD' undeclared (first use in this function)
nweb23.c:165: error: (Each undeclared identifier is reported only once
nweb23.c:165: error: for each function it appears in.)
nweb23.c:169: error: too few arguments to function 'setpgrp'

% clang nweb23.c
nweb23.c:165:15: error: use of undeclared identifier 'SIGCLD'
        (void)signal(SIGCLD, SIG_IGN); /* ignore child death */
                     ^
nweb23.c:169:16: error: too few arguments to function call, expected 2, have 0
        (void)setpgrp();                /* break away from process group */
              ~~~~~~~ ^
/usr/include/unistd.h:455:1: note: 'setpgrp' declared here
int      setpgrp(pid_t _pid, pid_t _pgrp); /* obsoleted by setpgid() */
^
2 errors generated.

To fix the undeclared SIGCLD error we need to add this after the #include (...) and #define (...) lines.

#ifndef SIGCLD
#  define SIGCLD SIGCHLD
#endif

So this is what we changed so far comparing to the nweb23.o.c original source code file.

--- nweb23.o.c    2012-08-19 23:15:45.000000000 +0200
+++ nweb23.c      2013-03-12 14:07:16.021757627 +0100
@@ -15,6 +15,9 @@
 #define LOG        44
 #define FORBIDDEN 403
 #define NOTFOUND  404
+#ifndef SIGCLD
+#  define SIGCLD SIGCHLD
+#endif

 struct {
        char *ext;

Lets try the compilation now.

% gcc nweb23.c
nweb23.c: In function 'main':
nweb23.c:172: error: too few arguments to function 'setpgrp'

% clang nweb23.c
nweb23.c:172:16: error: too few arguments to function call, expected 2, have 0
        (void)setpgrp();                /* break away from process group */
              ~~~~~~~ ^
/usr/include/unistd.h:455:1: note: 'setpgrp' declared here
int      setpgrp(pid_t _pid, pid_t _pgrp); /* obsoleted by setpgid() */
^
1 error generated.

So we have one error left. The setpgrp(); function in the code is executed without arguments while it needs two setpgrp(pid_t _pid, pid_t _pgrp);, lets fix that. In line 172 we will put (void)setpgrp(getpid(),getpid()); instead of (void)setpgrp(); function.

% gcc nweb23.c
% echo $?
0

% clang nweb23.c
% echo $?
0

Viola! It now compiles, here is the diff(1) for both changes.

--- nweb23.o.c    2012-08-19 23:15:45.000000000 +0200
+++ nweb23.c      2013-03-12 14:20:10.561753772 +0100
@@ -15,6 +15,9 @@
 #define LOG        44
 #define FORBIDDEN 403
 #define NOTFOUND  404
+#ifndef SIGCLD
+#  define SIGCLD SIGCHLD
+#endif

 struct {
        char *ext;
@@ -166,7 +169,7 @@
        (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 */
+       (void)setpgrp(getpid(),getpid());               /* break away from process group */
        logger(LOG,"nweb starting",argv[1],getpid());
        /* setup the network socket */
        if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0)

Lets now try how (and if) it works.

% ./a.out
hint: nweb Port-Number Top-Directory            version 23

        nweb is a small and very safe mini web server
        nweb only servers out file/web pages with extensions named below
         and only from the named directory or its sub-directories.
        There is no fancy features = safe and secure.

        Example: nweb 8181 /home/nwebdir &

        Only Supports: gif jpg jpeg png ico zip gz tar htm html
        Not Supported: URLs including "..", Java, Javascript, CGI
        Not Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin
        No warranty given or implied
        Nigel Griffiths nag@uk.ibm.com

The nweb server seems to work, lets try to serve the directory with extracted files.

% ./a.out 8080 ./

% echo $?
0

% ps ax | grep a.out
40755  7  S      0:00.00 ./a.out 8080 ./

% netstat -a -n -f inet | grep 8080
tcp4       0      0 *.8080                 *.*                    LISTEN

It seems to work, lets try that in the browser.

I did not thought that I would ever quote the default Apache page but, "It works!".

[1] http://www.ibm.com/developerworks/systems/library/es-nweb/index.html




Post a Comment