1 //
2 // ssl/detail/io.hpp
3 // ~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff 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_DETAIL_IO_HPP
12 #define BOOST_ASIO_SSL_DETAIL_IO_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 
20 #include <boost/asio/detail/base_from_cancellation_state.hpp>
21 #include <boost/asio/detail/handler_tracking.hpp>
22 #include <boost/asio/ssl/detail/engine.hpp>
23 #include <boost/asio/ssl/detail/stream_core.hpp>
24 #include <boost/asio/write.hpp>
25 
26 #include <boost/asio/detail/push_options.hpp>
27 
28 namespace boost {
29 namespace asio {
30 namespace ssl {
31 namespace detail {
32 
33 template <typename Stream, typename Operation>
io(Stream & next_layer,stream_core & core,const Operation & op,boost::system::error_code & ec)34 std::size_t io(Stream& next_layer, stream_core& core,
35     const Operation& op, boost::system::error_code& ec)
36 {
37   boost::system::error_code io_ec;
38   std::size_t bytes_transferred = 0;
39   do switch (op(core.engine_, ec, bytes_transferred))
40   {
41   case engine::want_input_and_retry:
42 
43     // If the input buffer is empty then we need to read some more data from
44     // the underlying transport.
45     if (core.input_.size() == 0)
46     {
47       core.input_ = boost::asio::buffer(core.input_buffer_,
48           next_layer.read_some(core.input_buffer_, io_ec));
49       if (!ec)
50         ec = io_ec;
51     }
52 
53     // Pass the new input data to the engine.
54     core.input_ = core.engine_.put_input(core.input_);
55 
56     // Try the operation again.
57     continue;
58 
59   case engine::want_output_and_retry:
60 
61     // Get output data from the engine and write it to the underlying
62     // transport.
63     boost::asio::write(next_layer,
64         core.engine_.get_output(core.output_buffer_), io_ec);
65     if (!ec)
66       ec = io_ec;
67 
68     // Try the operation again.
69     continue;
70 
71   case engine::want_output:
72 
73     // Get output data from the engine and write it to the underlying
74     // transport.
75     boost::asio::write(next_layer,
76         core.engine_.get_output(core.output_buffer_), io_ec);
77     if (!ec)
78       ec = io_ec;
79 
80     // Operation is complete. Return result to caller.
81     core.engine_.map_error_code(ec);
82     return bytes_transferred;
83 
84   default:
85 
86     // Operation is complete. Return result to caller.
87     core.engine_.map_error_code(ec);
88     return bytes_transferred;
89 
90   } while (!ec);
91 
92   // Operation failed. Return result to caller.
93   core.engine_.map_error_code(ec);
94   return 0;
95 }
96 
97 template <typename Stream, typename Operation, typename Handler>
98 class io_op
99   : public boost::asio::detail::base_from_cancellation_state<Handler>
100 {
101 public:
io_op(Stream & next_layer,stream_core & core,const Operation & op,Handler & handler)102   io_op(Stream& next_layer, stream_core& core,
103       const Operation& op, Handler& handler)
104     : boost::asio::detail::base_from_cancellation_state<Handler>(handler),
105       next_layer_(next_layer),
106       core_(core),
107       op_(op),
108       start_(0),
109       want_(engine::want_nothing),
110       bytes_transferred_(0),
111       handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler))
112   {
113   }
114 
115 #if defined(BOOST_ASIO_HAS_MOVE)
io_op(const io_op & other)116   io_op(const io_op& other)
117     : boost::asio::detail::base_from_cancellation_state<Handler>(other),
118       next_layer_(other.next_layer_),
119       core_(other.core_),
120       op_(other.op_),
121       start_(other.start_),
122       want_(other.want_),
123       ec_(other.ec_),
124       bytes_transferred_(other.bytes_transferred_),
125       handler_(other.handler_)
126   {
127   }
128 
io_op(io_op && other)129   io_op(io_op&& other)
130     : boost::asio::detail::base_from_cancellation_state<Handler>(
131         BOOST_ASIO_MOVE_CAST(
132           boost::asio::detail::base_from_cancellation_state<Handler>)(
133             other)),
134       next_layer_(other.next_layer_),
135       core_(other.core_),
136       op_(BOOST_ASIO_MOVE_CAST(Operation)(other.op_)),
137       start_(other.start_),
138       want_(other.want_),
139       ec_(other.ec_),
140       bytes_transferred_(other.bytes_transferred_),
141       handler_(BOOST_ASIO_MOVE_CAST(Handler)(other.handler_))
142   {
143   }
144 #endif // defined(BOOST_ASIO_HAS_MOVE)
145 
operator ()(boost::system::error_code ec,std::size_t bytes_transferred=~std::size_t (0),int start=0)146   void operator()(boost::system::error_code ec,
147       std::size_t bytes_transferred = ~std::size_t(0), int start = 0)
148   {
149     switch (start_ = start)
150     {
151     case 1: // Called after at least one async operation.
152       do
153       {
154         switch (want_ = op_(core_.engine_, ec_, bytes_transferred_))
155         {
156         case engine::want_input_and_retry:
157 
158           // If the input buffer already has data in it we can pass it to the
159           // engine and then retry the operation immediately.
160           if (core_.input_.size() != 0)
161           {
162             core_.input_ = core_.engine_.put_input(core_.input_);
163             continue;
164           }
165 
166           // The engine wants more data to be read from input. However, we
167           // cannot allow more than one read operation at a time on the
168           // underlying transport. The pending_read_ timer's expiry is set to
169           // pos_infin if a read is in progress, and neg_infin otherwise.
170           if (core_.expiry(core_.pending_read_) == core_.neg_infin())
171           {
172             // Prevent other read operations from being started.
173             core_.pending_read_.expires_at(core_.pos_infin());
174 
175             BOOST_ASIO_HANDLER_LOCATION((
176                   __FILE__, __LINE__, Operation::tracking_name()));
177 
178             // Start reading some data from the underlying transport.
179             next_layer_.async_read_some(
180                 boost::asio::buffer(core_.input_buffer_),
181                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
182           }
183           else
184           {
185             BOOST_ASIO_HANDLER_LOCATION((
186                   __FILE__, __LINE__, Operation::tracking_name()));
187 
188             // Wait until the current read operation completes.
189             core_.pending_read_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
190           }
191 
192           // Yield control until asynchronous operation completes. Control
193           // resumes at the "default:" label below.
194           return;
195 
196         case engine::want_output_and_retry:
197         case engine::want_output:
198 
199           // The engine wants some data to be written to the output. However, we
200           // cannot allow more than one write operation at a time on the
201           // underlying transport. The pending_write_ timer's expiry is set to
202           // pos_infin if a write is in progress, and neg_infin otherwise.
203           if (core_.expiry(core_.pending_write_) == core_.neg_infin())
204           {
205             // Prevent other write operations from being started.
206             core_.pending_write_.expires_at(core_.pos_infin());
207 
208             BOOST_ASIO_HANDLER_LOCATION((
209                   __FILE__, __LINE__, Operation::tracking_name()));
210 
211             // Start writing all the data to the underlying transport.
212             boost::asio::async_write(next_layer_,
213                 core_.engine_.get_output(core_.output_buffer_),
214                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
215           }
216           else
217           {
218             BOOST_ASIO_HANDLER_LOCATION((
219                   __FILE__, __LINE__, Operation::tracking_name()));
220 
221             // Wait until the current write operation completes.
222             core_.pending_write_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
223           }
224 
225           // Yield control until asynchronous operation completes. Control
226           // resumes at the "default:" label below.
227           return;
228 
229         default:
230 
231           // The SSL operation is done and we can invoke the handler, but we
232           // have to keep in mind that this function might be being called from
233           // the async operation's initiating function. In this case we're not
234           // allowed to call the handler directly. Instead, issue a zero-sized
235           // read so the handler runs "as-if" posted using io_context::post().
236           if (start)
237           {
238             BOOST_ASIO_HANDLER_LOCATION((
239                   __FILE__, __LINE__, Operation::tracking_name()));
240 
241             next_layer_.async_read_some(
242                 boost::asio::buffer(core_.input_buffer_, 0),
243                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
244 
245             // Yield control until asynchronous operation completes. Control
246             // resumes at the "default:" label below.
247             return;
248           }
249           else
250           {
251             // Continue on to run handler directly.
252             break;
253           }
254         }
255 
256         default:
257         if (bytes_transferred == ~std::size_t(0))
258           bytes_transferred = 0; // Timer cancellation, no data transferred.
259         else if (!ec_)
260           ec_ = ec;
261 
262         switch (want_)
263         {
264         case engine::want_input_and_retry:
265 
266           // Add received data to the engine's input.
267           core_.input_ = boost::asio::buffer(
268               core_.input_buffer_, bytes_transferred);
269           core_.input_ = core_.engine_.put_input(core_.input_);
270 
271           // Release any waiting read operations.
272           core_.pending_read_.expires_at(core_.neg_infin());
273 
274           // Check for cancellation before continuing.
275           if (this->cancelled() != cancellation_type::none)
276           {
277             ec_ = boost::asio::error::operation_aborted;
278             break;
279           }
280 
281           // Try the operation again.
282           continue;
283 
284         case engine::want_output_and_retry:
285 
286           // Release any waiting write operations.
287           core_.pending_write_.expires_at(core_.neg_infin());
288 
289           // Check for cancellation before continuing.
290           if (this->cancelled() != cancellation_type::none)
291           {
292             ec_ = boost::asio::error::operation_aborted;
293             break;
294           }
295 
296           // Try the operation again.
297           continue;
298 
299         case engine::want_output:
300 
301           // Release any waiting write operations.
302           core_.pending_write_.expires_at(core_.neg_infin());
303 
304           // Fall through to call handler.
305 
306         default:
307 
308           // Pass the result to the handler.
309           op_.call_handler(handler_,
310               core_.engine_.map_error_code(ec_),
311               ec_ ? 0 : bytes_transferred_);
312 
313           // Our work here is done.
314           return;
315         }
316       } while (!ec_);
317 
318       // Operation failed. Pass the result to the handler.
319       op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0);
320     }
321   }
322 
323 //private:
324   Stream& next_layer_;
325   stream_core& core_;
326   Operation op_;
327   int start_;
328   engine::want want_;
329   boost::system::error_code ec_;
330   std::size_t bytes_transferred_;
331   Handler handler_;
332 };
333 
334 template <typename Stream, typename Operation, typename Handler>
335 inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,io_op<Stream,Operation,Handler> * this_handler)336 asio_handler_allocate(std::size_t size,
337     io_op<Stream, Operation, Handler>* this_handler)
338 {
339 #if defined(BOOST_ASIO_NO_DEPRECATED)
340   boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
341   return asio_handler_allocate_is_no_longer_used();
342 #else // defined(BOOST_ASIO_NO_DEPRECATED)
343   return boost_asio_handler_alloc_helpers::allocate(
344       size, this_handler->handler_);
345 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
346 }
347 
348 template <typename Stream, typename Operation, typename Handler>
349 inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void * pointer,std::size_t size,io_op<Stream,Operation,Handler> * this_handler)350 asio_handler_deallocate(void* pointer, std::size_t size,
351     io_op<Stream, Operation, Handler>* this_handler)
352 {
353   boost_asio_handler_alloc_helpers::deallocate(
354       pointer, size, this_handler->handler_);
355 #if defined(BOOST_ASIO_NO_DEPRECATED)
356   return asio_handler_deallocate_is_no_longer_used();
357 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
358 }
359 
360 template <typename Stream, typename Operation, typename Handler>
asio_handler_is_continuation(io_op<Stream,Operation,Handler> * this_handler)361 inline bool asio_handler_is_continuation(
362     io_op<Stream, Operation, Handler>* this_handler)
363 {
364   return this_handler->start_ == 0 ? true
365     : boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_);
366 }
367 
368 template <typename Function, typename Stream,
369     typename Operation, typename Handler>
370 inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function & function,io_op<Stream,Operation,Handler> * this_handler)371 asio_handler_invoke(Function& function,
372     io_op<Stream, Operation, Handler>* this_handler)
373 {
374   boost_asio_handler_invoke_helpers::invoke(
375       function, this_handler->handler_);
376 #if defined(BOOST_ASIO_NO_DEPRECATED)
377   return asio_handler_invoke_is_no_longer_used();
378 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
379 }
380 
381 template <typename Function, typename Stream,
382     typename Operation, typename Handler>
383 inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function & function,io_op<Stream,Operation,Handler> * this_handler)384 asio_handler_invoke(const Function& function,
385     io_op<Stream, Operation, Handler>* this_handler)
386 {
387   boost_asio_handler_invoke_helpers::invoke(
388       function, this_handler->handler_);
389 #if defined(BOOST_ASIO_NO_DEPRECATED)
390   return asio_handler_invoke_is_no_longer_used();
391 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
392 }
393 
394 template <typename Stream, typename Operation, typename Handler>
async_io(Stream & next_layer,stream_core & core,const Operation & op,Handler & handler)395 inline void async_io(Stream& next_layer, stream_core& core,
396     const Operation& op, Handler& handler)
397 {
398   io_op<Stream, Operation, Handler>(
399     next_layer, core, op, handler)(
400       boost::system::error_code(), 0, 1);
401 }
402 
403 } // namespace detail
404 } // namespace ssl
405 
406 template <template <typename, typename> class Associator,
407     typename Stream, typename Operation,
408     typename Handler, typename DefaultCandidate>
409 struct associator<Associator,
410     ssl::detail::io_op<Stream, Operation, Handler>,
411     DefaultCandidate>
412   : Associator<Handler, DefaultCandidate>
413 {
getboost::asio::associator414   static typename Associator<Handler, DefaultCandidate>::type get(
415       const ssl::detail::io_op<Stream, Operation, Handler>& h,
416       const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
417   {
418     return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
419   }
420 };
421 
422 } // namespace asio
423 } // namespace boost
424 
425 #include <boost/asio/detail/pop_options.hpp>
426 
427 #endif // BOOST_ASIO_SSL_DETAIL_IO_HPP
428