2012-02-13 20:54:27 -07:00
|
|
|
/*
|
|
|
|
* simple httpd to be started from tcpserver
|
|
|
|
*/
|
2011-08-16 14:36:11 -06:00
|
|
|
#define _FILE_OFFSET_BITS 64
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <time.h>
|
2012-02-13 20:54:27 -07:00
|
|
|
#include <stdio.h>
|
2011-08-16 14:36:11 -06:00
|
|
|
#include <string.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <errno.h>
|
2012-02-23 22:53:26 -07:00
|
|
|
#include <sys/select.h>
|
2011-08-16 14:36:11 -06:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <limits.h>
|
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
#ifdef __linux__
|
|
|
|
# include <sys/sendfile.h>
|
|
|
|
#else
|
|
|
|
# define sendfile(a, b, c, d) -1
|
|
|
|
#endif
|
|
|
|
|
2012-02-23 22:53:26 -07:00
|
|
|
#ifndef min
|
|
|
|
#define min(a,b) ((a)<(b)?(a):(b))
|
|
|
|
#endif
|
|
|
|
|
2012-02-13 20:54:27 -07:00
|
|
|
/*
|
|
|
|
* Some things I use for debugging
|
|
|
|
*/
|
|
|
|
#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
|
2011-10-12 17:21:32 -06:00
|
|
|
#define DUMP() DUMPf("")
|
|
|
|
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
|
2012-02-17 17:50:05 -07:00
|
|
|
#define DUMP_u(v) DUMPf("%s = %u", #v, v)
|
2011-10-12 17:21:32 -06:00
|
|
|
#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)
|
2012-02-17 17:50:05 -07:00
|
|
|
#define DUMP_buf(v, l) DUMPf("%s = %.*s", #v, (int)(l), v)
|
2011-10-12 17:21:32 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/* Wait this long (seconds) for a valid HTTP request */
|
2012-02-17 08:24:04 -07:00
|
|
|
#define READTIMEOUT 2
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/* Wait this long trying to write out the response */
|
2011-08-16 14:36:11 -06:00
|
|
|
#define WRITETIMEOUT 20
|
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/* Wait this long for CGI to complete */
|
|
|
|
#define CGI_TIMEOUT (5*60)
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/* Maximum size of a request header (the whole block) */
|
|
|
|
#define MAXHEADERLEN 8192
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/* Maximum size of a request line */
|
|
|
|
#define MAXREQUESTLEN 2048
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-03-06 17:27:56 -07:00
|
|
|
/* Maximum number of headers */
|
|
|
|
#define MAXHEADERS 40
|
|
|
|
|
2012-02-23 22:53:26 -07:00
|
|
|
/*
|
2012-02-29 17:22:09 -07:00
|
|
|
* Options
|
2012-02-23 22:53:26 -07:00
|
|
|
*/
|
2012-02-29 17:22:09 -07:00
|
|
|
int doauth = 0;
|
|
|
|
int docgi = 0;
|
|
|
|
int dirlist = 0;
|
|
|
|
int redirect = 0;
|
|
|
|
int portappend = 0;
|
|
|
|
|
|
|
|
/* Variables that persist between requests */
|
|
|
|
int keepalive = 0;
|
|
|
|
char *remote_ip = NULL;
|
|
|
|
char *remote_port = NULL;
|
|
|
|
char *remote_ident = NULL;
|
|
|
|
|
|
|
|
/* Things that are really super convenient to have globally */
|
|
|
|
char *host;
|
|
|
|
char *user_agent;
|
|
|
|
char *refer;
|
|
|
|
char *path;
|
|
|
|
int http_version;
|
2011-08-16 14:36:11 -06:00
|
|
|
|
|
|
|
static const char days[] = "SunMonTueWedThuFriSat";
|
|
|
|
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
|
|
|
|
2012-02-23 22:53:26 -07:00
|
|
|
#define BUFFER_SIZE 8192
|
|
|
|
char stdout_buf[BUFFER_SIZE];
|
2012-02-15 16:34:21 -07:00
|
|
|
|
2012-02-24 17:03:09 -07:00
|
|
|
/*
|
|
|
|
* TCP_CORK is a Linux extension to work around a TCP problem.
|
|
|
|
* http://www.baus.net/on-tcp_cork has a good description.
|
|
|
|
* XXX: Since we do our own buffering, TCP_CORK may not be helping
|
|
|
|
* with anything. This needs testing.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
cork(int enable)
|
|
|
|
{
|
|
|
|
#ifdef TCP_CORK
|
|
|
|
static int corked = 0;
|
|
|
|
|
|
|
|
if (enable != corked) {
|
|
|
|
setsockopt(1, IPPROTO_TCP, TCP_CORK, &enable, sizeof(enable));
|
|
|
|
corked = enable;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
|
|
|
|
/** Replace whitespace with underscores for logging */
|
2012-02-13 20:54:27 -07:00
|
|
|
static void
|
2012-02-29 17:22:09 -07:00
|
|
|
sanitize(char *s)
|
|
|
|
{
|
|
|
|
if (!s) {
|
2012-02-15 16:19:02 -07:00
|
|
|
return;
|
2012-02-29 17:22:09 -07:00
|
|
|
}
|
|
|
|
for (; *s; s += 1) {
|
|
|
|
if (isspace(*s)) {
|
|
|
|
*s = '_';
|
|
|
|
}
|
|
|
|
}
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/** Log a request */
|
2012-02-13 20:54:27 -07:00
|
|
|
static void
|
2012-02-29 17:22:09 -07:00
|
|
|
dolog(int code, off_t len)
|
2012-02-13 20:54:27 -07:00
|
|
|
{ /* write a log line to stderr */
|
|
|
|
sanitize(host);
|
2012-02-29 17:22:09 -07:00
|
|
|
sanitize(user_agent);
|
2012-02-13 22:49:10 -07:00
|
|
|
sanitize(refer);
|
2012-02-13 20:54:27 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
fprintf(stderr, "%s %d %lu %s %s %s %s\n",
|
|
|
|
remote_ip, code, (unsigned long) len, host, user_agent, refer, path);
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-02-13 20:54:27 -07:00
|
|
|
/*
|
|
|
|
* output an error message and exit
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
badrequest(long code, const char *httpcomment, const char *message)
|
|
|
|
{
|
2012-02-29 17:22:09 -07:00
|
|
|
size_t msglen = 0;
|
|
|
|
|
2012-02-14 17:23:32 -07:00
|
|
|
printf("HTTP/1.0 %ld %s\r\nConnection: close\r\n", code, httpcomment);
|
2012-02-17 17:50:05 -07:00
|
|
|
if (message) {
|
2012-02-29 17:22:09 -07:00
|
|
|
msglen = (strlen(message) * 2) + 15;
|
|
|
|
|
2012-02-14 17:23:32 -07:00
|
|
|
printf("Content-Length: %lu\r\nContent-Type: text/html\r\n\r\n",
|
2012-02-29 17:22:09 -07:00
|
|
|
(unsigned long) msglen);
|
2012-02-17 17:50:05 -07:00
|
|
|
printf("<title>%s</title>%s", message, message);
|
2012-02-13 20:54:27 -07:00
|
|
|
} else {
|
2012-02-14 17:23:32 -07:00
|
|
|
fputs("\r\n", stdout);
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-02-13 22:49:10 -07:00
|
|
|
fflush(stdout);
|
2012-02-29 17:22:09 -07:00
|
|
|
dolog(code, msglen);
|
|
|
|
|
2012-02-13 20:54:27 -07:00
|
|
|
exit(0);
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
|
|
|
|
/** Parse a header out of a line.
|
|
|
|
*
|
|
|
|
* This capitalizes the header name, and strips trailing [\r\n]
|
|
|
|
* Returns the length of the line (after stripping)
|
|
|
|
*/
|
|
|
|
size_t
|
|
|
|
extract_header_field(char *buf, char **val, int cgi)
|
|
|
|
{
|
2012-03-06 21:09:25 -07:00
|
|
|
size_t pos = 0; /* Input */
|
|
|
|
size_t len = 0; /* Output */
|
2012-02-29 17:22:09 -07:00
|
|
|
|
|
|
|
for (len = 0; buf[len]; len += 1) {
|
|
|
|
if (! *val) {
|
|
|
|
if (buf[len] == ':') {
|
|
|
|
buf[len] = 0;
|
|
|
|
for (*val = &(buf[len+1]); **val == ' '; *val += 1);
|
|
|
|
} else if (cgi) {
|
|
|
|
switch (buf[len]) {
|
|
|
|
case 'a'...'z':
|
|
|
|
buf[len] ^= ' ';
|
|
|
|
break;
|
|
|
|
case 'A'...'Z':
|
|
|
|
case '0'...'9':
|
|
|
|
case '\r':
|
|
|
|
case '\n':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
buf[len] = '_';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; (buf[len-1] == '\n') || (buf[len-1] == '\r'); len -= 1);
|
|
|
|
buf[len] = 0;
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-03-06 21:09:25 -07:00
|
|
|
int
|
2012-02-13 20:54:27 -07:00
|
|
|
fromhex(int c)
|
|
|
|
{
|
|
|
|
if (c >= '0' && c <= '9')
|
|
|
|
return c - '0';
|
|
|
|
else {
|
|
|
|
c |= ' ';
|
|
|
|
if (c >= 'a' && c <= 'f')
|
|
|
|
return c - 'a' + 10;
|
|
|
|
}
|
|
|
|
return -1;
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-02-13 20:54:27 -07:00
|
|
|
/*
|
|
|
|
* header(buf,buflen,"User-Agent")="Mozilla"
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
header(char *buf, int buflen, const char *hname)
|
|
|
|
{
|
2012-02-13 22:49:10 -07:00
|
|
|
int slen = strlen(hname);
|
2012-02-13 20:54:27 -07:00
|
|
|
int i;
|
|
|
|
char *c;
|
|
|
|
|
|
|
|
for (i = 0; i < buflen - slen - 2; ++i) {
|
|
|
|
if (!strncasecmp(buf + i, hname, slen)) {
|
|
|
|
if (i && (buf[i - 1] && buf[i - 1] != '\n'))
|
|
|
|
continue;
|
|
|
|
if (buf[i + slen] != ':' || buf[i + slen + 1] != ' ')
|
|
|
|
continue;
|
|
|
|
c = buf + i + slen + 2;
|
|
|
|
i += slen + 2;
|
|
|
|
for (; i < buflen; ++i) {
|
|
|
|
if (buf[i] == 0 || buf[i] == '\n' || buf[i] == '\r') {
|
|
|
|
buf[i] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (*c == ' ' || *c == '\t')
|
|
|
|
++c;
|
|
|
|
return c;
|
|
|
|
}
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
2012-02-13 20:54:27 -07:00
|
|
|
return 0;
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-02-13 20:54:27 -07:00
|
|
|
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"}, {
|
2012-02-21 17:11:46 -07:00
|
|
|
0, 0}};
|
|
|
|
|
|
|
|
static const char *default_mimetype = "application/octet-stream";
|
2012-02-13 20:54:27 -07:00
|
|
|
|
|
|
|
/*
|
2012-02-21 17:11:46 -07:00
|
|
|
* Determine MIME type from file extension
|
2012-02-13 20:54:27 -07:00
|
|
|
*/
|
2012-02-21 17:11:46 -07:00
|
|
|
static const char *
|
|
|
|
getmimetype(char *url)
|
2012-02-13 20:54:27 -07:00
|
|
|
{
|
2012-02-21 17:11:46 -07:00
|
|
|
char *ext = strrchr(url, '.');
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-17 17:50:05 -07:00
|
|
|
|
2012-02-21 17:11:46 -07:00
|
|
|
if (ext) {
|
|
|
|
int i;
|
2012-02-17 17:50:05 -07:00
|
|
|
|
2012-02-21 17:11:46 -07:00
|
|
|
ext++;
|
|
|
|
for (i = 0; mimetab[i].name; ++i) {
|
|
|
|
if (!strcmp(mimetab[i].name, ext)) {
|
|
|
|
return mimetab[i].type;
|
|
|
|
}
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
2012-02-21 17:11:46 -07:00
|
|
|
return default_mimetype;
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-02-21 17:11:46 -07:00
|
|
|
|
2011-08-16 15:16:03 -06:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2012-02-13 22:49:10 -07:00
|
|
|
static time_t
|
2012-02-13 20:54:27 -07:00
|
|
|
timerfc(const char *s)
|
2011-08-16 15:16:03 -06:00
|
|
|
{
|
2012-02-13 20:54:27 -07:00
|
|
|
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}
|
|
|
|
};
|
2012-02-29 17:22:09 -07:00
|
|
|
unsigned sec,
|
|
|
|
min,
|
|
|
|
hour,
|
|
|
|
day,
|
2012-02-13 20:54:27 -07:00
|
|
|
mon,
|
2012-02-29 17:22:09 -07:00
|
|
|
year;
|
|
|
|
char month[3];
|
2012-02-13 20:54:27 -07:00
|
|
|
int c;
|
2012-02-29 17:22:09 -07:00
|
|
|
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 };
|
2012-02-13 20:54:27 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
sec = 60;
|
|
|
|
min = 60;
|
|
|
|
hour = 24;
|
|
|
|
day = 32;
|
|
|
|
year = 1969;
|
|
|
|
isctime = 0;
|
|
|
|
month[0] = 0;
|
|
|
|
state = D_START;
|
|
|
|
n = 0;
|
|
|
|
flag = 1;
|
2012-02-13 20:54:27 -07:00
|
|
|
do {
|
|
|
|
c = *s++;
|
|
|
|
switch (state) {
|
2012-02-17 17:50:05 -07:00
|
|
|
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;
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
2012-02-17 17:50:05 -07:00
|
|
|
case 'D':
|
|
|
|
mon = 12;
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
2012-02-17 17:50:05 -07:00
|
|
|
case 'F':
|
|
|
|
mon = 2;
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
2012-02-17 17:50:05 -07:00
|
|
|
case 'J':
|
|
|
|
mon = (month[1] == 'a') ? 1 : ((month[2] == 'l') ? 7 : 6);
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
2012-02-17 17:50:05 -07:00
|
|
|
case 'M':
|
|
|
|
mon = (month[2] == 'r') ? 3 : 5;
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
2012-02-17 17:50:05 -07:00
|
|
|
case 'N':
|
|
|
|
mon = 11;
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
2012-02-17 17:50:05 -07:00
|
|
|
case 'O':
|
|
|
|
mon = 10;
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
2012-02-17 17:50:05 -07:00
|
|
|
case 'S':
|
|
|
|
mon = 9;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
|
|
|
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))));
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
void
|
|
|
|
get_ucspi_env()
|
2012-02-13 20:54:27 -07:00
|
|
|
{
|
2012-02-29 17:22:09 -07:00
|
|
|
char *ucspi = getenv("PROTO");
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
if (ucspi) {
|
|
|
|
int protolen = strlen(ucspi);
|
|
|
|
char buf[80];
|
2012-02-13 22:49:10 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
if (protolen > 20) {
|
|
|
|
return;
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-02-29 17:22:09 -07:00
|
|
|
strcpy(buf, ucspi);
|
2012-02-15 16:19:02 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
strcpy(buf + protolen, "REMOTEIP");
|
|
|
|
remote_ip = getenv(buf);
|
2012-02-15 16:34:21 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
strcpy(buf + protolen, "REMOTEPORT");
|
|
|
|
remote_port = getenv(buf);
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
strcpy(buf + protolen, "REMOTEINFO");
|
|
|
|
remote_ident = getenv(buf);
|
|
|
|
}
|
|
|
|
}
|
2012-02-17 17:50:05 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
void
|
|
|
|
parse_options(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int opt;
|
2012-02-13 20:54:27 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
while (-1 != (opt = getopt(argc, argv, "acdhkprv"))) {
|
|
|
|
switch (opt) {
|
|
|
|
case 'a':
|
|
|
|
doauth = 1;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
docgi = 1;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
dirlist = 1;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
portappend = 1;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
redirect = 1;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
printf(FNORD "\n");
|
|
|
|
exit(0);
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Usage: %s [OPTIONS]\n",
|
|
|
|
argv[0]);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "-a Enable authentication\n");
|
|
|
|
fprintf(stderr, "-c Enable CGI\n");
|
|
|
|
fprintf(stderr, "-d Enable directory listing\n");
|
|
|
|
fprintf(stderr, "-p Append port to hostname directory\n");
|
|
|
|
fprintf(stderr, "-r Enable symlink redirection\n");
|
|
|
|
fprintf(stderr, "-v Print version and exit\n");
|
|
|
|
exit(69);
|
|
|
|
}
|
2012-02-17 17:50:05 -07:00
|
|
|
}
|
2012-02-29 17:22:09 -07:00
|
|
|
}
|
2012-02-13 20:54:27 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
enum { GET, POST, HEAD };
|
2012-02-17 17:50:05 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
void
|
|
|
|
handle_request()
|
|
|
|
{
|
2012-03-06 17:27:56 -07:00
|
|
|
char request[MAXREQUESTLEN];
|
|
|
|
char fspath[MAXREQUESTLEN];
|
|
|
|
char buf[MAXHEADERLEN];
|
|
|
|
char *env[MAXHEADERS + 1];
|
2012-02-29 17:22:09 -07:00
|
|
|
char *p;
|
|
|
|
char *query_string = NULL;
|
2012-03-06 17:27:56 -07:00
|
|
|
int method;
|
|
|
|
time_t ims = 0;
|
2012-02-29 17:22:09 -07:00
|
|
|
|
|
|
|
host = NULL;
|
|
|
|
user_agent = NULL;
|
|
|
|
refer = NULL;
|
|
|
|
path = NULL;
|
|
|
|
|
|
|
|
alarm(READTIMEOUT);
|
|
|
|
|
|
|
|
/* Read request line first */
|
|
|
|
request[0] = 0;
|
|
|
|
fgets(request, sizeof request, stdin);
|
|
|
|
if (!strncmp(request, "GET /", 5)) {
|
2012-02-13 20:54:27 -07:00
|
|
|
method = GET;
|
2012-02-29 17:22:09 -07:00
|
|
|
p = request + 5;
|
|
|
|
} else if (!strncmp(request, "POST /", 6)) {
|
2012-02-13 20:54:27 -07:00
|
|
|
method = POST;
|
2012-02-29 17:22:09 -07:00
|
|
|
p = request + 6;
|
|
|
|
} else if (!strncmp(request, "HEAD /", 6)) {
|
2012-02-13 20:54:27 -07:00
|
|
|
method = HEAD;
|
2012-02-29 17:22:09 -07:00
|
|
|
p = request + 6;
|
|
|
|
} else {
|
|
|
|
/* This also handles the case where fgets does nothing */
|
2012-02-17 17:50:05 -07:00
|
|
|
badrequest(405, "Method Not Allowed", "Unsupported HTTP method.");
|
2012-02-29 17:22:09 -07:00
|
|
|
}
|
2012-02-13 20:54:27 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/* Interpret path into fspath */
|
|
|
|
path = p - 1;
|
2012-02-13 20:54:27 -07:00
|
|
|
{
|
2012-02-29 17:22:09 -07:00
|
|
|
char *fsp = fspath;
|
2012-02-17 17:50:05 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
*(fsp++) = '/';
|
|
|
|
for (; *p != ' '; p += 1) {
|
|
|
|
if (! query_string) {
|
|
|
|
char c = *p;
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
switch (c) {
|
|
|
|
case 0:
|
|
|
|
badrequest(413, "Request Entity Too Large", "The HTTP request was too long");
|
|
|
|
case '\n':
|
|
|
|
badrequest(505, "Version Not Supported", "HTTP/0.9 not supported");
|
|
|
|
case '?':
|
|
|
|
query_string = p + 1;
|
|
|
|
break;
|
|
|
|
case '.':
|
|
|
|
if (p[-1] == '/') {
|
|
|
|
c = ':';
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '%':
|
|
|
|
if (p[1] && p[2]) {
|
|
|
|
int a = fromhex(p[1]);
|
|
|
|
int b = fromhex(p[2]);
|
|
|
|
|
|
|
|
if ((a >= 0) && (b >= 0)) {
|
|
|
|
c = (a << 4) | b;
|
|
|
|
p += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-02-29 17:22:09 -07:00
|
|
|
|
|
|
|
*(fsp++) = c;
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
|
|
|
}
|
2012-02-29 17:22:09 -07:00
|
|
|
*fsp = 0;
|
|
|
|
DUMP_s(fspath);
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-02-29 17:22:09 -07:00
|
|
|
*(p++) = 0; /* NULL-terminate path */
|
|
|
|
DUMP_s(path);
|
2011-08-16 14:36:11 -06:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
http_version = -1;
|
|
|
|
if (! strncmp(p, "HTTP/1.", 7) && (p[8] || (p[8] == '\r') || (p[8] == '\n'))) {
|
|
|
|
http_version = p[7] - '0';
|
|
|
|
}
|
|
|
|
if (! ((http_version == 0) || (http_version == 1))) {
|
|
|
|
badrequest(505, "Version Not Supported", "HTTP version not supported");
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-03-06 17:27:56 -07:00
|
|
|
if (http_version == 1) {
|
|
|
|
keepalive = 1;
|
|
|
|
} else {
|
|
|
|
keepalive = 0;
|
|
|
|
}
|
2012-02-13 20:54:27 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
/* Read header fields */
|
2012-02-13 20:54:27 -07:00
|
|
|
{
|
2012-03-06 17:27:56 -07:00
|
|
|
char *base = buf;
|
|
|
|
size_t nheaders = 0;
|
2012-02-29 17:22:09 -07:00
|
|
|
|
|
|
|
while (1) {
|
2012-03-06 17:27:56 -07:00
|
|
|
char *cgi_name = base;
|
2012-02-29 17:22:09 -07:00
|
|
|
char *p;
|
2012-03-06 17:27:56 -07:00
|
|
|
int plen = (sizeof buf) - (base - buf);
|
2012-02-29 17:22:09 -07:00
|
|
|
char *name, *val;
|
|
|
|
size_t len;
|
|
|
|
|
2012-03-06 17:27:56 -07:00
|
|
|
/* 40 is totally arbitrary here. */
|
2012-02-29 17:22:09 -07:00
|
|
|
if (plen < 40) {
|
|
|
|
badrequest(431, "Request Header Too Large", "The HTTP header block was too large");
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-03-06 17:27:56 -07:00
|
|
|
if (nheaders >= MAXHEADERS) {
|
|
|
|
badrequest(431, "Request Header Too Large", "Too many HTTP Headers");
|
|
|
|
}
|
2012-02-29 17:22:09 -07:00
|
|
|
strcpy(cgi_name, "HTTP_");
|
|
|
|
plen -= 5;
|
|
|
|
p = cgi_name + 5;
|
2012-02-21 17:11:46 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
if (NULL == fgets(p, plen, stdin)) {
|
|
|
|
badrequest(500, "OS Error", "OS error reading headers");
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-02-21 17:11:46 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
len = extract_header_field(p, &val, 1);
|
|
|
|
if (! len) {
|
|
|
|
/* blank line */
|
2012-02-13 20:54:27 -07:00
|
|
|
break;
|
|
|
|
}
|
2012-02-17 17:50:05 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
name = p;
|
|
|
|
if (! val) {
|
|
|
|
badrequest(400, "Invalid header", "Unable to parse header block");
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-02-29 17:22:09 -07:00
|
|
|
|
2012-03-06 17:27:56 -07:00
|
|
|
if (! strcmp(name, "HOST")) {
|
|
|
|
host = val;
|
|
|
|
} else if (! strcmp(name, "USER_AGENT")) {
|
|
|
|
user_agent = val;
|
|
|
|
} else if (! strcmp(name, "REFERER")) {
|
|
|
|
refer = val;
|
|
|
|
} else if (! strcmp(name, "CONNECTION")) {
|
|
|
|
if (! strcasecmp(val, "keep-alive")) {
|
|
|
|
keepalive = 1;
|
|
|
|
} else {
|
|
|
|
keepalive = 0;
|
|
|
|
}
|
|
|
|
} else if (! strcmp(name, "IF_MODIFIED_SINCE")) {
|
|
|
|
ims = timerfc(val);
|
2012-02-29 17:22:09 -07:00
|
|
|
}
|
2012-03-06 17:27:56 -07:00
|
|
|
|
2012-03-06 21:09:25 -07:00
|
|
|
/* Set up CGI environment variables */
|
2012-03-06 17:27:56 -07:00
|
|
|
{
|
|
|
|
char *d = name + strlen(name);
|
|
|
|
char *s = val;
|
|
|
|
|
|
|
|
*(d++) = '=';
|
|
|
|
while (*s) {
|
|
|
|
*(d++) = *(s++);
|
|
|
|
}
|
|
|
|
*d = '\0';
|
|
|
|
DUMP_s(cgi_name);
|
2012-02-29 17:22:09 -07:00
|
|
|
|
2012-03-06 17:27:56 -07:00
|
|
|
env[nheaders++] = cgi_name;
|
|
|
|
base = d + 1;
|
|
|
|
}
|
2012-02-13 20:54:27 -07:00
|
|
|
}
|
2012-03-06 17:27:56 -07:00
|
|
|
env[nheaders] = NULL;
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|
|
|
|
|
2012-03-06 21:09:25 -07:00
|
|
|
/* Try to change into the appropriate directory */
|
|
|
|
{
|
|
|
|
char fn[PATH_MAX];
|
|
|
|
|
|
|
|
strncpy(fn, host, sizeof fn);
|
|
|
|
if (fn[0] == '.') {
|
|
|
|
fn[0] = ':';
|
|
|
|
}
|
|
|
|
for (p = fn; *p; p += 1) {
|
|
|
|
switch (*p) {
|
|
|
|
case '/':
|
|
|
|
*p = ':';
|
|
|
|
break;
|
|
|
|
case ':':
|
|
|
|
*p = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((-1 == chdir(fn)) && (-1 == chdir("default"))) {
|
|
|
|
badrequest(404, "Not Found", "This host is not served here");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
return;
|
|
|
|
}
|
2012-02-24 17:03:09 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
int
|
|
|
|
main(int argc, char *argv[], const char *const *envp)
|
|
|
|
{
|
2012-03-06 17:27:56 -07:00
|
|
|
int cwd = open(".", O_RDONLY);
|
2012-02-29 17:22:09 -07:00
|
|
|
parse_options(argc, argv);
|
2012-02-24 17:03:09 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
setbuffer(stdout, stdout_buf, sizeof stdout_buf);
|
2012-02-23 22:53:26 -07:00
|
|
|
|
2012-02-29 17:22:09 -07:00
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
|
|
get_ucspi_env();
|
|
|
|
|
2012-03-06 21:09:25 -07:00
|
|
|
while (1) {
|
2012-02-29 17:22:09 -07:00
|
|
|
handle_request();
|
2012-03-06 21:09:25 -07:00
|
|
|
if (! keepalive) {
|
|
|
|
break;
|
|
|
|
}
|
2012-03-06 17:27:56 -07:00
|
|
|
fchdir(cwd);
|
2012-03-06 21:09:25 -07:00
|
|
|
}
|
2012-02-23 22:53:26 -07:00
|
|
|
|
2012-02-24 17:03:09 -07:00
|
|
|
return 0;
|
2011-08-16 14:36:11 -06:00
|
|
|
}
|