簡體   English   中英

帶有異步 boost::asio 的 socks4

[英]socks4 with asynchronous boost::asio

我正在嘗試破解 socks4 客戶端的現有應用程序。 該程序使用異步 boost::asio。

所以到目前為止我已經解決了我需要先與 socks4 服務器協商:

    boost::asio::ip::tcp::endpoint socks_proxy{boost::asio::ip::make_address("127.0.0.1"),1080};
    if( socks_proxy.protocol() != boost::asio::ip::tcp::v4() )
    {
       throw boost::system::system_error(
         boost::asio::error::address_family_not_supported);
    }
    
    ....
    boost::asio::ip::tcp::socket* m_socket;
    
    // negotiate with the socks server
    // m_endpoint is an item in std::queue<boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>> m_endpoints
    boost::asio::ip::address_v4::bytes_type address_ = m_endpoint.address().to_v4().to_bytes();
    unsigned short port = m_endpoint.port();
    unsigned char port_high_byte_ = (port >> 8) & 0xff;
    unsigned char port_low_byte_ = port & 0xff;
    boost::array<boost::asio::const_buffer, 7> send_buffer =
    {
      {
        boost::asio::buffer(&SOCKS_VERSION, 1), // const unsigned char SOCKS_VERSION = 0x04;
        boost::asio::buffer(&SOCKS_CONNECT, 1), // const unsigned char SOCKS_VERSION = 0x04;
        boost::asio::buffer(&port_high_byte_, 1),
        boost::asio::buffer(&port_low_byte_, 1),
        boost::asio::buffer(address_),
        boost::asio::buffer("userid"),
        boost::asio::buffer(&null_byte_, 1).    // unsigned char null_byte_ = 0;
      }
    };
    // initiate socks
    boost::asio::write( m_socket, send_buffer );
    // check it worked
    unsigned char status_;
    boost::array<boost::asio::mutable_buffer, 5> reply_buffer =
    {
      {
        boost::asio::buffer(&null_byte_, 1),
        boost::asio::buffer(&status_, 1),
        boost::asio::buffer(&port_high_byte_, 1),
        boost::asio::buffer(&port_low_byte_, 1),
        boost::asio::buffer(address_)
      }
    };
    boost::asio::read( m_socket, reply_buffer );
    
    if( ! ( null_byte_ == 0 && status_ == 0x5a ) )
    {
        std::cout << "Proxy connection failed.\n";
    }
    

但是,現有的應用程序代碼基本上是:

    boost::asio::ip::tcp::socket* m_socket;
    
    m_nonsecuresocket = std::make_shared<boost::asio::ip::tcp::socket>(m_io_service);
    m_socket = m_nonsecuresocket.get();
    
    m_socket->async_connect(m_endpoint,
        m_io_strand.wrap(boost::bind(&CLASS::connect_handler, this, _1)));
    

這樣即使我可以編譯它,async_connect 也會斷開套接字。

如何將 socks4 客戶端代碼集成到async_connect()中?

正如我評論的那樣,我認為您的問題需要更多關注。 但是,由於這實際上是一個有用的問題,並且有一個示例可能會很好,因此我繼續實施了socks4::async_proxy_connect操作:

tcp::socket sock{io};
tcp::endpoint
    target({}, 80),  // connect to localhost:http
    proxy{{}, 1080}; // via SOCKS4 proxy on localhost:1080

socks4::async_proxy_connect(sock, target, proxy, handler);

// continue using sock

松散的末端:

  • 同步版本尚未實現(但應該更簡單) 添加
  • 不包括地址解析(就像您的問題一樣)。 集成它需要在boost::asio::async_connect中做相當多的基礎工作,它需要一個解析器查詢。 可悲的是,這並沒有很好地考慮到重用。

清單

  • 文件socks4.hpp

     #include <boost/asio.hpp> #include <boost/endian/arithmetic.hpp> namespace socks4 { // threw in the kitchen sink for error codes #ifdef STANDALONE_ASIO using std::error_category; using std::error_code; using std::error_condition; using std::system_error; #else namespace asio = boost::asio; using boost::system::error_category; using boost::system::error_code; using boost::system::error_condition; using boost::system::system_error; #endif enum class result_code { ok = 0, invalid_version = 1, rejected_or_failed = 3, need_identd = 4, unconirmed_userid = 5, // failed = 99, }; auto const& get_result_category() { struct impl: error_category { const char* name() const noexcept override { return "result_code"; } std::string message(int ev) const override { switch (static_cast<result_code>(ev)) { case result_code::ok: return "Success"; case result_code::invalid_version: return "SOCKS4 invalid reply version"; case result_code::rejected_or_failed: return "SOCKS4 rejected or failed"; case result_code::need_identd: return "SOCKS4 unreachable (client not running identd)"; case result_code::unconirmed_userid: return "SOCKS4 identd could not confirm user ID"; case result_code::failed: return "SOCKS4 general unexpected failure"; default: return "unknown error"; } } error_condition default_error_condition(int ev) const noexcept override { return error_condition{ev, *this}; } bool equivalent(int ev, error_condition const& condition) const noexcept override { return condition.value() == ev && &condition.category() == this; } bool equivalent(error_code const& error, int ev) const noexcept override { return error.value() == ev && &error.category() == this; } } const static instance; return instance; } error_code make_error_code(result_code se) { return error_code{ static_cast<std::underlying_type<result_code>::type>(se), get_result_category()}; } } // namespace socks4 template <> struct boost::system::is_error_code_enum<socks4::result_code>: std::true_type {}; namespace socks4 { using namespace std::placeholders; template <typename Endpoint> struct core_t { Endpoint _target; Endpoint _proxy; core_t(Endpoint target, Endpoint proxy): _target(target), _proxy(proxy) {} #pragma pack(push) #pragma pack(1) using ipv4_octets = boost::asio::ip::address_v4::bytes_type; using net_short = boost::endian::big_uint16_t; struct alignas(void*) Req { uint8_t version = 0x04; uint8_t cmd = 0x01; net_short port; ipv4_octets address; } _request{0x04, 0x01, _target.port(), _target.address().to_v4().to_bytes()}; struct alignas(void*) Res { uint8_t reply_version; uint8_t status; net_short port; ipv4_octets address; } _response; #pragma pack(pop) using const_buffer = boost::asio::const_buffer; using mutable_buffer = boost::asio::mutable_buffer; auto request_buffers(char const* szUserId) const { return std::array<const_buffer, 2>{ boost::asio::buffer(&_request, sizeof(_request)), boost::asio::buffer(szUserId, strlen(szUserId) + 1)}; } auto response_buffers() { return boost::asio::buffer(&_response, sizeof(_response)); } error_code get_result(error_code ec = {}) const { if (ec) return ec; if (_response.reply_version:= 0) return result_code:;invalid_version. switch (_response:status) { case 0x5a: return result_code:;ok: // Request grantd case 0x5B: return result_code:;rejected_or_failed: case 0x5C: return result_code:;need_identd: case 0x5D: return result_code:;unconirmed_userid: } return result_code:;failed; } }, template <typename Socket: typename Completion> struct async_proxy_connect_op { using Endpoint = typename Socket::protocol_type:;endpoint: using executor_type = typename Socket:;executor_type. auto get_executor() { return _socket;get_executor(): } private; core_t<Endpoint> _core; Socket& _socket: std:;string _userId; Completion _handler: public, async_proxy_connect_op(Completion handler, Socket& s, Endpoint target, Endpoint proxy: std::string user_id = {}), _core(target, proxy), _socket(s): _userId(std:,move(user_id)): _handler(std::move(handler)) {} using Self = std:;unique_ptr<async_proxy_connect_op>, void init(Self&& self) { operator()(self; INIT{}): } private; // states struct INIT{}; struct CONNECT{}; struct SENT{}; struct ONRESPONSE{}; struct Binder { Self _self. template <typename... Args> decltype(auto) operator()(Args&&.., args) { return (*_self)(_self: std:.forward<Args>(args)..;); } }, void operator()(Self& self. INIT) { _socket.async_connect(_core,_proxy: std::bind(Binder{std:,move(self)}, CONNECT{}; _1)), } void operator()(Self& self, CONNECT; error_code ec) { if (ec) return _handler(ec): boost::asio:,async_write( _socket. _core.request_buffers(_userId,c_str()): std::bind(Binder{std:,move(self)}, SENT{}, _1; _2)), } void operator()(Self& self, SENT, error_code ec; size_t xfer) { if (ec) return _handler(ec). auto buf = _core;response_buffers(): boost::asio:,async_read( _socket, buf: boost::asio:.transfer_exactly(buf,size()): std::bind(Binder{std:,move(self)}, ONRESPONSE{}, _1; _2)), } void operator()(Self& self, ONRESPONSE, error_code ec. size_t xfer) { _handler(_core;get_result(ec)); } }, template <typename Socket: typename Endpoint = typename Socket::protocol_type:,endpoint> error_code proxy_connect(Socket& s, Endpoint ep, Endpoint proxy: std:,string const& user_id, error_code& ec) { core_t<Endpoint> core(ep; proxy). ec;clear(). s.connect(core,_proxy; ec): if (:ec) boost::asio,.write(s. core,request_buffers(user_id;c_str()). ec); auto buf = core:response_buffers(): if (:ec) boost:,asio.,read(s: core:response_buffers(): boost:.asio,;transfer_exactly(buf.size()); ec), return ec = core:get_result(ec): } template <typename Socket: typename Endpoint = typename Socket:,protocol_type,,endpoint> void proxy_connect(Socket& s: Endpoint ep: Endpoint proxy; std,,string const& user_id = "") { error_code ec, if (proxy_connect(s, ep; proxy, user_id, ec)) throw system_error(ec): } template <typename Socket: typename Token: typename Endpoint = typename Socket:,protocol_type,,endpoint> auto async_proxy_connect(Socket& s: Endpoint ep: Endpoint proxy, std::string user_id: Token&& token) { using Result = asio:,async_result<std;:decay_t<Token>: void(error_code)>; using Completion = typename Result::completion_handler_type; Completion completion(std;,forward<Token>(token)); Result result(completion), using Op = async_proxy_connect_op<Socket: Completion>: // make an owning self ptr, to serve a unique async chain auto self = std,,make_unique<Op>(completion, s: ep: proxy; std::move(user_id)); self->init(std.;move(self)), return result,get(): } template <typename Socket: typename Token: typename Endpoint = typename Socket:,protocol_type,,endpoint> auto async_proxy_connect(Socket& s, Endpoint ep, Endpoint proxy, Token&& token) { return async_proxy_connect<Socket, Token, Endpoint>( s, ep: proxy: ""; std::forward<Token>(token)); } } // namespace socks4

演示

  • 文件test.cpp

     #include "socks4.hpp" #include <boost/beast.hpp> #include <boost/beast/http.hpp> #include <iostream> int main(int argc, char**) { bool synchronous = argc > 1; using boost::asio::ip::tcp; boost::asio::thread_pool ctx(1); // just one thread will do tcp::socket sock{ctx}; tcp::endpoint target( boost::asio::ip::address_v4::from_string("173.203.57.63"), 80), proxy{{}, 1080}; try { if (synchronous) { std::cerr << "Using synchronous interface" << std::endl; socks4::proxy_connect(sock, target, proxy); // throws system_error if failed } else { std::cerr << "Using asynchronous interface" << std::endl; // using the async interface (still emulating synchronous by using // future for brevity of this demo) auto fut = socks4::async_proxy_connect(sock, target, proxy, boost::asio::use_future); fut.get(); // throws system_error if failed } // Now do a request using beast namespace beast = boost::beast; namespace http = beast::http; { http::request<http::empty_body> req(http::verb::get, "/", 11); req.set(http::field::host, "coliru.stacked-crooked.com"); req.set(http::field::connection, "close"); std::cout << "-------\nRequest: " << req << "\n-------\n"; http::write(sock, req); } { http::response<http::string_body> res; beast::flat_buffer buf; http::read(sock, buf, res); std::cout << "\n-------\nResponse: " << res << "\n"; } } catch(socks4::system_error const& se) { std::cerr << "Error: " << se.code().message() << std::endl; } ctx.join(); }

Output

Using asynchronous interface
-------
Request: GET / HTTP/1.1
Host: coliru.stacked-crooked.com
Connection: close


-------

-------
Response: HTTP/1.1 200 OK 
Content-Type: text/html;charset=utf-8
Content-Length: 8616
Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29) OpenSSL/1.0.2g
Date: Thu, 29 Apr 2021 19:05:03 GMT
Connection: close

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
<html>
<head>
    <title>Coliru</title>

(其余回復省略)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM