Skip to content

Commit 5574b45

Browse files
Code review and cleanup of http_receive()
Improve readabilty by moving conditionals that operate on 'n' right after assignment of 'n'. The conditional at the end of the loop: while (n > = 0) has been replaced by this conditional: if (n < = 0) and this one that triggers a tight 2nd loop: if (n == ERR_SSL_AGAIN) ⇔ if (n == 0) The new codes *returns from the function* with ERR_HTTP_SSL upon: if (n < 0) The previous code would only *leave the loop* upon: if (n < 0) and then return from the function with ERR_HTTP_SSL only upon: if (!header) Therefore SSL violations were silently ignored after reading the header and while reading the body of the HTTP response. Increase the HTTP buffer capacity when needed. You never know. The previous size of 32 KB used to work well from 2015 to 2020. In 2020 a case was reported where 32 KB were not enough anymore and we increased the buffer size to 64 KB. Someday 64 KB might not be enough either. Yet in most cases 32 KB are more than enough. So start with 32 KB and increase well beyond 64 KB if needed, instead of bailing out with ERR_HTTP_SSL. These changes will help introduce 3rd party HTTP parsing code if we decide to go that way.
1 parent 8b0c235 commit 5574b45

3 files changed

Lines changed: 112 additions & 79 deletions

File tree

src/http.c

Lines changed: 108 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "userinput.h"
2323
#include "log.h"
2424

25+
#include <assert.h>
2526
#include <ctype.h>
2627
#include <string.h>
2728
#include <stdarg.h>
@@ -30,7 +31,8 @@
3031
#include <unistd.h>
3132
#include <arpa/inet.h>
3233

33-
#define BUFSZ 0x10000
34+
static const uint32_t HTTP_BUFFER_SIZE = 0x8000;
35+
3436

3537
/*
3638
* URL-encodes a string for HTTP requests.
@@ -58,6 +60,7 @@ static void url_encode(char *dest, const char *str)
5860
*dest = '\0';
5961
}
6062

63+
6164
/*
6265
* Sends data to the HTTP server.
6366
*
@@ -68,13 +71,13 @@ static void url_encode(char *dest, const char *str)
6871
int http_send(struct tunnel *tunnel, const char *request, ...)
6972
{
7073
va_list args;
71-
char buffer[BUFSZ];
72-
char logbuffer[BUFSZ];
74+
char buffer[HTTP_BUFFER_SIZE];
75+
char logbuffer[HTTP_BUFFER_SIZE];
7376
int length;
7477
int n = 0;
7578

7679
va_start(args, request);
77-
length = vsnprintf(buffer, BUFSZ, request, args);
80+
length = vsnprintf(buffer, HTTP_BUFFER_SIZE, request, args);
7881
va_end(args);
7982
strcpy(logbuffer, buffer);
8083
if (loglevel <= OFV_LOG_DEBUG_DETAILS && tunnel->config->password[0] != '\0') {
@@ -96,7 +99,7 @@ int http_send(struct tunnel *tunnel, const char *request, ...)
9699

97100
if (length < 0)
98101
return ERR_HTTP_INVALID;
99-
else if (length >= BUFSZ)
102+
else if (length >= HTTP_BUFFER_SIZE)
100103
return ERR_HTTP_TOO_LONG;
101104

102105
log_debug_details("%s:\n%s\n", __func__, logbuffer);
@@ -113,6 +116,7 @@ int http_send(struct tunnel *tunnel, const char *request, ...)
113116
return 1;
114117
}
115118

119+
116120
static const char *find_header(
117121
const char *res,
118122
const char *header,
@@ -135,6 +139,7 @@ static const char *find_header(
135139
return NULL;
136140
}
137141

142+
138143
/*
139144
* Receives data from the HTTP server.
140145
*
@@ -150,75 +155,87 @@ int http_receive(
150155
uint32_t *response_size
151156
)
152157
{
153-
uint32_t res_size = BUFSZ;
154-
char *buffer, *res;
155-
int n = 0;
156-
int bytes_read = 0;
157-
int header_size = 0;
158-
int content_size = 0;
158+
uint32_t capacity = HTTP_BUFFER_SIZE;
159+
char *buffer;
160+
uint32_t bytes_read = 0;
161+
uint32_t header_size = 0;
162+
uint32_t content_size = 0;
159163
int chunked = 0;
160164

161-
buffer = malloc(res_size);
165+
buffer = malloc(capacity);
162166
if (buffer == NULL)
163167
return ERR_HTTP_NO_MEM;
164168

165-
do {
166-
n = safe_ssl_read(tunnel->ssl_handle,
167-
(uint8_t *) buffer + bytes_read,
168-
BUFSZ - 1 - bytes_read);
169-
if (n > 0) {
170-
log_debug_details("%s:\n%s\n", __func__, buffer);
171-
const char *eoh;
172-
173-
bytes_read += n;
174-
175-
if (!header_size) {
176-
/* Did we see the header end? Then get the body size. */
177-
eoh = memmem(buffer, bytes_read, "\r\n\r\n", 4);
178-
if (eoh) {
179-
const char *header;
180-
181-
header = find_header(
182-
buffer,
183-
"Content-Length: ", BUFSZ);
184-
header_size = eoh - buffer + 4;
185-
if (header)
186-
content_size = atoi(header);
187-
188-
if (find_header(buffer,
189-
"Transfer-Encoding: chunked",
190-
BUFSZ))
191-
chunked = 1;
192-
}
169+
while (1) {
170+
int n;
171+
172+
while ((n = safe_ssl_read(tunnel->ssl_handle,
173+
(uint8_t *) buffer + bytes_read,
174+
capacity - bytes_read)) == ERR_SSL_AGAIN)
175+
;
176+
if (n < 0) {
177+
log_error("Error reading from SSL connection (%s).\n",
178+
err_ssl_str(n));
179+
free(buffer);
180+
return ERR_HTTP_SSL;
181+
}
182+
bytes_read += n;
183+
184+
log_debug_details("%s:\n%s\n", __func__, buffer);
185+
186+
if (!header_size) {
187+
/* Have we reached the end of the HTTP header? */
188+
static const char EOH[4] = "\r\n\r\n";
189+
const char *eoh = memmem(buffer, bytes_read,
190+
EOH, sizeof(EOH));
191+
192+
if (eoh) {
193+
header_size = eoh - buffer + sizeof(EOH);
194+
195+
/* Get the body size. */
196+
const char *header = find_header(buffer,
197+
"Content-Length: ",
198+
header_size);
199+
200+
if (header)
201+
content_size = atoi(header);
202+
203+
if (find_header(buffer,
204+
"Transfer-Encoding: chunked",
205+
header_size))
206+
chunked = 1;
193207
}
208+
}
194209

195-
if (header_size) {
196-
/* We saw the whole header, is the body done as well? */
197-
if (chunked) {
198-
/* Last chunk terminator. Done naively. */
199-
if (bytes_read >= 7 &&
200-
!memcmp(&buffer[bytes_read - 7],
201-
"\r\n0\r\n\r\n", 7))
202-
break;
203-
} else {
204-
if (bytes_read >= header_size + content_size)
205-
break;
206-
}
210+
if (header_size) {
211+
/* Have we reached the end of the HTTP body? */
212+
if (chunked) {
213+
static const char EOB[7] = "\r\n0\r\n\r\n";
214+
215+
/* Last chunk terminator. Done naively. */
216+
if (bytes_read >= sizeof(EOB) &&
217+
!memcmp(&buffer[bytes_read - sizeof(EOB)],
218+
EOB, sizeof(EOB)))
219+
break;
220+
} else {
221+
if (bytes_read >= header_size + content_size)
222+
break;
207223
}
224+
}
208225

209-
if (bytes_read == BUFSZ - 1) {
210-
log_warn("Response too big\n");
226+
/* expand the buffer if necessary */
227+
if (bytes_read == capacity) {
228+
char *new_buffer;
229+
230+
assert(UINT32_MAX / capacity >= 2);
231+
capacity *= 2;
232+
new_buffer = realloc(buffer, capacity);
233+
if (new_buffer == NULL) {
211234
free(buffer);
212-
return ERR_HTTP_SSL;
235+
return ERR_HTTP_NO_MEM;
213236
}
237+
buffer = new_buffer;
214238
}
215-
} while (n >= 0);
216-
217-
if (!header_size) {
218-
log_debug("Error reading from SSL connection (%s).\n",
219-
err_ssl_str(n));
220-
free(buffer);
221-
return ERR_HTTP_SSL;
222239
}
223240

224241
if (memmem(&buffer[header_size], bytes_read - header_size,
@@ -231,23 +248,26 @@ int http_receive(
231248

232249
if (response == NULL) {
233250
free(buffer);
234-
return 1;
235-
}
251+
} else {
252+
char *tmp;
253+
254+
assert(capacity < UINT32_MAX);
255+
capacity = bytes_read + 1;
256+
tmp = realloc(buffer, capacity);
257+
if (tmp == NULL) {
258+
free(buffer);
259+
return ERR_HTTP_NO_MEM;
260+
}
261+
tmp[bytes_read] = '\0';
262+
*response = tmp;
236263

237-
res_size = bytes_read + 1;
238-
res = realloc(buffer, res_size);
239-
if (res == NULL) {
240-
free(buffer);
241-
return ERR_HTTP_NO_MEM;
264+
if (response_size != NULL)
265+
*response_size = capacity;
242266
}
243-
res[bytes_read] = '\0';
244-
245-
*response = res;
246-
if (response_size != NULL)
247-
*response_size = res_size;
248267
return 1;
249268
}
250269

270+
251271
static int do_http_request(struct tunnel *tunnel,
252272
const char *method,
253273
const char *uri,
@@ -276,6 +296,8 @@ static int do_http_request(struct tunnel *tunnel,
276296

277297
return http_receive(tunnel, response, response_size);
278298
}
299+
300+
279301
/*
280302
* Sends and receives data from the HTTP server.
281303
*
@@ -307,6 +329,7 @@ static int http_request(struct tunnel *tunnel, const char *method,
307329
return ret;
308330
}
309331

332+
310333
/*
311334
* Read value for key from a string like "key1=value1&key2=value2".
312335
* The `key` arg is supposed to contains the final "=".
@@ -351,6 +374,7 @@ static int get_value_from_response(const char *buf, const char *key,
351374
return ret;
352375
}
353376

377+
354378
static int get_auth_cookie(
355379
struct tunnel *tunnel,
356380
char *buf,
@@ -390,6 +414,7 @@ static int get_auth_cookie(
390414
return ret;
391415
}
392416

417+
393418
static void delay_otp(struct tunnel *tunnel)
394419
{
395420
if (tunnel->config->otp_delay > 0) {
@@ -398,8 +423,8 @@ static void delay_otp(struct tunnel *tunnel)
398423
}
399424
}
400425

401-
static
402-
int try_otp_auth(
426+
427+
static int try_otp_auth(
403428
struct tunnel *tunnel,
404429
const char *buffer,
405430
char **res,
@@ -555,6 +580,7 @@ int try_otp_auth(
555580
#undef SPACE_AVAILABLE
556581
}
557582

583+
558584
/*
559585
* Authenticates to gateway by sending username and password.
560586
*
@@ -684,11 +710,13 @@ int auth_log_in(struct tunnel *tunnel)
684710
return ret;
685711
}
686712

713+
687714
int auth_log_out(struct tunnel *tunnel)
688715
{
689716
return http_request(tunnel, "GET", "/remote/logout", "", NULL, NULL);
690717
}
691718

719+
692720
int auth_request_vpn_allocation(struct tunnel *tunnel)
693721
{
694722
int ret = http_request(tunnel, "GET", "/remote/index", "", NULL, NULL);
@@ -699,6 +727,7 @@ int auth_request_vpn_allocation(struct tunnel *tunnel)
699727
return http_request(tunnel, "GET", "/remote/fortisslvpn", "", NULL, NULL);
700728
}
701729

730+
702731
static int parse_xml_config(struct tunnel *tunnel, const char *buffer)
703732
{
704733
const char *val;
@@ -778,8 +807,8 @@ static int parse_xml_config(struct tunnel *tunnel, const char *buffer)
778807
return 1;
779808
}
780809

781-
static
782-
int parse_config(struct tunnel *tunnel, const char *buffer)
810+
811+
static int parse_config(struct tunnel *tunnel, const char *buffer)
783812
{
784813
const char *c, *end;
785814

@@ -828,6 +857,7 @@ int parse_config(struct tunnel *tunnel, const char *buffer)
828857
return 1;
829858
}
830859

860+
831861
int auth_get_config(struct tunnel *tunnel)
832862
{
833863
char *buffer;

src/http.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
#include "tunnel.h"
2222

23+
#include <stdint.h>
24+
2325
#define ERR_HTTP_INVALID -1
2426
#define ERR_HTTP_TOO_LONG -2
2527
#define ERR_HTTP_NO_MEM -3

src/ssl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#define OPENFORTIVPN_SSL_H
3131

3232
#include <errno.h>
33+
#include <stdint.h>
3334
#include <string.h>
3435
#include <openssl/err.h>
3536
#include <openssl/ssl.h>
@@ -76,7 +77,7 @@ static inline const char *err_ssl_str(int code)
7677
else if (code == ERR_SSL_SEE_ERRNO)
7778
return strerror(errno);
7879
else if (code == ERR_SSL_SEE_SSLERR)
79-
return ERR_error_string(ERR_peek_last_error(), NULL);
80+
return ERR_reason_error_string(ERR_peek_last_error());
8081
return "unknown";
8182
}
8283

0 commit comments

Comments
 (0)