Gut compile-time options, add command-line options

This commit is contained in:
Neale Pickett 2012-02-15 16:19:02 -07:00
parent 10258ec975
commit 18e2662b50
2 changed files with 117 additions and 266 deletions

View File

@ -2,15 +2,7 @@ VERSION := $(shell head -n 1 CHANGES | tr -d :)
CFLAGS = -DFNORD='"fnord/$(VERSION)"' -Wall -Werror
all: fnord fnord-cgi fnord-idx
fnord-cgi: CFLAGS += -DCGI
fnord-cgi: fnord.c
$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $<
fnord-idx: CFLAGS += -DDIR_LIST
fnord-idx: fnord.c
$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $<
all: fnord
clean:
rm -f *.[oa] fnord fnord-cgi fnord-idx
rm -f *.[oa] fnord

371
fnord.c
View File

@ -39,77 +39,6 @@
#define DUMP_c(v) DUMPf("%s = %c", #v, v)
#define DUMP_p(v) DUMPf("%s = %p", #v, v)
/*
* uncomment the following line to enable support for CGI
*/
// #define CGI
#ifdef CGI
/*
* uncomment the following line to enable support for "index.cgi" That is:
* if "index.html" is not present then look for "index.cgi"
*/
#define INDEX_CGI
#endif
/*
* the following switch will make fnord normalize the Host: HTTP header
* from "foo" to "foo:80"
*/
#define NORMALIZE_HOST
/*
* uncomment the following line to enable support for autogenerated
* directory-listings for directories without index
*/
/*
* #define DIR_LIST
*/
#ifdef DIR_LIST
/*
* uncomment the following line to enable support for system symlink
* dereferencingr. HOPE YOU KNOW WHAT YOU'RE LINKING ! e.g.: if a file
* foo is a symlink to /etc/passwd and you don't have a chroot enviroment
* then the system-wide /etc/passwd is provided !!! If the symlink is
* dangling OR this option is not active the symlink is provided as a new
* http-uri. e.g.: is foo a symlink to /etc/passwd than the clinet gets a
* href to http://<vhost>/etc/passwd
*/
/*
* #define SYSTEM_SYMLINK_DEREF
*/
#endif
/*
* uncomment the following line to get full-host redirection. If a file is
* not found locally, and $REDIRECT_HOST is set, fnord will issue a
* redirect to strcat($REDIRECT_HOST,uri). Otherwise, if $REDIRECT_URI is
* set, fnord will issue a redirect to $REDIRECT_URI. Only if those fail
* will a 404 error be returned.
*/
#define OLD_STYLE_REDIRECT
/*
* uncomment the following line to get full-host redirection. if the
* virtual host directory/symlink is a broken symlink, fnord will issue a
* redirect. If the contents of the symlink starts with an equal sign
* ('='), fnord will throw away the URI part.
*/
#define REDIRECT
/*
* uncomment the following line to make fnord chroot to the current
* working directory and drop privileges
*/
#define CHROOT
/*
* uncomment the following line to make fnord support connection
* keep-alive
*/
#define KEEPALIVE
/*
* the following is the time in seconds that fnord should wait for a valid
* HTTP request
@ -165,12 +94,9 @@ char *ua = "?"; /* user-agent */
char *refer; /* Referrer: header */
char *accept_enc; /* Accept-Encoding */
int httpversion; /* 0 == 1.0, 1 == 1.1 */
#ifdef KEEPALIVE
int keepalive = 0; /* should we keep the connection alive? */
int rootdir; /* fd of root directory, so we can fchdir
* * back for keep-alive */
#endif
#ifdef CGI
char *cookie; /* Referrer: header */
char *uri; /* copy of url before demangling */
char *content_type;
@ -179,7 +105,6 @@ char *auth_type;
char *post_miss;
unsigned long post_mlen;
unsigned long post_len = 0;
#endif
#if _FILE_OFFSET_BITS == 64
static unsigned long long rangestart,
@ -197,17 +122,16 @@ static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
#define MAXHEADERLEN 8192
char *remote_ip;
#ifdef CGI
char *remote_port;
char *remote_ident;
#endif
static void
sanitize(char *ua)
{ /* replace strings with underscores for *
* logging */
int j;
if (! ua) return;
if (!ua)
return;
for (j = 0; ua[j]; ++j)
if (isspace(ua[j]))
ua[j] = '_';
@ -222,7 +146,7 @@ dolog(off_t len)
fprintf(stderr, "%s %ld %lu %s %s %s %s\n",
remote_ip ? remote_ip : "0.0.0.0",
retcode, (unsigned long)len, host, ua, refer, url);
retcode, (unsigned long) len, host, ua, refer, url);
}
/*
@ -245,7 +169,6 @@ badrequest(long code, const char *httpcomment, const char *message)
exit(0);
}
#ifdef CGI
#define CGIENVLEN 21
static const char *cgivars[CGIENVLEN] = {
@ -275,11 +198,11 @@ static const char *cgivars[CGIENVLEN] = {
static int
iscgivar(const char *s)
{
int sl = strlen(s);
int sl = strlen(s);
register unsigned int i = 0;
for (; i < CGIENVLEN; i++)
if (! strncmp(s, cgivars[i], sl))
if (!strncmp(s, cgivars[i], sl))
return 1;
return 0;
}
@ -293,14 +216,14 @@ elen(register const char *const *e)
return i;
}
char *
char *
env_append(const char *key, const char *val)
{
static char buf[MAXHEADERLEN * 2 + PATH_MAX + 200];
static char *p = buf;
char *ret = p;
static char buf[MAXHEADERLEN * 2 + PATH_MAX + 200];
static char *p = buf;
char *ret = p;
if (! key) {
if (!key) {
p = buf;
return NULL;
}
@ -337,24 +260,35 @@ do_cgi(const char *pathinfo, const char *const *envp)
cgi_env[i++] = env_append("REQUEST_METHOD", method_name[method]);
cgi_env[i++] = env_append("REQUEST_URI", uri);
cgi_env[i++] = env_append("SCRIPT_NAME", url);
if (remote_ip) cgi_env[i++] = env_append("REMOTE_ADDR", remote_ip);
if (remote_port) cgi_env[i++] = env_append("REMOTE_PORT", remote_port);
if (remote_ident) cgi_env[i++] = env_append("REMOTE_IDENT", remote_ident);
if (ua) cgi_env[i++] = env_append("HTTP_USER_AGENT", ua);
if (cookie) cgi_env[i++] = env_append("HTTP_COOKIE", cookie);
if (refer) cgi_env[i++] = env_append("HTTP_REFERER", refer);
if (accept_enc) cgi_env[i++] = env_append("HTTP_ACCEPT_ENCODING", accept_enc);
if (auth_type) cgi_env[i++] = env_append("AUTH_TYPE", auth_type);
if (content_type) cgi_env[i++] = env_append("CONTENT_TYPE", content_type);
if (content_type) cgi_env[i++] = env_append("CONTENT_LENGTH", content_len);
if (args) cgi_env[i++] = env_append("QUERY_STRING", args);
if (remote_ip)
cgi_env[i++] = env_append("REMOTE_ADDR", remote_ip);
if (remote_port)
cgi_env[i++] = env_append("REMOTE_PORT", remote_port);
if (remote_ident)
cgi_env[i++] = env_append("REMOTE_IDENT", remote_ident);
if (ua)
cgi_env[i++] = env_append("HTTP_USER_AGENT", ua);
if (cookie)
cgi_env[i++] = env_append("HTTP_COOKIE", cookie);
if (refer)
cgi_env[i++] = env_append("HTTP_REFERER", refer);
if (accept_enc)
cgi_env[i++] = env_append("HTTP_ACCEPT_ENCODING", accept_enc);
if (auth_type)
cgi_env[i++] = env_append("AUTH_TYPE", auth_type);
if (content_type)
cgi_env[i++] = env_append("CONTENT_TYPE", content_type);
if (content_type)
cgi_env[i++] = env_append("CONTENT_LENGTH", content_len);
if (args)
cgi_env[i++] = env_append("QUERY_STRING", args);
if (pathinfo) {
char *rp = realpath(pathinfo, NULL);
char *rp = realpath(pathinfo, NULL);
cgi_env[i++] = env_append("PATH_INFO", pathinfo);
if (! rp) rp = pathinfo;
cgi_env[i++] = env_append("PATH_TRANSLATED", rp);
if (rp != pathinfo) free(rp);
cgi_env[i++] = env_append("PATH_TRANSLATED", rp ? rp : pathinfo);
if (rp)
free(rp);
}
{
@ -389,7 +323,7 @@ do_cgi(const char *pathinfo, const char *const *envp)
}
{
char tmp[PATH_MAX];
char tmp[PATH_MAX];
i = strrchr(url, '/') - url;
strncpy(tmp, url + 1, i);
@ -397,8 +331,8 @@ do_cgi(const char *pathinfo, const char *const *envp)
chdir(tmp);
}
{
char tmp[PATH_MAX];
{
char tmp[PATH_MAX];
/*
* program name
@ -411,7 +345,7 @@ do_cgi(const char *pathinfo, const char *const *envp)
* start cgi
*/
execve(cgi_arg[0], cgi_arg, cgi_env);
raise(SIGQUIT); /* gateway unavailable. */
raise(SIGQUIT); /* gateway unavailable. */
}
}
@ -604,7 +538,6 @@ start_cgi(int nph, const char *pathinfo, const char *const *envp)
}
exit(0);
}
#endif
static int
fromhex(int c)
@ -629,9 +562,7 @@ header(char *buf, int buflen, const char *hname)
int i;
char *c;
DUMPf("buflen %d, slen %d", buflen, slen);
for (i = 0; i < buflen - slen - 2; ++i) {
DUMPf("[%.*s] [%s]", slen, buf + i, hname);
if (!strncasecmp(buf + i, hname, slen)) {
if (i && (buf[i - 1] && buf[i - 1] != '\n'))
continue;
@ -1083,9 +1014,7 @@ handleredirect(const char *url, const char *origurl)
{
char symlink[1024];
int len;
#ifdef OLD_STYLE_REDIRECT
char *env;
#endif
while (*url == '/')
++url;
if ((len = readlink(url, symlink, 1023)) > 0) {
@ -1094,32 +1023,14 @@ handleredirect(const char *url, const char *origurl)
*/
redirectboilerplate();
printf("%.*s", len, symlink);
#ifdef OLD_STYLE_REDIRECT
fini:
#endif
retcode = 301;
printf("\r\n\r\n");
dolog(0);
fflush(stdout);
exit(0);
}
#ifdef OLD_STYLE_REDIRECT
if ((env = getenv("REDIRECT_HOST"))) {
redirectboilerplate();
fputs(env, stdout);
while (*origurl == '/')
++origurl;
fputs(origurl, stdout);
goto fini;
} else if ((env = getenv("REDIRECT_URI"))) {
redirectboilerplate();
fputs(env, stdout);
goto fini;
}
#endif
}
#ifdef DIR_LIST
static void
hdl_encode_html(const char *s, unsigned int sl)
{
@ -1127,8 +1038,7 @@ hdl_encode_html(const char *s, unsigned int sl)
for (i = 0; i < sl; ++i) {
unsigned char ch = s[i];
if (ch > 159) {
encode_dec:
printf("&#%lu;", ch);
printf("&#%u;", ch);
} else if ((ch > 128) || (ch < 32)) {
putchar('_');
} else if (ch == '"')
@ -1173,12 +1083,12 @@ handledirlist(const char *origurl)
&& ((st.st_mode & S_IRWXO) == 5)) {
if (nl)
chdir(nurl);
if (dir = opendir(".")) {
if ((dir = opendir("."))) {
struct dirent *de;
unsigned int i,
size = 32 + nl;
fputs("HTTP/1.0 200 OK\r\nServer: " FNORD
"\r\nConnection: close\r\n", stdout);
"\r\nConnection: close\r\n", stdout);
fputs("Content-Type: text/html\r\n", stdout);
fputs("\r\n<h3>Directory Listing: /", stdout);
hdl_encode_html(nurl, nl);
@ -1196,7 +1106,7 @@ handledirlist(const char *origurl)
fputs("</a>\n", stdout);
size += 40 + i;
}
while (de = readdir(dir)) {
while ((de = readdir(dir))) {
char symlink[1024];
char *p = de->d_name;
unsigned int pl,
@ -1209,14 +1119,9 @@ handledirlist(const char *origurl)
if (S_ISDIR(st.st_mode))
fputs("[DIR] ", stdout);
else if (S_ISLNK(st.st_mode)) {
#ifdef SYSTEM_SYMLINK_DEREF
if (stat(de->d_name, &st)) /* dangling symlink */
#endif
{
if ((pl = readlink(de->d_name, symlink, 1023)) < 1)
continue;
p = symlink;
}
if ((pl = readlink(de->d_name, symlink, 1023)) < 1)
continue;
p = symlink;
fputs("[LNK] ", stdout); /* a symlink to *
* something ... */
} else if (S_ISREG(st.st_mode))
@ -1246,9 +1151,7 @@ handledirlist(const char *origurl)
}
}
}
#endif
#ifdef INDEX_CGI
static int
handleindexcgi(const char *testurl, const char *origurl, char *space)
{
@ -1263,7 +1166,7 @@ handleindexcgi(const char *testurl, const char *origurl, char *space)
test = space;
++test;
ul -= 4;
byte_copy(test, ul, testurl);
memcpy(test, testurl, ul);
test[ul] = 'c';
test[++ul] = 'g';
test[++ul] = 'i';
@ -1281,39 +1184,34 @@ handleindexcgi(const char *testurl, const char *origurl, char *space)
url = test;
return 1; /* Wow... now start "index.cgi" */
}
#endif
static void
get_ucspi_env(void)
{
char *ucspi = getenv("PROTO");
if (ucspi) {
int protolen = strlen(ucspi);
char *buf = alloca(protolen + 20);
int protolen = strlen(ucspi);
char *buf = alloca(protolen + 20);
strcpy(buf, ucspi);
strcpy(buf + protolen, "REMOTEIP");
remote_ip = getenv(buf);
#ifdef CGI
strcpy(buf + protolen, "REMOTEPORT");
remote_port = getenv(buf);
strcpy(buf + protolen, "REMOTEINFO");
remote_ident = getenv(buf);
#endif
}
}
#ifdef CGI
static int
findcgi(const char *c)
{
return (c[0] == '.' && c[1] == 'c' &&
c[2] == 'g' && c[3] == 'i' && (c[4] == '/' || c[4] == 0));
}
#endif
static int
serve_read_write(int fd)
@ -1500,60 +1398,40 @@ int
main(int argc, char *argv[], const char *const *envp)
{
char buf[MAXHEADERLEN];
#if 0
char buf2[MAXHEADERLEN];
#endif
char *nurl,
*origurl;
int docgi = 0;
int dirlist = 0;
int redirect = 0;
int len;
int in;
if (argc > 1)
chdir(argv[1]);
{
int opt;
#ifdef CHROOT
if (chroot(".")) {
if (errno != EPERM)
goto error500;
/*
* else fnord was called with uid!=0, i.e. it already is chroot
*/
} else {
char *tmp;
if (chdir("/"))
goto error500;
if ((tmp = getenv("GID"))) {
long gid;
char *endptr;
gid = strtoul(tmp, &endptr, 0);
if (*endptr == 0) {
gid_t gi = gid;
if (setgroups(1, &gi))
goto error500;
} else
goto error500;
}
if ((tmp = getenv("UID"))) {
long uid;
char *endptr;
uid = strtoul(tmp, &endptr, 0);
if (*endptr == 0) {
if (setuid(uid))
goto error500;
} else
goto error500;
while (-1 != (opt = getopt(argc, argv, "cdr"))) {
switch (opt) {
case 'c':
docgi = 1;
break;
case 'd':
dirlist = 1;
break;
case 'r':
redirect = 1;
break;
default:
fprintf(stderr, "%d Usage: %s [-d] [-r]\n", opt, argv[0]);
return 69;
}
}
}
#endif
signal(SIGPIPE, SIG_IGN);
get_ucspi_env();
#ifdef KEEPALIVE
handlenext:
encoding = 0;
#endif
// alarm(20);
{
@ -1572,7 +1450,8 @@ main(int argc, char *argv[], const char *const *envp)
if (time(&now) < fini)
continue; /* fall through */
case -1: /* timeout or error */
// badrequest(408,"Request Time-out","No request appeared
// badrequest(408,"Request Time-out","No request
// appeared
// within a reasonable time period.");
return 1;
}
@ -1626,9 +1505,7 @@ main(int argc, char *argv[], const char *const *envp)
"<title>Bad Request</title>Only HTTP 1.x supported");
*space = 0;
httpversion = space[8] - '0';
#ifdef KEEPALIVE
keepalive = 0;
#endif
/*
* demangle path in-place
@ -1670,10 +1547,7 @@ main(int argc, char *argv[], const char *const *envp)
*/
}
#ifdef CGI
uri = alloca(space + 1);
byte_copy(uri, space + 1, url);
#endif
uri = strdup(url);
}
{
@ -1681,31 +1555,27 @@ main(int argc, char *argv[], const char *const *envp)
ua = header(buf, len, "User-Agent");
refer = header(buf, len, "Referer");
accept_enc = header(buf, len, "Accept-Encoding");
#ifdef KEEPALIVE
if ((tmp = header(buf, len, "Connection"))) { /* see if it's
* "keep-alive" or
* * "close" */
* "keep-alive"
* or * "close" */
if (!strcasecmp(tmp, "keep-alive"))
keepalive = 1;
else if (!strcasecmp(tmp, "close"))
keepalive = -1;
}
#endif
#ifdef CGI
cookie = header(buf, len, "Cookie");
auth_type = header(buf, len, "Authorization");
if (method == POST) {
content_type = header(buf, len, "Content-Type");
content_len = header(buf, len, "Content-Length");
if (content_len) {
scan_ulong(content_len, &post_len);
post_len = strtoul(content_len, NULL, 10);
post_miss = buf + len + 1;
post_mlen = in - len - 1;
if (post_len <= post_mlen)
post_mlen = post_len;
}
}
#endif
}
port = getenv("TCPLOCALPORT");
@ -1728,14 +1598,12 @@ main(int argc, char *argv[], const char *const *envp)
exit(101);
sprintf(Buf, "%s:%s", ip, port);
host = Buf;
#ifdef NORMALIZE_HOST
} else {
char *colon = strchr(host, ':');
if (*colon == 0) {
if (!colon) {
sprintf(Buf, "%s:%s", host, port);
host = Buf;
}
#endif
}
for (i = strlen(host); i >= 0; --i)
if ((host[i] = tolower(host[i])) == '/')
@ -1745,36 +1613,35 @@ main(int argc, char *argv[], const char *const *envp)
if (host[0] == '.')
goto hostb0rken;
// fprintf(stderr,"host %s\n",host);
#ifdef KEEPALIVE
if (keepalive > 0) {
if ((rootdir = open(".", O_RDONLY)) < 0)
keepalive = -1;
}
#endif
if (chdir(host)) {
#ifdef REDIRECT
char symlink[1024];
int linklen;
if ((linklen = readlink(host, symlink, sizeof symlink)) > 0) {
/*
* it is a broken symlink. Do a redirection
*/
redirectboilerplate();
if (symlink[0] == '=') {
fwrite(symlink + 1, linklen - 1, 1, stdout);
} else {
fwrite(symlink, linklen, 1, stdout);
while (url[0] == '/')
++url;
fputs(url, stdout);
if (redirect) {
char symlink[1024];
int linklen;
if ((linklen =
readlink(host, symlink, sizeof symlink)) > 0) {
/*
* it is a broken symlink. Do a redirection
*/
redirectboilerplate();
if (symlink[0] == '=') {
fwrite(symlink + 1, linklen - 1, 1, stdout);
} else {
fwrite(symlink, linklen, 1, stdout);
while (url[0] == '/')
++url;
fputs(url, stdout);
}
retcode = 301;
fputs("\r\n\r\n", stdout);
dolog(0);
fflush(stdout);
exit(0);
}
retcode = 301;
fputs("\r\n\r\n", stdout);
dolog(0);
fflush(stdout);
exit(0);
}
#endif
if (chdir("default") && argc < 2) {
badrequest(404, "Not Found",
"<title>Not Found</title>This host is not served here.");
@ -1815,11 +1682,11 @@ main(int argc, char *argv[], const char *const *envp)
retcode = 401;
dolog(0);
fputs("HTTP/1.0 401 Authorization Required\r\n"
"WWW-Authenticate: Basic realm=\"", stdout);
"WWW-Authenticate: Basic realm=\"", stdout);
fputs(host, stdout);
fputs("\"\r\nConnection: close\r\n\r\n"
"Access to this site is restricted.\r\n"
"Please provide credentials.\r\n", stdout);
"Access to this site is restricted.\r\n"
"Please provide credentials.\r\n", stdout);
fflush(stdout);
exit(0);
}
@ -1837,9 +1704,9 @@ main(int argc, char *argv[], const char *const *envp)
url = nurl;
nurl = url + i;
}
#ifdef CGI
nurl -= 3;
{
if (docgi) {
char *tmp,
*pathinfo;
pathinfo = 0;
@ -1876,13 +1743,10 @@ main(int argc, char *argv[], const char *const *envp)
*/
--nurl;
}
#ifdef INDEX_CGI
indexcgi:
#endif
start_cgi(0, pathinfo, envp); /* start a CGI */
}
}
#endif
{
int fd;
@ -1922,7 +1786,6 @@ main(int argc, char *argv[], const char *const *envp)
fputs("Server: " FNORD "\r\nContent-Type: ", stdout);
fputs(mimetype, stdout);
fputs("\r\n", stdout);
#ifdef KEEPALIVE
switch (keepalive) {
case -1:
fputs("Connection: close\r\n", stdout);
@ -1931,11 +1794,11 @@ main(int argc, char *argv[], const char *const *envp)
fputs("Connection: Keep-Alive\r\n", stdout);
break;
}
#endif
if (encoding) {
printf("Content-Encoding: %s\r\n", encoding);
}
printf("Content-Length: %llu\r\n", (unsigned long long)(rangeend - rangestart));
printf("Content-Length: %llu\r\n",
(unsigned long long) (rangeend - rangestart));
printf("Last-Modified: ");
{
struct tm *x = gmtime(&st.st_mtime);
@ -1952,9 +1815,9 @@ main(int argc, char *argv[], const char *const *envp)
if (rangestart || rangeend != st.st_size) {
printf
("Accept-Ranges: bytes\r\nContent-Range: bytes %llu-%llu/%llu\r\n",
(unsigned long long)rangestart,
(unsigned long long)rangeend - 1,
(unsigned long long)st.st_size);
(unsigned long long) rangestart,
(unsigned long long) rangeend - 1,
(unsigned long long) st.st_size);
}
fputs("\r\n", stdout);
if (method == GET || method == POST) {
@ -1966,7 +1829,6 @@ main(int argc, char *argv[], const char *const *envp)
case 1:
return 1;
}
#ifdef KEEPALIVE
#ifdef TCP_CORK
if (corked) {
int zero = 0;
@ -1980,7 +1842,6 @@ main(int argc, char *argv[], const char *const *envp)
close(rootdir);
goto handlenext;
}
#endif
exit(0);
error500:
retcode = 500;
@ -1991,16 +1852,14 @@ main(int argc, char *argv[], const char *const *envp)
switch (retcode) {
case 404:
{
#ifdef INDEX_CGI
char *space = alloca(strlen(url) + 2);
if (handleindexcgi(url, origurl, space))
goto indexcgi;
#endif
handleredirect(url, origurl);
#ifdef DIR_LIST
handledirlist(origurl);
#endif
if (dirlist) {
handledirlist(origurl);
}
badrequest(404, "Not Found",
"<title>Not Found</title>No such file or directory.");
}