1// Copyright 2015 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <brillo/streams/openssl_stream_bio.h>
6
7#include <openssl/bio.h>
8
9#include <base/numerics/safe_conversions.h>
10#include <brillo/streams/stream.h>
11
12namespace brillo {
13
14namespace {
15
16// Internal functions for implementing OpenSSL BIO on brillo::Stream.
17int stream_write(BIO* bio, const char* buf, int size) {
18  brillo::Stream* stream = static_cast<brillo::Stream*>(bio->ptr);
19  size_t written = 0;
20  BIO_clear_retry_flags(bio);
21  if (!stream->WriteNonBlocking(buf, size, &written, nullptr))
22    return -1;
23
24  if (written == 0) {
25    // Socket's output buffer is full, try again later.
26    BIO_set_retry_write(bio);
27    return -1;
28  }
29  return base::checked_cast<int>(written);
30}
31
32int stream_read(BIO* bio, char* buf, int size) {
33  brillo::Stream* stream = static_cast<brillo::Stream*>(bio->ptr);
34  size_t read = 0;
35  BIO_clear_retry_flags(bio);
36  bool eos = false;
37  if (!stream->ReadNonBlocking(buf, size, &read, &eos, nullptr))
38    return -1;
39
40  if (read == 0 && !eos) {
41    // If no data is available on the socket and it is still not closed,
42    // ask OpenSSL to try again later.
43    BIO_set_retry_read(bio);
44    return -1;
45  }
46  return base::checked_cast<int>(read);
47}
48
49// NOLINTNEXTLINE(runtime/int)
50long stream_ctrl(BIO* bio, int cmd, long /* num */, void* /* ptr */) {
51  if (cmd == BIO_CTRL_FLUSH) {
52    brillo::Stream* stream = static_cast<brillo::Stream*>(bio->ptr);
53    return stream->FlushBlocking(nullptr) ? 1 : 0;
54  }
55  return 0;
56}
57
58int stream_new(BIO* bio) {
59  bio->shutdown = 0;  // By default do not close underlying stream on shutdown.
60  bio->init = 0;
61  bio->num = -1;  // not used.
62  return 1;
63}
64
65int stream_free(BIO* bio) {
66  if (!bio)
67    return 0;
68
69  if (bio->init) {
70    bio->ptr = nullptr;
71    bio->init = 0;
72  }
73  return 1;
74}
75
76// BIO_METHOD structure describing the BIO built on top of brillo::Stream.
77BIO_METHOD stream_method = {
78    0x7F | BIO_TYPE_SOURCE_SINK,  // type: 0x7F is an arbitrary unused type ID.
79    "stream",      // name
80    stream_write,  // write function
81    stream_read,   // read function
82    nullptr,       // puts function, not implemented
83    nullptr,       // gets function, not implemented
84    stream_ctrl,   // control function
85    stream_new,    // creation
86    stream_free,   // free
87    nullptr,       // callback function, not used
88};
89
90}  // anonymous namespace
91
92BIO* BIO_new_stream(brillo::Stream* stream) {
93  BIO* bio = BIO_new(&stream_method);
94  if (bio) {
95    bio->ptr = stream;
96    bio->init = 1;
97  }
98  return bio;
99}
100
101}  // namespace brillo
102