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