]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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-2016 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 | ||
59 | base_handler(boost::asio::io_service& io_service) | |
60 | : op_(NULL) | |
61 | , io_service_(io_service) | |
62 | , work_(io_service) | |
63 | {} | |
64 | ||
65 | void do_func(const boost::system::error_code& error, size_t size) | |
66 | { | |
67 | func_(error, size); | |
68 | } | |
69 | ||
70 | void set_operation(openssl_operation<Stream>* op) { op_ = op; } | |
71 | void set_func(func_t func) { func_ = func; } | |
72 | ||
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: | |
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_; | |
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: | |
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_; | |
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: | |
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_; | |
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. | |
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. | |
175 | void shutdown_service() | |
176 | { | |
177 | } | |
178 | ||
179 | // Return a null stream implementation. | |
180 | impl_type null() const | |
181 | { | |
182 | return 0; | |
183 | } | |
184 | ||
185 | // Create a new stream implementation. | |
186 | template <typename Stream, typename Context_Service> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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 | ||
542 | static int SSL_accept(SSL *ssl) | |
543 | { | |
544 | typename Mutex::scoped_lock lock(ssl_mutex_); | |
545 | return ::SSL_accept(ssl); | |
546 | } | |
547 | ||
548 | static int SSL_connect(SSL *ssl) | |
549 | { | |
550 | typename Mutex::scoped_lock lock(ssl_mutex_); | |
551 | return ::SSL_connect(ssl); | |
552 | } | |
553 | ||
554 | 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 |