simple_httpd.c revision 43939
1/*-
2 * SimpleHTTPd v1.0 - a very small, barebones HTTP server
3 *
4 * Copyright (c) 1998-1999 Marc Nicholas <marc@netstor.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 *	$Id: simple_httpd.c,v 1.2.2.1 1999/02/05 12:21:41 abial Exp $
29 */
30
31#include <stdio.h>
32#include <unistd.h>
33#include <stdlib.h>
34#include <sys/stat.h>
35#include <sys/time.h>
36#include <sys/types.h>
37#include <time.h>
38#include <sys/socket.h>
39#include <netinet/in.h>
40#include <arpa/inet.h>
41#include <netdb.h>
42#include <fcntl.h>
43#include <string.h>
44#include <signal.h>
45#include <sys/wait.h>
46
47int             http_sock, con_sock;
48int             http_port = 80;
49struct sockaddr_in source;
50char           homedir[100];
51char           *adate();
52struct hostent *hst;
53
54void
55init_servconnection(void)
56{
57	struct sockaddr_in server;
58
59	/* Create a socket */
60	http_sock = socket(AF_INET, SOCK_STREAM, 0);
61	if (http_sock < 0) {
62		perror("socket");
63		exit(1);
64	}
65	server.sin_family = AF_INET;
66	server.sin_port = htons(http_port);
67	server.sin_addr.s_addr = INADDR_ANY;
68	if (bind(http_sock, (struct sockaddr *) & server, sizeof(server)) < 0) {
69		perror("bind socket");
70		exit(1);
71	}
72        printf("simpleHTTPd running on %d port\n",http_port);
73}
74
75attenteconnection(void)
76{
77	int lg;
78
79	lg = sizeof(struct sockaddr_in);
80
81	con_sock = accept(http_sock, (struct sockaddr *) & source, &lg);
82	if (con_sock <= 0) {
83		perror("accept");
84		exit(1);
85	}
86}
87
88outdate()
89{
90	time_t	tl;
91	char	buff[50];
92
93	tl = time(NULL);
94	strftime(buff, 50, "Date: %a, %d %h %Y %H:%M:%S %Z\r\n", gmtime(&tl));
95	write(con_sock, buff, strlen(buff));
96}
97
98char           *rep_err_nget[2] = {"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 405</H1>\
99This server is supports only GET and HEAD  requests\n</BODY></HTML>\r\n",
100"HTTP/1.0 405 Method Not allowed\r\nAllow: GET,HEAD\r\nServer: jhttpd\r\n"};
101
102char           *rep_err_acc[2] = {"<HTML><HEAD><TITLE>Error</TITLE></HEAD><BODY><H1>Error 404</H1>\
103Not found - file doesn't exist or is read protected\n</BODY></HTML>\r\n",
104"HTTP/1.0 404 Not found\r\nServer: jhttpd\r\n"};
105
106outerror(char **rep, int http1) /* ������ ������ ������� � html- ���� */
107{
108
109	if (http1) {
110		write(con_sock, rep[1], strlen(rep[1]));
111		outdate();
112		write(con_sock, "\r\n", 2);
113	}
114	write(con_sock, rep[0], strlen(rep[0]));
115}
116
117char            rep_head[] = "HTTP/1.0 200 OK\r\nServer: simpleHTTPD\r\n";
118
119traite_req()
120{
121	char            buff[8192];
122	int             fd, lg, cmd, http1, i;
123	char           *filename, *c;
124	struct stat     statres;
125	char            req[1024];
126        char            logfile[80];
127        char            msg[1024];
128        char           *p,
129                       *par;
130        long            addr;
131        FILE           *log;
132
133	lg = read(con_sock, req, 1024);
134
135        if (p=strstr(req,"\n")) *p=0;
136        if (p=strstr(req,"\r")) *p=0;
137
138       if (geteuid())
139          {
140          strcpy(logfile,getenv("HOME"));
141          strcat(logfile,"/");
142          strcat(logfile,"jhttp.log");
143          }
144       else strcpy(logfile,"/var/log/jhttpd.log");
145
146       if ( access(logfile,W_OK))
147            {
148            lg=creat (logfile,O_WRONLY);
149            chmod (logfile,00600);
150            close(lg);
151            }
152
153        strcpy(buff,inet_ntoa(source.sin_addr));
154
155        addr=inet_addr(buff);
156
157        strcpy(msg,adate());
158        strcat(msg,"    ");
159        hst=gethostbyaddr((char*) &addr, 4, AF_INET);
160        if (hst) strcat(msg,hst->h_name);
161        strcat(msg," (");
162        strcat(msg,buff);
163        strcat(msg,")   ");
164        strcat(msg,req);
165
166        log=fopen(logfile,"a");
167        fprintf(log,"%s\n",msg);
168        fclose(log);
169
170	c = strtok(req, " ");
171	if (c == NULL) {
172		outerror(rep_err_nget, 0);
173		goto error;
174	}
175	cmd = 0;
176	if (strncmp(c, "GET", 3) == 0)
177		cmd = 1;
178	if (strncmp(c, "HEAD", 4) == 0) {
179		cmd = 2;
180	}
181
182	filename = strtok(NULL, " ");
183
184	http1 = 0;
185	c = strtok(NULL, " ");
186	if (c != NULL && strncmp(c, "HTTP", 4) == 0)
187		http1 = 1;
188
189	if (cmd == 0) {
190		outerror(rep_err_nget, http1);
191		goto error;
192	}
193
194	if (filename == NULL ||
195            strlen(filename)==1) filename="/index.html";
196
197         while (filename[0]== '/') filename++;
198
199        /**/
200        if (!strncmp(filename,"cgi-bin/",8))
201           {
202           par=0;
203           if (par=strstr(filename,"?"))
204              {
205               *par=0;
206                par++;
207              }
208           if (access(filename,X_OK)) goto conti;
209           stat (filename,&statres);
210           if (setuid(statres.st_uid)) return(0);
211           if (seteuid(statres.st_uid)) return(0);
212           if (!fork())
213              {
214               close(1);
215               dup(con_sock);
216               printf("HTTP/1.0 200 OK\nContent-type: text/html\n\n\n");
217               execlp (filename,filename,par,0);
218              }
219            wait(&i);
220            return(0);
221            }
222        conti:
223	if (filename == NULL) {
224		outerror(rep_err_acc, http1);
225		goto error;
226	}
227	/* interdit les .. dans le path */
228	c = filename;
229	while (*c != '\0')
230		if (c[0] == '.' && c[1] == '.') {
231			outerror(rep_err_acc, http1);
232			goto error;
233		} else
234			c++;
235
236	fd = open(filename, O_RDONLY);
237	if (fd < 0) {
238		outerror(rep_err_acc, http1);
239		goto error;
240	}
241	if (fstat(fd, &statres) < 0) {
242		outerror(rep_err_acc, http1);
243		goto error;
244	}
245	if (!S_ISREG(statres.st_mode))
246	    {
247	    outerror(rep_err_acc, http1);
248            goto error;
249	    }
250	if (http1) {
251		char            buff[50];
252		time_t          tl;
253
254		write(con_sock, rep_head, strlen(rep_head));
255		sprintf(buff, "Content-length: %d\r\n", statres.st_size);
256		write(con_sock, buff, strlen(buff));
257		outdate();
258
259                if (strstr(filename,"."))
260                   {
261                   strcpy(buff,"Content-type: ");
262                   strcat(buff,strstr(filename,".")+1);
263                   strcat(buff,"\r\n");
264                   write(con_sock,buff,strlen(buff));
265                   }
266
267                if (strstr(filename,".txt"))
268                   {
269                   strcpy(buff,"Content-type: text/plain\r\n");
270                   write(con_sock, buff, strlen(buff));
271                   }
272
273                if (strstr(filename,".html") ||
274                    strstr(filename,".htm"))
275                   {
276                   strcpy(buff,"Content-type: text/html\r\n");
277                   write(con_sock, buff, strlen(buff));
278                   }
279
280                if (strstr(filename,".gif"))
281                   {
282                   strcpy(buff,"Content-type: image/gif\r\n");
283                   write(con_sock, buff, strlen(buff));
284                   }
285
286                if (strstr(filename,".jpg"))
287                   {
288                   strcpy(buff,"Content-type: image/jpeg\r\n");
289                   write(con_sock, buff, strlen(buff));
290                   }
291
292		strftime(buff, 50, "Last-Modified: %a, %d %h %Y %H:%M:%S %Z\r\n\r\n", gmtime(&statres.st_mtime));
293		write(con_sock, buff, strlen(buff));
294	}
295	if (cmd == 1) {
296		while (lg = read(fd, buff, 8192))
297			write(con_sock, buff, lg);
298	}
299
300error:
301	close(fd);
302	close(con_sock);
303
304}
305
306
307main(int argc, char **argv)
308{
309	int             lg;
310        char            hello[100];
311
312        if (argc<2 && geteuid())
313           {
314           printf("Usage: simple_htppd <port>\n");
315           exit(1);
316           }
317
318	if (argc>=2) http_port = atoi(argv[1]);
319
320	strcpy (homedir,getenv("HOME"));
321        if (!geteuid()) strcpy (homedir,"/httphome");
322           else         strcat (homedir,"/httphome");
323
324        strcpy(hello,homedir);
325        strcat(hello,"/0hello.html");
326
327	if (chdir(homedir))
328           {
329	   perror("chdir");
330           puts(homedir);
331           exit(1);
332	   }
333        init_servconnection();
334
335        if (fork()) exit(0);
336
337        setpgrp(0,65534);
338	signal(SIGQUIT, SIG_IGN);
339	signal(SIGHUP, SIG_IGN);
340
341        if (listen(http_sock,100) < 0) exit(1);
342
343        label:
344	attenteconnection();
345        if (fork())
346           {
347           close(con_sock);
348           goto label;
349           }
350        alarm(1800);
351	traite_req();
352        exit(0);
353}
354
355
356
357char *adate()
358{
359        static char out[50];
360        long now;
361        struct tm *t;
362        time(&now);
363        t = localtime(&now);
364        sprintf(out, "%02d:%02d:%02d %02d/%02d/%02d",
365                     t->tm_hour, t->tm_min, t->tm_sec,
366                     t->tm_mday, t->tm_mon+1, t->tm_year );
367        return out;
368}
369