Monero
Loading...
Searching...
No Matches
net_helper.h
Go to the documentation of this file.
1// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above copyright
9// notice, this list of conditions and the following disclaimer in the
10// documentation and/or other materials provided with the distribution.
11// * Neither the name of the Andrey N. Sabelnikov nor the
12// names of its contributors may be used to endorse or promote products
13// derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
19// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25//
26
27
28
29
30#pragma once
31
32//#include <Winsock2.h>
33//#include <Ws2tcpip.h>
34#include <atomic>
35#include <memory>
36#include <string>
37#include <boost/version.hpp>
38#include <boost/asio/io_context.hpp>
39#include <boost/asio/ip/tcp.hpp>
40#include <boost/asio/post.hpp>
41#include <boost/asio/read.hpp>
42#include <boost/asio/ssl.hpp>
43#include <boost/asio/steady_timer.hpp>
44#include <boost/thread/future.hpp>
45#include <boost/lambda/bind.hpp>
46#include <boost/lambda/lambda.hpp>
47#include <boost/system/error_code.hpp>
48#include <boost/utility/string_ref.hpp>
49#include <functional>
50#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
51#include <boost/asio/posix/stream_descriptor.hpp>
52#include <cerrno>
53#include <fcntl.h>
54#include <stdexcept>
55#include <unistd.h>
56#endif
57#include "net/net_utils_base.h"
58#include "net/net_ssl.h"
59#include "misc_language.h"
60
61#undef MONERO_DEFAULT_LOG_CATEGORY
62#define MONERO_DEFAULT_LOG_CATEGORY "net"
63
64#ifndef MAKE_IP
65#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
66#endif
67
68
69namespace epee
70{
71namespace net_utils
72{
74 {
75 boost::unique_future<boost::asio::ip::tcp::socket>
76 operator()(const std::string& addr, const std::string& port, boost::asio::steady_timer&) const;
77 };
78
79
81 {
88
89
90
92 {
93 handler_obj(boost::system::error_code& error, size_t& bytes_transferred):ref_error(error), ref_bytes_transferred(bytes_transferred)
94 {}
97
98 boost::system::error_code& ref_error;
100
101 void operator()(const boost::system::error_code& error, // Result of operation.
102 std::size_t bytes_transferred // Number of bytes read.
103 )
104 {
105 ref_error = error;
106 ref_bytes_transferred = bytes_transferred;
107 }
108 };
109
110 public:
111 inline
113 m_io_service(),
114 m_ctx(boost::asio::ssl::context::tlsv12),
115 m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx)),
120 m_deadline(m_io_service, std::chrono::steady_clock::time_point::max()),
121#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
122 m_wake_reader(m_io_service),
123 m_wake_writer(-1),
124#endif
126 m_bytes_sent(0),
128 {
130#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
131 // self-pipe so shutdown() can wake a blocked run_one() with an async-signal-safe
132 // write(); without it aborts would wait out the op deadline, so refuse to construct
133 int fds[2];
134 if (0 != ::pipe(fds))
135 throw std::runtime_error("failed to create shutdown wake pipe");
136 bool flags_set = true;
137 for (const int fd : fds)
138 {
139 if (-1 == ::fcntl(fd, F_SETFL, O_NONBLOCK) || -1 == ::fcntl(fd, F_SETFD, FD_CLOEXEC))
140 flags_set = false;
141 }
142 boost::system::error_code ec;
143 if (flags_set)
144 m_wake_reader.assign(fds[0], ec);
145 if (!flags_set || ec)
146 {
147 ::close(fds[0]);
148 ::close(fds[1]);
149 throw std::runtime_error("failed to set up shutdown wake pipe");
150 }
151 m_wake_writer = fds[1];
152 wait_wake();
153#endif
154 }
155
170 using connect_func = boost::unique_future<boost::asio::ip::tcp::socket>(const std::string&, const std::string&, boost::asio::steady_timer&);
171
172 inline
174 {
175 //profile_tools::local_coast lc("~blocked_mode_client()", 3);
176 try { disconnect(); }
177 catch(...) { /* ignore */ }
178#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
179 if (m_wake_writer != -1)
180 ::close(m_wake_writer); // read end is owned and closed by m_wake_reader
181#endif
182 }
183
184 inline void set_ssl(ssl_options_t ssl_options)
185 {
186 if (ssl_options)
187 m_ctx = ssl_options.create_context();
188 else
189 m_ctx = boost::asio::ssl::context(boost::asio::ssl::context::tlsv12);
190 m_ssl_options = std::move(ssl_options);
191 }
192
193 inline
194 bool connect(const std::string& addr, int port, std::chrono::milliseconds timeout)
195 {
196 return connect(addr, std::to_string(port), timeout);
197 }
198
199 inline
200 try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
201 {
202 const auto deadline = std::chrono::steady_clock::now() + timeout;
203 m_deadline.expires_after(timeout);
204 boost::unique_future<boost::asio::ip::tcp::socket> connection = m_connector(addr, port, m_deadline);
205 for (;;)
206 {
207 // aborted or timed out: abandon the attempt; its handlers own their state
208 // and their socket dies with them, at completion or client destruction
209 if (shutdown_requested() || std::chrono::steady_clock::now() >= deadline)
210 return CONNECT_FAILURE;
211
212 m_io_service.restart();
213 m_io_service.run_one();
214
215 if (connection.is_ready())
216 break;
217 }
218
219 m_ssl_socket->next_layer() = connection.get();
220 m_deadline.cancel();
221 if (shutdown_requested())
222 {
223 boost::system::error_code ignored_ec;
224 m_ssl_socket->next_layer().close(ignored_ec);
225 return CONNECT_FAILURE; // aborted by shutdown() from another thread
226 }
227 if (m_ssl_socket->next_layer().is_open())
228 {
229 m_connected = true;
230 m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
231 // SSL Options
233 {
234 if (!m_ssl_options.handshake(m_io_service, *m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout, [this] { return shutdown_requested(); }))
235 {
236 if (shutdown_requested())
237 {
238 m_connected = false;
239 return CONNECT_FAILURE; // aborted: says nothing about the server's TLS support
240 }
242 {
243 boost::system::error_code ignored_ec;
244 m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
245 m_ssl_socket->next_layer().close();
246 m_connected = false;
247 return CONNECT_NO_SSL;
248 }
249 else
250 {
251 MWARNING("Failed to establish SSL connection");
252 m_connected = false;
253 return CONNECT_FAILURE;
254 }
255 }
256 }
257 // recheck after publishing m_connected: shutdown() between the check above
258 // and the publish must not leave a live connection behind
259 if (shutdown_requested())
260 {
261 m_connected = false;
262 boost::system::error_code ignored_ec;
263 m_ssl_socket->next_layer().close(ignored_ec);
264 return CONNECT_FAILURE;
265 }
266 return CONNECT_SUCCESS;
267 }else
268 {
269 MWARNING("Some problems at connect, expected open socket");
270 return CONNECT_FAILURE;
271 }
272
273 }
274
275 inline
276 bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
277 {
278 if (shutdown_requested())
279 return false; // torn down: the client never reconnects after shutdown()
280 m_connected = false;
281 try
282 {
283 m_ssl_socket->next_layer().close();
284 m_pending_read.reset();
285
286 // Set SSL options
287 // disable sslv2
288 m_ssl_socket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx));
289
290 // Get a list of endpoints corresponding to the server name.
291
292 try_connect_result_t try_connect_result = try_connect(addr, port, timeout);
293 if (try_connect_result == CONNECT_FAILURE)
294 return false;
296 {
297 if (try_connect_result == CONNECT_NO_SSL)
298 {
299 MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
301 if (try_connect(addr, port, timeout) != CONNECT_SUCCESS)
302 return false;
303 }
304 }
305 }
306 catch(const boost::system::system_error& er)
307 {
308 MDEBUG("Some problems at connect, message: " << er.what());
309 return false;
310 }
311 catch(...)
312 {
313 MDEBUG("Some fatal problems.");
314 return false;
315 }
316
317 return true;
318 }
319
320 void set_connector(std::function<connect_func> connector)
321 {
322 m_connector = std::move(connector);
323 }
324
325 inline
327 {
328 m_connected = false;
329 try
330 {
331 if(!shutdown_requested() && m_ssl_socket->next_layer().is_open())
332 {
333 if(m_ssl_options)
334 shutdown_ssl();
335 // The peer or the TLS shutdown deadline may already have closed the connection.
336 boost::system::error_code ignored_ec;
337 m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
338 // shutdown() alone leaves is_open() true and repeats TLS shutdown on later calls.
339 m_ssl_socket->next_layer().close();
340 }
341 }
342 catch(const boost::system::system_error& /*er*/)
343 {
344 //LOG_ERROR("Some problems at disconnect, message: " << er.what());
345 return false;
346 }
347 catch(...)
348 {
349 //LOG_ERROR("Some fatal problems.");
350 return false;
351 }
352 return true;
353 }
354
355
356 inline
357 bool send(const boost::string_ref buff, std::chrono::milliseconds timeout)
358 {
359 try
360 {
361 m_deadline.expires_after(timeout);
362
363 // Set up the variable that receives the result of the asynchronous
364 // operation. The error code is set to would_block to signal that the
365 // operation is incomplete. Asio guarantees that its asynchronous
366 // operations will never fail with would_block, so any other value in
367 // ec indicates completion.
368 boost::system::error_code ec = boost::asio::error::would_block;
369
370 // Start the asynchronous operation itself. The boost::lambda function
371 // object is used as a callback and will update the ec variable when the
372 // operation completes. The blocking_udp_client.cpp example shows how you
373 // can use boost::bind rather than boost::lambda.
374 async_write(buff.data(), buff.size(), ec);
375
376 // Block until the asynchronous operation has completed.
377 while (ec == boost::asio::error::would_block && !shutdown_requested())
378 {
379 m_io_service.restart();
380 m_io_service.run_one();
381 }
382
383 // aborted by shutdown(): the pending handler references this frame, so
384 // close the socket and pump until it completes before returning
385 if (ec == boost::asio::error::would_block)
386 {
387 boost::system::error_code ignored_ec;
388 m_ssl_socket->next_layer().close(ignored_ec);
389 while (ec == boost::asio::error::would_block)
390 {
391 m_io_service.restart();
392 m_io_service.run_one();
393 }
394 }
395
396 if (ec)
397 {
398 LOG_PRINT_L3("Problems at write: " << ec.message());
399 m_connected = false;
400 return false;
401 }else
402 {
403 m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
404 m_bytes_sent += buff.size();
405 }
406 }
407
408 catch(const boost::system::system_error& er)
409 {
410 LOG_ERROR("Some problems at connect, message: " << er.what());
411 return false;
412 }
413 catch(...)
414 {
415 LOG_ERROR("Some fatal problems.");
416 return false;
417 }
418
419 return true;
420 }
421
422 inline
423 bool send(const void* data, size_t sz)
424 {
425 try
426 {
427 /*
428 m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
429
430 // Set up the variable that receives the result of the asynchronous
431 // operation. The error code is set to would_block to signal that the
432 // operation is incomplete. Asio guarantees that its asynchronous
433 // operations will never fail with would_block, so any other value in
434 // ec indicates completion.
435 boost::system::error_code ec = boost::asio::error::would_block;
436
437 // Start the asynchronous operation itself. The boost::lambda function
438 // object is used as a callback and will update the ec variable when the
439 // operation completes. The blocking_udp_client.cpp example shows how you
440 // can use boost::bind rather than boost::lambda.
441 boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
442
443 // Block until the asynchronous operation has completed.
444 while (ec == boost::asio::error::would_block)
445 {
446 m_io_service.run_one();
447 }
448 */
449 boost::system::error_code ec;
450
451 size_t writen = write(data, sz, ec);
452
453 if (!writen || ec)
454 {
455 LOG_PRINT_L3("Problems at write: " << ec.message());
456 m_connected = false;
457 return false;
458 }else
459 {
460 m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
461 m_bytes_sent += sz;
462 }
463 }
464
465 catch(const boost::system::system_error& er)
466 {
467 LOG_ERROR("Some problems at send, message: " << er.what());
468 m_connected = false;
469 return false;
470 }
471 catch(...)
472 {
473 LOG_ERROR("Some fatal problems.");
474 return false;
475 }
476
477 return true;
478 }
479
480 bool is_connected(bool *ssl = NULL)
481 {
482 if (!m_connected || !m_ssl_socket->next_layer().is_open())
483 return false;
484
485 boost::system::error_code ec;
486 if (m_ssl_options)
487 {
488 // let Asio process TLS alerts and retain a byte of application data for recv
489 if (!m_pending_read)
490 {
491 const auto pending = std::make_shared<pending_read>();
492 // reconnect may replace the stream before this handler completes
493 const auto socket = m_ssl_socket;
494 socket->async_read_some(boost::asio::buffer(&pending->byte, 1),
495 [socket, pending](const boost::system::error_code& error, size_t) {
496 pending->error = error;
497 if (pending->handler)
498 {
499 auto handler = std::move(pending->handler);
500 handler(error, pending->byte);
501 }
502 });
503 m_pending_read = pending;
504 }
505 m_io_service.restart();
506 m_io_service.poll();
507 ec = m_pending_read->error;
508 }
509 else
510 {
511 // peek for EOF without consuming application data or changing the socket mode
512 auto& socket = m_ssl_socket->next_layer();
513 const bool non_blocking = socket.non_blocking();
514 socket.non_blocking(true, ec);
515 if (!ec)
516 {
517 char byte;
518 socket.receive(boost::asio::buffer(&byte, 1), boost::asio::ip::tcp::socket::message_peek, ec);
519 boost::system::error_code restore_error;
520 socket.non_blocking(non_blocking, restore_error);
521 if (restore_error)
522 {
523 m_connected = false;
524 return false;
525 }
526 }
527 }
528 if (ec && ec != boost::asio::error::would_block &&
529 ec != boost::asio::error::try_again && ec != boost::asio::error::interrupted)
530 {
531 MDEBUG("Peer closed idle connection, marking disconnected: " << ec.message());
532 m_connected = false;
533 return false;
534 }
535
536 if (ssl)
538 return m_connected;
539 }
540
541 inline
542 bool recv(std::string& buff, std::chrono::milliseconds timeout)
543 {
544 try
545 {
546 // Set a deadline for the asynchronous operation. Since this function uses
547 // a composed operation (async_read_until), the deadline applies to the
548 // entire operation, rather than individual reads from the socket.
549 m_deadline.expires_after(timeout);
550
551 // Set up the variable that receives the result of the asynchronous
552 // operation. The error code is set to would_block to signal that the
553 // operation is incomplete. Asio guarantees that its asynchronous
554 // operations will never fail with would_block, so any other value in
555 // ec indicates completion.
556 //boost::system::error_code ec = boost::asio::error::would_block;
557
558 // Start the asynchronous operation itself. The boost::lambda function
559 // object is used as a callback and will update the ec variable when the
560 // operation completes. The blocking_udp_client.cpp example shows how you
561 // can use boost::bind rather than boost::lambda.
562
563 boost::system::error_code ec = boost::asio::error::would_block;
564 size_t bytes_transfered = 0;
565
566 handler_obj hndlr(ec, bytes_transfered);
567
568 static const size_t max_size = 16384;
569 buff.resize(max_size);
570
571 async_read(&buff[0], max_size, boost::asio::transfer_at_least(1), hndlr);
572
573 // Block until the asynchronous operation has completed.
574 while (ec == boost::asio::error::would_block && !shutdown_requested())
575 {
576 m_io_service.restart();
577 m_io_service.run_one();
578 }
579
580 // aborted by shutdown(): the pending handler references this frame, so
581 // close the socket and pump until it completes before returning
582 if (ec == boost::asio::error::would_block)
583 {
584 boost::system::error_code ignored_ec;
585 m_ssl_socket->next_layer().close(ignored_ec);
586 while (ec == boost::asio::error::would_block)
587 {
588 m_io_service.restart();
589 m_io_service.run_one();
590 }
591 }
592
593 if (ec)
594 {
595 m_connected = false;
596 MTRACE("READ ENDS: Connection err_code " << ec.value());
597 if(ec == boost::asio::error::eof)
598 {
599 MTRACE("Connection err_code eof.");
600 //connection closed there, empty
601 buff.clear();
602 return true;
603 }
604
605 MDEBUG("Problems at read: " << ec.message());
606 return false;
607 }else
608 {
609 MTRACE("READ ENDS: Success. bytes_tr: " << bytes_transfered);
610 m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
611 }
612
613 /*if(!bytes_transfered)
614 return false;*/
615
616 m_bytes_received += bytes_transfered;
617 buff.resize(bytes_transfered);
618 return true;
619 }
620
621 catch(const boost::system::system_error& er)
622 {
623 LOG_ERROR("Some problems at read, message: " << er.what());
624 m_connected = false;
625 return false;
626 }
627 catch(...)
628 {
629 LOG_ERROR("Some fatal problems at read.");
630 return false;
631 }
632
633
634
635 return false;
636
637 }
638
639 inline bool recv_n(std::string& buff, int64_t sz, std::chrono::milliseconds timeout)
640 {
641
642 try
643 {
644 // Set a deadline for the asynchronous operation. Since this function uses
645 // a composed operation (async_read_until), the deadline applies to the
646 // entire operation, rather than individual reads from the socket.
647 m_deadline.expires_after(timeout);
648
649 // Set up the variable that receives the result of the asynchronous
650 // operation. The error code is set to would_block to signal that the
651 // operation is incomplete. Asio guarantees that its asynchronous
652 // operations will never fail with would_block, so any other value in
653 // ec indicates completion.
654 //boost::system::error_code ec = boost::asio::error::would_block;
655
656 // Start the asynchronous operation itself. The boost::lambda function
657 // object is used as a callback and will update the ec variable when the
658 // operation completes. The blocking_udp_client.cpp example shows how you
659 // can use boost::bind rather than boost::lambda.
660
661 buff.resize(static_cast<size_t>(sz));
662 boost::system::error_code ec = boost::asio::error::would_block;
663 size_t bytes_transfered = 0;
664
665
666 handler_obj hndlr(ec, bytes_transfered);
667 async_read((char*)buff.data(), buff.size(), boost::asio::transfer_at_least(buff.size()), hndlr);
668
669 // Block until the asynchronous operation has completed.
670 while (ec == boost::asio::error::would_block && !shutdown_requested())
671 {
672 m_io_service.run_one();
673 }
674
675 // aborted by shutdown(): the pending handler references this frame, so
676 // close the socket and pump until it completes before returning
677 if (ec == boost::asio::error::would_block)
678 {
679 boost::system::error_code ignored_ec;
680 m_ssl_socket->next_layer().close(ignored_ec);
681 while (ec == boost::asio::error::would_block)
682 {
683 m_io_service.restart();
684 m_io_service.run_one();
685 }
686 }
687
688 if (ec)
689 {
690 LOG_PRINT_L3("Problems at read: " << ec.message());
691 m_connected = false;
692 return false;
693 }else
694 {
695 m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
696 }
697
698 m_bytes_received += bytes_transfered;
699 if(bytes_transfered != buff.size())
700 {
701 LOG_ERROR("Transferred mismatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size());
702 return false;
703 }
704
705 return true;
706 }
707
708 catch(const boost::system::system_error& er)
709 {
710 LOG_ERROR("Some problems at read, message: " << er.what());
711 m_connected = false;
712 return false;
713 }
714 catch(...)
715 {
716 LOG_ERROR("Some fatal problems at read.");
717 return false;
718 }
719
720
721
722 return false;
723 }
724
725 bool shutdown()
726 {
727 // callable from any thread, on POSIX even a signal handler: only atomics and a self-pipe
728 // write(); a blocked call closes the socket on abort, an idle client's closes at destruction
729 m_aborted = true;
730 m_connected = false;
731#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
732 // construction guarantees the pipe exists
733 const int saved_errno = errno;
734 const char wake = 0;
735 while (::write(m_wake_writer, &wake, 1) == -1 && errno == EINTR) {}
736 errno = saved_errno; // a full pipe already holds a pending wake
737#else
738 // Windows console handlers run on their own thread: post() a wake
739 boost::asio::post(m_io_service, []{});
740#endif
741 return true;
742 }
743
744 boost::asio::io_context& get_io_service()
745 {
746 return m_io_service;
747 }
748
749 boost::asio::ip::tcp::socket& get_socket()
750 {
751 return m_ssl_socket->next_layer();
752 }
753
755 {
756 return m_bytes_sent;
757 }
758
760 {
761 return m_bytes_received;
762 }
763
764 private:
765
767 {
768 char byte = 0;
769 boost::system::error_code error = boost::asio::error::would_block;
770 std::function<void(const boost::system::error_code&, char)> handler;
771 };
772
774 {
775 return m_aborted.load();
776 }
777
778#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
779 // perpetual pipe read so a wake byte written by shutdown() unblocks run_one()
780 void wait_wake()
781 {
782 m_wake_reader.async_read_some(boost::asio::buffer(m_wake_buf),
783 [this] (const boost::system::error_code& ec, std::size_t) { if (!ec) wait_wake(); });
784 }
785#endif
786
788 {
789 // Check whether the deadline has passed. We compare the deadline against
790 // the current time since a new asynchronous operation may have moved the
791 // deadline before this actor had a chance to run.
792 if (m_deadline.expiry() <= std::chrono::steady_clock::now())
793 {
794 // The deadline has passed. The socket is closed so that any outstanding
795 // asynchronous operations are cancelled. This allows the blocked
796 // connect(), read_line() or write_line() functions to return.
797 LOG_PRINT_L3("Timed out socket");
798 m_connected = false;
799 m_ssl_socket->next_layer().close();
800
801 // There is no longer an active deadline. The expiry is set to positive
802 // infinity so that the actor takes no action until a new deadline is set.
803 m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
804 }
805
806 // Put the actor back to sleep.
807 m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this));
808 }
809
811 // ssl socket shutdown blocks if server doesn't respond. We close after 2 secs
812 boost::system::error_code ec = boost::asio::error::would_block;
813 m_deadline.expires_after(std::chrono::milliseconds(2000));
814 m_ssl_socket->async_shutdown(boost::lambda::var(ec) = boost::lambda::_1);
815 while (ec == boost::asio::error::would_block)
816 {
817 m_io_service.restart();
818 m_io_service.run_one();
819 }
820 // Ignore "short read" error
821 if (ec.category() == boost::asio::error::get_ssl_category() &&
822 ec.value() !=
823#if BOOST_VERSION >= 106200
824 boost::asio::ssl::error::stream_truncated
825#else // older Boost supports only OpenSSL 1.0, so 1.0-only macros are appropriate
826 ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)
827#endif
828 )
829 MDEBUG("Problems at ssl shutdown: " << ec.message());
830 }
831
832 protected:
833 bool write(const void* data, size_t sz, boost::system::error_code& ec)
834 {
835 bool success;
837 success = boost::asio::write(*m_ssl_socket, boost::asio::buffer(data, sz), ec);
838 else
839 success = boost::asio::write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), ec);
840 return success;
841 }
842
843 void async_write(const void* data, size_t sz, boost::system::error_code& ec)
844 {
846 boost::asio::async_write(*m_ssl_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
847 else
848 boost::asio::async_write(m_ssl_socket->next_layer(), boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
849 }
850
851 void async_read(char* buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj& hndlr)
852 {
853 // finish the probe before starting another TLS read
854 if (sz && m_pending_read)
855 {
856 const auto pending = m_pending_read;
857 auto on_read = [socket = m_ssl_socket, buff, sz, transfer_at_least, hndlr]
858 (const boost::system::error_code& error, char byte) mutable {
859 if (error)
860 {
861 hndlr(error, 0);
862 return;
863 }
864 buff[0] = byte;
865 if (!transfer_at_least(error, 1) || sz == 1)
866 hndlr(error, 1);
867 else
868 boost::asio::async_read(*socket, boost::asio::buffer(buff + 1, sz - 1),
869 [transfer_at_least](const boost::system::error_code& ec, size_t bytes) mutable {
870 return transfer_at_least(ec, bytes + 1);
871 },
872 [hndlr](const boost::system::error_code& ec, size_t bytes) mutable { hndlr(ec, bytes + 1); });
873 };
874 if (pending->error == boost::asio::error::would_block)
875 pending->handler = std::move(on_read);
876 m_pending_read.reset();
877 if (pending->error != boost::asio::error::would_block)
878 on_read(pending->error, pending->byte);
879 return;
880 }
882 boost::asio::async_read(m_ssl_socket->next_layer(), boost::asio::buffer(buff, sz), transfer_at_least, hndlr);
883 else
884 boost::asio::async_read(*m_ssl_socket, boost::asio::buffer(buff, sz), transfer_at_least, hndlr);
885
886 }
887
888 protected:
889 boost::asio::io_context m_io_service;
890 boost::asio::ssl::context m_ctx;
891 std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket;
892 std::shared_ptr<pending_read> m_pending_read;
893 std::function<connect_func> m_connector;
896 std::atomic<bool> m_connected;
897 boost::asio::steady_timer m_deadline;
898#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
899 // self-pipe: shutdown() write()s the pipe to wake run_one() signal-safely
900 boost::asio::posix::stream_descriptor m_wake_reader;
901 int m_wake_writer;
902 char m_wake_buf[8];
903#endif
904 // sticky: set once by shutdown(), never cleared; the client is being torn down
905 std::atomic<bool> m_aborted;
906 std::atomic<uint64_t> m_bytes_sent;
907 std::atomic<uint64_t> m_bytes_received;
908 };
909}
910}
911
#define fd(x)
Definition aesb.c:127
bool shutdown_requested() const
Definition net_helper.h:773
uint64_t get_bytes_received() const
Definition net_helper.h:759
boost::asio::steady_timer m_deadline
Definition net_helper.h:897
boost::unique_future< boost::asio::ip::tcp::socket >(const std::string &, const std::string &, boost::asio::steady_timer &) connect_func
Definition net_helper.h:170
void async_read(char *buff, size_t sz, boost::asio::detail::transfer_at_least_t transfer_at_least, handler_obj &hndlr)
Definition net_helper.h:851
uint64_t get_bytes_sent() const
Definition net_helper.h:754
bool shutdown()
Definition net_helper.h:725
bool connect(const std::string &addr, const std::string &port, std::chrono::milliseconds timeout)
Definition net_helper.h:276
bool write(const void *data, size_t sz, boost::system::error_code &ec)
Definition net_helper.h:833
try_connect_result_t try_connect(const std::string &addr, const std::string &port, std::chrono::milliseconds timeout)
Definition net_helper.h:200
void set_connector(std::function< connect_func > connector)
Change the connection routine (proxy, etc.).
Definition net_helper.h:320
bool recv(std::string &buff, std::chrono::milliseconds timeout)
Definition net_helper.h:542
void shutdown_ssl()
Definition net_helper.h:810
bool recv_n(std::string &buff, int64_t sz, std::chrono::milliseconds timeout)
Definition net_helper.h:639
void async_write(const void *data, size_t sz, boost::system::error_code &ec)
Definition net_helper.h:843
std::shared_ptr< pending_read > m_pending_read
Definition net_helper.h:892
void check_deadline()
Definition net_helper.h:787
~blocked_mode_client()
Definition net_helper.h:173
std::atomic< uint64_t > m_bytes_received
Definition net_helper.h:907
try_connect_result_t
Definition net_helper.h:83
@ CONNECT_FAILURE
Definition net_helper.h:85
@ CONNECT_NO_SSL
Definition net_helper.h:86
@ CONNECT_SUCCESS
Definition net_helper.h:84
blocked_mode_client()
Definition net_helper.h:112
std::atomic< bool > m_aborted
Definition net_helper.h:905
boost::asio::io_context & get_io_service()
Definition net_helper.h:744
std::function< connect_func > m_connector
Definition net_helper.h:893
bool send(const void *data, size_t sz)
Definition net_helper.h:423
bool connect(const std::string &addr, int port, std::chrono::milliseconds timeout)
Definition net_helper.h:194
boost::asio::io_context m_io_service
Definition net_helper.h:889
bool disconnect()
Definition net_helper.h:326
boost::asio::ip::tcp::socket & get_socket()
Definition net_helper.h:749
bool is_connected(bool *ssl=NULL)
Definition net_helper.h:480
void set_ssl(ssl_options_t ssl_options)
Definition net_helper.h:184
std::atomic< uint64_t > m_bytes_sent
Definition net_helper.h:906
bool send(const boost::string_ref buff, std::chrono::milliseconds timeout)
Definition net_helper.h:357
boost::asio::ssl::context m_ctx
Definition net_helper.h:890
ssl_options_t m_ssl_options
Definition net_helper.h:894
std::shared_ptr< boost::asio::ssl::stream< boost::asio::ip::tcp::socket > > m_ssl_socket
Definition net_helper.h:891
std::atomic< bool > m_connected
Definition net_helper.h:896
bool m_initialized
Definition net_helper.h:895
Represents a single connection from a client.
Definition abstract_tcp_server2.h:100
Definition net_ssl.h:78
ssl_support_t support
Definition net_ssl.h:85
boost::asio::ssl::context create_context() const
Definition net_ssl.cpp:303
bool handshake(boost::asio::io_context &io_context, boost::asio::ssl::stream< boost::asio::ip::tcp::socket > &socket, boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer={}, const std::string &host={}, std::chrono::milliseconds timeout=std::chrono::seconds(15), const std::function< bool()> &abort_requested=nullptr) const
Definition net_ssl.cpp:553
bool success
Definition cold-transaction.cpp:57
#define true
#define false
Definition portable_binary_archive.hpp:29
Definition abstract_http_client.h:36
ssl_support_t
Definition net_ssl.h:50
@ e_ssl_support_disabled
Definition net_ssl.h:51
@ e_ssl_support_autodetect
Definition net_ssl.h:53
@ e_ssl_support_enabled
Definition net_ssl.h:52
TODO: (mj-xmr) This will be reduced in an another PR.
Definition byte_slice.h:40
Definition enums.h:68
signed __int64 int64_t
Definition stdint.h:135
unsigned __int64 uint64_t
Definition stdint.h:136
handler_obj(const handler_obj &other_obj)
Definition net_helper.h:95
void operator()(const boost::system::error_code &error, std::size_t bytes_transferred)
Definition net_helper.h:101
boost::system::error_code & ref_error
Definition net_helper.h:98
handler_obj(boost::system::error_code &error, size_t &bytes_transferred)
Definition net_helper.h:93
size_t & ref_bytes_transferred
Definition net_helper.h:99
boost::system::error_code error
Definition net_helper.h:769
std::function< void(const boost::system::error_code &, char)> handler
Definition net_helper.h:770
Definition net_helper.h:74
boost::unique_future< boost::asio::ip::tcp::socket > operator()(const std::string &addr, const std::string &port, boost::asio::steady_timer &) const
Definition net_helper.cpp:22
std::string data
Definition base58.cpp:37