diff --git a/src/http.c b/src/http.c index 3420d5e0..e32ecf93 100644 --- a/src/http.c +++ b/src/http.c @@ -22,6 +22,7 @@ #include "userinput.h" #include "log.h" +#include #include #include #include @@ -30,7 +31,8 @@ #include #include -#define BUFSZ 0x10000 +static const uint32_t HTTP_BUFFER_SIZE = 0x8000; + /* * URL-encodes a string for HTTP requests. @@ -58,6 +60,7 @@ static void url_encode(char *dest, const char *str) *dest = '\0'; } + /* * Sends data to the HTTP server. * @@ -68,13 +71,13 @@ static void url_encode(char *dest, const char *str) int http_send(struct tunnel *tunnel, const char *request, ...) { va_list args; - char buffer[BUFSZ]; - char logbuffer[BUFSZ]; + char buffer[HTTP_BUFFER_SIZE]; + char logbuffer[HTTP_BUFFER_SIZE]; int length; int n = 0; va_start(args, request); - length = vsnprintf(buffer, BUFSZ, request, args); + length = vsnprintf(buffer, HTTP_BUFFER_SIZE, request, args); va_end(args); strcpy(logbuffer, buffer); if (loglevel <= OFV_LOG_DEBUG_DETAILS && tunnel->config->password[0] != '\0') { @@ -96,7 +99,7 @@ int http_send(struct tunnel *tunnel, const char *request, ...) if (length < 0) return ERR_HTTP_INVALID; - else if (length >= BUFSZ) + else if (length >= HTTP_BUFFER_SIZE) return ERR_HTTP_TOO_LONG; log_debug_details("%s:\n%s\n", __func__, logbuffer); @@ -113,6 +116,7 @@ int http_send(struct tunnel *tunnel, const char *request, ...) return 1; } + static const char *find_header( const char *res, const char *header, @@ -135,6 +139,7 @@ static const char *find_header( return NULL; } + /* * Receives data from the HTTP server. * @@ -150,75 +155,87 @@ int http_receive( uint32_t *response_size ) { - uint32_t res_size = BUFSZ; - char *buffer, *res; - int n = 0; - int bytes_read = 0; - int header_size = 0; - int content_size = 0; + uint32_t capacity = HTTP_BUFFER_SIZE; + char *buffer; + uint32_t bytes_read = 0; + uint32_t header_size = 0; + uint32_t content_size = 0; int chunked = 0; - buffer = malloc(res_size); + buffer = malloc(capacity); if (buffer == NULL) return ERR_HTTP_NO_MEM; - do { - n = safe_ssl_read(tunnel->ssl_handle, - (uint8_t *) buffer + bytes_read, - BUFSZ - 1 - bytes_read); - if (n > 0) { - log_debug_details("%s:\n%s\n", __func__, buffer); - const char *eoh; - - bytes_read += n; - - if (!header_size) { - /* Did we see the header end? Then get the body size. */ - eoh = memmem(buffer, bytes_read, "\r\n\r\n", 4); - if (eoh) { - const char *header; - - header = find_header( - buffer, - "Content-Length: ", BUFSZ); - header_size = eoh - buffer + 4; - if (header) - content_size = atoi(header); - - if (find_header(buffer, - "Transfer-Encoding: chunked", - BUFSZ)) - chunked = 1; - } + while (1) { + int n; + + while ((n = safe_ssl_read(tunnel->ssl_handle, + (uint8_t *) buffer + bytes_read, + capacity - bytes_read)) == ERR_SSL_AGAIN) + ; + if (n < 0) { + log_error("Error reading from SSL connection (%s).\n", + err_ssl_str(n)); + free(buffer); + return ERR_HTTP_SSL; + } + 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; } + } - if (header_size) { - /* We saw the whole header, is the body done as well? */ - if (chunked) { - /* Last chunk terminator. Done naively. */ - if (bytes_read >= 7 && - !memcmp(&buffer[bytes_read - 7], - "\r\n0\r\n\r\n", 7)) - break; - } else { - if (bytes_read >= header_size + content_size) - break; - } + if (header_size) { + /* Have we reached the end of the HTTP body? */ + if (chunked) { + static const char EOB[7] = "\r\n0\r\n\r\n"; + + /* Last chunk terminator. Done naively. */ + if (bytes_read >= sizeof(EOB) && + !memcmp(&buffer[bytes_read - sizeof(EOB)], + EOB, sizeof(EOB))) + break; + } else { + if (bytes_read >= header_size + content_size) + break; } + } - if (bytes_read == BUFSZ - 1) { - log_warn("Response too big\n"); + /* expand the buffer if necessary */ + if (bytes_read == capacity) { + char *new_buffer; + + assert(UINT32_MAX / capacity >= 2); + capacity *= 2; + new_buffer = realloc(buffer, capacity); + if (new_buffer == NULL) { free(buffer); - return ERR_HTTP_SSL; + return ERR_HTTP_NO_MEM; } + buffer = new_buffer; } - } while (n >= 0); - - if (!header_size) { - log_debug("Error reading from SSL connection (%s).\n", - err_ssl_str(n)); - free(buffer); - return ERR_HTTP_SSL; } if (memmem(&buffer[header_size], bytes_read - header_size, @@ -231,23 +248,26 @@ int http_receive( if (response == NULL) { free(buffer); - return 1; - } + } else { + char *tmp; + + assert(capacity < UINT32_MAX); + capacity = bytes_read + 1; + tmp = realloc(buffer, capacity); + if (tmp == NULL) { + free(buffer); + return ERR_HTTP_NO_MEM; + } + tmp[bytes_read] = '\0'; + *response = tmp; - res_size = bytes_read + 1; - res = realloc(buffer, res_size); - if (res == NULL) { - free(buffer); - return ERR_HTTP_NO_MEM; + if (response_size != NULL) + *response_size = capacity; } - res[bytes_read] = '\0'; - - *response = res; - if (response_size != NULL) - *response_size = res_size; return 1; } + static int do_http_request(struct tunnel *tunnel, const char *method, const char *uri, @@ -276,6 +296,8 @@ static int do_http_request(struct tunnel *tunnel, return http_receive(tunnel, response, response_size); } + + /* * Sends and receives data from the HTTP server. * @@ -307,6 +329,7 @@ static int http_request(struct tunnel *tunnel, const char *method, return ret; } + /* * Read value for key from a string like "key1=value1&key2=value2". * 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, return ret; } + static int get_auth_cookie( struct tunnel *tunnel, char *buf, @@ -390,6 +414,7 @@ static int get_auth_cookie( return ret; } + static void delay_otp(struct tunnel *tunnel) { if (tunnel->config->otp_delay > 0) { @@ -398,8 +423,8 @@ static void delay_otp(struct tunnel *tunnel) } } -static -int try_otp_auth( + +static int try_otp_auth( struct tunnel *tunnel, const char *buffer, char **res, @@ -555,6 +580,7 @@ int try_otp_auth( #undef SPACE_AVAILABLE } + /* * Authenticates to gateway by sending username and password. * @@ -684,11 +710,13 @@ int auth_log_in(struct tunnel *tunnel) return ret; } + int auth_log_out(struct tunnel *tunnel) { return http_request(tunnel, "GET", "/remote/logout", "", NULL, NULL); } + int auth_request_vpn_allocation(struct tunnel *tunnel) { int ret = http_request(tunnel, "GET", "/remote/index", "", NULL, NULL); @@ -699,6 +727,7 @@ int auth_request_vpn_allocation(struct tunnel *tunnel) return http_request(tunnel, "GET", "/remote/fortisslvpn", "", NULL, NULL); } + static int parse_xml_config(struct tunnel *tunnel, const char *buffer) { const char *val; @@ -778,8 +807,8 @@ static int parse_xml_config(struct tunnel *tunnel, const char *buffer) return 1; } -static -int parse_config(struct tunnel *tunnel, const char *buffer) + +static int parse_config(struct tunnel *tunnel, const char *buffer) { const char *c, *end; @@ -828,6 +857,7 @@ int parse_config(struct tunnel *tunnel, const char *buffer) return 1; } + int auth_get_config(struct tunnel *tunnel) { char *buffer; diff --git a/src/http.h b/src/http.h index e1e17101..ec124e99 100644 --- a/src/http.h +++ b/src/http.h @@ -20,6 +20,8 @@ #include "tunnel.h" +#include + #define ERR_HTTP_INVALID -1 #define ERR_HTTP_TOO_LONG -2 #define ERR_HTTP_NO_MEM -3 diff --git a/src/ssl.h b/src/ssl.h index 879b9d49..c0fe623b 100644 --- a/src/ssl.h +++ b/src/ssl.h @@ -30,6 +30,7 @@ #define OPENFORTIVPN_SSL_H #include +#include #include #include #include @@ -76,7 +77,7 @@ static inline const char *err_ssl_str(int code) else if (code == ERR_SSL_SEE_ERRNO) return strerror(errno); else if (code == ERR_SSL_SEE_SSLERR) - return ERR_error_string(ERR_peek_last_error(), NULL); + return ERR_reason_error_string(ERR_peek_last_error()); return "unknown"; }