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