Skip to content

Commit 1257c39

Browse files
Parse HTTP header with PicoHTTPParser
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.
1 parent 330d8be commit 1257c39

5 files changed

Lines changed: 823 additions & 25 deletions

File tree

Makefile.am

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ openfortivpn_SOURCES = src/config.c src/config.h src/hdlc.c src/hdlc.h \
77
src/tunnel.h src/main.c src/ssl.h src/xml.c \
88
src/xml.h src/userinput.c src/userinput.h \
99
src/openssl_hostname_validation.c \
10-
src/openssl_hostname_validation.h
10+
src/openssl_hostname_validation.h \
11+
src/picohttpparser.c src/picohttpparser.h
1112
openfortivpn_CFLAGS = -Wall -pedantic
1213
openfortivpn_CPPFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\" \
1314
-DPPP_PATH=\"@PPP_PATH@\" \

src/http.c

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "ipv4.h"
2222
#include "userinput.h"
2323
#include "log.h"
24+
#include "picohttpparser.h"
2425

2526
#include <unistd.h>
2627
#include <arpa/inet.h>
@@ -145,6 +146,57 @@ static const char *find_header(
145146
}
146147

147148

149+
static inline int header_name_cmp(const struct phr_header header, const char *s)
150+
{
151+
return strncmp(header.name, s, header.name_len);
152+
}
153+
154+
155+
static inline int header_value_cmp(const struct phr_header header, const char *s)
156+
{
157+
return strncmp(header.value, s, header.value_len);
158+
}
159+
160+
161+
/*
162+
* Extract "Content-Size" and "Transfer-Encoding: chunked" from headers.
163+
*
164+
* @param[out] content_size
165+
* @param[out] chunked
166+
* @return 1 in case of success
167+
* < 0 in case of error
168+
*/
169+
static int parse_response_header(const struct phr_header *headers, size_t num_headers,
170+
uint32_t *content_size, int *chunked)
171+
{
172+
uint32_t content_length = 0;
173+
int transfer_encoding_chunked = 0;
174+
175+
for (int i = 0; i != num_headers; ++i) {
176+
if (header_name_cmp(headers[i], "Content-Length") == 0) {
177+
long l = strtol(headers[i].name, NULL, 10);
178+
179+
if (errno || l < 0)
180+
return ERR_HTTP_INVALID;
181+
else if (l > UINT32_MAX)
182+
return ERR_HTTP_TOO_LONG;
183+
content_length = l;
184+
}
185+
if (header_name_cmp(headers[i], "Transfer-Encoding") == 0) {
186+
if (header_value_cmp(headers[i], "chunked") == 0)
187+
transfer_encoding_chunked = 1;
188+
else
189+
return ERR_HTTP_INVALID;
190+
}
191+
}
192+
if (content_length && transfer_encoding_chunked)
193+
return ERR_HTTP_INVALID;
194+
*content_size = content_length;
195+
*chunked = transfer_encoding_chunked;
196+
return 1;
197+
}
198+
199+
148200
/*
149201
* Receives data from the HTTP server.
150202
*
@@ -162,7 +214,7 @@ int http_receive(
162214
{
163215
uint32_t capacity = HTTP_BUFFER_SIZE;
164216
char *buffer;
165-
uint32_t bytes_read = 0;
217+
uint32_t bytes_read = 0, last_bytes_read = 0;
166218
uint32_t header_size = 0;
167219
uint32_t content_size = 0;
168220
int chunked = 0;
@@ -184,31 +236,44 @@ int http_receive(
184236
free(buffer);
185237
return ERR_HTTP_SSL;
186238
}
239+
last_bytes_read = bytes_read;
187240
bytes_read += n;
188241

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

191244
if (!header_size) {
192245
/* Have we reached the end of the HTTP header? */
193-
static const char EOH[4] = "\r\n\r\n";
194-
const char *eoh = memmem(buffer, bytes_read,
195-
EOH, sizeof(EOH));
196-
197-
if (eoh) {
198-
header_size = eoh - buffer + sizeof(EOH);
199-
200-
/* Get the body size. */
201-
const char *header = find_header(buffer,
202-
"Content-Length: ",
203-
header_size);
204-
205-
if (header)
206-
content_size = atoi(header);
207-
208-
if (find_header(buffer,
209-
"Transfer-Encoding: chunked",
210-
header_size))
211-
chunked = 1;
246+
int minor_version, status;
247+
const char *msg;
248+
size_t msg_len;
249+
struct phr_header headers[100]; // FIXME
250+
size_t num_headers = ARRAY_SIZE(headers);
251+
252+
n = phr_parse_response(buffer, bytes_read,
253+
&minor_version, &status,
254+
&msg, &msg_len,
255+
headers, &num_headers,
256+
last_bytes_read);
257+
if (n > 0) {
258+
header_size = n;
259+
n = parse_response_header(headers, num_headers,
260+
&content_size, &chunked);
261+
if (n < 0) {
262+
free(buffer);
263+
return n;
264+
}
265+
if (content_size > UINT32_MAX - header_size) {
266+
free(buffer);
267+
return ERR_HTTP_TOO_LONG;
268+
} else if (content_size == 0 && !chunked) {
269+
free(buffer);
270+
return ERR_HTTP_INVALID;
271+
}
272+
} else if (n == -2) {
273+
/* response is partial, continue the loop */
274+
} else {
275+
/* failed to parse the response */
276+
assert(n == -1);
212277
}
213278
}
214279

0 commit comments

Comments
 (0)