3 * Copyright (C) 2005 Simone Piccardi
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.
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.
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.
19 /****************************************************************
22 * Elementary WWW server (port 80)
24 * Author: Simone Piccardi
27 * Usage: wwwd -h give all info
29 ****************************************************************/
31 * Include needed headers
34 #include <sys/types.h> /* primitive system data types */
35 #include <sys/stat.h> /* file characteristics constants and functions */
36 #include <stdlib.h> /* C standard library */
37 #include <stdio.h> /* standard I/O library */
38 #include <unistd.h> /* unix standard library */
39 #include <arpa/inet.h> /* IP addresses conversion utilities */
40 #include <sys/socket.h> /* socket constants, types and functions */
41 #include <time.h> /* date and time constants, types and functions */
42 #include <syslog.h> /* syslog system functions */
43 #include <signal.h> /* signal constants, types and functions */
44 #include <errno.h> /* error definitions and routines */
45 #include <string.h> /* C strings library */
46 #include <dirent.h> /* directory operation constants and functions */
51 * Function and globals definitions
55 int demonize = 1; /* daemon use option: default is daemon */
56 int debugging = 0; /* debug info printing option: default is no debug */
64 void ServPage(int sockfd);
65 void PrintErr(char * error);
66 void print_headers(FILE *file, struct code_page code);
67 void print_error(FILE *file, struct code_page page, char * string);
71 */int main(int argc, char *argv[])
74 * Variables definition
82 struct sockaddr_in cli_add;
84 char debug[MAXLINE], ipaddr[20];
86 * Input section: decode parameters passed in the calling
90 opterr = 0; /* don't want writing to stderr */
91 while ( (i = getopt(argc, argv, "hwdicr:")) != -1) {
97 printf("Wrong -h option use\n");
117 case '?': /* unrecognized options */
118 printf("Unrecognized options -%c\n",optopt);
120 default: /* should not reached */
124 /* ***********************************************************
126 * Options processing completed
128 * Main code beginning
130 * ***********************************************************/
131 /* Main code begin here */
132 if (compat) { /* install signal handler */
133 Signal(SIGCHLD, HandSigCHLD); /* non restarting handler */
135 SignalRestart(SIGCHLD, HandSigCHLD); /* restarting handler */
137 /* create and bind socket */
138 if ( (list_fd = sockbindopt(argv[optind], "www", 6,
139 SOCK_STREAM, reuse)) < 0) {
142 /* chroot if requested */
144 if (chdir(rootdir)) {
145 perror("Cannot find directory to chroot");
148 if (chroot(rootdir)) {
149 perror("Cannot chroot");
153 /* release privileges and go daemon */
154 if (setgid(65534) !=0) { /* first give away group privileges */
155 perror("cannot give away group privileges");
158 if (setuid(65534) !=0) { /* and only after user ... */
159 perror("cannot give away user privileges");
162 if (demonize) { /* go daemon */
163 openlog(argv[0], 0, LOG_DAEMON); /* open logging */
164 if (daemon(0, 0) != 0) {
165 perror("cannot start as daemon");
170 if (listen(list_fd, BACKLOG) < 0 ) {
171 PrintErr("listen error");
174 /* handle echo to client */
176 /* accept connection */
177 len = sizeof(cli_add);
178 while (((conn_fd = accept(list_fd, (struct sockaddr *)&cli_add, &len))
179 < 0) && (errno == EINTR));
181 PrintErr("accept error");
185 inet_ntop(AF_INET, &cli_add.sin_addr, ipaddr, sizeof(ipaddr));
186 snprintf(debug, MAXLINE, "Accepted connection form %s\n", ipaddr);
188 syslog(LOG_DEBUG, debug);
193 /* fork to handle connection */
194 if ( (pid = fork()) < 0 ){
195 PrintErr("fork error");
198 if (pid == 0) { /* child */
199 close(list_fd); /* close listening socket */
200 ServPage(conn_fd); /* handle echo */
202 snprintf(debug, MAXLINE, "Closed connection %s\n", ipaddr);
204 syslog(LOG_DEBUG, debug);
210 } else { /* parent */
211 close(conn_fd); /* close connected socket */
214 /* normal exit, never reached */
218 * routine to print usage info and exit
222 printf("Elementary echo server\n");
224 printf(" wwwd [-h] \n");
225 printf(" -h print this help\n");
226 printf(" -d write debug info\n");
227 printf(" -i use interactively\n");
228 printf(" -c disable BSD semantics\n");
229 printf(" -r /path chroot on /path\n");
233 * routine to handle echo for connection
235 void ServPage(int sockfd)
237 char buffer[MAXLINE];
240 char *line, *copy, *method, *ptr, *filename, *version;
241 char *methods[] = { "GET", "HEAD", NULL };
242 struct code_page codes[] = {
243 { "200", "OK", "%s"},
244 { "400", "Bad Request",
245 "Your browser sent a request that this server could not understand."
246 "<P>The request line<P>%s<P> is invalid following the protocol<P>"},
247 { "404", "Not Found",
248 "The requested URL %s was not found on this server.<P>"},
249 { "500", "Internal Server Error",
250 "We got an error processing your request.<P>Error is: %s<P>"},
251 { "405", "Method Not Allowed", "Method %s not allowed.<P>"},
252 { "403", "Forbidden", "You cannot access %s.<P>"}
257 sock = fdopen(sockfd, "w+");
258 /* main loop, reading 0 char means client close connection */
259 line = fgets(buffer, MAXLINE, sock);
261 PrintErr("Errore in lettura");
264 /* parsing first line, getting method and filename */
265 copy = strndupa(line, MAXLINE);
266 if ((method = strtok_r(copy, " ", &ptr)) == NULL) {
267 print_headers(sock, codes[1]);
268 print_error(sock, codes[1], line);
271 if ((filename = strtok_r(NULL, " ", &ptr)) == NULL) {
272 print_headers(sock, codes[1]);
273 print_error(sock, codes[1], line);
276 if ((version = strtok_r(NULL, " ", &ptr)) == NULL) {
277 print_headers(sock, codes[1]);
278 print_error(sock, codes[1], line);
282 while ( (ptr = methods[i]) != NULL) {
283 if ( (strncmp(ptr, method, strlen(ptr)) == 0)) {
289 print_headers(sock, codes[4]);
290 print_error(sock, codes[4], method);
294 while (strcmp(line,"\r\n")) {
295 line = fgets(buffer, MAXLINE, sock);
298 if ( (file = fopen(filename, "r")) == NULL) {
299 if ( (errno == EACCES)||(errno == EPERM) ) {
300 print_headers(sock, codes[5]);
301 print_error(sock, codes[5], filename);
303 print_headers(sock, codes[2]);
304 print_error(sock, codes[2], filename);
308 print_headers(sock, codes[0]);
309 while (!feof(file)) {
310 if ( (nleft = full_fread(file, outbuf, 1024)) != 0) {
312 strncpy(buffer, strerror(errno), MAXLINE);
313 print_headers(sock, codes[3]);
314 print_error(sock, codes[3], buffer);
318 if (full_fwrite(sock, outbuf, 1024-nleft) != 0) {
320 strncpy(buffer, strerror(errno), MAXLINE);
321 print_headers(sock, codes[3]);
322 print_error(sock, codes[3], buffer);
332 * routine to print error on stout or syslog
334 void PrintErr(char * error)
336 if (demonize) { /* daemon mode */
337 syslog(LOG_ERR, "%s: %m", error); /* log string and error message */
344 void print_headers(FILE *file, struct code_page code)
348 fprintf(file, "HTTP/1.0 %s %s \n", code.code, code.name);
350 fprintf(file, "Date: %s", ctime(&tempo));
351 fprintf(file, "Server: WWWd test server\n");
352 fprintf(file, "Connection: close\n");
353 fprintf(file, "Content-Type: text/html; charset=iso-8859-1\n");
358 void print_error(FILE *file, struct code_page page, char * string)
360 fprintf(file, "<HTML><HEAD><TITLE>%s %s</TITLE></HEAD>\n",
361 page.code, page.name);
362 fprintf(file, "<BODY><H1>%s</H1>\n", page.name);
363 fprintf(file, page.body, string);
364 fprintf(file, "<HR><ADDRESS>WWWd by S. Piccardi</ADDRESS></BODY></HTML>");