OpenSDN source code
ssl_session.cc
Go to the documentation of this file.
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  SslReader(int task_id, SslSessionPtr session, ReadHandler read_fn,
39  Buffer buffer)
40  : Task(task_id, session->GetSessionInstance()),
41  session_(session), read_fn_(read_fn), buffer_(buffer) {
42  }
43  virtual bool Run() {
44  if (session_->IsEstablished()) {
45  session_->ssl_last_read_len_ = BufferSize(buffer_);
46  read_fn_(buffer_);
47  if (session_->IsReaderDeferred()) {
48  // Update socket read block count.
49  session_->stats_.read_block_start_time = UTCTimestampUsec();
50  session_->stats_.read_blocked++;
51  session_->server()->stats_.read_blocked++;
52  } else {
53  if (session_->IsSslDisabled()) {
54  session_->AsyncReadStart();
55  } else if (!session_->IsSslHandShakeInProgress()) {
56  session_->AsyncReadStart();
57  }
58  }
59  }
60  return true;
61  }
62  string Description() const { return "SslSession::SslReader"; }
63 
64 private:
68 };
69 
71  bool async_read_ready)
72  : TcpSession(server, NULL, async_read_ready),
73  ssl_socket_(ssl_socket),
74  ssl_handshake_in_progress_(false),
75  ssl_handshake_success_(false),
76  ssl_enabled_(true),
77  ssl_handshake_delayed_(false),
78  ssl_last_read_len_(0) {
79 
80  if (server) {
81  ssl_enabled_ = server->ssl_enabled_;
82  ssl_handshake_delayed_ = server->ssl_handshake_delayed_;
83  }
84 }
85 
87 }
88 
89 Task* SslSession::CreateReaderTask(mutable_buffer buffer,
90  size_t bytes_transferred) {
91  Buffer rdbuf(buffer_cast<const uint8_t *>(buffer), bytes_transferred);
92  SslReader *task = new SslReader(this->reader_task_id(),
93  SslSessionPtr(this), bind(&SslSession::OnRead, this, _1), rdbuf);
94  return (task);
95 }
96 
97 
99  if (ssl_socket_) {
100  // return tcp socket
101  return &ssl_socket_->next_layer();
102  }
103  return NULL;
104 }
105 
106 // Register for data read notification from the tcp socket or from the ssl
107 // socket, as appropriate.
109  if (established()) {
110  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
114  } else {
115  // trigger Async Read Handler for immediate read
117  }
118  }
119 }
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.
125  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 bool SslSession::IsSocketErrorHard(const error_code &ec) {
134 
135  bool error;
136  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  return error;
148 }
149 
150 size_t SslSession::ReadSome(mutable_buffer buffer, error_code *error) {
151  // Read data from the tcp socket or from the ssl socket, as appropriate.
154  return TcpSession::ReadSome(buffer, error);
155 
156  return ssl_socket_->read_some(mutable_buffers_1(buffer), *error);
157 }
158 
159 void SslSession::AsyncWrite(const uint8_t *data, size_t size) {
161  async_write(*ssl_socket_.get(), buffer(data, size),
163  TcpSessionPtr(this), error, bytes_transferred));
164  } else {
165  return (TcpSession::AsyncWrite(data, size));
166  }
167 }
168 
170  SslSessionPtr session,
171  const error_code &error) {
172  session->ssl_handshake_in_progress_ = false;
173  if (!error) {
174  session->SetSslHandShakeSuccess();
175  } else {
176  session->SetSslHandShakeFailure();
177  }
178 
179  if (session->socket() != NULL && !(session->IsClosed())) {
180  cb(session, error);
181  }
182 }
183 
186  srand(static_cast<unsigned>(time(0)));
187  error_code ec;
188  session->ssl_handshake_in_progress_ = true;
189  if (session->IsServerSession()) {
190  session->ssl_socket_->async_handshake(stream_base::server,
191  bind(&SslSession::SslHandShakeCallback, cb, session,
192  error));
193  } else {
194  session->ssl_socket_->async_handshake(stream_base::client,
195  bind(&SslSession::SslHandShakeCallback, cb, session,
196  error));
197  }
198 }
199 
201  server()->event_manager()->io_service()->post(
202  bind(&TriggerSslHandShakeInternal, SslSessionPtr(this), cb));
203 }
boost::asio::io_context * io_service()
Definition: event_manager.h:42
SslSessionPtr session_
Definition: ssl_session.cc:65
SslReader(int task_id, SslSessionPtr session, ReadHandler read_fn, Buffer buffer)
Definition: ssl_session.cc:38
virtual bool Run()
Code to execute in a task. Returns true if task is completed. Return false to reschedule the task.
Definition: ssl_session.cc:43
ReadHandler read_fn_
Definition: ssl_session.cc:66
string Description() const
Gives a description of the task.
Definition: ssl_session.cc:62
function< void(Buffer)> ReadHandler
Definition: ssl_session.cc:36
static bool IsSocketErrorHard(const boost::system::error_code &ec)
Definition: ssl_session.cc:133
size_t ReadSome(boost::asio::mutable_buffer buffer, boost::system::error_code *error)
Definition: ssl_session.cc:150
virtual Socket * socket() const
Definition: ssl_session.cc:98
boost::asio::ssl::stream< boost::asio::ip::tcp::socket > SslSocket
Definition: ssl_session.h:20
virtual void AsyncReadSome()
Definition: ssl_session.cc:108
void AsyncWrite(const uint8_t *data, std::size_t size)
Definition: ssl_session.cc:159
virtual ~SslSession()
Definition: ssl_session.cc:86
SslSession(SslServer *server, SslSocket *socket, bool async_read_ready=true)
Definition: ssl_session.cc:70
bool ssl_enabled_
Definition: ssl_session.h:102
static void SslHandShakeCallback(SslHandShakeCallbackHandler cb, SslSessionPtr, const boost::system::error_code &error)
Definition: ssl_session.cc:169
size_t ssl_last_read_len_
Definition: ssl_session.h:106
virtual Task * CreateReaderTask(boost::asio::mutable_buffer, size_t)
Definition: ssl_session.cc:89
boost::scoped_ptr< SslSocket > ssl_socket_
Definition: ssl_session.h:94
bool ssl_handshake_delayed_
Definition: ssl_session.h:103
virtual size_t GetReadBufferSize() const
Definition: ssl_session.cc:124
void TriggerSslHandShake(SslHandShakeCallbackHandler)
Definition: ssl_session.cc:200
bool IsSslHandShakeSuccessLocked()
Definition: ssl_session.h:45
static void TriggerSslHandShakeInternal(SslSessionPtr ptr, SslHandShakeCallbackHandler cb)
Definition: ssl_session.cc:184
bool ssl_handshake_in_progress_
Definition: ssl_session.h:97
Task is a class to describe a computational task within OpenSDN control plane applications....
Definition: task.h:79
EventManager * event_manager()
Definition: tcp_server.h:76
virtual int reader_task_id() const
Definition: tcp_session.h:204
TcpServer * server()
Definition: tcp_session.h:84
virtual void AsyncWrite(const uint8_t *data, std::size_t size)
Definition: tcp_session.cc:200
static bool IsSocketErrorHard(const boost::system::error_code &ec)
Definition: tcp_session.cc:743
void TriggerAsyncReadHandler()
Definition: tcp_session.cc:347
bool established() const
Definition: tcp_session.h:208
virtual void AsyncReadSome()
Definition: tcp_session.cc:193
virtual void OnRead(Buffer buffer)=0
static void AsyncWriteHandler(TcpSessionPtr session, const boost::system::error_code &error, std::size_t bytes_transferred)
Definition: tcp_session.cc:379
boost::asio::const_buffer Buffer
Definition: tcp_session.h:60
boost::intrusive_ptr< TcpSession > TcpSessionPtr
Definition: tcp_session.h:180
static const int kDefaultBufferSize
Definition: tcp_session.h:40
boost::asio::ip::tcp::socket Socket
Definition: tcp_session.h:56
virtual size_t ReadSome(boost::asio::mutable_buffer buffer, boost::system::error_code *error)
Definition: tcp_session.cc:469
boost::intrusive_ptr< SslSession > SslSessionPtr
Definition: ssl_session.h:13
boost::function< void(SslSessionPtr, const boost::system::error_code &error)> SslHandShakeCallbackHandler
Definition: ssl_session.h:16
struct task_ task
static uint64_t UTCTimestampUsec()
Definition: time_util.h:13