diff options
Diffstat (limited to 'socks5.hpp')
-rw-r--r-- | socks5.hpp | 253 |
1 files changed, 190 insertions, 63 deletions
@@ -1,80 +1,112 @@ #include <atomic> #include <boost/asio.hpp> #include <boost/asio/buffer.hpp> +#include <boost/asio/deadline_timer.hpp> #include <boost/asio/io_context.hpp> #include <boost/asio/ip/tcp.hpp> #include <boost/core/noncopyable.hpp> +#include <boost/date_time/posix_time/posix_time_duration.hpp> #include <boost/noncopyable.hpp> #include <boost/system/detail/error_code.hpp> #include <boost/system/error_code.hpp> +#include <boost/variant.hpp> #include <cstdint> #include <memory> #include <string_view> +#include "boost-asio-fastbuffer/fastbuffer.hpp" + namespace SOCKS5 { -class StreamBuffer : public boost::noncopyable { +class DestinationSocketBase : private boost::noncopyable { public: - explicit StreamBuffer(std::size_t size) - : m_bufferUsed{0}, m_bufferSize{size} { - m_buffer = new std::uint8_t[size]; - } - StreamBuffer(StreamBuffer &&moveable) { - m_bufferUsed = moveable.m_bufferUsed; - m_bufferSize = moveable.m_bufferSize; - m_buffer = moveable.m_buffer; - moveable.m_buffer = nullptr; - } - ~StreamBuffer() { delete[] m_buffer; } - void operator+=(std::size_t commit_size) { m_bufferUsed += commit_size; } - void operator+=(const std::initializer_list<uint8_t> &to_add) { - std::copy(to_add.begin(), to_add.end(), &m_buffer[m_bufferUsed]); - m_bufferUsed += to_add.size(); - } - void operator-=(std::size_t consume_size) { m_bufferUsed -= consume_size; } - auto operator+() { - return boost::asio::buffer(&m_buffer[m_bufferUsed], - m_bufferSize - m_bufferUsed); + virtual ~DestinationSocketBase() {} + + template <typename Callback> + void + connect_tcp(boost::asio::ip::tcp::resolver::results_type::const_iterator &it, + Callback &&handler) { + do_connect_tcp(it, std::forward<Callback>(handler)); } - auto operator-() { return boost::asio::buffer(&m_buffer[0], m_bufferUsed); } - auto operator[](std::size_t index) const { return m_buffer[index]; } - const auto *operator()(std::size_t index = 0) const { - return &m_buffer[index]; + void tcp_bind() { throw std::runtime_error("TCP Bind not implemented"); } + void udp_bind() { throw std::runtime_error("UDP Bind not implemented"); } + template <typename Callback> + bool read(BufferBase &buffer, Callback &&handler) { + return do_read(buffer, std::forward<Callback>(handler)); } - auto size() const { return m_bufferUsed; } - auto getHealth() const { - return (static_cast<float>(m_bufferUsed) / - static_cast<float>(m_bufferSize)); + template <typename Callback> + bool write(BufferBase &buffer, std::size_t length, Callback &&handler) { + return do_write(buffer, length, std::forward<Callback>(handler)); } + virtual bool cancel() = 0; -private: - std::size_t m_bufferUsed; - std::size_t m_bufferSize; - std::uint8_t *m_buffer; +protected: + virtual void do_connect_tcp( + boost::asio::ip::tcp::resolver::results_type::const_iterator &it, + std::function<void(boost::system::error_code)>) = 0; + virtual bool + do_read(BufferBase &, + std::function<void(boost::system::error_code, std::size_t)>) = 0; + virtual bool + do_write(BufferBase &, std::size_t, + std::function<void(boost::system::error_code, std::size_t)>) = 0; +}; + +template <typename Executor> +class AsyncDestinationSocket : public DestinationSocketBase { +public: + AsyncDestinationSocket(const Executor &exec) : m_strand(exec) {} + ~AsyncDestinationSocket() {} + +protected: + void do_connect_tcp( + boost::asio::ip::tcp::resolver::results_type::const_iterator &it, + std::function<void(boost::system::error_code)> handler) override; + bool do_read(BufferBase &buffer, + std::function<void(boost::system::error_code, std::size_t)> + handler) override; + bool do_write(BufferBase &buffer, std::size_t length, + std::function<void(boost::system::error_code, std::size_t)> + handler) override; + bool cancel() override; + + boost::asio::strand<Executor> m_strand; + boost::optional<boost::variant<boost::asio::ip::tcp::socket // TCP connect + >> + m_socket; }; -class ProxySessionBase : public boost::noncopyable { +class ProxyBase : private boost::noncopyable { public: - ProxySessionBase(std::uint32_t session_id, - boost::asio::ip::tcp::socket &&client_socket, - std::size_t buffer_size = BUFSIZ); - ProxySessionBase(std::uint32_t session_id, - boost::asio::ip::tcp::socket &&client_socket, - StreamBuffer &&input_buffer, StreamBuffer &&output_buffer); + ProxyBase(std::uint32_t session_id, + boost::asio::ip::tcp::socket &&client_socket, + std::size_t buffer_size = BUFSIZ); + ProxyBase(std::uint32_t session_id, + boost::asio::ip::tcp::socket &&client_socket, + ContiguousStreamBuffer &&input_buffer, + ContiguousStreamBuffer &&output_buffer); +protected: std::uint32_t m_sessionId; - StreamBuffer m_inBuf; - StreamBuffer m_outBuf; + ContiguousStreamBuffer m_inBuf; + ContiguousStreamBuffer m_outBuf; boost::asio::ip::tcp::socket m_clientSocket; }; -class ProxySessionAuth : public ProxySessionBase, - public std::enable_shared_from_this<ProxySessionAuth> { +class ProxyAuth : private ProxyBase, + public std::enable_shared_from_this<ProxyAuth> { public: - ProxySessionAuth(std::uint32_t session_id, - boost::asio::ip::tcp::socket &&client_socket); - void start(); + ProxyAuth(std::uint32_t session_id, + boost::asio::ip::tcp::socket &&client_socket); + template <typename GetDsCallback> void start(GetDsCallback &&handler) { + m_getDestinationSocket = std::forward<GetDsCallback>(handler); + start_internal(); + } + void set_session_buffer_size(std::size_t buffer_size) { + session_buffer_size = buffer_size; + } private: + void start_internal(); void recv_client_greeting(const boost::system::error_code &ec, std::size_t length); void send_server_greeting(bool auth_supported); @@ -82,27 +114,38 @@ private: std::size_t length); void process_connection_request(); void send_server_response(std::uint8_t proxy_cmd, std::uint8_t status_code); - void resolve_destination_host(std::uint8_t proxy_cmd, - const std::string_view &host, - std::uint16_t port); + void resolve_tcp_destination_host(std::uint8_t proxy_cmd, + const std::string_view &host, + std::uint16_t port); void connect_to_destination(std::uint8_t proxy_cmd); void handle_write(const boost::system::error_code &ec, std::size_t length); void handle_response_write(std::uint8_t proxy_cmd, std::uint8_t status_code, const boost::system::error_code &ec, std::size_t length); - boost::asio::ip::tcp::resolver m_resolver; - boost::asio::ip::tcp::endpoint m_endpoint; - boost::asio::ip::tcp::socket m_destinationSocket; + std::size_t session_buffer_size; + std::function<std::shared_ptr<DestinationSocketBase>( + boost::asio::any_io_executor)> + m_getDestinationSocket; + std::shared_ptr<DestinationSocketBase> m_destinationSocket; + boost::optional<boost::asio::ip::tcp::resolver> m_tcp_resolver; + boost::asio::ip::tcp::resolver::results_type m_tcp_resolver_results; + boost::asio::ip::tcp::resolver::results_type::const_iterator + m_tcp_resolver_iter; }; -class ProxySessionTcp : public ProxySessionBase, - public std::enable_shared_from_this<ProxySessionTcp> { +class ProxySession : private ProxyBase, + public std::enable_shared_from_this<ProxySession> { public: - explicit ProxySessionTcp(std::uint32_t session_id, - boost::asio::ip::tcp::socket &&client_socket, - boost::asio::ip::tcp::socket &&destination_socket, - std::size_t buffer_size = 65535); + ProxySession(std::uint32_t session_id, + boost::asio::ip::tcp::socket &&client_socket, + std::shared_ptr<DestinationSocketBase> &&dest_socket, + std::size_t buffer_size); + ProxySession(std::uint32_t session_id, + boost::asio::ip::tcp::socket &&client_socket, + std::shared_ptr<DestinationSocketBase> &&dest_socket, + ContiguousStreamBuffer &&input_buffer, + ContiguousStreamBuffer &&output_buffer); void start(); private: @@ -116,21 +159,105 @@ private: void handle_destination_write(const boost::system::error_code &ec, std::size_t length); - boost::asio::ip::tcp::socket m_destinationSocket; + std::shared_ptr<DestinationSocketBase> m_destinationSocket; }; -class ProxyServer : public boost::noncopyable { +class ProxyServer : private boost::noncopyable { public: ProxyServer(boost::asio::io_context &ioc, const boost::asio::ip::tcp::endpoint &local_endpoint); ProxyServer(boost::asio::io_context &ioc, const std::string &listen_addr, std::uint16_t listen_port); void start(); + void stop(); -private: - void async_accept(); +protected: + virtual void async_accept(); std::atomic<uint32_t> m_nextSessionId; boost::asio::ip::tcp::acceptor m_acceptor; }; + +// End Of Minimal Implementation + +template <typename Executor> +class LoggingAsyncDestinationSocket : public AsyncDestinationSocket<Executor> { +public: + LoggingAsyncDestinationSocket(const Executor &exec) + : AsyncDestinationSocket<Executor>(exec), m_bytesRead{0}, + m_bytesWritten{0} {} + ~LoggingAsyncDestinationSocket() {} + std::size_t get_bytes_read() { + return m_bytesRead.exchange(0, std::memory_order_relaxed); + } + std::size_t get_bytes_written() { + return m_bytesWritten.exchange(0, std::memory_order_relaxed); + } + +private: + void do_connect_tcp( + boost::asio::ip::tcp::resolver::results_type::const_iterator &it, + std::function<void(boost::system::error_code)> handler) override; + bool do_read(BufferBase &buffer, + std::function<void(boost::system::error_code, std::size_t)> + handler) override; + bool do_write(BufferBase &buffer, std::size_t length, + std::function<void(boost::system::error_code, std::size_t)> + handler) override; + + std::atomic<std::size_t> m_bytesRead; + std::atomic<std::size_t> m_bytesWritten; +}; + +class LoggingProxyServer : public ProxyServer { +public: + LoggingProxyServer(boost::asio::io_context &ioc, + const std::string &listen_addr, std::uint16_t listen_port); + void start(); + void stop(); + +private: + void async_timer(); + void async_accept() override; + + std::vector<std::weak_ptr< + LoggingAsyncDestinationSocket<boost::asio::any_io_executor>>> + m_weakDestinationSockets; + boost::asio::deadline_timer m_statusLogger; + std::atomic<std::size_t> m_bytesRead; + std::atomic<std::size_t> m_bytesWritten; +}; + +// The following is just an example if you want to use a custom proxy/tunnel +// protocol ;) + +template <typename Executor> +class CustomProtocolAsyncDestinationSocket + : public AsyncDestinationSocket<Executor> { +public: + CustomProtocolAsyncDestinationSocket(const Executor &exec) + : AsyncDestinationSocket<Executor>(exec) {} + ~CustomProtocolAsyncDestinationSocket() {} + +private: + bool do_read(BufferBase &buffer, + std::function<void(boost::system::error_code, std::size_t)> + handler) override; + bool do_write(BufferBase &buffer, std::size_t length, + std::function<void(boost::system::error_code, std::size_t)> + handler) override; +}; + +class CustomProtocolProxyServer : public ProxyServer { +public: + CustomProtocolProxyServer(boost::asio::io_context &ioc, + const std::string &listen_addr, + std::uint16_t listen_port); + void start(); + void stop(); + +private: + void async_accept() override; +}; + }; // namespace SOCKS5 |