Modifiche dell'ultimo minuto e messo insieme anche una opzione di chroot
[gapil.git] / sources / wwwd.c
1 /* wwwd.c
2  * 
3  * Copyright (C) 2005 Simone Piccardi
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /****************************************************************
20  *
21  * Program wwwd 
22  * Elementary WWW server (port 80)
23  *
24  * Author: Simone Piccardi
25  * Mar. 2005
26  *
27  * Usage: wwwd -h give all info
28  *
29  * $Id$ 
30  *
31  ****************************************************************/
32 /* 
33  * Include needed headers
34  */
35 #define _GNU_SOURCE
36 #include <string.h>      /* error strings */
37 #include <sys/types.h>   /* predefined types */
38 #include <unistd.h>      /* include unix standard library */
39 #include <arpa/inet.h>   /* IP addresses conversion utiliites */
40 #include <sys/socket.h>  /* socket library */
41 #include <stdio.h>       /* include standard I/O library */
42 #include <time.h>
43 #include <syslog.h>      /* syslog system functions */
44 #include <signal.h>      /* signal functions */
45 #include <errno.h>       /* error code */
46 #include <stdlib.h>
47
48 #include "Gapil.h"
49
50 #define BACKLOG 10
51 #define MAXLINE 256
52 int demonize  = 1;  /* daemon use option: default is daemon */
53 int debugging = 0;  /* debug info printing option: default is no debug */
54
55 /* Subroutines declaration */
56 void usage(void);
57 void ServPage(int sockfd);
58 void PrintErr(char * error);
59 void print_headers(FILE *file);
60 void print_err404(FILE *file, char *filename);
61 void print_err400(FILE *file, char *string);
62 void print_err500(FILE *file, char *string);
63
64 /* Program beginning */
65 int main(int argc, char *argv[])
66 {
67 /* 
68  * Variables definition  
69  */
70     int list_fd, conn_fd;
71     int waiting = 0;
72     int compat = 0;
73     int reroot = 0;
74     char * rootdir;
75     pid_t pid;
76     struct sockaddr_in cli_add;
77     socklen_t len;
78     char debug[MAXLINE], ipaddr[20];
79     /*
80      * Input section: decode parameters passed in the calling 
81      * Use getopt function
82      */
83     int i;
84     opterr = 0;  /* don't want writing to stderr */
85     while ( (i = getopt(argc, argv, "hdicw:r:")) != -1) {
86         switch (i) {
87         /* 
88          * Handling options 
89          */ 
90         case 'h':  
91             printf("Wrong -h option use\n");
92             usage();
93             return(0);
94             break;
95         case 'i':
96             demonize = 0;
97             break;
98         case 'c':
99             compat = 1;
100             break;
101         case 'd':
102             debugging = 1;
103             break;
104         case 'w':
105             waiting = strtol(optarg, NULL, 10);
106             break;
107         case 'r':
108             printf("chroot\n");
109             reroot = 1;
110             rootdir = optarg;
111             break;
112         case '?':   /* unrecognized options */
113             printf("Unrecognized options -%c\n",optopt);
114             usage();
115         default:    /* should not reached */
116             usage();
117         }
118     }
119     /* ***********************************************************
120      * 
121      *           Options processing completed
122      *
123      *                Main code beginning
124      * 
125      * ***********************************************************/
126     /* Main code begin here */
127     if (compat) {                             /* install signal handler */
128         Signal(SIGCHLD, HandSigCHLD);         /* non restarting handler */
129     } else {
130         SignalRestart(SIGCHLD, HandSigCHLD);  /* restarting handler */
131     }
132     /* create and bind socket */
133     if ( (list_fd = sockbind2(argv[optind], "www", 6, SOCK_STREAM)) < 0) {
134         return 1;
135     }   
136     /* chroot if requested */
137     if (reroot) {
138         printf("chroot to %s\n", rootdir);
139         if (chdir(rootdir)) {
140             perror("Cannot find directory to chroot");
141             exit(1);
142         }
143         if (chroot(rootdir)) {
144             perror("Cannot chroot");
145             exit(1);
146         }
147     }
148     /* release privileges and go daemon */
149     if (setgid(65534) !=0) { /* first give away group privileges */
150         perror("cannot give away group privileges");
151         exit(1);
152     }
153     if (setuid(65534) !=0) { /* and only after user ... */
154         perror("cannot give away user privileges");
155         exit(1);
156     }
157     if (demonize) {          /* go daemon */
158         openlog(argv[0], 0, LOG_DAEMON); /* open logging */
159         if (daemon(0, 0) != 0) {
160             perror("cannot start as daemon");
161             exit(1);
162         }
163     }
164     /* main body */
165     if (listen(list_fd, BACKLOG) < 0 ) {
166         PrintErr("listen error");
167         exit(1);
168     }
169     if (waiting) sleep(waiting);
170     /* handle echo to client */
171     while (1) {
172         /* accept connection */
173         len = sizeof(cli_add);
174         while (((conn_fd = accept(list_fd, (struct sockaddr *)&cli_add, &len)) 
175                 < 0) && (errno == EINTR)); 
176         if (conn_fd < 0) {
177             PrintErr("accept error");
178             exit(1);
179         }
180         if (debugging) {
181             inet_ntop(AF_INET, &cli_add.sin_addr, ipaddr, sizeof(ipaddr));
182             snprintf(debug, MAXLINE, "Accepted connection form %s\n", ipaddr);
183             if (demonize) {
184                 syslog(LOG_DEBUG, debug);
185             } else {
186                 printf("%s", debug);
187             }
188         }
189         /* fork to handle connection */
190         if ( (pid = fork()) < 0 ){
191             PrintErr("fork error");
192             exit(1);
193         }
194         if (pid == 0) {      /* child */
195             close(list_fd);          /* close listening socket */   
196             ServPage(conn_fd);       /* handle echo */
197             if (debugging) {
198                 snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr);
199                 if (demonize) {
200                     syslog(LOG_DEBUG, debug);
201                 } else {
202                     printf("%s", debug);
203                 }
204             }
205             exit(0);
206         } else {             /* parent */
207             close(conn_fd);          /* close connected socket */
208         }
209     }
210     /* normal exit, never reached */
211     exit(0);
212 }
213 /*
214  * routine to print usage info and exit
215  */
216 void usage(void) 
217 {
218     printf("Elementary echo server\n");
219     printf("Usage:\n");
220     printf("  echod [-h] \n");
221     printf("  -h           print this help\n");
222     printf("  -d           write debug info\n");
223     printf("  -i           use interactively\n");
224     printf("  -c           disable BSD semantics\n");
225     printf("  -w N         wait N sec. before calling accept\n");
226     exit(1);
227 }
228 /*
229  * routine to handle echo for connection
230  */
231 void ServPage(int sockfd) 
232 {
233     char buffer[MAXLINE];
234     char outbuf[1024];
235     FILE *sock, *file;
236     char *line, *copy, *method, *ptr, *filename, *version;
237     char *methods[] = { "GET", "PUT", NULL };
238     char *codes[] = {
239         "200 OK",
240         "400 Bad Request",
241         "404 Not Found",
242         "500 Internal Server Error",
243         NULL
244     };
245     int nleft;
246     int i, j;
247
248     sock = fdopen(sockfd, "w+");
249     /* main loop, reading 0 char means client close connection */
250     line = fgets(buffer, MAXLINE, sock);
251     if (line == NULL) {
252         PrintErr("Errore in lettura");
253         return;
254     }
255     /* parsing first line, getting method and filename */
256     copy = strndupa(line, MAXLINE);
257     if ((method = strtok_r(copy, " ", &ptr)) == NULL) {
258         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
259         print_headers(sock);
260         print_err400(sock, line);
261         return;
262     }
263     if ((filename = strtok_r(NULL, " ", &ptr)) == NULL) {
264         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
265         print_headers(sock);
266         print_err400(sock, line);
267         return;
268     }
269     if ((version = strtok_r(NULL, " ", &ptr)) == NULL) {
270         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
271         print_headers(sock);
272         print_err400(sock, line);
273         return;
274     }
275     i = 0;
276     while ( (ptr = methods[i]) != NULL) {
277         if ( (strncmp(ptr, method, strlen(ptr)) == 0)) {
278             break;
279         }
280         i++;
281     }
282     if (i>=2) {
283         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
284         print_headers(sock);
285         print_err400(sock, line);
286         return;
287     }
288
289     while (strcmp(line,"\r\n")) {
290         line = fgets(buffer, MAXLINE, sock);
291     }
292
293     if ( (file = fopen(filename, "r")) == NULL) {
294         fprintf(sock, "HTTP/1.0 %s\n", codes[2]);
295         print_headers(sock);
296         print_err404(sock, filename);
297         return;
298     }
299     fprintf(sock, "HTTP/1.0 %s\n", codes[0]);
300     print_headers(sock);
301
302     j = 0;
303     while (!feof(file)) {
304         if ( (nleft = full_fread(file, outbuf, 1024)) != 0) {
305             if (ferror(file)) {
306                 fprintf(sock, "HTTP/1.0 %s\n", codes[3]);
307                 print_headers(sock);
308                 snprintf(buffer, MAXLINE, "reading %s", filename);
309                 print_err500(sock, buffer);
310                 return;
311             }
312         }
313         if (full_fwrite(sock, outbuf, 1024-nleft) != 0) {
314             if (ferror(file)) {
315                 fprintf(sock, "HTTP/1.0 %s\n", codes[3]);
316                 print_headers(sock);
317                 snprintf(buffer, MAXLINE, "writing");
318                 print_err500(sock, buffer);
319                 return;
320             }   
321         }
322     }
323     fclose(file);
324     fclose(sock);
325     return;
326 }
327 /*
328  * routine to print error on stout or syslog
329  */
330 void PrintErr(char * error) 
331 {
332     if (demonize) {                       /* daemon mode */
333         syslog(LOG_ERR, "%s: %m", error); /* log string and error message */
334     } else {
335         perror(error);
336     }
337     return;
338 }
339
340 void print_headers(FILE *file)
341 {
342     time_t tempo;
343
344     time(&tempo);
345     fprintf(file, "Date: %s", ctime(&tempo));
346     fprintf(file, "Server: WWWd test server\n");
347     fprintf(file, "Connection: close\n");
348     fprintf(file, "Content-Type: text/html; charset=iso-8859-1\n");
349     fprintf(file, "\n");
350     return;
351 }
352
353 void print_err404(FILE *file, char *filename)
354 {
355     fprintf(file, "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
356     fprintf(file, "<BODY><H1>Not Found</H1>\nThe requested ");
357     fprintf(file, "URL %s was not found on this server.<P><HR>", filename);
358     fprintf(file, "<ADDRESS>WWWd by Simone Piccardi</ADDRESS></BODY></HTML>");
359     return;
360 }
361
362 void print_err400(FILE *file, char *string)
363 {
364     fprintf(file, "<HTML><HEAD><TITLE>404 Bad Request</TITLE></HEAD>\n");
365     fprintf(file, "<BODY><H1>Bad Request</H1>\n ");
366     fprintf(file, "Your browser sent a request that this server could not ");
367     fprintf(file, "understand.<P>The request line<P> %s <P>", string);
368     fprintf(file, "is invalid following the protocol<p><HR>");
369     fprintf(file, "<ADDRESS>WWWd by Simone Piccardi </ADDRESS></BODY></HTML>");
370     return;
371 }
372
373
374 void print_err500(FILE *file, char *string)
375 {
376     fprintf(file, "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE>\n");
377     fprintf(file, "</HEAD><BODY><H1>Internal Server Error</H1>\n ");
378     fprintf(file, "We got an error processing your request.<P>");
379     fprintf(file, "Error is: %s <P><HR>\n", string);
380     fprintf(file, "<ADDRESS>WWWd by Simone Piccardi </ADDRESS></BODY></HTML>");
381     return;
382 }