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/bind.hpp>
21 #include <boost/asio/buffer.hpp>
22 #include <boost/asio/detail/assert.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 
33 namespace boost {
34 namespace asio {
35 namespace ssl {
36 namespace old {
37 namespace detail {
38 
39 typedef boost::function<int (::SSL*)> ssl_primitive_func;
40 typedef boost::function<void (const boost::system::error_code&, int)>
41   user_handler_func;
42 
43 // Network send_/recv buffer implementation
44 //
45 //
46 class 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 
54 public:
net_buffer()55   net_buffer()
56   {
57     data_start_ = data_end_ = buf_;
58   }
get_unused_start()59   unsigned char* get_unused_start() { return data_end_; }
get_data_start()60   unsigned char* get_data_start() { return data_start_; }
get_unused_len()61   size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); }
get_data_len()62   size_t get_data_len() { return (data_end_ - data_start_); }
data_added(size_t count)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   }
data_removed(size_t count)70   void data_removed(size_t count)
71   {
72     data_start_ += count;
73     if (data_start_ >= data_end_) reset();
74   }
reset()75   void reset() { data_start_ = buf_; data_end_ = buf_; }
has_data()76   bool has_data() { return (data_start_ < data_end_); }
77 }; // class net_buffer
78 
79 //
80 // Operation class
81 //
82 //
83 template <typename Stream>
84 class openssl_operation
85 {
86 public:
87 
88   // Constructor for asynchronous operations
openssl_operation(ssl_primitive_func primitive,Stream & socket,net_buffer & recv_buf,SSL * session,BIO * ssl_bio,user_handler_func handler,boost::asio::io_service::strand & strand)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
openssl_operation(ssl_primitive_func primitive,Stream & socket,net_buffer & recv_buf,SSL * session,BIO * ssl_bio)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...
start()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, it 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
244 private:
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   //
sync_user_handler(const boost::system::error_code & error,int rc)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 
async_user_handler(boost::system::error_code error,int rc)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
do_async_write(bool is_operation_done,int rc)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_ASIO_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 
async_write_handler(bool is_operation_done,int rc,const boost::system::error_code & error,size_t bytes_sent)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 
do_async_read()379   int do_async_read()
380   {
381     // Wait for new data
382     BOOST_ASIO_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 
async_read_handler(const boost::system::error_code & error,size_t bytes_recvd)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...
do_sync_write(bool is_operation_done,int rc)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 
do_sync_read()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
527