diff options
Diffstat (limited to 'contrib/epee/include/net')
39 files changed, 10745 insertions, 0 deletions
diff --git a/contrib/epee/include/net/abstract_tcp_server.h b/contrib/epee/include/net/abstract_tcp_server.h new file mode 100644 index 000000000..c74444c8e --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server.h @@ -0,0 +1,316 @@ +// 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. +// + + + +#ifndef _ABSTRACT_TCP_SERVER_H_ +#define _ABSTRACT_TCP_SERVER_H_ + +#include <process.h> +#include <list> +#include <winsock2.h> +#include "winobj.h" +//#include "threads_helper.h" +#include "net_utils_base.h" + +#pragma comment(lib, "Ws2_32.lib") + +namespace epee +{ +namespace net_utils +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class soket_sender: public i_service_endpoint + { + public: + soket_sender(SOCKET sock):m_sock(sock){} + private: + virtual bool handle_send(const void* ptr, size_t cb) + { + if(cb != send(m_sock, (char*)ptr, (int)cb, 0)) + { + int sock_err = WSAGetLastError(); + LOG_ERROR("soket_sender: Failed to send " << cb << " bytes, Error=" << sock_err); + return false; + } + return true; + + } + + SOCKET m_sock; + }; + + + + /************************************************************************/ + /* */ + /************************************************************************/ + template<class THandler> + class abstract_tcp_server + { + public: + abstract_tcp_server(); + + bool init_server(int port_no); + bool deinit_server(); + bool run_server(); + bool send_stop_signal(); + + typename THandler::config_type& get_config_object(){return m_config;} + + private: + bool invoke_connection(SOCKET hnew_sock, long ip_from, int post_from); + static unsigned __stdcall ConnectionHandlerProc(void* lpParameter); + + class thread_context; + typedef std::list<thread_context> connections_container; + typedef typename connections_container::iterator connections_iterator; + + struct thread_context + { + HANDLE m_htread; + SOCKET m_socket; + abstract_tcp_server* powner; + connection_context m_context; + typename connections_iterator m_self_it; + }; + + SOCKET m_listen_socket; + int m_port; + bool m_initialized; + volatile LONG m_stop_server; + volatile LONG m_threads_count; + typename THandler::config_type m_config; + connections_container m_connections; + critical_section m_connections_lock; + }; + + template<class THandler> + unsigned __stdcall abstract_tcp_server<THandler>::ConnectionHandlerProc(void* lpParameter) + { + + thread_context* pthread_context = (thread_context*)lpParameter; + if(!pthread_context) + return 0; + abstract_tcp_server<THandler>* pthis = pthread_context->powner; + + ::InterlockedIncrement(&pthis->m_threads_count); + + ::CoInitialize(NULL); + + + LOG_PRINT("Handler thread STARTED with socket=" << pthread_context->m_socket, LOG_LEVEL_2); + int res = 0; + + soket_sender sndr(pthread_context->m_socket); + THandler srv(&sndr, pthread_context->powner->m_config, pthread_context->m_context); + + + srv.after_init_connection(); + + char buff[1000] = {0}; + std::string ansver; + while ( (res = recv(pthread_context->m_socket, (char*)buff, 1000, 0)) > 0) + { + LOG_PRINT("Data in, " << res << " bytes", LOG_LEVEL_3); + if(!srv.handle_recv(buff, res)) + break; + } + shutdown(pthread_context->m_socket, SD_BOTH); + closesocket(pthread_context->m_socket); + + abstract_tcp_server* powner = pthread_context->powner; + LOG_PRINT("Handler thread with socket=" << pthread_context->m_socket << " STOPPED", LOG_LEVEL_2); + powner->m_connections_lock.lock(); + ::CloseHandle(pthread_context->m_htread); + pthread_context->powner->m_connections.erase(pthread_context->m_self_it); + powner->m_connections_lock.unlock(); + CoUninitialize(); + ::InterlockedDecrement(&pthis->m_threads_count); + return 1; + } + //---------------------------------------------------------------------------------------- + template<class THandler> + abstract_tcp_server<THandler>::abstract_tcp_server():m_listen_socket(INVALID_SOCKET), + m_initialized(false), + m_stop_server(0), m_port(0), m_threads_count(0) + { + + } + + //---------------------------------------------------------------------------------------- + template<class THandler> + bool abstract_tcp_server<THandler>::init_server(int port_no) + { + m_port = port_no; + WSADATA wsad = {0}; + int err = ::WSAStartup(MAKEWORD(2,2), &wsad); + if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 ) + { + LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + m_initialized = true; + + m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if(INVALID_SOCKET == m_listen_socket) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + int opt = 1; + setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast<char*>(&opt), sizeof(int)); + + sockaddr_in adr = {0}; + adr.sin_family = AF_INET; + adr.sin_addr.s_addr = htonl(INADDR_ANY); + adr.sin_port = (u_short)htons(port_no); + + err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr )); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); + deinit_server(); + return false; + } + + ::InterlockedExchange(&m_stop_server, 0); + + return true; + } + //---------------------------------------------------------------------------------------- + template<class THandler> + bool abstract_tcp_server<THandler>::deinit_server() + { + + if(!m_initialized) + return true; + + if(INVALID_SOCKET != m_listen_socket) + { + shutdown(m_listen_socket, SD_BOTH); + int res = closesocket(m_listen_socket); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_listen_socket = INVALID_SOCKET; + } + + int res = ::WSACleanup(); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_initialized = false; + + return true; + } + //---------------------------------------------------------------------------------------- + template<class THandler> + bool abstract_tcp_server<THandler>::send_stop_signal() + { + InterlockedExchange(&m_stop_server, 1); + return true; + } + //---------------------------------------------------------------------------------------- + template<class THandler> + bool abstract_tcp_server<THandler>::run_server() + { + int err = listen(m_listen_socket, 10000); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + LOG_PRINT("Listening port "<< m_port << "...." , LOG_LEVEL_2); + + while(!m_stop_server) + { + sockaddr_in adr_from = {0}; + int adr_len = sizeof(adr_from); + fd_set read_fs = {0}; + read_fs.fd_count = 1; + read_fs.fd_array[0] = m_listen_socket; + TIMEVAL tv = {0}; + tv.tv_usec = 100; + int select_res = select(0, &read_fs, NULL, NULL, &tv); + if(!select_res) + continue; + SOCKET new_sock = WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, NULL, NULL); + LOG_PRINT("Accepted connection on socket=" << new_sock, LOG_LEVEL_2); + invoke_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port); + } + + deinit_server(); + +#define ABSTR_TCP_SRV_WAIT_COUNT_MAX 5000 +#define ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL 1000 + + int wait_count = 0; + + while(m_threads_count && wait_count*1000 < ABSTR_TCP_SRV_WAIT_COUNT_MAX) + { + ::Sleep(ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL); + wait_count++; + } + LOG_PRINT("abstract_tcp_server exit with wait count=" << wait_count*ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL << "(max=" << ABSTR_TCP_SRV_WAIT_COUNT_MAX <<")", LOG_LEVEL_0); + + return true; + } + //---------------------------------------------------------------------------------------- + template<class THandler> + bool abstract_tcp_server<THandler>::invoke_connection(SOCKET hnew_sock, long ip_from, int post_from) + { + m_connections_lock.lock(); + m_connections.push_back(thread_context()); + m_connections_lock.unlock(); + m_connections.back().m_socket = hnew_sock; + m_connections.back().powner = this; + m_connections.back().m_self_it = --m_connections.end(); + m_connections.back().m_context.m_remote_ip = ip_from; + m_connections.back().m_context.m_remote_port = post_from; + m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back()); + + return true; + } + //---------------------------------------------------------------------------------------- + + //---------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------- +} +} +#endif //_ABSTRACT_TCP_SERVER_H_ diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h new file mode 100644 index 000000000..d49b8f864 --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -0,0 +1,276 @@ +// 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. +// + + + +#ifndef _ABSTRACT_TCP_SERVER2_H_ +#define _ABSTRACT_TCP_SERVER2_H_ + + +#include <boost/asio.hpp> +#include <string> +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <atomic> + +#include <boost/asio.hpp> +#include <boost/array.hpp> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/thread/thread.hpp> +#include "net_utils_base.h" +#include "syncobj.h" + + +#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 100 + +namespace epee +{ +namespace net_utils +{ + + struct i_connection_filter + { + virtual bool is_remote_ip_allowed(boost::uint32_t adress)=0; + protected: + virtual ~i_connection_filter(){} + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + /// Represents a single connection from a client. + template<class t_protocol_handler> + class connection + : public boost::enable_shared_from_this<connection<t_protocol_handler> >, + private boost::noncopyable, + public i_service_endpoint + { + public: + typedef typename t_protocol_handler::connection_context t_connection_context; + /// Construct a connection with the given io_service. + explicit connection(boost::asio::io_service& io_service, + typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter * &pfilter); + + virtual ~connection(); + /// Get the socket associated with the connection. + boost::asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + bool start(bool is_income, bool is_multithreaded); + + void get_context(t_connection_context& context_){context_ = context;} + + void call_back_starter(); + private: + //----------------- i_service_endpoint --------------------- + virtual bool do_send(const void* ptr, size_t cb); + virtual bool close(); + virtual bool call_run_once_service_io(); + virtual bool request_callback(); + virtual boost::asio::io_service& get_io_service(); + virtual bool add_ref(); + virtual bool release(); + //------------------------------------------------------ + boost::shared_ptr<connection<t_protocol_handler> > safe_shared_from_this(); + bool shutdown(); + /// Handle completion of a read operation. + void handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const boost::system::error_code& e, size_t cb); + + /// Strand to ensure the connection's handlers are not called concurrently. + boost::asio::io_service::strand strand_; + + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + /// Buffer for incoming data. + boost::array<char, 8192> buffer_; + + t_connection_context context; + volatile boost::uint32_t m_want_close_connection; + std::atomic<bool> m_was_shutdown; + critical_section m_send_que_lock; + std::list<std::string> m_send_que; + volatile boost::uint32_t& m_ref_sockets_count; + i_connection_filter* &m_pfilter; + volatile bool m_is_multithreaded; + + //this should be the last one, because it could be wait on destructor, while other activities possible on other threads + t_protocol_handler m_protocol_handler; + //typename t_protocol_handler::config_type m_dummy_config; + std::list<boost::shared_ptr<connection<t_protocol_handler> > > m_self_refs; // add_ref/release support + critical_section m_self_refs_lock; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + template<class t_protocol_handler> + class boosted_tcp_server + : private boost::noncopyable + { + public: + typedef boost::shared_ptr<connection<t_protocol_handler> > connection_ptr; + typedef typename t_protocol_handler::connection_context t_connection_context; + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + boosted_tcp_server(); + explicit boosted_tcp_server(boost::asio::io_service& external_io_service); + ~boosted_tcp_server(); + + bool init_server(uint32_t port, const std::string address = "0.0.0.0"); + bool init_server(const std::string port, const std::string& address = "0.0.0.0"); + + /// Run the server's io_service loop. + bool run_server(size_t threads_count, bool wait = true); + + /// wait for service workers stop + bool timed_wait_server_stop(boost::uint64_t wait_mseconds); + + /// Stop the server. + void send_stop_signal(); + + bool is_stop_signal_sent(); + + void set_threads_prefix(const std::string& prefix_name); + + bool deinit_server(){return true;} + + size_t get_threads_count(){return m_threads_count;} + + void set_connection_filter(i_connection_filter* pfilter); + + bool connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0"); + template<class t_callback> + bool connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_callback cb, const std::string& bind_ip = "0.0.0.0"); + + typename t_protocol_handler::config_type& get_config_object(){return m_config;} + + int get_binded_port(){return m_port;} + + boost::asio::io_service& get_io_service(){return io_service_;} + + struct idle_callback_conext_base + { + virtual ~idle_callback_conext_base(){} + + virtual bool call_handler(){return true;} + + idle_callback_conext_base(boost::asio::io_service& io_serice): + m_timer(io_serice) + {} + boost::asio::deadline_timer m_timer; + boost::uint64_t m_period; + }; + + template <class t_handler> + struct idle_callback_conext: public idle_callback_conext_base + { + idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, boost::uint64_t period): + idle_callback_conext_base(io_serice), + m_handler(h) + {this->m_period = period;} + + t_handler m_handler; + virtual bool call_handler() + { + return m_handler(); + } + }; + + template<class t_handler> + bool add_idle_handler(t_handler t_callback, boost::uint64_t timeout_ms) + { + boost::shared_ptr<idle_callback_conext_base> ptr(new idle_callback_conext<t_handler>(io_service_, t_callback, timeout_ms)); + //needed call handler here ?... + ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); + ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr)); + return true; + } + + bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr<idle_callback_conext_base> ptr) + { + //if handler return false - he don't want to be called anymore + if(!ptr->call_handler()) + return true; + ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); + ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr)); + return true; + } + + template<class t_handler> + bool async_call(t_handler t_callback) + { + io_service_.post(t_callback); + return true; + } + + protected: + typename t_protocol_handler::config_type m_config; + + private: + /// Run the server's io_service loop. + bool worker_thread(); + /// Handle completion of an asynchronous accept operation. + void handle_accept(const boost::system::error_code& e); + + bool is_thread_worker(); + + /// The io_service used to perform asynchronous operations. + std::unique_ptr<boost::asio::io_service> m_io_service_local_instance; + boost::asio::io_service& io_service_; + + /// Acceptor used to listen for incoming connections. + boost::asio::ip::tcp::acceptor acceptor_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + std::atomic<bool> m_stop_signal_sent; + uint32_t m_port; + volatile boost::uint32_t m_sockets_count; + std::string m_address; + std::string m_thread_name_prefix; + size_t m_threads_count; + i_connection_filter* m_pfilter; + std::vector<boost::shared_ptr<boost::thread> > m_threads; + boost::thread::id m_main_thread_id; + critical_section m_threads_lock; + volatile uint32_t m_thread_index; + }; +} +} + +#include "abstract_tcp_server2.inl" + +#endif
\ No newline at end of file diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl new file mode 100644 index 000000000..1d6a1662f --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -0,0 +1,811 @@ +// 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. +// + + + +#include "net_utils_base.h" +#include <boost/lambda/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/uuid/random_generator.hpp> +#include <boost/chrono.hpp> +#include <boost/utility/value_init.hpp> +#include <boost/asio/deadline_timer.hpp> +#include "misc_language.h" +#include "pragma_comp_defs.h" + +PRAGMA_WARNING_PUSH +namespace epee +{ +namespace net_utils +{ + /************************************************************************/ + /* */ + /************************************************************************/ +PRAGMA_WARNING_DISABLE_VS(4355) + + template<class t_protocol_handler> + connection<t_protocol_handler>::connection(boost::asio::io_service& io_service, + typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter* &pfilter) + : strand_(io_service), + socket_(io_service), + //context(typename boost::value_initialized<t_connection_context>()) + m_protocol_handler(this, config, context), + m_want_close_connection(0), + m_was_shutdown(0), + m_ref_sockets_count(sock_count), + m_pfilter(pfilter) + { + boost::interprocess::ipcdetail::atomic_inc32(&m_ref_sockets_count); + } +PRAGMA_WARNING_DISABLE_VS(4355) + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + connection<t_protocol_handler>::~connection() + { + if(!m_was_shutdown) + { + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed without shutdown."); + shutdown(); + } + + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed"); + boost::interprocess::ipcdetail::atomic_dec32(&m_ref_sockets_count); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + boost::asio::ip::tcp::socket& connection<t_protocol_handler>::socket() + { + return socket_; + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + boost::shared_ptr<connection<t_protocol_handler> > connection<t_protocol_handler>::safe_shared_from_this() + { + try + { + return connection<t_protocol_handler>::shared_from_this(); + } + catch (const boost::bad_weak_ptr&) + { + // It happens when the connection is being deleted + return boost::shared_ptr<connection<t_protocol_handler> >(); + } + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::start(bool is_income, bool is_multithreaded) + { + TRY_ENTRY(); + + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + + m_is_multithreaded = is_multithreaded; + + boost::system::error_code ec; + auto remote_ep = socket_.remote_endpoint(ec); + CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value()); + + auto local_ep = socket_.local_endpoint(ec); + CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value()); + + context = boost::value_initialized<t_connection_context>(); + long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong()); + + context.set_details(boost::uuids::random_generator()(), ip_, remote_ep.port(), is_income); + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << + " to " << local_ep.address().to_string() << ':' << local_ep.port() << + ", total sockets objects " << m_ref_sockets_count); + + if(m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip)) + { + LOG_PRINT_L2("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection"); + close(); + return false; + } + + m_protocol_handler.after_init_connection(); + + socket_.async_read_some(boost::asio::buffer(buffer_), + strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_read, self, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + + return true; + + CATCH_ENTRY_L0("connection<t_protocol_handler>::start()", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::request_callback() + { + TRY_ENTRY(); + LOG_PRINT_L2("[" << print_connection_context_short(context) << "] request_callback"); + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + + strand_.post(boost::bind(&connection<t_protocol_handler>::call_back_starter, self)); + CATCH_ENTRY_L0("connection<t_protocol_handler>::request_callback()", false); + return true; + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + boost::asio::io_service& connection<t_protocol_handler>::get_io_service() + { + return socket_.get_io_service(); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::add_ref() + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] add_ref"); + CRITICAL_REGION_LOCAL(m_self_refs_lock); + + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + if(m_was_shutdown) + return false; + m_self_refs.push_back(self); + return true; + CATCH_ENTRY_L0("connection<t_protocol_handler>::add_ref()", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::release() + { + TRY_ENTRY(); + boost::shared_ptr<connection<t_protocol_handler> > back_connection_copy; + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] release"); + CRITICAL_REGION_BEGIN(m_self_refs_lock); + CHECK_AND_ASSERT_MES(m_self_refs.size(), false, "[sock " << socket_.native_handle() << "] m_self_refs empty at connection<t_protocol_handler>::release() call"); + //erasing from container without additional copy can cause start deleting object, including m_self_refs + back_connection_copy = m_self_refs.back(); + m_self_refs.pop_back(); + CRITICAL_REGION_END(); + return true; + CATCH_ENTRY_L0("connection<t_protocol_handler>::release()", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + void connection<t_protocol_handler>::call_back_starter() + { + TRY_ENTRY(); + LOG_PRINT_L2("[" << print_connection_context_short(context) << "] fired_callback"); + m_protocol_handler.handle_qued_callback(); + CATCH_ENTRY_L0("connection<t_protocol_handler>::call_back_starter()", void()); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + void connection<t_protocol_handler>::handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred) + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async read calledback."); + + if (!e) + { + LOG_PRINT("[sock " << socket_.native_handle() << "] RECV " << bytes_transferred, LOG_LEVEL_4); + bool recv_res = m_protocol_handler.handle_recv(buffer_.data(), bytes_transferred); + if(!recv_res) + { + LOG_PRINT("[sock " << socket_.native_handle() << "] protocol_want_close", LOG_LEVEL_4); + + //some error in protocol, protocol handler ask to close connection + boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); + bool do_shutdown = false; + CRITICAL_REGION_BEGIN(m_send_que_lock); + if(!m_send_que.size()) + do_shutdown = true; + CRITICAL_REGION_END(); + if(do_shutdown) + shutdown(); + }else + { + socket_.async_read_some(boost::asio::buffer(buffer_), + strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_read, connection<t_protocol_handler>::shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "]Async read requested."); + } + }else + { + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some not success at read: " << e.message() << ':' << e.value()); + if(e.value() != 2) + { + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some problems at read: " << e.message() << ':' << e.value()); + shutdown(); + } + } + // If an error occurs then no new asynchronous operations are started. This + // means that all shared_ptr references to the connection object will + // disappear and the object will be destroyed automatically after this + // handler returns. The connection class's destructor closes the socket. + CATCH_ENTRY_L0("connection<t_protocol_handler>::handle_read", void()); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::call_run_once_service_io() + { + TRY_ENTRY(); + if(!m_is_multithreaded) + { + //single thread model, we can wait in blocked call + size_t cnt = socket_.get_io_service().run_one(); + if(!cnt)//service is going to quit + return false; + }else + { + //multi thread model, we can't(!) wait in blocked call + //so we make non blocking call and releasing CPU by calling sleep(0); + //if no handlers were called + //TODO: Maybe we need to have have critical section + event + callback to upper protocol to + //ask it inside(!) critical region if we still able to go in event wait... + size_t cnt = socket_.get_io_service().poll_one(); + if(!cnt) + misc_utils::sleep_no_w(0); + } + + return true; + CATCH_ENTRY_L0("connection<t_protocol_handler>::call_run_once_service_io", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::do_send(const void* ptr, size_t cb) + { + TRY_ENTRY(); + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + if(m_was_shutdown) + return false; + + LOG_PRINT("[sock " << socket_.native_handle() << "] SEND " << cb, LOG_LEVEL_4); + //some data should be wrote to stream + //request complete + + epee::critical_region_t<decltype(m_send_que_lock)> send_guard(m_send_que_lock); + if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT) + { + send_guard.unlock(); + LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); + close(); + return false; + } + + m_send_que.resize(m_send_que.size()+1); + m_send_que.back().assign((const char*)ptr, cb); + + if(m_send_que.size() > 1) + { + //active operation should be in progress, nothing to do, just wait last operation callback + }else + { + //no active operation + if(m_send_que.size()!=1) + { + LOG_ERROR("Looks like no active operations, but send que size != 1!!"); + return false; + } + + boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()), + //strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_write, self, _1, _2) + //) + ); + + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send requested " << m_send_que.front().size()); + } + + return true; + + CATCH_ENTRY_L0("connection<t_protocol_handler>::do_send", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::shutdown() + { + // Initiate graceful connection closure. + boost::system::error_code ignored_ec; + socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + m_was_shutdown = true; + m_protocol_handler.release_protocol(); + return true; + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool connection<t_protocol_handler>::close() + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Que Shutdown called."); + size_t send_que_size = 0; + CRITICAL_REGION_BEGIN(m_send_que_lock); + send_que_size = m_send_que.size(); + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); + if(!send_que_size) + { + shutdown(); + } + + return true; + CATCH_ENTRY_L0("connection<t_protocol_handler>::close", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + void connection<t_protocol_handler>::handle_write(const boost::system::error_code& e, size_t cb) + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send calledback " << cb); + + if (e) + { + LOG_PRINT_L0("[sock " << socket_.native_handle() << "] Some problems at write: " << e.message() << ':' << e.value()); + shutdown(); + return; + } + + bool do_shutdown = false; + CRITICAL_REGION_BEGIN(m_send_que_lock); + if(m_send_que.empty()) + { + LOG_ERROR("[sock " << socket_.native_handle() << "] m_send_que.size() == 0 at handle_write!"); + return; + } + + m_send_que.pop_front(); + if(m_send_que.empty()) + { + if(boost::interprocess::ipcdetail::atomic_read32(&m_want_close_connection)) + { + do_shutdown = true; + } + }else + { + //have more data to send + boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()), + //strand_.wrap( + boost::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), _1, _2)); + //); + } + CRITICAL_REGION_END(); + + if(do_shutdown) + { + shutdown(); + } + CATCH_ENTRY_L0("connection<t_protocol_handler>::handle_write", void()); + } + /************************************************************************/ + /* */ + /************************************************************************/ + template<class t_protocol_handler> + boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(): + m_io_service_local_instance(new boost::asio::io_service()), + io_service_(*m_io_service_local_instance.get()), + acceptor_(io_service_), + new_connection_(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)), + m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0) + { + m_thread_name_prefix = "NET"; + } + + template<class t_protocol_handler> + boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_service& extarnal_io_service): + io_service_(extarnal_io_service), + acceptor_(io_service_), + new_connection_(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)), + m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0) + { + m_thread_name_prefix = "NET"; + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + boosted_tcp_server<t_protocol_handler>::~boosted_tcp_server() + { + this->send_stop_signal(); + timed_wait_server_stop(10000); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address) + { + TRY_ENTRY(); + m_stop_signal_sent = false; + m_port = port; + m_address = address; + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + boost::asio::ip::tcp::resolver resolver(io_service_); + boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port)); + boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint(); + m_port = binded_endpoint.port(); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, + boost::asio::placeholders::error)); + + return true; + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::init_server", false); + } + //----------------------------------------------------------------------------- +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address) + { + uint32_t p = 0; + + if (port.size() && !string_tools::get_xtype_from_string(p, port)) { + return false; + LOG_ERROR("Failed to convert port no = port"); + } + return this->init_server(p, address); + } +POP_WARNINGS + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::worker_thread() + { + TRY_ENTRY(); + uint32_t local_thr_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); + std::string thread_name = std::string("[") + m_thread_name_prefix; + thread_name += boost::to_string(local_thr_index) + "]"; + log_space::log_singletone::set_thread_log_prefix(thread_name); + while(!m_stop_signal_sent) + { + try + { + io_service_.run(); + } + catch(const std::exception& ex) + { + LOG_ERROR("Exception at server worker thread, what=" << ex.what()); + } + catch(...) + { + LOG_ERROR("Exception at server worker thread, unknown execption"); + } + } + LOG_PRINT_L4("Worker thread finished"); + return true; + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::worker_thread", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + void boosted_tcp_server<t_protocol_handler>::set_threads_prefix(const std::string& prefix_name) + { + m_thread_name_prefix = prefix_name; + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + void boosted_tcp_server<t_protocol_handler>::set_connection_filter(i_connection_filter* pfilter) + { + m_pfilter = pfilter; + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait) + { + TRY_ENTRY(); + m_threads_count = threads_count; + m_main_thread_id = boost::this_thread::get_id(); + log_space::log_singletone::set_thread_log_prefix("[SRV_MAIN]"); + while(!m_stop_signal_sent) + { + + // Create a pool of threads to run all of the io_services. + CRITICAL_REGION_BEGIN(m_threads_lock); + for (std::size_t i = 0; i < threads_count; ++i) + { + boost::shared_ptr<boost::thread> thread(new boost::thread( + boost::bind(&boosted_tcp_server<t_protocol_handler>::worker_thread, this))); + m_threads.push_back(thread); + } + CRITICAL_REGION_END(); + // Wait for all threads in the pool to exit. + if(wait) + { + for (std::size_t i = 0; i < m_threads.size(); ++i) + m_threads[i]->join(); + m_threads.clear(); + + }else + { + return true; + } + + if(wait && !m_stop_signal_sent) + { + //some problems with the listening socket ?.. + LOG_PRINT_L0("Net service stopped without stop request, restarting..."); + if(!this->init_server(m_port, m_address)) + { + LOG_PRINT_L0("Reiniting service failed, exit."); + return false; + }else + { + LOG_PRINT_L0("Reiniting OK."); + } + } + } + return true; + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::run_server", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::is_thread_worker() + { + TRY_ENTRY(); + CRITICAL_REGION_LOCAL(m_threads_lock); + BOOST_FOREACH(boost::shared_ptr<boost::thread>& thp, m_threads) + { + if(thp->get_id() == boost::this_thread::get_id()) + return true; + } + if(m_threads_count == 1 && boost::this_thread::get_id() == m_main_thread_id) + return true; + return false; + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::is_thread_worker", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop(boost::uint64_t wait_mseconds) + { + TRY_ENTRY(); + boost::chrono::milliseconds ms(wait_mseconds); + for (std::size_t i = 0; i < m_threads.size(); ++i) + { + if(m_threads[i]->joinable() && !m_threads[i]->try_join_for(ms)) + { + LOG_PRINT_L0("Interrupting thread " << m_threads[i]->native_handle()); + m_threads[i]->interrupt(); + } + } + return true; + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + void boosted_tcp_server<t_protocol_handler>::send_stop_signal() + { + m_stop_signal_sent = true; + TRY_ENTRY(); + io_service_.stop(); + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void()); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::is_stop_signal_sent() + { + return m_stop_signal_sent; + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e) + { + TRY_ENTRY(); + if (!e) + { + connection_ptr conn(std::move(new_connection_)); + + new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this, + boost::asio::placeholders::error)); + + bool r = conn->start(true, 1 < m_threads_count); + if (!r) + LOG_ERROR("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count); + }else + { + LOG_ERROR("Some problems at accept: " << e.message() << ", connections_count = " << m_sockets_count); + } + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::handle_accept", void()); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> + bool boosted_tcp_server<t_protocol_handler>::connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip) + { + TRY_ENTRY(); + + connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter) ); + boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); + + ////////////////////////////////////////////////////////////////////////// + boost::asio::ip::tcp::resolver resolver(io_service_); + boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, 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 " << adr); + 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); + + sock_.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(adr.c_str()), 0); + sock_.bind(local_endpoint); + } + + /* + NOTICE: be careful to make sync connection from event handler: in case if all threads suddenly do sync connect, there will be no thread to dispatch events from io service. + */ + + boost::system::error_code ec = boost::asio::error::would_block; + + //have another free thread(s), work in wait mode, without event handling + struct local_async_context + { + boost::system::error_code ec; + boost::mutex connect_mut; + boost::condition_variable cond; + }; + + boost::shared_ptr<local_async_context> local_shared_context(new local_async_context()); + local_shared_context->ec = boost::asio::error::would_block; + boost::unique_lock<boost::mutex> lock(local_shared_context->connect_mut); + auto connect_callback = [](boost::system::error_code ec_, boost::shared_ptr<local_async_context> shared_context) + { + shared_context->connect_mut.lock(); shared_context->ec = ec_; shared_context->connect_mut.unlock(); shared_context->cond.notify_one(); + }; + + sock_.async_connect(remote_endpoint, boost::bind<void>(connect_callback, _1, local_shared_context)); + while(local_shared_context->ec == boost::asio::error::would_block) + { + bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout)); + if(local_shared_context->ec == boost::asio::error::would_block && !r) + { + //timeout + sock_.close(); + LOG_PRINT_L3("Failed to connect to " << adr << ":" << port << ", because of timeout (" << conn_timeout << ")"); + return false; + } + } + ec = local_shared_context->ec; + + if (ec || !sock_.is_open()) + { + LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3); + return false; + } + + LOG_PRINT_L3("Connected success to " << adr << ':' << port); + + bool r = new_connection_l->start(false, 1 < m_threads_count); + if (r) + { + new_connection_l->get_context(conn_context); + //new_connection_l.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)); + } + else + { + LOG_ERROR("[sock " << new_connection_->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count); + } + + return r; + + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect", false); + } + //--------------------------------------------------------------------------------- + template<class t_protocol_handler> template<class t_callback> + bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_callback cb, const std::string& bind_ip) + { + TRY_ENTRY(); + connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter) ); + boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); + + ////////////////////////////////////////////////////////////////////////// + boost::asio::ip::tcp::resolver resolver(io_service_); + boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, 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 " << adr); + return false; + } + ////////////////////////////////////////////////////////////////////////// + boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); + + sock_.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(adr.c_str()), 0); + sock_.bind(local_endpoint); + } + + boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_)); + //start deadline + sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)); + sh_deadline->async_wait([=](const boost::system::error_code& error) + { + if(error != boost::asio::error::operation_aborted) + { + LOG_PRINT_L3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")"); + new_connection_l->socket().close(); + } + }); + //start async connect + sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_) + { + t_connection_context conn_context = AUTO_VAL_INIT(conn_context); + boost::system::error_code ignored_ec; + boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec); + if(!ec_) + {//success + if(!sh_deadline->cancel()) + { + cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation + }else + { + LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port << + " from " << lep.address().to_string() << ':' << lep.port()); + bool r = new_connection_l->start(false, 1 < m_threads_count); + if (r) + { + new_connection_l->get_context(conn_context); + cb(conn_context, ec_); + } + else + { + LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port); + cb(conn_context, boost::asio::error::fault); + } + } + }else + { + LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port << + " from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value()); + cb(conn_context, ec_); + } + }); + return true; + CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false); + } +} +} +PRAGMA_WARNING_POP diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.h b/contrib/epee/include/net/abstract_tcp_server_cp.h new file mode 100644 index 000000000..b6410e120 --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server_cp.h @@ -0,0 +1,233 @@ +// 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. +// + + +#ifndef _LEVIN_CP_SERVER_H_ +#define _LEVIN_CP_SERVER_H_ + +#include <winsock2.h> +#include <rpc.h> +#include <string> +#include <map> +#include <boost/shared_ptr.hpp> + +#include "misc_log_ex.h" +//#include "threads_helper.h" +#include "syncobj.h" +#define ENABLE_PROFILING +#include "profile_tools.h" +#include "net_utils_base.h" +#include "pragma_comp_defs.h" + +#define LEVIN_DEFAULT_DATA_BUFF_SIZE 2000 + +namespace epee +{ +namespace net_utils +{ + + template<class TProtocol> + class cp_server_impl//: public abstract_handler + { + public: + cp_server_impl(/*abstract_handler* phandler = NULL*/); + virtual ~cp_server_impl(); + + bool init_server(int port_no); + bool deinit_server(); + bool run_server(int threads_count = 0); + bool send_stop_signal(); + bool is_stop_signal(); + virtual bool on_net_idle(){return true;} + size_t get_active_connections_num(); + typename TProtocol::config_type& get_config_object(){return m_config;} + private: + enum overlapped_operation_type + { + op_type_recv, + op_type_send, + op_type_stop + }; + + struct io_data_base + { + OVERLAPPED m_overlapped; + WSABUF DataBuf; + overlapped_operation_type m_op_type; + DWORD TotalBuffBytes; + volatile LONG m_is_in_use; + char Buffer[1]; + }; + +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4355) + template<class TProtocol> + struct connection: public net_utils::i_service_endpoint + { + connection(typename TProtocol::config_type& ref_config):m_sock(INVALID_SOCKET), m_tprotocol_handler(this, ref_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) + { + } + + //connection():m_sock(INVALID_SOCKET), m_tprotocol_handler(this, m_dummy_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) + //{ + //} + + connection<TProtocol>& operator=(const connection<TProtocol>& obj) + { + return *this; + } + + bool init_buffers() + { + m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; + m_psend_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; + m_precv_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; + m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; + return true; + } + + bool query_shutdown() + { + if(!::InterlockedCompareExchange(&m_asked_to_shutdown, 1, 0)) + { + m_psend_data->m_op_type = op_type_stop; + ::PostQueuedCompletionStatus(m_completion_port, 0, (ULONG_PTR)this, &m_psend_data->m_overlapped); + } + return true; + } + + //bool set_config(typename TProtocol::config_type& config) + //{ + // this->~connection(); + // new(this) connection<TProtocol>(config); + // return true; + //} + ~connection() + { + if(m_psend_data) + delete m_psend_data; + + if(m_precv_data) + delete m_precv_data; + } + virtual bool handle_send(const void* ptr, size_t cb) + { + PROFILE_FUNC("[handle_send]"); + if(m_psend_data->TotalBuffBytes < cb) + resize_send_buff((DWORD)cb); + + ZeroMemory(&m_psend_data->m_overlapped, sizeof(OVERLAPPED)); + m_psend_data->DataBuf.len = (u_long)cb;//m_psend_data->TotalBuffBytes; + m_psend_data->DataBuf.buf = m_psend_data->Buffer; + memcpy(m_psend_data->DataBuf.buf, ptr, cb); + m_psend_data->m_op_type = op_type_send; + InterlockedExchange(&m_psend_data->m_is_in_use, 1); + DWORD bytes_sent = 0; + DWORD flags = 0; + int res = 0; + { + PROFILE_FUNC("[handle_send] ::WSASend"); + res = ::WSASend(m_sock, &(m_psend_data->DataBuf), 1, &bytes_sent, flags, &(m_psend_data->m_overlapped), NULL); + } + + if(res == SOCKET_ERROR ) + { + int err = ::WSAGetLastError(); + if(WSA_IO_PENDING == err ) + return true; + } + LOG_ERROR("BIG FAIL: WSASend error code not correct, res=" << res << " last_err=" << err); + ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); + query_shutdown(); + //closesocket(m_psend_data); + return false; + }else if(0 == res) + { + ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); + if(!bytes_sent || bytes_sent != cb) + { + int err = ::WSAGetLastError(); + LOG_ERROR("BIG FAIL: WSASend immediatly complete? but bad results, res=" << res << " last_err=" << err); + query_shutdown(); + return false; + }else + { + return true; + } + } + + return true; + } + bool resize_send_buff(DWORD new_size) + { + if(m_psend_data->TotalBuffBytes >= new_size) + return true; + + delete m_psend_data; + m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + new_size-1]; + m_psend_data->TotalBuffBytes = new_size; + LOG_PRINT("Connection buffer resized up to " << new_size, LOG_LEVEL_3); + return true; + } + + + SOCKET m_sock; + net_utils::connection_context_base context; + TProtocol m_tprotocol_handler; + typename TProtocol::config_type m_dummy_config; + io_data_base* m_precv_data; + io_data_base* m_psend_data; + HANDLE m_completion_port; + volatile LONG m_asked_to_shutdown; + volatile LONG m_connection_shutwoned; + }; +PRAGMA_WARNING_POP + + bool worker_thread_member(); + static unsigned CALLBACK worker_thread(void* param); + + bool add_new_connection(SOCKET new_sock, long ip_from, int port_from); + bool shutdown_connection(connection<TProtocol>* pconn); + + + typedef std::map<SOCKET, boost::shared_ptr<connection<TProtocol> > > connections_container; + SOCKET m_listen_socket; + HANDLE m_completion_port; + connections_container m_connections; + critical_section m_connections_lock; + int m_port; + volatile LONG m_stop; + //abstract_handler* m_phandler; + bool m_initialized; + volatile LONG m_worker_thread_counter; + typename TProtocol::config_type m_config; + }; +} +} +#include "abstract_tcp_server_cp.inl" + + +#endif //_LEVIN_SERVER_H_ diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.inl b/contrib/epee/include/net/abstract_tcp_server_cp.inl new file mode 100644 index 000000000..5673c50be --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server_cp.inl @@ -0,0 +1,605 @@ +// 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 comment(lib, "Ws2_32.lib") + +namespace epee +{ +namespace net_utils +{ +template<class TProtocol> +cp_server_impl<TProtocol>::cp_server_impl(): + m_port(0), m_stop(false), + m_worker_thread_counter(0), m_listen_socket(INVALID_SOCKET) +{ +} +//------------------------------------------------------------- +template<class TProtocol> +cp_server_impl<TProtocol>::~cp_server_impl() +{ + deinit_server(); +} +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::init_server(int port_no) +{ + m_port = port_no; + + WSADATA wsad = {0}; + int err = ::WSAStartup(MAKEWORD(2,2), &wsad); + if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 ) + { + LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + m_initialized = true; + + m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); + if(INVALID_SOCKET == m_listen_socket) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + + int opt = 1; + err = setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast<char*>(&opt), sizeof(int)); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to setsockopt(SO_REUSEADDR), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); + deinit_server(); + return false; + } + + + sockaddr_in adr = {0}; + adr.sin_family = AF_INET; + adr.sin_addr.s_addr = htonl(INADDR_ANY); + adr.sin_port = (u_short)htons(m_port); + + //binding + err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr )); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); + deinit_server(); + return false; + } + + + m_completion_port = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if(INVALID_HANDLE_VALUE == m_completion_port) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to CreateIoCompletionPort, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); + deinit_server(); + return false; + } + + + return true; +} +//------------------------------------------------------------- + +//------------------------------------------------------------- +static int CALLBACK CPConditionFunc( + IN LPWSABUF lpCallerId, + IN LPWSABUF lpCallerData, + IN OUT LPQOS lpSQOS, + IN OUT LPQOS lpGQOS, + IN LPWSABUF lpCalleeId, + OUT LPWSABUF lpCalleeData, + OUT GROUP FAR *g, + IN DWORD_PTR dwCallbackData + ) +{ + + /*cp_server_impl* pthis = (cp_server_impl*)dwCallbackData; + if(!pthis) + return CF_REJECT;*/ + /*if(pthis->get_active_connections_num()>=FD_SETSIZE-1) + { + LOG_PRINT("Maximum connections count overfull.", LOG_LEVEL_2); + return CF_REJECT; + }*/ + + return CF_ACCEPT; +} +//------------------------------------------------------------- +template<class TProtocol> +size_t cp_server_impl<TProtocol>::get_active_connections_num() +{ + return m_connections.size(); +} +//------------------------------------------------------------- +template<class TProtocol> +unsigned CALLBACK cp_server_impl<TProtocol>::worker_thread(void* param) +{ + if(!param) + return 0; + + cp_server_impl<TProtocol>* pthis = (cp_server_impl<TProtocol>*)param; + pthis->worker_thread_member(); + return 1; +} +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::worker_thread_member() +{ + LOG_PRINT("Worker thread STARTED", LOG_LEVEL_1); + bool stop_handling = false; + while(!stop_handling) + { + PROFILE_FUNC("[worker_thread]Worker Loop"); + DWORD bytes_transfered = 0; + connection<TProtocol>* pconnection = 0; + io_data_base* pio_data = 0; + + { + PROFILE_FUNC("[worker_thread]GetQueuedCompletionStatus"); + BOOL res = ::GetQueuedCompletionStatus (m_completion_port, &bytes_transfered , (PULONG_PTR)&pconnection, (LPOVERLAPPED *)&pio_data, INFINITE); + if (res == 0) + { + // check return code for error + int err = GetLastError(); + LOG_PRINT("GetQueuedCompletionStatus failed with error " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_1); + + if(pio_data) + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + + + continue; + } + } + + if(pio_data) + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + + + + if(!bytes_transfered && !pconnection && !pio_data) + { + //signal to stop + break; + } + if(!pconnection || !pio_data) + { + LOG_PRINT("BIG FAIL: pconnection or pio_data is empty: pconnection=" << pconnection << " pio_data=" << pio_data, LOG_LEVEL_0); + break; + } + + + + if(::InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)) + { + LOG_ERROR("InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)"); + //DebugBreak(); + } + + if(pio_data->m_op_type == op_type_stop) + { + if(!pconnection) + { + LOG_ERROR("op_type=op_type_stop, but pconnection is empty!!!"); + continue; + } + shutdown_connection(pconnection); + continue;// + } + else if(pio_data->m_op_type == op_type_send) + { + continue; + //do nothing, just queuing request + }else if(pio_data->m_op_type == op_type_recv) + { + PROFILE_FUNC("[worker_thread]m_tprotocol_handler.handle_recv"); + if(bytes_transfered) + { + bool res = pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_transfered); + if(!res) + pconnection->query_shutdown(); + } + else + { + pconnection->query_shutdown(); + continue; + } + + } + + //preparing new request, + + { + PROFILE_FUNC("[worker_thread]RECV Request small loop"); + int res = 0; + while(true) + { + LOG_PRINT("Prepearing data for WSARecv....", LOG_LEVEL_3); + ZeroMemory(&pio_data->m_overlapped, sizeof(OVERLAPPED)); + pio_data->DataBuf.len = pio_data->TotalBuffBytes; + pio_data->DataBuf.buf = pio_data->Buffer; + pio_data->m_op_type = op_type_recv; + //calling WSARecv() and go to completion waiting + DWORD bytes_recvd = 0; + DWORD flags = 0; + + LOG_PRINT("Calling WSARecv....", LOG_LEVEL_3); + ::InterlockedExchange(&pio_data->m_is_in_use, 1); + res = WSARecv(pconnection->m_sock, &(pio_data->DataBuf), 1, &bytes_recvd , &flags, &(pio_data->m_overlapped), NULL); + if(res == SOCKET_ERROR ) + { + int err = ::WSAGetLastError(); + if(WSA_IO_PENDING == err ) + {//go pending, ok + LOG_PRINT("WSARecv return WSA_IO_PENDING", LOG_LEVEL_3); + break; + } + LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err); + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + pconnection->query_shutdown(); + break; + } + break; + /*else if(0 == res) + { + if(!bytes_recvd) + { + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + LOG_PRINT("WSARecv return 0, bytes_recvd=0, graceful close.", LOG_LEVEL_3); + int err = ::WSAGetLastError(); + //LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err); + //pconnection->query_shutdown(); + break; + }else + { + LOG_PRINT("WSARecv return immediatily 0, bytes_recvd=" << bytes_recvd, LOG_LEVEL_3); + //pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_recvd); + } + }*/ + } + } + } + + + LOG_PRINT("Worker thread STOPED", LOG_LEVEL_1); + ::InterlockedDecrement(&m_worker_thread_counter); + return true; +} +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::shutdown_connection(connection<TProtocol>* pconn) +{ + PROFILE_FUNC("[shutdown_connection]"); + + if(!pconn) + { + LOG_ERROR("Attempt to remove null pptr connection!"); + return false; + } + else + { + LOG_PRINT("Shutting down connection ("<< pconn << ")", LOG_LEVEL_3); + } + m_connections_lock.lock(); + connections_container::iterator it = m_connections.find(pconn->m_sock); + m_connections_lock.unlock(); + if(it == m_connections.end()) + { + LOG_ERROR("Failed to find closing socket=" << pconn->m_sock); + return false; + } + SOCKET sock = it->second->m_sock; + { + PROFILE_FUNC("[shutdown_connection] shutdown, close"); + ::shutdown(it->second->m_sock, SD_SEND ); + } + size_t close_sock_wait_count = 0; + { + LOG_PRINT("Entered to 'in_use wait zone'", LOG_LEVEL_3); + PROFILE_FUNC("[shutdown_connection] wait for in_use"); + while(::InterlockedCompareExchange(&it->second->m_precv_data->m_is_in_use, 1, 1)) + { + + Sleep(100); + close_sock_wait_count++; + } + LOG_PRINT("First step to 'in_use wait zone'", LOG_LEVEL_3); + + + while(::InterlockedCompareExchange(&it->second->m_psend_data->m_is_in_use, 1, 1)) + { + Sleep(100); + close_sock_wait_count++; + } + LOG_PRINT("Leaved 'in_use wait zone'", LOG_LEVEL_3); + } + + ::closesocket(it->second->m_sock); + + ::InterlockedExchange(&it->second->m_connection_shutwoned, 1); + m_connections_lock.lock(); + m_connections.erase(it); + m_connections_lock.unlock(); + LOG_PRINT("Socked " << sock << " closed, wait_count=" << close_sock_wait_count, LOG_LEVEL_2); + return true; +} +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::run_server(int threads_count = 0) +{ + int err = listen(m_listen_socket, 100); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + if(!threads_count) + { + SYSTEM_INFO si = {0}; + ::GetSystemInfo(&si); + threads_count = si.dwNumberOfProcessors + 2; + } + for(int i = 0; i != threads_count; i++) + { + boost::thread(boost::bind(&cp_server_impl::worker_thread_member, this)); + //HANDLE h_thread = threads_helper::create_thread(worker_thread, this); + InterlockedIncrement(&m_worker_thread_counter); + //::CloseHandle(h_thread); + } + + LOG_PRINT("Numbers of worker threads started: " << threads_count, LOG_LEVEL_1); + + m_stop = false; + while(!m_stop) + { + PROFILE_FUNC("[run_server] main_loop"); + TIMEVAL tv = {0}; + tv.tv_sec = 0; + tv.tv_usec = 100; + fd_set sock_set; + sock_set.fd_count = 1; + sock_set.fd_array[0] = m_listen_socket; + int select_res = 0; + { + PROFILE_FUNC("[run_server] select"); + select_res = select(0, &sock_set, &sock_set, NULL, &tv); + } + + if(SOCKET_ERROR == select_res) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to select, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + if(!select_res) + { + on_net_idle(); + continue; + } + else + { + sockaddr_in adr_from = {0}; + int adr_len = sizeof(adr_from); + SOCKET new_sock = INVALID_SOCKET; + { + PROFILE_FUNC("[run_server] WSAAccept"); + new_sock = ::WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, CPConditionFunc, (DWORD_PTR)this); + } + + if(INVALID_SOCKET == new_sock) + { + if(m_stop) + break; + int err = ::WSAGetLastError(); + LOG_PRINT("Failed to WSAAccept, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); + continue; + } + LOG_PRINT("Accepted connection (new socket=" << new_sock << ")", LOG_LEVEL_2); + { + PROFILE_FUNC("[run_server] Add new connection"); + add_new_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port); + } + + } + + } + LOG_PRINT("Closing connections("<< m_connections.size() << ") and waiting...", LOG_LEVEL_2); + m_connections_lock.lock(); + for(connections_container::iterator it = m_connections.begin(); it != m_connections.end(); it++) + { + ::shutdown(it->second->m_sock, SD_BOTH); + ::closesocket(it->second->m_sock); + } + m_connections_lock.unlock(); + size_t wait_count = 0; + while(m_connections.size() && wait_count < 100) + { + ::Sleep(100); + wait_count++; + } + LOG_PRINT("Connections closed OK (wait_count=" << wait_count << ")", LOG_LEVEL_2); + + + LOG_PRINT("Stopping worker threads("<< m_worker_thread_counter << ").", LOG_LEVEL_2); + for(int i = 0; i<m_worker_thread_counter; i++) + { + ::PostQueuedCompletionStatus(m_completion_port, 0, 0, 0); + } + + wait_count = 0; + while(InterlockedCompareExchange(&m_worker_thread_counter, 0, 0) && wait_count < 100) + { + Sleep(100); + wait_count++; + } + + LOG_PRINT("Net Server STOPPED, wait_count = " << wait_count, LOG_LEVEL_1); + return true; +} +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::add_new_connection(SOCKET new_sock, long ip_from, int port_from) +{ + PROFILE_FUNC("[add_new_connection]"); + + LOG_PRINT("Add new connection zone: entering lock", LOG_LEVEL_3); + m_connections_lock.lock(); + + boost::shared_ptr<connection<TProtocol> > ptr; + ptr.reset(new connection<TProtocol>(m_config)); + + connection<TProtocol>& conn = *ptr.get(); + m_connections[new_sock] = ptr; + LOG_PRINT("Add new connection zone: leaving lock", LOG_LEVEL_3); + m_connections_lock.unlock(); + conn.init_buffers(); + conn.m_sock = new_sock; + conn.context.m_remote_ip = ip_from; + conn.context.m_remote_port = port_from; + conn.m_completion_port = m_completion_port; + { + PROFILE_FUNC("[add_new_connection] CreateIoCompletionPort"); + ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0); + } + + //if(NULL == ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0)) + //{ + // int err = ::GetLastError(); + // LOG_PRINT("Failed to CreateIoCompletionPort(associate socket and completion port), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); + // return false; + //} + + conn.m_tprotocol_handler.after_init_connection(); + { + PROFILE_FUNC("[add_new_connection] starting loop"); + int res = 0; + while(true)//res!=SOCKET_ERROR) + { + PROFILE_FUNC("[add_new_connection] in loop time"); + conn.m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; + ZeroMemory(&conn.m_precv_data->m_overlapped, sizeof(OVERLAPPED)); + conn.m_precv_data->DataBuf.len = conn.m_precv_data->TotalBuffBytes; + conn.m_precv_data->DataBuf.buf = conn.m_precv_data->Buffer; + conn.m_precv_data->m_op_type = op_type_recv; + InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1); + DWORD bytes_recvd = 0; + DWORD flags = 0; + + ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1); + { + PROFILE_FUNC("[add_new_connection] ::WSARecv"); + res = ::WSARecv(conn.m_sock, &(conn.m_precv_data->DataBuf), 1, &bytes_recvd , &flags, &(conn.m_precv_data->m_overlapped), NULL); + } + if(res == SOCKET_ERROR ) + { + int err = ::WSAGetLastError(); + if(WSA_IO_PENDING == err ) + { + break; + } + LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err << " " << log_space::get_win32_err_descr(err)); + ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0); + conn.query_shutdown(); + //shutdown_connection(&conn); + break; + } + + + break; + /*else if(0 == res) + { + if(!bytes_recvd) + { + PROFILE_FUNC("[add_new_connection] shutdown_connection"); + ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0); + conn.query_shutdown(); + //shutdown_connection(&conn); + break; + }else + { + PROFILE_FUNC("[add_new_connection] handle_recv"); + } + }*/ + } + } + + + + return true; +} +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::deinit_server() +{ + if(!m_initialized) + return true; + + if(INVALID_SOCKET != m_listen_socket) + { + shutdown(m_listen_socket, SD_BOTH); + int res = closesocket(m_listen_socket); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_listen_socket = INVALID_SOCKET; + } + + int res = ::WSACleanup(); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_initialized = false; + + return true; +} + +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::send_stop_signal() +{ + ::InterlockedExchange(&m_stop, 1); + return true; +} +//------------------------------------------------------------- +template<class TProtocol> +bool cp_server_impl<TProtocol>::is_stop_signal() +{ + return m_stop?true:false; +} +//------------------------------------------------------------- +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h new file mode 100644 index 000000000..6de537a4c --- /dev/null +++ b/contrib/epee/include/net/http_base.h @@ -0,0 +1,184 @@ +// 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 <boost/lexical_cast.hpp> +#include <boost/regex.hpp> + +#include "string_tools.h" +namespace epee +{ +namespace net_utils +{ + namespace http + { + + enum http_method{ + http_method_get, + http_method_post, + http_method_put, + http_method_head, + http_method_etc, + http_method_unknown + }; + + enum http_content_type + { + http_content_type_text_html, + http_content_type_image_gif, + http_content_type_other, + http_content_type_not_set + }; + + typedef std::list<std::pair<std::string, std::string> > fields_list; + + inline + std::string get_value_from_fields_list(const std::string& param_name, const net_utils::http::fields_list& fields) + { + fields_list::const_iterator it = fields.begin(); + for(; it != fields.end(); it++) + if(!string_tools::compare_no_case(param_name, it->first)) + break; + + if(it==fields.end()) + return std::string(); + + return it->second; + } + + + inline + std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri) + { + std::string buff = "([\\?|&])"; + buff += param_name + "=([^&]*)"; + boost::regex match_param(buff.c_str(), boost::regex::icase | boost::regex::normal); + boost::smatch result; + if(boost::regex_search(uri, result, match_param, boost::match_default) && result[0].matched) + { + return result[2]; + } + return std::string(); + } + + + + struct http_header_info + { + std::string m_connection; //"Connection:" + std::string m_referer; //"Referer:" + std::string m_content_length; //"Content-Length:" + std::string m_content_type; //"Content-Type:" + std::string m_transfer_encoding;//"Transfer-Encoding:" + std::string m_content_encoding; //"Content-Encoding:" + std::string m_host; //"Host:" + std::string m_cookie; //"Cookie:" + fields_list m_etc_fields; + + void clear() + { + m_connection.clear(); + m_referer.clear(); + m_content_length.clear(); + m_content_type.clear(); + m_transfer_encoding.clear(); + m_content_encoding.clear(); + m_host.clear(); + m_cookie.clear(); + m_etc_fields.clear(); + } + }; + + struct uri_content + { + std::string m_path; + std::string m_query; + std::string m_fragment; + std::list<std::pair<std::string, std::string> > m_query_params; + }; + + struct url_content + { + std::string schema; + std::string host; + std::string uri; + boost::uint64_t port; + uri_content m_uri_content; + }; + + + struct http_request_info + { + http_request_info():m_http_method(http_method_unknown), + m_http_ver_hi(0), + m_http_ver_lo(0), + m_have_to_block(false) + {} + + http_method m_http_method; + std::string m_URI; + std::string m_http_method_str; + std::string m_full_request_str; + std::string m_replace_html; + std::string m_request_head; + int m_http_ver_hi; + int m_http_ver_lo; + bool m_have_to_block; + http_header_info m_header_info; + uri_content m_uri_content; + size_t m_full_request_buf_size; + std::string m_body; + + void clear() + { + this->~http_request_info(); + new(this) http_request_info(); + } + }; + + + struct http_response_info + { + int m_response_code; + std::string m_response_comment; + fields_list m_additional_fields; + std::string m_body; + std::string m_mime_tipe; + http_header_info m_header_info; + int m_http_ver_hi;// OUT paramter only + int m_http_ver_lo;// OUT paramter only + + void clear() + { + this->~http_response_info(); + new(this) http_response_info(); + } + }; + } +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h new file mode 100644 index 000000000..4a88c06ef --- /dev/null +++ b/contrib/epee/include/net/http_client.h @@ -0,0 +1,875 @@ +// 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 <boost/shared_ptr.hpp> +#include <boost/regex.hpp> +#include <boost/lexical_cast.hpp> +//#include <mbstring.h> +#include <algorithm> +#include <cctype> +#include <functional> + +#include "net_helper.h" +#include "http_client_base.h" + +#ifdef HTTP_ENABLE_GZIP +#include "gzip_encoding.h" +#endif + +#include "string_tools.h" +#include "reg_exp_definer.h" +#include "http_base.h" +#include "to_nonconst_iterator.h" +#include "net_parse_helpers.h" + +//#include "shlwapi.h" + +//#pragma comment(lib, "shlwapi.lib") + +extern epee::critical_section gregexp_lock; + + +namespace epee +{ +namespace net_utils +{ + +using namespace std; + + /*struct url + { + public: + void parse(const std::string& url_s) + { + const string prot_end("://"); + string::const_iterator prot_i = search(url_s.begin(), url_s.end(), + prot_end.begin(), prot_end.end()); + protocol_.reserve(distance(url_s.begin(), prot_i)); + transform(url_s.begin(), prot_i, + back_inserter(protocol_), + ptr_fun<int,int>(tolower)); // protocol is icase + if( prot_i == url_s.end() ) + return; + advance(prot_i, prot_end.length()); + string::const_iterator path_i = find(prot_i, url_s.end(), '/'); + host_.reserve(distance(prot_i, path_i)); + transform(prot_i, path_i, + back_inserter(host_), + ptr_fun<int,int>(tolower)); // host is icase + string::const_iterator query_i = find(path_i, url_s.end(), '?'); + path_.assign(path_i, query_i); + if( query_i != url_s.end() ) + ++query_i; + query_.assign(query_i, url_s.end()); + } + + std::string protocol_; + std::string host_; + std::string path_; + std::string query_; + };*/ + + + + + //--------------------------------------------------------------------------- + static inline const char* get_hex_vals() + { + static char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + return hexVals; + } + + static inline const char* get_unsave_chars() + { + //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&"; + static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; + return unsave_chars; + } + + static inline bool is_unsafe(unsigned char compare_char) + { + if(compare_char <= 32 || compare_char >= 123) + return true; + + const char* punsave = get_unsave_chars(); + + for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++) + if(compare_char == punsave[ichar_pos]) + return true; + + return false; + } + + static inline + std::string dec_to_hex(char num, int radix) + { + int temp=0; + std::string csTmp; + int num_char; + + num_char = (int) num; + if (num_char < 0) + num_char = 256 + num_char; + + while (num_char >= radix) + { + temp = num_char % radix; + num_char = (int)floor((float)num_char / (float)radix); + csTmp = get_hex_vals()[temp]; + } + + csTmp += get_hex_vals()[num_char]; + + if(csTmp.size() < 2) + { + csTmp += '0'; + } + + std::reverse(csTmp.begin(), csTmp.end()); + //_mbsrev((unsigned char*)csTmp.data()); + + return csTmp; + } + + static inline std::string convert(char val) + { + std::string csRet; + csRet += "%"; + csRet += dec_to_hex(val, 16); + return csRet; + } + static inline std::string conver_to_url_format(const std::string& uri) + { + + std::string result; + + for(size_t i = 0; i!= uri.size(); i++) + { + if(is_unsafe(uri[i])) + result += convert(uri[i]); + else + result += uri[i]; + + } + + return result; + } + + static inline std::string convert_to_url_format_force_all(const std::string& uri) + { + + std::string result; + + for(size_t i = 0; i!= uri.size(); i++) + { + result += convert(uri[i]); + } + + return result; + } + + + + + + namespace http + { + + class http_simple_client: public i_target_handler + { + public: + + + private: + enum reciev_machine_state + { + reciev_machine_state_header, + reciev_machine_state_body_content_len, + reciev_machine_state_body_connection_close, + reciev_machine_state_body_chunked, + reciev_machine_state_done, + reciev_machine_state_error + }; + + + + enum chunked_state{ + http_chunked_state_chunk_head, + http_chunked_state_chunk_body, + http_chunked_state_done, + http_chunked_state_undefined + }; + + + blocked_mode_client m_net_client; + std::string m_host_buff; + std::string m_port; + unsigned int m_timeout; + std::string m_header_cache; + http_response_info m_response_info; + size_t m_len_in_summary; + size_t m_len_in_remain; + //std::string* m_ptarget_buffer; + boost::shared_ptr<i_sub_handler> m_pcontent_encoding_handler; + reciev_machine_state m_state; + chunked_state m_chunked_state; + std::string m_chunked_cache; + critical_section m_lock; + + public: + void set_host_name(const std::string& name) + { + CRITICAL_REGION_LOCAL(m_lock); + m_host_buff = name; + } + bool connect(const std::string& host, int port, unsigned int timeout) + { + return connect(host, std::to_string(port), timeout); + } + bool connect(const std::string& host, const std::string& port, unsigned int timeout) + { + CRITICAL_REGION_LOCAL(m_lock); + m_host_buff = host; + m_port = port; + m_timeout = timeout; + + return m_net_client.connect(host, port, timeout, timeout); + } + //--------------------------------------------------------------------------- + bool disconnect() + { + CRITICAL_REGION_LOCAL(m_lock); + return m_net_client.disconnect(); + } + //--------------------------------------------------------------------------- + bool is_connected() + { + CRITICAL_REGION_LOCAL(m_lock); + return m_net_client.is_connected(); + } + //--------------------------------------------------------------------------- + virtual bool handle_target_data(std::string& piece_of_transfer) + { + CRITICAL_REGION_LOCAL(m_lock); + m_response_info.m_body += piece_of_transfer; + piece_of_transfer.clear(); + return true; + } + //--------------------------------------------------------------------------- + inline + bool invoke_get(const std::string& uri, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + { + CRITICAL_REGION_LOCAL(m_lock); + return invoke(uri, "GET", body, ppresponse_info, additional_params); + } + + //--------------------------------------------------------------------------- + inline bool invoke(const std::string& uri, const std::string& method, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!is_connected()) + { + LOG_PRINT("Reconnecting...", LOG_LEVEL_3); + if(!connect(m_host_buff, m_port, m_timeout)) + { + LOG_PRINT("Failed to connect to " << m_host_buff << ":" << m_port, LOG_LEVEL_3); + return false; + } + } + m_response_info.clear(); + std::string req_buff = method + " "; + req_buff += uri + " HTTP/1.1\r\n" + + "Host: "+ m_host_buff +"\r\n" + "Content-Length: " + boost::lexical_cast<std::string>(body.size()) + "\r\n"; + + + //handle "additional_params" + for(fields_list::const_iterator it = additional_params.begin(); it!=additional_params.end(); it++) + req_buff += it->first + ": " + it->second + "\r\n"; + req_buff += "\r\n"; + //-- + + bool res = m_net_client.send(req_buff); + CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); + if(body.size()) + res = m_net_client.send(body); + CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); + + if(ppresponse_info) + *ppresponse_info = &m_response_info; + + m_state = reciev_machine_state_header; + return handle_reciev(); + } + //--------------------------------------------------------------------------- + inline bool invoke_post(const std::string& uri, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + { + CRITICAL_REGION_LOCAL(m_lock); + return invoke(uri, "POST", body, ppresponse_info, additional_params); + } + private: + //--------------------------------------------------------------------------- + inline bool handle_reciev() + { + CRITICAL_REGION_LOCAL(m_lock); + bool keep_handling = true; + bool need_more_data = true; + std::string recv_buffer; + while(keep_handling) + { + if(need_more_data) + { + if(!m_net_client.recv(recv_buffer)) + { + LOG_PRINT("Unexpected reciec fail", LOG_LEVEL_3); + m_state = reciev_machine_state_error; + } + if(!recv_buffer.size()) + { + //connection is going to be closed + if(reciev_machine_state_body_connection_close != m_state) + { + m_state = reciev_machine_state_error; + } + } + need_more_data = false; + } + switch(m_state) + { + case reciev_machine_state_header: + keep_handling = handle_header(recv_buffer, need_more_data); + break; + case reciev_machine_state_body_content_len: + keep_handling = handle_body_content_len(recv_buffer, need_more_data); + break; + case reciev_machine_state_body_connection_close: + keep_handling = handle_body_connection_close(recv_buffer, need_more_data); + break; + case reciev_machine_state_body_chunked: + keep_handling = handle_body_body_chunked(recv_buffer, need_more_data); + break; + case reciev_machine_state_done: + keep_handling = false; + break; + case reciev_machine_state_error: + keep_handling = false; + break; + } + + } + m_header_cache.clear(); + if(m_state != reciev_machine_state_error) + { + if(m_response_info.m_header_info.m_connection.size() && !string_tools::compare_no_case("close", m_response_info.m_header_info.m_connection)) + disconnect(); + + return true; + } + else + { + LOG_PRINT_L3("Returning false because of wrong state machine. state: " << m_state); + return false; + } + } + //--------------------------------------------------------------------------- + inline + bool handle_header(std::string& recv_buff, bool& need_more_data) + { + + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + LOG_ERROR("Connection closed at handle_header"); + m_state = reciev_machine_state_error; + return false; + } + + m_header_cache += recv_buff; + recv_buff.clear(); + std::string::size_type pos = m_header_cache.find("\r\n\r\n"); + if(pos != std::string::npos) + { + recv_buff.assign(m_header_cache.begin()+pos+4, m_header_cache.end()); + m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end()); + + analize_cached_header_and_invoke_state(); + m_header_cache.clear(); + if(!recv_buff.size() && (m_state != reciev_machine_state_error && m_state != reciev_machine_state_done)) + need_more_data = true; + + return true; + }else + need_more_data = true; + return true; + } + //--------------------------------------------------------------------------- + inline + bool handle_body_content_len(std::string& recv_buff, bool& need_more_data) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + LOG_PRINT("Warning: Content-Len mode, but connection unexpectedly closed", LOG_LEVEL_3); + m_state = reciev_machine_state_done; + return true; + } + CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_len_in_remain >= recv_buff.size()"); + m_len_in_remain -= recv_buff.size(); + m_pcontent_encoding_handler->update_in(recv_buff); + + if(m_len_in_remain == 0) + m_state = reciev_machine_state_done; + else + need_more_data = true; + + return true; + } + //--------------------------------------------------------------------------- + inline + bool handle_body_connection_close(std::string& recv_buff, bool& need_more_data) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + m_state = reciev_machine_state_done; + return true; + } + need_more_data = true; + m_pcontent_encoding_handler->update_in(recv_buff); + + + return true; + } + //--------------------------------------------------------------------------- + inline bool is_hex_symbol(char ch) + { + + if( (ch >= '0' && ch <='9')||(ch >= 'A' && ch <='F')||(ch >= 'a' && ch <='f')) + return true; + else + return false; + } + //--------------------------------------------------------------------------- + inline + bool get_len_from_chunk_head(const std::string &chunk_head, size_t& result_size) + { + std::stringstream str_stream; + str_stream << std::hex; + if(!(str_stream << chunk_head && str_stream >> result_size)) + return false; + + return true; + } + //--------------------------------------------------------------------------- + inline + bool get_chunk_head(std::string& buff, size_t& chunk_size, bool& is_matched) + { + is_matched = false; + size_t offset = 0; + for(std::string::iterator it = buff.begin(); it!= buff.end(); it++, offset++) + { + if(!is_hex_symbol(*it)) + { + if(*it == '\r' || *it == ' ' ) + { + offset--; + continue; + } + else if(*it == '\n') + { + std::string chunk_head = buff.substr(0, offset); + if(!get_len_from_chunk_head(chunk_head, chunk_size)) + return false; + + if(0 == chunk_size) + { + //Here is a small confusion + //In breif - if the chunk is the last one we need to get terminating sequence + //along with the cipher, generally in the "ddd\r\n\r\n" form + + for(it++;it != buff.end(); it++) + { + if('\r' == *it) + continue; + else if('\n' == *it) + break; + else + { + LOG_ERROR("http_stream_filter: Wrong last chunk terminator"); + return false; + } + } + + if(it == buff.end()) + return true; + } + + buff.erase(buff.begin(), ++it); + + is_matched = true; + return true; + } + else + return false; + } + } + + return true; + } + //--------------------------------------------------------------------------- + inline + bool handle_body_body_chunked(std::string& recv_buff, bool& need_more_data) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + LOG_PRINT("Warning: CHUNKED mode, but connection unexpectedly closed", LOG_LEVEL_3); + m_state = reciev_machine_state_done; + return true; + } + m_chunked_cache += recv_buff; + recv_buff.clear(); + bool is_matched = false; + + while(true) + { + if(!m_chunked_cache.size()) + { + need_more_data = true; + break; + } + + switch(m_chunked_state) + { + case http_chunked_state_chunk_head: + if(m_chunked_cache[0] == '\n' || m_chunked_cache[0] == '\r') + { + //optimize a bit + if(m_chunked_cache[0] == '\r' && m_chunked_cache.size()>1 && m_chunked_cache[1] == '\n') + m_chunked_cache.erase(0, 2); + else + m_chunked_cache.erase(0, 1); + break; + } + if(!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched)) + { + LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache); + m_state = reciev_machine_state_error; + return false; + } + + if(!is_matched) + { + need_more_data = true; + return true; + }else + { + m_chunked_state = http_chunked_state_chunk_body; + if(m_len_in_remain == 0) + {//last chunk, let stop the stream and fix the chunk queue. + m_state = reciev_machine_state_done; + return true; + } + m_chunked_state = http_chunked_state_chunk_body; + break; + } + break; + case http_chunked_state_chunk_body: + { + std::string chunk_body; + if(m_len_in_remain >= m_chunked_cache.size()) + { + m_len_in_remain -= m_chunked_cache.size(); + chunk_body.swap(m_chunked_cache); + }else + { + chunk_body.assign(m_chunked_cache, 0, m_len_in_remain); + m_chunked_cache.erase(0, m_len_in_remain); + m_len_in_remain = 0; + } + + m_pcontent_encoding_handler->update_in(chunk_body); + + if(!m_len_in_remain) + m_chunked_state = http_chunked_state_chunk_head; + } + break; + case http_chunked_state_done: + m_state = reciev_machine_state_done; + return true; + case http_chunked_state_undefined: + default: + LOG_ERROR("http_stream_filter::handle_chunked(): Wrong state" << m_chunked_state); + return false; + } + } + + return true; + } + //--------------------------------------------------------------------------- + inline + bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process) + { + LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_4); + + STATIC_REGEXP_EXPR_1(rexp_mach_field, + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)" + // 12 3 4 5 6 7 8 9 + "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", + //10 1112 13 + boost::regex::icase | boost::regex::normal); + + boost::smatch result; + std::string::const_iterator it_current_bound = m_cache_to_process.begin(); + std::string::const_iterator it_end_bound = m_cache_to_process.end(); + + + + //lookup all fields and fill well-known fields + while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + { + const size_t field_val = 12; + //const size_t field_etc_name = 10; + + int i = 2; //start position = 2 + if(result[i++].matched)//"Connection" + body_info.m_connection = result[field_val]; + else if(result[i++].matched)//"Referrer" + body_info.m_referer = result[field_val]; + else if(result[i++].matched)//"Content-Length" + body_info.m_content_length = result[field_val]; + else if(result[i++].matched)//"Content-Type" + body_info.m_content_type = result[field_val]; + else if(result[i++].matched)//"Transfer-Encoding" + body_info.m_transfer_encoding = result[field_val]; + else if(result[i++].matched)//"Content-Encoding" + body_info.m_content_encoding = result[field_val]; + else if(result[i++].matched)//"Host" + { body_info.m_host = result[field_val]; + string_tools::trim(body_info.m_host); + } + else if(result[i++].matched)//"Cookie" + body_info.m_cookie = result[field_val]; + else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) + {;} + else + {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);} + + it_current_bound = result[(int)result.size()-1]. first; + } + return true; + + } + inline + bool analize_first_response_line() + { + + //First line response, look like this: "HTTP/1.1 200 OK" + STATIC_REGEXP_EXPR_1(rexp_match_first_response_line, "^HTTP/(\\d+).(\\d+) ((\\d)\\d{2})( [^\n]*)?\r?\n", boost::regex::icase | boost::regex::normal); + // 1 2 34 5 + //size_t match_len = 0; + boost::smatch result; + if(boost::regex_search( m_header_cache, result, rexp_match_first_response_line, boost::match_default) && result[0].matched) + { + CHECK_AND_ASSERT_MES(result[1].matched&&result[2].matched, false, "http_stream_filter::handle_invoke_reply_line() assert failed..."); + m_response_info.m_http_ver_hi = boost::lexical_cast<int>(result[1]); + m_response_info.m_http_ver_lo = boost::lexical_cast<int>(result[2]); + m_response_info.m_response_code = boost::lexical_cast<int>(result[3]); + + m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second)); + return true; + }else + { + LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache); + return false; + } + + } + inline + bool set_reply_content_encoder() + { + STATIC_REGEXP_EXPR_1(rexp_match_gzip, "^.*?((gzip)|(deflate))", boost::regex::icase | boost::regex::normal); + boost::smatch result; // 12 3 + if(boost::regex_search( m_response_info.m_header_info.m_content_encoding, result, rexp_match_gzip, boost::match_default) && result[0].matched) + { +#ifdef HTTP_ENABLE_GZIP + m_pcontent_encoding_handler.reset(new content_encoding_gzip(this, result[3].matched)); +#else + m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this)); + LOG_ERROR("GZIP encoding not supported in this build, please add zlib to your project and define HTTP_ENABLE_GZIP"); + return false; +#endif + } + else + { + m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this)); + } + + return true; + } + inline + bool analize_cached_header_and_invoke_state() + { + m_response_info.clear(); + analize_first_response_line(); + std::string fake_str; //gcc error workaround + + bool res = parse_header(m_response_info.m_header_info, m_header_cache); + CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache); + + set_reply_content_encoder(); + + m_len_in_summary = 0; + bool content_len_valid = false; + if(m_response_info.m_header_info.m_content_length.size()) + content_len_valid = string_tools::get_xtype_from_string(m_len_in_summary, m_response_info.m_header_info.m_content_length); + + + + if(!m_len_in_summary && ((m_response_info.m_response_code>=100&&m_response_info.m_response_code<200) + || 204 == m_response_info.m_response_code + || 304 == m_response_info.m_response_code) ) + {//There will be no response body, server will display the local page with error + m_state = reciev_machine_state_done; + return true; + }else if(m_response_info.m_header_info.m_transfer_encoding.size()) + { + string_tools::trim(m_response_info.m_header_info.m_transfer_encoding); + if(string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked")) + { + LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding); + m_state = reciev_machine_state_error; + return false; + } + m_state = reciev_machine_state_body_chunked; + m_chunked_state = http_chunked_state_chunk_head; + return true; + } + else if(!m_response_info.m_header_info.m_content_length.empty()) + { + //In the response header the length was specified + if(!content_len_valid) + { + LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_response_info.m_header_info.m_content_length); + m_state = reciev_machine_state_error; + return false; + } + if(!m_len_in_summary) + { + m_state = reciev_machine_state_done; + return true; + } + else + { + m_len_in_remain = m_len_in_summary; + m_state = reciev_machine_state_body_content_len; + return true; + } + }else if(!m_response_info.m_header_info.m_connection.empty() && is_connection_close_field(m_response_info.m_header_info.m_connection)) + { //By indirect signs we suspect that data transfer will end with a connection break + m_state = reciev_machine_state_body_connection_close; + }else if(is_multipart_body(m_response_info.m_header_info, fake_str)) + { + m_state = reciev_machine_state_error; + LOG_ERROR("Unsupported MULTIPART BODY."); + return false; + }else + { //Apparently there are no signs of the form of transfer, will receive data until the connection is closed + m_state = reciev_machine_state_error; + LOG_PRINT("Undefinded transfer type, consider http_body_transfer_connection_close method.", LOG_LEVEL_2); + return false; + } + return false; + } + inline + bool is_connection_close_field(const std::string& str) + { + STATIC_REGEXP_EXPR_1(rexp_match_close, "^\\s*close", boost::regex::icase | boost::regex::normal); + boost::smatch result; + if(boost::regex_search( str, result, rexp_match_close, boost::match_default) && result[0].matched) + return true; + else + return false; + } + inline + bool is_multipart_body(const http_header_info& head_info, OUT std::string& boundary) + { + //Check whether this is multi part - if yes, capture boundary immediately + STATIC_REGEXP_EXPR_1(rexp_match_multipart_type, "^\\s*multipart/([\\w\\-]+); boundary=((\"(.*?)\")|(\\\\\"(.*?)\\\\\")|([^\\s;]*))", boost::regex::icase | boost::regex::normal); + boost::smatch result; + if(boost::regex_search(head_info.m_content_type, result, rexp_match_multipart_type, boost::match_default) && result[0].matched) + { + if(result[4].matched) + boundary = result[4]; + else if(result[6].matched) + boundary = result[6]; + else if(result[7].matched) + boundary = result[7]; + else + { + LOG_ERROR("Failed to match boundary in content-type=" << head_info.m_content_type); + return false; + } + return true; + } + else + return false; + + return true; + } + }; + + + + /************************************************************************/ + /* */ + /************************************************************************/ + //inline + template<class t_transport> + bool invoke_request(const std::string& url, t_transport& tr, unsigned int timeout, const http_response_info** ppresponse_info, const std::string& method = "GET", const std::string& body = std::string(), const fields_list& additional_params = fields_list()) + { + http::url_content u_c; + bool res = parse_url(url, u_c); + + if(!tr.is_connected()) + { + CHECK_AND_ASSERT_MES(res, false, "failed to parse url: " << url); + + if(!u_c.port) + u_c.port = 80;//default for http + + res = tr.connect(u_c.host, static_cast<int>(u_c.port), timeout); + CHECK_AND_ASSERT_MES(res, false, "failed to connect " << u_c.host << ":" << u_c.port); + } + + return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params); + } + + } +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/http_client_abstract_invoke.h b/contrib/epee/include/net/http_client_abstract_invoke.h new file mode 100644 index 000000000..425a355ee --- /dev/null +++ b/contrib/epee/include/net/http_client_abstract_invoke.h @@ -0,0 +1,98 @@ + +// 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 "storages/serializeble_struct_helper.h" + +namespace epee +{ + namespace net_utils + { + namespace http + { + template<class TArg, class TResult, class TTransport> + bool invoke_http_json_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + StorageNamed::InMemStorageSpace::json::store_t_to_json(out_struct, req_param); + + const http_response_info* pri = NULL; + if(!invoke_request(url, transport, timeout, &pri, method, req_param)) + { + LOG_PRINT_L1("Failed to invoke http request to " << url); + return false; + } + + if(!pri->m_response_code) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); + return false; + } + + return StorageNamed::InMemStorageSpace::json::load_t_from_json(result_struct, pri->m_body); + } + + + + template<class TArg, class TResult, class TTransport> + bool invoke_http_bin_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + epee::StorageNamed::save_struct_as_storage_to_buff(out_struct, req_param); + + const http_response_info* pri = NULL; + if(!invoke_request(url, transport, timeout, &pri, method, req_param)) + { + LOG_PRINT_L1("Failed to invoke http request to " << url); + return false; + } + + if(!pri->m_response_code) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); + return false; + } + + return epee::StorageNamed::load_struct_from_storage_buff(result_struct, pri->m_body); + } + + + } + } +} diff --git a/contrib/epee/include/net/http_client_base.h b/contrib/epee/include/net/http_client_base.h new file mode 100644 index 000000000..571e27f73 --- /dev/null +++ b/contrib/epee/include/net/http_client_base.h @@ -0,0 +1,73 @@ +// 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 + +namespace epee +{ + namespace net_utils + { + struct i_sub_handler + { + virtual ~i_sub_handler(){} + + virtual bool update_in( std::string& piece_of_transfer)=0; + virtual void stop(std::string& OUT collect_remains)=0; + virtual bool update_and_stop(std::string& OUT collect_remains, bool& is_changed) + { + is_changed = true; + bool res = this->update_in(collect_remains); + if(res) + this->stop(collect_remains); + return res; + } + }; + + + struct i_target_handler + { + virtual ~i_target_handler(){} + virtual bool handle_target_data( std::string& piece_of_transfer)=0; + }; + + + class do_nothing_sub_handler: public i_sub_handler + { + public: + do_nothing_sub_handler(i_target_handler* powner_filter):m_powner_filter(powner_filter) + {} + virtual bool update_in( std::string& piece_of_transfer) + { + return m_powner_filter->handle_target_data(piece_of_transfer); + } + virtual void stop(std::string& OUT collect_remains) + { + + } + i_target_handler* m_powner_filter; + }; + } +}
\ No newline at end of file diff --git a/contrib/epee/include/net/http_client_via_api_helper.h b/contrib/epee/include/net/http_client_via_api_helper.h new file mode 100644 index 000000000..45a70993b --- /dev/null +++ b/contrib/epee/include/net/http_client_via_api_helper.h @@ -0,0 +1,177 @@ +// 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 <wininet.h> +#include <atlutil.h> +#pragma comment(lib, "Wininet.lib") + +namespace epee +{ +namespace net_utils +{ + inline + bool http_ssl_invoke(const std::string& url, const std::string usr, const std::string psw, std::string& http_response_body, bool use_post = false) + { + bool final_res = false; + + ATL::CUrl url_obj; + BOOL crack_rss = url_obj.CrackUrl(string_encoding::convert_to_t<std::basic_string<TCHAR> >(url).c_str()); + + HINTERNET hinet = ::InternetOpenA(SHARED_JOBSCOMMON_HTTP_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + if(!hinet) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetOpenA, \nError: " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + return false; + } + + DWORD dwFlags = 0; + DWORD dwBuffLen = sizeof(dwFlags); + + if(usr.size()) + { + dwFlags |= INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID| + INTERNET_FLAG_PRAGMA_NOCACHE | SECURITY_FLAG_IGNORE_UNKNOWN_CA|INTERNET_FLAG_SECURE; + }else + { + dwFlags |= INTERNET_FLAG_PRAGMA_NOCACHE; + } + + + int port = url_obj.GetPortNumber(); + BOOL res = FALSE; + + HINTERNET hsession = ::InternetConnectA(hinet, string_encoding::convert_to_ansii(url_obj.GetHostName()).c_str(), port/*INTERNET_DEFAULT_HTTPS_PORT*/, usr.c_str(), psw.c_str(), INTERNET_SERVICE_HTTP, dwFlags, NULL); + if(hsession) + { + const std::string uri = string_encoding::convert_to_ansii(url_obj.GetUrlPath()) + string_encoding::convert_to_ansii(url_obj.GetExtraInfo()); + + HINTERNET hrequest = ::HttpOpenRequestA(hsession, use_post?"POST":NULL, uri.c_str(), NULL, NULL,NULL, dwFlags, NULL); + if(hrequest) + { + while(true) + { + res = ::HttpSendRequestA(hrequest, NULL, 0, NULL, 0); + if(!res) + { + //ERROR_INTERNET_INVALID_CA 45 + //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5) + int err = ::GetLastError(); + LOG_PRINT("Failed to call HttpSendRequestA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + break; + } + + DWORD code = 0; + DWORD buf_len = sizeof(code); + DWORD index = 0; + res = ::HttpQueryInfo(hrequest, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &code, &buf_len, &index); + if(!res) + { + //ERROR_INTERNET_INVALID_CA 45 + //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5) + int err = ::GetLastError(); + LOG_PRINT("Failed to call HttpQueryInfo, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + break; + } + if(code < 200 || code > 299) + { + LOG_PRINT("Wrong server response, HttpQueryInfo returned statuse code" << code , LOG_LEVEL_0); + break; + } + + + char buff[100000] = {0}; + DWORD readed = 0; + while(true) + { + res = ::InternetReadFile(hrequest, buff, sizeof(buff), &readed); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetReadFile, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + break; + } + if(readed) + { + http_response_body.append(buff, readed); + } + else + break; + } + + if(!res) + break; + + + //we success + final_res = true; + + res = ::InternetCloseHandle(hrequest); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + + break; + } + } + else + { + //ERROR_INTERNET_INVALID_CA + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetOpenUrlA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + return false; + } + + res = ::InternetCloseHandle(hsession); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + }else + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetConnectA(" << string_encoding::convert_to_ansii(url_obj.GetHostName()) << ", port " << port << " \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + + + + res = ::InternetCloseHandle(hinet); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + return final_res; + } +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h new file mode 100644 index 000000000..4aebcf2aa --- /dev/null +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -0,0 +1,209 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_H_ +#define _HTTP_SERVER_H_ + +#include <string> +#include "net_utils_base.h" +#include "to_nonconst_iterator.h" +#include "http_base.h" + +namespace epee +{ +namespace net_utils +{ + namespace http + { + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct http_server_config + { + std::string m_folder; + critical_section m_lock; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class simple_http_connection_handler + { + public: + typedef net_utils::connection_context_base connection_context; + typedef http_server_config config_type; + + simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config); + virtual ~simple_http_connection_handler(){} + + bool release_protocol() + { + return true; + } + + virtual bool thread_init() + { + return true; + } + + virtual bool thread_deinit() + { + return true; + } + bool after_init_connection() + { + return true; + } + virtual bool handle_recv(const void* ptr, size_t cb); + virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response); + + + //temporary here + //bool parse_uri(const std::string uri, uri_content& content); + + private: + enum machine_state{ + http_state_retriving_comand_line, + http_state_retriving_header, + http_state_retriving_body, + http_state_connection_close, + http_state_error + }; + + enum body_transfer_type{ + http_body_transfer_chunked, + http_body_transfer_measure,//mean "Content-Length" valid + http_body_transfer_chunked_instead_measure, + http_body_transfer_connection_close, + http_body_transfer_multipart, + http_body_transfer_undefined + }; + + bool handle_buff_in(std::string& buf); + + bool analize_cached_request_header_and_invoke_state(size_t pos); + + bool handle_invoke_query_line(); + bool parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos); + std::string::size_type match_end_of_header(const std::string& buf); + bool get_len_from_content_lenght(const std::string& str, size_t& len); + bool handle_retriving_query_body(); + bool handle_query_measure(); + bool set_ready_state(); + bool slash_to_back_slash(std::string& str); + std::string get_file_mime_tipe(const std::string& path); + std::string get_response_header(const http_response_info& response); + + //major function + inline bool handle_request_and_send_response(const http::http_request_info& query_info); + + + std::string get_not_found_response_body(const std::string& URI); + + std::string m_root_path; + std::string m_cache; + machine_state m_state; + body_transfer_type m_body_transfer_type; + bool m_is_stop_handling; + http::http_request_info m_query_info; + size_t m_len_summary, m_len_remain; + config_type& m_config; + bool m_want_close; + protected: + i_service_endpoint* m_psnd_hndlr; + }; + + + struct i_http_server_handler + { + virtual ~i_http_server_handler(){} + virtual bool handle_http_request(const http_request_info& query_info, http_response_info& response, const net_utils::connection_context_base& m_conn_context)=0; + virtual bool init_server_thread(){return true;} + virtual bool deinit_server_thread(){return true;} + }; + + + struct custum_handler_config: public http_server_config + { + i_http_server_handler* m_phandler; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class http_custom_handler: public simple_http_connection_handler + { + public: + typedef custum_handler_config config_type; + + http_custom_handler(i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):simple_http_connection_handler(psnd_hndlr, config), + m_config(config), + m_conn_context(conn_context) + {} + inline bool handle_request(const http_request_info& query_info, http_response_info& response) + { + CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!"); + //fill with default values + response.m_mime_tipe = "text/plain"; + response.m_response_code = 200; + response.m_response_comment = "OK"; + response.m_body.clear(); + return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context); + } + + virtual bool thread_init() + { + return m_config.m_phandler->init_server_thread();; + } + + virtual bool thread_deinit() + { + return m_config.m_phandler->deinit_server_thread(); + } + void handle_qued_callback() + {} + bool after_init_connection() + { + return true; + } + + private: + //simple_http_connection_handler::config_type m_stub_config; + config_type& m_config; + const net_utils::connection_context_base& m_conn_context; + }; + } +} +} + +#include "http_protocol_handler.inl" + +#endif //_HTTP_SERVER_H_ diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl new file mode 100644 index 000000000..810c46db9 --- /dev/null +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -0,0 +1,664 @@ +// 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. +// + + +#include <boost/regex.hpp> +#include <boost/lexical_cast.hpp> +#include "http_protocol_handler.h" +#include "reg_exp_definer.h" +#include "string_tools.h" +#include "file_io_utils.h" +#include "net_parse_helpers.h" + +#define HTTP_MAX_URI_LEN 9000 +#define HTTP_MAX_HEADER_LEN 100000 + +namespace epee +{ +namespace net_utils +{ + namespace http + { + + struct multipart_entry + { + std::list<std::pair<std::string, std::string> > m_etc_header_fields; + std::string m_content_disposition; + std::string m_content_type; + std::string m_body; + }; + + inline + bool match_boundary(const std::string& content_type, std::string& boundary) + { + STATIC_REGEXP_EXPR_1(rexp_match_boundary, "boundary=(.*?)(($)|([;\\s,]))", boost::regex::icase | boost::regex::normal); + // 1 + boost::smatch result; + if(boost::regex_search(content_type, result, rexp_match_boundary, boost::match_default) && result[0].matched) + { + boundary = result[1]; + return true; + } + + return false; + } + + inline + bool parse_header(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry) + { + STATIC_REGEXP_EXPR_1(rexp_mach_field, + "\n?((Content-Disposition)|(Content-Type)" + // 12 3 + "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", + //4 56 7 + boost::regex::icase | boost::regex::normal); + + boost::smatch result; + std::string::const_iterator it_current_bound = it_begin; + std::string::const_iterator it_end_bound = it_end; + + //lookup all fields and fill well-known fields + while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + { + const size_t field_val = 6; + const size_t field_etc_name = 4; + + int i = 2; //start position = 2 + if(result[i++].matched)//"Content-Disposition" + entry.m_content_disposition = result[field_val]; + else if(result[i++].matched)//"Content-Type" + entry.m_content_type = result[field_val]; + else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) + entry.m_etc_header_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val])); + else + { + LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<<std::string(it_current_bound, it_end)); + } + + it_current_bound = result[(int)result.size()-1].first; + } + return true; + } + + inline + bool handle_part_of_multipart(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry) + { + std::string end_str = "\r\n\r\n"; + std::string::const_iterator end_header_it = std::search(it_begin, it_end, end_str.begin(), end_str.end()); + if(end_header_it == it_end) + { + //header not matched + return false; + } + + if(!parse_header(it_begin, end_header_it+4, entry)) + { + LOG_ERROR("Failed to parse header:" << std::string(it_begin, end_header_it+2)); + return false; + } + + entry.m_body.assign(end_header_it+4, it_end); + + return true; + } + + inline + bool parse_multipart_body(const std::string& content_type, const std::string& body, std::list<multipart_entry>& out_values) + { + //bool res = file_io_utils::load_file_to_string("C:\\public\\multupart_data", body); + + std::string boundary; + if(!match_boundary(content_type, boundary)) + { + LOG_PRINT("Failed to match boundary in content type: " << content_type, LOG_LEVEL_0); + return false; + } + + boundary+="\r\n"; + bool is_stop = false; + bool first_step = true; + + std::string::const_iterator it_begin = body.begin(); + std::string::const_iterator it_end; + while(!is_stop) + { + std::string::size_type pos = body.find(boundary, std::distance(body.begin(), it_begin)); + + if(std::string::npos == pos) + { + is_stop = true; + boundary.erase(boundary.size()-2, 2); + boundary+= "--"; + pos = body.find(boundary, std::distance(body.begin(), it_begin)); + if(std::string::npos == pos) + { + LOG_PRINT("Error: Filed to match closing multipart tag", LOG_LEVEL_0); + it_end = body.end(); + }else + { + it_end = body.begin() + pos; + } + }else + it_end = body.begin() + pos; + + + if(first_step && !is_stop) + { + first_step = false; + it_begin = it_end + boundary.size(); + std::string temp = "\r\n--"; + boundary = temp + boundary; + continue; + } + + out_values.push_back(multipart_entry()); + if(!handle_part_of_multipart(it_begin, it_end, out_values.back())) + { + LOG_PRINT("Failed to handle_part_of_multipart", LOG_LEVEL_0); + return false; + } + + it_begin = it_end + boundary.size(); + } + + return true; + } + + + + + //-------------------------------------------------------------------------------------------- + inline + simple_http_connection_handler::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config): + m_state(http_state_retriving_comand_line), + m_body_transfer_type(http_body_transfer_undefined), + m_is_stop_handling(false), + m_len_summary(0), + m_len_remain(0), + m_config(config), + m_want_close(false), + m_psnd_hndlr(psnd_hndlr) + { + + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::set_ready_state() + { + m_is_stop_handling = false; + m_state = http_state_retriving_comand_line; + m_body_transfer_type = http_body_transfer_undefined; + m_query_info.clear(); + m_len_summary = 0; + return true; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_recv(const void* ptr, size_t cb) + { + std::string buf((const char*)ptr, cb); + //LOG_PRINT_L0("HTTP_RECV: " << ptr << "\r\n" << buf); + //file_io_utils::save_string_to_file(string_tools::get_current_module_folder() + "/" + boost::lexical_cast<std::string>(ptr), std::string((const char*)ptr, cb)); + + bool res = handle_buff_in(buf); + if(m_want_close/*m_state == http_state_connection_close || m_state == http_state_error*/) + return false; + return res; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_buff_in(std::string& buf) + { + + if(m_cache.size()) + m_cache += buf; + else + m_cache.swap(buf); + + m_is_stop_handling = false; + while(!m_is_stop_handling) + { + switch(m_state) + { + case http_state_retriving_comand_line: + //The HTTP protocol does not place any a priori limit on the length of a URI. (c)RFC2616 + //but we forebly restirct it len to HTTP_MAX_URI_LEN to make it more safely + if(!m_cache.size()) + break; + + //check_and_handle_fake_response(); + if((m_cache[0] == '\r' || m_cache[0] == '\n')) + { + //some times it could be that before query line cold be few line breaks + //so we have to be calm without panic with assers + m_cache.erase(0, 1); + break; + } + + if(std::string::npos != m_cache.find('\n', 0)) + handle_invoke_query_line(); + else + { + m_is_stop_handling = true; + if(m_cache.size() > HTTP_MAX_URI_LEN) + { + LOG_ERROR("simple_http_connection_handler::handle_buff_out: Too long URI line"); + m_state = http_state_error; + return false; + } + } + break; + case http_state_retriving_header: + { + std::string::size_type pos = match_end_of_header(m_cache); + if(std::string::npos == pos) + { + m_is_stop_handling = true; + if(m_cache.size() > HTTP_MAX_HEADER_LEN) + { + LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too long header area"); + m_state = http_state_error; + return false; + } + break; + } + analize_cached_request_header_and_invoke_state(pos); + break; + } + case http_state_retriving_body: + return handle_retriving_query_body(); + case http_state_connection_close: + return false; + default: + LOG_ERROR("simple_http_connection_handler::handle_char_out: Wrong state: " << m_state); + return false; + case http_state_error: + LOG_ERROR("simple_http_connection_handler::handle_char_out: Error state!!!"); + return false; + } + + if(!m_cache.size()) + m_is_stop_handling = true; + } + + return true; + } + //-------------------------------------------------------------------------------------------- + inline bool analize_http_method(const boost::smatch& result, http::http_method& method, int& http_ver_major, int& http_ver_minor) + { + CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed..."); + http_ver_major = boost::lexical_cast<int>(result[11]); + http_ver_minor = boost::lexical_cast<int>(result[12]); + if(result[4].matched) + method = http::http_method_get; + else if(result[5].matched) + method = http::http_method_head; + else if(result[6].matched) + method = http::http_method_post; + else if(result[7].matched) + method = http::http_method_put; + else + method = http::http_method_etc; + + return true; + } + + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_invoke_query_line() + { + LOG_FRAME("simple_http_connection_handler::handle_recognize_protocol_out(*)", LOG_LEVEL_3); + + STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^(((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|(DELETE)|(TRACE)) (\\S+) HTTP/(\\d+).(\\d+))\r?\n", boost::regex::icase | boost::regex::normal); + // 123 4 5 6 7 8 9 10 11 12 + //size_t match_len = 0; + boost::smatch result; + if(boost::regex_search(m_cache, result, rexp_match_command_line, boost::match_default) && result[0].matched) + { + analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi); + m_query_info.m_URI = result[10]; + parse_uri(m_query_info.m_URI, m_query_info.m_uri_content); + m_query_info.m_http_method_str = result[2]; + m_query_info.m_full_request_str = result[0]; + + m_cache.erase(m_cache.begin(), to_nonsonst_iterator(m_cache, result[0].second)); + + m_state = http_state_retriving_header; + + return true; + }else + { + m_state = http_state_error; + LOG_ERROR("simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line: " << m_cache); + return false; + } + + return false; + } + //-------------------------------------------------------------------------------------------- + inline std::string::size_type simple_http_connection_handler::match_end_of_header(const std::string& buf) + { + + //Here we returning head size, including terminating sequence (\r\n\r\n or \n\n) + std::string::size_type res = buf.find("\r\n\r\n"); + if(std::string::npos != res) + return res+4; + res = buf.find("\n\n"); + if(std::string::npos != res) + return res+2; + return res; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::analize_cached_request_header_and_invoke_state(size_t pos) + { + //LOG_PRINT_L4("HTTP HEAD:\r\n" << m_cache.substr(0, pos)); + + LOG_FRAME("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(*)", LOG_LEVEL_3); + + m_query_info.m_full_request_buf_size = pos; + m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos); + + if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos)) + { + LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache); + m_state = http_state_error; + } + + m_cache.erase(0, pos); + + std::string req_command_str = m_query_info.m_full_request_str; + //if we have POST or PUT command, it is very possible tha we will get body + //but now, we suppose than we have body only in case of we have "ContentLength" + if(m_query_info.m_header_info.m_content_length.size()) + { + m_state = http_state_retriving_body; + m_body_transfer_type = http_body_transfer_measure; + if(!get_len_from_content_lenght(m_query_info.m_header_info.m_content_length, m_len_summary)) + { + LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_query_info.m_header_info.m_content_length); + m_state = http_state_error; + return false; + } + if(0 == m_len_summary) + { //current query finished, next will be next query + if(handle_request_and_send_response(m_query_info)) + set_ready_state(); + else + m_state = http_state_error; + } + m_len_remain = m_len_summary; + }else + {//current query finished, next will be next query + handle_request_and_send_response(m_query_info); + set_ready_state(); + } + + return true; + } + //----------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_retriving_query_body() + { + switch(m_body_transfer_type) + { + case http_body_transfer_measure: + return handle_query_measure(); + case http_body_transfer_chunked: + case http_body_transfer_connection_close: + case http_body_transfer_multipart: + case http_body_transfer_undefined: + default: + LOG_ERROR("simple_http_connection_handler::handle_retriving_query_body(): Unexpected m_body_query_type state:" << m_body_transfer_type); + m_state = http_state_error; + return false; + } + + return true; + } + //----------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_query_measure() + { + + if(m_len_remain >= m_cache.size()) + { + m_len_remain -= m_cache.size(); + m_query_info.m_body += m_cache; + m_cache.clear(); + }else + { + m_query_info.m_body.append(m_cache.begin(), m_cache.begin() + m_len_remain); + m_cache.erase(0, m_len_remain); + m_len_remain = 0; + } + + if(!m_len_remain) + { + if(handle_request_and_send_response(m_query_info)) + set_ready_state(); + else + m_state = http_state_error; + } + return true; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos) + { + LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_3); + + STATIC_REGEXP_EXPR_1(rexp_mach_field, + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)" + // 12 3 4 5 6 7 8 9 + "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", + //10 1112 13 + boost::regex::icase | boost::regex::normal); + + boost::smatch result; + std::string::const_iterator it_current_bound = m_cache_to_process.begin(); + std::string::const_iterator it_end_bound = m_cache_to_process.begin()+pos; + + body_info.clear(); + + //lookup all fields and fill well-known fields + while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + { + const size_t field_val = 12; + const size_t field_etc_name = 10; + + int i = 2; //start position = 2 + if(result[i++].matched)//"Connection" + body_info.m_connection = result[field_val]; + else if(result[i++].matched)//"Referer" + body_info.m_referer = result[field_val]; + else if(result[i++].matched)//"Content-Length" + body_info.m_content_length = result[field_val]; + else if(result[i++].matched)//"Content-Type" + body_info.m_content_type = result[field_val]; + else if(result[i++].matched)//"Transfer-Encoding" + body_info.m_transfer_encoding = result[field_val]; + else if(result[i++].matched)//"Content-Encoding" + body_info.m_content_encoding = result[field_val]; + else if(result[i++].matched)//"Host" + body_info.m_host = result[field_val]; + else if(result[i++].matched)//"Cookie" + body_info.m_cookie = result[field_val]; + else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) + body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val])); + else + { + LOG_ERROR("simple_http_connection_handler::parse_cached_header() not matched last entry in:"<<m_cache_to_process); + } + + it_current_bound = result[(int)result.size()-1]. first; + } + return true; + } + //----------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::get_len_from_content_lenght(const std::string& str, size_t& OUT len) + { + STATIC_REGEXP_EXPR_1(rexp_mach_field, "\\d+", boost::regex::normal); + std::string res; + boost::smatch result; + if(!(boost::regex_search( str, result, rexp_mach_field, boost::match_default) && result[0].matched)) + return false; + + len = boost::lexical_cast<size_t>(result[0]); + return true; + } + //----------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_request_and_send_response(const http::http_request_info& query_info) + { + http_response_info response; + bool res = handle_request(query_info, response); + //CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" ); + + std::string response_data = get_response_header(response); + + //LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body); + LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data); + + m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); + if(response.m_body.size()) + m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); + return res; + } + //----------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_request(const http::http_request_info& query_info, http_response_info& response) + { + + std::string uri_to_path = query_info.m_uri_content.m_path; + if("/" == uri_to_path) + uri_to_path = "/index.html"; + + //slash_to_back_slash(uri_to_path); + m_config.m_lock.lock(); + std::string destination_file_path = m_config.m_folder + uri_to_path; + m_config.m_lock.unlock(); + if(!file_io_utils::load_file_to_string(destination_file_path.c_str(), response.m_body)) + { + LOG_PRINT("URI \""<< query_info.m_full_request_str.substr(0, query_info.m_full_request_str.size()-2) << "\" [" << destination_file_path << "] Not Found (404 )" , LOG_LEVEL_1); + response.m_body = get_not_found_response_body(query_info.m_URI); + response.m_response_code = 404; + response.m_response_comment = "Not found"; + response.m_mime_tipe = "text/html"; + return true; + } + + LOG_PRINT(" -->> " << query_info.m_full_request_str << "\r\n<<--OK" , LOG_LEVEL_3); + response.m_response_code = 200; + response.m_response_comment = "OK"; + response.m_mime_tipe = get_file_mime_tipe(uri_to_path); + + + return true; + } + //----------------------------------------------------------------------------------- + inline std::string simple_http_connection_handler::get_response_header(const http_response_info& response) + { + std::string buf = "HTTP/1.1 "; + buf += boost::lexical_cast<std::string>(response.m_response_code) + " " + response.m_response_comment + "\r\n" + + "Server: Siski v0.1\r\n" + "Content-Length: "; + buf += boost::lexical_cast<std::string>(response.m_body.size()) + "\r\n"; + buf += "Content-Type: "; + buf += response.m_mime_tipe + "\r\n"; + + buf += "Last-Modified: "; + time_t tm; + time(&tm); + buf += misc_utils::get_internet_time_str(tm) + "\r\n"; + buf += "Accept-Ranges: bytes\r\n"; + //Wed, 01 Dec 2010 03:27:41 GMT" + + string_tools::trim(m_query_info.m_header_info.m_connection); + if(m_query_info.m_header_info.m_connection.size()) + { + if(!string_tools::compare_no_case("close", m_query_info.m_header_info.m_connection)) + { + //closing connection after sending + buf += "Connection: close\r\n"; + m_state = http_state_connection_close; + m_want_close = true; + } + } + //add additional fields, if it is + for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++) + buf += it->first + ":" + it->second + "\r\n"; + + buf+="\r\n"; + + return buf; + } + //----------------------------------------------------------------------------------- + inline std::string simple_http_connection_handler::get_file_mime_tipe(const std::string& path) + { + std::string result; + std::string ext = string_tools::get_extension(path); + if(!string_tools::compare_no_case(ext, "gif")) + result = "image/gif"; + else if(!string_tools::compare_no_case(ext, "jpg")) + result = "image/jpeg"; + else if(!string_tools::compare_no_case(ext, "html")) + result = "text/html"; + else if(!string_tools::compare_no_case(ext, "htm")) + result = "text/html"; + else if(!string_tools::compare_no_case(ext, "js")) + result = "application/x-javascript"; + else if(!string_tools::compare_no_case(ext, "css")) + result = "text/css"; + else if(!string_tools::compare_no_case(ext, "xml")) + result = "application/xml"; + else if(!string_tools::compare_no_case(ext, "svg")) + result = "image/svg+xml"; + + + return result; + } + //----------------------------------------------------------------------------------- + inline std::string simple_http_connection_handler::get_not_found_response_body(const std::string& URI) + { + std::string body = + "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n" + "<html><head>\r\n" + "<title>404 Not Found</title>\r\n" + "</head><body>\r\n" + "<h1>Not Found</h1>\r\n" + "<p>The requested URL \r\n"; + body += URI; + body += "was not found on this server.</p>\r\n" + "</body></html>\r\n"; + + return body; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::slash_to_back_slash(std::string& str) + { + for(std::string::iterator it = str.begin(); it!=str.end(); it++) + if('/' == *it) + *it = '\\'; + return true; + } + } +} +} + +//-------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------
\ No newline at end of file diff --git a/contrib/epee/include/net/http_server_cp.h b/contrib/epee/include/net/http_server_cp.h new file mode 100644 index 000000000..bbb167f9f --- /dev/null +++ b/contrib/epee/include/net/http_server_cp.h @@ -0,0 +1,48 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server_cp.h" +#include "http_server.h" +namespace epee +{ +namespace net_utils +{ + typedef cp_server_impl<http::simple_http_connection_handler> cp_http_server_file_system; + typedef cp_server_impl<http::http_custom_handler> cp_http_server_custum_handling; +} +} + + + +#endif + + diff --git a/contrib/epee/include/net/http_server_cp2.h b/contrib/epee/include/net/http_server_cp2.h new file mode 100644 index 000000000..dd76d06f8 --- /dev/null +++ b/contrib/epee/include/net/http_server_cp2.h @@ -0,0 +1,47 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_CP2_H_ +#define _HTTP_SERVER_CP2_H_ + +#include "abstract_tcp_server2.h" +#include "http_protocol_handler.h" +namespace epee +{ +namespace net_utils +{ + typedef boosted_tcp_server<http::simple_http_connection_handler> boosted_http_server_file_system; + typedef boosted_tcp_server<http::http_custom_handler> boosted_http_server_custum_handling; +} +} + + +#endif + + diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h new file mode 100644 index 000000000..7a8bdd4ad --- /dev/null +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -0,0 +1,260 @@ +// 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 "serialization/keyvalue_serialization.h" +#include "storages/portable_storage_template_helper.h" +#include "http_base.h" + + +#define CHAIN_HTTP_TO_MAP2() bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \ + epee::net_utils::http::http_response_info& response, \ + const epee::net_utils::connection_context_base& m_conn_context) \ +{\ + LOG_PRINT_L2("HTTP [" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \ + response.m_response_code = 200; \ + response.m_response_comment = "Ok"; \ + if(!handle_http_request_map(query_info, response, m_conn_context)) \ + {response.m_response_code = 404;response.m_response_comment = "Not found";} \ + return true; \ +} + + +#define BEGIN_URI_MAP2() bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \ + epee::net_utils::http::http_response_info& response_info, \ + const epee::net_utils::connection_context_base& m_conn_context) { \ + bool handled = false; \ + if(false) return true; //just a stub to have "else if" + +#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, m_conn_context); + +#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format + +#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) \ + else if(query_info.m_URI == s_pattern) \ + { \ + handled = true; \ + boost::uint64_t ticks = misc_utils::get_tick_count(); \ + boost::value_initialized<command_type::request> req; \ + bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \ + CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \ + boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ + boost::value_initialized<command_type::response> resp;\ + if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp))) \ + { \ + LOG_ERROR("Failed to " << #callback_f << "()"); \ + response_info.m_response_code = 500; \ + response_info.m_response_comment = "Internal Server Error"; \ + return true; \ + } \ + boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_json(static_cast<command_type::response&>(resp), response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ + response_info.m_header_info.m_content_type = " application/json"; \ + LOG_PRINT( s_pattern << " processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + } + +#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \ + else if(query_info.m_URI == s_pattern) \ + { \ + handled = true; \ + boost::uint64_t ticks = misc_utils::get_tick_count(); \ + boost::value_initialized<command_type::request> req; \ + bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), query_info.m_body); \ + CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ + boost::uint64_t ticks1 = misc_utils::get_tick_count(); \ + boost::value_initialized<command_type::response> resp;\ + if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp))) \ + { \ + LOG_ERROR("Failed to " << #callback_f << "()"); \ + response_info.m_response_code = 500; \ + response_info.m_response_comment = "Internal Server Error"; \ + return true; \ + } \ + boost::uint64_t ticks2 = misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = " application/octet-stream"; \ + response_info.m_header_info.m_content_type = " application/octet-stream"; \ + LOG_PRINT( s_pattern << "() processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + } + +#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);handled = true;} + +#define END_URI_MAP2() return handled;} + + + + +namespace epee +{ + namespace json_rpc + { + template<typename t_param> + struct request + { + std::string version; + std::string method; + std::string id; + t_param params; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + KV_SERIALIZE(method) + KV_SERIALIZE(id) + KV_SERIALIZE(params) + END_KV_SERIALIZE_MAP() + }; + + struct error + { + int64_t code; + std::string message; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(code) + KV_SERIALIZE(message) + END_KV_SERIALIZE_MAP() + }; + + struct dummy_error + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + template<typename t_param, typename t_error> + struct response + { + t_param result; + t_error error; + std::string id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(result) + KV_SERIALIZE(error) + KV_SERIALIZE(id) + END_KV_SERIALIZE_MAP() + }; + + typedef response<std::string, error> error_response; + } +} + + + + +#define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \ + { \ + boost::uint64_t ticks = epee::misc_utils::get_tick_count(); \ + epee::serialization::portable_storage ps; \ + if(!ps.load_from_json(query_info.m_body)) \ + { \ + boost::value_initialized<epee::json_rpc::error_response> rsp; \ + static_cast<epee::json_rpc::error_response&>(rsp).error.code = -32700; \ + static_cast<epee::json_rpc::error_response&>(rsp).error.message = "Parse error"; \ + epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \ + return true; \ + } \ + std::string callback_name; \ + if(!ps.get_value("method", callback_name, nullptr)) \ + { \ + epee::json_rpc::error_response rsp; \ + rsp.error.code = -32600; \ + rsp.error.message = "Invalid Request"; \ + epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \ + return true; \ + } \ + if(false) return true; //just a stub to have "else if" + +#define MAP_JON_RPC_WE(method_name, callback_f, command_type) \ + else if(callback_name == method_name) \ +{ \ + handled = true; \ + boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \ + epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\ + req.load(ps); \ + boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ + boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \ + epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \ + resp.id = req.id; \ + epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ + fail_resp.id = req.id; \ + if(!callback_f(req.params, resp.result, fail_resp.error)) \ +{ \ + epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \ + return true; \ +} \ + boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_json(resp, response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ + response_info.m_header_info.m_content_type = " application/json"; \ + LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + return true;\ +} + + +#define MAP_JON_RPC(method_name, callback_f, command_type) \ + else if(callback_name == method_name) \ +{ \ + handled = true; \ + boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \ + epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\ + req.load(ps); \ + boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ + boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \ + epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \ + resp.id = req.id; \ + if(!callback_f(req.params, resp.result)) \ +{ \ + epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ + fail_resp.id = req.id; \ + fail_resp.error.code = -32603; \ + fail_resp.error.message = "Internal error"; \ + epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \ + return true; \ +} \ + boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_json(resp, response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ + response_info.m_header_info.m_content_type = " application/json"; \ + LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + return true;\ +} + + +#define END_JSON_RPC_MAP() \ + epee::json_rpc::error_response rsp; \ + rsp.error.code = -32601; \ + rsp.error.message = "Method not found"; \ + epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \ + return true; \ + } + + diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h new file mode 100644 index 000000000..f81b4f601 --- /dev/null +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -0,0 +1,112 @@ +// 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 <boost/thread.hpp> +#include <boost/bind.hpp> + +#include "net/http_server_cp2.h" +#include "net/http_server_handlers_map2.h" + +namespace epee +{ + + template<class t_child_class> + class http_server_impl_base: public net_utils::http::i_http_server_handler + { + + public: + http_server_impl_base() + : m_net_server() + {} + + explicit http_server_impl_base(boost::asio::io_service& external_io_service) + : m_net_server(external_io_service) + {} + + bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0") + { + + //set self as callback handler + m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this); + + //here set folder for hosting reqests + m_net_server.get_config_object().m_folder = ""; + + LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); + bool res = m_net_server.init_server(bind_port, bind_ip); + if(!res) + { + LOG_ERROR("Failed to bind server"); + return false; + } + return true; + } + + bool run(size_t threads_count, bool wait = true) + { + //go to loop + LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0); + if(!m_net_server.run_server(threads_count, wait)) + { + LOG_ERROR("Failed to run net tcp server!"); + } + + if(wait) + LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); + return true; + } + + bool deinit() + { + return m_net_server.deinit_server(); + } + + bool timed_wait_server_stop(uint64_t ms) + { + return m_net_server.timed_wait_server_stop(ms); + } + + bool send_stop_signal() + { + m_net_server.send_stop_signal(); + return true; + } + + int get_binded_port() + { + return m_net_server.get_binded_port(); + } + + protected: + net_utils::boosted_http_server_custum_handling m_net_server; + }; +}
\ No newline at end of file diff --git a/contrib/epee/include/net/http_server_thread_per_connect.h b/contrib/epee/include/net/http_server_thread_per_connect.h new file mode 100644 index 000000000..bec43b726 --- /dev/null +++ b/contrib/epee/include/net/http_server_thread_per_connect.h @@ -0,0 +1,48 @@ +// 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. +// + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server.h" +#include "http_server.h" + +namespace epee +{ +namespace net_utils +{ + typedef abstract_tcp_server<http::simple_http_connection_handler> mt_http_server_file_system; + typedef abstract_tcp_server<http::http_custom_handler> mt_http_server_custum_handling; + +} +} + + +#endif + + diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h new file mode 100644 index 000000000..503a9e5df --- /dev/null +++ b/contrib/epee/include/net/levin_base.h @@ -0,0 +1,125 @@ +// 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. +// + + + +#ifndef _LEVIN_BASE_H_ +#define _LEVIN_BASE_H_ + +#include "net_utils_base.h" + +#define LEVIN_SIGNATURE 0x0101010101012101LL //Bender's nightmare + +namespace epee +{ +namespace levin +{ +#pragma pack(push) +#pragma pack(1) + struct bucket_head + { + boost::uint64_t m_signature; + boost::uint64_t m_cb; + bool m_have_to_return_data; + boost::uint32_t m_command; + boost::int32_t m_return_code; + boost::uint32_t m_reservedA; //probably some flags in future + boost::uint32_t m_reservedB; //probably some check sum in future + }; +#pragma pack(pop) + + +#pragma pack(push) +#pragma pack(1) + struct bucket_head2 + { + boost::uint64_t m_signature; + boost::uint64_t m_cb; + bool m_have_to_return_data; + boost::uint32_t m_command; + boost::int32_t m_return_code; + boost::uint32_t m_flags; + boost::uint32_t m_protocol_version; + }; +#pragma pack(pop) + + +#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0 +#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default + +#define LEVIN_PACKET_REQUEST 0x00000001 +#define LEVIN_PACKET_RESPONSE 0x00000002 + + +#define LEVIN_PROTOCOL_VER_0 0 +#define LEVIN_PROTOCOL_VER_1 1 + + template<class t_connection_context = net_utils::connection_context_base> + struct levin_commands_handler + { + virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, t_connection_context& context)=0; + virtual int notify(int command, const std::string& in_buff, t_connection_context& context)=0; + virtual void callback(t_connection_context& context){}; + + virtual void on_connection_new(t_connection_context& context){}; + virtual void on_connection_close(t_connection_context& context){}; + + }; + +#define LEVIN_OK 0 +#define LEVIN_ERROR_CONNECTION -1 +#define LEVIN_ERROR_CONNECTION_NOT_FOUND -2 +#define LEVIN_ERROR_CONNECTION_DESTROYED -3 +#define LEVIN_ERROR_CONNECTION_TIMEDOUT -4 +#define LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL -5 +#define LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED -6 +#define LEVIN_ERROR_FORMAT -7 + +#define DESCRIBE_RET_CODE(code) case code: return #code; + inline + const char* get_err_descr(int err) + { + switch(err) + { + DESCRIBE_RET_CODE(LEVIN_OK); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NOT_FOUND); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_DESTROYED); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_TIMEDOUT); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED); + DESCRIBE_RET_CODE(LEVIN_ERROR_FORMAT); + default: + return "unknown code"; + } + } + + +} +} + + +#endif //_LEVIN_BASE_H_ diff --git a/contrib/epee/include/net/levin_client.h b/contrib/epee/include/net/levin_client.h new file mode 100644 index 000000000..335f6ba02 --- /dev/null +++ b/contrib/epee/include/net/levin_client.h @@ -0,0 +1,89 @@ +// 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. +// + + + + + +#ifndef _LEVIN_CLIENT_H_ +#define _LEVIN_CLIENT_H_ + +#include "net_helper.h" +#include "levin_base.h" + + +#ifndef MAKE_IP +#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) +#endif + +namespace epee +{ +namespace levin +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class levin_client_impl + { + public: + levin_client_impl(); + virtual ~levin_client_impl(); + + bool connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0"); + bool connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0"); + bool is_connected(); + bool disconnect(); + + virtual int invoke(int command, const std::string& in_buff, std::string& buff_out); + virtual int notify(int command, const std::string& in_buff); + + protected: + net_utils::blocked_mode_client m_transport; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + class levin_client_impl2: public levin_client_impl + { + public: + + int invoke(int command, const std::string& in_buff, std::string& buff_out); + int notify(int command, const std::string& in_buff); + }; + +} +namespace net_utils +{ + typedef levin::levin_client_impl levin_client; + typedef levin::levin_client_impl2 levin_client2; +} +} + +#include "levin_client.inl" + +#endif //_LEVIN_CLIENT_H_ diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl new file mode 100644 index 000000000..ae159da6e --- /dev/null +++ b/contrib/epee/include/net/levin_client.inl @@ -0,0 +1,194 @@ +// 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. +// + + + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +#include "string_tools.h" +namespace epee +{ +namespace levin +{ +inline +bool levin_client_impl::connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip) +{ + return m_transport.connect(string_tools::get_ip_string_from_int32(ip), port, timeout, timeout, bind_ip); +} +//------------------------------------------------------------------------------ +inline + bool levin_client_impl::connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip) +{ + return m_transport.connect(addr, port, timeout, timeout, bind_ip); +} +//------------------------------------------------------------------------------ +inline +bool levin_client_impl::is_connected() +{ + return m_transport.is_connected(); +} +//------------------------------------------------------------------------------ +inline +bool levin_client_impl::disconnect() +{ + return m_transport.disconnect(); +} +//------------------------------------------------------------------------------ +inline +levin_client_impl::levin_client_impl() +{ +} +//------------------------------------------------------------------------------ +inline +levin_client_impl::~levin_client_impl() +{ + disconnect(); +} +//------------------------------------------------------------------------------ +inline +int levin_client_impl::invoke(int command, const std::string& in_buff, std::string& buff_out) +{ + if(!is_connected()) + return -1; + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + head.m_command = command; + if(!m_transport.send(&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + std::string local_buff; + if(!m_transport.recv_n(local_buff, sizeof(bucket_head))) + return -1; + + head = *(bucket_head*)local_buff.data(); + + + if(head.m_signature!=LEVIN_SIGNATURE) + { + LOG_PRINT_L0("Signature missmatch in response"); + return -1; + } + + if(!m_transport.recv_n(buff_out, head.m_cb)) + return -1; + + return head.m_return_code; +} +//------------------------------------------------------------------------------ +inline +int levin_client_impl::notify(int command, const std::string& in_buff) +{ + if(!is_connected()) + return -1; + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = false; + head.m_command = command; + + if(!m_transport.send((const char*)&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + return 1; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +inline + int levin_client_impl2::invoke(int command, const std::string& in_buff, std::string& buff_out) +{ + if(!is_connected()) + return -1; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + if(!m_transport.send(&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + std::string local_buff; + if(!m_transport.recv_n(local_buff, sizeof(bucket_head2))) + return -1; + + head = *(bucket_head2*)local_buff.data(); + + + if(head.m_signature!=LEVIN_SIGNATURE) + { + LOG_PRINT_L0("Signature missmatch in response"); + return -1; + } + + if(!m_transport.recv_n(buff_out, head.m_cb)) + return -1; + + return head.m_return_code; +} +//------------------------------------------------------------------------------ +inline + int levin_client_impl2::notify(int command, const std::string& in_buff) +{ + if(!is_connected()) + return -1; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = false; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + + if(!m_transport.send((const char*)&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + return 1; +} + +} +} +//------------------------------------------------------------------------------
\ No newline at end of file diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h new file mode 100644 index 000000000..b02fa7ee7 --- /dev/null +++ b/contrib/epee/include/net/levin_client_async.h @@ -0,0 +1,577 @@ +// 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 "" +#include "net_helper.h" +#include "levin_base.h" + + +namespace epee +{ +namespace levin +{ + + /************************************************************************ + * levin_client_async - probably it is not really fast implementation, + * each handler thread could make up to 30 ms latency. + * But, handling events in reader thread will cause dead locks in + * case of recursive call (call invoke() to the same connection + * on reader thread on remote invoke() handler) + ***********************************************************************/ + + + class levin_client_async + { + levin_commands_handler* m_pcommands_handler; + volatile boost::uint32_t m_is_stop; + volatile boost::uint32_t m_threads_count; + ::critical_section m_send_lock; + + std::string m_local_invoke_buff; + ::critical_section m_local_invoke_buff_lock; + volatile int m_invoke_res; + + volatile boost::uint32_t m_invoke_data_ready; + volatile boost::uint32_t m_invoke_is_active; + + boost::mutex m_invoke_event; + boost::condition_variable m_invoke_cond; + size_t m_timeout; + + ::critical_section m_recieved_packets_lock; + struct packet_entry + { + bucket_head m_hd; + std::string m_body; + boost::uint32_t m_connection_index; + }; + std::list<packet_entry> m_recieved_packets; + /* + m_current_connection_index needed when some connection was broken and reconnected - in this + case we could have some received packets in que, which shoud not be handled + */ + volatile boost::uint32_t m_current_connection_index; + ::critical_section m_invoke_lock; + ::critical_section m_reciev_packet_lock; + ::critical_section m_connection_lock; + net_utils::blocked_mode_client m_transport; + public: + levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + {} + levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + {} + ~levin_client_async() + { + boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1); + disconnect(); + + + while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count)) + ::Sleep(100); + } + + void set_handler(levin_commands_handler* phandler) + { + m_pcommands_handler = phandler; + } + + bool connect(boost::uint32_t ip, boost::uint32_t port, boost::uint32_t timeout) + { + loop_call_guard(); + critical_region cr(m_connection_lock); + + m_timeout = timeout; + bool res = false; + CRITICAL_REGION_BEGIN(m_reciev_packet_lock); + CRITICAL_REGION_BEGIN(m_send_lock); + res = levin_client_impl::connect(ip, port, timeout); + boost::interprocess::ipcdetail::atomic_inc32(&m_current_connection_index); + CRITICAL_REGION_END(); + CRITICAL_REGION_END(); + if(res && !boost::interprocess::ipcdetail::atomic_read32(&m_threads_count) ) + { + //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 0);//m_is_stop = false; + boost::thread( boost::bind(&levin_duplex_client::reciever_thread, this) ); + boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) ); + boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) ); + } + + return res; + } + bool is_connected() + { + loop_call_guard(); + critical_region cr(m_cs); + return levin_client_impl::is_connected(); + } + + inline + bool check_connection() + { + loop_call_guard(); + critical_region cr(m_cs); + + if(!is_connected()) + { + if( !reconnect() ) + { + LOG_ERROR("Reconnect Failed. Failed to invoke() becouse not connected!"); + return false; + } + } + return true; + } + + //------------------------------------------------------------------------------ + inline + bool recv_n(SOCKET s, char* pbuff, size_t cb) + { + while(cb) + { + int res = ::recv(m_socket, pbuff, (int)cb, 0); + + if(SOCKET_ERROR == res) + { + if(!m_connected) + return false; + + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to recv(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + //reconnect(); + return false; + }else if(res == 0) + { + disconnect(); + //reconnect(); + return false; + } + LOG_PRINT_L4("[" << m_socket <<"] RECV " << res); + cb -= res; + pbuff += res; + } + + return true; + } + + //------------------------------------------------------------------------------ + inline + bool recv_n(SOCKET s, std::string& buff) + { + size_t cb_remain = buff.size(); + char* m_current_ptr = (char*)buff.data(); + return recv_n(s, m_current_ptr, cb_remain); + } + + bool disconnect() + { + //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1);//m_is_stop = true; + loop_call_guard(); + critical_region cr(m_cs); + levin_client_impl::disconnect(); + + CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); + m_local_invoke_buff.clear(); + m_invoke_res = LEVIN_ERROR_CONNECTION_DESTROYED; + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true; + m_invoke_cond.notify_all(); + return true; + } + + void loop_call_guard() + { + + } + + void on_leave_invoke() + { + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 0); + } + + int invoke(const GUID& target, int command, const std::string& in_buff, std::string& buff_out) + { + + critical_region cr_invoke(m_invoke_lock); + + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 1); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 0); + misc_utils::destr_ptr hdlr = misc_utils::add_exit_scope_handler(boost::bind(&levin_duplex_client::on_leave_invoke, this)); + + loop_call_guard(); + + if(!check_connection()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + head.m_id = target; +#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS + ::UuidCreate(&head.m_id); +#endif + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + LOG_PRINT("[" << m_socket <<"] Sending invoke data", LOG_LEVEL_4); + + CRITICAL_REGION_BEGIN(m_send_lock); + LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head)); + int res = ::send(m_socket, (const char*)&head, sizeof(head), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size()); + res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + CRITICAL_REGION_END(); + LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + //hard coded timeout in 10 minutes for maximum invoke period. if it happens, it could mean only some real troubles. + boost::system_time timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100); + size_t timeout_count = 0; + boost::unique_lock<boost::mutex> lock(m_invoke_event); + + while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_data_ready)) + { + if(!m_invoke_cond.timed_wait(lock, timeout)) + { + if(timeout_count < 10) + { + //workaround to avoid freezing at timed_wait called after notify_all. + timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100); + ++timeout_count; + continue; + }else if(timeout_count == 10) + { + //workaround to avoid freezing at timed_wait called after notify_all. + timeout = boost::get_system_time()+ boost::posix_time::minutes(10); + ++timeout_count; + continue; + }else + { + LOG_PRINT("[" << m_socket <<"] Timeout on waiting invoke result. ", LOG_LEVEL_0); + //disconnect(); + return LEVIN_ERROR_CONNECTION_TIMEDOUT; + } + } + } + + + CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); + buff_out.swap(m_local_invoke_buff); + m_local_invoke_buff.clear(); + CRITICAL_REGION_END(); + return m_invoke_res; + } + + int notify(const GUID& target, int command, const std::string& in_buff) + { + if(!check_connection()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = false; + head.m_id = target; +#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS + ::UuidCreate(&head.m_id); +#endif + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + CRITICAL_REGION_BEGIN(m_send_lock); + LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head)); + int res = ::send(m_socket, (const char*)&head, sizeof(head), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size()); + res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + CRITICAL_REGION_END(); + LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + return 1; + } + + + private: + bool have_some_data(SOCKET sock, int interval = 1) + { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + fd_set fdse; + FD_ZERO(&fdse); + FD_SET(sock, &fdse); + + + timeval tv; + tv.tv_sec = interval; + tv.tv_usec = 0; + + int sel_res = select(0, &fds, 0, &fdse, &tv); + if(0 == sel_res) + return false; + else if(sel_res == SOCKET_ERROR) + { + if(m_is_stop) + return false; + int err_code = ::WSAGetLastError(); + LOG_ERROR("Filed to call select, err code = " << err_code); + disconnect(); + }else + { + if(fds.fd_array[0]) + {//some read operations was performed + return true; + }else if(fdse.fd_array[0]) + {//some error was at the socket + return true; + } + } + return false; + } + + + bool reciev_and_process_incoming_data() + { + bucket_head head = {0}; + boost::uint32_t conn_index = 0; + bool is_request = false; + std::string local_buff; + CRITICAL_REGION_BEGIN(m_reciev_packet_lock);//to protect from socket reconnect between head and body + + if(!recv_n(m_socket, (char*)&head, sizeof(head))) + { + if(m_is_stop) + return false; + LOG_ERROR("Failed to recv_n"); + return false; + } + + conn_index = boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index); + + if(head.m_signature!=LEVIN_SIGNATURE) + { + LOG_ERROR("Signature missmatch in response"); + return false; + } + + is_request = (head.m_protocol_version == LEVIN_PROTOCOL_VER_1 && head.m_flags&LEVIN_PACKET_REQUEST); + + + local_buff.resize((size_t)head.m_cb); + if(!recv_n(m_socket, local_buff)) + { + if(m_is_stop) + return false; + LOG_ERROR("Filed to reciev"); + return false; + } + CRITICAL_REGION_END(); + + LOG_PRINT_L4("LEVIN_PACKET_RECIEVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + if(is_request) + { + CRITICAL_REGION_BEGIN(m_recieved_packets_lock); + m_recieved_packets.resize(m_recieved_packets.size() + 1); + m_recieved_packets.back().m_hd = head; + m_recieved_packets.back().m_body.swap(local_buff); + m_recieved_packets.back().m_connection_index = conn_index; + CRITICAL_REGION_END(); + /* + + */ + }else + {//this is some response + + CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); + m_local_invoke_buff.swap(local_buff); + m_invoke_res = head.m_return_code; + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true; + m_invoke_cond.notify_all(); + + } + return true; + } + + bool reciever_thread() + { + LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread started.[m_threads_count=" << m_threads_count << "]"); + log_space::log_singletone::set_thread_log_prefix("RECIEVER_WORKER"); + boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count); + + while(!m_is_stop) + { + if(!m_connected) + { + Sleep(100); + continue; + } + + if(have_some_data(m_socket, 1)) + { + if(!reciev_and_process_incoming_data()) + { + if(m_is_stop) + { + break;//boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + //return true; + } + LOG_ERROR("Failed to reciev_and_process_incoming_data. shutting down"); + //boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + //disconnect_no_wait(); + //break; + } + } + } + + boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread stopped.[m_threads_count=" << m_threads_count << "]"); + return true; + } + + bool process_recieved_packet(bucket_head& head, const std::string& local_buff, boost::uint32_t conn_index) + { + + net_utils::connection_context_base conn_context; + conn_context.m_remote_ip = m_ip; + conn_context.m_remote_port = m_port; + if(head.m_have_to_return_data) + { + std::string return_buff; + if(m_pcommands_handler) + head.m_return_code = m_pcommands_handler->invoke(head.m_id, head.m_command, local_buff, return_buff, conn_context); + else + head.m_return_code = LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; + + + + head.m_cb = return_buff.size(); + head.m_have_to_return_data = false; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_RESPONSE; + + std::string send_buff((const char*)&head, sizeof(head)); + send_buff += return_buff; + CRITICAL_REGION_BEGIN(m_send_lock); + if(conn_index != boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index)) + {//there was reconnect, send response back is not allowed + return true; + } + int res = ::send(m_socket, (const char*)send_buff.data(), send_buff.size(), 0); + if(res == SOCKET_ERROR) + { + int err_code = ::WSAGetLastError(); + LOG_ERROR("Failed to send, err = " << err_code); + return false; + } + CRITICAL_REGION_END(); + LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + } + else + { + if(m_pcommands_handler) + m_pcommands_handler->notify(head.m_id, head.m_command, local_buff, conn_context); + } + + return true; + } + + bool handler_thread() + { + LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread started.[m_threads_count=" << m_threads_count << "]"); + log_space::log_singletone::set_thread_log_prefix("HANDLER_WORKER"); + boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count); + + while(!m_is_stop) + { + bool have_some_work = false; + std::string local_buff; + bucket_head bh = {0}; + boost::uint32_t conn_index = 0; + + CRITICAL_REGION_BEGIN(m_recieved_packets_lock); + if(m_recieved_packets.size()) + { + bh = m_recieved_packets.begin()->m_hd; + conn_index = m_recieved_packets.begin()->m_connection_index; + local_buff.swap(m_recieved_packets.begin()->m_body); + have_some_work = true; + m_recieved_packets.pop_front(); + } + CRITICAL_REGION_END(); + + if(have_some_work) + { + process_recieved_packet(bh, local_buff, conn_index); + }else + { + //Idle when no work + Sleep(30); + } + } + + boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread stopped.[m_threads_count=" << m_threads_count << "]"); + return true; + } + }; + +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/levin_client_async.inl b/contrib/epee/include/net/levin_client_async.inl new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/contrib/epee/include/net/levin_client_async.inl diff --git a/contrib/epee/include/net/levin_helper.h b/contrib/epee/include/net/levin_helper.h new file mode 100644 index 000000000..a8406103c --- /dev/null +++ b/contrib/epee/include/net/levin_helper.h @@ -0,0 +1,137 @@ +// 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 "levin_base.h" +#include "serializeble_struct_helper.h" + +namespace epee +{ +namespace levin +{ + template<class t_struct> + bool pack_struct_to_levin_message(const t_struct& t, std::string& buff, int command_id) + { + buff.resize(sizeof(levin::bucket_head)); + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = 0; + head.m_have_to_return_data = true; + head.m_command = command_id; + head.m_return_code = 1; + head.m_reservedA = rand(); //probably some flags in future + head.m_reservedB = rand(); //probably some check summ in future + + std::string buff_strg; + if(!StorageNamed::save_struct_as_storage_to_buff_t<t_struct, StorageNamed::DefaultStorageType>(t, buff_strg)) + return false; + + head.m_cb = buff_strg.size(); + buff.append(buff_strg); + return true; + } + + + bool pack_data_to_levin_message(const std::string& data, std::string& buff, int command_id) + { + buff.resize(sizeof(levin::bucket_head)); + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = 0; + head.m_have_to_return_data = true; + head.m_command = command_id; + head.m_return_code = 1; + head.m_reservedA = rand(); //probably some flags in future + head.m_reservedB = rand(); //probably some check summ in future + + head.m_cb = data.size(); + buff.append(data); + return true; + } + + bool load_levin_data_from_levin_message(std::string& levin_data, const std::string& buff, int& command) + { + if(buff.size() < sizeof(levin::bucket_head) ) + { + LOG_PRINT_L3("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message"); + return false; + } + + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + if(head.m_signature != LEVIN_SIGNATURE) + { + LOG_PRINT_L3("Failed to read signature in levin message, at load_struct_from_levin_message"); + return false; + } + if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) + { + LOG_PRINT_L3("sizes missmatch, at load_struct_from_levin_message"); + return false; + } + + //std::string buff_strg; + levin_data.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head)); + command = head.m_command; + return true; + } + + template<class t_struct> + bool load_struct_from_levin_message(t_struct& t, const std::string& buff, int& command) + { + if(buff.size() < sizeof(levin::bucket_head) ) + { + LOG_ERROR("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message"); + return false; + } + + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + if(head.m_signature != LEVIN_SIGNATURE) + { + LOG_ERROR("Failed to read signature in levin message, at load_struct_from_levin_message"); + return false; + } + if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) + { + LOG_ERROR("sizes missmatch, at load_struct_from_levin_message"); + return false; + } + + std::string buff_strg; + buff_strg.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head)); + + if(!StorageNamed::load_struct_from_storage_buff_t<t_struct, StorageNamed::DefaultStorageType>(t, buff_strg)) + { + LOG_ERROR("Failed to read storage, at load_struct_from_levin_message"); + return false; + } + command = head.m_command; + return true; + } +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h new file mode 100644 index 000000000..adc6e95d5 --- /dev/null +++ b/contrib/epee/include/net/levin_protocol_handler.h @@ -0,0 +1,178 @@ +// 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. +// + + + +#ifndef _LEVIN_PROTOCOL_HANDLER_H_ +#define _LEVIN_PROTOCOL_HANDLER_H_ + +#include <boost/uuid/uuid_generators.hpp> +#include "levin_base.h" + +namespace epee +{ +namespace levin +{ + template<class t_connection_context = net_utils::connection_context_base> + struct protocl_handler_config + { + levin_commands_handler<t_connection_context>* m_pcommands_handler; + }; + + template<class t_connection_context = net_utils::connection_context_base> + class protocol_handler + { + public: + typedef t_connection_context connection_context; + typedef protocl_handler_config<t_connection_context> config_type; + + protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context); + virtual ~protocol_handler(){} + + virtual bool handle_recv(const void* ptr, size_t cb); + + bool after_init_connection(){return true;} + private: + enum connection_data_state + { + conn_state_reading_head, + conn_state_reading_body + }; + + + config_type& m_config; + t_connection_context& m_conn_context; + net_utils::i_service_endpoint* m_psnd_hndlr; + std::string m_cach_in_buffer; + connection_data_state m_state; + bucket_head m_current_head; + }; + + template<class t_connection_context> + protocol_handler<t_connection_context>::protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context): + m_config(config), + m_conn_context(conn_context), + m_psnd_hndlr(psnd_hndlr), + m_state(conn_state_reading_head), + m_current_head(bucket_head()) + {} + + template<class t_connection_context> + bool protocol_handler<t_connection_context>::handle_recv(const void* ptr, size_t cb) + { + if(!m_config.m_pcommands_handler) + { + LOG_ERROR("Command handler not set!"); + return false; + } + m_cach_in_buffer.append((const char*)ptr, cb); + + bool is_continue = true; + while(is_continue) + { + switch(m_state) + { + case conn_state_reading_head: + if(m_cach_in_buffer.size() < sizeof(bucket_head)) + { + if(m_cach_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cach_in_buffer.data()) != LEVIN_SIGNATURE) + { + LOG_ERROR("Signature missmatch on accepted connection"); + return false; + } + is_continue = false; + break; + } + { + bucket_head* phead = (bucket_head*)m_cach_in_buffer.data(); + if(LEVIN_SIGNATURE != phead->m_signature) + { + LOG_ERROR("Signature missmatch on accepted connection"); + return false; + } + m_current_head = *phead; + } + m_cach_in_buffer.erase(0, sizeof(bucket_head)); + m_state = conn_state_reading_body; + break; + case conn_state_reading_body: + if(m_cach_in_buffer.size() < m_current_head.m_cb) + { + is_continue = false; + break; + } + { + std::string buff_to_invoke; + if(m_cach_in_buffer.size() == m_current_head.m_cb) + buff_to_invoke.swap(m_cach_in_buffer); + else + { + buff_to_invoke.assign(m_cach_in_buffer, 0, (std::string::size_type)m_current_head.m_cb); + m_cach_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb); + } + + + if(m_current_head.m_have_to_return_data) + { + std::string return_buff; + m_current_head.m_return_code = m_config.m_pcommands_handler->invoke(m_current_head.m_command, buff_to_invoke, return_buff, m_conn_context); + m_current_head.m_cb = return_buff.size(); + m_current_head.m_have_to_return_data = false; + std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); + send_buff += return_buff; + + if(!m_psnd_hndlr->do_send(send_buff.data(), send_buff.size())) + return false; + + } + else + m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_conn_context); + } + m_state = conn_state_reading_head; + break; + default: + LOG_ERROR("Undefined state in levin_server_impl::connection_handler, m_state=" << m_state); + return false; + } + } + + return true; + } + + + + + + + +} +} + + + + +#endif //_LEVIN_PROTOCOL_HANDLER_H_ + diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h new file mode 100644 index 000000000..dc4f41146 --- /dev/null +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -0,0 +1,778 @@ +// 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 <boost/uuid/uuid_generators.hpp> +#include <boost/interprocess/detail/atomic.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <atomic> + +#include "levin_base.h" +#include "misc_language.h" + + +namespace epee +{ +namespace levin +{ + +/************************************************************************/ +/* */ +/************************************************************************/ +template<class t_connection_context> +class async_protocol_handler; + +template<class t_connection_context> +class async_protocol_handler_config +{ + typedef std::map<boost::uuids::uuid, async_protocol_handler<t_connection_context>* > connections_map; + critical_section m_connects_lock; + connections_map m_connects; + + void add_connection(async_protocol_handler<t_connection_context>* pc); + void del_connection(async_protocol_handler<t_connection_context>* pc); + + async_protocol_handler<t_connection_context>* find_connection(boost::uuids::uuid connection_id) const; + int find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler<t_connection_context>*& aph); + + friend class async_protocol_handler<t_connection_context>; + +public: + typedef t_connection_context connection_context; + levin_commands_handler<t_connection_context>* m_pcommands_handler; + boost::uint64_t m_max_packet_size; + boost::uint64_t m_invoke_timeout; + + int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id); + template<class callback_t> + int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); + + int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id); + bool close(boost::uuids::uuid connection_id); + bool update_connection_context(const t_connection_context& contxt); + bool request_callback(boost::uuids::uuid connection_id); + template<class callback_t> + bool foreach_connection(callback_t cb); + size_t get_connections_count(); + + async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) + {} +}; + + +/************************************************************************/ +/* */ +/************************************************************************/ +template<class t_connection_context = net_utils::connection_context_base> +class async_protocol_handler +{ +public: + typedef t_connection_context connection_context; + typedef async_protocol_handler_config<t_connection_context> config_type; + + enum stream_state + { + stream_state_head, + stream_state_body + }; + + std::atomic<bool> m_deletion_initiated; + std::atomic<bool> m_protocol_released; + volatile uint32_t m_invoke_buf_ready; + + volatile int m_invoke_result_code; + + critical_section m_local_inv_buff_lock; + std::string m_local_inv_buff; + + critical_section m_send_lock; + critical_section m_call_lock; + + volatile uint32_t m_wait_count; + volatile uint32_t m_close_called; + bucket_head2 m_current_head; + net_utils::i_service_endpoint* m_pservice_endpoint; + config_type& m_config; + t_connection_context& m_connection_context; + + std::string m_cache_in_buffer; + stream_state m_state; + + boost::int32_t m_oponent_protocol_ver; + bool m_connection_initialized; + + struct invoke_response_handler_base + { + virtual bool handle(int res, const std::string& buff, connection_context& context)=0; + virtual bool is_timer_started() const=0; + virtual void cancel()=0; + virtual bool cancel_timer()=0; + }; + template <class callback_t> + struct anvoke_handler: invoke_response_handler_base + { + anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command) + :m_cb(cb), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false), + m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command) + { + if(m_con.start_outer_call()) + { + m_timer.expires_from_now(boost::posix_time::milliseconds(timeout)); + m_timer.async_wait([&con, command, cb](const boost::system::error_code& ec) + { + if(ec == boost::asio::error::operation_aborted) + return; + LOG_PRINT_CC(con.get_context_ref(), "Timeout on invoke operation happened, command: " << command, LOG_LEVEL_2); + std::string fake; + cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref()); + con.close(); + con.finish_outer_call(); + }); + m_timer_started = true; + } + } + virtual ~anvoke_handler() + {} + callback_t m_cb; + async_protocol_handler& m_con; + boost::asio::deadline_timer m_timer; + bool m_timer_started; + bool m_cancel_timer_called; + bool m_timer_cancelled; + int m_command; + virtual bool handle(int res, const std::string& buff, typename async_protocol_handler::connection_context& context) + { + if(!cancel_timer()) + return false; + m_cb(res, buff, context); + m_con.finish_outer_call(); + return true; + } + virtual bool is_timer_started() const + { + return m_timer_started; + } + virtual void cancel() + { + if(cancel_timer()) + { + std::string fake; + m_cb(LEVIN_ERROR_CONNECTION_DESTROYED, fake, m_con.get_context_ref()); + m_con.finish_outer_call(); + } + } + virtual bool cancel_timer() + { + if(!m_cancel_timer_called) + { + m_cancel_timer_called = true; + boost::system::error_code ignored_ec; + m_timer_cancelled = 1 == m_timer.cancel(ignored_ec); + } + return m_timer_cancelled; + } + }; + critical_section m_invoke_response_handlers_lock; + std::list<boost::shared_ptr<invoke_response_handler_base> > m_invoke_response_handlers; + + template<class callback_t> + bool add_invoke_response_handler(callback_t cb, uint64_t timeout, async_protocol_handler& con, int command) + { + CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock); + boost::shared_ptr<invoke_response_handler_base> handler(boost::make_shared<anvoke_handler<callback_t>>(cb, timeout, con, command)); + m_invoke_response_handlers.push_back(handler); + return handler->is_timer_started(); + } + template<class callback_t> friend struct anvoke_handler; +public: + async_protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, + config_type& config, + t_connection_context& conn_context): + m_current_head(bucket_head2()), + m_pservice_endpoint(psnd_hndlr), + m_config(config), + m_connection_context(conn_context), + m_state(stream_state_head) + { + m_close_called = 0; + m_deletion_initiated = false; + m_protocol_released = false; + m_wait_count = 0; + m_oponent_protocol_ver = 0; + m_connection_initialized = false; + } + virtual ~async_protocol_handler() + { + m_deletion_initiated = true; + if(m_connection_initialized) + { + m_config.del_connection(this); + } + + for (size_t i = 0; i < 60 * 1000 / 100 && 0 != boost::interprocess::ipcdetail::atomic_read32(&m_wait_count); ++i) + { + misc_utils::sleep_no_w(100); + } + CHECK_AND_ASSERT_MES_NO_RET(0 == boost::interprocess::ipcdetail::atomic_read32(&m_wait_count), "Failed to wait for operation completion. m_wait_count = " << m_wait_count); + + LOG_PRINT_CC(m_connection_context, "~async_protocol_handler()", LOG_LEVEL_4); + } + + bool start_outer_call() + { + LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] -->> start_outer_call"); + if(!m_pservice_endpoint->add_ref()) + { + LOG_PRINT_CC_RED(m_connection_context, "[levin_protocol] -->> start_outer_call failed", LOG_LEVEL_4); + return false; + } + boost::interprocess::ipcdetail::atomic_inc32(&m_wait_count); + return true; + } + bool finish_outer_call() + { + LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] <<-- finish_outer_call"); + boost::interprocess::ipcdetail::atomic_dec32(&m_wait_count); + m_pservice_endpoint->release(); + return true; + } + + bool release_protocol() + { + decltype(m_invoke_response_handlers) local_invoke_response_handlers; + CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock); + local_invoke_response_handlers.swap(m_invoke_response_handlers); + m_protocol_released = true; + CRITICAL_REGION_END(); + + // Never call callback inside critical section, that can cause deadlock. Callback can be called when + // invoke_response_handler_base is cancelled + std::for_each(local_invoke_response_handlers.begin(), local_invoke_response_handlers.end(), [](const boost::shared_ptr<invoke_response_handler_base>& pinv_resp_hndlr) { + pinv_resp_hndlr->cancel(); + }); + + return true; + } + + bool close() + { + boost::interprocess::ipcdetail::atomic_inc32(&m_close_called); + + m_pservice_endpoint->close(); + return true; + } + + void update_connection_context(const connection_context& contxt) + { + m_connection_context = contxt; + } + + void request_callback() + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + m_pservice_endpoint->request_callback(); + } + + void handle_qued_callback() + { + m_config.m_pcommands_handler->callback(m_connection_context); + } + + virtual bool handle_recv(const void* ptr, size_t cb) + { + if(boost::interprocess::ipcdetail::atomic_read32(&m_close_called)) + return false; //closing connections + + if(!m_config.m_pcommands_handler) + { + LOG_ERROR_CC(m_connection_context, "Commands handler not set!"); + return false; + } + + if(m_cache_in_buffer.size() + cb > m_config.m_max_packet_size) + { + LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size + << ", packet received " << m_cache_in_buffer.size() + cb + << ", connection will be closed."); + return false; + } + + m_cache_in_buffer.append((const char*)ptr, cb); + + bool is_continue = true; + while(is_continue) + { + switch(m_state) + { + case stream_state_body: + if(m_cache_in_buffer.size() < m_current_head.m_cb) + { + is_continue = false; + break; + } + { + std::string buff_to_invoke; + if(m_cache_in_buffer.size() == m_current_head.m_cb) + buff_to_invoke.swap(m_cache_in_buffer); + else + { + buff_to_invoke.assign(m_cache_in_buffer, 0, (std::string::size_type)m_current_head.m_cb); + m_cache_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb); + } + + bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE); + + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_RECIEVED. [len=" << m_current_head.m_cb + << ", flags" << m_current_head.m_flags + << ", r?=" << m_current_head.m_have_to_return_data + <<", cmd = " << m_current_head.m_command + << ", v=" << m_current_head.m_protocol_version); + + if(is_response) + {//response to some invoke + + epee::critical_region_t<decltype(m_invoke_response_handlers_lock)> invoke_response_handlers_guard(m_invoke_response_handlers_lock); + if(!m_invoke_response_handlers.empty()) + {//async call scenario + boost::shared_ptr<invoke_response_handler_base> response_handler = m_invoke_response_handlers.front(); + bool timer_cancelled = response_handler->cancel_timer(); + // Don't pop handler, to avoid destroying it + if(timer_cancelled) + m_invoke_response_handlers.pop_front(); + invoke_response_handlers_guard.unlock(); + + if(timer_cancelled) + response_handler->handle(m_current_head.m_command, buff_to_invoke, m_connection_context); + } + else + { + invoke_response_handlers_guard.unlock(); + //use sync call scenario + if(!boost::interprocess::ipcdetail::atomic_read32(&m_wait_count) && !boost::interprocess::ipcdetail::atomic_read32(&m_close_called)) + { + LOG_ERROR_CC(m_connection_context, "no active invoke when response came, wtf?"); + return false; + }else + { + CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); + buff_to_invoke.swap(m_local_inv_buff); + buff_to_invoke.clear(); + m_invoke_result_code = m_current_head.m_return_code; + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 1); + } + } + }else + { + if(m_current_head.m_have_to_return_data) + { + std::string return_buff; + m_current_head.m_return_code = m_config.m_pcommands_handler->invoke( + m_current_head.m_command, + buff_to_invoke, + return_buff, + m_connection_context); + m_current_head.m_cb = return_buff.size(); + m_current_head.m_have_to_return_data = false; + m_current_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + m_current_head.m_flags = LEVIN_PACKET_RESPONSE; + std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); + send_buff += return_buff; + CRITICAL_REGION_BEGIN(m_send_lock); + if(!m_pservice_endpoint->do_send(send_buff.data(), send_buff.size())) + return false; + CRITICAL_REGION_END(); + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << m_current_head.m_cb + << ", flags" << m_current_head.m_flags + << ", r?=" << m_current_head.m_have_to_return_data + <<", cmd = " << m_current_head.m_command + << ", ver=" << m_current_head.m_protocol_version); + } + else + m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_connection_context); + } + } + m_state = stream_state_head; + break; + case stream_state_head: + { + if(m_cache_in_buffer.size() < sizeof(bucket_head2)) + { + if(m_cache_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cache_in_buffer.data()) != LEVIN_SIGNATURE) + { + LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); + return false; + } + is_continue = false; + break; + } + + bucket_head2* phead = (bucket_head2*)m_cache_in_buffer.data(); + if(LEVIN_SIGNATURE != phead->m_signature) + { + LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); + return false; + } + m_current_head = *phead; + + m_cache_in_buffer.erase(0, sizeof(bucket_head2)); + m_state = stream_state_body; + m_oponent_protocol_ver = m_current_head.m_protocol_version; + if(m_current_head.m_cb > m_config.m_max_packet_size) + { + LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size + << ", packet header received " << m_current_head.m_cb + << ", connection will be closed."); + return false; + } + } + break; + default: + LOG_ERROR_CC(m_connection_context, "Undefined state in levin_server_impl::connection_handler, m_state=" << m_state); + return false; + } + } + + return true; + } + + bool after_init_connection() + { + if (!m_connection_initialized) + { + m_connection_initialized = true; + m_config.add_connection(this); + } + return true; + } + + template<class callback_t> + bool async_invoke(int command, const std::string& in_buff, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + if(timeout == LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + timeout = m_config.m_invoke_timeout; + + int err_code = LEVIN_OK; + do + { + if(m_deletion_initiated) + { + err_code = LEVIN_ERROR_CONNECTION_DESTROYED; + break; + } + + CRITICAL_REGION_LOCAL(m_call_lock); + + if(m_deletion_initiated) + { + err_code = LEVIN_ERROR_CONNECTION_DESTROYED; + break; + } + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + + head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); + CRITICAL_REGION_BEGIN(m_send_lock); + CRITICAL_REGION_LOCAL1(m_invoke_response_handlers_lock); + if(!m_pservice_endpoint->do_send(&head, sizeof(head))) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + err_code = LEVIN_ERROR_CONNECTION; + break; + } + + if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + err_code = LEVIN_ERROR_CONNECTION; + break; + } + + if(!add_invoke_response_handler(cb, timeout, *this, command)) + { + err_code = LEVIN_ERROR_CONNECTION_DESTROYED; + break; + } + CRITICAL_REGION_END(); + } while (false); + + if (LEVIN_OK != err_code) + { + std::string stub_buff; + // Never call callback inside critical section, that can cause deadlock + cb(err_code, stub_buff, m_connection_context); + return false; + } + + return true; + } + + int invoke(int command, const std::string& in_buff, std::string& buff_out) + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + CRITICAL_REGION_LOCAL(m_call_lock); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + + head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); + CRITICAL_REGION_BEGIN(m_send_lock); + if(!m_pservice_endpoint->do_send(&head, sizeof(head))) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + return LEVIN_ERROR_CONNECTION; + } + + if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + return LEVIN_ERROR_CONNECTION; + } + CRITICAL_REGION_END(); + + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb + << ", f=" << head.m_flags + << ", r?=" << head.m_have_to_return_data + << ", cmd = " << head.m_command + << ", ver=" << head.m_protocol_version); + + uint64_t ticks_start = misc_utils::get_tick_count(); + + while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_buf_ready) && !m_deletion_initiated && !m_protocol_released) + { + if(misc_utils::get_tick_count() - ticks_start > m_config.m_invoke_timeout) + { + LOG_PRINT_CC_L2(m_connection_context, "invoke timeout (" << m_config.m_invoke_timeout << "), closing connection "); + close(); + return LEVIN_ERROR_CONNECTION_TIMEDOUT; + } + if(!m_pservice_endpoint->call_run_once_service_io()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + + if(m_deletion_initiated || m_protocol_released) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); + buff_out.swap(m_local_inv_buff); + m_local_inv_buff.clear(); + CRITICAL_REGION_END(); + + return m_invoke_result_code; + } + + int notify(int command, const std::string& in_buff) + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + CRITICAL_REGION_LOCAL(m_call_lock); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_have_to_return_data = false; + head.m_cb = in_buff.size(); + + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + CRITICAL_REGION_BEGIN(m_send_lock); + if(!m_pservice_endpoint->do_send(&head, sizeof(head))) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); + return -1; + } + + if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + { + LOG_ERROR("Failed to do_send()"); + return -1; + } + CRITICAL_REGION_END(); + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb << + ", f=" << head.m_flags << + ", r?=" << head.m_have_to_return_data << + ", cmd = " << head.m_command << + ", ver=" << head.m_protocol_version); + + return 1; + } + //------------------------------------------------------------------------------------------ + boost::uuids::uuid get_connection_id() {return m_connection_context.m_connection_id;} + //------------------------------------------------------------------------------------------ + t_connection_context& get_context_ref() {return m_connection_context;} +}; +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +void async_protocol_handler_config<t_connection_context>::del_connection(async_protocol_handler<t_connection_context>* pconn) +{ + CRITICAL_REGION_BEGIN(m_connects_lock); + m_connects.erase(pconn->get_connection_id()); + CRITICAL_REGION_END(); + m_pcommands_handler->on_connection_close(pconn->m_connection_context); +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +void async_protocol_handler_config<t_connection_context>::add_connection(async_protocol_handler<t_connection_context>* pconn) +{ + CRITICAL_REGION_BEGIN(m_connects_lock); + m_connects[pconn->get_connection_id()] = pconn; + CRITICAL_REGION_END(); + m_pcommands_handler->on_connection_new(pconn->m_connection_context); +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +async_protocol_handler<t_connection_context>* async_protocol_handler_config<t_connection_context>::find_connection(boost::uuids::uuid connection_id) const +{ + auto it = m_connects.find(connection_id); + return it == m_connects.end() ? 0 : it->second; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +int async_protocol_handler_config<t_connection_context>::find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler<t_connection_context>*& aph) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + aph = find_connection(connection_id); + if(0 == aph) + return LEVIN_ERROR_CONNECTION_NOT_FOUND; + if(!aph->start_outer_call()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + return LEVIN_OK; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +int async_protocol_handler_config<t_connection_context>::invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id) +{ + async_protocol_handler<t_connection_context>* aph; + int r = find_and_lock_connection(connection_id, aph); + return LEVIN_OK == r ? aph->invoke(command, in_buff, buff_out) : r; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> template<class callback_t> +int async_protocol_handler_config<t_connection_context>::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout) +{ + async_protocol_handler<t_connection_context>* aph; + int r = find_and_lock_connection(connection_id, aph); + return LEVIN_OK == r ? aph->async_invoke(command, in_buff, cb, timeout) : r; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> template<class callback_t> +bool async_protocol_handler_config<t_connection_context>::foreach_connection(callback_t cb) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + for(auto& c: m_connects) + { + async_protocol_handler<t_connection_context>* aph = c.second; + if(!cb(aph->get_context_ref())) + return false; + } + return true; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +size_t async_protocol_handler_config<t_connection_context>::get_connections_count() +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + return m_connects.size(); +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id) +{ + async_protocol_handler<t_connection_context>* aph; + int r = find_and_lock_connection(connection_id, aph); + return LEVIN_OK == r ? aph->notify(command, in_buff) : r; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +bool async_protocol_handler_config<t_connection_context>::close(boost::uuids::uuid connection_id) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + async_protocol_handler<t_connection_context>* aph = find_connection(connection_id); + return 0 != aph ? aph->close() : false; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +bool async_protocol_handler_config<t_connection_context>::update_connection_context(const t_connection_context& contxt) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + async_protocol_handler<t_connection_context>* aph = find_connection(contxt.m_connection_id); + if(0 == aph) + return false; + aph->update_connection_context(contxt); + return true; +} +//------------------------------------------------------------------------------------------ +template<class t_connection_context> +bool async_protocol_handler_config<t_connection_context>::request_callback(boost::uuids::uuid connection_id) +{ + async_protocol_handler<t_connection_context>* aph; + int r = find_and_lock_connection(connection_id, aph); + if(LEVIN_OK == r) + { + aph->request_callback(); + return true; + } + else + { + return false; + } +} +} +} diff --git a/contrib/epee/include/net/levin_server_cp.h b/contrib/epee/include/net/levin_server_cp.h new file mode 100644 index 000000000..8ece35059 --- /dev/null +++ b/contrib/epee/include/net/levin_server_cp.h @@ -0,0 +1,47 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server_cp.h" +#include "levin_protocol_handler.h" +namespace epee +{ +namespace net_utils +{ + typedef cp_server_impl<levin::protocol_handler> cp_levin_server; +} +} + + + +#endif + + diff --git a/contrib/epee/include/net/levin_server_cp2.h b/contrib/epee/include/net/levin_server_cp2.h new file mode 100644 index 000000000..b29d49bf8 --- /dev/null +++ b/contrib/epee/include/net/levin_server_cp2.h @@ -0,0 +1,49 @@ +// 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. +// + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server2.h" +#include "levin_protocol_handler.h" +#include "levin_protocol_handler_async.h" + +namespace epee +{ +namespace net_utils +{ + typedef boosted_tcp_server<levin::protocol_handler<> > boosted_levin_server; + typedef boosted_tcp_server<levin::async_protocol_handler<> > boosted_levin_async_server; +} +} + + + +#endif + + diff --git a/contrib/epee/include/net/local_ip.h b/contrib/epee/include/net/local_ip.h new file mode 100644 index 000000000..028ad73ef --- /dev/null +++ b/contrib/epee/include/net/local_ip.h @@ -0,0 +1,72 @@ +// 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 + +namespace epee +{ + namespace net_utils + { + inline + bool is_ip_local(boost::uint32_t ip) + { + /* + local ip area + 10.0.0.0 — 10.255.255.255 + 172.16.0.0 — 172.31.255.255 + 192.168.0.0 — 192.168.255.255 + */ + if( (ip | 0xffffff00) == 0xffffff0a) + return true; + + if( (ip | 0xffff0000) == 0xffffa8c0) + return true; + + if( (ip | 0xffffff00) == 0xffffffac) + { + uint32_t second_num = (ip << 8) & 0xff000000; + if(second_num >= 16 && second_num <= 31 ) + return true; + } + return false; + } + inline + bool is_ip_loopback(boost::uint32_t ip) + { + if( (ip | 0xffffff00) == 0xffffff7f) + return true; + //MAKE_IP + /* + loopback ip + 127.0.0.0 — 127.255.255.255 + */ + return false; + } + + } +} + diff --git a/contrib/epee/include/net/multiprotocols_server.h b/contrib/epee/include/net/multiprotocols_server.h new file mode 100644 index 000000000..4807a4421 --- /dev/null +++ b/contrib/epee/include/net/multiprotocols_server.h @@ -0,0 +1,47 @@ +// 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. +// + + + +#ifndef _MULTIPROTOCOLS_SERVER_H_ +#define _MULTIPROTOCOLS_SERVER_H_ + +//#include "abstract_tcp_server_cp.h" +#include "protocol_switcher.h" +#include "abstract_tcp_server2.h" + +namespace epee +{ +namespace net_utils +{ + //typedef cp_server_impl<net_utils::protocol_switcher> multiprotocol_server; + typedef boosted_tcp_server<net_utils::protocol_switcher> boosted_multiprotocol_server; +} +} + + +#endif //_MULTIPROTOCOLS_SERVER_H_ + diff --git a/contrib/epee/include/net/munin_connection_handler.h b/contrib/epee/include/net/munin_connection_handler.h new file mode 100644 index 000000000..8579339c5 --- /dev/null +++ b/contrib/epee/include/net/munin_connection_handler.h @@ -0,0 +1,376 @@ +// 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. +// + + + +#ifndef _MUNIN_CONNECTION_HANDLER_H_ +#define _MUNIN_CONNECTION_HANDLER_H_ + +#include <string> +#include "net_utils_base.h" +#include "to_nonconst_iterator.h" +#include "http_base.h" +#include "reg_exp_definer.h" + +#define MUNIN_ARGS_DEFAULT(vertial_lable_str) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " \n" +#define MUNIN_ARGS_FORCE_AUPPER_LIMIT(vertial_lable_str, limit) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " --rigid --upper-limit " limit " \n" +#define MUNIN_TITLE(title_str) "graph_title " title_str "\n" +#define MUNIN_CATEGORY(category_str) "graph_category " category_str "\n" +#define MUNIN_INFO(info_str) "graph_info " info_str "\n" +#define MUNIN_ENTRY(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" +#define MUNIN_ENTRY_AREA(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" #var_name".draw AREASTACK\n" +#define MUNIN_ENTRY_ALIAS(var_name, alias) #var_name".label " #alias"\n" #var_name".info "#alias".\n" +#define BEGIN_MUNIN_SERVICE(servivece_name_str) if(servivece_name_str == pservice->m_service_name) { +#define END_MUNIN_SERVICE() } +#define MUNIN_SERVICE_PARAM(munin_var_name_str, variable) paramters_text += std::string() + munin_var_name_str ".value " + boost::lexical_cast<std::string>(variable) + "\n" + + + + +namespace epee +{ +namespace net_utils +{ + namespace munin + { + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct munin_service; + + struct munin_service_data_provider + { + virtual bool update_service_data(munin_service* pservice, std::string& paramters_text)=0; + }; + + struct munin_service + { + std::string m_service_name; + std::string m_service_config_string; + munin_service_data_provider* m_pdata_provider; + }; + + struct node_server_config + { + std::list<munin_service> m_services; + //TODO: + }; + + struct fake_send_handler: public i_service_endpoint + { + virtual bool do_send(const void* ptr, size_t cb) + { + m_cache += std::string((const char*)ptr, cb); + return true; + } + public: + + std::string m_cache; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class munin_node_server_connection_handler + { + public: + typedef node_server_config config_type; + typedef connection_context_base connection_context; + + munin_node_server_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, const connection_context_base& context):m_psnd_hndlr(psnd_hndlr), + m_machine_state(http_state_retriving_comand_line), + m_config(config) + { + init(); + } + virtual ~munin_node_server_connection_handler() + { + + } + + bool release_protocol() + { + return true; + } + bool after_init_connection() + { + std::string hello_str = "# munin node at "; + hello_str += m_host_name + "\n"; + send_hook(hello_str); + return true; + } + + virtual bool thread_init() + { + return true; + } + + virtual bool thread_deinit() + { + return true; + } + + void handle_qued_callback() + { + + } + + virtual bool handle_recv(const void* ptr, size_t cb) + { + + const char* pbuff = (const char*)ptr; + std::string recvd_buff(pbuff, cb); + LOG_PRINT("munin_recv: \n" << recvd_buff, LOG_LEVEL_3); + + m_cache += recvd_buff; + + bool stop_handling = false; + while(!stop_handling) + { + switch(m_machine_state) + { + case http_state_retriving_comand_line: + { + + std::string::size_type fpos = m_cache.find('\n'); + if(std::string::npos != fpos ) + { + bool res = handle_command(m_cache); + if(!res) + return false; + m_cache.erase(0, fpos+1); + continue; + } + stop_handling = true; + } + break; + case http_state_error: + stop_handling = true; + return false; + default: + LOG_ERROR("Error in munin state machine! Unkonwon state=" << m_machine_state); + stop_handling = true; + m_machine_state = http_state_error; + return false; + } + + } + + return true; + } + + private: + + + bool init() + { + char hostname[64] = {0}; + int res = gethostname(hostname, 64); + hostname[63] = 0;//be happy + m_host_name = hostname; + return true; + } + bool handle_command(const std::string& command) + { + // list, nodes, config, fetch, version or quit + STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^((list)|(nodes)|(config)|(fetch)|(version)|(quit))(\\s+(\\S+))?", boost::regex::icase | boost::regex::normal); + // 12 3 4 5 6 7 8 9 + size_t match_len = 0; + boost::smatch result; + if(boost::regex_search(command, result, rexp_match_command_line, boost::match_default) && result[0].matched) + { + if(result[2].matched) + {//list command + return handle_list_command(); + }else if(result[3].matched) + {//nodes command + return handle_nodes_command(); + }else if(result[4].matched) + {//config command + if(result[9].matched) + return handle_config_command(result[9]); + else + { + send_hook("Unknown service\n"); + } + }else if(result[5].matched) + {//fetch command + if(result[9].matched) + return handle_fetch_command(result[9]); + else + { + send_hook("Unknown service\n"); + } + }else if(result[6].matched) + {//version command + return handle_version_command(); + }else if(result[7].matched) + {//quit command + return handle_quit_command(); + } + else + return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n"); + } + + return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n");; + } + + bool handle_list_command() + { + std::string buff_to_send; + for(std::list<munin_service>::const_iterator it = m_config.m_services.begin(); it!=m_config.m_services.end();it++) + { + buff_to_send += it->m_service_name + " "; + } + buff_to_send+='\n'; + return send_hook(buff_to_send); + } + bool handle_nodes_command() + { + //supports only one node - host name + send_hook(m_host_name + "\n.\n"); + return true; + } + bool handle_config_command(const std::string& service_name) + { + munin_service* psrv = get_service_by_name(service_name); + if(!psrv) + return send_hook(std::string() + "Unknown service\n"); + + + return send_hook(psrv->m_service_config_string + ".\n"); + } + + bool handle_fetch_command(const std::string& service_name) + { + munin_service* psrv = get_service_by_name(service_name); + if(!psrv) + return send_hook(std::string() + "Unknown service\n"); + + std::string buff; + psrv->m_pdata_provider->update_service_data(psrv, buff); + + buff += ".\n"; + return send_hook(buff); + } + bool handle_version_command() + { + return send_hook("Munin node component by Andrey Sabelnikov\n"); + } + bool handle_quit_command() + { + return false; + } + + bool send_hook(const std::string& buff) + { + LOG_PRINT("munin_send: \n" << buff, LOG_LEVEL_3); + + if(m_psnd_hndlr) + return m_psnd_hndlr->do_send(buff.data(), buff.size()); + else + return false; + } + + + munin_service* get_service_by_name(const std::string& srv_name) + { + std::list<munin_service>::iterator it = m_config.m_services.begin(); + for(; it!=m_config.m_services.end(); it++) + if(it->m_service_name == srv_name) + break; + + if(it==m_config.m_services.end()) + return NULL; + + return &(*it); + } + + enum machine_state{ + http_state_retriving_comand_line, + http_state_error + }; + + + config_type& m_config; + machine_state m_machine_state; + std::string m_cache; + std::string m_host_name; + protected: + i_service_endpoint* m_psnd_hndlr; + }; + + + inline bool test_self() + { + /*WSADATA w; + ::WSAStartup(MAKEWORD(1, 1), &w); + node_server_config sc; + sc.m_services.push_back(munin_service()); + sc.m_services.back().m_service_name = "test_service"; + + sc.m_services.back().m_service_config_string = + "graph_args --base 1000 -l 0 --vertical-label N --upper-limit 329342976\n" + "graph_title REPORTS STATICTICS\n" + "graph_category bind\n" + "graph_info This graph shows how many reports came in fixed time period.\n" + "graph_order apps free swap\n" + "apps.label apps\n" + "apps.draw AREA\n" + "apps.info Memory used by user-space applications.\n" + "swap.label swap\n" + "swap.draw STACK\n" + "swap.info Swap space used.\n" + "free.label unused\n" + "free.draw STACK\n" + "free.info Wasted memory. Memory that is not used for anything at all.\n" + "committed.label committed\n" + "committed.draw LINE2\n" + "committed.warn 625410048\n" + "committed.info The amount of memory that would be used if all the memory that's been allocated were to be used.\n"; + + + sc.m_services.push_back(munin_service()); + sc.m_services.back().m_service_name = "test_service1"; + fake_send_handler fh; + munin_node_server_connection_handler mh(&fh, sc); + + std::string buff = "list\n"; + mh.handle_recv(buff.data(), buff.size()); + + + buff = "nodes\n"; + mh.handle_recv(buff.data(), buff.size()); +*/ + return true; + } + + } +} +} +#endif//!_MUNIN_CONNECTION_HANDLER_H_
\ No newline at end of file diff --git a/contrib/epee/include/net/munin_node_server.h b/contrib/epee/include/net/munin_node_server.h new file mode 100644 index 000000000..07637f550 --- /dev/null +++ b/contrib/epee/include/net/munin_node_server.h @@ -0,0 +1,49 @@ +// 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. +// + + + +#ifndef _MUNIN_NODE_SERVER_H_ +#define _MUNIN_NODE_SERVER_H_ + +#include <string> +//#include "net_utils_base.h" +#include "munin_connection_handler.h" +//#include "abstract_tcp_server.h" +//#include "abstract_tcp_server_cp.h" +#include "abstract_tcp_server2.h" +namespace epee +{ +namespace net_utils +{ + namespace munin + { + typedef boosted_tcp_server<munin_node_server_connection_handler> munin_node_server; + //typedef cp_server_impl<munin_node_server_connection_handler> munin_node_cp_server; + } +} +} +#endif//!_MUNIN_NODE_SERVER_H_
\ No newline at end of file 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)); + } + }; +} +} diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h new file mode 100644 index 000000000..16641a970 --- /dev/null +++ b/contrib/epee/include/net/net_parse_helpers.h @@ -0,0 +1,168 @@ +// 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 "http_base.h" +#include "reg_exp_definer.h" + + +namespace epee +{ +namespace net_utils +{ + + inline bool parse_uri_query(const std::string& query, std::list<std::pair<std::string, std::string> >& params) + { + enum state + { + st_param_name, + st_param_val + }; + state st = st_param_name; + std::string::const_iterator start_it = query.begin(); + std::pair<std::string, std::string> e; + for(std::string::const_iterator it = query.begin(); it != query.end(); it++) + { + switch(st) + { + case st_param_name: + if(*it == '=') + { + e.first.assign(start_it, it); + start_it = it;++start_it; + st = st_param_val; + } + break; + case st_param_val: + if(*it == '&') + { + e.second.assign(start_it, it); + start_it = it;++start_it; + params.push_back(e); + e.first.clear();e.second.clear(); + st = st_param_name; + } + break; + default: + LOG_ERROR("Unknown state " << (int)st); + return false; + } + } + if(st == st_param_name) + { + if(start_it != query.end()) + { + e.first.assign(start_it, query.end()); + params.push_back(e); + } + }else + { + if(start_it != query.end()) + e.second.assign(start_it, query.end()); + + if(e.first.size()) + params.push_back(e); + } + return true; + } + + inline + bool parse_uri(const std::string uri, http::uri_content& content) + { + + ///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash= + content.m_query_params.clear(); + STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal); + + boost::smatch result; + if(!boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched) + { + LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << uri); + content.m_path = uri; + return true; + } + if(result[1].matched) + { + content.m_path = result[1]; + } + if(result[3].matched) + { + content.m_query = result[3]; + } + if(result[5].matched) + { + content.m_fragment = result[5]; + } + if(content.m_query.size()) + { + parse_uri_query(content.m_query, content.m_query_params); + } + return true; + } + + + inline + bool parse_url(const std::string url_str, http::url_content& content) + { + + ///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash= + //STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal); + STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(([^/:]*)(:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal); + // 12 34 5 6 7 + content.port = 0; + boost::smatch result; + if(!boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched) + { + LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << rexp_match_uri); + //content.m_path = uri; + return true; + } + if(result[2].matched) + { + content.schema = result[2]; + } + if(result[4].matched) + { + content.host = result[4]; + } + if(result[6].matched) + { + content.port = boost::lexical_cast<boost::uint64_t>(result[6]); + } + if(result[7].matched) + { + content.uri = result[7]; + return parse_uri(result[7], content.m_uri_content); + } + + return true; + } + +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h new file mode 100644 index 000000000..86797bb85 --- /dev/null +++ b/contrib/epee/include/net/net_utils_base.h @@ -0,0 +1,150 @@ +// 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. +// + + + +#ifndef _NET_UTILS_BASE_H_ +#define _NET_UTILS_BASE_H_ + +#include <boost/uuid/uuid.hpp> +#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 +{ + /************************************************************************/ + /* */ + /************************************************************************/ + struct connection_context_base + { + const boost::uuids::uuid m_connection_id; + const boost::uint32_t m_remote_ip; + const boost::uint32_t m_remote_port; + const bool m_is_income; + + connection_context_base(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income): + m_connection_id(connection_id), + m_remote_ip(remote_ip), + m_remote_port(remote_port), + m_is_income(is_income) + + + {} + + connection_context_base(): m_connection_id(), + m_remote_ip(0), + m_remote_port(0), + m_is_income(false) + {} + + connection_context_base& operator=(const connection_context_base& a) + { + set_details(a.m_connection_id, a.m_remote_ip, a.m_remote_port, a.m_is_income); + return *this; + } + + private: + template<class t_protocol_handler> + friend class connection; + void set_details(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income) + { + this->~connection_context_base(); + new(this) connection_context_base(connection_id, remote_ip, remote_port, is_income); + } + + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct i_service_endpoint + { + virtual bool do_send(const void* ptr, size_t cb)=0; + virtual bool close()=0; + virtual bool call_run_once_service_io()=0; + virtual bool request_callback()=0; + virtual boost::asio::io_service& get_io_service()=0; + //protect from deletion connection object(with protocol instance) during external call "invoke" + virtual bool add_ref()=0; + virtual bool release()=0; + protected: + virtual ~i_service_endpoint(){} + }; + + + //some helpers + + + inline + std::string print_connection_context(const connection_context_base& ctx) + { + std::stringstream ss; + ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); + return ss.str(); + } + + inline + std::string print_connection_context_short(const connection_context_base& ctx) + { + std::stringstream ss; + ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << (ctx.m_is_income ? " INC":" OUT"); + return ss.str(); + } + +#define LOG_PRINT_CC(ct, message, log_level) LOG_PRINT("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_PRINT_CC_GREEN(ct, message, log_level) LOG_PRINT_GREEN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_PRINT_CC_RED(ct, message, log_level) LOG_PRINT_RED("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_PRINT_CC_BLUE(ct, message, log_level) LOG_PRINT_BLUE("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_ERROR_CC(ct, message) LOG_ERROR("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) + +#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L1(ct, message) LOG_PRINT_L1("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L2(ct, message) LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L3(ct, message) LOG_PRINT_L3("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L4(ct, message) LOG_PRINT_L4("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) + +#define LOG_PRINT_CCONTEXT_L0(message) LOG_PRINT_CC_L0(context, message) +#define LOG_PRINT_CCONTEXT_L1(message) LOG_PRINT_CC_L1(context, message) +#define LOG_PRINT_CCONTEXT_L2(message) LOG_PRINT_CC_L2(context, message) +#define LOG_PRINT_CCONTEXT_L3(message) LOG_PRINT_CC_L3(context, message) +#define LOG_ERROR_CCONTEXT(message) LOG_ERROR_CC(context, message) + +#define LOG_PRINT_CCONTEXT_GREEN(message, log_level) LOG_PRINT_CC_GREEN(context, message, log_level) +#define LOG_PRINT_CCONTEXT_RED(message, log_level) LOG_PRINT_CC_RED(context, message, log_level) +#define LOG_PRINT_CCONTEXT_BLUE(message, log_level) LOG_PRINT_CC_BLUE(context, message, log_level) + +#define CHECK_AND_ASSERT_MES_CC(condition, return_val, err_message) CHECK_AND_ASSERT_MES(condition, return_val, "[" << epee::net_utils::print_connection_context_short(context) << "]" << err_message) + +} +} + +#endif //_NET_UTILS_BASE_H_ diff --git a/contrib/epee/include/net/protocol_switcher.h b/contrib/epee/include/net/protocol_switcher.h new file mode 100644 index 000000000..f9a6dbe6f --- /dev/null +++ b/contrib/epee/include/net/protocol_switcher.h @@ -0,0 +1,121 @@ +// 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. +// + + + +#ifndef _PROTOCOL_SWITCHER_H_ +#define _PROTOCOL_SWITCHER_H_ + +#include "levin_base.h" +#include "http_server.h" +#include "levin_protocol_handler.h" +//#include "abstract_tcp_server.h" + +namespace epee +{ +namespace net_utils +{ + struct protocl_switcher_config + { + http::http_custom_handler::config_type m_http_config; + levin::protocol_handler::config_type m_levin_config; + }; + + + struct i_protocol_handler + { + virtual bool handle_recv(const void* ptr, size_t cb)=0; + }; + + template<class t> + class t_protocol_handler: public i_protocol_handler + { + public: + typedef t t_type; + t_protocol_handler(i_service_endpoint* psnd_hndlr, typename t_type::config_type& config, const connection_context& conn_context):m_hadler(psnd_hndlr, config, conn_context) + {} + private: + bool handle_recv(const void* ptr, size_t cb) + { + return m_hadler.handle_recv(ptr, cb); + } + t_type m_hadler; + }; + + + class protocol_switcher + { + public: + typedef protocl_switcher_config config_type; + + protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context); + virtual ~protocol_switcher(){} + + virtual bool handle_recv(const void* ptr, size_t cb); + + bool after_init_connection(){return true;} + private: + t_protocol_handler<http::http_custom_handler> m_http_handler; + t_protocol_handler<levin::protocol_handler> m_levin_handler; + i_protocol_handler* pcurrent_handler; + + std::string m_cached_buff; + }; + + protocol_switcher::protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):m_http_handler(psnd_hndlr, config.m_http_config, conn_context), m_levin_handler(psnd_hndlr, config.m_levin_config, conn_context), pcurrent_handler(NULL) + {} + + bool protocol_switcher::handle_recv(const void* ptr, size_t cb) + { + if(pcurrent_handler) + return pcurrent_handler->handle_recv(ptr, cb); + else + { + m_cached_buff.append((const char*)ptr, cb); + if(m_cached_buff.size() < sizeof(boost::uint64_t)) + return true; + + if(*((boost::uint64_t*)&m_cached_buff[0]) == LEVIN_SIGNATURE) + { + pcurrent_handler = &m_levin_handler; + return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size()); + } + if(m_cached_buff.substr(0, 4) == "GET " || m_cached_buff.substr(0, 4) == "POST") + { + pcurrent_handler = &m_http_handler; + return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size()); + }else + { + LOG_ERROR("Wrong protocol accepted on port..."); + return false; + } + } + + return true; + } +} +} +#endif //_PROTOCOL_SWITCHER_H_
\ No newline at end of file diff --git a/contrib/epee/include/net/rpc_method_name.h b/contrib/epee/include/net/rpc_method_name.h new file mode 100644 index 000000000..c226639c4 --- /dev/null +++ b/contrib/epee/include/net/rpc_method_name.h @@ -0,0 +1,31 @@ +// 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 + + +#define RPC_METHOD_NAME(name) static inline const char* methodname(){return name;}
\ No newline at end of file diff --git a/contrib/epee/include/net/smtp.h b/contrib/epee/include/net/smtp.h new file mode 100644 index 000000000..d2e8598fd --- /dev/null +++ b/contrib/epee/include/net/smtp.h @@ -0,0 +1,181 @@ +// 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 <iostream> +#include <istream> +#include <ostream> +#include <string> +#include <boost/asio.hpp> +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/archive/iterators/base64_from_binary.hpp> +#include <boost/archive/iterators/transform_width.hpp> +#include <boost/archive/iterators/ostream_iterator.hpp> + + +namespace epee +{ +namespace net_utils +{ + namespace smtp + { + + using boost::asio::ip::tcp; + using namespace boost::archive::iterators; + typedef base64_from_binary<transform_width<const char *,6,8> > base64_text; + + /************************************************************************/ + /* */ + /************************************************************************/ + class smtp_client + { + public: + smtp_client(std::string pServer,unsigned int pPort,std::string pUser,std::string pPassword): + mServer(pServer),mPort(pPort),mUserName(pUser),mPassword(pPassword),mSocket(mIOService),mResolver(mIOService) + { + tcp::resolver::query qry(mServer,boost::lexical_cast<std::string>( mPort )); + mResolver.async_resolve(qry,boost::bind(&smtp_client::handleResolve,this,boost::asio::placeholders::error, + boost::asio::placeholders::iterator)); + } + bool Send(std::string pFrom,std::string pTo,std::string pSubject,std::string pMessage) + { + mHasError = true; + mFrom=pFrom; + mTo=pTo; + mSubject=pSubject; + mMessage=pMessage; + mIOService.run(); + return !mHasError; + } + private: + std::string encodeBase64(std::string pData) + { + std::stringstream os; + size_t sz=pData.size(); + std::copy(base64_text(pData.c_str()),base64_text(pData.c_str()+sz),std::ostream_iterator<char>(os)); + return os.str(); + } + void handleResolve(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator) + { + if(!err) + { + tcp::endpoint endpoint=*endpoint_iterator; + mSocket.async_connect(endpoint, + boost::bind(&smtp_client::handleConnect,this,boost::asio::placeholders::error,++endpoint_iterator)); + } + else + { + mHasError=true; + mErrorMsg= err.message(); + } + } + void writeLine(std::string pData) + { + std::ostream req_strm(&mRequest); + req_strm << pData << "\r\n"; + boost::asio::write(mSocket,mRequest); + req_strm.clear(); + } + void readLine(std::string& pData) + { + boost::asio::streambuf response; + boost::asio::read_until(mSocket, response, "\r\n"); + std::istream response_stream(&response); + response_stream >> pData; + } + void handleConnect(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator) + { + if (!err) + { + std::string read_buff; + // The connection was successful. Send the request. + std::ostream req_strm(&mRequest); + writeLine("EHLO "+mServer); + readLine(read_buff);//220 + writeLine("AUTH LOGIN"); + readLine(read_buff);// + writeLine(encodeBase64(mUserName)); + readLine(read_buff); + writeLine(encodeBase64(mPassword)); + readLine(read_buff); + writeLine( "MAIL FROM:<"+mFrom+">"); + writeLine( "RCPT TO:<"+mTo+">"); + writeLine( "DATA"); + writeLine( "SUBJECT:"+mSubject); + writeLine( "From:"+mFrom); + writeLine( "To:"+mTo); + writeLine( ""); + writeLine( mMessage ); + writeLine( "\r\n.\r\n"); + readLine(read_buff); + if(read_buff == "250") + mHasError = false; + writeLine( "QUIT"); + } + else + { + mHasError=true; + mErrorMsg= err.message(); + } + } + std::string mServer; + std::string mUserName; + std::string mPassword; + std::string mFrom; + std::string mTo; + std::string mSubject; + std::string mMessage; + unsigned int mPort; + boost::asio::io_service mIOService; + tcp::resolver mResolver; + tcp::socket mSocket; + boost::asio::streambuf mRequest; + boost::asio::streambuf mResponse; + bool mHasError; + std::string mErrorMsg; + }; + + + bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_email, /*"STIL CRAWLER",*/ + const std::string& maillist, const std::string& subject, const std::string& body) + { + STD_TRY_BEGIN(); + //smtp_client mailc("yoursmtpserver.com",25,"user@yourdomain.com","password"); + //mailc.Send("from@yourdomain.com","to@somewhere.com","subject","Hello from C++ SMTP Client!"); + smtp_client mailc(server,port,login,pass); + return mailc.Send(from_email,maillist,subject,body); + STD_TRY_CATCH("at send_mail", false); + } + + } +} +} + +//#include "smtp.inl"
\ No newline at end of file diff --git a/contrib/epee/include/net/smtp.inl b/contrib/epee/include/net/smtp.inl new file mode 100644 index 000000000..d42c8b950 --- /dev/null +++ b/contrib/epee/include/net/smtp.inl @@ -0,0 +1,1569 @@ +// 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. +// + + + +#include "md5.h" + +namespace epee +{ +namespace net_utils +{ + namespace smtp + { + + + ////////////////////////////////////////////////////////////////////////// + inline char * convert_hex( unsigned char *in, int len ) + { + static char hex[] = "0123456789abcdef"; + char * out; + int i; + + out = (char *) malloc(len * 2 + 1); + if (out == NULL) + return NULL; + + for (i = 0; i < len; i++) { + out[i * 2] = hex[in[i] >> 4]; + out[i * 2 + 1] = hex[in[i] & 15]; + } + + out[i*2] = 0; + + return out; + } + + ////////////////////////////////////////////////////////////////////////// + inline char * hash_md5(const char * sec_key, const char * data, int len) + { + char key[65], digest[24]; + char * hash_hex; + + int sec_len, i; + + sec_len = strlen(sec_key); + + if (sec_len < 64) { + memcpy(key, sec_key, sec_len); + for (i = sec_len; i < 64; i++) { + key[i] = 0; + } + } else { + memcpy(key, sec_key, 64); + } + + md5::hmac_md5( (const unsigned char*)data, len, (const unsigned char*)key, 64, (unsigned char*)digest ); + hash_hex = convert_hex( (unsigned char*)digest, 16 ); + + return hash_hex; + } + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + inline CSMTPClient::CSMTPClient(void) + { + m_dwSupportedAuthModesCount = 0; + m_bConnected = FALSE; + m_hSocket = INVALID_SOCKET; + m_pErrorText = NULL; + + // Initialize WinSock + WORD wVer = MAKEWORD( 2, 2 ); + if ( WSAStartup( wVer, &m_wsaData ) != NO_ERROR ) + { + SetErrorText( "WSAStartup.", WSAGetLastError() ); + throw; + } + if ( LOBYTE( m_wsaData.wVersion ) != 2 || HIBYTE( m_wsaData.wVersion ) != 2 ) + { + SetErrorText( "Can't find a useable WinSock DLL." ); + WSACleanup(); + throw; + } + } + + ////////////////////////////////////////////////////////////////////////// + inline CSMTPClient::~CSMTPClient(void) + { + if ( m_pErrorText ) + { + free( m_pErrorText ); + m_pErrorText = NULL; + } + + if ( m_bConnected ) + ServerDisconnect(); + + // Cleanup + WSACleanup(); + } + + ////////////////////////////////////////////////////////////////////////// + inline void CSMTPClient::SetErrorText( LPCSTR szErrorText, DWORD dwErrorCode ) + { + if ( m_pErrorText ) + { + free( m_pErrorText ); + m_pErrorText = NULL; + } + + LPVOID lpMsgBuf = NULL; + if ( dwErrorCode ) + { + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, + 0, NULL ); + } + + if ( szErrorText && strlen( szErrorText ) ) + { + m_pErrorText = (LPBYTE)malloc( strlen( szErrorText ) + 1 ); + strcpy( (char*)m_pErrorText, szErrorText ); + + if ( lpMsgBuf ) + { + strcat( (char*)m_pErrorText, " " ); + strcpy( (char*)m_pErrorText, (char*)lpMsgBuf ); + + LocalFree( lpMsgBuf ); + } + } + } + + inline void CSMTPClient::SetErrorText( PBYTE szErrorText, DWORD dwErrorCode ) + { + SetErrorText( (LPCSTR)szErrorText, dwErrorCode ); + } + + ////////////////////////////////////////////////////////////////////////// + inline char* CSMTPClient::GetLastErrorText() + { + return (char*)m_pErrorText; + } + + ////////////////////////////////////////////////////////////////////////// + inline DWORD CSMTPClient::ReceiveData( SOCKET hSocket, PBYTE pReceiveBuffer, DWORD dwReceiveBufferSize ) + { + DWORD dwReceivedDataSize = 0; + + if ( hSocket != INVALID_SOCKET && pReceiveBuffer && dwReceiveBufferSize ) + { + int iReceived = 0; + int iLength = 0; + + iLength = recv( hSocket, (LPSTR)pReceiveBuffer + iReceived, dwReceiveBufferSize - iReceived, + NO_FLAGS ); + + if ( iLength != 0 && iLength != SOCKET_ERROR ) + iReceived += iLength; + + dwReceivedDataSize = iReceived; + + pReceiveBuffer[ iReceived ] = 0; + } + + return dwReceivedDataSize; + } + + inline ////////////////////////////////////////////////////////////////////////// + DWORD CSMTPClient::SendData( SOCKET hSocket, PBYTE pSendBuffer, DWORD dwSendBufferSize ) + { + DWORD dwSended = 0; + + if ( hSocket != INVALID_SOCKET && pSendBuffer && dwSendBufferSize ) + { + int iSended = 0; + int iLength = 0; + + while ( iLength != SOCKET_ERROR && dwSendBufferSize - iSended > 0 ) + { + iLength = send( hSocket, (LPSTR)pSendBuffer + iSended, dwSendBufferSize - iSended, + NO_FLAGS ); + + if ( iLength != 0 && iLength != SOCKET_ERROR ) + iSended += iLength; + } + + dwSended = iSended; + } + + //if ( dwSended ) + // printf( "C: %s", pSendBuffer ); + + return dwSended; + } + + ////////////////////////////////////////////////////////////////////////// + inline unsigned short CSMTPClient::GetResponseCode( LPBYTE pBuffer, DWORD dwBufferSize ) + { + unsigned short iCode = 0; + + if ( dwBufferSize >= 3 ) + { + CHAR szResponseCode[ 4 ] = { 0 }; + memcpy( szResponseCode, pBuffer, 3 ); + szResponseCode[ 3 ] = 0; + iCode = atoi( szResponseCode ); + } + + return iCode; + } + + ////////////////////////////////////////////////////////////////////////// + inline void CSMTPClient::ParseESMTPExtensions( LPBYTE pBuffer, DWORD dwBufferSize ) + { + const char *szSubstring = strstr( (const char*)pBuffer, "250-AUTH " ); + if ( !szSubstring ) + { + szSubstring = strstr( (const char*)pBuffer, "250 AUTH " ); + } + + if ( szSubstring ) + { + const char *szSubstringEnd = strstr( (const char*)szSubstring, "\r\n" ); + if ( szSubstringEnd ) + { + szSubstring += 9; + char szAuthMode[ 256 ] = { 0 }; + for ( ; szSubstring < szSubstringEnd + 1 ; szSubstring++ ) + { + if ( *szSubstring == ' ' || *szSubstring == '\r' ) + { + if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_PLAIN ) == 0 ) + { + m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_PLAIN; + m_dwSupportedAuthModesCount++; + } + else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_LOGIN ) == 0 ) + { + m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_LOGIN; + m_dwSupportedAuthModesCount++; + } + else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_CRAM_MD5 ) == 0 ) + { + m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_CRAM_MD5; + m_dwSupportedAuthModesCount++; + } + + szAuthMode[ 0 ] = 0; + + if ( m_dwSupportedAuthModesCount == MAX_AUTH_MODES_COUND ) + break; + } + else + { + szAuthMode[ strlen( szAuthMode ) + 1 ] = 0; + szAuthMode[ strlen( szAuthMode ) ] = *szSubstring; + } + } + } + } + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber ) + { + if ( m_bConnected ) + ServerDisconnect(); + + m_bConnected = FALSE; + m_hSocket = INVALID_SOCKET; + + m_hSocket = _connectServerSocket( szServerAddress, iPortNumber ); + + if ( m_hSocket != INVALID_SOCKET ) + { + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + // Connected. Wait server hello string. + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + // Check 220 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 220 ) + { + SetErrorText( pReceiveBuffer ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error. ", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + // EHLO / HELO + BYTE szHelloBuffer[ 256 ]; + sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_EHLO, (char*)szServerAddress ); + if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode == 500 ) + { + SetErrorText( pReceiveBuffer ); + + sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_HELO, (char*)szServerAddress ); + if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 250 ) + { + SetErrorText( pReceiveBuffer ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + } + else if ( iResponseCode != 250 ) + { + SetErrorText( pReceiveBuffer ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + // Parse AUTH supported modes + ParseESMTPExtensions( pReceiveBuffer, iReceived ); + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + free( pReceiveBuffer ); + } + } + else + { + return FALSE; + } + + m_bConnected = TRUE; + + return TRUE; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber, LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + bSuccess = ServerConnect( szServerAddress, iPortNumber ); + if ( bSuccess ) + { + if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) ) + { + ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) ) + { + ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) ) + { + ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN ); + } + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline SOCKET CSMTPClient::_connectServerSocket( LPCSTR szServerAddress, const unsigned short iPortNumber ) + { + int nConnect; + short nProtocolPort = iPortNumber; + LPHOSTENT lpHostEnt; + SOCKADDR_IN sockAddr; + + SOCKET hServerSocket = INVALID_SOCKET; + + lpHostEnt = gethostbyname( szServerAddress ); + if (lpHostEnt) + { + hServerSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if (hServerSocket != INVALID_SOCKET) + { + sockAddr.sin_family = AF_INET; + sockAddr.sin_port = htons( nProtocolPort ); + sockAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list); + + nConnect = connect( hServerSocket, (PSOCKADDR)&sockAddr, + sizeof(sockAddr) ); + + if ( nConnect != 0 ) + { + SetErrorText( "connect error.", WSAGetLastError() ); + hServerSocket = INVALID_SOCKET; + } + } + else + { + SetErrorText( "Invalid socket." ); + throw; + } + } + else + { + SetErrorText( "Error retrieving host by name.", WSAGetLastError() ); + } + + return hServerSocket ; + } + + ////////////////////////////////////////////////////////////////////////// + inline void CSMTPClient::ServerDisconnect() + { + if ( m_hSocket != INVALID_SOCKET ) + { + if ( SendData( m_hSocket, (PBYTE)SMTP_COMMAND_QUIT, strlen( SMTP_COMMAND_QUIT ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + + if ( iReceived ) + SetErrorText( pReceiveBuffer ); + + free( pReceiveBuffer ); + } + + m_hSocket = INVALID_SOCKET; + } + + m_bConnected = FALSE; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::GetAuthModeIsSupported( int iMode ) + { + BOOL bSupported = FALSE; + + for ( int i = 0 ; i < m_dwSupportedAuthModesCount ; i++ ) + { + if ( m_aSupportedAuthModes[ i ] == iMode ) + { + bSupported = TRUE; + break; + } + } + + return bSupported; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword, int iAuthMode ) + { + BOOL bSuccess = FALSE; + + if ( iAuthMode == AUTH_MODE_PLAIN ) + { + bSuccess = ServerLoginMethodPlain( szUsername, szPassword ); + } + else if ( iAuthMode == AUTH_MODE_LOGIN ) + { + bSuccess = ServerLoginMethodLogin( szUsername, szPassword ); + } + else if ( iAuthMode == AUTH_MODE_CRAM_MD5 ) + { + bSuccess = ServerLoginMethodCramMD5( szUsername, szPassword ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) ) + { + bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) ) + { + bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) ) + { + bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLoginMethodPlain( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_PLAIN ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + // Connected. Wait server hello string. + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + // Encode. + DWORD dwLoginBuffer = strlen( szUsername ) + strlen( szPassword ) + 3; + char *pLoginBuffer = (char*)malloc( dwLoginBuffer ); + if ( pLoginBuffer ) + { + ZeroMemory( pLoginBuffer, dwLoginBuffer ); + strcpy( pLoginBuffer + 1, szUsername ); + strcpy( pLoginBuffer + 1 + strlen( szUsername ) + 1, szPassword ); + + Base64Coder coder; + coder.Encode( (const PBYTE)pLoginBuffer, dwLoginBuffer - 1 ); + LPCSTR szLoginBufferEncoded = coder.EncodedMessage(); + + if ( szLoginBufferEncoded && strlen( szLoginBufferEncoded ) > 0 ) + { + DWORD dwSendBufferSize = strlen( szLoginBufferEncoded ) + 4; + char* pSendBuffer = (char*)malloc( dwSendBufferSize ); + if ( pSendBuffer ) + { + strcpy( pSendBuffer, szLoginBufferEncoded ); + strcat( pSendBuffer, "\r\n" ); + + if ( SendData( m_hSocket, (PBYTE)pSendBuffer, strlen( (const char*)pSendBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pSendBuffer ); + free( pLoginBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + + free( pSendBuffer ); + } + } + + free( pLoginBuffer ); + + // check result + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 235 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 235 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + bSuccess = TRUE; + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + } + + free( pReceiveBuffer ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLoginMethodLogin( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_LOGIN ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + // Check request + if ( iReceived > 6 ) + { + Base64Coder coder; + coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); + LPCSTR szRequest = coder.DecodedMessage(); + if ( szRequest && strlen( szRequest ) > 0 ) + { + if ( strcmpi( szRequest, "Username:" ) == 0 ) + { + coder.Encode( (const PBYTE)szUsername, strlen( szUsername ) ); + LPCSTR szUsernameEncoded = coder.EncodedMessage(); + + char* szLoginUsernameBuffer = (char*)malloc( strlen( szUsernameEncoded ) + 4 ); + if ( szLoginUsernameBuffer ) + { + strcpy( szLoginUsernameBuffer, szUsernameEncoded ); + strcat( szLoginUsernameBuffer, "\r\n" ); + + if ( SendData( m_hSocket, (PBYTE)szLoginUsernameBuffer, strlen( (const char*)szLoginUsernameBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( szLoginUsernameBuffer ); + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + // Check request + if ( iReceived > 6 ) + { + coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); + LPCSTR szRequest2 = coder.DecodedMessage(); + if ( szRequest2 && strlen( szRequest2 ) > 0 ) + { + if ( strcmpi( szRequest2, "Password:" ) == 0 ) + { + coder.Encode( (const PBYTE)szPassword, strlen( szPassword ) ); + LPCSTR szPasswordEncoded = coder.EncodedMessage(); + + char* szLoginPasswordBuffer = (char*)malloc( strlen( szPasswordEncoded ) + 4 ); + if ( szLoginPasswordBuffer ) + { + strcpy( szLoginPasswordBuffer, szPasswordEncoded ); + strcat( szLoginPasswordBuffer, "\r\n" ); + + if ( SendData( m_hSocket, (PBYTE)szLoginPasswordBuffer, strlen( (const char*)szLoginPasswordBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( szLoginPasswordBuffer ); + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 235 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 235 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + bSuccess = TRUE; + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + } + } + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( pReceiveBuffer ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLoginMethodCramMD5( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_CRAM_MD5 ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + // Connected. Wait server hello string. + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + // Check request + if ( iReceived > 6 ) + { + Base64Coder coder; + coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); + LPCSTR szResponse = coder.DecodedMessage(); + if ( szResponse && strlen( szResponse ) > 0 ) + { + char *auth_hex = hash_md5( szPassword, szResponse, strlen(szResponse) ); + if ( !auth_hex ) + { + free( pReceiveBuffer ); + return FALSE; + } + + char *szCommand = (char*)malloc( strlen( szUsername ) + strlen( auth_hex ) + 5 ); + if ( szCommand ) + { + sprintf( szCommand, "%s %s", szUsername, auth_hex ); + + free( auth_hex ); + + coder.Encode( (const PBYTE)szCommand, strlen( szCommand ) ); + + free( szCommand ); + + LPCSTR szAuthEncoded = coder.EncodedMessage(); + if ( szAuthEncoded == NULL ) + { + free( pReceiveBuffer ); + return FALSE; + } + + char *szAuthCommand = (char*)malloc( strlen( szAuthEncoded ) + 4 ); + if ( szAuthCommand ) + { + strcpy( szAuthCommand, szAuthEncoded ); + strcat( szAuthCommand, "\r\n" ); + + // Send auth data + if ( SendData( m_hSocket, (PBYTE)szAuthCommand, strlen( (const char*)szAuthCommand ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( szAuthCommand ); + free( pReceiveBuffer ); + return FALSE; + } + + // Check response + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 235 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 235 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + bSuccess = TRUE; + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( szAuthCommand ); + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + free( auth_hex ); + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( pReceiveBuffer ); + } + else + { + SetErrorText( "malloc() failed.", GetLastError() ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::SendMessage( LPCSTR szFromAddress, LPCSTR szFromName, LPCSTR szToAddresses, LPCSTR szSubject, LPCSTR szXMailer, LPBYTE pBodyBuffer, DWORD dwBodySize ) + { + BOOL bSuccess = FALSE; + + // Format Header + if ( !szFromAddress ) + { + SetErrorText( "SendMessage. Invalid Parameters!" ); + return NULL; + } + + char *szHeaderBuffer = (char*)malloc( 1024 * 16 ); + if ( szHeaderBuffer ) + { + // get the current date and time + char szDate[ 500 ]; + char sztTime[ 500 ]; + + SYSTEMTIME st = { 0 }; + ::GetSystemTime(&st); + + ::GetDateFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, &st, "ddd',' dd MMM yyyy", szDate , sizeof( szDate ) ); + ::GetTimeFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), TIME_FORCE24HOURFORMAT, &st, "HH':'mm':'ss", sztTime, sizeof( sztTime ) ); + + sprintf( szHeaderBuffer, "DATE: %s %s\r\n", szDate, sztTime ); + + // X-Mailer Field + if ( szXMailer && strlen( szXMailer ) ) + { + strcat( szHeaderBuffer, "X-Mailer: " ); + strcat( szHeaderBuffer, szXMailer ); + strcat( szHeaderBuffer, "\r\n" ); + } + + // From: + strcat( szHeaderBuffer, "From: " ); + if ( szFromName ) + { + strcat( szHeaderBuffer, "\"" ); + strcat( szHeaderBuffer, szFromName ); + strcat( szHeaderBuffer, "\" <" ); + strcat( szHeaderBuffer, szFromAddress ); + strcat( szHeaderBuffer, ">\r\n" ); + } + else + { + strcat( szHeaderBuffer, "<" ); + strcat( szHeaderBuffer, szFromAddress ); + strcat( szHeaderBuffer, ">\r\n" ); + } + + // Subject: + if ( szSubject && strlen( szSubject ) ) + { + strcat( szHeaderBuffer, "Subject: " ); + strcat( szHeaderBuffer, szSubject ); + strcat( szHeaderBuffer, "\r\n" ); + } + + // To Fields + strcat( szHeaderBuffer, "To: " ); + strcat( szHeaderBuffer, szToAddresses ); + strcat( szHeaderBuffer, "\r\n" ); + + // MIME + strcat( szHeaderBuffer, "MIME-Version: 1.0\r\nContent-type: text/plain; charset=US-ASCII\r\n" ); + + // End Header + strcat( szHeaderBuffer, "\r\n" ); + } + else + { + SetErrorText( "malloc error.", GetLastError() ); + return FALSE; + } + + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "MAIL FROM:<%s> SIZE=%u\r\n", (char*)szFromAddress, strlen( szHeaderBuffer ) + dwBodySize + 2 ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( szHeaderBuffer ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 250 ) + { + free( szHeaderBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( szHeaderBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + + // Post "RCTP TO:" + char *szCurrentAddr = (char*)malloc( strlen( szToAddresses ) + 1 ); + if ( !szCurrentAddr ) + { + SetErrorText( "malloc error.", GetLastError() ); + free( szHeaderBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + + const char* szToOffset = szToAddresses; + char* szZap = NULL; + + BOOL bRCPTAccepted = FALSE; + do + { + strcpy( szCurrentAddr, szToOffset ); + char *szExtractedAdress = szCurrentAddr; + szZap = strchr( szCurrentAddr, ',' ); + + if ( szZap ) + { + *szZap = 0; + szToOffset = szZap + 1; + } + + char *pSkobka1 = strchr( szCurrentAddr, '<' ); + char *pSkobka2 = strchr( szCurrentAddr, '>' ); + + if ( pSkobka1 && pSkobka2 && pSkobka2 > pSkobka1 ) + { + szExtractedAdress = pSkobka1 + 1; + *pSkobka2 = NULL; + } + + if ( szExtractedAdress && strlen( szExtractedAdress ) > 0 ) + { + sprintf( (char*)szCommandBuffer, "RCPT TO:<%s>\r\n", (char*)szExtractedAdress ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( szCurrentAddr ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode == 250 ) + { + bRCPTAccepted = TRUE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( szCurrentAddr ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + } + + } while( szZap ); + + free( szCurrentAddr ); + + if ( bRCPTAccepted ) + { + sprintf( (char*)szCommandBuffer, "DATA\r\n" ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 354 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 354 ) + { + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + // Send message data (header + body + .) + if ( SendData( m_hSocket, (PBYTE)szHeaderBuffer, strlen( (const char*)szHeaderBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + if ( SendData( m_hSocket, (PBYTE)pBodyBuffer, dwBodySize ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + if ( SendData( m_hSocket, (PBYTE)"\r\n.\r\n", 5 ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode == 250 ) + { + bSuccess = TRUE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + } + } + + free( pReceiveBuffer ); + } + else + { + SetErrorText( "malloc error.", GetLastError() ); + } + + if ( szHeaderBuffer ) + free( szHeaderBuffer ); + + return bSuccess; + } + + + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif + +#ifndef ROUNDTOPAGE +#define ROUNDTOPAGE(a) (((a/4096)+1)*4096) +#endif + + ////////////////////////////////////////////////////////////////////// + // Construction/Destruction + ////////////////////////////////////////////////////////////////////// + + inline Base64Coder::Base64Coder() + : m_pDBuffer(NULL), + m_pEBuffer(NULL), + m_nDBufLen(0), + m_nEBufLen(0) + { + + } + + inline Base64Coder::~Base64Coder() + { + if(m_pDBuffer != NULL) + delete [] m_pDBuffer; + + if(m_pEBuffer != NULL) + delete [] m_pEBuffer; + } + + inline LPCSTR Base64Coder::DecodedMessage() const + { + return (LPCSTR) m_pDBuffer; + } + + inline LPCSTR Base64Coder::EncodedMessage() const + { + return (LPCSTR) m_pEBuffer; + } + + inline void Base64Coder::AllocEncode(DWORD nSize) + { + if(m_nEBufLen < nSize) + { + if(m_pEBuffer != NULL) + delete [] m_pEBuffer; + + m_nEBufLen = ROUNDTOPAGE(nSize); + m_pEBuffer = new BYTE[m_nEBufLen]; + } + + ::ZeroMemory(m_pEBuffer, m_nEBufLen); + m_nEDataLen = 0; + } + + inline void Base64Coder::AllocDecode(DWORD nSize) + { + if(m_nDBufLen < nSize) + { + if(m_pDBuffer != NULL) + delete [] m_pDBuffer; + + m_nDBufLen = ROUNDTOPAGE(nSize); + m_pDBuffer = new BYTE[m_nDBufLen]; + } + + ::ZeroMemory(m_pDBuffer, m_nDBufLen); + m_nDDataLen = 0; + } + + inline void Base64Coder::SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen) + { + DWORD i = 0; + + AllocEncode(nBufLen); + while(i < nBufLen) + { + if(!_IsBadMimeChar(pBuffer[i])) + { + m_pEBuffer[m_nEDataLen] = pBuffer[i]; + m_nEDataLen++; + } + + i++; + } + } + + inline void Base64Coder::SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen) + { + AllocDecode(nBufLen); + ::CopyMemory(m_pDBuffer, pBuffer, nBufLen); + m_nDDataLen = nBufLen; + } + + inline void Base64Coder::Encode(const PBYTE pBuffer, DWORD nBufLen) + { + SetDecodeBuffer(pBuffer, nBufLen); + AllocEncode(nBufLen * 2); + + TempBucket Raw; + DWORD nIndex = 0; + + while((nIndex + 3) <= nBufLen) + { + Raw.Clear(); + ::CopyMemory(&Raw, m_pDBuffer + nIndex, 3); + Raw.nSize = 3; + _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); + nIndex += 3; + m_nEDataLen += 4; + } + + if(nBufLen > nIndex) + { + Raw.Clear(); + Raw.nSize = (BYTE) (nBufLen - nIndex); + ::CopyMemory(&Raw, m_pDBuffer + nIndex, nBufLen - nIndex); + _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); + m_nEDataLen += 4; + } + } + + inline void Base64Coder::Encode(LPCSTR szMessage) + { + if(szMessage != NULL) + Base64Coder::Encode((const PBYTE)szMessage, strlen( (const char*)szMessage)); + } + + inline void Base64Coder::Decode(const PBYTE pBuffer, DWORD dwBufLen) + { + if(is_init()) + _Init(); + + SetEncodeBuffer(pBuffer, dwBufLen); + + AllocDecode(dwBufLen); + + TempBucket Raw; + + DWORD nIndex = 0; + + while((nIndex + 4) <= m_nEDataLen) + { + Raw.Clear(); + Raw.nData[0] = DecodeTable()[m_pEBuffer[nIndex]]; + Raw.nData[1] = DecodeTable()[m_pEBuffer[nIndex + 1]]; + Raw.nData[2] = DecodeTable()[m_pEBuffer[nIndex + 2]]; + Raw.nData[3] = DecodeTable()[m_pEBuffer[nIndex + 3]]; + + if(Raw.nData[2] == 255) + Raw.nData[2] = 0; + if(Raw.nData[3] == 255) + Raw.nData[3] = 0; + + Raw.nSize = 4; + _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); + nIndex += 4; + m_nDDataLen += 3; + } + + // If nIndex < m_nEDataLen, then we got a decode message without padding. + // We may want to throw some kind of warning here, but we are still required + // to handle the decoding as if it was properly padded. + if(nIndex < m_nEDataLen) + { + Raw.Clear(); + for(DWORD i = nIndex; i < m_nEDataLen; i++) + { + Raw.nData[i - nIndex] = DecodeTable()[m_pEBuffer[i]]; + Raw.nSize++; + if(Raw.nData[i - nIndex] == 255) + Raw.nData[i - nIndex] = 0; + } + + _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); + m_nDDataLen += (m_nEDataLen - nIndex); + } + } + + inline void Base64Coder::Decode(LPCSTR szMessage) + { + if(szMessage != NULL) + Base64Coder::Decode((const PBYTE)szMessage, strlen((const char*)szMessage)); + } + + inline DWORD Base64Coder::_DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) + { + TempBucket Data; + DWORD nCount = 0; + + _DecodeRaw(Data, Decode); + + for(int i = 0; i < 3; i++) + { + pBuffer[i] = Data.nData[i]; + if(pBuffer[i] != 255) + nCount++; + } + + return nCount; + } + + + inline void Base64Coder::_EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) + { + TempBucket Data; + + _EncodeRaw(Data, Decode); + + for(int i = 0; i < 4; i++) + pBuffer[i] = Base64Digits()[Data.nData[i]]; + + switch(Decode.nSize) + { + case 1: + pBuffer[2] = '='; + case 2: + pBuffer[3] = '='; + } + } + + inline void Base64Coder::_DecodeRaw(TempBucket &Data, const TempBucket &Decode) + { + BYTE nTemp; + + Data.nData[0] = Decode.nData[0]; + Data.nData[0] <<= 2; + + nTemp = Decode.nData[1]; + nTemp >>= 4; + nTemp &= 0x03; + Data.nData[0] |= nTemp; + + Data.nData[1] = Decode.nData[1]; + Data.nData[1] <<= 4; + + nTemp = Decode.nData[2]; + nTemp >>= 2; + nTemp &= 0x0F; + Data.nData[1] |= nTemp; + + Data.nData[2] = Decode.nData[2]; + Data.nData[2] <<= 6; + nTemp = Decode.nData[3]; + nTemp &= 0x3F; + Data.nData[2] |= nTemp; + } + + inline void Base64Coder::_EncodeRaw(TempBucket &Data, const TempBucket &Decode) + { + BYTE nTemp; + + Data.nData[0] = Decode.nData[0]; + Data.nData[0] >>= 2; + + Data.nData[1] = Decode.nData[0]; + Data.nData[1] <<= 4; + nTemp = Decode.nData[1]; + nTemp >>= 4; + Data.nData[1] |= nTemp; + Data.nData[1] &= 0x3F; + + Data.nData[2] = Decode.nData[1]; + Data.nData[2] <<= 2; + + nTemp = Decode.nData[2]; + nTemp >>= 6; + + Data.nData[2] |= nTemp; + Data.nData[2] &= 0x3F; + + Data.nData[3] = Decode.nData[2]; + Data.nData[3] &= 0x3F; + } + + inline BOOL Base64Coder::_IsBadMimeChar(BYTE nData) + { + switch(nData) + { + case '\r': case '\n': case '\t': case ' ' : + case '\b': case '\a': case '\f': case '\v': + return TRUE; + default: + return FALSE; + } + } + + inline void Base64Coder::_Init() + { // Initialize Decoding table. + + int i; + + for(i = 0; i < 256; i++) + DecodeTable()[i] = -2; + + for(i = 0; i < 64; i++) + { + DecodeTable()[Base64Digits()[i]] = i; + DecodeTable()[Base64Digits()[i]|0x80] = i; + } + + DecodeTable()['='] = -1; + DecodeTable()['='|0x80] = -1; + + is_init() = TRUE; + } + + + } +} +}
\ No newline at end of file diff --git a/contrib/epee/include/net/smtp_helper.h b/contrib/epee/include/net/smtp_helper.h new file mode 100644 index 000000000..b8252e1cf --- /dev/null +++ b/contrib/epee/include/net/smtp_helper.h @@ -0,0 +1,88 @@ +// 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 "smtp.h" + +namespace epee +{ +namespace net_utils +{ + namespace smtp + { + + inline bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_addres, const std::string& from_name, const std::string& maillist, const std::string& subject, const std::string& mail_body) + { + net_utils::smtp::CSMTPClient smtp; + + if ( !smtp.ServerConnect( server.c_str(), port ) ) + { + LOG_PRINT("Reporting: Failed to connect to server " << server <<":"<< port, LOG_LEVEL_0); + return false; + } + + if(login.size() && pass.size()) + { + if ( !smtp.ServerLogin( login.c_str(), pass.c_str()) ) + { + LOG_PRINT("Reporting: Failed to auth on server " << server <<":"<< port, LOG_LEVEL_0); + return false; + + } + } + + if ( !smtp.SendMessage( from_addres.c_str(), + from_name.c_str(), + maillist.c_str(), + subject.c_str(), + "bicycle-client", + (LPBYTE)mail_body.data(), + mail_body.size())) + { + char *szErrorText = smtp.GetLastErrorText(); + if ( szErrorText ) + { + LOG_PRINT("Failed to send message, error text: " << szErrorText, LOG_LEVEL_0); + } + else + { + LOG_PRINT("Failed to send message, error text: null", LOG_LEVEL_0); + } + return false; + } + + smtp.ServerDisconnect(); + + return true; + + + } + } +} +}
\ No newline at end of file |