aboutsummaryrefslogtreecommitdiff
path: root/crypto/http/http_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/http/http_client.c')
-rw-r--r--crypto/http/http_client.c282
1 files changed, 202 insertions, 80 deletions
diff --git a/crypto/http/http_client.c b/crypto/http/http_client.c
index c86db4405b8f..dea69ffc64d4 100644
--- a/crypto/http/http_client.c
+++ b/crypto/http/http_client.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2001-2023 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2001-2025 The OpenSSL Project Authors. All Rights Reserved.
* Copyright Siemens AG 2018-2020
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
@@ -8,7 +8,7 @@
* https://www.openssl.org/source/license.html
*/
-#include "e_os.h"
+#include "internal/e_os.h"
#include <stdio.h>
#include <stdlib.h>
#include "crypto/ctype.h"
@@ -20,10 +20,10 @@
#include <openssl/cmperr.h>
#include <openssl/buffer.h>
#include <openssl/http.h>
+#include <openssl/trace.h>
#include "internal/sockets.h"
-#include "internal/cryptlib.h" /* for ossl_assert() */
+#include "internal/common.h" /* for ossl_assert() */
-#define HAS_PREFIX(str, prefix) (strncmp(str, prefix, sizeof(prefix) - 1) == 0)
#define HTTP_PREFIX "HTTP/"
#define HTTP_VERSION_PATT "1." /* allow 1.x */
#define HTTP_VERSION_STR_LEN sizeof(HTTP_VERSION_PATT) /* == strlen("1.0") */
@@ -35,6 +35,7 @@
#define HTTP_STATUS_CODE_OK 200
#define HTTP_STATUS_CODE_MOVED_PERMANENTLY 301
#define HTTP_STATUS_CODE_FOUND 302
+#define HTTP_STATUS_CODES_NONFATAL_ERROR 400
/* Stateful HTTP request code, supporting blocking and non-blocking I/O */
@@ -51,13 +52,14 @@ struct ossl_http_req_ctx_st {
void *upd_arg; /* Optional arg for update callback function */
int use_ssl; /* Use HTTPS */
char *proxy; /* Optional proxy name or URI */
- char *server; /* Optional server host name */
+ char *server; /* Optional server hostname */
char *port; /* Optional server port */
BIO *mem; /* Mem BIO holding request header or response */
BIO *req; /* BIO holding the request provided by caller */
int method_POST; /* HTTP method is POST (else GET) */
+ int text; /* Request content type is (likely) text */
char *expected_ct; /* Optional expected Content-Type */
- int expect_asn1; /* Response must be ASN.1-encoded */
+ int expect_asn1; /* Response content must be ASN.1-encoded */
unsigned char *pos; /* Current position sending data */
long len_to_send; /* Number of bytes still to send */
size_t resp_len; /* Length of response */
@@ -66,24 +68,29 @@ struct ossl_http_req_ctx_st {
time_t max_time; /* Maximum end time of current transfer, or 0 */
time_t max_total_time; /* Maximum end time of total transfer, or 0 */
char *redirection_url; /* Location obtained from HTTP status 301/302 */
+ size_t max_hdr_lines; /* Max. number of response header lines, or 0 */
};
-/* HTTP states */
+/* HTTP client OSSL_HTTP_REQ_CTX_nbio() internal states, in typical order */
#define OHS_NOREAD 0x1000 /* If set no reading should be performed */
#define OHS_ERROR (0 | OHS_NOREAD) /* Error condition */
#define OHS_ADD_HEADERS (1 | OHS_NOREAD) /* Adding header lines to request */
#define OHS_WRITE_INIT (2 | OHS_NOREAD) /* 1st call: ready to start send */
-#define OHS_WRITE_HDR (3 | OHS_NOREAD) /* Request header being sent */
-#define OHS_WRITE_REQ (4 | OHS_NOREAD) /* Request contents being sent */
-#define OHS_FLUSH (5 | OHS_NOREAD) /* Request being flushed */
+#define OHS_WRITE_HDR1 (3 | OHS_NOREAD) /* Request header to be sent */
+#define OHS_WRITE_HDR (4 | OHS_NOREAD) /* Request header being sent */
+#define OHS_WRITE_REQ (5 | OHS_NOREAD) /* Request content (body) being sent */
+#define OHS_FLUSH (6 | OHS_NOREAD) /* Request being flushed */
+
#define OHS_FIRSTLINE 1 /* First line of response being read */
#define OHS_HEADERS 2 /* MIME headers of response being read */
-#define OHS_REDIRECT 3 /* MIME headers being read, expecting Location */
-#define OHS_ASN1_HEADER 4 /* ASN1 sequence header (tag+length) being read */
-#define OHS_ASN1_CONTENT 5 /* ASN1 content octets being read */
-#define OHS_ASN1_DONE (6 | OHS_NOREAD) /* ASN1 content read completed */
-#define OHS_STREAM (7 | OHS_NOREAD) /* HTTP content stream to be read */
+#define OHS_HEADERS_ERROR 3 /* MIME headers of response being read after fatal error */
+#define OHS_REDIRECT 4 /* MIME headers being read, expecting Location */
+#define OHS_ASN1_HEADER 5 /* ASN1 sequence header (tag+length) being read */
+#define OHS_ASN1_CONTENT 6 /* ASN1 content octets being read */
+#define OHS_ASN1_DONE 7 /* ASN1 content read completed */
+#define OHS_STREAM 8 /* HTTP content stream to be read by caller */
+#define OHS_ERROR_CONTENT 9 /* response content (body) being read after fatal error */
/* Low-level HTTP API implementation */
@@ -103,6 +110,7 @@ OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size)
rctx->buf = OPENSSL_malloc(rctx->buf_size);
rctx->wbio = wbio;
rctx->rbio = rbio;
+ rctx->max_hdr_lines = OSSL_HTTP_DEFAULT_MAX_RESP_HDR_LINES;
if (rctx->buf == NULL) {
OPENSSL_free(rctx);
return NULL;
@@ -296,9 +304,14 @@ static int set1_content(OSSL_HTTP_REQ_CTX *rctx,
return 0;
}
- if (content_type != NULL
- && BIO_printf(rctx->mem, "Content-Type: %s\r\n", content_type) <= 0)
- return 0;
+ if (content_type == NULL) {
+ rctx->text = 1; /* assuming request to be text by default, used just for tracing */
+ } else {
+ if (HAS_CASE_PREFIX(content_type, "text/"))
+ rctx->text = 1;
+ if (BIO_printf(rctx->mem, "Content-Type: %s\r\n", content_type) <= 0)
+ return 0;
+ }
/*
* BIO_CTRL_INFO yields the data length at least for memory BIOs, but for
@@ -347,6 +360,16 @@ int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type
return res;
}
+void OSSL_HTTP_REQ_CTX_set_max_response_hdr_lines(OSSL_HTTP_REQ_CTX *rctx,
+ size_t count)
+{
+ if (rctx == NULL) {
+ ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
+ return;
+ }
+ rctx->max_hdr_lines = count;
+}
+
static int add1_headers(OSSL_HTTP_REQ_CTX *rctx,
const STACK_OF(CONF_VALUE) *headers, const char *host)
{
@@ -409,13 +432,13 @@ static OSSL_HTTP_REQ_CTX *http_req_ctx_new(int free_wbio, BIO *wbio, BIO *rbio,
static int parse_http_line1(char *line, int *found_keep_alive)
{
- int i, retcode, err;
+ int i, retcode;
char *code, *reason, *end;
- if (!HAS_PREFIX(line, HTTP_PREFIX_VERSION))
+ if (!CHECK_AND_SKIP_PREFIX(line, HTTP_PREFIX_VERSION))
goto err;
/* above HTTP 1.0, connection persistence is the default */
- *found_keep_alive = line[strlen(HTTP_PREFIX_VERSION)] > '0';
+ *found_keep_alive = *line > '0';
/* Skip to first whitespace (past protocol info) */
for (code = line; *code != '\0' && !ossl_isspace(*code); code++)
@@ -465,14 +488,11 @@ static int parse_http_line1(char *line, int *found_keep_alive)
case HTTP_STATUS_CODE_FOUND:
return retcode;
default:
- err = HTTP_R_RECEIVED_ERROR;
- if (retcode < 400)
- err = HTTP_R_STATUS_CODE_UNSUPPORTED;
- if (*reason == '\0')
- ERR_raise_data(ERR_LIB_HTTP, err, "code=%s", code);
- else
- ERR_raise_data(ERR_LIB_HTTP, err, "code=%s, reason=%s", code,
- reason);
+ if (retcode < HTTP_STATUS_CODES_NONFATAL_ERROR) {
+ ERR_raise_data(ERR_LIB_HTTP, HTTP_R_STATUS_CODE_UNSUPPORTED, "code=%s", code);
+ if (*reason != '\0')
+ ERR_add_error_data(2, ", reason=", reason);
+ } /* must return content normally if status >= 400 */
return retcode;
}
@@ -485,17 +505,23 @@ static int parse_http_line1(char *line, int *found_keep_alive)
return 0;
}
-static int check_set_resp_len(OSSL_HTTP_REQ_CTX *rctx, size_t len)
+static int check_max_len(const char *desc, size_t max_len, size_t len)
{
- if (rctx->max_resp_len != 0 && len > rctx->max_resp_len) {
+ if (max_len != 0 && len > max_len) {
ERR_raise_data(ERR_LIB_HTTP, HTTP_R_MAX_RESP_LEN_EXCEEDED,
- "length=%zu, max=%zu", len, rctx->max_resp_len);
+ "%s length=%zu, max=%zu", desc, len, max_len);
return 0;
}
+ return 1;
+}
+
+static int check_set_resp_len(const char *desc, OSSL_HTTP_REQ_CTX *rctx, size_t len)
+{
+ if (!check_max_len(desc, rctx->max_resp_len, len))
+ return 0;
if (rctx->resp_len != 0 && rctx->resp_len != len) {
ERR_raise_data(ERR_LIB_HTTP, HTTP_R_INCONSISTENT_CONTENT_LENGTH,
- "ASN.1 length=%zu, Content-Length=%zu",
- len, rctx->resp_len);
+ "%s length=%zu, Content-Length=%zu", desc, len, rctx->resp_len);
return 0;
}
rctx->resp_len = len;
@@ -524,10 +550,12 @@ static int may_still_retry(time_t max_time, int *ptimeout)
int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
{
int i, found_expected_ct = 0, found_keep_alive = 0;
+ int got_text = 1;
long n;
- size_t resp_len;
+ size_t resp_len = 0;
const unsigned char *p;
char *buf, *key, *value, *line_end = NULL;
+ size_t resp_hdr_lines = 0;
if (rctx == NULL) {
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
@@ -542,12 +570,13 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
next_io:
buf = (char *)rctx->buf;
if ((rctx->state & OHS_NOREAD) == 0) {
- if (rctx->expect_asn1) {
- n = BIO_read(rctx->rbio, rctx->buf, rctx->buf_size);
- } else {
+ if (rctx->expect_asn1 && (rctx->state == OHS_ASN1_HEADER
+ || rctx->state == OHS_ASN1_CONTENT)) {
+ n = BIO_read(rctx->rbio, buf, rctx->buf_size);
+ } else { /* read one text line */
(void)ERR_set_mark();
n = BIO_gets(rctx->rbio, buf, rctx->buf_size);
- if (n == -2) { /* unsupported method */
+ if (n == -2) { /* some BIOs, such as SSL, do not support "gets" */
(void)ERR_pop_to_mark();
n = BIO_get_line(rctx->rbio, buf, rctx->buf_size);
} else {
@@ -555,6 +584,13 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
}
}
if (n <= 0) {
+ if (rctx->state == OHS_ERROR_CONTENT) {
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE(HTTP, "]\n"); /* end of error response content */
+ /* in addition, throw error on inconsistent length: */
+ (void)check_set_resp_len("error response content", rctx, resp_len);
+ return 0;
+ }
if (BIO_should_retry(rctx->rbio))
return -1;
ERR_raise(ERR_LIB_HTTP, HTTP_R_FAILED_READING_DATA);
@@ -562,11 +598,15 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
}
/* Write data to memory BIO */
- if (BIO_write(rctx->mem, rctx->buf, n) != n)
+ if (BIO_write(rctx->mem, buf, n) != n)
return 0;
}
switch (rctx->state) {
+ case OHS_ERROR:
+ default:
+ return 0;
+
case OHS_ADD_HEADERS:
/* Last operation was adding headers: need a final \r\n */
if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
@@ -575,27 +615,39 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
}
rctx->state = OHS_WRITE_INIT;
- /* fall thru */
+ /* fall through */
case OHS_WRITE_INIT:
rctx->len_to_send = BIO_get_mem_data(rctx->mem, &rctx->pos);
- rctx->state = OHS_WRITE_HDR;
+ rctx->state = OHS_WRITE_HDR1;
- /* fall thru */
+ /* fall through */
+ case OHS_WRITE_HDR1:
case OHS_WRITE_HDR:
/* Copy some chunk of data from rctx->mem to rctx->wbio */
case OHS_WRITE_REQ:
/* Copy some chunk of data from rctx->req to rctx->wbio */
if (rctx->len_to_send > 0) {
- i = BIO_write(rctx->wbio, rctx->pos, rctx->len_to_send);
- if (i <= 0) {
+ size_t sz;
+
+ if (!BIO_write_ex(rctx->wbio, rctx->pos, rctx->len_to_send, &sz)) {
if (BIO_should_retry(rctx->wbio))
return -1;
rctx->state = OHS_ERROR;
return 0;
}
- rctx->pos += i;
- rctx->len_to_send -= i;
+ if (OSSL_TRACE_ENABLED(HTTP)) {
+ if (rctx->state == OHS_WRITE_HDR1)
+ OSSL_TRACE(HTTP, "Sending request header: [\n");
+ /* for request headers, this usually traces several lines at once: */
+ OSSL_TRACE_STRING(HTTP, rctx->state != OHS_WRITE_REQ || rctx->text,
+ rctx->state != OHS_WRITE_REQ, rctx->pos, sz);
+ OSSL_TRACE(HTTP, "]\n"); /* end of request header or content */
+ }
+ if (rctx->state == OHS_WRITE_HDR1)
+ rctx->state = OHS_WRITE_HDR;
+ rctx->pos += sz;
+ rctx->len_to_send -= sz;
goto next_io;
}
if (rctx->state == OHS_WRITE_HDR) {
@@ -603,6 +655,9 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
rctx->state = OHS_WRITE_REQ;
}
if (rctx->req != NULL && !BIO_eof(rctx->req)) {
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE1(HTTP, "Sending request content (likely %s)\n",
+ rctx->text ? "text" : "ASN.1");
n = BIO_read(rctx->req, rctx->buf, rctx->buf_size);
if (n <= 0) {
if (BIO_should_retry(rctx->req))
@@ -616,7 +671,7 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
}
rctx->state = OHS_FLUSH;
- /* fall thru */
+ /* fall through */
case OHS_FLUSH:
i = BIO_flush(rctx->wbio);
@@ -632,12 +687,13 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
rctx->state = OHS_ERROR;
return 0;
- case OHS_ERROR:
- return 0;
+ /* State machine could be broken up at this point and bulky code sections factorized out. */
case OHS_FIRSTLINE:
case OHS_HEADERS:
+ case OHS_HEADERS_ERROR:
case OHS_REDIRECT:
+ case OHS_ERROR_CONTENT:
/* Attempt to read a line in */
next_line:
@@ -663,6 +719,22 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
return 0;
}
+ if (rctx->state == OHS_ERROR_CONTENT) {
+ resp_len += n;
+ if (!check_max_len("error response content", rctx->max_resp_len, resp_len))
+ return 0;
+ if (OSSL_TRACE_ENABLED(HTTP)) /* dump response content line */
+ OSSL_TRACE_STRING(HTTP, got_text, 1, (unsigned char *)buf, n);
+ goto next_line;
+ }
+
+ resp_hdr_lines++;
+ if (rctx->max_hdr_lines != 0 && rctx->max_hdr_lines < resp_hdr_lines) {
+ ERR_raise(ERR_LIB_HTTP, HTTP_R_RESPONSE_TOO_MANY_HDRLINES);
+ rctx->state = OHS_ERROR;
+ return 0;
+ }
+
/* Don't allow excessive lines */
if (n == rctx->buf_size) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_RESPONSE_LINE_TOO_LONG);
@@ -670,9 +742,17 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
return 0;
}
- /* First line */
+ if (OSSL_TRACE_ENABLED(HTTP)) {
+ /* dump all response header line */
+ if (rctx->state == OHS_FIRSTLINE)
+ OSSL_TRACE(HTTP, "Receiving response header: [\n");
+ OSSL_TRACE_STRING(HTTP, 1, 1, (unsigned char *)buf, n);
+ }
+
+ /* First line in response header */
if (rctx->state == OHS_FIRSTLINE) {
- switch (parse_http_line1(buf, &found_keep_alive)) {
+ i = parse_http_line1(buf, &found_keep_alive);
+ switch (i) {
case HTTP_STATUS_CODE_OK:
rctx->state = OHS_HEADERS;
goto next_line;
@@ -686,8 +766,10 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
/* redirection is not supported/recommended for POST */
/* fall through */
default:
- rctx->state = OHS_ERROR;
- goto next_line;
+ /* must return content if status >= 400 */
+ rctx->state = i < HTTP_STATUS_CODES_NONFATAL_ERROR
+ ? OHS_HEADERS_ERROR : OHS_HEADERS;
+ goto next_line; /* continue parsing, also on HTTP error */
}
}
key = buf;
@@ -706,17 +788,33 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
if (rctx->state == OHS_REDIRECT
&& OPENSSL_strcasecmp(key, "Location") == 0) {
rctx->redirection_url = value;
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE(HTTP, "]\n");
+ /* stop reading due to redirect */
+ (void)BIO_reset(rctx->rbio);
return 0;
}
- if (rctx->state == OHS_HEADERS && rctx->expected_ct != NULL
- && OPENSSL_strcasecmp(key, "Content-Type") == 0) {
- if (OPENSSL_strcasecmp(rctx->expected_ct, value) != 0) {
- ERR_raise_data(ERR_LIB_HTTP, HTTP_R_UNEXPECTED_CONTENT_TYPE,
- "expected=%s, actual=%s",
- rctx->expected_ct, value);
- return 0;
+ if (OPENSSL_strcasecmp(key, "Content-Type") == 0) {
+ got_text = HAS_CASE_PREFIX(value, "text/");
+ if (rctx->state == OHS_HEADERS
+ && rctx->expected_ct != NULL) {
+ const char *semicolon;
+
+ if (OPENSSL_strcasecmp(rctx->expected_ct, value) != 0
+ /* ignore past ';' unless expected_ct contains ';' */
+ && (strchr(rctx->expected_ct, ';') != NULL
+ || (semicolon = strchr(value, ';')) == NULL
+ || (size_t)(semicolon - value) != strlen(rctx->expected_ct)
+ || OPENSSL_strncasecmp(rctx->expected_ct, value,
+ semicolon - value) != 0)) {
+ ERR_raise_data(ERR_LIB_HTTP,
+ HTTP_R_UNEXPECTED_CONTENT_TYPE,
+ "expected=%s, actual=%s",
+ rctx->expected_ct, value);
+ return 0;
+ }
+ found_expected_ct = 1;
}
- found_expected_ct = 1;
}
/* https://tools.ietf.org/html/rfc7230#section-6.3 Persistence */
@@ -726,14 +824,15 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
else if (OPENSSL_strcasecmp(value, "close") == 0)
found_keep_alive = 0;
} else if (OPENSSL_strcasecmp(key, "Content-Length") == 0) {
- resp_len = (size_t)strtoul(value, &line_end, 10);
+ size_t content_len = (size_t)strtoul(value, &line_end, 10);
+
if (line_end == value || *line_end != '\0') {
ERR_raise_data(ERR_LIB_HTTP,
HTTP_R_ERROR_PARSING_CONTENT_LENGTH,
"input=%s", value);
return 0;
}
- if (!check_set_resp_len(rctx, resp_len))
+ if (!check_set_resp_len("response content-length", rctx, content_len))
return 0;
}
}
@@ -743,9 +842,13 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
if (*p != '\r' && *p != '\n')
break;
}
- if (*p != '\0') /* not end of headers */
+ if (*p != '\0') /* not end of headers or not end of error reponse content */
goto next_line;
+ /* Found blank line(s) indicating end of headers */
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE(HTTP, "]\n"); /* end of response header */
+
if (rctx->keep_alive != 0 /* do not let server initiate keep_alive */
&& !found_keep_alive /* otherwise there is no change */) {
if (rctx->keep_alive == 2) {
@@ -756,8 +859,17 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
rctx->keep_alive = 0;
}
- if (rctx->state == OHS_ERROR)
+ if (rctx->state == OHS_HEADERS_ERROR) {
+ rctx->state = OHS_ERROR_CONTENT;
+ if (OSSL_TRACE_ENABLED(HTTP)) {
+ OSSL_TRACE1(HTTP, "Receiving error response content (likely %s): [\n",
+ got_text ? "text" : "ASN.1");
+ goto next_line;
+ }
+ /* discard response content when trace not enabled */
+ (void)BIO_reset(rctx->rbio);
return 0;
+ }
if (rctx->expected_ct != NULL && !found_expected_ct) {
ERR_raise_data(ERR_LIB_HTTP, HTTP_R_MISSING_CONTENT_TYPE,
@@ -770,11 +882,16 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
return 0;
}
+ /* Note: in non-error situations cannot trace response content */
if (!rctx->expect_asn1) {
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE(HTTP, "Receiving response text content\n");
rctx->state = OHS_STREAM;
return 1;
}
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE(HTTP, "Receiving response ASN.1 content\n");
rctx->state = OHS_ASN1_HEADER;
/* Fall thru */
@@ -818,18 +935,21 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
} else {
resp_len = *p + 2;
}
- if (!check_set_resp_len(rctx, resp_len))
+ if (!check_set_resp_len("ASN.1 DER content", rctx, resp_len))
return 0;
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE1(HTTP, "Expected response ASN.1 DER content length: %zd\n", resp_len);
rctx->state = OHS_ASN1_CONTENT;
/* Fall thru */
case OHS_ASN1_CONTENT:
- default:
n = BIO_get_mem_data(rctx->mem, NULL);
if (n < 0 || (size_t)n < rctx->resp_len)
goto next_io;
+ if (OSSL_TRACE_ENABLED(HTTP))
+ OSSL_TRACE(HTTP, "Finished receiving response ASN.1 content\n");
rctx->state = OHS_ASN1_DONE;
return 1;
}
@@ -1090,7 +1210,7 @@ BIO *OSSL_HTTP_exchange(OSSL_HTTP_REQ_CTX *rctx, char **redirection_url)
&& reason == CMP_R_POTENTIALLY_INVALID_CERTIFICATE)
#endif
) {
- if (rctx->server != NULL) {
+ if (rctx->server != NULL && *rctx->server != '\0') {
BIO_snprintf(buf, sizeof(buf), "server=http%s://%s%s%s",
rctx->use_ssl ? "s" : "", rctx->server,
rctx->port != NULL ? ":" : "",
@@ -1138,13 +1258,12 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
const char *expected_ct, int expect_asn1,
size_t max_resp_len, int timeout)
{
- char *current_url, *redirection_url = NULL;
+ char *current_url;
int n_redirs = 0;
char *host;
char *port;
char *path;
int use_ssl;
- OSSL_HTTP_REQ_CTX *rctx = NULL;
BIO *resp = NULL;
time_t max_time = timeout > 0 ? time(NULL) + timeout : 0;
@@ -1156,6 +1275,9 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
return NULL;
for (;;) {
+ OSSL_HTTP_REQ_CTX *rctx;
+ char *redirection_url;
+
if (!OSSL_HTTP_parse_url(current_url, &use_ssl, NULL /* user */, &host,
&port, NULL /* port_num */, &path, NULL, NULL))
break;
@@ -1164,6 +1286,7 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
use_ssl, bio, rbio, bio_update_fn, arg,
buf_size, timeout);
new_rpath:
+ redirection_url = NULL;
if (rctx != NULL) {
if (!OSSL_HTTP_set1_request(rctx, path, headers,
NULL /* content_type */,
@@ -1173,9 +1296,9 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
0 /* no keep_alive */)) {
OSSL_HTTP_REQ_CTX_free(rctx);
rctx = NULL;
- } else {
+ } else {
resp = OSSL_HTTP_exchange(rctx, &redirection_url);
- }
+ }
}
OPENSSL_free(path);
if (resp == NULL && redirection_url != NULL) {
@@ -1190,7 +1313,6 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
OPENSSL_free(host);
OPENSSL_free(port);
(void)OSSL_HTTP_close(rctx, 1);
- rctx = NULL;
BIO_free(resp);
OPENSSL_free(current_url);
return NULL;
@@ -1200,7 +1322,6 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
OPENSSL_free(host);
OPENSSL_free(port);
(void)OSSL_HTTP_close(rctx, 1);
- rctx = NULL;
continue;
}
/* if redirection not allowed, ignore it */
@@ -1210,7 +1331,6 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
OPENSSL_free(port);
if (!OSSL_HTTP_close(rctx, resp != NULL)) {
BIO_free(resp);
- rctx = NULL;
resp = NULL;
}
break;
@@ -1395,15 +1515,15 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port,
continue;
/* Check for HTTP/1.x */
- if (!HAS_PREFIX(mbuf, HTTP_PREFIX) != 0) {
+ mbufp = mbuf;
+ if (!CHECK_AND_SKIP_PREFIX(mbufp, HTTP_PREFIX)) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_HEADER_PARSE_ERROR);
BIO_printf(bio_err, "%s: HTTP CONNECT failed, non-HTTP response\n",
prog);
/* Wrong protocol, not even HTTP, so stop reading headers */
goto end;
}
- mbufp = mbuf + strlen(HTTP_PREFIX);
- if (!HAS_PREFIX(mbufp, HTTP_VERSION_PATT) != 0) {
+ if (!HAS_PREFIX(mbufp, HTTP_VERSION_PATT)) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_RECEIVED_WRONG_HTTP_VERSION);
BIO_printf(bio_err,
"%s: HTTP CONNECT failed, bad HTTP version %.*s\n",
@@ -1414,6 +1534,8 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port,
/* RFC 7231 4.3.6: any 2xx status code is valid */
if (!HAS_PREFIX(mbufp, " 2")) {
+ if (ossl_isspace(*mbufp))
+ mbufp++;
/* chop any trailing whitespace */
while (read_len > 0 && ossl_isspace(mbuf[read_len - 1]))
read_len--;
@@ -1432,7 +1554,7 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port,
do {
/*
* This does not necessarily catch the case when the full
- * HTTP response came in in more than a single TCP message.
+ * HTTP response came in more than a single TCP message.
*/
read_len = BIO_gets(fbio, mbuf, BUF_SIZE);
} while (read_len > 2);