diff --git a/fnord.c b/fnord.c index b7c0552..8341654 100644 --- a/fnord.c +++ b/fnord.c @@ -1,4 +1,6 @@ -/* simple httpd to be started from tcpserver */ +/* + * simple httpd to be started from tcpserver + */ #define _FILE_OFFSET_BITS 64 #include #include @@ -6,7 +8,7 @@ #include #include #include -//#include +#include #include #include #include @@ -19,17 +21,14 @@ #include #include #include -#include "fmt.h" -#include "buffer.h" -#include "byte.h" -#include "scan.h" -/* Some things I use for debugging */ +/* + * Some things I use for debugging + */ #ifdef DUMP -# include -# define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) +#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) #else -# define DUMPf(fmt, args...) +#define DUMPf(fmt, args...) #endif #define DUMP() DUMPf("") #define DUMP_d(v) DUMPf("%s = %d", #v, v) @@ -38,75 +37,98 @@ #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 */ +/* + * 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" */ +/* + * 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" */ +/* + * 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 */ +/* + * 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 */ +/* + * 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. */ +/* + * 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. */ +/* + * 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 */ +/* + * 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 */ +/* + * 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 */ +/* + * 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 */ +/* + * 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 */ +#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. */ +/* + * 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__ @@ -129,41 +151,40 @@ enum { UNKNOWN, GET, HEAD, POST } method; #ifdef TCP_CORK -static int corked; +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 */ +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 */ +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; +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 */ -#define scan_range scan_ulonglong -#define buffer_putrange buffer_putulonglong -#include "scan_ulonglong.c" -#include "fmt_ulonglong.c" -#include "buffer_putulonglong.c" +static unsigned long long rangestart, + rangeend; /* for ranged queries */ #else -static unsigned long rangestart, rangeend; /* for ranged queries */ +static unsigned long rangestart, + rangeend; /* for ranged queries */ #define scan_range scan_ulong #define buffer_putrange buffer_putulong #endif @@ -173,618 +194,686 @@ static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; #define MAXHEADERLEN 8192 -char* remote_ip; +char *remote_ip; #ifdef CGI -char* remote_port; -char* remote_ident; +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 +sanitize(char *ua) +{ /* replace strings with underscores for + * logging */ + int j; + for (j = 0; ua[j]; ++j) + if (isspace(ua[j])) + ua[j] = '_'; } -static int buffer_put2digits(buffer* b,unsigned int i) { - char x[2]; - x[0]=(i/10)+'0'; - x[1]=(i%10)+'0'; - return buffer_put(b,x,2); +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); } -static void dolog(off_t len) { /* write a log line to stderr */ -#ifdef COLF - time_t t=time(0); - struct tm* x=localtime(&t); - int l=-(timezone/60); - buffer_puts(buffer_2,remote_ip?remote_ip:"0.0.0.0"); - buffer_puts(buffer_2," - - ["); - - buffer_put2digits(buffer_2,x->tm_mday); - buffer_puts(buffer_2,"/"); - buffer_put(buffer_2,months+3*x->tm_mon,3); - buffer_puts(buffer_2,"/"); - buffer_put2digits(buffer_2,(x->tm_year+1900)/100); - buffer_put2digits(buffer_2,(x->tm_year+1900)%100); - buffer_puts(buffer_2,":"); - buffer_put2digits(buffer_2,x->tm_hour); - buffer_puts(buffer_2,":"); - buffer_put2digits(buffer_2,x->tm_min); - buffer_puts(buffer_2,":"); - buffer_put2digits(buffer_2,x->tm_sec); - buffer_puts(buffer_2,l>=0?" +":" -"); - if (l<0) l=-l; - buffer_put2digits(buffer_2,l/60); - buffer_put2digits(buffer_2,l%60); - buffer_puts(buffer_2,"] \""); - switch (method) { - case GET: buffer_puts(buffer_2,"GET "); break; - case POST: buffer_puts(buffer_2,"POST "); break; - case HEAD: buffer_puts(buffer_2,"HEAD "); break; - default: buffer_puts(buffer_2,"? "); break; - } - buffer_puts(buffer_2,url); - buffer_puts(buffer_2,httpversion?" HTTP/1.1\" ":" HTTP/1.0\" "); - buffer_putulong(buffer_2,retcode); - buffer_putspace(buffer_2); - buffer_putrange(buffer_2,len); - -#else - buffer_puts(buffer_2,remote_ip?remote_ip:"0.0.0.0"); - buffer_putspace(buffer_2); - buffer_putulong(buffer_2,retcode); - buffer_putspace(buffer_2); - buffer_putrange(buffer_2,len); - buffer_putspace(buffer_2); - sanitize(host); - buffer_puts(buffer_2,host); - buffer_putspace(buffer_2); - sanitize(ua); - buffer_puts(buffer_2,ua); - buffer_putspace(buffer_2); - if (!refer) refer="none"; - sanitize(refer); - buffer_puts(buffer_2,refer); - buffer_putspace(buffer_2); - if (url) - buffer_puts(buffer_2,url); - else - buffer_puts(buffer_2,"(null)"); -#endif - buffer_puts(buffer_2,"\n"); - buffer_flush(buffer_2); -} - -/* 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); +/* + * 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=" + "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 (;i0) { - 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 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 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; +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]; } - final=(*tmp==0 || *tmp==';'); - if (matchcommalist("*/*",accept)) break; - if (matchcommalist(haystack,accept)) break; - accept=tmp+1; - if (final) return 0; - } - return 1; + 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; } /* @@ -821,1001 +910,1230 @@ static int findincommalist(const char* needle,const char* haystack) { * 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 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 }; + 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)))); + 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 */ +/* + * 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 *field = header(buf,buflen,"If-Modified-Since"); + 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; } - } + 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); } - 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; + 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 +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; +static void +handleredirect(const char *url, const char *origurl) +{ + char symlink[1024]; + int len; #ifdef OLD_STYLE_REDIRECT - char* env; + char *env; #endif - while (*url=='/') ++url; - if ((len=readlink(url,symlink,1023))>0) { - /* el-cheapo redirection */ - redirectboilerplate(); - buffer_put(buffer_1,symlink,len); + 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: + fini: #endif - retcode=301; - buffer_puts(buffer_1,"\r\n\r\n"); - dolog(0); - buffer_flush(buffer_1); - exit(0); - } + 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; - } + 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;i159) { -encode_dec: - buffer_puts(buffer_1,"&#"); - buffer_putulong(buffer_1,ch); - buffer_puts(buffer_1,";"); +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); } - else if ((ch>128)||(ch<32)) { - buffer_put(buffer_1," ",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); } - 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;i32)&&(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)) {
+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 */
+                    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); + { + 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" */ +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); +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); + 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)); +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 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; } - 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 (rangestart0) { - int written; - switch (poll(&duh,1,(fini-now)*1000)) { - case 0: if (now 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); } - 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); + 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; - } +} + +/* + * 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; - } + 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); - { - 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; - } + { + int one = 1; + setsockopt(1, IPPROTO_TCP, TCP_CORK, &one, sizeof(one)); + corked = 1; + } #endif #ifdef USE_MMAP - return serve_mmap(fd); + return serve_mmap(fd); #else - return serve_read_write(fd); + return serve_read_write(fd); #endif #endif } -int main(int argc,char *argv[],const char *const *envp) { - char buf[MAXHEADERLEN]; +int +main(int argc, char *argv[], const char *const *envp) +{ + char buf[MAXHEADERLEN]; #if 0 - char buf2[MAXHEADERLEN]; + char buf2[MAXHEADERLEN]; #endif - char *nurl,*origurl; - int len; - int in; + char *nurl, + *origurl; + int len; + int in; - if (argc>1) chdir(argv[1]); + 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)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 + if (chroot(".")) { + if (errno != EPERM) + goto error500; + /* + * else fnord was called with uid!=0, i.e. it already is chroot + */ } 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 + 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; + } } - 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); +#endif + signal(SIGPIPE, SIG_IGN); + get_ucspi_env(); + #ifdef KEEPALIVE - if (keepalive>0) { - if ((rootdir=open(".",O_RDONLY))<0) - keepalive=-1; - } + handlenext: + encoding = 0; #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; + // alarm(20); - 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-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."); + 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; + } + } } - 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; + 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; }