Line data Source code
1 : /*
2 : * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include "io/ssl_session.h"
7 :
8 : #include <string>
9 :
10 : #include <boost/asio.hpp>
11 : #include <boost/bind/bind.hpp>
12 :
13 : #include "io/event_manager.h"
14 : #include "io/io_log.h"
15 : #include "io/io_utils.h"
16 :
17 : using boost::asio::async_write;
18 : using boost::asio::buffer;
19 : using boost::asio::buffer_cast;
20 : using boost::asio::mutable_buffer;
21 : using boost::asio::mutable_buffers_1;
22 : using boost::asio::null_buffers;
23 : using boost::asio::placeholders::error;
24 : using boost::asio::placeholders::bytes_transferred;
25 : using boost::asio::ssl::stream_base;
26 : using boost::bind;
27 : using boost::function;
28 : using boost::system::error_code;
29 : using std::size_t;
30 : using std::srand;
31 : using std::string;
32 : using std::time;
33 : using namespace boost::placeholders;
34 :
35 : class SslSession::SslReader : public Task {
36 : public:
37 : typedef function<void(Buffer)> ReadHandler;
38 :
39 1174975 : SslReader(int task_id, SslSessionPtr session, ReadHandler read_fn,
40 : Buffer buffer)
41 2349950 : : Task(task_id, session->GetSessionInstance()),
42 1174975 : session_(session), read_fn_(read_fn), buffer_(buffer) {
43 1174975 : }
44 1174952 : virtual bool Run() {
45 1174952 : if (session_->IsEstablished()) {
46 1174235 : session_->ssl_last_read_len_ = BufferSize(buffer_);
47 1174221 : read_fn_(buffer_);
48 1174237 : if (session_->IsReaderDeferred()) {
49 : // Update socket read block count.
50 0 : session_->stats_.read_block_start_time = UTCTimestampUsec();
51 0 : session_->stats_.read_blocked++;
52 0 : session_->server()->stats_.read_blocked++;
53 : } else {
54 1174230 : if (session_->IsSslDisabled()) {
55 315387 : session_->AsyncReadStart();
56 858840 : } else if (!session_->IsSslHandShakeInProgress()) {
57 855934 : session_->AsyncReadStart();
58 : }
59 : }
60 : }
61 1174974 : return true;
62 : }
63 0 : string Description() const { return "SslSession::SslReader"; }
64 :
65 : private:
66 : SslSessionPtr session_;
67 : ReadHandler read_fn_;
68 : Buffer buffer_;
69 : };
70 :
71 14475 : SslSession::SslSession(SslServer *server, SslSocket *ssl_socket,
72 14475 : bool async_read_ready)
73 : : TcpSession(server, NULL, async_read_ready),
74 14475 : ssl_socket_(ssl_socket),
75 14475 : ssl_handshake_in_progress_(false),
76 14475 : ssl_handshake_success_(false),
77 14475 : ssl_enabled_(true),
78 14475 : ssl_handshake_delayed_(false),
79 14475 : ssl_last_read_len_(0) {
80 :
81 14475 : if (server) {
82 14474 : ssl_enabled_ = server->ssl_enabled_;
83 14474 : ssl_handshake_delayed_ = server->ssl_handshake_delayed_;
84 : }
85 14475 : }
86 :
87 14470 : SslSession::~SslSession() {
88 14470 : }
89 :
90 1174975 : Task* SslSession::CreateReaderTask(mutable_buffer buffer,
91 : size_t bytes_transferred) {
92 1174975 : Buffer rdbuf(buffer_cast<const uint8_t *>(buffer), bytes_transferred);
93 1174975 : SslReader *task = new SslReader(this->reader_task_id(),
94 1174975 : SslSessionPtr(this), bind(&SslSession::OnRead, this, _1), rdbuf);
95 1174975 : return (task);
96 : }
97 :
98 :
99 3235880 : TcpSession::Socket *SslSession::socket() const {
100 3235880 : if (ssl_socket_) {
101 : // return tcp socket
102 3235808 : return &ssl_socket_->next_layer();
103 : }
104 0 : return NULL;
105 : }
106 :
107 : // Register for data read notification from the tcp socket or from the ssl
108 : // socket, as appropriate.
109 1187468 : void SslSession::AsyncReadSome() {
110 1187468 : if (established()) {
111 1187250 : if (ssl_last_read_len_ == 0) {
112 : // we have drained the read buffer of the socket
113 : // register for a read notification from the tcp socket
114 157374 : TcpSession::AsyncReadSome();
115 : } else {
116 : // trigger Async Read Handler for immediate read
117 1029876 : TriggerAsyncReadHandler();
118 : }
119 : }
120 1187468 : }
121 :
122 : // Tests with large data have consistently shown 16K as the maximum read data
123 : // size even though when a lot more data was available in the underlying socket.
124 : // Also, there is no available() api for the ssl socket.
125 1181523 : size_t SslSession::GetReadBufferSize() const {
126 1181523 : return kDefaultBufferSize;
127 : }
128 :
129 : //
130 : // Check if a socker error is hard and fatal. Only then should we close the
131 : // socket. Soft errors like EINTR and EAGAIN should be ignored or properly
132 : // handled with retries
133 : //
134 0 : bool SslSession::IsSocketErrorHard(const error_code &ec) {
135 :
136 : bool error;
137 0 : error = TcpSession::IsSocketErrorHard(ec);
138 : #if defined(SSL_R_SHORT_READ) // openssl 1.0
139 : if (ec.value() == ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)) {
140 : error = false;
141 : }
142 : #elif defined(BOOST_ASIO_SSL_ERROR_STREAM_TRUNCATED) // new openssl and boost
143 : if (ec.value() == boost::asio::ssl::error::stream_truncated) {
144 : error = false;
145 : }
146 : #endif
147 :
148 0 : return error;
149 : }
150 :
151 1181523 : size_t SslSession::ReadSome(mutable_buffer buffer, error_code *error) {
152 : // Read data from the tcp socket or from the ssl socket, as appropriate.
153 1181523 : assert(!ssl_handshake_in_progress_);
154 1181523 : if (!IsSslHandShakeSuccessLocked())
155 330305 : return TcpSession::ReadSome(buffer, error);
156 :
157 851218 : return ssl_socket_->read_some(mutable_buffers_1(buffer), *error);
158 : }
159 :
160 1660378 : void SslSession::AsyncWrite(const uint8_t *data, size_t size) {
161 1660378 : if (IsSslHandShakeSuccessLocked()) {
162 807337 : async_write(*ssl_socket_.get(), buffer(data, size),
163 1614674 : bind(&TcpSession::AsyncWriteHandler,
164 1614674 : TcpSessionPtr(this), error, bytes_transferred));
165 : } else {
166 853041 : return (TcpSession::AsyncWrite(data, size));
167 : }
168 : }
169 :
170 2931 : void SslSession::SslHandShakeCallback(SslHandShakeCallbackHandler cb,
171 : SslSessionPtr session,
172 : const error_code &error) {
173 2931 : session->ssl_handshake_in_progress_ = false;
174 2931 : if (!error) {
175 2910 : session->SetSslHandShakeSuccess();
176 : } else {
177 21 : session->SetSslHandShakeFailure();
178 : }
179 :
180 2931 : if (session->socket() != NULL && !(session->IsClosed())) {
181 2931 : cb(session, error);
182 : }
183 2931 : }
184 :
185 2931 : void SslSession::TriggerSslHandShakeInternal(
186 : SslSessionPtr session, SslHandShakeCallbackHandler cb) {
187 2931 : srand(static_cast<unsigned>(time(0)));
188 2931 : error_code ec;
189 2931 : session->ssl_handshake_in_progress_ = true;
190 2931 : if (session->IsServerSession()) {
191 2910 : session->ssl_socket_->async_handshake(stream_base::server,
192 2910 : bind(&SslSession::SslHandShakeCallback, cb, session,
193 : error));
194 : } else {
195 2952 : session->ssl_socket_->async_handshake(stream_base::client,
196 2952 : bind(&SslSession::SslHandShakeCallback, cb, session,
197 : error));
198 : }
199 2931 : }
200 :
201 2931 : void SslSession::TriggerSslHandShake(SslHandShakeCallbackHandler cb) {
202 5862 : server()->event_manager()->io_service()->post(
203 5862 : bind(&TriggerSslHandShakeInternal, SslSessionPtr(this), cb));
204 2931 : }
|