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