Skip to content

Commit

Permalink
Parse HTTP header with PicoHTTPParser
Browse files Browse the repository at this point in the history
For now replace the simple piece of code that detects the end of the header.
This actually adds code instead of simplifying. Simplification will occur
later on, when the parsed header is returned to callers.
  • Loading branch information
DimitriPapadopoulos committed Apr 23, 2020
1 parent 144857c commit 95a4394
Show file tree
Hide file tree
Showing 5 changed files with 823 additions and 25 deletions.
3 changes: 2 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ openfortivpn_SOURCES = src/config.c src/config.h src/hdlc.c src/hdlc.h \
src/tunnel.h src/main.c src/ssl.h src/xml.c \
src/xml.h src/userinput.c src/userinput.h \
src/openssl_hostname_validation.c \
src/openssl_hostname_validation.h
src/openssl_hostname_validation.h \
src/picohttpparser.c src/picohttpparser.h
openfortivpn_CFLAGS = -Wall -pedantic
openfortivpn_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" \
-DPPP_PATH=\"@PPP_PATH@\" \
Expand Down
105 changes: 85 additions & 20 deletions src/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "ipv4.h"
#include "userinput.h"
#include "log.h"
#include "picohttpparser.h"

#include <assert.h>
#include <ctype.h>
Expand Down Expand Up @@ -144,6 +145,57 @@ static const char *find_header(
}


static inline int header_name_cmp(const struct phr_header header, const char *s)
{
return strncmp(header.name, s, header.name_len);
}


static inline int header_value_cmp(const struct phr_header header, const char *s)
{
return strncmp(header.value, s, header.value_len);
}


/*
* Extract "Content-Size" and "Transfer-Encoding: chunked" from headers.
*
* @param[out] content_size
* @param[out] chunked
* @return 1 in case of success
* < 0 in case of error
*/
static int parse_response_header(const struct phr_header *headers, size_t num_headers,
uint32_t *content_size, int *chunked)
{
uint32_t content_length = 0;
int transfer_encoding_chunked = 0;

for (int i = 0; i != num_headers; ++i) {
if (header_name_cmp(headers[i], "Content-Length") == 0) {
long l = strtol(headers[i].name, NULL, 10);

if (errno || l < 0)
return ERR_HTTP_INVALID;
else if (l > UINT32_MAX)
return ERR_HTTP_TOO_LONG;
content_length = l;
}
if (header_name_cmp(headers[i], "Transfer-Encoding") == 0) {
if (header_value_cmp(headers[i], "chunked") == 0)
transfer_encoding_chunked = 1;
else
return ERR_HTTP_INVALID;
}
}
if (content_length && transfer_encoding_chunked)
return ERR_HTTP_INVALID;
*content_size = content_length;
*chunked = transfer_encoding_chunked;
return 1;
}


/*
* Receives data from the HTTP server.
*
Expand All @@ -161,7 +213,7 @@ int http_receive(
{
uint32_t capacity = HTTP_BUFFER_SIZE;
char *buffer;
uint32_t bytes_read = 0;
uint32_t bytes_read = 0, last_bytes_read = 0;
uint32_t header_size = 0;
uint32_t content_size = 0;
int chunked = 0;
Expand All @@ -183,31 +235,44 @@ int http_receive(
free(buffer);
return ERR_HTTP_SSL;
}
last_bytes_read = bytes_read;
bytes_read += n;

log_debug_details("%s:\n%s\n", __func__, buffer);

if (!header_size) {
/* Have we reached the end of the HTTP header? */
static const char EOH[4] = "\r\n\r\n";
const char *eoh = memmem(buffer, bytes_read,
EOH, sizeof(EOH));

if (eoh) {
header_size = eoh - buffer + sizeof(EOH);

/* Get the body size. */
const char *header = find_header(buffer,
"Content-Length: ",
header_size);

if (header)
content_size = atoi(header);

if (find_header(buffer,
"Transfer-Encoding: chunked",
header_size))
chunked = 1;
int minor_version, status;
const char *msg;
size_t msg_len;
struct phr_header headers[100]; // FIXME
size_t num_headers = ARRAY_SIZE(headers);

n = phr_parse_response(buffer, bytes_read,
&minor_version, &status,
&msg, &msg_len,
headers, &num_headers,
last_bytes_read);
if (n > 0) {
header_size = n;
n = parse_response_header(headers, num_headers,
&content_size, &chunked);
if (n < 0) {
free(buffer);
return n;
}
if (content_size > UINT32_MAX - header_size) {
free(buffer);
return ERR_HTTP_TOO_LONG;
} else if (content_size == 0 && !chunked) {
free(buffer);
return ERR_HTTP_INVALID;
}
} else if (n == -2) {
/* response is partial, continue the loop */
} else {
/* failed to parse the response */
assert(n == -1);
}
}

Expand Down
Loading

0 comments on commit 95a4394

Please sign in to comment.