/*
- * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2001-2021 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
struct ossl_http_req_ctx_st {
int state; /* Current I/O state */
- unsigned char *iobuf; /* Line buffer */
- int iobuflen; /* Line buffer length */
+ unsigned char *readbuf; /* Buffer for reading response by line */
+ int readbuflen; /* Buffer length, equals maxline */
BIO *wbio; /* BIO to send request to */
BIO *rbio; /* BIO to read response from */
BIO *mem; /* Memory BIO response is built into */
- int method_GET; /* HTTP method "GET" or "POST" */
+ int method_POST; /* HTTP method is "POST" (else "GET") */
const char *expected_ct; /* expected Content-Type, or NULL */
int expect_asn1; /* response must be ASN.1-encoded */
+ long len_to_send; /* number of bytes in request still to send */
unsigned long resp_len; /* length of response */
unsigned long max_resp_len; /* Maximum length of response */
time_t max_time; /* Maximum end time of the transfer, or 0 */
char *redirection_url; /* Location given with HTTP status 301/302 */
};
-#define HTTP_DEFAULT_MAX_LINE_LENGTH (4 * 1024)
-#define HTTP_DEFAULT_MAX_RESP_LEN (100 * 1024)
-
/* HTTP states */
#define OHS_NOREAD 0x1000 /* If set no reading should be performed */
#define OHS_HEADERS 2 /* MIME headers being read */
#define OHS_ASN1_HEADER 3 /* HTTP initial header (tag+length) being read */
#define OHS_CONTENT 4 /* HTTP content octets being read */
-#define OHS_WRITE_INIT (5 | OHS_NOREAD) /* 1st call: ready to start I/O */
+#define OHS_WRITE_INIT (5 | OHS_NOREAD) /* 1st call: ready to start send */
#define OHS_WRITE (6 | OHS_NOREAD) /* Request being sent */
#define OHS_FLUSH (7 | OHS_NOREAD) /* Request being flushed */
#define OHS_DONE (8 | OHS_NOREAD) /* Completed */
#define OHS_HTTP_HEADER (9 | OHS_NOREAD) /* Headers set, w/o final \r\n */
OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio,
- int method_GET, int maxline,
+ int method_POST, int maxline,
unsigned long max_resp_len,
int timeout,
const char *expected_content_type,
if ((rctx = OPENSSL_zalloc(sizeof(*rctx))) == NULL)
return NULL;
rctx->state = OHS_ERROR;
- rctx->iobuflen = maxline > 0 ? maxline : HTTP_DEFAULT_MAX_LINE_LENGTH;
- rctx->iobuf = OPENSSL_malloc(rctx->iobuflen);
+ rctx->readbuflen = maxline > 0 ? maxline : HTTP_DEFAULT_MAX_LINE_LENGTH;
+ rctx->readbuf = OPENSSL_malloc(rctx->readbuflen);
rctx->wbio = wbio;
rctx->rbio = rbio;
- rctx->mem = BIO_new(BIO_s_mem());
- if (rctx->iobuf == NULL || rctx->mem == NULL) {
- OSSL_HTTP_REQ_CTX_free(rctx);
+ if (rctx->readbuf == NULL) {
+ OPENSSL_free(rctx);
return NULL;
}
- rctx->method_GET = method_GET;
+ rctx->method_POST = method_POST;
rctx->expected_ct = expected_content_type;
rctx->expect_asn1 = expect_asn1;
rctx->resp_len = 0;
OSSL_HTTP_REQ_CTX_set_max_response_length(rctx, max_resp_len);
rctx->max_time = timeout > 0 ? time(NULL) + timeout : 0;
+ /* everything else is 0, e.g. rctx->len_to_send, or NULL, e.g. rctx->mem */
return rctx;
}
if (rctx == NULL)
return;
BIO_free(rctx->mem); /* this may indirectly call ERR_clear_error() */
- OPENSSL_free(rctx->iobuf);
+ OPENSSL_free(rctx->readbuf);
OPENSSL_free(rctx);
}
}
/*
- * Create HTTP header using given op and path (or "/" in case path is NULL).
+ * Create request line using |ctx| and |path| (or "/" in case |path| is NULL).
* Server name (and port) must be given if and only if plain HTTP proxy is used.
*/
-int OSSL_HTTP_REQ_CTX_header(OSSL_HTTP_REQ_CTX *rctx, const char *server,
- const char *port, const char *path)
+int OSSL_HTTP_REQ_CTX_set_request_line(OSSL_HTTP_REQ_CTX *rctx,
+ const char *server, const char *port,
+ const char *path)
{
if (rctx == NULL) {
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ BIO_free(rctx->mem);
+ if ((rctx->mem = BIO_new(BIO_s_mem())) == NULL)
+ return 0;
- if (BIO_printf(rctx->mem, "%s ", rctx->method_GET ? "GET" : "POST") <= 0)
+ if (BIO_printf(rctx->mem, "%s ", rctx->method_POST ? "POST" : "GET") <= 0)
return 0;
if (server != NULL) { /* HTTP (but not HTTPS) proxy is used */
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (rctx->mem == NULL) {
+ ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
if (BIO_puts(rctx->mem, name) <= 0)
return 0;
return 1;
}
-static int OSSL_HTTP_REQ_CTX_content(OSSL_HTTP_REQ_CTX *rctx,
- const char *content_type, BIO *req_mem)
+static int OSSL_HTTP_REQ_CTX_set_content(OSSL_HTTP_REQ_CTX *rctx,
+ const char *content_type, BIO *req_mem)
{
const unsigned char *req;
long req_len;
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (rctx->mem == NULL || !rctx->method_POST) {
+ ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
if (content_type != NULL
&& BIO_printf(rctx->mem, "Content-Type: %s\r\n", content_type) <= 0)
}
res = (mem = HTTP_asn1_item2bio(it, req)) != NULL
- && OSSL_HTTP_REQ_CTX_content(rctx, content_type, mem);
+ && OSSL_HTTP_REQ_CTX_set_content(rctx, content_type, mem);
BIO_free(mem);
return res;
}
}
/* remaining parameters are checked indirectly by the functions called */
- if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, req_mem == NULL, maxline,
+ if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, req_mem != NULL, maxline,
max_resp_len, timeout,
expected_content_type, expect_asn1))
== NULL)
return NULL;
- if (OSSL_HTTP_REQ_CTX_header(rctx, use_http_proxy ? server : NULL,
- port, path)
+ if (OSSL_HTTP_REQ_CTX_set_request_line(rctx,
+ use_http_proxy ? server : NULL, port,
+ path)
&& OSSL_HTTP_REQ_CTX_add1_headers(rctx, headers, server)
&& (req_mem == NULL
- || OSSL_HTTP_REQ_CTX_content(rctx, content_type, req_mem)))
+ || OSSL_HTTP_REQ_CTX_set_content(rctx, content_type, req_mem)))
return rctx;
OSSL_HTTP_REQ_CTX_free(rctx);
return retcode;
default:
if (retcode < 400)
- ERR_raise(ERR_LIB_HTTP, HTTP_R_STATUS_CODE_UNSUPPORTED);
+ retcode = HTTP_R_STATUS_CODE_UNSUPPORTED;
else
- ERR_raise(ERR_LIB_HTTP, HTTP_R_RECEIVED_ERROR);
+ retcode = HTTP_R_RECEIVED_ERROR;
if (*reason == '\0')
- ERR_add_error_data(2, "Code=", code);
+ ERR_raise_data(ERR_LIB_HTTP, retcode, "Code=%s", code);
else
- ERR_add_error_data(4, "Code=", code, ",Reason=", reason);
+ ERR_raise_data(ERR_LIB_HTTP, retcode,
+ "Code=%s, Reason=%s", code, reason);
return 0;
}
}
static int check_set_resp_len(OSSL_HTTP_REQ_CTX *rctx, unsigned long len)
{
- const char *tag = NULL;
- unsigned long val = 0;
-
- if (len > rctx->max_resp_len) {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_MAX_RESP_LEN_EXCEEDED);
- tag = ",max=";
- val = rctx->max_resp_len;
- }
- if (rctx->resp_len != 0 && rctx->resp_len != len) {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_INCONSISTENT_CONTENT_LENGTH);
- tag = ",before=";
- val = rctx->resp_len;
- }
- if (tag != NULL) {
- char len_str[32];
- char str[32];
-
- BIO_snprintf(len_str, sizeof(len_str), "%lu", len);
- BIO_snprintf(str, sizeof(str), "%lu", val);
- ERR_add_error_data(4, "length=", len_str, tag, str);
- return 0;
- }
+ if (len > rctx->max_resp_len)
+ ERR_raise_data(ERR_LIB_HTTP, HTTP_R_MAX_RESP_LEN_EXCEEDED,
+ "length=%lu, max=%lu", len, rctx->max_resp_len);
+ if (rctx->resp_len != 0 && rctx->resp_len != len)
+ ERR_raise_data(ERR_LIB_HTTP, HTTP_R_INCONSISTENT_CONTENT_LENGTH,
+ "ASN.1 length=%lu, Content-Length=%lu",
+ len, rctx->resp_len);
rctx->resp_len = len;
return 1;
}
int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx)
{
int i;
- long n, n_to_send = 0;
+ long n;
unsigned long resp_len;
const unsigned char *p;
char *key, *value, *line_end = NULL;
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
+ if (rctx->mem == NULL || rctx->wbio == NULL || rctx->rbio == NULL) {
+ ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
rctx->redirection_url = NULL;
next_io:
if ((rctx->state & OHS_NOREAD) == 0) {
- n = BIO_read(rctx->rbio, rctx->iobuf, rctx->iobuflen);
+ n = BIO_read(rctx->rbio, rctx->readbuf, rctx->readbuflen);
if (n <= 0) {
if (BIO_should_retry(rctx->rbio))
return -1;
+ ERR_raise(ERR_LIB_HTTP, HTTP_R_FAILED_READING_DATA);
return 0;
}
/* Write data to memory BIO */
- if (BIO_write(rctx->mem, rctx->iobuf, n) != n)
+ if (BIO_write(rctx->mem, rctx->readbuf, n) != n)
return 0;
}
/* fall thru */
case OHS_WRITE_INIT:
- n_to_send = BIO_get_mem_data(rctx->mem, NULL);
+ rctx->len_to_send = BIO_get_mem_data(rctx->mem, NULL);
rctx->state = OHS_WRITE;
/* fall thru */
case OHS_WRITE:
- n = BIO_get_mem_data(rctx->mem, &p);
-
- i = BIO_write(rctx->wbio, p + (n - n_to_send), n_to_send);
+ n = BIO_get_mem_data(rctx->mem, &p) - rctx->len_to_send;
+ i = BIO_write(rctx->wbio, p + n, rctx->len_to_send);
if (i <= 0) {
if (BIO_should_retry(rctx->wbio))
return 0;
}
- n_to_send -= i;
+ rctx->len_to_send -= i;
- if (n_to_send > 0)
+ if (rctx->len_to_send > 0)
goto next_io;
rctx->state = OHS_FLUSH;
*/
n = BIO_get_mem_data(rctx->mem, &p);
if (n <= 0 || memchr(p, '\n', n) == 0) {
- if (n >= rctx->iobuflen) {
+ if (n >= rctx->readbuflen) {
rctx->state = OHS_ERROR;
return 0;
}
goto next_io;
}
- n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
+ n = BIO_gets(rctx->mem, (char *)rctx->readbuf, rctx->readbuflen);
if (n <= 0) {
if (BIO_should_retry(rctx->mem))
}
/* Don't allow excessive lines */
- if (n == rctx->iobuflen) {
+ if (n == rctx->readbuflen) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_RESPONSE_LINE_TOO_LONG);
rctx->state = OHS_ERROR;
return 0;
/* First line */
if (rctx->state == OHS_FIRSTLINE) {
- switch (parse_http_line1((char *)rctx->iobuf)) {
+ switch (parse_http_line1((char *)rctx->readbuf)) {
case HTTP_STATUS_CODE_OK:
rctx->state = OHS_HEADERS;
goto next_line;
case HTTP_STATUS_CODE_MOVED_PERMANENTLY:
case HTTP_STATUS_CODE_FOUND: /* i.e., moved temporarily */
- if (rctx->method_GET) {
+ if (!rctx->method_POST) { /* method is GET */
rctx->state = OHS_REDIRECT;
goto next_line;
}
return 0;
}
}
- key = (char *)rctx->iobuf;
+ key = (char *)rctx->readbuf;
value = strchr(key, ':');
if (value != NULL) {
*(value++) = '\0';
if (rctx->expected_ct != NULL
&& strcasecmp(key, "Content-Type") == 0) {
if (strcasecmp(rctx->expected_ct, value) != 0) {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_UNEXPECTED_CONTENT_TYPE);
- ERR_add_error_data(4, "expected=", rctx->expected_ct,
- ",actual=", value);
+ ERR_raise_data(ERR_LIB_HTTP, HTTP_R_UNEXPECTED_CONTENT_TYPE,
+ "expected=%s, actual=%s",
+ rctx->expected_ct, value);
return 0;
}
rctx->expected_ct = NULL; /* content-type has been found */
if (strcasecmp(key, "Content-Length") == 0) {
resp_len = strtoul(value, &line_end, 10);
if (line_end == value || *line_end != '\0') {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_CONTENT_LENGTH);
- ERR_add_error_data(2, "input=", value);
+ 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))
}
}
- /* Look for blank line: end of headers */
- for (p = rctx->iobuf; *p != '\0'; p++) {
+ /* Look for blank line indicating end of headers */
+ for (p = rctx->readbuf; *p != '\0'; p++) {
if (*p != '\r' && *p != '\n')
break;
}
goto next_line;
if (rctx->expected_ct != NULL) {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_MISSING_CONTENT_TYPE);
- ERR_add_error_data(2, "expected=", rctx->expected_ct);
+ ERR_raise_data(ERR_LIB_HTTP, HTTP_R_MISSING_CONTENT_TYPE,
+ "expected=%s", rctx->expected_ct);
return 0;
}
if (rctx->state == OHS_REDIRECT) {
BIO_printf(fbio, "Proxy-Authorization: Basic %s\r\n", proxyauthenc);
OPENSSL_clear_free(proxyauthenc, strlen(proxyauthenc));
}
- proxy_end:
+ proxy_end:
OPENSSL_clear_free(proxyauth, len);
if (proxyauthenc == NULL)
goto end;
while (read_len > 0 && ossl_isspace(mbuf[read_len - 1]))
read_len--;
mbuf[read_len] = '\0';
- ERR_raise(ERR_LIB_HTTP, HTTP_R_CONNECT_FAILURE);
- ERR_add_error_data(2, "Reason=", mbufp);
+ ERR_raise_data(ERR_LIB_HTTP, HTTP_R_CONNECT_FAILURE,
+ "Reason=%s", mbufp);
BIO_printf(bio_err, "%s: HTTP CONNECT failed, Reason=%s\n",
prog, mbufp);
goto end;