2279 righe
58 KiB
C
2279 righe
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);
|
|
}
|
|
}
|