5 #include <boost/asio/ssl/error.hpp> 7 #include "base/logging.h" 8 #include "util/http/ssl_stream.h" 14 using namespace boost;
15 using asio::ssl::detail::stream_core;
19 using asio::ssl::stream_base;
20 using asio::ssl::detail::engine;
22 Engine::Engine(SSL_CTX* context) : ssl_(::SSL_new(context)) {
25 ::SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE);
26 ::SSL_set_mode(ssl_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
27 ::SSL_set_mode(ssl_, SSL_MODE_RELEASE_BUFFERS);
30 ::BIO_new_bio_pair(&int_bio, 0, &ext_bio_, 0);
31 ::SSL_set_bio(ssl_, int_bio, int_bio);
35 CHECK(!SSL_get_app_data(ssl_));
41 system::error_code Engine::set_verify_mode(verify_mode v, system::error_code& ec) {
42 ::SSL_set_verify(ssl_, v, ::SSL_get_verify_callback(ssl_));
44 ec = system::error_code();
48 engine::want Engine::perform(
int (Engine::*op)(
void*, std::size_t),
void* data, std::size_t length,
49 system::error_code& ec, std::size_t* bytes_transferred) {
50 std::size_t pending_output_before = ::BIO_ctrl_pending(ext_bio_);
52 int result = (this->*op)(data, length);
53 int ssl_error = ::SSL_get_error(ssl_, result);
54 int sys_error = static_cast<int>(::ERR_get_error());
55 std::size_t pending_output_after = ::BIO_ctrl_pending(ext_bio_);
57 if (ssl_error == SSL_ERROR_SSL) {
58 ec = system::error_code(sys_error, asio::error::get_ssl_category());
59 return pending_output_after > pending_output_before ? engine::want_output
60 : engine::want_nothing;
63 if (ssl_error == SSL_ERROR_SYSCALL) {
65 ec = asio::ssl::error::unspecified_system_error;
67 ec = system::error_code(sys_error, asio::error::get_ssl_category());
69 return pending_output_after > pending_output_before ? engine::want_output
70 : engine::want_nothing;
73 if (result > 0 && bytes_transferred)
74 *bytes_transferred = static_cast<std::size_t>(result);
76 if (ssl_error == SSL_ERROR_WANT_WRITE) {
77 ec = system::error_code();
78 return engine::want_output_and_retry;
79 }
else if (pending_output_after > pending_output_before) {
80 ec = system::error_code();
81 return result > 0 ? engine::want_output : engine::want_output_and_retry;
82 }
else if (ssl_error == SSL_ERROR_WANT_READ) {
83 ec = system::error_code();
84 return engine::want_input_and_retry;
85 }
else if (ssl_error == SSL_ERROR_ZERO_RETURN) {
86 ec = asio::error::eof;
87 return engine::want_nothing;
88 }
else if (ssl_error == SSL_ERROR_NONE) {
89 ec = system::error_code();
90 return engine::want_nothing;
92 ec = asio::ssl::error::unexpected_result;
93 return engine::want_nothing;
97 int Engine::do_connect(
void*, std::size_t) {
98 return ::SSL_connect(ssl_);
101 int Engine::do_shutdown(
void*, std::size_t) {
102 int result = ::SSL_shutdown(ssl_);
104 result = ::SSL_shutdown(ssl_);
108 int Engine::do_read(
void* data, std::size_t length) {
109 return ::SSL_read(ssl_, data, length < INT_MAX ? static_cast<int>(length) : INT_MAX);
112 int Engine::do_write(
void* data, std::size_t length) {
113 return ::SSL_write(ssl_, data, length < INT_MAX ? static_cast<int>(length) : INT_MAX);
116 Engine::want Engine::handshake(stream_base::handshake_type type, system::error_code& ec) {
117 CHECK_EQ(stream_base::client, type);
119 return perform(&Engine::do_connect, 0, 0, ec, 0);
122 Engine::want Engine::shutdown(system::error_code& ec) {
123 return perform(&Engine::do_shutdown, 0, 0, ec, 0);
126 Engine::want Engine::write(
const asio::const_buffer& data, system::error_code& ec,
127 std::size_t& bytes_transferred) {
128 if (data.size() == 0) {
129 ec = system::error_code();
130 return engine::want_nothing;
133 return perform(&Engine::do_write, const_cast<void*>(data.data()), data.size(), ec,
137 Engine::want Engine::read(
const asio::mutable_buffer& data, system::error_code& ec,
138 std::size_t& bytes_transferred) {
139 if (data.size() == 0) {
140 ec = system::error_code();
141 return engine::want_nothing;
144 return perform(&Engine::do_read, data.data(), data.size(), ec, &bytes_transferred);
147 void Engine::GetWriteBuf(asio::mutable_buffer* mbuf) {
150 int res = BIO_nwrite0(ext_bio_, &buf);
152 *mbuf = asio::mutable_buffer{buf, size_t(res)};
155 void Engine::CommitWriteBuf(
size_t sz) {
156 CHECK_EQ(sz, BIO_nwrite(ext_bio_,
nullptr, sz));
159 void Engine::GetReadBuf(asio::const_buffer* cbuf) {
162 int res = BIO_nread0(ext_bio_, &buf);
164 *cbuf = asio::const_buffer{buf, size_t(res)};
167 void Engine::AdvanceRead(
size_t sz) {
168 CHECK_EQ(sz, BIO_nread(ext_bio_,
nullptr, sz));
171 const system::error_code& Engine::map_error_code(system::error_code& ec)
const {
173 if (ec != asio::error::eof)
177 if (BIO_wpending(ext_bio_)) {
178 ec = asio::ssl::error::stream_truncated;
184 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) 185 if (SSL_version(ssl_) == SSL2_VERSION)
187 #endif // (OPENSSL_VERSION_NUMBER < 0x10100000L) 190 if ((::SSL_get_shutdown(ssl_) & SSL_RECEIVED_SHUTDOWN) == 0) {
191 ec = asio::ssl::error::stream_truncated;
199 SslStream::SslStream(FiberSyncSocket&& arg, asio::ssl::context& ctx)
200 : engine_(ctx.native_handle()), next_layer_(std::move(arg)) {
203 void SslStream::handshake(Impl::handshake_type type, error_code& ec) {
204 namespace a = ::boost::asio;
205 auto cb = [&](detail::Engine& eng, error_code& ec,
size_t& bytes_transferred) {
206 bytes_transferred = 0;
207 return eng.handshake(type, ec);
213 void SslStream::IoHandler(want op_code, system::error_code& ec) {
214 using asio::ssl::detail::engine;
215 DVLOG(1) <<
"io_fun::start";
216 asio::mutable_buffer mb;
217 asio::const_buffer cbuf;
221 case engine::want_input_and_retry:
222 DVLOG(2) <<
"want_input_and_retry";
223 engine_.GetWriteBuf(&mb);
224 buf_size = next_layer_.read_some(mb, ec);
226 engine_.CommitWriteBuf(buf_size);
230 case engine::want_output_and_retry:
231 case engine::want_output:
233 DVLOG(2) <<
"engine::want_output" 234 << (op_code == engine::want_output_and_retry ?
"_and_retry" :
"");
235 engine_.GetReadBuf(&cbuf);
240 asio::write(next_layer_, cbuf, ec);
242 engine_.AdvanceRead(cbuf.size());