annotate DEPENDENCIES/generic/include/boost/asio/ssl/old/detail/openssl_operation.hpp @ 133:4acb5d8d80b6 tip

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