simple_httpd.c revision 94135
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 94135 2002-04-07 17:42:27Z asmodai $ 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> 4546493Sabial#include <stdlib.h> 4638589Sabial#include <string.h> 4746493Sabial#include <time.h> 4846493Sabial#include <unistd.h> 4938589Sabial 5046493Sabialint http_port = 80; 5146493Sabialint daemonize = 1; 5246493Sabialint verbose = 0; 5338589Sabialint http_sock, con_sock; 5446493Sabial 5546493Sabialchar fetch_mode[100]; 5646493Sabialchar homedir[100]; 5746493Sabialchar logfile[80]; 5838589Sabialchar *adate(); 5946493Sabial 6038589Sabialstruct hostent *hst; 6146493Sabialstruct sockaddr_in source; 6238589Sabial 6346493Sabial/* HTTP basics */ 6446493Sabialstatic char httpd_server_ident[] = "Server: FreeBSD/PicoBSD simple_httpd 1.1\r"; 6546493Sabial 6646493Sabialstatic char http_200[] = "HTTP/1.0 200 OK\r"; 6746493Sabial 6846493Sabial/* Two parts, HTTP Header and then HTML */ 6946493Sabialstatic char *http_404[2] = 7046493Sabial {"HTTP/1.0 404 Not found\r\n", 7146493Sabial"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 404</H1>\ 7246493SabialNot found - file doesn't exist or you do not have permission.\n</BODY></HTML>\r\n" 7346493Sabial}; 7446493Sabial 7546493Sabialstatic char *http_405[2] = 7646493Sabial {"HTTP/1.0 405 Method Not allowed\r\nAllow: GET,HEAD\r\n", 7746493Sabial"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 405</H1>\ 7846493SabialThis server only supports GET and HEAD requests.\n</BODY></HTML>\r\n" 7946493Sabial}; 8046493Sabial 8146493Sabial/* 8246493Sabial * Only called on initial invocation 8346493Sabial */ 8438589Sabialvoid 8538589Sabialinit_servconnection(void) 8638589Sabial{ 8738589Sabial struct sockaddr_in server; 8838589Sabial 8938589Sabial /* Create a socket */ 9038589Sabial http_sock = socket(AF_INET, SOCK_STREAM, 0); 9138589Sabial if (http_sock < 0) { 9238589Sabial perror("socket"); 9338589Sabial exit(1); 9438589Sabial } 9538589Sabial server.sin_family = AF_INET; 9638589Sabial server.sin_port = htons(http_port); 9738589Sabial server.sin_addr.s_addr = INADDR_ANY; 9838589Sabial if (bind(http_sock, (struct sockaddr *) & server, sizeof(server)) < 0) { 9938589Sabial perror("bind socket"); 10038589Sabial exit(1); 10138589Sabial } 10294130Sasmodai if (verbose) printf("simple_httpd:%d\n",http_port); 10338589Sabial} 10438589Sabial 10546493Sabial/* 10646493Sabial * Wait here until we see an incoming http request 10746493Sabial */ 10894134Sasmodaivoid 10946493Sabialwait_connection(void) 11038589Sabial{ 11138589Sabial int lg; 11238589Sabial 11338589Sabial lg = sizeof(struct sockaddr_in); 11438589Sabial 11538589Sabial con_sock = accept(http_sock, (struct sockaddr *) & source, &lg); 11638589Sabial if (con_sock <= 0) { 11738589Sabial perror("accept"); 11838589Sabial exit(1); 11938589Sabial } 12038589Sabial} 12138589Sabial 12246493Sabial/* 12346493Sabial * Print timestamp for HTTP HEAD and GET 12446493Sabial */ 12594134Sasmodaivoid 12694134Sasmodaihttp_date(void) 12738589Sabial{ 12838589Sabial time_t tl; 12938589Sabial char buff[50]; 13038589Sabial 13138589Sabial tl = time(NULL); 13238589Sabial strftime(buff, 50, "Date: %a, %d %h %Y %H:%M:%S %Z\r\n", gmtime(&tl)); 13338589Sabial write(con_sock, buff, strlen(buff)); 13446493Sabial //return(buff); 13538589Sabial} 13638589Sabial 13746493Sabial/* 13846493Sabial * Send data to the open socket 13946493Sabial */ 14094134Sasmodaivoid 14146493Sabialhttp_output(char *html) 14246493Sabial{ 14346493Sabial write(con_sock, html, strlen(html)); 14446493Sabial write(con_sock, "\r\n", 2); 14546493Sabial} 14638589Sabial 14738589Sabial 14846493Sabial/* 14946493Sabial * Create and write the log information to file 15046493Sabial * Log file format is one line per entry 15146493Sabial */ 15294134Sasmodaivoid 15346493Sabiallog_line(char *req) 15438589Sabial{ 15546493Sabial char log_buff[256]; 15646493Sabial char msg[1024]; 15746493Sabial char env_host[80], env_addr[80]; 15846493Sabial long addr; 15946493Sabial FILE *log; 16038589Sabial 16146493Sabial strcpy(log_buff,inet_ntoa(source.sin_addr)); 16246493Sabial sprintf(env_addr, "REMOTE_ADDR=%s",log_buff); 16346493Sabial 16446493Sabial addr=inet_addr(log_buff); 16546493Sabial 16646493Sabial strcpy(msg,adate()); 16746493Sabial strcat(msg," "); 16846493Sabial hst=gethostbyaddr((char*) &addr, 4, AF_INET); 16946493Sabial 17046493Sabial /* If DNS hostname exists */ 17146493Sabial if (hst) { 17246493Sabial strcat(msg,hst->h_name); 17346493Sabial sprintf(env_host, "REMOTE_HOST=%s",hst->h_name); 17438589Sabial } 17546493Sabial strcat(msg," ("); 17646493Sabial strcat(msg,log_buff); 17746493Sabial strcat(msg,") "); 17846493Sabial strcat(msg,req); 17946493Sabial 18046493Sabial if (daemonize) { 18146493Sabial log=fopen(logfile,"a"); 18246493Sabial fprintf(log,"%s\n",msg); 18346493Sabial fclose(log); 18446493Sabial } else 18546493Sabial printf("%s\n",msg); 18646493Sabial 18746493Sabial /* This is for CGI scripts */ 18846493Sabial putenv(env_addr); 18946493Sabial putenv(env_host); 19038589Sabial} 19138589Sabial 19246493Sabial/* 19346493Sabial * We have a connection. Identify what type of request GET, HEAD, CGI, etc 19446493Sabial * and do what needs to be done 19546493Sabial */ 19694134Sasmodaivoid 19794135Sasmodaihttp_request(void) 19838589Sabial{ 19994132Sasmodai int fd, lg, i; 20046493Sabial int cmd = 0; 20146493Sabial char *p, *par; 20238589Sabial char *filename, *c; 20346493Sabial struct stat file_status; 20438589Sabial char req[1024]; 20546493Sabial char buff[8192]; 20638589Sabial 20738589Sabial lg = read(con_sock, req, 1024); 20838589Sabial 20938589Sabial if (p=strstr(req,"\n")) *p=0; 21038589Sabial if (p=strstr(req,"\r")) *p=0; 21138589Sabial 21246493Sabial log_line(req); 21338589Sabial 21446493Sabial c = strtok(req, " "); 21538589Sabial 21646493Sabial /* Error msg if request is nothing */ 21746493Sabial if (c == NULL) { 21846493Sabial http_output(http_404[0]); 21946493Sabial http_output(http_404[1]); 22046493Sabial goto end_request; 22146493Sabial } 22238589Sabial 22346493Sabial if (strncmp(c, "GET", 3) == 0) cmd = 1; 22446493Sabial if (strncmp(c, "HEAD", 4) == 0) cmd = 2; 22538589Sabial 22646493Sabial /* Do error msg for any other type of request */ 22746493Sabial if (cmd == 0) { 22846493Sabial http_output(http_405[0]); 22946493Sabial http_output(http_405[1]); 23046493Sabial goto end_request; 23138589Sabial } 23238589Sabial 23338589Sabial filename = strtok(NULL, " "); 23438589Sabial 23538589Sabial c = strtok(NULL, " "); 23646493Sabial if (fetch_mode[0] != NULL) strcpy(filename,fetch_mode); 23738589Sabial if (filename == NULL || 23838589Sabial strlen(filename)==1) filename="/index.html"; 23938589Sabial 24046493Sabial while (filename[0]== '/') filename++; 24146493Sabial 24246493Sabial /* CGI handling. Untested */ 24338589Sabial if (!strncmp(filename,"cgi-bin/",8)) 24438589Sabial { 24538589Sabial par=0; 24638589Sabial if (par=strstr(filename,"?")) 24738589Sabial { 24838589Sabial *par=0; 24938589Sabial par++; 25038589Sabial } 25138589Sabial if (access(filename,X_OK)) goto conti; 25246493Sabial stat (filename,&file_status); 25346493Sabial if (setuid(file_status.st_uid)) return(0); 25446493Sabial if (seteuid(file_status.st_uid)) return(0); 25538589Sabial if (!fork()) 25638589Sabial { 25738589Sabial close(1); 25838589Sabial dup(con_sock); 25946493Sabial //printf("HTTP/1.0 200 OK\nContent-type: text/html\n\n\n"); 26046493Sabial printf("HTTP/1.0 200 OK\r\n"); 26146493Sabial /* Plug in environment variable, others in log_line */ 26246493Sabial putenv("SERVER_SOFTWARE=FreeBSD/PicoBSD"); 26346493Sabial 26479452Sbrian execlp (filename,filename,par,(char *)0); 26538589Sabial } 26638589Sabial wait(&i); 26738589Sabial return(0); 26838589Sabial } 26938589Sabial conti: 27038589Sabial if (filename == NULL) { 27146493Sabial http_output(http_405[0]); 27246493Sabial http_output(http_405[1]); 27346493Sabial goto end_request; 27438589Sabial } 27546493Sabial /* End of CGI handling */ 27646493Sabial 27746493Sabial /* Reject any request with '..' in it, bad hacker */ 27838589Sabial c = filename; 27938589Sabial while (*c != '\0') 28046493Sabial if (c[0] == '.' && c[1] == '.') { 28146493Sabial http_output(http_404[0]); 28246493Sabial http_output(http_404[1]); 28346493Sabial goto end_request; 28446493Sabial } else 28546493Sabial c++; 28646493Sabial 28746493Sabial /* Open filename */ 28838589Sabial fd = open(filename, O_RDONLY); 28938589Sabial if (fd < 0) { 29046493Sabial http_output(http_404[0]); 29146493Sabial http_output(http_404[1]); 29246493Sabial goto end_request; 29338589Sabial } 29446493Sabial 29546493Sabial /* Get file status information */ 29646493Sabial if (fstat(fd, &file_status) < 0) { 29746493Sabial http_output(http_404[0]); 29846493Sabial http_output(http_404[1]); 29946493Sabial goto end_request; 30038589Sabial } 30146493Sabial 30246493Sabial /* Is it a regular file? */ 30346493Sabial if (!S_ISREG(file_status.st_mode)) { 30446493Sabial http_output(http_404[0]); 30546493Sabial http_output(http_404[1]); 30646493Sabial goto end_request; 30746493Sabial } 30838589Sabial 30946493Sabial /* Past this point we are serving either a GET or HEAD */ 31046493Sabial /* Print all the header info */ 31146493Sabial http_output(http_200); 31246493Sabial http_output(httpd_server_ident); 31346493Sabial http_date(); 31438589Sabial 31594133Sasmodai sprintf(buff, "Content-length: %lld\r\n", file_status.st_size); 31638589Sabial 31746493Sabial if (strstr(filename,".txt")) { 31846493Sabial strcpy(buff,"Content-type: text/plain\r\n"); 31946493Sabial } else if (strstr(filename,".html") || strstr(filename,".htm")) { 32046493Sabial strcpy(buff,"Content-type: text/html\r\n"); 32146493Sabial } else if (strstr(filename,".gif")) { 32246493Sabial strcpy(buff,"Content-type: image/gif\r\n"); 32346493Sabial } else if (strstr(filename,".jpg")) { 32446493Sabial strcpy(buff,"Content-type: image/jpeg\r\n"); 32546493Sabial } else { 32646493Sabial /* Take a guess at content if we don't have something already */ 32746493Sabial strcpy(buff,"Content-type: "); 32846493Sabial strcat(buff,strstr(filename,".")+1); 32946493Sabial strcat(buff,"\r\n"); 33046493Sabial } 33146493Sabial write(con_sock, buff, strlen(buff)); 33246493Sabial 33346493Sabial strftime(buff, 50, "Last-Modified: %a, %d %h %Y %H:%M:%S %Z\r\n\r\n", gmtime(&file_status.st_mtime)); 33446493Sabial write(con_sock, buff, strlen(buff)); 33538589Sabial 33646493Sabial /* Send data only if GET request */ 33738589Sabial if (cmd == 1) { 33846493Sabial while (lg = read(fd, buff, 8192)) 33946493Sabial write(con_sock, buff, lg); 34038589Sabial } 34138589Sabial 34246493Sabialend_request: 34338589Sabial close(fd); 34438589Sabial close(con_sock); 34538589Sabial 34638589Sabial} 34738589Sabial 34846493Sabial/* 34946493Sabial * Simple httpd server for use in PicoBSD or other embedded application. 35046493Sabial * Should satisfy simple httpd needs. For more demanding situations 35146493Sabial * apache is probably a better (but much larger) choice. 35246493Sabial */ 35394134Sasmodaiint 35446493Sabialmain(int argc, char *argv[]) 35538589Sabial{ 35646493Sabial extern char *optarg; 35746493Sabial extern int optind; 35894132Sasmodai int ch, ld; 35946493Sabial int httpd_group = 65534; 36046493Sabial pid_t server_pid; 36146493Sabial 36246493Sabial /* Default for html directory */ 36338589Sabial strcpy (homedir,getenv("HOME")); 36438589Sabial if (!geteuid()) strcpy (homedir,"/httphome"); 36538589Sabial else strcat (homedir,"/httphome"); 36638589Sabial 36746493Sabial /* Defaults for log file */ 36846493Sabial if (geteuid()) { 36946493Sabial strcpy(logfile,getenv("HOME")); 37046493Sabial strcat(logfile,"/"); 37146493Sabial strcat(logfile,"jhttp.log"); 37246493Sabial } else 37346493Sabial strcpy(logfile,"/var/log/jhttpd.log"); 37438589Sabial 37546493Sabial /* Parse command line arguments */ 37646493Sabial while ((ch = getopt(argc, argv, "d:f:g:l:p:vDh")) != -1) 37746493Sabial switch (ch) { 37846493Sabial case 'd': 37946493Sabial strcpy(homedir,optarg); 38046493Sabial break; 38146493Sabial case 'f': 38246493Sabial daemonize = 0; 38346493Sabial verbose = 1; 38446493Sabial strcpy(fetch_mode,optarg); 38546493Sabial break; 38646493Sabial case 'g': 38746493Sabial httpd_group = atoi(optarg); 38846493Sabial break; 38946493Sabial case 'l': 39046493Sabial strcpy(logfile,optarg); 39146493Sabial break; 39246493Sabial case 'p': 39346493Sabial http_port = atoi(optarg); 39446493Sabial break; 39546493Sabial case 'v': 39646493Sabial verbose = 1; 39746493Sabial break; 39846493Sabial case 'D': 39946493Sabial daemonize = 0; 40046493Sabial break; 40146493Sabial case '?': 40246493Sabial case 'h': 40346493Sabial default: 40446493Sabial printf("usage: simple_httpd [[-d directory][-g grpid][-l logfile][-p port][-vD]]\n"); 40546493Sabial exit(1); 40646493Sabial /* NOTREACHED */ 40746493Sabial } 40846493Sabial 40946493Sabial /* Not running as root and no port supplied, assume 1080 */ 41046493Sabial if ((http_port == 80) && geteuid()) { 41146493Sabial http_port = 1080; 41246493Sabial } 41346493Sabial 41446493Sabial /* Do we really have rights in the html directory? */ 41546493Sabial if (fetch_mode[0] == NULL) { 41646493Sabial if (chdir(homedir)) { 41746493Sabial perror("chdir"); 41846493Sabial puts(homedir); 41946493Sabial exit(1); 42046493Sabial } 42146493Sabial } 42246493Sabial 42346493Sabial /* Create log file if it doesn't exit */ 42446493Sabial if ((access(logfile,W_OK)) && daemonize) { 42546493Sabial ld = open (logfile,O_WRONLY); 42646493Sabial chmod (logfile,00600); 42746493Sabial close(ld); 42846493Sabial } 42946493Sabial 43038589Sabial init_servconnection(); 43138589Sabial 43246493Sabial if (verbose) { 43346493Sabial printf("Server started with options \n"); 43446493Sabial printf("port: %d\n",http_port); 43546493Sabial if (fetch_mode[0] == NULL) printf("html home: %s\n",homedir); 43646493Sabial if (daemonize) printf("logfile: %s\n",logfile); 43746493Sabial } 43838589Sabial 43946493Sabial /* httpd is spawned */ 44046493Sabial if (daemonize) { 44146493Sabial if (server_pid = fork()) { 44246493Sabial wait3(0,WNOHANG,0); 44346493Sabial if (verbose) printf("pid: %d\n",server_pid); 44446493Sabial exit(0); 44546493Sabial } 44646493Sabial wait3(0,WNOHANG,0); 44746493Sabial } 44838589Sabial 44946493Sabial if (fetch_mode[0] == NULL) setpgrp(0,httpd_group); 45046493Sabial 45146493Sabial /* How many connections do you want? 45246493Sabial * Keep this lower than the available number of processes 45346493Sabial */ 45446493Sabial if (listen(http_sock,15) < 0) exit(1); 45546493Sabial 45638589Sabial label: 45746493Sabial wait_connection(); 45846493Sabial 45946493Sabial if (fork()) { 46046493Sabial wait3(0,WNOHANG,0); 46146493Sabial close(con_sock); 46246493Sabial goto label; 46346493Sabial } 46446493Sabial 46546493Sabial http_request(); 46646493Sabial 46746493Sabial wait3(0,WNOHANG,0); 46846493Sabial exit(0); 46938589Sabial} 47038589Sabial 47138589Sabial 47238589Sabialchar *adate() 47338589Sabial{ 47438589Sabial static char out[50]; 47538589Sabial long now; 47638589Sabial struct tm *t; 47738589Sabial time(&now); 47838589Sabial t = localtime(&now); 47943939Sabial sprintf(out, "%02d:%02d:%02d %02d/%02d/%02d", 48043939Sabial t->tm_hour, t->tm_min, t->tm_sec, 48143939Sabial t->tm_mday, t->tm_mon+1, t->tm_year ); 48238589Sabial return out; 48338589Sabial} 484