diff options
Diffstat (limited to 'contrib/epee/include/net/net_helper.h')
-rw-r--r-- | contrib/epee/include/net/net_helper.h | 683 |
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)); + } + }; +} +} |