/Src/Dependencies/Boost/boost/asio/ssl/old/detail/openssl_operation.hpp

http://hadesmem.googlecode.com/ · C++ Header · 526 lines · 394 code · 63 blank · 69 comment · 57 complexity · 95915edb16a4669344b95b0f218cbbe7 MD5 · raw file

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