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 1195531 : SslReader(int task_id, SslSessionPtr session, ReadHandler read_fn,
39 : Buffer buffer)
40 2391062 : : Task(task_id, session->GetSessionInstance()),
41 1195531 : session_(session), read_fn_(read_fn), buffer_(buffer) {
42 1195531 : }
43 1195506 : virtual bool Run() {
44 1195506 : if (session_->IsEstablished()) {
45 1194700 : session_->ssl_last_read_len_ = BufferSize(buffer_);
46 1194678 : read_fn_(buffer_);
47 1194702 : 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 1194694 : if (session_->IsSslDisabled()) {
54 317769 : session_->AsyncReadStart();
55 876922 : } else if (!session_->IsSslHandShakeInProgress()) {
56 874022 : session_->AsyncReadStart();
57 : }
58 : }
59 : }
60 1195523 : 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 14734 : SslSession::SslSession(SslServer *server, SslSocket *ssl_socket,
71 14734 : bool async_read_ready)
72 : : TcpSession(server, NULL, async_read_ready),
73 14736 : ssl_socket_(ssl_socket),
74 14736 : ssl_handshake_in_progress_(false),
75 14736 : ssl_handshake_success_(false),
76 14736 : ssl_enabled_(true),
77 14736 : ssl_handshake_delayed_(false),
78 14734 : ssl_last_read_len_(0) {
79 :
80 14736 : if (server) {
81 14735 : ssl_enabled_ = server->ssl_enabled_;
82 14735 : ssl_handshake_delayed_ = server->ssl_handshake_delayed_;
83 : }
84 14736 : }
85 :
86 14727 : SslSession::~SslSession() {
87 14727 : }
88 :
89 1195531 : Task* SslSession::CreateReaderTask(mutable_buffer buffer,
90 : size_t bytes_transferred) {
91 1195531 : Buffer rdbuf(buffer_cast<const uint8_t *>(buffer), bytes_transferred);
92 1195531 : SslReader *task = new SslReader(this->reader_task_id(),
93 1195531 : SslSessionPtr(this), bind(&SslSession::OnRead, this, _1), rdbuf);
94 1195531 : return (task);
95 : }
96 :
97 :
98 3286581 : TcpSession::Socket *SslSession::socket() const {
99 3286581 : if (ssl_socket_) {
100 : // return tcp socket
101 3285939 : 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 1207971 : void SslSession::AsyncReadSome() {
109 1207971 : if (established()) {
110 1207827 : 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 159691 : TcpSession::AsyncReadSome();
114 : } else {
115 : // trigger Async Read Handler for immediate read
116 1048136 : TriggerAsyncReadHandler();
117 : }
118 : }
119 1207971 : }
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 1202072 : size_t SslSession::GetReadBufferSize() const {
125 1202072 : 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 1202072 : 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 1202072 : assert(!ssl_handshake_in_progress_);
153 1202072 : if (!IsSslHandShakeSuccessLocked())
154 332968 : return TcpSession::ReadSome(buffer, error);
155 :
156 869104 : return ssl_socket_->read_some(mutable_buffers_1(buffer), *error);
157 : }
158 :
159 1690659 : void SslSession::AsyncWrite(const uint8_t *data, size_t size) {
160 1690659 : if (IsSslHandShakeSuccessLocked()) {
161 825020 : async_write(*ssl_socket_.get(), buffer(data, size),
162 1650040 : bind(&TcpSession::AsyncWriteHandler,
163 1650040 : TcpSessionPtr(this), error, bytes_transferred));
164 : } else {
165 865639 : return (TcpSession::AsyncWrite(data, size));
166 : }
167 : }
168 :
169 2927 : void SslSession::SslHandShakeCallback(SslHandShakeCallbackHandler cb,
170 : SslSessionPtr session,
171 : const error_code &error) {
172 2927 : session->ssl_handshake_in_progress_ = false;
173 2927 : if (!error) {
174 2906 : session->SetSslHandShakeSuccess();
175 : } else {
176 21 : session->SetSslHandShakeFailure();
177 : }
178 :
179 2927 : if (session->socket() != NULL && !(session->IsClosed())) {
180 2927 : cb(session, error);
181 : }
182 2927 : }
183 :
184 2927 : void SslSession::TriggerSslHandShakeInternal(
185 : SslSessionPtr session, SslHandShakeCallbackHandler cb) {
186 2927 : srand(static_cast<unsigned>(time(0)));
187 2927 : error_code ec;
188 2927 : session->ssl_handshake_in_progress_ = true;
189 2927 : if (session->IsServerSession()) {
190 2906 : session->ssl_socket_->async_handshake(stream_base::server,
191 2906 : bind(&SslSession::SslHandShakeCallback, cb, session,
192 : error));
193 : } else {
194 2948 : session->ssl_socket_->async_handshake(stream_base::client,
195 2948 : bind(&SslSession::SslHandShakeCallback, cb, session,
196 : error));
197 : }
198 2927 : }
199 :
200 2926 : void SslSession::TriggerSslHandShake(SslHandShakeCallbackHandler cb) {
201 5853 : server()->event_manager()->io_service()->post(
202 5854 : bind(&TriggerSslHandShakeInternal, SslSessionPtr(this), cb));
203 2927 : }
|