int OCSP_REQ_CTX_nbio()

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;
}