in crypto/ocsp/ocsp_http.c [149:358]
int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx) {
int ret, tmp_data_len;
size_t data_len;
const unsigned char *data;
next_io:
if (!(rctx->state & OHS_NOREAD)) {
tmp_data_len = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen);
if (tmp_data_len <= 0) {
if (BIO_should_retry(rctx->io)) {
return -1;
}
return 0;
}
// Write data to memory BIO.
if (BIO_write(rctx->mem, rctx->iobuf, tmp_data_len) != tmp_data_len) {
return 0;
}
data_len = (size_t)tmp_data_len;
}
switch (rctx->state) {
case OHS_HTTP_HEADER:
// Last operation was adding headers: need a final "\r\n".
if (BIO_write(rctx->mem, "\r\n", 2) != 2) {
rctx->state = OHS_ERROR;
return 0;
}
rctx->state = OHS_ASN1_WRITE_INIT;
OPENSSL_FALLTHROUGH;
case OHS_ASN1_WRITE_INIT:
if(!BIO_mem_contents(rctx->mem, NULL, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
rctx->asn1_len = data_len;
rctx->state = OHS_ASN1_WRITE;
OPENSSL_FALLTHROUGH;
case OHS_ASN1_WRITE:
if(!BIO_mem_contents(rctx->mem, &data, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
int write_len = BIO_write(rctx->io, data + (data_len - rctx->asn1_len),
(int)rctx->asn1_len);
if (write_len <= 0) {
if (BIO_should_retry(rctx->io)) {
return -1;
}
rctx->state = OHS_ERROR;
return 0;
}
rctx->asn1_len -= write_len;
if (rctx->asn1_len > 0) {
goto next_io;
}
rctx->state = OHS_ASN1_FLUSH;
if(!BIO_reset(rctx->mem)) {
return 0;
}
OPENSSL_FALLTHROUGH;
case OHS_ASN1_FLUSH:
ret = BIO_flush(rctx->io);
if (ret > 0) {
rctx->state = OHS_FIRSTLINE;
goto next_io;
}
if (BIO_should_retry(rctx->io)) {
return -1;
}
rctx->state = OHS_ERROR;
return 0;
case OHS_ERROR:
return 0;
case OHS_FIRSTLINE:
case OHS_HEADERS:
// Attempt to read a line in.
next_line:
// Due to strange memory BIO behaviour with BIO_gets we have to
// check there's a complete line in there before calling BIO_gets
// or we'll just get a partial read.
if(!BIO_mem_contents(rctx->mem, &data, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
if ((data_len <= 0) || !memchr(data, '\n', data_len)) {
if (data_len >= (size_t)rctx->iobuflen) {
rctx->state = OHS_ERROR;
return 0;
}
goto next_io;
}
tmp_data_len = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen);
if (tmp_data_len <= 0) {
if (BIO_should_retry(rctx->mem)) {
goto next_io;
}
rctx->state = OHS_ERROR;
return 0;
}
// Don't allow excessive lines.
if (tmp_data_len >= rctx->iobuflen) {
rctx->state = OHS_ERROR;
return 0;
}
data_len = (size_t)tmp_data_len;
// First line.
if (rctx->state == OHS_FIRSTLINE) {
if (parse_http_line((char *)rctx->iobuf)) {
rctx->state = OHS_HEADERS;
goto next_line;
} else {
rctx->state = OHS_ERROR;
return 0;
}
} else {
// Look for blank line: end of headers.
for (data = rctx->iobuf; *data; data++) {
if ((*data != '\r') && (*data != '\n')) {
break;
}
}
if (*data != '\0') {
goto next_line;
}
rctx->state = OHS_ASN1_HEADER;
}
OPENSSL_FALLTHROUGH;
case OHS_ASN1_HEADER:
// Now reading ASN1 header: can read at least 2 bytes which is
// enough for ASN1 SEQUENCE header and either length field or at
// least the length of the length field.
if(!BIO_mem_contents(rctx->mem, &data, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
if (data_len < 2) {
goto next_io;
}
// Check it is an ASN1 SEQUENCE.
if (*data++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) {
rctx->state = OHS_ERROR;
return 0;
}
// Check out length field. This is checking to see if the length is
// encoded as long-form (multiple bytes) versus being a length that
// can be encoded into 7 bits. "0x80" implies "0x80 + N", where N is
// the number of length bytes to follow.
if ((*data & 0x80) != 0) {
// If MSB set on initial length octet we can now always read 6
// octets: make sure we have them.
if (data_len < 6) {
goto next_io;
}
data_len = *data & 0x7F;
// Not NDEF or excessive length.
if (!data_len || (data_len > 4)) {
rctx->state = OHS_ERROR;
return 0;
}
data++;
rctx->asn1_len = 0;
for (size_t i = 0; i < data_len; i++) {
rctx->asn1_len <<= 8;
rctx->asn1_len |= *data++;
}
if (rctx->asn1_len > rctx->max_resp_len) {
rctx->state = OHS_ERROR;
return 0;
}
rctx->asn1_len += data_len + 2;
} else {
rctx->asn1_len = *data + 2;
}
rctx->state = OHS_ASN1_CONTENT;
OPENSSL_FALLTHROUGH;
case OHS_ASN1_CONTENT:
if(!BIO_mem_contents(rctx->mem, NULL, &data_len)) {
rctx->state = OHS_ERROR;
return 0;
}
if (data_len < rctx->asn1_len) {
goto next_io;
}
rctx->state = OHS_DONE;
return 1;
case OHS_DONE:
return 1;
}
return 0;
}