From 2b4b9248aaf8b72d70be8276cd8feddda361ec6a Mon Sep 17 00:00:00 2001 From: maxtorm <37799339+miximtor@users.noreply.github.com> Date: Wed, 17 Apr 2019 20:24:00 +0800 Subject: [PATCH] Shadowsocks protocol supported (#2) * systemd support * refactory * intrusive_list * use intrusive list * use macro buffer size controls * add visual stuio ignores * add session buffer size macro control * remove boost third party * shadowsocks protocol supported --- .gitignore | 5 + CMakeLists.txt | 64 +++-- daemon/msocksd.service | 10 +- include/msocks/endpoint/basic_endpoint.hpp | 66 +++++ include/msocks/endpoint/client.hpp | 23 -- include/msocks/endpoint/client_endpoint.hpp | 41 +++ include/msocks/endpoint/endpoint.hpp | 49 ---- include/msocks/endpoint/server.hpp | 24 -- include/msocks/endpoint/server_endpoint.hpp | 38 +++ include/msocks/session/basic_session.hpp | 44 +++ include/msocks/session/client_session.hpp | 63 ++--- include/msocks/session/pool.hpp | 130 ++++----- include/msocks/session/server_session.hpp | 67 ++--- include/msocks/session/session.hpp | 61 ---- include/msocks/usings.hpp | 7 - include/msocks/utility/intrusive_list.hpp | 85 ++++++ .../msocks/utility/intrusive_list_hook.hpp | 36 +++ include/msocks/utility/limiter.hpp | 84 ------ include/msocks/utility/local_socks5.hpp | 195 ++----------- include/msocks/utility/rate_limiter.hpp | 41 +++ include/msocks/utility/signal_handle.hpp | 0 include/msocks/utility/socket_pair.hpp | 75 +++-- include/msocks/utility/socks_constants.hpp | 18 +- include/msocks/utility/socks_erorr.hpp | 179 ++++++------ src/endpoint/client.cpp | 36 --- src/endpoint/client_endpoint.cpp | 30 ++ src/endpoint/server.cpp | 38 --- src/endpoint/server_endpoint.cpp | 36 +++ src/main.cpp | 124 +++++---- src/session/basic_session.cpp | 40 +++ src/session/client_session.cpp | 229 ++++++++------- src/session/server_session.cpp | 260 +++++++++++------- src/utility/local_socks5.cpp | 125 +++++++++ src/utility/rate_limiter.cpp | 75 +++++ src/utility/socks_error.cpp | 9 + 35 files changed, 1370 insertions(+), 1037 deletions(-) create mode 100644 include/msocks/endpoint/basic_endpoint.hpp delete mode 100644 include/msocks/endpoint/client.hpp create mode 100644 include/msocks/endpoint/client_endpoint.hpp delete mode 100644 include/msocks/endpoint/endpoint.hpp delete mode 100644 include/msocks/endpoint/server.hpp create mode 100644 include/msocks/endpoint/server_endpoint.hpp create mode 100644 include/msocks/session/basic_session.hpp delete mode 100644 include/msocks/session/session.hpp delete mode 100644 include/msocks/usings.hpp create mode 100644 include/msocks/utility/intrusive_list.hpp create mode 100644 include/msocks/utility/intrusive_list_hook.hpp delete mode 100644 include/msocks/utility/limiter.hpp create mode 100644 include/msocks/utility/rate_limiter.hpp delete mode 100644 include/msocks/utility/signal_handle.hpp delete mode 100644 src/endpoint/client.cpp create mode 100644 src/endpoint/client_endpoint.cpp delete mode 100644 src/endpoint/server.cpp create mode 100644 src/endpoint/server_endpoint.cpp create mode 100644 src/session/basic_session.cpp create mode 100644 src/utility/local_socks5.cpp create mode 100644 src/utility/rate_limiter.cpp create mode 100644 src/utility/socks_error.cpp diff --git a/.gitignore b/.gitignore index 4df66e1..245d643 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,8 @@ # Clion build outputs .idea* cmake-build-* + +#Visual Studio build outputs + +CMakeSettings.json +.vs* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8721722..59f26bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,42 +1,53 @@ cmake_minimum_required(VERSION 3.9.2) + project(msocks) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) + +set(Boost_ARCHITECTURE -x64) set(CMAKE_CXX_STANDARD 17) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) + find_package(Threads REQUIRED) find_package(Botan2 2.9.0 REQUIRED) find_package(Boost 1.69 COMPONENTS system coroutine random thread REQUIRED) -if (BOTAN2_FOUND) - message("Use Botan2: ${BOTAN2_LIBRARIES}") -endif() -if (Boost_FOUND) - message("Use Boost: ${Boost_LIBRARIES}") -endif() - -include_directories(${Boost_INCLUDE_DIRS}) +add_subdirectory(third_party/spdlog) +include_directories(${BOOST_LIBRARIES}) include_directories(${BOTAN2_INCLUDE_DIRS}) include_directories(third_party/spdlog/include) -include_directories(${PROJECT_SOURCE_DIR}/include) +include_directories(include) add_definitions(-DBOOST_ASIO_NO_DEPRECATED) add_definitions(-DBOOST_COROUTINES_NO_DEPRECATION_WARNING) +if (MSVC) + add_definitions(-D_WIN32_WINNT=0x0601) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + set(CompilerFlags + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO + ) +# REPLACE MD(MultiThread DLL) TO MT (MultiThread) + foreach(CompilerFlag ${CompilerFlags}) + string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") + endforeach() +endif () -if (WIN32) - set(WIN_EXTRA_LIB ws2_32 wsock32) -else() - set(WIN_EXTRA_LIB) -endif() - file(GLOB MSOCKS_INCLUDE include/msocks/*hpp) file(GLOB MSOCKS_INCLUDE_ENDPOINT include/msocks/endpoint/*.hpp) file(GLOB MSOCKS_INCLUDE_SESSION include/msocks/session/*.hpp) file(GLOB MSOCKS_INCLUDE_UTILITY include/msocks/utility/*.hpp) file(GLOB MSOCKS_SRC_SESSION src/session/*.cpp) file(GLOB MSOCKS_SRC_ENDPOINT src/endpoint/*.cpp) +file(GLOB MSOCKS_SRC_UTILITY src/utility/*.cpp) add_executable(msocks ${MSOCKS_INCLUDE} @@ -45,7 +56,26 @@ add_executable(msocks ${MSOCKS_INCLUDE_UTILITY} ${MSOCKS_SRC_SESSION} ${MSOCKS_SRC_ENDPOINT} + ${MSOCKS_SRC_UTILITY} src/main.cpp) -target_link_options(msocks PRIVATE -fstack-protector) -target_link_libraries(msocks Boost::coroutine Boost::random Boost::system Boost::thread Botan2::Botan2 ${CMAKE_THREAD_LIBS_INIT} ${WIN_EXTRA_LIB}) \ No newline at end of file +target_link_libraries(msocks ${BOTAN2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_link_libraries(msocks stdc++) +endif () + + target_link_libraries(msocks Boost::coroutine Boost::system Boost::random Boost::thread) + +if (WIN32) + target_link_libraries(msocks ws2_32 wsock32) + if (MSVC) + target_link_libraries(msocks bcrypt) + endif () +endif () + + +if (UNIX) + install(TARGETS msocks DESTINATION ${CMAKE_INSTALL_BINDIR}/bin) + install(FILES daemon/msocksd.service DESTINATION /etc/systemd/system/) +endif () \ No newline at end of file diff --git a/daemon/msocksd.service b/daemon/msocksd.service index 9038988..d999832 100644 --- a/daemon/msocksd.service +++ b/daemon/msocksd.service @@ -1,3 +1,9 @@ +[Unit] +Description = msocks daemon service + [Service] -ExecStart=/usr/bin/msocks -Type = simple +ExecStart=/usr/bin/msocks s 0.0.0.0 4500 123456 4096 +ExecStop=/bin/kill -TERM $MAINPID + +[Install] +WantedBy=multi-user.target diff --git a/include/msocks/endpoint/basic_endpoint.hpp b/include/msocks/endpoint/basic_endpoint.hpp new file mode 100644 index 0000000..26cee2e --- /dev/null +++ b/include/msocks/endpoint/basic_endpoint.hpp @@ -0,0 +1,66 @@ +// +// Created by maxtorm on 2019/4/13. +// +#pragma once + +#include +#include +#include + +using namespace boost::asio; +using namespace boost::system; + +namespace msocks +{ + +class basic_endpoint : public noncopyable +{ +protected: + + explicit basic_endpoint(io_context& ioc) : + ioc_(ioc), acceptor_(ioc) + {} + + template + void start_service(SessionCreate create, const ip::tcp::endpoint& ep) + { + spawn( + ioc_, + [create(std::move(create)), this, ep](yield_context yield) + { + do_async_accept(create, ep, yield); + }); + } + + io_context& ioc_; + ip::tcp::acceptor acceptor_; + +private: + + template + void do_async_accept(SessionCreate create, const ip::tcp::endpoint ep, yield_context yield) + { + try + { + acceptor_.open(ep.protocol()); + acceptor_.set_option(ip::tcp::socket::reuse_address(true)); + acceptor_.bind(ep); + acceptor_.listen(); + while (true) + { + ip::tcp::socket s(ioc_); + acceptor_.async_accept(s, yield); + auto session = create(std::move(s)); + session->go(); + } + } + catch (system_error & e) + { + std::stringstream ss; + ss << ep; + spdlog::error("endpoint {}: error {}", ss.str(), e.what()); + } + } +}; +} + diff --git a/include/msocks/endpoint/client.hpp b/include/msocks/endpoint/client.hpp deleted file mode 100644 index 4792073..0000000 --- a/include/msocks/endpoint/client.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include -namespace msocks -{ - -class client final : public endpoint -{ -public: - client( - const ip::tcp::endpoint &listen, - const ip::tcp::endpoint &remote_, - const std::vector & key_ - ); - void start(); -private: - const ip::tcp::endpoint &remote; - const std::vector &key; -}; - -} - diff --git a/include/msocks/endpoint/client_endpoint.hpp b/include/msocks/endpoint/client_endpoint.hpp new file mode 100644 index 0000000..2cdd135 --- /dev/null +++ b/include/msocks/endpoint/client_endpoint.hpp @@ -0,0 +1,41 @@ +// +// Created by maxtorm on 2019/4/13. +// + +#pragma once +#include + +namespace msocks +{ + +struct client_config +{ + client_config() : timeout(0) + { + }; + std::string local_address; + uint16_t local_port = 0; + std::string remote_address; + uint16_t remote_port = 0; + std::vector key; + std::string method; + boost::posix_time::seconds timeout; +}; + +class client_endpoint final : public basic_endpoint +{ +public: + client_endpoint(io_context& ioc, client_config cfg) : + basic_endpoint(ioc), + cfg_(std::move(cfg)) + {} + + void start(); + +private: + client_config cfg_; + +}; + +} + diff --git a/include/msocks/endpoint/endpoint.hpp b/include/msocks/endpoint/endpoint.hpp deleted file mode 100644 index 68f7a22..0000000 --- a/include/msocks/endpoint/endpoint.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace msocks -{ - -struct endpoint : public noncopyable -{ - endpoint( - const ip::tcp::endpoint &listen - ) : - strand(context), - acceptor(context, listen) - {} - -protected: - template - void async_accept(yield_context yield,SessionFactory factory) - { - try - { - while (true) - { - ip::tcp::socket socket(context); - acceptor.async_accept(socket,yield); - auto session = factory(std::move(socket)); - session->go(); - } - } catch (system_error & e) - { - auto ep = acceptor.local_endpoint(); - std::string addr_port = ep.address().to_string() + ":"+std::to_string(ep.port()); - spdlog::error("{} error: {}",addr_port,e.what()); - } - } - - io_context context; - io_context::strand strand; - ip::tcp::acceptor acceptor; - - -}; -} \ No newline at end of file diff --git a/include/msocks/endpoint/server.hpp b/include/msocks/endpoint/server.hpp deleted file mode 100644 index 06f5e61..0000000 --- a/include/msocks/endpoint/server.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -namespace msocks -{ -class server : public endpoint -{ -public: - server(const ip::tcp::endpoint &listen,const std::vector & key_,std::size_t limit); - void start(); -private: - const std::vector &key; - std::shared_ptr limiter; - pool session_pool; -}; -} - - diff --git a/include/msocks/endpoint/server_endpoint.hpp b/include/msocks/endpoint/server_endpoint.hpp new file mode 100644 index 0000000..26646a2 --- /dev/null +++ b/include/msocks/endpoint/server_endpoint.hpp @@ -0,0 +1,38 @@ +// +// Created by maxtorm on 2019/4/13. +// +#pragma once + +#include +#include +#include +#include + +namespace msocks +{ + +struct server_endpoint_config +{ + server_endpoint_config() : timeout(0) {}; + std::string server_address; + uint16_t server_port = 0; + size_t speed_limit = 0; + std::vector key; + bool no_delay = true; + std::string method; + boost::posix_time::seconds timeout; +}; + +class server_endpoint final : public basic_endpoint +{ +public: + server_endpoint(io_context& ioc, pool& session_pool, server_endpoint_config cfg); + + void start(); +private: + pool& session_pool_; + server_endpoint_config cfg_; + std::shared_ptr limiter_; +}; + +} diff --git a/include/msocks/session/basic_session.hpp b/include/msocks/session/basic_session.hpp new file mode 100644 index 0000000..cf3adf9 --- /dev/null +++ b/include/msocks/session/basic_session.hpp @@ -0,0 +1,44 @@ +// +// Created by maxtorm on 2019/4/14. +// + +#pragma once + + +#include +#include + +using namespace boost::asio; +using namespace boost::system; +namespace msocks +{ + +class basic_session : public noncopyable +{ +public: + + const std::string& uuid() const noexcept; + +protected: + basic_session(io_context& ioc, ip::tcp::socket local); + + static void cipher_setup( + std::unique_ptr & cipher, + const std::string& method, + const std::vector& key, + const std::vector& iv + ); + + io_context& ioc_; + std::string uuid_; + ip::tcp::socket local_; + std::array buffer_local_; + ip::tcp::socket remote_; + std::array buffer_remote_; + + std::unique_ptr local_remote_cipher_; + std::unique_ptr remote_local_cipher_; + +}; + +} diff --git a/include/msocks/session/client_session.hpp b/include/msocks/session/client_session.hpp index c8a6e17..70ab89f 100644 --- a/include/msocks/session/client_session.hpp +++ b/include/msocks/session/client_session.hpp @@ -2,43 +2,44 @@ #include #include -#include -#include -#include +#include +using namespace boost::system; +using namespace boost::asio; namespace msocks { -class client_session final: - public session, - public std::enable_shared_from_this + +struct client_session_attribute +{ + client_session_attribute() : timeout(0) {}; + std::string remote_address; + uint16_t remote_port = 0; + std::vector key; + std::string method; + boost::posix_time::seconds timeout; +}; + +class client_session final : public basic_session, public std::enable_shared_from_this { public: - - client_session( - io_context::strand &strand_, - ip::tcp::socket local_, - const ip::tcp::endpoint &addr_, - const std::vector &key_ - ); - - void go(); - + client_session(io_context& ioc, ip::tcp::socket local, const client_session_attribute& attribute) : + basic_session(ioc, std::move(local)), attribute_(attribute) + {} + + void go(); private: - - using Result = async_result; - using Handler = Result::completion_handler_type; - - void start(yield_context yield); - - void async_handshake(const std::string &host_service, yield_context yield); - - void do_async_handshake(Handler handler,const std::string & host_service,yield_context yield); - - void fwd_local_remote(yield_context yield); - void fwd_remote_local(yield_context yield); - const ip::tcp::endpoint &addr; - + + void start(yield_context yield); + + async_result::return_type async_handshake(const std::vector& target_address, yield_context yield); + + void do_async_handshake(async_result::completion_handler_type handler, const std::vector &target_address, yield_context yield); + + void fwd_local_remote(yield_context yield); + void fwd_remote_local(yield_context yield); + + const client_session_attribute& attribute_; + }; } - diff --git a/include/msocks/session/pool.hpp b/include/msocks/session/pool.hpp index 776af7d..77f9830 100644 --- a/include/msocks/session/pool.hpp +++ b/include/msocks/session/pool.hpp @@ -1,24 +1,26 @@ #pragma once -#include -#include #include #include -#include + +#include + #include -#include + +using namespace boost::asio; +using namespace boost::system; namespace msocks { template -void notify_reuse(Session &session, Args &&... args) +void notify_reuse(Session& session, Args&& ... args) { session.notify_reuse(std::forward(args)...); } template -std::add_pointer_t raw_ptr_factory(Args &&... args) +std::add_pointer_t raw_ptr_factory(Args&& ... args) { static_assert(std::is_constructible_v, "Can't construct"); return new Session(std::forward(args)...); @@ -27,76 +29,80 @@ std::add_pointer_t raw_ptr_factory(Args &&... args) template class pool { -private: - using session_raw_ptr = std::add_pointer_t; - using deleter = std::function; - using session_unique_ptr = std::unique_ptr; - using session_shared_ptr = std::shared_ptr; + using pointer_type = typename utility::intrusive_list::pointer_type; + using deleter_type = std::function; + using unique_pointer_type = std::unique_ptr; + using shared_pointer_type = std::shared_ptr; public: - explicit pool(io_context::strand &strand_) : strand(strand_),timer(strand.context()) + + explicit pool(io_context& ioc) : ioc_(ioc), timer_(ioc) { timed_shrink_list(); } - + template - session_shared_ptr take(Args &&... args) - { - session_raw_ptr session; - if ( session_list.empty()) - { - session = raw_ptr_factory(std::forward(args)...); - spdlog::info("pool: create session {}", session->uuid()); - } - else - { - session = session_list.front(); - session_list.pop_front(); - notify_reuse(*session, std::forward(args)...); - spdlog::info("pool: reuse session {}", session->uuid()); - } - out++; - return session_unique_ptr(session, session_recycle_deleter); - } + shared_pointer_type take(Args&& ... args); private: - io_context::strand & strand; - steady_timer timer; - std::size_t out = 0; - - void timed_shrink_list() + io_context& ioc_; + steady_timer timer_; + std::size_t out_ = 0; + + void timed_shrink_list(); + + void recycle(pointer_type session); + + utility::intrusive_list session_list_; + +}; + + +template +template +typename pool::shared_pointer_type pool::take(Args&& ... args) +{ + pointer_type session; + if (session_list_.empty()) + { + session = raw_ptr_factory(std::forward(args)...); + } + else { - spawn(strand,[this](yield_context yield) + session = session_list_.take(); + notify_reuse(*session, std::forward(args)...); + } + out_++; + return unique_pointer_type(session, [this](pointer_type session) { recycle(session); }); +} + +template +void pool::recycle(pointer_type session) +{ + if (session == nullptr) + return; + out_--; + session_list_.offer(session); +} + +template +void pool::timed_shrink_list() +{ + spawn( + ioc_, + [this](yield_context yield) { - while(true) + while (true) { - timer.expires_after(std::chrono::seconds(30)); + timer_.expires_after(std::chrono::seconds(30)); error_code ec; - timer.async_wait(yield[ec]); - spdlog::info("pool: ready release {} session",session_list.size() > out/2?session_list.size()-out/2:0); - while (session_list.size() > out/2) + timer_.async_wait(yield[ec]); + while (session_list_.size() > out_ / 2) { - auto p = session_list.back(); - session_list.pop_back(); - spdlog::info("pool: release session {}",p->uuid()); - delete p; + pointer_type session = session_list_.release(); + delete session; } } }); - } - - void recycle(session_raw_ptr ptr) - { - if ( ptr == nullptr ) - return; - out--; - spdlog::info("pool: recycle session: {}", ptr->uuid()); - session_list.push_front(ptr); - } - - std::deque session_list; - deleter session_recycle_deleter = [this](session_raw_ptr ptr) - { recycle(ptr); }; -}; - } +} diff --git a/include/msocks/session/server_session.hpp b/include/msocks/session/server_session.hpp index 9b670b5..2570de1 100644 --- a/include/msocks/session/server_session.hpp +++ b/include/msocks/session/server_session.hpp @@ -2,49 +2,54 @@ #include #include -#include -#include -#include -#include +#include +#include +#include + +using namespace boost::asio; +using namespace boost::system; namespace msocks { -class server_session final : - public session, - public std::enable_shared_from_this +struct server_session_attribute +{ + server_session_attribute() : timeout(0) {}; + std::vector key; + std::string method; + boost::posix_time::seconds timeout; + std::size_t limit = 0; + std::shared_ptr limiter; +}; + +class server_session final : + public basic_session, + public std::enable_shared_from_this, + public utility::intrusive_list_hook { public: - server_session( - io_context::strand &strand_, - ip::tcp::socket local_, - const std::vector &key_, - std::shared_ptr limiter_); - + + server_session(io_context& ioc, ip::tcp::socket local, const server_session_attribute& attribute) : + basic_session(ioc, std::move(local)), attribute_(attribute) + {} + void go(); - - void notify_reuse( - const io_context::strand &strand_, - ip::tcp::socket local_, - const std::vector &key_, - const std::shared_ptr &limiter_); + + void notify_reuse(const io_context& ioc, ip::tcp::socket local, const server_session_attribute& attribute); private: - - using Result = async_result)>; - using Handler = Result::completion_handler_type; - + void start(yield_context yield); - + void fwd_local_remote(yield_context yield); - + void fwd_remote_local(yield_context yield); - - Result::return_type async_handshake(yield_context yield); - - void do_async_handshake(Handler handler, yield_context yield); - - std::shared_ptr limiter; + + async_result)>::return_type async_handshake(yield_context yield); + + void do_async_handshake(async_result)>::completion_handler_type handler, yield_context yield); + + const server_session_attribute& attribute_; }; } diff --git a/include/msocks/session/session.hpp b/include/msocks/session/session.hpp deleted file mode 100644 index 4e636ca..0000000 --- a/include/msocks/session/session.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace msocks -{ - -struct session : public noncopyable -{ -public: - session( - io_context::strand &strand_, - ip::tcp::socket local_, - const std::vector &key_ - ) : - strand(strand_), - local(std::move(local_)), - remote(local.get_executor().context()), - session_uuid(boost::uuids::to_string(boost::uuids::random_generator_mt19937()())), - key(key_) - {} - - const std::string &uuid() noexcept - { - return session_uuid; - } - - -protected: - - void cipher_setup( - const std::string &method, - const std::vector &iv, - std::unique_ptr &cipher - ) - { - if ( cipher == nullptr || cipher->name() != method ) - cipher = Botan::StreamCipher::create_or_throw(method); - cipher->set_key(key.data(), key.size()); - cipher->set_iv(iv.data(), iv.size()); - } - - std::string session_uuid; - io_context::strand &strand; - ip::tcp::socket local; - std::array buffer_local; - ip::tcp::socket remote; - std::array buffer_remote; - - const std::vector &key; - std::unique_ptr send_cipher; - std::unique_ptr recv_cipher; -}; - -} \ No newline at end of file diff --git a/include/msocks/usings.hpp b/include/msocks/usings.hpp deleted file mode 100644 index 467cb03..0000000 --- a/include/msocks/usings.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -using namespace boost::asio; -using namespace boost::endian; -using namespace boost::system; diff --git a/include/msocks/utility/intrusive_list.hpp b/include/msocks/utility/intrusive_list.hpp new file mode 100644 index 0000000..e441b92 --- /dev/null +++ b/include/msocks/utility/intrusive_list.hpp @@ -0,0 +1,85 @@ +#pragma once +#include +#include +namespace msocks::utility +{ + +template +class intrusive_list; + +template +class intrusive_list : public boost::noncopyable +{ + static_assert(std::is_base_of_v, Object>, "Object must be an intrusive list"); +public: + + using pointer_type = typename intrusive_list_hook::pointer_type; + + std::size_t size() const noexcept + { + return size_; + } + + bool empty() const noexcept + { + return size() == 0; + } + + pointer_type take() noexcept + { + if (begin_ == nullptr) + return nullptr; + pointer_type object = begin_; + --size_; + if (begin_ == end_) + { + begin_ = end_ = nullptr; + } + else + { + begin_ = begin_->next(); + } + return object; + } + + pointer_type release() noexcept + { + if (end_ == nullptr) + return nullptr; + pointer_type object = end_; + --size_; + if (begin_ == end_) + { + begin_ = end_ = nullptr; + } + else + { + end_ = end_->prev(); + } + return object; + } + + void offer(pointer_type object) noexcept + { + if (object == nullptr) + return; + + ++size_; + if (begin_ == nullptr) + { + end_ = begin_ = object; + return; + } + object->prev(nullptr); + object->next(begin_); + begin_->prev(object); + begin_ = begin_->prev(); + } + +private: + pointer_type begin_ = nullptr; + pointer_type end_ = nullptr; + std::size_t size_ = 0; +}; + +} \ No newline at end of file diff --git a/include/msocks/utility/intrusive_list_hook.hpp b/include/msocks/utility/intrusive_list_hook.hpp new file mode 100644 index 0000000..e2a7032 --- /dev/null +++ b/include/msocks/utility/intrusive_list_hook.hpp @@ -0,0 +1,36 @@ +#pragma once + +namespace msocks::utility +{ +template +class intrusive_list_hook +{ + template + friend class intrusive_list; + using pointer_type = std::add_pointer_t; + + pointer_type next() const noexcept + { + return next_; + } + + void next(pointer_type n) noexcept + { + next_ = n; + } + + pointer_type prev() const noexcept + { + return prev_; + } + + void prev(pointer_type n) noexcept + { + prev_ = n; + } + + pointer_type next_ = nullptr; + pointer_type prev_ = nullptr; +}; + +} diff --git a/include/msocks/utility/limiter.hpp b/include/msocks/utility/limiter.hpp deleted file mode 100644 index 4abfa10..0000000 --- a/include/msocks/utility/limiter.hpp +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace utility -{ - -class limiter : - public noncopyable, - public std::enable_shared_from_this -{ -private: - using Result = async_result; - using Handler = typename Result::completion_handler_type; -public: - limiter(io_context::strand &strand_, std::size_t limit_per_sec) : - strand(strand_), - timer(strand.context()), - signal(strand.context()), - increment(limit_per_sec), - available(0) - { - } - - auto async_get(std::size_t n, yield_context yield) - { - if (available >= n) - { - available-=n; - return; - } - if ( queue.empty()) - { - signal.cancel(); - } - Handler handler(yield); - Result result(handler); - queue.push(std::make_pair(n, handler)); - return result.get(); - } - - void start() - { - spawn(strand, [this, p = shared_from_this()](yield_context yield) - { - while ( true ) - { - - if ( queue.empty()) - { - signal.expires_from_now(boost::posix_time::pos_infin); - error_code ignored_ec; - signal.async_wait(yield[ignored_ec]); - } - - timer.expires_from_now(boost::posix_time::seconds(1)); - timer.async_wait(yield); - available += increment; - while ( !queue.empty() && queue.front().first <= available ) - { - auto handler = queue.front().second; - available -= queue.front().first; - queue.pop(); - post(strand, handler); - } - } - }); - } - -private: - io_context::strand &strand; - deadline_timer timer; - deadline_timer signal; - std::size_t increment; - std::size_t available; - std::queue> queue; -}; - -} \ No newline at end of file diff --git a/include/msocks/utility/local_socks5.hpp b/include/msocks/utility/local_socks5.hpp index 29343c7..cfd71de 100644 --- a/include/msocks/utility/local_socks5.hpp +++ b/include/msocks/utility/local_socks5.hpp @@ -1,190 +1,33 @@ #pragma once -#include #include -#include -#include -#include -#include -#include -#include +#include -#define MSOCKS_PACK(DEC) DEC __attribute__((__packed__)) +using namespace boost::asio; +using namespace boost::system; -namespace utility +#if defined(_MSC_VER) +#define MSOCKS_PACK(MSOCKS_STRUCT_DECLARE) __pragma(pack(push, 1)) MSOCKS_STRUCT_DECLARE __pragma( pack(pop) ) +#else +#define MSOCKS_PACK(MSOCKS_STRUCT_DECLARE) MSOCKS_STRUCT_DECLARE __attribute__((packed)) +#endif + +namespace msocks::utility { + namespace detail { -using Result = async_result; -using Handler = Result::completion_handler_type; - void do_local_socks5( - io_context::strand &strand, - ip::tcp::socket &local, - Handler handler, - yield_context yield -); + io_context& ioc, + ip::tcp::socket& local, + async_result)>::completion_handler_type handler, + yield_context yield); } -detail::Result::return_type -async_local_socks5( - io_context::strand &strand, - ip::tcp::socket &local, - yield_context yield) -{ - detail::Handler handler(yield); - detail::Result result(handler); - spawn( - strand, - [&strand, &local, handler](yield_context yield) - { detail::do_local_socks5(strand, local, handler, yield); } - ); - return result.get(); -} +async_result)>::return_type +async_local_socks5(io_context& ioc, ip::tcp::socket& local, yield_context yield); + -namespace detail -{ -void do_local_socks5( - io_context::strand &strand, - ip::tcp::socket &local, - Handler handler, - yield_context yield) -{ - namespace ms = msocks; - namespace msc = ms::constant; - std::array temp{}; - MSOCKS_PACK( - struct - { - uint8_t version; - uint8_t n_method; - }) - auth_method{}; - MSOCKS_PACK( - struct - { - uint8_t version; - uint8_t method; - } - ) auth_reply{msc::SOCKS5_VERSION, msc::AUTH_NO_AUTH}; - - MSOCKS_PACK ( - struct - { - uint8_t version; - uint8_t cmd; - uint8_t reserve; - uint8_t addr_type; - }) - request{}; - - MSOCKS_PACK ( - struct - { - uint8_t version; - uint8_t rep; - uint8_t rsv; - uint8_t atyp; - uint32_t bnd_addr; - uint16_t bnd_port; - }) - reply{msc::SOCKS5_VERSION, 0x00, 0x00, msc::ADDR_IPV4, 0x00000000, 0x0000}; - - std::string result; - error_code ec; - try - { - async_read(local, buffer(&auth_method, sizeof(auth_method)), yield); - - if ( auth_method.version != msc::SOCKS5_VERSION ) - throw system_error(ms::errc::version_not_supported, ms::socks_category()); - - async_read(local, buffer(temp, auth_method.n_method), yield); - bool find_auth = false; - for ( int i = 0; i < auth_method.n_method && !find_auth; i++ ) - find_auth = temp[i] == msc::AUTH_NO_AUTH; - - if ( !find_auth ) - throw system_error(ms::errc::auth_not_found, ms::socks_category()); - - async_write(local, buffer(&auth_reply, sizeof(auth_reply)), yield); - - async_read(local, buffer(&request, sizeof(request)), yield); - - if ( request.version != msc::SOCKS5_VERSION ) - throw system_error(ms::errc::version_not_supported, ms::socks_category()); - - switch ( request.cmd ) - { - case msc::CONN_TCP: - break; - case msc::CONN_BND: - case msc::CONN_UDP: - default: - throw system_error(ms::errc::cmd_not_supported, ms::socks_category()); - } - - if ( request.addr_type == msc::ADDR_IPV4 ) - { - async_read(local, buffer(temp, 32 / 8 + 2), yield); - MSOCKS_PACK( - struct - { - uint32_t addr; - uint16_t port; - } - ) - address_port{}; - - buffer_copy(buffer(&address_port, sizeof(address_port)), buffer(temp), - sizeof(address_port)); - result = - ip::make_address_v4(big_to_native(address_port.addr)).to_string() + - ":" + - std::to_string(big_to_native(address_port.port)); - } - else if ( request.addr_type == msc::ADDR_IPV6 ) - { - async_read(local, buffer(temp, 128 / 8 + 2), yield); - MSOCKS_PACK( - struct - { - ip::address_v6::bytes_type addr; - uint16_t port; - }) - address_port{}; - buffer_copy(buffer(&address_port, sizeof(address_port)), buffer(temp), - sizeof(address_port)); - std::reverse(address_port.addr.begin(), address_port.addr.end()); - result = - ip::make_address_v6(address_port.addr).to_string() + - ":" + - std::to_string(big_to_native(address_port.port)); - } - else if ( request.addr_type == msc::ADDR_DOMAIN ) - { - uint8_t domain_length; - async_read(local, buffer(&domain_length, sizeof(domain_length)), yield); - async_read(local, dynamic_buffer(result), transfer_exactly(domain_length), - yield); - uint16_t port = 0; - async_read(local, buffer(&port, sizeof(port)), yield); - big_to_native_inplace(port); - result += ":" + std::to_string(port); - } - else - { - throw system_error(ms::errc::address_not_supported, ms::socks_category()); - } - async_write(local, buffer(&reply, sizeof(reply)), yield); - } - catch ( system_error &e ) - { - ec = e.code(); - } - auto cb = [ec,result,handler]() mutable {handler(ec,result);}; - post(strand,std::move(cb)); -} -} } + diff --git a/include/msocks/utility/rate_limiter.hpp b/include/msocks/utility/rate_limiter.hpp new file mode 100644 index 0000000..bbd5d7f --- /dev/null +++ b/include/msocks/utility/rate_limiter.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +using namespace boost::asio; +using namespace boost::system; + +namespace msocks::utility +{ + +class rate_limiter : public noncopyable, public std::enable_shared_from_this +{ +private: + using storage_pair = std::pair::completion_handler_type>; + using unique_pair = std::unique_ptr; +public: + rate_limiter(io_context& ioc, const std::size_t limit) : + ioc_(ioc), timer_(ioc), signal_(ioc), limit_(limit), no_limit_(limit == 0), + wait_queue_([](const unique_pair& l, const unique_pair& r) { return l->first >= r->first; }) + {} + + async_result::return_type async_get(std::size_t n, yield_context yield); + + void start(); + +private: + io_context& ioc_; + deadline_timer timer_; + deadline_timer signal_; + const std::size_t limit_; + bool no_limit_; + std::size_t available_ = 0; + std::priority_queue< + unique_pair, + std::vector, + std::function + > wait_queue_; +}; +} diff --git a/include/msocks/utility/signal_handle.hpp b/include/msocks/utility/signal_handle.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/include/msocks/utility/socket_pair.hpp b/include/msocks/utility/socket_pair.hpp index 176a85e..941512b 100644 --- a/include/msocks/utility/socket_pair.hpp +++ b/include/msocks/utility/socket_pair.hpp @@ -1,33 +1,62 @@ #pragma once #include +#include #include -#include -#include +#include -namespace msocks +using namespace boost::asio; +using namespace boost::system; + +namespace msocks::utility { -template -error_code pair( - ip::tcp::socket &src, - ip::tcp::socket &dst, - yield_context yield, - mutable_buffer buf, - Transform transform +template +async_result::return_type +socket_pair( + ip::tcp::socket& source, + ip::tcp::socket& destination, + io_context& ioc, + mutable_buffer m_buf, + std::function before_read, + std::function before_write, + CompletionToken&& token ) { - error_code ec; - while (true) - { - auto n_read = src.async_read_some(buffer(buf),yield[ec]); - if (ec) goto out; - transform(buffer(buf,n_read)); - async_write(dst,buffer(buf,n_read),yield[ec]); - if (ec) goto out; - } - out: - return ec; + using result_type = async_result; + using handler_type = typename result_type::completion_handler_type; + handler_type handler(std::forward(token)); + result_type result(handler); + spawn( + ioc, + [ + &source, + &destination, + &ioc, + m_buf, + before_read(std::move(before_read)), + before_write(std::move(before_write)), + handler + ](yield_context yield) mutable + { + error_code ec; + while (true) + { + auto n_read = source.async_read_some(m_buf, yield[ec]); + if (ec) + { + break; + } + before_read(n_read, yield); + before_write(buffer(m_buf, n_read)); + async_write(destination, buffer(m_buf, n_read), yield[ec]); + if (ec) + { + break; + } + } + post(ioc, std::bind(handler, ec)); + }); + return result.get(); } - -} \ No newline at end of file +} diff --git a/include/msocks/utility/socks_constants.hpp b/include/msocks/utility/socks_constants.hpp index fa66ab1..626df5e 100644 --- a/include/msocks/utility/socks_constants.hpp +++ b/include/msocks/utility/socks_constants.hpp @@ -2,17 +2,17 @@ #include -namespace msocks::constant +namespace msocks::socks { -constexpr uint8_t SOCKS5_VERSION = 0x05; -constexpr uint8_t AUTH_NO_AUTH = 0x00; -constexpr uint8_t CONN_TCP = 0x01; -constexpr uint8_t CONN_BND = 0x02; -constexpr uint8_t CONN_UDP = 0x03; -constexpr uint8_t ADDR_IPV4 = 0x01; -constexpr uint8_t ADDR_DOMAIN = 0x03; -constexpr uint8_t ADDR_IPV6 = 0x04; +constexpr uint8_t socks5_version = 0x05; +constexpr uint8_t auth_no_auth = 0x00; +constexpr uint8_t conn_tcp = 0x01; +constexpr uint8_t conn_bind = 0x02; +constexpr uint8_t conn_udp = 0x03; +constexpr uint8_t addr_ipv4 = 0x01; +constexpr uint8_t addr_domain = 0x03; +constexpr uint8_t addr_ipv6 = 0x04; } diff --git a/include/msocks/utility/socks_erorr.hpp b/include/msocks/utility/socks_erorr.hpp index 3366a1d..16bef28 100644 --- a/include/msocks/utility/socks_erorr.hpp +++ b/include/msocks/utility/socks_erorr.hpp @@ -1,60 +1,62 @@ #pragma once #include + namespace msocks { namespace errc { enum errc_t { - success = 0, - version_not_supported = 1, - auth_not_found = 2, - cmd_not_supported = 3, - address_not_supported = 4 + success = 0, + version_not_supported = 1, + auth_not_found = 2, + cmd_not_supported = 3, + address_not_supported = 4 }; +inline std::string make_errc_string(errc_t code) { - switch ( code ) - { - case success: - return "success"; - case version_not_supported: - return "unsupported socks version"; - case auth_not_found: - return "anonymous auth method not found"; - case cmd_not_supported: - return "connect command not supported"; - case address_not_supported: - return "address type not supported"; - default: - return "unknown"; - } + switch (code) + { + case success: + return "success"; + case version_not_supported: + return "unsupported socks version"; + case auth_not_found: + return "anonymous auth method not found"; + case cmd_not_supported: + return "connect command not supported"; + case address_not_supported: + return "address type not supported"; + default: + return "unknown"; + } } -const char * -make_errc_string(errc_t code, char *buffer, std::size_t len) noexcept +inline const char* +make_errc_string(errc_t code, char* buffer, std::size_t len) noexcept { - switch ( code ) - { - case success: - return strncpy(buffer, "success", len); - case version_not_supported: - return strncpy(buffer, "unsupported socks version", len); - case auth_not_found: - return strncpy(buffer, "anonymous auth method not found", len); - case cmd_not_supported: - return strncpy(buffer, "connect command not supported", len); - case address_not_supported: - return strncpy(buffer, "address type not supported", len); - default: - return strncpy(buffer, "unknown", len); - } + switch (code) + { + case success: + return strncpy(buffer, "success", len); + case version_not_supported: + return strncpy(buffer, "unsupported socks version", len); + case auth_not_found: + return strncpy(buffer, "anonymous auth method not found", len); + case cmd_not_supported: + return strncpy(buffer, "connect command not supported", len); + case address_not_supported: + return strncpy(buffer, "address type not supported", len); + default: + return strncpy(buffer, "unknown", len); + } } - +inline bool code_in_range(int code) { - return 0 <= code && code < 5; + return 0 <= code && code < 5; } @@ -62,68 +64,63 @@ bool code_in_range(int code) namespace detail { + class socks_category : public boost::system::error_category { public: - socks_category() = default; - - socks_category(const socks_category &) = delete; - - socks_category &operator=(const socks_category &) = delete; - - const char *name() const noexcept override - { - return "msocks::socks"; - } - - std::string message(int ev) const override - { - return errc::make_errc_string(errc::errc_t(ev)); - } - - const char * - message(int ev, char *buffer, std::size_t len) const noexcept override - { - return errc::make_errc_string(errc::errc_t(ev), buffer, len); - } - - boost::system::error_condition - default_error_condition(int ev) const noexcept override - { - return boost::system::error_condition(ev, *this); - } - - bool equivalent(int code, - const boost::system::error_condition &condition) const noexcept override - { - if ( condition.category() != *this ) - return false; - return errc::code_in_range(code); - } - - bool equivalent(const boost::system::error_code &code, - int condition) const noexcept override - { - if ( code.category() != *this ) - return false; - return errc::code_in_range(condition); - } - -}; -} + socks_category() = default; -boost::system::error_category &socks_category() -{ - static detail::socks_category category; - return category; + socks_category(const socks_category&) = delete; + + socks_category& operator=(const socks_category&) = delete; + + const char* name() const noexcept override + { + return "msocks::socks"; + } + + std::string message(int ev) const override + { + return errc::make_errc_string(errc::errc_t(ev)); + } + + const char* + message(int ev, char* buffer, std::size_t len) const noexcept override + { + return errc::make_errc_string(errc::errc_t(ev), buffer, len); + } + + boost::system::error_condition + default_error_condition(int ev) const noexcept override + { + return boost::system::error_condition(ev, *this); + } + + bool equivalent(int code, + const boost::system::error_condition& condition) const noexcept override + { + if (condition.category() != *this) + return false; + return errc::code_in_range(code); + } + + bool equivalent(const boost::system::error_code & code, + int condition) const noexcept override + { + if (code.category() != *this) + return false; + return errc::code_in_range(condition); + } + +}; } +boost::system::error_category& socks_category(); } namespace boost::system { template<> struct is_error_code_enum : public std::true_type -{ -}; +{}; } diff --git a/src/endpoint/client.cpp b/src/endpoint/client.cpp deleted file mode 100644 index 0f32545..0000000 --- a/src/endpoint/client.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include -#include - -namespace msocks -{ - -client::client( - const ip::tcp::endpoint &listen, - const ip::tcp::endpoint &remote_, - const std::vector &key_) : - endpoint(listen), - remote(remote_), - key(key_) -{ -} - -void client::start() -{ - spawn(strand, [this](yield_context yield) - { - async_accept( - yield, - [this](ip::tcp::socket socket) - { - return std::make_shared( - strand, std::move(socket), remote, key - ); - }); - }); - context.run(); -} - -} - diff --git a/src/endpoint/client_endpoint.cpp b/src/endpoint/client_endpoint.cpp new file mode 100644 index 0000000..bc71680 --- /dev/null +++ b/src/endpoint/client_endpoint.cpp @@ -0,0 +1,30 @@ +// +// Created by maxtorm on 2019/4/14. +// + +#include +#include + +namespace msocks +{ + +void client_endpoint::start() +{ + const ip::tcp::endpoint listen(ip::make_address_v4(cfg_.local_address), cfg_.local_port); + static client_session_attribute attribute; + attribute.key = cfg_.key; + attribute.method = cfg_.method; + attribute.timeout = cfg_.timeout; + attribute.remote_address = cfg_.remote_address; + attribute.remote_port = cfg_.remote_port; + + start_service( + [this](ip::tcp::socket socket) -> std::shared_ptr + { + return std::make_shared(std::ref(ioc_), std::move(socket), std::ref(attribute)); + }, + listen + ); +} + +} diff --git a/src/endpoint/server.cpp b/src/endpoint/server.cpp deleted file mode 100644 index 0fe2e1e..0000000 --- a/src/endpoint/server.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include - -namespace msocks -{ - -server::server( - const ip::tcp::endpoint &listen, - const std::vector &key_, - std::size_t limit) : - endpoint(listen), - key(key_), - limiter(new utility::limiter(strand, limit * 1024)), - session_pool(strand) -{ -} - -void server::start() -{ - spawn(strand, [this](yield_context yield) - { - async_accept( - yield, - [this](ip::tcp::socket socket) - { - return session_pool.take( - std::ref(strand), - std::move(socket), - std::cref(key), - limiter - ); - }); - }); - limiter->start(); - context.run(); -} -} diff --git a/src/endpoint/server_endpoint.cpp b/src/endpoint/server_endpoint.cpp new file mode 100644 index 0000000..4c9a45e --- /dev/null +++ b/src/endpoint/server_endpoint.cpp @@ -0,0 +1,36 @@ +// +// Created by maxtorm on 2019/4/14. +// + +#include + +namespace msocks +{ + +server_endpoint::server_endpoint(io_context &ioc, pool &session_pool, server_endpoint_config cfg) : + basic_endpoint(ioc), + session_pool_(session_pool), + cfg_(std::move(cfg)), + limiter_(new utility::rate_limiter(ioc_,cfg_.speed_limit * 1024)) +{} + +void server_endpoint::start() +{ + + const ip::tcp::endpoint listen(ip::make_address_v4(cfg_.server_address),cfg_.server_port); + static server_session_attribute attribute; + attribute.timeout = cfg_.timeout; + attribute.method = cfg_.method; + attribute.key = cfg_.key; + attribute.limit = cfg_.speed_limit; + attribute.limiter = limiter_; + start_service( + [this](ip::tcp::socket socket) -> std::shared_ptr + { + socket.set_option(ip::tcp::no_delay(cfg_.no_delay)); + return session_pool_.take(std::ref(ioc_),std::move(socket),std::ref(attribute)); + },listen); +} + +} + diff --git a/src/main.cpp b/src/main.cpp index 178799d..fd524a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,61 +1,89 @@ -#include -#include +#include -#include -#include -#include +#include +#include +#include +#include #include -#include +#include -msocks::config parse_config(int argc,char *argv[]) +void log_setup() { - msocks::config config; - return config; + spdlog::set_pattern("[%l] %v"); } -void log_setup() +std::vector evpBytesTokey(const std::string& password) { - spdlog::set_pattern("[%l] %v"); + std::vector> m; + std::vector password1(password.begin(), password.end()); + std::vector data; + Botan::MD5 md5; + int i = 0; + while (m.size() < 32 + 8) + { + if (i == 0) + { + data = password1; + } + else + { + data = m[i - 1]; + std::copy(password1.begin(), password1.end(), std::back_inserter(data)); + } + i++; + auto hash_result = md5.process(data.data(), data.size()); + m.push_back(std::vector(hash_result.begin(), hash_result.end())); + } + std::vector key; + for (auto& mh : m) + { + std::copy(mh.begin(), mh.end(), std::back_inserter(key)); + } + return std::vector(key.begin(), key.begin() + 32); } -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { - log_setup(); - try - { - auto config = parse_config(argc, argv); - using namespace std::string_literals; - std::string ip = argv[2]; - unsigned short port = std::stoi(argv[3]); - ip::tcp::endpoint listen(ip::make_address_v4(ip), port); - std::string password = argv[4]; - Botan::SHA_256 sha256; - sha256.update(password); - std::vector key; - sha256.final(key); - if ( argv[1] == "s"s ) - { - std::size_t limit = std::stoull(argv[5]); - msocks::server server(listen, key,limit); - server.start(); - } - else if ( argv[1] == "c"s ) - { - ip::tcp::endpoint local_ep(ip::make_address("127.0.0.1"), 1081); - msocks::client client(local_ep, listen, key); - client.start(); - } - } - catch (boost::exception &e) - { - spdlog::error("main error: {}",boost::diagnostic_information(e)); - return -1; - } - catch (std::exception &e) + try + { + io_context ioc; + std::string password(argv[4]); + auto key = evpBytesTokey(password); + if (!strcmp(argv[1], "s")) + { + msocks::pool pool(ioc); + msocks::server_endpoint_config config; + config.no_delay = true; + config.server_address = argv[2]; + config.server_port = std::stoi(argv[3]); + config.key = key; + config.speed_limit = std::stoi(argv[5]); + config.method = "ChaCha(20)"; + config.timeout = boost::posix_time::seconds(2); + msocks::server_endpoint server(ioc, pool, std::move(config)); + server.start(); + ioc.run(); + } + else + { + msocks::client_config config; + config.local_address = "127.0.0.1"; + config.local_port = 1081; + config.key = key; + config.remote_address = argv[2]; + config.remote_port = std::stoi(argv[3]); + config.method = "ChaCha(20)"; + config.timeout = boost::posix_time::seconds(2); + msocks::client_endpoint client(ioc, std::move(config)); + client.start(); + ioc.run(); + } + } + catch (boost::exception & e) { - spdlog::error("main error: {}",e.what()); - return -2; + spdlog::error("{}",boost::diagnostic_information(e)); } - return 0; -} \ No newline at end of file + system("pause"); +} + diff --git a/src/session/basic_session.cpp b/src/session/basic_session.cpp new file mode 100644 index 0000000..b515633 --- /dev/null +++ b/src/session/basic_session.cpp @@ -0,0 +1,40 @@ +// +// Created by maxtorm on 2019/4/14. +// +#include +#include +#include +#include +#include +using namespace boost::uuids; +namespace msocks +{ + +const std::string &basic_session::uuid() const noexcept +{ + return uuid_; +} + +basic_session::basic_session(io_context &ioc, ip::tcp::socket local) : + ioc_(ioc), + local_(std::move(local)), + buffer_local_(), + remote_(ioc), + buffer_remote_(), + uuid_(to_string(random_generator_mt19937()())) +{ +} + +void basic_session::cipher_setup( + std::unique_ptr &cipher, + const std::string &method, + const std::vector &key, + const std::vector &iv) +{ + if (cipher == nullptr || cipher->name() != method) + cipher = Botan::StreamCipher::create_or_throw(method); + cipher->set_key(key.data(), key.size()); + cipher->set_iv(iv.data(), iv.size()); +} + +} diff --git a/src/session/client_session.cpp b/src/session/client_session.cpp index 3518583..5988575 100644 --- a/src/session/client_session.cpp +++ b/src/session/client_session.cpp @@ -1,152 +1,145 @@ #include #include + #include #include + #include -#include #include #include +#include + namespace msocks { void client_session::start(yield_context yield) { - try - { - auto host_service = utility::async_local_socks5(strand, local, yield); - spdlog::info("[{}] connect to: {}",session_uuid,host_service); - remote.async_connect(addr, yield); - async_handshake(host_service, yield); - spawn( - strand, - [this, p = shared_from_this()](yield_context yield) - { - fwd_local_remote(yield); - }); - spawn( - strand, - [this, p = shared_from_this()](yield_context yield) - { - fwd_remote_local(yield); - }); - } - catch ( system_error &e ) - { - spdlog::info("[{}] error: {}",session_uuid,e.what()); - } + try + { + auto target_address = utility::async_local_socks5(ioc_, local_, yield); + ip::tcp::endpoint ep(ip::make_address(attribute_.remote_address), attribute_.remote_port); + remote_.async_connect(ep, yield); + async_handshake(target_address, yield); + spawn( + ioc_, + [this, p = shared_from_this()](yield_context yield) + { + fwd_local_remote(yield); + }); + spawn( + ioc_, + [this, p = shared_from_this()](yield_context yield) + { + fwd_remote_local(yield); + }); + } + catch (system_error& e) + { + spdlog::info("[{}] error: {}", uuid(), e.what()); + } } -client_session::client_session( - io_context::strand &strand_, - ip::tcp::socket local_, - const ip::tcp::endpoint &addr_, - const std::vector &key_) : - session(strand_, std::move(local_), key_), - addr(addr_) +async_result::return_type client_session::async_handshake( + const std::vector& target_address, + yield_context yield) { -} - - -void client_session::async_handshake( - const std::string &host_service, - yield_context yield) -{ - Handler handler(yield); - Result result(handler); - spawn( - strand, - [this, p = shared_from_this(), &host_service, handler](yield_context yield) - { - do_async_handshake(handler, host_service, yield); - } - ); - return result.get(); + async_result::completion_handler_type handler(yield); + async_result result(handler); + spawn( + ioc_, + [this, p = shared_from_this(), target_address, handler](yield_context yield) + { + do_async_handshake(handler, target_address, yield); + } + ); + return result.get(); } void client_session::fwd_remote_local(yield_context yield) { - auto ec = pair(remote, local, yield, buffer(buffer_remote), - [this](mutable_buffer b) - { - recv_cipher->cipher1((uint8_t *) b.data(), b.size()); - }); - switch ( ec.value()) - { - case error::eof: - case error::operation_aborted: - break; - default: - break; - } - error_code ignored_ec; - local.cancel(ignored_ec); - remote.cancel(ignored_ec); + try + { + std::vector iv(8); + async_read(remote_, buffer(iv), yield); + cipher_setup(remote_local_cipher_, attribute_.method, attribute_.key, iv); + + error_code ec; + utility::socket_pair( + remote_, local_, + ioc_, + buffer(buffer_remote_), + [](std::size_t, yield_context) { return; }, + [this](mutable_buffer m_buf) + { + remote_local_cipher_->cipher1((uint8_t*)m_buf.data(), m_buf.size()); + }, + yield[ec]); + + } + catch (system_error & ignored) + { + } } void client_session::fwd_local_remote(yield_context yield) { - auto ec = pair( - local, remote, - yield, - buffer(buffer_local), - [this](mutable_buffer b) - { - send_cipher->cipher1((uint8_t *) b.data(), b.size()); - }); - - switch ( ec.value()) - { - case error::eof: - case error::operation_aborted: - break; - default: - break; - } - error_code ignored_ec; - local.cancel(ignored_ec); - remote.cancel(ignored_ec); + try + { + error_code ec; + utility::socket_pair( + local_, remote_, + ioc_, + buffer(buffer_local_), + [](std::size_t n, yield_context) { return; }, + [this](mutable_buffer m_buf) + { + local_remote_cipher_->cipher1((uint8_t*)m_buf.data(), m_buf.size()); + }, + yield[ec]); + + } + catch (system_error & ignored) + { + } } void client_session::go() { - spawn(strand, [this, p = shared_from_this()](yield_context yield) - { - start(yield); - }); + spawn( + ioc_, + [this, p = shared_from_this()](yield_context yield) + { + start(yield); + } + ); } void client_session::do_async_handshake( - client_session::Handler handler, - const std::string &host_service, - yield_context yield) + async_result::completion_handler_type handler, + const std::vector& target_address, + yield_context yield) { - error_code ec; - try - { - Botan::AutoSeeded_RNG rng; - std::vector send_iv(8); - std::vector recv_iv(8); - rng.randomize(send_iv.data(), send_iv.size()); - cipher_setup("ChaCha(20)", send_iv, send_cipher); - uint16_t size = send_iv.size() + host_service.size(); - std::vector temp(host_service.begin(), host_service.end()); - send_cipher->encrypt(temp); - std::vector buffers - { - buffer(&size, sizeof(size)), - buffer(send_iv), - buffer(temp) - }; - async_write(remote, buffers, transfer_all(), yield); - async_read(remote, buffer(recv_iv), transfer_all(), yield); - cipher_setup("ChaCha(20)", recv_iv, recv_cipher); - } - catch ( system_error &e ) - { - ec = e.code(); - } - auto cb = [ec,handler]()mutable {handler(ec);}; - post(strand,std::move(cb)); + error_code ec; + try + { + Botan::AutoSeeded_RNG rng; + std::vector iv(8); + rng.randomize(iv.data(), iv.size()); + cipher_setup(local_remote_cipher_, attribute_.method, attribute_.key, iv); + auto target_address_encrypted = target_address; + local_remote_cipher_->encrypt(target_address_encrypted); + std::array buffers + { + buffer(iv), + buffer(target_address_encrypted) + }; + async_write(remote_, buffers, transfer_all(), yield); + } + catch (system_error & e) + { + ec = e.code(); + } + post(ioc_, std::bind(handler, ec)); } } diff --git a/src/session/server_session.cpp b/src/session/server_session.cpp index a12aef4..d08dabf 100644 --- a/src/session/server_session.cpp +++ b/src/session/server_session.cpp @@ -1,183 +1,229 @@ #include #include -#include +#include +#include -#include -#include - -#include #include +#include +#include +#include + +#include #include +using namespace boost::endian; namespace msocks { -server_session::server_session( - io_context::strand &strand_, - ip::tcp::socket local_, - const std::vector &key_, - std::shared_ptr limiter_) : - session(strand_, std::move(local_), key_), - limiter(std::move(limiter_)) -{} - void server_session::start(yield_context yield) { try { - steady_timer timer(strand.context()); - ip::tcp::resolver resolver(local.get_executor().context()); - spawn(strand, [this, p = shared_from_this(), &resolver, &timer](yield_context yield) + deadline_timer timer(ioc_); + ip::tcp::resolver resolver(ioc_); + spawn( + ioc_, [this, p = shared_from_this(), &resolver, &timer](yield_context yield) { - timer.expires_after(std::chrono::milliseconds(600)); + timer.expires_from_now(attribute_.timeout); error_code ec; timer.async_wait(yield[ec]); - if ( ec != error::operation_aborted ) + if (ec != error::operation_aborted) { - error_code ec; - local.cancel(ec); + local_.cancel(ec); resolver.cancel(); - remote.cancel(ec); + remote_.cancel(ec); } }); - auto[host, service] = async_handshake(yield); + auto [host, service] = async_handshake(yield); auto result = resolver.async_resolve(host, service, yield); - remote.async_connect(*result.begin(), yield); + remote_.async_connect(*result.begin(), yield); timer.cancel(); spawn( - strand, + ioc_, [this, p = shared_from_this()](yield_context yield) - { - fwd_local_remote(yield); - }); + { + fwd_local_remote(yield); + }); spawn( - strand, + ioc_, [this, p = shared_from_this()](yield_context yield) - { - fwd_remote_local(yield); - }); + { + fwd_remote_local(yield); + }); } - catch ( system_error &e ) + catch (system_error& e) { - - spdlog::info("[{}] error: {}", session_uuid, e.what()); + if (e.code() != error::operation_aborted) + { + spdlog::info("[{}] error: {}", uuid_, e.what()); + } } } void server_session::fwd_local_remote(yield_context yield) { - auto ec = pair(local, remote, yield, buffer(buffer_local), - [this, yield](mutable_buffer b) - { - recv_cipher->cipher1((uint8_t *) b.data(), b.size()); - }); - switch ( ec.value()) + try + { + error_code ec; + utility::socket_pair( + local_, remote_, + ioc_, + buffer(buffer_local_), + [this](std::size_t n, yield_context yield) + { + attribute_.limiter->async_get(n, yield); + }, + [this](mutable_buffer m_buf) + { + local_remote_cipher_->cipher1((uint8_t*)m_buf.data(), m_buf.size()); + printf("\n"); + }, yield[ec]); + } + catch (system_error & ignored) { - case error::eof: - case error::operation_aborted: - case error::connection_reset: - break; - default: - break; + } - error_code ignored_ec; - local.cancel(ignored_ec); - remote.cancel(ignored_ec); } void server_session::fwd_remote_local(yield_context yield) { - auto ec = pair(remote, local, yield, buffer(buffer_remote), - [this, yield](mutable_buffer b) - { - limiter->async_get(b.size(), yield); - send_cipher->cipher1((uint8_t *) b.data(), b.size()); - }); - switch ( ec.value()) + try + { + std::vector iv(8); + Botan::AutoSeeded_RNG rng; + rng.randomize(iv.data(), iv.size()); + cipher_setup(remote_local_cipher_, attribute_.method, attribute_.key, iv); + async_write(local_, buffer(iv), yield); + + error_code ec; + utility::socket_pair( + remote_, local_, + ioc_, + buffer(buffer_remote_), + [this](std::size_t n, yield_context yield) + { + attribute_.limiter->async_get(n, yield); + }, + [this](mutable_buffer m_buf) + { + remote_local_cipher_->cipher1((uint8_t*)m_buf.data(), m_buf.size()); + }, yield[ec]); + } + catch (system_error & ignored) { - case error::eof: - case error::operation_aborted: - break; - default: - break; } - error_code ignored_ec; - local.cancel(ignored_ec); - remote.cancel(ignored_ec); } void server_session::do_async_handshake( - server_session::Handler handler, + async_result)>::completion_handler_type handler, yield_context yield) { error_code ec; std::pair result; + std::vector iv(8); try { - Botan::AutoSeeded_RNG rng; - std::vector send_iv(8); - rng.randomize(send_iv.data(), send_iv.size()); - async_write(local, buffer(send_iv), transfer_all(), yield); - cipher_setup("ChaCha(20)", send_iv, send_cipher); - - uint16_t size = 0; - async_read(local, buffer(&size, sizeof(size)), yield); - std::vector recv_iv(8); - std::vector host_service(size - 8); - std::vector sequence + + async_read(local_, buffer(iv), yield); + cipher_setup(local_remote_cipher_, attribute_.method, attribute_.key, iv); + uint8_t address_type = 0; + async_read(local_, buffer(&address_type, sizeof(address_type)), yield); + local_remote_cipher_->cipher1(&address_type, 1); + if (address_type == socks::addr_ipv4) + { + uint32_t ipv4; + uint16_t port; + std::array sequence + { + buffer(&ipv4,sizeof(ipv4)), + buffer(&port,sizeof(port)) + }; + async_read(local_, sequence, transfer_all(), yield); + local_remote_cipher_->cipher1((uint8_t*)& ipv4, sizeof(ipv4)); + local_remote_cipher_->cipher1((uint8_t*)& port, sizeof(port)); + result.first = ip::make_address_v4(big_to_native(ipv4)).to_string(); + result.second = std::to_string(big_to_native(port)); + } + else if (address_type == socks::addr_ipv6) + { + ip::address_v6::bytes_type ipv6; + uint16_t port; + std::array sequence { - buffer(recv_iv), - buffer(host_service) + buffer(&ipv6,ipv6.size()), + buffer(&port,sizeof(port)) }; - async_read(local, sequence, yield); - cipher_setup("ChaCha(20)", recv_iv, recv_cipher); - recv_cipher->decrypt(host_service); - auto split = std::find(host_service.begin(), host_service.end(), ':'); - result.first = std::string(host_service.begin(), split); - result.second = std::string(split + 1, host_service.end()); + async_read(local_, sequence, transfer_all(), yield); + local_remote_cipher_->cipher1(ipv6.data(), ipv6.size()); + local_remote_cipher_->cipher1((uint8_t*)& port, sizeof(port)); + std::reverse(ipv6.begin(), ipv6.end()); + result.first = ip::make_address_v6(ipv6).to_string(); + result.second = std::to_string(big_to_native(port)); + } + else if (address_type == socks::addr_domain) + { + uint8_t domain_length; + std::string domain; + uint16_t port; + + async_read(local_, buffer(&domain_length, sizeof(domain_length)), yield); + local_remote_cipher_->cipher1(&domain_length, sizeof(domain_length)); + + async_read(local_, dynamic_buffer(domain), transfer_exactly(domain_length), yield); + local_remote_cipher_->cipher1((uint8_t*)domain.data(), domain.size()); + + async_read(local_, buffer(&port, sizeof(port)), yield); + local_remote_cipher_->cipher1((uint8_t*)& port, sizeof(port)); + + result.first = domain; + result.second = std::to_string(big_to_native(port)); + } + else + { + throw system_error(errc::address_not_supported, socks_category()); + } } - catch ( system_error &e ) + catch (system_error & e) { - if ( e.code() != error::operation_aborted ) + if (e.code() != error::operation_aborted) ec = e.code(); } - auto cb = [handler, ec, result]()mutable - { handler(ec, result); }; - post(strand, std::move(cb)); + post(ioc_, std::bind(handler, ec, result)); } -server_session::Result::return_type +async_result)>::return_type server_session::async_handshake(yield_context yield) { - Handler handler(yield); - Result result(handler); + async_result)>::completion_handler_type handler(yield); + async_result)> result(handler); spawn( - strand, + ioc_, [handler, this, p = shared_from_this()](yield_context yield) - { - do_async_handshake(handler, yield); - }); + { + do_async_handshake(handler, yield); + }); return result.get(); } + void server_session::go() { - spawn(strand, [this, p = shared_from_this()](yield_context yield) + spawn( + ioc_, + [this, p = shared_from_this()](yield_context yield) { start(yield); }); -} -void server_session::notify_reuse( - const io_context::strand &strand_, ip::tcp::socket local_, - const std::vector &key_, const std::shared_ptr &limiter_) +} +void +server_session::notify_reuse(const io_context& ioc, ip::tcp::socket local, const server_session_attribute& attribute) { - (void) strand_; - (void) key_; - (void) limiter_; - local = std::move(local_); - remote = ip::tcp::socket(local.get_executor().context()); + (void)ioc; + (void)attribute; + local_ = std::move(local); + remote_ = ip::tcp::socket(ioc_); } } \ No newline at end of file diff --git a/src/utility/local_socks5.cpp b/src/utility/local_socks5.cpp new file mode 100644 index 0000000..6b133f4 --- /dev/null +++ b/src/utility/local_socks5.cpp @@ -0,0 +1,125 @@ +// +// Created by maxtorm on 2019/4/14. +// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +using namespace boost::endian; + +namespace msocks::utility +{ +void detail::do_local_socks5( + io_context& strand, + ip::tcp::socket& local, + async_result)>::completion_handler_type handler, + yield_context yield) +{ + std::array temp{}; + MSOCKS_PACK( + struct + { + uint8_t version; + uint8_t n_method; + }) + auth_method {}; + MSOCKS_PACK( + struct + { + uint8_t version; + uint8_t method; + } + ) auth_reply { + socks::socks5_version, socks::auth_no_auth + }; + + MSOCKS_PACK( + struct + { + uint8_t version; + uint8_t cmd; + uint8_t reserve; + uint8_t addr_type; + }) + request {}; + + MSOCKS_PACK( + struct + { + uint8_t version; + uint8_t rep; + uint8_t rsv; + uint8_t atyp; + uint32_t bnd_addr; + uint16_t bnd_port; + }) + reply { + socks::socks5_version, 0x00, 0x00, socks::addr_ipv4, 0x00000000, 0x0000 + }; + + error_code ec; + std::vector result; + try + { + async_read(local, buffer(&auth_method, sizeof(auth_method)), yield); + async_read(local, buffer(temp, auth_method.n_method), yield); + async_write(local, buffer(&auth_reply, sizeof(auth_reply)), yield); + async_read(local, buffer(&request, sizeof(request)), yield); + switch (request.cmd) + { + case socks::conn_tcp: + break; + case socks::conn_bind: + case socks::conn_udp: + default: + throw system_error(errc::cmd_not_supported, socks_category()); + } + result.push_back(request.addr_type); + if (request.addr_type == socks::addr_ipv4) + { + + async_read(local, buffer(temp, 32 / 8 + 2), yield); + std::copy(temp.begin(), temp.begin() + 32 / 8 + 2, std::back_inserter(result)); + } + else if (request.addr_type == socks::addr_ipv6) + { + async_read(local, buffer(temp, 128 / 8 + 2), yield); + std::copy(temp.begin(), temp.begin() + 128 / 8 + 2, std::back_inserter(result)); + } + else if (request.addr_type == socks::addr_domain) + { + uint8_t domain_length; + async_read(local, buffer(&domain_length, sizeof(domain_length)), yield); + result.push_back(domain_length); + async_read(local, buffer(temp, domain_length + 2), yield); + std::copy(temp.begin(), temp.begin() + domain_length + 2, std::back_inserter(result)); + } + else + { + throw system_error(errc::address_not_supported, socks_category()); + } + async_write(local, buffer(&reply, sizeof(reply)), yield); + } + catch (system_error& e) + { + ec = e.code(); + } + post(strand, std::bind(handler, ec, result)); +} + +async_result)>::return_type +async_local_socks5(io_context& ioc, ip::tcp::socket& local, yield_context yield) +{ + async_result)>::completion_handler_type handler(yield); + async_result)> result(handler); + spawn(ioc, std::bind(&detail::do_local_socks5, std::ref(ioc), std::ref(local), handler, std::placeholders::_1)); + return result.get(); +} +} diff --git a/src/utility/rate_limiter.cpp b/src/utility/rate_limiter.cpp new file mode 100644 index 0000000..c9f4aa2 --- /dev/null +++ b/src/utility/rate_limiter.cpp @@ -0,0 +1,75 @@ +// +// Created by maxtorm on 2019/4/14. +// + +#include + +#include + +namespace msocks::utility +{ + +async_result::return_type rate_limiter::async_get(std::size_t n, yield_context yield) +{ + async_result::completion_handler_type handler(yield); + async_result result(handler); + if (no_limit_) + { + post(ioc_, std::bind(handler)); + return result.get(); + } + + if (available_ >= n) + { + available_ -= n; + post(ioc_, std::bind(handler)); + return result.get(); + } + + if (wait_queue_.empty()) + { + signal_.expires_from_now(boost::posix_time::pos_infin); + } + + unique_pair pair(new storage_pair(n, handler)); + wait_queue_.emplace(std::move(pair)); + return result.get(); +} + + +void rate_limiter::start() +{ + if (no_limit_) + { + return; + } + + spawn( + ioc_, + [this, p = shared_from_this()](yield_context yield) + { + while (true) + { + if (wait_queue_.empty()) + { + error_code ignored_ec; + signal_.async_wait(yield[ignored_ec]); + } + for (auto i = 0; i < 10; i++) + { + timer_.expires_from_now(boost::posix_time::milliseconds(100)); + timer_.async_wait(yield); + available_ += limit_ / 10; + while (!wait_queue_.empty() && wait_queue_.top()->first <= available_) + { + auto handler = wait_queue_.top()->second; + available_ -= wait_queue_.top()->first; + wait_queue_.pop(); + post(ioc_, std::bind(handler)); + } + } + } + }); +} + +} \ No newline at end of file diff --git a/src/utility/socks_error.cpp b/src/utility/socks_error.cpp new file mode 100644 index 0000000..6333417 --- /dev/null +++ b/src/utility/socks_error.cpp @@ -0,0 +1,9 @@ +#include +namespace msocks +{ +boost::system::error_category& socks_category() +{ + static class detail::socks_category category; + return category; +} +} \ No newline at end of file