Skip to content

Commit 95a4394

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 144857c commit 95a4394

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 <assert.h>
2627
#include <ctype.h>
@@ -144,6 +145,57 @@ static const char *find_header(
144145
}
145146

146147

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

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

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

0 commit comments

Comments
 (0)