Inserito un inizio di esempio dell'uso delle funzioni delle capabilities.
[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 struct code_page {
57     char * code; 
58     char * name;
59     char * body;
60 };
61
62 void usage(void);
63 void ServPage(int sockfd);
64 void PrintErr(char * error);
65 void print_headers(FILE *file, struct code_page code);
66 void print_error(FILE *file, struct code_page page, char * string);
67
68 /* Program beginning */
69 int main(int argc, char *argv[])
70 {
71 /* 
72  * Variables definition  
73  */
74     int list_fd, conn_fd;
75     int compat = 0;
76     int reroot = 0;
77     int reuse = 1;
78     char * rootdir;
79     pid_t pid;
80     struct sockaddr_in cli_add;
81     socklen_t len;
82     char debug[MAXLINE], ipaddr[20];
83     /*
84      * Input section: decode parameters passed in the calling 
85      * Use getopt function
86      */
87     int i;
88     opterr = 0;  /* don't want writing to stderr */
89     while ( (i = getopt(argc, argv, "hwdicr:")) != -1) {
90         switch (i) {
91         /* 
92          * Handling options 
93          */ 
94         case 'h':  
95             printf("Wrong -h option use\n");
96             usage();
97             return(0);
98             break;
99         case 'i':
100             demonize = 0;
101             break;
102         case 'c':
103             compat = 1;
104             break;
105         case 'd':
106             debugging = 1;
107             break;
108         case 'w':
109             reuse = 0;
110             break;
111         case 'r':
112             reroot = 1;
113             rootdir = optarg;
114             break;
115         case '?':   /* unrecognized options */
116             printf("Unrecognized options -%c\n",optopt);
117             usage();
118         default:    /* should not reached */
119             usage();
120         }
121     }
122     /* ***********************************************************
123      * 
124      *           Options processing completed
125      *
126      *                Main code beginning
127      * 
128      * ***********************************************************/
129     /* Main code begin here */
130     if (compat) {                             /* install signal handler */
131         Signal(SIGCHLD, HandSigCHLD);         /* non restarting handler */
132     } else {
133         SignalRestart(SIGCHLD, HandSigCHLD);  /* restarting handler */
134     }
135     /* create and bind socket */
136     if ( (list_fd = sockbindopt(argv[optind], "www", 6, 
137                                 SOCK_STREAM, reuse)) < 0) {
138         return 1;
139     }   
140     /* chroot if requested */
141     if (reroot) {
142         if (chdir(rootdir)) {
143             perror("Cannot find directory to chroot");
144             exit(1);
145         }
146         if (chroot(rootdir)) {
147             perror("Cannot chroot");
148             exit(1);
149         }
150     }
151     /* release privileges and go daemon */
152     if (setgid(65534) !=0) { /* first give away group privileges */
153         perror("cannot give away group privileges");
154         exit(1);
155     }
156     if (setuid(65534) !=0) { /* and only after user ... */
157         perror("cannot give away user privileges");
158         exit(1);
159     }
160     if (demonize) {          /* go daemon */
161         openlog(argv[0], 0, LOG_DAEMON); /* open logging */
162         if (daemon(0, 0) != 0) {
163             perror("cannot start as daemon");
164             exit(1);
165         }
166     }
167     /* main body */
168     if (listen(list_fd, BACKLOG) < 0 ) {
169         PrintErr("listen error");
170         exit(1);
171     }
172     /* handle echo to client */
173     while (1) {
174         /* accept connection */
175         len = sizeof(cli_add);
176         while (((conn_fd = accept(list_fd, (struct sockaddr *)&cli_add, &len)) 
177                 < 0) && (errno == EINTR)); 
178         if (conn_fd < 0) {
179             PrintErr("accept error");
180             exit(1);
181         }
182         if (debugging) {
183             inet_ntop(AF_INET, &cli_add.sin_addr, ipaddr, sizeof(ipaddr));
184             snprintf(debug, MAXLINE, "Accepted connection form %s\n", ipaddr);
185             if (demonize) {
186                 syslog(LOG_DEBUG, debug);
187             } else {
188                 printf("%s", debug);
189             }
190         }
191         /* fork to handle connection */
192         if ( (pid = fork()) < 0 ){
193             PrintErr("fork error");
194             exit(1);
195         }
196         if (pid == 0) {      /* child */
197             close(list_fd);          /* close listening socket */   
198             ServPage(conn_fd);       /* handle echo */
199             if (debugging) {
200                 snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr);
201                 if (demonize) {
202                     syslog(LOG_DEBUG, debug);
203                 } else {
204                     printf("%s", debug);
205                 }
206             }
207             exit(0);
208         } else {             /* parent */
209             close(conn_fd);          /* close connected socket */
210         }
211     }
212     /* normal exit, never reached */
213     exit(0);
214 }
215 /*
216  * routine to print usage info and exit
217  */
218 void usage(void) 
219 {
220     printf("Elementary echo server\n");
221     printf("Usage:\n");
222     printf("  echod [-h] \n");
223     printf("  -h           print this help\n");
224     printf("  -d           write debug info\n");
225     printf("  -i           use interactively\n");
226     printf("  -c           disable BSD semantics\n");
227     printf("  -r /path     chroot on /path\n");
228     exit(1);
229 }
230 /*
231  * routine to handle echo for connection
232  */
233 void ServPage(int sockfd) 
234 {
235     char buffer[MAXLINE];
236     char outbuf[1024];
237     FILE *sock, *file;
238     char *line, *copy, *method, *ptr, *filename, *version;
239     char *methods[] = { "GET", "HEAD", NULL };
240     struct code_page codes[] = {
241         { "200", "OK", "%s"},
242         { "400", "Bad Request", 
243           "Your browser sent a request that this server could not understand."
244           "<P>The request line<P>%s<P> is invalid following the protocol<P>"}, 
245         { "404", "Not Found", 
246           "The requested URL %s was not found on this server.<P>"},
247         { "500", "Internal Server Error", 
248           "We got an error processing your request.<P>Error is: %s<P>"},
249         { "405", "Method Not Allowed", "Method %s not allowed.<P>"},
250         { "403", "Forbidden", "You cannot access %s.<P>"}
251     };
252     int nleft;
253     int i;
254
255     sock = fdopen(sockfd, "w+");
256     /* main loop, reading 0 char means client close connection */
257     line = fgets(buffer, MAXLINE, sock);
258     if (line == NULL) {
259         PrintErr("Errore in lettura");
260         return;
261     }
262     /* parsing first line, getting method and filename */
263     copy = strndupa(line, MAXLINE);
264     if ((method = strtok_r(copy, " ", &ptr)) == NULL) {
265         print_headers(sock, codes[1]);
266         print_error(sock, codes[1], line);
267         return;
268     }
269     if ((filename = strtok_r(NULL, " ", &ptr)) == NULL) {
270         print_headers(sock, codes[1]);
271         print_error(sock, codes[1], line);
272         return;
273     }
274     if ((version = strtok_r(NULL, " ", &ptr)) == NULL) {
275         print_headers(sock, codes[1]);
276         print_error(sock, codes[1], line);
277         return;
278     }
279     i = 0;
280     while ( (ptr = methods[i]) != NULL) {
281         if ( (strncmp(ptr, method, strlen(ptr)) == 0)) {
282             break;
283         }
284         i++;
285     }
286     if (i>=2) {
287         print_headers(sock, codes[4]);
288         print_error(sock, codes[4], method);
289         return;
290     }
291
292     while (strcmp(line,"\r\n")) {
293         line = fgets(buffer, MAXLINE, sock);
294     }
295
296     if ( (file = fopen(filename, "r")) == NULL) {
297         if ( (errno == EACCES)||(errno == EPERM) ) {
298             print_headers(sock, codes[5]);
299             print_error(sock, codes[5], filename);
300         } else {
301             print_headers(sock, codes[2]);
302             print_error(sock, codes[2], filename);
303         }
304         return;
305     }
306     print_headers(sock, codes[0]);
307     while (!feof(file)) {
308         if ( (nleft = full_fread(file, outbuf, 1024)) != 0) {
309             if (ferror(file)) {
310                 strncpy(buffer, strerror(errno), MAXLINE);
311                 print_headers(sock, codes[3]);
312                 print_error(sock, codes[3], buffer);
313                 return;
314             }
315         }
316         if (full_fwrite(sock, outbuf, 1024-nleft) != 0) {
317             if (ferror(file)) {
318                 strncpy(buffer, strerror(errno), MAXLINE);
319                 print_headers(sock, codes[3]);
320                 print_error(sock, codes[3], buffer);
321                 return;
322             }   
323         }
324     }
325     fclose(file);
326     fclose(sock);
327     return;
328 }
329 /*
330  * routine to print error on stout or syslog
331  */
332 void PrintErr(char * error) 
333 {
334     if (demonize) {                       /* daemon mode */
335         syslog(LOG_ERR, "%s: %m", error); /* log string and error message */
336     } else {
337         perror(error);
338     }
339     return;
340 }
341
342 void print_headers(FILE *file, struct code_page code)
343 {
344     time_t tempo;
345
346     fprintf(file, "HTTP/1.0 %s %s \n", code.code, code.name);
347     time(&tempo);
348     fprintf(file, "Date: %s", ctime(&tempo));
349     fprintf(file, "Server: WWWd test server\n");
350     fprintf(file, "Connection: close\n");
351     fprintf(file, "Content-Type: text/html; charset=iso-8859-1\n");
352     fprintf(file, "\n");
353     return;
354 }
355
356 void print_error(FILE *file, struct code_page page, char * string)
357 {
358     fprintf(file, "<HTML><HEAD><TITLE>%s %s</TITLE></HEAD>\n",
359             page.code, page.name);
360     fprintf(file, "<BODY><H1>%s</H1>\n", page.name);
361     fprintf(file, page.body, string);
362     fprintf(file, "<HR><ADDRESS>WWWd by S. Piccardi</ADDRESS></BODY></HTML>");
363     return;
364 }
365