// Copyright 2015 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include namespace brillo { namespace { // TODO(crbug.com/984789): Remove once support for OpenSSL <1.1 is dropped. #if OPENSSL_VERSION_NUMBER < 0x10100000L static void BIO_set_data(BIO* a, void* ptr) { a->ptr = ptr; } static void* BIO_get_data(BIO* a) { return a->ptr; } static void BIO_set_init(BIO* a, int init) { a->init = init; } static int BIO_get_init(BIO* a) { return a->init; } static void BIO_set_shutdown(BIO* a, int shut) { a->shutdown = shut; } #endif // Internal functions for implementing OpenSSL BIO on brillo::Stream. int stream_write(BIO* bio, const char* buf, int size) { brillo::Stream* stream = static_cast(BIO_get_data(bio)); size_t written = 0; BIO_clear_retry_flags(bio); if (!stream->WriteNonBlocking(buf, size, &written, nullptr)) return -1; if (written == 0) { // Socket's output buffer is full, try again later. BIO_set_retry_write(bio); return -1; } return base::checked_cast(written); } int stream_read(BIO* bio, char* buf, int size) { brillo::Stream* stream = static_cast(BIO_get_data(bio)); size_t read = 0; BIO_clear_retry_flags(bio); bool eos = false; if (!stream->ReadNonBlocking(buf, size, &read, &eos, nullptr)) return -1; if (read == 0 && !eos) { // If no data is available on the socket and it is still not closed, // ask OpenSSL to try again later. BIO_set_retry_read(bio); return -1; } return base::checked_cast(read); } // NOLINTNEXTLINE(runtime/int) long stream_ctrl(BIO* bio, int cmd, long /* num */, void* /* ptr */) { if (cmd == BIO_CTRL_FLUSH) { brillo::Stream* stream = static_cast(BIO_get_data(bio)); return stream->FlushBlocking(nullptr) ? 1 : 0; } return 0; } int stream_new(BIO* bio) { // By default do not close underlying stream on shutdown. BIO_set_shutdown(bio, 0); BIO_set_init(bio, 0); return 1; } int stream_free(BIO* bio) { if (!bio) return 0; if (BIO_get_init(bio)) { BIO_set_data(bio, nullptr); BIO_set_init(bio, 0); } return 1; } #if OPENSSL_VERSION_NUMBER < 0x10100000L // TODO(crbug.com/984789): Remove #ifdef once support for OpenSSL <1.1 is // dropped. // BIO_METHOD structure describing the BIO built on top of brillo::Stream. BIO_METHOD stream_method = { 0x7F | BIO_TYPE_SOURCE_SINK, // type: 0x7F is an arbitrary unused type ID. "stream", // name stream_write, // write function stream_read, // read function nullptr, // puts function, not implemented nullptr, // gets function, not implemented stream_ctrl, // control function stream_new, // creation stream_free, // free nullptr, // callback function, not used }; BIO_METHOD* stream_get_method() { return &stream_method; } #else BIO_METHOD* stream_get_method() { static BIO_METHOD* stream_method; if (!stream_method) { stream_method = BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "stream"); BIO_meth_set_write(stream_method, stream_write); BIO_meth_set_read(stream_method, stream_read); BIO_meth_set_ctrl(stream_method, stream_ctrl); BIO_meth_set_create(stream_method, stream_new); BIO_meth_set_destroy(stream_method, stream_free); } return stream_method; } #endif } // anonymous namespace BIO* BIO_new_stream(brillo::Stream* stream) { BIO* bio = BIO_new(stream_get_method()); if (bio) { BIO_set_data(bio, stream); BIO_set_init(bio, 1); } return bio; } } // namespace brillo