2 * Simple_HTTPd v1.1 - a very small, barebones HTTP server
4 * Copyright (c) 1998-1999 Marc Nicholas <marc@netstor.com>
7 * Major rewrite by William Lloyd <wlloyd@slap.net>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * $FreeBSD: src/release/picobsd/tinyware/simple_httpd/simple_httpd.c,v 1.6 1999/08/28 01:33:59 peter Exp $
31 * $DragonFly: src/release/picobsd/tinyware/simple_httpd/Attic/simple_httpd.c,v 1.2 2003/06/17 04:27:20 dillon Exp $
36 #include <sys/types.h>
37 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
54 int http_sock, con_sock;
62 struct sockaddr_in source;
65 static char httpd_server_ident[] = "Server: FreeBSD/PicoBSD simple_httpd 1.1\r";
67 static char http_200[] = "HTTP/1.0 200 OK\r";
69 /* Two parts, HTTP Header and then HTML */
70 static char *http_404[2] =
71 {"HTTP/1.0 404 Not found\r\n",
72 "<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 404</H1>\
73 Not found - file doesn't exist or you do not have permission.\n</BODY></HTML>\r\n"
76 static char *http_405[2] =
77 {"HTTP/1.0 405 Method Not allowed\r\nAllow: GET,HEAD\r\n",
78 "<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 405</H1>\
79 This server only supports GET and HEAD requests.\n</BODY></HTML>\r\n"
83 * Only called on initial invocation
86 init_servconnection(void)
88 struct sockaddr_in server;
91 http_sock = socket(AF_INET, SOCK_STREAM, 0);
96 server.sin_family = AF_INET;
97 server.sin_port = htons(http_port);
98 server.sin_addr.s_addr = INADDR_ANY;
99 if (bind(http_sock, (struct sockaddr *) & server, sizeof(server)) < 0) {
100 perror("bind socket");
103 if (verbose) printf("simple_httpd\n",http_port);
107 * Wait here until we see an incoming http request
109 wait_connection(void)
113 lg = sizeof(struct sockaddr_in);
115 con_sock = accept(http_sock, (struct sockaddr *) & source, &lg);
123 * Print timestamp for HTTP HEAD and GET
131 strftime(buff, 50, "Date: %a, %d %h %Y %H:%M:%S %Z\r\n", gmtime(&tl));
132 write(con_sock, buff, strlen(buff));
137 * Send data to the open socket
139 http_output(char *html)
141 write(con_sock, html, strlen(html));
142 write(con_sock, "\r\n", 2);
147 * Create and write the log information to file
148 * Log file format is one line per entry
154 char env_host[80], env_addr[80];
158 strcpy(log_buff,inet_ntoa(source.sin_addr));
159 sprintf(env_addr, "REMOTE_ADDR=%s",log_buff);
161 addr=inet_addr(log_buff);
165 hst=gethostbyaddr((char*) &addr, 4, AF_INET);
167 /* If DNS hostname exists */
169 strcat(msg,hst->h_name);
170 sprintf(env_host, "REMOTE_HOST=%s",hst->h_name);
173 strcat(msg,log_buff);
178 log=fopen(logfile,"a");
179 fprintf(log,"%s\n",msg);
184 /* This is for CGI scripts */
190 * We have a connection. Identify what type of request GET, HEAD, CGI, etc
191 * and do what needs to be done
200 struct stat file_status;
205 lg = read(con_sock, req, 1024);
207 if (p=strstr(req,"\n")) *p=0;
208 if (p=strstr(req,"\r")) *p=0;
212 c = strtok(req, " ");
214 /* Error msg if request is nothing */
216 http_output(http_404[0]);
217 http_output(http_404[1]);
221 if (strncmp(c, "GET", 3) == 0) cmd = 1;
222 if (strncmp(c, "HEAD", 4) == 0) cmd = 2;
224 /* Do error msg for any other type of request */
226 http_output(http_405[0]);
227 http_output(http_405[1]);
231 filename = strtok(NULL, " ");
233 c = strtok(NULL, " ");
234 if (fetch_mode[0] != NULL) strcpy(filename,fetch_mode);
235 if (filename == NULL ||
236 strlen(filename)==1) filename="/index.html";
238 while (filename[0]== '/') filename++;
240 /* CGI handling. Untested */
241 if (!strncmp(filename,"cgi-bin/",8))
244 if (par=strstr(filename,"?"))
249 if (access(filename,X_OK)) goto conti;
250 stat (filename,&file_status);
251 if (setuid(file_status.st_uid)) return(0);
252 if (seteuid(file_status.st_uid)) return(0);
257 //printf("HTTP/1.0 200 OK\nContent-type: text/html\n\n\n");
258 printf("HTTP/1.0 200 OK\r\n");
259 /* Plug in environment variable, others in log_line */
260 putenv("SERVER_SOFTWARE=FreeBSD/PicoBSD");
262 execlp (filename,filename,par,0);
268 if (filename == NULL) {
269 http_output(http_405[0]);
270 http_output(http_405[1]);
273 /* End of CGI handling */
275 /* Reject any request with '..' in it, bad hacker */
278 if (c[0] == '.' && c[1] == '.') {
279 http_output(http_404[0]);
280 http_output(http_404[1]);
286 fd = open(filename, O_RDONLY);
288 http_output(http_404[0]);
289 http_output(http_404[1]);
293 /* Get file status information */
294 if (fstat(fd, &file_status) < 0) {
295 http_output(http_404[0]);
296 http_output(http_404[1]);
300 /* Is it a regular file? */
301 if (!S_ISREG(file_status.st_mode)) {
302 http_output(http_404[0]);
303 http_output(http_404[1]);
307 /* Past this point we are serving either a GET or HEAD */
308 /* Print all the header info */
309 http_output(http_200);
310 http_output(httpd_server_ident);
313 sprintf(buff, "Content-length: %d\r\n", file_status.st_size);
315 if (strstr(filename,".txt")) {
316 strcpy(buff,"Content-type: text/plain\r\n");
317 } else if (strstr(filename,".html") || strstr(filename,".htm")) {
318 strcpy(buff,"Content-type: text/html\r\n");
319 } else if (strstr(filename,".gif")) {
320 strcpy(buff,"Content-type: image/gif\r\n");
321 } else if (strstr(filename,".jpg")) {
322 strcpy(buff,"Content-type: image/jpeg\r\n");
324 /* Take a guess at content if we don't have something already */
325 strcpy(buff,"Content-type: ");
326 strcat(buff,strstr(filename,".")+1);
329 write(con_sock, buff, strlen(buff));
331 strftime(buff, 50, "Last-Modified: %a, %d %h %Y %H:%M:%S %Z\r\n\r\n", gmtime(&file_status.st_mtime));
332 write(con_sock, buff, strlen(buff));
334 /* Send data only if GET request */
336 while (lg = read(fd, buff, 8192))
337 write(con_sock, buff, lg);
347 * Simple httpd server for use in PicoBSD or other embedded application.
348 * Should satisfy simple httpd needs. For more demanding situations
349 * apache is probably a better (but much larger) choice.
351 main(int argc, char *argv[])
355 int bflag, ch, fd, ld;
357 int httpd_group = 65534;
360 /* Default for html directory */
361 strcpy (homedir,getenv("HOME"));
362 if (!geteuid()) strcpy (homedir,"/httphome");
363 else strcat (homedir,"/httphome");
365 /* Defaults for log file */
367 strcpy(logfile,getenv("HOME"));
369 strcat(logfile,"jhttp.log");
371 strcpy(logfile,"/var/log/jhttpd.log");
373 /* Parse command line arguments */
374 while ((ch = getopt(argc, argv, "d:f:g:l:p:vDh")) != -1)
377 strcpy(homedir,optarg);
382 strcpy(fetch_mode,optarg);
385 httpd_group = atoi(optarg);
388 strcpy(logfile,optarg);
391 http_port = atoi(optarg);
402 printf("usage: simple_httpd [[-d directory][-g grpid][-l logfile][-p port][-vD]]\n");
407 /* Not running as root and no port supplied, assume 1080 */
408 if ((http_port == 80) && geteuid()) {
412 /* Do we really have rights in the html directory? */
413 if (fetch_mode[0] == NULL) {
414 if (chdir(homedir)) {
421 /* Create log file if it doesn't exit */
422 if ((access(logfile,W_OK)) && daemonize) {
423 ld = open (logfile,O_WRONLY);
424 chmod (logfile,00600);
428 init_servconnection();
431 printf("Server started with options \n");
432 printf("port: %d\n",http_port);
433 if (fetch_mode[0] == NULL) printf("html home: %s\n",homedir);
434 if (daemonize) printf("logfile: %s\n",logfile);
437 /* httpd is spawned */
439 if (server_pid = fork()) {
441 if (verbose) printf("pid: %d\n",server_pid);
447 if (fetch_mode[0] == NULL) setpgrp(0,httpd_group);
449 /* How many connections do you want?
450 * Keep this lower than the available number of processes
452 if (listen(http_sock,15) < 0) exit(1);
477 sprintf(out, "%02d:%02d:%02d %02d/%02d/%02d",
478 t->tm_hour, t->tm_min, t->tm_sec,
479 t->tm_mday, t->tm_mon+1, t->tm_year );