Search code examples
c++boostsocks

Trying to connect to a SOCKS 5 server using boost::asio but my connection request turns out malformed


I'm trying to establish a SOCKS 5 connection (to a tor proxy) in my code but my connection request (after greeting and auth method choose) is completely malformed for some reason.

I tried using gdb to look at values in-memory but that looks fine. also tried printing info to console which gave me nothing too.

    std::vector<boost::asio::const_buffer> conn(unsigned char cmd, unsigned char type, std::string hostname, int port) {
                int cmd_ = 0x04;
                int type_ = 0x03;
                std::vector<boost::asio::const_buffer> buffers = {
                    //{
                        boost::asio::buffer(&version, 1),
                        boost::asio::buffer(&cmd_, 1),
                        boost::asio::buffer(&null_byte, 1),
                        boost::asio::buffer(&type_, 1)
                    //}
                };
    
                std::cout << "Conn:" << cmd << std::endl << "type:" << type << std::endl;
    
                if (type_ == IPv4) {
                    int byte1=0x5d, byte2=0xb8, byte3=0xd8, byte4=0x22;
                    char dot;
                    std::istringstream s(hostname);  // input stream that now contains the ip address string
    
                    //s >> byte1 >> dot >> byte2 >> dot >> byte3 >> dot >> byte4 >> dot;
                    // buffers.push_back(boost::asio::buffer({
                    //     (unsigned char)byte1,
                    //     (unsigned char)byte2,
                    //     (unsigned char)byte3,
                    //     (unsigned char)byte4
                    // }));
                    buffers.push_back(boost::asio::buffer(&byte1, 1));    
                    buffers.push_back(boost::asio::buffer(&byte2, 1));            
                    buffers.push_back(boost::asio::buffer(&byte3, 1));            
                    buffers.push_back(boost::asio::buffer(&byte4, 1));            
    
                } else if (type_ == IPv6) {
                    int byte1,
                        byte2,
                        byte3,
                        byte4,
                        byte5,
                        byte6,
                        byte7,
                        byte8,
                        byte9,
                        byte10,
                        byte11,
                        byte12,
                        byte13,
                        byte14,
                        byte15,
                        byte16;
                    char sep;
                    std::istringstream s(hostname);  // input stream that now contains the ip address string
    
                    s >> 
                        byte1 >> sep >>
                        byte2 >> sep >>
                        byte3 >> sep >>
                        byte4 >> sep >>
                        byte5 >> sep >>
                        byte6 >> sep >>
                        byte7 >> sep >>
                        byte8 >> sep >>
                        byte9 >> sep >>
                        byte10 >> sep >>
                        byte11 >> sep >>
                        byte12 >> sep >>
                        byte13 >> sep >>
                        byte14 >> sep >>
                        byte15 >> sep >>
                        byte16;
    
                    buffers.push_back(boost::asio::buffer({
                        (unsigned char)byte1,
                        (unsigned char)byte2,
                        (unsigned char)byte3,
                        (unsigned char)byte4,
                        (unsigned char)byte5,
                        (unsigned char)byte6,
                        (unsigned char)byte7,
                        (unsigned char)byte8,
                        (unsigned char)byte9,
                        (unsigned char)byte10,
                        (unsigned char)byte11,
                        (unsigned char)byte12,
                        (unsigned char)byte13,
                        (unsigned char)byte14,
                        (unsigned char)byte15,
                        (unsigned char)byte16
                    }));
                } else if (type_ == Domain) {
                    int hostnameLen = hostname.length();
                    buffers.push_back(boost::asio::buffer(&hostnameLen, 1));
                    buffers.push_back(boost::asio::buffer(hostname, 255));
    
                    std::cout << hostnameLen << std::endl << hostname << std::endl;
                }
    
                unsigned char port_high_byte_ = (port >> 8) & 0xff;
                unsigned char port_low_byte_ = port & 0xff;
    
                buffers.push_back(boost::asio::buffer(&port_high_byte_, 1));
                buffers.push_back(boost::asio::buffer(&port_low_byte_, 1));
    
                std::cout << buffers[0].data();
                return buffers;
            }            
    
    };
    
    class reply {
    
        private:
            int hostnameLen;
            int hostname;
            int port_nbo;
    
            enum state {
                AUTH_CHOICE = 0,
                PASSWORD = 1,
                CONNECT = 2
            };
    
            state state;
    
        public:
    
            unsigned char ver;
            unsigned char auth;
            unsigned char auth_ver;
            unsigned char status_;
            addr_type type;
    
            bool success() {
                return ver == version &&
                    (state == AUTH_CHOICE ? state == NO_AUTH || state == PASSWORD : true) &&
                    (state == PASSWORD ? status_ == 0x00 : true) &&
                    (state == CONNECT ? status_ == 0x00 : true);
            }
    
            std::string status() const {
                if (state == AUTH_CHOICE) {
                    if (auth == 0xFF) {
                        return "No acceptable auth methods";
                    }
                } else if (state == PASSWORD) {
                    return status_ == 0x00 ? "Auth success" : "Auth error: "+std::to_string(status_);
                } else if (state == CONNECT) {
                    return status_ == 0x00 ? "Connect success" : "Connect error: "+std::to_string(status_);
                }
                return "";
            }
    
            std::array<boost::asio::mutable_buffer, 5> auth_choice() {
                state = AUTH_CHOICE;
                return {
                    {
                        boost::asio::buffer(&ver, 1),
                        boost::asio::buffer(&auth, 1)
                    }
                };
            }
    
            std::array<boost::asio::mutable_buffer, 5> password_verif() {
                state = PASSWORD;
                return {
                    {
                        boost::asio::buffer(&auth_ver, 1),
                        boost::asio::buffer(&status_, 1)
                    }
                };
            }
    
            std::vector<boost::asio::mutable_buffer> connect_reply() {
                state = CONNECT;
                std::vector<boost::asio::mutable_buffer> buffers = {
                    {
                        boost::asio::buffer(&ver, 1),
                        boost::asio::buffer(&status_, 1),
                        boost::asio::buffer(&null_byte, 1),
                        boost::asio::buffer(&type, 1)
                    }
                };
    
                if (type == IPv4) {
                    buffers.push_back(boost::asio::buffer(&hostname, 4));
                } else if (type == IPv6) {
                    buffers.push_back(boost::asio::buffer(&hostname, 16));
                } else if (type == Domain) {
                    buffers.push_back(boost::asio::buffer(&hostnameLen, 1));
                    buffers.push_back(boost::asio::buffer(&hostname, 255));
                }
    
                buffers.push_back(boost::asio::buffer(&port_nbo, 2));
    
                return buffers;
            }

Solution

  • boost::asio::write(socket, socks_request.conn(socks5::request::connect, socks5::addr_type::Domain, hostname, port));

    we can split this code into:

    [1] get buffers
    std::vector<boost::asio::const_buffer> buffs = 
    socks_request.conn(socks5::request::connect, socks5::addr_type::Domain, hostname, port);
    [2] send data
    boost::asio::write(socket, buffs);
    

    in conn we have a lot local variables, which are passed into boost::asio::buffer. What does buffer do ? It returns wrapper for data details here, which consists of pointer to data and length of data. No data are copied. So when conn ends, you have vector of buffers which refer to data which was destroyed - undefined behaviour.

    You need to find new approach to send data, or provide that data are valid when boost::asio::write uses buffer to them.