2 // ssl/old/detail/openssl_operation.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11 #ifndef BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_OPERATION_HPP
12 #define BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_OPERATION_HPP
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18 #include <boost/asio/detail/config.hpp>
19 #include <boost/function.hpp>
20 #include <boost/bind.hpp>
21 #include <boost/asio/buffer.hpp>
22 #include <boost/asio/detail/assert.hpp>
23 #include <boost/asio/detail/socket_ops.hpp>
24 #include <boost/asio/placeholders.hpp>
25 #include <boost/asio/ssl/detail/openssl_types.hpp>
26 #include <boost/asio/ssl/error.hpp>
27 #include <boost/asio/strand.hpp>
28 #include <boost/system/system_error.hpp>
29 #include <boost/asio/write.hpp>
31 #include <boost/asio/detail/push_options.hpp>
39 typedef boost::function<int (::SSL*)> ssl_primitive_func;
40 typedef boost::function<void (const boost::system::error_code&, int)>
43 // Network send_/recv buffer implementation
48 static const int NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare
50 unsigned char buf_[NET_BUF_SIZE];
51 unsigned char* data_start_;
52 unsigned char* data_end_;
57 data_start_ = data_end_ = buf_;
59 unsigned char* get_unused_start() { return data_end_; }
60 unsigned char* get_data_start() { return data_start_; }
61 size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); }
62 size_t get_data_len() { return (data_end_ - data_start_); }
63 void data_added(size_t count)
66 data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)?
67 (buf_ + NET_BUF_SIZE):
70 void data_removed(size_t count)
73 if (data_start_ >= data_end_) reset();
75 void reset() { data_start_ = buf_; data_end_ = buf_; }
76 bool has_data() { return (data_start_ < data_end_); }
77 }; // class net_buffer
83 template <typename Stream>
84 class openssl_operation
88 // Constructor for asynchronous operations
89 openssl_operation(ssl_primitive_func primitive,
94 user_handler_func handler,
95 boost::asio::io_service::strand& strand
97 : primitive_(primitive)
98 , user_handler_(handler)
100 , recv_buf_(recv_buf)
105 write_ = boost::bind(
106 &openssl_operation::do_async_write,
107 this, boost::arg<1>(), boost::arg<2>()
110 &openssl_operation::do_async_read,
113 handler_= boost::bind(
114 &openssl_operation::async_user_handler,
115 this, boost::arg<1>(), boost::arg<2>()
119 // Constructor for synchronous operations
120 openssl_operation(ssl_primitive_func primitive,
122 net_buffer& recv_buf,
125 : primitive_(primitive)
127 , recv_buf_(recv_buf)
132 write_ = boost::bind(
133 &openssl_operation::do_sync_write,
134 this, boost::arg<1>(), boost::arg<2>()
137 &openssl_operation::do_sync_read,
140 handler_ = boost::bind(
141 &openssl_operation::sync_user_handler,
142 this, boost::arg<1>(), boost::arg<2>()
147 // In case of asynchronous it returns 0, in sync mode returns success code
148 // or throws an error...
151 int rc = primitive_( session_ );
153 bool is_operation_done = (rc > 0);
154 // For connect/accept/shutdown, the operation
155 // is done, when return code is 1
156 // for write, it is done, when is retcode > 0
157 // for read, it is done when retcode > 0
159 int error_code = !is_operation_done ?
160 ::SSL_get_error( session_, rc ) :
162 int sys_error_code = ERR_get_error();
164 if (error_code == SSL_ERROR_SSL)
165 return handler_(boost::system::error_code(
166 sys_error_code, boost::asio::error::get_ssl_category()), rc);
168 bool is_read_needed = (error_code == SSL_ERROR_WANT_READ);
169 bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE ||
170 ::BIO_ctrl_pending( ssl_bio_ ));
171 bool is_shut_down_received =
172 ((::SSL_get_shutdown( session_ ) & SSL_RECEIVED_SHUTDOWN) ==
173 SSL_RECEIVED_SHUTDOWN);
174 bool is_shut_down_sent =
175 ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) ==
178 if (is_shut_down_sent && is_shut_down_received
179 && is_operation_done && !is_write_needed)
180 // SSL connection is shut down cleanly
181 return handler_(boost::system::error_code(), 1);
183 if (is_shut_down_received && !is_operation_done)
184 // Shutdown has been requested, while we were reading or writing...
185 // abort our action...
186 return handler_(boost::asio::error::shut_down, 0);
188 if (!is_operation_done && !is_read_needed && !is_write_needed
189 && !is_shut_down_sent)
191 // The operation has failed... It is not completed and does
192 // not want network communication nor does want to send shutdown out...
193 if (error_code == SSL_ERROR_SYSCALL)
195 return handler_(boost::system::error_code(
196 sys_error_code, boost::asio::error::system_category), rc);
200 return handler_(boost::system::error_code(
201 sys_error_code, boost::asio::error::get_ssl_category()), rc);
205 if (!is_operation_done && !is_write_needed)
207 // We may have left over data that we can pass to SSL immediately
208 if (recv_buf_.get_data_len() > 0)
210 // Pass the buffered data to SSL
211 int written = ::BIO_write
214 recv_buf_.get_data_start(),
215 recv_buf_.get_data_len()
220 recv_buf_.data_removed(written);
222 else if (written < 0)
224 if (!BIO_should_retry(ssl_bio_))
226 // Some serios error with BIO....
227 return handler_(boost::asio::error::no_recovery, 0);
233 else if (is_read_needed || (is_shut_down_sent && !is_shut_down_received))
239 // Continue with operation, flush any SSL data out to network...
240 return write_(is_operation_done, rc);
243 // Private implementation
245 typedef boost::function<int (const boost::system::error_code&, int)>
247 typedef boost::function<int (bool, int)> write_func;
248 typedef boost::function<int ()> read_func;
250 ssl_primitive_func primitive_;
251 user_handler_func user_handler_;
252 boost::asio::io_service::strand* strand_;
255 int_handler_func handler_;
257 net_buffer send_buf_; // buffers for network IO
259 // The recv buffer is owned by the stream, not the operation, since there can
260 // be left over bytes after passing the data up to the application, and these
261 // bytes need to be kept around for the next read operation issued by the
263 net_buffer& recv_buf_;
270 int sync_user_handler(const boost::system::error_code& error, int rc)
275 throw boost::system::system_error(error);
278 int async_user_handler(boost::system::error_code error, int rc)
283 error = boost::asio::error::no_recovery;
287 user_handler_(error, rc);
291 // Writes bytes asynchronously from SSL to NET
292 int do_async_write(bool is_operation_done, int rc)
294 int len = ::BIO_ctrl_pending( ssl_bio_ );
297 // There is something to write into net, do it...
298 len = (int)send_buf_.get_unused_len() > len?
300 send_buf_.get_unused_len();
304 // In case our send buffer is full, we have just to wait until
305 // previous send to complete...
309 // Read outgoing data from bio
310 len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len);
314 unsigned char *data_start = send_buf_.get_unused_start();
315 send_buf_.data_added(len);
317 BOOST_ASIO_ASSERT(strand_);
318 boost::asio::async_write
321 boost::asio::buffer(data_start, len),
326 &openssl_operation::async_write_handler,
330 boost::asio::placeholders::error,
331 boost::asio::placeholders::bytes_transferred
338 else if (!BIO_should_retry(ssl_bio_))
340 // Seems like fatal error
341 // reading from SSL BIO has failed...
342 handler_(boost::asio::error::no_recovery, 0);
347 if (is_operation_done)
349 // Finish the operation, with success
350 handler_(boost::system::error_code(), rc);
354 // OPeration is not done and writing to net has been made...
355 // start operation again
361 void async_write_handler(bool is_operation_done, int rc,
362 const boost::system::error_code& error, size_t bytes_sent)
366 // Remove data from send buffer
367 send_buf_.data_removed(bytes_sent);
369 if (is_operation_done)
370 handler_(boost::system::error_code(), rc);
372 // Since the operation was not completed, try it again...
382 BOOST_ASIO_ASSERT(strand_);
383 socket_.async_read_some
385 boost::asio::buffer(recv_buf_.get_unused_start(),
386 recv_buf_.get_unused_len()),
391 &openssl_operation::async_read_handler,
393 boost::asio::placeholders::error,
394 boost::asio::placeholders::bytes_transferred
401 void async_read_handler(const boost::system::error_code& error,
406 recv_buf_.data_added(bytes_recvd);
408 // Pass the received data to SSL
409 int written = ::BIO_write
412 recv_buf_.get_data_start(),
413 recv_buf_.get_data_len()
418 recv_buf_.data_removed(written);
420 else if (written < 0)
422 if (!BIO_should_retry(ssl_bio_))
424 // Some serios error with BIO....
425 handler_(boost::asio::error::no_recovery, 0);
430 // and try the SSL primitive again
435 // Error in network level...
436 // SSL can't continue either...
441 // Syncronous functions...
442 int do_sync_write(bool is_operation_done, int rc)
444 int len = ::BIO_ctrl_pending( ssl_bio_ );
447 // There is something to write into net, do it...
448 len = (int)send_buf_.get_unused_len() > len?
450 send_buf_.get_unused_len();
452 // Read outgoing data from bio
453 len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len);
457 size_t sent_len = boost::asio::write(
459 boost::asio::buffer(send_buf_.get_unused_start(), len)
462 send_buf_.data_added(len);
463 send_buf_.data_removed(sent_len);
465 else if (!BIO_should_retry(ssl_bio_))
467 // Seems like fatal error
468 // reading from SSL BIO has failed...
469 throw boost::system::system_error(boost::asio::error::no_recovery);
473 if (is_operation_done)
474 // Finish the operation, with success
477 // Operation is not finished, start again.
483 size_t len = socket_.read_some
485 boost::asio::buffer(recv_buf_.get_unused_start(),
486 recv_buf_.get_unused_len())
490 recv_buf_.data_added(len);
492 // Pass the received data to SSL
493 int written = ::BIO_write
496 recv_buf_.get_data_start(),
497 recv_buf_.get_data_len()
502 recv_buf_.data_removed(written);
504 else if (written < 0)
506 if (!BIO_should_retry(ssl_bio_))
508 // Some serios error with BIO....
509 throw boost::system::system_error(boost::asio::error::no_recovery);
513 // Try the operation again
516 }; // class openssl_operation
518 } // namespace detail
524 #include <boost/asio/detail/pop_options.hpp>
526 #endif // BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_OPERATION_HPP