]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/asio/include/boost/asio/ssl/old/detail/openssl_operation.hpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / asio / include / boost / asio / ssl / old / detail / openssl_operation.hpp
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:
55 net_buffer()
56 {
57 data_start_ = data_end_ = buf_;
58 }
59 unsigned char* get_unused_start() { return data_end_; }
60 unsigned char* get_data_start() { return data_start_; }
61 size_t get_unused_len() { return (NET_BUF_SIZE - (data_end_ - buf_)); }
62 size_t get_data_len() { return (data_end_ - data_start_); }
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 }
70 void data_removed(size_t count)
71 {
72 data_start_ += count;
73 if (data_start_ >= data_end_) reset();
74 }
75 void reset() { data_start_ = buf_; data_end_ = buf_; }
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
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
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...
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 //
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
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
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
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
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
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...
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
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