1 //
2 // ssl/old/detail/stream_service.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2005 Voipster / Indrek dot Juhani at voipster dot com
6 // Copyright (c) 2005-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
7 //
8 // Distributed under the Boost Software License, Version 1.0. (See accompanying
9 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10 //
11 
12 #ifndef BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_STREAM_SERVICE_HPP
13 #define BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_STREAM_SERVICE_HPP
14 
15 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
16 # pragma once
17 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
18 
19 #include <boost/asio/detail/config.hpp>
20 #include <cstddef>
21 #include <climits>
22 #include <memory>
23 #include <boost/config.hpp>
24 #include <boost/noncopyable.hpp>
25 #include <boost/function.hpp>
26 #include <boost/bind.hpp>
27 #include <boost/asio/detail/buffer_sequence_adapter.hpp>
28 #include <boost/asio/error.hpp>
29 #include <boost/asio/io_service.hpp>
30 #include <boost/asio/ssl/basic_context.hpp>
31 #include <boost/asio/ssl/stream_base.hpp>
32 #include <boost/asio/ssl/old/detail/openssl_operation.hpp>
33 #include <boost/asio/ssl/detail/openssl_types.hpp>
34 #include <boost/asio/strand.hpp>
35 #include <boost/system/system_error.hpp>
36 
37 #include <boost/asio/detail/push_options.hpp>
38 
39 namespace boost {
40 namespace asio {
41 namespace ssl {
42 namespace old {
43 namespace detail {
44 
45 class openssl_stream_service
46   : public boost::asio::detail::service_base<openssl_stream_service>
47 {
48 private:
49   enum { max_buffer_size = INT_MAX };
50 
51   //Base handler for asyncrhonous operations
52   template <typename Stream>
53   class base_handler
54   {
55   public:
56     typedef boost::function<
57       void (const boost::system::error_code&, size_t)> func_t;
58 
base_handler(boost::asio::io_service & io_service)59     base_handler(boost::asio::io_service& io_service)
60       : op_(NULL)
61       , io_service_(io_service)
62       , work_(io_service)
63     {}
64 
do_func(const boost::system::error_code & error,size_t size)65     void do_func(const boost::system::error_code& error, size_t size)
66     {
67       func_(error, size);
68     }
69 
set_operation(openssl_operation<Stream> * op)70     void set_operation(openssl_operation<Stream>* op) { op_ = op; }
set_func(func_t func)71     void set_func(func_t func) { func_ = func; }
72 
~base_handler()73     ~base_handler()
74     {
75       delete op_;
76     }
77 
78   private:
79     func_t func_;
80     openssl_operation<Stream>* op_;
81     boost::asio::io_service& io_service_;
82     boost::asio::io_service::work work_;
83   };  // class base_handler
84 
85   // Handler for asynchronous IO (write/read) operations
86   template<typename Stream, typename Handler>
87   class io_handler
88     : public base_handler<Stream>
89   {
90   public:
io_handler(Handler handler,boost::asio::io_service & io_service)91     io_handler(Handler handler, boost::asio::io_service& io_service)
92       : base_handler<Stream>(io_service)
93       , handler_(handler)
94     {
95       this->set_func(boost::bind(
96         &io_handler<Stream, Handler>::handler_impl,
97         this, boost::arg<1>(), boost::arg<2>() ));
98     }
99 
100   private:
101     Handler handler_;
handler_impl(const boost::system::error_code & error,size_t size)102     void handler_impl(const boost::system::error_code& error, size_t size)
103     {
104       std::auto_ptr<io_handler<Stream, Handler> > this_ptr(this);
105       handler_(error, size);
106     }
107   };  // class io_handler
108 
109   // Handler for asyncrhonous handshake (connect, accept) functions
110   template <typename Stream, typename Handler>
111   class handshake_handler
112     : public base_handler<Stream>
113   {
114   public:
handshake_handler(Handler handler,boost::asio::io_service & io_service)115     handshake_handler(Handler handler, boost::asio::io_service& io_service)
116       : base_handler<Stream>(io_service)
117       , handler_(handler)
118     {
119       this->set_func(boost::bind(
120         &handshake_handler<Stream, Handler>::handler_impl,
121         this, boost::arg<1>(), boost::arg<2>() ));
122     }
123 
124   private:
125     Handler handler_;
handler_impl(const boost::system::error_code & error,size_t)126     void handler_impl(const boost::system::error_code& error, size_t)
127     {
128       std::auto_ptr<handshake_handler<Stream, Handler> > this_ptr(this);
129       handler_(error);
130     }
131 
132   };  // class handshake_handler
133 
134   // Handler for asyncrhonous shutdown
135   template <typename Stream, typename Handler>
136   class shutdown_handler
137     : public base_handler<Stream>
138   {
139   public:
shutdown_handler(Handler handler,boost::asio::io_service & io_service)140     shutdown_handler(Handler handler, boost::asio::io_service& io_service)
141       : base_handler<Stream>(io_service),
142         handler_(handler)
143     {
144       this->set_func(boost::bind(
145         &shutdown_handler<Stream, Handler>::handler_impl,
146         this, boost::arg<1>(), boost::arg<2>() ));
147     }
148 
149   private:
150     Handler handler_;
handler_impl(const boost::system::error_code & error,size_t)151     void handler_impl(const boost::system::error_code& error, size_t)
152     {
153       std::auto_ptr<shutdown_handler<Stream, Handler> > this_ptr(this);
154       handler_(error);
155     }
156   };  // class shutdown_handler
157 
158 public:
159   // The implementation type.
160   typedef struct impl_struct
161   {
162     ::SSL* ssl;
163     ::BIO* ext_bio;
164     net_buffer recv_buf;
165   } * impl_type;
166 
167   // Construct a new stream socket service for the specified io_service.
openssl_stream_service(boost::asio::io_service & io_service)168   explicit openssl_stream_service(boost::asio::io_service& io_service)
169     : boost::asio::detail::service_base<openssl_stream_service>(io_service),
170       strand_(io_service)
171   {
172   }
173 
174   // Destroy all user-defined handler objects owned by the service.
shutdown_service()175   void shutdown_service()
176   {
177   }
178 
179   // Return a null stream implementation.
null() const180   impl_type null() const
181   {
182     return 0;
183   }
184 
185   // Create a new stream implementation.
186   template <typename Stream, typename Context_Service>
create(impl_type & impl,Stream &,basic_context<Context_Service> & context)187   void create(impl_type& impl, Stream& /*next_layer*/,
188       basic_context<Context_Service>& context)
189   {
190     impl = new impl_struct;
191     impl->ssl = ::SSL_new(context.impl());
192     ::SSL_set_mode(impl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
193     ::SSL_set_mode(impl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
194     ::BIO* int_bio = 0;
195     impl->ext_bio = 0;
196     ::BIO_new_bio_pair(&int_bio, 8192, &impl->ext_bio, 8192);
197     ::SSL_set_bio(impl->ssl, int_bio, int_bio);
198   }
199 
200   // Destroy a stream implementation.
201   template <typename Stream>
destroy(impl_type & impl,Stream &)202   void destroy(impl_type& impl, Stream& /*next_layer*/)
203   {
204     if (impl != 0)
205     {
206       ::BIO_free(impl->ext_bio);
207       ::SSL_free(impl->ssl);
208       delete impl;
209       impl = 0;
210     }
211   }
212 
213   // Perform SSL handshaking.
214   template <typename Stream>
handshake(impl_type & impl,Stream & next_layer,stream_base::handshake_type type,boost::system::error_code & ec)215   boost::system::error_code handshake(impl_type& impl, Stream& next_layer,
216       stream_base::handshake_type type, boost::system::error_code& ec)
217   {
218     try
219     {
220       openssl_operation<Stream> op(
221         type == stream_base::client ?
222           &ssl_wrap<mutex_type>::SSL_connect:
223           &ssl_wrap<mutex_type>::SSL_accept,
224         next_layer,
225         impl->recv_buf,
226         impl->ssl,
227         impl->ext_bio);
228       op.start();
229     }
230     catch (boost::system::system_error& e)
231     {
232       ec = e.code();
233       return ec;
234     }
235 
236     ec = boost::system::error_code();
237     return ec;
238   }
239 
240   // Start an asynchronous SSL handshake.
241   template <typename Stream, typename Handler>
async_handshake(impl_type & impl,Stream & next_layer,stream_base::handshake_type type,Handler handler)242   void async_handshake(impl_type& impl, Stream& next_layer,
243       stream_base::handshake_type type, Handler handler)
244   {
245     typedef handshake_handler<Stream, Handler> connect_handler;
246 
247     connect_handler* local_handler =
248       new connect_handler(handler, get_io_service());
249 
250     openssl_operation<Stream>* op = new openssl_operation<Stream>
251     (
252       type == stream_base::client ?
253         &ssl_wrap<mutex_type>::SSL_connect:
254         &ssl_wrap<mutex_type>::SSL_accept,
255       next_layer,
256       impl->recv_buf,
257       impl->ssl,
258       impl->ext_bio,
259       boost::bind
260       (
261         &base_handler<Stream>::do_func,
262         local_handler,
263         boost::arg<1>(),
264         boost::arg<2>()
265       ),
266       strand_
267     );
268     local_handler->set_operation(op);
269 
270     strand_.post(boost::bind(&openssl_operation<Stream>::start, op));
271   }
272 
273   // Shut down SSL on the stream.
274   template <typename Stream>
shutdown(impl_type & impl,Stream & next_layer,boost::system::error_code & ec)275   boost::system::error_code shutdown(impl_type& impl, Stream& next_layer,
276       boost::system::error_code& ec)
277   {
278     try
279     {
280       openssl_operation<Stream> op(
281         &ssl_wrap<mutex_type>::SSL_shutdown,
282         next_layer,
283         impl->recv_buf,
284         impl->ssl,
285         impl->ext_bio);
286       op.start();
287     }
288     catch (boost::system::system_error& e)
289     {
290       ec = e.code();
291       return ec;
292     }
293 
294     ec = boost::system::error_code();
295     return ec;
296   }
297 
298   // Asynchronously shut down SSL on the stream.
299   template <typename Stream, typename Handler>
async_shutdown(impl_type & impl,Stream & next_layer,Handler handler)300   void async_shutdown(impl_type& impl, Stream& next_layer, Handler handler)
301   {
302     typedef shutdown_handler<Stream, Handler> disconnect_handler;
303 
304     disconnect_handler* local_handler =
305       new disconnect_handler(handler, get_io_service());
306 
307     openssl_operation<Stream>* op = new openssl_operation<Stream>
308     (
309       &ssl_wrap<mutex_type>::SSL_shutdown,
310       next_layer,
311       impl->recv_buf,
312       impl->ssl,
313       impl->ext_bio,
314       boost::bind
315       (
316         &base_handler<Stream>::do_func,
317         local_handler,
318         boost::arg<1>(),
319         boost::arg<2>()
320       ),
321       strand_
322     );
323     local_handler->set_operation(op);
324 
325     strand_.post(boost::bind(&openssl_operation<Stream>::start, op));
326   }
327 
328   // Write some data to the stream.
329   template <typename Stream, typename Const_Buffers>
write_some(impl_type & impl,Stream & next_layer,const Const_Buffers & buffers,boost::system::error_code & ec)330   std::size_t write_some(impl_type& impl, Stream& next_layer,
331       const Const_Buffers& buffers, boost::system::error_code& ec)
332   {
333     size_t bytes_transferred = 0;
334     try
335     {
336       boost::asio::const_buffer buffer =
337         boost::asio::detail::buffer_sequence_adapter<
338           boost::asio::const_buffer, Const_Buffers>::first(buffers);
339 
340       std::size_t buffer_size = boost::asio::buffer_size(buffer);
341       if (buffer_size > max_buffer_size)
342         buffer_size = max_buffer_size;
343       else if (buffer_size == 0)
344       {
345         ec = boost::system::error_code();
346         return 0;
347       }
348 
349       boost::function<int (SSL*)> send_func =
350         boost::bind(boost::type<int>(), &::SSL_write, boost::arg<1>(),
351             boost::asio::buffer_cast<const void*>(buffer),
352             static_cast<int>(buffer_size));
353       openssl_operation<Stream> op(
354         send_func,
355         next_layer,
356         impl->recv_buf,
357         impl->ssl,
358         impl->ext_bio
359       );
360       bytes_transferred = static_cast<size_t>(op.start());
361     }
362     catch (boost::system::system_error& e)
363     {
364       ec = e.code();
365       return 0;
366     }
367 
368     ec = boost::system::error_code();
369     return bytes_transferred;
370   }
371 
372   // Start an asynchronous write.
373   template <typename Stream, typename Const_Buffers, typename Handler>
async_write_some(impl_type & impl,Stream & next_layer,const Const_Buffers & buffers,Handler handler)374   void async_write_some(impl_type& impl, Stream& next_layer,
375       const Const_Buffers& buffers, Handler handler)
376   {
377     typedef io_handler<Stream, Handler> send_handler;
378 
379     boost::asio::const_buffer buffer =
380       boost::asio::detail::buffer_sequence_adapter<
381         boost::asio::const_buffer, Const_Buffers>::first(buffers);
382 
383     std::size_t buffer_size = boost::asio::buffer_size(buffer);
384     if (buffer_size > max_buffer_size)
385       buffer_size = max_buffer_size;
386     else if (buffer_size == 0)
387     {
388       get_io_service().post(boost::asio::detail::bind_handler(
389             handler, boost::system::error_code(), 0));
390       return;
391     }
392 
393     send_handler* local_handler = new send_handler(handler, get_io_service());
394 
395     boost::function<int (SSL*)> send_func =
396       boost::bind(boost::type<int>(), &::SSL_write, boost::arg<1>(),
397           boost::asio::buffer_cast<const void*>(buffer),
398           static_cast<int>(buffer_size));
399 
400     openssl_operation<Stream>* op = new openssl_operation<Stream>
401     (
402       send_func,
403       next_layer,
404       impl->recv_buf,
405       impl->ssl,
406       impl->ext_bio,
407       boost::bind
408       (
409         &base_handler<Stream>::do_func,
410         local_handler,
411         boost::arg<1>(),
412         boost::arg<2>()
413       ),
414       strand_
415     );
416     local_handler->set_operation(op);
417 
418     strand_.post(boost::bind(&openssl_operation<Stream>::start, op));
419   }
420 
421   // Read some data from the stream.
422   template <typename Stream, typename Mutable_Buffers>
read_some(impl_type & impl,Stream & next_layer,const Mutable_Buffers & buffers,boost::system::error_code & ec)423   std::size_t read_some(impl_type& impl, Stream& next_layer,
424       const Mutable_Buffers& buffers, boost::system::error_code& ec)
425   {
426     size_t bytes_transferred = 0;
427     try
428     {
429       boost::asio::mutable_buffer buffer =
430         boost::asio::detail::buffer_sequence_adapter<
431           boost::asio::mutable_buffer, Mutable_Buffers>::first(buffers);
432 
433       std::size_t buffer_size = boost::asio::buffer_size(buffer);
434       if (buffer_size > max_buffer_size)
435         buffer_size = max_buffer_size;
436       else if (buffer_size == 0)
437       {
438         ec = boost::system::error_code();
439         return 0;
440       }
441 
442       boost::function<int (SSL*)> recv_func =
443         boost::bind(boost::type<int>(), &::SSL_read, boost::arg<1>(),
444             boost::asio::buffer_cast<void*>(buffer),
445             static_cast<int>(buffer_size));
446       openssl_operation<Stream> op(recv_func,
447         next_layer,
448         impl->recv_buf,
449         impl->ssl,
450         impl->ext_bio
451       );
452 
453       bytes_transferred = static_cast<size_t>(op.start());
454     }
455     catch (boost::system::system_error& e)
456     {
457       ec = e.code();
458       return 0;
459     }
460 
461     ec = boost::system::error_code();
462     return bytes_transferred;
463   }
464 
465   // Start an asynchronous read.
466   template <typename Stream, typename Mutable_Buffers, typename Handler>
async_read_some(impl_type & impl,Stream & next_layer,const Mutable_Buffers & buffers,Handler handler)467   void async_read_some(impl_type& impl, Stream& next_layer,
468       const Mutable_Buffers& buffers, Handler handler)
469   {
470     typedef io_handler<Stream, Handler> recv_handler;
471 
472     boost::asio::mutable_buffer buffer =
473       boost::asio::detail::buffer_sequence_adapter<
474         boost::asio::mutable_buffer, Mutable_Buffers>::first(buffers);
475 
476     std::size_t buffer_size = boost::asio::buffer_size(buffer);
477     if (buffer_size > max_buffer_size)
478       buffer_size = max_buffer_size;
479     else if (buffer_size == 0)
480     {
481       get_io_service().post(boost::asio::detail::bind_handler(
482             handler, boost::system::error_code(), 0));
483       return;
484     }
485 
486     recv_handler* local_handler = new recv_handler(handler, get_io_service());
487 
488     boost::function<int (SSL*)> recv_func =
489       boost::bind(boost::type<int>(), &::SSL_read, boost::arg<1>(),
490           boost::asio::buffer_cast<void*>(buffer),
491           static_cast<int>(buffer_size));
492 
493     openssl_operation<Stream>* op = new openssl_operation<Stream>
494     (
495       recv_func,
496       next_layer,
497       impl->recv_buf,
498       impl->ssl,
499       impl->ext_bio,
500       boost::bind
501       (
502         &base_handler<Stream>::do_func,
503         local_handler,
504         boost::arg<1>(),
505         boost::arg<2>()
506       ),
507       strand_
508     );
509     local_handler->set_operation(op);
510 
511     strand_.post(boost::bind(&openssl_operation<Stream>::start, op));
512   }
513 
514   // Peek at the incoming data on the stream.
515   template <typename Stream, typename Mutable_Buffers>
peek(impl_type &,Stream &,const Mutable_Buffers &,boost::system::error_code & ec)516   std::size_t peek(impl_type& /*impl*/, Stream& /*next_layer*/,
517       const Mutable_Buffers& /*buffers*/, boost::system::error_code& ec)
518   {
519     ec = boost::system::error_code();
520     return 0;
521   }
522 
523   // Determine the amount of data that may be read without blocking.
524   template <typename Stream>
in_avail(impl_type &,Stream &,boost::system::error_code & ec)525   std::size_t in_avail(impl_type& /*impl*/, Stream& /*next_layer*/,
526       boost::system::error_code& ec)
527   {
528     ec = boost::system::error_code();
529     return 0;
530   }
531 
532 private:
533   boost::asio::io_service::strand strand_;
534 
535   typedef boost::asio::detail::mutex mutex_type;
536 
537   template<typename Mutex>
538   struct ssl_wrap
539   {
540     static Mutex ssl_mutex_;
541 
SSL_acceptboost::asio::ssl::old::detail::openssl_stream_service::ssl_wrap542     static int SSL_accept(SSL *ssl)
543     {
544       typename Mutex::scoped_lock lock(ssl_mutex_);
545       return ::SSL_accept(ssl);
546     }
547 
SSL_connectboost::asio::ssl::old::detail::openssl_stream_service::ssl_wrap548     static int SSL_connect(SSL *ssl)
549     {
550       typename Mutex::scoped_lock lock(ssl_mutex_);
551       return ::SSL_connect(ssl);
552     }
553 
SSL_shutdownboost::asio::ssl::old::detail::openssl_stream_service::ssl_wrap554     static int SSL_shutdown(SSL *ssl)
555     {
556       typename Mutex::scoped_lock lock(ssl_mutex_);
557       return ::SSL_shutdown(ssl);
558     }
559   };
560 };
561 
562 template<typename Mutex>
563 Mutex openssl_stream_service::ssl_wrap<Mutex>::ssl_mutex_;
564 
565 } // namespace detail
566 } // namespace old
567 } // namespace ssl
568 } // namespace asio
569 } // namespace boost
570 
571 #include <boost/asio/detail/pop_options.hpp>
572 
573 #endif // BOOST_ASIO_SSL_OLD_DETAIL_OPENSSL_STREAM_SERVICE_HPP
574