PageRenderTime 49ms CodeModel.GetById 16ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/Src/Dependencies/Boost/boost/asio/ssl/old/detail/openssl_operation.hpp

http://hadesmem.googlecode.com/
C++ Header | 526 lines | 394 code | 63 blank | 69 comment | 57 complexity | 95915edb16a4669344b95b0f218cbbe7 MD5 | raw file
  1//
  2// ssl/old/detail/openssl_operation.hpp
  3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4//
  5// Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
  6//
  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)
  9//
 10
 11#ifndef BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_OPERATION_HPP
 12#define BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_OPERATION_HPP
 13
 14#if defined(_MSC_VER) && (_MSC_VER >= 1200)
 15# pragma once
 16#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 17
 18#include <boost/asio/detail/config.hpp>
 19#include <boost/function.hpp>
 20#include <boost/assert.hpp>
 21#include <boost/bind.hpp>
 22#include <boost/asio/buffer.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>
 30
 31#include <boost/asio/detail/push_options.hpp>
 32
 33namespace boost {
 34namespace asio {
 35namespace ssl {
 36namespace old {
 37namespace detail {
 38
 39typedef boost::function<int (::SSL*)> ssl_primitive_func; 
 40typedef boost::function<void (const boost::system::error_code&, int)>
 41  user_handler_func;
 42
 43// Network send_/recv buffer implementation
 44//
 45//
 46class net_buffer
 47{
 48  static const int  NET_BUF_SIZE = 16*1024 + 256; // SSL record size + spare
 49
 50  unsigned char buf_[NET_BUF_SIZE];
 51  unsigned char* data_start_;
 52  unsigned char* data_end_;
 53
 54public:
 55  net_buffer()
 56  {
 57    data_start_ = data_end_ = buf_;
 58  }
 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)
 64  { 
 65    data_end_ += count; 
 66    data_end_ = data_end_ > (buf_ + NET_BUF_SIZE)? 
 67      (buf_ + NET_BUF_SIZE):
 68      data_end_; 
 69  }
 70  void data_removed(size_t count) 
 71  { 
 72    data_start_ += count; 
 73    if (data_start_ >= data_end_) reset(); 
 74  }
 75  void reset() { data_start_ = buf_; data_end_ = buf_; }               
 76  bool has_data() { return (data_start_ < data_end_); }
 77}; // class net_buffer
 78
 79//
 80// Operation class
 81//
 82//
 83template <typename Stream>
 84class openssl_operation
 85{
 86public:
 87
 88  // Constructor for asynchronous operations
 89  openssl_operation(ssl_primitive_func primitive,
 90                    Stream& socket,
 91                    net_buffer& recv_buf,
 92                    SSL* session,
 93                    BIO* ssl_bio,
 94                    user_handler_func  handler,
 95                    boost::asio::io_service::strand& strand
 96                    )
 97    : primitive_(primitive)
 98    , user_handler_(handler)
 99    , strand_(&strand)
100    , recv_buf_(recv_buf)
101    , socket_(socket)
102    , ssl_bio_(ssl_bio)
103    , session_(session)
104  {
105    write_ = boost::bind(
106      &openssl_operation::do_async_write, 
107      this, boost::arg<1>(), boost::arg<2>()
108    );
109    read_ = boost::bind(
110      &openssl_operation::do_async_read, 
111      this
112    );
113    handler_= boost::bind(
114      &openssl_operation::async_user_handler, 
115      this, boost::arg<1>(), boost::arg<2>()
116    );
117  }
118
119  // Constructor for synchronous operations
120  openssl_operation(ssl_primitive_func primitive,
121                    Stream& socket,
122                    net_buffer& recv_buf,
123                    SSL* session,
124                    BIO* ssl_bio)
125    : primitive_(primitive)
126    , strand_(0)
127    , recv_buf_(recv_buf)
128    , socket_(socket)
129    , ssl_bio_(ssl_bio)
130    , session_(session)
131  {      
132    write_ = boost::bind(
133      &openssl_operation::do_sync_write, 
134      this, boost::arg<1>(), boost::arg<2>()
135    );
136    read_ = boost::bind(
137      &openssl_operation::do_sync_read, 
138      this
139    );
140    handler_ = boost::bind(
141      &openssl_operation::sync_user_handler, 
142      this, boost::arg<1>(), boost::arg<2>()
143      );
144  }
145
146  // Start operation
147  // In case of asynchronous it returns 0, in sync mode returns success code
148  // or throws an error...
149  int start()
150  {
151    int rc = primitive_( session_ );
152
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, is is done when retcode > 0
158
159    int error_code =  !is_operation_done ?
160          ::SSL_get_error( session_, rc ) :
161          0;        
162    int sys_error_code = ERR_get_error();
163
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);
167
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) ==
176            SSL_SENT_SHUTDOWN);
177
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);
182
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);
187
188    if (!is_operation_done && !is_read_needed && !is_write_needed 
189      && !is_shut_down_sent)
190    {
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)
194      {
195        return handler_(boost::system::error_code(
196              sys_error_code, boost::asio::error::system_category), rc); 
197      }
198      else
199      {
200        return handler_(boost::system::error_code(
201              sys_error_code, boost::asio::error::get_ssl_category()), rc); 
202      }
203    }
204
205    if (!is_operation_done && !is_write_needed)
206    {
207      // We may have left over data that we can pass to SSL immediately
208      if (recv_buf_.get_data_len() > 0)
209      {
210        // Pass the buffered data to SSL
211        int written = ::BIO_write
212        ( 
213          ssl_bio_, 
214          recv_buf_.get_data_start(), 
215          recv_buf_.get_data_len() 
216        );
217
218        if (written > 0)
219        {
220          recv_buf_.data_removed(written);
221        }
222        else if (written < 0)
223        {
224          if (!BIO_should_retry(ssl_bio_))
225          {
226            // Some serios error with BIO....
227            return handler_(boost::asio::error::no_recovery, 0);
228          }
229        }
230
231        return start();
232      }
233      else if (is_read_needed || (is_shut_down_sent && !is_shut_down_received))
234      {
235        return read_();
236      }
237    }
238
239    // Continue with operation, flush any SSL data out to network...
240    return write_(is_operation_done, rc); 
241  }
242
243// Private implementation
244private:
245  typedef boost::function<int (const boost::system::error_code&, int)>
246    int_handler_func;
247  typedef boost::function<int (bool, int)> write_func;
248  typedef boost::function<int ()> read_func;
249
250  ssl_primitive_func  primitive_;
251  user_handler_func  user_handler_;
252  boost::asio::io_service::strand* strand_;
253  write_func  write_;
254  read_func  read_;
255  int_handler_func handler_;
256    
257  net_buffer send_buf_; // buffers for network IO
258
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
262  // application.
263  net_buffer& recv_buf_;
264
265  Stream& socket_;
266  BIO*    ssl_bio_;
267  SSL*    session_;
268
269  //
270  int sync_user_handler(const boost::system::error_code& error, int rc)
271  {
272    if (!error)
273      return rc;
274
275    throw boost::system::system_error(error);
276  }
277    
278  int async_user_handler(boost::system::error_code error, int rc)
279  {
280    if (rc < 0)
281    {
282      if (!error)
283        error = boost::asio::error::no_recovery;
284      rc = 0;
285    }
286
287    user_handler_(error, rc);
288    return 0;
289  }
290
291  // Writes bytes asynchronously from SSL to NET
292  int  do_async_write(bool is_operation_done, int rc) 
293  {
294    int len = ::BIO_ctrl_pending( ssl_bio_ );
295    if ( len )
296    { 
297      // There is something to write into net, do it...
298      len = (int)send_buf_.get_unused_len() > len? 
299        len: 
300        send_buf_.get_unused_len();
301        
302      if (len == 0)
303      {
304        // In case our send buffer is full, we have just to wait until 
305        // previous send to complete...
306        return 0;
307      }
308
309      // Read outgoing data from bio
310      len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); 
311         
312      if (len > 0)
313      {
314        unsigned char *data_start = send_buf_.get_unused_start();
315        send_buf_.data_added(len);
316 
317        BOOST_ASSERT(strand_); 
318        boost::asio::async_write
319        ( 
320          socket_, 
321          boost::asio::buffer(data_start, len),
322          strand_->wrap
323          (
324            boost::bind
325            (
326              &openssl_operation::async_write_handler, 
327              this, 
328              is_operation_done,
329              rc, 
330              boost::asio::placeholders::error, 
331              boost::asio::placeholders::bytes_transferred
332            )
333          )
334        );
335                  
336        return 0;
337      }
338      else if (!BIO_should_retry(ssl_bio_))
339      {
340        // Seems like fatal error
341        // reading from SSL BIO has failed...
342        handler_(boost::asio::error::no_recovery, 0);
343        return 0;
344      }
345    }
346    
347    if (is_operation_done)
348    {
349      // Finish the operation, with success
350      handler_(boost::system::error_code(), rc);
351      return 0;
352    }
353    
354    // OPeration is not done and writing to net has been made...
355    // start operation again
356    start();
357          
358    return 0;
359  }
360
361  void async_write_handler(bool is_operation_done, int rc, 
362    const boost::system::error_code& error, size_t bytes_sent)
363  {
364    if (!error)
365    {
366      // Remove data from send buffer
367      send_buf_.data_removed(bytes_sent);
368
369      if (is_operation_done)
370        handler_(boost::system::error_code(), rc);
371      else
372        // Since the operation was not completed, try it again...
373        start();
374    }
375    else 
376      handler_(error, rc);
377  }
378
379  int do_async_read()
380  {
381    // Wait for new data
382    BOOST_ASSERT(strand_);
383    socket_.async_read_some
384    ( 
385      boost::asio::buffer(recv_buf_.get_unused_start(),
386        recv_buf_.get_unused_len()),
387      strand_->wrap
388      (
389        boost::bind
390        (
391          &openssl_operation::async_read_handler, 
392          this, 
393          boost::asio::placeholders::error, 
394          boost::asio::placeholders::bytes_transferred
395        )
396      )
397    );
398    return 0;
399  }
400
401  void async_read_handler(const boost::system::error_code& error,
402      size_t bytes_recvd)
403  {
404    if (!error)
405    {
406      recv_buf_.data_added(bytes_recvd);
407
408      // Pass the received data to SSL
409      int written = ::BIO_write
410      ( 
411        ssl_bio_, 
412        recv_buf_.get_data_start(), 
413        recv_buf_.get_data_len() 
414      );
415
416      if (written > 0)
417      {
418        recv_buf_.data_removed(written);
419      }
420      else if (written < 0)
421      {
422        if (!BIO_should_retry(ssl_bio_))
423        {
424          // Some serios error with BIO....
425          handler_(boost::asio::error::no_recovery, 0);
426          return;
427        }
428      }
429
430      // and try the SSL primitive again
431      start();
432    }
433    else
434    {
435      // Error in network level...
436      // SSL can't continue either...
437      handler_(error, 0);
438    }
439  }
440
441  // Syncronous functions...
442  int do_sync_write(bool is_operation_done, int rc)
443  {
444    int len = ::BIO_ctrl_pending( ssl_bio_ );
445    if ( len )
446    { 
447      // There is something to write into net, do it...
448      len = (int)send_buf_.get_unused_len() > len? 
449        len: 
450        send_buf_.get_unused_len();
451        
452      // Read outgoing data from bio
453      len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); 
454         
455      if (len > 0)
456      {
457        size_t sent_len = boost::asio::write( 
458                  socket_, 
459                  boost::asio::buffer(send_buf_.get_unused_start(), len)
460                  );
461
462        send_buf_.data_added(len);
463        send_buf_.data_removed(sent_len);
464      }          
465      else if (!BIO_should_retry(ssl_bio_))
466      {
467        // Seems like fatal error
468        // reading from SSL BIO has failed...
469        throw boost::system::system_error(boost::asio::error::no_recovery);
470      }
471    }
472    
473    if (is_operation_done)
474      // Finish the operation, with success
475      return rc;
476                
477    // Operation is not finished, start again.
478    return start();
479  }
480
481  int do_sync_read()
482  {
483    size_t len = socket_.read_some
484      ( 
485        boost::asio::buffer(recv_buf_.get_unused_start(),
486          recv_buf_.get_unused_len())
487      );
488
489    // Write data to ssl
490    recv_buf_.data_added(len);
491
492    // Pass the received data to SSL
493    int written = ::BIO_write
494    ( 
495      ssl_bio_, 
496      recv_buf_.get_data_start(), 
497      recv_buf_.get_data_len() 
498    );
499
500    if (written > 0)
501    {
502      recv_buf_.data_removed(written);
503    }
504    else if (written < 0)
505    {
506      if (!BIO_should_retry(ssl_bio_))
507      {
508        // Some serios error with BIO....
509        throw boost::system::system_error(boost::asio::error::no_recovery);
510      }
511    }
512
513    // Try the operation again
514    return start();
515  }
516}; // class openssl_operation
517
518} // namespace detail
519} // namespace old
520} // namespace ssl
521} // namespace asio
522} // namespace boost
523
524#include <boost/asio/detail/pop_options.hpp>
525
526#endif // BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_OPERATION_HPP