~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): Cristian Greco
  • Date: 2008-07-02 10:46:21 UTC
  • Revision ID: james.westby@ubuntu.com-20080702104621-jzx3pfke9lkcxfxn
Tags: upstream-0.13.1
ImportĀ upstreamĀ versionĀ 0.13.1

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)
 
172
      // SSL connection is shut down cleanly
 
173
      return handler_(asio::error_code(), 1);
 
174
 
 
175
    if (is_shut_down_received && !is_write_needed)
 
176
      return handler_(asio::error::eof, 0);
 
177
 
 
178
    if (is_shut_down_received)
 
179
      // Shutdown has been requested, while we were reading or writing...
 
180
      // abort our action...
 
181
      return handler_(asio::error::shut_down, 0);
 
182
 
 
183
    if (!is_operation_done && !is_read_needed && !is_write_needed 
 
184
      && !is_shut_down_sent)
 
185
    {
 
186
      // The operation has failed... It is not completed and does 
 
187
      // not want network communication nor does want to send shutdown out...
 
188
      if (error_code == SSL_ERROR_SYSCALL)
 
189
      {
 
190
        return handler_(asio::error_code(
 
191
              sys_error_code, asio::error::system_category), rc); 
 
192
      }
 
193
      else
 
194
      {
 
195
        return handler_(asio::error_code(
 
196
              error_code, asio::error::get_ssl_category()), rc); 
 
197
      }
 
198
    }
 
199
 
 
200
    if (!is_operation_done && !is_write_needed)
 
201
    {
 
202
      // We may have left over data that we can pass to SSL immediately
 
203
      if (recv_buf_.get_data_len() > 0)
 
204
      {
 
205
        // Pass the buffered data to SSL
 
206
        int written = ::BIO_write
 
207
        ( 
 
208
          ssl_bio_, 
 
209
          recv_buf_.get_data_start(), 
 
210
          recv_buf_.get_data_len() 
 
211
        );
 
212
 
 
213
        if (written > 0)
 
214
        {
 
215
          recv_buf_.data_removed(written);
 
216
        }
 
217
        else if (written < 0)
 
218
        {
 
219
          if (!BIO_should_retry(ssl_bio_))
 
220
          {
 
221
            // Some serios error with BIO....
 
222
            return handler_(asio::error::no_recovery, 0);
 
223
          }
 
224
        }
 
225
 
 
226
        return start();
 
227
      }
 
228
      else if (is_read_needed)
 
229
      {
 
230
        return read_();
 
231
      }
 
232
    }
 
233
 
 
234
    // Continue with operation, flush any SSL data out to network...
 
235
    return write_(is_operation_done, rc); 
 
236
  }
 
237
 
 
238
// Private implementation
 
239
private:
 
240
  typedef boost::function<int (const asio::error_code&, int)>
 
241
    int_handler_func;
 
242
  typedef boost::function<int (bool, int)> write_func;
 
243
  typedef boost::function<int ()> read_func;
 
244
 
 
245
  ssl_primitive_func  primitive_;
 
246
  user_handler_func  user_handler_;
 
247
  asio::io_service::strand* strand_;
 
248
  write_func  write_;
 
249
  read_func  read_;
 
250
  int_handler_func handler_;
 
251
    
 
252
  net_buffer send_buf_; // buffers for network IO
 
253
 
 
254
  // The recv buffer is owned by the stream, not the operation, since there can
 
255
  // be left over bytes after passing the data up to the application, and these
 
256
  // bytes need to be kept around for the next read operation issued by the
 
257
  // application.
 
258
  net_buffer& recv_buf_;
 
259
 
 
260
  Stream& socket_;
 
261
  BIO*    ssl_bio_;
 
262
  SSL*    session_;
 
263
 
 
264
  //
 
265
  int sync_user_handler(const asio::error_code& error, int rc)
 
266
  {
 
267
    if (!error)
 
268
      return rc;
 
269
 
 
270
    throw asio::system_error(error);
 
271
  }
 
272
    
 
273
  int async_user_handler(asio::error_code error, int rc)
 
274
  {
 
275
    if (rc < 0)
 
276
    {
 
277
      if (!error)
 
278
        error = asio::error::no_recovery;
 
279
      rc = 0;
 
280
    }
 
281
 
 
282
    user_handler_(error, rc);
 
283
    return 0;
 
284
  }
 
285
 
 
286
  // Writes bytes asynchronously from SSL to NET
 
287
  int  do_async_write(bool is_operation_done, int rc) 
 
288
  {
 
289
    int len = ::BIO_ctrl_pending( ssl_bio_ );
 
290
    if ( len )
 
291
    { 
 
292
      // There is something to write into net, do it...
 
293
      len = (int)send_buf_.get_unused_len() > len? 
 
294
        len: 
 
295
        send_buf_.get_unused_len();
 
296
        
 
297
      if (len == 0)
 
298
      {
 
299
        // In case our send buffer is full, we have just to wait until 
 
300
        // previous send to complete...
 
301
        return 0;
 
302
      }
 
303
 
 
304
      // Read outgoing data from bio
 
305
      len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); 
 
306
         
 
307
      if (len > 0)
 
308
      {
 
309
        unsigned char *data_start = send_buf_.get_unused_start();
 
310
        send_buf_.data_added(len);
 
311
 
 
312
        BOOST_ASSERT(strand_); 
 
313
        asio::async_write
 
314
        ( 
 
315
          socket_, 
 
316
          asio::buffer(data_start, len),
 
317
          strand_->wrap
 
318
          (
 
319
            boost::bind
 
320
            (
 
321
              &openssl_operation::async_write_handler, 
 
322
              this, 
 
323
              is_operation_done,
 
324
              rc, 
 
325
              asio::placeholders::error, 
 
326
              asio::placeholders::bytes_transferred
 
327
            )
 
328
          )
 
329
        );
 
330
                  
 
331
        return 0;
 
332
      }
 
333
      else if (!BIO_should_retry(ssl_bio_))
 
334
      {
 
335
        // Seems like fatal error
 
336
        // reading from SSL BIO has failed...
 
337
        handler_(asio::error::no_recovery, 0);
 
338
        return 0;
 
339
      }
 
340
    }
 
341
    
 
342
    if (is_operation_done)
 
343
    {
 
344
      // Finish the operation, with success
 
345
      handler_(asio::error_code(), rc);
 
346
      return 0;
 
347
    }
 
348
    
 
349
    // OPeration is not done and writing to net has been made...
 
350
    // start operation again
 
351
    start();
 
352
          
 
353
    return 0;
 
354
  }
 
355
 
 
356
  void async_write_handler(bool is_operation_done, int rc, 
 
357
    const asio::error_code& error, size_t bytes_sent)
 
358
  {
 
359
    if (!error)
 
360
    {
 
361
      // Remove data from send buffer
 
362
      send_buf_.data_removed(bytes_sent);
 
363
 
 
364
      if (is_operation_done)
 
365
        handler_(asio::error_code(), rc);
 
366
      else
 
367
        // Since the operation was not completed, try it again...
 
368
        start();
 
369
    }
 
370
    else 
 
371
      handler_(error, rc);
 
372
  }
 
373
 
 
374
  int do_async_read()
 
375
  {
 
376
    // Wait for new data
 
377
    BOOST_ASSERT(strand_);
 
378
    socket_.async_read_some
 
379
    ( 
 
380
      asio::buffer(recv_buf_.get_unused_start(),
 
381
        recv_buf_.get_unused_len()),
 
382
      strand_->wrap
 
383
      (
 
384
        boost::bind
 
385
        (
 
386
          &openssl_operation::async_read_handler, 
 
387
          this, 
 
388
          asio::placeholders::error, 
 
389
          asio::placeholders::bytes_transferred
 
390
        )
 
391
      )
 
392
    );
 
393
    return 0;
 
394
  }
 
395
 
 
396
  void async_read_handler(const asio::error_code& error,
 
397
      size_t bytes_recvd)
 
398
  {
 
399
    if (!error)
 
400
    {
 
401
      recv_buf_.data_added(bytes_recvd);
 
402
 
 
403
      // Pass the received data to SSL
 
404
      int written = ::BIO_write
 
405
      ( 
 
406
        ssl_bio_, 
 
407
        recv_buf_.get_data_start(), 
 
408
        recv_buf_.get_data_len() 
 
409
      );
 
410
 
 
411
      if (written > 0)
 
412
      {
 
413
        recv_buf_.data_removed(written);
 
414
      }
 
415
      else if (written < 0)
 
416
      {
 
417
        if (!BIO_should_retry(ssl_bio_))
 
418
        {
 
419
          // Some serios error with BIO....
 
420
          handler_(asio::error::no_recovery, 0);
 
421
          return;
 
422
        }
 
423
      }
 
424
 
 
425
      // and try the SSL primitive again
 
426
      start();
 
427
    }
 
428
    else
 
429
    {
 
430
      // Error in network level...
 
431
      // SSL can't continue either...
 
432
      handler_(error, 0);
 
433
    }
 
434
  }
 
435
 
 
436
  // Syncronous functions...
 
437
  int do_sync_write(bool is_operation_done, int rc)
 
438
  {
 
439
    int len = ::BIO_ctrl_pending( ssl_bio_ );
 
440
    if ( len )
 
441
    { 
 
442
      // There is something to write into net, do it...
 
443
      len = (int)send_buf_.get_unused_len() > len? 
 
444
        len: 
 
445
        send_buf_.get_unused_len();
 
446
        
 
447
      // Read outgoing data from bio
 
448
      len = ::BIO_read( ssl_bio_, send_buf_.get_unused_start(), len); 
 
449
         
 
450
      if (len > 0)
 
451
      {
 
452
        size_t sent_len = asio::write( 
 
453
                  socket_, 
 
454
                  asio::buffer(send_buf_.get_unused_start(), len)
 
455
                  );
 
456
 
 
457
        send_buf_.data_added(len);
 
458
        send_buf_.data_removed(sent_len);
 
459
      }          
 
460
      else if (!BIO_should_retry(ssl_bio_))
 
461
      {
 
462
        // Seems like fatal error
 
463
        // reading from SSL BIO has failed...
 
464
        throw asio::system_error(asio::error::no_recovery);
 
465
      }
 
466
    }
 
467
    
 
468
    if (is_operation_done)
 
469
      // Finish the operation, with success
 
470
      return rc;
 
471
                
 
472
    // Operation is not finished, start again.
 
473
    return start();
 
474
  }
 
475
 
 
476
  int do_sync_read()
 
477
  {
 
478
    size_t len = socket_.read_some
 
479
      ( 
 
480
        asio::buffer(recv_buf_.get_unused_start(),
 
481
          recv_buf_.get_unused_len())
 
482
      );
 
483
 
 
484
    // Write data to ssl
 
485
    recv_buf_.data_added(len);
 
486
 
 
487
    // Pass the received data to SSL
 
488
    int written = ::BIO_write
 
489
    ( 
 
490
      ssl_bio_, 
 
491
      recv_buf_.get_data_start(), 
 
492
      recv_buf_.get_data_len() 
 
493
    );
 
494
 
 
495
    if (written > 0)
 
496
    {
 
497
      recv_buf_.data_removed(written);
 
498
    }
 
499
    else if (written < 0)
 
500
    {
 
501
      if (!BIO_should_retry(ssl_bio_))
 
502
      {
 
503
        // Some serios error with BIO....
 
504
        throw asio::system_error(asio::error::no_recovery);
 
505
      }
 
506
    }
 
507
 
 
508
    // Try the operation again
 
509
    return start();
 
510
  }
 
511
}; // class openssl_operation
 
512
 
 
513
} // namespace detail
 
514
} // namespace ssl
 
515
} // namespace asio
 
516
 
 
517
#include "asio/detail/pop_options.hpp"
 
518
 
 
519
#endif // ASIO_SSL_DETAIL_OPENSSL_OPERATION_HPP