~ubuntu-branches/ubuntu/maverick/libtorrent-rasterbar/maverick

« back to all changes in this revision

Viewing changes to include/libtorrent/asio/ssl/detail/openssl_operation.hpp

  • Committer: Bazaar Package Importer
  • Author(s): Christophe Sauthier
  • Date: 2010-08-10 12:59:37 UTC
  • mfrom: (1.3.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20100810125937-jbcmmf17y8yo9hgz
Tags: 0.15.0-0ubuntu1
* New upstream version.
* debian/patches/100_fix_html_docs.patch: refreshed.
* debian/control: bump up standards-version to 3.9.1 (no changes).

Show diffs side-by-side

added added

removed removed

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