simple_httpd.c revision 189936
143939Sabial/*- 246493Sabial * Simple_HTTPd v1.1 - a very small, barebones HTTP server 343939Sabial * 443939Sabial * Copyright (c) 1998-1999 Marc Nicholas <marc@netstor.com> 543939Sabial * All rights reserved. 643939Sabial * 746493Sabial * Major rewrite by William Lloyd <wlloyd@slap.net> 846493Sabial * 943939Sabial * Redistribution and use in source and binary forms, with or without 1043939Sabial * modification, are permitted provided that the following conditions 1143939Sabial * are met: 1243939Sabial * 1. Redistributions of source code must retain the above copyright 1343939Sabial * notice, this list of conditions and the following disclaimer. 1443939Sabial * 2. Redistributions in binary form must reproduce the above copyright 1543939Sabial * notice, this list of conditions and the following disclaimer in the 1643939Sabial * documentation and/or other materials provided with the distribution. 1743939Sabial * 1843939Sabial * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1943939Sabial * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2043939Sabial * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2143939Sabial * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2243939Sabial * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2343939Sabial * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2443939Sabial * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2543939Sabial * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2643939Sabial * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2743939Sabial * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2843939Sabial * SUCH DAMAGE. 2943939Sabial * 3050479Speter * $FreeBSD: head/release/picobsd/tinyware/simple_httpd/simple_httpd.c 189936 2009-03-17 19:51:04Z dwmalone $ 3138589Sabial */ 3238589Sabial 3338589Sabial#include <sys/stat.h> 3438589Sabial#include <sys/time.h> 3538589Sabial#include <sys/types.h> 3638589Sabial#include <sys/socket.h> 3746493Sabial#include <sys/wait.h> 3838589Sabial#include <netinet/in.h> 3938589Sabial#include <arpa/inet.h> 4046493Sabial 4146493Sabial#include <fcntl.h> 4238589Sabial#include <netdb.h> 4346493Sabial#include <signal.h> 4446493Sabial#include <stdio.h> 45189936Sdwmalone#include <stdint.h> 4646493Sabial#include <stdlib.h> 4738589Sabial#include <string.h> 4846493Sabial#include <time.h> 4946493Sabial#include <unistd.h> 5038589Sabial 5146493Sabialint http_port = 80; 5246493Sabialint daemonize = 1; 5346493Sabialint verbose = 0; 5438589Sabialint http_sock, con_sock; 5546493Sabial 56113126Sdwmaloneconst char *fetch_mode = NULL; 5746493Sabialchar homedir[100]; 5846493Sabialchar logfile[80]; 59113126Sdwmalonechar *adate(void); 60113126Sdwmalonevoid init_servconnection(void); 61113126Sdwmalonevoid http_date(void); 62113126Sdwmalonevoid http_output(const char *html); 63113126Sdwmalonevoid http_request(void); 64113126Sdwmalonevoid log_line(char *req); 65113126Sdwmalonevoid wait_connection(void); 6646493Sabial 6738589Sabialstruct hostent *hst; 6846493Sabialstruct sockaddr_in source; 6938589Sabial 7046493Sabial/* HTTP basics */ 7146493Sabialstatic char httpd_server_ident[] = "Server: FreeBSD/PicoBSD simple_httpd 1.1\r"; 7246493Sabial 7346493Sabialstatic char http_200[] = "HTTP/1.0 200 OK\r"; 7446493Sabial 75133836Sdwmaloneconst char *default_mime_type = "application/octet-stream"; 76133836Sdwmalone 77133836Sdwmaloneconst char *mime_type[][2] = { 78133836Sdwmalone { "txt", "text/plain" }, 79133836Sdwmalone { "htm", "text/html" }, 80133836Sdwmalone { "html", "text/html" }, 81133836Sdwmalone { "gif", "image/gif" }, 82133836Sdwmalone { "jpg", "image/jpeg" }, 83133836Sdwmalone { "mp3", "audio/mpeg" } 84133836Sdwmalone}; 85133836Sdwmalone 86133836Sdwmaloneconst int mime_type_max = sizeof(mime_type) / sizeof(mime_type[0]) - 1; 87133836Sdwmalone 8846493Sabial/* Two parts, HTTP Header and then HTML */ 89113126Sdwmalonestatic const char *http_404[2] = 9046493Sabial {"HTTP/1.0 404 Not found\r\n", 9146493Sabial"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 404</H1>\ 9246493SabialNot found - file doesn't exist or you do not have permission.\n</BODY></HTML>\r\n" 9346493Sabial}; 9446493Sabial 95113126Sdwmalonestatic const char *http_405[2] = 9646493Sabial {"HTTP/1.0 405 Method Not allowed\r\nAllow: GET,HEAD\r\n", 9746493Sabial"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 405</H1>\ 9846493SabialThis server only supports GET and HEAD requests.\n</BODY></HTML>\r\n" 9946493Sabial}; 10046493Sabial 10146493Sabial/* 10246493Sabial * Only called on initial invocation 10346493Sabial */ 10438589Sabialvoid 10538589Sabialinit_servconnection(void) 10638589Sabial{ 10738589Sabial struct sockaddr_in server; 10838589Sabial 10938589Sabial /* Create a socket */ 11038589Sabial http_sock = socket(AF_INET, SOCK_STREAM, 0); 11138589Sabial if (http_sock < 0) { 11238589Sabial perror("socket"); 11338589Sabial exit(1); 11438589Sabial } 11538589Sabial server.sin_family = AF_INET; 11638589Sabial server.sin_port = htons(http_port); 11738589Sabial server.sin_addr.s_addr = INADDR_ANY; 11838589Sabial if (bind(http_sock, (struct sockaddr *) & server, sizeof(server)) < 0) { 11938589Sabial perror("bind socket"); 12038589Sabial exit(1); 12138589Sabial } 12294130Sasmodai if (verbose) printf("simple_httpd:%d\n",http_port); 12338589Sabial} 12438589Sabial 12546493Sabial/* 12646493Sabial * Wait here until we see an incoming http request 12746493Sabial */ 12894134Sasmodaivoid 12946493Sabialwait_connection(void) 13038589Sabial{ 131113126Sdwmalone socklen_t lg; 13238589Sabial 13338589Sabial lg = sizeof(struct sockaddr_in); 13438589Sabial 13538589Sabial con_sock = accept(http_sock, (struct sockaddr *) & source, &lg); 13638589Sabial if (con_sock <= 0) { 13738589Sabial perror("accept"); 13838589Sabial exit(1); 13938589Sabial } 14038589Sabial} 14138589Sabial 14246493Sabial/* 14346493Sabial * Print timestamp for HTTP HEAD and GET 14446493Sabial */ 14594134Sasmodaivoid 14694134Sasmodaihttp_date(void) 14738589Sabial{ 14838589Sabial time_t tl; 14938589Sabial char buff[50]; 15038589Sabial 15138589Sabial tl = time(NULL); 15238589Sabial strftime(buff, 50, "Date: %a, %d %h %Y %H:%M:%S %Z\r\n", gmtime(&tl)); 15338589Sabial write(con_sock, buff, strlen(buff)); 154113126Sdwmalone /* return(buff); */ 15538589Sabial} 15638589Sabial 15746493Sabial/* 15846493Sabial * Send data to the open socket 15946493Sabial */ 16094134Sasmodaivoid 161113126Sdwmalonehttp_output(const char *html) 16246493Sabial{ 16346493Sabial write(con_sock, html, strlen(html)); 16446493Sabial write(con_sock, "\r\n", 2); 16546493Sabial} 16638589Sabial 16738589Sabial 16846493Sabial/* 16946493Sabial * Create and write the log information to file 17046493Sabial * Log file format is one line per entry 17146493Sabial */ 17294134Sasmodaivoid 17346493Sabiallog_line(char *req) 17438589Sabial{ 17546493Sabial char log_buff[256]; 17646493Sabial char msg[1024]; 17746493Sabial char env_host[80], env_addr[80]; 17846493Sabial long addr; 17946493Sabial FILE *log; 18038589Sabial 18146493Sabial strcpy(log_buff,inet_ntoa(source.sin_addr)); 18246493Sabial sprintf(env_addr, "REMOTE_ADDR=%s",log_buff); 18346493Sabial 18446493Sabial addr=inet_addr(log_buff); 18546493Sabial 18646493Sabial strcpy(msg,adate()); 18746493Sabial strcat(msg," "); 18846493Sabial hst=gethostbyaddr((char*) &addr, 4, AF_INET); 18946493Sabial 19046493Sabial /* If DNS hostname exists */ 19146493Sabial if (hst) { 19246493Sabial strcat(msg,hst->h_name); 19346493Sabial sprintf(env_host, "REMOTE_HOST=%s",hst->h_name); 19438589Sabial } 19546493Sabial strcat(msg," ("); 19646493Sabial strcat(msg,log_buff); 19746493Sabial strcat(msg,") "); 19846493Sabial strcat(msg,req); 19946493Sabial 20046493Sabial if (daemonize) { 20146493Sabial log=fopen(logfile,"a"); 20246493Sabial fprintf(log,"%s\n",msg); 20346493Sabial fclose(log); 20446493Sabial } else 20546493Sabial printf("%s\n",msg); 20646493Sabial 20746493Sabial /* This is for CGI scripts */ 20846493Sabial putenv(env_addr); 20946493Sabial putenv(env_host); 21038589Sabial} 21138589Sabial 21246493Sabial/* 21346493Sabial * We have a connection. Identify what type of request GET, HEAD, CGI, etc 21446493Sabial * and do what needs to be done 21546493Sabial */ 21694134Sasmodaivoid 21794135Sasmodaihttp_request(void) 21838589Sabial{ 21994132Sasmodai int fd, lg, i; 22046493Sabial int cmd = 0; 22146493Sabial char *p, *par; 222133836Sdwmalone const char *filename, *c, *ext, *type; 22346493Sabial struct stat file_status; 22438589Sabial char req[1024]; 22546493Sabial char buff[8192]; 22638589Sabial 22738589Sabial lg = read(con_sock, req, 1024); 22838589Sabial 229113126Sdwmalone if ((p=strstr(req,"\n"))) *p=0; 230113126Sdwmalone if ((p=strstr(req,"\r"))) *p=0; 23138589Sabial 23246493Sabial log_line(req); 23338589Sabial 23446493Sabial c = strtok(req, " "); 23538589Sabial 23646493Sabial /* Error msg if request is nothing */ 23746493Sabial if (c == NULL) { 23846493Sabial http_output(http_404[0]); 23946493Sabial http_output(http_404[1]); 24046493Sabial goto end_request; 24146493Sabial } 24238589Sabial 24346493Sabial if (strncmp(c, "GET", 3) == 0) cmd = 1; 24446493Sabial if (strncmp(c, "HEAD", 4) == 0) cmd = 2; 24538589Sabial 24646493Sabial /* Do error msg for any other type of request */ 24746493Sabial if (cmd == 0) { 24846493Sabial http_output(http_405[0]); 24946493Sabial http_output(http_405[1]); 25046493Sabial goto end_request; 25138589Sabial } 25238589Sabial 25338589Sabial filename = strtok(NULL, " "); 25438589Sabial 25538589Sabial c = strtok(NULL, " "); 256113126Sdwmalone if (fetch_mode != NULL) filename=fetch_mode; 25738589Sabial if (filename == NULL || 25838589Sabial strlen(filename)==1) filename="/index.html"; 25938589Sabial 26046493Sabial while (filename[0]== '/') filename++; 26146493Sabial 26246493Sabial /* CGI handling. Untested */ 26338589Sabial if (!strncmp(filename,"cgi-bin/",8)) 26438589Sabial { 26538589Sabial par=0; 266113126Sdwmalone if ((par=strstr(filename,"?"))) 26738589Sabial { 26838589Sabial *par=0; 26938589Sabial par++; 27038589Sabial } 27138589Sabial if (access(filename,X_OK)) goto conti; 27246493Sabial stat (filename,&file_status); 273112205Sdwmalone if (setuid(file_status.st_uid)) return; 274112205Sdwmalone if (seteuid(file_status.st_uid)) return; 27538589Sabial if (!fork()) 27638589Sabial { 27738589Sabial close(1); 27838589Sabial dup(con_sock); 279113126Sdwmalone /*printf("HTTP/1.0 200 OK\nContent-type: text/html\n\n\n");*/ 28046493Sabial printf("HTTP/1.0 200 OK\r\n"); 28146493Sabial /* Plug in environment variable, others in log_line */ 282189936Sdwmalone setenv("SERVER_SOFTWARE", "FreeBSD/PicoBSD", 1); 28346493Sabial 28479452Sbrian execlp (filename,filename,par,(char *)0); 28538589Sabial } 28638589Sabial wait(&i); 287112205Sdwmalone return; 28838589Sabial } 28938589Sabial conti: 29038589Sabial if (filename == NULL) { 29146493Sabial http_output(http_405[0]); 29246493Sabial http_output(http_405[1]); 29346493Sabial goto end_request; 29438589Sabial } 29546493Sabial /* End of CGI handling */ 29646493Sabial 29746493Sabial /* Reject any request with '..' in it, bad hacker */ 29838589Sabial c = filename; 29938589Sabial while (*c != '\0') 30046493Sabial if (c[0] == '.' && c[1] == '.') { 30146493Sabial http_output(http_404[0]); 30246493Sabial http_output(http_404[1]); 30346493Sabial goto end_request; 30446493Sabial } else 30546493Sabial c++; 30646493Sabial 30746493Sabial /* Open filename */ 30838589Sabial fd = open(filename, O_RDONLY); 30938589Sabial if (fd < 0) { 31046493Sabial http_output(http_404[0]); 31146493Sabial http_output(http_404[1]); 31246493Sabial goto end_request; 31338589Sabial } 31446493Sabial 31546493Sabial /* Get file status information */ 31646493Sabial if (fstat(fd, &file_status) < 0) { 31746493Sabial http_output(http_404[0]); 31846493Sabial http_output(http_404[1]); 319113126Sdwmalone goto end_request2; 32038589Sabial } 32146493Sabial 32246493Sabial /* Is it a regular file? */ 32346493Sabial if (!S_ISREG(file_status.st_mode)) { 32446493Sabial http_output(http_404[0]); 32546493Sabial http_output(http_404[1]); 326113126Sdwmalone goto end_request2; 32746493Sabial } 32838589Sabial 32946493Sabial /* Past this point we are serving either a GET or HEAD */ 33046493Sabial /* Print all the header info */ 33146493Sabial http_output(http_200); 33246493Sabial http_output(httpd_server_ident); 33346493Sabial http_date(); 33438589Sabial 335189936Sdwmalone sprintf(buff, "Content-length: %jd\r\n", (intmax_t)file_status.st_size); 336113129Sdwmalone write(con_sock, buff, strlen(buff)); 33738589Sabial 338133836Sdwmalone strcpy(buff, "Content-type: "); 339133836Sdwmalone type = default_mime_type; 340133836Sdwmalone if ((ext = strrchr(filename, '.')) != NULL) { 341133836Sdwmalone for (i = mime_type_max; i >= 0; i--) 342133836Sdwmalone if (strcmp(ext + 1, mime_type[i][0]) == 0) { 343133836Sdwmalone type = mime_type[i][1]; 344133836Sdwmalone break; 345133836Sdwmalone } 34646493Sabial } 347133836Sdwmalone strcat(buff, type); 348133836Sdwmalone http_output(buff); 34946493Sabial 35046493Sabial strftime(buff, 50, "Last-Modified: %a, %d %h %Y %H:%M:%S %Z\r\n\r\n", gmtime(&file_status.st_mtime)); 35146493Sabial write(con_sock, buff, strlen(buff)); 35238589Sabial 35346493Sabial /* Send data only if GET request */ 35438589Sabial if (cmd == 1) { 355113126Sdwmalone while ((lg = read(fd, buff, 8192)) > 0) 35646493Sabial write(con_sock, buff, lg); 35738589Sabial } 35838589Sabial 359113126Sdwmaloneend_request2: 360113126Sdwmalone close(fd); 36146493Sabialend_request: 36238589Sabial close(con_sock); 36338589Sabial 36438589Sabial} 36538589Sabial 36646493Sabial/* 36746493Sabial * Simple httpd server for use in PicoBSD or other embedded application. 36846493Sabial * Should satisfy simple httpd needs. For more demanding situations 36946493Sabial * apache is probably a better (but much larger) choice. 37046493Sabial */ 37194134Sasmodaiint 37246493Sabialmain(int argc, char *argv[]) 37338589Sabial{ 37494132Sasmodai int ch, ld; 37546493Sabial int httpd_group = 65534; 37646493Sabial pid_t server_pid; 37746493Sabial 37846493Sabial /* Default for html directory */ 37938589Sabial strcpy (homedir,getenv("HOME")); 38038589Sabial if (!geteuid()) strcpy (homedir,"/httphome"); 38138589Sabial else strcat (homedir,"/httphome"); 38238589Sabial 38346493Sabial /* Defaults for log file */ 38446493Sabial if (geteuid()) { 38546493Sabial strcpy(logfile,getenv("HOME")); 38646493Sabial strcat(logfile,"/"); 38746493Sabial strcat(logfile,"jhttp.log"); 38846493Sabial } else 38946493Sabial strcpy(logfile,"/var/log/jhttpd.log"); 39038589Sabial 39146493Sabial /* Parse command line arguments */ 39246493Sabial while ((ch = getopt(argc, argv, "d:f:g:l:p:vDh")) != -1) 39346493Sabial switch (ch) { 39446493Sabial case 'd': 39546493Sabial strcpy(homedir,optarg); 39646493Sabial break; 39746493Sabial case 'f': 39846493Sabial daemonize = 0; 39946493Sabial verbose = 1; 400113126Sdwmalone fetch_mode = optarg; 40146493Sabial break; 40246493Sabial case 'g': 40346493Sabial httpd_group = atoi(optarg); 40446493Sabial break; 40546493Sabial case 'l': 40646493Sabial strcpy(logfile,optarg); 40746493Sabial break; 40846493Sabial case 'p': 40946493Sabial http_port = atoi(optarg); 41046493Sabial break; 41146493Sabial case 'v': 41246493Sabial verbose = 1; 41346493Sabial break; 41446493Sabial case 'D': 41546493Sabial daemonize = 0; 41646493Sabial break; 41746493Sabial case '?': 41846493Sabial case 'h': 41946493Sabial default: 42046493Sabial printf("usage: simple_httpd [[-d directory][-g grpid][-l logfile][-p port][-vD]]\n"); 42146493Sabial exit(1); 42246493Sabial /* NOTREACHED */ 42346493Sabial } 42446493Sabial 42546493Sabial /* Not running as root and no port supplied, assume 1080 */ 42646493Sabial if ((http_port == 80) && geteuid()) { 42746493Sabial http_port = 1080; 42846493Sabial } 42946493Sabial 43046493Sabial /* Do we really have rights in the html directory? */ 431113126Sdwmalone if (fetch_mode == NULL) { 43246493Sabial if (chdir(homedir)) { 43346493Sabial perror("chdir"); 43446493Sabial puts(homedir); 43546493Sabial exit(1); 43646493Sabial } 43746493Sabial } 43846493Sabial 43946493Sabial /* Create log file if it doesn't exit */ 44046493Sabial if ((access(logfile,W_OK)) && daemonize) { 44146493Sabial ld = open (logfile,O_WRONLY); 44246493Sabial chmod (logfile,00600); 44346493Sabial close(ld); 44446493Sabial } 44546493Sabial 44638589Sabial init_servconnection(); 44738589Sabial 44846493Sabial if (verbose) { 44946493Sabial printf("Server started with options \n"); 45046493Sabial printf("port: %d\n",http_port); 451113126Sdwmalone if (fetch_mode == NULL) printf("html home: %s\n",homedir); 45246493Sabial if (daemonize) printf("logfile: %s\n",logfile); 45346493Sabial } 45438589Sabial 45546493Sabial /* httpd is spawned */ 45646493Sabial if (daemonize) { 457113126Sdwmalone if ((server_pid = fork()) != 0) { 45846493Sabial wait3(0,WNOHANG,0); 45946493Sabial if (verbose) printf("pid: %d\n",server_pid); 46046493Sabial exit(0); 46146493Sabial } 46246493Sabial wait3(0,WNOHANG,0); 46346493Sabial } 46438589Sabial 465113126Sdwmalone if (fetch_mode == NULL) setpgrp(0,httpd_group); 46646493Sabial 46746493Sabial /* How many connections do you want? 46846493Sabial * Keep this lower than the available number of processes 46946493Sabial */ 47046493Sabial if (listen(http_sock,15) < 0) exit(1); 47146493Sabial 47238589Sabial label: 47346493Sabial wait_connection(); 47446493Sabial 47546493Sabial if (fork()) { 47646493Sabial wait3(0,WNOHANG,0); 47746493Sabial close(con_sock); 47846493Sabial goto label; 47946493Sabial } 48046493Sabial 48146493Sabial http_request(); 48246493Sabial 48346493Sabial wait3(0,WNOHANG,0); 48446493Sabial exit(0); 48538589Sabial} 48638589Sabial 48738589Sabial 488113126Sdwmalonechar * 489113126Sdwmaloneadate(void) 49038589Sabial{ 49138589Sabial static char out[50]; 492112205Sdwmalone time_t now; 49338589Sabial struct tm *t; 49438589Sabial time(&now); 49538589Sabial t = localtime(&now); 49643939Sabial sprintf(out, "%02d:%02d:%02d %02d/%02d/%02d", 49743939Sabial t->tm_hour, t->tm_min, t->tm_sec, 49843939Sabial t->tm_mday, t->tm_mon+1, t->tm_year ); 49938589Sabial return out; 50038589Sabial} 501