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: stable/11/release/picobsd/tinyware/simple_httpd/simple_httpd.c 351466 2019-08-25 00:57:51Z eugen $
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
51351466Seugenstatic int	http_port = 80;
52351466Seugenstatic int	daemonize = 1;
53351466Seugenstatic int	verbose = 0;
54351466Seugenstatic int	http_sock, con_sock;
5546493Sabial
56351466Seugenstatic const char *fetch_mode = NULL;
57351466Seugenstatic char	homedir[100];
58351466Seugenstatic char	logfile[80];
59351466Seugenstatic char	*adate(void);
60351466Seugenstatic void	init_servconnection(void);
61351466Seugenstatic void	http_date(void);
62351466Seugenstatic void	http_output(const char *html);
63351466Seugenstatic void	http_request(void);
64351466Seugenstatic void	log_line(char *req);
65351466Seugenstatic void	wait_connection(void);
6646493Sabial
67351466Seugenstatic struct hostent *hst;
68351466Seugenstatic struct 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
75351466Seugenstatic const char *default_mime_type = "application/octet-stream";
76133836Sdwmalone
77351466Seugenstatic const 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
86351466Seugenstatic const 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 */
104351466Seugenstatic void
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 */
128351466Seugenstatic void
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 */
145351466Seugenstatic void
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 */
160351466Seugenstatic void
161113126Sdwmalonehttp_output(const char *html)
16246493Sabial{
163271787Spfg	write(con_sock, html, strlen(html));
164271787Spfg	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 */
172351466Seugenstatic void
17346493Sabiallog_line(char *req)
17438589Sabial{
175271787Spfg	char	log_buff[256];
176271787Spfg	char	msg[1024];
177271787Spfg	char	env_host[80], env_addr[80];
178271787Spfg	long	addr;
179271787Spfg	FILE	*log;
18038589Sabial
18146493Sabial	strcpy(log_buff,inet_ntoa(source.sin_addr));
18246493Sabial	sprintf(env_addr, "REMOTE_ADDR=%s",log_buff);
18346493Sabial
184271787Spfg	addr=inet_addr(log_buff);
18546493Sabial
186271787Spfg	strcpy(msg,adate());
187271787Spfg	strcat(msg,"    ");
188271787Spfg	hst=gethostbyaddr((char*) &addr, 4, AF_INET);
189271787Spfg
19046493Sabial	/* If DNS hostname exists */
191271787Spfg	if (hst) {
19246493Sabial	  strcat(msg,hst->h_name);
19346493Sabial	  sprintf(env_host, "REMOTE_HOST=%s",hst->h_name);
19438589Sabial	}
195271787Spfg	strcat(msg," (");
196271787Spfg	strcat(msg,log_buff);
197271787Spfg	strcat(msg,")   ");
198271787Spfg	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 */
216351466Seugenstatic void
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
229271787Spfg	if ((p=strstr(req,"\n"))) *p=0;
230271787Spfg	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, " ");
256271787Spfg	if (fetch_mode != NULL) filename=fetch_mode;
257271787Spfg	if (filename == NULL ||
258271787Spfg	    strlen(filename)==1) filename="/index.html";
25938589Sabial
260271787Spfg	while (filename[0]== '/') filename++;
26146493Sabial
262271787Spfg	/* CGI handling.  Untested */
263271787Spfg	if (!strncmp(filename,"cgi-bin/",8))
264271787Spfg	   {
265271787Spfg	   par=0;
266271787Spfg	   if ((par=strstr(filename,"?")))
267271787Spfg	      {
268271787Spfg	       *par=0;
269271787Spfg	        par++;
270271787Spfg	      }
271271787Spfg	   if (access(filename,X_OK)) goto conti;
272271787Spfg	   stat (filename,&file_status);
273271787Spfg	   if (setuid(file_status.st_uid)) return;
274271787Spfg	   if (seteuid(file_status.st_uid)) return;
275271787Spfg	   if (!fork())
276271787Spfg	      {
277271787Spfg	       close(1);
278271787Spfg	       dup(con_sock);
279271787Spfg	       /*printf("HTTP/1.0 200 OK\nContent-type: text/html\n\n\n");*/
28046493Sabial	       printf("HTTP/1.0 200 OK\r\n");
281271787Spfg	       /* Plug in environment variable, others in log_line */
282189936Sdwmalone	       setenv("SERVER_SOFTWARE", "FreeBSD/PicoBSD", 1);
28346493Sabial
28479452Sbrian	       execlp (filename,filename,par,(char *)0);
285271787Spfg	      }
286271787Spfg	    wait(&i);
287271787Spfg	    return;
288271787Spfg	    }
289271787Spfg	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) {
310271787Spfg		http_output(http_404[0]);
311271787Spfg		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	}
328271787Spfg
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{
374271787Spfg	int ch, ld;
375271787Spfg	pid_t httpd_group = 65534;
376271787Spfg	pid_t server_pid;
377271787Spfg
37846493Sabial	/* Default for html directory */
37938589Sabial	strcpy (homedir,getenv("HOME"));
380271787Spfg	if (!geteuid())	strcpy (homedir,"/httphome");
381271787Spfg	   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 */
423271787Spfg	  }
42446493Sabial
42546493Sabial	/* Not running as root and no port supplied, assume 1080 */
426271787Spfg	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 */
440271787Spfg	if ((access(logfile,W_OK)) && daemonize) {
441271787Spfg	  ld = open (logfile,O_WRONLY);
44246493Sabial	  chmod (logfile,00600);
44346493Sabial	  close(ld);
44446493Sabial	}
44546493Sabial
446271787Spfg	init_servconnection();
44738589Sabial
448271787Spfg	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 */
456271787Spfg	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
465271787Spfg	if (fetch_mode == NULL)
466271787Spfg		setpgrp((pid_t)0, httpd_group);
46746493Sabial
46846493Sabial	/* How many connections do you want?
46946493Sabial	 * Keep this lower than the available number of processes
47046493Sabial	 */
471271787Spfg	if (listen(http_sock,15) < 0) exit(1);
47246493Sabial
473271787Spfg	label:
47446493Sabial	wait_connection();
475271787Spfg
47646493Sabial	if (fork()) {
47746493Sabial	  wait3(0,WNOHANG,0);
47846493Sabial	  close(con_sock);
47946493Sabial	  goto label;
48046493Sabial	}
48146493Sabial
48246493Sabial	http_request();
48346493Sabial
48446493Sabial	wait3(0,WNOHANG,0);
48546493Sabial	exit(0);
48638589Sabial}
48738589Sabial
48838589Sabial
489113126Sdwmalonechar *
490113126Sdwmaloneadate(void)
49138589Sabial{
492271787Spfg	static char out[50];
493271787Spfg	time_t now;
494271787Spfg	struct tm *t;
495271787Spfg	time(&now);
496271787Spfg	t = localtime(&now);
497271787Spfg	sprintf(out, "%02d:%02d:%02d %02d/%02d/%02d",
498271787Spfg		     t->tm_hour, t->tm_min, t->tm_sec,
499271787Spfg		     t->tm_mday, t->tm_mon+1, t->tm_year );
500271787Spfg	return out;
50138589Sabial}
502