/* * simple httpd to be started from tcpserver */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Some things I use for debugging */ #ifdef DUMP #define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) #else #define DUMPf(fmt, args...) #endif #define DUMP() DUMPf("") #define DUMP_d(v) DUMPf("%s = %d", #v, v) #define DUMP_x(v) DUMPf("%s = 0x%x", #v, v) #define DUMP_s(v) DUMPf("%s = %s", #v, v) #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:///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 */ #define READTIMEOUT 20 /* * the following is the time in seconds that fnord should wait before * aborting a request when trying to write the answer */ #define WRITETIMEOUT 20 #define CGI_TIMEOUT (5*60) /* 5 minutes time-out for CGI to complete */ /* * defining USE_SENDFILE enables zero-copy TCP on Linux for static files. * I measured over 320 meg per second with apache bench over localhost * with sendfile and keep-alive. However, sendfile does not work with * large files and may be considered cheating ;-) Also, sendfile is a * blocking operation. Thus, no timeout handling. */ #define USE_SENDFILE #ifndef __linux__ #undef USE_SENDFILE #endif #ifdef USE_SENDFILE #include #endif #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif #define USE_MMAP #ifndef _POSIX_MAPPED_FILES #undef USE_MMAP #endif enum { UNKNOWN, GET, HEAD, POST } method; #ifdef TCP_CORK static int corked; #endif static long retcode = 404; /* used for logging code */ char *host = "?"; /* Host: header */ char *port; /* also Host: header, :80 part */ char *args; /* URL behind ? (for CGIs) */ char *url; /* string between GET and HTTP/1.0, * demangled */ 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; char *content_len; 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, rangeend; /* for ranged queries */ #else static unsigned long rangestart, rangeend; /* for ranged queries */ #define scan_range scan_ulong #define buffer_putrange buffer_putulong #endif static const char days[] = "SunMonTueWedThuFriSat"; 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; for (j = 0; ua[j]; ++j) if (isspace(ua[j])) ua[j] = '_'; } static void dolog(off_t len) { /* write a log line to stderr */ sanitize(host); sanitize(ua); if (refer) sanitize(refer); fprintf(stderr, "%s %d %d %s %s %s %s\n", remote_ip ? remote_ip : "0.0.0.0", retcode, len, host, ua, refer, url); } /* * output an error message and exit */ static void badrequest(long code, const char *httpcomment, const char *message) { retcode = code; dolog(0); buffer_puts(buffer_1, "HTTP/1.0 "); buffer_putulong(buffer_1, code); buffer_putspace(buffer_1); buffer_puts(buffer_1, httpcomment); buffer_puts(buffer_1, "\r\nConnection: close\r\n"); if (message[0]) { buffer_puts(buffer_1, "Content-Length: "); buffer_putulong(buffer_1, strlen(message)); buffer_puts(buffer_1, "\r\nContent-Type: text/html\r\n\r\n"); buffer_puts(buffer_1, message); } else { buffer_puts(buffer_1, "\r\n"); } buffer_flush(buffer_1); exit(0); } #ifdef CGI #define CGIENVLEN 21 static const char *cgivars[CGIENVLEN] = { "GATEWAY_INTERFACE=", "SERVER_PROTOCOL=", "SERVER_SOFTWARE=", "SERVER_NAME=", "SERVER_PORT=", "REQUEST_METHOD=", "REQUEST_URI=", "SCRIPT_NAME=", "REMOTE_ADDR=", "REMOTE_PORT=", "REMOTE_IDENT=", "HTTP_USER_AGENT=", "HTTP_COOKIE=", "HTTP_REFERER=", "HTTP_ACCEPT_ENCODING=", "AUTH_TYPE=", "CONTENT_TYPE=", "CONTENT_LENGTH=", "QUERY_STRING=", "PATH_INFO=", "PATH_TRANSLATED=" }; static int iscgivar(const char *s) { register unsigned int i = 0; for (; i < CGIENVLEN; i++) if (str_start(s, cgivars[i])) return 1; return 0; } static unsigned int elen(register const char *const *e) { register unsigned int i = 0; while (e[i]) i++; return i; } static void do_cgi(const char *pathinfo, const char *const *envp) { const char *method_name[] = { "?", "GET", "HEAD", "POST" }; char cgi_env_buf[MAXHEADERLEN * 2 + PATH_MAX + 200]; register unsigned int en = elen(envp); char *tmp = cgi_env_buf; char **cgi_arg; register int i; char **cgi_env = (char **) alloca((CGIENVLEN + en + 1) * sizeof(char *)); cgi_env[0] = "GATEWAY_INTERFACE=CGI/1.1"; cgi_env[1] = "SERVER_PROTOCOL=HTTP/1.0"; cgi_env[2] = "SERVER_SOFTWARE=" FNORD; cgi_env[3] = tmp; tmp += str_copy(tmp, "SERVER_NAME="); tmp += str_copy(tmp, host); *tmp = 0; ++tmp; cgi_env[4] = tmp; tmp += str_copy(tmp, "SERVER_PORT="); tmp += str_copy(tmp, port); *tmp = 0; ++tmp; cgi_env[5] = tmp; tmp += str_copy(tmp, "REQUEST_METHOD="); tmp += str_copy(tmp, method_name[method]); *tmp = 0; ++tmp; cgi_env[6] = tmp; tmp += str_copy(tmp, "REQUEST_URI="); tmp += str_copy(tmp, uri); *tmp = 0; ++tmp; cgi_env[7] = tmp; tmp += str_copy(tmp, "SCRIPT_NAME="); tmp += str_copy(tmp, url); *tmp = 0; ++tmp; i = 7; if (remote_ip) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "REMOTE_ADDR="); tmp += str_copy(tmp, remote_ip); *tmp = 0; ++tmp; } if (remote_port) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "REMOTE_PORT="); tmp += str_copy(tmp, remote_port); *tmp = 0; ++tmp; } if (remote_ident) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "REMOTE_IDENT="); tmp += str_copy(tmp, remote_ident); *tmp = 0; ++tmp; } if (ua) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "HTTP_USER_AGENT="); tmp += str_copy(tmp, ua); *tmp = 0; ++tmp; } if (cookie) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "HTTP_COOKIE="); tmp += str_copy(tmp, cookie); *tmp = 0; ++tmp; } if (refer) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "HTTP_REFERER="); tmp += str_copy(tmp, refer); *tmp = 0; ++tmp; } if (accept_enc) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "HTTP_ACCEPT_ENCODING="); tmp += str_copy(tmp, accept_enc); *tmp = 0; ++tmp; } if (auth_type) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "AUTH_TYPE="); tmp += str_copy(tmp, auth_type); *tmp = 0; ++tmp; } if (content_type) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "CONTENT_TYPE="); tmp += str_copy(tmp, content_type); *tmp = 0; ++tmp; cgi_env[++i] = tmp; tmp += str_copy(tmp, "CONTENT_LENGTH="); DUMP_s(content_len); tmp += str_copy(tmp, content_len); *tmp = 0; ++tmp; } if (args) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "QUERY_STRING="); tmp += str_copy(tmp, args); *tmp = 0; ++tmp; } if (pathinfo) { cgi_env[++i] = tmp; tmp += str_copy(tmp, "PATH_INFO="); tmp += str_copy(tmp, pathinfo); *tmp = 0; ++tmp; cgi_env[++i] = tmp; tmp += str_copy(tmp, "PATH_TRANSLATED="); tmp += realpath(pathinfo, tmp) ? str_len(tmp) : str_copy(tmp, pathinfo); ++tmp; } { unsigned int j = 0; for (; j < en; j++) if (!iscgivar(envp[j])) cgi_env[++i] = (char *) envp[j]; } cgi_env[++i] = 0; /* * argv */ if (args && (args[str_chr(args, '=')] == 0)) { int n = 3; for (i = 0; args[i]; ++i) if (args[i] == '+') ++n; cgi_arg = alloca(n * sizeof(char *)); cgi_arg[n = 1] = args; for (i = 0; args[i]; ++i) { if (args[i] == '+') { args[i] = 0; ++i; cgi_arg[++n] = args + i; } } cgi_arg[++n] = 0; } else { cgi_arg = alloca(2 * sizeof(char *)); cgi_arg[1] = 0; } i = strrchr(url, '/') - url; strncpy(tmp, url + 1, i); tmp[i] = 0; chdir(tmp); /* * program name */ cgi_arg[0] = tmp; tmp[0] = '.'; tmp[str_copy(tmp + 1, url + i) + 1] = 0; /* * start cgi */ execve(cgi_arg[0], cgi_arg, cgi_env); raise(SIGQUIT); /* gateway unavailable. */ } static void cgi_child(int sig) { int n, pid = waitpid(0, &n, WNOHANG); if (pid > 0) { if (WIFSIGNALED(n)) { if (WTERMSIG(n) == SIGALRM) badrequest(504, "Gateway Time-out", "Gateway has hit the Time-out."); else badrequest(502, "Bad Gateway", "Gateway broken or unavailable."); } } signal(SIGCHLD, cgi_child); } static void cgi_send_correct_http(const char *s, unsigned int sl) { unsigned int i; char ch = 0; for (i = 0; i < sl; ++i) { if ((s[i] == '\r') && (s[i + 1] == '\n')) { ++i; goto out_nl; } else { if (s[i] != '\n') buffer_put(buffer_1, s + i, 1); else { out_nl:buffer_put(buffer_1, "\r\n", 2); if (ch == '\n') { ++i; break; } } } ch = s[i]; } buffer_put(buffer_1, s + i, sl - i); } static void start_cgi(int nph, const char *pathinfo, const char *const *envp) { size_t size = 0; int n; int pid; char ibuf[8192], obuf[8192]; int fd[2], df[2]; if (pipe(fd) || pipe(df)) { badrequest(500, "Internal Server Error", "Server Resource problem."); } if ((pid = fork())) { if (pid > 0) { struct pollfd pfd[2]; int nr = 1; int startup = 1; signal(SIGCHLD, cgi_child); signal(SIGPIPE, SIG_IGN); /* NO! no signal! */ close(df[0]); close(fd[1]); pfd[0].fd = fd[0]; pfd[0].events = POLLIN; pfd[0].revents = 0; pfd[1].fd = df[1]; pfd[1].events = POLLOUT; pfd[1].revents = 0; if (post_len) ++nr; /* have post data */ else close(df[1]); /* no post data */ while (poll(pfd, nr, -1) != -1) { /* * read from cgi */ if (pfd[0].revents & POLLIN) { if (!(n = read(fd[0], ibuf, sizeof(ibuf)))) break; if (n < 0) goto cgi_500; /* * startup */ if (startup) { startup = 0; if (nph) { /* NPH-CGI */ buffer_put(buffer_1, ibuf, n); scan_ulong(ibuf + 9, &retcode); /* only * get * error * code / * str_len("HTTP/x.x * ")==9 */ } else { /* CGI */ if (byte_diff(ibuf, 10, "Location: ") == 0) { retcode = 302; buffer_puts(buffer_1, "HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n"); signal(SIGCHLD, SIG_IGN); cgi_send_correct_http(ibuf, n); buffer_flush(buffer_1); dolog(0); exit(0); } else { retcode = 200; buffer_puts(buffer_1, "HTTP/1.0 200 OK\r\nServer: " FNORD "\r\nPragma: no-cache\r\nConnection: close\r\n"); signal(SIGCHLD, SIG_IGN); cgi_send_correct_http(ibuf, n); } } } /* * non startup */ else { buffer_put(buffer_1, ibuf, n); } size += n; if (pfd[0].revents & POLLHUP) break; } /* * write to cgi the post data */ else if (nr > 1 && pfd[1].revents & POLLOUT) { if (post_miss) { write(df[1], post_miss, post_mlen); post_miss = 0; } else if (post_mlen < post_len) { n = read(0, obuf, sizeof(obuf)); if (n < 1) goto cgi_500; post_mlen += n; write(df[1], obuf, n); } else { --nr; close(df[1]); } } else if (pfd[0].revents & POLLHUP) break; else { cgi_500:if (startup) badrequest(500, "Internal Server Error", "Looks like the CGI crashed."); else { buffer_puts(buffer_1, "\n\n"); buffer_puts(buffer_1, "Looks like the CGI crashed."); buffer_puts(buffer_1, "\n\n"); break; } } } buffer_flush(buffer_1); dolog(size); #ifdef TCP_CORK { int zero = 0; setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero)); } #endif } } else { close(df[1]); close(fd[0]); dup2(df[0], 0); dup2(fd[1], 1); close(df[0]); close(fd[1]); alarm(CGI_TIMEOUT); do_cgi(pathinfo, envp); } exit(0); } #endif static int fromhex(int c) { if (c >= '0' && c <= '9') return c - '0'; else { c |= ' '; if (c >= 'a' && c <= 'f') return c - 'a' + 10; } return -1; } /* * header(buf,buflen,"User-Agent")="Mozilla" */ static char * header(char *buf, int buflen, const char *hname) { int slen = str_len(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)) { DUMP(); if (i && (buf[i - 1] && buf[i - 1] != '\n')) continue; DUMP(); if (buf[i + slen] != ':' || buf[i + slen + 1] != ' ') continue; DUMP(); c = buf + i + slen + 2; i += slen + 2; for (; i < buflen; ++i) { DUMP_c(buf[i]); if (buf[i] == 0 || buf[i] == '\n' || buf[i] == '\r') { buf[i] = 0; break; } } while (*c == ' ' || *c == '\t') ++c; return c; } } return 0; } static char *encoding = 0; static char *mimetype = "application/octet-stream"; static struct mimeentry { const char *name, *type; } mimetab[] = { { "html", "text/html; charset=UTF-8"}, { "htm", "text/html; charset=UTF-8"}, { "txt", "text/plain; charset=UTF-8"}, { "css", "text/css"}, { "ps", "application/postscript"}, { "pdf", "application/pdf"}, { "js", "application/javascript"}, { "gif", "image/gif"}, { "png", "image/png"}, { "jpeg", "image/jpeg"}, { "jpg", "image/jpeg"}, { "svg", "image/svg+xml"}, { "mpeg", "video/mpeg"}, { "mpg", "video/mpeg"}, { "avi", "video/x-msvideo"}, { "mov", "video/quicktime"}, { "qt", "video/quicktime"}, { "mp3", "audio/mpeg"}, { "ogg", "audio/ogg"}, { "wav", "audio/x-wav"}, { "epub", "application/epub+zip"}, { "dvi", "application/x-dvi"}, { "pac", "application/x-ns-proxy-autoconfig"}, { "sig", "application/pgp-signature"}, { "swf", "application/x-shockwave-flash"}, { "torrent", "application/x-bittorrent"}, { "tar", "application/x-tar"}, { "zip", "application/zip"}, { "dtd", "text/xml"}, { "xml", "text/xml"}, { "xbm", "image/x-xbitmap"}, { "xpm", "image/x-xpixmap"}, { "xwd", "image/x-xwindowdump"}, { "ico", "image/x-icon"}, { 0}}; /* * try to find out MIME type and content encoding. This is called twice, * once for the actual URL and once for URL.gz. If the actual URL already * ende with .gz, return application/octet-stream to make sure the client * can download the file even if he does not support gzip encoding */ static void getmimetype(char *url, int explicit) { char save; int ext; ext = str_len(url); while (ext > 0 && url[ext] != '.' && url[ext] != '/') --ext; if (url[ext] == '.') { ++ext; if (str_equal(url + ext, "bz2")) goto octetstream; if (str_equal(url + ext, "gz")) { if (!encoding) { if (explicit) goto octetstream; encoding = "gzip"; save = url[ext - 1]; url[ext - 1] = 0; getmimetype(url, explicit); url[ext - 1] = save; } else octetstream: mimetype = "application/octet-stream"; } else { int i; for (i = 0; mimetab[i].name; ++i) if (str_equal(mimetab[i].name, url + ext)) { mimetype = (char *) mimetab[i].type; break; } } } } static int matchcommalist(const char *needle, const char *haystack) { /* * needle: "text/html", haystack: the accept header, "text/html, * text/plain\r\n" */ /* * return nonzero if match was found */ int len = str_len(needle); if (!byte_equal(needle, len, haystack)) return 0; switch (haystack[len]) { case ';': case ',': case '\r': case '\n': case 0: return 1; } return 0; } static int findincommalist(const char *needle, const char *haystack) { const char *accept; for (accept = haystack; accept;) { /* * format: foo/bar, */ const char *tmp = accept; int final; while (*tmp) { if (*tmp == ';') break; else if (*tmp == ',') break; ++tmp; } final = (*tmp == 0 || *tmp == ';'); if (matchcommalist("*/*", accept)) break; if (matchcommalist(haystack, accept)) break; accept = tmp + 1; if (final) return 0; } return 1; } /* * timerfc function Copyright 1996, Michiel Boland. * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ static time_t timerfc(const char *s) { static const int daytab[2][12] = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} }; unsigned sec, min, hour, day, mon, year; char month[3]; int c; unsigned n; char flag; char state; char isctime; enum { D_START, D_END, D_MON, D_DAY, D_YEAR, D_HOUR, D_MIN, D_SEC }; sec = 60; min = 60; hour = 24; day = 32; year = 1969; isctime = 0; month[0] = 0; state = D_START; n = 0; flag = 1; do { c = *s++; switch (state) { case D_START: if (c == ' ') { state = D_MON; isctime = 1; } else if (c == ',') state = D_DAY; break; case D_MON: if (isalpha(c)) { if (n < 3) month[n++] = c; } else { if (n < 3) return -1; n = 0; state = isctime ? D_DAY : D_YEAR; } break; case D_DAY: if (c == ' ' && flag); else if (isdigit(c)) { flag = 0; n = 10 * n + (c - '0'); } else { day = n; n = 0; state = isctime ? D_HOUR : D_MON; } break; case D_YEAR: if (isdigit(c)) n = 10 * n + (c - '0'); else { year = n; n = 0; state = isctime ? D_END : D_HOUR; } break; case D_HOUR: if (isdigit(c)) n = 10 * n + (c - '0'); else { hour = n; n = 0; state = D_MIN; } break; case D_MIN: if (isdigit(c)) n = 10 * n + (c - '0'); else { min = n; n = 0; state = D_SEC; } break; case D_SEC: if (isdigit(c)) n = 10 * n + (c - '0'); else { sec = n; n = 0; state = isctime ? D_YEAR : D_END; } break; } } while (state != D_END && c); switch (month[0]) { case 'A': mon = (month[1] == 'p') ? 4 : 8; break; case 'D': mon = 12; break; case 'F': mon = 2; break; case 'J': mon = (month[1] == 'a') ? 1 : ((month[2] == 'l') ? 7 : 6); break; case 'M': mon = (month[2] == 'r') ? 3 : 5; break; case 'N': mon = 11; break; case 'O': mon = 10; break; case 'S': mon = 9; break; default: return -1; } if (year <= 100) year += (year < 70) ? 2000 : 1900; --mon; --day; if (sec >= 60 || min >= 60 || hour >= 60 || day >= 31) return -1; if (year < 1970) return 0; return sec + 60L * (min + 60L * (hour + 24L * (day + daytab[year % 4 == 0 && (year % 100 || year % 400 == 0)][mon] + 365L * (year - 1970L) + ((year - 1969L) >> 2)))); } static struct stat st; /* * try to return a file */ static int doit(char *buf, int buflen, char *url, int explicit) { int fd = -1; char *accept; while (url[0] == '/') ++url; getmimetype(url, explicit); { char *b = buf; int l = buflen; for (;;) { char *h = header(b, l, "Accept"); if (!h) goto ok; if (findincommalist(mimetype, h)) goto ok; l -= (h - b) + 1; b = h + 1; } retcode = 406; goto bad; } ok: if (encoding) { /* see if client accepts the encoding */ char *tmp = header(buf, buflen, "Accept-Encoding"); if (!tmp || !strstr(tmp, "gzip")) { retcode = 406; goto bad; } } if ((fd = open(url, O_RDONLY)) >= 0) { if (fstat(fd, &st)) goto bad; /* * no directories */ if (S_ISDIR(st.st_mode)) goto bad; /* * see if the peer accepts MIME type */ /* * see if the document has been changed */ { char *field = header(buf, buflen, "If-Modified-Since"); if (field) { time_t ims; ims = timerfc(field); if ((ims != (time_t) - 1) && (st.st_mtime <= ims)) { retcode = 304; goto bad; } } } rangestart = 0; rangeend = st.st_size; if ((accept = header(buf, buflen, "Range"))) { /* * format: "bytes=17-23", "bytes=23-" */ if (!strncmp(accept, "bytes=", 6)) { int i; accept += 6; i = scan_range(accept, &rangestart); if (i) { accept += i; if (*accept == '-') { ++accept; if (*accept) { i = scan_range(accept, &rangeend); if (!i) rangeend = st.st_size; else ++rangeend; } } } } if (rangestart > rangeend || rangeend > st.st_size) { retcode = 416; goto bad; } } return fd; bad: if (fd >= 0) close(fd); } return -1; } static void redirectboilerplate() { buffer_puts(buffer_1, "HTTP/1.0 301 Go Away\r\nConnection: close\r\nContent-Length: 0\r\nLocation: "); } static void 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) { /* * el-cheapo redirection */ redirectboilerplate(); buffer_put(buffer_1, symlink, len); #ifdef OLD_STYLE_REDIRECT fini: #endif retcode = 301; buffer_puts(buffer_1, "\r\n\r\n"); dolog(0); buffer_flush(buffer_1); exit(0); } #ifdef OLD_STYLE_REDIRECT if ((env = getenv("REDIRECT_HOST"))) { redirectboilerplate(); buffer_puts(buffer_1, env); while (*origurl == '/') ++origurl; buffer_puts(buffer_1, origurl); goto fini; } else if ((env = getenv("REDIRECT_URI"))) { redirectboilerplate(); buffer_puts(buffer_1, env); goto fini; } #endif } #ifdef DIR_LIST static void hdl_encode_html(const char *s, unsigned int sl) { int i; for (i = 0; i < sl; ++i) { unsigned char ch = s[i]; if (ch > 159) { encode_dec: buffer_puts(buffer_1, "&#"); buffer_putulong(buffer_1, ch); buffer_puts(buffer_1, ";"); } else if ((ch > 128) || (ch < 32)) { buffer_put(buffer_1, " ", 1); } else if (ch == '"') buffer_puts(buffer_1, """); else if (ch == '&') buffer_puts(buffer_1, "&"); else if (ch == '<') buffer_puts(buffer_1, "<"); else if (ch == '>') buffer_puts(buffer_1, ">"); else buffer_put(buffer_1, &ch, 1); } } static int buffer_puthex(unsigned int i) { unsigned int t; char x[4]; t = '0' | (i >> 4) & 0xf; if (t > '9') t += 39; i = '0' | (i & 0xf); if (i > '9') i += 39; x[0] = '%'; x[1] = t; x[2] = i; return buffer_put(buffer_1, x, 3); } static void hdl_encode_uri(const char *s, unsigned int sl) { int i; for (i = 0; i < sl; ++i) { unsigned char ch = s[i]; if ((ch != '%') && (ch > 32) && (ch < 127)) buffer_put(buffer_1, &ch, 1); else buffer_puthex(ch); } } static void handledirlist(const char *origurl) { DIR *dir; unsigned int nl = str_len(origurl); const char *nurl = origurl; url = (char *) origurl; while (nurl[0] == '/') ++nurl; if (nurl <= origurl) return; nl = str_len(nurl); if (nurl[nl - 1] != '/') return; if (!stat(nl ? nurl : ".", &st) && (S_ISDIR(st.st_mode)) && ((st.st_mode & S_IRWXO) == 5)) { if (nl) chdir(nurl); if (dir = opendir(".")) { struct dirent *de; unsigned int i, size = 32 + nl; buffer_puts(buffer_1, "HTTP/1.0 200 OK\r\nServer: " FNORD "\r\nConnection: close\r\n"); buffer_puts(buffer_1, "Content-Type: text/html\r\n"); buffer_puts(buffer_1, "\r\n

Directory Listing: /"); hdl_encode_html(nurl, nl); buffer_puts(buffer_1, "

\n
\n");
            if (nl != 0) {
                for (i = nl - 2; i > 0; --i)
                    if (nurl[i] == '/')
                        break;
                buffer_puts(buffer_1, " 0)
                    buffer_puts(buffer_1, "/");
                buffer_puts(buffer_1, "\">Parent directory");
                buffer_puts(buffer_1, "\n");
                size += 40 + i;
            }
            while (de = readdir(dir)) {
                char            symlink[1024];
                char           *p = de->d_name;
                unsigned int    pl,
                                dl = str_len(de->d_name);
                pl = dl;
                if (de->d_name[0] == '.')
                    continue;   /* hidden files -> skip */
                if (lstat(de->d_name, &st))
                    continue;   /* can't stat -> skip */
                if (S_ISDIR(st.st_mode))
                    buffer_puts(buffer_1, "[DIR] ");
                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;
                    }
                    buffer_puts(buffer_1, "[LNK] ");    /* a symlink to
                                                         * something ... */
                } else if (S_ISREG(st.st_mode))
                    buffer_puts(buffer_1, "[TXT] ");
                else
                    continue;   /* not a file we can provide -> skip */
                /*
                 * write a href 
                 */
                buffer_puts(buffer_1, "");
                if (de->d_name[0] == ':')
                    de->d_name[0] = '.';        /* fnord special ... */
                hdl_encode_html(de->d_name, dl);
                buffer_puts(buffer_1, "\n");
                size += 22 + (dl << 1);
            }
            closedir(dir);
            buffer_puts(buffer_1, "
\n"); buffer_flush(buffer_1); retcode = 200; dolog(size); exit(0); } } } #endif #ifdef INDEX_CGI static int handleindexcgi(const char *testurl, const char *origurl, char *space) { unsigned int ul, ol = str_len(origurl); char *test; while (testurl[0] == '/') ++testurl, --ol; ul = str_len(testurl); if (str_diff(testurl + ol, "index.html")) return 0; /* no request for index.html */ test = space; ++test; ul -= 4; byte_copy(test, ul, testurl); test[ul] = 'c'; test[++ul] = 'g'; test[++ul] = 'i'; test[++ul] = 0; if (stat(test, &st)) return 0; /* no index.cgi present */ ul = 1; if (st.st_gid == getegid()) ul = 010; if (st.st_uid == geteuid()) ul = 0100; if (!(st.st_mode & ul)) return 0; /* should be executable */ *(--test) = '/'; url = test; return 1; /* Wow... now start "index.cgi" */ } #endif static void get_ucspi_env(void) { char *ucspi = getenv("PROTO"); if (ucspi) { char *buf = alloca(str_len(ucspi) + 20); unsigned int tmp = str_copy(buf, ucspi); buf[tmp + str_copy(buf + tmp, "REMOTEIP")] = 0; remote_ip = getenv(buf); #ifdef CGI buf[tmp + str_copy(buf + tmp, "REMOTEPORT")] = 0; remote_port = getenv(buf); buf[tmp + str_copy(buf + tmp, "REMOTEINFO")] = 0; 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) { char tmp[4096]; struct pollfd duh; time_t now, fini; char *tmp2; int len; off_t todo = rangeend - rangestart; duh.fd = 1; duh.events = POLLOUT; if (rangestart) lseek(fd, rangestart, SEEK_SET); while (todo > 0) { int olen; fini = time(&now) + WRITETIMEOUT; len = read(fd, tmp, todo > 4096 ? 4096 : todo); olen = len; tmp2 = tmp; while (len > 0) { int written; switch (poll(&duh, 1, (fini - now) * 1000)) { case 0: if (now < fini) continue; /* fall through */ case -1: return 1; /* timeout or error */ } if ((written = write(1, tmp2, len)) < 0) return -1; len -= written; tmp2 += written; time(&now); } todo -= olen; } } static int serve_mmap(int fd) { off_t mapstart, maplen; unsigned long mapofs; char *map, *tmp2; struct pollfd duh; time_t now, fini; mapstart = rangestart & (~(off_t) 0xfff); /* round down to 4k page */ maplen = rangeend - mapstart; mapofs = rangestart - mapstart; if (maplen > 64 * 1024 * 1024) maplen = 64 * 1024 * 1024; map = mmap(0, maplen, PROT_READ, MAP_PRIVATE, fd, mapstart); if (map == MAP_FAILED) { if (errno == EINVAL && mapstart) { /* * try rounded to 64k pages */ mapstart = rangestart & 0xffff; maplen = rangeend - mapstart; mapofs = rangestart - mapstart; map = mmap(0, maplen, PROT_READ, MAP_PRIVATE, fd, mapstart); if (map == MAP_FAILED) /* * didn't work, use read/write instead. */ return serve_read_write(fd); } else return serve_read_write(fd); } duh.fd = 1; duh.events = POLLOUT; while (rangestart < rangeend) { int len; fini = time(&now) + WRITETIMEOUT; len = maplen - mapofs; tmp2 = map + mapofs; while (len > 0) { int written; switch (poll(&duh, 1, (fini - now) * 1000)) { case 0: if (now < fini) continue; /* fall through */ case -1: return 1; /* timeout or error */ } if ((written = write(1, tmp2, len)) < 0) return -1; len -= written; tmp2 += written; time(&now); } rangestart += maplen - mapofs; mapstart += maplen; munmap(map, maplen); mapofs = 0; maplen = rangeend - mapstart; if (maplen) { if (maplen > 64 * 1024 * 1024) maplen = 64 * 1024 * 1024; map = mmap(0, maplen, PROT_READ, MAP_SHARED, fd, mapstart); if (map == MAP_FAILED) /* * can't happen, really */ return serve_read_write(fd); } } return 0; } /* * write from offset "rangestart" to offset "rangeend" to fd #1 */ static int serve_static_data(int fd) { off_t len = rangeend - rangestart; #ifdef TCP_CORK corked = 0; #endif if (len < 4096) { /* for small files, sendfile is actually * slower */ char tmp[4096]; if (rangestart) lseek(fd, rangestart, SEEK_SET); read(fd, tmp, len); /* if read fails, we can't back down now. * We already committed on the * content-length */ buffer_put(buffer_1, tmp, len); buffer_flush(buffer_1); return 0; } #ifdef USE_SENDFILE { off_t offset = rangestart; #ifdef TCP_CORK { int one = 1; setsockopt(1, IPPROTO_TCP, TCP_CORK, &one, sizeof(one)); corked = 1; } #endif buffer_flush(buffer_1); { off_t l = rangeend - rangestart; do { off_t c; c = (l > (1ul << 31)) ? 1ul << 31 : l; if (sendfile(1, fd, &offset, c) == -1) #ifdef USE_MMAP return serve_mmap(fd); #else return serve_read_write(fd); #endif l -= c; } while (l); } return 0; } #else buffer_flush(buffer_1); #ifdef TCP_CORK { int one = 1; setsockopt(1, IPPROTO_TCP, TCP_CORK, &one, sizeof(one)); corked = 1; } #endif #ifdef USE_MMAP return serve_mmap(fd); #else return serve_read_write(fd); #endif #endif } int main(int argc, char *argv[], const char *const *envp) { char buf[MAXHEADERLEN]; #if 0 char buf2[MAXHEADERLEN]; #endif char *nurl, *origurl; int len; int in; if (argc > 1) chdir(argv[1]); #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; if (tmp[scan_ulong(tmp, &gid)] == 0) { gid_t gi = gid; if (setgroups(1, &gi)) goto error500; } else goto error500; } if ((tmp = getenv("UID"))) { long uid; if (tmp[scan_ulong(tmp, &uid)] == 0) { if (setuid(uid)) goto error500; } else goto error500; } } #endif signal(SIGPIPE, SIG_IGN); get_ucspi_env(); #ifdef KEEPALIVE handlenext: encoding = 0; #endif // alarm(20); { int found = 0; time_t fini, now; struct pollfd duh; fini = time(&now) + READTIMEOUT; duh.fd = 0; duh.events = POLLIN; for (in = len = 0; found < 2;) { int tmp; switch (poll(&duh, 1, READTIMEOUT * 1000)) { case 0: if (time(&now) < fini) continue; /* fall through */ case -1: /* timeout or error */ // badrequest(408,"Request Time-out","No request appeared // within a reasonable time period."); return 1; } tmp = read(0, buf + len, MAXHEADERLEN - len - 5); if (tmp < 0) return 1; if (tmp == 0) return 1; in += tmp; now = time(0); for (; (found < 2) && (len < in); ++len) { if (buf[len] == '\r') continue; if (buf[len] == '\n') ++found; else found = 0; if (found > 1) break; } } } if (len < 10) badrequest(400, "Bad Request", "Bad RequestThat does not look like HTTP to me..."); buf[len] = 0; if (!strncasecmp(buf, "GET /", 5)) { method = GET; url = buf + 4; } else if (!strncasecmp(buf, "POST /", 6)) { method = POST; url = buf + 5; } else if (!strncasecmp(buf, "HEAD /", 6)) { method = HEAD; url = buf + 5; } else badrequest(400, "Bad Request", "Bad RequestUnsupported HTTP method."); origurl = url; { int nl = str_chr(buf, '\r'); int space = str_chr(url, ' '); if (space >= nl) badrequest(400, "Bad Request", "Bad RequestHTTP/0.9 not supported"); if (str_diffn(url + space + 1, "HTTP/1.", 7)) badrequest(400, "Bad Request", "Bad RequestOnly HTTP 1.x supported"); url[space] = 0; httpversion = url[space + 8] - '0'; #ifdef KEEPALIVE keepalive = 0; #endif /* * demangle path in-place */ { register char *tmp, *d; for (tmp = d = url; *tmp; ++tmp) { if (*tmp == '?') { args = tmp + 1; break; } if (*tmp == ' ') break; if (*tmp == '%') { int a, b; a = fromhex(tmp[1]); b = fromhex(tmp[2]); if (a >= 0 && b >= 0) { *d = (a << 4) + b; tmp += 2; } else *d = *tmp; } else *d = *tmp; if (d > url + 1 && *d == '/' && d[-1] == ':' && d[-2] == '/') d -= 2; if (d > url && *d == '/' && d[-1] == '/') --d; if (d > url && *d == '.' && d[-1] == '/') *d = ':'; ++d; } *d = 0; /* * not good enough, we need a second pass */ } #ifdef CGI uri = alloca(space + 1); byte_copy(uri, space + 1, url); #endif } { char *tmp; 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" */ 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_miss = buf + len + 1; post_mlen = in - len - 1; if (post_len <= post_mlen) post_mlen = post_len; } } #endif } port = getenv("TCPLOCALPORT"); if (!port) port = "80"; { char *Buf; int i; host = header(buf, len, "Host"); if (!host) i = 100; else i = str_len(host) + 7; Buf = alloca(i); if (!host) { char *ip = getenv("TCPLOCALIP"); if (!ip) ip = "127.0.0.1"; if (str_len(ip) + str_len(port) > 90) exit(101); host = Buf; i = str_copy(Buf, ip); i += str_copy(Buf + i, ":"); i += str_copy(Buf + i, port); #ifdef NORMALIZE_HOST } else { int colon = str_chr(host, ':'); if (host[colon] == 0) { i = str_copy(Buf, host); i += str_copy(Buf + i, ":"); i += str_copy(Buf + i, port); host = Buf; } #endif } for (i = str_len(host); i >= 0; --i) if ((host[i] = tolower(host[i])) == '/') hostb0rken: badrequest(400, "Bad Request", "Bad RequestBullshit Host header"); 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] == '=') { buffer_put(buffer_1, symlink + 1, linklen - 1); } else { buffer_put(buffer_1, symlink, linklen); while (url[0] == '/') ++url; buffer_puts(buffer_1, url); } retcode = 301; buffer_puts(buffer_1, "\r\n\r\n"); dolog(0); buffer_flush(buffer_1); exit(0); } #endif if (chdir("default") && argc < 2) { badrequest(404, "Not Found", "Not FoundThis host is not served here."); } } } #ifdef AUTH { char *auth_script = ".http-auth"; struct stat st; if (!stat(auth_script, &st)) { pid_t child; const char *authorization; authorization = header(buf, len, "Authorization"); child = fork(); if (child < 0) { badrequest(500, "Internal Server Error", "Server Resource problem."); } else if (child == 0) { const char *argv[5] = { auth_script, host, url, authorization, NULL }; dup2(2, 1); execve(auth_script, argv, envp); _exit(1); } else { int status; pid_t childr; while ((childr = waitpid(child, &status, 0)) < 0 && errno == EINTR); if (childr != child) badrequest(500, "Internal Server Error", "Server system problem."); if (!WIFEXITED(status) || WEXITSTATUS(status)) { retcode = 401; dolog(0); buffer_puts(buffer_1, "HTTP/1.0 401 Authorization Required\r\n" "WWW-Authenticate: Basic realm=\""); buffer_puts(buffer_1, host); buffer_puts(buffer_1, "\"\r\nConnection: close\r\n\r\n" "Access to this site is restricted.\r\n" "Please provide credentials.\r\n"); buffer_flush(buffer_1); exit(0); } } } } #endif /* AUTH */ nurl = url + str_len(url); if (nurl > url) --nurl; if (*nurl == '/') { int i; nurl = alloca(str_len(url) + 12); i = str_copy(nurl, url); i += str_copy(nurl + i, "index.html"); nurl[i] = 0; url = nurl; nurl = url + i; } #ifdef CGI nurl -= 3; { char *tmp, *pathinfo; pathinfo = 0; for (tmp = url; tmp < nurl; ++tmp) if (findcgi(tmp)) { nurl = tmp; if (tmp[4] == '/') pathinfo = tmp + 4; break; } if (pathinfo) { int len = str_len(pathinfo) + 1; tmp = alloca(len); memcpy(tmp, pathinfo, len); *pathinfo = 0; pathinfo = tmp; } if (findcgi(nurl)) { int i; if ((method == HEAD)) badrequest(400, "Bad Request", "Illegal HTTP method for Gateway call."); #ifdef TCP_CORK { int one = 1; setsockopt(1, IPPROTO_TCP, TCP_CORK, &one, sizeof(one)); } #endif for (i = nurl - url; i > -1; --i) { if ((nurl[0] == '/') && (nurl[1] == 'n') && (nurl[2] == 'p') && (nurl[3] == 'h') && (nurl[4] == '-')) start_cgi(1, pathinfo, envp); /* start a NPH-CGI */ --nurl; } #ifdef INDEX_CGI indexcgi: #endif start_cgi(0, pathinfo, envp); /* start a CGI */ } } #endif { int fd; if ((fd = doit(buf, len, url, 1)) >= 0) { /* file was there */ /* * look if file.gz is also there and acceptable */ char *fnord = alloca(str_len(url) + 4); int i, fd2, trypng = 0; char *oldencoding = encoding; char *oldmimetype = mimetype; i = str_copy(fnord, url); if (i > 4 && str_equal(fnord + i - 4, ".gif")) { trypng = 1; str_copy(fnord + i - 3, "png"); } else str_copy(fnord + i, ".gz"); fd2 = doit(buf, len, fnord, 0); if (fd2 >= 0) { /* yeah! */ url = fnord; close(fd); fd = fd2; } else { encoding = oldencoding; if (trypng) mimetype = oldmimetype; } retcode = 200; dolog(st.st_size); if (rangestart || rangeend != st.st_size) buffer_puts(buffer_1, "HTTP/1.0 206 Partial Content\r\nServer: " FNORD "\r\nContent-Type: "); else buffer_puts(buffer_1, "HTTP/1.0 200 OK\r\nServer: " FNORD "\r\nContent-Type: "); buffer_puts(buffer_1, mimetype); buffer_puts(buffer_1, "\r\n"); #ifdef KEEPALIVE switch (keepalive) { case -1: buffer_puts(buffer_1, "Connection: close\r\n"); break; case 1: buffer_puts(buffer_1, "Connection: Keep-Alive\r\n"); break; } #endif if (encoding) { buffer_puts(buffer_1, "Content-Encoding: "); buffer_puts(buffer_1, encoding); buffer_puts(buffer_1, "\r\n"); } buffer_puts(buffer_1, "Content-Length: "); buffer_putrange(buffer_1, rangeend - rangestart); buffer_puts(buffer_1, "\r\nLast-Modified: "); { struct tm *x = gmtime(&st.st_mtime); /* * "Sun, 06 Nov 1994 08:49:37 GMT" */ buffer_put(buffer_1, days + 3 * x->tm_wday, 3); buffer_puts(buffer_1, ", "); buffer_put2digits(buffer_1, x->tm_mday); buffer_puts(buffer_1, " "); buffer_put(buffer_1, months + 3 * x->tm_mon, 3); buffer_puts(buffer_1, " "); buffer_put2digits(buffer_1, (x->tm_year + 1900) / 100); buffer_put2digits(buffer_1, (x->tm_year + 1900) % 100); buffer_puts(buffer_1, " "); buffer_put2digits(buffer_1, x->tm_hour); buffer_puts(buffer_1, ":"); buffer_put2digits(buffer_1, x->tm_min); buffer_puts(buffer_1, ":"); buffer_put2digits(buffer_1, x->tm_sec); buffer_puts(buffer_1, " GMT\r\n"); } if (rangestart || rangeend != st.st_size) { buffer_puts(buffer_1, "Accept-Ranges: bytes\r\nContent-Range: bytes "); buffer_putrange(buffer_1, rangestart); buffer_puts(buffer_1, "-"); buffer_putrange(buffer_1, rangeend - 1); buffer_puts(buffer_1, "/"); buffer_putrange(buffer_1, st.st_size); buffer_puts(buffer_1, "\r\n"); } buffer_puts(buffer_1, "\r\n"); if (method == GET || method == POST) { switch (serve_static_data(fd)) { case 0: break; case -1: goto error500; case 1: return 1; } #ifdef KEEPALIVE #ifdef TCP_CORK if (corked) { int zero = 0; setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero)); } #endif if (keepalive > 0) { close(fd); fchdir(rootdir); close(rootdir); goto handlenext; } #endif exit(0); error500: retcode = 500; } else buffer_flush(buffer_1); } } #ifdef CHROOT tuttikaputti: #endif switch (retcode) { case 404: { char *space = alloca(strlen(url) + 2); #ifdef INDEX_CGI if (handleindexcgi(url, origurl, space)) goto indexcgi; #endif handleredirect(url, origurl); #ifdef DIR_LIST handledirlist(origurl); #endif badrequest(404, "Not Found", "Not FoundNo such file or directory."); } case 406: badrequest(406, "Not Acceptable", "Not AcceptableNothing acceptable found."); case 416: badrequest(416, "Requested Range Not Satisfiable", ""); case 304: badrequest(304, "Not Changed", ""); case 500: badrequest(500, "Internal Server Error", ""); } return 1; }