Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Features:
* ISC license.
* suckless.org says [darkhttpd sucks less](http://suckless.org/rocks/).
* Small Docker image (<100KB)
* Partial [human-readable proxy protocol v1](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support.

Security:

Expand Down
79 changes: 71 additions & 8 deletions darkhttpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ static char *trusted_ip = NULL; /* Address of a trusted reverse proxy */
static uint64_t num_requests = 0, total_in = 0, total_out = 0;
static int accepting = 1; /* set to 0 to stop accept()ing */
static int syslog_enabled = 0;
static int proxy_protocol = 0;
volatile int running = 0; /* signal handler sets this to false */

#define INVALID_UID ((uid_t) -1)
Expand Down Expand Up @@ -1011,6 +1012,9 @@ static void usage(const char *argv0) {
printf("\t--trusted-ip ip\n"
"\t\tIf the request comes from this IP, the X-Forwarded-For header\n"
"\t\tcontent is used in the log instead of the connection peer IP.\n\n");
printf("\t--proxy-protocol\n"
"\t\tEnables the proxy protocol, which is useful for calls received\n"
"\t\by stunnel(for example) to obtain the IP address of the remote client.\n\n");
printf("\t--header 'Header: Value'\n"
"\t\tAdd a custom header to all responses.\n"
"\t\tThis option can be specified multiple times, in which case\n"
Expand Down Expand Up @@ -1253,6 +1257,9 @@ static void parse_commandline(const int argc, char *argv[]) {
else if (strcmp(argv[i], "--forward-https") == 0) {
forward_to_https = 1;
}
else if (strcmp(argv[i], "--proxy-protocol") == 0) {
proxy_protocol = 1;
}
else if (strcmp(argv[i], "--header") == 0) {
if (++i >= argc)
errx(1, "missing argument after --header");
Expand Down Expand Up @@ -1894,26 +1901,82 @@ static void parse_range_field(struct connection *conn) {
free(range);
}

static int parse_proxy_protocol_v1(struct connection *conn) {
size_t bound1, bound2;
char *protocol;

/* parse proxy protocol */
for (bound1 = 6;
(bound1 < conn->request_length) &&
(conn->request[bound1] != ' ');
bound1++)
;

protocol = split_string(conn->request, 6, bound1);

/* parse source ip */
for (bound2 = ++bound1;
(bound2 < conn->request_length) &&
(conn->request[bound2] != ' ');
bound2++)
;

if (strcmp(protocol, "TCP4") == 0) {
#ifdef HAVE_INET6
if (inet6) {
return -1;
}
#endif
inet_pton(AF_INET, split_string(conn->request, bound1, bound2), &conn->client);
}

if (strcmp(protocol, "TCP6") == 0) {
#ifdef HAVE_INET6
if (inet6) {
inet_pton(AF_INET6, split_string(conn->request, bound1, bound2), &conn->client);
}
#else
return -1;
#endif
}

/* ignore rest of the line */
for (;
(bound1 < conn->request_length) &&
!(conn->request[bound1] == '\r' && conn->request[bound1+1] == '\n');
bound1++)
;

return bound1 + 2;
}

/* Parse an HTTP request like "GET / HTTP/1.1" to get the method (GET), the
* url (/), the referer (if given) and the user-agent (if given). Remember to
* deallocate all these buffers. The method will be returned in uppercase.
*/
static int parse_request(struct connection *conn) {
size_t bound1, bound2;
size_t bound1 = 0, bound2;
char *tmp;

/* search for proxy protocol v1 header (if enabled) */
if (proxy_protocol && strncmp(conn->request, "PROXY TCP", 7) == 0) {
if ((bound1 = parse_proxy_protocol_v1(conn)) < 0) {
return -1;
}
}

/* parse method */
for (bound1 = 0;
(bound1 < conn->request_length) &&
(conn->request[bound1] != ' ');
bound1++)
for (bound2 = bound1;
(bound2 < conn->request_length) &&
(conn->request[bound2] != ' ');
bound2++)
;

conn->method = split_string(conn->request, 0, bound1);
strntoupper(conn->method, bound1);
conn->method = split_string(conn->request, bound1, bound2);
strntoupper(conn->method, bound2 - bound1);

/* parse url */
for (;
for (bound1 = bound2 + 1 ;
(bound1 < conn->request_length) &&
(conn->request[bound1] == ' ');
bound1++)
Expand Down