How not to write a http daemon
Below you can see the source code of a http daemon, based on the ioccc entry "Hibachi".
Though it is a robust and toughout program, it would not do well,
at all, as a CN lab solution. Actually, it breaks almost every rule (both
implicit and explicit) in the book of propper C writing, as well as all
the directives given in the handout and the CN lab website.
Do not, I repeat, do not submit a solution even remotly
like this, and expect to pass.
It is smaller than 160 lines of code. It's list of features,
however, is impressive, and it takes a lot of experience to write
something like this. In fact, it takes a lot of experience to read
something like this.
Features
- Implements HTTP/1.1
- Supports virtual hosts.
- Supports all MIME types.
- Supports Common Gateway Interface (CGI) scripts and programs.
- Supports multiple index.* file types:
index.htm, index.html, index.php, index.pl,
index.sh, index.rb, index.py
- Supports subset of RFC 2616 HTTP/1.1 methods:
GET, HEAD, and POST
- Simple & straight forward configuration using environment variables.
- Portability across Unix-like environments, such as:
Cygwin, FreeBSD, Linux, SunOS.
- Known to work with Lynx, Mozilla, Opera, and Internet Explorer
web browsers.
- Is a dedicated process-forking server that does not use inetd.
- Is secure against relative path file snooping.
- Is secure against directory searches.
- CGI script execution managed through Unix file permissions.
C Source Code
This code is way past the category "Don't try this at home!"
Study this code at your own risk!
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define bufsize 8192
char r[bufsize], q[bufsize], l[bufsize], w[bufsize],
*o, *m = "404 Not Found", *e = "500 Error";
void X(int t, char *u, long j)
{
j = snprintf(q, bufsize,
"HTTP/1.0 %s\r\nContent-Length: %ld\r\n\r\n%s",
u, j ? j : strcspn(u, ""), j ? "" : u);
send(t, q, j, 0);
}
void A(char *i, char *j)
{
char *u;
if ((u = strstr(o, i + 5)))
sscanf(u + strcspn(u, ":") + 1, j, getenv(i));
}
int main(int c, char **p)
{
int f, t, i;
struct stat g;
struct sockaddr_in a;
char *v, *u;
if ((v = getenv("SERVER_PORT")) == 0) {
return 4;
}
i = (int)strtol(v, 0, 10);
a.sin_addr.s_addr = htonl(INADDR_ANY);
a.sin_port = htons((unsigned short)i);
a.sin_family = AF_INET;
if ((f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
return 5;
}
if (bind(f, (void *)&a, sizeof a)) {
return 6;
}
if (listen(f, 10)) {
return 7;
}
for (;; close(t)) {
for (; waitpid(0, 0, WNOHANG) > 0;);
i = sizeof a;
if ((t = accept(f, (void *)&a, &i)) < 0)
continue;
if (fork())
continue;
for (f = i = 0; f < 2 && i < bufsize; i++) {
if (recv(t, r + i, 1, 0) <= 0)
return 1;
if (r[i] == '\n')
f++;
if (r[i] == ':')
f = 0;
if (f)
r[i] = r[i] == '-' ? '_' : toupper(r[i]);
}
if (bufsize <= i)
break;
r[i] = 0;
if (*r != 'G' && *r != 'H' && r[2] != 'S') {
X(t, "501 Unsupported", 0);
break;
}
*l = 0;
if ((v = strstr(r, "HOST:")))
sscanf(v + 5, " %255[^:\r\n]", l);
snprintf(w, bufsize, "DOCUMENT_ROOT=%s/%s",
getenv("DOCUMENT_ROOT"), l);
putenv(w);
u = r + 4 + (*r != 'G');
if (*u != '/' || strstr(u, "../")) {
X(t, m, 0);
break;
}
u[i = strcspn(u, " ")] = 0;
o = u + i + 1;
i = strcspn(u, "?");
if (u[i]) {
snprintf(q, bufsize, "QUERY_STRING=%s", u + i + 1);
putenv(q);
u[i] = 0;
}
snprintf(l, bufsize, "SCRIPT_FILENAME=%s%s", w + 14, u);
v = l + 16;
if (stat(v, &g) < 0) {
X(t, m, 0);
break;
}
if (S_ISDIR(g.st_mode)) {
struct dirent *d;
DIR *D = opendir(v);
for (; (d = readdir(D));) {
if (!strncmp(d->d_name, "index.", 6)) {
strncat(v, d->d_name, bufsize - 16);
stat(v, &g);
break;
}
}
closedir(D);
if (d == 0) {
X(t, m, 0);
break;
}
}
if (*r != 'H') {
if (g.st_mode & 0111) {
putenv(l);
sscanf(r, "%4s", getenv("REQUEST_METHOD"));
sscanf(inet_ntoa(a.sin_addr), "%15s",
getenv("REMOTE_ADDR"));
A("HTTP_COOKIE", "%*[ \t]%80[^\r\n]");
A("CONTENT_LENGTH", "%20s");
A("CONTENT_TYPE", "%60s");
dup2(t, 0);
dup2(t, 1);
if (system(v))
X(t, e, 0);
break;
}
if ((f = open(v, O_RDONLY)) < 0) {
X(t, e, 0);
break;
}
}
X(t, "200 OK", g.st_size);
if (*r != 'H') {
for (; 0 < (i = read(f, r, bufsize));)
send(t, r, i, 0);
}
break;
}
shutdown(t, SHUT_WR);
return 0;
}
|