Simple web server for Linux

A nice accomplishment, I have coded my first web server. Made in C for Linux. To run it, pass 2 arguments to it: the port number and the absolute web root directory. Inside your webroot directory you must have _mimes.txt, everything else is optional.

Download source, binary, and sample webroot directory: http://abacus.subluminal.net/~jakashthree/Jakash3_webserver.tar.gz

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <dirent.h>
#include <sys/stat.h>

char* webroot;
int server;

void sessions();
int mime(char* dst, const char* fname);
int filter(char* ipaddr, char* request);
void flist(const char* dir);
char* http_head(char* dst, const char* ipstr, char* fname);
void do_http(int client, const char* ipstr, char* header);

void help() {
   puts(
      "Jakash3's first webserver\n"
      "Arguments: PORT WEBROOT_DIR"
   );
   exit(1);
}

void quit() { shutdown(server, SHUT_RD | SHUT_WR); close(server); exit(0); }

int main(int argc, char** argv) {
   if (argc != 3) help();
   webroot = argv[2];
   int i;
   int ppid = getpid();
   struct sigaction siga;
   siga.sa_handler = &quit;
   siga.sa_flags = 0;
   sigemptyset(&siga.sa_mask);
   sigaction(SIGINT, &siga, 0);
   struct addrinfo* ai;
   struct addrinfo hints;
   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_protocol = IPPROTO_TCP;
   hints.ai_flags = AI_PASSIVE;
   getaddrinfo(0, argv[1], &hints, &ai);
   server = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
   bind(server, ai->ai_addr, ai->ai_addrlen);
   freeaddrinfo(ai);
   listen(server, 5);
   while (1) { for (i = 0; i < 8; i++) if (getpid() == ppid) fork(); sessions(); }
}

void sessions() {
   int i;
   int client;
   FILE* f = 0;
   char* str = 0;
   char buf[512];
   char addr[40];
   struct sockaddr_storage ip;
   socklen_t iplen = sizeof(struct sockaddr_storage);
   chdir(webroot);
   while (1) {
      client = accept(server, 0, 0);
      getpeername(client, (struct sockaddr*)&ip, &iplen);
      if (ip.ss_family == AF_INET)
          inet_ntop(ip.ss_family, &((struct sockaddr_in*)&ip)->sin_addr, addr, 40);
      else if (ip.ss_family == AF_INET6)
          inet_ntop(ip.ss_family, &((struct sockaddr_in6*)&ip)->sin6_addr, addr, 40);
      for (i = 0, buf[1] = 0; i < 512; recv(client, buf + (i++), 1, 0), buf[i] = 0)
         if (!strncmp(buf + i - 4, "\r\n\r\n", 4)) break;
      filter(addr, buf);
      //if (str = strstr(buf, "User-Agent: ")) str += 12;
      fputs(addr, stdout);
      //if ((int)str) { *strchr(str, '\r') = 0; printf("/%s", str); }
      *strchr(buf, '\r') = 0;
      printf(" ---> %s\n", buf);
      buf[strlen(buf)] = '\r';
      do_http(client, addr, buf);
      close(client);
   }
}

void do_http(int client, const char* ipstr, char* header) {
   FILE* f;
   char* rsrc;
   char buf[512];
   chdir(webroot);
   *strchr(header, '\r') = 0;
   *strrchr(header, ' ') = 0;
   rsrc = strchr(header, ' ') + 1;
   *strchr(header, ' ') = 0;
   if (!strcmp(header, "GET") || !strcmp(header, "HEAD"))
      rsrc = http_head(buf, ipstr, rsrc);
   else
      rsrc = http_head(buf, ipstr, "/_400.html");
   send(client, buf, strlen(buf), 0);
   if (!strcmp(header, "GET")) {
      f = fopen(rsrc, "rb");
      while (!feof(f)) { buf[0] = fgetc(f); send(client, buf, 1, 0); }
      fclose(f);
   }
}

char* http_head(char* dst, const char* ipstr, char* fname) {
   int i = 0;
   struct stat finfo;
   time_t t;
   dst[0] = 0;
   chdir(webroot);
   if (!strcmp(fname, "/")) fname = "index.html";
   else fname = fname + 1;
   if (!strcmp(fname, "_400.html")) {
      i = stat(fname, &finfo);
      strcpy(dst, "HTTP/1.1 400 Bad Request\r\n");
   } else if (stat(fname, &finfo) == -1 || fname[0] == '.')
      if (!strcmp(fname, "index.html")) {
          flist(0);
          i = stat(fname = "_ls.html", &finfo);
      } else {
          i = stat(fname = "_404.html", &finfo);
          strcpy(dst, "HTTP/1.1 404 Not Found\r\n");
      }
   if (S_ISDIR(finfo.st_mode)) {
       flist(fname); chdir(webroot);
       i = stat(fname = "_ls.html", &finfo);
   }
   if (!dst[0]) strcpy(dst, "HTTP/1.1 200 OK\r\n");
   time(&t);
   sprintf(dst + strlen(dst), "Date: %s", ctime(&t));
   *strrchr(dst, '\n') = 0;
   strcat(dst, "\r\n");
   sprintf(
      dst + strlen(dst),
      "Server: Jakash3's first webserver\r\n"
      "X-Your-IP: %s",
      ipstr
   );
   if (!i) {
      sprintf(
         dst + strlen(dst),
         "\r\nContent-Length: %d\r\nContent-Type: ",
         (unsigned int)finfo.st_size
      );
      if (!mime(dst, fname)) strcat(dst, "application/octet-stream");
   }
   strcat(dst, "\r\n\r\n");
   return fname;
}

int mime(char* dst, const char* fname) {
   FILE* f;
   int i;
   char buf[512];
   if (!strchr(fname, '.')) return 0;
   fname = strchr(fname, '.') + 1;
   chdir(webroot);
   if (!(f = fopen("_mimes.txt", "r"))) return 0;
   while (!feof(f))
      if (fgets(buf, 512, f))
         if (!strncmp(buf, fname, strlen(fname))) {
            for (i = 0; (int)buf[i]; i++)
               if (isspace(buf[i])) { buf[i] = 0; break; }
            strcat(dst, buf + strlen(fname) + 1);
            fclose(f);
            return 1;
         }
   fclose(f);
   return 0;
}

int filter(char* ipaddr, char* request) {
   FILE* f;
   int i;
   char site[512];
   char buf[512];
   chdir(webroot);
   if (!(f = fopen("_filters.txt", "r"))) return 0;
   while (!feof(f))
      if (fgets(buf, 512, f)) {
         *strchr(buf, '\n') = 0;
         if (buf[0] == '+') strcpy(site, buf + 1);
         else if (!strcmp(ipaddr, buf)) {
            sprintf(request, "GET %s HTTP/1.1\r\n\r\n", site);
            fclose(f);
            return 1;
         }
      }
   fclose(f);
   return 0;
}

void flist(const char* dir) {
   FILE* out;
   DIR* d;
   struct dirent* f;
   struct stat finfo;
   char path[512];
   strcpy(path, webroot);
   if ((int)dir) { strcat(path, "/"); strcat(path, dir); }
   chdir(webroot);
   if (!(out = fopen("_ls.html", "w+"))) return;
   if (!(d = opendir(path))) return;
   if (!(int)dir) dir = "";
   fprintf(
      out,
      "<html><head><title>Index of /%s</title></head>\n"
      "<body><font face=\"monospace\"><h3>Index of /%s</h3>\n"
      "<table border=\"1\">\n"
      "<tr><th>Name</th><th>Bytes</th><th>Modified</tr>\n",
      dir, dir
   );
   chdir(path);
   fputs("<tr><td><a href=\"../\">../</a></td></tr>\n", out);
   while (f = readdir(d)) {
      if (f->d_name[0] == '_' || f->d_name[0] == '.') continue;
      stat(f->d_name, &finfo);
      fprintf(out, "<tr><td><a href=\"%s", f->d_name);
      if (S_ISDIR(finfo.st_mode)) fputc('/', out);
      fprintf(out, "\">%s", f->d_name);
      if (S_ISDIR(finfo.st_mode)) fputc('/', out);
      fprintf(
         out,
         "</a></td><td>%d</td><td>%s</td></tr>\n",
         (unsigned int)finfo.st_size, ctime(&finfo.st_mtime)
      );
   }
   closedir(d);
   fprintf(out, "</table></font></body></html>\n");
   fclose(out);
}

3 thoughts on “Simple web server for Linux

  1. This is nice, I’d sure love to take a look at it.

    Hey, you should start helping me with learning C/C++ again. Can you give me anything for getting started in GUI?

  2. Pingback: Simple Web Server Linux | Cheap Dedicated Servers

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: