aboutsummaryrefslogtreecommitdiff
path: root/contrib/epee/include/net/net_helper.h
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/epee/include/net/net_helper.h')
-rw-r--r--contrib/epee/include/net/net_helper.h683
1 files changed, 683 insertions, 0 deletions
diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h
new file mode 100644
index 000000000..d2a4cfec3
--- /dev/null
+++ b/contrib/epee/include/net/net_helper.h
@@ -0,0 +1,683 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#pragma once
+
+//#include <Winsock2.h>
+//#include <Ws2tcpip.h>
+#include <boost/lexical_cast.hpp>
+#include <iostream>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <boost/asio.hpp>
+#include <boost/preprocessor/selection/min.hpp>
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/interprocess/detail/atomic.hpp>
+#include "net/net_utils_base.h"
+#include "misc_language.h"
+//#include "profile_tools.h"
+#include "../string_tools.h"
+
+#ifndef MAKE_IP
+#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
+#endif
+
+
+namespace epee
+{
+namespace net_utils
+{
+
+ class blocked_mode_client
+ {
+
+
+ struct handler_obj
+ {
+ handler_obj(boost::system::error_code& error, size_t& bytes_transferred):ref_error(error), ref_bytes_transferred(bytes_transferred)
+ {}
+ handler_obj(const handler_obj& other_obj):ref_error(other_obj.ref_error), ref_bytes_transferred(other_obj.ref_bytes_transferred)
+ {}
+
+ boost::system::error_code& ref_error;
+ size_t& ref_bytes_transferred;
+
+ void operator()(const boost::system::error_code& error, // Result of operation.
+ std::size_t bytes_transferred // Number of bytes read.
+ )
+ {
+ ref_error = error;
+ ref_bytes_transferred = bytes_transferred;
+ }
+ };
+
+ public:
+ inline
+ blocked_mode_client():m_socket(m_io_service),
+ m_initialized(false),
+ m_connected(false),
+ m_deadline(m_io_service),
+ m_shutdowned(0)
+ {
+
+
+ m_initialized = true;
+
+
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_deadline();
+
+ }
+ inline
+ ~blocked_mode_client()
+ {
+ //profile_tools::local_coast lc("~blocked_mode_client()", 3);
+ shutdown();
+ }
+
+ inline void set_recv_timeout(int reciev_timeout)
+ {
+ m_reciev_timeout = reciev_timeout;
+ }
+
+ inline
+ bool connect(const std::string& addr, int port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
+ {
+ return connect(addr, std::to_string(port), connect_timeout, reciev_timeout, bind_ip);
+ }
+
+ inline
+ bool connect(const std::string& addr, const std::string& port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
+ {
+ m_connect_timeout = connect_timeout;
+ m_reciev_timeout = reciev_timeout;
+ m_connected = false;
+ if(!m_reciev_timeout)
+ m_reciev_timeout = m_connect_timeout;
+
+ try
+ {
+ m_socket.close();
+ // Get a list of endpoints corresponding to the server name.
+
+
+ //////////////////////////////////////////////////////////////////////////
+
+ boost::asio::ip::tcp::resolver resolver(m_io_service);
+ boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port);
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::asio::ip::tcp::resolver::iterator end;
+ if(iterator == end)
+ {
+ LOG_ERROR("Failed to resolve " << addr);
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+
+ //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
+ boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
+
+
+ m_socket.open(remote_endpoint.protocol());
+ if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
+ {
+ boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0);
+ m_socket.bind(local_endpoint);
+ }
+
+
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_connect_timeout));
+
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ //m_socket.connect(remote_endpoint);
+ m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1);
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+
+ if (!ec && m_socket.is_open())
+ {
+ m_connected = true;
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ return true;
+ }else
+ {
+ LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3);
+ return false;
+ }
+
+ }
+ catch(const boost::system::system_error& er)
+ {
+ LOG_PRINT("Some problems at connect, message: " << er.what(), LOG_LEVEL_4);
+ return false;
+ }
+ catch(...)
+ {
+ LOG_PRINT("Some fatal problems.", LOG_LEVEL_4);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ inline
+ bool disconnect()
+ {
+ try
+ {
+ if(m_connected)
+ {
+ m_connected = false;
+ m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
+
+ }
+ }
+
+ catch(const boost::system::system_error& /*er*/)
+ {
+ //LOG_ERROR("Some problems at disconnect, message: " << er.what());
+ return false;
+ }
+ catch(...)
+ {
+ //LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+ return true;
+ }
+
+
+ inline
+ bool send(const std::string& buff)
+ {
+
+ try
+ {
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+
+ if (ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at connect, message: " << er.what());
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
+
+ inline
+ bool send(const void* data, size_t sz)
+ {
+ try
+ {
+ /*
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+ */
+ boost::system::error_code ec;
+
+ size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
+
+
+
+ if (!writen || ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at send, message: " << er.what());
+ m_connected = false;
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
+
+ bool is_connected()
+ {
+ return m_connected && m_socket.is_open();
+ //TRY_ENTRY()
+ //return m_socket.is_open();
+ //CATCH_ENTRY_L0("is_connected", false)
+ }
+
+ inline
+ bool recv(std::string& buff)
+ {
+
+ try
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ //boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t bytes_transfered = 0;
+
+ handler_obj hndlr(ec, bytes_transfered);
+
+ char local_buff[10000] = {0};
+ //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr);
+ boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
+ {
+ m_io_service.run_one();
+ }
+
+
+ if (ec)
+ {
+ LOG_PRINT_L4("READ ENDS: Connection err_code " << ec.value());
+ if(ec == boost::asio::error::eof)
+ {
+ LOG_PRINT_L4("Connection err_code eof.");
+ //connection closed there, empty
+ return true;
+ }
+
+ LOG_PRINT_L3("Problems at read: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ LOG_PRINT_L4("READ ENDS: Success. bytes_tr: " << bytes_transfered);
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ /*if(!bytes_transfered)
+ return false;*/
+
+ buff.assign(local_buff, bytes_transfered);
+ return true;
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at read, message: " << er.what());
+ m_connected = false;
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems at read.");
+ return false;
+ }
+
+
+
+ return false;
+
+ }
+
+ inline bool recv_n(std::string& buff, boost::int64_t sz)
+ {
+
+ try
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ //boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+
+ buff.resize(static_cast<size_t>(sz));
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t bytes_transfered = 0;
+
+
+ handler_obj hndlr(ec, bytes_transfered);
+
+ //char local_buff[10000] = {0};
+ boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
+ {
+ m_io_service.run_one();
+ }
+
+ if (ec)
+ {
+ LOG_PRINT_L3("Problems at read: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ if(bytes_transfered != buff.size())
+ {
+ LOG_ERROR("Transferred missmatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size());
+ return false;
+ }
+
+ return true;
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at read, message: " << er.what());
+ m_connected = false;
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems at read.");
+ return false;
+ }
+
+
+
+ return false;
+ }
+
+ bool shutdown()
+ {
+ m_deadline.cancel();
+ boost::system::error_code ignored_ec;
+ m_socket.cancel(ignored_ec);
+ m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ m_socket.close(ignored_ec);
+ boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1);
+ m_connected = false;
+ return true;
+ }
+
+ void set_connected(bool connected)
+ {
+ m_connected = connected;
+ }
+ boost::asio::io_service& get_io_service()
+ {
+ return m_io_service;
+ }
+
+ boost::asio::ip::tcp::socket& get_socket()
+ {
+ return m_socket;
+ }
+
+ private:
+
+ void check_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ LOG_PRINT_L3("Timed out socket");
+ m_connected = false;
+ m_socket.close();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this));
+ }
+
+
+
+ protected:
+ boost::asio::io_service m_io_service;
+ boost::asio::ip::tcp::socket m_socket;
+ int m_connect_timeout;
+ int m_reciev_timeout;
+ bool m_initialized;
+ bool m_connected;
+ boost::asio::deadline_timer m_deadline;
+ volatile boost::uint32_t m_shutdowned;
+ };
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class async_blocked_mode_client: public blocked_mode_client
+ {
+ public:
+ async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service)
+ {
+
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_send_deadline();
+ }
+ ~async_blocked_mode_client()
+ {
+ m_send_deadline.cancel();
+ }
+
+ bool shutdown()
+ {
+ blocked_mode_client::shutdown();
+ m_send_deadline.cancel();
+ return true;
+ }
+
+ inline
+ bool send(const void* data, size_t sz)
+ {
+ try
+ {
+ /*
+ m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while(ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }*/
+
+ boost::system::error_code ec;
+
+ size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
+
+ if (!writen || ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ return false;
+ }else
+ {
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at connect, message: " << er.what());
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
+
+
+ private:
+
+ boost::asio::deadline_timer m_send_deadline;
+
+ void check_send_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ LOG_PRINT_L3("Timed out socket");
+ m_socket.close();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this));
+ }
+ };
+}
+}