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