Vitaly Buka | 4615e0d | 2015-10-14 15:35:12 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Weave Authors. All rights reserved. |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Johan Euphrosine | 3523fdd | 2015-10-14 20:46:05 -0700 | [diff] [blame] | 5 | #include "examples/provider/ssl_stream.h" |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 6 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 7 | #include <openssl/err.h> |
| 8 | |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 9 | #include <base/bind.h> |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 10 | #include <base/bind_helpers.h> |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 11 | #include <weave/provider/task_runner.h> |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 12 | |
| 13 | namespace weave { |
| 14 | namespace examples { |
| 15 | |
Johan Euphrosine | 2121c19 | 2015-10-15 14:11:55 -0700 | [diff] [blame] | 16 | namespace { |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 17 | |
| 18 | void AddSslError(ErrorPtr* error, |
| 19 | const tracked_objects::Location& location, |
| 20 | const std::string& error_code, |
| 21 | unsigned long ssl_error_code) { |
| 22 | ERR_load_BIO_strings(); |
Johan Euphrosine | 2121c19 | 2015-10-15 14:11:55 -0700 | [diff] [blame] | 23 | SSL_load_error_strings(); |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 24 | Error::AddToPrintf(error, location, "ssl_stream", error_code, "%s: %s", |
| 25 | ERR_lib_error_string(ssl_error_code), |
| 26 | ERR_reason_error_string(ssl_error_code)); |
Johan Euphrosine | 2121c19 | 2015-10-15 14:11:55 -0700 | [diff] [blame] | 27 | } |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 28 | |
| 29 | void RetryAsyncTask(provider::TaskRunner* task_runner, |
| 30 | const tracked_objects::Location& location, |
| 31 | const base::Closure& task) { |
| 32 | task_runner->PostDelayedTask(FROM_HERE, task, |
| 33 | base::TimeDelta::FromMilliseconds(100)); |
| 34 | } |
| 35 | |
Johan Euphrosine | 2121c19 | 2015-10-15 14:11:55 -0700 | [diff] [blame] | 36 | } // namespace |
| 37 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 38 | void SSLStream::SslDeleter::operator()(BIO* bio) const { |
| 39 | BIO_free(bio); |
| 40 | } |
| 41 | |
| 42 | void SSLStream::SslDeleter::operator()(SSL* ssl) const { |
| 43 | SSL_free(ssl); |
| 44 | } |
| 45 | |
| 46 | void SSLStream::SslDeleter::operator()(SSL_CTX* ctx) const { |
| 47 | SSL_CTX_free(ctx); |
| 48 | } |
| 49 | |
| 50 | SSLStream::SSLStream(provider::TaskRunner* task_runner, |
| 51 | std::unique_ptr<BIO, SslDeleter> stream_bio) |
Johan Euphrosine | 2121c19 | 2015-10-15 14:11:55 -0700 | [diff] [blame] | 52 | : task_runner_{task_runner} { |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 53 | ctx_.reset(SSL_CTX_new(TLSv1_2_client_method())); |
| 54 | CHECK(ctx_); |
| 55 | ssl_.reset(SSL_new(ctx_.get())); |
| 56 | |
| 57 | SSL_set_bio(ssl_.get(), stream_bio.get(), stream_bio.get()); |
| 58 | stream_bio.release(); // Owned by ssl now. |
| 59 | SSL_set_connect_state(ssl_.get()); |
Johan Euphrosine | 2121c19 | 2015-10-15 14:11:55 -0700 | [diff] [blame] | 60 | } |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 61 | |
| 62 | SSLStream::~SSLStream() { |
Vitaly Buka | da98728 | 2015-09-23 16:50:40 -0700 | [diff] [blame] | 63 | CancelPendingOperations(); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 64 | } |
| 65 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 66 | void SSLStream::RunTask(const base::Closure& task) { |
Vitaly Buka | 6c5c99d | 2015-09-24 17:00:18 -0700 | [diff] [blame] | 67 | task.Run(); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 68 | } |
| 69 | |
Vitaly Buka | 4ebd329 | 2015-09-23 18:04:17 -0700 | [diff] [blame] | 70 | void SSLStream::Read(void* buffer, |
| 71 | size_t size_to_read, |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 72 | const ReadCallback& callback) { |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 73 | int res = SSL_read(ssl_.get(), buffer, size_to_read); |
| 74 | if (res > 0) { |
| 75 | task_runner_->PostDelayedTask( |
| 76 | FROM_HERE, |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 77 | base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(), |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 78 | base::Bind(callback, res, nullptr)), |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 79 | {}); |
| 80 | return; |
| 81 | } |
| 82 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 83 | int err = SSL_get_error(ssl_.get(), res); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 84 | |
| 85 | if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 86 | return RetryAsyncTask( |
| 87 | task_runner_, FROM_HERE, |
| 88 | base::Bind(&SSLStream::Read, weak_ptr_factory_.GetWeakPtr(), buffer, |
| 89 | size_to_read, callback)); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | ErrorPtr weave_error; |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 93 | AddSslError(&weave_error, FROM_HERE, "read_failed", err); |
| 94 | return task_runner_->PostDelayedTask( |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 95 | FROM_HERE, |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 96 | base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(), |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 97 | base::Bind(callback, 0, base::Passed(&weave_error))), |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 98 | {}); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 99 | } |
| 100 | |
Vitaly Buka | 1fd619a | 2015-09-24 11:46:05 -0700 | [diff] [blame] | 101 | void SSLStream::Write(const void* buffer, |
| 102 | size_t size_to_write, |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 103 | const WriteCallback& callback) { |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 104 | int res = SSL_write(ssl_.get(), buffer, size_to_write); |
| 105 | if (res > 0) { |
| 106 | buffer = static_cast<const char*>(buffer) + res; |
| 107 | size_to_write -= res; |
| 108 | if (size_to_write == 0) { |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 109 | return task_runner_->PostDelayedTask( |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 110 | FROM_HERE, |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 111 | base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(), |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 112 | base::Bind(callback, nullptr)), |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 113 | {}); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 114 | } |
| 115 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 116 | return RetryAsyncTask( |
| 117 | task_runner_, FROM_HERE, |
| 118 | base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(), buffer, |
| 119 | size_to_write, callback)); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 120 | } |
| 121 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 122 | int err = SSL_get_error(ssl_.get(), res); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 123 | |
| 124 | if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 125 | return RetryAsyncTask( |
| 126 | task_runner_, FROM_HERE, |
| 127 | base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(), buffer, |
| 128 | size_to_write, callback)); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | ErrorPtr weave_error; |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 132 | AddSslError(&weave_error, FROM_HERE, "write_failed", err); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 133 | task_runner_->PostDelayedTask( |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 134 | FROM_HERE, base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(), |
| 135 | base::Bind(callback, base::Passed(&weave_error))), |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 136 | {}); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 137 | } |
| 138 | |
Vitaly Buka | da98728 | 2015-09-23 16:50:40 -0700 | [diff] [blame] | 139 | void SSLStream::CancelPendingOperations() { |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 140 | weak_ptr_factory_.InvalidateWeakPtrs(); |
| 141 | } |
| 142 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 143 | void SSLStream::Connect( |
| 144 | provider::TaskRunner* task_runner, |
| 145 | const std::string& host, |
| 146 | uint16_t port, |
| 147 | const provider::Network::OpenSslSocketCallback& callback) { |
| 148 | SSL_library_init(); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 149 | |
| 150 | char end_point[255]; |
| 151 | snprintf(end_point, sizeof(end_point), "%s:%u", host.c_str(), port); |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 152 | |
| 153 | std::unique_ptr<BIO, SslDeleter> stream_bio(BIO_new_connect(end_point)); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 154 | CHECK(stream_bio); |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 155 | BIO_set_nbio(stream_bio.get(), 1); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 156 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 157 | std::unique_ptr<SSLStream> stream{ |
| 158 | new SSLStream{task_runner, std::move(stream_bio)}}; |
| 159 | ConnectBio(std::move(stream), callback); |
| 160 | } |
| 161 | |
| 162 | void SSLStream::ConnectBio( |
| 163 | std::unique_ptr<SSLStream> stream, |
| 164 | const provider::Network::OpenSslSocketCallback& callback) { |
| 165 | BIO* bio = SSL_get_rbio(stream->ssl_.get()); |
| 166 | if (BIO_do_connect(bio) == 1) |
| 167 | return DoHandshake(std::move(stream), callback); |
| 168 | |
| 169 | auto task_runner = stream->task_runner_; |
| 170 | if (BIO_should_retry(bio)) { |
| 171 | return RetryAsyncTask( |
| 172 | task_runner, FROM_HERE, |
| 173 | base::Bind(&SSLStream::ConnectBio, base::Passed(&stream), callback)); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 174 | } |
| 175 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 176 | ErrorPtr error; |
| 177 | AddSslError(&error, FROM_HERE, "connect_failed", ERR_get_error()); |
| 178 | task_runner->PostDelayedTask( |
| 179 | FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {}); |
| 180 | } |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 181 | |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 182 | void SSLStream::DoHandshake( |
| 183 | std::unique_ptr<SSLStream> stream, |
| 184 | const provider::Network::OpenSslSocketCallback& callback) { |
| 185 | int res = SSL_do_handshake(stream->ssl_.get()); |
| 186 | auto task_runner = stream->task_runner_; |
| 187 | if (res == 1) { |
| 188 | return task_runner->PostDelayedTask( |
| 189 | FROM_HERE, base::Bind(callback, base::Passed(&stream), nullptr), {}); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 190 | } |
Vitaly Buka | 10e69bc | 2015-10-16 01:21:07 -0700 | [diff] [blame] | 191 | |
| 192 | res = SSL_get_error(stream->ssl_.get(), res); |
| 193 | |
| 194 | if (res == SSL_ERROR_WANT_READ || res == SSL_ERROR_WANT_WRITE) { |
| 195 | return RetryAsyncTask( |
| 196 | task_runner, FROM_HERE, |
| 197 | base::Bind(&SSLStream::DoHandshake, base::Passed(&stream), callback)); |
| 198 | } |
| 199 | |
| 200 | ErrorPtr error; |
| 201 | AddSslError(&error, FROM_HERE, "handshake_failed", res); |
| 202 | task_runner->PostDelayedTask( |
| 203 | FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {}); |
Vitaly Buka | cd85060 | 2015-09-21 17:23:57 -0700 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | } // namespace examples |
| 207 | } // namespace weave |