2279 regels
		
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2279 regels
		
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * @PROJECT			Kagera uHTTP Daemon
 | |
|  * @COPYRIGHT		See COPYING in the top level directory
 | |
|  * @FILE			kuhttpd.c
 | |
|  * @PURPOSE			HTTP Server
 | |
|  * @DEVELOPERS		Eric Bishop <eric@gargoyle-router.com>
 | |
|  *					Rafal Kupiec <belliash@asiotec.eu.org>
 | |
|  *					Jef Poskanzer <jef@acme.com>
 | |
|  */
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdarg.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <syslog.h>
 | |
| #include <limits.h>
 | |
| #include <sys/param.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/mman.h>
 | |
| #include <time.h>
 | |
| #include <pwd.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <signal.h>
 | |
| #include <ctype.h>
 | |
| #include <sys/wait.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <netinet/tcp.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <netdb.h>
 | |
| #include <dirent.h>
 | |
| 
 | |
| #include <cyassl/openssl/ssl.h>
 | |
| #include <cyassl/openssl/err.h>
 | |
| #include <cyassl/error.h>
 | |
| 
 | |
| #ifdef CROSS_BUILD
 | |
| 	#include <cyassl/cyassl_error.h>
 | |
| #endif
 | |
| 
 | |
| #include "kuhttpd.h"
 | |
| #include "dateparse.h"
 | |
| #include "match.h"
 | |
| #include "mime_enc.h"
 | |
| #include "mime_typ.h"
 | |
| 
 | |
| static void add_headers(int s, char* title, char* extra_header, char* me, char* mt, off_t b, time_t mod) {
 | |
| 	time_t now;
 | |
| 	char timebuf[100];
 | |
| 	char buf[10000];
 | |
| 	int buflen;
 | |
| 	const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S GMT";
 | |
| 
 | |
| 	status = s;
 | |
| 	bytes = b;
 | |
| 	make_log_entry();
 | |
| 	start_response();
 | |
| 	buflen = snprintf(buf, sizeof(buf), "%s %d %s\015\012", protocol, status, title);
 | |
| 	add_to_response(buf, buflen);
 | |
| 	buflen = snprintf(buf, sizeof(buf), "Server: %s\015\012", SERVER_SOFTWARE);
 | |
| 	add_to_response(buf, buflen);
 | |
| 	now = time((time_t*) 0);
 | |
| 	(void) strftime(timebuf, sizeof(timebuf), rfc1123_fmt, gmtime(&now));
 | |
| 	buflen = snprintf(buf, sizeof(buf), "Date: %s\015\012", timebuf);
 | |
| 	add_to_response(buf, buflen);
 | |
| 	buflen = snprintf(buf, sizeof(buf), "Expires: %s\015\012", timebuf);
 | |
| 	add_to_response(buf, buflen);
 | |
| 	if(mod != (time_t) -1) {
 | |
| 		(void) strftime(timebuf, sizeof(timebuf), rfc1123_fmt, gmtime(&mod));
 | |
| 		buflen = snprintf(buf, sizeof(buf), "Last-Modified: %s\015\012", timebuf);
 | |
| 		add_to_response(buf, buflen);
 | |
| 	}
 | |
| 	if(extra_header != (char*) 0 && extra_header[0] != '\0') {
 | |
| 		buflen = snprintf(buf, sizeof(buf), "%s\015\012", extra_header);
 | |
| 		add_to_response(buf, buflen);
 | |
| 	}
 | |
| 	if(me != (char*) 0 && me[0] != '\0') {
 | |
| 		buflen = snprintf(buf, sizeof(buf), "Content-Encoding: %s\015\012", me);
 | |
| 		add_to_response(buf, buflen);
 | |
| 	}
 | |
| 	if(mt != (char*) 0 && mt[0] != '\0') {
 | |
| 		buflen = snprintf(buf, sizeof(buf), "Content-Type: %s\015\012", mt);
 | |
| 		add_to_response(buf, buflen);
 | |
| 	}
 | |
| 	if(bytes >= 0) {
 | |
| 		buflen = snprintf(buf, sizeof(buf), "Content-Length: %lld\015\012", (long long int) bytes);
 | |
| 		add_to_response(buf, buflen);
 | |
| 	}
 | |
| 	if(p3p != (char*) 0 && p3p[0] != '\0') {
 | |
| 		buflen = snprintf(buf, sizeof(buf), "P3P: %s\015\012", p3p);
 | |
| 		add_to_response(buf, buflen);
 | |
| 	}
 | |
| 	buflen = snprintf(buf, sizeof(buf), "\015\012");
 | |
| 	add_to_response(buf, buflen);
 | |
| }
 | |
| 
 | |
| static void add_to_buf(char** bufP, size_t* bufsizeP, size_t* buflenP, char* str, size_t len) {
 | |
| 	if(*bufsizeP == 0) {
 | |
| 		*bufsizeP = len + 500;
 | |
| 		*buflenP = 0;
 | |
| 		*bufP = (char*) e_malloc(*bufsizeP);
 | |
| 	} else if(*buflenP + len >= *bufsizeP) {
 | |
| 		*bufsizeP = *buflenP + len + 500;
 | |
| 		*bufP = (char*) e_realloc((void*) *bufP, *bufsizeP);
 | |
| 	}
 | |
| 	(void) memmove(&((*bufP)[*buflenP]), str, len);
 | |
| 	*buflenP += len;
 | |
| 	(*bufP)[*buflenP] = '\0';
 | |
| }
 | |
| 
 | |
| static void add_to_request(char* str, size_t len) {
 | |
| 	add_to_buf(&request, &request_size, &request_len, str, len);
 | |
| }
 | |
| 
 | |
| static void add_to_response(char* str, size_t len) {
 | |
| 	add_to_buf(&response, &response_size, &response_len, str, len);
 | |
| }
 | |
| 
 | |
| static void auth_check(char* dirname, int is_ssl) {
 | |
| 	char authpath[10000];
 | |
| 	char realmName[500];
 | |
| 	struct stat sb;
 | |
| 	char authinfo[500];
 | |
| 	char* authpass;
 | |
| 	char* colon;
 | |
| 	static char line[10000];
 | |
| 	int l;
 | |
| 	FILE* fp;
 | |
| 	char* cryp;
 | |
| 
 | |
| 	if(dirname[strlen(dirname) - 1] == '/') {
 | |
| 		(void) snprintf(authpath, sizeof(authpath), "%s%s", dirname, AUTH_FILE);
 | |
| 	} else {
 | |
| 		(void) snprintf(authpath, sizeof(authpath), "%s/%s", dirname, AUTH_FILE);
 | |
| 	}
 | |
| 	if(stat(authpath, &sb) < 0) {
 | |
| 		if(defaultRealmName == NULL || defaultRealmPasswordFile == NULL) {
 | |
| 			return;
 | |
| 		} else {
 | |
| 			snprintf(authpath, sizeof(authpath), "%s", defaultRealmPasswordFile);
 | |
| 		}
 | |
| 	}
 | |
| 	if(strcmp(authpath, defaultRealmPasswordFile) == 0) {
 | |
| 		snprintf(realmName, sizeof(realmName), "%s", defaultRealmName);
 | |
| 	} else {
 | |
| 		snprintf(realmName, sizeof(realmName), "%s", dirname);
 | |
| 	}
 | |
| 	if(authorization == (char*) 0) {
 | |
| 		send_authenticate(realmName, is_ssl);
 | |
| 	}
 | |
| 	if(strncmp(authorization, "Basic ", 6) != 0) {
 | |
| 		send_authenticate(realmName, is_ssl);
 | |
| 	}
 | |
| 	l = b64_decode(&(authorization[6]), (unsigned char*) authinfo, sizeof(authinfo) - 1);
 | |
| 	authinfo[l] = '\0';
 | |
| 	authpass = strchr(authinfo, ':');
 | |
| 	if(authpass == (char*) 0) {
 | |
| 		send_authenticate(realmName, is_ssl);
 | |
| 	}
 | |
| 	*authpass++ = '\0';
 | |
| 	colon = strchr(authpass, ':');
 | |
| 	if(colon != (char*) 0) {
 | |
| 		*colon = '\0';
 | |
| 	}
 | |
| 	fp = fopen(authpath, "r");
 | |
| 	if(fp == (FILE*) 0) {
 | |
| 		syslog(LOG_ERR, "%.80s auth file %.80s could not be opened - %m", ntoa(&client_addr), authpath);
 | |
| 		send_error(403, "Forbidden", "", "File is protected.", is_ssl);
 | |
| 	}
 | |
| 	while(fgets(line, sizeof(line), fp) != (char*) 0) {
 | |
| 		l = strlen(line);
 | |
| 		if(line[l - 1] == '\n') {
 | |
| 			line[l - 1] = '\0';
 | |
| 		}
 | |
| 		cryp = strchr(line, ':');
 | |
| 		if(cryp == (char*) 0) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		*cryp++ = '\0';
 | |
| 		if(strcmp(line, authinfo) == 0) {
 | |
| 			(void) fclose(fp);
 | |
| 			if(strcmp(crypt(authpass, cryp), cryp) == 0) {
 | |
| 				remoteuser = line;
 | |
| 				return;
 | |
| 			} else {
 | |
| 				send_authenticate(realmName, is_ssl);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	(void) fclose(fp);
 | |
| 	send_authenticate(realmName, is_ssl);
 | |
| }
 | |
| 
 | |
| static int b64_decode(const char* str, unsigned char* space, int size) {
 | |
| 	const char* cp;
 | |
| 	int space_idx, phase;
 | |
| 	int d, prev_d = 0;
 | |
| 	unsigned char c;
 | |
| 
 | |
| 	space_idx = 0;
 | |
| 	phase = 0;
 | |
| 	for(cp = str; *cp != '\0'; ++cp) {
 | |
| 		d = b64_decode_table[(int) *cp];
 | |
| 		if(d != -1) {
 | |
| 			switch(phase) {
 | |
| 				case 0:
 | |
| 					++phase;
 | |
| 					break;
 | |
| 				case 1:
 | |
| 					c = ((prev_d << 2) | ((d & 0x30) >> 4));
 | |
| 					if(space_idx < size) {
 | |
| 						space[space_idx++] = c;
 | |
| 					}
 | |
| 					++phase;
 | |
| 					break;
 | |
| 				case 2:
 | |
| 					c = (((prev_d & 0xf) << 4) | ((d & 0x3c) >> 2));
 | |
| 					if(space_idx < size) {
 | |
| 						space[space_idx++] = c;
 | |
| 					}
 | |
| 					++phase;
 | |
| 					break;
 | |
| 				case 3:
 | |
| 					c = (((prev_d & 0x03) << 6) | d);
 | |
| 					if(space_idx < size) {
 | |
| 						space[space_idx++] = c;
 | |
| 					}
 | |
| 					phase = 0;
 | |
| 				break;
 | |
| 			}
 | |
| 			prev_d = d;
 | |
| 		}
 | |
| 	}
 | |
| 	return space_idx;
 | |
| }
 | |
| 
 | |
| static char* build_env(char* fmt, char* arg) {
 | |
| 	char* cp;
 | |
| 	int size;
 | |
| 	static char* buf;
 | |
| 	static int maxbuf = 0;
 | |
| 
 | |
| 	size = strlen(fmt) + strlen(arg);
 | |
| 	if(size > maxbuf) {
 | |
| 		if(maxbuf == 0) {
 | |
| 			maxbuf = MAX(200, size + 100);
 | |
| 			buf = (char*) e_malloc(maxbuf);
 | |
| 		} else {
 | |
| 			maxbuf = MAX(maxbuf * 2, size * 5 / 4);
 | |
| 			buf = (char*) e_realloc((void*) buf, maxbuf);
 | |
| 		}
 | |
| 	}
 | |
| 	(void) snprintf(buf, maxbuf, fmt, arg);
 | |
| 	cp = e_strdup(buf);
 | |
| 	return cp;
 | |
| }
 | |
| 
 | |
| static void cgi_interpose_input(int wfd, int is_ssl) {
 | |
| 	size_t c;
 | |
| 	ssize_t r, r2;
 | |
| 	char buf[1024];
 | |
| 
 | |
| 	c = request_len - request_idx;
 | |
| 	if(c > 0) {
 | |
| 		if(write(wfd, &(request[request_idx]), c) != c) {
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	while(c < content_length) {
 | |
| 		r = my_read(buf, MIN( sizeof(buf), content_length - c), is_ssl);
 | |
| 		if(r < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 			sleep(1);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if(r <= 0) {
 | |
| 			return;
 | |
| 		}
 | |
| 		for(;;) {
 | |
| 			r2 = write(wfd, buf, r);
 | |
| 			if(r2 < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 				sleep(1);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if(r2 != r) {
 | |
| 				return;
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 		c += r;
 | |
| 	}
 | |
| 	post_post_garbage_hack(is_ssl);
 | |
| }
 | |
| 
 | |
| static void cgi_interpose_output(int rfd, int parse_headers, int is_ssl) {
 | |
| 	ssize_t r, r2;
 | |
| 	char buf[1024];
 | |
| 	
 | |
| 	if(!parse_headers) {
 | |
| 		char http_head[] = "HTTP/1.0 200 OK\015\012";
 | |
| 		(void) my_write(http_head, sizeof(http_head), is_ssl);
 | |
| 	} else {
 | |
| 		size_t headers_size, headers_len;
 | |
| 		char* headers;
 | |
| 		char* br;
 | |
| 		int status;
 | |
| 		char* title;
 | |
| 		char* cp;
 | |
| 		headers_size = 0;
 | |
| 		add_to_buf(&headers, &headers_size, &headers_len, (char*) 0, 0);
 | |
| 		for(;;) {
 | |
| 			r = read(rfd, buf, sizeof(buf));
 | |
| 			if(r < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 				sleep(1);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if(r <= 0) {
 | |
| 				br = &(headers[headers_len]);
 | |
| 				break;
 | |
| 			}
 | |
| 			add_to_buf(&headers, &headers_size, &headers_len, buf, r);
 | |
| 			if((br = strstr(headers, "\015\012\015\012")) != (char*) 0 || (br = strstr(headers, "\012\012")) != (char*) 0) {
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		if(headers[0] == '\0') {
 | |
| 			return;
 | |
| 		}
 | |
| 		status = 200;
 | |
| 		if((cp = strstr(headers, "Status:")) != (char*) 0 && cp < br && (cp == headers || *(cp-1) == '\012')) {
 | |
| 			cp += 7;
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			status = atoi(cp);
 | |
| 		}
 | |
| 		if((cp = strstr(headers, "Location:")) != (char*) 0 && cp < br && (cp == headers || *(cp-1) == '\012')) {
 | |
| 			status = 302;
 | |
| 		}
 | |
| 		switch(status) {
 | |
| 			case 200:
 | |
| 				title = "OK";
 | |
| 				break;
 | |
| 			case 302:
 | |
| 				title = "Found";
 | |
| 				break;
 | |
| 			case 304:
 | |
| 				title = "Not Modified";
 | |
| 				break;
 | |
| 			case 400:
 | |
| 				title = "Bad Request";
 | |
| 				break;
 | |
| 			case 401:
 | |
| 				title = "Unauthorized";
 | |
| 				break;
 | |
| 			case 403:
 | |
| 				title = "Forbidden";
 | |
| 				break;
 | |
| 			case 404:
 | |
| 				title = "Not Found";
 | |
| 				break;
 | |
| 			case 408:
 | |
| 				title = "Request Timeout";
 | |
| 				break;
 | |
| 			case 500:
 | |
| 				title = "Internal Error";
 | |
| 				break;
 | |
| 			case 501:
 | |
| 				title = "Not Implemented";
 | |
| 				break;
 | |
| 			case 503:
 | |
| 				title = "Service Temporarily Overloaded";
 | |
| 				break;
 | |
| 			default:
 | |
| 				title = "Something";
 | |
| 				break;
 | |
| 		}
 | |
| 		(void) snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, title);
 | |
| 		(void) my_write(buf, strlen(buf), is_ssl);
 | |
| 		if(strstr(headers, "Server:") == NULL) {
 | |
| 			char line[200];
 | |
| 			sprintf(line, "Server: %s\015\012", SERVER_SOFTWARE);
 | |
| 			(void) my_write(line, strlen(line), is_ssl);
 | |
| 		}
 | |
| 		if(strstr(headers, "Date:") == NULL) {
 | |
| 			char line[200];
 | |
| 			char timebuf[100];
 | |
| 			time_t now = time((time_t*) 0);
 | |
| 			const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S GMT";
 | |
| 			(void) strftime(timebuf, sizeof(timebuf), rfc1123_fmt, gmtime(&now));
 | |
| 			sprintf(line, "Date: %s\015\012", timebuf);
 | |
| 			(void) my_write(line, strlen(line), is_ssl);
 | |
| 			if(strstr(headers, "Expires:") == NULL) {
 | |
| 				sprintf(line, "Expires: %s\015\012", timebuf);
 | |
| 				(void) my_write(line, strlen(line), is_ssl);
 | |
| 			}
 | |
| 		} else if(strstr(headers, "Expires:") == NULL) {
 | |
| 			char* line = "Expires: Thu, 01 Jan 1970 00:00:00 GMT\015\012";
 | |
| 			(void) my_write(line, strlen(line), is_ssl);
 | |
| 		}
 | |
| 		(void) my_write(headers, headers_len, is_ssl);
 | |
| 	}
 | |
| 	for(;;) {
 | |
| 		r = read(rfd, buf, sizeof(buf));
 | |
| 		if(r < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 			sleep(1);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if(r <= 0) {
 | |
| 			goto done;
 | |
| 		}
 | |
| 		for(;;) {
 | |
| 			r2 = my_write(buf, r, is_ssl);
 | |
| 			if(r2 < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 				sleep(1);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if(r2 != r) {
 | |
| 				goto done;
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| done:
 | |
| 	shutdown(conn_fd, SHUT_WR);
 | |
| }
 | |
| 
 | |
| static void check_referer(int is_ssl) {
 | |
| 	char* cp;
 | |
| 
 | |
| 	if(url_pattern == (char*) 0) {
 | |
| 		return;
 | |
| 	}
 | |
| 	if(really_check_referer()) {
 | |
| 		return;
 | |
| 	}
 | |
| 	cp = hostname;
 | |
| 	if(cp == (char*) 0) {
 | |
| 		cp = "";
 | |
| 	}
 | |
| 	syslog(LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"", ntoa(&client_addr), cp, path, referer);
 | |
| 	send_error(403, "Forbidden", "", "You must supply a local referer.", is_ssl);
 | |
| }
 | |
| 
 | |
| static void clear_ndelay(int fd) {
 | |
| 	int flags, newflags;
 | |
| 
 | |
| 	flags = fcntl(fd, F_GETFL, 0);
 | |
| 	if(flags != -1) {
 | |
| 		newflags = flags & ~ (int) O_NDELAY;
 | |
| 		if(newflags != flags) {
 | |
| 			(void) fcntl(fd, F_SETFL, newflags);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void de_dotdot(char* file) {
 | |
| 	char* cp;
 | |
| 	char* cp2;
 | |
| 	int l;
 | |
| 
 | |
| 	while((cp = strstr(file, "//")) != (char*) 0) {
 | |
| 		for(cp2 = cp + 2; *cp2 == '/'; ++cp2) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		(void) strcpy(cp + 1, cp2);
 | |
| 	}
 | |
| 	while(strncmp(file, "./", 2) == 0) {
 | |
| 		(void) strcpy(file, file + 2);
 | |
| 	}
 | |
| 	while((cp = strstr( file, "/./")) != (char*) 0) {
 | |
| 		(void) strcpy(cp, cp + 2);
 | |
| 	}
 | |
| 	for(;;) {
 | |
| 		while(strncmp(file, "../", 3) == 0) {
 | |
| 			(void) strcpy( file, file + 3 );
 | |
| 		}
 | |
| 		cp = strstr(file, "/../");
 | |
| 		if(cp == (char*) 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		for(cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		(void) strcpy(cp2 + 1, cp + 4);
 | |
| 	}
 | |
| 	while((l = strlen(file)) > 3 && strcmp((cp = file + l - 3), "/..") == 0) {
 | |
| 		for(cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		if(cp2 < file) {
 | |
| 			break;
 | |
| 		}
 | |
| 		*cp2 = '\0';
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void do_cgi(int is_ssl, unsigned short conn_port) {
 | |
| 	char** argp;
 | |
| 	char** envp;
 | |
| 	int parse_headers;
 | |
| 	char* binary;
 | |
| 	char* directory;
 | |
| 
 | |
| 	if(method != METHOD_GET && method != METHOD_POST) {
 | |
| 		send_error(501, "Not Implemented", "", "That method is not implemented for CGI.", is_ssl);
 | |
| 	}
 | |
| 	if(conn_fd == STDIN_FILENO || conn_fd == STDOUT_FILENO || conn_fd == STDERR_FILENO) {
 | |
| 		int newfd = dup2(conn_fd, STDERR_FILENO + 1);
 | |
| 		if(newfd >= 0) {
 | |
| 			conn_fd = newfd;
 | |
| 		}
 | |
| 	}
 | |
| 	envp = make_envp(is_ssl, conn_port);
 | |
| 	argp = make_argp();
 | |
| 	if((method == METHOD_POST && request_len > request_idx) || is_ssl) {
 | |
| 		int p[2];
 | |
| 		int r;
 | |
| 		if(pipe(p) < 0) {
 | |
| 			send_error(500, "Internal Error", "", "Something unexpected went wrong making a pipe.", is_ssl);
 | |
| 		}
 | |
| 		r = fork();
 | |
| 		if(r < 0) {
 | |
| 			send_error(500, "Internal Error", "", "Something unexpected went wrong forking an interposer.", is_ssl);
 | |
| 		}
 | |
| 		if(r == 0) {
 | |
| 			(void) close(p[0]);
 | |
| 			cgi_interpose_input(p[1], is_ssl);
 | |
| 			exit(0);
 | |
| 		}
 | |
| 		(void) close(p[1]);
 | |
| 		if(p[0] != STDIN_FILENO) {
 | |
| 			(void) dup2(p[0], STDIN_FILENO);
 | |
| 			(void) close(p[0]);
 | |
| 		}
 | |
| 	} else {
 | |
| 		if(conn_fd != STDIN_FILENO) {
 | |
| 			(void) dup2(conn_fd, STDIN_FILENO);
 | |
| 		}
 | |
| 	}
 | |
| 	if(strncmp(argp[0], "nph-", 4) == 0) {
 | |
| 		parse_headers = 0;
 | |
| 	} else {
 | |
| 		parse_headers = 1;
 | |
| 	}
 | |
| 	if(parse_headers || is_ssl) {
 | |
| 		int p[2];
 | |
| 		int r;
 | |
| 		if(pipe(p) < 0) {
 | |
| 			send_error(500, "Internal Error", "", "Something unexpected went wrong making a pipe.", is_ssl);
 | |
| 		}
 | |
| 		r = fork();
 | |
| 		if(r < 0) {
 | |
| 			send_error(500, "Internal Error", "", "Something unexpected went wrong forking an interposer.", is_ssl);
 | |
| 		}
 | |
| 		if(r == 0) {
 | |
| 			(void) close(p[1]);
 | |
| 			cgi_interpose_output(p[0], parse_headers, is_ssl);
 | |
| 			exit(0);
 | |
| 		}
 | |
| 		(void) close(p[0]);
 | |
| 		if(p[1] != STDOUT_FILENO) {
 | |
| 			(void) dup2(p[1], STDOUT_FILENO);
 | |
| 		}
 | |
| 		if(p[1] != STDERR_FILENO) {
 | |
| 			(void) dup2(p[1], STDERR_FILENO);
 | |
| 		}
 | |
| 		if(p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO) {
 | |
| 			(void) close(p[1]);
 | |
| 		}
 | |
| 	} else {
 | |
| 		if(conn_fd != STDOUT_FILENO) {
 | |
| 			(void) dup2(conn_fd, STDOUT_FILENO);
 | |
| 		}
 | |
| 		if(conn_fd != STDERR_FILENO) {
 | |
| 			(void) dup2(conn_fd, STDERR_FILENO);
 | |
| 		}
 | |
| 	}
 | |
| 	if(logfp != (FILE*) 0) {
 | |
| 		(void) fclose(logfp);
 | |
| 	}
 | |
| 	closelog();
 | |
| 	if(nice(CGI_NICE) < 0) { ; }
 | |
| 	directory = e_strdup(file);
 | |
| 	binary = strrchr(directory, '/');
 | |
| 	if(binary == (char*) 0) {
 | |
| 		binary = file;
 | |
| 	} else {
 | |
| 		*binary++ = '\0';
 | |
| 		if(chdir(directory) < 0) { ; }
 | |
| 	}
 | |
| 	(void) signal(SIGPIPE, SIG_DFL);
 | |
| 	(void) execve(binary, argp, envp);
 | |
| 	send_error(500, "Internal Error", "", "Something unexpected went wrong running a CGI program.", is_ssl);
 | |
| }
 | |
| 
 | |
| static void do_dir(int is_ssl) {
 | |
| 	char buf[10000];
 | |
| 	size_t buflen;
 | |
| 	char* contents;
 | |
| 	size_t contents_size, contents_len;
 | |
| 	int n, i;
 | |
| 	struct dirent **dl;
 | |
| 	char* name_info;
 | |
| 
 | |
| 	if(pathinfo != (char*) 0) {
 | |
| 		send_error(404, "Not Found", "", "File not found.", is_ssl);
 | |
| 	}
 | |
| 	auth_check(file, is_ssl);
 | |
| 	check_referer(is_ssl);
 | |
| 	n = scandir(file, &dl, NULL, alphasort);
 | |
| 	if(n < 0) {
 | |
| 		syslog(LOG_INFO, "%.80s Directory \"%.80s\" is protected", ntoa(&client_addr), path);
 | |
| 		send_error(403, "Forbidden", "", "Directory is protected.", is_ssl);
 | |
| 	}
 | |
| 	contents_size = 0;
 | |
| 	buflen = snprintf(buf, sizeof(buf), "<html>\n<head><title>Index of %s</title></head>\n<body bgcolor=\"#FFFFFF\">\n<h1>Index of %s</h1><hr><pre>\n", file, file);
 | |
| 	add_to_buf(&contents, &contents_size, &contents_len, buf, buflen);
 | |
| 	for(i = 0; i < n; ++i) {
 | |
| 		if(strcmp(dl[i]->d_name, ".") == 0) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		name_info = file_details(file, dl[i]->d_name);
 | |
| 		add_to_buf(&contents, &contents_size, &contents_len, name_info, strlen(name_info));
 | |
| 	}
 | |
| 	buflen = snprintf(buf, sizeof(buf), "</pre><hr>\n%s\n</body>\n</html>\n", SERVER_SOFTWARE);
 | |
| 	add_to_buf(&contents, &contents_size, &contents_len, buf, buflen);
 | |
| 	add_headers(200, "Ok", "", "", "text/html; charset=%s", contents_len, sb.st_mtime);
 | |
| 	if(method != METHOD_HEAD) {
 | |
| 		add_to_response(contents, contents_len);
 | |
| 	}
 | |
| 	send_response(is_ssl);
 | |
| }
 | |
| 
 | |
| static void do_file(int is_ssl, unsigned short conn_port) {
 | |
| 	char buf[10000];
 | |
| 	char mime_encodings[500];
 | |
| 	const char* mime_type;
 | |
| 	char fixed_mime_type[500];
 | |
| 	char* cp;
 | |
| 	int fd;
 | |
| 
 | |
| 	(void) strncpy(buf, file, sizeof(buf));
 | |
| 	cp = strrchr(buf, '/');
 | |
| 	if(cp == (char*) 0) {
 | |
| 		(void) strcpy(buf, ".");
 | |
| 	} else {
 | |
| 		*cp = '\0';
 | |
| 	}
 | |
| 	auth_check(buf, is_ssl);
 | |
| 	if(strcmp(file, AUTH_FILE) == 0 || (strcmp(&(file[strlen(file) - sizeof(AUTH_FILE) + 1]), AUTH_FILE) == 0 && file[strlen(file) - sizeof(AUTH_FILE)] == '/')) {
 | |
| 		syslog(LOG_NOTICE, "%.80s URL \"%.80s\" tried to retrieve an auth file", ntoa(&client_addr), path);
 | |
| 		send_error(403, "Forbidden", "", "File is protected.", is_ssl);
 | |
| 	}
 | |
| 	if(defaultRealmName != NULL && defaultRealmPasswordFile != NULL) {
 | |
| 		if(strcmp(file, defaultRealmPasswordFile) == 0 || strcmp(&(file[strlen(file) - sizeof(defaultRealmPasswordFile) + 1]), defaultRealmPasswordFile) == 0 && file[strlen(file) - sizeof(defaultRealmPasswordFile)] == '/') {
 | |
| 			syslog(LOG_NOTICE, "%.80s URL \"%.80s\" tried to retrieve an auth file", ntoa(&client_addr), path);
 | |
| 			send_error(403, "Forbidden", "", "File is protected.", is_ssl);
 | |
| 		}
 | |
| 	}
 | |
| 	check_referer(is_ssl);
 | |
| 	if(cgi_pattern != (char*) 0 && match(cgi_pattern, file)) {
 | |
| 		do_cgi(is_ssl, conn_port);
 | |
| 		return;
 | |
| 	} else if(pathinfo != (char*) 0) {
 | |
| 		send_error(404, "Not Found", "", "File not found.", is_ssl);
 | |
| 	}
 | |
| 	fd = open(file, O_RDONLY);
 | |
| 	if(fd < 0) {
 | |
| 		syslog(LOG_INFO, "%.80s File \"%.80s\" is protected", ntoa(&client_addr), path);
 | |
| 		send_error(403, "Forbidden", "", "File is protected.", is_ssl);
 | |
| 	}
 | |
| 	mime_type = figure_mime(file, mime_encodings, sizeof(mime_encodings));
 | |
| 	(void) snprintf(fixed_mime_type, sizeof(fixed_mime_type), mime_type, charset);
 | |
| 	if(if_modified_since != (time_t) -1 && if_modified_since >= sb.st_mtime) {
 | |
| 		add_headers(304, "Not Modified", "", mime_encodings, fixed_mime_type, (off_t) -1, sb.st_mtime);
 | |
| 		send_response(is_ssl);
 | |
| 		return;
 | |
| 	}
 | |
| 	add_headers(200, "Ok", "", mime_encodings, fixed_mime_type, sb.st_size, sb.st_mtime);
 | |
| 	send_response(is_ssl);
 | |
| 	if(method == METHOD_HEAD) {
 | |
| 		return;
 | |
| 	}
 | |
| 	if(sb.st_size > 0) {
 | |
| 		send_via_write(fd, sb.st_size, is_ssl);
 | |
| 	}
 | |
| 	(void) close(fd);
 | |
| }
 | |
| 
 | |
| static void* e_malloc(size_t size) {
 | |
| 	void* ptr;
 | |
| 
 | |
| 	ptr = malloc(size);
 | |
| 	if(ptr == (void*) 0) {
 | |
| 		syslog(LOG_CRIT, "out of memory");
 | |
| 		(void) fprintf(stderr, "%s: out of memory\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	return ptr;
 | |
| }
 | |
| 
 | |
| static void* e_realloc(void* optr, size_t size) {
 | |
| 	void* ptr;
 | |
| 
 | |
| 	ptr = realloc(optr, size);
 | |
| 	if(ptr == (void*) 0) {
 | |
| 		syslog(LOG_CRIT, "out of memory");
 | |
| 		(void) fprintf(stderr, "%s: out of memory\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	return ptr;
 | |
| }
 | |
| 
 | |
| static char* e_strdup(char* ostr) {
 | |
| 	char* str;
 | |
| 
 | |
| 	str = strdup(ostr);
 | |
| 	if(str == (char*) 0) {
 | |
| 		syslog(LOG_CRIT, "out of memory copying a string");
 | |
| 		(void) fprintf(stderr, "%s: out of memory copying a string\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| static int ext_compare(struct mime_entry* a, struct mime_entry* b) {
 | |
| 	return strcmp(a->ext, b->ext);
 | |
| }
 | |
| 
 | |
| static const char* figure_mime(char* name, char* me, size_t me_size) {
 | |
| 	char* prev_dot;
 | |
| 	char* dot;
 | |
| 	char* ext;
 | |
| 	int me_indexes[100], n_me_indexes;
 | |
| 	size_t ext_len, me_len;
 | |
| 	int i, top, bot, mid;
 | |
| 	int r;
 | |
| 	const char* default_type = "application/octet-stream ";
 | |
| 	const char* type;
 | |
| 
 | |
| 	n_me_indexes = 0;
 | |
| 	for(prev_dot = &name[strlen(name)]; ; prev_dot = dot) {
 | |
| 		for(dot = prev_dot - 1; dot >= name && *dot != '.'; --dot);
 | |
| 		if(dot < name) {
 | |
| 			type = default_type;
 | |
| 			goto done;
 | |
| 		}
 | |
| 		ext = dot + 1;
 | |
| 		ext_len = prev_dot - ext;
 | |
| 		for(i = 0; i < n_enc_tab; ++i) {
 | |
| 			if(ext_len == enc_tab[i].ext_len && strncasecmp(ext, enc_tab[i].ext, ext_len) == 0) {
 | |
| 				if(n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes)) {
 | |
| 					me_indexes[n_me_indexes] = i;
 | |
| 					++n_me_indexes;
 | |
| 				}
 | |
| 				goto next;
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 		next: ;
 | |
| 	}
 | |
| 	top = n_typ_tab - 1;
 | |
| 	bot = 0;
 | |
| 	while(top >= bot) {
 | |
| 		mid = (top + bot) / 2;
 | |
| 		r = strncasecmp(ext, typ_tab[mid].ext, ext_len);
 | |
| 		if(r < 0) {
 | |
| 			top = mid - 1;
 | |
| 		} else if(r > 0) {
 | |
| 			bot = mid + 1;
 | |
| 		} else if(ext_len < typ_tab[mid].ext_len) {
 | |
| 			top = mid - 1;
 | |
| 		} else if(ext_len > typ_tab[mid].ext_len) {
 | |
| 			bot = mid + 1;
 | |
| 		} else {
 | |
| 			type = typ_tab[mid].val;
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 	type = default_type;
 | |
| 	done:
 | |
| 	me[0] = '\0';
 | |
| 	me_len = 0;
 | |
| 	for(i = n_me_indexes - 1; i >= 0; --i) {
 | |
| 		if(me_len + enc_tab[me_indexes[i]].val_len + 1 < me_size) {
 | |
| 			if(me[0] != '\0') {
 | |
| 				(void) strcpy(&me[me_len], ",");
 | |
| 				++me_len;
 | |
| 			}
 | |
| 			(void) strcpy(&me[me_len], enc_tab[me_indexes[i]].val);
 | |
| 			me_len += enc_tab[me_indexes[i]].val_len;
 | |
| 		}
 | |
| 	}
 | |
| 	return type;
 | |
| }
 | |
| 
 | |
| static char* file_details(const char* dir, const char* name) {
 | |
| 	struct stat sb;
 | |
| 	char f_time[20];
 | |
| 	static char encname[1000];
 | |
| 	static char buf[2000];
 | |
| 
 | |
| 	(void) snprintf(buf, sizeof(buf), "%s/%s", dir, name);
 | |
| 	if(lstat(buf, &sb) < 0)
 | |
| 	return "???";
 | |
| 	(void) strftime(f_time, sizeof(f_time), "%d-%b-%Y %H:%M", localtime(&sb.st_mtime));
 | |
| 	strencode(encname, sizeof(encname), name);
 | |
| 	(void) snprintf(buf, sizeof(buf), "<a href=\"%s\">%-50s</a>    %15s %14lld\n", encname, name, f_time, (long long int) sb.st_size);
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| static char* get_method_str(int m) {
 | |
| 	switch(m) {
 | |
| 		case METHOD_GET:
 | |
| 			return "GET";
 | |
| 		case METHOD_HEAD:
 | |
| 			return "HEAD";
 | |
| 		case METHOD_POST:
 | |
| 			return "POST";
 | |
| 		default:
 | |
| 			return "UNKNOWN";
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int get_pathinfo(void) {
 | |
| 	int r;
 | |
| 
 | |
| 	pathinfo = &file[strlen(file)];
 | |
| 	for(;;) {
 | |
| 		do {
 | |
| 			--pathinfo;
 | |
| 			if(pathinfo <= file) {
 | |
| 				pathinfo = (char*) 0;
 | |
| 				return -1;
 | |
| 			}
 | |
| 		} while(*pathinfo != '/');
 | |
| 		*pathinfo = '\0';
 | |
| 		r = stat(file, &sb);
 | |
| 		if(r >= 0) {
 | |
| 			++pathinfo;
 | |
| 			return r;
 | |
| 		} else {
 | |
| 			*pathinfo = '/';
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static char* get_request_line(void) {
 | |
| 	int i;
 | |
| 	char c;
 | |
| 
 | |
| 	for(i = request_idx; request_idx < request_len; ++request_idx) {
 | |
| 		c = request[request_idx];
 | |
| 		if(c == '\012' || c == '\015') {
 | |
| 			request[request_idx] = '\0';
 | |
| 			++request_idx;
 | |
| 			if(c == '\015' && request_idx < request_len && request[request_idx] == '\012') {
 | |
| 				request[request_idx] = '\0';
 | |
| 				++request_idx;
 | |
| 			}
 | |
| 			return &(request[i]);
 | |
| 		}
 | |
| 	}
 | |
| 	return (char*) 0;
 | |
| }
 | |
| 
 | |
| static void handle_read_timeout(int sig, int is_ssl) {
 | |
| 	syslog(LOG_INFO, "%.80s connection timed out reading", ntoa(&client_addr));
 | |
| 	send_error(408, "Request Timeout", "", "No request appeared within a reasonable time period.", is_ssl);
 | |
| }
 | |
| 
 | |
| static void handle_read_timeout_sig(int sig) {
 | |
| 	handle_read_timeout(0,0);
 | |
| }
 | |
| 
 | |
| static void handle_request(int is_ssl, unsigned short conn_port) {
 | |
| 	char* method_str;
 | |
| 	char* line;
 | |
| 	char* cp;
 | |
| 	int r, file_len, i;
 | |
| 	const char* index_names[] = {"default.html", "default.htm", "index.html", "index.htm", "index.cgi", "index.sh" };
 | |
| 
 | |
| 	(void) signal(SIGALRM, handle_read_timeout_sig);
 | |
| 	(void) alarm(READ_TIMEOUT);
 | |
| 
 | |
| 	remoteuser = (char*) 0;
 | |
| 	method = METHOD_UNKNOWN;
 | |
| 	path = (char*) 0;
 | |
| 	file = (char*) 0;
 | |
| 	pathinfo = (char*) 0;
 | |
| 	query = "";
 | |
| 	protocol = (char*) 0;
 | |
| 	status = 0;
 | |
| 	bytes = -1;
 | |
| 	authorization = (char*) 0;
 | |
| 	content_type = (char*) 0;
 | |
| 	content_length = -1;
 | |
| 	cookie = (char*) 0;
 | |
| 	host = (char*) 0;
 | |
| 	if_modified_since = (time_t) -1;
 | |
| 	referer = "";
 | |
| 	useragent = "";
 | |
| 
 | |
| #ifdef TCP_NOPUSH
 | |
| 	r = 1;
 | |
| 	(void) setsockopt(conn_fd, IPPROTO_TCP, TCP_NOPUSH, (void*) &r, sizeof(r));
 | |
| #endif
 | |
| 	if(is_ssl) {
 | |
| 		ssl = SSL_new(ssl_ctx);
 | |
| 		SSL_set_fd(ssl, conn_fd);
 | |
| 		int accept_ret = SSL_accept(ssl);
 | |
| 		if(accept_ret <= 0) {
 | |
| 			int e = SSL_get_error(ssl, accept_ret);
 | |
| 			if(e != VERSION_ERROR) {
 | |
| 				syslog(LOG_CRIT, "error: can't initialize ssl connection, error = %d\n", e);
 | |
| 			}
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	start_request();
 | |
| 	for(;;) {
 | |
| 		char buf[10000];
 | |
| 		int r = my_read( buf, sizeof(buf), is_ssl );
 | |
| 		if(r < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		if(r <= 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		(void) alarm(READ_TIMEOUT);
 | |
| 		add_to_request(buf, r);
 | |
| 		if(strstr(request, "\015\012\015\012") != (char*) 0 || strstr(request, "\012\012") != (char*) 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	method_str = get_request_line();
 | |
| 	if(method_str == (char*) 0) {
 | |
| 		send_error(400, "Bad Request", "", "Can't parse request.", is_ssl);
 | |
| 	}
 | |
| 	path = strpbrk(method_str, " \t\012\015");
 | |
| 	if(path == (char*) 0) {
 | |
| 		send_error(400, "Bad Request", "", "Can't parse request.", is_ssl);
 | |
| 	}
 | |
| 	*path++ = '\0';
 | |
| 	path += strspn(path, " \t\012\015");
 | |
| 	protocol = strpbrk(path, " \t\012\015");
 | |
| 	if(protocol == (char*) 0) {
 | |
| 		send_error(400, "Bad Request", "", "Can't parse request.", is_ssl);
 | |
| 	}
 | |
| 	*protocol++ = '\0';
 | |
| 	protocol += strspn(protocol, " \t\012\015");
 | |
| 	query = strchr(path, '?');
 | |
| 	if(query == (char*) 0) {
 | |
| 		query = "";
 | |
| 	} else {
 | |
| 		*query++ = '\0';
 | |
| 	}
 | |
| 	while((line = get_request_line()) != (char*) 0) {
 | |
| 		if(line[0] == '\0') {
 | |
| 			break;
 | |
| 		} else if(strncasecmp(line, "Authorization:", 14) == 0) {
 | |
| 			cp = &line[14];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			authorization = cp;
 | |
| 		} else if(strncasecmp(line, "Content-Length:", 15) == 0) {
 | |
| 			cp = &line[15];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			content_length = atol(cp);
 | |
| 		} else if(strncasecmp(line, "Content-Type:", 13) == 0) {
 | |
| 			cp = &line[13];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			content_type = cp;
 | |
| 		} else if(strncasecmp(line, "Cookie:", 7) == 0) {
 | |
| 			cp = &line[7];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			cookie = cp;
 | |
| 		} else if(strncasecmp(line, "Host:", 5) == 0) {
 | |
| 			cp = &line[5];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			host = cp;
 | |
| 			if(strchr(host, '/') != (char*) 0 || host[0] == '.') {
 | |
| 				send_error(400, "Bad Request", "", "Can't parse request.", is_ssl);
 | |
| 			}
 | |
| 		} else if(strncasecmp(line, "If-Modified-Since:", 18) == 0) {
 | |
| 			cp = &line[18];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			if_modified_since = dateparse(cp);
 | |
| 		} else if(strncasecmp(line, "Referer:", 8) == 0) {
 | |
| 			cp = &line[8];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			referer = cp;
 | |
| 		} else if(strncasecmp(line, "User-Agent:", 11) == 0) {
 | |
| 			cp = &line[11];
 | |
| 			cp += strspn(cp, " \t");
 | |
| 			useragent = cp;
 | |
| 		}
 | |
| 	}
 | |
| 	if(strcasecmp(method_str, get_method_str(METHOD_GET)) == 0) {
 | |
| 		method = METHOD_GET;
 | |
| 	} else if(strcasecmp(method_str, get_method_str(METHOD_HEAD)) == 0) {
 | |
| 		method = METHOD_HEAD;
 | |
| 	} else if(strcasecmp(method_str, get_method_str(METHOD_POST)) == 0) {
 | |
| 		method = METHOD_POST;
 | |
| 	} else {
 | |
| 		send_error(501, "Not Implemented", "", "That method is not implemented.", is_ssl);
 | |
| 	}
 | |
| 	strdecode(path, path);
 | |
| 	if(path[0] != '/') {
 | |
| 		send_error(400, "Bad Request", "", "Bad filename.", is_ssl);
 | |
| 	}
 | |
| 	file = &(path[1]);
 | |
| 	de_dotdot(file);
 | |
| 	if(file[0] == '\0') {
 | |
| 		file = "./";
 | |
| 	}
 | |
| 	if(file[0] == '/' || (file[0] == '.' && file[1] == '.' && (file[2] == '\0' || file[2] == '/'))) {
 | |
| 		send_error(400, "Bad Request", "", "Illegal filename.", is_ssl);
 | |
| 	}
 | |
| 
 | |
| 	(void) signal(SIGALRM, handle_write_timeout);
 | |
| 	(void) alarm(WRITE_TIMEOUT);
 | |
| 	r = stat(file, &sb);
 | |
| 	if(r < 0) {
 | |
| 		r = get_pathinfo();
 | |
| 	}
 | |
| 	if(r < 0) {
 | |
| 		if(pageNotFoundFile != NULL && host != NULL) {
 | |
| 			send_redirect("", host, pageNotFoundFile, is_ssl);
 | |
| 		} else {
 | |
| 			send_error(404, "Not Found", "", "File not found.", is_ssl);
 | |
| 		}
 | |
| 	}
 | |
| 	file_len = strlen( file );
 | |
| 	if(!S_ISDIR(sb.st_mode)) {
 | |
| 		while(file[file_len - 1] == '/') {
 | |
| 			file[file_len - 1] = '\0';
 | |
| 			--file_len;
 | |
| 		}
 | |
| 		do_file(is_ssl, conn_port);
 | |
| 	} else {
 | |
| 		char idx[10000];
 | |
| 		unsigned char found_index = 0;
 | |
| 		if(file[file_len - 1] != '/' && pathinfo == (char*) 0) {
 | |
| 			char location[10000];
 | |
| 			if(query[0] != '\0') {
 | |
| 				(void) snprintf(location, sizeof(location), "Location: %s/?%s", path, query);
 | |
| 			} else {
 | |
| 				(void) snprintf(location, sizeof(location), "Location: %s/", path);
 | |
| 				send_error(302, "Found", location, "Directories must end with a slash.", is_ssl);
 | |
| 			}
 | |
| 		}
 | |
| 		if(defaultPageFile != NULL) {
 | |
| 			(void) snprintf(idx, sizeof(idx), "%s%s", file, defaultPageFile);
 | |
| 			if(stat( idx, &sb ) >= 0) {
 | |
| 				file = idx;
 | |
| 				do_file(is_ssl, conn_port);
 | |
| 				found_index = 1;
 | |
| 			}
 | |
| 		}
 | |
| 		for(i = 0; i < (sizeof(index_names) / sizeof(char*)) && found_index == 0; ++i) {
 | |
| 			(void) snprintf(idx, sizeof(idx), "%s%s", file, index_names[i]);
 | |
| 			if(stat(idx, &sb) >= 0) {
 | |
| 				file = idx;
 | |
| 				do_file(is_ssl, conn_port);
 | |
| 				found_index = 1;
 | |
| 			}
 | |
| 		}
 | |
| 		if(found_index == 0) {
 | |
| 			if(allowDirectoryListing == 0) {
 | |
| 				if(pageNotFoundFile != NULL && host != NULL) {
 | |
| 					send_redirect("", host, pageNotFoundFile, is_ssl);
 | |
| 				} else {
 | |
| 					send_error(404, "Not Found", "", "File not found.", is_ssl);
 | |
| 				}
 | |
| 			} else {
 | |
| 				do_dir(is_ssl);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if(is_ssl) {
 | |
| 		SSL_free(ssl);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void handle_sigchld(int sig) {
 | |
| 	const int oerrno = errno;
 | |
| 	pid_t pid;
 | |
| 	int status;
 | |
| 
 | |
| 	(void) signal(SIGCHLD, handle_sigchld);
 | |
| 	for(;;) {
 | |
| 		pid = waitpid((pid_t) -1, &status, WNOHANG);
 | |
| 		if((int) pid == 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		if((int) pid < 0) {
 | |
| 			if(errno == EINTR || errno == EAGAIN) {
 | |
| 				continue;
 | |
| 			}
 | |
| 			if(errno != ECHILD) {
 | |
| 				syslog(LOG_ERR, "child wait - %m");
 | |
| 				perror("child wait");
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	errno = oerrno;
 | |
| }
 | |
| 
 | |
| static void handle_sighup(int sig) {
 | |
| 	const int oerrno = errno;
 | |
| 
 | |
| 	(void) signal(SIGHUP, handle_sighup);
 | |
| 	got_hup = 1;
 | |
| 	errno = oerrno;
 | |
| }
 | |
| 
 | |
| static void handle_sigterm(int sig) {
 | |
| 	syslog(LOG_NOTICE, "exiting due to signal %d", sig);
 | |
| 	(void) fprintf(stderr, "%s: exiting due to signal %d\n", argv0, sig);
 | |
| 	closelog();
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| static void handle_write_timeout(int sig) {
 | |
| 	syslog(LOG_INFO, "%.80s connection timed out writing", ntoa(&client_addr));
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| static int hexit(char c) {
 | |
| 	if(c >= '0' && c <= '9') {
 | |
| 		return c - '0';
 | |
| 	}
 | |
| 	if(c >= 'a' && c <= 'f') {
 | |
| 		return c - 'a' + 10;
 | |
| 	}
 | |
| 	if(c >= 'A' && c <= 'F') {
 | |
| 		return c - 'A' + 10;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void init_mime(void) {
 | |
| 	int i;
 | |
| 
 | |
| 	qsort(enc_tab, n_enc_tab, sizeof(*enc_tab), (int(*)(const void*, const void*)) ext_compare);
 | |
| 	qsort(typ_tab, n_typ_tab, sizeof(*typ_tab), (int(*)(const void*, const void*)) ext_compare);
 | |
| 
 | |
| 	for(i = 0; i < n_enc_tab; ++i) {
 | |
| 		enc_tab[i].ext_len = strlen(enc_tab[i].ext);
 | |
| 		enc_tab[i].val_len = strlen(enc_tab[i].val);
 | |
| 	}
 | |
| 	for(i = 0; i < n_typ_tab; ++i) {
 | |
| 		typ_tab[i].ext_len = strlen(typ_tab[i].ext);
 | |
| 		typ_tab[i].val_len = strlen(typ_tab[i].val);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int initialize_listen_socket(usockaddr* usaP) {
 | |
| 	int listen_fd;
 | |
| 	int i;
 | |
| 
 | |
| 	if(!sockaddr_check(usaP)) {
 | |
| 		syslog(LOG_ERR, "unknown sockaddr family on listen socket - %d", usaP->sa.sa_family);
 | |
| 		(void) fprintf(stderr, "%s: unknown sockaddr family on listen socket - %d\n", argv0, usaP->sa.sa_family);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	listen_fd = socket(usaP->sa.sa_family, SOCK_STREAM, 0);
 | |
| 	if(listen_fd < 0) {
 | |
| 		if(usaP->sa.sa_family == AF_INET6 && (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT || errno == ESOCKTNOSUPPORT || EPFNOSUPPORT || EAFNOSUPPORT)) {
 | |
| 			/* IPv6 not compiled into kernel, no big deal, don't print errors */
 | |
| 		} else {
 | |
| 			syslog(LOG_CRIT, "socket %.80s - %m", ntoa(usaP));
 | |
| 			perror("socket");
 | |
| 		}
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	(void) fcntl(listen_fd, F_SETFD, 1);
 | |
| 	i = 1;
 | |
| 	if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &i, sizeof(i)) < 0) {
 | |
| 		syslog(LOG_CRIT, "setsockopt SO_REUSEADDR - %m");
 | |
| 		perror("setsockopt SO_REUSEADDR");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if(bind(listen_fd, &usaP->sa, sockaddr_len(usaP)) < 0) {
 | |
| 		syslog(LOG_CRIT, "bind %.80s - %m", ntoa(usaP));
 | |
| 		perror("bind");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if(listen(listen_fd, 1024) < 0) {
 | |
| 		syslog(LOG_CRIT, "listen - %m");
 | |
| 		perror("listen");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	return listen_fd;
 | |
| }
 | |
| 
 | |
| static void lookup_hostname(usockaddr* usa4P, usockaddr* usa4sP, size_t sa4_len, int* gotv4P, int* gotv4sP, usockaddr* usa6P, usockaddr* usa6sP, size_t sa6_len, int* gotv6P, int* gotv6sP) {
 | |
| 	int port_index;
 | |
| 	unsigned short port_list[4];
 | |
| 	port_list[0] = port;
 | |
| 	port_list[1] = sslPort;
 | |
| 	port_list[2] = 0;
 | |
| 
 | |
| 	*gotv6P  = 0;
 | |
| 	*gotv6sP = 0;
 | |
| 	*gotv4P  = 0;
 | |
| 	*gotv4sP = 0;
 | |
| 	for(port_index=0; port_list[port_index] != 0; port_index++) {
 | |
| 		struct addrinfo hints;
 | |
| 		char portstr[10];
 | |
| 		int gaierr;
 | |
| 		struct addrinfo* ai;
 | |
| 		struct addrinfo* ai2;
 | |
| 		struct addrinfo* aiv6;
 | |
| 		struct addrinfo* aiv4;
 | |
| 		(void) memset(&hints, 0, sizeof(hints));
 | |
| 		hints.ai_family = PF_UNSPEC;
 | |
| 		hints.ai_flags = AI_PASSIVE;
 | |
| 		hints.ai_socktype = SOCK_STREAM;
 | |
| 		(void) snprintf(portstr, sizeof(portstr), "%d", (int) port_list[port_index]);
 | |
| 		if((gaierr = getaddrinfo(hostname, portstr, &hints, &ai)) != 0) {
 | |
| 			syslog(LOG_CRIT, "getaddrinfo %.80s - %s", hostname, gai_strerror(gaierr));
 | |
| 			(void) fprintf(stderr, "%s: getaddrinfo %.80s - %s\n", argv0, hostname, gai_strerror(gaierr));
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		aiv6 = (struct addrinfo*) 0;
 | |
| 		aiv4 = (struct addrinfo*) 0;
 | |
| 		for(ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next) {
 | |
| 			switch(ai2->ai_family) {
 | |
| 				case AF_INET6:
 | |
| 					if(aiv6 == (struct addrinfo*) 0) {
 | |
| 						aiv6 = ai2;
 | |
| 					}
 | |
| 					break;
 | |
| 				case AF_INET:
 | |
| 					if(aiv4 == (struct addrinfo*) 0) {
 | |
| 						aiv4 = ai2;
 | |
| 					}
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 		if(aiv6 != (struct addrinfo*) 0) {
 | |
| 			if(sa6_len < aiv6->ai_addrlen) {
 | |
| 				syslog(LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", hostname, (unsigned long) sa6_len, (unsigned long) aiv6->ai_addrlen);
 | |
| 				(void) fprintf(stderr, "%s: %.80s - sockaddr too small (%lu < %lu)\n", argv0, hostname, (unsigned long) sa6_len, (unsigned long) aiv6->ai_addrlen);
 | |
| 				exit(1);
 | |
| 			}
 | |
| 			if(port_index==0) {
 | |
| 				*gotv6P = 1;
 | |
| 				(void) memset(usa6P, 0, sa6_len);
 | |
| 				(void) memmove(usa6P, aiv6->ai_addr, aiv6->ai_addrlen);
 | |
| 			} else {
 | |
| 				*gotv6sP = 1;
 | |
| 				(void) memset(usa6sP, 0, sa6_len);
 | |
| 				(void) memmove(usa6sP, aiv6->ai_addr, aiv6->ai_addrlen);
 | |
| 			}
 | |
| 		}
 | |
| 		if(aiv4 != (struct addrinfo*) 0) {
 | |
| 			if(sa4_len < aiv4->ai_addrlen) {
 | |
| 				syslog(LOG_CRIT, "%.80s - sockaddr too small (%lu < %lu)", hostname, (unsigned long) sa4_len, (unsigned long) aiv4->ai_addrlen);
 | |
| 				(void) fprintf(stderr, "%s: %.80s - sockaddr too small (%lu < %lu)\n", argv0, hostname, (unsigned long) sa4_len, (unsigned long) aiv4->ai_addrlen);
 | |
| 				exit(1);
 | |
| 			}
 | |
| 			if(port_index == 0) {
 | |
| 				*gotv4P = 1;
 | |
| 				(void) memset(usa4P, 0, sa4_len);
 | |
| 				(void) memmove(usa4P, aiv4->ai_addr, aiv4->ai_addrlen);
 | |
| 			} else {
 | |
| 				*gotv4sP = 1;
 | |
| 				(void) memset(usa4sP, 0, sa4_len);
 | |
| 				(void) memmove(usa4sP, aiv4->ai_addr, aiv4->ai_addrlen);
 | |
| 			}
 | |
| 			*gotv4P = 1;
 | |
| 		}
 | |
| 		freeaddrinfo(ai);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int main(int argc, char** argv) {
 | |
| 	int argn;
 | |
| 	struct passwd* pwd;
 | |
| 	uid_t uid = 32767;
 | |
| 	gid_t gid = 32767;
 | |
| 	usockaddr host_addr4;
 | |
| 	usockaddr host_addr6;
 | |
| 	int gotv4, gotv4s, gotv6, gotv6s;
 | |
| 	fd_set lfdset;
 | |
| 	int maxfd;
 | |
| 	usockaddr usa;
 | |
| 	int sz, r;
 | |
| 	char* cp;
 | |
| 	usockaddr host_addr4s;
 | |
| 	usockaddr host_addr6s;
 | |
| 
 | |
| 	argv0 = argv[0];
 | |
| 	debug = 0;
 | |
| 	port = 0;
 | |
| 	dir = (char*) 0;
 | |
| 	data_dir = (char*) 0;
 | |
| 	cgi_pattern = DEFAULT_CGI_PATTERN;
 | |
| 	url_pattern = (char*) 0;
 | |
| 	no_empty_referers = 0;
 | |
| 	local_pattern = (char*) 0;
 | |
| 	charset = DEFAULT_CHARSET;
 | |
| 	language = DEFAULT_LANGUAGE;
 | |
| 	p3p = (char*) 0;
 | |
| 	max_age = -1;
 | |
| 	user = NULL;
 | |
| 	hostname = (char*) 0;
 | |
| 	logfile = (char*) 0;
 | |
| 	pidfile = (char*) 0;
 | |
| 	logfp = (FILE*) 0;
 | |
| 	do_ssl = 0;
 | |
| 	certfile = DEFAULT_CERTFILE;
 | |
| 	cipher = (char*) 0;
 | |
| 
 | |
| 	defaultRealmName = NULL;
 | |
| 	defaultRealmPasswordFile = NULL;
 | |
| 	defaultPageFile = NULL;
 | |
| 	pageNotFoundFile = NULL;
 | |
| 	allowDirectoryListing = 1;
 | |
| 	sslPort = 0;
 | |
| 	argn = 1;
 | |
| 
 | |
| 	while(argn < argc && argv[argn][0] == '-') {
 | |
| 		if(strcmp( argv[argn], "-V" ) == 0) {
 | |
| 			(void) printf("%s\n", SERVER_SOFTWARE);
 | |
| 			exit(0);
 | |
| 		} else if(strcmp(argv[argn], "-C") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			read_config(argv[argn]);
 | |
| 		} else if(strcmp(argv[argn], "-D") == 0) {
 | |
| 			debug = 1;
 | |
| 		} else if(strcmp(argv[argn], "-S") == 0) {
 | |
| 			do_ssl = 1;
 | |
| 		} else if(strcmp(argv[argn], "-E") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			certfile = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-Y") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			cipher = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-p") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			port = (unsigned short) atoi(argv[argn]);
 | |
| 		} else if(strcmp(argv[argn], "-d") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			dir = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-dd") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			data_dir = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-c") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			cgi_pattern = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-u") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			user = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-h") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			hostname = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-l") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			logfile = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-i") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			pidfile = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-T") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			charset = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-L") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			language = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-P") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			p3p = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-M") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			max_age = atoi(argv[argn]);
 | |
| 		} else if(strcmp(argv[argn], "-DRN") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			defaultRealmName = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-DRP") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			defaultRealmPasswordFile = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-DPF") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			defaultPageFile = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-PNF") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			pageNotFoundFile = argv[argn];
 | |
| 		} else if(strcmp(argv[argn], "-ADL") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			if(strcmp(argv[argn], "1") == 0 || strcmp(argv[argn], "true") == 0 || strcmp(argv[argn], "TRUE") == 0 || strcmp(argv[argn], "yes") == 0 || strcmp(argv[argn], "YES") == 0) {
 | |
| 				allowDirectoryListing = 1;
 | |
| 			} else {
 | |
| 				allowDirectoryListing = 0;
 | |
| 			}
 | |
| 		} else if(strcmp(argv[argn], "-SP") == 0 && argn + 1 < argc) {
 | |
| 			++argn;
 | |
| 			sslPort = (unsigned short) atoi(argv[argn]);
 | |
| 		} else {
 | |
| 			usage();
 | |
| 		}
 | |
| 		++argn;
 | |
| 	}
 | |
| 	if(argn != argc) {
 | |
| 		usage();
 | |
| 	}
 | |
| 
 | |
| 	cp = strrchr(argv0, '/');
 | |
| 	if(cp != (char*) 0) {
 | |
| 		++cp;
 | |
| 	} else {
 | |
| 		cp = argv0;
 | |
| 	}
 | |
| 	openlog(cp, LOG_NDELAY|LOG_PID, LOG_DAEMON);
 | |
| 
 | |
| 	if(port == 0) {
 | |
| 		if(sslPort != 0 && do_ssl) {
 | |
| 			port = sslPort;
 | |
| 			sslPort = 0;
 | |
| 		}
 | |
| 		if(do_ssl) {
 | |
| 			port = DEFAULT_HTTPS_PORT;
 | |
| 		} else {
 | |
| 			port = DEFAULT_HTTP_PORT;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if(getuid() == 0 && user != NULL) {
 | |
| 		pwd = getpwnam(user);
 | |
| 		if(pwd == (struct passwd*) 0) {
 | |
| 			syslog(LOG_CRIT, "unknown user - '%s'", user);
 | |
| 			(void) fprintf(stderr, "%s: unknown user - '%s'\n", argv0, user);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		uid = pwd->pw_uid;
 | |
| 		gid = pwd->pw_gid;
 | |
| 	}
 | |
| 
 | |
| 	if(logfile != (char*) 0) {
 | |
| 		logfp = fopen(logfile, "a");
 | |
| 		if(logfp == (FILE*) 0) {
 | |
| 			syslog(LOG_CRIT, "%s - %m", logfile);
 | |
| 			perror(logfile);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if(logfile[0] != '/') {
 | |
| 			syslog(LOG_WARNING, "logfile is not an absolute path, you may not be able to re-open it");
 | |
| 			(void) fprintf(stderr, "%s: logfile is not an absolute path, you may not be able to re-open it\n", argv0);
 | |
| 		}
 | |
| 		if(getuid() == 0) {
 | |
| 			if(fchown(fileno(logfp), uid, gid) < 0) {
 | |
| 				syslog(LOG_WARNING, "fchown logfile - %m");
 | |
| 				perror("fchown logfile");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
|     lookup_hostname(&host_addr4, &host_addr4s, sizeof(struct sockaddr_in),  &gotv4, &gotv4s, &host_addr6, &host_addr6s, sizeof(struct sockaddr_in6), &gotv6, &gotv6s);
 | |
|     if(hostname == (char*) 0) {
 | |
| 		(void) gethostname(hostname_buf, sizeof(hostname_buf));
 | |
| 		hostname = hostname_buf;
 | |
| 	}
 | |
| 	if(!( gotv4 || gotv4s || gotv6 || gotv6s)) {
 | |
| 		syslog(LOG_CRIT, "can't find any valid address");
 | |
| 		(void) fprintf(stderr, "%s: can't find any valid address\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
|     listen6_fd  = -1;
 | |
|     listen6s_fd = -1;
 | |
|     listen4_fd  = -1;
 | |
|     listen4s_fd = -1;
 | |
|     if(gotv6) {
 | |
| 		listen6_fd = initialize_listen_socket(&host_addr6);
 | |
| 	}
 | |
| 	if(gotv6s) {
 | |
| 		listen6s_fd = initialize_listen_socket(&host_addr6s);
 | |
| 	}
 | |
| 	if(gotv4) {
 | |
| 		listen4_fd = initialize_listen_socket(&host_addr4);
 | |
| 	}
 | |
| 	if(gotv4s) {
 | |
| 		listen4s_fd = initialize_listen_socket(&host_addr4s);
 | |
| 	}
 | |
| 
 | |
|     if(listen4_fd == -1 && listen6_fd == -1 && listen4s_fd == -1 && listen6s_fd == -1) {
 | |
| 		syslog(LOG_CRIT, "can't bind to any address");
 | |
| 		(void) fprintf(stderr, "%s: can't bind to any address\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	if(do_ssl) {
 | |
| 		ssl_ctx = SSL_CTX_new(TLSv1_server_method());
 | |
| 		SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, 0);
 | |
| 		if(certfile[0] != '\0') {
 | |
| 			if(SSL_CTX_use_certificate_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) != SSL_SUCCESS || SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) != SSL_SUCCESS) {
 | |
| 				syslog(LOG_CRIT, "can't load certificate and/or private key\n");
 | |
| 				exit(1);
 | |
| 			}
 | |
| 		}
 | |
| 		if(cipher != (char*) 0) {
 | |
| 			if(SSL_CTX_set_cipher_list(ssl_ctx, cipher) <= 0) {
 | |
| 				syslog(LOG_CRIT, "can't load certificate and/or private key\n");
 | |
| 				exit(1);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if(!debug) {
 | |
| 		if(daemon(1, 0) < 0) {
 | |
| 			syslog(LOG_CRIT, "daemon - %m");
 | |
| 			perror("daemon");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	} else {
 | |
| 		(void) setsid();
 | |
| 	}
 | |
| 
 | |
| 	if(pidfile != (char*) 0) {
 | |
| 		FILE* pidfp = fopen(pidfile, "w");
 | |
| 		if(pidfp == (FILE*) 0) {
 | |
| 			syslog(LOG_CRIT, "%s - %m", pidfile);
 | |
| 			perror(pidfile);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		(void) fprintf(pidfp, "%d\n", (int) getpid());
 | |
| 		(void) fclose(pidfp);
 | |
| 	}
 | |
| 
 | |
| 	tzset();
 | |
| 
 | |
| 	if(getuid() == 0 && user != NULL) {
 | |
| 		if(setgroups(0, (gid_t*) 0) < 0) {
 | |
| 			syslog(LOG_CRIT, "setgroups - %m");
 | |
| 			perror("setgroups");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if(setgid(gid) < 0) {
 | |
| 			syslog(LOG_CRIT, "setgid - %m");
 | |
| 			perror("setgid");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if(initgroups(user, gid ) < 0) {
 | |
| 			syslog(LOG_ERR, "initgroups - %m");
 | |
| 			perror("initgroups");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if(dir != (char*) 0) {
 | |
| 		if(chdir(dir) < 0) {
 | |
| 			syslog(LOG_CRIT, "chdir - %m");
 | |
| 			perror("chdir");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	if(getcwd(cwd, sizeof(cwd) - 1) == NULL) { ; }
 | |
| 	if(cwd[strlen(cwd) - 1] != '/') {
 | |
| 		(void) strcat( cwd, "/" );
 | |
| 	}
 | |
| 	if(data_dir != (char*) 0) {
 | |
| 		if(chdir(data_dir) < 0) {
 | |
| 			syslog(LOG_CRIT, "data_dir chdir - %m");
 | |
| 			perror("data_dir chdir");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	if(getuid() == 0 && user != NULL) {
 | |
| 		if(setuid(uid) < 0) {
 | |
| 			syslog(LOG_CRIT, "setuid - %m");
 | |
| 			perror("setuid");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	(void) signal(SIGTERM, handle_sigterm);
 | |
| 	(void) signal(SIGINT, handle_sigterm);
 | |
| 	(void) signal(SIGUSR1, handle_sigterm);
 | |
| 	(void) signal(SIGHUP, handle_sighup);
 | |
| 	(void) signal(SIGCHLD, handle_sigchld);
 | |
| 	(void) signal(SIGPIPE, SIG_IGN);
 | |
| 	got_hup = 0;
 | |
| 	init_mime();
 | |
| 
 | |
| 	if(hostname == (char*) 0) {
 | |
| 		syslog(LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE, (int) port);
 | |
| 	} else {
 | |
| 		syslog(LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE, hostname, (int) port);
 | |
| 	}
 | |
| 
 | |
| 	for(;;) {
 | |
| 		int is_ssl;
 | |
| 		unsigned short conn_port;
 | |
| 		if(got_hup) {
 | |
| 			reopen_logfile();
 | |
| 			got_hup = 0;
 | |
| 		}
 | |
| 		FD_ZERO(&lfdset);
 | |
| 		maxfd = -1;
 | |
| 		if(listen4_fd != -1) {
 | |
| 			FD_SET(listen4_fd, &lfdset);
 | |
| 			if(listen4_fd > maxfd) {
 | |
| 				maxfd = listen4_fd;
 | |
| 			}
 | |
| 		}
 | |
| 		if(listen4s_fd != -1) {
 | |
| 			FD_SET(listen4s_fd, &lfdset);
 | |
| 			if(listen4s_fd > maxfd) {
 | |
| 				maxfd = listen4s_fd;
 | |
| 			}
 | |
| 		}
 | |
| 		if(listen6_fd != -1) {
 | |
| 			FD_SET(listen6_fd, &lfdset);
 | |
| 			if(listen6_fd > maxfd) {
 | |
| 				maxfd = listen6_fd;
 | |
| 			}
 | |
| 		}
 | |
| 		if(listen6s_fd != -1) {
 | |
| 			FD_SET(listen6s_fd, &lfdset);
 | |
| 			if(listen6s_fd > maxfd) {
 | |
| 				maxfd = listen6s_fd;
 | |
| 			}
 | |
| 		}
 | |
| 		if(select(maxfd + 1, &lfdset, (fd_set*) 0, (fd_set*) 0, (struct timeval*) 0) < 0) {
 | |
| 			if(errno == EINTR || errno == EAGAIN) {
 | |
| 				continue;
 | |
| 			}
 | |
| 			syslog(LOG_CRIT, "select - %m");
 | |
| 			perror("select");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		is_ssl= 0;
 | |
| 		conn_port = port;
 | |
| 		if(do_ssl) {
 | |
| 			if(sslPort == 0) {
 | |
| 				is_ssl= 1;
 | |
| 			} else if(listen4s_fd != -1 && FD_ISSET(listen4s_fd, &lfdset)) {
 | |
| 				is_ssl = 1;
 | |
| 				conn_port = sslPort;
 | |
| 			} else if(listen6s_fd != -1 && FD_ISSET(listen6s_fd, &lfdset)) {
 | |
| 				is_ssl = 1;
 | |
| 				conn_port = sslPort;
 | |
| 			}
 | |
| 		}
 | |
| 		sz = sizeof(usa);
 | |
| 		if(listen4_fd != -1 && FD_ISSET(listen4_fd, &lfdset)) {
 | |
| 			conn_fd = accept(listen4_fd, &usa.sa, &sz);
 | |
| 		} else if(listen4s_fd != -1 && FD_ISSET(listen4s_fd, &lfdset)) {
 | |
| 			conn_fd = accept(listen4s_fd, &usa.sa, &sz);
 | |
| 		} else if(listen6_fd != -1 && FD_ISSET(listen6_fd, &lfdset)) {
 | |
| 			conn_fd = accept(listen6_fd, &usa.sa, &sz);
 | |
| 		} else if(listen6s_fd != -1 && FD_ISSET(listen6s_fd, &lfdset)) {
 | |
| 			conn_fd = accept(listen6s_fd, &usa.sa, &sz);
 | |
| 		} else {
 | |
| 			syslog(LOG_CRIT, "select failure");
 | |
| 			(void) fprintf(stderr, "%s: select failure\n", argv0);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if(conn_fd < 0) {
 | |
| 			if(errno == EINTR || errno == EAGAIN) {
 | |
| 				continue;
 | |
| 			}
 | |
| #ifdef EPROTO
 | |
| 			if(errno == EPROTO) {
 | |
| 				continue;
 | |
| 			}
 | |
| #endif
 | |
| 			syslog(LOG_CRIT, "accept - %m");
 | |
| 			perror("accept");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		r = fork();
 | |
| 		if(r < 0) {
 | |
| 			syslog(LOG_CRIT, "fork - %m");
 | |
| 			perror("fork");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if(r == 0) {
 | |
| 			client_addr = usa;
 | |
| 			if(listen4_fd != -1)  {
 | |
| 				(void) close(listen4_fd);
 | |
| 			}
 | |
| 			if(listen4s_fd != -1) {
 | |
| 				(void) close(listen4s_fd);
 | |
| 			}
 | |
| 			if(listen6_fd != -1) {
 | |
| 				(void) close(listen6_fd);
 | |
| 			}
 | |
| 			if(listen6s_fd != -1) {
 | |
| 				(void) close(listen6s_fd);
 | |
| 			}
 | |
| 			handle_request(is_ssl, conn_port);
 | |
| 			exit(0);
 | |
| 		}
 | |
| 		(void) close(conn_fd);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static char** make_argp(void) {
 | |
| 	char** argp;
 | |
| 	int argn;
 | |
| 	char* cp1;
 | |
| 	char* cp2;
 | |
| 
 | |
| 	argp = (char**) malloc((strlen(query) + 2) * sizeof(char*));
 | |
| 	if(argp == NULL) {
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	argp[0] = strrchr(file, '/');
 | |
| 	if(argp[0] != NULL) {
 | |
| 		++argp[0];
 | |
| 	} else {
 | |
| 		argp[0] = file;
 | |
| 	}
 | |
| 	argn = 1;
 | |
| 	if(strchr(query, '=') == NULL) {
 | |
| 		for(cp1 = cp2 = query; *cp2 != '\0'; ++cp2) {
 | |
| 			if(*cp2 == '+') {
 | |
| 				*cp2 = '\0';
 | |
| 				strdecode(cp1, cp1);
 | |
| 				argp[argn++] = cp1;
 | |
| 				cp1 = cp2 + 1;
 | |
| 			}
 | |
| 		}
 | |
| 		if(cp2 != cp1) {
 | |
| 			strdecode(cp1, cp1);
 | |
| 			argp[argn++] = cp1;
 | |
| 		}
 | |
| 	}
 | |
| 	argp[argn] = (char*) 0;
 | |
| 	return argp;
 | |
| }
 | |
| 
 | |
| static char** make_envp(int is_ssl, unsigned short conn_port) {
 | |
| 	static char* envp[50];
 | |
| 	int envn;
 | |
| 	char* cp;
 | |
| 	char buf[256];
 | |
| 
 | |
| 	envn = 0;
 | |
| 	envp[envn++] = build_env("PATH=%s", CGI_PATH);
 | |
| 	envp[envn++] = build_env("LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH);
 | |
| 	envp[envn++] = build_env("SERVER_SOFTWARE=%s", SERVER_SOFTWARE);
 | |
| 	envp[envn++] = build_env("WWW_LANGUAGE=%s", language);
 | |
| 	cp = hostname;
 | |
| 	if(cp != (char*) 0) {
 | |
| 		envp[envn++] = build_env("SERVER_NAME=%s", cp);
 | |
| 	}
 | |
| 	envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
 | |
| 	envp[envn++] = "SERVER_PROTOCOL=HTTP/1.0";
 | |
| 	(void) snprintf(buf, sizeof(buf), "%d", (int) conn_port);
 | |
| 	envp[envn++] = build_env("SERVER_PORT=%s", buf);
 | |
| 	envp[envn++] = build_env("REQUEST_METHOD=%s", get_method_str(method));
 | |
| 	envp[envn++] = build_env("SCRIPT_NAME=%s", path);
 | |
| 	if(pathinfo != (char*) 0) {
 | |
| 		envp[envn++] = build_env("PATH_INFO=/%s", pathinfo);
 | |
| 		(void) snprintf(buf, sizeof(buf), "%s%s", cwd, pathinfo);
 | |
| 		envp[envn++] = build_env("PATH_TRANSLATED=%s", buf);
 | |
| 	}
 | |
| 	if(query[0] != '\0') {
 | |
| 		envp[envn++] = build_env("QUERY_STRING=%s", query);
 | |
| 	}
 | |
| 	envp[envn++] = build_env("REMOTE_ADDR=%s", ntoa(&client_addr));
 | |
| 	if(referer[0] != '\0') {
 | |
| 		envp[envn++] = build_env("HTTP_REFERER=%s", referer);
 | |
| 	}
 | |
| 	if(useragent[0] != '\0') {
 | |
| 		envp[envn++] = build_env("HTTP_USER_AGENT=%s", useragent);
 | |
| 	}
 | |
| 	if(cookie != (char*) 0) {
 | |
| 		envp[envn++] = build_env("HTTP_COOKIE=%s", cookie);
 | |
| 	}
 | |
| 	if(host != (char*) 0) {
 | |
| 		envp[envn++] = build_env("HTTP_HOST=%s", host);
 | |
| 	}
 | |
| 	if(content_type != (char*) 0) {
 | |
| 		envp[envn++] = build_env("CONTENT_TYPE=%s", content_type);
 | |
| 	}
 | |
| 	if(content_length != -1) {
 | |
| 		(void) snprintf(buf, sizeof(buf), "%lu", (unsigned long) content_length);
 | |
| 		envp[envn++] = build_env("CONTENT_LENGTH=%s", buf);
 | |
| 	}
 | |
| 	if(remoteuser != (char*) 0) {
 | |
| 		envp[envn++] = build_env("REMOTE_USER=%s", remoteuser);
 | |
| 	}
 | |
| 	if(authorization != (char*) 0) {
 | |
| 		envp[envn++] = build_env("AUTH_TYPE=%s", "Basic");
 | |
| 	}
 | |
| 	if(getenv("TZ") != (char*) 0) {
 | |
| 		envp[envn++] = build_env("TZ=%s", getenv("TZ"));
 | |
| 	}
 | |
| 	envp[envn] = (char*) 0;
 | |
| 	return envp;
 | |
| }
 | |
| 
 | |
| static void make_log_entry(void) {
 | |
| 	char* ru;
 | |
| 	char url[500];
 | |
| 	char bytes_str[40];
 | |
| 	time_t now;
 | |
| 	struct tm* t;
 | |
| 	const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
 | |
| 	char date_nozone[100];
 | |
| 	int zone;
 | |
| 	char sign;
 | |
| 	char date[100];
 | |
| 
 | |
| 	if(logfp == (FILE*) 0) {
 | |
| 		return;
 | |
| 	}
 | |
| 	if(protocol == (char*) 0) {
 | |
| 		protocol = "UNKNOWN";
 | |
| 	}
 | |
| 	if(path == (char*) 0) {
 | |
| 		path = "";
 | |
| 	}
 | |
| 	if(remoteuser != (char*) 0) {
 | |
| 		ru = remoteuser;
 | |
| 	} else {
 | |
| 		ru = "-";
 | |
| 	}
 | |
| 	now = time((time_t*) 0);
 | |
| 	(void) snprintf(url, sizeof(url), "%s", path);
 | |
| 	if(bytes >= 0) {
 | |
| 		(void) snprintf(bytes_str, sizeof(bytes_str), "%lld", (long long int) bytes );
 | |
| 	} else {
 | |
| 		(void) strcpy(bytes_str, "-");
 | |
| 	}
 | |
| 	t = localtime(&now);
 | |
| 	(void) strftime(date_nozone, sizeof(date_nozone), cernfmt_nozone, t);
 | |
| 	zone = t->tm_gmtoff / 60L;
 | |
| 	if(zone >= 0) {
 | |
| 		sign = '+';
 | |
| 	} else {
 | |
| 		sign = '-';
 | |
| 		zone = -zone;
 | |
| 	}
 | |
| 	zone = (zone / 60) * 100 + zone % 60;
 | |
| 	(void) snprintf(date, sizeof(date), "%s %c%04d", date_nozone, sign, zone);
 | |
| 	(void) fprintf(logfp, "%.80s - %.80s [%s] \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"\n", ntoa(&client_addr), ru, date, get_method_str(method), url, protocol, status, bytes_str, referer, useragent);
 | |
| 	(void) fflush(logfp);
 | |
| }
 | |
| 
 | |
| static ssize_t my_read(char* buf, size_t size, int is_ssl) {
 | |
| 	if(is_ssl) {
 | |
| 		return SSL_read(ssl, buf, size);
 | |
| 	} else {
 | |
| 		return read(conn_fd, buf, size);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static ssize_t my_write(char* buf, size_t size, int is_ssl) {
 | |
| 	if(is_ssl) {
 | |
| 		return SSL_write(ssl, buf, size);
 | |
| 	} else {
 | |
| 		return write(conn_fd, buf, size);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void no_value_required(char* name, char* value) {
 | |
| 	if(value != (char*) 0) {
 | |
| 		(void) fprintf(stderr, "%s: no value required for %s option\n", argv0, name);
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static char* ntoa(usockaddr* usaP) {
 | |
| 	static char str[200];
 | |
| 
 | |
| 	if(getnameinfo(&usaP->sa, sockaddr_len(usaP), str, sizeof(str), 0, 0, NI_NUMERICHOST) != 0) {
 | |
| 		str[0] = '?';
 | |
| 		str[1] = '\0';
 | |
| 	} else if(IN6_IS_ADDR_V4MAPPED(&usaP->sa_in6.sin6_addr) && strncmp(str, "::ffff:", 7) == 0) {
 | |
| 		(void) strcpy(str, &str[7]);
 | |
| 	}
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| static void post_post_garbage_hack(int is_ssl) {
 | |
| 	char buf[2];
 | |
| 
 | |
| 	if(is_ssl) {
 | |
| 		return;
 | |
| 	}
 | |
| 	set_ndelay(conn_fd);
 | |
| 	if(read(conn_fd, buf, sizeof(buf)) < 0) { ; };
 | |
| 	clear_ndelay(conn_fd);
 | |
| }
 | |
| 
 | |
| static void read_config(char* filename) {
 | |
| 	FILE* fp;
 | |
| 	char line[10000];
 | |
| 	char* cp;
 | |
| 	char* cp2;
 | |
| 	char* name;
 | |
| 	char* value;
 | |
| 
 | |
| 	fp = fopen(filename, "r");
 | |
| 	if(fp == (FILE*) 0) {
 | |
| 		syslog(LOG_CRIT, "%s - %m", filename);
 | |
| 		perror(filename);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	while(fgets(line, sizeof(line), fp) != (char*) 0) {
 | |
| 		if((cp = strchr(line, '#')) != (char*) 0) {
 | |
| 			*cp = '\0';
 | |
| 		}
 | |
| 		cp = line;
 | |
| 		cp += strspn(cp, " \t\012\015");
 | |
| 		while(*cp != '\0') {
 | |
| 			cp2 = cp + strcspn(cp, " \t\012\015");
 | |
| 			while(*cp2 == ' ' || *cp2 == '\t' || *cp2 == '\012' || *cp2 == '\015') {
 | |
| 				*cp2++ = '\0';
 | |
| 			}
 | |
| 			name = cp;
 | |
| 			value = strchr(name, '=');
 | |
| 			if(value != (char*) 0) {
 | |
| 				*value++ = '\0';
 | |
| 			}
 | |
| 			if(strcasecmp(name, "debug") == 0) {
 | |
| 				no_value_required(name, value);
 | |
| 				debug = 1;
 | |
| 			} else if(strcasecmp(name, "port") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				port = (unsigned short) atoi(value);
 | |
| 			} else if(strcasecmp(name, "dir") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				dir = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "data_dir") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				data_dir = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "user") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				user = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "cgipat") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				cgi_pattern = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "urlpat") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				url_pattern = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "noemptyreferers") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				no_empty_referers = 1;
 | |
| 			} else if(strcasecmp(name, "localpat") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				local_pattern = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "host") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				hostname = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "logfile") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				logfile = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "pidfile") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				pidfile = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "charset") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				charset = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "p3p") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				p3p = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "max_age") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				max_age = atoi(value);
 | |
| 			} else if(strcasecmp(name, "default_realm_name") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				defaultRealmName = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "default_realm_password_file") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				defaultRealmPasswordFile = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "default_page_file") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				defaultPageFile = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "page_not_found_file") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				pageNotFoundFile = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "allow_directory_listing") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				if(strcmp(value, "1") == 0 || strcmp(value, "true") == 0 || strcmp(value, "TRUE") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "YES") == 0) {
 | |
| 					allowDirectoryListing = 1;
 | |
| 				} else {
 | |
| 					allowDirectoryListing = 0;
 | |
| 				}
 | |
| 			} else if(strcasecmp(name, "ssl") == 0) {
 | |
| 				no_value_required(name, value);
 | |
| 				do_ssl = 1;
 | |
| 			} else if(strcasecmp(name, "certfile") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				certfile = e_strdup(value);
 | |
| 			} else if(strcasecmp(name, "cipher") == 0) {
 | |
| 				value_required(name, value);
 | |
| 				cipher = e_strdup(value);
 | |
| 			} else {
 | |
| 				(void) fprintf(stderr, "%s: unknown config option '%s'\n", argv0, name);
 | |
| 				exit(1);
 | |
| 			}
 | |
| 			cp = cp2;
 | |
| 			cp += strspn(cp, " \t\012\015");
 | |
| 		}
 | |
| 	}
 | |
| 	(void) fclose(fp);
 | |
| }
 | |
| 
 | |
| static int really_check_referer(void) {
 | |
| 	char* cp1;
 | |
| 	char* cp2;
 | |
| 	char* cp3;
 | |
| 	char* refhost;
 | |
| 	char *lp;
 | |
| 
 | |
| 	if(referer == (char*) 0 || referer[0] == '\0' || (cp1 = strstr(referer, "//")) == (char*) 0) {
 | |
| 		if(no_empty_referers && match(url_pattern, path)) {
 | |
| 			return 0;
 | |
| 		} else {
 | |
| 			return 1;
 | |
| 		}
 | |
| 	}
 | |
| 	cp1 += 2;
 | |
| 	for(cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2) {
 | |
| 		continue;
 | |
| 	}
 | |
| 	refhost = (char*) e_malloc(cp2 - cp1 + 1);
 | |
| 	for(cp3 = refhost; cp1 < cp2; ++cp1, ++cp3) {
 | |
| 		if(isupper(*cp1)) {
 | |
| 			*cp3 = tolower(*cp1);
 | |
| 		} else {
 | |
| 			*cp3 = *cp1;
 | |
| 		}
 | |
| 	}
 | |
| 	*cp3 = '\0';
 | |
| 	if(local_pattern != (char*) 0) {
 | |
| 		lp = local_pattern;
 | |
| 	} else {
 | |
| 		lp = hostname;
 | |
| 		if(lp == (char*) 0) {
 | |
| 			return 1;
 | |
| 		}
 | |
| 	}
 | |
| 	if(!match(lp, refhost) && match(url_pattern, path)) {
 | |
| 		return 0;
 | |
| 	} else {
 | |
| 		return 1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void reopen_logfile(void) {
 | |
| 	if(logfp != (FILE*) 0) {
 | |
| 		(void) fclose(logfp);
 | |
| 		logfp = (FILE*) 0;
 | |
| 	}
 | |
| 	if(logfile != (char*) 0) {
 | |
| 		syslog(LOG_NOTICE, "re-opening logfile");
 | |
| 		logfp = fopen(logfile, "a");
 | |
| 		if(logfp == (FILE*) 0) {
 | |
| 			syslog(LOG_CRIT, "%s - %m", logfile);
 | |
| 			perror(logfile);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void send_authenticate(char* realm, int is_ssl) {
 | |
| 	char header[10000];
 | |
| 
 | |
| 	(void) snprintf(header, sizeof(header), "WWW-Authenticate: Basic realm=\"%s\"", realm);
 | |
| 	send_error(401, "Unauthorized", header, "Authorization required.", is_ssl);
 | |
| }
 | |
| 
 | |
| static void send_error(int s, char* title, char* extra_header, char* text, int is_ssl) {
 | |
| 	add_headers(s, title, extra_header, "", "text/html; charset=%s", (off_t) -1, (time_t) -1);
 | |
| 	send_error_body(s, title, text);
 | |
| 	send_error_tail();
 | |
| 	send_response(is_ssl);
 | |
| 	SSL_free(ssl);
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| static void send_error_body(int s, char* title, char* text) {
 | |
| 	char filename[1000];
 | |
| 	char buf[10000];
 | |
| 	int buflen;
 | |
| 
 | |
| 	(void) snprintf(filename, sizeof(filename), "%s/err%d.html", ERR_DIR, s);
 | |
| 	if(send_error_file(filename)) {
 | |
| 		return;
 | |
| 	}
 | |
| 	buflen = snprintf(buf, sizeof(buf), "<html>\n<head><title>Error %d - %s</title></head>\n<body bgcolor=\"#cc9999\" text=\"#000000\">\n<h2>Error %d</h2>\n", s, title, s);
 | |
| 	add_to_response(buf, buflen);
 | |
| 	buflen = snprintf(buf, sizeof(buf), "%s\n", text);
 | |
| 	add_to_response(buf, buflen);
 | |
| }
 | |
| 
 | |
| static int send_error_file(char* filename) {
 | |
| 	FILE* fp;
 | |
| 	char buf[1000];
 | |
| 	size_t r;
 | |
| 
 | |
| 	fp = fopen(filename, "r");
 | |
| 	if(fp == (FILE*) 0) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 	for(;;) {
 | |
| 		r = fread(buf, 1, sizeof(buf), fp);
 | |
| 		if(r == 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		add_to_response(buf, r);
 | |
| 	}
 | |
| 	(void) fclose(fp);
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| static void send_error_tail(void) {
 | |
| 	char buf[500];
 | |
| 	int buflen;
 | |
| 
 | |
| 	if(match("**MSIE**", useragent)) {
 | |
| 		int n;
 | |
| 		buflen = snprintf(buf, sizeof(buf), "<!--\n");
 | |
| 		add_to_response(buf, buflen);
 | |
| 		for(n = 0; n < 6; ++n) {
 | |
| 			buflen = snprintf(buf, sizeof(buf), "Padding so that MSIE deigns to show this error instead of its own canned one.\n");
 | |
| 			add_to_response(buf, buflen);
 | |
| 		}
 | |
| 		buflen = snprintf(buf, sizeof(buf), "-->\n");
 | |
| 		add_to_response(buf, buflen);
 | |
| 	}
 | |
| 	buflen = snprintf(buf, sizeof(buf), "<hr>\n%s\n</body>\n</html>\n", SERVER_SOFTWARE);
 | |
| 	add_to_response(buf, buflen);
 | |
| }
 | |
| 
 | |
| static void send_redirect(char* extra_header, char* hostname, char* new_location, int is_ssl) {
 | |
| 	char extra_header_buf[5000];
 | |
| 	const char *sep = new_location[0] == '/' ? "" : "/";
 | |
| 	const char *proto = is_ssl == 1 ? "https://" : "http://";
 | |
| 
 | |
| 	extra_header = extra_header == NULL ? "" : extra_header;
 | |
| 	if(strcmp(extra_header, "") == 0) {
 | |
| 		sprintf(extra_header_buf, "Location: %s%s%s%s", proto, hostname, sep, new_location);
 | |
| 	} else {
 | |
| 		sprintf(extra_header_buf, "%s\r\nLocation: %s%s%s%s", extra_header, proto, hostname, sep, new_location);
 | |
| 	}
 | |
| 	add_headers(301, "Moved Permanently", extra_header_buf, "", "text/html; charset=%s", (off_t) -1, (time_t) -1);
 | |
| 	send_error_body(301, "Moved Permanently", "Moved Permanently");
 | |
| 	send_error_tail();
 | |
| 	send_response(is_ssl);
 | |
| 	SSL_free(ssl);
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| static void send_response(is_ssl) {
 | |
| 	(void) my_write(response, response_len, is_ssl);
 | |
| }
 | |
| 
 | |
| static void send_via_write(int fd, off_t size, int is_ssl) {
 | |
| 	if(size <= SIZE_T_MAX) {
 | |
| 		size_t size_size = (size_t) size;
 | |
| 		void* ptr = mmap(0, size_size, PROT_READ, MAP_PRIVATE, fd, 0);
 | |
| 		if(ptr != (void*) -1) {
 | |
| 			(void) my_write(ptr, size_size, is_ssl);
 | |
| 			(void) munmap(ptr, size_size);
 | |
| 		}
 | |
| #ifdef MADV_SEQUENTIAL
 | |
| 		(void) madvise(ptr, size_size, MADV_SEQUENTIAL);
 | |
| #endif
 | |
| 	} else {
 | |
| 		char buf[30000];
 | |
| 		ssize_t r, r2;
 | |
| 		for(;;) {
 | |
| 			r = read(fd, buf, sizeof(buf));
 | |
| 			if(r < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 				sleep(1);
 | |
| 				continue;
 | |
| 			}
 | |
| 			if(r <= 0) {
 | |
| 				return;
 | |
| 			}
 | |
| 			for(;;) {
 | |
| 				r2 = my_write(buf, r, is_ssl);
 | |
| 				if(r2 < 0 && (errno == EINTR || errno == EAGAIN)) {
 | |
| 					sleep(1);
 | |
| 					continue;
 | |
| 				}
 | |
| 				if(r2 != r) {
 | |
| 					return;
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void set_ndelay(int fd) {
 | |
| 	int flags, newflags;
 | |
| 
 | |
| 	flags = fcntl(fd, F_GETFL, 0);
 | |
| 	if(flags != -1) {
 | |
| 		newflags = flags | (int) O_NDELAY;
 | |
| 		if(newflags != flags) {
 | |
| 			(void) fcntl(fd, F_SETFL, newflags);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int sockaddr_check(usockaddr* usaP) {
 | |
| 	switch (usaP->sa.sa_family) {
 | |
| 		case AF_INET:
 | |
| 			return 1;
 | |
| 		case AF_INET6:
 | |
| 			return 1;
 | |
| 		default:
 | |
| 			return 0;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static size_t sockaddr_len(usockaddr* usaP) {
 | |
| 	switch(usaP->sa.sa_family) {
 | |
| 		case AF_INET:
 | |
| 			return sizeof(struct sockaddr_in);
 | |
| 		case AF_INET6:
 | |
| 			return sizeof(struct sockaddr_in6);
 | |
| 		default:
 | |
| 			return 0;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void start_request(void) {
 | |
| 	request_size = 0;
 | |
| 	request_idx = 0;
 | |
| }
 | |
| 
 | |
| static void start_response(void) {
 | |
| 	response_size = 0;
 | |
| }
 | |
| 
 | |
| static void strdecode(char* to, char* from) {
 | |
| 	for(; *from != '\0'; ++to, ++from) {
 | |
| 		if(from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) {
 | |
| 			*to = hexit(from[1]) * 16 + hexit(from[2]);
 | |
| 			from += 2;
 | |
| 		} else {
 | |
| 			*to = *from;
 | |
| 		}
 | |
| 	}
 | |
| 	*to = '\0';
 | |
| }
 | |
| 
 | |
| static void strencode(char* to, size_t tosize, const char* from) {
 | |
| 	int tolen;
 | |
| 
 | |
| 	for(tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) {
 | |
| 		if(isalnum(*from) || strchr("/_.-~", *from) != (char*) 0) {
 | |
| 			*to = *from;
 | |
| 			++to;
 | |
| 			++tolen;
 | |
| 		} else {
 | |
| 			(void) sprintf(to, "%%%02x", (int) *from & 0xff);
 | |
| 			to += 3;
 | |
| 			tolen += 3;
 | |
| 		}
 | |
| 	}
 | |
| 	*to = '\0';
 | |
| }
 | |
| 
 | |
| static void usage(void) {
 | |
| 	(void) fprintf(stderr, "Usage:  %s [-C configfile] [-D] [-S use ssl, if no ssl port is specified all connections will be SSL] [-E certfile] [-SP ssl port] [-Y cipher] [-p port] [-d dir] [-dd data_dir] [-c cgipat] [-u user] [-h hostname] [-l logfile] [-i pidfile] [-T charset] [-L language] [-P P3P] [-M maxage] [-DRN default realm name] [-DRP default realm password file] [-DPF default page file] [-PNF Page to load when 404 Not Found error occurs] \n", argv0);
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| static void value_required(char* name, char* value) {
 | |
| 	if(value == (char*) 0) {
 | |
| 		(void) fprintf(stderr, "%s: value required for %s option\n", argv0, name);
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 |