openssl_operation.hpp
Go to the documentation of this file.
00001 //
00002 // openssl_operation.hpp
00003 // ~~~~~~~~~~~~~~~~~~~~~
00004 //
00005 // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
00006 //
00007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
00008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
00009 //
00010 
00011 #ifndef ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP
00012 #define ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP
00013 
00014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
00015 # pragma once
00016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
00017 
00018 #include "asio/detail/push_options.hpp"
00019 
00020 #include "asio/detail/push_options.hpp"
00021 #include <boost/function.hpp>
00022 #include <boost/assert.hpp>
00023 #include <boost/bind.hpp>
00024 #include "asio/detail/pop_options.hpp"
00025 
00026 #include "asio/buffer.hpp"
00027 #include "asio/placeholders.hpp"
00028 #include "asio/write.hpp"
00029 #include "asio/detail/socket_ops.hpp"
00030 #include "asio/ssl/detail/openssl_types.hpp"
00031 
00032 namespace asio {
00033 namespace ssl {
00034 namespace detail {
00035 
00036 typedef boost::function<int (::SSL*)> ssl_primitive_func; 
00037 typedef boost::function<void (const asio::error_code&, int)>
00038   user_handler_func;
00039 
00040 // Network send_/recv buffer implementation
00041 //
00042 //
00043 class net_buffer
00044 {
00045   static const int  NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare
00046 
00047   unsigned char buf_[NET_BUF_SIZE];
00048   unsigned char* data_start_;
00049   unsigned char* data_end_;
00050 
00051 public:
00052   net_buffer()
00053   {
00054     data_start_ = data_end_ = buf_;
00055   }
00056   unsigned char* get_unused_start() { return data_end_; }
00057   unsigned char* get_data_start() { return data_start_; }
00058   size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); }    
00059   size_t get_data_len() { return (data_end_ - data_start_); }    
00060   void data_added(size_t count)
00061   { 
00062     data_end_ += count; 
00063     data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)? 
00064       (buf_ + NET_BUF_SIZE):
00065       data_end_; 
00066   }
00067   void data_removed(size_t count) 
00068   { 
00069     data_start_ += count; 
00070     if (data_start_ >= data_end_) reset(); 
00071   }
00072   void reset() { data_start_ = buf_; data_end_ = buf_; }               
00073   bool has_data() { return (data_start_ < data_end_); }
00074 }; // class net_buffer
00075 
00076 //
00077 // Operation class
00078 //
00079 //
00080 template <typename Stream>
00081 class openssl_operation
00082 {
00083 public:
00084 
00085   // Constructor for asynchronous operations
00086   openssl_operation(ssl_primitive_func primitive,
00087                     Stream& socket,
00088                     net_buffer& recv_buf,
00089                     SSL* session,
00090                     BIO* ssl_bio,
00091                     user_handler_func  handler,
00092                     asio::io_service::strand& strand
00093                     )
00094     : primitive_(primitive)
00095     , user_handler_(handler)
00096     , strand_(&strand)
00097     , recv_buf_(recv_buf)
00098     , socket_(socket)
00099     , ssl_bio_(ssl_bio)
00100     , session_(session)
00101   {
00102     write_ = boost::bind(
00103       &openssl_operation::do_async_write, 
00104       this, boost::arg<1>(), boost::arg<2>()
00105     );
00106     read_ = boost::bind(
00107       &openssl_operation::do_async_read, 
00108       this
00109     );
00110     handler_= boost::bind(
00111       &openssl_operation::async_user_handler, 
00112       this, boost::arg<1>(), boost::arg<2>()
00113     );
00114   }
00115 
00116   // Constructor for synchronous operations
00117   openssl_operation(ssl_primitive_func primitive,
00118                     Stream& socket,
00119                     net_buffer& recv_buf,
00120                     SSL* session,
00121                     BIO* ssl_bio)
00122     : primitive_(primitive)
00123     , strand_(0)
00124     , recv_buf_(recv_buf)
00125     , socket_(socket)
00126     , ssl_bio_(ssl_bio)
00127     , session_(session)
00128   {      
00129     write_ = boost::bind(
00130       &openssl_operation::do_sync_write, 
00131       this, boost::arg<1>(), boost::arg<2>()
00132     );
00133     read_ = boost::bind(
00134       &openssl_operation::do_sync_read, 
00135       this
00136     );
00137     handler_ = boost::bind(
00138       &openssl_operation::sync_user_handler, 
00139       this, boost::arg<1>(), boost::arg<2>()
00140       );
00141   }
00142 
00143   // Start operation
00144   // In case of asynchronous it returns 0, in sync mode returns success code
00145   // or throws an error...
00146   int start()
00147   {
00148     int rc = primitive_( session_ );
00149 
00150     bool is_operation_done = (rc > 0);  
00151                 // For connect/accept/shutdown, the operation
00152                 // is done, when return code is 1
00153                 // for write, it is done, when is retcode > 0
00154                 // for read, is is done when retcode > 0
00155 
00156     int error_code =  !is_operation_done ?
00157           ::SSL_get_error( session_, rc ) :
00158           0;        
00159     int sys_error_code = ERR_get_error();
00160 
00161     bool is_read_needed = (error_code == SSL_ERROR_WANT_READ);
00162     bool is_write_needed = (error_code == SSL_ERROR_WANT_WRITE ||
00163                               ::BIO_ctrl_pending( ssl_bio_ ));
00164     bool is_shut_down_received = 
00165       ((::SSL_get_shutdown( session_ ) & SSL_RECEIVED_SHUTDOWN) == 
00166           SSL_RECEIVED_SHUTDOWN);
00167     bool is_shut_down_sent = 
00168       ((::SSL_get_shutdown( session_ ) & SSL_SENT_SHUTDOWN) ==
00169             SSL_SENT_SHUTDOWN);
00170 
00171     if (is_shut_down_sent && is_shut_down_received && is_operation_done && !is_write_needed)
00172       // SSL connection is shut down cleanly
00173       return handler_(asio::error_code(), 1);
00174 
00175     if (is_shut_down_received && !is_operation_done)
00176       // Shutdown has been requested, while we were reading or writing...
00177       // abort our action...
00178       return handler_(asio::error::shut_down, 0);
00179 
00180     if (!is_operation_done && !is_read_needed && !is_write_needed 
00181       && !is_shut_down_sent)
00182     {
00183       // The operation has failed... It is not completed and does 
00184       // not want network communication nor does want to send shutdown out...
00185       if (error_code == SSL_ERROR_SYSCALL)
00186       {
00187         return handler_(asio::error_code(
00188               sys_error_code, asio::error::system_category), rc); 
00189       }
00190       else
00191       {
00192         return handler_(asio::error_code(
00193               error_code, asio::error::get_ssl_category()), rc); 
00194       }
00195     }
00196 
00197     if (!is_operation_done && !is_write_needed)
00198     {
00199       // We may have left over data that we can pass to SSL immediately
00200       if (recv_buf_.get_data_len() > 0)
00201       {
00202         // Pass the buffered data to SSL
00203         int written = ::BIO_write
00204         ( 
00205           ssl_bio_, 
00206           recv_buf_.get_data_start(), 
00207           recv_buf_.get_data_len() 
00208         );
00209 
00210         if (written > 0)
00211         {
00212           recv_buf_.data_removed(written);
00213         }
00214         else if (written < 0)
00215         {
00216           if (!BIO_should_retry(ssl_bio_))
00217           {
00218             // Some serios error with BIO....
00219             return handler_(asio::error::no_recovery, 0);
00220           }
00221         }
00222 
00223         return start();
00224       }
00225       else if (is_read_needed || (is_shut_down_sent && !is_shut_down_received))
00226       {
00227         return read_();
00228       }
00229     }
00230 
00231     // Continue with operation, flush any SSL data out to network...
00232     return write_(is_operation_done, rc); 
00233   }
00234 
00235 // Private implementation
00236 private:
00237   typedef boost::function<int (const asio::error_code&, int)>
00238     int_handler_func;
00239   typedef boost::function<int (bool, int)> write_func;
00240   typedef boost::function<int ()> read_func;
00241 
00242   ssl_primitive_func  primitive_;
00243   user_handler_func  user_handler_;
00244   asio::io_service::strand* strand_;
00245   write_func  write_;
00246   read_func  read_;
00247   int_handler_func handler_;
00248     
00249   net_buffer send_buf_; // buffers for network IO
00250 
00251   // The recv buffer is owned by the stream, not the operation, since there can
00252   // be left over bytes after passing the data up to the application, and these
00253   // bytes need to be kept around for the next read operation issued by the
00254   // application.
00255   net_buffer& recv_buf_;
00256 
00257   Stream& socket_;
00258   BIO*    ssl_bio_;
00259   SSL*    session_;
00260 
00261   //
00262   int sync_user_handler(const asio::error_code& error, int rc)
00263   {
00264     if (!error)
00265       return rc;
00266 
00267     throw asio::system_error(error);
00268   }
00269     
00270   int async_user_handler(asio::error_code error, int rc)
00271   {
00272     if (rc < 0)
00273     {
00274       if (!error)
00275         error = asio::error::no_recovery;
00276       rc = 0;
00277     }
00278 
00279     user_handler_(error, rc);
00280     return 0;
00281   }
00282 
00283   // Writes bytes asynchronously from SSL to NET
00284   int  do_async_write(bool is_operation_done, int rc) 
00285   {
00286     int len = ::BIO_ctrl_pending( ssl_bio_ );
00287     if ( len )
00288     { 
00289       // There is something to write into net, do it...
00290       len = (int)send_buf_.get_unused_len() > len? 
00291         len: 
00292         send_buf_.get_unused_len();
00293         
00294       if (len == 0)
00295       {
00296         // In case our send buffer is full, we have just to wait until 
00297         // previous send to complete...
00298         return 0;
00299       }
00300 
00301       // Read outgoing data from bio
00302       len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); 
00303          
00304       if (len > 0)
00305       {
00306         unsigned char *data_start = send_buf_.get_unused_start();
00307         send_buf_.data_added(len);
00308  
00309         BOOST_ASSERT(strand_); 
00310         asio::async_write
00311         ( 
00312           socket_, 
00313           asio::buffer(data_start, len),
00314           strand_->wrap
00315           (
00316             boost::bind
00317             (
00318               &openssl_operation::async_write_handler, 
00319               this, 
00320               is_operation_done,
00321               rc, 
00322               asio::placeholders::error, 
00323               asio::placeholders::bytes_transferred
00324             )
00325           )
00326         );
00327                   
00328         return 0;
00329       }
00330       else if (!BIO_should_retry(ssl_bio_))
00331       {
00332         // Seems like fatal error
00333         // reading from SSL BIO has failed...
00334         handler_(asio::error::no_recovery, 0);
00335         return 0;
00336       }
00337     }
00338     
00339     if (is_operation_done)
00340     {
00341       // Finish the operation, with success
00342       handler_(asio::error_code(), rc);
00343       return 0;
00344     }
00345     
00346     // OPeration is not done and writing to net has been made...
00347     // start operation again
00348     start();
00349           
00350     return 0;
00351   }
00352 
00353   void async_write_handler(bool is_operation_done, int rc, 
00354     const asio::error_code& error, size_t bytes_sent)
00355   {
00356     if (!error)
00357     {
00358       // Remove data from send buffer
00359       send_buf_.data_removed(bytes_sent);
00360 
00361       if (is_operation_done)
00362         handler_(asio::error_code(), rc);
00363       else
00364         // Since the operation was not completed, try it again...
00365         start();
00366     }
00367     else 
00368       handler_(error, rc);
00369   }
00370 
00371   int do_async_read()
00372   {
00373     // Wait for new data
00374     BOOST_ASSERT(strand_);
00375     socket_.async_read_some
00376     ( 
00377       asio::buffer(recv_buf_.get_unused_start(),
00378         recv_buf_.get_unused_len()),
00379       strand_->wrap
00380       (
00381         boost::bind
00382         (
00383           &openssl_operation::async_read_handler, 
00384           this, 
00385           asio::placeholders::error, 
00386           asio::placeholders::bytes_transferred
00387         )
00388       )
00389     );
00390     return 0;
00391   }
00392 
00393   void async_read_handler(const asio::error_code& error,
00394       size_t bytes_recvd)
00395   {
00396     if (!error)
00397     {
00398       recv_buf_.data_added(bytes_recvd);
00399 
00400       // Pass the received data to SSL
00401       int written = ::BIO_write
00402       ( 
00403         ssl_bio_, 
00404         recv_buf_.get_data_start(), 
00405         recv_buf_.get_data_len() 
00406       );
00407 
00408       if (written > 0)
00409       {
00410         recv_buf_.data_removed(written);
00411       }
00412       else if (written < 0)
00413       {
00414         if (!BIO_should_retry(ssl_bio_))
00415         {
00416           // Some serios error with BIO....
00417           handler_(asio::error::no_recovery, 0);
00418           return;
00419         }
00420       }
00421 
00422       // and try the SSL primitive again
00423       start();
00424     }
00425     else
00426     {
00427       // Error in network level...
00428       // SSL can't continue either...
00429       handler_(error, 0);
00430     }
00431   }
00432 
00433   // Syncronous functions...
00434   int do_sync_write(bool is_operation_done, int rc)
00435   {
00436     int len = ::BIO_ctrl_pending( ssl_bio_ );
00437     if ( len )
00438     { 
00439       // There is something to write into net, do it...
00440       len = (int)send_buf_.get_unused_len() > len? 
00441         len: 
00442         send_buf_.get_unused_len();
00443         
00444       // Read outgoing data from bio
00445       len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); 
00446          
00447       if (len > 0)
00448       {
00449         size_t sent_len = asio::write( 
00450                   socket_, 
00451                   asio::buffer(send_buf_.get_unused_start(), len)
00452                   );
00453 
00454         send_buf_.data_added(len);
00455         send_buf_.data_removed(sent_len);
00456       }          
00457       else if (!BIO_should_retry(ssl_bio_))
00458       {
00459         // Seems like fatal error
00460         // reading from SSL BIO has failed...
00461         throw asio::system_error(asio::error::no_recovery);
00462       }
00463     }
00464     
00465     if (is_operation_done)
00466       // Finish the operation, with success
00467       return rc;
00468                 
00469     // Operation is not finished, start again.
00470     return start();
00471   }
00472 
00473   int do_sync_read()
00474   {
00475     size_t len = socket_.read_some
00476       ( 
00477         asio::buffer(recv_buf_.get_unused_start(),
00478           recv_buf_.get_unused_len())
00479       );
00480 
00481     // Write data to ssl
00482     recv_buf_.data_added(len);
00483 
00484     // Pass the received data to SSL
00485     int written = ::BIO_write
00486     ( 
00487       ssl_bio_, 
00488       recv_buf_.get_data_start(), 
00489       recv_buf_.get_data_len() 
00490     );
00491 
00492     if (written > 0)
00493     {
00494       recv_buf_.data_removed(written);
00495     }
00496     else if (written < 0)
00497     {
00498       if (!BIO_should_retry(ssl_bio_))
00499       {
00500         // Some serios error with BIO....
00501         throw asio::system_error(asio::error::no_recovery);
00502       }
00503     }
00504 
00505     // Try the operation again
00506     return start();
00507   }
00508 }; // class openssl_operation
00509 
00510 } // namespace detail
00511 } // namespace ssl
00512 } // namespace asio
00513 
00514 #include "asio/detail/pop_options.hpp"
00515 
00516 #endif // ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines


Castor
Author(s): Carpe Noctem
autogenerated on Fri Nov 8 2013 11:05:39